// URL capture for `claude --verbose --remote-control`.
//
// Claude Code prints the line:
//
//     /remote-control is active · <url>
//
// On 2.1.142 the inter-word separators were literal U+0020 spaces. On
// 2.1.143 they switched to `\x1b[1C` (CSI Cursor Forward 1) — same visual
// layout, different bytes. The 2.1.142-only regex required literal spaces
// between `is`/`active` and silently timed out on 2.1.143.
//
// Strategy:
//   1. Strip every CSI sequence (`ESC [ ... letter`) from a buffer copy.
//      `\x1b[1C` becomes empty; `\x1b[38;5;246m` colour prefixes drop too.
//   2. Match on the stripped buffer with a pattern that anchors only on
//      `/remote-control` (the slash-prefixed keyword claude always prints
//      verbatim). Whatever sits between the anchor and the URL is
//      `[\s\S]*?` so a future release reverting to literal spaces, or
//      changing the middle-dot, still matches.
//   3. Capture stops at whitespace or ESC so the URL is clean.
//
// We accumulate onData chunks into a buffer so a multi-byte split across
// two callbacks still matches. After the first URL is captured, the handler
// unregisters itself so subsequent PTY bytes flow through unchanged.
// Bounded, non-backtracking: a CSI sequence is ESC `[`, then zero-or-more
// digits/semicolons/question-marks, terminated by a single letter.
const CSI = /\x1b\[[0-9;?]*[A-Za-z]/g;
function stripCsi(s) {
    return s.replace(CSI, '');
}
// `[^\s\x1b]+` terminates the URL at the first whitespace or ESC, so even
// if a colour-code prefix snuck through stripCsi the URL stays clean.
const URL_PATTERN = /\/remote-control[\s\S]*?(https?:\/\/[^\s\x1b]+)/;
export function startUrlCapture(opts) {
    const start = Date.now();
    let buffer = '';
    let settled = false;
    let dataSub = null;
    const timeout = setTimeout(() => {
        if (settled)
            return;
        settled = true;
        if (dataSub)
            dataSub.dispose();
        opts.onTimeout();
    }, opts.timeoutMs);
    dataSub = opts.pty.onData((data) => {
        if (settled)
            return;
        buffer += data;
        const match = stripCsi(buffer).match(URL_PATTERN);
        if (!match) {
            // Cap the raw buffer so PTY scroll output does not grow unbounded.
            const keep = 16 * 1024;
            if (buffer.length > keep)
                buffer = buffer.slice(buffer.length - keep);
            return;
        }
        settled = true;
        clearTimeout(timeout);
        if (dataSub)
            dataSub.dispose();
        const url = match[1];
        const ms = Date.now() - start;
        opts.logger(`url-captured pid=${opts.pty.pid} url=${url} ms=${ms}`);
        opts.onUrl(url, ms);
    });
    return {
        cancel: () => {
            if (settled)
                return;
            settled = true;
            clearTimeout(timeout);
            if (dataSub)
                dataSub.dispose();
        },
    };
}
//# sourceMappingURL=url-capture.js.map