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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/README.md +20 -24
  2. package/build/bluetooth-sdk/debug.d.ts +2 -0
  3. package/build/bluetooth-sdk/debug.d.ts.map +1 -0
  4. package/build/bluetooth-sdk/debug.js +2 -0
  5. package/build/bluetooth-sdk/debug.js.map +1 -0
  6. package/build/bluetooth-sdk/index.d.ts +3 -0
  7. package/build/bluetooth-sdk/index.d.ts.map +1 -0
  8. package/build/bluetooth-sdk/index.js +3 -0
  9. package/build/bluetooth-sdk/index.js.map +1 -0
  10. package/build/bluetooth-sdk/photo-receiver.d.ts +3 -0
  11. package/build/bluetooth-sdk/photo-receiver.d.ts.map +1 -0
  12. package/build/bluetooth-sdk/photo-receiver.js +3 -0
  13. package/build/bluetooth-sdk/photo-receiver.js.map +1 -0
  14. package/build/bluetooth-sdk/react.d.ts +2 -0
  15. package/build/bluetooth-sdk/react.d.ts.map +1 -0
  16. package/build/bluetooth-sdk/react.js +2 -0
  17. package/build/bluetooth-sdk/react.js.map +1 -0
  18. package/build/bluetooth-sdk/types.d.ts +2 -0
  19. package/build/bluetooth-sdk/types.d.ts.map +1 -0
  20. package/build/bluetooth-sdk/types.js +2 -0
  21. package/build/bluetooth-sdk/types.js.map +1 -0
  22. package/build/engine.d.ts +13 -13
  23. package/build/engine.d.ts.map +1 -1
  24. package/build/engine.js +2 -2
  25. package/build/engine.js.map +1 -1
  26. package/build/facades/display.d.ts +1 -1
  27. package/build/facades/display.d.ts.map +1 -1
  28. package/build/facades/display.js +1 -1
  29. package/build/facades/display.js.map +1 -1
  30. package/build/facades/glasses.d.ts +3 -13
  31. package/build/facades/glasses.d.ts.map +1 -1
  32. package/build/facades/glasses.js +3 -3
  33. package/build/facades/glasses.js.map +1 -1
  34. package/build/facades/ota.d.ts +5 -16
  35. package/build/facades/ota.d.ts.map +1 -1
  36. package/build/facades/ota.js +3 -3
  37. package/build/facades/ota.js.map +1 -1
  38. package/build/facades/pairing.d.ts +4 -5
  39. package/build/facades/pairing.d.ts.map +1 -1
  40. package/build/facades/pairing.js +2 -2
  41. package/build/facades/pairing.js.map +1 -1
  42. package/build/facades/speech.d.ts +7 -6
  43. package/build/facades/speech.d.ts.map +1 -1
  44. package/build/facades/speech.js +1 -1
  45. package/build/facades/speech.js.map +1 -1
  46. package/build/generated/releaseMetadata.d.ts +12 -0
  47. package/build/generated/releaseMetadata.d.ts.map +1 -0
  48. package/build/generated/releaseMetadata.js +10 -0
  49. package/build/generated/releaseMetadata.js.map +1 -0
  50. package/build/index.d.ts +4 -5
  51. package/build/index.d.ts.map +1 -1
  52. package/build/index.js +2 -3
  53. package/build/index.js.map +1 -1
  54. package/build/services/AppRegistry.d.ts +8 -0
  55. package/build/services/AppRegistry.d.ts.map +1 -1
  56. package/build/services/AppRegistry.js +15 -0
  57. package/build/services/AppRegistry.js.map +1 -1
  58. package/build/services/OtaInstallCoordinator.d.ts +1 -1
  59. package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
  60. package/build/services/OtaInstallCoordinator.js +3 -0
  61. package/build/services/OtaInstallCoordinator.js.map +1 -1
  62. package/build/services/OtaUpdateCheckService.d.ts +1 -1
  63. package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
  64. package/build/services/OtaUpdateCheckService.js +12 -16
  65. package/build/services/OtaUpdateCheckService.js.map +1 -1
  66. package/build/services/glassesClockSync.d.ts.map +1 -1
  67. package/build/services/glassesClockSync.js +4 -0
  68. package/build/services/glassesClockSync.js.map +1 -1
  69. package/build/services/otaDisplayState.d.ts +1 -1
  70. package/build/services/otaDisplayState.d.ts.map +1 -1
  71. package/build/services/otaDisplayState.js.map +1 -1
  72. package/build/services/otaInstallPolicy.d.ts +1 -1
  73. package/build/services/otaInstallPolicy.d.ts.map +1 -1
  74. package/build/services/otaInstallPolicy.js.map +1 -1
  75. package/build/services/otaLegacyMapping.d.ts +1 -1
  76. package/build/services/otaLegacyMapping.d.ts.map +1 -1
  77. package/build/services/otaLegacyMapping.js.map +1 -1
  78. package/build/services/otaManifestPolicy.d.ts +14 -0
  79. package/build/services/otaManifestPolicy.d.ts.map +1 -0
  80. package/build/services/otaManifestPolicy.js +19 -0
  81. package/build/services/otaManifestPolicy.js.map +1 -0
  82. package/build/services/otaManifestUrl.d.ts +2 -1
  83. package/build/services/otaManifestUrl.d.ts.map +1 -1
  84. package/build/services/otaManifestUrl.js +24 -32
  85. package/build/services/otaManifestUrl.js.map +1 -1
  86. package/build/stores/settings.js +3 -3
  87. package/build/stores/settings.js.map +1 -1
  88. package/build/types/enums.d.ts +1 -25
  89. package/build/types/enums.d.ts.map +1 -1
  90. package/build/types/enums.js +1 -27
  91. package/build/types/enums.js.map +1 -1
  92. package/build/types/hardware.d.ts +3 -13
  93. package/build/types/hardware.d.ts.map +1 -1
  94. package/build/types/hardware.js +1 -3
  95. package/build/types/hardware.js.map +1 -1
  96. package/build/types/index.d.ts +2 -6
  97. package/build/types/index.d.ts.map +1 -1
  98. package/build/types/index.js +2 -6
  99. package/build/types/index.js.map +1 -1
  100. package/package.json +40 -22
  101. package/src/bluetooth-sdk/debug.ts +1 -0
  102. package/src/bluetooth-sdk/index.ts +2 -0
  103. package/src/bluetooth-sdk/photo-receiver.ts +2 -0
  104. package/src/bluetooth-sdk/react.ts +1 -0
  105. package/src/bluetooth-sdk/types.ts +1 -0
  106. package/src/engine.ts +2 -2
  107. package/src/facades/display.ts +1 -1
  108. package/src/facades/glasses.ts +4 -4
  109. package/src/facades/ota.ts +4 -4
  110. package/src/facades/pairing.ts +4 -4
  111. package/src/facades/speech.ts +21 -7
  112. package/src/generated/releaseMetadata.ts +20 -0
  113. package/src/index.ts +13 -11
  114. package/src/services/AppRegistry.ts +16 -0
  115. package/src/services/OtaInstallCoordinator.ts +4 -1
  116. package/src/services/OtaUpdateCheckService.ts +20 -20
  117. package/src/services/glassesClockSync.ts +4 -0
  118. package/src/services/otaDisplayState.ts +1 -1
  119. package/src/services/otaInstallPolicy.ts +1 -1
  120. package/src/services/otaLegacyMapping.ts +1 -1
  121. package/src/services/otaManifestPolicy.ts +42 -0
  122. package/src/services/otaManifestUrl.ts +27 -36
  123. package/src/stores/settings.ts +3 -3
  124. package/src/types/enums.ts +1 -27
  125. package/src/types/hardware.ts +3 -13
  126. package/src/types/index.ts +2 -6
  127. package/build/devtools.d.ts +0 -11
  128. package/build/devtools.d.ts.map +0 -1
  129. package/build/devtools.js +0 -13
  130. package/build/devtools.js.map +0 -1
  131. package/build/internal.d.ts +0 -67
  132. package/build/internal.d.ts.map +0 -1
  133. package/build/internal.js +0 -90
  134. package/build/internal.js.map +0 -1
  135. package/src/devtools.ts +0 -13
  136. package/src/internal.ts +0 -160
