@vapi-ai/web 2.3.6 → 2.3.8
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/api.ts +13044 -9544
- package/dist/api.d.ts +4692 -3038
- package/dist/api.js +447 -256
- package/dist/vapi.d.ts +27 -2
- package/dist/vapi.js +332 -34
- package/package.json +16 -9
- package/vapi.ts +389 -36
- package/.prettierrc +0 -5
- package/__tests__/vapi.test.ts +0 -56
- package/dist/__tests__/vapi.test.d.ts +0 -1
- package/dist/__tests__/vapi.test.js +0 -51
- package/generate-api.sh +0 -11
- package/jest.config.ts +0 -11
- package/tsconfig.json +0 -109
- package/update.sh +0 -1
package/vapi.ts
CHANGED
|
@@ -55,7 +55,36 @@ type VapiEventNames =
|
|
|
55
55
|
| 'message'
|
|
56
56
|
| 'video'
|
|
57
57
|
| 'error'
|
|
58
|
-
| '
|
|
58
|
+
| 'camera-error'
|
|
59
|
+
| 'network-quality-change'
|
|
60
|
+
| 'network-connection'
|
|
61
|
+
| 'daily-participant-updated'
|
|
62
|
+
| 'call-start-progress'
|
|
63
|
+
| 'call-start-success'
|
|
64
|
+
| 'call-start-failed';
|
|
65
|
+
|
|
66
|
+
interface CallStartProgressEvent {
|
|
67
|
+
stage: string;
|
|
68
|
+
status: 'started' | 'completed' | 'failed';
|
|
69
|
+
duration?: number;
|
|
70
|
+
timestamp: string;
|
|
71
|
+
metadata?: Record<string, any>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface CallStartSuccessEvent {
|
|
75
|
+
totalDuration: number;
|
|
76
|
+
callId?: string;
|
|
77
|
+
timestamp: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface CallStartFailedEvent {
|
|
81
|
+
stage: string;
|
|
82
|
+
totalDuration: number;
|
|
83
|
+
error: string;
|
|
84
|
+
errorStack?: string;
|
|
85
|
+
timestamp: string;
|
|
86
|
+
context: Record<string, any>;
|
|
87
|
+
}
|
|
59
88
|
|
|
60
89
|
type VapiEventListeners = {
|
|
61
90
|
'call-end': () => void;
|
|
@@ -66,7 +95,13 @@ type VapiEventListeners = {
|
|
|
66
95
|
video: (track: MediaStreamTrack) => void;
|
|
67
96
|
message: (message: any) => void;
|
|
68
97
|
error: (error: any) => void;
|
|
98
|
+
'camera-error': (error: any) => void;
|
|
99
|
+
'network-quality-change': (event: any) => void;
|
|
100
|
+
'network-connection': (event: any) => void;
|
|
69
101
|
'daily-participant-updated': (participant: DailyParticipant) => void;
|
|
102
|
+
'call-start-progress': (event: CallStartProgressEvent) => void;
|
|
103
|
+
'call-start-success': (event: CallStartSuccessEvent) => void;
|
|
104
|
+
'call-start-failed': (event: CallStartFailedEvent) => void;
|
|
70
105
|
};
|
|
71
106
|
|
|
72
107
|
async function startAudioPlayer(
|
|
@@ -203,16 +238,53 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
203
238
|
workflow?: CreateWorkflowDTO | string,
|
|
204
239
|
workflowOverrides?: WorkflowOverrides,
|
|
205
240
|
): Promise<Call | null> {
|
|
241
|
+
const startTime = Date.now();
|
|
242
|
+
|
|
243
|
+
// Input validation with detailed error messages
|
|
206
244
|
if (!assistant && !squad && !workflow) {
|
|
207
|
-
|
|
245
|
+
const error = new Error('Assistant or Squad or Workflow must be provided.');
|
|
246
|
+
this.emit('error', {
|
|
247
|
+
type: 'validation-error',
|
|
248
|
+
stage: 'input-validation',
|
|
249
|
+
message: error.message,
|
|
250
|
+
timestamp: new Date().toISOString()
|
|
251
|
+
});
|
|
252
|
+
throw error;
|
|
208
253
|
}
|
|
209
254
|
|
|
210
255
|
if (this.started) {
|
|
256
|
+
this.emit('call-start-progress', {
|
|
257
|
+
stage: 'validation',
|
|
258
|
+
status: 'failed',
|
|
259
|
+
timestamp: new Date().toISOString(),
|
|
260
|
+
metadata: { reason: 'already-started' }
|
|
261
|
+
});
|
|
211
262
|
return null;
|
|
212
263
|
}
|
|
264
|
+
|
|
265
|
+
this.emit('call-start-progress', {
|
|
266
|
+
stage: 'initialization',
|
|
267
|
+
status: 'started',
|
|
268
|
+
timestamp: new Date().toISOString(),
|
|
269
|
+
metadata: {
|
|
270
|
+
hasAssistant: !!assistant,
|
|
271
|
+
hasSquad: !!squad,
|
|
272
|
+
hasWorkflow: !!workflow
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
|
|
213
276
|
this.started = true;
|
|
214
277
|
|
|
215
278
|
try {
|
|
279
|
+
// Stage 1: Create web call
|
|
280
|
+
this.emit('call-start-progress', {
|
|
281
|
+
stage: 'web-call-creation',
|
|
282
|
+
status: 'started',
|
|
283
|
+
timestamp: new Date().toISOString()
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
const webCallStartTime = Date.now();
|
|
287
|
+
|
|
216
288
|
const webCall = (
|
|
217
289
|
await client.call.callControllerCreateWebCall({
|
|
218
290
|
assistant: typeof assistant === 'string' ? undefined : assistant,
|
|
@@ -225,8 +297,27 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
225
297
|
workflowOverrides,
|
|
226
298
|
})
|
|
227
299
|
).data;
|
|
300
|
+
|
|
301
|
+
const webCallDuration = Date.now() - webCallStartTime;
|
|
302
|
+
this.emit('call-start-progress', {
|
|
303
|
+
stage: 'web-call-creation',
|
|
304
|
+
status: 'completed',
|
|
305
|
+
duration: webCallDuration,
|
|
306
|
+
timestamp: new Date().toISOString(),
|
|
307
|
+
metadata: {
|
|
308
|
+
callId: webCall?.id || 'unknown',
|
|
309
|
+
videoRecordingEnabled: webCall?.artifactPlan?.videoRecordingEnabled ?? false,
|
|
310
|
+
voiceProvider: webCall?.assistant?.voice?.provider || 'unknown'
|
|
311
|
+
}
|
|
312
|
+
});
|
|
228
313
|
|
|
229
314
|
if (this.call) {
|
|
315
|
+
this.emit('call-start-progress', {
|
|
316
|
+
stage: 'daily-call-object-creation',
|
|
317
|
+
status: 'started',
|
|
318
|
+
timestamp: new Date().toISOString(),
|
|
319
|
+
metadata: { action: 'cleanup-existing' }
|
|
320
|
+
});
|
|
230
321
|
this.cleanup();
|
|
231
322
|
}
|
|
232
323
|
|
|
@@ -235,11 +326,53 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
235
326
|
|
|
236
327
|
const isVideoEnabled = webCall?.assistant?.voice?.provider === 'tavus';
|
|
237
328
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
329
|
+
// Stage 2: Create Daily call object
|
|
330
|
+
this.emit('call-start-progress', {
|
|
331
|
+
stage: 'daily-call-object-creation',
|
|
332
|
+
status: 'started',
|
|
333
|
+
timestamp: new Date().toISOString(),
|
|
334
|
+
metadata: {
|
|
335
|
+
audioSource: this.dailyCallObject.audioSource ?? true,
|
|
336
|
+
videoSource: this.dailyCallObject.videoSource ?? isVideoRecordingEnabled,
|
|
337
|
+
isVideoRecordingEnabled,
|
|
338
|
+
isVideoEnabled
|
|
339
|
+
}
|
|
242
340
|
});
|
|
341
|
+
|
|
342
|
+
const dailyCallStartTime = Date.now();
|
|
343
|
+
|
|
344
|
+
try {
|
|
345
|
+
this.call = DailyIframe.createCallObject({
|
|
346
|
+
audioSource: this.dailyCallObject.audioSource ?? true,
|
|
347
|
+
videoSource: this.dailyCallObject.videoSource ?? isVideoRecordingEnabled,
|
|
348
|
+
dailyConfig: this.dailyCallConfig,
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
const dailyCallDuration = Date.now() - dailyCallStartTime;
|
|
352
|
+
this.emit('call-start-progress', {
|
|
353
|
+
stage: 'daily-call-object-creation',
|
|
354
|
+
status: 'completed',
|
|
355
|
+
duration: dailyCallDuration,
|
|
356
|
+
timestamp: new Date().toISOString()
|
|
357
|
+
});
|
|
358
|
+
} catch (error) {
|
|
359
|
+
const dailyCallDuration = Date.now() - dailyCallStartTime;
|
|
360
|
+
this.emit('call-start-progress', {
|
|
361
|
+
stage: 'daily-call-object-creation',
|
|
362
|
+
status: 'failed',
|
|
363
|
+
duration: dailyCallDuration,
|
|
364
|
+
timestamp: new Date().toISOString(),
|
|
365
|
+
metadata: { error: error?.toString() }
|
|
366
|
+
});
|
|
367
|
+
this.emit('error', {
|
|
368
|
+
type: 'daily-call-object-creation-error',
|
|
369
|
+
stage: 'daily-call-object-creation',
|
|
370
|
+
error,
|
|
371
|
+
timestamp: new Date().toISOString()
|
|
372
|
+
});
|
|
373
|
+
throw error;
|
|
374
|
+
}
|
|
375
|
+
|
|
243
376
|
this.call.iframe()?.style.setProperty('display', 'none');
|
|
244
377
|
|
|
245
378
|
this.call.on('left-meeting', () => {
|
|
@@ -266,7 +399,15 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
266
399
|
});
|
|
267
400
|
|
|
268
401
|
this.call.on('camera-error', (error: any) => {
|
|
269
|
-
this.emit('error', error);
|
|
402
|
+
this.emit('camera-error', error);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
this.call.on('network-quality-change', (event: any) => {
|
|
406
|
+
this.emit('network-quality-change', event);
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
this.call.on('network-connection', (event: any) => {
|
|
410
|
+
this.emit('network-connection', event);
|
|
270
411
|
});
|
|
271
412
|
|
|
272
413
|
this.call.on('track-started', async (e) => {
|
|
@@ -312,41 +453,182 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
312
453
|
destroyAudioPlayer(e.participant.session_id);
|
|
313
454
|
});
|
|
314
455
|
|
|
315
|
-
//
|
|
316
|
-
|
|
317
|
-
|
|
456
|
+
// Stage 3: Mobile device handling and permissions
|
|
457
|
+
const isMobile = this.isMobileDevice();
|
|
458
|
+
this.emit('call-start-progress', {
|
|
459
|
+
stage: 'mobile-permissions',
|
|
460
|
+
status: 'started',
|
|
461
|
+
timestamp: new Date().toISOString(),
|
|
462
|
+
metadata: { isMobile }
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
if (isMobile) {
|
|
466
|
+
const mobileWaitStartTime = Date.now();
|
|
318
467
|
await this.sleep(1000);
|
|
468
|
+
const mobileWaitDuration = Date.now() - mobileWaitStartTime;
|
|
469
|
+
this.emit('call-start-progress', {
|
|
470
|
+
stage: 'mobile-permissions',
|
|
471
|
+
status: 'completed',
|
|
472
|
+
duration: mobileWaitDuration,
|
|
473
|
+
timestamp: new Date().toISOString(),
|
|
474
|
+
metadata: { action: 'permissions-wait' }
|
|
475
|
+
});
|
|
476
|
+
} else {
|
|
477
|
+
this.emit('call-start-progress', {
|
|
478
|
+
stage: 'mobile-permissions',
|
|
479
|
+
status: 'completed',
|
|
480
|
+
timestamp: new Date().toISOString(),
|
|
481
|
+
metadata: { action: 'skipped-not-mobile' }
|
|
482
|
+
});
|
|
319
483
|
}
|
|
320
484
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
485
|
+
// Stage 4: Join the call
|
|
486
|
+
this.emit('call-start-progress', {
|
|
487
|
+
stage: 'daily-call-join',
|
|
488
|
+
status: 'started',
|
|
489
|
+
timestamp: new Date().toISOString()
|
|
325
490
|
});
|
|
491
|
+
|
|
492
|
+
const joinStartTime = Date.now();
|
|
493
|
+
|
|
494
|
+
try {
|
|
495
|
+
await this.call.join({
|
|
496
|
+
// @ts-expect-error This exists
|
|
497
|
+
url: webCall.webCallUrl,
|
|
498
|
+
subscribeToTracksAutomatically: false,
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
const joinDuration = Date.now() - joinStartTime;
|
|
502
|
+
this.emit('call-start-progress', {
|
|
503
|
+
stage: 'daily-call-join',
|
|
504
|
+
status: 'completed',
|
|
505
|
+
duration: joinDuration,
|
|
506
|
+
timestamp: new Date().toISOString()
|
|
507
|
+
});
|
|
508
|
+
} catch (error) {
|
|
509
|
+
const joinDuration = Date.now() - joinStartTime;
|
|
510
|
+
this.emit('call-start-progress', {
|
|
511
|
+
stage: 'daily-call-join',
|
|
512
|
+
status: 'failed',
|
|
513
|
+
duration: joinDuration,
|
|
514
|
+
timestamp: new Date().toISOString(),
|
|
515
|
+
metadata: { error: error?.toString() }
|
|
516
|
+
});
|
|
517
|
+
this.emit('error', {
|
|
518
|
+
type: 'daily-call-join-error',
|
|
519
|
+
stage: 'daily-call-join',
|
|
520
|
+
error,
|
|
521
|
+
duration: joinDuration,
|
|
522
|
+
timestamp: new Date().toISOString()
|
|
523
|
+
});
|
|
524
|
+
throw error;
|
|
525
|
+
}
|
|
326
526
|
|
|
527
|
+
// Stage 5: Video recording setup (if enabled)
|
|
327
528
|
if (isVideoRecordingEnabled) {
|
|
529
|
+
this.emit('call-start-progress', {
|
|
530
|
+
stage: 'video-recording-setup',
|
|
531
|
+
status: 'started',
|
|
532
|
+
timestamp: new Date().toISOString()
|
|
533
|
+
});
|
|
534
|
+
|
|
328
535
|
const recordingRequestedTime = new Date().getTime();
|
|
536
|
+
const recordingStartTime = Date.now();
|
|
329
537
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
538
|
+
try {
|
|
539
|
+
this.call.startRecording({
|
|
540
|
+
width: 1280,
|
|
541
|
+
height: 720,
|
|
542
|
+
backgroundColor: '#FF1F2D3D',
|
|
543
|
+
layout: {
|
|
544
|
+
preset: 'default',
|
|
545
|
+
},
|
|
546
|
+
});
|
|
338
547
|
|
|
339
|
-
|
|
340
|
-
this.
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
548
|
+
const recordingSetupDuration = Date.now() - recordingStartTime;
|
|
549
|
+
this.emit('call-start-progress', {
|
|
550
|
+
stage: 'video-recording-setup',
|
|
551
|
+
status: 'completed',
|
|
552
|
+
duration: recordingSetupDuration,
|
|
553
|
+
timestamp: new Date().toISOString()
|
|
345
554
|
});
|
|
555
|
+
|
|
556
|
+
this.call.on('recording-started', () => {
|
|
557
|
+
const totalRecordingDelay = (new Date().getTime() - recordingRequestedTime) / 1000;
|
|
558
|
+
this.emit('call-start-progress', {
|
|
559
|
+
stage: 'video-recording-started',
|
|
560
|
+
status: 'completed',
|
|
561
|
+
timestamp: new Date().toISOString(),
|
|
562
|
+
metadata: { delaySeconds: totalRecordingDelay }
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
this.send({
|
|
566
|
+
type: 'control',
|
|
567
|
+
control: 'say-first-message',
|
|
568
|
+
videoRecordingStartDelaySeconds: totalRecordingDelay,
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
} catch (error) {
|
|
572
|
+
const recordingSetupDuration = Date.now() - recordingStartTime;
|
|
573
|
+
this.emit('call-start-progress', {
|
|
574
|
+
stage: 'video-recording-setup',
|
|
575
|
+
status: 'failed',
|
|
576
|
+
duration: recordingSetupDuration,
|
|
577
|
+
timestamp: new Date().toISOString(),
|
|
578
|
+
metadata: { error: error?.toString() }
|
|
579
|
+
});
|
|
580
|
+
this.emit('error', {
|
|
581
|
+
type: 'video-recording-setup-error',
|
|
582
|
+
stage: 'video-recording-setup',
|
|
583
|
+
error,
|
|
584
|
+
timestamp: new Date().toISOString()
|
|
585
|
+
});
|
|
586
|
+
// Don't throw here, video recording is optional
|
|
587
|
+
}
|
|
588
|
+
} else {
|
|
589
|
+
this.emit('call-start-progress', {
|
|
590
|
+
stage: 'video-recording-setup',
|
|
591
|
+
status: 'completed',
|
|
592
|
+
timestamp: new Date().toISOString(),
|
|
593
|
+
metadata: { action: 'skipped-not-enabled' }
|
|
346
594
|
});
|
|
347
595
|
}
|
|
348
596
|
|
|
349
|
-
|
|
597
|
+
// Stage 6: Audio level observer setup
|
|
598
|
+
this.emit('call-start-progress', {
|
|
599
|
+
stage: 'audio-observer-setup',
|
|
600
|
+
status: 'started',
|
|
601
|
+
timestamp: new Date().toISOString()
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
const audioObserverStartTime = Date.now();
|
|
605
|
+
|
|
606
|
+
try {
|
|
607
|
+
this.call.startRemoteParticipantsAudioLevelObserver(100);
|
|
608
|
+
const audioObserverDuration = Date.now() - audioObserverStartTime;
|
|
609
|
+
this.emit('call-start-progress', {
|
|
610
|
+
stage: 'audio-observer-setup',
|
|
611
|
+
status: 'completed',
|
|
612
|
+
duration: audioObserverDuration,
|
|
613
|
+
timestamp: new Date().toISOString()
|
|
614
|
+
});
|
|
615
|
+
} catch (error) {
|
|
616
|
+
const audioObserverDuration = Date.now() - audioObserverStartTime;
|
|
617
|
+
this.emit('call-start-progress', {
|
|
618
|
+
stage: 'audio-observer-setup',
|
|
619
|
+
status: 'failed',
|
|
620
|
+
duration: audioObserverDuration,
|
|
621
|
+
timestamp: new Date().toISOString(),
|
|
622
|
+
metadata: { error: error?.toString() }
|
|
623
|
+
});
|
|
624
|
+
this.emit('error', {
|
|
625
|
+
type: 'audio-observer-setup-error',
|
|
626
|
+
stage: 'audio-observer-setup',
|
|
627
|
+
error,
|
|
628
|
+
timestamp: new Date().toISOString()
|
|
629
|
+
});
|
|
630
|
+
// Don't throw here, this is non-critical
|
|
631
|
+
}
|
|
350
632
|
|
|
351
633
|
this.call.on('remote-participants-audio-level', (e) => {
|
|
352
634
|
if (e) this.handleRemoteParticipantsAudioLevel(e);
|
|
@@ -371,18 +653,89 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
371
653
|
}
|
|
372
654
|
});
|
|
373
655
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
656
|
+
// Stage 7: Audio processing setup
|
|
657
|
+
this.emit('call-start-progress', {
|
|
658
|
+
stage: 'audio-processing-setup',
|
|
659
|
+
status: 'started',
|
|
660
|
+
timestamp: new Date().toISOString()
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
const audioProcessingStartTime = Date.now();
|
|
664
|
+
|
|
665
|
+
try {
|
|
666
|
+
this.call.updateInputSettings({
|
|
667
|
+
audio: {
|
|
668
|
+
processor: {
|
|
669
|
+
type: 'noise-cancellation',
|
|
670
|
+
},
|
|
378
671
|
},
|
|
379
|
-
}
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
const audioProcessingDuration = Date.now() - audioProcessingStartTime;
|
|
675
|
+
this.emit('call-start-progress', {
|
|
676
|
+
stage: 'audio-processing-setup',
|
|
677
|
+
status: 'completed',
|
|
678
|
+
duration: audioProcessingDuration,
|
|
679
|
+
timestamp: new Date().toISOString()
|
|
680
|
+
});
|
|
681
|
+
} catch (error) {
|
|
682
|
+
const audioProcessingDuration = Date.now() - audioProcessingStartTime;
|
|
683
|
+
this.emit('call-start-progress', {
|
|
684
|
+
stage: 'audio-processing-setup',
|
|
685
|
+
status: 'failed',
|
|
686
|
+
duration: audioProcessingDuration,
|
|
687
|
+
timestamp: new Date().toISOString(),
|
|
688
|
+
metadata: { error: error?.toString() }
|
|
689
|
+
});
|
|
690
|
+
this.emit('error', {
|
|
691
|
+
type: 'audio-processing-setup-error',
|
|
692
|
+
stage: 'audio-processing-setup',
|
|
693
|
+
error,
|
|
694
|
+
timestamp: new Date().toISOString()
|
|
695
|
+
});
|
|
696
|
+
// Don't throw here, this is non-critical
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
const totalDuration = Date.now() - startTime;
|
|
700
|
+
this.emit('call-start-success', {
|
|
701
|
+
totalDuration,
|
|
702
|
+
callId: webCall?.id || 'unknown',
|
|
703
|
+
timestamp: new Date().toISOString()
|
|
380
704
|
});
|
|
381
705
|
|
|
382
706
|
return webCall;
|
|
383
707
|
} catch (e) {
|
|
384
|
-
|
|
385
|
-
|
|
708
|
+
const totalDuration = Date.now() - startTime;
|
|
709
|
+
|
|
710
|
+
this.emit('call-start-failed', {
|
|
711
|
+
stage: 'unknown',
|
|
712
|
+
totalDuration,
|
|
713
|
+
error: e?.toString() || 'Unknown error occurred',
|
|
714
|
+
errorStack: e instanceof Error ? e.stack : 'No stack trace available',
|
|
715
|
+
timestamp: new Date().toISOString(),
|
|
716
|
+
context: {
|
|
717
|
+
hasAssistant: !!assistant,
|
|
718
|
+
hasSquad: !!squad,
|
|
719
|
+
hasWorkflow: !!workflow,
|
|
720
|
+
isMobile: this.isMobileDevice()
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
// Also emit the generic error event for backward compatibility
|
|
725
|
+
this.emit('error', {
|
|
726
|
+
type: 'start-method-error',
|
|
727
|
+
stage: 'unknown',
|
|
728
|
+
error: e,
|
|
729
|
+
totalDuration,
|
|
730
|
+
timestamp: new Date().toISOString(),
|
|
731
|
+
context: {
|
|
732
|
+
hasAssistant: !!assistant,
|
|
733
|
+
hasSquad: !!squad,
|
|
734
|
+
hasWorkflow: !!workflow,
|
|
735
|
+
isMobile: this.isMobileDevice()
|
|
736
|
+
}
|
|
737
|
+
});
|
|
738
|
+
|
|
386
739
|
this.cleanup();
|
|
387
740
|
return null;
|
|
388
741
|
}
|
package/.prettierrc
DELETED
package/__tests__/vapi.test.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import Vapi from "../vapi"; // Adjust the import path based on your project structure
|
|
2
|
-
|
|
3
|
-
describe("Vapi", () => {
|
|
4
|
-
let vapi: Vapi;
|
|
5
|
-
let mockCall: any;
|
|
6
|
-
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
// Mock the DailyCall object
|
|
9
|
-
mockCall = {
|
|
10
|
-
setLocalAudio: jest.fn(),
|
|
11
|
-
localAudio: jest.fn(),
|
|
12
|
-
};
|
|
13
|
-
// Initialize Vapi instance and inject the mock
|
|
14
|
-
vapi = new Vapi("dummy_token");
|
|
15
|
-
vapi["call"] = mockCall; // Assuming you have a way to set this for testing
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
describe("setMuted", () => {
|
|
19
|
-
it("should mute the audio", () => {
|
|
20
|
-
vapi["setMuted"](true);
|
|
21
|
-
expect(mockCall.setLocalAudio).toHaveBeenCalledWith(false);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("should unmute the audio", () => {
|
|
25
|
-
vapi["setMuted"](false);
|
|
26
|
-
expect(mockCall.setLocalAudio).toHaveBeenCalledWith(true);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("should handle errors when call object is not available", () => {
|
|
30
|
-
vapi["call"] = null; // Simulate call object not being available
|
|
31
|
-
expect(() => vapi["setMuted"](true)).toThrow(
|
|
32
|
-
"Call object is not available."
|
|
33
|
-
);
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
describe("isMuted", () => {
|
|
38
|
-
it("should return false if false", () => {
|
|
39
|
-
mockCall.localAudio.mockReturnValue(false); // Initially not muted
|
|
40
|
-
const res = vapi.isMuted();
|
|
41
|
-
expect(res).toBe(true);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("should return true if true", () => {
|
|
45
|
-
mockCall.localAudio.mockReturnValue(true); // Initially not muted
|
|
46
|
-
const res = vapi.isMuted();
|
|
47
|
-
expect(res).toBe(false);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("should return false if no call in progress", () => {
|
|
51
|
-
vapi["call"] = null; // Simulate call object not being available
|
|
52
|
-
const res = vapi.isMuted();
|
|
53
|
-
expect(res).toBe(false);
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const vapi_1 = __importDefault(require("../vapi")); // Adjust the import path based on your project structure
|
|
7
|
-
describe("Vapi", () => {
|
|
8
|
-
let vapi;
|
|
9
|
-
let mockCall;
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
// Mock the DailyCall object
|
|
12
|
-
mockCall = {
|
|
13
|
-
setLocalAudio: jest.fn(),
|
|
14
|
-
localAudio: jest.fn(),
|
|
15
|
-
};
|
|
16
|
-
// Initialize Vapi instance and inject the mock
|
|
17
|
-
vapi = new vapi_1.default("dummy_token");
|
|
18
|
-
vapi["call"] = mockCall; // Assuming you have a way to set this for testing
|
|
19
|
-
});
|
|
20
|
-
describe("setMuted", () => {
|
|
21
|
-
it("should mute the audio", () => {
|
|
22
|
-
vapi["setMuted"](true);
|
|
23
|
-
expect(mockCall.setLocalAudio).toHaveBeenCalledWith(false);
|
|
24
|
-
});
|
|
25
|
-
it("should unmute the audio", () => {
|
|
26
|
-
vapi["setMuted"](false);
|
|
27
|
-
expect(mockCall.setLocalAudio).toHaveBeenCalledWith(true);
|
|
28
|
-
});
|
|
29
|
-
it("should handle errors when call object is not available", () => {
|
|
30
|
-
vapi["call"] = null; // Simulate call object not being available
|
|
31
|
-
expect(() => vapi["setMuted"](true)).toThrow("Call object is not available.");
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
describe("isMuted", () => {
|
|
35
|
-
it("should return false if false", () => {
|
|
36
|
-
mockCall.localAudio.mockReturnValue(false); // Initially not muted
|
|
37
|
-
const res = vapi.isMuted();
|
|
38
|
-
expect(res).toBe(true);
|
|
39
|
-
});
|
|
40
|
-
it("should return true if true", () => {
|
|
41
|
-
mockCall.localAudio.mockReturnValue(true); // Initially not muted
|
|
42
|
-
const res = vapi.isMuted();
|
|
43
|
-
expect(res).toBe(false);
|
|
44
|
-
});
|
|
45
|
-
it("should return false if no call in progress", () => {
|
|
46
|
-
vapi["call"] = null; // Simulate call object not being available
|
|
47
|
-
const res = vapi.isMuted();
|
|
48
|
-
expect(res).toBe(false);
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
});
|
package/generate-api.sh
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# Step 1: Manually download the Swagger JSON file
|
|
2
|
-
curl -o ./api-extended-json.json https://api.vapi.ai/api-extended-json
|
|
3
|
-
|
|
4
|
-
# Step 2: Find and replace transcript[transcriptType=\"final\"] with transcript[transcriptType='final']
|
|
5
|
-
sed -i '' 's/transcript\[transcriptType=\\"final\\"\]/transcript[transcriptType='\''final'\'']/g' ./api-extended-json.json
|
|
6
|
-
|
|
7
|
-
# Step 3: Generate TypeScript API using the edited JSON file
|
|
8
|
-
npx swagger-typescript-api -p ./api-extended-json.json -o . -n api.ts
|
|
9
|
-
|
|
10
|
-
# Step 4: Remove the edited JSON file
|
|
11
|
-
rm ./api-extended-json.json
|
package/jest.config.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { Config } from '@jest/types';
|
|
2
|
-
|
|
3
|
-
const config: Config.InitialOptions = {
|
|
4
|
-
preset: 'ts-jest',
|
|
5
|
-
testEnvironment: 'node',
|
|
6
|
-
// Your Jest configuration options here
|
|
7
|
-
// For example, to match test files:
|
|
8
|
-
testMatch: ['**/__tests__/**/*.ts?(x)', '**/?(*.)+(spec|test).ts?(x)'],
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export default config;
|