@mentra/engine 3.1.0-beta.166 → 3.1.0-beta.170
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 +54 -6
- package/build/react/MentraLiveOtaFlow.js.map +1 -1
- package/build/react/useMentraLiveOta.d.ts +9 -0
- package/build/react/useMentraLiveOta.d.ts.map +1 -1
- package/build/react/useMentraLiveOta.js +4 -0
- package/build/react/useMentraLiveOta.js.map +1 -1
- package/build/services/OtaArtifactDownloader.d.ts.map +1 -1
- package/build/services/OtaArtifactDownloader.js +10 -0
- package/build/services/OtaArtifactDownloader.js.map +1 -1
- package/build/services/OtaInstallCoordinator.d.ts +3 -0
- package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
- package/build/services/OtaInstallCoordinator.js +7 -1
- package/build/services/OtaInstallCoordinator.js.map +1 -1
- package/package.json +6 -6
- package/src/generated/releaseMetadata.ts +5 -5
- package/src/react/MentraLiveOtaFlow.tsx +66 -6
- package/src/react/useMentraLiveOta.ts +9 -0
- package/src/services/OtaArtifactDownloader.ts +10 -0
- package/src/services/OtaInstallCoordinator.ts +9 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mentra/engine",
|
|
3
|
-
"version": "3.1.0-beta.
|
|
3
|
+
"version": "3.1.0-beta.170",
|
|
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,11 +97,11 @@
|
|
|
97
97
|
"registry": "https://registry.npmjs.org/"
|
|
98
98
|
},
|
|
99
99
|
"dependencies": {
|
|
100
|
-
"@mentra/bluetooth-sdk": "3.1.0-beta.
|
|
101
|
-
"@mentra/cloud-client": "3.1.0-beta.
|
|
102
|
-
"@mentra/cloud-protocol": "3.1.0-beta.
|
|
103
|
-
"@mentra/crust": "3.1.0-beta.
|
|
104
|
-
"@mentra/miniapp": "3.1.0-beta.
|
|
100
|
+
"@mentra/bluetooth-sdk": "3.1.0-beta.170",
|
|
101
|
+
"@mentra/cloud-client": "3.1.0-beta.170",
|
|
102
|
+
"@mentra/cloud-protocol": "3.1.0-beta.170",
|
|
103
|
+
"@mentra/crust": "3.1.0-beta.170",
|
|
104
|
+
"@mentra/miniapp": "3.1.0-beta.170",
|
|
105
105
|
"buffer": "^6.0.3",
|
|
106
106
|
"events": "^3.3.0",
|
|
107
107
|
"react-native-marked": "8.1.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.1.0",
|
|
15
|
-
"releaseIdentity": "3.1.0-beta.
|
|
16
|
-
"releaseSetId": "mentra-3.1.0-beta.
|
|
17
|
-
"sourceCommit": "
|
|
18
|
-
"otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.1.0/mentra-live-ota-3.1.0-beta.
|
|
19
|
-
"otaManifestSha256": "
|
|
15
|
+
"releaseIdentity": "3.1.0-beta.170",
|
|
16
|
+
"releaseSetId": "mentra-3.1.0-beta.170",
|
|
17
|
+
"sourceCommit": "4b429bc36e827f56658c1c51f5248209fb05886f",
|
|
18
|
+
"otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.1.0/mentra-live-ota-3.1.0-beta.170.json",
|
|
19
|
+
"otaManifestSha256": "2f3a546e6e8c6339e464b7032274d42d09c48a06111af746ecf8c7af3f583508"
|
|
20
20
|
})
|
|
@@ -70,6 +70,20 @@ const DEFAULT_THEME: MentraLiveOtaFlowTheme = {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
const ENGLISH_COPY: Record<string, string> = {
|
|
73
|
+
"ota:downloadingToPhone": "Downloading update to phone...",
|
|
74
|
+
"ota:startingGlassesHotspot": "Starting glasses hotspot...",
|
|
75
|
+
"ota:connectingPhoneToGlasses": "Connecting phone to glasses...",
|
|
76
|
+
"ota:startingHotspotUpdate": "Starting update...",
|
|
77
|
+
"ota:transferringToGlasses": "Transferring update to glasses...",
|
|
78
|
+
"ota:installingOnGlasses": "Installing update on glasses...",
|
|
79
|
+
"ota:componentApk": "Glasses software",
|
|
80
|
+
"ota:componentMtk": "System firmware",
|
|
81
|
+
"ota:componentBes": "Bluetooth firmware",
|
|
82
|
+
"ota:updateFile": "File {{current}} of {{total}} · {{component}}",
|
|
83
|
+
"ota:updatePart": "Update {{current}} of {{total}} · {{component}}",
|
|
84
|
+
"ota:phoneFileProgress": "Each file downloads separately. Progress is for the current file.",
|
|
85
|
+
"ota:transferFileProgress": "Progress is for this file’s transfer from your phone.",
|
|
86
|
+
|
|
73
87
|
"common:continue": "Continue",
|
|
74
88
|
"common:done": "Done",
|
|
75
89
|
"ota:checkingForUpdates": "Checking for updates",
|
|
@@ -125,6 +139,12 @@ const ENGLISH_COPY: Record<string, string> = {
|
|
|
125
139
|
"Your glasses restarted with new firmware. One more step: they'll now continue to the required version.",
|
|
126
140
|
}
|
|
127
141
|
|
|
142
|
+
const componentCopyKey = {
|
|
143
|
+
apk: "ota:componentApk",
|
|
144
|
+
mtk: "ota:componentMtk",
|
|
145
|
+
bes: "ota:componentBes",
|
|
146
|
+
} as const
|
|
147
|
+
|
|
128
148
|
function defaultTranslate(key: string, options?: Record<string, string>): string {
|
|
129
149
|
let value = ENGLISH_COPY[key] ?? key
|
|
130
150
|
for (const [name, replacement] of Object.entries(options ?? {})) {
|
|
@@ -374,28 +394,65 @@ function OtaFlowContent({
|
|
|
374
394
|
}
|
|
375
395
|
|
|
376
396
|
if (state.screen === "starting" || state.screen === "preparing_hotspot") {
|
|
377
|
-
const title =
|
|
397
|
+
const title = translate(
|
|
378
398
|
state.hotspotPhase === "downloading"
|
|
379
|
-
? "
|
|
399
|
+
? "ota:downloadingToPhone"
|
|
380
400
|
: state.hotspotPhase === "starting_hotspot"
|
|
381
|
-
? "
|
|
401
|
+
? "ota:startingGlassesHotspot"
|
|
382
402
|
: state.hotspotPhase === "joining_hotspot"
|
|
383
|
-
? "
|
|
384
|
-
: "
|
|
403
|
+
? "ota:connectingPhoneToGlasses"
|
|
404
|
+
: "ota:startingHotspotUpdate",
|
|
405
|
+
)
|
|
406
|
+
const artifact = state.hotspotPhase === "downloading" ? state.hotspotArtifact : null
|
|
385
407
|
return (
|
|
386
408
|
<FlowPage colors={colors} icon="download" title={title}>
|
|
409
|
+
{artifact ? (
|
|
410
|
+
<BodyText colors={colors}>
|
|
411
|
+
{translate("ota:updateFile", {
|
|
412
|
+
current: String(artifact.index + 1),
|
|
413
|
+
total: String(artifact.totalCount),
|
|
414
|
+
component: translate(componentCopyKey[artifact.kind]),
|
|
415
|
+
})}
|
|
416
|
+
</BodyText>
|
|
417
|
+
) : null}
|
|
387
418
|
{state.hotspotPhase === "downloading" && state.hotspotArtifactPercent !== null ? (
|
|
388
419
|
<PercentText colors={colors} percent={state.hotspotArtifactPercent} />
|
|
389
420
|
) : null}
|
|
390
421
|
<ActivityIndicator size="large" color={colors.foreground} />
|
|
422
|
+
{state.hotspotPhase === "downloading" ? (
|
|
423
|
+
<BodyText colors={colors}>{translate("ota:phoneFileProgress")}</BodyText>
|
|
424
|
+
) : null}
|
|
391
425
|
<BodyText colors={colors}>Do not disconnect your glasses</BodyText>
|
|
392
426
|
</FlowPage>
|
|
393
427
|
)
|
|
394
428
|
}
|
|
395
429
|
|
|
396
430
|
if (state.screen === "updating") {
|
|
431
|
+
const hotspot = state.transport === "hotspot"
|
|
432
|
+
const title = hotspot
|
|
433
|
+
? translate(state.phase === "download" ? "ota:transferringToGlasses" : "ota:installingOnGlasses")
|
|
434
|
+
: state.phase === "download"
|
|
435
|
+
? "Downloading..."
|
|
436
|
+
: "Installing..."
|
|
437
|
+
const component = state.step ? translate(componentCopyKey[state.step]) : null
|
|
438
|
+
const hasStepCount =
|
|
439
|
+
state.currentStep !== null &&
|
|
440
|
+
state.totalSteps !== null &&
|
|
441
|
+
state.currentStep > 0 &&
|
|
442
|
+
state.currentStep <= state.totalSteps
|
|
397
443
|
return (
|
|
398
|
-
<FlowPage colors={colors} icon="download" title={
|
|
444
|
+
<FlowPage colors={colors} icon="download" title={title}>
|
|
445
|
+
{hotspot && component ? (
|
|
446
|
+
<BodyText colors={colors}>
|
|
447
|
+
{hasStepCount
|
|
448
|
+
? translate("ota:updatePart", {
|
|
449
|
+
current: String(state.currentStep),
|
|
450
|
+
total: String(state.totalSteps),
|
|
451
|
+
component,
|
|
452
|
+
})
|
|
453
|
+
: component}
|
|
454
|
+
</BodyText>
|
|
455
|
+
) : null}
|
|
399
456
|
{state.installingApkOnly ? (
|
|
400
457
|
<ActivityIndicator size="large" color={colors.foreground} />
|
|
401
458
|
) : (
|
|
@@ -409,6 +466,9 @@ function OtaFlowContent({
|
|
|
409
466
|
</>
|
|
410
467
|
)}
|
|
411
468
|
<BodyText colors={colors}>Do not disconnect your glasses</BodyText>
|
|
469
|
+
{hotspot && state.phase === "download" ? (
|
|
470
|
+
<BodyText colors={colors}>{translate("ota:transferFileProgress")}</BodyText>
|
|
471
|
+
) : null}
|
|
412
472
|
{state.versionChange && state.phase === "install" ? (
|
|
413
473
|
<BodyText colors={colors}>{translate("ota:downgradeDuration")}</BodyText>
|
|
414
474
|
) : null}
|
|
@@ -82,6 +82,11 @@ export type MentraLiveOtaState = {
|
|
|
82
82
|
hotspotSupported: boolean
|
|
83
83
|
hotspotPhase: MentraLiveOtaHotspotPhase
|
|
84
84
|
hotspotArtifactPercent: number | null
|
|
85
|
+
/**
|
|
86
|
+
* Current phone download context. Index is zero-based; percent applies to this file only.
|
|
87
|
+
* Optional for source compatibility; this hook returns null when no file is active.
|
|
88
|
+
*/
|
|
89
|
+
hotspotArtifact?: {kind: MentraLiveOtaStep; index: number; totalCount: number} | null
|
|
85
90
|
phase: MentraLiveOtaInstallPhase | null
|
|
86
91
|
step: MentraLiveOtaStep | null
|
|
87
92
|
currentStep: number | null
|
|
@@ -624,6 +629,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
624
629
|
hotspotSupported,
|
|
625
630
|
hotspotPhase: "idle",
|
|
626
631
|
hotspotArtifactPercent: null,
|
|
632
|
+
hotspotArtifact: null,
|
|
627
633
|
phase: null,
|
|
628
634
|
step: null,
|
|
629
635
|
currentStep: null,
|
|
@@ -678,6 +684,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
678
684
|
hotspotSupported,
|
|
679
685
|
hotspotPhase: "idle",
|
|
680
686
|
hotspotArtifactPercent: null,
|
|
687
|
+
hotspotArtifact: null,
|
|
681
688
|
phase: null,
|
|
682
689
|
step: null,
|
|
683
690
|
currentStep: null,
|
|
@@ -732,6 +739,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
732
739
|
}
|
|
733
740
|
: null
|
|
734
741
|
const totalSteps = installSnapshot.otaStatus?.totalSteps ?? null
|
|
742
|
+
const artifact = installSnapshot.hotspotArtifact
|
|
735
743
|
const changelogs = screen === "complete" ? releaseChangelogsForActiveChain() : []
|
|
736
744
|
return {
|
|
737
745
|
screen,
|
|
@@ -747,6 +755,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
747
755
|
hotspotSupported,
|
|
748
756
|
hotspotPhase: installSnapshot.hotspotPhase,
|
|
749
757
|
hotspotArtifactPercent: installSnapshot.hotspotArtifactPercent,
|
|
758
|
+
hotspotArtifact: artifact ? {kind: artifact.kind, index: artifact.index, totalCount: artifact.totalCount} : null,
|
|
750
759
|
phase: installSnapshot.otaStatus?.phase ?? installSnapshot.otaProgress?.stage ?? null,
|
|
751
760
|
step: installSnapshot.otaStatus?.stepType ?? null,
|
|
752
761
|
currentStep: installSnapshot.otaStatus?.currentStep ?? null,
|
|
@@ -139,6 +139,16 @@ export async function prepareArtifacts(
|
|
|
139
139
|
const prepared: PreparedOtaArtifact[] = []
|
|
140
140
|
for (let index = 0; index < plan.length; index++) {
|
|
141
141
|
const entry = plan[index]
|
|
142
|
+
// Announce each file before cache verification or native download callbacks,
|
|
143
|
+
// so the previous file's 100% cannot linger under the next file's label.
|
|
144
|
+
onProgress?.({
|
|
145
|
+
kind: entry.kind,
|
|
146
|
+
index,
|
|
147
|
+
totalCount: plan.length,
|
|
148
|
+
artifactPercent: 0,
|
|
149
|
+
bytesWritten: 0,
|
|
150
|
+
contentLength: 0,
|
|
151
|
+
})
|
|
142
152
|
|
|
143
153
|
const cachedPath = `${directory}/${entry.sha256}`
|
|
144
154
|
if (await RNFS.exists(cachedPath)) {
|
|
@@ -22,6 +22,7 @@ import GlobalEventEmitter from "../utils/GlobalEventEmitter"
|
|
|
22
22
|
import {isGlassesConnected, useGlassesStore} from "../stores/glasses"
|
|
23
23
|
import {resolveOtaManifestUrl} from "./otaManifestUrl"
|
|
24
24
|
import {hotspotOtaTransport, type HotspotOtaPhase} from "./HotspotOtaTransport"
|
|
25
|
+
import type {OtaArtifactDownloadProgress} from "./OtaArtifactDownloader"
|
|
25
26
|
import type {OtaCheckCurrentGlassesResult} from "./OtaUpdateCheckService"
|
|
26
27
|
import {deriveDisplayState, type DisplayState} from "./otaDisplayState"
|
|
27
28
|
import {
|
|
@@ -171,6 +172,7 @@ export interface OtaInstallSnapshot {
|
|
|
171
172
|
/** Pre-ota_start phone staging/join state for a hotspot attempt. */
|
|
172
173
|
hotspotPhase: HotspotOtaPhase
|
|
173
174
|
hotspotArtifactPercent: number | null
|
|
175
|
+
hotspotArtifact: OtaArtifactDownloadProgress | null
|
|
174
176
|
/** Transport selected from the checked glasses capabilities and Wi-Fi state. */
|
|
175
177
|
transport: "wifi" | "hotspot"
|
|
176
178
|
}
|
|
@@ -189,6 +191,7 @@ class OtaInstallCoordinator {
|
|
|
189
191
|
private hotspotManifestUrl: string | null = null
|
|
190
192
|
private hotspotPhase: HotspotOtaPhase = "idle"
|
|
191
193
|
private hotspotArtifactPercent: number | null = null
|
|
194
|
+
private hotspotArtifact: OtaArtifactDownloadProgress | null = null
|
|
192
195
|
|
|
193
196
|
// Genuinely session-local state (was component state/refs).
|
|
194
197
|
private errorMsg = ""
|
|
@@ -303,6 +306,7 @@ class OtaInstallCoordinator {
|
|
|
303
306
|
this.hotspotManifestUrl = null
|
|
304
307
|
this.hotspotPhase = "idle"
|
|
305
308
|
this.hotspotArtifactPercent = null
|
|
309
|
+
this.hotspotArtifact = null
|
|
306
310
|
return this.selectedTransport
|
|
307
311
|
}
|
|
308
312
|
|
|
@@ -510,6 +514,7 @@ class OtaInstallCoordinator {
|
|
|
510
514
|
versionChangePhase: this.deriveVersionChangePhase(connected),
|
|
511
515
|
hotspotPhase: this.hotspotPhase,
|
|
512
516
|
hotspotArtifactPercent: this.hotspotArtifactPercent,
|
|
517
|
+
hotspotArtifact: this.hotspotArtifact ? {...this.hotspotArtifact} : null,
|
|
513
518
|
transport: this.selectedTransport,
|
|
514
519
|
}
|
|
515
520
|
}
|
|
@@ -1501,7 +1506,9 @@ class OtaInstallCoordinator {
|
|
|
1501
1506
|
}
|
|
1502
1507
|
this.hotspotManifestUrl = await hotspotOtaTransport.prepare(this.preparedCheckResult, (progress) => {
|
|
1503
1508
|
this.hotspotPhase = progress.phase
|
|
1504
|
-
this.
|
|
1509
|
+
this.hotspotArtifact = progress.artifact ? {...progress.artifact} : null
|
|
1510
|
+
this.hotspotArtifactPercent =
|
|
1511
|
+
progress.artifact && progress.artifact.contentLength > 0 ? progress.artifact.artifactPercent : null
|
|
1505
1512
|
this.emitInternalChange()
|
|
1506
1513
|
})
|
|
1507
1514
|
}
|
|
@@ -1581,6 +1588,7 @@ class OtaInstallCoordinator {
|
|
|
1581
1588
|
this.hotspotManifestUrl = null
|
|
1582
1589
|
this.hotspotPhase = "idle"
|
|
1583
1590
|
this.hotspotArtifactPercent = null
|
|
1591
|
+
this.hotspotArtifact = null
|
|
1584
1592
|
this.emitInternalChange()
|
|
1585
1593
|
}
|
|
1586
1594
|
|