@@ -1 +1 @@
1
- {"version":3,"file":"hardware.js","sourceRoot":"","sources":["../../src/types/hardware.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAA0C,MAAM,SAAS,CAAC;AAyK9E;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAiC;IACjE,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,eAAe;IAC5C,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,eAAe;IAC5C,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,aAAa;IACxC,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,UAAU;IAClC,CAAC,gBAAgB,CAAC,SAAS,CAAC,EAAE,gBAAgB;IAC9C,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,SAAS;IAChC,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,mDAAmD;IACnF,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;QAClB,GAAG,eAAe;QAClB,SAAS,EAAE,WAAW,CAAC,IAAI;QAC3B,OAAO,EAAE;YACP,GAAG,eAAe,CAAC,OAAO;YAC1B,gBAAgB,EAAE,KAAK;SACxB;QACD,aAAa,EAAE,IAAI;QACnB,MAAM,EAAE,IAAI;QACZ,UAAU,EAAE;YACV,KAAK,EAAE,CAAC;YACR,MAAM,EAAE,KAAK;SACd;KACF;IACD,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI;CACvB,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,UAAuB,EAAgB,EAAE;IAC5E,MAAM,SAAS,GAAG,UAAoB,CAAC;IACvC,IAAI,CAAC,qBAAqB,CAAC,SAAS,CAAC,EAAE,CAAC;QACtC,OAAO,qBAAqB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,qBAAqB,CAAC,SAAS,CAAC,CAAC;AAC1C,CAAC,CAAC;AAEF,iCAAiC;AACjC,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC","sourcesContent":["/**\n * @mentra/types - Hardware capability types\n */\n\nimport { evenRealitiesG1 } from \"./capabilities/even-realities-g1\";\nimport { evenRealitiesG2 } from \"./capabilities/even-realities-g2\";\nimport { mentraDisplay } from \"./capabilities/mentra-display\";\nimport { mentraLive } from \"./capabilities/mentra-live\";\nimport { simulatedGlasses } from \"./capabilities/simulated-glasses\";\nimport { vuzixZ100 } from \"./capabilities/vuzix-z100\";\nimport { none } from \"./capabilities/none\";\nimport { DeviceTypes, HardwareRequirementLevel, HardwareType } from \"./enums\";\n\n/**\n * Hardware requirement for an app\n * Specifies what hardware components an app needs\n */\nexport interface HardwareRequirement {\n type: HardwareType;\n level: HardwareRequirementLevel;\n description?: string; // Why this hardware is needed\n}\n\n/**\n * Camera capabilities\n */\nexport interface CameraCapabilities {\n resolution?: { width: number; height: number };\n hasHDR?: boolean;\n hasFocus?: boolean;\n video: {\n canRecord: boolean;\n canStream: boolean;\n supportedStreamTypes?: string[];\n supportedResolutions?: { width: number; height: number }[];\n };\n}\n\n/**\n * Display capabilities\n */\nexport interface DisplayCapabilities {\n count?: number;\n isColor?: boolean;\n color?: string; // e.g., \"green\", \"full_color\", \"pallet\"\n canDisplayBitmap?: boolean;\n resolution?: { width: number; height: number };\n fieldOfView?: { horizontal?: number; vertical?: number };\n maxTextLines?: number;\n adjustBrightness?: boolean;\n\n // --- Scene display API (display.render()) — typed capabilities ---\n // Pure DATA the host scene pipeline acts on generically; a device without\n // canPosition renders scenes via the host degrade path instead.\n /** Public drawable canvas in px — the raw coordinate space for render() boxes. */\n width?: number;\n height?: number;\n /** True ⇒ the device renders positioned elements (containers / canvas components). */\n canPosition?: boolean;\n /** Element budgets. Rects share the text pool on container-based devices. */\n maxTextElements?: number;\n maxImageElements?: number;\n /** Per-image dimension cap (box-level), when the firmware has one. */\n maxImagePx?: { width: number; height: number };\n shapes?: \"rect\"[];\n intensityLevels?: number;\n partialUpdate?: boolean;\n}\n\n/**\n * Microphone capabilities\n */\nexport interface MicrophoneCapabilities {\n count?: number;\n hasVAD?: boolean; // Voice Activity Detection\n}\n\n/**\n * Speaker capabilities\n */\nexport interface SpeakerCapabilities {\n count?: number;\n isPrivate?: boolean; // e.g., bone conduction\n}\n\n/**\n * IMU (Inertial Measurement Unit) capabilities\n */\nexport interface IMUCapabilities {\n axisCount?: number;\n hasAccelerometer?: boolean;\n hasCompass?: boolean;\n hasGyroscope?: boolean;\n}\n\n/**\n * Button capabilities\n */\nexport interface ButtonCapabilities {\n count?: number;\n buttons?: {\n type: \"press\" | \"swipe1d\" | \"swipe2d\";\n events: string[]; // e.g., \"press\", \"double_press\", \"long_press\", \"swipe_up\", \"swipe_down\"\n isCapacitive?: boolean;\n }[];\n}\n\n/**\n * Light capabilities\n */\nexport interface LightCapabilities {\n count?: number;\n lights?: {\n id: string; // Unique identifier for the LED (e.g., \"privacy\", \"user_feedback\")\n purpose: \"privacy\" | \"user_feedback\" | \"general\"; // LED purpose/function\n isFullColor: boolean;\n color?: string; // e.g., \"white\", \"rgb\"\n position?: \"front_facing\" | \"user_facing\" | \"side\" | \"unknown\"; // LED physical position\n }[];\n}\n\n/**\n * Power capabilities\n */\nexport interface PowerCapabilities {\n hasExternalBattery: boolean; // e.g., a case or puck\n}\n\n/**\n * Device hardware capabilities\n * Complete information about what hardware a device has\n */\nexport interface Capabilities {\n modelName: string;\n\n // Camera capabilities\n hasCamera: boolean;\n camera: CameraCapabilities | null;\n\n // Display capabilities\n hasDisplay: boolean;\n display: DisplayCapabilities | null;\n\n // Microphone capabilities\n hasMicrophone: boolean;\n microphone: MicrophoneCapabilities | null;\n\n // Speaker capabilities\n hasSpeaker: boolean;\n speaker: SpeakerCapabilities | null;\n\n // IMU capabilities\n hasIMU: boolean;\n imu: IMUCapabilities | null;\n\n // Button capabilities\n hasButton: boolean;\n button: ButtonCapabilities | null;\n\n // Light capabilities\n hasLight: boolean;\n light: LightCapabilities | null;\n\n // Power capabilities\n power: PowerCapabilities;\n\n // WiFi capability\n hasWifi: boolean;\n\n // OTA capability\n hasOta?: boolean;\n\n // Native dashboard capability\n // True when the device renders its own dashboard in firmware (e.g. Even\n // Realities' built-in dashboard on G2). MentraOS does not manage or expose\n // dashboard settings for these devices. Absent/false means MentraOS owns the\n // dashboard.\n hasNativeDashboard?: boolean;\n}\n\n/**\n * Hardware capability profiles for supported glasses models\n * Key: model_name string (e.g., \"Even Realities G1\", \"Mentra Live\")\n * Value: Capabilities object defining device features\n */\nexport const HARDWARE_CAPABILITIES: Record<string, Capabilities> = {\n [evenRealitiesG1.modelName]: evenRealitiesG1,\n [evenRealitiesG2.modelName]: evenRealitiesG2,\n [mentraDisplay.modelName]: mentraDisplay,\n [mentraLive.modelName]: mentraLive,\n [simulatedGlasses.modelName]: simulatedGlasses,\n [vuzixZ100.modelName]: vuzixZ100,\n [DeviceTypes.MACH1]: vuzixZ100, // Mach1 uses same Vuzix Ultralite hardware as Z100\n [DeviceTypes.AR99]: {\n ...evenRealitiesG1,\n modelName: DeviceTypes.AR99,\n display: {\n ...evenRealitiesG1.display,\n canDisplayBitmap: false,\n },\n hasMicrophone: true,\n hasOta: true,\n microphone: {\n count: 1,\n hasVAD: false,\n },\n },\n [none.modelName]: none,\n};\n\nexport const getModelCapabilities = (deviceType: DeviceTypes): Capabilities => {\n const modelName = deviceType as string;\n if (!HARDWARE_CAPABILITIES[modelName]) {\n return HARDWARE_CAPABILITIES[DeviceTypes.NONE];\n }\n return HARDWARE_CAPABILITIES[modelName];\n};\n\n// export * from \"./capabilities\"\nexport { simulatedGlasses, evenRealitiesG1, evenRealitiesG2, mentraLive, vuzixZ100, mentraDisplay };\n"]}
1
+ {"version":3,"file":"hardware.js","sourceRoot":"","sources":["../../src/types/hardware.ts"],"names":[],"mappings":"AAAA,oDAAoD;AAEpD,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAiKtC;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAiC;IACjE,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,eAAe;IAC5C,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,eAAe;IAC5C,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,aAAa;IACxC,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,UAAU;IAClC,CAAC,gBAAgB,CAAC,SAAS,CAAC,EAAE,gBAAgB;IAC9C,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,SAAS;IAChC,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,mDAAmD;IACnF,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;QAClB,GAAG,eAAe;QAClB,SAAS,EAAE,WAAW,CAAC,IAAI;QAC3B,OAAO,EAAE;YACP,GAAG,eAAe,CAAC,OAAO;YAC1B,gBAAgB,EAAE,KAAK;SACxB;QACD,aAAa,EAAE,IAAI;QACnB,MAAM,EAAE,IAAI;QACZ,UAAU,EAAE;YACV,KAAK,EAAE,CAAC;YACR,MAAM,EAAE,KAAK;SACd;KACF;IACD,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI;CACvB,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,UAAuB,EAAgB,EAAE;IAC5E,MAAM,SAAS,GAAG,UAAoB,CAAC;IACvC,IAAI,CAAC,qBAAqB,CAAC,SAAS,CAAC,EAAE,CAAC;QACtC,OAAO,qBAAqB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,qBAAqB,CAAC,SAAS,CAAC,CAAC;AAC1C,CAAC,CAAC;AAEF,iCAAiC;AACjC,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC","sourcesContent":["/** Hardware capability types used by the Engine. */\n\nimport { evenRealitiesG1 } from \"./capabilities/even-realities-g1\";\nimport { evenRealitiesG2 } from \"./capabilities/even-realities-g2\";\nimport { mentraDisplay } from \"./capabilities/mentra-display\";\nimport { mentraLive } from \"./capabilities/mentra-live\";\nimport { simulatedGlasses } from \"./capabilities/simulated-glasses\";\nimport { vuzixZ100 } from \"./capabilities/vuzix-z100\";\nimport { none } from \"./capabilities/none\";\nimport { DeviceTypes } from \"./enums\";\n\nexport type { HardwareRequirement } from \"@mentra/miniapp/hardware\";\n\n/**\n * Camera capabilities\n */\nexport interface CameraCapabilities {\n resolution?: { width: number; height: number };\n hasHDR?: boolean;\n hasFocus?: boolean;\n video: {\n canRecord: boolean;\n canStream: boolean;\n supportedStreamTypes?: string[];\n supportedResolutions?: { width: number; height: number }[];\n };\n}\n\n/**\n * Display capabilities\n */\nexport interface DisplayCapabilities {\n count?: number;\n isColor?: boolean;\n color?: string; // e.g., \"green\", \"full_color\", \"pallet\"\n canDisplayBitmap?: boolean;\n resolution?: { width: number; height: number };\n fieldOfView?: { horizontal?: number; vertical?: number };\n maxTextLines?: number;\n adjustBrightness?: boolean;\n\n // --- Scene display API (display.render()) — typed capabilities ---\n // Pure DATA the host scene pipeline acts on generically; a device without\n // canPosition renders scenes via the host degrade path instead.\n /** Public drawable canvas in px — the raw coordinate space for render() boxes. */\n width?: number;\n height?: number;\n /** True ⇒ the device renders positioned elements (containers / canvas components). */\n canPosition?: boolean;\n /** Element budgets. Rects share the text pool on container-based devices. */\n maxTextElements?: number;\n maxImageElements?: number;\n /** Per-image dimension cap (box-level), when the firmware has one. */\n maxImagePx?: { width: number; height: number };\n shapes?: \"rect\"[];\n intensityLevels?: number;\n partialUpdate?: boolean;\n}\n\n/**\n * Microphone capabilities\n */\nexport interface MicrophoneCapabilities {\n count?: number;\n hasVAD?: boolean; // Voice Activity Detection\n}\n\n/**\n * Speaker capabilities\n */\nexport interface SpeakerCapabilities {\n count?: number;\n isPrivate?: boolean; // e.g., bone conduction\n}\n\n/**\n * IMU (Inertial Measurement Unit) capabilities\n */\nexport interface IMUCapabilities {\n axisCount?: number;\n hasAccelerometer?: boolean;\n hasCompass?: boolean;\n hasGyroscope?: boolean;\n}\n\n/**\n * Button capabilities\n */\nexport interface ButtonCapabilities {\n count?: number;\n buttons?: {\n type: \"press\" | \"swipe1d\" | \"swipe2d\";\n events: string[]; // e.g., \"press\", \"double_press\", \"long_press\", \"swipe_up\", \"swipe_down\"\n isCapacitive?: boolean;\n }[];\n}\n\n/**\n * Light capabilities\n */\nexport interface LightCapabilities {\n count?: number;\n lights?: {\n id: string; // Unique identifier for the LED (e.g., \"privacy\", \"user_feedback\")\n purpose: \"privacy\" | \"user_feedback\" | \"general\"; // LED purpose/function\n isFullColor: boolean;\n color?: string; // e.g., \"white\", \"rgb\"\n position?: \"front_facing\" | \"user_facing\" | \"side\" | \"unknown\"; // LED physical position\n }[];\n}\n\n/**\n * Power capabilities\n */\nexport interface PowerCapabilities {\n hasExternalBattery: boolean; // e.g., a case or puck\n}\n\n/**\n * Device hardware capabilities\n * Complete information about what hardware a device has\n */\nexport interface Capabilities {\n modelName: string;\n\n // Camera capabilities\n hasCamera: boolean;\n camera: CameraCapabilities | null;\n\n // Display capabilities\n hasDisplay: boolean;\n display: DisplayCapabilities | null;\n\n // Microphone capabilities\n hasMicrophone: boolean;\n microphone: MicrophoneCapabilities | null;\n\n // Speaker capabilities\n hasSpeaker: boolean;\n speaker: SpeakerCapabilities | null;\n\n // IMU capabilities\n hasIMU: boolean;\n imu: IMUCapabilities | null;\n\n // Button capabilities\n hasButton: boolean;\n button: ButtonCapabilities | null;\n\n // Light capabilities\n hasLight: boolean;\n light: LightCapabilities | null;\n\n // Power capabilities\n power: PowerCapabilities;\n\n // WiFi capability\n hasWifi: boolean;\n\n // OTA capability\n hasOta?: boolean;\n\n // Native dashboard capability\n // True when the device renders its own dashboard in firmware (e.g. Even\n // Realities' built-in dashboard on G2). MentraOS does not manage or expose\n // dashboard settings for these devices. Absent/false means MentraOS owns the\n // dashboard.\n hasNativeDashboard?: boolean;\n}\n\n/**\n * Hardware capability profiles for supported glasses models\n * Key: model_name string (e.g., \"Even Realities G1\", \"Mentra Live\")\n * Value: Capabilities object defining device features\n */\nexport const HARDWARE_CAPABILITIES: Record<string, Capabilities> = {\n [evenRealitiesG1.modelName]: evenRealitiesG1,\n [evenRealitiesG2.modelName]: evenRealitiesG2,\n [mentraDisplay.modelName]: mentraDisplay,\n [mentraLive.modelName]: mentraLive,\n [simulatedGlasses.modelName]: simulatedGlasses,\n [vuzixZ100.modelName]: vuzixZ100,\n [DeviceTypes.MACH1]: vuzixZ100, // Mach1 uses same Vuzix Ultralite hardware as Z100\n [DeviceTypes.AR99]: {\n ...evenRealitiesG1,\n modelName: DeviceTypes.AR99,\n display: {\n ...evenRealitiesG1.display,\n canDisplayBitmap: false,\n },\n hasMicrophone: true,\n hasOta: true,\n microphone: {\n count: 1,\n hasVAD: false,\n },\n },\n [none.modelName]: none,\n};\n\nexport const getModelCapabilities = (deviceType: DeviceTypes): Capabilities => {\n const modelName = deviceType as string;\n if (!HARDWARE_CAPABILITIES[modelName]) {\n return HARDWARE_CAPABILITIES[DeviceTypes.NONE];\n }\n return HARDWARE_CAPABILITIES[modelName];\n};\n\n// export * from \"./capabilities\"\nexport { simulatedGlasses, evenRealitiesG1, evenRealitiesG2, mentraLive, vuzixZ100, mentraDisplay };\n"]}
@@ -1,9 +1,5 @@
1
- /**
2
- * Engine shared types — copied verbatim from @mentra/types so the module is
3
- * self-contained. Keep these in sync with cloud/packages/types/src when the
4
- * canonical types change. The capability profiles also live here under
5
- * ./capabilities/.
6
- */
1
+ /** Engine capability types and profiles. Miniapp manifest requirement types
2
+ * are re-exported from @mentra/miniapp, their canonical owner. */
7
3
  export { HardwareType, HardwareRequirementLevel, DeviceTypes, ControllerTypes } from "./enums";
8
4
  export type { HardwareRequirement, CameraCapabilities, DisplayCapabilities, MicrophoneCapabilities, SpeakerCapabilities, IMUCapabilities, ButtonCapabilities, LightCapabilities, PowerCapabilities, Capabilities, } from "./hardware";
9
5
  export { HARDWARE_CAPABILITIES, getModelCapabilities, simulatedGlasses, evenRealitiesG1, evenRealitiesG2, mentraLive, vuzixZ100, mentraDisplay, } from "./hardware";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAC,YAAY,EAAE,wBAAwB,EAAE,WAAW,EAAE,eAAe,EAAC,MAAM,SAAS,CAAA;AAG5F,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,GACb,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,UAAU,EACV,SAAS,EACT,aAAa,GACd,MAAM,YAAY,CAAA;AAGnB,YAAY,EAAC,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,eAAe,EAAE,SAAS,EAAC,MAAM,UAAU,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;kEACkE;AAGlE,OAAO,EAAC,YAAY,EAAE,wBAAwB,EAAE,WAAW,EAAE,eAAe,EAAC,MAAM,SAAS,CAAA;AAG5F,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,GACb,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,UAAU,EACV,SAAS,EACT,aAAa,GACd,MAAM,YAAY,CAAA;AAGnB,YAAY,EAAC,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,eAAe,EAAE,SAAS,EAAC,MAAM,UAAU,CAAA"}
@@ -1,9 +1,5 @@
1
- /**
2
- * Engine shared types — copied verbatim from @mentra/types so the module is
3
- * self-contained. Keep these in sync with cloud/packages/types/src when the
4
- * canonical types change. The capability profiles also live here under
5
- * ./capabilities/.
6
- */
1
+ /** Engine capability types and profiles. Miniapp manifest requirement types
2
+ * are re-exported from @mentra/miniapp, their canonical owner. */
7
3
  // Enums (runtime values)
8
4
  export { HardwareType, HardwareRequirementLevel, DeviceTypes, ControllerTypes } from "./enums";
9
5
  export { HARDWARE_CAPABILITIES, getModelCapabilities, simulatedGlasses, evenRealitiesG1, evenRealitiesG2, mentraLive, vuzixZ100, mentraDisplay, } from "./hardware";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,yBAAyB;AACzB,OAAO,EAAC,YAAY,EAAE,wBAAwB,EAAE,WAAW,EAAE,eAAe,EAAC,MAAM,SAAS,CAAA;AAgB5F,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,UAAU,EACV,SAAS,EACT,aAAa,GACd,MAAM,YAAY,CAAA","sourcesContent":["/**\n * Engine shared types copied verbatim from @mentra/types so the module is\n * self-contained. Keep these in sync with cloud/packages/types/src when the\n * canonical types change. The capability profiles also live here under\n * ./capabilities/.\n */\n\n// Enums (runtime values)\nexport {HardwareType, HardwareRequirementLevel, DeviceTypes, ControllerTypes} from \"./enums\"\n\n// Hardware types\nexport type {\n HardwareRequirement,\n CameraCapabilities,\n DisplayCapabilities,\n MicrophoneCapabilities,\n SpeakerCapabilities,\n IMUCapabilities,\n ButtonCapabilities,\n LightCapabilities,\n PowerCapabilities,\n Capabilities,\n} from \"./hardware\"\n\nexport {\n HARDWARE_CAPABILITIES,\n getModelCapabilities,\n simulatedGlasses,\n evenRealitiesG1,\n evenRealitiesG2,\n mentraLive,\n vuzixZ100,\n mentraDisplay,\n} from \"./hardware\"\n\n// Applet types\nexport type {AppletType, AppPermissionType, AppletPermission, AppletInterface, ClientApp} from \"./applet\"\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;kEACkE;AAElE,yBAAyB;AACzB,OAAO,EAAC,YAAY,EAAE,wBAAwB,EAAE,WAAW,EAAE,eAAe,EAAC,MAAM,SAAS,CAAA;AAgB5F,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,UAAU,EACV,SAAS,EACT,aAAa,GACd,MAAM,YAAY,CAAA","sourcesContent":["/** Engine capability types and profiles. Miniapp manifest requirement types\n * are re-exported from @mentra/miniapp, their canonical owner. */\n\n// Enums (runtime values)\nexport {HardwareType, HardwareRequirementLevel, DeviceTypes, ControllerTypes} from \"./enums\"\n\n// Hardware types\nexport type {\n HardwareRequirement,\n CameraCapabilities,\n DisplayCapabilities,\n MicrophoneCapabilities,\n SpeakerCapabilities,\n IMUCapabilities,\n ButtonCapabilities,\n LightCapabilities,\n PowerCapabilities,\n Capabilities,\n} from \"./hardware\"\n\nexport {\n HARDWARE_CAPABILITIES,\n getModelCapabilities,\n simulatedGlasses,\n evenRealitiesG1,\n evenRealitiesG2,\n mentraLive,\n vuzixZ100,\n mentraDisplay,\n} from \"./hardware\"\n\n// Applet types\nexport type {AppletType, AppPermissionType, AppletPermission, AppletInterface, ClientApp} from \"./applet\"\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentra/engine",
3
- "version": "0.1.0-dev.1",
3
+ "version": "3.1.0-dev.4",
4
4
  "description": "Mentra Engine — miniapp registry, WebView bridge, and miniapp store",
5
5
  "main": "build/index.js",
6
6
  "react-native": "src/index.ts",
@@ -11,21 +11,36 @@
11
11
  "react-native": "./src/index.ts",
12
12
  "default": "./build/index.js"
13
13
  },
