@ateam-ai-mobile/plugin-runtime 0.1.8 → 0.1.9
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/dist/{chunk-RXR6M623.mjs → chunk-SICFDA5K.mjs} +121 -15
- package/dist/chunk-SICFDA5K.mjs.map +1 -0
- package/dist/index.d.mts +114 -18
- package/dist/index.d.ts +114 -18
- package/dist/index.js +142 -70
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10 -59
- package/dist/index.mjs.map +1 -1
- package/dist/{loader-UUOPZQSI.mjs → loader-MSZ4PVTO.mjs} +2 -2
- package/package.json +2 -2
- package/src/api/plugins.ts +15 -9
- package/src/bundles/bundleManager.ts +28 -9
- package/src/bundles/bundleModuleNames.ts +52 -0
- package/src/bundles/bundleUrl.ts +41 -0
- package/src/bundles/storage.ts +52 -0
- package/src/hooks/usePlugin.ts +2 -1
- package/src/index.ts +10 -36
- package/src/sdk/CameraPreview.tsx +7 -0
- package/src/sdk/device-state.ts +20 -7
- package/src/sdk/index.ts +34 -0
- package/src/sdk/types.ts +13 -0
- package/src/sdk/voice.ts +48 -4
- package/src/tools/loader.ts +4 -2
- package/src/tools/registry.ts +2 -2
- package/dist/chunk-RXR6M623.mjs.map +0 -1
- /package/dist/{loader-UUOPZQSI.mjs.map → loader-MSZ4PVTO.mjs.map} +0 -0
|
@@ -7,6 +7,23 @@ var __export = (target, all) => {
|
|
|
7
7
|
// src/api/plugins.ts
|
|
8
8
|
import { apiFetch } from "@ateam-ai-mobile/core";
|
|
9
9
|
import { getCoreApiUrl } from "@ateam-ai-mobile/core";
|
|
10
|
+
|
|
11
|
+
// src/bundles/bundleUrl.ts
|
|
12
|
+
function pluginBundleUrl(pluginId) {
|
|
13
|
+
return `/api/ui-plugins/${encodeURIComponent(pluginId)}/bundle.js`;
|
|
14
|
+
}
|
|
15
|
+
function isPluginBundleUrl(url, pluginId) {
|
|
16
|
+
if (typeof url !== "string") return false;
|
|
17
|
+
const canonical = pluginBundleUrl(pluginId);
|
|
18
|
+
if (url === canonical) return true;
|
|
19
|
+
try {
|
|
20
|
+
return decodeURIComponent(url) === decodeURIComponent(canonical);
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/api/plugins.ts
|
|
10
27
|
function supportsNative(manifest) {
|
|
11
28
|
const mode = manifest.render?.mode;
|
|
12
29
|
return mode === "react-native" || mode === "adaptive";
|
|
@@ -17,12 +34,9 @@ function getNativeComponentId(manifest) {
|
|
|
17
34
|
return null;
|
|
18
35
|
}
|
|
19
36
|
function getNativeBundleUrl(manifest) {
|
|
20
|
-
const direct = manifest.bundleUrl;
|
|
21
|
-
if (direct) return direct;
|
|
22
37
|
const render = manifest.render;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return null;
|
|
38
|
+
const declared = manifest.bundleUrl || (render?.mode === "react-native" ? render.bundleUrl : null) || (render?.mode === "adaptive" ? render.reactNative?.bundleUrl : null);
|
|
39
|
+
return declared && manifest.id ? pluginBundleUrl(manifest.id) : null;
|
|
26
40
|
}
|
|
27
41
|
function getNativeBundleHash(manifest) {
|
|
28
42
|
const direct = manifest.bundleHash;
|
|
@@ -155,6 +169,8 @@ __export(sdk_exports, {
|
|
|
155
169
|
TextInput: () => TextInput,
|
|
156
170
|
TouchableOpacity: () => TouchableOpacity,
|
|
157
171
|
View: () => View,
|
|
172
|
+
explainCapability: () => explainCapability,
|
|
173
|
+
isActionable: () => isActionable,
|
|
158
174
|
register: () => register,
|
|
159
175
|
registerService: () => registerService,
|
|
160
176
|
registerTools: () => registerTools,
|
|
@@ -197,6 +213,60 @@ function registerService(id, config) {
|
|
|
197
213
|
// src/sdk/voice.ts
|
|
198
214
|
var PLATFORM_VOICE_CALLER = /* @__PURE__ */ Symbol("adas.voice.platform");
|
|
199
215
|
|
|
216
|
+
// src/sdk/device-state.ts
|
|
217
|
+
var device_state_exports = {};
|
|
218
|
+
__export(device_state_exports, {
|
|
219
|
+
DeviceBridgeError: () => DeviceBridgeError,
|
|
220
|
+
explainCapability: () => explainCapability,
|
|
221
|
+
isActionable: () => isActionable,
|
|
222
|
+
neverObserved: () => neverObserved
|
|
223
|
+
});
|
|
224
|
+
var DeviceBridgeError = class extends Error {
|
|
225
|
+
constructor(code, message, opts) {
|
|
226
|
+
super(message);
|
|
227
|
+
this.name = "DeviceBridgeError";
|
|
228
|
+
this.code = code;
|
|
229
|
+
this.domain = opts?.domain;
|
|
230
|
+
this.recoverable = opts?.recoverable;
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
var CONFIDENCE_ORDER = {
|
|
234
|
+
unknown: 0,
|
|
235
|
+
low: 1,
|
|
236
|
+
medium: 2,
|
|
237
|
+
high: 3
|
|
238
|
+
};
|
|
239
|
+
function isActionable(quality, budget) {
|
|
240
|
+
const { freshness, confidence } = quality;
|
|
241
|
+
if (freshness.state === "unknown" || freshness.state === "stale") return false;
|
|
242
|
+
if (freshness.ageMs === null || freshness.ageMs > budget.maxAgeMs) return false;
|
|
243
|
+
if (budget.minConfidence) {
|
|
244
|
+
const have = CONFIDENCE_ORDER[confidence?.level ?? "unknown"];
|
|
245
|
+
if (have < CONFIDENCE_ORDER[budget.minConfidence]) return false;
|
|
246
|
+
}
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
function neverObserved() {
|
|
250
|
+
return { state: "unknown", observedAt: null, ageMs: null };
|
|
251
|
+
}
|
|
252
|
+
function explainCapability(status) {
|
|
253
|
+
if (status.support === "unsupported") return "this device does not have it";
|
|
254
|
+
if (status.permission === "denied") return "permission was denied";
|
|
255
|
+
if (status.permission === "restricted") return "permission is restricted";
|
|
256
|
+
if (status.permission === "notDetermined") return "permission has not been asked for yet";
|
|
257
|
+
if (status.permission === "limited") return "only limited access was granted";
|
|
258
|
+
if (status.restriction === "policyRestricted") return "this plugin did not declare it";
|
|
259
|
+
if (status.restriction === "backgroundRestricted") return "the app is in the background";
|
|
260
|
+
if (status.restriction === "lowPowerRestricted") return "the device is in low-power mode";
|
|
261
|
+
if (status.availability === "busy") return "it is in use by something else";
|
|
262
|
+
if (status.availability === "temporarilyUnavailable") return "it is temporarily unavailable";
|
|
263
|
+
if (status.availability === "unavailable") return "it is unavailable";
|
|
264
|
+
if (status.support === "unknown" || status.availability === "unknown") {
|
|
265
|
+
return "its state could not be determined";
|
|
266
|
+
}
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
|
|
200
270
|
// src/sdk/primitives.ts
|
|
201
271
|
import {
|
|
202
272
|
View,
|
|
@@ -1563,6 +1633,14 @@ function SurfaceDispatcher() {
|
|
|
1563
1633
|
] });
|
|
1564
1634
|
}
|
|
1565
1635
|
|
|
1636
|
+
// src/bundles/bundleModuleNames.ts
|
|
1637
|
+
var PLUGIN_SDK_MODULE = "@adas/plugin-sdk";
|
|
1638
|
+
function sdkModuleRefusal(name) {
|
|
1639
|
+
if (name === PLUGIN_SDK_MODULE) return null;
|
|
1640
|
+
if (!name.startsWith("@ateam-ai-mobile/") && !name.startsWith("@adas/")) return null;
|
|
1641
|
+
return `a plugin bundle required '${name}', which does not exist inside a bundle. Import from '${PLUGIN_SDK_MODULE}' instead \u2014 it is the only SDK module a bundle can require (values and types alike), and the one to keep external when building. '@ateam-ai-mobile/*' are the host app's packages, not the plugin's.`;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1566
1644
|
// src/utils/sha256.ts
|
|
1567
1645
|
var K = new Uint32Array([
|
|
1568
1646
|
1116352408,
|
|
@@ -1717,6 +1795,22 @@ function selectOwnedCacheEntries(entries, tenant) {
|
|
|
1717
1795
|
if (!tenant) return [];
|
|
1718
1796
|
return (entries || []).filter((e) => !!e && e.tenant === tenant);
|
|
1719
1797
|
}
|
|
1798
|
+
async function loadCacheIndex(storage = getBundleStorage()) {
|
|
1799
|
+
const index = await storage.loadIndex() || {};
|
|
1800
|
+
for (const [key, entry] of Object.entries(index)) {
|
|
1801
|
+
const pluginId = entry?.trusted?.pluginId;
|
|
1802
|
+
if (!pluginId) continue;
|
|
1803
|
+
const canonical = pluginBundleUrl(pluginId);
|
|
1804
|
+
if (key === canonical) continue;
|
|
1805
|
+
if (!isPluginBundleUrl(key, pluginId) || !isPluginBundleUrl(entry.bundleUrl, pluginId)) continue;
|
|
1806
|
+
delete index[key];
|
|
1807
|
+
const held = index[canonical];
|
|
1808
|
+
if (!held || (entry.timestamp ?? 0) > (held.timestamp ?? 0)) {
|
|
1809
|
+
index[canonical] = { ...entry, bundleUrl: canonical };
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
return index;
|
|
1813
|
+
}
|
|
1720
1814
|
var MemoryBundleStorage = class {
|
|
1721
1815
|
constructor() {
|
|
1722
1816
|
this.index = {};
|
|
@@ -2215,6 +2309,9 @@ function clearDeviceTools() {
|
|
|
2215
2309
|
}
|
|
2216
2310
|
|
|
2217
2311
|
// src/bundles/bundleManager.ts
|
|
2312
|
+
function bundleKey(pluginId, bundleUrl) {
|
|
2313
|
+
return isPluginBundleUrl(bundleUrl, pluginId) ? pluginBundleUrl(pluginId) : bundleUrl;
|
|
2314
|
+
}
|
|
2218
2315
|
var ReactShim = {
|
|
2219
2316
|
__esModule: true,
|
|
2220
2317
|
default: React2,
|
|
@@ -2260,7 +2357,9 @@ var PluginSDKShim = Object.freeze({
|
|
|
2260
2357
|
var requireShim = (name) => {
|
|
2261
2358
|
if (name === "react") return ReactShim;
|
|
2262
2359
|
if (name === "react-native") return ReactNative;
|
|
2263
|
-
if (name ===
|
|
2360
|
+
if (name === PLUGIN_SDK_MODULE) return PluginSDKShim;
|
|
2361
|
+
const refusal = sdkModuleRefusal(name);
|
|
2362
|
+
if (refusal) throw new Error(`[BundleManager] ${refusal}`);
|
|
2264
2363
|
const hostModule = resolveBundleModule(name);
|
|
2265
2364
|
if (hostModule !== void 0) return hostModule;
|
|
2266
2365
|
console.warn(`[BundleManager] Unknown module: ${name} \u2014 register it via registerBundleModules()`);
|
|
@@ -2388,6 +2487,7 @@ var BundleManager = class {
|
|
|
2388
2487
|
* manifest-driven tool loader already made.
|
|
2389
2488
|
*/
|
|
2390
2489
|
async load(pluginId, bundleUrl, version, expectedHash, grants) {
|
|
2490
|
+
bundleUrl = bundleKey(pluginId, bundleUrl);
|
|
2391
2491
|
const tenant = this.requireTenant();
|
|
2392
2492
|
const epoch = this.epoch;
|
|
2393
2493
|
const cached = this.getComponentFromMemory(pluginId, bundleUrl);
|
|
@@ -2433,6 +2533,7 @@ var BundleManager = class {
|
|
|
2433
2533
|
if (!grants) {
|
|
2434
2534
|
throw new Error(`[BundleManager] loadTools(${pluginId}) needs the manifest's grants (resolvePluginGrants) \u2014 refusing to register tools on the bundle's own claim`);
|
|
2435
2535
|
}
|
|
2536
|
+
bundleUrl = bundleKey(pluginId, bundleUrl);
|
|
2436
2537
|
const tenant = this.requireTenant();
|
|
2437
2538
|
const epoch = this.epoch;
|
|
2438
2539
|
let modulePromise = this.inflight.get(bundleUrl);
|
|
@@ -2503,11 +2604,12 @@ var BundleManager = class {
|
|
|
2503
2604
|
*/
|
|
2504
2605
|
async clearPlugin(pluginId) {
|
|
2505
2606
|
const storage = getBundleStorage();
|
|
2506
|
-
const index = await storage
|
|
2507
|
-
const
|
|
2607
|
+
const index = await loadCacheIndex(storage);
|
|
2608
|
+
const bundleUrl = pluginBundleUrl(pluginId);
|
|
2609
|
+
const entry = index[bundleUrl];
|
|
2508
2610
|
if (entry) {
|
|
2509
|
-
this.modules.delete(
|
|
2510
|
-
delete index[
|
|
2611
|
+
this.modules.delete(bundleUrl);
|
|
2612
|
+
delete index[bundleUrl];
|
|
2511
2613
|
await storage.saveIndex(index);
|
|
2512
2614
|
try {
|
|
2513
2615
|
await storage.deleteBundle(entry.localPath);
|
|
@@ -2536,7 +2638,7 @@ var BundleManager = class {
|
|
|
2536
2638
|
return;
|
|
2537
2639
|
}
|
|
2538
2640
|
const storage = getBundleStorage();
|
|
2539
|
-
const index = await storage
|
|
2641
|
+
const index = await loadCacheIndex(storage);
|
|
2540
2642
|
const expectedLc = expectedHash ? expectedHash.toLowerCase() : null;
|
|
2541
2643
|
let content = null;
|
|
2542
2644
|
let verifiedHash = null;
|
|
@@ -2671,7 +2773,7 @@ var BundleManager = class {
|
|
|
2671
2773
|
if (this.recordedDeclarations.get(bundleUrl) === key) return;
|
|
2672
2774
|
try {
|
|
2673
2775
|
const storage = getBundleStorage();
|
|
2674
|
-
const index = await storage
|
|
2776
|
+
const index = await loadCacheIndex(storage);
|
|
2675
2777
|
const entry = index[bundleUrl];
|
|
2676
2778
|
const running = this.provenance.get(bundleUrl)?.hash;
|
|
2677
2779
|
if (!entry || entry.tenant !== tenant || !running || entry.bundleHash?.toLowerCase() !== running) return;
|
|
@@ -2960,7 +3062,7 @@ async function ensureDeviceToolsLoaded({ force = false } = {}) {
|
|
|
2960
3062
|
}
|
|
2961
3063
|
async function loadFromDiskCache() {
|
|
2962
3064
|
try {
|
|
2963
|
-
const index = await getBundleStorage()
|
|
3065
|
+
const index = await loadCacheIndex(getBundleStorage());
|
|
2964
3066
|
const entries = Object.values(index || {});
|
|
2965
3067
|
if (entries.length === 0) return [];
|
|
2966
3068
|
const tenant = getTenant3();
|
|
@@ -2997,11 +3099,14 @@ function resetDeviceToolLoader() {
|
|
|
2997
3099
|
}
|
|
2998
3100
|
|
|
2999
3101
|
export {
|
|
3000
|
-
__export,
|
|
3001
3102
|
register,
|
|
3002
3103
|
registerTools,
|
|
3003
3104
|
registerService,
|
|
3004
3105
|
PLATFORM_VOICE_CALLER,
|
|
3106
|
+
isActionable,
|
|
3107
|
+
neverObserved,
|
|
3108
|
+
explainCapability,
|
|
3109
|
+
device_state_exports,
|
|
3005
3110
|
View,
|
|
3006
3111
|
Text,
|
|
3007
3112
|
ScrollView,
|
|
@@ -3060,6 +3165,7 @@ export {
|
|
|
3060
3165
|
listDeviceTools,
|
|
3061
3166
|
callDeviceTool,
|
|
3062
3167
|
clearDeviceTools,
|
|
3168
|
+
pluginBundleUrl,
|
|
3063
3169
|
supportsNative,
|
|
3064
3170
|
getNativeComponentId,
|
|
3065
3171
|
getNativeBundleUrl,
|
|
@@ -3082,4 +3188,4 @@ export {
|
|
|
3082
3188
|
ensureDeviceToolsLoaded,
|
|
3083
3189
|
resetDeviceToolLoader
|
|
3084
3190
|
};
|
|
3085
|
-
//# sourceMappingURL=chunk-
|
|
3191
|
+
//# sourceMappingURL=chunk-SICFDA5K.mjs.map
|