import { execFile, spawn } from 'node:child_process';
import { existsSync } from 'node:fs';
import { delimiter, join } from 'node:path';
import { promisify } from 'node:util';
export const execFileAsync = promisify(execFile);
// 系统默认任务执行模型。用 alias 'sonnet' — 性价比最优,eval/sample/evolve 统一默认。
// 需要更高质量时显式传 --model opus。report.meta.executorRuntime 记录实际模型 ID。
export const DEFAULT_MODEL = 'sonnet';
export const JUDGE_MODEL = 'haiku';
export const DEFAULT_TIMEOUT_MS = 600_000;
export const MAX_BUFFER = 10 * 1024 * 1024;
export function asErrorLike(err) {
    return typeof err === 'object' && err !== null ? err : {};
}
export function errorMessage(err, fallback = 'unknown error') {
    const details = asErrorLike(err);
    return details.message || fallback;
}
export function parseJson(content) {
    return JSON.parse(content);
}
export function buildExecEnv(skillDir) {
    const proxyUrl = process.env.CCV_PROXY_URL || undefined;
    const env = proxyUrl
        ? { ...process.env, ANTHROPIC_BASE_URL: proxyUrl }
        : { ...process.env };
    if (skillDir) {
        const nodeBin = join(skillDir, 'node_modules', '.bin');
        if (existsSync(nodeBin)) {
            env.PATH = `${nodeBin}${env.PATH ? delimiter + env.PATH : ''}`;
        }
    }
    return env;
}
export function timeoutExecResult(timeoutMs, durationMs) {
    return {
        ok: false,
        error: `execution timed out after ${timeoutMs / 1000}s`,
        durationMs,
        durationApiMs: 0,
        inputTokens: 0,
        outputTokens: 0,
        cacheReadTokens: 0,
        cacheCreationTokens: 0,
        costUSD: 0,
        output: null,
        stopReason: 'timeout',
        numTurns: 0,
    };
}
// SIGINT-killed child(用户在 host 按 Ctrl+C,parent 收到 SIGINT 后广播 SIGTERM 给 children)
// 跟 timeout-killed 区分:stopReason='interrupted',跟 'timeout' / 'error' 都不一样,
// caller / renderer 可以据此显示不同的语义("用户中断" vs "超时" vs "执行错误")。
export function interruptedExecResult(durationMs) {
    return {
        ok: false,
        error: 'execution interrupted (SIGINT)',
        durationMs,
        durationApiMs: 0,
        inputTokens: 0,
        outputTokens: 0,
        cacheReadTokens: 0,
        cacheCreationTokens: 0,
        costUSD: 0,
        output: null,
        stopReason: 'interrupted',
        numTurns: 0,
    };
}
// ===========================================================================
// SIGINT propagation:统一让 spawn 出来的 child 在 parent 收到 SIGINT 时被 kill,
// 避免 host CLI(codex / claude code)Ctrl+C 后内层子进程成为 orphan。
// ===========================================================================
//
// 设计要点:
// - 单一进程级 process.on('SIGINT', ...) listener,惰性首次 spawn 时安装一次(零 leak)
// - 模块级 Set<ChildProcess> registry,child 退出 / 出错时立即 delete
// - SIGTERM 先发(child 一般会做 telemetry flush),500ms grace,然后 SIGKILL fallback
// - handler 用具名 function + process.removeListener(self),不动 host 自己的 listener;
//   然后 process.kill(pid, 'SIGINT') re-raise 让 default action / host listener 接管
// - shuttingDown flag:第一次 SIGINT 进 handler 后置 true,第二次按 Ctrl+C 走 default action
//   立即退出(软关 → 硬关两段式)
// - 同时支持 timeout 跟 abortSignal 两条 kill 路径,跟 SIGINT 共用 grace 逻辑
const activeChildren = new Set();
let sigintListenerInstalled = false;
let shuttingDown = false;
const SIGTERM_GRACE_MS = 500;
function broadcastShutdown() {
    if (shuttingDown)
        return;
    shuttingDown = true;
    for (const child of activeChildren) {
        try {
            child.kill('SIGTERM');
        }
        catch { /* already dead */ }
        setTimeout(() => {
            try {
                if (child.exitCode === null && child.signalCode === null)
                    child.kill('SIGKILL');
            }
            catch { /* */ }
        }, SIGTERM_GRACE_MS).unref();
    }
    // 卸载自己,re-raise SIGINT 让 host listener / default action(exit code 130)接管
    process.removeListener('SIGINT', sigintHandler);
    process.kill(process.pid, 'SIGINT');
}
function sigintHandler() {
    broadcastShutdown();
}
function ensureSigintListener() {
    if (sigintListenerInstalled)
        return;
    sigintListenerInstalled = true;
    process.on('SIGINT', sigintHandler);
}
// test-only:重置模块级状态,让 vitest 之间互不污染
export function __resetSigintRegistryForTest() {
    activeChildren.clear();
    if (sigintListenerInstalled) {
        process.removeListener('SIGINT', sigintHandler);
        sigintListenerInstalled = false;
    }
    shuttingDown = false;
}
/**
 * spawn child + 注册到全局 SIGINT registry。返回 { child, done } 让 caller 自己
 * 操作 stdin(写入 / 关闭),通过 done 等结果。
 *
 * 适用:claude / codex / gemini / script CLI 子进程。HTTP executor(*-api)用 fetch +
 * AbortSignal.timeout,自带 abort,不走这个。claude-sdk in-process 也不走。
 */