14
- "./internal": {
15
- "types": "./build/internal.d.ts",
16
- "react-native": "./src/internal.ts",
17
- "default": "./build/internal.js"
18
- },
19
- "./devtools": {
20
- "types": "./build/devtools.d.ts",
21
- "react-native": "./src/devtools.ts",
22
- "default": "./build/devtools.js"
23
- },
24
14
  "./ota": {
25
15
  "types": "./build/react/index.d.ts",
26
16
  "react-native": "./src/react/index.ts",
27
17
  "default": "./build/react/index.js"
28
18
  },
19
+ "./bluetooth-sdk": {
20
+ "types": "./build/bluetooth-sdk/index.d.ts",
21
+ "react-native": "./src/bluetooth-sdk/index.ts",
22
+ "default": "./build/bluetooth-sdk/index.js"
23
+ },
24
+ "./bluetooth-sdk/react": {
25
+ "types": "./build/bluetooth-sdk/react.d.ts",
26
+ "react-native": "./src/bluetooth-sdk/react.ts",
27
+ "default": "./build/bluetooth-sdk/react.js"
28
+ },
29
+ "./bluetooth-sdk/types": {
30
+ "types": "./build/bluetooth-sdk/types.d.ts",
31
+ "react-native": "./src/bluetooth-sdk/types.ts",
32
+ "default": "./build/bluetooth-sdk/types.js"
33
+ },
34
+ "./bluetooth-sdk/photo-receiver": {
35
+ "types": "./build/bluetooth-sdk/photo-receiver.d.ts",
36
+ "react-native": "./src/bluetooth-sdk/photo-receiver.ts",
37
+ "default": "./build/bluetooth-sdk/photo-receiver.js"
38
+ },
39
+ "./bluetooth-sdk/debug": {
40
+ "types": "./build/bluetooth-sdk/debug.d.ts",
41
+ "react-native": "./src/bluetooth-sdk/debug.ts",
42
+ "default": "./build/bluetooth-sdk/debug.js"
43
+ },
29
44
  "./build/*": null,
