mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-20 18:30:15 +01:00
✨ feat: Implement WebRTC messaging and audio handling in the WebRTC service
This commit is contained in:
parent
d5bc8d3869
commit
7717d3a514
8 changed files with 771 additions and 139 deletions
|
|
@ -82,6 +82,7 @@
|
||||||
"mongoose": "^8.9.5",
|
"mongoose": "^8.9.5",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"nanoid": "^3.3.7",
|
"nanoid": "^3.3.7",
|
||||||
|
"node-pre-gyp": "^0.17.0",
|
||||||
"nodemailer": "^6.9.15",
|
"nodemailer": "^6.9.15",
|
||||||
"ollama": "^0.5.0",
|
"ollama": "^0.5.0",
|
||||||
"openai": "^4.47.1",
|
"openai": "^4.47.1",
|
||||||
|
|
@ -102,6 +103,7 @@
|
||||||
"ua-parser-js": "^1.0.36",
|
"ua-parser-js": "^1.0.36",
|
||||||
"winston": "^3.11.0",
|
"winston": "^3.11.0",
|
||||||
"winston-daily-rotate-file": "^4.7.1",
|
"winston-daily-rotate-file": "^4.7.1",
|
||||||
|
"wrtc": "^0.4.7",
|
||||||
"ws": "^8.18.0",
|
"ws": "^8.18.0",
|
||||||
"youtube-transcript": "^1.2.1",
|
"youtube-transcript": "^1.2.1",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
const { WebSocketServer } = require('ws');
|
const { WebSocketServer } = require('ws');
|
||||||
const fs = require('fs');
|
const { RTCPeerConnection } = require('wrtc');
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
module.exports.WebSocketService = class {
|
module.exports.WebSocketService = class {
|
||||||
constructor(server) {
|
constructor(server) {
|
||||||
this.wss = new WebSocketServer({ server, path: '/ws' });
|
this.wss = new WebSocketServer({ server, path: '/ws' });
|
||||||
this.log('Server initialized');
|
this.log('Server initialized');
|
||||||
this.clientAudioBuffers = new Map();
|
this.activeClients = new Map();
|
||||||
|
this.iceServers = [
|
||||||
|
{ urls: 'stun:stun.l.google.com:19302' },
|
||||||
|
{ urls: 'stun:stun1.l.google.com:19302' },
|
||||||
|
];
|
||||||
this.setupHandlers();
|
this.setupHandlers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -17,7 +20,13 @@ module.exports.WebSocketService = class {
|
||||||
setupHandlers() {
|
setupHandlers() {
|
||||||
this.wss.on('connection', (ws) => {
|
this.wss.on('connection', (ws) => {
|
||||||
const clientId = Date.now().toString();
|
const clientId = Date.now().toString();
|
||||||
this.clientAudioBuffers.set(clientId, []);
|
this.activeClients.set(clientId, {
|
||||||
|
ws,
|
||||||
|
state: 'idle',
|
||||||
|
audioBuffer: [],
|
||||||
|
currentTranscription: '',
|
||||||
|
isProcessing: false,
|
||||||
|
});
|
||||||
|
|
||||||
this.log(`Client connected: ${clientId}`);
|
this.log(`Client connected: ${clientId}`);
|
||||||
|
|
||||||
|
|
@ -29,42 +38,175 @@ module.exports.WebSocketService = class {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.type === 'audio-chunk') {
|
switch (message.type) {
|
||||||
if (!this.clientAudioBuffers.has(clientId)) {
|
case 'call-start':
|
||||||
this.clientAudioBuffers.set(clientId, []);
|
this.handleCallStart(clientId);
|
||||||
}
|
break;
|
||||||
this.clientAudioBuffers.get(clientId).push(message.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.type === 'request-response') {
|
case 'audio-chunk':
|
||||||
const filePath = path.join(__dirname, './assets/response.mp3');
|
await this.handleAudioChunk(clientId, message.data);
|
||||||
const audioFile = fs.readFileSync(filePath);
|
break;
|
||||||
ws.send(JSON.stringify({ type: 'audio-response', data: audioFile.toString('base64') }));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.type === 'call-ended') {
|
case 'processing-start':
|
||||||
const allChunks = this.clientAudioBuffers.get(clientId);
|
await this.processAudioStream(clientId);
|
||||||
this.writeAudioFile(clientId, allChunks);
|
break;
|
||||||
this.clientAudioBuffers.delete(clientId);
|
|
||||||
|
case 'audio-received':
|
||||||
|
this.confirmAudioReceived(clientId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'call-ended':
|
||||||
|
this.handleCallEnd(clientId);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.on('close', () => {
|
ws.on('close', () => {
|
||||||
|
this.handleCallEnd(clientId);
|
||||||
|
this.activeClients.delete(clientId);
|
||||||
this.log(`Client disconnected: ${clientId}`);
|
this.log(`Client disconnected: ${clientId}`);
|
||||||
this.clientAudioBuffers.delete(clientId);
|
});
|
||||||
|
|
||||||
|
ws.on('error', (error) => {
|
||||||
|
this.log(`Error for client ${clientId}: ${error.message}`);
|
||||||
|
this.handleCallEnd(clientId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
writeAudioFile(clientId, base64Chunks) {
|
async handleCallStart(clientId) {
|
||||||
if (!base64Chunks || base64Chunks.length === 0) {
|
const client = this.activeClients.get(clientId);
|
||||||
|
if (!client) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const filePath = path.join(__dirname, `recorded_${clientId}.webm`);
|
|
||||||
const buffer = Buffer.concat(
|
try {
|
||||||
base64Chunks.map((chunk) => Buffer.from(chunk.split(',')[1], 'base64')),
|
client.state = 'active';
|
||||||
|
client.audioBuffer = [];
|
||||||
|
client.currentTranscription = '';
|
||||||
|
client.isProcessing = false;
|
||||||
|
|
||||||
|
const peerConnection = new RTCPeerConnection({
|
||||||
|
iceServers: this.iceServers,
|
||||||
|
sdpSemantics: 'unified-plan',
|
||||||
|
});
|
||||||
|
|
||||||
|
client.peerConnection = peerConnection;
|
||||||
|
client.dataChannel = peerConnection.createDataChannel('audio', {
|
||||||
|
ordered: true,
|
||||||
|
maxRetransmits: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
client.dataChannel.onopen = () => this.log(`Data channel opened for ${clientId}`);
|
||||||
|
client.dataChannel.onmessage = async (event) => {
|
||||||
|
await this.handleAudioChunk(clientId, event.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
peerConnection.onicecandidate = (event) => {
|
||||||
|
if (event.candidate) {
|
||||||
|
client.ws.send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'ice-candidate',
|
||||||
|
candidate: event.candidate,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
fs.writeFileSync(filePath, buffer);
|
}
|
||||||
this.log(`Saved audio to ${filePath}`);
|
};
|
||||||
|
|
||||||
|
peerConnection.onnegotiationneeded = async () => {
|
||||||
|
try {
|
||||||
|
const offer = await peerConnection.createOffer();
|
||||||
|
await peerConnection.setLocalDescription(offer);
|
||||||
|
client.ws.send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'webrtc-offer',
|
||||||
|
sdp: peerConnection.localDescription,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
this.log(`Negotiation failed for ${clientId}: ${error}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.log(`Call started for client ${clientId}`);
|
||||||
|
} catch (error) {
|
||||||
|
this.log(`Error starting call for ${clientId}: ${error.message}`);
|
||||||
|
this.handleCallEnd(clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleAudioChunk(clientId, data) {
|
||||||
|
const client = this.activeClients.get(clientId);
|
||||||
|
if (!client || client.state !== 'active') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.audioBuffer.push(data);
|
||||||
|
client.ws.send(JSON.stringify({ type: 'audio-received' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
async processAudioStream(clientId) {
|
||||||
|
const client = this.activeClients.get(clientId);
|
||||||
|
if (!client || client.state !== 'active' || client.isProcessing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.isProcessing = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Process transcription
|
||||||
|
client.ws.send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'transcription',
|
||||||
|
data: 'Processing audio...',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Stream LLM response
|
||||||
|
client.ws.send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'llm-response',
|
||||||
|
data: 'Processing response...',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Stream TTS chunks
|
||||||
|
client.ws.send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'tts-chunk',
|
||||||
|
data: 'audio_data_here',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
this.log(`Processing error for client ${clientId}: ${error.message}`);
|
||||||
|
} finally {
|
||||||
|
client.isProcessing = false;
|
||||||
|
client.audioBuffer = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmAudioReceived(clientId) {
|
||||||
|
const client = this.activeClients.get(clientId);
|
||||||
|
if (!client) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.ws.send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'audio-received',
|
||||||
|
data: null,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCallEnd(clientId) {
|
||||||
|
const client = this.activeClients.get(clientId);
|
||||||
|
if (!client) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.state = 'idle';
|
||||||
|
client.audioBuffer = [];
|
||||||
|
client.currentTranscription = '';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,17 @@ export type AudioChunk = {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface RTCMessage {
|
||||||
|
type:
|
||||||
|
| 'audio-chunk'
|
||||||
|
| 'audio-received'
|
||||||
|
| 'transcription'
|
||||||
|
| 'llm-response'
|
||||||
|
| 'tts-chunk'
|
||||||
|
| 'call-ended';
|
||||||
|
data?: string | ArrayBuffer | null;
|
||||||
|
}
|
||||||
|
|
||||||
export type AssistantListItem = {
|
export type AssistantListItem = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
||||||
|
|
@ -1,67 +1,106 @@
|
||||||
import { useState, useRef, useCallback } from 'react';
|
import { useState, useRef, useCallback } from 'react';
|
||||||
import useWebSocket from './useWebSocket';
|
|
||||||
import { WebRTCService } from '../services/WebRTC/WebRTCService';
|
import { WebRTCService } from '../services/WebRTC/WebRTCService';
|
||||||
|
import type { RTCMessage } from '~/common';
|
||||||
|
import useWebSocket from './useWebSocket';
|
||||||
|
|
||||||
const SILENCE_THRESHOLD = -50;
|
const SILENCE_THRESHOLD = -50;
|
||||||
const SILENCE_DURATION = 1000;
|
const SILENCE_DURATION = 1000;
|
||||||
|
|
||||||
const useCall = () => {
|
const useCall = () => {
|
||||||
const { sendMessage } = useWebSocket();
|
const { sendMessage: wsMessage } = useWebSocket();
|
||||||
const [isCalling, setIsCalling] = useState(false);
|
const [isCalling, setIsCalling] = useState(false);
|
||||||
|
const [isProcessing, setIsProcessing] = useState(false);
|
||||||
const audioContextRef = useRef<AudioContext | null>(null);
|
const audioContextRef = useRef<AudioContext | null>(null);
|
||||||
const analyserRef = useRef<AnalyserNode | null>(null);
|
const analyserRef = useRef<AnalyserNode | null>(null);
|
||||||
|
const audioChunksRef = useRef<Blob[]>([]);
|
||||||
const silenceStartRef = useRef<number | null>(null);
|
const silenceStartRef = useRef<number | null>(null);
|
||||||
const intervalRef = useRef<number | null>(null);
|
const intervalRef = useRef<number | null>(null);
|
||||||
const webrtcServiceRef = useRef<WebRTCService | null>(null);
|
const webrtcServiceRef = useRef<WebRTCService | null>(null);
|
||||||
|
|
||||||
const checkSilence = useCallback(() => {
|
const sendAudioChunk = useCallback(() => {
|
||||||
if (!analyserRef.current || !isCalling) {
|
if (audioChunksRef.current.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const data = new Float32Array(analyserRef.current.frequencyBinCount);
|
|
||||||
analyserRef.current.getFloatFrequencyData(data);
|
const audioBlob = new Blob(audioChunksRef.current, { type: 'audio/webm' });
|
||||||
const avg = data.reduce((a, b) => a + b) / data.length;
|
// Send audio through WebRTC data channel
|
||||||
if (avg < SILENCE_THRESHOLD) {
|
webrtcServiceRef.current?.sendAudioChunk(audioBlob);
|
||||||
if (!silenceStartRef.current) {
|
// Signal processing start via WebSocket
|
||||||
silenceStartRef.current = Date.now();
|
wsMessage({ type: 'processing-start' });
|
||||||
} else if (Date.now() - silenceStartRef.current > SILENCE_DURATION) {
|
|
||||||
sendMessage({ type: 'request-response' });
|
audioChunksRef.current = [];
|
||||||
silenceStartRef.current = null;
|
setIsProcessing(true);
|
||||||
|
}, [wsMessage]);
|
||||||
|
|
||||||
|
const handleRTCMessage = useCallback((message: RTCMessage) => {
|
||||||
|
if (message.type === 'audio-received') {
|
||||||
|
// Backend confirmed audio receipt
|
||||||
|
setIsProcessing(true);
|
||||||
}
|
}
|
||||||
} else {
|
}, []);
|
||||||
silenceStartRef.current = null;
|
|
||||||
}
|
|
||||||
}, [isCalling, sendMessage]);
|
|
||||||
|
|
||||||
const startCall = useCallback(async () => {
|
const startCall = useCallback(async () => {
|
||||||
webrtcServiceRef.current = new WebRTCService(sendMessage);
|
// Initialize WebRTC with message handler
|
||||||
|
webrtcServiceRef.current = new WebRTCService(handleRTCMessage);
|
||||||
await webrtcServiceRef.current.initializeCall();
|
await webrtcServiceRef.current.initializeCall();
|
||||||
|
|
||||||
|
// Signal call start via WebSocket
|
||||||
|
wsMessage({ type: 'call-start' });
|
||||||
|
|
||||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||||
audioContextRef.current = new AudioContext();
|
audioContextRef.current = new AudioContext();
|
||||||
const source = audioContextRef.current.createMediaStreamSource(stream);
|
const source = audioContextRef.current.createMediaStreamSource(stream);
|
||||||
analyserRef.current = audioContextRef.current.createAnalyser();
|
analyserRef.current = audioContextRef.current.createAnalyser();
|
||||||
source.connect(analyserRef.current);
|
source.connect(analyserRef.current);
|
||||||
|
|
||||||
intervalRef.current = window.setInterval(checkSilence, 100);
|
// Start VAD monitoring
|
||||||
|
intervalRef.current = window.setInterval(() => {
|
||||||
|
if (!analyserRef.current || !isCalling) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = new Float32Array(analyserRef.current.frequencyBinCount);
|
||||||
|
analyserRef.current.getFloatFrequencyData(data);
|
||||||
|
const avg = data.reduce((a, b) => a + b) / data.length;
|
||||||
|
|
||||||
|
if (avg < SILENCE_THRESHOLD) {
|
||||||
|
if (silenceStartRef.current === null) {
|
||||||
|
silenceStartRef.current = Date.now();
|
||||||
|
} else if (Date.now() - silenceStartRef.current > SILENCE_DURATION) {
|
||||||
|
sendAudioChunk();
|
||||||
|
silenceStartRef.current = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
silenceStartRef.current = null;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
setIsCalling(true);
|
setIsCalling(true);
|
||||||
}, [checkSilence, sendMessage]);
|
}, [handleRTCMessage, wsMessage, sendAudioChunk]);
|
||||||
|
|
||||||
const hangUp = useCallback(async () => {
|
const hangUp = useCallback(async () => {
|
||||||
if (intervalRef.current) {
|
if (intervalRef.current) {
|
||||||
clearInterval(intervalRef.current);
|
clearInterval(intervalRef.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
analyserRef.current = null;
|
analyserRef.current = null;
|
||||||
audioContextRef.current?.close();
|
audioContextRef.current?.close();
|
||||||
audioContextRef.current = null;
|
audioContextRef.current = null;
|
||||||
|
|
||||||
await webrtcServiceRef.current?.endCall();
|
await webrtcServiceRef.current?.endCall();
|
||||||
webrtcServiceRef.current = null;
|
webrtcServiceRef.current = null;
|
||||||
setIsCalling(false);
|
|
||||||
sendMessage({ type: 'call-ended' });
|
|
||||||
}, [sendMessage]);
|
|
||||||
|
|
||||||
return { isCalling, startCall, hangUp };
|
setIsCalling(false);
|
||||||
|
setIsProcessing(false);
|
||||||
|
wsMessage({ type: 'call-ended' });
|
||||||
|
}, [wsMessage]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
isCalling,
|
||||||
|
isProcessing,
|
||||||
|
startCall,
|
||||||
|
hangUp,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useCall;
|
export default useCall;
|
||||||
|
|
|
||||||
|
|
@ -1,75 +1,44 @@
|
||||||
import { useRef, useCallback } from 'react';
|
import { useRef, useCallback } from 'react';
|
||||||
|
import { WebRTCService } from '../services/WebRTC/WebRTCService';
|
||||||
|
import type { RTCMessage } from '~/common';
|
||||||
import useWebSocket from './useWebSocket';
|
import useWebSocket from './useWebSocket';
|
||||||
|
|
||||||
const SILENCE_THRESHOLD = -50;
|
|
||||||
const SILENCE_DURATION = 1000;
|
|
||||||
|
|
||||||
const useWebRTC = () => {
|
const useWebRTC = () => {
|
||||||
const { sendMessage } = useWebSocket();
|
const { sendMessage } = useWebSocket();
|
||||||
const localStreamRef = useRef<MediaStream | null>(null);
|
const webrtcServiceRef = useRef<WebRTCService | null>(null);
|
||||||
const audioContextRef = useRef<AudioContext | null>(null);
|
|
||||||
const analyserRef = useRef<AnalyserNode | null>(null);
|
|
||||||
const silenceStartTime = useRef<number | null>(null);
|
|
||||||
const isProcessingRef = useRef(false);
|
|
||||||
|
|
||||||
const log = (msg: string) => console.log(`[WebRTC ${new Date().toISOString()}] ${msg}`);
|
const handleRTCMessage = useCallback(
|
||||||
|
(message: RTCMessage) => {
|
||||||
const processAudioLevel = () => {
|
switch (message.type) {
|
||||||
if (!analyserRef.current || !isProcessingRef.current) {
|
case 'audio-chunk':
|
||||||
return;
|
sendMessage({ type: 'processing-start' });
|
||||||
|
break;
|
||||||
|
case 'transcription':
|
||||||
|
case 'llm-response':
|
||||||
|
case 'tts-chunk':
|
||||||
|
// TODO: Handle streaming responses
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
const dataArray = new Float32Array(analyserRef.current.frequencyBinCount);
|
[sendMessage],
|
||||||
analyserRef.current.getFloatFrequencyData(dataArray);
|
);
|
||||||
const average = dataArray.reduce((a, b) => a + b) / dataArray.length;
|
|
||||||
|
|
||||||
if (average < SILENCE_THRESHOLD) {
|
|
||||||
if (!silenceStartTime.current) {
|
|
||||||
silenceStartTime.current = Date.now();
|
|
||||||
log(`Silence started: ${average}dB`);
|
|
||||||
} else if (Date.now() - silenceStartTime.current > SILENCE_DURATION) {
|
|
||||||
log('Silence threshold reached - requesting response');
|
|
||||||
sendMessage({ type: 'request-response' });
|
|
||||||
silenceStartTime.current = null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
silenceStartTime.current = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
requestAnimationFrame(processAudioLevel);
|
|
||||||
};
|
|
||||||
|
|
||||||
const startLocalStream = async () => {
|
const startLocalStream = async () => {
|
||||||
try {
|
try {
|
||||||
log('Starting audio capture');
|
webrtcServiceRef.current = new WebRTCService(handleRTCMessage);
|
||||||
localStreamRef.current = await navigator.mediaDevices.getUserMedia({ audio: true });
|
await webrtcServiceRef.current.initializeCall();
|
||||||
|
sendMessage({ type: 'call-start' });
|
||||||
audioContextRef.current = new AudioContext();
|
|
||||||
const source = audioContextRef.current.createMediaStreamSource(localStreamRef.current);
|
|
||||||
analyserRef.current = audioContextRef.current.createAnalyser();
|
|
||||||
|
|
||||||
source.connect(analyserRef.current);
|
|
||||||
isProcessingRef.current = true;
|
|
||||||
processAudioLevel();
|
|
||||||
|
|
||||||
log('Audio capture started');
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
console.error(error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const stopLocalStream = useCallback(() => {
|
const stopLocalStream = useCallback(() => {
|
||||||
log('Stopping audio capture');
|
webrtcServiceRef.current?.endCall();
|
||||||
isProcessingRef.current = false;
|
webrtcServiceRef.current = null;
|
||||||
audioContextRef.current?.close();
|
sendMessage({ type: 'call-ended' });
|
||||||
localStreamRef.current?.getTracks().forEach((track) => track.stop());
|
}, [sendMessage]);
|
||||||
|
|
||||||
localStreamRef.current = null;
|
|
||||||
audioContextRef.current = null;
|
|
||||||
analyserRef.current = null;
|
|
||||||
silenceStartTime.current = null;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return { startLocalStream, stopLocalStream };
|
return { startLocalStream, stopLocalStream };
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,47 @@
|
||||||
import { useEffect, useRef, useState, useCallback } from 'react';
|
import { useEffect, useRef, useState, useCallback } from 'react';
|
||||||
import { useGetWebsocketUrlQuery } from 'librechat-data-provider/react-query';
|
import { useGetWebsocketUrlQuery } from 'librechat-data-provider/react-query';
|
||||||
|
import type { RTCMessage } from '~/common';
|
||||||
|
|
||||||
const useWebSocket = () => {
|
const useWebSocket = () => {
|
||||||
const { data: url } = useGetWebsocketUrlQuery();
|
const { data: data } = useGetWebsocketUrlQuery();
|
||||||
const [isConnected, setIsConnected] = useState(false);
|
const [isConnected, setIsConnected] = useState(false);
|
||||||
const wsRef = useRef<WebSocket | null>(null);
|
const wsRef = useRef<WebSocket | null>(null);
|
||||||
|
|
||||||
console.log('wsConfig:', url?.url);
|
|
||||||
|
|
||||||
const connect = useCallback(() => {
|
const connect = useCallback(() => {
|
||||||
if (!url?.url) {
|
if (!data || !data.url) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wsRef.current = new WebSocket(url?.url);
|
wsRef.current = new WebSocket(data.url);
|
||||||
wsRef.current.onopen = () => setIsConnected(true);
|
wsRef.current.onopen = () => setIsConnected(true);
|
||||||
wsRef.current.onclose = () => setIsConnected(false);
|
wsRef.current.onclose = () => setIsConnected(false);
|
||||||
wsRef.current.onerror = (err) => console.error('WebSocket error:', err);
|
wsRef.current.onerror = (err) => console.error('WebSocket error:', err);
|
||||||
|
|
||||||
wsRef.current.onmessage = (event) => {
|
wsRef.current.onmessage = (event) => {
|
||||||
const msg = JSON.parse(event.data);
|
const msg: RTCMessage = JSON.parse(event.data);
|
||||||
if (msg.type === 'audio-response') {
|
switch (msg.type) {
|
||||||
const audioData = msg.data;
|
case 'transcription':
|
||||||
const audio = new Audio(`data:audio/mp3;base64,${audioData}`);
|
// TODO: Handle transcription update
|
||||||
|
break;
|
||||||
|
case 'llm-response':
|
||||||
|
// TODO: Handle LLM streaming response
|
||||||
|
break;
|
||||||
|
case 'tts-chunk':
|
||||||
|
if (typeof msg.data === 'string') {
|
||||||
|
const audio = new Audio(`data:audio/mp3;base64,${msg.data}`);
|
||||||
audio.play().catch(console.error);
|
audio.play().catch(console.error);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [url?.url]);
|
}, [data?.url]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
connect();
|
connect();
|
||||||
return () => wsRef.current?.close();
|
return () => wsRef.current?.close();
|
||||||
}, [connect]);
|
}, [connect]);
|
||||||
|
|
||||||
const sendMessage = useCallback((message: any) => {
|
const sendMessage = useCallback((message: Record<string, unknown>) => {
|
||||||
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
||||||
wsRef.current.send(JSON.stringify(message));
|
wsRef.current.send(JSON.stringify(message));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,55 @@
|
||||||
|
import type { RTCMessage } from '~/common';
|
||||||
export class WebRTCService {
|
export class WebRTCService {
|
||||||
private peerConnection: RTCPeerConnection | null = null;
|
private peerConnection: RTCPeerConnection | null = null;
|
||||||
|
private dataChannel: RTCDataChannel | null = null;
|
||||||
private mediaRecorder: MediaRecorder | null = null;
|
private mediaRecorder: MediaRecorder | null = null;
|
||||||
private sendMessage: (msg: any) => void;
|
private onMessage: (msg: RTCMessage) => void;
|
||||||
|
|
||||||
constructor(sendMessage: (msg: any) => void) {
|
constructor(onMessage: (msg: RTCMessage) => void) {
|
||||||
this.sendMessage = sendMessage;
|
this.onMessage = onMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
async initializeCall() {
|
async initializeCall() {
|
||||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
||||||
this.peerConnection = new RTCPeerConnection();
|
this.peerConnection = new RTCPeerConnection();
|
||||||
stream.getTracks().forEach((track) => this.peerConnection?.addTrack(track, stream));
|
this.dataChannel = this.peerConnection.createDataChannel('audio');
|
||||||
|
|
||||||
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||||
this.mediaRecorder = new MediaRecorder(stream);
|
this.mediaRecorder = new MediaRecorder(stream);
|
||||||
|
|
||||||
this.mediaRecorder.ondataavailable = (e) => {
|
this.mediaRecorder.ondataavailable = (e) => {
|
||||||
if (e.data.size > 0) {
|
if (e.data.size > 0 && this.dataChannel?.readyState === 'open') {
|
||||||
const reader = new FileReader();
|
e.data.arrayBuffer().then((buffer) => {
|
||||||
reader.onload = () => {
|
this.dataChannel?.send(buffer);
|
||||||
this.sendMessage({
|
|
||||||
type: 'audio-chunk',
|
|
||||||
data: reader.result,
|
|
||||||
});
|
});
|
||||||
};
|
|
||||||
reader.readAsDataURL(e.data);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.mediaRecorder.start();
|
|
||||||
|
this.mediaRecorder.start(100);
|
||||||
|
this.setupDataChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupDataChannel() {
|
||||||
|
if (!this.dataChannel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dataChannel.onmessage = (event) => {
|
||||||
|
this.onMessage({
|
||||||
|
type: 'audio-chunk',
|
||||||
|
data: event.data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async sendAudioChunk(audioBlob: Blob) {
|
||||||
|
if (this.dataChannel && this.dataChannel.readyState === 'open') {
|
||||||
|
this.dataChannel.send(await audioBlob.arrayBuffer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async endCall() {
|
async endCall() {
|
||||||
this.mediaRecorder?.stop();
|
this.mediaRecorder?.stop();
|
||||||
|
this.dataChannel?.close();
|
||||||
this.peerConnection?.close();
|
this.peerConnection?.close();
|
||||||
this.peerConnection = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
450
package-lock.json
generated
450
package-lock.json
generated
|
|
@ -98,6 +98,7 @@
|
||||||
"mongoose": "^8.9.5",
|
"mongoose": "^8.9.5",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"nanoid": "^3.3.7",
|
"nanoid": "^3.3.7",
|
||||||
|
"node-pre-gyp": "^0.17.0",
|
||||||
"nodemailer": "^6.9.15",
|
"nodemailer": "^6.9.15",
|
||||||
"ollama": "^0.5.0",
|
"ollama": "^0.5.0",
|
||||||
"openai": "^4.47.1",
|
"openai": "^4.47.1",
|
||||||
|
|
@ -118,6 +119,7 @@
|
||||||
"ua-parser-js": "^1.0.36",
|
"ua-parser-js": "^1.0.36",
|
||||||
"winston": "^3.11.0",
|
"winston": "^3.11.0",
|
||||||
"winston-daily-rotate-file": "^4.7.1",
|
"winston-daily-rotate-file": "^4.7.1",
|
||||||
|
"wrtc": "^0.4.7",
|
||||||
"ws": "^8.18.0",
|
"ws": "^8.18.0",
|
||||||
"youtube-transcript": "^1.2.1",
|
"youtube-transcript": "^1.2.1",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
|
|
@ -17171,8 +17173,7 @@
|
||||||
"node_modules/abbrev": {
|
"node_modules/abbrev": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/abort-controller": {
|
"node_modules/abort-controller": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
|
|
@ -17379,6 +17380,21 @@
|
||||||
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
||||||
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
|
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/aproba": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
|
||||||
|
},
|
||||||
|
"node_modules/are-we-there-yet": {
|
||||||
|
"version": "1.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
|
||||||
|
"integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"dependencies": {
|
||||||
|
"delegates": "^1.0.0",
|
||||||
|
"readable-stream": "^2.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/arg": {
|
"node_modules/arg": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||||
|
|
@ -18861,6 +18877,14 @@
|
||||||
"node": ">= 0.12.0"
|
"node": ">= 0.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/code-point-at": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cohere-ai": {
|
"node_modules/cohere-ai": {
|
||||||
"version": "7.9.1",
|
"version": "7.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/cohere-ai/-/cohere-ai-7.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/cohere-ai/-/cohere-ai-7.9.1.tgz",
|
||||||
|
|
@ -19110,6 +19134,11 @@
|
||||||
"integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
|
"integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/console-control-strings": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
|
||||||
|
},
|
||||||
"node_modules/constants-browserify": {
|
"node_modules/constants-browserify": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
|
||||||
|
|
@ -19827,6 +19856,11 @@
|
||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/delegates": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
|
||||||
|
},
|
||||||
"node_modules/denque": {
|
"node_modules/denque": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
|
||||||
|
|
@ -22111,6 +22145,23 @@
|
||||||
"universalify": "^0.1.0"
|
"universalify": "^0.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fs-minipass": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
|
||||||
|
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
|
||||||
|
"dependencies": {
|
||||||
|
"minipass": "^2.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs-minipass/node_modules/minipass": {
|
||||||
|
"version": "2.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
|
||||||
|
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "^5.1.2",
|
||||||
|
"yallist": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
|
@ -22167,6 +22218,65 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/gauge": {
|
||||||
|
"version": "2.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
|
||||||
|
"integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"dependencies": {
|
||||||
|
"aproba": "^1.0.3",
|
||||||
|
"console-control-strings": "^1.0.0",
|
||||||
|
"has-unicode": "^2.0.0",
|
||||||
|
"object-assign": "^4.1.0",
|
||||||
|
"signal-exit": "^3.0.0",
|
||||||
|
"string-width": "^1.0.1",
|
||||||
|
"strip-ansi": "^3.0.1",
|
||||||
|
"wide-align": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/gauge/node_modules/ansi-regex": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/gauge/node_modules/is-fullwidth-code-point": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
|
||||||
|
"dependencies": {
|
||||||
|
"number-is-nan": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/gauge/node_modules/string-width": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
|
||||||
|
"dependencies": {
|
||||||
|
"code-point-at": "^1.0.0",
|
||||||
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
|
"strip-ansi": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/gauge/node_modules/strip-ansi": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/gaxios": {
|
"node_modules/gaxios": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.2.0.tgz",
|
||||||
|
|
@ -22833,6 +22943,11 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/has-unicode": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
|
||||||
|
},
|
||||||
"node_modules/hash-base": {
|
"node_modules/hash-base": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
|
||||||
|
|
@ -23485,6 +23600,14 @@
|
||||||
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
|
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/ignore-walk": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"minimatch": "^3.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/import-fresh": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
|
|
@ -28118,6 +28241,23 @@
|
||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/minizlib": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"minipass": "^2.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minizlib/node_modules/minipass": {
|
||||||
|
"version": "2.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
|
||||||
|
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "^5.1.2",
|
||||||
|
"yallist": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mkdirp": {
|
"node_modules/mkdirp": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
|
|
@ -28395,6 +28535,30 @@
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/needle": {
|
||||||
|
"version": "2.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz",
|
||||||
|
"integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "^3.2.6",
|
||||||
|
"iconv-lite": "^0.4.4",
|
||||||
|
"sax": "^1.2.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"needle": "bin/needle"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4.4.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/needle/node_modules/debug": {
|
||||||
|
"version": "3.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||||
|
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/negotiator": {
|
"node_modules/negotiator": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||||
|
|
@ -28511,6 +28675,101 @@
|
||||||
"integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
|
"integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/node-pre-gyp": {
|
||||||
|
"version": "0.17.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.17.0.tgz",
|
||||||
|
"integrity": "sha512-abzZt1hmOjkZez29ppg+5gGqdPLUuJeAEwVPtHYEJgx0qzttCbcKFpxrCQn2HYbwCv2c+7JwH4BgEzFkUGpn4A==",
|
||||||
|
"deprecated": "Please upgrade to @mapbox/node-pre-gyp: the non-scoped node-pre-gyp package is deprecated and only the @mapbox scoped package will recieve updates in the future",
|
||||||
|
"dependencies": {
|
||||||
|
"detect-libc": "^1.0.3",
|
||||||
|
"mkdirp": "^0.5.5",
|
||||||
|
"needle": "^2.5.2",
|
||||||
|
"nopt": "^4.0.3",
|
||||||
|
"npm-packlist": "^1.4.8",
|
||||||
|
"npmlog": "^4.1.2",
|
||||||
|
"rc": "^1.2.8",
|
||||||
|
"rimraf": "^2.7.1",
|
||||||
|
"semver": "^5.7.1",
|
||||||
|
"tar": "^4.4.13"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"node-pre-gyp": "bin/node-pre-gyp"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/detect-libc": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
|
||||||
|
"bin": {
|
||||||
|
"detect-libc": "bin/detect-libc.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/mkdirp": {
|
||||||
|
"version": "0.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||||
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
|
"dependencies": {
|
||||||
|
"minimist": "^1.2.6"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"mkdirp": "bin/cmd.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/nopt": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
|
||||||
|
"dependencies": {
|
||||||
|
"abbrev": "1",
|
||||||
|
"osenv": "^0.1.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"nopt": "bin/nopt.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/rimraf": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||||
|
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||||
|
"dependencies": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rimraf": "bin.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/semver": {
|
||||||
|
"version": "5.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||||
|
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.18",
|
"version": "2.0.18",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
|
||||||
|
|
@ -28720,6 +28979,29 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/npm-bundled": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"npm-normalize-package-bin": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/npm-normalize-package-bin": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
|
||||||
|
},
|
||||||
|
"node_modules/npm-packlist": {
|
||||||
|
"version": "1.4.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
|
||||||
|
"integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
|
||||||
|
"dependencies": {
|
||||||
|
"ignore-walk": "^3.0.1",
|
||||||
|
"npm-bundled": "^1.0.1",
|
||||||
|
"npm-normalize-package-bin": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/npm-run-path": {
|
"node_modules/npm-run-path": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
|
||||||
|
|
@ -28732,6 +29014,18 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/npmlog": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"dependencies": {
|
||||||
|
"are-we-there-yet": "~1.1.2",
|
||||||
|
"console-control-strings": "~1.1.0",
|
||||||
|
"gauge": "~2.7.3",
|
||||||
|
"set-blocking": "~2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nth-check": {
|
"node_modules/nth-check": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
||||||
|
|
@ -28745,6 +29039,14 @@
|
||||||
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/number-is-nan": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nwsapi": {
|
"node_modules/nwsapi": {
|
||||||
"version": "2.2.7",
|
"version": "2.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz",
|
||||||
|
|
@ -29067,6 +29369,32 @@
|
||||||
"integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==",
|
"integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/os-homedir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/os-tmpdir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/osenv": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
|
||||||
|
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"dependencies": {
|
||||||
|
"os-homedir": "^1.0.0",
|
||||||
|
"os-tmpdir": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/outvariant": {
|
"node_modules/outvariant": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.0.tgz",
|
||||||
|
|
@ -33220,6 +33548,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/sax": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="
|
||||||
|
},
|
||||||
"node_modules/saxes": {
|
"node_modules/saxes": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
|
||||||
|
|
@ -33348,6 +33681,11 @@
|
||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/set-blocking": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
|
||||||
|
},
|
||||||
"node_modules/set-function-length": {
|
"node_modules/set-function-length": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||||
|
|
@ -33540,8 +33878,7 @@
|
||||||
"node_modules/signal-exit": {
|
"node_modules/signal-exit": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/simple-concat": {
|
"node_modules/simple-concat": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
|
|
@ -34496,6 +34833,23 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tar": {
|
||||||
|
"version": "4.4.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz",
|
||||||
|
"integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==",
|
||||||
|
"dependencies": {
|
||||||
|
"chownr": "^1.1.4",
|
||||||
|
"fs-minipass": "^1.2.7",
|
||||||
|
"minipass": "^2.9.0",
|
||||||
|
"minizlib": "^1.3.3",
|
||||||
|
"mkdirp": "^0.5.5",
|
||||||
|
"safe-buffer": "^5.2.1",
|
||||||
|
"yallist": "^3.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tar-fs": {
|
"node_modules/tar-fs": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz",
|
||||||
|
|
@ -34516,6 +34870,26 @@
|
||||||
"streamx": "^2.15.0"
|
"streamx": "^2.15.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tar/node_modules/minipass": {
|
||||||
|
"version": "2.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
|
||||||
|
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "^5.1.2",
|
||||||
|
"yallist": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tar/node_modules/mkdirp": {
|
||||||
|
"version": "0.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||||
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
|
"dependencies": {
|
||||||
|
"minimist": "^1.2.6"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"mkdirp": "bin/cmd.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/temp-dir": {
|
"node_modules/temp-dir": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz",
|
||||||
|
|
@ -36411,6 +36785,40 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/wide-align": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
|
||||||
|
"integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^1.0.2 || 2 || 3 || 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wide-align/node_modules/emoji-regex": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||||
|
},
|
||||||
|
"node_modules/wide-align/node_modules/is-fullwidth-code-point": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wide-align/node_modules/string-width": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/winston": {
|
"node_modules/winston": {
|
||||||
"version": "3.11.0",
|
"version": "3.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz",
|
||||||
|
|
@ -37092,6 +37500,40 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/wrtc": {
|
||||||
|
"version": "0.4.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.7.tgz",
|
||||||
|
"integrity": "sha512-P6Hn7VT4lfSH49HxLHcHhDq+aFf/jd9dPY7lDHeFhZ22N3858EKuwm2jmnlPzpsRGEPaoF6XwkcxY5SYnt4f/g==",
|
||||||
|
"bundleDependencies": [
|
||||||
|
"node-pre-gyp"
|
||||||
|
],
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"node-pre-gyp": "^0.13.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.11.2 || >=10.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"domexception": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wrtc/node_modules/domexception": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
|
||||||
|
"deprecated": "Use your platform's native DOMException instead",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"webidl-conversions": "^4.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wrtc/node_modules/webidl-conversions": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "8.18.0",
|
"version": "8.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue