@glassly/engine 0.4.0 → 0.4.1-dev.1137
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/engine.d.ts +3 -1
- package/build/engine.d.ts.map +1 -1
- package/build/engine.js +6 -4
- package/build/engine.js.map +1 -1
- package/build/facades/display.d.ts +1 -1
- package/build/facades/display.d.ts.map +1 -1
- package/build/facades/display.js +1 -1
- package/build/facades/display.js.map +1 -1
- package/build/facades/glasses.js +1 -1
- package/build/facades/glasses.js.map +1 -1
- package/build/facades/permissions.d.ts.map +1 -1
- package/build/facades/permissions.js +0 -2
- package/build/facades/permissions.js.map +1 -1
- package/build/facades/phoneNotifications.d.ts +7 -0
- package/build/facades/phoneNotifications.d.ts.map +1 -1
- package/build/facades/phoneNotifications.js +6 -0
- package/build/facades/phoneNotifications.js.map +1 -1
- package/build/facades/providerKeys.d.ts +1 -1
- package/build/facades/providerKeys.js +1 -1
- package/build/facades/providerKeys.js.map +1 -1
- package/build/facades/reports.d.ts +15 -0
- package/build/facades/reports.d.ts.map +1 -1
- package/build/facades/reports.js +37 -0
- package/build/facades/reports.js.map +1 -1
- package/build/facades/settings.js +1 -1
- package/build/facades/settings.js.map +1 -1
- package/build/generated/releaseMetadata.js +4 -4
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/index.d.ts +2 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/build/react/useMentraLiveOta.d.ts.map +1 -1
- package/build/react/useMentraLiveOta.js.map +1 -1
- package/build/runtime/config.d.ts.map +1 -1
- package/build/runtime/config.js.map +1 -1
- package/build/services/AiTierResolution.d.ts +1 -1
- package/build/services/AiTierResolution.d.ts.map +1 -1
- package/build/services/AiTierResolution.js.map +1 -1
- package/build/services/AppRegistry.d.ts +12 -1
- package/build/services/AppRegistry.d.ts.map +1 -1
- package/build/services/AppRegistry.js +33 -0
- package/build/services/AppRegistry.js.map +1 -1
- package/build/services/AutomaticReportResult.d.ts.map +1 -1
- package/build/services/AutomaticReportResult.js.map +1 -1
- package/build/services/CloudClientService.d.ts +7 -5
- package/build/services/CloudClientService.d.ts.map +1 -1
- package/build/services/CloudClientService.js +17 -14
- package/build/services/CloudClientService.js.map +1 -1
- package/build/services/DisplayProcessor.d.ts.map +1 -1
- package/build/services/DisplayProcessor.js +2 -1
- package/build/services/DisplayProcessor.js.map +1 -1
- package/build/services/GestureDotsService.js +1 -1
- package/build/services/GestureDotsService.js.map +1 -1
- package/build/services/HotspotOtaTransport.d.ts.map +1 -1
- package/build/services/HotspotOtaTransport.js +0 -1
- package/build/services/HotspotOtaTransport.js.map +1 -1
- package/build/services/JSRouter.js +2 -2
- package/build/services/JSRouter.js.map +1 -1
- package/build/services/LocalLlmModelManager.d.ts.map +1 -1
- package/build/services/LocalLlmModelManager.js +4 -3
- package/build/services/LocalLlmModelManager.js.map +1 -1
- package/build/services/LocalLlmProvider.d.ts.map +1 -1
- package/build/services/LocalLlmProvider.js +5 -6
- package/build/services/LocalLlmProvider.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +10 -3
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +142 -30
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -1
- package/build/services/LocalSttFallbackCoordinator.js.map +1 -1
- package/build/services/NativeNavigationService.d.ts.map +1 -1
- package/build/services/NativeNavigationService.js.map +1 -1
- package/build/services/NavigationHandlers.d.ts +1 -1
- package/build/services/NavigationHandlers.js +2 -2
- package/build/services/NavigationHandlers.js.map +1 -1
- package/build/services/NavigationService.d.ts +2 -2
- package/build/services/NavigationService.d.ts.map +1 -1
- package/build/services/NavigationService.js +11 -11
- package/build/services/NavigationService.js.map +1 -1
- package/build/services/PairingIdentity.d.ts.map +1 -1
- package/build/services/PairingIdentity.js.map +1 -1
- package/build/services/PhoneCalendarService.d.ts.map +1 -1
- package/build/services/PhoneCalendarService.js.map +1 -1
- package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
- package/build/services/PhoneNotificationsSync.js +2 -1
- package/build/services/PhoneNotificationsSync.js.map +1 -1
- package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
- package/build/services/PhoneStreamCoordinator.js.map +1 -1
- package/build/services/ProviderKeyValidation.d.ts +1 -1
- package/build/services/ProviderKeyValidation.js +1 -1
- package/build/services/ProviderKeyValidation.js.map +1 -1
- package/build/services/STTModelManager.d.ts.map +1 -1
- package/build/services/STTModelManager.js +1 -6
- package/build/services/STTModelManager.js.map +1 -1
- package/build/services/SupportProfileSync.d.ts.map +1 -1
- package/build/services/SupportProfileSync.js +3 -0
- package/build/services/SupportProfileSync.js.map +1 -1
- package/build/services/TranscriptionRouting.d.ts +3 -0
- package/build/services/TranscriptionRouting.d.ts.map +1 -1
- package/build/services/TranscriptionRouting.js +14 -0
- package/build/services/TranscriptionRouting.js.map +1 -1
- package/build/services/UIRouter.d.ts.map +1 -1
- package/build/services/UIRouter.js.map +1 -1
- package/build/services/controllers.d.ts.map +1 -1
- package/build/services/controllers.js +1 -0
- package/build/services/controllers.js.map +1 -1
- package/build/services/effectiveCapabilities.js +3 -2
- package/build/services/effectiveCapabilities.js.map +1 -1
- package/build/services/gestureDotGlyphs.d.ts.map +1 -1
- package/build/services/gestureDotGlyphs.js.map +1 -1
- package/build/services/manifestActions.d.ts.map +1 -1
- package/build/services/manifestActions.js.map +1 -1
- package/build/services/neuralBand/MetaAuth.d.ts +1 -0
- package/build/services/neuralBand/MetaAuth.d.ts.map +1 -1
- package/build/services/neuralBand/MetaAuth.js +36 -8
- package/build/services/neuralBand/MetaAuth.js.map +1 -1
- package/build/services/notificationAppPayload.d.ts +14 -0
- package/build/services/notificationAppPayload.d.ts.map +1 -0
- package/build/services/notificationAppPayload.js +15 -0
- package/build/services/notificationAppPayload.js.map +1 -0
- package/build/services/notifications/miniappNotifications.d.ts +31 -0
- package/build/services/notifications/miniappNotifications.d.ts.map +1 -0
- package/build/services/notifications/miniappNotifications.js +24 -0
- package/build/services/notifications/miniappNotifications.js.map +1 -0
- package/build/services/pages/pageEligibility.d.ts +1 -2
- package/build/services/pages/pageEligibility.d.ts.map +1 -1
- package/build/services/pages/pageEligibility.js +4 -2
- package/build/services/pages/pageEligibility.js.map +1 -1
- package/build/services/settingsBridge.d.ts.map +1 -1
- package/build/services/settingsBridge.js +0 -2
- package/build/services/settingsBridge.js.map +1 -1
- package/build/services/uiShim.d.ts.map +1 -1
- package/build/services/uiShim.js +14 -0
- package/build/services/uiShim.js.map +1 -1
- package/build/stores/appVisibility.d.ts.map +1 -1
- package/build/stores/appVisibility.js.map +1 -1
- package/build/stores/gallerySync.d.ts.map +1 -1
- package/build/stores/gallerySync.js.map +1 -1
- package/build/stores/glasses.d.ts.map +1 -1
- package/build/stores/glasses.js +1 -0
- package/build/stores/glasses.js.map +1 -1
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +27 -28
- package/build/stores/settings.js.map +1 -1
- package/build/types/capabilities/even-realities-g1.d.ts.map +1 -1
- package/build/types/capabilities/even-realities-g1.js +4 -0
- package/build/types/capabilities/even-realities-g1.js.map +1 -1
- package/build/types/capabilities/nimo.d.ts.map +1 -1
- package/build/types/capabilities/nimo.js +21 -7
- package/build/types/capabilities/nimo.js.map +1 -1
- package/build/types/enums.d.ts +2 -1
- package/build/types/enums.d.ts.map +1 -1
- package/build/types/enums.js +1 -0
- package/build/types/enums.js.map +1 -1
- package/build/utils/display/index.d.ts +1 -0
- package/build/utils/display/index.d.ts.map +1 -1
- package/build/utils/display/index.js +2 -2
- package/build/utils/display/index.js.map +1 -1
- package/build/utils/display/profiles/index.d.ts +1 -0
- package/build/utils/display/profiles/index.d.ts.map +1 -1
- package/build/utils/display/profiles/index.js +1 -0
- package/build/utils/display/profiles/index.js.map +1 -1
- package/build/utils/display/profiles/nimo.d.ts +2 -0
- package/build/utils/display/profiles/nimo.d.ts.map +1 -0
- package/build/utils/display/profiles/nimo.js +3 -0
- package/build/utils/display/profiles/nimo.js.map +1 -0
- package/build/utils/display/scene/svgPath.js.map +1 -1
- package/build/utils/memory/search.d.ts.map +1 -1
- package/build/utils/memory/search.js +17 -7
- package/build/utils/memory/search.js.map +1 -1
- package/package.json +15 -15
- package/src/engine.ts +9 -7
- package/src/facades/display.ts +1 -1
- package/src/facades/glasses.ts +1 -1
- package/src/facades/permissions.ts +0 -2
- package/src/facades/phoneNotifications.ts +10 -0
- package/src/facades/providerKeys.ts +1 -1
- package/src/facades/reports.ts +41 -0
- package/src/facades/settings.ts +1 -1
- package/src/generated/releaseMetadata.ts +4 -4
- package/src/index.ts +2 -0
- package/src/react/useMentraLiveOta.ts +1 -4
- package/src/runtime/config.ts +1 -6
- package/src/services/AiTierResolution.ts +1 -1
- package/src/services/AppRegistry.ts +38 -1
- package/src/services/AutomaticReportResult.ts +1 -3
- package/src/services/CloudClientService.ts +17 -11
- package/src/services/DisplayProcessor.ts +2 -0
- package/src/services/GestureDotsService.ts +1 -1
- package/src/services/HotspotOtaTransport.ts +1 -5
- package/src/services/JSRouter.ts +2 -2
- package/src/services/LocalLlmModelManager.ts +4 -3
- package/src/services/LocalLlmProvider.ts +6 -7
- package/src/services/LocalMiniappRuntime.ts +170 -40
- package/src/services/LocalSttFallbackCoordinator.ts +1 -5
- package/src/services/NativeNavigationService.ts +1 -2
- package/src/services/NavigationHandlers.ts +2 -2
- package/src/services/NavigationService.ts +12 -11
- package/src/services/PairingIdentity.ts +1 -3
- package/src/services/PhoneCalendarService.ts +1 -3
- package/src/services/PhoneNotificationsSync.ts +2 -1
- package/src/services/PhoneStreamCoordinator.ts +1 -2
- package/src/services/ProviderKeyValidation.ts +1 -1
- package/src/services/STTModelManager.ts +1 -6
- package/src/services/SupportProfileSync.ts +2 -0
- package/src/services/TranscriptionRouting.ts +18 -0
- package/src/services/UIRouter.ts +3 -9
- package/src/services/controllers.ts +1 -0
- package/src/services/effectiveCapabilities.ts +3 -2
- package/src/services/gestureDotGlyphs.ts +10 -5
- package/src/services/manifestActions.ts +2 -1
- package/src/services/neuralBand/MetaAuth.ts +37 -8
- package/src/services/notificationAppPayload.ts +15 -0
- package/src/services/notifications/miniappNotifications.ts +53 -0
- package/src/services/pages/pageEligibility.ts +3 -2
- package/src/services/settingsBridge.ts +1 -4
- package/src/services/uiShim.ts +14 -0
- package/src/stores/appVisibility.ts +2 -5
- package/src/stores/gallerySync.ts +1 -7
- package/src/stores/glasses.ts +1 -0
- package/src/stores/settings.ts +27 -28
- package/src/types/capabilities/even-realities-g1.ts +5 -0
- package/src/types/capabilities/nimo.ts +23 -7
- package/src/types/enums.ts +1 -0
- package/src/utils/display/index.ts +2 -2
- package/src/utils/display/profiles/index.ts +2 -0
- package/src/utils/display/profiles/nimo.ts +2 -0
- package/src/utils/display/scene/svgPath.ts +1 -1
- package/src/utils/memory/search.ts +17 -7
|
@@ -14,6 +14,7 @@ import {AppState, Linking} from "react-native"
|
|
|
14
14
|
import {logBuffer} from "../utils/devLogging"
|
|
15
15
|
import {collectDisplayDiagnostics} from "./DisplayDiagnostics"
|
|
16
16
|
import Share from "react-native-share"
|
|
17
|
+
import {uses24HourClock} from "react-native-localize"
|
|
17
18
|
import * as Battery from "expo-battery"
|
|
18
19
|
import * as Clipboard from "expo-clipboard"
|
|
19
20
|
import {File, Paths} from "expo-file-system"
|
|
@@ -83,6 +84,13 @@ import overlayPages from "./pages/OverlayPages"
|
|
|
83
84
|
import dashboardOpenTrace from "./debug/DashboardOpenTrace"
|
|
84
85
|
import {isAppForegrounded, isPhoneActive, onAppForegroundChange, onPhoneActivityChange} from "./PhoneActivity"
|
|
85
86
|
import {installedNotificationApps} from "./PhoneNotificationsSync"
|
|
87
|
+
import {
|
|
88
|
+
emitMiniappNotification,
|
|
89
|
+
MINIAPP_NOTIFICATION_CONTENT_MAX,
|
|
90
|
+
MINIAPP_NOTIFICATION_ID_MAX,
|
|
91
|
+
MINIAPP_NOTIFICATION_TITLE_MAX,
|
|
92
|
+
miniappNotificationId,
|
|
93
|
+
} from "./notifications/miniappNotifications"
|
|
86
94
|
import {MiniappIcons} from "./MiniappIcons"
|
|
87
95
|
import {pairedControllersFrom, readyControllersKey} from "./controllers"
|
|
88
96
|
import {
|
|
@@ -141,6 +149,7 @@ import {phoneStreamCoordinator} from "./PhoneStreamCoordinator"
|
|
|
141
149
|
import {phoneVideoCoordinator} from "./PhoneVideoCoordinator"
|
|
142
150
|
import {runSentenceTtsPipeline} from "./SentenceTtsPipeline"
|
|
143
151
|
import {
|
|
152
|
+
matchesTranscriptionSubscription,
|
|
144
153
|
summarizeTranscriptionRoutes,
|
|
145
154
|
transcriptionDeliveryRoute,
|
|
146
155
|
type TranscriptionDeliveryRoute,
|
|
@@ -433,7 +442,7 @@ const PERMISSION_TYPE_TO_CANONICAL: Record<string, string> = {
|
|
|
433
442
|
}
|
|
434
443
|
|
|
435
444
|
/**
|
|
436
|
-
* User API keys (Settings > API Keys) a miniapp may read via
|
|
445
|
+
* User API keys (Settings > Privacy > API Keys) a miniapp may read via
|
|
437
446
|
* session.keys.get(), mapped provider → settings-store key. Explicit
|
|
438
447
|
* allowlist: the Soniox key is deliberately absent — it gates the
|
|
439
448
|
* phone→Soniox direct transcription path and is not exposed to miniapps.
|
|
@@ -477,9 +486,9 @@ function directLlmProvider(name: AiTierProvider): LlmProviderImpl {
|
|
|
477
486
|
}
|
|
478
487
|
|
|
479
488
|
const QUOTA_EXCEEDED_MESSAGE =
|
|
480
|
-
"AI credits are used up. Get more in Settings > Membership, or add your own API key in Settings > API Keys."
|
|
489
|
+
"AI credits are used up. Get more in Settings > Membership, or add your own API key in Settings > Privacy > API Keys."
|
|
481
490
|
|
|
482
|
-
/** The user's saved key (Settings > API Keys) for an LLM provider, "" if none. */
|
|
491
|
+
/** The user's saved key (Settings > Privacy > API Keys) for an LLM provider, "" if none. */
|
|
483
492
|
function userLlmKey(provider: AiTierProvider): string {
|
|
484
493
|
const raw = useSettingsStore.getState().getSetting(MINIAPP_READABLE_API_KEYS[provider])
|
|
485
494
|
return typeof raw === "string" ? raw.trim() : ""
|
|
@@ -1512,6 +1521,12 @@ class LocalMiniappRuntime {
|
|
|
1512
1521
|
case MiniappRequestType.NOTIFICATION_APPS_LIST:
|
|
1513
1522
|
void this.handleNotificationAppsList(packageName, requestId)
|
|
1514
1523
|
break
|
|
1524
|
+
case MiniappRequestType.NOTIFICATION_POST:
|
|
1525
|
+
this.handleNotificationPost(packageName, payload, requestId)
|
|
1526
|
+
break
|
|
1527
|
+
case MiniappRequestType.NOTIFICATION_DISMISS:
|
|
1528
|
+
this.handleNotificationDismiss(packageName, payload, requestId)
|
|
1529
|
+
break
|
|
1515
1530
|
case MiniappRequestType.NATIVE_NAV:
|
|
1516
1531
|
this.handleNativeNav(packageName, payload, requestId)
|
|
1517
1532
|
break
|
|
@@ -2791,7 +2806,17 @@ class LocalMiniappRuntime {
|
|
|
2791
2806
|
return
|
|
2792
2807
|
}
|
|
2793
2808
|
try {
|
|
2794
|
-
|
|
2809
|
+
const posters = this.interopApps().filter(
|
|
2810
|
+
(app) => app.packageName && app.permissions?.some((permission) => permission.type === "POST_NOTIFICATIONS"),
|
|
2811
|
+
)
|
|
2812
|
+
const miniapps = await Promise.all(
|
|
2813
|
+
posters.map(async (app) => ({
|
|
2814
|
+
packageName: app.packageName,
|
|
2815
|
+
appName: app.name || app.packageName,
|
|
2816
|
+
icon: (await this.miniappIcons.read(app)) ?? null,
|
|
2817
|
+
})),
|
|
2818
|
+
)
|
|
2819
|
+
this.sendResult(packageName, requestId, true, [...(await installedNotificationApps()), ...miniapps])
|
|
2795
2820
|
} catch (err) {
|
|
2796
2821
|
this.sendResult(packageName, requestId, false, undefined, {
|
|
2797
2822
|
code: MiniappErrorCode.INTERNAL,
|
|
@@ -2800,6 +2825,60 @@ class LocalMiniappRuntime {
|
|
|
2800
2825
|
}
|
|
2801
2826
|
}
|
|
2802
2827
|
|
|
2828
|
+
private handleNotificationPost(packageName: string, payload: Record<string, unknown>, requestId?: string): void {
|
|
2829
|
+
if (!this.requirePermission(packageName, requestId, "POST_NOTIFICATIONS", "to post notifications")) return
|
|
2830
|
+
const title = typeof payload.title === "string" ? payload.title.slice(0, MINIAPP_NOTIFICATION_TITLE_MAX) : ""
|
|
2831
|
+
const content =
|
|
2832
|
+
typeof payload.content === "string" ? payload.content.slice(0, MINIAPP_NOTIFICATION_CONTENT_MAX) : ""
|
|
2833
|
+
if (!title.trim() && !content.trim()) {
|
|
2834
|
+
this.sendResult(packageName, requestId, false, undefined, {
|
|
2835
|
+
code: MiniappErrorCode.INVALID_ARGUMENT,
|
|
2836
|
+
message: "A notification needs a title or content",
|
|
2837
|
+
})
|
|
2838
|
+
return
|
|
2839
|
+
}
|
|
2840
|
+
if (payload.id !== undefined && (typeof payload.id !== "string" || !payload.id.trim())) {
|
|
2841
|
+
this.sendResult(packageName, requestId, false, undefined, {
|
|
2842
|
+
code: MiniappErrorCode.INVALID_ARGUMENT,
|
|
2843
|
+
message: "A notification id must be a non-empty string",
|
|
2844
|
+
})
|
|
2845
|
+
return
|
|
2846
|
+
}
|
|
2847
|
+
const id =
|
|
2848
|
+
typeof payload.id === "string"
|
|
2849
|
+
? payload.id.trim().slice(0, MINIAPP_NOTIFICATION_ID_MAX)
|
|
2850
|
+
: `${Date.now().toString(36)}-${++this.notificationSeq}`
|
|
2851
|
+
const app = this.interopApps().find((candidate) => candidate.packageName === packageName)
|
|
2852
|
+
emitMiniappNotification({
|
|
2853
|
+
type: "posted",
|
|
2854
|
+
notificationId: miniappNotificationId(packageName, id),
|
|
2855
|
+
source: packageName,
|
|
2856
|
+
app: app?.name || packageName,
|
|
2857
|
+
title,
|
|
2858
|
+
content,
|
|
2859
|
+
timestamp: Date.now(),
|
|
2860
|
+
})
|
|
2861
|
+
this.sendResult(packageName, requestId, true, {id})
|
|
2862
|
+
}
|
|
2863
|
+
|
|
2864
|
+
private handleNotificationDismiss(packageName: string, payload: Record<string, unknown>, requestId?: string): void {
|
|
2865
|
+
if (!this.requirePermission(packageName, requestId, "POST_NOTIFICATIONS", "to dismiss notifications")) return
|
|
2866
|
+
if (typeof payload.id !== "string" || !payload.id.trim()) {
|
|
2867
|
+
this.sendResult(packageName, requestId, false, undefined, {
|
|
2868
|
+
code: MiniappErrorCode.INVALID_ARGUMENT,
|
|
2869
|
+
message: "dismiss needs the id post() returned",
|
|
2870
|
+
})
|
|
2871
|
+
return
|
|
2872
|
+
}
|
|
2873
|
+
emitMiniappNotification({
|
|
2874
|
+
type: "dismissed",
|
|
2875
|
+
notificationId: miniappNotificationId(packageName, payload.id.trim().slice(0, MINIAPP_NOTIFICATION_ID_MAX)),
|
|
2876
|
+
source: packageName,
|
|
2877
|
+
timestamp: Date.now(),
|
|
2878
|
+
})
|
|
2879
|
+
this.sendResult(packageName, requestId, true)
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2803
2882
|
/**
|
|
2804
2883
|
* Push bridged settings as they change: stored keys from the store, the
|
|
2805
2884
|
* derived keys whenever their inputs move (phone activity, the display,
|
|
@@ -4149,7 +4228,7 @@ class LocalMiniappRuntime {
|
|
|
4149
4228
|
}
|
|
4150
4229
|
|
|
4151
4230
|
// ---------------------------------------------------------------------------
|
|
4152
|
-
// User API keys (Settings > API Keys)
|
|
4231
|
+
// User API keys (Settings > Privacy > API Keys)
|
|
4153
4232
|
// ---------------------------------------------------------------------------
|
|
4154
4233
|
|
|
4155
4234
|
/**
|
|
@@ -4217,7 +4296,7 @@ class LocalMiniappRuntime {
|
|
|
4217
4296
|
* The request names a TIER; this handler resolves it to a provider+model
|
|
4218
4297
|
* (Settings > AI Models dropdowns, or auto from saved keys) and then picks
|
|
4219
4298
|
* one of two transport paths, decided by whether the user saved a key for
|
|
4220
|
-
* the resolved provider (Settings > API Keys) — the phone is where both
|
|
4299
|
+
* the resolved provider (Settings > Privacy > API Keys) — the phone is where both
|
|
4221
4300
|
* settings live, so this is the only place the choice can be made:
|
|
4222
4301
|
*
|
|
4223
4302
|
* - User key saved → the phone calls the vendor DIRECTLY via
|
|
@@ -4490,7 +4569,7 @@ class LocalMiniappRuntime {
|
|
|
4490
4569
|
this.sendResult(packageName, requestId, false, undefined, {
|
|
4491
4570
|
code: MiniappErrorCode.QUOTA_EXCEEDED,
|
|
4492
4571
|
message:
|
|
4493
|
-
"AI credits are used up. Get more in Settings > Membership, or add your own API key in Settings > API Keys.",
|
|
4572
|
+
"AI credits are used up. Get more in Settings > Membership, or add your own API key in Settings > Privacy > API Keys.",
|
|
4494
4573
|
})
|
|
4495
4574
|
return
|
|
4496
4575
|
}
|
|
@@ -4744,7 +4823,11 @@ class LocalMiniappRuntime {
|
|
|
4744
4823
|
* session.system.webLogin — host in-app browser sheet. Same contract as
|
|
4745
4824
|
* scanQr: the seam presents a Modal and never clears miniapp foreground.
|
|
4746
4825
|
*/
|
|
4747
|
-
private async handleWebLogin(
|
|
4826
|
+
private async handleWebLogin(
|
|
4827
|
+
packageName: string,
|
|
4828
|
+
payload: Record<string, unknown>,
|
|
4829
|
+
requestId?: string,
|
|
4830
|
+
): Promise<void> {
|
|
4748
4831
|
try {
|
|
4749
4832
|
const result = await invokeWebLoginSeam(getUiSeams().webLogin, payload, packageName)
|
|
4750
4833
|
this.sendResult(packageName, requestId, true, result)
|
|
@@ -5385,6 +5468,9 @@ class LocalMiniappRuntime {
|
|
|
5385
5468
|
*/
|
|
5386
5469
|
private buildCloudAudioSubscriptions(cloudStreams: Set<string>): AudioSubscription[] {
|
|
5387
5470
|
const subs: AudioSubscription[] = []
|
|
5471
|
+
// One auto recognizer serves every transcription listener. Explicit-language
|
|
5472
|
+
// listeners are filtered at delivery; translations still need their own jobs.
|
|
5473
|
+
const hasAutoTranscription = cloudStreams.has("transcription:auto")
|
|
5388
5474
|
// Union of every connected app's TRANSCRIPTION_CONFIG hints. Hints only
|
|
5389
5475
|
// apply to the auto-mode TRANSCRIPTION subscription (the wire schema has
|
|
5390
5476
|
// no hints field on specific-language sources — the language IS the hint
|
|
@@ -5395,6 +5481,7 @@ class LocalMiniappRuntime {
|
|
|
5395
5481
|
for (const stream of cloudStreams) {
|
|
5396
5482
|
if (stream.startsWith("transcription:")) {
|
|
5397
5483
|
const lang = stream.substring("transcription:".length)
|
|
5484
|
+
if (hasAutoTranscription && lang !== "auto") continue
|
|
5398
5485
|
const source = langSource(lang)
|
|
5399
5486
|
subs.push({
|
|
5400
5487
|
kind: "transcription",
|
|
@@ -5527,6 +5614,12 @@ class LocalMiniappRuntime {
|
|
|
5527
5614
|
}
|
|
5528
5615
|
|
|
5529
5616
|
private handleTranscriptionData(d: TranscriptionData): void {
|
|
5617
|
+
const expectsAuto = [...(this.streamSubscribers.get("transcription:auto") ?? [])].some(
|
|
5618
|
+
(packageName) => this.appTranscriptionRoutesForSubscription(packageName, "transcription:auto").cloud,
|
|
5619
|
+
)
|
|
5620
|
+
// Subscription replacement is asynchronous. Ignore late results from the
|
|
5621
|
+
// old pipeline while switching between auto and language-specific jobs.
|
|
5622
|
+
if ((d.subscription.language.mode === "auto") !== expectsAuto) return
|
|
5530
5623
|
const receivedAt = Date.now()
|
|
5531
5624
|
if (TRANSCRIPT_TIMING_TELEMETRY) {
|
|
5532
5625
|
console.log(
|
|
@@ -5565,6 +5658,8 @@ class LocalMiniappRuntime {
|
|
|
5565
5658
|
__hostReceivedAt: receivedAt,
|
|
5566
5659
|
},
|
|
5567
5660
|
"cloud",
|
|
5661
|
+
undefined,
|
|
5662
|
+
d.subscription,
|
|
5568
5663
|
)
|
|
5569
5664
|
}
|
|
5570
5665
|
|
|
@@ -5642,8 +5737,7 @@ class LocalMiniappRuntime {
|
|
|
5642
5737
|
/** Model profile + runtime overrides (the G2 CFW scene canvas) for the default wearable. */
|
|
5643
5738
|
private currentCapabilities(): Capabilities {
|
|
5644
5739
|
const defaultWearable = useSettingsStore.getState().getSetting(ISLAND_SETTINGS_KEYS.defaultWearable) as
|
|
5645
|
-
|
|
|
5646
|
-
| undefined
|
|
5740
|
+
DeviceTypes | undefined
|
|
5647
5741
|
return getEffectiveCapabilities(defaultWearable || DeviceTypes.NONE)
|
|
5648
5742
|
}
|
|
5649
5743
|
|
|
@@ -5661,7 +5755,7 @@ class LocalMiniappRuntime {
|
|
|
5661
5755
|
// connected controllers add their input surfaces to `button`.
|
|
5662
5756
|
useGlassesStore.subscribe(
|
|
5663
5757
|
(s) =>
|
|
5664
|
-
`${s.sceneCanvasWidth ?? 0}x${s.sceneCanvasHeight ?? 0}:${s.sceneVector === true}:${s.firmwareVersion}:${s.connection.state}:${s.deviceModel}:${readyControllersKey(s.controllers)}`,
|
|
5758
|
+
`${s.sceneCanvasWidth ?? 0}x${s.sceneCanvasHeight ?? 0}:${s.sceneVector === true}:${s.sceneStereo === true}:${s.firmwareVersion}:${s.connection.state}:${s.deviceModel}:${readyControllersKey(s.controllers)}`,
|
|
5665
5759
|
broadcast,
|
|
5666
5760
|
)
|
|
5667
5761
|
// The debug knob flips hasNativeNavigation on custom firmware.
|
|
@@ -5720,28 +5814,33 @@ class LocalMiniappRuntime {
|
|
|
5720
5814
|
}
|
|
5721
5815
|
}
|
|
5722
5816
|
|
|
5723
|
-
/** Host user preferences miniapps follow by default
|
|
5817
|
+
/** Host user preferences miniapps follow by default: the metric toggle and the phone's own clock format. */
|
|
5724
5818
|
private currentPreferences(): MiniappPreferences {
|
|
5725
5819
|
const settings = useSettingsStore.getState()
|
|
5726
5820
|
return {
|
|
5727
5821
|
unitSystem: settings.getSetting(SETTINGS.metric_system.key) === true ? "metric" : "imperial",
|
|
5728
|
-
twelveHourTime:
|
|
5822
|
+
twelveHourTime: !uses24HourClock(),
|
|
5729
5823
|
}
|
|
5730
5824
|
}
|
|
5731
5825
|
|
|
5732
5826
|
private ensurePreferencesWired(): void {
|
|
5733
5827
|
if (this.preferencesWired) return
|
|
5734
5828
|
this.preferencesWired = true
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
()
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
}
|
|
5743
|
-
}
|
|
5744
|
-
|
|
5829
|
+
let sent = JSON.stringify(this.currentPreferences())
|
|
5830
|
+
const pushIfChanged = () => {
|
|
5831
|
+
const preferences = this.currentPreferences()
|
|
5832
|
+
const key = JSON.stringify(preferences)
|
|
5833
|
+
if (key === sent) return
|
|
5834
|
+
sent = key
|
|
5835
|
+
for (const packageName of this.handshookApps) {
|
|
5836
|
+
this.sendToMiniapp(packageName, {type: MiniappResponseType.PREFERENCES_UPDATE, preferences})
|
|
5837
|
+
}
|
|
5838
|
+
}
|
|
5839
|
+
useSettingsStore.subscribe((s) => s.getSetting(SETTINGS.metric_system.key) === true, pushIfChanged)
|
|
5840
|
+
// The clock format is an OS setting, so it can only have changed while the app was in the background.
|
|
5841
|
+
AppState.addEventListener("change", (state) => {
|
|
5842
|
+
if (state === "active") pushIfChanged()
|
|
5843
|
+
})
|
|
5745
5844
|
}
|
|
5746
5845
|
|
|
5747
5846
|
private currentCloudStatus(): MiniappCloudStatus {
|
|
@@ -5790,6 +5889,8 @@ class LocalMiniappRuntime {
|
|
|
5790
5889
|
transcriptionSource?: TranscriptionEventSource,
|
|
5791
5890
|
/** Deliver to this subscriber alone (the designated dashboard app's gestures while it shows). */
|
|
5792
5891
|
only?: string,
|
|
5892
|
+
/** Producing cloud/direct subscription; auto results also serve matching language listeners. */
|
|
5893
|
+
transcriptionSubscription?: TranscriptionData["subscription"],
|
|
5793
5894
|
): void {
|
|
5794
5895
|
// Paused miniapps (glasses disconnected) hear no streams. The resume push
|
|
5795
5896
|
// lands before the reconnect's own glasses_connection event, so nothing
|
|
@@ -5801,6 +5902,12 @@ class LocalMiniappRuntime {
|
|
|
5801
5902
|
if (transcriptionSource === "local") markTranscriptSourceRecent("local")
|
|
5802
5903
|
// Translate cloud event names to miniapp protocol stream types
|
|
5803
5904
|
const normalizedStream = this.normalizeStreamType(streamType)
|
|
5905
|
+
const transcriptionStreams =
|
|
5906
|
+
transcriptionSource === "cloud" && transcriptionSubscription
|
|
5907
|
+
? [...this.streamSubscribers.keys()].filter((stream) =>
|
|
5908
|
+
matchesTranscriptionSubscription(stream, normalizedStream, transcriptionSubscription),
|
|
5909
|
+
)
|
|
5910
|
+
: undefined
|
|
5804
5911
|
|
|
5805
5912
|
// Passive memory capture happens HERE, before subscriber fan-out, so the
|
|
5806
5913
|
// timeline reflects what was said around the user regardless of which
|
|
@@ -5809,10 +5916,10 @@ class LocalMiniappRuntime {
|
|
|
5809
5916
|
// recorded while it was open.)
|
|
5810
5917
|
this.captureTranscriptToMemory(normalizedStream, data)
|
|
5811
5918
|
|
|
5812
|
-
//
|
|
5813
|
-
//
|
|
5814
|
-
//
|
|
5815
|
-
//
|
|
5919
|
+
// Auto cloud/direct results serve auto listeners and listeners requesting
|
|
5920
|
+
// the detected language. Specific jobs serve only their own subscription.
|
|
5921
|
+
// Local/legacy events have no producing subscription and retain wildcard
|
|
5922
|
+
// matching so one on-device recognizer can serve auto and explicit listeners.
|
|
5816
5923
|
//
|
|
5817
5924
|
// Translation streams support cloud v3's wildcard patterns. Incoming
|
|
5818
5925
|
// events are keyed `translation:<source>:<target>` and we match against
|
|
@@ -5824,11 +5931,13 @@ class LocalMiniappRuntime {
|
|
|
5824
5931
|
// translation:auto — back-compat alias for *:*
|
|
5825
5932
|
const matchedSubs = new Set<string>()
|
|
5826
5933
|
let translationPatterns: string[] = []
|
|
5827
|
-
const
|
|
5828
|
-
|
|
5934
|
+
for (const stream of transcriptionStreams ?? [normalizedStream]) {
|
|
5935
|
+
const exact = this.streamSubscribers.get(stream)
|
|
5936
|
+
if (exact) for (const p of exact) matchedSubs.add(p)
|
|
5937
|
+
}
|
|
5829
5938
|
|
|
5830
5939
|
if (normalizedStream.startsWith("transcription:")) {
|
|
5831
|
-
const autoSubs = this.streamSubscribers.get("transcription:auto")
|
|
5940
|
+
const autoSubs = transcriptionStreams ? undefined : this.streamSubscribers.get("transcription:auto")
|
|
5832
5941
|
if (autoSubs) for (const p of autoSubs) matchedSubs.add(p)
|
|
5833
5942
|
} else if (normalizedStream.startsWith("translation:")) {
|
|
5834
5943
|
// translation:<source>:<target> — match each wildcard variant.
|
|
@@ -5907,8 +6016,18 @@ class LocalMiniappRuntime {
|
|
|
5907
6016
|
// The wearer's touches reach a widget-only app through its host's widget events alone.
|
|
5908
6017
|
if (normalizedStream === MiniappStreamType.TOUCH_EVENT && miniappWidgetMode.isWidgetOnly(packageName)) continue
|
|
5909
6018
|
let transcriptionRoute: TranscriptionDeliveryRoute | undefined
|
|
6019
|
+
let outboundStream = normalizedStream
|
|
5910
6020
|
if (normalizedStream.startsWith("transcription:") && transcriptionSource) {
|
|
5911
|
-
const
|
|
6021
|
+
const cloudStreams = transcriptionStreams?.filter(
|
|
6022
|
+
(stream) => this.appTranscriptionRoutesForSubscription(packageName, stream).cloud,
|
|
6023
|
+
)
|
|
6024
|
+
const routes = cloudStreams
|
|
6025
|
+
? {cloud: cloudStreams.length > 0, forceLocal: false}
|
|
6026
|
+
: this.appTranscriptionRoutesForEvent(packageName, normalizedStream)
|
|
6027
|
+
// Existing miniapp bundles dispatch explicit listeners by their BCP-47
|
|
6028
|
+
// subscription key, while providers detect bare codes (es vs es-ES).
|
|
6029
|
+
// Send one envelope per app, even when it also subscribes to auto.
|
|
6030
|
+
outboundStream = cloudStreams?.find((stream) => stream !== "transcription:auto") ?? normalizedStream
|
|
5912
6031
|
transcriptionRoute =
|
|
5913
6032
|
transcriptionDeliveryRoute(transcriptionSource, routes, this.transcriptionPrimaryUp()) ?? undefined
|
|
5914
6033
|
if (!transcriptionRoute) continue
|
|
@@ -5937,7 +6056,7 @@ class LocalMiniappRuntime {
|
|
|
5937
6056
|
}
|
|
5938
6057
|
this.sendToMiniapp(packageName, {
|
|
5939
6058
|
type: MiniappResponseType.EVENT,
|
|
5940
|
-
streamType:
|
|
6059
|
+
streamType: outboundStream,
|
|
5941
6060
|
data: outboundData,
|
|
5942
6061
|
...(transcriptionRoute ? {transcriptionRoute} : {}),
|
|
5943
6062
|
})
|
|
@@ -6233,6 +6352,7 @@ class LocalMiniappRuntime {
|
|
|
6233
6352
|
}
|
|
6234
6353
|
|
|
6235
6354
|
private miniappIcons = new MiniappIcons()
|
|
6355
|
+
private notificationSeq = 0
|
|
6236
6356
|
|
|
6237
6357
|
private async handleMiniappsList(
|
|
6238
6358
|
packageName: string,
|
|
@@ -6538,7 +6658,12 @@ class LocalMiniappRuntime {
|
|
|
6538
6658
|
// and an unplace racing it cannot stop the provider early. A provider the
|
|
6539
6659
|
// store already runs (a wearer's launch still spawning, say) stays a
|
|
6540
6660
|
// normal run; the registry alone lags that launch.
|
|
6541
|
-
this.widgetPlacements = placeWidget(this.widgetPlacements, {
|
|
6661
|
+
this.widgetPlacements = placeWidget(this.widgetPlacements, {
|
|
6662
|
+
hostPackageName,
|
|
6663
|
+
providerPackageName: provider,
|
|
6664
|
+
widgetId,
|
|
6665
|
+
box,
|
|
6666
|
+
})
|
|
6542
6667
|
miniappWidgetMode.setPlaced(provider, true)
|
|
6543
6668
|
if (!app.running && !miniappRunningRegistry.has(provider)) miniappWidgetMode.setWidgetOnly(provider, true)
|
|
6544
6669
|
try {
|
|
@@ -6575,7 +6700,10 @@ class LocalMiniappRuntime {
|
|
|
6575
6700
|
this.widgetPlacements = unplaceWidget(this.widgetPlacements, hostPackageName, provider, target.widgetId)
|
|
6576
6701
|
this.sendResult(hostPackageName, requestId, true)
|
|
6577
6702
|
if (!had) return
|
|
6578
|
-
if (
|
|
6703
|
+
if (
|
|
6704
|
+
this.connectedApps.has(provider) &&
|
|
6705
|
+
widgetHostsOf(this.widgetPlacements, provider, target.widgetId).length === 0
|
|
6706
|
+
) {
|
|
6579
6707
|
this.sendWidgetCall(provider, target.widgetId, {kind: "box", box: null})
|
|
6580
6708
|
}
|
|
6581
6709
|
await this.settleWidgetProvider(provider)
|
|
@@ -6706,9 +6834,7 @@ class LocalMiniappRuntime {
|
|
|
6706
6834
|
if (!widget) return
|
|
6707
6835
|
const view: WidgetView = payload.view === "expanded" ? "expanded" : "card"
|
|
6708
6836
|
if (view === "expanded" && !widget.expandable) {
|
|
6709
|
-
console.warn(
|
|
6710
|
-
`${LOG_TAG}: dropping expanded frame from ${providerPackageName}/${widgetId}: it does not list EX`,
|
|
6711
|
-
)
|
|
6837
|
+
console.warn(`${LOG_TAG}: dropping expanded frame from ${providerPackageName}/${widgetId}: it does not list EX`)
|
|
6712
6838
|
return
|
|
6713
6839
|
}
|
|
6714
6840
|
const cap =
|
|
@@ -6733,8 +6859,7 @@ class LocalMiniappRuntime {
|
|
|
6733
6859
|
|
|
6734
6860
|
/** A miniapp disconnected: as a host its placements go, as a provider its boxes are forgotten and its frames kept. */
|
|
6735
6861
|
private onWidgetPartyGone(packageName: string): void {
|
|
6736
|
-
|
|
6737
|
-
this.widgetPlacements = dropWidgetHost(this.widgetPlacements, packageName)
|
|
6862
|
+
this.releaseWidgetHost(packageName)
|
|
6738
6863
|
this.widgetPlacements = dropWidgetProvider(this.widgetPlacements, packageName)
|
|
6739
6864
|
miniappWidgetMode.setPlaced(packageName, false)
|
|
6740
6865
|
miniappWidgetMode.setWidgetOnly(packageName, false)
|
|
@@ -6749,8 +6874,13 @@ class LocalMiniappRuntime {
|
|
|
6749
6874
|
})
|
|
6750
6875
|
}
|
|
6751
6876
|
}
|
|
6877
|
+
}
|
|
6878
|
+
|
|
6879
|
+
public releaseWidgetHost(hostPackageName: string): void {
|
|
6880
|
+
const providersBefore = placedProviders(this.widgetPlacements)
|
|
6881
|
+
this.widgetPlacements = dropWidgetHost(this.widgetPlacements, hostPackageName)
|
|
6752
6882
|
for (const provider of providersBefore) {
|
|
6753
|
-
if (provider !==
|
|
6883
|
+
if (provider !== hostPackageName) void this.settleWidgetProvider(provider)
|
|
6754
6884
|
}
|
|
6755
6885
|
}
|
|
6756
6886
|
|
|
@@ -28,11 +28,7 @@ import sttModelManager from "./STTModelManager"
|
|
|
28
28
|
* `force_local` (a forced subscription would keep local STT on either way).
|
|
29
29
|
*/
|
|
30
30
|
export type LocalSttFallbackReason =
|
|
31
|
-
| "
|
|
32
|
-
| "cloud_disconnected"
|
|
33
|
-
| "cloud_disabled"
|
|
34
|
-
| "stt_quota"
|
|
35
|
-
| "provider_unreachable"
|
|
31
|
+
"force_local" | "cloud_disconnected" | "cloud_disabled" | "stt_quota" | "provider_unreachable"
|
|
36
32
|
|
|
37
33
|
class LocalSttFallbackCoordinator {
|
|
38
34
|
private static instance: LocalSttFallbackCoordinator
|
|
@@ -61,8 +61,7 @@ class NativeNavigationService {
|
|
|
61
61
|
*/
|
|
62
62
|
public isSupported(): boolean {
|
|
63
63
|
const defaultWearable = useSettingsStore.getState().getSetting(ISLAND_SETTINGS_KEYS.defaultWearable) as
|
|
64
|
-
|
|
|
65
|
-
| undefined
|
|
64
|
+
DeviceTypes | undefined
|
|
66
65
|
// Effective, not model: custom firmware drops hasNativeNavigation so the
|
|
67
66
|
// miniapp's scene HUD owns the display (see effectiveCapabilities).
|
|
68
67
|
const capabilities = getEffectiveCapabilities(defaultWearable || DeviceTypes.NONE)
|
|
@@ -364,7 +364,7 @@ export class NavigationHandlers {
|
|
|
364
364
|
|
|
365
365
|
async handleComputeRoute(packageName: string, payload: Record<string, unknown>, requestId?: string): Promise<void> {
|
|
366
366
|
try {
|
|
367
|
-
// With the user's own Mapbox key (Settings > API Keys), compute
|
|
367
|
+
// With the user's own Mapbox key (Settings > Privacy > API Keys), compute
|
|
368
368
|
// on-device so routing works without the cloud and bills their account.
|
|
369
369
|
// Without one, go through the v2 cloud maps proxy — org-token Mapbox
|
|
370
370
|
// usage only ever happens cloud-side.
|
|
@@ -437,7 +437,7 @@ export class NavigationHandlers {
|
|
|
437
437
|
|
|
438
438
|
/**
|
|
439
439
|
* Type-ahead place search. Runs on-device against Mapbox Search Box when the
|
|
440
|
-
* user saved their own key (Settings > API Keys) — that key exists precisely
|
|
440
|
+
* user saved their own key (Settings > Privacy > API Keys) — that key exists precisely
|
|
441
441
|
* so search works without the cloud — and falls back to the v2 cloud maps
|
|
442
442
|
* service otherwise. Resolves `{ok: true, suggestions}` — an empty query or no
|
|
443
443
|
* matches is `{ok: true, suggestions: []}`, not an error.
|
|
@@ -211,7 +211,7 @@ class NavigationService {
|
|
|
211
211
|
const mapboxRenderToken = await resolveMapboxRenderToken()
|
|
212
212
|
if (!mapboxRenderToken) {
|
|
213
213
|
const error =
|
|
214
|
-
"No Mapbox token available — add your key in Settings > API Keys" +
|
|
214
|
+
"No Mapbox token available — add your key in Settings > Privacy > API Keys" +
|
|
215
215
|
" (required while Glassly Cloud is disabled or unreachable)"
|
|
216
216
|
console.warn(`${LOG_TAG}: start blocked — ${error}`)
|
|
217
217
|
return {ok: false, error}
|
|
@@ -329,7 +329,7 @@ class NavigationService {
|
|
|
329
329
|
* Compute one or more routes without starting a trip.
|
|
330
330
|
*
|
|
331
331
|
* Reached from the miniapp bridge (NavigationHandlers.handleComputeRoute)
|
|
332
|
-
* when the user has saved their own Mapbox key in Settings > API Keys —
|
|
332
|
+
* when the user has saved their own Mapbox key in Settings > Privacy > API Keys —
|
|
333
333
|
* that key exists so navigation works without the cloud. Without a user
|
|
334
334
|
* key the bridge uses the v2 cloud maps service, which holds a provider
|
|
335
335
|
* token server-side and caches results. Implemented by
|
|
@@ -379,7 +379,7 @@ class NavigationService {
|
|
|
379
379
|
return !!resolveMapboxAccessToken()
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
-
/** True when the user saved their own Mapbox key in Settings > API Keys. */
|
|
382
|
+
/** True when the user saved their own Mapbox key in Settings > Privacy > API Keys. */
|
|
383
383
|
public hasUserMapboxKey(): boolean {
|
|
384
384
|
return !!userMapboxApiKey()
|
|
385
385
|
}
|
|
@@ -501,7 +501,7 @@ export default navigationService
|
|
|
501
501
|
// ---------------------------------------------------------------------------
|
|
502
502
|
|
|
503
503
|
// Routes + Geocoding go DIRECT to Mapbox from the phone process ONLY when the
|
|
504
|
-
// user saved their own key (Settings > API Keys) — "use my account". Without
|
|
504
|
+
// user saved their own key (Settings > Privacy > API Keys) — "use my account". Without
|
|
505
505
|
// one, callers (NavigationHandlers) route these operations through the v2
|
|
506
506
|
// cloud maps proxy instead; the binary ships no baked Mapbox key.
|
|
507
507
|
const MAPBOX_DIRECTIONS_BASE = "https://api.mapbox.com/directions/v5/mapbox"
|
|
@@ -532,7 +532,7 @@ function mapboxToken(): string {
|
|
|
532
532
|
|
|
533
533
|
/**
|
|
534
534
|
* Phone-side helper: hit the Mapbox Directions API directly with the user's own
|
|
535
|
-
* key (Settings > API Keys). Preferred over the v2 cloud maps service whenever
|
|
535
|
+
* key (Settings > Privacy > API Keys). Preferred over the v2 cloud maps service whenever
|
|
536
536
|
* that key is set, so routing works without the cloud. Lives here
|
|
537
537
|
* (not in Kotlin) so it's platform-independent across the JS bridge.
|
|
538
538
|
* Returns the SDK-shaped result with primary + alternates.
|
|
@@ -583,7 +583,8 @@ async function computeRouteViaRoutesApi(payload: Record<string, unknown>): Promi
|
|
|
583
583
|
}
|
|
584
584
|
|
|
585
585
|
const token = mapboxToken()
|
|
586
|
-
if (!token)
|
|
586
|
+
if (!token)
|
|
587
|
+
return {ok: false, error: "computeRoute: no Mapbox key saved (add yours in Settings > Privacy > API Keys)"}
|
|
587
588
|
|
|
588
589
|
// Mapbox path is a semicolon-joined `lng,lat` list: origin then each stop.
|
|
589
590
|
const coords = [`${oLng},${oLat}`, ...stops.map((s) => `${s.lng},${s.lat}`)].join(";")
|
|
@@ -739,7 +740,7 @@ function mapboxManeuverToKind(type?: string, modifier?: string): string | undefi
|
|
|
739
740
|
|
|
740
741
|
/**
|
|
741
742
|
* Reverse-geocode a coordinate via the Mapbox Geocoding v6 API directly, using
|
|
742
|
-
* the user's own key (Settings > API Keys). Preferred over the v2 cloud maps
|
|
743
|
+
* the user's own key (Settings > Privacy > API Keys). Preferred over the v2 cloud maps
|
|
743
744
|
* service whenever that key is set, so pin labels work without the cloud.
|
|
744
745
|
*
|
|
745
746
|
* Returns the road/street name (e.g. "Octavia Blvd") plus the full formatted
|
|
@@ -767,7 +768,7 @@ async function reverseGeocodeRoadViaGeocodingApi(coord: {lat: number; lng: numbe
|
|
|
767
768
|
if (!token)
|
|
768
769
|
return {
|
|
769
770
|
ok: false,
|
|
770
|
-
error: "reverseGeocode: no Mapbox key saved (add yours in Settings > API Keys)",
|
|
771
|
+
error: "reverseGeocode: no Mapbox key saved (add yours in Settings > Privacy > API Keys)",
|
|
771
772
|
}
|
|
772
773
|
const params = new URLSearchParams({
|
|
773
774
|
longitude: String(coord.lng),
|
|
@@ -813,7 +814,7 @@ async function reverseGeocodeRoadViaGeocodingApi(coord: {lat: number; lng: numbe
|
|
|
813
814
|
|
|
814
815
|
/**
|
|
815
816
|
* Type-ahead place search straight from the device via Mapbox Search Box,
|
|
816
|
-
* using the user's own key (Settings > API Keys). This is the offline-from-
|
|
817
|
+
* using the user's own key (Settings > Privacy > API Keys). This is the offline-from-
|
|
817
818
|
* the-cloud path: with a key saved, place search works with no runtime
|
|
818
819
|
* involvement at all.
|
|
819
820
|
*
|
|
@@ -840,7 +841,7 @@ async function placeAutocompleteViaSearchBox(
|
|
|
840
841
|
if (!token) {
|
|
841
842
|
return {
|
|
842
843
|
ok: false,
|
|
843
|
-
error: "placeAutocomplete: no Mapbox key saved (add yours in Settings > API Keys)",
|
|
844
|
+
error: "placeAutocomplete: no Mapbox key saved (add yours in Settings > Privacy > API Keys)",
|
|
844
845
|
}
|
|
845
846
|
}
|
|
846
847
|
const params = new URLSearchParams({
|
|
@@ -904,7 +905,7 @@ async function placeDetailsViaSearchBox(
|
|
|
904
905
|
if (!token) {
|
|
905
906
|
return {
|
|
906
907
|
ok: false,
|
|
907
|
-
error: "placeDetails: no Mapbox key saved (add yours in Settings > API Keys)",
|
|
908
|
+
error: "placeDetails: no Mapbox key saved (add yours in Settings > Privacy > API Keys)",
|
|
908
909
|
}
|
|
909
910
|
}
|
|
910
911
|
const params = new URLSearchParams({access_token: token, session_token: sessionToken})
|
|
@@ -40,9 +40,7 @@ import {useSettingsStore, SETTINGS} from "../stores/settings"
|
|
|
40
40
|
import {DeviceTypes} from "../types"
|
|
41
41
|
|
|
42
42
|
export type IdentitySnapshot =
|
|
43
|
-
| {kind: "
|
|
44
|
-
| {kind: "pending"; model: string}
|
|
45
|
-
| {kind: "paired"; model: string; name: string; address?: string}
|
|
43
|
+
{kind: "none"} | {kind: "pending"; model: string} | {kind: "paired"; model: string; name: string; address?: string}
|
|
46
44
|
|
|
47
45
|
// Identity keys hold model/name strings; anything else (legacy null, a stray
|
|
48
46
|
// non-string) reads as absent, matching native currentDefaultDevice()'s
|
|
@@ -14,9 +14,7 @@ import {
|
|
|
14
14
|
|
|
15
15
|
export {PhoneCalendarError} from "./PhoneCalendarHelpers"
|
|
16
16
|
|
|
17
|
-
type CalendarPermissionStatus = Awaited<
|
|
18
|
-
ReturnType<typeof Calendar.getCalendarPermissionsAsync>
|
|
19
|
-
>["status"]
|
|
17
|
+
type CalendarPermissionStatus = Awaited<ReturnType<typeof Calendar.getCalendarPermissionsAsync>>["status"]
|
|
20
18
|
|
|
21
19
|
/**
|
|
22
20
|
* expo-calendar reports anything below EventKit full access as not granted,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {notificationAppPayload} from "./notificationAppPayload"
|
|
1
2
|
/**
|
|
2
3
|
* Phone-notifications sync — engine-owned, started by `engine.start()`. Keeps
|
|
3
4
|
* three things in step with settings and the connected glasses, for any host:
|
|
@@ -78,7 +79,7 @@ export function getKnownNotificationApps(): NotificationApp[] {
|
|
|
78
79
|
export function installedNotificationApps(): Promise<
|
|
79
80
|
Array<{packageName: string; appName: string; icon: string | null}>
|
|
80
81
|
> {
|
|
81
|
-
if (Platform.OS === "android") return CrustModule.getInstalledApps()
|
|
82
|
+
if (Platform.OS === "android") return CrustModule.getInstalledApps().then(notificationAppPayload)
|
|
82
83
|
if (Platform.OS !== "ios") return Promise.resolve([])
|
|
83
84
|
return Promise.resolve(
|
|
84
85
|
getKnownNotificationApps().map((app) => ({packageName: app.appId, appName: app.displayName, icon: null})),
|
|
@@ -339,8 +339,7 @@ export class PhoneStreamCoordinator {
|
|
|
339
339
|
// block subsequent start/stop transitions on this coordinator.
|
|
340
340
|
assertGlassesConnected()
|
|
341
341
|
type JoinDecision =
|
|
342
|
-
|
|
343
|
-
| {kind: "fresh"; entry: ManagedEntry}
|
|
342
|
+
{kind: "join"; entry: ManagedEntry; immediate: ManagedStartResult | null} | {kind: "fresh"; entry: ManagedEntry}
|
|
344
343
|
|
|
345
344
|
const decision = await this.runExclusive(async (): Promise<JoinDecision> => {
|
|
346
345
|
if (this.current && this.current.kind === "unmanaged") {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* ProviderKeyValidation
|
|
3
3
|
*
|
|
4
4
|
* Cheap validate-before-save probes for the AI/voice provider keys on the
|
|
5
|
-
* Keys screen (Settings > API Keys), mirroring validateMapboxApiKey's shape:
|
|
5
|
+
* Keys screen (Settings > Privacy > API Keys), mirroring validateMapboxApiKey's shape:
|
|
6
6
|
* each probe hits the provider's cheapest authenticated read endpoint (no
|
|
7
7
|
* metered usage), maps auth failures to `invalid_key`, and maps everything
|
|
8
8
|
* else to `network` so the caller can offer "save anyway".
|
|
@@ -151,12 +151,7 @@ class STTModelManager {
|
|
|
151
151
|
fileName: "sherpa-onnx-nemo-streaming-fast-conformer-transducer-en-480ms-int8",
|
|
152
152
|
size: 101 * 1024 * 1024,
|
|
153
153
|
type: "transducer",
|
|
154
|
-
requiredFiles: [
|
|
155
|
-
"encoder.int8.onnx",
|
|
156
|
-
"decoder.int8.onnx",
|
|
157
|
-
"joiner.int8.onnx",
|
|
158
|
-
"tokens.txt",
|
|
159
|
-
],
|
|
154
|
+
requiredFiles: ["encoder.int8.onnx", "decoder.int8.onnx", "joiner.int8.onnx", "tokens.txt"],
|
|
160
155
|
languageCode: "en-US",
|
|
161
156
|
},
|
|
162
157
|
// NVIDIA Nemotron streaming ASR. These are model choices rather than plain
|