Major refactor: Multi-user Chrome MCP extension with remote server architecture

This commit is contained in:
nasir@endelospay.com
2025-08-21 20:09:57 +05:00
parent d97cad1736
commit 5d869f6a7c
125 changed files with 16249 additions and 11906 deletions

View File

@@ -0,0 +1,235 @@
/**
* 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);