export function spawnWithSigintPropagation(command, args, options = {}) {
    const { cwd, env, timeoutMs, maxBuffer = MAX_BUFFER, abortSignal } = options;
    ensureSigintListener();
    const child = spawn(command, args, {
        stdio: ['pipe', 'pipe', 'pipe'],
        ...(cwd && { cwd }),
        ...(env && { env }),
    });
    activeChildren.add(child);
    let stdout = '';
    let stderr = '';
    let bufferOverflow = false;
    let killedByTimeout = false;
    let killedBySignalReason = null;
    let graceTimer = null;
    let timeoutTimer = null;
    function killWithGrace(reason) {
        // 优先级:abort 跟 timeout 互不覆盖(谁先设谁赢);buffer 不重写已有 reason
        if (reason === 'timeout' && !killedBySignalReason)
            killedByTimeout = true;
        if (reason === 'abort' && !killedByTimeout)
            killedBySignalReason = 'SIGTERM';
        try {
            child.kill('SIGTERM');
        }
        catch { /* already dead */ }
        if (graceTimer)
            return;
        // 即使 child trap SIGTERM 不退出,500ms 后强 SIGKILL 兜底
        graceTimer = setTimeout(() => {
            try {
                if (child.exitCode === null && child.signalCode === null)
                    child.kill('SIGKILL');
            }
            catch { /* */ }
        }, SIGTERM_GRACE_MS);
        graceTimer.unref();
    }
    if (timeoutMs && timeoutMs > 0) {
        timeoutTimer = setTimeout(() => killWithGrace('timeout'), timeoutMs);
        timeoutTimer.unref();
    }
    let abortListener = null;
    if (abortSignal) {
        abortListener = () => killWithGrace('abort');
        abortSignal.addEventListener('abort', abortListener, { once: true });
    }
    child.stdout?.on('data', (chunk) => {
        stdout += chunk.toString();
        if (stdout.length > maxBuffer && !bufferOverflow) {
            bufferOverflow = true;
            // 走 killWithGrace 保证 SIGTERM trap child 也会被 500ms 后 SIGKILL 兜底
            killWithGrace('buffer');
        }
    });
    child.stderr?.on('data', (chunk) => {
        stderr += chunk.toString();
    });
    function cleanup() {
        activeChildren.delete(child);
        if (timeoutTimer)
            clearTimeout(timeoutTimer);
        if (graceTimer)
            clearTimeout(graceTimer);
        if (abortListener && abortSignal)
            abortSignal.removeEventListener('abort', abortListener);
    }
    const done = new Promise((resolve, reject) => {
        child.on('error', (err) => {
            cleanup();
            const e = err;
            e.stdout = stdout;
            e.stderr = stderr;
            reject(e);
        });
        child.on('close', (code, signal) => {
            cleanup();
            const killedSig = killedBySignalReason
                || (signal && !killedByTimeout ? signal : null);
            const result = {
                stdout, stderr, code, signal,
                killedByTimeout,
                killedBySignal: killedSig,
            };
            // bufferOverflow 优先 — 数据已截断不可信,即使 child 后来 exit 0 也不能用
            if (bufferOverflow) {
                const e = Object.assign(new Error(`stdout maxBuffer (${maxBuffer}) exceeded`), result);
                reject(e);
                return;
            }
            // **code === 0 时 child 是干净完成的**:即使我们前面发了 SIGTERM(timeout / abort),
            // child 可能 trap 信号并 graceful exit 0 完成数据写入。这种情况 stdout 是完整的,
            // 不应该当 timeout / signal 错误 reject。优先级:exit 0 > 任何 kill reason。
            if (code === 0) {
                resolve(result);
                return;
            }
            if (killedByTimeout) {
                const tSec = timeoutMs ? (timeoutMs / 1000).toFixed(0) : '?';
                const e = Object.assign(new Error(`execution timed out after ${tSec}s`), result);
                reject(e);
                return;
            }
            if (killedSig) {
                const e = Object.assign(new Error(`execution interrupted by signal ${killedSig}`), result);
                reject(e);
                return;
            }
            if (code !== null) {
                const e = Object.assign(new Error(`${command} exited with code ${code}`), result);
                reject(e);
                return;
            }
            // code === null && signal === null:罕见,fallback resolve
            resolve(result);
        });
    });
    return { child, done };
}
//# sourceMappingURL=shared.js.map