@mentra/engine 3.4.0-dev.990 → 3.4.0-dev.991

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 (39) hide show
  1. package/build/generated/releaseMetadata.js +5 -5
  2. package/build/generated/releaseMetadata.js.map +1 -1
  3. package/build/react/MentraLiveOtaFlow.d.ts.map +1 -1
  4. package/build/react/MentraLiveOtaFlow.js +1 -4
  5. package/build/react/MentraLiveOtaFlow.js.map +1 -1
  6. package/build/react/useMentraLiveOta.js +2 -2
  7. package/build/react/useMentraLiveOta.js.map +1 -1
  8. package/build/services/OtaArtifactDownloader.d.ts +3 -1
  9. package/build/services/OtaArtifactDownloader.d.ts.map +1 -1
  10. package/build/services/OtaArtifactDownloader.js +57 -13
  11. package/build/services/OtaArtifactDownloader.js.map +1 -1
  12. package/build/services/OtaArtifactFailure.d.ts +18 -0
  13. package/build/services/OtaArtifactFailure.d.ts.map +1 -0
  14. package/build/services/OtaArtifactFailure.js +72 -0
  15. package/build/services/OtaArtifactFailure.js.map +1 -0
  16. package/build/services/OtaErrorMapping.d.ts +4 -2
  17. package/build/services/OtaErrorMapping.d.ts.map +1 -1
  18. package/build/services/OtaErrorMapping.js +57 -6
  19. package/build/services/OtaErrorMapping.js.map +1 -1
  20. package/build/services/OtaFailureReportService.d.ts +2 -0
  21. package/build/services/OtaFailureReportService.d.ts.map +1 -1
  22. package/build/services/OtaFailureReportService.js.map +1 -1
  23. package/build/services/OtaInstallCoordinator.d.ts +3 -1
  24. package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
  25. package/build/services/OtaInstallCoordinator.js +15 -26
  26. package/build/services/OtaInstallCoordinator.js.map +1 -1
  27. package/build/services/otaInstallPolicy.d.ts +2 -2
  28. package/build/services/otaInstallPolicy.js +2 -2
  29. package/build/services/otaInstallPolicy.js.map +1 -1
  30. package/package.json +8 -8
  31. package/src/generated/releaseMetadata.ts +5 -5
  32. package/src/react/MentraLiveOtaFlow.tsx +1 -6
  33. package/src/react/useMentraLiveOta.ts +2 -2
  34. package/src/services/OtaArtifactDownloader.ts +62 -15
  35. package/src/services/OtaArtifactFailure.ts +102 -0
  36. package/src/services/OtaErrorMapping.ts +60 -6
  37. package/src/services/OtaFailureReportService.ts +2 -0
  38. package/src/services/OtaInstallCoordinator.ts +17 -28
  39. package/src/services/otaInstallPolicy.ts +2 -2
