@ateam-ai-mobile/plugin-runtime 0.1.5 → 0.1.8
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 +3085 -0
- package/dist/chunk-RXR6M623.mjs.map +1 -0
- package/dist/index.d.mts +2083 -12
- package/dist/index.d.ts +2083 -12
- package/dist/index.js +2605 -1332
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +265 -2157
- package/dist/index.mjs.map +1 -1
- package/dist/loader-UUOPZQSI.mjs +9 -0
- package/dist/loader-UUOPZQSI.mjs.map +1 -0
- package/package.json +2 -2
- package/src/bundles/bundleManager.ts +496 -51
- package/src/bundles/integrity.ts +29 -0
- package/src/bundles/storage.ts +59 -0
- package/src/deviceStateHost.ts +222 -0
- package/src/hooks/usePlugin.ts +14 -4
- package/src/host.ts +82 -8
- package/src/index.ts +96 -2
- package/src/pluginRuntime.ts +108 -0
- package/src/sdk/CameraPreview.tsx +94 -0
- package/src/sdk/device-state.ts +1315 -0
- package/src/sdk/index.ts +42 -3
- package/src/sdk/registration.ts +40 -0
- package/src/sdk/types.ts +489 -8
- package/src/sdk/voice.ts +186 -0
- package/src/tools/loader.ts +185 -0
- package/src/tools/platformTools.ts +41 -0
- package/src/tools/registry.ts +190 -0
- package/src/tools/serverCalls.ts +78 -0
- package/src/tools/sessionSync.ts +107 -0
- package/src/voiceHost.ts +128 -0
package/dist/index.js
CHANGED
|
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
8
11
|
var __export = (target, all) => {
|
|
9
12
|
for (var name in all)
|
|
10
13
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -27,118 +30,24 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
30
|
));
|
|
28
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
32
|
|
|
30
|
-
// src/index.ts
|
|
31
|
-
var index_exports = {};
|
|
32
|
-
__export(index_exports, {
|
|
33
|
-
ActionButton: () => ActionButton,
|
|
34
|
-
ActivityIndicator: () => import_react_native.ActivityIndicator,
|
|
35
|
-
AdaOrb: () => AdaOrb,
|
|
36
|
-
Animated: () => import_react_native.Animated,
|
|
37
|
-
Badge: () => Badge,
|
|
38
|
-
Card: () => Card,
|
|
39
|
-
EmptyState: () => EmptyState,
|
|
40
|
-
ErrorState: () => ErrorState,
|
|
41
|
-
FlatList: () => import_react_native.FlatList,
|
|
42
|
-
Image: () => import_react_native.Image,
|
|
43
|
-
ListItem: () => ListItem,
|
|
44
|
-
LoadingState: () => LoadingState,
|
|
45
|
-
MemoryBundleStorage: () => MemoryBundleStorage,
|
|
46
|
-
Modal: () => import_react_native.Modal,
|
|
47
|
-
Platform: () => import_react_native.Platform,
|
|
48
|
-
PluginSDK: () => PluginSDK,
|
|
49
|
-
Pressable: () => import_react_native.Pressable,
|
|
50
|
-
RefreshControl: () => import_react_native.RefreshControl,
|
|
51
|
-
ScrollView: () => import_react_native.ScrollView,
|
|
52
|
-
SearchBar: () => SearchBar,
|
|
53
|
-
SectionList: () => import_react_native.SectionList,
|
|
54
|
-
StyleSheet: () => import_react_native.StyleSheet,
|
|
55
|
-
SurfaceCard: () => SurfaceCard,
|
|
56
|
-
SurfaceDispatcher: () => SurfaceDispatcher,
|
|
57
|
-
SurfaceDrawer: () => SurfaceDrawer,
|
|
58
|
-
SurfaceFullscreen: () => SurfaceFullscreen,
|
|
59
|
-
Switch: () => import_react_native.Switch,
|
|
60
|
-
TabBar: () => TabBar,
|
|
61
|
-
Text: () => import_react_native.Text,
|
|
62
|
-
TextInput: () => import_react_native.TextInput,
|
|
63
|
-
TouchableOpacity: () => import_react_native.TouchableOpacity,
|
|
64
|
-
View: () => import_react_native.View,
|
|
65
|
-
bundleManager: () => bundleManager,
|
|
66
|
-
createNativeBridge: () => createNativeBridge,
|
|
67
|
-
gatePluginNative: () => gatePluginNative,
|
|
68
|
-
getHaptics: () => getHaptics,
|
|
69
|
-
getNativeBundleHash: () => getNativeBundleHash,
|
|
70
|
-
getNativeBundleUrl: () => getNativeBundleUrl,
|
|
71
|
-
getNativeComponentId: () => getNativeComponentId,
|
|
72
|
-
getPlugin: () => getPlugin,
|
|
73
|
-
getPluginNative: () => getPluginNative,
|
|
74
|
-
isInlineSurface: () => isInlineSurface,
|
|
75
|
-
isMountSurface: () => isMountSurface,
|
|
76
|
-
listPlugins: () => listPlugins,
|
|
77
|
-
nativePluginRegistry: () => nativePluginRegistry,
|
|
78
|
-
register: () => register,
|
|
79
|
-
registerBundleModules: () => registerBundleModules,
|
|
80
|
-
registerNativePlugin: () => registerNativePlugin,
|
|
81
|
-
registerService: () => registerService,
|
|
82
|
-
resolvePluginUrl: () => resolvePluginUrl,
|
|
83
|
-
setBundleStorage: () => setBundleStorage,
|
|
84
|
-
setHapticsProvider: () => setHapticsProvider,
|
|
85
|
-
setPluginNativeProvider: () => setPluginNativeProvider,
|
|
86
|
-
setPluginThemeColors: () => setPluginThemeColors,
|
|
87
|
-
supportsNative: () => supportsNative,
|
|
88
|
-
supportsWebView: () => supportsWebView,
|
|
89
|
-
unregisterNativePlugin: () => unregisterNativePlugin,
|
|
90
|
-
useApi: () => useApi,
|
|
91
|
-
useNativeBridge: () => useNativeBridge,
|
|
92
|
-
usePlugin: () => usePlugin,
|
|
93
|
-
usePluginTheme: () => usePluginTheme,
|
|
94
|
-
useRemotePlugins: () => useRemotePlugins,
|
|
95
|
-
useSurfaceStore: () => useSurfaceStore
|
|
96
|
-
});
|
|
97
|
-
module.exports = __toCommonJS(index_exports);
|
|
98
|
-
|
|
99
|
-
// src/sdk/index.ts
|
|
100
|
-
var sdk_exports = {};
|
|
101
|
-
__export(sdk_exports, {
|
|
102
|
-
ActionButton: () => ActionButton,
|
|
103
|
-
ActivityIndicator: () => import_react_native.ActivityIndicator,
|
|
104
|
-
Animated: () => import_react_native.Animated,
|
|
105
|
-
Badge: () => Badge,
|
|
106
|
-
Card: () => Card,
|
|
107
|
-
EmptyState: () => EmptyState,
|
|
108
|
-
ErrorState: () => ErrorState,
|
|
109
|
-
FlatList: () => import_react_native.FlatList,
|
|
110
|
-
Image: () => import_react_native.Image,
|
|
111
|
-
ListItem: () => ListItem,
|
|
112
|
-
LoadingState: () => LoadingState,
|
|
113
|
-
Modal: () => import_react_native.Modal,
|
|
114
|
-
Platform: () => import_react_native.Platform,
|
|
115
|
-
PluginSDK: () => PluginSDK,
|
|
116
|
-
Pressable: () => import_react_native.Pressable,
|
|
117
|
-
RefreshControl: () => import_react_native.RefreshControl,
|
|
118
|
-
ScrollView: () => import_react_native.ScrollView,
|
|
119
|
-
SearchBar: () => SearchBar,
|
|
120
|
-
SectionList: () => import_react_native.SectionList,
|
|
121
|
-
StyleSheet: () => import_react_native.StyleSheet,
|
|
122
|
-
Switch: () => import_react_native.Switch,
|
|
123
|
-
TabBar: () => TabBar,
|
|
124
|
-
Text: () => import_react_native.Text,
|
|
125
|
-
TextInput: () => import_react_native.TextInput,
|
|
126
|
-
TouchableOpacity: () => import_react_native.TouchableOpacity,
|
|
127
|
-
View: () => import_react_native.View,
|
|
128
|
-
register: () => register,
|
|
129
|
-
registerService: () => registerService,
|
|
130
|
-
setPluginThemeColors: () => setPluginThemeColors,
|
|
131
|
-
useApi: () => useApi,
|
|
132
|
-
usePluginTheme: () => usePluginTheme
|
|
133
|
-
});
|
|
134
|
-
|
|
135
33
|
// src/sdk/registration.ts
|
|
136
34
|
function register(id, config) {
|
|
137
35
|
return {
|
|
138
36
|
id,
|
|
139
37
|
type: config.type || "ui",
|
|
140
38
|
capabilities: config.capabilities,
|
|
141
|
-
|
|
39
|
+
stateDomains: config.stateDomains,
|
|
40
|
+
Component: config.Component,
|
|
41
|
+
tools: config.tools
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function registerTools(id, config) {
|
|
45
|
+
return {
|
|
46
|
+
id,
|
|
47
|
+
type: "tools",
|
|
48
|
+
capabilities: config.capabilities,
|
|
49
|
+
stateDomains: config.stateDomains,
|
|
50
|
+
tools: config.tools
|
|
142
51
|
};
|
|
143
52
|
}
|
|
144
53
|
function registerService(id, config) {
|
|
@@ -151,13 +60,31 @@ function registerService(id, config) {
|
|
|
151
60
|
onStop: config.onStop
|
|
152
61
|
};
|
|
153
62
|
}
|
|
63
|
+
var init_registration = __esm({
|
|
64
|
+
"src/sdk/registration.ts"() {
|
|
65
|
+
"use strict";
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// src/sdk/voice.ts
|
|
70
|
+
var PLATFORM_VOICE_CALLER;
|
|
71
|
+
var init_voice = __esm({
|
|
72
|
+
"src/sdk/voice.ts"() {
|
|
73
|
+
"use strict";
|
|
74
|
+
PLATFORM_VOICE_CALLER = /* @__PURE__ */ Symbol("adas.voice.platform");
|
|
75
|
+
}
|
|
76
|
+
});
|
|
154
77
|
|
|
155
78
|
// src/sdk/primitives.ts
|
|
156
|
-
var import_react_native
|
|
79
|
+
var import_react_native;
|
|
80
|
+
var init_primitives = __esm({
|
|
81
|
+
"src/sdk/primitives.ts"() {
|
|
82
|
+
"use strict";
|
|
83
|
+
import_react_native = require("react-native");
|
|
84
|
+
}
|
|
85
|
+
});
|
|
157
86
|
|
|
158
87
|
// src/sdk/components.ts
|
|
159
|
-
var import_react = __toESM(require("react"));
|
|
160
|
-
var import_react_native2 = require("react-native");
|
|
161
88
|
function Card({ theme, children, style, onPress }) {
|
|
162
89
|
const cardStyle = [
|
|
163
90
|
styles.card,
|
|
@@ -172,13 +99,6 @@ function Card({ theme, children, style, onPress }) {
|
|
|
172
99
|
}
|
|
173
100
|
return import_react.default.createElement(import_react_native2.View, { style: cardStyle }, children);
|
|
174
101
|
}
|
|
175
|
-
var BADGE_COLORS = {
|
|
176
|
-
accent: { bg: "accentSoft", text: "accent" },
|
|
177
|
-
success: { bg: "successSoft", text: "success" },
|
|
178
|
-
warning: { bg: "warningSoft", text: "warning" },
|
|
179
|
-
error: { bg: "errorSoft", text: "error" },
|
|
180
|
-
purple: { bg: "purpleSoft", text: "purple" }
|
|
181
|
-
};
|
|
182
102
|
function Badge({ theme, label, color = "accent", style }) {
|
|
183
103
|
const mapping = BADGE_COLORS[color];
|
|
184
104
|
return import_react.default.createElement(
|
|
@@ -337,105 +257,272 @@ function ActionButton({ theme, label, icon, onPress, style }) {
|
|
|
337
257
|
icon || import_react.default.createElement(import_react_native2.Text, { style: styles.fabText }, label || "+")
|
|
338
258
|
);
|
|
339
259
|
}
|
|
340
|
-
var
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
260
|
+
var import_react, import_react_native2, BADGE_COLORS, styles;
|
|
261
|
+
var init_components = __esm({
|
|
262
|
+
"src/sdk/components.ts"() {
|
|
263
|
+
"use strict";
|
|
264
|
+
import_react = __toESM(require("react"));
|
|
265
|
+
import_react_native2 = require("react-native");
|
|
266
|
+
BADGE_COLORS = {
|
|
267
|
+
accent: { bg: "accentSoft", text: "accent" },
|
|
268
|
+
success: { bg: "successSoft", text: "success" },
|
|
269
|
+
warning: { bg: "warningSoft", text: "warning" },
|
|
270
|
+
error: { bg: "errorSoft", text: "error" },
|
|
271
|
+
purple: { bg: "purpleSoft", text: "purple" }
|
|
272
|
+
};
|
|
273
|
+
styles = import_react_native2.StyleSheet.create({
|
|
274
|
+
card: {
|
|
275
|
+
padding: 12,
|
|
276
|
+
borderRadius: 12,
|
|
277
|
+
borderWidth: 1,
|
|
278
|
+
marginBottom: 8
|
|
279
|
+
},
|
|
280
|
+
badge: {
|
|
281
|
+
paddingHorizontal: 8,
|
|
282
|
+
paddingVertical: 3
|
|
283
|
+
},
|
|
284
|
+
badgeText: {
|
|
285
|
+
fontSize: 11,
|
|
286
|
+
fontWeight: "600"
|
|
287
|
+
},
|
|
288
|
+
tabBar: {
|
|
289
|
+
flexDirection: "row",
|
|
290
|
+
borderBottomWidth: 1
|
|
291
|
+
},
|
|
292
|
+
tab: {
|
|
293
|
+
flex: 1,
|
|
294
|
+
padding: 12,
|
|
295
|
+
alignItems: "center",
|
|
296
|
+
borderBottomWidth: 2
|
|
297
|
+
},
|
|
298
|
+
listItem: {
|
|
299
|
+
flexDirection: "row",
|
|
300
|
+
alignItems: "center",
|
|
301
|
+
paddingVertical: 12,
|
|
302
|
+
paddingHorizontal: 16,
|
|
303
|
+
borderBottomWidth: import_react_native2.StyleSheet.hairlineWidth
|
|
304
|
+
},
|
|
305
|
+
listItemContent: {
|
|
306
|
+
flex: 1
|
|
307
|
+
},
|
|
308
|
+
searchBar: {
|
|
309
|
+
height: 40,
|
|
310
|
+
paddingHorizontal: 12,
|
|
311
|
+
borderWidth: 1
|
|
312
|
+
},
|
|
313
|
+
emptyState: {
|
|
314
|
+
flex: 1,
|
|
315
|
+
justifyContent: "center",
|
|
316
|
+
alignItems: "center",
|
|
317
|
+
padding: 32
|
|
318
|
+
},
|
|
319
|
+
retryButton: {
|
|
320
|
+
marginTop: 12,
|
|
321
|
+
paddingHorizontal: 16,
|
|
322
|
+
paddingVertical: 8
|
|
323
|
+
},
|
|
324
|
+
fab: {
|
|
325
|
+
position: "absolute",
|
|
326
|
+
bottom: 24,
|
|
327
|
+
right: 24,
|
|
328
|
+
width: 56,
|
|
329
|
+
height: 56,
|
|
330
|
+
justifyContent: "center",
|
|
331
|
+
alignItems: "center",
|
|
332
|
+
shadowColor: "#000",
|
|
333
|
+
shadowOffset: { width: 0, height: 2 },
|
|
334
|
+
shadowOpacity: 0.25,
|
|
335
|
+
shadowRadius: 4,
|
|
336
|
+
elevation: 5
|
|
337
|
+
},
|
|
338
|
+
fabText: {
|
|
339
|
+
color: "#fff",
|
|
340
|
+
fontSize: 24,
|
|
341
|
+
fontWeight: "600"
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// src/host.ts
|
|
348
|
+
function setHapticsProvider(haptics) {
|
|
349
|
+
_haptics = haptics;
|
|
350
|
+
}
|
|
351
|
+
function getHaptics() {
|
|
352
|
+
return _haptics;
|
|
353
|
+
}
|
|
354
|
+
function unavailable(api) {
|
|
355
|
+
return new Proxy({}, {
|
|
356
|
+
get: (_t, method) => () => Promise.reject(new Error(`[adas] native.${api}.${String(method)} unavailable \u2014 host did not call setPluginNativeProvider()`))
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
function setPluginNativeProvider(native) {
|
|
360
|
+
_pluginNative = native;
|
|
361
|
+
}
|
|
362
|
+
function getPluginNative() {
|
|
363
|
+
return _pluginNative;
|
|
364
|
+
}
|
|
365
|
+
function blocked(api, reason) {
|
|
366
|
+
return new Proxy({}, {
|
|
367
|
+
get: (_t, method) => {
|
|
368
|
+
if (typeof method !== "string") return void 0;
|
|
369
|
+
return () => {
|
|
370
|
+
console.warn(`[adas] native.${api}.${method} blocked \u2014 ${reason ?? `capability '${api}' not declared in the plugin manifest`}`);
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
function gatePluginNative(native, declared, pluginId) {
|
|
376
|
+
const caps = declared || {};
|
|
377
|
+
const scopedFileSystem = native.fileSystemFor && pluginId ? native.fileSystemFor(pluginId) : null;
|
|
378
|
+
const scopedStorage = native.storageFor && pluginId ? native.storageFor(pluginId) : null;
|
|
379
|
+
const gated = {
|
|
380
|
+
camera: caps.camera ? native.camera : blocked("camera"),
|
|
381
|
+
location: caps.location ? native.location : blocked("location"),
|
|
382
|
+
storage: caps.offline ? scopedStorage ?? blocked("storage", "no trusted pluginId to scope storage \u2014 refusing shared access (fail closed)") : blocked("storage"),
|
|
383
|
+
biometrics: caps.biometrics ? native.biometrics : blocked("biometrics"),
|
|
384
|
+
biometricsV2: caps.biometricsV2 ? native.biometricsV2 : blocked("biometricsV2"),
|
|
385
|
+
notifications: caps.notifications ? native.notifications : blocked("notifications"),
|
|
386
|
+
haptics: caps.haptics ? native.haptics : blocked("haptics"),
|
|
387
|
+
connectivity: native.connectivity,
|
|
388
|
+
fileSystem: caps.fileSystem ? scopedFileSystem ?? blocked("fileSystem", "no trusted pluginId to scope the filesystem \u2014 refusing shared access (fail closed)") : blocked("fileSystem"),
|
|
389
|
+
cookies: caps.cookies ? native.cookies : blocked("cookies"),
|
|
390
|
+
sharing: caps.sharing ? native.sharing : blocked("sharing"),
|
|
391
|
+
scanner: caps.scanner ? native.scanner : blocked("scanner"),
|
|
392
|
+
nfc: caps.nfc ? native.nfc : blocked("nfc"),
|
|
393
|
+
// Gated like the rest even though nothing implements it yet. An undeclared
|
|
394
|
+
// plugin gets the usual blocked proxy; a DECLARED one reaches an API whose
|
|
395
|
+
// every method refuses by name. Both are better than the `undefined` this
|
|
396
|
+
// used to be, which crashed with "cannot read properties of undefined".
|
|
397
|
+
bluetooth: caps.bluetooth ? native.bluetooth : blocked("bluetooth"),
|
|
398
|
+
vision: caps.vision ? native.vision : blocked("vision"),
|
|
399
|
+
maps: caps.maps ? native.maps : blocked("maps")
|
|
400
|
+
// What this plugin DECLARED, carried on the object it already receives.
|
|
401
|
+
//
|
|
402
|
+
// It is how a COMPONENT can be capability-gated: <CameraPreview> is handed
|
|
403
|
+
// `native` and reads this, so showing the camera obeys the same flag as
|
|
404
|
+
// calling it. Without this the preview would be a second door to the camera
|
|
405
|
+
// with only one lock. Non-enumerable so it cannot be mistaken for an API.
|
|
406
|
+
};
|
|
407
|
+
Object.defineProperty(gated, "__declaredCapabilities", {
|
|
408
|
+
value: Object.freeze({ ...caps }),
|
|
409
|
+
enumerable: false,
|
|
410
|
+
writable: false
|
|
411
|
+
});
|
|
412
|
+
return gated;
|
|
413
|
+
}
|
|
414
|
+
function setCameraPreviewProvider(Component) {
|
|
415
|
+
_cameraPreview = Component;
|
|
416
|
+
}
|
|
417
|
+
function getCameraPreviewProvider() {
|
|
418
|
+
return _cameraPreview;
|
|
419
|
+
}
|
|
420
|
+
function registerNativePlugin(id, plugin) {
|
|
421
|
+
_nativeRegistry.set(id, plugin);
|
|
422
|
+
}
|
|
423
|
+
function unregisterNativePlugin(id) {
|
|
424
|
+
_nativeRegistry.delete(id);
|
|
425
|
+
}
|
|
426
|
+
function registerBundleModules(modules) {
|
|
427
|
+
for (const [name, mod] of Object.entries(modules)) {
|
|
428
|
+
_bundleModules.set(name, mod);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
function resolveBundleModule(name) {
|
|
432
|
+
return _bundleModules.get(name);
|
|
433
|
+
}
|
|
434
|
+
var _haptics, noopNative, _pluginNative, _cameraPreview, _nativeRegistry, nativePluginRegistry, _bundleModules;
|
|
435
|
+
var init_host = __esm({
|
|
436
|
+
"src/host.ts"() {
|
|
437
|
+
"use strict";
|
|
438
|
+
_haptics = null;
|
|
439
|
+
noopNative = {
|
|
440
|
+
camera: unavailable("camera"),
|
|
441
|
+
location: unavailable("location"),
|
|
442
|
+
storage: unavailable("storage"),
|
|
443
|
+
biometrics: unavailable("biometrics"),
|
|
444
|
+
biometricsV2: unavailable("biometricsV2"),
|
|
445
|
+
notifications: unavailable("notifications"),
|
|
446
|
+
haptics: { impact: () => {
|
|
447
|
+
}, notification: () => {
|
|
448
|
+
}, selection: () => {
|
|
449
|
+
}, error: () => {
|
|
450
|
+
} },
|
|
451
|
+
connectivity: { isOnline: () => true, onChange: () => () => {
|
|
452
|
+
} },
|
|
453
|
+
fileSystem: unavailable("fileSystem"),
|
|
454
|
+
cookies: unavailable("cookies"),
|
|
455
|
+
sharing: unavailable("sharing"),
|
|
456
|
+
scanner: unavailable("scanner"),
|
|
457
|
+
nfc: unavailable("nfc"),
|
|
458
|
+
bluetooth: unavailable("bluetooth"),
|
|
459
|
+
vision: unavailable("vision"),
|
|
460
|
+
maps: unavailable("maps")
|
|
461
|
+
};
|
|
462
|
+
_pluginNative = noopNative;
|
|
463
|
+
_cameraPreview = null;
|
|
464
|
+
_nativeRegistry = /* @__PURE__ */ new Map();
|
|
465
|
+
nativePluginRegistry = {
|
|
466
|
+
has(id) {
|
|
467
|
+
return _nativeRegistry.has(id);
|
|
468
|
+
},
|
|
469
|
+
async get(id) {
|
|
470
|
+
const entry = _nativeRegistry.get(id);
|
|
471
|
+
if (!entry) return null;
|
|
472
|
+
if (typeof entry === "function") {
|
|
473
|
+
try {
|
|
474
|
+
return await entry();
|
|
475
|
+
} catch (err) {
|
|
476
|
+
console.warn(`[adas-plugins] lazy native plugin '${id}' failed to load:`, err);
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return entry;
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
_bundleModules = /* @__PURE__ */ new Map();
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// src/sdk/CameraPreview.tsx
|
|
488
|
+
function declaredCapabilities(native) {
|
|
489
|
+
return native && native.__declaredCapabilities || null;
|
|
490
|
+
}
|
|
491
|
+
function CameraPreview({ native, facing = "back", style, renderUnavailable }) {
|
|
492
|
+
const caps = declaredCapabilities(native);
|
|
493
|
+
if (caps && !caps.camera) {
|
|
494
|
+
return unavailable2(
|
|
495
|
+
"this plugin did not declare `capabilities: { camera: true }`, so it may not show the camera",
|
|
496
|
+
renderUnavailable,
|
|
497
|
+
style
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
const Provider = getCameraPreviewProvider();
|
|
501
|
+
if (!Provider) {
|
|
502
|
+
return unavailable2(
|
|
503
|
+
"this app does not provide a camera preview (the host has not called setCameraPreviewProvider)",
|
|
504
|
+
renderUnavailable,
|
|
505
|
+
style
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Provider, { facing, style, renderUnavailable });
|
|
509
|
+
}
|
|
510
|
+
function unavailable2(reason, render, style) {
|
|
511
|
+
if (render) return render(reason);
|
|
512
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: [{ flex: 1, backgroundColor: "#000", alignItems: "center", justifyContent: "center", padding: 20 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Text, { style: { color: "#39FF66", fontSize: 12, textAlign: "center" }, children: `NO CAMERA PREVIEW
|
|
513
|
+
${reason}` }) });
|
|
514
|
+
}
|
|
515
|
+
var import_jsx_runtime;
|
|
516
|
+
var init_CameraPreview = __esm({
|
|
517
|
+
"src/sdk/CameraPreview.tsx"() {
|
|
518
|
+
"use strict";
|
|
519
|
+
init_primitives();
|
|
520
|
+
init_host();
|
|
521
|
+
import_jsx_runtime = require("react/jsx-runtime");
|
|
409
522
|
}
|
|
410
523
|
});
|
|
411
524
|
|
|
412
525
|
// src/sdk/usePluginTheme.ts
|
|
413
|
-
var import_react2 = require("react");
|
|
414
|
-
var import_react_native3 = require("react-native");
|
|
415
|
-
var import_core = require("@ateam-ai-mobile/core");
|
|
416
|
-
var adaColors = {
|
|
417
|
-
bg: import_core.ada.paper,
|
|
418
|
-
surface: import_core.ada.paperSoft,
|
|
419
|
-
surfaceAlt: import_core.ada.paperDeep,
|
|
420
|
-
input: import_core.ada.paper,
|
|
421
|
-
border: import_core.ada.border,
|
|
422
|
-
text: import_core.ada.ink,
|
|
423
|
-
textSecondary: import_core.ada.inkSoft,
|
|
424
|
-
textMuted: import_core.ada.inkMuted,
|
|
425
|
-
accent: import_core.ada.orange,
|
|
426
|
-
accentSoft: import_core.ada.orangeSoft,
|
|
427
|
-
success: "#5A8A5C",
|
|
428
|
-
successSoft: "rgba(90,138,92,0.14)",
|
|
429
|
-
warning: "#D49A4E",
|
|
430
|
-
warningSoft: "rgba(212,154,78,0.16)",
|
|
431
|
-
error: "#C15545",
|
|
432
|
-
errorSoft: "rgba(193,85,69,0.14)",
|
|
433
|
-
purple: import_core.ada.orangeDeep,
|
|
434
|
-
purpleSoft: import_core.ada.orangeSoft
|
|
435
|
-
};
|
|
436
|
-
var _colors = adaColors;
|
|
437
|
-
var _isDark = null;
|
|
438
|
-
var _subscribers = /* @__PURE__ */ new Set();
|
|
439
526
|
function setPluginThemeColors(colors, opts) {
|
|
440
527
|
_colors = { ...adaColors, ...colors };
|
|
441
528
|
if (opts?.isDark !== void 0) _isDark = opts.isDark;
|
|
@@ -478,9 +565,40 @@ function usePluginTheme() {
|
|
|
478
565
|
isDark
|
|
479
566
|
}), [colors, isDark]);
|
|
480
567
|
}
|
|
568
|
+
var import_react2, import_react_native3, import_core, adaColors, _colors, _isDark, _subscribers;
|
|
569
|
+
var init_usePluginTheme = __esm({
|
|
570
|
+
"src/sdk/usePluginTheme.ts"() {
|
|
571
|
+
"use strict";
|
|
572
|
+
import_react2 = require("react");
|
|
573
|
+
import_react_native3 = require("react-native");
|
|
574
|
+
import_core = require("@ateam-ai-mobile/core");
|
|
575
|
+
adaColors = {
|
|
576
|
+
bg: import_core.ada.paper,
|
|
577
|
+
surface: import_core.ada.paperSoft,
|
|
578
|
+
surfaceAlt: import_core.ada.paperDeep,
|
|
579
|
+
input: import_core.ada.paper,
|
|
580
|
+
border: import_core.ada.border,
|
|
581
|
+
text: import_core.ada.ink,
|
|
582
|
+
textSecondary: import_core.ada.inkSoft,
|
|
583
|
+
textMuted: import_core.ada.inkMuted,
|
|
584
|
+
accent: import_core.ada.orange,
|
|
585
|
+
accentSoft: import_core.ada.orangeSoft,
|
|
586
|
+
success: "#5A8A5C",
|
|
587
|
+
successSoft: "rgba(90,138,92,0.14)",
|
|
588
|
+
warning: "#D49A4E",
|
|
589
|
+
warningSoft: "rgba(212,154,78,0.16)",
|
|
590
|
+
error: "#C15545",
|
|
591
|
+
errorSoft: "rgba(193,85,69,0.14)",
|
|
592
|
+
purple: import_core.ada.orangeDeep,
|
|
593
|
+
purpleSoft: import_core.ada.orangeSoft
|
|
594
|
+
};
|
|
595
|
+
_colors = adaColors;
|
|
596
|
+
_isDark = null;
|
|
597
|
+
_subscribers = /* @__PURE__ */ new Set();
|
|
598
|
+
}
|
|
599
|
+
});
|
|
481
600
|
|
|
482
601
|
// src/sdk/useApi.ts
|
|
483
|
-
var import_react3 = require("react");
|
|
484
602
|
function useApi(bridge) {
|
|
485
603
|
return (0, import_react3.useMemo)(() => {
|
|
486
604
|
const apiFn = async (connectorId, tool, args = {}) => {
|
|
@@ -509,131 +627,767 @@ function useApi(bridge) {
|
|
|
509
627
|
return apiFn;
|
|
510
628
|
}, [bridge]);
|
|
511
629
|
}
|
|
630
|
+
var import_react3;
|
|
631
|
+
var init_useApi = __esm({
|
|
632
|
+
"src/sdk/useApi.ts"() {
|
|
633
|
+
"use strict";
|
|
634
|
+
import_react3 = require("react");
|
|
635
|
+
}
|
|
636
|
+
});
|
|
512
637
|
|
|
513
638
|
// src/sdk/index.ts
|
|
514
|
-
var
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
|
|
639
|
+
var sdk_exports = {};
|
|
640
|
+
__export(sdk_exports, {
|
|
641
|
+
ActionButton: () => ActionButton,
|
|
642
|
+
ActivityIndicator: () => import_react_native.ActivityIndicator,
|
|
643
|
+
Animated: () => import_react_native.Animated,
|
|
644
|
+
Badge: () => Badge,
|
|
645
|
+
CameraPreview: () => CameraPreview,
|
|
646
|
+
Card: () => Card,
|
|
647
|
+
EmptyState: () => EmptyState,
|
|
648
|
+
ErrorState: () => ErrorState,
|
|
649
|
+
FlatList: () => import_react_native.FlatList,
|
|
650
|
+
Image: () => import_react_native.Image,
|
|
651
|
+
ListItem: () => ListItem,
|
|
652
|
+
LoadingState: () => LoadingState,
|
|
653
|
+
Modal: () => import_react_native.Modal,
|
|
654
|
+
PLATFORM_VOICE_CALLER: () => PLATFORM_VOICE_CALLER,
|
|
655
|
+
Platform: () => import_react_native.Platform,
|
|
656
|
+
PluginSDK: () => PluginSDK,
|
|
657
|
+
Pressable: () => import_react_native.Pressable,
|
|
658
|
+
RefreshControl: () => import_react_native.RefreshControl,
|
|
659
|
+
ScrollView: () => import_react_native.ScrollView,
|
|
660
|
+
SearchBar: () => SearchBar,
|
|
661
|
+
SectionList: () => import_react_native.SectionList,
|
|
662
|
+
StyleSheet: () => import_react_native.StyleSheet,
|
|
663
|
+
Switch: () => import_react_native.Switch,
|
|
664
|
+
TabBar: () => TabBar,
|
|
665
|
+
Text: () => import_react_native.Text,
|
|
666
|
+
TextInput: () => import_react_native.TextInput,
|
|
667
|
+
TouchableOpacity: () => import_react_native.TouchableOpacity,
|
|
668
|
+
View: () => import_react_native.View,
|
|
669
|
+
register: () => register,
|
|
670
|
+
registerService: () => registerService,
|
|
671
|
+
registerTools: () => registerTools,
|
|
672
|
+
setPluginThemeColors: () => setPluginThemeColors,
|
|
673
|
+
useApi: () => useApi,
|
|
674
|
+
usePluginTheme: () => usePluginTheme
|
|
675
|
+
});
|
|
676
|
+
var PluginSDK;
|
|
677
|
+
var init_sdk = __esm({
|
|
678
|
+
"src/sdk/index.ts"() {
|
|
679
|
+
"use strict";
|
|
680
|
+
init_registration();
|
|
681
|
+
init_registration();
|
|
682
|
+
init_voice();
|
|
683
|
+
init_primitives();
|
|
684
|
+
init_components();
|
|
685
|
+
init_CameraPreview();
|
|
686
|
+
init_usePluginTheme();
|
|
687
|
+
init_useApi();
|
|
688
|
+
PluginSDK = { register, registerService, registerTools };
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
// src/deviceStateHost.ts
|
|
693
|
+
function deniedSnapshot(domain) {
|
|
694
|
+
const base = { quality: UNKNOWN_QUALITY };
|
|
695
|
+
switch (domain) {
|
|
696
|
+
case "spatial":
|
|
697
|
+
return {
|
|
698
|
+
...base,
|
|
699
|
+
position: null,
|
|
700
|
+
movement: { speedMps: null, bearingDeg: null, distanceRecentM: null, state: "unknown" },
|
|
701
|
+
orientation: { headingDeg: null },
|
|
702
|
+
recentPath: []
|
|
703
|
+
};
|
|
704
|
+
case "visual":
|
|
705
|
+
return {
|
|
706
|
+
...base,
|
|
707
|
+
camera: { available: false, active: false, facing: "unknown" },
|
|
708
|
+
latest: null,
|
|
709
|
+
recentTimeline: { windowMs: 0, frames: [] }
|
|
710
|
+
};
|
|
711
|
+
case "deviceContext":
|
|
712
|
+
return {
|
|
713
|
+
...base,
|
|
714
|
+
power: { batteryLevel: null, charging: null, lowPowerMode: null },
|
|
715
|
+
thermal: { state: "unknown" },
|
|
716
|
+
resources: { memoryPressure: "unknown", storagePressure: "unknown" }
|
|
717
|
+
};
|
|
718
|
+
case "connectivity":
|
|
719
|
+
return {
|
|
720
|
+
...base,
|
|
721
|
+
reachability: "unknown",
|
|
722
|
+
transport: "unknown",
|
|
723
|
+
qualityClass: "unknown",
|
|
724
|
+
stability: "unknown",
|
|
725
|
+
latencyMs: null
|
|
726
|
+
};
|
|
727
|
+
case "interaction":
|
|
728
|
+
return {
|
|
729
|
+
...base,
|
|
730
|
+
lifecycle: { appState: "unknown", screenOn: null },
|
|
731
|
+
audio: { captureActive: null, playbackActive: null, route: "unknown", interruption: "unknown" }
|
|
732
|
+
};
|
|
733
|
+
case "activity":
|
|
734
|
+
return {
|
|
735
|
+
...base,
|
|
736
|
+
// `confidence` is REQUIRED on both blocks in the freeze, precisely
|
|
737
|
+
// because an inferred activity must never be handed over without
|
|
738
|
+
// saying how sure the inference is. An empty snapshot is the least
|
|
739
|
+
// sure of all.
|
|
740
|
+
human: { state: "unknown", since: null, confidence: { level: "unknown" } },
|
|
741
|
+
devicePose: { placement: "unknown", orientation: "unknown", confidence: { level: "unknown" } },
|
|
742
|
+
recentTransitions: []
|
|
743
|
+
};
|
|
744
|
+
case "environment":
|
|
745
|
+
return {
|
|
746
|
+
...base,
|
|
747
|
+
lighting: { level: "unknown", lux: null },
|
|
748
|
+
acoustics: { level: "unknown", approximateDb: null },
|
|
749
|
+
// 'unknown' rather than 'uncertain': uncertain means we looked and
|
|
750
|
+
// could not decide, and nothing looked at all here.
|
|
751
|
+
enclosure: { state: "unknown" },
|
|
752
|
+
proximity: { distanceClass: "unknown" },
|
|
753
|
+
nearbySignals: { bleAvailable: false, beaconCount: null, uwbAvailable: false }
|
|
754
|
+
};
|
|
755
|
+
case "capability": {
|
|
756
|
+
const unknownStatus = { support: "unknown", permission: "unknown", availability: "unknown" };
|
|
757
|
+
return {
|
|
758
|
+
...base,
|
|
759
|
+
camera: unknownStatus,
|
|
760
|
+
microphone: unknownStatus,
|
|
761
|
+
location: { ...unknownStatus, precision: "unknown", backgroundPermission: "unknown" },
|
|
762
|
+
motion: unknownStatus,
|
|
763
|
+
bluetooth: unknownStatus,
|
|
764
|
+
uwb: unknownStatus,
|
|
765
|
+
nfc: unknownStatus,
|
|
766
|
+
notifications: unknownStatus,
|
|
767
|
+
health: unknownStatus,
|
|
768
|
+
backgroundExecution: unknownStatus
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
default:
|
|
772
|
+
return base;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
function deniedDomain(domain, why) {
|
|
567
776
|
return {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
777
|
+
async getSnapshot() {
|
|
778
|
+
return deniedSnapshot(domain);
|
|
779
|
+
},
|
|
780
|
+
async start() {
|
|
781
|
+
throw denial(domain, why);
|
|
782
|
+
},
|
|
783
|
+
async stop() {
|
|
784
|
+
throw denial(domain, why);
|
|
785
|
+
},
|
|
786
|
+
subscribe() {
|
|
787
|
+
throw denial(domain, why);
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
function denial(domain, why) {
|
|
792
|
+
const err = new Error(`deviceState.${domain}: ${why}`);
|
|
793
|
+
err.code = "RESTRICTED";
|
|
794
|
+
err.domain = domain;
|
|
795
|
+
return err;
|
|
796
|
+
}
|
|
797
|
+
function setDeviceStateProvider(state) {
|
|
798
|
+
_provider = state || {};
|
|
799
|
+
}
|
|
800
|
+
function gateDeviceState(declared) {
|
|
801
|
+
const allowed = new Set(declared || []);
|
|
802
|
+
const out = {};
|
|
803
|
+
for (const domain of ALL_DOMAINS) {
|
|
804
|
+
if (!allowed.has(domain)) {
|
|
805
|
+
out[domain] = deniedDomain(
|
|
806
|
+
domain,
|
|
807
|
+
`not declared in this plugin's stateDomains. Add '${domain}' to use it.`
|
|
808
|
+
);
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
const engine = _provider[domain];
|
|
812
|
+
out[domain] = engine ?? deniedDomain(
|
|
813
|
+
domain,
|
|
814
|
+
"no engine in this build \u2014 the native side does not exist yet. See docs/DEVICE_API_STATUS.md."
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
return out;
|
|
818
|
+
}
|
|
819
|
+
function withoutSubscribe(state) {
|
|
820
|
+
if (!state) return state;
|
|
821
|
+
const out = {};
|
|
822
|
+
for (const domain of ALL_DOMAINS) {
|
|
823
|
+
const d = state[domain];
|
|
824
|
+
out[domain] = d && Object.assign(Object.create(d), {
|
|
825
|
+
subscribe() {
|
|
826
|
+
throw denial(domain, "a device tool call has no lifetime to own a subscription. Subscribe from the plugin UI, or read getSnapshot() within the call.");
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
return out;
|
|
831
|
+
}
|
|
832
|
+
function implementedStateDomains() {
|
|
833
|
+
return ALL_DOMAINS.filter((d) => !!_provider[d]);
|
|
834
|
+
}
|
|
835
|
+
var UNKNOWN_QUALITY, ALL_DOMAINS, _provider;
|
|
836
|
+
var init_deviceStateHost = __esm({
|
|
837
|
+
"src/deviceStateHost.ts"() {
|
|
838
|
+
"use strict";
|
|
839
|
+
UNKNOWN_QUALITY = {
|
|
840
|
+
freshness: { state: "unknown", observedAt: null, ageMs: null },
|
|
841
|
+
confidence: { level: "unknown" }
|
|
842
|
+
};
|
|
843
|
+
ALL_DOMAINS = [
|
|
844
|
+
"spatial",
|
|
845
|
+
"visual",
|
|
846
|
+
"activity",
|
|
847
|
+
"deviceContext",
|
|
848
|
+
"connectivity",
|
|
849
|
+
"capability",
|
|
850
|
+
"environment",
|
|
851
|
+
"interaction"
|
|
852
|
+
];
|
|
853
|
+
_provider = {};
|
|
854
|
+
}
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
// src/voiceHost.ts
|
|
858
|
+
function setVoiceProvider(provider) {
|
|
859
|
+
_provider2 = provider || null;
|
|
860
|
+
}
|
|
861
|
+
function implementedVoice() {
|
|
862
|
+
return !!_provider2;
|
|
863
|
+
}
|
|
864
|
+
function voiceDenial(why) {
|
|
865
|
+
const err = new Error(`voice: ${why}`);
|
|
866
|
+
err.code = "RESTRICTED";
|
|
867
|
+
return err;
|
|
868
|
+
}
|
|
869
|
+
function deniedVoice(why) {
|
|
870
|
+
const fail = () => {
|
|
871
|
+
throw voiceDenial(why);
|
|
872
|
+
};
|
|
873
|
+
return {
|
|
874
|
+
async start() {
|
|
875
|
+
return fail();
|
|
876
|
+
},
|
|
877
|
+
stop() {
|
|
878
|
+
fail();
|
|
879
|
+
},
|
|
880
|
+
setMuted() {
|
|
881
|
+
fail();
|
|
882
|
+
},
|
|
883
|
+
prompt() {
|
|
884
|
+
fail();
|
|
885
|
+
},
|
|
886
|
+
observe() {
|
|
887
|
+
fail();
|
|
888
|
+
},
|
|
889
|
+
getSnapshot() {
|
|
890
|
+
return {
|
|
891
|
+
state: "error",
|
|
892
|
+
sessionId: null,
|
|
893
|
+
muted: false,
|
|
894
|
+
speaking: false,
|
|
895
|
+
listening: false,
|
|
896
|
+
userSpeaking: false,
|
|
897
|
+
lastUserSpeechEndedAt: null,
|
|
898
|
+
lastDeviceTool: null,
|
|
899
|
+
authorizedSessionId: null,
|
|
900
|
+
lastError: why
|
|
901
|
+
};
|
|
902
|
+
},
|
|
903
|
+
subscribe() {
|
|
904
|
+
return fail();
|
|
905
|
+
},
|
|
906
|
+
transcript() {
|
|
907
|
+
return [];
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
function scopedVoice(provider, pluginId) {
|
|
912
|
+
return {
|
|
913
|
+
start: (opts) => provider.start(opts, pluginId),
|
|
914
|
+
stop: () => provider.stop(pluginId),
|
|
915
|
+
setMuted: (muted) => provider.setMuted(muted, pluginId),
|
|
916
|
+
prompt: (input) => provider.prompt(input, pluginId),
|
|
917
|
+
observe: (text) => provider.observe(text, pluginId),
|
|
918
|
+
getSnapshot: () => provider.getSnapshot(pluginId),
|
|
919
|
+
subscribe: (cb) => provider.subscribe(cb, pluginId),
|
|
920
|
+
transcript: () => provider.transcript(pluginId)
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
function gateVoice(declared, pluginId) {
|
|
924
|
+
if (!declared) {
|
|
925
|
+
return deniedVoice(
|
|
926
|
+
"not declared in this plugin's capabilities. Add `capabilities: { voice: true }` to use it."
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
if (!pluginId) {
|
|
930
|
+
return deniedVoice(
|
|
931
|
+
"no plugin identity \u2014 voice fails closed without a caller to scope the session to."
|
|
932
|
+
);
|
|
933
|
+
}
|
|
934
|
+
return _provider2 ? scopedVoice(_provider2, pluginId) : deniedVoice(
|
|
935
|
+
"no voice session in this build \u2014 the app has not mounted a VoiceSessionHost / called setVoiceProvider."
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
var _provider2;
|
|
939
|
+
var init_voiceHost = __esm({
|
|
940
|
+
"src/voiceHost.ts"() {
|
|
941
|
+
"use strict";
|
|
942
|
+
_provider2 = null;
|
|
943
|
+
}
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
// src/pluginRuntime.ts
|
|
947
|
+
function mergeCaps(decl) {
|
|
948
|
+
if (!decl) return void 0;
|
|
949
|
+
return { ...decl.native, ...decl.capabilities };
|
|
950
|
+
}
|
|
951
|
+
function resolvePluginGrants(src) {
|
|
952
|
+
const trusted = src.manifest ?? src.plugin ?? null;
|
|
953
|
+
const stateDomains = trusted?.stateDomains ? [...trusted.stateDomains] : void 0;
|
|
954
|
+
return { capabilities: mergeCaps(trusted), stateDomains };
|
|
955
|
+
}
|
|
956
|
+
function resolvePluginRuntimeProps(src) {
|
|
957
|
+
const { capabilities, stateDomains } = resolvePluginGrants(src);
|
|
958
|
+
const native = gatePluginNative(getPluginNative(), capabilities, src.pluginId);
|
|
959
|
+
const deviceState = gateDeviceState(stateDomains);
|
|
960
|
+
const voice = gateVoice(!!capabilities?.voice, src.pluginId);
|
|
961
|
+
return { device: native, native, deviceState, voice };
|
|
962
|
+
}
|
|
963
|
+
var init_pluginRuntime = __esm({
|
|
964
|
+
"src/pluginRuntime.ts"() {
|
|
965
|
+
"use strict";
|
|
966
|
+
init_host();
|
|
967
|
+
init_deviceStateHost();
|
|
968
|
+
init_voiceHost();
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
// src/bridge/native.ts
|
|
973
|
+
function buildContext(pluginId, connectorId) {
|
|
974
|
+
const user = (0, import_core2.getUser)();
|
|
975
|
+
return {
|
|
976
|
+
tenant: (0, import_core2.getTenant)(),
|
|
977
|
+
pluginId,
|
|
978
|
+
connectorId,
|
|
979
|
+
user: user ? { id: user.actorId || "", name: user.name || "", roles: user.roles || [] } : void 0
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
function useNativeBridge(pluginId, connectorId, commandBufferRef) {
|
|
983
|
+
const initCallbackRef = (0, import_react4.useRef)(null);
|
|
984
|
+
const commandListenersRef = (0, import_react4.useRef)(/* @__PURE__ */ new Set());
|
|
985
|
+
const initializedRef = (0, import_react4.useRef)(false);
|
|
986
|
+
const fireInit = (0, import_react4.useCallback)(() => {
|
|
987
|
+
if (initializedRef.current || !initCallbackRef.current) return;
|
|
988
|
+
initCallbackRef.current(buildContext(pluginId, connectorId));
|
|
989
|
+
initializedRef.current = true;
|
|
990
|
+
}, [pluginId, connectorId]);
|
|
991
|
+
const onInit = (0, import_react4.useCallback)((cb) => {
|
|
992
|
+
initCallbackRef.current = cb;
|
|
993
|
+
setTimeout(fireInit, 0);
|
|
994
|
+
}, [fireInit]);
|
|
995
|
+
const mcpCall = (0, import_react4.useCallback)(async (toolOrConnector, argsOrTool, cIdOrArgs) => {
|
|
996
|
+
let tool;
|
|
997
|
+
let args;
|
|
998
|
+
let cId;
|
|
999
|
+
if (typeof argsOrTool === "string") {
|
|
1000
|
+
cId = toolOrConnector;
|
|
1001
|
+
tool = argsOrTool;
|
|
1002
|
+
args = cIdOrArgs || {};
|
|
1003
|
+
} else {
|
|
1004
|
+
tool = toolOrConnector;
|
|
1005
|
+
args = argsOrTool;
|
|
1006
|
+
cId = typeof cIdOrArgs === "string" ? cIdOrArgs : void 0;
|
|
1007
|
+
}
|
|
1008
|
+
const targetConnector = cId || connectorId;
|
|
1009
|
+
if (!targetConnector) {
|
|
1010
|
+
console.warn(`[NativeBridge:${pluginId}] mcpCall failed \u2014 no connectorId. tool=${tool}`);
|
|
1011
|
+
throw new Error(`[NativeBridge:${pluginId}] mcpCall requires a connectorId`);
|
|
1012
|
+
}
|
|
1013
|
+
try {
|
|
1014
|
+
const data = await (0, import_core2.connectorCall)(targetConnector, tool, args);
|
|
1015
|
+
if (!data.ok && data.error) {
|
|
1016
|
+
throw new Error(data.error);
|
|
1017
|
+
}
|
|
1018
|
+
return (0, import_core2.unwrapMcpResult)(data.result);
|
|
1019
|
+
} catch (err) {
|
|
1020
|
+
console.warn(`[NativeBridge:${pluginId}] mcpCall failed \u2192 connector=${targetConnector} tool=${tool}:`, err?.message || err);
|
|
1021
|
+
throw err;
|
|
1022
|
+
}
|
|
1023
|
+
}, [pluginId, connectorId]);
|
|
1024
|
+
const mcpFetch = (0, import_react4.useCallback)(async (body) => {
|
|
1025
|
+
return (0, import_core2.mcpFetch)(body);
|
|
1026
|
+
}, []);
|
|
1027
|
+
const sendCommand = (0, import_react4.useCallback)((type, payload) => {
|
|
1028
|
+
if (type === "plugin.command.result" && payload?.correlationId) {
|
|
1029
|
+
(0, import_core2.postCommandResult)(payload.correlationId, payload.result, payload.error);
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
}, [pluginId]);
|
|
1033
|
+
const onCommand = (0, import_react4.useCallback)((cb) => {
|
|
1034
|
+
commandListenersRef.current.add(cb);
|
|
1035
|
+
if (commandBufferRef) {
|
|
1036
|
+
const buf = commandBufferRef.current;
|
|
1037
|
+
const remaining = [];
|
|
1038
|
+
for (const cmd of buf) {
|
|
1039
|
+
if (cmd.pluginId === pluginId && cmd.command !== "__focus") {
|
|
1040
|
+
try {
|
|
1041
|
+
cb(cmd.command, { ...cmd.args, correlationId: cmd.correlationId });
|
|
1042
|
+
} catch {
|
|
1043
|
+
}
|
|
1044
|
+
} else {
|
|
1045
|
+
remaining.push(cmd);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
commandBufferRef.current = remaining;
|
|
1049
|
+
}
|
|
1050
|
+
for (const cmd of import_core2.pluginCommandEmitter.drainPluginCommands(pluginId)) {
|
|
1051
|
+
try {
|
|
1052
|
+
cb(cmd.command, { ...cmd.args, correlationId: cmd.correlationId });
|
|
1053
|
+
} catch {
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
return () => {
|
|
1057
|
+
commandListenersRef.current.delete(cb);
|
|
1058
|
+
};
|
|
1059
|
+
}, [pluginId, commandBufferRef]);
|
|
1060
|
+
(0, import_react4.useEffect)(() => {
|
|
1061
|
+
const unsub = import_core2.pluginCommandEmitter.on("plugin-command", (cmd) => {
|
|
1062
|
+
if (cmd.pluginId !== pluginId) return;
|
|
1063
|
+
if (cmd.command === "__focus") return;
|
|
1064
|
+
commandListenersRef.current.forEach((fn) => {
|
|
1065
|
+
try {
|
|
1066
|
+
fn(cmd.command, { ...cmd.args, correlationId: cmd.correlationId });
|
|
1067
|
+
} catch {
|
|
1068
|
+
}
|
|
1069
|
+
});
|
|
1070
|
+
});
|
|
1071
|
+
return unsub;
|
|
1072
|
+
}, [pluginId]);
|
|
1073
|
+
(0, import_react4.useEffect)(() => {
|
|
1074
|
+
const timer = setTimeout(() => {
|
|
1075
|
+
const dispatch = (cmd) => {
|
|
1076
|
+
commandListenersRef.current.forEach((fn) => {
|
|
1077
|
+
try {
|
|
1078
|
+
fn(cmd.command, { ...cmd.args, correlationId: cmd.correlationId });
|
|
1079
|
+
} catch {
|
|
1080
|
+
}
|
|
1081
|
+
});
|
|
1082
|
+
};
|
|
1083
|
+
if (commandBufferRef) {
|
|
1084
|
+
const remaining = [];
|
|
1085
|
+
for (const cmd of commandBufferRef.current) {
|
|
1086
|
+
if (cmd.pluginId === pluginId && cmd.command !== "__focus") dispatch(cmd);
|
|
1087
|
+
else remaining.push(cmd);
|
|
1088
|
+
}
|
|
1089
|
+
commandBufferRef.current = remaining;
|
|
1090
|
+
}
|
|
1091
|
+
for (const cmd of import_core2.pluginCommandEmitter.drainPluginCommands(pluginId)) {
|
|
1092
|
+
dispatch(cmd);
|
|
1093
|
+
}
|
|
1094
|
+
}, 500);
|
|
1095
|
+
return () => clearTimeout(timer);
|
|
1096
|
+
}, [pluginId, commandBufferRef]);
|
|
1097
|
+
(0, import_react4.useEffect)(() => {
|
|
1098
|
+
return () => {
|
|
1099
|
+
commandListenersRef.current.clear();
|
|
1100
|
+
initializedRef.current = false;
|
|
1101
|
+
};
|
|
1102
|
+
}, []);
|
|
1103
|
+
const close = (0, import_react4.useCallback)(() => {
|
|
1104
|
+
import_core2.pluginCommandEmitter.emit("plugin-close", { pluginId });
|
|
1105
|
+
}, [pluginId]);
|
|
1106
|
+
const sendMessage = (0, import_react4.useCallback)((text) => {
|
|
1107
|
+
const t = (text || "").trim();
|
|
1108
|
+
if (!t) return;
|
|
1109
|
+
try {
|
|
1110
|
+
import_core2.useChatStore.getState().sendMessage(t);
|
|
1111
|
+
} catch (err) {
|
|
1112
|
+
console.warn("[bridge.sendMessage] failed:", err instanceof Error ? err.message : String(err));
|
|
1113
|
+
}
|
|
1114
|
+
}, []);
|
|
1115
|
+
const prepareMessage = (0, import_react4.useCallback)((text) => {
|
|
1116
|
+
const t = (text || "").trim();
|
|
1117
|
+
if (!t) return;
|
|
1118
|
+
import_core2.pluginCommandEmitter.emit("prepare-chat-message", { text: t });
|
|
1119
|
+
}, []);
|
|
1120
|
+
return (0, import_react4.useMemo)(
|
|
1121
|
+
() => ({ onInit, mcpCall, mcpFetch, sendCommand, onCommand, close, sendMessage, prepareMessage }),
|
|
1122
|
+
[onInit, mcpCall, mcpFetch, sendCommand, onCommand, close, sendMessage, prepareMessage]
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
function createNativeBridge(pluginId, connectorId) {
|
|
1126
|
+
const commandListeners = /* @__PURE__ */ new Set();
|
|
1127
|
+
return {
|
|
1128
|
+
onInit(cb) {
|
|
1129
|
+
setTimeout(() => {
|
|
1130
|
+
cb(buildContext(pluginId, connectorId));
|
|
1131
|
+
}, 0);
|
|
1132
|
+
},
|
|
1133
|
+
async mcpCall(tool, args, cId) {
|
|
1134
|
+
const targetConnector = cId || connectorId;
|
|
1135
|
+
if (!targetConnector) {
|
|
1136
|
+
throw new Error(`[NativeBridge:${pluginId}] mcpCall requires a connectorId`);
|
|
1137
|
+
}
|
|
1138
|
+
const data = await (0, import_core2.connectorCall)(targetConnector, tool, args);
|
|
1139
|
+
if (!data.ok && data.error) throw new Error(data.error);
|
|
1140
|
+
return (0, import_core2.unwrapMcpResult)(data.result);
|
|
1141
|
+
},
|
|
1142
|
+
async mcpFetch(body) {
|
|
1143
|
+
return (0, import_core2.mcpFetch)(body);
|
|
1144
|
+
},
|
|
1145
|
+
sendCommand(_type, _payload) {
|
|
1146
|
+
},
|
|
1147
|
+
onCommand(cb) {
|
|
1148
|
+
commandListeners.add(cb);
|
|
1149
|
+
return () => {
|
|
1150
|
+
commandListeners.delete(cb);
|
|
1151
|
+
};
|
|
1152
|
+
},
|
|
1153
|
+
close() {
|
|
1154
|
+
import_core2.pluginCommandEmitter.emit("plugin-close", { pluginId });
|
|
1155
|
+
},
|
|
1156
|
+
sendMessage(text) {
|
|
1157
|
+
const t = (text || "").trim();
|
|
1158
|
+
if (!t) return;
|
|
1159
|
+
try {
|
|
1160
|
+
import_core2.useChatStore.getState().sendMessage(t);
|
|
1161
|
+
} catch (err) {
|
|
1162
|
+
console.warn("[bridge.sendMessage] failed:", err instanceof Error ? err.message : String(err));
|
|
1163
|
+
}
|
|
1164
|
+
},
|
|
1165
|
+
prepareMessage(text) {
|
|
1166
|
+
const t = (text || "").trim();
|
|
1167
|
+
if (!t) return;
|
|
1168
|
+
import_core2.pluginCommandEmitter.emit("prepare-chat-message", { text: t });
|
|
1169
|
+
}
|
|
581
1170
|
};
|
|
582
1171
|
}
|
|
583
|
-
var
|
|
584
|
-
|
|
585
|
-
|
|
1172
|
+
var import_react4, import_core2;
|
|
1173
|
+
var init_native = __esm({
|
|
1174
|
+
"src/bridge/native.ts"() {
|
|
1175
|
+
"use strict";
|
|
1176
|
+
import_react4 = require("react");
|
|
1177
|
+
import_core2 = require("@ateam-ai-mobile/core");
|
|
1178
|
+
}
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
// src/tools/registry.ts
|
|
1182
|
+
function setDeviceToolContextProvider(fn) {
|
|
1183
|
+
contextProvider = fn;
|
|
586
1184
|
}
|
|
587
|
-
function
|
|
588
|
-
|
|
1185
|
+
function defaultContext(pluginId, capabilities, stateDomains) {
|
|
1186
|
+
return {
|
|
1187
|
+
bridge: createNativeBridge(pluginId),
|
|
1188
|
+
native: gatePluginNative(getPluginNative(), capabilities, pluginId),
|
|
1189
|
+
deviceState: gateDeviceState(stateDomains)
|
|
1190
|
+
};
|
|
589
1191
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
}
|
|
1192
|
+
function registerDeviceTools(pluginId, tools, capabilities, stateDomains) {
|
|
1193
|
+
const accepted = [];
|
|
1194
|
+
for (const tool of tools || []) {
|
|
1195
|
+
if (!tool || typeof tool.name !== "string" || typeof tool.run !== "function") {
|
|
1196
|
+
console.warn(`[deviceTools] ${pluginId}: skipping malformed tool entry`);
|
|
1197
|
+
continue;
|
|
1198
|
+
}
|
|
1199
|
+
const existing = registry.get(tool.name);
|
|
1200
|
+
if (existing && existing.pluginId !== pluginId) {
|
|
1201
|
+
console.warn(
|
|
1202
|
+
`[deviceTools] ${pluginId}: "${tool.name}" already registered by ${existing.pluginId} \u2014 refusing to override`
|
|
1203
|
+
);
|
|
1204
|
+
continue;
|
|
604
1205
|
}
|
|
605
|
-
|
|
1206
|
+
registry.set(tool.name, { pluginId, tool, capabilities, stateDomains });
|
|
1207
|
+
accepted.push(tool.name);
|
|
606
1208
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
function registerBundleModules(modules) {
|
|
610
|
-
for (const [name, mod] of Object.entries(modules)) {
|
|
611
|
-
_bundleModules.set(name, mod);
|
|
1209
|
+
if (accepted.length > 0) {
|
|
1210
|
+
console.log(`[deviceTools] ${pluginId} registered: ${accepted.join(", ")}`);
|
|
612
1211
|
}
|
|
1212
|
+
return accepted;
|
|
613
1213
|
}
|
|
614
|
-
function
|
|
615
|
-
|
|
1214
|
+
function unregisterDeviceTools(pluginId) {
|
|
1215
|
+
for (const [name, entry] of registry.entries()) {
|
|
1216
|
+
if (entry.pluginId === pluginId) registry.delete(name);
|
|
1217
|
+
}
|
|
616
1218
|
}
|
|
1219
|
+
function hasDeviceTool(name, pluginId) {
|
|
1220
|
+
const entry = registry.get(name);
|
|
1221
|
+
return !!entry && (pluginId === void 0 || entry.pluginId === pluginId);
|
|
1222
|
+
}
|
|
1223
|
+
function listDeviceTools() {
|
|
1224
|
+
return Array.from(registry.entries()).map(([name, { pluginId, tool }]) => ({
|
|
1225
|
+
name,
|
|
1226
|
+
pluginId,
|
|
1227
|
+
description: tool.description,
|
|
1228
|
+
input_schema: tool.input_schema
|
|
1229
|
+
}));
|
|
1230
|
+
}
|
|
1231
|
+
async function callDeviceTool(name, args = {}, opts = {}) {
|
|
1232
|
+
const entry = registry.get(name);
|
|
1233
|
+
if (!entry) {
|
|
1234
|
+
return { ok: false, reason: "not_found", error: `No device tool registered as "${name}"` };
|
|
1235
|
+
}
|
|
1236
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
1237
|
+
let timer;
|
|
1238
|
+
try {
|
|
1239
|
+
const build = contextProvider || defaultContext;
|
|
1240
|
+
const built = build(entry.pluginId, entry.capabilities, entry.stateDomains);
|
|
1241
|
+
const ctx = {
|
|
1242
|
+
...built,
|
|
1243
|
+
// Whoever built the context, a call cannot own a subscription.
|
|
1244
|
+
deviceState: withoutSubscribe(built.deviceState),
|
|
1245
|
+
signal: opts.signal
|
|
1246
|
+
};
|
|
1247
|
+
const result = await Promise.race([
|
|
1248
|
+
Promise.resolve(entry.tool.run(args, ctx)),
|
|
1249
|
+
new Promise((_, reject) => {
|
|
1250
|
+
timer = setTimeout(
|
|
1251
|
+
() => reject(new Error(`Device tool "${name}" timed out after ${timeoutMs}ms`)),
|
|
1252
|
+
timeoutMs
|
|
1253
|
+
);
|
|
1254
|
+
})
|
|
1255
|
+
]);
|
|
1256
|
+
return { ok: true, result };
|
|
1257
|
+
} catch (err) {
|
|
1258
|
+
const message = err?.message || String(err);
|
|
1259
|
+
const timedOut = message.includes("timed out after");
|
|
1260
|
+
return { ok: false, reason: timedOut ? "timeout" : "failed", error: message };
|
|
1261
|
+
} finally {
|
|
1262
|
+
if (timer) clearTimeout(timer);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
function clearDeviceTools() {
|
|
1266
|
+
registry.clear();
|
|
1267
|
+
}
|
|
1268
|
+
var registry, contextProvider, DEFAULT_TIMEOUT_MS;
|
|
1269
|
+
var init_registry = __esm({
|
|
1270
|
+
"src/tools/registry.ts"() {
|
|
1271
|
+
"use strict";
|
|
1272
|
+
init_native();
|
|
1273
|
+
init_host();
|
|
1274
|
+
init_deviceStateHost();
|
|
1275
|
+
registry = /* @__PURE__ */ new Map();
|
|
1276
|
+
contextProvider = null;
|
|
1277
|
+
DEFAULT_TIMEOUT_MS = 3e4;
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
617
1280
|
|
|
618
|
-
// src/
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
1281
|
+
// src/api/plugins.ts
|
|
1282
|
+
function supportsNative(manifest) {
|
|
1283
|
+
const mode = manifest.render?.mode;
|
|
1284
|
+
return mode === "react-native" || mode === "adaptive";
|
|
1285
|
+
}
|
|
1286
|
+
function getNativeComponentId(manifest) {
|
|
1287
|
+
if (manifest.render?.mode === "react-native") return manifest.render.component;
|
|
1288
|
+
if (manifest.render?.mode === "adaptive") return manifest.render.reactNative?.component;
|
|
1289
|
+
return null;
|
|
1290
|
+
}
|
|
1291
|
+
function getNativeBundleUrl(manifest) {
|
|
1292
|
+
const direct = manifest.bundleUrl;
|
|
1293
|
+
if (direct) return direct;
|
|
1294
|
+
const render = manifest.render;
|
|
1295
|
+
if (render.mode === "react-native") return render.bundleUrl || null;
|
|
1296
|
+
if (render.mode === "adaptive") return render.reactNative?.bundleUrl || null;
|
|
1297
|
+
return null;
|
|
1298
|
+
}
|
|
1299
|
+
function getNativeBundleHash(manifest) {
|
|
1300
|
+
const direct = manifest.bundleHash;
|
|
1301
|
+
if (direct) return direct;
|
|
1302
|
+
const render = manifest.render;
|
|
1303
|
+
if (render.mode === "react-native") return render.bundleHash || null;
|
|
1304
|
+
if (render.mode === "adaptive") return render.reactNative?.bundleHash || null;
|
|
1305
|
+
return null;
|
|
1306
|
+
}
|
|
1307
|
+
function supportsWebView(manifest) {
|
|
1308
|
+
return manifest.render?.mode === "iframe" || manifest.render?.mode === "adaptive";
|
|
1309
|
+
}
|
|
1310
|
+
async function listPlugins() {
|
|
1311
|
+
const res = await (0, import_core3.apiFetch)("/mcp", {
|
|
1312
|
+
method: "POST",
|
|
1313
|
+
body: JSON.stringify({
|
|
1314
|
+
jsonrpc: "2.0",
|
|
1315
|
+
id: Date.now(),
|
|
1316
|
+
method: "tools/call",
|
|
1317
|
+
params: {
|
|
1318
|
+
name: "cp.listContextPlugins",
|
|
1319
|
+
arguments: {}
|
|
1320
|
+
}
|
|
1321
|
+
})
|
|
1322
|
+
});
|
|
1323
|
+
const structured = res.result?.structuredContent?.plugins;
|
|
1324
|
+
if (structured) return structured;
|
|
1325
|
+
const text = res.result?.content?.[0]?.text;
|
|
1326
|
+
if (!text) return [];
|
|
1327
|
+
try {
|
|
1328
|
+
const parsed = JSON.parse(text);
|
|
1329
|
+
return parsed.plugins || parsed || [];
|
|
1330
|
+
} catch {
|
|
1331
|
+
return [];
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
async function getPlugin(pluginId) {
|
|
1335
|
+
const res = await (0, import_core3.apiFetch)("/mcp", {
|
|
1336
|
+
method: "POST",
|
|
1337
|
+
body: JSON.stringify({
|
|
1338
|
+
jsonrpc: "2.0",
|
|
1339
|
+
id: Date.now(),
|
|
1340
|
+
method: "tools/call",
|
|
1341
|
+
params: {
|
|
1342
|
+
name: "cp.getContextPlugin",
|
|
1343
|
+
arguments: { id: pluginId }
|
|
1344
|
+
// Backend expects { id }, NOT { pluginId }
|
|
1345
|
+
}
|
|
1346
|
+
})
|
|
1347
|
+
});
|
|
1348
|
+
if (res.result?.structuredContent) return res.result.structuredContent;
|
|
1349
|
+
const text = res.result?.content?.[0]?.text;
|
|
1350
|
+
if (!text) return null;
|
|
1351
|
+
try {
|
|
1352
|
+
const parsed = JSON.parse(text);
|
|
1353
|
+
if (parsed.error) {
|
|
1354
|
+
console.warn("[getPlugin] Backend error:", parsed.error);
|
|
1355
|
+
return null;
|
|
1356
|
+
}
|
|
1357
|
+
return parsed;
|
|
1358
|
+
} catch {
|
|
1359
|
+
return null;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
function resolvePluginUrl(manifest, tenant) {
|
|
1363
|
+
const render = manifest.render;
|
|
1364
|
+
let url;
|
|
1365
|
+
if (render?.mode === "iframe") {
|
|
1366
|
+
url = render.iframeUrl;
|
|
1367
|
+
} else if (render?.mode === "adaptive") {
|
|
1368
|
+
url = render.iframeUrl || render.iframe?.iframeUrl;
|
|
1369
|
+
}
|
|
1370
|
+
if (!url) return "";
|
|
1371
|
+
if (url.startsWith("http")) {
|
|
1372
|
+
const sep = url.includes("?") ? "&" : "?";
|
|
1373
|
+
return tenant ? `${url}${sep}tenant=${encodeURIComponent(tenant)}` : url;
|
|
1374
|
+
}
|
|
1375
|
+
if (tenant && url.startsWith("/mcp-ui/")) {
|
|
1376
|
+
const tenantUrl = url.replace("/mcp-ui/", `/mcp-ui/${encodeURIComponent(tenant)}/`);
|
|
1377
|
+
return `${(0, import_core4.getCoreApiUrl)()}${tenantUrl}`;
|
|
1378
|
+
}
|
|
1379
|
+
return `${(0, import_core4.getCoreApiUrl)()}${url}`;
|
|
1380
|
+
}
|
|
1381
|
+
var import_core3, import_core4;
|
|
1382
|
+
var init_plugins = __esm({
|
|
1383
|
+
"src/api/plugins.ts"() {
|
|
1384
|
+
"use strict";
|
|
1385
|
+
import_core3 = require("@ateam-ai-mobile/core");
|
|
1386
|
+
import_core4 = require("@ateam-ai-mobile/core");
|
|
1387
|
+
}
|
|
1388
|
+
});
|
|
629
1389
|
|
|
630
1390
|
// src/surfaces/AdaOrb.tsx
|
|
631
|
-
var import_react4 = require("react");
|
|
632
|
-
var import_react_native4 = require("react-native");
|
|
633
|
-
var import_react_native_reanimated = __toESM(require("react-native-reanimated"));
|
|
634
|
-
var import_react_native_svg = __toESM(require("react-native-svg"));
|
|
635
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
636
|
-
var AnimatedCircle = import_react_native_reanimated.default.createAnimatedComponent(import_react_native_svg.Circle);
|
|
637
1391
|
function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
638
1392
|
const showInscription = inscription && size >= 60;
|
|
639
1393
|
const center = size / 2;
|
|
@@ -649,7 +1403,7 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
649
1403
|
const orbit2 = (0, import_react_native_reanimated.useSharedValue)(0);
|
|
650
1404
|
const orbit3 = (0, import_react_native_reanimated.useSharedValue)(0);
|
|
651
1405
|
const beat = (0, import_react_native_reanimated.useSharedValue)(1);
|
|
652
|
-
(0,
|
|
1406
|
+
(0, import_react5.useEffect)(() => {
|
|
653
1407
|
(0, import_react_native_reanimated.cancelAnimation)(coreScale);
|
|
654
1408
|
(0, import_react_native_reanimated.cancelAnimation)(innerScale);
|
|
655
1409
|
(0, import_react_native_reanimated.cancelAnimation)(outerScale);
|
|
@@ -799,29 +1553,29 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
799
1553
|
opacity: state === "thinking" ? 0.7 : 0
|
|
800
1554
|
};
|
|
801
1555
|
});
|
|
802
|
-
return /* @__PURE__ */ (0,
|
|
803
|
-
/* @__PURE__ */ (0,
|
|
804
|
-
/* @__PURE__ */ (0,
|
|
805
|
-
/* @__PURE__ */ (0,
|
|
806
|
-
/* @__PURE__ */ (0,
|
|
807
|
-
/* @__PURE__ */ (0,
|
|
808
|
-
/* @__PURE__ */ (0,
|
|
1556
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native4.View, { style: [styles2.wrap, { width: size, height: size }], children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native_svg.default, { width: size, height: size, children: [
|
|
1557
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native_svg.Defs, { children: [
|
|
1558
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native_svg.RadialGradient, { id: "adaCore", cx: "36%", cy: "30%", r: "70%", children: [
|
|
1559
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "0%", stopColor: "#FFE0B0", stopOpacity: "1" }),
|
|
1560
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "28%", stopColor: "#FFA85A", stopOpacity: "1" }),
|
|
1561
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "65%", stopColor: "#FF7A28", stopOpacity: "1" }),
|
|
1562
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "100%", stopColor: "#B8410A", stopOpacity: "1" })
|
|
809
1563
|
] }),
|
|
810
|
-
/* @__PURE__ */ (0,
|
|
811
|
-
/* @__PURE__ */ (0,
|
|
812
|
-
/* @__PURE__ */ (0,
|
|
1564
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native_svg.RadialGradient, { id: "adaInner", cx: "50%", cy: "50%", r: "50%", children: [
|
|
1565
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "0%", stopColor: "#FF8A3C", stopOpacity: "0.55" }),
|
|
1566
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "100%", stopColor: "#FF7028", stopOpacity: "0" })
|
|
813
1567
|
] }),
|
|
814
|
-
/* @__PURE__ */ (0,
|
|
815
|
-
/* @__PURE__ */ (0,
|
|
816
|
-
/* @__PURE__ */ (0,
|
|
1568
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native_svg.RadialGradient, { id: "adaOuter", cx: "50%", cy: "50%", r: "50%", children: [
|
|
1569
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "0%", stopColor: "#FF7A28", stopOpacity: "0.4" }),
|
|
1570
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "100%", stopColor: "#FF6018", stopOpacity: "0" })
|
|
817
1571
|
] }),
|
|
818
|
-
/* @__PURE__ */ (0,
|
|
819
|
-
/* @__PURE__ */ (0,
|
|
820
|
-
/* @__PURE__ */ (0,
|
|
821
|
-
/* @__PURE__ */ (0,
|
|
1572
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native_svg.RadialGradient, { id: "adaSpark", cx: "50%", cy: "50%", r: "50%", children: [
|
|
1573
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "0%", stopColor: "#FFFAEB", stopOpacity: "1" }),
|
|
1574
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "60%", stopColor: "#FFD095", stopOpacity: "0.9" }),
|
|
1575
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "100%", stopColor: "#FFA85A", stopOpacity: "0" })
|
|
822
1576
|
] })
|
|
823
1577
|
] }),
|
|
824
|
-
/* @__PURE__ */ (0,
|
|
1578
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
825
1579
|
AnimatedCircle,
|
|
826
1580
|
{
|
|
827
1581
|
cx: center,
|
|
@@ -832,7 +1586,7 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
832
1586
|
animatedProps: ripple1Props
|
|
833
1587
|
}
|
|
834
1588
|
),
|
|
835
|
-
/* @__PURE__ */ (0,
|
|
1589
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
836
1590
|
AnimatedCircle,
|
|
837
1591
|
{
|
|
838
1592
|
cx: center,
|
|
@@ -843,7 +1597,7 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
843
1597
|
animatedProps: ripple2Props
|
|
844
1598
|
}
|
|
845
1599
|
),
|
|
846
|
-
/* @__PURE__ */ (0,
|
|
1600
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
847
1601
|
AnimatedCircle,
|
|
848
1602
|
{
|
|
849
1603
|
cx: center,
|
|
@@ -854,10 +1608,10 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
854
1608
|
animatedProps: ripple3Props
|
|
855
1609
|
}
|
|
856
1610
|
),
|
|
857
|
-
/* @__PURE__ */ (0,
|
|
858
|
-
/* @__PURE__ */ (0,
|
|
859
|
-
/* @__PURE__ */ (0,
|
|
860
|
-
/* @__PURE__ */ (0,
|
|
1611
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AnimatedCircle, { cx: center, cy: center, fill: "url(#adaOuter)", animatedProps: outerProps }),
|
|
1612
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AnimatedCircle, { cx: center, cy: center, fill: "url(#adaInner)", animatedProps: innerProps }),
|
|
1613
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AnimatedCircle, { cx: center, cy: center, fill: "url(#adaCore)", animatedProps: coreProps }),
|
|
1614
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
861
1615
|
AnimatedCircle,
|
|
862
1616
|
{
|
|
863
1617
|
r: Math.max(3, size * 0.05),
|
|
@@ -865,7 +1619,7 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
865
1619
|
animatedProps: spark1Props
|
|
866
1620
|
}
|
|
867
1621
|
),
|
|
868
|
-
/* @__PURE__ */ (0,
|
|
1622
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
869
1623
|
AnimatedCircle,
|
|
870
1624
|
{
|
|
871
1625
|
r: Math.max(2.5, size * 0.04),
|
|
@@ -873,7 +1627,7 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
873
1627
|
animatedProps: spark2Props
|
|
874
1628
|
}
|
|
875
1629
|
),
|
|
876
|
-
/* @__PURE__ */ (0,
|
|
1630
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
877
1631
|
AnimatedCircle,
|
|
878
1632
|
{
|
|
879
1633
|
r: Math.max(2, size * 0.032),
|
|
@@ -881,7 +1635,7 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
881
1635
|
animatedProps: spark3Props
|
|
882
1636
|
}
|
|
883
1637
|
),
|
|
884
|
-
showInscription && /* @__PURE__ */ (0,
|
|
1638
|
+
showInscription && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
885
1639
|
import_react_native_svg.Text,
|
|
886
1640
|
{
|
|
887
1641
|
x: center,
|
|
@@ -896,17 +1650,26 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
896
1650
|
)
|
|
897
1651
|
] }) });
|
|
898
1652
|
}
|
|
899
|
-
var
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
1653
|
+
var import_react5, import_react_native4, import_react_native_reanimated, import_react_native_svg, import_jsx_runtime2, AnimatedCircle, styles2;
|
|
1654
|
+
var init_AdaOrb = __esm({
|
|
1655
|
+
"src/surfaces/AdaOrb.tsx"() {
|
|
1656
|
+
"use strict";
|
|
1657
|
+
import_react5 = require("react");
|
|
1658
|
+
import_react_native4 = require("react-native");
|
|
1659
|
+
import_react_native_reanimated = __toESM(require("react-native-reanimated"));
|
|
1660
|
+
import_react_native_svg = __toESM(require("react-native-svg"));
|
|
1661
|
+
import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1662
|
+
AnimatedCircle = import_react_native_reanimated.default.createAnimatedComponent(import_react_native_svg.Circle);
|
|
1663
|
+
styles2 = import_react_native4.StyleSheet.create({
|
|
1664
|
+
wrap: {
|
|
1665
|
+
alignItems: "center",
|
|
1666
|
+
justifyContent: "center"
|
|
1667
|
+
}
|
|
1668
|
+
});
|
|
903
1669
|
}
|
|
904
1670
|
});
|
|
905
1671
|
|
|
906
1672
|
// src/surfaces/SurfaceDrawer.tsx
|
|
907
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
908
|
-
var SCREEN_H = import_react_native5.Dimensions.get("window").height;
|
|
909
|
-
var fontFam = import_react_native5.Platform.OS === "ios" ? "SF Pro Rounded" : void 0;
|
|
910
1673
|
function SurfaceDrawer({
|
|
911
1674
|
visible,
|
|
912
1675
|
onClose,
|
|
@@ -918,9 +1681,9 @@ function SurfaceDrawer({
|
|
|
918
1681
|
children
|
|
919
1682
|
}) {
|
|
920
1683
|
const sheetH = Math.round(SCREEN_H * heightRatio);
|
|
921
|
-
const fadeAnim = (0,
|
|
922
|
-
const slideAnim = (0,
|
|
923
|
-
(0,
|
|
1684
|
+
const fadeAnim = (0, import_react6.useRef)(new import_react_native5.Animated.Value(0)).current;
|
|
1685
|
+
const slideAnim = (0, import_react6.useRef)(new import_react_native5.Animated.Value(sheetH)).current;
|
|
1686
|
+
(0, import_react6.useEffect)(() => {
|
|
924
1687
|
if (visible) {
|
|
925
1688
|
import_react_native5.Animated.parallel([
|
|
926
1689
|
import_react_native5.Animated.timing(fadeAnim, { toValue: 1, duration: 220, useNativeDriver: true }),
|
|
@@ -938,9 +1701,9 @@ function SurfaceDrawer({
|
|
|
938
1701
|
]).start(() => onClose());
|
|
939
1702
|
};
|
|
940
1703
|
if (!visible) return null;
|
|
941
|
-
return /* @__PURE__ */ (0,
|
|
942
|
-
/* @__PURE__ */ (0,
|
|
943
|
-
/* @__PURE__ */ (0,
|
|
1704
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native5.Animated.View, { style: [styles3.overlay, { opacity: fadeAnim }], pointerEvents: "auto", children: [
|
|
1705
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native5.Pressable, { style: import_react_native5.StyleSheet.absoluteFill, onPress: close }),
|
|
1706
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
944
1707
|
import_react_native5.Animated.View,
|
|
945
1708
|
{
|
|
946
1709
|
style: [
|
|
@@ -948,21 +1711,21 @@ function SurfaceDrawer({
|
|
|
948
1711
|
{ height: sheetH, transform: [{ translateY: slideAnim }] }
|
|
949
1712
|
],
|
|
950
1713
|
children: [
|
|
951
|
-
/* @__PURE__ */ (0,
|
|
952
|
-
/* @__PURE__ */ (0,
|
|
953
|
-
showOrb ? /* @__PURE__ */ (0,
|
|
954
|
-
/* @__PURE__ */ (0,
|
|
955
|
-
/* @__PURE__ */ (0,
|
|
956
|
-
subtitle ? /* @__PURE__ */ (0,
|
|
957
|
-
privacy ? /* @__PURE__ */ (0,
|
|
958
|
-
/* @__PURE__ */ (0,
|
|
959
|
-
/* @__PURE__ */ (0,
|
|
960
|
-
/* @__PURE__ */ (0,
|
|
1714
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native5.View, { style: styles3.handle }),
|
|
1715
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native5.View, { style: styles3.header, children: [
|
|
1716
|
+
showOrb ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native5.View, { style: styles3.headerOrb, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AdaOrb, { size: 36, state: "idle" }) }) : null,
|
|
1717
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native5.View, { style: { flex: 1 }, children: [
|
|
1718
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native5.Text, { style: styles3.title, children: title }),
|
|
1719
|
+
subtitle ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native5.Text, { style: styles3.subtitle, children: subtitle }) : null,
|
|
1720
|
+
privacy ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native5.View, { style: styles3.privacyPill, children: [
|
|
1721
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native_svg2.default, { width: 11, height: 11, viewBox: "0 0 24 24", fill: "none", stroke: import_core5.ada.inkSoft, strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1722
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_svg2.Path, { d: "M19 11H5a2 2 0 00-2 2v7a2 2 0 002 2h14a2 2 0 002-2v-7a2 2 0 00-2-2z" }),
|
|
1723
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_svg2.Path, { d: "M7 11V7a5 5 0 0110 0v4" })
|
|
961
1724
|
] }),
|
|
962
|
-
/* @__PURE__ */ (0,
|
|
1725
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native5.Text, { style: styles3.privacyText, children: "Only you can see this" })
|
|
963
1726
|
] }) : null
|
|
964
1727
|
] }),
|
|
965
|
-
/* @__PURE__ */ (0,
|
|
1728
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
966
1729
|
import_react_native5.Pressable,
|
|
967
1730
|
{
|
|
968
1731
|
accessibilityRole: "button",
|
|
@@ -970,105 +1733,115 @@ function SurfaceDrawer({
|
|
|
970
1733
|
style: styles3.closeBtn,
|
|
971
1734
|
onPress: close,
|
|
972
1735
|
hitSlop: 12,
|
|
973
|
-
children: /* @__PURE__ */ (0,
|
|
1736
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_svg2.default, { width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", stroke: import_core5.ada.inkSoft, strokeWidth: 2.2, strokeLinecap: "round", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_svg2.Path, { d: "M18 6L6 18M6 6l12 12" }) })
|
|
974
1737
|
}
|
|
975
1738
|
)
|
|
976
1739
|
] }),
|
|
977
|
-
/* @__PURE__ */ (0,
|
|
1740
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native5.View, { style: styles3.body, children })
|
|
978
1741
|
]
|
|
979
1742
|
}
|
|
980
1743
|
)
|
|
981
1744
|
] });
|
|
982
1745
|
}
|
|
983
|
-
var
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1746
|
+
var import_react6, import_react_native5, import_react_native_svg2, import_core5, import_jsx_runtime3, SCREEN_H, fontFam, styles3;
|
|
1747
|
+
var init_SurfaceDrawer = __esm({
|
|
1748
|
+
"src/surfaces/SurfaceDrawer.tsx"() {
|
|
1749
|
+
"use strict";
|
|
1750
|
+
import_react6 = require("react");
|
|
1751
|
+
import_react_native5 = require("react-native");
|
|
1752
|
+
import_react_native_svg2 = __toESM(require("react-native-svg"));
|
|
1753
|
+
import_core5 = require("@ateam-ai-mobile/core");
|
|
1754
|
+
init_AdaOrb();
|
|
1755
|
+
import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1756
|
+
SCREEN_H = import_react_native5.Dimensions.get("window").height;
|
|
1757
|
+
fontFam = import_react_native5.Platform.OS === "ios" ? "SF Pro Rounded" : void 0;
|
|
1758
|
+
styles3 = import_react_native5.StyleSheet.create({
|
|
1759
|
+
overlay: {
|
|
1760
|
+
position: "absolute",
|
|
1761
|
+
top: 0,
|
|
1762
|
+
left: 0,
|
|
1763
|
+
right: 0,
|
|
1764
|
+
bottom: 0,
|
|
1765
|
+
backgroundColor: "rgba(26, 20, 16, 0.35)",
|
|
1766
|
+
justifyContent: "flex-end",
|
|
1767
|
+
zIndex: 999,
|
|
1768
|
+
elevation: 999
|
|
1769
|
+
},
|
|
1770
|
+
sheet: {
|
|
1771
|
+
backgroundColor: import_core5.ada.paper,
|
|
1772
|
+
borderTopLeftRadius: 28,
|
|
1773
|
+
borderTopRightRadius: 28,
|
|
1774
|
+
overflow: "hidden"
|
|
1775
|
+
},
|
|
1776
|
+
handle: {
|
|
1777
|
+
width: 40,
|
|
1778
|
+
height: 4,
|
|
1779
|
+
borderRadius: 2,
|
|
1780
|
+
backgroundColor: import_core5.ada.inkMuted,
|
|
1781
|
+
opacity: 0.3,
|
|
1782
|
+
alignSelf: "center",
|
|
1783
|
+
marginTop: 10,
|
|
1784
|
+
marginBottom: 6
|
|
1785
|
+
},
|
|
1786
|
+
header: {
|
|
1787
|
+
flexDirection: "row",
|
|
1788
|
+
alignItems: "center",
|
|
1789
|
+
paddingHorizontal: 22,
|
|
1790
|
+
paddingVertical: 16,
|
|
1791
|
+
gap: 12,
|
|
1792
|
+
borderBottomWidth: 1,
|
|
1793
|
+
borderBottomColor: import_core5.ada.border
|
|
1794
|
+
},
|
|
1795
|
+
headerOrb: {
|
|
1796
|
+
width: 36,
|
|
1797
|
+
height: 36,
|
|
1798
|
+
alignItems: "center",
|
|
1799
|
+
justifyContent: "center"
|
|
1800
|
+
},
|
|
1801
|
+
title: {
|
|
1802
|
+
fontSize: 17,
|
|
1803
|
+
fontWeight: "600",
|
|
1804
|
+
color: import_core5.ada.ink,
|
|
1805
|
+
letterSpacing: -0.2,
|
|
1806
|
+
fontFamily: fontFam
|
|
1807
|
+
},
|
|
1808
|
+
subtitle: {
|
|
1809
|
+
fontSize: 13,
|
|
1810
|
+
fontWeight: "400",
|
|
1811
|
+
color: import_core5.ada.inkSoft,
|
|
1812
|
+
marginTop: 2,
|
|
1813
|
+
letterSpacing: -0.1,
|
|
1814
|
+
fontFamily: fontFam
|
|
1815
|
+
},
|
|
1816
|
+
privacyPill: {
|
|
1817
|
+
flexDirection: "row",
|
|
1818
|
+
alignItems: "center",
|
|
1819
|
+
gap: 5,
|
|
1820
|
+
marginTop: 4
|
|
1821
|
+
},
|
|
1822
|
+
privacyText: {
|
|
1823
|
+
fontSize: 11,
|
|
1824
|
+
fontWeight: "500",
|
|
1825
|
+
color: import_core5.ada.inkSoft,
|
|
1826
|
+
letterSpacing: 0.2,
|
|
1827
|
+
fontFamily: fontFam
|
|
1828
|
+
},
|
|
1829
|
+
closeBtn: {
|
|
1830
|
+
width: 32,
|
|
1831
|
+
height: 32,
|
|
1832
|
+
borderRadius: 16,
|
|
1833
|
+
backgroundColor: import_core5.ada.paperSoft,
|
|
1834
|
+
justifyContent: "center",
|
|
1835
|
+
alignItems: "center"
|
|
1836
|
+
},
|
|
1837
|
+
body: {
|
|
1838
|
+
flex: 1
|
|
1839
|
+
}
|
|
1840
|
+
});
|
|
1064
1841
|
}
|
|
1065
1842
|
});
|
|
1066
1843
|
|
|
1067
1844
|
// src/surfaces/SurfaceCard.tsx
|
|
1068
|
-
var import_react_native6 = require("react-native");
|
|
1069
|
-
var import_core3 = require("@ateam-ai-mobile/core");
|
|
1070
|
-
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1071
|
-
var fontFam2 = import_react_native6.Platform.OS === "ios" ? "SF Pro Rounded" : void 0;
|
|
1072
1845
|
function SurfaceCard({
|
|
1073
1846
|
icon,
|
|
1074
1847
|
title,
|
|
@@ -1089,7 +1862,7 @@ function SurfaceCard({
|
|
|
1089
1862
|
getHaptics()?.impact("medium");
|
|
1090
1863
|
onLongPress();
|
|
1091
1864
|
};
|
|
1092
|
-
return /* @__PURE__ */ (0,
|
|
1865
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1093
1866
|
import_react_native6.Pressable,
|
|
1094
1867
|
{
|
|
1095
1868
|
accessibilityRole: "button",
|
|
@@ -1100,71 +1873,76 @@ function SurfaceCard({
|
|
|
1100
1873
|
onLongPress: onLongPress ? handleLongPress : void 0,
|
|
1101
1874
|
disabled: !onPress && !onLongPress,
|
|
1102
1875
|
children: [
|
|
1103
|
-
/* @__PURE__ */ (0,
|
|
1104
|
-
icon ? /* @__PURE__ */ (0,
|
|
1105
|
-
/* @__PURE__ */ (0,
|
|
1106
|
-
/* @__PURE__ */ (0,
|
|
1107
|
-
subtitle ? /* @__PURE__ */ (0,
|
|
1876
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native6.View, { style: styles4.row, children: [
|
|
1877
|
+
icon ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native6.Text, { style: styles4.icon, children: icon }) : null,
|
|
1878
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native6.View, { style: { flex: 1 }, children: [
|
|
1879
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native6.Text, { style: styles4.title, children: title }),
|
|
1880
|
+
subtitle ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native6.Text, { style: styles4.subtitle, children: subtitle }) : null
|
|
1108
1881
|
] }),
|
|
1109
|
-
arrow ? /* @__PURE__ */ (0,
|
|
1882
|
+
arrow ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native6.Text, { style: styles4.arrow, children: "\u2192" }) : null
|
|
1110
1883
|
] }),
|
|
1111
|
-
children ? /* @__PURE__ */ (0,
|
|
1884
|
+
children ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native6.View, { style: styles4.body, children }) : null
|
|
1112
1885
|
]
|
|
1113
1886
|
}
|
|
1114
1887
|
);
|
|
1115
1888
|
}
|
|
1116
|
-
var
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1889
|
+
var import_react_native6, import_core6, import_jsx_runtime4, fontFam2, styles4;
|
|
1890
|
+
var init_SurfaceCard = __esm({
|
|
1891
|
+
"src/surfaces/SurfaceCard.tsx"() {
|
|
1892
|
+
"use strict";
|
|
1893
|
+
import_react_native6 = require("react-native");
|
|
1894
|
+
init_host();
|
|
1895
|
+
import_core6 = require("@ateam-ai-mobile/core");
|
|
1896
|
+
import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1897
|
+
fontFam2 = import_react_native6.Platform.OS === "ios" ? "SF Pro Rounded" : void 0;
|
|
1898
|
+
styles4 = import_react_native6.StyleSheet.create({
|
|
1899
|
+
card: {
|
|
1900
|
+
backgroundColor: import_core6.ada.paperSoft,
|
|
1901
|
+
borderWidth: 1,
|
|
1902
|
+
borderColor: import_core6.ada.border,
|
|
1903
|
+
borderRadius: import_core6.borderRadius.xl,
|
|
1904
|
+
paddingHorizontal: 16,
|
|
1905
|
+
paddingVertical: 14
|
|
1906
|
+
},
|
|
1907
|
+
row: {
|
|
1908
|
+
flexDirection: "row",
|
|
1909
|
+
alignItems: "center",
|
|
1910
|
+
gap: 14
|
|
1911
|
+
},
|
|
1912
|
+
icon: {
|
|
1913
|
+
fontSize: 22
|
|
1914
|
+
},
|
|
1915
|
+
title: {
|
|
1916
|
+
fontSize: 15,
|
|
1917
|
+
fontWeight: "600",
|
|
1918
|
+
color: import_core6.ada.ink,
|
|
1919
|
+
letterSpacing: -0.2,
|
|
1920
|
+
fontFamily: fontFam2
|
|
1921
|
+
},
|
|
1922
|
+
subtitle: {
|
|
1923
|
+
fontSize: 13,
|
|
1924
|
+
fontWeight: "400",
|
|
1925
|
+
color: import_core6.ada.inkMuted,
|
|
1926
|
+
marginTop: 2,
|
|
1927
|
+
letterSpacing: -0.1,
|
|
1928
|
+
fontFamily: fontFam2
|
|
1929
|
+
},
|
|
1930
|
+
arrow: {
|
|
1931
|
+
fontSize: 18,
|
|
1932
|
+
color: import_core6.ada.inkMuted,
|
|
1933
|
+
fontWeight: "400"
|
|
1934
|
+
},
|
|
1935
|
+
body: {
|
|
1936
|
+
marginTop: 10,
|
|
1937
|
+
paddingTop: 10,
|
|
1938
|
+
borderTopWidth: 1,
|
|
1939
|
+
borderTopColor: import_core6.ada.border
|
|
1940
|
+
}
|
|
1941
|
+
});
|
|
1158
1942
|
}
|
|
1159
1943
|
});
|
|
1160
1944
|
|
|
1161
1945
|
// src/surfaces/SurfaceFullscreen.tsx
|
|
1162
|
-
var import_react6 = require("react");
|
|
1163
|
-
var import_react_native7 = require("react-native");
|
|
1164
|
-
var import_react_native_svg3 = __toESM(require("react-native-svg"));
|
|
1165
|
-
var import_core4 = require("@ateam-ai-mobile/core");
|
|
1166
|
-
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1167
|
-
var fontFam3 = import_react_native7.Platform.OS === "ios" ? "SF Pro Rounded" : void 0;
|
|
1168
1946
|
function SurfaceFullscreen({
|
|
1169
1947
|
visible,
|
|
1170
1948
|
onClose,
|
|
@@ -1175,9 +1953,9 @@ function SurfaceFullscreen({
|
|
|
1175
1953
|
closeIcon = "x",
|
|
1176
1954
|
closeLabel = "Close"
|
|
1177
1955
|
}) {
|
|
1178
|
-
const fadeAnim = (0,
|
|
1179
|
-
const heroScale = (0,
|
|
1180
|
-
(0,
|
|
1956
|
+
const fadeAnim = (0, import_react7.useRef)(new import_react_native7.Animated.Value(0)).current;
|
|
1957
|
+
const heroScale = (0, import_react7.useRef)(new import_react_native7.Animated.Value(0.6)).current;
|
|
1958
|
+
(0, import_react7.useEffect)(() => {
|
|
1181
1959
|
if (visible) {
|
|
1182
1960
|
import_react_native7.Animated.parallel([
|
|
1183
1961
|
import_react_native7.Animated.timing(fadeAnim, { toValue: 1, duration: 280, useNativeDriver: true }),
|
|
@@ -1197,22 +1975,22 @@ function SurfaceFullscreen({
|
|
|
1197
1975
|
if (!visible) return null;
|
|
1198
1976
|
const renderCloseIcon = () => {
|
|
1199
1977
|
if (closeIcon === "hangup") {
|
|
1200
|
-
return /* @__PURE__ */ (0,
|
|
1978
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_svg3.default, { width: 22, height: 22, viewBox: "0 0 24 24", fill: import_core7.ada.paper, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_svg3.Path, { d: "M3.68 16.07l3.92-3.11c.28-.23.68-.28 1.01-.14l2.77 1.15c.36.15.61.5.61.89v2.82c4.34-.76 7.93-4.35 8.69-8.69h-2.82c-.39 0-.74-.25-.89-.61L16.12 5.6c-.13-.33-.09-.73.14-1.01l3.11-3.92c.29-.37.81-.41 1.15-.08l3.24 3.24c.2.2.31.47.31.76C24 14.44 14.44 24 4.59 24c-.29 0-.56-.11-.76-.31L.59 20.45c-.33-.34-.29-.86.09-1.15z" }) });
|
|
1201
1979
|
}
|
|
1202
1980
|
if (closeIcon === "x") {
|
|
1203
|
-
return /* @__PURE__ */ (0,
|
|
1981
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_svg3.default, { width: 20, height: 20, viewBox: "0 0 24 24", fill: "none", stroke: import_core7.ada.paper, strokeWidth: 2.5, strokeLinecap: "round", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_svg3.Path, { d: "M18 6L6 18M6 6l12 12" }) });
|
|
1204
1982
|
}
|
|
1205
1983
|
return closeIcon;
|
|
1206
1984
|
};
|
|
1207
|
-
return /* @__PURE__ */ (0,
|
|
1208
|
-
/* @__PURE__ */ (0,
|
|
1209
|
-
showWordmark ? /* @__PURE__ */ (0,
|
|
1210
|
-
status ? /* @__PURE__ */ (0,
|
|
1985
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native7.Animated.View, { style: [styles5.overlay, { opacity: fadeAnim }], pointerEvents: "auto", children: [
|
|
1986
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native7.View, { style: styles5.topBar, children: [
|
|
1987
|
+
showWordmark ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native7.Text, { style: styles5.wordmark, children: "ada" }) : null,
|
|
1988
|
+
status ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native7.Text, { style: styles5.status, children: status }) : null
|
|
1211
1989
|
] }),
|
|
1212
|
-
hero ? /* @__PURE__ */ (0,
|
|
1213
|
-
/* @__PURE__ */ (0,
|
|
1214
|
-
/* @__PURE__ */ (0,
|
|
1215
|
-
/* @__PURE__ */ (0,
|
|
1990
|
+
hero ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native7.View, { style: styles5.heroWrap, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native7.Animated.View, { style: { transform: [{ scale: heroScale }] }, children: hero }) }) : null,
|
|
1991
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native7.View, { style: styles5.body, children }),
|
|
1992
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native7.View, { style: styles5.bottomBar, children: [
|
|
1993
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1216
1994
|
import_react_native7.Pressable,
|
|
1217
1995
|
{
|
|
1218
1996
|
accessibilityRole: "button",
|
|
@@ -1223,151 +2001,166 @@ function SurfaceFullscreen({
|
|
|
1223
2001
|
children: renderCloseIcon()
|
|
1224
2002
|
}
|
|
1225
2003
|
),
|
|
1226
|
-
closeLabel ? /* @__PURE__ */ (0,
|
|
2004
|
+
closeLabel ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native7.Text, { style: styles5.closeLabel, children: closeLabel }) : null
|
|
1227
2005
|
] })
|
|
1228
2006
|
] });
|
|
1229
2007
|
}
|
|
1230
|
-
var
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
2008
|
+
var import_react7, import_react_native7, import_react_native_svg3, import_core7, import_jsx_runtime5, fontFam3, styles5;
|
|
2009
|
+
var init_SurfaceFullscreen = __esm({
|
|
2010
|
+
"src/surfaces/SurfaceFullscreen.tsx"() {
|
|
2011
|
+
"use strict";
|
|
2012
|
+
import_react7 = require("react");
|
|
2013
|
+
import_react_native7 = require("react-native");
|
|
2014
|
+
import_react_native_svg3 = __toESM(require("react-native-svg"));
|
|
2015
|
+
import_core7 = require("@ateam-ai-mobile/core");
|
|
2016
|
+
import_jsx_runtime5 = require("react/jsx-runtime");
|
|
2017
|
+
fontFam3 = import_react_native7.Platform.OS === "ios" ? "SF Pro Rounded" : void 0;
|
|
2018
|
+
styles5 = import_react_native7.StyleSheet.create({
|
|
2019
|
+
overlay: {
|
|
2020
|
+
position: "absolute",
|
|
2021
|
+
top: 0,
|
|
2022
|
+
left: 0,
|
|
2023
|
+
right: 0,
|
|
2024
|
+
bottom: 0,
|
|
2025
|
+
backgroundColor: import_core7.ada.paper,
|
|
2026
|
+
zIndex: 1e3,
|
|
2027
|
+
elevation: 1e3
|
|
2028
|
+
},
|
|
2029
|
+
topBar: {
|
|
2030
|
+
paddingTop: import_react_native7.Platform.OS === "ios" ? 64 : 36,
|
|
2031
|
+
paddingHorizontal: 24,
|
|
2032
|
+
alignItems: "center",
|
|
2033
|
+
gap: 6
|
|
2034
|
+
},
|
|
2035
|
+
wordmark: {
|
|
2036
|
+
fontSize: 14,
|
|
2037
|
+
fontFamily: fontFam3,
|
|
2038
|
+
fontWeight: "500",
|
|
2039
|
+
color: import_core7.ada.inkMuted,
|
|
2040
|
+
letterSpacing: 4
|
|
2041
|
+
},
|
|
2042
|
+
status: {
|
|
2043
|
+
fontSize: 17,
|
|
2044
|
+
fontFamily: fontFam3,
|
|
2045
|
+
fontWeight: "500",
|
|
2046
|
+
color: import_core7.ada.ink,
|
|
2047
|
+
letterSpacing: -0.2
|
|
2048
|
+
},
|
|
2049
|
+
heroWrap: {
|
|
2050
|
+
paddingTop: 24,
|
|
2051
|
+
paddingBottom: 12,
|
|
2052
|
+
alignItems: "center",
|
|
2053
|
+
justifyContent: "center"
|
|
2054
|
+
},
|
|
2055
|
+
body: {
|
|
2056
|
+
flex: 1
|
|
2057
|
+
},
|
|
2058
|
+
bottomBar: {
|
|
2059
|
+
paddingBottom: import_react_native7.Platform.OS === "ios" ? 48 : 32,
|
|
2060
|
+
paddingTop: 12,
|
|
2061
|
+
alignItems: "center",
|
|
2062
|
+
gap: 8
|
|
2063
|
+
},
|
|
2064
|
+
closeBtn: {
|
|
2065
|
+
width: 64,
|
|
2066
|
+
height: 64,
|
|
2067
|
+
borderRadius: 32,
|
|
2068
|
+
backgroundColor: import_core7.ada.ink,
|
|
2069
|
+
justifyContent: "center",
|
|
2070
|
+
alignItems: "center",
|
|
2071
|
+
shadowColor: import_core7.ada.ink,
|
|
2072
|
+
shadowOffset: { width: 0, height: 8 },
|
|
2073
|
+
shadowOpacity: 0.18,
|
|
2074
|
+
shadowRadius: 14,
|
|
2075
|
+
elevation: 8
|
|
2076
|
+
},
|
|
2077
|
+
closeLabel: {
|
|
2078
|
+
fontSize: 13,
|
|
2079
|
+
fontFamily: fontFam3,
|
|
2080
|
+
fontWeight: "500",
|
|
2081
|
+
color: import_core7.ada.inkMuted,
|
|
2082
|
+
letterSpacing: 0.3
|
|
2083
|
+
}
|
|
2084
|
+
});
|
|
1295
2085
|
}
|
|
1296
2086
|
});
|
|
1297
2087
|
|
|
1298
2088
|
// src/registry/store.ts
|
|
1299
|
-
var import_zustand = require("zustand");
|
|
1300
|
-
var useSurfaceStore = (0, import_zustand.create)((set, get) => ({
|
|
1301
|
-
manifests: {},
|
|
1302
|
-
activeDrawer: null,
|
|
1303
|
-
activeFullscreen: null,
|
|
1304
|
-
activeProps: {},
|
|
1305
|
-
mountKey: 0,
|
|
1306
|
-
register: (manifest) => set((s) => ({ manifests: { ...s.manifests, [manifest.id]: manifest } })),
|
|
1307
|
-
unregister: (id) => set((s) => {
|
|
1308
|
-
const next = { ...s.manifests };
|
|
1309
|
-
delete next[id];
|
|
1310
|
-
const nextProps = { ...s.activeProps };
|
|
1311
|
-
delete nextProps[id];
|
|
1312
|
-
return { manifests: next, activeProps: nextProps };
|
|
1313
|
-
}),
|
|
1314
|
-
show: (id, props) => {
|
|
1315
|
-
const m = get().manifests[id];
|
|
1316
|
-
if (!m) {
|
|
1317
|
-
console.warn("[SurfaceStore] show: unknown surface id", id);
|
|
1318
|
-
return;
|
|
1319
|
-
}
|
|
1320
|
-
set((s) => {
|
|
1321
|
-
const nextProps = props !== void 0 ? { ...s.activeProps, [id]: props } : s.activeProps;
|
|
1322
|
-
const mountKey = s.mountKey + 1;
|
|
1323
|
-
if (m.type === "drawer") return { activeDrawer: id, activeProps: nextProps, mountKey };
|
|
1324
|
-
if (m.type === "fullscreen") return { activeFullscreen: id, activeProps: nextProps, mountKey };
|
|
1325
|
-
return { mountKey };
|
|
1326
|
-
});
|
|
1327
|
-
},
|
|
1328
|
-
hide: (id) => {
|
|
1329
|
-
const s = get();
|
|
1330
|
-
if (!id) {
|
|
1331
|
-
const cleanup2 = { ...s.activeProps };
|
|
1332
|
-
if (s.activeDrawer) delete cleanup2[s.activeDrawer];
|
|
1333
|
-
if (s.activeFullscreen) delete cleanup2[s.activeFullscreen];
|
|
1334
|
-
set({ activeDrawer: null, activeFullscreen: null, activeProps: cleanup2 });
|
|
1335
|
-
return;
|
|
1336
|
-
}
|
|
1337
|
-
const cleanup = { ...s.activeProps };
|
|
1338
|
-
delete cleanup[id];
|
|
1339
|
-
const patch = { activeProps: cleanup };
|
|
1340
|
-
if (s.activeDrawer === id) patch.activeDrawer = null;
|
|
1341
|
-
if (s.activeFullscreen === id) patch.activeFullscreen = null;
|
|
1342
|
-
set(patch);
|
|
1343
|
-
},
|
|
1344
|
-
list: (filter) => {
|
|
1345
|
-
const all = Object.values(get().manifests);
|
|
1346
|
-
return all.filter((m) => {
|
|
1347
|
-
if (filter?.type && m.type !== filter.type) return false;
|
|
1348
|
-
if (filter?.visibility && !filter.visibility.includes(m.visibility)) return false;
|
|
1349
|
-
if (filter?.placement) {
|
|
1350
|
-
if (filter.placement === "unset") {
|
|
1351
|
-
if (m.placement) return false;
|
|
1352
|
-
} else if (m.placement !== filter.placement) {
|
|
1353
|
-
return false;
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
return true;
|
|
1357
|
-
});
|
|
1358
|
-
}
|
|
1359
|
-
}));
|
|
1360
2089
|
function isMountSurface(m) {
|
|
1361
2090
|
return m.type === "drawer" || m.type === "fullscreen";
|
|
1362
2091
|
}
|
|
1363
2092
|
function isInlineSurface(m) {
|
|
1364
2093
|
return m.type === "card" || m.type === "header" || m.type === "ambient" || m.type === "nudge";
|
|
1365
2094
|
}
|
|
2095
|
+
var import_zustand, useSurfaceStore;
|
|
2096
|
+
var init_store = __esm({
|
|
2097
|
+
"src/registry/store.ts"() {
|
|
2098
|
+
"use strict";
|
|
2099
|
+
import_zustand = require("zustand");
|
|
2100
|
+
useSurfaceStore = (0, import_zustand.create)((set, get) => ({
|
|
2101
|
+
manifests: {},
|
|
2102
|
+
activeDrawer: null,
|
|
2103
|
+
activeFullscreen: null,
|
|
2104
|
+
activeProps: {},
|
|
2105
|
+
mountKey: 0,
|
|
2106
|
+
register: (manifest) => set((s) => ({ manifests: { ...s.manifests, [manifest.id]: manifest } })),
|
|
2107
|
+
unregister: (id) => set((s) => {
|
|
2108
|
+
const next = { ...s.manifests };
|
|
2109
|
+
delete next[id];
|
|
2110
|
+
const nextProps = { ...s.activeProps };
|
|
2111
|
+
delete nextProps[id];
|
|
2112
|
+
return { manifests: next, activeProps: nextProps };
|
|
2113
|
+
}),
|
|
2114
|
+
show: (id, props) => {
|
|
2115
|
+
const m = get().manifests[id];
|
|
2116
|
+
if (!m) {
|
|
2117
|
+
console.warn("[SurfaceStore] show: unknown surface id", id);
|
|
2118
|
+
return;
|
|
2119
|
+
}
|
|
2120
|
+
set((s) => {
|
|
2121
|
+
const nextProps = props !== void 0 ? { ...s.activeProps, [id]: props } : s.activeProps;
|
|
2122
|
+
const mountKey = s.mountKey + 1;
|
|
2123
|
+
if (m.type === "drawer") return { activeDrawer: id, activeProps: nextProps, mountKey };
|
|
2124
|
+
if (m.type === "fullscreen") return { activeFullscreen: id, activeProps: nextProps, mountKey };
|
|
2125
|
+
return { mountKey };
|
|
2126
|
+
});
|
|
2127
|
+
},
|
|
2128
|
+
hide: (id) => {
|
|
2129
|
+
const s = get();
|
|
2130
|
+
if (!id) {
|
|
2131
|
+
const cleanup2 = { ...s.activeProps };
|
|
2132
|
+
if (s.activeDrawer) delete cleanup2[s.activeDrawer];
|
|
2133
|
+
if (s.activeFullscreen) delete cleanup2[s.activeFullscreen];
|
|
2134
|
+
set({ activeDrawer: null, activeFullscreen: null, activeProps: cleanup2 });
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
const cleanup = { ...s.activeProps };
|
|
2138
|
+
delete cleanup[id];
|
|
2139
|
+
const patch = { activeProps: cleanup };
|
|
2140
|
+
if (s.activeDrawer === id) patch.activeDrawer = null;
|
|
2141
|
+
if (s.activeFullscreen === id) patch.activeFullscreen = null;
|
|
2142
|
+
set(patch);
|
|
2143
|
+
},
|
|
2144
|
+
list: (filter) => {
|
|
2145
|
+
const all = Object.values(get().manifests);
|
|
2146
|
+
return all.filter((m) => {
|
|
2147
|
+
if (filter?.type && m.type !== filter.type) return false;
|
|
2148
|
+
if (filter?.visibility && !filter.visibility.includes(m.visibility)) return false;
|
|
2149
|
+
if (filter?.placement) {
|
|
2150
|
+
if (filter.placement === "unset") {
|
|
2151
|
+
if (m.placement) return false;
|
|
2152
|
+
} else if (m.placement !== filter.placement) {
|
|
2153
|
+
return false;
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
return true;
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
2159
|
+
}));
|
|
2160
|
+
}
|
|
2161
|
+
});
|
|
1366
2162
|
|
|
1367
2163
|
// src/registry/SurfaceDispatcher.tsx
|
|
1368
|
-
var import_react7 = require("react");
|
|
1369
|
-
var import_core5 = require("@ateam-ai-mobile/core");
|
|
1370
|
-
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1371
2164
|
function SurfaceDispatcher() {
|
|
1372
2165
|
const manifests = useSurfaceStore((s) => s.manifests);
|
|
1373
2166
|
const activeDrawer = useSurfaceStore((s) => s.activeDrawer);
|
|
@@ -1375,8 +2168,8 @@ function SurfaceDispatcher() {
|
|
|
1375
2168
|
const activeProps = useSurfaceStore((s) => s.activeProps);
|
|
1376
2169
|
const mountKey = useSurfaceStore((s) => s.mountKey);
|
|
1377
2170
|
const hide = useSurfaceStore((s) => s.hide);
|
|
1378
|
-
(0,
|
|
1379
|
-
const unsub =
|
|
2171
|
+
(0, import_react8.useEffect)(() => {
|
|
2172
|
+
const unsub = import_core8.pluginCommandEmitter.on("plugin-close", ({ pluginId } = {}) => {
|
|
1380
2173
|
if (!pluginId) return;
|
|
1381
2174
|
const s = useSurfaceStore.getState();
|
|
1382
2175
|
if (s.activeDrawer === pluginId || s.activeFullscreen === pluginId) {
|
|
@@ -1389,8 +2182,8 @@ function SurfaceDispatcher() {
|
|
|
1389
2182
|
const fullscreen = activeFullscreen ? manifests[activeFullscreen] : null;
|
|
1390
2183
|
const drawerProps = activeDrawer ? activeProps[activeDrawer] : void 0;
|
|
1391
2184
|
const fullscreenProps = activeFullscreen ? activeProps[activeFullscreen] : void 0;
|
|
1392
|
-
return /* @__PURE__ */ (0,
|
|
1393
|
-
drawer && drawer.type === "drawer" && drawer.Component ? /* @__PURE__ */ (0,
|
|
2185
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
2186
|
+
drawer && drawer.type === "drawer" && drawer.Component ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1394
2187
|
drawer.Component,
|
|
1395
2188
|
{
|
|
1396
2189
|
dismiss: () => hide(drawer.id),
|
|
@@ -1398,7 +2191,7 @@ function SurfaceDispatcher() {
|
|
|
1398
2191
|
},
|
|
1399
2192
|
`drawer-${drawer.id}-${mountKey}`
|
|
1400
2193
|
) : null,
|
|
1401
|
-
fullscreen && fullscreen.type === "fullscreen" && fullscreen.Component ? /* @__PURE__ */ (0,
|
|
2194
|
+
fullscreen && fullscreen.type === "fullscreen" && fullscreen.Component ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1402
2195
|
fullscreen.Component,
|
|
1403
2196
|
{
|
|
1404
2197
|
dismiss: () => hide(fullscreen.id),
|
|
@@ -1408,75 +2201,18 @@ function SurfaceDispatcher() {
|
|
|
1408
2201
|
) : null
|
|
1409
2202
|
] });
|
|
1410
2203
|
}
|
|
2204
|
+
var import_react8, import_core8, import_jsx_runtime6;
|
|
2205
|
+
var init_SurfaceDispatcher = __esm({
|
|
2206
|
+
"src/registry/SurfaceDispatcher.tsx"() {
|
|
2207
|
+
"use strict";
|
|
2208
|
+
import_react8 = require("react");
|
|
2209
|
+
init_store();
|
|
2210
|
+
import_core8 = require("@ateam-ai-mobile/core");
|
|
2211
|
+
import_jsx_runtime6 = require("react/jsx-runtime");
|
|
2212
|
+
}
|
|
2213
|
+
});
|
|
1411
2214
|
|
|
1412
2215
|
// src/utils/sha256.ts
|
|
1413
|
-
var K = new Uint32Array([
|
|
1414
|
-
1116352408,
|
|
1415
|
-
1899447441,
|
|
1416
|
-
3049323471,
|
|
1417
|
-
3921009573,
|
|
1418
|
-
961987163,
|
|
1419
|
-
1508970993,
|
|
1420
|
-
2453635748,
|
|
1421
|
-
2870763221,
|
|
1422
|
-
3624381080,
|
|
1423
|
-
310598401,
|
|
1424
|
-
607225278,
|
|
1425
|
-
1426881987,
|
|
1426
|
-
1925078388,
|
|
1427
|
-
2162078206,
|
|
1428
|
-
2614888103,
|
|
1429
|
-
3248222580,
|
|
1430
|
-
3835390401,
|
|
1431
|
-
4022224774,
|
|
1432
|
-
264347078,
|
|
1433
|
-
604807628,
|
|
1434
|
-
770255983,
|
|
1435
|
-
1249150122,
|
|
1436
|
-
1555081692,
|
|
1437
|
-
1996064986,
|
|
1438
|
-
2554220882,
|
|
1439
|
-
2821834349,
|
|
1440
|
-
2952996808,
|
|
1441
|
-
3210313671,
|
|
1442
|
-
3336571891,
|
|
1443
|
-
3584528711,
|
|
1444
|
-
113926993,
|
|
1445
|
-
338241895,
|
|
1446
|
-
666307205,
|
|
1447
|
-
773529912,
|
|
1448
|
-
1294757372,
|
|
1449
|
-
1396182291,
|
|
1450
|
-
1695183700,
|
|
1451
|
-
1986661051,
|
|
1452
|
-
2177026350,
|
|
1453
|
-
2456956037,
|
|
1454
|
-
2730485921,
|
|
1455
|
-
2820302411,
|
|
1456
|
-
3259730800,
|
|
1457
|
-
3345764771,
|
|
1458
|
-
3516065817,
|
|
1459
|
-
3600352804,
|
|
1460
|
-
4094571909,
|
|
1461
|
-
275423344,
|
|
1462
|
-
430227734,
|
|
1463
|
-
506948616,
|
|
1464
|
-
659060556,
|
|
1465
|
-
883997877,
|
|
1466
|
-
958139571,
|
|
1467
|
-
1322822218,
|
|
1468
|
-
1537002063,
|
|
1469
|
-
1747873779,
|
|
1470
|
-
1955562222,
|
|
1471
|
-
2024104815,
|
|
1472
|
-
2227730452,
|
|
1473
|
-
2361852424,
|
|
1474
|
-
2428436474,
|
|
1475
|
-
2756734187,
|
|
1476
|
-
3204031479,
|
|
1477
|
-
3329325298
|
|
1478
|
-
]);
|
|
1479
|
-
var rotr = (n, x) => x >>> n | x << 32 - n;
|
|
1480
2216
|
function sha256Hex(message) {
|
|
1481
2217
|
const utf8 = unescape(encodeURIComponent(message));
|
|
1482
2218
|
const bytes = new Uint8Array(utf8.length);
|
|
@@ -1544,547 +2280,1143 @@ function sha256Hex(message) {
|
|
|
1544
2280
|
}
|
|
1545
2281
|
return out;
|
|
1546
2282
|
}
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
2283
|
+
var K, rotr;
|
|
2284
|
+
var init_sha256 = __esm({
|
|
2285
|
+
"src/utils/sha256.ts"() {
|
|
2286
|
+
"use strict";
|
|
2287
|
+
K = new Uint32Array([
|
|
2288
|
+
1116352408,
|
|
2289
|
+
1899447441,
|
|
2290
|
+
3049323471,
|
|
2291
|
+
3921009573,
|
|
2292
|
+
961987163,
|
|
2293
|
+
1508970993,
|
|
2294
|
+
2453635748,
|
|
2295
|
+
2870763221,
|
|
2296
|
+
3624381080,
|
|
2297
|
+
310598401,
|
|
2298
|
+
607225278,
|
|
2299
|
+
1426881987,
|
|
2300
|
+
1925078388,
|
|
2301
|
+
2162078206,
|
|
2302
|
+
2614888103,
|
|
2303
|
+
3248222580,
|
|
2304
|
+
3835390401,
|
|
2305
|
+
4022224774,
|
|
2306
|
+
264347078,
|
|
2307
|
+
604807628,
|
|
2308
|
+
770255983,
|
|
2309
|
+
1249150122,
|
|
2310
|
+
1555081692,
|
|
2311
|
+
1996064986,
|
|
2312
|
+
2554220882,
|
|
2313
|
+
2821834349,
|
|
2314
|
+
2952996808,
|
|
2315
|
+
3210313671,
|
|
2316
|
+
3336571891,
|
|
2317
|
+
3584528711,
|
|
2318
|
+
113926993,
|
|
2319
|
+
338241895,
|
|
2320
|
+
666307205,
|
|
2321
|
+
773529912,
|
|
2322
|
+
1294757372,
|
|
2323
|
+
1396182291,
|
|
2324
|
+
1695183700,
|
|
2325
|
+
1986661051,
|
|
2326
|
+
2177026350,
|
|
2327
|
+
2456956037,
|
|
2328
|
+
2730485921,
|
|
2329
|
+
2820302411,
|
|
2330
|
+
3259730800,
|
|
2331
|
+
3345764771,
|
|
2332
|
+
3516065817,
|
|
2333
|
+
3600352804,
|
|
2334
|
+
4094571909,
|
|
2335
|
+
275423344,
|
|
2336
|
+
430227734,
|
|
2337
|
+
506948616,
|
|
2338
|
+
659060556,
|
|
2339
|
+
883997877,
|
|
2340
|
+
958139571,
|
|
2341
|
+
1322822218,
|
|
2342
|
+
1537002063,
|
|
2343
|
+
1747873779,
|
|
2344
|
+
1955562222,
|
|
2345
|
+
2024104815,
|
|
2346
|
+
2227730452,
|
|
2347
|
+
2361852424,
|
|
2348
|
+
2428436474,
|
|
2349
|
+
2756734187,
|
|
2350
|
+
3204031479,
|
|
2351
|
+
3329325298
|
|
2352
|
+
]);
|
|
2353
|
+
rotr = (n, x) => x >>> n | x << 32 - n;
|
|
1556
2354
|
}
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
const localPath = `memory://${key}`;
|
|
1565
|
-
this.files.set(localPath, content);
|
|
1566
|
-
return localPath;
|
|
2355
|
+
});
|
|
2356
|
+
|
|
2357
|
+
// src/bundles/integrity.ts
|
|
2358
|
+
function assertBundleIntegrity(content, verifiedHash, label) {
|
|
2359
|
+
const digest = sha256Hex(content);
|
|
2360
|
+
if (!verifiedHash) {
|
|
2361
|
+
throw new Error(`[bundle-integrity] refusing to evaluate ${label}: no verified SHA-256 (fail closed).`);
|
|
1567
2362
|
}
|
|
1568
|
-
|
|
1569
|
-
|
|
2363
|
+
if (digest !== verifiedHash.toLowerCase()) {
|
|
2364
|
+
throw new Error(
|
|
2365
|
+
`[bundle-integrity] integrity FAIL at eval for ${label}: content sha256 ${digest.slice(0, 12)}\u2026 \u2260 verified ${verifiedHash.slice(0, 12)}\u2026. Refusing to evaluate.`
|
|
2366
|
+
);
|
|
1570
2367
|
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
2368
|
+
}
|
|
2369
|
+
var init_integrity = __esm({
|
|
2370
|
+
"src/bundles/integrity.ts"() {
|
|
2371
|
+
"use strict";
|
|
2372
|
+
init_sha256();
|
|
1574
2373
|
}
|
|
1575
|
-
};
|
|
1576
|
-
|
|
2374
|
+
});
|
|
2375
|
+
|
|
2376
|
+
// src/bundles/storage.ts
|
|
2377
|
+
function selectOwnedCacheEntries(entries, tenant) {
|
|
2378
|
+
if (!tenant) return [];
|
|
2379
|
+
return (entries || []).filter((e) => !!e && e.tenant === tenant);
|
|
2380
|
+
}
|
|
1577
2381
|
function setBundleStorage(storage) {
|
|
1578
2382
|
_storage = storage;
|
|
1579
2383
|
}
|
|
1580
2384
|
function getBundleStorage() {
|
|
1581
2385
|
return _storage;
|
|
1582
2386
|
}
|
|
2387
|
+
var MemoryBundleStorage, _storage;
|
|
2388
|
+
var init_storage = __esm({
|
|
2389
|
+
"src/bundles/storage.ts"() {
|
|
2390
|
+
"use strict";
|
|
2391
|
+
MemoryBundleStorage = class {
|
|
2392
|
+
constructor() {
|
|
2393
|
+
this.index = {};
|
|
2394
|
+
this.files = /* @__PURE__ */ new Map();
|
|
2395
|
+
}
|
|
2396
|
+
async loadIndex() {
|
|
2397
|
+
return this.index;
|
|
2398
|
+
}
|
|
2399
|
+
async saveIndex(index) {
|
|
2400
|
+
this.index = index;
|
|
2401
|
+
}
|
|
2402
|
+
async readBundle(localPath) {
|
|
2403
|
+
return this.files.get(localPath) ?? null;
|
|
2404
|
+
}
|
|
2405
|
+
async writeBundle(key, content) {
|
|
2406
|
+
const localPath = `memory://${key}`;
|
|
2407
|
+
this.files.set(localPath, content);
|
|
2408
|
+
return localPath;
|
|
2409
|
+
}
|
|
2410
|
+
async deleteBundle(localPath) {
|
|
2411
|
+
this.files.delete(localPath);
|
|
2412
|
+
}
|
|
2413
|
+
async clearAll() {
|
|
2414
|
+
this.index = {};
|
|
2415
|
+
this.files.clear();
|
|
2416
|
+
}
|
|
2417
|
+
};
|
|
2418
|
+
_storage = new MemoryBundleStorage();
|
|
2419
|
+
}
|
|
2420
|
+
});
|
|
1583
2421
|
|
|
1584
2422
|
// src/bundles/bundleManager.ts
|
|
1585
|
-
var ReactShim
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
2423
|
+
var import_react9, import_react10, ReactNative, import_core9, ReactShim, PluginSDKShim, requireShim, CACHE_TTL, hasComponent, hasTools, BundleManager, bundleManager;
|
|
2424
|
+
var init_bundleManager = __esm({
|
|
2425
|
+
"src/bundles/bundleManager.ts"() {
|
|
2426
|
+
"use strict";
|
|
2427
|
+
import_react9 = __toESM(require("react"));
|
|
2428
|
+
import_react10 = require("react");
|
|
2429
|
+
ReactNative = __toESM(require("react-native"));
|
|
2430
|
+
import_core9 = require("@ateam-ai-mobile/core");
|
|
2431
|
+
init_sdk();
|
|
2432
|
+
init_SurfaceDrawer();
|
|
2433
|
+
init_SurfaceCard();
|
|
2434
|
+
init_SurfaceFullscreen();
|
|
2435
|
+
init_store();
|
|
2436
|
+
init_SurfaceDispatcher();
|
|
2437
|
+
init_host();
|
|
2438
|
+
init_sha256();
|
|
2439
|
+
init_integrity();
|
|
2440
|
+
init_storage();
|
|
2441
|
+
init_registry();
|
|
2442
|
+
ReactShim = {
|
|
2443
|
+
__esModule: true,
|
|
2444
|
+
default: import_react9.default,
|
|
2445
|
+
...import_react9.default,
|
|
2446
|
+
// Explicitly include hooks (React 19 may not have them as own enumerable props)
|
|
2447
|
+
useState: import_react10.useState,
|
|
2448
|
+
useEffect: import_react10.useEffect,
|
|
2449
|
+
useCallback: import_react10.useCallback,
|
|
2450
|
+
useMemo: import_react10.useMemo,
|
|
2451
|
+
useRef: import_react10.useRef,
|
|
2452
|
+
useContext: import_react10.useContext,
|
|
2453
|
+
useReducer: import_react10.useReducer,
|
|
2454
|
+
useLayoutEffect: import_react10.useLayoutEffect,
|
|
2455
|
+
useImperativeHandle: import_react10.useImperativeHandle,
|
|
2456
|
+
useDebugValue: import_react10.useDebugValue,
|
|
2457
|
+
createElement: import_react9.default.createElement,
|
|
2458
|
+
Fragment: import_react9.default.Fragment,
|
|
2459
|
+
Component: import_react9.default.Component,
|
|
2460
|
+
PureComponent: import_react9.default.PureComponent,
|
|
2461
|
+
createContext: import_react9.default.createContext,
|
|
2462
|
+
forwardRef: import_react9.default.forwardRef,
|
|
2463
|
+
memo: import_react9.default.memo,
|
|
2464
|
+
Children: import_react9.default.Children,
|
|
2465
|
+
cloneElement: import_react9.default.cloneElement,
|
|
2466
|
+
isValidElement: import_react9.default.isValidElement,
|
|
2467
|
+
createRef: import_react9.default.createRef,
|
|
2468
|
+
lazy: import_react9.default.lazy,
|
|
2469
|
+
Suspense: import_react9.default.Suspense
|
|
2470
|
+
};
|
|
2471
|
+
PluginSDKShim = Object.freeze({
|
|
2472
|
+
...sdk_exports,
|
|
2473
|
+
SurfaceDrawer,
|
|
2474
|
+
SurfaceCard,
|
|
2475
|
+
SurfaceFullscreen,
|
|
2476
|
+
useSurfaceStore,
|
|
2477
|
+
SurfaceDispatcher,
|
|
2478
|
+
PluginSDK: Object.freeze({
|
|
2479
|
+
...PluginSDK,
|
|
2480
|
+
register: PluginSDK.register
|
|
2481
|
+
}),
|
|
2482
|
+
register
|
|
2483
|
+
});
|
|
2484
|
+
requireShim = (name) => {
|
|
2485
|
+
if (name === "react") return ReactShim;
|
|
2486
|
+
if (name === "react-native") return ReactNative;
|
|
2487
|
+
if (name === "@adas/plugin-sdk") return PluginSDKShim;
|
|
2488
|
+
const hostModule = resolveBundleModule(name);
|
|
2489
|
+
if (hostModule !== void 0) return hostModule;
|
|
2490
|
+
console.warn(`[BundleManager] Unknown module: ${name} \u2014 register it via registerBundleModules()`);
|
|
2491
|
+
return {};
|
|
2492
|
+
};
|
|
2493
|
+
CACHE_TTL = 7 * 24 * 60 * 60 * 1e3;
|
|
2494
|
+
hasComponent = (val) => !!val && typeof val === "object" && typeof val.Component === "function";
|
|
2495
|
+
hasTools = (val) => !!val && typeof val === "object" && Array.isArray(val.tools) && val.tools.length > 0;
|
|
2496
|
+
BundleManager = class {
|
|
2497
|
+
constructor() {
|
|
2498
|
+
/** In-memory module cache: bundleUrl → evaluated module.exports. Holds ONE
|
|
2499
|
+
* tenant's modules at a time — the tenant boundary is `cacheTenant` /
|
|
2500
|
+
* `requireTenant()`, NOT the key. */
|
|
2501
|
+
this.modules = /* @__PURE__ */ new Map();
|
|
2502
|
+
/** What is actually RUNNING, keyed by bundleUrl (for the current tenant). See
|
|
2503
|
+
* getLoadedBundles(). */
|
|
2504
|
+
this.provenance = /* @__PURE__ */ new Map();
|
|
2505
|
+
/** In-flight download deduplication: bundleUrl → Promise */
|
|
2506
|
+
this.inflight = /* @__PURE__ */ new Map();
|
|
2507
|
+
/**
|
|
2508
|
+
* The tenant the in-memory caches (modules / provenance / inflight) currently
|
|
2509
|
+
* belong to. This is the tenant BOUNDARY for evaluated code.
|
|
2510
|
+
*
|
|
2511
|
+
* A bundleUrl resolves PER-TENANT on the server — one url, different bytes for
|
|
2512
|
+
* different tenants — so a memory hit keyed on url alone would hand tenant B
|
|
2513
|
+
* the module tenant A already evaluated and ran. PR #9 scoped the DISK cache
|
|
2514
|
+
* (per-tenant index + hash gate); the in-memory map was left url-keyed. This
|
|
2515
|
+
* closes that: the caches only ever hold ONE tenant's modules, and a switch
|
|
2516
|
+
* drops them before anything is served.
|
|
2517
|
+
*/
|
|
2518
|
+
this.cacheTenant = null;
|
|
2519
|
+
/**
|
|
2520
|
+
* pluginIds this manager registered device tools for. A tenant switch drops
|
|
2521
|
+
* the in-memory modules AND unregisters these, so the incoming tenant's bundle
|
|
2522
|
+
* re-registers into a clean registry instead of being refused as a duplicate
|
|
2523
|
+
* of the previous tenant's tools.
|
|
2524
|
+
*/
|
|
2525
|
+
this.registeredToolPluginIds = /* @__PURE__ */ new Set();
|
|
2526
|
+
/** bundleUrl → the trusted declaration last recorded on its disk-cache entry
|
|
2527
|
+
* this session, so a memory hit with unchanged grants skips the index I/O. */
|
|
2528
|
+
this.recordedDeclarations = /* @__PURE__ */ new Map();
|
|
2529
|
+
/**
|
|
2530
|
+
* Enforce the in-memory tenant boundary. Call at the top of every public load
|
|
2531
|
+
* path, BEFORE any cache read.
|
|
2532
|
+
*
|
|
2533
|
+
* FAIL CLOSED: without a tenant identity we cannot prove a cached module is
|
|
2534
|
+
* this session's, so we drop everything and refuse rather than risk serving
|
|
2535
|
+
* one tenant's evaluated code to another. On a tenant change we drop the
|
|
2536
|
+
* previous tenant's modules (and their tool registrations) before proceeding.
|
|
2537
|
+
*/
|
|
2538
|
+
/**
|
|
2539
|
+
* Monotonic load generation. Bumped on every in-memory wipe (a tenant change,
|
|
2540
|
+
* fail-closed, or clearAll). A load captures it at ENTRY; a completion whose
|
|
2541
|
+
* generation no longer matches is stale and is discarded before it can commit.
|
|
2542
|
+
* This closes the async race the sequential guard cannot: tenant A's in-flight
|
|
2543
|
+
* loadModule can resolve AFTER a switch to B — clearing `inflight` does not
|
|
2544
|
+
* cancel that Promise — and would otherwise write A's module / provenance /
|
|
2545
|
+
* device tools into the singleton now marked cacheTenant=B.
|
|
2546
|
+
*/
|
|
2547
|
+
this.epoch = 0;
|
|
2548
|
+
}
|
|
2549
|
+
requireTenant() {
|
|
2550
|
+
const tenant = (0, import_core9.getTenant)();
|
|
2551
|
+
if (!tenant) {
|
|
2552
|
+
this.dropInMemory();
|
|
2553
|
+
this.cacheTenant = null;
|
|
2554
|
+
throw new Error(
|
|
2555
|
+
"[BundleManager] refusing to load a tenant-scoped bundle without a tenant identity (fail closed)"
|
|
2556
|
+
);
|
|
2557
|
+
}
|
|
2558
|
+
if (tenant !== this.cacheTenant) {
|
|
2559
|
+
this.dropInMemory();
|
|
2560
|
+
this.cacheTenant = tenant;
|
|
1695
2561
|
}
|
|
2562
|
+
return tenant;
|
|
1696
2563
|
}
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
/**
|
|
1713
|
-
* Clear cache for a specific plugin/bundle.
|
|
1714
|
-
*/
|
|
1715
|
-
async clearPlugin(pluginId) {
|
|
1716
|
-
const storage = getBundleStorage();
|
|
1717
|
-
const index = await storage.loadIndex();
|
|
1718
|
-
const entry = index[pluginId];
|
|
1719
|
-
if (entry) {
|
|
1720
|
-
this.modules.delete(entry.bundleUrl);
|
|
1721
|
-
delete index[pluginId];
|
|
1722
|
-
await storage.saveIndex(index);
|
|
1723
|
-
try {
|
|
1724
|
-
await storage.deleteBundle(entry.localPath);
|
|
1725
|
-
} catch {
|
|
2564
|
+
/** Drop every in-memory cache and unregister the device tools this manager
|
|
2565
|
+
* registered — used on a tenant change and by clearAll(). */
|
|
2566
|
+
dropInMemory() {
|
|
2567
|
+
for (const pluginId of this.registeredToolPluginIds) {
|
|
2568
|
+
try {
|
|
2569
|
+
unregisterDeviceTools(pluginId);
|
|
2570
|
+
} catch {
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
this.registeredToolPluginIds.clear();
|
|
2574
|
+
this.recordedDeclarations.clear();
|
|
2575
|
+
this.modules.clear();
|
|
2576
|
+
this.provenance.clear();
|
|
2577
|
+
this.inflight.clear();
|
|
2578
|
+
this.epoch++;
|
|
1726
2579
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
const
|
|
1740
|
-
if (
|
|
1741
|
-
|
|
1742
|
-
if (content) break;
|
|
2580
|
+
/** True once the boundary has moved since a load captured `epoch` — i.e. a
|
|
2581
|
+
* tenant switch (or wipe) happened while this load was in flight. Its results
|
|
2582
|
+
* belong to a tenant that is no longer current and must be discarded. */
|
|
2583
|
+
isStale(epoch, tenant) {
|
|
2584
|
+
return this.epoch !== epoch || (0, import_core9.getTenant)() !== tenant;
|
|
2585
|
+
}
|
|
2586
|
+
/** Record a memory serve: the running bytes are the already-evaluated module,
|
|
2587
|
+
* and `expected` is refreshed from the manifest hash the caller JUST passed —
|
|
2588
|
+
* so after a redeploy the record reads hash !== expected (STALE) instead of
|
|
2589
|
+
* the old in-sync pair. The ONE place both memory paths (load()'s early return
|
|
2590
|
+
* and loadModule's) record reuse. No-op if nothing was recorded yet. */
|
|
2591
|
+
markMemoryHit(bundleUrl, expectedHash) {
|
|
2592
|
+
const prev = this.provenance.get(bundleUrl);
|
|
2593
|
+
if (prev) {
|
|
2594
|
+
this.provenance.set(bundleUrl, { ...prev, source: "memory", expected: expectedHash?.toLowerCase() ?? prev.expected });
|
|
1743
2595
|
}
|
|
1744
2596
|
}
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
2597
|
+
// ── Public API ──────────────────────────────────────────────
|
|
2598
|
+
/**
|
|
2599
|
+
* Load a plugin component. Returns the RegisteredUIPlugin object.
|
|
2600
|
+
* Handles: in-memory cache → disk cache → download → evaluate → extract.
|
|
2601
|
+
*
|
|
2602
|
+
* `expectedHash` (SHA-256 hex from cp.getContextPlugin manifest) MUST be
|
|
2603
|
+
* provided for any bundle the user has not previously verified — without
|
|
2604
|
+
* it, a tampered bundle is `eval`'d as JS with full RN runtime + every
|
|
2605
|
+
* native capability the manifest declares.
|
|
2606
|
+
*
|
|
2607
|
+
* `grants` is what the MANIFEST grants this plugin (resolvePluginGrants) — the
|
|
2608
|
+
* only authority its device tools are registered under. Omitted (e.g. a
|
|
2609
|
+
* prewarm from the slim plugin list) → the bundle is loaded but its tools are
|
|
2610
|
+
* NOT registered: without the manifest there is nothing trusted to gate them
|
|
2611
|
+
* on, and registering with nothing would downgrade a registration the
|
|
2612
|
+
* manifest-driven tool loader already made.
|
|
2613
|
+
*/
|
|
2614
|
+
async load(pluginId, bundleUrl, version, expectedHash, grants) {
|
|
2615
|
+
const tenant = this.requireTenant();
|
|
2616
|
+
const epoch = this.epoch;
|
|
2617
|
+
const cached = this.getComponentFromMemory(pluginId, bundleUrl);
|
|
2618
|
+
if (cached) {
|
|
2619
|
+
this.markMemoryHit(bundleUrl, expectedHash);
|
|
2620
|
+
if (grants) await this.commitTools(pluginId, bundleUrl, grants, tenant, epoch);
|
|
2621
|
+
return cached;
|
|
2622
|
+
}
|
|
2623
|
+
let modulePromise = this.inflight.get(bundleUrl);
|
|
2624
|
+
if (!modulePromise) {
|
|
2625
|
+
const promise = this.loadModule(bundleUrl, version, expectedHash, tenant, epoch);
|
|
2626
|
+
modulePromise = promise;
|
|
2627
|
+
this.inflight.set(bundleUrl, promise);
|
|
2628
|
+
promise.catch(() => {
|
|
2629
|
+
}).finally(() => {
|
|
2630
|
+
if (this.inflight.get(bundleUrl) === promise) this.inflight.delete(bundleUrl);
|
|
2631
|
+
});
|
|
2632
|
+
}
|
|
2633
|
+
await modulePromise;
|
|
2634
|
+
if (this.isStale(epoch, tenant)) {
|
|
2635
|
+
throw new Error("[BundleManager] tenant changed while loading \u2014 discarding (fail closed)");
|
|
2636
|
+
}
|
|
2637
|
+
if (grants) await this.commitTools(pluginId, bundleUrl, grants, tenant, epoch);
|
|
2638
|
+
const component = this.getComponentFromMemory(pluginId, bundleUrl);
|
|
2639
|
+
if (!component) {
|
|
2640
|
+
throw new Error(
|
|
2641
|
+
`Bundle loaded from ${bundleUrl} but no component found for "${pluginId}". Available exports: ${this.describeModule(this.modules.get(bundleUrl))}`
|
|
2642
|
+
);
|
|
2643
|
+
}
|
|
2644
|
+
return component;
|
|
2645
|
+
}
|
|
2646
|
+
/**
|
|
2647
|
+
* Load a bundle for its `tools/` section only — no UI required.
|
|
2648
|
+
*
|
|
2649
|
+
* Same download, same integrity check, same evaluation as `load()`; the only
|
|
2650
|
+
* difference is that a bundle exporting tools and no `Component` is a success
|
|
2651
|
+
* here rather than "no component found". Returns the tool names registered.
|
|
2652
|
+
*
|
|
2653
|
+
* `grants` is REQUIRED: the tools are registered under `pluginId` with exactly
|
|
2654
|
+
* what the manifest grants (see load()), never what the bundle claims.
|
|
2655
|
+
*/
|
|
2656
|
+
async loadTools(pluginId, bundleUrl, version, expectedHash, grants) {
|
|
2657
|
+
if (!grants) {
|
|
2658
|
+
throw new Error(`[BundleManager] loadTools(${pluginId}) needs the manifest's grants (resolvePluginGrants) \u2014 refusing to register tools on the bundle's own claim`);
|
|
2659
|
+
}
|
|
2660
|
+
const tenant = this.requireTenant();
|
|
2661
|
+
const epoch = this.epoch;
|
|
2662
|
+
let modulePromise = this.inflight.get(bundleUrl);
|
|
2663
|
+
if (!modulePromise) {
|
|
2664
|
+
const promise = this.loadModule(bundleUrl, version, expectedHash, tenant, epoch);
|
|
2665
|
+
modulePromise = promise;
|
|
2666
|
+
this.inflight.set(bundleUrl, promise);
|
|
2667
|
+
promise.catch(() => {
|
|
2668
|
+
}).finally(() => {
|
|
2669
|
+
if (this.inflight.get(bundleUrl) === promise) this.inflight.delete(bundleUrl);
|
|
2670
|
+
});
|
|
2671
|
+
}
|
|
2672
|
+
await modulePromise;
|
|
2673
|
+
if (this.isStale(epoch, tenant)) {
|
|
2674
|
+
throw new Error("[BundleManager] tenant changed while loading tools \u2014 discarding (fail closed)");
|
|
2675
|
+
}
|
|
2676
|
+
await this.commitTools(pluginId, bundleUrl, grants, tenant, epoch);
|
|
2677
|
+
const names = listDeviceTools().filter((t) => t.pluginId === pluginId).map((t) => t.name);
|
|
2678
|
+
if (names.length === 0) {
|
|
2679
|
+
console.warn(
|
|
2680
|
+
`[BundleManager] ${pluginId}: bundle loaded from ${bundleUrl} but registered no tools. Available exports: ${this.describeModule(this.modules.get(bundleUrl))}`
|
|
2681
|
+
);
|
|
2682
|
+
}
|
|
2683
|
+
return names;
|
|
2684
|
+
}
|
|
2685
|
+
/**
|
|
2686
|
+
* Prewarm: load every plugin bundle in the background after app boot, so a
|
|
2687
|
+
* later open is instant (in-memory hit) instead of round-tripping the
|
|
2688
|
+
* network. Errors are swallowed per-plugin — one bad bundle shouldn't
|
|
2689
|
+
* block others. Concurrency capped at 3.
|
|
2690
|
+
*/
|
|
2691
|
+
async prewarmAll(items) {
|
|
2692
|
+
const queue = [...items];
|
|
2693
|
+
const concurrency = Math.min(3, queue.length);
|
|
2694
|
+
const start = Date.now();
|
|
2695
|
+
let okCount = 0;
|
|
2696
|
+
let failCount = 0;
|
|
2697
|
+
const worker = async () => {
|
|
2698
|
+
while (queue.length > 0) {
|
|
2699
|
+
const it = queue.shift();
|
|
2700
|
+
if (!it) return;
|
|
2701
|
+
try {
|
|
2702
|
+
await this.load(it.pluginId, it.bundleUrl, it.version, it.bundleHash, it.grants);
|
|
2703
|
+
okCount++;
|
|
2704
|
+
} catch (err) {
|
|
2705
|
+
failCount++;
|
|
2706
|
+
console.log(`[prewarm] ${it.pluginId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
1758
2709
|
};
|
|
1759
|
-
await
|
|
1760
|
-
|
|
1761
|
-
console.warn("[BundleManager] Cache write error:", err);
|
|
2710
|
+
await Promise.all(Array.from({ length: concurrency }, worker));
|
|
2711
|
+
console.log(`[prewarm] ${okCount} loaded, ${failCount} failed in ${Date.now() - start}ms`);
|
|
1762
2712
|
}
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
async download(bundleUrl, expectedHash) {
|
|
1775
|
-
const cacheBust = `${bundleUrl}${bundleUrl.includes("?") ? "&" : "?"}t=${Date.now()}`;
|
|
1776
|
-
const response = await (0, import_core6.apiFetchRaw)(cacheBust);
|
|
1777
|
-
if (!response.ok) {
|
|
1778
|
-
const body = await response.text().catch(() => "");
|
|
1779
|
-
throw new Error(`Download failed: ${response.status} ${response.statusText} \u2014 ${body.substring(0, 200)}`);
|
|
1780
|
-
}
|
|
1781
|
-
const content = await response.text();
|
|
1782
|
-
if (content.length < 50 || content.trimStart().startsWith("<!")) {
|
|
1783
|
-
throw new Error("Bundle content is empty or HTML \u2014 server may have returned an error page");
|
|
1784
|
-
}
|
|
1785
|
-
const headerHash = (response.headers.get("x-bundle-sha256") || "").toLowerCase();
|
|
1786
|
-
const computed = sha256Hex(content);
|
|
1787
|
-
if (expectedHash) {
|
|
1788
|
-
if (computed !== expectedHash) {
|
|
1789
|
-
throw new Error(
|
|
1790
|
-
`Bundle integrity FAIL: expected sha256=${expectedHash.slice(0, 12)}... got ${computed.slice(0, 12)}.... Refusing to evaluate.`
|
|
1791
|
-
);
|
|
2713
|
+
/**
|
|
2714
|
+
* Clear all caches (in-memory + storage).
|
|
2715
|
+
*/
|
|
2716
|
+
async clearAll() {
|
|
2717
|
+
this.dropInMemory();
|
|
2718
|
+
this.cacheTenant = null;
|
|
2719
|
+
try {
|
|
2720
|
+
await getBundleStorage().clearAll();
|
|
2721
|
+
} catch (err) {
|
|
2722
|
+
console.warn("[BundleManager] clearAll error:", err);
|
|
2723
|
+
}
|
|
1792
2724
|
}
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
2725
|
+
/**
|
|
2726
|
+
* Clear cache for a specific plugin/bundle.
|
|
2727
|
+
*/
|
|
2728
|
+
async clearPlugin(pluginId) {
|
|
2729
|
+
const storage = getBundleStorage();
|
|
2730
|
+
const index = await storage.loadIndex();
|
|
2731
|
+
const entry = index[pluginId];
|
|
2732
|
+
if (entry) {
|
|
2733
|
+
this.modules.delete(entry.bundleUrl);
|
|
2734
|
+
delete index[pluginId];
|
|
2735
|
+
await storage.saveIndex(index);
|
|
2736
|
+
try {
|
|
2737
|
+
await storage.deleteBundle(entry.localPath);
|
|
2738
|
+
} catch {
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
1797
2741
|
}
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
2742
|
+
/**
|
|
2743
|
+
* What every loaded bundle IS, right now: the sha256 of the bytes running,
|
|
2744
|
+
* the hash the server advertised, and where they came from.
|
|
2745
|
+
*
|
|
2746
|
+
* Deliberately not filtered or prettified — this is the raw answer to "did my
|
|
2747
|
+
* redeploy reach the phone?", and it is comparable directly against the
|
|
2748
|
+
* manifest `bundleHash` and the server's X-Bundle-SHA256 header.
|
|
2749
|
+
*/
|
|
2750
|
+
getLoadedBundles() {
|
|
2751
|
+
return [...this.provenance.values()];
|
|
2752
|
+
}
|
|
2753
|
+
// ── Private: module loading pipeline ────────────────────────
|
|
2754
|
+
async loadModule(bundleUrl, version, expectedHash, expectedTenant, expectedEpoch) {
|
|
2755
|
+
if (this.isStale(expectedEpoch, expectedTenant)) {
|
|
2756
|
+
throw new Error("[BundleManager] tenant changed before load started \u2014 discarding (fail closed)");
|
|
2757
|
+
}
|
|
2758
|
+
if (this.modules.has(bundleUrl)) {
|
|
2759
|
+
this.markMemoryHit(bundleUrl, expectedHash);
|
|
2760
|
+
return;
|
|
2761
|
+
}
|
|
2762
|
+
const storage = getBundleStorage();
|
|
2763
|
+
const index = await storage.loadIndex();
|
|
2764
|
+
const expectedLc = expectedHash ? expectedHash.toLowerCase() : null;
|
|
2765
|
+
let content = null;
|
|
2766
|
+
let verifiedHash = null;
|
|
2767
|
+
let servedFrom = "download";
|
|
2768
|
+
for (const entry of Object.values(index)) {
|
|
2769
|
+
if (entry.bundleUrl === bundleUrl && entry.version === version) {
|
|
2770
|
+
const age = Date.now() - entry.timestamp;
|
|
2771
|
+
const hashOk = expectedLc && entry.bundleHash && entry.bundleHash.toLowerCase() === expectedLc;
|
|
2772
|
+
if (age < CACHE_TTL && hashOk) {
|
|
2773
|
+
content = await storage.readBundle(entry.localPath);
|
|
2774
|
+
if (content) {
|
|
2775
|
+
verifiedHash = entry.bundleHash.toLowerCase();
|
|
2776
|
+
servedFrom = "disk-cache";
|
|
2777
|
+
break;
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
if (!content) {
|
|
2783
|
+
const downloaded = await this.download(bundleUrl, expectedLc);
|
|
2784
|
+
content = downloaded.content;
|
|
2785
|
+
verifiedHash = downloaded.sha256.toLowerCase();
|
|
2786
|
+
if (this.isStale(expectedEpoch, expectedTenant)) {
|
|
2787
|
+
throw new Error("[BundleManager] tenant changed during download \u2014 discarding (fail closed)");
|
|
2788
|
+
}
|
|
2789
|
+
try {
|
|
2790
|
+
const localPath = await storage.writeBundle(`${verifiedHash}.js`, content);
|
|
2791
|
+
if (!this.isStale(expectedEpoch, expectedTenant)) {
|
|
2792
|
+
index[bundleUrl] = {
|
|
2793
|
+
localPath,
|
|
2794
|
+
version,
|
|
2795
|
+
timestamp: Date.now(),
|
|
2796
|
+
bundleUrl,
|
|
2797
|
+
bundleHash: downloaded.sha256,
|
|
2798
|
+
tenant: expectedTenant
|
|
2799
|
+
};
|
|
2800
|
+
await storage.saveIndex(index);
|
|
2801
|
+
}
|
|
2802
|
+
} catch (err) {
|
|
2803
|
+
console.warn("[BundleManager] Cache write error:", err);
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
if (this.isStale(expectedEpoch, expectedTenant)) {
|
|
2807
|
+
throw new Error("[BundleManager] tenant changed while loading \u2014 discarding stale bundle before eval (fail closed)");
|
|
2808
|
+
}
|
|
2809
|
+
const mod = this.evaluate(content, bundleUrl, verifiedHash);
|
|
2810
|
+
this.modules.set(bundleUrl, mod);
|
|
2811
|
+
this.provenance.set(bundleUrl, {
|
|
2812
|
+
bundleUrl,
|
|
2813
|
+
version,
|
|
2814
|
+
hash: verifiedHash,
|
|
2815
|
+
expected: expectedLc,
|
|
2816
|
+
source: servedFrom,
|
|
2817
|
+
// The captured owner — same rule as the disk stamp. The final stale check
|
|
2818
|
+
// above guarantees getTenant() === expectedTenant here, but binding it to
|
|
2819
|
+
// the captured value keeps the ownership record impossible to mislabel.
|
|
2820
|
+
tenant: expectedTenant,
|
|
2821
|
+
loadedAt: Date.now()
|
|
2822
|
+
});
|
|
2823
|
+
console.log(
|
|
2824
|
+
`[BundleManager] RUNNING ${bundleUrl} v${version} sha=${(verifiedHash || "none").slice(0, 12)} (${servedFrom}${expectedLc && verifiedHash && expectedLc !== verifiedHash ? `, EXPECTED ${expectedLc.slice(0, 12)} \u2014 STALE` : ""})`
|
|
1802
2825
|
);
|
|
1803
2826
|
}
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
"module.exports = $1"
|
|
1820
|
-
);
|
|
1821
|
-
const mod = { exports: {} };
|
|
1822
|
-
try {
|
|
1823
|
-
const factory = new Function(
|
|
1824
|
-
"module",
|
|
1825
|
-
"exports",
|
|
1826
|
-
"require",
|
|
1827
|
-
"__filename",
|
|
1828
|
-
"__dirname",
|
|
1829
|
-
cleaned
|
|
1830
|
-
);
|
|
1831
|
-
factory(mod, mod.exports, requireShim, bundleUrl, "adas-plugin-bundles");
|
|
1832
|
-
} catch (err) {
|
|
1833
|
-
throw new Error(`Bundle evaluation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1834
|
-
}
|
|
1835
|
-
const raw = mod.exports;
|
|
1836
|
-
const materialized = {};
|
|
1837
|
-
for (const key of Object.getOwnPropertyNames(raw)) {
|
|
1838
|
-
try {
|
|
1839
|
-
materialized[key] = raw[key];
|
|
1840
|
-
} catch {
|
|
2827
|
+
/**
|
|
2828
|
+
* Register THIS plugin's device tools under the TRUSTED declaration, then
|
|
2829
|
+
* record that declaration on the disk-cache entry. Runs on every trusted load
|
|
2830
|
+
* — memory hit included — so the grants in force are always the latest the
|
|
2831
|
+
* manifest gave (a revoked grant stops applying on the next load, not the next
|
|
2832
|
+
* app start). Callers run it only after their stale check.
|
|
2833
|
+
*/
|
|
2834
|
+
async commitTools(pluginId, bundleUrl, grants, tenant, epoch) {
|
|
2835
|
+
const declaration = {
|
|
2836
|
+
pluginId,
|
|
2837
|
+
capabilities: grants.capabilities,
|
|
2838
|
+
stateDomains: grants.stateDomains
|
|
2839
|
+
};
|
|
2840
|
+
this.registerToolsFromModule(this.modules.get(bundleUrl), declaration);
|
|
2841
|
+
await this.recordTrustedDeclaration(bundleUrl, declaration, tenant, epoch);
|
|
1841
2842
|
}
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
2843
|
+
/**
|
|
2844
|
+
* Put the plugin's own `tools` section in the device tool registry — under the
|
|
2845
|
+
* pluginId and grants the MANIFEST resolved, never the bundle's own `id`,
|
|
2846
|
+
* `capabilities` or `stateDomains`. A bundle is code the manifest authorised
|
|
2847
|
+
* by hash; what it may touch is the manifest's to say, same as its rendered
|
|
2848
|
+
* surface (resolvePluginRuntimeProps). Its own `id` would also let it register
|
|
2849
|
+
* as another plugin — including the host's platform tools, which a same-id
|
|
2850
|
+
* re-registration replaces.
|
|
2851
|
+
*
|
|
2852
|
+
* Only this plugin's export (the same lookup the component uses) is
|
|
2853
|
+
* registered. Another export's tools in the same bundle belong to another
|
|
2854
|
+
* plugin and register when THAT plugin's manifest loads it.
|
|
2855
|
+
*/
|
|
2856
|
+
registerToolsFromModule(mod, { pluginId, capabilities, stateDomains }) {
|
|
2857
|
+
if (!mod) return;
|
|
2858
|
+
const own = this.findPluginExport(mod, pluginId, hasTools);
|
|
2859
|
+
const others = Object.keys(mod).filter((k) => hasTools(mod[k]) && mod[k] !== own);
|
|
2860
|
+
if (others.length > 0) {
|
|
2861
|
+
console.warn(
|
|
2862
|
+
`[BundleManager] ${pluginId}: not registering tools from export(s) ${others.join(", ")} \u2014 they are not this plugin's, and only their own manifest can grant them`
|
|
2863
|
+
);
|
|
2864
|
+
}
|
|
2865
|
+
if (!own) return;
|
|
2866
|
+
const ungranted = [
|
|
2867
|
+
...(Array.isArray(own.stateDomains) ? own.stateDomains : []).filter((d) => !stateDomains?.includes(d)),
|
|
2868
|
+
...Object.keys(own.capabilities || {}).filter((k) => own.capabilities[k] === true && !capabilities?.[k])
|
|
2869
|
+
];
|
|
2870
|
+
if (ungranted.length > 0) {
|
|
2871
|
+
console.warn(
|
|
2872
|
+
`[BundleManager] ${pluginId}: bundle declares ${ungranted.join(", ")} but the manifest does not grant it \u2014 denied`
|
|
2873
|
+
);
|
|
2874
|
+
}
|
|
2875
|
+
try {
|
|
2876
|
+
registerDeviceTools(pluginId, own.tools, capabilities, stateDomains);
|
|
2877
|
+
this.registeredToolPluginIds.add(pluginId);
|
|
2878
|
+
} catch (err) {
|
|
2879
|
+
console.warn(
|
|
2880
|
+
`[BundleManager] tool registration failed for ${pluginId}:`,
|
|
2881
|
+
err instanceof Error ? err.message : String(err)
|
|
2882
|
+
);
|
|
2883
|
+
}
|
|
1868
2884
|
}
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
}
|
|
1895
|
-
function useNativeBridge(pluginId, connectorId, commandBufferRef) {
|
|
1896
|
-
const initCallbackRef = (0, import_react10.useRef)(null);
|
|
1897
|
-
const commandListenersRef = (0, import_react10.useRef)(/* @__PURE__ */ new Set());
|
|
1898
|
-
const initializedRef = (0, import_react10.useRef)(false);
|
|
1899
|
-
const fireInit = (0, import_react10.useCallback)(() => {
|
|
1900
|
-
if (initializedRef.current || !initCallbackRef.current) return;
|
|
1901
|
-
initCallbackRef.current(buildContext(pluginId, connectorId));
|
|
1902
|
-
initializedRef.current = true;
|
|
1903
|
-
}, [pluginId, connectorId]);
|
|
1904
|
-
const onInit = (0, import_react10.useCallback)((cb) => {
|
|
1905
|
-
initCallbackRef.current = cb;
|
|
1906
|
-
setTimeout(fireInit, 0);
|
|
1907
|
-
}, [fireInit]);
|
|
1908
|
-
const mcpCall = (0, import_react10.useCallback)(async (toolOrConnector, argsOrTool, cIdOrArgs) => {
|
|
1909
|
-
let tool;
|
|
1910
|
-
let args;
|
|
1911
|
-
let cId;
|
|
1912
|
-
if (typeof argsOrTool === "string") {
|
|
1913
|
-
cId = toolOrConnector;
|
|
1914
|
-
tool = argsOrTool;
|
|
1915
|
-
args = cIdOrArgs || {};
|
|
1916
|
-
} else {
|
|
1917
|
-
tool = toolOrConnector;
|
|
1918
|
-
args = argsOrTool;
|
|
1919
|
-
cId = typeof cIdOrArgs === "string" ? cIdOrArgs : void 0;
|
|
1920
|
-
}
|
|
1921
|
-
const targetConnector = cId || connectorId;
|
|
1922
|
-
if (!targetConnector) {
|
|
1923
|
-
console.warn(`[NativeBridge:${pluginId}] mcpCall failed \u2014 no connectorId. tool=${tool}`);
|
|
1924
|
-
throw new Error(`[NativeBridge:${pluginId}] mcpCall requires a connectorId`);
|
|
1925
|
-
}
|
|
1926
|
-
try {
|
|
1927
|
-
const data = await (0, import_core7.connectorCall)(targetConnector, tool, args);
|
|
1928
|
-
if (!data.ok && data.error) {
|
|
1929
|
-
throw new Error(data.error);
|
|
2885
|
+
/**
|
|
2886
|
+
* Record the trusted declaration on this tenant's disk-cache entry for the
|
|
2887
|
+
* bytes RUNNING — the recovery path (tools/loader loadFromDiskCache) has no
|
|
2888
|
+
* manifest and re-registers with exactly this. Only onto an entry this tenant
|
|
2889
|
+
* owns whose hash is the running hash: the grant was issued for these bytes,
|
|
2890
|
+
* and those are the bytes recovery will evaluate. Best-effort — a lost write
|
|
2891
|
+
* leaves no `trusted`, which recovery treats as fail closed.
|
|
2892
|
+
*/
|
|
2893
|
+
async recordTrustedDeclaration(bundleUrl, declaration, tenant, epoch) {
|
|
2894
|
+
const key = JSON.stringify(declaration);
|
|
2895
|
+
if (this.recordedDeclarations.get(bundleUrl) === key) return;
|
|
2896
|
+
try {
|
|
2897
|
+
const storage = getBundleStorage();
|
|
2898
|
+
const index = await storage.loadIndex();
|
|
2899
|
+
const entry = index[bundleUrl];
|
|
2900
|
+
const running = this.provenance.get(bundleUrl)?.hash;
|
|
2901
|
+
if (!entry || entry.tenant !== tenant || !running || entry.bundleHash?.toLowerCase() !== running) return;
|
|
2902
|
+
if (this.isStale(epoch, tenant)) return;
|
|
2903
|
+
if (JSON.stringify(entry.trusted) !== key) {
|
|
2904
|
+
index[bundleUrl] = { ...entry, trusted: declaration };
|
|
2905
|
+
await storage.saveIndex(index);
|
|
2906
|
+
}
|
|
2907
|
+
this.recordedDeclarations.set(bundleUrl, key);
|
|
2908
|
+
} catch (err) {
|
|
2909
|
+
console.warn("[BundleManager] could not record trusted declaration:", err);
|
|
2910
|
+
}
|
|
1930
2911
|
}
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
2912
|
+
/**
|
|
2913
|
+
* Download + integrity-verify a bundle. Refuses to return when:
|
|
2914
|
+
* - manifest hash and computed hash disagree
|
|
2915
|
+
* - server's `X-Bundle-SHA256` disagrees with manifest hash
|
|
2916
|
+
* - no expected hash AND no server hash header (cannot prove the bundle
|
|
2917
|
+
* wasn't tampered with — fail closed rather than trust the wire).
|
|
2918
|
+
*/
|
|
2919
|
+
async download(bundleUrl, expectedHash) {
|
|
2920
|
+
const cacheBust = `${bundleUrl}${bundleUrl.includes("?") ? "&" : "?"}t=${Date.now()}`;
|
|
2921
|
+
const response = await (0, import_core9.apiFetchRaw)(cacheBust);
|
|
2922
|
+
if (!response.ok) {
|
|
2923
|
+
const body = await response.text().catch(() => "");
|
|
2924
|
+
throw new Error(`Download failed: ${response.status} ${response.statusText} \u2014 ${body.substring(0, 200)}`);
|
|
2925
|
+
}
|
|
2926
|
+
const content = await response.text();
|
|
2927
|
+
if (content.length < 50 || content.trimStart().startsWith("<!")) {
|
|
2928
|
+
throw new Error("Bundle content is empty or HTML \u2014 server may have returned an error page");
|
|
2929
|
+
}
|
|
2930
|
+
const headerHash = (response.headers.get("x-bundle-sha256") || "").toLowerCase();
|
|
2931
|
+
const computed = sha256Hex(content);
|
|
2932
|
+
if (expectedHash) {
|
|
2933
|
+
if (computed !== expectedHash) {
|
|
2934
|
+
throw new Error(
|
|
2935
|
+
`Bundle integrity FAIL: expected sha256=${expectedHash.slice(0, 12)}... got ${computed.slice(0, 12)}.... Refusing to evaluate.`
|
|
2936
|
+
);
|
|
2937
|
+
}
|
|
2938
|
+
if (headerHash && headerHash !== expectedHash) {
|
|
2939
|
+
throw new Error(
|
|
2940
|
+
`Bundle hash mismatch: manifest=${expectedHash.slice(0, 12)}... server=${headerHash.slice(0, 12)}.... Refusing to evaluate.`
|
|
2941
|
+
);
|
|
1956
2942
|
}
|
|
2943
|
+
} else if (headerHash) {
|
|
2944
|
+
if (computed !== headerHash) {
|
|
2945
|
+
throw new Error(
|
|
2946
|
+
`Bundle body sha256 (${computed.slice(0, 12)}...) does not match server's X-Bundle-SHA256 (${headerHash.slice(0, 12)}...).`
|
|
2947
|
+
);
|
|
2948
|
+
}
|
|
2949
|
+
console.warn(`[BundleManager] No manifest bundleHash \u2014 verified via X-Bundle-SHA256 header only`);
|
|
1957
2950
|
} else {
|
|
1958
|
-
|
|
2951
|
+
throw new Error(
|
|
2952
|
+
`No bundleHash in manifest and no X-Bundle-SHA256 header from server. Refusing to evaluate untrusted bundle.`
|
|
2953
|
+
);
|
|
1959
2954
|
}
|
|
2955
|
+
return { content, sha256: computed };
|
|
1960
2956
|
}
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
}, [pluginId, commandBufferRef]);
|
|
1973
|
-
(0, import_react10.useEffect)(() => {
|
|
1974
|
-
const unsub = import_core7.pluginCommandEmitter.on("plugin-command", (cmd) => {
|
|
1975
|
-
if (cmd.pluginId !== pluginId) return;
|
|
1976
|
-
if (cmd.command === "__focus") return;
|
|
1977
|
-
commandListenersRef.current.forEach((fn) => {
|
|
2957
|
+
evaluate(content, bundleUrl, verifiedHash) {
|
|
2958
|
+
assertBundleIntegrity(content, verifiedHash, bundleUrl);
|
|
2959
|
+
let cleaned = content.replace(
|
|
2960
|
+
/__toESM\(require\(("[^"]+")\)(?:,\s*\d+)?\)/g,
|
|
2961
|
+
"require($1)"
|
|
2962
|
+
);
|
|
2963
|
+
cleaned = cleaned.replace(
|
|
2964
|
+
/module\.exports\s*=\s*__toCommonJS\((\w+)\)/g,
|
|
2965
|
+
"module.exports = $1"
|
|
2966
|
+
);
|
|
2967
|
+
const mod = { exports: {} };
|
|
1978
2968
|
try {
|
|
1979
|
-
|
|
1980
|
-
|
|
2969
|
+
const factory = new Function(
|
|
2970
|
+
"module",
|
|
2971
|
+
"exports",
|
|
2972
|
+
"require",
|
|
2973
|
+
"__filename",
|
|
2974
|
+
"__dirname",
|
|
2975
|
+
cleaned
|
|
2976
|
+
);
|
|
2977
|
+
factory(mod, mod.exports, requireShim, bundleUrl, "adas-plugin-bundles");
|
|
2978
|
+
} catch (err) {
|
|
2979
|
+
throw new Error(`Bundle evaluation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1981
2980
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
}, [pluginId]);
|
|
1986
|
-
(0, import_react10.useEffect)(() => {
|
|
1987
|
-
const timer = setTimeout(() => {
|
|
1988
|
-
const dispatch = (cmd) => {
|
|
1989
|
-
commandListenersRef.current.forEach((fn) => {
|
|
2981
|
+
const raw = mod.exports;
|
|
2982
|
+
const materialized = {};
|
|
2983
|
+
for (const key of Object.getOwnPropertyNames(raw)) {
|
|
1990
2984
|
try {
|
|
1991
|
-
|
|
2985
|
+
materialized[key] = raw[key];
|
|
1992
2986
|
} catch {
|
|
1993
2987
|
}
|
|
1994
|
-
});
|
|
1995
|
-
};
|
|
1996
|
-
if (commandBufferRef) {
|
|
1997
|
-
const remaining = [];
|
|
1998
|
-
for (const cmd of commandBufferRef.current) {
|
|
1999
|
-
if (cmd.pluginId === pluginId && cmd.command !== "__focus") dispatch(cmd);
|
|
2000
|
-
else remaining.push(cmd);
|
|
2001
2988
|
}
|
|
2002
|
-
|
|
2989
|
+
return materialized;
|
|
2003
2990
|
}
|
|
2004
|
-
|
|
2005
|
-
|
|
2991
|
+
// ── Private: component extraction ───────────────────────────
|
|
2992
|
+
/** Find a plugin component in a loaded module. */
|
|
2993
|
+
getComponentFromMemory(pluginId, bundleUrl) {
|
|
2994
|
+
return this.findPluginExport(this.modules.get(bundleUrl), pluginId, hasComponent);
|
|
2995
|
+
}
|
|
2996
|
+
/**
|
|
2997
|
+
* Find THIS plugin's export in a loaded module — the one lookup both the
|
|
2998
|
+
* component and the device tools use, so a plugin's UI and its tools always
|
|
2999
|
+
* come from the same export. `accept` says what the caller needs from it.
|
|
3000
|
+
* Handles: direct key match, PascalCase key, default export, multi-plugin .id match.
|
|
3001
|
+
*/
|
|
3002
|
+
findPluginExport(mod, pluginId, accept) {
|
|
3003
|
+
if (!mod) return null;
|
|
3004
|
+
const shortName = pluginId.includes(":") ? pluginId.split(":").pop() : pluginId;
|
|
3005
|
+
if (accept(mod[pluginId])) return mod[pluginId];
|
|
3006
|
+
const pascal = shortName.replace(/(^|-)([a-z])/g, (_, __, c) => c.toUpperCase());
|
|
3007
|
+
if (accept(mod[pascal])) return mod[pascal];
|
|
3008
|
+
if (accept(mod.default)) return mod.default;
|
|
3009
|
+
for (const key of Object.keys(mod)) {
|
|
3010
|
+
const val = mod[key];
|
|
3011
|
+
if (val && (val.id === pluginId || val.id === shortName) && accept(val)) return val;
|
|
3012
|
+
}
|
|
3013
|
+
return null;
|
|
3014
|
+
}
|
|
3015
|
+
describeModule(mod) {
|
|
3016
|
+
if (!mod) return "(empty)";
|
|
3017
|
+
return Object.keys(mod).map((k) => {
|
|
3018
|
+
const v = mod[k];
|
|
3019
|
+
if (v && typeof v === "object" && v.id) return `${k}(id="${v.id}")`;
|
|
3020
|
+
return k;
|
|
3021
|
+
}).join(", ");
|
|
2006
3022
|
}
|
|
2007
|
-
}, 500);
|
|
2008
|
-
return () => clearTimeout(timer);
|
|
2009
|
-
}, [pluginId, commandBufferRef]);
|
|
2010
|
-
(0, import_react10.useEffect)(() => {
|
|
2011
|
-
return () => {
|
|
2012
|
-
commandListenersRef.current.clear();
|
|
2013
|
-
initializedRef.current = false;
|
|
2014
3023
|
};
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
if (!targetConnector) {
|
|
2049
|
-
throw new Error(`[NativeBridge:${pluginId}] mcpCall requires a connectorId`);
|
|
3024
|
+
bundleManager = new BundleManager();
|
|
3025
|
+
}
|
|
3026
|
+
});
|
|
3027
|
+
|
|
3028
|
+
// src/tools/loader.ts
|
|
3029
|
+
var loader_exports = {};
|
|
3030
|
+
__export(loader_exports, {
|
|
3031
|
+
ensureDeviceToolsLoaded: () => ensureDeviceToolsLoaded,
|
|
3032
|
+
resetDeviceToolLoader: () => resetDeviceToolLoader
|
|
3033
|
+
});
|
|
3034
|
+
async function ensureDeviceToolsLoaded({ force = false } = {}) {
|
|
3035
|
+
if (loadedOnce && !force) return listDeviceTools().map((t) => t.name);
|
|
3036
|
+
if (inflight) return inflight;
|
|
3037
|
+
inflight = (async () => {
|
|
3038
|
+
try {
|
|
3039
|
+
const summaries = await listPlugins();
|
|
3040
|
+
const manifests = await Promise.all(
|
|
3041
|
+
(summaries || []).map(
|
|
3042
|
+
(s) => getPlugin(s.id).catch(() => null)
|
|
3043
|
+
)
|
|
3044
|
+
);
|
|
3045
|
+
const targets = manifests.filter((m) => {
|
|
3046
|
+
const declared = m?.capabilities?.device_tools;
|
|
3047
|
+
return Array.isArray(declared) && declared.length > 0;
|
|
3048
|
+
});
|
|
3049
|
+
if (targets.length === 0) {
|
|
3050
|
+
const recovered = await loadFromDiskCache();
|
|
3051
|
+
if (recovered.length > 0) {
|
|
3052
|
+
console.log(`[deviceTools] catalog listed none \u2014 recovered from cache: ${recovered.join(", ")}`);
|
|
3053
|
+
return recovered;
|
|
3054
|
+
}
|
|
3055
|
+
console.log("[deviceTools] no device tools available yet \u2014 will look again next call");
|
|
3056
|
+
return [];
|
|
2050
3057
|
}
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
3058
|
+
await Promise.all(
|
|
3059
|
+
targets.map(async (m) => {
|
|
3060
|
+
const bundleUrl = getNativeBundleUrl(m);
|
|
3061
|
+
if (!bundleUrl) {
|
|
3062
|
+
console.warn(`[deviceTools] ${m.id} declares device_tools but has no native bundle`);
|
|
3063
|
+
return;
|
|
3064
|
+
}
|
|
3065
|
+
try {
|
|
3066
|
+
await bundleManager.loadTools(
|
|
3067
|
+
m.id,
|
|
3068
|
+
bundleUrl,
|
|
3069
|
+
m.version || "1.0.0",
|
|
3070
|
+
getNativeBundleHash(m) || void 0,
|
|
3071
|
+
resolvePluginGrants({ pluginId: m.id, manifest: m })
|
|
3072
|
+
);
|
|
3073
|
+
} catch (err) {
|
|
3074
|
+
console.warn(`[deviceTools] ${m.id} failed to load:`, err?.message || String(err));
|
|
3075
|
+
}
|
|
3076
|
+
})
|
|
3077
|
+
);
|
|
3078
|
+
const names = listDeviceTools().map((t) => t.name);
|
|
3079
|
+
loadedOnce = names.length > 0;
|
|
3080
|
+
console.log(`[deviceTools] ready: ${names.length > 0 ? names.join(", ") : "(none registered \u2014 will retry next call)"}`);
|
|
3081
|
+
return names;
|
|
3082
|
+
} finally {
|
|
3083
|
+
inflight = null;
|
|
3084
|
+
}
|
|
3085
|
+
})();
|
|
3086
|
+
return inflight;
|
|
3087
|
+
}
|
|
3088
|
+
async function loadFromDiskCache() {
|
|
3089
|
+
try {
|
|
3090
|
+
const index = await getBundleStorage().loadIndex();
|
|
3091
|
+
const entries = Object.values(index || {});
|
|
3092
|
+
if (entries.length === 0) return [];
|
|
3093
|
+
const tenant = (0, import_core10.getTenant)();
|
|
3094
|
+
if (!tenant) {
|
|
3095
|
+
console.warn("[deviceTools] disk-cache recovery skipped \u2014 no active tenant");
|
|
3096
|
+
return [];
|
|
3097
|
+
}
|
|
3098
|
+
const mine = selectOwnedCacheEntries(entries, tenant);
|
|
3099
|
+
const skipped = entries.length - mine.length;
|
|
3100
|
+
if (skipped > 0) {
|
|
3101
|
+
console.log(`[deviceTools] disk-cache recovery: ${mine.length} for "${tenant}", skipped ${skipped} not owned by it`);
|
|
3102
|
+
}
|
|
3103
|
+
const declared = mine.filter((e) => !!e.trusted?.pluginId);
|
|
3104
|
+
if (declared.length < mine.length) {
|
|
3105
|
+
console.log(
|
|
3106
|
+
`[deviceTools] disk-cache recovery: skipped ${mine.length - declared.length} with no recorded manifest grant (fail closed)`
|
|
3107
|
+
);
|
|
3108
|
+
}
|
|
3109
|
+
if (declared.length === 0) return [];
|
|
3110
|
+
await Promise.all(declared.map(async (e) => {
|
|
2072
3111
|
try {
|
|
2073
|
-
|
|
2074
|
-
} catch
|
|
2075
|
-
console.warn("[bridge.sendMessage] failed:", err instanceof Error ? err.message : String(err));
|
|
3112
|
+
await bundleManager.loadTools(e.trusted.pluginId, e.bundleUrl, e.version, e.bundleHash, e.trusted);
|
|
3113
|
+
} catch {
|
|
2076
3114
|
}
|
|
3115
|
+
}));
|
|
3116
|
+
return listDeviceTools().map((t) => t.name);
|
|
3117
|
+
} catch (err) {
|
|
3118
|
+
console.warn("[deviceTools] disk-cache recovery failed:", err?.message || String(err));
|
|
3119
|
+
return [];
|
|
3120
|
+
}
|
|
3121
|
+
}
|
|
3122
|
+
function resetDeviceToolLoader() {
|
|
3123
|
+
loadedOnce = false;
|
|
3124
|
+
}
|
|
3125
|
+
var import_core10, inflight, loadedOnce;
|
|
3126
|
+
var init_loader = __esm({
|
|
3127
|
+
"src/tools/loader.ts"() {
|
|
3128
|
+
"use strict";
|
|
3129
|
+
init_plugins();
|
|
3130
|
+
init_bundleManager();
|
|
3131
|
+
init_registry();
|
|
3132
|
+
init_storage();
|
|
3133
|
+
init_pluginRuntime();
|
|
3134
|
+
import_core10 = require("@ateam-ai-mobile/core");
|
|
3135
|
+
inflight = null;
|
|
3136
|
+
loadedOnce = false;
|
|
3137
|
+
}
|
|
3138
|
+
});
|
|
3139
|
+
|
|
3140
|
+
// src/index.ts
|
|
3141
|
+
var index_exports = {};
|
|
3142
|
+
__export(index_exports, {
|
|
3143
|
+
ActionButton: () => ActionButton,
|
|
3144
|
+
ActivityIndicator: () => import_react_native.ActivityIndicator,
|
|
3145
|
+
AdaOrb: () => AdaOrb,
|
|
3146
|
+
Animated: () => import_react_native.Animated,
|
|
3147
|
+
Badge: () => Badge,
|
|
3148
|
+
CameraPreview: () => CameraPreview,
|
|
3149
|
+
Card: () => Card,
|
|
3150
|
+
EmptyState: () => EmptyState,
|
|
3151
|
+
ErrorState: () => ErrorState,
|
|
3152
|
+
FlatList: () => import_react_native.FlatList,
|
|
3153
|
+
Image: () => import_react_native.Image,
|
|
3154
|
+
ListItem: () => ListItem,
|
|
3155
|
+
LoadingState: () => LoadingState,
|
|
3156
|
+
MemoryBundleStorage: () => MemoryBundleStorage,
|
|
3157
|
+
Modal: () => import_react_native.Modal,
|
|
3158
|
+
PLATFORM_VOICE_CALLER: () => PLATFORM_VOICE_CALLER,
|
|
3159
|
+
Platform: () => import_react_native.Platform,
|
|
3160
|
+
PluginSDK: () => PluginSDK,
|
|
3161
|
+
Pressable: () => import_react_native.Pressable,
|
|
3162
|
+
RefreshControl: () => import_react_native.RefreshControl,
|
|
3163
|
+
ScrollView: () => import_react_native.ScrollView,
|
|
3164
|
+
SearchBar: () => SearchBar,
|
|
3165
|
+
SectionList: () => import_react_native.SectionList,
|
|
3166
|
+
StyleSheet: () => import_react_native.StyleSheet,
|
|
3167
|
+
SurfaceCard: () => SurfaceCard,
|
|
3168
|
+
SurfaceDispatcher: () => SurfaceDispatcher,
|
|
3169
|
+
SurfaceDrawer: () => SurfaceDrawer,
|
|
3170
|
+
SurfaceFullscreen: () => SurfaceFullscreen,
|
|
3171
|
+
Switch: () => import_react_native.Switch,
|
|
3172
|
+
TabBar: () => TabBar,
|
|
3173
|
+
Text: () => import_react_native.Text,
|
|
3174
|
+
TextInput: () => import_react_native.TextInput,
|
|
3175
|
+
TouchableOpacity: () => import_react_native.TouchableOpacity,
|
|
3176
|
+
View: () => import_react_native.View,
|
|
3177
|
+
__resetSessionSyncForTest: () => __resetSessionSyncForTest,
|
|
3178
|
+
bundleManager: () => bundleManager,
|
|
3179
|
+
callDeviceTool: () => callDeviceTool,
|
|
3180
|
+
clearDeviceTools: () => clearDeviceTools,
|
|
3181
|
+
createNativeBridge: () => createNativeBridge,
|
|
3182
|
+
deviceStateV2: () => device_state_exports,
|
|
3183
|
+
deviceToolsActionForSession: () => deviceToolsActionForSession,
|
|
3184
|
+
ensureDeviceToolsLoaded: () => ensureDeviceToolsLoaded,
|
|
3185
|
+
explainCapability: () => explainCapability,
|
|
3186
|
+
gateDeviceState: () => gateDeviceState,
|
|
3187
|
+
gatePluginNative: () => gatePluginNative,
|
|
3188
|
+
gateVoice: () => gateVoice,
|
|
3189
|
+
getCameraPreviewProvider: () => getCameraPreviewProvider,
|
|
3190
|
+
getHaptics: () => getHaptics,
|
|
3191
|
+
getNativeBundleHash: () => getNativeBundleHash,
|
|
3192
|
+
getNativeBundleUrl: () => getNativeBundleUrl,
|
|
3193
|
+
getNativeComponentId: () => getNativeComponentId,
|
|
3194
|
+
getPlugin: () => getPlugin,
|
|
3195
|
+
getPluginNative: () => getPluginNative,
|
|
3196
|
+
handleServerDeviceToolCall: () => handleServerDeviceToolCall,
|
|
3197
|
+
hasDeviceTool: () => hasDeviceTool,
|
|
3198
|
+
implementedStateDomains: () => implementedStateDomains,
|
|
3199
|
+
implementedVoice: () => implementedVoice,
|
|
3200
|
+
isActionable: () => isActionable,
|
|
3201
|
+
isDeviceToolCommand: () => isDeviceToolCommand,
|
|
3202
|
+
isInlineSurface: () => isInlineSurface,
|
|
3203
|
+
isMountSurface: () => isMountSurface,
|
|
3204
|
+
listDeviceTools: () => listDeviceTools,
|
|
3205
|
+
listPlugins: () => listPlugins,
|
|
3206
|
+
nativePluginRegistry: () => nativePluginRegistry,
|
|
3207
|
+
neverObserved: () => neverObserved,
|
|
3208
|
+
register: () => register,
|
|
3209
|
+
registerBundleModules: () => registerBundleModules,
|
|
3210
|
+
registerDeviceTools: () => registerDeviceTools,
|
|
3211
|
+
registerNativePlugin: () => registerNativePlugin,
|
|
3212
|
+
registerPlatformDeviceTools: () => registerPlatformDeviceTools,
|
|
3213
|
+
registerService: () => registerService,
|
|
3214
|
+
registerTools: () => registerTools,
|
|
3215
|
+
resetDeviceToolLoader: () => resetDeviceToolLoader,
|
|
3216
|
+
resolvePluginGrants: () => resolvePluginGrants,
|
|
3217
|
+
resolvePluginRuntimeProps: () => resolvePluginRuntimeProps,
|
|
3218
|
+
resolvePluginUrl: () => resolvePluginUrl,
|
|
3219
|
+
setBundleStorage: () => setBundleStorage,
|
|
3220
|
+
setCameraPreviewProvider: () => setCameraPreviewProvider,
|
|
3221
|
+
setDeviceStateProvider: () => setDeviceStateProvider,
|
|
3222
|
+
setDeviceToolContextProvider: () => setDeviceToolContextProvider,
|
|
3223
|
+
setDeviceToolResultPoster: () => setDeviceToolResultPoster,
|
|
3224
|
+
setHapticsProvider: () => setHapticsProvider,
|
|
3225
|
+
setPluginNativeProvider: () => setPluginNativeProvider,
|
|
3226
|
+
setPluginThemeColors: () => setPluginThemeColors,
|
|
3227
|
+
setVoiceProvider: () => setVoiceProvider,
|
|
3228
|
+
supportsNative: () => supportsNative,
|
|
3229
|
+
supportsWebView: () => supportsWebView,
|
|
3230
|
+
syncDeviceToolsForSession: () => syncDeviceToolsForSession,
|
|
3231
|
+
unregisterDeviceTools: () => unregisterDeviceTools,
|
|
3232
|
+
unregisterNativePlugin: () => unregisterNativePlugin,
|
|
3233
|
+
useApi: () => useApi,
|
|
3234
|
+
useNativeBridge: () => useNativeBridge,
|
|
3235
|
+
usePlugin: () => usePlugin,
|
|
3236
|
+
usePluginTheme: () => usePluginTheme,
|
|
3237
|
+
useRemotePlugins: () => useRemotePlugins,
|
|
3238
|
+
useSurfaceStore: () => useSurfaceStore
|
|
3239
|
+
});
|
|
3240
|
+
module.exports = __toCommonJS(index_exports);
|
|
3241
|
+
init_sdk();
|
|
3242
|
+
|
|
3243
|
+
// src/sdk/device-state.ts
|
|
3244
|
+
var device_state_exports = {};
|
|
3245
|
+
__export(device_state_exports, {
|
|
3246
|
+
DeviceBridgeError: () => DeviceBridgeError,
|
|
3247
|
+
explainCapability: () => explainCapability,
|
|
3248
|
+
isActionable: () => isActionable,
|
|
3249
|
+
neverObserved: () => neverObserved
|
|
3250
|
+
});
|
|
3251
|
+
var DeviceBridgeError = class extends Error {
|
|
3252
|
+
constructor(code, message, opts) {
|
|
3253
|
+
super(message);
|
|
3254
|
+
this.name = "DeviceBridgeError";
|
|
3255
|
+
this.code = code;
|
|
3256
|
+
this.domain = opts?.domain;
|
|
3257
|
+
this.recoverable = opts?.recoverable;
|
|
3258
|
+
}
|
|
3259
|
+
};
|
|
3260
|
+
var CONFIDENCE_ORDER = {
|
|
3261
|
+
unknown: 0,
|
|
3262
|
+
low: 1,
|
|
3263
|
+
medium: 2,
|
|
3264
|
+
high: 3
|
|
3265
|
+
};
|
|
3266
|
+
function isActionable(quality, budget) {
|
|
3267
|
+
const { freshness, confidence } = quality;
|
|
3268
|
+
if (freshness.state === "unknown" || freshness.state === "stale") return false;
|
|
3269
|
+
if (freshness.ageMs === null || freshness.ageMs > budget.maxAgeMs) return false;
|
|
3270
|
+
if (budget.minConfidence) {
|
|
3271
|
+
const have = CONFIDENCE_ORDER[confidence?.level ?? "unknown"];
|
|
3272
|
+
if (have < CONFIDENCE_ORDER[budget.minConfidence]) return false;
|
|
3273
|
+
}
|
|
3274
|
+
return true;
|
|
3275
|
+
}
|
|
3276
|
+
function neverObserved() {
|
|
3277
|
+
return { state: "unknown", observedAt: null, ageMs: null };
|
|
3278
|
+
}
|
|
3279
|
+
function explainCapability(status) {
|
|
3280
|
+
if (status.support === "unsupported") return "this device does not have it";
|
|
3281
|
+
if (status.permission === "denied") return "permission was denied";
|
|
3282
|
+
if (status.permission === "restricted") return "permission is restricted";
|
|
3283
|
+
if (status.permission === "notDetermined") return "permission has not been asked for yet";
|
|
3284
|
+
if (status.permission === "limited") return "only limited access was granted";
|
|
3285
|
+
if (status.restriction === "policyRestricted") return "this plugin did not declare it";
|
|
3286
|
+
if (status.restriction === "backgroundRestricted") return "the app is in the background";
|
|
3287
|
+
if (status.restriction === "lowPowerRestricted") return "the device is in low-power mode";
|
|
3288
|
+
if (status.availability === "busy") return "it is in use by something else";
|
|
3289
|
+
if (status.availability === "temporarilyUnavailable") return "it is temporarily unavailable";
|
|
3290
|
+
if (status.availability === "unavailable") return "it is unavailable";
|
|
3291
|
+
if (status.support === "unknown" || status.availability === "unknown") {
|
|
3292
|
+
return "its state could not be determined";
|
|
3293
|
+
}
|
|
3294
|
+
return null;
|
|
3295
|
+
}
|
|
3296
|
+
|
|
3297
|
+
// src/index.ts
|
|
3298
|
+
init_deviceStateHost();
|
|
3299
|
+
init_voiceHost();
|
|
3300
|
+
init_host();
|
|
3301
|
+
init_pluginRuntime();
|
|
3302
|
+
init_registry();
|
|
3303
|
+
init_loader();
|
|
3304
|
+
|
|
3305
|
+
// src/tools/platformTools.ts
|
|
3306
|
+
init_registry();
|
|
3307
|
+
var PLATFORM_PLUGIN_ID = "platform:device-tools";
|
|
3308
|
+
function registerPlatformDeviceTools() {
|
|
3309
|
+
registerDeviceTools(PLATFORM_PLUGIN_ID, [
|
|
3310
|
+
{
|
|
3311
|
+
name: "device.echo",
|
|
3312
|
+
description: "Echo a string back from the user's own device.",
|
|
3313
|
+
input_schema: {
|
|
3314
|
+
type: "object",
|
|
3315
|
+
properties: { text: { type: "string" } },
|
|
3316
|
+
required: ["text"]
|
|
3317
|
+
},
|
|
3318
|
+
run: (args) => ({
|
|
3319
|
+
ok: true,
|
|
3320
|
+
echoed: args && args.text || "",
|
|
3321
|
+
// The proof: a server-side implementation could not say this truthfully.
|
|
3322
|
+
ran_on: "device"
|
|
3323
|
+
})
|
|
2077
3324
|
},
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
3325
|
+
{
|
|
3326
|
+
name: "device.ping",
|
|
3327
|
+
description: "Check that the device is reachable.",
|
|
3328
|
+
input_schema: { type: "object", properties: {} },
|
|
3329
|
+
run: () => ({ ok: true, ran_on: "device", at: (/* @__PURE__ */ new Date()).toISOString() })
|
|
2082
3330
|
}
|
|
2083
|
-
|
|
3331
|
+
]);
|
|
3332
|
+
}
|
|
3333
|
+
|
|
3334
|
+
// src/tools/sessionSync.ts
|
|
3335
|
+
init_registry();
|
|
3336
|
+
function deviceToolsActionForSession(next, prevTenant2) {
|
|
3337
|
+
const tenant = next.authenticated ? next.tenant ?? null : null;
|
|
3338
|
+
if (tenant && tenant !== prevTenant2) return { action: "load", tenant };
|
|
3339
|
+
if (!tenant && prevTenant2) return { action: "reset", tenant: null };
|
|
3340
|
+
return { action: "none", tenant: prevTenant2 };
|
|
3341
|
+
}
|
|
3342
|
+
var REAL_DEPS = {
|
|
3343
|
+
clear: clearDeviceTools,
|
|
3344
|
+
registerPlatform: registerPlatformDeviceTools,
|
|
3345
|
+
// `./loader` reaches react-native via the bundle manager; import it only when
|
|
3346
|
+
// an effect actually fires (real app), never at module load (tests).
|
|
3347
|
+
resetLoader: () => {
|
|
3348
|
+
void Promise.resolve().then(() => (init_loader(), loader_exports)).then((m) => m.resetDeviceToolLoader());
|
|
3349
|
+
},
|
|
3350
|
+
load: () => {
|
|
3351
|
+
void Promise.resolve().then(() => (init_loader(), loader_exports)).then((m) => {
|
|
3352
|
+
m.resetDeviceToolLoader();
|
|
3353
|
+
return m.ensureDeviceToolsLoaded({ force: true });
|
|
3354
|
+
});
|
|
3355
|
+
}
|
|
3356
|
+
};
|
|
3357
|
+
var prevTenant = null;
|
|
3358
|
+
function syncDeviceToolsForSession(next, deps = REAL_DEPS) {
|
|
3359
|
+
const { action, tenant } = deviceToolsActionForSession(next, prevTenant);
|
|
3360
|
+
prevTenant = tenant;
|
|
3361
|
+
if (action === "load") {
|
|
3362
|
+
deps.clear();
|
|
3363
|
+
deps.registerPlatform();
|
|
3364
|
+
deps.load();
|
|
3365
|
+
} else if (action === "reset") {
|
|
3366
|
+
deps.resetLoader();
|
|
3367
|
+
}
|
|
3368
|
+
return action;
|
|
3369
|
+
}
|
|
3370
|
+
function __resetSessionSyncForTest() {
|
|
3371
|
+
prevTenant = null;
|
|
3372
|
+
}
|
|
3373
|
+
|
|
3374
|
+
// src/tools/serverCalls.ts
|
|
3375
|
+
init_registry();
|
|
3376
|
+
init_loader();
|
|
3377
|
+
var postResult = null;
|
|
3378
|
+
function setDeviceToolResultPoster(fn) {
|
|
3379
|
+
postResult = fn;
|
|
3380
|
+
}
|
|
3381
|
+
function isDeviceToolCommand(cmd) {
|
|
3382
|
+
return !!cmd && cmd.kind === "device_tool" && typeof cmd.tool === "string" && !!cmd.correlationId;
|
|
3383
|
+
}
|
|
3384
|
+
async function handleServerDeviceToolCall(cmd) {
|
|
3385
|
+
if (!isDeviceToolCommand(cmd)) return;
|
|
3386
|
+
const { tool, args, correlationId } = cmd;
|
|
3387
|
+
if (!postResult) {
|
|
3388
|
+
console.warn("[deviceTools] server called a tool but no result poster is installed");
|
|
3389
|
+
return;
|
|
3390
|
+
}
|
|
3391
|
+
try {
|
|
3392
|
+
await ensureDeviceToolsLoaded();
|
|
3393
|
+
const outcome = await callDeviceTool(tool, args || {});
|
|
3394
|
+
console.log(`[deviceTools] server-called ${tool} \u2192 ${outcome.ok ? "ok" : outcome.reason}`);
|
|
3395
|
+
await postResult(
|
|
3396
|
+
correlationId,
|
|
3397
|
+
outcome.ok ? { ok: true, ...unwrap(outcome.result) } : void 0,
|
|
3398
|
+
outcome.ok ? void 0 : outcome.error
|
|
3399
|
+
);
|
|
3400
|
+
} catch (err) {
|
|
3401
|
+
await postResult(correlationId, void 0, err?.message || String(err)).catch(() => {
|
|
3402
|
+
});
|
|
3403
|
+
}
|
|
2084
3404
|
}
|
|
3405
|
+
function unwrap(result) {
|
|
3406
|
+
if (result && typeof result === "object" && !Array.isArray(result)) return result;
|
|
3407
|
+
return { result };
|
|
3408
|
+
}
|
|
3409
|
+
|
|
3410
|
+
// src/index.ts
|
|
3411
|
+
init_bundleManager();
|
|
3412
|
+
init_storage();
|
|
3413
|
+
init_native();
|
|
2085
3414
|
|
|
2086
3415
|
// src/hooks/usePlugin.ts
|
|
2087
3416
|
var import_react11 = require("react");
|
|
3417
|
+
init_bundleManager();
|
|
3418
|
+
init_host();
|
|
3419
|
+
init_pluginRuntime();
|
|
2088
3420
|
function extractBundleUrl(pluginId, manifest) {
|
|
2089
3421
|
const render = manifest.render;
|
|
2090
3422
|
if (!render || typeof render !== "object") return { url: null, source: "none" };
|
|
@@ -2122,6 +3454,8 @@ function usePlugin(pluginId, manifest) {
|
|
|
2122
3454
|
const bundleHash = extractBundleHash(manifest);
|
|
2123
3455
|
const componentId = extractComponentId(manifest);
|
|
2124
3456
|
const lookupId = componentId || pluginId;
|
|
3457
|
+
const grants = resolvePluginGrants({ pluginId, manifest });
|
|
3458
|
+
const grantsKey = JSON.stringify(grants);
|
|
2125
3459
|
if (ignoredUrl) {
|
|
2126
3460
|
console.warn(`[usePlugin] ${pluginId}: IGNORED connector-injected bundleUrl="${ignoredUrl}". The host derives bundle URLs from pluginId via the canonical route.`);
|
|
2127
3461
|
}
|
|
@@ -2144,7 +3478,7 @@ function usePlugin(pluginId, manifest) {
|
|
|
2144
3478
|
}
|
|
2145
3479
|
if (bundleUrl) {
|
|
2146
3480
|
try {
|
|
2147
|
-
const component = await bundleManager.load(pluginId, bundleUrl, version, bundleHash);
|
|
3481
|
+
const component = await bundleManager.load(pluginId, bundleUrl, version, bundleHash, grants);
|
|
2148
3482
|
if (cancelled) return;
|
|
2149
3483
|
setPlugin(component);
|
|
2150
3484
|
setLoading(false);
|
|
@@ -2181,13 +3515,13 @@ function usePlugin(pluginId, manifest) {
|
|
|
2181
3515
|
return () => {
|
|
2182
3516
|
cancelled = true;
|
|
2183
3517
|
};
|
|
2184
|
-
}, [pluginId, lookupId, bundleUrl, bundleHash, version]);
|
|
3518
|
+
}, [pluginId, lookupId, bundleUrl, bundleHash, version, grantsKey]);
|
|
2185
3519
|
return { plugin, loading, error, bundleMissing };
|
|
2186
3520
|
}
|
|
2187
3521
|
|
|
2188
3522
|
// src/hooks/useRemotePlugins.ts
|
|
2189
3523
|
var import_react12 = require("react");
|
|
2190
|
-
var
|
|
3524
|
+
var import_core11 = require("@ateam-ai-mobile/core");
|
|
2191
3525
|
function useRemotePlugins(solutionId) {
|
|
2192
3526
|
const [plugins, setPlugins] = (0, import_react12.useState)([]);
|
|
2193
3527
|
const [loading, setLoading] = (0, import_react12.useState)(true);
|
|
@@ -2199,7 +3533,7 @@ function useRemotePlugins(solutionId) {
|
|
|
2199
3533
|
const targetSolutionId = solutionId || "current";
|
|
2200
3534
|
let data;
|
|
2201
3535
|
try {
|
|
2202
|
-
data = await (0,
|
|
3536
|
+
data = await (0, import_core11.apiFetch)(`/api/solutions/${targetSolutionId}/ui-plugins`);
|
|
2203
3537
|
} catch (err) {
|
|
2204
3538
|
if (err?.message?.includes("404")) {
|
|
2205
3539
|
console.warn(`[useRemotePlugins] Solution ${targetSolutionId} not found or has no plugins`);
|
|
@@ -2255,108 +3589,14 @@ function useRemotePlugins(solutionId) {
|
|
|
2255
3589
|
};
|
|
2256
3590
|
}
|
|
2257
3591
|
|
|
2258
|
-
// src/
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
if (manifest.render?.mode === "react-native") return manifest.render.component;
|
|
2267
|
-
if (manifest.render?.mode === "adaptive") return manifest.render.reactNative?.component;
|
|
2268
|
-
return null;
|
|
2269
|
-
}
|
|
2270
|
-
function getNativeBundleUrl(manifest) {
|
|
2271
|
-
const direct = manifest.bundleUrl;
|
|
2272
|
-
if (direct) return direct;
|
|
2273
|
-
const render = manifest.render;
|
|
2274
|
-
if (render.mode === "react-native") return render.bundleUrl || null;
|
|
2275
|
-
if (render.mode === "adaptive") return render.reactNative?.bundleUrl || null;
|
|
2276
|
-
return null;
|
|
2277
|
-
}
|
|
2278
|
-
function getNativeBundleHash(manifest) {
|
|
2279
|
-
const direct = manifest.bundleHash;
|
|
2280
|
-
if (direct) return direct;
|
|
2281
|
-
const render = manifest.render;
|
|
2282
|
-
if (render.mode === "react-native") return render.bundleHash || null;
|
|
2283
|
-
if (render.mode === "adaptive") return render.reactNative?.bundleHash || null;
|
|
2284
|
-
return null;
|
|
2285
|
-
}
|
|
2286
|
-
function supportsWebView(manifest) {
|
|
2287
|
-
return manifest.render?.mode === "iframe" || manifest.render?.mode === "adaptive";
|
|
2288
|
-
}
|
|
2289
|
-
async function listPlugins() {
|
|
2290
|
-
const res = await (0, import_core9.apiFetch)("/mcp", {
|
|
2291
|
-
method: "POST",
|
|
2292
|
-
body: JSON.stringify({
|
|
2293
|
-
jsonrpc: "2.0",
|
|
2294
|
-
id: Date.now(),
|
|
2295
|
-
method: "tools/call",
|
|
2296
|
-
params: {
|
|
2297
|
-
name: "cp.listContextPlugins",
|
|
2298
|
-
arguments: {}
|
|
2299
|
-
}
|
|
2300
|
-
})
|
|
2301
|
-
});
|
|
2302
|
-
const structured = res.result?.structuredContent?.plugins;
|
|
2303
|
-
if (structured) return structured;
|
|
2304
|
-
const text = res.result?.content?.[0]?.text;
|
|
2305
|
-
if (!text) return [];
|
|
2306
|
-
try {
|
|
2307
|
-
const parsed = JSON.parse(text);
|
|
2308
|
-
return parsed.plugins || parsed || [];
|
|
2309
|
-
} catch {
|
|
2310
|
-
return [];
|
|
2311
|
-
}
|
|
2312
|
-
}
|
|
2313
|
-
async function getPlugin(pluginId) {
|
|
2314
|
-
const res = await (0, import_core9.apiFetch)("/mcp", {
|
|
2315
|
-
method: "POST",
|
|
2316
|
-
body: JSON.stringify({
|
|
2317
|
-
jsonrpc: "2.0",
|
|
2318
|
-
id: Date.now(),
|
|
2319
|
-
method: "tools/call",
|
|
2320
|
-
params: {
|
|
2321
|
-
name: "cp.getContextPlugin",
|
|
2322
|
-
arguments: { id: pluginId }
|
|
2323
|
-
// Backend expects { id }, NOT { pluginId }
|
|
2324
|
-
}
|
|
2325
|
-
})
|
|
2326
|
-
});
|
|
2327
|
-
if (res.result?.structuredContent) return res.result.structuredContent;
|
|
2328
|
-
const text = res.result?.content?.[0]?.text;
|
|
2329
|
-
if (!text) return null;
|
|
2330
|
-
try {
|
|
2331
|
-
const parsed = JSON.parse(text);
|
|
2332
|
-
if (parsed.error) {
|
|
2333
|
-
console.warn("[getPlugin] Backend error:", parsed.error);
|
|
2334
|
-
return null;
|
|
2335
|
-
}
|
|
2336
|
-
return parsed;
|
|
2337
|
-
} catch {
|
|
2338
|
-
return null;
|
|
2339
|
-
}
|
|
2340
|
-
}
|
|
2341
|
-
function resolvePluginUrl(manifest, tenant) {
|
|
2342
|
-
const render = manifest.render;
|
|
2343
|
-
let url;
|
|
2344
|
-
if (render?.mode === "iframe") {
|
|
2345
|
-
url = render.iframeUrl;
|
|
2346
|
-
} else if (render?.mode === "adaptive") {
|
|
2347
|
-
url = render.iframeUrl || render.iframe?.iframeUrl;
|
|
2348
|
-
}
|
|
2349
|
-
if (!url) return "";
|
|
2350
|
-
if (url.startsWith("http")) {
|
|
2351
|
-
const sep = url.includes("?") ? "&" : "?";
|
|
2352
|
-
return tenant ? `${url}${sep}tenant=${encodeURIComponent(tenant)}` : url;
|
|
2353
|
-
}
|
|
2354
|
-
if (tenant && url.startsWith("/mcp-ui/")) {
|
|
2355
|
-
const tenantUrl = url.replace("/mcp-ui/", `/mcp-ui/${encodeURIComponent(tenant)}/`);
|
|
2356
|
-
return `${(0, import_core10.getCoreApiUrl)()}${tenantUrl}`;
|
|
2357
|
-
}
|
|
2358
|
-
return `${(0, import_core10.getCoreApiUrl)()}${url}`;
|
|
2359
|
-
}
|
|
3592
|
+
// src/index.ts
|
|
3593
|
+
init_plugins();
|
|
3594
|
+
init_SurfaceDrawer();
|
|
3595
|
+
init_SurfaceCard();
|
|
3596
|
+
init_SurfaceFullscreen();
|
|
3597
|
+
init_AdaOrb();
|
|
3598
|
+
init_store();
|
|
3599
|
+
init_SurfaceDispatcher();
|
|
2360
3600
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2361
3601
|
0 && (module.exports = {
|
|
2362
3602
|
ActionButton,
|
|
@@ -2364,6 +3604,7 @@ function resolvePluginUrl(manifest, tenant) {
|
|
|
2364
3604
|
AdaOrb,
|
|
2365
3605
|
Animated,
|
|
2366
3606
|
Badge,
|
|
3607
|
+
CameraPreview,
|
|
2367
3608
|
Card,
|
|
2368
3609
|
EmptyState,
|
|
2369
3610
|
ErrorState,
|
|
@@ -2373,6 +3614,7 @@ function resolvePluginUrl(manifest, tenant) {
|
|
|
2373
3614
|
LoadingState,
|
|
2374
3615
|
MemoryBundleStorage,
|
|
2375
3616
|
Modal,
|
|
3617
|
+
PLATFORM_VOICE_CALLER,
|
|
2376
3618
|
Platform,
|
|
2377
3619
|
PluginSDK,
|
|
2378
3620
|
Pressable,
|
|
@@ -2391,30 +3633,61 @@ function resolvePluginUrl(manifest, tenant) {
|
|
|
2391
3633
|
TextInput,
|
|
2392
3634
|
TouchableOpacity,
|
|
2393
3635
|
View,
|
|
3636
|
+
__resetSessionSyncForTest,
|
|
2394
3637
|
bundleManager,
|
|
3638
|
+
callDeviceTool,
|
|
3639
|
+
clearDeviceTools,
|
|
2395
3640
|
createNativeBridge,
|
|
3641
|
+
deviceStateV2,
|
|
3642
|
+
deviceToolsActionForSession,
|
|
3643
|
+
ensureDeviceToolsLoaded,
|
|
3644
|
+
explainCapability,
|
|
3645
|
+
gateDeviceState,
|
|
2396
3646
|
gatePluginNative,
|
|
3647
|
+
gateVoice,
|
|
3648
|
+
getCameraPreviewProvider,
|
|
2397
3649
|
getHaptics,
|
|
2398
3650
|
getNativeBundleHash,
|
|
2399
3651
|
getNativeBundleUrl,
|
|
2400
3652
|
getNativeComponentId,
|
|
2401
3653
|
getPlugin,
|
|
2402
3654
|
getPluginNative,
|
|
3655
|
+
handleServerDeviceToolCall,
|
|
3656
|
+
hasDeviceTool,
|
|
3657
|
+
implementedStateDomains,
|
|
3658
|
+
implementedVoice,
|
|
3659
|
+
isActionable,
|
|
3660
|
+
isDeviceToolCommand,
|
|
2403
3661
|
isInlineSurface,
|
|
2404
3662
|
isMountSurface,
|
|
3663
|
+
listDeviceTools,
|
|
2405
3664
|
listPlugins,
|
|
2406
3665
|
nativePluginRegistry,
|
|
3666
|
+
neverObserved,
|
|
2407
3667
|
register,
|
|
2408
3668
|
registerBundleModules,
|
|
3669
|
+
registerDeviceTools,
|
|
2409
3670
|
registerNativePlugin,
|
|
3671
|
+
registerPlatformDeviceTools,
|
|
2410
3672
|
registerService,
|
|
3673
|
+
registerTools,
|
|
3674
|
+
resetDeviceToolLoader,
|
|
3675
|
+
resolvePluginGrants,
|
|
3676
|
+
resolvePluginRuntimeProps,
|
|
2411
3677
|
resolvePluginUrl,
|
|
2412
3678
|
setBundleStorage,
|
|
3679
|
+
setCameraPreviewProvider,
|
|
3680
|
+
setDeviceStateProvider,
|
|
3681
|
+
setDeviceToolContextProvider,
|
|
3682
|
+
setDeviceToolResultPoster,
|
|
2413
3683
|
setHapticsProvider,
|
|
2414
3684
|
setPluginNativeProvider,
|
|
2415
3685
|
setPluginThemeColors,
|
|
3686
|
+
setVoiceProvider,
|
|
2416
3687
|
supportsNative,
|
|
2417
3688
|
supportsWebView,
|
|
3689
|
+
syncDeviceToolsForSession,
|
|
3690
|
+
unregisterDeviceTools,
|
|
2418
3691
|
unregisterNativePlugin,
|
|
2419
3692
|
useApi,
|
|
2420
3693
|
useNativeBridge,
|