@glassly/engine 0.3.5 → 0.3.6-dev.846
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 +11 -1
- package/build/engine.d.ts.map +1 -1
- package/build/engine.js +13 -1
- package/build/engine.js.map +1 -1
- package/build/facades/ai.d.ts +3 -3
- package/build/facades/ai.d.ts.map +1 -1
- package/build/facades/ai.js +3 -3
- package/build/facades/ai.js.map +1 -1
- package/build/facades/glasses.d.ts +5 -1
- package/build/facades/glasses.d.ts.map +1 -1
- package/build/facades/glasses.js +39 -30
- package/build/facades/glasses.js.map +1 -1
- package/build/facades/memory.d.ts +10 -2
- package/build/facades/memory.d.ts.map +1 -1
- package/build/facades/memory.js +10 -0
- package/build/facades/memory.js.map +1 -1
- package/build/facades/miniapps.d.ts +2 -0
- package/build/facades/miniapps.d.ts.map +1 -1
- package/build/facades/miniapps.js +2 -0
- package/build/facades/miniapps.js.map +1 -1
- package/build/facades/pairing.d.ts.map +1 -1
- package/build/facades/pairing.js +3 -2
- package/build/facades/pairing.js.map +1 -1
- package/build/facades/providerKeys.d.ts +6 -2
- package/build/facades/providerKeys.d.ts.map +1 -1
- package/build/facades/providerKeys.js +6 -2
- package/build/facades/providerKeys.js.map +1 -1
- package/build/generated/releaseMetadata.js +4 -4
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/services/AiTierResolution.d.ts +3 -2
- package/build/services/AiTierResolution.d.ts.map +1 -1
- package/build/services/AiTierResolution.js +7 -2
- package/build/services/AiTierResolution.js.map +1 -1
- package/build/services/AppRegistry.d.ts +4 -1
- package/build/services/AppRegistry.d.ts.map +1 -1
- package/build/services/AppRegistry.js +16 -2
- package/build/services/AppRegistry.js.map +1 -1
- package/build/services/BlobStore.js +3 -3
- package/build/services/BlobStore.js.map +1 -1
- package/build/services/BluetoothRestoreLaunch.d.ts +8 -0
- package/build/services/BluetoothRestoreLaunch.d.ts.map +1 -0
- package/build/services/BluetoothRestoreLaunch.js +34 -0
- package/build/services/BluetoothRestoreLaunch.js.map +1 -0
- package/build/services/CloudClientService.d.ts +4 -4
- package/build/services/ForegroundLocationPermission.d.ts +1 -1
- package/build/services/ForegroundLocationPermission.d.ts.map +1 -1
- package/build/services/ForegroundLocationPermission.js.map +1 -1
- package/build/services/GestureDotsService.d.ts.map +1 -1
- package/build/services/GestureDotsService.js +2 -1
- package/build/services/GestureDotsService.js.map +1 -1
- package/build/services/GlassesTouchInput.d.ts +6 -4
- package/build/services/GlassesTouchInput.d.ts.map +1 -1
- package/build/services/GlassesTouchInput.js +81 -41
- package/build/services/GlassesTouchInput.js.map +1 -1
- package/build/services/LocalDisplayManager.d.ts +12 -4
- package/build/services/LocalDisplayManager.d.ts.map +1 -1
- package/build/services/LocalDisplayManager.js +21 -7
- package/build/services/LocalDisplayManager.js.map +1 -1
- package/build/services/LocalLlmModelManager.d.ts +3 -1
- package/build/services/LocalLlmModelManager.d.ts.map +1 -1
- package/build/services/LocalLlmModelManager.js +13 -5
- package/build/services/LocalLlmModelManager.js.map +1 -1
- package/build/services/LocalLlmPrompt.d.ts +10 -9
- package/build/services/LocalLlmPrompt.d.ts.map +1 -1
- package/build/services/LocalLlmPrompt.js +43 -32
- package/build/services/LocalLlmPrompt.js.map +1 -1
- package/build/services/LocalLlmProvider.d.ts +1 -0
- package/build/services/LocalLlmProvider.d.ts.map +1 -1
- package/build/services/LocalLlmProvider.js +62 -22
- package/build/services/LocalLlmProvider.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +28 -6
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +190 -148
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/MemoryStore.d.ts +13 -1
- package/build/services/MemoryStore.d.ts.map +1 -1
- package/build/services/MemoryStore.js +98 -1
- package/build/services/MemoryStore.js.map +1 -1
- package/build/services/MiniappExitGesture.d.ts +6 -3
- package/build/services/MiniappExitGesture.d.ts.map +1 -1
- package/build/services/MiniappExitGesture.js +4 -4
- package/build/services/MiniappExitGesture.js.map +1 -1
- package/build/services/MiniappWidgetMode.d.ts +6 -4
- package/build/services/MiniappWidgetMode.d.ts.map +1 -1
- package/build/services/MiniappWidgetMode.js +7 -6
- package/build/services/MiniappWidgetMode.js.map +1 -1
- package/build/services/ProviderKeyValidation.d.ts +2 -0
- package/build/services/ProviderKeyValidation.d.ts.map +1 -1
- package/build/services/ProviderKeyValidation.js +4 -0
- package/build/services/ProviderKeyValidation.js.map +1 -1
- package/build/services/SceneRenderer.d.ts.map +1 -1
- package/build/services/SceneRenderer.js +3 -1
- package/build/services/SceneRenderer.js.map +1 -1
- package/build/services/ScreenshotFixture.d.ts +56 -0
- package/build/services/ScreenshotFixture.d.ts.map +1 -0
- package/build/services/ScreenshotFixture.js +72 -0
- package/build/services/ScreenshotFixture.js.map +1 -0
- package/build/services/actionInvoke.d.ts +37 -0
- package/build/services/actionInvoke.d.ts.map +1 -0
- package/build/services/actionInvoke.js +89 -0
- package/build/services/actionInvoke.js.map +1 -0
- package/build/services/appleWatch/AppleWatchLink.d.ts +42 -0
- package/build/services/appleWatch/AppleWatchLink.d.ts.map +1 -0
- package/build/services/appleWatch/AppleWatchLink.js +259 -0
- package/build/services/appleWatch/AppleWatchLink.js.map +1 -0
- package/build/services/controllers.d.ts.map +1 -1
- package/build/services/controllers.js +16 -0
- package/build/services/controllers.js.map +1 -1
- package/build/services/manifestActions.d.ts +4 -2
- package/build/services/manifestActions.d.ts.map +1 -1
- package/build/services/manifestActions.js +9 -3
- package/build/services/manifestActions.js.map +1 -1
- package/build/services/memoryBackupFile.d.ts +9 -0
- package/build/services/memoryBackupFile.d.ts.map +1 -0
- package/build/services/memoryBackupFile.js +47 -0
- package/build/services/memoryBackupFile.js.map +1 -0
- package/build/services/settingsBridge.d.ts +4 -4
- package/build/services/settingsBridge.d.ts.map +1 -1
- package/build/services/settingsBridge.js +9 -4
- package/build/services/settingsBridge.js.map +1 -1
- package/build/services/widgetPlacements.d.ts +8 -7
- package/build/services/widgetPlacements.d.ts.map +1 -1
- package/build/services/widgetPlacements.js +7 -6
- package/build/services/widgetPlacements.js.map +1 -1
- package/build/stores/apps.d.ts +6 -0
- package/build/stores/apps.d.ts.map +1 -1
- package/build/stores/apps.js +29 -12
- package/build/stores/apps.js.map +1 -1
- package/build/stores/bluetoothSettingKeys.d.ts +2 -0
- package/build/stores/bluetoothSettingKeys.d.ts.map +1 -1
- package/build/stores/bluetoothSettingKeys.js +17 -0
- package/build/stores/bluetoothSettingKeys.js.map +1 -1
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +42 -2
- package/build/stores/settings.js.map +1 -1
- package/build/types/applet.d.ts +5 -1
- package/build/types/applet.d.ts.map +1 -1
- package/build/types/applet.js.map +1 -1
- package/build/types/capabilities/rayneo-io.d.ts.map +1 -1
- package/build/types/capabilities/rayneo-io.js +2 -0
- package/build/types/capabilities/rayneo-io.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/cloudClient/RnUdpAdapter.d.ts.map +1 -1
- package/build/utils/cloudClient/RnUdpAdapter.js +13 -2
- package/build/utils/cloudClient/RnUdpAdapter.js.map +1 -1
- package/build/utils/display/scene/index.d.ts +1 -1
- package/build/utils/display/scene/index.d.ts.map +1 -1
- package/build/utils/display/scene/index.js.map +1 -1
- package/build/utils/display/scene/process.d.ts.map +1 -1
- package/build/utils/display/scene/process.js +20 -1
- package/build/utils/display/scene/process.js.map +1 -1
- package/build/utils/display/scene/types.d.ts +1 -1
- package/build/utils/display/scene/types.d.ts.map +1 -1
- package/build/utils/display/scene/types.js.map +1 -1
- package/build/utils/miniappGlobals.d.ts +6 -0
- package/build/utils/miniappGlobals.d.ts.map +1 -1
- package/build/utils/miniappGlobals.js +28 -4
- package/build/utils/miniappGlobals.js.map +1 -1
- package/package.json +9 -9
- package/src/engine.ts +13 -1
- package/src/facades/ai.ts +3 -3
- package/src/facades/glasses.ts +65 -42
- package/src/facades/memory.ts +11 -2
- package/src/facades/miniapps.ts +3 -0
- package/src/facades/pairing.ts +4 -2
- package/src/facades/providerKeys.ts +6 -1
- package/src/generated/releaseMetadata.ts +4 -4
- package/src/services/AiTierResolution.ts +9 -3
- package/src/services/AppRegistry.ts +18 -3
- package/src/services/BlobStore.ts +3 -3
- package/src/services/BluetoothRestoreLaunch.ts +37 -0
- package/src/services/ForegroundLocationPermission.ts +1 -1
- package/src/services/GestureDotsService.ts +2 -1
- package/src/services/GlassesTouchInput.ts +86 -38
- package/src/services/LocalDisplayManager.ts +22 -8
- package/src/services/LocalLlmModelManager.ts +16 -6
- package/src/services/LocalLlmPrompt.ts +56 -37
- package/src/services/LocalLlmProvider.ts +74 -24
- package/src/services/LocalMiniappRuntime.ts +228 -147
- package/src/services/MemoryStore.ts +111 -2
- package/src/services/MiniappExitGesture.ts +8 -5
- package/src/services/MiniappWidgetMode.ts +9 -7
- package/src/services/ProviderKeyValidation.ts +5 -0
- package/src/services/SceneRenderer.ts +3 -1
- package/src/services/ScreenshotFixture.ts +113 -0
- package/src/services/actionInvoke.ts +105 -0
- package/src/services/appleWatch/AppleWatchLink.ts +297 -0
- package/src/services/controllers.ts +17 -0
- package/src/services/manifestActions.ts +16 -4
- package/src/services/memoryBackupFile.ts +50 -0
- package/src/services/settingsBridge.ts +9 -4
- package/src/services/widgetPlacements.ts +23 -9
- package/src/stores/apps.ts +35 -11
- package/src/stores/bluetoothSettingKeys.ts +20 -0
- package/src/stores/settings.ts +44 -2
- package/src/types/applet.ts +5 -1
- package/src/types/capabilities/rayneo-io.ts +2 -0
- package/src/types/enums.ts +1 -0
- package/src/utils/cloudClient/RnUdpAdapter.ts +13 -1
- package/src/utils/display/scene/index.ts +1 -0
- package/src/utils/display/scene/process.ts +17 -1
- package/src/utils/display/scene/types.ts +1 -0
- package/src/utils/miniappGlobals.ts +30 -4
package/src/facades/ai.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* ai facade — `engine.ai.local`: the on-device LLM model that serves
|
|
3
3
|
* session.ai calls routed to the phone (forceLocal / localFallback). Thin
|
|
4
4
|
* passthrough over LocalLlmModelManager and LocalLlmProvider so Settings >
|
|
5
|
-
*
|
|
6
|
-
* into engine internals.
|
|
5
|
+
* Offline Models can list, pick, download, and delete models without
|
|
6
|
+
* reaching into engine internals.
|
|
7
7
|
*/
|
|
8
8
|
import localLlmModelManager, {
|
|
9
9
|
LocalLlmModelManager,
|
|
@@ -17,7 +17,7 @@ export const ai = {
|
|
|
17
17
|
local: {
|
|
18
18
|
/** The catalog, in dropdown order. */
|
|
19
19
|
models: (): LocalLlmModelConfig[] => localLlmModelManager.models(),
|
|
20
|
-
/** The Settings >
|
|
20
|
+
/** The Settings > Offline Models pick. */
|
|
21
21
|
selectedModelId: (): LocalLlmModelId => localLlmModelManager.selectedModelId(),
|
|
22
22
|
select: (id: LocalLlmModelId): Promise<void> => localLlmModelManager.select(id),
|
|
23
23
|
isDownloaded: (id?: LocalLlmModelId): Promise<boolean> => localLlmModelManager.isModelAvailable(id),
|
package/src/facades/glasses.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
// standalone build); jest moduleNameMapper + tsconfig both resolve it.
|
|
13
13
|
import BluetoothSdk from "@glassly/bluetooth-sdk/internal"
|
|
14
14
|
import type {
|
|
15
|
+
AppleWatchAvailability,
|
|
15
16
|
ButtonPressEvent,
|
|
16
17
|
ConnectOptions,
|
|
17
18
|
ControllerStatus,
|
|
@@ -26,6 +27,13 @@ import {useSettingsStore, SETTINGS} from "../stores/settings"
|
|
|
26
27
|
import {hasDefaultDevice} from "../services/DeviceStoreHydration"
|
|
27
28
|
import micStateCoordinator from "../services/MicStateCoordinator"
|
|
28
29
|
import {projectPairingIdentity} from "../services/PairingIdentity"
|
|
30
|
+
import {
|
|
31
|
+
fixtureControllerList,
|
|
32
|
+
fixtureControllerStatuses,
|
|
33
|
+
fixtureInfo,
|
|
34
|
+
fixtureStatus,
|
|
35
|
+
isScreenshotMode,
|
|
36
|
+
} from "../services/ScreenshotFixture"
|
|
29
37
|
import {isGlassesConnected, isGlassesReady} from "../services/GlassesReadiness"
|
|
30
38
|
import {pushAllBluetoothSettings} from "../services/GlassesSettingsSync"
|
|
31
39
|
import {isControllerReady, pairedControllersFrom, readyControllers} from "../services/controllers"
|
|
@@ -42,25 +50,33 @@ import {glassesSettings} from "./glassesSettings"
|
|
|
42
50
|
|
|
43
51
|
let micLevelSubscriptions = 0
|
|
44
52
|
|
|
53
|
+
// The screenshot fixture (ScreenshotFixture.ts) dresses these read-models
|
|
54
|
+
// while the identity is the simulated device; it is a no-op outside that build.
|
|
45
55
|
function projectStatus() {
|
|
46
56
|
const s = useGlassesStore.getState()
|
|
47
|
-
return
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
return fixtureStatus(
|
|
58
|
+
{
|
|
59
|
+
state: s.connection.state,
|
|
60
|
+
fullyBooted: isGlassesReady(s.connection),
|
|
61
|
+
battery: s.batteryLevel,
|
|
62
|
+
charging: s.charging,
|
|
63
|
+
case: {battery: s.caseBatteryLevel, charging: s.caseCharging, open: s.caseOpen, removed: s.caseRemoved},
|
|
64
|
+
signal: s.signalStrength,
|
|
65
|
+
micEnabled: s.micEnabled,
|
|
66
|
+
vadEnabled: s.voiceActivityDetectionEnabled,
|
|
67
|
+
btClassic: s.bluetoothClassicConnected,
|
|
68
|
+
// iOS: glasses may read the phone's ANCS stream ("Share System Notifications").
|
|
69
|
+
ancsAuthorized: Boolean(s.ancsAuthorized),
|
|
70
|
+
},
|
|
71
|
+
projectPairingIdentity(),
|
|
72
|
+
)
|
|
60
73
|
}
|
|
61
74
|
|
|
62
75
|
function projectControllerList(): PairedController[] {
|
|
63
|
-
return
|
|
76
|
+
return fixtureControllerList(
|
|
77
|
+
pairedControllersFrom(useSettingsStore.getState().getSetting(SETTINGS.controllers.key)),
|
|
78
|
+
projectPairingIdentity(),
|
|
79
|
+
)
|
|
64
80
|
}
|
|
65
81
|
|
|
66
82
|
// Copies: snapshots must not hand callers mutable references into the store.
|
|
@@ -69,7 +85,27 @@ function projectControllerStatuses(): Record<string, ControllerStatus> {
|
|
|
69
85
|
for (const [id, status] of Object.entries(useGlassesStore.getState().controllers ?? {})) {
|
|
70
86
|
statuses[id] = {...status}
|
|
71
87
|
}
|
|
72
|
-
return statuses
|
|
88
|
+
return fixtureControllerStatuses(statuses, projectPairingIdentity())
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Dedupe on the projected snapshot: the store updates on many fields; fire
|
|
92
|
+
// only when the projection changes. The fixture's read-models also follow the
|
|
93
|
+
// identity (a settings key), so that build watches the settings store too.
|
|
94
|
+
function subscribeProjection<T>(project: () => T, cb: (snapshot: T) => void): () => void {
|
|
95
|
+
let last = JSON.stringify(project())
|
|
96
|
+
const notify = () => {
|
|
97
|
+
const snap = project()
|
|
98
|
+
const key = JSON.stringify(snap)
|
|
99
|
+
if (key === last) return
|
|
100
|
+
last = key
|
|
101
|
+
cb(snap)
|
|
102
|
+
}
|
|
103
|
+
const unsubscribeGlasses = useGlassesStore.subscribe(notify)
|
|
104
|
+
const unsubscribeSettings = isScreenshotMode() ? useSettingsStore.subscribe(notify) : () => {}
|
|
105
|
+
return () => {
|
|
106
|
+
unsubscribeGlasses()
|
|
107
|
+
unsubscribeSettings()
|
|
108
|
+
}
|
|
73
109
|
}
|
|
74
110
|
|
|
75
111
|
function projectKeyboardStatus() {
|
|
@@ -88,6 +124,10 @@ function projectKeyboardStatus() {
|
|
|
88
124
|
|
|
89
125
|
function projectInfo() {
|
|
90
126
|
const s = useGlassesStore.getState()
|
|
127
|
+
return fixtureInfo(projectRawInfo(s), projectPairingIdentity())
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function projectRawInfo(s: ReturnType<typeof useGlassesStore.getState>) {
|
|
91
131
|
return {
|
|
92
132
|
model: s.deviceModel,
|
|
93
133
|
style: s.style,
|
|
@@ -190,29 +230,9 @@ export const glasses = {
|
|
|
190
230
|
|
|
191
231
|
// --- read-model (projected from the engine-owned glasses store) ---
|
|
192
232
|
status: (): GlassesStatusSnapshot => projectStatus(),
|
|
193
|
-
onStatus: (cb: (status: GlassesStatusSnapshot) => void): (() => void) =>
|
|
194
|
-
// Dedupe: the glasses store updates on many fields; only fire when the
|
|
195
|
-
// projected status snapshot actually changes.
|
|
196
|
-
let last = JSON.stringify(projectStatus())
|
|
197
|
-
return useGlassesStore.subscribe(() => {
|
|
198
|
-
const snap = projectStatus()
|
|
199
|
-
const key = JSON.stringify(snap)
|
|
200
|
-
if (key === last) return
|
|
201
|
-
last = key
|
|
202
|
-
cb(snap)
|
|
203
|
-
})
|
|
204
|
-
},
|
|
233
|
+
onStatus: (cb: (status: GlassesStatusSnapshot) => void): (() => void) => subscribeProjection(projectStatus, cb),
|
|
205
234
|
info: (): GlassesInfoSnapshot => projectInfo(),
|
|
206
|
-
onInfo: (cb: (info: GlassesInfoSnapshot) => void): (() => void) =>
|
|
207
|
-
let last = JSON.stringify(projectInfo())
|
|
208
|
-
return useGlassesStore.subscribe(() => {
|
|
209
|
-
const snap = projectInfo()
|
|
210
|
-
const key = JSON.stringify(snap)
|
|
211
|
-
if (key === last) return
|
|
212
|
-
last = key
|
|
213
|
-
cb(snap)
|
|
214
|
-
})
|
|
215
|
-
},
|
|
235
|
+
onInfo: (cb: (info: GlassesInfoSnapshot) => void): (() => void) => subscribeProjection(projectInfo, cb),
|
|
216
236
|
capabilities: () => getModelCapabilities(useGlassesStore.getState().deviceModel as DeviceTypes),
|
|
217
237
|
/** Ask the glasses to report fresh firmware/version info (updates the store). */
|
|
218
238
|
requestVersionInfo: () => BluetoothSdk.requestVersionInfo(),
|
|
@@ -283,14 +303,11 @@ export const glasses = {
|
|
|
283
303
|
statuses: (): Record<string, ControllerStatus> => projectControllerStatuses(),
|
|
284
304
|
status: (id: string): ControllerStatus | undefined => projectControllerStatuses()[id],
|
|
285
305
|
onStatus: (cb: (statuses: Record<string, ControllerStatus>) => void): (() => void) =>
|
|
286
|
-
|
|
287
|
-
(s) => s.controllers,
|
|
288
|
-
() => cb(projectControllerStatuses()),
|
|
289
|
-
),
|
|
306
|
+
subscribeProjection(projectControllerStatuses, cb),
|
|
290
307
|
/** Controllers delivering input (linked, route or claim done). */
|
|
291
308
|
connected: (): ControllerStatus[] => readyControllers(projectControllerStatuses()),
|
|
292
309
|
isConnected: (model?: string): boolean =>
|
|
293
|
-
Object.values(
|
|
310
|
+
Object.values(projectControllerStatuses()).some(
|
|
294
311
|
(status) => isControllerReady(status) && (model === undefined || status.model === model),
|
|
295
312
|
),
|
|
296
313
|
connect: (id: string): Promise<void> => BluetoothSdk.connectController(id),
|
|
@@ -329,6 +346,12 @@ export const glasses = {
|
|
|
329
346
|
setHand: (id: string, hand: "left" | "right"): Promise<void> => BluetoothSdk.neuralBandSetHand(id, hand),
|
|
330
347
|
},
|
|
331
348
|
|
|
349
|
+
// --- Apple Watch (iOS): pairs through the Glassly watch app over WatchConnectivity ---
|
|
350
|
+
appleWatch: {
|
|
351
|
+
/** Whether this iPhone has a paired watch and the Glassly watch app is on it. */
|
|
352
|
+
availability: (): Promise<AppleWatchAvailability> => BluetoothSdk.appleWatchAvailability(),
|
|
353
|
+
},
|
|
354
|
+
|
|
332
355
|
// --- audio (glasses media-volume, own-app playback hint, live mic level) ---
|
|
333
356
|
audio: {
|
|
334
357
|
/** Read the glasses' current media volume. */
|
package/src/facades/memory.ts
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* purge everything; the full timeline surface (lines, sessions, summaries)
|
|
5
5
|
* is what miniapps reach through session.memory with the MEMORY permission.
|
|
6
6
|
*/
|
|
7
|
-
import type {MemoryEntry, MemorySession} from "@glassly/miniapp"
|
|
7
|
+
import type {MemoryEntry, MemoryExportResult, MemoryImportResult, MemorySession} from "@glassly/miniapp"
|
|
8
8
|
|
|
9
|
-
import
|
|
9
|
+
import {exportMemoryFile, importMemoryFile} from "../services/memoryBackupFile"
|
|
10
|
+
import {MemoryBackupError, type MemoryStats} from "../services/MemoryStore"
|
|
10
11
|
import {memoryStore} from "../services/memoryStoreInstance"
|
|
11
12
|
|
|
12
13
|
export const memory = {
|
|
@@ -30,6 +31,14 @@ export const memory = {
|
|
|
30
31
|
stats: (): Promise<MemoryStats> => memoryStore.stats(),
|
|
31
32
|
/** Drop the entire timeline: every entry and every summary. */
|
|
32
33
|
clearAll: (): Promise<void> => memoryStore.clear(),
|
|
34
|
+
/** Share the whole timeline as a JSON backup file. */
|
|
35
|
+
exportFile: (): Promise<MemoryExportResult> => exportMemoryFile(),
|
|
36
|
+
/**
|
|
37
|
+
* Pick a backup file and merge it in, skipping what's already stored.
|
|
38
|
+
* Resolves null on cancel; rejects with a MemoryBackupError for a non-backup file.
|
|
39
|
+
*/
|
|
40
|
+
importFile: (): Promise<MemoryImportResult | null> => importMemoryFile(),
|
|
41
|
+
isBackupError: (err: unknown): boolean => err instanceof MemoryBackupError,
|
|
33
42
|
/** Subscribe to store mutations (from miniapps or Settings). */
|
|
34
43
|
onChanged: (listener: () => void): (() => void) => memoryStore.onChanged(listener),
|
|
35
44
|
}
|
package/src/facades/miniapps.ts
CHANGED
|
@@ -64,6 +64,9 @@ export const miniapps = {
|
|
|
64
64
|
useAppStatusStore.getState().setHiddenStatus(...args),
|
|
65
65
|
/** Mark a miniapp Always On (starts at launch, stops only from the switcher pill), persisted for the next boot. */
|
|
66
66
|
setAlwaysOn: (...args: Parameters<AppsState["setAlwaysOn"]>) => useAppStatusStore.getState().setAlwaysOn(...args),
|
|
67
|
+
/** Whether a tap expands one of the miniapp's `EX` widgets to the full screen, persisted. */
|
|
68
|
+
setWidgetExpand: (...args: Parameters<AppsState["setWidgetExpand"]>) =>
|
|
69
|
+
useAppStatusStore.getState().setWidgetExpand(...args),
|
|
67
70
|
|
|
68
71
|
/** Installed dashboard miniapps (manifest type "dashboard"); one runs at a time. */
|
|
69
72
|
dashboards: (): ClientApp[] => useAppStatusStore.getState().apps.filter((app) => app.type === "dashboard"),
|
package/src/facades/pairing.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
subscribePairingIdentity,
|
|
22
22
|
type IdentitySnapshot,
|
|
23
23
|
} from "../services/PairingIdentity"
|
|
24
|
+
import {fixtureIdentity} from "../services/ScreenshotFixture"
|
|
24
25
|
import {isGlassesConnected, isGlassesLinkLayerBusy, isGlassesReady} from "../services/GlassesReadiness"
|
|
25
26
|
import {
|
|
26
27
|
logAutomaticReportSubmissionStatus,
|
|
@@ -278,10 +279,11 @@ export const pairing = {
|
|
|
278
279
|
// --- pairing-identity lifecycle (the PairingIdentity read-model + the JS-owned
|
|
279
280
|
// identity writes; promotion to `paired` only ever happens natively) ---
|
|
280
281
|
/** The identity lifecycle snapshot: none | pending (chosen, never paired) | paired. */
|
|
281
|
-
identity: (): IdentitySnapshot => projectPairingIdentity(),
|
|
282
|
+
identity: (): IdentitySnapshot => fixtureIdentity(projectPairingIdentity()),
|
|
282
283
|
/** Subscribe to identity changes; fires only when the projected snapshot changes.
|
|
283
284
|
* Returns an unsubscribe. */
|
|
284
|
-
onIdentity: (cb: (identity: IdentitySnapshot) => void): (() => void) =>
|
|
285
|
+
onIdentity: (cb: (identity: IdentitySnapshot) => void): (() => void) =>
|
|
286
|
+
subscribePairingIdentity((identity) => cb(fixtureIdentity(identity))),
|
|
285
287
|
/** Mark the chosen model as the pending selection (the scan-entry write); the
|
|
286
288
|
* host renders it as a finish-pairing affordance until pairing succeeds. */
|
|
287
289
|
markPendingSelection: (model: string) => markPendingSelection(model),
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* providerKeys facade — `engine.providerKeys.{anthropic,gemini,elevenlabs}`:
|
|
2
|
+
* providerKeys facade — `engine.providerKeys.{anthropic,gemini,openai,deepseek,elevenlabs}`:
|
|
3
3
|
* validate-before-save probes for the user-supplied AI/voice provider keys
|
|
4
4
|
* (Settings > API Keys). Same per-provider shape as `engine.maps.mapbox` /
|
|
5
5
|
* `engine.speech.sonioxDirect` so the Keys screen treats providers uniformly.
|
|
6
6
|
*/
|
|
7
7
|
import {
|
|
8
8
|
validateAnthropicApiKey,
|
|
9
|
+
validateDeepSeekApiKey,
|
|
9
10
|
validateElevenLabsApiKey,
|
|
10
11
|
validateGeminiApiKey,
|
|
11
12
|
validateOpenAiApiKey,
|
|
@@ -24,6 +25,10 @@ export const providerKeys = {
|
|
|
24
25
|
/** Probe a key against the OpenAI API without saving it. */
|
|
25
26
|
validateKey: (...args: Parameters<typeof validateOpenAiApiKey>) => validateOpenAiApiKey(...args),
|
|
26
27
|
},
|
|
28
|
+
deepseek: {
|
|
29
|
+
/** Probe a key against the DeepSeek API without saving it. */
|
|
30
|
+
validateKey: (...args: Parameters<typeof validateDeepSeekApiKey>) => validateDeepSeekApiKey(...args),
|
|
31
|
+
},
|
|
27
32
|
elevenlabs: {
|
|
28
33
|
/** Probe a key against the ElevenLabs API without saving it. */
|
|
29
34
|
validateKey: (...args: Parameters<typeof validateElevenLabsApiKey>) => validateElevenLabsApiKey(...args),
|
|
@@ -11,10 +11,10 @@ export interface EngineReleaseMetadata {
|
|
|
11
11
|
|
|
12
12
|
export const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({
|
|
13
13
|
"schemaVersion": 1,
|
|
14
|
-
"familyBaseVersion": "0.3.
|
|
15
|
-
"releaseIdentity": "0.3.
|
|
16
|
-
"releaseSetId": "glassly-0.3.
|
|
17
|
-
"sourceCommit": "
|
|
14
|
+
"familyBaseVersion": "0.3.6",
|
|
15
|
+
"releaseIdentity": "0.3.6-dev.846",
|
|
16
|
+
"releaseSetId": "glassly-0.3.6-dev.846",
|
|
17
|
+
"sourceCommit": "c62af5b0d288f3b12d094fd57e5a909859742081",
|
|
18
18
|
"otaManifestUrl": null,
|
|
19
19
|
"otaManifestSha256": null
|
|
20
20
|
})
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
export type AiTier = "quick" | "balanced" | "frontier"
|
|
14
|
-
export type AiTierProvider = "anthropic" | "gemini" | "openai"
|
|
14
|
+
export type AiTierProvider = "anthropic" | "gemini" | "openai" | "deepseek"
|
|
15
15
|
|
|
16
16
|
export const AI_TIERS: readonly AiTier[] = ["quick", "balanced", "frontier"]
|
|
17
17
|
|
|
@@ -27,23 +27,27 @@ export interface TierModelOption {
|
|
|
27
27
|
* Curated per-tier model registry — the only place model ids live on the
|
|
28
28
|
* phone. Order within a tier is the "auto" preference order: first entry
|
|
29
29
|
* whose provider has a saved key wins. Anthropic leads (matching the cloud's
|
|
30
|
-
* default provider), then Gemini (the pre-tier quick default), then OpenAI
|
|
30
|
+
* default provider), then Gemini (the pre-tier quick default), then OpenAI,
|
|
31
|
+
* then DeepSeek.
|
|
31
32
|
*/
|
|
32
33
|
export const TIER_MODEL_OPTIONS: Record<AiTier, TierModelOption[]> = {
|
|
33
34
|
quick: [
|
|
34
35
|
{provider: "anthropic", model: "claude-haiku-4-5", label: "Claude Haiku 4.5"},
|
|
35
36
|
{provider: "gemini", model: "gemini-3.5-flash-lite", label: "Gemini 3.5 Flash Lite"},
|
|
36
37
|
{provider: "openai", model: "gpt-5-mini", label: "GPT-5 mini"},
|
|
38
|
+
{provider: "deepseek", model: "deepseek-chat", label: "DeepSeek Chat"},
|
|
37
39
|
],
|
|
38
40
|
balanced: [
|
|
39
41
|
{provider: "anthropic", model: "claude-haiku-4-5", label: "Claude Haiku 4.5"},
|
|
40
42
|
{provider: "gemini", model: "gemini-3.5-flash", label: "Gemini 3.5 Flash"},
|
|
41
43
|
{provider: "openai", model: "gpt-5.1", label: "GPT-5.1"},
|
|
44
|
+
{provider: "deepseek", model: "deepseek-chat", label: "DeepSeek Chat"},
|
|
42
45
|
],
|
|
43
46
|
frontier: [
|
|
44
47
|
{provider: "anthropic", model: "claude-sonnet-5", label: "Claude Sonnet 5"},
|
|
45
48
|
{provider: "gemini", model: "gemini-3.1-pro-preview", label: "Gemini 3.1 Pro"},
|
|
46
49
|
{provider: "openai", model: "gpt-5.1", label: "GPT-5.1"},
|
|
50
|
+
{provider: "deepseek", model: "deepseek-reasoner", label: "DeepSeek Reasoner"},
|
|
47
51
|
],
|
|
48
52
|
}
|
|
49
53
|
|
|
@@ -82,7 +86,9 @@ export function parseTierSetting(value: unknown): {provider: AiTierProvider; mod
|
|
|
82
86
|
const stored = value.slice(sep + 1).trim()
|
|
83
87
|
if (!stored) return null
|
|
84
88
|
const model = RETIRED_MODELS[stored] ?? stored
|
|
85
|
-
if (provider !== "anthropic" && provider !== "gemini" && provider !== "openai")
|
|
89
|
+
if (provider !== "anthropic" && provider !== "gemini" && provider !== "openai" && provider !== "deepseek") {
|
|
90
|
+
return null
|
|
91
|
+
}
|
|
86
92
|
return {provider, model}
|
|
87
93
|
}
|
|
88
94
|
|
|
@@ -22,7 +22,7 @@ import {unzip} from "react-native-zip-archive"
|
|
|
22
22
|
import semver from "semver"
|
|
23
23
|
import {AsyncResult, Result, result as Res} from "typesafe-ts"
|
|
24
24
|
|
|
25
|
-
import type {AppletPermission, AppPermissionType, AppletType, ClientApp} from "../types/applet"
|
|
25
|
+
import type {AppletPermission, AppPermissionType, AppletType, ClientApp, DeclaredWidget} from "../types/applet"
|
|
26
26
|
import {HardwareRequirement, HardwareRequirementLevel, HardwareType} from "../types"
|
|
27
27
|
import {configuredDevHost} from "../utils/configuredDevHost"
|
|
28
28
|
import {storage} from "../utils/storage/storage"
|
|
@@ -776,7 +776,7 @@ class AppRegistry {
|
|
|
776
776
|
|
|
777
777
|
// Declared actions (for session.miniapps.list + invoke gating).
|
|
778
778
|
const actions = normalizeManifestActions(manifest?.actions)
|
|
779
|
-
const widgets = normalizeManifestWidgets(manifest?.widgets)
|
|
779
|
+
const widgets = withWidgetExpandChoices(lmaInfo.packageName, normalizeManifestWidgets(manifest?.widgets))
|
|
780
780
|
|
|
781
781
|
// Dev miniapps live in the same lmas/ tree as installed ones, but
|
|
782
782
|
// their version directory name starts with "dev-".
|
|
@@ -864,7 +864,9 @@ class AppRegistry {
|
|
|
864
864
|
permissions,
|
|
865
865
|
hardwareRequirements,
|
|
866
866
|
actions: normalizeManifestActions(rec.actions),
|
|
867
|
-
...(normalizeManifestWidgets(rec.widgets).length > 0
|
|
867
|
+
...(normalizeManifestWidgets(rec.widgets).length > 0
|
|
868
|
+
? {widgets: withWidgetExpandChoices(rec.packageName, normalizeManifestWidgets(rec.widgets))}
|
|
869
|
+
: {}),
|
|
868
870
|
isMiniappDev: true,
|
|
869
871
|
devUrl: rec.devUrl,
|
|
870
872
|
devPort: rec.devPort,
|
|
@@ -948,6 +950,19 @@ export function saveAlwaysOnOverride(packageName: string, status: boolean): void
|
|
|
948
950
|
storage.save(`${packageName}_always_on`, status)
|
|
949
951
|
}
|
|
950
952
|
|
|
953
|
+
/** Applies the wearer's tap-to-expand choice to each expandable widget; expansion is on until they turn it off. */
|
|
954
|
+
export function withWidgetExpandChoices(packageName: string, widgets: DeclaredWidget[]): DeclaredWidget[] {
|
|
955
|
+
return widgets.map((widget) => {
|
|
956
|
+
if (!widget.expandable) return widget
|
|
957
|
+
const res = storage.load<boolean>(`${packageName}_widget_expand_${widget.id}`)
|
|
958
|
+
return {...widget, expandOnTap: res.is_ok() ? res.value !== false : true}
|
|
959
|
+
})
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
export function saveWidgetExpandChoice(packageName: string, widgetId: string, expandOnTap: boolean): void {
|
|
963
|
+
storage.save(`${packageName}_widget_expand_${widgetId}`, expandOnTap)
|
|
964
|
+
}
|
|
965
|
+
|
|
951
966
|
export function getLocalAppScreenshot(packageName: string): string | undefined {
|
|
952
967
|
const key = `${packageName}_screenshot`
|
|
953
968
|
const res = storage.load<string>(key)
|
|
@@ -383,7 +383,7 @@ export class BlobStore {
|
|
|
383
383
|
return
|
|
384
384
|
}
|
|
385
385
|
up.handle.offset = payload.offset
|
|
386
|
-
up.handle.
|
|
386
|
+
up.handle.writeBytesSync(bytes)
|
|
387
387
|
up.handle.offset = up.bytes // restore append position
|
|
388
388
|
this.hooks.sendResult(packageName, requestId, true, {bytesWritten: up.bytes})
|
|
389
389
|
return
|
|
@@ -397,7 +397,7 @@ export class BlobStore {
|
|
|
397
397
|
return
|
|
398
398
|
}
|
|
399
399
|
up.handle.offset = up.bytes
|
|
400
|
-
up.handle.
|
|
400
|
+
up.handle.writeBytesSync(bytes)
|
|
401
401
|
up.bytes += bytes.length
|
|
402
402
|
this.hooks.sendResult(packageName, requestId, true, {bytesWritten: up.bytes})
|
|
403
403
|
} catch (err) {
|
|
@@ -633,7 +633,7 @@ export class BlobStore {
|
|
|
633
633
|
}
|
|
634
634
|
try {
|
|
635
635
|
const want = Math.max(0, Math.min(Number(payload.maxBytes) || 1024 * 1024, reader.remaining))
|
|
636
|
-
const bytes = want > 0 ? reader.handle.
|
|
636
|
+
const bytes = want > 0 ? reader.handle.readBytesSync(want) : new Uint8Array(0)
|
|
637
637
|
reader.remaining -= bytes.length
|
|
638
638
|
const done = reader.remaining <= 0
|
|
639
639
|
const base64 = bytes.length
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reconnect the glasses when iOS relaunched the app for a preserved link.
|
|
3
|
+
*
|
|
4
|
+
* With CoreBluetooth state restoration, iOS keeps the lens connections after
|
|
5
|
+
* it terminates the app and relaunches it in the background on the next
|
|
6
|
+
* peripheral event. No screen mounts on that launch, so the host's usual
|
|
7
|
+
* "connect on the home screen" never runs; the engine asks native whether this
|
|
8
|
+
* is such a launch and connects the default wearable itself.
|
|
9
|
+
*/
|
|
10
|
+
import BluetoothSdk from "@glassly/bluetooth-sdk/internal"
|
|
11
|
+
|
|
12
|
+
type Deps = {
|
|
13
|
+
launchedForRestore: () => Promise<boolean>
|
|
14
|
+
connectDefault: () => Promise<void>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function reconnectAfterBluetoothRestoreLaunch(deps: Deps): Promise<boolean> {
|
|
18
|
+
let launched = false
|
|
19
|
+
try {
|
|
20
|
+
launched = await deps.launchedForRestore()
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.warn("BluetoothRestoreLaunch: restore-launch query failed:", error instanceof Error ? error.message : error)
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
if (!launched) return false
|
|
26
|
+
console.log("BluetoothRestoreLaunch: relaunched for a preserved glasses link; reconnecting")
|
|
27
|
+
try {
|
|
28
|
+
await deps.connectDefault()
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.warn("BluetoothRestoreLaunch: reconnect failed:", error instanceof Error ? error.message : error)
|
|
31
|
+
}
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function nativeRestoreLaunchQuery(): () => Promise<boolean> {
|
|
36
|
+
return () => BluetoothSdk.consumeBluetoothRestoreLaunch()
|
|
37
|
+
}
|
|
@@ -13,7 +13,7 @@ export interface ForegroundLocationPermissionClient {
|
|
|
13
13
|
*/
|
|
14
14
|
export async function resolveForegroundLocationPermission(
|
|
15
15
|
client: ForegroundLocationPermissionClient,
|
|
16
|
-
getAppState: () => string,
|
|
16
|
+
getAppState: () => string | null | undefined,
|
|
17
17
|
): Promise<ForegroundLocationPermissionResponse> {
|
|
18
18
|
const current = await client.getForegroundPermissionsAsync()
|
|
19
19
|
if (current.status === "granted" || getAppState() !== "active") {
|
|
@@ -132,7 +132,8 @@ class GestureDotsService {
|
|
|
132
132
|
* `controllerId` keeps two controllers' fingers apart.
|
|
133
133
|
*/
|
|
134
134
|
noteGesture(gestureName: string, claimed = false, fingerIndex?: number, controllerId?: string): void {
|
|
135
|
-
|
|
135
|
+
// A ring's scroll steps ride beside their stroke's one swipe, which already flashed.
|
|
136
|
+
if (!this.started || !this.enabled() || gestureName === "scroll_up" || gestureName === "scroll_down") return
|
|
136
137
|
this.applyGesture(gestureName, claimed, fingerIndex, controllerId)
|
|
137
138
|
this.trace("input", {gestureName, claimed})
|
|
138
139
|
}
|