#!/usr/bin/env node
/**
 * ╔══════════════════════════════════════════════════════════════════════╗
 * ║   OVERMIND BRIDGE — CLI (overmind-bridge)                            ║
 * ║                                                                      ║
 * ║   Point d'entrée unifié pour le bridge Overmind.                     ║
 * ║   Supporte 8 subcommands : server, call, scenario, replay, status,   ║
 * ║   health, send, agent.                                               ║
 * ║                                                                      ║
 * ║   USAGE EXAMPLES                                                      ║
 * ║   ──────────────                                                      ║
 * ║   # Start server                                                      ║
 * ║   overmind-bridge server --port 3100                                 ║
 * ║                                                                      ║
 * ║   # One-shot agent call (8 prompt sources)                            ║
 * ║   overmind-bridge call agent.run --agent scout --runner kilo \        ║
 * ║       --prompt "Analyse BTC"                                         ║
 * ║   echo "Analyse BTC" | overmind-bridge call agent.run \              ║
 * ║       --agent scout --runner kilo --prompt-stdin                    ║
 * ║   overmind-bridge call agent.run --agent scout --runner kilo \        ║
 * ║       --prompt-file ./brief.txt --var ticker=BTC                     ║
 * ║                                                                      ║
 * ║   # A2A — agent A parle à agent B                                    ║
 * ║   overmind-bridge call agent.a2a \                                    ║
 * ║       --from scout --to analyst --runner kilo \                      ║
 * ║       --prompt "Valide mon analyse"                                  ║
 * ║                                                                      ║
 * ║   # Multi-agent scenario from JSON                                   ║
 * ║   overmind-bridge scenario ./workflow.json --var ticker=BTC          ║
 * ║                                                                      ║
 * ║   # Replay a failed message                                          ║
 * ║   overmind-bridge replay --id 7f3e8a1b-...                            ║
 * ║                                                                      ║
 * ║   # Status of all agents                                              ║
 * ║   overmind-bridge status                                             ║
 * ║                                                                      ║
 * ║   # Server health                                                     ║
 * ║   overmind-bridge health                                              ║
 * ╚══════════════════════════════════════════════════════════════════════╝
 */
