UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

2,321 lines • 86.2 kB
import { g as readStringValue, l as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty } from "./string-coerce-6kR-86XC.mjs";
import { M as resolveNonNegativeIntegerOption, b as parseFiniteNumber, j as resolveIntegerOption } from "./number-coercion-Dq1l5muL.mjs";
import { u as toErrorObject } from "./error-coercion-1P2One0X.mjs";
import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.mjs";
import { A as withTimeout } from "./fs-safe-CS1DrPTK.mjs";
import { c as sleepWithAbort } from "./src-DqaLEvc_.mjs";
import { t as formatErrorMessage } from "./errors-BSdPMFao.mjs";
import { n as resolvePreferredOpenClawTmpDir } from "./tmp-openclaw-dir-BSb3HetY.mjs";
import { t as normalizeHostname } from "./hostname-KSsoqRD2.mjs";
import { d as isPrivateNetworkAllowedByPolicy } from "./ssrf-D5TD9F1s.mjs";
import { n as detectMime } from "./mime-CGDYrRzL.mjs";
import { o as getImageMetadata } from "./image-ops-C_E9VrVA.mjs";
import "./error-runtime-C9z8-Tyo.mjs";
import "./string-coerce-runtime-B1b8UNzb.mjs";
import "./text-utility-runtime-UZWYbiiX.mjs";
import "./runtime-env-DMdB1vhM.mjs";
import "./number-runtime-DyNDaYIV.mjs";
import "./media-runtime-BmP2MmqJ.mjs";
import "./media-mime-DLFR6fqC.mjs";
import "./security-runtime-9ynw5sNI.mjs";
import "./temp-path-iPXfxl6o.mjs";
import { a as createDeferred } from "./extension-shared-DrsO57mB.mjs";
import { d as DEFAULT_BROWSER_SNAPSHOT_TIMEOUT_MS, o as DEFAULT_BROWSER_DOWNLOAD_TIMEOUT_MS, r as DEFAULT_AI_SNAPSHOT_MAX_CHARS } from "./constants-C-i9f6FL.mjs";
import { c as resolveActInteractionTimeoutMs, d as resolveBrowserNavigationTimeoutMs, l as resolveActWaitTimeoutMs, n as ACT_MAX_VIEWPORT_DIMENSION, r as ACT_MAX_WAIT_TIME_MS, t as ACT_MAX_CLICK_DELAY_MS } from "./act-policy-tIEm1pjf.mjs";
import { d as BrowserTabNotFoundError } from "./errors-BqlAThZX.mjs";
import { v as getPlaywrightCore } from "./cdp.helpers-B9Hc2ly9.mjs";
import { C as buildRoleSnapshotFromAiSnapshot, F as assertBrowserNavigationResultAllowed, I as parseBrowserNavigationUrl, M as InvalidBrowserNavigationUrlError, N as assertBrowserNavigationAllowed, R as withBrowserNavigationPolicy, S as appendSnapshotUrls, _ as formatAriaSnapshot, w as finalizeRoleSnapshot, x as snapshotRoleViaCdpSession } from "./chrome-DbIHmS_R.mjs";
import { a as resolveStrictExistingUploadPaths, n as DEFAULT_TRACE_DIR } from "./paths-DlZwlk-a.mjs";
import { $ as ensurePageState, A as markBackendDomRefsOnPage, B as withPageNavigationRequestGuard, C as createPageViaPlaywright, D as getObservedBrowserStateViaPlaywright, E as getDocumentIdentitiesViaPlaywright, F as closeBlockedNavigationTarget, G as retirePlaywrightBrowserConnectionExact, H as ensureContextState, I as gotoPageWithNavigationGuard, J as closeRelayOperationConnection, K as isConnectionScopedPage, L as isPolicyDenyNavigationError, M as withCdpSnapshotRoot, N as withPageScopedCdpClient, O as listPagesViaPlaywright, P as assertPageNavigationCompletedSafely, Q as createObservedDialogAbortSignalForPage, R as quarantineBlockedNavigationTarget, S as closeResolvedPageViaPlaywright, T as forceDisconnectPlaywrightForTarget, U as getPageForTargetId, V as connectBrowser, W as hasCachedPlaywrightBrowserConnection, X as isDownloadStartingNavigationError, Y as beginActionDownloadCaptureOnPage, Z as armObservedDialogResponseOnPage, _ as requireRef, a as createAbortPromiseWithListener, at as createDownloadCaptureForPage, b as closePageByTargetIdViaPlaywright, c as interactionNavigationPolicy, ct as buildOverlayInjectionScript, d as runCancellablePageInteraction, dt as matchBrowserUrlPattern, et as getObservedBrowserStateForPage, f as throwIfInteractionAborted, ft as normalizeBrowserEvaluateFunctionSource, g as normalizeTimeoutMs, h as bumpUploadArmId, i as awaitNavigationGuardedInteraction, it as storeRoleRefsForTarget, j as readMainFrameDocumentIdentityForPage, k as refLocator, l as reconcileRemoteDialogAfterActionSettled, lt as planAnnotations, m as bumpDownloadArmId, n as assertInteractionCurrent, nt as respondToObservedDialogOnPage, o as getRestoredPageForTarget, ot as isBrowserObservedDialogBlockedError, p as toFriendlyInteractionError, q as pageTargetInfo, r as awaitActionWithAbort, rt as restoreRoleRefsForTarget, s as hasInteractionNavigationPolicy, st as buildOverlayClearScript, t as BrowserInteractionAuthorityError, tt as markObservedDialogsHandledRemotelyForPage, u as resolveBoundedDelayMs, ut as scaleAnnotations, v as requireRefOrSelector, w as focusPageByTargetIdViaPlaywright, x as closePlaywrightBrowserConnection, y as toAIFriendlyError, z as wasBrowserNavigationSourcePreservedAfterPolicyDenial } from "./pw-tools-core.interactions.navigation-BmxH8HOX.mjs";
import "./form-fields-B9V6H6db.mjs";
import { t as writeExternalFileWithinOutputRoot } from "./output-files-Dx2qs87Q.mjs";
import path from "node:path";
import fs from "node:fs/promises";
//#region extensions/browser/src/browser/pw-tools-core.activity.ts
/**
* Page inspection helpers for visible text, observed errors, network requests,
* and console messages from Playwright page state.
*/
/** Returns visible page text without evaluating caller-provided JavaScript. */
async function getPageTextViaPlaywright(opts) {
	const maxChars = Math.min(opts.maxChars ?? 4e4, DEFAULT_AI_SNAPSHOT_MAX_CHARS);
	if (!Number.isSafeInteger(maxChars) || maxChars <= 0) throw new Error("maxChars must be a positive integer.");
	const timeout = DEFAULT_BROWSER_SNAPSHOT_TIMEOUT_MS;
	const deadline = Date.now() + timeout;
	const controller = new AbortController();
	const signal = opts.signal ? AbortSignal.any([opts.signal, controller.signal]) : controller.signal;
	const { abortPromise, cleanup } = createAbortPromiseWithListener(signal);
	const read = async () => {
		signal.throwIfAborted();
		const page = await getPageForTargetId(opts);
		signal.throwIfAborted();
		let locator = page.locator(opts.selector ?? "body").first();
		if (!opts.selector) for (const selector of ["article", "main"]) {
			const candidate = page.locator(selector).first();
			const count = await candidate.count();
			signal.throwIfAborted();
			if (count) {
				locator = candidate;
				break;
			}
		}
		return await locator.innerText({
			timeout: Math.max(1, deadline - Date.now()),
			signal
		});
	};
	try {
		const text = await withTimeout(awaitActionWithAbort(read(), abortPromise), timeout, { createError: () => {
			const error = /* @__PURE__ */ new Error(`Page text extraction timed out after ${timeout}ms`);
			controller.abort(error);
			return error;
		} });
		return {
			text: text.slice(0, maxChars),
			truncated: text.length > maxChars
		};
	} finally {
		cleanup();
	}
}
/** Returns captured page errors, optionally clearing the per-page buffer. */
async function getPageErrorsViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	const state = ensurePageState(page);
	const errors = [...state.errors];
	if (opts.clear) state.errors = [];
	return { errors };
}
/** Returns captured requests, optionally filtering URLs/resource types and clearing. */
async function getNetworkRequestsViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	const state = ensurePageState(page);
	const raw = [...state.requests.values()];
	const filter = typeof opts.filter === "string" ? opts.filter.trim() : "";
	const requests = filter ? raw.filter((r) => r.url.includes(filter) || r.resourceType?.includes(filter)) : raw;
	if (opts.clear) {
		state.requests.clear();
		state.requestIds = /* @__PURE__ */ new WeakMap();
	}
	return { requests };
}
function consolePriority(level) {
	switch (level) {
		case "error": return 3;
		case "warn":
		case "warning": return 2;
		case "info":
		case "log": return 1;
		case "debug": return 0;
		default: return 1;
	}
}
/** Returns captured console messages at or above the requested priority level. */
async function getConsoleMessagesViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	const state = ensurePageState(page);
	if (!opts.level) return [...state.console];
	const min = consolePriority(opts.level);
	return state.console.filter((msg) => consolePriority(msg.type) >= min);
}
//#endregion
//#region extensions/browser/src/browser/pw-tools-core.interactions.actions.ts
async function highlightViaPlaywright(opts) {
	const page = await getRestoredPageForTarget(opts);
	const ref = requireRef(opts.ref);
	try {
		await refLocator(page, ref).highlight();
	} catch (err) {
		throw toFriendlyInteractionError(err, ref);
	}
}
async function clickViaPlaywright(opts) {
	const resolved = requireRefOrSelector(opts.ref, opts.selector);
	const page = opts.resolvedPage ?? await getRestoredPageForTarget(opts);
	if (opts.resolvedPage) {
		ensurePageState(page);
		restoreRoleRefsForTarget({
			cdpUrl: opts.cdpUrl,
			targetId: opts.targetId,
			page
		});
	}
	const { label, locator } = resolveInteractionElement(page, resolved);
	const timeout = resolveActInteractionTimeoutMs(opts.timeoutMs);
	await runCancellablePageInteraction(page, opts, async (signal) => {
		const delayMs = resolveBoundedDelayMs(opts.delayMs, "click delayMs", ACT_MAX_CLICK_DELAY_MS);
		if (delayMs > 0) {
			await locator.hover({
				timeout,
				signal
			});
			throwIfInteractionAborted(opts.signal);
			await sleepWithAbort(delayMs, opts.signal);
			if (opts.assertCurrent) {
				const assertion = assertInteractionCurrent(opts);
				if (assertion) await assertion;
			}
			throwIfInteractionAborted(opts.signal);
		}
		const clickOptions = {
			timeout,
			signal,
			button: opts.button,
			modifiers: opts.modifiers
		};
		await (opts.doubleClick ? locator.dblclick(clickOptions) : locator.click(clickOptions));
	}, label);
}
async function clickCoordsViaPlaywright(opts) {
	const page = await getRestoredPageForTarget(opts);
	await runGuardedPageInteraction(page, opts, async () => {
		await page.mouse.click(opts.x, opts.y, {
			button: opts.button,
			clickCount: opts.doubleClick ? 2 : 1,
			delay: resolveBoundedDelayMs(opts.delayMs, "clickCoords delayMs", ACT_MAX_CLICK_DELAY_MS)
		});
	});
}
async function runGuardedPageInteraction(page, opts, action) {
	const { abortPromise, cleanup } = createAbortPromiseWithListener(opts.signal);
	try {
		return await awaitNavigationGuardedInteraction({
			action,
			cdpUrl: opts.cdpUrl,
			page,
			...interactionNavigationPolicy(opts),
			targetId: opts.targetId,
			assertCurrent: opts.assertCurrent
		}, abortPromise, opts.signal, () => reconcileRemoteDialogAfterActionSettled(page, opts.signal));
	} finally {
		cleanup();
	}
}
function resolveInteractionElement(page, resolved) {
	return {
		label: resolved.ref ?? resolved.selector,
		locator: resolved.ref ? refLocator(page, requireRef(resolved.ref)) : page.locator(resolved.selector)
	};
}
async function hoverViaPlaywright(opts) {
	const resolved = requireRefOrSelector(opts.ref, opts.selector);
	const page = await getRestoredPageForTarget(opts);
	const { label, locator } = resolveInteractionElement(page, resolved);
	await runCancellablePageInteraction(page, opts, async (signal) => await locator.hover({
		timeout: resolveActInteractionTimeoutMs(opts.timeoutMs),
		signal
	}), label);
}
async function dragViaPlaywright(opts) {
	const resolvedStart = requireRefOrSelector(opts.startRef, opts.startSelector);
	const resolvedEnd = requireRefOrSelector(opts.endRef, opts.endSelector);
	const page = await getRestoredPageForTarget(opts);
	const { label: startLabel, locator: startLocator } = resolveInteractionElement(page, resolvedStart);
	const { label: endLabel, locator: endLocator } = resolveInteractionElement(page, resolvedEnd);
	await runCancellablePageInteraction(page, opts, async (signal) => await startLocator.dragTo(endLocator, {
		timeout: resolveActInteractionTimeoutMs(opts.timeoutMs),
		signal
	}), `${startLabel} -> ${endLabel}`);
}
async function selectOptionViaPlaywright(opts) {
	const resolved = requireRefOrSelector(opts.ref, opts.selector);
	if (!opts.values?.length) throw new Error("values are required");
	const page = await getRestoredPageForTarget(opts);
	const { label, locator } = resolveInteractionElement(page, resolved);
	await runCancellablePageInteraction(page, opts, async (signal) => {
		await locator.selectOption(opts.values, {
			timeout: resolveActInteractionTimeoutMs(opts.timeoutMs),
			signal
		});
	}, label);
}
async function pressKeyViaPlaywright(opts) {
	const key = normalizeOptionalString(opts.key) ?? "";
	if (!key) throw new Error("key is required");
	const page = await getPageForTargetId(opts);
	await runGuardedPageInteraction(page, opts, async () => {
		await page.keyboard.press(key, { delay: resolveNonNegativeIntegerOption(opts.delayMs, 0) });
	});
}
async function insertTextViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	await runGuardedPageInteraction(page, opts, async () => {
		try {
			await page.keyboard.insertText(opts.text);
		} catch {
			throw new Error("Unable to paste text into the browser. Focus an editable field and try again.");
		}
	});
}
async function typeViaPlaywright(opts) {
	const resolved = requireRefOrSelector(opts.ref, opts.selector);
	const text = opts.text ?? "";
	const page = await getRestoredPageForTarget(opts);
	const { label, locator } = resolveInteractionElement(page, resolved);
	const timeout = resolveActInteractionTimeoutMs(opts.timeoutMs);
	await runCancellablePageInteraction(page, opts, async (signal) => {
		if (opts.slowly) {
			await locator.click({
				timeout,
				signal
			});
			if (opts.assertCurrent) {
				const assertion = assertInteractionCurrent(opts);
				if (assertion) await assertion;
			}
			throwIfInteractionAborted(opts.signal);
			await locator.type(text, {
				timeout,
				signal,
				delay: 75
			});
		} else await locator.fill(text, {
			timeout,
			signal
		});
		if (opts.submit) {
			if (opts.assertCurrent) {
				const assertion = assertInteractionCurrent(opts);
				if (assertion) await assertion;
			}
			throwIfInteractionAborted(opts.signal);
			await locator.press("Enter", {
				timeout,
				signal
			});
		}
	}, label);
}
async function fillFormViaPlaywright(opts) {
	const page = await getRestoredPageForTarget(opts);
	const timeout = resolveActInteractionTimeoutMs(opts.timeoutMs);
	for (const field of opts.fields) {
		const ref = field.ref.trim();
		if (!ref) continue;
		const type = (field.type || "text").trim() || "text";
		const rawValue = field.value;
		const value = typeof rawValue === "string" ? rawValue : typeof rawValue === "number" || typeof rawValue === "boolean" ? String(rawValue) : "";
		const locator = refLocator(page, ref);
		await runCancellablePageInteraction(page, opts, async (signal) => {
			if (type === "checkbox" || type === "radio") {
				const checked = rawValue === true || rawValue === 1 || rawValue === "1" || rawValue === "true";
				await locator.setChecked(checked, {
					timeout,
					signal
				});
			} else await locator.fill(value, {
				timeout,
				signal
			});
		}, ref);
	}
}
async function evaluateViaPlaywright(opts) {
	const fnText = normalizeOptionalString(opts.fn) ?? "";
	if (!fnText) throw new Error("function is required");
	const fnSource = normalizeBrowserEvaluateFunctionSource(fnText, opts.ref ? { argumentName: "el" } : void 0);
	const page = await getRestoredPageForTarget(opts);
	const outerTimeout = normalizeTimeoutMs(opts.timeoutMs, 2e4);
	let evaluateTimeout = Math.max(1e3, Math.min(12e4, outerTimeout - 500));
	evaluateTimeout = Math.min(evaluateTimeout, outerTimeout);
	const signal = opts.signal;
	const { abortPromise, cleanup } = createAbortPromiseWithListener(signal, (reason) => {
		if (isBrowserObservedDialogBlockedError(reason)) return;
		forceDisconnectPlaywrightForTarget({
			cdpUrl: opts.cdpUrl,
			page,
			targetId: opts.targetId,
			ssrfPolicy: opts.ssrfPolicy
		}).catch(() => {});
	});
	if (signal?.aborted) throw signal.reason ?? /* @__PURE__ */ new Error("aborted");
	try {
		const navigationPolicy = interactionNavigationPolicy(opts);
		const reconcileRemoteDialog = () => reconcileRemoteDialogAfterActionSettled(page, signal);
		const evaluatorBody = `
        "use strict";
        var fnSource = args.fnSource, timeoutMs = args.timeoutMs;
        try {
          var candidate = eval("(" + fnSource + ")");
          if (typeof candidate !== "function") {
            throw new Error("evaluate source did not produce a function");
          }
          var result = candidate(${opts.ref ? "el" : ""});
          if (result && typeof result.then === "function") {
            return Promise.race([
              result,
              new Promise(function(_, reject) {
                setTimeout(function() { reject(new Error("evaluate timed out after " + timeoutMs + "ms")); }, timeoutMs);
              })
            ]);
          }
          return result;
        } catch (err) {
          throw new Error("Invalid evaluate function: " + (err && err.message ? err.message : String(err)));
        }
      `;
		const args = {
			fnSource,
			timeoutMs: evaluateTimeout
		};
		let action;
		if (opts.ref) {
			const locator = refLocator(page, opts.ref);
			const evaluate = new Function("el", "args", evaluatorBody);
			action = async () => await locator.evaluate(evaluate, args);
		} else {
			const evaluate = new Function("args", evaluatorBody);
			action = async () => await page.evaluate(evaluate, args);
		}
		return await awaitNavigationGuardedInteraction({
			action,
			cdpUrl: opts.cdpUrl,
			page,
			...navigationPolicy,
			targetId: opts.targetId,
			assertCurrent: opts.assertCurrent
		}, abortPromise, signal, reconcileRemoteDialog);
	} finally {
		cleanup();
	}
}
async function scrollIntoViewViaPlaywright(opts) {
	const resolved = requireRefOrSelector(opts.ref, opts.selector);
	const page = await getRestoredPageForTarget(opts);
	const timeout = normalizeTimeoutMs(opts.timeoutMs, 2e4);
	const { label, locator } = resolveInteractionElement(page, resolved);
	await runCancellablePageInteraction(page, opts, async (signal) => await locator.scrollIntoViewIfNeeded({
		timeout,
		signal
	}), label);
}
//#endregion
//#region extensions/browser/src/browser/pw-tools-core.state.ts
function resolvePageEmulationState(state) {
	return state.emulation ??= {};
}
function resolvePageEmulationSession(page, state) {
	const emulation = resolvePageEmulationState(state);
	if (emulation.session) return emulation.session;
	const pending = page.context().newCDPSession(page);
	emulation.session = pending;
	pending.catch(() => {
		if (emulation.session === pending) delete emulation.session;
	});
	return pending;
}
async function setViewportSizeOnPage(page, state, viewport, assertCurrent) {
	const emulation = state.emulation;
	if (emulation?.metricsOwner && (emulation.metricsOwner.viewport.width !== viewport.width || emulation.metricsOwner.viewport.height !== viewport.height)) {
		await emulation.metricsOwner.session.send("Emulation.clearDeviceMetricsOverride");
		delete emulation.metricsOwner;
	}
	const assertion = assertInteractionCurrent({ assertCurrent });
	if (assertion) await assertion;
	await page.setViewportSize(viewport);
}
async function runPageEmulationTransition(params) {
	params.signal?.throwIfAborted();
	const emulation = resolvePageEmulationState(params.state);
	const interrupted = emulation.transitionAbort ??= new AbortController();
	interrupted.signal.throwIfAborted();
	const signal = params.signal ? AbortSignal.any([params.signal, interrupted.signal]) : interrupted.signal;
	const { abortPromise, cleanup } = createAbortPromiseWithListener(signal);
	const transition = (emulation.transitionTail ?? Promise.resolve()).catch(() => {}).then(async () => {
		signal.throwIfAborted();
		const interrupt = () => interrupted.abort(/* @__PURE__ */ new Error("A previous screenshot or emulation action was cancelled but is still running. Retry when it finishes; if it remains stuck, close and reopen this tab."));
		params.signal?.addEventListener("abort", interrupt, { once: true });
		try {
			return await params.run();
		} finally {
			params.signal?.removeEventListener("abort", interrupt);
		}
	});
	const tail = transition.then(() => {}, () => {}).finally(() => {
		if (emulation.transitionTail === tail) {
			delete emulation.transitionTail;
			delete emulation.transitionAbort;
		}
	});
	emulation.transitionTail = tail;
	try {
		return await awaitActionWithAbort(transition, abortPromise);
	} finally {
		cleanup();
	}
}
async function setOfflineViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	if (opts.assertCurrent) await assertInteractionCurrent(opts);
	await page.context().setOffline(opts.offline);
}
async function setExtraHTTPHeadersViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	if (opts.assertCurrent) await assertInteractionCurrent(opts);
	await page.context().setExtraHTTPHeaders(opts.headers);
}
async function setHttpCredentialsViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	if (opts.assertCurrent) await assertInteractionCurrent(opts);
	if (opts.clear) {
		await page.context().setHTTPCredentials(null);
		return;
	}
	const username = opts.username ?? "";
	const password = opts.password ?? "";
	if (!username) throw new Error("username is required (or set clear=true)");
	await page.context().setHTTPCredentials({
		username,
		password
	});
}
async function setGeolocationViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	const context = page.context();
	if (opts.assertCurrent) await assertInteractionCurrent(opts);
	if (opts.clear) {
		await context.setGeolocation(null);
		await context.clearPermissions().catch(() => {});
		return;
	}
	if (typeof opts.latitude !== "number" || typeof opts.longitude !== "number") throw new Error("latitude and longitude are required (or set clear=true)");
	await context.setGeolocation({
		latitude: opts.latitude,
		longitude: opts.longitude,
		accuracy: typeof opts.accuracy === "number" ? opts.accuracy : void 0
	});
	const origin = normalizeOptionalString(opts.origin) || (() => {
		try {
			return new URL(page.url()).origin;
		} catch {
			return "";
		}
	})();
	if (origin) {
		if (opts.assertCurrent) await assertInteractionCurrent(opts);
		await context.grantPermissions(["geolocation"], { origin }).catch(() => {});
	}
}
async function emulateMediaViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	if (opts.assertCurrent) await assertInteractionCurrent(opts);
	await page.emulateMedia({ colorScheme: opts.colorScheme });
}
async function setLocaleViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	const pageState = ensurePageState(page);
	const locale = normalizeOptionalString(opts.locale) ?? "";
	if (!locale) throw new Error("locale is required");
	const session = await resolvePageEmulationSession(page, pageState);
	if (opts.assertCurrent) await assertInteractionCurrent(opts);
	try {
		await session.send("Emulation.setLocaleOverride", { locale });
	} catch (err) {
		if (!String(err).includes("Another locale override is already in effect")) throw err;
	}
}
async function setTimezoneViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	const pageState = ensurePageState(page);
	const timezoneId = normalizeOptionalString(opts.timezoneId) ?? "";
	if (!timezoneId) throw new Error("timezoneId is required");
	const session = await resolvePageEmulationSession(page, pageState);
	if (opts.assertCurrent) await assertInteractionCurrent(opts);
	try {
		await session.send("Emulation.setTimezoneOverride", { timezoneId });
	} catch (err) {
		const msg = String(err);
		if (msg.includes("Timezone override is already in effect")) return;
		if (msg.includes("Invalid timezone")) throw new Error(`Invalid timezone ID: ${timezoneId}`, { cause: err });
		throw err;
	}
}
async function setDeviceViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	const pageState = ensurePageState(page);
	const name = normalizeOptionalString(opts.name) ?? "";
	if (!name) throw new Error("device name is required");
	const descriptor = getPlaywrightCore().devices[name];
	if (!descriptor) throw new Error(`Unknown device "${name}".`);
	await runPageEmulationTransition({
		state: pageState,
		signal: opts.signal,
		run: async () => {
			if (opts.assertCurrent) {
				await assertInteractionCurrent(opts);
				opts.signal?.throwIfAborted();
			}
			const screen = descriptor.screen ?? descriptor.viewport;
			const isLandscape = screen.width > screen.height;
			await setViewportSizeOnPage(page, pageState, { ...descriptor.viewport });
			const session = await resolvePageEmulationSession(page, pageState);
			await session.send("Emulation.setUserAgentOverride", { userAgent: descriptor.userAgent });
			await session.send("Emulation.setDeviceMetricsOverride", {
				mobile: descriptor.isMobile,
				width: descriptor.viewport.width,
				height: descriptor.viewport.height,
				deviceScaleFactor: descriptor.deviceScaleFactor,
				screenWidth: screen.width,
				screenHeight: screen.height,
				screenOrientation: descriptor.isMobile && !isLandscape ? {
					angle: 0,
					type: "portraitPrimary"
				} : {
					angle: descriptor.isMobile ? 90 : 0,
					type: "landscapePrimary"
				}
			});
			const emulation = resolvePageEmulationState(pageState);
			emulation.metricsOwner = {
				session,
				viewport: { ...descriptor.viewport }
			};
			await session.send("Emulation.setTouchEmulationEnabled", { enabled: descriptor.hasTouch });
			emulation.touch = {
				session,
				enabled: descriptor.hasTouch
			};
		}
	});
}
//#endregion
//#region extensions/browser/src/browser/pw-tools-core.interactions.content.ts
const DEFAULT_UPLOAD_MIME_TYPE = "application/octet-stream";
const PLAYWRIGHT_FILE_PAYLOAD_SIZE_LIMIT_BYTES = 52428800;
async function toPlaywrightFilePayloads(paths) {
	const stats = await Promise.all(paths.map(async (filePath) => await fs.stat(filePath)));
	if (stats.reduce((size, stat) => size + stat.size, 0) >= PLAYWRIGHT_FILE_PAYLOAD_SIZE_LIMIT_BYTES) throw new Error("Cannot set buffer larger than 50Mb, please write it to a file and pass its path instead.");
	return await Promise.all(paths.map(async (filePath, index) => {
		const buffer = await fs.readFile(filePath);
		return {
			name: path.basename(filePath),
			mimeType: await detectMime({
				buffer,
				filePath
			}) ?? DEFAULT_UPLOAD_MIME_TYPE,
			buffer,
			lastModifiedMs: stats[index]?.mtimeMs
		};
	}));
}
async function resolvePlaywrightUploadFiles(opts) {
	const { abortPromise, cleanup } = createAbortPromiseWithListener(opts.signal);
	try {
		return await awaitActionWithAbort((async () => {
			const resolved = await resolveStrictExistingUploadPaths({ requestedPaths: opts.paths });
			if (!resolved.ok) throw new Error(resolved.error);
			return opts.ssrfPolicy && opts.browserFilesystemLocal !== true ? await toPlaywrightFilePayloads(resolved.paths) : resolved.paths;
		})(), abortPromise);
	} finally {
		cleanup();
	}
}
function createBrowserWaitPredicate(source) {
	return new Function("state", `
      if (state.document !== this.document) throw "Wait predicate document changed";
      state.predicate ??= (${source});
      var settled = state.settled;
      if (settled) {
        delete state.settled;
        if (settled.kind === "error") throw settled.error;
        if (!!settled.value) return true;
      }
      if (state.pending) return false;
      var predicate = state.predicate;
      var value = predicate();
      if (!value || typeof value.then !== "function") return !!value;
      state.pending = true;
      value.then(
        function(resolved) {
          state.settled = { kind: "value", value: resolved };
          delete state.pending;
        },
        function(error) {
          state.settled = { error: error, kind: "error" };
          delete state.pending;
        }
      );
      return false;
    `);
}
async function waitForViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	const timeout = resolveActWaitTimeoutMs(opts.timeoutMs);
	const fn = normalizeOptionalString(opts.fn) ?? "";
	const predicateSource = fn ? normalizeBrowserEvaluateFunctionSource(fn) : "";
	const predicate = fn ? createBrowserWaitPredicate(predicateSource) : void 0;
	const { abortPromise, cleanup } = createAbortPromiseWithListener(opts.signal);
	const reconcileRemoteDialog = () => reconcileRemoteDialogAfterActionSettled(page, opts.signal);
	const waitForStep = async (stepPromise) => {
		await awaitActionWithAbort(stepPromise, abortPromise, reconcileRemoteDialog);
	};
	const waitForSettledStep = async (stepPromise) => {
		await stepPromise;
		reconcileRemoteDialog();
		throwIfInteractionAborted(opts.signal);
	};
	const runWaitSequence = async (waitFor) => {
		if (typeof opts.timeMs === "number" && Number.isFinite(opts.timeMs)) await waitFor(page.waitForTimeout(resolveBoundedDelayMs(opts.timeMs, "wait timeMs", ACT_MAX_WAIT_TIME_MS)));
		if (opts.text) await waitFor(page.getByText(opts.text).first().waitFor({
			state: "visible",
			timeout
		}));
		if (opts.textGone) await waitFor(page.getByText(opts.textGone).first().waitFor({
			state: "hidden",
			timeout
		}));
		if (opts.selector) {
			const selector = normalizeOptionalString(opts.selector) ?? "";
			if (selector) await waitFor(page.locator(selector).first().waitFor({
				state: "visible",
				timeout
			}));
		}
		if (opts.url) {
			const url = normalizeOptionalString(opts.url) ?? "";
			if (url) await waitFor(page.waitForURL(url, { timeout }));
		}
		if (opts.loadState) await waitFor(page.waitForLoadState(opts.loadState, { timeout }));
		if (fn) {
			if (opts.assertCurrent) {
				const assertion = assertInteractionCurrent(opts);
				if (assertion) await assertion;
				throwIfInteractionAborted(opts.signal);
			}
			const documentHandle = await page.evaluateHandle(() => globalThis.document);
			try {
				if (opts.assertCurrent) {
					const assertion = assertInteractionCurrent(opts);
					if (assertion) await assertion;
				}
				throwIfInteractionAborted(opts.signal);
				await waitFor(page.waitForFunction(predicate, { document: documentHandle }, { timeout }));
			} finally {
				await documentHandle.dispose();
			}
		}
	};
	try {
		if (!fn) {
			await runWaitSequence(waitForStep);
			return;
		}
		await awaitNavigationGuardedInteraction({
			action: async () => await runWaitSequence(waitForSettledStep),
			cdpUrl: opts.cdpUrl,
			page,
			...interactionNavigationPolicy(opts),
			targetId: opts.targetId,
			assertCurrent: opts.assertCurrent
		}, abortPromise, opts.signal, reconcileRemoteDialog);
	} finally {
		cleanup();
	}
}
async function runScreenshotOperation(page, opts, run) {
	const controller = new AbortController();
	const signal = opts.signal ? AbortSignal.any([opts.signal, controller.signal]) : controller.signal;
	const timeoutMs = opts.timeoutMs ?? 2e4;
	return await withTimeout(runPageEmulationTransition({
		state: ensurePageState(page),
		signal,
		run: () => run(signal)
	}), timeoutMs, { createError: () => {
		const error = /* @__PURE__ */ new Error(`Screenshot via Playwright timed out after ${timeoutMs}ms`);
		controller.abort(error);
		return error;
	} });
}
function screenshotLocator(page, ref, element) {
	return ref ? refLocator(page, ref) : element ? page.locator(element).first() : void 0;
}
async function capturePageScreenshot(page, opts, locator) {
	opts.signal?.throwIfAborted();
	if (locator && opts.fullPage) throw new Error("fullPage is not supported for element screenshots");
	const type = opts.type ?? "png";
	const emulation = ensurePageState(page).emulation;
	const owner = emulation?.metricsOwner;
	const preparation = {
		timeout: opts.timeoutMs ?? 2e4,
		signal: opts.signal
	};
	const element = await locator?.elementHandle({ timeout: preparation.timeout });
	try {
		await element?.scrollIntoViewIfNeeded(preparation);
		opts.signal?.throwIfAborted();
		if (!owner) return { buffer: await (element ? element.screenshot({
			type,
			timeout: 0
		}) : page.screenshot({
			type,
			fullPage: Boolean(opts.fullPage),
			timeout: 0
		})) };
		const box = element ? await element.boundingBox() : void 0;
		if (locator && (!box || !box.width || !box.height)) throw new Error("Cannot take a screenshot of an element that is not visible or has no size");
		const metrics = await owner.session.send("Page.getLayoutMetrics");
		const visual = metrics.cssVisualViewport;
		let clip = {
			...metrics.cssContentSize,
			scale: 1
		};
		if (box) {
			const x = Math.floor(box.x + visual.pageX);
			const y = Math.floor(box.y + visual.pageY);
			clip = {
				x,
				y,
				width: Math.ceil(box.x + visual.pageX + box.width) - x,
				height: Math.ceil(box.y + visual.pageY + box.height) - y,
				scale: 1
			};
		} else if (!opts.fullPage) clip = {
			x: visual.pageX,
			y: visual.pageY,
			width: Math.ceil(owner.viewport.width / visual.scale),
			height: Math.ceil(owner.viewport.height / visual.scale),
			scale: visual.scale
		};
		const captureBeyondViewport = Boolean((opts.fullPage || locator) && (clip.width > owner.viewport.width || clip.height > owner.viewport.height));
		opts.signal?.throwIfAborted();
		const result = await owner.session.send("Page.captureScreenshot", {
			format: type,
			clip,
			captureBeyondViewport
		});
		return {
			buffer: Buffer.from(result.data, "base64"),
			clip
		};
	} finally {
		try {
			if (emulation?.touch) await emulation.touch.session.send("Emulation.setTouchEmulationEnabled", { enabled: emulation.touch.enabled });
		} finally {
			await element?.dispose();
		}
	}
}
async function takeScreenshotViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	return await runScreenshotOperation(page, opts, async (signal) => {
		restoreRoleRefsForTarget({
			cdpUrl: opts.cdpUrl,
			targetId: opts.targetId,
			page
		});
		const { buffer } = await capturePageScreenshot(page, {
			...opts,
			signal
		}, screenshotLocator(page, opts.ref, opts.element));
		return { buffer };
	});
}
async function screenshotWithLabelsViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	return await runScreenshotOperation(page, opts, (signal) => screenshotWithLabelsOnPage(page, {
		...opts,
		signal
	}));
}
async function screenshotWithLabelsOnPage(page, opts) {
	restoreRoleRefsForTarget({
		cdpUrl: opts.cdpUrl,
		targetId: opts.targetId,
		page
	});
	const maxLabels = typeof opts.maxLabels === "number" && Number.isFinite(opts.maxLabels) ? Math.max(1, Math.floor(opts.maxLabels)) : 150;
	const refKey = normalizeOptionalString(opts.ref) ?? void 0;
	const elementSelector = normalizeOptionalString(opts.element) ?? void 0;
	const locator = screenshotLocator(page, refKey, elementSelector);
	const space = opts.fullPage ? "fullpage" : refKey || elementSelector ? "element" : "viewport";
	const view = await page.evaluate((viewportWidth) => ({
		x: window.visualViewport.pageLeft,
		y: window.visualViewport.pageTop,
		width: window.visualViewport.width,
		height: window.visualViewport.height,
		nativeCaptureWidth: Math.floor((viewportWidth ?? window.innerWidth) / window.visualViewport.scale + .001),
		fullWidth: Math.max(document.body?.scrollWidth ?? 0, document.documentElement.scrollWidth, document.body?.offsetWidth ?? 0, document.documentElement.offsetWidth, document.body?.clientWidth ?? 0, document.documentElement.clientWidth)
	}), page.viewportSize()?.width);
	let elementRect;
	if (space === "element") {
		const box = await locator?.boundingBox().catch(() => null);
		if (!box) throw new Error(`screenshotWithLabelsViaPlaywright: element not found for ${refKey ? `ref="${refKey}"` : `selector="${elementSelector ?? ""}"`}`);
		elementRect = {
			x: box.x + view.x,
			y: box.y + view.y,
			width: box.width,
			height: box.height
		};
	}
	const refs = opts.refs ?? ensurePageState(page).roleRefs ?? {};
	const refKeys = Object.keys(refs);
	const inputs = [];
	let skippedRefs = 0;
	for (const ref of refKeys) {
		const refInfo = refs[ref];
		if (refInfo === void 0) continue;
		const target = refLocator(page, ref);
		if (space === "fullpage" && inputs.length >= maxLabels) {
			skippedRefs += 1;
			continue;
		}
		const box = await target.boundingBox().catch(() => null);
		if (!box) {
			skippedRefs += 1;
			continue;
		}
		inputs.push({
			ref,
			role: refInfo.role,
			name: refInfo.name,
			doc: {
				x: box.x + view.x,
				y: box.y + view.y,
				width: box.width,
				height: box.height
			}
		});
	}
	const origin = space === "element" ? elementRect : space === "viewport" ? view : {
		x: 0,
		y: 0
	};
	const plan = planAnnotations({
		inputs,
		space,
		scroll: origin,
		viewport: view,
		elementRect,
		maxLabels
	});
	try {
		opts.signal?.throwIfAborted();
		if (plan.overlayItems.length > 0) await page.evaluate(buildOverlayInjectionScript({
			items: plan.overlayItems,
			captureY: origin.y
		}));
		const capture = await capturePageScreenshot(page, opts, locator);
		const clip = capture.clip ?? (space === "viewport" ? {
			...view,
			width: view.nativeCaptureWidth
		} : {
			x: Math.floor(origin.x + .001),
			y: Math.floor(origin.y + .001),
			width: elementRect ? Math.ceil(elementRect.x + elementRect.width - .001) - Math.floor(origin.x + .001) : view.fullWidth
		});
		const image = await getImageMetadata(capture.buffer);
		if (!image) throw new Error("Cannot determine screenshot dimensions for label annotations");
		const scale = image.width / clip.width;
		return {
			buffer: capture.buffer,
			labels: plan.overlayItems.length,
			skipped: plan.skipped + skippedRefs,
			annotations: scaleAnnotations(plan.annotations, scale, scale, {
				x: clip.x - origin.x,
				y: clip.y - origin.y
			})
		};
	} finally {
		await page.evaluate(buildOverlayClearScript()).catch(() => {});
	}
}
async function setFileChooserFilesViaPlaywright(opts) {
	const resolvedFiles = await resolvePlaywrightUploadFiles(opts);
	await runCancellablePageInteraction(opts.page, opts, async (signal) => {
		await opts.fileChooser.setFiles(resolvedFiles, {
			timeout: opts.timeoutMs,
			signal
		});
	});
}
async function setInputFilesViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	restoreRoleRefsForTarget({
		cdpUrl: opts.cdpUrl,
		targetId: opts.targetId,
		page
	});
	if (!opts.paths.length) throw new Error("paths are required");
	const inputRef = normalizeOptionalString(opts.inputRef) ?? "";
	const element = normalizeOptionalString(opts.element) ?? "";
	if (inputRef && element) throw new Error("inputRef and element are mutually exclusive");
	if (!inputRef && !element) throw new Error("inputRef or element is required");
	const locator = inputRef ? refLocator(page, inputRef) : page.locator(element).first();
	const resolvedFiles = await resolvePlaywrightUploadFiles(opts);
	await runCancellablePageInteraction(page, opts, async (signal) => await locator.setInputFiles(resolvedFiles, {
		timeout: normalizeTimeoutMs(opts.timeoutMs, DEFAULT_BROWSER_DOWNLOAD_TIMEOUT_MS),
		signal
	}), inputRef || element);
}
//#endregion
//#region extensions/browser/src/browser/pw-snapshot-page.ts
function resolveSnapshotTimeoutMs(timeoutMs) {
	return Math.max(500, Math.min(6e4, Math.floor(parseFiniteNumber(timeoutMs) ?? 5e3)));
}
async function collectSnapshotUrls(page) {
	const urls = await page.evaluate(() => {
		const seen = /* @__PURE__ */ new Set();
		const out = [];
		for (const anchor of document.querySelectorAll("a[href]")) {
			const href = anchor instanceof HTMLAnchorElement ? anchor.href : "";
			if (!href || seen.has(href)) continue;
			const text = (anchor.textContent || anchor.getAttribute("aria-label") || "").replace(/\s+/g, " ").trim().slice(0, 121) || href;
			seen.add(href);
			out.push({
				text,
				url: href
			});
			if (out.length >= 100) break;
		}
		return out;
	}).catch(() => []);
	return Array.isArray(urls) ? urls.map((entry) => {
		entry.text = truncateUtf16Safe(entry.text, 120) || entry.url;
		return entry;
	}) : [];
}
async function prepareSnapshotPageViaPlaywright(opts) {
	const page = await getPageForTargetId({
		cdpUrl: opts.cdpUrl,
		targetId: opts.targetId
	});
	if (opts.ssrfPolicy) await assertPageNavigationCompletedSafely({
		cdpUrl: opts.cdpUrl,
		page,
		response: null,
		ssrfPolicy: opts.ssrfPolicy,
		targetId: opts.targetId
	});
	return page;
}
async function withSnapshotFrameGuard(opts) {
	let frameCurrent = true;
	const onFrameChanged = (frame) => {
		if (!opts.frame || frame === opts.frame) frameCurrent = false;
	};
	opts.page.on("framenavigated", onFrameChanged);
	opts.page.on("framedetached", onFrameChanged);
	const assertCurrent = () => {
		opts.signal?.throwIfAborted();
		opts.assertCurrent?.();
		if (!frameCurrent) throw new Error("Frame changed while its browser snapshot was being captured; retry.");
		if (opts.deadlineMs !== void 0 && performance.now() >= opts.deadlineMs) throw new Error("Browser snapshot capture timed out.");
	};
	try {
		assertCurrent();
		return await opts.run(assertCurrent);
	} finally {
		frameCurrent = false;
		opts.page.off("framenavigated", onFrameChanged);
		opts.page.off("framedetached", onFrameChanged);
	}
}
//#endregion
//#region extensions/browser/src/browser/pw-role-snapshot-capture.ts
async function finalizeRoleSnapshotViaPlaywright(params) {
	const snapshot = params.urls ? appendSnapshotUrls(params.built.snapshot, await collectSnapshotUrls(params.frame ?? params.page)) : params.built.snapshot;
	params.assertCurrent();
	const finalized = finalizeRoleSnapshot({
		snapshot,
		refs: params.built.refs,
		maxChars: params.maxChars,
		delta: params.delta
	});
	storeRoleRefsForTarget({
		page: params.page,
		cdpUrl: params.cdpUrl,
		targetId: params.targetId,
		refs: finalized.refs,
		...params.frameSelector ? { frameSelector: params.frameSelector } : {},
		...params.frame ? { frame: params.frame } : {},
		mode: params.mode
	});
	return finalized;
}
/** Captures a role-ref snapshot used by model-facing browser interaction tools. */
async function snapshotRoleViaPlaywright(opts) {
	const page = await prepareSnapshotPageViaPlaywright({
		cdpUrl: opts.cdpUrl,
		targetId: opts.targetId,
		ssrfPolicy: opts.ssrfPolicy
	});
	const ariaSnapshotTimeout = resolveSnapshotTimeoutMs(opts.timeoutMs);
	const captureDeadline = performance.now() + ariaSnapshotTimeout;
	if (opts.refsMode === "aria") {
		if (normalizeOptionalString(opts.selector) || normalizeOptionalString(opts.frameSelector)) throw new Error("refs=aria does not support selector/frame snapshots yet.");
		return await withSnapshotFrameGuard({
			page,
			signal: opts.signal,
			deadlineMs: captureDeadline,
			run: async (assertCurrent) => {
				const snapshot = await page.ariaSnapshot({
					mode: "ai",
					timeout: ariaSnapshotTimeout
				});
				const built = buildRoleSnapshotFromAiSnapshot(snapshot, opts.options);
				if (opts.options === void 0) built.snapshot = snapshot;
				return await finalizeRoleSnapshotViaPlaywright({
					page,
					cdpUrl: opts.cdpUrl,
					targetId: opts.targetId,
					assertCurrent,
					built,
					mode: "aria",
					urls: opts.urls,
					maxChars: opts.maxChars,
					delta: opts.delta
				});
			}
		});
	}
	const frameSelector = normalizeOptionalString(opts.frameSelector) ?? "";
	const selector = normalizeOptionalString(opts.selector) ?? "";
	const frameElement = frameSelector ? await page.locator(frameSelector).elementHandle({ timeout: ariaSnapshotTimeout }) : void 0;
	let frame;
	if (frameElement) try {
		frame = await frameElement.contentFrame() ?? void 0;
	} finally {
		await frameElement.dispose();
	}
	if (frameSelector && !frame) throw new Error("Frame was unavailable while its browser snapshot was being captured.");
	return await withSnapshotFrameGuard({
		page,
		frame: frame ?? page.mainFrame(),
		signal: opts.signal,
		deadlineMs: captureDeadline,
		run: async (assertCurrent) => {
			const snapshotScope = frame ?? page;
			const locator = snapshotScope.locator(selector || ":root");
			if (!(!selector || await withTimeout(locator.count(), ariaSnapshotTimeout, "Role snapshot selector") > 0)) return await finalizeRoleSnapshotViaPlaywright({
				...opts,
				page,
				frameSelector: frameSelector || void 0,
				frame,
				assertCurrent,
				built: {
					snapshot: opts.options?.interactive ? "(no interactive elements)" : "(empty)",
					refs: {}
				},
				mode: "role",
				urls: false
			});
			const remaining = () => {
				assertCurrent();
				return Math.max(1, Math.ceil(captureDeadline - performance.now()));
			};
			const root = await locator.elementHandle({ timeout: remaining() });
			if (!root) throw new Error("Snapshot root changed before native capture; retry.");
			let result;
			let captureOwnsRoot = false;
			try {
				result = await withPageScopedCdpClient({
					page,
					frame,
					timeoutMs: remaining(),
					fn: async (send) => {
						assertCurrent();
						captureOwnsRoot = true;
						try {
							return await withCdpSnapshotRoot({
								root,
								send,
								run: async (rootBackendNodeId) => {
									const captured = await snapshotRoleViaCdpSession({
										send,
										rootBackendNodeId,
										options: opts.options,
										urlEntries: opts.urls ? await collectSnapshotUrls(snapshotScope) : void 0,
										maxChars: opts.maxChars,
										delta: opts.delta,
										recurseIframes: false
									});
									remaining();
									const backendRefs = Object.entries(captured.refs).map(([ref, info]) => {
										if (!info.backendDOMNodeId) throw new Error("Snapshot control has no DOM identity; retry.");
										return {
											ref,
											backendDOMNodeId: info.backendDOMNodeId
										};
									});
									if ((await markBackendDomRefsOnPage({
										page,
										frame,
										send,
										rootBackendNodeId,
										refs: backendRefs,
										assertCurrent
									})).size !== backendRefs.length) throw new Error("Snapshot controls changed before refs were bound; retry.");
									remaining();
									const refs = Object.fromEntries(Object.entries(captured.refs).map(([ref, info]) => {
										const { backendDOMNodeId: _backend, ...metadata } = info;
										return [ref, {
											...metadata,
											domMarker: true
										}];
									}));
									return {
										...captured,
										refs
									};
								}
							});
						} finally {
							root.dispose().catch(() => {});
						}
					}
				});
			} finally {
				if (!captureOwnsRoot) root.dispose().catch(() => {});
			}
			remaining();
			storeRoleRefsForTarget({
				page,
				frame,
				frameSelector: frameSelector || void 0,
				cdpUrl: opts.cdpUrl,
				targetId: opts.targetId,
				refs: result.refs,
				mode: "role"
			});
			return result;
		}
	});
}
//#endregion
//#region extensions/browser/src/browser/pw-tools-core.snapshot.ts
/**
* Snapshot, navigation, viewport, close, and PDF helpers for Playwright-backed
* browser tools.
*/
function resolveViewportDimension(value, label) {
	const dimension = resolveIntegerOption(value, 1, { min: 1 });
	if (dimension > 8192) throw new Error(`viewport ${label} exceeds maximum of ${ACT_MAX_VIEWPORT_DIMENSION}`);
	return dimension;
}
function buildStoredAriaRefs(nodes) {
	const refs = {};
	const groups = /* @__PURE__ */ new Map();
	for (const node of nodes) {
		const role = normalizeLowercaseStringOrEmpty(node.role) || "unknown";
		const name = node.name.trim();
		const key = `${role}:${name}`;
		const group = groups.get(key);
		const nth = group?.count ?? 0;
		if (group) group.count += 1;
		else groups.set(key, {
			count: 1,
			firstRef: node.ref
		});
		refs[node.ref] = {
			role,
			name,
			nth,
			...typeof node.backendDOMNodeId === "number" ? { backendDOMNodeId: node.backendDOMNodeId } : {}
		};
	}
	for (const { count, firstRef } of groups.values()) if (count === 1 && firstRef) delete refs[firstRef]?.nth;
	return refs;
}
/** Publish raw or finalized snapshot refs into the Playwright action cache. */
async function storeSnapshotRefsViaPlaywright(opts) {
	const sourceRefs = opts.refs ?? buildStoredAriaRefs(opts.nodes ?? []);
	const page = opts.page ?? await getPageForTargetId({
		cdpUrl: opts.cdpUrl,
		targetId: opts.targetId
	});
	ensurePageState(page);
	const backendRefs = [];
	for (const [ref, info] of Object.entries(sourceRefs)) if (typeof info.backendDOMNodeId === "number") backendRefs.push({
		ref,
		backendDOMNodeId: info.backendDOMNodeId
	});
	await withSnapshotFrameGuard({
		page,
		frame: page.mainFrame(),
		signal: opts.signal,
		deadlineMs: opts.deadlineMs,
		assertCurrent: opts.assertCurrent,
		run: async (assertCurrent) => {
			if (opts.expectedDocumentIdentity && await readMainFrameDocumentIdentityForPage(page) !== opts.expectedDocumentIdentity) throw new Error("Frame changed while its browser snapshot refs were being published; retry.");
			await markBackendDomRefsOnPage({
				page,
				refs: backendRefs,
				assertCurrent
			});
			assertCurrent();
			const refs = Object.fromEntries(Object.entries(sourceRefs).map(([ref, info]) => {
				const { backendDOMNodeId, ...storedInfo } = info;
				if (typeof backendDOMNodeId === "number") storedInfo.domMarker = true;
				return [ref, storedInfo];
			}));
			storeRoleRefsForTarget({
				page,
				cdpUrl: opts.cdpUrl,
				targetId: opts.targetId,
				refs,
				mode: "role"
			});
		}
	});
}
/** Captures a raw accessibility tree snapshot and stores matching role refs. */
async function snapshotAriaViaPlaywright(opts) {
	const limit = resolveIntegerOption(opts.limit, 500, {
		min: 1,
		max: 2e3
	});
	const page = await prepareSnapshotPageViaPlaywright({
		cdpUrl: opts.cdpUrl,
		targetId: opts.targetId,
		ssrfPolicy: opts.ssrfPolicy
	});
	const ariaTimeoutMs = resolveSnapshotTimeoutMs(opts.timeoutMs);
	return await withSnapshotFrameGuard({
		page,
		frame: page.mainFrame(),
		signal: opts.signal,
		deadlineMs: performance.now() + ariaTimeoutMs,
		run: async (assertCurrent) => {
			const res = await withPageScopedCdpClient({
				page,
				timeoutMs: ariaTimeoutMs,
				fn: async (send) => {
					await send("Accessibility.enable").catch(() => {});
					return await send("Accessibility.getFullAXTree");
				}
			});
			assertCurrent();
			const nodes = Array.isArray(res?.nodes) ? res.nodes : [];
			const formatted = formatAriaSnapshot(nodes, limit);
			await storeSnapshotRefsViaPlaywright({
				cdpUrl: opts.cdpUrl,
				targetId: opts.targetId,
				nodes: formatted,
				page,
				signal: opts.signal,
				assertCurrent
			});
			return { nodes: formatted };
		}
	});
}
/** Navigates the target page while enforcing browser SSRF policy before and after load. */
async function navigateViaPlaywright(opts) {
	const isRetryableNavigateError = (err) => {
		const msg = typeof err === "string" ? err.toLowerCase() : err instanceof Error ? err.message.toLowerCase() : "";
		return msg.includes("frame has been detached") || msg.includes("target page, context or browser has been closed");
	};
	const url = normalizeOptionalString(opts.url) ?? "";
	if (!url) throw new Error("url is required");
	const navigationPolicy = withBrowserNavigationPolicy(opts.ssrfPolicy, { browserProxyMode: opts.browserProxyMode });
	await assertBrowserNavigationAllowed({
		url,
		...navigationPolicy
	});
	const timeout = resolveBrowserNavigationTimeoutMs(opts.timeoutMs);
	let currentTargetId = opts.targetId;
	let page = await getPageForTargetId(opts);
	let pageState = ensurePageState(page);
	const navigate = async () => await gotoPageWithNavigationGuard({
		cdpUrl: opts.cdpUrl,
		page,
		url,
		timeoutMs: timeout,
		ssrfPolicy: opts.ssrfPolicy,
		browserProxyMode: opts.browserProxyMode,
		targetId: currentTargetId,
		...opts.resolveOperationTarget ? { assertPageCurrent: async () => {
			if (await opts.resolveOperationTarget?.() !== currentTargetId) throw new BrowserTabNotFoundError({ input: currentTargetId });
			if (opts.assertCurrent) await opts.assertCurrent();
		} } : opts.assertCurrent ? { assertPageCurrent: opts.assertCurrent } : {}
	});
	const navigateWithDownloadCapture = async () => {
		const downloadCapture = createDownloadCaptureForPage(page, pageState, timeout, {
			mode: "passive",
			timeoutMessage: "Timeout waiting for navigation download",
			beforeSave: async (download) => {
				await assertBrowserNavigationResultAllowed({
					url: download.url || url,
					...navigationPolicy
				});
			}
		});
		downloadCapture.promise.catch(() => {});
		try {
			const response = await navigate();
			downloadCapture.cancel();
			return { response };
		} catch (err) {
			if (!isDownloadStartingNavigationError(err, url) || !downloadCapture.armed) {
				downloadCapture.cancel();
				throw err;
			}
			try {
				return {
					response: null,
					download: await downloadCapture.promise
				};
			} catch (downloadErr) {
				if (downloadErr instanceof Error && downloadErr.message === "Timeout waiting for navigation download") throw err;
				if (isPolicyDenyNavigationError(downloadErr)) await closeBlockedNavigationTarget({
					cdpUrl: opts.cdpUrl,
					page,
					targetId: currentTargetId
				});
				throw downloadErr;
			}
		}
	};
	let navigationResult;
	try {
		navigationResult = await navigateWithDownloadCapture();
	} catch (err) {
		if (isConnectionScopedPage(page) || !isRetryableNavigateError(err)) throw err;
		if (opts.relayReference) await closeRelayOperationConnection(opts.relayReference);
		else await forceDisconnectPlaywrightForTarget({
			cdpUrl: opts.cdpUrl,
			page,
			targetId: opts.targetId,
			ssrfPolicy: opts.ssrfPolicy
		}).catch(() => {});
		if (opts.resolveOperationTarget) {
			await connectBrowser(opts.cdpUrl, opts.ssrfPolicy, opts.relayReference);
			const replacementTargetId = await opts.resolveOperationTarget();
			if (!replacementTargetId) throw new BrowserTabNotFoundError({ input: currentTargetId });
			page = await getPageForTargetId({
				...opts,
				targetId: replacementTargetId
			});
			if (await opts.resolveOperationTarget() !== replacementTargetId) throw new BrowserTabNotFoundError({ input: currentTargetId });
			currentTargetId = replacementTargetId;
		} else page = await getPageForTargetId(opts);
		pageState = ensurePageState(page);
		navigationResult = await navigateWithDownloadCapture();
	}
	try {
		if (!navigationResult.download) await assertPageNavigationCompletedSafely({
			cdpUrl: opts.cdpUrl,
			page,
			response: navigationResult.response,
			ssrfPolicy: opts.ssrfPolicy,
			browserProxyMode: opts.browserProxyMode,
			targetId: currentTargetId
		});
	} catch (err) {
		if (isPolicyDenyNavigationError(err)) await closeBlockedNavigationTarget({
			cdpUrl: opts.cdpUrl,
			page,
			targetId: currentTargetId
		});
		throw err;
	}
	const finalUrl = navigationResult.download?.url || page.url();
	const targetId = (await pageTargetInfo(page).catch(() => null))?.targetId;
	return {
		url: finalUrl,
		...targetId ? { targetId } : {},
		...navigationResult.download ? { download: navigationResult.download } : {}
	};
}
/** Resizes the target page viewport within the browser action policy bounds. */
async function resizeViewportViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	const state = ensurePageState(page);
	const viewport = {
		width: resolveViewportDimension(opts.width, "width"),
		height: resolveViewportDimension(opts.height, "height")
	};
	await runPageEmulationTransition({
		state,
		signal: opts.signal,
		run: opts.assertCurrent ? async () => {
			const assertion = assertInteractionCurrent(opts);
			if (assertion) await assertion;
			opts.signal?.throwIfAborted();
			await setViewportSizeOnPage(page, state, viewport, opts.assertCurrent);
		} : () => setViewportSizeOnPage(page, state, viewport)
	});
}
/** Closes the target Playwright page. */
async function closePageViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	await closeResolvedPageViaPlaywright(page, {
		cdpUrl: opts.cdpUrl,
		assertCurrent: opts.assertCurrent ? () => assertInteractionCurrent(opts) : void 0
	});
}
/** Renders the target page to a PDF buffer. */
async function pdfViaPlaywright(opts) {
	return { buffer: await (await getPageForTargetId(opts)).pdf({ printBackground: true }) };
}
//#endregion
//#region extensions/browser/src/browser/pw-tools-core.interactions.execution.ts
const ACT_DOWNLOAD_MAX_DRAIN_MS = 1e3;
async function executeSingleAction(action, cdpUrl, targetId, evaluateEnabled, navigationPolicy = {}, depth = 0, signal, assertCurrent) {
	if (depth > 5) throw new Error(`Batch nesting depth exceeds maximum of 5`);
	const effectiveTargetId = action.targetId ?? targetId;
	const interaction = {
		cdpUrl,
		targetId: effectiveTargetId,
		...navigationPolicy,
		signal,
		assertCurrent
	};
	if (assertCurrent) {
		const assertion = assertInteractionCurrent(interaction);
		if (assertion) await assertion;
	}
	switch (action.kind) {
		case "click":
			await clickViaPlaywright({
				...action,
				...interaction,
				button: action.button,
				modifiers: action.modifiers
			});
			break;
		case "clickCoords":
			await clickCoordsViaPlaywright({
				...action,
				...interaction,
				button: action.button
			});
			break;
		case "type":
			await typeViaPlaywright({
				...action,
				...interaction
			});
			break;
		case "insertText":
			await insertTextViaPlaywright({
				...action,
				...interaction
			});
			break;
		case "press":
			await pressKeyViaPlaywright({
				...action,
				...interaction
			});
			break;
		case "hover":
			await hoverViaPlaywright({
				...action,
				...interaction
			});
			break;
		case "scrollIntoView":
			await scrollIntoViewViaPlaywright({
				...action,
				...interaction
			});
			break;
		case "drag":
			await dragViaPlaywright({
				...action,
				...interaction
			});
			break;
		case "select":
			await selectOptionViaPlaywright({
				...action,
				...interaction
			});
			break;
		case "fill":
			await fillFormViaPlaywright({
				...action,
				...interaction
			});
			break;
		case "resize":
			await resizeViewportViaPlaywright({
				cdpUrl,
				targetId: effectiveTargetId,
				width: action.width,
				height: action.height,
				signal,
				assertCurrent
			});
			break;
		case "wait":
			if (action.fn && !evaluateEnabled) throw new Error("wait --fn is disabled by config (browser.evaluateEnabled=false)");
			await waitForViaPlaywright({
				...action,
				...interaction
			});
			break;
		case "evaluate":
			if (!evaluateEnabled) throw new Error("act:evaluate is disabled by config (browser.evaluateEnabled=false)");
			return await evaluateViaPlaywright({
				...action,
				...interaction
			});
		case "close":
			await closePageViaPlaywright({
				cdpUrl,
				targetId: effectiveTargetId,
				assertCurrent
			});
			break;
		case "batch": {
			const failure = (await batchViaPlaywright({
				...action,
				...interaction,
				evaluateEnabled,
				depth: depth + 1
			})).results.find((result) => !result.ok);
			if (failure) throw new Error(failure.error);
			break;
		}
		default: throw new Error(`Unsupported batch action kind: ${action.kind}`);
	}
}
function actionUsesNavigationRequestGuard(action) {
	if (action.kind === "batch") return action.actions.some(actionUsesNavigationRequestGuard);
	return action.kind === "wait" ? Boolean(action.fn) : action.kind !== "close" && action.kind !== "resize";
}
function actionNeedsStandaloneDownloadGrace(action, navigationPolicy) {
	return actionUsesNavigationRequestGuard(action) && !hasInteractionNavigationPolicy(navigationPolicy);
}
async function executeActViaPlaywright(opts) {
	const navigationPolicy = interactionNavigationPolicy(opts);
	const page = await getPageForTargetId({
		cdpUrl: opts.cdpUrl,
		targetId: opts.targetId,
		ssrfPolicy: opts.ssrfPolicy
	});
	const withOperationTarget = async (payload) => {
		const targetId = (await pageTargetInfo(page).catch(() => null))?.targetId;
		return {
			...payload,
			...targetId ? { targetId } : {}
		};
	};
	const downloadCapture = beginActionDownloadCaptureOnPage(page, { beforeSave: async (download) => {
		if (!download.url) throw new Error("Action download URL is unavailable");
		await assertBrowserNavigationResultAllowed({
			url: download.url,
			...navigationPolicy
		});
	} });
	const downloadGraceMs = actionNeedsStandaloneDownloadGrace(opts.action, navigationPolicy) ? 250 : 0;
	const drainDownloads = async (firstEventGraceMs = downloadGraceMs) => await downloadCapture.drain({
		firstEventGraceMs,
		maxWaitMs: ACT_DOWNLOAD_MAX_DRAIN_MS,
		quietMs: 250
	});
	const dialogAbort = createObservedDialogAbortSignalForPage({
		page,
		parentSignal: opts.signal
	});
	try {
		if (opts.action.kind === "batch") {
			const batch = await batchViaPlaywright({
				cdpUrl: opts.cdpUrl,
				targetId: opts.targetId,
				page,
				...navigationPolicy,
				actions: opts.action.actions,
				stopOnError: opts.action.stopOnError,
				evaluateEnabled: opts.evaluateEnabled,
				signal: dialogAbort.signal,
				assertCurrent: opts.assertCurrent
			});
			const newDownloads = await drainDownloads();
			return await withOperationTarget({
				results: batch.results,
				...batch.aborted ? { aborted: batch.aborted } : {},
				...newDownloads ? { downloads: newDownloads } : {}
			});
		}
		const result = await executeSingleAction(opts.action, opts.cdpUrl, opts.targetId, opts.evaluateEnabled, navigationPolicy, 0, dialogAbort.signal, opts.assertCurrent);
		const newDownloads = await drainDownloads();
		if (opts.action.kind === "evaluate") return await withOperationTarget({
			result,
			...newDownloads ? { downloads: newDownloads } : {}
		});
		return await withOperationTarget(newDownloads ? { downloads: newDownloads } : {});
	} catch (err) {
		let failure = err;
		try {
			await drainDownloads(dialogAbort.signal.aborted && actionUsesNavigationRequestGuard(opts.action) ? 250 : downloadGraceMs);
		} catch (downloadErr) {
			failure = downloadErr;
		}
		if (isBrowserObservedDialogBlockedError(failure)) return await withOperationTarget({
			blockedByDialog: true,
			browserState: failure.browserState
		});
		if (isPolicyDenyNavigationError(failure) && !wasBrowserNavigationSourcePreservedAfterPolicyDenial(failure)) await quarantineBlockedNavigationTarget({
			cdpUrl: opts.cdpUrl,
			page,
			targetId: opts.targetId
		});
		throw failure;
	} finally {
		downloadCapture.dispose();
		dialogAbort.cleanup();
	}
}
async function batchViaPlaywright(opts) {
	const navigationPolicy = interactionNavigationPolicy(opts);
	const depth = opts.depth ?? 0;
	if (depth > 5) throw new Error(`Batch nesting depth exceeds maximum of 5`);
	if (opts.actions.length > 100) throw new Error(`Batch exceeds maximum of 100 actions`);
	const page = opts.page ?? await getPageForTargetId(opts);
	const results = [];
	const finishAborted = (reason, afterAction, url, skipped) => skipped === 0 ? { results } : {
		results,
		aborted: {
			reason,
			afterAction,
			url,
			skipped
		}
	};
	let mainFrameNavigations = 0;
	let navigationsAtLastDispatch = 0;
	const currentMainFrameUrl = () => page.mainFrame?.().url() ?? page.url();
	const onFrameNavigated = (frame) => {
		if (frame === page.mainFrame?.()) mainFrameNavigations += 1;
	};
	const finishNavigation = (afterAction, skipped) => {
		const url = currentMainFrameUrl();
		const lastResult = results.at(-1);
		if (lastResult) results[results.length - 1] = {
			...lastResult,
			navigated: true,
			url
		};
		return finishAborted("navigation", afterAction, url, skipped);
	};
	page.on?.("framenavigated", onFrameNavigated);
	try {
		for (const [index, action] of opts.actions.entries()) {
			if (opts.signal?.aborted) throw opts.signal.reason ?? /* @__PURE__ */ new Error("aborted");
			if (mainFrameNavigations > navigationsAtLastDispatch) return finishNavigation(index, opts.actions.length - index);
			if (page.isClosed?.()) return finishAborted("closed", index, currentMainFrameUrl(), opts.actions.length - index);
			navigationsAtLastDispatch = mainFrameNavigations;
			let result;
			try {
				await executeSingleAction(action, opts.cdpUrl, opts.targetId, opts.evaluateEnabled, navigationPolicy, depth, opts.signal, opts.assertCurrent);
				result = { ok: true };
			} catch (err) {
				if (isBrowserObservedDialogBlockedError(err) || err instanceof BrowserInteractionAuthorityError) throw err;
				if (isPolicyDenyNavigationError(err)) throw err;
				result = {
					ok: false,
					error: formatErrorMessage(err)
				};
			}
			results.push(result);
			if (page.isClosed?.()) return finishAborted("closed", index + 1, currentMainFrameUrl(), opts.actions.length - index - 1);
			if (mainFrameNavigations > navigationsAtLastDispatch) return finishNavigation(index + 1, opts.actions.length - index - 1);
			if (!result.ok && opts.stopOnError !== false) break;
		}
		return { results };
	} finally {
		page.off?.("framenavigated", onFrameNavigated);
	}
}
//#endregion
//#region extensions/browser/src/browser/pw-tools-core.downloads.ts
/**
* File chooser, dialog, and download helpers for Playwright-backed browser
* tools.
*/
async function dismissFileChooser(page) {
	await page.keyboard.press("Escape").catch(() => {});
}
const activeUploads = /* @__PURE__ */ new WeakMap();
function createExplicitDownloadCapture(params) {
	params.state.armIdDownload = bumpDownloadArmId();
	const armId = params.state.armIdDownload;
	return createDownloadCaptureForPage(params.page, params.state, params.timeoutMs, {
		mode: "explicit",
		outputPath: params.outPath,
		outputRoot: params.rootDir,
		signal: params.signal,
		cancelOnBeforeSaveError: () => params.state.armIdDownload === armId,
		beforeSave: async (download) => {
			if (params.state.armIdDownload !== armId) throw new Error("Download was superseded by another waiter");
			if (params.ssrfPolicy !== void 0 || params.browserProxyMode !== void 0) await assertBrowserNavigationResultAllowed({
				url: download.url,
				ssrfPolicy: params.ssrfPolicy,
				browserProxyMode: params.browserProxyMode,
				signal: params.signal
			});
			await params.beforeSave?.(download);
			if (params.state.armIdDownload !== armId) throw new Error("Download was superseded by another waiter");
		}
	});
}
function resolveImplicitDownloadRoot() {
	return path.join(resolvePreferredOpenClawTmpDir(), "downloads");
}
async function runFileUpload(opts) {
	opts.signal?.throwIfAborted();
	const atomic = opts.ref !== void 0;
	const armId = bumpUploadArmId();
	const timeout = normalizeTimeoutMs(opts.timeoutMs, DEFAULT_BROWSER_DOWNLOAD_TIMEOUT_MS);
	const controller = new AbortController();
	const signal = opts.signal ? AbortSignal.any([opts.signal, controller.signal]) : controller.signal;
	const { abortPromise, cleanup } = createAbortPromiseWithListener(signal);
	const armed = createDeferred();
	let started = false;
	let deadline = 0;
	let timer;
	const startDeadline = () => {
		deadline = Date.now() + timeout;
		timer = setTimeout(() => controller.abort(/* @__PURE__ */ new Error(`Timeout ${timeout}ms exceeded while completing file upload`)), timeout);
	};
	if (atomic) startDeadline();
	const completion = (async () => {
		const page = await awaitActionWithAbort(getPageForTargetId(opts), abortPromise);
		signal.throwIfAborted();
		if (opts.assertCurrent) {
			await assertInteractionCurrent(opts);
			signal.throwIfAborted();
		}
		const state = ensurePageState(page);
		if (state.armIdUpload > armId) throw new Error("File upload was superseded by another waiter");
		state.armIdUpload = armId;
		const previous = activeUploads.get(page);
		const execution = Promise.resolve().then(async () => {
			await previous?.settled;
			signal.throwIfAborted();
			if (opts.assertCurrent) {
				await assertInteractionCurrent(opts);
				signal.throwIfAborted();
			}
			started = true;
			if (!atomic) startDeadline();
			const chooser = page.waitForEvent("filechooser", {
				timeout: 0,
				signal
			});
			chooser.catch(() => {});
			const completionOptions = atomic ? opts : {
				...opts,
				assertCurrent: void 0
			};
			armed.resolve();
			let chooserAcquired = false;
			try {
				if (atomic) await clickViaPlaywright({
					...opts,
					ref: opts.ref,
					timeoutMs: Math.max(1, deadline - Date.now()),
					resolvedPage: page,
					signal
				});
				const fileChooser = await chooser;
				chooserAcquired = true;
				signal.throwIfAborted();
				let paths = opts.paths ?? [];
				if (!atomic) {
					const resolved = await awaitActionWithAbort(resolveStrictExistingUploadPaths({ requestedPaths: paths }), abortPromise);
					signal.throwIfAborted();
					if (!paths.length || !resolved.ok) {
						await dismissFileChooser(page);
						return;
					}
					paths = resolved.paths;
				}
				await setFileChooserFilesViaPlaywright({
					...completionOptions,
					page,
					fileChooser,
					paths,
					timeoutMs: Math.max(1, deadline - Date.now()),
					signal
				});
				signal.throwIfAborted();
			} catch (error) {
				controller.abort(error);
				if (chooserAcquired && error instanceof BrowserInteractionAuthorityError) await dismissFileChooser(page);
				if (error instanceof Error && error.name === "AbortError" && error.cause === signal.reason) signal.throwIfAborted();
				throw error;
			} finally {
				await chooser.catch(() => {});
			}
		});
		const active = {
			controller,
			settled: execution.then(() => {}, () => {})
		};
		activeUploads.set(page, active);
		previous?.controller.abort(/* @__PURE__ */ new Error("File upload was superseded by another waiter"));
		try {
			await execution;
		} finally {
			if (activeUploads.get(page) === active) activeUploads.delete(page);
		}
	})().finally(() => {
		clearTimeout(timer);
		cleanup();
	});
	completion.catch(() => {});
	try {
		await awaitActionWithAbort(atomic ? completion : Promise.race([armed.promise, completion]), abortPromise);
	} catch (error) {
		if (atomic && started) await completion;
		throw error;
	}
}
/** Arms the next page file chooser and fills it with strict existing paths. */
async function armFileUploadViaPlaywright(opts) {
	await runFileUpload(opts);
}
/** Clicks a ref and completes its file chooser as one request-owned operation. */
async function uploadViaPlaywright(opts) {
	await runFileUpload(opts);
}
/** Accepts or dismisses a pending dialog, or arms the next matching dialog response. */
async function armDialogViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	const timeout = normalizeTimeoutMs(opts.timeoutMs, DEFAULT_BROWSER_DOWNLOAD_TIMEOUT_MS);
	if (opts.assertCurrent) await assertInteractionCurrent(opts);
	try {
		await respondToObservedDialogOnPage({
			page,
			accept: opts.accept,
			closedBy: "agent",
			...opts.dialogId !== void 0 ? { dialogId: opts.dialogId } : {},
			...opts.promptText !== void 0 ? { promptText: opts.promptText } : {}
		});
		return;
	} catch (err) {
		if (opts.dialogId || err instanceof Error && !err.message.includes("No dialog is pending")) throw err;
	}
	if (opts.assertCurrent) await assertInteractionCurrent(opts);
	armObservedDialogResponseOnPage({
		page,
		accept: opts.accept,
		timeoutMs: timeout,
		...opts.promptText !== void 0 ? { promptText: opts.promptText } : {}
	});
}
/** Waits for the next page download and writes it under the configured output root. */
async function waitForDownloadViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	const state = ensurePageState(page);
	const timeout = normalizeTimeoutMs(opts.timeoutMs, 12e4);
	const navigationPolicy = interactionNavigationPolicy(opts);
	const policyDenial = new AbortController();
	const signal = opts.signal ? AbortSignal.any([opts.signal, policyDenial.signal]) : policyDenial.signal;
	const waitForCapture = async () => {
		if (opts.assertCurrent) await assertInteractionCurrent(opts);
		return await createExplicitDownloadCapture({
			page,
			state,
			timeoutMs: timeout,
			outPath: opts.path,
			rootDir: opts.path?.trim() ? opts.rootDir : opts.rootDir ?? resolveImplicitDownloadRoot(),
			signal,
			...navigationPolicy
		}).promise;
	};
	if (!hasInteractionNavigationPolicy(navigationPolicy)) return await waitForCapture();
	return await withPageNavigationRequestGuard({
		page,
		...navigationPolicy,
		onPolicyDenied: (event) => {
			if (event.state === "detected") policyDenial.abort(event.error instanceof Error ? event.error : new Error("Browser navigation blocked by policy", { cause: event.error }));
		},
		action: waitForCapture
	});
}
/** Clicks an element ref and saves the download triggered by that click. */
async function downloadViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	if (opts.assertCurrent) await assertInteractionCurrent(opts);
	const state = ensurePageState(page);
	restoreRoleRefsForTarget({
		cdpUrl: opts.cdpUrl,
		targetId: opts.targetId,
		page
	});
	const timeout = normalizeTimeoutMs(opts.timeoutMs, 12e4);
	const ref = requireRef(opts.ref);
	const outPath = opts.path?.trim() ?? "";
	if (!outPath) throw new Error("path is required");
	const capture = createExplicitDownloadCapture({
		page,
		state,
		timeoutMs: timeout,
		outPath,
		rootDir: opts.rootDir,
		signal: opts.signal,
		ssrfPolicy: opts.ssrfPolicy,
		browserProxyMode: opts.browserProxyMode
	});
	capture.promise.catch(() => {});
	try {
		const locator = refLocator(page, ref);
		await runCancellablePageInteraction(page, opts, async (signal) => await locator.click({
			timeout,
			signal
		}), ref);
	} catch (err) {
		capture.cancel();
		throw opts.signal?.aborted && opts.signal.reason instanceof Error ? opts.signal.reason : toAIFriendlyError(err, ref);
	}
	return await capture.promise;
}
/** Save the displayed document using its browser session without navigating its preview. */
async function downloadCurrentDocumentViaPlaywright(opts) {
	opts.signal?.throwIfAborted();
	const expectedUrl = opts.expectedUrl;
	const parsed = parseBrowserNavigationUrl(expectedUrl);
	if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new InvalidBrowserNavigationUrlError("Only HTTP(S) documents can be downloaded");
	const hasHostnameRestrictions = [...opts.ssrfPolicy?.hostnameAllowlist ?? [], ...opts.ssrfPolicy?.blockedHostnames ?? []].some((pattern) => {
		const normalized = normalizeHostname(pattern);
		return normalized.length > 0 && normalized !== "*";
	});
	if (!isPrivateNetworkAllowedByPolicy(opts.ssrfPolicy) || hasHostnameRestrictions) throw new InvalidBrowserNavigationUrlError("Current-document downloads are unavailable under this browser network policy because download redirects cannot be inspected");
	const operation = new AbortController();
	const signal = opts.signal ? AbortSignal.any([opts.signal, operation.signal]) : operation.signal;
	const { abortPromise, cleanup } = createAbortPromiseWithListener(signal);
	let page;
	const changedError = () => /* @__PURE__ */ new Error("The tab changed before its download completed. Try again.");
	const assertCurrentDocument = () => {
		signal.throwIfAborted();
		if (!page || page.isClosed() || page.url() !== expectedUrl) throw changedError();
	};
	const onClose = () => operation.abort(changedError());
	const onNavigation = (frame) => {
		if (frame === page?.mainFrame()) operation.abort(changedError());
	};
	try {
		page = await awaitActionWithAbort(getPageForTargetId(opts), abortPromise);
		page.on("close", onClose);
		page.on("framenavigated", onNavigation);
		assertCurrentDocument();
		await awaitActionWithAbort(assertBrowserNavigationAllowed({
			url: page.url(),
			ssrfPolicy: opts.ssrfPolicy,
			browserProxyMode: opts.browserProxyMode,
			signal
		}), abortPromise);
		assertCurrentDocument();
		if (opts.assertCurrent) {
			await assertInteractionCurrent(opts);
			assertCurrentDocument();
		}
		const timeout = normalizeTimeoutMs(opts.timeoutMs, DEFAULT_BROWSER_DOWNLOAD_TIMEOUT_MS);
		const capture = createExplicitDownloadCapture({
			page,
			state: ensurePageState(page),
			timeoutMs: timeout,
			rootDir: opts.rootDir ?? resolveImplicitDownloadRoot(),
			signal,
			beforeSave: async (download) => {
				try {
					assertCurrentDocument();
					await assertBrowserNavigationAllowed({
						url: download.url,
						ssrfPolicy: opts.ssrfPolicy,
						browserProxyMode: opts.browserProxyMode,
						signal
					});
					assertCurrentDocument();
				} catch (error) {
					operation.abort(error);
					throw error;
				}
			}
		});
		capture.promise.catch(() => {});
		try {
			assertCurrentDocument();
			const trigger = page.evaluate(({ expectedUrl: documentUrl, deadline }) => {
				if (location.href !== documentUrl || Date.now() >= deadline) throw new Error("The tab changed before its download started. Try again.");
				const anchor = document.createElement("a");
				anchor.href = location.href;
				anchor.download = "";
				anchor.click();
			}, {
				expectedUrl,
				deadline: Date.now() + timeout
			});
			await Promise.race([trigger, capture.promise]);
			return await capture.promise;
		} catch (error) {
			operation.abort(error);
			throw error;
		}
	} finally {
		page?.off("close", onClose);
		page?.off("framenavigated", onNavigation);
		cleanup();
	}
}
//#endregion
//#region extensions/browser/src/browser/pw-tools-core.responses.ts
/**
* Response-body retrieval for Playwright-backed browser tools.
*/
/** Waits for a response URL pattern and returns a bounded text body. */
async function responseBodyViaPlaywright(opts) {
	const pattern = normalizeOptionalString(opts.url) ?? "";
	if (!pattern) throw new Error("url is required");
	const maxChars = typeof opts.maxChars === "number" && Number.isFinite(opts.maxChars) ? Math.max(1, Math.min(5e6, Math.floor(opts.maxChars))) : 2e5;
	const timeout = normalizeTimeoutMs(opts.timeoutMs, 2e4);
	const maxBytes = maxChars * 4;
	opts.signal?.throwIfAborted();
	const page = await getPageForTargetId(opts);
	opts.signal?.throwIfAborted();
	let cleanup;
	const promise = new Promise((resolve, reject) => {
		let matched = false;
		const handler = (response) => {
			if (matched || !matchBrowserUrlPattern(pattern, response.url())) return;
			matched = true;
			page.off("response", handler);
			response.body().then((buffer) => resolve({
				response,
				buffer
			}), (error) => reject(new Error(`Failed to read response body for "${response.url()}": ${String(error)}`, { cause: error })));
		};
		const onAbort = () => reject(toErrorObject(opts.signal?.reason, "Response request aborted."));
		const onClose = () => reject(/* @__PURE__ */ new Error("Page closed before response body was available."));
		const timer = setTimeout(() => {
			reject(/* @__PURE__ */ new Error(matched ? `Response body timed out after ${timeout}ms for url pattern "${pattern}".` : `Response not found for url pattern "${pattern}". Run 'openclaw browser requests' to inspect recent network activity.`));
		}, timeout);
		cleanup = () => {
			clearTimeout(timer);
			page.off("response", handler);
			page.off("close", onClose);
			opts.signal?.removeEventListener("abort", onAbort);
		};
		page.on("response", handler);
		page.on("close", onClose);
		opts.signal?.addEventListener("abort", onAbort, { once: true });
	});
	try {
		const { response, buffer } = await promise;
		const bodyText = new TextDecoder("utf-8").decode(buffer.subarray(0, maxBytes));
		const body = bodyText.length > maxChars ? truncateUtf16Safe(bodyText, maxChars) : bodyText;
		return {
			url: response.url(),
			status: response.status(),
			headers: response.headers(),
			body,
			truncated: buffer.byteLength > maxBytes || bodyText.length > maxChars ? true : void 0
		};
	} finally {
		cleanup();
	}
}
//#endregion
//#region extensions/browser/src/browser/pw-tools-core.storage.ts
async function cookiesGetViaPlaywright(opts) {
	return { cookies: await (await getPageForTargetId(opts)).context().cookies() };
}
async function cookiesSetViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	const cookie = opts.cookie;
	if (!cookie.name || cookie.value === void 0) throw new Error("cookie name and value are required");
	const hasUrl = typeof cookie.url === "string" && cookie.url.trim();
	const hasDomainPath = typeof cookie.domain === "string" && cookie.domain.trim() && typeof cookie.path === "string" && cookie.path.trim();
	if (!hasUrl && !hasDomainPath) throw new Error("cookie requires url, or domain+path");
	if (opts.assertCurrent) await assertInteractionCurrent(opts);
	await page.context().addCookies([cookie]);
}
/**
* Add cookies in bounded batches on one browser context. On a batch error, retry
* that batch cookie-by-cookie so one cookie Playwright rejects neither drops the
* whole batch nor aborts the import. Returns the count actually added so callers
* can report rejects instead of leaving an ambiguous partial write.
*/
async function cookiesSetManyViaPlaywright(opts) {
	opts.signal?.throwIfAborted();
	const context = (await getPageForTargetId(opts)).context();
	let added = 0;
	for (let index = 0; index < opts.cookies.length; index += 500) {
		opts.signal?.throwIfAborted();
		const batch = opts.cookies.slice(index, index + 500);
		if (opts.assertCurrent) {
			await assertInteractionCurrent(opts);
			opts.signal?.throwIfAborted();
		}
		try {
			await context.addCookies(batch);
			added += batch.length;
		} catch {
			for (const cookie of batch) {
				opts.signal?.throwIfAborted();
				if (opts.assertCurrent) {
					await assertInteractionCurrent(opts);
					opts.signal?.throwIfAborted();
				}
				try {
					await context.addCookies([cookie]);
					added += 1;
				} catch {}
			}
		}
	}
	opts.signal?.throwIfAborted();
	return { added };
}
async function cookiesClearViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	if (opts.assertCurrent) await assertInteractionCurrent(opts);
	await page.context().clearCookies();
}
async function storageGetViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	const kind = opts.kind;
	const key = readStringValue(opts.key);
	const entries = await page.evaluate(({ kind: kind2, key: key2 }) => {
		const store = kind2 === "session" ? window.sessionStorage : window.localStorage;
		if (key2) {
			const value = store.getItem(key2);
			return value === null ? [] : [[key2, value]];
		}
		const out = [];
		for (let i = 0; i < store.length; i += 1) {
			const k = store.key(i);
			if (k === null) continue;
			const v = store.getItem(k);
			if (v !== null) out.push([k, v]);
		}
		return out;
	}, {
		kind,
		key
	});
	return { values: Object.fromEntries(entries) };
}
async function storageSetViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	const key = opts.key;
	if (!key) throw new Error("key is required");
	if (opts.assertCurrent) await assertInteractionCurrent(opts);
	await page.evaluate(({ kind, key: k, value }) => {
		(kind === "session" ? window.sessionStorage : window.localStorage).setItem(k, value);
	}, {
		kind: opts.kind,
		key,
		value: opts.value
	});
}
async function storageClearViaPlaywright(opts) {
	const page = await getPageForTargetId(opts);
	if (opts.assertCurrent) await assertInteractionCurrent(opts);
	await page.evaluate(({ kind }) => {
		(kind === "session" ? window.sessionStorage : window.localStorage).clear();
	}, { kind: opts.kind });
}
//#endregion
//#region extensions/browser/src/browser/pw-tools-core.trace.ts
/**
* Playwright trace lifecycle helpers for Browser plugin diagnostics.
*/
/** Starts Playwright tracing for the target page context. */
async function traceStartViaPlaywright(opts) {
	const context = (await getPageForTargetId(opts)).context();
	const ctxState = ensureContextState(context);
	if (ctxState.traceActive) throw new Error("Trace already running. Stop the current trace before starting a new one.");
	await context.tracing.start({
		screenshots: opts.screenshots ?? true,
		snapshots: opts.snapshots ?? true,
		sources: opts.sources ?? false
	});
	ctxState.traceActive = true;
}
/** Stops Playwright tracing and returns the committed trace zip path. */
async function traceStopViaPlaywright(opts) {
	const context = (await getPageForTargetId(opts)).context();
	const ctxState = ensureContextState(context);
	if (!ctxState.traceActive) throw new Error("No active trace. Start a trace before stopping it.");
	return await writeExternalFileWithinOutputRoot({
		rootDir: DEFAULT_TRACE_DIR,
		path: opts.path,
		write: async (tempPath) => {
			await context.tracing.stop({ path: tempPath });
			ctxState.traceActive = false;
		}
	});
}
//#endregion
//#region extensions/browser/src/browser/pw-ai.ts
/** Playwright-backed browser helpers loaded as one optional runtime object. */
const pwAi = {
	downloadCurrentDocumentViaPlaywright,
	closePageByTargetIdViaPlaywright,
	closePlaywrightBrowserConnection,
	retirePlaywrightBrowserConnectionExact,
	createPageViaPlaywright,
	ensurePageState,
	forceDisconnectPlaywrightForTarget,
	focusPageByTargetIdViaPlaywright,
	createObservedDialogAbortSignalForPage,
	getObservedBrowserStateForPage,
	getObservedBrowserStateViaPlaywright,
	getDocumentIdentitiesViaPlaywright,
	getPageForTargetId,
	hasCachedPlaywrightBrowserConnection,
	isBrowserObservedDialogBlockedError,
	listPagesViaPlaywright,
	markObservedDialogsHandledRemotelyForPage,
	refLocator,
	respondToObservedDialogOnPage,
	armDialogViaPlaywright,
	armFileUploadViaPlaywright,
	batchViaPlaywright,
	clickViaPlaywright,
	closePageViaPlaywright,
	cookiesClearViaPlaywright,
	cookiesGetViaPlaywright,
	cookiesSetManyViaPlaywright,
	cookiesSetViaPlaywright,
	downloadViaPlaywright,
	dragViaPlaywright,
	emulateMediaViaPlaywright,
	evaluateViaPlaywright,
	executeActViaPlaywright,
	fillFormViaPlaywright,
	getConsoleMessagesViaPlaywright,
	getNetworkRequestsViaPlaywright,
	getPageErrorsViaPlaywright,
	getPageTextViaPlaywright,
	highlightViaPlaywright,
	hoverViaPlaywright,
	navigateViaPlaywright,
	pdfViaPlaywright,
	pressKeyViaPlaywright,
	resizeViewportViaPlaywright,
	responseBodyViaPlaywright,
	scrollIntoViewViaPlaywright,
	selectOptionViaPlaywright,
	setDeviceViaPlaywright,
	setExtraHTTPHeadersViaPlaywright,
	setGeolocationViaPlaywright,
	setHttpCredentialsViaPlaywright,
	setInputFilesViaPlaywright,
	setLocaleViaPlaywright,
	setOfflineViaPlaywright,
	setTimezoneViaPlaywright,
	snapshotAriaViaPlaywright,
	snapshotRoleViaPlaywright,
	storeSnapshotRefsViaPlaywright,
	screenshotWithLabelsViaPlaywright,
	storageClearViaPlaywright,
	storageGetViaPlaywright,
	storageSetViaPlaywright,
	takeScreenshotViaPlaywright,
	traceStartViaPlaywright,
	traceStopViaPlaywright,
	typeViaPlaywright,
	uploadViaPlaywright,
	waitForDownloadViaPlaywright,
	waitForViaPlaywright
};
//#endregion
export { pwAi };