"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.AgentBrowser = exports.Page = void 0;
exports.getB4n1webBinary = getB4n1webBinary;
exports.getB4n1webVersion = getB4n1webVersion;
exports.checkVersionCompatibility = checkVersionCompatibility;
exports.createBrowserAndGoto = createBrowserAndGoto;
const child_process_1 = require("child_process");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const types_1 = require("./types");
function getB4n1webBinary() {
    const home = process.env.HOME || process.env.USERPROFILE || '/home/' + process.env.USER;
    const paths = [];
    const bundledBinary = path.join(__dirname, '..', 'bin', 'b4n1web-linux');
    if (fs.existsSync(bundledBinary)) {
        try {
            fs.accessSync(bundledBinary, fs.constants.X_OK);
            return bundledBinary;
        }
        catch {
        }
    }
    const envBinary = process.env.B4N1WEB_BINARY;
    if (envBinary)
        paths.push(envBinary);
    paths.push(home + '/.local/bin/b4n1web', home + '/.b4n1web/bin/b4n1web', '/usr/local/bin/b4n1web', '/usr/bin/b4n1web');
    const pathEnv = process.env.PATH || '';
    const pathDirs = pathEnv.split(':').filter(p => p);
    for (const dir of pathDirs) {
        paths.push(dir + '/b4n1web');
    }
    for (const filePath of paths) {
        try {
            if (fs.existsSync(filePath)) {
                const stats = fs.statSync(filePath);
                if (stats.isFile() && (stats.mode & 0o111) !== 0) {
                    return filePath;
                }
            }
        }
        catch {
        }
    }
    return null;
}
function getB4n1webVersion() {
    const binaryPath = getB4n1webBinary();
    if (!binaryPath) {
        return null;
    }
    try {
        const version = (0, child_process_1.execSync)(`${binaryPath} --version`, { timeout: 5000 }).toString().trim();
        const parts = version.split(' ');
        if (parts.length >= 2 && parts[0] === 'b4n1web') {
            return parts[1];
        }
        return null;
    }
    catch {
        return null;
    }
}
const SDK_VERSION = '0.8.0';
function checkVersionCompatibility() {
    const binaryVersion = getB4n1webVersion();
    if (!binaryVersion) {
        return null;
    }
    if (binaryVersion !== SDK_VERSION) {
        console.warn(`Warning: Version mismatch: SDK v${SDK_VERSION} requires binary v${SDK_VERSION}, ` +
            `but found v${binaryVersion}. Some features may not work correctly. ` +
            `To update: curl -sL https://web.b4n1.com/install | bash`);
    }
    return binaryVersion;
}
class Page {
    url;
    markdown;
    links;
    screenshot;
    jsOutput;
    constructor(data) {
        this.url = data.url;
        this.markdown = data.markdown;
        this.links = data.links;
        this.screenshot = data.screenshot;
        this.jsOutput = data.jsOutput;
    }
    getMainContent() {
        const lines = this.markdown.split('\n');
        const contentLines = lines.length > 2 ? lines.slice(2) : lines;
        return contentLines.join('\n').trim();
    }
    findLinksByText(text) {
        const lowerText = text.toLowerCase();
        return this.links.filter(link => link.toLowerCase().includes(lowerText));
    }
}
exports.Page = Page;
class AgentBrowser {
    mode;
    timeout;
    userAgent;
    binaryPath;
    currentUrl = null;
    sessionId;
    sessionStarted = false;
    viewportWidth = 1280;
    viewportHeight = 720;
    emulatedDevice = null;
    constructor(options = {}) {
        this.mode = options.mode ?? types_1.BrowserMode.LIGHT;
        this.timeout = options.timeout ?? 30;
        this.userAgent = options.userAgent ?? 'B4n1Web-Agent/1.0';
        this.sessionId = `agent-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
        const binary = getB4n1webBinary();
        if (!binary) {
            const home = process.env.HOME || '';
            const pathDirs = (process.env.PATH || '').split(':').filter(p => p);
            throw new types_1.BinaryNotFoundError(`b4n1web binary not found.\nChecked paths:\n` +
                `- /usr/local/bin/b4n1web\n` +
                `- /usr/bin/b4n1web\n` +
                `- ${home}/.local/bin/b4n1web\n` +
                `- ${home}/.b4n1web/bin/b4n1web\n` +
                `- PATH: ${pathDirs.join(', ')}\n\n` +
                `For MCP server: curl -sL https://web.b4n1.com/install | bash`);
        }
        this.binaryPath = binary;
    }
    escapeArg(arg) {
        return JSON.stringify(arg);
    }
    runSessionCommand(subcommand, ...args) {
        try {
            return (0, child_process_1.execSync)(`${this.binaryPath} session ${subcommand} ${this.sessionId} ${args.join(' ')}`, { timeout: this.timeout * 1000 }).toString().trim();
        }
        catch (error) {
            if (error.message?.includes('timed out')) {
                throw new Error(`Session command timed out after ${this.timeout}s`);
            }
            throw new Error(`Session command "${subcommand}" failed: ${error.message}`);
        }
    }
    ensureSession(url) {
        if (!this.sessionStarted) {
            (0, child_process_1.execSync)(`${this.binaryPath} session start ${this.sessionId}`, { timeout: 10000 });
            this.sessionStarted = true;
        }
        if (url) {
            (0, child_process_1.execSync)(`${this.binaryPath} session goto ${this.sessionId} ${this.escapeArg(url)}`, { timeout: this.timeout * 1000 });
        }
    }
    async goto(url, waitFor) {
        this.currentUrl = url;
        return new Promise((resolve, reject) => {
            try {
                let cmd = `${this.binaryPath} goto ${url} --mode ${this.mode}`;
                if (waitFor) {
                    cmd += ` --wait-for ${JSON.stringify(waitFor)}`;
                }
                const output = (0, child_process_1.execSync)(cmd, { timeout: this.timeout * 1000 }).toString();
                const page = this.parseOutput(url, output);
                resolve(page);
            }
            catch (error) {
                if (error.message?.includes('timed out')) {
                    reject(new Error(`Binary timed out after ${this.timeout}s`));
                }
                else {
                    reject(new Error(`Binary error: ${error.message}`));
                }
            }
        });
    }
    parseOutput(url, output) {
        let markdown = '';
        let links = [];
        let screenshot;
        let jsOutput;
        for (const line of output.split('\n')) {
            if (line.startsWith('URL:')) {
                continue;
            }
            else if (line.startsWith('Markdown:')) {
                continue;
            }
            else if (line.startsWith('Links:')) {
                try {
                    links = JSON.parse(line.slice(6).trim());
                }
                catch {
                    links = [];
                }
            }
            else if (line.startsWith('Screenshot:')) {
                screenshot = line.slice(11).trim();
                if (screenshot === '') {
                    screenshot = undefined;
                }
            }
            else if (line.startsWith('js_output:')) {
                jsOutput = line.slice(10).trim();
                if (jsOutput === '') {
                    jsOutput = undefined;
                }
            }
            else {
                markdown += line + '\n';
            }
        }
        return new Page({
            url,
            markdown: markdown.trim(),
            links,
            screenshot,
            jsOutput,
        });
    }
    async click(selector) {
        if (!this.currentUrl) {
            throw new Error('No page loaded. Call goto() first.');
        }
        this.ensureSession(this.currentUrl);
        try {
            (0, child_process_1.execSync)(`${this.binaryPath} session click ${this.sessionId} ${this.escapeArg(selector)}`, { timeout: this.timeout * 1000 });
        }
        catch (error) {
            throw new Error(`Click failed on "${selector}": ${error.message}`);
        }
    }
    async typeText(selector, text, clearFirst = false) {
        if (!this.currentUrl) {
            throw new Error('No page loaded. Call goto() first.');
        }
        this.ensureSession(this.currentUrl);
        try {
            let cmd = `${this.binaryPath} session type ${this.sessionId} ${this.escapeArg(selector)} ${this.escapeArg(text)}`;
            if (clearFirst) {
                cmd += ' --clear-first';
            }
            (0, child_process_1.execSync)(cmd, { timeout: this.timeout * 1000 });
        }
        catch (error) {
            throw new Error(`Type text failed on "${selector}": ${error.message}`);
        }
    }
    async waitForSelector(selector, timeoutMs = 10000) {
        if (!this.currentUrl) {
            throw new Error('No page loaded. Call goto() first.');
        }
        this.ensureSession(this.currentUrl);
        try {
            const output = (0, child_process_1.execSync)(`${this.binaryPath} session wait ${this.sessionId} ${this.escapeArg(selector)} --timeout-ms ${timeoutMs}`, { timeout: Math.ceil(timeoutMs / 1000) + 5 }).toString().trim();
            return output === 'true';
        }
        catch {
            return false;
        }
    }
    async screenshot(url) {
        const targetUrl = url || this.currentUrl;
        if (!targetUrl) {
            throw new Error('No URL provided. Pass a URL or call goto() first.');
        }
        this.ensureSession(targetUrl);
        try {
            const output = (0, child_process_1.execSync)(`${this.binaryPath} session screenshot ${this.sessionId} ${this.escapeArg(targetUrl)}`, { timeout: this.timeout * 1000 }).toString().trim();
            return output;
        }
        catch (error) {
            if (error.message?.includes('timed out')) {
                throw new Error(`Screenshot timed out after ${this.timeout}s`);
            }
            throw new Error(`Screenshot failed: ${error.message}`);
        }
    }
    async frames() {
        if (!this.currentUrl) {
            throw new Error('No page loaded. Call goto() first.');
        }
        this.ensureSession(this.currentUrl);
        try {
            const output = this.runSessionCommand('frames');
            return JSON.parse(output);
        }
        catch (error) {
            throw new Error(`Failed to list frames: ${error.message}`);
        }
    }
    async iframeText(index) {
        if (!this.currentUrl) {
            throw new Error('No page loaded. Call goto() first.');
        }
        this.ensureSession(this.currentUrl);
        try {
            return this.runSessionCommand('iframe-text', String(index));
        }
        catch (error) {
            throw new Error(`Failed to get iframe text at index ${index}: ${error.message}`);
        }
    }
    async setViewport(width, height) {
        this.viewportWidth = width;
        this.viewportHeight = height;
        if (this.sessionStarted && this.currentUrl) {
            try {
                (0, child_process_1.execSync)(`${this.binaryPath} session goto ${this.sessionId} ${this.escapeArg(this.currentUrl)} --viewport-width ${width} --viewport-height ${height}`, { timeout: this.timeout * 1000 });
            }
            catch (error) {
                throw new Error(`setViewport failed: ${error.message}`);
            }
        }
    }
    async emulateDevice(device) {
        this.emulatedDevice = device;
        if (this.sessionStarted && this.currentUrl) {
            try {
                (0, child_process_1.execSync)(`${this.binaryPath} session goto ${this.sessionId} ${this.escapeArg(this.currentUrl)} --emulate ${this.escapeArg(device)}`, { timeout: this.timeout * 1000 });
            }
            catch (error) {
                throw new Error(`emulateDevice failed: ${error.message}`);
            }
        }
    }
    getLinks() {
        if (!this.currentUrl) {
            throw new Error('No page loaded. Call goto() first.');
        }
        try {
            const output = (0, child_process_1.execSync)(`${this.binaryPath} get-links --url ${this.currentUrl} --mode ${this.mode}`, { timeout: this.timeout * 1000 }).toString().trim();
            try {
                return JSON.parse(output);
            }
            catch {
                return [];
            }
        }
        catch {
            return [];
        }
    }
    static async getLinksFromPage(url, mode = types_1.BrowserMode.LIGHT) {
        const browser = new AgentBrowser({ mode });
        try {
            const page = await browser.goto(url);
            return page.links;
        }
        finally {
            browser.close();
        }
    }
    static findBinary() {
        return getB4n1webBinary();
    }
    close() {
        if (this.sessionStarted) {
            try {
                (0, child_process_1.execSync)(`${this.binaryPath} session close ${this.sessionId}`, { timeout: 5000 });
            }
            catch {
            }
            this.sessionStarted = false;
        }
    }
    async [Symbol.asyncDispose]() {
        this.close();
    }
}
exports.AgentBrowser = AgentBrowser;
async function createBrowserAndGoto(url, options = {}) {
    const browser = new AgentBrowser(options);
    try {
        return await browser.goto(url);
    }
    finally {
        browser.close();
    }
}
//# sourceMappingURL=browser.js.map