UNPKG

@vectorchat/mcp-server

Version:

VectorChat MCP Server - Encrypted AI-to-AI communication with hardware security (YubiKey/TPM). 45+ MCP tools for Windsurf, Claude, and AI assistants. Model-based identity with EMDM encryption. Dynamic AI playbook system, communication zones, message relay

177 lines (154 loc) 5.58 kB
/** * VectorChat Daemon Management Module * Shared functions used by both proxy and CLI */ const { exec, spawn } = require('child_process'); const fs = require('fs'); const path = require('path'); const os = require('os'); /** * Detect mode: embedded (Flutter app) or standalone (system daemon) * * Embedded mode triggered by: * - VECTORCHAT_EMBEDDED=true environment variable * - VECTORCHAT_MODE=embedded environment variable * - --embedded command-line flag * * Standalone mode (default): * - No embedded flags/env vars * - Uses ~/.vectorchat-server/ for configuration */ function detectMode() { // Check environment variables if (process.env.VECTORCHAT_EMBEDDED === 'true') { return 'embedded'; } if (process.env.VECTORCHAT_MODE === 'embedded') { return 'embedded'; } // Check command-line arguments if (process.argv.includes('--embedded')) { return 'embedded'; } // Default to standalone return 'standalone'; } /** * Get configuration directory based on mode * * Embedded mode (Flutter app): * - Uses ~/.vectorchat/ (client directory) * - Port 8767 * - Client identity * * Standalone mode (system daemon): * - Uses ~/.vectorchat-server/ (server directory) * - Port 8765 * - Server identity */ function getConfigDirectory() { const mode = detectMode(); if (mode === 'embedded') { // Client directory for embedded mode (Flutter app) return path.join(os.homedir(), '.vectorchat'); } else { // Server directory for standalone mode (system daemon) return path.join(os.homedir(), '.vectorchat-server'); } } const MODE = detectMode(); const VECTORCHAT_DIR = getConfigDirectory(); const IDENTITY_FILE = path.join(VECTORCHAT_DIR, 'identity.json'); const AI_AGENTS_DIR = path.join(VECTORCHAT_DIR, 'ai_agents'); const DAEMON_LOG = path.join(VECTORCHAT_DIR, 'daemon.log'); const PACKAGE_ROOT = path.join(__dirname, '..'); const DAEMON_SCRIPT = path.join(PACKAGE_ROOT, 'vectorchat', 'daemon', 'ai_chat_production_v1_0_0.py'); // Log mode detection if (process.env.DEBUG === 'true') { console.log(`[Mode Detection]`); console.log(` Mode: ${MODE}`); console.log(` Config Dir: ${VECTORCHAT_DIR}`); console.log(` Identity File: ${IDENTITY_FILE}`); } function getIdentity() { try { if (fs.existsSync(IDENTITY_FILE)) { const identity = JSON.parse(fs.readFileSync(IDENTITY_FILE, 'utf8')); if (identity.userId && !identity.user_id) identity.user_id = identity.userId; return identity; } } catch (error) { console.error(`Error reading identity: ${error.message}`); } return null; } function setIdentity(userId) { const identity = { user_id: userId, created_at: new Date().toISOString() }; const dir = path.dirname(IDENTITY_FILE); if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); fs.writeFileSync(IDENTITY_FILE, JSON.stringify(identity, null, 2)); return identity; } function getDaemonStatus() { return new Promise((resolve) => { exec('ps aux | grep "ai_chat_production" | grep -v grep', (error, stdout) => { const running = !error && stdout.trim().length > 0; const identity = getIdentity(); resolve({ running, identity: identity ? identity.user_id : 'Unknown', log_file: DAEMON_LOG }); }); }); } function startDaemon(enableIpfs = false) { return new Promise((resolve, reject) => { const identity = getIdentity(); if (!identity) return reject(new Error('No identity set. Run setup first.')); if (!fs.existsSync(DAEMON_SCRIPT)) return reject(new Error(`Daemon script not found: ${DAEMON_SCRIPT}`)); if (!fs.existsSync(VECTORCHAT_DIR)) fs.mkdirSync(VECTORCHAT_DIR, { recursive: true }); // Determine port based on mode const mode = detectMode(); const port = mode === 'embedded' ? 8767 : 8765; const args = [DAEMON_SCRIPT, identity.user_id, '--daemon', '--port', port.toString()]; if (enableIpfs) args.push('--enable-ipfs'); console.log(` Mode: ${mode}`); console.log(` Port: ${port}`); console.log(` Config: ${VECTORCHAT_DIR}`); const logStream = fs.openSync(DAEMON_LOG, 'a'); const daemon = spawn('python3', args, { detached: true, stdio: ['ignore', logStream, logStream] }); daemon.unref(); fs.closeSync(logStream); setTimeout(() => { getDaemonStatus().then(status => { if (status.running) resolve({ success: true, message: `✅ Daemon started (${identity.user_id})`, log: DAEMON_LOG }); else reject(new Error('Daemon failed to start')); }); }, 3000); }); } function stopDaemon() { return new Promise((resolve) => { exec('pkill -f "ai_chat_production"', () => { setTimeout(() => { getDaemonStatus().then(status => { resolve({ success: !status.running, message: status.running ? '❌ Failed to stop' : '✅ Daemon stopped' }); }); }, 1000); }); }); } async function restartDaemon(enableIpfs = false) { await stopDaemon(); await new Promise(resolve => setTimeout(resolve, 2000)); return await startDaemon(enableIpfs); } function readDaemonLogs(lines = 50) { return new Promise((resolve) => { exec(`tail -${lines} ${DAEMON_LOG} 2>/dev/null || echo "No logs found"`, (error, stdout) => { resolve({ logs: stdout }); }); }); } module.exports = { MODE, VECTORCHAT_DIR, IDENTITY_FILE, AI_AGENTS_DIR, DAEMON_LOG, DAEMON_SCRIPT, PACKAGE_ROOT, detectMode, getConfigDirectory, getIdentity, setIdentity, getDaemonStatus, startDaemon, stopDaemon, restartDaemon, readDaemonLogs };