30
45
  "./src/*": null,
31
46
  "./package.json": "./package.json"
@@ -34,6 +49,10 @@
34
49
  "build",
35
50
  "README.md",
36
51
  "src",
52
+ "!build/devtools.*",
53
+ "!build/internal.*",
54
+ "!src/devtools.ts",
55
+ "!src/internal.ts",
37
56
  "!src/**/__tests__",
38
57
  "!src/**/*.test.ts"
39
58
  ],
@@ -41,7 +60,10 @@
41
60
  "build": "expo-module build",
42
61
  "clean": "expo-module clean",
43
62
  "lint": "expo-module lint",
44
- "test": "./scripts/test.sh",
63
+ "test": "./scripts/test.sh && node --test scripts/*.test.mjs",
64
+ "write:release-metadata": "node scripts/write-release-metadata.mjs",
65
+ "verify:release-package": "node scripts/verify-release-package.mjs",
66
+ "test:public-api": "bun test src/bluetooth-sdk/__tests__/publicApi.test.ts",
45
67
  "build:module": "expo-module prepare",
46
68
  "prepublishOnly": "expo-module prepublishOnly",
47
69
  "expo-module": "expo-module"
@@ -69,16 +91,17 @@
69
91
  "registry": "https://registry.npmjs.org/"
