//#region extensions/crypto/src/services/browser-service.ts
/**
* Browser Automation Service — PinchTab HTTP API client.
*
* PinchTab is a 12MB Go binary providing browser control via HTTP.
* Token-efficient: extracts structured content (~800 tokens/page)
* instead of screenshots. Stealth mode for bot detection bypass.
*
* HTTP API endpoints:
*   POST /navigate     — Navigate to URL, return page content
*   POST /click        — Click an element by selector
*   POST /type         — Type text into an input field
*   POST /extract      — Extract structured data from current page
*   POST /screenshot   — Take a screenshot (fallback for visual inspection)
*   GET  /status       — Check if PinchTab is running
*
* Default: http://localhost:9222 (configurable via PINCHTAB_URL env var)
*/
/** Throws if the URL does not resolve to a loopback address. */
function assertLocalhost(url) {
	try {
		const host = new URL(url).hostname.toLowerCase();
		if (host === "localhost" || host === "127.0.0.1" || host === "[::1]" || host === "::1") return;
		throw new Error(`BrowserService requires a localhost URL, got "${host}". Set PINCHTAB_URL to http://localhost:<port> or http://127.0.0.1:<port>.`);
	} catch (err) {
		if (err instanceof TypeError) throw new Error(`Invalid PINCHTAB_URL: "${url}" is not a valid URL.`);
		throw err;
	}
}
var BrowserService = class {
	baseUrl;
	constructor(baseUrl) {
		this.baseUrl = baseUrl ?? process.env.PINCHTAB_URL ?? "http://localhost:9222";
		assertLocalhost(this.baseUrl);
	}
	/**
	* Check if PinchTab is running and accessible.
	*/
	async getStatus() {
		try {
			const response = await fetch(`${this.baseUrl}/status`, { signal: AbortSignal.timeout(5e3) });
			if (!response.ok) return { running: false };
			const data = await response.json();
			return {
				running: true,
				url: this.baseUrl,
				version: data.version,
				currentPage: data.currentPage
			};
		} catch {
			return { running: false };
		}
	}
	/**
	* Navigate to a URL and extract page content.
	*/
	async navigate(url, options) {
		const response = await fetch(`${this.baseUrl}/navigate`, {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify({
				url,
				waitFor: options?.waitFor,
				stealth: options?.stealth ?? true,
				timeout: options?.timeout ?? 3e4
			}),
			signal: AbortSignal.timeout(6e4)
		});
		if (!response.ok) {
			const text = await response.text().catch(() => "unknown error");
			throw new Error(`Navigate failed (${response.status}): ${text}`);
		}
		const data = await response.json();
		return {
			url: data.url ?? url,
			title: data.title ?? "",
			content: data.content ?? "",
			links: (data.links ?? []).map((l) => ({
				text: l.text ?? "",
				href: l.href ?? "",
				selector: l.selector ?? ""
			})),
			forms: (data.forms ?? []).map((f) => ({
				action: f.action ?? "",
				method: f.method ?? "GET",
				inputs: (f.inputs ?? []).map((i) => ({
					name: i.name ?? "",
					type: i.type ?? "text",
					placeholder: i.placeholder,
					selector: i.selector ?? ""
				}))
			})),
			status: data.status ?? 200,
			loadTimeMs: data.loadTimeMs ?? 0
		};
	}
	/**
	* Click an element on the current page.
	*/
	async click(selector, options) {
		const response = await fetch(`${this.baseUrl}/click`, {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify({
				selector,
				waitForNavigation: options?.waitForNavigation ?? true
			}),
			signal: AbortSignal.timeout(3e4)
		});
		if (!response.ok) {
			const text = await response.text().catch(() => "unknown error");
			throw new Error(`Click failed (${response.status}): ${text}`);
		}
		const data = await response.json();
		return {
			clicked: data.clicked ?? false,
			selector,
			newUrl: data.newUrl,
			content: data.content
		};
	}
	/**
	* Type text into an input field.
	*/
	async type(selector, text, options) {
		const response = await fetch(`${this.baseUrl}/type`, {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify({
				selector,
				text,
				clear: options?.clear ?? true,
				pressEnter: options?.pressEnter ?? false
			}),
			signal: AbortSignal.timeout(15e3)
		});
		if (!response.ok) {
			const text = await response.text().catch(() => "unknown error");
			throw new Error(`Type failed (${response.status}): ${text}`);
		}
		const data = await response.json();
		return {
			typed: data.typed ?? false,
			selector,
			value: data.value ?? text
		};
	}
	/**
	* Extract structured data from the current page.
	* PinchTab uses smart extraction to keep token count low.
	*/
	async extract(options) {
		const response = await fetch(`${this.baseUrl}/extract`, {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify({
				selector: options?.selector,
				format: options?.format ?? "text"
			}),
			signal: AbortSignal.timeout(15e3)
		});
		if (!response.ok) {
			const text = await response.text().catch(() => "unknown error");
			throw new Error(`Extract failed (${response.status}): ${text}`);
		}
		const data = await response.json();
		return {
			data: data.data ?? {},
			tokens: data.tokens ?? 0
		};
	}
	/**
	* Take a screenshot of the current page.
	* Returns base64-encoded PNG. Use sparingly — not token-efficient.
	*/
	async screenshot(options) {
		const response = await fetch(`${this.baseUrl}/screenshot`, {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify({
				fullPage: options?.fullPage ?? false,
				selector: options?.selector
			}),
			signal: AbortSignal.timeout(15e3)
		});
		if (!response.ok) {
			const text = await response.text().catch(() => "unknown error");
			throw new Error(`Screenshot failed (${response.status}): ${text}`);
		}
		const data = await response.json();
		return {
			base64: data.base64 ?? "",
			width: data.width ?? 0,
			height: data.height ?? 0
		};
	}
};
let _instance = null;
function getBrowserService() {
	if (!_instance) _instance = new BrowserService();
	return _instance;
}
function resetBrowserService() {
	_instance = null;
}
//#endregion
export { BrowserService, getBrowserService, resetBrowserService };

//# sourceMappingURL=browser-service.mjs.map