@guava-ai/guava-sdk 0.37.0 → 0.38.0
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 +44 -2
- package/dist/guava-webrtc.esm.min.js +2 -0
- package/dist/guava-webrtc.esm.min.js.map +7 -0
- package/dist/guava-webrtc.min.js +2 -0
- package/dist/guava-webrtc.min.js.map +7 -0
- package/dist/src/call.d.ts +5 -4
- package/dist/src/call.js +4 -4
- package/dist/src/call.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +3 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/sip.d.ts +15 -0
- package/dist/src/sip.js +52 -0
- package/dist/src/sip.js.map +1 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/webrtc/index.d.ts +42 -0
- package/dist/src/webrtc/index.js +299 -0
- package/dist/src/webrtc/index.js.map +1 -0
- package/dist/src/webrtc/tones.d.ts +31 -0
- package/dist/src/webrtc/tones.js +172 -0
- package/dist/src/webrtc/tones.js.map +1 -0
- package/package.json +16 -3
- package/src/call.ts +7 -6
- package/src/index.ts +1 -0
- package/src/sip.ts +62 -0
- package/src/version.ts +1 -1
- package/src/webrtc/index.ts +372 -0
- package/src/webrtc/tones.ts +200 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Call = exports.CallError = exports.RingbackTone = exports.JingleTone = void 0;
|
|
4
|
+
const tones_ts_1 = require("./tones.js");
|
|
5
|
+
var tones_ts_2 = require("./tones.js");
|
|
6
|
+
Object.defineProperty(exports, "JingleTone", { enumerable: true, get: function () { return tones_ts_2.JingleTone; } });
|
|
7
|
+
Object.defineProperty(exports, "RingbackTone", { enumerable: true, get: function () { return tones_ts_2.RingbackTone; } });
|
|
8
|
+
class CallError extends Error {
|
|
9
|
+
code;
|
|
10
|
+
constructor(code, message, options) {
|
|
11
|
+
super(message, options);
|
|
12
|
+
this.name = "CallError";
|
|
13
|
+
this.code = code;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.CallError = CallError;
|
|
17
|
+
// Used if the server doesn't send ICE servers with the auth reply.
|
|
18
|
+
const DEFAULT_ICE_SERVERS = [
|
|
19
|
+
{ urls: ["stun:stun.l.google.com:19302", "stun:stun.sipgate.net:10000"] },
|
|
20
|
+
];
|
|
21
|
+
const SOCKET_PROTOCOLS = {
|
|
22
|
+
"http:": "ws:",
|
|
23
|
+
"https:": "wss:",
|
|
24
|
+
"ws:": "ws:",
|
|
25
|
+
"wss:": "wss:",
|
|
26
|
+
};
|
|
27
|
+
class Call {
|
|
28
|
+
_state = "not-started";
|
|
29
|
+
_error;
|
|
30
|
+
_stateHandlers = new Set();
|
|
31
|
+
_remoteStreamHandlers = new Set();
|
|
32
|
+
_webrtcCode;
|
|
33
|
+
_sockUrl;
|
|
34
|
+
_remoteAudio;
|
|
35
|
+
_connectingTone;
|
|
36
|
+
_sock;
|
|
37
|
+
_localStream;
|
|
38
|
+
_peerConnection;
|
|
39
|
+
_remoteStream;
|
|
40
|
+
_iceServers;
|
|
41
|
+
// Settles the promise returned by start().
|
|
42
|
+
_pendingStart;
|
|
43
|
+
constructor({ webrtcCode, baseUrl = "https://app.goguava.ai/", connectingTone = new tones_ts_1.JingleTone(), }) {
|
|
44
|
+
this._webrtcCode = webrtcCode;
|
|
45
|
+
if (connectingTone !== "disabled") {
|
|
46
|
+
this._connectingTone = connectingTone;
|
|
47
|
+
}
|
|
48
|
+
this._sockUrl = new URL("/webrtc/", baseUrl);
|
|
49
|
+
const sockProtocol = SOCKET_PROTOCOLS[this._sockUrl.protocol];
|
|
50
|
+
if (sockProtocol === undefined) {
|
|
51
|
+
throw new Error(`Unsupported baseUrl protocol: ${this._sockUrl.protocol}`);
|
|
52
|
+
}
|
|
53
|
+
this._sockUrl.protocol = sockProtocol;
|
|
54
|
+
this._remoteAudio = document.createElement("audio");
|
|
55
|
+
this._remoteAudio.autoplay = true;
|
|
56
|
+
}
|
|
57
|
+
get state() {
|
|
58
|
+
return this._state;
|
|
59
|
+
}
|
|
60
|
+
// Set if the call ended due to a failure.
|
|
61
|
+
get error() {
|
|
62
|
+
return this._error;
|
|
63
|
+
}
|
|
64
|
+
// Set once the remote audio track arrives, which happens before the call reaches "connected".
|
|
65
|
+
get remoteStream() {
|
|
66
|
+
return this._remoteStream;
|
|
67
|
+
}
|
|
68
|
+
// Returns a function that unregisters the handler.
|
|
69
|
+
onStateChange(handler) {
|
|
70
|
+
this._stateHandlers.add(handler);
|
|
71
|
+
return () => {
|
|
72
|
+
this._stateHandlers.delete(handler);
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Fires whenever the remote stream is replaced, including the first time it arrives.
|
|
76
|
+
// Returns a function that unregisters the handler.
|
|
77
|
+
onRemoteStream(handler) {
|
|
78
|
+
this._remoteStreamHandlers.add(handler);
|
|
79
|
+
return () => {
|
|
80
|
+
this._remoteStreamHandlers.delete(handler);
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
_setState(state) {
|
|
84
|
+
if (state === this._state) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
this._state = state;
|
|
88
|
+
if (state === "connecting") {
|
|
89
|
+
this._connectingTone?.start();
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
this._connectingTone?.stop();
|
|
93
|
+
}
|
|
94
|
+
for (const handler of this._stateHandlers) {
|
|
95
|
+
handler(state);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async start() {
|
|
99
|
+
if (this._state !== "not-started") {
|
|
100
|
+
throw new Error(`start() was already called.`);
|
|
101
|
+
}
|
|
102
|
+
this._setState("starting");
|
|
103
|
+
// Add the audio element.
|
|
104
|
+
document.body.appendChild(this._remoteAudio);
|
|
105
|
+
// We start by requesting device permissions first. The idea is that, after permission has been granted,
|
|
106
|
+
// it makes sence to play a ringback, but it doesn't make sense to do so during the permission prompt.
|
|
107
|
+
const permissionTimer = setTimeout(() => {
|
|
108
|
+
this._setState("waiting-for-permission");
|
|
109
|
+
}, 500);
|
|
110
|
+
try {
|
|
111
|
+
this._localStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
const callError = new CallError("MICROPHONE_ACCESS_FAILED", "Failed to access microphone.", {
|
|
115
|
+
cause: error,
|
|
116
|
+
});
|
|
117
|
+
this._end(callError);
|
|
118
|
+
throw callError;
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
clearTimeout(permissionTimer);
|
|
122
|
+
}
|
|
123
|
+
// hangup() was called during the permission prompt.
|
|
124
|
+
if (this.state === "ended") {
|
|
125
|
+
this._teardown();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
// Now open and authenticate the signaling socket.
|
|
129
|
+
this._setState("connecting"); // UI can start playing rinback tones here.
|
|
130
|
+
const started = new Promise((resolve, reject) => {
|
|
131
|
+
this._pendingStart = { resolve, reject };
|
|
132
|
+
});
|
|
133
|
+
this._sock = new WebSocket(this._sockUrl);
|
|
134
|
+
this._sock.onopen = () => {
|
|
135
|
+
this._send({ type: "auth", body: this._webrtcCode });
|
|
136
|
+
};
|
|
137
|
+
this._sock.onmessage = (event) => {
|
|
138
|
+
this._onMessage(JSON.parse(event.data));
|
|
139
|
+
};
|
|
140
|
+
this._sock.onclose = () => {
|
|
141
|
+
this._end(new CallError("CONNECTION_FAILED", "Signaling connection closed."));
|
|
142
|
+
};
|
|
143
|
+
await started;
|
|
144
|
+
}
|
|
145
|
+
async hangup() {
|
|
146
|
+
if (this._state === "not-started") {
|
|
147
|
+
throw new Error(`hangup() called before start().`);
|
|
148
|
+
}
|
|
149
|
+
if (this._state === "ended") {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
this._send({ type: "hangup" });
|
|
153
|
+
this._end();
|
|
154
|
+
}
|
|
155
|
+
_send(message) {
|
|
156
|
+
if (this._sock !== undefined && this._sock.readyState === WebSocket.OPEN) {
|
|
157
|
+
this._sock.send(JSON.stringify(message));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
_onMessage(message) {
|
|
161
|
+
switch (message.type) {
|
|
162
|
+
case "auth":
|
|
163
|
+
if (message.body !== "OK") {
|
|
164
|
+
this._end(new CallError("AUTHENTICATION_FAILED", message.reason || "Authentication failed."));
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
this._iceServers = message.ice_servers;
|
|
168
|
+
this._send({ type: "connect", body: {} });
|
|
169
|
+
void this._connectPeer();
|
|
170
|
+
break;
|
|
171
|
+
case "desc":
|
|
172
|
+
if (!this._peerConnection) {
|
|
173
|
+
this._end(new CallError("PROTOCOL_ERROR", "Received desc before the peer connection was created."));
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
this._peerConnection.setRemoteDescription(message.body).catch((error) => {
|
|
177
|
+
this._end(new CallError("CONNECTION_FAILED", "Failed to set remote description.", {
|
|
178
|
+
cause: error,
|
|
179
|
+
}));
|
|
180
|
+
});
|
|
181
|
+
break;
|
|
182
|
+
case "candidate":
|
|
183
|
+
if (!this._peerConnection) {
|
|
184
|
+
this._end(new CallError("PROTOCOL_ERROR", "Received candidate before the peer connection was created."));
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
this._peerConnection.addIceCandidate(message.body).catch((error) => {
|
|
188
|
+
console.warn("Failed to add ICE candidate:", error);
|
|
189
|
+
});
|
|
190
|
+
break;
|
|
191
|
+
case "hangup":
|
|
192
|
+
if (this._state === "connected") {
|
|
193
|
+
this._end();
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
this._end(new CallError("REMOTE_HANGUP", "The remote hung up before the call connected."));
|
|
197
|
+
}
|
|
198
|
+
break;
|
|
199
|
+
case "error":
|
|
200
|
+
this._end(new CallError("SERVER_ERROR", message.body || "Unknown server error."));
|
|
201
|
+
break;
|
|
202
|
+
case "transfer":
|
|
203
|
+
this._end(new CallError("PROTOCOL_ERROR", "Transfers are not supported."));
|
|
204
|
+
break;
|
|
205
|
+
default:
|
|
206
|
+
console.warn("Unknown signaling message:", message);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async _connectPeer() {
|
|
210
|
+
if (!this._localStream) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
this._peerConnection = new RTCPeerConnection({
|
|
214
|
+
iceServers: this._iceServers ?? DEFAULT_ICE_SERVERS,
|
|
215
|
+
});
|
|
216
|
+
this._peerConnection.onicecandidate = (event) => {
|
|
217
|
+
if (event.candidate) {
|
|
218
|
+
this._send({ type: "candidate", body: event.candidate });
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
this._peerConnection.ontrack = (event) => {
|
|
222
|
+
this._remoteStream = new MediaStream([event.track]);
|
|
223
|
+
this._remoteAudio.srcObject = this._remoteStream;
|
|
224
|
+
for (const handler of this._remoteStreamHandlers) {
|
|
225
|
+
handler(this._remoteStream);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
this._peerConnection.onconnectionstatechange = () => {
|
|
229
|
+
if (this._peerConnection === undefined) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
// "disconnected" is often transient, so wait for "failed" before ending the call.
|
|
233
|
+
if (this._peerConnection.connectionState === "connected") {
|
|
234
|
+
this._setState("connected");
|
|
235
|
+
if (this._pendingStart !== undefined) {
|
|
236
|
+
this._pendingStart.resolve();
|
|
237
|
+
this._pendingStart = undefined;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
else if (this._peerConnection.connectionState === "failed") {
|
|
241
|
+
this._end(new CallError("CONNECTION_FAILED", "Peer connection failed."));
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
for (const track of this._localStream.getAudioTracks()) {
|
|
245
|
+
this._peerConnection.addTrack(track);
|
|
246
|
+
}
|
|
247
|
+
try {
|
|
248
|
+
await this._peerConnection.setLocalDescription();
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
this._end(new CallError("CONNECTION_FAILED", "Failed to create offer.", { cause: error }));
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
// The call may have ended while creating the offer.
|
|
255
|
+
if (this.state === "ended") {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
this._send({ type: "desc", body: this._peerConnection.localDescription });
|
|
259
|
+
}
|
|
260
|
+
_end(error) {
|
|
261
|
+
if (this._state === "ended") {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
this._error = error;
|
|
265
|
+
this._teardown();
|
|
266
|
+
this._setState("ended");
|
|
267
|
+
// If start() is still pending, it rejects on failure and resolves on hangup().
|
|
268
|
+
if (this._pendingStart !== undefined) {
|
|
269
|
+
if (error) {
|
|
270
|
+
this._pendingStart.reject(error);
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
this._pendingStart.resolve();
|
|
274
|
+
}
|
|
275
|
+
this._pendingStart = undefined;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
_teardown() {
|
|
279
|
+
if (this._sock) {
|
|
280
|
+
// Don't fire close handlers for a socket we closed ourselves.
|
|
281
|
+
this._sock.onclose = null;
|
|
282
|
+
this._sock.close();
|
|
283
|
+
this._sock = undefined;
|
|
284
|
+
}
|
|
285
|
+
if (this._peerConnection !== undefined) {
|
|
286
|
+
this._peerConnection.close();
|
|
287
|
+
this._peerConnection = undefined;
|
|
288
|
+
}
|
|
289
|
+
for (const track of this._localStream?.getTracks() ?? []) {
|
|
290
|
+
track.stop();
|
|
291
|
+
}
|
|
292
|
+
this._localStream = undefined;
|
|
293
|
+
this._remoteStream = undefined;
|
|
294
|
+
this._remoteAudio.srcObject = null;
|
|
295
|
+
this._remoteAudio.remove();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
exports.Call = Call;
|
|
299
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/webrtc/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface ConnectingTone {
|
|
2
|
+
start(): void;
|
|
3
|
+
stop(): void;
|
|
4
|
+
}
|
|
5
|
+
export declare class RingbackTone implements ConnectingTone {
|
|
6
|
+
private context;
|
|
7
|
+
private intervalId;
|
|
8
|
+
private readonly volume;
|
|
9
|
+
private readonly onSeconds;
|
|
10
|
+
private readonly periodSeconds;
|
|
11
|
+
private readonly startDelay;
|
|
12
|
+
start(): void;
|
|
13
|
+
stop(): void;
|
|
14
|
+
}
|
|
15
|
+
export declare class JingleTone implements ConnectingTone {
|
|
16
|
+
private context;
|
|
17
|
+
private masterGain;
|
|
18
|
+
private intervalId;
|
|
19
|
+
private running;
|
|
20
|
+
private pulseCount;
|
|
21
|
+
private nextBarStart;
|
|
22
|
+
private readonly volume;
|
|
23
|
+
private readonly bpm;
|
|
24
|
+
private readonly beatsPerBar;
|
|
25
|
+
private readonly startDelay;
|
|
26
|
+
start(): void;
|
|
27
|
+
private noteToFrequency;
|
|
28
|
+
stop(): void;
|
|
29
|
+
private playPulse;
|
|
30
|
+
private playNote;
|
|
31
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JingleTone = exports.RingbackTone = void 0;
|
|
4
|
+
// Seconds from the start of bar 0 to the given 0-indexed beat.
|
|
5
|
+
function beatTime(beat, bpm) {
|
|
6
|
+
return beat * (60 / bpm);
|
|
7
|
+
}
|
|
8
|
+
// North American ringback: 440 + 480 Hz, 2s on, 4s off.
|
|
9
|
+
class RingbackTone {
|
|
10
|
+
context = null;
|
|
11
|
+
intervalId = null;
|
|
12
|
+
volume = 0.15;
|
|
13
|
+
onSeconds = 2;
|
|
14
|
+
periodSeconds = 6;
|
|
15
|
+
// Delay before the first ring, so near-instant connections stay silent.
|
|
16
|
+
startDelay = 0.3;
|
|
17
|
+
start() {
|
|
18
|
+
if (this.context)
|
|
19
|
+
return;
|
|
20
|
+
const context = new AudioContext();
|
|
21
|
+
const gain = context.createGain();
|
|
22
|
+
gain.gain.value = 0;
|
|
23
|
+
gain.connect(context.destination);
|
|
24
|
+
for (const frequency of [440, 480]) {
|
|
25
|
+
const oscillator = context.createOscillator();
|
|
26
|
+
oscillator.frequency.value = frequency;
|
|
27
|
+
oscillator.connect(gain);
|
|
28
|
+
oscillator.start();
|
|
29
|
+
}
|
|
30
|
+
let nextRing = context.currentTime + this.startDelay;
|
|
31
|
+
const ring = () => {
|
|
32
|
+
gain.gain.setValueAtTime(this.volume, nextRing);
|
|
33
|
+
gain.gain.setValueAtTime(0, nextRing + this.onSeconds);
|
|
34
|
+
nextRing += this.periodSeconds;
|
|
35
|
+
};
|
|
36
|
+
ring();
|
|
37
|
+
this.intervalId = setInterval(ring, this.periodSeconds * 1000);
|
|
38
|
+
this.context = context;
|
|
39
|
+
}
|
|
40
|
+
stop() {
|
|
41
|
+
if (!this.context)
|
|
42
|
+
return;
|
|
43
|
+
if (this.intervalId) {
|
|
44
|
+
clearInterval(this.intervalId);
|
|
45
|
+
this.intervalId = null;
|
|
46
|
+
}
|
|
47
|
+
// Closing the context stops the oscillators.
|
|
48
|
+
void this.context.close();
|
|
49
|
+
this.context = null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.RingbackTone = RingbackTone;
|
|
53
|
+
class JingleTone {
|
|
54
|
+
context = null;
|
|
55
|
+
masterGain = null;
|
|
56
|
+
intervalId = null;
|
|
57
|
+
running = false;
|
|
58
|
+
pulseCount = 0;
|
|
59
|
+
// Audio-clock time of the next bar to schedule.
|
|
60
|
+
nextBarStart = 0;
|
|
61
|
+
// Configuration
|
|
62
|
+
volume = 0.25;
|
|
63
|
+
bpm = 120;
|
|
64
|
+
beatsPerBar = 4;
|
|
65
|
+
// Delay before the first note, so near-instant connections stay silent.
|
|
66
|
+
startDelay = 0.3;
|
|
67
|
+
start() {
|
|
68
|
+
if (this.running)
|
|
69
|
+
return;
|
|
70
|
+
this.running = true;
|
|
71
|
+
this.pulseCount = 0;
|
|
72
|
+
this.context = new AudioContext();
|
|
73
|
+
this.masterGain = this.context.createGain();
|
|
74
|
+
this.masterGain.gain.value = this.volume;
|
|
75
|
+
this.masterGain.connect(this.context.destination);
|
|
76
|
+
this.nextBarStart = this.context.currentTime + this.startDelay;
|
|
77
|
+
// Stay one bar ahead so timer jitter doesn't shift the beat.
|
|
78
|
+
this.playPulse();
|
|
79
|
+
this.playPulse();
|
|
80
|
+
const barLength = beatTime(this.beatsPerBar, this.bpm);
|
|
81
|
+
this.intervalId = setInterval(() => {
|
|
82
|
+
this.playPulse();
|
|
83
|
+
}, barLength * 1000);
|
|
84
|
+
}
|
|
85
|
+
noteToFrequency(note) {
|
|
86
|
+
const match = note.match(/^([A-G])(#|b)?(-?\d+)$/);
|
|
87
|
+
if (!match) {
|
|
88
|
+
throw new Error(`Invalid note: ${note}`);
|
|
89
|
+
}
|
|
90
|
+
const [, name, accidental, octave] = match;
|
|
91
|
+
const semitones = {
|
|
92
|
+
C: 0,
|
|
93
|
+
D: 2,
|
|
94
|
+
E: 4,
|
|
95
|
+
F: 5,
|
|
96
|
+
G: 7,
|
|
97
|
+
A: 9,
|
|
98
|
+
B: 11,
|
|
99
|
+
};
|
|
100
|
+
const offset = accidental === "#" ? 1 : accidental === "b" ? -1 : 0;
|
|
101
|
+
const midi = (Number(octave) + 1) * 12 + semitones[name] + offset;
|
|
102
|
+
return 440 * Math.pow(2, (midi - 69) / 12);
|
|
103
|
+
}
|
|
104
|
+
stop() {
|
|
105
|
+
if (!this.running)
|
|
106
|
+
return;
|
|
107
|
+
this.running = false;
|
|
108
|
+
if (this.intervalId) {
|
|
109
|
+
clearInterval(this.intervalId);
|
|
110
|
+
this.intervalId = null;
|
|
111
|
+
}
|
|
112
|
+
const context = this.context;
|
|
113
|
+
const gain = this.masterGain;
|
|
114
|
+
if (!context || !gain)
|
|
115
|
+
return;
|
|
116
|
+
// Smooth fade-out
|
|
117
|
+
const now = context.currentTime;
|
|
118
|
+
gain.gain.setValueAtTime(gain.gain.value, now);
|
|
119
|
+
gain.gain.linearRampToValueAtTime(0, now + 0.15);
|
|
120
|
+
setTimeout(() => {
|
|
121
|
+
void context.close();
|
|
122
|
+
}, 200);
|
|
123
|
+
this.context = null;
|
|
124
|
+
this.masterGain = null;
|
|
125
|
+
}
|
|
126
|
+
playPulse() {
|
|
127
|
+
if (!this.context || !this.masterGain)
|
|
128
|
+
return;
|
|
129
|
+
const bar = this.nextBarStart;
|
|
130
|
+
const beat = (n) => bar + beatTime(n, this.bpm);
|
|
131
|
+
const beatLength = beatTime(1, this.bpm);
|
|
132
|
+
// Notes on beats 1-3, silence on beat 4.
|
|
133
|
+
if (this.pulseCount % 4 === 0 || this.pulseCount % 4 === 2) {
|
|
134
|
+
this.playNote("C5", beat(0), beatLength);
|
|
135
|
+
this.playNote("E5", beat(0.5), beatLength);
|
|
136
|
+
this.playNote("G4", beat(1), beatLength);
|
|
137
|
+
}
|
|
138
|
+
else if (this.pulseCount % 4 === 3) {
|
|
139
|
+
this.playNote("E5", beat(0), beatLength);
|
|
140
|
+
this.playNote("G5", beat(0.5), beatLength);
|
|
141
|
+
this.playNote("C5", beat(1), beatLength);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
this.playNote("E5", beat(0), beatLength);
|
|
145
|
+
this.playNote("G4", beat(0.5), beatLength);
|
|
146
|
+
this.playNote("C5", beat(1), beatLength);
|
|
147
|
+
}
|
|
148
|
+
this.pulseCount++;
|
|
149
|
+
this.nextBarStart = beat(this.beatsPerBar);
|
|
150
|
+
}
|
|
151
|
+
playNote(note, startTime, duration) {
|
|
152
|
+
if (!this.context || !this.masterGain)
|
|
153
|
+
return;
|
|
154
|
+
const oscillator = this.context.createOscillator();
|
|
155
|
+
const envelope = this.context.createGain();
|
|
156
|
+
oscillator.type = "sine";
|
|
157
|
+
oscillator.frequency.value = this.noteToFrequency(note);
|
|
158
|
+
envelope.gain.setValueAtTime(0, startTime);
|
|
159
|
+
envelope.gain.linearRampToValueAtTime(0.7, startTime + 0.05);
|
|
160
|
+
envelope.gain.exponentialRampToValueAtTime(0.001, startTime + duration);
|
|
161
|
+
oscillator.connect(envelope);
|
|
162
|
+
envelope.connect(this.masterGain);
|
|
163
|
+
oscillator.start(startTime);
|
|
164
|
+
oscillator.stop(startTime + duration + 0.01);
|
|
165
|
+
oscillator.onended = () => {
|
|
166
|
+
oscillator.disconnect();
|
|
167
|
+
envelope.disconnect();
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
exports.JingleTone = JingleTone;
|
|
172
|
+
//# sourceMappingURL=tones.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tones.js","sourceRoot":"","sources":["../../../src/webrtc/tones.ts"],"names":[],"mappings":";;;AAAA,+DAA+D;AAC/D,SAAS,QAAQ,CAAC,IAAY,EAAE,GAAW;IACzC,OAAO,IAAI,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC;AAC3B,CAAC;AAOD,wDAAwD;AACxD,MAAa,YAAY;IACf,OAAO,GAAwB,IAAI,CAAC;IACpC,UAAU,GAA0C,IAAI,CAAC;IAEhD,MAAM,GAAG,IAAI,CAAC;IACd,SAAS,GAAG,CAAC,CAAC;IACd,aAAa,GAAG,CAAC,CAAC;IACnC,wEAAwE;IACvD,UAAU,GAAG,GAAG,CAAC;IAElC,KAAK;QACH,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QAEzB,MAAM,OAAO,GAAG,IAAI,YAAY,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAElC,KAAK,MAAM,SAAS,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;YACnC,MAAM,UAAU,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;YAC9C,UAAU,CAAC,SAAS,CAAC,KAAK,GAAG,SAAS,CAAC;YACvC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACzB,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;QAED,IAAI,QAAQ,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC;QACrD,MAAM,IAAI,GAAG,GAAG,EAAE;YAChB,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YAChD,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;YACvD,QAAQ,IAAI,IAAI,CAAC,aAAa,CAAC;QACjC,CAAC,CAAC;QACF,IAAI,EAAE,CAAC;QACP,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;QAC/D,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAE1B,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC/B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;QACD,6CAA6C;QAC7C,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;CACF;AA/CD,oCA+CC;AAED,MAAa,UAAU;IACb,OAAO,GAAwB,IAAI,CAAC;IACpC,UAAU,GAAoB,IAAI,CAAC;IACnC,UAAU,GAA0C,IAAI,CAAC;IACzD,OAAO,GAAG,KAAK,CAAC;IAChB,UAAU,GAAG,CAAC,CAAC;IACvB,gDAAgD;IACxC,YAAY,GAAG,CAAC,CAAC;IAEzB,gBAAgB;IACC,MAAM,GAAG,IAAI,CAAC;IACd,GAAG,GAAG,GAAG,CAAC;IACV,WAAW,GAAG,CAAC,CAAC;IACjC,wEAAwE;IACvD,UAAU,GAAG,GAAG,CAAC;IAElC,KAAK;QACH,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QAEzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,OAAO,GAAG,IAAI,YAAY,EAAE,CAAC;QAClC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QAC5C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;QACzC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAClD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC;QAE/D,6DAA6D;QAC7D,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,SAAS,EAAE,CAAC;QAEjB,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QACvD,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE;YACjC,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC,CAAC;IACvB,CAAC;IAEO,eAAe,CAAC,IAAY;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAEnD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;QAC3C,CAAC;QAED,MAAM,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC;QAE3C,MAAM,SAAS,GAA2B;YACxC,CAAC,EAAE,CAAC;YACJ,CAAC,EAAE,CAAC;YACJ,CAAC,EAAE,CAAC;YACJ,CAAC,EAAE,CAAC;YACJ,CAAC,EAAE,CAAC;YACJ,CAAC,EAAE,CAAC;YACJ,CAAC,EAAE,EAAE;SACN,CAAC;QAEF,MAAM,MAAM,GAAG,UAAU,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACpE,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC;QAElE,OAAO,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,IAAI;QACF,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAE1B,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QAErB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC/B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC;QAE7B,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI;YAAE,OAAO;QAE9B,kBAAkB;QAClB,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC;QAChC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC/C,IAAI,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,CAAC;QAEjD,UAAU,CAAC,GAAG,EAAE;YACd,KAAK,OAAO,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC,EAAE,GAAG,CAAC,CAAC;QAER,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACzB,CAAC;IAEO,SAAS;QACf,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO;QAE9C,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC;QAC9B,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,GAAG,GAAG,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QACxD,MAAM,UAAU,GAAG,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QAEzC,yCAAyC;QACzC,IAAI,IAAI,CAAC,UAAU,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,UAAU,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3D,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;YACzC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC,CAAC;YAC3C,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QAC3C,CAAC;aAAM,IAAI,IAAI,CAAC,UAAU,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;YACzC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC,CAAC;YAC3C,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QAC3C,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;YACzC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC,CAAC;YAC3C,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QAC3C,CAAC;QACD,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC7C,CAAC;IAEO,QAAQ,CAAC,IAAY,EAAE,SAAiB,EAAE,QAAgB;QAChE,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO;QAE9C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC;QACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QAE3C,UAAU,CAAC,IAAI,GAAG,MAAM,CAAC;QACzB,UAAU,CAAC,SAAS,CAAC,KAAK,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAExD,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;QAC3C,QAAQ,CAAC,IAAI,CAAC,uBAAuB,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC,CAAC;QAC7D,QAAQ,CAAC,IAAI,CAAC,4BAA4B,CAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,CAAC,CAAC;QAExE,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC7B,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAElC,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAC5B,UAAU,CAAC,IAAI,CAAC,SAAS,GAAG,QAAQ,GAAG,IAAI,CAAC,CAAC;QAE7C,UAAU,CAAC,OAAO,GAAG,GAAG,EAAE;YACxB,UAAU,CAAC,UAAU,EAAE,CAAC;YACxB,QAAQ,CAAC,UAAU,EAAE,CAAC;QACxB,CAAC,CAAC;IACJ,CAAC;CACF;AA3ID,gCA2IC"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@guava-ai/guava-sdk",
|
|
3
3
|
"description": "The official TypeScript SDK for building Guava voice agents.",
|
|
4
4
|
"homepage": "https://docs.goguava.ai/typescript-sdk/",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.38.0",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"main": "dist/src/index.js",
|
|
8
8
|
"types": "dist/src/index.d.ts",
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
"types": "./dist/src/helpers/openai.d.ts",
|
|
20
20
|
"default": "./dist/src/helpers/openai.js"
|
|
21
21
|
},
|
|
22
|
+
"./webrtc": {
|
|
23
|
+
"types": "./dist/src/webrtc/index.d.ts",
|
|
24
|
+
"default": "./dist/src/webrtc/index.js"
|
|
25
|
+
},
|
|
22
26
|
"./example-data": {
|
|
23
27
|
"types": "./dist/src/example-data.d.ts",
|
|
24
28
|
"default": "./dist/src/example-data.js"
|
|
@@ -37,7 +41,9 @@
|
|
|
37
41
|
"@types/jest": "^30.0.0",
|
|
38
42
|
"@types/node": "^25.0.7",
|
|
39
43
|
"@types/ws": "^8.18.1",
|
|
44
|
+
"esbuild": "^0.28.2",
|
|
40
45
|
"jest": "^30.4.2",
|
|
46
|
+
"jest-environment-jsdom": "^30.5.2",
|
|
41
47
|
"openai": "^6.16.0",
|
|
42
48
|
"ts-jest": "^29.4.11",
|
|
43
49
|
"tsx": "^4.21.0",
|
|
@@ -70,7 +76,10 @@
|
|
|
70
76
|
"test:e2e": "jest --config jest.e2e.config.cjs",
|
|
71
77
|
"check": "npm run typecheck && npm audit --omit=dev && npm run lint && npm run check-format",
|
|
72
78
|
"clean": "rm -rf ./dist",
|
|
73
|
-
"build": "tsx scripts/update-version.ts && tsc && chmod +x ./dist/bin/example-runner.js",
|
|
79
|
+
"build": "tsx scripts/update-version.ts && tsc && chmod +x ./dist/bin/example-runner.js && npm run build-webrtc && tsx scripts/update-readme.ts",
|
|
80
|
+
"build-webrtc": "npm run build-webrtc:esm && npm run build-webrtc:iife",
|
|
81
|
+
"build-webrtc:esm": "esbuild src/webrtc/index.ts --bundle --minify --sourcemap --target=es2020 --format=esm --outfile=dist/guava-webrtc.esm.min.js",
|
|
82
|
+
"build-webrtc:iife": "esbuild src/webrtc/index.ts --bundle --minify --sourcemap --target=es2020 --format=iife --global-name=Guava --outfile=dist/guava-webrtc.min.js",
|
|
74
83
|
"prepare": "npm run clean && npm run build",
|
|
75
84
|
"release": "npm publish --ignore-scripts=false"
|
|
76
85
|
},
|
|
@@ -85,5 +94,9 @@
|
|
|
85
94
|
"/dist",
|
|
86
95
|
"/examples",
|
|
87
96
|
"/bin"
|
|
88
|
-
]
|
|
97
|
+
],
|
|
98
|
+
"repository": {
|
|
99
|
+
"type": "git",
|
|
100
|
+
"url": "git+https://github.com/goguava-ai/typescript-sdk.git"
|
|
101
|
+
}
|
|
89
102
|
}
|
package/src/call.ts
CHANGED
|
@@ -25,6 +25,7 @@ import type {
|
|
|
25
25
|
import { Say } from "./action-item.ts";
|
|
26
26
|
import { telemetryClient } from "./telemetry.ts";
|
|
27
27
|
import type { CallInfo } from "./socket/call-info.ts";
|
|
28
|
+
import type { SipUri } from "./sip.ts";
|
|
28
29
|
|
|
29
30
|
export type TaskObjective =
|
|
30
31
|
| { objective: string }
|
|
@@ -248,13 +249,13 @@ export class Call {
|
|
|
248
249
|
* This method does not interfere with turn-taking — use this when
|
|
249
250
|
* you want the agent to naturally transfer the call.
|
|
250
251
|
*
|
|
251
|
-
* @param destination - The phone number
|
|
252
|
+
* @param destination - The phone number, SIP address, or SipUri to transfer the call to.
|
|
252
253
|
* @param instructions - Optional guidance for how the agent should handle the transfer.
|
|
253
254
|
*/
|
|
254
|
-
async transfer(destination: string, instructions?: string) {
|
|
255
|
+
async transfer(destination: string | SipUri, instructions?: string) {
|
|
255
256
|
await this.sendCommand(TransferCommand, {
|
|
256
257
|
command_type: "transfer-call",
|
|
257
|
-
to_number: destination,
|
|
258
|
+
to_number: destination.toString(),
|
|
258
259
|
transfer_message:
|
|
259
260
|
instructions ?? "Notify the caller that you will be transferring them, and then transfer.",
|
|
260
261
|
soft_transfer: true,
|
|
@@ -267,14 +268,14 @@ export class Call {
|
|
|
267
268
|
* Unlike transfer(), this does not respect turn-taking. The agent immediately
|
|
268
269
|
* initiates the transfer, saying the script (if provided).
|
|
269
270
|
*
|
|
270
|
-
* @param destination - The phone number
|
|
271
|
+
* @param destination - The phone number, SIP address, or SipUri to transfer the call to.
|
|
271
272
|
* @param finalScript - Optional message for the agent to deliver immediately
|
|
272
273
|
* before transferring. Defaults to no message.
|
|
273
274
|
*/
|
|
274
|
-
async immediateTransfer(destination: string, finalScript?: string) {
|
|
275
|
+
async immediateTransfer(destination: string | SipUri, finalScript?: string) {
|
|
275
276
|
await this.sendCommand(TransferCommand, {
|
|
276
277
|
command_type: "transfer-call",
|
|
277
|
-
to_number: destination,
|
|
278
|
+
to_number: destination.toString(),
|
|
278
279
|
transfer_message: finalScript ?? "",
|
|
279
280
|
soft_transfer: false,
|
|
280
281
|
});
|
package/src/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ export { Say, Field } from "./action-item.ts";
|
|
|
13
13
|
export { Logger, getConsoleLogger, getDefaultLogger } from "./logging.ts";
|
|
14
14
|
export { Agent, CallInfo } from "./agent.ts";
|
|
15
15
|
export { SIPCallInfo } from "./socket/call-info.ts";
|
|
16
|
+
export { SipUri } from "./sip.ts";
|
|
16
17
|
export { Call, DEFAULT_REACH_PERSON_OUTCOMES } from "./call.ts";
|
|
17
18
|
export type { ReachPersonOutcome } from "./call.ts";
|
|
18
19
|
export type { SpeedPreset, Language } from "./commands.ts";
|