@glassly/engine 0.3.5 → 0.3.6-beta.851
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
|
@@ -46,6 +46,7 @@ import {markTranscriptSourceRecent, useTranscriptionDebugStore} from "../stores/
|
|
|
46
46
|
import {resolveMapboxRenderToken} from "./MapboxTokenService"
|
|
47
47
|
import {
|
|
48
48
|
createAnthropicProvider,
|
|
49
|
+
createDeepSeekProvider,
|
|
49
50
|
createGeminiProvider,
|
|
50
51
|
createOpenAiProvider,
|
|
51
52
|
type LlmProviderImpl,
|
|
@@ -109,6 +110,7 @@ import {
|
|
|
109
110
|
unplaceWidget,
|
|
110
111
|
type WidgetBox,
|
|
111
112
|
type WidgetPlacements,
|
|
113
|
+
type WidgetView,
|
|
112
114
|
} from "./widgetPlacements"
|
|
113
115
|
import {
|
|
114
116
|
activeDashboard,
|
|
@@ -162,6 +164,13 @@ import {invokeScanQrSeam} from "../runtime/scanQrSeam"
|
|
|
162
164
|
import {invokeWebLoginSeam} from "../runtime/webLoginSeam"
|
|
163
165
|
import {normalizeStreamAudioConfig, normalizeStreamVideoConfig} from "../runtime/streamConfig"
|
|
164
166
|
import {visibleActionRows} from "./manifestActions"
|
|
167
|
+
import {
|
|
168
|
+
actionFailure,
|
|
169
|
+
appDeclaresAction,
|
|
170
|
+
checkActionAccess,
|
|
171
|
+
PendingActionCalls,
|
|
172
|
+
type ActionOutcome,
|
|
173
|
+
} from "./actionInvoke"
|
|
165
174
|
import {toLanguageHint} from "@glassly/cloud-protocol/languages"
|
|
166
175
|
import {buildMiniappManifestSnapshot, type MiniappRuntimeDiagnosticSnapshot} from "../utils/miniappDiagnostics"
|
|
167
176
|
import ttsModelManager from "./TTSModelManager"
|
|
@@ -176,6 +185,8 @@ import {listPhoneCalendarEvents, PhoneCalendarError} from "./PhoneCalendarServic
|
|
|
176
185
|
import {LocalMiniappStorage} from "./LocalMiniappStorage"
|
|
177
186
|
import {captureDecisionFor, type CaptureCandidate} from "./MemoryCapture"
|
|
178
187
|
import {memoryStore} from "./memoryStoreInstance"
|
|
188
|
+
import {exportMemoryFile, importMemoryFile} from "./memoryBackupFile"
|
|
189
|
+
import {MemoryBackupError} from "./MemoryStore"
|
|
179
190
|
import {speakerProfileStore} from "./speakerProfileStoreInstance"
|
|
180
191
|
|
|
181
192
|
// =============================================================================
|
|
@@ -419,6 +430,7 @@ const MINIAPP_READABLE_API_KEYS: Record<string, string> = {
|
|
|
419
430
|
anthropic: SETTINGS.anthropic_api_key.key,
|
|
420
431
|
gemini: SETTINGS.gemini_api_key.key,
|
|
421
432
|
openai: SETTINGS.openai_api_key.key,
|
|
433
|
+
deepseek: SETTINGS.deepseek_api_key.key,
|
|
422
434
|
elevenlabs: SETTINGS.elevenlabs_api_key.key,
|
|
423
435
|
mapbox: SETTINGS.mapbox_api_key.key,
|
|
424
436
|
}
|
|
@@ -440,7 +452,9 @@ function directLlmProvider(name: AiTierProvider): LlmProviderImpl {
|
|
|
440
452
|
? createGeminiProvider({logger})
|
|
441
453
|
: name === "openai"
|
|
442
454
|
? createOpenAiProvider({logger})
|
|
443
|
-
:
|
|
455
|
+
: name === "deepseek"
|
|
456
|
+
? createDeepSeekProvider({logger})
|
|
457
|
+
: createAnthropicProvider({logger})
|
|
444
458
|
directLlmProviders.set(name, built)
|
|
445
459
|
return built
|
|
446
460
|
}
|
|
@@ -460,6 +474,7 @@ function savedLlmKeys(): SavedLlmKeys {
|
|
|
460
474
|
anthropic: userLlmKey("anthropic").length > 0,
|
|
461
475
|
gemini: userLlmKey("gemini").length > 0,
|
|
462
476
|
openai: userLlmKey("openai").length > 0,
|
|
477
|
+
deepseek: userLlmKey("deepseek").length > 0,
|
|
463
478
|
}
|
|
464
479
|
}
|
|
465
480
|
|
|
@@ -641,6 +656,10 @@ class LocalMiniappRuntime {
|
|
|
641
656
|
private connectedApps: Map<string, ConnectedMiniapp> = new Map()
|
|
642
657
|
/** Claimed gestures (CLAIM_GESTURES): a stack per gesture, the most recent claimer holding it. */
|
|
643
658
|
private gestureClaims: GestureClaimState = emptyGestureClaims()
|
|
659
|
+
// The last main-view frame each app asked to render. Widget mode refuses
|
|
660
|
+
// frames and demotion clears them, and an app that has drawn once may not
|
|
661
|
+
// draw again on its own, so promotion replays this.
|
|
662
|
+
private retainedRenders = new Map<string, Record<string, unknown>>()
|
|
644
663
|
|
|
645
664
|
/**
|
|
646
665
|
* Packages that have completed their CONNECT handshake (sent
|
|
@@ -705,7 +724,13 @@ class LocalMiniappRuntime {
|
|
|
705
724
|
private localSecret = `miniapp_${Date.now()}_${Math.random().toString(36).slice(2, 14)}`
|
|
706
725
|
private usedTokens = new Set<string>()
|
|
707
726
|
|
|
708
|
-
private constructor() {
|
|
727
|
+
private constructor() {
|
|
728
|
+
miniappWidgetMode.subscribe((packageName, widgetOnly) => {
|
|
729
|
+
// Promotion happens inside the apps store's start(); the core-app slot
|
|
730
|
+
// settles with that store write, so replay once it has.
|
|
731
|
+
if (!widgetOnly) queueMicrotask(() => this.replayRetainedRender(packageName))
|
|
732
|
+
})
|
|
733
|
+
}
|
|
709
734
|
|
|
710
735
|
/** Snapshot of miniapps actively subscribed to hardware button events. */
|
|
711
736
|
public getButtonPressSubscribers(): string[] {
|
|
@@ -1122,7 +1147,11 @@ class LocalMiniappRuntime {
|
|
|
1122
1147
|
// window ends early once this app pushes its first display (see
|
|
1123
1148
|
// LocalDisplayManager.request), or after ~1.5s. Fires once per spawn —
|
|
1124
1149
|
// including a crash-respawn, since each spawn is a fresh "app is starting".
|
|
1125
|
-
|
|
1150
|
+
// A spawn for a dashboard widget is not the wearer starting the app: it draws
|
|
1151
|
+
// nothing of its own, so it shows no boot text either.
|
|
1152
|
+
if (!miniappWidgetMode.isWidgetOnly(packageName)) {
|
|
1153
|
+
localDisplayManager.onMount(packageName, installedManifest?.name ?? packageName)
|
|
1154
|
+
}
|
|
1126
1155
|
}
|
|
1127
1156
|
|
|
1128
1157
|
/**
|
|
@@ -1244,6 +1273,7 @@ class LocalMiniappRuntime {
|
|
|
1244
1273
|
}
|
|
1245
1274
|
// Its gesture claims go back to whoever claimed them before.
|
|
1246
1275
|
this.applyGestureClaimChanges(dropGestureClaims(this.gestureClaims, packageName))
|
|
1276
|
+
this.retainedRenders.delete(packageName)
|
|
1247
1277
|
|
|
1248
1278
|
// Warm-up is a request-owned camera lease. Cancel it even if the app record was already
|
|
1249
1279
|
// removed so a close racing the camera-open promise cannot leave the sensor running.
|
|
@@ -1569,6 +1599,8 @@ class LocalMiniappRuntime {
|
|
|
1569
1599
|
case MiniappRequestType.MEMORY_QUERY:
|
|
1570
1600
|
case MiniappRequestType.MEMORY_WINDOW:
|
|
1571
1601
|
case MiniappRequestType.MEMORY_EXPORT_TEXT:
|
|
1602
|
+
case MiniappRequestType.MEMORY_EXPORT_FILE:
|
|
1603
|
+
case MiniappRequestType.MEMORY_IMPORT_FILE:
|
|
1572
1604
|
case MiniappRequestType.MEMORY_SUMMARY_ADD:
|
|
1573
1605
|
case MiniappRequestType.MEMORY_SUMMARY_LIST:
|
|
1574
1606
|
case MiniappRequestType.MEMORY_PENDING_SUMMARY_HOUR:
|
|
@@ -1797,6 +1829,9 @@ class LocalMiniappRuntime {
|
|
|
1797
1829
|
case MiniappRequestType.WIDGET_RESULT:
|
|
1798
1830
|
this.handleWidgetResult(packageName, payload)
|
|
1799
1831
|
break
|
|
1832
|
+
case MiniappRequestType.WIDGET_EXPANDABLE:
|
|
1833
|
+
this.handleWidgetExpandable(packageName, payload, requestId)
|
|
1834
|
+
break
|
|
1800
1835
|
case MiniappRequestType.SYSTEM_EXIT:
|
|
1801
1836
|
void this.handleSystemExit(packageName, requestId)
|
|
1802
1837
|
break
|
|
@@ -2489,11 +2524,6 @@ class LocalMiniappRuntime {
|
|
|
2489
2524
|
this.sendResult(packageName, requestId, true, {status: "blocked", reason: "app stopped"})
|
|
2490
2525
|
return
|
|
2491
2526
|
}
|
|
2492
|
-
// A widget-only app draws through its widget alone.
|
|
2493
|
-
if (miniappWidgetMode.isWidgetOnly(packageName)) {
|
|
2494
|
-
this.sendResult(packageName, requestId, true, {status: "blocked", reason: "widget mode"})
|
|
2495
|
-
return
|
|
2496
|
-
}
|
|
2497
2527
|
if (!Array.isArray(payload.elements)) {
|
|
2498
2528
|
this.sendResult(packageName, requestId, false, undefined, {
|
|
2499
2529
|
code: MiniappErrorCode.INTERNAL,
|
|
@@ -2501,6 +2531,15 @@ class LocalMiniappRuntime {
|
|
|
2501
2531
|
})
|
|
2502
2532
|
return
|
|
2503
2533
|
}
|
|
2534
|
+
if (payload.view !== "dashboard") {
|
|
2535
|
+
if (payload.elements.length === 0) this.retainedRenders.delete(packageName)
|
|
2536
|
+
else this.retainedRenders.set(packageName, payload)
|
|
2537
|
+
}
|
|
2538
|
+
// A widget-only app draws through its widget alone; the frame waits for promotion.
|
|
2539
|
+
if (miniappWidgetMode.isWidgetOnly(packageName)) {
|
|
2540
|
+
this.sendResult(packageName, requestId, true, {status: "blocked", reason: "widget mode"})
|
|
2541
|
+
return
|
|
2542
|
+
}
|
|
2504
2543
|
// The dashboard view is the overlay app's alone; anyone else's frame
|
|
2505
2544
|
// is dropped, reported rather than silently lost.
|
|
2506
2545
|
if (payload.view === "dashboard" && !isOverlayApp(packageName)) {
|
|
@@ -2530,6 +2569,13 @@ class LocalMiniappRuntime {
|
|
|
2530
2569
|
}
|
|
2531
2570
|
}
|
|
2532
2571
|
|
|
2572
|
+
/** Put a promoted app's last main-view frame on the glasses; widget mode had refused it. */
|
|
2573
|
+
private replayRetainedRender(packageName: string): void {
|
|
2574
|
+
const payload = this.retainedRenders.get(packageName)
|
|
2575
|
+
if (!payload || !this.connectedApps.has(packageName) || miniappWidgetMode.isWidgetOnly(packageName)) return
|
|
2576
|
+
this.handleRender(packageName, payload)
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2533
2579
|
/**
|
|
2534
2580
|
* display.showView: the active dashboard takes or releases the overlay
|
|
2535
2581
|
* slot. Refused (NOT_PERMITTED) for an app that is not a dashboard;
|
|
@@ -3973,6 +4019,14 @@ class LocalMiniappRuntime {
|
|
|
3973
4019
|
this.sendResult(packageName, requestId, true, {text: await memoryStore.exportText(scope, num(payload.at))})
|
|
3974
4020
|
return
|
|
3975
4021
|
}
|
|
4022
|
+
case MiniappRequestType.MEMORY_EXPORT_FILE: {
|
|
4023
|
+
this.sendResult(packageName, requestId, true, await exportMemoryFile())
|
|
4024
|
+
return
|
|
4025
|
+
}
|
|
4026
|
+
case MiniappRequestType.MEMORY_IMPORT_FILE: {
|
|
4027
|
+
this.sendResult(packageName, requestId, true, {imported: await importMemoryFile()})
|
|
4028
|
+
return
|
|
4029
|
+
}
|
|
3976
4030
|
case MiniappRequestType.MEMORY_SUMMARY_ADD: {
|
|
3977
4031
|
const hourStart = num(payload.hourStart)
|
|
3978
4032
|
if (hourStart == null || typeof payload.text !== "string") {
|
|
@@ -4008,6 +4062,13 @@ class LocalMiniappRuntime {
|
|
|
4008
4062
|
})
|
|
4009
4063
|
}
|
|
4010
4064
|
} catch (err) {
|
|
4065
|
+
if (err instanceof MemoryBackupError) {
|
|
4066
|
+
this.sendResult(packageName, requestId, false, undefined, {
|
|
4067
|
+
code: MiniappErrorCode.INVALID_ARGUMENT,
|
|
4068
|
+
message: err.message,
|
|
4069
|
+
})
|
|
4070
|
+
return
|
|
4071
|
+
}
|
|
4011
4072
|
console.error(`${LOG_TAG}: ${requestType} error:`, err)
|
|
4012
4073
|
this.sendResult(packageName, requestId, false, undefined, {
|
|
4013
4074
|
code: MiniappErrorCode.INTERNAL,
|
|
@@ -4629,7 +4690,7 @@ class LocalMiniappRuntime {
|
|
|
4629
4690
|
if (base64) {
|
|
4630
4691
|
// File share via base64 — write to temp file then share
|
|
4631
4692
|
const tempFile = new File(Paths.cache, filename || "shared_file")
|
|
4632
|
-
tempFile.
|
|
4693
|
+
tempFile.writeSync(base64, {encoding: "base64"})
|
|
4633
4694
|
await Share.open({
|
|
4634
4695
|
url: tempFile.uri,
|
|
4635
4696
|
type: mimeType || "application/octet-stream",
|
|
@@ -4709,7 +4770,7 @@ class LocalMiniappRuntime {
|
|
|
4709
4770
|
let file: File
|
|
4710
4771
|
if (base64) {
|
|
4711
4772
|
file = new File(Paths.cache, name)
|
|
4712
|
-
file.
|
|
4773
|
+
file.writeSync(base64, {encoding: "base64"})
|
|
4713
4774
|
} else if (url) {
|
|
4714
4775
|
file = await File.downloadFileAsync(url, new File(Paths.cache, name), {idempotent: true})
|
|
4715
4776
|
} else {
|
|
@@ -5995,10 +6056,7 @@ class LocalMiniappRuntime {
|
|
|
5995
6056
|
private static readonly ACTION_PAYLOAD_CAP = 256 * 1024
|
|
5996
6057
|
|
|
5997
6058
|
/** Outstanding action invocations, keyed by host-generated callId. */
|
|
5998
|
-
private actionCalls = new
|
|
5999
|
-
string,
|
|
6000
|
-
{callerPackageName: string; targetPackageName: string; callerRequestId?: string; timer: number}
|
|
6001
|
-
>()
|
|
6059
|
+
private actionCalls = new PendingActionCalls(BgTimer, (value) => this.actionPayloadTooLarge(value))
|
|
6002
6060
|
private actionCallSeq = 0
|
|
6003
6061
|
|
|
6004
6062
|
private interopApps(): ClientApp[] {
|
|
@@ -6013,6 +6071,8 @@ class LocalMiniappRuntime {
|
|
|
6013
6071
|
private async startInteropApp(packageName: string): Promise<boolean> {
|
|
6014
6072
|
const app = this.interopApps().find((a) => a.packageName === packageName)
|
|
6015
6073
|
if (!app) return false
|
|
6074
|
+
// Running only for its widget: start() promotes it to a normal run, as opening it on the phone does.
|
|
6075
|
+
if (app.widgetOnly) return useAppStatusStore.getState().start(app, {skipNavigation: true})
|
|
6016
6076
|
if (app.local) {
|
|
6017
6077
|
await miniappLauncher.ensureConnected(packageName)
|
|
6018
6078
|
return true
|
|
@@ -6074,7 +6134,8 @@ class LocalMiniappRuntime {
|
|
|
6074
6134
|
packageName: app.packageName,
|
|
6075
6135
|
name: app.name,
|
|
6076
6136
|
version: app.version ?? "",
|
|
6077
|
-
running
|
|
6137
|
+
// A provider running only for a placed widget is not a run the wearer started.
|
|
6138
|
+
running: app.running && !app.widgetOnly,
|
|
6078
6139
|
hidden: app.hidden === true,
|
|
6079
6140
|
system: isSystemMiniapp(app),
|
|
6080
6141
|
compatibility,
|
|
@@ -6268,6 +6329,8 @@ class LocalMiniappRuntime {
|
|
|
6268
6329
|
private widgetCallSeq = 0
|
|
6269
6330
|
/** Elements per widget frame; a card is small, and the link is slow. */
|
|
6270
6331
|
private static readonly WIDGET_FRAME_ELEMENT_CAP = 64
|
|
6332
|
+
/** Elements per expanded widget frame: the full screen, on the same link. */
|
|
6333
|
+
private static readonly WIDGET_EXPANDED_FRAME_ELEMENT_CAP = 128
|
|
6271
6334
|
|
|
6272
6335
|
private requireWidgetHost(packageName: string, requestId: string | undefined, op: string): boolean {
|
|
6273
6336
|
return this.requirePermission(packageName, requestId, "HOST_WIDGETS", `to ${op} widgets`)
|
|
@@ -6316,6 +6379,7 @@ class LocalMiniappRuntime {
|
|
|
6316
6379
|
name: widget.name,
|
|
6317
6380
|
sizes: widget.sizes,
|
|
6318
6381
|
defaultSize: widget.defaultSize,
|
|
6382
|
+
expandable: widget.expandOnTap,
|
|
6319
6383
|
running: app.running,
|
|
6320
6384
|
compatible: app.compatibility?.isCompatible !== false,
|
|
6321
6385
|
})
|
|
@@ -6327,7 +6391,8 @@ class LocalMiniappRuntime {
|
|
|
6327
6391
|
private parseWidgetBox(raw: unknown): WidgetBox | null {
|
|
6328
6392
|
const box = raw as {size?: unknown; width?: unknown; height?: unknown} | null
|
|
6329
6393
|
if (!box || typeof box !== "object") return null
|
|
6330
|
-
if (box.size !== "XS" && box.size !== "SM" && box.size !== "MD" && box.size !== "LG")
|
|
6394
|
+
if (box.size !== "XS" && box.size !== "XSM" && box.size !== "SM" && box.size !== "MD" && box.size !== "LG")
|
|
6395
|
+
return null
|
|
6331
6396
|
const width = Number(box.width)
|
|
6332
6397
|
const height = Number(box.height)
|
|
6333
6398
|
if (!(width > 0) || !(height > 0)) return null
|
|
@@ -6353,7 +6418,7 @@ class LocalMiniappRuntime {
|
|
|
6353
6418
|
/**
|
|
6354
6419
|
* session.widgets.place: record the host's box, wake the provider in widget
|
|
6355
6420
|
* mode when it is not running, hand it the box, and answer with the last
|
|
6356
|
-
*
|
|
6421
|
+
* card and expanded frames the provider pushed for the widget.
|
|
6357
6422
|
*/
|
|
6358
6423
|
private async handleWidgetPlace(
|
|
6359
6424
|
hostPackageName: string,
|
|
@@ -6381,10 +6446,12 @@ class LocalMiniappRuntime {
|
|
|
6381
6446
|
}
|
|
6382
6447
|
const provider = app.packageName
|
|
6383
6448
|
// Record the placement before the wake so the spawn lands in widget mode
|
|
6384
|
-
// and an unplace racing it cannot stop the provider early.
|
|
6449
|
+
// and an unplace racing it cannot stop the provider early. A provider the
|
|
6450
|
+
// store already runs (a wearer's launch still spawning, say) stays a
|
|
6451
|
+
// normal run; the registry alone lags that launch.
|
|
6385
6452
|
this.widgetPlacements = placeWidget(this.widgetPlacements, {hostPackageName, providerPackageName: provider, widgetId, box})
|
|
6386
6453
|
miniappWidgetMode.setPlaced(provider, true)
|
|
6387
|
-
if (!miniappRunningRegistry.has(provider)) miniappWidgetMode.setWidgetOnly(provider, true)
|
|
6454
|
+
if (!app.running && !miniappRunningRegistry.has(provider)) miniappWidgetMode.setWidgetOnly(provider, true)
|
|
6388
6455
|
try {
|
|
6389
6456
|
await this.wakeInteropApp(provider)
|
|
6390
6457
|
} catch (e) {
|
|
@@ -6400,7 +6467,10 @@ class LocalMiniappRuntime {
|
|
|
6400
6467
|
if (placementOf(this.widgetPlacements, hostPackageName, provider, widgetId) && this.connectedApps.has(provider)) {
|
|
6401
6468
|
this.sendWidgetCall(provider, widgetId, {kind: "box", box})
|
|
6402
6469
|
}
|
|
6403
|
-
this.sendResult(hostPackageName, requestId, true, {
|
|
6470
|
+
this.sendResult(hostPackageName, requestId, true, {
|
|
6471
|
+
frame: widgetFrameOf(this.widgetPlacements, provider, widgetId),
|
|
6472
|
+
expandedFrame: widgetFrameOf(this.widgetPlacements, provider, widgetId, "expanded"),
|
|
6473
|
+
})
|
|
6404
6474
|
}
|
|
6405
6475
|
|
|
6406
6476
|
private async handleWidgetUnplace(
|
|
@@ -6518,20 +6588,57 @@ class LocalMiniappRuntime {
|
|
|
6518
6588
|
}
|
|
6519
6589
|
}
|
|
6520
6590
|
|
|
6521
|
-
/** Provider
|
|
6591
|
+
/** Provider: read, or set with `expandable`, whether a tap expands one of its own `EX` widgets. */
|
|
6592
|
+
private handleWidgetExpandable(packageName: string, payload: Record<string, unknown>, requestId?: string): void {
|
|
6593
|
+
const widgetId = payload.widgetId as string | undefined
|
|
6594
|
+
const app = this.interopApps().find((a) => a.packageName === packageName)
|
|
6595
|
+
const widget = app && widgetId ? this.declaredWidget(app, widgetId) : undefined
|
|
6596
|
+
if (!widget?.expandable) {
|
|
6597
|
+
this.sendResult(packageName, requestId, false, undefined, {
|
|
6598
|
+
code: MiniappErrorCode.WIDGET_NOT_FOUND,
|
|
6599
|
+
message: `${packageName} has no widget "${widgetId ?? "(missing widgetId)"}" that lists EX`,
|
|
6600
|
+
})
|
|
6601
|
+
return
|
|
6602
|
+
}
|
|
6603
|
+
const expandable =
|
|
6604
|
+
typeof payload.expandable === "boolean"
|
|
6605
|
+
? useAppStatusStore.getState().setWidgetExpand(packageName, widget.id, payload.expandable)
|
|
6606
|
+
: widget.expandOnTap
|
|
6607
|
+
this.sendResult(packageName, requestId, true, {expandable})
|
|
6608
|
+
}
|
|
6609
|
+
|
|
6610
|
+
/** Provider → host, one-shot: cache the frame for its view and push it to every host holding the widget. */
|
|
6522
6611
|
private handleWidgetUpdate(providerPackageName: string, payload: Record<string, unknown>): void {
|
|
6523
6612
|
const widgetId = payload.widgetId as string | undefined
|
|
6524
6613
|
const elements = payload.elements
|
|
6525
6614
|
if (!widgetId || !Array.isArray(elements)) return
|
|
6526
6615
|
const app = this.interopApps().find((a) => a.packageName === providerPackageName)
|
|
6527
|
-
|
|
6528
|
-
if (
|
|
6529
|
-
|
|
6616
|
+
const widget = app ? this.declaredWidget(app, widgetId) : undefined
|
|
6617
|
+
if (!widget) return
|
|
6618
|
+
const view: WidgetView = payload.view === "expanded" ? "expanded" : "card"
|
|
6619
|
+
if (view === "expanded" && !widget.expandable) {
|
|
6620
|
+
console.warn(
|
|
6621
|
+
`${LOG_TAG}: dropping expanded frame from ${providerPackageName}/${widgetId}: it does not list EX`,
|
|
6622
|
+
)
|
|
6530
6623
|
return
|
|
6531
6624
|
}
|
|
6532
|
-
|
|
6625
|
+
const cap =
|
|
6626
|
+
view === "expanded"
|
|
6627
|
+
? LocalMiniappRuntime.WIDGET_EXPANDED_FRAME_ELEMENT_CAP
|
|
6628
|
+
: LocalMiniappRuntime.WIDGET_FRAME_ELEMENT_CAP
|
|
6629
|
+
if (elements.length > cap || this.actionPayloadTooLarge(elements)) {
|
|
6630
|
+
console.warn(`${LOG_TAG}: dropping oversized ${view} widget frame from ${providerPackageName}/${widgetId}`)
|
|
6631
|
+
return
|
|
6632
|
+
}
|
|
6633
|
+
this.widgetPlacements = setWidgetFrame(this.widgetPlacements, providerPackageName, widgetId, elements, view)
|
|
6533
6634
|
for (const host of widgetHostsOf(this.widgetPlacements, providerPackageName, widgetId)) {
|
|
6534
|
-
this.sendToMiniapp(host, {
|
|
6635
|
+
this.sendToMiniapp(host, {
|
|
6636
|
+
type: MiniappResponseType.WIDGET_FRAME,
|
|
6637
|
+
packageName: providerPackageName,
|
|
6638
|
+
widgetId,
|
|
6639
|
+
elements,
|
|
6640
|
+
...(view === "expanded" ? {view} : {}),
|
|
6641
|
+
})
|
|
6535
6642
|
}
|
|
6536
6643
|
}
|
|
6537
6644
|
|
|
@@ -6568,111 +6675,118 @@ class LocalMiniappRuntime {
|
|
|
6568
6675
|
}
|
|
6569
6676
|
}
|
|
6570
6677
|
|
|
6678
|
+
/** Floored at 6s: a freshly-woken target buffers an undelivered ACTION_CALL for 5s (HANDLER_WAIT_MS in @glassly/miniapp's actions module) + 1s for the reply. Keep in sync. */
|
|
6679
|
+
private static clampActionTimeout(raw: unknown): number {
|
|
6680
|
+
return Math.min(Math.max(Number(raw) || 30_000, 6_000), 120_000)
|
|
6681
|
+
}
|
|
6682
|
+
|
|
6683
|
+
/** Caller identity the host presents for host-initiated actions (a system caller). */
|
|
6684
|
+
private static readonly HOST_ACTION_CALLER = "host"
|
|
6685
|
+
|
|
6686
|
+
/** Installed, compatible, and declares the action — the same gates `invokeActionFromHost` applies up front. */
|
|
6687
|
+
public hostActionAvailable(targetPackageName: string, actionId: string): boolean {
|
|
6688
|
+
return appDeclaresAction(
|
|
6689
|
+
this.interopApps().find((a) => a.packageName === targetPackageName),
|
|
6690
|
+
actionId,
|
|
6691
|
+
)
|
|
6692
|
+
}
|
|
6693
|
+
|
|
6571
6694
|
/**
|
|
6572
|
-
*
|
|
6573
|
-
* and
|
|
6695
|
+
* Host-initiated action call (e.g. the watch remote): same gates, headless
|
|
6696
|
+
* wake and ACTION_CALL/ACTION_RESULT correlation as a miniapp's
|
|
6697
|
+
* session.actions.invoke, with the host as a system caller.
|
|
6698
|
+
* Rejects with an Error whose `code` is a MiniappErrorCode.
|
|
6574
6699
|
*/
|
|
6700
|
+
public async invokeActionFromHost(
|
|
6701
|
+
targetPackageName: string,
|
|
6702
|
+
actionId: string,
|
|
6703
|
+
params: Record<string, unknown> = {},
|
|
6704
|
+
timeoutMs?: number,
|
|
6705
|
+
): Promise<unknown> {
|
|
6706
|
+
const outcome = await this.runActionInvoke(
|
|
6707
|
+
LocalMiniappRuntime.HOST_ACTION_CALLER,
|
|
6708
|
+
true,
|
|
6709
|
+
targetPackageName,
|
|
6710
|
+
actionId,
|
|
6711
|
+
params,
|
|
6712
|
+
LocalMiniappRuntime.clampActionTimeout(timeoutMs),
|
|
6713
|
+
)
|
|
6714
|
+
if (outcome.ok) return outcome.data
|
|
6715
|
+
throw Object.assign(new Error(outcome.error.message), {code: outcome.error.code})
|
|
6716
|
+
}
|
|
6717
|
+
|
|
6718
|
+
/** session.actions.invoke → run the call, then relay the outcome to the caller's pending request. */
|
|
6575
6719
|
private async handleActionInvoke(
|
|
6576
6720
|
callerPackageName: string,
|
|
6577
6721
|
payload: Record<string, unknown>,
|
|
6578
6722
|
requestId?: string,
|
|
6579
6723
|
): Promise<void> {
|
|
6580
|
-
const
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6724
|
+
const outcome = await this.runActionInvoke(
|
|
6725
|
+
callerPackageName,
|
|
6726
|
+
this.isSystemPackage(callerPackageName),
|
|
6727
|
+
payload.targetPackageName as string | undefined,
|
|
6728
|
+
payload.actionId as string | undefined,
|
|
6729
|
+
(payload.params as Record<string, unknown> | undefined) ?? {},
|
|
6730
|
+
LocalMiniappRuntime.clampActionTimeout(payload.timeoutMs),
|
|
6731
|
+
)
|
|
6732
|
+
if (outcome.ok) this.sendResult(callerPackageName, requestId, true, outcome.data)
|
|
6733
|
+
else this.sendResult(callerPackageName, requestId, false, undefined, outcome.error)
|
|
6734
|
+
}
|
|
6735
|
+
|
|
6736
|
+
/**
|
|
6737
|
+
* Gate, wake the target headlessly, deliver an ACTION_CALL, and resolve when
|
|
6738
|
+
* its ACTION_RESULT (or the timeout) arrives. Never rejects.
|
|
6739
|
+
*/
|
|
6740
|
+
private async runActionInvoke(
|
|
6741
|
+
callerPackageName: string,
|
|
6742
|
+
callerIsSystem: boolean,
|
|
6743
|
+
target: string | undefined,
|
|
6744
|
+
actionId: string | undefined,
|
|
6745
|
+
params: Record<string, unknown>,
|
|
6746
|
+
timeoutMs: number,
|
|
6747
|
+
): Promise<ActionOutcome> {
|
|
6748
|
+
const fail = actionFailure
|
|
6591
6749
|
|
|
6592
6750
|
if (this.actionPayloadTooLarge(params)) {
|
|
6593
|
-
|
|
6594
|
-
code: MiniappErrorCode.PAYLOAD_TOO_LARGE,
|
|
6595
|
-
message: "action params exceeded the 256 KB cap",
|
|
6596
|
-
})
|
|
6597
|
-
return
|
|
6751
|
+
return fail(MiniappErrorCode.PAYLOAD_TOO_LARGE, "action params exceeded the 256 KB cap")
|
|
6598
6752
|
}
|
|
6599
6753
|
|
|
6600
6754
|
const app = target ? this.interopApps().find((a) => a.packageName === target) : undefined
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
packageName: target,
|
|
6623
|
-
reason: `${target} is not compatible with the connected glasses`,
|
|
6624
|
-
metadata: {missingRequired: app.compatibility.missingRequired ?? [], via: "miniapps.invoke"},
|
|
6625
|
-
timestamp: Date.now(),
|
|
6626
|
-
})
|
|
6627
|
-
return
|
|
6628
|
-
}
|
|
6629
|
-
// Declared-action gate: the target must declare the action in its manifest.
|
|
6630
|
-
const declared = (app.actions ?? []).find((a) => a.id === actionId)
|
|
6631
|
-
if (!declared) {
|
|
6632
|
-
this.sendResult(callerPackageName, requestId, false, undefined, {
|
|
6633
|
-
code: MiniappErrorCode.ACTION_NOT_FOUND,
|
|
6634
|
-
message: `${target} does not declare action "${actionId}"`,
|
|
6635
|
-
})
|
|
6636
|
-
return
|
|
6637
|
-
}
|
|
6638
|
-
// Visibility gate: declared actions are public by default; ones marked
|
|
6639
|
-
// `visibility: "system"` stay restricted to system/dev callers.
|
|
6640
|
-
if (declared.visibility === "system" && !this.isSystemPackage(callerPackageName)) {
|
|
6641
|
-
this.auditInterop({
|
|
6642
|
-
caller: callerPackageName,
|
|
6643
|
-
op: "invoke",
|
|
6644
|
-
target,
|
|
6645
|
-
actionId,
|
|
6646
|
-
ok: false,
|
|
6647
|
-
errorCode: MiniappErrorCode.NOT_PERMITTED,
|
|
6648
|
-
})
|
|
6649
|
-
this.sendResult(callerPackageName, requestId, false, undefined, {
|
|
6650
|
-
code: MiniappErrorCode.NOT_PERMITTED,
|
|
6651
|
-
message: `Action "${actionId}" on ${target} is restricted to system apps`,
|
|
6652
|
-
})
|
|
6653
|
-
return
|
|
6755
|
+
const denied = checkActionAccess(app, target, actionId, callerIsSystem)
|
|
6756
|
+
if (denied) {
|
|
6757
|
+
if (denied.code === MiniappErrorCode.APP_NOT_COMPATIBLE && target && app) {
|
|
6758
|
+
islandNotifications.emit({
|
|
6759
|
+
kind: "version_incompatible",
|
|
6760
|
+
packageName: target,
|
|
6761
|
+
reason: denied.message,
|
|
6762
|
+
metadata: {missingRequired: app.compatibility?.missingRequired ?? [], via: "miniapps.invoke"},
|
|
6763
|
+
timestamp: Date.now(),
|
|
6764
|
+
})
|
|
6765
|
+
} else if (denied.code === MiniappErrorCode.NOT_PERMITTED) {
|
|
6766
|
+
this.auditInterop({
|
|
6767
|
+
caller: callerPackageName,
|
|
6768
|
+
op: "invoke",
|
|
6769
|
+
target,
|
|
6770
|
+
actionId,
|
|
6771
|
+
ok: false,
|
|
6772
|
+
errorCode: MiniappErrorCode.NOT_PERMITTED,
|
|
6773
|
+
})
|
|
6774
|
+
}
|
|
6775
|
+
return {ok: false, error: denied}
|
|
6654
6776
|
}
|
|
6777
|
+
if (!target || !actionId) return fail(MiniappErrorCode.INTERNAL, "unreachable")
|
|
6655
6778
|
|
|
6656
6779
|
// Headless wake + wait for CONNECT (idempotent / fast if already connected).
|
|
6657
6780
|
try {
|
|
6658
6781
|
await this.wakeInteropApp(target)
|
|
6659
6782
|
} catch (e) {
|
|
6660
|
-
|
|
6661
|
-
code: MiniappErrorCode.WAKE_FAILED,
|
|
6662
|
-
message: (e as Error)?.message ?? `failed to wake ${target}`,
|
|
6663
|
-
})
|
|
6664
|
-
return
|
|
6783
|
+
return fail(MiniappErrorCode.WAKE_FAILED, (e as Error)?.message ?? `failed to wake ${target}`)
|
|
6665
6784
|
}
|
|
6666
6785
|
|
|
6667
6786
|
// The wake waited for CONNECT, but the target could have dropped in the gap
|
|
6668
6787
|
// before delivery — fail fast rather than arming a timer for a call that
|
|
6669
|
-
// sendToMiniapp would silently drop
|
|
6670
|
-
// full invoke timeout for an ACTION_CALL that was never delivered).
|
|
6788
|
+
// sendToMiniapp would silently drop.
|
|
6671
6789
|
if (!this.connectedApps.has(target)) {
|
|
6672
|
-
this.sendResult(callerPackageName, requestId, false, undefined, {
|
|
6673
|
-
code: MiniappErrorCode.WAKE_FAILED,
|
|
6674
|
-
message: `${target} disconnected before the action could be delivered`,
|
|
6675
|
-
})
|
|
6676
6790
|
this.auditInterop({
|
|
6677
6791
|
caller: callerPackageName,
|
|
6678
6792
|
op: "invoke",
|
|
@@ -6681,19 +6795,12 @@ class LocalMiniappRuntime {
|
|
|
6681
6795
|
ok: false,
|
|
6682
6796
|
errorCode: MiniappErrorCode.WAKE_FAILED,
|
|
6683
6797
|
})
|
|
6684
|
-
return
|
|
6798
|
+
return fail(MiniappErrorCode.WAKE_FAILED, `${target} disconnected before the action could be delivered`)
|
|
6685
6799
|
}
|
|
6686
6800
|
|
|
6687
|
-
// Deliver the call and arm the handler timeout; ACTION_RESULT
|
|
6801
|
+
// Deliver the call and arm the handler timeout; ACTION_RESULT settles it.
|
|
6688
6802
|
const callId = `act-${Date.now().toString(36)}-${this.actionCallSeq++}`
|
|
6689
|
-
const
|
|
6690
|
-
this.actionCalls.delete(callId)
|
|
6691
|
-
this.sendResult(callerPackageName, requestId, false, undefined, {
|
|
6692
|
-
code: MiniappErrorCode.ACTION_TIMEOUT,
|
|
6693
|
-
message: `action "${actionId}" timed out after ${timeoutMs}ms`,
|
|
6694
|
-
})
|
|
6695
|
-
}, timeoutMs)
|
|
6696
|
-
this.actionCalls.set(callId, {callerPackageName, targetPackageName: target, callerRequestId: requestId, timer})
|
|
6803
|
+
const outcome = this.actionCalls.register(callId, target, actionId, timeoutMs)
|
|
6697
6804
|
this.sendToMiniapp(target, {
|
|
6698
6805
|
type: MiniappResponseType.ACTION_CALL,
|
|
6699
6806
|
callId,
|
|
@@ -6702,38 +6809,12 @@ class LocalMiniappRuntime {
|
|
|
6702
6809
|
callerPackageName,
|
|
6703
6810
|
})
|
|
6704
6811
|
this.auditInterop({caller: callerPackageName, op: "invoke", target, actionId, ok: true})
|
|
6812
|
+
return outcome
|
|
6705
6813
|
}
|
|
6706
6814
|
|
|
6707
|
-
/** Target → host:
|
|
6815
|
+
/** Target → host: settle the pending invoke for this ACTION_RESULT. */
|
|
6708
6816
|
private handleActionResult(targetPackageName: string, payload: Record<string, unknown>): void {
|
|
6709
|
-
|
|
6710
|
-
if (!callId) return
|
|
6711
|
-
const pending = this.actionCalls.get(callId)
|
|
6712
|
-
if (!pending) return
|
|
6713
|
-
// Only the invoked target may resolve this call. callIds are guessable
|
|
6714
|
-
// (timestamp + seq), so a different connected miniapp must not be able to
|
|
6715
|
-
// spoof a result/error for someone else's invoke.
|
|
6716
|
-
if (pending.targetPackageName !== targetPackageName) return
|
|
6717
|
-
this.actionCalls.delete(callId)
|
|
6718
|
-
BgTimer.clearTimeout(pending.timer)
|
|
6719
|
-
|
|
6720
|
-
if (payload.ok === true) {
|
|
6721
|
-
const result = payload.result
|
|
6722
|
-
if (this.actionPayloadTooLarge(result)) {
|
|
6723
|
-
this.sendResult(pending.callerPackageName, pending.callerRequestId, false, undefined, {
|
|
6724
|
-
code: MiniappErrorCode.PAYLOAD_TOO_LARGE,
|
|
6725
|
-
message: "action result exceeded the 256 KB cap",
|
|
6726
|
-
})
|
|
6727
|
-
return
|
|
6728
|
-
}
|
|
6729
|
-
this.sendResult(pending.callerPackageName, pending.callerRequestId, true, result ?? null)
|
|
6730
|
-
} else {
|
|
6731
|
-
const error = (payload.error as ({code: string; message: string} & Record<string, unknown>) | undefined) ?? {
|
|
6732
|
-
code: MiniappErrorCode.INTERNAL,
|
|
6733
|
-
message: "action handler error",
|
|
6734
|
-
}
|
|
6735
|
-
this.sendResult(pending.callerPackageName, pending.callerRequestId, false, undefined, error)
|
|
6736
|
-
}
|
|
6817
|
+
this.actionCalls.handleResult(targetPackageName, payload)
|
|
6737
6818
|
}
|
|
6738
6819
|
|
|
6739
6820
|
private sendResult(
|