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
196 lines (171 loc) • 9.27 kB
JavaScript
/**
* ═══════════════════════════════════════════════════════════════════════════════
* OVERMIND-MCP — Post-Install Verification (Smoke Test)
* ═══════════════════════════════════════════════════════════════════════════════
*
* Vérifie que l'installation overmind-mcp est fonctionnelle:
* 1. Binaire overmind accessible dans le PATH
* 2. ~/.overmind/.env existe et contient les vars critiques
* 3. PostgreSQL est accessible (Docker ou natif)
* 4. Port 3099 est libre ou répond
*
* Usage:
* overmind-verify # après installation
* node scripts/verify-install.mjs
* ═══════════════════════════════════════════════════════════════════════════════
*/
import { execSync } from 'child_process';
import { existsSync, readFileSync } from 'fs';
import { join } from 'path';
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
let PKG_VERSION = 'unknown';
try {
const pkg = require('../../package.json');
PKG_VERSION = pkg.version;
} catch {}
const C = {
green: '\x1b[32m',
yellow: '\x1b[33m',
red: '\x1b[31m',
cyan: '\x1b[36m',
bold: '\x1b[1m',
reset: '\x1b[0m',
};
const HOME = process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH || '';
const OVERMIND_DIR = join(HOME, '.overmind');
const OVERMIND_HERMES_DIR = join(OVERMIND_DIR, 'hermes');
const NATIVE_HERMES_DIR = join(HOME, '.hermes');
const ENV_FILE = join(OVERMIND_DIR, '.env');
let passed = 0;
let warnings = 0;
let failed = 0;
function ok(msg) { console.log(`${C.green} ✅${C.reset} ${msg}`); passed++; }
function warn(msg) { console.log(`${C.yellow} ⚠️ ${C.reset} ${msg}`); warnings++; }
function fail(msg) { console.log(`${C.red} ❌${C.reset} ${msg}`); failed++; }
function runCmd(cmd) {
try { return execSync(cmd, { encoding: 'utf8', timeout: 5000, stdio: 'pipe' }).trim(); }
catch { return null; }
}
// ═══════════════════════════════════════════════════════════════════════════════
console.log(`\n${C.bold}${C.cyan}══════════════════════════════════════════════════════${C.reset}`);
console.log(`${C.bold}${C.cyan} 🧪 Overmind Post-Install Verification v${PKG_VERSION}${C.reset}`);
console.log(`${C.bold}${C.cyan}══════════════════════════════════════════════════════${C.reset}\n`);
// 1. Binaire overmind
console.log(`${C.bold}1. Binaire overmind${C.reset}`);
const overmindBin = runCmd('which overmind') || runCmd('where overmind');
if (overmindBin) {
ok(`overmind trouvé: ${overmindBin}`);
} else {
fail('overmind non trouvé dans le PATH — rejeu: npm install -g overmind-mcp');
}
const version = runCmd('overmind --version 2>&1') || runCmd('overmind version 2>&1');
if (version && (version.includes(PKG_VERSION) || version.length > 0)) {
ok(`Version: ${version.split('\n')[0]}`);
} else {
warn('overmind --version ne répond pas (peut nécessiter .env configuré)');
}
// 2. .env
console.log(`\n${C.bold}2. Configuration .env${C.reset}`);
if (!existsSync(ENV_FILE)) {
fail(`${ENV_FILE} non trouvé — rejeu: overmind-setup`);
} else {
ok('.env existe');
const envContent = readFileSync(ENV_FILE, 'utf8');
const requiredVars = [
'POSTGRES_HOST',
'POSTGRES_DATABASE',
'POSTGRES_PASSWORD',
'POSTGRES_USER',
];
for (const varName of requiredVars) {
const re = new RegExp(`^${varName}=(.+)$`, 'm');
const match = envContent.match(re);
if (match && match[1].trim() && !match[1].includes('change_me')) {
ok(`${varName} = ${match[1].substring(0, 8)}...`);
} else if (match && match[1].includes('change_me')) {
warn(`${varName} contient encore le mot de passe par défaut — changez-le!`);
} else {
fail(`${varName} manquant dans .env`);
}
}
// Check for old POSTGRES_DB (v2.x compat)
if (envContent.match(/^POSTGRES_DB=/m) && !envContent.match(/^POSTGRES_DATABASE=/m)) {
warn('POSTGRES_DB détecté (v2.x) — renommez en POSTGRES_DATABASE (v3.x)');
}
}
// 3. PostgreSQL
console.log(`\n${C.bold}3. PostgreSQL${C.reset}`);
const pgDocker = runCmd('docker ps --filter "name=postgres" --format "{{.Names}}" 2>/dev/null');
const pgNative = runCmd('pg_isready -h localhost -p 5432 2>/dev/null');
if (pgDocker && pgDocker.length > 0) {
ok(`PostgreSQL Docker actif: ${pgDocker.split('\n')[0]}`);
} else if (pgNative && pgNative.includes('accepting')) {
ok('PostgreSQL natif actif sur :5432');
} else {
warn('PostgreSQL non détecté — démarrez-le: overmind-postgres-mcp up (Docker) ou sudo apt install postgresql (natif)');
}
// 4. Port 3099
console.log(`\n${C.bold}4. Port Overmind MCP${C.reset}`);
const portCheck = runCmd('lsof -iTCP:3099 -sTCP:LISTEN 2>/dev/null') || runCmd('ss -tlnp 2>/dev/null | grep 3099');
if (portCheck && portCheck.length > 0) {
ok('Port 3099 en écoute — Overmind MCP est démarré');
} else {
warn('Port 3099 libre — démarrez: overmind --transport httpStream --port 3099');
}
// 5. MCP health check
if (portCheck) {
console.log(`\n${C.bold}5. MCP Health Check${C.reset}`);
const health = runCmd('curl -s -m 5 http://localhost:3099/mcp -X POST -H "Content-Type: application/json" -d \'{"jsonrpc":"2.0","id":1,"method":"ping","params":{}}\' 2>/dev/null');
if (health && (health.includes('jsonrpc') || health.includes('pong'))) {
ok('MCP :3099 répond au ping');
} else {
warn('MCP :3099 ne répond pas au ping (peut nécessiter auth token)');
}
}
// 6. Hermes root alignment
console.log(`\n${C.bold}6. Hermes root alignment${C.reset}`);
if (process.platform === 'win32') {
ok('Windows Desktop: racine attendue = %LOCALAPPDATA%\\hermes (pas de symlink ~/.hermes)');
} else if (!existsSync(OVERMIND_HERMES_DIR)) {
warn('~/.overmind/hermes absent — sera créé par overmind-install-native ou postinstall');
} else if (!existsSync(NATIVE_HERMES_DIR)) {
warn('~/.hermes absent — postinstall devrait créer le symlink vers ~/.overmind/hermes');
} else {
const linkTarget = runCmd(`readlink "${NATIVE_HERMES_DIR}" 2>/dev/null`);
if (linkTarget === OVERMIND_HERMES_DIR) {
ok('~/.hermes → ~/.overmind/hermes (symlink complet OK)');
} else {
const nativeProfilesTarget = runCmd(`readlink "${NATIVE_HERMES_DIR}/profiles" 2>/dev/null`);
const nativeConfigTarget = runCmd(`readlink "${NATIVE_HERMES_DIR}/config.yaml" 2>/dev/null`);
if (nativeProfilesTarget && nativeConfigTarget) {
warn('Mode hybride détecté: ~/.hermes est un vrai dossier mais profiles/config.yaml sont symlinkés. Rejeu recommandé: npm i -g overmind-mcp@latest ou overmind-install-native');
} else {
warn('Dual-path détecté: ~/.hermes n’est pas symlinké vers ~/.overmind/hermes. Risque de config divergente.');
}
}
}
// 7. Hermes (optionnel)
console.log(`\n${C.bold}7. Hermes CLI (optionnel)${C.reset}`);
const hermes = runCmd('which hermes 2>/dev/null');
if (hermes) {
const hermesVersion = runCmd('hermes version 2>/dev/null');
ok(`Hermes trouvé: ${hermes} (${hermesVersion || 'version inconnue'})`);
} else {
warn('Hermes non installé — requis pour les agents Hermes: pip install hermes-agent');
}
// ═══════════════════════════════════════════════════════════════════════════════
console.log(`\n${C.bold}${C.cyan}══════════════════════════════════════════════════════${C.reset}`);
console.log(`${C.bold} Résultat: ${C.green}${passed} OK${C.reset}${failed > 0 ? `, ${C.red}${failed} échec${C.reset}` : ''}${warnings > 0 ? `, ${C.yellow}${warnings} avertissement${warnings > 1 ? 's' : ''}${C.reset}` : ''}`);
console.log(`${C.bold}${C.cyan}══════════════════════════════════════════════════════${C.reset}\n`);
if (failed > 0) {
console.log(`${C.red}${C.bold}❌ Échec — corrigez les erreurs ci-dessus avant de continuer.${C.reset}\n`);
process.exit(1);
} else if (warnings > 0) {
console.log(`${C.yellow}⚠️ Installation fonctionnelle avec avertissements.${C.reset}\n`);
process.exit(0);
} else {
console.log(`${C.green}${C.bold}✅ Tout est prêt! Overmind MCP est opérationnel.${C.reset}\n`);
process.exit(0);
}