getpatter 0.4.4 → 0.5.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  <p align="center">
2
2
  <h1 align="center">Patter TypeScript SDK</h1>
3
- <p align="center">Connect AI agents to phone numbers with 4 lines of code</p>
3
+ <p align="center">Connect AI agents to phone numbers in four lines of code</p>
4
4
  </p>
5
5
 
6
6
  <p align="center">
@@ -28,52 +28,63 @@ Patter is the open-source SDK that gives your AI agent a phone number. Point it
28
28
  npm install getpatter
29
29
  ```
30
30
 
31
- ```typescript
32
- import { Patter } from "getpatter";
31
+ Set the env vars your carrier and engine need:
33
32
 
34
- const phone = new Patter({
35
- twilioSid: "AC...", twilioToken: "...",
36
- openaiKey: "sk-...",
37
- phoneNumber: "+1...",
38
- });
33
+ ```bash
34
+ export TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
35
+ export TWILIO_AUTH_TOKEN=your_auth_token
36
+ export OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
37
+ ```
39
38
 
40
- const agent = phone.agent({
41
- systemPrompt: "You are a friendly customer service agent for Acme Corp.",
42
- voice: "alloy",
43
- firstMessage: "Hello! Thanks for calling. How can I help?",
44
- });
39
+ Four lines of TypeScript:
45
40
 
46
- await phone.serve({ agent, port: 8000 });
41
+ ```typescript
42
+ import { Patter, Twilio, OpenAIRealtime } from "getpatter";
43
+
44
+ const phone = new Patter({ carrier: new Twilio(), phoneNumber: "+15550001234" });
45
+ const agent = phone.agent({ engine: new OpenAIRealtime(), systemPrompt: "You are a friendly receptionist for Acme Corp.", firstMessage: "Hello! How can I help?" });
46
+ await phone.serve({ agent, tunnel: true });
47
47
  ```
48
48
 
49
+ `tunnel: true` spawns a Cloudflare tunnel and points your Twilio number at it. In production, pass `webhookUrl: "api.prod.example.com"` to the constructor instead.
50
+
49
51
  ## Features
50
52
 
51
53
  | Feature | Method | Example |
52
54
  |---|---|---|
53
- | Inbound calls | `phone.serve(agent)` | Answer calls as an AI |
54
- | Outbound calls + AMD | `phone.call(to, machineDetection)` | Place calls with voicemail detection |
55
- | Tool calling (webhooks) | `agent(tools=[...])` | Agent calls external APIs mid-conversation |
56
- | Custom STT + TTS | `agent(provider="pipeline")` | Bring your own voice providers |
57
- | Dynamic variables | `agent(variables={...})` | Personalize prompts per caller |
58
- | Custom LLM (any model) | `serve(onMessage=handler)` | Claude, Mistral, LLaMA, etc. |
59
- | Call recording | `serve(recording=true)` | Record all calls |
55
+ | Inbound calls | `phone.serve({ agent })` | Answer calls as an AI |
56
+ | Outbound calls + AMD | `phone.call({ to, machineDetection: true })` | Place calls with voicemail detection |
57
+ | Tool calling | `agent({ tools: [tool(...)] })` | Agent calls external APIs mid-conversation |
58
+ | Custom STT + TTS | `agent({ stt: new DeepgramSTT(), tts: new ElevenLabsTTS() })` | Bring your own voice providers |
59
+ | Dynamic variables | `agent({ variables: {...} })` | Personalize prompts per caller |
60
+ | Pluggable LLM | `agent({ llm: new AnthropicLLM() })` | 5 built-in providers: OpenAI, Anthropic, Groq, Cerebras, Google |
61
+ | Custom LLM (any model) | `serve({ onMessage })` | Route to anything — local inference, internal gateways, etc. |
62
+ | Call recording | `serve({ recording: true })` | Record all calls |
60
63
  | Call transfer | `transfer_call` (auto-injected) | Transfer to a human |
