@memberjunction/ai-bridge-twilio 5.41.0 → 5.42.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/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/register-native.d.ts +19 -0
- package/dist/register-native.d.ts.map +1 -0
- package/dist/register-native.js +34 -0
- package/dist/register-native.js.map +1 -0
- package/dist/twilio-native-call-sdk.d.ts +281 -0
- package/dist/twilio-native-call-sdk.d.ts.map +1 -0
- package/dist/twilio-native-call-sdk.js +313 -0
- package/dist/twilio-native-call-sdk.js.map +1 -0
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
export * from './twilio-call-sdk.js';
|
|
2
|
+
export * from './twilio-native-call-sdk.js';
|
|
2
3
|
export * from './twilio-bridge.js';
|
|
4
|
+
export * from './register-native.js';
|
|
3
5
|
import { LoadTwilioBridge } from './twilio-bridge.js';
|
|
6
|
+
import { RegisterTwilioNativeSdk } from './register-native.js';
|
|
4
7
|
// Static reference so bundlers cannot tree-shake the @RegisterClass(BaseRealtimeBridge, 'TwilioBridge')
|
|
5
8
|
// registration. Calling the no-op here keeps the driver resolvable by the engine's ClassFactory.
|
|
6
9
|
LoadTwilioBridge();
|
|
10
|
+
// Register Twilio's native two-way call-SDK binding so the engine auto-binds it at StartBridgeSession.
|
|
11
|
+
RegisterTwilioNativeSdk();
|
|
7
12
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAElC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,wGAAwG;AACxG,iGAAiG;AACjG,gBAAgB,EAAE,CAAC;AACnB,uGAAuG;AACvG,uBAAuB,EAAE,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Registers the Twilio **native two-way** call-SDK binding into the {@link BridgeNativeSdkRegistry}
|
|
3
|
+
* so `AIBridgeEngine.StartBridgeSession` auto-binds it onto a resolved `TwilioBridge` before connect — no
|
|
4
|
+
* per-deployment wiring needed. The typed downcast + `SetSdkFactory` stay confined here (where the concrete
|
|
5
|
+
* `TwilioBridge` type is known), keeping the engine seam free of provider types.
|
|
6
|
+
*
|
|
7
|
+
* A host can still override per-session via `StartBridgeSessionParams.BindSdk` — e.g. to inject a fake
|
|
8
|
+
* telephony SDK in a test instead of the native two-way default.
|
|
9
|
+
*
|
|
10
|
+
* @module @memberjunction/ai-bridge-twilio
|
|
11
|
+
* @author MemberJunction.com
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Registers Twilio's native two-way call binding. Idempotent (latest-wins in the registry), and invoked from
|
|
15
|
+
* the package `index.ts` so merely importing `@memberjunction/ai-bridge-twilio` wires the default binding.
|
|
16
|
+
* Also exported as a tree-shake-safe no-op-returning function callers can reference to force-retain the module.
|
|
17
|
+
*/
|
|
18
|
+
export declare function RegisterTwilioNativeSdk(): void;
|
|
19
|
+
//# sourceMappingURL=register-native.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-native.d.ts","sourceRoot":"","sources":["../src/register-native.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AASH;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAS9C"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Registers the Twilio **native two-way** call-SDK binding into the {@link BridgeNativeSdkRegistry}
|
|
3
|
+
* so `AIBridgeEngine.StartBridgeSession` auto-binds it onto a resolved `TwilioBridge` before connect — no
|
|
4
|
+
* per-deployment wiring needed. The typed downcast + `SetSdkFactory` stay confined here (where the concrete
|
|
5
|
+
* `TwilioBridge` type is known), keeping the engine seam free of provider types.
|
|
6
|
+
*
|
|
7
|
+
* A host can still override per-session via `StartBridgeSessionParams.BindSdk` — e.g. to inject a fake
|
|
8
|
+
* telephony SDK in a test instead of the native two-way default.
|
|
9
|
+
*
|
|
10
|
+
* @module @memberjunction/ai-bridge-twilio
|
|
11
|
+
* @author MemberJunction.com
|
|
12
|
+
*/
|
|
13
|
+
import { BridgeNativeSdkRegistry } from '@memberjunction/ai-bridge-base';
|
|
14
|
+
import { BindTwilioNativeCall } from './twilio-native-call-sdk.js';
|
|
15
|
+
/** The `MJ: AI Bridge Providers.DriverClass` value the Twilio provider row carries. */
|
|
16
|
+
const TWILIO_DRIVER_CLASS = 'TwilioBridge';
|
|
17
|
+
/**
|
|
18
|
+
* Registers Twilio's native two-way call binding. Idempotent (latest-wins in the registry), and invoked from
|
|
19
|
+
* the package `index.ts` so merely importing `@memberjunction/ai-bridge-twilio` wires the default binding.
|
|
20
|
+
* Also exported as a tree-shake-safe no-op-returning function callers can reference to force-retain the module.
|
|
21
|
+
*/
|
|
22
|
+
export function RegisterTwilioNativeSdk() {
|
|
23
|
+
BridgeNativeSdkRegistry.Instance.Register(TWILIO_DRIVER_CLASS, (driver) => {
|
|
24
|
+
// Safe downcast: the registry is keyed by DriverClass, so the engine only ever applies this to the
|
|
25
|
+
// TwilioBridge it resolved for 'TwilioBridge'. TwilioBridge extends BaseTelephonyBridge (a
|
|
26
|
+
// BaseRealtimeBridge), so the registry's BaseRealtimeBridge driver param accommodates it.
|
|
27
|
+
// BindTwilioNativeCall() defaults to the lazy native loader, which reads NativeModuleSpecifier from
|
|
28
|
+
// the per-session Configuration.
|
|
29
|
+
driver.SetSdkFactory(BindTwilioNativeCall());
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
// Register on import (mirrors the LoadTwilioBridge() tree-shake pattern in index.ts).
|
|
33
|
+
RegisterTwilioNativeSdk();
|
|
34
|
+
//# sourceMappingURL=register-native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-native.js","sourceRoot":"","sources":["../src/register-native.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,uBAAuB,EAAsB,MAAM,gCAAgC,CAAC;AAE7F,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAEhE,uFAAuF;AACvF,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAE3C;;;;GAIG;AACH,MAAM,UAAU,uBAAuB;IACnC,uBAAuB,CAAC,QAAQ,CAAC,QAAQ,CAAC,mBAAmB,EAAE,CAAC,MAA0B,EAAE,EAAE;QAC1F,mGAAmG;QACnG,2FAA2F;QAC3F,0FAA0F;QAC1F,oGAAoG;QACpG,iCAAiC;QAChC,MAAuB,CAAC,aAAa,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;AACP,CAAC;AAED,sFAAsF;AACtF,uBAAuB,EAAE,CAAC"}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `TwilioNativeCallSdk` — a **real, two-way** {@link ITelephonyCallSdk} binding over the
|
|
3
|
+
* native **Twilio Programmable Voice + Media Streams** path. Unlike the shipped, unbound
|
|
4
|
+
* {@link import('./twilio-call-sdk.js').TwilioCallSdk} (every operation throws "bind the real Twilio
|
|
5
|
+
* client"), this adapter actually carries the call: {@link TwilioNativeCallSdk.sendAudioFrame} forwards
|
|
6
|
+
* the agent's synthesized PCM onto the call's Media-Streams websocket (the agent's **voice** on the
|
|
7
|
+
* call), inbound Media-Streams audio maps to the seam's audio-frame callback (what the agent **hears**),
|
|
8
|
+
* and DTMF / transfer / hangup actually reach the native client.
|
|
9
|
+
*
|
|
10
|
+
* ## Why a native binding
|
|
11
|
+
* A phone call is a single-leg, bidirectional audio source. The native Twilio path has two halves — a
|
|
12
|
+
* **REST API** (place / hang-up / transfer / send-DTMF) and a **Media Streams** websocket (bidirectional
|
|
13
|
+
* realtime audio + inbound DTMF events). This adapter sits over both so a single binding gives the agent
|
|
14
|
+
* full-duplex audio plus the call controls the telephony seam declares.
|
|
15
|
+
*
|
|
16
|
+
* ## The native module seam (no SDK types leak)
|
|
17
|
+
* The `twilio` SDK is NOT imported here. Deployments wrap the REST + Media-Streams plumbing behind the
|
|
18
|
+
* small structural {@link NativeCallModule} surface declared below, and this adapter depends ONLY on that
|
|
19
|
+
* surface — none of `twilio`'s real types leak into `@memberjunction/ai-bridge-twilio`. The module is
|
|
20
|
+
* loaded **lazily** behind an injectable {@link NativeModuleLoader} so the package builds and unit-tests
|
|
21
|
+
* with **no `twilio` install and no network**. Tests inject a fake module; production points
|
|
22
|
+
* {@link TwilioNativeSdkConfig.NativeModuleSpecifier} at the real adapter package/path.
|
|
23
|
+
*
|
|
24
|
+
* ## Auth (deployment responsibility)
|
|
25
|
+
* The native client authenticates with the Twilio **Account SID + Auth Token** (or API key/secret) and a
|
|
26
|
+
* resolved Media-Streams websocket URL. These resolve **upstream** (MJ credential system / provider
|
|
27
|
+
* `Configuration`) and are **never inlined** at a call site — {@link readNativeConfig} reads already-
|
|
28
|
+
* resolved values out of the loose `Configuration` map the engine passes at connect.
|
|
29
|
+
*
|
|
30
|
+
* @module @memberjunction/ai-bridge-twilio
|
|
31
|
+
* @author MemberJunction.com
|
|
32
|
+
* @see {@link ITelephonyCallSdk} — the platform-agnostic telephony seam this binds.
|
|
33
|
+
*/
|
|
34
|
+
import { ITelephonyCallSdk } from '@memberjunction/ai-bridge-base';
|
|
35
|
+
/** One inbound Media-Streams audio frame the native adapter surfaces (what the agent hears). */
|
|
36
|
+
export interface NativeCallAudioFrame {
|
|
37
|
+
/** Raw PCM/μ-law bytes for this frame (`Uint8Array` view or standalone `ArrayBuffer`). */
|
|
38
|
+
data: Uint8Array | ArrayBuffer;
|
|
39
|
+
/** Optional epoch-ms capture timestamp (informational; the seam carries only the bytes). */
|
|
40
|
+
timestampMs?: number;
|
|
41
|
+
}
|
|
42
|
+
/** Options the native adapter's `createCall` (outbound dial) accepts. Mirrors the seam's `dial` args. */
|
|
43
|
+
export interface NativeDialArgs {
|
|
44
|
+
/** Destination number to dial (E.164 recommended). */
|
|
45
|
+
toNumber: string;
|
|
46
|
+
/** The agent's caller-id / DID the call originates FROM. */
|
|
47
|
+
fromNumber: string;
|
|
48
|
+
/** Provider-specific dial options (status-callback URL, stream URL, recording flags, …). */
|
|
49
|
+
extra?: Record<string, unknown>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The live native-call client the adapter hands back. The surface mirrors {@link ITelephonyCallSdk} but in
|
|
53
|
+
* the native adapter's own (lower-cased) vocabulary; this adapter maps between the two. VERIFY against the
|
|
54
|
+
* native Twilio adapter.
|
|
55
|
+
*/
|
|
56
|
+
export interface NativeCallClient {
|
|
57
|
+
/** Places the outbound call (REST `calls.create` with a `<Connect><Stream>` TwiML). Resolves the Call SID. */
|
|
58
|
+
dial(args: NativeDialArgs): Promise<string>;
|
|
59
|
+
/** Accepts the Media-Streams websocket for an already-delivered inbound Call SID. */
|
|
60
|
+
answer(callId: string): Promise<void>;
|
|
61
|
+
/** Ends the call (REST `calls(sid).update({ status: 'completed' })`). */
|
|
62
|
+
hangup(callId: string): Promise<void>;
|
|
63
|
+
/** Pushes one outbound PCM frame onto the call's Media-Streams websocket — the agent's voice. */
|
|
64
|
+
sendAudioFrame(pcm: ArrayBuffer): void;
|
|
65
|
+
/** Registers the inbound Media-Streams audio callback. "Latest handler wins." */
|
|
66
|
+
onAudioFrame(cb: (frame: NativeCallAudioFrame) => void): void;
|
|
67
|
+
/** Sends DTMF digits on the call (REST `<Play digits>` / `<Dial sendDigits>`). */
|
|
68
|
+
sendDtmf(digits: string): Promise<void>;
|
|
69
|
+
/** Registers the inbound DTMF callback (`<Gather>` results or Media-Streams `dtmf` events). */
|
|
70
|
+
onDtmf(cb: (digits: string) => void): void;
|
|
71
|
+
/** Transfers the live call (REST `calls(sid).update({ twiml: '<Dial>...' })`). */
|
|
72
|
+
transfer(callId: string, toNumber: string): Promise<void>;
|
|
73
|
+
/** Registers the call-ended callback (status-callback `completed`/`failed` or stream `stop`). */
|
|
74
|
+
onCallEnded(cb: () => void): void;
|
|
75
|
+
}
|
|
76
|
+
/** Options passed to {@link NativeCallModule.createClient}. Credentials are resolved upstream. */
|
|
77
|
+
export interface NativeClientOptions {
|
|
78
|
+
/** The Twilio Account SID (resolved upstream). */
|
|
79
|
+
AccountSid?: string;
|
|
80
|
+
/** The Twilio Auth Token (resolved upstream). */
|
|
81
|
+
AuthToken?: string;
|
|
82
|
+
/** The Twilio API Key SID (resolved upstream; alternative to Account SID + Auth Token). */
|
|
83
|
+
ApiKeySid?: string;
|
|
84
|
+
/** The Twilio API Key Secret (resolved upstream). */
|
|
85
|
+
ApiKeySecret?: string;
|
|
86
|
+
/** The Media-Streams websocket URL the call's `<Connect><Stream>` connects to (resolved upstream). */
|
|
87
|
+
StreamUrl?: string;
|
|
88
|
+
}
|
|
89
|
+
/** The native adapter module surface — a factory that constructs a {@link NativeCallClient}. */
|
|
90
|
+
export interface NativeCallModule {
|
|
91
|
+
/**
|
|
92
|
+
* Constructs a native call client from resolved options (Twilio credentials already resolved upstream).
|
|
93
|
+
* VERIFY against the native Twilio adapter (`createClient` vs a `Client` constructor).
|
|
94
|
+
*/
|
|
95
|
+
createClient(options: NativeClientOptions): NativeCallClient;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Resolved configuration for {@link TwilioNativeCallSdk}. Credentials resolve **upstream** (MJ credential
|
|
99
|
+
* system / provider `Configuration`) — this object carries already-resolved values; **never inline
|
|
100
|
+
* secrets** at a call site.
|
|
101
|
+
*/
|
|
102
|
+
export interface TwilioNativeSdkConfig {
|
|
103
|
+
/** The Twilio Account SID (resolved upstream). */
|
|
104
|
+
AccountSid?: string;
|
|
105
|
+
/** The Twilio Auth Token (resolved upstream). */
|
|
106
|
+
AuthToken?: string;
|
|
107
|
+
/** The Twilio API Key SID (resolved upstream; alternative to Account SID + Auth Token). */
|
|
108
|
+
ApiKeySid?: string;
|
|
109
|
+
/** The Twilio API Key Secret (resolved upstream). */
|
|
110
|
+
ApiKeySecret?: string;
|
|
111
|
+
/** The Media-Streams websocket URL the call's `<Connect><Stream>` connects to (resolved upstream). */
|
|
112
|
+
StreamUrl?: string;
|
|
113
|
+
/**
|
|
114
|
+
* The module specifier of the native Twilio adapter to load (an internal adapter package name or an
|
|
115
|
+
* absolute path to a sidecar entry). Required in production; tests inject a loader.
|
|
116
|
+
*/
|
|
117
|
+
NativeModuleSpecifier?: string;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Copies arbitrary PCM bytes into a standalone `ArrayBuffer`, never aliasing the caller's buffer window.
|
|
121
|
+
* A `Uint8Array` view may be a slice of a larger backing buffer (offset/length), so a naive `.buffer`
|
|
122
|
+
* grab would leak neighbouring audio; this copies exactly the view's bytes. Defined locally because the
|
|
123
|
+
* Twilio package has no shared coercion helper to reuse (unlike the Zoom package's RTMS binding).
|
|
124
|
+
*/
|
|
125
|
+
export declare function toArrayBuffer(data: Uint8Array | ArrayBuffer): ArrayBuffer;
|
|
126
|
+
/**
|
|
127
|
+
* **Pure mapping** of one native inbound audio frame onto the raw PCM `ArrayBuffer` the telephony seam's
|
|
128
|
+
* audio callback delivers. Copies the bytes (see {@link toArrayBuffer}). Isolated from I/O for direct
|
|
129
|
+
* unit testing. (The telephony seam carries no per-speaker label — a 1:1 call has one remote party.)
|
|
130
|
+
*/
|
|
131
|
+
export declare function mapNativeAudioFrame(frame: NativeCallAudioFrame): ArrayBuffer;
|
|
132
|
+
/**
|
|
133
|
+
* The injectable loader for the native Twilio adapter — overridable so unit tests supply a fake module
|
|
134
|
+
* with no `twilio` install and no network. Production leaves it as {@link defaultNativeLoader}, which
|
|
135
|
+
* lazily imports the configured {@link TwilioNativeSdkConfig.NativeModuleSpecifier}.
|
|
136
|
+
*/
|
|
137
|
+
export type NativeModuleLoader = (specifier: string) => Promise<NativeCallModule>;
|
|
138
|
+
/**
|
|
139
|
+
* Lazily loads the native Twilio adapter at the given specifier (category: runtime plugin discovery from
|
|
140
|
+
* config — the adapter path is deployment-supplied and not known at build time, so a static import is
|
|
141
|
+
* impossible). Throws a precise, actionable error when it can't be loaded so a misconfigured deployment
|
|
142
|
+
* fails loudly, not silently.
|
|
143
|
+
*
|
|
144
|
+
* VERIFY against the native Twilio adapter: the module's default/namespace interop + that it exposes
|
|
145
|
+
* `createClient`.
|
|
146
|
+
*/
|
|
147
|
+
export declare const defaultNativeLoader: NativeModuleLoader;
|
|
148
|
+
/**
|
|
149
|
+
* A **real, two-way** {@link ITelephonyCallSdk} over the native Twilio Programmable Voice + Media Streams
|
|
150
|
+
* path.
|
|
151
|
+
*
|
|
152
|
+
* Gives the agent both **hearing** (inbound Media-Streams audio → the seam's audio callback) and a
|
|
153
|
+
* **voice** ({@link sendAudioFrame} → the native Media-Streams send path), plus working DTMF, transfer,
|
|
154
|
+
* and hangup. Construct via {@link BindTwilioNativeCall} (the factory the bridge's `SetSdkFactory` wants),
|
|
155
|
+
* not directly, so config resolution + the lazy loader wire consistently.
|
|
156
|
+
*
|
|
157
|
+
* The adapter tracks the active Call SID so per-call REST/websocket operations can be addressed, and
|
|
158
|
+
* re-registers any handlers the bridge set before the call existed once a call comes online.
|
|
159
|
+
*/
|
|
160
|
+
export declare class TwilioNativeCallSdk implements ITelephonyCallSdk {
|
|
161
|
+
/** Resolved config (credentials + stream URL + the native module specifier). */
|
|
162
|
+
private readonly config;
|
|
163
|
+
/** The native-adapter module loader (overridable for tests). */
|
|
164
|
+
private readonly loadModule;
|
|
165
|
+
/** The live native client once {@link dial}/{@link answer} brings a call online. */
|
|
166
|
+
private client;
|
|
167
|
+
/** The active call's SID, so per-call operations can address it. */
|
|
168
|
+
private activeCallId;
|
|
169
|
+
/** The inbound audio handler the bridge registered (may predate the call). */
|
|
170
|
+
private audioHandler?;
|
|
171
|
+
/** The inbound DTMF handler the bridge registered (may predate the call). */
|
|
172
|
+
private dtmfHandler?;
|
|
173
|
+
/** The call-ended handler the bridge registered (may predate the call). */
|
|
174
|
+
private endedHandler?;
|
|
175
|
+
/**
|
|
176
|
+
* @param config Resolved credentials + stream URL + the native module specifier.
|
|
177
|
+
* @param loadModule The native-adapter loader (defaults to the lazy specifier loader).
|
|
178
|
+
*/
|
|
179
|
+
constructor(config: TwilioNativeSdkConfig, loadModule?: NativeModuleLoader);
|
|
180
|
+
/**
|
|
181
|
+
* Loads the native adapter, constructs a client, wires its callbacks, and places the **outbound** call.
|
|
182
|
+
* Brings BOTH hearing and the agent's voice online. Resolves the platform-native Call SID.
|
|
183
|
+
*
|
|
184
|
+
* @param toNumber The destination phone number to dial.
|
|
185
|
+
* @param fromNumber The agent's caller-id / DID the call originates from.
|
|
186
|
+
* @param args Provider-specific dial parameters.
|
|
187
|
+
* @returns The Twilio Call SID for the placed call.
|
|
188
|
+
* @throws When the native module specifier is missing or the adapter can't be loaded.
|
|
189
|
+
*/
|
|
190
|
+
dial(toNumber: string, fromNumber: string, args?: Record<string, unknown>): Promise<string>;
|
|
191
|
+
/**
|
|
192
|
+
* Loads the native adapter, constructs a client, wires its callbacks, and **answers** an inbound call
|
|
193
|
+
* already delivered to the agent's number. Brings BOTH hearing and the agent's voice online.
|
|
194
|
+
*
|
|
195
|
+
* @param callId The platform-native identifier of the inbound call to answer.
|
|
196
|
+
* @throws When the native module specifier is missing or the adapter can't be loaded.
|
|
197
|
+
*/
|
|
198
|
+
answer(callId: string): Promise<void>;
|
|
199
|
+
/**
|
|
200
|
+
* Hangs up the call via the native client and clears the active call. Tolerant of teardown errors.
|
|
201
|
+
*
|
|
202
|
+
* @param callId The platform-native identifier of the call to end.
|
|
203
|
+
*/
|
|
204
|
+
hangup(callId: string): Promise<void>;
|
|
205
|
+
/**
|
|
206
|
+
* Sends one raw PCM frame as the agent's outbound voice into the call — **the agent's real voice on
|
|
207
|
+
* the call**, via the native Media-Streams send path. No-ops (without throwing) before a call exists
|
|
208
|
+
* so an early model frame never crashes the session.
|
|
209
|
+
*
|
|
210
|
+
* @param pcm The PCM audio bytes to speak into the call.
|
|
211
|
+
*/
|
|
212
|
+
sendAudioFrame(pcm: ArrayBuffer): void;
|
|
213
|
+
/**
|
|
214
|
+
* Registers the inbound audio handler (the agent's hearing path). Wires it to the live client when a
|
|
215
|
+
* call already exists; otherwise it is (re-)registered at {@link bindCall} when the call comes online.
|
|
216
|
+
*
|
|
217
|
+
* @param cb Invoked with each inbound PCM audio frame.
|
|
218
|
+
*/
|
|
219
|
+
onAudioFrame(cb: (pcm: ArrayBuffer) => void): void;
|
|
220
|
+
/**
|
|
221
|
+
* Sends DTMF touch-tones on the call via the native client. No-ops (without throwing) before a call
|
|
222
|
+
* exists.
|
|
223
|
+
*
|
|
224
|
+
* @param digits The DTMF digit string to send.
|
|
225
|
+
*/
|
|
226
|
+
sendDtmf(digits: string): Promise<void>;
|
|
227
|
+
/**
|
|
228
|
+
* Registers the inbound DTMF handler. Wires it to the live client when a call already exists; otherwise
|
|
229
|
+
* it is (re-)registered at {@link bindCall} when the call comes online.
|
|
230
|
+
*
|
|
231
|
+
* @param cb Invoked with each received DTMF digit string.
|
|
232
|
+
*/
|
|
233
|
+
onDtmf(cb: (digits: string) => void): void;
|
|
234
|
+
/**
|
|
235
|
+
* Transfers the live call to another party via the native client. No-ops (without throwing) before a
|
|
236
|
+
* call exists.
|
|
237
|
+
*
|
|
238
|
+
* @param callId The platform-native identifier of the call to transfer.
|
|
239
|
+
* @param toNumber The transfer destination.
|
|
240
|
+
*/
|
|
241
|
+
transfer(callId: string, toNumber: string): Promise<void>;
|
|
242
|
+
/**
|
|
243
|
+
* Registers the call-ended handler. Wires it to the live client when a call already exists; otherwise
|
|
244
|
+
* it is (re-)registered at {@link bindCall} when the call comes online.
|
|
245
|
+
*
|
|
246
|
+
* @param cb Invoked when the call ends.
|
|
247
|
+
*/
|
|
248
|
+
onCallEnded(cb: () => void): void;
|
|
249
|
+
/**
|
|
250
|
+
* Lazily loads the native adapter and constructs the client (once), throwing an actionable error when
|
|
251
|
+
* the required {@link TwilioNativeSdkConfig.NativeModuleSpecifier} is absent.
|
|
252
|
+
*/
|
|
253
|
+
private ensureClient;
|
|
254
|
+
/** Binds the active Call SID and (re-)registers any handlers the bridge set before the call existed. */
|
|
255
|
+
private bindCall;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Builds the {@link import('@memberjunction/ai-bridge-base').TelephonyCallSdkFactory}-shaped factory that
|
|
259
|
+
* constructs a {@link TwilioNativeCallSdk} from the bridge's per-session `Configuration`. Pass the result
|
|
260
|
+
* to `TwilioBridge.SetSdkFactory(...)` so a deployment activates **two-way native Twilio audio** without
|
|
261
|
+
* code changes to the driver.
|
|
262
|
+
*
|
|
263
|
+
* Reads Twilio credentials + stream URL + the native module specifier out of the config map the engine
|
|
264
|
+
* passes at connect (credentials resolved upstream — **never inline secrets**).
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* // Where bridge drivers are configured (creds already resolved from the MJ credential system):
|
|
268
|
+
* bridge.SetSdkFactory(BindTwilioNativeCall());
|
|
269
|
+
*
|
|
270
|
+
* @param loadModule Optional native-adapter loader override (tests inject a fake; production omits it).
|
|
271
|
+
* @returns A factory `(config) => TwilioNativeCallSdk`.
|
|
272
|
+
*/
|
|
273
|
+
export declare function BindTwilioNativeCall(loadModule?: NativeModuleLoader): (config?: Record<string, unknown>) => TwilioNativeCallSdk;
|
|
274
|
+
/**
|
|
275
|
+
* Extracts a {@link TwilioNativeSdkConfig} from the engine's loosely-typed `Configuration` map without
|
|
276
|
+
* ever widening to `any`. Each field is read + type-checked individually so a malformed config yields a
|
|
277
|
+
* clean, partially-resolved object (and {@link TwilioNativeCallSdk} then throws a precise error if the
|
|
278
|
+
* required specifier is absent) rather than a half-typed blob.
|
|
279
|
+
*/
|
|
280
|
+
export declare function readNativeConfig(config?: Record<string, unknown>): TwilioNativeSdkConfig;
|
|
281
|
+
//# sourceMappingURL=twilio-native-call-sdk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"twilio-native-call-sdk.d.ts","sourceRoot":"","sources":["../src/twilio-native-call-sdk.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `TwilioNativeCallSdk` — a **real, two-way** {@link ITelephonyCallSdk} binding over the
|
|
3
|
+
* native **Twilio Programmable Voice + Media Streams** path. Unlike the shipped, unbound
|
|
4
|
+
* {@link import('./twilio-call-sdk.js').TwilioCallSdk} (every operation throws "bind the real Twilio
|
|
5
|
+
* client"), this adapter actually carries the call: {@link TwilioNativeCallSdk.sendAudioFrame} forwards
|
|
6
|
+
* the agent's synthesized PCM onto the call's Media-Streams websocket (the agent's **voice** on the
|
|
7
|
+
* call), inbound Media-Streams audio maps to the seam's audio-frame callback (what the agent **hears**),
|
|
8
|
+
* and DTMF / transfer / hangup actually reach the native client.
|
|
9
|
+
*
|
|
10
|
+
* ## Why a native binding
|
|
11
|
+
* A phone call is a single-leg, bidirectional audio source. The native Twilio path has two halves — a
|
|
12
|
+
* **REST API** (place / hang-up / transfer / send-DTMF) and a **Media Streams** websocket (bidirectional
|
|
13
|
+
* realtime audio + inbound DTMF events). This adapter sits over both so a single binding gives the agent
|
|
14
|
+
* full-duplex audio plus the call controls the telephony seam declares.
|
|
15
|
+
*
|
|
16
|
+
* ## The native module seam (no SDK types leak)
|
|
17
|
+
* The `twilio` SDK is NOT imported here. Deployments wrap the REST + Media-Streams plumbing behind the
|
|
18
|
+
* small structural {@link NativeCallModule} surface declared below, and this adapter depends ONLY on that
|
|
19
|
+
* surface — none of `twilio`'s real types leak into `@memberjunction/ai-bridge-twilio`. The module is
|
|
20
|
+
* loaded **lazily** behind an injectable {@link NativeModuleLoader} so the package builds and unit-tests
|
|
21
|
+
* with **no `twilio` install and no network**. Tests inject a fake module; production points
|
|
22
|
+
* {@link TwilioNativeSdkConfig.NativeModuleSpecifier} at the real adapter package/path.
|
|
23
|
+
*
|
|
24
|
+
* ## Auth (deployment responsibility)
|
|
25
|
+
* The native client authenticates with the Twilio **Account SID + Auth Token** (or API key/secret) and a
|
|
26
|
+
* resolved Media-Streams websocket URL. These resolve **upstream** (MJ credential system / provider
|
|
27
|
+
* `Configuration`) and are **never inlined** at a call site — {@link readNativeConfig} reads already-
|
|
28
|
+
* resolved values out of the loose `Configuration` map the engine passes at connect.
|
|
29
|
+
*
|
|
30
|
+
* @module @memberjunction/ai-bridge-twilio
|
|
31
|
+
* @author MemberJunction.com
|
|
32
|
+
* @see {@link ITelephonyCallSdk} — the platform-agnostic telephony seam this binds.
|
|
33
|
+
*/
|
|
34
|
+
import { LogError } from '@memberjunction/core';
|
|
35
|
+
/**
|
|
36
|
+
* Copies arbitrary PCM bytes into a standalone `ArrayBuffer`, never aliasing the caller's buffer window.
|
|
37
|
+
* A `Uint8Array` view may be a slice of a larger backing buffer (offset/length), so a naive `.buffer`
|
|
38
|
+
* grab would leak neighbouring audio; this copies exactly the view's bytes. Defined locally because the
|
|
39
|
+
* Twilio package has no shared coercion helper to reuse (unlike the Zoom package's RTMS binding).
|
|
40
|
+
*/
|
|
41
|
+
export function toArrayBuffer(data) {
|
|
42
|
+
if (data instanceof ArrayBuffer) {
|
|
43
|
+
return data.slice(0);
|
|
44
|
+
}
|
|
45
|
+
const copy = new Uint8Array(data.byteLength);
|
|
46
|
+
copy.set(data);
|
|
47
|
+
return copy.buffer;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* **Pure mapping** of one native inbound audio frame onto the raw PCM `ArrayBuffer` the telephony seam's
|
|
51
|
+
* audio callback delivers. Copies the bytes (see {@link toArrayBuffer}). Isolated from I/O for direct
|
|
52
|
+
* unit testing. (The telephony seam carries no per-speaker label — a 1:1 call has one remote party.)
|
|
53
|
+
*/
|
|
54
|
+
export function mapNativeAudioFrame(frame) {
|
|
55
|
+
return toArrayBuffer(frame.data);
|
|
56
|
+
}
|
|
57
|
+
/** Structural guard: a value is a {@link NativeCallModule} when it exposes a `createClient` function. */
|
|
58
|
+
function isNativeModule(value) {
|
|
59
|
+
return (value != null &&
|
|
60
|
+
typeof value === 'object' &&
|
|
61
|
+
typeof value.createClient === 'function');
|
|
62
|
+
}
|
|
63
|
+
/** Unwraps a `{ default }` interop wrapper (CJS-with-default), returning the inner value or the input. */
|
|
64
|
+
function unwrapDefault(mod) {
|
|
65
|
+
if (mod && typeof mod === 'object' && 'default' in mod) {
|
|
66
|
+
const inner = mod.default;
|
|
67
|
+
if (isNativeModule(inner)) {
|
|
68
|
+
return inner;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return mod;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Lazily loads the native Twilio adapter at the given specifier (category: runtime plugin discovery from
|
|
75
|
+
* config — the adapter path is deployment-supplied and not known at build time, so a static import is
|
|
76
|
+
* impossible). Throws a precise, actionable error when it can't be loaded so a misconfigured deployment
|
|
77
|
+
* fails loudly, not silently.
|
|
78
|
+
*
|
|
79
|
+
* VERIFY against the native Twilio adapter: the module's default/namespace interop + that it exposes
|
|
80
|
+
* `createClient`.
|
|
81
|
+
*/
|
|
82
|
+
export const defaultNativeLoader = async (specifier) => {
|
|
83
|
+
try {
|
|
84
|
+
const mod = await import(/* @vite-ignore */ specifier);
|
|
85
|
+
const resolved = unwrapDefault(mod);
|
|
86
|
+
if (!isNativeModule(resolved)) {
|
|
87
|
+
throw new Error('resolved module has no createClient() factory');
|
|
88
|
+
}
|
|
89
|
+
return resolved;
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
93
|
+
throw new Error(`TwilioNativeCallSdk could not load the native Twilio adapter at '${specifier}'. Build/install the ` +
|
|
94
|
+
'native Twilio Programmable Voice + Media Streams adapter (REST + websocket) and set ' +
|
|
95
|
+
`TwilioNativeSdkConfig.NativeModuleSpecifier to it. Underlying error: ${message}`);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* A **real, two-way** {@link ITelephonyCallSdk} over the native Twilio Programmable Voice + Media Streams
|
|
100
|
+
* path.
|
|
101
|
+
*
|
|
102
|
+
* Gives the agent both **hearing** (inbound Media-Streams audio → the seam's audio callback) and a
|
|
103
|
+
* **voice** ({@link sendAudioFrame} → the native Media-Streams send path), plus working DTMF, transfer,
|
|
104
|
+
* and hangup. Construct via {@link BindTwilioNativeCall} (the factory the bridge's `SetSdkFactory` wants),
|
|
105
|
+
* not directly, so config resolution + the lazy loader wire consistently.
|
|
106
|
+
*
|
|
107
|
+
* The adapter tracks the active Call SID so per-call REST/websocket operations can be addressed, and
|
|
108
|
+
* re-registers any handlers the bridge set before the call existed once a call comes online.
|
|
109
|
+
*/
|
|
110
|
+
export class TwilioNativeCallSdk {
|
|
111
|
+
/**
|
|
112
|
+
* @param config Resolved credentials + stream URL + the native module specifier.
|
|
113
|
+
* @param loadModule The native-adapter loader (defaults to the lazy specifier loader).
|
|
114
|
+
*/
|
|
115
|
+
constructor(config, loadModule = defaultNativeLoader) {
|
|
116
|
+
/** The live native client once {@link dial}/{@link answer} brings a call online. */
|
|
117
|
+
this.client = null;
|
|
118
|
+
/** The active call's SID, so per-call operations can address it. */
|
|
119
|
+
this.activeCallId = null;
|
|
120
|
+
this.config = config;
|
|
121
|
+
this.loadModule = loadModule;
|
|
122
|
+
}
|
|
123
|
+
// ── ITelephonyCallSdk — lifecycle ────────────────────────────────────────────────
|
|
124
|
+
/**
|
|
125
|
+
* Loads the native adapter, constructs a client, wires its callbacks, and places the **outbound** call.
|
|
126
|
+
* Brings BOTH hearing and the agent's voice online. Resolves the platform-native Call SID.
|
|
127
|
+
*
|
|
128
|
+
* @param toNumber The destination phone number to dial.
|
|
129
|
+
* @param fromNumber The agent's caller-id / DID the call originates from.
|
|
130
|
+
* @param args Provider-specific dial parameters.
|
|
131
|
+
* @returns The Twilio Call SID for the placed call.
|
|
132
|
+
* @throws When the native module specifier is missing or the adapter can't be loaded.
|
|
133
|
+
*/
|
|
134
|
+
async dial(toNumber, fromNumber, args) {
|
|
135
|
+
const client = await this.ensureClient();
|
|
136
|
+
const callSid = await client.dial({ toNumber, fromNumber, extra: args });
|
|
137
|
+
this.bindCall(callSid);
|
|
138
|
+
return callSid;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Loads the native adapter, constructs a client, wires its callbacks, and **answers** an inbound call
|
|
142
|
+
* already delivered to the agent's number. Brings BOTH hearing and the agent's voice online.
|
|
143
|
+
*
|
|
144
|
+
* @param callId The platform-native identifier of the inbound call to answer.
|
|
145
|
+
* @throws When the native module specifier is missing or the adapter can't be loaded.
|
|
146
|
+
*/
|
|
147
|
+
async answer(callId) {
|
|
148
|
+
const client = await this.ensureClient();
|
|
149
|
+
await client.answer(callId);
|
|
150
|
+
this.bindCall(callId);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Hangs up the call via the native client and clears the active call. Tolerant of teardown errors.
|
|
154
|
+
*
|
|
155
|
+
* @param callId The platform-native identifier of the call to end.
|
|
156
|
+
*/
|
|
157
|
+
async hangup(callId) {
|
|
158
|
+
const client = this.client;
|
|
159
|
+
this.activeCallId = null;
|
|
160
|
+
if (client) {
|
|
161
|
+
try {
|
|
162
|
+
await client.hangup(callId);
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
LogError(`[TwilioNativeCallSdk] hangup() failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// ── ITelephonyCallSdk — two-way audio (real) ─────────────────────────────────────
|
|
170
|
+
/**
|
|
171
|
+
* Sends one raw PCM frame as the agent's outbound voice into the call — **the agent's real voice on
|
|
172
|
+
* the call**, via the native Media-Streams send path. No-ops (without throwing) before a call exists
|
|
173
|
+
* so an early model frame never crashes the session.
|
|
174
|
+
*
|
|
175
|
+
* @param pcm The PCM audio bytes to speak into the call.
|
|
176
|
+
*/
|
|
177
|
+
sendAudioFrame(pcm) {
|
|
178
|
+
this.client?.sendAudioFrame(pcm);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Registers the inbound audio handler (the agent's hearing path). Wires it to the live client when a
|
|
182
|
+
* call already exists; otherwise it is (re-)registered at {@link bindCall} when the call comes online.
|
|
183
|
+
*
|
|
184
|
+
* @param cb Invoked with each inbound PCM audio frame.
|
|
185
|
+
*/
|
|
186
|
+
onAudioFrame(cb) {
|
|
187
|
+
this.audioHandler = cb;
|
|
188
|
+
this.client?.onAudioFrame((frame) => cb(mapNativeAudioFrame(frame)));
|
|
189
|
+
}
|
|
190
|
+
// ── ITelephonyCallSdk — DTMF, transfer (real) ────────────────────────────────────
|
|
191
|
+
/**
|
|
192
|
+
* Sends DTMF touch-tones on the call via the native client. No-ops (without throwing) before a call
|
|
193
|
+
* exists.
|
|
194
|
+
*
|
|
195
|
+
* @param digits The DTMF digit string to send.
|
|
196
|
+
*/
|
|
197
|
+
async sendDtmf(digits) {
|
|
198
|
+
await this.client?.sendDtmf(digits);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Registers the inbound DTMF handler. Wires it to the live client when a call already exists; otherwise
|
|
202
|
+
* it is (re-)registered at {@link bindCall} when the call comes online.
|
|
203
|
+
*
|
|
204
|
+
* @param cb Invoked with each received DTMF digit string.
|
|
205
|
+
*/
|
|
206
|
+
onDtmf(cb) {
|
|
207
|
+
this.dtmfHandler = cb;
|
|
208
|
+
this.client?.onDtmf(cb);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Transfers the live call to another party via the native client. No-ops (without throwing) before a
|
|
212
|
+
* call exists.
|
|
213
|
+
*
|
|
214
|
+
* @param callId The platform-native identifier of the call to transfer.
|
|
215
|
+
* @param toNumber The transfer destination.
|
|
216
|
+
*/
|
|
217
|
+
async transfer(callId, toNumber) {
|
|
218
|
+
await this.client?.transfer(callId, toNumber);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Registers the call-ended handler. Wires it to the live client when a call already exists; otherwise
|
|
222
|
+
* it is (re-)registered at {@link bindCall} when the call comes online.
|
|
223
|
+
*
|
|
224
|
+
* @param cb Invoked when the call ends.
|
|
225
|
+
*/
|
|
226
|
+
onCallEnded(cb) {
|
|
227
|
+
this.endedHandler = cb;
|
|
228
|
+
this.client?.onCallEnded(cb);
|
|
229
|
+
}
|
|
230
|
+
// ── internals ────────────────────────────────────────────────────────────────────
|
|
231
|
+
/**
|
|
232
|
+
* Lazily loads the native adapter and constructs the client (once), throwing an actionable error when
|
|
233
|
+
* the required {@link TwilioNativeSdkConfig.NativeModuleSpecifier} is absent.
|
|
234
|
+
*/
|
|
235
|
+
async ensureClient() {
|
|
236
|
+
if (this.client) {
|
|
237
|
+
return this.client;
|
|
238
|
+
}
|
|
239
|
+
const specifier = this.config.NativeModuleSpecifier;
|
|
240
|
+
if (!specifier) {
|
|
241
|
+
throw new Error('TwilioNativeCallSdk: no NativeModuleSpecifier configured. Set it to the native Twilio Programmable ' +
|
|
242
|
+
'Voice + Media Streams adapter (REST + websocket) in the session Configuration. See the README.');
|
|
243
|
+
}
|
|
244
|
+
const mod = await this.loadModule(specifier);
|
|
245
|
+
this.client = mod.createClient({
|
|
246
|
+
AccountSid: this.config.AccountSid,
|
|
247
|
+
AuthToken: this.config.AuthToken,
|
|
248
|
+
ApiKeySid: this.config.ApiKeySid,
|
|
249
|
+
ApiKeySecret: this.config.ApiKeySecret,
|
|
250
|
+
StreamUrl: this.config.StreamUrl,
|
|
251
|
+
});
|
|
252
|
+
return this.client;
|
|
253
|
+
}
|
|
254
|
+
/** Binds the active Call SID and (re-)registers any handlers the bridge set before the call existed. */
|
|
255
|
+
bindCall(callId) {
|
|
256
|
+
this.activeCallId = callId;
|
|
257
|
+
const client = this.client;
|
|
258
|
+
if (!client) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (this.audioHandler) {
|
|
262
|
+
const cb = this.audioHandler;
|
|
263
|
+
client.onAudioFrame((frame) => cb(mapNativeAudioFrame(frame)));
|
|
264
|
+
}
|
|
265
|
+
if (this.dtmfHandler) {
|
|
266
|
+
client.onDtmf(this.dtmfHandler);
|
|
267
|
+
}
|
|
268
|
+
if (this.endedHandler) {
|
|
269
|
+
client.onCallEnded(this.endedHandler);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Builds the {@link import('@memberjunction/ai-bridge-base').TelephonyCallSdkFactory}-shaped factory that
|
|
275
|
+
* constructs a {@link TwilioNativeCallSdk} from the bridge's per-session `Configuration`. Pass the result
|
|
276
|
+
* to `TwilioBridge.SetSdkFactory(...)` so a deployment activates **two-way native Twilio audio** without
|
|
277
|
+
* code changes to the driver.
|
|
278
|
+
*
|
|
279
|
+
* Reads Twilio credentials + stream URL + the native module specifier out of the config map the engine
|
|
280
|
+
* passes at connect (credentials resolved upstream — **never inline secrets**).
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* // Where bridge drivers are configured (creds already resolved from the MJ credential system):
|
|
284
|
+
* bridge.SetSdkFactory(BindTwilioNativeCall());
|
|
285
|
+
*
|
|
286
|
+
* @param loadModule Optional native-adapter loader override (tests inject a fake; production omits it).
|
|
287
|
+
* @returns A factory `(config) => TwilioNativeCallSdk`.
|
|
288
|
+
*/
|
|
289
|
+
export function BindTwilioNativeCall(loadModule = defaultNativeLoader) {
|
|
290
|
+
return (config) => new TwilioNativeCallSdk(readNativeConfig(config), loadModule);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Extracts a {@link TwilioNativeSdkConfig} from the engine's loosely-typed `Configuration` map without
|
|
294
|
+
* ever widening to `any`. Each field is read + type-checked individually so a malformed config yields a
|
|
295
|
+
* clean, partially-resolved object (and {@link TwilioNativeCallSdk} then throws a precise error if the
|
|
296
|
+
* required specifier is absent) rather than a half-typed blob.
|
|
297
|
+
*/
|
|
298
|
+
export function readNativeConfig(config) {
|
|
299
|
+
const cfg = config ?? {};
|
|
300
|
+
return {
|
|
301
|
+
AccountSid: readString(cfg.AccountSid),
|
|
302
|
+
AuthToken: readString(cfg.AuthToken),
|
|
303
|
+
ApiKeySid: readString(cfg.ApiKeySid),
|
|
304
|
+
ApiKeySecret: readString(cfg.ApiKeySecret),
|
|
305
|
+
StreamUrl: readString(cfg.StreamUrl),
|
|
306
|
+
NativeModuleSpecifier: readString(cfg.NativeModuleSpecifier),
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
/** Reads a value as a non-empty string, or `undefined`. */
|
|
310
|
+
function readString(value) {
|
|
311
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=twilio-native-call-sdk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"twilio-native-call-sdk.js","sourceRoot":"","sources":["../src/twilio-native-call-sdk.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ai-bridge-twilio",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.42.0",
|
|
5
5
|
"description": "MemberJunction: Twilio Realtime Bridge driver (telephony). Connects the realtime agent engine to a phone call over Twilio Programmable Voice + Media Streams — outbound dial, inbound DID routing, single-party audio in/out, DTMF send/receive, and call transfer — via an injectable telephony call SDK seam. Audio only: no video, no screen, no Meeting Controls.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
"author": "MemberJunction.com",
|
|
18
18
|
"license": "ISC",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@memberjunction/core": "5.
|
|
21
|
-
"@memberjunction/global": "5.
|
|
22
|
-
"@memberjunction/core-entities": "5.
|
|
23
|
-
"@memberjunction/ai-bridge-base": "5.
|
|
20
|
+
"@memberjunction/core": "5.42.0",
|
|
21
|
+
"@memberjunction/global": "5.42.0",
|
|
22
|
+
"@memberjunction/core-entities": "5.42.0",
|
|
23
|
+
"@memberjunction/ai-bridge-base": "5.42.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/node": "24.10.11",
|