@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,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Telnyx telephony provider for AgentOS voice calls.
|
|
3
|
+
*
|
|
4
|
+
* Implements {@link IVoiceCallProvider} using the Telnyx Call Control v2 API.
|
|
5
|
+
* Webhook verification uses Ed25519 public key verification as specified in
|
|
6
|
+
* the Telnyx security documentation.
|
|
7
|
+
*
|
|
8
|
+
* @module @framers/agentos/voice/providers/telnyx
|
|
9
|
+
*/
|
|
10
|
+
import { verify } from 'node:crypto';
|
|
11
|
+
import { randomUUID } from 'node:crypto';
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// TelnyxVoiceProvider
|
|
14
|
+
// ============================================================================
|
|
15
|
+
/**
|
|
16
|
+
* Telnyx voice call provider.
|
|
17
|
+
*
|
|
18
|
+
* Uses the Telnyx Call Control v2 API for outbound call initiation and
|
|
19
|
+
* in-call actions (hangup, speak). Webhook verification uses Ed25519 public
|
|
20
|
+
* key signing.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const provider = new TelnyxVoiceProvider({
|
|
25
|
+
* apiKey: process.env.TELNYX_API_KEY!,
|
|
26
|
+
* connectionId: process.env.TELNYX_CONNECTION_ID!,
|
|
27
|
+
* publicKey: process.env.TELNYX_PUBLIC_KEY, // optional
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export class TelnyxVoiceProvider {
|
|
32
|
+
constructor(config) {
|
|
33
|
+
this.name = 'telnyx';
|
|
34
|
+
this.config = config;
|
|
35
|
+
this.baseUrl = 'https://api.telnyx.com/v2';
|
|
36
|
+
this.authHeader = `Bearer ${config.apiKey}`;
|
|
37
|
+
this.fetch = config.fetchImpl ?? globalThis.fetch;
|
|
38
|
+
}
|
|
39
|
+
// ── Webhook ───────────────────────────────────────────────────────────────
|
|
40
|
+
/**
|
|
41
|
+
* Verify an incoming Telnyx webhook using Ed25519 signature verification.
|
|
42
|
+
*
|
|
43
|
+
* Telnyx signs `{timestamp}|{rawBody}` with the configured Ed25519 private key.
|
|
44
|
+
* The matching public key is provided as a base64-encoded DER SPKI blob.
|
|
45
|
+
*
|
|
46
|
+
* If no public key is configured, the check is skipped and `valid: true`
|
|
47
|
+
* is returned so the provider can operate without cryptographic validation
|
|
48
|
+
* during initial development.
|
|
49
|
+
*/
|
|
50
|
+
verifyWebhook(ctx) {
|
|
51
|
+
if (!this.config.publicKey) {
|
|
52
|
+
return { valid: true };
|
|
53
|
+
}
|
|
54
|
+
const timestamp = ctx.headers['x-telnyx-timestamp'];
|
|
55
|
+
const sigHeader = ctx.headers['x-telnyx-signature-ed25519'];
|
|
56
|
+
if (!timestamp || Array.isArray(timestamp) || !sigHeader || Array.isArray(sigHeader)) {
|
|
57
|
+
return { valid: false, error: 'Missing Telnyx signature headers' };
|
|
58
|
+
}
|
|
59
|
+
const signature = Buffer.from(sigHeader, 'base64');
|
|
60
|
+
const payload = Buffer.from(`${timestamp}|${ctx.body.toString()}`);
|
|
61
|
+
try {
|
|
62
|
+
const valid = verify(null, payload, {
|
|
63
|
+
key: Buffer.from(this.config.publicKey, 'base64'),
|
|
64
|
+
format: 'der',
|
|
65
|
+
type: 'spki',
|
|
66
|
+
}, signature);
|
|
67
|
+
return { valid };
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return { valid: false, error: 'Verification failed' };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Parse a Telnyx webhook JSON body into normalized {@link NormalizedCallEvent}s.
|
|
75
|
+
*
|
|
76
|
+
* Handles Telnyx Call Control event types:
|
|
77
|
+
* - `call.initiated` → `call-ringing`
|
|
78
|
+
* - `call.answered` → `call-answered`
|
|
79
|
+
* - `call.hangup` → `call-completed` or `call-hangup-user` based on hangup_cause
|
|
80
|
+
* - `call.dtmf.received` → `call-dtmf`
|
|
81
|
+
* - `call.machine.detection.ended` with `result === 'machine'` → `call-voicemail`
|
|
82
|
+
*/
|
|
83
|
+
parseWebhookEvent(ctx) {
|
|
84
|
+
let parsed;
|
|
85
|
+
try {
|
|
86
|
+
parsed = JSON.parse(ctx.body.toString());
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return { events: [] };
|
|
90
|
+
}
|
|
91
|
+
const { event_type, payload } = parsed.data;
|
|
92
|
+
const providerCallId = payload.call_control_id ?? '';
|
|
93
|
+
const timestamp = Date.now();
|
|
94
|
+
const events = [];
|
|
95
|
+
const base = () => ({ eventId: randomUUID(), providerCallId, timestamp });
|
|
96
|
+
switch (event_type) {
|
|
97
|
+
case 'call.initiated':
|
|
98
|
+
events.push({ ...base(), kind: 'call-ringing' });
|
|
99
|
+
break;
|
|
100
|
+
case 'call.answered':
|
|
101
|
+
events.push({ ...base(), kind: 'call-answered' });
|
|
102
|
+
break;
|
|
103
|
+
case 'call.hangup': {
|
|
104
|
+
// Distinguish user-initiated hangup from normal completion
|
|
105
|
+
const cause = payload.hangup_cause ?? '';
|
|
106
|
+
const kind = cause === 'user_busy' || cause === 'normal_clearing' || cause === 'originator_cancel'
|
|
107
|
+
? 'call-hangup-user'
|
|
108
|
+
: 'call-completed';
|
|
109
|
+
events.push({ ...base(), kind });
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case 'call.dtmf.received': {
|
|
113
|
+
const digit = payload.digit ?? '';
|
|
114
|
+
events.push({ ...base(), kind: 'call-dtmf', digit });
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
case 'call.machine.detection.ended':
|
|
118
|
+
if (payload.result === 'machine') {
|
|
119
|
+
events.push({ ...base(), kind: 'call-voicemail' });
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
default:
|
|
123
|
+
// Unrecognized event type — emit nothing
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
return { events };
|
|
127
|
+
}
|
|
128
|
+
// ── Call Control ──────────────────────────────────────────────────────────
|
|
129
|
+
/**
|
|
130
|
+
* Initiate an outbound call via the Telnyx Call Control v2 API.
|
|
131
|
+
*
|
|
132
|
+
* POSTs to `/calls` with a JSON body. The `mediaStreamUrl` (if provided)
|
|
133
|
+
* is stored internally for use after the call is answered — it is NOT sent
|
|
134
|
+
* in the initial call creation request.
|
|
135
|
+
*/
|
|
136
|
+
async initiateCall(input) {
|
|
137
|
+
const url = `${this.baseUrl}/calls`;
|
|
138
|
+
const response = await this.fetch(url, {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: {
|
|
141
|
+
Authorization: this.authHeader,
|
|
142
|
+
'Content-Type': 'application/json',
|
|
143
|
+
},
|
|
144
|
+
body: JSON.stringify({
|
|
145
|
+
connection_id: this.config.connectionId,
|
|
146
|
+
to: input.toNumber,
|
|
147
|
+
from: input.fromNumber,
|
|
148
|
+
webhook_url: input.webhookUrl,
|
|
149
|
+
}),
|
|
150
|
+
});
|
|
151
|
+
if (!response.ok) {
|
|
152
|
+
const text = await response.text().catch(() => String(response.status));
|
|
153
|
+
return { providerCallId: '', success: false, error: `Telnyx error ${response.status}: ${text}` };
|
|
154
|
+
}
|
|
155
|
+
const data = (await response.json());
|
|
156
|
+
return { providerCallId: data.data.call_control_id, success: true };
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Hang up an active call via the Telnyx Call Control hangup action.
|
|
160
|
+
*/
|
|
161
|
+
async hangupCall(input) {
|
|
162
|
+
const url = `${this.baseUrl}/calls/${input.providerCallId}/actions/hangup`;
|
|
163
|
+
await this.fetch(url, {
|
|
164
|
+
method: 'POST',
|
|
165
|
+
headers: {
|
|
166
|
+
Authorization: this.authHeader,
|
|
167
|
+
'Content-Type': 'application/json',
|
|
168
|
+
},
|
|
169
|
+
body: JSON.stringify({}),
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Speak text into a live call using Telnyx's text-to-speech speak action.
|
|
174
|
+
*
|
|
175
|
+
* Defaults to `female` voice and `en-US` language when not specified.
|
|
176
|
+
*/
|
|
177
|
+
async playTts(input) {
|
|
178
|
+
const url = `${this.baseUrl}/calls/${input.providerCallId}/actions/speak`;
|
|
179
|
+
await this.fetch(url, {
|
|
180
|
+
method: 'POST',
|
|
181
|
+
headers: {
|
|
182
|
+
Authorization: this.authHeader,
|
|
183
|
+
'Content-Type': 'application/json',
|
|
184
|
+
},
|
|
185
|
+
body: JSON.stringify({
|
|
186
|
+
payload: input.text,
|
|
187
|
+
voice: input.voice ?? 'female',
|
|
188
|
+
language: 'en-US',
|
|
189
|
+
}),
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=telnyx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telnyx.js","sourceRoot":"","sources":["../../../src/voice/providers/telnyx.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Twilio telephony provider for AgentOS voice calls.
|
|
3
|
+
*
|
|
4
|
+
* Implements {@link IVoiceCallProvider} using the Twilio REST API v2010-04-01.
|
|
5
|
+
* Webhook verification uses HMAC-SHA1 per Twilio's security documentation.
|
|
6
|
+
*
|
|
7
|
+
* @module @framers/agentos/voice/providers/twilio
|
|
8
|
+
*/
|
|
9
|
+
import type { IVoiceCallProvider, InitiateCallInput, InitiateCallResult, HangupCallInput, PlayTtsInput } from '../IVoiceCallProvider.js';
|
|
10
|
+
import type { WebhookContext, WebhookVerificationResult, WebhookParseResult } from '../types.js';
|
|
11
|
+
/** Configuration for {@link TwilioVoiceProvider}. */
|
|
12
|
+
export interface TwilioVoiceProviderConfig {
|
|
13
|
+
/** Twilio Account SID (starts with "AC"). */
|
|
14
|
+
accountSid: string;
|
|
15
|
+
/** Twilio Auth Token. */
|
|
16
|
+
authToken: string;
|
|
17
|
+
/**
|
|
18
|
+
* Optional fetch override — inject a mock in tests.
|
|
19
|
+
* Defaults to the global `fetch`.
|
|
20
|
+
*/
|
|
21
|
+
fetchImpl?: typeof fetch;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Twilio voice call provider.
|
|
25
|
+
*
|
|
26
|
+
* Uses the Twilio REST API 2010-04-01 for outbound call control and
|
|
27
|
+
* HMAC-SHA1 for inbound webhook signature verification.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const provider = new TwilioVoiceProvider({
|
|
32
|
+
* accountSid: process.env.TWILIO_ACCOUNT_SID!,
|
|
33
|
+
* authToken: process.env.TWILIO_AUTH_TOKEN!,
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare class TwilioVoiceProvider implements IVoiceCallProvider {
|
|
38
|
+
readonly name: "twilio";
|
|
39
|
+
private readonly config;
|
|
40
|
+
private readonly baseUrl;
|
|
41
|
+
private readonly authHeader;
|
|
42
|
+
private readonly fetch;
|
|
43
|
+
constructor(config: TwilioVoiceProviderConfig);
|
|
44
|
+
/**
|
|
45
|
+
* Verify an incoming Twilio webhook request using HMAC-SHA1.
|
|
46
|
+
*
|
|
47
|
+
* Twilio constructs the signed data by appending all POST params (sorted
|
|
48
|
+
* alphabetically) as key+value pairs directly to the full request URL.
|
|
49
|
+
*/
|
|
50
|
+
verifyWebhook(ctx: WebhookContext): WebhookVerificationResult;
|
|
51
|
+
/**
|
|
52
|
+
* Parse a Twilio webhook body into normalized {@link NormalizedCallEvent}s.
|
|
53
|
+
*
|
|
54
|
+
* Handles call status transitions (ringing, in-progress, completed, failed,
|
|
55
|
+
* busy, no-answer, canceled) and DTMF input.
|
|
56
|
+
*/
|
|
57
|
+
parseWebhookEvent(ctx: WebhookContext): WebhookParseResult;
|
|
58
|
+
/**
|
|
59
|
+
* Initiate an outbound call via the Twilio Calls API.
|
|
60
|
+
*
|
|
61
|
+
* Posts to `/Accounts/{accountSid}/Calls.json` with form-encoded body.
|
|
62
|
+
* All four status callback events (initiated, ringing, answered, completed)
|
|
63
|
+
* are requested.
|
|
64
|
+
*/
|
|
65
|
+
initiateCall(input: InitiateCallInput): Promise<InitiateCallResult>;
|
|
66
|
+
/**
|
|
67
|
+
* Hang up an active call by posting `Status=completed`.
|
|
68
|
+
*/
|
|
69
|
+
hangupCall(input: HangupCallInput): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Inject TTS into a live call using a TwiML `<Say>` verb.
|
|
72
|
+
*
|
|
73
|
+
* Sends a `Twiml` parameter containing a minimal `<Response><Say>` document.
|
|
74
|
+
* The optional `voice` attribute maps to Twilio's built-in voice names
|
|
75
|
+
* (e.g., `alice`, `Polly.Joanna`).
|
|
76
|
+
*/
|
|
77
|
+
playTts(input: PlayTtsInput): Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=twilio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"twilio.d.ts","sourceRoot":"","sources":["../../../src/voice/providers/twilio.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACb,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EACV,cAAc,EACd,yBAAyB,EACzB,kBAAkB,EAEnB,MAAM,aAAa,CAAC;AAMrB,qDAAqD;AACrD,MAAM,WAAW,yBAAyB;IACxC,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAMD;;;;;;;;;;;;;GAaG;AACH,qBAAa,mBAAoB,YAAW,kBAAkB;IAC5D,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAU;IAElC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAEzB,MAAM,EAAE,yBAAyB;IAW7C;;;;;OAKG;IACH,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,yBAAyB;IAyB7D;;;;;OAKG;IACH,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,kBAAkB;IAyD1D;;;;;;OAMG;IACG,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAgCzE;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAavD;;;;;;OAMG;IACG,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAelD"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Twilio telephony provider for AgentOS voice calls.
|
|
3
|
+
*
|
|
4
|
+
* Implements {@link IVoiceCallProvider} using the Twilio REST API v2010-04-01.
|
|
5
|
+
* Webhook verification uses HMAC-SHA1 per Twilio's security documentation.
|
|
6
|
+
*
|
|
7
|
+
* @module @framers/agentos/voice/providers/twilio
|
|
8
|
+
*/
|
|
9
|
+
import { createHmac } from 'node:crypto';
|
|
10
|
+
import { randomUUID } from 'node:crypto';
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// TwilioVoiceProvider
|
|
13
|
+
// ============================================================================
|
|
14
|
+
/**
|
|
15
|
+
* Twilio voice call provider.
|
|
16
|
+
*
|
|
17
|
+
* Uses the Twilio REST API 2010-04-01 for outbound call control and
|
|
18
|
+
* HMAC-SHA1 for inbound webhook signature verification.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const provider = new TwilioVoiceProvider({
|
|
23
|
+
* accountSid: process.env.TWILIO_ACCOUNT_SID!,
|
|
24
|
+
* authToken: process.env.TWILIO_AUTH_TOKEN!,
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class TwilioVoiceProvider {
|
|
29
|
+
constructor(config) {
|
|
30
|
+
this.name = 'twilio';
|
|
31
|
+
this.config = config;
|
|
32
|
+
this.baseUrl = 'https://api.twilio.com/2010-04-01';
|
|
33
|
+
this.authHeader =
|
|
34
|
+
'Basic ' +
|
|
35
|
+
Buffer.from(`${config.accountSid}:${config.authToken}`).toString('base64');
|
|
36
|
+
this.fetch = config.fetchImpl ?? globalThis.fetch;
|
|
37
|
+
}
|
|
38
|
+
// ── Webhook ───────────────────────────────────────────────────────────────
|
|
39
|
+
/**
|
|
40
|
+
* Verify an incoming Twilio webhook request using HMAC-SHA1.
|
|
41
|
+
*
|
|
42
|
+
* Twilio constructs the signed data by appending all POST params (sorted
|
|
43
|
+
* alphabetically) as key+value pairs directly to the full request URL.
|
|
44
|
+
*/
|
|
45
|
+
verifyWebhook(ctx) {
|
|
46
|
+
const signature = ctx.headers['x-twilio-signature'];
|
|
47
|
+
if (!signature || Array.isArray(signature)) {
|
|
48
|
+
return { valid: false, error: 'Missing x-twilio-signature header' };
|
|
49
|
+
}
|
|
50
|
+
// Build the signed data string
|
|
51
|
+
const bodyParams = new URLSearchParams(ctx.body.toString());
|
|
52
|
+
const sorted = [...bodyParams.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
53
|
+
let data = ctx.url;
|
|
54
|
+
for (const [key, value] of sorted) {
|
|
55
|
+
data += key + value;
|
|
56
|
+
}
|
|
57
|
+
const expected = createHmac('sha1', this.config.authToken)
|
|
58
|
+
.update(data)
|
|
59
|
+
.digest('base64');
|
|
60
|
+
const valid = expected === signature;
|
|
61
|
+
return {
|
|
62
|
+
valid,
|
|
63
|
+
...(valid ? {} : { error: 'Signature mismatch' }),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Parse a Twilio webhook body into normalized {@link NormalizedCallEvent}s.
|
|
68
|
+
*
|
|
69
|
+
* Handles call status transitions (ringing, in-progress, completed, failed,
|
|
70
|
+
* busy, no-answer, canceled) and DTMF input.
|
|
71
|
+
*/
|
|
72
|
+
parseWebhookEvent(ctx) {
|
|
73
|
+
const params = new URLSearchParams(ctx.body.toString());
|
|
74
|
+
const callSid = params.get('CallSid') ?? '';
|
|
75
|
+
const callStatus = params.get('CallStatus') ?? '';
|
|
76
|
+
const digits = params.get('Digits');
|
|
77
|
+
const timestamp = Date.now();
|
|
78
|
+
const events = [];
|
|
79
|
+
/** Helper: shared base fields. */
|
|
80
|
+
const base = () => ({
|
|
81
|
+
eventId: randomUUID(),
|
|
82
|
+
providerCallId: callSid,
|
|
83
|
+
timestamp,
|
|
84
|
+
});
|
|
85
|
+
switch (callStatus) {
|
|
86
|
+
case 'ringing':
|
|
87
|
+
events.push({ ...base(), kind: 'call-ringing' });
|
|
88
|
+
break;
|
|
89
|
+
case 'in-progress':
|
|
90
|
+
events.push({ ...base(), kind: 'call-answered' });
|
|
91
|
+
break;
|
|
92
|
+
case 'completed':
|
|
93
|
+
events.push({ ...base(), kind: 'call-completed' });
|
|
94
|
+
break;
|
|
95
|
+
case 'failed':
|
|
96
|
+
events.push({ ...base(), kind: 'call-failed' });
|
|
97
|
+
break;
|
|
98
|
+
case 'busy':
|
|
99
|
+
events.push({ ...base(), kind: 'call-busy' });
|
|
100
|
+
break;
|
|
101
|
+
case 'no-answer':
|
|
102
|
+
events.push({ ...base(), kind: 'call-no-answer' });
|
|
103
|
+
break;
|
|
104
|
+
case 'canceled':
|
|
105
|
+
events.push({ ...base(), kind: 'call-hangup-user' });
|
|
106
|
+
break;
|
|
107
|
+
default:
|
|
108
|
+
// initiated / queued / etc. — no normalized event emitted
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
// DTMF digit input
|
|
112
|
+
if (digits != null && digits !== '') {
|
|
113
|
+
events.push({
|
|
114
|
+
...base(),
|
|
115
|
+
kind: 'call-dtmf',
|
|
116
|
+
digit: digits,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return { events };
|
|
120
|
+
}
|
|
121
|
+
// ── Call Control ──────────────────────────────────────────────────────────
|
|
122
|
+
/**
|
|
123
|
+
* Initiate an outbound call via the Twilio Calls API.
|
|
124
|
+
*
|
|
125
|
+
* Posts to `/Accounts/{accountSid}/Calls.json` with form-encoded body.
|
|
126
|
+
* All four status callback events (initiated, ringing, answered, completed)
|
|
127
|
+
* are requested.
|
|
128
|
+
*/
|
|
129
|
+
async initiateCall(input) {
|
|
130
|
+
const url = `${this.baseUrl}/Accounts/${this.config.accountSid}/Calls.json`;
|
|
131
|
+
const body = [
|
|
132
|
+
`To=${encodeURIComponent(input.toNumber)}`,
|
|
133
|
+
`From=${encodeURIComponent(input.fromNumber)}`,
|
|
134
|
+
`Url=${encodeURIComponent(input.webhookUrl)}`,
|
|
135
|
+
`StatusCallback=${encodeURIComponent(input.statusCallbackUrl ?? '')}`,
|
|
136
|
+
`StatusCallbackEvent=initiated`,
|
|
137
|
+
`StatusCallbackEvent=ringing`,
|
|
138
|
+
`StatusCallbackEvent=answered`,
|
|
139
|
+
`StatusCallbackEvent=completed`,
|
|
140
|
+
].join('&');
|
|
141
|
+
const response = await this.fetch(url, {
|
|
142
|
+
method: 'POST',
|
|
143
|
+
headers: {
|
|
144
|
+
Authorization: this.authHeader,
|
|
145
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
146
|
+
},
|
|
147
|
+
body,
|
|
148
|
+
});
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
const text = await response.text().catch(() => String(response.status));
|
|
151
|
+
return { providerCallId: '', success: false, error: `Twilio error ${response.status}: ${text}` };
|
|
152
|
+
}
|
|
153
|
+
const data = (await response.json());
|
|
154
|
+
return { providerCallId: data.sid, success: true };
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Hang up an active call by posting `Status=completed`.
|
|
158
|
+
*/
|
|
159
|
+
async hangupCall(input) {
|
|
160
|
+
const url = `${this.baseUrl}/Accounts/${this.config.accountSid}/Calls/${input.providerCallId}.json`;
|
|
161
|
+
await this.fetch(url, {
|
|
162
|
+
method: 'POST',
|
|
163
|
+
headers: {
|
|
164
|
+
Authorization: this.authHeader,
|
|
165
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
166
|
+
},
|
|
167
|
+
body: 'Status=completed',
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Inject TTS into a live call using a TwiML `<Say>` verb.
|
|
172
|
+
*
|
|
173
|
+
* Sends a `Twiml` parameter containing a minimal `<Response><Say>` document.
|
|
174
|
+
* The optional `voice` attribute maps to Twilio's built-in voice names
|
|
175
|
+
* (e.g., `alice`, `Polly.Joanna`).
|
|
176
|
+
*/
|
|
177
|
+
async playTts(input) {
|
|
178
|
+
const url = `${this.baseUrl}/Accounts/${this.config.accountSid}/Calls/${input.providerCallId}.json`;
|
|
179
|
+
const voiceAttr = input.voice ? ` voice="${input.voice}"` : '';
|
|
180
|
+
const twiml = `<Response><Say${voiceAttr}>${input.text}</Say></Response>`;
|
|
181
|
+
await this.fetch(url, {
|
|
182
|
+
method: 'POST',
|
|
183
|
+
headers: {
|
|
184
|
+
Authorization: this.authHeader,
|
|
185
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
186
|
+
},
|
|
187
|
+
body: `Twiml=${encodeURIComponent(twiml)}`,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=twilio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"twilio.js","sourceRoot":"","sources":["../../../src/voice/providers/twilio.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview TwiML and XML generation helpers for telephony providers.
|
|
3
|
+
*
|
|
4
|
+
* Generates provider-specific XML/TwiML response payloads for Twilio, Telnyx,
|
|
5
|
+
* and Plivo. All text content and attribute values are XML-escaped to prevent
|
|
6
|
+
* injection or malformed markup.
|
|
7
|
+
*
|
|
8
|
+
* @module @framers/agentos/voice/twiml
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Generate TwiML for Twilio conversation mode using a bidirectional media stream.
|
|
12
|
+
*
|
|
13
|
+
* The returned markup instructs Twilio to open a WebSocket to `streamUrl` and
|
|
14
|
+
* stream audio in both directions for the duration of the call.
|
|
15
|
+
*
|
|
16
|
+
* @param streamUrl - WebSocket URL Twilio should connect to (e.g. `wss://…/stream`).
|
|
17
|
+
* @param token - Optional bearer token appended as a `?token=` query parameter.
|
|
18
|
+
* @returns A complete TwiML XML document string.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* res.type('text/xml').send(twilioConversationTwiml('wss://api.example.com/call', jwtToken));
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare function twilioConversationTwiml(streamUrl: string, token?: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Generate TwiML for Twilio notify mode — synthesise `text` over the call then hang up.
|
|
28
|
+
*
|
|
29
|
+
* Useful for delivering one-shot announcements (e.g. voicemail greetings, error
|
|
30
|
+
* messages) without establishing a full media stream.
|
|
31
|
+
*
|
|
32
|
+
* @param text - The message to speak to the caller.
|
|
33
|
+
* @param voice - Optional Twilio voice name (e.g. `'Polly.Joanna'`, `'alice'`).
|
|
34
|
+
* @returns A complete TwiML XML document string.
|
|
35
|
+
*/
|
|
36
|
+
export declare function twilioNotifyTwiml(text: string, voice?: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Generate a Telnyx streaming XML response.
|
|
39
|
+
*
|
|
40
|
+
* Telnyx primarily uses a REST API for call control, but this helper produces
|
|
41
|
+
* an XML acknowledgment document wrapping the stream URL for webhook responses
|
|
42
|
+
* that require XML.
|
|
43
|
+
*
|
|
44
|
+
* @param streamUrl - WebSocket URL Telnyx should stream audio to.
|
|
45
|
+
* @returns A complete XML document string.
|
|
46
|
+
*/
|
|
47
|
+
export declare function telnyxStreamXml(streamUrl: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Generate Plivo bidirectional streaming XML.
|
|
50
|
+
*
|
|
51
|
+
* Instructs Plivo to open a bidirectional WebSocket stream and keep the call
|
|
52
|
+
* alive for the duration of the stream session.
|
|
53
|
+
*
|
|
54
|
+
* @param streamUrl - WebSocket URL Plivo should connect to.
|
|
55
|
+
* @returns A complete Plivo XML document string.
|
|
56
|
+
*/
|
|
57
|
+
export declare function plivoStreamXml(streamUrl: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Generate Plivo speak + hangup XML.
|
|
60
|
+
*
|
|
61
|
+
* Synthesises `text` to the caller using Plivo's TTS engine and immediately
|
|
62
|
+
* hangs up after playback completes.
|
|
63
|
+
*
|
|
64
|
+
* @param text - The message to speak to the caller.
|
|
65
|
+
* @param voice - Optional Plivo voice name (e.g. `'WOMAN'`, `'Polly.Joanna'`).
|
|
66
|
+
* @returns A complete Plivo XML document string.
|
|
67
|
+
*/
|
|
68
|
+
export declare function plivoNotifyXml(text: string, voice?: string): string;
|
|
69
|
+
//# sourceMappingURL=twiml.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"twiml.d.ts","sourceRoot":"","sources":["../../src/voice/twiml.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGjF;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGtE;AAMD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzD;AAMD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGnE"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview TwiML and XML generation helpers for telephony providers.
|
|
3
|
+
*
|
|
4
|
+
* Generates provider-specific XML/TwiML response payloads for Twilio, Telnyx,
|
|
5
|
+
* and Plivo. All text content and attribute values are XML-escaped to prevent
|
|
6
|
+
* injection or malformed markup.
|
|
7
|
+
*
|
|
8
|
+
* @module @framers/agentos/voice/twiml
|
|
9
|
+
*/
|
|
10
|
+
import { escapeXml } from './telephony-audio.js';
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Twilio TwiML
|
|
13
|
+
// ============================================================================
|
|
14
|
+
/**
|
|
15
|
+
* Generate TwiML for Twilio conversation mode using a bidirectional media stream.
|
|
16
|
+
*
|
|
17
|
+
* The returned markup instructs Twilio to open a WebSocket to `streamUrl` and
|
|
18
|
+
* stream audio in both directions for the duration of the call.
|
|
19
|
+
*
|
|
20
|
+
* @param streamUrl - WebSocket URL Twilio should connect to (e.g. `wss://…/stream`).
|
|
21
|
+
* @param token - Optional bearer token appended as a `?token=` query parameter.
|
|
22
|
+
* @returns A complete TwiML XML document string.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* res.type('text/xml').send(twilioConversationTwiml('wss://api.example.com/call', jwtToken));
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export function twilioConversationTwiml(streamUrl, token) {
|
|
30
|
+
const url = token ? `${streamUrl}?token=${token}` : streamUrl;
|
|
31
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n<Response><Connect><Stream url="${escapeXml(url)}" /></Connect></Response>`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Generate TwiML for Twilio notify mode — synthesise `text` over the call then hang up.
|
|
35
|
+
*
|
|
36
|
+
* Useful for delivering one-shot announcements (e.g. voicemail greetings, error
|
|
37
|
+
* messages) without establishing a full media stream.
|
|
38
|
+
*
|
|
39
|
+
* @param text - The message to speak to the caller.
|
|
40
|
+
* @param voice - Optional Twilio voice name (e.g. `'Polly.Joanna'`, `'alice'`).
|
|
41
|
+
* @returns A complete TwiML XML document string.
|
|
42
|
+
*/
|
|
43
|
+
export function twilioNotifyTwiml(text, voice) {
|
|
44
|
+
const voiceAttr = voice ? ` voice="${escapeXml(voice)}"` : '';
|
|
45
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n<Response><Say${voiceAttr}>${escapeXml(text)}</Say><Hangup/></Response>`;
|
|
46
|
+
}
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// Telnyx XML
|
|
49
|
+
// ============================================================================
|
|
50
|
+
/**
|
|
51
|
+
* Generate a Telnyx streaming XML response.
|
|
52
|
+
*
|
|
53
|
+
* Telnyx primarily uses a REST API for call control, but this helper produces
|
|
54
|
+
* an XML acknowledgment document wrapping the stream URL for webhook responses
|
|
55
|
+
* that require XML.
|
|
56
|
+
*
|
|
57
|
+
* @param streamUrl - WebSocket URL Telnyx should stream audio to.
|
|
58
|
+
* @returns A complete XML document string.
|
|
59
|
+
*/
|
|
60
|
+
export function telnyxStreamXml(streamUrl) {
|
|
61
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n<Response><Stream url="${escapeXml(streamUrl)}" /></Response>`;
|
|
62
|
+
}
|
|
63
|
+
// ============================================================================
|
|
64
|
+
// Plivo XML
|
|
65
|
+
// ============================================================================
|
|
66
|
+
/**
|
|
67
|
+
* Generate Plivo bidirectional streaming XML.
|
|
68
|
+
*
|
|
69
|
+
* Instructs Plivo to open a bidirectional WebSocket stream and keep the call
|
|
70
|
+
* alive for the duration of the stream session.
|
|
71
|
+
*
|
|
72
|
+
* @param streamUrl - WebSocket URL Plivo should connect to.
|
|
73
|
+
* @returns A complete Plivo XML document string.
|
|
74
|
+
*/
|
|
75
|
+
export function plivoStreamXml(streamUrl) {
|
|
76
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n<Response><Stream bidirectional="true" keepCallAlive="true">${escapeXml(streamUrl)}</Stream></Response>`;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Generate Plivo speak + hangup XML.
|
|
80
|
+
*
|
|
81
|
+
* Synthesises `text` to the caller using Plivo's TTS engine and immediately
|
|
82
|
+
* hangs up after playback completes.
|
|
83
|
+
*
|
|
84
|
+
* @param text - The message to speak to the caller.
|
|
85
|
+
* @param voice - Optional Plivo voice name (e.g. `'WOMAN'`, `'Polly.Joanna'`).
|
|
86
|
+
* @returns A complete Plivo XML document string.
|
|
87
|
+
*/
|
|
88
|
+
export function plivoNotifyXml(text, voice) {
|
|
89
|
+
const voiceAttr = voice ? ` voice="${escapeXml(voice)}"` : '';
|
|
90
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n<Response><Speak${voiceAttr}>${escapeXml(text)}</Speak><Hangup/></Response>`;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=twiml.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"twiml.js","sourceRoot":"","sources":["../../src/voice/twiml.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,+EAA+E;AAC/E,eAAe;AACf,+EAA+E;AAE/E;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,uBAAuB,CAAC,SAAiB,EAAE,KAAc;IACvE,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9D,OAAO,2EAA2E,SAAS,CAAC,GAAG,CAAC,2BAA2B,CAAC;AAC9H,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,KAAc;IAC5D,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,WAAW,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,OAAO,yDAAyD,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC,4BAA4B,CAAC;AAC3H,CAAC;AAED,+EAA+E;AAC/E,aAAa;AACb,+EAA+E;AAE/E;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,OAAO,kEAAkE,SAAS,CAAC,SAAS,CAAC,iBAAiB,CAAC;AACjH,CAAC;AAED,+EAA+E;AAC/E,YAAY;AACZ,+EAA+E;AAE/E;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,OAAO,uGAAuG,SAAS,CAAC,SAAS,CAAC,sBAAsB,CAAC;AAC3J,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,KAAc;IACzD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,WAAW,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,OAAO,2DAA2D,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC,8BAA8B,CAAC;AAC/H,CAAC"}
|