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

182 lines (154 loc) • 7.58 kB
#!/usr/bin/env node /** * ═══════════════════════════════════════════════════════════════════════════════ * OVERMIND — Ngrok Webhook Tunnel * ═══════════════════════════════════════════════════════════════════════════════ * * Démarre un tunnel ngrok vers le bridge Overmind pour recevoir les webhooks * entrants (Telegram, VoIP.ms, Twilio, Discord, generic). * * Usage: * node scripts/ngrok-webhook.mjs # default: bridge :3100 * node scripts/ngrok-webhook.mjs --port 3149 # custom port (bt-sms) * node scripts/ngrok-webhook.mjs --port 3001 --region eu # sniperbot * node scripts/ngrok-webhook.mjs --provider telegram # affiche l'URL Telegram * * Prérequis: * npm install -g ngrok (ou snap install ngrok) * ngrok authtoken <TOKEN> (une seule fois) * * Le script: * 1. Démarre ngrok sur le port spécifié * 2. Récupère l'URL publique * 3. Affiche les URLs de webhook pour chaque provider * 4. Sur Ctrl+C: ferme le tunnel proprement * ═══════════════════════════════════════════════════════════════════════════════ */ import { spawn } from 'child_process'; import { createInterface } from 'readline'; // ─── Parse args ───────────────────────────────────────────────────────────── const args = process.argv.slice(2); function getFlag(name, defaultValue) { const idx = args.indexOf(`--${name}`); if (idx !== -1 && args[idx + 1]) return args[idx + 1]; return defaultValue; } const PORT = parseInt(getFlag('port', '3100'), 10); const REGION = getFlag('region', ''); const PROVIDER = getFlag('provider', ''); const SUBDOMAIN = getFlag('subdomain', ''); // ─── Provider webhook paths ────────────────────────────────────────────────── const PROVIDER_PATHS = { telegram: '/webhook/telegram', voipms: '/webhook/voipms', twilio: '/webhook/twilio', discord: '/webhook/discord', generic: '/webhook/generic', }; // ─── Colors ────────────────────────────────────────────────────────────────── const C = { cyan: '\x1b[36m', green: '\x1b[32m', yellow: '\x1b[33m', red: '\x1b[31m', dim: '\x1b[2m', bold: '\x1b[1m', reset: '\x1b[0m', }; function banner(text) { console.log(`${C.bold}${C.cyan}══════════════════════════════════════════════════════${C.reset}`); console.log(`${C.bold}${C.cyan} ${text}${C.reset}`); console.log(`${C.bold}${C.cyan}══════════════════════════════════════════════════════${C.reset}\n`); } // ─── Main ──────────────────────────────────────────────────────────────────── banner('🚀 Overmind Ngrok Webhook Tunnel'); // Build ngrok args const ngrokArgs = ['http', PORT.toString()]; if (REGION) ngrokArgs.push('--region', REGION); if (SUBDOMAIN) ngrokArgs.push('--domain', SUBDOMAIN); console.log(`${C.dim}Starting ngrok:${C.reset} ngrok ${ngrokArgs.join(' ')}\n`); const ngrok = spawn('ngrok', ngrokArgs, { stdio: ['ignore', 'pipe', 'pipe'] }); let publicUrl = ''; let gotUrl = false; const stdout = createInterface({ input: ngrok.stdout }); const stderr = createInterface({ input: ngrok.stderr }); stdout.on('line', (line) => { // ngrok prints forwarding info on stderr in newer versions, but check both const urlMatch = line.match(/url=(https:\/\/[^\s]+)/i) || line.match(/(https:\/\/[a-z0-9-]+\.ngrok[a-z.-]*)/i); if (urlMatch && !gotUrl) { publicUrl = urlMatch[1]; gotUrl = true; printUrls(); } if (line.trim()) console.log(`${C.dim}[ngrok] ${line}${C.reset}`); }); stderr.on('line', (line) => { const urlMatch = line.match(/url=(https:\/\/[^\s]+)/i) || line.match(/(https:\/\/[a-z0-9-]+\.ngrok[a-z.-]*)/i); if (urlMatch && !gotUrl) { publicUrl = urlMatch[1]; gotUrl = true; printUrls(); } if (line.trim()) console.error(`${C.red}[ngrok] ${line}${C.reset}`); }); function printUrls() { console.log(`\n${C.green}${C.bold}✅ Tunnel actif!${C.reset}\n`); console.log(`${C.bold}Public URL:${C.reset} ${C.yellow}${publicUrl}${C.reset}\n`); if (PROVIDER) { // Single provider mode const path = PROVIDER_PATHS[PROVIDER] || `/webhook/${PROVIDER}`; console.log(`${C.bold}Webhook URL (${PROVIDER}):${C.reset}`); console.log(` ${C.green}${publicUrl}${path}${C.reset}\n`); printProviderConfig(PROVIDER, `${publicUrl}${path}`); } else { // All providers console.log(`${C.bold}Webhook URLs:${C.reset}\n`); for (const [name, path] of Object.entries(PROVIDER_PATHS)) { console.log(` ${name.padEnd(12)} ${C.green}${publicUrl}${path}${C.reset}`); } console.log(''); for (const name of Object.keys(PROVIDER_PATHS)) { printProviderConfig(name, `${publicUrl}${PROVIDER_PATHS[name]}`); } } console.log(`${C.dim}Ctrl+C pour fermer le tunnel.${C.reset}\n`); } function printProviderConfig(provider, url) { switch (provider) { case 'telegram': console.log(`${C.dim}Telegram: curl -s "https://api.telegram.org/bot<TOKEN>/setWebhook?url=${url}"${C.reset}`); break; case 'voipms': console.log(`${C.dim}VoIP.ms: Configure SMS callback URL → ${url}${C.reset}`); break; case 'twilio': console.log(`${C.dim}Twilio: Phone Numbers → Webhook → ${url}${C.reset}`); break; case 'discord': console.log(`${C.dim}Discord: Application → Interactions Endpoint → ${url}${C.reset}`); break; case 'generic': console.log(`${C.dim}Generic: POST any JSON to ${url}${C.reset}`); break; } } // ─── Cleanup ───────────────────────────────────────────────────────────────── process.on('SIGINT', () => { console.log(`\n${C.yellow}Closing tunnel...${C.reset}`); ngrok.kill('SIGTERM'); process.exit(0); }); process.on('SIGTERM', () => { ngrok.kill('SIGTERM'); process.exit(0); }); ngrok.on('exit', (code) => { console.log(`${C.dim}ngrok exited (code ${code})${C.reset}`); process.exit(code ?? 0); }); // Timeout si ngrok ne démarre pas setTimeout(() => { if (!gotUrl) { console.error(`\n${C.red}❌ ngrok n'a pas répondu après 15s.${C.reset}`); console.error(`${C.dim}Vérifiez: ngrok authtoken <TOKEN>${C.reset}`); console.error(`${C.dim}Ou installez: npm install -g ngrok${C.reset}`); ngrok.kill('SIGTERM'); process.exit(1); } }, 15000);