export class OpenCodeBridge {
    type = 'opencode';
    activeSessions = new Map();
    timeoutMs;
    baseUrl;
    authHeader;
    constructor(config) {
        this.baseUrl = config?.baseUrl ?? process.env.OPENCODE_BASE_URL ?? 'http://127.0.0.1:4096';
        this.timeoutMs = config?.timeoutMs ?? parseInt(process.env.BRIDGE_TIMEOUT_MS ?? '300000', 10);
        const password = process.env.OPENCODE_PASSWORD;
        if (password) {
            const username = process.env.OPENCODE_USERNAME ?? 'opencode';
            this.authHeader = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`;
        }
        else {
            this.authHeader = null;
        }
        console.log(`[opencode-bridge] Using OpenCode at ${this.baseUrl}`);
    }
    async fetch(path, options) {
        const headers = {
            'Content-Type': 'application/json',
            ...(options?.headers ?? {}),
        };
        if (this.authHeader) {
            headers['Authorization'] = this.authHeader;
        }
        return fetch(`${this.baseUrl}${path}`, { ...options, headers });
    }
    async createSession() {
        const res = await this.fetch('/session', {
            method: 'POST',
            body: JSON.stringify({}),
        });
        if (!res.ok) {
            const text = await res.text();
            throw new Error(`Failed to create OpenCode session: ${res.status} ${text}`);
        }
        const data = await res.json();
        return data.id;
    }
    async pollStream(sessionId, onChunk) {
        const timeoutAt = Date.now() + this.timeoutMs;
        let fullText = '';
        while (Date.now() < timeoutAt) {
            const res = await this.fetch(`/session/${sessionId}/message?limit=5`);
            if (!res.ok) {
                const text = await res.text();
                throw new Error(`Failed to get messages: ${res.status} ${text}`);
            }
            const messages = await res.json();
            let newText = '';
            for (const msg of messages) {
                if (msg.info?.role === 'assistant') {
                    const content = msg.info.content ?? msg.parts ?? [];
                    for (const block of content) {
                        if (block.type === 'text' && block.text) {
                            newText += block.text;
                        }
                    }
                }
            }
            if (newText.length > fullText.length) {
                const delta = newText.slice(fullText.length);
                fullText = newText;
                onChunk(delta);
            }
            if (fullText.trim()) {
                return fullText;
            }
            await new Promise((r) => setTimeout(r, 500));
        }
        throw new Error('OpenCode session timed out');
    }
    send(opts) {
        const key = opts.squadName ?? opts.cwd;
        const sessionIdPromise = this.createSession();
        sessionIdPromise
            .then(async (sessionId) => {
            this.activeSessions.set(key, sessionId);
            console.log(`[opencode-bridge] Created session ${sessionId} for key="${key}"`);
            try {
                const res = await this.fetch(`/session/${sessionId}/message`, {
                    method: 'POST',
                    body: JSON.stringify({
                        parts: [{ type: 'text', text: opts.prompt }],
                        ...(opts.model ? { model: opts.model } : {}),
                    }),
                });
                if (!res.ok) {
                    const text = await res.text();
                    throw new Error(`Failed to send prompt: ${res.status} ${text}`);
                }
                const fullText = await this.pollStream(sessionId, opts.onChunk);
                if (!fullText.trim()) {
                    console.log(`[opencode-bridge] WARNING: empty response from OpenCode for key="${key}"`);
                    opts.onError({ code: null, message: 'Empty response from OpenCode' });
                    return;
                }
                console.log(`[opencode-bridge] SUCCESS for key="${key}": ${fullText.slice(0, 100)}`);
                opts.onDone(fullText);
            }
            catch (err) {
                const msg = err instanceof Error ? err.message : String(err);
                console.error(`[opencode-bridge] Error for key="${key}":`, msg);
                opts.onError({ code: null, message: msg });
            }
            finally {
                this.activeSessions.delete(key);
                try {
                    await this.fetch(`/session/${sessionId}`, { method: 'DELETE' });
                }
                catch {
                    // ignore cleanup errors
                }
            }
        })
            .catch((err) => {
            const msg = err instanceof Error ? err.message : String(err);
            console.error(`[opencode-bridge] Failed to create session for key="${key}":`, msg);
            opts.onError({ code: null, message: `OpenCode unavailable: ${msg}` });
        });
    }
    isActive(squadName) {
        return this.activeSessions.has(squadName);
    }
    kill(squadName) {
        if (squadName === '__all__') {
            console.log(`[opencode-bridge] Killing all ${this.activeSessions.size} active sessions`);
            for (const [key, sessionId] of this.activeSessions) {
                console.log(`[opencode-bridge] Aborting session for "${key}"`);
                this.fetch(`/session/${sessionId}/abort`, { method: 'POST' }).catch(() => { });
            }
            this.activeSessions.clear();
            return;
        }
        const sessionId = this.activeSessions.get(squadName);
        if (sessionId) {
            this.fetch(`/session/${sessionId}/abort`, { method: 'POST' }).catch(() => { });
            this.activeSessions.delete(squadName);
        }
    }
}
//# sourceMappingURL=opencode-bridge.js.map