UNPKG

overmind-mcp

Version:

Orchestrateur universel agents IA multi-modeles via MCP. Inclut le protocole 'Custom-Nickname' pour identifier vos agents avec des surnoms originaux (The Chaos Prophet, Shadow Sniper, etc.), l'isolation mémoire (Private Memory Context) et le support pour

465 lines (387 loc) • 18 kB
#!/usr/bin/env node /** * ═══════════════════════════════════════════════════════════════════════════════ * INSTALL-DEPENDENCIES SCRIPT (SIMPLIFIÉ) * ═══════════════════════════════════════════════════════════════════════════════ * Script cross-platform qui détecte et installe uniquement ce qui manque : * - Docker Desktop / Docker Engine * - PostgreSQL + pgvector (uniquement) * * Supporte: Windows (Docker Desktop), Linux (Docker Engine), macOS (Docker Desktop) * ═══════════════════════════════════════════════════════════════════════════════ */ import { execSync, spawn } from 'child_process'; import { existsSync } from 'fs'; import { join } from 'path'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; import { randomBytes } from 'crypto'; import { createInterface } from 'readline'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // ═══════════════════════════════════════════════════════════════════════════════ // UTILS // ═══════════════════════════════════════════════════════════════════════════════ function logSection(title) { console.log('\n╔══════════════════════════════════════════════════════════════════╗'); console.log(`║ ${title.padEnd(64)} ║`); console.log('╚══════════════════════════════════════════════════════════════════╝'); } function runCommand(cmd, options = {}) { try { return execSync(cmd, { stdio: 'pipe', encoding: 'utf8', ...options }); } catch { return null; } } function runCommandAsync(cmd, description) { return new Promise((resolve, reject) => { console.log(`🔧 ${description}`); console.log(` $ ${cmd}`); const child = spawn(cmd, { shell: true, stdio: 'inherit' }); child.on('close', (code) => { if (code === 0) { console.log(`✅ ${description} terminé`); resolve(true); } else { console.error(`❌ Erreur (code ${code})`); reject(new Error(`Command failed with code ${code}`)); } }); child.on('error', (err) => { console.error('❌ Erreur:', err.message); reject(err); }); }); } function promptYesNo(question) { const rl = createInterface({ input: process.stdin, output: process.stdout, }); return new Promise((resolve) => { rl.question(`${question} (y/N): `, (answer) => { rl.close(); resolve(answer.toLowerCase().startsWith('y')); }); }); } function openUrl(url) { const platform = process.platform; if (platform === 'win32') { runCommand(`start ${url}`, { stdio: 'ignore' }); } else if (platform === 'darwin') { runCommand(`open ${url}`, { stdio: 'ignore' }); } else { runCommand(`xdg-open ${url}`, { stdio: 'ignore' }); } } // ═══════════════════════════════════════════════════════════════════════════════ // DOCKER DETECTION // ═══════════════════════════════════════════════════════════════════════════════ async function checkDocker() { logSection('VÉRIFICATION DOCKER'); // Check if Docker is installed const version = runCommand('docker --version'); if (!version) { console.log('❌ Docker non trouvé'); return null; } console.log('✅ Docker détecté:', version.trim()); // Detect Docker Desktop vs Docker Engine const platform = process.platform; if (platform === 'win32' || platform === 'darwin') { // Windows/macOS: Check Docker Desktop const desktopStatus = runCommand('docker info --format "{{.OperatingSystem}}"'); if (desktopStatus?.includes('Docker Desktop')) { console.log('✅ Docker Desktop détecté'); return { type: 'desktop', platform }; } } else if (platform === 'linux') { // Linux: Docker Engine console.log('✅ Docker Engine (Linux) détecté'); return { type: 'engine', platform }; } return { type: 'unknown', platform }; } async function installDockerIfNeeded() { const dockerInfo = await checkDocker(); if (dockerInfo) { return dockerInfo; } console.log(''); console.log('📥 Installation Docker requise:'); console.log(' • Windows: Docker Desktop for Windows'); console.log(' • macOS: Docker Desktop for Mac'); console.log(' • Linux: Docker Engine (apt/yum/dnf)'); console.log(''); const answer = await promptYesNo('Voulez-vous ouvrir le site de téléchargement ?'); if (answer) { const url = 'https://www.docker.com/products/docker-desktop/'; openUrl(url); console.log('✅ Navigateur ouvert. Installez Docker, puis relancez ce script.'); } return null; } // ═══════════════════════════════════════════════════════════════════════════════ // POSTGRESQL + PGVECTOR DETECTION & INSTALLATION // ═══════════════════════════════════════════════════════════════════════════════ async function detectExistingPostgreSQL() { logSection('DÉTECTION POSTGRESQL EXISTANT'); // 1. Check native PostgreSQL (psql command) const nativePg = runCommand('psql --version'); if (nativePg) { console.log('✅ PostgreSQL natif détecté:', nativePg.trim()); // Check if pgvector is installed const pgvectorCheck = runCommand('psql -U postgres -t -c "SELECT extname FROM pg_extension WHERE extname = \'vector\';"'); if (pgvectorCheck?.includes('vector')) { console.log('✅ pgvector est installé'); return { type: 'native', hasPgvector: true }; } else { console.log('⚠️ pgvector NON installé (installation manuelle requise)'); return { type: 'native', hasPgvector: false }; } } // 2. Check Docker containers with pgvector (multiple detection methods) console.log('🔍 Recherche containers PostgreSQL avec pgvector...'); // Method A: Check by image name (pgvector/pgvector) let containers = runCommand('docker ps --filter "ancestor=pgvector/pgvector" --format "{{.Names}}"', { stdio: 'pipe' }); // Method B: Check all containers with "pgvector" or "postgres" in name if (!containers) { containers = runCommand('docker ps --format "{{.Names}}" | grep -E "(pgvector|postgres)"', { stdio: 'pipe', shell: true }); } if (containers) { const containerList = containers.trim().split('\n').filter(Boolean); console.log(`✅ ${containerList.length} container(s) PostgreSQL détecté(s):`); containerList.forEach(name => console.log(` • ${name}`)); // Check each container for pgvector for (const container of containerList) { try { const pgvectorCheck = runCommand( `docker exec ${container} psql -U postgres -t -c "SELECT extname FROM pg_extension WHERE extname = 'vector';" 2>&1`, { stdio: 'pipe' } ); if (pgvectorCheck?.includes('vector')) { console.log(`✅ pgvector actif dans ${container}`); return { type: 'docker', container, hasPgvector: true }; } } catch { // Container might not be accessible, try next } } console.log('⚠️ Containers trouvés mais SANS pgvector installé'); } // 3. Check for any PostgreSQL container (might not have pgvector) const allContainers = runCommand('docker ps --format "{{.Names}}"', { stdio: 'pipe' }); if (allContainers) { const pgContainers = allContainers .trim() .split('\n') .filter(name => name.toLowerCase().includes('postgres') || name.toLowerCase().includes('pgvector')); if (pgContainers.length > 0) { console.log(`⚠️ ${pgContainers.length} container(s) PostgreSQL détecté(s) SANS pgvector:`); pgContainers.forEach(name => console.log(` • ${name}`)); console.log(''); console.log('💡 Le script va créer un nouveau container dédié.'); } } if (genericContainers) { const containerList = genericContainers.trim().split('\n').filter(Boolean); console.log(`⚠️ ${containerList.length} container(s) PostgreSQL détecté(s) SANS pgvector:`); containerList.forEach(name => console.log(` • ${name}`)); console.log(''); console.log('💡 Ces containers n\'ont PAS pgvector. Le script va créer un nouveau container dédié.'); } return null; } async function waitForPostgreSQL(containerName, maxWait = 60) { console.log(`⏳ Attente démarrage PostgreSQL (max ${maxWait}s)...`); for (let i = 0; i < maxWait; i++) { try { execSync( `docker exec ${containerName} pg_isready -U postgres`, { stdio: 'pipe', encoding: 'utf8' } ); console.log('✅ PostgreSQL prêt !'); return true; } catch { process.stdout.write('.'); await new Promise(resolve => setTimeout(resolve, 1000)); } } console.log(''); console.log('❌ Timeout: PostgreSQL n\'a pas démarré à temps'); return false; } async function installPostgreSQLDocker() { logSection('INSTALLATION POSTGRESQL + PGVECTOR (DOCKER)'); const containerName = 'overmind-postgres-pgvector'; try { // Pull image console.log('📥 Téléchargement image pgvector/pgvector:pg16...'); await runCommandAsync( 'docker pull pgvector/pgvector:pg16', 'Téléchargement image' ); // Remove existing container if present try { execSync(`docker inspect ${containerName}`, { stdio: 'pipe' }); console.log(`⚠️ Container ${containerName} existe déjà. Suppression...`); execSync(`docker rm -f ${containerName}`, { stdio: 'pipe' }); } catch { // Container doesn't exist, that's fine } // Create volume if doesn't exist try { execSync('docker volume create overmind_postgres_data', { stdio: 'pipe' }); } catch { // Volume might already exist } // Run container console.log('🚀 Démarrage container PostgreSQL...'); const runCommand = [ 'docker', 'run', '-d', '--name', containerName, '-p', '5432:5432', '-e', `POSTGRES_PASSWORD=${randomBytes(18).toString('base64url')}`, '-e', 'POSTGRES_USER=postgres', '-e', 'POSTGRES_DB=overmind_memory', '-v', 'overmind_postgres_data:/var/lib/postgresql/data', '--restart', 'unless-stopped', 'pgvector/pgvector:pg16' ].join(' '); await runCommandAsync(runCommand, 'Démarrage PostgreSQL'); // Wait for PostgreSQL to be ready const ready = await waitForPostgreSQL(containerName); if (!ready) { throw new Error('PostgreSQL n\'a pas démarré'); } // Test connection console.log('🔍 Test connexion PostgreSQL...'); await runCommandAsync( `docker exec ${containerName} psql -U postgres -c "SELECT version();"`, 'Test connexion' ); // Enable pgvector console.log('🔧 Activation extension pgvector...'); await runCommandAsync( `docker exec ${containerName} psql -U postgres -c "CREATE EXTENSION IF NOT EXISTS vector;"`, 'Activation pgvector' ); // Verify pgvector console.log('✅ Vérification pgvector...'); const result = runCommand( `docker exec ${containerName} psql -U postgres -t -c "SELECT extname FROM pg_extension WHERE extname = 'vector';"`, { encoding: 'utf8', stdio: 'pipe' } ).trim(); if (result.includes('vector')) { console.log('✅ pgvector installé avec succès !'); } else { throw new Error('pgvector non trouvé'); } console.log(''); console.log('🎉 PostgreSQL + pgvector installés !'); console.log(''); console.log('📋 Détails:'); console.log(` Container: ${containerName}`); console.log(' Port: 5432'); console.log(' User: postgres'); console.log(' Password: (généré automatiquement — voir ~/.overmind/.env)'); console.log(' Extension: vector (pgvector)'); return { type: 'docker', container: containerName, hasPgvector: true }; } catch (error) { console.error('❌ Erreur installation PostgreSQL:', error.message); console.error(''); console.log('💡 Solution manuelle:'); console.log(` docker run -d --name ${containerName} \\`); console.log(' -p 5432:5432 \\'); console.log(' -e POSTGRES_PASSWORD=votre_pass \\'); console.log(' -e POSTGRES_USER=postgres \\'); console.log(' -v overmind_postgres_data:/var/lib/postgresql/data \\'); console.log(' --restart unless-stopped \\'); console.log(' pgvector/pgvector:pg16'); console.log(''); console.log('Ou en une ligne (Windows):'); console.log(` docker run -d --name ${containerName} -p 5432:5432 -e POSTGRES_PASSWORD=votre_pass -e POSTGRES_USER=postgres -v overmind_postgres_data:/var/lib/postgresql/data --restart unless-stopped pgvector/pgvector:pg16`); return null; } } // ═══════════════════════════════════════════════════════════════════════════════ // MAIN // ═══════════════════════════════════════════════════════════════════════════════ async function main() { console.log('╔══════════════════════════════════════════════════════════════════╗'); console.log('║ ║'); console.log('║ 🗄️️ INSTALLATION POSTGRESQL + PGVECTOR ║'); console.log('║ Détection & Installation Automatique ║'); console.log('║ ║'); console.log('╚══════════════════════════════════════════════════════════════════╝'); console.log(''); // Step 1: Check/Install Docker const dockerInfo = await installDockerIfNeeded(); if (!dockerInfo) { console.log('\n⚠️ Relancez ce script après avoir installé Docker.'); process.exit(1); } console.log(''); console.log(`📊 Plateforme: ${dockerInfo.type} (${dockerInfo.platform})`); console.log(''); // Step 2: Detect existing PostgreSQL const existingPg = await detectExistingPostgreSQL(); if (existingPg && existingPg.hasPgvector) { console.log(''); console.log('✅ PostgreSQL + pgvector sont déjà installés !'); if (existingPg.type === 'docker') { console.log(` Container: ${existingPg.container}`); console.log(' Port: 5432'); console.log(' User: postgres'); console.log(' Extension: vector (pgvector)'); } else if (existingPg.type === 'native') { console.log(' Installation native détectée'); console.log(' Extension: vector (pgvector)'); } console.log(''); console.log('🎉 Rien à installer !'); return; } // Step 3: Install PostgreSQL + pgvector console.log(''); console.log('📦 Installation de PostgreSQL + pgvector requise...'); const installResult = await installPostgreSQLDocker(); if (!installResult) { console.log('\n⚠️ Installation échouée. Vérifiez les erreurs ci-dessus.'); process.exit(1); } // Success logSection('✅ INSTALLATION TERMINÉE'); console.log(` 🎉 PostgreSQL + pgvector sont prêts ! 📋 PROCHAINE ÉTAPE: → Relancez: overmind-setup Ou manuellement: npm install -g overmind-mcp 💡 RAPPEL: • Container: ${installResult.container} • Port: 5432 • User: postgres • Password: (généré automatiquement — voir ~/.overmind/.env) • Extension: vector (pgvector) ═════════════════════════════════════════════════════════════════════ `); } // Run main // Check if this file is being run directly (not imported) const modulePath = fileURLToPath(import.meta.url); const isMainModule = process.argv[1] === modulePath; if (isMainModule) { main().catch((error) => { console.error('\n❌ ERREUR FATALE:', error.message); process.exit(1); }); }