61
- | Voicemail drop | `call(voicemailMessage="...")` | Play message on voicemail |
64
+ | Voicemail drop | `call({ voicemailMessage: "..." })` | Play message on voicemail |
62
65
 
63
66
  ## Configuration
64
67
 
65
- ### Environment Variables
66
-
67
- | Variable | Required | Description |
68
- |---|---|---|
69
- | `OPENAI_API_KEY` | Yes (Realtime mode) | OpenAI API key with Realtime access |
70
- | `TWILIO_ACCOUNT_SID` | Yes | Twilio account SID |
71
- | `TWILIO_AUTH_TOKEN` | Yes | Twilio auth token |
72
- | `TWILIO_PHONE_NUMBER` | Yes | Your Twilio phone number (E.164) |
73
- | `DEEPGRAM_API_KEY` | Pipeline mode | Deepgram STT key |
74
- | `ELEVENLABS_API_KEY` | Pipeline mode | ElevenLabs TTS key |
75
- | `ANTHROPIC_API_KEY` | Custom LLM | For bringing your own model |
76
- | `WEBHOOK_URL` | No | Public URL (auto-tunneled via Cloudflare if omitted) |
68
+ ### Environment variables
69
+
70
+ Every provider reads its credentials from the environment by default. Pass `apiKey: "..."` to any constructor to override.
71
+
72
+ | Variable | Used by |
73
+ |---|---|
74
+ | `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN` | `new Twilio()` carrier |
75
+ | `TELNYX_API_KEY`, `TELNYX_CONNECTION_ID`, `TELNYX_PUBLIC_KEY` (optional) | `new Telnyx()` carrier |
76
+ | `OPENAI_API_KEY` | `OpenAIRealtime`, `WhisperSTT`, `OpenAITTS` |
77
+ | `ELEVENLABS_API_KEY`, `ELEVENLABS_AGENT_ID` | `ElevenLabsConvAI`, `ElevenLabsTTS` |
78
+ | `DEEPGRAM_API_KEY` | `DeepgramSTT` |
79
+ | `CARTESIA_API_KEY` | `CartesiaSTT`, `CartesiaTTS` |
80
+ | `RIME_API_KEY` | `RimeTTS` |
81
+ | `LMNT_API_KEY` | `LMNTTTS` |
82
+ | `SONIOX_API_KEY` | `SonioxSTT` |
83
+ | `ASSEMBLYAI_API_KEY` | `AssemblyAISTT` |
84
+ | `ANTHROPIC_API_KEY` | `AnthropicLLM` |
85
+ | `GROQ_API_KEY` | `GroqLLM` |
86
+ | `CEREBRAS_API_KEY` | `CerebrasLLM` |
87
+ | `GEMINI_API_KEY` (or `GOOGLE_API_KEY`) | `GoogleLLM` |
77
88
 
78
89
  ```bash
79
90
  cp .env.example .env
@@ -87,219 +98,206 @@ cp .env.example .env
87
98
  | Mode | Latency | Quality | Best For |
88
99
  |---|---|---|---|
89
100
  | **OpenAI Realtime** | Lowest | High | Fluid, low-latency conversations |
90
- | **Deepgram + ElevenLabs** | Low | High | Independent control over STT and TTS |
101
+ | **Pipeline** (STT + LLM + TTS) | Low | High | Independent control over STT and TTS |
91
102
  | **ElevenLabs ConvAI** | Low | High | ElevenLabs-managed conversation flow |
92
103
 
93
104
  ## API Reference
94
105
 
95
- ### `Patter` Constructor
106
+ ### `Patter` constructor
96
107
 
97
108
  ```typescript
98
109
  new Patter({
99
- twilioSid: string;
100
- twilioToken: string;
101
- openaiKey: string;
110
+ carrier: Twilio | Telnyx;
102
111
  phoneNumber: string;
103
- webhookUrl?: string; // Optional; auto-tunneled via Cloudflare if omitted
112
+ webhookUrl?: string; // Public hostname. Mutually exclusive with tunnel.
113
+ tunnel?: CloudflareTunnel | StaticTunnel; // Or pass tunnel: true on serve() for dev.
104
114
  })
