@vapi-ai/web 2.4.0 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api.ts +4566 -1352
- package/dist/api.d.ts +3466 -907
- package/dist/api.js +259 -68
- package/dist/vapi.d.ts +91 -2
- package/dist/vapi.js +509 -1
- package/package.json +4 -3
- package/vapi.ts +602 -1
package/dist/vapi.js
CHANGED
|
@@ -90,7 +90,7 @@ class Vapi extends VapiEventEmitter {
|
|
|
90
90
|
async sleep(ms) {
|
|
91
91
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
92
92
|
}
|
|
93
|
-
async start(assistant, assistantOverrides, squad, workflow, workflowOverrides) {
|
|
93
|
+
async start(assistant, assistantOverrides, squad, workflow, workflowOverrides, options) {
|
|
94
94
|
const startTime = Date.now();
|
|
95
95
|
// Input validation with detailed error messages
|
|
96
96
|
if (!assistant && !squad && !workflow) {
|
|
@@ -140,6 +140,7 @@ class Vapi extends VapiEventEmitter {
|
|
|
140
140
|
workflow: typeof workflow === 'string' ? undefined : workflow,
|
|
141
141
|
workflowId: typeof workflow === 'string' ? workflow : undefined,
|
|
142
142
|
workflowOverrides,
|
|
143
|
+
roomDeleteOnUserLeaveEnabled: options?.roomDeleteOnUserLeaveEnabled,
|
|
143
144
|
})).data;
|
|
144
145
|
const webCallDuration = Date.now() - webCallStartTime;
|
|
145
146
|
this.emit('call-start-progress', {
|
|
@@ -587,6 +588,12 @@ class Vapi extends VapiEventEmitter {
|
|
|
587
588
|
this.speakingTimeout = null;
|
|
588
589
|
}, 1000);
|
|
589
590
|
}
|
|
591
|
+
/**
|
|
592
|
+
* Stops the call by destroying the Daily call object.
|
|
593
|
+
*
|
|
594
|
+
* If `roomDeleteOnUserLeaveEnabled` is set to `false`, the Vapi call will be kept alive, allowing reconnections to the same call using the `reconnect` method.
|
|
595
|
+
* If `roomDeleteOnUserLeaveEnabled` is set to `true`, the Vapi call will also be destroyed, preventing any reconnections.
|
|
596
|
+
*/
|
|
590
597
|
async stop() {
|
|
591
598
|
this.started = false;
|
|
592
599
|
if (this.call) {
|
|
@@ -595,6 +602,11 @@ class Vapi extends VapiEventEmitter {
|
|
|
595
602
|
}
|
|
596
603
|
this.speakingTimeout = null;
|
|
597
604
|
}
|
|
605
|
+
/**
|
|
606
|
+
* Sends a Live Call Control message to the Vapi server.
|
|
607
|
+
*
|
|
608
|
+
* Docs: https://docs.vapi.ai/calls/call-features
|
|
609
|
+
*/
|
|
598
610
|
send(message) {
|
|
599
611
|
this.call?.sendAppMessage(JSON.stringify(message));
|
|
600
612
|
}
|
|
@@ -616,6 +628,17 @@ class Vapi extends VapiEventEmitter {
|
|
|
616
628
|
interruptAssistantEnabled: interruptAssistantEnabled ?? false,
|
|
617
629
|
});
|
|
618
630
|
}
|
|
631
|
+
/**
|
|
632
|
+
* Ends the call immediately by sending a `end-call` message using Live Call Control, and destroys the Daily call object.
|
|
633
|
+
*
|
|
634
|
+
* This method always ends the call, regardless of the `roomDeleteOnUserLeaveEnabled` option.
|
|
635
|
+
*/
|
|
636
|
+
end() {
|
|
637
|
+
this.send({
|
|
638
|
+
type: 'end-call',
|
|
639
|
+
});
|
|
640
|
+
this.stop();
|
|
641
|
+
}
|
|
619
642
|
setInputDevicesAsync(options) {
|
|
620
643
|
return (0, daily_guards_1.safeSetInputDevicesAsync)(this.call, options);
|
|
621
644
|
}
|
|
@@ -654,5 +677,490 @@ class Vapi extends VapiEventEmitter {
|
|
|
654
677
|
stopScreenSharing() {
|
|
655
678
|
this.call?.stopScreenShare();
|
|
656
679
|
}
|
|
680
|
+
/**
|
|
681
|
+
* Reconnects to an active call.
|
|
682
|
+
*
|
|
683
|
+
*
|
|
684
|
+
* @param webCall
|
|
685
|
+
*/
|
|
686
|
+
async reconnect(webCall) {
|
|
687
|
+
const startTime = Date.now();
|
|
688
|
+
if (this.started) {
|
|
689
|
+
throw new Error('Cannot reconnect while a call is already in progress. Call stop() first.');
|
|
690
|
+
}
|
|
691
|
+
if (!webCall.webCallUrl) {
|
|
692
|
+
throw new Error('webCallUrl is required for reconnection.');
|
|
693
|
+
}
|
|
694
|
+
this.emit('call-start-progress', {
|
|
695
|
+
stage: 'reconnect-initialization',
|
|
696
|
+
status: 'started',
|
|
697
|
+
timestamp: new Date().toISOString(),
|
|
698
|
+
metadata: {
|
|
699
|
+
callId: webCall.id || 'unknown',
|
|
700
|
+
hasVideoRecording: !!webCall?.artifactPlan?.videoRecordingEnabled,
|
|
701
|
+
voiceProvider: webCall?.assistant?.voice?.provider || 'unknown'
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
this.started = true;
|
|
705
|
+
try {
|
|
706
|
+
// Clean up any existing call object
|
|
707
|
+
if (this.call) {
|
|
708
|
+
this.emit('call-start-progress', {
|
|
709
|
+
stage: 'cleanup-existing-call',
|
|
710
|
+
status: 'started',
|
|
711
|
+
timestamp: new Date().toISOString()
|
|
712
|
+
});
|
|
713
|
+
await this.cleanup();
|
|
714
|
+
this.emit('call-start-progress', {
|
|
715
|
+
stage: 'cleanup-existing-call',
|
|
716
|
+
status: 'completed',
|
|
717
|
+
timestamp: new Date().toISOString()
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
const isVideoRecordingEnabled = webCall?.artifactPlan?.videoRecordingEnabled ?? false;
|
|
721
|
+
const isVideoEnabled = webCall?.assistant?.voice?.provider === 'tavus';
|
|
722
|
+
// Stage 1: Create Daily call object
|
|
723
|
+
this.emit('call-start-progress', {
|
|
724
|
+
stage: 'daily-call-object-creation',
|
|
725
|
+
status: 'started',
|
|
726
|
+
timestamp: new Date().toISOString(),
|
|
727
|
+
metadata: {
|
|
728
|
+
audioSource: this.dailyCallObject.audioSource ?? true,
|
|
729
|
+
videoSource: this.dailyCallObject.videoSource ?? isVideoRecordingEnabled,
|
|
730
|
+
isVideoRecordingEnabled,
|
|
731
|
+
isVideoEnabled
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
const dailyCallStartTime = Date.now();
|
|
735
|
+
this.call = daily_js_1.default.createCallObject({
|
|
736
|
+
audioSource: this.dailyCallObject.audioSource ?? true,
|
|
737
|
+
videoSource: this.dailyCallObject.videoSource ?? isVideoRecordingEnabled,
|
|
738
|
+
dailyConfig: this.dailyCallConfig,
|
|
739
|
+
});
|
|
740
|
+
const dailyCallDuration = Date.now() - dailyCallStartTime;
|
|
741
|
+
this.emit('call-start-progress', {
|
|
742
|
+
stage: 'daily-call-object-creation',
|
|
743
|
+
status: 'completed',
|
|
744
|
+
duration: dailyCallDuration,
|
|
745
|
+
timestamp: new Date().toISOString()
|
|
746
|
+
});
|
|
747
|
+
this.call.iframe()?.style.setProperty('display', 'none');
|
|
748
|
+
// Set up event listeners
|
|
749
|
+
this.call.on('left-meeting', () => {
|
|
750
|
+
this.emit('call-end');
|
|
751
|
+
if (!this.hasEmittedCallEndedStatus) {
|
|
752
|
+
this.emit('message', {
|
|
753
|
+
type: 'status-update',
|
|
754
|
+
status: 'ended',
|
|
755
|
+
'endedReason': 'customer-ended-call',
|
|
756
|
+
});
|
|
757
|
+
this.hasEmittedCallEndedStatus = true;
|
|
758
|
+
}
|
|
759
|
+
if (isVideoRecordingEnabled) {
|
|
760
|
+
this.call?.stopRecording();
|
|
761
|
+
}
|
|
762
|
+
this.cleanup().catch(console.error);
|
|
763
|
+
});
|
|
764
|
+
this.call.on('error', (error) => {
|
|
765
|
+
this.emit('error', error);
|
|
766
|
+
if (isVideoRecordingEnabled) {
|
|
767
|
+
this.call?.stopRecording();
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
this.call.on('camera-error', (error) => {
|
|
771
|
+
this.emit('camera-error', error);
|
|
772
|
+
});
|
|
773
|
+
this.call.on('network-quality-change', (event) => {
|
|
774
|
+
this.emit('network-quality-change', event);
|
|
775
|
+
});
|
|
776
|
+
this.call.on('network-connection', (event) => {
|
|
777
|
+
this.emit('network-connection', event);
|
|
778
|
+
});
|
|
779
|
+
this.call.on('track-started', async (e) => {
|
|
780
|
+
if (!e || !e.participant) {
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
if (e.participant?.local) {
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
if (e.participant?.user_name !== 'Vapi Speaker') {
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
if (e.track.kind === 'video') {
|
|
790
|
+
this.emit('video', e.track);
|
|
791
|
+
}
|
|
792
|
+
if (e.track.kind === 'audio') {
|
|
793
|
+
await buildAudioPlayer(e.track, e.participant.session_id);
|
|
794
|
+
}
|
|
795
|
+
this.call?.sendAppMessage('playable');
|
|
796
|
+
});
|
|
797
|
+
this.call.on('participant-joined', (e) => {
|
|
798
|
+
if (!e || !this.call)
|
|
799
|
+
return;
|
|
800
|
+
subscribeToTracks(e, this.call, isVideoRecordingEnabled, isVideoEnabled);
|
|
801
|
+
});
|
|
802
|
+
this.call.on('participant-updated', (e) => {
|
|
803
|
+
if (!e) {
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
this.emit('daily-participant-updated', e.participant);
|
|
807
|
+
});
|
|
808
|
+
this.call.on('participant-left', (e) => {
|
|
809
|
+
if (!e) {
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
destroyAudioPlayer(e.participant.session_id);
|
|
813
|
+
});
|
|
814
|
+
this.call.on('remote-participants-audio-level', (e) => {
|
|
815
|
+
if (e)
|
|
816
|
+
this.handleRemoteParticipantsAudioLevel(e);
|
|
817
|
+
});
|
|
818
|
+
this.call.on('app-message', (e) => this.onAppMessage(e));
|
|
819
|
+
this.call.on('nonfatal-error', (e) => {
|
|
820
|
+
// https://docs.daily.co/reference/daily-js/events/meeting-events#type-audio-processor-error
|
|
821
|
+
if (e?.type === 'audio-processor-error') {
|
|
822
|
+
this.call
|
|
823
|
+
?.updateInputSettings({
|
|
824
|
+
audio: {
|
|
825
|
+
processor: {
|
|
826
|
+
type: 'none',
|
|
827
|
+
},
|
|
828
|
+
},
|
|
829
|
+
})
|
|
830
|
+
.then(() => {
|
|
831
|
+
(0, daily_guards_1.safeSetLocalAudio)(this.call, true);
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
// Stage 2: Mobile device handling and permissions
|
|
836
|
+
const isMobile = this.isMobileDevice();
|
|
837
|
+
this.emit('call-start-progress', {
|
|
838
|
+
stage: 'mobile-permissions',
|
|
839
|
+
status: 'started',
|
|
840
|
+
timestamp: new Date().toISOString(),
|
|
841
|
+
metadata: { isMobile }
|
|
842
|
+
});
|
|
843
|
+
if (isMobile) {
|
|
844
|
+
const mobileWaitStartTime = Date.now();
|
|
845
|
+
await this.sleep(1000);
|
|
846
|
+
const mobileWaitDuration = Date.now() - mobileWaitStartTime;
|
|
847
|
+
this.emit('call-start-progress', {
|
|
848
|
+
stage: 'mobile-permissions',
|
|
849
|
+
status: 'completed',
|
|
850
|
+
duration: mobileWaitDuration,
|
|
851
|
+
timestamp: new Date().toISOString(),
|
|
852
|
+
metadata: { action: 'permissions-wait' }
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
else {
|
|
856
|
+
this.emit('call-start-progress', {
|
|
857
|
+
stage: 'mobile-permissions',
|
|
858
|
+
status: 'completed',
|
|
859
|
+
timestamp: new Date().toISOString(),
|
|
860
|
+
metadata: { action: 'skipped-not-mobile' }
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
// Stage 3: Join the call
|
|
864
|
+
this.emit('call-start-progress', {
|
|
865
|
+
stage: 'daily-call-join',
|
|
866
|
+
status: 'started',
|
|
867
|
+
timestamp: new Date().toISOString()
|
|
868
|
+
});
|
|
869
|
+
const joinStartTime = Date.now();
|
|
870
|
+
await this.call.join({
|
|
871
|
+
url: webCall.webCallUrl,
|
|
872
|
+
subscribeToTracksAutomatically: false,
|
|
873
|
+
});
|
|
874
|
+
const joinDuration = Date.now() - joinStartTime;
|
|
875
|
+
this.emit('call-start-progress', {
|
|
876
|
+
stage: 'daily-call-join',
|
|
877
|
+
status: 'completed',
|
|
878
|
+
duration: joinDuration,
|
|
879
|
+
timestamp: new Date().toISOString()
|
|
880
|
+
});
|
|
881
|
+
// Stage 4: Video recording setup (if enabled)
|
|
882
|
+
if (isVideoRecordingEnabled) {
|
|
883
|
+
this.emit('call-start-progress', {
|
|
884
|
+
stage: 'video-recording-setup',
|
|
885
|
+
status: 'started',
|
|
886
|
+
timestamp: new Date().toISOString()
|
|
887
|
+
});
|
|
888
|
+
const recordingStartTime = Date.now();
|
|
889
|
+
const recordingRequestedTime = new Date().getTime();
|
|
890
|
+
try {
|
|
891
|
+
this.call.startRecording({
|
|
892
|
+
width: 1280,
|
|
893
|
+
height: 720,
|
|
894
|
+
backgroundColor: '#FF1F2D3D',
|
|
895
|
+
layout: {
|
|
896
|
+
preset: 'default',
|
|
897
|
+
},
|
|
898
|
+
});
|
|
899
|
+
const recordingSetupDuration = Date.now() - recordingStartTime;
|
|
900
|
+
this.emit('call-start-progress', {
|
|
901
|
+
stage: 'video-recording-setup',
|
|
902
|
+
status: 'completed',
|
|
903
|
+
duration: recordingSetupDuration,
|
|
904
|
+
timestamp: new Date().toISOString()
|
|
905
|
+
});
|
|
906
|
+
this.call.on('recording-started', () => {
|
|
907
|
+
const totalRecordingDelay = (new Date().getTime() - recordingRequestedTime) / 1000;
|
|
908
|
+
this.emit('call-start-progress', {
|
|
909
|
+
stage: 'video-recording-started',
|
|
910
|
+
status: 'completed',
|
|
911
|
+
timestamp: new Date().toISOString(),
|
|
912
|
+
metadata: { delaySeconds: totalRecordingDelay }
|
|
913
|
+
});
|
|
914
|
+
this.send({
|
|
915
|
+
type: 'control',
|
|
916
|
+
control: 'say-first-message',
|
|
917
|
+
videoRecordingStartDelaySeconds: totalRecordingDelay,
|
|
918
|
+
});
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
catch (error) {
|
|
922
|
+
const recordingSetupDuration = Date.now() - recordingStartTime;
|
|
923
|
+
this.emit('call-start-progress', {
|
|
924
|
+
stage: 'video-recording-setup',
|
|
925
|
+
status: 'failed',
|
|
926
|
+
duration: recordingSetupDuration,
|
|
927
|
+
timestamp: new Date().toISOString(),
|
|
928
|
+
metadata: { error: error?.toString() }
|
|
929
|
+
});
|
|
930
|
+
// Don't throw here, video recording is optional
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
else {
|
|
934
|
+
this.emit('call-start-progress', {
|
|
935
|
+
stage: 'video-recording-setup',
|
|
936
|
+
status: 'completed',
|
|
937
|
+
timestamp: new Date().toISOString(),
|
|
938
|
+
metadata: { action: 'skipped-not-enabled' }
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
// Stage 5: Audio level observer setup
|
|
942
|
+
this.emit('call-start-progress', {
|
|
943
|
+
stage: 'audio-observer-setup',
|
|
944
|
+
status: 'started',
|
|
945
|
+
timestamp: new Date().toISOString()
|
|
946
|
+
});
|
|
947
|
+
const audioObserverStartTime = Date.now();
|
|
948
|
+
try {
|
|
949
|
+
this.call.startRemoteParticipantsAudioLevelObserver(100);
|
|
950
|
+
const audioObserverDuration = Date.now() - audioObserverStartTime;
|
|
951
|
+
this.emit('call-start-progress', {
|
|
952
|
+
stage: 'audio-observer-setup',
|
|
953
|
+
status: 'completed',
|
|
954
|
+
duration: audioObserverDuration,
|
|
955
|
+
timestamp: new Date().toISOString()
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
catch (error) {
|
|
959
|
+
const audioObserverDuration = Date.now() - audioObserverStartTime;
|
|
960
|
+
this.emit('call-start-progress', {
|
|
961
|
+
stage: 'audio-observer-setup',
|
|
962
|
+
status: 'failed',
|
|
963
|
+
duration: audioObserverDuration,
|
|
964
|
+
timestamp: new Date().toISOString(),
|
|
965
|
+
metadata: { error: error?.toString() }
|
|
966
|
+
});
|
|
967
|
+
// Don't throw here, this is non-critical
|
|
968
|
+
}
|
|
969
|
+
// Stage 6: Audio processing setup
|
|
970
|
+
this.emit('call-start-progress', {
|
|
971
|
+
stage: 'audio-processing-setup',
|
|
972
|
+
status: 'started',
|
|
973
|
+
timestamp: new Date().toISOString()
|
|
974
|
+
});
|
|
975
|
+
const audioProcessingStartTime = Date.now();
|
|
976
|
+
try {
|
|
977
|
+
this.call.updateInputSettings({
|
|
978
|
+
audio: {
|
|
979
|
+
processor: {
|
|
980
|
+
type: 'noise-cancellation',
|
|
981
|
+
},
|
|
982
|
+
},
|
|
983
|
+
});
|
|
984
|
+
const audioProcessingDuration = Date.now() - audioProcessingStartTime;
|
|
985
|
+
this.emit('call-start-progress', {
|
|
986
|
+
stage: 'audio-processing-setup',
|
|
987
|
+
status: 'completed',
|
|
988
|
+
duration: audioProcessingDuration,
|
|
989
|
+
timestamp: new Date().toISOString()
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
catch (error) {
|
|
993
|
+
const audioProcessingDuration = Date.now() - audioProcessingStartTime;
|
|
994
|
+
this.emit('call-start-progress', {
|
|
995
|
+
stage: 'audio-processing-setup',
|
|
996
|
+
status: 'failed',
|
|
997
|
+
duration: audioProcessingDuration,
|
|
998
|
+
timestamp: new Date().toISOString(),
|
|
999
|
+
metadata: { error: error?.toString() }
|
|
1000
|
+
});
|
|
1001
|
+
// Don't throw here, this is non-critical
|
|
1002
|
+
}
|
|
1003
|
+
const totalDuration = Date.now() - startTime;
|
|
1004
|
+
this.emit('call-start-success', {
|
|
1005
|
+
totalDuration,
|
|
1006
|
+
callId: webCall?.id || 'unknown',
|
|
1007
|
+
timestamp: new Date().toISOString()
|
|
1008
|
+
});
|
|
1009
|
+
this.emit('call-start');
|
|
1010
|
+
}
|
|
1011
|
+
catch (e) {
|
|
1012
|
+
const totalDuration = Date.now() - startTime;
|
|
1013
|
+
this.emit('call-start-failed', {
|
|
1014
|
+
stage: 'reconnect',
|
|
1015
|
+
totalDuration,
|
|
1016
|
+
error: e?.toString() || 'Unknown error occurred',
|
|
1017
|
+
errorStack: e instanceof Error ? e.stack : 'No stack trace available',
|
|
1018
|
+
timestamp: new Date().toISOString(),
|
|
1019
|
+
context: {
|
|
1020
|
+
isReconnect: true,
|
|
1021
|
+
callId: webCall?.id || 'unknown',
|
|
1022
|
+
hasVideoRecording: !!webCall?.artifactPlan?.videoRecordingEnabled,
|
|
1023
|
+
voiceProvider: webCall?.assistant?.voice?.provider || 'unknown',
|
|
1024
|
+
isMobile: this.isMobileDevice()
|
|
1025
|
+
}
|
|
1026
|
+
});
|
|
1027
|
+
// Also emit the generic error event for backward compatibility
|
|
1028
|
+
this.emit('error', {
|
|
1029
|
+
type: 'reconnect-error',
|
|
1030
|
+
error: e,
|
|
1031
|
+
totalDuration,
|
|
1032
|
+
timestamp: new Date().toISOString(),
|
|
1033
|
+
context: {
|
|
1034
|
+
isReconnect: true,
|
|
1035
|
+
callId: webCall?.id || 'unknown',
|
|
1036
|
+
hasVideoRecording: !!webCall?.artifactPlan?.videoRecordingEnabled,
|
|
1037
|
+
voiceProvider: webCall?.assistant?.voice?.provider || 'unknown',
|
|
1038
|
+
isMobile: this.isMobileDevice()
|
|
1039
|
+
}
|
|
1040
|
+
});
|
|
1041
|
+
await this.cleanup();
|
|
1042
|
+
throw e;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Runs all network connectivity tests for pre-call diagnostics.
|
|
1047
|
+
* Creates a temporary Daily call object for testing purposes.
|
|
1048
|
+
*
|
|
1049
|
+
* Tests performed:
|
|
1050
|
+
* 1. Network connectivity (TURN server) - Tests if traffic can be relayed through TURN servers
|
|
1051
|
+
* 2. Websocket connectivity - Tests if websocket connections can be established
|
|
1052
|
+
* 3. Call quality - Tests overall call quality metrics (if available in SDK version)
|
|
1053
|
+
*
|
|
1054
|
+
* @returns {Promise<Record<string, any>>} Test results object with status for each test
|
|
1055
|
+
*
|
|
1056
|
+
* @example
|
|
1057
|
+
* // Run pre-call network diagnostics
|
|
1058
|
+
* const results = await Vapi.runNetworkTestsStandalone();
|
|
1059
|
+
* if (results.networkConnectivity?.result === 'failed') {
|
|
1060
|
+
* console.warn('Network issues detected - calls may not work properly');
|
|
1061
|
+
* }
|
|
1062
|
+
*
|
|
1063
|
+
* @static
|
|
1064
|
+
*/
|
|
1065
|
+
static async runNetworkTestsStandalone() {
|
|
1066
|
+
console.log('Starting standalone network connectivity tests...');
|
|
1067
|
+
const results = {};
|
|
1068
|
+
let tempCall = null;
|
|
1069
|
+
try {
|
|
1070
|
+
// Create a temporary call object for testing
|
|
1071
|
+
console.log('Creating temporary call object for testing...');
|
|
1072
|
+
tempCall = daily_js_1.default.createCallObject({
|
|
1073
|
+
audioSource: true,
|
|
1074
|
+
videoSource: true,
|
|
1075
|
+
});
|
|
1076
|
+
// Test 1: Network Connectivity (TURN server test)
|
|
1077
|
+
console.log('\n1. Testing network connectivity (TURN server)...');
|
|
1078
|
+
let videoTrack = null;
|
|
1079
|
+
try {
|
|
1080
|
+
// Create a dummy video track for the test
|
|
1081
|
+
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
|
1082
|
+
videoTrack = stream.getVideoTracks()[0];
|
|
1083
|
+
const networkTest = await tempCall.testNetworkConnectivity(videoTrack);
|
|
1084
|
+
results.networkConnectivity = networkTest;
|
|
1085
|
+
console.log('Network connectivity test result:', networkTest);
|
|
1086
|
+
}
|
|
1087
|
+
catch (error) {
|
|
1088
|
+
results.networkConnectivity = { result: 'error', error: error?.toString() };
|
|
1089
|
+
console.error('Network connectivity test error:', error);
|
|
1090
|
+
}
|
|
1091
|
+
finally {
|
|
1092
|
+
// Clean up the video track
|
|
1093
|
+
if (videoTrack) {
|
|
1094
|
+
videoTrack.stop();
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
// Test 2: Websocket Connectivity
|
|
1098
|
+
console.log('\n2. Testing websocket connectivity...');
|
|
1099
|
+
try {
|
|
1100
|
+
const websocketTest = await tempCall.testWebsocketConnectivity();
|
|
1101
|
+
results.websocketConnectivity = websocketTest;
|
|
1102
|
+
console.log('Websocket connectivity test result:', websocketTest);
|
|
1103
|
+
}
|
|
1104
|
+
catch (error) {
|
|
1105
|
+
results.websocketConnectivity = { result: 'error', error: error?.toString() };
|
|
1106
|
+
console.error('Websocket connectivity test error:', error);
|
|
1107
|
+
}
|
|
1108
|
+
// Test 3: Call Quality (if available)
|
|
1109
|
+
console.log('\n3. Testing call quality...');
|
|
1110
|
+
try {
|
|
1111
|
+
// Check if the method exists
|
|
1112
|
+
if (typeof tempCall.testCallQuality === 'function') {
|
|
1113
|
+
// Call quality test requires startCamera to initialize call state
|
|
1114
|
+
try {
|
|
1115
|
+
// Use startCamera to initialize the call state without needing a room
|
|
1116
|
+
console.log('Initializing call state with startCamera...');
|
|
1117
|
+
await tempCall.startCamera();
|
|
1118
|
+
// Now run the call quality test
|
|
1119
|
+
const callQualityTest = await tempCall.testCallQuality();
|
|
1120
|
+
results.callQuality = callQualityTest;
|
|
1121
|
+
console.log('Call quality test result:', callQualityTest);
|
|
1122
|
+
// Camera will be cleaned up when we destroy the call object
|
|
1123
|
+
}
|
|
1124
|
+
catch (startCameraError) {
|
|
1125
|
+
// If startCamera fails, it might be due to permissions or other issues
|
|
1126
|
+
console.error('Failed to start camera for call quality test:', startCameraError);
|
|
1127
|
+
results.callQuality = {
|
|
1128
|
+
result: 'error',
|
|
1129
|
+
error: startCameraError?.toString(),
|
|
1130
|
+
message: 'Failed to initialize camera for call quality test. Check camera permissions.'
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
else {
|
|
1135
|
+
results.callQuality = { result: 'not-available', message: 'testCallQuality method not available' };
|
|
1136
|
+
console.log('Call quality test not available in current Daily.co version');
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
catch (error) {
|
|
1140
|
+
results.callQuality = { result: 'error', error: error?.toString() };
|
|
1141
|
+
console.error('Call quality test error:', error);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
catch (error) {
|
|
1145
|
+
console.error('Failed to create temporary call object:', error);
|
|
1146
|
+
results.error = error?.toString();
|
|
1147
|
+
}
|
|
1148
|
+
finally {
|
|
1149
|
+
// Clean up the temporary call object
|
|
1150
|
+
if (tempCall) {
|
|
1151
|
+
try {
|
|
1152
|
+
console.log('Cleaning up temporary call object...');
|
|
1153
|
+
await tempCall.destroy();
|
|
1154
|
+
}
|
|
1155
|
+
catch (error) {
|
|
1156
|
+
console.error('Error destroying temporary call object:', error);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
// Summary
|
|
1161
|
+
console.log('\n=== Network Test Summary ===');
|
|
1162
|
+
console.log('Results:', JSON.stringify(results, null, 2));
|
|
1163
|
+
return results;
|
|
1164
|
+
}
|
|
657
1165
|
}
|
|
658
1166
|
exports.default = Vapi;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vapi-ai/web",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/vapi.js",
|
|
6
6
|
"types": "dist/vapi.d.ts",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"test:example": "npm run pack:local && cd example && npm install && npm run build",
|
|
15
15
|
"pack:local": "./scripts/build-latest.sh",
|
|
16
16
|
"clean-builds": "rm -f vapi-ai-web-*.tgz && echo '🧹 Cleaned up all build tarballs'",
|
|
17
|
-
"dev:example": "npm run pack:local && cd example && npm install && npm run dev"
|
|
17
|
+
"dev:example": "npm run pack:local && cd example && npm install && npm run dev",
|
|
18
|
+
"re-build-local-example": "./scripts/re-build-local-example.sh"
|
|
18
19
|
},
|
|
19
20
|
"repository": {
|
|
20
21
|
"type": "git",
|
|
@@ -30,7 +31,7 @@
|
|
|
30
31
|
"node": ">=18.0.0"
|
|
31
32
|
},
|
|
32
33
|
"dependencies": {
|
|
33
|
-
"@daily-co/daily-js": "^0.
|
|
34
|
+
"@daily-co/daily-js": "^0.85.0",
|
|
34
35
|
"events": "^3.3.0"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|