/**
 * overmind-client.ts — Native Node.js client for Overmind HTTP MCP
 * ═══════════════════════════════════════════════════════════════════
 *
 * Import this module to programmatically control agents.
 * No MCP library needed — raw HTTP + JSON-RPC 2.0.
 *
 * Usage:
 *   npm run build && node dist/overmind-client.js
 *
 * Or import in your TypeScript:
 *   import { OvermindClient } from './overmind-client.js';
 */
const DEFAULT_BASE = 'http://localhost:3099/mcp';
const DEFAULT_AUTH = process.env.OVERMIND_AUTH || 'changeme';
const HEALTH_URL = 'http://localhost:3099/health';
// ─── JSON-RPC helpers ─────────────────────────────────────────────────────────
function jsonrpc(id, method, params = {}) {
    return { jsonrpc: '2.0', id, method, params };
}
async function callMcp(url, auth, method, params, timeoutMs = 60_000) {
    const res = await fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json, text/event-stream',
            'Authorization': `Bearer ${auth}`,
        },
        body: JSON.stringify(jsonrpc(Date.now(), method, params)),
        signal: AbortSignal.timeout(timeoutMs),
    });
    if (!res.ok) {
        throw new Error(`HTTP ${res.status} ${res.statusText}`);
    }
    // SSE stream — read all chunks
    const text = await res.text();
    // Parse SSE: each line is "data: {json}"
    const lines = text.split('\n');
    for (const line of lines) {
        const trimmed = line.trim();
        if (trimmed.startsWith('data: ')) {
            const data = JSON.parse(trimmed.slice(6));
            if (data.result)
                return { result: data.result };
            if (data.error)
                return { error: data.error };
        }
    }
    throw new Error('No result in SSE response');
}
// ─── OvermindClient ───────────────────────────────────────────────────────────
export class OvermindClient {
    url;
    auth;
    id = 0;
    nextId() { return ++this.id; }
    constructor(opts = {}) {
        this.url = opts.url ?? DEFAULT_BASE;
        this.auth = opts.auth ?? DEFAULT_AUTH;
    }
    // ─── Health ──────────────────────────────────────────────────────────────
    async health() {
        try {
            const res = await fetch(HEALTH_URL, {
                headers: { 'Authorization': `Bearer ${this.auth}` },
                signal: AbortSignal.timeout(3000),
            });
            return res.ok;
        }
        catch {
            return false;
        }
    }
    // ─── Agent CRUD ──────────────────────────────────────────────────────────
    /** Create a new agent definition (stored in registry) */
    async createAgent(opts) {
        const res = await callMcp(this.url, this.auth, 'tools/call', {
            name: 'create_agent',
            arguments: opts,
        });
        if ('error' in res)
            throw new Error(`create_agent failed: ${res.error?.message}`);
        return { name: opts.name, runner: opts.runner };
    }
    /** List all registered agents */
    async listAgents() {
        const res = await callMcp(this.url, this.auth, 'tools/call', {
            name: 'list_agents',
            arguments: {},
        });
        if ('error' in res)
            throw new Error(`list_agents failed: ${res.error?.message}`);
        const text = res.result.content[0].text;
        // Parse the markdown list
        return text.split('\n').filter(l => l.trim().startsWith('- ')).map(l => l.trim().slice(2));
    }
    /** Delete an agent from the registry */
    async deleteAgent(name) {
        const res = await callMcp(this.url, this.auth, 'tools/call', {
            name: 'delete_agent',
            arguments: { agentName: name },
        });
        if ('error' in res)
            throw new Error(`delete_agent failed: ${res.error?.message}`);
    }
    // ─── Run agents ─────────────────────────────────────────────────────────
    /**
     * Run a single agent and wait for its output.
     * Timeout is per-agent, not global.
     */
    async runAgent(opts) {
        const start = Date.now();
        const res = await callMcp(this.url, this.auth, 'tools/call', {
            name: 'run_agent',
            arguments: {
                agentName: opts.agentName,
                prompt: opts.prompt,
                timeoutMs: opts.timeoutMs ?? 90_000,
            },
        }, opts.timeoutMs ?? 120_000);
        if ('error' in res) {
            return { agentName: opts.agentName, runner: 'claude', output: '', error: res.error?.message, durationMs: Date.now() - start };
        }
        const result = res.result;
        return {
            agentName: opts.agentName,
            runner: 'claude',
            output: result.content?.[0]?.text ?? '',
            sessionId: result.sessionId,
            durationMs: Date.now() - start,
        };
    }
    /**
     * Run multiple agents in parallel. Returns results in the same order.
     */
    async runPool(agents) {
        return Promise.all(agents.map(a => this.runAgent({ agentName: a.name, prompt: a.prompt, timeoutMs: a.timeoutMs })));
    }
    // ─── Lifecycle control ──────────────────────────────────────────────────
    /** Get status of a running agent (reads from RAM, no disk I/O) */
    async agentStatus(agentName, runner) {
        const res = await callMcp(this.url, this.auth, 'tools/call', {
            name: 'agent_control',
            arguments: { agentName, runner, action: 'status' },
        });
        if ('error' in res)
            return `Error: ${res.error?.message}`;
        const text = res.result.content[0].text;
        return text;
    }
    /** Stream output from a running agent (non-blocking) */
    async agentStream(agentName, runner) {
        const res = await callMcp(this.url, this.auth, 'tools/call', {
            name: 'agent_control',
            arguments: { agentName, runner, action: 'stream' },
        });
        if ('error' in res)
            return { output: `Error: ${res.error?.message}`, isComplete: true };
        const text = res.result.content[0].text;
        const isComplete = text.includes('isComplete: true') || text.includes('**isComplete:** true');
        return { output: text, isComplete };
    }
    /** Wait for an agent to finish (blocking, polls every 1s) */
    async agentWait(agentName, timeoutMs = 900_000, runner) {
        const res = await callMcp(this.url, this.auth, 'tools/call', {
            name: 'agent_control',
            arguments: { agentName, runner, action: 'wait', timeoutMs },
        }, timeoutMs + 10_000);
        if ('error' in res)
            return `Error: ${res.error?.message}`;
        return res.result.content[0].text;
    }
    /** Kill a running agent */
    async agentKill(agentName, runner) {
        const res = await callMcp(this.url, this.auth, 'tools/call', {
            name: 'agent_control',
            arguments: { agentName, runner, action: 'kill' },
        });
        if ('error' in res)
            return `Error: ${res.error?.message}`;
        return res.result.content[0].text;
    }
}
// ─── CLI demo ────────────────────────────────────────────────────────────────
async function demo() {
    const client = new OvermindClient();
    console.log('\n═══ Overmind Client Demo ═══\n');
    // Health
    const ok = await client.health();
    console.log(`[health] ${ok ? '✓' : '✗'} Overmind MCP server`);
    // List agents
    const agents = await client.listAgents();
    console.log(`[list_agents] ${agents.length} agents registered`);
    // Demo: create + run a probe agent
    const TEST_AGENT = 'client_demo_probe';
    try {
        await client.createAgent({
            name: TEST_AGENT,
            runner: 'claude',
            prompt: 'Réponds exactement: PONG',
        });
        console.log(`[create_agent] ${TEST_AGENT} créé`);
        const result = await client.runAgent({
            agentName: TEST_AGENT,
            prompt: 'PING',
            timeoutMs: 30_000,
        });
        console.log(`[run_agent] ✓ (${result.durationMs}ms)`);
        console.log(`  Output: ${result.output.slice(0, 120).trim()}`);
        await client.deleteAgent(TEST_AGENT);
        console.log(`[delete_agent] ${TEST_AGENT} supprimé`);
    }
    catch (e) {
        console.error('[demo] Erreur:', e);
    }
}
// Run demo if executed directly
demo().catch(console.error);
// Export for use as module
export { OvermindClient as default };
//# sourceMappingURL=overmind-client.js.map