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

722 lines (615 loc) • 29.8 kB
#!/usr/bin/env node /** * ═══════════════════════════════════════════════════════════════════════════════ * OVERMIND-MCP - POST-INSTALL AUTOMATIQUE (SIMPLIFIÉ) * ═══════════════════════════════════════════════════════════════════════════════ * Script exécuté automatiquement après npm install -g overmind-mcp * INSTALLE UNIQUEMENT : * - Vérifie Docker * - Installe PostgreSQL + pgvector (si absent) * - Copie .env.example → .env * - Copie .mcp.json.example → .mcp.json * ═══════════════════════════════════════════════════════════════════════════════ */ import { execSync, spawn } from 'child_process'; import { existsSync, mkdirSync, writeFileSync, readFileSync, lstatSync, renameSync, symlinkSync, unlinkSync, cpSync, } from 'fs'; import { join, basename } from 'path'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; import { randomBytes } from 'crypto'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const INSTALL_DIR = join( process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH, '.overmind' ); // ═══════════════════════════════════════════════════════════════════════════════ // GLOBAL PASSWORD — One single source of truth // ═══════════════════════════════════════════════════════════════════════════════ const PG_PASSWORD = randomBytes(18).toString('base64url'); // ═══════════════════════════════════════════════════════════════════════════════ // COLORS // ═════════════════════════════════════════════════════════════════════════════ const COLORS = { cyan: '\x1b[36m', green: '\x1b[32m', yellow: '\x1b[33m', red: '\x1b[31m', white: '\x1b[37m', reset: '\x1b[0m' }; function log(color, str) { console.log(`${color}${str}${COLORS.reset}`); } 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; } } async 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); }); }); } // ═══════════════════════════════════════════════════════════════════════════════ // INSTALLATION STEPS // ═══════════════════════════════════════════════════════════════════════════════ /** * Détecte si on doit proposer le mode natif (sans Docker). * Critères : --native explicite, OU pas de Docker + Linux. */ function shouldOfferNative() { if (process.argv.includes('--native')) return { native: true, reason: 'flag --native' }; const isLinux = process.platform === 'linux'; const hasDocker = runCommand('docker --version'); if (isLinux && !hasDocker) { return { native: true, reason: 'Linux détecté, Docker absent' }; } return { native: false }; } async function offerNativeInstall(reason) { logSection('🐧 MODE NATIF SANS DOCKER DISPONIBLE'); log(COLORS.yellow, `Raison : ${reason}`); log(COLORS.cyan, ''); log(COLORS.cyan, 'Ce package supporte aussi une installation 100% native (sans Docker) :'); log(COLORS.cyan, ' • PostgreSQL 18 + pgvector via apt'); log(COLORS.cyan, ' • Services systemd (overmind-mcp, overmind-postgres-mcp)'); log(COLORS.cyan, ' • Bind 127.0.0.1 (plus sûr)'); log(COLORS.cyan, ''); log(COLORS.green, 'Pour lancer l\'install native :'); log(COLORS.white, ' sudo overmind-install-native'); log(COLORS.cyan, ''); log(COLORS.cyan, 'Le script natif est embarqué dans le package à :'); log(COLORS.white, ' /usr/lib/node_modules/overmind-mcp/bin/install-overmind-native.sh'); log(COLORS.cyan, ''); log(COLORS.cyan, 'Si Docker est intentionnel, poursuivez ci-dessous.'); log(COLORS.cyan, ''); } async function checkDocker() { logSection('VÉRIFICATION DOCKER'); const version = runCommand('docker --version'); if (!version) { log(COLORS.red, '❌ Docker non trouvé'); console.log(''); log(COLORS.yellow, '📥 Installation Docker requise:'); const platform = process.platform; if (platform === 'win32') { console.log(' Windows: Docker Desktop, Rancher Desktop, ou Podman'); console.log(' https://www.docker.com/products/docker-desktop/'); } else if (platform === 'darwin') { console.log(' macOS: Docker Desktop, Colima, OrbStack, ou Podman'); console.log(' https://www.docker.com/products/docker-desktop/'); } else { console.log(' Linux: Docker Engine, Podman, ou rootless Docker'); console.log(' https://docs.docker.com/engine/install/'); console.log(' Podman: https://podman.io/getting-started/installation'); } log(COLORS.cyan, '\nAprès installation de Docker, relancez: npm install -g overmind-mcp'); return false; } // Détecter le type d'implémentation Docker let implType = 'Docker'; try { const dockerInfo = runCommand('docker info --format "{{.ServerVersion}}"'); if (dockerInfo) { // Essayer de détecter Podman const podmanCheck = runCommand('docker info --format "{{.OperatingSystem}}"'); if (podmanCheck && podmanCheck.toLowerCase().includes('podman')) { implType = 'Podman'; } } } catch (e) { // Ignorer les erreurs de détection } log(COLORS.green, '✅ Docker détecté: ' + version.trim()); if (implType !== 'Docker') { log(COLORS.cyan, ' Implémentation: ' + implType); } return true; } async function setupPostgreSQL() { logSection('INSTALLATION POSTGRESQL + PGVECTOR'); // Check if already exists const existingContainer = runCommand( 'docker ps --filter "name=postgres" --format "{{.Names}}"', { stdio: 'pipe' } ); if (existingContainer) { log(COLORS.green, '✅ PostgreSQL + pgvector déjà installé'); log(COLORS.cyan, ' Container: ' + existingContainer.trim()); return true; } log(COLORS.yellow, '📦 Installation PostgreSQL + pgvector...'); try { await runCommandAsync( 'docker pull pgvector/pgvector:pg16', 'Téléchargement image' ); // Remove existing ONLY if stopped (never kill a running container) const existingStopped = runCommand('docker ps -a --filter "name=overmind-postgres-pgvector" --filter "status=exited" --format "{{.Names}}"', { stdio: 'pipe' }); if (existingStopped) { runCommand('docker rm -f overmind-postgres-pgvector', { stdio: 'pipe' }); } const runCmd = [ 'docker', 'run', '-d', '--name', 'overmind-postgres-pgvector', '-p', '5432:5432', '-e', `POSTGRES_PASSWORD=${PG_PASSWORD}`, '-e', 'POSTGRES_DB=overmind_memory', '-e', 'POSTGRES_USER=postgres', '-v', 'overmind_postgres_data:/var/lib/postgresql/data', '--restart', 'unless-stopped', 'pgvector/pgvector:pg16' ].join(' '); await runCommandAsync(runCmd, 'Démarrage PostgreSQL'); log(COLORS.cyan, '\n⏳ Attente démarrage PostgreSQL (20s)...'); await new Promise(resolve => setTimeout(resolve, 20000)); // Enable pgvector await runCommandAsync( `docker exec overmind-postgres-pgvector psql -U postgres -c "CREATE EXTENSION IF NOT EXISTS vector;"`, 'Activation pgvector' ); log(COLORS.green, '\n✅ PostgreSQL + pgvector installés !'); return true; } catch (error) { log(COLORS.red, '❌ Erreur installation PostgreSQL: ' + error.message); return false; } } async function setupConfigFiles() { logSection('TÉLÉCHARGEMENT CONFIGURATIONS'); mkdirSync(INSTALL_DIR, { recursive: true }); log(COLORS.yellow, '📥 Téléchargement fichiers de configuration...'); const envExampleUrl = 'https://raw.githubusercontent.com/DeamonDev888/overmind-mcp/main/.env.example'; const mcpExampleUrl = 'https://raw.githubusercontent.com/DeamonDev888/overmind-mcp/main/.mcp.json.example'; try { // Télécharger .env.example const envExampleData = runCommand(`curl -sL --max-time 30 ${envExampleUrl}`); if (envExampleData) { writeFileSync(join(INSTALL_DIR, '.env.example'), envExampleData); log(COLORS.green, '✅ .env.example téléchargé'); } // Télécharger .mcp.json.example const mcpExampleData = runCommand(`curl -sL --max-time 30 ${mcpExampleUrl}`); if (mcpExampleData) { writeFileSync(join(INSTALL_DIR, '.mcp.json.example'), mcpExampleData); log(COLORS.green, '✅ .mcp.json.example téléchargé'); } return true; } catch (error) { log(COLORS.red, '❌ Erreur téléchargement: ' + error.message); return false; } } async function installPostgresMCP() { logSection('INSTALLATION OVERMIND-POSTGRES-MCP'); log(COLORS.yellow, '📦 Installation du serveur MCP PostgreSQL...'); try { // Vérifier si déjà installé const checkInstalled = runCommand('npm list -g overmind-postgres-mcp', { stdio: 'pipe' }); if (checkInstalled && checkInstalled.includes('overmind-postgres-mcp')) { log(COLORS.green, '✅ overmind-postgres-mcp déjà installé'); return true; } await runCommandAsync( 'npm install -g overmind-postgres-mcp', 'Installation overmind-postgres-mcp' ); log(COLORS.green, '✅ overmind-postgres-mcp installé avec succès !'); return true; } catch (error) { log(COLORS.yellow, "⚠️ Erreur installation overmind-postgres-mcp: " + error.message); log(COLORS.cyan, "💡 Vous pouvez l'installer manuellement: npm install -g overmind-postgres-mcp"); return false; // Non bloquant } } function copyIfMissing(src, dest) { try { if (!existsSync(src) || existsSync(dest)) return false; cpSync(src, dest, { recursive: true, preserveTimestamps: true }); return true; } catch { return false; } } function alignHermesHome() { logSection('ALIGNEMENT HERMES_HOME'); // Windows Desktop utilise %LOCALAPPDATA%\\hermes. Ne pas symlinker ~/.hermes sur Windows. if (process.platform === 'win32') { log(COLORS.cyan, 'Windows détecté: Hermes Desktop utilise %LOCALAPPDATA%\\hermes. Symlink ~/.hermes ignoré.'); return true; } const home = process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH; const overmindHermes = join(INSTALL_DIR, 'hermes'); const nativeHermes = join(home, '.hermes'); const profilesDir = join(overmindHermes, 'profiles'); mkdirSync(profilesDir, { recursive: true }); mkdirSync(join(overmindHermes, 'distributions'), { recursive: true }); try { if (!existsSync(nativeHermes)) { symlinkSync(overmindHermes, nativeHermes, 'dir'); log(COLORS.green, `✅ Symlink créé: ~/.hermes → ${overmindHermes}`); return true; } const stat = lstatSync(nativeHermes); if (stat.isSymbolicLink()) { const target = runCommand(`readlink "${nativeHermes}"`)?.trim(); if (target === overmindHermes) { log(COLORS.green, '✅ ~/.hermes pointe déjà vers ~/.overmind/hermes'); return true; } log(COLORS.yellow, `⚠️ ~/.hermes pointe vers ${target || '(inconnu)'} — correction`); unlinkSync(nativeHermes); symlinkSync(overmindHermes, nativeHermes, 'dir'); log(COLORS.green, '✅ Symlink ~/.hermes corrigé'); return true; } if (!stat.isDirectory()) { log(COLORS.yellow, '⚠️ ~/.hermes existe mais n’est pas un dossier — backup + symlink'); } else { log(COLORS.yellow, '⚠️ Dual-path détecté: ~/.hermes est un vrai dossier — migration non destructive'); } // Copier les profils de ~/.hermes/profiles vers ~/.overmind/hermes/profiles sans écraser. const nativeProfiles = join(nativeHermes, 'profiles'); if (existsSync(nativeProfiles)) { for (const entry of runCommand(`find "${nativeProfiles}" -mindepth 1 -maxdepth 1 -type d 2>/dev/null`)?.split('\n') || []) { if (!entry.trim()) continue; const name = basename(entry.trim()); const destProfile = join(profilesDir, name); mkdirSync(destProfile, { recursive: true }); for (const file of ['config.yaml', 'SOUL.md', '.env', '.mcp.json', 'profile.yaml', 'workspace.yaml', 'auth.json', 'state.db']) { if (copyIfMissing(join(entry.trim(), file), join(destProfile, file))) { log(COLORS.cyan, ` Copie profil ${name}/${file}`); } } for (const dir of ['skills', 'memories', 'cron', 'hooks', 'sessions']) { if (copyIfMissing(join(entry.trim(), dir), join(destProfile, dir))) { log(COLORS.cyan, ` Copie profil ${name}/${dir}/`); } } } } // Copier les fichiers globaux sans écraser. for (const file of ['config.yaml', 'auth.json', '.env', '.mcp.json', 'SOUL.md', 'state.db']) { if (copyIfMissing(join(nativeHermes, file), join(overmindHermes, file))) { log(COLORS.cyan, ` Copie global ${file}`); } } for (const dir of ['skills', 'memories', 'cron', 'hooks', 'sessions']) { if (copyIfMissing(join(nativeHermes, dir), join(overmindHermes, dir))) { log(COLORS.cyan, ` Copie global ${dir}/`); } } const backup = `${nativeHermes}.backup.${new Date().toISOString().replace(/[-:T.Z]/g, '').slice(0, 14)}`; renameSync(nativeHermes, backup); symlinkSync(overmindHermes, nativeHermes, 'dir'); log(COLORS.green, `✅ Symlink créé: ~/.hermes → ${overmindHermes}`); log(COLORS.green, `✅ Backup conservé: ${backup}`); log(COLORS.green, '✅ Aucune donnée effacée'); return true; } catch (error) { log(COLORS.yellow, `⚠️ Alignement ~/.hermes échoué: ${error.message}`); log(COLORS.cyan, ' Fix manuel: mv ~/.hermes ~/.hermes.backup.$(date +%Y%m%d%H%M%S) && ln -s ~/.overmind/hermes ~/.hermes'); return false; } } function createEnvConfig() { logSection('CRÉATION CONFIGURATION'); mkdirSync(INSTALL_DIR, { recursive: true }); const envFile = join(INSTALL_DIR, '.env'); const envExampleFile = join(INSTALL_DIR, '.env.example'); const mcpFile = join(INSTALL_DIR, '.mcp.json'); const mcpExampleFile = join(INSTALL_DIR, '.mcp.json.example'); const postgresEnvFile = join(INSTALL_DIR, '.env.postgres'); // Copier .env.example → .env si existe if (existsSync(envExampleFile) && !existsSync(envFile)) { try { const content = readFileSync(envExampleFile, 'utf8'); writeFileSync(envFile, content); log(COLORS.green, '✅ .env créé (à partir de .env.example)'); } catch (e) { log(COLORS.yellow, '⚠️ Impossible de copier .env.example: ' + e.message); } } // Créer .env minimal si n'existe pas if (!existsSync(envFile)) { // Use global password (shared with Docker) const randomPassword = PG_PASSWORD; const envContent = `# OverMind-MCP Environment Configuration (v3.1) # Généré automatiquement par npm install # PostgreSQL + pgvector POSTGRES_HOST=localhost POSTGRES_PORT=5432 POSTGRES_USER=postgres POSTGRES_PASSWORD=${randomPassword} POSTGRES_DATABASE=overmind_memory # OverMind OVERMIND_WORKSPACE=${INSTALL_DIR} OVERMIND_MEMORY_TYPE=postgres OVERMIND_LOG_LEVEL=info # Embeddings (Qwen 3 Embedding 8B - 4096D) OVERMIND_EMBEDDING_MODEL=qwen/qwen3-embedding-8b OVERMIND_EMBEDDING_DIMENSIONS=4096 # OVERMIND_EMBEDDING_URL=https://openrouter.ai/api/v1 # OVERMIND_EMBEDDING_KEY=sk-or-v1-... # OpenTelemetry (optionnel) OTEL_ENABLED=false `; writeFileSync(envFile, envContent); log(COLORS.green, '✅ .env créé: ' + envFile); log(COLORS.yellow, '⚠️ Password généré automatiquement. Sauvegardez-le!'); log(COLORS.cyan, ' Éditez .env pour ajouter vos clés API LLM.'); } // Créer .env.postgres pour overmind-postgres-mcp if (!existsSync(postgresEnvFile)) { const postgresEnvContent = `# OverMind-PostgreSQL-MCP Configuration # Généré automatiquement par OverMind-MCP # Activer la base de données USE_DATABASE=true # PostgreSQL Configuration (compatible OverMind) POSTGRES_HOST=localhost POSTGRES_PORT=5432 POSTGRES_DATABASE=overmind_memory POSTGRES_USER=postgres POSTGRES_PASSWORD=${PG_PASSWORD} # Additional PostgreSQL Settings POSTGRES_SSL=false POSTGRES_MAX_CONNECTIONS=10 POSTGRES_IDLE_TIMEOUT=30000 # Environment NODE_ENV=production # OpenRouter Configuration (Qwen3 Embedding 8B - 4096D) OPENROUTER_API_KEY=sk-or-v1-your_key_here OPENROUTER_MODEL=qwen/qwen3-embedding-8b EMBEDDING_PROVIDER=openrouter EMBEDDING_DIMENSIONS=4096 EMBEDDING_CACHE_SIZE=1000 `; writeFileSync(postgresEnvFile, postgresEnvContent); log(COLORS.green, '✅ Configuration .env.postgres créée: ' + postgresEnvFile); } // Copier .mcp.json.example → .mcp.json si existe if (existsSync(mcpExampleFile) && !existsSync(mcpFile)) { try { const content = readFileSync(mcpExampleFile, 'utf8'); writeFileSync(mcpFile, content); log(COLORS.green, '✅ .mcp.json créé (à partir de .mcp.json.example)'); } catch (e) { log(COLORS.yellow, '⚠️ Impossible de copier .mcp.json.example: ' + e.message); } } } async function startPostgreSQL() { logSection('VÉRIFICATION POSTGRESQL + PGVECTOR'); try { // Vérifier si PostgreSQL existe déjà (n'importe quel container postgres) const anyPostgres = runCommand( 'docker ps --filter "name=postgres" --filter "publish=5432" --format "{{.Names}}"', { stdio: 'pipe' } ); if (anyPostgres) { log(COLORS.green, '✅ PostgreSQL déjà actif sur le port 5432'); log(COLORS.cyan, ' Container: ' + anyPostgres.trim()); log(COLORS.yellow, ' 💡 Utilisation de PostgreSQL existant (pas de création OverMind)'); return true; } // Vérifier si le container OverMind existe déjà const overmindContainer = runCommand( 'docker ps -a --filter "name=overmind-postgres-pgvector" --format "{{.Names}}"', { stdio: 'pipe' } ); if (overmindContainer) { log(COLORS.yellow, '⚠️ Container OverMind existe mais non démarré'); await runCommandAsync( 'docker start overmind-postgres-pgvector', 'Démarrage container OverMind existant' ); return true; } log(COLORS.yellow, '🚀 Création et démarrage PostgreSQL + pgvector...'); await runCommandAsync( `docker run -d --name overmind-postgres-pgvector -p 5432:5432 -e POSTGRES_PASSWORD=${PG_PASSWORD} -e POSTGRES_DB=overmind_memory -e POSTGRES_USER=postgres -v overmind_postgres_data:/var/lib/postgresql/data --restart unless-stopped pgvector/pgvector:pg16`, 'Création PostgreSQL OverMind' ); log(COLORS.cyan, '\n⏳ Attente démarrage PostgreSQL (20s)...'); await new Promise(resolve => setTimeout(resolve, 20000)); return true; } catch (error) { log(COLORS.red, '\n⚠️ Erreur démarrage PostgreSQL: ' + error.message); return false; } } async function validatePostgreSQL() { logSection('VALIDATION POSTGRESQL'); log(COLORS.yellow, '🔍 Vérification PostgreSQL + pgvector...\n'); const containerName = runCommand( 'docker ps --filter "name=postgres" --format "{{.Names}}"', { stdio: 'pipe' } ); if (containerName) { log(COLORS.green, ` ✅ PostgreSQL + pgvector: ${containerName.trim()}`); return true; } else { log(COLORS.red, ' ❌ PostgreSQL + pgvector: Non trouvé'); return false; } } function showSummary() { console.log('\n╔══════════════════════════════════════════════════════════════════╗'); console.log('║' + ' '.repeat(64) + '║'); console.log('║' + COLORS.green + ' ✅ INSTALLATION TERMINÉE !' + COLORS.reset + ' '.repeat(33) + '║'); console.log('║' + ' '.repeat(64) + '║'); console.log('╚══════════════════════════════════════════════════════════════════╝'); console.log(''); log(COLORS.yellow, "📋 COMPOSANTS INSTALLÉS:"); console.log(''); console.log('┌─────────────────────────────────────────────────────────────────┐'); console.log('│ ' + COLORS.cyan + 'Ouvrez votre interface Docker (Containers)' + COLORS.reset + ' │'); console.log('│ ' + COLORS.cyan + 'Vous verrez le service OverMind actif:' + COLORS.reset + ' │'); console.log('│ ' + COLORS.green + ' • PostgreSQL + pgvector (Mémoire Vectorielle)' + COLORS.reset + ' │'); console.log('│ │'); console.log('│ ' + COLORS.green + ' • overmind-postgres-mcp (Serveur MCP PostgreSQL)' + COLORS.reset + ' │'); console.log('│ │'); console.log('│ ' + COLORS.yellow + 'Détails de connexion:' + COLORS.reset + ' │'); console.log('│ • Host: localhost:5432' + ' │'); console.log('│ • User: postgres' + ' │'); console.log('│ • Password: ' + PG_PASSWORD.substring(0, 8) + '... (stocké dans ~/.overmind/.env)'); console.log('│ • Extension: vector (pgvector)' + ' │'); console.log('│ • Database: overmind_memory' + ' │'); console.log('└─────────────────────────────────────────────────────────────────┘'); console.log(''); log(COLORS.yellow, "📁 FICHIERS DE CONFIGURATION:"); console.log(" • ~/.overmind/.env (Configuration OverMind)"); console.log(" • ~/.overmind/.env.postgres (Configuration PostgreSQL MCP)"); console.log(" • ~/.overmind/.mcp.json (Configuration serveurs MCP)"); console.log(''); log(COLORS.yellow, "🔧 SERVEURS MCP ACTIFS:"); console.log(" • overmind (Orchestration + mémoire vectorielle — :3099)"); console.log(" • postgres (PostgreSQL vectoriel — :5433)"); console.log(''); log(COLORS.yellow, "📚 DOCUMENTATION:"); console.log(" • https://github.com/DeamonDev888/overmind-mcp"); console.log(" • https://www.npmjs.com/package/overmind-mcp"); console.log(" • https://github.com/DeamonDev888/PostgreSQL-MCP-Serveur"); console.log(''); log(COLORS.yellow, "🎉 PROCHAINE ÉTAPE:"); console.log(" • Créez votre premier agent: overmind create-agent"); console.log(" • Ou listez les agents: overmind list-agents"); console.log(" • Gestion PostgreSQL: overmind-postgres-mcp up/status/down"); console.log(''); } // ═════════════════════════════════════════════════════════════════════════════ // MAIN // ═════════════════════════════════════════════════════════════════════════════ async function main() { console.log('╔══════════════════════════════════════════════════════════════════╗'); console.log('║' + ' '.repeat(64) + '║'); console.log('║' + COLORS.cyan + ' 🚀 OVERMIND-MCP - INSTALLATION AUTOMATIQUE' + COLORS.reset + ' '.repeat(25) + '║'); console.log('║' + ' '.repeat(64) + '║'); console.log('╚══════════════════════════════════════════════════════════════════╝'); console.log(''); // Banner console.log(COLORS.cyan + 'Ce script VA installer automatiquement:' + COLORS.reset); console.log(' ✓ Vérifier Docker'); console.log(' ✓ Installer PostgreSQL + pgvector (si absent)'); console.log(' ✓ Télécharger fichiers de configuration'); console.log(' ✓ Installer overmind-postgres-mcp'); console.log(' ✓ Démarrer PostgreSQL + pgvector'); console.log(' ✓ Copier .env.example → .env'); console.log(' ✓ Copier .mcp.json.example → .mcp.json'); console.log(' ✓ Aligner Hermes: ~/.hermes → ~/.overmind/hermes (Linux/macOS)'); console.log(' ✓ Valider PostgreSQL'); console.log(''); // Step 0: Offre install native (non bloquant) const nativeOffer = shouldOfferNative(); if (nativeOffer.native) { await offerNativeInstall(nativeOffer.reason); if (process.argv.includes('--native-only')) { log(COLORS.green, '→ --native-only : on s\'arrête là. Lance sudo overmind-install-native quand prêt.'); return; } } // Step 1: Check Docker const dockerOk = await checkDocker(); if (!dockerOk) { return; } // Step 2: Setup .env and .mcp.json (BEFORE Docker so password is shared) createEnvConfig(); // Step 2b: Align Hermes roots (Linux/macOS npm -g path hygiene) alignHermesHome(); // Step 3: Setup PostgreSQL (uses PG_PASSWORD) await setupPostgreSQL(); // Step 4: Download config files const downloaded = await setupConfigFiles(); // Step 5: Install overmind-postgres-mcp if (downloaded) { await installPostgresMCP(); } // Step 6: Start PostgreSQL if (downloaded) { const started = await startPostgreSQL(); if (!started) { log(COLORS.yellow, '\n⚠️ PostgreSQL non démarré automatiquement.'); } } // Step 7: Validate PostgreSQL if (downloaded) { const ok = await validatePostgreSQL(); if (ok) { logSection('✅ POSTGRESQL + PGVECTOR EST ACTIF'); log(COLORS.green, '🎉 Installation réussie !'); } else { logSection('⚠️ POSTGRESQL NON DÉMARRÉ'); } } // Show final summary showSummary(); // Auto-verify to show user what's ready/missing logSection('🔍 VÉRIFICATION POST-INSTALL'); try { const verifyScript = join(__dirname, 'verify-install.mjs'); if (existsSync(verifyScript)) { await runCommandAsync(`node "${verifyScript}"`, 'Smoke test'); } } catch { // Non-blocking } } // Run main main().catch((error) => { console.error('\n❌ ERREUR FATALE:', error.message); process.exit(1); });