70
92
  },
71
93
  "dependencies": {
94
+ "@mentra/bluetooth-sdk": "3.1.0-dev.4",
95
+ "@mentra/cloud-client": "3.1.0-dev.4",
96
+ "@mentra/cloud-protocol": "3.1.0-dev.4",
97
+ "@mentra/crust": "3.1.0-dev.4",
98
+ "@mentra/miniapp": "3.1.0-dev.4",
72
99
  "buffer": "^6.0.3",
73
100
  "events": "^3.3.0",
74
101
  "semver": "^7.7.4",
75
102
  "typesafe-ts": "^1.7.1"
76
103
  },
77
104
  "devDependencies": {
78
- "@mentra/bluetooth-sdk": "workspace:*",
79
- "@mentra/cloud-client": "workspace:*",
80
- "@mentra/cloud-protocol": "workspace:*",
81
- "@mentra/miniapp": "workspace:*",
82
105
  "@types/semver": "^7.7.1",
83
106
  "expo-module-scripts": "^55.0.2",
84
107
  "expo-modules-core": "55.0.25",
@@ -119,11 +142,6 @@
119
142
  "@react-native-community/netinfo": "*",
120
143
  "@dr.pogodin/react-native-fs": "*",
121
144
  "@craftzdog/react-native-buffer": "*",
122
- "axios": "*",
123
- "@mentra/bluetooth-sdk": "*",
124
- "@mentra/crust": "^0.1.0-dev.1",
125
- "@mentra/cloud-client": "^0.1.0-dev.0",
126
- "@mentra/miniapp": "^0.3.0-dev.1",
127
- "@mentra/cloud-protocol": "^0.1.0-dev.0"
145
+ "axios": "*"
128
146
  }
129
147
  }
@@ -0,0 +1 @@
1
+ export * from "@mentra/bluetooth-sdk/debug"
@@ -0,0 +1,2 @@
1
+ export {default} from "@mentra/bluetooth-sdk"
2
+ export * from "@mentra/bluetooth-sdk"
@@ -0,0 +1,2 @@
1
+ export {default} from "@mentra/bluetooth-sdk/photo-receiver"
2
+ export * from "@mentra/bluetooth-sdk/photo-receiver"
@@ -0,0 +1 @@
1
+ export * from "@mentra/bluetooth-sdk/react"
@@ -0,0 +1 @@
1
+ export * from "@mentra/bluetooth-sdk/types"
package/src/engine.ts CHANGED
@@ -5,8 +5,8 @@
5
5
  * name stays `engine` in code; the public API surface is `engine`.)
6
6
  *
7
7
  * It grows one facade at a time. The migration-era flat store/service exports
8
- * live on the `@mentra/engine/internal` entry (and the debug singletons on
9
- * `@mentra/engine/devtools`); they shrink as screens move onto `engine.*`.
8
+ * remain in a private MentraOS host package; they shrink as screens move onto
9
+ * `engine.*`.
10
10
  */
11
11
  import {configure, start as bootstrapStart, stop as bootstrapStop, updateUiSeams} from "./runtime/bootstrap"
12
12
  import {cloudClientService} from "./services/CloudClientService"
@@ -4,7 +4,7 @@
4
4
  * glasses display for dev tools and escape-hatch use (normal rendering goes through
5
5
  * the engine DisplayProcessor / layout system, not these).
6
6
  */
7
- import BluetoothSdk from "@mentra/bluetooth-sdk/internal"
7
+ import BluetoothSdk from "@mentra/bluetooth-sdk"
8
8
  import {displayMirror} from "./displayMirror"
9
9
 
