@ateam-ai-mobile/plugin-runtime 0.1.4 → 0.1.7
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-PKDQLNBL.mjs +2873 -0
- package/dist/chunk-PKDQLNBL.mjs.map +1 -0
- package/dist/index.d.mts +1759 -8
- package/dist/index.d.ts +1759 -8
- package/dist/index.js +2467 -1321
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +335 -2139
- package/dist/index.mjs.map +1 -1
- package/dist/loader-5LUVXUY2.mjs +9 -0
- package/dist/loader-5LUVXUY2.mjs.map +1 -0
- package/package.json +2 -2
- package/src/bundles/bundleManager.ts +368 -27
- package/src/bundles/integrity.ts +29 -0
- package/src/bundles/storage.ts +40 -0
- package/src/deviceStateHost.ts +197 -0
- package/src/hooks/usePlugin.ts +1 -1
- package/src/host.ts +80 -8
- package/src/index.ts +90 -1
- package/src/pluginRuntime.ts +97 -0
- package/src/sdk/CameraPreview.tsx +94 -0
- package/src/sdk/device-state.ts +1225 -0
- package/src/sdk/index.ts +35 -3
- package/src/sdk/registration.ts +39 -0
- package/src/sdk/types.ts +345 -8
- package/src/sdk/voice.ts +148 -0
- package/src/tools/loader.ts +167 -0
- package/src/tools/platformTools.ts +41 -0
- package/src/tools/registry.ts +183 -0
- package/src/tools/serverCalls.ts +78 -0
- package/src/tools/sessionSync.ts +107 -0
- package/src/voiceHost.ts +126 -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,270 @@ 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
|
+
// What this plugin DECLARED, carried on the object it already receives.
|
|
400
|
+
//
|
|
401
|
+
// It is how a COMPONENT can be capability-gated: <CameraPreview> is handed
|
|
402
|
+
// `native` and reads this, so showing the camera obeys the same flag as
|
|
403
|
+
// calling it. Without this the preview would be a second door to the camera
|
|
404
|
+
// with only one lock. Non-enumerable so it cannot be mistaken for an API.
|
|
405
|
+
};
|
|
406
|
+
Object.defineProperty(gated, "__declaredCapabilities", {
|
|
407
|
+
value: Object.freeze({ ...caps }),
|
|
408
|
+
enumerable: false,
|
|
409
|
+
writable: false
|
|
410
|
+
});
|
|
411
|
+
return gated;
|
|
412
|
+
}
|
|
413
|
+
function setCameraPreviewProvider(Component) {
|
|
414
|
+
_cameraPreview = Component;
|
|
415
|
+
}
|
|
416
|
+
function getCameraPreviewProvider() {
|
|
417
|
+
return _cameraPreview;
|
|
418
|
+
}
|
|
419
|
+
function registerNativePlugin(id, plugin) {
|
|
420
|
+
_nativeRegistry.set(id, plugin);
|
|
421
|
+
}
|
|
422
|
+
function unregisterNativePlugin(id) {
|
|
423
|
+
_nativeRegistry.delete(id);
|
|
424
|
+
}
|
|
425
|
+
function registerBundleModules(modules) {
|
|
426
|
+
for (const [name, mod] of Object.entries(modules)) {
|
|
427
|
+
_bundleModules.set(name, mod);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
function resolveBundleModule(name) {
|
|
431
|
+
return _bundleModules.get(name);
|
|
432
|
+
}
|
|
433
|
+
var _haptics, noopNative, _pluginNative, _cameraPreview, _nativeRegistry, nativePluginRegistry, _bundleModules;
|
|
434
|
+
var init_host = __esm({
|
|
435
|
+
"src/host.ts"() {
|
|
436
|
+
"use strict";
|
|
437
|
+
_haptics = null;
|
|
438
|
+
noopNative = {
|
|
439
|
+
camera: unavailable("camera"),
|
|
440
|
+
location: unavailable("location"),
|
|
441
|
+
storage: unavailable("storage"),
|
|
442
|
+
biometrics: unavailable("biometrics"),
|
|
443
|
+
biometricsV2: unavailable("biometricsV2"),
|
|
444
|
+
notifications: unavailable("notifications"),
|
|
445
|
+
haptics: { impact: () => {
|
|
446
|
+
}, notification: () => {
|
|
447
|
+
}, selection: () => {
|
|
448
|
+
}, error: () => {
|
|
449
|
+
} },
|
|
450
|
+
connectivity: { isOnline: () => true, onChange: () => () => {
|
|
451
|
+
} },
|
|
452
|
+
fileSystem: unavailable("fileSystem"),
|
|
453
|
+
cookies: unavailable("cookies"),
|
|
454
|
+
sharing: unavailable("sharing"),
|
|
455
|
+
scanner: unavailable("scanner"),
|
|
456
|
+
nfc: unavailable("nfc"),
|
|
457
|
+
bluetooth: unavailable("bluetooth"),
|
|
458
|
+
vision: unavailable("vision")
|
|
459
|
+
};
|
|
460
|
+
_pluginNative = noopNative;
|
|
461
|
+
_cameraPreview = null;
|
|
462
|
+
_nativeRegistry = /* @__PURE__ */ new Map();
|
|
463
|
+
nativePluginRegistry = {
|
|
464
|
+
has(id) {
|
|
465
|
+
return _nativeRegistry.has(id);
|
|
466
|
+
},
|
|
467
|
+
async get(id) {
|
|
468
|
+
const entry = _nativeRegistry.get(id);
|
|
469
|
+
if (!entry) return null;
|
|
470
|
+
if (typeof entry === "function") {
|
|
471
|
+
try {
|
|
472
|
+
return await entry();
|
|
473
|
+
} catch (err) {
|
|
474
|
+
console.warn(`[adas-plugins] lazy native plugin '${id}' failed to load:`, err);
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return entry;
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
_bundleModules = /* @__PURE__ */ new Map();
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// src/sdk/CameraPreview.tsx
|
|
486
|
+
function declaredCapabilities(native) {
|
|
487
|
+
return native && native.__declaredCapabilities || null;
|
|
488
|
+
}
|
|
489
|
+
function CameraPreview({ native, facing = "back", style, renderUnavailable }) {
|
|
490
|
+
const caps = declaredCapabilities(native);
|
|
491
|
+
if (caps && !caps.camera) {
|
|
492
|
+
return unavailable2(
|
|
493
|
+
"this plugin did not declare `capabilities: { camera: true }`, so it may not show the camera",
|
|
494
|
+
renderUnavailable,
|
|
495
|
+
style
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
const Provider = getCameraPreviewProvider();
|
|
499
|
+
if (!Provider) {
|
|
500
|
+
return unavailable2(
|
|
501
|
+
"this app does not provide a camera preview (the host has not called setCameraPreviewProvider)",
|
|
502
|
+
renderUnavailable,
|
|
503
|
+
style
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Provider, { facing, style, renderUnavailable });
|
|
507
|
+
}
|
|
508
|
+
function unavailable2(reason, render, style) {
|
|
509
|
+
if (render) return render(reason);
|
|
510
|
+
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
|
|
511
|
+
${reason}` }) });
|
|
512
|
+
}
|
|
513
|
+
var import_jsx_runtime;
|
|
514
|
+
var init_CameraPreview = __esm({
|
|
515
|
+
"src/sdk/CameraPreview.tsx"() {
|
|
516
|
+
"use strict";
|
|
517
|
+
init_primitives();
|
|
518
|
+
init_host();
|
|
519
|
+
import_jsx_runtime = require("react/jsx-runtime");
|
|
409
520
|
}
|
|
410
521
|
});
|
|
411
522
|
|
|
412
523
|
// 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
524
|
function setPluginThemeColors(colors, opts) {
|
|
440
525
|
_colors = { ...adaColors, ...colors };
|
|
441
526
|
if (opts?.isDark !== void 0) _isDark = opts.isDark;
|
|
@@ -478,9 +563,40 @@ function usePluginTheme() {
|
|
|
478
563
|
isDark
|
|
479
564
|
}), [colors, isDark]);
|
|
480
565
|
}
|
|
566
|
+
var import_react2, import_react_native3, import_core, adaColors, _colors, _isDark, _subscribers;
|
|
567
|
+
var init_usePluginTheme = __esm({
|
|
568
|
+
"src/sdk/usePluginTheme.ts"() {
|
|
569
|
+
"use strict";
|
|
570
|
+
import_react2 = require("react");
|
|
571
|
+
import_react_native3 = require("react-native");
|
|
572
|
+
import_core = require("@ateam-ai-mobile/core");
|
|
573
|
+
adaColors = {
|
|
574
|
+
bg: import_core.ada.paper,
|
|
575
|
+
surface: import_core.ada.paperSoft,
|
|
576
|
+
surfaceAlt: import_core.ada.paperDeep,
|
|
577
|
+
input: import_core.ada.paper,
|
|
578
|
+
border: import_core.ada.border,
|
|
579
|
+
text: import_core.ada.ink,
|
|
580
|
+
textSecondary: import_core.ada.inkSoft,
|
|
581
|
+
textMuted: import_core.ada.inkMuted,
|
|
582
|
+
accent: import_core.ada.orange,
|
|
583
|
+
accentSoft: import_core.ada.orangeSoft,
|
|
584
|
+
success: "#5A8A5C",
|
|
585
|
+
successSoft: "rgba(90,138,92,0.14)",
|
|
586
|
+
warning: "#D49A4E",
|
|
587
|
+
warningSoft: "rgba(212,154,78,0.16)",
|
|
588
|
+
error: "#C15545",
|
|
589
|
+
errorSoft: "rgba(193,85,69,0.14)",
|
|
590
|
+
purple: import_core.ada.orangeDeep,
|
|
591
|
+
purpleSoft: import_core.ada.orangeSoft
|
|
592
|
+
};
|
|
593
|
+
_colors = adaColors;
|
|
594
|
+
_isDark = null;
|
|
595
|
+
_subscribers = /* @__PURE__ */ new Set();
|
|
596
|
+
}
|
|
597
|
+
});
|
|
481
598
|
|
|
482
599
|
// src/sdk/useApi.ts
|
|
483
|
-
var import_react3 = require("react");
|
|
484
600
|
function useApi(bridge) {
|
|
485
601
|
return (0, import_react3.useMemo)(() => {
|
|
486
602
|
const apiFn = async (connectorId, tool, args = {}) => {
|
|
@@ -509,131 +625,635 @@ function useApi(bridge) {
|
|
|
509
625
|
return apiFn;
|
|
510
626
|
}, [bridge]);
|
|
511
627
|
}
|
|
628
|
+
var import_react3;
|
|
629
|
+
var init_useApi = __esm({
|
|
630
|
+
"src/sdk/useApi.ts"() {
|
|
631
|
+
"use strict";
|
|
632
|
+
import_react3 = require("react");
|
|
633
|
+
}
|
|
634
|
+
});
|
|
512
635
|
|
|
513
636
|
// 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
|
-
|
|
637
|
+
var sdk_exports = {};
|
|
638
|
+
__export(sdk_exports, {
|
|
639
|
+
ActionButton: () => ActionButton,
|
|
640
|
+
ActivityIndicator: () => import_react_native.ActivityIndicator,
|
|
641
|
+
Animated: () => import_react_native.Animated,
|
|
642
|
+
Badge: () => Badge,
|
|
643
|
+
CameraPreview: () => CameraPreview,
|
|
644
|
+
Card: () => Card,
|
|
645
|
+
EmptyState: () => EmptyState,
|
|
646
|
+
ErrorState: () => ErrorState,
|
|
647
|
+
FlatList: () => import_react_native.FlatList,
|
|
648
|
+
Image: () => import_react_native.Image,
|
|
649
|
+
ListItem: () => ListItem,
|
|
650
|
+
LoadingState: () => LoadingState,
|
|
651
|
+
Modal: () => import_react_native.Modal,
|
|
652
|
+
PLATFORM_VOICE_CALLER: () => PLATFORM_VOICE_CALLER,
|
|
653
|
+
Platform: () => import_react_native.Platform,
|
|
654
|
+
PluginSDK: () => PluginSDK,
|
|
655
|
+
Pressable: () => import_react_native.Pressable,
|
|
656
|
+
RefreshControl: () => import_react_native.RefreshControl,
|
|
657
|
+
ScrollView: () => import_react_native.ScrollView,
|
|
658
|
+
SearchBar: () => SearchBar,
|
|
659
|
+
SectionList: () => import_react_native.SectionList,
|
|
660
|
+
StyleSheet: () => import_react_native.StyleSheet,
|
|
661
|
+
Switch: () => import_react_native.Switch,
|
|
662
|
+
TabBar: () => TabBar,
|
|
663
|
+
Text: () => import_react_native.Text,
|
|
664
|
+
TextInput: () => import_react_native.TextInput,
|
|
665
|
+
TouchableOpacity: () => import_react_native.TouchableOpacity,
|
|
666
|
+
View: () => import_react_native.View,
|
|
667
|
+
register: () => register,
|
|
668
|
+
registerService: () => registerService,
|
|
669
|
+
registerTools: () => registerTools,
|
|
670
|
+
setPluginThemeColors: () => setPluginThemeColors,
|
|
671
|
+
useApi: () => useApi,
|
|
672
|
+
usePluginTheme: () => usePluginTheme
|
|
673
|
+
});
|
|
674
|
+
var PluginSDK;
|
|
675
|
+
var init_sdk = __esm({
|
|
676
|
+
"src/sdk/index.ts"() {
|
|
677
|
+
"use strict";
|
|
678
|
+
init_registration();
|
|
679
|
+
init_registration();
|
|
680
|
+
init_voice();
|
|
681
|
+
init_primitives();
|
|
682
|
+
init_components();
|
|
683
|
+
init_CameraPreview();
|
|
684
|
+
init_usePluginTheme();
|
|
685
|
+
init_useApi();
|
|
686
|
+
PluginSDK = { register, registerService, registerTools };
|
|
687
|
+
}
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
// src/deviceStateHost.ts
|
|
691
|
+
function deniedSnapshot(domain) {
|
|
692
|
+
const base = { quality: UNKNOWN_QUALITY };
|
|
693
|
+
switch (domain) {
|
|
694
|
+
case "spatial":
|
|
695
|
+
return {
|
|
696
|
+
...base,
|
|
697
|
+
position: null,
|
|
698
|
+
movement: { speedMps: null, bearingDeg: null, distanceRecentM: null, state: "unknown" },
|
|
699
|
+
orientation: { headingDeg: null },
|
|
700
|
+
recentPath: []
|
|
701
|
+
};
|
|
702
|
+
case "visual":
|
|
703
|
+
return {
|
|
704
|
+
...base,
|
|
705
|
+
camera: { available: false, active: false, facing: "unknown" },
|
|
706
|
+
latest: null,
|
|
707
|
+
recentTimeline: { windowMs: 0, frames: [] }
|
|
708
|
+
};
|
|
709
|
+
case "deviceContext":
|
|
710
|
+
return {
|
|
711
|
+
...base,
|
|
712
|
+
power: { batteryLevel: null, charging: null, lowPowerMode: null },
|
|
713
|
+
thermal: { state: "unknown" },
|
|
714
|
+
resources: { memoryPressure: "unknown", storagePressure: "unknown" }
|
|
715
|
+
};
|
|
716
|
+
case "connectivity":
|
|
717
|
+
return {
|
|
718
|
+
...base,
|
|
719
|
+
reachability: "unknown",
|
|
720
|
+
transport: "unknown",
|
|
721
|
+
qualityClass: "unknown",
|
|
722
|
+
stability: "unknown",
|
|
723
|
+
latencyMs: null
|
|
724
|
+
};
|
|
725
|
+
case "interaction":
|
|
726
|
+
return {
|
|
727
|
+
...base,
|
|
728
|
+
lifecycle: { appState: "unknown", screenOn: null },
|
|
729
|
+
audio: { captureActive: null, playbackActive: null, route: "unknown", interruption: "unknown" }
|
|
730
|
+
};
|
|
731
|
+
case "activity":
|
|
732
|
+
return {
|
|
733
|
+
...base,
|
|
734
|
+
// `confidence` is REQUIRED on both blocks in the freeze, precisely
|
|
735
|
+
// because an inferred activity must never be handed over without
|
|
736
|
+
// saying how sure the inference is. An empty snapshot is the least
|
|
737
|
+
// sure of all.
|
|
738
|
+
human: { state: "unknown", since: null, confidence: { level: "unknown" } },
|
|
739
|
+
devicePose: { placement: "unknown", orientation: "unknown", confidence: { level: "unknown" } },
|
|
740
|
+
recentTransitions: []
|
|
741
|
+
};
|
|
742
|
+
case "environment":
|
|
743
|
+
return {
|
|
744
|
+
...base,
|
|
745
|
+
lighting: { level: "unknown", lux: null },
|
|
746
|
+
acoustics: { level: "unknown", approximateDb: null },
|
|
747
|
+
// 'unknown' rather than 'uncertain': uncertain means we looked and
|
|
748
|
+
// could not decide, and nothing looked at all here.
|
|
749
|
+
enclosure: { state: "unknown" },
|
|
750
|
+
proximity: { distanceClass: "unknown" },
|
|
751
|
+
nearbySignals: { bleAvailable: false, beaconCount: null, uwbAvailable: false }
|
|
752
|
+
};
|
|
753
|
+
case "capability": {
|
|
754
|
+
const unknownStatus = { support: "unknown", permission: "unknown", availability: "unknown" };
|
|
755
|
+
return {
|
|
756
|
+
...base,
|
|
757
|
+
camera: unknownStatus,
|
|
758
|
+
microphone: unknownStatus,
|
|
759
|
+
location: { ...unknownStatus, precision: "unknown", backgroundPermission: "unknown" },
|
|
760
|
+
motion: unknownStatus,
|
|
761
|
+
bluetooth: unknownStatus,
|
|
762
|
+
uwb: unknownStatus,
|
|
763
|
+
nfc: unknownStatus,
|
|
764
|
+
notifications: unknownStatus,
|
|
765
|
+
health: unknownStatus,
|
|
766
|
+
backgroundExecution: unknownStatus
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
default:
|
|
770
|
+
return base;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
function deniedDomain(domain, why) {
|
|
567
774
|
return {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
nfc: caps.nfc ? native.nfc : blocked("nfc")
|
|
775
|
+
async getSnapshot() {
|
|
776
|
+
return deniedSnapshot(domain);
|
|
777
|
+
},
|
|
778
|
+
async start() {
|
|
779
|
+
throw denial(domain, why);
|
|
780
|
+
},
|
|
781
|
+
async stop() {
|
|
782
|
+
throw denial(domain, why);
|
|
783
|
+
},
|
|
784
|
+
subscribe() {
|
|
785
|
+
throw denial(domain, why);
|
|
786
|
+
}
|
|
581
787
|
};
|
|
582
788
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
789
|
+
function denial(domain, why) {
|
|
790
|
+
const err = new Error(`deviceState.${domain}: ${why}`);
|
|
791
|
+
err.code = "RESTRICTED";
|
|
792
|
+
err.domain = domain;
|
|
793
|
+
return err;
|
|
586
794
|
}
|
|
587
|
-
function
|
|
588
|
-
|
|
795
|
+
function setDeviceStateProvider(state) {
|
|
796
|
+
_provider = state || {};
|
|
589
797
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
798
|
+
function gateDeviceState(declared) {
|
|
799
|
+
const allowed = new Set(declared || []);
|
|
800
|
+
const out = {};
|
|
801
|
+
for (const domain of ALL_DOMAINS) {
|
|
802
|
+
if (!allowed.has(domain)) {
|
|
803
|
+
out[domain] = deniedDomain(
|
|
804
|
+
domain,
|
|
805
|
+
`not declared in this plugin's stateDomains. Add '${domain}' to use it.`
|
|
806
|
+
);
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
const engine = _provider[domain];
|
|
810
|
+
out[domain] = engine ?? deniedDomain(
|
|
811
|
+
domain,
|
|
812
|
+
"no engine in this build \u2014 the native side does not exist yet. See docs/DEVICE_API_STATUS.md."
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
return out;
|
|
816
|
+
}
|
|
817
|
+
function implementedStateDomains() {
|
|
818
|
+
return ALL_DOMAINS.filter((d) => !!_provider[d]);
|
|
819
|
+
}
|
|
820
|
+
var UNKNOWN_QUALITY, ALL_DOMAINS, _provider;
|
|
821
|
+
var init_deviceStateHost = __esm({
|
|
822
|
+
"src/deviceStateHost.ts"() {
|
|
823
|
+
"use strict";
|
|
824
|
+
UNKNOWN_QUALITY = {
|
|
825
|
+
freshness: { state: "unknown", observedAt: null, ageMs: null },
|
|
826
|
+
confidence: { level: "unknown" }
|
|
827
|
+
};
|
|
828
|
+
ALL_DOMAINS = [
|
|
829
|
+
"spatial",
|
|
830
|
+
"visual",
|
|
831
|
+
"activity",
|
|
832
|
+
"deviceContext",
|
|
833
|
+
"connectivity",
|
|
834
|
+
"capability",
|
|
835
|
+
"environment",
|
|
836
|
+
"interaction"
|
|
837
|
+
];
|
|
838
|
+
_provider = {};
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
// src/bridge/native.ts
|
|
843
|
+
function buildContext(pluginId, connectorId) {
|
|
844
|
+
const user = (0, import_core2.getUser)();
|
|
845
|
+
return {
|
|
846
|
+
tenant: (0, import_core2.getTenant)(),
|
|
847
|
+
pluginId,
|
|
848
|
+
connectorId,
|
|
849
|
+
user: user ? { id: user.actorId || "", name: user.name || "", roles: user.roles || [] } : void 0
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
function useNativeBridge(pluginId, connectorId, commandBufferRef) {
|
|
853
|
+
const initCallbackRef = (0, import_react4.useRef)(null);
|
|
854
|
+
const commandListenersRef = (0, import_react4.useRef)(/* @__PURE__ */ new Set());
|
|
855
|
+
const initializedRef = (0, import_react4.useRef)(false);
|
|
856
|
+
const fireInit = (0, import_react4.useCallback)(() => {
|
|
857
|
+
if (initializedRef.current || !initCallbackRef.current) return;
|
|
858
|
+
initCallbackRef.current(buildContext(pluginId, connectorId));
|
|
859
|
+
initializedRef.current = true;
|
|
860
|
+
}, [pluginId, connectorId]);
|
|
861
|
+
const onInit = (0, import_react4.useCallback)((cb) => {
|
|
862
|
+
initCallbackRef.current = cb;
|
|
863
|
+
setTimeout(fireInit, 0);
|
|
864
|
+
}, [fireInit]);
|
|
865
|
+
const mcpCall = (0, import_react4.useCallback)(async (toolOrConnector, argsOrTool, cIdOrArgs) => {
|
|
866
|
+
let tool;
|
|
867
|
+
let args;
|
|
868
|
+
let cId;
|
|
869
|
+
if (typeof argsOrTool === "string") {
|
|
870
|
+
cId = toolOrConnector;
|
|
871
|
+
tool = argsOrTool;
|
|
872
|
+
args = cIdOrArgs || {};
|
|
873
|
+
} else {
|
|
874
|
+
tool = toolOrConnector;
|
|
875
|
+
args = argsOrTool;
|
|
876
|
+
cId = typeof cIdOrArgs === "string" ? cIdOrArgs : void 0;
|
|
877
|
+
}
|
|
878
|
+
const targetConnector = cId || connectorId;
|
|
879
|
+
if (!targetConnector) {
|
|
880
|
+
console.warn(`[NativeBridge:${pluginId}] mcpCall failed \u2014 no connectorId. tool=${tool}`);
|
|
881
|
+
throw new Error(`[NativeBridge:${pluginId}] mcpCall requires a connectorId`);
|
|
882
|
+
}
|
|
883
|
+
try {
|
|
884
|
+
const data = await (0, import_core2.connectorCall)(targetConnector, tool, args);
|
|
885
|
+
if (!data.ok && data.error) {
|
|
886
|
+
throw new Error(data.error);
|
|
887
|
+
}
|
|
888
|
+
return (0, import_core2.unwrapMcpResult)(data.result);
|
|
889
|
+
} catch (err) {
|
|
890
|
+
console.warn(`[NativeBridge:${pluginId}] mcpCall failed \u2192 connector=${targetConnector} tool=${tool}:`, err?.message || err);
|
|
891
|
+
throw err;
|
|
892
|
+
}
|
|
893
|
+
}, [pluginId, connectorId]);
|
|
894
|
+
const mcpFetch = (0, import_react4.useCallback)(async (body) => {
|
|
895
|
+
return (0, import_core2.mcpFetch)(body);
|
|
896
|
+
}, []);
|
|
897
|
+
const sendCommand = (0, import_react4.useCallback)((type, payload) => {
|
|
898
|
+
if (type === "plugin.command.result" && payload?.correlationId) {
|
|
899
|
+
(0, import_core2.postCommandResult)(payload.correlationId, payload.result, payload.error);
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
}, [pluginId]);
|
|
903
|
+
const onCommand = (0, import_react4.useCallback)((cb) => {
|
|
904
|
+
commandListenersRef.current.add(cb);
|
|
905
|
+
if (commandBufferRef) {
|
|
906
|
+
const buf = commandBufferRef.current;
|
|
907
|
+
const remaining = [];
|
|
908
|
+
for (const cmd of buf) {
|
|
909
|
+
if (cmd.pluginId === pluginId && cmd.command !== "__focus") {
|
|
910
|
+
try {
|
|
911
|
+
cb(cmd.command, { ...cmd.args, correlationId: cmd.correlationId });
|
|
912
|
+
} catch {
|
|
913
|
+
}
|
|
914
|
+
} else {
|
|
915
|
+
remaining.push(cmd);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
commandBufferRef.current = remaining;
|
|
919
|
+
}
|
|
920
|
+
for (const cmd of import_core2.pluginCommandEmitter.drainPluginCommands(pluginId)) {
|
|
598
921
|
try {
|
|
599
|
-
|
|
922
|
+
cb(cmd.command, { ...cmd.args, correlationId: cmd.correlationId });
|
|
923
|
+
} catch {
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return () => {
|
|
927
|
+
commandListenersRef.current.delete(cb);
|
|
928
|
+
};
|
|
929
|
+
}, [pluginId, commandBufferRef]);
|
|
930
|
+
(0, import_react4.useEffect)(() => {
|
|
931
|
+
const unsub = import_core2.pluginCommandEmitter.on("plugin-command", (cmd) => {
|
|
932
|
+
if (cmd.pluginId !== pluginId) return;
|
|
933
|
+
if (cmd.command === "__focus") return;
|
|
934
|
+
commandListenersRef.current.forEach((fn) => {
|
|
935
|
+
try {
|
|
936
|
+
fn(cmd.command, { ...cmd.args, correlationId: cmd.correlationId });
|
|
937
|
+
} catch {
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
});
|
|
941
|
+
return unsub;
|
|
942
|
+
}, [pluginId]);
|
|
943
|
+
(0, import_react4.useEffect)(() => {
|
|
944
|
+
const timer = setTimeout(() => {
|
|
945
|
+
const dispatch = (cmd) => {
|
|
946
|
+
commandListenersRef.current.forEach((fn) => {
|
|
947
|
+
try {
|
|
948
|
+
fn(cmd.command, { ...cmd.args, correlationId: cmd.correlationId });
|
|
949
|
+
} catch {
|
|
950
|
+
}
|
|
951
|
+
});
|
|
952
|
+
};
|
|
953
|
+
if (commandBufferRef) {
|
|
954
|
+
const remaining = [];
|
|
955
|
+
for (const cmd of commandBufferRef.current) {
|
|
956
|
+
if (cmd.pluginId === pluginId && cmd.command !== "__focus") dispatch(cmd);
|
|
957
|
+
else remaining.push(cmd);
|
|
958
|
+
}
|
|
959
|
+
commandBufferRef.current = remaining;
|
|
960
|
+
}
|
|
961
|
+
for (const cmd of import_core2.pluginCommandEmitter.drainPluginCommands(pluginId)) {
|
|
962
|
+
dispatch(cmd);
|
|
963
|
+
}
|
|
964
|
+
}, 500);
|
|
965
|
+
return () => clearTimeout(timer);
|
|
966
|
+
}, [pluginId, commandBufferRef]);
|
|
967
|
+
(0, import_react4.useEffect)(() => {
|
|
968
|
+
return () => {
|
|
969
|
+
commandListenersRef.current.clear();
|
|
970
|
+
initializedRef.current = false;
|
|
971
|
+
};
|
|
972
|
+
}, []);
|
|
973
|
+
const close = (0, import_react4.useCallback)(() => {
|
|
974
|
+
import_core2.pluginCommandEmitter.emit("plugin-close", { pluginId });
|
|
975
|
+
}, [pluginId]);
|
|
976
|
+
const sendMessage = (0, import_react4.useCallback)((text) => {
|
|
977
|
+
const t = (text || "").trim();
|
|
978
|
+
if (!t) return;
|
|
979
|
+
try {
|
|
980
|
+
import_core2.useChatStore.getState().sendMessage(t);
|
|
981
|
+
} catch (err) {
|
|
982
|
+
console.warn("[bridge.sendMessage] failed:", err instanceof Error ? err.message : String(err));
|
|
983
|
+
}
|
|
984
|
+
}, []);
|
|
985
|
+
const prepareMessage = (0, import_react4.useCallback)((text) => {
|
|
986
|
+
const t = (text || "").trim();
|
|
987
|
+
if (!t) return;
|
|
988
|
+
import_core2.pluginCommandEmitter.emit("prepare-chat-message", { text: t });
|
|
989
|
+
}, []);
|
|
990
|
+
return (0, import_react4.useMemo)(
|
|
991
|
+
() => ({ onInit, mcpCall, mcpFetch, sendCommand, onCommand, close, sendMessage, prepareMessage }),
|
|
992
|
+
[onInit, mcpCall, mcpFetch, sendCommand, onCommand, close, sendMessage, prepareMessage]
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
function createNativeBridge(pluginId, connectorId) {
|
|
996
|
+
const commandListeners = /* @__PURE__ */ new Set();
|
|
997
|
+
return {
|
|
998
|
+
onInit(cb) {
|
|
999
|
+
setTimeout(() => {
|
|
1000
|
+
cb(buildContext(pluginId, connectorId));
|
|
1001
|
+
}, 0);
|
|
1002
|
+
},
|
|
1003
|
+
async mcpCall(tool, args, cId) {
|
|
1004
|
+
const targetConnector = cId || connectorId;
|
|
1005
|
+
if (!targetConnector) {
|
|
1006
|
+
throw new Error(`[NativeBridge:${pluginId}] mcpCall requires a connectorId`);
|
|
1007
|
+
}
|
|
1008
|
+
const data = await (0, import_core2.connectorCall)(targetConnector, tool, args);
|
|
1009
|
+
if (!data.ok && data.error) throw new Error(data.error);
|
|
1010
|
+
return (0, import_core2.unwrapMcpResult)(data.result);
|
|
1011
|
+
},
|
|
1012
|
+
async mcpFetch(body) {
|
|
1013
|
+
return (0, import_core2.mcpFetch)(body);
|
|
1014
|
+
},
|
|
1015
|
+
sendCommand(_type, _payload) {
|
|
1016
|
+
},
|
|
1017
|
+
onCommand(cb) {
|
|
1018
|
+
commandListeners.add(cb);
|
|
1019
|
+
return () => {
|
|
1020
|
+
commandListeners.delete(cb);
|
|
1021
|
+
};
|
|
1022
|
+
},
|
|
1023
|
+
close() {
|
|
1024
|
+
import_core2.pluginCommandEmitter.emit("plugin-close", { pluginId });
|
|
1025
|
+
},
|
|
1026
|
+
sendMessage(text) {
|
|
1027
|
+
const t = (text || "").trim();
|
|
1028
|
+
if (!t) return;
|
|
1029
|
+
try {
|
|
1030
|
+
import_core2.useChatStore.getState().sendMessage(t);
|
|
600
1031
|
} catch (err) {
|
|
601
|
-
console.warn(
|
|
602
|
-
return null;
|
|
1032
|
+
console.warn("[bridge.sendMessage] failed:", err instanceof Error ? err.message : String(err));
|
|
603
1033
|
}
|
|
1034
|
+
},
|
|
1035
|
+
prepareMessage(text) {
|
|
1036
|
+
const t = (text || "").trim();
|
|
1037
|
+
if (!t) return;
|
|
1038
|
+
import_core2.pluginCommandEmitter.emit("prepare-chat-message", { text: t });
|
|
604
1039
|
}
|
|
605
|
-
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
var import_react4, import_core2;
|
|
1043
|
+
var init_native = __esm({
|
|
1044
|
+
"src/bridge/native.ts"() {
|
|
1045
|
+
"use strict";
|
|
1046
|
+
import_react4 = require("react");
|
|
1047
|
+
import_core2 = require("@ateam-ai-mobile/core");
|
|
606
1048
|
}
|
|
607
|
-
};
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
1049
|
+
});
|
|
1050
|
+
|
|
1051
|
+
// src/tools/registry.ts
|
|
1052
|
+
function setDeviceToolContextProvider(fn) {
|
|
1053
|
+
contextProvider = fn;
|
|
1054
|
+
}
|
|
1055
|
+
function defaultContext(pluginId, capabilities, stateDomains) {
|
|
1056
|
+
return {
|
|
1057
|
+
bridge: createNativeBridge(pluginId),
|
|
1058
|
+
native: gatePluginNative(getPluginNative(), capabilities, pluginId),
|
|
1059
|
+
deviceState: gateDeviceState(stateDomains)
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
function registerDeviceTools(pluginId, tools, capabilities, stateDomains) {
|
|
1063
|
+
const accepted = [];
|
|
1064
|
+
for (const tool of tools || []) {
|
|
1065
|
+
if (!tool || typeof tool.name !== "string" || typeof tool.run !== "function") {
|
|
1066
|
+
console.warn(`[deviceTools] ${pluginId}: skipping malformed tool entry`);
|
|
1067
|
+
continue;
|
|
1068
|
+
}
|
|
1069
|
+
const existing = registry.get(tool.name);
|
|
1070
|
+
if (existing && existing.pluginId !== pluginId) {
|
|
1071
|
+
console.warn(
|
|
1072
|
+
`[deviceTools] ${pluginId}: "${tool.name}" already registered by ${existing.pluginId} \u2014 refusing to override`
|
|
1073
|
+
);
|
|
1074
|
+
continue;
|
|
1075
|
+
}
|
|
1076
|
+
registry.set(tool.name, { pluginId, tool, capabilities, stateDomains });
|
|
1077
|
+
accepted.push(tool.name);
|
|
612
1078
|
}
|
|
1079
|
+
if (accepted.length > 0) {
|
|
1080
|
+
console.log(`[deviceTools] ${pluginId} registered: ${accepted.join(", ")}`);
|
|
1081
|
+
}
|
|
1082
|
+
return accepted;
|
|
613
1083
|
}
|
|
614
|
-
function
|
|
615
|
-
|
|
1084
|
+
function unregisterDeviceTools(pluginId) {
|
|
1085
|
+
for (const [name, entry] of registry.entries()) {
|
|
1086
|
+
if (entry.pluginId === pluginId) registry.delete(name);
|
|
1087
|
+
}
|
|
616
1088
|
}
|
|
1089
|
+
function hasDeviceTool(name) {
|
|
1090
|
+
return registry.has(name);
|
|
1091
|
+
}
|
|
1092
|
+
function listDeviceTools() {
|
|
1093
|
+
return Array.from(registry.entries()).map(([name, { pluginId, tool }]) => ({
|
|
1094
|
+
name,
|
|
1095
|
+
pluginId,
|
|
1096
|
+
description: tool.description,
|
|
1097
|
+
input_schema: tool.input_schema
|
|
1098
|
+
}));
|
|
1099
|
+
}
|
|
1100
|
+
async function callDeviceTool(name, args = {}, opts = {}) {
|
|
1101
|
+
const entry = registry.get(name);
|
|
1102
|
+
if (!entry) {
|
|
1103
|
+
return { ok: false, reason: "not_found", error: `No device tool registered as "${name}"` };
|
|
1104
|
+
}
|
|
1105
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
1106
|
+
let timer;
|
|
1107
|
+
try {
|
|
1108
|
+
const build = contextProvider || defaultContext;
|
|
1109
|
+
const ctx = {
|
|
1110
|
+
...build(entry.pluginId, entry.capabilities, entry.stateDomains),
|
|
1111
|
+
signal: opts.signal
|
|
1112
|
+
};
|
|
1113
|
+
const result = await Promise.race([
|
|
1114
|
+
Promise.resolve(entry.tool.run(args, ctx)),
|
|
1115
|
+
new Promise((_, reject) => {
|
|
1116
|
+
timer = setTimeout(
|
|
1117
|
+
() => reject(new Error(`Device tool "${name}" timed out after ${timeoutMs}ms`)),
|
|
1118
|
+
timeoutMs
|
|
1119
|
+
);
|
|
1120
|
+
})
|
|
1121
|
+
]);
|
|
1122
|
+
return { ok: true, result };
|
|
1123
|
+
} catch (err) {
|
|
1124
|
+
const message = err?.message || String(err);
|
|
1125
|
+
const timedOut = message.includes("timed out after");
|
|
1126
|
+
return { ok: false, reason: timedOut ? "timeout" : "failed", error: message };
|
|
1127
|
+
} finally {
|
|
1128
|
+
if (timer) clearTimeout(timer);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
function clearDeviceTools() {
|
|
1132
|
+
registry.clear();
|
|
1133
|
+
}
|
|
1134
|
+
var registry, contextProvider, DEFAULT_TIMEOUT_MS;
|
|
1135
|
+
var init_registry = __esm({
|
|
1136
|
+
"src/tools/registry.ts"() {
|
|
1137
|
+
"use strict";
|
|
1138
|
+
init_native();
|
|
1139
|
+
init_host();
|
|
1140
|
+
init_deviceStateHost();
|
|
1141
|
+
registry = /* @__PURE__ */ new Map();
|
|
1142
|
+
contextProvider = null;
|
|
1143
|
+
DEFAULT_TIMEOUT_MS = 3e4;
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
617
1146
|
|
|
618
|
-
// src/
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
1147
|
+
// src/api/plugins.ts
|
|
1148
|
+
function supportsNative(manifest) {
|
|
1149
|
+
const mode = manifest.render?.mode;
|
|
1150
|
+
return mode === "react-native" || mode === "adaptive";
|
|
1151
|
+
}
|
|
1152
|
+
function getNativeComponentId(manifest) {
|
|
1153
|
+
if (manifest.render?.mode === "react-native") return manifest.render.component;
|
|
1154
|
+
if (manifest.render?.mode === "adaptive") return manifest.render.reactNative?.component;
|
|
1155
|
+
return null;
|
|
1156
|
+
}
|
|
1157
|
+
function getNativeBundleUrl(manifest) {
|
|
1158
|
+
const direct = manifest.bundleUrl;
|
|
1159
|
+
if (direct) return direct;
|
|
1160
|
+
const render = manifest.render;
|
|
1161
|
+
if (render.mode === "react-native") return render.bundleUrl || null;
|
|
1162
|
+
if (render.mode === "adaptive") return render.reactNative?.bundleUrl || null;
|
|
1163
|
+
return null;
|
|
1164
|
+
}
|
|
1165
|
+
function getNativeBundleHash(manifest) {
|
|
1166
|
+
const direct = manifest.bundleHash;
|
|
1167
|
+
if (direct) return direct;
|
|
1168
|
+
const render = manifest.render;
|
|
1169
|
+
if (render.mode === "react-native") return render.bundleHash || null;
|
|
1170
|
+
if (render.mode === "adaptive") return render.reactNative?.bundleHash || null;
|
|
1171
|
+
return null;
|
|
1172
|
+
}
|
|
1173
|
+
function supportsWebView(manifest) {
|
|
1174
|
+
return manifest.render?.mode === "iframe" || manifest.render?.mode === "adaptive";
|
|
1175
|
+
}
|
|
1176
|
+
async function listPlugins() {
|
|
1177
|
+
const res = await (0, import_core3.apiFetch)("/mcp", {
|
|
1178
|
+
method: "POST",
|
|
1179
|
+
body: JSON.stringify({
|
|
1180
|
+
jsonrpc: "2.0",
|
|
1181
|
+
id: Date.now(),
|
|
1182
|
+
method: "tools/call",
|
|
1183
|
+
params: {
|
|
1184
|
+
name: "cp.listContextPlugins",
|
|
1185
|
+
arguments: {}
|
|
1186
|
+
}
|
|
1187
|
+
})
|
|
1188
|
+
});
|
|
1189
|
+
const structured = res.result?.structuredContent?.plugins;
|
|
1190
|
+
if (structured) return structured;
|
|
1191
|
+
const text = res.result?.content?.[0]?.text;
|
|
1192
|
+
if (!text) return [];
|
|
1193
|
+
try {
|
|
1194
|
+
const parsed = JSON.parse(text);
|
|
1195
|
+
return parsed.plugins || parsed || [];
|
|
1196
|
+
} catch {
|
|
1197
|
+
return [];
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
async function getPlugin(pluginId) {
|
|
1201
|
+
const res = await (0, import_core3.apiFetch)("/mcp", {
|
|
1202
|
+
method: "POST",
|
|
1203
|
+
body: JSON.stringify({
|
|
1204
|
+
jsonrpc: "2.0",
|
|
1205
|
+
id: Date.now(),
|
|
1206
|
+
method: "tools/call",
|
|
1207
|
+
params: {
|
|
1208
|
+
name: "cp.getContextPlugin",
|
|
1209
|
+
arguments: { id: pluginId }
|
|
1210
|
+
// Backend expects { id }, NOT { pluginId }
|
|
1211
|
+
}
|
|
1212
|
+
})
|
|
1213
|
+
});
|
|
1214
|
+
if (res.result?.structuredContent) return res.result.structuredContent;
|
|
1215
|
+
const text = res.result?.content?.[0]?.text;
|
|
1216
|
+
if (!text) return null;
|
|
1217
|
+
try {
|
|
1218
|
+
const parsed = JSON.parse(text);
|
|
1219
|
+
if (parsed.error) {
|
|
1220
|
+
console.warn("[getPlugin] Backend error:", parsed.error);
|
|
1221
|
+
return null;
|
|
1222
|
+
}
|
|
1223
|
+
return parsed;
|
|
1224
|
+
} catch {
|
|
1225
|
+
return null;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
function resolvePluginUrl(manifest, tenant) {
|
|
1229
|
+
const render = manifest.render;
|
|
1230
|
+
let url;
|
|
1231
|
+
if (render?.mode === "iframe") {
|
|
1232
|
+
url = render.iframeUrl;
|
|
1233
|
+
} else if (render?.mode === "adaptive") {
|
|
1234
|
+
url = render.iframeUrl || render.iframe?.iframeUrl;
|
|
1235
|
+
}
|
|
1236
|
+
if (!url) return "";
|
|
1237
|
+
if (url.startsWith("http")) {
|
|
1238
|
+
const sep = url.includes("?") ? "&" : "?";
|
|
1239
|
+
return tenant ? `${url}${sep}tenant=${encodeURIComponent(tenant)}` : url;
|
|
1240
|
+
}
|
|
1241
|
+
if (tenant && url.startsWith("/mcp-ui/")) {
|
|
1242
|
+
const tenantUrl = url.replace("/mcp-ui/", `/mcp-ui/${encodeURIComponent(tenant)}/`);
|
|
1243
|
+
return `${(0, import_core4.getCoreApiUrl)()}${tenantUrl}`;
|
|
1244
|
+
}
|
|
1245
|
+
return `${(0, import_core4.getCoreApiUrl)()}${url}`;
|
|
1246
|
+
}
|
|
1247
|
+
var import_core3, import_core4;
|
|
1248
|
+
var init_plugins = __esm({
|
|
1249
|
+
"src/api/plugins.ts"() {
|
|
1250
|
+
"use strict";
|
|
1251
|
+
import_core3 = require("@ateam-ai-mobile/core");
|
|
1252
|
+
import_core4 = require("@ateam-ai-mobile/core");
|
|
1253
|
+
}
|
|
1254
|
+
});
|
|
629
1255
|
|
|
630
1256
|
// 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
1257
|
function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
638
1258
|
const showInscription = inscription && size >= 60;
|
|
639
1259
|
const center = size / 2;
|
|
@@ -649,7 +1269,7 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
649
1269
|
const orbit2 = (0, import_react_native_reanimated.useSharedValue)(0);
|
|
650
1270
|
const orbit3 = (0, import_react_native_reanimated.useSharedValue)(0);
|
|
651
1271
|
const beat = (0, import_react_native_reanimated.useSharedValue)(1);
|
|
652
|
-
(0,
|
|
1272
|
+
(0, import_react5.useEffect)(() => {
|
|
653
1273
|
(0, import_react_native_reanimated.cancelAnimation)(coreScale);
|
|
654
1274
|
(0, import_react_native_reanimated.cancelAnimation)(innerScale);
|
|
655
1275
|
(0, import_react_native_reanimated.cancelAnimation)(outerScale);
|
|
@@ -799,29 +1419,29 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
799
1419
|
opacity: state === "thinking" ? 0.7 : 0
|
|
800
1420
|
};
|
|
801
1421
|
});
|
|
802
|
-
return /* @__PURE__ */ (0,
|
|
803
|
-
/* @__PURE__ */ (0,
|
|
804
|
-
/* @__PURE__ */ (0,
|
|
805
|
-
/* @__PURE__ */ (0,
|
|
806
|
-
/* @__PURE__ */ (0,
|
|
807
|
-
/* @__PURE__ */ (0,
|
|
808
|
-
/* @__PURE__ */ (0,
|
|
1422
|
+
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: [
|
|
1423
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native_svg.Defs, { children: [
|
|
1424
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native_svg.RadialGradient, { id: "adaCore", cx: "36%", cy: "30%", r: "70%", children: [
|
|
1425
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "0%", stopColor: "#FFE0B0", stopOpacity: "1" }),
|
|
1426
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "28%", stopColor: "#FFA85A", stopOpacity: "1" }),
|
|
1427
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "65%", stopColor: "#FF7A28", stopOpacity: "1" }),
|
|
1428
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "100%", stopColor: "#B8410A", stopOpacity: "1" })
|
|
809
1429
|
] }),
|
|
810
|
-
/* @__PURE__ */ (0,
|
|
811
|
-
/* @__PURE__ */ (0,
|
|
812
|
-
/* @__PURE__ */ (0,
|
|
1430
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native_svg.RadialGradient, { id: "adaInner", cx: "50%", cy: "50%", r: "50%", children: [
|
|
1431
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "0%", stopColor: "#FF8A3C", stopOpacity: "0.55" }),
|
|
1432
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "100%", stopColor: "#FF7028", stopOpacity: "0" })
|
|
813
1433
|
] }),
|
|
814
|
-
/* @__PURE__ */ (0,
|
|
815
|
-
/* @__PURE__ */ (0,
|
|
816
|
-
/* @__PURE__ */ (0,
|
|
1434
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native_svg.RadialGradient, { id: "adaOuter", cx: "50%", cy: "50%", r: "50%", children: [
|
|
1435
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "0%", stopColor: "#FF7A28", stopOpacity: "0.4" }),
|
|
1436
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "100%", stopColor: "#FF6018", stopOpacity: "0" })
|
|
817
1437
|
] }),
|
|
818
|
-
/* @__PURE__ */ (0,
|
|
819
|
-
/* @__PURE__ */ (0,
|
|
820
|
-
/* @__PURE__ */ (0,
|
|
821
|
-
/* @__PURE__ */ (0,
|
|
1438
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native_svg.RadialGradient, { id: "adaSpark", cx: "50%", cy: "50%", r: "50%", children: [
|
|
1439
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "0%", stopColor: "#FFFAEB", stopOpacity: "1" }),
|
|
1440
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "60%", stopColor: "#FFD095", stopOpacity: "0.9" }),
|
|
1441
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Stop, { offset: "100%", stopColor: "#FFA85A", stopOpacity: "0" })
|
|
822
1442
|
] })
|
|
823
1443
|
] }),
|
|
824
|
-
/* @__PURE__ */ (0,
|
|
1444
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
825
1445
|
AnimatedCircle,
|
|
826
1446
|
{
|
|
827
1447
|
cx: center,
|
|
@@ -832,7 +1452,7 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
832
1452
|
animatedProps: ripple1Props
|
|
833
1453
|
}
|
|
834
1454
|
),
|
|
835
|
-
/* @__PURE__ */ (0,
|
|
1455
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
836
1456
|
AnimatedCircle,
|
|
837
1457
|
{
|
|
838
1458
|
cx: center,
|
|
@@ -843,7 +1463,7 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
843
1463
|
animatedProps: ripple2Props
|
|
844
1464
|
}
|
|
845
1465
|
),
|
|
846
|
-
/* @__PURE__ */ (0,
|
|
1466
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
847
1467
|
AnimatedCircle,
|
|
848
1468
|
{
|
|
849
1469
|
cx: center,
|
|
@@ -854,10 +1474,10 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
854
1474
|
animatedProps: ripple3Props
|
|
855
1475
|
}
|
|
856
1476
|
),
|
|
857
|
-
/* @__PURE__ */ (0,
|
|
858
|
-
/* @__PURE__ */ (0,
|
|
859
|
-
/* @__PURE__ */ (0,
|
|
860
|
-
/* @__PURE__ */ (0,
|
|
1477
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AnimatedCircle, { cx: center, cy: center, fill: "url(#adaOuter)", animatedProps: outerProps }),
|
|
1478
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AnimatedCircle, { cx: center, cy: center, fill: "url(#adaInner)", animatedProps: innerProps }),
|
|
1479
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AnimatedCircle, { cx: center, cy: center, fill: "url(#adaCore)", animatedProps: coreProps }),
|
|
1480
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
861
1481
|
AnimatedCircle,
|
|
862
1482
|
{
|
|
863
1483
|
r: Math.max(3, size * 0.05),
|
|
@@ -865,7 +1485,7 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
865
1485
|
animatedProps: spark1Props
|
|
866
1486
|
}
|
|
867
1487
|
),
|
|
868
|
-
/* @__PURE__ */ (0,
|
|
1488
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
869
1489
|
AnimatedCircle,
|
|
870
1490
|
{
|
|
871
1491
|
r: Math.max(2.5, size * 0.04),
|
|
@@ -873,7 +1493,7 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
873
1493
|
animatedProps: spark2Props
|
|
874
1494
|
}
|
|
875
1495
|
),
|
|
876
|
-
/* @__PURE__ */ (0,
|
|
1496
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
877
1497
|
AnimatedCircle,
|
|
878
1498
|
{
|
|
879
1499
|
r: Math.max(2, size * 0.032),
|
|
@@ -881,7 +1501,7 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
881
1501
|
animatedProps: spark3Props
|
|
882
1502
|
}
|
|
883
1503
|
),
|
|
884
|
-
showInscription && /* @__PURE__ */ (0,
|
|
1504
|
+
showInscription && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
885
1505
|
import_react_native_svg.Text,
|
|
886
1506
|
{
|
|
887
1507
|
x: center,
|
|
@@ -896,17 +1516,26 @@ function AdaOrb({ size = 120, state = "idle", inscription = true }) {
|
|
|
896
1516
|
)
|
|
897
1517
|
] }) });
|
|
898
1518
|
}
|
|
899
|
-
var
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
1519
|
+
var import_react5, import_react_native4, import_react_native_reanimated, import_react_native_svg, import_jsx_runtime2, AnimatedCircle, styles2;
|
|
1520
|
+
var init_AdaOrb = __esm({
|
|
1521
|
+
"src/surfaces/AdaOrb.tsx"() {
|
|
1522
|
+
"use strict";
|
|
1523
|
+
import_react5 = require("react");
|
|
1524
|
+
import_react_native4 = require("react-native");
|
|
1525
|
+
import_react_native_reanimated = __toESM(require("react-native-reanimated"));
|
|
1526
|
+
import_react_native_svg = __toESM(require("react-native-svg"));
|
|
1527
|
+
import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1528
|
+
AnimatedCircle = import_react_native_reanimated.default.createAnimatedComponent(import_react_native_svg.Circle);
|
|
1529
|
+
styles2 = import_react_native4.StyleSheet.create({
|
|
1530
|
+
wrap: {
|
|
1531
|
+
alignItems: "center",
|
|
1532
|
+
justifyContent: "center"
|
|
1533
|
+
}
|
|
1534
|
+
});
|
|
903
1535
|
}
|
|
904
1536
|
});
|
|
905
1537
|
|
|
906
1538
|
// 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
1539
|
function SurfaceDrawer({
|
|
911
1540
|
visible,
|
|
912
1541
|
onClose,
|
|
@@ -918,9 +1547,9 @@ function SurfaceDrawer({
|
|
|
918
1547
|
children
|
|
919
1548
|
}) {
|
|
920
1549
|
const sheetH = Math.round(SCREEN_H * heightRatio);
|
|
921
|
-
const fadeAnim = (0,
|
|
922
|
-
const slideAnim = (0,
|
|
923
|
-
(0,
|
|
1550
|
+
const fadeAnim = (0, import_react6.useRef)(new import_react_native5.Animated.Value(0)).current;
|
|
1551
|
+
const slideAnim = (0, import_react6.useRef)(new import_react_native5.Animated.Value(sheetH)).current;
|
|
1552
|
+
(0, import_react6.useEffect)(() => {
|
|
924
1553
|
if (visible) {
|
|
925
1554
|
import_react_native5.Animated.parallel([
|
|
926
1555
|
import_react_native5.Animated.timing(fadeAnim, { toValue: 1, duration: 220, useNativeDriver: true }),
|
|
@@ -938,9 +1567,9 @@ function SurfaceDrawer({
|
|
|
938
1567
|
]).start(() => onClose());
|
|
939
1568
|
};
|
|
940
1569
|
if (!visible) return null;
|
|
941
|
-
return /* @__PURE__ */ (0,
|
|
942
|
-
/* @__PURE__ */ (0,
|
|
943
|
-
/* @__PURE__ */ (0,
|
|
1570
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native5.Animated.View, { style: [styles3.overlay, { opacity: fadeAnim }], pointerEvents: "auto", children: [
|
|
1571
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native5.Pressable, { style: import_react_native5.StyleSheet.absoluteFill, onPress: close }),
|
|
1572
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
944
1573
|
import_react_native5.Animated.View,
|
|
945
1574
|
{
|
|
946
1575
|
style: [
|
|
@@ -948,21 +1577,21 @@ function SurfaceDrawer({
|
|
|
948
1577
|
{ height: sheetH, transform: [{ translateY: slideAnim }] }
|
|
949
1578
|
],
|
|
950
1579
|
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,
|
|
1580
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native5.View, { style: styles3.handle }),
|
|
1581
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native5.View, { style: styles3.header, children: [
|
|
1582
|
+
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,
|
|
1583
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native5.View, { style: { flex: 1 }, children: [
|
|
1584
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native5.Text, { style: styles3.title, children: title }),
|
|
1585
|
+
subtitle ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native5.Text, { style: styles3.subtitle, children: subtitle }) : null,
|
|
1586
|
+
privacy ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native5.View, { style: styles3.privacyPill, children: [
|
|
1587
|
+
/* @__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: [
|
|
1588
|
+
/* @__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" }),
|
|
1589
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_svg2.Path, { d: "M7 11V7a5 5 0 0110 0v4" })
|
|
961
1590
|
] }),
|
|
962
|
-
/* @__PURE__ */ (0,
|
|
1591
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native5.Text, { style: styles3.privacyText, children: "Only you can see this" })
|
|
963
1592
|
] }) : null
|
|
964
1593
|
] }),
|
|
965
|
-
/* @__PURE__ */ (0,
|
|
1594
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
966
1595
|
import_react_native5.Pressable,
|
|
967
1596
|
{
|
|
968
1597
|
accessibilityRole: "button",
|
|
@@ -970,105 +1599,115 @@ function SurfaceDrawer({
|
|
|
970
1599
|
style: styles3.closeBtn,
|
|
971
1600
|
onPress: close,
|
|
972
1601
|
hitSlop: 12,
|
|
973
|
-
children: /* @__PURE__ */ (0,
|
|
1602
|
+
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
1603
|
}
|
|
975
1604
|
)
|
|
976
1605
|
] }),
|
|
977
|
-
/* @__PURE__ */ (0,
|
|
1606
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native5.View, { style: styles3.body, children })
|
|
978
1607
|
]
|
|
979
1608
|
}
|
|
980
1609
|
)
|
|
981
1610
|
] });
|
|
982
1611
|
}
|
|
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
|
-
|
|
1612
|
+
var import_react6, import_react_native5, import_react_native_svg2, import_core5, import_jsx_runtime3, SCREEN_H, fontFam, styles3;
|
|
1613
|
+
var init_SurfaceDrawer = __esm({
|
|
1614
|
+
"src/surfaces/SurfaceDrawer.tsx"() {
|
|
1615
|
+
"use strict";
|
|
1616
|
+
import_react6 = require("react");
|
|
1617
|
+
import_react_native5 = require("react-native");
|
|
1618
|
+
import_react_native_svg2 = __toESM(require("react-native-svg"));
|
|
1619
|
+
import_core5 = require("@ateam-ai-mobile/core");
|
|
1620
|
+
init_AdaOrb();
|
|
1621
|
+
import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1622
|
+
SCREEN_H = import_react_native5.Dimensions.get("window").height;
|
|
1623
|
+
fontFam = import_react_native5.Platform.OS === "ios" ? "SF Pro Rounded" : void 0;
|
|
1624
|
+
styles3 = import_react_native5.StyleSheet.create({
|
|
1625
|
+
overlay: {
|
|
1626
|
+
position: "absolute",
|
|
1627
|
+
top: 0,
|
|
1628
|
+
left: 0,
|
|
1629
|
+
right: 0,
|
|
1630
|
+
bottom: 0,
|
|
1631
|
+
backgroundColor: "rgba(26, 20, 16, 0.35)",
|
|
1632
|
+
justifyContent: "flex-end",
|
|
1633
|
+
zIndex: 999,
|
|
1634
|
+
elevation: 999
|
|
1635
|
+
},
|
|
1636
|
+
sheet: {
|
|
1637
|
+
backgroundColor: import_core5.ada.paper,
|
|
1638
|
+
borderTopLeftRadius: 28,
|
|
1639
|
+
borderTopRightRadius: 28,
|
|
1640
|
+
overflow: "hidden"
|
|
1641
|
+
},
|
|
1642
|
+
handle: {
|
|
1643
|
+
width: 40,
|
|
1644
|
+
height: 4,
|
|
1645
|
+
borderRadius: 2,
|
|
1646
|
+
backgroundColor: import_core5.ada.inkMuted,
|
|
1647
|
+
opacity: 0.3,
|
|
1648
|
+
alignSelf: "center",
|
|
1649
|
+
marginTop: 10,
|
|
1650
|
+
marginBottom: 6
|
|
1651
|
+
},
|
|
1652
|
+
header: {
|
|
1653
|
+
flexDirection: "row",
|
|
1654
|
+
alignItems: "center",
|
|
1655
|
+
paddingHorizontal: 22,
|
|
1656
|
+
paddingVertical: 16,
|
|
1657
|
+
gap: 12,
|
|
1658
|
+
borderBottomWidth: 1,
|
|
1659
|
+
borderBottomColor: import_core5.ada.border
|
|
1660
|
+
},
|
|
1661
|
+
headerOrb: {
|
|
1662
|
+
width: 36,
|
|
1663
|
+
height: 36,
|
|
1664
|
+
alignItems: "center",
|
|
1665
|
+
justifyContent: "center"
|
|
1666
|
+
},
|
|
1667
|
+
title: {
|
|
1668
|
+
fontSize: 17,
|
|
1669
|
+
fontWeight: "600",
|
|
1670
|
+
color: import_core5.ada.ink,
|
|
1671
|
+
letterSpacing: -0.2,
|
|
1672
|
+
fontFamily: fontFam
|
|
1673
|
+
},
|
|
1674
|
+
subtitle: {
|
|
1675
|
+
fontSize: 13,
|
|
1676
|
+
fontWeight: "400",
|
|
1677
|
+
color: import_core5.ada.inkSoft,
|
|
1678
|
+
marginTop: 2,
|
|
1679
|
+
letterSpacing: -0.1,
|
|
1680
|
+
fontFamily: fontFam
|
|
1681
|
+
},
|
|
1682
|
+
privacyPill: {
|
|
1683
|
+
flexDirection: "row",
|
|
1684
|
+
alignItems: "center",
|
|
1685
|
+
gap: 5,
|
|
1686
|
+
marginTop: 4
|
|
1687
|
+
},
|
|
1688
|
+
privacyText: {
|
|
1689
|
+
fontSize: 11,
|
|
1690
|
+
fontWeight: "500",
|
|
1691
|
+
color: import_core5.ada.inkSoft,
|
|
1692
|
+
letterSpacing: 0.2,
|
|
1693
|
+
fontFamily: fontFam
|
|
1694
|
+
},
|
|
1695
|
+
closeBtn: {
|
|
1696
|
+
width: 32,
|
|
1697
|
+
height: 32,
|
|
1698
|
+
borderRadius: 16,
|
|
1699
|
+
backgroundColor: import_core5.ada.paperSoft,
|
|
1700
|
+
justifyContent: "center",
|
|
1701
|
+
alignItems: "center"
|
|
1702
|
+
},
|
|
1703
|
+
body: {
|
|
1704
|
+
flex: 1
|
|
1705
|
+
}
|
|
1706
|
+
});
|
|
1064
1707
|
}
|
|
1065
1708
|
});
|
|
1066
1709
|
|
|
1067
1710
|
// 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
1711
|
function SurfaceCard({
|
|
1073
1712
|
icon,
|
|
1074
1713
|
title,
|
|
@@ -1089,7 +1728,7 @@ function SurfaceCard({
|
|
|
1089
1728
|
getHaptics()?.impact("medium");
|
|
1090
1729
|
onLongPress();
|
|
1091
1730
|
};
|
|
1092
|
-
return /* @__PURE__ */ (0,
|
|
1731
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1093
1732
|
import_react_native6.Pressable,
|
|
1094
1733
|
{
|
|
1095
1734
|
accessibilityRole: "button",
|
|
@@ -1100,71 +1739,76 @@ function SurfaceCard({
|
|
|
1100
1739
|
onLongPress: onLongPress ? handleLongPress : void 0,
|
|
1101
1740
|
disabled: !onPress && !onLongPress,
|
|
1102
1741
|
children: [
|
|
1103
|
-
/* @__PURE__ */ (0,
|
|
1104
|
-
icon ? /* @__PURE__ */ (0,
|
|
1105
|
-
/* @__PURE__ */ (0,
|
|
1106
|
-
/* @__PURE__ */ (0,
|
|
1107
|
-
subtitle ? /* @__PURE__ */ (0,
|
|
1742
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native6.View, { style: styles4.row, children: [
|
|
1743
|
+
icon ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native6.Text, { style: styles4.icon, children: icon }) : null,
|
|
1744
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native6.View, { style: { flex: 1 }, children: [
|
|
1745
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native6.Text, { style: styles4.title, children: title }),
|
|
1746
|
+
subtitle ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native6.Text, { style: styles4.subtitle, children: subtitle }) : null
|
|
1108
1747
|
] }),
|
|
1109
|
-
arrow ? /* @__PURE__ */ (0,
|
|
1748
|
+
arrow ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native6.Text, { style: styles4.arrow, children: "\u2192" }) : null
|
|
1110
1749
|
] }),
|
|
1111
|
-
children ? /* @__PURE__ */ (0,
|
|
1750
|
+
children ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native6.View, { style: styles4.body, children }) : null
|
|
1112
1751
|
]
|
|
1113
1752
|
}
|
|
1114
1753
|
);
|
|
1115
1754
|
}
|
|
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
|
-
|
|
1755
|
+
var import_react_native6, import_core6, import_jsx_runtime4, fontFam2, styles4;
|
|
1756
|
+
var init_SurfaceCard = __esm({
|
|
1757
|
+
"src/surfaces/SurfaceCard.tsx"() {
|
|
1758
|
+
"use strict";
|
|
1759
|
+
import_react_native6 = require("react-native");
|
|
1760
|
+
init_host();
|
|
1761
|
+
import_core6 = require("@ateam-ai-mobile/core");
|
|
1762
|
+
import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1763
|
+
fontFam2 = import_react_native6.Platform.OS === "ios" ? "SF Pro Rounded" : void 0;
|
|
1764
|
+
styles4 = import_react_native6.StyleSheet.create({
|
|
1765
|
+
card: {
|
|
1766
|
+
backgroundColor: import_core6.ada.paperSoft,
|
|
1767
|
+
borderWidth: 1,
|
|
1768
|
+
borderColor: import_core6.ada.border,
|
|
1769
|
+
borderRadius: import_core6.borderRadius.xl,
|
|
1770
|
+
paddingHorizontal: 16,
|
|
1771
|
+
paddingVertical: 14
|
|
1772
|
+
},
|
|
1773
|
+
row: {
|
|
1774
|
+
flexDirection: "row",
|
|
1775
|
+
alignItems: "center",
|
|
1776
|
+
gap: 14
|
|
1777
|
+
},
|
|
1778
|
+
icon: {
|
|
1779
|
+
fontSize: 22
|
|
1780
|
+
},
|
|
1781
|
+
title: {
|
|
1782
|
+
fontSize: 15,
|
|
1783
|
+
fontWeight: "600",
|
|
1784
|
+
color: import_core6.ada.ink,
|
|
1785
|
+
letterSpacing: -0.2,
|
|
1786
|
+
fontFamily: fontFam2
|
|
1787
|
+
},
|
|
1788
|
+
subtitle: {
|
|
1789
|
+
fontSize: 13,
|
|
1790
|
+
fontWeight: "400",
|
|
1791
|
+
color: import_core6.ada.inkMuted,
|
|
1792
|
+
marginTop: 2,
|
|
1793
|
+
letterSpacing: -0.1,
|
|
1794
|
+
fontFamily: fontFam2
|
|
1795
|
+
},
|
|
1796
|
+
arrow: {
|
|
1797
|
+
fontSize: 18,
|
|
1798
|
+
color: import_core6.ada.inkMuted,
|
|
1799
|
+
fontWeight: "400"
|
|
1800
|
+
},
|
|
1801
|
+
body: {
|
|
1802
|
+
marginTop: 10,
|
|
1803
|
+
paddingTop: 10,
|
|
1804
|
+
borderTopWidth: 1,
|
|
1805
|
+
borderTopColor: import_core6.ada.border
|
|
1806
|
+
}
|
|
1807
|
+
});
|
|
1158
1808
|
}
|
|
1159
1809
|
});
|
|
1160
1810
|
|
|
1161
1811
|
// 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
1812
|
function SurfaceFullscreen({
|
|
1169
1813
|
visible,
|
|
1170
1814
|
onClose,
|
|
@@ -1175,9 +1819,9 @@ function SurfaceFullscreen({
|
|
|
1175
1819
|
closeIcon = "x",
|
|
1176
1820
|
closeLabel = "Close"
|
|
1177
1821
|
}) {
|
|
1178
|
-
const fadeAnim = (0,
|
|
1179
|
-
const heroScale = (0,
|
|
1180
|
-
(0,
|
|
1822
|
+
const fadeAnim = (0, import_react7.useRef)(new import_react_native7.Animated.Value(0)).current;
|
|
1823
|
+
const heroScale = (0, import_react7.useRef)(new import_react_native7.Animated.Value(0.6)).current;
|
|
1824
|
+
(0, import_react7.useEffect)(() => {
|
|
1181
1825
|
if (visible) {
|
|
1182
1826
|
import_react_native7.Animated.parallel([
|
|
1183
1827
|
import_react_native7.Animated.timing(fadeAnim, { toValue: 1, duration: 280, useNativeDriver: true }),
|
|
@@ -1197,22 +1841,22 @@ function SurfaceFullscreen({
|
|
|
1197
1841
|
if (!visible) return null;
|
|
1198
1842
|
const renderCloseIcon = () => {
|
|
1199
1843
|
if (closeIcon === "hangup") {
|
|
1200
|
-
return /* @__PURE__ */ (0,
|
|
1844
|
+
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
1845
|
}
|
|
1202
1846
|
if (closeIcon === "x") {
|
|
1203
|
-
return /* @__PURE__ */ (0,
|
|
1847
|
+
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
1848
|
}
|
|
1205
1849
|
return closeIcon;
|
|
1206
1850
|
};
|
|
1207
|
-
return /* @__PURE__ */ (0,
|
|
1208
|
-
/* @__PURE__ */ (0,
|
|
1209
|
-
showWordmark ? /* @__PURE__ */ (0,
|
|
1210
|
-
status ? /* @__PURE__ */ (0,
|
|
1851
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native7.Animated.View, { style: [styles5.overlay, { opacity: fadeAnim }], pointerEvents: "auto", children: [
|
|
1852
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native7.View, { style: styles5.topBar, children: [
|
|
1853
|
+
showWordmark ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native7.Text, { style: styles5.wordmark, children: "ada" }) : null,
|
|
1854
|
+
status ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native7.Text, { style: styles5.status, children: status }) : null
|
|
1211
1855
|
] }),
|
|
1212
|
-
hero ? /* @__PURE__ */ (0,
|
|
1213
|
-
/* @__PURE__ */ (0,
|
|
1214
|
-
/* @__PURE__ */ (0,
|
|
1215
|
-
/* @__PURE__ */ (0,
|
|
1856
|
+
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,
|
|
1857
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native7.View, { style: styles5.body, children }),
|
|
1858
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native7.View, { style: styles5.bottomBar, children: [
|
|
1859
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1216
1860
|
import_react_native7.Pressable,
|
|
1217
1861
|
{
|
|
1218
1862
|
accessibilityRole: "button",
|
|
@@ -1223,151 +1867,166 @@ function SurfaceFullscreen({
|
|
|
1223
1867
|
children: renderCloseIcon()
|
|
1224
1868
|
}
|
|
1225
1869
|
),
|
|
1226
|
-
closeLabel ? /* @__PURE__ */ (0,
|
|
1870
|
+
closeLabel ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native7.Text, { style: styles5.closeLabel, children: closeLabel }) : null
|
|
1227
1871
|
] })
|
|
1228
1872
|
] });
|
|
1229
1873
|
}
|
|
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
|
-
|
|
1874
|
+
var import_react7, import_react_native7, import_react_native_svg3, import_core7, import_jsx_runtime5, fontFam3, styles5;
|
|
1875
|
+
var init_SurfaceFullscreen = __esm({
|
|
1876
|
+
"src/surfaces/SurfaceFullscreen.tsx"() {
|
|
1877
|
+
"use strict";
|
|
1878
|
+
import_react7 = require("react");
|
|
1879
|
+
import_react_native7 = require("react-native");
|
|
1880
|
+
import_react_native_svg3 = __toESM(require("react-native-svg"));
|
|
1881
|
+
import_core7 = require("@ateam-ai-mobile/core");
|
|
1882
|
+
import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1883
|
+
fontFam3 = import_react_native7.Platform.OS === "ios" ? "SF Pro Rounded" : void 0;
|
|
1884
|
+
styles5 = import_react_native7.StyleSheet.create({
|
|
1885
|
+
overlay: {
|
|
1886
|
+
position: "absolute",
|
|
1887
|
+
top: 0,
|
|
1888
|
+
left: 0,
|
|
1889
|
+
right: 0,
|
|
1890
|
+
bottom: 0,
|
|
1891
|
+
backgroundColor: import_core7.ada.paper,
|
|
1892
|
+
zIndex: 1e3,
|
|
1893
|
+
elevation: 1e3
|
|
1894
|
+
},
|
|
1895
|
+
topBar: {
|
|
1896
|
+
paddingTop: import_react_native7.Platform.OS === "ios" ? 64 : 36,
|
|
1897
|
+
paddingHorizontal: 24,
|
|
1898
|
+
alignItems: "center",
|
|
1899
|
+
gap: 6
|
|
1900
|
+
},
|
|
1901
|
+
wordmark: {
|
|
1902
|
+
fontSize: 14,
|
|
1903
|
+
fontFamily: fontFam3,
|
|
1904
|
+
fontWeight: "500",
|
|
1905
|
+
color: import_core7.ada.inkMuted,
|
|
1906
|
+
letterSpacing: 4
|
|
1907
|
+
},
|
|
1908
|
+
status: {
|
|
1909
|
+
fontSize: 17,
|
|
1910
|
+
fontFamily: fontFam3,
|
|
1911
|
+
fontWeight: "500",
|
|
1912
|
+
color: import_core7.ada.ink,
|
|
1913
|
+
letterSpacing: -0.2
|
|
1914
|
+
},
|
|
1915
|
+
heroWrap: {
|
|
1916
|
+
paddingTop: 24,
|
|
1917
|
+
paddingBottom: 12,
|
|
1918
|
+
alignItems: "center",
|
|
1919
|
+
justifyContent: "center"
|
|
1920
|
+
},
|
|
1921
|
+
body: {
|
|
1922
|
+
flex: 1
|
|
1923
|
+
},
|
|
1924
|
+
bottomBar: {
|
|
1925
|
+
paddingBottom: import_react_native7.Platform.OS === "ios" ? 48 : 32,
|
|
1926
|
+
paddingTop: 12,
|
|
1927
|
+
alignItems: "center",
|
|
1928
|
+
gap: 8
|
|
1929
|
+
},
|
|
1930
|
+
closeBtn: {
|
|
1931
|
+
width: 64,
|
|
1932
|
+
height: 64,
|
|
1933
|
+
borderRadius: 32,
|
|
1934
|
+
backgroundColor: import_core7.ada.ink,
|
|
1935
|
+
justifyContent: "center",
|
|
1936
|
+
alignItems: "center",
|
|
1937
|
+
shadowColor: import_core7.ada.ink,
|
|
1938
|
+
shadowOffset: { width: 0, height: 8 },
|
|
1939
|
+
shadowOpacity: 0.18,
|
|
1940
|
+
shadowRadius: 14,
|
|
1941
|
+
elevation: 8
|
|
1942
|
+
},
|
|
1943
|
+
closeLabel: {
|
|
1944
|
+
fontSize: 13,
|
|
1945
|
+
fontFamily: fontFam3,
|
|
1946
|
+
fontWeight: "500",
|
|
1947
|
+
color: import_core7.ada.inkMuted,
|
|
1948
|
+
letterSpacing: 0.3
|
|
1949
|
+
}
|
|
1950
|
+
});
|
|
1295
1951
|
}
|
|
1296
1952
|
});
|
|
1297
1953
|
|
|
1298
1954
|
// 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
1955
|
function isMountSurface(m) {
|
|
1361
1956
|
return m.type === "drawer" || m.type === "fullscreen";
|
|
1362
1957
|
}
|
|
1363
1958
|
function isInlineSurface(m) {
|
|
1364
1959
|
return m.type === "card" || m.type === "header" || m.type === "ambient" || m.type === "nudge";
|
|
1365
1960
|
}
|
|
1961
|
+
var import_zustand, useSurfaceStore;
|
|
1962
|
+
var init_store = __esm({
|
|
1963
|
+
"src/registry/store.ts"() {
|
|
1964
|
+
"use strict";
|
|
1965
|
+
import_zustand = require("zustand");
|
|
1966
|
+
useSurfaceStore = (0, import_zustand.create)((set, get) => ({
|
|
1967
|
+
manifests: {},
|
|
1968
|
+
activeDrawer: null,
|
|
1969
|
+
activeFullscreen: null,
|
|
1970
|
+
activeProps: {},
|
|
1971
|
+
mountKey: 0,
|
|
1972
|
+
register: (manifest) => set((s) => ({ manifests: { ...s.manifests, [manifest.id]: manifest } })),
|
|
1973
|
+
unregister: (id) => set((s) => {
|
|
1974
|
+
const next = { ...s.manifests };
|
|
1975
|
+
delete next[id];
|
|
1976
|
+
const nextProps = { ...s.activeProps };
|
|
1977
|
+
delete nextProps[id];
|
|
1978
|
+
return { manifests: next, activeProps: nextProps };
|
|
1979
|
+
}),
|
|
1980
|
+
show: (id, props) => {
|
|
1981
|
+
const m = get().manifests[id];
|
|
1982
|
+
if (!m) {
|
|
1983
|
+
console.warn("[SurfaceStore] show: unknown surface id", id);
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1986
|
+
set((s) => {
|
|
1987
|
+
const nextProps = props !== void 0 ? { ...s.activeProps, [id]: props } : s.activeProps;
|
|
1988
|
+
const mountKey = s.mountKey + 1;
|
|
1989
|
+
if (m.type === "drawer") return { activeDrawer: id, activeProps: nextProps, mountKey };
|
|
1990
|
+
if (m.type === "fullscreen") return { activeFullscreen: id, activeProps: nextProps, mountKey };
|
|
1991
|
+
return { mountKey };
|
|
1992
|
+
});
|
|
1993
|
+
},
|
|
1994
|
+
hide: (id) => {
|
|
1995
|
+
const s = get();
|
|
1996
|
+
if (!id) {
|
|
1997
|
+
const cleanup2 = { ...s.activeProps };
|
|
1998
|
+
if (s.activeDrawer) delete cleanup2[s.activeDrawer];
|
|
1999
|
+
if (s.activeFullscreen) delete cleanup2[s.activeFullscreen];
|
|
2000
|
+
set({ activeDrawer: null, activeFullscreen: null, activeProps: cleanup2 });
|
|
2001
|
+
return;
|
|
2002
|
+
}
|
|
2003
|
+
const cleanup = { ...s.activeProps };
|
|
2004
|
+
delete cleanup[id];
|
|
2005
|
+
const patch = { activeProps: cleanup };
|
|
2006
|
+
if (s.activeDrawer === id) patch.activeDrawer = null;
|
|
2007
|
+
if (s.activeFullscreen === id) patch.activeFullscreen = null;
|
|
2008
|
+
set(patch);
|
|
2009
|
+
},
|
|
2010
|
+
list: (filter) => {
|
|
2011
|
+
const all = Object.values(get().manifests);
|
|
2012
|
+
return all.filter((m) => {
|
|
2013
|
+
if (filter?.type && m.type !== filter.type) return false;
|
|
2014
|
+
if (filter?.visibility && !filter.visibility.includes(m.visibility)) return false;
|
|
2015
|
+
if (filter?.placement) {
|
|
2016
|
+
if (filter.placement === "unset") {
|
|
2017
|
+
if (m.placement) return false;
|
|
2018
|
+
} else if (m.placement !== filter.placement) {
|
|
2019
|
+
return false;
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
return true;
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
2025
|
+
}));
|
|
2026
|
+
}
|
|
2027
|
+
});
|
|
1366
2028
|
|
|
1367
2029
|
// 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
2030
|
function SurfaceDispatcher() {
|
|
1372
2031
|
const manifests = useSurfaceStore((s) => s.manifests);
|
|
1373
2032
|
const activeDrawer = useSurfaceStore((s) => s.activeDrawer);
|
|
@@ -1375,8 +2034,8 @@ function SurfaceDispatcher() {
|
|
|
1375
2034
|
const activeProps = useSurfaceStore((s) => s.activeProps);
|
|
1376
2035
|
const mountKey = useSurfaceStore((s) => s.mountKey);
|
|
1377
2036
|
const hide = useSurfaceStore((s) => s.hide);
|
|
1378
|
-
(0,
|
|
1379
|
-
const unsub =
|
|
2037
|
+
(0, import_react8.useEffect)(() => {
|
|
2038
|
+
const unsub = import_core8.pluginCommandEmitter.on("plugin-close", ({ pluginId } = {}) => {
|
|
1380
2039
|
if (!pluginId) return;
|
|
1381
2040
|
const s = useSurfaceStore.getState();
|
|
1382
2041
|
if (s.activeDrawer === pluginId || s.activeFullscreen === pluginId) {
|
|
@@ -1389,8 +2048,8 @@ function SurfaceDispatcher() {
|
|
|
1389
2048
|
const fullscreen = activeFullscreen ? manifests[activeFullscreen] : null;
|
|
1390
2049
|
const drawerProps = activeDrawer ? activeProps[activeDrawer] : void 0;
|
|
1391
2050
|
const fullscreenProps = activeFullscreen ? activeProps[activeFullscreen] : void 0;
|
|
1392
|
-
return /* @__PURE__ */ (0,
|
|
1393
|
-
drawer && drawer.type === "drawer" && drawer.Component ? /* @__PURE__ */ (0,
|
|
2051
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
2052
|
+
drawer && drawer.type === "drawer" && drawer.Component ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1394
2053
|
drawer.Component,
|
|
1395
2054
|
{
|
|
1396
2055
|
dismiss: () => hide(drawer.id),
|
|
@@ -1398,7 +2057,7 @@ function SurfaceDispatcher() {
|
|
|
1398
2057
|
},
|
|
1399
2058
|
`drawer-${drawer.id}-${mountKey}`
|
|
1400
2059
|
) : null,
|
|
1401
|
-
fullscreen && fullscreen.type === "fullscreen" && fullscreen.Component ? /* @__PURE__ */ (0,
|
|
2060
|
+
fullscreen && fullscreen.type === "fullscreen" && fullscreen.Component ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1402
2061
|
fullscreen.Component,
|
|
1403
2062
|
{
|
|
1404
2063
|
dismiss: () => hide(fullscreen.id),
|
|
@@ -1408,75 +2067,18 @@ function SurfaceDispatcher() {
|
|
|
1408
2067
|
) : null
|
|
1409
2068
|
] });
|
|
1410
2069
|
}
|
|
2070
|
+
var import_react8, import_core8, import_jsx_runtime6;
|
|
2071
|
+
var init_SurfaceDispatcher = __esm({
|
|
2072
|
+
"src/registry/SurfaceDispatcher.tsx"() {
|
|
2073
|
+
"use strict";
|
|
2074
|
+
import_react8 = require("react");
|
|
2075
|
+
init_store();
|
|
2076
|
+
import_core8 = require("@ateam-ai-mobile/core");
|
|
2077
|
+
import_jsx_runtime6 = require("react/jsx-runtime");
|
|
2078
|
+
}
|
|
2079
|
+
});
|
|
1411
2080
|
|
|
1412
2081
|
// 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
2082
|
function sha256Hex(message) {
|
|
1481
2083
|
const utf8 = unescape(encodeURIComponent(message));
|
|
1482
2084
|
const bytes = new Uint8Array(utf8.length);
|
|
@@ -1544,547 +2146,1153 @@ function sha256Hex(message) {
|
|
|
1544
2146
|
}
|
|
1545
2147
|
return out;
|
|
1546
2148
|
}
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
2149
|
+
var K, rotr;
|
|
2150
|
+
var init_sha256 = __esm({
|
|
2151
|
+
"src/utils/sha256.ts"() {
|
|
2152
|
+
"use strict";
|
|
2153
|
+
K = new Uint32Array([
|
|
2154
|
+
1116352408,
|
|
2155
|
+
1899447441,
|
|
2156
|
+
3049323471,
|
|
2157
|
+
3921009573,
|
|
2158
|
+
961987163,
|
|
2159
|
+
1508970993,
|
|
2160
|
+
2453635748,
|
|
2161
|
+
2870763221,
|
|
2162
|
+
3624381080,
|
|
2163
|
+
310598401,
|
|
2164
|
+
607225278,
|
|
2165
|
+
1426881987,
|
|
2166
|
+
1925078388,
|
|
2167
|
+
2162078206,
|
|
2168
|
+
2614888103,
|
|
2169
|
+
3248222580,
|
|
2170
|
+
3835390401,
|
|
2171
|
+
4022224774,
|
|
2172
|
+
264347078,
|
|
2173
|
+
604807628,
|
|
2174
|
+
770255983,
|
|
2175
|
+
1249150122,
|
|
2176
|
+
1555081692,
|
|
2177
|
+
1996064986,
|
|
2178
|
+
2554220882,
|
|
2179
|
+
2821834349,
|
|
2180
|
+
2952996808,
|
|
2181
|
+
3210313671,
|
|
2182
|
+
3336571891,
|
|
2183
|
+
3584528711,
|
|
2184
|
+
113926993,
|
|
2185
|
+
338241895,
|
|
2186
|
+
666307205,
|
|
2187
|
+
773529912,
|
|
2188
|
+
1294757372,
|
|
2189
|
+
1396182291,
|
|
2190
|
+
1695183700,
|
|
2191
|
+
1986661051,
|
|
2192
|
+
2177026350,
|
|
2193
|
+
2456956037,
|
|
2194
|
+
2730485921,
|
|
2195
|
+
2820302411,
|
|
2196
|
+
3259730800,
|
|
2197
|
+
3345764771,
|
|
2198
|
+
3516065817,
|
|
2199
|
+
3600352804,
|
|
2200
|
+
4094571909,
|
|
2201
|
+
275423344,
|
|
2202
|
+
430227734,
|
|
2203
|
+
506948616,
|
|
2204
|
+
659060556,
|
|
2205
|
+
883997877,
|
|
2206
|
+
958139571,
|
|
2207
|
+
1322822218,
|
|
2208
|
+
1537002063,
|
|
2209
|
+
1747873779,
|
|
2210
|
+
1955562222,
|
|
2211
|
+
2024104815,
|
|
2212
|
+
2227730452,
|
|
2213
|
+
2361852424,
|
|
2214
|
+
2428436474,
|
|
2215
|
+
2756734187,
|
|
2216
|
+
3204031479,
|
|
2217
|
+
3329325298
|
|
2218
|
+
]);
|
|
2219
|
+
rotr = (n, x) => x >>> n | x << 32 - n;
|
|
1562
2220
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
2221
|
+
});
|
|
2222
|
+
|
|
2223
|
+
// src/bundles/integrity.ts
|
|
2224
|
+
function assertBundleIntegrity(content, verifiedHash, label) {
|
|
2225
|
+
const digest = sha256Hex(content);
|
|
2226
|
+
if (!verifiedHash) {
|
|
2227
|
+
throw new Error(`[bundle-integrity] refusing to evaluate ${label}: no verified SHA-256 (fail closed).`);
|
|
1567
2228
|
}
|
|
1568
|
-
|
|
1569
|
-
|
|
2229
|
+
if (digest !== verifiedHash.toLowerCase()) {
|
|
2230
|
+
throw new Error(
|
|
2231
|
+
`[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.`
|
|
2232
|
+
);
|
|
1570
2233
|
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
2234
|
+
}
|
|
2235
|
+
var init_integrity = __esm({
|
|
2236
|
+
"src/bundles/integrity.ts"() {
|
|
2237
|
+
"use strict";
|
|
2238
|
+
init_sha256();
|
|
1574
2239
|
}
|
|
1575
|
-
};
|
|
1576
|
-
|
|
2240
|
+
});
|
|
2241
|
+
|
|
2242
|
+
// src/bundles/storage.ts
|
|
2243
|
+
function selectOwnedCacheEntries(entries, tenant) {
|
|
2244
|
+
if (!tenant) return [];
|
|
2245
|
+
return (entries || []).filter((e) => !!e && e.tenant === tenant);
|
|
2246
|
+
}
|
|
1577
2247
|
function setBundleStorage(storage) {
|
|
1578
2248
|
_storage = storage;
|
|
1579
2249
|
}
|
|
1580
2250
|
function getBundleStorage() {
|
|
1581
2251
|
return _storage;
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
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
|
-
Suspense: import_react8.default.Suspense
|
|
1613
|
-
};
|
|
1614
|
-
var PluginSDKShim = Object.freeze({
|
|
1615
|
-
...sdk_exports,
|
|
1616
|
-
SurfaceDrawer,
|
|
1617
|
-
SurfaceCard,
|
|
1618
|
-
SurfaceFullscreen,
|
|
1619
|
-
useSurfaceStore,
|
|
1620
|
-
SurfaceDispatcher,
|
|
1621
|
-
PluginSDK: Object.freeze({
|
|
1622
|
-
...PluginSDK,
|
|
1623
|
-
register: PluginSDK.register
|
|
1624
|
-
}),
|
|
1625
|
-
register
|
|
1626
|
-
});
|
|
1627
|
-
var requireShim = (name) => {
|
|
1628
|
-
if (name === "react") return ReactShim;
|
|
1629
|
-
if (name === "react-native") return ReactNative;
|
|
1630
|
-
if (name === "@adas/plugin-sdk") return PluginSDKShim;
|
|
1631
|
-
const hostModule = resolveBundleModule(name);
|
|
1632
|
-
if (hostModule !== void 0) return hostModule;
|
|
1633
|
-
console.warn(`[BundleManager] Unknown module: ${name} \u2014 register it via registerBundleModules()`);
|
|
1634
|
-
return {};
|
|
1635
|
-
};
|
|
1636
|
-
var CACHE_TTL = 7 * 24 * 60 * 60 * 1e3;
|
|
1637
|
-
var BundleManager = class {
|
|
1638
|
-
constructor() {
|
|
1639
|
-
/** In-memory module cache: bundleUrl → evaluated module.exports */
|
|
1640
|
-
this.modules = /* @__PURE__ */ new Map();
|
|
1641
|
-
/** In-flight download deduplication: bundleUrl → Promise */
|
|
1642
|
-
this.inflight = /* @__PURE__ */ new Map();
|
|
1643
|
-
}
|
|
1644
|
-
// ── Public API ──────────────────────────────────────────────
|
|
1645
|
-
/**
|
|
1646
|
-
* Load a plugin component. Returns the RegisteredUIPlugin object.
|
|
1647
|
-
* Handles: in-memory cache → disk cache → download → evaluate → extract.
|
|
1648
|
-
*
|
|
1649
|
-
* `expectedHash` (SHA-256 hex from cp.getContextPlugin manifest) MUST be
|
|
1650
|
-
* provided for any bundle the user has not previously verified — without
|
|
1651
|
-
* it, a tampered bundle is `eval`'d as JS with full RN runtime + every
|
|
1652
|
-
* native capability the manifest declares.
|
|
1653
|
-
*/
|
|
1654
|
-
async load(pluginId, bundleUrl, version, expectedHash) {
|
|
1655
|
-
const cached = this.getComponentFromMemory(pluginId, bundleUrl);
|
|
1656
|
-
if (cached) return cached;
|
|
1657
|
-
let modulePromise = this.inflight.get(bundleUrl);
|
|
1658
|
-
if (!modulePromise) {
|
|
1659
|
-
modulePromise = this.loadModule(bundleUrl, version, expectedHash);
|
|
1660
|
-
this.inflight.set(bundleUrl, modulePromise);
|
|
1661
|
-
modulePromise.catch(() => {
|
|
1662
|
-
}).finally(() => this.inflight.delete(bundleUrl));
|
|
1663
|
-
}
|
|
1664
|
-
await modulePromise;
|
|
1665
|
-
const component = this.getComponentFromMemory(pluginId, bundleUrl);
|
|
1666
|
-
if (!component) {
|
|
1667
|
-
throw new Error(
|
|
1668
|
-
`Bundle loaded from ${bundleUrl} but no component found for "${pluginId}". Available exports: ${this.describeModule(this.modules.get(bundleUrl))}`
|
|
1669
|
-
);
|
|
1670
|
-
}
|
|
1671
|
-
return component;
|
|
1672
|
-
}
|
|
1673
|
-
/**
|
|
1674
|
-
* Prewarm: load every plugin bundle in the background after app boot, so a
|
|
1675
|
-
* later open is instant (in-memory hit) instead of round-tripping the
|
|
1676
|
-
* network. Errors are swallowed per-plugin — one bad bundle shouldn't
|
|
1677
|
-
* block others. Concurrency capped at 3.
|
|
1678
|
-
*/
|
|
1679
|
-
async prewarmAll(items) {
|
|
1680
|
-
const queue = [...items];
|
|
1681
|
-
const concurrency = Math.min(3, queue.length);
|
|
1682
|
-
const start = Date.now();
|
|
1683
|
-
let okCount = 0;
|
|
1684
|
-
let failCount = 0;
|
|
1685
|
-
const worker = async () => {
|
|
1686
|
-
while (queue.length > 0) {
|
|
1687
|
-
const it = queue.shift();
|
|
1688
|
-
if (!it) return;
|
|
1689
|
-
try {
|
|
1690
|
-
await this.load(it.pluginId, it.bundleUrl, it.version, it.bundleHash);
|
|
1691
|
-
okCount++;
|
|
1692
|
-
} catch (err) {
|
|
1693
|
-
failCount++;
|
|
1694
|
-
console.log(`[prewarm] ${it.pluginId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1695
|
-
}
|
|
2252
|
+
}
|
|
2253
|
+
var MemoryBundleStorage, _storage;
|
|
2254
|
+
var init_storage = __esm({
|
|
2255
|
+
"src/bundles/storage.ts"() {
|
|
2256
|
+
"use strict";
|
|
2257
|
+
MemoryBundleStorage = class {
|
|
2258
|
+
constructor() {
|
|
2259
|
+
this.index = {};
|
|
2260
|
+
this.files = /* @__PURE__ */ new Map();
|
|
2261
|
+
}
|
|
2262
|
+
async loadIndex() {
|
|
2263
|
+
return this.index;
|
|
2264
|
+
}
|
|
2265
|
+
async saveIndex(index) {
|
|
2266
|
+
this.index = index;
|
|
2267
|
+
}
|
|
2268
|
+
async readBundle(localPath) {
|
|
2269
|
+
return this.files.get(localPath) ?? null;
|
|
2270
|
+
}
|
|
2271
|
+
async writeBundle(key, content) {
|
|
2272
|
+
const localPath = `memory://${key}`;
|
|
2273
|
+
this.files.set(localPath, content);
|
|
2274
|
+
return localPath;
|
|
2275
|
+
}
|
|
2276
|
+
async deleteBundle(localPath) {
|
|
2277
|
+
this.files.delete(localPath);
|
|
2278
|
+
}
|
|
2279
|
+
async clearAll() {
|
|
2280
|
+
this.index = {};
|
|
2281
|
+
this.files.clear();
|
|
1696
2282
|
}
|
|
1697
2283
|
};
|
|
1698
|
-
|
|
1699
|
-
console.log(`[prewarm] ${okCount} loaded, ${failCount} failed in ${Date.now() - start}ms`);
|
|
1700
|
-
}
|
|
1701
|
-
/**
|
|
1702
|
-
* Clear all caches (in-memory + storage).
|
|
1703
|
-
*/
|
|
1704
|
-
async clearAll() {
|
|
1705
|
-
this.modules.clear();
|
|
1706
|
-
try {
|
|
1707
|
-
await getBundleStorage().clearAll();
|
|
1708
|
-
} catch (err) {
|
|
1709
|
-
console.warn("[BundleManager] clearAll error:", err);
|
|
1710
|
-
}
|
|
2284
|
+
_storage = new MemoryBundleStorage();
|
|
1711
2285
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
2286
|
+
});
|
|
2287
|
+
|
|
2288
|
+
// src/bundles/bundleManager.ts
|
|
2289
|
+
var import_react9, import_react10, ReactNative, import_core9, ReactShim, PluginSDKShim, requireShim, CACHE_TTL, BundleManager, bundleManager;
|
|
2290
|
+
var init_bundleManager = __esm({
|
|
2291
|
+
"src/bundles/bundleManager.ts"() {
|
|
2292
|
+
"use strict";
|
|
2293
|
+
import_react9 = __toESM(require("react"));
|
|
2294
|
+
import_react10 = require("react");
|
|
2295
|
+
ReactNative = __toESM(require("react-native"));
|
|
2296
|
+
import_core9 = require("@ateam-ai-mobile/core");
|
|
2297
|
+
init_sdk();
|
|
2298
|
+
init_SurfaceDrawer();
|
|
2299
|
+
init_SurfaceCard();
|
|
2300
|
+
init_SurfaceFullscreen();
|
|
2301
|
+
init_store();
|
|
2302
|
+
init_SurfaceDispatcher();
|
|
2303
|
+
init_host();
|
|
2304
|
+
init_sha256();
|
|
2305
|
+
init_integrity();
|
|
2306
|
+
init_storage();
|
|
2307
|
+
init_registry();
|
|
2308
|
+
ReactShim = {
|
|
2309
|
+
__esModule: true,
|
|
2310
|
+
default: import_react9.default,
|
|
2311
|
+
...import_react9.default,
|
|
2312
|
+
// Explicitly include hooks (React 19 may not have them as own enumerable props)
|
|
2313
|
+
useState: import_react10.useState,
|
|
2314
|
+
useEffect: import_react10.useEffect,
|
|
2315
|
+
useCallback: import_react10.useCallback,
|
|
2316
|
+
useMemo: import_react10.useMemo,
|
|
2317
|
+
useRef: import_react10.useRef,
|
|
2318
|
+
useContext: import_react10.useContext,
|
|
2319
|
+
useReducer: import_react10.useReducer,
|
|
2320
|
+
useLayoutEffect: import_react10.useLayoutEffect,
|
|
2321
|
+
useImperativeHandle: import_react10.useImperativeHandle,
|
|
2322
|
+
useDebugValue: import_react10.useDebugValue,
|
|
2323
|
+
createElement: import_react9.default.createElement,
|
|
2324
|
+
Fragment: import_react9.default.Fragment,
|
|
2325
|
+
Component: import_react9.default.Component,
|
|
2326
|
+
PureComponent: import_react9.default.PureComponent,
|
|
2327
|
+
createContext: import_react9.default.createContext,
|
|
2328
|
+
forwardRef: import_react9.default.forwardRef,
|
|
2329
|
+
memo: import_react9.default.memo,
|
|
2330
|
+
Children: import_react9.default.Children,
|
|
2331
|
+
cloneElement: import_react9.default.cloneElement,
|
|
2332
|
+
isValidElement: import_react9.default.isValidElement,
|
|
2333
|
+
createRef: import_react9.default.createRef,
|
|
2334
|
+
lazy: import_react9.default.lazy,
|
|
2335
|
+
Suspense: import_react9.default.Suspense
|
|
2336
|
+
};
|
|
2337
|
+
PluginSDKShim = Object.freeze({
|
|
2338
|
+
...sdk_exports,
|
|
2339
|
+
SurfaceDrawer,
|
|
2340
|
+
SurfaceCard,
|
|
2341
|
+
SurfaceFullscreen,
|
|
2342
|
+
useSurfaceStore,
|
|
2343
|
+
SurfaceDispatcher,
|
|
2344
|
+
PluginSDK: Object.freeze({
|
|
2345
|
+
...PluginSDK,
|
|
2346
|
+
register: PluginSDK.register
|
|
2347
|
+
}),
|
|
2348
|
+
register
|
|
2349
|
+
});
|
|
2350
|
+
requireShim = (name) => {
|
|
2351
|
+
if (name === "react") return ReactShim;
|
|
2352
|
+
if (name === "react-native") return ReactNative;
|
|
2353
|
+
if (name === "@adas/plugin-sdk") return PluginSDKShim;
|
|
2354
|
+
const hostModule = resolveBundleModule(name);
|
|
2355
|
+
if (hostModule !== void 0) return hostModule;
|
|
2356
|
+
console.warn(`[BundleManager] Unknown module: ${name} \u2014 register it via registerBundleModules()`);
|
|
2357
|
+
return {};
|
|
2358
|
+
};
|
|
2359
|
+
CACHE_TTL = 7 * 24 * 60 * 60 * 1e3;
|
|
2360
|
+
BundleManager = class {
|
|
2361
|
+
constructor() {
|
|
2362
|
+
/** In-memory module cache: bundleUrl → evaluated module.exports. Holds ONE
|
|
2363
|
+
* tenant's modules at a time — the tenant boundary is `cacheTenant` /
|
|
2364
|
+
* `requireTenant()`, NOT the key. */
|
|
2365
|
+
this.modules = /* @__PURE__ */ new Map();
|
|
2366
|
+
/** What is actually RUNNING, keyed by bundleUrl (for the current tenant). See
|
|
2367
|
+
* getLoadedBundles(). */
|
|
2368
|
+
this.provenance = /* @__PURE__ */ new Map();
|
|
2369
|
+
/** In-flight download deduplication: bundleUrl → Promise */
|
|
2370
|
+
this.inflight = /* @__PURE__ */ new Map();
|
|
2371
|
+
/**
|
|
2372
|
+
* The tenant the in-memory caches (modules / provenance / inflight) currently
|
|
2373
|
+
* belong to. This is the tenant BOUNDARY for evaluated code.
|
|
2374
|
+
*
|
|
2375
|
+
* A bundleUrl resolves PER-TENANT on the server — one url, different bytes for
|
|
2376
|
+
* different tenants — so a memory hit keyed on url alone would hand tenant B
|
|
2377
|
+
* the module tenant A already evaluated and ran. PR #9 scoped the DISK cache
|
|
2378
|
+
* (per-tenant index + hash gate); the in-memory map was left url-keyed. This
|
|
2379
|
+
* closes that: the caches only ever hold ONE tenant's modules, and a switch
|
|
2380
|
+
* drops them before anything is served.
|
|
2381
|
+
*/
|
|
2382
|
+
this.cacheTenant = null;
|
|
2383
|
+
/**
|
|
2384
|
+
* pluginIds this manager registered device tools for. A tenant switch drops
|
|
2385
|
+
* the in-memory modules AND unregisters these, so the incoming tenant's bundle
|
|
2386
|
+
* re-registers into a clean registry instead of being refused as a duplicate
|
|
2387
|
+
* of the previous tenant's tools.
|
|
2388
|
+
*/
|
|
2389
|
+
this.registeredToolPluginIds = /* @__PURE__ */ new Set();
|
|
2390
|
+
/**
|
|
2391
|
+
* Enforce the in-memory tenant boundary. Call at the top of every public load
|
|
2392
|
+
* path, BEFORE any cache read.
|
|
2393
|
+
*
|
|
2394
|
+
* FAIL CLOSED: without a tenant identity we cannot prove a cached module is
|
|
2395
|
+
* this session's, so we drop everything and refuse rather than risk serving
|
|
2396
|
+
* one tenant's evaluated code to another. On a tenant change we drop the
|
|
2397
|
+
* previous tenant's modules (and their tool registrations) before proceeding.
|
|
2398
|
+
*/
|
|
2399
|
+
/**
|
|
2400
|
+
* Monotonic load generation. Bumped on every in-memory wipe (a tenant change,
|
|
2401
|
+
* fail-closed, or clearAll). A load captures it at ENTRY; a completion whose
|
|
2402
|
+
* generation no longer matches is stale and is discarded before it can commit.
|
|
2403
|
+
* This closes the async race the sequential guard cannot: tenant A's in-flight
|
|
2404
|
+
* loadModule can resolve AFTER a switch to B — clearing `inflight` does not
|
|
2405
|
+
* cancel that Promise — and would otherwise write A's module / provenance /
|
|
2406
|
+
* device tools into the singleton now marked cacheTenant=B.
|
|
2407
|
+
*/
|
|
2408
|
+
this.epoch = 0;
|
|
1726
2409
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
const hashOk = expectedLc && entry.bundleHash && entry.bundleHash.toLowerCase() === expectedLc;
|
|
1740
|
-
if (age < CACHE_TTL && hashOk) {
|
|
1741
|
-
content = await storage.readBundle(entry.localPath);
|
|
1742
|
-
if (content) break;
|
|
2410
|
+
requireTenant() {
|
|
2411
|
+
const tenant = (0, import_core9.getTenant)();
|
|
2412
|
+
if (!tenant) {
|
|
2413
|
+
this.dropInMemory();
|
|
2414
|
+
this.cacheTenant = null;
|
|
2415
|
+
throw new Error(
|
|
2416
|
+
"[BundleManager] refusing to load a tenant-scoped bundle without a tenant identity (fail closed)"
|
|
2417
|
+
);
|
|
2418
|
+
}
|
|
2419
|
+
if (tenant !== this.cacheTenant) {
|
|
2420
|
+
this.dropInMemory();
|
|
2421
|
+
this.cacheTenant = tenant;
|
|
1743
2422
|
}
|
|
2423
|
+
return tenant;
|
|
1744
2424
|
}
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
await storage.saveIndex(index);
|
|
1760
|
-
} catch (err) {
|
|
1761
|
-
console.warn("[BundleManager] Cache write error:", err);
|
|
2425
|
+
/** Drop every in-memory cache and unregister the device tools this manager
|
|
2426
|
+
* registered — used on a tenant change and by clearAll(). */
|
|
2427
|
+
dropInMemory() {
|
|
2428
|
+
for (const pluginId of this.registeredToolPluginIds) {
|
|
2429
|
+
try {
|
|
2430
|
+
unregisterDeviceTools(pluginId);
|
|
2431
|
+
} catch {
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
this.registeredToolPluginIds.clear();
|
|
2435
|
+
this.modules.clear();
|
|
2436
|
+
this.provenance.clear();
|
|
2437
|
+
this.inflight.clear();
|
|
2438
|
+
this.epoch++;
|
|
1762
2439
|
}
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
* Download + integrity-verify a bundle. Refuses to return when:
|
|
1769
|
-
* - manifest hash and computed hash disagree
|
|
1770
|
-
* - server's `X-Bundle-SHA256` disagrees with manifest hash
|
|
1771
|
-
* - no expected hash AND no server hash header (cannot prove the bundle
|
|
1772
|
-
* wasn't tampered with — fail closed rather than trust the wire).
|
|
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
|
-
);
|
|
2440
|
+
/** True once the boundary has moved since a load captured `epoch` — i.e. a
|
|
2441
|
+
* tenant switch (or wipe) happened while this load was in flight. Its results
|
|
2442
|
+
* belong to a tenant that is no longer current and must be discarded. */
|
|
2443
|
+
isStale(epoch, tenant) {
|
|
2444
|
+
return this.epoch !== epoch || (0, import_core9.getTenant)() !== tenant;
|
|
1792
2445
|
}
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
2446
|
+
/** Flip an existing provenance record to source:'memory' — a later serve of an
|
|
2447
|
+
* already-evaluated bundle came from the in-memory cache, not a fresh
|
|
2448
|
+
* download/disk read. No-op if nothing was recorded yet. */
|
|
2449
|
+
markMemoryHit(bundleUrl) {
|
|
2450
|
+
const prev = this.provenance.get(bundleUrl);
|
|
2451
|
+
if (prev && prev.source !== "memory") {
|
|
2452
|
+
this.provenance.set(bundleUrl, { ...prev, source: "memory" });
|
|
2453
|
+
}
|
|
1797
2454
|
}
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
2455
|
+
// ── Public API ──────────────────────────────────────────────
|
|
2456
|
+
/**
|
|
2457
|
+
* Load a plugin component. Returns the RegisteredUIPlugin object.
|
|
2458
|
+
* Handles: in-memory cache → disk cache → download → evaluate → extract.
|
|
2459
|
+
*
|
|
2460
|
+
* `expectedHash` (SHA-256 hex from cp.getContextPlugin manifest) MUST be
|
|
2461
|
+
* provided for any bundle the user has not previously verified — without
|
|
2462
|
+
* it, a tampered bundle is `eval`'d as JS with full RN runtime + every
|
|
2463
|
+
* native capability the manifest declares.
|
|
2464
|
+
*/
|
|
2465
|
+
async load(pluginId, bundleUrl, version, expectedHash) {
|
|
2466
|
+
const tenant = this.requireTenant();
|
|
2467
|
+
const epoch = this.epoch;
|
|
2468
|
+
const cached = this.getComponentFromMemory(pluginId, bundleUrl);
|
|
2469
|
+
if (cached) {
|
|
2470
|
+
this.markMemoryHit(bundleUrl);
|
|
2471
|
+
return cached;
|
|
2472
|
+
}
|
|
2473
|
+
let modulePromise = this.inflight.get(bundleUrl);
|
|
2474
|
+
if (!modulePromise) {
|
|
2475
|
+
const promise = this.loadModule(bundleUrl, version, expectedHash, tenant, epoch);
|
|
2476
|
+
modulePromise = promise;
|
|
2477
|
+
this.inflight.set(bundleUrl, promise);
|
|
2478
|
+
promise.catch(() => {
|
|
2479
|
+
}).finally(() => {
|
|
2480
|
+
if (this.inflight.get(bundleUrl) === promise) this.inflight.delete(bundleUrl);
|
|
2481
|
+
});
|
|
2482
|
+
}
|
|
2483
|
+
await modulePromise;
|
|
2484
|
+
if (this.isStale(epoch, tenant)) {
|
|
2485
|
+
throw new Error("[BundleManager] tenant changed while loading \u2014 discarding (fail closed)");
|
|
2486
|
+
}
|
|
2487
|
+
const component = this.getComponentFromMemory(pluginId, bundleUrl);
|
|
2488
|
+
if (!component) {
|
|
2489
|
+
throw new Error(
|
|
2490
|
+
`Bundle loaded from ${bundleUrl} but no component found for "${pluginId}". Available exports: ${this.describeModule(this.modules.get(bundleUrl))}`
|
|
2491
|
+
);
|
|
2492
|
+
}
|
|
2493
|
+
return component;
|
|
1803
2494
|
}
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
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 {
|
|
2495
|
+
/**
|
|
2496
|
+
* Load a bundle for its `tools/` section only — no UI required.
|
|
2497
|
+
*
|
|
2498
|
+
* Same download, same integrity check, same evaluation as `load()`; the only
|
|
2499
|
+
* difference is that a bundle exporting tools and no `Component` is a success
|
|
2500
|
+
* here rather than "no component found". Returns the tool names registered.
|
|
2501
|
+
*/
|
|
2502
|
+
async loadTools(pluginId, bundleUrl, version, expectedHash) {
|
|
2503
|
+
const tenant = this.requireTenant();
|
|
2504
|
+
const epoch = this.epoch;
|
|
2505
|
+
let modulePromise = this.inflight.get(bundleUrl);
|
|
2506
|
+
if (!modulePromise) {
|
|
2507
|
+
const promise = this.loadModule(bundleUrl, version, expectedHash, tenant, epoch);
|
|
2508
|
+
modulePromise = promise;
|
|
2509
|
+
this.inflight.set(bundleUrl, promise);
|
|
2510
|
+
promise.catch(() => {
|
|
2511
|
+
}).finally(() => {
|
|
2512
|
+
if (this.inflight.get(bundleUrl) === promise) this.inflight.delete(bundleUrl);
|
|
2513
|
+
});
|
|
2514
|
+
}
|
|
2515
|
+
await modulePromise;
|
|
2516
|
+
if (this.isStale(epoch, tenant)) {
|
|
2517
|
+
throw new Error("[BundleManager] tenant changed while loading tools \u2014 discarding (fail closed)");
|
|
2518
|
+
}
|
|
2519
|
+
const names = listDeviceTools().filter((t) => t.pluginId === pluginId).map((t) => t.name);
|
|
2520
|
+
if (names.length === 0) {
|
|
2521
|
+
console.warn(
|
|
2522
|
+
`[BundleManager] ${pluginId}: bundle loaded from ${bundleUrl} but registered no tools. Available exports: ${this.describeModule(this.modules.get(bundleUrl))}`
|
|
2523
|
+
);
|
|
2524
|
+
}
|
|
2525
|
+
return names;
|
|
1841
2526
|
}
|
|
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
|
-
|
|
2527
|
+
/**
|
|
2528
|
+
* Prewarm: load every plugin bundle in the background after app boot, so a
|
|
2529
|
+
* later open is instant (in-memory hit) instead of round-tripping the
|
|
2530
|
+
* network. Errors are swallowed per-plugin — one bad bundle shouldn't
|
|
2531
|
+
* block others. Concurrency capped at 3.
|
|
2532
|
+
*/
|
|
2533
|
+
async prewarmAll(items) {
|
|
2534
|
+
const queue = [...items];
|
|
2535
|
+
const concurrency = Math.min(3, queue.length);
|
|
2536
|
+
const start = Date.now();
|
|
2537
|
+
let okCount = 0;
|
|
2538
|
+
let failCount = 0;
|
|
2539
|
+
const worker = async () => {
|
|
2540
|
+
while (queue.length > 0) {
|
|
2541
|
+
const it = queue.shift();
|
|
2542
|
+
if (!it) return;
|
|
2543
|
+
try {
|
|
2544
|
+
await this.load(it.pluginId, it.bundleUrl, it.version, it.bundleHash);
|
|
2545
|
+
okCount++;
|
|
2546
|
+
} catch (err) {
|
|
2547
|
+
failCount++;
|
|
2548
|
+
console.log(`[prewarm] ${it.pluginId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
};
|
|
2552
|
+
await Promise.all(Array.from({ length: concurrency }, worker));
|
|
2553
|
+
console.log(`[prewarm] ${okCount} loaded, ${failCount} failed in ${Date.now() - start}ms`);
|
|
1868
2554
|
}
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
};
|
|
1881
|
-
var bundleManager = new BundleManager();
|
|
1882
|
-
|
|
1883
|
-
// src/bridge/native.ts
|
|
1884
|
-
var import_react10 = require("react");
|
|
1885
|
-
var import_core7 = require("@ateam-ai-mobile/core");
|
|
1886
|
-
function buildContext(pluginId, connectorId) {
|
|
1887
|
-
const user = (0, import_core7.getUser)();
|
|
1888
|
-
return {
|
|
1889
|
-
tenant: (0, import_core7.getTenant)(),
|
|
1890
|
-
pluginId,
|
|
1891
|
-
connectorId,
|
|
1892
|
-
user: user ? { id: user.actorId || "", name: user.name || "", roles: user.roles || [] } : void 0
|
|
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);
|
|
2555
|
+
/**
|
|
2556
|
+
* Clear all caches (in-memory + storage).
|
|
2557
|
+
*/
|
|
2558
|
+
async clearAll() {
|
|
2559
|
+
this.dropInMemory();
|
|
2560
|
+
this.cacheTenant = null;
|
|
2561
|
+
try {
|
|
2562
|
+
await getBundleStorage().clearAll();
|
|
2563
|
+
} catch (err) {
|
|
2564
|
+
console.warn("[BundleManager] clearAll error:", err);
|
|
2565
|
+
}
|
|
1930
2566
|
}
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
(0, import_core7.postCommandResult)(payload.correlationId, payload.result, payload.error);
|
|
1943
|
-
return;
|
|
1944
|
-
}
|
|
1945
|
-
}, [pluginId]);
|
|
1946
|
-
const onCommand = (0, import_react10.useCallback)((cb) => {
|
|
1947
|
-
commandListenersRef.current.add(cb);
|
|
1948
|
-
if (commandBufferRef) {
|
|
1949
|
-
const buf = commandBufferRef.current;
|
|
1950
|
-
const remaining = [];
|
|
1951
|
-
for (const cmd of buf) {
|
|
1952
|
-
if (cmd.pluginId === pluginId && cmd.command !== "__focus") {
|
|
2567
|
+
/**
|
|
2568
|
+
* Clear cache for a specific plugin/bundle.
|
|
2569
|
+
*/
|
|
2570
|
+
async clearPlugin(pluginId) {
|
|
2571
|
+
const storage = getBundleStorage();
|
|
2572
|
+
const index = await storage.loadIndex();
|
|
2573
|
+
const entry = index[pluginId];
|
|
2574
|
+
if (entry) {
|
|
2575
|
+
this.modules.delete(entry.bundleUrl);
|
|
2576
|
+
delete index[pluginId];
|
|
2577
|
+
await storage.saveIndex(index);
|
|
1953
2578
|
try {
|
|
1954
|
-
|
|
2579
|
+
await storage.deleteBundle(entry.localPath);
|
|
1955
2580
|
} catch {
|
|
1956
2581
|
}
|
|
1957
|
-
} else {
|
|
1958
|
-
remaining.push(cmd);
|
|
1959
2582
|
}
|
|
1960
2583
|
}
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
2584
|
+
/**
|
|
2585
|
+
* What every loaded bundle IS, right now: the sha256 of the bytes running,
|
|
2586
|
+
* the hash the server advertised, and where they came from.
|
|
2587
|
+
*
|
|
2588
|
+
* Deliberately not filtered or prettified — this is the raw answer to "did my
|
|
2589
|
+
* redeploy reach the phone?", and it is comparable directly against the
|
|
2590
|
+
* manifest `bundleHash` and the server's X-Bundle-SHA256 header.
|
|
2591
|
+
*/
|
|
2592
|
+
getLoadedBundles() {
|
|
2593
|
+
return [...this.provenance.values()];
|
|
1967
2594
|
}
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
2595
|
+
// ── Private: module loading pipeline ────────────────────────
|
|
2596
|
+
async loadModule(bundleUrl, version, expectedHash, expectedTenant, expectedEpoch) {
|
|
2597
|
+
if (this.isStale(expectedEpoch, expectedTenant)) {
|
|
2598
|
+
throw new Error("[BundleManager] tenant changed before load started \u2014 discarding (fail closed)");
|
|
2599
|
+
}
|
|
2600
|
+
if (this.modules.has(bundleUrl)) {
|
|
2601
|
+
const prev = this.provenance.get(bundleUrl);
|
|
2602
|
+
if (prev) this.provenance.set(bundleUrl, { ...prev, source: "memory", expected: expectedHash?.toLowerCase() ?? prev.expected });
|
|
2603
|
+
return;
|
|
2604
|
+
}
|
|
2605
|
+
const storage = getBundleStorage();
|
|
2606
|
+
const index = await storage.loadIndex();
|
|
2607
|
+
const expectedLc = expectedHash ? expectedHash.toLowerCase() : null;
|
|
2608
|
+
let content = null;
|
|
2609
|
+
let verifiedHash = null;
|
|
2610
|
+
let servedFrom = "download";
|
|
2611
|
+
for (const entry of Object.values(index)) {
|
|
2612
|
+
if (entry.bundleUrl === bundleUrl && entry.version === version) {
|
|
2613
|
+
const age = Date.now() - entry.timestamp;
|
|
2614
|
+
const hashOk = expectedLc && entry.bundleHash && entry.bundleHash.toLowerCase() === expectedLc;
|
|
2615
|
+
if (age < CACHE_TTL && hashOk) {
|
|
2616
|
+
content = await storage.readBundle(entry.localPath);
|
|
2617
|
+
if (content) {
|
|
2618
|
+
verifiedHash = entry.bundleHash.toLowerCase();
|
|
2619
|
+
servedFrom = "disk-cache";
|
|
2620
|
+
break;
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
if (!content) {
|
|
2626
|
+
const downloaded = await this.download(bundleUrl, expectedLc);
|
|
2627
|
+
content = downloaded.content;
|
|
2628
|
+
verifiedHash = downloaded.sha256.toLowerCase();
|
|
2629
|
+
if (this.isStale(expectedEpoch, expectedTenant)) {
|
|
2630
|
+
throw new Error("[BundleManager] tenant changed during download \u2014 discarding (fail closed)");
|
|
2631
|
+
}
|
|
2632
|
+
try {
|
|
2633
|
+
const localPath = await storage.writeBundle(`${verifiedHash}.js`, content);
|
|
2634
|
+
if (!this.isStale(expectedEpoch, expectedTenant)) {
|
|
2635
|
+
index[bundleUrl] = {
|
|
2636
|
+
localPath,
|
|
2637
|
+
version,
|
|
2638
|
+
timestamp: Date.now(),
|
|
2639
|
+
bundleUrl,
|
|
2640
|
+
bundleHash: downloaded.sha256,
|
|
2641
|
+
tenant: expectedTenant
|
|
2642
|
+
};
|
|
2643
|
+
await storage.saveIndex(index);
|
|
2644
|
+
}
|
|
2645
|
+
} catch (err) {
|
|
2646
|
+
console.warn("[BundleManager] Cache write error:", err);
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
if (this.isStale(expectedEpoch, expectedTenant)) {
|
|
2650
|
+
throw new Error("[BundleManager] tenant changed while loading \u2014 discarding stale bundle before eval (fail closed)");
|
|
2651
|
+
}
|
|
2652
|
+
const mod = this.evaluate(content, bundleUrl, verifiedHash);
|
|
2653
|
+
this.modules.set(bundleUrl, mod);
|
|
2654
|
+
this.provenance.set(bundleUrl, {
|
|
2655
|
+
bundleUrl,
|
|
2656
|
+
version,
|
|
2657
|
+
hash: verifiedHash,
|
|
2658
|
+
expected: expectedLc,
|
|
2659
|
+
source: servedFrom,
|
|
2660
|
+
// The captured owner — same rule as the disk stamp. The final stale check
|
|
2661
|
+
// above guarantees getTenant() === expectedTenant here, but binding it to
|
|
2662
|
+
// the captured value keeps the ownership record impossible to mislabel.
|
|
2663
|
+
tenant: expectedTenant,
|
|
2664
|
+
loadedAt: Date.now()
|
|
2665
|
+
});
|
|
2666
|
+
console.log(
|
|
2667
|
+
`[BundleManager] RUNNING ${bundleUrl} v${version} sha=${(verifiedHash || "none").slice(0, 12)} (${servedFrom}${expectedLc && verifiedHash && expectedLc !== verifiedHash ? `, EXPECTED ${expectedLc.slice(0, 12)} \u2014 STALE` : ""})`
|
|
2668
|
+
);
|
|
2669
|
+
this.registerToolsFromModule(mod, bundleUrl);
|
|
2670
|
+
}
|
|
2671
|
+
/**
|
|
2672
|
+
* Pick up every `tools` section in an evaluated bundle and put it in the
|
|
2673
|
+
* device tool registry. Runs for UI, hybrid and tools-only bundles alike: a
|
|
2674
|
+
* plugin that ships both gets its tools registered without its UI mounting.
|
|
2675
|
+
*/
|
|
2676
|
+
registerToolsFromModule(mod, bundleUrl) {
|
|
2677
|
+
if (!mod) return;
|
|
2678
|
+
for (const key of Object.keys(mod)) {
|
|
2679
|
+
const val = mod[key];
|
|
2680
|
+
if (!val || typeof val !== "object" || !Array.isArray(val.tools) || val.tools.length === 0) {
|
|
2681
|
+
continue;
|
|
2682
|
+
}
|
|
2683
|
+
const pluginId = typeof val.id === "string" ? val.id : `${bundleUrl}#${key}`;
|
|
2684
|
+
try {
|
|
2685
|
+
registerDeviceTools(pluginId, val.tools, val.capabilities, val.stateDomains);
|
|
2686
|
+
this.registeredToolPluginIds.add(pluginId);
|
|
2687
|
+
} catch (err) {
|
|
2688
|
+
console.warn(
|
|
2689
|
+
`[BundleManager] tool registration failed for ${pluginId}:`,
|
|
2690
|
+
err instanceof Error ? err.message : String(err)
|
|
2691
|
+
);
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
/**
|
|
2696
|
+
* Download + integrity-verify a bundle. Refuses to return when:
|
|
2697
|
+
* - manifest hash and computed hash disagree
|
|
2698
|
+
* - server's `X-Bundle-SHA256` disagrees with manifest hash
|
|
2699
|
+
* - no expected hash AND no server hash header (cannot prove the bundle
|
|
2700
|
+
* wasn't tampered with — fail closed rather than trust the wire).
|
|
2701
|
+
*/
|
|
2702
|
+
async download(bundleUrl, expectedHash) {
|
|
2703
|
+
const cacheBust = `${bundleUrl}${bundleUrl.includes("?") ? "&" : "?"}t=${Date.now()}`;
|
|
2704
|
+
const response = await (0, import_core9.apiFetchRaw)(cacheBust);
|
|
2705
|
+
if (!response.ok) {
|
|
2706
|
+
const body = await response.text().catch(() => "");
|
|
2707
|
+
throw new Error(`Download failed: ${response.status} ${response.statusText} \u2014 ${body.substring(0, 200)}`);
|
|
2708
|
+
}
|
|
2709
|
+
const content = await response.text();
|
|
2710
|
+
if (content.length < 50 || content.trimStart().startsWith("<!")) {
|
|
2711
|
+
throw new Error("Bundle content is empty or HTML \u2014 server may have returned an error page");
|
|
2712
|
+
}
|
|
2713
|
+
const headerHash = (response.headers.get("x-bundle-sha256") || "").toLowerCase();
|
|
2714
|
+
const computed = sha256Hex(content);
|
|
2715
|
+
if (expectedHash) {
|
|
2716
|
+
if (computed !== expectedHash) {
|
|
2717
|
+
throw new Error(
|
|
2718
|
+
`Bundle integrity FAIL: expected sha256=${expectedHash.slice(0, 12)}... got ${computed.slice(0, 12)}.... Refusing to evaluate.`
|
|
2719
|
+
);
|
|
2720
|
+
}
|
|
2721
|
+
if (headerHash && headerHash !== expectedHash) {
|
|
2722
|
+
throw new Error(
|
|
2723
|
+
`Bundle hash mismatch: manifest=${expectedHash.slice(0, 12)}... server=${headerHash.slice(0, 12)}.... Refusing to evaluate.`
|
|
2724
|
+
);
|
|
2725
|
+
}
|
|
2726
|
+
} else if (headerHash) {
|
|
2727
|
+
if (computed !== headerHash) {
|
|
2728
|
+
throw new Error(
|
|
2729
|
+
`Bundle body sha256 (${computed.slice(0, 12)}...) does not match server's X-Bundle-SHA256 (${headerHash.slice(0, 12)}...).`
|
|
2730
|
+
);
|
|
2731
|
+
}
|
|
2732
|
+
console.warn(`[BundleManager] No manifest bundleHash \u2014 verified via X-Bundle-SHA256 header only`);
|
|
2733
|
+
} else {
|
|
2734
|
+
throw new Error(
|
|
2735
|
+
`No bundleHash in manifest and no X-Bundle-SHA256 header from server. Refusing to evaluate untrusted bundle.`
|
|
2736
|
+
);
|
|
2737
|
+
}
|
|
2738
|
+
return { content, sha256: computed };
|
|
2739
|
+
}
|
|
2740
|
+
evaluate(content, bundleUrl, verifiedHash) {
|
|
2741
|
+
assertBundleIntegrity(content, verifiedHash, bundleUrl);
|
|
2742
|
+
let cleaned = content.replace(
|
|
2743
|
+
/__toESM\(require\(("[^"]+")\)(?:,\s*\d+)?\)/g,
|
|
2744
|
+
"require($1)"
|
|
2745
|
+
);
|
|
2746
|
+
cleaned = cleaned.replace(
|
|
2747
|
+
/module\.exports\s*=\s*__toCommonJS\((\w+)\)/g,
|
|
2748
|
+
"module.exports = $1"
|
|
2749
|
+
);
|
|
2750
|
+
const mod = { exports: {} };
|
|
1978
2751
|
try {
|
|
1979
|
-
|
|
1980
|
-
|
|
2752
|
+
const factory = new Function(
|
|
2753
|
+
"module",
|
|
2754
|
+
"exports",
|
|
2755
|
+
"require",
|
|
2756
|
+
"__filename",
|
|
2757
|
+
"__dirname",
|
|
2758
|
+
cleaned
|
|
2759
|
+
);
|
|
2760
|
+
factory(mod, mod.exports, requireShim, bundleUrl, "adas-plugin-bundles");
|
|
2761
|
+
} catch (err) {
|
|
2762
|
+
throw new Error(`Bundle evaluation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1981
2763
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
}, [pluginId]);
|
|
1986
|
-
(0, import_react10.useEffect)(() => {
|
|
1987
|
-
const timer = setTimeout(() => {
|
|
1988
|
-
const dispatch = (cmd) => {
|
|
1989
|
-
commandListenersRef.current.forEach((fn) => {
|
|
2764
|
+
const raw = mod.exports;
|
|
2765
|
+
const materialized = {};
|
|
2766
|
+
for (const key of Object.getOwnPropertyNames(raw)) {
|
|
1990
2767
|
try {
|
|
1991
|
-
|
|
2768
|
+
materialized[key] = raw[key];
|
|
1992
2769
|
} catch {
|
|
1993
2770
|
}
|
|
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
2771
|
}
|
|
2002
|
-
|
|
2772
|
+
return materialized;
|
|
2003
2773
|
}
|
|
2004
|
-
|
|
2005
|
-
|
|
2774
|
+
// ── Private: component extraction ───────────────────────────
|
|
2775
|
+
/**
|
|
2776
|
+
* Find a plugin component in a loaded module.
|
|
2777
|
+
* Handles: default export, direct key match, multi-plugin .id match.
|
|
2778
|
+
*/
|
|
2779
|
+
getComponentFromMemory(pluginId, bundleUrl) {
|
|
2780
|
+
const mod = this.modules.get(bundleUrl);
|
|
2781
|
+
if (!mod) return null;
|
|
2782
|
+
const shortName = pluginId.includes(":") ? pluginId.split(":").pop() : pluginId;
|
|
2783
|
+
if (mod[pluginId] && typeof mod[pluginId].Component === "function") {
|
|
2784
|
+
return mod[pluginId];
|
|
2785
|
+
}
|
|
2786
|
+
const pascal = shortName.replace(/(^|-)([a-z])/g, (_, __, c) => c.toUpperCase());
|
|
2787
|
+
if (mod[pascal] && typeof mod[pascal] === "object" && typeof mod[pascal].Component === "function") {
|
|
2788
|
+
return mod[pascal];
|
|
2789
|
+
}
|
|
2790
|
+
if (mod.default && typeof mod.default === "object" && typeof mod.default.Component === "function") {
|
|
2791
|
+
return mod.default;
|
|
2792
|
+
}
|
|
2793
|
+
for (const key of Object.keys(mod)) {
|
|
2794
|
+
const val = mod[key];
|
|
2795
|
+
if (val && typeof val === "object" && val.id === shortName && typeof val.Component === "function") {
|
|
2796
|
+
return val;
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
return null;
|
|
2800
|
+
}
|
|
2801
|
+
describeModule(mod) {
|
|
2802
|
+
if (!mod) return "(empty)";
|
|
2803
|
+
return Object.keys(mod).map((k) => {
|
|
2804
|
+
const v = mod[k];
|
|
2805
|
+
if (v && typeof v === "object" && v.id) return `${k}(id="${v.id}")`;
|
|
2806
|
+
return k;
|
|
2807
|
+
}).join(", ");
|
|
2006
2808
|
}
|
|
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
2809
|
};
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2810
|
+
bundleManager = new BundleManager();
|
|
2811
|
+
}
|
|
2812
|
+
});
|
|
2813
|
+
|
|
2814
|
+
// src/tools/loader.ts
|
|
2815
|
+
var loader_exports = {};
|
|
2816
|
+
__export(loader_exports, {
|
|
2817
|
+
ensureDeviceToolsLoaded: () => ensureDeviceToolsLoaded,
|
|
2818
|
+
resetDeviceToolLoader: () => resetDeviceToolLoader
|
|
2819
|
+
});
|
|
2820
|
+
async function ensureDeviceToolsLoaded({ force = false } = {}) {
|
|
2821
|
+
if (loadedOnce && !force) return listDeviceTools().map((t) => t.name);
|
|
2822
|
+
if (inflight) return inflight;
|
|
2823
|
+
inflight = (async () => {
|
|
2022
2824
|
try {
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2825
|
+
const summaries = await listPlugins();
|
|
2826
|
+
const manifests = await Promise.all(
|
|
2827
|
+
(summaries || []).map(
|
|
2828
|
+
(s) => getPlugin(s.id).catch(() => null)
|
|
2829
|
+
)
|
|
2830
|
+
);
|
|
2831
|
+
const targets = manifests.filter((m) => {
|
|
2832
|
+
const declared = m?.capabilities?.device_tools;
|
|
2833
|
+
return Array.isArray(declared) && declared.length > 0;
|
|
2834
|
+
});
|
|
2835
|
+
if (targets.length === 0) {
|
|
2836
|
+
const recovered = await loadFromDiskCache();
|
|
2837
|
+
if (recovered.length > 0) {
|
|
2838
|
+
console.log(`[deviceTools] catalog listed none \u2014 recovered from cache: ${recovered.join(", ")}`);
|
|
2839
|
+
return recovered;
|
|
2840
|
+
}
|
|
2841
|
+
console.log("[deviceTools] no device tools available yet \u2014 will look again next call");
|
|
2842
|
+
return [];
|
|
2843
|
+
}
|
|
2844
|
+
await Promise.all(
|
|
2845
|
+
targets.map(async (m) => {
|
|
2846
|
+
const bundleUrl = getNativeBundleUrl(m);
|
|
2847
|
+
if (!bundleUrl) {
|
|
2848
|
+
console.warn(`[deviceTools] ${m.id} declares device_tools but has no native bundle`);
|
|
2849
|
+
return;
|
|
2850
|
+
}
|
|
2851
|
+
try {
|
|
2852
|
+
await bundleManager.loadTools(
|
|
2853
|
+
m.id,
|
|
2854
|
+
bundleUrl,
|
|
2855
|
+
m.version || "1.0.0",
|
|
2856
|
+
getNativeBundleHash(m) || void 0
|
|
2857
|
+
);
|
|
2858
|
+
} catch (err) {
|
|
2859
|
+
console.warn(`[deviceTools] ${m.id} failed to load:`, err?.message || String(err));
|
|
2860
|
+
}
|
|
2861
|
+
})
|
|
2862
|
+
);
|
|
2863
|
+
const names = listDeviceTools().map((t) => t.name);
|
|
2864
|
+
loadedOnce = names.length > 0;
|
|
2865
|
+
console.log(`[deviceTools] ready: ${names.length > 0 ? names.join(", ") : "(none registered \u2014 will retry next call)"}`);
|
|
2866
|
+
return names;
|
|
2867
|
+
} finally {
|
|
2868
|
+
inflight = null;
|
|
2026
2869
|
}
|
|
2027
|
-
}
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2870
|
+
})();
|
|
2871
|
+
return inflight;
|
|
2872
|
+
}
|
|
2873
|
+
async function loadFromDiskCache() {
|
|
2874
|
+
try {
|
|
2875
|
+
const index = await getBundleStorage().loadIndex();
|
|
2876
|
+
const entries = Object.values(index || {});
|
|
2877
|
+
if (entries.length === 0) return [];
|
|
2878
|
+
const tenant = (0, import_core10.getTenant)();
|
|
2879
|
+
if (!tenant) {
|
|
2880
|
+
console.warn("[deviceTools] disk-cache recovery skipped \u2014 no active tenant");
|
|
2881
|
+
return [];
|
|
2882
|
+
}
|
|
2883
|
+
const mine = selectOwnedCacheEntries(entries, tenant);
|
|
2884
|
+
const skipped = entries.length - mine.length;
|
|
2885
|
+
if (skipped > 0) {
|
|
2886
|
+
console.log(`[deviceTools] disk-cache recovery: ${mine.length} for "${tenant}", skipped ${skipped} not owned by it`);
|
|
2887
|
+
}
|
|
2888
|
+
if (mine.length === 0) return [];
|
|
2889
|
+
await Promise.all(mine.map(async (e) => {
|
|
2890
|
+
try {
|
|
2891
|
+
await bundleManager.loadTools("cache:" + e.bundleUrl, e.bundleUrl, e.version, e.bundleHash);
|
|
2892
|
+
} catch {
|
|
2893
|
+
}
|
|
2894
|
+
}));
|
|
2895
|
+
return listDeviceTools().map((t) => t.name);
|
|
2896
|
+
} catch (err) {
|
|
2897
|
+
console.warn("[deviceTools] disk-cache recovery failed:", err?.message || String(err));
|
|
2898
|
+
return [];
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
function resetDeviceToolLoader() {
|
|
2902
|
+
loadedOnce = false;
|
|
2903
|
+
}
|
|
2904
|
+
var import_core10, inflight, loadedOnce;
|
|
2905
|
+
var init_loader = __esm({
|
|
2906
|
+
"src/tools/loader.ts"() {
|
|
2907
|
+
"use strict";
|
|
2908
|
+
init_plugins();
|
|
2909
|
+
init_bundleManager();
|
|
2910
|
+
init_registry();
|
|
2911
|
+
init_storage();
|
|
2912
|
+
import_core10 = require("@ateam-ai-mobile/core");
|
|
2913
|
+
inflight = null;
|
|
2914
|
+
loadedOnce = false;
|
|
2915
|
+
}
|
|
2916
|
+
});
|
|
2917
|
+
|
|
2918
|
+
// src/index.ts
|
|
2919
|
+
var index_exports = {};
|
|
2920
|
+
__export(index_exports, {
|
|
2921
|
+
ActionButton: () => ActionButton,
|
|
2922
|
+
ActivityIndicator: () => import_react_native.ActivityIndicator,
|
|
2923
|
+
AdaOrb: () => AdaOrb,
|
|
2924
|
+
Animated: () => import_react_native.Animated,
|
|
2925
|
+
Badge: () => Badge,
|
|
2926
|
+
CameraPreview: () => CameraPreview,
|
|
2927
|
+
Card: () => Card,
|
|
2928
|
+
EmptyState: () => EmptyState,
|
|
2929
|
+
ErrorState: () => ErrorState,
|
|
2930
|
+
FlatList: () => import_react_native.FlatList,
|
|
2931
|
+
Image: () => import_react_native.Image,
|
|
2932
|
+
ListItem: () => ListItem,
|
|
2933
|
+
LoadingState: () => LoadingState,
|
|
2934
|
+
MemoryBundleStorage: () => MemoryBundleStorage,
|
|
2935
|
+
Modal: () => import_react_native.Modal,
|
|
2936
|
+
PLATFORM_VOICE_CALLER: () => PLATFORM_VOICE_CALLER,
|
|
2937
|
+
Platform: () => import_react_native.Platform,
|
|
2938
|
+
PluginSDK: () => PluginSDK,
|
|
2939
|
+
Pressable: () => import_react_native.Pressable,
|
|
2940
|
+
RefreshControl: () => import_react_native.RefreshControl,
|
|
2941
|
+
ScrollView: () => import_react_native.ScrollView,
|
|
2942
|
+
SearchBar: () => SearchBar,
|
|
2943
|
+
SectionList: () => import_react_native.SectionList,
|
|
2944
|
+
StyleSheet: () => import_react_native.StyleSheet,
|
|
2945
|
+
SurfaceCard: () => SurfaceCard,
|
|
2946
|
+
SurfaceDispatcher: () => SurfaceDispatcher,
|
|
2947
|
+
SurfaceDrawer: () => SurfaceDrawer,
|
|
2948
|
+
SurfaceFullscreen: () => SurfaceFullscreen,
|
|
2949
|
+
Switch: () => import_react_native.Switch,
|
|
2950
|
+
TabBar: () => TabBar,
|
|
2951
|
+
Text: () => import_react_native.Text,
|
|
2952
|
+
TextInput: () => import_react_native.TextInput,
|
|
2953
|
+
TouchableOpacity: () => import_react_native.TouchableOpacity,
|
|
2954
|
+
View: () => import_react_native.View,
|
|
2955
|
+
__resetSessionSyncForTest: () => __resetSessionSyncForTest,
|
|
2956
|
+
bundleManager: () => bundleManager,
|
|
2957
|
+
callDeviceTool: () => callDeviceTool,
|
|
2958
|
+
clearDeviceTools: () => clearDeviceTools,
|
|
2959
|
+
createNativeBridge: () => createNativeBridge,
|
|
2960
|
+
deviceStateV2: () => device_state_exports,
|
|
2961
|
+
deviceToolsActionForSession: () => deviceToolsActionForSession,
|
|
2962
|
+
ensureDeviceToolsLoaded: () => ensureDeviceToolsLoaded,
|
|
2963
|
+
explainCapability: () => explainCapability,
|
|
2964
|
+
gateDeviceState: () => gateDeviceState,
|
|
2965
|
+
gatePluginNative: () => gatePluginNative,
|
|
2966
|
+
gateVoice: () => gateVoice,
|
|
2967
|
+
getCameraPreviewProvider: () => getCameraPreviewProvider,
|
|
2968
|
+
getHaptics: () => getHaptics,
|
|
2969
|
+
getNativeBundleHash: () => getNativeBundleHash,
|
|
2970
|
+
getNativeBundleUrl: () => getNativeBundleUrl,
|
|
2971
|
+
getNativeComponentId: () => getNativeComponentId,
|
|
2972
|
+
getPlugin: () => getPlugin,
|
|
2973
|
+
getPluginNative: () => getPluginNative,
|
|
2974
|
+
handleServerDeviceToolCall: () => handleServerDeviceToolCall,
|
|
2975
|
+
hasDeviceTool: () => hasDeviceTool,
|
|
2976
|
+
implementedStateDomains: () => implementedStateDomains,
|
|
2977
|
+
implementedVoice: () => implementedVoice,
|
|
2978
|
+
isActionable: () => isActionable,
|
|
2979
|
+
isDeviceToolCommand: () => isDeviceToolCommand,
|
|
2980
|
+
isInlineSurface: () => isInlineSurface,
|
|
2981
|
+
isMountSurface: () => isMountSurface,
|
|
2982
|
+
listDeviceTools: () => listDeviceTools,
|
|
2983
|
+
listPlugins: () => listPlugins,
|
|
2984
|
+
nativePluginRegistry: () => nativePluginRegistry,
|
|
2985
|
+
neverObserved: () => neverObserved,
|
|
2986
|
+
register: () => register,
|
|
2987
|
+
registerBundleModules: () => registerBundleModules,
|
|
2988
|
+
registerDeviceTools: () => registerDeviceTools,
|
|
2989
|
+
registerNativePlugin: () => registerNativePlugin,
|
|
2990
|
+
registerPlatformDeviceTools: () => registerPlatformDeviceTools,
|
|
2991
|
+
registerService: () => registerService,
|
|
2992
|
+
registerTools: () => registerTools,
|
|
2993
|
+
resetDeviceToolLoader: () => resetDeviceToolLoader,
|
|
2994
|
+
resolvePluginRuntimeProps: () => resolvePluginRuntimeProps,
|
|
2995
|
+
resolvePluginUrl: () => resolvePluginUrl,
|
|
2996
|
+
setBundleStorage: () => setBundleStorage,
|
|
2997
|
+
setCameraPreviewProvider: () => setCameraPreviewProvider,
|
|
2998
|
+
setDeviceStateProvider: () => setDeviceStateProvider,
|
|
2999
|
+
setDeviceToolContextProvider: () => setDeviceToolContextProvider,
|
|
3000
|
+
setDeviceToolResultPoster: () => setDeviceToolResultPoster,
|
|
3001
|
+
setHapticsProvider: () => setHapticsProvider,
|
|
3002
|
+
setPluginNativeProvider: () => setPluginNativeProvider,
|
|
3003
|
+
setPluginThemeColors: () => setPluginThemeColors,
|
|
3004
|
+
setVoiceProvider: () => setVoiceProvider,
|
|
3005
|
+
supportsNative: () => supportsNative,
|
|
3006
|
+
supportsWebView: () => supportsWebView,
|
|
3007
|
+
syncDeviceToolsForSession: () => syncDeviceToolsForSession,
|
|
3008
|
+
unregisterDeviceTools: () => unregisterDeviceTools,
|
|
3009
|
+
unregisterNativePlugin: () => unregisterNativePlugin,
|
|
3010
|
+
useApi: () => useApi,
|
|
3011
|
+
useNativeBridge: () => useNativeBridge,
|
|
3012
|
+
usePlugin: () => usePlugin,
|
|
3013
|
+
usePluginTheme: () => usePluginTheme,
|
|
3014
|
+
useRemotePlugins: () => useRemotePlugins,
|
|
3015
|
+
useSurfaceStore: () => useSurfaceStore
|
|
3016
|
+
});
|
|
3017
|
+
module.exports = __toCommonJS(index_exports);
|
|
3018
|
+
init_sdk();
|
|
3019
|
+
|
|
3020
|
+
// src/sdk/device-state.ts
|
|
3021
|
+
var device_state_exports = {};
|
|
3022
|
+
__export(device_state_exports, {
|
|
3023
|
+
DeviceBridgeError: () => DeviceBridgeError,
|
|
3024
|
+
explainCapability: () => explainCapability,
|
|
3025
|
+
isActionable: () => isActionable,
|
|
3026
|
+
neverObserved: () => neverObserved
|
|
3027
|
+
});
|
|
3028
|
+
var DeviceBridgeError = class extends Error {
|
|
3029
|
+
constructor(code, message, opts) {
|
|
3030
|
+
super(message);
|
|
3031
|
+
this.name = "DeviceBridgeError";
|
|
3032
|
+
this.code = code;
|
|
3033
|
+
this.domain = opts?.domain;
|
|
3034
|
+
this.recoverable = opts?.recoverable;
|
|
3035
|
+
}
|
|
3036
|
+
};
|
|
3037
|
+
var CONFIDENCE_ORDER = {
|
|
3038
|
+
unknown: 0,
|
|
3039
|
+
low: 1,
|
|
3040
|
+
medium: 2,
|
|
3041
|
+
high: 3
|
|
3042
|
+
};
|
|
3043
|
+
function isActionable(quality, budget) {
|
|
3044
|
+
const { freshness, confidence } = quality;
|
|
3045
|
+
if (freshness.state === "unknown" || freshness.state === "stale") return false;
|
|
3046
|
+
if (freshness.ageMs === null || freshness.ageMs > budget.maxAgeMs) return false;
|
|
3047
|
+
if (budget.minConfidence) {
|
|
3048
|
+
const have = CONFIDENCE_ORDER[confidence?.level ?? "unknown"];
|
|
3049
|
+
if (have < CONFIDENCE_ORDER[budget.minConfidence]) return false;
|
|
3050
|
+
}
|
|
3051
|
+
return true;
|
|
2037
3052
|
}
|
|
2038
|
-
function
|
|
2039
|
-
|
|
3053
|
+
function neverObserved() {
|
|
3054
|
+
return { state: "unknown", observedAt: null, ageMs: null };
|
|
3055
|
+
}
|
|
3056
|
+
function explainCapability(status) {
|
|
3057
|
+
if (status.support === "unsupported") return "this device does not have it";
|
|
3058
|
+
if (status.permission === "denied") return "permission was denied";
|
|
3059
|
+
if (status.permission === "restricted") return "permission is restricted";
|
|
3060
|
+
if (status.permission === "notDetermined") return "permission has not been asked for yet";
|
|
3061
|
+
if (status.permission === "limited") return "only limited access was granted";
|
|
3062
|
+
if (status.restriction === "policyRestricted") return "this plugin did not declare it";
|
|
3063
|
+
if (status.restriction === "backgroundRestricted") return "the app is in the background";
|
|
3064
|
+
if (status.restriction === "lowPowerRestricted") return "the device is in low-power mode";
|
|
3065
|
+
if (status.availability === "busy") return "it is in use by something else";
|
|
3066
|
+
if (status.availability === "temporarilyUnavailable") return "it is temporarily unavailable";
|
|
3067
|
+
if (status.availability === "unavailable") return "it is unavailable";
|
|
3068
|
+
if (status.support === "unknown" || status.availability === "unknown") {
|
|
3069
|
+
return "its state could not be determined";
|
|
3070
|
+
}
|
|
3071
|
+
return null;
|
|
3072
|
+
}
|
|
3073
|
+
|
|
3074
|
+
// src/index.ts
|
|
3075
|
+
init_deviceStateHost();
|
|
3076
|
+
|
|
3077
|
+
// src/voiceHost.ts
|
|
3078
|
+
var _provider2 = null;
|
|
3079
|
+
function setVoiceProvider(provider) {
|
|
3080
|
+
_provider2 = provider || null;
|
|
3081
|
+
}
|
|
3082
|
+
function implementedVoice() {
|
|
3083
|
+
return !!_provider2;
|
|
3084
|
+
}
|
|
3085
|
+
function voiceDenial(why) {
|
|
3086
|
+
const err = new Error(`voice: ${why}`);
|
|
3087
|
+
err.code = "RESTRICTED";
|
|
3088
|
+
return err;
|
|
3089
|
+
}
|
|
3090
|
+
function deniedVoice(why) {
|
|
3091
|
+
const fail = () => {
|
|
3092
|
+
throw voiceDenial(why);
|
|
3093
|
+
};
|
|
2040
3094
|
return {
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
cb(buildContext(pluginId, connectorId));
|
|
2044
|
-
}, 0);
|
|
3095
|
+
async start() {
|
|
3096
|
+
return fail();
|
|
2045
3097
|
},
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
if (!targetConnector) {
|
|
2049
|
-
throw new Error(`[NativeBridge:${pluginId}] mcpCall requires a connectorId`);
|
|
2050
|
-
}
|
|
2051
|
-
const data = await (0, import_core7.connectorCall)(targetConnector, tool, args);
|
|
2052
|
-
if (!data.ok && data.error) throw new Error(data.error);
|
|
2053
|
-
return (0, import_core7.unwrapMcpResult)(data.result);
|
|
3098
|
+
stop() {
|
|
3099
|
+
fail();
|
|
2054
3100
|
},
|
|
2055
|
-
|
|
2056
|
-
|
|
3101
|
+
setMuted() {
|
|
3102
|
+
fail();
|
|
2057
3103
|
},
|
|
2058
|
-
|
|
3104
|
+
prompt() {
|
|
3105
|
+
fail();
|
|
2059
3106
|
},
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
return () => {
|
|
2063
|
-
commandListeners.delete(cb);
|
|
2064
|
-
};
|
|
3107
|
+
observe() {
|
|
3108
|
+
fail();
|
|
2065
3109
|
},
|
|
2066
|
-
|
|
2067
|
-
|
|
3110
|
+
getSnapshot() {
|
|
3111
|
+
return {
|
|
3112
|
+
state: "error",
|
|
3113
|
+
sessionId: null,
|
|
3114
|
+
muted: false,
|
|
3115
|
+
speaking: false,
|
|
3116
|
+
listening: false,
|
|
3117
|
+
userSpeaking: false,
|
|
3118
|
+
lastUserSpeechEndedAt: null,
|
|
3119
|
+
lastError: why
|
|
3120
|
+
};
|
|
2068
3121
|
},
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
if (!t) return;
|
|
2072
|
-
try {
|
|
2073
|
-
import_core7.useChatStore.getState().sendMessage(t);
|
|
2074
|
-
} catch (err) {
|
|
2075
|
-
console.warn("[bridge.sendMessage] failed:", err instanceof Error ? err.message : String(err));
|
|
2076
|
-
}
|
|
3122
|
+
subscribe() {
|
|
3123
|
+
return fail();
|
|
2077
3124
|
},
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
if (!t) return;
|
|
2081
|
-
import_core7.pluginCommandEmitter.emit("prepare-chat-message", { text: t });
|
|
3125
|
+
transcript() {
|
|
3126
|
+
return [];
|
|
2082
3127
|
}
|
|
2083
3128
|
};
|
|
2084
3129
|
}
|
|
3130
|
+
function scopedVoice(provider, pluginId) {
|
|
3131
|
+
return {
|
|
3132
|
+
start: (opts) => provider.start(opts, pluginId),
|
|
3133
|
+
stop: () => provider.stop(pluginId),
|
|
3134
|
+
setMuted: (muted) => provider.setMuted(muted, pluginId),
|
|
3135
|
+
prompt: (input) => provider.prompt(input, pluginId),
|
|
3136
|
+
observe: (text) => provider.observe(text, pluginId),
|
|
3137
|
+
getSnapshot: () => provider.getSnapshot(pluginId),
|
|
3138
|
+
subscribe: (cb) => provider.subscribe(cb, pluginId),
|
|
3139
|
+
transcript: () => provider.transcript(pluginId)
|
|
3140
|
+
};
|
|
3141
|
+
}
|
|
3142
|
+
function gateVoice(declared, pluginId) {
|
|
3143
|
+
if (!declared) {
|
|
3144
|
+
return deniedVoice(
|
|
3145
|
+
"not declared in this plugin's capabilities. Add `capabilities: { voice: true }` to use it."
|
|
3146
|
+
);
|
|
3147
|
+
}
|
|
3148
|
+
if (!pluginId) {
|
|
3149
|
+
return deniedVoice(
|
|
3150
|
+
"no plugin identity \u2014 voice fails closed without a caller to scope the session to."
|
|
3151
|
+
);
|
|
3152
|
+
}
|
|
3153
|
+
return _provider2 ? scopedVoice(_provider2, pluginId) : deniedVoice(
|
|
3154
|
+
"no voice session in this build \u2014 the app has not mounted a VoiceSessionHost / called setVoiceProvider."
|
|
3155
|
+
);
|
|
3156
|
+
}
|
|
3157
|
+
|
|
3158
|
+
// src/index.ts
|
|
3159
|
+
init_host();
|
|
3160
|
+
|
|
3161
|
+
// src/pluginRuntime.ts
|
|
3162
|
+
init_host();
|
|
3163
|
+
init_deviceStateHost();
|
|
3164
|
+
function mergeCaps(decl) {
|
|
3165
|
+
if (!decl) return void 0;
|
|
3166
|
+
return { ...decl.native, ...decl.capabilities };
|
|
3167
|
+
}
|
|
3168
|
+
function resolvePluginRuntimeProps(src) {
|
|
3169
|
+
const trusted = src.manifest ?? src.plugin ?? null;
|
|
3170
|
+
const capabilities = mergeCaps(trusted);
|
|
3171
|
+
const stateDomains = trusted?.stateDomains;
|
|
3172
|
+
const native = gatePluginNative(getPluginNative(), capabilities, src.pluginId);
|
|
3173
|
+
const deviceState = gateDeviceState(stateDomains);
|
|
3174
|
+
const voice = gateVoice(!!capabilities?.voice, src.pluginId);
|
|
3175
|
+
return { device: native, native, deviceState, voice };
|
|
3176
|
+
}
|
|
3177
|
+
|
|
3178
|
+
// src/index.ts
|
|
3179
|
+
init_registry();
|
|
3180
|
+
init_loader();
|
|
3181
|
+
|
|
3182
|
+
// src/tools/platformTools.ts
|
|
3183
|
+
init_registry();
|
|
3184
|
+
var PLATFORM_PLUGIN_ID = "platform:device-tools";
|
|
3185
|
+
function registerPlatformDeviceTools() {
|
|
3186
|
+
registerDeviceTools(PLATFORM_PLUGIN_ID, [
|
|
3187
|
+
{
|
|
3188
|
+
name: "device.echo",
|
|
3189
|
+
description: "Echo a string back from the user's own device.",
|
|
3190
|
+
input_schema: {
|
|
3191
|
+
type: "object",
|
|
3192
|
+
properties: { text: { type: "string" } },
|
|
3193
|
+
required: ["text"]
|
|
3194
|
+
},
|
|
3195
|
+
run: (args) => ({
|
|
3196
|
+
ok: true,
|
|
3197
|
+
echoed: args && args.text || "",
|
|
3198
|
+
// The proof: a server-side implementation could not say this truthfully.
|
|
3199
|
+
ran_on: "device"
|
|
3200
|
+
})
|
|
3201
|
+
},
|
|
3202
|
+
{
|
|
3203
|
+
name: "device.ping",
|
|
3204
|
+
description: "Check that the device is reachable.",
|
|
3205
|
+
input_schema: { type: "object", properties: {} },
|
|
3206
|
+
run: () => ({ ok: true, ran_on: "device", at: (/* @__PURE__ */ new Date()).toISOString() })
|
|
3207
|
+
}
|
|
3208
|
+
]);
|
|
3209
|
+
}
|
|
3210
|
+
|
|
3211
|
+
// src/tools/sessionSync.ts
|
|
3212
|
+
init_registry();
|
|
3213
|
+
function deviceToolsActionForSession(next, prevTenant2) {
|
|
3214
|
+
const tenant = next.authenticated ? next.tenant ?? null : null;
|
|
3215
|
+
if (tenant && tenant !== prevTenant2) return { action: "load", tenant };
|
|
3216
|
+
if (!tenant && prevTenant2) return { action: "reset", tenant: null };
|
|
3217
|
+
return { action: "none", tenant: prevTenant2 };
|
|
3218
|
+
}
|
|
3219
|
+
var REAL_DEPS = {
|
|
3220
|
+
clear: clearDeviceTools,
|
|
3221
|
+
registerPlatform: registerPlatformDeviceTools,
|
|
3222
|
+
// `./loader` reaches react-native via the bundle manager; import it only when
|
|
3223
|
+
// an effect actually fires (real app), never at module load (tests).
|
|
3224
|
+
resetLoader: () => {
|
|
3225
|
+
void Promise.resolve().then(() => (init_loader(), loader_exports)).then((m) => m.resetDeviceToolLoader());
|
|
3226
|
+
},
|
|
3227
|
+
load: () => {
|
|
3228
|
+
void Promise.resolve().then(() => (init_loader(), loader_exports)).then((m) => {
|
|
3229
|
+
m.resetDeviceToolLoader();
|
|
3230
|
+
return m.ensureDeviceToolsLoaded({ force: true });
|
|
3231
|
+
});
|
|
3232
|
+
}
|
|
3233
|
+
};
|
|
3234
|
+
var prevTenant = null;
|
|
3235
|
+
function syncDeviceToolsForSession(next, deps = REAL_DEPS) {
|
|
3236
|
+
const { action, tenant } = deviceToolsActionForSession(next, prevTenant);
|
|
3237
|
+
prevTenant = tenant;
|
|
3238
|
+
if (action === "load") {
|
|
3239
|
+
deps.clear();
|
|
3240
|
+
deps.registerPlatform();
|
|
3241
|
+
deps.load();
|
|
3242
|
+
} else if (action === "reset") {
|
|
3243
|
+
deps.resetLoader();
|
|
3244
|
+
}
|
|
3245
|
+
return action;
|
|
3246
|
+
}
|
|
3247
|
+
function __resetSessionSyncForTest() {
|
|
3248
|
+
prevTenant = null;
|
|
3249
|
+
}
|
|
3250
|
+
|
|
3251
|
+
// src/tools/serverCalls.ts
|
|
3252
|
+
init_registry();
|
|
3253
|
+
init_loader();
|
|
3254
|
+
var postResult = null;
|
|
3255
|
+
function setDeviceToolResultPoster(fn) {
|
|
3256
|
+
postResult = fn;
|
|
3257
|
+
}
|
|
3258
|
+
function isDeviceToolCommand(cmd) {
|
|
3259
|
+
return !!cmd && cmd.kind === "device_tool" && typeof cmd.tool === "string" && !!cmd.correlationId;
|
|
3260
|
+
}
|
|
3261
|
+
async function handleServerDeviceToolCall(cmd) {
|
|
3262
|
+
if (!isDeviceToolCommand(cmd)) return;
|
|
3263
|
+
const { tool, args, correlationId } = cmd;
|
|
3264
|
+
if (!postResult) {
|
|
3265
|
+
console.warn("[deviceTools] server called a tool but no result poster is installed");
|
|
3266
|
+
return;
|
|
3267
|
+
}
|
|
3268
|
+
try {
|
|
3269
|
+
await ensureDeviceToolsLoaded();
|
|
3270
|
+
const outcome = await callDeviceTool(tool, args || {});
|
|
3271
|
+
console.log(`[deviceTools] server-called ${tool} \u2192 ${outcome.ok ? "ok" : outcome.reason}`);
|
|
3272
|
+
await postResult(
|
|
3273
|
+
correlationId,
|
|
3274
|
+
outcome.ok ? { ok: true, ...unwrap(outcome.result) } : void 0,
|
|
3275
|
+
outcome.ok ? void 0 : outcome.error
|
|
3276
|
+
);
|
|
3277
|
+
} catch (err) {
|
|
3278
|
+
await postResult(correlationId, void 0, err?.message || String(err)).catch(() => {
|
|
3279
|
+
});
|
|
3280
|
+
}
|
|
3281
|
+
}
|
|
3282
|
+
function unwrap(result) {
|
|
3283
|
+
if (result && typeof result === "object" && !Array.isArray(result)) return result;
|
|
3284
|
+
return { result };
|
|
3285
|
+
}
|
|
3286
|
+
|
|
3287
|
+
// src/index.ts
|
|
3288
|
+
init_bundleManager();
|
|
3289
|
+
init_storage();
|
|
3290
|
+
init_native();
|
|
2085
3291
|
|
|
2086
3292
|
// src/hooks/usePlugin.ts
|
|
2087
3293
|
var import_react11 = require("react");
|
|
3294
|
+
init_bundleManager();
|
|
3295
|
+
init_host();
|
|
2088
3296
|
function extractBundleUrl(pluginId, manifest) {
|
|
2089
3297
|
const render = manifest.render;
|
|
2090
3298
|
if (!render || typeof render !== "object") return { url: null, source: "none" };
|
|
@@ -2187,7 +3395,7 @@ function usePlugin(pluginId, manifest) {
|
|
|
2187
3395
|
|
|
2188
3396
|
// src/hooks/useRemotePlugins.ts
|
|
2189
3397
|
var import_react12 = require("react");
|
|
2190
|
-
var
|
|
3398
|
+
var import_core11 = require("@ateam-ai-mobile/core");
|
|
2191
3399
|
function useRemotePlugins(solutionId) {
|
|
2192
3400
|
const [plugins, setPlugins] = (0, import_react12.useState)([]);
|
|
2193
3401
|
const [loading, setLoading] = (0, import_react12.useState)(true);
|
|
@@ -2199,7 +3407,7 @@ function useRemotePlugins(solutionId) {
|
|
|
2199
3407
|
const targetSolutionId = solutionId || "current";
|
|
2200
3408
|
let data;
|
|
2201
3409
|
try {
|
|
2202
|
-
data = await (0,
|
|
3410
|
+
data = await (0, import_core11.apiFetch)(`/api/solutions/${targetSolutionId}/ui-plugins`);
|
|
2203
3411
|
} catch (err) {
|
|
2204
3412
|
if (err?.message?.includes("404")) {
|
|
2205
3413
|
console.warn(`[useRemotePlugins] Solution ${targetSolutionId} not found or has no plugins`);
|
|
@@ -2255,108 +3463,14 @@ function useRemotePlugins(solutionId) {
|
|
|
2255
3463
|
};
|
|
2256
3464
|
}
|
|
2257
3465
|
|
|
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
|
-
}
|
|
3466
|
+
// src/index.ts
|
|
3467
|
+
init_plugins();
|
|
3468
|
+
init_SurfaceDrawer();
|
|
3469
|
+
init_SurfaceCard();
|
|
3470
|
+
init_SurfaceFullscreen();
|
|
3471
|
+
init_AdaOrb();
|
|
3472
|
+
init_store();
|
|
3473
|
+
init_SurfaceDispatcher();
|
|
2360
3474
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2361
3475
|
0 && (module.exports = {
|
|
2362
3476
|
ActionButton,
|
|
@@ -2364,6 +3478,7 @@ function resolvePluginUrl(manifest, tenant) {
|
|
|
2364
3478
|
AdaOrb,
|
|
2365
3479
|
Animated,
|
|
2366
3480
|
Badge,
|
|
3481
|
+
CameraPreview,
|
|
2367
3482
|
Card,
|
|
2368
3483
|
EmptyState,
|
|
2369
3484
|
ErrorState,
|
|
@@ -2373,6 +3488,7 @@ function resolvePluginUrl(manifest, tenant) {
|
|
|
2373
3488
|
LoadingState,
|
|
2374
3489
|
MemoryBundleStorage,
|
|
2375
3490
|
Modal,
|
|
3491
|
+
PLATFORM_VOICE_CALLER,
|
|
2376
3492
|
Platform,
|
|
2377
3493
|
PluginSDK,
|
|
2378
3494
|
Pressable,
|
|
@@ -2391,30 +3507,60 @@ function resolvePluginUrl(manifest, tenant) {
|
|
|
2391
3507
|
TextInput,
|
|
2392
3508
|
TouchableOpacity,
|
|
2393
3509
|
View,
|
|
3510
|
+
__resetSessionSyncForTest,
|
|
2394
3511
|
bundleManager,
|
|
3512
|
+
callDeviceTool,
|
|
3513
|
+
clearDeviceTools,
|
|
2395
3514
|
createNativeBridge,
|
|
3515
|
+
deviceStateV2,
|
|
3516
|
+
deviceToolsActionForSession,
|
|
3517
|
+
ensureDeviceToolsLoaded,
|
|
3518
|
+
explainCapability,
|
|
3519
|
+
gateDeviceState,
|
|
2396
3520
|
gatePluginNative,
|
|
3521
|
+
gateVoice,
|
|
3522
|
+
getCameraPreviewProvider,
|
|
2397
3523
|
getHaptics,
|
|
2398
3524
|
getNativeBundleHash,
|
|
2399
3525
|
getNativeBundleUrl,
|
|
2400
3526
|
getNativeComponentId,
|
|
2401
3527
|
getPlugin,
|
|
2402
3528
|
getPluginNative,
|
|
3529
|
+
handleServerDeviceToolCall,
|
|
3530
|
+
hasDeviceTool,
|
|
3531
|
+
implementedStateDomains,
|
|
3532
|
+
implementedVoice,
|
|
3533
|
+
isActionable,
|
|
3534
|
+
isDeviceToolCommand,
|
|
2403
3535
|
isInlineSurface,
|
|
2404
3536
|
isMountSurface,
|
|
3537
|
+
listDeviceTools,
|
|
2405
3538
|
listPlugins,
|
|
2406
3539
|
nativePluginRegistry,
|
|
3540
|
+
neverObserved,
|
|
2407
3541
|
register,
|
|
2408
3542
|
registerBundleModules,
|
|
3543
|
+
registerDeviceTools,
|
|
2409
3544
|
registerNativePlugin,
|
|
3545
|
+
registerPlatformDeviceTools,
|
|
2410
3546
|
registerService,
|
|
3547
|
+
registerTools,
|
|
3548
|
+
resetDeviceToolLoader,
|
|
3549
|
+
resolvePluginRuntimeProps,
|
|
2411
3550
|
resolvePluginUrl,
|
|
2412
3551
|
setBundleStorage,
|
|
3552
|
+
setCameraPreviewProvider,
|
|
3553
|
+
setDeviceStateProvider,
|
|
3554
|
+
setDeviceToolContextProvider,
|
|
3555
|
+
setDeviceToolResultPoster,
|
|
2413
3556
|
setHapticsProvider,
|
|
2414
3557
|
setPluginNativeProvider,
|
|
2415
3558
|
setPluginThemeColors,
|
|
3559
|
+
setVoiceProvider,
|
|
2416
3560
|
supportsNative,
|
|
2417
3561
|
supportsWebView,
|
|
3562
|
+
syncDeviceToolsForSession,
|
|
3563
|
+
unregisterDeviceTools,
|
|
2418
3564
|
unregisterNativePlugin,
|
|
2419
3565
|
useApi,
|
|
2420
3566
|
useNativeBridge,
|