@mentra/engine 3.2.0-dev.160 → 3.2.0-dev.163

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.
@@ -1,10 +1,10 @@
1
1
  export const ENGINE_RELEASE_METADATA = Object.freeze({
2
2
  "schemaVersion": 1,
3
3
  "familyBaseVersion": "3.2.0",
4
- "releaseIdentity": "3.2.0-dev.160",
5
- "releaseSetId": "mentra-3.2.0-dev.160",
6
- "sourceCommit": "e39984a0722d39d29b0286f7ff5725c10bf895a9",
7
- "otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.160.json",
8
- "otaManifestSha256": "6595ac3a4e59bc887516f2983c382413c794157c61f2d9189195ddaa0f88ea32"
4
+ "releaseIdentity": "3.2.0-dev.163",
5
+ "releaseSetId": "mentra-3.2.0-dev.163",
6
+ "sourceCommit": "af1c07b2f4b8c1ff5940fa3dc8d9532960b0a105",
7
+ "otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.163.json",
8
+ "otaManifestSha256": "bf777f4bfd5ad18e98615015e92f33fdf90126f16e87d524ef6e69e8acb93354"
9
9
  });
10
10
  //# sourceMappingURL=releaseMetadata.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"releaseMetadata.js","sourceRoot":"","sources":["../../src/generated/releaseMetadata.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,uBAAuB,GAAoC,MAAM,CAAC,MAAM,CAAC;IACpF,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,OAAO;IAC5B,iBAAiB,EAAE,eAAe;IAClC,cAAc,EAAE,sBAAsB;IACtC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,wHAAwH;IAC1I,mBAAmB,EAAE,kEAAkE;CACxF,CAAC,CAAA","sourcesContent":["/** Generated by release CI. Do not edit in a release checkout. */\nexport interface EngineReleaseMetadata {\n schemaVersion: 1\n familyBaseVersion: string | null\n releaseIdentity: string | null\n releaseSetId: string | null\n sourceCommit: string | null\n otaManifestUrl: string | null\n otaManifestSha256: string | null\n}\n\nexport const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({\n \"schemaVersion\": 1,\n \"familyBaseVersion\": \"3.2.0\",\n \"releaseIdentity\": \"3.2.0-dev.160\",\n \"releaseSetId\": \"mentra-3.2.0-dev.160\",\n \"sourceCommit\": \"e39984a0722d39d29b0286f7ff5725c10bf895a9\",\n \"otaManifestUrl\": \"https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.160.json\",\n \"otaManifestSha256\": \"6595ac3a4e59bc887516f2983c382413c794157c61f2d9189195ddaa0f88ea32\"\n})\n"]}