105
115
  ```
106
116
 
107
117
  | Parameter | Type | Description |
108
118
  |---|---|---|
109
- | `twilioSid` | `string` | Twilio account SID |
110
- | `twilioToken` | `string` | Twilio auth token |
111
- | `openaiKey` | `string` | OpenAI API key |
112
- | `phoneNumber` | `string` | Your Twilio phone number (E.164 format) |
113
- | `webhookUrl` | `string` | Public URL for Twilio webhooks (optional) |
119
+ | `carrier` | `Twilio` / `Telnyx` | Carrier instance. Reads env vars by default. |
120
+ | `phoneNumber` | `string` | Your phone number in E.164 format. |
121
+ | `webhookUrl` | `string` | Public hostname your local server is reachable on. |
122
+ | `tunnel` | instance | `new CloudflareTunnel()` or `new StaticTunnel({ hostname: ... })`. |
114
123
 
115
- ### `phone.agent()` Method
124
+ ### `phone.agent()`
116
125
 
117
126
  ```typescript
118
127
  phone.agent({
119
128
  systemPrompt: string;
129
+ engine?: OpenAIRealtime | ElevenLabsConvAI; // default: new OpenAIRealtime()
130
+ stt?: STTProvider; // e.g. new DeepgramSTT()
131
+ tts?: TTSProvider; // e.g. new ElevenLabsTTS()
120
132
  voice?: string;
133
+ model?: string;
134
+ language?: string;
121
135
  firstMessage?: string;
136
+ tools?: Tool[];
137
+ guardrails?: Guardrail[];
122
138
  variables?: Record<string, string>;
123
- tools?: Array<{name, description, parameters, webhookUrl}>;
124
139
  })
125
140
  ```
126
141
 
127
- | Parameter | Type | Description |
128
- |---|---|---|
129
- | `systemPrompt` | `string` | Prompt with optional `{variable}` placeholders |
130
- | `voice` | `string` | TTS voice name (e.g., "alloy", "echo", "fable") |
131
- | `firstMessage` | `string` | Opening message (supports `{variable}` placeholders) |
132
- | `variables` | `Record<string, string>` | Values substituted into prompts |
133
- | `tools` | `Array` | Tool definitions: `{name, description, parameters, webhookUrl}` |
142
+ Pass `engine` for end-to-end mode, `stt` + `tts` for pipeline mode. Both arguments may take plain adapter instances (e.g. `new DeepgramSTT()`) that read their API key from the environment.
134
143
 
135
- ### `phone.serve()` Method
144
+ ### `phone.serve()`
136
145
 
137
146
  ```typescript
138
147
  await phone.serve({
139
148
  agent: Agent;
140
149
  port?: number;
150
+ tunnel?: boolean; // shortcut for Patter({ tunnel: new CloudflareTunnel() })
141
151
  dashboard?: boolean;
142
152
  recording?: boolean;
143
- onCallStart?: (data: CallData) => Promise<void>;
144
- onCallEnd?: (data: CallData) => Promise<void>;
145
- onTranscript?: (data: TranscriptData) => Promise<void>;
146
- })
153
+ onCallStart?: (data) => Promise<void>;
154
+ onCallEnd?: (data) => Promise<void>;
155
+ onTranscript?: (data) => Promise<void>;
156
+ onMessage?: (data) => Promise<string> | string;
157
+ voicemailMessage?: string;
158
+ dashboardToken?: string;
159
+ });
147
160
  ```
148
161
 
149
- | Parameter | Type | Description |
150
- |---|---|---|
151
- | `agent` | `Agent` | Agent configuration to use for calls |
152
- | `port` | `number` | Port to listen on (default: 8000) |
153
- | `dashboard` | `boolean` | Enable the built-in monitoring dashboard |
154
- | `recording` | `boolean` | Enable call recording via the telephony provider |
155
- | `onCallStart` | `(data) => Promise<void>` | Called when a call connects; receives `data.caller`, `data.callId` |
156
- | `onCallEnd` | `(data) => Promise<void>` | Called when a call ends; receives `data.history`, `data.transcript`, `data.duration` |
157
- | `onTranscript` | `(data) => Promise<void>` | Called on each transcript turn; receives `data.role`, `data.text`, `data.history` |
158
-
159
- ### `phone.call()` Method
162
+ ### `phone.call()`
160
163
 
161
164
  ```typescript