10
10
  export const display = {
@@ -11,7 +11,7 @@
11
11
  // surface, not the public entry. Relative path (the alias doesn't resolve in engine's
12
12
  // standalone build); jest moduleNameMapper + tsconfig both resolve it.
13
13
  import BluetoothSdk from "@mentra/bluetooth-sdk/internal"
14
- import type {ButtonPressEvent, TouchEvent} from "@mentra/bluetooth-sdk/internal"
14
+ import type {ButtonPressEvent, ConnectOptions, Device, TouchEvent} from "@mentra/bluetooth-sdk"
15
15
  import {useGlassesStore} from "../stores/glasses"
16
16
  import {useSettingsStore, SETTINGS} from "../stores/settings"
17
17
  import {hasDefaultDevice} from "../services/DeviceStoreHydration"
@@ -94,10 +94,10 @@ export const glasses = {
94
94
  disconnect: (): Promise<void> => BluetoothSdk.disconnect(),
95
95
  forget: (): Promise<void> => BluetoothSdk.forget(),
96
96
  /** Connect to a specific (discovered) device. */
97
- connect: async (...args: Parameters<typeof BluetoothSdk.connect>): Promise<void> => {
97
+ connect: async (device: Device, options?: ConnectOptions): Promise<void> => {
98
98
  try {
99
99
  await pushAllBluetoothSettings()
100
- await BluetoothSdk.connect(...args)
100
+ await BluetoothSdk.connect(device, options)
101
101
  } catch (error) {
102
102
  recordSupportProfileConnectionFailure(error, "connect")
103
103
  throw error
@@ -106,7 +106,7 @@ export const glasses = {
106
106
  /** Connect the built-in simulated glasses (dev/testing). */
107
107
  connectSimulated: (): Promise<void> => BluetoothSdk.connectSimulated(),
108
108
  /** Set a device as the `connectDefault()` target. */
109
- setDefault: (...args: Parameters<typeof BluetoothSdk.setDefaultDevice>) => BluetoothSdk.setDefaultDevice(...args),
109
+ setDefault: (device: Device | null): Promise<void> => BluetoothSdk.setDefaultDevice(device),
110
110
  /**
111
111
  * Reconnect to the saved default glasses. Resolves `false` when there's nothing to
112
112
  * reconnect to (no default paired) or the connect attempt fails (e.g. BLE powered
@@ -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 "@mentra/bluetooth-sdk/internal"
12
- import type {OtaProgress, OtaProgressStatus, OtaStatus, OtaUpdateInfo} from "@mentra/bluetooth-sdk/internal"
12
+ import type {OtaProgress, OtaProgressStatus, OtaStartAckEvent, OtaStatus, OtaUpdateInfo} from "@mentra/bluetooth-sdk"
13
13
  import {isGlassesConnected, useGlassesStore} from "../stores/glasses"
14
14
  import {resolveOtaManifestUrl} from "../services/otaManifestUrl"
15
15
  import {otaInstallCoordinator, type OtaInstallSnapshot} from "../services/OtaInstallCoordinator"
@@ -65,11 +65,11 @@ export const ota = {
65
65
  },
66
66
  /**
67
67
  * Start the firmware install with the resolved OTA manifest URL. Progress lands on
68
- * `status()`/`onStatus()`. (The host resolves the manifest URL — dev-override/env/prod
69
- * resolution stays with the OTA config; the host-side stuck/retry watchdog is its own
68
+ * `status()`/`onStatus()`. (The host resolves the manifest URL — developer override,
69
+ * host pin, or Engine release pin resolution stays with the OTA config; the stuck/retry watchdog is its own
70
70
  * resilience layer on top of this command.)
71
71
  */
72
- install: (...args: Parameters<typeof BluetoothSdk.startOtaUpdate>) => BluetoothSdk.startOtaUpdate(...args),
72
+ install: (otaVersionUrl?: string | null): Promise<OtaStartAckEvent> => BluetoothSdk.startOtaUpdate(otaVersionUrl),
73
73
  // Deferred: this facade entry was intended to become the engine-owned retry/check
74
74
  // action, but BluetoothSdk.checkForOtaUpdate() only returns a boolean. Exposing it
75
75
  // here would make callers think the rich otaUpdateAvailable read model is refreshed,
@@ -10,7 +10,7 @@
10
10
  // target hint) lives on the full surface, not the public entry (same reason
11
11
  // the glasses facade imports internal).
12
12
  import BluetoothSdk from "@mentra/bluetooth-sdk/internal"
13
- import type {ConnectOptions, Device, PairFailureEvent, GlassesNotReadyEvent} from "@mentra/bluetooth-sdk"
13
+ import type {ConnectOptions, Device, DeviceModel, PairFailureEvent, GlassesNotReadyEvent} from "@mentra/bluetooth-sdk"
14
14
  import {useCoreStore} from "../stores/core"
15
15
  import {useGlassesStore} from "../stores/glasses"
16
16
  import {hasDefaultDevice} from "../services/DeviceStoreHydration"
@@ -206,7 +206,7 @@ export const pairing = {
206
206
  markPendingSelection: (model: string) => markPendingSelection(model),
207
207
 
208
208
  /** Start scanning for nearby glasses. Results land on `searchResults()`/`onFound()`. */
209
- scan: (...args: Parameters<typeof BluetoothSdk.startScan>) => BluetoothSdk.startScan(...args),
209
+ scan: (model: DeviceModel): Promise<void> => BluetoothSdk.startScan(model),
210
210
  /** Whether a scan is currently in progress. */
211
211
  scanning: (): boolean => useCoreStore.getState().searching,
212
212
  /** Subscribe to scan-in-progress changes; fires only when it changes. Returns an unsubscribe. */
@@ -226,7 +226,7 @@ export const pairing = {
226
226
  // leak as mutable references into the store.
227
227
  searchResults: () => useCoreStore.getState().searchResults.map((result) => ({...result})),
228
228
  /** Subscribe to scan-result changes; fires only when they change. Returns an unsubscribe. */
229
- onFound: (cb: (results: ReturnType<typeof useCoreStore.getState>["searchResults"]) => void): (() => void) => {
229
+ onFound: (cb: (results: Device[]) => void): (() => void) => {
230
230
  let last = JSON.stringify(useCoreStore.getState().searchResults)
231
231
  return useCoreStore.subscribe(() => {
232
232
  const results = useCoreStore.getState().searchResults
@@ -250,7 +250,7 @@ export const pairing = {
250
250
  return BluetoothSdk.connect(device, {...options, saveAsDefault: false})
251
251
  },
252
252
  /** Set a device as the default for subsequent `glasses.connectDefault()`. */
253
- setDefault: (...args: Parameters<typeof BluetoothSdk.setDefaultDevice>) => BluetoothSdk.setDefaultDevice(...args),
253
+ setDefault: (device: Device | null): Promise<void> => BluetoothSdk.setDefaultDevice(device),
254
254
  /**
255
255
  * Prime the native Bluetooth Classic audio watcher with the picked device.
256
256
  * The iOS Mentra Live flow pairs Classic audio BEFORE any BLE connect
@@ -7,24 +7,38 @@
7
7
  import BluetoothSdk from "@mentra/bluetooth-sdk/internal"
8
8
  import sttModelManager from "../services/STTModelManager"
9
9
  import ttsModelManager from "../services/TTSModelManager"
10
- import offlineSpeechModelService from "../services/OfflineSpeechModelService"
10
+ import offlineSpeechModelService, {
11
+ type DownloadStatus as OfflineModelDownloadStatus,
12
+ } from "../services/OfflineSpeechModelService"
13
+ import type {
14
+ DownloadProgress as SttDownloadProgress,
15
+ ExtractionProgress as SttExtractionProgress,
16
+ LanguageConfig as SttLanguageConfig,
17
+ LanguageInfo as SttLanguageInfo,
18
+ } from "../services/STTModelManager"
11
19
 
12
20
  export const speech = {
13
21
  /** Restart the glasses' on-device transcriber (e.g. after switching STT language). */
14
22
  restartTranscriber: (): Promise<void> => BluetoothSdk.restartTranscriber(),
15
23
  stt: {
16
24
  currentLanguage: () => sttModelManager.getCurrentLanguage(),
17
- languages: () => sttModelManager.getAvailableLanguages(),
18
- languageInfo: () => sttModelManager.getAllLanguageInfo(),
19
- download: (...args: Parameters<typeof sttModelManager.downloadModel>) => sttModelManager.downloadModel(...args),
25
+ languages: (): SttLanguageConfig[] => sttModelManager.getAvailableLanguages(),
26
+ languageInfo: (): Promise<SttLanguageInfo[]> => sttModelManager.getAllLanguageInfo(),
27
+ download: (
28
+ ...args: [
29
+ code?: string,
30
+ onProgress?: (progress: SttDownloadProgress) => void,
31
+ onExtractionProgress?: (progress: SttExtractionProgress) => void,
32
+ ]
33
+ ): Promise<void> => sttModelManager.downloadModel(...args),
20
34
  activate: (code: string) => sttModelManager.setCurrentLanguage(code),
21
35
  cancelDownload: () => sttModelManager.cancelDownload(),
22
36
  deleteModel: (...args: Parameters<typeof sttModelManager.deleteModel>) => sttModelManager.deleteModel(...args),
23
37
  /** Current auto-download status of the offline STT model. */
24
- status: () => offlineSpeechModelService.getStatus(),
38
+ status: (): OfflineModelDownloadStatus | null => offlineSpeechModelService.getStatus(),
25
39
  /** Subscribe to auto-download status changes; returns an unsubscribe. */
26
- onStatusChanged: (...args: Parameters<typeof offlineSpeechModelService.subscribe>) =>
27
- offlineSpeechModelService.subscribe(...args),
40
+ onStatusChanged: (listener: (status: OfflineModelDownloadStatus | null) => void): (() => void) =>
41
+ offlineSpeechModelService.subscribe(listener),
28
42
  },
29
43
  tts: {
30
44
  currentLanguage: () => ttsModelManager.getCurrentLanguage(),
@@ -0,0 +1,20 @@
1
+ /** Generated by release CI. Do not edit in a release checkout. */
2
+ export interface EngineReleaseMetadata {
3
+ schemaVersion: 1
4
+ familyBaseVersion: string | null
5
+ releaseIdentity: string | null
6
+ releaseSetId: string | null
7
+ sourceCommit: string | null
8
+ otaManifestUrl: string | null
9
+ otaManifestSha256: string | null
10
+ }
11
+
12
+ export const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({
13
+ "schemaVersion": 1,
14
+ "familyBaseVersion": "3.1.0",
15
+ "releaseIdentity": "3.1.0-dev.4",
16
+ "releaseSetId": "mentra-3.1.0-dev.4",
17
+ "sourceCommit": "43b81aea1c84675fade024077063e45fddde4861",
18
+ "otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-v3.1.0-dev.4/mentra-live-ota-3.1.0-dev.4.json",
19
+ "otaManifestSha256": "8fbea7589345658dc5dffcb394229ffc5c52fbe75352acb8f6827d83290ce750"
20
+ })
package/src/index.ts CHANGED
@@ -6,8 +6,7 @@
6
6
  * renders with (decision functions, sort/order helpers, policy constants,
7
7
  * capability tables, timers). Judgment rule: read models, commands, pure
8
8
  * functions and types belong here; anything that mutates runtime state or
9
- * exposes a raw store/service lives on `@mentra/engine/internal`
10
- * (migration-era surface) or `@mentra/engine/devtools` (debug-only) instead.
9
+ * exposes a raw store/service remains private to the MentraOS host.
11
10
  * See cloud-v2/docs/issues/020-glasses-status-boundary/integration-review.md §D.
12
11
  */
13
12
 
@@ -47,12 +46,7 @@ export {MENTRA_LIVE_SETTING_KEYS, getBluetoothSettingKeysForDevice} from "./stor
47
46
 
48
47
  // Runtime-shared constants and contract DTOs.
49
48
  export {ISLAND_SETTINGS_KEYS} from "./runtime/config"
50
- export type {
51
- CloudClientStatusSnapshot,
52
- MiniappAuthToken,
53
- InteropAuditEvent,
54
- TtsSynthesisResult,
55
- } from "./runtime/config"
49
+ export type {CloudClientStatusSnapshot, MiniappAuthToken, InteropAuditEvent, TtsSynthesisResult} from "./runtime/config"
56
50
 
57
51
  // Pure readiness predicates over glasses connection state.
58
52
  export {
@@ -132,11 +126,19 @@ export {
132
126
  export type {GalleryNotice, GalleryNoticeCode} from "./services/asg/galleryNotices"
133
127
  export {MediaLibraryPermissions} from "./utils/permissions/MediaLibraryPermissions"
134
128
  export {deriveGalleryDisplayName} from "./utils/permissions/galleryDisplayName"
135
- export type {PhotoInfo, CaptureFile, CaptureGroup, GalleryResponse, ServerStatus, HealthResponse, GalleryEvent} from "./types/asg"
129
+ export type {
130
+ PhotoInfo,
131
+ CaptureFile,
132
+ CaptureGroup,
133
+ GalleryResponse,
134
+ ServerStatus,
135
+ HealthResponse,
136
+ GalleryEvent,
137
+ } from "./types/asg"
136
138
 
137
139
  // Bluetooth SDK event types host UI subscribes to via engine facades (the
138
140
  // BluetoothSdk singleton passthrough itself is internal).
139
- export type {PairFailureEvent, GlassesNotReadyEvent} from "@mentra/bluetooth-sdk/internal"
141
+ export type {PairFailureEvent, GlassesNotReadyEvent} from "@mentra/bluetooth-sdk"
140
142
 
141
143
  // Cloud connection status enum (read model; the connection store is internal).
142
144
  export {WebSocketStatus} from "./stores/connection"
@@ -181,7 +183,7 @@ export {
181
183
  export {HardwareCompatibility, type CompatibilityResult} from "./utils/hardware"
182
184
  export {BgTimer, throttle, debounce} from "./utils/timers"
183
185
 
184
- // Types (copied from @mentra/types keep in sync with cloud/packages/types/src)
186
+ // Hardware types and capability profiles.
185
187
  export {
186
188
  HardwareType,
187
189
  HardwareRequirementLevel,
@@ -871,6 +871,22 @@ class AppRegistry {
871
871
  this.notify()
872
872
  }
873
873
 
874
+ /**
875
+ * Update whether a registered offline app is projected into launcher UIs.
876
+ *
877
+ * Offline apps are registered once at host startup, so settings-backed
878
+ * visibility changes need to update the projection without reinstalling or
879
+ * duplicating the app. No-op when the app is missing or already matches.
880
+ */
881
+ public setOfflineAppHidden(packageName: string, hidden: boolean): void {
882
+ const app = this.offlineApps.find((candidate) => candidate.packageName === packageName)
883
+ if (!app || app.hidden === hidden) return
884
+
885
+ app.hidden = hidden
886
+ this.refreshNeeded = true
887
+ this.notify()
888
+ }
889
+
874
890
  /**
875
891
  * Registered as needing the on-device STT model before it can start (the
876
892
  * host declares this per offline app; the apps store gates start() on it).
@@ -18,7 +18,7 @@
18
18
  * during React effects scheduled a re-render after the current effect batch.
19
19
  */
20
20
  import BluetoothSdk from "@mentra/bluetooth-sdk/internal"
21
- import type {OtaProgress, OtaStatus} from "@mentra/bluetooth-sdk/internal"
21
+ import type {OtaProgress, OtaStatus} from "@mentra/bluetooth-sdk"
22
22
  import GlobalEventEmitter from "../utils/GlobalEventEmitter"
23
23
  import {isGlassesConnected, useGlassesStore} from "../stores/glasses"
24
24
  import {resolveOtaManifestUrl} from "./otaManifestUrl"
@@ -1492,6 +1492,9 @@ class OtaInstallCoordinator {
1492
1492
  otaVersionUrl = this.hotspotManifestUrl
1493
1493
  this.maybeArmStuckWatchdog()
1494
1494
  }
1495
+ if (!otaVersionUrl) {
1496
+ throw new Error("OTA is disabled because this build has no immutable manifest pin")
1497
+ }
1495
1498
  console.log(`[OTA_PROGRESS] sending ota_start with ${this.selectedTransport} manifest URL: ${otaVersionUrl}`)
1496
1499
  nativeStartAttempted = true
1497
1500
  await BluetoothSdk.startOtaUpdate(otaVersionUrl)
@@ -1,9 +1,8 @@
1
1
  import BluetoothSdk from "@mentra/bluetooth-sdk/internal"
2
- import type {OtaUpdateInfo} from "@mentra/bluetooth-sdk/internal"
2
+ import type {OtaUpdateInfo} from "@mentra/bluetooth-sdk"
3
3
  import {getGlassesSystemTimeMs, isGlassesConnected, useGlassesStore, waitForGlassesState} from "../stores/glasses"
4
- import {SETTINGS, useSettingsStore} from "../stores/settings"
5
4
  import {maybeFixGlassesClockFromVersionInfo} from "./glassesClockSync"
6
- import {resolveOtaManifestUrl} from "./otaManifestUrl"
5
+ import {hasConfiguredModernOtaManifestPin, resolveOtaManifestUrl} from "./otaManifestUrl"
7
6
 
8
7
  /**
9
8
  * Phone-side mirror of ASG's `DOWNGRADE_FLOOR_VERSION_CODE`. Set to the versionCode of the first
@@ -127,7 +126,10 @@ async function fetchVersionInfoDetailed(url: string): Promise<ManifestFetchResul
127
126
  // 4xx: the manifest does not exist (or is gone) — permanent for this app
128
127
  // build, retrying cannot help. Everything else is transient server/network
129
128
  // trouble and retryable.
130
- return {json: null, failureReason: response.status >= 400 && response.status < 500 ? "pin_unavailable" : "network"}
129
+ return {
130
+ json: null,
131
+ failureReason: response.status >= 400 && response.status < 500 ? "pin_unavailable" : "network",
132
+ }
131
133
  }
132
134
  try {
133
135
  return {json: await response.json()}
@@ -194,12 +196,7 @@ export function getApkUpdateDirection(
194
196
  // Downgrades require an exact pin AND a target at/above the enabled floor. A non-positive floor
195
197
  // disables downgrades entirely, matching ASG's fail-closed DowngradeGate — so the phone never
196
198
  // offers what the glasses would refuse.
197
- if (
198
- serverVersion < currentVersion &&
199
- exactPin &&
200
- floorVersionCode > 0 &&
201
- serverVersion >= floorVersionCode
202
- ) {
199
+ if (serverVersion < currentVersion && exactPin && floorVersionCode > 0 && serverVersion >= floorVersionCode) {
203
200
  return "downgrade"
204
201
  }
205
202
  return null
@@ -403,17 +400,10 @@ export async function checkCurrentGlassesForUpdate(
403
400
  return emptyCheckResult("disconnected")
404
401
  }
405
402
 
406
- const embeddedManifestUrl = process.env.EXPO_PUBLIC_ASG_OTA_VERSION_URL?.trim()
407
- const superMode = useSettingsStore.getState().getSetting(SETTINGS.super_mode.key)
408
- const overrideUrl = useSettingsStore.getState().getSetting(SETTINGS.ota_version_url.key)
409
- const overrideActive = superMode && typeof overrideUrl === "string" && overrideUrl.trim() !== ""
410
- if (!embeddedManifestUrl && !overrideActive) {
411
- console.log("OTA: check skipped - mobile app has no embedded OTA manifest pin")
403
+ if (!hasConfiguredModernOtaManifestPin()) {
404
+ console.log("OTA: check skipped - this app or Engine package has no embedded OTA manifest pin")
412
405
  return emptyCheckResult("dev_build")
413
406
  }
414
- if (!embeddedManifestUrl) {
415
- console.log("OTA: no embedded manifest pin, but super-mode manifest override is active - checking anyway")
416
- }
417
407
 
418
408
  if (refreshVersionInfo) {
419
409
  void BluetoothSdk.requestVersionInfo().catch((error) => {
@@ -472,7 +462,17 @@ export async function checkCurrentGlassesForUpdate(
472
462
  }
473
463
 
474
464
  const manifestUrl = resolveOtaManifestUrl(useGlassesStore.getState().otaVersionUrl, buildNumber)
475
- const result = await checkForOtaUpdate(manifestUrl, buildNumber, mtkFirmwareVersion, besFirmwareVersion, floorVersionCode)
465
+ if (!manifestUrl) {
466
+ console.log("OTA: check skipped - no OTA manifest pin resolved")
467
+ return emptyCheckResult("dev_build")
468
+ }
469
+ const result = await checkForOtaUpdate(
470
+ manifestUrl,
471
+ buildNumber,
472
+ mtkFirmwareVersion,
473
+ besFirmwareVersion,
474
+ floorVersionCode,
475
+ )
476
476
 
477
477
  if (!result.hasCheckCompleted) {
478
478
  return {
@@ -87,6 +87,10 @@ export async function handleOtaClockSkewFromGlasses(
87
87
  lastOtaClockFixAt = Date.now()
88
88
  const {buildNumber, otaVersionUrl} = useGlassesStore.getState()
89
89
  const manifestUrl = resolveOtaManifestUrl(otaVersionUrl, buildNumber)
90
+ if (!manifestUrl) {
91
+ console.warn("[GlassesClockSync] OTA restart skipped because this build has no manifest pin")
92
+ return false
93
+ }
90
94
  console.log("[GlassesClockSync] ⏰ Restarting OTA with ota_start after clock fix")
91
95
  await BluetoothSdk.startOtaUpdate(manifestUrl)
92
96
  return true
@@ -1,4 +1,4 @@
1
- import type {OtaProgress, OtaStatus} from "@mentra/bluetooth-sdk/internal"
1
+ import type {OtaProgress, OtaStatus} from "@mentra/bluetooth-sdk"
2
2
 
3
3
  import {isLegacyShapedOtaStatus} from "./otaInstallPolicy"
4
4
 
@@ -1,5 +1,5 @@
1
1
  /** Shared OTA progress watchdog timings (see progress-legacy.tsx / progress.tsx). */
2
- import type {OtaProgress, OtaStatus} from "@mentra/bluetooth-sdk/internal"
2
+ import type {OtaProgress, OtaStatus} from "@mentra/bluetooth-sdk"
3
3
 
4
4
  /**
5
5
  * First glasses `build_number` (ASG `versionCode`) that uses `progress.tsx` with unified
@@ -1,4 +1,4 @@
1
- import type {OtaProgress, OtaProgressStatus, OtaStatus} from "@mentra/bluetooth-sdk/internal"
1
+ import type {OtaProgress, OtaProgressStatus, OtaStatus} from "@mentra/bluetooth-sdk"
2
2
 
3
3
  /** Normalized ota_status fields (snake_case) after bridging Core/expo (snake or camel). */
4
4
  export type NormalizedOtaStatusEvent = {