@mentra/engine 3.2.0-dev.216 → 3.2.0-dev.221
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/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/build/react/MentraLiveOtaFlow.d.ts.map +1 -1
- package/build/react/MentraLiveOtaFlow.js +19 -3
- package/build/react/MentraLiveOtaFlow.js.map +1 -1
- package/build/react/useMentraLiveOta.d.ts +12 -0
- package/build/react/useMentraLiveOta.d.ts.map +1 -1
- package/build/react/useMentraLiveOta.js +31 -5
- package/build/react/useMentraLiveOta.js.map +1 -1
- package/build/services/OtaErrorMapping.d.ts +25 -2
- package/build/services/OtaErrorMapping.d.ts.map +1 -1
- package/build/services/OtaErrorMapping.js +68 -23
- package/build/services/OtaErrorMapping.js.map +1 -1
- package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
- package/build/services/OtaInstallCoordinator.js +6 -1
- package/build/services/OtaInstallCoordinator.js.map +1 -1
- package/package.json +7 -7
- package/src/generated/releaseMetadata.ts +5 -5
- package/src/index.ts +6 -0
- package/src/react/MentraLiveOtaFlow.tsx +22 -3
- package/src/react/useMentraLiveOta.ts +44 -6
- package/src/services/OtaErrorMapping.ts +77 -25
- package/src/services/OtaInstallCoordinator.ts +6 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mentra/engine",
|
|
3
|
-
"version": "3.2.0-dev.
|
|
3
|
+
"version": "3.2.0-dev.221",
|
|
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,12 +97,12 @@
|
|
|
97
97
|
"registry": "https://registry.npmjs.org/"
|
|
98
98
|
},
|
|
99
99
|
"dependencies": {
|
|
100
|
-
"@mentra/acs-meeting": "3.2.0-dev.
|
|
101
|
-
"@mentra/bluetooth-sdk": "3.2.0-dev.
|
|
102
|
-
"@mentra/cloud-client": "3.2.0-dev.
|
|
103
|
-
"@mentra/cloud-protocol": "3.2.0-dev.
|
|
104
|
-
"@mentra/crust": "3.2.0-dev.
|
|
105
|
-
"@mentra/miniapp": "3.2.0-dev.
|
|
100
|
+
"@mentra/acs-meeting": "3.2.0-dev.221",
|
|
101
|
+
"@mentra/bluetooth-sdk": "3.2.0-dev.221",
|
|
102
|
+
"@mentra/cloud-client": "3.2.0-dev.221",
|
|
103
|
+
"@mentra/cloud-protocol": "3.2.0-dev.221",
|
|
104
|
+
"@mentra/crust": "3.2.0-dev.221",
|
|
105
|
+
"@mentra/miniapp": "3.2.0-dev.221",
|
|
106
106
|
"buffer": "^6.0.3",
|
|
107
107
|
"events": "^3.3.0",
|
|
108
108
|
"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.2.0",
|
|
15
|
-
"releaseIdentity": "3.2.0-dev.
|
|
16
|
-
"releaseSetId": "mentra-3.2.0-dev.
|
|
17
|
-
"sourceCommit": "
|
|
18
|
-
"otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.
|
|
19
|
-
"otaManifestSha256": "
|
|
15
|
+
"releaseIdentity": "3.2.0-dev.221",
|
|
16
|
+
"releaseSetId": "mentra-3.2.0-dev.221",
|
|
17
|
+
"sourceCommit": "3837a3055e7a69e18553ce51c3a97579d2ad3b83",
|
|
18
|
+
"otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.221.json",
|
|
19
|
+
"otaManifestSha256": "657e9616ad0c464ee57d827c05b53393e45559992c13b15adbb06e0c903eb870"
|
|
20
20
|
})
|
package/src/index.ts
CHANGED
|
@@ -119,7 +119,13 @@ export {
|
|
|
119
119
|
export type {OtaAutoChainAdvanceResult} from "./services/OtaAutoChain"
|
|
120
120
|
export {
|
|
121
121
|
BES_INSTALL_RESTART_MESSAGE,
|
|
122
|
+
OTA_ERROR_BES_RESTART_REQUIRED_COPY_KEY,
|
|
123
|
+
OTA_ERROR_ENGLISH_COPY,
|
|
124
|
+
OTA_ERROR_GENERIC_COPY_KEY,
|
|
125
|
+
OTA_ERROR_UNKNOWN_GLASSES_COPY_KEY,
|
|
126
|
+
OTA_GLASSES_ERROR_COPY_KEYS,
|
|
122
127
|
getOtaErrorMessage,
|
|
128
|
+
otaErrorCopyKey,
|
|
123
129
|
shouldRequireGlassesRebootForBesFailure,
|
|
124
130
|
shouldShowChangeWifiForOtaDownloadFailure,
|
|
125
131
|
} from "./services/OtaErrorMapping"
|
|
@@ -14,10 +14,12 @@ import {useMarkdown, type MarkedStyles, type useMarkdownHookOptions} from "react
|
|
|
14
14
|
import {SafeAreaView} from "react-native-safe-area-context"
|
|
15
15
|
import Svg, {Path, Rect} from "react-native-svg"
|
|
16
16
|
|
|
17
|
+
import {OTA_ERROR_ENGLISH_COPY} from "../services/OtaErrorMapping"
|
|
17
18
|
import {
|
|
18
19
|
MINIMUM_OTA_BATTERY_LEVEL,
|
|
19
20
|
useMentraLiveOta,
|
|
20
21
|
type MentraLiveOtaController,
|
|
22
|
+
type MentraLiveOtaError,
|
|
21
23
|
type MentraLiveOtaFlowPage,
|
|
22
24
|
} from "./useMentraLiveOta"
|
|
23
25
|
|
|
@@ -142,6 +144,8 @@ const ENGLISH_COPY: Record<string, string> = {
|
|
|
142
144
|
"ota:versionChangeFirmwarePassComplete": "Firmware updated",
|
|
143
145
|
"ota:versionChangeFirmwarePassCompleteMessage":
|
|
144
146
|
"Your glasses restarted with new firmware. One more step: they'll now continue to the required version.",
|
|
147
|
+
"ota:updateFailed": "Update Failed",
|
|
148
|
+
...OTA_ERROR_ENGLISH_COPY,
|
|
145
149
|
}
|
|
146
150
|
|
|
147
151
|
const componentCopyKey = {
|
|
@@ -150,8 +154,17 @@ const componentCopyKey = {
|
|
|
150
154
|
bes: "ota:componentBes",
|
|
151
155
|
} as const
|
|
152
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Copy for the failed screen: the translated copy key when the failure maps to one,
|
|
159
|
+
* otherwise the engine's English message (phone-side watchdog and preflight text).
|
|
160
|
+
*/
|
|
161
|
+
function failureMessage(error: MentraLiveOtaError | null, translate: MentraLiveOtaFlowTranslate): string {
|
|
162
|
+
if (!error) return translate("ota:errorGeneric")
|
|
163
|
+
return error.copyKey ? translate(error.copyKey) : error.message
|
|
164
|
+
}
|
|
165
|
+
|
|
153
166
|
function defaultTranslate(key: string, options?: Record<string, string>): string {
|
|
154
|
-
let value = ENGLISH_COPY[key]
|
|
167
|
+
let value = Object.prototype.hasOwnProperty.call(ENGLISH_COPY, key) ? ENGLISH_COPY[key] : key
|
|
155
168
|
for (const [name, replacement] of Object.entries(options ?? {})) {
|
|
156
169
|
value = value.replaceAll(`{{${name}}}`, replacement)
|
|
157
170
|
}
|
|
@@ -559,8 +572,13 @@ function OtaFlowContent({
|
|
|
559
572
|
}
|
|
560
573
|
colors={colors}
|
|
561
574
|
icon="alert"
|
|
562
|
-
title="
|
|
563
|
-
<BodyText colors={colors}>{state.error
|
|
575
|
+
title={translate("ota:updateFailed")}>
|
|
576
|
+
<BodyText colors={colors}>{failureMessage(state.error, translate)}</BodyText>
|
|
577
|
+
{state.error?.glassesCode ? (
|
|
578
|
+
<Text style={[styles.errorCode, {color: colors.textDim}]} testID="ota-error-code">
|
|
579
|
+
{translate("ota:errorCode", {code: state.error.glassesCode})}
|
|
580
|
+
</Text>
|
|
581
|
+
) : null}
|
|
564
582
|
</FlowPage>
|
|
565
583
|
)
|
|
566
584
|
}
|
|
@@ -810,6 +828,7 @@ const styles = StyleSheet.create({
|
|
|
810
828
|
icon: {fontSize: 64, fontWeight: "500", lineHeight: 72, textAlign: "center"},
|
|
811
829
|
title: {fontSize: 20, fontWeight: "600", textAlign: "center"},
|
|
812
830
|
body: {fontSize: 14, lineHeight: 20, maxWidth: 420, textAlign: "center"},
|
|
831
|
+
errorCode: {fontSize: 12, fontVariant: ["tabular-nums"], lineHeight: 16, opacity: 0.7, textAlign: "center"},
|
|
813
832
|
percent: {fontSize: 30, fontVariant: ["tabular-nums"], fontWeight: "700"},
|
|
814
833
|
progressTrack: {borderRadius: 4, height: 8, maxWidth: 420, overflow: "hidden", width: "100%"},
|
|
815
834
|
progressFill: {borderRadius: 4, height: 8},
|
|
@@ -14,7 +14,9 @@ import {
|
|
|
14
14
|
} from "../services/OtaAutoChain"
|
|
15
15
|
import {
|
|
16
16
|
BES_INSTALL_RESTART_MESSAGE,
|
|
17
|
+
OTA_ERROR_BES_RESTART_REQUIRED_COPY_KEY,
|
|
17
18
|
getOtaErrorMessage,
|
|
19
|
+
otaErrorCopyKey,
|
|
18
20
|
shouldRequireGlassesRebootForBesFailure,
|
|
19
21
|
shouldShowChangeWifiForOtaDownloadFailure,
|
|
20
22
|
} from "../services/OtaErrorMapping"
|
|
@@ -54,7 +56,19 @@ export type MentraLiveOtaErrorCode =
|
|
|
54
56
|
|
|
55
57
|
export type MentraLiveOtaError = {
|
|
56
58
|
code: MentraLiveOtaErrorCode
|
|
59
|
+
/** English copy for hosts without localization. */
|
|
57
60
|
message: string
|
|
61
|
+
/**
|
|
62
|
+
* Copy key for `message` when the failure maps to known copy, so localized hosts can
|
|
63
|
+
* translate it. Null for phone-side watchdog and preflight messages, which are English-only.
|
|
64
|
+
* Optional so host code that builds this type by hand keeps compiling; the hook always sets it.
|
|
65
|
+
*/
|
|
66
|
+
copyKey?: string | null
|
|
67
|
+
/**
|
|
68
|
+
* Raw failure code reported by the glasses (`ota_status.error`), for support. Null when the
|
|
69
|
+
* failure originated on the phone. Optional for the same source-compatibility reason.
|
|
70
|
+
*/
|
|
71
|
+
glassesCode?: string | null
|
|
58
72
|
}
|
|
59
73
|
|
|
60
74
|
export type MentraLiveOtaTransport = "wifi" | "hotspot"
|
|
@@ -680,9 +694,16 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
680
694
|
error = {
|
|
681
695
|
code: "check_failed",
|
|
682
696
|
message: "Couldn't check for updates. Please check your connection and try again.",
|
|
697
|
+
copyKey: "ota:checkFailedMessage",
|
|
698
|
+
glassesCode: null,
|
|
683
699
|
}
|
|
684
700
|
} else if (screen === "update_info_unavailable") {
|
|
685
|
-
error = {
|
|
701
|
+
error = {
|
|
702
|
+
code: "update_info_unavailable",
|
|
703
|
+
message: "Update information for this app version is unavailable.",
|
|
704
|
+
copyKey: "ota:updateInfoUnavailableMessage",
|
|
705
|
+
glassesCode: null,
|
|
706
|
+
}
|
|
686
707
|
}
|
|
687
708
|
return {
|
|
688
709
|
screen,
|
|
@@ -745,16 +766,33 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
745
766
|
installSnapshot.otaProgress,
|
|
746
767
|
installSnapshot.errorMsg,
|
|
747
768
|
)
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
769
|
+
// The raw code the glasses attached to their failure report, kept for support even when
|
|
770
|
+
// phone-side copy outranks it. Only a failed ota_status carries a current code.
|
|
771
|
+
const glassesCode = installSnapshot.otaStatus?.status === "failed" ? installSnapshot.otaStatus.error || null : null
|
|
772
|
+
// Precedence mirrors the legacy screen: the BES restart instruction, then a phone-side
|
|
773
|
+
// watchdog/preflight message (English-only, no copy key), then the glasses code mapped to
|
|
774
|
+
// copy (unknown codes get the generic glasses-error copy rather than the raw code).
|
|
775
|
+
let displayedError: string
|
|
776
|
+
let copyKey: string | null
|
|
777
|
+
if (requiresGlassesReboot) {
|
|
778
|
+
displayedError = BES_INSTALL_RESTART_MESSAGE
|
|
779
|
+
copyKey = OTA_ERROR_BES_RESTART_REQUIRED_COPY_KEY
|
|
780
|
+
} else if (installSnapshot.errorMsg) {
|
|
781
|
+
displayedError = installSnapshot.errorMsg
|
|
782
|
+
copyKey = null
|
|
783
|
+
} else {
|
|
784
|
+
displayedError = getOtaErrorMessage(glassesCode)
|
|
785
|
+
copyKey = otaErrorCopyKey(glassesCode)
|
|
786
|
+
}
|
|
751
787
|
const progressState = progressScreen(installSnapshot)
|
|
752
788
|
const screen = progressState === "complete" && autoChainActive ? "finishing" : progressState
|
|
753
|
-
const error =
|
|
789
|
+
const error: MentraLiveOtaError | null =
|
|
754
790
|
screen === "failed"
|
|
755
791
|
? {
|
|
756
|
-
code: requiresGlassesReboot ?
|
|
792
|
+
code: requiresGlassesReboot ? "bes_restart_required" : "install_failed",
|
|
757
793
|
message: displayedError,
|
|
794
|
+
copyKey,
|
|
795
|
+
glassesCode,
|
|
758
796
|
}
|
|
759
797
|
: null
|
|
760
798
|
const totalSteps = installSnapshot.otaStatus?.totalSteps ?? null
|
|
@@ -1,7 +1,79 @@
|
|
|
1
1
|
import type {OtaProgress, OtaStatus} from "../facades/ota"
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Copy keys for OTA failures the glasses report by code (`ota_status.error`), plus the
|
|
5
|
+
* phone-side BES restart instruction. Hosts translate these keys; the engine's own
|
|
6
|
+
* English copy for each key lives in `OTA_ERROR_ENGLISH_COPY`.
|
|
7
|
+
*
|
|
8
|
+
* The glasses-side producers are `sendProgressToPhone(..., "FAILED", errorCode)` in the
|
|
9
|
+
* ASG client's OtaHelper (download classification, install failures, and the downgrade
|
|
10
|
+
* handoff to the recovery worker). Keep this table in sync with those codes; anything
|
|
11
|
+
* else falls back to `OTA_ERROR_UNKNOWN_GLASSES_COPY_KEY` and the raw code is shown in a
|
|
12
|
+
* secondary line so support can still identify it.
|
|
13
|
+
*/
|
|
14
|
+
export const OTA_GLASSES_ERROR_COPY_KEYS: Readonly<Record<string, string>> = {
|
|
15
|
+
no_internet: "ota:errorNoInternet",
|
|
16
|
+
clock_skew: "ota:errorClockSkew",
|
|
17
|
+
ssl_error: "ota:errorSslError",
|
|
18
|
+
download_failed: "ota:errorDownloadFailed",
|
|
19
|
+
insufficient_storage: "ota:errorInsufficientStorage",
|
|
20
|
+
firmware_too_large: "ota:errorFirmwareTooLarge",
|
|
21
|
+
firmware_verify_failed: "ota:errorFirmwareVerifyFailed",
|
|
22
|
+
apk_verify_failed: "ota:errorApkVerifyFailed",
|
|
23
|
+
install_failed: "ota:errorInstallFailed",
|
|
24
|
+
apk_restart_guard_not_persisted: "ota:errorApkRestartGuardNotPersisted",
|
|
25
|
+
downgrade_handoff_refused: "ota:errorDowngradeHandoffRefused",
|
|
26
|
+
downgrade_handoff_failed: "ota:errorDowngradeHandoffFailed",
|
|
27
|
+
downgrade_transaction_stalled: "ota:errorDowngradeTransactionStalled",
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const OTA_ERROR_UNKNOWN_GLASSES_COPY_KEY = "ota:errorGlassesUnknown"
|
|
31
|
+
export const OTA_ERROR_GENERIC_COPY_KEY = "ota:errorGeneric"
|
|
32
|
+
export const OTA_ERROR_BES_RESTART_REQUIRED_COPY_KEY = "ota:errorBesRestartRequired"
|
|
33
|
+
|
|
34
|
+
/** English copy for every OTA error key above. Mirrored in the Mentra App's `ota` i18n namespace. */
|
|
35
|
+
export const OTA_ERROR_ENGLISH_COPY: Readonly<Record<string, string>> = {
|
|
36
|
+
"ota:errorNoInternet": "Glasses WiFi has no internet connection",
|
|
37
|
+
"ota:errorClockSkew": "Glasses clock is wrong — syncing time from your phone, then retrying update check",
|
|
38
|
+
"ota:errorSslError": "Secure connection failed — try a different WiFi network",
|
|
39
|
+
"ota:errorDownloadFailed": "Download failed — check glasses WiFi connection",
|
|
40
|
+
"ota:errorInsufficientStorage": "Not enough storage on your glasses — free up space before trying again",
|
|
41
|
+
"ota:errorFirmwareTooLarge": "Firmware file is unexpectedly large — please contact support",
|
|
42
|
+
"ota:errorFirmwareVerifyFailed": "Firmware verification failed — please try again or contact support",
|
|
43
|
+
"ota:errorApkVerifyFailed": "Update verification failed — please try again or contact support",
|
|
44
|
+
"ota:errorInstallFailed": "Install failed — please try again",
|
|
45
|
+
"ota:errorApkRestartGuardNotPersisted":
|
|
46
|
+
"Your glasses could not save the update before restarting. Restart your glasses and try again.",
|
|
47
|
+
"ota:errorDowngradeHandoffRefused":
|
|
48
|
+
"Your glasses could not start the version change. Restart your glasses and try again.",
|
|
49
|
+
"ota:errorDowngradeHandoffFailed":
|
|
50
|
+
"The recovery service on your glasses did not respond. Restart your glasses and try again.",
|
|
51
|
+
"ota:errorDowngradeTransactionStalled":
|
|
52
|
+
"The version change on your glasses did not finish. Restart your glasses and try again.",
|
|
53
|
+
"ota:errorGlassesUnknown": "Your glasses reported an unexpected error. Restart your glasses and try again.",
|
|
54
|
+
"ota:errorGeneric": "Update failed",
|
|
55
|
+
"ota:errorBesRestartRequired": "Restart your glasses to safely exit firmware update mode before trying again",
|
|
56
|
+
"ota:errorCode": "Error code: {{code}}",
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const BES_INSTALL_RESTART_MESSAGE = OTA_ERROR_ENGLISH_COPY[OTA_ERROR_BES_RESTART_REQUIRED_COPY_KEY]
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Copy key for a glasses-reported failure code. Unknown codes get the generic
|
|
63
|
+
* glasses-error copy; a missing code gets the plain generic copy.
|
|
64
|
+
*/
|
|
65
|
+
export function otaErrorCopyKey(error?: string | null): string {
|
|
66
|
+
if (!error) return OTA_ERROR_GENERIC_COPY_KEY
|
|
67
|
+
// Own-property lookup: a code that happens to name an inherited Object member
|
|
68
|
+
// ("constructor", "toString", ...) is unknown, not a mapped key.
|
|
69
|
+
return hasOwn(OTA_GLASSES_ERROR_COPY_KEYS, error)
|
|
70
|
+
? OTA_GLASSES_ERROR_COPY_KEYS[error]
|
|
71
|
+
: OTA_ERROR_UNKNOWN_GLASSES_COPY_KEY
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function hasOwn(table: Readonly<Record<string, string>>, key: string): boolean {
|
|
75
|
+
return Object.prototype.hasOwnProperty.call(table, key)
|
|
76
|
+
}
|
|
5
77
|
|
|
6
78
|
function isDownloadPhaseSnapshot(
|
|
7
79
|
otaStatus: OtaStatus | null | undefined,
|
|
@@ -43,29 +115,9 @@ export function shouldShowChangeWifiForOtaDownloadFailure(
|
|
|
43
115
|
return false
|
|
44
116
|
}
|
|
45
117
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return "Glasses WiFi has no internet connection"
|
|
50
|
-
case "clock_skew":
|
|
51
|
-
return "Glasses clock is wrong — syncing time from your phone, then retrying update check"
|
|
52
|
-
case "ssl_error":
|
|
53
|
-
return "Secure connection failed — try a different WiFi network"
|
|
54
|
-
case "download_failed":
|
|
55
|
-
return "Download failed — check glasses WiFi connection"
|
|
56
|
-
case "insufficient_storage":
|
|
57
|
-
return "Not enough storage on your glasses — free up space before trying again"
|
|
58
|
-
case "firmware_too_large":
|
|
59
|
-
return "Firmware file is unexpectedly large — please contact support"
|
|
60
|
-
case "firmware_verify_failed":
|
|
61
|
-
return "Firmware verification failed — please try again or contact support"
|
|
62
|
-
case "apk_verify_failed":
|
|
63
|
-
return "Update verification failed — please try again or contact support"
|
|
64
|
-
case "install_failed":
|
|
65
|
-
return "Install failed — please try again"
|
|
66
|
-
default:
|
|
67
|
-
return error || "Update failed"
|
|
68
|
-
}
|
|
118
|
+
/** English message for a glasses-reported failure code. Never echoes the raw code. */
|
|
119
|
+
export function getOtaErrorMessage(error?: string | null): string {
|
|
120
|
+
return OTA_ERROR_ENGLISH_COPY[otaErrorCopyKey(error)]
|
|
69
121
|
}
|
|
70
122
|
|
|
71
123
|
/**
|
|
@@ -853,7 +853,12 @@ class OtaInstallCoordinator {
|
|
|
853
853
|
// suppress ota_start and hang the fresh downgrade until the global timeout).
|
|
854
854
|
this.otaStartOwnership?.outcome === "acknowledged" &&
|
|
855
855
|
otaStatus?.stepType === "apk" &&
|
|
856
|
-
otaStatus.phase === "install"
|
|
856
|
+
otaStatus.phase === "install" &&
|
|
857
|
+
// Only a live install arms the latch. A terminal failed status keeps stepType/phase
|
|
858
|
+
// apk/install and stays in the store, so without this gate every later pass (any
|
|
859
|
+
// unrelated store change) would re-arm the latch and the non-ownership block below
|
|
860
|
+
// would release it again, flooding the log with paired enter/release lines.
|
|
861
|
+
otaStatus.status === "in_progress"
|
|
857
862
|
) {
|
|
858
863
|
console.log("[OTA_PROGRESS] version-change: apk install started — entering detour wait")
|
|
859
864
|
this.versionChangeInstallStarted = true
|