236 lines
7.3 KiB
JavaScript
236 lines
7.3 KiB
JavaScript
/**
|
|
* 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);
|