/**
 * 进程检测服务
 *
 * 用于检测孤儿进程（当 runtime-bridge 异常关闭后仍在运行的 Agent 进程）
 */
import { execSync } from 'child_process';
import os from 'os';
import { createLogger } from '../utils/logger.js';
const log = createLogger('process-detector');
/**
 * 在 Windows 上执行 PowerShell 命令
 *
 * 使用 -EncodedCommand 传递 Base64 编码的命令，
 * 彻底避免 cmd.exe 对管道符 |、引号、$ 等特殊字符的转义破坏。
 *
 * @param psScript PowerShell 脚本内容（UTF-16LE 编码后 Base64）
 * @param timeout 超时时间（毫秒）
 * @returns 命令输出
 */
function execPowerShell(psScript, timeout = 15000) {
    // PowerShell -EncodedCommand 需要 UTF-16LE 编码的 Base64
    const buffer = Buffer.from(psScript, 'utf16le');
    const encoded = buffer.toString('base64');
    const output = execSync(`powershell -NoProfile -NonInteractive -EncodedCommand ${encoded}`, { encoding: 'utf8', timeout, windowsHide: true });
    // ★ 过滤 PowerShell CLIXML 噪音（进度信息等 stderr 混入 stdout 的内容）
    // CLIXML 格式：#< CLIXML <Objs ...>...</Objs>
    return output.replace(/#\s*<\s*CLIXML[\s\S]*?(?=#<\s*CLIXML|$)/g, '').trim();
}
/**
 * 检测进程是否存活
 *
 * @param pid 进程 ID
 * @returns 是否存活
 */
export function isProcessRunning(pid) {
    try {
        if (os.platform() === 'win32') {
            // Windows: 使用 tasklist 检测，比 process.kill(pid, 0) 更可靠
            // process.kill(pid, 0) 在 Windows 上对某些子进程可能权限不足
            const output = execSync(`tasklist /FI "PID eq ${pid}" /NH /FO CSV`, { encoding: 'utf8', timeout: 5000, windowsHide: true });
            // tasklist 返回中包含 PID 则进程存活
            return output.includes(String(pid));
        }
        else {
            // Unix: 发送信号 0 检测进程是否存活
            process.kill(pid, 0);
            return true;
        }
    }
    catch {
        // 进程不存在或无法访问
        return false;
    }
}
/**
 * 查找 Agent 相关的进程（Claude Code、OpenCode 等）
 *
 * @returns 匹配的进程列表
 */
export function findClaudeCodeProcesses() {
    const platform = os.platform();
    const results = [];
    try {
        if (platform === 'win32') {
            // Windows: 优先使用 PowerShell（wmic 已弃用）
            results.push(...findProcessesWindows());
        }
        else {
            // macOS/Linux: 使用 ps
            results.push(...findProcessesUnix());
        }
    }
    catch (error) {
        // 命令执行失败，可能没有匹配的进程
        log.debug('Failed to list processes:', error);
    }
    return results;
}
/**
 * Windows: 使用 PowerShell 查找 Agent 进程
 * PowerShell 比 wmic 更可靠（wmic 在 Win10 21H1+ 已弃用）
 */
function findProcessesWindows() {
    const results = [];
    try {
        const psScript = `Get-CimInstance Win32_Process -Filter "Name LIKE '%node%' OR Name LIKE '%claude%' OR Name LIKE '%opencode%' OR Name LIKE '%python%'" | Select-Object ProcessId, CommandLine | ConvertTo-Json -Compress`;
        const output = execPowerShell(psScript, 10000);
        if (!output || !output.trim())
            return results;
        let processes;
        try {
            const parsed = JSON.parse(output);
            // PowerShell 单条结果返回对象，多条返回数组
            processes = Array.isArray(parsed) ? parsed : [parsed];
        }
        catch {
            return results;
        }
        for (const proc of processes) {
            if (!proc.ProcessId || !proc.CommandLine)
                continue;
            const cmd = proc.CommandLine;
            if (isAgentProcess(cmd)) {
                const agentId = extractAgentId(cmd);
                results.push({
                    pid: proc.ProcessId,
                    command: cmd,
                    agentId,
                });
            }
        }
    }
    catch (error) {
        // PowerShell 失败，回退到 wmic
        log.debug('PowerShell process query failed, trying wmic fallback:', error);
        results.push(...findProcessesWindowsWmic());
    }
    return results;
}
/**
 * Windows: wmic 回退方案
 */
function findProcessesWindowsWmic() {
    const results = [];
    try {
        const output = execSync('wmic process where "name like \'%node%\' or name like \'%claude%\' or name like \'%opencode%\'" get processid,commandline /format:list', { encoding: 'utf8', timeout: 5000, windowsHide: true });
        const lines = output.split('\n');
        let currentPid = null;
        let currentCmd = null;
        for (const line of lines) {
            const trimmed = line.trim();
            if (trimmed.startsWith('CommandLine=')) {
                currentCmd = trimmed.substring('CommandLine='.length);
            }
            else if (trimmed.startsWith('ProcessId=')) {
                currentPid = trimmed.substring('ProcessId='.length);
            }
            if (currentPid && currentCmd) {
                if (isAgentProcess(currentCmd)) {
                    const agentId = extractAgentId(currentCmd);
                    results.push({
                        pid: parseInt(currentPid, 10),
                        command: currentCmd,
                        agentId,
                    });
                }
                currentPid = null;
                currentCmd = null;
            }
        }
    }
    catch (error) {
        log.debug('wmic process query failed:', error);
    }
    return results;
}
/**
 * Unix: 使用 ps 查找 Agent 进程
 */
function findProcessesUnix() {
    const results = [];
    try {
        const output = execSync('ps aux | grep -E "claude|claude-code|opencode" | grep -v grep', { encoding: 'utf8', timeout: 5000 });
        const lines = output.split('\n').filter(l => l.trim());
        for (const line of lines) {
            const parts = line.trim().split(/\s+/);
            if (parts.length >= 2) {
                const pid = parseInt(parts[1], 10);
                const cmd = parts.slice(10).join(' ');
                if (isAgentProcess(cmd)) {
                    const agentId = extractAgentId(cmd);
                    results.push({
                        pid,
                        command: cmd,
                        agentId,
                    });
                }
            }
        }
    }
    catch (error) {
        log.debug('ps process query failed:', error);
    }
    return results;
}
/**
 * 检查命令行是否是 Agent 相关进程
 * 包含 Claude Code、OpenCode 等各种 Agent 进程特征
 *
 * @param cmd 命令行
 * @returns 是否匹配
 */
function isAgentProcess(cmd) {
    if (!cmd)
        return false;
    const lowerCmd = cmd.toLowerCase();
    // Claude Code 相关特征
    if (lowerCmd.includes('claude') ||
        lowerCmd.includes('claude-code') ||
        lowerCmd.includes('@anthropic-ai/claude-code') ||
        lowerCmd.includes('cli.js')) {
        return true;
    }
    // OpenCode 相关特征
    if (lowerCmd.includes('opencode') ||
        lowerCmd.includes('open-code') ||
        lowerCmd.includes('opencode.exe') ||
        lowerCmd.includes('opencode.cmd')) {
        return true;
    }
    // 通用特征：包含 serve --port 模式的 node 进程（OpenCode SDK 的 opencode serve --port）
    if (lowerCmd.includes('serve') && lowerCmd.includes('--port')) {
        return true;
    }
    return false;
}
/**
 * 从命令行提取 Agent ID
 *
 * @param cmd 命令行
 * @returns Agent ID 或 undefined
 */
function extractAgentId(cmd) {
    if (!cmd)
        return undefined;
    // 尝试匹配 AWS_AGENT_ID 环境变量（某些情况下 wmic/PowerShell 会包含环境变量）
    const envMatch = cmd.match(/AWS_AGENT_ID=(\S+)/);
    if (envMatch) {
        return envMatch[1];
    }
    // 尝试匹配 --agent-id 参数
    const argMatch = cmd.match(/--agent-id[=\s]+(\S+)/);
    if (argMatch) {
        return argMatch[1];
    }
    return undefined;
}
/**
 * 根据 agentId 查找孤儿进程
 *
 * 检测策略（按优先级）：
 * 1. 通过命令行中的 agentId 直接匹配
 * 2. Windows: 通过 PowerShell 在命令行中模糊搜索 agentId
 * 3. Windows: 通过进程树搜索，覆盖 agentId 仅存在于祖先进程环境变量中的场景
 *
 * @param agentId Agent ID
 * @returns 进程信息或 undefined
 */
export function findOrphanProcessByAgentId(agentId) {
    // 1. 先通过命令行匹配
    const processes = findClaudeCodeProcesses();
    const cmdMatch = processes.find(p => p.agentId === agentId);
    if (cmdMatch)
        return cmdMatch;
    // 2. Windows 特有：在命令行中模糊搜索 agentId
    if (os.platform() === 'win32') {
        const envMatch = findProcessByEnvVarWindows(agentId);
        if (envMatch)
            return envMatch;
        // ★ 3. Windows: 进程树搜索 —— 遍历所有 Agent 进程，
        //    检查其祖先进程中是否有 node(runtime-bridge) → shell 的模式
        const treeMatch = findOrphanByAncestorEnvSearch(agentId);
        if (treeMatch)
            return treeMatch;
    }
    return undefined;
}
/**
 * Windows: 通过进程环境变量查找包含 AWS_AGENT_ID 的进程
 *
 * 策略：在所有 node/claude/opencode 进程的命令行中模糊搜索 agentId
 */
function findProcessByEnvVarWindows(agentId) {
    try {
        const psScript = `$targetId = '${agentId}'; $procs = Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -and ($_.Name -like '*node*' -or $_.Name -like '*claude*' -or $_.Name -like '*opencode*') }; foreach ($proc in $procs) { if ($proc.CommandLine -like "*$targetId*") { @{ProcessId=$proc.ProcessId; CommandLine=$proc.CommandLine; AgentId=$targetId} | ConvertTo-Json -Compress } }`;
        const output = execPowerShell(psScript, 10000);
        if (!output || !output.trim())
            return undefined;
        const lines = output.trim().split('\n').filter(l => l.trim());
        for (const line of lines) {
            try {
                const parsed = JSON.parse(line.trim());
                if (parsed.ProcessId) {
                    return {
                        pid: parsed.ProcessId,
                        command: parsed.CommandLine || '',
                        agentId: parsed.AgentId || agentId,
                    };
                }
            }
            catch {
                // 跳过无法解析的行
            }
        }
    }
    catch (error) {
        log.debug('PowerShell env var query failed:', error);
    }
    return undefined;
}
/**
 * Windows: 通过祖先环境变量搜索孤儿进程。
 *
 * 场景说明：
 * runtime-bridge 或其子进程可能把 AWS_AGENT_ID 传给 shell/agent 祖先进程，
 * 最终运行的 claude/opencode/codex 进程命令行里不一定包含 agentId。
 *
 * 此时：
 * - findProcessesWindows() 能找到 agent CLI 进程，但 extractAgentId() 提取不到 agentId
 * - findProcessByEnvVarWindows() 在命令行中搜不到 agentId（环境变量不在命令行中）
 *
 * 策略：用极简 PowerShell 获取全量进程树数据 → 在 Node.js 层做祖先链匹配
 */
function findOrphanByAncestorEnvSearch(agentId) {
    try {
        // ★ 极简 PowerShell —— 只做数据采集，不做任何逻辑判断
        const psScript = `Get-CimInstance Win32_Process | Select-Object ProcessId, ParentProcessId, Name, CommandLine | ConvertTo-Json -Compress`;
        const output = execPowerShell(psScript, 15000);
        if (!output || !output.trim())
            return undefined;
        // ★ 所有逻辑在 Node.js 层执行
        return searchOrphanInProcessTree(output.trim(), agentId);
    }
    catch (error) {
        log.debug('[findOrphanByAncestorEnvSearch] Failed:', error);
    }
    return undefined;
}
/**
 * 在进程树中搜索孤儿 Agent 进程（纯 Node.js 逻辑）
 *
 * 核心思路：
 * 1. 解析全部 Windows 进程列表
 * 2. 找出所有 Agent 候选进程（claude.exe / opencode.exe / 含特征的 node 进程）
 * 3. 对每个候选向上遍历祖先链
 * 4. 如果祖先链中出现 node(runtime-bridge) → powershell/cmd 的模式 → 判定为孤儿
 */
function searchOrphanInProcessTree(rawJson, agentId) {
    let allProcs;
    try {
        // ★ 过滤 CLIXML 噪音后再解析 JSON
        const cleaned = rawJson.replace(/#\s*<\s*CLIXML[\s\S]*?(?=\{|\[|$)/g, '').trim();
        const jsonStr = extractJsonFromOutput(cleaned);
        const parsed = JSON.parse(jsonStr);
        allProcs = Array.isArray(parsed) ? parsed : [parsed];
    }
    catch (e) {
        log.debug(`[searchOrphanInProcessTree] JSON parse failed, raw length=${rawJson.length}`, e);
        return undefined;
    }
    if (!allProcs.length)
        return undefined;
    // 构建快速查找表
    const procMap = new Map();
    for (const p of allProcs) {
        if (p.processId) {
            procMap.set(p.processId, p);
        }
    }
    /**
     * 检查进程是否是 Agent 相关进程
     */
    function isCandidate(p) {
        const nameLower = p.name.toLowerCase();
        const cmd = (p.commandLine || '').toLowerCase();
        // Claude Code 进程
        if (nameLower.includes('claude'))
            return true;
        if (cmd.includes('claude') || cmd.includes('@anthropic-ai/claude-code') || cmd.includes('cli.js'))
            return true;
        // OpenCode 进程
        if (nameLower.includes('opencode'))
            return true;
        if (cmd.includes('opencode'))
            return true;
        // serve --port 模式
        if (cmd.includes('serve') && cmd.includes('--port'))
            return true;
        return false;
    }
    /**
     * 检查进程名是否为 Shell 类型
     */
    function isShell(name) {
        const n = name.toLowerCase();
        return n === 'powershell.exe' || n === 'cmd.exe' || n === 'pwsh.exe' || n === 'bash.exe';
    }
    /**
     * 向上追踪祖先链，查找 node(bridge) → shell 的模式
     * ★ 改进：返回匹配的 shell 进程 PID，用于后续 agentId 验证
     */
    function findAncestorShell(pid) {
        let currentPid = pid;
        const maxDepth = 10;
        for (let depth = 0; depth < maxDepth; depth++) {
            const ancestor = procMap.get(currentPid);
            if (!ancestor)
                break;
            // 如果祖先是 shell 进程
            if (isShell(ancestor.name)) {
                // 检查 shell 的父进程是否是 node（runtime-bridge 是 node 进程）
                const shellParent = procMap.get(ancestor.parentProcessId);
                if (shellParent && shellParent.name.toLowerCase() === 'node.exe') {
                    const nodeCmd = (shellParent.commandLine || '').toLowerCase();
                    // 验证这个 node 是否是 runtime-bridge
                    if (nodeCmd.includes('runtime-bridge') || nodeCmd.includes('agentswork')) {
                        log.debug(`[searchOrphanInProcessTree] Found bridge ancestor chain: agent(${pid}) -> shell(${ancestor.processId}) -> node(${shellParent.processId})`);
                        return { shellPid: ancestor.processId, shellCmd: ancestor.commandLine || '' };
                    }
                }
            }
            currentPid = ancestor.parentProcessId;
            if (currentPid <= 0)
                break;
        }
        return null;
    }
    // ★ 策略1：遍历所有进程，找 Agent 候选并检查其祖先链
    // 然后通过 shell 的命令行或环境变量验证 agentId
    for (const proc of allProcs) {
        if (!isCandidate(proc))
            continue;
        const shellInfo = findAncestorShell(proc.processId);
        if (shellInfo) {
            // ★ 验证 agentId：检查 shell 命令行中是否包含目标 agentId
            // shell 的命令行中不一定直接包含 agentId，但可以尝试匹配。
            const shellCmdLower = shellInfo.shellCmd.toLowerCase();
            const cmdLineIncludesAgentId = shellCmdLower.includes(agentId.toLowerCase());
            if (cmdLineIncludesAgentId) {
                log.info(`[searchOrphanInProcessTree] Found orphan by tree search (agentId verified): agentId=${agentId}, pid=${proc.processId}, name=${proc.name}`);
                return {
                    pid: proc.processId,
                    command: proc.commandLine || '',
                    agentId,
                };
            }
            // ★ 如果只有一个候选进程匹配 runtime-bridge 祖先链，直接返回
            // （没有其他 agent 竞争时无需精确验证）
            const allCandidates = allProcs.filter(p => isCandidate(p) && findAncestorShell(p.processId));
            if (allCandidates.length === 1) {
                log.info(`[searchOrphanInProcessTree] Found orphan by tree search (single candidate): agentId=${agentId}, pid=${proc.processId}, name=${proc.name}`);
                return {
                    pid: proc.processId,
                    command: proc.commandLine || '',
                    agentId,
                };
            }
            // 多个候选时，尝试通过进程命令行中的工作目录匹配
            log.debug(`[searchOrphanInProcessTree] Multiple candidates found (${allCandidates.length}), cannot determine which belongs to agentId=${agentId}`);
        }
    }
    // ★ 策略2：宽松搜索 —— 查找所有不在已知会话中的 Agent 候选进程
    // 当进程树断链时（shell 父进程已死，agent 被 reparent），走这个策略
    // 通过直接读取进程的环境变量来验证 agentId
    const candidateWithoutBridge = allProcs.filter(p => isCandidate(p) && !findAncestorShell(p.processId));
    for (const proc of candidateWithoutBridge) {
        // 尝试通过读取进程环境变量来验证 agentId
        const verified = verifyProcessAgentId(proc.processId, agentId);
        if (verified) {
            log.info(`[searchOrphanInProcessTree] Found orphan by env verification: agentId=${agentId}, pid=${proc.processId}, name=${proc.name}`);
            return {
                pid: proc.processId,
                command: proc.commandLine || '',
                agentId,
            };
        }
    }
    return undefined;
}
/**
 * 从混合输出中提取 JSON 字符串
 * 处理 CLIXML 噪音、进度信息等 PowerShell 杂讯
 */
function extractJsonFromOutput(output) {
    // 找到第一个 [ 或 { 开始的位置
    const arrayStart = output.indexOf('[');
    const objStart = output.indexOf('{');
    let start = -1;
    if (arrayStart >= 0 && objStart >= 0) {
        start = Math.min(arrayStart, objStart);
    }
    else if (arrayStart >= 0) {
        start = arrayStart;
    }
    else if (objStart >= 0) {
        start = objStart;
    }
    if (start < 0)
        return output;
    return output.substring(start);
}
/**
 * 通过读取进程环境变量来验证 agentId
 *
 * Windows 下无法直接读取其他进程的环境变量，但可以通过以下方式间接验证：
 * 1. 检查进程命令行中是否包含 agentId
 * 2. 检查父进程命令行中是否包含 agentId
 * 3. 检查进程的整个祖先链中是否有匹配的 runtime-bridge 启动的 shell
 *
 * @param pid 进程 PID
 * @param targetAgentId 目标 Agent ID
 * @returns 是否匹配
 */
function verifyProcessAgentId(pid, targetAgentId) {
    try {
        // 策略1：检查进程命令行是否包含 agentId
        const psScript = `
      try {
        $proc = Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}" -ErrorAction SilentlyContinue
        if ($proc -and $proc.CommandLine -like '*${targetAgentId}*') { 'MATCH' } else { 'NOMATCH' }
      } catch { 'ERROR' }
    `;
        const result = execPowerShell(psScript, 5000).trim();
        if (result === 'MATCH')
            return true;
        // 策略2：检查父进程命令行是否包含 agentId
        const parentPsScript = `
      try {
        $proc = Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}" -ErrorAction SilentlyContinue
        if ($proc) {
          $parentPid = $proc.ParentProcessId
          $parent = Get-CimInstance Win32_Process -Filter "ProcessId = $parentPid" -ErrorAction SilentlyContinue
          if ($parent -and $parent.CommandLine -like '*${targetAgentId}*') { 'MATCH' } else { 'NOMATCH' }
        } else { 'NOPROC' }
      } catch { 'ERROR' }
    `;
        const parentResult = execPowerShell(parentPsScript, 5000).trim();
        if (parentResult === 'MATCH')
            return true;
        // 策略3：检查祖父进程（node → powershell → claude 三级链中，agentId 在 shell 命令行中）
        const grandParentPsScript = `
      try {
        $proc = Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}" -ErrorAction SilentlyContinue
        if ($proc) {
          $parentPid = $proc.ParentProcessId
          $parent = Get-CimInstance Win32_Process -Filter "ProcessId = $parentPid" -ErrorAction SilentlyContinue
          if ($parent) {
            $grandParentPid = $parent.ParentProcessId
            $grandParent = Get-CimInstance Win32_Process -Filter "ProcessId = $grandParentPid" -ErrorAction SilentlyContinue
            if ($grandParent -and $grandParent.CommandLine -like '*${targetAgentId}*') { 'MATCH' } else { 'NOMATCH' }
          } else { 'NOPARENT' }
        } else { 'NOPROC' }
      } catch { 'ERROR' }
    `;
        const grandParentResult = execPowerShell(grandParentPsScript, 5000).trim();
        return grandParentResult === 'MATCH';
    }
    catch (error) {
        log.debug(`[verifyProcessAgentId] Failed for pid=${pid}, agentId=${targetAgentId}:`, error);
        return false;
    }
}
/**
 * 通过工作目录路径查找孤儿进程
 *
 * 当 agentId 无法匹配到进程时，通过工作目录路径做宽泛搜索。
 * 适用于以下场景：
 * - runtime-bridge 重启后持久化会话丢失
 * - agentId 可能仅在祖先进程环境变量中，命令行搜索不到
 * - 进程树断链，无法通过祖先链匹配
 *
 * @param workspacePath 工作目录路径
 * @returns 匹配的进程信息或 undefined
 */
export function findOrphanProcessByWorkspace(workspacePath) {
    if (!workspacePath)
        return undefined;
    try {
        const processes = findClaudeCodeProcesses();
        // 在匹配的进程命令行中搜索工作目录路径
        const normalizedPath = workspacePath.replace(/\\/g, '/').toLowerCase();
        for (const proc of processes) {
            const cmdNormalized = proc.command.replace(/\\/g, '/').toLowerCase();
            if (cmdNormalized.includes(normalizedPath)) {
                log.info(`[findOrphanProcessByWorkspace] Found process by workspace: pid=${proc.pid}, workspace=${workspacePath}`);
                return proc;
            }
        }
        // ★ Windows 特有：通过 PowerShell 搜索工作目录匹配的进程
        if (os.platform() === 'win32') {
            const escapedPath = workspacePath.replace(/'/g, "''");
            const psScript = `
        $targetPath = '${escapedPath}'.ToLower().Replace('\\', '/')
        $procs = Get-CimInstance Win32_Process | Where-Object { 
          $_.CommandLine -and ($_.Name -like '*node*' -or $_.Name -like '*claude*' -or $_.Name -like '*opencode*')
        }
        foreach ($proc in $procs) {
          $cmdPath = $proc.CommandLine.ToLower().Replace('\\', '/')
          if ($cmdPath -like "*$targetPath*") {
            @{ProcessId=$proc.ProcessId; CommandLine=$proc.CommandLine} | ConvertTo-Json -Compress
          }
        }
      `;
            const output = execPowerShell(psScript, 10000);
            if (output && output.trim()) {
                const lines = output.trim().split('\n').filter(l => l.trim());
                for (const line of lines) {
                    try {
                        const parsed = JSON.parse(line.trim());
                        if (parsed.ProcessId) {
                            log.info(`[findOrphanProcessByWorkspace] Found process by PowerShell workspace search: pid=${parsed.ProcessId}`);
                            return {
                                pid: parsed.ProcessId,
                                command: parsed.CommandLine || '',
                            };
                        }
                    }
                    catch {
                        // 跳过无法解析的行
                    }
                }
            }
        }
    }
    catch (error) {
        log.debug('[findOrphanProcessByWorkspace] Failed:', error);
    }
    return undefined;
}
/**
 * 根据 PID 终止进程
 *
 * @param pid 进程 ID
 * @returns 是否成功
 */
export function terminateProcess(pid) {
    try {
        process.kill(pid, 'SIGTERM');
        return true;
    }
    catch (error) {
        log.warn(`Failed to terminate process ${pid}:`, error);
        return false;
    }
}
/**
 * 终止进程树（包括所有子进程）
 *
 * 跨平台实现：
 * - Windows: 使用 taskkill /T 终止整个进程树
 * - Unix: 递归获取所有子进程并逐个终止
 *
 * @param pid 进程 ID
 * @returns { terminated: 成功终止的进程数，failed: 失败的进程数 }
 */
export function terminateProcessTree(pid) {
    const result = { terminated: 0, failed: 0 };
    if (os.platform() === 'win32') {
        // Windows: 使用 taskkill /T /F 终止进程树
        try {
            execSync(`taskkill /PID ${pid} /T /F`, { encoding: 'utf8', timeout: 5000, windowsHide: true });
            result.terminated++;
            log.info(`Terminated process tree for pid=${pid}`);
        }
        catch (error) {
            const err = error;
            log.warn(`Failed to terminate process tree for pid=${pid}:`, err.message);
            result.failed++;
        }
    }
    else {
        // Unix: 递归获取所有子进程并终止
        const childPids = getChildProcesses(pid);
        log.debug(`Found ${childPids.length} child processes for pid=${pid}`);
        // 先终止所有子进程
        for (const childPid of childPids) {
            const childResult = terminateProcessTree(childPid);
            result.terminated += childResult.terminated;
            result.failed += childResult.failed;
        }
        // 然后终止父进程
        if (terminateProcess(pid)) {
            result.terminated++;
        }
        else {
            result.failed++;
        }
    }
    return result;
}
/**
 * Unix: 获取进程的所有子进程（递归）
 *
 * @param pid 父进程 ID
 * @returns 子进程 PID 列表
 */
function getChildProcesses(pid) {
    try {
        const output = execSync(`pgrep -P ${pid}`, { encoding: 'utf8', timeout: 3000 });
        const children = output.trim().split('\n').filter(Boolean).map(Number);
        // 递归获取孙进程
        const allDescendants = [];
        for (const childPid of children) {
            allDescendants.push(childPid);
            allDescendants.push(...getChildProcesses(childPid));
        }
        return allDescendants;
    }
    catch {
        // pgrep 失败（无子进程或命令不存在）
        return [];
    }
}
/**
 * 等待进程完全终止（轮询检测）
 *
 * @param pid 进程 ID
 * @param timeoutMs 超时时间（毫秒）
 * @returns 是否成功终止
 */
export async function waitForProcessExit(pid, timeoutMs = 5000) {
    const startTime = Date.now();
    while (Date.now() - startTime < timeoutMs) {
        if (!isProcessRunning(pid)) {
            return true;
        }
        // 等待 100ms 后重试
        const delay = Math.min(100, timeoutMs - (Date.now() - startTime));
        if (delay > 0) {
            await new Promise(resolve => setTimeout(resolve, delay));
        }
    }
    return false;
}
/**
 * 检测并收集所有孤儿进程信息
 * 用于 runtime-bridge 启动时恢复控制
 *
 * 改进策略：
 * 1. 检查持久化会话中的 PID 是否仍在运行（使用可靠的 isProcessRunning）
 * 2. PID 存活时，优先从进程列表匹配详细信息，匹配不到则用持久化信息构造
 * 3. 对于没有 PID 的会话，通过 agentId 搜索进程（包括环境变量搜索）
 *
 * @param persistedSessions 持久化的会话列表（包含 PID 信息）
 * @returns 孤儿进程列表
 */
export function detectOrphanProcesses(persistedSessions) {
    const orphans = [];
    // 1. 首先检查持久化会话中的 PID 是否仍在运行
    for (const session of persistedSessions) {
        if (session.pid && isProcessRunning(session.pid)) {
            // PID 存活，尝试获取详细进程信息
            const processes = findClaudeCodeProcesses();
            const matched = processes.find(p => p.pid === session.pid);
            if (matched) {
                orphans.push({
                    agentId: session.agentId,
                    process: matched,
                    sessionInfo: {
                        workspacePath: session.workspacePath,
                        command: session.command,
                    },
                });
                log.info(`Found orphan process by PID: agentId=${session.agentId}, pid=${session.pid}`);
            }
            else {
                // ★ PID 存活但进程列表匹配不到（可能是命令行特征未被识别）
                // 仍然报告为孤儿进程，用持久化信息构造 ProcessInfo
                orphans.push({
                    agentId: session.agentId,
                    process: {
                        pid: session.pid,
                        command: session.command || '(unknown - persisted session)',
                        agentId: session.agentId,
                    },
                    sessionInfo: {
                        workspacePath: session.workspacePath,
                        command: session.command,
                    },
                });
                log.info(`Found orphan process by PID (no cmd match): agentId=${session.agentId}, pid=${session.pid}`);
            }
        }
    }
    // 2. 对于没有 PID 但有 agentId 的会话，尝试通过 agentId 搜索进程
    for (const session of persistedSessions) {
        if (!session.pid && session.agentId) {
            const process = findOrphanProcessByAgentId(session.agentId);
            if (process && !orphans.some(o => o.agentId === session.agentId)) {
                orphans.push({
                    agentId: session.agentId,
                    process,
                    sessionInfo: {
                        workspacePath: session.workspacePath,
                        command: session.command,
                    },
                });
                log.info(`Found orphan process by agentId: agentId=${session.agentId}, pid=${process.pid}`);
            }
        }
    }
    return orphans;
}
/**
 * 检测进程健康状态
 *
 * @param pid 进程 ID
 * @param agentId Agent ID
 * @returns 健康状态
 */
export async function checkProcessHealth(pid, agentId) {
    const status = {
        pid,
        agentId,
        alive: false,
        cpu: 0,
        memoryMB: 0,
        status: 'dead',
        lastUpdate: new Date(),
    };
    try {
        // 检查进程是否存在
        if (!isProcessRunning(pid)) {
            status.status = 'dead';
            log.debug(`Process ${pid} is not running`);
            return status;
        }
        status.alive = true;
        // 获取进程资源使用情况
        const { cpu, memoryMB } = await getProcessResources(pid);
        status.cpu = cpu;
        status.memoryMB = memoryMB;
        // 判断状态
        if (memoryMB > 2048) {
            status.status = 'high_memory';
        }
        else if (cpu > 100) {
            status.status = 'high_cpu';
        }
        else {
            status.status = 'healthy';
        }
        log.debug(`Process ${pid} health: CPU=${cpu}%, Memory=${memoryMB}MB, Status=${status.status}`);
        return status;
    }
    catch (error) {
        log.warn(`Failed to check health for process ${pid}:`, error);
        status.status = 'dead';
        return status;
    }
}
/**
 * 获取进程资源使用情况
 *
 * @param pid 进程 ID
 * @returns { cpu: CPU使用率, memoryMB: 内存使用(MB) }
 */
async function getProcessResources(pid) {
    const platform = os.platform();
    if (platform === 'win32') {
        // Windows: 使用 PowerShell 获取进程资源
        const psScript = `Get-CimInstance Win32_Process -Filter "ProcessId=${pid}" | Select-Object @{Name='CPU';Expression={$_.WorkingSetSize/1MB}}, @{Name='Mem';Expression={$_.WorkingSetSize/1MB}} | ConvertTo-Json -Compress`;
        try {
            const output = execPowerShell(psScript, 5000);
            if (!output || !output.trim()) {
                return { cpu: 0, memoryMB: 0 };
            }
            const parsed = JSON.parse(output);
            // WorkingSetSize 是内存使用
            return {
                cpu: 0, // Windows 难以准确获取 CPU 使用率
                memoryMB: Math.round((parsed.WorkingSetSize || 0) / 1024 / 1024),
            };
        }
        catch {
            return { cpu: 0, memoryMB: 0 };
        }
    }
    else {
        // Unix: 使用 ps 获取资源
        try {
            const output = execSync(`ps -p ${pid} -o %cpu=,rss=`, { encoding: 'utf8', timeout: 5000 });
            const parts = output.trim().split(/\s+/);
            if (parts.length >= 2) {
                return {
                    cpu: parseFloat(parts[0]) || 0,
                    memoryMB: Math.round(parseInt(parts[1], 10) / 1024), // RSS is KB
                };
            }
        }
        catch {
            return { cpu: 0, memoryMB: 0 };
        }
    }
    return { cpu: 0, memoryMB: 0 };
}
/**
 * 批量检测会话健康状态
 *
 * @param sessions 持久化会话列表
 * @returns 健康状态列表
 */
export async function checkAllSessionsHealth(sessions) {
    const results = new Map();
    const promises = sessions
        .filter(s => s.pid)
        .map(async (session) => {
        // s.pid is guaranteed to exist after filter
        const health = await checkProcessHealth(session.pid, session.agentId);
        results.set(session.agentId, health);
    });
    await Promise.all(promises);
    return results;
}
/**
 * 全局发现：扫描机器上所有 Agent 进程，并标注管理状态
 *
 * 返回三类进程：
 * - managed: 在注册表中且进程存活
 * - orphan: 进程存在但不在注册表中（脱离控制）
 * - unknown: 在注册表中但进程已死亡
 *
 * @param managedPids 已知被管理的 PID 集合（来自 ProcessRegistry）
 * @returns 发现的进程列表
 */
export function discoverAllProcesses(managedPids = new Set(), managedAgentIds = new Set()) {
    const results = [];
    // 1. 扫描机器上所有 Agent 进程
    const allAgentProcesses = findClaudeCodeProcesses();
    for (const proc of allAgentProcesses) {
        const isManaged = managedPids.has(proc.pid);
        const isAgentManaged = proc.agentId ? managedAgentIds.has(proc.agentId) : false;
        results.push({
            ...proc,
            isRunning: true,
            managementStatus: isManaged || isAgentManaged ? 'managed' : 'orphan',
            sessionId: undefined,
            lastSeen: new Date().toISOString(),
        });
    }
    // 2. 找出在注册表中但未被扫描到的进程（可能已死亡）
    // 这部分由调用方（AgentProcessManager）补充
    return results;
}
/**
 * 批量发现：扫描所有进程并补充注册表中缺失的进程状态
 *
 * @param registryExport 注册表导出的进程记录（含 agentId, pid）
 * @returns 完整的发现结果
 */
export function discoverAllProcessesFull(registryExport) {
    const managedPids = new Set(registryExport.map(r => r.pid));
    const managedAgentIds = new Set(registryExport.map(r => r.agentId));
    const agentIdToRecord = new Map(registryExport.map(r => [r.agentId, r]));
    const results = [];
    const seenPids = new Set();
    // 1. 扫描实际运行的进程
    const runningProcesses = findClaudeCodeProcesses();
    for (const proc of runningProcesses) {
        seenPids.add(proc.pid);
        const isManaged = managedPids.has(proc.pid);
        const isAgentManaged = proc.agentId ? managedAgentIds.has(proc.agentId) : false;
        const record = proc.agentId ? agentIdToRecord.get(proc.agentId) : undefined;
        results.push({
            ...proc,
            isRunning: true,
            managementStatus: isManaged || isAgentManaged ? 'managed' : 'orphan',
            sessionId: record ? undefined : undefined,
            lastSeen: new Date().toISOString(),
        });
    }
    // 2. 注册表中存在但进程已死亡的记录
    for (const record of registryExport) {
        if (!seenPids.has(record.pid) && !isProcessRunning(record.pid)) {
            results.push({
                pid: record.pid,
                command: '(process exited)',
                agentId: record.agentId,
                isRunning: false,
                managementStatus: 'unknown',
                lastSeen: undefined,
            });
        }
    }
    return results;
}