162
165
  await phone.call({
163
166
  to: string;
167
+ agent?: Agent;
168
+ from?: string;
164
169
  firstMessage?: string;
165
170
  machineDetection?: boolean;
166
171
  voicemailMessage?: string;
167
- })
172
+ ringTimeout?: number;
173
+ });
168
174
  ```
169
175
 
170
- | Parameter | Type | Description |
171
- |---|---|---|
172
- | `to` | `string` | Destination phone number (E.164 format) |
173
- | `firstMessage` | `string` | Opening message for the outbound call |
174
- | `machineDetection` | `boolean` | Enable answering machine detection |
175
- | `voicemailMessage` | `string` | Message to play when voicemail is detected |
176
-
177
- ### Static Provider Helpers
176
+ ### STT / TTS catalog
178
177
 
179
178
  ```typescript
180
- Patter.deepgram(options: { apiKey: string; language?: string }) -> STT
181
- Patter.elevenlabs(options: { apiKey: string; voice?: string }) -> TTS
182
- Patter.openaiTts(options: { apiKey: string; voice?: string }) -> TTS
183
- Patter.whisper(options: { apiKey: string; language?: string }) -> STT
179
+ import {
180
+ // Carriers
181
+ Twilio, Telnyx,
182
+ // Engines
183
+ OpenAIRealtime, ElevenLabsConvAI,
184
+ // STT
185
+ DeepgramSTT, WhisperSTT, CartesiaSTT, SonioxSTT, AssemblyAISTT,
186
+ // TTS
187
+ ElevenLabsTTS, OpenAITTS, CartesiaTTS, RimeTTS, LMNTTTS,
188
+ // LLM
189
+ OpenAILLM, AnthropicLLM, GroqLLM, CerebrasLLM, GoogleLLM,
190
+ // Tunnels
191
+ CloudflareTunnel, StaticTunnel,
192
+ // Primitives
193
+ Tool, Guardrail, tool, guardrail,
194
+ } from "getpatter";
184
195
  ```
185
196
 
197
+ Every class reads its API key from the environment by default, so `new DeepgramSTT()` / `new ElevenLabsTTS()` work out of the box when the corresponding env var is set.
198
+
186
199
  ## Examples
187
200
 
188
- ### Inbound Calls (AI answers the phone)
201
+ ### Inbound calls default engine
189
202
 
190
203
  ```typescript
191
- import { Patter, IncomingMessage } from "getpatter";
204
+ import { Patter, Twilio, OpenAIRealtime } from "getpatter";
192
205
 
