/**
 * @module        core/processInspect
 * @description   SPS-ACP 启动的 claude 进程巡检 —— **按真实进程判断,不依赖卡片/slot 记录**。
 *                扫 /proc:认 `CLAUDE_CODE_ENTRYPOINT=sdk-ts`(SPS 经 ACP SDK 启动的标记)的 claude 进程,
 *                据此与"其它途径启动的 claude"(如用户自己的开发会话,无此标记)区分;
 *                有 `SPS_WORKER_SLOT` 的是构建 worker,否则是 chat 会话。
 *                只列**真实存活**的进程(进程没了就不在列表,不留卡片态的幽灵记录)。
 *                提供:列表(类型/内存/cwd/启动时间)、按 pid 关闭(双护栏:必须是 SPS 进程 + cwd 校验防 pid 复用误杀)。
 * @layer         core
 */
import { readdirSync, readFileSync, readlinkSync } from 'node:fs';
import { resolve } from 'node:path';
import { sessionsDir } from '../shared/runtimePaths.js';
function readEnviron(pid) {
    try {
        const env = {};
        for (const kv of readFileSync(`/proc/${pid}/environ`, 'utf-8').split('\0')) {
            const i = kv.indexOf('=');
            if (i > 0)
                env[kv.slice(0, i)] = kv.slice(i + 1);
        }
        return env;
    }
    catch {
        return {};
    }
}
function readComm(pid) {
    try {
        return readFileSync(`/proc/${pid}/comm`, 'utf-8').trim();
    }
    catch {
        return '';
    }
}
function readCwd(pid) {
    try {
        return readlinkSync(`/proc/${pid}/cwd`);
    }
    catch {
        return null;
    }
}
function memMB(pid) {
    try {
        const m = readFileSync(`/proc/${pid}/status`, 'utf-8').match(/VmRSS:\s+(\d+)\s+kB/);
        return m ? Math.round(Number(m[1]) / 1024) : null;
    }
    catch {
        return null;
    }
}
function bootEpoch() {
    try {
        return Number(readFileSync('/proc/stat', 'utf-8').match(/^btime (\d+)/m)?.[1] ?? 0);
    }
    catch {
        return 0;
    }
}
/** 进程启动时刻:/proc/<pid>/stat 第 22 字段(starttime,时钟滴答)+ 系统 btime。CLK_TCK 取 100。 */
function startedAt(pid, bt) {
    try {
        const stat = readFileSync(`/proc/${pid}/stat`, 'utf-8');
        const rest = stat.slice(stat.lastIndexOf(')') + 2).split(' '); // rest[0]=state(field3)…
        const starttime = Number(rest[19]); // field22 = rest[22-3]
        if (!starttime || !bt)
            return null;
        return new Date((bt + starttime / 100) * 1000).toISOString();
    }
    catch {
        return null;
    }
}
/** 交叉引用全局 sessions(chat 的 busy/ready 与 slot/sessionId)。pid → 记录。 */
function sessionByPid() {
    const map = new Map();
    try {
        const parsed = JSON.parse(readFileSync(resolve(sessionsDir(), 'state.json'), 'utf-8'));
        for (const s of Object.values(parsed.sessions ?? {})) {
            if (s.pid != null && s.pid !== '') {
                map.set(Number(s.pid), { slot: s.slot, sessionState: s.sessionState ?? s.status, sessionId: s.sessionId });
            }
        }
    }
    catch {
        /* ignore */
    }
    return map;
}
/** 列出所有 SPS-ACP 启动的真实存活 claude 进程(chat + worker)。 */
export function listSpsProcesses() {
    const out = [];
    let pids;
    try {
        pids = readdirSync('/proc');
    }
    catch {
        return out;
    }
    const bt = bootEpoch();
    const sessions = sessionByPid();
    for (const pid of pids) {
        if (!/^\d+$/.test(pid))
            continue;
        const comm = readComm(pid);
        if (comm !== 'claude' && comm !== 'node')
            continue;
        const env = readEnviron(pid);
        if (env.CLAUDE_CODE_ENTRYPOINT !== 'sdk-ts')
            continue; // 仅 SPS 经 ACP SDK 启动的
        const n = Number(pid);
        const cwd = readCwd(n);
        const isWorker = !!env.SPS_WORKER_SLOT;
        const sess = sessions.get(n);
        const projFromCwd = cwd ? (cwd.split('/').pop() ?? null) : null;
        out.push({
            kind: isWorker ? 'worker' : 'chat',
            slot: isWorker ? `${env.SPS_PROJECT ?? projFromCwd ?? '?'}/${env.SPS_WORKER_SLOT}` : (sess?.slot ?? `${projFromCwd ?? 'chat'}`),
            tool: 'claude',
            pid: n,
            alive: true,
            sessionState: sess?.sessionState ?? 'running',
            project: env.SPS_PROJECT ?? projFromCwd,
            cwd,
            memoryMB: memMB(n),
            startedAt: startedAt(n, bt),
            sessionId: sess?.sessionId ?? null,
        });
    }
    // worker 在前,再按内存降序
    return out.sort((a, b) => (a.kind === b.kind ? (b.memoryMB ?? 0) - (a.memoryMB ?? 0) : a.kind === 'worker' ? -1 : 1));
}
/**
 * 关闭指定 pid 的 SPS 进程。双护栏:
 *  1) pid 必须是当前列出的 SPS 进程(否则可能是用户自己的 claude,拒绝);
 *  2) /proc/<pid>/cwd 必须与登记一致(防 pid 被系统复用后误杀)。
 */
export function killSpsProcess(pid) {
    const rec = listSpsProcesses().find((p) => p.pid === pid);
    if (!rec)
        return { ok: false, message: '该 pid 不是 SPS 启动的进程,拒绝关闭(可能是其它途径启动的 claude)' };
    if (rec.cwd) {
        const live = readCwd(pid);
        if (live && live !== rec.cwd) {
            return { ok: false, message: `pid 的实际 cwd(${live})与登记不符,疑似 pid 被复用,拒绝关闭` };
        }
    }
    try {
        process.kill(pid, 'SIGKILL');
        return { ok: true, message: '已关闭' };
    }
    catch (e) {
        return { ok: false, message: e instanceof Error ? e.message : String(e) };
    }
}
//# sourceMappingURL=processInspect.js.map