1
+ {"version":3,"file":"releaseMetadata.js","sourceRoot":"","sources":["../../src/generated/releaseMetadata.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,uBAAuB,GAAoC,MAAM,CAAC,MAAM,CAAC;IACpF,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,OAAO;IAC5B,iBAAiB,EAAE,eAAe;IAClC,cAAc,EAAE,sBAAsB;IACtC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,wHAAwH;IAC1I,mBAAmB,EAAE,kEAAkE;CACxF,CAAC,CAAA","sourcesContent":["/** Generated by release CI. Do not edit in a release checkout. */\nexport interface EngineReleaseMetadata {\n schemaVersion: 1\n familyBaseVersion: string | null\n releaseIdentity: string | null\n releaseSetId: string | null\n sourceCommit: string | null\n otaManifestUrl: string | null\n otaManifestSha256: string | null\n}\n\nexport const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({\n \"schemaVersion\": 1,\n \"familyBaseVersion\": \"3.2.0\",\n \"releaseIdentity\": \"3.2.0-dev.163\",\n \"releaseSetId\": \"mentra-3.2.0-dev.163\",\n \"sourceCommit\": \"af1c07b2f4b8c1ff5940fa3dc8d9532960b0a105\",\n \"otaManifestUrl\": \"https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.163.json\",\n \"otaManifestSha256\": \"bf777f4bfd5ad18e98615015e92f33fdf90126f16e87d524ef6e69e8acb93354\"\n})\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"OtaArtifactDownloader.d.ts","sourceRoot":"","sources":["../../src/services/OtaArtifactDownloader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,4BAA4B,EAAc,MAAM,yBAAyB,CAAA;AAEtF;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAA;AAEnD,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,eAAe,CAAA;IACrB,oFAAoF;IACpF,GAAG,EAAE,MAAM,CAAA;IACX,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,mBAAoB,SAAQ,oBAAoB;IAC/D,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,eAAe,CAAA;IACrB,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,iFAAiF;IACjF,eAAe,EAAE,MAAM,CAAA;IACvB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,IAAI,EAAE,0BAA0B,GAAG,wBAAwB,GAAG,kBAAkB;gBAAhF,IAAI,EAAE,0BAA0B,GAAG,wBAAwB,GAAG,kBAAkB,EAChG,OAAO,EAAE,MAAM;CAKlB;AAID,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,4BAA4B,GAAG,oBAAoB,EAAE,CAuD1F;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,oBAAoB,EAAE,EAC5B,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,2BAA2B,KAAK,IAAI,EAC5D,cAAc,CAAC,EAAE,CACf,KAAK,EAAE,oBAAoB,EAC3B,WAAW,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,KAAK,IAAI,KAC/D,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAC,CAAC,GACjC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CA0EhC;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,mBAAmB,EAAE,EAChC,OAAO,EAAE,MAAM,GACd,MAAM,CA0BR;AAED,gGAAgG;AAChG,wBAAsB,gBAAgB,CAAC,WAAW,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAYhF"}
1
+ {"version":3,"file":"OtaArtifactDownloader.d.ts","sourceRoot":"","sources":["../../src/services/OtaArtifactDownloader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,4BAA4B,EAAc,MAAM,yBAAyB,CAAA;AAEtF;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAA;AAEnD,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,eAAe,CAAA;IACrB,oFAAoF;IACpF,GAAG,EAAE,MAAM,CAAA;IACX,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,mBAAoB,SAAQ,oBAAoB;IAC/D,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,eAAe,CAAA;IACrB,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,iFAAiF;IACjF,eAAe,EAAE,MAAM,CAAA;IACvB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,IAAI,EAAE,0BAA0B,GAAG,wBAAwB,GAAG,kBAAkB;gBAAhF,IAAI,EAAE,0BAA0B,GAAG,wBAAwB,GAAG,kBAAkB,EAChG,OAAO,EAAE,MAAM;CAKlB;AAID,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,4BAA4B,GAAG,oBAAoB,EAAE,CAwD1F;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,oBAAoB,EAAE,EAC5B,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,2BAA2B,KAAK,IAAI,EAC5D,cAAc,CAAC,EAAE,CACf,KAAK,EAAE,oBAAoB,EAC3B,WAAW,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,KAAK,IAAI,KAC/D,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAC,CAAC,GACjC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CA0EhC;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,mBAAmB,EAAE,EAChC,OAAO,EAAE,MAAM,GACd,MAAM,CA6BR;AAED,gGAAgG;AAChG,wBAAsB,gBAAgB,CAAC,WAAW,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAYhF"}
@@ -41,7 +41,8 @@ export function planArtifacts(result) {
41
41
  });
42
42
  }
43
43
  if (result.updates.includes("mtk") && result.mtkPatch) {
44
- const raw = (manifest.mtk_patches ?? []).find((patch) => firmwareUrl(patch) === firmwareUrl(result.mtkPatch));
44
+ const candidates = [...(manifest.mtk_patches ?? []), ...(manifest.mtk_full_ota ? [manifest.mtk_full_ota] : [])];
45
+ const raw = candidates.find((entry) => firmwareUrl(entry) === firmwareUrl(result.mtkPatch));
45
46
  const url = firmwareUrl(raw) ?? firmwareUrl(result.mtkPatch);
46
47
  if (!url) {
47
48
  throw new OtaArtifactError("manifest_invalid", "Manifest has no URL for the pending MTK patch");
@@ -159,6 +160,9 @@ export function rewriteManifestForLocalServer(manifestBody, artifacts, baseUrl)
159
160
  for (const patch of manifest.mtk_patches ?? []) {
160
161
  rewriteFirmwareEntry(patch, byUrl, localUrl);
161
162
  }
163
+ if (manifest.mtk_full_ota) {
164
+ rewriteFirmwareEntry(manifest.mtk_full_ota, byUrl, localUrl);
165
+ }
162
166
  if (manifest.bes_firmware) {
163
167
  rewriteFirmwareEntry(manifest.bes_firmware, byUrl, localUrl);
164
168
  }
@@ -1 +1 @@
1
- {"version":3,"file":"OtaArtifactDownloader.js","sourceRoot":"","sources":["../../src/services/OtaArtifactDownloader.ts"],"names":[],"mappings":"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","sourcesContent":["import * as RNFS from \"@dr.pogodin/react-native-fs\"\nimport type {OtaCheckCurrentGlassesResult, VersionJson} from \"./OtaUpdateCheckService\"\n\n/**\n * Phone-side artifact staging for hotspot-served OTA (OS-1676).\n *\n * When the glasses have no WiFi, the phone downloads the OTA artifacts itself (over\n * whatever network it is on — the user's tap on \"update\" is the consent) and serves them\n * to the glasses via the LocalOtaServer over the glasses' hotspot. This module owns:\n *\n * - planning which artifacts the pending update actually needs (from the raw manifest the\n * update check fetched, so the glasses re-parse byte-identical version data),\n * - downloading them with .part staging and sha256 verification, keyed on disk by hash so\n * retries and the post-APK continuation reuse verified files,\n * - rewriting the manifest's URL fields to point at the phone server, and\n * - cleanup once the update completes or the manifest moves on.\n */\n\nexport type OtaArtifactKind = \"apk\" | \"mtk\" | \"bes\"\n\nexport interface OtaArtifactPlanEntry {\n kind: OtaArtifactKind\n /** URL this artifact is fetched from, exactly as it appears in the raw manifest. */\n url: string\n /** Required content hash from the manifest. */\n sha256: string\n}\n\nexport interface PreparedOtaArtifact extends OtaArtifactPlanEntry {\n filePath: string\n}\n\nexport interface OtaArtifactDownloadProgress {\n kind: OtaArtifactKind\n /** 0-based index of the artifact being downloaded. */\n index: number\n totalCount: number\n /** Percent of the current artifact, 0-100 (0 when the server sent no length). */\n artifactPercent: number\n bytesWritten: number\n contentLength: number\n}\n\nexport class OtaArtifactError extends Error {\n constructor(\n public readonly code: \"artifact_download_failed\" | \"artifact_verify_failed\" | \"manifest_invalid\",\n message: string,\n ) {\n super(message)\n this.name = \"OtaArtifactError\"\n }\n}\n\nconst ASG_PACKAGE = \"com.mentra.asg_client\"\n\nexport function otaArtifactsDirectory(): string {\n return `${RNFS.DocumentDirectoryPath}/ota_artifacts`\n}\n\n/**\n * Derive the artifact list the pending update needs from a completed check.\n * Works off the raw manifest body (not the normalized check fields) so the URLs and\n * hashes match exactly what the glasses will re-parse.\n */\nexport function planArtifacts(result: OtaCheckCurrentGlassesResult): OtaArtifactPlanEntry[] {\n if (!result.manifestBody) {\n throw new OtaArtifactError(\"manifest_invalid\", \"Check result carries no manifest body\")\n }\n let manifest: VersionJson\n try {\n manifest = JSON.parse(result.manifestBody)\n } catch {\n throw new OtaArtifactError(\"manifest_invalid\", \"Manifest body is not valid JSON\")\n }\n\n const entries: OtaArtifactPlanEntry[] = []\n\n if (result.updates.includes(\"apk\")) {\n const app = manifest.apps?.[ASG_PACKAGE] as unknown as Record<string, unknown> | undefined\n const url = typeof app?.apkUrl === \"string\" ? app.apkUrl : null\n if (!url) {\n throw new OtaArtifactError(\"manifest_invalid\", \"Manifest has no apkUrl for the pending APK update\")\n }\n entries.push({\n kind: \"apk\",\n url,\n sha256: requireSha256(app, \"APK\"),\n })\n }\n\n if (result.updates.includes(\"mtk\") && result.mtkPatch) {\n const raw = (manifest.mtk_patches ?? []).find(\n (patch) => firmwareUrl(patch) === firmwareUrl(result.mtkPatch!),\n ) as unknown as Record<string, unknown> | undefined\n const url = firmwareUrl(raw) ?? firmwareUrl(result.mtkPatch)\n if (!url) {\n throw new OtaArtifactError(\"manifest_invalid\", \"Manifest has no URL for the pending MTK patch\")\n }\n entries.push({\n kind: \"mtk\",\n url,\n sha256: requireSha256(raw, \"MTK\"),\n })\n }\n\n if (result.updates.includes(\"bes\") && manifest.bes_firmware) {\n const raw = manifest.bes_firmware as unknown as Record<string, unknown>\n const url = firmwareUrl(raw)\n if (!url) {\n throw new OtaArtifactError(\"manifest_invalid\", \"Manifest has no URL for the pending BES firmware\")\n }\n entries.push({\n kind: \"bes\",\n url,\n sha256: requireSha256(raw, \"BES\"),\n })\n }\n\n return entries\n}\n\n/**\n * Download every planned artifact into the on-disk hash-keyed store, reusing files that\n * already verify. Sequential on purpose: progress is legible and the hotspot window that\n * follows is the slow part, not phone-side parallelism.\n */\nexport async function prepareArtifacts(\n plan: OtaArtifactPlanEntry[],\n onProgress?: (progress: OtaArtifactDownloadProgress) => void,\n nativeDownload?: (\n entry: OtaArtifactPlanEntry,\n destination: string,\n onProgress?: (bytesWritten: number, contentLength: number) => void,\n ) => Promise<{statusCode: number}>,\n): Promise<PreparedOtaArtifact[]> {\n const directory = otaArtifactsDirectory()\n await RNFS.mkdir(directory, {NSURLIsExcludedFromBackupKey: true})\n\n const prepared: PreparedOtaArtifact[] = []\n for (let index = 0; index < plan.length; index++) {\n const entry = plan[index]\n\n const cachedPath = `${directory}/${entry.sha256}`\n if (await RNFS.exists(cachedPath)) {\n const cachedHash = (await RNFS.hash(cachedPath, \"sha256\")).toLowerCase()\n if (cachedHash === entry.sha256) {\n prepared.push({...entry, filePath: cachedPath})\n continue\n }\n await safeUnlink(cachedPath)\n }\n\n const partPath = `${directory}/download-${index}.part`\n await safeUnlink(partPath)\n try {\n const reportProgress = (bytesWritten: number, contentLength: number) => {\n onProgress?.({\n kind: entry.kind,\n index,\n totalCount: plan.length,\n artifactPercent: contentLength > 0 ? Math.round((bytesWritten / contentLength) * 100) : 0,\n bytesWritten,\n contentLength,\n })\n }\n const downloadResult = nativeDownload\n ? await nativeDownload(entry, partPath, reportProgress)\n : await RNFS.downloadFile({\n fromUrl: entry.url,\n toFile: partPath,\n connectionTimeout: 30_000,\n readTimeout: 30_000,\n progressDivider: 5,\n progress: (res: RNFS.DownloadProgressCallbackResultT) => {\n reportProgress(res.bytesWritten, res.contentLength)\n },\n }).promise\n if (downloadResult.statusCode !== 200) {\n throw new OtaArtifactError(\n \"artifact_download_failed\",\n `Download of ${entry.kind} artifact failed with HTTP ${downloadResult.statusCode}`,\n )\n }\n\n const actualSha256 = (await RNFS.hash(partPath, \"sha256\")).toLowerCase()\n if (actualSha256 !== entry.sha256) {\n throw new OtaArtifactError(\n \"artifact_verify_failed\",\n `${entry.kind} artifact hash mismatch: expected ${entry.sha256}, got ${actualSha256}`,\n )\n }\n\n const finalPath = `${directory}/${entry.sha256}`\n await safeUnlink(finalPath)\n await RNFS.moveFile(partPath, finalPath)\n prepared.push({...entry, filePath: finalPath})\n } catch (error) {\n await safeUnlink(partPath)\n if (error instanceof OtaArtifactError) {\n throw error\n }\n throw new OtaArtifactError(\n \"artifact_download_failed\",\n `Download of ${entry.kind} artifact failed: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n }\n return prepared\n}\n\n/**\n * Point the manifest's artifact URLs at the phone server, leaving every other field\n * semantically identical to what the update check saw. Entries we host get\n * `<baseUrl>/artifacts/<sha256>`; entries the update does not need keep their CDN URLs.\n * Only URL fields change: target versions, sizes, and manifest hashes remain untouched.\n */\nexport function rewriteManifestForLocalServer(\n manifestBody: string,\n artifacts: PreparedOtaArtifact[],\n baseUrl: string,\n): string {\n let manifest: VersionJson\n try {\n manifest = JSON.parse(manifestBody)\n } catch {\n throw new OtaArtifactError(\"manifest_invalid\", \"Manifest body is not valid JSON\")\n }\n const byUrl = new Map(artifacts.map((artifact) => [artifact.url, artifact]))\n const localUrl = (artifact: PreparedOtaArtifact) => `${baseUrl}/artifacts/${artifact.sha256}`\n\n for (const app of Object.values(manifest.apps ?? {})) {\n const record = app as unknown as Record<string, unknown>\n const artifact = typeof record.apkUrl === \"string\" ? byUrl.get(record.apkUrl) : undefined\n if (artifact) {\n record.apkUrl = localUrl(artifact)\n }\n }\n\n for (const patch of manifest.mtk_patches ?? []) {\n rewriteFirmwareEntry(patch as unknown as Record<string, unknown>, byUrl, localUrl)\n }\n if (manifest.bes_firmware) {\n rewriteFirmwareEntry(manifest.bes_firmware as unknown as Record<string, unknown>, byUrl, localUrl)\n }\n\n return JSON.stringify(manifest)\n}\n\n/** Delete every stored artifact whose hash is not in `keepSha256s` (plus stray .part files). */\nexport async function cleanupArtifacts(keepSha256s: string[] = []): Promise<void> {\n const directory = otaArtifactsDirectory()\n if (!(await RNFS.exists(directory))) {\n return\n }\n const keep = new Set(keepSha256s.map((sha) => sha.toLowerCase()))\n const items = await RNFS.readDir(directory)\n for (const item of items) {\n if (!item.isFile()) continue\n if (keep.has(item.name.toLowerCase())) continue\n await safeUnlink(item.path)\n }\n}\n\nfunction rewriteFirmwareEntry(\n entry: Record<string, unknown>,\n byUrl: Map<string, PreparedOtaArtifact>,\n localUrl: (artifact: PreparedOtaArtifact) => string,\n): void {\n const url = firmwareUrl(entry)\n const artifact = url ? byUrl.get(url) : undefined\n if (!artifact) {\n return\n }\n entry.url = localUrl(artifact)\n // Overwrite the legacy key too when present, so no glasses build can prefer a stale\n // CDN URL over the local one.\n if (typeof entry.firmwareUrl === \"string\") {\n entry.firmwareUrl = localUrl(artifact)\n }\n}\n\nfunction firmwareUrl(entry: unknown): string | null {\n if (!entry || typeof entry !== \"object\") {\n return null\n }\n const record = entry as Record<string, unknown>\n if (typeof record.url === \"string\" && record.url.length > 0) {\n return record.url\n }\n if (typeof record.firmwareUrl === \"string\" && record.firmwareUrl.length > 0) {\n return record.firmwareUrl\n }\n return null\n}\n\nfunction requireSha256(entry: Record<string, unknown> | undefined, label: string): string {\n const sha256 = typeof entry?.sha256 === \"string\" ? entry.sha256.trim().toLowerCase() : \"\"\n if (!/^[a-f0-9]{64}$/.test(sha256)) {\n throw new OtaArtifactError(\"manifest_invalid\", `${label} artifact has no valid manifest sha256`)\n }\n return sha256\n}\n\nasync function safeUnlink(path: string): Promise<void> {\n try {\n if (await RNFS.exists(path)) {\n await RNFS.unlink(path)\n }\n } catch {\n // Best-effort: a leftover file only wastes space and is cleaned up next run.\n }\n}\n"]}
1
+ {"version":3,"file":"OtaArtifactDownloader.js","sourceRoot":"","sources":["../../src/services/OtaArtifactDownloader.ts"],"names":[],"mappings":"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","sourcesContent":["import * as RNFS from \"@dr.pogodin/react-native-fs\"\nimport type {OtaCheckCurrentGlassesResult, VersionJson} from \"./OtaUpdateCheckService\"\n\n/**\n * Phone-side artifact staging for hotspot-served OTA (OS-1676).\n *\n * When the glasses have no WiFi, the phone downloads the OTA artifacts itself (over\n * whatever network it is on — the user's tap on \"update\" is the consent) and serves them\n * to the glasses via the LocalOtaServer over the glasses' hotspot. This module owns:\n *\n * - planning which artifacts the pending update actually needs (from the raw manifest the\n * update check fetched, so the glasses re-parse byte-identical version data),\n * - downloading them with .part staging and sha256 verification, keyed on disk by hash so\n * retries and the post-APK continuation reuse verified files,\n * - rewriting the manifest's URL fields to point at the phone server, and\n * - cleanup once the update completes or the manifest moves on.\n */\n\nexport type OtaArtifactKind = \"apk\" | \"mtk\" | \"bes\"\n\nexport interface OtaArtifactPlanEntry {\n kind: OtaArtifactKind\n /** URL this artifact is fetched from, exactly as it appears in the raw manifest. */\n url: string\n /** Required content hash from the manifest. */\n sha256: string\n}\n\nexport interface PreparedOtaArtifact extends OtaArtifactPlanEntry {\n filePath: string\n}\n\nexport interface OtaArtifactDownloadProgress {\n kind: OtaArtifactKind\n /** 0-based index of the artifact being downloaded. */\n index: number\n totalCount: number\n /** Percent of the current artifact, 0-100 (0 when the server sent no length). */\n artifactPercent: number\n bytesWritten: number\n contentLength: number\n}\n\nexport class OtaArtifactError extends Error {\n constructor(\n public readonly code: \"artifact_download_failed\" | \"artifact_verify_failed\" | \"manifest_invalid\",\n message: string,\n ) {\n super(message)\n this.name = \"OtaArtifactError\"\n }\n}\n\nconst ASG_PACKAGE = \"com.mentra.asg_client\"\n\nexport function otaArtifactsDirectory(): string {\n return `${RNFS.DocumentDirectoryPath}/ota_artifacts`\n}\n\n/**\n * Derive the artifact list the pending update needs from a completed check.\n * Works off the raw manifest body (not the normalized check fields) so the URLs and\n * hashes match exactly what the glasses will re-parse.\n */\nexport function planArtifacts(result: OtaCheckCurrentGlassesResult): OtaArtifactPlanEntry[] {\n if (!result.manifestBody) {\n throw new OtaArtifactError(\"manifest_invalid\", \"Check result carries no manifest body\")\n }\n let manifest: VersionJson\n try {\n manifest = JSON.parse(result.manifestBody)\n } catch {\n throw new OtaArtifactError(\"manifest_invalid\", \"Manifest body is not valid JSON\")\n }\n\n const entries: OtaArtifactPlanEntry[] = []\n\n if (result.updates.includes(\"apk\")) {\n const app = manifest.apps?.[ASG_PACKAGE] as unknown as Record<string, unknown> | undefined\n const url = typeof app?.apkUrl === \"string\" ? app.apkUrl : null\n if (!url) {\n throw new OtaArtifactError(\"manifest_invalid\", \"Manifest has no apkUrl for the pending APK update\")\n }\n entries.push({\n kind: \"apk\",\n url,\n sha256: requireSha256(app, \"APK\"),\n })\n }\n\n if (result.updates.includes(\"mtk\") && result.mtkPatch) {\n const candidates = [...(manifest.mtk_patches ?? []), ...(manifest.mtk_full_ota ? [manifest.mtk_full_ota] : [])]\n const raw = candidates.find((entry) => firmwareUrl(entry) === firmwareUrl(result.mtkPatch!)) as unknown as\n | Record<string, unknown>\n | undefined\n const url = firmwareUrl(raw) ?? firmwareUrl(result.mtkPatch)\n if (!url) {\n throw new OtaArtifactError(\"manifest_invalid\", \"Manifest has no URL for the pending MTK patch\")\n }\n entries.push({\n kind: \"mtk\",\n url,\n sha256: requireSha256(raw, \"MTK\"),\n })\n }\n\n if (result.updates.includes(\"bes\") && manifest.bes_firmware) {\n const raw = manifest.bes_firmware as unknown as Record<string, unknown>\n const url = firmwareUrl(raw)\n if (!url) {\n throw new OtaArtifactError(\"manifest_invalid\", \"Manifest has no URL for the pending BES firmware\")\n }\n entries.push({\n kind: \"bes\",\n url,\n sha256: requireSha256(raw, \"BES\"),\n })\n }\n\n return entries\n}\n\n/**\n * Download every planned artifact into the on-disk hash-keyed store, reusing files that\n * already verify. Sequential on purpose: progress is legible and the hotspot window that\n * follows is the slow part, not phone-side parallelism.\n */\nexport async function prepareArtifacts(\n plan: OtaArtifactPlanEntry[],\n onProgress?: (progress: OtaArtifactDownloadProgress) => void,\n nativeDownload?: (\n entry: OtaArtifactPlanEntry,\n destination: string,\n onProgress?: (bytesWritten: number, contentLength: number) => void,\n ) => Promise<{statusCode: number}>,\n): Promise<PreparedOtaArtifact[]> {\n const directory = otaArtifactsDirectory()\n await RNFS.mkdir(directory, {NSURLIsExcludedFromBackupKey: true})\n\n const prepared: PreparedOtaArtifact[] = []\n for (let index = 0; index < plan.length; index++) {\n const entry = plan[index]\n\n const cachedPath = `${directory}/${entry.sha256}`\n if (await RNFS.exists(cachedPath)) {\n const cachedHash = (await RNFS.hash(cachedPath, \"sha256\")).toLowerCase()\n if (cachedHash === entry.sha256) {\n prepared.push({...entry, filePath: cachedPath})\n continue\n }\n await safeUnlink(cachedPath)\n }\n\n const partPath = `${directory}/download-${index}.part`\n await safeUnlink(partPath)\n try {\n const reportProgress = (bytesWritten: number, contentLength: number) => {\n onProgress?.({\n kind: entry.kind,\n index,\n totalCount: plan.length,\n artifactPercent: contentLength > 0 ? Math.round((bytesWritten / contentLength) * 100) : 0,\n bytesWritten,\n contentLength,\n })\n }\n const downloadResult = nativeDownload\n ? await nativeDownload(entry, partPath, reportProgress)\n : await RNFS.downloadFile({\n fromUrl: entry.url,\n toFile: partPath,\n connectionTimeout: 30_000,\n readTimeout: 30_000,\n progressDivider: 5,\n progress: (res: RNFS.DownloadProgressCallbackResultT) => {\n reportProgress(res.bytesWritten, res.contentLength)\n },\n }).promise\n if (downloadResult.statusCode !== 200) {\n throw new OtaArtifactError(\n \"artifact_download_failed\",\n `Download of ${entry.kind} artifact failed with HTTP ${downloadResult.statusCode}`,\n )\n }\n\n const actualSha256 = (await RNFS.hash(partPath, \"sha256\")).toLowerCase()\n if (actualSha256 !== entry.sha256) {\n throw new OtaArtifactError(\n \"artifact_verify_failed\",\n `${entry.kind} artifact hash mismatch: expected ${entry.sha256}, got ${actualSha256}`,\n )\n }\n\n const finalPath = `${directory}/${entry.sha256}`\n await safeUnlink(finalPath)\n await RNFS.moveFile(partPath, finalPath)\n prepared.push({...entry, filePath: finalPath})\n } catch (error) {\n await safeUnlink(partPath)\n if (error instanceof OtaArtifactError) {\n throw error\n }\n throw new OtaArtifactError(\n \"artifact_download_failed\",\n `Download of ${entry.kind} artifact failed: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n }\n return prepared\n}\n\n/**\n * Point the manifest's artifact URLs at the phone server, leaving every other field\n * semantically identical to what the update check saw. Entries we host get\n * `<baseUrl>/artifacts/<sha256>`; entries the update does not need keep their CDN URLs.\n * Only URL fields change: target versions, sizes, and manifest hashes remain untouched.\n */\nexport function rewriteManifestForLocalServer(\n manifestBody: string,\n artifacts: PreparedOtaArtifact[],\n baseUrl: string,\n): string {\n let manifest: VersionJson\n try {\n manifest = JSON.parse(manifestBody)\n } catch {\n throw new OtaArtifactError(\"manifest_invalid\", \"Manifest body is not valid JSON\")\n }\n const byUrl = new Map(artifacts.map((artifact) => [artifact.url, artifact]))\n const localUrl = (artifact: PreparedOtaArtifact) => `${baseUrl}/artifacts/${artifact.sha256}`\n\n for (const app of Object.values(manifest.apps ?? {})) {\n const record = app as unknown as Record<string, unknown>\n const artifact = typeof record.apkUrl === \"string\" ? byUrl.get(record.apkUrl) : undefined\n if (artifact) {\n record.apkUrl = localUrl(artifact)\n }\n }\n\n for (const patch of manifest.mtk_patches ?? []) {\n rewriteFirmwareEntry(patch as unknown as Record<string, unknown>, byUrl, localUrl)\n }\n if (manifest.mtk_full_ota) {\n rewriteFirmwareEntry(manifest.mtk_full_ota as unknown as Record<string, unknown>, byUrl, localUrl)\n }\n if (manifest.bes_firmware) {\n rewriteFirmwareEntry(manifest.bes_firmware as unknown as Record<string, unknown>, byUrl, localUrl)\n }\n\n return JSON.stringify(manifest)\n}\n\n/** Delete every stored artifact whose hash is not in `keepSha256s` (plus stray .part files). */\nexport async function cleanupArtifacts(keepSha256s: string[] = []): Promise<void> {\n const directory = otaArtifactsDirectory()\n if (!(await RNFS.exists(directory))) {\n return\n }\n const keep = new Set(keepSha256s.map((sha) => sha.toLowerCase()))\n const items = await RNFS.readDir(directory)\n for (const item of items) {\n if (!item.isFile()) continue\n if (keep.has(item.name.toLowerCase())) continue\n await safeUnlink(item.path)\n }\n}\n\nfunction rewriteFirmwareEntry(\n entry: Record<string, unknown>,\n byUrl: Map<string, PreparedOtaArtifact>,\n localUrl: (artifact: PreparedOtaArtifact) => string,\n): void {\n const url = firmwareUrl(entry)\n const artifact = url ? byUrl.get(url) : undefined\n if (!artifact) {\n return\n }\n entry.url = localUrl(artifact)\n // Overwrite the legacy key too when present, so no glasses build can prefer a stale\n // CDN URL over the local one.\n if (typeof entry.firmwareUrl === \"string\") {\n entry.firmwareUrl = localUrl(artifact)\n }\n}\n\nfunction firmwareUrl(entry: unknown): string | null {\n if (!entry || typeof entry !== \"object\") {\n return null\n }\n const record = entry as Record<string, unknown>\n if (typeof record.url === \"string\" && record.url.length > 0) {\n return record.url\n }\n if (typeof record.firmwareUrl === \"string\" && record.firmwareUrl.length > 0) {\n return record.firmwareUrl\n }\n return null\n}\n\nfunction requireSha256(entry: Record<string, unknown> | undefined, label: string): string {\n const sha256 = typeof entry?.sha256 === \"string\" ? entry.sha256.trim().toLowerCase() : \"\"\n if (!/^[a-f0-9]{64}$/.test(sha256)) {\n throw new OtaArtifactError(\"manifest_invalid\", `${label} artifact has no valid manifest sha256`)\n }\n return sha256\n}\n\nasync function safeUnlink(path: string): Promise<void> {\n try {\n if (await RNFS.exists(path)) {\n await RNFS.unlink(path)\n }\n } catch {\n // Best-effort: a leftover file only wastes space and is cleaned up next run.\n }\n}\n"]}
@@ -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,eAAO,MAAM,2BAA2B,iFACwC,CAAA;AAehF;;;;;;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,CAWT;AAED,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAqBzD;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,eAAO,MAAM,2BAA2B,iFACwC,CAAA;AAehF;;;;;;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,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAuBzD;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"}
@@ -16,6 +16,9 @@ function isDownloadPhaseSnapshot(otaStatus, otaProgress) {
16
16
  * an active download phase (stall / global timeout mid-download).
17
17
  */
18
18
  export function shouldShowChangeWifiForOtaDownloadFailure(otaStatus, otaProgress, localErrorMessage) {
19
+ if (otaStatus?.error === "insufficient_storage" || otaProgress?.errorMessage === "insufficient_storage") {
20
+ return false;
21
+ }
19
22
  if (otaStatus?.status === "failed" && otaStatus.phase === "download") {
20
23
  return true;
21
24
  }
@@ -37,6 +40,8 @@ export function getOtaErrorMessage(error) {
37
40
  return "Secure connection failed — try a different WiFi network";
38
41
  case "download_failed":
39
42
  return "Download failed — check glasses WiFi connection";
43
+ case "insufficient_storage":
44
+ return "Not enough storage on your glasses — free up space before trying again";
40
45
  case "firmware_too_large":
41
46
  return "Firmware file is unexpectedly large — please contact support";
42
47
  case "firmware_verify_failed":
@@ -1 +1 @@
1
- {"version":3,"file":"OtaErrorMapping.js","sourceRoot":"","sources":["../../src/services/OtaErrorMapping.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,2BAA2B,GACtC,8EAA8E,CAAA;AAEhF,SAAS,uBAAuB,CAC9B,SAAuC,EACvC,WAA2C;IAE3C,IAAI,SAAS,EAAE,KAAK,KAAK,UAAU,EAAE,CAAC;QACpC,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,WAAW,EAAE,KAAK,KAAK,UAAU,EAAE,CAAC;QACtC,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yCAAyC,CACvD,SAAuC,EACvC,WAA2C,EAC3C,iBAAyB;IAEzB,IAAI,SAAS,EAAE,MAAM,KAAK,QAAQ,IAAI,SAAS,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;QACrE,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,WAAW,EAAE,MAAM,KAAK,QAAQ,IAAI,WAAW,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;QACzE,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,iBAAiB,IAAI,uBAAuB,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,CAAC;QACzE,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,aAAa;YAChB,OAAO,yCAAyC,CAAA;QAClD,KAAK,YAAY;YACf,OAAO,mFAAmF,CAAA;QAC5F,KAAK,WAAW;YACd,OAAO,yDAAyD,CAAA;QAClE,KAAK,iBAAiB;YACpB,OAAO,iDAAiD,CAAA;QAC1D,KAAK,oBAAoB;YACvB,OAAO,8DAA8D,CAAA;QACvE,KAAK,wBAAwB;YAC3B,OAAO,oEAAoE,CAAA;QAC7E,KAAK,mBAAmB;YACtB,OAAO,kEAAkE,CAAA;QAC3E,KAAK,gBAAgB;YACnB,OAAO,mCAAmC,CAAA;QAC5C;YACE,OAAO,KAAK,IAAI,eAAe,CAAA;IACnC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uCAAuC,CACrD,SAAuC,EACvC,YAA4C,EAC5C,iBAAyB;IAEzB,IAAI,SAAS,EAAE,QAAQ,KAAK,KAAK,IAAI,SAAS,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACnE,OAAO,KAAK,CAAA;IACd,CAAC;IACD,OAAO,SAAS,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAA;AACpE,CAAC","sourcesContent":["import type {OtaProgress, OtaStatus} from \"../facades/ota\"\n\nexport const BES_INSTALL_RESTART_MESSAGE =\n \"Restart your glasses to safely exit firmware update mode before trying again\"\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?.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\nexport function getOtaErrorMessage(error?: string): string {\n switch (error) {\n case \"no_internet\":\n return \"Glasses WiFi has no internet connection\"\n case \"clock_skew\":\n return \"Glasses clock is wrong — syncing time from your phone, then retrying update check\"\n case \"ssl_error\":\n return \"Secure connection failed — try a different WiFi network\"\n case \"download_failed\":\n return \"Download failed — check glasses WiFi connection\"\n case \"firmware_too_large\":\n return \"Firmware file is unexpectedly large — please contact support\"\n case \"firmware_verify_failed\":\n return \"Firmware verification failed — please try again or contact support\"\n case \"apk_verify_failed\":\n return \"Update verification failed — please try again or contact support\"\n case \"install_failed\":\n return \"Install failed — please try again\"\n default:\n return error || \"Update failed\"\n }\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":"AAEA,MAAM,CAAC,MAAM,2BAA2B,GACtC,8EAA8E,CAAA;AAEhF,SAAS,uBAAuB,CAC9B,SAAuC,EACvC,WAA2C;IAE3C,IAAI,SAAS,EAAE,KAAK,KAAK,UAAU,EAAE,CAAC;QACpC,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,WAAW,EAAE,KAAK,KAAK,UAAU,EAAE,CAAC;QACtC,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yCAAyC,CACvD,SAAuC,EACvC,WAA2C,EAC3C,iBAAyB;IAEzB,IAAI,SAAS,EAAE,KAAK,KAAK,sBAAsB,IAAI,WAAW,EAAE,YAAY,KAAK,sBAAsB,EAAE,CAAC;QACxG,OAAO,KAAK,CAAA;IACd,CAAC;IACD,IAAI,SAAS,EAAE,MAAM,KAAK,QAAQ,IAAI,SAAS,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;QACrE,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,WAAW,EAAE,MAAM,KAAK,QAAQ,IAAI,WAAW,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;QACzE,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,iBAAiB,IAAI,uBAAuB,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,CAAC;QACzE,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,aAAa;YAChB,OAAO,yCAAyC,CAAA;QAClD,KAAK,YAAY;YACf,OAAO,mFAAmF,CAAA;QAC5F,KAAK,WAAW;YACd,OAAO,yDAAyD,CAAA;QAClE,KAAK,iBAAiB;YACpB,OAAO,iDAAiD,CAAA;QAC1D,KAAK,sBAAsB;YACzB,OAAO,wEAAwE,CAAA;QACjF,KAAK,oBAAoB;YACvB,OAAO,8DAA8D,CAAA;QACvE,KAAK,wBAAwB;YAC3B,OAAO,oEAAoE,CAAA;QAC7E,KAAK,mBAAmB;YACtB,OAAO,kEAAkE,CAAA;QAC3E,KAAK,gBAAgB;YACnB,OAAO,mCAAmC,CAAA;QAC5C;YACE,OAAO,KAAK,IAAI,eAAe,CAAA;IACnC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uCAAuC,CACrD,SAAuC,EACvC,YAA4C,EAC5C,iBAAyB;IAEzB,IAAI,SAAS,EAAE,QAAQ,KAAK,KAAK,IAAI,SAAS,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACnE,OAAO,KAAK,CAAA;IACd,CAAC;IACD,OAAO,SAAS,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAA;AACpE,CAAC","sourcesContent":["import type {OtaProgress, OtaStatus} from \"../facades/ota\"\n\nexport const BES_INSTALL_RESTART_MESSAGE =\n \"Restart your glasses to safely exit firmware update mode before trying again\"\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\nexport function getOtaErrorMessage(error?: string): string {\n switch (error) {\n case \"no_internet\":\n return \"Glasses WiFi has no internet connection\"\n case \"clock_skew\":\n return \"Glasses clock is wrong — syncing time from your phone, then retrying update check\"\n case \"ssl_error\":\n return \"Secure connection failed — try a different WiFi network\"\n case \"download_failed\":\n return \"Download failed — check glasses WiFi connection\"\n case \"insufficient_storage\":\n return \"Not enough storage on your glasses — free up space before trying again\"\n case \"firmware_too_large\":\n return \"Firmware file is unexpectedly large — please contact support\"\n case \"firmware_verify_failed\":\n return \"Firmware verification failed — please try again or contact support\"\n case \"apk_verify_failed\":\n return \"Update verification failed — please try again or contact support\"\n case \"install_failed\":\n return \"Install failed — please try again\"\n default:\n return error || \"Update failed\"\n }\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"]}
@@ -102,6 +102,8 @@ declare class OtaInstallCoordinator {
102
102
  private lastBuildNumber;
103
103
  private lastDisplayState;
104
104
  private lastStallSig;
105
+ private downloadStepKey;
106
+ private downloadByteHighWater;
105
107
  private reacting;
106
108
  private reactQueued;
107
109
  private storeUnsubscribe;
@@ -241,15 +243,13 @@ declare class OtaInstallCoordinator {
241
243
  /**
242
244
  * "Glasses are talking to us" — suppresses retry if the native request later rejects.
243
245
  * Important: this does NOT clear the stuck-at-zero watchdog; that one fires
244
- * on real progress > 0% (see {@link onFirstNonZeroProgress}).
246
+ * on real byte/percent progress (see {@link onFirstNonZeroProgress}).
245
247
  */
246
248
  private onFirstActivity;
247
249
  /**
248
250
  * "Real download progress arrived" — clears the stuck-at-zero watchdog. We
249
- * deliberately wait for non-zero progress before clearing this so that an
250
- * ota_status reply with stepPercent: 0 (which is "first activity" but not
251
- * "real progress") doesn't disable the only watchdog that catches a wedged
252
- * download.
251
+ * require advancing bytes or non-zero percent, so an ordinary status reply
252
+ * cannot disable the watchdog that catches a wedged download.
253
253
  */
254
254
  private onFirstNonZeroProgress;
255
255
  private maybeStartGlobalTimeout;
@@ -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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"}
@@ -169,7 +169,7 @@ class OtaInstallCoordinator {
169
169
  // command as permission to start a second OTA transaction.
170
170
  otaStartOwnership = null;
171
171
  hasFirstActivity = false;
172
- // Stuck-at-zero watchdog clears only on first NON-ZERO progress; "first activity"
172
+ // Stuck-at-zero watchdog clears only on real byte/percent progress; "first activity"
173
173
  // alone (e.g. ota_status with stepPercent=0) used to clear it too eagerly.
174
174
  hasFirstNonZeroProgress = false;
175
175
  retryCount = 0;
@@ -182,6 +182,8 @@ class OtaInstallCoordinator {
182
182
  lastBuildNumber = "";
183
183
  lastDisplayState = null;
184
184
  lastStallSig = "";
185
+ downloadStepKey = "";
186
+ downloadByteHighWater = 0;
185
187
  // Reaction pass re-entrancy guard (mirrors React deferring a setState-during-
186
188
  // effects re-render until the current effect batch finished).
187
189
  reacting = false;
@@ -479,6 +481,8 @@ class OtaInstallCoordinator {
479
481
  this.lastBuildNumber = "";
480
482
  this.lastDisplayState = null;
481
483
  this.lastStallSig = "";
484
+ this.downloadStepKey = "";
485
+ this.downloadByteHighWater = 0;
482
486
  this.reactQueued = false;
483
487
  }
484
488
  /** Begin a genuinely new command attempt only after native ownership has settled. */
@@ -488,6 +492,8 @@ class OtaInstallCoordinator {
488
492
  return false;
489
493
  }
490
494
  this.otaStartOwnership = null;
495
+ this.downloadStepKey = "";
496
+ this.downloadByteHighWater = 0;
491
497
  this.hasFirstActivity = false;
492
498
  this.hasFirstNonZeroProgress = false;
493
499
  this.setApkCompletedViaBuildIncrease(false);
@@ -633,6 +639,17 @@ class OtaInstallCoordinator {
633
639
  versionChangeSession: this.versionChangeSession,
634
640
  });
635
641
  const stallSig = buildProgressStalenessSignature(otaStatus, otaProgress, displayState);
642
+ // Percent is a UI projection, not transfer liveness. Repeated status replies (including
643
+ // older replies without bytes) must not keep a stalled download alive.
644
+ const downloadKey = otaStatus?.phase === "download" ? `${otaStatus.sessionId}|${otaStatus.currentStep}|${otaStatus.stepType}` : "";
645
+ if (downloadKey !== this.downloadStepKey) {
646
+ this.downloadStepKey = downloadKey;
647
+ this.downloadByteHighWater = 0;
648
+ }
649
+ const receivedBytes = otaStatus?.bytesDownloaded ?? 0;
650
+ const downloadAdvanced = downloadKey !== "" && Number.isSafeInteger(receivedBytes) && receivedBytes > this.downloadByteHighWater;
651
+ if (downloadAdvanced)
652
+ this.downloadByteHighWater = receivedBytes;
636
653
  const stallDuration = progressTimeoutDurationMs(otaStatus, otaProgress, legacySession);
637
654
  const displayStateChanged = isMount || displayState !== this.lastDisplayState;
638
655
  const stallSigChanged = isMount || stallSig !== this.lastStallSig;
@@ -808,7 +825,7 @@ class OtaInstallCoordinator {
808
825
  if ((otaStatusChanged || otaProgressChanged) && (otaStatus || otaProgress)) {
809
826
  this.onFirstActivity();
810
827
  }
811
- // The stuck-at-zero watchdog clears only on the FIRST real (>0%) progress.
828
+ // The stuck-at-zero watchdog clears only on real byte or percent progress.
812
829
  // Without this distinction, an ota_status reply with stepPercent=0 would
813
830
  // disable the watchdog before any download had actually started, hiding
814
831
  // wedged downloads from the user.
@@ -816,7 +833,7 @@ class OtaInstallCoordinator {
816
833
  const stepPct = otaStatus?.stepPercent ?? 0;
817
834
  const overallPct = otaStatus?.overallPercent ?? 0;
818
835
  const legacyPct = otaProgress?.progress ?? 0;
819
- if (stepPct > 0 || overallPct > 0 || legacyPct > 0) {
836
+ if (downloadAdvanced || stepPct > 0 || overallPct > 0 || legacyPct > 0) {
820
837
  this.onFirstNonZeroProgress();
821
838
  }
822
839
  }
@@ -824,7 +841,7 @@ class OtaInstallCoordinator {
824
841
  // Keyed on the staleness SIGNATURE string (not the object refs) so a store
825
842
  // update that doesn't change the signature keeps the same timer running
826
843
  // instead of forever re-extending the deadline.
827
- if (stallSigChanged || displayStateChanged) {
844
+ if (stallSigChanged || displayStateChanged || downloadAdvanced) {
828
845
  if (this.isInVersionChangeDetour()) {
829
846
  // Recovery worker owns the transaction; there are no progress events to stall on.
830
847
  this.clearProgressTimeout();
@@ -1173,7 +1190,7 @@ class OtaInstallCoordinator {
1173
1190
  /**
1174
1191
  * "Glasses are talking to us" — suppresses retry if the native request later rejects.
1175
1192
  * Important: this does NOT clear the stuck-at-zero watchdog; that one fires
1176
- * on real progress > 0% (see {@link onFirstNonZeroProgress}).
1193
+ * on real byte/percent progress (see {@link onFirstNonZeroProgress}).
1177
1194
  */
1178
1195
  onFirstActivity() {
1179
1196
  if (this.hasFirstActivity)
@@ -1186,10 +1203,8 @@ class OtaInstallCoordinator {
1186
1203
  }
1187
1204
  /**
1188
1205
  * "Real download progress arrived" — clears the stuck-at-zero watchdog. We
1189
- * deliberately wait for non-zero progress before clearing this so that an
1190
- * ota_status reply with stepPercent: 0 (which is "first activity" but not
1191
- * "real progress") doesn't disable the only watchdog that catches a wedged
1192
- * download.
1206
+ * require advancing bytes or non-zero percent, so an ordinary status reply
1207
+ * cannot disable the watchdog that catches a wedged download.
1193
1208
  */
1194
1209
  onFirstNonZeroProgress() {
1195
1210
  if (this.hasFirstNonZeroProgress)