@glassly/engine 0.3.5-beta.710 → 0.3.5-beta.760
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 +4 -1
- package/build/engine.d.ts.map +1 -1
- package/build/engine.js +5 -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 +5 -0
- package/build/facades/glasses.js.map +1 -1
- package/build/generated/releaseMetadata.js +3 -3
- package/build/generated/releaseMetadata.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 +76 -37
- 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.map +1 -1
- package/build/services/LocalLlmProvider.js +39 -20
- package/build/services/LocalLlmProvider.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +22 -4
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +108 -134
- package/build/services/LocalMiniappRuntime.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/SceneRenderer.d.ts.map +1 -1
- package/build/services/SceneRenderer.js +3 -1
- package/build/services/SceneRenderer.js.map +1 -1
- 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/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/stores/apps.d.ts.map +1 -1
- package/build/stores/apps.js +13 -11
- 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 +32 -2
- package/build/stores/settings.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/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/package.json +7 -7
- package/src/engine.ts +5 -1
- package/src/facades/ai.ts +3 -3
- package/src/facades/glasses.ts +7 -0
- package/src/generated/releaseMetadata.ts +3 -3
- package/src/services/GestureDotsService.ts +2 -1
- package/src/services/GlassesTouchInput.ts +81 -34
- 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 +49 -22
- package/src/services/LocalMiniappRuntime.ts +140 -134
- package/src/services/MiniappExitGesture.ts +8 -5
- package/src/services/MiniappWidgetMode.ts +9 -7
- package/src/services/SceneRenderer.ts +3 -1
- 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/settingsBridge.ts +9 -4
- package/src/stores/apps.ts +12 -10
- package/src/stores/bluetoothSettingKeys.ts +20 -0
- package/src/stores/settings.ts +34 -2
- package/src/types/capabilities/rayneo-io.ts +2 -0
- package/src/types/enums.ts +1 -0
- 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
|
@@ -82,6 +82,7 @@ import { invokeScanQrSeam } from "../runtime/scanQrSeam";
|
|
|
82
82
|
import { invokeWebLoginSeam } from "../runtime/webLoginSeam";
|
|
83
83
|
import { normalizeStreamAudioConfig, normalizeStreamVideoConfig } from "../runtime/streamConfig";
|
|
84
84
|
import { visibleActionRows } from "./manifestActions";
|
|
85
|
+
import { actionFailure, appDeclaresAction, checkActionAccess, PendingActionCalls, } from "./actionInvoke";
|
|
85
86
|
import { toLanguageHint } from "@glassly/cloud-protocol/languages";
|
|
86
87
|
import { buildMiniappManifestSnapshot } from "../utils/miniappDiagnostics";
|
|
87
88
|
import ttsModelManager from "./TTSModelManager";
|
|
@@ -438,6 +439,10 @@ class LocalMiniappRuntime {
|
|
|
438
439
|
connectedApps = new Map();
|
|
439
440
|
/** Claimed gestures (CLAIM_GESTURES): a stack per gesture, the most recent claimer holding it. */
|
|
440
441
|
gestureClaims = emptyGestureClaims();
|
|
442
|
+
// The last main-view frame each app asked to render. Widget mode refuses
|
|
443
|
+
// frames and demotion clears them, and an app that has drawn once may not
|
|
444
|
+
// draw again on its own, so promotion replays this.
|
|
445
|
+
retainedRenders = new Map();
|
|
441
446
|
/**
|
|
442
447
|
* Packages that have completed their CONNECT handshake (sent
|
|
443
448
|
* `miniapp_connect`, got `CONNECT_ACK`). Distinct from `connectedApps`,
|
|
@@ -490,7 +495,14 @@ class LocalMiniappRuntime {
|
|
|
490
495
|
// secret never leaves the device.
|
|
491
496
|
localSecret = `miniapp_${Date.now()}_${Math.random().toString(36).slice(2, 14)}`;
|
|
492
497
|
usedTokens = new Set();
|
|
493
|
-
constructor() {
|
|
498
|
+
constructor() {
|
|
499
|
+
miniappWidgetMode.subscribe((packageName, widgetOnly) => {
|
|
500
|
+
// Promotion happens inside the apps store's start(); the core-app slot
|
|
501
|
+
// settles with that store write, so replay once it has.
|
|
502
|
+
if (!widgetOnly)
|
|
503
|
+
queueMicrotask(() => this.replayRetainedRender(packageName));
|
|
504
|
+
});
|
|
505
|
+
}
|
|
494
506
|
/** Snapshot of miniapps actively subscribed to hardware button events. */
|
|
495
507
|
getButtonPressSubscribers() {
|
|
496
508
|
return [...(this.streamSubscribers.get(MiniappStreamType.BUTTON_PRESS) ?? [])].sort();
|
|
@@ -874,7 +886,11 @@ class LocalMiniappRuntime {
|
|
|
874
886
|
// window ends early once this app pushes its first display (see
|
|
875
887
|
// LocalDisplayManager.request), or after ~1.5s. Fires once per spawn —
|
|
876
888
|
// including a crash-respawn, since each spawn is a fresh "app is starting".
|
|
877
|
-
|
|
889
|
+
// A spawn for a dashboard widget is not the wearer starting the app: it draws
|
|
890
|
+
// nothing of its own, so it shows no boot text either.
|
|
891
|
+
if (!miniappWidgetMode.isWidgetOnly(packageName)) {
|
|
892
|
+
localDisplayManager.onMount(packageName, installedManifest?.name ?? packageName);
|
|
893
|
+
}
|
|
878
894
|
}
|
|
879
895
|
/**
|
|
880
896
|
* Resolve once `packageName` has completed its CONNECT handshake. If it has
|
|
@@ -996,6 +1012,7 @@ class LocalMiniappRuntime {
|
|
|
996
1012
|
}
|
|
997
1013
|
// Its gesture claims go back to whoever claimed them before.
|
|
998
1014
|
this.applyGestureClaimChanges(dropGestureClaims(this.gestureClaims, packageName));
|
|
1015
|
+
this.retainedRenders.delete(packageName);
|
|
999
1016
|
// Warm-up is a request-owned camera lease. Cancel it even if the app record was already
|
|
1000
1017
|
// removed so a close racing the camera-open promise cannot leave the sensor running.
|
|
1001
1018
|
// Keep camera teardown ordered: restoring FOV can restart the HAL, so the warm-up lease must
|
|
@@ -2124,11 +2141,6 @@ class LocalMiniappRuntime {
|
|
|
2124
2141
|
this.sendResult(packageName, requestId, true, { status: "blocked", reason: "app stopped" });
|
|
2125
2142
|
return;
|
|
2126
2143
|
}
|
|
2127
|
-
// A widget-only app draws through its widget alone.
|
|
2128
|
-
if (miniappWidgetMode.isWidgetOnly(packageName)) {
|
|
2129
|
-
this.sendResult(packageName, requestId, true, { status: "blocked", reason: "widget mode" });
|
|
2130
|
-
return;
|
|
2131
|
-
}
|
|
2132
2144
|
if (!Array.isArray(payload.elements)) {
|
|
2133
2145
|
this.sendResult(packageName, requestId, false, undefined, {
|
|
2134
2146
|
code: MiniappErrorCode.INTERNAL,
|
|
@@ -2136,6 +2148,17 @@ class LocalMiniappRuntime {
|
|
|
2136
2148
|
});
|
|
2137
2149
|
return;
|
|
2138
2150
|
}
|
|
2151
|
+
if (payload.view !== "dashboard") {
|
|
2152
|
+
if (payload.elements.length === 0)
|
|
2153
|
+
this.retainedRenders.delete(packageName);
|
|
2154
|
+
else
|
|
2155
|
+
this.retainedRenders.set(packageName, payload);
|
|
2156
|
+
}
|
|
2157
|
+
// A widget-only app draws through its widget alone; the frame waits for promotion.
|
|
2158
|
+
if (miniappWidgetMode.isWidgetOnly(packageName)) {
|
|
2159
|
+
this.sendResult(packageName, requestId, true, { status: "blocked", reason: "widget mode" });
|
|
2160
|
+
return;
|
|
2161
|
+
}
|
|
2139
2162
|
// The dashboard view is the overlay app's alone; anyone else's frame
|
|
2140
2163
|
// is dropped, reported rather than silently lost.
|
|
2141
2164
|
if (payload.view === "dashboard" && !isOverlayApp(packageName)) {
|
|
@@ -2160,6 +2183,13 @@ class LocalMiniappRuntime {
|
|
|
2160
2183
|
});
|
|
2161
2184
|
}
|
|
2162
2185
|
}
|
|
2186
|
+
/** Put a promoted app's last main-view frame on the glasses; widget mode had refused it. */
|
|
2187
|
+
replayRetainedRender(packageName) {
|
|
2188
|
+
const payload = this.retainedRenders.get(packageName);
|
|
2189
|
+
if (!payload || !this.connectedApps.has(packageName) || miniappWidgetMode.isWidgetOnly(packageName))
|
|
2190
|
+
return;
|
|
2191
|
+
this.handleRender(packageName, payload);
|
|
2192
|
+
}
|
|
2163
2193
|
/**
|
|
2164
2194
|
* display.showView: the active dashboard takes or releases the overlay
|
|
2165
2195
|
* slot. Refused (NOT_PERMITTED) for an app that is not a dashboard;
|
|
@@ -5224,7 +5254,7 @@ class LocalMiniappRuntime {
|
|
|
5224
5254
|
/** Max serialized size of an action call's params / result (256 KB). */
|
|
5225
5255
|
static ACTION_PAYLOAD_CAP = 256 * 1024;
|
|
5226
5256
|
/** Outstanding action invocations, keyed by host-generated callId. */
|
|
5227
|
-
actionCalls = new
|
|
5257
|
+
actionCalls = new PendingActionCalls(BgTimer, (value) => this.actionPayloadTooLarge(value));
|
|
5228
5258
|
actionCallSeq = 0;
|
|
5229
5259
|
interopApps() {
|
|
5230
5260
|
return useAppStatusStore.getState().apps;
|
|
@@ -5564,10 +5594,12 @@ class LocalMiniappRuntime {
|
|
|
5564
5594
|
}
|
|
5565
5595
|
const provider = app.packageName;
|
|
5566
5596
|
// Record the placement before the wake so the spawn lands in widget mode
|
|
5567
|
-
// and an unplace racing it cannot stop the provider early.
|
|
5597
|
+
// and an unplace racing it cannot stop the provider early. A provider the
|
|
5598
|
+
// store already runs (a wearer's launch still spawning, say) stays a
|
|
5599
|
+
// normal run; the registry alone lags that launch.
|
|
5568
5600
|
this.widgetPlacements = placeWidget(this.widgetPlacements, { hostPackageName, providerPackageName: provider, widgetId, box });
|
|
5569
5601
|
miniappWidgetMode.setPlaced(provider, true);
|
|
5570
|
-
if (!miniappRunningRegistry.has(provider))
|
|
5602
|
+
if (!app.running && !miniappRunningRegistry.has(provider))
|
|
5571
5603
|
miniappWidgetMode.setWidgetOnly(provider, true);
|
|
5572
5604
|
try {
|
|
5573
5605
|
await this.wakeInteropApp(provider);
|
|
@@ -5754,104 +5786,82 @@ class LocalMiniappRuntime {
|
|
|
5754
5786
|
return false;
|
|
5755
5787
|
}
|
|
5756
5788
|
}
|
|
5789
|
+
/** 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. */
|
|
5790
|
+
static clampActionTimeout(raw) {
|
|
5791
|
+
return Math.min(Math.max(Number(raw) || 30_000, 6_000), 120_000);
|
|
5792
|
+
}
|
|
5793
|
+
/** Caller identity the host presents for host-initiated actions (a system caller). */
|
|
5794
|
+
static HOST_ACTION_CALLER = "host";
|
|
5795
|
+
/** Installed, compatible, and declares the action — the same gates `invokeActionFromHost` applies up front. */
|
|
5796
|
+
hostActionAvailable(targetPackageName, actionId) {
|
|
5797
|
+
return appDeclaresAction(this.interopApps().find((a) => a.packageName === targetPackageName), actionId);
|
|
5798
|
+
}
|
|
5757
5799
|
/**
|
|
5758
|
-
*
|
|
5759
|
-
* and
|
|
5800
|
+
* Host-initiated action call (e.g. the watch remote): same gates, headless
|
|
5801
|
+
* wake and ACTION_CALL/ACTION_RESULT correlation as a miniapp's
|
|
5802
|
+
* session.actions.invoke, with the host as a system caller.
|
|
5803
|
+
* Rejects with an Error whose `code` is a MiniappErrorCode.
|
|
5760
5804
|
*/
|
|
5805
|
+
async invokeActionFromHost(targetPackageName, actionId, params = {}, timeoutMs) {
|
|
5806
|
+
const outcome = await this.runActionInvoke(LocalMiniappRuntime.HOST_ACTION_CALLER, true, targetPackageName, actionId, params, LocalMiniappRuntime.clampActionTimeout(timeoutMs));
|
|
5807
|
+
if (outcome.ok)
|
|
5808
|
+
return outcome.data;
|
|
5809
|
+
throw Object.assign(new Error(outcome.error.message), { code: outcome.error.code });
|
|
5810
|
+
}
|
|
5811
|
+
/** session.actions.invoke → run the call, then relay the outcome to the caller's pending request. */
|
|
5761
5812
|
async handleActionInvoke(callerPackageName, payload, requestId) {
|
|
5762
|
-
const
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5813
|
+
const outcome = await this.runActionInvoke(callerPackageName, this.isSystemPackage(callerPackageName), payload.targetPackageName, payload.actionId, payload.params ?? {}, LocalMiniappRuntime.clampActionTimeout(payload.timeoutMs));
|
|
5814
|
+
if (outcome.ok)
|
|
5815
|
+
this.sendResult(callerPackageName, requestId, true, outcome.data);
|
|
5816
|
+
else
|
|
5817
|
+
this.sendResult(callerPackageName, requestId, false, undefined, outcome.error);
|
|
5818
|
+
}
|
|
5819
|
+
/**
|
|
5820
|
+
* Gate, wake the target headlessly, deliver an ACTION_CALL, and resolve when
|
|
5821
|
+
* its ACTION_RESULT (or the timeout) arrives. Never rejects.
|
|
5822
|
+
*/
|
|
5823
|
+
async runActionInvoke(callerPackageName, callerIsSystem, target, actionId, params, timeoutMs) {
|
|
5824
|
+
const fail = actionFailure;
|
|
5773
5825
|
if (this.actionPayloadTooLarge(params)) {
|
|
5774
|
-
|
|
5775
|
-
code: MiniappErrorCode.PAYLOAD_TOO_LARGE,
|
|
5776
|
-
message: "action params exceeded the 256 KB cap",
|
|
5777
|
-
});
|
|
5778
|
-
return;
|
|
5826
|
+
return fail(MiniappErrorCode.PAYLOAD_TOO_LARGE, "action params exceeded the 256 KB cap");
|
|
5779
5827
|
}
|
|
5780
5828
|
const app = target ? this.interopApps().find((a) => a.packageName === target) : undefined;
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
reason: `${target} is not compatible with the connected glasses`,
|
|
5804
|
-
metadata: { missingRequired: app.compatibility.missingRequired ?? [], via: "miniapps.invoke" },
|
|
5805
|
-
timestamp: Date.now(),
|
|
5806
|
-
});
|
|
5807
|
-
return;
|
|
5808
|
-
}
|
|
5809
|
-
// Declared-action gate: the target must declare the action in its manifest.
|
|
5810
|
-
const declared = (app.actions ?? []).find((a) => a.id === actionId);
|
|
5811
|
-
if (!declared) {
|
|
5812
|
-
this.sendResult(callerPackageName, requestId, false, undefined, {
|
|
5813
|
-
code: MiniappErrorCode.ACTION_NOT_FOUND,
|
|
5814
|
-
message: `${target} does not declare action "${actionId}"`,
|
|
5815
|
-
});
|
|
5816
|
-
return;
|
|
5817
|
-
}
|
|
5818
|
-
// Visibility gate: declared actions are public by default; ones marked
|
|
5819
|
-
// `visibility: "system"` stay restricted to system/dev callers.
|
|
5820
|
-
if (declared.visibility === "system" && !this.isSystemPackage(callerPackageName)) {
|
|
5821
|
-
this.auditInterop({
|
|
5822
|
-
caller: callerPackageName,
|
|
5823
|
-
op: "invoke",
|
|
5824
|
-
target,
|
|
5825
|
-
actionId,
|
|
5826
|
-
ok: false,
|
|
5827
|
-
errorCode: MiniappErrorCode.NOT_PERMITTED,
|
|
5828
|
-
});
|
|
5829
|
-
this.sendResult(callerPackageName, requestId, false, undefined, {
|
|
5830
|
-
code: MiniappErrorCode.NOT_PERMITTED,
|
|
5831
|
-
message: `Action "${actionId}" on ${target} is restricted to system apps`,
|
|
5832
|
-
});
|
|
5833
|
-
return;
|
|
5829
|
+
const denied = checkActionAccess(app, target, actionId, callerIsSystem);
|
|
5830
|
+
if (denied) {
|
|
5831
|
+
if (denied.code === MiniappErrorCode.APP_NOT_COMPATIBLE && target && app) {
|
|
5832
|
+
islandNotifications.emit({
|
|
5833
|
+
kind: "version_incompatible",
|
|
5834
|
+
packageName: target,
|
|
5835
|
+
reason: denied.message,
|
|
5836
|
+
metadata: { missingRequired: app.compatibility?.missingRequired ?? [], via: "miniapps.invoke" },
|
|
5837
|
+
timestamp: Date.now(),
|
|
5838
|
+
});
|
|
5839
|
+
}
|
|
5840
|
+
else if (denied.code === MiniappErrorCode.NOT_PERMITTED) {
|
|
5841
|
+
this.auditInterop({
|
|
5842
|
+
caller: callerPackageName,
|
|
5843
|
+
op: "invoke",
|
|
5844
|
+
target,
|
|
5845
|
+
actionId,
|
|
5846
|
+
ok: false,
|
|
5847
|
+
errorCode: MiniappErrorCode.NOT_PERMITTED,
|
|
5848
|
+
});
|
|
5849
|
+
}
|
|
5850
|
+
return { ok: false, error: denied };
|
|
5834
5851
|
}
|
|
5852
|
+
if (!target || !actionId)
|
|
5853
|
+
return fail(MiniappErrorCode.INTERNAL, "unreachable");
|
|
5835
5854
|
// Headless wake + wait for CONNECT (idempotent / fast if already connected).
|
|
5836
5855
|
try {
|
|
5837
5856
|
await this.wakeInteropApp(target);
|
|
5838
5857
|
}
|
|
5839
5858
|
catch (e) {
|
|
5840
|
-
|
|
5841
|
-
code: MiniappErrorCode.WAKE_FAILED,
|
|
5842
|
-
message: e?.message ?? `failed to wake ${target}`,
|
|
5843
|
-
});
|
|
5844
|
-
return;
|
|
5859
|
+
return fail(MiniappErrorCode.WAKE_FAILED, e?.message ?? `failed to wake ${target}`);
|
|
5845
5860
|
}
|
|
5846
5861
|
// The wake waited for CONNECT, but the target could have dropped in the gap
|
|
5847
5862
|
// before delivery — fail fast rather than arming a timer for a call that
|
|
5848
|
-
// sendToMiniapp would silently drop
|
|
5849
|
-
// full invoke timeout for an ACTION_CALL that was never delivered).
|
|
5863
|
+
// sendToMiniapp would silently drop.
|
|
5850
5864
|
if (!this.connectedApps.has(target)) {
|
|
5851
|
-
this.sendResult(callerPackageName, requestId, false, undefined, {
|
|
5852
|
-
code: MiniappErrorCode.WAKE_FAILED,
|
|
5853
|
-
message: `${target} disconnected before the action could be delivered`,
|
|
5854
|
-
});
|
|
5855
5865
|
this.auditInterop({
|
|
5856
5866
|
caller: callerPackageName,
|
|
5857
5867
|
op: "invoke",
|
|
@@ -5860,18 +5870,11 @@ class LocalMiniappRuntime {
|
|
|
5860
5870
|
ok: false,
|
|
5861
5871
|
errorCode: MiniappErrorCode.WAKE_FAILED,
|
|
5862
5872
|
});
|
|
5863
|
-
return;
|
|
5873
|
+
return fail(MiniappErrorCode.WAKE_FAILED, `${target} disconnected before the action could be delivered`);
|
|
5864
5874
|
}
|
|
5865
|
-
// Deliver the call and arm the handler timeout; ACTION_RESULT
|
|
5875
|
+
// Deliver the call and arm the handler timeout; ACTION_RESULT settles it.
|
|
5866
5876
|
const callId = `act-${Date.now().toString(36)}-${this.actionCallSeq++}`;
|
|
5867
|
-
const
|
|
5868
|
-
this.actionCalls.delete(callId);
|
|
5869
|
-
this.sendResult(callerPackageName, requestId, false, undefined, {
|
|
5870
|
-
code: MiniappErrorCode.ACTION_TIMEOUT,
|
|
5871
|
-
message: `action "${actionId}" timed out after ${timeoutMs}ms`,
|
|
5872
|
-
});
|
|
5873
|
-
}, timeoutMs);
|
|
5874
|
-
this.actionCalls.set(callId, { callerPackageName, targetPackageName: target, callerRequestId: requestId, timer });
|
|
5877
|
+
const outcome = this.actionCalls.register(callId, target, actionId, timeoutMs);
|
|
5875
5878
|
this.sendToMiniapp(target, {
|
|
5876
5879
|
type: MiniappResponseType.ACTION_CALL,
|
|
5877
5880
|
callId,
|
|
@@ -5880,40 +5883,11 @@ class LocalMiniappRuntime {
|
|
|
5880
5883
|
callerPackageName,
|
|
5881
5884
|
});
|
|
5882
5885
|
this.auditInterop({ caller: callerPackageName, op: "invoke", target, actionId, ok: true });
|
|
5886
|
+
return outcome;
|
|
5883
5887
|
}
|
|
5884
|
-
/** Target → host:
|
|
5888
|
+
/** Target → host: settle the pending invoke for this ACTION_RESULT. */
|
|
5885
5889
|
handleActionResult(targetPackageName, payload) {
|
|
5886
|
-
|
|
5887
|
-
if (!callId)
|
|
5888
|
-
return;
|
|
5889
|
-
const pending = this.actionCalls.get(callId);
|
|
5890
|
-
if (!pending)
|
|
5891
|
-
return;
|
|
5892
|
-
// Only the invoked target may resolve this call. callIds are guessable
|
|
5893
|
-
// (timestamp + seq), so a different connected miniapp must not be able to
|
|
5894
|
-
// spoof a result/error for someone else's invoke.
|
|
5895
|
-
if (pending.targetPackageName !== targetPackageName)
|
|
5896
|
-
return;
|
|
5897
|
-
this.actionCalls.delete(callId);
|
|
5898
|
-
BgTimer.clearTimeout(pending.timer);
|
|
5899
|
-
if (payload.ok === true) {
|
|
5900
|
-
const result = payload.result;
|
|
5901
|
-
if (this.actionPayloadTooLarge(result)) {
|
|
5902
|
-
this.sendResult(pending.callerPackageName, pending.callerRequestId, false, undefined, {
|
|
5903
|
-
code: MiniappErrorCode.PAYLOAD_TOO_LARGE,
|
|
5904
|
-
message: "action result exceeded the 256 KB cap",
|
|
5905
|
-
});
|
|
5906
|
-
return;
|
|
5907
|
-
}
|
|
5908
|
-
this.sendResult(pending.callerPackageName, pending.callerRequestId, true, result ?? null);
|
|
5909
|
-
}
|
|
5910
|
-
else {
|
|
5911
|
-
const error = payload.error ?? {
|
|
5912
|
-
code: MiniappErrorCode.INTERNAL,
|
|
5913
|
-
message: "action handler error",
|
|
5914
|
-
};
|
|
5915
|
-
this.sendResult(pending.callerPackageName, pending.callerRequestId, false, undefined, error);
|
|
5916
|
-
}
|
|
5890
|
+
this.actionCalls.handleResult(targetPackageName, payload);
|
|
5917
5891
|
}
|
|
5918
5892
|
sendResult(packageName, requestId, ok, data, error) {
|
|
5919
5893
|
if (!requestId)
|