avin-ai 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.
@@ -1,26 +1,32 @@
1
1
  var u = Object.defineProperty;
2
- var b = (a, e, t) => e in a ? u(a, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : a[e] = t;
3
- var r = (a, e, t) => b(a, typeof e != "symbol" ? e + "" : e, t);
2
+ var b = (s, e, t) => e in s ? u(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t;
3
+ var a = (s, e, t) => b(s, typeof e != "symbol" ? e + "" : e, t);
4
4
  class f {
5
5
  constructor(e) {
6
- r(this, "serverUrl");
7
- r(this, "agentId");
8
- r(this, "callId");
9
- r(this, "pc", null);
10
- r(this, "dataChannel", null);
11
- r(this, "audioElement", null);
12
- r(this, "connected", !1);
13
- r(this, "onConnected");
14
- r(this, "onDisconnected");
15
- r(this, "onError");
16
- r(this, "onTranscription");
6
+ a(this, "serverUrl");
7
+ a(this, "agentId");
8
+ a(this, "callId");
9
+ a(this, "onConnected");
10
+ a(this, "onDisconnected");
11
+ a(this, "onError");
12
+ a(this, "onTranscription");
13
+ // @ts-ignore
14
+ a(this, "onRemoteTrack");
15
+ a(this, "pc", null);
16
+ a(this, "dataChannel", null);
17
+ a(this, "audioElement", null);
18
+ a(this, "connected", !1);
17
19
  if (!e.serverUrl) throw new Error("serverUrl is required");
18
20
  if (!e.agentId) throw new Error("agentId is required");
19
- this.serverUrl = e.serverUrl.replace(/\/$/, ""), this.agentId = e.agentId, this.callId = this.generateCallId(), this.onConnected = e.onConnected || (() => {
21
+ this.serverUrl = e.serverUrl.replace(/\/$/, ""), this.agentId = e.agentId, this.callId = e.callId || this.generateCallId(), this.onConnected = e.onConnected || (() => {
20
22
  }), this.onDisconnected = e.onDisconnected || (() => {
21
23
  }), this.onError = e.onError || ((t) => console.error("[WebRTC]", t)), this.onTranscription = e.onTranscription || (() => {
24
+ }), this.onRemoteTrack = e.onRemoteTrack || (() => {
22
25
  });
23
26
  }
27
+ /**
28
+ * Connect via HTTP signaling (no WebSocket)
29
+ */
24
30
  async connect() {
25
31
  console.log("đŸ”ĩ [WebRTC] Starting connection...");
26
32
  try {
@@ -36,16 +42,37 @@ class f {
36
42
  channelCount: 1
37
43
  }
38
44
  });
39
- console.log("🎤 [WebRTC] Microphone access granted"), e.getTracks().forEach((i) => {
40
- this.pc && this.pc.addTrack(i, e);
41
- }), this.dataChannel = this.pc.createDataChannel("control"), this.dataChannel.onopen = () => console.log("📡 [WebRTC] DataChannel opened"), this.dataChannel.onmessage = (i) => this.handleControlEvent(JSON.parse(i.data)), this.dataChannel.onerror = (i) => console.error("❌ [WebRTC] DataChannel error:", i), this.pc.ontrack = (i) => {
42
- const s = i.track, c = i.streams[0];
43
- console.log(`đŸ“Ĩ [WebRTC] Received ${s.kind} track`), s.kind === "audio" && (console.log("🔊 [WebRTC] Setting up audio playback"), this.audioElement = new Audio(), this.audioElement.srcObject = c, this.audioElement.play().catch((l) => console.warn("Audio autoplay blocked:", l)), this.audioElement.onended = () => {
44
- this.sendEvent("playedStream");
45
+ if (console.log("🎤 [WebRTC] Microphone access granted"), !this.pc) {
46
+ console.log("[WebRTC] Connection aborted: peer connection closed during setup"), e.getTracks().forEach((o) => o.stop());
47
+ return;
48
+ }
49
+ if (e.getTracks().forEach((o) => {
50
+ var r;
51
+ (r = this.pc) == null || r.addTrack(o, e);
52
+ }), !this.pc)
53
+ throw new Error("RTCPeerConnection was closed unexpectedly");
54
+ this.dataChannel = this.pc.createDataChannel("control"), this.dataChannel.onopen = () => console.log("📡 [WebRTC] DataChannel opened"), this.dataChannel.onmessage = (o) => {
55
+ if (typeof o.data == "string")
56
+ try {
57
+ this.handleControlEvent(JSON.parse(o.data));
58
+ } catch {
59
+ console.warn("[WebRTC] Failed to parse message:", o.data);
60
+ }
61
+ else o.data instanceof ArrayBuffer ? console.log("[WebRTC] Received binary data:", o.data.byteLength, "bytes") : o.data instanceof Blob && o.data.text().then((r) => {
62
+ try {
63
+ this.handleControlEvent(JSON.parse(r));
64
+ } catch {
65
+ console.warn("[WebRTC] Failed to parse blob data:", r);
66
+ }
45
67
  });
68
+ }, this.dataChannel.onerror = (o) => console.error("❌ [WebRTC] DataChannel error:", o), this.pc.ontrack = (o) => {
69
+ const r = o.track, c = o.streams[0];
70
+ console.log(`đŸ“Ĩ [WebRTC] Received ${r.kind} track`), r.kind === "audio" ? (console.log("🔊 [WebRTC] Received audio track from server"), this.audioElement = new Audio(), this.audioElement.srcObject = c, this.audioElement.play().catch((l) => console.warn("Audio autoplay blocked:", l)), this.audioElement.onended = () => {
71
+ this.sendEvent("playedStream"), console.log("✅ [WebRTC] TTS playback complete");
72
+ }) : r.kind === "video" && (console.log("📹 [WebRTC] Video track received"), this.onRemoteTrack(r, c));
46
73
  }, this.pc.onconnectionstatechange = () => {
47
- var i, s, c, l, h;
48
- console.log("🔄 [WebRTC] State:", (i = this.pc) == null ? void 0 : i.connectionState), ((s = this.pc) == null ? void 0 : s.connectionState) === "connected" ? (this.connected = !0, this.onConnected()) : (((c = this.pc) == null ? void 0 : c.connectionState) === "failed" || ((l = this.pc) == null ? void 0 : l.connectionState) === "disconnected" || ((h = this.pc) == null ? void 0 : h.connectionState) === "closed") && (this.connected = !1, this.onDisconnected());
74
+ var o, r, c, l, h;
75
+ console.log("🔄 [WebRTC] State:", (o = this.pc) == null ? void 0 : o.connectionState), ((r = this.pc) == null ? void 0 : r.connectionState) === "connected" ? (this.connected = !0, this.onConnected()) : (((c = this.pc) == null ? void 0 : c.connectionState) === "failed" || ((l = this.pc) == null ? void 0 : l.connectionState) === "disconnected" || ((h = this.pc) == null ? void 0 : h.connectionState) === "closed") && (this.connected = !1, this.onDisconnected());
49
76
  };
50
77
  const t = await this.pc.createOffer();
51
78
  await this.pc.setLocalDescription(t), console.log("📝 [WebRTC] Created offer, waiting for ICE gathering..."), await this.waitForIceGathering(), console.log("🧊 [WebRTC] ICE gathering complete"), console.log("📤 [WebRTC] Sending offer via HTTP...");
@@ -59,15 +86,18 @@ class f {
59
86
  })
60
87
  });
61
88
  if (!n.ok) {
62
- const i = await n.json();
63
- throw new Error(i.error || `HTTP ${n.status}`);
89
+ const o = await n.json();
90
+ throw new Error(o.error || `HTTP ${n.status}`);
64
91
  }
65
- const { answer: o } = await n.json();
66
- console.log("đŸ“Ĩ [WebRTC] Received answer from server"), await this.pc.setRemoteDescription(o), console.log("✅ [WebRTC] Connection established!");
92
+ const { answer: i } = await n.json();
93
+ console.log("đŸ“Ĩ [WebRTC] Received answer from server"), await this.pc.setRemoteDescription(i), console.log("✅ [WebRTC] Connection established!");
67
94
  } catch (e) {
68
- throw console.error("❌ [WebRTC] Connection failed:", e), this.onError(e.message || e), e;
95
+ throw console.error("❌ [WebRTC] Connection failed:", e), this.disconnect(), this.onError(e.message || e), e;
69
96
  }
70
97
  }
98
+ /**
99
+ * Wait for ICE gathering to complete
100
+ */
71
101
  waitForIceGathering() {
72
102
  return new Promise((e) => {
73
103
  if (!this.pc) return e();
@@ -75,8 +105,8 @@ class f {
75
105
  e();
76
106
  else {
77
107
  const t = () => {
78
- var n;
79
- ((n = this.pc) == null ? void 0 : n.iceGatheringState) === "complete" && (this.pc.removeEventListener("icegatheringstatechange", t), e());
108
+ var n, i;
109
+ ((n = this.pc) == null ? void 0 : n.iceGatheringState) === "complete" && ((i = this.pc) == null || i.removeEventListener("icegatheringstatechange", t), e());
80
110
  };
81
111
  this.pc.addEventListener("icegatheringstatechange", t), setTimeout(() => {
82
112
  var n;
@@ -85,17 +115,23 @@ class f {
85
115
  }
86
116
  });
87
117
  }
118
+ /**
119
+ * Send control event via DataChannel
120
+ */
88
121
  sendEvent(e, t = {}) {
89
122
  var n;
90
123
  ((n = this.dataChannel) == null ? void 0 : n.readyState) === "open" && this.dataChannel.send(JSON.stringify({ event: e, ...t }));
91
124
  }
125
+ /**
126
+ * Handle control events from server
127
+ */
92
128
  handleControlEvent(e) {
93
129
  switch (e.event) {
94
130
  case "clearAudio":
95
131
  console.log("🛑 [WebRTC] Interrupt: stopping audio"), this.audioElement && (this.audioElement.pause(), this.audioElement.currentTime = 0);
96
132
  break;
97
133
  case "transcription":
98
- console.log("📝 [WebRTC] Transcription:", e.text), e.text && this.onTranscription(e.text, !!e.isFinal);
134
+ console.log("📝 [WebRTC] Transcription:", e.text), this.onTranscription(e.text, e.isFinal);
99
135
  break;
100
136
  case "mark":
101
137
  console.log("đŸˇī¸ [WebRTC] Mark:", e.name);
@@ -104,15 +140,23 @@ class f {
104
140
  console.log("â„šī¸ [WebRTC] Unknown event:", e.event);
105
141
  }
106
142
  }
143
+ /**
144
+ * Disconnect and cleanup
145
+ */
107
146
  disconnect() {
108
- console.log("🔴 [WebRTC] Disconnecting..."), this.audioElement && (this.audioElement.pause(), this.audioElement.srcObject = null), this.dataChannel && this.dataChannel.close(), this.pc && this.pc.close(), this.connected = !1, this.onDisconnected();
147
+ console.log("🔴 [WebRTC] Disconnecting..."), this.audioElement && (this.audioElement.pause(), this.audioElement.srcObject = null), this.dataChannel && (this.dataChannel.close(), this.dataChannel = null), this.pc && (this.pc.getSenders().forEach((e) => {
148
+ e.track && e.track.stop();
149
+ }), this.pc.close(), this.pc = null), this.connected = !1, this.onDisconnected();
109
150
  }
151
+ /**
152
+ * Generate unique call ID
153
+ */
110
154
  generateCallId() {
111
155
  return "web_" + Date.now() + "_" + Math.random().toString(36).substr(2, 8);
112
156
  }
113
157
  }
114
158
  const w = "https://coredb.travelr.club/v1/graphql";
115
- async function v(a) {
159
+ async function v(s) {
116
160
  var t;
117
161
  const e = `
118
162
  query GetWidgetConfig($id: uuid!) {
@@ -131,19 +175,19 @@ async function v(a) {
131
175
  }
132
176
  `;
133
177
  try {
134
- const i = (t = (await (await fetch(w, {
178
+ const o = (t = (await (await fetch(w, {
135
179
  method: "POST",
136
180
  headers: {
137
181
  "Content-Type": "application/json"
138
182
  },
139
183
  body: JSON.stringify({
140
184
  query: e,
141
- variables: { id: a }
185
+ variables: { id: s }
142
186
  })
143
187
  })).json()).data) == null ? void 0 : t.app_widget_by_pk;
144
- if (!i)
145
- throw new Error(`Widget configuration not found for ID: ${a}`);
146
- return i;
188
+ if (!o)
189
+ throw new Error(`Widget configuration not found for ID: ${s}`);
190
+ return o;
147
191
  } catch (n) {
148
192
  throw console.error("[AvinAI] Failed to fetch widget config:", n), n;
149
193
  }
@@ -443,16 +487,16 @@ const d = {
443
487
  class y extends HTMLElement {
444
488
  constructor() {
445
489
  super();
446
- r(this, "isPanelOpen", !1);
447
- r(this, "callActive", !1);
448
- r(this, "transcript", "");
449
- r(this, "client", null);
450
- r(this, "shadow");
490
+ a(this, "isPanelOpen", !1);
491
+ a(this, "callActive", !1);
492
+ a(this, "transcript", "");
493
+ a(this, "client", null);
494
+ a(this, "shadow");
451
495
  // Configuration
452
- r(this, "widgetId", "");
453
- r(this, "serverUrl", "https://api.travelr.club");
496
+ a(this, "widgetId", "");
497
+ a(this, "serverUrl", "https://api.travelr.club");
454
498
  // Default
455
- r(this, "agentId", "");
499
+ a(this, "agentId", "");
456
500
  this.shadow = this.attachShadow({ mode: "open" });
457
501
  }
458
502
  static get observedAttributes() {
@@ -464,27 +508,27 @@ class y extends HTMLElement {
464
508
  this.render(t, n), this.attachEventListeners(), this.widgetId && !this.agentId && this.initializeConfig();
465
509
  }
466
510
  async initializeConfig() {
467
- var t, n, o, i;
511
+ var t, n, i, o;
468
512
  try {
469
513
  console.log(`[AvinAI Widget] Fetching config for widget: ${this.widgetId}`);
470
- const s = await v(this.widgetId);
471
- this.agentId = s.agent_id, ((i = (o = (n = (t = s.agent) == null ? void 0 : t.tts_voice) == null ? void 0 : n.provider) == null ? void 0 : o.root_name) == null ? void 0 : i.toLowerCase()) === "premium" ? this.serverUrl = "https://godspeed.travelr.club" : this.serverUrl = "https://in-godspeed.travelr.club", console.log(`[AvinAI Widget] Configured for Agent ID: ${this.agentId}`), console.log(`[AvinAI Widget] Using Server URL: ${this.serverUrl}`);
472
- } catch (s) {
473
- console.error("[AvinAI Widget] Configuration failed:", s), this.updateStatus("Configuration Failed", "error");
514
+ const r = await v(this.widgetId);
515
+ this.agentId = r.agent_id, ((o = (i = (n = (t = r.agent) == null ? void 0 : t.tts_voice) == null ? void 0 : n.provider) == null ? void 0 : i.root_name) == null ? void 0 : o.toLowerCase()) === "premium" ? this.serverUrl = "https://godspeed.travelr.club" : this.serverUrl = "https://in-godspeed.travelr.club", console.log(`[AvinAI Widget] Configured for Agent ID: ${this.agentId}`), console.log(`[AvinAI Widget] Using Server URL: ${this.serverUrl}`);
516
+ } catch (r) {
517
+ console.error("[AvinAI Widget] Configuration failed:", r), this.updateStatus("Configuration Failed", "error");
474
518
  }
475
519
  }
476
520
  render(t, n) {
477
- const o = {
521
+ const i = {
478
522
  "bottom-right": { bottom: "24px", right: "24px" },
479
523
  "bottom-left": { bottom: "24px", left: "24px" },
480
524
  "top-right": { top: "24px", right: "24px" },
481
525
  "top-left": { top: "24px", left: "24px" }
482
- }, i = o[t] || o["bottom-right"], s = Object.entries(i).map(([p, g]) => `${p}: ${g}`).join("; "), c = t.includes("bottom") ? "bottom: 100px;" : "top: 100px;", l = t.includes("right") ? "right: 24px;" : "left: 24px;", h = m.replace("--primary: #6366f1", `--primary: ${n}`);
526
+ }, o = i[t] || i["bottom-right"], r = Object.entries(o).map(([p, g]) => `${p}: ${g}`).join("; "), c = t.includes("bottom") ? "bottom: 100px;" : "top: 100px;", l = t.includes("right") ? "right: 24px;" : "left: 24px;", h = m.replace("--primary: #6366f1", `--primary: ${n}`);
483
527
  this.shadow.innerHTML = `
484
528
  <style>${h}</style>
485
529
 
486
530
  <!-- Floating Action Button -->
487
- <button class="widget-fab" style="${s}">
531
+ <button class="widget-fab" style="${r}">
488
532
  ${d.phone}
489
533
  </button>
490
534
 
@@ -518,18 +562,18 @@ class y extends HTMLElement {
518
562
  `;
519
563
  }
520
564
  attachEventListeners() {
521
- const t = this.shadow.querySelector(".widget-fab"), n = this.shadow.querySelector(".widget-panel"), o = this.shadow.querySelector("#close-panel"), i = this.shadow.querySelector("#start-btn");
565
+ const t = this.shadow.querySelector(".widget-fab"), n = this.shadow.querySelector(".widget-panel"), i = this.shadow.querySelector("#close-panel"), o = this.shadow.querySelector("#start-btn");
522
566
  t == null || t.addEventListener("click", () => {
523
567
  this.isPanelOpen = !this.isPanelOpen, n == null || n.classList.toggle("hidden", !this.isPanelOpen);
524
- }), o == null || o.addEventListener("click", () => {
525
- this.isPanelOpen = !1, n == null || n.classList.add("hidden");
526
568
  }), i == null || i.addEventListener("click", () => {
569
+ this.isPanelOpen = !1, n == null || n.classList.add("hidden");
570
+ }), o == null || o.addEventListener("click", () => {
527
571
  this.callActive ? this.stopCall() : this.startCall();
528
572
  });
529
573
  }
530
574
  updateStatus(t, n) {
531
- const o = this.shadow.querySelector("#status-text"), i = this.shadow.querySelector("#status-dot");
532
- o && (o.textContent = t), i && (n === "idle" && (i.style.background = "#ef4444"), n === "connecting" && (i.style.background = "#f59e0b"), n === "connected" && (i.style.background = "#10b981"), n === "error" && (i.style.background = "#ef4444"));
575
+ const i = this.shadow.querySelector("#status-text"), o = this.shadow.querySelector("#status-dot");
576
+ i && (i.textContent = t), o && (n === "idle" && (o.style.background = "#ef4444"), n === "connecting" && (o.style.background = "#f59e0b"), n === "connected" && (o.style.background = "#10b981"), n === "error" && (o.style.background = "#ef4444"));
533
577
  }
534
578
  async startCall() {
535
579
  if (!this.agentId) {
@@ -547,16 +591,16 @@ class y extends HTMLElement {
547
591
  onDisconnected: () => {
548
592
  this.stopCall();
549
593
  },
550
- onError: (o) => {
551
- this.updateStatus(`Error: ${o.message || "Connection failed"}`, "error"), t && (t.disabled = !1);
594
+ onError: (i) => {
595
+ this.updateStatus(`Error: ${i.message || "Connection failed"}`, "error"), t && (t.disabled = !1);
552
596
  },
553
- onTranscription: (o, i) => {
554
- i && (this.transcript += (this.transcript ? `
555
- ` : "") + o, n && (n.innerHTML = this.transcript, n.scrollTop = n.scrollHeight));
597
+ onTranscription: (i, o) => {
598
+ o && (this.transcript += (this.transcript ? `
599
+ ` : "") + i, n && (n.innerHTML = this.transcript, n.scrollTop = n.scrollHeight));
556
600
  }
557
601
  }), await this.client.connect();
558
- } catch (o) {
559
- console.error("[AvinAI Widget] Start call failed:", o), this.updateStatus("Connection failed", "error"), t && (t.disabled = !1);
602
+ } catch (i) {
603
+ console.error("[AvinAI Widget] Start call failed:", i), this.updateStatus("Connection failed", "error"), t && (t.disabled = !1);
560
604
  }
561
605
  }
562
606
  stopCall() {
package/dist/avin-ai.js CHANGED
@@ -1,4 +1,4 @@
1
- var x=Object.defineProperty;var y=(d,a,p)=>a in d?x(d,a,{enumerable:!0,configurable:!0,writable:!0,value:p}):d[a]=p;var r=(d,a,p)=>y(d,typeof a!="symbol"?a+"":a,p);(function(){"use strict";class d{constructor(n){r(this,"serverUrl");r(this,"agentId");r(this,"callId");r(this,"pc",null);r(this,"dataChannel",null);r(this,"audioElement",null);r(this,"connected",!1);r(this,"onConnected");r(this,"onDisconnected");r(this,"onError");r(this,"onTranscription");if(!n.serverUrl)throw new Error("serverUrl is required");if(!n.agentId)throw new Error("agentId is required");this.serverUrl=n.serverUrl.replace(/\/$/,""),this.agentId=n.agentId,this.callId=this.generateCallId(),this.onConnected=n.onConnected||(()=>{}),this.onDisconnected=n.onDisconnected||(()=>{}),this.onError=n.onError||(t=>console.error("[WebRTC]",t)),this.onTranscription=n.onTranscription||(()=>{})}async connect(){console.log("đŸ”ĩ [WebRTC] Starting connection...");try{this.pc=new RTCPeerConnection({iceServers:[{urls:"stun:stun.l.google.com:19302"}]}),this.pc.addTransceiver("video",{direction:"recvonly"});const n=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,sampleRate:{ideal:16e3},channelCount:1}});console.log("🎤 [WebRTC] Microphone access granted"),n.getTracks().forEach(i=>{this.pc&&this.pc.addTrack(i,n)}),this.dataChannel=this.pc.createDataChannel("control"),this.dataChannel.onopen=()=>console.log("📡 [WebRTC] DataChannel opened"),this.dataChannel.onmessage=i=>this.handleControlEvent(JSON.parse(i.data)),this.dataChannel.onerror=i=>console.error("❌ [WebRTC] DataChannel error:",i),this.pc.ontrack=i=>{const s=i.track,h=i.streams[0];console.log(`đŸ“Ĩ [WebRTC] Received ${s.kind} track`),s.kind==="audio"&&(console.log("🔊 [WebRTC] Setting up audio playback"),this.audioElement=new Audio,this.audioElement.srcObject=h,this.audioElement.play().catch(g=>console.warn("Audio autoplay blocked:",g)),this.audioElement.onended=()=>{this.sendEvent("playedStream")})},this.pc.onconnectionstatechange=()=>{var i,s,h,g,b;console.log("🔄 [WebRTC] State:",(i=this.pc)==null?void 0:i.connectionState),((s=this.pc)==null?void 0:s.connectionState)==="connected"?(this.connected=!0,this.onConnected()):(((h=this.pc)==null?void 0:h.connectionState)==="failed"||((g=this.pc)==null?void 0:g.connectionState)==="disconnected"||((b=this.pc)==null?void 0:b.connectionState)==="closed")&&(this.connected=!1,this.onDisconnected())};const t=await this.pc.createOffer();await this.pc.setLocalDescription(t),console.log("📝 [WebRTC] Created offer, waiting for ICE gathering..."),await this.waitForIceGathering(),console.log("🧊 [WebRTC] ICE gathering complete"),console.log("📤 [WebRTC] Sending offer via HTTP...");const e=await fetch(`${this.serverUrl}/webrtc?agent=${this.agentId}_${this.callId}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({offer:this.pc.localDescription,agentId:this.agentId,callId:this.callId})});if(!e.ok){const i=await e.json();throw new Error(i.error||`HTTP ${e.status}`)}const{answer:o}=await e.json();console.log("đŸ“Ĩ [WebRTC] Received answer from server"),await this.pc.setRemoteDescription(o),console.log("✅ [WebRTC] Connection established!")}catch(n){throw console.error("❌ [WebRTC] Connection failed:",n),this.onError(n.message||n),n}}waitForIceGathering(){return new Promise(n=>{if(!this.pc)return n();if(this.pc.iceGatheringState==="complete")n();else{const t=()=>{var e;((e=this.pc)==null?void 0:e.iceGatheringState)==="complete"&&(this.pc.removeEventListener("icegatheringstatechange",t),n())};this.pc.addEventListener("icegatheringstatechange",t),setTimeout(()=>{var e;(e=this.pc)==null||e.removeEventListener("icegatheringstatechange",t),console.warn("âš ī¸ [WebRTC] ICE gathering timeout, proceeding anyway"),n()},5e3)}})}sendEvent(n,t={}){var e;((e=this.dataChannel)==null?void 0:e.readyState)==="open"&&this.dataChannel.send(JSON.stringify({event:n,...t}))}handleControlEvent(n){switch(n.event){case"clearAudio":console.log("🛑 [WebRTC] Interrupt: stopping audio"),this.audioElement&&(this.audioElement.pause(),this.audioElement.currentTime=0);break;case"transcription":console.log("📝 [WebRTC] Transcription:",n.text),n.text&&this.onTranscription(n.text,!!n.isFinal);break;case"mark":console.log("đŸˇī¸ [WebRTC] Mark:",n.name);break;default:console.log("â„šī¸ [WebRTC] Unknown event:",n.event)}}disconnect(){console.log("🔴 [WebRTC] Disconnecting..."),this.audioElement&&(this.audioElement.pause(),this.audioElement.srcObject=null),this.dataChannel&&this.dataChannel.close(),this.pc&&this.pc.close(),this.connected=!1,this.onDisconnected()}generateCallId(){return"web_"+Date.now()+"_"+Math.random().toString(36).substr(2,8)}}const a="https://coredb.travelr.club/v1/graphql";async function p(c){var t;const n=`
1
+ var y=Object.defineProperty;var x=(l,s,p)=>s in l?y(l,s,{enumerable:!0,configurable:!0,writable:!0,value:p}):l[s]=p;var a=(l,s,p)=>x(l,typeof s!="symbol"?s+"":s,p);(function(){"use strict";class l{constructor(e){a(this,"serverUrl");a(this,"agentId");a(this,"callId");a(this,"onConnected");a(this,"onDisconnected");a(this,"onError");a(this,"onTranscription");a(this,"onRemoteTrack");a(this,"pc",null);a(this,"dataChannel",null);a(this,"audioElement",null);a(this,"connected",!1);if(!e.serverUrl)throw new Error("serverUrl is required");if(!e.agentId)throw new Error("agentId is required");this.serverUrl=e.serverUrl.replace(/\/$/,""),this.agentId=e.agentId,this.callId=e.callId||this.generateCallId(),this.onConnected=e.onConnected||(()=>{}),this.onDisconnected=e.onDisconnected||(()=>{}),this.onError=e.onError||(t=>console.error("[WebRTC]",t)),this.onTranscription=e.onTranscription||(()=>{}),this.onRemoteTrack=e.onRemoteTrack||(()=>{})}async connect(){console.log("đŸ”ĩ [WebRTC] Starting connection...");try{this.pc=new RTCPeerConnection({iceServers:[{urls:"stun:stun.l.google.com:19302"}]}),this.pc.addTransceiver("video",{direction:"recvonly"});const e=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,sampleRate:{ideal:16e3},channelCount:1}});if(console.log("🎤 [WebRTC] Microphone access granted"),!this.pc){console.log("[WebRTC] Connection aborted: peer connection closed during setup"),e.getTracks().forEach(i=>i.stop());return}if(e.getTracks().forEach(i=>{var r;(r=this.pc)==null||r.addTrack(i,e)}),!this.pc)throw new Error("RTCPeerConnection was closed unexpectedly");this.dataChannel=this.pc.createDataChannel("control"),this.dataChannel.onopen=()=>console.log("📡 [WebRTC] DataChannel opened"),this.dataChannel.onmessage=i=>{if(typeof i.data=="string")try{this.handleControlEvent(JSON.parse(i.data))}catch{console.warn("[WebRTC] Failed to parse message:",i.data)}else i.data instanceof ArrayBuffer?console.log("[WebRTC] Received binary data:",i.data.byteLength,"bytes"):i.data instanceof Blob&&i.data.text().then(r=>{try{this.handleControlEvent(JSON.parse(r))}catch{console.warn("[WebRTC] Failed to parse blob data:",r)}})},this.dataChannel.onerror=i=>console.error("❌ [WebRTC] DataChannel error:",i),this.pc.ontrack=i=>{const r=i.track,c=i.streams[0];console.log(`đŸ“Ĩ [WebRTC] Received ${r.kind} track`),r.kind==="audio"?(console.log("🔊 [WebRTC] Received audio track from server"),this.audioElement=new Audio,this.audioElement.srcObject=c,this.audioElement.play().catch(g=>console.warn("Audio autoplay blocked:",g)),this.audioElement.onended=()=>{this.sendEvent("playedStream"),console.log("✅ [WebRTC] TTS playback complete")}):r.kind==="video"&&(console.log("📹 [WebRTC] Video track received"),this.onRemoteTrack(r,c))},this.pc.onconnectionstatechange=()=>{var i,r,c,g,b;console.log("🔄 [WebRTC] State:",(i=this.pc)==null?void 0:i.connectionState),((r=this.pc)==null?void 0:r.connectionState)==="connected"?(this.connected=!0,this.onConnected()):(((c=this.pc)==null?void 0:c.connectionState)==="failed"||((g=this.pc)==null?void 0:g.connectionState)==="disconnected"||((b=this.pc)==null?void 0:b.connectionState)==="closed")&&(this.connected=!1,this.onDisconnected())};const t=await this.pc.createOffer();await this.pc.setLocalDescription(t),console.log("📝 [WebRTC] Created offer, waiting for ICE gathering..."),await this.waitForIceGathering(),console.log("🧊 [WebRTC] ICE gathering complete"),console.log("📤 [WebRTC] Sending offer via HTTP...");const n=await fetch(`${this.serverUrl}/webrtc?agent=${this.agentId}_${this.callId}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({offer:this.pc.localDescription,agentId:this.agentId,callId:this.callId})});if(!n.ok){const i=await n.json();throw new Error(i.error||`HTTP ${n.status}`)}const{answer:o}=await n.json();console.log("đŸ“Ĩ [WebRTC] Received answer from server"),await this.pc.setRemoteDescription(o),console.log("✅ [WebRTC] Connection established!")}catch(e){throw console.error("❌ [WebRTC] Connection failed:",e),this.disconnect(),this.onError(e.message||e),e}}waitForIceGathering(){return new Promise(e=>{if(!this.pc)return e();if(this.pc.iceGatheringState==="complete")e();else{const t=()=>{var n,o;((n=this.pc)==null?void 0:n.iceGatheringState)==="complete"&&((o=this.pc)==null||o.removeEventListener("icegatheringstatechange",t),e())};this.pc.addEventListener("icegatheringstatechange",t),setTimeout(()=>{var n;(n=this.pc)==null||n.removeEventListener("icegatheringstatechange",t),console.warn("âš ī¸ [WebRTC] ICE gathering timeout, proceeding anyway"),e()},5e3)}})}sendEvent(e,t={}){var n;((n=this.dataChannel)==null?void 0:n.readyState)==="open"&&this.dataChannel.send(JSON.stringify({event:e,...t}))}handleControlEvent(e){switch(e.event){case"clearAudio":console.log("🛑 [WebRTC] Interrupt: stopping audio"),this.audioElement&&(this.audioElement.pause(),this.audioElement.currentTime=0);break;case"transcription":console.log("📝 [WebRTC] Transcription:",e.text),this.onTranscription(e.text,e.isFinal);break;case"mark":console.log("đŸˇī¸ [WebRTC] Mark:",e.name);break;default:console.log("â„šī¸ [WebRTC] Unknown event:",e.event)}}disconnect(){console.log("🔴 [WebRTC] Disconnecting..."),this.audioElement&&(this.audioElement.pause(),this.audioElement.srcObject=null),this.dataChannel&&(this.dataChannel.close(),this.dataChannel=null),this.pc&&(this.pc.getSenders().forEach(e=>{e.track&&e.track.stop()}),this.pc.close(),this.pc=null),this.connected=!1,this.onDisconnected()}generateCallId(){return"web_"+Date.now()+"_"+Math.random().toString(36).substr(2,8)}}const s="https://coredb.travelr.club/v1/graphql";async function p(d){var t;const e=`
2
2
  query GetWidgetConfig($id: uuid!) {
3
3
  app_widget_by_pk(id: $id) {
4
4
  agent_id
@@ -13,7 +13,7 @@ var x=Object.defineProperty;var y=(d,a,p)=>a in d?x(d,a,{enumerable:!0,configura
13
13
  }
14
14
  }
15
15
  }
16
- `;try{const i=(t=(await(await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({query:n,variables:{id:c}})})).json()).data)==null?void 0:t.app_widget_by_pk;if(!i)throw new Error(`Widget configuration not found for ID: ${c}`);return i}catch(e){throw console.error("[AvinAI] Failed to fetch widget config:",e),e}}const u={phone:`
16
+ `;try{const i=(t=(await(await fetch(s,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({query:e,variables:{id:d}})})).json()).data)==null?void 0:t.app_widget_by_pk;if(!i)throw new Error(`Widget configuration not found for ID: ${d}`);return i}catch(n){throw console.error("[AvinAI] Failed to fetch widget config:",n),n}}const u={phone:`
17
17
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
18
18
  <path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/>
19
19
  </svg>
@@ -298,16 +298,16 @@ var x=Object.defineProperty;var y=(d,a,p)=>a in d?x(d,a,{enumerable:!0,configura
298
298
  border-radius: 0;
299
299
  }
300
300
  }
301
- `;class f extends HTMLElement{constructor(){super();r(this,"isPanelOpen",!1);r(this,"callActive",!1);r(this,"transcript","");r(this,"client",null);r(this,"shadow");r(this,"widgetId","");r(this,"serverUrl","https://api.travelr.club");r(this,"agentId","");this.shadow=this.attachShadow({mode:"open"})}static get observedAttributes(){return["widget-id","agent-id","position","primary-color","server-url"]}connectedCallback(){console.log("[AvinAI Widget] Initializing..."),this.widgetId=this.getAttribute("widget-id")||"",this.agentId=this.getAttribute("agent-id")||"",this.serverUrl=this.getAttribute("server-url")||"https://api.travelr.club";const t=this.getAttribute("position")||"bottom-right",e=this.getAttribute("primary-color")||"#6366f1";this.render(t,e),this.attachEventListeners(),this.widgetId&&!this.agentId&&this.initializeConfig()}async initializeConfig(){var t,e,o,i;try{console.log(`[AvinAI Widget] Fetching config for widget: ${this.widgetId}`);const s=await p(this.widgetId);this.agentId=s.agent_id,((i=(o=(e=(t=s.agent)==null?void 0:t.tts_voice)==null?void 0:e.provider)==null?void 0:o.root_name)==null?void 0:i.toLowerCase())==="premium"?this.serverUrl="https://godspeed.travelr.club":this.serverUrl="https://in-godspeed.travelr.club",console.log(`[AvinAI Widget] Configured for Agent ID: ${this.agentId}`),console.log(`[AvinAI Widget] Using Server URL: ${this.serverUrl}`)}catch(s){console.error("[AvinAI Widget] Configuration failed:",s),this.updateStatus("Configuration Failed","error")}}render(t,e){const o={"bottom-right":{bottom:"24px",right:"24px"},"bottom-left":{bottom:"24px",left:"24px"},"top-right":{top:"24px",right:"24px"},"top-left":{top:"24px",left:"24px"}},i=o[t]||o["bottom-right"],s=Object.entries(i).map(([v,m])=>`${v}: ${m}`).join("; "),h=t.includes("bottom")?"bottom: 100px;":"top: 100px;",g=t.includes("right")?"right: 24px;":"left: 24px;",b=w.replace("--primary: #6366f1",`--primary: ${e}`);this.shadow.innerHTML=`
301
+ `;class f extends HTMLElement{constructor(){super();a(this,"isPanelOpen",!1);a(this,"callActive",!1);a(this,"transcript","");a(this,"client",null);a(this,"shadow");a(this,"widgetId","");a(this,"serverUrl","https://api.travelr.club");a(this,"agentId","");this.shadow=this.attachShadow({mode:"open"})}static get observedAttributes(){return["widget-id","agent-id","position","primary-color","server-url"]}connectedCallback(){console.log("[AvinAI Widget] Initializing..."),this.widgetId=this.getAttribute("widget-id")||"",this.agentId=this.getAttribute("agent-id")||"",this.serverUrl=this.getAttribute("server-url")||"https://api.travelr.club";const t=this.getAttribute("position")||"bottom-right",n=this.getAttribute("primary-color")||"#6366f1";this.render(t,n),this.attachEventListeners(),this.widgetId&&!this.agentId&&this.initializeConfig()}async initializeConfig(){var t,n,o,i;try{console.log(`[AvinAI Widget] Fetching config for widget: ${this.widgetId}`);const r=await p(this.widgetId);this.agentId=r.agent_id,((i=(o=(n=(t=r.agent)==null?void 0:t.tts_voice)==null?void 0:n.provider)==null?void 0:o.root_name)==null?void 0:i.toLowerCase())==="premium"?this.serverUrl="https://godspeed.travelr.club":this.serverUrl="https://in-godspeed.travelr.club",console.log(`[AvinAI Widget] Configured for Agent ID: ${this.agentId}`),console.log(`[AvinAI Widget] Using Server URL: ${this.serverUrl}`)}catch(r){console.error("[AvinAI Widget] Configuration failed:",r),this.updateStatus("Configuration Failed","error")}}render(t,n){const o={"bottom-right":{bottom:"24px",right:"24px"},"bottom-left":{bottom:"24px",left:"24px"},"top-right":{top:"24px",right:"24px"},"top-left":{top:"24px",left:"24px"}},i=o[t]||o["bottom-right"],r=Object.entries(i).map(([v,m])=>`${v}: ${m}`).join("; "),c=t.includes("bottom")?"bottom: 100px;":"top: 100px;",g=t.includes("right")?"right: 24px;":"left: 24px;",b=w.replace("--primary: #6366f1",`--primary: ${n}`);this.shadow.innerHTML=`
302
302
  <style>${b}</style>
303
303
 
304
304
  <!-- Floating Action Button -->
305
- <button class="widget-fab" style="${s}">
305
+ <button class="widget-fab" style="${r}">
306
306
  ${u.phone}
307
307
  </button>
308
308
 
309
309
  <!-- Voice Panel -->
310
- <div class="widget-panel hidden" style="width: 380px; height: 480px; ${h} ${g}">
310
+ <div class="widget-panel hidden" style="width: 380px; height: 480px; ${c} ${g}">
311
311
  <div class="widget-header">
312
312
  <h3 class="widget-title">Voice Assistant</h3>
313
313
  <button class="close-btn" id="close-panel">
@@ -333,5 +333,5 @@ var x=Object.defineProperty;var y=(d,a,p)=>a in d?x(d,a,{enumerable:!0,configura
333
333
  </div>
334
334
  </div>
335
335
  </div>
336
- `}attachEventListeners(){const t=this.shadow.querySelector(".widget-fab"),e=this.shadow.querySelector(".widget-panel"),o=this.shadow.querySelector("#close-panel"),i=this.shadow.querySelector("#start-btn");t==null||t.addEventListener("click",()=>{this.isPanelOpen=!this.isPanelOpen,e==null||e.classList.toggle("hidden",!this.isPanelOpen)}),o==null||o.addEventListener("click",()=>{this.isPanelOpen=!1,e==null||e.classList.add("hidden")}),i==null||i.addEventListener("click",()=>{this.callActive?this.stopCall():this.startCall()})}updateStatus(t,e){const o=this.shadow.querySelector("#status-text"),i=this.shadow.querySelector("#status-dot");o&&(o.textContent=t),i&&(e==="idle"&&(i.style.background="#ef4444"),e==="connecting"&&(i.style.background="#f59e0b"),e==="connected"&&(i.style.background="#10b981"),e==="error"&&(i.style.background="#ef4444"))}async startCall(){if(!this.agentId){this.updateStatus("Error: Agent ID not configured","error");return}const t=this.shadow.querySelector("#start-btn"),e=this.shadow.querySelector("#transcript");try{this.updateStatus("Connecting...","connecting"),t&&(t.disabled=!0),this.client=new d({serverUrl:this.serverUrl,agentId:this.agentId,onConnected:()=>{this.callActive=!0,this.updateStatus("Connected - Listening...","connected"),t&&(t.innerHTML=`${u.close}<span>End Call</span>`,t.className="control-btn danger",t.disabled=!1)},onDisconnected:()=>{this.stopCall()},onError:o=>{this.updateStatus(`Error: ${o.message||"Connection failed"}`,"error"),t&&(t.disabled=!1)},onTranscription:(o,i)=>{i&&(this.transcript+=(this.transcript?`
337
- `:"")+o,e&&(e.innerHTML=this.transcript,e.scrollTop=e.scrollHeight))}}),await this.client.connect()}catch(o){console.error("[AvinAI Widget] Start call failed:",o),this.updateStatus("Connection failed","error"),t&&(t.disabled=!1)}}stopCall(){this.client&&(this.client.disconnect(),this.client=null),this.callActive=!1,this.updateStatus("Click Start to begin conversation","idle");const t=this.shadow.querySelector("#start-btn");t&&(t.innerHTML=`${u.mic}<span>Start Call</span>`,t.className="control-btn primary",t.disabled=!1)}disconnectedCallback(){this.client&&this.client.disconnect()}}customElements.get("avin-convai")||customElements.define("avin-convai",f),typeof window<"u"&&(window.AvinConvAI=f);const l=document.currentScript;if(l){const c=l.getAttribute("data-widget-id")||l.getAttribute("widget-id"),n=l.getAttribute("data-agent-id")||l.getAttribute("agent-id");if(c||n){const t=l.getAttribute("data-position")||"bottom-right",e=l.getAttribute("data-primary-color")||"#6366f1",o=document.createElement("avin-convai");c&&o.setAttribute("widget-id",c),n&&o.setAttribute("agent-id",n),o.setAttribute("position",t),o.setAttribute("primary-color",e),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>document.body.appendChild(o)):document.body.appendChild(o)}}})();
336
+ `}attachEventListeners(){const t=this.shadow.querySelector(".widget-fab"),n=this.shadow.querySelector(".widget-panel"),o=this.shadow.querySelector("#close-panel"),i=this.shadow.querySelector("#start-btn");t==null||t.addEventListener("click",()=>{this.isPanelOpen=!this.isPanelOpen,n==null||n.classList.toggle("hidden",!this.isPanelOpen)}),o==null||o.addEventListener("click",()=>{this.isPanelOpen=!1,n==null||n.classList.add("hidden")}),i==null||i.addEventListener("click",()=>{this.callActive?this.stopCall():this.startCall()})}updateStatus(t,n){const o=this.shadow.querySelector("#status-text"),i=this.shadow.querySelector("#status-dot");o&&(o.textContent=t),i&&(n==="idle"&&(i.style.background="#ef4444"),n==="connecting"&&(i.style.background="#f59e0b"),n==="connected"&&(i.style.background="#10b981"),n==="error"&&(i.style.background="#ef4444"))}async startCall(){if(!this.agentId){this.updateStatus("Error: Agent ID not configured","error");return}const t=this.shadow.querySelector("#start-btn"),n=this.shadow.querySelector("#transcript");try{this.updateStatus("Connecting...","connecting"),t&&(t.disabled=!0),this.client=new l({serverUrl:this.serverUrl,agentId:this.agentId,onConnected:()=>{this.callActive=!0,this.updateStatus("Connected - Listening...","connected"),t&&(t.innerHTML=`${u.close}<span>End Call</span>`,t.className="control-btn danger",t.disabled=!1)},onDisconnected:()=>{this.stopCall()},onError:o=>{this.updateStatus(`Error: ${o.message||"Connection failed"}`,"error"),t&&(t.disabled=!1)},onTranscription:(o,i)=>{i&&(this.transcript+=(this.transcript?`
337
+ `:"")+o,n&&(n.innerHTML=this.transcript,n.scrollTop=n.scrollHeight))}}),await this.client.connect()}catch(o){console.error("[AvinAI Widget] Start call failed:",o),this.updateStatus("Connection failed","error"),t&&(t.disabled=!1)}}stopCall(){this.client&&(this.client.disconnect(),this.client=null),this.callActive=!1,this.updateStatus("Click Start to begin conversation","idle");const t=this.shadow.querySelector("#start-btn");t&&(t.innerHTML=`${u.mic}<span>Start Call</span>`,t.className="control-btn primary",t.disabled=!1)}disconnectedCallback(){this.client&&this.client.disconnect()}}customElements.get("avin-convai")||customElements.define("avin-convai",f),typeof window<"u"&&(window.AvinConvAI=f);const h=document.currentScript;if(h){const d=h.getAttribute("data-widget-id")||h.getAttribute("widget-id"),e=h.getAttribute("data-agent-id")||h.getAttribute("agent-id");if(d||e){const t=h.getAttribute("data-position")||"bottom-right",n=h.getAttribute("data-primary-color")||"#6366f1",o=document.createElement("avin-convai");d&&o.setAttribute("widget-id",d),e&&o.setAttribute("agent-id",e),o.setAttribute("position",t),o.setAttribute("primary-color",n),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>document.body.appendChild(o)):document.body.appendChild(o)}}})();
@@ -1,4 +1,4 @@
1
- (function(s,a){typeof exports=="object"&&typeof module<"u"?a(exports):typeof define=="function"&&define.amd?define(["exports"],a):(s=typeof globalThis<"u"?globalThis:s||self,a(s.AvinAI={}))})(this,function(s){"use strict";var x=Object.defineProperty;var y=(s,a,d)=>a in s?x(s,a,{enumerable:!0,configurable:!0,writable:!0,value:d}):s[a]=d;var r=(s,a,d)=>y(s,typeof a!="symbol"?a+"":a,d);class a{constructor(n){r(this,"serverUrl");r(this,"agentId");r(this,"callId");r(this,"pc",null);r(this,"dataChannel",null);r(this,"audioElement",null);r(this,"connected",!1);r(this,"onConnected");r(this,"onDisconnected");r(this,"onError");r(this,"onTranscription");if(!n.serverUrl)throw new Error("serverUrl is required");if(!n.agentId)throw new Error("agentId is required");this.serverUrl=n.serverUrl.replace(/\/$/,""),this.agentId=n.agentId,this.callId=this.generateCallId(),this.onConnected=n.onConnected||(()=>{}),this.onDisconnected=n.onDisconnected||(()=>{}),this.onError=n.onError||(t=>console.error("[WebRTC]",t)),this.onTranscription=n.onTranscription||(()=>{})}async connect(){console.log("đŸ”ĩ [WebRTC] Starting connection...");try{this.pc=new RTCPeerConnection({iceServers:[{urls:"stun:stun.l.google.com:19302"}]}),this.pc.addTransceiver("video",{direction:"recvonly"});const n=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,sampleRate:{ideal:16e3},channelCount:1}});console.log("🎤 [WebRTC] Microphone access granted"),n.getTracks().forEach(i=>{this.pc&&this.pc.addTrack(i,n)}),this.dataChannel=this.pc.createDataChannel("control"),this.dataChannel.onopen=()=>console.log("📡 [WebRTC] DataChannel opened"),this.dataChannel.onmessage=i=>this.handleControlEvent(JSON.parse(i.data)),this.dataChannel.onerror=i=>console.error("❌ [WebRTC] DataChannel error:",i),this.pc.ontrack=i=>{const c=i.track,l=i.streams[0];console.log(`đŸ“Ĩ [WebRTC] Received ${c.kind} track`),c.kind==="audio"&&(console.log("🔊 [WebRTC] Setting up audio playback"),this.audioElement=new Audio,this.audioElement.srcObject=l,this.audioElement.play().catch(h=>console.warn("Audio autoplay blocked:",h)),this.audioElement.onended=()=>{this.sendEvent("playedStream")})},this.pc.onconnectionstatechange=()=>{var i,c,l,h,u;console.log("🔄 [WebRTC] State:",(i=this.pc)==null?void 0:i.connectionState),((c=this.pc)==null?void 0:c.connectionState)==="connected"?(this.connected=!0,this.onConnected()):(((l=this.pc)==null?void 0:l.connectionState)==="failed"||((h=this.pc)==null?void 0:h.connectionState)==="disconnected"||((u=this.pc)==null?void 0:u.connectionState)==="closed")&&(this.connected=!1,this.onDisconnected())};const t=await this.pc.createOffer();await this.pc.setLocalDescription(t),console.log("📝 [WebRTC] Created offer, waiting for ICE gathering..."),await this.waitForIceGathering(),console.log("🧊 [WebRTC] ICE gathering complete"),console.log("📤 [WebRTC] Sending offer via HTTP...");const e=await fetch(`${this.serverUrl}/webrtc?agent=${this.agentId}_${this.callId}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({offer:this.pc.localDescription,agentId:this.agentId,callId:this.callId})});if(!e.ok){const i=await e.json();throw new Error(i.error||`HTTP ${e.status}`)}const{answer:o}=await e.json();console.log("đŸ“Ĩ [WebRTC] Received answer from server"),await this.pc.setRemoteDescription(o),console.log("✅ [WebRTC] Connection established!")}catch(n){throw console.error("❌ [WebRTC] Connection failed:",n),this.onError(n.message||n),n}}waitForIceGathering(){return new Promise(n=>{if(!this.pc)return n();if(this.pc.iceGatheringState==="complete")n();else{const t=()=>{var e;((e=this.pc)==null?void 0:e.iceGatheringState)==="complete"&&(this.pc.removeEventListener("icegatheringstatechange",t),n())};this.pc.addEventListener("icegatheringstatechange",t),setTimeout(()=>{var e;(e=this.pc)==null||e.removeEventListener("icegatheringstatechange",t),console.warn("âš ī¸ [WebRTC] ICE gathering timeout, proceeding anyway"),n()},5e3)}})}sendEvent(n,t={}){var e;((e=this.dataChannel)==null?void 0:e.readyState)==="open"&&this.dataChannel.send(JSON.stringify({event:n,...t}))}handleControlEvent(n){switch(n.event){case"clearAudio":console.log("🛑 [WebRTC] Interrupt: stopping audio"),this.audioElement&&(this.audioElement.pause(),this.audioElement.currentTime=0);break;case"transcription":console.log("📝 [WebRTC] Transcription:",n.text),n.text&&this.onTranscription(n.text,!!n.isFinal);break;case"mark":console.log("đŸˇī¸ [WebRTC] Mark:",n.name);break;default:console.log("â„šī¸ [WebRTC] Unknown event:",n.event)}}disconnect(){console.log("🔴 [WebRTC] Disconnecting..."),this.audioElement&&(this.audioElement.pause(),this.audioElement.srcObject=null),this.dataChannel&&this.dataChannel.close(),this.pc&&this.pc.close(),this.connected=!1,this.onDisconnected()}generateCallId(){return"web_"+Date.now()+"_"+Math.random().toString(36).substr(2,8)}}const d="https://coredb.travelr.club/v1/graphql";async function f(g){var t;const n=`
1
+ (function(s,c){typeof exports=="object"&&typeof module<"u"?c(exports):typeof define=="function"&&define.amd?define(["exports"],c):(s=typeof globalThis<"u"?globalThis:s||self,c(s.AvinAI={}))})(this,function(s){"use strict";var y=Object.defineProperty;var x=(s,c,d)=>c in s?y(s,c,{enumerable:!0,configurable:!0,writable:!0,value:d}):s[c]=d;var a=(s,c,d)=>x(s,typeof c!="symbol"?c+"":c,d);class c{constructor(o){a(this,"serverUrl");a(this,"agentId");a(this,"callId");a(this,"onConnected");a(this,"onDisconnected");a(this,"onError");a(this,"onTranscription");a(this,"onRemoteTrack");a(this,"pc",null);a(this,"dataChannel",null);a(this,"audioElement",null);a(this,"connected",!1);if(!o.serverUrl)throw new Error("serverUrl is required");if(!o.agentId)throw new Error("agentId is required");this.serverUrl=o.serverUrl.replace(/\/$/,""),this.agentId=o.agentId,this.callId=o.callId||this.generateCallId(),this.onConnected=o.onConnected||(()=>{}),this.onDisconnected=o.onDisconnected||(()=>{}),this.onError=o.onError||(t=>console.error("[WebRTC]",t)),this.onTranscription=o.onTranscription||(()=>{}),this.onRemoteTrack=o.onRemoteTrack||(()=>{})}async connect(){console.log("đŸ”ĩ [WebRTC] Starting connection...");try{this.pc=new RTCPeerConnection({iceServers:[{urls:"stun:stun.l.google.com:19302"}]}),this.pc.addTransceiver("video",{direction:"recvonly"});const o=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,sampleRate:{ideal:16e3},channelCount:1}});if(console.log("🎤 [WebRTC] Microphone access granted"),!this.pc){console.log("[WebRTC] Connection aborted: peer connection closed during setup"),o.getTracks().forEach(n=>n.stop());return}if(o.getTracks().forEach(n=>{var r;(r=this.pc)==null||r.addTrack(n,o)}),!this.pc)throw new Error("RTCPeerConnection was closed unexpectedly");this.dataChannel=this.pc.createDataChannel("control"),this.dataChannel.onopen=()=>console.log("📡 [WebRTC] DataChannel opened"),this.dataChannel.onmessage=n=>{if(typeof n.data=="string")try{this.handleControlEvent(JSON.parse(n.data))}catch{console.warn("[WebRTC] Failed to parse message:",n.data)}else n.data instanceof ArrayBuffer?console.log("[WebRTC] Received binary data:",n.data.byteLength,"bytes"):n.data instanceof Blob&&n.data.text().then(r=>{try{this.handleControlEvent(JSON.parse(r))}catch{console.warn("[WebRTC] Failed to parse blob data:",r)}})},this.dataChannel.onerror=n=>console.error("❌ [WebRTC] DataChannel error:",n),this.pc.ontrack=n=>{const r=n.track,l=n.streams[0];console.log(`đŸ“Ĩ [WebRTC] Received ${r.kind} track`),r.kind==="audio"?(console.log("🔊 [WebRTC] Received audio track from server"),this.audioElement=new Audio,this.audioElement.srcObject=l,this.audioElement.play().catch(h=>console.warn("Audio autoplay blocked:",h)),this.audioElement.onended=()=>{this.sendEvent("playedStream"),console.log("✅ [WebRTC] TTS playback complete")}):r.kind==="video"&&(console.log("📹 [WebRTC] Video track received"),this.onRemoteTrack(r,l))},this.pc.onconnectionstatechange=()=>{var n,r,l,h,u;console.log("🔄 [WebRTC] State:",(n=this.pc)==null?void 0:n.connectionState),((r=this.pc)==null?void 0:r.connectionState)==="connected"?(this.connected=!0,this.onConnected()):(((l=this.pc)==null?void 0:l.connectionState)==="failed"||((h=this.pc)==null?void 0:h.connectionState)==="disconnected"||((u=this.pc)==null?void 0:u.connectionState)==="closed")&&(this.connected=!1,this.onDisconnected())};const t=await this.pc.createOffer();await this.pc.setLocalDescription(t),console.log("📝 [WebRTC] Created offer, waiting for ICE gathering..."),await this.waitForIceGathering(),console.log("🧊 [WebRTC] ICE gathering complete"),console.log("📤 [WebRTC] Sending offer via HTTP...");const e=await fetch(`${this.serverUrl}/webrtc?agent=${this.agentId}_${this.callId}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({offer:this.pc.localDescription,agentId:this.agentId,callId:this.callId})});if(!e.ok){const n=await e.json();throw new Error(n.error||`HTTP ${e.status}`)}const{answer:i}=await e.json();console.log("đŸ“Ĩ [WebRTC] Received answer from server"),await this.pc.setRemoteDescription(i),console.log("✅ [WebRTC] Connection established!")}catch(o){throw console.error("❌ [WebRTC] Connection failed:",o),this.disconnect(),this.onError(o.message||o),o}}waitForIceGathering(){return new Promise(o=>{if(!this.pc)return o();if(this.pc.iceGatheringState==="complete")o();else{const t=()=>{var e,i;((e=this.pc)==null?void 0:e.iceGatheringState)==="complete"&&((i=this.pc)==null||i.removeEventListener("icegatheringstatechange",t),o())};this.pc.addEventListener("icegatheringstatechange",t),setTimeout(()=>{var e;(e=this.pc)==null||e.removeEventListener("icegatheringstatechange",t),console.warn("âš ī¸ [WebRTC] ICE gathering timeout, proceeding anyway"),o()},5e3)}})}sendEvent(o,t={}){var e;((e=this.dataChannel)==null?void 0:e.readyState)==="open"&&this.dataChannel.send(JSON.stringify({event:o,...t}))}handleControlEvent(o){switch(o.event){case"clearAudio":console.log("🛑 [WebRTC] Interrupt: stopping audio"),this.audioElement&&(this.audioElement.pause(),this.audioElement.currentTime=0);break;case"transcription":console.log("📝 [WebRTC] Transcription:",o.text),this.onTranscription(o.text,o.isFinal);break;case"mark":console.log("đŸˇī¸ [WebRTC] Mark:",o.name);break;default:console.log("â„šī¸ [WebRTC] Unknown event:",o.event)}}disconnect(){console.log("🔴 [WebRTC] Disconnecting..."),this.audioElement&&(this.audioElement.pause(),this.audioElement.srcObject=null),this.dataChannel&&(this.dataChannel.close(),this.dataChannel=null),this.pc&&(this.pc.getSenders().forEach(o=>{o.track&&o.track.stop()}),this.pc.close(),this.pc=null),this.connected=!1,this.onDisconnected()}generateCallId(){return"web_"+Date.now()+"_"+Math.random().toString(36).substr(2,8)}}const d="https://coredb.travelr.club/v1/graphql";async function b(g){var t;const o=`
2
2
  query GetWidgetConfig($id: uuid!) {
3
3
  app_widget_by_pk(id: $id) {
4
4
  agent_id
@@ -13,7 +13,7 @@
13
13
  }
14
14
  }
15
15
  }
16
- `;try{const i=(t=(await(await fetch(d,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({query:n,variables:{id:g}})})).json()).data)==null?void 0:t.app_widget_by_pk;if(!i)throw new Error(`Widget configuration not found for ID: ${g}`);return i}catch(e){throw console.error("[AvinAI] Failed to fetch widget config:",e),e}}const p={phone:`
16
+ `;try{const n=(t=(await(await fetch(d,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({query:o,variables:{id:g}})})).json()).data)==null?void 0:t.app_widget_by_pk;if(!n)throw new Error(`Widget configuration not found for ID: ${g}`);return n}catch(e){throw console.error("[AvinAI] Failed to fetch widget config:",e),e}}const p={phone:`
17
17
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
18
18
  <path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/>
19
19
  </svg>
@@ -41,7 +41,7 @@
41
41
  <path d="M2 12c.6.5 1.2 1 2.5 1 2.5 0 2.5-2 5-2 2.6 0 2.4 2 5 2 2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1"/>
42
42
  <path d="M2 18c.6.5 1.2 1 2.5 1 2.5 0 2.5-2 5-2 2.6 0 2.4 2 5 2 2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1"/>
43
43
  </svg>
44
- `},b=`
44
+ `},f=`
45
45
  * {
46
46
  box-sizing: border-box;
47
47
  }
@@ -298,11 +298,11 @@
298
298
  border-radius: 0;
299
299
  }
300
300
  }
301
- `;class w extends HTMLElement{constructor(){super();r(this,"isPanelOpen",!1);r(this,"callActive",!1);r(this,"transcript","");r(this,"client",null);r(this,"shadow");r(this,"widgetId","");r(this,"serverUrl","https://api.travelr.club");r(this,"agentId","");this.shadow=this.attachShadow({mode:"open"})}static get observedAttributes(){return["widget-id","agent-id","position","primary-color","server-url"]}connectedCallback(){console.log("[AvinAI Widget] Initializing..."),this.widgetId=this.getAttribute("widget-id")||"",this.agentId=this.getAttribute("agent-id")||"",this.serverUrl=this.getAttribute("server-url")||"https://api.travelr.club";const t=this.getAttribute("position")||"bottom-right",e=this.getAttribute("primary-color")||"#6366f1";this.render(t,e),this.attachEventListeners(),this.widgetId&&!this.agentId&&this.initializeConfig()}async initializeConfig(){var t,e,o,i;try{console.log(`[AvinAI Widget] Fetching config for widget: ${this.widgetId}`);const c=await f(this.widgetId);this.agentId=c.agent_id,((i=(o=(e=(t=c.agent)==null?void 0:t.tts_voice)==null?void 0:e.provider)==null?void 0:o.root_name)==null?void 0:i.toLowerCase())==="premium"?this.serverUrl="https://godspeed.travelr.club":this.serverUrl="https://in-godspeed.travelr.club",console.log(`[AvinAI Widget] Configured for Agent ID: ${this.agentId}`),console.log(`[AvinAI Widget] Using Server URL: ${this.serverUrl}`)}catch(c){console.error("[AvinAI Widget] Configuration failed:",c),this.updateStatus("Configuration Failed","error")}}render(t,e){const o={"bottom-right":{bottom:"24px",right:"24px"},"bottom-left":{bottom:"24px",left:"24px"},"top-right":{top:"24px",right:"24px"},"top-left":{top:"24px",left:"24px"}},i=o[t]||o["bottom-right"],c=Object.entries(i).map(([v,m])=>`${v}: ${m}`).join("; "),l=t.includes("bottom")?"bottom: 100px;":"top: 100px;",h=t.includes("right")?"right: 24px;":"left: 24px;",u=b.replace("--primary: #6366f1",`--primary: ${e}`);this.shadow.innerHTML=`
301
+ `;class v extends HTMLElement{constructor(){super();a(this,"isPanelOpen",!1);a(this,"callActive",!1);a(this,"transcript","");a(this,"client",null);a(this,"shadow");a(this,"widgetId","");a(this,"serverUrl","https://api.travelr.club");a(this,"agentId","");this.shadow=this.attachShadow({mode:"open"})}static get observedAttributes(){return["widget-id","agent-id","position","primary-color","server-url"]}connectedCallback(){console.log("[AvinAI Widget] Initializing..."),this.widgetId=this.getAttribute("widget-id")||"",this.agentId=this.getAttribute("agent-id")||"",this.serverUrl=this.getAttribute("server-url")||"https://api.travelr.club";const t=this.getAttribute("position")||"bottom-right",e=this.getAttribute("primary-color")||"#6366f1";this.render(t,e),this.attachEventListeners(),this.widgetId&&!this.agentId&&this.initializeConfig()}async initializeConfig(){var t,e,i,n;try{console.log(`[AvinAI Widget] Fetching config for widget: ${this.widgetId}`);const r=await b(this.widgetId);this.agentId=r.agent_id,((n=(i=(e=(t=r.agent)==null?void 0:t.tts_voice)==null?void 0:e.provider)==null?void 0:i.root_name)==null?void 0:n.toLowerCase())==="premium"?this.serverUrl="https://godspeed.travelr.club":this.serverUrl="https://in-godspeed.travelr.club",console.log(`[AvinAI Widget] Configured for Agent ID: ${this.agentId}`),console.log(`[AvinAI Widget] Using Server URL: ${this.serverUrl}`)}catch(r){console.error("[AvinAI Widget] Configuration failed:",r),this.updateStatus("Configuration Failed","error")}}render(t,e){const i={"bottom-right":{bottom:"24px",right:"24px"},"bottom-left":{bottom:"24px",left:"24px"},"top-right":{top:"24px",right:"24px"},"top-left":{top:"24px",left:"24px"}},n=i[t]||i["bottom-right"],r=Object.entries(n).map(([w,m])=>`${w}: ${m}`).join("; "),l=t.includes("bottom")?"bottom: 100px;":"top: 100px;",h=t.includes("right")?"right: 24px;":"left: 24px;",u=f.replace("--primary: #6366f1",`--primary: ${e}`);this.shadow.innerHTML=`
302
302
  <style>${u}</style>
303
303
 
304
304
  <!-- Floating Action Button -->
305
- <button class="widget-fab" style="${c}">
305
+ <button class="widget-fab" style="${r}">
306
306
  ${p.phone}
307
307
  </button>
308
308
 
@@ -333,5 +333,5 @@
333
333
  </div>
334
334
  </div>
335
335
  </div>
336
- `}attachEventListeners(){const t=this.shadow.querySelector(".widget-fab"),e=this.shadow.querySelector(".widget-panel"),o=this.shadow.querySelector("#close-panel"),i=this.shadow.querySelector("#start-btn");t==null||t.addEventListener("click",()=>{this.isPanelOpen=!this.isPanelOpen,e==null||e.classList.toggle("hidden",!this.isPanelOpen)}),o==null||o.addEventListener("click",()=>{this.isPanelOpen=!1,e==null||e.classList.add("hidden")}),i==null||i.addEventListener("click",()=>{this.callActive?this.stopCall():this.startCall()})}updateStatus(t,e){const o=this.shadow.querySelector("#status-text"),i=this.shadow.querySelector("#status-dot");o&&(o.textContent=t),i&&(e==="idle"&&(i.style.background="#ef4444"),e==="connecting"&&(i.style.background="#f59e0b"),e==="connected"&&(i.style.background="#10b981"),e==="error"&&(i.style.background="#ef4444"))}async startCall(){if(!this.agentId){this.updateStatus("Error: Agent ID not configured","error");return}const t=this.shadow.querySelector("#start-btn"),e=this.shadow.querySelector("#transcript");try{this.updateStatus("Connecting...","connecting"),t&&(t.disabled=!0),this.client=new a({serverUrl:this.serverUrl,agentId:this.agentId,onConnected:()=>{this.callActive=!0,this.updateStatus("Connected - Listening...","connected"),t&&(t.innerHTML=`${p.close}<span>End Call</span>`,t.className="control-btn danger",t.disabled=!1)},onDisconnected:()=>{this.stopCall()},onError:o=>{this.updateStatus(`Error: ${o.message||"Connection failed"}`,"error"),t&&(t.disabled=!1)},onTranscription:(o,i)=>{i&&(this.transcript+=(this.transcript?`
337
- `:"")+o,e&&(e.innerHTML=this.transcript,e.scrollTop=e.scrollHeight))}}),await this.client.connect()}catch(o){console.error("[AvinAI Widget] Start call failed:",o),this.updateStatus("Connection failed","error"),t&&(t.disabled=!1)}}stopCall(){this.client&&(this.client.disconnect(),this.client=null),this.callActive=!1,this.updateStatus("Click Start to begin conversation","idle");const t=this.shadow.querySelector("#start-btn");t&&(t.innerHTML=`${p.mic}<span>Start Call</span>`,t.className="control-btn primary",t.disabled=!1)}disconnectedCallback(){this.client&&this.client.disconnect()}}s.AvinWidget=w,s.WebRTCClient=a,s.fetchWidgetConfig=f,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})});
336
+ `}attachEventListeners(){const t=this.shadow.querySelector(".widget-fab"),e=this.shadow.querySelector(".widget-panel"),i=this.shadow.querySelector("#close-panel"),n=this.shadow.querySelector("#start-btn");t==null||t.addEventListener("click",()=>{this.isPanelOpen=!this.isPanelOpen,e==null||e.classList.toggle("hidden",!this.isPanelOpen)}),i==null||i.addEventListener("click",()=>{this.isPanelOpen=!1,e==null||e.classList.add("hidden")}),n==null||n.addEventListener("click",()=>{this.callActive?this.stopCall():this.startCall()})}updateStatus(t,e){const i=this.shadow.querySelector("#status-text"),n=this.shadow.querySelector("#status-dot");i&&(i.textContent=t),n&&(e==="idle"&&(n.style.background="#ef4444"),e==="connecting"&&(n.style.background="#f59e0b"),e==="connected"&&(n.style.background="#10b981"),e==="error"&&(n.style.background="#ef4444"))}async startCall(){if(!this.agentId){this.updateStatus("Error: Agent ID not configured","error");return}const t=this.shadow.querySelector("#start-btn"),e=this.shadow.querySelector("#transcript");try{this.updateStatus("Connecting...","connecting"),t&&(t.disabled=!0),this.client=new c({serverUrl:this.serverUrl,agentId:this.agentId,onConnected:()=>{this.callActive=!0,this.updateStatus("Connected - Listening...","connected"),t&&(t.innerHTML=`${p.close}<span>End Call</span>`,t.className="control-btn danger",t.disabled=!1)},onDisconnected:()=>{this.stopCall()},onError:i=>{this.updateStatus(`Error: ${i.message||"Connection failed"}`,"error"),t&&(t.disabled=!1)},onTranscription:(i,n)=>{n&&(this.transcript+=(this.transcript?`
337
+ `:"")+i,e&&(e.innerHTML=this.transcript,e.scrollTop=e.scrollHeight))}}),await this.client.connect()}catch(i){console.error("[AvinAI Widget] Start call failed:",i),this.updateStatus("Connection failed","error"),t&&(t.disabled=!1)}}stopCall(){this.client&&(this.client.disconnect(),this.client=null),this.callActive=!1,this.updateStatus("Click Start to begin conversation","idle");const t=this.shadow.querySelector("#start-btn");t&&(t.innerHTML=`${p.mic}<span>Start Call</span>`,t.className="control-btn primary",t.disabled=!1)}disconnectedCallback(){this.client&&this.client.disconnect()}}s.AvinWidget=v,s.WebRTCClient=c,s.fetchWidgetConfig=b,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})});
package/dist/index.d.ts CHANGED
@@ -45,30 +45,51 @@ export declare class WebRTCClient {
45
45
  private serverUrl;
46
46
  private agentId;
47
47
  private callId;
48
- private pc;
49
- private dataChannel;
50
- private audioElement;
51
- connected: boolean;
52
48
  private onConnected;
53
49
  private onDisconnected;
54
50
  private onError;
55
51
  private onTranscription;
52
+ private onRemoteTrack;
53
+ private pc;
54
+ private dataChannel;
55
+ private audioElement;
56
+ connected: boolean;
56
57
  constructor(config: WebRTCClientConfig);
58
+ /**
59
+ * Connect via HTTP signaling (no WebSocket)
60
+ */
57
61
  connect(): Promise<void>;
62
+ /**
63
+ * Wait for ICE gathering to complete
64
+ */
58
65
  private waitForIceGathering;
59
- sendEvent(event: string, payload?: {}): void;
66
+ /**
67
+ * Send control event via DataChannel
68
+ */
69
+ sendEvent(event: string, data?: {}): void;
70
+ /**
71
+ * Handle control events from server
72
+ */
60
73
  private handleControlEvent;
74
+ /**
75
+ * Disconnect and cleanup
76
+ */
61
77
  disconnect(): void;
62
- private generateCallId;
78
+ /**
79
+ * Generate unique call ID
80
+ */
81
+ generateCallId(): string;
63
82
  }
64
83
 
65
84
  export declare interface WebRTCClientConfig {
66
85
  serverUrl: string;
67
86
  agentId: string;
87
+ callId?: string;
68
88
  onConnected?: () => void;
69
89
  onDisconnected?: () => void;
70
90
  onError?: (error: any) => void;
71
91
  onTranscription?: (text: string, isFinal: boolean) => void;
92
+ onRemoteTrack?: (track: MediaStreamTrack, stream: MediaStream) => void;
72
93
  }
73
94
 
74
95
  export declare interface WidgetConfig {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "avin-ai",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "AvinAI Voice Widget SDK",
5
5
  "type": "module",
6
6
  "main": "./dist/avin-ai.umd.js",