193
- const phone = new Patter({
194
- twilioSid: "AC...", twilioToken: "...",
195
- openaiKey: "sk-...",
196
- phoneNumber: "+1...",
206
+ const phone = new Patter({ carrier: new Twilio(), phoneNumber: "+15550001234" });
207
+ const agent = phone.agent({
208
+ engine: new OpenAIRealtime(),
209
+ systemPrompt: "You are a helpful customer service agent.",
210
+ firstMessage: "Hello! How can I help?",
197
211
  });
198
212
 
199
- async function agent(msg: IncomingMessage): Promise<string> {
200
- if (msg.text.toLowerCase().includes("hours")) {
201
- return "We're open Monday through Friday, 9 to 5.";
202
- }
203
- return "How can I help you today?";
204
- }
205
-
206
213
  await phone.serve({
207
- agent: phone.agent({
208
- systemPrompt: "You are a helpful customer service agent.",
209
- firstMessage: "Hello! How can I help?",
210
- }),
211
- port: 8000,
214
+ agent,
215
+ tunnel: true,
212
216
  onCallStart: (data) => console.log(`Call from ${data.caller}`),
213
- onCallEnd: (data) => console.log("Call ended"),
217
+ onCallEnd: () => console.log("Call ended"),
214
218
  });
215
219
  ```
216
220
 
217
- ### Outbound Calls (AI calls someone)
221
+ ### Custom voice Deepgram STT + ElevenLabs TTS
218
222
 
219
223
  ```typescript
220
- import { Patter } from "getpatter";
221
-
222
- const phone = new Patter({
223
- twilioSid: "AC...", twilioToken: "...",
224
- openaiKey: "sk-...",
225
- phoneNumber: "+1...",
226
- });
227
-
228
- const agentConfig = phone.agent({
229
- systemPrompt: "You are making reminder calls.",
230
- firstMessage: "Hi, this is an automated reminder from Acme Corp.",
231
- });
224
+ import { Patter, Twilio, DeepgramSTT, ElevenLabsTTS } from "getpatter";
232
225
 
233
- await phone.serve({ agent: agentConfig, port: 8000 });
234
- await phone.call({
235
- to: "+14155551234",
236
- firstMessage: "Hi, just checking in.",
226
+ const phone = new Patter({ carrier: new Twilio(), phoneNumber: "+15550001234" });
227
+ const agent = phone.agent({
228
+ stt: new DeepgramSTT(), // reads DEEPGRAM_API_KEY
229
+ tts: new ElevenLabsTTS({ voice: "rachel" }), // reads ELEVENLABS_API_KEY
230
+ systemPrompt: "You are a helpful voice assistant.",
237
231
  });
232
+ await phone.serve({ agent, tunnel: true });
238
233
  ```
239
234
 
240
- ### Tool Calling (Agent calls external APIs)
235
+ ### Pipeline mode pick STT, LLM, TTS independently
241
236
 
242
237
  ```typescript
238
+ import { Patter, Twilio, DeepgramSTT, AnthropicLLM, ElevenLabsTTS } from "getpatter";
239
+
240
+ const phone = new Patter({ carrier: new Twilio(), phoneNumber: "+15550001234" });
243
241
  const agent = phone.agent({
244
- systemPrompt: "You are a booking assistant. Check availability before confirming.",
245
- tools: [{
246
- name: "check_availability",
247
- description: "Check appointment availability for a given date",
248
- parameters: {
249
- type: "object",
250
- properties: {
251
- date: { type: "string", description: "ISO date, e.g. 2025-06-15" },
252
- },
253
- required: ["date"],
254
- },
255
- webhookUrl: "https://api.example.com/availability",
256
- }],
242
+ stt: new DeepgramSTT(), // reads DEEPGRAM_API_KEY
243
+ llm: new AnthropicLLM(), // reads ANTHROPIC_API_KEY
244
+ tts: new ElevenLabsTTS({ voiceId: "rachel" }), // reads ELEVENLABS_API_KEY
245
+ systemPrompt: "You are a helpful voice assistant.",
257
246
  });
247
+ await phone.serve({ agent, tunnel: true });
258
248
  ```
259
249
 
260
- ### Custom Voice (Deepgram STT + ElevenLabs TTS)
250
+ Available LLM providers: `OpenAILLM`, `AnthropicLLM`, `GroqLLM`, `CerebrasLLM`, `GoogleLLM`. Tool calling works across all five. For fully custom logic, drop `llm` and pass an `onMessage` callback to `serve()` instead.
251
+
252
+ ### Tool calling
261
253
 
262
254
  ```typescript
263
- const phone = new Patter({
264
- twilioSid: "AC...", twilioToken: "...",
265
- openaiKey: "sk-...",
266
- phoneNumber: "+1...",
255
+ import { Patter, Twilio, OpenAIRealtime, tool } from "getpatter";
256
+
257
+ const checkAvailability = tool({
258
+ name: "check_availability",
259
+ description: "Check appointment availability for a given ISO date.",
260
+ parameters: {
261
+ type: "object",
262
+ properties: { date: { type: "string" } },
263
+ required: ["date"],
264
+ },
265
+ handler: async ({ date }) => ({ available: true }),
267
266
  });
268
267
 
268
+ const phone = new Patter({ carrier: new Twilio(), phoneNumber: "+15550001234" });
269
269
  const agent = phone.agent({
270
- systemPrompt: "You are a helpful voice assistant.",
271
- voice: "aria",
272
- });
273
-
274
- // Use custom STT and TTS in pipeline mode
275
- await phone.serve({
276
- agent,
277
- port: 8000,
278
- stt: Patter.deepgram({ apiKey: "dg_...", language: "en" }),
279
- tts: Patter.elevenlabs({ apiKey: "el_...", voice: "aria" }),
270
+ engine: new OpenAIRealtime(),
271
+ systemPrompt: "You are a booking assistant.",
272
+ tools: [checkAvailability],
280
273
  });
274
+ await phone.serve({ agent, tunnel: true });
281
275
  ```
282
276
 
283
- ### Call Recording
277
+ ### Outbound calls
284
278
 
285
279
  ```typescript
286
- await phone.serve({
287
- agent,
288
- port: 8000,
289
- recording: true, // Records all inbound and outbound calls
280
+ import { Patter, Twilio, OpenAIRealtime } from "getpatter";
281
+
282
+ const phone = new Patter({ carrier: new Twilio(), phoneNumber: "+15550001234" });
283
+ const agent = phone.agent({
284
+ engine: new OpenAIRealtime(),
285
+ systemPrompt: "You are making reminder calls.",
286
+ firstMessage: "Hi, this is a reminder from Acme Corp.",
290
287
  });
288
+
289
+ await phone.serve({ agent, tunnel: true });
290
+ await phone.call({ to: "+14155551234", agent });
291
291
  ```
292
292
 
293
- ### Dynamic Variables in Prompts
293
+ ### Dynamic variables
294
294
 
295
295
  ```typescript
296
296
  const agent = phone.agent({
297
+ engine: new OpenAIRealtime(),
297
298
  systemPrompt: "You are helping {customer_name}, account #{account_id}.",
298
299
  firstMessage: "Hi {customer_name}! How can I help you today?",
299
- variables: {
300
- customer_name: "Jane",
301
- account_id: "A-789",
302
- },
300
+ variables: { customer_name: "Jane", account_id: "A-789" },
303
301
  });
304
302
  ```
305
303
 
@@ -0,0 +1,84 @@
1
+ import {
2
+ getLogger
3
+ } from "./chunk-FMNRCP5X.mjs";
4
+ import "./chunk-OOIUSZB4.mjs";
5
+
6
+ // src/carrier-config.ts
7
+ var TWILIO_API_BASE = "https://api.twilio.com/2010-04-01";
8
+ var TELNYX_API_BASE = "https://api.telnyx.com/v2";
9
+ async function configureTwilioNumber(accountSid, authToken, phoneNumber, voiceUrl) {
10
+ const auth = `Basic ${Buffer.from(`${accountSid}:${authToken}`).toString("base64")}`;
11
+ const listUrl = `${TWILIO_API_BASE}/Accounts/${accountSid}/IncomingPhoneNumbers.json?PhoneNumber=${encodeURIComponent(phoneNumber)}`;
12
+ const listResp = await fetch(listUrl, {
13
+ method: "GET",
14
+ headers: { Authorization: auth }
15
+ });
16
+ if (!listResp.ok) {
17
+ throw new Error(
18
+ `Twilio IncomingPhoneNumbers.list failed: ${listResp.status} ${await listResp.text()}`
19
+ );
20
+ }
21
+ const body = await listResp.json();
22
+ const match = body.incoming_phone_numbers?.[0];
23
+ if (!match) {
24
+ throw new Error(`Twilio number ${phoneNumber} not found on account ${accountSid}`);
25
+ }
26
+ const updateUrl = `${TWILIO_API_BASE}/Accounts/${accountSid}/IncomingPhoneNumbers/${match.sid}.json`;
27
+ const form = new URLSearchParams({ VoiceUrl: voiceUrl, VoiceMethod: "POST" });
28
+ const updateResp = await fetch(updateUrl, {
29
+ method: "POST",
30
+ headers: {
31
+ Authorization: auth,
32
+ "Content-Type": "application/x-www-form-urlencoded"
33
+ },
34
+ body: form.toString()
35
+ });
36
+ if (!updateResp.ok) {
37
+ throw new Error(
38
+ `Twilio IncomingPhoneNumbers.update failed: ${updateResp.status} ${await updateResp.text()}`
39
+ );
40
+ }
41
+ }
42
+ async function configureTelnyxNumber(apiKey, connectionId, phoneNumber) {
43
+ const resp = await fetch(`${TELNYX_API_BASE}/phone_numbers/${encodeURIComponent(phoneNumber)}`, {
44
+ method: "PATCH",
45
+ headers: {
46
+ Authorization: `Bearer ${apiKey}`,
47
+ "Content-Type": "application/json"
48
+ },
49
+ body: JSON.stringify({ connection_id: connectionId })
50
+ });
51
+ if (!resp.ok) {
52
+ throw new Error(
53
+ `Telnyx PATCH /phone_numbers/${phoneNumber} failed: ${resp.status} ${await resp.text()}`
54
+ );
55
+ }
56
+ }
57
+ async function autoConfigureCarrier(params) {
58
+ const log = getLogger();
59
+ const provider = params.telephonyProvider ?? (params.twilioSid ? "twilio" : "telnyx");
60
+ if (provider === "twilio" && params.twilioSid && params.twilioToken) {
61
+ const voiceUrl = `https://${params.webhookHost}/webhooks/twilio/voice`;
62
+ try {
63
+ await configureTwilioNumber(params.twilioSid, params.twilioToken, params.phoneNumber, voiceUrl);
64
+ log.info("Twilio webhook set to %s", voiceUrl);
65
+ } catch (err) {
66
+ log.warn("Could not auto-configure Twilio webhook: %s", err instanceof Error ? err.message : String(err));
67
+ log.info("Set webhook manually to: %s", voiceUrl);
68
+ }
69
+ return;
70
+ }
71
+ if (provider === "telnyx" && params.telnyxKey && params.telnyxConnectionId) {
72
+ try {
73
+ await configureTelnyxNumber(params.telnyxKey, params.telnyxConnectionId, params.phoneNumber);
74
+ log.info("Telnyx number %s associated with connection %s", params.phoneNumber, params.telnyxConnectionId);
75
+ } catch (err) {
76
+ log.warn("Could not auto-configure Telnyx number: %s", err instanceof Error ? err.message : String(err));
77
+ }
78
+ }
79
+ }
80
+ export {
81
+ autoConfigureCarrier,
82
+ configureTelnyxNumber,
83
+ configureTwilioNumber
84
+ };
@@ -7,7 +7,7 @@ var log = getLogger();
7
7
  async function startTunnel(port, timeoutMs = 3e4) {
8
8
  let tunnelMod;
9
9
  try {
10
- tunnelMod = await import("./lib-4WCAS54J.mjs");
10
+ tunnelMod = await import("cloudflared");
11
11
  } catch {
12
12
  throw new Error(
13
13
  'Built-in tunnel requires the "cloudflared" package. Install it with:\n\n npm install cloudflared\n\nOr provide your own webhookUrl instead of using tunnel: true.'