@mentra/engine 3.4.0-dev.966 → 3.4.0-dev.973
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.
- package/build/generated/releaseMetadata.js +5 -5
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/react/MentraLiveOtaFlow.d.ts.map +1 -1
- package/build/react/MentraLiveOtaFlow.js +1 -21
- package/build/react/MentraLiveOtaFlow.js.map +1 -1
- package/build/react/useMentraLiveOta.d.ts.map +1 -1
- package/build/react/useMentraLiveOta.js +8 -7
- package/build/react/useMentraLiveOta.js.map +1 -1
- package/build/services/OtaInstallCoordinator.d.ts +1 -0
- package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
- package/build/services/OtaInstallCoordinator.js +34 -10
- package/build/services/OtaInstallCoordinator.js.map +1 -1
- package/package.json +8 -8
- package/src/generated/releaseMetadata.ts +5 -5
- package/src/react/MentraLiveOtaFlow.tsx +1 -28
- package/src/react/useMentraLiveOta.ts +8 -7
- package/src/services/OtaInstallCoordinator.ts +34 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mentra/engine",
|
|
3
|
-
"version": "3.4.0-dev.
|
|
3
|
+
"version": "3.4.0-dev.973",
|
|
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",
|
|
@@ -97,13 +97,13 @@
|
|
|
97
97
|
"registry": "https://registry.npmjs.org/"
|
|
98
98
|
},
|
|
99
99
|
"dependencies": {
|
|
100
|
-
"@mentra/acs-meeting": "3.4.0-dev.
|
|
101
|
-
"@mentra/glasses-media": "3.4.0-dev.
|
|
102
|
-
"@mentra/bluetooth-sdk": "3.4.0-dev.
|
|
103
|
-
"@mentra/cloud-client": "3.4.0-dev.
|
|
104
|
-
"@mentra/cloud-protocol": "3.4.0-dev.
|
|
105
|
-
"@mentra/crust": "3.4.0-dev.
|
|
106
|
-
"@mentra/miniapp": "3.4.0-dev.
|
|
100
|
+
"@mentra/acs-meeting": "3.4.0-dev.973",
|
|
101
|
+
"@mentra/glasses-media": "3.4.0-dev.973",
|
|
102
|
+
"@mentra/bluetooth-sdk": "3.4.0-dev.973",
|
|
103
|
+
"@mentra/cloud-client": "3.4.0-dev.973",
|
|
104
|
+
"@mentra/cloud-protocol": "3.4.0-dev.973",
|
|
105
|
+
"@mentra/crust": "3.4.0-dev.973",
|
|
106
|
+
"@mentra/miniapp": "3.4.0-dev.973",
|
|
107
107
|
"buffer": "^6.0.3",
|
|
108
108
|
"events": "^3.3.0",
|
|
109
109
|
"jszip": "^3.10.1",
|
|
@@ -12,9 +12,9 @@ export interface EngineReleaseMetadata {
|
|
|
12
12
|
export const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({
|
|
13
13
|
"schemaVersion": 1,
|
|
14
14
|
"familyBaseVersion": "3.4.0",
|
|
15
|
-
"releaseIdentity": "3.4.0-dev.
|
|
16
|
-
"releaseSetId": "mentra-3.4.0-dev.
|
|
17
|
-
"sourceCommit": "
|
|
18
|
-
"otaManifestUrl": "https://artifactscdn.mentraglass.com/Mentra-Community/MentraOS/releases/mentra-builds-v3.4.0/mentra-live-ota-3.4.0-dev.
|
|
19
|
-
"otaManifestSha256": "
|
|
15
|
+
"releaseIdentity": "3.4.0-dev.973",
|
|
16
|
+
"releaseSetId": "mentra-3.4.0-dev.973",
|
|
17
|
+
"sourceCommit": "fd4df756e0e979c229fee7ee145db7f7ee2ed1b4",
|
|
18
|
+
"otaManifestUrl": "https://artifactscdn.mentraglass.com/Mentra-Community/MentraOS/releases/mentra-builds-v3.4.0/mentra-live-ota-3.4.0-dev.973.json",
|
|
19
|
+
"otaManifestSha256": "79f09b7f9267d33febe4346b9aae9595c14cbff97caf661ee301e582b4bbbb66"
|
|
20
20
|
})
|
|
@@ -79,8 +79,6 @@ const ENGLISH_COPY: Record<string, string> = {
|
|
|
79
79
|
"ota:startingHotspotUpdate": "Starting update…",
|
|
80
80
|
"ota:transferringToGlasses": "Transferring update to glasses…",
|
|
81
81
|
"ota:installingOnGlasses": "Installing update on glasses…",
|
|
82
|
-
"ota:transportHotspot": "Using glasses hotspot",
|
|
83
|
-
"ota:transportWifi": "Using Wi-Fi",
|
|
84
82
|
"ota:componentApk": "Glasses software",
|
|
85
83
|
"ota:componentMtk": "System firmware",
|
|
86
84
|
"ota:componentBes": "Bluetooth firmware",
|
|
@@ -242,36 +240,12 @@ export function MentraLiveOtaPreview({
|
|
|
242
240
|
}
|
|
243
241
|
|
|
244
242
|
function OtaFlowFrame({allowDevSkip, colors, controller, deviceName, style, superMode, translate}: OtaFlowFrameProps) {
|
|
245
|
-
const {state} = controller
|
|
246
|
-
const transportLabel =
|
|
247
|
-
state.transport &&
|
|
248
|
-
[
|
|
249
|
-
"starting",
|
|
250
|
-
"preparing_hotspot",
|
|
251
|
-
"updating",
|
|
252
|
-
"restarting",
|
|
253
|
-
"verifying",
|
|
254
|
-
"complete",
|
|
255
|
-
"failed",
|
|
256
|
-
"disconnected",
|
|
257
|
-
].includes(state.screen)
|
|
258
|
-
? translate(state.transport === "hotspot" ? "ota:transportHotspot" : "ota:transportWifi")
|
|
259
|
-
: null
|
|
260
|
-
|
|
261
243
|
return (
|
|
262
244
|
<SafeAreaView style={[styles.safeArea, {backgroundColor: colors.background}, style]}>
|
|
263
245
|
<View style={styles.header}>
|
|
264
246
|
<View />
|
|
265
247
|
<MentraMark color={colors.primary} />
|
|
266
248
|
</View>
|
|
267
|
-
{transportLabel ? (
|
|
268
|
-
<Text
|
|
269
|
-
accessibilityLabel={transportLabel}
|
|
270
|
-
style={[styles.transport, {color: colors.textDim}]}
|
|
271
|
-
testID={`ota.transport.${state.transport}`}>
|
|
272
|
-
{transportLabel}
|
|
273
|
-
</Text>
|
|
274
|
-
) : null}
|
|
275
249
|
<OtaFlowContent
|
|
276
250
|
allowDevSkip={allowDevSkip}
|
|
277
251
|
colors={colors}
|
|
@@ -369,7 +343,7 @@ function OtaFlowContent({
|
|
|
369
343
|
<>
|
|
370
344
|
<FlowButton
|
|
371
345
|
colors={colors}
|
|
372
|
-
disabled={!state.wifiStatusKnown}
|
|
346
|
+
disabled={state.screen === "wifi_required" ? !state.wifiStatusKnown : !state.canInstall}
|
|
373
347
|
label={translate(state.screen === "wifi_required" ? "ota:setupWifi" : "ota:updateNow")}
|
|
374
348
|
onPress={state.screen === "wifi_required" ? controller.openWifiSetup : controller.install}
|
|
375
349
|
/>
|
|
@@ -924,7 +898,6 @@ const styles = StyleSheet.create({
|
|
|
924
898
|
svgIcon: {alignItems: "center", height: 72, justifyContent: "center", width: 72},
|
|
925
899
|
title: {fontSize: 20, fontWeight: "600", textAlign: "center"},
|
|
926
900
|
body: {fontSize: 14, lineHeight: 20, maxWidth: 420, textAlign: "center"},
|
|
927
|
-
transport: {fontSize: 12, lineHeight: 16, textAlign: "center"},
|
|
928
901
|
errorCode: {fontSize: 12, fontVariant: ["tabular-nums"], lineHeight: 16, opacity: 0.7, textAlign: "center"},
|
|
929
902
|
percent: {fontSize: 30, fontVariant: ["tabular-nums"], fontWeight: "700"},
|
|
930
903
|
progressTrack: {borderRadius: 4, height: 8, maxWidth: 420, overflow: "hidden", width: "100%"},
|
|
@@ -325,7 +325,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
325
325
|
return false
|
|
326
326
|
if (installActionPendingRef.current || !isOtaAutoChainActive() || !result.updateInfo) return false
|
|
327
327
|
const snapshot = ota.snapshot()
|
|
328
|
-
if (
|
|
328
|
+
if (snapshot.hotspotOtaVersion !== 1 && (!snapshot.wifiStatusKnown || !snapshot.wifiConnected)) return false
|
|
329
329
|
|
|
330
330
|
const admission = tryAdvanceOtaAutoChain(
|
|
331
331
|
otaAutoChainFingerprint(result),
|
|
@@ -484,7 +484,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
484
484
|
const fromVersion = currentReleaseVersion(ota.snapshot().appVersion)
|
|
485
485
|
setOfferedReleaseTransition(result.releaseVersion ? {fromVersion, toVersion: result.releaseVersion} : null)
|
|
486
486
|
}
|
|
487
|
-
if (isOtaAutoChainActive() && !ota.snapshot().wifiStatusKnown) {
|
|
487
|
+
if (isOtaAutoChainActive() && !ota.snapshot().wifiStatusKnown && ota.snapshot().hotspotOtaVersion !== 1) {
|
|
488
488
|
checkCompletedRef.current = true
|
|
489
489
|
return
|
|
490
490
|
}
|
|
@@ -534,7 +534,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
534
534
|
!runtimeReady ||
|
|
535
535
|
page !== "check" ||
|
|
536
536
|
(checkState !== "checking" && checkState !== "update_available") ||
|
|
537
|
-
!otaSnapshot.wifiStatusKnown ||
|
|
537
|
+
(!otaSnapshot.wifiStatusKnown && otaSnapshot.hotspotOtaVersion !== 1) ||
|
|
538
538
|
!isOtaAutoChainActive()
|
|
539
539
|
) {
|
|
540
540
|
return
|
|
@@ -624,7 +624,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
624
624
|
setBatteryBlocked(true)
|
|
625
625
|
return
|
|
626
626
|
}
|
|
627
|
-
if (!snapshot.wifiStatusKnown) {
|
|
627
|
+
if (!snapshot.wifiStatusKnown && snapshot.hotspotOtaVersion !== 1) {
|
|
628
628
|
check()
|
|
629
629
|
return
|
|
630
630
|
}
|
|
@@ -667,6 +667,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
667
667
|
const finish = useCallback(async () => {
|
|
668
668
|
if (deviceRevision !== otaDeviceSessionRevision()) return
|
|
669
669
|
if (page === "check") {
|
|
670
|
+
stopOtaAutoChain()
|
|
670
671
|
onFinishedRef.current?.()
|
|
671
672
|
return
|
|
672
673
|
}
|
|
@@ -702,7 +703,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
702
703
|
|
|
703
704
|
const state = useMemo<MentraLiveOtaState>(() => {
|
|
704
705
|
const hotspotSupported = otaSnapshot.hotspotOtaVersion === 1
|
|
705
|
-
const canInstall = otaSnapshot.wifiStatusKnown &&
|
|
706
|
+
const canInstall = hotspotSupported || (otaSnapshot.wifiStatusKnown && otaSnapshot.wifiConnected)
|
|
706
707
|
const autoChainActive = isOtaAutoChainActive()
|
|
707
708
|
if (!runtimeReady) {
|
|
708
709
|
return {
|
|
@@ -776,7 +777,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
776
777
|
screen,
|
|
777
778
|
connected: otaSnapshot.connected,
|
|
778
779
|
batteryLevel: otaSnapshot.batteryLevel,
|
|
779
|
-
transport:
|
|
780
|
+
transport: hotspotSupported ? "hotspot" : otaSnapshot.wifiConnected ? "wifi" : null,
|
|
780
781
|
updateRequired: isUpdateRequired,
|
|
781
782
|
versionChange: isVersionChange,
|
|
782
783
|
versionChangeConverged: false,
|
|
@@ -896,7 +897,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
896
897
|
canFinish: screen === "complete" || (screen === "failed" && requiresGlassesReboot),
|
|
897
898
|
canDismiss: false,
|
|
898
899
|
canDiscard: screen === "disconnected",
|
|
899
|
-
canOpenWifiSetup: screen === "failed" && showChangeWifi,
|
|
900
|
+
canOpenWifiSetup: screen === "failed" && !hotspotSupported && showChangeWifi,
|
|
900
901
|
continueDisabled: installSnapshot.continueButtonDisabled,
|
|
901
902
|
completedUpdate: false,
|
|
902
903
|
releaseTransition: releaseTransitionFromRange(otaAutoChainReleaseRange()),
|
|
@@ -27,7 +27,7 @@ import {waitForOtaInternet} from "./OtaInternetRecovery"
|
|
|
27
27
|
import {submitOtaFailureReport, type OtaFailureReason} from "./OtaFailureReportService"
|
|
28
28
|
import type {OtaArtifactDownloadProgress} from "./OtaArtifactDownloader"
|
|
29
29
|
import {otaDeviceSessionRevision, subscribeOtaDeviceSession} from "./OtaDeviceSession"
|
|
30
|
-
import {isOtaCheckForCurrentDevice} from "./OtaUpdateCheckService"
|
|
30
|
+
import {checkCurrentGlassesForUpdate, isOtaCheckForCurrentDevice} from "./OtaUpdateCheckService"
|
|
31
31
|
import type {OtaCheckCurrentGlassesResult} from "./OtaUpdateCheckService"
|
|
32
32
|
import {deriveDisplayState, type DisplayState} from "./otaDisplayState"
|
|
33
33
|
import {
|
|
@@ -325,17 +325,17 @@ class OtaInstallCoordinator {
|
|
|
325
325
|
prepare(checkResult: OtaCheckCurrentGlassesResult): "wifi" | "hotspot" {
|
|
326
326
|
if (!isOtaCheckForCurrentDevice(checkResult)) throw new Error("The checked glasses have changed")
|
|
327
327
|
const state = useGlassesStore.getState()
|
|
328
|
-
|
|
328
|
+
const hotspotSupported = state.hotspotOtaVersion === 1
|
|
329
|
+
if (!hotspotSupported && !state.wifiStatusKnown) {
|
|
329
330
|
throw new Error("Glasses Wi-Fi status is not available")
|
|
330
331
|
}
|
|
331
|
-
const hotspotSupported = state.hotspotOtaVersion === 1
|
|
332
332
|
const wifiConnected = state.wifi.state === "connected"
|
|
333
333
|
if (!wifiConnected && !hotspotSupported) {
|
|
334
334
|
throw new Error("Connected glasses require Wi-Fi for OTA")
|
|
335
335
|
}
|
|
336
336
|
this.reportAttemptId = `${Date.now()}-${++this.reportAttemptSequence}`
|
|
337
337
|
this.preparedCheckResult = checkResult
|
|
338
|
-
this.selectedTransport =
|
|
338
|
+
this.selectedTransport = hotspotSupported ? "hotspot" : "wifi"
|
|
339
339
|
this.observedTransport = this.selectedTransport
|
|
340
340
|
this.hotspotManifestUrl = null
|
|
341
341
|
this.hotspotPhase = "idle"
|
|
@@ -345,6 +345,18 @@ class OtaInstallCoordinator {
|
|
|
345
345
|
return this.selectedTransport
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
+
private initializeCheckedInstall(selected: OtaCheckCurrentGlassesResult["updateInfo"]): void {
|
|
349
|
+
const target =
|
|
350
|
+
selected?.isDowngrade && typeof selected.versionCode === "number" && selected.versionCode > 0
|
|
351
|
+
? selected.versionCode
|
|
352
|
+
: null
|
|
353
|
+
this.versionChangeSession = target !== null
|
|
354
|
+
this.versionChangeTarget = target
|
|
355
|
+
if (target !== null) {
|
|
356
|
+
console.log(`[OTA_PROGRESS] version-change (downgrade) session, target=${target}`)
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
348
360
|
/**
|
|
349
361
|
* Bind the state machine to a mounted progress screen. Idempotent per
|
|
350
362
|
* attach/detach cycle. Runs the initial-mount arbitration (send ota_start
|
|
@@ -364,11 +376,7 @@ class OtaInstallCoordinator {
|
|
|
364
376
|
// A late completion from the previous OTA step can clear the store's offer
|
|
365
377
|
// between check and mount. Keep this install bound to its prepared check.
|
|
366
378
|
const selected = this.preparedCheckResult ? this.preparedCheckResult.updateInfo : initialState.otaUpdateAvailable
|
|
367
|
-
|
|
368
|
-
this.versionChangeSession = true
|
|
369
|
-
this.versionChangeTarget = selected.versionCode
|
|
370
|
-
console.log(`[OTA_PROGRESS] version-change (downgrade) session, target=${selected.versionCode}`)
|
|
371
|
-
}
|
|
379
|
+
this.initializeCheckedInstall(selected)
|
|
372
380
|
const recoveryTarget = this.otaStartOwnership?.versionChangeTarget
|
|
373
381
|
if (this.otaStartOwnership?.outcome === "acknowledged" && recoveryTarget !== undefined) {
|
|
374
382
|
// The progress route is an observer of this acknowledged install. A remount
|
|
@@ -1648,6 +1656,9 @@ class OtaInstallCoordinator {
|
|
|
1648
1656
|
if (existingOwnership?.outcome === "rejected" && !this.prepareFreshOtaStartAttempt()) {
|
|
1649
1657
|
return Promise.resolve()
|
|
1650
1658
|
}
|
|
1659
|
+
// Capability is cleared on link loss before version_info returns. Preserve
|
|
1660
|
+
// hotspot selection for these paired glasses; the device boundary resets it.
|
|
1661
|
+
if (useGlassesStore.getState().hotspotOtaVersion === 1) this.selectedTransport = "hotspot"
|
|
1651
1662
|
const ownership: OtaStartOwnership = {
|
|
1652
1663
|
outcome: "pending",
|
|
1653
1664
|
promise: Promise.resolve(),
|
|
@@ -1674,7 +1685,20 @@ class OtaInstallCoordinator {
|
|
|
1674
1685
|
if (this.selectedTransport === "hotspot") {
|
|
1675
1686
|
if (!this.hotspotManifestUrl) {
|
|
1676
1687
|
if (!this.preparedCheckResult) {
|
|
1677
|
-
|
|
1688
|
+
const result = await checkCurrentGlassesForUpdate()
|
|
1689
|
+
if (!isCurrentAttempt()) return
|
|
1690
|
+
if (!result.hasCheckCompleted || !result.updateAvailable || !result.manifestBody) {
|
|
1691
|
+
throw new Error("No selected OTA check is available for hotspot staging")
|
|
1692
|
+
}
|
|
1693
|
+
this.preparedCheckResult = result
|
|
1694
|
+
this.initializeCheckedInstall(result.updateInfo)
|
|
1695
|
+
// The recovery check can reveal a downgrade after the start attempt
|
|
1696
|
+
// was created. Give that detour its full bounded recovery deadline.
|
|
1697
|
+
if (this.versionChangeSession) {
|
|
1698
|
+
ownership.deadlineAt = Date.now() + LEGACY_GLOBAL_OTA_TIMEOUT_MS
|
|
1699
|
+
this.clearGlobalTimeout()
|
|
1700
|
+
this.maybeStartGlobalTimeout()
|
|
1701
|
+
}
|
|
1678
1702
|
}
|
|
1679
1703
|
const manifestUrl = await hotspotOtaTransport.prepare(this.preparedCheckResult, (progress) => {
|
|
1680
1704
|
if (!isCurrentAttempt()) return
|