@framers/agentos 0.1.74 → 0.1.76
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 +139 -34
- package/dist/core/agency/AgentCommunicationBus.d.ts +1 -0
- package/dist/core/agency/AgentCommunicationBus.d.ts.map +1 -1
- package/dist/core/agency/AgentCommunicationBus.js +62 -8
- package/dist/core/agency/AgentCommunicationBus.js.map +1 -1
- package/dist/core/agency/IAgentCommunicationBus.d.ts +1 -1
- package/dist/core/agency/IAgentCommunicationBus.d.ts.map +1 -1
- package/dist/orchestration/builders/index.d.ts +1 -1
- package/dist/orchestration/builders/index.d.ts.map +1 -1
- package/dist/orchestration/builders/index.js +1 -1
- package/dist/orchestration/builders/index.js.map +1 -1
- package/dist/orchestration/builders/nodes.d.ts +15 -0
- package/dist/orchestration/builders/nodes.d.ts.map +1 -1
- package/dist/orchestration/builders/nodes.js +33 -0
- package/dist/orchestration/builders/nodes.js.map +1 -1
- package/dist/orchestration/runtime/LoopController.d.ts +10 -10
- package/dist/orchestration/runtime/LoopController.d.ts.map +1 -1
- package/dist/orchestration/runtime/LoopController.js +1 -1
- package/dist/orchestration/runtime/LoopController.js.map +1 -1
- package/dist/orchestration/runtime/index.d.ts +1 -1
- package/dist/orchestration/runtime/index.d.ts.map +1 -1
- package/dist/orchestration/runtime/index.js.map +1 -1
- package/dist/speech/FallbackProxy.d.ts +104 -0
- package/dist/speech/FallbackProxy.d.ts.map +1 -0
- package/dist/speech/FallbackProxy.js +151 -0
- package/dist/speech/FallbackProxy.js.map +1 -0
- package/dist/speech/SpeechProviderResolver.d.ts +103 -0
- package/dist/speech/SpeechProviderResolver.d.ts.map +1 -0
- package/dist/speech/SpeechProviderResolver.js +256 -0
- package/dist/speech/SpeechProviderResolver.js.map +1 -0
- package/dist/speech/SpeechRuntime.d.ts +23 -1
- package/dist/speech/SpeechRuntime.d.ts.map +1 -1
- package/dist/speech/SpeechRuntime.js +82 -8
- package/dist/speech/SpeechRuntime.js.map +1 -1
- package/dist/speech/index.d.ts +6 -0
- package/dist/speech/index.d.ts.map +1 -1
- package/dist/speech/index.js +6 -0
- package/dist/speech/index.js.map +1 -1
- package/dist/speech/providerCatalog.d.ts.map +1 -1
- package/dist/speech/providerCatalog.js +15 -1
- package/dist/speech/providerCatalog.js.map +1 -1
- package/dist/speech/providers/AssemblyAISTTProvider.d.ts +49 -0
- package/dist/speech/providers/AssemblyAISTTProvider.d.ts.map +1 -0
- package/dist/speech/providers/AssemblyAISTTProvider.js +151 -0
- package/dist/speech/providers/AssemblyAISTTProvider.js.map +1 -0
- package/dist/speech/providers/AzureSpeechSTTProvider.d.ts +48 -0
- package/dist/speech/providers/AzureSpeechSTTProvider.d.ts.map +1 -0
- package/dist/speech/providers/AzureSpeechSTTProvider.js +90 -0
- package/dist/speech/providers/AzureSpeechSTTProvider.js.map +1 -0
- package/dist/speech/providers/AzureSpeechTTSProvider.d.ts +60 -0
- package/dist/speech/providers/AzureSpeechTTSProvider.d.ts.map +1 -0
- package/dist/speech/providers/AzureSpeechTTSProvider.js +127 -0
- package/dist/speech/providers/AzureSpeechTTSProvider.js.map +1 -0
- package/dist/speech/providers/DeepgramBatchSTTProvider.d.ts +55 -0
- package/dist/speech/providers/DeepgramBatchSTTProvider.d.ts.map +1 -0
- package/dist/speech/providers/DeepgramBatchSTTProvider.js +102 -0
- package/dist/speech/providers/DeepgramBatchSTTProvider.js.map +1 -0
- package/dist/speech/types.d.ts +35 -0
- package/dist/speech/types.d.ts.map +1 -1
- package/dist/voice/CallManager.d.ts +1 -1
- package/dist/voice/CallManager.d.ts.map +1 -1
- package/dist/voice/CallManager.js +9 -0
- package/dist/voice/CallManager.js.map +1 -1
- package/dist/voice/MediaStreamParser.d.ts +83 -0
- package/dist/voice/MediaStreamParser.d.ts.map +1 -0
- package/dist/voice/MediaStreamParser.js +2 -0
- package/dist/voice/MediaStreamParser.js.map +1 -0
- package/dist/voice/TelephonyStreamTransport.d.ts +112 -0
- package/dist/voice/TelephonyStreamTransport.d.ts.map +1 -0
- package/dist/voice/TelephonyStreamTransport.js +208 -0
- package/dist/voice/TelephonyStreamTransport.js.map +1 -0
- package/dist/voice/index.d.ts +10 -0
- package/dist/voice/index.d.ts.map +1 -1
- package/dist/voice/index.js +11 -0
- package/dist/voice/index.js.map +1 -1
- package/dist/voice/parsers/PlivoMediaStreamParser.d.ts +43 -0
- package/dist/voice/parsers/PlivoMediaStreamParser.d.ts.map +1 -0
- package/dist/voice/parsers/PlivoMediaStreamParser.js +92 -0
- package/dist/voice/parsers/PlivoMediaStreamParser.js.map +1 -0
- package/dist/voice/parsers/TelnyxMediaStreamParser.d.ts +51 -0
- package/dist/voice/parsers/TelnyxMediaStreamParser.d.ts.map +1 -0
- package/dist/voice/parsers/TelnyxMediaStreamParser.js +103 -0
- package/dist/voice/parsers/TelnyxMediaStreamParser.js.map +1 -0
- package/dist/voice/parsers/TwilioMediaStreamParser.d.ts +50 -0
- package/dist/voice/parsers/TwilioMediaStreamParser.d.ts.map +1 -0
- package/dist/voice/parsers/TwilioMediaStreamParser.js +144 -0
- package/dist/voice/parsers/TwilioMediaStreamParser.js.map +1 -0
- package/dist/voice/providers/plivo.d.ts +77 -0
- package/dist/voice/providers/plivo.d.ts.map +1 -0
- package/dist/voice/providers/plivo.js +180 -0
- package/dist/voice/providers/plivo.js.map +1 -0
- package/dist/voice/providers/telnyx.d.ts +93 -0
- package/dist/voice/providers/telnyx.d.ts.map +1 -0
- package/dist/voice/providers/telnyx.js +193 -0
- package/dist/voice/providers/telnyx.js.map +1 -0
- package/dist/voice/providers/twilio.d.ts +79 -0
- package/dist/voice/providers/twilio.d.ts.map +1 -0
- package/dist/voice/providers/twilio.js +191 -0
- package/dist/voice/providers/twilio.js.map +1 -0
- package/dist/voice/twiml.d.ts +69 -0
- package/dist/voice/twiml.d.ts.map +1 -0
- package/dist/voice/twiml.js +92 -0
- package/dist/voice/twiml.js.map +1 -0
- package/dist/voice/types.d.ts +9 -1
- package/dist/voice/types.d.ts.map +1 -1
- package/dist/voice-pipeline/AcousticEndpointDetector.d.ts +90 -0
- package/dist/voice-pipeline/AcousticEndpointDetector.d.ts.map +1 -0
- package/dist/voice-pipeline/AcousticEndpointDetector.js +123 -0
- package/dist/voice-pipeline/AcousticEndpointDetector.js.map +1 -0
- package/dist/voice-pipeline/HardCutBargeinHandler.d.ts +67 -0
- package/dist/voice-pipeline/HardCutBargeinHandler.d.ts.map +1 -0
- package/dist/voice-pipeline/HardCutBargeinHandler.js +55 -0
- package/dist/voice-pipeline/HardCutBargeinHandler.js.map +1 -0
- package/dist/voice-pipeline/HeuristicEndpointDetector.d.ts +128 -0
- package/dist/voice-pipeline/HeuristicEndpointDetector.d.ts.map +1 -0
- package/dist/voice-pipeline/HeuristicEndpointDetector.js +240 -0
- package/dist/voice-pipeline/HeuristicEndpointDetector.js.map +1 -0
- package/dist/voice-pipeline/SoftFadeBargeinHandler.d.ts +96 -0
- package/dist/voice-pipeline/SoftFadeBargeinHandler.d.ts.map +1 -0
- package/dist/voice-pipeline/SoftFadeBargeinHandler.js +69 -0
- package/dist/voice-pipeline/SoftFadeBargeinHandler.js.map +1 -0
- package/dist/voice-pipeline/VoicePipelineOrchestrator.d.ts +122 -0
- package/dist/voice-pipeline/VoicePipelineOrchestrator.d.ts.map +1 -0
- package/dist/voice-pipeline/VoicePipelineOrchestrator.js +317 -0
- package/dist/voice-pipeline/VoicePipelineOrchestrator.js.map +1 -0
- package/dist/voice-pipeline/WebSocketStreamTransport.d.ts +148 -0
- package/dist/voice-pipeline/WebSocketStreamTransport.d.ts.map +1 -0
- package/dist/voice-pipeline/WebSocketStreamTransport.js +207 -0
- package/dist/voice-pipeline/WebSocketStreamTransport.js.map +1 -0
- package/dist/voice-pipeline/index.d.ts +13 -0
- package/dist/voice-pipeline/index.d.ts.map +1 -0
- package/dist/voice-pipeline/index.js +13 -0
- package/dist/voice-pipeline/index.js.map +1 -0
- package/dist/voice-pipeline/types.d.ts +905 -0
- package/dist/voice-pipeline/types.d.ts.map +1 -0
- package/dist/voice-pipeline/types.js +23 -0
- package/dist/voice-pipeline/types.js.map +1 -0
- package/package.json +6 -1
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview TelephonyStreamTransport — bridges a telephony WebSocket media
|
|
3
|
+
* stream to the AgentOS streaming voice pipeline.
|
|
4
|
+
*
|
|
5
|
+
* Incoming mu-law 8 kHz audio from the phone network is decoded to Int16 PCM,
|
|
6
|
+
* resampled to the pipeline's preferred sample rate, normalised to Float32, and
|
|
7
|
+
* emitted as {@link AudioFrame} events. Outbound {@link EncodedAudioChunk}
|
|
8
|
+
* payloads are resampled from the pipeline's sample rate back to 8 kHz, encoded
|
|
9
|
+
* to mu-law, and forwarded through the provider-specific {@link MediaStreamParser}.
|
|
10
|
+
*
|
|
11
|
+
* @module @framers/agentos/voice/TelephonyStreamTransport
|
|
12
|
+
*/
|
|
13
|
+
import { EventEmitter } from 'node:events';
|
|
14
|
+
import { randomUUID } from 'node:crypto';
|
|
15
|
+
import { convertPcmToMulaw8k, convertMulawToPcm16 } from './telephony-audio.js';
|
|
16
|
+
/**
|
|
17
|
+
* Adapts a telephony provider WebSocket media stream to the
|
|
18
|
+
* {@link IStreamTransport} interface consumed by the AgentOS voice pipeline.
|
|
19
|
+
*
|
|
20
|
+
* ## Inbound path (phone → pipeline)
|
|
21
|
+
* 1. Provider WebSocket frames arrive as raw `Buffer` or JSON `string`.
|
|
22
|
+
* 2. {@link MediaStreamParser.parseIncoming} normalises them to
|
|
23
|
+
* {@link MediaStreamIncoming} events.
|
|
24
|
+
* 3. `'audio'` events: mu-law 8 kHz → Int16 PCM → resample → Float32 → `'audio'` emit.
|
|
25
|
+
* 4. `'dtmf'` / `'mark'` events are re-emitted as-is for higher-layer handling.
|
|
26
|
+
* 5. `'start'` transitions the transport to `'open'` and sends the optional
|
|
27
|
+
* connection acknowledgment from the parser.
|
|
28
|
+
* 6. `'stop'` or WebSocket close transitions to `'closed'` and emits `'close'`.
|
|
29
|
+
*
|
|
30
|
+
* ## Outbound path (pipeline → phone)
|
|
31
|
+
* 1. {@link sendAudio} receives an {@link EncodedAudioChunk} (PCM Int16 format assumed).
|
|
32
|
+
* 2. Chunk is resampled from `chunk.sampleRate` → 8 kHz via linear interpolation.
|
|
33
|
+
* 3. Resampled PCM is mu-law encoded via {@link convertPcmToMulaw8k}.
|
|
34
|
+
* 4. {@link MediaStreamParser.formatOutgoing} wraps the bytes for the provider.
|
|
35
|
+
* 5. The formatted payload is sent over the WebSocket.
|
|
36
|
+
*
|
|
37
|
+
* Emits:
|
|
38
|
+
* - `'audio'` ({@link AudioFrame}) — inbound decoded audio for STT / VAD.
|
|
39
|
+
* - `'dtmf'` (`{ digit: string; durationMs?: number }`) — caller key-press.
|
|
40
|
+
* - `'mark'` (`{ name: string }`) — named stream marker.
|
|
41
|
+
* - `'close'` () — transport has been fully closed.
|
|
42
|
+
* - `'error'` (Error) — unrecoverable WebSocket or parsing error.
|
|
43
|
+
*/
|
|
44
|
+
export class TelephonyStreamTransport extends EventEmitter {
|
|
45
|
+
/** Current connection lifecycle state. */
|
|
46
|
+
get state() {
|
|
47
|
+
return this._state;
|
|
48
|
+
}
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Constructor
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
/**
|
|
53
|
+
* @param ws - WebSocket-like object (must emit `'message'`, `'close'`, `'error'`
|
|
54
|
+
* and expose `send(data)` and `close(code?, reason?)` methods).
|
|
55
|
+
* @param parser - Provider-specific message parser/formatter.
|
|
56
|
+
* @param config - Optional configuration overrides.
|
|
57
|
+
*/
|
|
58
|
+
constructor(ws, // WebSocket-like; typed `any` to avoid hard dep on ws package
|
|
59
|
+
parser, config) {
|
|
60
|
+
super();
|
|
61
|
+
this.ws = ws;
|
|
62
|
+
this.parser = parser;
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// IStreamTransport — identity & state
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
/** Stable UUID for this transport connection. */
|
|
67
|
+
this.id = randomUUID();
|
|
68
|
+
this._state = 'connecting';
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Private fields
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
/** Provider-assigned stream identifier; populated on the 'start' event. */
|
|
73
|
+
this.streamSid = null;
|
|
74
|
+
this.outputSampleRate = config?.outputSampleRate ?? 16000;
|
|
75
|
+
this.ws.on('message', (data) => {
|
|
76
|
+
const parsed = this.parser.parseIncoming(data);
|
|
77
|
+
if (!parsed)
|
|
78
|
+
return;
|
|
79
|
+
switch (parsed.type) {
|
|
80
|
+
case 'start': {
|
|
81
|
+
this.streamSid = parsed.streamSid;
|
|
82
|
+
this._state = 'open';
|
|
83
|
+
// Send connection acknowledgment if the provider requires one.
|
|
84
|
+
const connMsg = this.parser.formatConnected?.(parsed.streamSid);
|
|
85
|
+
if (connMsg)
|
|
86
|
+
this.ws.send(connMsg);
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
case 'audio': {
|
|
90
|
+
// mu-law 8 kHz → Int16 PCM buffer (2 bytes per sample).
|
|
91
|
+
const pcm16Buf = convertMulawToPcm16(parsed.payload);
|
|
92
|
+
// Convert Buffer to Int16Array (shared memory, no copy).
|
|
93
|
+
const int16 = new Int16Array(pcm16Buf.buffer, pcm16Buf.byteOffset, pcm16Buf.byteLength / 2);
|
|
94
|
+
// Resample 8 kHz → outputSampleRate.
|
|
95
|
+
const resampled = this.resample(int16, 8000, this.outputSampleRate);
|
|
96
|
+
// Normalise Int16 → Float32 [-1, 1].
|
|
97
|
+
const float32 = new Float32Array(resampled.length);
|
|
98
|
+
for (let i = 0; i < resampled.length; i++) {
|
|
99
|
+
float32[i] = resampled[i] / 32768;
|
|
100
|
+
}
|
|
101
|
+
const frame = {
|
|
102
|
+
samples: float32,
|
|
103
|
+
sampleRate: this.outputSampleRate,
|
|
104
|
+
timestamp: Date.now(),
|
|
105
|
+
};
|
|
106
|
+
this.emit('audio', frame);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
case 'dtmf':
|
|
110
|
+
this.emit('dtmf', { digit: parsed.digit, durationMs: parsed.durationMs });
|
|
111
|
+
break;
|
|
112
|
+
case 'stop':
|
|
113
|
+
this._state = 'closed';
|
|
114
|
+
this.emit('close');
|
|
115
|
+
break;
|
|
116
|
+
case 'mark':
|
|
117
|
+
this.emit('mark', { name: parsed.name });
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
this.ws.on('close', () => {
|
|
122
|
+
if (this._state !== 'closed') {
|
|
123
|
+
this._state = 'closed';
|
|
124
|
+
this.emit('close');
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
this.ws.on('error', (err) => this.emit('error', err));
|
|
128
|
+
}
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
// IStreamTransport — outbound methods
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
/**
|
|
133
|
+
* Send synthesised audio to the caller.
|
|
134
|
+
*
|
|
135
|
+
* Assumes `chunk.format === 'pcm'` and that `chunk.audio` contains raw
|
|
136
|
+
* signed 16-bit little-endian PCM samples at `chunk.sampleRate`. The audio
|
|
137
|
+
* is resampled to 8 kHz, mu-law encoded, and forwarded via the parser.
|
|
138
|
+
*
|
|
139
|
+
* @param chunk - Encoded audio chunk from the TTS pipeline.
|
|
140
|
+
*/
|
|
141
|
+
async sendAudio(chunk) {
|
|
142
|
+
if (!this.streamSid || this._state !== 'open')
|
|
143
|
+
return;
|
|
144
|
+
// Interpret raw bytes as Int16 samples.
|
|
145
|
+
const int16 = new Int16Array(chunk.audio.buffer, chunk.audio.byteOffset, chunk.audio.byteLength / 2);
|
|
146
|
+
// Resample to 8 kHz, write into a Buffer for convertPcmToMulaw8k.
|
|
147
|
+
const resampled8k = this.resample(int16, chunk.sampleRate, 8000);
|
|
148
|
+
const pcm8kBuf = Buffer.from(resampled8k.buffer, resampled8k.byteOffset, resampled8k.byteLength);
|
|
149
|
+
// Encode to mu-law. convertPcmToMulaw8k already handles resampling, but
|
|
150
|
+
// we pre-resample here to avoid a second pass; pass sampleRate=8000 so
|
|
151
|
+
// the function's internal resampler is a no-op.
|
|
152
|
+
const mulaw = convertPcmToMulaw8k(pcm8kBuf, 8000);
|
|
153
|
+
const formatted = this.parser.formatOutgoing(mulaw, this.streamSid);
|
|
154
|
+
this.ws.send(formatted);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Send a JSON control message over the WebSocket.
|
|
158
|
+
*
|
|
159
|
+
* @param message - Server-to-client pipeline protocol message.
|
|
160
|
+
*/
|
|
161
|
+
async sendControl(message) {
|
|
162
|
+
if (this._state !== 'open')
|
|
163
|
+
return;
|
|
164
|
+
this.ws.send(JSON.stringify(message));
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Initiate graceful closure of the transport.
|
|
168
|
+
*
|
|
169
|
+
* @param code - Optional WebSocket close code (default 1000).
|
|
170
|
+
* @param reason - Optional human-readable close reason.
|
|
171
|
+
*/
|
|
172
|
+
close(code, reason) {
|
|
173
|
+
this._state = 'closing';
|
|
174
|
+
this.ws.close(code, reason);
|
|
175
|
+
}
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
// Private helpers
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
/**
|
|
180
|
+
* Linear interpolation resampler for 16-bit signed PCM.
|
|
181
|
+
*
|
|
182
|
+
* Not studio-quality but sufficient for narrow-band voice telephony. The
|
|
183
|
+
* output length is computed as `round(input.length * toRate / fromRate)` to
|
|
184
|
+
* avoid cumulative rounding drift across many small frames.
|
|
185
|
+
*
|
|
186
|
+
* @param input - Source samples as a signed 16-bit integer array.
|
|
187
|
+
* @param fromRate - Sample rate of the input, in Hz.
|
|
188
|
+
* @param toRate - Desired sample rate of the output, in Hz.
|
|
189
|
+
* @returns A new Int16Array at `toRate`.
|
|
190
|
+
*/
|
|
191
|
+
resample(input, fromRate, toRate) {
|
|
192
|
+
if (fromRate === toRate)
|
|
193
|
+
return input;
|
|
194
|
+
const ratio = fromRate / toRate;
|
|
195
|
+
const outputLen = Math.round(input.length / ratio);
|
|
196
|
+
const output = new Int16Array(outputLen);
|
|
197
|
+
for (let i = 0; i < outputLen; i++) {
|
|
198
|
+
const srcIdx = i * ratio;
|
|
199
|
+
const idx = Math.floor(srcIdx);
|
|
200
|
+
const frac = srcIdx - idx;
|
|
201
|
+
const a = input[idx] ?? 0;
|
|
202
|
+
const b = input[Math.min(idx + 1, input.length - 1)] ?? 0;
|
|
203
|
+
output[i] = Math.round(a + frac * (b - a));
|
|
204
|
+
}
|
|
205
|
+
return output;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=TelephonyStreamTransport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TelephonyStreamTransport.js","sourceRoot":"","sources":["../../src/voice/TelephonyStreamTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAwBhF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,OAAO,wBAAyB,SAAQ,YAAY;IAUxD,0CAA0C;IAC1C,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAYD,8EAA8E;IAC9E,cAAc;IACd,8EAA8E;IAE9E;;;;;OAKG;IACH,YACmB,EAAO,EAAE,8DAA8D;IACvE,MAAyB,EAC1C,MAAuC;QAEvC,KAAK,EAAE,CAAC;QAJS,OAAE,GAAF,EAAE,CAAK;QACP,WAAM,GAAN,MAAM,CAAmB;QApC5C,8EAA8E;QAC9E,sCAAsC;QACtC,8EAA8E;QAE9E,iDAAiD;QACxC,OAAE,GAAW,UAAU,EAAE,CAAC;QAE3B,WAAM,GAAiD,YAAY,CAAC;QAO5E,8EAA8E;QAC9E,iBAAiB;QACjB,8EAA8E;QAE9E,2EAA2E;QACnE,cAAS,GAAkB,IAAI,CAAC;QAqBtC,IAAI,CAAC,gBAAgB,GAAG,MAAM,EAAE,gBAAgB,IAAI,KAAK,CAAC;QAE1D,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAqB,EAAE,EAAE;YAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YAC/C,IAAI,CAAC,MAAM;gBAAE,OAAO;YAEpB,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;gBACpB,KAAK,OAAO,CAAC,CAAC,CAAC;oBACb,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;oBAClC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;oBACrB,+DAA+D;oBAC/D,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;oBAChE,IAAI,OAAO;wBAAE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBACnC,MAAM;gBACR,CAAC;gBAED,KAAK,OAAO,CAAC,CAAC,CAAC;oBACb,wDAAwD;oBACxD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;oBAErD,yDAAyD;oBACzD,MAAM,KAAK,GAAG,IAAI,UAAU,CAC1B,QAAQ,CAAC,MAAM,EACf,QAAQ,CAAC,UAAU,EACnB,QAAQ,CAAC,UAAU,GAAG,CAAC,CACxB,CAAC;oBAEF,qCAAqC;oBACrC,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;oBAEpE,qCAAqC;oBACrC,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;oBACnD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;wBAC1C,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;oBACpC,CAAC;oBAED,MAAM,KAAK,GAAe;wBACxB,OAAO,EAAE,OAAO;wBAChB,UAAU,EAAE,IAAI,CAAC,gBAAgB;wBACjC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;qBACtB,CAAC;oBACF,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;oBAC1B,MAAM;gBACR,CAAC;gBAED,KAAK,MAAM;oBACT,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;oBAC1E,MAAM;gBAER,KAAK,MAAM;oBACT,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;oBACvB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBACnB,MAAM;gBAER,KAAK,MAAM;oBACT,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;oBACzC,MAAM;YACV,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACvB,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;gBAC7B,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;gBACvB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACrB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IAC/D,CAAC;IAED,8EAA8E;IAC9E,sCAAsC;IACtC,8EAA8E;IAE9E;;;;;;;;OAQG;IACH,KAAK,CAAC,SAAS,CAAC,KAAwB;QACtC,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM;YAAE,OAAO;QAEtD,wCAAwC;QACxC,MAAM,KAAK,GAAG,IAAI,UAAU,CAC1B,KAAK,CAAC,KAAK,CAAC,MAAM,EAClB,KAAK,CAAC,KAAK,CAAC,UAAU,EACtB,KAAK,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAC3B,CAAC;QAEF,kEAAkE;QAClE,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QACjE,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,UAAU,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;QAEjG,wEAAwE;QACxE,uEAAuE;QACvE,gDAAgD;QAChD,MAAM,KAAK,GAAG,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAElD,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACpE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,CAAC,OAA0B;QAC1C,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM;YAAE,OAAO;QACnC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACxC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,IAAa,EAAE,MAAe;QAClC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED,8EAA8E;IAC9E,kBAAkB;IAClB,8EAA8E;IAE9E;;;;;;;;;;;OAWG;IACK,QAAQ,CAAC,KAAiB,EAAE,QAAgB,EAAE,MAAc;QAClE,IAAI,QAAQ,KAAK,MAAM;YAAE,OAAO,KAAK,CAAC;QAEtC,MAAM,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC;QAEzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;YACnC,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC;YACzB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC/B,MAAM,IAAI,GAAG,MAAM,GAAG,GAAG,CAAC;YAC1B,MAAM,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC1B,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC1D,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC7C,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
|
package/dist/voice/index.d.ts
CHANGED
|
@@ -8,4 +8,14 @@ export type { IVoiceCallProvider, InitiateCallInput, InitiateCallResult, HangupC
|
|
|
8
8
|
export { CallManager } from './CallManager.js';
|
|
9
9
|
export type { CallManagerEventType, CallManagerEvent, CallManagerEventHandler, } from './CallManager.js';
|
|
10
10
|
export { convertPcmToMulaw8k, convertMulawToPcm16, escapeXml, validateE164, } from './telephony-audio.js';
|
|
11
|
+
export type { MediaStreamParser, MediaStreamIncoming } from './MediaStreamParser.js';
|
|
12
|
+
export { TwilioMediaStreamParser } from './parsers/TwilioMediaStreamParser.js';
|
|
13
|
+
export { TelnyxMediaStreamParser } from './parsers/TelnyxMediaStreamParser.js';
|
|
14
|
+
export { PlivoMediaStreamParser } from './parsers/PlivoMediaStreamParser.js';
|
|
15
|
+
export { TelephonyStreamTransport } from './TelephonyStreamTransport.js';
|
|
16
|
+
export type { TelephonyStreamTransportConfig } from './TelephonyStreamTransport.js';
|
|
17
|
+
export { TwilioVoiceProvider } from './providers/twilio.js';
|
|
18
|
+
export { TelnyxVoiceProvider } from './providers/telnyx.js';
|
|
19
|
+
export { PlivoVoiceProvider } from './providers/plivo.js';
|
|
20
|
+
export { twilioConversationTwiml, twilioNotifyTwiml, telnyxStreamXml, plivoStreamXml, plivoNotifyXml, } from './twiml.js';
|
|
11
21
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/voice/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAC;AAC3B,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EACV,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,SAAS,EACT,YAAY,GACb,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/voice/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAC;AAC3B,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EACV,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,SAAS,EACT,YAAY,GACb,MAAM,sBAAsB,CAAC;AAG9B,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAG7E,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,YAAY,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAGpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAG1D,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,cAAc,GACf,MAAM,YAAY,CAAC"}
|
package/dist/voice/index.js
CHANGED
|
@@ -6,4 +6,15 @@
|
|
|
6
6
|
export * from './types.js';
|
|
7
7
|
export { CallManager } from './CallManager.js';
|
|
8
8
|
export { convertPcmToMulaw8k, convertMulawToPcm16, escapeXml, validateE164, } from './telephony-audio.js';
|
|
9
|
+
export { TwilioMediaStreamParser } from './parsers/TwilioMediaStreamParser.js';
|
|
10
|
+
export { TelnyxMediaStreamParser } from './parsers/TelnyxMediaStreamParser.js';
|
|
11
|
+
export { PlivoMediaStreamParser } from './parsers/PlivoMediaStreamParser.js';
|
|
12
|
+
// ── Telephony stream transport ────────────────────────────────────────────────
|
|
13
|
+
export { TelephonyStreamTransport } from './TelephonyStreamTransport.js';
|
|
14
|
+
// ── Voice call providers ──────────────────────────────────────────────────────
|
|
15
|
+
export { TwilioVoiceProvider } from './providers/twilio.js';
|
|
16
|
+
export { TelnyxVoiceProvider } from './providers/telnyx.js';
|
|
17
|
+
export { PlivoVoiceProvider } from './providers/plivo.js';
|
|
18
|
+
// ── TwiML / XML helpers ───────────────────────────────────────────────────────
|
|
19
|
+
export { twilioConversationTwiml, twilioNotifyTwiml, telnyxStreamXml, plivoStreamXml, plivoNotifyXml, } from './twiml.js';
|
|
9
20
|
//# sourceMappingURL=index.js.map
|
package/dist/voice/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/voice/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAC;AAU3B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAM/C,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,SAAS,EACT,YAAY,GACb,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/voice/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAC;AAU3B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAM/C,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,SAAS,EACT,YAAY,GACb,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAE7E,iFAAiF;AACjF,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAGzE,iFAAiF;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,iFAAiF;AACjF,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,cAAc,GACf,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { MediaStreamParser, MediaStreamIncoming } from '../MediaStreamParser.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parses the Plivo Audio Stream WebSocket protocol.
|
|
4
|
+
*
|
|
5
|
+
* Plivo sends JSON-encoded messages for stream lifecycle events (`start`,
|
|
6
|
+
* `stop`) and audio chunks (`media`). The audio payload is base64-encoded
|
|
7
|
+
* mu-law PCM, delivered in a `payload` field inside the `media` object.
|
|
8
|
+
*
|
|
9
|
+
* Outgoing audio is wrapped in a `playAudio` JSON envelope, which is the
|
|
10
|
+
* format Plivo expects when the server streams audio back to the caller.
|
|
11
|
+
* No explicit connection acknowledgment is required after the handshake.
|
|
12
|
+
*
|
|
13
|
+
* @see {@link https://www.plivo.com/docs/voice/xml/stream}
|
|
14
|
+
*/
|
|
15
|
+
export declare class PlivoMediaStreamParser implements MediaStreamParser {
|
|
16
|
+
/**
|
|
17
|
+
* Parse a raw WebSocket frame from Plivo's audio stream.
|
|
18
|
+
*
|
|
19
|
+
* Supported Plivo event types:
|
|
20
|
+
* - `start` — stream established; `stream_id` maps to `streamSid`,
|
|
21
|
+
* `call_uuid` maps to `callSid`.
|
|
22
|
+
* - `media` — audio chunk; `media.payload` contains base64-encoded mu-law.
|
|
23
|
+
* - `stop` — stream ended.
|
|
24
|
+
*
|
|
25
|
+
* @param data - Raw WebSocket frame payload (JSON string or Buffer from Plivo).
|
|
26
|
+
* @returns Normalised {@link MediaStreamIncoming} event, or `null` for
|
|
27
|
+
* unknown event types or malformed messages.
|
|
28
|
+
*/
|
|
29
|
+
parseIncoming(data: Buffer | string): MediaStreamIncoming | null;
|
|
30
|
+
/**
|
|
31
|
+
* Encode mu-law audio for transmission back to Plivo.
|
|
32
|
+
*
|
|
33
|
+
* Plivo requires audio to be base64-encoded and wrapped in a `playAudio`
|
|
34
|
+
* JSON envelope.
|
|
35
|
+
*
|
|
36
|
+
* @param audio - Raw mu-law PCM bytes to send to the caller.
|
|
37
|
+
* @param _streamSid - Unused by Plivo's `playAudio` format (accepted for
|
|
38
|
+
* interface parity with other parsers).
|
|
39
|
+
* @returns JSON string conforming to the Plivo `playAudio` envelope.
|
|
40
|
+
*/
|
|
41
|
+
formatOutgoing(audio: Buffer, _streamSid: string): string;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=PlivoMediaStreamParser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlivoMediaStreamParser.d.ts","sourceRoot":"","sources":["../../../src/voice/parsers/PlivoMediaStreamParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAEtF;;;;;;;;;;;;GAYG;AACH,qBAAa,sBAAuB,YAAW,iBAAiB;IAC9D;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,mBAAmB,GAAG,IAAI;IAqDhE;;;;;;;;;;OAUG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM;CAM1D"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses the Plivo Audio Stream WebSocket protocol.
|
|
3
|
+
*
|
|
4
|
+
* Plivo sends JSON-encoded messages for stream lifecycle events (`start`,
|
|
5
|
+
* `stop`) and audio chunks (`media`). The audio payload is base64-encoded
|
|
6
|
+
* mu-law PCM, delivered in a `payload` field inside the `media` object.
|
|
7
|
+
*
|
|
8
|
+
* Outgoing audio is wrapped in a `playAudio` JSON envelope, which is the
|
|
9
|
+
* format Plivo expects when the server streams audio back to the caller.
|
|
10
|
+
* No explicit connection acknowledgment is required after the handshake.
|
|
11
|
+
*
|
|
12
|
+
* @see {@link https://www.plivo.com/docs/voice/xml/stream}
|
|
13
|
+
*/
|
|
14
|
+
export class PlivoMediaStreamParser {
|
|
15
|
+
/**
|
|
16
|
+
* Parse a raw WebSocket frame from Plivo's audio stream.
|
|
17
|
+
*
|
|
18
|
+
* Supported Plivo event types:
|
|
19
|
+
* - `start` — stream established; `stream_id` maps to `streamSid`,
|
|
20
|
+
* `call_uuid` maps to `callSid`.
|
|
21
|
+
* - `media` — audio chunk; `media.payload` contains base64-encoded mu-law.
|
|
22
|
+
* - `stop` — stream ended.
|
|
23
|
+
*
|
|
24
|
+
* @param data - Raw WebSocket frame payload (JSON string or Buffer from Plivo).
|
|
25
|
+
* @returns Normalised {@link MediaStreamIncoming} event, or `null` for
|
|
26
|
+
* unknown event types or malformed messages.
|
|
27
|
+
*/
|
|
28
|
+
parseIncoming(data) {
|
|
29
|
+
const raw = typeof data === 'string' ? data : data.toString('utf8');
|
|
30
|
+
let msg;
|
|
31
|
+
try {
|
|
32
|
+
msg = JSON.parse(raw);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const event = msg['event'];
|
|
38
|
+
const streamSid = msg['stream_id'];
|
|
39
|
+
if (!event || !streamSid) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
switch (event) {
|
|
43
|
+
case 'start': {
|
|
44
|
+
const callSid = msg['call_uuid'] ?? '';
|
|
45
|
+
const result = {
|
|
46
|
+
type: 'start',
|
|
47
|
+
streamSid,
|
|
48
|
+
callSid,
|
|
49
|
+
};
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
case 'media': {
|
|
53
|
+
const media = msg['media'];
|
|
54
|
+
if (!media)
|
|
55
|
+
return null;
|
|
56
|
+
const payloadB64 = media['payload'];
|
|
57
|
+
if (!payloadB64)
|
|
58
|
+
return null;
|
|
59
|
+
const result = {
|
|
60
|
+
type: 'audio',
|
|
61
|
+
payload: Buffer.from(payloadB64, 'base64'),
|
|
62
|
+
streamSid,
|
|
63
|
+
};
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
case 'stop': {
|
|
67
|
+
const result = { type: 'stop', streamSid };
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
default:
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Encode mu-law audio for transmission back to Plivo.
|
|
76
|
+
*
|
|
77
|
+
* Plivo requires audio to be base64-encoded and wrapped in a `playAudio`
|
|
78
|
+
* JSON envelope.
|
|
79
|
+
*
|
|
80
|
+
* @param audio - Raw mu-law PCM bytes to send to the caller.
|
|
81
|
+
* @param _streamSid - Unused by Plivo's `playAudio` format (accepted for
|
|
82
|
+
* interface parity with other parsers).
|
|
83
|
+
* @returns JSON string conforming to the Plivo `playAudio` envelope.
|
|
84
|
+
*/
|
|
85
|
+
formatOutgoing(audio, _streamSid) {
|
|
86
|
+
return JSON.stringify({
|
|
87
|
+
event: 'playAudio',
|
|
88
|
+
media: { payload: audio.toString('base64') },
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=PlivoMediaStreamParser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlivoMediaStreamParser.js","sourceRoot":"","sources":["../../../src/voice/parsers/PlivoMediaStreamParser.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,sBAAsB;IACjC;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,IAAqB;QACjC,MAAM,GAAG,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAEpE,IAAI,GAA4B,CAAC;QACjC,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAuB,CAAC;QACjD,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAuB,CAAC;QAEzD,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,QAAQ,KAAK,EAAE,CAAC;YACd,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,MAAM,OAAO,GAAI,GAAG,CAAC,WAAW,CAAwB,IAAI,EAAE,CAAC;gBAC/D,MAAM,MAAM,GAAwB;oBAClC,IAAI,EAAE,OAAO;oBACb,SAAS;oBACT,OAAO;iBACR,CAAC;gBACF,OAAO,MAAM,CAAC;YAChB,CAAC;YAED,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAwC,CAAC;gBAClE,IAAI,CAAC,KAAK;oBAAE,OAAO,IAAI,CAAC;gBAExB,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,CAAuB,CAAC;gBAC1D,IAAI,CAAC,UAAU;oBAAE,OAAO,IAAI,CAAC;gBAE7B,MAAM,MAAM,GAAwB;oBAClC,IAAI,EAAE,OAAO;oBACb,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC;oBAC1C,SAAS;iBACV,CAAC;gBACF,OAAO,MAAM,CAAC;YAChB,CAAC;YAED,KAAK,MAAM,CAAC,CAAC,CAAC;gBACZ,MAAM,MAAM,GAAwB,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;gBAChE,OAAO,MAAM,CAAC;YAChB,CAAC;YAED;gBACE,OAAO,IAAI,CAAC;QAChB,CAAC;IACH,CAAC;IAED;;;;;;;;;;OAUG;IACH,cAAc,CAAC,KAAa,EAAE,UAAkB;QAC9C,OAAO,IAAI,CAAC,SAAS,CAAC;YACpB,KAAK,EAAE,WAAW;YAClB,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;SAC7C,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { MediaStreamParser, MediaStreamIncoming } from '../MediaStreamParser.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parses the Telnyx media stream WebSocket protocol.
|
|
4
|
+
*
|
|
5
|
+
* Telnyx sends JSON-encoded messages for stream lifecycle events (`start`,
|
|
6
|
+
* `stop`) and audio chunks (`media`). Unlike Twilio, Telnyx does NOT deliver
|
|
7
|
+
* DTMF events over the media stream WebSocket — those arrive as HTTP webhooks
|
|
8
|
+
* to a separate endpoint and must be handled outside this parser.
|
|
9
|
+
*
|
|
10
|
+
* Outgoing audio is sent as a **raw binary Buffer** (mu-law PCM bytes without
|
|
11
|
+
* any JSON envelope) because Telnyx accepts unframed binary WebSocket frames
|
|
12
|
+
* directly. No explicit connection acknowledgment is needed after the
|
|
13
|
+
* handshake.
|
|
14
|
+
*
|
|
15
|
+
* @see {@link https://developers.telnyx.com/docs/voice/media-streaming}
|
|
16
|
+
*/
|
|
17
|
+
export declare class TelnyxMediaStreamParser implements MediaStreamParser {
|
|
18
|
+
/**
|
|
19
|
+
* Parse a raw WebSocket frame from Telnyx's media stream.
|
|
20
|
+
*
|
|
21
|
+
* Supported Telnyx event types:
|
|
22
|
+
* - `start` — stream established; `stream_id` maps to `streamSid`,
|
|
23
|
+
* `call_control_id` maps to `callSid`.
|
|
24
|
+
* - `media` — audio chunk; `chunk` field contains base64-encoded mu-law
|
|
25
|
+
* bytes; only `inbound` track frames are returned.
|
|
26
|
+
* - `stop` — stream ended.
|
|
27
|
+
*
|
|
28
|
+
* @param data - Raw WebSocket frame payload (JSON string or Buffer from Telnyx).
|
|
29
|
+
* @returns Normalised {@link MediaStreamIncoming} event, or `null` for
|
|
30
|
+
* outbound audio tracks, unknown event types, or malformed messages.
|
|
31
|
+
*/
|
|
32
|
+
parseIncoming(data: Buffer | string): MediaStreamIncoming | null;
|
|
33
|
+
/**
|
|
34
|
+
* Encode mu-law audio for transmission back to Telnyx.
|
|
35
|
+
*
|
|
36
|
+
* Telnyx accepts raw binary WebSocket frames; no JSON wrapping is applied.
|
|
37
|
+
*
|
|
38
|
+
* @param audio - Raw mu-law PCM bytes to send to the caller.
|
|
39
|
+
* @param _streamSid - Unused by Telnyx binary framing (accepted for interface
|
|
40
|
+
* parity with other parsers).
|
|
41
|
+
* @returns The audio Buffer unchanged, ready to send as a binary WS frame.
|
|
42
|
+
*/
|
|
43
|
+
formatOutgoing(audio: Buffer, _streamSid: string): Buffer;
|
|
44
|
+
/**
|
|
45
|
+
* No explicit connection acknowledgment is required by Telnyx.
|
|
46
|
+
*
|
|
47
|
+
* @returns Always `null`.
|
|
48
|
+
*/
|
|
49
|
+
formatConnected(_streamSid: string): null;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=TelnyxMediaStreamParser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TelnyxMediaStreamParser.d.ts","sourceRoot":"","sources":["../../../src/voice/parsers/TelnyxMediaStreamParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAEtF;;;;;;;;;;;;;;GAcG;AACH,qBAAa,uBAAwB,YAAW,iBAAiB;IAC/D;;;;;;;;;;;;;OAaG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,mBAAmB,GAAG,IAAI;IAyDhE;;;;;;;;;OASG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM;IAIzD;;;;OAIG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;CAG1C"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses the Telnyx media stream WebSocket protocol.
|
|
3
|
+
*
|
|
4
|
+
* Telnyx sends JSON-encoded messages for stream lifecycle events (`start`,
|
|
5
|
+
* `stop`) and audio chunks (`media`). Unlike Twilio, Telnyx does NOT deliver
|
|
6
|
+
* DTMF events over the media stream WebSocket — those arrive as HTTP webhooks
|
|
7
|
+
* to a separate endpoint and must be handled outside this parser.
|
|
8
|
+
*
|
|
9
|
+
* Outgoing audio is sent as a **raw binary Buffer** (mu-law PCM bytes without
|
|
10
|
+
* any JSON envelope) because Telnyx accepts unframed binary WebSocket frames
|
|
11
|
+
* directly. No explicit connection acknowledgment is needed after the
|
|
12
|
+
* handshake.
|
|
13
|
+
*
|
|
14
|
+
* @see {@link https://developers.telnyx.com/docs/voice/media-streaming}
|
|
15
|
+
*/
|
|
16
|
+
export class TelnyxMediaStreamParser {
|
|
17
|
+
/**
|
|
18
|
+
* Parse a raw WebSocket frame from Telnyx's media stream.
|
|
19
|
+
*
|
|
20
|
+
* Supported Telnyx event types:
|
|
21
|
+
* - `start` — stream established; `stream_id` maps to `streamSid`,
|
|
22
|
+
* `call_control_id` maps to `callSid`.
|
|
23
|
+
* - `media` — audio chunk; `chunk` field contains base64-encoded mu-law
|
|
24
|
+
* bytes; only `inbound` track frames are returned.
|
|
25
|
+
* - `stop` — stream ended.
|
|
26
|
+
*
|
|
27
|
+
* @param data - Raw WebSocket frame payload (JSON string or Buffer from Telnyx).
|
|
28
|
+
* @returns Normalised {@link MediaStreamIncoming} event, or `null` for
|
|
29
|
+
* outbound audio tracks, unknown event types, or malformed messages.
|
|
30
|
+
*/
|
|
31
|
+
parseIncoming(data) {
|
|
32
|
+
const raw = typeof data === 'string' ? data : data.toString('utf8');
|
|
33
|
+
let msg;
|
|
34
|
+
try {
|
|
35
|
+
msg = JSON.parse(raw);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const event = msg['event'];
|
|
41
|
+
const streamSid = msg['stream_id'];
|
|
42
|
+
if (!event || !streamSid) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
switch (event) {
|
|
46
|
+
case 'start': {
|
|
47
|
+
const callSid = msg['call_control_id'] ?? '';
|
|
48
|
+
const result = {
|
|
49
|
+
type: 'start',
|
|
50
|
+
streamSid,
|
|
51
|
+
callSid,
|
|
52
|
+
};
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
case 'media': {
|
|
56
|
+
const media = msg['media'];
|
|
57
|
+
if (!media)
|
|
58
|
+
return null;
|
|
59
|
+
// Ignore outbound audio echoes from Telnyx.
|
|
60
|
+
const track = media['track'];
|
|
61
|
+
if (track === 'outbound')
|
|
62
|
+
return null;
|
|
63
|
+
const chunk = media['chunk'];
|
|
64
|
+
if (!chunk)
|
|
65
|
+
return null;
|
|
66
|
+
const result = {
|
|
67
|
+
type: 'audio',
|
|
68
|
+
payload: Buffer.from(chunk, 'base64'),
|
|
69
|
+
streamSid,
|
|
70
|
+
};
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
case 'stop': {
|
|
74
|
+
const result = { type: 'stop', streamSid };
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
default:
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Encode mu-law audio for transmission back to Telnyx.
|
|
83
|
+
*
|
|
84
|
+
* Telnyx accepts raw binary WebSocket frames; no JSON wrapping is applied.
|
|
85
|
+
*
|
|
86
|
+
* @param audio - Raw mu-law PCM bytes to send to the caller.
|
|
87
|
+
* @param _streamSid - Unused by Telnyx binary framing (accepted for interface
|
|
88
|
+
* parity with other parsers).
|
|
89
|
+
* @returns The audio Buffer unchanged, ready to send as a binary WS frame.
|
|
90
|
+
*/
|
|
91
|
+
formatOutgoing(audio, _streamSid) {
|
|
92
|
+
return audio;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* No explicit connection acknowledgment is required by Telnyx.
|
|
96
|
+
*
|
|
97
|
+
* @returns Always `null`.
|
|
98
|
+
*/
|
|
99
|
+
formatConnected(_streamSid) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=TelnyxMediaStreamParser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TelnyxMediaStreamParser.js","sourceRoot":"","sources":["../../../src/voice/parsers/TelnyxMediaStreamParser.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,uBAAuB;IAClC;;;;;;;;;;;;;OAaG;IACH,aAAa,CAAC,IAAqB;QACjC,MAAM,GAAG,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAEpE,IAAI,GAA4B,CAAC;QACjC,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAuB,CAAC;QACjD,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAuB,CAAC;QAEzD,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,QAAQ,KAAK,EAAE,CAAC;YACd,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,MAAM,OAAO,GAAI,GAAG,CAAC,iBAAiB,CAAwB,IAAI,EAAE,CAAC;gBACrE,MAAM,MAAM,GAAwB;oBAClC,IAAI,EAAE,OAAO;oBACb,SAAS;oBACT,OAAO;iBACR,CAAC;gBACF,OAAO,MAAM,CAAC;YAChB,CAAC;YAED,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAwC,CAAC;gBAClE,IAAI,CAAC,KAAK;oBAAE,OAAO,IAAI,CAAC;gBAExB,4CAA4C;gBAC5C,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAuB,CAAC;gBACnD,IAAI,KAAK,KAAK,UAAU;oBAAE,OAAO,IAAI,CAAC;gBAEtC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAuB,CAAC;gBACnD,IAAI,CAAC,KAAK;oBAAE,OAAO,IAAI,CAAC;gBAExB,MAAM,MAAM,GAAwB;oBAClC,IAAI,EAAE,OAAO;oBACb,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC;oBACrC,SAAS;iBACV,CAAC;gBACF,OAAO,MAAM,CAAC;YAChB,CAAC;YAED,KAAK,MAAM,CAAC,CAAC,CAAC;gBACZ,MAAM,MAAM,GAAwB,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;gBAChE,OAAO,MAAM,CAAC;YAChB,CAAC;YAED;gBACE,OAAO,IAAI,CAAC;QAChB,CAAC;IACH,CAAC;IAED;;;;;;;;;OASG;IACH,cAAc,CAAC,KAAa,EAAE,UAAkB;QAC9C,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;OAIG;IACH,eAAe,CAAC,UAAkB;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { MediaStreamParser, MediaStreamIncoming } from '../MediaStreamParser.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parses the Twilio `<Connect><Stream>` WebSocket media stream protocol.
|
|
4
|
+
*
|
|
5
|
+
* Twilio sends all messages as JSON-encoded strings. Outbound audio is
|
|
6
|
+
* wrapped in the same JSON envelope so Twilio can associate it with the
|
|
7
|
+
* correct stream. An explicit `connected` acknowledgment is sent once
|
|
8
|
+
* immediately after the WebSocket handshake to signal that the listener is
|
|
9
|
+
* ready to receive media.
|
|
10
|
+
*
|
|
11
|
+
* @see {@link https://www.twilio.com/docs/voice/twiml/stream}
|
|
12
|
+
*/
|
|
13
|
+
export declare class TwilioMediaStreamParser implements MediaStreamParser {
|
|
14
|
+
/**
|
|
15
|
+
* Parse a raw WebSocket frame from Twilio's media stream.
|
|
16
|
+
*
|
|
17
|
+
* Supported Twilio event types:
|
|
18
|
+
* - `start` — stream established, includes callSid
|
|
19
|
+
* - `media` — audio chunk (inbound track only; outbound chunks are ignored)
|
|
20
|
+
* - `dtmf` — DTMF keypress detected
|
|
21
|
+
* - `stop` — stream ended
|
|
22
|
+
* - `mark` — named synchronisation marker
|
|
23
|
+
*
|
|
24
|
+
* @param data - Raw WebSocket frame payload (always a JSON string from Twilio).
|
|
25
|
+
* @returns Normalised {@link MediaStreamIncoming} event, or `null` for
|
|
26
|
+
* outbound audio tracks, unknown event types, or malformed messages.
|
|
27
|
+
*/
|
|
28
|
+
parseIncoming(data: Buffer | string): MediaStreamIncoming | null;
|
|
29
|
+
/**
|
|
30
|
+
* Encode mu-law audio for transmission back to the Twilio stream.
|
|
31
|
+
*
|
|
32
|
+
* Twilio requires base64-encoded audio wrapped in a JSON `media` envelope
|
|
33
|
+
* so it can route the audio to the correct stream.
|
|
34
|
+
*
|
|
35
|
+
* @param audio - Raw mu-law PCM bytes to send to the caller.
|
|
36
|
+
* @param streamSid - The stream identifier to include in the envelope.
|
|
37
|
+
* @returns JSON string conforming to the Twilio media-out envelope format.
|
|
38
|
+
*/
|
|
39
|
+
formatOutgoing(audio: Buffer, streamSid: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Generate the initial `connected` acknowledgment expected by Twilio
|
|
42
|
+
* immediately after the WebSocket connection is established.
|
|
43
|
+
*
|
|
44
|
+
* @param _streamSid - Unused — Twilio does not require the stream ID in the
|
|
45
|
+
* `connected` message, but the parameter is accepted for interface parity.
|
|
46
|
+
* @returns JSON string with the `connected` envelope.
|
|
47
|
+
*/
|
|
48
|
+
formatConnected(_streamSid: string): string;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=TwilioMediaStreamParser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TwilioMediaStreamParser.d.ts","sourceRoot":"","sources":["../../../src/voice/parsers/TwilioMediaStreamParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAEtF;;;;;;;;;;GAUG;AACH,qBAAa,uBAAwB,YAAW,iBAAiB;IAC/D;;;;;;;;;;;;;OAaG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,mBAAmB,GAAG,IAAI;IA+FhE;;;;;;;;;OASG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAQxD;;;;;;;OAOG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;CAO5C"}
|