import { parseArgs, requireFlag, getFlag } from '../bridge/ArgParser.js';
import { resolvePrompt, parseVars } from '../bridge/PromptSource.js';
import { loadScenario, runScenario } from '../bridge/ScenarioLoader.js';
import { BridgeHttpClient } from '../bridge/BridgeHttpClient.js';
import { OverBridgeService, OverBridgeServer, loadMessageLogConfigFromEnv, createBridgeLogger, } from '../bridge/index.js';
const log = createBridgeLogger('overmind-bridge');
const clog = (msg) => log.info(msg);
// ─── Entry Point ───────────────────────────────────────────────────────────
async function main() {
    const args = parseArgs();
    const command = args.command ?? 'help';
    try {
        switch (command) {
            case 'server':
                await cmdServer(args);
                break;
            case 'call':
                await cmdCall(args);
                break;
            case 'scenario':
                await cmdScenario(args);
                break;
            case 'replay':
                await cmdReplay(args);
                break;
            case 'status':
                await cmdStatus(args);
                break;
            case 'health':
                await cmdHealth(args);
                break;
            case 'agents':
            case 'agent':
                await cmdAgents(args);
                break;
            case 'history':
                await cmdHistory(args);
                break;
            case 'sessions':
            case 'session':
                await cmdSessions(args);
                break;
            case 'webhook':
                await cmdWebhook(args);
                break;
            case 'send':
                await cmdSend(args);
                break;
            case 'version':
            case '--version':
            case '-v':
                console.log('overmind-bridge 1.0.0');
                break;
            case 'help':
            case '--help':
            case '-h':
            case undefined:
                printHelp();
                break;
            default:
                console.error(`❌ Unknown command: ${command}`);
                console.error(`Run 'overmind-bridge help' for usage.`);
                process.exit(1);
        }
    }
    catch (err) {
        console.error(`💥 ${command} failed: ${err.message}`);
        if (process.env.DEBUG) {
            console.error(err.stack);
        }
        process.exit(1);
    }
}
// ─── Subcommand: server ────────────────────────────────────────────────────
async function cmdServer(args) {
    const port = Number(getFlag(args, 'port', 3100));
    const host = String(getFlag(args, 'host', '127.0.0.1'));
    const authToken = getFlag(args, 'auth-token') ?? process.env.BRIDGE_AUTH_TOKEN;
    const enableLog = getFlag(args, 'no-log', undefined) === undefined; // --no-log désactive
    const healthInterval = Number(getFlag(args, 'health-interval', 30_000));
    const mcpUrl = String(getFlag(args, 'mcp-url', process.env.MCP_URL ?? 'http://localhost:3099/mcp'));
    const enableSessionStore = getFlag(args, 'no-session-store', undefined) === undefined;
    const enableDirectives = getFlag(args, 'no-directives', undefined) === undefined;
    const enableWebhooks = getFlag(args, 'webhooks', false);
    const enableSanitize = getFlag(args, 'sanitize-json', true);
    const sessionTtlMs = Number(getFlag(args, 'session-ttl', 4 * 60 * 60 * 1000));
    clog(`🚀 Starting OverBridgeServer on ${host}:${port}`);
    clog(`   MCP upstream: ${mcpUrl}`);
    clog(`   MessageLog: ${enableLog ? 'enabled' : 'disabled'}`);
    clog(`   SessionStore: ${enableSessionStore ? 'enabled' : 'disabled'}`);
    clog(`   Directives: ${enableDirectives ? 'enabled' : 'disabled'}`);
    clog(`   Webhooks: ${enableWebhooks ? 'enabled' : 'disabled'}`);
    clog(`   Sanitize JSON: ${enableSanitize ? 'enabled' : 'disabled'}`);
    if (authToken)
        clog(`   Auth: enabled`);
    const service = new OverBridgeService({ mcpUrl });
    const server = new OverBridgeServer(service, {
        port,
        host,
        postgres: loadMessageLogConfigFromEnv(),
        enableMessageLog: enableLog,
        authToken,
        healthCheckIntervalMs: healthInterval,
        enableSessionStore,
        enableDirectives,
        enableWebhooks,
        sanitizeJson: enableSanitize,
        sessionTtlMs,
    }, log);
    const { url } = await server.start();
    console.log(`\n  OverBridgeServer ready at ${url}`);
    console.log(`  POST ${url}/rpc   (JSON-RPC 2.0)`);
    console.log(`  GET  ${url}/health`);
    if (enableWebhooks) {
        console.log(`  POST ${url}/webhook/:provider   (voipms, twilio, discord, generic)`);
    }
    console.log('');
    // Graceful shutdown
    const shutdown = async (signal) => {
        clog(`\n🛑 Received ${signal}, shutting down...`);
        await server.stop();
        process.exit(0);
    };
    process.on('SIGINT', () => void shutdown('SIGINT'));
    process.on('SIGTERM', () => void shutdown('SIGTERM'));
    // Keep alive
    await new Promise(() => { });
}
// ─── Subcommand: call (one-shot JSON-RPC) ──────────────────────────────────
async function cmdCall(args) {
    // 2 formats acceptés:
    //   call <method> [--param1 v1 --param2 v2 ...]
    //   call --method <method> [--param1 v1 ...]
    const method = (args.positionals[0] ?? getFlag(args, 'method'));
    if (!method) {
        throw new Error('call requires a method (e.g. "call agent.run --agent scout ..." or "call --method agent.run ...")');
    }
    const client = getClient(args);
    // Prompt resolution (8 sources)
    const prompt = await resolvePrompt({
        prompt: getFlag(args, 'prompt'),
        promptFile: getFlag(args, 'prompt-file'),
        promptStdin: getFlag(args, 'prompt-stdin', false),
        promptBase64: getFlag(args, 'prompt-base64'),
        promptFileBase64: getFlag(args, 'prompt-file-base64'),
        promptUrl: getFlag(args, 'prompt-url'),
        promptJson: getFlag(args, 'prompt-json'),
        promptTemplate: getFlag(args, 'prompt-template'),
        vars: parseVars(getFlag(args, 'var')),
    });
    // Construit les params dynamiquement depuis tous les flags non-préfixés
    const params = {};
    const reservedFlags = new Set([
        'method', 'server', 'port', 'host', 'auth-token', 'no-log', 'health-interval', 'mcp-url',
        'prompt', 'prompt-file', 'prompt-stdin', 'prompt-base64', 'prompt-file-base64',
        'prompt-url', 'prompt-json', 'prompt-template', 'var',
        'id', 'output', 'json', 'pretty',
    ]);
    for (const [k, v] of Object.entries(args.flags)) {
        if (!reservedFlags.has(k)) {
            params[k] = v;
        }
    }
    // Si prompt résolu, l'injecte (sauf si déjà fourni via params)
    if (prompt.text && !params.prompt) {
        params.prompt = prompt.text;
    }
    // Métadonnées (vars, prompt source)
    if (Object.keys(params).length > 0 || prompt.text) {
        const metadata = {
            promptSource: prompt.source,
            promptBytes: prompt.meta.bytes,
        };
        if (prompt.meta.origin)
            metadata.promptOrigin = prompt.meta.origin;
        params.metadata = { ...params.metadata, ...metadata };
    }
    clog(`📞 ${method} → ${client.baseUrl}`);
    const result = await client.call(method, params, Number(getFlag(args, 'timeout', 600_000)));
    printResult(result, getFlag(args, 'pretty', false));
}
// ─── Subcommand: send (alias pour call agent.run) ──────────────────────────
async function cmdSend(args) {
    // send est un raccourci : call agent.run avec les bons flags
    const agentName = requireFlag(args, 'agent');
    const runner = requireFlag(args, 'runner');
    const prompt = await resolvePrompt({
        prompt: getFlag(args, 'prompt'),
        promptFile: getFlag(args, 'prompt-file'),
        promptStdin: getFlag(args, 'prompt-stdin', true),
        promptBase64: getFlag(args, 'prompt-base64'),
        promptFileBase64: getFlag(args, 'prompt-file-base64'),
        promptUrl: getFlag(args, 'prompt-url'),
        promptJson: getFlag(args, 'prompt-json'),
        promptTemplate: getFlag(args, 'prompt-template'),
        vars: parseVars(getFlag(args, 'var')),
    });
    const client = getClient(args);
    const result = await client.call('agent.run', {
        agentName,
        runner,
        prompt: prompt.text,
        sessionId: getFlag(args, 'session'),
        model: getFlag(args, 'model'),
        mode: getFlag(args, 'mode'),
        path: getFlag(args, 'path'),
        silent: getFlag(args, 'silent', false),
    }, Number(getFlag(args, 'timeout', 600_000)));
    printResult(result, getFlag(args, 'pretty', false));
}
// ─── Subcommand: scenario ──────────────────────────────────────────────────
async function cmdScenario(args) {
    const file = getFlag(args, 'file') ?? args.positionals[0];
    if (!file)
        throw new Error('scenario requires --file <path.json> or positional <path.json>');
    const client = getClient(args);
    const scenario = await loadScenario(file);
    const inputVars = parseVars(getFlag(args, 'var'));
    clog(`📜 Scenario: ${scenario.name} (${scenario.steps.length} steps)`);
    const results = await runScenario(scenario, {
        vars: inputVars,
        log: (msg) => clog(msg),
        runAgent: async (params) => {
            const r = await client.call('agent.run', params);
            return {
                text: r.content.map((c) => c.text).join('\n'),
                sessionId: r.sessionId,
                isError: r.isError,
                messageId: r.messageId,
            };
        },
        runA2A: async (params) => {
            const r = await client.call('agent.a2a', params);
            return {
                text: r.content.map((c) => c.text).join('\n'),
                sessionId: r.sessionId,
                isError: r.isError,
                messageId: r.messageId,
            };
        },
    });
    printResult({ scenario: scenario.name, results }, getFlag(args, 'pretty', false));
    const allOk = results.every((r) => r.success);
    if (!allOk)
        process.exit(2);
}
// ─── Subcommand: replay ────────────────────────────────────────────────────
async function cmdReplay(args) {
    const id = requireFlag(args, 'id');
    const client = getClient(args);
    clog(`🔁 Replaying message ${id}...`);
    const result = await client.call('message.replay', { id }, Number(getFlag(args, 'timeout', 600_000)));
    printResult(result, getFlag(args, 'pretty', false));
}
// ─── Subcommand: status (alias agent.list) ─────────────────────────────────
async function cmdStatus(args) {
    const client = getClient(args);
    const result = await client.call('agent.list', {
        status: getFlag(args, 'status'),
        runner: getFlag(args, 'runner'),
    });
    printResult(result, getFlag(args, 'pretty', false));
}
// ─── Subcommand: agents (alias) ────────────────────────────────────────────
async function cmdAgents(args) {
    return cmdStatus(args);
}
// ─── Subcommand: health ────────────────────────────────────────────────────
async function cmdHealth(args) {
    const client = getClient(args);
    try {
        const result = await client.health();
        printResult(result, getFlag(args, 'pretty', false));
    }
    catch (err) {
        console.error(`❌ Server unreachable: ${err.message}`);
        process.exit(1);
    }
}
// ─── Subcommand: history (alias message.history) ───────────────────────────
async function cmdHistory(args) {
    const client = getClient(args);
    const result = await client.call('message.history', {
        toAgent: getFlag(args, 'to'),
        fromAgent: getFlag(args, 'from'),
        status: getFlag(args, 'status'),
        limit: Number(getFlag(args, 'limit', 50)),
        offset: Number(getFlag(args, 'offset', 0)),
        sinceHours: getFlag(args, 'since-hours', undefined),
    });
    printResult(result, getFlag(args, 'pretty', false));
}
// ─── Subcommand: sessions (multi-tenant) ─────────────────────────────────
async function cmdSessions(args) {
    const client = getClient(args);
    const method = args.positionals[1] ?? 'list';
    if (method === 'list' || method === 'stats') {
        const result = await client.call('session.list');
        printResult(result, getFlag(args, 'pretty', false));
    }
    else if (method === 'get') {
        const result = await client.call('session.get', {
            externalKey: requireFlag(args, 'key'),
            agentName: requireFlag(args, 'agent'),
        });
        printResult(result, getFlag(args, 'pretty', false));
    }
    else if (method === 'delete' || method === 'rm') {
        const result = await client.call('session.delete', {
            externalKey: requireFlag(args, 'key'),
            agentName: requireFlag(args, 'agent'),
        });
        printResult(result, getFlag(args, 'pretty', false));
    }
    else {
        throw new Error(`Unknown session subcommand: ${method}. Use: list, get, delete`);
    }
}
// ─── Subcommand: webhook (envoi programmatique) ───────────────────────────
async function cmdWebhook(args) {
    const provider = String(getFlag(args, 'provider', 'voipms'));
    const client = getClient(args);
    // Payload depuis --payload-file ou --payload (JSON string)
    let payload;
    const payloadFile = getFlag(args, 'payload-file');
    const payloadStr = getFlag(args, 'payload');
    if (payloadFile) {
        const fs = await import('node:fs/promises');
        const raw = await fs.readFile(payloadFile, 'utf-8');
        payload = JSON.parse(raw);
    }
    else if (payloadStr) {
        payload = JSON.parse(payloadStr);
    }
    else {
        throw new Error('webhook requires --payload <json> or --payload-file <path>');
    }
    const autoDispatch = getFlag(args, 'agent')
        ? {
            agentName: requireFlag(args, 'agent'),
            runner: requireFlag(args, 'runner'),
            model: getFlag(args, 'model'),
            mode: getFlag(args, 'mode'),
        }
        : undefined;
    const result = await client.call('webhook.sms', {
        provider,
        payload,
        externalKey: getFlag(args, 'key'),
        autoDispatch,
    });
    printResult(result, getFlag(args, 'pretty', false));
}
// ─── Helpers ───────────────────────────────────────────────────────────────
function getClient(args) {
    const serverUrl = String(getFlag(args, 'server', process.env.BRIDGE_URL ?? 'http://127.0.0.1:3100'));
    const authToken = getFlag(args, 'auth-token') ?? process.env.BRIDGE_AUTH_TOKEN;
    return new BridgeHttpClient({ baseUrl: serverUrl, authToken });
}
function printResult(result, pretty) {
    if (pretty) {
        console.log(JSON.stringify(result, null, 2));
    }
    else {
        console.log(JSON.stringify(result));
    }
}
// ─── Help ──────────────────────────────────────────────────────────────────
function printHelp() {
    console.log(`
overmind-bridge — Multi-agent orchestrator CLI

USAGE
  overmind-bridge <command> [options]

COMMANDS
  server       Start the HTTP JSON-RPC 2.0 server
  call         One-shot JSON-RPC call (use --method or positional)
  send         Shortcut for 'call agent.run'
  scenario     Run a multi-agent scenario from JSON/YAML
  status       List all known agents and their state
  history      Show persisted message history
  sessions     Manage multi-tenant sessions (list, get, delete)
  webhook      Send a webhook payload (voipms/twilio/discord/generic) and optionally auto-dispatch
  replay       Replay a failed/stuck message by ID
  health       Ping the server's /health endpoint
  version      Print version

COMMON OPTIONS
  --server <url>          Bridge server URL (default: http://127.0.0.1:3100)
                          or env BRIDGE_URL
  --auth-token <token>    Bearer token (or env BRIDGE_AUTH_TOKEN)
  --pretty                Pretty-print JSON output
  --timeout <ms>          RPC timeout in ms (default: 600000)
  --var <key=value>       Variable for templating (repeatable)

PROMPT SOURCES (for 'call' and 'send')
  --prompt "..."          Literal string
  --prompt-file <path>    Read from file
  --prompt-stdin          Read from stdin (auto-detect if TTY absent)
  --prompt-base64 "..."   Decode base64 string
  --prompt-file-base64    Decode base64 from file
  --prompt-url <url>      Fetch URL
  --prompt-json '{...}'   Parse JSON object, reformat as text
  --prompt-template "..." Use --var for interpolation

EXAMPLES
  # Server
  overmind-bridge server --port 3100 --auth-token secret

  # Agent call
  overmind-bridge call agent.run --agent scout --runner kilo \\
      --prompt "Analyse BTC"

  # From file with template
  overmind-bridge send --agent scout --runner kilo \\
      --prompt-file ./brief.txt --var ticker=BTC

  # A2A (agent-to-agent)
  overmind-bridge call agent.a2a --from scout --to analyst \\
      --runner kilo --prompt "Validate my analysis"

  # Multi-agent scenario
  overmind-bridge scenario ./workflow.json --var ticker=BTC

  # Status
  overmind-bridge status --status busy

  # Multi-tenant sessions
  overmind-bridge sessions list
  overmind-bridge sessions get --key "+14187207735" --agent pdf_bon_travail
  overmind-bridge sessions rm --key "+14187207735" --agent pdf_bon_travail

  # Webhook programmatic dispatch
  overmind-bridge webhook --provider voipms \\
      --payload '{"from":"+14187207735","message":"Salut","id":"abc"}' \\
      --agent pdf_bon_travail --runner hermes

  # Server with all features
  overmind-bridge server --port 3100 --webhooks --sanitize-json

ENV VARS
  BRIDGE_URL              Default server URL
  BRIDGE_AUTH_TOKEN       Default auth token
  MCP_URL                 Overmind MCP URL (default: http://localhost:3099/mcp)
  POSTGRES_HOST           Postgres host
  POSTGRES_PORT           Postgres port (default: 5432)
  POSTGRES_USER           Postgres user
  POSTGRES_PASSWORD       Postgres password
  POSTGRES_DB             Postgres database

For more help on a specific command:
  overmind-bridge <command> --help
`);
}
// ─── Run ───────────────────────────────────────────────────────────────────
main().catch((err) => {
    console.error('💥 Fatal:', err.message);
    if (process.env.DEBUG)
        console.error(err.stack);
    process.exit(1);
});
//# sourceMappingURL=overmind-bridge.js.map