@origonai/web-chat-sdk 0.1.1 → 0.1.2
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/README.md +14 -8
- package/dist/origon-chat-sdk.js +124 -134
- package/dist/origon-chat-sdk.js.map +1 -1
- package/package.json +1 -1
- package/src/chat.js +1 -1
- package/src/http.js +3 -29
- package/src/index.js +3 -2
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ yarn add @origonai/web-chat-sdk
|
|
|
21
21
|
|
|
22
22
|
## Quick Start
|
|
23
23
|
|
|
24
|
-
###
|
|
24
|
+
### Chat Example
|
|
25
25
|
|
|
26
26
|
```javascript
|
|
27
27
|
import {
|
|
@@ -34,11 +34,14 @@ import {
|
|
|
34
34
|
|
|
35
35
|
// 1. Set up callbacks for state updates
|
|
36
36
|
setCallbacks({
|
|
37
|
-
|
|
38
|
-
console.log('
|
|
37
|
+
onMessageAdd: (message) => {
|
|
38
|
+
console.log('New message:', message)
|
|
39
|
+
},
|
|
40
|
+
onMessageUpdate: (index, updatedMsg) => {
|
|
41
|
+
console.log('Message updated at index:', index, updatedMsg)
|
|
39
42
|
},
|
|
40
43
|
onTyping: (isTyping) => {
|
|
41
|
-
console.log('Agent is typing:', isTyping)
|
|
44
|
+
console.log('Live Agent is typing:', isTyping)
|
|
42
45
|
},
|
|
43
46
|
onSessionUpdate: (sessionId) => {
|
|
44
47
|
console.log('Session ID:', sessionId)
|
|
@@ -63,7 +66,7 @@ await sendMessage({ text: 'Hello!' })
|
|
|
63
66
|
disconnect()
|
|
64
67
|
```
|
|
65
68
|
|
|
66
|
-
###
|
|
69
|
+
### Call Example
|
|
67
70
|
|
|
68
71
|
```javascript
|
|
69
72
|
import {
|
|
@@ -143,7 +146,8 @@ Sets callback functions for chat events.
|
|
|
143
146
|
|
|
144
147
|
```javascript
|
|
145
148
|
setCallbacks({
|
|
146
|
-
|
|
149
|
+
onMessageAdd: (message: Message) => void,
|
|
150
|
+
onMessageUpdate: (index: number, updatedMsg: Message) => void,
|
|
147
151
|
onTyping: (isTyping: boolean) => void,
|
|
148
152
|
onLiveAgentMode: (isLiveAgent: boolean) => void,
|
|
149
153
|
onSessionUpdate: (sessionId: string) => void
|
|
@@ -309,7 +313,8 @@ interface Message {
|
|
|
309
313
|
|
|
310
314
|
```typescript
|
|
311
315
|
interface ChatCallbacks {
|
|
312
|
-
|
|
316
|
+
onMessageAdd?: (message: Message) => void
|
|
317
|
+
onMessageUpdate?: (index: number, updatedMsg: Message) => void
|
|
313
318
|
onTyping?: (isTyping: boolean) => void
|
|
314
319
|
onLiveAgentMode?: (isLiveAgent: boolean) => void
|
|
315
320
|
onSessionUpdate?: (sessionId: string) => void
|
|
@@ -367,7 +372,8 @@ import {
|
|
|
367
372
|
|
|
368
373
|
// Set up both chat and call callbacks
|
|
369
374
|
setCallbacks({
|
|
370
|
-
|
|
375
|
+
onMessageAdd: (message) => addMessageToUI(message),
|
|
376
|
+
onMessageUpdate: (index, updatedMsg) => updateMessageInUI(index, updatedMsg),
|
|
371
377
|
onSessionUpdate: (sessionId) => saveSession(sessionId)
|
|
372
378
|
})
|
|
373
379
|
|
package/dist/origon-chat-sdk.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fetchEventSource as
|
|
1
|
+
import { fetchEventSource as _ } from "@microsoft/fetch-event-source";
|
|
2
2
|
function j() {
|
|
3
3
|
const e = Date.now(), t = new Uint8Array(16);
|
|
4
4
|
crypto.getRandomValues(t), t[0] = e >> 40 & 255, t[1] = e >> 32 & 255, t[2] = e >> 24 & 255, t[3] = e >> 16 & 255, t[4] = e >> 8 & 255, t[5] = e & 255, t[6] = t[6] & 15 | 112, t[8] = t[8] & 63 | 128;
|
|
@@ -27,7 +27,7 @@ function Y(e) {
|
|
|
27
27
|
}
|
|
28
28
|
return t;
|
|
29
29
|
}
|
|
30
|
-
const
|
|
30
|
+
const E = {
|
|
31
31
|
BOT: "assistant",
|
|
32
32
|
// this can be automated or LLM AI Agent response
|
|
33
33
|
USER: "user",
|
|
@@ -52,32 +52,32 @@ function L(e = {}) {
|
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
let o = L();
|
|
55
|
-
function
|
|
55
|
+
function pe(e) {
|
|
56
56
|
o.callbacks = { ...o.callbacks, ...e };
|
|
57
57
|
}
|
|
58
|
-
function
|
|
58
|
+
function me(e) {
|
|
59
59
|
console.log("Initializing chat...", e), o.credentials = e, e.token && (o.authenticated = !0);
|
|
60
60
|
}
|
|
61
|
-
function
|
|
61
|
+
function J() {
|
|
62
62
|
return o.credentials;
|
|
63
63
|
}
|
|
64
|
-
function
|
|
64
|
+
function Z(e) {
|
|
65
65
|
var t, s;
|
|
66
66
|
e && e !== o.sessionId && (o.sessionId = e, (s = (t = o.callbacks).onSessionUpdate) == null || s.call(t, e));
|
|
67
67
|
}
|
|
68
|
-
async function
|
|
68
|
+
async function Se(e = {}) {
|
|
69
69
|
var t;
|
|
70
70
|
try {
|
|
71
71
|
console.log("startChat: ", e, o);
|
|
72
72
|
let s = null;
|
|
73
|
-
o.authenticated ? s = o.configData : (s = await
|
|
73
|
+
o.authenticated ? s = o.configData : (s = await ne(o.credentials), o.authenticated = !0, o.configData = s);
|
|
74
74
|
let a = [];
|
|
75
75
|
if (e.sessionId) {
|
|
76
|
-
const
|
|
77
|
-
a = ((t =
|
|
76
|
+
const u = await te(e.sessionId);
|
|
77
|
+
a = ((t = u == null ? void 0 : u.sessionHistory) != null ? t : []).map((r) => ({
|
|
78
78
|
id: r.id,
|
|
79
79
|
text: r.text,
|
|
80
|
-
role: r.youtubeVideo ?
|
|
80
|
+
role: r.youtubeVideo ? E.BOT : r.role,
|
|
81
81
|
timestamp: r.timestamp,
|
|
82
82
|
video: r.youtubeVideo,
|
|
83
83
|
channel: r.channel,
|
|
@@ -93,13 +93,13 @@ async function Ie(e = {}) {
|
|
|
93
93
|
configData: s
|
|
94
94
|
};
|
|
95
95
|
} catch (s) {
|
|
96
|
-
throw console.error(`Failed to start chat: ${s.message}`),
|
|
96
|
+
throw console.error(`Failed to start chat: ${s.message}`), z(), s;
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
-
function
|
|
100
|
-
|
|
99
|
+
function Ce() {
|
|
100
|
+
z();
|
|
101
101
|
}
|
|
102
|
-
function
|
|
102
|
+
function z() {
|
|
103
103
|
o.abortController && o.abortController.abort();
|
|
104
104
|
const { callbacks: e, credentials: t } = o;
|
|
105
105
|
o = L(e), o.credentials = t, console.log("Chat session cleaned up");
|
|
@@ -108,45 +108,45 @@ function $() {
|
|
|
108
108
|
var e;
|
|
109
109
|
return (e = o.credentials) != null && e.externalId ? o.credentials.externalId : V();
|
|
110
110
|
}
|
|
111
|
-
function
|
|
111
|
+
function Ie({ text: e, html: t }) {
|
|
112
112
|
return new Promise((s, a) => {
|
|
113
113
|
(async () => {
|
|
114
|
-
var l,
|
|
114
|
+
var l, u, r, k, i, w, M;
|
|
115
115
|
try {
|
|
116
|
-
const
|
|
117
|
-
role:
|
|
116
|
+
const S = {
|
|
117
|
+
role: E.USER,
|
|
118
118
|
text: e,
|
|
119
119
|
html: t,
|
|
120
120
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
121
121
|
};
|
|
122
|
-
o.messages = [...o.messages,
|
|
123
|
-
const
|
|
124
|
-
role:
|
|
122
|
+
o.messages = [...o.messages, S], (u = (l = o.callbacks).onMessageAdd) == null || u.call(l, S), await Q(200);
|
|
123
|
+
const y = {
|
|
124
|
+
role: E.BOT,
|
|
125
125
|
text: "",
|
|
126
126
|
loading: !0
|
|
127
127
|
};
|
|
128
|
-
o.messages = [...o.messages,
|
|
128
|
+
o.messages = [...o.messages, y], (k = (r = o.callbacks).onMessageAdd) == null || k.call(r, y);
|
|
129
129
|
const g = new URL(o.sseUrl);
|
|
130
130
|
o.sessionId && g.searchParams.set("sessionId", o.sessionId), o.requestId && g.searchParams.set("requestId", o.requestId), o.lastStreamId = void 0, o.abortController = new AbortController();
|
|
131
|
-
const
|
|
131
|
+
const h = {
|
|
132
132
|
"Content-Type": "application/json"
|
|
133
133
|
};
|
|
134
|
-
(i = o.credentials) != null && i.token && (
|
|
134
|
+
(i = o.credentials) != null && i.token && (h.Authorization = `Bearer ${o.credentials.token}`), await _(g.toString(), {
|
|
135
135
|
method: "POST",
|
|
136
|
-
headers:
|
|
136
|
+
headers: h,
|
|
137
137
|
body: JSON.stringify({
|
|
138
138
|
message: e,
|
|
139
139
|
html: t
|
|
140
140
|
}),
|
|
141
141
|
signal: o.abortController.signal,
|
|
142
|
-
onopen: async (
|
|
143
|
-
if (!
|
|
144
|
-
throw console.error("Failed to send message bad response: ",
|
|
142
|
+
onopen: async (d) => {
|
|
143
|
+
if (!d.ok)
|
|
144
|
+
throw console.error("Failed to send message bad response: ", d), new Error("Failed to send message");
|
|
145
145
|
},
|
|
146
|
-
onmessage: (
|
|
147
|
-
var
|
|
148
|
-
console.log("Event: ",
|
|
149
|
-
const c = JSON.parse(
|
|
146
|
+
onmessage: (d) => {
|
|
147
|
+
var b, O, x, P, U, D, N, q, F;
|
|
148
|
+
console.log("Event: ", d);
|
|
149
|
+
const c = JSON.parse(d.data);
|
|
150
150
|
if (c.status === "connected")
|
|
151
151
|
o.sessionId = c.sessionId, o.requestId = c.requestId;
|
|
152
152
|
else if (c.message !== void 0) {
|
|
@@ -156,63 +156,55 @@ function ke({ text: e, html: t }) {
|
|
|
156
156
|
else if (c.streamId !== o.lastStreamId) {
|
|
157
157
|
o.lastStreamId = c.streamId;
|
|
158
158
|
const p = {
|
|
159
|
-
role:
|
|
159
|
+
role: E.BOT,
|
|
160
160
|
text: "",
|
|
161
161
|
loading: !0
|
|
162
162
|
};
|
|
163
|
-
o.messages = [...o.messages, p], (O = (
|
|
163
|
+
o.messages = [...o.messages, p], (O = (b = o.callbacks).onMessageAdd) == null || O.call(b, p);
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
|
-
const
|
|
166
|
+
const C = o.messages.length - 1, f = o.messages[C], T = {
|
|
167
167
|
...f,
|
|
168
168
|
loading: !1,
|
|
169
169
|
text: (f.text || "") + c.message,
|
|
170
170
|
done: (x = c.done) != null ? x : f.done
|
|
171
171
|
};
|
|
172
172
|
o.messages = o.messages.map(
|
|
173
|
-
(p, R) => R ===
|
|
174
|
-
), (U = (P = o.callbacks).onMessageUpdate) == null || U.call(P,
|
|
173
|
+
(p, R) => R === C ? T : p
|
|
174
|
+
), (U = (P = o.callbacks).onMessageUpdate) == null || U.call(P, C, T), c.done && s(o.sessionId), o.sessionId = (D = c.session_id) != null ? D : o.sessionId, o.requestId = (N = c.requestId) != null ? N : o.requestId;
|
|
175
175
|
} else if (c.error) {
|
|
176
|
-
const
|
|
176
|
+
const C = "Failed to connect to the system", f = o.messages.length - 1, p = {
|
|
177
177
|
...o.messages[f],
|
|
178
178
|
loading: !1,
|
|
179
|
-
errorText:
|
|
179
|
+
errorText: C
|
|
180
180
|
};
|
|
181
181
|
o.messages = o.messages.map(
|
|
182
|
-
(R,
|
|
183
|
-
), (F = (q = o.callbacks).onMessageUpdate) == null || F.call(q, f, p), a(new Error(
|
|
182
|
+
(R, K) => K === f ? p : R
|
|
183
|
+
), (F = (q = o.callbacks).onMessageUpdate) == null || F.call(q, f, p), a(new Error(C));
|
|
184
184
|
}
|
|
185
185
|
},
|
|
186
|
-
onerror: (
|
|
187
|
-
throw
|
|
186
|
+
onerror: (d) => {
|
|
187
|
+
throw d;
|
|
188
188
|
},
|
|
189
189
|
openWhenHidden: !0
|
|
190
190
|
});
|
|
191
|
-
} catch (
|
|
192
|
-
console.error("Failed to send message: ",
|
|
193
|
-
const
|
|
194
|
-
...
|
|
191
|
+
} catch (S) {
|
|
192
|
+
console.error("Failed to send message: ", S);
|
|
193
|
+
const y = "Failed to connect to the system", g = o.messages.length - 1, h = o.messages[g], d = {
|
|
194
|
+
...h,
|
|
195
195
|
loading: !1,
|
|
196
|
-
errorText:
|
|
196
|
+
errorText: h.done ? void 0 : S.message || y,
|
|
197
197
|
done: !0
|
|
198
198
|
};
|
|
199
199
|
o.messages = o.messages.map(
|
|
200
|
-
(c,
|
|
201
|
-
), (M = (
|
|
200
|
+
(c, b) => b === g ? d : c
|
|
201
|
+
), (M = (w = o.callbacks).onMessageUpdate) == null || M.call(w, g, d), a(S);
|
|
202
202
|
}
|
|
203
203
|
})();
|
|
204
204
|
});
|
|
205
205
|
}
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
endpoint: null
|
|
209
|
-
};
|
|
210
|
-
function te(e) {
|
|
211
|
-
z = {
|
|
212
|
-
endpoint: e.endpoint
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
async function oe(e) {
|
|
206
|
+
const X = "Something went wrong initializing the chat", ee = "Chat SDK not initialized";
|
|
207
|
+
async function ne(e) {
|
|
216
208
|
const { endpoint: t } = e, s = `${t}/config`, a = await fetch(s, {
|
|
217
209
|
method: "GET",
|
|
218
210
|
headers: {
|
|
@@ -221,12 +213,11 @@ async function oe(e) {
|
|
|
221
213
|
});
|
|
222
214
|
if (!a.ok) {
|
|
223
215
|
const r = await a.json();
|
|
224
|
-
throw new Error((r == null ? void 0 : r.error) ||
|
|
216
|
+
throw new Error((r == null ? void 0 : r.error) || X);
|
|
225
217
|
}
|
|
226
|
-
|
|
227
|
-
return te({ endpoint: t }), d;
|
|
218
|
+
return (await a.json()).data;
|
|
228
219
|
}
|
|
229
|
-
async function
|
|
220
|
+
async function he() {
|
|
230
221
|
const e = new URLSearchParams({
|
|
231
222
|
externalId: $()
|
|
232
223
|
}), t = await G(`/sessions?${e.toString()}`, "GET");
|
|
@@ -234,7 +225,7 @@ async function we() {
|
|
|
234
225
|
throw new Error("Unable to load history, please try again later");
|
|
235
226
|
return t.json();
|
|
236
227
|
}
|
|
237
|
-
async function
|
|
228
|
+
async function te(e) {
|
|
238
229
|
const t = new URLSearchParams({
|
|
239
230
|
sessionId: e
|
|
240
231
|
}), s = await G(`/session?${t.toString()}`, "GET");
|
|
@@ -243,10 +234,10 @@ async function se(e) {
|
|
|
243
234
|
return s.json();
|
|
244
235
|
}
|
|
245
236
|
async function G(e, t = "GET", s = null) {
|
|
246
|
-
const
|
|
247
|
-
if (!a)
|
|
248
|
-
throw new Error(
|
|
249
|
-
const l = `${
|
|
237
|
+
const a = J();
|
|
238
|
+
if (!(a != null && a.endpoint))
|
|
239
|
+
throw new Error(ee);
|
|
240
|
+
const l = `${endpoint}${e}`;
|
|
250
241
|
return fetch(l, {
|
|
251
242
|
headers: {
|
|
252
243
|
"Content-Type": "application/json"
|
|
@@ -274,14 +265,14 @@ function W(e = {}) {
|
|
|
274
265
|
};
|
|
275
266
|
}
|
|
276
267
|
let n = W();
|
|
277
|
-
const
|
|
268
|
+
const oe = {
|
|
278
269
|
iceServers: [{ urls: "stun:stun.l.google.com:19302" }, { urls: "stun:stun1.l.google.com:19302" }]
|
|
279
270
|
};
|
|
280
|
-
function
|
|
271
|
+
function ke(e) {
|
|
281
272
|
n.callbacks = { ...n.callbacks, ...e };
|
|
282
273
|
}
|
|
283
274
|
function B() {
|
|
284
|
-
n.peerConnection && (n.peerConnection.close(), n.peerConnection = null), n.localStream && (n.localStream.getTracks().forEach((t) => t.stop()), n.localStream = null), n.remoteStream && (n.remoteStream = null), n.remoteAudio && (n.remoteAudio.srcObject = null, n.remoteAudio.parentNode && n.remoteAudio.parentNode.removeChild(n.remoteAudio), n.remoteAudio = null), n.socket && (n.socket.close(), n.socket = null),
|
|
275
|
+
n.peerConnection && (n.peerConnection.close(), n.peerConnection = null), n.localStream && (n.localStream.getTracks().forEach((t) => t.stop()), n.localStream = null), n.remoteStream && (n.remoteStream = null), n.remoteAudio && (n.remoteAudio.srcObject = null, n.remoteAudio.parentNode && n.remoteAudio.parentNode.removeChild(n.remoteAudio), n.remoteAudio = null), n.socket && (n.socket.close(), n.socket = null), A();
|
|
285
276
|
const e = n.callbacks;
|
|
286
277
|
n = W(e), console.log("Call session cleaned up");
|
|
287
278
|
}
|
|
@@ -289,15 +280,15 @@ function m(e) {
|
|
|
289
280
|
var t, s;
|
|
290
281
|
n.callStatus = e, (s = (t = n.callbacks).onCallStatus) == null || s.call(t, e);
|
|
291
282
|
}
|
|
292
|
-
function
|
|
283
|
+
function v(e) {
|
|
293
284
|
var t, s;
|
|
294
285
|
(s = (t = n.callbacks).onCallError) == null || s.call(t, e);
|
|
295
286
|
}
|
|
296
|
-
function
|
|
287
|
+
function A() {
|
|
297
288
|
n.pingInterval && (clearInterval(n.pingInterval), n.pingInterval = null);
|
|
298
289
|
}
|
|
299
|
-
function
|
|
300
|
-
|
|
290
|
+
function se() {
|
|
291
|
+
A(), n.pingInterval = setInterval(() => {
|
|
301
292
|
if (n.socket && n.socket.readyState === WebSocket.OPEN) {
|
|
302
293
|
n.pingCount++;
|
|
303
294
|
const e = {
|
|
@@ -305,15 +296,15 @@ function re() {
|
|
|
305
296
|
timestamp: Date.now(),
|
|
306
297
|
count: n.pingCount
|
|
307
298
|
};
|
|
308
|
-
|
|
299
|
+
I(e), console.log(`Sending keep-alive ping #${n.pingCount}`);
|
|
309
300
|
} else
|
|
310
|
-
console.log("Socket not open, stopping ping interval"),
|
|
301
|
+
console.log("Socket not open, stopping ping interval"), A();
|
|
311
302
|
}, 1e4);
|
|
312
303
|
}
|
|
313
|
-
function
|
|
304
|
+
function ae() {
|
|
314
305
|
n.lastPongTime = Date.now(), console.log(`Received pong #${n.pingCount}`);
|
|
315
306
|
}
|
|
316
|
-
function
|
|
307
|
+
function I(e) {
|
|
317
308
|
if (!n.socket) {
|
|
318
309
|
console.error("Failed to send event: no socket instance");
|
|
319
310
|
return;
|
|
@@ -324,7 +315,7 @@ function C(e) {
|
|
|
324
315
|
}
|
|
325
316
|
n.socket.send(JSON.stringify(e));
|
|
326
317
|
}
|
|
327
|
-
async function
|
|
318
|
+
async function re() {
|
|
328
319
|
try {
|
|
329
320
|
n.localStream = await navigator.mediaDevices.getUserMedia({
|
|
330
321
|
audio: !0,
|
|
@@ -334,11 +325,11 @@ async function ie() {
|
|
|
334
325
|
throw console.error(`Failed to get audio media: ${e.message}`), e;
|
|
335
326
|
}
|
|
336
327
|
}
|
|
337
|
-
function
|
|
338
|
-
n.peerConnection = new RTCPeerConnection(
|
|
328
|
+
function ce() {
|
|
329
|
+
n.peerConnection = new RTCPeerConnection(oe), n.peerConnection.onicecandidate = (e) => {
|
|
339
330
|
if (e.candidate) {
|
|
340
331
|
const t = JSON.stringify(e.candidate);
|
|
341
|
-
n.peerConnection && n.peerConnection.remoteDescription ? (
|
|
332
|
+
n.peerConnection && n.peerConnection.remoteDescription ? (I({
|
|
342
333
|
type: "ice",
|
|
343
334
|
data: {
|
|
344
335
|
candidate: t
|
|
@@ -354,14 +345,14 @@ function le() {
|
|
|
354
345
|
console.log(`ICE connection state: ${n.peerConnection.iceConnectionState}`);
|
|
355
346
|
};
|
|
356
347
|
}
|
|
357
|
-
function
|
|
348
|
+
function ie(e) {
|
|
358
349
|
return new Promise((t, s) => {
|
|
359
350
|
if (n.socket && (n.socket.readyState === WebSocket.CONNECTING || n.socket.readyState === WebSocket.OPEN)) {
|
|
360
351
|
console.log("Socket in connecting/open state, returning."), t(n.socket.readyState === WebSocket.OPEN);
|
|
361
352
|
return;
|
|
362
353
|
}
|
|
363
354
|
console.log("Initializing socket connection..");
|
|
364
|
-
const a =
|
|
355
|
+
const a = J();
|
|
365
356
|
if (!a || !a.endpoint) {
|
|
366
357
|
s(new Error("SDK not initialized. Please initialize SDK first."));
|
|
367
358
|
return;
|
|
@@ -375,58 +366,58 @@ function de(e) {
|
|
|
375
366
|
);
|
|
376
367
|
return;
|
|
377
368
|
}
|
|
378
|
-
const
|
|
379
|
-
externalId:
|
|
369
|
+
const u = $(), r = new URLSearchParams({
|
|
370
|
+
externalId: u
|
|
380
371
|
});
|
|
381
372
|
e.sessionId && r.set("sessionId", e.sessionId), a.token && r.set("token", a.token);
|
|
382
|
-
const
|
|
383
|
-
n.socket = new WebSocket(
|
|
384
|
-
console.log("Socket connection established: ", i),
|
|
373
|
+
const k = `${l}?${r.toString()}`;
|
|
374
|
+
n.socket = new WebSocket(k), n.socket.onopen = (i) => {
|
|
375
|
+
console.log("Socket connection established: ", i), se(), t(!0);
|
|
385
376
|
}, n.socket.onmessage = (i) => {
|
|
386
|
-
const
|
|
387
|
-
|
|
377
|
+
const w = JSON.parse(i.data);
|
|
378
|
+
le(w);
|
|
388
379
|
}, n.socket.onerror = (i) => {
|
|
389
|
-
console.error("Socket error: ", i), m("error"),
|
|
380
|
+
console.error("Socket error: ", i), m("error"), v(i.message || "Unable to connect voice"), s(i);
|
|
390
381
|
}, n.socket.onclose = (i) => {
|
|
391
|
-
console.log("Socket connection closed: ", i),
|
|
382
|
+
console.log("Socket connection closed: ", i), A();
|
|
392
383
|
};
|
|
393
384
|
});
|
|
394
385
|
}
|
|
395
|
-
function
|
|
386
|
+
function le(e) {
|
|
396
387
|
switch (console.log("Handling socket server event: ", e), e.type) {
|
|
397
388
|
case "pong":
|
|
398
|
-
|
|
389
|
+
ae();
|
|
399
390
|
break;
|
|
400
391
|
case "answer":
|
|
401
|
-
|
|
392
|
+
de(e.data);
|
|
402
393
|
break;
|
|
403
394
|
case "ice":
|
|
404
|
-
|
|
395
|
+
ue(e.data);
|
|
405
396
|
break;
|
|
406
397
|
case "renegotiationOffer":
|
|
407
|
-
|
|
398
|
+
ge(e.data);
|
|
408
399
|
break;
|
|
409
400
|
case "end":
|
|
410
401
|
H();
|
|
411
402
|
break;
|
|
412
403
|
case "error":
|
|
413
|
-
m("error"),
|
|
404
|
+
m("error"), v(e.error || "Unable to connect voice");
|
|
414
405
|
break;
|
|
415
406
|
default:
|
|
416
407
|
console.log("Unknown call event type: ", e.type);
|
|
417
408
|
break;
|
|
418
409
|
}
|
|
419
410
|
}
|
|
420
|
-
async function
|
|
411
|
+
async function de(e) {
|
|
421
412
|
try {
|
|
422
|
-
if (console.log("Received answer"), n.sessionId = e.sessionId,
|
|
413
|
+
if (console.log("Received answer"), n.sessionId = e.sessionId, Z(e.sessionId), n.peerConnection) {
|
|
423
414
|
const t = new RTCSessionDescription({
|
|
424
415
|
type: "answer",
|
|
425
416
|
sdp: e.sdp
|
|
426
417
|
});
|
|
427
418
|
console.log("Setting remote description answer: ", t), await n.peerConnection.setRemoteDescription(t), console.log("Remote description set");
|
|
428
419
|
for (const s of n.localIceCandidates)
|
|
429
|
-
|
|
420
|
+
I({
|
|
430
421
|
type: "ice",
|
|
431
422
|
data: {
|
|
432
423
|
candidate: s
|
|
@@ -446,7 +437,7 @@ async function ge(e) {
|
|
|
446
437
|
console.error(`Failed to handle answer: ${t.message}`);
|
|
447
438
|
}
|
|
448
439
|
}
|
|
449
|
-
async function
|
|
440
|
+
async function ue(e) {
|
|
450
441
|
try {
|
|
451
442
|
if (n.peerConnection) {
|
|
452
443
|
if (!n.peerConnection.remoteDescription) {
|
|
@@ -460,7 +451,7 @@ async function fe(e) {
|
|
|
460
451
|
console.error(`Failed to add ICE candidate: ${t.message}`);
|
|
461
452
|
}
|
|
462
453
|
}
|
|
463
|
-
async function
|
|
454
|
+
async function ge(e) {
|
|
464
455
|
try {
|
|
465
456
|
if (console.log("Received renegotiation offer"), n.peerConnection) {
|
|
466
457
|
const t = new RTCSessionDescription({
|
|
@@ -469,7 +460,7 @@ async function pe(e) {
|
|
|
469
460
|
});
|
|
470
461
|
console.log("Setting remote description offer: ", t), await n.peerConnection.setRemoteDescription(t), console.log("Remote description set");
|
|
471
462
|
const s = await n.peerConnection.createAnswer();
|
|
472
|
-
await n.peerConnection.setLocalDescription(s),
|
|
463
|
+
await n.peerConnection.setLocalDescription(s), I({
|
|
473
464
|
type: "renegotiationAnswer",
|
|
474
465
|
data: {
|
|
475
466
|
sdp: s.sdp
|
|
@@ -480,32 +471,32 @@ async function pe(e) {
|
|
|
480
471
|
console.error(`Failed to handle renegotiation offer: ${t.message}`);
|
|
481
472
|
}
|
|
482
473
|
}
|
|
483
|
-
async function
|
|
474
|
+
async function we(e = {}) {
|
|
484
475
|
try {
|
|
485
476
|
if (n.callStatus === "connecting" || n.callStatus === "connected") {
|
|
486
477
|
console.log(`Call already in ${n.callStatus} state`);
|
|
487
478
|
return;
|
|
488
479
|
}
|
|
489
|
-
console.log("Starting audio call..."), m("connecting"),
|
|
480
|
+
console.log("Starting audio call..."), m("connecting"), v(null), n.sessionId = e.sessionId, await re(), ce(), n.localStream.getTracks().forEach((s) => {
|
|
490
481
|
n.peerConnection.addTrack(s, n.localStream), console.log(`Added ${s.kind} track`);
|
|
491
|
-
}), await
|
|
482
|
+
}), await ie(e);
|
|
492
483
|
const t = await n.peerConnection.createOffer();
|
|
493
|
-
await n.peerConnection.setLocalDescription(t),
|
|
484
|
+
await n.peerConnection.setLocalDescription(t), I({
|
|
494
485
|
type: "offer",
|
|
495
486
|
data: {
|
|
496
487
|
sdp: t.sdp
|
|
497
488
|
}
|
|
498
489
|
}), console.log("Call initiated successfully");
|
|
499
490
|
} catch (t) {
|
|
500
|
-
console.log("error: ", t), console.error(`Failed to start call: ${t.message}`), m("error"),
|
|
491
|
+
console.log("error: ", t), console.error(`Failed to start call: ${t.message}`), m("error"), v(t.message || "Unable to connect voice"), B();
|
|
501
492
|
}
|
|
502
493
|
}
|
|
503
494
|
function H() {
|
|
504
|
-
|
|
495
|
+
I({
|
|
505
496
|
type: "end"
|
|
506
497
|
}), n.socket && (n.socket.close(), n.socket = null), m("disconnected"), n.peerConnection && (n.peerConnection.close(), n.peerConnection = null), n.localStream && (n.localStream.getTracks().forEach((e) => e.stop()), n.localStream = null), B();
|
|
507
498
|
}
|
|
508
|
-
function
|
|
499
|
+
function ye() {
|
|
509
500
|
if (n.localStream) {
|
|
510
501
|
const e = n.localStream.getAudioTracks()[0];
|
|
511
502
|
if (e)
|
|
@@ -513,10 +504,10 @@ function Ee() {
|
|
|
513
504
|
}
|
|
514
505
|
return !1;
|
|
515
506
|
}
|
|
516
|
-
function
|
|
507
|
+
function be() {
|
|
517
508
|
return n.localStream;
|
|
518
509
|
}
|
|
519
|
-
function
|
|
510
|
+
function Ee() {
|
|
520
511
|
return new Promise((e, t) => {
|
|
521
512
|
if (!n.peerConnection) {
|
|
522
513
|
t(new Error("no peer connection"));
|
|
@@ -531,7 +522,7 @@ function Ae() {
|
|
|
531
522
|
});
|
|
532
523
|
});
|
|
533
524
|
}
|
|
534
|
-
function
|
|
525
|
+
function ve() {
|
|
535
526
|
return new Promise((e, t) => {
|
|
536
527
|
if (!n.peerConnection) {
|
|
537
528
|
t(new Error("no peer connection"));
|
|
@@ -547,22 +538,21 @@ function Te() {
|
|
|
547
538
|
});
|
|
548
539
|
}
|
|
549
540
|
export {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
he as disconnect,
|
|
541
|
+
E as MESSAGE_ROLES,
|
|
542
|
+
ne as authenticate,
|
|
543
|
+
Ce as disconnect,
|
|
554
544
|
H as disconnectCall,
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
545
|
+
he as getHistory,
|
|
546
|
+
Ee as getInboundAudioEnergy,
|
|
547
|
+
be as getLocalStream,
|
|
548
|
+
te as getMessages,
|
|
549
|
+
ve as getOutboundAudioEnergy,
|
|
550
|
+
me as initialize,
|
|
551
|
+
Ie as sendMessage,
|
|
552
|
+
ke as setCallCallbacks,
|
|
553
|
+
pe as setCallbacks,
|
|
554
|
+
we as startCall,
|
|
555
|
+
Se as startChat,
|
|
556
|
+
ye as toggleMute
|
|
567
557
|
};
|
|
568
558
|
//# sourceMappingURL=origon-chat-sdk.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"origon-chat-sdk.js","sources":["../src/utils.js","../src/constants.js","../src/chat.js","../src/http.js","../src/call.js"],"sourcesContent":["/**\n * Utility functions for the Chat SDK\n */\n\nexport function uuidv7() {\n const timestamp = Date.now()\n const bytes = new Uint8Array(16)\n crypto.getRandomValues(bytes)\n\n // Set timestamp (48 bits)\n bytes[0] = (timestamp >> 40) & 0xff\n bytes[1] = (timestamp >> 32) & 0xff\n bytes[2] = (timestamp >> 24) & 0xff\n bytes[3] = (timestamp >> 16) & 0xff\n bytes[4] = (timestamp >> 8) & 0xff\n bytes[5] = timestamp & 0xff\n\n // Set version 7 (0111)\n bytes[6] = (bytes[6] & 0x0f) | 0x70\n\n // Set variant (10xx)\n bytes[8] = (bytes[8] & 0x3f) | 0x80\n\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('')\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(\n 16,\n 20\n )}-${hex.slice(20)}`\n}\n\nexport function getDeviceId() {\n if (localStorage.getItem('chatDeviceId')) {\n return localStorage.getItem('chatDeviceId')\n }\n\n const deviceId = uuidv7()\n localStorage.setItem('chatDeviceId', deviceId)\n return deviceId\n}\n\nexport function parseJwt(token) {\n try {\n const base64Url = token.split('.')[1]\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')\n const jsonPayload = decodeURIComponent(\n atob(base64)\n .split('')\n .map(function (c) {\n return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)\n })\n .join('')\n )\n\n return JSON.parse(jsonPayload)\n } catch {\n return null\n }\n}\n\nexport async function sleep(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nexport function getSocketEndpoint(baseUrl) {\n let socketEndpoint\n try {\n const url = new URL(baseUrl)\n socketEndpoint = `wss://${url.hostname}${url.pathname}/wss`\n } catch {\n console.error('Invalid base URL: ', baseUrl)\n }\n return socketEndpoint\n}\n\nexport function getCallServerEndpoint(baseUrl) {\n let socketEndpoint\n try {\n const url = new URL(baseUrl)\n socketEndpoint = `wss://${url.hostname}${url.pathname}/audio`\n } catch {\n console.error('getCallServerEndpoint: Invalid base URL: ', baseUrl)\n }\n return socketEndpoint\n}\n","/**\n * Constants for the Chat SDK\n */\n\nexport const MESSAGE_ROLES = {\n BOT: 'assistant', // this can be automated or LLM AI Agent response\n USER: 'user', // this is widget user\n AGENT: 'agent', // this is human agent (dock side)\n SYSTEM: 'system' // this is system message, for ex \"Agent joined\" / \"Agent left\"\n}\n","/**\n * Chat Service for Chat SDK\n * Handles real-time chat functionality without depending on external state\n * Uses callbacks to communicate state changes to the consumer\n */\n\nimport { fetchEventSource } from '@microsoft/fetch-event-source'\nimport { getMessages, authenticate } from './http.js'\nimport { getDeviceId, sleep } from './utils.js'\nimport { MESSAGE_ROLES } from './constants.js'\n\n/**\n * @typedef {Object} ChatCallbacks\n * @property {(message: Object) => void} [onMessageAdd] - Called when a new message is added\n * @property {(index: number, updatedMsg: Object) => void} [onMessageUpdate] - Called when an existing message is updated\n * @property {(sessionId: string) => void} [onSessionUpdate] - Called when session ID is updated\n */\n\n/**\n * @typedef {Object} ChatSession\n * @property {string} sessionId\n * @property {string} sseUrl\n * @property {string} [requestId]\n * @property {AbortController} [abortController]\n * @property {string} [lastStreamId]\n * @property {Array} messages\n * @property {ChatCallbacks} callbacks\n */\n\n/**\n * Create a new chat session\n * @param {ChatCallbacks} [callbacks={}]\n * @returns {ChatSession}\n */\nfunction createSession(callbacks = {}) {\n return {\n credentials: undefined,\n authenticated: false,\n configData: undefined,\n sessionId: undefined,\n requestId: undefined,\n sseUrl: undefined,\n abortController: undefined,\n lastStreamId: undefined,\n messages: [],\n callbacks\n }\n}\n\n/** @type {ChatSession} */\nlet currentSession = createSession()\n\n/**\n * Set callbacks for the current session\n * @param {ChatCallbacks} callbacks\n */\nexport function setCallbacks(callbacks) {\n currentSession.callbacks = { ...currentSession.callbacks, ...callbacks }\n}\n\n/**\n * Initialize the chat session\n * @param {Object} credentials - Credentials for the chat\n */\nexport function initialize(credentials) {\n console.log('Initializing chat...', credentials)\n currentSession.credentials = credentials\n if (credentials.token) {\n currentSession.authenticated = true\n }\n}\n\n/**\n * Get current chat session credentials\n * @returns {{ endpoint: string, apiKey: string } | undefined}\n */\nexport function getCredentials() {\n return currentSession.credentials\n}\n\n/**\n * Update the session ID and notify via callback\n * @param {string} sessionId - The new session ID\n */\nexport function updateSessionId(sessionId) {\n if (sessionId && sessionId !== currentSession.sessionId) {\n currentSession.sessionId = sessionId\n currentSession.callbacks.onSessionUpdate?.(sessionId)\n }\n}\n\n/**\n * Initiate a new chat session or resume an existing one\n * @param {Object} credentials - Credentials for the chat\n * @param {Object} payload - Payload for the chat. It contains sessionId (optional)\n * @param {string} [payload.sessionId] - Optional session ID to resume\n * @returns {Promise<{ sessionId: string, messages: Array }>}\n */\nexport async function startChat(payload = {}) {\n try {\n console.log('startChat: ', payload, currentSession)\n\n let configData = null\n if (!currentSession.authenticated) {\n configData = await authenticate(currentSession.credentials)\n currentSession.authenticated = true\n currentSession.configData = configData\n } else {\n configData = currentSession.configData\n }\n\n let messages = []\n\n if (payload.sessionId) {\n const messagesRes = await getMessages(payload.sessionId)\n messages = (messagesRes?.sessionHistory ?? []).map((msg) => ({\n id: msg.id,\n text: msg.text,\n role: msg.youtubeVideo\n ? MESSAGE_ROLES.BOT // for youtube video messages, role is \"system\" from backend, we need to make it \"assistant\"\n : msg.role,\n timestamp: msg.timestamp,\n video: msg.youtubeVideo,\n channel: msg.channel,\n done: true\n }))\n }\n\n const searchParams = new URLSearchParams({\n externalId: getExternalId()\n })\n currentSession.sseUrl = `${currentSession.credentials.endpoint}?${searchParams.toString()}`\n currentSession.sessionId = payload.sessionId\n currentSession.messages = messages\n\n console.log('Chat initiated successfully')\n\n return {\n sessionId: currentSession.sessionId,\n messages,\n configData\n }\n } catch (error) {\n console.error(`Failed to start chat: ${error.message}`)\n cleanup()\n throw error\n }\n}\n\n/**\n * Disconnect from the current chat session\n */\nexport function disconnect() {\n cleanup()\n}\n\n/**\n * Clean up the current session\n */\nfunction cleanup() {\n if (currentSession.abortController) {\n currentSession.abortController.abort()\n }\n\n const { callbacks, credentials } = currentSession\n currentSession = createSession(callbacks)\n currentSession.credentials = credentials\n\n console.log('Chat session cleaned up')\n}\n\nexport function getExternalId() {\n if (currentSession.credentials?.externalId) {\n return currentSession.credentials.externalId\n }\n return getDeviceId()\n}\n\n/**\n * Send a message in the current chat session\n * @param {{ text: string, html?: string }} message\n * @returns {Promise<string>}\n */\nexport function sendMessage({ text, html }) {\n return new Promise((resolve, reject) => {\n ;(async () => {\n try {\n // Add user message\n const userMessage = {\n role: MESSAGE_ROLES.USER,\n text,\n html,\n timestamp: new Date().toISOString()\n }\n currentSession.messages = [...currentSession.messages, userMessage]\n currentSession.callbacks.onMessageAdd?.(userMessage)\n\n await sleep(200)\n\n const loadingMessage = {\n role: MESSAGE_ROLES.BOT,\n text: '',\n loading: true\n }\n currentSession.messages = [...currentSession.messages, loadingMessage]\n currentSession.callbacks.onMessageAdd?.(loadingMessage)\n\n const url = new URL(currentSession.sseUrl)\n if (currentSession.sessionId) {\n url.searchParams.set('sessionId', currentSession.sessionId)\n }\n if (currentSession.requestId) {\n url.searchParams.set('requestId', currentSession.requestId)\n }\n\n currentSession.lastStreamId = undefined\n\n // Create a new abort controller for this request\n currentSession.abortController = new AbortController()\n\n const headers = {\n 'Content-Type': 'application/json'\n }\n if (currentSession.credentials?.token) {\n headers.Authorization = `Bearer ${currentSession.credentials.token}`\n }\n\n await fetchEventSource(url.toString(), {\n method: 'POST',\n headers,\n body: JSON.stringify({\n message: text,\n html\n }),\n signal: currentSession.abortController.signal,\n onopen: async (response) => {\n if (!response.ok) {\n console.error('Failed to send message bad response: ', response)\n throw new Error('Failed to send message')\n }\n },\n onmessage: (event) => {\n console.log('Event: ', event)\n const data = JSON.parse(event.data)\n\n if (data.status === 'connected') {\n currentSession.sessionId = data.sessionId\n currentSession.requestId = data.requestId\n } else if (data.message !== undefined) {\n // If streamId changes, start a new assistant message\n if (data.streamId !== undefined) {\n if (currentSession.lastStreamId === undefined) {\n currentSession.lastStreamId = data.streamId\n } else if (data.streamId !== currentSession.lastStreamId) {\n currentSession.lastStreamId = data.streamId\n const newBotMessage = {\n role: MESSAGE_ROLES.BOT,\n text: '',\n loading: true\n }\n currentSession.messages = [...currentSession.messages, newBotMessage]\n currentSession.callbacks.onMessageAdd?.(newBotMessage)\n }\n }\n\n // Update the last message with new content\n const lastIndex = currentSession.messages.length - 1\n const lastMsg = currentSession.messages[lastIndex]\n const updatedMsg = {\n ...lastMsg,\n loading: false,\n text: (lastMsg.text || '') + data.message,\n done: data.done ?? lastMsg.done\n }\n currentSession.messages = currentSession.messages.map((msg, index) =>\n index === lastIndex ? updatedMsg : msg\n )\n\n currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)\n\n if (data.done) {\n resolve(currentSession.sessionId)\n }\n\n // Store session info for reuse\n currentSession.sessionId = data.session_id ?? currentSession.sessionId\n currentSession.requestId = data.requestId ?? currentSession.requestId\n } else if (data.error) {\n const errorMessage = 'Failed to connect to the system'\n const lastIndex = currentSession.messages.length - 1\n const lastMsg = currentSession.messages[lastIndex]\n const updatedMsg = {\n ...lastMsg,\n loading: false,\n errorText: errorMessage\n }\n currentSession.messages = currentSession.messages.map((msg, index) =>\n index === lastIndex ? updatedMsg : msg\n )\n currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)\n reject(new Error(errorMessage))\n }\n },\n onerror: (error) => {\n throw error // Rethrow to stop retries\n },\n openWhenHidden: true\n })\n } catch (error) {\n console.error('Failed to send message: ', error)\n const errorMessage = 'Failed to connect to the system'\n const lastIndex = currentSession.messages.length - 1\n const lastMsg = currentSession.messages[lastIndex]\n const updatedMsg = {\n ...lastMsg,\n loading: false,\n errorText: errorMessage,\n done: true\n }\n currentSession.messages = currentSession.messages.map((msg, index) =>\n index === lastIndex ? updatedMsg : msg\n )\n currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)\n reject(error)\n }\n })()\n })\n}\n","/**\n * API Service for Chat SDK\n * Handles all HTTP requests without depending on external state\n */\n\nimport { getExternalId } from './chat.js'\n\nconst AUTHENTICATION_ERROR = 'Something went wrong initializing the chat'\nconst INITIALIZATION_ERROR = 'Chat SDK not initialized'\n\n// Module-level configuration\nlet _config = {\n endpoint: null\n}\n\n/**\n * Configure the API service with endpoint\n * @param {{ endpoint: string }} credentials\n */\nexport function configure(credentials) {\n _config = {\n endpoint: credentials.endpoint\n }\n}\n\n/**\n * Get current configuration\n * @returns {{ endpoint: string | null }}\n */\nexport function getConfig() {\n return { ..._config }\n}\n\n/**\n * Authenticate with the chat service\n * @param {{ endpoint: string }} credentials\n * @returns {Promise<object>} Authentication response data\n */\nexport async function authenticate(payload) {\n const { endpoint } = payload\n const url = `${endpoint}/config`\n\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json'\n }\n })\n\n if (!response.ok) {\n const errorPayload = await response.json()\n throw new Error(errorPayload?.error || AUTHENTICATION_ERROR)\n }\n\n const res = await response.json()\n const data = res.data\n\n // Store endpoint for subsequent requests\n configure({ endpoint })\n\n return data\n}\n\n/**\n * Get chat history for the current device\n * @returns {Promise<{ sessions: Array }>}\n */\nexport async function getHistory() {\n const queryParams = new URLSearchParams({\n externalId: getExternalId()\n })\n const response = await fetchRequest(`/sessions?${queryParams.toString()}`, 'GET')\n\n if (!response.ok) {\n throw new Error('Unable to load history, please try again later')\n }\n\n return response.json()\n}\n\n/**\n * Get messages for a specific session\n * @param {string} sessionId\n * @returns {Promise<{ sessionHistory: Array }>}\n */\nexport async function getMessages(sessionId) {\n const queryParams = new URLSearchParams({\n sessionId\n })\n const response = await fetchRequest(`/session?${queryParams.toString()}`, 'GET')\n\n if (!response.ok) {\n throw new Error('Unable to load messages, please try again later')\n }\n\n return response.json()\n}\n\n/**\n * Internal fetch request helper\n * @param {string} pathname\n * @param {string} method\n * @param {object|null} body\n * @returns {Promise<Response>}\n */\nasync function fetchRequest(pathname, method = 'GET', body = null) {\n const { endpoint } = _config\n\n if (!endpoint) {\n throw new Error(INITIALIZATION_ERROR)\n }\n\n const url = `${endpoint}${pathname}`\n\n return fetch(url, {\n headers: {\n 'Content-Type': 'application/json'\n },\n method,\n body: body ? JSON.stringify(body) : null\n })\n}\n","/**\n * Socket Service for Call SDK\n * Handles WebRTC call functionality without depending on external state\n * Uses callbacks to communicate state changes to the consumer\n */\n\nimport { getCallServerEndpoint } from './utils.js'\nimport { getCredentials, getExternalId, updateSessionId } from './chat.js'\n\n/**\n * @typedef {Object} CallCallbacks\n * @property {(status: string) => void} [onCallStatus] - Called when call status changes\n * @property {(error: string | null) => void} [onCallError] - Called when call error occurs\n */\n\n/**\n * @typedef {Object} CallSession\n * @property {string} [sessionId]\n * @property {WebSocket} [socket]\n * @property {RTCPeerConnection} [peerConnection]\n * @property {MediaStream} [localStream]\n * @property {MediaStream} [remoteStream]\n * @property {HTMLAudioElement} [remoteAudio]\n * @property {boolean} isMuted\n * @property {string} callStatus\n * @property {NodeJS.Timeout} [pingInterval]\n * @property {number} pingCount\n * @property {number | null} lastPongTime\n * @property {CallCallbacks} callbacks\n * @property {string[]} localIceCandidates - Queued local ICE candidates to send after remote description is set\n * @property {string[]} pendingRemoteIceCandidates - Queued remote ICE candidates to add after remote description is set\n */\n\n/**\n * Create a new call session\n * @param {CallCallbacks} [callbacks={}]\n * @returns {CallSession}\n */\nfunction createSession(callbacks = {}) {\n return {\n sessionId: undefined,\n socket: null,\n peerConnection: null,\n localStream: null,\n remoteStream: null,\n remoteAudio: null,\n isMuted: false,\n callStatus: 'disconnected',\n pingInterval: null,\n pingCount: 0,\n lastPongTime: null,\n callbacks,\n localIceCandidates: [],\n pendingRemoteIceCandidates: []\n }\n}\n\n/** @type {CallSession} */\nlet currentSession = createSession()\n\nconst rtcConfig = {\n iceServers: [{ urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun1.l.google.com:19302' }]\n}\n\n/**\n * Set callbacks for the current session\n * @param {CallCallbacks} callbacks\n */\nexport function setCallCallbacks(callbacks) {\n currentSession.callbacks = { ...currentSession.callbacks, ...callbacks }\n}\n\n/**\n * Clean up the current session\n */\nfunction cleanup() {\n if (currentSession.peerConnection) {\n currentSession.peerConnection.close()\n currentSession.peerConnection = null\n }\n\n if (currentSession.localStream) {\n currentSession.localStream.getTracks().forEach((track) => track.stop())\n currentSession.localStream = null\n }\n\n if (currentSession.remoteStream) {\n currentSession.remoteStream = null\n }\n\n if (currentSession.remoteAudio) {\n currentSession.remoteAudio.srcObject = null\n if (currentSession.remoteAudio.parentNode) {\n currentSession.remoteAudio.parentNode.removeChild(currentSession.remoteAudio)\n }\n currentSession.remoteAudio = null\n }\n\n if (currentSession.socket) {\n currentSession.socket.close()\n currentSession.socket = null\n }\n\n stopPingInterval()\n\n const callbacks = currentSession.callbacks\n currentSession = createSession(callbacks)\n\n console.log('Call session cleaned up')\n}\n\n/**\n * Update call status and notify callback\n * @param {string} status\n */\nfunction setCallStatus(status) {\n currentSession.callStatus = status\n currentSession.callbacks.onCallStatus?.(status)\n}\n\n/**\n * Update call error and notify callback\n * @param {string | null} error\n */\nfunction setCallError(error) {\n currentSession.callbacks.onCallError?.(error)\n}\n\n/**\n * Stop ping interval\n */\nfunction stopPingInterval() {\n if (currentSession.pingInterval) {\n clearInterval(currentSession.pingInterval)\n currentSession.pingInterval = null\n }\n}\n\n/**\n * Start ping interval\n */\nfunction startPingInterval() {\n stopPingInterval()\n\n currentSession.pingInterval = setInterval(() => {\n if (currentSession.socket && currentSession.socket.readyState === WebSocket.OPEN) {\n currentSession.pingCount++\n const pingMessage = {\n type: 'ping',\n timestamp: Date.now(),\n count: currentSession.pingCount\n }\n sendEvent(pingMessage)\n console.log(`Sending keep-alive ping #${currentSession.pingCount}`)\n } else {\n console.log('Socket not open, stopping ping interval')\n stopPingInterval()\n }\n }, 10000)\n}\n\n/**\n * Handle pong response\n */\nfunction handlePong() {\n currentSession.lastPongTime = Date.now()\n console.log(`Received pong #${currentSession.pingCount}`)\n}\n\n/**\n * Send event through socket\n * @param {Object} payload\n */\nfunction sendEvent(payload) {\n if (!currentSession.socket) {\n console.error('Failed to send event: no socket instance')\n return\n }\n if (currentSession.socket.readyState !== WebSocket.OPEN) {\n console.error('Failed to send event: socket state not open ', payload)\n return\n }\n\n currentSession.socket.send(JSON.stringify(payload))\n}\n\n/**\n * Get user media\n */\nasync function getUserMedia() {\n try {\n currentSession.localStream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n video: false\n })\n console.log('Got audio media')\n } catch (error) {\n console.error(`Failed to get audio media: ${error.message}`)\n throw error\n }\n}\n\n/**\n * Create peer connection\n */\nfunction createPeerConnection() {\n currentSession.peerConnection = new RTCPeerConnection(rtcConfig)\n\n currentSession.peerConnection.onicecandidate = (event) => {\n if (event.candidate) {\n const candidateJson = JSON.stringify(event.candidate)\n // Queue local ICE candidates until remote description is set\n if (currentSession.peerConnection && currentSession.peerConnection.remoteDescription) {\n sendEvent({\n type: 'ice',\n data: {\n candidate: candidateJson\n }\n })\n console.log('Sent ICE candidate immediately')\n } else {\n currentSession.localIceCandidates.push(candidateJson)\n console.log('Queued local ICE candidate')\n }\n }\n }\n\n currentSession.peerConnection.ontrack = (event) => {\n console.log('Received remote audio stream')\n currentSession.remoteStream = event.streams[0]\n\n if (!currentSession.remoteAudio) {\n currentSession.remoteAudio = document.createElement('audio')\n currentSession.remoteAudio.autoplay = true\n currentSession.remoteAudio.controls = false\n document.body.appendChild(currentSession.remoteAudio)\n }\n currentSession.remoteAudio.srcObject = currentSession.remoteStream\n // explicitly kick off playback and catch any policy/gesture errors\n currentSession.remoteAudio\n .play()\n .then(() => console.log('🔊 remote audio playing'))\n .catch((err) => console.error('❌ playback error:', err))\n }\n\n currentSession.peerConnection.onconnectionstatechange = () => {\n const newState = currentSession.peerConnection.connectionState\n console.log(`Connection state: ${newState}`)\n\n if (newState === 'connected') {\n setCallStatus('connected')\n } else if (newState === 'disconnected' || newState === 'closed') {\n setCallStatus('disconnected')\n disconnectCall()\n }\n }\n\n currentSession.peerConnection.oniceconnectionstatechange = () => {\n console.log(`ICE connection state: ${currentSession.peerConnection.iceConnectionState}`)\n }\n}\n\n/**\n * Connect socket\n * @param {{ sessionId?: string }} payload\n */\nfunction connectSocket(payload) {\n return new Promise((fulfill, reject) => {\n if (\n currentSession.socket &&\n (currentSession.socket.readyState === WebSocket.CONNECTING ||\n currentSession.socket.readyState === WebSocket.OPEN)\n ) {\n console.log('Socket in connecting/open state, returning.')\n fulfill(currentSession.socket.readyState === WebSocket.OPEN)\n return\n }\n\n console.log('Initializing socket connection..')\n const credentials = getCredentials()\n if (!credentials || !credentials.endpoint) {\n reject(new Error('SDK not initialized. Please initialize SDK first.'))\n return\n }\n\n // Extract hostname from endpoint\n const socketEndpoint = getCallServerEndpoint(credentials.endpoint)\n if (!socketEndpoint) {\n reject(\n new Error(\n 'Invalid endpoint while initializing SDK. Please check the endpoint and try again.'\n )\n )\n return\n }\n\n const externalId = getExternalId()\n const queryParams = new URLSearchParams({\n externalId\n })\n if (payload.sessionId) {\n queryParams.set('sessionId', payload.sessionId)\n }\n if (credentials.token) {\n queryParams.set('token', credentials.token)\n }\n\n const socketUrl = `${socketEndpoint}?${queryParams.toString()}`\n currentSession.socket = new WebSocket(socketUrl)\n\n currentSession.socket.onopen = (event) => {\n console.log('Socket connection established: ', event)\n startPingInterval()\n fulfill(true)\n }\n\n currentSession.socket.onmessage = (event) => {\n const data = JSON.parse(event.data)\n handleCallServerEvent(data)\n }\n\n currentSession.socket.onerror = (error) => {\n console.error('Socket error: ', error)\n setCallStatus('error')\n setCallError(error.message || 'Unable to connect voice')\n reject(error)\n }\n\n currentSession.socket.onclose = (event) => {\n console.log('Socket connection closed: ', event)\n stopPingInterval()\n }\n })\n}\n\n/**\n * Handle call server event\n * @param {Object} action\n */\nfunction handleCallServerEvent(action) {\n console.log('Handling socket server event: ', action)\n\n switch (action.type) {\n case 'pong':\n handlePong()\n break\n\n case 'answer':\n handleAnswer(action.data)\n break\n\n case 'ice':\n handleIceCandidate(action.data)\n break\n\n case 'renegotiationOffer':\n handleRenegotiationOffer(action.data)\n break\n case 'end':\n disconnectCall()\n break\n case 'error':\n setCallStatus('error')\n setCallError(action.error || 'Unable to connect voice')\n break\n\n default:\n console.log('Unknown call event type: ', action.type)\n break\n }\n}\n\n/**\n * Handle answer\n * @param {Object} data\n */\nasync function handleAnswer(data) {\n try {\n console.log('Received answer')\n\n currentSession.sessionId = data.sessionId\n // Update chat session with the new sessionId and notify controller\n updateSessionId(data.sessionId)\n\n if (currentSession.peerConnection) {\n const answer = new RTCSessionDescription({\n type: 'answer',\n sdp: data.sdp\n })\n console.log('Setting remote description answer: ', answer)\n await currentSession.peerConnection.setRemoteDescription(answer)\n console.log('Remote description set')\n\n // Send all queued local ICE candidates\n for (const candidateJson of currentSession.localIceCandidates) {\n sendEvent({\n type: 'ice',\n data: {\n candidate: candidateJson\n }\n })\n console.log('Sent queued local ICE candidate')\n }\n currentSession.localIceCandidates = []\n\n // Process any pending remote ICE candidates\n for (const candidateJson of currentSession.pendingRemoteIceCandidates) {\n try {\n const candidate = new RTCIceCandidate(JSON.parse(candidateJson))\n await currentSession.peerConnection.addIceCandidate(candidate)\n console.log('Added pending remote ICE candidate')\n } catch (err) {\n console.error(`Failed to add pending ICE candidate: ${err.message}`)\n }\n }\n currentSession.pendingRemoteIceCandidates = []\n }\n } catch (error) {\n console.error(`Failed to handle answer: ${error.message}`)\n }\n}\n\n/**\n * Handle ICE candidate\n * @param {Object} data\n */\nasync function handleIceCandidate(data) {\n try {\n if (currentSession.peerConnection) {\n // Check if remote description is set\n if (!currentSession.peerConnection.remoteDescription) {\n // Queue the candidate until remote description is set\n currentSession.pendingRemoteIceCandidates.push(data.candidate)\n console.log('Queued remote ICE candidate - remote description not set')\n return\n }\n const candidate = new RTCIceCandidate(JSON.parse(data.candidate))\n await currentSession.peerConnection.addIceCandidate(candidate)\n console.log('Added ICE candidate')\n }\n } catch (error) {\n console.error(`Failed to add ICE candidate: ${error.message}`)\n }\n}\n\n/**\n * Handle renegotiation offer\n * @param {Object} data\n */\nasync function handleRenegotiationOffer(data) {\n try {\n console.log('Received renegotiation offer')\n\n if (currentSession.peerConnection) {\n const offer = new RTCSessionDescription({\n type: 'offer',\n sdp: data.sdp\n })\n console.log('Setting remote description offer: ', offer)\n await currentSession.peerConnection.setRemoteDescription(offer)\n console.log('Remote description set')\n\n const answer = await currentSession.peerConnection.createAnswer()\n await currentSession.peerConnection.setLocalDescription(answer)\n\n sendEvent({\n type: 'renegotiationAnswer',\n data: {\n sdp: answer.sdp\n }\n })\n }\n } catch (error) {\n console.error(`Failed to handle renegotiation offer: ${error.message}`)\n }\n}\n\n/**\n * Start a call\n * @param {{ sessionId?: string }} payload\n */\nexport async function startCall(payload = {}) {\n try {\n if (currentSession.callStatus === 'connecting' || currentSession.callStatus === 'connected') {\n console.log(`Call already in ${currentSession.callStatus} state`)\n return\n }\n\n console.log('Starting audio call...')\n setCallStatus('connecting')\n setCallError(null)\n\n currentSession.sessionId = payload.sessionId\n\n await getUserMedia()\n\n createPeerConnection()\n\n currentSession.localStream.getTracks().forEach((track) => {\n currentSession.peerConnection.addTrack(track, currentSession.localStream)\n console.log(`Added ${track.kind} track`)\n })\n await connectSocket(payload)\n const offer = await currentSession.peerConnection.createOffer()\n await currentSession.peerConnection.setLocalDescription(offer)\n\n sendEvent({\n type: 'offer',\n data: {\n sdp: offer.sdp\n }\n })\n\n console.log('Call initiated successfully')\n } catch (error) {\n console.log('error: ', error)\n console.error(`Failed to start call: ${error.message}`)\n setCallStatus('error')\n setCallError(error.message || 'Unable to connect voice')\n cleanup()\n }\n}\n\n/**\n * Disconnect call\n */\nexport function disconnectCall() {\n sendEvent({\n type: 'end'\n })\n if (currentSession.socket) {\n currentSession.socket.close()\n currentSession.socket = null\n }\n setCallStatus('disconnected')\n if (currentSession.peerConnection) {\n currentSession.peerConnection.close()\n currentSession.peerConnection = null\n }\n if (currentSession.localStream) {\n currentSession.localStream.getTracks().forEach((track) => track.stop())\n currentSession.localStream = null\n }\n cleanup()\n}\n\n/**\n * Toggle mute\n * @returns {boolean}\n */\nexport function toggleMute() {\n if (currentSession.localStream) {\n const audioTrack = currentSession.localStream.getAudioTracks()[0]\n if (audioTrack) {\n audioTrack.enabled = !audioTrack.enabled\n currentSession.isMuted = !audioTrack.enabled\n console.log(`Audio ${currentSession.isMuted ? 'muted' : 'unmuted'}`)\n return currentSession.isMuted\n }\n }\n return false\n}\n\n/**\n * Get local stream\n * @returns {MediaStream | null}\n */\nexport function getLocalStream() {\n return currentSession.localStream\n}\n\n/**\n * Get inbound audio energy\n * @returns {Promise<number>}\n */\nexport function getInboundAudioEnergy() {\n return new Promise((resolve, reject) => {\n if (!currentSession.peerConnection) {\n reject(new Error('no peer connection'))\n return\n }\n currentSession.peerConnection\n .getStats()\n .then((stats) => {\n stats.forEach((report) => {\n if (report.type == 'inbound-rtp') {\n resolve(report.totalAudioEnergy)\n }\n })\n reject(new Error('no inbound-rtp stats found'))\n })\n .catch((err) => {\n reject(err)\n })\n })\n}\n\n/**\n * Get outbound audio energy (not implemented in original, but may be needed)\n * @returns {Promise<number>}\n */\nexport function getOutboundAudioEnergy() {\n return new Promise((resolve, reject) => {\n if (!currentSession.peerConnection) {\n reject(new Error('no peer connection'))\n return\n }\n currentSession.peerConnection\n .getStats()\n .then((stats) => {\n stats.forEach((report) => {\n if (report.type == 'outbound-rtp') {\n resolve(report.totalAudioEnergy)\n }\n })\n reject(new Error('no outbound-rtp stats found'))\n })\n .catch((err) => {\n reject(err)\n })\n })\n}\n"],"names":["uuidv7","timestamp","bytes","hex","b","getDeviceId","deviceId","sleep","ms","resolve","getCallServerEndpoint","baseUrl","socketEndpoint","url","e","MESSAGE_ROLES","createSession","callbacks","currentSession","setCallbacks","initialize","credentials","getCredentials","updateSessionId","sessionId","startChat","payload","configData","authenticate","messages","messagesRes","getMessages","_a","msg","searchParams","getExternalId","error","cleanup","disconnect","sendMessage","text","html","reject","userMessage","loadingMessage","headers","fetchEventSource","response","event","data","newBotMessage","lastIndex","lastMsg","updatedMsg","_c","index","_f","_g","errorMessage","AUTHENTICATION_ERROR","INITIALIZATION_ERROR","_config","configure","endpoint","errorPayload","getHistory","queryParams","fetchRequest","pathname","method","body","rtcConfig","setCallCallbacks","track","stopPingInterval","setCallStatus","status","_b","setCallError","startPingInterval","pingMessage","sendEvent","handlePong","getUserMedia","createPeerConnection","candidateJson","err","newState","disconnectCall","connectSocket","fulfill","externalId","socketUrl","handleCallServerEvent","action","handleAnswer","handleIceCandidate","handleRenegotiationOffer","answer","candidate","offer","startCall","toggleMute","audioTrack","getLocalStream","getInboundAudioEnergy","stats","report","getOutboundAudioEnergy"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"origon-chat-sdk.js","sources":["../src/utils.js","../src/constants.js","../src/chat.js","../src/http.js","../src/call.js"],"sourcesContent":["/**\n * Utility functions for the Chat SDK\n */\n\nexport function uuidv7() {\n const timestamp = Date.now()\n const bytes = new Uint8Array(16)\n crypto.getRandomValues(bytes)\n\n // Set timestamp (48 bits)\n bytes[0] = (timestamp >> 40) & 0xff\n bytes[1] = (timestamp >> 32) & 0xff\n bytes[2] = (timestamp >> 24) & 0xff\n bytes[3] = (timestamp >> 16) & 0xff\n bytes[4] = (timestamp >> 8) & 0xff\n bytes[5] = timestamp & 0xff\n\n // Set version 7 (0111)\n bytes[6] = (bytes[6] & 0x0f) | 0x70\n\n // Set variant (10xx)\n bytes[8] = (bytes[8] & 0x3f) | 0x80\n\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('')\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(\n 16,\n 20\n )}-${hex.slice(20)}`\n}\n\nexport function getDeviceId() {\n if (localStorage.getItem('chatDeviceId')) {\n return localStorage.getItem('chatDeviceId')\n }\n\n const deviceId = uuidv7()\n localStorage.setItem('chatDeviceId', deviceId)\n return deviceId\n}\n\nexport function parseJwt(token) {\n try {\n const base64Url = token.split('.')[1]\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')\n const jsonPayload = decodeURIComponent(\n atob(base64)\n .split('')\n .map(function (c) {\n return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)\n })\n .join('')\n )\n\n return JSON.parse(jsonPayload)\n } catch {\n return null\n }\n}\n\nexport async function sleep(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nexport function getSocketEndpoint(baseUrl) {\n let socketEndpoint\n try {\n const url = new URL(baseUrl)\n socketEndpoint = `wss://${url.hostname}${url.pathname}/wss`\n } catch {\n console.error('Invalid base URL: ', baseUrl)\n }\n return socketEndpoint\n}\n\nexport function getCallServerEndpoint(baseUrl) {\n let socketEndpoint\n try {\n const url = new URL(baseUrl)\n socketEndpoint = `wss://${url.hostname}${url.pathname}/audio`\n } catch {\n console.error('getCallServerEndpoint: Invalid base URL: ', baseUrl)\n }\n return socketEndpoint\n}\n","/**\n * Constants for the Chat SDK\n */\n\nexport const MESSAGE_ROLES = {\n BOT: 'assistant', // this can be automated or LLM AI Agent response\n USER: 'user', // this is widget user\n AGENT: 'agent', // this is human agent (dock side)\n SYSTEM: 'system' // this is system message, for ex \"Agent joined\" / \"Agent left\"\n}\n","/**\n * Chat Service for Chat SDK\n * Handles real-time chat functionality without depending on external state\n * Uses callbacks to communicate state changes to the consumer\n */\n\nimport { fetchEventSource } from '@microsoft/fetch-event-source'\nimport { getMessages, authenticate } from './http.js'\nimport { getDeviceId, sleep } from './utils.js'\nimport { MESSAGE_ROLES } from './constants.js'\n\n/**\n * @typedef {Object} ChatCallbacks\n * @property {(message: Object) => void} [onMessageAdd] - Called when a new message is added\n * @property {(index: number, updatedMsg: Object) => void} [onMessageUpdate] - Called when an existing message is updated\n * @property {(sessionId: string) => void} [onSessionUpdate] - Called when session ID is updated\n */\n\n/**\n * @typedef {Object} ChatSession\n * @property {string} sessionId\n * @property {string} sseUrl\n * @property {string} [requestId]\n * @property {AbortController} [abortController]\n * @property {string} [lastStreamId]\n * @property {Array} messages\n * @property {ChatCallbacks} callbacks\n */\n\n/**\n * Create a new chat session\n * @param {ChatCallbacks} [callbacks={}]\n * @returns {ChatSession}\n */\nfunction createSession(callbacks = {}) {\n return {\n credentials: undefined,\n authenticated: false,\n configData: undefined,\n sessionId: undefined,\n requestId: undefined,\n sseUrl: undefined,\n abortController: undefined,\n lastStreamId: undefined,\n messages: [],\n callbacks\n }\n}\n\n/** @type {ChatSession} */\nlet currentSession = createSession()\n\n/**\n * Set callbacks for the current session\n * @param {ChatCallbacks} callbacks\n */\nexport function setCallbacks(callbacks) {\n currentSession.callbacks = { ...currentSession.callbacks, ...callbacks }\n}\n\n/**\n * Initialize the chat session\n * @param {Object} credentials - Credentials for the chat\n */\nexport function initialize(credentials) {\n console.log('Initializing chat...', credentials)\n currentSession.credentials = credentials\n if (credentials.token) {\n currentSession.authenticated = true\n }\n}\n\n/**\n * Get current chat session credentials\n * @returns {{ endpoint: string, apiKey: string } | undefined}\n */\nexport function getCredentials() {\n return currentSession.credentials\n}\n\n/**\n * Update the session ID and notify via callback\n * @param {string} sessionId - The new session ID\n */\nexport function updateSessionId(sessionId) {\n if (sessionId && sessionId !== currentSession.sessionId) {\n currentSession.sessionId = sessionId\n currentSession.callbacks.onSessionUpdate?.(sessionId)\n }\n}\n\n/**\n * Initiate a new chat session or resume an existing one\n * @param {Object} credentials - Credentials for the chat\n * @param {Object} payload - Payload for the chat. It contains sessionId (optional)\n * @param {string} [payload.sessionId] - Optional session ID to resume\n * @returns {Promise<{ sessionId: string, messages: Array }>}\n */\nexport async function startChat(payload = {}) {\n try {\n console.log('startChat: ', payload, currentSession)\n\n let configData = null\n if (!currentSession.authenticated) {\n configData = await authenticate(currentSession.credentials)\n currentSession.authenticated = true\n currentSession.configData = configData\n } else {\n configData = currentSession.configData\n }\n\n let messages = []\n\n if (payload.sessionId) {\n const messagesRes = await getMessages(payload.sessionId)\n messages = (messagesRes?.sessionHistory ?? []).map((msg) => ({\n id: msg.id,\n text: msg.text,\n role: msg.youtubeVideo\n ? MESSAGE_ROLES.BOT // for youtube video messages, role is \"system\" from backend, we need to make it \"assistant\"\n : msg.role,\n timestamp: msg.timestamp,\n video: msg.youtubeVideo,\n channel: msg.channel,\n done: true\n }))\n }\n\n const searchParams = new URLSearchParams({\n externalId: getExternalId()\n })\n currentSession.sseUrl = `${currentSession.credentials.endpoint}?${searchParams.toString()}`\n currentSession.sessionId = payload.sessionId\n currentSession.messages = messages\n\n console.log('Chat initiated successfully')\n\n return {\n sessionId: currentSession.sessionId,\n messages,\n configData\n }\n } catch (error) {\n console.error(`Failed to start chat: ${error.message}`)\n cleanup()\n throw error\n }\n}\n\n/**\n * Disconnect from the current chat session\n */\nexport function disconnect() {\n cleanup()\n}\n\n/**\n * Clean up the current session\n */\nfunction cleanup() {\n if (currentSession.abortController) {\n currentSession.abortController.abort()\n }\n\n const { callbacks, credentials } = currentSession\n currentSession = createSession(callbacks)\n currentSession.credentials = credentials\n\n console.log('Chat session cleaned up')\n}\n\nexport function getExternalId() {\n if (currentSession.credentials?.externalId) {\n return currentSession.credentials.externalId\n }\n return getDeviceId()\n}\n\n/**\n * Send a message in the current chat session\n * @param {{ text: string, html?: string }} message\n * @returns {Promise<string>}\n */\nexport function sendMessage({ text, html }) {\n return new Promise((resolve, reject) => {\n ;(async () => {\n try {\n // Add user message\n const userMessage = {\n role: MESSAGE_ROLES.USER,\n text,\n html,\n timestamp: new Date().toISOString()\n }\n currentSession.messages = [...currentSession.messages, userMessage]\n currentSession.callbacks.onMessageAdd?.(userMessage)\n\n await sleep(200)\n\n const loadingMessage = {\n role: MESSAGE_ROLES.BOT,\n text: '',\n loading: true\n }\n currentSession.messages = [...currentSession.messages, loadingMessage]\n currentSession.callbacks.onMessageAdd?.(loadingMessage)\n\n const url = new URL(currentSession.sseUrl)\n if (currentSession.sessionId) {\n url.searchParams.set('sessionId', currentSession.sessionId)\n }\n if (currentSession.requestId) {\n url.searchParams.set('requestId', currentSession.requestId)\n }\n\n currentSession.lastStreamId = undefined\n\n // Create a new abort controller for this request\n currentSession.abortController = new AbortController()\n\n const headers = {\n 'Content-Type': 'application/json'\n }\n if (currentSession.credentials?.token) {\n headers.Authorization = `Bearer ${currentSession.credentials.token}`\n }\n\n await fetchEventSource(url.toString(), {\n method: 'POST',\n headers,\n body: JSON.stringify({\n message: text,\n html\n }),\n signal: currentSession.abortController.signal,\n onopen: async (response) => {\n if (!response.ok) {\n console.error('Failed to send message bad response: ', response)\n throw new Error('Failed to send message')\n }\n },\n onmessage: (event) => {\n console.log('Event: ', event)\n const data = JSON.parse(event.data)\n\n if (data.status === 'connected') {\n currentSession.sessionId = data.sessionId\n currentSession.requestId = data.requestId\n } else if (data.message !== undefined) {\n // If streamId changes, start a new assistant message\n if (data.streamId !== undefined) {\n if (currentSession.lastStreamId === undefined) {\n currentSession.lastStreamId = data.streamId\n } else if (data.streamId !== currentSession.lastStreamId) {\n currentSession.lastStreamId = data.streamId\n const newBotMessage = {\n role: MESSAGE_ROLES.BOT,\n text: '',\n loading: true\n }\n currentSession.messages = [...currentSession.messages, newBotMessage]\n currentSession.callbacks.onMessageAdd?.(newBotMessage)\n }\n }\n\n // Update the last message with new content\n const lastIndex = currentSession.messages.length - 1\n const lastMsg = currentSession.messages[lastIndex]\n const updatedMsg = {\n ...lastMsg,\n loading: false,\n text: (lastMsg.text || '') + data.message,\n done: data.done ?? lastMsg.done\n }\n currentSession.messages = currentSession.messages.map((msg, index) =>\n index === lastIndex ? updatedMsg : msg\n )\n\n currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)\n\n if (data.done) {\n resolve(currentSession.sessionId)\n }\n\n // Store session info for reuse\n currentSession.sessionId = data.session_id ?? currentSession.sessionId\n currentSession.requestId = data.requestId ?? currentSession.requestId\n } else if (data.error) {\n const errorMessage = 'Failed to connect to the system'\n const lastIndex = currentSession.messages.length - 1\n const lastMsg = currentSession.messages[lastIndex]\n const updatedMsg = {\n ...lastMsg,\n loading: false,\n errorText: errorMessage\n }\n currentSession.messages = currentSession.messages.map((msg, index) =>\n index === lastIndex ? updatedMsg : msg\n )\n currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)\n reject(new Error(errorMessage))\n }\n },\n onerror: (error) => {\n throw error // Rethrow to stop retries\n },\n openWhenHidden: true\n })\n } catch (error) {\n console.error('Failed to send message: ', error)\n const errorMessage = 'Failed to connect to the system'\n const lastIndex = currentSession.messages.length - 1\n const lastMsg = currentSession.messages[lastIndex]\n const updatedMsg = {\n ...lastMsg,\n loading: false,\n errorText: lastMsg.done ? undefined : error.message || errorMessage,\n done: true\n }\n currentSession.messages = currentSession.messages.map((msg, index) =>\n index === lastIndex ? updatedMsg : msg\n )\n currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)\n reject(error)\n }\n })()\n })\n}\n","/**\n * API Service for Chat SDK\n * Handles all HTTP requests without depending on external state\n */\n\nimport { getCredentials, getExternalId } from './chat.js'\n\nconst AUTHENTICATION_ERROR = 'Something went wrong initializing the chat'\nconst INITIALIZATION_ERROR = 'Chat SDK not initialized'\n\n/**\n * Authenticate with the chat service\n * @param {{ endpoint: string }} credentials\n * @returns {Promise<object>} Authentication response data\n */\nexport async function authenticate(payload) {\n const { endpoint } = payload\n const url = `${endpoint}/config`\n\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json'\n }\n })\n\n if (!response.ok) {\n const errorPayload = await response.json()\n throw new Error(errorPayload?.error || AUTHENTICATION_ERROR)\n }\n\n const res = await response.json()\n const data = res.data\n\n return data\n}\n\n/**\n * Get chat history for the current device\n * @returns {Promise<{ sessions: Array }>}\n */\nexport async function getHistory() {\n const queryParams = new URLSearchParams({\n externalId: getExternalId()\n })\n const response = await fetchRequest(`/sessions?${queryParams.toString()}`, 'GET')\n\n if (!response.ok) {\n throw new Error('Unable to load history, please try again later')\n }\n\n return response.json()\n}\n\n/**\n * Get messages for a specific session\n * @param {string} sessionId\n * @returns {Promise<{ sessionHistory: Array }>}\n */\nexport async function getMessages(sessionId) {\n const queryParams = new URLSearchParams({\n sessionId\n })\n const response = await fetchRequest(`/session?${queryParams.toString()}`, 'GET')\n\n if (!response.ok) {\n throw new Error('Unable to load messages, please try again later')\n }\n\n return response.json()\n}\n\n/**\n * Internal fetch request helper\n * @param {string} pathname\n * @param {string} method\n * @param {object|null} body\n * @returns {Promise<Response>}\n */\nasync function fetchRequest(pathname, method = 'GET', body = null) {\n const credentials = getCredentials()\n\n if (!credentials?.endpoint) {\n throw new Error(INITIALIZATION_ERROR)\n }\n\n const url = `${endpoint}${pathname}`\n\n return fetch(url, {\n headers: {\n 'Content-Type': 'application/json'\n },\n method,\n body: body ? JSON.stringify(body) : null\n })\n}\n","/**\n * Socket Service for Call SDK\n * Handles WebRTC call functionality without depending on external state\n * Uses callbacks to communicate state changes to the consumer\n */\n\nimport { getCallServerEndpoint } from './utils.js'\nimport { getCredentials, getExternalId, updateSessionId } from './chat.js'\n\n/**\n * @typedef {Object} CallCallbacks\n * @property {(status: string) => void} [onCallStatus] - Called when call status changes\n * @property {(error: string | null) => void} [onCallError] - Called when call error occurs\n */\n\n/**\n * @typedef {Object} CallSession\n * @property {string} [sessionId]\n * @property {WebSocket} [socket]\n * @property {RTCPeerConnection} [peerConnection]\n * @property {MediaStream} [localStream]\n * @property {MediaStream} [remoteStream]\n * @property {HTMLAudioElement} [remoteAudio]\n * @property {boolean} isMuted\n * @property {string} callStatus\n * @property {NodeJS.Timeout} [pingInterval]\n * @property {number} pingCount\n * @property {number | null} lastPongTime\n * @property {CallCallbacks} callbacks\n * @property {string[]} localIceCandidates - Queued local ICE candidates to send after remote description is set\n * @property {string[]} pendingRemoteIceCandidates - Queued remote ICE candidates to add after remote description is set\n */\n\n/**\n * Create a new call session\n * @param {CallCallbacks} [callbacks={}]\n * @returns {CallSession}\n */\nfunction createSession(callbacks = {}) {\n return {\n sessionId: undefined,\n socket: null,\n peerConnection: null,\n localStream: null,\n remoteStream: null,\n remoteAudio: null,\n isMuted: false,\n callStatus: 'disconnected',\n pingInterval: null,\n pingCount: 0,\n lastPongTime: null,\n callbacks,\n localIceCandidates: [],\n pendingRemoteIceCandidates: []\n }\n}\n\n/** @type {CallSession} */\nlet currentSession = createSession()\n\nconst rtcConfig = {\n iceServers: [{ urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun1.l.google.com:19302' }]\n}\n\n/**\n * Set callbacks for the current session\n * @param {CallCallbacks} callbacks\n */\nexport function setCallCallbacks(callbacks) {\n currentSession.callbacks = { ...currentSession.callbacks, ...callbacks }\n}\n\n/**\n * Clean up the current session\n */\nfunction cleanup() {\n if (currentSession.peerConnection) {\n currentSession.peerConnection.close()\n currentSession.peerConnection = null\n }\n\n if (currentSession.localStream) {\n currentSession.localStream.getTracks().forEach((track) => track.stop())\n currentSession.localStream = null\n }\n\n if (currentSession.remoteStream) {\n currentSession.remoteStream = null\n }\n\n if (currentSession.remoteAudio) {\n currentSession.remoteAudio.srcObject = null\n if (currentSession.remoteAudio.parentNode) {\n currentSession.remoteAudio.parentNode.removeChild(currentSession.remoteAudio)\n }\n currentSession.remoteAudio = null\n }\n\n if (currentSession.socket) {\n currentSession.socket.close()\n currentSession.socket = null\n }\n\n stopPingInterval()\n\n const callbacks = currentSession.callbacks\n currentSession = createSession(callbacks)\n\n console.log('Call session cleaned up')\n}\n\n/**\n * Update call status and notify callback\n * @param {string} status\n */\nfunction setCallStatus(status) {\n currentSession.callStatus = status\n currentSession.callbacks.onCallStatus?.(status)\n}\n\n/**\n * Update call error and notify callback\n * @param {string | null} error\n */\nfunction setCallError(error) {\n currentSession.callbacks.onCallError?.(error)\n}\n\n/**\n * Stop ping interval\n */\nfunction stopPingInterval() {\n if (currentSession.pingInterval) {\n clearInterval(currentSession.pingInterval)\n currentSession.pingInterval = null\n }\n}\n\n/**\n * Start ping interval\n */\nfunction startPingInterval() {\n stopPingInterval()\n\n currentSession.pingInterval = setInterval(() => {\n if (currentSession.socket && currentSession.socket.readyState === WebSocket.OPEN) {\n currentSession.pingCount++\n const pingMessage = {\n type: 'ping',\n timestamp: Date.now(),\n count: currentSession.pingCount\n }\n sendEvent(pingMessage)\n console.log(`Sending keep-alive ping #${currentSession.pingCount}`)\n } else {\n console.log('Socket not open, stopping ping interval')\n stopPingInterval()\n }\n }, 10000)\n}\n\n/**\n * Handle pong response\n */\nfunction handlePong() {\n currentSession.lastPongTime = Date.now()\n console.log(`Received pong #${currentSession.pingCount}`)\n}\n\n/**\n * Send event through socket\n * @param {Object} payload\n */\nfunction sendEvent(payload) {\n if (!currentSession.socket) {\n console.error('Failed to send event: no socket instance')\n return\n }\n if (currentSession.socket.readyState !== WebSocket.OPEN) {\n console.error('Failed to send event: socket state not open ', payload)\n return\n }\n\n currentSession.socket.send(JSON.stringify(payload))\n}\n\n/**\n * Get user media\n */\nasync function getUserMedia() {\n try {\n currentSession.localStream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n video: false\n })\n console.log('Got audio media')\n } catch (error) {\n console.error(`Failed to get audio media: ${error.message}`)\n throw error\n }\n}\n\n/**\n * Create peer connection\n */\nfunction createPeerConnection() {\n currentSession.peerConnection = new RTCPeerConnection(rtcConfig)\n\n currentSession.peerConnection.onicecandidate = (event) => {\n if (event.candidate) {\n const candidateJson = JSON.stringify(event.candidate)\n // Queue local ICE candidates until remote description is set\n if (currentSession.peerConnection && currentSession.peerConnection.remoteDescription) {\n sendEvent({\n type: 'ice',\n data: {\n candidate: candidateJson\n }\n })\n console.log('Sent ICE candidate immediately')\n } else {\n currentSession.localIceCandidates.push(candidateJson)\n console.log('Queued local ICE candidate')\n }\n }\n }\n\n currentSession.peerConnection.ontrack = (event) => {\n console.log('Received remote audio stream')\n currentSession.remoteStream = event.streams[0]\n\n if (!currentSession.remoteAudio) {\n currentSession.remoteAudio = document.createElement('audio')\n currentSession.remoteAudio.autoplay = true\n currentSession.remoteAudio.controls = false\n document.body.appendChild(currentSession.remoteAudio)\n }\n currentSession.remoteAudio.srcObject = currentSession.remoteStream\n // explicitly kick off playback and catch any policy/gesture errors\n currentSession.remoteAudio\n .play()\n .then(() => console.log('🔊 remote audio playing'))\n .catch((err) => console.error('❌ playback error:', err))\n }\n\n currentSession.peerConnection.onconnectionstatechange = () => {\n const newState = currentSession.peerConnection.connectionState\n console.log(`Connection state: ${newState}`)\n\n if (newState === 'connected') {\n setCallStatus('connected')\n } else if (newState === 'disconnected' || newState === 'closed') {\n setCallStatus('disconnected')\n disconnectCall()\n }\n }\n\n currentSession.peerConnection.oniceconnectionstatechange = () => {\n console.log(`ICE connection state: ${currentSession.peerConnection.iceConnectionState}`)\n }\n}\n\n/**\n * Connect socket\n * @param {{ sessionId?: string }} payload\n */\nfunction connectSocket(payload) {\n return new Promise((fulfill, reject) => {\n if (\n currentSession.socket &&\n (currentSession.socket.readyState === WebSocket.CONNECTING ||\n currentSession.socket.readyState === WebSocket.OPEN)\n ) {\n console.log('Socket in connecting/open state, returning.')\n fulfill(currentSession.socket.readyState === WebSocket.OPEN)\n return\n }\n\n console.log('Initializing socket connection..')\n const credentials = getCredentials()\n if (!credentials || !credentials.endpoint) {\n reject(new Error('SDK not initialized. Please initialize SDK first.'))\n return\n }\n\n // Extract hostname from endpoint\n const socketEndpoint = getCallServerEndpoint(credentials.endpoint)\n if (!socketEndpoint) {\n reject(\n new Error(\n 'Invalid endpoint while initializing SDK. Please check the endpoint and try again.'\n )\n )\n return\n }\n\n const externalId = getExternalId()\n const queryParams = new URLSearchParams({\n externalId\n })\n if (payload.sessionId) {\n queryParams.set('sessionId', payload.sessionId)\n }\n if (credentials.token) {\n queryParams.set('token', credentials.token)\n }\n\n const socketUrl = `${socketEndpoint}?${queryParams.toString()}`\n currentSession.socket = new WebSocket(socketUrl)\n\n currentSession.socket.onopen = (event) => {\n console.log('Socket connection established: ', event)\n startPingInterval()\n fulfill(true)\n }\n\n currentSession.socket.onmessage = (event) => {\n const data = JSON.parse(event.data)\n handleCallServerEvent(data)\n }\n\n currentSession.socket.onerror = (error) => {\n console.error('Socket error: ', error)\n setCallStatus('error')\n setCallError(error.message || 'Unable to connect voice')\n reject(error)\n }\n\n currentSession.socket.onclose = (event) => {\n console.log('Socket connection closed: ', event)\n stopPingInterval()\n }\n })\n}\n\n/**\n * Handle call server event\n * @param {Object} action\n */\nfunction handleCallServerEvent(action) {\n console.log('Handling socket server event: ', action)\n\n switch (action.type) {\n case 'pong':\n handlePong()\n break\n\n case 'answer':\n handleAnswer(action.data)\n break\n\n case 'ice':\n handleIceCandidate(action.data)\n break\n\n case 'renegotiationOffer':\n handleRenegotiationOffer(action.data)\n break\n case 'end':\n disconnectCall()\n break\n case 'error':\n setCallStatus('error')\n setCallError(action.error || 'Unable to connect voice')\n break\n\n default:\n console.log('Unknown call event type: ', action.type)\n break\n }\n}\n\n/**\n * Handle answer\n * @param {Object} data\n */\nasync function handleAnswer(data) {\n try {\n console.log('Received answer')\n\n currentSession.sessionId = data.sessionId\n // Update chat session with the new sessionId and notify controller\n updateSessionId(data.sessionId)\n\n if (currentSession.peerConnection) {\n const answer = new RTCSessionDescription({\n type: 'answer',\n sdp: data.sdp\n })\n console.log('Setting remote description answer: ', answer)\n await currentSession.peerConnection.setRemoteDescription(answer)\n console.log('Remote description set')\n\n // Send all queued local ICE candidates\n for (const candidateJson of currentSession.localIceCandidates) {\n sendEvent({\n type: 'ice',\n data: {\n candidate: candidateJson\n }\n })\n console.log('Sent queued local ICE candidate')\n }\n currentSession.localIceCandidates = []\n\n // Process any pending remote ICE candidates\n for (const candidateJson of currentSession.pendingRemoteIceCandidates) {\n try {\n const candidate = new RTCIceCandidate(JSON.parse(candidateJson))\n await currentSession.peerConnection.addIceCandidate(candidate)\n console.log('Added pending remote ICE candidate')\n } catch (err) {\n console.error(`Failed to add pending ICE candidate: ${err.message}`)\n }\n }\n currentSession.pendingRemoteIceCandidates = []\n }\n } catch (error) {\n console.error(`Failed to handle answer: ${error.message}`)\n }\n}\n\n/**\n * Handle ICE candidate\n * @param {Object} data\n */\nasync function handleIceCandidate(data) {\n try {\n if (currentSession.peerConnection) {\n // Check if remote description is set\n if (!currentSession.peerConnection.remoteDescription) {\n // Queue the candidate until remote description is set\n currentSession.pendingRemoteIceCandidates.push(data.candidate)\n console.log('Queued remote ICE candidate - remote description not set')\n return\n }\n const candidate = new RTCIceCandidate(JSON.parse(data.candidate))\n await currentSession.peerConnection.addIceCandidate(candidate)\n console.log('Added ICE candidate')\n }\n } catch (error) {\n console.error(`Failed to add ICE candidate: ${error.message}`)\n }\n}\n\n/**\n * Handle renegotiation offer\n * @param {Object} data\n */\nasync function handleRenegotiationOffer(data) {\n try {\n console.log('Received renegotiation offer')\n\n if (currentSession.peerConnection) {\n const offer = new RTCSessionDescription({\n type: 'offer',\n sdp: data.sdp\n })\n console.log('Setting remote description offer: ', offer)\n await currentSession.peerConnection.setRemoteDescription(offer)\n console.log('Remote description set')\n\n const answer = await currentSession.peerConnection.createAnswer()\n await currentSession.peerConnection.setLocalDescription(answer)\n\n sendEvent({\n type: 'renegotiationAnswer',\n data: {\n sdp: answer.sdp\n }\n })\n }\n } catch (error) {\n console.error(`Failed to handle renegotiation offer: ${error.message}`)\n }\n}\n\n/**\n * Start a call\n * @param {{ sessionId?: string }} payload\n */\nexport async function startCall(payload = {}) {\n try {\n if (currentSession.callStatus === 'connecting' || currentSession.callStatus === 'connected') {\n console.log(`Call already in ${currentSession.callStatus} state`)\n return\n }\n\n console.log('Starting audio call...')\n setCallStatus('connecting')\n setCallError(null)\n\n currentSession.sessionId = payload.sessionId\n\n await getUserMedia()\n\n createPeerConnection()\n\n currentSession.localStream.getTracks().forEach((track) => {\n currentSession.peerConnection.addTrack(track, currentSession.localStream)\n console.log(`Added ${track.kind} track`)\n })\n await connectSocket(payload)\n const offer = await currentSession.peerConnection.createOffer()\n await currentSession.peerConnection.setLocalDescription(offer)\n\n sendEvent({\n type: 'offer',\n data: {\n sdp: offer.sdp\n }\n })\n\n console.log('Call initiated successfully')\n } catch (error) {\n console.log('error: ', error)\n console.error(`Failed to start call: ${error.message}`)\n setCallStatus('error')\n setCallError(error.message || 'Unable to connect voice')\n cleanup()\n }\n}\n\n/**\n * Disconnect call\n */\nexport function disconnectCall() {\n sendEvent({\n type: 'end'\n })\n if (currentSession.socket) {\n currentSession.socket.close()\n currentSession.socket = null\n }\n setCallStatus('disconnected')\n if (currentSession.peerConnection) {\n currentSession.peerConnection.close()\n currentSession.peerConnection = null\n }\n if (currentSession.localStream) {\n currentSession.localStream.getTracks().forEach((track) => track.stop())\n currentSession.localStream = null\n }\n cleanup()\n}\n\n/**\n * Toggle mute\n * @returns {boolean}\n */\nexport function toggleMute() {\n if (currentSession.localStream) {\n const audioTrack = currentSession.localStream.getAudioTracks()[0]\n if (audioTrack) {\n audioTrack.enabled = !audioTrack.enabled\n currentSession.isMuted = !audioTrack.enabled\n console.log(`Audio ${currentSession.isMuted ? 'muted' : 'unmuted'}`)\n return currentSession.isMuted\n }\n }\n return false\n}\n\n/**\n * Get local stream\n * @returns {MediaStream | null}\n */\nexport function getLocalStream() {\n return currentSession.localStream\n}\n\n/**\n * Get inbound audio energy\n * @returns {Promise<number>}\n */\nexport function getInboundAudioEnergy() {\n return new Promise((resolve, reject) => {\n if (!currentSession.peerConnection) {\n reject(new Error('no peer connection'))\n return\n }\n currentSession.peerConnection\n .getStats()\n .then((stats) => {\n stats.forEach((report) => {\n if (report.type == 'inbound-rtp') {\n resolve(report.totalAudioEnergy)\n }\n })\n reject(new Error('no inbound-rtp stats found'))\n })\n .catch((err) => {\n reject(err)\n })\n })\n}\n\n/**\n * Get outbound audio energy (not implemented in original, but may be needed)\n * @returns {Promise<number>}\n */\nexport function getOutboundAudioEnergy() {\n return new Promise((resolve, reject) => {\n if (!currentSession.peerConnection) {\n reject(new Error('no peer connection'))\n return\n }\n currentSession.peerConnection\n .getStats()\n .then((stats) => {\n stats.forEach((report) => {\n if (report.type == 'outbound-rtp') {\n resolve(report.totalAudioEnergy)\n }\n })\n reject(new Error('no outbound-rtp stats found'))\n })\n .catch((err) => {\n reject(err)\n })\n })\n}\n"],"names":["uuidv7","timestamp","bytes","hex","b","getDeviceId","deviceId","sleep","ms","resolve","getCallServerEndpoint","baseUrl","socketEndpoint","url","e","MESSAGE_ROLES","createSession","callbacks","currentSession","setCallbacks","initialize","credentials","getCredentials","updateSessionId","sessionId","startChat","payload","configData","authenticate","messages","messagesRes","getMessages","_a","msg","searchParams","getExternalId","error","cleanup","disconnect","sendMessage","text","html","reject","userMessage","loadingMessage","headers","fetchEventSource","response","event","data","newBotMessage","lastIndex","lastMsg","updatedMsg","_c","index","_f","_g","errorMessage","AUTHENTICATION_ERROR","INITIALIZATION_ERROR","endpoint","errorPayload","getHistory","queryParams","fetchRequest","pathname","method","body","rtcConfig","setCallCallbacks","track","stopPingInterval","setCallStatus","status","_b","setCallError","startPingInterval","pingMessage","sendEvent","handlePong","getUserMedia","createPeerConnection","candidateJson","err","newState","disconnectCall","connectSocket","fulfill","externalId","socketUrl","handleCallServerEvent","action","handleAnswer","handleIceCandidate","handleRenegotiationOffer","answer","candidate","offer","startCall","toggleMute","audioTrack","getLocalStream","getInboundAudioEnergy","stats","report","getOutboundAudioEnergy"],"mappings":"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"}
|
package/package.json
CHANGED
package/src/chat.js
CHANGED
|
@@ -314,7 +314,7 @@ export function sendMessage({ text, html }) {
|
|
|
314
314
|
const updatedMsg = {
|
|
315
315
|
...lastMsg,
|
|
316
316
|
loading: false,
|
|
317
|
-
errorText: errorMessage,
|
|
317
|
+
errorText: lastMsg.done ? undefined : error.message || errorMessage,
|
|
318
318
|
done: true
|
|
319
319
|
}
|
|
320
320
|
currentSession.messages = currentSession.messages.map((msg, index) =>
|
package/src/http.js
CHANGED
|
@@ -3,34 +3,11 @@
|
|
|
3
3
|
* Handles all HTTP requests without depending on external state
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { getExternalId } from './chat.js'
|
|
6
|
+
import { getCredentials, getExternalId } from './chat.js'
|
|
7
7
|
|
|
8
8
|
const AUTHENTICATION_ERROR = 'Something went wrong initializing the chat'
|
|
9
9
|
const INITIALIZATION_ERROR = 'Chat SDK not initialized'
|
|
10
10
|
|
|
11
|
-
// Module-level configuration
|
|
12
|
-
let _config = {
|
|
13
|
-
endpoint: null
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Configure the API service with endpoint
|
|
18
|
-
* @param {{ endpoint: string }} credentials
|
|
19
|
-
*/
|
|
20
|
-
export function configure(credentials) {
|
|
21
|
-
_config = {
|
|
22
|
-
endpoint: credentials.endpoint
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Get current configuration
|
|
28
|
-
* @returns {{ endpoint: string | null }}
|
|
29
|
-
*/
|
|
30
|
-
export function getConfig() {
|
|
31
|
-
return { ..._config }
|
|
32
|
-
}
|
|
33
|
-
|
|
34
11
|
/**
|
|
35
12
|
* Authenticate with the chat service
|
|
36
13
|
* @param {{ endpoint: string }} credentials
|
|
@@ -55,9 +32,6 @@ export async function authenticate(payload) {
|
|
|
55
32
|
const res = await response.json()
|
|
56
33
|
const data = res.data
|
|
57
34
|
|
|
58
|
-
// Store endpoint for subsequent requests
|
|
59
|
-
configure({ endpoint })
|
|
60
|
-
|
|
61
35
|
return data
|
|
62
36
|
}
|
|
63
37
|
|
|
@@ -104,9 +78,9 @@ export async function getMessages(sessionId) {
|
|
|
104
78
|
* @returns {Promise<Response>}
|
|
105
79
|
*/
|
|
106
80
|
async function fetchRequest(pathname, method = 'GET', body = null) {
|
|
107
|
-
const
|
|
81
|
+
const credentials = getCredentials()
|
|
108
82
|
|
|
109
|
-
if (!endpoint) {
|
|
83
|
+
if (!credentials?.endpoint) {
|
|
110
84
|
throw new Error(INITIALIZATION_ERROR)
|
|
111
85
|
}
|
|
112
86
|
|
package/src/index.js
CHANGED
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
* // Set up callbacks for state updates
|
|
22
22
|
* setCallbacks({
|
|
23
23
|
* onMessage: (msg) => console.log('New message:', msg),
|
|
24
|
-
*
|
|
24
|
+
* onMessageAdd: (message) => updateUI(message),
|
|
25
|
+
* onMessageUpdate: (index, updatedMsg) => updateUI(updatedMsg),
|
|
25
26
|
* onTyping: (isTyping) => showTypingIndicator(isTyping),
|
|
26
27
|
* onError: (error) => console.error(error)
|
|
27
28
|
* })
|
|
@@ -38,7 +39,7 @@
|
|
|
38
39
|
*/
|
|
39
40
|
|
|
40
41
|
// HTTP API functions
|
|
41
|
-
export { authenticate, getHistory, getMessages
|
|
42
|
+
export { authenticate, getHistory, getMessages } from './http.js'
|
|
42
43
|
|
|
43
44
|
// Chat functions
|
|
44
45
|
export { initialize, startChat, sendMessage, disconnect, setCallbacks } from './chat.js'
|