@mentra/engine 3.4.0-dev.1209 → 3.4.0-dev.1210

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.4.0",
4
- "releaseIdentity": "3.4.0-dev.1209",
5
- "releaseSetId": "mentra-3.4.0-dev.1209",
6
- "sourceCommit": "1f73b5402e1a0b5f06000b84300695f0dfc879c1",
7
- "otaManifestUrl": "https://artifactscdn.mentraglass.com/Mentra-Community/MentraOS/releases/mentra-builds-v3.4.0/mentra-live-ota-3.4.0-dev.1209.json",
8
- "otaManifestSha256": "f93b7fed880136625a8b29c4e74210e882b5322bd79f76aa84d79a31bb95b82a"
4
+ "releaseIdentity": "3.4.0-dev.1210",
5
+ "releaseSetId": "mentra-3.4.0-dev.1210",
6
+ "sourceCommit": "2007a90070b4909ea37ff32423b29afb2cef7c01",
7
+ "otaManifestUrl": "https://artifactscdn.mentraglass.com/Mentra-Community/MentraOS/releases/mentra-builds-v3.4.0/mentra-live-ota-3.4.0-dev.1210.json",
8
+ "otaManifestSha256": "88dd086945cfe6b9f90928cdbbc9f82612277ec7894f3360f8539a489972fcc4"
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,gBAAgB;IACnC,cAAc,EAAE,uBAAuB;IACvC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,kIAAkI;IACpJ,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.4.0\",\n \"releaseIdentity\": \"3.4.0-dev.1209\",\n \"releaseSetId\": \"mentra-3.4.0-dev.1209\",\n \"sourceCommit\": \"1f73b5402e1a0b5f06000b84300695f0dfc879c1\",\n \"otaManifestUrl\": \"https://artifactscdn.mentraglass.com/Mentra-Community/MentraOS/releases/mentra-builds-v3.4.0/mentra-live-ota-3.4.0-dev.1209.json\",\n \"otaManifestSha256\": \"f93b7fed880136625a8b29c4e74210e882b5322bd79f76aa84d79a31bb95b82a\"\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,gBAAgB;IACnC,cAAc,EAAE,uBAAuB;IACvC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,kIAAkI;IACpJ,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.4.0\",\n \"releaseIdentity\": \"3.4.0-dev.1210\",\n \"releaseSetId\": \"mentra-3.4.0-dev.1210\",\n \"sourceCommit\": \"2007a90070b4909ea37ff32423b29afb2cef7c01\",\n \"otaManifestUrl\": \"https://artifactscdn.mentraglass.com/Mentra-Community/MentraOS/releases/mentra-builds-v3.4.0/mentra-live-ota-3.4.0-dev.1210.json\",\n \"otaManifestSha256\": \"88dd086945cfe6b9f90928cdbbc9f82612277ec7894f3360f8539a489972fcc4\"\n})\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"MiniappEngine.d.ts","sourceRoot":"","sources":["../../src/services/MiniappEngine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAQH,OAAO,EAAC,uBAAuB,EAAC,MAAM,2BAA2B,CAAA;AACjE,OAAO,EAAC,cAAc,EAA4B,MAAM,kBAAkB,CAAA;AAC1E,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAG/C,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,cAAc,CAAA;IACtB,QAAQ,EAAE,cAAc,CAAA;IACxB,eAAe,EAAE,uBAAuB,CAAA;CACzC;AAuDD;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,aAAa,CA0CnD;AAED,8EAA8E;AAC9E,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAsBvD;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,IAAI,aAAa,GAAG,IAAI,CAEvD"}
1
+ {"version":3,"file":"MiniappEngine.d.ts","sourceRoot":"","sources":["../../src/services/MiniappEngine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAQH,OAAO,EAAC,uBAAuB,EAAC,MAAM,2BAA2B,CAAA;AACjE,OAAO,EAAC,cAAc,EAA4B,MAAM,kBAAkB,CAAA;AAC1E,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAG/C,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,cAAc,CAAA;IACtB,QAAQ,EAAE,cAAc,CAAA;IACxB,eAAe,EAAE,uBAAuB,CAAA;CACzC;AAyDD;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,aAAa,CA0CnD;AAED,8EAA8E;AAC9E,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAsBvD;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,IAAI,aAAa,GAAG,IAAI,CAEvD"}
@@ -35,6 +35,8 @@ function wireDevServerRespawnBackground(router, uiRouter) {
35
35
  console.warn(`MentraJS: respawn-bg could not resolve bundle for ${packageName}`);
36
36
  return;
37
37
  }
38
+ // The rebuilt bundle meets a launch's Mentra App version rule; refused, the running context stays.
39
+ miniappLauncher.assertRunsOnPhone(packageName, resolved);
38
40
  // Force a respawn (kill + spawn) rather than launcher.ensureRunning,
39
41
  // which is idempotent and would no-op an already-registered context.
40
42
  const projectRunning = router.isProjectedRunning(packageName);
@@ -1 +1 @@
1
- {"version":3,"file":"MiniappEngine.js","sourceRoot":"","sources":["../../src/services/MiniappEngine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MiniappEngine — constructs the MentraJS router singletons (crash controller,\n * UI router, JS router), binds them to the native Crust module + the launcher,\n * wires the dev-server background-respawn signal, and starts the message pump.\n *\n * Engine owns this so a bare OEM's `engine.start()` brings up the local-miniapp\n * engine with no host construction step. Host-only concerns that remain outside\n * engine, such as Mentra-app crashloop telemetry, are attached by the host after\n * this returns via `router.onCrashloop` / `router.onRestartToast`.\n *\n * Idempotent — `ensureMiniappEngine()` returns the same singletons on every call,\n * so it's safe to call from both `engine.start()` and the host bootstrap.\n */\n\nimport CrustModule from \"@mentra/crust\"\n\nimport devServerBridge from \"./DevServerBridge\"\nimport {fleetEvents} from \"./fleet/fleetEvents\"\nimport localMiniappRuntime from \"./LocalMiniappRuntime\"\nimport {miniappLauncher} from \"./MiniappLauncher\"\nimport {MentraJSCrashController} from \"./MentraJSCrashController\"\nimport {MentraJSRouter, type MentraJSCrustBinding} from \"./MentraJSRouter\"\nimport {MentraUIRouter} from \"./MentraUIRouter\"\nimport {BgTimer} from \"../utils/timers\"\n\nexport interface MiniappEngine {\n router: MentraJSRouter\n uiRouter: MentraUIRouter\n crashController: MentraJSCrashController\n}\n\nlet engine: MiniappEngine | null = null\n\nfunction wireDevServerRespawnBackground(router: MentraJSRouter, uiRouter: MentraUIRouter): void {\n devServerBridge.onRespawnBackground(async (packageName) => {\n try {\n // Re-resolve the freshly built bundle (dev: HTTP off the dev server;\n // released: file:// snapshot) via the launcher's shared recipe. Carries\n // the declared permissions + manifest across the respawn — omitting them\n // would respawn the JSContext with no permissions, so SUBSCRIBE gates and\n // per-call dispatch checks would start rejecting after a background\n // hot-reload.\n const resolved = await miniappLauncher.resolveBundle(packageName)\n if (!resolved) {\n console.warn(`MentraJS: respawn-bg could not resolve bundle for ${packageName}`)\n return\n }\n // Force a respawn (kill + spawn) rather than launcher.ensureRunning,\n // which is idempotent and would no-op an already-registered context.\n const projectRunning = router.isProjectedRunning(packageName)\n await router.unregister(packageName)\n const ok = await router.spawnAndRegister(packageName, resolved.bgSource, {\n permissions: resolved.declaredPermissions,\n installedManifest: resolved.installedManifest,\n hostTrustedSystem: resolved.hostTrustedSystem,\n projectRunning,\n developmentBuild: resolved.devBuild,\n })\n // A miniapp running as user activity starts again from the new bundle: an automatic launch to Fleet.\n if (projectRunning) {\n const manifest = resolved.installedManifest\n fleetEvents.launchUnlessClaimed({\n packageName,\n ...(manifest?.name ? {miniappName: manifest.name} : {}),\n ...(manifest?.version ? {versionName: manifest.version} : {}),\n start: \"automatic\",\n outcome: ok ? \"started\" : \"failed\",\n })\n }\n if (!ok) {\n console.warn(`MentraJS: respawn-bg failed for ${packageName}`)\n return\n }\n // The respawned JSContext is a fresh MiniappSession with ui.bound false.\n // The mounted WebView won't re-fire mentra.ready() (it's latched), so\n // re-announce it so RPC replies (mentra.request) reach the UI again\n // instead of being dropped while unbound.\n uiRouter.notifyReopen(packageName)\n } catch (e) {\n console.warn(`MentraJS: respawn-bg threw for ${packageName}:`, e)\n }\n })\n}\n\n/**\n * Construct (once) and return the MentraJS engine singletons. Starts the router's\n * message pump on first call. Subsequent calls return the same instances.\n */\nexport function ensureMiniappEngine(): MiniappEngine {\n if (engine) {\n wireDevServerRespawnBackground(engine.router, engine.uiRouter)\n engine.router.start()\n return engine\n }\n\n // The Crust native module's published TS typing doesn't include the new\n // mentraJs* functions until expo prebuild regenerates it; cast to the binding\n // shapes the routers expect.\n const crust = CrustModule as unknown as MentraJSCrustBinding\n\n const crashController = new MentraJSCrashController({\n maxRetries: 3,\n })\n const uiRouter = new MentraUIRouter({\n mentraJsDispatchToJs: (packageName: string, envelope: Record<string, unknown>) =>\n (\n CrustModule as unknown as {\n mentraJsDispatchToJs: (p: string, e: Record<string, unknown>) => Promise<void>\n }\n ).mentraJsDispatchToJs(packageName, envelope),\n })\n const router = new MentraJSRouter(localMiniappRuntime, crust)\n router.crashController = crashController\n router.uiRouter = uiRouter\n router.timer = BgTimer\n\n // Hand the router to the engine MiniappLauncher so headless launch/teardown\n // (apps.ts start/stop, the action broker, the WebView mount path) all spawn\n // through one place.\n miniappLauncher.configure({router})\n\n // Wire up the dev server's \"respawn-bg\" signal so a touch under\n // src/background/ kills + re-spawns the JSContext with the latest bundle.\n // The WebView reload path stays separate (devServerBridge.onReload).\n wireDevServerRespawnBackground(router, uiRouter)\n\n router.start()\n\n engine = {router, uiRouter, crashController}\n return engine\n}\n\n/** Stop the MentraJS message pump and tear down router-managed JSContexts. */\nexport async function stopMiniappEngine(): Promise<void> {\n const current = engine\n if (!current) return\n // Null the singleton BEFORE the async teardown below: a concurrent\n // ensureMiniappEngine() must construct fresh instead of re-starting (and\n // re-subscribing) the router that is mid-teardown, which would leak a zombie\n // Crust subscription once this function finishes.\n engine = null\n\n devServerBridge.clearRespawnBackgroundHandler()\n current.router.stop()\n\n const packages = current.router.registeredPackages()\n await Promise.all(\n packages.map(async (packageName) => {\n try {\n await current.router.unregister(packageName)\n } catch (err) {\n console.warn(`MentraJS: failed to unregister ${packageName} during stop:`, err)\n }\n }),\n )\n}\n\n/** Returns the engine singletons if already constructed, else null. */\nexport function getMiniappEngine(): MiniappEngine | null {\n return engine\n}\n"]}
1
+ {"version":3,"file":"MiniappEngine.js","sourceRoot":"","sources":["../../src/services/MiniappEngine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MiniappEngine — constructs the MentraJS router singletons (crash controller,\n * UI router, JS router), binds them to the native Crust module + the launcher,\n * wires the dev-server background-respawn signal, and starts the message pump.\n *\n * Engine owns this so a bare OEM's `engine.start()` brings up the local-miniapp\n * engine with no host construction step. Host-only concerns that remain outside\n * engine, such as Mentra-app crashloop telemetry, are attached by the host after\n * this returns via `router.onCrashloop` / `router.onRestartToast`.\n *\n * Idempotent — `ensureMiniappEngine()` returns the same singletons on every call,\n * so it's safe to call from both `engine.start()` and the host bootstrap.\n */\n\nimport CrustModule from \"@mentra/crust\"\n\nimport devServerBridge from \"./DevServerBridge\"\nimport {fleetEvents} from \"./fleet/fleetEvents\"\nimport localMiniappRuntime from \"./LocalMiniappRuntime\"\nimport {miniappLauncher} from \"./MiniappLauncher\"\nimport {MentraJSCrashController} from \"./MentraJSCrashController\"\nimport {MentraJSRouter, type MentraJSCrustBinding} from \"./MentraJSRouter\"\nimport {MentraUIRouter} from \"./MentraUIRouter\"\nimport {BgTimer} from \"../utils/timers\"\n\nexport interface MiniappEngine {\n router: MentraJSRouter\n uiRouter: MentraUIRouter\n crashController: MentraJSCrashController\n}\n\nlet engine: MiniappEngine | null = null\n\nfunction wireDevServerRespawnBackground(router: MentraJSRouter, uiRouter: MentraUIRouter): void {\n devServerBridge.onRespawnBackground(async (packageName) => {\n try {\n // Re-resolve the freshly built bundle (dev: HTTP off the dev server;\n // released: file:// snapshot) via the launcher's shared recipe. Carries\n // the declared permissions + manifest across the respawn — omitting them\n // would respawn the JSContext with no permissions, so SUBSCRIBE gates and\n // per-call dispatch checks would start rejecting after a background\n // hot-reload.\n const resolved = await miniappLauncher.resolveBundle(packageName)\n if (!resolved) {\n console.warn(`MentraJS: respawn-bg could not resolve bundle for ${packageName}`)\n return\n }\n // The rebuilt bundle meets a launch's Mentra App version rule; refused, the running context stays.\n miniappLauncher.assertRunsOnPhone(packageName, resolved)\n // Force a respawn (kill + spawn) rather than launcher.ensureRunning,\n // which is idempotent and would no-op an already-registered context.\n const projectRunning = router.isProjectedRunning(packageName)\n await router.unregister(packageName)\n const ok = await router.spawnAndRegister(packageName, resolved.bgSource, {\n permissions: resolved.declaredPermissions,\n installedManifest: resolved.installedManifest,\n hostTrustedSystem: resolved.hostTrustedSystem,\n projectRunning,\n developmentBuild: resolved.devBuild,\n })\n // A miniapp running as user activity starts again from the new bundle: an automatic launch to Fleet.\n if (projectRunning) {\n const manifest = resolved.installedManifest\n fleetEvents.launchUnlessClaimed({\n packageName,\n ...(manifest?.name ? {miniappName: manifest.name} : {}),\n ...(manifest?.version ? {versionName: manifest.version} : {}),\n start: \"automatic\",\n outcome: ok ? \"started\" : \"failed\",\n })\n }\n if (!ok) {\n console.warn(`MentraJS: respawn-bg failed for ${packageName}`)\n return\n }\n // The respawned JSContext is a fresh MiniappSession with ui.bound false.\n // The mounted WebView won't re-fire mentra.ready() (it's latched), so\n // re-announce it so RPC replies (mentra.request) reach the UI again\n // instead of being dropped while unbound.\n uiRouter.notifyReopen(packageName)\n } catch (e) {\n console.warn(`MentraJS: respawn-bg threw for ${packageName}:`, e)\n }\n })\n}\n\n/**\n * Construct (once) and return the MentraJS engine singletons. Starts the router's\n * message pump on first call. Subsequent calls return the same instances.\n */\nexport function ensureMiniappEngine(): MiniappEngine {\n if (engine) {\n wireDevServerRespawnBackground(engine.router, engine.uiRouter)\n engine.router.start()\n return engine\n }\n\n // The Crust native module's published TS typing doesn't include the new\n // mentraJs* functions until expo prebuild regenerates it; cast to the binding\n // shapes the routers expect.\n const crust = CrustModule as unknown as MentraJSCrustBinding\n\n const crashController = new MentraJSCrashController({\n maxRetries: 3,\n })\n const uiRouter = new MentraUIRouter({\n mentraJsDispatchToJs: (packageName: string, envelope: Record<string, unknown>) =>\n (\n CrustModule as unknown as {\n mentraJsDispatchToJs: (p: string, e: Record<string, unknown>) => Promise<void>\n }\n ).mentraJsDispatchToJs(packageName, envelope),\n })\n const router = new MentraJSRouter(localMiniappRuntime, crust)\n router.crashController = crashController\n router.uiRouter = uiRouter\n router.timer = BgTimer\n\n // Hand the router to the engine MiniappLauncher so headless launch/teardown\n // (apps.ts start/stop, the action broker, the WebView mount path) all spawn\n // through one place.\n miniappLauncher.configure({router})\n\n // Wire up the dev server's \"respawn-bg\" signal so a touch under\n // src/background/ kills + re-spawns the JSContext with the latest bundle.\n // The WebView reload path stays separate (devServerBridge.onReload).\n wireDevServerRespawnBackground(router, uiRouter)\n\n router.start()\n\n engine = {router, uiRouter, crashController}\n return engine\n}\n\n/** Stop the MentraJS message pump and tear down router-managed JSContexts. */\nexport async function stopMiniappEngine(): Promise<void> {\n const current = engine\n if (!current) return\n // Null the singleton BEFORE the async teardown below: a concurrent\n // ensureMiniappEngine() must construct fresh instead of re-starting (and\n // re-subscribing) the router that is mid-teardown, which would leak a zombie\n // Crust subscription once this function finishes.\n engine = null\n\n devServerBridge.clearRespawnBackgroundHandler()\n current.router.stop()\n\n const packages = current.router.registeredPackages()\n await Promise.all(\n packages.map(async (packageName) => {\n try {\n await current.router.unregister(packageName)\n } catch (err) {\n console.warn(`MentraJS: failed to unregister ${packageName} during stop:`, err)\n }\n }),\n )\n}\n\n/** Returns the engine singletons if already constructed, else null. */\nexport function getMiniappEngine(): MiniappEngine | null {\n return engine\n}\n"]}
@@ -53,6 +53,18 @@ export declare class MiniappAgeRestrictedError extends Error {
53
53
  readonly verdict: Exclude<AgeGateVerdict, "allowed">;
54
54
  constructor(packageName: string, verdict: Exclude<AgeGateVerdict, "allowed">);
55
55
  }
56
+ /**
57
+ * The bundle needs a newer Mentra App, or a Mentra Miniapp SDK this one doesn't run: the rule installs refuse by,
58
+ * checked again as it launches. A release a newer Mentra App installed stays on disk when an older one replaces it (a
59
+ * development build replaced from TestFlight), and would otherwise start against features this one lacks. The message
60
+ * is for logs; a screen explains it in the person's language, as an install refused by the same rule does.
61
+ */
62
+ export declare class MiniappIncompatibleHostError extends Error {
63
+ readonly packageName: string;
64
+ readonly miniappName: string;
65
+ readonly reason: string;
66
+ constructor(packageName: string, miniappName: string, reason: string);
67
+ }
56
68
  interface LauncherDeps {
57
69
  /** The host-constructed router (needs the native Crust binding). */
58
70
  router: MentraJSRouter;
@@ -128,7 +140,8 @@ export interface LaunchResult {
128
140
  }
129
141
  /**
130
142
  * Why a launch failed, as the code Fleet takes (Phase 12): never the error's text. A bundle that cannot be found is
131
- * `bundle_missing`; a deployment, age or access block `policy_blocked`; a context that did not start `spawn_failed`.
143
+ * `bundle_missing`; one that needs a newer Mentra App `incompatible_host`; a deployment, age or access block
144
+ * `policy_blocked`; a context that did not start `spawn_failed`.
132
145
  */
133
146
  export declare function launchFailureReason(error: unknown): FleetLaunchFailureReason;
134
147
  declare class MiniappLauncher {
@@ -185,6 +198,8 @@ declare class MiniappLauncher {
185
198
  resolveBundle(packageName: string, hints?: LaunchHints, runtimeOptions?: RuntimeLaunchOptions): Promise<ResolvedBundle | null>;
186
199
  private resolveLiveHttp;
187
200
  private resolveInstalledBundle;
201
+ /** Throws {@link MiniappIncompatibleHostError} when this Mentra App can't run the resolved bundle. */
202
+ assertRunsOnPhone(packageName: string, resolved: ResolvedBundle): void;
188
203
  /**
189
204
  * Ensure a miniapp's background JS context is spawned. Idempotent — if the
190
205
  * context is already alive this is a no-op spawn-wise (it still resolves the
@@ -221,9 +236,10 @@ declare class MiniappLauncher {
221
236
  * persisted running flag if ensureRunning fails (for example, bundle resolution,
222
237
  * deployment policy, or context spawning) so it does not retry every boot.
223
238
  * Each restore is an automatic launch to Fleet, reported by ensureRunning.
224
- * Not compatibility-gated: a previously-running background app shouldn't be
239
+ * Not gated on the glasses' hardware: a previously-running background app shouldn't be
225
240
  * dropped just because glasses are momentarily disconnected at boot — it
226
- * resumes when they reconnect, same as mid-session.
241
+ * resumes when they reconnect, same as mid-session. A release this Mentra App
242
+ * can't run is refused like any launch (see {@link assertRunsOnPhone}).
227
243
  */
228
244
  autostartLocalMiniapps(): Promise<void>;
229
245
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"MiniappLauncher.d.ts","sourceRoot":"","sources":["../../src/services/MiniappLauncher.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MiniappLauncher.d.ts","sourceRoot":"","sources":["../../src/services/MiniappLauncher.ts"],"names":[],"mappings":"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"}
@@ -37,7 +37,7 @@
37
37
  */
38
38
  import { File } from "expo-file-system";
39
39
  import { AppState, Platform } from "react-native";
40
- import { isDevMiniappAllowed, isMiniappAvailable } from "../runtime/bootstrap";
40
+ import { getConfigValues, isDevMiniappAllowed, isMiniappAvailable, } from "../runtime/bootstrap";
41
41
  import { resolveDevBundleSource } from "../utils/devMiniappSnapshot";
42
42
  import { storage } from "../utils/storage/storage";
43
43
  import { MiniappRunningError } from "../utils/storeInstallRuntime";
@@ -45,6 +45,7 @@ import appRegistry, { getLocalAppRunningState, saveLocalAppRunningState } from "
45
45
  import devServerBridge from "./DevServerBridge";
46
46
  import { fleetEvents } from "./fleet/fleetEvents";
47
47
  import localMiniappRuntime from "./LocalMiniappRuntime";
48
+ import { checkManifestVersions } from "./manifestVersionGate";
48
49
  import { checkMiniappAge } from "./miniappAgeGate";
49
50
  import { developmentOriginName, miniappOrigin, miniappOriginUrl } from "./miniappOrigin";
50
51
  import { isHostTrustedSystemMiniapp } from "./SystemMiniappPolicy";
@@ -71,6 +72,24 @@ export class MiniappAgeRestrictedError extends Error {
71
72
  this.name = "MiniappAgeRestrictedError";
72
73
  }
73
74
  }
75
+ /**
76
+ * The bundle needs a newer Mentra App, or a Mentra Miniapp SDK this one doesn't run: the rule installs refuse by,
77
+ * checked again as it launches. A release a newer Mentra App installed stays on disk when an older one replaces it (a
78
+ * development build replaced from TestFlight), and would otherwise start against features this one lacks. The message
79
+ * is for logs; a screen explains it in the person's language, as an install refused by the same rule does.
80
+ */
81
+ export class MiniappIncompatibleHostError extends Error {
82
+ packageName;
83
+ miniappName;
84
+ reason;
85
+ constructor(packageName, miniappName, reason) {
86
+ super(`${miniappName} can't run on this phone: ${reason}`);
87
+ this.packageName = packageName;
88
+ this.miniappName = miniappName;
89
+ this.reason = reason;
90
+ this.name = "MiniappIncompatibleHostError";
91
+ }
92
+ }
74
93
  function availabilityMode(options) {
75
94
  return options?.projectRunning === false ? "background" : "interactive";
76
95
  }
@@ -97,9 +116,12 @@ function serverOrigin(url) {
97
116
  }
98
117
  /**
99
118
  * Why a launch failed, as the code Fleet takes (Phase 12): never the error's text. A bundle that cannot be found is
100
- * `bundle_missing`; a deployment, age or access block `policy_blocked`; a context that did not start `spawn_failed`.
119
+ * `bundle_missing`; one that needs a newer Mentra App `incompatible_host`; a deployment, age or access block
120
+ * `policy_blocked`; a context that did not start `spawn_failed`.
101
121
  */
102
122
  export function launchFailureReason(error) {
123
+ if (error instanceof MiniappIncompatibleHostError)
124
+ return "incompatible_host";
103
125
  if (error instanceof MiniappAgeRestrictedError || error instanceof MiniappAccessRevokedError)
104
126
  return "policy_blocked";
105
127
  const message = error instanceof Error ? error.message : "";
@@ -365,6 +387,16 @@ class MiniappLauncher {
365
387
  devPort: null,
366
388
  };
367
389
  }
390
+ /** Throws {@link MiniappIncompatibleHostError} when this Mentra App can't run the resolved bundle. */
391
+ assertRunsOnPhone(packageName, resolved) {
392
+ const manifest = resolved.installedManifest;
393
+ const { hostVersion, supportedMiniappSdkRange } = getConfigValues();
394
+ if (!manifest || !hostVersion || !supportedMiniappSdkRange)
395
+ return;
396
+ const versions = checkManifestVersions(manifest, { hostVersion, supportedSdkRange: supportedMiniappSdkRange });
397
+ if (!versions.ok)
398
+ throw new MiniappIncompatibleHostError(packageName, manifest.name || packageName, versions.reason);
399
+ }
368
400
  /**
369
401
  * Ensure a miniapp's background JS context is spawned. Idempotent — if the
370
402
  * context is already alive this is a no-op spawn-wise (it still resolves the
@@ -462,6 +494,8 @@ class MiniappLauncher {
462
494
  if (!resolved) {
463
495
  throw new Error(`MiniappLauncher: cannot resolve bundle for ${packageName}`);
464
496
  }
497
+ // Before the age gate, so a miniapp this app can't run never asks the person's age.
498
+ this.assertRunsOnPhone(packageName, resolved);
465
499
  const version = resolved.installedManifest?.version;
466
500
  const projectRunning = runtimeOptions?.projectRunning ?? true;
467
501
  // The age gate, before the deployment checks below, which then see the state after any question the person
@@ -531,9 +565,10 @@ class MiniappLauncher {
531
565
  * persisted running flag if ensureRunning fails (for example, bundle resolution,
532
566
  * deployment policy, or context spawning) so it does not retry every boot.
533
567
  * Each restore is an automatic launch to Fleet, reported by ensureRunning.
534
- * Not compatibility-gated: a previously-running background app shouldn't be
568
+ * Not gated on the glasses' hardware: a previously-running background app shouldn't be
535
569
  * dropped just because glasses are momentarily disconnected at boot — it
536
- * resumes when they reconnect, same as mid-session.
570
+ * resumes when they reconnect, same as mid-session. A release this Mentra App
571
+ * can't run is refused like any launch (see {@link assertRunsOnPhone}).
537
572
  */
538
573
  async autostartLocalMiniapps() {
539
574
  const apps = await appRegistry.getInstalledMiniapps();
@@ -1 +1 @@
1
- {"version":3,"file":"MiniappLauncher.js","sourceRoot":"","sources":["../../src/services/MiniappLauncher.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MiniappLauncher — headless launch/teardown of a local miniapp's background\n * JS context.\n *\n * This is the one piece of \"run a miniapp\" that used to live inside the\n * `LocalMiniappView` React component (resolve the bundle → read the manifest →\n * `spawnAndRegister`), triggered only when the Compositor mounted that\n * component on foreground. Pulling it into the runtime means:\n *\n * - `start()` can actually spawn an app even when nothing foregrounds it\n * (e.g. a system app launching another miniapp via `session.miniapps`).\n * - the action broker can wake a stopped target headlessly — no UI at all.\n * - the dev hot-reload respawn path and the WebView mount path share one\n * resolve+spawn recipe instead of three copies.\n *\n * The launcher is **headless**: no React, no RN components. The engine-owned\n * MiniappEngine hands it the {@link MentraJSRouter} instance once during\n * engine construction. OEMs embedding the runtime drive miniapp lifecycle\n * through the engine facade and stores, not a separate launcher configure API.\n *\n * Boundary: the launcher owns the **background** context. WebView *rendering*\n * (the UI layer) stays with the host — the launcher just hands back the\n * resolved UI entry so the host can mount its `<WebView>`.\n *\n * Fleet: every path that makes a local miniapp execute as user activity comes\n * through here, so the launcher reports each such launch (and each that fails\n * once past the availability gate) — a restore, a miniapp started by another\n * miniapp or an action, a screen opened directly, a restart after a reinstall.\n * A launch the apps store's `start` makes is reported by `start` itself\n * (`fleetEvents.claimLaunch`); a context already executing, or one a hidden\n * action wake keeps out of the running tray, is no launch.\n *\n * Age ratings: every launch path comes through here, so this is where a miniapp rated above the host app's own\n * rating meets the age gate as it opens, whether it was installed with the sheet or headlessly (a Fleet assignment,\n * a system miniapp). A launch the person makes in front of the app may ask their age once; any other launch needs it\n * declared already.\n */\n\nimport type {FleetLaunchFailureReason} from \"@mentra/cloud-protocol\"\nimport {File} from \"expo-file-system\"\nimport {AppState, Platform} from \"react-native\"\n\nimport {isDevMiniappAllowed, isMiniappAvailable, type MiniappAvailabilityMode} from \"../runtime/bootstrap\"\nimport {resolveDevBundleSource} from \"../utils/devMiniappSnapshot\"\nimport {storage} from \"../utils/storage/storage\"\nimport {MiniappRunningError} from \"../utils/storeInstallRuntime\"\nimport appRegistry, {getLocalAppRunningState, saveLocalAppRunningState} from \"./AppRegistry\"\nimport devServerBridge from \"./DevServerBridge\"\nimport {fleetEvents} from \"./fleet/fleetEvents\"\nimport localMiniappRuntime, {type InstalledMiniappManifest} from \"./LocalMiniappRuntime\"\nimport type {MentraJSRouter} from \"./MentraJSRouter\"\nimport {checkMiniappAge, type AgeGateVerdict, type AgeRatedMiniapp} from \"./miniappAgeGate\"\nimport {developmentOriginName, miniappOrigin, miniappOriginUrl, type MiniappOriginPlatform} from \"./miniappOrigin\"\nimport {isHostTrustedSystemMiniapp} from \"./SystemMiniappPolicy\"\n\n/** The miniapp's server no longer admits this account (Fleet revocation): it cannot launch until access returns. */\nexport class MiniappAccessRevokedError extends Error {\n constructor(readonly packageName: string) {\n super(`MiniappLauncher: access to ${packageName} was revoked`)\n this.name = \"MiniappAccessRevokedError\"\n }\n}\n\n/**\n * The miniapp is rated above the host app's own rating and the person's declared age doesn't let it open (see\n * {@link AgeGateVerdict}). A person opening it in front of the app was asked their age, or told why.\n */\nexport class MiniappAgeRestrictedError extends Error {\n constructor(\n readonly packageName: string,\n readonly verdict: Exclude<AgeGateVerdict, \"allowed\">,\n ) {\n super(`MiniappLauncher: ${packageName} is age-rated and did not open (${verdict})`)\n this.name = \"MiniappAgeRestrictedError\"\n }\n}\n\ninterface LauncherDeps {\n /** The host-constructed router (needs the native Crust binding). */\n router: MentraJSRouter\n}\n\n/** Hints the host may pass from a view's props to avoid re-deriving them. */\nexport interface LaunchHints {\n devUrl?: string\n /** Retained for caller compatibility; released launches use the committed active version. */\n version?: string\n devPort?: string\n}\n\nexport interface RuntimeLaunchOptions {\n /** False for an invocation-scoped wake that must not enter the running tray. */\n projectRunning?: boolean\n /**\n * Who started the miniapp, for the Fleet launch this call makes, if it makes one: the person (a screen they opened)\n * or the platform (a restore, another miniapp, a restart). Defaults to automatic.\n */\n trigger?: \"user\" | \"automatic\"\n}\n\nfunction availabilityMode(options?: RuntimeLaunchOptions): MiniappAvailabilityMode {\n return options?.projectRunning === false ? \"background\" : \"interactive\"\n}\n\n/** Everything the launcher resolved for a package — bg source + UI entry. */\nexport interface ResolvedBundle {\n bgSource: string\n /** Where the screen loads from. See {@link LaunchResult.uiUri}. */\n uiUri: string | null\n /** The origin `uiUri` belongs to. See {@link LaunchResult.uiOrigin}. */\n uiOrigin: string | null\n /** The bundle root the package's origin serves. See {@link LaunchResult.uiRootDir}. */\n uiRootDir: string | null\n /** The name the origin is for. See {@link LaunchResult.uiOriginName}. */\n uiOriginName: string | null\n /** A development build: a live development server or a development snapshot. */\n devBuild: boolean\n declaredPermissions: string[]\n installedManifest?: InstalledMiniappManifest\n /** What the bundle's `miniapp.json` declares as `ageRating`; null when it declares none. */\n ageRating: unknown\n /** Build-owned SYSTEM trust derived from install provenance, never the manifest. */\n hostTrustedSystem: boolean\n /** Set for dev miniapps (HTTP off the dev server); null for released. */\n devUrl: string | null\n devPort: number | null\n}\n\n/** What the host needs back to render the UI layer. */\nexport interface LaunchResult {\n /**\n * Where the screen loads from. An installed release loads from its\n * package's own origin: `miniapp://<packageName>/<ui entry>` on iOS and\n * `https://<host>.miniapp.invalid/<ui entry>` on Android (see\n * {@link miniappOriginUrl}), a development snapshot from the origin of its\n * {@link uiOriginName}. A live development build loads from its development\n * server. Null when there is no screen to load.\n */\n uiUri: string | null\n /** The origin `uiUri` belongs to, for the WebView's `originWhitelist`. */\n uiOrigin: string | null\n /**\n * The root of the active version's bundle, which the package's origin\n * serves (the WebView's `miniappOrigin.rootDir`). Null when the screen loads\n * from a development server.\n */\n uiRootDir: string | null\n /**\n * The name the origin serves `uiRootDir` under (the WebView's\n * `miniappOrigin.packageName`): the package name for a release, and\n * {@link developmentOriginName} for a development snapshot, so a development\n * build never shares web storage with a release of its name. Null with\n * `uiRootDir`.\n */\n uiOriginName: string | null\n /** A development build: a live development server or a development snapshot. */\n devBuild: boolean\n}\n\nconst NO_UI: LaunchResult = {uiUri: null, uiOrigin: null, uiRootDir: null, uiOriginName: null, devBuild: false}\n\nfunction launchResultOf(resolved: ResolvedBundle | null | undefined): LaunchResult {\n if (!resolved) return NO_UI\n return {\n uiUri: resolved.uiUri,\n uiOrigin: resolved.uiOrigin,\n uiRootDir: resolved.uiRootDir,\n uiOriginName: resolved.uiOriginName,\n devBuild: resolved.devBuild,\n }\n}\n\n/** The platform whose origin shape the Mentra App's WebView serves. */\nfunction originPlatform(): MiniappOriginPlatform {\n return Platform.OS === \"android\" ? \"android\" : \"ios\"\n}\n\n/** `scheme://host[:port]` of an http(s) URL, lowercased as a page's `location.origin` reads it. */\nfunction serverOrigin(url: string): string | null {\n const match = /^(https?:\\/\\/[^/?#]+)/i.exec(url)\n return match ? match[1].toLowerCase() : null\n}\n\n/**\n * Why a launch failed, as the code Fleet takes (Phase 12): never the error's text. A bundle that cannot be found is\n * `bundle_missing`; a deployment, age or access block `policy_blocked`; a context that did not start `spawn_failed`.\n */\nexport function launchFailureReason(error: unknown): FleetLaunchFailureReason {\n if (error instanceof MiniappAgeRestrictedError || error instanceof MiniappAccessRevokedError) return \"policy_blocked\"\n const message = error instanceof Error ? error.message : \"\"\n if (message.includes(\"cannot resolve bundle\")) return \"bundle_missing\"\n if (message.includes(\"by deployment policy\")) return \"policy_blocked\"\n if (message.includes(\"spawn failed\")) return \"spawn_failed\"\n return \"unknown\"\n}\n\nclass MiniappLauncher {\n private deps: LauncherDeps | null = null\n\n /**\n * In-flight launches keyed by packageName. Both apps.ts start() and the\n * WebView mount call ensureRunning() before the first spawnAndRegister\n * resolves — without this, neither sees the package in registeredPackages()\n * yet and both spawn the same context (a clobber/double-spawn). Concurrent\n * callers share the same promise.\n */\n private readonly inFlight = new Map<string, Promise<LaunchResult>>()\n /** In-flight launches that keep their context out of the running tray (a hidden action wake). */\n private readonly hiddenInFlight = new Set<string>()\n\n /**\n * Installs hold new launches of their package until they commit or roll\n * back: explicit installs for the whole install, automatic updates only while\n * their files change.\n */\n private readonly installing = new Map<string, Promise<void>>()\n\n /**\n * Run an automatic update only while the miniapp is idle. `install` prepares\n * the release, then calls `beforeActivate` immediately before files change:\n * a launch during preparation wins and the update defers with\n * {@link MiniappRunningError}; a launch during activation waits for the\n * commit or rollback and then reads the resulting bundle.\n */\n async installWhenIdle<T>(packageName: string, install: (beforeActivate: () => void) => Promise<T>): Promise<T> {\n const assertIdle = () => {\n if (this.isRunning(packageName) || this.inFlight.has(packageName)) throw new MiniappRunningError(packageName)\n }\n assertIdle()\n let release: (() => void) | undefined\n try {\n return await install(() => {\n assertIdle()\n release = this.reserveInstall(packageName)\n })\n } finally {\n release?.()\n }\n }\n\n /** Stabilize existing launches and hold new ones until an explicit install finishes. */\n async pauseLaunches(packageName: string): Promise<() => void> {\n const release = this.reserveInstall(packageName)\n // Launches already resolving a bundle must settle before the installer\n // decides whether a context needs stopping/restarting. A failed launch is also settled.\n await this.inFlight.get(packageName)?.catch(() => {})\n return release\n }\n\n private reserveInstall(packageName: string): () => void {\n if (this.installing.has(packageName)) throw new Error(`An install is already activating ${packageName}`)\n let resume!: () => void\n const pending = new Promise<void>((resolve) => {\n resume = resolve\n })\n this.installing.set(packageName, pending)\n return () => {\n if (this.installing.get(packageName) !== pending) return\n this.installing.delete(packageName)\n resume()\n }\n }\n\n /**\n * The name and age rating of the installed bundle a launch of `packageName` runs (the version `getActiveVersion`\n * chooses, a development snapshot included), read without waiting. The apps store checks it before it opens the\n * screen or stops other miniapps, so a refusal changes nothing. A live development server's rating is read when\n * its context spawns, where every launch is checked again.\n */\n installedAgeRating(packageName: string): AgeRatedMiniapp {\n const version = appRegistry.peekActiveVersion(packageName)\n const manifest = version\n ? (appRegistry.getMiniappManifest(packageName, version) as {name?: unknown; ageRating?: unknown} | null)\n : null\n const name = typeof manifest?.name === \"string\" && manifest.name ? manifest.name : packageName\n return {packageName, name, ageRating: manifest?.ageRating ?? null}\n }\n\n /** Wire the router in. Called once from MiniappEngine construction. */\n configure(deps: LauncherDeps): void {\n this.deps = deps\n }\n\n private requireRouter(): MentraJSRouter {\n if (!this.deps) {\n throw new Error(\"MiniappLauncher not configured — ensureMiniappEngine() has not run\")\n }\n return this.deps.router\n }\n\n /** True iff the package's background JS context is currently spawned. */\n isRunning(packageName: string): boolean {\n return this.deps?.router.registeredPackages().includes(packageName) ?? false\n }\n\n /** True iff the live context is projected as normal user activity. */\n isProjectedRunning(packageName: string): boolean {\n return this.deps?.router.isProjectedRunning(packageName) ?? false\n }\n\n /**\n * Resolve the background JS source + UI entry + declared permissions for a\n * package. Handles both dev (HTTP off the running dev server) and released\n * (the installed snapshot on disk, whose screen loads from the package's own\n * origin). Reads disk/network/storage; does\n * NOT spawn. Returns null when the bundle can't be resolved (dev server\n * unreachable with no on-disk snapshot, missing entry, no installed version).\n */\n async resolveBundle(\n packageName: string,\n hints?: LaunchHints,\n runtimeOptions?: RuntimeLaunchOptions,\n ): Promise<ResolvedBundle | null> {\n if (!isMiniappAvailable(packageName, availabilityMode(runtimeOptions))) return null\n const devAllowed = isDevMiniappAllowed(packageName)\n // Live dev code is unsigned, so it never runs under a package installed with\n // a publisher signature; that package runs its installed bundle instead.\n const liveDevAllowed = devAllowed && !appRegistry.getPublisherKeyFingerprint(packageName)\n const devUrl = liveDevAllowed ? (hints?.devUrl ?? this.storedDevUrl(packageName)) : undefined\n\n const selectedSnapshot = devAllowed ? appRegistry.getSelectedDevSnapshot(packageName) : null\n if (selectedSnapshot) return this.resolveInstalledBundle(packageName, selectedSnapshot)\n\n // --- Dev: live HTTP, then the last on-disk snapshot if the laptop is gone. ---\n // A stored URL from a scan that this workspace will not run must fall\n // through to the released bundle, or the home tile disappears.\n if (devUrl && devAllowed) {\n const source = await resolveDevBundleSource(packageName, devUrl)\n if (source.kind === \"live\") {\n const live = await this.resolveLiveHttp(packageName, source.resolvedUrl, source.manifest, hints)\n if (live) return live\n // Live probe succeeded but the entry fetch failed — still try disk.\n }\n const snapshotVersion =\n source.kind === \"snapshot\" ? source.version : appRegistry.getLatestDevSnapshotVersion(packageName)\n if (snapshotVersion) {\n const snapshot = await this.resolveInstalledBundle(packageName, snapshotVersion)\n if (snapshot) return snapshot\n }\n return null\n }\n\n // --- Released: resolve from the installed snapshot on disk. ---\n // A view can retain its old version prop across an install or rollback.\n // Resolve the committed pointer here, including after an installation wait.\n const version = await appRegistry.getActiveVersion(packageName)\n if (!version) return null\n return this.resolveInstalledBundle(packageName, version)\n }\n\n private async resolveLiveHttp(\n packageName: string,\n resolvedUrl: string,\n manifest: {entry?: unknown; permissions?: unknown; [key: string]: unknown},\n hints?: LaunchHints,\n ): Promise<ResolvedBundle | null> {\n const entry = manifest.entry as {background?: string; ui?: string} | undefined\n if (!entry?.background) return null\n\n // Use the host that actually answered — may differ from the stored IP\n // after a laptop Wi-Fi change (mDNS / Metro failover inside resolveDevBundleSource).\n const base = resolvedUrl.replace(/\\/$/, \"\")\n // entry.* are bundle-root paths (dist/ stripped); the dev server serves\n // files relative to cwd, so prepend dist/.\n const bgUrl = `${base}/dist/${entry.background.replace(/^\\.?\\/+/, \"\")}`\n const uiUri = entry.ui ? `${base}/dist/${entry.ui.replace(/^\\.?\\/+/, \"\")}` : null\n\n const perms = manifest.permissions as Array<{type?: string} | string> | undefined\n const declaredPermissions = (perms ?? [])\n .map((p) => (typeof p === \"string\" ? p : p?.type))\n .filter((t): t is string => typeof t === \"string\")\n const installedManifest: InstalledMiniappManifest = {\n packageName: typeof manifest.packageName === \"string\" ? manifest.packageName : packageName,\n name: typeof manifest.name === \"string\" ? manifest.name : undefined,\n version: typeof manifest.version === \"string\" ? manifest.version : undefined,\n sdkVersion: typeof manifest.sdkVersion === \"string\" ? manifest.sdkVersion : undefined,\n minHostVersion: typeof manifest.minHostVersion === \"string\" ? manifest.minHostVersion : undefined,\n type: typeof manifest.type === \"string\" ? manifest.type : undefined,\n entry: manifest.entry as InstalledMiniappManifest[\"entry\"],\n permissions: manifest.permissions as InstalledMiniappManifest[\"permissions\"],\n hardwareRequirements: manifest.hardwareRequirements as InstalledMiniappManifest[\"hardwareRequirements\"],\n actions: manifest.actions as InstalledMiniappManifest[\"actions\"],\n }\n\n let bgSource: string\n try {\n const res = await fetch(bgUrl)\n if (!res.ok) return null\n bgSource = await res.text()\n } catch {\n return null\n }\n\n return {\n bgSource,\n uiUri,\n uiOrigin: uiUri ? serverOrigin(uiUri) : null,\n uiRootDir: null,\n uiOriginName: null,\n devBuild: true,\n declaredPermissions,\n installedManifest,\n ageRating: manifest.ageRating ?? null,\n // A dev server is never host-trusted, whatever package name it claims.\n hostTrustedSystem: false,\n devUrl: resolvedUrl,\n devPort: this.resolveDevPort(hints?.devPort, packageName),\n }\n }\n\n private async resolveInstalledBundle(packageName: string, version: string): Promise<ResolvedBundle | null> {\n const entryPaths = appRegistry.getMiniappEntryPaths(packageName, version)\n if (!entryPaths?.background) return null\n\n const manifest = appRegistry.getMiniappManifest(packageName, version) as {\n packageName?: string\n name?: string\n version?: string\n sdkVersion?: string\n minHostVersion?: string\n type?: string\n ageRating?: unknown\n entry?: {background?: string; ui?: string}\n permissions?: Array<{type: string; required?: boolean; description?: string}>\n hardwareRequirements?: Array<{type: string; level: string; description?: string}>\n actions?: Array<{\n id?: unknown\n description?: unknown\n parameters?: unknown\n outputSchema?: unknown\n lifecycle?: unknown\n audience?: unknown\n }>\n } | null\n const declaredPermissions = (manifest?.permissions ?? [])\n .map((p) => p.type)\n .filter((t): t is string => typeof t === \"string\")\n const installedManifest: InstalledMiniappManifest | undefined = manifest\n ? {\n packageName: manifest.packageName ?? packageName,\n name: manifest.name,\n version: manifest.version ?? version,\n sdkVersion: manifest.sdkVersion,\n minHostVersion: manifest.minHostVersion,\n type: manifest.type,\n entry: manifest.entry,\n permissions: manifest.permissions,\n hardwareRequirements: manifest.hardwareRequirements,\n actions: manifest.actions,\n }\n : undefined\n\n let bgSource: string\n try {\n bgSource = new File(entryPaths.background).textSync()\n } catch {\n return null\n }\n\n // The screen loads from the package's own origin, which serves only this\n // version's bundle and never grants file access. A development snapshot\n // claims its name without proof, so it loads from a development origin of\n // that name instead and never sees a release's web storage. A package name\n // that can't have an origin gets no screen rather than a file:// page.\n const devBuild = version.startsWith(\"dev-\")\n const originName = devBuild ? developmentOriginName(packageName) : packageName\n const platform = originPlatform()\n const uiUri = entryPaths.uiPath ? miniappOriginUrl(originName, entryPaths.uiPath, platform) : null\n if (entryPaths.uiPath && !uiUri) {\n console.warn(`MiniappLauncher: ${packageName} can't have an origin of its own, so its screen doesn't load`)\n }\n\n return {\n bgSource,\n uiUri,\n uiOrigin: uiUri ? miniappOrigin(originName, platform) : null,\n uiRootDir: uiUri ? entryPaths.rootDir : null,\n uiOriginName: uiUri ? originName : null,\n devBuild,\n declaredPermissions,\n installedManifest,\n ageRating: manifest?.ageRating ?? null,\n // A development build never runs with the build's privileges, whatever name it claims.\n hostTrustedSystem:\n !devBuild && isHostTrustedSystemMiniapp(packageName, appRegistry.getReleaseIdentity(packageName, version)),\n // A snapshot on disk — do not wire the sidecar; the laptop is gone.\n devUrl: null,\n devPort: null,\n }\n }\n\n /**\n * Ensure a miniapp's background JS context is spawned. Idempotent — if the\n * context is already alive this is a no-op spawn-wise (it still resolves the\n * UI entry so a re-mounting host gets its `<WebView>` source back).\n *\n * Resolves once the context is SPAWNED. It does NOT block on the context's\n * CONNECT handshake — the host's WebView mount shouldn't wait on background\n * JS. Callers that need the context to be connected and ready to receive\n * messages (the action broker) follow up with\n * {@link LocalMiniappRuntime.waitForConnect}.\n */\n async ensureRunning(\n packageName: string,\n hints?: LaunchHints,\n runtimeOptions?: RuntimeLaunchOptions,\n ): Promise<LaunchResult> {\n // An install holds launches until its files and metadata commit or roll\n // back; the launch then resolves whichever bundle is active.\n const installation = this.installing.get(packageName)\n if (installation) await installation\n if (\n !isMiniappAvailable(packageName, availabilityMode(runtimeOptions)) ||\n !appRegistry.isPackageAllowed(packageName)\n ) {\n throw new Error(`MiniappLauncher: ${packageName} is disabled by deployment policy`)\n }\n if (appRegistry.isMiniappAccessRevoked(packageName)) throw new MiniappAccessRevokedError(packageName)\n const router = this.requireRouter()\n const projectRunning = runtimeOptions?.projectRunning ?? true\n\n // Already spawned: best-effort resolve for the UI entry, never throw —\n // a headless caller that doesn't need UI gets {null, null} fast even if\n // the dev server has since dropped. A user launch promotes a context a\n // transient action woke.\n if (router.registeredPackages().includes(packageName)) {\n const promoted = projectRunning && this.promote(packageName)\n const existing = await this.resolveBundle(packageName, hints, runtimeOptions).catch(() => null)\n if (promoted) this.reportLaunch(packageName, \"started\", runtimeOptions, existing?.installedManifest)\n return launchResultOf(existing)\n }\n\n // Coalesce concurrent launches of the same package onto one promise.\n const pending = this.inFlight.get(packageName)\n if (pending) {\n const joinedHidden = this.hiddenInFlight.has(packageName)\n let result: LaunchResult\n try {\n result = await pending\n } catch (error) {\n // A visible launch reports its own failure; one joined to a hidden wake fails with it.\n if (projectRunning && joinedHidden) this.reportLaunch(packageName, \"failed\", runtimeOptions, undefined, launchFailureReason(error))\n throw error\n }\n if (projectRunning && this.promote(packageName)) this.reportLaunch(packageName, \"started\", runtimeOptions)\n return result\n }\n\n const launch = this.spawn(packageName, hints, {...runtimeOptions, projectRunning})\n this.inFlight.set(packageName, launch)\n if (!projectRunning) this.hiddenInFlight.add(packageName)\n try {\n return await launch\n } catch (error) {\n if (projectRunning) this.reportLaunch(packageName, \"failed\", runtimeOptions, undefined, launchFailureReason(error))\n throw error\n } finally {\n this.inFlight.delete(packageName)\n this.hiddenInFlight.delete(packageName)\n }\n }\n\n /** Projects a context already spawned as user activity: true if this made it so. */\n private promote(packageName: string): boolean {\n const router = this.requireRouter()\n if (router.isProjectedRunning(packageName)) return false\n router.projectRunning(packageName)\n return router.isProjectedRunning(packageName)\n }\n\n /** Reports a launch this call made to Fleet, unless the apps store's `start` is making it and reports it itself. */\n private reportLaunch(\n packageName: string,\n outcome: \"started\" | \"failed\",\n runtimeOptions?: RuntimeLaunchOptions,\n manifest?: InstalledMiniappManifest,\n failureReason?: FleetLaunchFailureReason,\n ): void {\n fleetEvents.launchUnlessClaimed({\n packageName,\n ...(manifest?.name ? {miniappName: manifest.name} : {}),\n ...(manifest?.version ? {versionName: manifest.version} : {}),\n start: runtimeOptions?.trigger ?? \"automatic\",\n outcome,\n ...(outcome === \"failed\" ? {failureReason: failureReason ?? \"unknown\"} : {}),\n })\n }\n\n /** Resolve the bundle and spawn the context. Serialized via {@link inFlight}. */\n private async spawn(\n packageName: string,\n hints?: LaunchHints,\n runtimeOptions?: RuntimeLaunchOptions,\n ): Promise<LaunchResult> {\n const router = this.requireRouter()\n const resolved = await this.resolveBundle(packageName, hints, runtimeOptions)\n if (!resolved) {\n throw new Error(`MiniappLauncher: cannot resolve bundle for ${packageName}`)\n }\n const version = resolved.installedManifest?.version\n const projectRunning = runtimeOptions?.projectRunning ?? true\n // The age gate, before the deployment checks below, which then see the state after any question the person\n // answered. Only a launch the person makes in front of the app may ask.\n const verdict = await checkMiniappAge(\n {packageName, name: resolved.installedManifest?.name || packageName, ageRating: resolved.ageRating},\n {interactive: projectRunning && AppState.currentState === \"active\"},\n )\n if (verdict !== \"allowed\") throw new MiniappAgeRestrictedError(packageName, verdict)\n // A live dev server sets devUrl. A snapshot keeps the directory name `dev-*`\n // as the active version. Either one is the scanned build; a release fallback is neither.\n const activeVersion = await appRegistry.getActiveVersion(packageName)\n const devBuild = Boolean(resolved.devUrl) || Boolean(activeVersion?.startsWith(\"dev-\"))\n if (\n // Availability may change while the bundle resolves.\n !isMiniappAvailable(packageName, availabilityMode(runtimeOptions)) ||\n (devBuild ? !isDevMiniappAllowed(packageName) : !appRegistry.isReleaseAllowed(packageName, version))\n ) {\n throw new Error(`MiniappLauncher: ${packageName} bundle is not authorized by deployment policy`)\n }\n\n // Re-check after the async resolve: a different path may have spawned it\n // while we were fetching/reading the bundle.\n if (router.registeredPackages().includes(packageName)) {\n if (projectRunning && this.promote(packageName)) {\n this.reportLaunch(packageName, \"started\", runtimeOptions, resolved.installedManifest)\n }\n } else {\n const ok = await router.spawnAndRegister(packageName, resolved.bgSource, {\n permissions: resolved.declaredPermissions,\n installedManifest: resolved.installedManifest,\n hostTrustedSystem: resolved.hostTrustedSystem,\n projectRunning,\n developmentBuild: resolved.devBuild,\n })\n if (!ok) {\n throw new Error(`MiniappLauncher: spawn failed for ${packageName}`)\n }\n\n // Dev: wire the hot-reload + log-forwarding sidecar. No-op in prod\n // (devServerBridge silently drops when no sidecar is up).\n if (resolved.devUrl && resolved.devPort != null) {\n devServerBridge.connect(packageName, resolved.devUrl, resolved.devPort)\n } else if (resolved.devUrl) {\n // Dev miniapp with no resolvable port — it still launches, but hot\n // reload + log forwarding won't connect. Surface it instead of failing\n // silently (the old inline path hard-failed with \"no dev port\").\n console.warn(`MiniappLauncher: ${packageName} has no dev port — hot reload + log forwarding disabled`)\n }\n if (projectRunning) this.reportLaunch(packageName, \"started\", runtimeOptions, resolved.installedManifest)\n }\n\n return launchResultOf(resolved)\n }\n\n /**\n * Re-spawn local miniapps that were running when the app was last killed.\n *\n * Every miniapp runs as local JavaScript in the Mentra App on the phone.\n * A host-process kill tears down its JSContext, so without this restoration\n * it stays stopped on the next launch even though the user left it running.\n *\n * Each local miniapp persists its running flag to disk on start/stop\n * (`saveLocalAppRunningState`, via the applet's `onStart`/`onStop`). We read\n * those flags back and re-spawn the background context headlessly (no\n * foreground, no navigation) — the spawn registers the package so the home\n * tray/switcher project it as running, exactly as a normal start would. The\n * WebView, if any, re-attaches lazily when the user opens the app.\n *\n * Idempotent and best-effort: skips already-spawned contexts and clears the\n * persisted running flag if ensureRunning fails (for example, bundle resolution,\n * deployment policy, or context spawning) so it does not retry every boot.\n * Each restore is an automatic launch to Fleet, reported by ensureRunning.\n * Not compatibility-gated: a previously-running background app shouldn't be\n * dropped just because glasses are momentarily disconnected at boot — it\n * resumes when they reconnect, same as mid-session.\n */\n async autostartLocalMiniapps(): Promise<void> {\n const apps = await appRegistry.getInstalledMiniapps()\n for (const app of apps) {\n // Only phone-hosted miniapps have a JSContext to re-spawn. Offline\n // built-ins (`local:false, offline:true`) restore their native running\n // state elsewhere and have no bundle to launch.\n if (!app.local) continue\n if (!getLocalAppRunningState(app.packageName)) continue\n if (this.isRunning(app.packageName)) continue\n try {\n // Restoring what was running is the platform's doing, never the user's.\n await this.ensureRunning(app.packageName, undefined, {trigger: \"automatic\"})\n } catch (e) {\n console.warn(`MiniappLauncher: autostart failed for ${app.packageName} — clearing stale running flag`, e)\n saveLocalAppRunningState(app.packageName, false)\n }\n }\n }\n\n /**\n * Ensure the context is running AND has completed its CONNECT handshake.\n * Used by the action broker, which must not deliver to a context that hasn't\n * come up yet. Throws on spawn failure or connect timeout.\n */\n async ensureConnected(\n packageName: string,\n connectTimeoutMs = 10_000,\n hints?: LaunchHints,\n runtimeOptions?: RuntimeLaunchOptions,\n ): Promise<void> {\n await this.ensureRunning(packageName, hints, runtimeOptions)\n await localMiniappRuntime.waitForConnect(packageName, connectTimeoutMs)\n }\n\n /** Tear down a miniapp's background JS context. */\n async stop(packageName: string): Promise<void> {\n // If a launch is in flight, let it finish first. Otherwise unregister()\n // no-ops on the not-yet-registered package and the pending spawn then\n // re-registers the context — leaving it running despite the stop.\n const pending = this.inFlight.get(packageName)\n if (pending) {\n try {\n await pending\n } catch {\n // Launch failed — nothing got registered, so nothing to tear down.\n }\n }\n await this.deps?.router.unregister(packageName)\n }\n\n // --- internals -----------------------------------------------------------\n\n private storedDevUrl(packageName: string): string | undefined {\n const r = storage.load<string>(`${packageName}_dev_url`)\n return r.is_ok() ? r.value : undefined\n }\n\n /** resolveDevPort — moved verbatim from the old LocalMiniappView helper. */\n private resolveDevPort(searchParam: string | undefined, packageName: string): number | null {\n if (searchParam) {\n const n = parseInt(searchParam, 10)\n if (Number.isFinite(n)) return n\n }\n const stored = storage.load<number>(`${packageName}_dev_port`)\n if (stored.is_ok()) return stored.value\n return null\n }\n}\n\nexport const miniappLauncher = new MiniappLauncher()\n"]}
1
+ {"version":3,"file":"MiniappLauncher.js","sourceRoot":"","sources":["../../src/services/MiniappLauncher.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MiniappLauncher — headless launch/teardown of a local miniapp's background\n * JS context.\n *\n * This is the one piece of \"run a miniapp\" that used to live inside the\n * `LocalMiniappView` React component (resolve the bundle → read the manifest →\n * `spawnAndRegister`), triggered only when the Compositor mounted that\n * component on foreground. Pulling it into the runtime means:\n *\n * - `start()` can actually spawn an app even when nothing foregrounds it\n * (e.g. a system app launching another miniapp via `session.miniapps`).\n * - the action broker can wake a stopped target headlessly — no UI at all.\n * - the dev hot-reload respawn path and the WebView mount path share one\n * resolve+spawn recipe instead of three copies.\n *\n * The launcher is **headless**: no React, no RN components. The engine-owned\n * MiniappEngine hands it the {@link MentraJSRouter} instance once during\n * engine construction. OEMs embedding the runtime drive miniapp lifecycle\n * through the engine facade and stores, not a separate launcher configure API.\n *\n * Boundary: the launcher owns the **background** context. WebView *rendering*\n * (the UI layer) stays with the host — the launcher just hands back the\n * resolved UI entry so the host can mount its `<WebView>`.\n *\n * Fleet: every path that makes a local miniapp execute as user activity comes\n * through here, so the launcher reports each such launch (and each that fails\n * once past the availability gate) — a restore, a miniapp started by another\n * miniapp or an action, a screen opened directly, a restart after a reinstall.\n * A launch the apps store's `start` makes is reported by `start` itself\n * (`fleetEvents.claimLaunch`); a context already executing, or one a hidden\n * action wake keeps out of the running tray, is no launch.\n *\n * Age ratings: every launch path comes through here, so this is where a miniapp rated above the host app's own\n * rating meets the age gate as it opens, whether it was installed with the sheet or headlessly (a Fleet assignment,\n * a system miniapp). A launch the person makes in front of the app may ask their age once; any other launch needs it\n * declared already.\n */\n\nimport type {FleetLaunchFailureReason} from \"@mentra/cloud-protocol\"\nimport {File} from \"expo-file-system\"\nimport {AppState, Platform} from \"react-native\"\n\nimport {\n getConfigValues,\n isDevMiniappAllowed,\n isMiniappAvailable,\n type MiniappAvailabilityMode,\n} from \"../runtime/bootstrap\"\nimport {resolveDevBundleSource} from \"../utils/devMiniappSnapshot\"\nimport {storage} from \"../utils/storage/storage\"\nimport {MiniappRunningError} from \"../utils/storeInstallRuntime\"\nimport appRegistry, {getLocalAppRunningState, saveLocalAppRunningState} from \"./AppRegistry\"\nimport devServerBridge from \"./DevServerBridge\"\nimport {fleetEvents} from \"./fleet/fleetEvents\"\nimport localMiniappRuntime, {type InstalledMiniappManifest} from \"./LocalMiniappRuntime\"\nimport {checkManifestVersions} from \"./manifestVersionGate\"\nimport type {MentraJSRouter} from \"./MentraJSRouter\"\nimport {checkMiniappAge, type AgeGateVerdict, type AgeRatedMiniapp} from \"./miniappAgeGate\"\nimport {developmentOriginName, miniappOrigin, miniappOriginUrl, type MiniappOriginPlatform} from \"./miniappOrigin\"\nimport {isHostTrustedSystemMiniapp} from \"./SystemMiniappPolicy\"\n\n/** The miniapp's server no longer admits this account (Fleet revocation): it cannot launch until access returns. */\nexport class MiniappAccessRevokedError extends Error {\n constructor(readonly packageName: string) {\n super(`MiniappLauncher: access to ${packageName} was revoked`)\n this.name = \"MiniappAccessRevokedError\"\n }\n}\n\n/**\n * The miniapp is rated above the host app's own rating and the person's declared age doesn't let it open (see\n * {@link AgeGateVerdict}). A person opening it in front of the app was asked their age, or told why.\n */\nexport class MiniappAgeRestrictedError extends Error {\n constructor(\n readonly packageName: string,\n readonly verdict: Exclude<AgeGateVerdict, \"allowed\">,\n ) {\n super(`MiniappLauncher: ${packageName} is age-rated and did not open (${verdict})`)\n this.name = \"MiniappAgeRestrictedError\"\n }\n}\n\n/**\n * The bundle needs a newer Mentra App, or a Mentra Miniapp SDK this one doesn't run: the rule installs refuse by,\n * checked again as it launches. A release a newer Mentra App installed stays on disk when an older one replaces it (a\n * development build replaced from TestFlight), and would otherwise start against features this one lacks. The message\n * is for logs; a screen explains it in the person's language, as an install refused by the same rule does.\n */\nexport class MiniappIncompatibleHostError extends Error {\n constructor(\n readonly packageName: string,\n readonly miniappName: string,\n readonly reason: string,\n ) {\n super(`${miniappName} can't run on this phone: ${reason}`)\n this.name = \"MiniappIncompatibleHostError\"\n }\n}\n\ninterface LauncherDeps {\n /** The host-constructed router (needs the native Crust binding). */\n router: MentraJSRouter\n}\n\n/** Hints the host may pass from a view's props to avoid re-deriving them. */\nexport interface LaunchHints {\n devUrl?: string\n /** Retained for caller compatibility; released launches use the committed active version. */\n version?: string\n devPort?: string\n}\n\nexport interface RuntimeLaunchOptions {\n /** False for an invocation-scoped wake that must not enter the running tray. */\n projectRunning?: boolean\n /**\n * Who started the miniapp, for the Fleet launch this call makes, if it makes one: the person (a screen they opened)\n * or the platform (a restore, another miniapp, a restart). Defaults to automatic.\n */\n trigger?: \"user\" | \"automatic\"\n}\n\nfunction availabilityMode(options?: RuntimeLaunchOptions): MiniappAvailabilityMode {\n return options?.projectRunning === false ? \"background\" : \"interactive\"\n}\n\n/** Everything the launcher resolved for a package — bg source + UI entry. */\nexport interface ResolvedBundle {\n bgSource: string\n /** Where the screen loads from. See {@link LaunchResult.uiUri}. */\n uiUri: string | null\n /** The origin `uiUri` belongs to. See {@link LaunchResult.uiOrigin}. */\n uiOrigin: string | null\n /** The bundle root the package's origin serves. See {@link LaunchResult.uiRootDir}. */\n uiRootDir: string | null\n /** The name the origin is for. See {@link LaunchResult.uiOriginName}. */\n uiOriginName: string | null\n /** A development build: a live development server or a development snapshot. */\n devBuild: boolean\n declaredPermissions: string[]\n installedManifest?: InstalledMiniappManifest\n /** What the bundle's `miniapp.json` declares as `ageRating`; null when it declares none. */\n ageRating: unknown\n /** Build-owned SYSTEM trust derived from install provenance, never the manifest. */\n hostTrustedSystem: boolean\n /** Set for dev miniapps (HTTP off the dev server); null for released. */\n devUrl: string | null\n devPort: number | null\n}\n\n/** What the host needs back to render the UI layer. */\nexport interface LaunchResult {\n /**\n * Where the screen loads from. An installed release loads from its\n * package's own origin: `miniapp://<packageName>/<ui entry>` on iOS and\n * `https://<host>.miniapp.invalid/<ui entry>` on Android (see\n * {@link miniappOriginUrl}), a development snapshot from the origin of its\n * {@link uiOriginName}. A live development build loads from its development\n * server. Null when there is no screen to load.\n */\n uiUri: string | null\n /** The origin `uiUri` belongs to, for the WebView's `originWhitelist`. */\n uiOrigin: string | null\n /**\n * The root of the active version's bundle, which the package's origin\n * serves (the WebView's `miniappOrigin.rootDir`). Null when the screen loads\n * from a development server.\n */\n uiRootDir: string | null\n /**\n * The name the origin serves `uiRootDir` under (the WebView's\n * `miniappOrigin.packageName`): the package name for a release, and\n * {@link developmentOriginName} for a development snapshot, so a development\n * build never shares web storage with a release of its name. Null with\n * `uiRootDir`.\n */\n uiOriginName: string | null\n /** A development build: a live development server or a development snapshot. */\n devBuild: boolean\n}\n\nconst NO_UI: LaunchResult = {uiUri: null, uiOrigin: null, uiRootDir: null, uiOriginName: null, devBuild: false}\n\nfunction launchResultOf(resolved: ResolvedBundle | null | undefined): LaunchResult {\n if (!resolved) return NO_UI\n return {\n uiUri: resolved.uiUri,\n uiOrigin: resolved.uiOrigin,\n uiRootDir: resolved.uiRootDir,\n uiOriginName: resolved.uiOriginName,\n devBuild: resolved.devBuild,\n }\n}\n\n/** The platform whose origin shape the Mentra App's WebView serves. */\nfunction originPlatform(): MiniappOriginPlatform {\n return Platform.OS === \"android\" ? \"android\" : \"ios\"\n}\n\n/** `scheme://host[:port]` of an http(s) URL, lowercased as a page's `location.origin` reads it. */\nfunction serverOrigin(url: string): string | null {\n const match = /^(https?:\\/\\/[^/?#]+)/i.exec(url)\n return match ? match[1].toLowerCase() : null\n}\n\n/**\n * Why a launch failed, as the code Fleet takes (Phase 12): never the error's text. A bundle that cannot be found is\n * `bundle_missing`; one that needs a newer Mentra App `incompatible_host`; a deployment, age or access block\n * `policy_blocked`; a context that did not start `spawn_failed`.\n */\nexport function launchFailureReason(error: unknown): FleetLaunchFailureReason {\n if (error instanceof MiniappIncompatibleHostError) return \"incompatible_host\"\n if (error instanceof MiniappAgeRestrictedError || error instanceof MiniappAccessRevokedError) return \"policy_blocked\"\n const message = error instanceof Error ? error.message : \"\"\n if (message.includes(\"cannot resolve bundle\")) return \"bundle_missing\"\n if (message.includes(\"by deployment policy\")) return \"policy_blocked\"\n if (message.includes(\"spawn failed\")) return \"spawn_failed\"\n return \"unknown\"\n}\n\nclass MiniappLauncher {\n private deps: LauncherDeps | null = null\n\n /**\n * In-flight launches keyed by packageName. Both apps.ts start() and the\n * WebView mount call ensureRunning() before the first spawnAndRegister\n * resolves — without this, neither sees the package in registeredPackages()\n * yet and both spawn the same context (a clobber/double-spawn). Concurrent\n * callers share the same promise.\n */\n private readonly inFlight = new Map<string, Promise<LaunchResult>>()\n /** In-flight launches that keep their context out of the running tray (a hidden action wake). */\n private readonly hiddenInFlight = new Set<string>()\n\n /**\n * Installs hold new launches of their package until they commit or roll\n * back: explicit installs for the whole install, automatic updates only while\n * their files change.\n */\n private readonly installing = new Map<string, Promise<void>>()\n\n /**\n * Run an automatic update only while the miniapp is idle. `install` prepares\n * the release, then calls `beforeActivate` immediately before files change:\n * a launch during preparation wins and the update defers with\n * {@link MiniappRunningError}; a launch during activation waits for the\n * commit or rollback and then reads the resulting bundle.\n */\n async installWhenIdle<T>(packageName: string, install: (beforeActivate: () => void) => Promise<T>): Promise<T> {\n const assertIdle = () => {\n if (this.isRunning(packageName) || this.inFlight.has(packageName)) throw new MiniappRunningError(packageName)\n }\n assertIdle()\n let release: (() => void) | undefined\n try {\n return await install(() => {\n assertIdle()\n release = this.reserveInstall(packageName)\n })\n } finally {\n release?.()\n }\n }\n\n /** Stabilize existing launches and hold new ones until an explicit install finishes. */\n async pauseLaunches(packageName: string): Promise<() => void> {\n const release = this.reserveInstall(packageName)\n // Launches already resolving a bundle must settle before the installer\n // decides whether a context needs stopping/restarting. A failed launch is also settled.\n await this.inFlight.get(packageName)?.catch(() => {})\n return release\n }\n\n private reserveInstall(packageName: string): () => void {\n if (this.installing.has(packageName)) throw new Error(`An install is already activating ${packageName}`)\n let resume!: () => void\n const pending = new Promise<void>((resolve) => {\n resume = resolve\n })\n this.installing.set(packageName, pending)\n return () => {\n if (this.installing.get(packageName) !== pending) return\n this.installing.delete(packageName)\n resume()\n }\n }\n\n /**\n * The name and age rating of the installed bundle a launch of `packageName` runs (the version `getActiveVersion`\n * chooses, a development snapshot included), read without waiting. The apps store checks it before it opens the\n * screen or stops other miniapps, so a refusal changes nothing. A live development server's rating is read when\n * its context spawns, where every launch is checked again.\n */\n installedAgeRating(packageName: string): AgeRatedMiniapp {\n const version = appRegistry.peekActiveVersion(packageName)\n const manifest = version\n ? (appRegistry.getMiniappManifest(packageName, version) as {name?: unknown; ageRating?: unknown} | null)\n : null\n const name = typeof manifest?.name === \"string\" && manifest.name ? manifest.name : packageName\n return {packageName, name, ageRating: manifest?.ageRating ?? null}\n }\n\n /** Wire the router in. Called once from MiniappEngine construction. */\n configure(deps: LauncherDeps): void {\n this.deps = deps\n }\n\n private requireRouter(): MentraJSRouter {\n if (!this.deps) {\n throw new Error(\"MiniappLauncher not configured — ensureMiniappEngine() has not run\")\n }\n return this.deps.router\n }\n\n /** True iff the package's background JS context is currently spawned. */\n isRunning(packageName: string): boolean {\n return this.deps?.router.registeredPackages().includes(packageName) ?? false\n }\n\n /** True iff the live context is projected as normal user activity. */\n isProjectedRunning(packageName: string): boolean {\n return this.deps?.router.isProjectedRunning(packageName) ?? false\n }\n\n /**\n * Resolve the background JS source + UI entry + declared permissions for a\n * package. Handles both dev (HTTP off the running dev server) and released\n * (the installed snapshot on disk, whose screen loads from the package's own\n * origin). Reads disk/network/storage; does\n * NOT spawn. Returns null when the bundle can't be resolved (dev server\n * unreachable with no on-disk snapshot, missing entry, no installed version).\n */\n async resolveBundle(\n packageName: string,\n hints?: LaunchHints,\n runtimeOptions?: RuntimeLaunchOptions,\n ): Promise<ResolvedBundle | null> {\n if (!isMiniappAvailable(packageName, availabilityMode(runtimeOptions))) return null\n const devAllowed = isDevMiniappAllowed(packageName)\n // Live dev code is unsigned, so it never runs under a package installed with\n // a publisher signature; that package runs its installed bundle instead.\n const liveDevAllowed = devAllowed && !appRegistry.getPublisherKeyFingerprint(packageName)\n const devUrl = liveDevAllowed ? (hints?.devUrl ?? this.storedDevUrl(packageName)) : undefined\n\n const selectedSnapshot = devAllowed ? appRegistry.getSelectedDevSnapshot(packageName) : null\n if (selectedSnapshot) return this.resolveInstalledBundle(packageName, selectedSnapshot)\n\n // --- Dev: live HTTP, then the last on-disk snapshot if the laptop is gone. ---\n // A stored URL from a scan that this workspace will not run must fall\n // through to the released bundle, or the home tile disappears.\n if (devUrl && devAllowed) {\n const source = await resolveDevBundleSource(packageName, devUrl)\n if (source.kind === \"live\") {\n const live = await this.resolveLiveHttp(packageName, source.resolvedUrl, source.manifest, hints)\n if (live) return live\n // Live probe succeeded but the entry fetch failed — still try disk.\n }\n const snapshotVersion =\n source.kind === \"snapshot\" ? source.version : appRegistry.getLatestDevSnapshotVersion(packageName)\n if (snapshotVersion) {\n const snapshot = await this.resolveInstalledBundle(packageName, snapshotVersion)\n if (snapshot) return snapshot\n }\n return null\n }\n\n // --- Released: resolve from the installed snapshot on disk. ---\n // A view can retain its old version prop across an install or rollback.\n // Resolve the committed pointer here, including after an installation wait.\n const version = await appRegistry.getActiveVersion(packageName)\n if (!version) return null\n return this.resolveInstalledBundle(packageName, version)\n }\n\n private async resolveLiveHttp(\n packageName: string,\n resolvedUrl: string,\n manifest: {entry?: unknown; permissions?: unknown; [key: string]: unknown},\n hints?: LaunchHints,\n ): Promise<ResolvedBundle | null> {\n const entry = manifest.entry as {background?: string; ui?: string} | undefined\n if (!entry?.background) return null\n\n // Use the host that actually answered — may differ from the stored IP\n // after a laptop Wi-Fi change (mDNS / Metro failover inside resolveDevBundleSource).\n const base = resolvedUrl.replace(/\\/$/, \"\")\n // entry.* are bundle-root paths (dist/ stripped); the dev server serves\n // files relative to cwd, so prepend dist/.\n const bgUrl = `${base}/dist/${entry.background.replace(/^\\.?\\/+/, \"\")}`\n const uiUri = entry.ui ? `${base}/dist/${entry.ui.replace(/^\\.?\\/+/, \"\")}` : null\n\n const perms = manifest.permissions as Array<{type?: string} | string> | undefined\n const declaredPermissions = (perms ?? [])\n .map((p) => (typeof p === \"string\" ? p : p?.type))\n .filter((t): t is string => typeof t === \"string\")\n const installedManifest: InstalledMiniappManifest = {\n packageName: typeof manifest.packageName === \"string\" ? manifest.packageName : packageName,\n name: typeof manifest.name === \"string\" ? manifest.name : undefined,\n version: typeof manifest.version === \"string\" ? manifest.version : undefined,\n sdkVersion: typeof manifest.sdkVersion === \"string\" ? manifest.sdkVersion : undefined,\n minHostVersion: typeof manifest.minHostVersion === \"string\" ? manifest.minHostVersion : undefined,\n type: typeof manifest.type === \"string\" ? manifest.type : undefined,\n entry: manifest.entry as InstalledMiniappManifest[\"entry\"],\n permissions: manifest.permissions as InstalledMiniappManifest[\"permissions\"],\n hardwareRequirements: manifest.hardwareRequirements as InstalledMiniappManifest[\"hardwareRequirements\"],\n actions: manifest.actions as InstalledMiniappManifest[\"actions\"],\n }\n\n let bgSource: string\n try {\n const res = await fetch(bgUrl)\n if (!res.ok) return null\n bgSource = await res.text()\n } catch {\n return null\n }\n\n return {\n bgSource,\n uiUri,\n uiOrigin: uiUri ? serverOrigin(uiUri) : null,\n uiRootDir: null,\n uiOriginName: null,\n devBuild: true,\n declaredPermissions,\n installedManifest,\n ageRating: manifest.ageRating ?? null,\n // A dev server is never host-trusted, whatever package name it claims.\n hostTrustedSystem: false,\n devUrl: resolvedUrl,\n devPort: this.resolveDevPort(hints?.devPort, packageName),\n }\n }\n\n private async resolveInstalledBundle(packageName: string, version: string): Promise<ResolvedBundle | null> {\n const entryPaths = appRegistry.getMiniappEntryPaths(packageName, version)\n if (!entryPaths?.background) return null\n\n const manifest = appRegistry.getMiniappManifest(packageName, version) as {\n packageName?: string\n name?: string\n version?: string\n sdkVersion?: string\n minHostVersion?: string\n type?: string\n ageRating?: unknown\n entry?: {background?: string; ui?: string}\n permissions?: Array<{type: string; required?: boolean; description?: string}>\n hardwareRequirements?: Array<{type: string; level: string; description?: string}>\n actions?: Array<{\n id?: unknown\n description?: unknown\n parameters?: unknown\n outputSchema?: unknown\n lifecycle?: unknown\n audience?: unknown\n }>\n } | null\n const declaredPermissions = (manifest?.permissions ?? [])\n .map((p) => p.type)\n .filter((t): t is string => typeof t === \"string\")\n const installedManifest: InstalledMiniappManifest | undefined = manifest\n ? {\n packageName: manifest.packageName ?? packageName,\n name: manifest.name,\n version: manifest.version ?? version,\n sdkVersion: manifest.sdkVersion,\n minHostVersion: manifest.minHostVersion,\n type: manifest.type,\n entry: manifest.entry,\n permissions: manifest.permissions,\n hardwareRequirements: manifest.hardwareRequirements,\n actions: manifest.actions,\n }\n : undefined\n\n let bgSource: string\n try {\n bgSource = new File(entryPaths.background).textSync()\n } catch {\n return null\n }\n\n // The screen loads from the package's own origin, which serves only this\n // version's bundle and never grants file access. A development snapshot\n // claims its name without proof, so it loads from a development origin of\n // that name instead and never sees a release's web storage. A package name\n // that can't have an origin gets no screen rather than a file:// page.\n const devBuild = version.startsWith(\"dev-\")\n const originName = devBuild ? developmentOriginName(packageName) : packageName\n const platform = originPlatform()\n const uiUri = entryPaths.uiPath ? miniappOriginUrl(originName, entryPaths.uiPath, platform) : null\n if (entryPaths.uiPath && !uiUri) {\n console.warn(`MiniappLauncher: ${packageName} can't have an origin of its own, so its screen doesn't load`)\n }\n\n return {\n bgSource,\n uiUri,\n uiOrigin: uiUri ? miniappOrigin(originName, platform) : null,\n uiRootDir: uiUri ? entryPaths.rootDir : null,\n uiOriginName: uiUri ? originName : null,\n devBuild,\n declaredPermissions,\n installedManifest,\n ageRating: manifest?.ageRating ?? null,\n // A development build never runs with the build's privileges, whatever name it claims.\n hostTrustedSystem:\n !devBuild && isHostTrustedSystemMiniapp(packageName, appRegistry.getReleaseIdentity(packageName, version)),\n // A snapshot on disk — do not wire the sidecar; the laptop is gone.\n devUrl: null,\n devPort: null,\n }\n }\n\n /** Throws {@link MiniappIncompatibleHostError} when this Mentra App can't run the resolved bundle. */\n assertRunsOnPhone(packageName: string, resolved: ResolvedBundle): void {\n const manifest = resolved.installedManifest\n const {hostVersion, supportedMiniappSdkRange} = getConfigValues()\n if (!manifest || !hostVersion || !supportedMiniappSdkRange) return\n const versions = checkManifestVersions(manifest, {hostVersion, supportedSdkRange: supportedMiniappSdkRange})\n if (!versions.ok) throw new MiniappIncompatibleHostError(packageName, manifest.name || packageName, versions.reason)\n }\n\n /**\n * Ensure a miniapp's background JS context is spawned. Idempotent — if the\n * context is already alive this is a no-op spawn-wise (it still resolves the\n * UI entry so a re-mounting host gets its `<WebView>` source back).\n *\n * Resolves once the context is SPAWNED. It does NOT block on the context's\n * CONNECT handshake — the host's WebView mount shouldn't wait on background\n * JS. Callers that need the context to be connected and ready to receive\n * messages (the action broker) follow up with\n * {@link LocalMiniappRuntime.waitForConnect}.\n */\n async ensureRunning(\n packageName: string,\n hints?: LaunchHints,\n runtimeOptions?: RuntimeLaunchOptions,\n ): Promise<LaunchResult> {\n // An install holds launches until its files and metadata commit or roll\n // back; the launch then resolves whichever bundle is active.\n const installation = this.installing.get(packageName)\n if (installation) await installation\n if (\n !isMiniappAvailable(packageName, availabilityMode(runtimeOptions)) ||\n !appRegistry.isPackageAllowed(packageName)\n ) {\n throw new Error(`MiniappLauncher: ${packageName} is disabled by deployment policy`)\n }\n if (appRegistry.isMiniappAccessRevoked(packageName)) throw new MiniappAccessRevokedError(packageName)\n const router = this.requireRouter()\n const projectRunning = runtimeOptions?.projectRunning ?? true\n\n // Already spawned: best-effort resolve for the UI entry, never throw —\n // a headless caller that doesn't need UI gets {null, null} fast even if\n // the dev server has since dropped. A user launch promotes a context a\n // transient action woke.\n if (router.registeredPackages().includes(packageName)) {\n const promoted = projectRunning && this.promote(packageName)\n const existing = await this.resolveBundle(packageName, hints, runtimeOptions).catch(() => null)\n if (promoted) this.reportLaunch(packageName, \"started\", runtimeOptions, existing?.installedManifest)\n return launchResultOf(existing)\n }\n\n // Coalesce concurrent launches of the same package onto one promise.\n const pending = this.inFlight.get(packageName)\n if (pending) {\n const joinedHidden = this.hiddenInFlight.has(packageName)\n let result: LaunchResult\n try {\n result = await pending\n } catch (error) {\n // A visible launch reports its own failure; one joined to a hidden wake fails with it.\n if (projectRunning && joinedHidden) this.reportLaunch(packageName, \"failed\", runtimeOptions, undefined, launchFailureReason(error))\n throw error\n }\n if (projectRunning && this.promote(packageName)) this.reportLaunch(packageName, \"started\", runtimeOptions)\n return result\n }\n\n const launch = this.spawn(packageName, hints, {...runtimeOptions, projectRunning})\n this.inFlight.set(packageName, launch)\n if (!projectRunning) this.hiddenInFlight.add(packageName)\n try {\n return await launch\n } catch (error) {\n if (projectRunning) this.reportLaunch(packageName, \"failed\", runtimeOptions, undefined, launchFailureReason(error))\n throw error\n } finally {\n this.inFlight.delete(packageName)\n this.hiddenInFlight.delete(packageName)\n }\n }\n\n /** Projects a context already spawned as user activity: true if this made it so. */\n private promote(packageName: string): boolean {\n const router = this.requireRouter()\n if (router.isProjectedRunning(packageName)) return false\n router.projectRunning(packageName)\n return router.isProjectedRunning(packageName)\n }\n\n /** Reports a launch this call made to Fleet, unless the apps store's `start` is making it and reports it itself. */\n private reportLaunch(\n packageName: string,\n outcome: \"started\" | \"failed\",\n runtimeOptions?: RuntimeLaunchOptions,\n manifest?: InstalledMiniappManifest,\n failureReason?: FleetLaunchFailureReason,\n ): void {\n fleetEvents.launchUnlessClaimed({\n packageName,\n ...(manifest?.name ? {miniappName: manifest.name} : {}),\n ...(manifest?.version ? {versionName: manifest.version} : {}),\n start: runtimeOptions?.trigger ?? \"automatic\",\n outcome,\n ...(outcome === \"failed\" ? {failureReason: failureReason ?? \"unknown\"} : {}),\n })\n }\n\n /** Resolve the bundle and spawn the context. Serialized via {@link inFlight}. */\n private async spawn(\n packageName: string,\n hints?: LaunchHints,\n runtimeOptions?: RuntimeLaunchOptions,\n ): Promise<LaunchResult> {\n const router = this.requireRouter()\n const resolved = await this.resolveBundle(packageName, hints, runtimeOptions)\n if (!resolved) {\n throw new Error(`MiniappLauncher: cannot resolve bundle for ${packageName}`)\n }\n // Before the age gate, so a miniapp this app can't run never asks the person's age.\n this.assertRunsOnPhone(packageName, resolved)\n const version = resolved.installedManifest?.version\n const projectRunning = runtimeOptions?.projectRunning ?? true\n // The age gate, before the deployment checks below, which then see the state after any question the person\n // answered. Only a launch the person makes in front of the app may ask.\n const verdict = await checkMiniappAge(\n {packageName, name: resolved.installedManifest?.name || packageName, ageRating: resolved.ageRating},\n {interactive: projectRunning && AppState.currentState === \"active\"},\n )\n if (verdict !== \"allowed\") throw new MiniappAgeRestrictedError(packageName, verdict)\n // A live dev server sets devUrl. A snapshot keeps the directory name `dev-*`\n // as the active version. Either one is the scanned build; a release fallback is neither.\n const activeVersion = await appRegistry.getActiveVersion(packageName)\n const devBuild = Boolean(resolved.devUrl) || Boolean(activeVersion?.startsWith(\"dev-\"))\n if (\n // Availability may change while the bundle resolves.\n !isMiniappAvailable(packageName, availabilityMode(runtimeOptions)) ||\n (devBuild ? !isDevMiniappAllowed(packageName) : !appRegistry.isReleaseAllowed(packageName, version))\n ) {\n throw new Error(`MiniappLauncher: ${packageName} bundle is not authorized by deployment policy`)\n }\n\n // Re-check after the async resolve: a different path may have spawned it\n // while we were fetching/reading the bundle.\n if (router.registeredPackages().includes(packageName)) {\n if (projectRunning && this.promote(packageName)) {\n this.reportLaunch(packageName, \"started\", runtimeOptions, resolved.installedManifest)\n }\n } else {\n const ok = await router.spawnAndRegister(packageName, resolved.bgSource, {\n permissions: resolved.declaredPermissions,\n installedManifest: resolved.installedManifest,\n hostTrustedSystem: resolved.hostTrustedSystem,\n projectRunning,\n developmentBuild: resolved.devBuild,\n })\n if (!ok) {\n throw new Error(`MiniappLauncher: spawn failed for ${packageName}`)\n }\n\n // Dev: wire the hot-reload + log-forwarding sidecar. No-op in prod\n // (devServerBridge silently drops when no sidecar is up).\n if (resolved.devUrl && resolved.devPort != null) {\n devServerBridge.connect(packageName, resolved.devUrl, resolved.devPort)\n } else if (resolved.devUrl) {\n // Dev miniapp with no resolvable port — it still launches, but hot\n // reload + log forwarding won't connect. Surface it instead of failing\n // silently (the old inline path hard-failed with \"no dev port\").\n console.warn(`MiniappLauncher: ${packageName} has no dev port — hot reload + log forwarding disabled`)\n }\n if (projectRunning) this.reportLaunch(packageName, \"started\", runtimeOptions, resolved.installedManifest)\n }\n\n return launchResultOf(resolved)\n }\n\n /**\n * Re-spawn local miniapps that were running when the app was last killed.\n *\n * Every miniapp runs as local JavaScript in the Mentra App on the phone.\n * A host-process kill tears down its JSContext, so without this restoration\n * it stays stopped on the next launch even though the user left it running.\n *\n * Each local miniapp persists its running flag to disk on start/stop\n * (`saveLocalAppRunningState`, via the applet's `onStart`/`onStop`). We read\n * those flags back and re-spawn the background context headlessly (no\n * foreground, no navigation) — the spawn registers the package so the home\n * tray/switcher project it as running, exactly as a normal start would. The\n * WebView, if any, re-attaches lazily when the user opens the app.\n *\n * Idempotent and best-effort: skips already-spawned contexts and clears the\n * persisted running flag if ensureRunning fails (for example, bundle resolution,\n * deployment policy, or context spawning) so it does not retry every boot.\n * Each restore is an automatic launch to Fleet, reported by ensureRunning.\n * Not gated on the glasses' hardware: a previously-running background app shouldn't be\n * dropped just because glasses are momentarily disconnected at boot — it\n * resumes when they reconnect, same as mid-session. A release this Mentra App\n * can't run is refused like any launch (see {@link assertRunsOnPhone}).\n */\n async autostartLocalMiniapps(): Promise<void> {\n const apps = await appRegistry.getInstalledMiniapps()\n for (const app of apps) {\n // Only phone-hosted miniapps have a JSContext to re-spawn. Offline\n // built-ins (`local:false, offline:true`) restore their native running\n // state elsewhere and have no bundle to launch.\n if (!app.local) continue\n if (!getLocalAppRunningState(app.packageName)) continue\n if (this.isRunning(app.packageName)) continue\n try {\n // Restoring what was running is the platform's doing, never the user's.\n await this.ensureRunning(app.packageName, undefined, {trigger: \"automatic\"})\n } catch (e) {\n console.warn(`MiniappLauncher: autostart failed for ${app.packageName} — clearing stale running flag`, e)\n saveLocalAppRunningState(app.packageName, false)\n }\n }\n }\n\n /**\n * Ensure the context is running AND has completed its CONNECT handshake.\n * Used by the action broker, which must not deliver to a context that hasn't\n * come up yet. Throws on spawn failure or connect timeout.\n */\n async ensureConnected(\n packageName: string,\n connectTimeoutMs = 10_000,\n hints?: LaunchHints,\n runtimeOptions?: RuntimeLaunchOptions,\n ): Promise<void> {\n await this.ensureRunning(packageName, hints, runtimeOptions)\n await localMiniappRuntime.waitForConnect(packageName, connectTimeoutMs)\n }\n\n /** Tear down a miniapp's background JS context. */\n async stop(packageName: string): Promise<void> {\n // If a launch is in flight, let it finish first. Otherwise unregister()\n // no-ops on the not-yet-registered package and the pending spawn then\n // re-registers the context — leaving it running despite the stop.\n const pending = this.inFlight.get(packageName)\n if (pending) {\n try {\n await pending\n } catch {\n // Launch failed — nothing got registered, so nothing to tear down.\n }\n }\n await this.deps?.router.unregister(packageName)\n }\n\n // --- internals -----------------------------------------------------------\n\n private storedDevUrl(packageName: string): string | undefined {\n const r = storage.load<string>(`${packageName}_dev_url`)\n return r.is_ok() ? r.value : undefined\n }\n\n /** resolveDevPort — moved verbatim from the old LocalMiniappView helper. */\n private resolveDevPort(searchParam: string | undefined, packageName: string): number | null {\n if (searchParam) {\n const n = parseInt(searchParam, 10)\n if (Number.isFinite(n)) return n\n }\n const stored = storage.load<number>(`${packageName}_dev_port`)\n if (stored.is_ok()) return stored.value\n return null\n }\n}\n\nexport const miniappLauncher = new MiniappLauncher()\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentra/engine",
3
- "version": "3.4.0-dev.1209",
3
+ "version": "3.4.0-dev.1210",
4
4
  "description": "Mentra Engine — miniapp registry, WebView bridge, and miniapp store",
5
5
  "main": "build/index.js",
6
6
  "react-native": "src/index.ts",
@@ -97,13 +97,13 @@
97
97
  "registry": "https://registry.npmjs.org/"
98
98
  },
99
99
  "dependencies": {
100
- "@mentra/acs-meeting": "3.4.0-dev.1209",
101
- "@mentra/glasses-media": "3.4.0-dev.1209",
102
- "@mentra/bluetooth-sdk": "3.4.0-dev.1209",
103
- "@mentra/cloud-client": "3.4.0-dev.1209",
104
- "@mentra/cloud-protocol": "3.4.0-dev.1209",
105
- "@mentra/crust": "3.4.0-dev.1209",
106
- "@mentra/miniapp": "3.4.0-dev.1209",
100
+ "@mentra/acs-meeting": "3.4.0-dev.1210",
101
+ "@mentra/glasses-media": "3.4.0-dev.1210",
102
+ "@mentra/bluetooth-sdk": "3.4.0-dev.1210",
103
+ "@mentra/cloud-client": "3.4.0-dev.1210",
104
+ "@mentra/cloud-protocol": "3.4.0-dev.1210",
105
+ "@mentra/crust": "3.4.0-dev.1210",
106
+ "@mentra/miniapp": "3.4.0-dev.1210",
107
107
  "buffer": "^6.0.3",
108
108
  "events": "^3.3.0",
109
109
  "jszip": "^3.10.1",
@@ -12,9 +12,9 @@ export interface EngineReleaseMetadata {
12
12
  export const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({
13
13
  "schemaVersion": 1,
14
14
  "familyBaseVersion": "3.4.0",
15
- "releaseIdentity": "3.4.0-dev.1209",
16
- "releaseSetId": "mentra-3.4.0-dev.1209",
17
- "sourceCommit": "1f73b5402e1a0b5f06000b84300695f0dfc879c1",
18
- "otaManifestUrl": "https://artifactscdn.mentraglass.com/Mentra-Community/MentraOS/releases/mentra-builds-v3.4.0/mentra-live-ota-3.4.0-dev.1209.json",
19
- "otaManifestSha256": "f93b7fed880136625a8b29c4e74210e882b5322bd79f76aa84d79a31bb95b82a"
15
+ "releaseIdentity": "3.4.0-dev.1210",
16
+ "releaseSetId": "mentra-3.4.0-dev.1210",
17
+ "sourceCommit": "2007a90070b4909ea37ff32423b29afb2cef7c01",
18
+ "otaManifestUrl": "https://artifactscdn.mentraglass.com/Mentra-Community/MentraOS/releases/mentra-builds-v3.4.0/mentra-live-ota-3.4.0-dev.1210.json",
19
+ "otaManifestSha256": "88dd086945cfe6b9f90928cdbbc9f82612277ec7894f3360f8539a489972fcc4"
20
20
  })
@@ -45,6 +45,8 @@ function wireDevServerRespawnBackground(router: MentraJSRouter, uiRouter: Mentra
45
45
  console.warn(`MentraJS: respawn-bg could not resolve bundle for ${packageName}`)
46
46
  return
47
47
  }
48
+ // The rebuilt bundle meets a launch's Mentra App version rule; refused, the running context stays.
49
+ miniappLauncher.assertRunsOnPhone(packageName, resolved)
48
50
  // Force a respawn (kill + spawn) rather than launcher.ensureRunning,
49
51
  // which is idempotent and would no-op an already-registered context.
50
52
  const projectRunning = router.isProjectedRunning(packageName)
@@ -40,7 +40,12 @@ import type {FleetLaunchFailureReason} from "@mentra/cloud-protocol"
40
40
  import {File} from "expo-file-system"
41
41
  import {AppState, Platform} from "react-native"
42
42
 
43
- import {isDevMiniappAllowed, isMiniappAvailable, type MiniappAvailabilityMode} from "../runtime/bootstrap"
43
+ import {
44
+ getConfigValues,
45
+ isDevMiniappAllowed,
46
+ isMiniappAvailable,
47
+ type MiniappAvailabilityMode,
48
+ } from "../runtime/bootstrap"
44
49
  import {resolveDevBundleSource} from "../utils/devMiniappSnapshot"
45
50
  import {storage} from "../utils/storage/storage"
46
51
  import {MiniappRunningError} from "../utils/storeInstallRuntime"
@@ -48,6 +53,7 @@ import appRegistry, {getLocalAppRunningState, saveLocalAppRunningState} from "./
48
53
  import devServerBridge from "./DevServerBridge"
49
54
  import {fleetEvents} from "./fleet/fleetEvents"
50
55
  import localMiniappRuntime, {type InstalledMiniappManifest} from "./LocalMiniappRuntime"
56
+ import {checkManifestVersions} from "./manifestVersionGate"
51
57
  import type {MentraJSRouter} from "./MentraJSRouter"
52
58
  import {checkMiniappAge, type AgeGateVerdict, type AgeRatedMiniapp} from "./miniappAgeGate"
53
59
  import {developmentOriginName, miniappOrigin, miniappOriginUrl, type MiniappOriginPlatform} from "./miniappOrigin"
@@ -75,6 +81,23 @@ export class MiniappAgeRestrictedError extends Error {
75
81
  }
76
82
  }
77
83
 
84
+ /**
85
+ * The bundle needs a newer Mentra App, or a Mentra Miniapp SDK this one doesn't run: the rule installs refuse by,
86
+ * checked again as it launches. A release a newer Mentra App installed stays on disk when an older one replaces it (a
87
+ * development build replaced from TestFlight), and would otherwise start against features this one lacks. The message
88
+ * is for logs; a screen explains it in the person's language, as an install refused by the same rule does.
89
+ */
90
+ export class MiniappIncompatibleHostError extends Error {
91
+ constructor(
92
+ readonly packageName: string,
93
+ readonly miniappName: string,
94
+ readonly reason: string,
95
+ ) {
96
+ super(`${miniappName} can't run on this phone: ${reason}`)
97
+ this.name = "MiniappIncompatibleHostError"
98
+ }
99
+ }
100
+
78
101
  interface LauncherDeps {
79
102
  /** The host-constructed router (needs the native Crust binding). */
80
103
  router: MentraJSRouter
@@ -183,9 +206,11 @@ function serverOrigin(url: string): string | null {
183
206
 
184
207
  /**
185
208
  * Why a launch failed, as the code Fleet takes (Phase 12): never the error's text. A bundle that cannot be found is
186
- * `bundle_missing`; a deployment, age or access block `policy_blocked`; a context that did not start `spawn_failed`.
209
+ * `bundle_missing`; one that needs a newer Mentra App `incompatible_host`; a deployment, age or access block
210
+ * `policy_blocked`; a context that did not start `spawn_failed`.
187
211
  */
188
212
  export function launchFailureReason(error: unknown): FleetLaunchFailureReason {
213
+ if (error instanceof MiniappIncompatibleHostError) return "incompatible_host"
189
214
  if (error instanceof MiniappAgeRestrictedError || error instanceof MiniappAccessRevokedError) return "policy_blocked"
190
215
  const message = error instanceof Error ? error.message : ""
191
216
  if (message.includes("cannot resolve bundle")) return "bundle_missing"
@@ -489,6 +514,15 @@ class MiniappLauncher {
489
514
  }
490
515
  }
491
516
 
517
+ /** Throws {@link MiniappIncompatibleHostError} when this Mentra App can't run the resolved bundle. */
518
+ assertRunsOnPhone(packageName: string, resolved: ResolvedBundle): void {
519
+ const manifest = resolved.installedManifest
520
+ const {hostVersion, supportedMiniappSdkRange} = getConfigValues()
521
+ if (!manifest || !hostVersion || !supportedMiniappSdkRange) return
522
+ const versions = checkManifestVersions(manifest, {hostVersion, supportedSdkRange: supportedMiniappSdkRange})
523
+ if (!versions.ok) throw new MiniappIncompatibleHostError(packageName, manifest.name || packageName, versions.reason)
524
+ }
525
+
492
526
  /**
493
527
  * Ensure a miniapp's background JS context is spawned. Idempotent — if the
494
528
  * context is already alive this is a no-op spawn-wise (it still resolves the
@@ -597,6 +631,8 @@ class MiniappLauncher {
597
631
  if (!resolved) {
598
632
  throw new Error(`MiniappLauncher: cannot resolve bundle for ${packageName}`)
599
633
  }
634
+ // Before the age gate, so a miniapp this app can't run never asks the person's age.
635
+ this.assertRunsOnPhone(packageName, resolved)
600
636
  const version = resolved.installedManifest?.version
601
637
  const projectRunning = runtimeOptions?.projectRunning ?? true
602
638
  // The age gate, before the deployment checks below, which then see the state after any question the person
@@ -670,9 +706,10 @@ class MiniappLauncher {
670
706
  * persisted running flag if ensureRunning fails (for example, bundle resolution,
671
707
  * deployment policy, or context spawning) so it does not retry every boot.
672
708
  * Each restore is an automatic launch to Fleet, reported by ensureRunning.
673
- * Not compatibility-gated: a previously-running background app shouldn't be
709
+ * Not gated on the glasses' hardware: a previously-running background app shouldn't be
674
710
  * dropped just because glasses are momentarily disconnected at boot — it
675
- * resumes when they reconnect, same as mid-session.
711
+ * resumes when they reconnect, same as mid-session. A release this Mentra App
712
+ * can't run is refused like any launch (see {@link assertRunsOnPhone}).
676
713
  */
677
714
  async autostartLocalMiniapps(): Promise<void> {
678
715
  const apps = await appRegistry.getInstalledMiniapps()