@veroai/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +291 -0
- package/dist/index.cjs +1946 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1569 -0
- package/dist/index.d.ts +1569 -0
- package/dist/index.js +1926 -0
- package/dist/index.js.map +1 -0
- package/package.json +81 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1926 @@
|
|
|
1
|
+
// src/utils/errors.ts
|
|
2
|
+
var VeroAIError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
details;
|
|
5
|
+
statusCode;
|
|
6
|
+
constructor(message, code = "UNKNOWN_ERROR", statusCode, details) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "VeroAIError";
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.statusCode = statusCode;
|
|
11
|
+
this.details = details;
|
|
12
|
+
if (Error.captureStackTrace) {
|
|
13
|
+
Error.captureStackTrace(this, this.constructor);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
toJSON() {
|
|
17
|
+
return {
|
|
18
|
+
code: this.code,
|
|
19
|
+
message: this.message,
|
|
20
|
+
details: this.details
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
var APIError = class extends VeroAIError {
|
|
25
|
+
response;
|
|
26
|
+
constructor(message, code, statusCode, details, response) {
|
|
27
|
+
super(message, code, statusCode, details);
|
|
28
|
+
this.name = "APIError";
|
|
29
|
+
this.response = response;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var AuthenticationError = class extends APIError {
|
|
33
|
+
constructor(message = "Invalid API key or token", details) {
|
|
34
|
+
super(message, "AUTHENTICATION_ERROR", 401, details);
|
|
35
|
+
this.name = "AuthenticationError";
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var AuthorizationError = class extends APIError {
|
|
39
|
+
constructor(message = "Insufficient permissions", details) {
|
|
40
|
+
super(message, "AUTHORIZATION_ERROR", 403, details);
|
|
41
|
+
this.name = "AuthorizationError";
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var NotFoundError = class extends APIError {
|
|
45
|
+
constructor(message = "Resource not found", details) {
|
|
46
|
+
super(message, "NOT_FOUND", 404, details);
|
|
47
|
+
this.name = "NotFoundError";
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var ValidationError = class extends APIError {
|
|
51
|
+
constructor(message = "Invalid request", details) {
|
|
52
|
+
super(message, "VALIDATION_ERROR", 400, details);
|
|
53
|
+
this.name = "ValidationError";
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var RateLimitError = class extends APIError {
|
|
57
|
+
retryAfter;
|
|
58
|
+
constructor(message = "Rate limit exceeded", retryAfter, details) {
|
|
59
|
+
super(message, "RATE_LIMIT_EXCEEDED", 429, details);
|
|
60
|
+
this.name = "RateLimitError";
|
|
61
|
+
this.retryAfter = retryAfter;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var ServerError = class extends APIError {
|
|
65
|
+
constructor(message = "Internal server error", statusCode = 500, details) {
|
|
66
|
+
super(message, "SERVER_ERROR", statusCode, details);
|
|
67
|
+
this.name = "ServerError";
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
var TimeoutError = class extends VeroAIError {
|
|
71
|
+
constructor(message = "Request timed out") {
|
|
72
|
+
super(message, "TIMEOUT_ERROR");
|
|
73
|
+
this.name = "TimeoutError";
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
var NetworkError = class extends VeroAIError {
|
|
77
|
+
constructor(message = "Network error", cause) {
|
|
78
|
+
super(message, "NETWORK_ERROR");
|
|
79
|
+
this.name = "NetworkError";
|
|
80
|
+
this.cause = cause;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// src/utils/http.ts
|
|
85
|
+
var HttpClient = class {
|
|
86
|
+
apiKey;
|
|
87
|
+
baseUrl;
|
|
88
|
+
timeout;
|
|
89
|
+
maxRetries;
|
|
90
|
+
fetchFn;
|
|
91
|
+
constructor(config) {
|
|
92
|
+
this.apiKey = config.apiKey;
|
|
93
|
+
this.baseUrl = config.baseUrl?.replace(/\/$/, "") || "https://api.veroai.dev";
|
|
94
|
+
this.timeout = config.timeout || 3e4;
|
|
95
|
+
this.maxRetries = config.maxRetries ?? 3;
|
|
96
|
+
this.fetchFn = config.fetch || globalThis.fetch;
|
|
97
|
+
if (!this.apiKey) {
|
|
98
|
+
throw new Error("API key is required");
|
|
99
|
+
}
|
|
100
|
+
if (!this.fetchFn) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
"fetch is not available. Please provide a fetch implementation or use Node.js >= 18."
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async request(options) {
|
|
107
|
+
const url = this.buildUrl(options.path, options.query);
|
|
108
|
+
const headers = this.buildHeaders(options.headers);
|
|
109
|
+
const timeout = options.timeout || this.timeout;
|
|
110
|
+
let lastError = null;
|
|
111
|
+
let attempt = 0;
|
|
112
|
+
while (attempt <= this.maxRetries) {
|
|
113
|
+
try {
|
|
114
|
+
const response = await this.executeRequest(url, {
|
|
115
|
+
method: options.method,
|
|
116
|
+
headers,
|
|
117
|
+
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
118
|
+
timeout
|
|
119
|
+
});
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
const error = await this.handleErrorResponse(response);
|
|
122
|
+
if (response.status >= 500 || response.status === 429) {
|
|
123
|
+
const retryAfter = this.getRetryAfter(response, attempt);
|
|
124
|
+
if (attempt < this.maxRetries && retryAfter > 0) {
|
|
125
|
+
await this.sleep(retryAfter);
|
|
126
|
+
attempt++;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
if (response.status === 204) {
|
|
133
|
+
return void 0;
|
|
134
|
+
}
|
|
135
|
+
return await response.json();
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (error instanceof APIError) {
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
lastError = error;
|
|
141
|
+
if (attempt < this.maxRetries) {
|
|
142
|
+
await this.sleep(this.calculateBackoff(attempt));
|
|
143
|
+
attempt++;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
147
|
+
throw new NetworkError("Failed to connect to API", error);
|
|
148
|
+
}
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
throw lastError || new NetworkError("Request failed after retries");
|
|
153
|
+
}
|
|
154
|
+
async executeRequest(url, options) {
|
|
155
|
+
const controller = new AbortController();
|
|
156
|
+
const timeoutId = setTimeout(() => controller.abort(), options.timeout);
|
|
157
|
+
try {
|
|
158
|
+
return await this.fetchFn(url, {
|
|
159
|
+
method: options.method,
|
|
160
|
+
headers: options.headers,
|
|
161
|
+
body: options.body,
|
|
162
|
+
signal: controller.signal
|
|
163
|
+
});
|
|
164
|
+
} catch (error) {
|
|
165
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
166
|
+
throw new TimeoutError(`Request timed out after ${options.timeout}ms`);
|
|
167
|
+
}
|
|
168
|
+
throw error;
|
|
169
|
+
} finally {
|
|
170
|
+
clearTimeout(timeoutId);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
buildUrl(path, query) {
|
|
174
|
+
const url = new URL(path.startsWith("/") ? path : `/${path}`, this.baseUrl);
|
|
175
|
+
if (query) {
|
|
176
|
+
for (const [key, value] of Object.entries(query)) {
|
|
177
|
+
if (value !== void 0) {
|
|
178
|
+
url.searchParams.set(key, String(value));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return url.toString();
|
|
183
|
+
}
|
|
184
|
+
buildHeaders(custom) {
|
|
185
|
+
const headers = new Headers({
|
|
186
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
187
|
+
"Content-Type": "application/json",
|
|
188
|
+
"Accept": "application/json",
|
|
189
|
+
"User-Agent": "@veroai/sdk/0.1.0"
|
|
190
|
+
});
|
|
191
|
+
if (custom) {
|
|
192
|
+
for (const [key, value] of Object.entries(custom)) {
|
|
193
|
+
headers.set(key, value);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return headers;
|
|
197
|
+
}
|
|
198
|
+
async handleErrorResponse(response) {
|
|
199
|
+
let errorData = {};
|
|
200
|
+
try {
|
|
201
|
+
const json = await response.json();
|
|
202
|
+
if (json && typeof json === "object") {
|
|
203
|
+
errorData = json;
|
|
204
|
+
}
|
|
205
|
+
} catch {
|
|
206
|
+
}
|
|
207
|
+
const code = errorData.error?.code || "API_ERROR";
|
|
208
|
+
const message = errorData.error?.message || response.statusText || "Unknown error";
|
|
209
|
+
const details = errorData.error?.details;
|
|
210
|
+
switch (response.status) {
|
|
211
|
+
case 400:
|
|
212
|
+
return new ValidationError(message, details);
|
|
213
|
+
case 401:
|
|
214
|
+
return new AuthenticationError(message, details);
|
|
215
|
+
case 403:
|
|
216
|
+
return new AuthorizationError(message, details);
|
|
217
|
+
case 404:
|
|
218
|
+
return new NotFoundError(message, details);
|
|
219
|
+
case 429: {
|
|
220
|
+
const retryAfter = parseInt(response.headers.get("Retry-After") || "60", 10);
|
|
221
|
+
return new RateLimitError(message, retryAfter, details);
|
|
222
|
+
}
|
|
223
|
+
default:
|
|
224
|
+
if (response.status >= 500) {
|
|
225
|
+
return new ServerError(message, response.status, details);
|
|
226
|
+
}
|
|
227
|
+
return new APIError(message, code, response.status, details, response);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
getRetryAfter(response, attempt) {
|
|
231
|
+
const retryAfterHeader = response.headers.get("Retry-After");
|
|
232
|
+
if (retryAfterHeader) {
|
|
233
|
+
const retryAfter = parseInt(retryAfterHeader, 10);
|
|
234
|
+
if (!isNaN(retryAfter)) {
|
|
235
|
+
return retryAfter * 1e3;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return this.calculateBackoff(attempt);
|
|
239
|
+
}
|
|
240
|
+
calculateBackoff(attempt) {
|
|
241
|
+
const baseDelay = Math.min(1e3 * Math.pow(2, attempt), 3e4);
|
|
242
|
+
const jitter = Math.random() * 1e3;
|
|
243
|
+
return baseDelay + jitter;
|
|
244
|
+
}
|
|
245
|
+
sleep(ms) {
|
|
246
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
247
|
+
}
|
|
248
|
+
// Convenience methods
|
|
249
|
+
async get(path, query) {
|
|
250
|
+
return this.request({ method: "GET", path, query });
|
|
251
|
+
}
|
|
252
|
+
async post(path, body) {
|
|
253
|
+
return this.request({ method: "POST", path, body });
|
|
254
|
+
}
|
|
255
|
+
async put(path, body) {
|
|
256
|
+
return this.request({ method: "PUT", path, body });
|
|
257
|
+
}
|
|
258
|
+
async patch(path, body) {
|
|
259
|
+
return this.request({ method: "PATCH", path, body });
|
|
260
|
+
}
|
|
261
|
+
async delete(path) {
|
|
262
|
+
return this.request({ method: "DELETE", path });
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// src/resources/channels.ts
|
|
267
|
+
function transformChannel(data) {
|
|
268
|
+
return {
|
|
269
|
+
id: data.id,
|
|
270
|
+
tenantId: data.tenant_id,
|
|
271
|
+
name: data.name,
|
|
272
|
+
description: data.description,
|
|
273
|
+
adapterType: data.adapter_type,
|
|
274
|
+
direction: data.direction,
|
|
275
|
+
status: data.status,
|
|
276
|
+
createdAt: data.created_at,
|
|
277
|
+
updatedAt: data.updated_at
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
var ChannelsResource = class {
|
|
281
|
+
constructor(http) {
|
|
282
|
+
this.http = http;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* List all channels
|
|
286
|
+
*/
|
|
287
|
+
async list() {
|
|
288
|
+
const response = await this.http.get("/v1/channels");
|
|
289
|
+
return {
|
|
290
|
+
data: response.channels.map(transformChannel),
|
|
291
|
+
total: response.total,
|
|
292
|
+
hasMore: false
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Get a channel by ID
|
|
297
|
+
*/
|
|
298
|
+
async get(channelId) {
|
|
299
|
+
const response = await this.http.get(`/v1/channels/${channelId}`);
|
|
300
|
+
return transformChannel(response.channel);
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Create a new channel
|
|
304
|
+
* @returns The created channel and optionally an OAuth URL for OAuth-based adapters
|
|
305
|
+
*/
|
|
306
|
+
async create(params) {
|
|
307
|
+
const response = await this.http.post("/v1/channels", {
|
|
308
|
+
name: params.name,
|
|
309
|
+
description: params.description,
|
|
310
|
+
adapter_type: params.adapterType,
|
|
311
|
+
direction: params.direction,
|
|
312
|
+
config: params.config
|
|
313
|
+
});
|
|
314
|
+
return {
|
|
315
|
+
channel: transformChannel(response.channel),
|
|
316
|
+
oauthUrl: response.oauth_url
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Update a channel
|
|
321
|
+
*/
|
|
322
|
+
async update(channelId, params) {
|
|
323
|
+
const response = await this.http.put(`/v1/channels/${channelId}`, {
|
|
324
|
+
name: params.name,
|
|
325
|
+
description: params.description,
|
|
326
|
+
status: params.status,
|
|
327
|
+
config: params.config
|
|
328
|
+
});
|
|
329
|
+
return transformChannel(response.channel);
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Delete a channel
|
|
333
|
+
*/
|
|
334
|
+
async delete(channelId) {
|
|
335
|
+
await this.http.delete(`/v1/channels/${channelId}`);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Test channel connectivity
|
|
339
|
+
*/
|
|
340
|
+
async test(channelId) {
|
|
341
|
+
return this.http.post(`/v1/channels/${channelId}/test`);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Get channel health metrics
|
|
345
|
+
*/
|
|
346
|
+
async health(channelId) {
|
|
347
|
+
const response = await this.http.get(`/v1/channels/${channelId}/health`);
|
|
348
|
+
return {
|
|
349
|
+
status: response.status,
|
|
350
|
+
lastEventAt: response.last_event_at,
|
|
351
|
+
errorCount24h: response.error_count_24h,
|
|
352
|
+
successRate: response.success_rate
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
// src/resources/events.ts
|
|
358
|
+
function transformEvent(data) {
|
|
359
|
+
return {
|
|
360
|
+
eventId: data.event_id,
|
|
361
|
+
tenantId: data.tenant_id,
|
|
362
|
+
channelId: data.channel_id,
|
|
363
|
+
eventType: data.event_type,
|
|
364
|
+
canonicalType: data.canonical_type,
|
|
365
|
+
direction: data.direction,
|
|
366
|
+
adapterType: data.adapter_type,
|
|
367
|
+
occurredAt: data.occurred_at,
|
|
368
|
+
ingestedAt: data.ingested_at,
|
|
369
|
+
payload: data.payload
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
var EventsResource = class {
|
|
373
|
+
constructor(http) {
|
|
374
|
+
this.http = http;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* List activity events with optional filters
|
|
378
|
+
*/
|
|
379
|
+
async list(params = {}) {
|
|
380
|
+
const query = {
|
|
381
|
+
limit: params.limit,
|
|
382
|
+
offset: params.offset,
|
|
383
|
+
channel_id: params.channelId,
|
|
384
|
+
event_type: params.eventType,
|
|
385
|
+
canonical_type: params.canonicalType,
|
|
386
|
+
direction: params.direction
|
|
387
|
+
};
|
|
388
|
+
if (params.startDate) {
|
|
389
|
+
query.start_date = params.startDate instanceof Date ? params.startDate.toISOString() : params.startDate;
|
|
390
|
+
}
|
|
391
|
+
if (params.endDate) {
|
|
392
|
+
query.end_date = params.endDate instanceof Date ? params.endDate.toISOString() : params.endDate;
|
|
393
|
+
}
|
|
394
|
+
const response = await this.http.get("/v1/events", query);
|
|
395
|
+
return {
|
|
396
|
+
data: response.events.map(transformEvent),
|
|
397
|
+
total: response.total,
|
|
398
|
+
hasMore: response.next_offset !== void 0,
|
|
399
|
+
nextOffset: response.next_offset
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Get a single event by ID
|
|
404
|
+
*/
|
|
405
|
+
async get(eventId) {
|
|
406
|
+
const response = await this.http.get(`/v1/events/${eventId}`);
|
|
407
|
+
return transformEvent(response);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Get event statistics summary
|
|
411
|
+
*/
|
|
412
|
+
async stats(options = {}) {
|
|
413
|
+
const response = await this.http.get("/v1/events/stats/summary", {
|
|
414
|
+
days: options.days || 7
|
|
415
|
+
});
|
|
416
|
+
return {
|
|
417
|
+
totalEvents: response.total_events,
|
|
418
|
+
eventsByType: response.events_by_type,
|
|
419
|
+
eventsByChannel: response.events_by_channel
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Get time series data for charts
|
|
424
|
+
*/
|
|
425
|
+
async timeseries(options = {}) {
|
|
426
|
+
const response = await this.http.get("/v1/events/stats/timeseries", {
|
|
427
|
+
days: options.days || 7,
|
|
428
|
+
granularity: options.granularity || "hour"
|
|
429
|
+
});
|
|
430
|
+
return response.data;
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
// src/resources/messages.ts
|
|
435
|
+
var MessagesResource = class {
|
|
436
|
+
constructor(http) {
|
|
437
|
+
this.http = http;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Send a message through a channel
|
|
441
|
+
*
|
|
442
|
+
* @example
|
|
443
|
+
* ```typescript
|
|
444
|
+
* // Send an SMS
|
|
445
|
+
* const result = await veroai.messages.send({
|
|
446
|
+
* channelId: 'ch_abc123',
|
|
447
|
+
* to: '+15551234567',
|
|
448
|
+
* content: { type: 'text', text: 'Hello from VeroAI!' }
|
|
449
|
+
* });
|
|
450
|
+
*
|
|
451
|
+
* // Send an email
|
|
452
|
+
* const result = await veroai.messages.send({
|
|
453
|
+
* channelId: 'ch_def456',
|
|
454
|
+
* to: 'user@example.com',
|
|
455
|
+
* subject: 'Welcome!',
|
|
456
|
+
* content: {
|
|
457
|
+
* type: 'html',
|
|
458
|
+
* html: '<h1>Welcome to our platform</h1>'
|
|
459
|
+
* }
|
|
460
|
+
* });
|
|
461
|
+
* ```
|
|
462
|
+
*/
|
|
463
|
+
async send(params) {
|
|
464
|
+
const response = await this.http.post("/v1/messages", {
|
|
465
|
+
channel_id: params.channelId,
|
|
466
|
+
to: params.to,
|
|
467
|
+
subject: params.subject,
|
|
468
|
+
content: params.content,
|
|
469
|
+
metadata: params.metadata
|
|
470
|
+
});
|
|
471
|
+
return {
|
|
472
|
+
messageId: response.message_id,
|
|
473
|
+
eventId: response.event_id,
|
|
474
|
+
status: response.status,
|
|
475
|
+
providerMessageId: response.provider_message_id
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Send a message to multiple recipients (batch)
|
|
480
|
+
*
|
|
481
|
+
* @example
|
|
482
|
+
* ```typescript
|
|
483
|
+
* const results = await veroai.messages.sendBatch({
|
|
484
|
+
* channelId: 'ch_abc123',
|
|
485
|
+
* messages: [
|
|
486
|
+
* { to: '+15551234567', content: { type: 'text', text: 'Hello!' } },
|
|
487
|
+
* { to: '+15559876543', content: { type: 'text', text: 'Hi there!' } },
|
|
488
|
+
* ]
|
|
489
|
+
* });
|
|
490
|
+
* ```
|
|
491
|
+
*/
|
|
492
|
+
async sendBatch(params) {
|
|
493
|
+
const results = [];
|
|
494
|
+
const batchSize = 100;
|
|
495
|
+
for (let i = 0; i < params.messages.length; i += batchSize) {
|
|
496
|
+
const batch = params.messages.slice(i, i + batchSize);
|
|
497
|
+
const batchResults = await Promise.all(
|
|
498
|
+
batch.map(
|
|
499
|
+
(msg) => this.send({
|
|
500
|
+
channelId: params.channelId,
|
|
501
|
+
to: msg.to,
|
|
502
|
+
subject: msg.subject,
|
|
503
|
+
content: msg.content,
|
|
504
|
+
metadata: msg.metadata
|
|
505
|
+
})
|
|
506
|
+
)
|
|
507
|
+
);
|
|
508
|
+
results.push(...batchResults);
|
|
509
|
+
}
|
|
510
|
+
return results;
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
// src/resources/webhooks.ts
|
|
515
|
+
function transformWebhook(data) {
|
|
516
|
+
return {
|
|
517
|
+
id: data.id,
|
|
518
|
+
tenantId: data.tenant_id,
|
|
519
|
+
name: data.name,
|
|
520
|
+
url: data.url,
|
|
521
|
+
events: data.events,
|
|
522
|
+
channelId: data.channel_id,
|
|
523
|
+
headers: data.headers,
|
|
524
|
+
status: data.status,
|
|
525
|
+
retryConfig: {
|
|
526
|
+
enabled: data.retry_config?.enabled,
|
|
527
|
+
maxAttempts: data.retry_config?.max_attempts,
|
|
528
|
+
backoff: data.retry_config?.backoff
|
|
529
|
+
},
|
|
530
|
+
createdAt: data.created_at,
|
|
531
|
+
updatedAt: data.updated_at
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function transformDelivery(data) {
|
|
535
|
+
return {
|
|
536
|
+
id: data.id,
|
|
537
|
+
webhookId: data.webhook_id,
|
|
538
|
+
eventId: data.event_id,
|
|
539
|
+
eventType: data.event_type,
|
|
540
|
+
status: data.status,
|
|
541
|
+
attempts: data.attempts,
|
|
542
|
+
responseCode: data.response_code,
|
|
543
|
+
responseBody: data.response_body,
|
|
544
|
+
responseTimeMs: data.response_time_ms,
|
|
545
|
+
createdAt: data.created_at,
|
|
546
|
+
completedAt: data.completed_at
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
var WebhooksResource = class {
|
|
550
|
+
constructor(http) {
|
|
551
|
+
this.http = http;
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* List all webhooks
|
|
555
|
+
*/
|
|
556
|
+
async list() {
|
|
557
|
+
const response = await this.http.get("/v1/webhooks");
|
|
558
|
+
return {
|
|
559
|
+
data: response.webhooks.map(transformWebhook),
|
|
560
|
+
total: response.total,
|
|
561
|
+
hasMore: false
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Get a webhook by ID
|
|
566
|
+
*/
|
|
567
|
+
async get(webhookId) {
|
|
568
|
+
const response = await this.http.get(`/v1/webhooks/${webhookId}`);
|
|
569
|
+
return transformWebhook(response.webhook);
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Create a new webhook
|
|
573
|
+
* @returns The created webhook and the signing secret (only returned once)
|
|
574
|
+
*/
|
|
575
|
+
async create(params) {
|
|
576
|
+
const response = await this.http.post("/v1/webhooks", {
|
|
577
|
+
name: params.name,
|
|
578
|
+
url: params.url,
|
|
579
|
+
events: params.events,
|
|
580
|
+
channel_id: params.channelId,
|
|
581
|
+
headers: params.headers,
|
|
582
|
+
retry_config: params.retryConfig ? {
|
|
583
|
+
enabled: params.retryConfig.enabled ?? true,
|
|
584
|
+
max_attempts: params.retryConfig.maxAttempts ?? 5,
|
|
585
|
+
backoff: params.retryConfig.backoff ?? "exponential"
|
|
586
|
+
} : void 0
|
|
587
|
+
});
|
|
588
|
+
return {
|
|
589
|
+
webhook: transformWebhook(response.webhook),
|
|
590
|
+
secret: response.secret
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Update a webhook
|
|
595
|
+
*/
|
|
596
|
+
async update(webhookId, params) {
|
|
597
|
+
const response = await this.http.patch(`/v1/webhooks/${webhookId}`, {
|
|
598
|
+
name: params.name,
|
|
599
|
+
url: params.url,
|
|
600
|
+
events: params.events,
|
|
601
|
+
channel_id: params.channelId,
|
|
602
|
+
headers: params.headers,
|
|
603
|
+
status: params.status,
|
|
604
|
+
retry_config: params.retryConfig ? {
|
|
605
|
+
enabled: params.retryConfig.enabled,
|
|
606
|
+
max_attempts: params.retryConfig.maxAttempts,
|
|
607
|
+
backoff: params.retryConfig.backoff
|
|
608
|
+
} : void 0
|
|
609
|
+
});
|
|
610
|
+
return transformWebhook(response.webhook);
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Delete a webhook
|
|
614
|
+
*/
|
|
615
|
+
async delete(webhookId) {
|
|
616
|
+
await this.http.delete(`/v1/webhooks/${webhookId}`);
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Regenerate webhook signing secret
|
|
620
|
+
* @returns The new signing secret (only returned once)
|
|
621
|
+
*/
|
|
622
|
+
async regenerateSecret(webhookId) {
|
|
623
|
+
const response = await this.http.post(
|
|
624
|
+
`/v1/webhooks/${webhookId}/regenerate-secret`
|
|
625
|
+
);
|
|
626
|
+
return {
|
|
627
|
+
webhook: transformWebhook(response.webhook),
|
|
628
|
+
secret: response.secret
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* List delivery history for a webhook
|
|
633
|
+
*/
|
|
634
|
+
async deliveries(webhookId, options = {}) {
|
|
635
|
+
const response = await this.http.get(
|
|
636
|
+
`/v1/webhooks/${webhookId}/deliveries`,
|
|
637
|
+
{
|
|
638
|
+
limit: options.limit || 50,
|
|
639
|
+
cursor: options.cursor
|
|
640
|
+
}
|
|
641
|
+
);
|
|
642
|
+
return {
|
|
643
|
+
data: response.deliveries.map(transformDelivery),
|
|
644
|
+
total: response.total,
|
|
645
|
+
hasMore: response.has_more,
|
|
646
|
+
nextCursor: response.next_cursor
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Get webhook delivery statistics
|
|
651
|
+
*/
|
|
652
|
+
async stats(webhookId, options = {}) {
|
|
653
|
+
const response = await this.http.get(
|
|
654
|
+
`/v1/webhooks/${webhookId}/stats`,
|
|
655
|
+
{
|
|
656
|
+
time_range: options.timeRange || "24h"
|
|
657
|
+
}
|
|
658
|
+
);
|
|
659
|
+
return {
|
|
660
|
+
total: response.total,
|
|
661
|
+
success: response.success,
|
|
662
|
+
failed: response.failed,
|
|
663
|
+
pending: response.pending,
|
|
664
|
+
successRate: response.success_rate,
|
|
665
|
+
averageResponseTimeMs: response.average_response_time_ms,
|
|
666
|
+
timeRange: response.time_range
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
// src/resources/api-keys.ts
|
|
672
|
+
function transformApiKey(data) {
|
|
673
|
+
return {
|
|
674
|
+
id: data.id,
|
|
675
|
+
tenantId: data.tenant_id,
|
|
676
|
+
name: data.name,
|
|
677
|
+
keyPrefix: data.key_prefix,
|
|
678
|
+
environment: data.environment,
|
|
679
|
+
scopes: data.scopes,
|
|
680
|
+
expiresAt: data.expires_at,
|
|
681
|
+
lastUsedAt: data.last_used_at,
|
|
682
|
+
createdAt: data.created_at
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
var ApiKeysResource = class {
|
|
686
|
+
constructor(http) {
|
|
687
|
+
this.http = http;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* List all API keys
|
|
691
|
+
*/
|
|
692
|
+
async list() {
|
|
693
|
+
const response = await this.http.get("/v1/api-keys");
|
|
694
|
+
return {
|
|
695
|
+
data: response.api_keys.map(transformApiKey),
|
|
696
|
+
total: response.total,
|
|
697
|
+
hasMore: false
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Get an API key by ID
|
|
702
|
+
*/
|
|
703
|
+
async get(keyId) {
|
|
704
|
+
const response = await this.http.get(`/v1/api-keys/${keyId}`);
|
|
705
|
+
return transformApiKey(response.api_key);
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Create a new API key
|
|
709
|
+
* @returns The created API key and the plaintext key (only returned once)
|
|
710
|
+
*
|
|
711
|
+
* @example
|
|
712
|
+
* ```typescript
|
|
713
|
+
* const { apiKey, key } = await veroai.apiKeys.create({
|
|
714
|
+
* name: 'Production Key',
|
|
715
|
+
* environment: 'production',
|
|
716
|
+
* scopes: ['channels:read', 'channels:write', 'messages:send'],
|
|
717
|
+
* });
|
|
718
|
+
*
|
|
719
|
+
* // Save this key securely - it won't be shown again!
|
|
720
|
+
* console.log('API Key:', key); // sk_live_abc123...
|
|
721
|
+
* ```
|
|
722
|
+
*/
|
|
723
|
+
async create(params) {
|
|
724
|
+
const response = await this.http.post("/v1/api-keys", {
|
|
725
|
+
name: params.name,
|
|
726
|
+
environment: params.environment,
|
|
727
|
+
scopes: params.scopes,
|
|
728
|
+
expires_at: params.expiresAt instanceof Date ? params.expiresAt.toISOString() : params.expiresAt
|
|
729
|
+
});
|
|
730
|
+
return {
|
|
731
|
+
apiKey: transformApiKey(response.api_key),
|
|
732
|
+
key: response.key
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Delete (revoke) an API key
|
|
737
|
+
*/
|
|
738
|
+
async delete(keyId) {
|
|
739
|
+
await this.http.delete(`/v1/api-keys/${keyId}`);
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Alias for delete - revoke an API key
|
|
743
|
+
*/
|
|
744
|
+
async revoke(keyId) {
|
|
745
|
+
return this.delete(keyId);
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
|
|
749
|
+
// src/resources/domains.ts
|
|
750
|
+
function transformDomain(data) {
|
|
751
|
+
return {
|
|
752
|
+
id: data.id,
|
|
753
|
+
tenantId: data.tenant_id,
|
|
754
|
+
domain: data.domain,
|
|
755
|
+
status: data.status,
|
|
756
|
+
verificationRecord: data.verification_record,
|
|
757
|
+
verificationStatus: {
|
|
758
|
+
dkim: data.verification_status.dkim,
|
|
759
|
+
spf: data.verification_status.spf,
|
|
760
|
+
dmarc: data.verification_status.dmarc,
|
|
761
|
+
mx: data.verification_status.mx,
|
|
762
|
+
lastCheckedAt: data.verification_status.last_checked_at
|
|
763
|
+
},
|
|
764
|
+
dnsRecords: data.dns_records.map((record) => ({
|
|
765
|
+
type: record.type,
|
|
766
|
+
name: record.name,
|
|
767
|
+
value: record.value,
|
|
768
|
+
priority: record.priority,
|
|
769
|
+
verified: record.verified
|
|
770
|
+
})),
|
|
771
|
+
createdAt: data.created_at,
|
|
772
|
+
updatedAt: data.updated_at
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
var DomainsResource = class {
|
|
776
|
+
constructor(http) {
|
|
777
|
+
this.http = http;
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* List all domains
|
|
781
|
+
*/
|
|
782
|
+
async list() {
|
|
783
|
+
const response = await this.http.get("/v1/domains");
|
|
784
|
+
return {
|
|
785
|
+
data: response.domains.map(transformDomain),
|
|
786
|
+
total: response.total,
|
|
787
|
+
hasMore: false
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Get a domain by ID
|
|
792
|
+
*/
|
|
793
|
+
async get(domainId) {
|
|
794
|
+
const response = await this.http.get(`/v1/domains/${domainId}`);
|
|
795
|
+
return transformDomain(response.domain);
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Add a new domain
|
|
799
|
+
*
|
|
800
|
+
* @example
|
|
801
|
+
* ```typescript
|
|
802
|
+
* // Manual DNS verification
|
|
803
|
+
* const { domain } = await veroai.domains.create({
|
|
804
|
+
* domain: 'example.com',
|
|
805
|
+
* verificationMethod: 'manual',
|
|
806
|
+
* });
|
|
807
|
+
*
|
|
808
|
+
* // Automatic via Cloudflare
|
|
809
|
+
* const { domain } = await veroai.domains.create({
|
|
810
|
+
* domain: 'example.com',
|
|
811
|
+
* verificationMethod: 'cloudflare',
|
|
812
|
+
* cloudflareApiToken: 'your_cf_token',
|
|
813
|
+
* });
|
|
814
|
+
* ```
|
|
815
|
+
*/
|
|
816
|
+
async create(params) {
|
|
817
|
+
const response = await this.http.post("/v1/domains", {
|
|
818
|
+
domain: params.domain,
|
|
819
|
+
verification_method: params.verificationMethod,
|
|
820
|
+
cloudflare_api_token: params.cloudflareApiToken
|
|
821
|
+
});
|
|
822
|
+
return {
|
|
823
|
+
domain: transformDomain(response.domain),
|
|
824
|
+
verificationRecord: response.verification_record
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Delete a domain
|
|
829
|
+
*/
|
|
830
|
+
async delete(domainId) {
|
|
831
|
+
await this.http.delete(`/v1/domains/${domainId}`);
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Verify domain DNS records
|
|
835
|
+
*/
|
|
836
|
+
async verify(domainId) {
|
|
837
|
+
const response = await this.http.post(
|
|
838
|
+
`/v1/domains/${domainId}/verify`
|
|
839
|
+
);
|
|
840
|
+
return {
|
|
841
|
+
domain: transformDomain(response.domain),
|
|
842
|
+
verificationResults: response.verification_results
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Get DNS records that need to be configured
|
|
847
|
+
*/
|
|
848
|
+
async dnsRecords(domainId) {
|
|
849
|
+
const domain = await this.get(domainId);
|
|
850
|
+
return domain.dnsRecords;
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
|
|
854
|
+
// src/resources/voice/numbers.ts
|
|
855
|
+
function transformPhoneNumber(data) {
|
|
856
|
+
return {
|
|
857
|
+
id: data.id,
|
|
858
|
+
number: data.number,
|
|
859
|
+
country: data.country,
|
|
860
|
+
region: data.region,
|
|
861
|
+
locality: data.locality,
|
|
862
|
+
capabilities: data.capabilities,
|
|
863
|
+
channelId: data.channel_id,
|
|
864
|
+
channelName: data.channel_name,
|
|
865
|
+
status: data.status,
|
|
866
|
+
monthlyCostCents: data.monthly_cost_cents,
|
|
867
|
+
setupCostCents: data.setup_cost_cents,
|
|
868
|
+
createdAt: data.created_at,
|
|
869
|
+
updatedAt: data.updated_at
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
function transformAvailableNumber(data) {
|
|
873
|
+
return {
|
|
874
|
+
number: data.number,
|
|
875
|
+
country: data.country,
|
|
876
|
+
region: data.region,
|
|
877
|
+
locality: data.locality,
|
|
878
|
+
capabilities: data.capabilities,
|
|
879
|
+
monthlyCostCents: data.monthly_cost_cents,
|
|
880
|
+
setupCostCents: data.setup_cost_cents,
|
|
881
|
+
provider: data.provider
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
var VoiceNumbersResource = class {
|
|
885
|
+
constructor(http) {
|
|
886
|
+
this.http = http;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Search for available phone numbers to purchase
|
|
890
|
+
*/
|
|
891
|
+
async search(params) {
|
|
892
|
+
const response = await this.http.get("/v1/voice/numbers/search", {
|
|
893
|
+
country: params.country,
|
|
894
|
+
area_code: params.areaCode,
|
|
895
|
+
contains: params.contains,
|
|
896
|
+
capabilities: params.capabilities?.join(","),
|
|
897
|
+
limit: params.limit
|
|
898
|
+
});
|
|
899
|
+
return response.available_numbers.map(transformAvailableNumber);
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* Purchase a phone number
|
|
903
|
+
*/
|
|
904
|
+
async purchase(params) {
|
|
905
|
+
const response = await this.http.post("/v1/voice/numbers/purchase", {
|
|
906
|
+
number: params.number,
|
|
907
|
+
channel_id: params.channelId
|
|
908
|
+
});
|
|
909
|
+
return transformPhoneNumber(response.number);
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* List owned phone numbers
|
|
913
|
+
*/
|
|
914
|
+
async list(params) {
|
|
915
|
+
const response = await this.http.get("/v1/voice/numbers", {
|
|
916
|
+
channel_id: params?.channelId,
|
|
917
|
+
status: params?.status,
|
|
918
|
+
country: params?.country,
|
|
919
|
+
capabilities: params?.capabilities?.join(","),
|
|
920
|
+
limit: params?.limit,
|
|
921
|
+
offset: params?.offset
|
|
922
|
+
});
|
|
923
|
+
return {
|
|
924
|
+
data: response.numbers.map(transformPhoneNumber),
|
|
925
|
+
total: response.total,
|
|
926
|
+
hasMore: response.offset + response.numbers.length < response.total,
|
|
927
|
+
nextOffset: response.offset + response.numbers.length
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Get a phone number by ID
|
|
932
|
+
*/
|
|
933
|
+
async get(numberId) {
|
|
934
|
+
const response = await this.http.get(`/v1/voice/numbers/${numberId}`);
|
|
935
|
+
return transformPhoneNumber(response);
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Update a phone number (assign/unassign to channel)
|
|
939
|
+
*/
|
|
940
|
+
async update(numberId, params) {
|
|
941
|
+
const response = await this.http.patch(`/v1/voice/numbers/${numberId}`, {
|
|
942
|
+
channel_id: params.channelId
|
|
943
|
+
});
|
|
944
|
+
return transformPhoneNumber(response.number);
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Release a phone number
|
|
948
|
+
*/
|
|
949
|
+
async release(numberId) {
|
|
950
|
+
await this.http.delete(`/v1/voice/numbers/${numberId}`);
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Assign a phone number to a channel
|
|
954
|
+
*/
|
|
955
|
+
async assignToChannel(numberId, channelId) {
|
|
956
|
+
return this.update(numberId, { channelId });
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* Unassign a phone number from its channel
|
|
960
|
+
*/
|
|
961
|
+
async unassignFromChannel(numberId) {
|
|
962
|
+
return this.update(numberId, { channelId: null });
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
|
|
966
|
+
// src/resources/voice/calls.ts
|
|
967
|
+
function transformCall(data) {
|
|
968
|
+
return {
|
|
969
|
+
id: data.id,
|
|
970
|
+
channelId: data.channel_id,
|
|
971
|
+
providerCallSid: data.provider_call_sid,
|
|
972
|
+
fromNumber: data.from_number,
|
|
973
|
+
toNumber: data.to_number,
|
|
974
|
+
direction: data.direction,
|
|
975
|
+
status: data.status,
|
|
976
|
+
endReason: data.end_reason,
|
|
977
|
+
initiatedAt: data.initiated_at,
|
|
978
|
+
ringingAt: data.ringing_at,
|
|
979
|
+
answeredAt: data.answered_at,
|
|
980
|
+
endedAt: data.ended_at,
|
|
981
|
+
durationSeconds: data.duration_seconds,
|
|
982
|
+
recordingUrl: data.recording_url,
|
|
983
|
+
transcriptionUrl: data.transcription_url,
|
|
984
|
+
agentId: data.agent_id,
|
|
985
|
+
metadata: data.metadata
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
var VoiceCallsResource = class {
|
|
989
|
+
constructor(http) {
|
|
990
|
+
this.http = http;
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Initiate an outbound call
|
|
994
|
+
*/
|
|
995
|
+
async dial(params) {
|
|
996
|
+
const response = await this.http.post("/v1/voice/calls", {
|
|
997
|
+
channel_id: params.channelId,
|
|
998
|
+
to: params.to,
|
|
999
|
+
from: params.from,
|
|
1000
|
+
timeout: params.timeout,
|
|
1001
|
+
metadata: params.metadata
|
|
1002
|
+
});
|
|
1003
|
+
return transformCall(response.call);
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* List calls
|
|
1007
|
+
*/
|
|
1008
|
+
async list(params) {
|
|
1009
|
+
const response = await this.http.get("/v1/voice/calls", {
|
|
1010
|
+
channel_id: params?.channelId,
|
|
1011
|
+
direction: params?.direction,
|
|
1012
|
+
status: params?.status,
|
|
1013
|
+
from_number: params?.fromNumber,
|
|
1014
|
+
to_number: params?.toNumber,
|
|
1015
|
+
start_date: params?.startDate instanceof Date ? params.startDate.toISOString() : params?.startDate,
|
|
1016
|
+
end_date: params?.endDate instanceof Date ? params.endDate.toISOString() : params?.endDate,
|
|
1017
|
+
limit: params?.limit,
|
|
1018
|
+
offset: params?.offset
|
|
1019
|
+
});
|
|
1020
|
+
return {
|
|
1021
|
+
data: response.calls.map(transformCall),
|
|
1022
|
+
total: response.total,
|
|
1023
|
+
hasMore: response.offset + response.calls.length < response.total,
|
|
1024
|
+
nextOffset: response.offset + response.calls.length
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Get a call by ID
|
|
1029
|
+
*/
|
|
1030
|
+
async get(callId) {
|
|
1031
|
+
const response = await this.http.get(`/v1/voice/calls/${callId}`);
|
|
1032
|
+
return transformCall(response);
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Hang up an active call
|
|
1036
|
+
*/
|
|
1037
|
+
async hangup(callId) {
|
|
1038
|
+
await this.http.post(`/v1/voice/calls/${callId}/hangup`);
|
|
1039
|
+
}
|
|
1040
|
+
};
|
|
1041
|
+
|
|
1042
|
+
// src/resources/voice/rooms.ts
|
|
1043
|
+
var VoiceRoomsResource = class {
|
|
1044
|
+
constructor(http) {
|
|
1045
|
+
this.http = http;
|
|
1046
|
+
}
|
|
1047
|
+
/**
|
|
1048
|
+
* Create a new WebRTC room
|
|
1049
|
+
*
|
|
1050
|
+
* @example
|
|
1051
|
+
* ```typescript
|
|
1052
|
+
* const roomInfo = await veroai.voice.rooms.create({
|
|
1053
|
+
* name: 'support-call-123',
|
|
1054
|
+
* emptyTimeout: 300,
|
|
1055
|
+
* maxParticipants: 2,
|
|
1056
|
+
* });
|
|
1057
|
+
* // Connect to room using roomInfo.wsUrl and roomInfo.token
|
|
1058
|
+
* ```
|
|
1059
|
+
*/
|
|
1060
|
+
async create(params) {
|
|
1061
|
+
const response = await this.http.post("/voice/rooms", {
|
|
1062
|
+
name: params.name,
|
|
1063
|
+
empty_timeout: params.emptyTimeout,
|
|
1064
|
+
max_participants: params.maxParticipants,
|
|
1065
|
+
metadata: params.metadata
|
|
1066
|
+
});
|
|
1067
|
+
return {
|
|
1068
|
+
sid: response.room.id,
|
|
1069
|
+
name: response.room.name,
|
|
1070
|
+
wsUrl: response.room.ws_url,
|
|
1071
|
+
token: response.room.token,
|
|
1072
|
+
numParticipants: 0,
|
|
1073
|
+
metadata: params.metadata
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Join an existing room or create and join if it doesn't exist
|
|
1078
|
+
*
|
|
1079
|
+
* @example
|
|
1080
|
+
* ```typescript
|
|
1081
|
+
* const roomInfo = await veroai.voice.rooms.join({
|
|
1082
|
+
* roomName: 'support-call-123',
|
|
1083
|
+
* participantName: 'Agent Smith',
|
|
1084
|
+
* });
|
|
1085
|
+
* // Use roomInfo.token to connect via LiveKit client SDK
|
|
1086
|
+
* ```
|
|
1087
|
+
*/
|
|
1088
|
+
async join(params) {
|
|
1089
|
+
const response = await this.http.post(
|
|
1090
|
+
`/voice/rooms/${encodeURIComponent(params.roomName)}/join`,
|
|
1091
|
+
{
|
|
1092
|
+
participant_name: params.participantName,
|
|
1093
|
+
can_publish: params.canPublish,
|
|
1094
|
+
can_subscribe: params.canSubscribe,
|
|
1095
|
+
metadata: params.metadata
|
|
1096
|
+
}
|
|
1097
|
+
);
|
|
1098
|
+
return {
|
|
1099
|
+
sid: "",
|
|
1100
|
+
name: response.room_name,
|
|
1101
|
+
wsUrl: response.ws_url,
|
|
1102
|
+
token: response.token,
|
|
1103
|
+
numParticipants: 0
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
/**
|
|
1107
|
+
* Get room details
|
|
1108
|
+
*
|
|
1109
|
+
* @example
|
|
1110
|
+
* ```typescript
|
|
1111
|
+
* const room = await veroai.voice.rooms.get('support-call-123');
|
|
1112
|
+
* console.log(`Room: ${room.name}`);
|
|
1113
|
+
* ```
|
|
1114
|
+
*/
|
|
1115
|
+
async get(roomName) {
|
|
1116
|
+
const response = await this.http.get(
|
|
1117
|
+
`/voice/rooms/${encodeURIComponent(roomName)}`
|
|
1118
|
+
);
|
|
1119
|
+
return {
|
|
1120
|
+
sid: "",
|
|
1121
|
+
name: response.room.name,
|
|
1122
|
+
emptyTimeout: 300,
|
|
1123
|
+
maxParticipants: 10,
|
|
1124
|
+
creationTime: "",
|
|
1125
|
+
numParticipants: 0
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* List all active rooms
|
|
1130
|
+
*
|
|
1131
|
+
* @example
|
|
1132
|
+
* ```typescript
|
|
1133
|
+
* const rooms = await veroai.voice.rooms.list();
|
|
1134
|
+
* for (const room of rooms) {
|
|
1135
|
+
* console.log(`${room.name}`);
|
|
1136
|
+
* }
|
|
1137
|
+
* ```
|
|
1138
|
+
*/
|
|
1139
|
+
async list(params) {
|
|
1140
|
+
const searchParams = new URLSearchParams();
|
|
1141
|
+
if (params?.names) {
|
|
1142
|
+
for (const name of params.names) {
|
|
1143
|
+
searchParams.append("names", name);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
const query = searchParams.toString();
|
|
1147
|
+
const response = await this.http.get(
|
|
1148
|
+
`/voice/rooms${query ? `?${query}` : ""}`
|
|
1149
|
+
);
|
|
1150
|
+
return response.rooms.map((room) => ({
|
|
1151
|
+
sid: "",
|
|
1152
|
+
name: room.name,
|
|
1153
|
+
emptyTimeout: 300,
|
|
1154
|
+
maxParticipants: 10,
|
|
1155
|
+
creationTime: "",
|
|
1156
|
+
numParticipants: 0
|
|
1157
|
+
}));
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Delete a room and disconnect all participants
|
|
1161
|
+
*
|
|
1162
|
+
* @example
|
|
1163
|
+
* ```typescript
|
|
1164
|
+
* await veroai.voice.rooms.delete('support-call-123');
|
|
1165
|
+
* ```
|
|
1166
|
+
*/
|
|
1167
|
+
async delete(roomName) {
|
|
1168
|
+
await this.http.delete(`/voice/rooms/${encodeURIComponent(roomName)}`);
|
|
1169
|
+
}
|
|
1170
|
+
/**
|
|
1171
|
+
* List participants in a room
|
|
1172
|
+
*
|
|
1173
|
+
* @example
|
|
1174
|
+
* ```typescript
|
|
1175
|
+
* const participants = await veroai.voice.rooms.listParticipants('support-call-123');
|
|
1176
|
+
* for (const p of participants) {
|
|
1177
|
+
* console.log(`${p.name} (${p.state})`);
|
|
1178
|
+
* }
|
|
1179
|
+
* ```
|
|
1180
|
+
*/
|
|
1181
|
+
async listParticipants(roomName) {
|
|
1182
|
+
return this.http.get(
|
|
1183
|
+
`/voice/rooms/${encodeURIComponent(roomName)}/participants`
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
/**
|
|
1187
|
+
* Remove a participant from a room
|
|
1188
|
+
*
|
|
1189
|
+
* @example
|
|
1190
|
+
* ```typescript
|
|
1191
|
+
* await veroai.voice.rooms.removeParticipant('support-call-123', 'user-456');
|
|
1192
|
+
* ```
|
|
1193
|
+
*/
|
|
1194
|
+
async removeParticipant(roomName, identity) {
|
|
1195
|
+
await this.http.delete(
|
|
1196
|
+
`/voice/rooms/${encodeURIComponent(roomName)}/participants/${encodeURIComponent(identity)}`
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Mute or unmute a participant's audio
|
|
1201
|
+
*
|
|
1202
|
+
* @example
|
|
1203
|
+
* ```typescript
|
|
1204
|
+
* // Mute participant
|
|
1205
|
+
* await veroai.voice.rooms.muteParticipant('support-call-123', 'user-456', true);
|
|
1206
|
+
*
|
|
1207
|
+
* // Unmute participant
|
|
1208
|
+
* await veroai.voice.rooms.muteParticipant('support-call-123', 'user-456', false);
|
|
1209
|
+
* ```
|
|
1210
|
+
*/
|
|
1211
|
+
async muteParticipant(roomName, identity, muted) {
|
|
1212
|
+
await this.http.post(
|
|
1213
|
+
`/voice/rooms/${encodeURIComponent(roomName)}/participants/${encodeURIComponent(identity)}/mute`,
|
|
1214
|
+
{ muted }
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Send a data message to participants in a room
|
|
1219
|
+
*
|
|
1220
|
+
* @example
|
|
1221
|
+
* ```typescript
|
|
1222
|
+
* // Send to all participants
|
|
1223
|
+
* await veroai.voice.rooms.sendData('support-call-123', { type: 'notification', message: 'Recording started' });
|
|
1224
|
+
*
|
|
1225
|
+
* // Send to specific participants
|
|
1226
|
+
* await veroai.voice.rooms.sendData('support-call-123', { type: 'private' }, ['user-456']);
|
|
1227
|
+
* ```
|
|
1228
|
+
*/
|
|
1229
|
+
async sendData(roomName, data, destinationIdentities) {
|
|
1230
|
+
await this.http.post(`/voice/rooms/${encodeURIComponent(roomName)}/data`, {
|
|
1231
|
+
data,
|
|
1232
|
+
destinationIdentities
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
|
|
1237
|
+
// src/resources/voice/index.ts
|
|
1238
|
+
var VoiceResource = class {
|
|
1239
|
+
/**
|
|
1240
|
+
* Phone number management
|
|
1241
|
+
*/
|
|
1242
|
+
numbers;
|
|
1243
|
+
/**
|
|
1244
|
+
* Call management
|
|
1245
|
+
*/
|
|
1246
|
+
calls;
|
|
1247
|
+
/**
|
|
1248
|
+
* LiveKit WebRTC room management for browser-based voice/video calls
|
|
1249
|
+
*/
|
|
1250
|
+
rooms;
|
|
1251
|
+
constructor(http) {
|
|
1252
|
+
this.numbers = new VoiceNumbersResource(http);
|
|
1253
|
+
this.calls = new VoiceCallsResource(http);
|
|
1254
|
+
this.rooms = new VoiceRoomsResource(http);
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
|
|
1258
|
+
// src/resources/agents.ts
|
|
1259
|
+
function transformAgent(agent) {
|
|
1260
|
+
return {
|
|
1261
|
+
id: agent.id,
|
|
1262
|
+
name: agent.name,
|
|
1263
|
+
description: agent.description,
|
|
1264
|
+
enabled: agent.enabled,
|
|
1265
|
+
modelConfig: {
|
|
1266
|
+
provider: agent.model_config.provider,
|
|
1267
|
+
modelId: agent.model_config.model_id,
|
|
1268
|
+
temperature: agent.model_config.temperature,
|
|
1269
|
+
maxTokens: agent.model_config.max_tokens
|
|
1270
|
+
},
|
|
1271
|
+
systemPrompt: agent.system_prompt,
|
|
1272
|
+
status: agent.status
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
var AgentsResource = class {
|
|
1276
|
+
constructor(http) {
|
|
1277
|
+
this.http = http;
|
|
1278
|
+
}
|
|
1279
|
+
/**
|
|
1280
|
+
* List AI agents
|
|
1281
|
+
*
|
|
1282
|
+
* @example
|
|
1283
|
+
* ```typescript
|
|
1284
|
+
* const agents = await veroai.agents.list({ status: 'active' });
|
|
1285
|
+
* for (const agent of agents.data) {
|
|
1286
|
+
* console.log(`${agent.name} (${agent.modelConfig.provider})`);
|
|
1287
|
+
* }
|
|
1288
|
+
* ```
|
|
1289
|
+
*/
|
|
1290
|
+
async list(params) {
|
|
1291
|
+
const searchParams = new URLSearchParams();
|
|
1292
|
+
if (params?.status) searchParams.set("status", params.status);
|
|
1293
|
+
if (params?.enabled !== void 0) searchParams.set("enabled", String(params.enabled));
|
|
1294
|
+
if (params?.limit) searchParams.set("limit", String(params.limit));
|
|
1295
|
+
if (params?.offset) searchParams.set("offset", String(params.offset));
|
|
1296
|
+
const query = searchParams.toString();
|
|
1297
|
+
const response = await this.http.get(
|
|
1298
|
+
`/agents${query ? `?${query}` : ""}`
|
|
1299
|
+
);
|
|
1300
|
+
return {
|
|
1301
|
+
data: response.agents.map(transformAgent),
|
|
1302
|
+
total: response.total,
|
|
1303
|
+
hasMore: response.offset + response.agents.length < response.total,
|
|
1304
|
+
nextOffset: response.offset + response.agents.length < response.total ? response.offset + response.limit : void 0
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
* Get an agent by ID
|
|
1309
|
+
*
|
|
1310
|
+
* @example
|
|
1311
|
+
* ```typescript
|
|
1312
|
+
* const agent = await veroai.agents.get('agent_123');
|
|
1313
|
+
* console.log(`System prompt: ${agent.systemPrompt}`);
|
|
1314
|
+
* ```
|
|
1315
|
+
*/
|
|
1316
|
+
async get(agentId) {
|
|
1317
|
+
const response = await this.http.get(
|
|
1318
|
+
`/agents/${encodeURIComponent(agentId)}`
|
|
1319
|
+
);
|
|
1320
|
+
return transformAgent(response.agent);
|
|
1321
|
+
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Create a new AI agent
|
|
1324
|
+
*
|
|
1325
|
+
* @example
|
|
1326
|
+
* ```typescript
|
|
1327
|
+
* const agent = await veroai.agents.create({
|
|
1328
|
+
* name: 'Support Agent',
|
|
1329
|
+
* modelConfig: {
|
|
1330
|
+
* provider: 'anthropic',
|
|
1331
|
+
* modelId: 'claude-3-5-sonnet-20241022',
|
|
1332
|
+
* temperature: 0.7,
|
|
1333
|
+
* },
|
|
1334
|
+
* systemPrompt: 'You are a helpful support agent...',
|
|
1335
|
+
* enabled: true,
|
|
1336
|
+
* });
|
|
1337
|
+
* ```
|
|
1338
|
+
*/
|
|
1339
|
+
async create(params) {
|
|
1340
|
+
const response = await this.http.post("/agents", {
|
|
1341
|
+
name: params.name,
|
|
1342
|
+
description: params.description,
|
|
1343
|
+
model_config: {
|
|
1344
|
+
provider: params.modelConfig.provider,
|
|
1345
|
+
model_id: params.modelConfig.modelId,
|
|
1346
|
+
temperature: params.modelConfig.temperature ?? 0.7,
|
|
1347
|
+
max_tokens: params.modelConfig.maxTokens ?? 4096
|
|
1348
|
+
},
|
|
1349
|
+
system_prompt: params.systemPrompt,
|
|
1350
|
+
enabled: params.enabled ?? false
|
|
1351
|
+
});
|
|
1352
|
+
return transformAgent(response.agent);
|
|
1353
|
+
}
|
|
1354
|
+
/**
|
|
1355
|
+
* Update an agent
|
|
1356
|
+
*
|
|
1357
|
+
* @example
|
|
1358
|
+
* ```typescript
|
|
1359
|
+
* const agent = await veroai.agents.update('agent_123', {
|
|
1360
|
+
* systemPrompt: 'Updated prompt...',
|
|
1361
|
+
* enabled: true,
|
|
1362
|
+
* });
|
|
1363
|
+
* ```
|
|
1364
|
+
*/
|
|
1365
|
+
async update(agentId, params) {
|
|
1366
|
+
const body = {};
|
|
1367
|
+
if (params.name !== void 0) body.name = params.name;
|
|
1368
|
+
if (params.description !== void 0) body.description = params.description;
|
|
1369
|
+
if (params.systemPrompt !== void 0) body.system_prompt = params.systemPrompt;
|
|
1370
|
+
if (params.enabled !== void 0) body.enabled = params.enabled;
|
|
1371
|
+
if (params.status !== void 0) body.status = params.status;
|
|
1372
|
+
if (params.modelConfig) {
|
|
1373
|
+
body.model_config = {
|
|
1374
|
+
...params.modelConfig.provider && { provider: params.modelConfig.provider },
|
|
1375
|
+
...params.modelConfig.modelId && { model_id: params.modelConfig.modelId },
|
|
1376
|
+
...params.modelConfig.temperature !== void 0 && { temperature: params.modelConfig.temperature },
|
|
1377
|
+
...params.modelConfig.maxTokens !== void 0 && { max_tokens: params.modelConfig.maxTokens }
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
const response = await this.http.patch(
|
|
1381
|
+
`/agents/${encodeURIComponent(agentId)}`,
|
|
1382
|
+
body
|
|
1383
|
+
);
|
|
1384
|
+
return transformAgent(response.agent);
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Delete an agent
|
|
1388
|
+
*
|
|
1389
|
+
* @example
|
|
1390
|
+
* ```typescript
|
|
1391
|
+
* await veroai.agents.delete('agent_123');
|
|
1392
|
+
* ```
|
|
1393
|
+
*/
|
|
1394
|
+
async delete(agentId) {
|
|
1395
|
+
await this.http.delete(`/agents/${encodeURIComponent(agentId)}`);
|
|
1396
|
+
}
|
|
1397
|
+
/**
|
|
1398
|
+
* Enable an agent
|
|
1399
|
+
*
|
|
1400
|
+
* @example
|
|
1401
|
+
* ```typescript
|
|
1402
|
+
* const agent = await veroai.agents.enable('agent_123');
|
|
1403
|
+
* ```
|
|
1404
|
+
*/
|
|
1405
|
+
async enable(agentId) {
|
|
1406
|
+
return this.update(agentId, { enabled: true });
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Disable an agent
|
|
1410
|
+
*
|
|
1411
|
+
* @example
|
|
1412
|
+
* ```typescript
|
|
1413
|
+
* const agent = await veroai.agents.disable('agent_123');
|
|
1414
|
+
* ```
|
|
1415
|
+
*/
|
|
1416
|
+
async disable(agentId) {
|
|
1417
|
+
return this.update(agentId, { enabled: false });
|
|
1418
|
+
}
|
|
1419
|
+
};
|
|
1420
|
+
|
|
1421
|
+
// src/realtime/realtime.ts
|
|
1422
|
+
var DEFAULT_REALTIME_URL = "wss://wss.veroai.dev/ws";
|
|
1423
|
+
var DEFAULT_RECONNECT_INTERVAL = 1e3;
|
|
1424
|
+
var MAX_RECONNECT_INTERVAL = 3e4;
|
|
1425
|
+
var DEFAULT_MAX_RECONNECT_ATTEMPTS = 10;
|
|
1426
|
+
var DEFAULT_HEARTBEAT_INTERVAL = 3e4;
|
|
1427
|
+
function isBrowser() {
|
|
1428
|
+
return typeof window !== "undefined" && typeof window.WebSocket !== "undefined";
|
|
1429
|
+
}
|
|
1430
|
+
async function getWebSocket() {
|
|
1431
|
+
if (isBrowser()) {
|
|
1432
|
+
return window.WebSocket;
|
|
1433
|
+
}
|
|
1434
|
+
try {
|
|
1435
|
+
const ws = await import('ws');
|
|
1436
|
+
return ws.default || ws;
|
|
1437
|
+
} catch {
|
|
1438
|
+
throw new Error(
|
|
1439
|
+
'WebSocket is not available. In Node.js, install the "ws" package: npm install ws'
|
|
1440
|
+
);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
var RealtimeResource = class {
|
|
1444
|
+
config;
|
|
1445
|
+
tokenFetcher;
|
|
1446
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1447
|
+
ws = null;
|
|
1448
|
+
state = "disconnected";
|
|
1449
|
+
reconnectAttempts = 0;
|
|
1450
|
+
reconnectTimeout = null;
|
|
1451
|
+
heartbeatInterval = null;
|
|
1452
|
+
// Event handlers
|
|
1453
|
+
eventHandlers = /* @__PURE__ */ new Set();
|
|
1454
|
+
stateHandlers = /* @__PURE__ */ new Set();
|
|
1455
|
+
errorHandlers = /* @__PURE__ */ new Set();
|
|
1456
|
+
// Active subscriptions for reconnection
|
|
1457
|
+
activeSubscriptions = {
|
|
1458
|
+
eventTypes: /* @__PURE__ */ new Set(),
|
|
1459
|
+
channels: /* @__PURE__ */ new Set(),
|
|
1460
|
+
subscribedToAll: false
|
|
1461
|
+
};
|
|
1462
|
+
// Pending subscription confirmations
|
|
1463
|
+
pendingSubscriptions = /* @__PURE__ */ new Map();
|
|
1464
|
+
constructor(tokenFetcher, config) {
|
|
1465
|
+
this.tokenFetcher = tokenFetcher;
|
|
1466
|
+
this.config = {
|
|
1467
|
+
url: config?.url ?? DEFAULT_REALTIME_URL,
|
|
1468
|
+
autoReconnect: config?.autoReconnect ?? true,
|
|
1469
|
+
reconnectInterval: config?.reconnectInterval ?? DEFAULT_RECONNECT_INTERVAL,
|
|
1470
|
+
maxReconnectAttempts: config?.maxReconnectAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS,
|
|
1471
|
+
heartbeatInterval: config?.heartbeatInterval ?? DEFAULT_HEARTBEAT_INTERVAL
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
/**
|
|
1475
|
+
* Get current connection state
|
|
1476
|
+
*/
|
|
1477
|
+
getState() {
|
|
1478
|
+
return this.state;
|
|
1479
|
+
}
|
|
1480
|
+
/**
|
|
1481
|
+
* Connect to the WebSocket server
|
|
1482
|
+
*/
|
|
1483
|
+
async connect() {
|
|
1484
|
+
if (this.state === "connected" || this.state === "connecting") {
|
|
1485
|
+
return;
|
|
1486
|
+
}
|
|
1487
|
+
this.setState("connecting");
|
|
1488
|
+
return new Promise(async (resolve, reject) => {
|
|
1489
|
+
try {
|
|
1490
|
+
const token = await this.tokenFetcher();
|
|
1491
|
+
const WS = await getWebSocket();
|
|
1492
|
+
const url = new URL(this.config.url);
|
|
1493
|
+
url.searchParams.set("token", token);
|
|
1494
|
+
this.ws = new WS(url.toString());
|
|
1495
|
+
this.ws.onopen = () => {
|
|
1496
|
+
this.setState("connected");
|
|
1497
|
+
this.reconnectAttempts = 0;
|
|
1498
|
+
this.startHeartbeat();
|
|
1499
|
+
this.resubscribe();
|
|
1500
|
+
resolve();
|
|
1501
|
+
};
|
|
1502
|
+
this.ws.onclose = (event) => {
|
|
1503
|
+
this.stopHeartbeat();
|
|
1504
|
+
this.ws = null;
|
|
1505
|
+
if (this.state === "connecting") {
|
|
1506
|
+
reject(new Error(`Connection failed: ${event.reason || "Unknown reason"}`));
|
|
1507
|
+
this.setState("disconnected");
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
this.setState("disconnected");
|
|
1511
|
+
if (this.config.autoReconnect && this.shouldReconnect()) {
|
|
1512
|
+
this.scheduleReconnect();
|
|
1513
|
+
}
|
|
1514
|
+
};
|
|
1515
|
+
this.ws.onerror = (event) => {
|
|
1516
|
+
const error = new Error(
|
|
1517
|
+
"WebSocket error" + (event.message ? `: ${event.message}` : "")
|
|
1518
|
+
);
|
|
1519
|
+
this.emitError(error);
|
|
1520
|
+
if (this.state === "connecting") {
|
|
1521
|
+
reject(error);
|
|
1522
|
+
}
|
|
1523
|
+
};
|
|
1524
|
+
this.ws.onmessage = (event) => {
|
|
1525
|
+
this.handleMessage(event.data);
|
|
1526
|
+
};
|
|
1527
|
+
} catch (error) {
|
|
1528
|
+
this.setState("disconnected");
|
|
1529
|
+
reject(error);
|
|
1530
|
+
}
|
|
1531
|
+
});
|
|
1532
|
+
}
|
|
1533
|
+
/**
|
|
1534
|
+
* Disconnect from the WebSocket server
|
|
1535
|
+
*/
|
|
1536
|
+
disconnect() {
|
|
1537
|
+
this.config.autoReconnect = false;
|
|
1538
|
+
this.clearReconnectTimeout();
|
|
1539
|
+
this.stopHeartbeat();
|
|
1540
|
+
if (this.ws) {
|
|
1541
|
+
this.ws.close(1e3, "Client disconnect");
|
|
1542
|
+
this.ws = null;
|
|
1543
|
+
}
|
|
1544
|
+
this.setState("disconnected");
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Subscribe to all events for the tenant
|
|
1548
|
+
*/
|
|
1549
|
+
async subscribeAll() {
|
|
1550
|
+
this.activeSubscriptions.subscribedToAll = true;
|
|
1551
|
+
return this.sendSubscription({
|
|
1552
|
+
type: "subscribe",
|
|
1553
|
+
subscriptionType: "all"
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
/**
|
|
1557
|
+
* Unsubscribe from all events
|
|
1558
|
+
*/
|
|
1559
|
+
async unsubscribeAll() {
|
|
1560
|
+
this.activeSubscriptions.subscribedToAll = false;
|
|
1561
|
+
return this.sendSubscription({
|
|
1562
|
+
type: "unsubscribe",
|
|
1563
|
+
subscriptionType: "all"
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
/**
|
|
1567
|
+
* Subscribe to specific channels
|
|
1568
|
+
*/
|
|
1569
|
+
async subscribeChannels(channelIds) {
|
|
1570
|
+
for (const id of channelIds) {
|
|
1571
|
+
this.activeSubscriptions.channels.add(id);
|
|
1572
|
+
}
|
|
1573
|
+
return this.sendSubscription({
|
|
1574
|
+
type: "subscribe",
|
|
1575
|
+
subscriptionType: "channel",
|
|
1576
|
+
channels: channelIds
|
|
1577
|
+
});
|
|
1578
|
+
}
|
|
1579
|
+
/**
|
|
1580
|
+
* Unsubscribe from specific channels
|
|
1581
|
+
*/
|
|
1582
|
+
async unsubscribeChannels(channelIds) {
|
|
1583
|
+
for (const id of channelIds) {
|
|
1584
|
+
this.activeSubscriptions.channels.delete(id);
|
|
1585
|
+
}
|
|
1586
|
+
return this.sendSubscription({
|
|
1587
|
+
type: "unsubscribe",
|
|
1588
|
+
subscriptionType: "channel",
|
|
1589
|
+
channels: channelIds
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* Subscribe to specific event types
|
|
1594
|
+
*/
|
|
1595
|
+
async subscribeEventTypes(eventTypes) {
|
|
1596
|
+
for (const type of eventTypes) {
|
|
1597
|
+
this.activeSubscriptions.eventTypes.add(type);
|
|
1598
|
+
}
|
|
1599
|
+
return this.sendSubscription({
|
|
1600
|
+
type: "subscribe",
|
|
1601
|
+
subscriptionType: "event_type",
|
|
1602
|
+
eventTypes
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1606
|
+
* Unsubscribe from specific event types
|
|
1607
|
+
*/
|
|
1608
|
+
async unsubscribeEventTypes(eventTypes) {
|
|
1609
|
+
for (const type of eventTypes) {
|
|
1610
|
+
this.activeSubscriptions.eventTypes.delete(type);
|
|
1611
|
+
}
|
|
1612
|
+
return this.sendSubscription({
|
|
1613
|
+
type: "unsubscribe",
|
|
1614
|
+
subscriptionType: "event_type",
|
|
1615
|
+
eventTypes
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
/**
|
|
1619
|
+
* Subscribe to channels and/or event types
|
|
1620
|
+
*/
|
|
1621
|
+
async subscribe(options) {
|
|
1622
|
+
const promises = [];
|
|
1623
|
+
if (options.channels?.length) {
|
|
1624
|
+
promises.push(this.subscribeChannels(options.channels));
|
|
1625
|
+
}
|
|
1626
|
+
if (options.eventTypes?.length) {
|
|
1627
|
+
promises.push(this.subscribeEventTypes(options.eventTypes));
|
|
1628
|
+
}
|
|
1629
|
+
if (promises.length === 0) {
|
|
1630
|
+
throw new Error("Must specify at least one channel or event type");
|
|
1631
|
+
}
|
|
1632
|
+
await Promise.all(promises);
|
|
1633
|
+
}
|
|
1634
|
+
/**
|
|
1635
|
+
* Unsubscribe from channels and/or event types
|
|
1636
|
+
*/
|
|
1637
|
+
async unsubscribe(options) {
|
|
1638
|
+
const promises = [];
|
|
1639
|
+
if (options.channels?.length) {
|
|
1640
|
+
promises.push(this.unsubscribeChannels(options.channels));
|
|
1641
|
+
}
|
|
1642
|
+
if (options.eventTypes?.length) {
|
|
1643
|
+
promises.push(this.unsubscribeEventTypes(options.eventTypes));
|
|
1644
|
+
}
|
|
1645
|
+
await Promise.all(promises);
|
|
1646
|
+
}
|
|
1647
|
+
/**
|
|
1648
|
+
* Add event handler
|
|
1649
|
+
*/
|
|
1650
|
+
onEvent(handler) {
|
|
1651
|
+
this.eventHandlers.add(handler);
|
|
1652
|
+
return () => this.eventHandlers.delete(handler);
|
|
1653
|
+
}
|
|
1654
|
+
/**
|
|
1655
|
+
* Add connection state change handler
|
|
1656
|
+
*/
|
|
1657
|
+
onStateChange(handler) {
|
|
1658
|
+
this.stateHandlers.add(handler);
|
|
1659
|
+
return () => this.stateHandlers.delete(handler);
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Add error handler
|
|
1663
|
+
*/
|
|
1664
|
+
onError(handler) {
|
|
1665
|
+
this.errorHandlers.add(handler);
|
|
1666
|
+
return () => this.errorHandlers.delete(handler);
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Remove all handlers
|
|
1670
|
+
*/
|
|
1671
|
+
removeAllHandlers() {
|
|
1672
|
+
this.eventHandlers.clear();
|
|
1673
|
+
this.stateHandlers.clear();
|
|
1674
|
+
this.errorHandlers.clear();
|
|
1675
|
+
}
|
|
1676
|
+
// Private methods
|
|
1677
|
+
setState(state) {
|
|
1678
|
+
if (this.state === state) return;
|
|
1679
|
+
this.state = state;
|
|
1680
|
+
for (const handler of this.stateHandlers) {
|
|
1681
|
+
try {
|
|
1682
|
+
handler(state);
|
|
1683
|
+
} catch (error) {
|
|
1684
|
+
console.error("State handler error:", error);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
emitError(error) {
|
|
1689
|
+
for (const handler of this.errorHandlers) {
|
|
1690
|
+
try {
|
|
1691
|
+
handler(error);
|
|
1692
|
+
} catch (e) {
|
|
1693
|
+
console.error("Error handler error:", e);
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
emitEvent(event) {
|
|
1698
|
+
for (const handler of this.eventHandlers) {
|
|
1699
|
+
try {
|
|
1700
|
+
handler(event);
|
|
1701
|
+
} catch (error) {
|
|
1702
|
+
console.error("Event handler error:", error);
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
handleMessage(data) {
|
|
1707
|
+
try {
|
|
1708
|
+
const message = JSON.parse(typeof data === "string" ? data : data.toString());
|
|
1709
|
+
if (message.type === "connected") {
|
|
1710
|
+
return;
|
|
1711
|
+
}
|
|
1712
|
+
if (message.type === "subscription_confirmed" || message.type === "subscription_error") {
|
|
1713
|
+
const confirmation = message;
|
|
1714
|
+
const key = this.getSubscriptionKey(confirmation);
|
|
1715
|
+
const pending = this.pendingSubscriptions.get(key);
|
|
1716
|
+
if (pending) {
|
|
1717
|
+
this.pendingSubscriptions.delete(key);
|
|
1718
|
+
if (confirmation.type === "subscription_error") {
|
|
1719
|
+
pending.reject(new Error(confirmation.error || "Subscription failed"));
|
|
1720
|
+
} else {
|
|
1721
|
+
pending.resolve(confirmation);
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
return;
|
|
1725
|
+
}
|
|
1726
|
+
if (message.type === "event" && message.data) {
|
|
1727
|
+
const eventData = message.data;
|
|
1728
|
+
const event = {
|
|
1729
|
+
id: eventData.id,
|
|
1730
|
+
tenantId: eventData.tenant_id || eventData.tenantId,
|
|
1731
|
+
channelId: eventData.channel_id || eventData.channelId,
|
|
1732
|
+
canonicalType: eventData.canonical_type || eventData.canonicalType,
|
|
1733
|
+
eventType: eventData.event_type || eventData.eventType,
|
|
1734
|
+
direction: eventData.direction,
|
|
1735
|
+
payload: eventData.payload,
|
|
1736
|
+
enrichment: eventData.enrichment,
|
|
1737
|
+
timestamp: eventData.timestamp,
|
|
1738
|
+
processedAt: eventData.processed_at || eventData.processedAt
|
|
1739
|
+
};
|
|
1740
|
+
this.emitEvent(event);
|
|
1741
|
+
}
|
|
1742
|
+
} catch (error) {
|
|
1743
|
+
this.emitError(new Error(`Failed to parse message: ${error}`));
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
async sendSubscription(command) {
|
|
1747
|
+
if (!this.ws || this.state !== "connected") {
|
|
1748
|
+
throw new Error("Not connected");
|
|
1749
|
+
}
|
|
1750
|
+
return new Promise((resolve, reject) => {
|
|
1751
|
+
const key = this.getSubscriptionKey({
|
|
1752
|
+
action: command.type,
|
|
1753
|
+
subscriptionType: command.subscriptionType
|
|
1754
|
+
});
|
|
1755
|
+
const timeout = setTimeout(() => {
|
|
1756
|
+
this.pendingSubscriptions.delete(key);
|
|
1757
|
+
reject(new Error("Subscription timeout"));
|
|
1758
|
+
}, 1e4);
|
|
1759
|
+
this.pendingSubscriptions.set(key, {
|
|
1760
|
+
resolve: (confirmation) => {
|
|
1761
|
+
clearTimeout(timeout);
|
|
1762
|
+
resolve(confirmation);
|
|
1763
|
+
},
|
|
1764
|
+
reject: (error) => {
|
|
1765
|
+
clearTimeout(timeout);
|
|
1766
|
+
reject(error);
|
|
1767
|
+
}
|
|
1768
|
+
});
|
|
1769
|
+
this.ws.send(JSON.stringify(command));
|
|
1770
|
+
});
|
|
1771
|
+
}
|
|
1772
|
+
getSubscriptionKey(confirmation) {
|
|
1773
|
+
return `${confirmation.action}:${confirmation.subscriptionType}`;
|
|
1774
|
+
}
|
|
1775
|
+
shouldReconnect() {
|
|
1776
|
+
if (this.config.maxReconnectAttempts === 0) {
|
|
1777
|
+
return true;
|
|
1778
|
+
}
|
|
1779
|
+
return this.reconnectAttempts < this.config.maxReconnectAttempts;
|
|
1780
|
+
}
|
|
1781
|
+
scheduleReconnect() {
|
|
1782
|
+
if (this.reconnectTimeout) return;
|
|
1783
|
+
this.setState("reconnecting");
|
|
1784
|
+
this.reconnectAttempts++;
|
|
1785
|
+
const delay = Math.min(
|
|
1786
|
+
this.config.reconnectInterval * Math.pow(2, this.reconnectAttempts - 1),
|
|
1787
|
+
MAX_RECONNECT_INTERVAL
|
|
1788
|
+
);
|
|
1789
|
+
this.reconnectTimeout = setTimeout(async () => {
|
|
1790
|
+
this.reconnectTimeout = null;
|
|
1791
|
+
try {
|
|
1792
|
+
await this.connect();
|
|
1793
|
+
} catch {
|
|
1794
|
+
}
|
|
1795
|
+
}, delay);
|
|
1796
|
+
}
|
|
1797
|
+
clearReconnectTimeout() {
|
|
1798
|
+
if (this.reconnectTimeout) {
|
|
1799
|
+
clearTimeout(this.reconnectTimeout);
|
|
1800
|
+
this.reconnectTimeout = null;
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
async resubscribe() {
|
|
1804
|
+
try {
|
|
1805
|
+
if (this.activeSubscriptions.subscribedToAll) {
|
|
1806
|
+
await this.subscribeAll();
|
|
1807
|
+
}
|
|
1808
|
+
if (this.activeSubscriptions.channels.size > 0) {
|
|
1809
|
+
await this.subscribeChannels(Array.from(this.activeSubscriptions.channels));
|
|
1810
|
+
}
|
|
1811
|
+
if (this.activeSubscriptions.eventTypes.size > 0) {
|
|
1812
|
+
await this.subscribeEventTypes(Array.from(this.activeSubscriptions.eventTypes));
|
|
1813
|
+
}
|
|
1814
|
+
} catch (error) {
|
|
1815
|
+
this.emitError(new Error(`Failed to resubscribe: ${error}`));
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
startHeartbeat() {
|
|
1819
|
+
this.stopHeartbeat();
|
|
1820
|
+
this.heartbeatInterval = setInterval(() => {
|
|
1821
|
+
if (this.ws && this.state === "connected") {
|
|
1822
|
+
if (typeof this.ws.ping === "function") {
|
|
1823
|
+
this.ws.ping();
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
}, this.config.heartbeatInterval);
|
|
1827
|
+
}
|
|
1828
|
+
stopHeartbeat() {
|
|
1829
|
+
if (this.heartbeatInterval) {
|
|
1830
|
+
clearInterval(this.heartbeatInterval);
|
|
1831
|
+
this.heartbeatInterval = null;
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
};
|
|
1835
|
+
function createRealtimeResource(tokenFetcher, config) {
|
|
1836
|
+
return new RealtimeResource(tokenFetcher, config);
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
// src/client.ts
|
|
1840
|
+
var VeroAI = class _VeroAI {
|
|
1841
|
+
http;
|
|
1842
|
+
/** Manage communication channels (email, SMS, WhatsApp, etc.) */
|
|
1843
|
+
channels;
|
|
1844
|
+
/** Query activity events and analytics */
|
|
1845
|
+
events;
|
|
1846
|
+
/** Send messages through channels */
|
|
1847
|
+
messages;
|
|
1848
|
+
/** Manage webhook endpoints for real-time notifications */
|
|
1849
|
+
webhooks;
|
|
1850
|
+
/** Manage API keys for authentication */
|
|
1851
|
+
apiKeys;
|
|
1852
|
+
/** Manage email domains for sending */
|
|
1853
|
+
domains;
|
|
1854
|
+
/** Voice phone numbers and call management */
|
|
1855
|
+
voice;
|
|
1856
|
+
/** AI agent configurations */
|
|
1857
|
+
agents;
|
|
1858
|
+
/** Real-time event subscriptions via WebSocket */
|
|
1859
|
+
realtime;
|
|
1860
|
+
/**
|
|
1861
|
+
* Create a new VeroAI client instance
|
|
1862
|
+
*
|
|
1863
|
+
* @param config - Configuration options
|
|
1864
|
+
* @param config.apiKey - Your API key (required)
|
|
1865
|
+
* @param config.baseUrl - Custom API base URL (optional)
|
|
1866
|
+
* @param config.timeout - Request timeout in ms (default: 30000)
|
|
1867
|
+
* @param config.maxRetries - Max retry attempts (default: 3)
|
|
1868
|
+
* @param config.realtime - Realtime WebSocket configuration
|
|
1869
|
+
*
|
|
1870
|
+
* @example
|
|
1871
|
+
* ```typescript
|
|
1872
|
+
* // Basic usage
|
|
1873
|
+
* const veroai = new VeroAI({ apiKey: 'sk_live_...' });
|
|
1874
|
+
*
|
|
1875
|
+
* // With custom options
|
|
1876
|
+
* const veroai = new VeroAI({
|
|
1877
|
+
* apiKey: 'sk_test_...',
|
|
1878
|
+
* baseUrl: 'https://api.staging.veroai.dev',
|
|
1879
|
+
* timeout: 60000,
|
|
1880
|
+
* maxRetries: 5,
|
|
1881
|
+
* });
|
|
1882
|
+
* ```
|
|
1883
|
+
*/
|
|
1884
|
+
constructor(config) {
|
|
1885
|
+
this.http = new HttpClient(config);
|
|
1886
|
+
this.channels = new ChannelsResource(this.http);
|
|
1887
|
+
this.events = new EventsResource(this.http);
|
|
1888
|
+
this.messages = new MessagesResource(this.http);
|
|
1889
|
+
this.webhooks = new WebhooksResource(this.http);
|
|
1890
|
+
this.apiKeys = new ApiKeysResource(this.http);
|
|
1891
|
+
this.domains = new DomainsResource(this.http);
|
|
1892
|
+
this.voice = new VoiceResource(this.http);
|
|
1893
|
+
this.agents = new AgentsResource(this.http);
|
|
1894
|
+
const tokenFetcher = async () => {
|
|
1895
|
+
const response = await this.http.post("/v1/realtime/auth", {});
|
|
1896
|
+
return response.token;
|
|
1897
|
+
};
|
|
1898
|
+
this.realtime = new RealtimeResource(tokenFetcher, config.realtime);
|
|
1899
|
+
}
|
|
1900
|
+
/**
|
|
1901
|
+
* Create a client from environment variables
|
|
1902
|
+
*
|
|
1903
|
+
* Looks for VEROAI_API_KEY environment variable
|
|
1904
|
+
*
|
|
1905
|
+
* @example
|
|
1906
|
+
* ```typescript
|
|
1907
|
+
* // Reads VEROAI_API_KEY from environment
|
|
1908
|
+
* const veroai = VeroAI.fromEnv();
|
|
1909
|
+
* ```
|
|
1910
|
+
*/
|
|
1911
|
+
static fromEnv(overrides) {
|
|
1912
|
+
const apiKey = process.env.VEROAI_API_KEY;
|
|
1913
|
+
if (!apiKey) {
|
|
1914
|
+
throw new Error("VEROAI_API_KEY environment variable is not set");
|
|
1915
|
+
}
|
|
1916
|
+
return new _VeroAI({
|
|
1917
|
+
apiKey,
|
|
1918
|
+
baseUrl: process.env.VEROAI_BASE_URL,
|
|
1919
|
+
...overrides
|
|
1920
|
+
});
|
|
1921
|
+
}
|
|
1922
|
+
};
|
|
1923
|
+
|
|
1924
|
+
export { APIError, AuthenticationError, AuthorizationError, NetworkError, NotFoundError, RateLimitError, RealtimeResource, ServerError, TimeoutError, ValidationError, VeroAI, VeroAIError, VoiceCallsResource, VoiceNumbersResource, VoiceResource, createRealtimeResource, VeroAI as default };
|
|
1925
|
+
//# sourceMappingURL=index.js.map
|
|
1926
|
+
//# sourceMappingURL=index.js.map
|