/** * Voice Command Routing Test * Tests that voice commands from LiveKit agents are routed to the correct Chrome extension */ import WebSocket from 'ws'; import fetch from 'node-fetch'; const CHROME_WS_URL = 'ws://localhost:3001/chrome'; const MCP_HTTP_URL = 'http://localhost:3001/mcp'; class MockChromeExtension { constructor(userId) { this.userId = userId; this.chromeUserId = `user_${Date.now()}_${userId}_${Math.random().toString(36).substring(2, 8)}`; this.ws = null; this.sessionInfo = null; this.receivedCommands = []; } async connect() { return new Promise((resolve, reject) => { console.log(`๐Ÿ”Œ [Chrome ${this.userId}] Connecting with user ID: ${this.chromeUserId}`); this.ws = new WebSocket(CHROME_WS_URL); this.ws.on('open', () => { // Send connection info with user ID const connectionInfo = { type: 'connection_info', userId: this.chromeUserId, userAgent: `MockChrome-${this.userId}`, timestamp: Date.now(), extensionId: `mock-extension-${this.userId}` }; this.ws.send(JSON.stringify(connectionInfo)); }); this.ws.on('message', (data) => { try { const message = JSON.parse(data.toString()); if (message.type === 'session_info') { this.sessionInfo = message.sessionInfo; console.log(`โœ… [Chrome ${this.userId}] Session established: ${this.sessionInfo.sessionId}`); resolve(); } // Handle tool calls (voice commands) if (message.action === 'callTool') { this.receivedCommands.push(message); console.log(`๐ŸŽค [Chrome ${this.userId}] Received voice command: ${message.params.name}`); // Send response const response = { id: message.id, success: true, result: `Command executed by Chrome ${this.userId}`, timestamp: Date.now() }; this.ws.send(JSON.stringify(response)); } } catch (error) { console.error(`โŒ [Chrome ${this.userId}] Error parsing message:`, error); } }); this.ws.on('error', reject); setTimeout(() => { if (!this.sessionInfo) { reject(new Error(`Timeout waiting for session info for Chrome ${this.userId}`)); } }, 5000); }); } disconnect() { if (this.ws) { this.ws.close(); } } } class MockLiveKitAgent { constructor(userId) { this.userId = userId; this.chromeUserId = userId; // This should match the Chrome extension's user ID } async sendVoiceCommand(toolName, args) { console.log(`๐ŸŽ™๏ธ [LiveKit ${this.userId}] Sending voice command: ${toolName}`); const payload = { jsonrpc: '2.0', id: Date.now(), method: 'tools/call', params: { name: toolName, arguments: args } }; const headers = { 'Content-Type': 'application/json', 'chrome-user-id': this.chromeUserId // Route to specific Chrome extension }; try { const response = await fetch(MCP_HTTP_URL, { method: 'POST', headers: headers, body: JSON.stringify(payload) }); const result = await response.json(); console.log(`๐Ÿ“จ [LiveKit ${this.userId}] Voice command response:`, result); return result; } catch (error) { console.error(`โŒ [LiveKit ${this.userId}] Error sending voice command:`, error); throw error; } } } async function testVoiceCommandRouting() { console.log('๐ŸŽค Starting Voice Command Routing Test...\n'); const chromeExtensions = []; const liveKitAgents = []; try { // Step 1: Create and connect Chrome extensions console.log('๐Ÿ“‹ STEP 1: Setting up Chrome Extensions'); console.log('=' .repeat(50)); for (let i = 1; i <= 2; i++) { const chrome = new MockChromeExtension(i); chromeExtensions.push(chrome); await chrome.connect(); console.log(`โœ… Chrome ${i} connected with user ID: ${chrome.chromeUserId}`); // Create corresponding LiveKit agent const agent = new MockLiveKitAgent(chrome.chromeUserId); liveKitAgents.push(agent); await new Promise(resolve => setTimeout(resolve, 1000)); } // Step 2: Test voice command routing console.log('\n๐Ÿ“‹ STEP 2: Testing Voice Command Routing'); console.log('=' .repeat(50)); // Send commands from each LiveKit agent for (let i = 0; i < liveKitAgents.length; i++) { const agent = liveKitAgents[i]; const chrome = chromeExtensions[i]; console.log(`\n๐ŸŽ™๏ธ Testing voice command from LiveKit Agent ${i + 1} to Chrome ${i + 1}`); await agent.sendVoiceCommand('chrome_navigate', { url: `https://example.com/user${i + 1}`, userContext: agent.chromeUserId }); // Wait for command to be processed await new Promise(resolve => setTimeout(resolve, 2000)); } // Step 3: Test cross-user isolation console.log('\n๐Ÿ“‹ STEP 3: Testing Cross-User Isolation'); console.log('=' .repeat(50)); // Agent 1 sends command that should only go to Chrome 1 console.log('\n๐Ÿ”’ Testing isolation: Agent 1 โ†’ Chrome 1 only'); await liveKitAgents[0].sendVoiceCommand('chrome_click_element', { selector: '#test-button', userContext: liveKitAgents[0].chromeUserId }); await new Promise(resolve => setTimeout(resolve, 2000)); // Step 4: Verify results console.log('\n๐Ÿ“‹ STEP 4: Verifying Results'); console.log('=' .repeat(50)); chromeExtensions.forEach((chrome, index) => { console.log(`\n๐Ÿ‘ค Chrome Extension ${index + 1} (${chrome.chromeUserId}):`); console.log(` Session ID: ${chrome.sessionInfo?.sessionId}`); console.log(` Commands Received: ${chrome.receivedCommands.length}`); chrome.receivedCommands.forEach((cmd, cmdIndex) => { console.log(` Command ${cmdIndex + 1}: ${cmd.params.name}`); }); }); // Verify isolation const totalCommands = chromeExtensions.reduce((sum, chrome) => sum + chrome.receivedCommands.length, 0); const expectedCommands = liveKitAgents.length * 2; // 2 commands per agent console.log(`\n๐Ÿ“Š RESULTS:`); console.log(` Total Commands Sent: ${expectedCommands}`); console.log(` Total Commands Received: ${totalCommands}`); console.log(` Routing Success: ${totalCommands === expectedCommands ? 'โœ…' : 'โŒ'}`); // Check that each Chrome extension received the right number of commands const isolationSuccess = chromeExtensions.every(chrome => chrome.receivedCommands.length === 2); console.log(` User Isolation: ${isolationSuccess ? 'โœ…' : 'โŒ'}`); if (totalCommands === expectedCommands && isolationSuccess) { console.log('\n๐ŸŽ‰ Voice Command Routing Test PASSED!'); } else { console.log('\nโŒ Voice Command Routing Test FAILED!'); } } catch (error) { console.error('โŒ Test failed:', error); } finally { // Cleanup console.log('\n๐Ÿงน Cleaning up...'); chromeExtensions.forEach(chrome => chrome.disconnect()); setTimeout(() => { console.log('โœ… Test completed'); process.exit(0); }, 2000); } } // Run the test testVoiceCommandRouting().catch(console.error);