@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.
@@ -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
- if (render.mode === "react-native") return render.bundleUrl || null;
24
- if (render.mode === "adaptive") return render.reactNative?.bundleUrl || null;
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 === "@adas/plugin-sdk") return PluginSDKShim;
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.loadIndex();
2507
- const entry = index[pluginId];
2607
+ const index = await loadCacheIndex(storage);
2608
+ const bundleUrl = pluginBundleUrl(pluginId);
2609
+ const entry = index[bundleUrl];
2508
2610
  if (entry) {
2509
- this.modules.delete(entry.bundleUrl);
2510
- delete index[pluginId];
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.loadIndex();
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.loadIndex();
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().loadIndex();
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-RXR6M623.mjs.map
3191
+ //# sourceMappingURL=chunk-SICFDA5K.mjs.map