"use strict";
/**
 * AgentBridgeCommand — `openlife bridge ...` subcommands.
 *
 * v1.11.0 — Core ↔ Agent Bridge.
 *
 * The legacy `openlife agent <verb>` aliases (install/start/status) remain
 * intact in src/index.ts. The bridge namespace is dedicated to discovery and
 * cross-process commanding between core CLI and autonomous daemon.
 *
 * Subcommands:
 *   bridge status                 — manifest liveness from any cwd
 *   bridge push <verb> [args...]  — POST /api/v1/core/run via manifest token
 *   bridge pull                   — drain ~/.openlife/cli-inbox/
 *   bridge listen [--since=N]     — SSE tail of /api/v1/events
 *   bridge memory <query>         — local OmniMemory.search(query)
 *   bridge story list|show|transition|epics  — v1.12.0 Story Loader proxy
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.registerBridgeCommands = registerBridgeCommands;
const http = __importStar(require("http"));
const AgentDiscovery_1 = require("./AgentDiscovery");
const GatewayManifest_1 = require("../orchestrator/util/GatewayManifest");
function manifestAuthHeader(manifest) {
    const raw = `${GatewayManifest_1.MANIFEST_AUTH_USER}:${manifest.token}`;
    return `Basic ${Buffer.from(raw, 'utf-8').toString('base64')}`;
}
function emitJson(obj) {
    process.stdout.write(`${JSON.stringify(obj, null, 2)}\n`);
}
function requireAlive(info) {
    if (!info.alive || !info.manifest) {
        emitJson({ ok: false, error: 'daemon_not_reachable', liveness: info.liveness, source: info.source, ageSeconds: info.ageSeconds });
        process.exitCode = 1;
        return false;
    }
    return true;
}
function httpJsonRequest(manifest, method, pathname, body, timeoutMs = 30_000) {
    return new Promise((resolve, reject) => {
        const payload = body !== undefined ? Buffer.from(JSON.stringify(body), 'utf-8') : undefined;
        const req = http.request({
            host: manifest.host,
            port: manifest.port,
            method,
            path: pathname,
            headers: {
                'Authorization': manifestAuthHeader(manifest),
                ...(payload ? { 'Content-Type': 'application/json', 'Content-Length': String(payload.length) } : {}),
            },
            timeout: timeoutMs,
        }, (res) => {
            const chunks = [];
            res.on('data', (c) => chunks.push(c instanceof Buffer ? c : Buffer.from(c)));
            res.on('end', () => {
                const text = Buffer.concat(chunks).toString('utf-8');
                let parsed = null;
                try {
                    parsed = text ? JSON.parse(text) : null;
                }
                catch { /* non-json */ }
                resolve({ status: res.statusCode || 0, body: text, json: parsed });
            });
        });
        req.on('error', reject);
        req.on('timeout', () => {
            req.destroy(new Error(`request_timeout_after_${timeoutMs}ms`));
        });
        if (payload)
            req.write(payload);
        req.end();
    });
}
// ─── Sub-handlers ────────────────────────────────────────────────────────────
async function statusCmd() {
    const info = (0, AgentDiscovery_1.discover)();
    emitJson({
        ok: info.alive,
        alive: info.alive,
        liveness: info.liveness,
        ageSeconds: info.ageSeconds,
        source: info.source,
        path: info.path,
        manifest: info.manifest ? {
            host: info.manifest.host,
            port: info.manifest.port,
            pid: info.manifest.pid,
            profile: info.manifest.profile,
            startedAt: info.manifest.startedAt,
            refreshedAt: info.manifest.refreshedAt,
            version: info.manifest.version,
            telegram: info.manifest.telegram,
        } : null,
    });
    if (!info.alive)
        process.exitCode = 1;
}
async function pushCmd(verb, args, opts) {
    const info = (0, AgentDiscovery_1.discover)();
    if (!requireAlive(info))
        return;
    try {
        const res = await httpJsonRequest(info.manifest, 'POST', '/api/v1/core/run', {
            verb,
            args,
            text: opts.text,
            sync: opts.sync !== false,
        });
        emitJson({ ok: res.status >= 200 && res.status < 300, status: res.status, response: res.json ?? res.body });
        if (res.status >= 400)
            process.exitCode = 1;
    }
    catch (err) {
        emitJson({ ok: false, error: 'request_failed', detail: err instanceof Error ? err.message : String(err) });
        process.exitCode = 1;
    }
}
async function pullCmd() {
    // CliInbox lands in Phase 4 — until then return empty inbox so the command
    // exists end-to-end and tooling can rely on it.
    const { CliInbox } = await Promise.resolve().then(() => __importStar(require('./CliInbox'))).catch(() => ({ CliInbox: null }));
    if (!CliInbox) {
        emitJson({ ok: true, pending: [], note: 'cli_inbox_not_available' });
        return;
    }
    const pending = await CliInbox.drain();
    emitJson({ ok: true, pending });
}
async function listenCmd(opts) {
    const info = (0, AgentDiscovery_1.discover)();
    if (!requireAlive(info))
        return;
    const sinceQs = opts.since ? `?since=${encodeURIComponent(opts.since)}` : '';
    const req = http.request({
        host: info.manifest.host,
        port: info.manifest.port,
        method: 'GET',
        path: `/api/v1/events${sinceQs}`,
        headers: {
            'Authorization': manifestAuthHeader(info.manifest),
            'Accept': 'text/event-stream',
        },
    }, (res) => {
        if (res.statusCode !== 200) {
            emitJson({ ok: false, error: 'sse_handshake_failed', status: res.statusCode });
            process.exitCode = 1;
            return;
        }
        res.setEncoding('utf-8');
        res.on('data', (chunk) => {
            // SSE frames come in as raw text — print as-is so the user sees them.
            process.stdout.write(chunk);
        });
        res.on('end', () => process.exit(0));
    });
    req.on('error', (err) => {
        emitJson({ ok: false, error: 'sse_connection_failed', detail: err.message });
        process.exitCode = 1;
    });
    req.end();
    // Clean up on Ctrl+C
    process.on('SIGINT', () => { req.destroy(); process.exit(0); });
}
// v1.12.0 — story bridge proxies.
function buildStoriesQuery(opts) {
    const parts = [];
    if (opts.epic)
        parts.push(`epic=${encodeURIComponent(opts.epic)}`);
    if (opts.status)
        parts.push(`status=${encodeURIComponent(opts.status)}`);
    if (opts.cluster)
        parts.push(`cluster=${encodeURIComponent(opts.cluster)}`);
    return parts.length ? `?${parts.join('&')}` : '';
}
async function storyListCmd(opts) {
    const info = (0, AgentDiscovery_1.discover)();
    if (!requireAlive(info))
        return;
    try {
        const res = await httpJsonRequest(info.manifest, 'GET', `/api/v1/stories${buildStoriesQuery(opts)}`);
        emitJson({ ok: res.status >= 200 && res.status < 300, status: res.status, response: res.json ?? res.body });
        if (res.status >= 400)
            process.exitCode = 1;
    }
    catch (err) {
        emitJson({ ok: false, error: 'request_failed', detail: err instanceof Error ? err.message : String(err) });
        process.exitCode = 1;
    }
}
async function storyShowCmd(id) {
    const info = (0, AgentDiscovery_1.discover)();
    if (!requireAlive(info))
        return;
    try {
        const res = await httpJsonRequest(info.manifest, 'GET', `/api/v1/stories/${encodeURIComponent(id)}`);
        emitJson({ ok: res.status >= 200 && res.status < 300, status: res.status, response: res.json ?? res.body });
        if (res.status >= 400)
            process.exitCode = 1;
    }
    catch (err) {
        emitJson({ ok: false, error: 'request_failed', detail: err instanceof Error ? err.message : String(err) });
        process.exitCode = 1;
    }
}
async function storyTransitionCmd(id, to, opts) {
    const info = (0, AgentDiscovery_1.discover)();
    if (!requireAlive(info))
        return;
    try {
        const res = await httpJsonRequest(info.manifest, 'POST', `/api/v1/stories/${encodeURIComponent(id)}/transition`, {
            to, role: opts.role, author: opts.author, note: opts.note,
        });
        emitJson({ ok: res.status >= 200 && res.status < 300, status: res.status, response: res.json ?? res.body });
        if (res.status >= 400)
            process.exitCode = 1;
    }
    catch (err) {
        emitJson({ ok: false, error: 'request_failed', detail: err instanceof Error ? err.message : String(err) });
        process.exitCode = 1;
    }
}
async function epicsCmd() {
    const info = (0, AgentDiscovery_1.discover)();
    if (!requireAlive(info))
        return;
    try {
        const res = await httpJsonRequest(info.manifest, 'GET', '/api/v1/epics');
        emitJson({ ok: res.status >= 200 && res.status < 300, status: res.status, response: res.json ?? res.body });
        if (res.status >= 400)
            process.exitCode = 1;
    }
    catch (err) {
        emitJson({ ok: false, error: 'request_failed', detail: err instanceof Error ? err.message : String(err) });
        process.exitCode = 1;
    }
}
async function memoryCmd(query) {
    // OmniMemory imports are heavy — load lazily so `--help` stays fast.
    const { OmniMemory } = await Promise.resolve().then(() => __importStar(require('../memory/OmniMemory')));
    const omni = new OmniMemory();
    try {
        const result = await omni.search(query);
        if (!result || result.trim().length === 0) {
            emitJson({ ok: true, query, results: [], note: 'no_hits' });
            return;
        }
        process.stdout.write(`${result}\n`);
    }
    catch (err) {
        emitJson({ ok: false, error: 'memory_search_failed', detail: err instanceof Error ? err.message : String(err) });
        process.exitCode = 1;
    }
}
// ─── Registration ────────────────────────────────────────────────────────────
function registerBridgeCommands(program) {
    const bridge = program
        .command('bridge')
        .description('Core ↔ Agent bridge (v1.11.0): discover daemon, push commands, pull inbox, tail events, search memory');
    bridge
        .command('status')
        .description('Inspect daemon liveness via gateway-manifest.json from any cwd')
        .action(() => { void statusCmd(); });
    bridge
        .command('push <verb> [args...]')
        .description('POST /api/v1/core/run on the discovered daemon')
        .option('--text <text>', 'Free-form text payload (used when verb=chat)')
        .option('--no-sync', 'Fire and forget — do not wait for the reply')
        .action((verb, args, opts) => {
        void pushCmd(verb, args || [], opts);
    });
    bridge
        .command('pull')
        .description('Drain ~/.openlife/cli-inbox/ — daemon→core notifications')
        .action(() => { void pullCmd(); });
    bridge
        .command('listen')
        .description('Tail /api/v1/events as Server-Sent Events')
        .option('--since <id>', 'Resume from a specific Last-Event-Id')
        .action((opts) => { void listenCmd(opts); });
    bridge
        .command('memory <query...>')
        .description('Search OmniMemory locally (works even if daemon is down)')
        .action((queryParts) => {
        void memoryCmd(queryParts.join(' '));
    });
    // v1.12.0 — story bridge proxies.
    const story = bridge
        .command('story')
        .description('v1.12.0 — Proxy story-loader queries through the daemon');
    story
        .command('list')
        .description('GET /api/v1/stories with optional filters')
        .option('--epic <id>', 'Filter by epic id')
        .option('--status <s>', 'Filter by status')
        .option('--cluster <c>', 'Filter by cluster')
        .action((opts) => {
        void storyListCmd(opts);
    });
    story
        .command('show <id>')
        .description('GET /api/v1/stories/:id')
        .action((id) => { void storyShowCmd(id); });
    story
        .command('transition <id> <to>')
        .description('POST /api/v1/stories/:id/transition')
        .option('--role <role>', 'Override role (po|dev|qa|admin)')
        .option('--author <a>', 'Change log author marker')
        .option('--note <n>', 'Change log note')
        .action((id, to, opts) => {
        void storyTransitionCmd(id, to, opts);
    });
    bridge
        .command('epics')
        .description('GET /api/v1/epics — list epics seen by the daemon')
        .action(() => { void epicsCmd(); });
}
