/**
 * base.ts — Shared spawn helpers and signal-handling for all invokers.
 *
 * Provides:
 *   - spawnAgent()     — spawn a child process with a 5s SIGTERM→SIGKILL grace.
 *   - buildEnv()       — safe env inheritance + NEXUS_PARENT_DISPATCH marker.
 *   - cancelHandle()   — shared cancel implementation.
 *   - NESTED_LOOP_ENV  — the env-var name that guards against recursive dispatch.
 */
import { spawn } from 'node:child_process';
export const NESTED_LOOP_ENV = 'NEXUS_PARENT_DISPATCH';
const SIGTERM_GRACE_MS = 5_000;
// ─── Env construction ─────────────────────────────────────────────────────
export function buildEnv(req) {
    return {
        ...process.env,
        // Nested-loop guard: child adapters refuse nexus_session_bootstrap when set.
        [NESTED_LOOP_ENV]: req.runId,
        // Suppress any interactive prompts in the child.
        CI: '1',
    };
}
export function spawnAgent(cmd, args, opts) {
    const child = spawn(cmd, args, {
        cwd: opts.cwd,
        env: opts.env,
        stdio: ['ignore', 'pipe', 'pipe'],
    });
    const pid = child.pid ?? 0;
    // Hard timeout: SIGTERM → grace → SIGKILL
    const hardTimer = setTimeout(() => {
        if (!child.killed) {
            child.kill('SIGTERM');
            setTimeout(() => {
                if (!child.killed)
                    child.kill('SIGKILL');
            }, SIGTERM_GRACE_MS);
        }
    }, opts.timeoutMs);
    child.once('exit', () => clearTimeout(hardTimer));
    async function cancel() {
        clearTimeout(hardTimer);
        if (child.killed || child.exitCode !== null)
            return;
        child.kill('SIGTERM');
        await new Promise(resolve => {
            const done = () => resolve();
            child.once('exit', done);
            setTimeout(() => {
                if (!child.killed)
                    child.kill('SIGKILL');
                resolve();
            }, SIGTERM_GRACE_MS);
        });
    }
    return { process: child, pid, cancel };
}
// ─── Async generator: read stdout/stderr into line chunks ─────────────────
export async function* readLines(stream) {
    let buf = '';
    for await (const chunk of stream) {
        buf += String(chunk);
        const lines = buf.split('\n');
        buf = lines.pop() ?? '';
        for (const line of lines) {
            if (line)
                yield line;
        }
    }
    if (buf)
        yield buf;
}