@@ -0,0 +1,72 @@
1
+ /** Never retain artifact URLs, local paths, headers, or arbitrary native userInfo. */
2
+ function privateErrorText(value) {
3
+ if (typeof value !== "string" && typeof value !== "number")
4
+ return undefined;
5
+ return String(value)
6
+ .replace(/(?:https?|file):\/\/[^\s"'<>]+/gi, "[REDACTED_URL]")
7
+ .replace(/\/(?:data|var|private|storage|Users)\/[^\s"'<>]*/g, "[REDACTED_PATH]")
8
+ .replace(/\b(?:authorization|proxy-authorization|cookie|set-cookie)\s*[:=][^\r\n]*/gi, "[REDACTED_HEADER]")
9
+ .replace(/\b(?:bearer\s+\S+|(?:token|password|signature|api[_-]?key)\s*[:=]\s*(?:"[^"]*"|'[^']*'|\S+))/gi, "[REDACTED]")
10
+ .slice(0, 500);
11
+ }
12
+ /** Preserve Error's non-enumerable fields before JSON serialization drops them. */
13
+ export function otaArtifactFailure(error, context) {
14
+ const nativeErrors = [];
15
+ const seen = new Set();
16
+ let current = error;
17
+ for (let depth = 0; current != null && depth < 4 && !seen.has(current); depth++) {
18
+ seen.add(current);
19
+ if (typeof current !== "object") {
20
+ nativeErrors.push({ message: privateErrorText(current) });
21
+ break;
22
+ }
23
+ const detail = current;
24
+ nativeErrors.push({
25
+ name: privateErrorText(detail.name),
26
+ code: privateErrorText(detail.code),
27
+ domain: privateErrorText(detail.domain),
28
+ message: privateErrorText(detail.message),
29
+ });
30
+ current = detail.cause;
31
+ }
32
+ const text = nativeErrors.map((entry) => Object.values(entry).join(" ")).join(" ");
33
+ let httpStatus = context.httpStatus;
34
+ if (context.stage === "download" && httpStatus === undefined) {
35
+ // BackgroundOtaArtifactDownloader.swift rejects non-2xx responses instead of
36
+ // resolving a status. Expo can wrap this exact producer message in another Error.
37
+ for (const entry of nativeErrors) {
38
+ const response = /\bArtifact download failed with HTTP ([1-5]\d{2})\b/.exec(entry.message ?? "");
39
+ if (response) {
40
+ httpStatus = Number(response[1]);
41
+ break;
42
+ }
43
+ }
44
+ }
45
+ // A DNS error, timeout, reset, or HTTP failure alone does not establish no internet.
46
+ let category = context.stage === "verify" ? "verify" : "download";
47
+ if (/\bENOSPC\b|No space left on device/i.test(text))
48
+ category = "storage";
49
+ else if (context.stage === "save")
50
+ category = "save";
51
+ else if (context.stage === "download") {
52
+ if (/\b(?:EACCES|EROFS)\b|Permission denied|Read-only file system/i.test(text))
53
+ category = "save";
54
+ else if (httpStatus !== undefined &&
55
+ httpStatus >= 100 &&
56
+ httpStatus <= 599 &&
57
+ httpStatus !== 200 &&
58
+ httpStatus !== 206)
59
+ category = "server";
60
+ else if (nativeErrors.some((entry) => entry.domain === "NSURLErrorDomain" && entry.code === "-1009") ||
61
+ /\b(?:ENETDOWN|NSURLErrorNotConnectedToInternet)\b|Network is down|not connected to the Internet/i.test(text))
62
+ category = "internet";
63
+ else if (/\b(?:ETIMEDOUT|SocketTimeoutException)\b|timed? out/i.test(text))
64
+ category = "timeout";
65
+ else if (/\b(?:SSLHandshakeException|SSLPeerUnverifiedException|CERTIFICATE_VERIFY_FAILED)\b/i.test(text))
66
+ category = "secure_connection";
67
+ else if (/\b(?:ENETUNREACH|ECONNRESET|ECONNABORTED|EPIPE|UnknownHostException|EAI_NODATA|EAI_AGAIN)\b|Software caused connection abort|Connection reset|Unable to resolve host/i.test(text))
68
+ category = "connection";
69
+ }
70
+ return { ...context, httpStatus, category, nativeErrors };
71
+ }
72
+ //# sourceMappingURL=OtaArtifactFailure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OtaArtifactFailure.js","sourceRoot":"","sources":["../../src/services/OtaArtifactFailure.ts"],"names":[],"mappings":"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","sourcesContent":["export type OtaArtifactFailureCategory =\n | \"internet\"\n | \"connection\"\n | \"server\"\n | \"timeout\"\n | \"storage\"\n | \"save\"\n | \"secure_connection\"\n | \"download\"\n | \"verify\"\n\nexport interface OtaArtifactFailure {\n artifact: \"apk\" | \"mtk\" | \"bes\" | null\n stage: \"download\" | \"verify\" | \"save\"\n category: OtaArtifactFailureCategory\n httpStatus?: number\n bytesWritten: number\n contentLength: number\n nativeErrors: {name?: string; code?: string; domain?: string; message?: string}[]\n}\n\n/** Never retain artifact URLs, local paths, headers, or arbitrary native userInfo. */\nfunction privateErrorText(value: unknown): string | undefined {\n if (typeof value !== \"string\" && typeof value !== \"number\") return undefined\n return String(value)\n .replace(/(?:https?|file):\\/\\/[^\\s\"'<>]+/gi, \"[REDACTED_URL]\")\n .replace(/\\/(?:data|var|private|storage|Users)\\/[^\\s\"'<>]*/g, \"[REDACTED_PATH]\")\n .replace(/\\b(?:authorization|proxy-authorization|cookie|set-cookie)\\s*[:=][^\\r\\n]*/gi, \"[REDACTED_HEADER]\")\n .replace(\n /\\b(?:bearer\\s+\\S+|(?:token|password|signature|api[_-]?key)\\s*[:=]\\s*(?:\"[^\"]*\"|'[^']*'|\\S+))/gi,\n \"[REDACTED]\",\n )\n .slice(0, 500)\n}\n\n/** Preserve Error's non-enumerable fields before JSON serialization drops them. */\nexport function otaArtifactFailure(\n error: unknown,\n context: Omit<OtaArtifactFailure, \"category\" | \"nativeErrors\">,\n): OtaArtifactFailure {\n const nativeErrors: OtaArtifactFailure[\"nativeErrors\"] = []\n const seen = new Set<unknown>()\n let current = error\n for (let depth = 0; current != null && depth < 4 && !seen.has(current); depth++) {\n seen.add(current)\n if (typeof current !== \"object\") {\n nativeErrors.push({message: privateErrorText(current)})\n break\n }\n const detail = current as Record<string, unknown>\n nativeErrors.push({\n name: privateErrorText(detail.name),\n code: privateErrorText(detail.code),\n domain: privateErrorText(detail.domain),\n message: privateErrorText(detail.message),\n })\n current = detail.cause\n }\n const text = nativeErrors.map((entry) => Object.values(entry).join(\" \")).join(\" \")\n let httpStatus = context.httpStatus\n if (context.stage === \"download\" && httpStatus === undefined) {\n // BackgroundOtaArtifactDownloader.swift rejects non-2xx responses instead of\n // resolving a status. Expo can wrap this exact producer message in another Error.\n for (const entry of nativeErrors) {\n const response = /\\bArtifact download failed with HTTP ([1-5]\\d{2})\\b/.exec(entry.message ?? \"\")\n if (response) {\n httpStatus = Number(response[1])\n break\n }\n }\n }\n // A DNS error, timeout, reset, or HTTP failure alone does not establish no internet.\n let category: OtaArtifactFailureCategory = context.stage === \"verify\" ? \"verify\" : \"download\"\n if (/\\bENOSPC\\b|No space left on device/i.test(text)) category = \"storage\"\n else if (context.stage === \"save\") category = \"save\"\n else if (context.stage === \"download\") {\n if (/\\b(?:EACCES|EROFS)\\b|Permission denied|Read-only file system/i.test(text)) category = \"save\"\n else if (\n httpStatus !== undefined &&\n httpStatus >= 100 &&\n httpStatus <= 599 &&\n httpStatus !== 200 &&\n httpStatus !== 206\n )\n category = \"server\"\n else if (\n nativeErrors.some((entry) => entry.domain === \"NSURLErrorDomain\" && entry.code === \"-1009\") ||\n /\\b(?:ENETDOWN|NSURLErrorNotConnectedToInternet)\\b|Network is down|not connected to the Internet/i.test(text)\n )\n category = \"internet\"\n else if (/\\b(?:ETIMEDOUT|SocketTimeoutException)\\b|timed? out/i.test(text)) category = \"timeout\"\n else if (/\\b(?:SSLHandshakeException|SSLPeerUnverifiedException|CERTIFICATE_VERIFY_FAILED)\\b/i.test(text))\n category = \"secure_connection\"\n else if (\n /\\b(?:ENETUNREACH|ECONNRESET|ECONNABORTED|EPIPE|UnknownHostException|EAI_NODATA|EAI_AGAIN)\\b|Software caused connection abort|Connection reset|Unable to resolve host/i.test(\n text,\n )\n )\n category = \"connection\"\n }\n return {...context, httpStatus, category, nativeErrors}\n}\n"]}
@@ -7,13 +7,15 @@ import type { OtaProgress, OtaStatus } from "../facades/ota";
7
7
  * The glasses-side producers are `sendProgressToPhone(..., "FAILED", errorCode)` in the
8
8
  * ASG client's OtaHelper (download classification, install failures, and the downgrade
9
9
  * handoff to the recovery worker). Keep this table in sync with those codes; anything
10
- * else falls back to `OTA_ERROR_UNKNOWN_GLASSES_COPY_KEY` and the raw code is shown in a
11
- * secondary line so support can still identify it.
10
+ * else falls back to `OTA_ERROR_UNKNOWN_GLASSES_COPY_KEY`. Raw codes remain in private
11
+ * incident evidence rather than the product failure page.
12
12
  */
13
13
  export declare const OTA_GLASSES_ERROR_COPY_KEYS: Readonly<Record<string, string>>;
14
14
  export declare const OTA_ERROR_UNKNOWN_GLASSES_COPY_KEY = "ota:errorGlassesUnknown";
15
15
  export declare const OTA_ERROR_GENERIC_COPY_KEY = "ota:errorGeneric";
16
16
  export declare const OTA_ERROR_BES_RESTART_REQUIRED_COPY_KEY = "ota:errorBesRestartRequired";
17
+ /** A known failed stage is useful; it is not evidence of an internet outage. */
18
+ export declare function phoneOtaErrorCopyKey(error: unknown): string;
17
19
  /** English copy for every OTA error key above. Mirrored in the Mentra App's `ota` i18n namespace. */
18
20
  export declare const OTA_ERROR_ENGLISH_COPY: Readonly<Record<string, string>>;
19
21
  export declare const BES_INSTALL_RESTART_MESSAGE: string;
@@ -1 +1 @@
1
- {"version":3,"file":"OtaErrorMapping.d.ts","sourceRoot":"","sources":["../../src/services/OtaErrorMapping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAE,SAAS,EAAC,MAAM,gBAAgB,CAAA;AAE1D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAyBxE,CAAA;AAED,eAAO,MAAM,kCAAkC,4BAA4B,CAAA;AAC3E,eAAO,MAAM,0BAA0B,qBAAqB,CAAA;AAC5D,eAAO,MAAM,uCAAuC,gCAAgC,CAAA;AAEpF,qGAAqG;AACrG,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAsCnE,CAAA;AAED,eAAO,MAAM,2BAA2B,QAAkE,CAAA;AAE1G;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAO7D;AAmBD;;;;;;GAMG;AACH,wBAAgB,yCAAyC,CACvD,SAAS,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,EACvC,WAAW,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,EAC3C,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAcT;AAED,sFAAsF;AACtF,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAEhE;AAED;;;;GAIG;AACH,wBAAgB,uCAAuC,CACrD,SAAS,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,EACvC,YAAY,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,EAC5C,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAKT"}
1
+ {"version":3,"file":"OtaErrorMapping.d.ts","sourceRoot":"","sources":["../../src/services/OtaErrorMapping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAE,SAAS,EAAC,MAAM,gBAAgB,CAAA;AAE1D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAyBxE,CAAA;AAED,eAAO,MAAM,kCAAkC,4BAA4B,CAAA;AAC3E,eAAO,MAAM,0BAA0B,qBAAqB,CAAA;AAC5D,eAAO,MAAM,uCAAuC,gCAAgC,CAAA;AAcpF,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAwB3D;AAED,qGAAqG;AACrG,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAqDnE,CAAA;AAED,eAAO,MAAM,2BAA2B,QAAkE,CAAA;AAE1G;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAO7D;AAmBD;;;;;;GAMG;AACH,wBAAgB,yCAAyC,CACvD,SAAS,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,EACvC,WAAW,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,EAC3C,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAcT;AAED,sFAAsF;AACtF,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAEhE;AAED;;;;GAIG;AACH,wBAAgB,uCAAuC,CACrD,SAAS,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,EACvC,YAAY,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,EAC5C,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAKT"}
@@ -6,8 +6,8 @@
6
6
  * The glasses-side producers are `sendProgressToPhone(..., "FAILED", errorCode)` in the
7
7
  * ASG client's OtaHelper (download classification, install failures, and the downgrade
8
8
  * handoff to the recovery worker). Keep this table in sync with those codes; anything
9
- * else falls back to `OTA_ERROR_UNKNOWN_GLASSES_COPY_KEY` and the raw code is shown in a
10
- * secondary line so support can still identify it.
9
+ * else falls back to `OTA_ERROR_UNKNOWN_GLASSES_COPY_KEY`. Raw codes remain in private
10
+ * incident evidence rather than the product failure page.
11
11
  */
12
12
  export const OTA_GLASSES_ERROR_COPY_KEYS = {
13
13
  dns_failed: "ota:errorDnsFailed",
@@ -38,6 +38,44 @@ export const OTA_GLASSES_ERROR_COPY_KEYS = {
38
38
  export const OTA_ERROR_UNKNOWN_GLASSES_COPY_KEY = "ota:errorGlassesUnknown";
39
39
  export const OTA_ERROR_GENERIC_COPY_KEY = "ota:errorGeneric";
40
40
  export const OTA_ERROR_BES_RESTART_REQUIRED_COPY_KEY = "ota:errorBesRestartRequired";
41
+ const PHONE_ARTIFACT_COPY_KEYS = {
42
+ internet: "ota:errorPhoneInternet",
43
+ connection: "ota:errorPhoneConnection",
44
+ server: "ota:errorPhoneDownloadServer",
45
+ timeout: "ota:errorPhoneDownloadTimeout",
46
+ storage: "ota:errorPhoneStorage",
47
+ save: "ota:errorPhoneSave",
48
+ secure_connection: "ota:errorPhoneSecureConnection",
49
+ download: "ota:errorPhoneDownload",
50
+ verify: "ota:errorPhoneVerify",
51
+ };
52
+ /** A known failed stage is useful; it is not evidence of an internet outage. */
53
+ export function phoneOtaErrorCopyKey(error) {
54
+ if (typeof error !== "object" || error === null)
55
+ return OTA_ERROR_GENERIC_COPY_KEY;
56
+ const detail = error;
57
+ switch (detail.code) {
58
+ case "artifact_download_failed":
59
+ case "artifact_verify_failed": {
60
+ const category = detail.failure?.category;
61
+ if (typeof category === "string" && hasOwn(PHONE_ARTIFACT_COPY_KEYS, category))
62
+ return PHONE_ARTIFACT_COPY_KEYS[category];
63
+ return detail.code === "artifact_verify_failed" ? "ota:errorPhoneVerify" : "ota:errorPhoneDownload";
64
+ }
65
+ case "manifest_invalid":
66
+ return "ota:errorPhoneVerify";
67
+ case "hotspot_wifi_permission_denied":
68
+ return "ota:errorPhoneNearbyPermission";
69
+ case "hotspot_start_failed":
70
+ return "ota:errorGlassesHotspotStart";
71
+ case "hotspot_join_failed":
72
+ return "ota:errorPhoneHotspotJoin";
73
+ case "hotspot_server_failed":
74
+ return "ota:errorPhoneShareUpdate";
75
+ default:
76
+ return OTA_ERROR_GENERIC_COPY_KEY;
77
+ }
78
+ }
41
79
  /** English copy for every OTA error key above. Mirrored in the Mentra App's `ota` i18n namespace. */
42
80
  export const OTA_ERROR_ENGLISH_COPY = {
43
81
  "ota:errorDnsFailed": "Could not find the update server — retry, or check glasses Wi-Fi if this continues",
@@ -54,18 +92,31 @@ export const OTA_ERROR_ENGLISH_COPY = {
54
92
  "ota:errorNoInternet": "Glasses Wi-Fi has no internet connection",
55
93
  "ota:errorClockSkew": "Glasses clock is wrong — syncing time from your phone, then retrying update check",
56
94
  "ota:errorSslError": "Secure connection failed — try a different Wi-Fi network",
57
- "ota:errorDownloadFailed": "Download failed — check glasses Wi-Fi connection",
95
+ "ota:errorDownloadFailed": "The update could not be downloaded to your glasses. Please try again.",
58
96
  "ota:errorInsufficientStorage": "Not enough storage on your glasses — free up space before trying again",
59
97
  "ota:errorFirmwareTooLarge": "Firmware file is unexpectedly large — please contact support",
60
98
  "ota:errorFirmwareVerifyFailed": "Firmware verification failed — please try again or contact support",
61
99
  "ota:errorApkVerifyFailed": "Update verification failed — please try again or contact support",
62
- "ota:errorInstallFailed": "Install failed — please try again",
100
+ "ota:errorInstallFailed": "The update could not be installed on your glasses. Please try again.",
63
101
  "ota:errorApkRestartGuardNotPersisted": "Your glasses could not save the update before restarting. Restart your glasses and try again.",
64
102
  "ota:errorDowngradeHandoffRefused": "Your glasses could not start the version change. Restart your glasses and try again.",
65
103
  "ota:errorDowngradeHandoffFailed": "The recovery service on your glasses did not respond. Restart your glasses and try again.",
66
104
  "ota:errorDowngradeTransactionStalled": "The version change on your glasses did not finish. Restart your glasses and try again.",
67
- "ota:errorGlassesUnknown": "Your glasses reported an unexpected error. Restart your glasses and try again.",
68
- "ota:errorGeneric": "Update failed",
105
+ "ota:errorGlassesUnknown": "Something went wrong. Please try again.",
106
+ "ota:errorGeneric": "Something went wrong. Please try again.",
107
+ "ota:errorPhoneInternet": "Your phone could not connect to the internet. Check its connection and try again.",
108
+ "ota:errorPhoneConnection": "Your phone could not reach the update server. Check its connection and try again.",
109
+ "ota:errorPhoneDownloadServer": "The update server could not provide the download. Please try again later.",
110
+ "ota:errorPhoneDownloadTimeout": "The update download took too long. Please try again.",
111
+ "ota:errorPhoneStorage": "There is not enough space on your phone for the update. Free up space and try again.",
112
+ "ota:errorPhoneSave": "Your phone could not save the update. Please try again.",
113
+ "ota:errorPhoneSecureConnection": "Your phone could not make a secure connection to the update server. Please try again later.",
114
+ "ota:errorPhoneDownload": "The update could not be downloaded to your phone. Please try again.",
115
+ "ota:errorPhoneVerify": "The update file could not be verified. Please try again later.",
116
+ "ota:errorPhoneNearbyPermission": "Allow nearby devices access so your phone can connect to your glasses.",
117
+ "ota:errorGlassesHotspotStart": "Your glasses could not start the connection needed for the update. Please try again.",
118
+ "ota:errorPhoneHotspotJoin": "Your phone could not connect to your glasses for the update. Please try again.",
119
+ "ota:errorPhoneShareUpdate": "Your phone could not send the update to your glasses. Please try again.",
69
120
  "ota:errorBesRestartRequired": "Restart your glasses to safely exit firmware update mode before trying again",
70
121
  "ota:errorCode": "Error code: {{code}}",
71
122
  };
@@ -1 +1 @@
1
- {"version":3,"file":"OtaErrorMapping.js","sourceRoot":"","sources":["../../src/services/OtaErrorMapping.ts"],"names":[],"mappings":"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","sourcesContent":["import type {OtaProgress, OtaStatus} from \"../facades/ota\"\n\n/**\n * Copy keys for OTA failures the glasses report by code (`ota_status.error`), plus the\n * phone-side BES restart instruction. Hosts translate these keys; the engine's own\n * English copy for each key lives in `OTA_ERROR_ENGLISH_COPY`.\n *\n * The glasses-side producers are `sendProgressToPhone(..., \"FAILED\", errorCode)` in the\n * ASG client's OtaHelper (download classification, install failures, and the downgrade\n * handoff to the recovery worker). Keep this table in sync with those codes; anything\n * else falls back to `OTA_ERROR_UNKNOWN_GLASSES_COPY_KEY` and the raw code is shown in a\n * secondary line so support can still identify it.\n */\nexport const OTA_GLASSES_ERROR_COPY_KEYS: Readonly<Record<string, string>> = {\n dns_failed: \"ota:errorDnsFailed\",\n connection_failed: \"ota:errorConnectionFailed\",\n connect_timeout: \"ota:errorConnectTimeout\",\n download_timeout: \"ota:errorDownloadTimeout\",\n http_error: \"ota:errorHttpError\",\n downgrade_recovery_disabled: \"ota:errorDowngradeRecoveryDisabled\",\n downgrade_recovery_incompatible: \"ota:errorDowngradeRecoveryIncompatible\",\n downgrade_recovery_unavailable: \"ota:errorDowngradeRecoveryUnavailable\",\n downgrade_recovery_busy: \"ota:errorDowngradeRecoveryBusy\",\n downgrade_status_unknown: \"ota:errorDowngradeStatusUnknown\",\n downgrade_not_owned: \"ota:errorDowngradeNotOwned\",\n no_internet: \"ota:errorNoInternet\",\n clock_skew: \"ota:errorClockSkew\",\n ssl_error: \"ota:errorSslError\",\n download_failed: \"ota:errorDownloadFailed\",\n insufficient_storage: \"ota:errorInsufficientStorage\",\n firmware_too_large: \"ota:errorFirmwareTooLarge\",\n firmware_verify_failed: \"ota:errorFirmwareVerifyFailed\",\n apk_verify_failed: \"ota:errorApkVerifyFailed\",\n install_failed: \"ota:errorInstallFailed\",\n apk_restart_guard_not_persisted: \"ota:errorApkRestartGuardNotPersisted\",\n downgrade_handoff_refused: \"ota:errorDowngradeHandoffRefused\",\n downgrade_handoff_failed: \"ota:errorDowngradeHandoffFailed\",\n downgrade_transaction_stalled: \"ota:errorDowngradeTransactionStalled\",\n}\n\nexport const OTA_ERROR_UNKNOWN_GLASSES_COPY_KEY = \"ota:errorGlassesUnknown\"\nexport const OTA_ERROR_GENERIC_COPY_KEY = \"ota:errorGeneric\"\nexport const OTA_ERROR_BES_RESTART_REQUIRED_COPY_KEY = \"ota:errorBesRestartRequired\"\n\n/** English copy for every OTA error key above. Mirrored in the Mentra App's `ota` i18n namespace. */\nexport const OTA_ERROR_ENGLISH_COPY: Readonly<Record<string, string>> = {\n \"ota:errorDnsFailed\": \"Could not find the update server — retry, or check glasses Wi-Fi if this continues\",\n \"ota:errorConnectionFailed\": \"Could not connect to the update server — please retry\",\n \"ota:errorConnectTimeout\": \"The update server took too long to connect — please retry\",\n \"ota:errorDownloadTimeout\": \"The update stopped responding — please retry\",\n \"ota:errorHttpError\": \"The update server returned an error — retry later or contact support\",\n \"ota:errorDowngradeRecoveryDisabled\":\n \"The recovery service is disabled. Contact support to restore it before changing versions.\",\n \"ota:errorDowngradeRecoveryIncompatible\":\n \"The recovery service is incompatible. Contact support to restore it before changing versions.\",\n \"ota:errorDowngradeRecoveryUnavailable\":\n \"The recovery service is not ready. Wait a moment and retry; contact support if this continues.\",\n \"ota:errorDowngradeRecoveryBusy\":\n \"Another glasses recovery is still running. Keep your glasses nearby and retry after it finishes.\",\n \"ota:errorDowngradeStatusUnknown\":\n \"Still checking the version change on your glasses. Keep them nearby. Contact support if this continues.\",\n \"ota:errorDowngradeNotOwned\": \"The version change did not finish. Recovery has stopped; you can safely retry.\",\n \"ota:errorNoInternet\": \"Glasses Wi-Fi has no internet connection\",\n \"ota:errorClockSkew\": \"Glasses clock is wrong — syncing time from your phone, then retrying update check\",\n \"ota:errorSslError\": \"Secure connection failed — try a different Wi-Fi network\",\n \"ota:errorDownloadFailed\": \"Download failed — check glasses Wi-Fi connection\",\n \"ota:errorInsufficientStorage\": \"Not enough storage on your glasses — free up space before trying again\",\n \"ota:errorFirmwareTooLarge\": \"Firmware file is unexpectedly large — please contact support\",\n \"ota:errorFirmwareVerifyFailed\": \"Firmware verification failed — please try again or contact support\",\n \"ota:errorApkVerifyFailed\": \"Update verification failed — please try again or contact support\",\n \"ota:errorInstallFailed\": \"Install failed — please try again\",\n \"ota:errorApkRestartGuardNotPersisted\":\n \"Your glasses could not save the update before restarting. Restart your glasses and try again.\",\n \"ota:errorDowngradeHandoffRefused\":\n \"Your glasses could not start the version change. Restart your glasses and try again.\",\n \"ota:errorDowngradeHandoffFailed\":\n \"The recovery service on your glasses did not respond. Restart your glasses and try again.\",\n \"ota:errorDowngradeTransactionStalled\":\n \"The version change on your glasses did not finish. Restart your glasses and try again.\",\n \"ota:errorGlassesUnknown\": \"Your glasses reported an unexpected error. Restart your glasses and try again.\",\n \"ota:errorGeneric\": \"Update failed\",\n \"ota:errorBesRestartRequired\": \"Restart your glasses to safely exit firmware update mode before trying again\",\n \"ota:errorCode\": \"Error code: {{code}}\",\n}\n\nexport const BES_INSTALL_RESTART_MESSAGE = OTA_ERROR_ENGLISH_COPY[OTA_ERROR_BES_RESTART_REQUIRED_COPY_KEY]\n\n/**\n * Copy key for a glasses-reported failure code. Unknown codes get the generic\n * glasses-error copy; a missing code gets the plain generic copy.\n */\nexport function otaErrorCopyKey(error?: string | null): string {\n if (!error) return OTA_ERROR_GENERIC_COPY_KEY\n // Own-property lookup: a code that happens to name an inherited Object member\n // (\"constructor\", \"toString\", ...) is unknown, not a mapped key.\n return hasOwn(OTA_GLASSES_ERROR_COPY_KEYS, error)\n ? OTA_GLASSES_ERROR_COPY_KEYS[error]\n : OTA_ERROR_UNKNOWN_GLASSES_COPY_KEY\n}\n\nfunction hasOwn(table: Readonly<Record<string, string>>, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(table, key)\n}\n\nfunction isDownloadPhaseSnapshot(\n otaStatus: OtaStatus | null | undefined,\n otaProgress: OtaProgress | null | undefined,\n): boolean {\n if (otaStatus?.phase === \"download\") {\n return true\n }\n if (otaProgress?.stage === \"download\") {\n return true\n }\n return false\n}\n\n/**\n * Offer Change WiFi whenever the OTA flow failed while the glasses were in the\n * download step (any download failure — network, SSL, size cap, verify, etc.).\n *\n * Also covers phone-side watchdog failures that fire while the store still shows\n * an active download phase (stall / global timeout mid-download).\n */\nexport function shouldShowChangeWifiForOtaDownloadFailure(\n otaStatus: OtaStatus | null | undefined,\n otaProgress: OtaProgress | null | undefined,\n localErrorMessage: string,\n): boolean {\n if (otaStatus?.error === \"insufficient_storage\" || otaProgress?.errorMessage === \"insufficient_storage\") {\n return false\n }\n if (otaStatus?.status === \"failed\" && otaStatus.phase === \"download\") {\n return true\n }\n if (otaProgress?.status === \"FAILED\" && otaProgress.stage === \"download\") {\n return true\n }\n if (localErrorMessage && isDownloadPhaseSnapshot(otaStatus, otaProgress)) {\n return true\n }\n return false\n}\n\n/** English message for a glasses-reported failure code. Never echoes the raw code. */\nexport function getOtaErrorMessage(error?: string | null): string {\n return OTA_ERROR_ENGLISH_COPY[otaErrorCopyKey(error)]\n}\n\n/**\n * Once a BES install has started, any failure conservatively requires a glasses restart. This\n * avoids adding a BES-specific error code to the wire protocol and, more importantly, never offers\n * an unsafe retry when a generic failure or silence may mean BES entered its raw OTA parser.\n */\nexport function shouldRequireGlassesRebootForBesFailure(\n otaStatus: OtaStatus | null | undefined,\n _otaProgress: OtaProgress | null | undefined,\n localErrorMessage: string,\n): boolean {\n if (otaStatus?.stepType !== \"bes\" || otaStatus.phase !== \"install\") {\n return false\n }\n return otaStatus.status === \"failed\" || Boolean(localErrorMessage)\n}\n"]}
1
+ {"version":3,"file":"OtaErrorMapping.js","sourceRoot":"","sources":["../../src/services/OtaErrorMapping.ts"],"names":[],"mappings":"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","sourcesContent":["import type {OtaProgress, OtaStatus} from \"../facades/ota\"\n\n/**\n * Copy keys for OTA failures the glasses report by code (`ota_status.error`), plus the\n * phone-side BES restart instruction. Hosts translate these keys; the engine's own\n * English copy for each key lives in `OTA_ERROR_ENGLISH_COPY`.\n *\n * The glasses-side producers are `sendProgressToPhone(..., \"FAILED\", errorCode)` in the\n * ASG client's OtaHelper (download classification, install failures, and the downgrade\n * handoff to the recovery worker). Keep this table in sync with those codes; anything\n * else falls back to `OTA_ERROR_UNKNOWN_GLASSES_COPY_KEY`. Raw codes remain in private\n * incident evidence rather than the product failure page.\n */\nexport const OTA_GLASSES_ERROR_COPY_KEYS: Readonly<Record<string, string>> = {\n dns_failed: \"ota:errorDnsFailed\",\n connection_failed: \"ota:errorConnectionFailed\",\n connect_timeout: \"ota:errorConnectTimeout\",\n download_timeout: \"ota:errorDownloadTimeout\",\n http_error: \"ota:errorHttpError\",\n downgrade_recovery_disabled: \"ota:errorDowngradeRecoveryDisabled\",\n downgrade_recovery_incompatible: \"ota:errorDowngradeRecoveryIncompatible\",\n downgrade_recovery_unavailable: \"ota:errorDowngradeRecoveryUnavailable\",\n downgrade_recovery_busy: \"ota:errorDowngradeRecoveryBusy\",\n downgrade_status_unknown: \"ota:errorDowngradeStatusUnknown\",\n downgrade_not_owned: \"ota:errorDowngradeNotOwned\",\n no_internet: \"ota:errorNoInternet\",\n clock_skew: \"ota:errorClockSkew\",\n ssl_error: \"ota:errorSslError\",\n download_failed: \"ota:errorDownloadFailed\",\n insufficient_storage: \"ota:errorInsufficientStorage\",\n firmware_too_large: \"ota:errorFirmwareTooLarge\",\n firmware_verify_failed: \"ota:errorFirmwareVerifyFailed\",\n apk_verify_failed: \"ota:errorApkVerifyFailed\",\n install_failed: \"ota:errorInstallFailed\",\n apk_restart_guard_not_persisted: \"ota:errorApkRestartGuardNotPersisted\",\n downgrade_handoff_refused: \"ota:errorDowngradeHandoffRefused\",\n downgrade_handoff_failed: \"ota:errorDowngradeHandoffFailed\",\n downgrade_transaction_stalled: \"ota:errorDowngradeTransactionStalled\",\n}\n\nexport const OTA_ERROR_UNKNOWN_GLASSES_COPY_KEY = \"ota:errorGlassesUnknown\"\nexport const OTA_ERROR_GENERIC_COPY_KEY = \"ota:errorGeneric\"\nexport const OTA_ERROR_BES_RESTART_REQUIRED_COPY_KEY = \"ota:errorBesRestartRequired\"\n\nconst PHONE_ARTIFACT_COPY_KEYS: Readonly<Record<string, string>> = {\n internet: \"ota:errorPhoneInternet\",\n connection: \"ota:errorPhoneConnection\",\n server: \"ota:errorPhoneDownloadServer\",\n timeout: \"ota:errorPhoneDownloadTimeout\",\n storage: \"ota:errorPhoneStorage\",\n save: \"ota:errorPhoneSave\",\n secure_connection: \"ota:errorPhoneSecureConnection\",\n download: \"ota:errorPhoneDownload\",\n verify: \"ota:errorPhoneVerify\",\n}\n\n/** A known failed stage is useful; it is not evidence of an internet outage. */\nexport function phoneOtaErrorCopyKey(error: unknown): string {\n if (typeof error !== \"object\" || error === null) return OTA_ERROR_GENERIC_COPY_KEY\n const detail = error as {code?: unknown; failure?: {category?: unknown}}\n switch (detail.code) {\n case \"artifact_download_failed\":\n case \"artifact_verify_failed\": {\n const category = detail.failure?.category\n if (typeof category === \"string\" && hasOwn(PHONE_ARTIFACT_COPY_KEYS, category))\n return PHONE_ARTIFACT_COPY_KEYS[category]\n return detail.code === \"artifact_verify_failed\" ? \"ota:errorPhoneVerify\" : \"ota:errorPhoneDownload\"\n }\n case \"manifest_invalid\":\n return \"ota:errorPhoneVerify\"\n case \"hotspot_wifi_permission_denied\":\n return \"ota:errorPhoneNearbyPermission\"\n case \"hotspot_start_failed\":\n return \"ota:errorGlassesHotspotStart\"\n case \"hotspot_join_failed\":\n return \"ota:errorPhoneHotspotJoin\"\n case \"hotspot_server_failed\":\n return \"ota:errorPhoneShareUpdate\"\n default:\n return OTA_ERROR_GENERIC_COPY_KEY\n }\n}\n\n/** English copy for every OTA error key above. Mirrored in the Mentra App's `ota` i18n namespace. */\nexport const OTA_ERROR_ENGLISH_COPY: Readonly<Record<string, string>> = {\n \"ota:errorDnsFailed\": \"Could not find the update server — retry, or check glasses Wi-Fi if this continues\",\n \"ota:errorConnectionFailed\": \"Could not connect to the update server — please retry\",\n \"ota:errorConnectTimeout\": \"The update server took too long to connect — please retry\",\n \"ota:errorDownloadTimeout\": \"The update stopped responding — please retry\",\n \"ota:errorHttpError\": \"The update server returned an error — retry later or contact support\",\n \"ota:errorDowngradeRecoveryDisabled\":\n \"The recovery service is disabled. Contact support to restore it before changing versions.\",\n \"ota:errorDowngradeRecoveryIncompatible\":\n \"The recovery service is incompatible. Contact support to restore it before changing versions.\",\n \"ota:errorDowngradeRecoveryUnavailable\":\n \"The recovery service is not ready. Wait a moment and retry; contact support if this continues.\",\n \"ota:errorDowngradeRecoveryBusy\":\n \"Another glasses recovery is still running. Keep your glasses nearby and retry after it finishes.\",\n \"ota:errorDowngradeStatusUnknown\":\n \"Still checking the version change on your glasses. Keep them nearby. Contact support if this continues.\",\n \"ota:errorDowngradeNotOwned\": \"The version change did not finish. Recovery has stopped; you can safely retry.\",\n \"ota:errorNoInternet\": \"Glasses Wi-Fi has no internet connection\",\n \"ota:errorClockSkew\": \"Glasses clock is wrong — syncing time from your phone, then retrying update check\",\n \"ota:errorSslError\": \"Secure connection failed — try a different Wi-Fi network\",\n \"ota:errorDownloadFailed\": \"The update could not be downloaded to your glasses. Please try again.\",\n \"ota:errorInsufficientStorage\": \"Not enough storage on your glasses — free up space before trying again\",\n \"ota:errorFirmwareTooLarge\": \"Firmware file is unexpectedly large — please contact support\",\n \"ota:errorFirmwareVerifyFailed\": \"Firmware verification failed — please try again or contact support\",\n \"ota:errorApkVerifyFailed\": \"Update verification failed — please try again or contact support\",\n \"ota:errorInstallFailed\": \"The update could not be installed on your glasses. Please try again.\",\n \"ota:errorApkRestartGuardNotPersisted\":\n \"Your glasses could not save the update before restarting. Restart your glasses and try again.\",\n \"ota:errorDowngradeHandoffRefused\":\n \"Your glasses could not start the version change. Restart your glasses and try again.\",\n \"ota:errorDowngradeHandoffFailed\":\n \"The recovery service on your glasses did not respond. Restart your glasses and try again.\",\n \"ota:errorDowngradeTransactionStalled\":\n \"The version change on your glasses did not finish. Restart your glasses and try again.\",\n \"ota:errorGlassesUnknown\": \"Something went wrong. Please try again.\",\n \"ota:errorGeneric\": \"Something went wrong. Please try again.\",\n \"ota:errorPhoneInternet\": \"Your phone could not connect to the internet. Check its connection and try again.\",\n \"ota:errorPhoneConnection\": \"Your phone could not reach the update server. Check its connection and try again.\",\n \"ota:errorPhoneDownloadServer\": \"The update server could not provide the download. Please try again later.\",\n \"ota:errorPhoneDownloadTimeout\": \"The update download took too long. Please try again.\",\n \"ota:errorPhoneStorage\": \"There is not enough space on your phone for the update. Free up space and try again.\",\n \"ota:errorPhoneSave\": \"Your phone could not save the update. Please try again.\",\n \"ota:errorPhoneSecureConnection\":\n \"Your phone could not make a secure connection to the update server. Please try again later.\",\n \"ota:errorPhoneDownload\": \"The update could not be downloaded to your phone. Please try again.\",\n \"ota:errorPhoneVerify\": \"The update file could not be verified. Please try again later.\",\n \"ota:errorPhoneNearbyPermission\": \"Allow nearby devices access so your phone can connect to your glasses.\",\n \"ota:errorGlassesHotspotStart\":\n \"Your glasses could not start the connection needed for the update. Please try again.\",\n \"ota:errorPhoneHotspotJoin\": \"Your phone could not connect to your glasses for the update. Please try again.\",\n \"ota:errorPhoneShareUpdate\": \"Your phone could not send the update to your glasses. Please try again.\",\n \"ota:errorBesRestartRequired\": \"Restart your glasses to safely exit firmware update mode before trying again\",\n \"ota:errorCode\": \"Error code: {{code}}\",\n}\n\nexport const BES_INSTALL_RESTART_MESSAGE = OTA_ERROR_ENGLISH_COPY[OTA_ERROR_BES_RESTART_REQUIRED_COPY_KEY]\n\n/**\n * Copy key for a glasses-reported failure code. Unknown codes get the generic\n * glasses-error copy; a missing code gets the plain generic copy.\n */\nexport function otaErrorCopyKey(error?: string | null): string {\n if (!error) return OTA_ERROR_GENERIC_COPY_KEY\n // Own-property lookup: a code that happens to name an inherited Object member\n // (\"constructor\", \"toString\", ...) is unknown, not a mapped key.\n return hasOwn(OTA_GLASSES_ERROR_COPY_KEYS, error)\n ? OTA_GLASSES_ERROR_COPY_KEYS[error]\n : OTA_ERROR_UNKNOWN_GLASSES_COPY_KEY\n}\n\nfunction hasOwn(table: Readonly<Record<string, string>>, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(table, key)\n}\n\nfunction isDownloadPhaseSnapshot(\n otaStatus: OtaStatus | null | undefined,\n otaProgress: OtaProgress | null | undefined,\n): boolean {\n if (otaStatus?.phase === \"download\") {\n return true\n }\n if (otaProgress?.stage === \"download\") {\n return true\n }\n return false\n}\n\n/**\n * Offer Change WiFi whenever the OTA flow failed while the glasses were in the\n * download step (any download failure — network, SSL, size cap, verify, etc.).\n *\n * Also covers phone-side watchdog failures that fire while the store still shows\n * an active download phase (stall / global timeout mid-download).\n */\nexport function shouldShowChangeWifiForOtaDownloadFailure(\n otaStatus: OtaStatus | null | undefined,\n otaProgress: OtaProgress | null | undefined,\n localErrorMessage: string,\n): boolean {\n if (otaStatus?.error === \"insufficient_storage\" || otaProgress?.errorMessage === \"insufficient_storage\") {\n return false\n }\n if (otaStatus?.status === \"failed\" && otaStatus.phase === \"download\") {\n return true\n }\n if (otaProgress?.status === \"FAILED\" && otaProgress.stage === \"download\") {\n return true\n }\n if (localErrorMessage && isDownloadPhaseSnapshot(otaStatus, otaProgress)) {\n return true\n }\n return false\n}\n\n/** English message for a glasses-reported failure code. Never echoes the raw code. */\nexport function getOtaErrorMessage(error?: string | null): string {\n return OTA_ERROR_ENGLISH_COPY[otaErrorCopyKey(error)]\n}\n\n/**\n * Once a BES install has started, any failure conservatively requires a glasses restart. This\n * avoids adding a BES-specific error code to the wire protocol and, more importantly, never offers\n * an unsafe retry when a generic failure or silence may mean BES entered its raw OTA parser.\n */\nexport function shouldRequireGlassesRebootForBesFailure(\n otaStatus: OtaStatus | null | undefined,\n _otaProgress: OtaProgress | null | undefined,\n localErrorMessage: string,\n): boolean {\n if (otaStatus?.stepType !== \"bes\" || otaStatus.phase !== \"install\") {\n return false\n }\n return otaStatus.status === \"failed\" || Boolean(localErrorMessage)\n}\n"]}
@@ -1,9 +1,11 @@
1
1
  import type { OtaProgress, OtaStatus } from "@mentra/bluetooth-sdk";
2
+ import type { OtaArtifactFailure } from "./OtaArtifactFailure";
2
3
  export type OtaFailureReason = "download_stuck" | "progress_stalled" | "session_timeout" | "restart_timeout" | "start_failed" | "hotspot_preflight_failed" | "device_failed";
3
4
  export interface OtaFailureEvidence {
4
5
  attemptId: string;
5
6
  deviceRevision: number;
6
7
  reason: OtaFailureReason;
8
+ artifactFailure?: OtaArtifactFailure;
7
9
  transport: "wifi" | "hotspot" | null;
8
10
  hotspotPhase: string;
9
11
  connected: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"OtaFailureReportService.d.ts","sourceRoot":"","sources":["../../src/services/OtaFailureReportService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAE,SAAS,EAAC,MAAM,uBAAuB,CAAA;AAIjE,MAAM,MAAM,gBAAgB,GACxB,gBAAgB,GAChB,kBAAkB,GAClB,iBAAiB,GACjB,iBAAiB,GACjB,cAAc,GACd,0BAA0B,GAC1B,eAAe,CAAA;AAEnB,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,gBAAgB,CAAA;IACxB,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAA;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,OAAO,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,EAAE,OAAO,CAAA;IACzB,kBAAkB,EAAE,OAAO,CAAA;IAC3B,4BAA4B,EAAE,OAAO,CAAA;IACrC,2BAA2B,EAAE,OAAO,CAAA;IACpC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAA;IAC3B,WAAW,EAAE,WAAW,GAAG,IAAI,CAAA;CAChC;AAKD,iGAAiG;AACjG,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoCxF"}
1
+ {"version":3,"file":"OtaFailureReportService.d.ts","sourceRoot":"","sources":["../../src/services/OtaFailureReportService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAE,SAAS,EAAC,MAAM,uBAAuB,CAAA;AAEjE,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAA;AAG5D,MAAM,MAAM,gBAAgB,GACxB,gBAAgB,GAChB,kBAAkB,GAClB,iBAAiB,GACjB,iBAAiB,GACjB,cAAc,GACd,0BAA0B,GAC1B,eAAe,CAAA;AAEnB,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,gBAAgB,CAAA;IACxB,eAAe,CAAC,EAAE,kBAAkB,CAAA;IACpC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAA;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,OAAO,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,EAAE,OAAO,CAAA;IACzB,kBAAkB,EAAE,OAAO,CAAA;IAC3B,4BAA4B,EAAE,OAAO,CAAA;IACrC,2BAA2B,EAAE,OAAO,CAAA;IACpC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAA;IAC3B,WAAW,EAAE,WAAW,GAAG,IAAI,CAAA;CAChC;AAKD,iGAAiG;AACjG,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoCxF"}
@@ -1 +1 @@
1
- {"version":3,"file":"OtaFailureReportService.js","sourceRoot":"","sources":["../../src/services/OtaFailureReportService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,wBAAwB,EAAC,MAAM,iCAAiC,CAAA;AAmCxE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;AAClC,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAU,CAAA;AAE9C,iGAAiG;AACjG,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,QAA4B;IACvE,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAuB,CAAA;IAC3E,IAAI,QAAQ,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC;QAC9B,QAAQ,CAAC,SAAS,CAAC,KAAK,GAAG,wBAAwB,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAW,CAAA;IACzF,CAAC;IACD,IAAI,QAAQ,CAAC,WAAW,EAAE,YAAY,EAAE,CAAC;QACvC,QAAQ,CAAC,WAAW,CAAC,YAAY,GAAG,wBAAwB,CAAC,QAAQ,CAAC,WAAW,CAAC,YAAY,CAAW,CAAA;IAC3G,CAAC;IACD,MAAM,GAAG,GAAG,eAAe,QAAQ,CAAC,cAAc,IAAI,QAAQ,CAAC,SAAS,EAAE,SAAS,IAAI,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAA;IAC9H,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAM;IAC9D,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACjB,IAAI,CAAC;QACH,MAAM,EAAC,qBAAqB,EAAC,GAAG,OAAO,CAAC,oBAAoB,CAAwC,CAAA;QACpG,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC;YACzC,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,EAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAC;YAC5E,MAAM,EAAE;gBACN,gBAAgB,EAAE,gFAAgF;gBAClG,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;gBACjD,cAAc,EAAE,MAAM;aACvB;YACD,OAAO,EAAE,EAAC,UAAU,EAAE,QAAQ,EAAC;YAC/B,WAAW,EAAE,GAAG;SACjB,CAAC,CAAA;QACF,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YAC7B,6FAA6F;YAC7F,IAAI,oBAAoB,CAAC,IAAI,GAAG,GAAG;gBAAE,oBAAoB,CAAC,MAAM,CAAC,oBAAoB,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAM,CAAC,CAAA;QAC/G,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW;YAAE,OAAO,CAAC,GAAG,CAAC,kCAAkC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;aAC9F,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAA;IAC3F,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,sCAAsC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IACvG,CAAC;YAAS,CAAC;QACT,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACtB,CAAC;AACH,CAAC","sourcesContent":["import type {OtaProgress, OtaStatus} from \"@mentra/bluetooth-sdk\"\n\nimport {redactOtaDiagnosticError} from \"../utils/otaDiagnosticRedaction\"\n\nexport type OtaFailureReason =\n | \"download_stuck\"\n | \"progress_stalled\"\n | \"session_timeout\"\n | \"restart_timeout\"\n | \"start_failed\"\n | \"hotspot_preflight_failed\"\n | \"device_failed\"\n\nexport interface OtaFailureEvidence {\n attemptId: string\n deviceRevision: number\n reason: OtaFailureReason\n transport: \"wifi\" | \"hotspot\" | null\n hotspotPhase: string\n connected: boolean\n wifiState: string\n buildNumber: string | null\n besFirmwareVersion: string\n mtkFirmwareVersion: string\n targetBesVersion: string | null\n targetRelease: string | null\n targetBuild: number | null\n updates: string[]\n retryCount: number\n sawReconnectEdge: boolean\n besRestartExpected: boolean\n besRestartDisconnectObserved: boolean\n versionChangeInstallStarted: boolean\n otaStatus: OtaStatus | null\n otaProgress: OtaProgress | null\n}\n\nconst inFlight = new Set<string>()\nconst submittedOccurrences = new Set<string>()\n\n/** Snapshot synchronously so cleanup, retries and reconnects cannot rewrite failure evidence. */\nexport async function submitOtaFailureReport(evidence: OtaFailureEvidence): Promise<void> {\n const snapshot = JSON.parse(JSON.stringify(evidence)) as OtaFailureEvidence\n if (snapshot.otaStatus?.error) {\n snapshot.otaStatus.error = redactOtaDiagnosticError(snapshot.otaStatus.error) as string\n }\n if (snapshot.otaProgress?.errorMessage) {\n snapshot.otaProgress.errorMessage = redactOtaDiagnosticError(snapshot.otaProgress.errorMessage) as string\n }\n const key = `ota_failure:${snapshot.deviceRevision}:${snapshot.otaStatus?.sessionId || snapshot.attemptId}:${snapshot.reason}`\n if (inFlight.has(key) || submittedOccurrences.has(key)) return\n inFlight.add(key)\n try {\n const {submitAutomaticReport} = require(\"../facades/reports\") as typeof import(\"../facades/reports\")\n const result = await submitAutomaticReport({\n kind: \"automatic\",\n trigger: {type: \"automatic\", source: \"glasses_ota\", reason: snapshot.reason},\n report: {\n expectedBehavior: \"The glasses update completes and confirms the target version after restarting.\",\n actualBehavior: JSON.stringify(snapshot, null, 2),\n systemPriority: \"high\",\n },\n context: {otaFailure: snapshot},\n throttleKey: key,\n })\n if (result.status === \"submitted\") {\n submittedOccurrences.add(key)\n // Retain recent occurrences across remounts, without an unbounded process-lifetime registry.\n if (submittedOccurrences.size > 128) submittedOccurrences.delete(submittedOccurrences.values().next().value!)\n }\n if (result.status === \"submitted\") console.log(\"[OtaFailureReport] Report filed:\", result.reportId)\n else if (result.status === \"failed\") console.warn(\"[OtaFailureReport] submission failed\")\n } catch (error) {\n console.warn(\"[OtaFailureReport] submission failed\", error instanceof Error ? error.name : \"unknown\")\n } finally {\n inFlight.delete(key)\n }\n}\n"]}
1
+ {"version":3,"file":"OtaFailureReportService.js","sourceRoot":"","sources":["../../src/services/OtaFailureReportService.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,wBAAwB,EAAC,MAAM,iCAAiC,CAAA;AAoCxE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;AAClC,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAU,CAAA;AAE9C,iGAAiG;AACjG,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,QAA4B;IACvE,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAuB,CAAA;IAC3E,IAAI,QAAQ,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC;QAC9B,QAAQ,CAAC,SAAS,CAAC,KAAK,GAAG,wBAAwB,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAW,CAAA;IACzF,CAAC;IACD,IAAI,QAAQ,CAAC,WAAW,EAAE,YAAY,EAAE,CAAC;QACvC,QAAQ,CAAC,WAAW,CAAC,YAAY,GAAG,wBAAwB,CAAC,QAAQ,CAAC,WAAW,CAAC,YAAY,CAAW,CAAA;IAC3G,CAAC;IACD,MAAM,GAAG,GAAG,eAAe,QAAQ,CAAC,cAAc,IAAI,QAAQ,CAAC,SAAS,EAAE,SAAS,IAAI,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAA;IAC9H,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAM;IAC9D,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACjB,IAAI,CAAC;QACH,MAAM,EAAC,qBAAqB,EAAC,GAAG,OAAO,CAAC,oBAAoB,CAAwC,CAAA;QACpG,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC;YACzC,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,EAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAC;YAC5E,MAAM,EAAE;gBACN,gBAAgB,EAAE,gFAAgF;gBAClG,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;gBACjD,cAAc,EAAE,MAAM;aACvB;YACD,OAAO,EAAE,EAAC,UAAU,EAAE,QAAQ,EAAC;YAC/B,WAAW,EAAE,GAAG;SACjB,CAAC,CAAA;QACF,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YAC7B,6FAA6F;YAC7F,IAAI,oBAAoB,CAAC,IAAI,GAAG,GAAG;gBAAE,oBAAoB,CAAC,MAAM,CAAC,oBAAoB,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAM,CAAC,CAAA;QAC/G,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW;YAAE,OAAO,CAAC,GAAG,CAAC,kCAAkC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;aAC9F,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAA;IAC3F,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,sCAAsC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IACvG,CAAC;YAAS,CAAC;QACT,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACtB,CAAC;AACH,CAAC","sourcesContent":["import type {OtaProgress, OtaStatus} from \"@mentra/bluetooth-sdk\"\n\nimport type {OtaArtifactFailure} from \"./OtaArtifactFailure\"\nimport {redactOtaDiagnosticError} from \"../utils/otaDiagnosticRedaction\"\n\nexport type OtaFailureReason =\n | \"download_stuck\"\n | \"progress_stalled\"\n | \"session_timeout\"\n | \"restart_timeout\"\n | \"start_failed\"\n | \"hotspot_preflight_failed\"\n | \"device_failed\"\n\nexport interface OtaFailureEvidence {\n attemptId: string\n deviceRevision: number\n reason: OtaFailureReason\n artifactFailure?: OtaArtifactFailure\n transport: \"wifi\" | \"hotspot\" | null\n hotspotPhase: string\n connected: boolean\n wifiState: string\n buildNumber: string | null\n besFirmwareVersion: string\n mtkFirmwareVersion: string\n targetBesVersion: string | null\n targetRelease: string | null\n targetBuild: number | null\n updates: string[]\n retryCount: number\n sawReconnectEdge: boolean\n besRestartExpected: boolean\n besRestartDisconnectObserved: boolean\n versionChangeInstallStarted: boolean\n otaStatus: OtaStatus | null\n otaProgress: OtaProgress | null\n}\n\nconst inFlight = new Set<string>()\nconst submittedOccurrences = new Set<string>()\n\n/** Snapshot synchronously so cleanup, retries and reconnects cannot rewrite failure evidence. */\nexport async function submitOtaFailureReport(evidence: OtaFailureEvidence): Promise<void> {\n const snapshot = JSON.parse(JSON.stringify(evidence)) as OtaFailureEvidence\n if (snapshot.otaStatus?.error) {\n snapshot.otaStatus.error = redactOtaDiagnosticError(snapshot.otaStatus.error) as string\n }\n if (snapshot.otaProgress?.errorMessage) {\n snapshot.otaProgress.errorMessage = redactOtaDiagnosticError(snapshot.otaProgress.errorMessage) as string\n }\n const key = `ota_failure:${snapshot.deviceRevision}:${snapshot.otaStatus?.sessionId || snapshot.attemptId}:${snapshot.reason}`\n if (inFlight.has(key) || submittedOccurrences.has(key)) return\n inFlight.add(key)\n try {\n const {submitAutomaticReport} = require(\"../facades/reports\") as typeof import(\"../facades/reports\")\n const result = await submitAutomaticReport({\n kind: \"automatic\",\n trigger: {type: \"automatic\", source: \"glasses_ota\", reason: snapshot.reason},\n report: {\n expectedBehavior: \"The glasses update completes and confirms the target version after restarting.\",\n actualBehavior: JSON.stringify(snapshot, null, 2),\n systemPriority: \"high\",\n },\n context: {otaFailure: snapshot},\n throttleKey: key,\n })\n if (result.status === \"submitted\") {\n submittedOccurrences.add(key)\n // Retain recent occurrences across remounts, without an unbounded process-lifetime registry.\n if (submittedOccurrences.size > 128) submittedOccurrences.delete(submittedOccurrences.values().next().value!)\n }\n if (result.status === \"submitted\") console.log(\"[OtaFailureReport] Report filed:\", result.reportId)\n else if (result.status === \"failed\") console.warn(\"[OtaFailureReport] submission failed\")\n } catch (error) {\n console.warn(\"[OtaFailureReport] submission failed\", error instanceof Error ? error.name : \"unknown\")\n } finally {\n inFlight.delete(key)\n }\n}\n"]}
@@ -1,12 +1,13 @@
1
1
  import { type OtaProgress, type OtaStatus } from "@mentra/bluetooth-sdk";
2
2
  import { type HotspotOtaPhase } from "./HotspotOtaTransport";
3
- import type { OtaArtifactDownloadProgress } from "./OtaArtifactDownloader";
3
+ import { type OtaArtifactDownloadProgress } from "./OtaArtifactDownloader";
4
4
  import type { OtaCheckCurrentGlassesResult } from "./OtaUpdateCheckService";
5
5
  import { type DisplayState } from "./otaDisplayState";
6
6
  /** Read model the host progress screen renders from. */
7
7
  export interface OtaInstallSnapshot {
8
8
  displayState: DisplayState;
9
9
  errorMsg: string;
10
+ errorCopyKey?: string | null;
10
11
  continueButtonDisabled: boolean;
11
12
  connected: boolean;
12
13
  otaStatus: OtaStatus | null;
@@ -46,6 +47,7 @@ declare class OtaInstallCoordinator {
46
47
  private hotspotArtifactPercent;
47
48
  private hotspotArtifact;
48
49
  private errorMsg;
50
+ private errorCopyKey;
49
51
  private reportAttemptId;
50
52
  private reportAttemptSequence;
51
53
  private finishCompletion;
@@ -1 +1 @@
1
- {"version":3,"file":"OtaInstallCoordinator.d.ts","sourceRoot":"","sources":["../../src/services/OtaInstallCoordinator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"OtaInstallCoordinator.d.ts","sourceRoot":"","sources":["../../src/services/OtaInstallCoordinator.ts"],"names":[],"mappings":"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"}
@@ -23,8 +23,10 @@ import GlobalEventEmitter from "../utils/GlobalEventEmitter";
23
23
  import { isGlassesConnected, useGlassesStore } from "../stores/glasses";
24
24
  import { resolveOtaManifestUrl } from "./otaManifestUrl";
25
25
  import { hotspotOtaTransport } from "./HotspotOtaTransport";
26
+ import { OTA_ERROR_ENGLISH_COPY, phoneOtaErrorCopyKey } from "./OtaErrorMapping";
26
27
  import { waitForOtaInternet } from "./OtaInternetRecovery";
27
28
  import { submitOtaFailureReport } from "./OtaFailureReportService";
29
+ import { OtaArtifactError } from "./OtaArtifactDownloader";
28
30
  import { otaDeviceSessionRevision, subscribeOtaDeviceSession } from "./OtaDeviceSession";
29
31
  import { checkCurrentGlassesForUpdate, isOtaCheckForCurrentDevice } from "./OtaUpdateCheckService";
30
32
  import { deriveDisplayState } from "./otaDisplayState";
@@ -32,26 +34,6 @@ import { BES_CONTINUE_LOCKOUT_MS, BES_RESTART_TIMEOUT_MS, DOWNLOAD_STUCK_TIMEOUT
32
34
  function isTerminalForWatchdog(d) {
33
35
  return d === "complete" || d === "failed" || d === "restarting";
34
36
  }
35
- function hotspotPreflightErrorMessage(error) {
36
- const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "";
37
- switch (code) {
38
- case "artifact_download_failed":
39
- return OtaProgressMessages.hotspotArtifactDownloadFailed;
40
- case "artifact_verify_failed":
41
- case "manifest_invalid":
42
- return OtaProgressMessages.hotspotArtifactVerifyFailed;
43
- case "hotspot_wifi_permission_denied":
44
- return OtaProgressMessages.hotspotWifiPermissionDenied;
45
- case "hotspot_start_failed":
46
- return OtaProgressMessages.hotspotStartFailed;
47
- case "hotspot_join_failed":
48
- return OtaProgressMessages.hotspotJoinFailed;
49
- case "hotspot_server_failed":
50
- return OtaProgressMessages.hotspotServerFailed;
51
- default:
52
- return OtaProgressMessages.hotspotServerFailed;
53
- }
54
- }
55
37
  /** Non-empty when glasses are actively reporting work — drives stall timeout reset. */
56
38
  function buildProgressStalenessSignature(otaStatus, otaProgress, displayState) {
57
39
  if (displayState !== "starting" && displayState !== "updating")
@@ -129,6 +111,7 @@ class OtaInstallCoordinator {
129
111
  hotspotArtifact = null;
130
112
  // Genuinely session-local state (was component state/refs).
131
113
  errorMsg = "";
114
+ errorCopyKey = null;
132
115
  reportAttemptId = `${Date.now()}-0`;
133
116
  reportAttemptSequence = 0;
134
117
  finishCompletion = null;
@@ -489,6 +472,7 @@ class OtaInstallCoordinator {
489
472
  versionChangeSession: this.versionChangeSession,
490
473
  }),
491
474
  errorMsg: this.errorMsg,
475
+ errorCopyKey: this.errorCopyKey,
492
476
  // Keep Continue disabled while the expected BES reboot is still in flight.
493
477
  continueButtonDisabled: this.continueButtonDisabled || this.besRestartRecovery === "awaiting",
494
478
  connected,
@@ -559,6 +543,7 @@ class OtaInstallCoordinator {
559
543
  resetSessionState() {
560
544
  this.finishCompletion = null;
561
545
  this.errorMsg = "";
546
+ this.errorCopyKey = null;
562
547
  this.versionChangeReconnected = false;
563
548
  this.sawReconnectEdge = false;
564
549
  this.continueButtonDisabled = false;
@@ -620,12 +605,13 @@ class OtaInstallCoordinator {
620
605
  }
621
606
  // Setters mirror the old setState calls: bail on identical values (React's
622
607
  // setState bailout), notify snapshot subscribers, and queue a reaction pass.
623
- reportFailure(reason) {
608
+ reportFailure(reason, artifactFailure) {
624
609
  const state = useGlassesStore.getState();
625
610
  void submitOtaFailureReport({
626
611
  attemptId: this.reportAttemptId,
627
612
  deviceRevision: otaDeviceSessionRevision(),
628
613
  reason,
614
+ artifactFailure,
629
615
  transport: this.observedTransport,
630
616
  hotspotPhase: this.hotspotPhase,
631
617
  connected: isGlassesConnected(state.connection),
@@ -646,10 +632,11 @@ class OtaInstallCoordinator {
646
632
  otaProgress: state.otaProgress,
647
633
  });
648
634
  }
649
- setErrorMsg(next, failureReason) {
650
- if (this.errorMsg === next)
635
+ setErrorMsg(next, failureReason, copyKey = null) {
636
+ if (this.errorMsg === next && this.errorCopyKey === copyKey)
651
637
  return;
652
638
  this.errorMsg = next;
639
+ this.errorCopyKey = copyKey;
653
640
  if (next && failureReason)
654
641
  this.reportFailure(failureReason);
655
642
  this.emitInternalChange();
@@ -1533,12 +1520,14 @@ class OtaInstallCoordinator {
1533
1520
  if (this.selectedTransport === "hotspot" && !nativeStartAttempted) {
1534
1521
  ownership.outcome = "rejected";
1535
1522
  this.clearStuckTimeout();
1536
- this.reportFailure("hotspot_preflight_failed");
1523
+ this.reportFailure("hotspot_preflight_failed", err instanceof OtaArtifactError ? err.failure : undefined);
1537
1524
  await this.teardownHotspotTransport();
1538
1525
  if (!isCurrentAttempt())
1539
1526
  return;
1540
- if (this.attached)
1541
- this.setErrorMsg(hotspotPreflightErrorMessage(err));
1527
+ if (this.attached) {
1528
+ const copyKey = phoneOtaErrorCopyKey(err);
1529
+ this.setErrorMsg(OTA_ERROR_ENGLISH_COPY[copyKey], undefined, copyKey);
1530
+ }
1542
1531
  return;
1543
1532
  }
1544
1533
  // A received ota_status/legacy progress event, or the exact legacy APK