webrtc-mcp-chat
Version:
A remote WebRTC chat server with secure temporary rooms and MCP support for background agents
321 lines (278 loc) • 11.8 kB
JavaScript
import { Command } from 'commander';
import fetch from 'node-fetch';
const program = new Command();
const CHAT_SERVER_URL = process.env.CHAT_SERVER_URL || process.env.REMOTE_CHAT_URL || 'http://localhost:3000';
// Helper function to create fetch headers with ngrok support
function createHeaders(additionalHeaders = {}) {
const headers = {
'Content-Type': 'application/json',
...additionalHeaders
};
// Add ngrok header if URL contains ngrok
if (CHAT_SERVER_URL.includes('ngrok') || CHAT_SERVER_URL.includes('ngrok-free.app')) {
headers['ngrok-skip-browser-warning'] = 'true';
}
return headers;
}
// Colors for console output
const colors = {
green: '\x1b[32m',
red: '\x1b[31m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
cyan: '\x1b[36m',
reset: '\x1b[0m',
bold: '\x1b[1m'
};
function colorize(color, text) {
return `${colors[color]}${text}${colors.reset}`;
}
program
.name('chat-room')
.description('CLI tool for secure temporary chat rooms')
.version('2.0.2');
// Create temporary room command
program
.command('create')
.description('Create a secure temporary chat room')
.option('-e, --expires <minutes>', 'Room expiration in minutes (default: 60, max: 1440)', '60')
.option('-c, --created-by <identifier>', 'Creator identifier', 'cli-agent')
.option('-o, --output <format>', 'Output format: json, text, env', 'text')
.action(async (options) => {
try {
const expiresInMinutes = Math.min(parseInt(options.expires), 1440);
console.log(colorize('cyan', '🔐 Creating secure temporary chat room...'));
const response = await fetch(`${CHAT_SERVER_URL}/api/create-temp-room`, {
method: 'POST',
headers: createHeaders(),
body: JSON.stringify({
expiresInMinutes,
createdBy: options.createdBy
})
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.error || 'Failed to create room');
}
if (options.output === 'json') {
console.log(JSON.stringify(result, null, 2));
} else if (options.output === 'env') {
console.log(`export ROOM_ID="${result.roomId}"`);
console.log(`export ROOM_TOKEN="${result.roomToken}"`);
console.log(`export ROOM_URL="${result.joinUrl}"`);
} else {
console.log(colorize('green', '✅ Room created successfully!'));
console.log();
console.log(colorize('bold', '🔐 Room Details:'));
console.log(` ${colorize('cyan', 'Room ID:')} ${result.roomId}`);
console.log(` ${colorize('cyan', 'Token:')} ${result.roomToken}`);
console.log(` ${colorize('cyan', 'Expires:')} ${expiresInMinutes} minutes (${new Date(result.expiresAt).toLocaleString()})`);
console.log(` ${colorize('cyan', 'Join URL:')} ${result.joinUrl}`);
console.log();
console.log(colorize('yellow', '⚠️ Keep the token secure! Anyone with the room ID and token can join.'));
console.log();
console.log(colorize('bold', '🚀 Quick commands:'));
console.log(` ${colorize('green', 'Join:')} chat-room join ${result.roomId} ${result.roomToken} <username>`);
console.log(` ${colorize('green', 'Send:')} chat-room send ${result.roomId} ${result.roomToken} <username> "<message>"`);
console.log(` ${colorize('green', 'Info:')} chat-room info ${result.roomId} ${result.roomToken}`);
}
} catch (error) {
console.error(colorize('red', `❌ Error: ${error.message}`));
process.exit(1);
}
});
// Join room command
program
.command('join')
.description('Join a temporary chat room')
.argument('<roomId>', 'Room ID')
.argument('<roomToken>', 'Room token')
.argument('<username>', 'Your username')
.option('-m, --message <text>', 'Optional join message')
.action(async (roomId, roomToken, username, options) => {
try {
console.log(colorize('cyan', `🚀 Joining room ${roomId} as ${username}...`));
const response = await fetch(`${CHAT_SERVER_URL}/mcp/join`, {
method: 'POST',
headers: createHeaders(),
body: JSON.stringify({
roomId,
roomToken,
username,
message: options.message || `${username} joined the chat`
})
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.error || 'Failed to join room');
}
console.log(colorize('green', '✅ Successfully joined room!'));
console.log(` ${colorize('cyan', 'Message:')} ${result.message}`);
console.log(` ${colorize('cyan', 'Active users:')} ${result.activeUsers}`);
if (result.expiresAt) {
console.log(` ${colorize('yellow', 'Expires:')} ${new Date(result.expiresAt).toLocaleString()}`);
}
} catch (error) {
console.error(colorize('red', `❌ Error: ${error.message}`));
process.exit(1);
}
});
// Send message command
program
.command('send')
.description('Send a message to a temporary chat room')
.argument('<roomId>', 'Room ID')
.argument('<roomToken>', 'Room token')
.argument('<username>', 'Your username')
.argument('<message>', 'Message to send')
.action(async (roomId, roomToken, username, message) => {
try {
const response = await fetch(`${CHAT_SERVER_URL}/mcp/message`, {
method: 'POST',
headers: createHeaders(),
body: JSON.stringify({
roomId,
roomToken,
username,
message
})
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.error || 'Failed to send message');
}
console.log(colorize('green', '💬 Message sent!'));
console.log(` ${colorize('cyan', 'Room:')} ${roomId}`);
console.log(` ${colorize('cyan', 'Message:')} "${message}"`);
console.log(` ${colorize('cyan', 'Active users:')} ${result.activeUsers}`);
} catch (error) {
console.error(colorize('red', `❌ Error: ${error.message}`));
process.exit(1);
}
});
// Room info command
program
.command('info')
.description('Get information about a temporary chat room')
.argument('<roomId>', 'Room ID')
.argument('<roomToken>', 'Room token')
.option('-o, --output <format>', 'Output format: json, text', 'text')
.action(async (roomId, roomToken, options) => {
try {
const response = await fetch(`${CHAT_SERVER_URL}/api/temp-room/${roomId}?token=${roomToken}`, {
headers: createHeaders()
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.error || 'Failed to get room info');
}
if (options.output === 'json') {
console.log(JSON.stringify(result, null, 2));
} else {
console.log(colorize('cyan', `🔐 Room ${roomId} Information:`));
console.log();
console.log(` ${colorize('bold', 'Active users:')} ${result.userCount}`);
if (result.users && result.users.length > 0) {
result.users.forEach(user => {
const type = user.isMCP ? colorize('yellow', 'MCP') : colorize('blue', 'Web');
console.log(` • ${user.username} (${type})`);
});
} else {
console.log(` ${colorize('yellow', '• No users currently active')}`);
}
console.log();
if (result.expiresAt) {
console.log(` ${colorize('cyan', 'Expires:')} ${new Date(result.expiresAt).toLocaleString()}`);
}
console.log(` ${colorize('cyan', 'Created by:')} ${result.createdBy || 'Unknown'}`);
console.log();
console.log(colorize('yellow', '💡 Note: Web users have video/audio, MCP users have text chat'));
}
} catch (error) {
console.error(colorize('red', `❌ Error: ${error.message}`));
process.exit(1);
}
});
// Health check command
program
.command('health')
.description('Check the chat server status')
.action(async () => {
try {
console.log(colorize('cyan', '🔍 Checking server health...'));
const response = await fetch(`${CHAT_SERVER_URL}/health`, {
headers: createHeaders()
});
const result = await response.json();
console.log(colorize('green', `🟢 Server is ${result.status}`));
console.log();
console.log(` ${colorize('cyan', 'Server URL:')} ${result.serverUrl}`);
console.log(` ${colorize('cyan', 'Remote mode:')} ${result.remoteMode}`);
console.log(` ${colorize('cyan', 'Active rooms:')} ${result.activeRooms}`);
console.log(` ${colorize('cyan', 'Temporary rooms:')} ${result.temporaryRooms}`);
console.log(` ${colorize('cyan', 'Connected users:')} ${result.connectedUsers}`);
console.log();
console.log(colorize('green', '✅ Server is ready for operations!'));
} catch (error) {
console.error(colorize('red', '🔴 Server health check failed'));
console.error(` ${colorize('cyan', 'Server URL:')} ${CHAT_SERVER_URL}`);
console.error(` ${colorize('red', 'Error:')} ${error.message}`);
console.log();
console.log(colorize('yellow', '💡 Check:'));
console.log(' 1. Server is running and accessible');
console.log(' 2. CHAT_SERVER_URL environment variable is correct');
console.log(' 3. Network connectivity to the server');
process.exit(1);
}
});
// Interactive mode command
program
.command('interactive')
.alias('i')
.description('Start interactive mode for background agents')
.action(async () => {
console.log(colorize('bold', '🤖 Background Agent Interactive Mode'));
console.log(colorize('cyan', '='.repeat(40)));
console.log();
try {
// Check server health first
await fetch(`${CHAT_SERVER_URL}/health`, {
headers: createHeaders()
});
console.log(colorize('green', '✅ Connected to chat server'));
console.log(` ${colorize('cyan', 'Server:')} ${CHAT_SERVER_URL}`);
console.log();
console.log(colorize('bold', '🚀 Quick start for agents:'));
console.log();
console.log(colorize('yellow', '1. Create a secure room:'));
console.log(` ${colorize('green', 'chat-room create --expires 120 --created-by agent-1')}`);
console.log();
console.log(colorize('yellow', '2. Share room credentials with other agents'));
console.log();
console.log(colorize('yellow', '3. Join and communicate:'));
console.log(` ${colorize('green', 'chat-room join <roomId> <token> agent-2')}`);
console.log(` ${colorize('green', 'chat-room send <roomId> <token> agent-2 "Hello from agent!"')}`);
console.log();
console.log(colorize('yellow', '4. Room automatically expires and cleans up'));
console.log();
console.log(colorize('cyan', '💡 Perfect for:'));
console.log(' • Inter-agent coordination');
console.log(' • CI/CD notifications');
console.log(' • Temporary service communication');
console.log(' • Secure agent-to-agent messaging');
console.log();
console.log(colorize('bold', 'Environment Setup:'));
console.log(` ${colorize('cyan', 'export CHAT_SERVER_URL=')}${CHAT_SERVER_URL}`);
} catch (error) {
console.error(colorize('red', '❌ Cannot connect to chat server'));
console.error(` ${colorize('cyan', 'URL:')} ${CHAT_SERVER_URL}`);
console.error(` ${colorize('red', 'Error:')} ${error.message}`);
process.exit(1);
}
});
// Error handling
program.configureOutput({
writeErr: (str) => process.stderr.write(colorize('red', str))
});
program.parse();