import WebSocket from 'ws';
export function proxyAgentWebSocket(browserSocket, agent, terminalId) {
    const agentUrl = `${agent.url.replace(/^http/, 'ws')}/api/agent/terminals/${encodeURIComponent(terminalId)}/ws`;
    const agentSocket = new WebSocket(agentUrl, { headers: { Authorization: `Bearer ${agent.token}` } });
    // Pending entries hold both the raw data and the frame type so the relay
    // never silently changes a text frame into a binary one — the browser's
    // ws handler ignores non-string data, which manifests as "agent terminal
    // pane opens but has no output and no input ever takes effect".
    const pendingFromBrowser = [];
    agentSocket.on('open', () => {
        for (const entry of pendingFromBrowser) {
            agentSocket.send(entry.data, { binary: entry.binary });
        }
        pendingFromBrowser.length = 0;
    });
    agentSocket.on('message', (data, isBinary) => {
        if (browserSocket.readyState === WebSocket.OPEN) {
            browserSocket.send(data, { binary: isBinary });
        }
    });
    browserSocket.on('message', (data, isBinary) => {
        if (agentSocket.readyState === WebSocket.OPEN) {
            agentSocket.send(data, { binary: isBinary });
        }
        else if (agentSocket.readyState === WebSocket.CONNECTING) {
            pendingFromBrowser.push({ data, binary: isBinary });
        }
    });
    agentSocket.on('close', (code, reason) => {
        if (browserSocket.readyState === WebSocket.OPEN) {
            // Reserved codes 1005/1006/1015 (and anything <1000) cannot be sent
            // back over the wire — ws throws synchronously on close() if we try.
            // Map them to 1011 (internal error) so an agent that goes away
            // mid-update doesn't bring the main process down.
            const safeCode = code >= 1000 && code !== 1005 && code !== 1006 && code !== 1015 ? code : 1011;
            const safeReason = Buffer.isBuffer(reason) && reason.length <= 123 ? reason : undefined;
            try {
                browserSocket.close(safeCode, safeReason);
            }
            catch {
                // Last-resort: give up cleanly so a malformed reason can't crash us.
                try {
                    browserSocket.terminate();
                }
                catch { /* swallow */ }
            }
        }
    });
    browserSocket.on('close', () => {
        if (agentSocket.readyState === WebSocket.OPEN || agentSocket.readyState === WebSocket.CONNECTING) {
            agentSocket.close();
        }
    });
    agentSocket.on('error', () => {
        if (browserSocket.readyState === WebSocket.OPEN) {
            browserSocket.close(1011, 'agent error');
        }
    });
    browserSocket.on('error', () => {
        if (agentSocket.readyState === WebSocket.OPEN || agentSocket.readyState === WebSocket.CONNECTING) {
            agentSocket.close();
        }
    });
}
//# sourceMappingURL=agentWebSocketProxy.js.map