import { spawn } from 'node:child_process';
import { resolveCommand } from '../adapters/cli/registry.js';
class CodexAppServerProbe {
    child;
    nextId = 1;
    stdoutBuffer = '';
    pending = new Map();
    lastStderr = '';
    closed = false;
    constructor(codexBin, cwd) {
        this.child = spawn(codexBin, ['app-server', '--listen', 'stdio://'], {
            cwd,
            env: process.env,
            stdio: ['pipe', 'pipe', 'pipe'],
        });
        this.child.stdout.on('data', chunk => this.onStdout(chunk.toString('utf8')));
        this.child.stderr.on('data', chunk => {
            this.lastStderr = (this.lastStderr + chunk.toString('utf8')).slice(-8000);
        });
        this.child.on('error', err => this.failAll(new Error(`Failed to start Codex app-server: ${err.message}`)));
        this.child.on('exit', (code, signal) => {
            this.closed = true;
            this.failAll(new Error(`Codex app-server exited (code=${code}, signal=${signal})${this.lastStderr ? `\n${this.lastStderr}` : ''}`));
        });
    }
    async initialize(timeoutMs) {
        await this.withTimeout(this.request('initialize', {
            clientInfo: { name: 'botmux-codex-app-thread-picker', version: '0.0.0' },
            capabilities: {
                experimentalApi: true,
                suppressNotifications: ['thread/started', 'thread/status/changed'],
            },
        }), timeoutMs, 'initialize');
        this.notify('initialized');
    }
    request(method, params) {
        if (this.closed)
            return Promise.reject(new Error('Codex app-server is closed'));
        const id = this.nextId++;
        this.child.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n');
        return new Promise((resolve, reject) => {
            this.pending.set(id, { method, resolve, reject });
        });
    }
    notify(method, params) {
        if (this.closed)
            return;
        const msg = { jsonrpc: '2.0', method };
        if (params !== undefined)
            msg.params = params;
        this.child.stdin.write(JSON.stringify(msg) + '\n');
    }
    close() {
        this.closed = true;
        try {
            this.child.kill();
        }
        catch { /* already gone */ }
        this.failAll(new Error('Codex app-server probe closed'));
    }
    async withTimeout(promise, timeoutMs, label) {
        let timer;
        try {
            return await Promise.race([
                promise,
                new Promise((_, reject) => {
                    timer = setTimeout(() => reject(new Error(`Codex app-server ${label} timed out after ${timeoutMs}ms`)), timeoutMs);
                }),
            ]);
        }
        finally {
            if (timer)
                clearTimeout(timer);
        }
    }
    onStdout(data) {
        this.stdoutBuffer += data;
        for (;;) {
            const nl = this.stdoutBuffer.indexOf('\n');
            if (nl < 0)
                return;
            const line = this.stdoutBuffer.slice(0, nl).trim();
            this.stdoutBuffer = this.stdoutBuffer.slice(nl + 1);
            if (!line)
                continue;
            let msg;
            try {
                msg = JSON.parse(line);
            }
            catch {
                continue;
            }
            if (typeof msg.id === 'number' && (msg.result !== undefined || msg.error !== undefined)) {
                const pending = this.pending.get(msg.id);
                if (!pending)
                    continue;
                this.pending.delete(msg.id);
                if (msg.error)
                    pending.reject(new Error(`${pending.method}: ${JSON.stringify(msg.error)}`));
                else
                    pending.resolve(msg.result);
            }
        }
    }
    failAll(err) {
        for (const pending of this.pending.values())
            pending.reject(err);
        this.pending.clear();
    }
}
function stringifyStatus(status) {
    if (!status)
        return undefined;
    if (typeof status === 'string')
        return status;
    if (typeof status === 'object' && typeof status.type === 'string')
        return status.type;
    return undefined;
}
function normalizeThread(raw) {
    const threadId = typeof raw.id === 'string' ? raw.id : undefined;
    const cwd = typeof raw.cwd === 'string' ? raw.cwd : undefined;
    if (!threadId || !cwd)
        return null;
    const updatedAt = typeof raw.updatedAt === 'number' ? raw.updatedAt : undefined;
    const createdAt = typeof raw.createdAt === 'number' ? raw.createdAt : undefined;
    const name = typeof raw.name === 'string' && raw.name.trim() ? raw.name.trim() : undefined;
    const preview = typeof raw.preview === 'string' ? raw.preview.trim() : '';
    const source = typeof raw.source === 'string' ? raw.source : undefined;
    return {
        threadId,
        name,
        preview,
        cwd,
        updatedAtMs: updatedAt !== undefined ? updatedAt * 1000 : undefined,
        createdAtMs: createdAt !== undefined ? createdAt * 1000 : undefined,
        path: typeof raw.path === 'string' ? raw.path : undefined,
        source,
        status: stringifyStatus(raw.status),
    };
}
export async function listCodexAppThreads(opts = {}) {
    const timeoutMs = opts.timeoutMs ?? 7000;
    const codexBin = resolveCommand(opts.codexBin ?? 'codex');
    const cwd = opts.cwd ?? process.cwd();
    const client = new CodexAppServerProbe(codexBin, cwd);
    try {
        await client.initialize(timeoutMs);
        const result = await client.withTimeout(client.request('thread/list', {
            limit: opts.limit ?? 30,
            sortKey: 'updated_at',
            sortDirection: 'desc',
            archived: false,
            useStateDbOnly: true,
            searchTerm: opts.searchTerm && opts.searchTerm.trim() ? opts.searchTerm.trim() : null,
        }), timeoutMs, 'thread/list');
        const rows = Array.isArray(result?.data) ? result.data : [];
        const normalized = rows.map((row) => normalizeThread(row));
        return normalized.filter((thread) => !!thread);
    }
    finally {
        client.close();
    }
}
//# sourceMappingURL=codex-app-threads.js.map