@vanira/sdk 0.0.42 → 0.0.44
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/VaniraAI-D2KUxJJO.js +682 -0
- package/dist/VaniraAI-EFYOmJ2E.cjs +1 -0
- package/dist/__tests__/adapters.test.d.ts +14 -0
- package/dist/adapters/PeerConnectionAdapter.d.ts +90 -0
- package/dist/adapters/browser/BrowserAudioAdapter.d.ts +9 -0
- package/dist/adapters/browser/BrowserDataChannelAdapter.d.ts +16 -0
- package/dist/adapters/browser/BrowserMediaAdapter.d.ts +10 -0
- package/dist/adapters/browser/BrowserPeerAdapter.d.ts +9 -0
- package/dist/adapters/browser/index.d.ts +4 -0
- package/dist/adapters/interfaces.d.ts +70 -0
- package/dist/adapters/react-native/RNAudioAdapter.d.ts +9 -0
- package/dist/adapters/react-native/RNDataChannelAdapter.d.ts +16 -0
- package/dist/adapters/react-native/RNMediaAdapter.d.ts +17 -0
- package/dist/adapters/react-native/RNPeerAdapter.d.ts +19 -0
- package/dist/adapters/react-native/index.d.ts +4 -0
- package/dist/browser.d.ts +1 -0
- package/dist/core/VaniraAI.d.ts +2 -0
- package/dist/index.d.ts +21 -3
- package/dist/platforms/browser.cjs +8 -0
- package/dist/platforms/browser.d.ts +28 -0
- package/dist/platforms/browser.js +479 -0
- package/dist/platforms/react-native.cjs +1 -0
- package/dist/platforms/react-native.d.ts +46 -0
- package/dist/platforms/react-native.js +116 -0
- package/dist/react/PresetRenderer.d.ts +1 -2
- package/dist/react/index.d.ts +0 -4
- package/dist/react-native.d.ts +1 -0
- package/dist/runtime/browserRuntime.d.ts +17 -0
- package/dist/runtime/reactNativeRuntime.d.ts +18 -0
- package/dist/runtime/types.d.ts +96 -0
- package/dist/types.d.ts +36 -4
- package/dist/ui/presets/WidgetPresetRenderer.d.ts +2 -0
- package/dist/vanira-sdk.es.js +1795 -231
- package/dist/vanira-sdk.js +36 -36
- package/dist/vanira-sdk.js.map +1 -1
- package/dist/vanira-sdk.umd.js +1538 -0
- package/package.json +18 -3
- package/dist/react/presets/CalendarPreset.d.ts +0 -4
- package/dist/react/presets/CameraPreset.d.ts +0 -16
- package/dist/react/presets/FormPreset.d.ts +0 -4
- package/dist/react/presets/NavigatePreset.d.ts +0 -16
- package/dist/react/presets/UploadPreset.d.ts +0 -17
- package/dist/react/registry.d.ts +0 -3
- package/dist/react/types.d.ts +0 -21
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
var R = Object.defineProperty;
|
|
2
|
+
var H = (i, e, t) => e in i ? R(i, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : i[e] = t;
|
|
3
|
+
var p = (i, e, t) => H(i, typeof e != "symbol" ? e + "" : e, t);
|
|
4
|
+
import { W as x, V as U } from "../VaniraAI-D2KUxJJO.js";
|
|
5
|
+
const f = class f {
|
|
6
|
+
constructor(e) {
|
|
7
|
+
p(this, "tabId");
|
|
8
|
+
p(this, "sessionKey");
|
|
9
|
+
p(this, "channelName");
|
|
10
|
+
p(this, "channel", null);
|
|
11
|
+
p(this, "heartbeatTimer", null);
|
|
12
|
+
p(this, "listeners", /* @__PURE__ */ new Map());
|
|
13
|
+
const t = e.replace(/[^a-z0-9_-]/gi, "_");
|
|
14
|
+
this.sessionKey = `vaniraai_session_${t}`, this.channelName = `vaniraai_channel_${t}`, this.tabId = this._getOrCreateTabId(), this._setupChannel();
|
|
15
|
+
}
|
|
16
|
+
// ─── Public API ──────────────────────────────────────────────────────────
|
|
17
|
+
/** Returns this tab's unique ID (stable across same-tab page refreshes). */
|
|
18
|
+
getTabId() {
|
|
19
|
+
return this.tabId;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Try to claim the session for this tab.
|
|
23
|
+
* Returns true → session claimed (or restored after stale prior tab).
|
|
24
|
+
* Returns false → another live tab already owns the session.
|
|
25
|
+
*/
|
|
26
|
+
claimSession() {
|
|
27
|
+
const e = this._loadSession();
|
|
28
|
+
if (e) {
|
|
29
|
+
const o = Date.now() - e.lastActive, s = e.tabId === this.tabId, n = o > f.HEARTBEAT_TIMEOUT_MS;
|
|
30
|
+
if (s)
|
|
31
|
+
return this._startHeartbeat(), this._emit("session_restored"), !0;
|
|
32
|
+
if (!n)
|
|
33
|
+
return this._emit("tab_conflict"), !1;
|
|
34
|
+
}
|
|
35
|
+
const t = e ? { ...e, tabId: this.tabId, lastActive: Date.now() } : { tabId: this.tabId, prospectId: "", chatId: null, conversationId: null, messages: [], lastActive: Date.now() };
|
|
36
|
+
this._saveSession(t), this._startHeartbeat(), this._broadcast({ type: "took_over", tabId: this.tabId });
|
|
37
|
+
const a = !!(e != null && e.prospectId);
|
|
38
|
+
return this._emit(a ? "session_restored" : "session_claimed"), !0;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Force-claim the session even if another tab owns it.
|
|
42
|
+
* Call this when the user explicitly clicks "Use here" in the conflict UI.
|
|
43
|
+
*/
|
|
44
|
+
forceClaimSession() {
|
|
45
|
+
const e = this._loadSession(), t = e ? { ...e, tabId: this.tabId, lastActive: Date.now() } : { tabId: this.tabId, prospectId: "", chatId: null, conversationId: null, messages: [], lastActive: Date.now() };
|
|
46
|
+
this._saveSession(t), this._startHeartbeat(), this._broadcast({ type: "took_over", tabId: this.tabId }), this._emit("session_claimed");
|
|
47
|
+
}
|
|
48
|
+
/** Save / update the prospect and chat IDs for this session. */
|
|
49
|
+
saveIds(e, t, a) {
|
|
50
|
+
const o = this._loadSession() ?? this._blankSession();
|
|
51
|
+
this._saveSession({ ...o, prospectId: e, chatId: t, conversationId: a ?? o.conversationId ?? null, tabId: this.tabId, lastActive: Date.now() });
|
|
52
|
+
}
|
|
53
|
+
/** Append a message to the persisted history. */
|
|
54
|
+
pushMessage(e, t) {
|
|
55
|
+
const a = this._loadSession() ?? this._blankSession();
|
|
56
|
+
a.messages.push({ role: e, content: t, timestamp: Date.now() }), a.messages.length > 100 && (a.messages = a.messages.slice(-100)), this._saveSession({ ...a, tabId: this.tabId, lastActive: Date.now() });
|
|
57
|
+
}
|
|
58
|
+
/** Update the content of the last assistant message (streaming). */
|
|
59
|
+
updateLastAssistantMessage(e) {
|
|
60
|
+
const t = this._loadSession() ?? this._blankSession(), a = t.messages;
|
|
61
|
+
let o = !1;
|
|
62
|
+
for (let s = a.length - 1; s >= 0; s--)
|
|
63
|
+
if (a[s].role === "assistant" && a[s].content === "") {
|
|
64
|
+
a[s].content = e, o = !0;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
if (!o) {
|
|
68
|
+
for (let s = a.length - 1; s >= 0; s--)
|
|
69
|
+
if (a[s].role === "assistant") {
|
|
70
|
+
a[s].content = e, o = !0;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
o || a.push({ role: "assistant", content: e, timestamp: Date.now() }), this._saveSession({ ...t, tabId: this.tabId, lastActive: Date.now() });
|
|
75
|
+
}
|
|
76
|
+
/** Returns the currently persisted session data (null if none). */
|
|
77
|
+
getSession() {
|
|
78
|
+
return this._loadSession();
|
|
79
|
+
}
|
|
80
|
+
/** Clears ALL session data (prospect_id, chat_id, messages). */
|
|
81
|
+
clearSession() {
|
|
82
|
+
localStorage.removeItem(this.sessionKey), this._broadcast({ type: "session_cleared", tabId: this.tabId }), this._emit("session_cleared");
|
|
83
|
+
}
|
|
84
|
+
/** Clears chat-specific session data (chatId, conversationId, messages) but keeps the prospectId. */
|
|
85
|
+
clearChatKeepProspect() {
|
|
86
|
+
const e = this._loadSession();
|
|
87
|
+
e && this._saveSession({
|
|
88
|
+
...e,
|
|
89
|
+
chatId: null,
|
|
90
|
+
conversationId: null,
|
|
91
|
+
messages: [],
|
|
92
|
+
lastActive: Date.now()
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/** Stop the heartbeat and release resources. Call on widget destroy. */
|
|
96
|
+
destroy() {
|
|
97
|
+
this._stopHeartbeat(), this.channel && (this.channel.close(), this.channel = null);
|
|
98
|
+
}
|
|
99
|
+
// ─── Event Emitter ───────────────────────────────────────────────────────
|
|
100
|
+
on(e, t) {
|
|
101
|
+
return this.listeners.has(e) || this.listeners.set(e, /* @__PURE__ */ new Set()), this.listeners.get(e).add(t), this;
|
|
102
|
+
}
|
|
103
|
+
off(e, t) {
|
|
104
|
+
var a;
|
|
105
|
+
return (a = this.listeners.get(e)) == null || a.delete(t), this;
|
|
106
|
+
}
|
|
107
|
+
// ─── Private ─────────────────────────────────────────────────────────────
|
|
108
|
+
_getOrCreateTabId() {
|
|
109
|
+
let e = sessionStorage.getItem("vaniraai_tab_id");
|
|
110
|
+
return e || (e = `tab_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, sessionStorage.setItem("vaniraai_tab_id", e)), e;
|
|
111
|
+
}
|
|
112
|
+
_setupChannel() {
|
|
113
|
+
typeof BroadcastChannel > "u" || (this.channel = new BroadcastChannel(this.channelName), this.channel.onmessage = (e) => {
|
|
114
|
+
const t = e.data;
|
|
115
|
+
t.tabId !== this.tabId && (t.type === "took_over" && (this._stopHeartbeat(), this._emit("tab_took_over")), t.type, t.type === "session_cleared" && this._emit("session_cleared"));
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
_broadcast(e) {
|
|
119
|
+
var t;
|
|
120
|
+
(t = this.channel) == null || t.postMessage(e);
|
|
121
|
+
}
|
|
122
|
+
_startHeartbeat() {
|
|
123
|
+
this._stopHeartbeat(), this.heartbeatTimer = setInterval(() => {
|
|
124
|
+
const e = this._loadSession();
|
|
125
|
+
e && e.tabId === this.tabId && (this._saveSession({ ...e, lastActive: Date.now() }), this._broadcast({ type: "heartbeat", tabId: this.tabId }));
|
|
126
|
+
}, f.HEARTBEAT_INTERVAL_MS);
|
|
127
|
+
}
|
|
128
|
+
_stopHeartbeat() {
|
|
129
|
+
this.heartbeatTimer !== null && (clearInterval(this.heartbeatTimer), this.heartbeatTimer = null);
|
|
130
|
+
}
|
|
131
|
+
_loadSession() {
|
|
132
|
+
try {
|
|
133
|
+
const e = localStorage.getItem(this.sessionKey);
|
|
134
|
+
return e ? JSON.parse(e) : null;
|
|
135
|
+
} catch {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
_saveSession(e) {
|
|
140
|
+
try {
|
|
141
|
+
localStorage.setItem(this.sessionKey, JSON.stringify(e));
|
|
142
|
+
} catch (t) {
|
|
143
|
+
console.warn("[VaniraAI] SessionManager: failed to save session", t);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
_blankSession() {
|
|
147
|
+
return {
|
|
148
|
+
tabId: this.tabId,
|
|
149
|
+
prospectId: "",
|
|
150
|
+
chatId: null,
|
|
151
|
+
conversationId: null,
|
|
152
|
+
messages: [],
|
|
153
|
+
lastActive: Date.now()
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
_emit(e) {
|
|
157
|
+
var t;
|
|
158
|
+
(t = this.listeners.get(e)) == null || t.forEach((a) => a());
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
/** How often (ms) the active tab broadcasts its heartbeat */
|
|
162
|
+
p(f, "HEARTBEAT_INTERVAL_MS", 5e3), /** How old (ms) a heartbeat must be before we consider the tab dead */
|
|
163
|
+
p(f, "HEARTBEAT_TIMEOUT_MS", 15e3);
|
|
164
|
+
let O = f;
|
|
165
|
+
const j = "https://api.vanira.io";
|
|
166
|
+
class Q {
|
|
167
|
+
static async fetchWidgetConfig(e, t) {
|
|
168
|
+
var o;
|
|
169
|
+
const a = { "Content-Type": "application/json" };
|
|
170
|
+
t && (a["X-API-Key"] = t);
|
|
171
|
+
try {
|
|
172
|
+
const s = await fetch(`${j}/assistant/widget/${e}/config`, {
|
|
173
|
+
method: "GET",
|
|
174
|
+
headers: a
|
|
175
|
+
});
|
|
176
|
+
if (!s.ok)
|
|
177
|
+
throw new Error(`Widget config fetch failed: HTTP ${s.status}`);
|
|
178
|
+
const n = await s.json();
|
|
179
|
+
return (o = n.agent) != null && o.client && (n.client = n.agent.client), n;
|
|
180
|
+
} catch (s) {
|
|
181
|
+
throw console.error("[VaniraAI] Failed to fetch widget config:", s), s;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const L = "https://coredb.travelr.club/v1/graphql";
|
|
186
|
+
let g = "https://inboxapi.travelr.club";
|
|
187
|
+
class Y {
|
|
188
|
+
static setChatUrl(e) {
|
|
189
|
+
g = e;
|
|
190
|
+
}
|
|
191
|
+
static async createChatProspect(e) {
|
|
192
|
+
var a, o;
|
|
193
|
+
const t = `
|
|
194
|
+
mutation CreateChatProspect($prospectGroupId: uuid!, $name: String!) {
|
|
195
|
+
insert_prospects_one(object: {prospect_group_id: $prospectGroupId, name: $name, source: WEBSITE_WIDGET}) {
|
|
196
|
+
id
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
`;
|
|
200
|
+
try {
|
|
201
|
+
const n = await (await fetch(L, {
|
|
202
|
+
method: "POST",
|
|
203
|
+
headers: { "Content-Type": "application/json" },
|
|
204
|
+
body: JSON.stringify({
|
|
205
|
+
query: t,
|
|
206
|
+
variables: {
|
|
207
|
+
prospectGroupId: e,
|
|
208
|
+
name: `Widget Guest ${(/* @__PURE__ */ new Date()).toLocaleString()}`
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
})).json();
|
|
212
|
+
return n.errors ? (console.warn("[VaniraAI] Prospect creation failed, using anonymous ID", n.errors), `anon_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`) : ((o = (a = n.data) == null ? void 0 : a.insert_prospects_one) == null ? void 0 : o.id) || `anon_${Date.now()}`;
|
|
213
|
+
} catch (s) {
|
|
214
|
+
return console.error("[VaniraAI] Failed to create prospect:", s), `anon_${Date.now()}`;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Calls the new Go bridge /widget/chat endpoint with an empty message to initialize the session and get the Welcome config + inbox_id
|
|
218
|
+
static async fetchWelcomeMessage(e, t, a, o) {
|
|
219
|
+
try {
|
|
220
|
+
const s = { "Content-Type": "application/json" }, n = await fetch(`${g}/widget/chat`, {
|
|
221
|
+
method: "POST",
|
|
222
|
+
headers: s,
|
|
223
|
+
body: JSON.stringify({
|
|
224
|
+
agent_id: e,
|
|
225
|
+
...a ? { widget_id: a } : {},
|
|
226
|
+
message: "",
|
|
227
|
+
prospect_id: t,
|
|
228
|
+
stream: !1
|
|
229
|
+
})
|
|
230
|
+
});
|
|
231
|
+
if (!n.ok) throw new Error("Failed to fetch welcome message");
|
|
232
|
+
const r = await n.json();
|
|
233
|
+
let h = r.response || "Hey! how can I help you ?", d;
|
|
234
|
+
const c = r.chat_id || r.inbox_id, u = r.conversation_id || null;
|
|
235
|
+
return r.widget && (d = r.widget), { role: "assistant", content: h, widget: d, chatId: c, conversationId: u };
|
|
236
|
+
} catch (s) {
|
|
237
|
+
return console.error("[VaniraAI] Failed to fetch welcome message:", s), { role: "assistant", content: "Hey! how can I help you ?" };
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
static async sendChatMessage(e, t, a, o, s, n, r, h, d) {
|
|
241
|
+
var c, u, m, w;
|
|
242
|
+
try {
|
|
243
|
+
const I = { "Content-Type": "application/json" }, y = {
|
|
244
|
+
agent_id: e,
|
|
245
|
+
message: a,
|
|
246
|
+
prospect_id: t,
|
|
247
|
+
stream: !0
|
|
248
|
+
};
|
|
249
|
+
o && (y.inbox_id = o), h && (y.widget_id = h);
|
|
250
|
+
const A = await fetch(`${g}/widget/chat`, {
|
|
251
|
+
method: "POST",
|
|
252
|
+
headers: I,
|
|
253
|
+
body: JSON.stringify(y)
|
|
254
|
+
});
|
|
255
|
+
if (!A.ok) throw new Error("Chat request failed");
|
|
256
|
+
const C = (c = A.body) == null ? void 0 : c.getReader(), V = new TextDecoder();
|
|
257
|
+
if (!C) throw new Error("No reader");
|
|
258
|
+
let T = "", S = "", _ = null, b = null;
|
|
259
|
+
for (; ; ) {
|
|
260
|
+
const { done: P, value: k } = await C.read();
|
|
261
|
+
if (P) break;
|
|
262
|
+
S += V.decode(k, { stream: !0 });
|
|
263
|
+
const E = S.split(`
|
|
264
|
+
`);
|
|
265
|
+
S = E.pop() || "";
|
|
266
|
+
for (const M of E) {
|
|
267
|
+
const v = M.trim();
|
|
268
|
+
if (v && v.startsWith("data: ")) {
|
|
269
|
+
const D = v.slice(6);
|
|
270
|
+
if (D === "[DONE]") {
|
|
271
|
+
r(_, b);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
const l = JSON.parse(D);
|
|
276
|
+
if (l.type === "metadata") {
|
|
277
|
+
(l.chat_id || l.inbox_id) && (_ = l.chat_id || l.inbox_id), l.conversation_id && (b = l.conversation_id);
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
const $ = (w = (m = (u = l.choices) == null ? void 0 : u[0]) == null ? void 0 : m.delta) == null ? void 0 : w.content;
|
|
281
|
+
$ && (T += $, s(T)), l.widget && n(l.widget), l.chat_id && !_ && (_ = l.chat_id), l.conversation_id && !b && (b = l.conversation_id);
|
|
282
|
+
} catch {
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
r(_, b);
|
|
288
|
+
} catch (I) {
|
|
289
|
+
console.error("[VaniraAI] Chat error:", I), s("Sorry, I encountered an error. Please try again."), r(null, null);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
static listenForAdminReplies(e, t, a) {
|
|
293
|
+
const o = `${g}/inbox/stream?inbox_id=${e}&sender=${encodeURIComponent(t)}`;
|
|
294
|
+
let s = null, n = 0, r = !1;
|
|
295
|
+
const h = () => {
|
|
296
|
+
r || (s = new EventSource(o), console.log(`[VaniraAI] Subscribed to SSE EventSource at ${s.url}`), s.onopen = () => {
|
|
297
|
+
console.log("[VaniraAI] SSE connection opened successfully."), n = 0;
|
|
298
|
+
}, s.onmessage = (d) => {
|
|
299
|
+
console.log("[VaniraAI] 📡 Raw SSE Event triggered:", d.data);
|
|
300
|
+
try {
|
|
301
|
+
const c = JSON.parse(d.data);
|
|
302
|
+
console.log("[VaniraAI] SSE stream JSON parsed:", c);
|
|
303
|
+
const u = c.direction === "outgoing", m = !!c.content, w = c.source !== "ai";
|
|
304
|
+
u && m && w && (console.log("[VaniraAI] 🎯 Displaying admin message in chat UI:", c.content), a(c.content));
|
|
305
|
+
} catch (c) {
|
|
306
|
+
console.error("[VaniraAI] SSE parse error", c);
|
|
307
|
+
}
|
|
308
|
+
}, s.onerror = (d) => {
|
|
309
|
+
if (console.error("[VaniraAI] SSE connection error or disconnect", d), s && (s.close(), s = null), r) return;
|
|
310
|
+
const c = Math.min(2e3 * Math.pow(2, n), 3e4);
|
|
311
|
+
console.log(`[VaniraAI] Reconnecting SSE in ${c}ms (Attempt ${n + 1})...`), n++, setTimeout(h, c);
|
|
312
|
+
});
|
|
313
|
+
};
|
|
314
|
+
return h(), {
|
|
315
|
+
close: () => {
|
|
316
|
+
r = !0, s && (s.close(), s = null, console.log("[VaniraAI] SSE connection manually closed."));
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
static async createCall(e, t, a, o, s) {
|
|
321
|
+
try {
|
|
322
|
+
const n = "https://api.vanira.io", r = { "Content-Type": "application/json" };
|
|
323
|
+
s && (r["X-API-Key"] = s);
|
|
324
|
+
const h = await fetch(`${n}/calls/create`, {
|
|
325
|
+
method: "POST",
|
|
326
|
+
headers: r,
|
|
327
|
+
body: JSON.stringify({
|
|
328
|
+
agent_id: e,
|
|
329
|
+
prospect_id: a || void 0,
|
|
330
|
+
type: "web"
|
|
331
|
+
})
|
|
332
|
+
}), d = await h.json();
|
|
333
|
+
if (!h.ok)
|
|
334
|
+
throw new Error(`[VaniraAI] Call creation failed HTTP ${h.status}`);
|
|
335
|
+
if (!d.worker_url)
|
|
336
|
+
throw new Error("[VaniraAI] Worker URL missing from response. Call cannot proceed.");
|
|
337
|
+
return {
|
|
338
|
+
callId: d.call_id || d.id || `web_${Date.now()}`,
|
|
339
|
+
workerUrl: d.worker_url
|
|
340
|
+
// Pass the full authenticated URL
|
|
341
|
+
};
|
|
342
|
+
} catch (n) {
|
|
343
|
+
throw console.error("[VaniraAI] Failed to create call:", n), n;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
static async resolveConversation(e) {
|
|
347
|
+
try {
|
|
348
|
+
if (!(await fetch(`${g}/inbox/conversations/resolve`, {
|
|
349
|
+
method: "POST",
|
|
350
|
+
headers: { "Content-Type": "application/json" },
|
|
351
|
+
body: JSON.stringify({ conversation_id: e })
|
|
352
|
+
})).ok) throw new Error("Failed to resolve conversation");
|
|
353
|
+
} catch (t) {
|
|
354
|
+
throw console.error("[VaniraAI] Failed to resolve conversation:", t), t;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
class J {
|
|
359
|
+
constructor(e) {
|
|
360
|
+
p(this, "el");
|
|
361
|
+
this.el = new Audio(), this.el.srcObject = e, this.el.play().catch(
|
|
362
|
+
(t) => console.warn("[BrowserAudio] Autoplay blocked — user gesture may be required:", t)
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
pause() {
|
|
366
|
+
}
|
|
367
|
+
cleanup() {
|
|
368
|
+
this.el.pause(), this.el.srcObject = null;
|
|
369
|
+
}
|
|
370
|
+
onEnded(e) {
|
|
371
|
+
this.el.onended = e;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
class K {
|
|
375
|
+
createRemotePlayer(e) {
|
|
376
|
+
return new J(e);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
class W {
|
|
380
|
+
async getUserAudio(e) {
|
|
381
|
+
return navigator.mediaDevices.getUserMedia({ audio: e });
|
|
382
|
+
}
|
|
383
|
+
stopStream(e) {
|
|
384
|
+
e.getTracks().forEach((t) => t.stop());
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
class F {
|
|
388
|
+
create(e) {
|
|
389
|
+
return new RTCPeerConnection({
|
|
390
|
+
iceServers: e.iceServers,
|
|
391
|
+
iceTransportPolicy: e.iceTransportPolicy ?? "all"
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
class G {
|
|
396
|
+
bind(e, t) {
|
|
397
|
+
return e.onopen = () => t.onOpen(), e.onerror = (a) => t.onError(a), e.onmessage = (a) => {
|
|
398
|
+
if (typeof a.data == "string") {
|
|
399
|
+
t.onMessage({ text: a.data });
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (a.data instanceof ArrayBuffer) {
|
|
403
|
+
try {
|
|
404
|
+
const o = new TextDecoder().decode(a.data), s = JSON.parse(o);
|
|
405
|
+
(s == null ? void 0 : s.event) === "client_tool_call" ? (console.log("[DataChannel] Binary client_tool_call decoded and forwarded"), t.onMessage({ text: o })) : console.log("[DataChannel] Binary frame dropped (not client_tool_call):", s == null ? void 0 : s.event);
|
|
406
|
+
} catch {
|
|
407
|
+
console.log(
|
|
408
|
+
"[DataChannel] Non-decodable binary frame:",
|
|
409
|
+
a.data.byteLength,
|
|
410
|
+
"bytes — dropped"
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
a.data instanceof Blob && a.data.text().then((o) => {
|
|
416
|
+
t.onMessage({ text: o });
|
|
417
|
+
}).catch((o) => {
|
|
418
|
+
console.warn("[DataChannel] Failed to read Blob message:", o);
|
|
419
|
+
});
|
|
420
|
+
}, {
|
|
421
|
+
send(a) {
|
|
422
|
+
e.readyState === "open" && e.send(a);
|
|
423
|
+
},
|
|
424
|
+
isOpen() {
|
|
425
|
+
return e.readyState === "open";
|
|
426
|
+
},
|
|
427
|
+
close() {
|
|
428
|
+
e.close();
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
var B;
|
|
434
|
+
const q = {
|
|
435
|
+
supportsAudioEndedEvent: !0,
|
|
436
|
+
supportsScreenShare: typeof navigator < "u" && typeof ((B = navigator.mediaDevices) == null ? void 0 : B.getDisplayMedia) == "function",
|
|
437
|
+
supportsDom: typeof document < "u",
|
|
438
|
+
supportsHtmlAudio: typeof Audio < "u",
|
|
439
|
+
supportsCustomElements: typeof customElements < "u",
|
|
440
|
+
supportsLocalStorage: typeof localStorage < "u",
|
|
441
|
+
supportsBroadcastChannel: typeof BroadcastChannel < "u"
|
|
442
|
+
}, N = {
|
|
443
|
+
name: "browser",
|
|
444
|
+
callType: "web",
|
|
445
|
+
runtimeName: "browser",
|
|
446
|
+
callIdPrefix: "web_",
|
|
447
|
+
capabilities: q,
|
|
448
|
+
audio: new K(),
|
|
449
|
+
media: new W(),
|
|
450
|
+
peer: new F(),
|
|
451
|
+
dataChannel: new G()
|
|
452
|
+
};
|
|
453
|
+
function Z(i) {
|
|
454
|
+
return new x({
|
|
455
|
+
...i,
|
|
456
|
+
runtime: N
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
function ee(i) {
|
|
460
|
+
return new U({
|
|
461
|
+
...i,
|
|
462
|
+
runtime: N
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
export {
|
|
466
|
+
K as BrowserAudioAdapter,
|
|
467
|
+
G as BrowserDataChannelAdapter,
|
|
468
|
+
W as BrowserMediaAdapter,
|
|
469
|
+
F as BrowserPeerAdapter,
|
|
470
|
+
Y as ChatService,
|
|
471
|
+
Q as ConfigService,
|
|
472
|
+
O as SessionManager,
|
|
473
|
+
U as VaniraAI,
|
|
474
|
+
x as WebRTCClient,
|
|
475
|
+
q as browserCapabilities,
|
|
476
|
+
N as browserRuntime,
|
|
477
|
+
ee as createBrowserAI,
|
|
478
|
+
Z as createBrowserClient
|
|
479
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("../VaniraAI-EFYOmJ2E.cjs");class R{pause(){}cleanup(){}onEnded(e){console.warn("[RNAudio] onEnded is not supported in React Native. The `playedStream` DataChannel event will not be sent to the server. Ensure the backend handles missing playedStream gracefully for RN sessions.")}}class c{createRemotePlayer(e){return new R}}class l{async getUserAudio(e){return navigator.mediaDevices.getUserMedia({audio:e})}stopStream(e){e.getTracks().forEach(r=>r.stop())}}class d{create(e){return new globalThis.RTCPeerConnection({iceServers:e.iceServers,iceTransportPolicy:e.iceTransportPolicy??"all"})}}class p{bind(e,r){return e.onopen=()=>r.onOpen(),e.onerror=t=>r.onError(t),e.onmessage=t=>{if(typeof t.data=="string"){r.onMessage({text:t.data});return}if(t.data instanceof ArrayBuffer){try{const o=new TextDecoder().decode(t.data),i=JSON.parse(o);(i==null?void 0:i.event)==="client_tool_call"&&r.onMessage({text:o})}catch{console.log("[RN DataChannel] Non-decodable binary frame — dropped")}return}console.warn("[RN DataChannel] Unexpected message type:",typeof t.data)},{send(t){e.readyState==="open"&&e.send(t)},isOpen(){return e.readyState==="open"},close(){e.close()}}}}const u={supportsAudioEndedEvent:!1,supportsScreenShare:!1,supportsDom:!1,supportsHtmlAudio:!1,supportsCustomElements:!1,supportsLocalStorage:!1,supportsBroadcastChannel:!1},s={name:"react-native",callType:"web",runtimeName:"react-native",callIdPrefix:"rn_",capabilities:u,audio:new c,media:new l,peer:new d,dataChannel:new p};function f(a){return new n.WebRTCClient({...a,runtime:s})}function N(a){return new n.VaniraAI({...a,runtime:s})}function A(a){return f(a)}function m(a){return N(a)}exports.VaniraAI=n.VaniraAI;exports.WebRTCClient=n.WebRTCClient;exports.RNAudioAdapter=c;exports.RNDataChannelAdapter=p;exports.RNMediaAdapter=l;exports.RNPeerAdapter=d;exports.createReactNativeAI=N;exports.createReactNativeClient=f;exports.createVaniraAI=m;exports.createVaniraClient=A;exports.reactNativeCapabilities=u;exports.reactNativeRuntime=s;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { WebRTCClientConfig } from '../types';
|
|
2
|
+
import { VaniraAIConfig, VaniraAI } from '../core/VaniraAI';
|
|
3
|
+
import { WebRTCClient } from '../core/WebRTCClient';
|
|
4
|
+
/**
|
|
5
|
+
* @vanira/sdk — React Native entry point
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { createReactNativeClient, VaniraAI } from '@vanira/sdk/react-native'
|
|
9
|
+
*
|
|
10
|
+
* Prerequisites in the React Native project:
|
|
11
|
+
* npm install react-native-webrtc permission-handler
|
|
12
|
+
* # iOS: pod install
|
|
13
|
+
* # Add NSMicrophoneUsageDescription to Info.plist
|
|
14
|
+
* # Add RECORD_AUDIO permission to AndroidManifest.xml
|
|
15
|
+
* # Call registerGlobals() from react-native-webrtc at app entry:
|
|
16
|
+
* # import { registerGlobals } from 'react-native-webrtc';
|
|
17
|
+
* # registerGlobals();
|
|
18
|
+
*
|
|
19
|
+
* Platform differences vs browser:
|
|
20
|
+
* - supportsAudioEndedEvent: false — audio routes via native stack, no onended.
|
|
21
|
+
* playedStream is NOT sent from RN. Backend uses estimated-duration fallback.
|
|
22
|
+
* - supportsDom: false — no document/window APIs.
|
|
23
|
+
* - supportsHtmlAudio: false — no Audio element.
|
|
24
|
+
* - supportsCustomElements: false — no customElements.define().
|
|
25
|
+
* - supportsLocalStorage: false — use AsyncStorage instead.
|
|
26
|
+
* - callType: 'web', runtimeName: 'react-native' in /calls/create body.
|
|
27
|
+
*
|
|
28
|
+
* Legacy names:
|
|
29
|
+
* createVaniraClient / createVaniraAI still exported for backward compat.
|
|
30
|
+
*/
|
|
31
|
+
export { createReactNativeClient, createReactNativeAI, reactNativeRuntime, reactNativeCapabilities, } from '../runtime/reactNativeRuntime';
|
|
32
|
+
export { WebRTCClient } from '../core/WebRTCClient';
|
|
33
|
+
export { VaniraAI } from '../core/VaniraAI';
|
|
34
|
+
export { RNAudioAdapter } from '../adapters/react-native/RNAudioAdapter';
|
|
35
|
+
export { RNMediaAdapter } from '../adapters/react-native/RNMediaAdapter';
|
|
36
|
+
export { RNPeerAdapter } from '../adapters/react-native/RNPeerAdapter';
|
|
37
|
+
export { RNDataChannelAdapter } from '../adapters/react-native/RNDataChannelAdapter';
|
|
38
|
+
export type { WebRTCClientConfig, ControlEvent, } from '../types';
|
|
39
|
+
export type { VaniraAIConfig, VaniraAIStatus, ClientToolCall, TranscriptionEvent, } from '../core/VaniraAI';
|
|
40
|
+
export type { AudioAdapter, MediaAdapter, AudioPlayerHandle, MediaAudioConstraints, } from '../adapters/interfaces';
|
|
41
|
+
export type { PeerConnectionAdapter, DataChannelAdapter, PeerConnectionConfig, DataChannelController, } from '../adapters/PeerConnectionAdapter';
|
|
42
|
+
export type { VaniraRuntime, RuntimeCapabilities, } from '../runtime/types';
|
|
43
|
+
/** @deprecated Use createReactNativeClient() instead */
|
|
44
|
+
export declare function createVaniraClient(config: Omit<WebRTCClientConfig, 'audioAdapter' | 'mediaAdapter' | 'peerAdapter' | 'dataChannelAdapter'>): WebRTCClient;
|
|
45
|
+
/** @deprecated Use createReactNativeAI() instead */
|
|
46
|
+
export declare function createVaniraAI(config: VaniraAIConfig): VaniraAI;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { W as i, V as c } from "../VaniraAI-D2KUxJJO.js";
|
|
2
|
+
class l {
|
|
3
|
+
pause() {
|
|
4
|
+
}
|
|
5
|
+
cleanup() {
|
|
6
|
+
}
|
|
7
|
+
onEnded(e) {
|
|
8
|
+
console.warn(
|
|
9
|
+
"[RNAudio] onEnded is not supported in React Native. The `playedStream` DataChannel event will not be sent to the server. Ensure the backend handles missing playedStream gracefully for RN sessions."
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
class d {
|
|
14
|
+
createRemotePlayer(e) {
|
|
15
|
+
return new l();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
class p {
|
|
19
|
+
async getUserAudio(e) {
|
|
20
|
+
return navigator.mediaDevices.getUserMedia({
|
|
21
|
+
audio: e
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
stopStream(e) {
|
|
25
|
+
e.getTracks().forEach((r) => r.stop());
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
class u {
|
|
29
|
+
create(e) {
|
|
30
|
+
return new globalThis.RTCPeerConnection({
|
|
31
|
+
iceServers: e.iceServers,
|
|
32
|
+
iceTransportPolicy: e.iceTransportPolicy ?? "all"
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
class f {
|
|
37
|
+
bind(e, r) {
|
|
38
|
+
return e.onopen = () => r.onOpen(), e.onerror = (t) => r.onError(t), e.onmessage = (t) => {
|
|
39
|
+
if (typeof t.data == "string") {
|
|
40
|
+
r.onMessage({ text: t.data });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (t.data instanceof ArrayBuffer) {
|
|
44
|
+
try {
|
|
45
|
+
const s = new TextDecoder().decode(t.data), n = JSON.parse(s);
|
|
46
|
+
(n == null ? void 0 : n.event) === "client_tool_call" && r.onMessage({ text: s });
|
|
47
|
+
} catch {
|
|
48
|
+
console.log("[RN DataChannel] Non-decodable binary frame — dropped");
|
|
49
|
+
}
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
console.warn("[RN DataChannel] Unexpected message type:", typeof t.data);
|
|
53
|
+
}, {
|
|
54
|
+
send(t) {
|
|
55
|
+
e.readyState === "open" && e.send(t);
|
|
56
|
+
},
|
|
57
|
+
isOpen() {
|
|
58
|
+
return e.readyState === "open";
|
|
59
|
+
},
|
|
60
|
+
close() {
|
|
61
|
+
e.close();
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const m = {
|
|
67
|
+
supportsAudioEndedEvent: !1,
|
|
68
|
+
supportsScreenShare: !1,
|
|
69
|
+
supportsDom: !1,
|
|
70
|
+
supportsHtmlAudio: !1,
|
|
71
|
+
supportsCustomElements: !1,
|
|
72
|
+
supportsLocalStorage: !1,
|
|
73
|
+
supportsBroadcastChannel: !1
|
|
74
|
+
}, o = {
|
|
75
|
+
name: "react-native",
|
|
76
|
+
callType: "web",
|
|
77
|
+
runtimeName: "react-native",
|
|
78
|
+
callIdPrefix: "rn_",
|
|
79
|
+
capabilities: m,
|
|
80
|
+
audio: new d(),
|
|
81
|
+
media: new p(),
|
|
82
|
+
peer: new u(),
|
|
83
|
+
dataChannel: new f()
|
|
84
|
+
};
|
|
85
|
+
function y(a) {
|
|
86
|
+
return new i({
|
|
87
|
+
...a,
|
|
88
|
+
runtime: o
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function N(a) {
|
|
92
|
+
return new c({
|
|
93
|
+
...a,
|
|
94
|
+
runtime: o
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function v(a) {
|
|
98
|
+
return y(a);
|
|
99
|
+
}
|
|
100
|
+
function A(a) {
|
|
101
|
+
return N(a);
|
|
102
|
+
}
|
|
103
|
+
export {
|
|
104
|
+
d as RNAudioAdapter,
|
|
105
|
+
f as RNDataChannelAdapter,
|
|
106
|
+
p as RNMediaAdapter,
|
|
107
|
+
u as RNPeerAdapter,
|
|
108
|
+
c as VaniraAI,
|
|
109
|
+
i as WebRTCClient,
|
|
110
|
+
N as createReactNativeAI,
|
|
111
|
+
y as createReactNativeClient,
|
|
112
|
+
A as createVaniraAI,
|
|
113
|
+
v as createVaniraClient,
|
|
114
|
+
m as reactNativeCapabilities,
|
|
115
|
+
o as reactNativeRuntime
|
|
116
|
+
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import { VaniraPresetRegistry } from './types';
|
|
3
2
|
|
|
4
3
|
export interface BaseClient {
|
|
5
4
|
sendToolResult: (toolCallId: string, result: any) => void;
|
|
@@ -11,7 +10,7 @@ export interface BaseClient {
|
|
|
11
10
|
export interface PresetRendererProps {
|
|
12
11
|
client: BaseClient | null;
|
|
13
12
|
toolCall: any | null;
|
|
14
|
-
registry?:
|
|
13
|
+
registry?: Record<string, any>;
|
|
15
14
|
/** Fired immediately if the toolCall is NOT a preset */
|
|
16
15
|
onCustomTool?: (toolCall: any) => void;
|
|
17
16
|
}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './platforms/react-native'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { VaniraRuntime, RuntimeCapabilities } from './types';
|
|
2
|
+
import { WebRTCClientConfig } from '../types';
|
|
3
|
+
import { WebRTCClient } from '../core/WebRTCClient';
|
|
4
|
+
import { VaniraAI } from '../core/VaniraAI';
|
|
5
|
+
|
|
6
|
+
export declare const browserCapabilities: RuntimeCapabilities;
|
|
7
|
+
export declare const browserRuntime: VaniraRuntime;
|
|
8
|
+
/**
|
|
9
|
+
* Create a WebRTCClient pre-wired for the browser runtime.
|
|
10
|
+
* Adapter fields (audioAdapter, mediaAdapter, peerAdapter, dataChannelAdapter)
|
|
11
|
+
* are set automatically — do not set them manually.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createBrowserClient(config: Omit<WebRTCClientConfig, 'audioAdapter' | 'mediaAdapter' | 'peerAdapter' | 'dataChannelAdapter'>): WebRTCClient;
|
|
14
|
+
/**
|
|
15
|
+
* Create a VaniraAI instance pre-wired for the browser runtime.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createBrowserAI(config: Omit<WebRTCClientConfig, 'audioAdapter' | 'mediaAdapter' | 'peerAdapter' | 'dataChannelAdapter'>): VaniraAI;
|