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
JavaScript
/**
* ═══════════════════════════════════════════════════════════════════════════════
* 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);
});
}