| |
| |
| |
|
|
|
|
| import { z } from 'zod';
|
| import { browserManager } from '../browser.js';
|
|
|
|
|
| export const navigateSchema = z.object({
|
| url: z.string().url().describe('The URL to navigate to'),
|
| });
|
|
|
| export const screenshotSchema = z.object({
|
| fullPage: z.boolean().optional().default(false).describe('Whether to capture the full page'),
|
| });
|
|
|
| export const clickSchema = z.object({
|
| selector: z.string().describe('CSS selector of the element to click'),
|
| });
|
|
|
| export const typeSchema = z.object({
|
| selector: z.string().describe('CSS selector of the input field'),
|
| text: z.string().describe('Text to type'),
|
| });
|
|
|
| export const extractTextSchema = z.object({
|
| selector: z.string().optional().describe('CSS selector to extract text from (default: body)'),
|
| });
|
|
|
| export const executeJsSchema = z.object({
|
| script: z.string().describe('JavaScript code to execute on the page'),
|
| });
|
|
|
| export const waitForSchema = z.object({
|
| selector: z.string().describe('CSS selector to wait for'),
|
| timeout: z.number().optional().default(5000).describe('Timeout in milliseconds'),
|
| });
|
|
|
| |
| |
|
|
| export async function navigate(url: string): Promise<{ success: boolean; title?: string; error?: string }> {
|
| try {
|
| const page = await browserManager.getPage();
|
| await page.goto(url);
|
| await page.waitForLoadState('networkidle');
|
| const title = await page.title();
|
| return { success: true, title };
|
| } catch (error) {
|
| return { success: false, error: String(error) };
|
| }
|
| }
|
|
|
| |
| |
|
|
| export async function screenshot(fullPage: boolean = false): Promise<{
|
| success: boolean;
|
| base64?: string;
|
| error?: string
|
| }> {
|
| try {
|
| const page = await browserManager.getPage();
|
| const buffer = await page.screenshot({ fullPage });
|
| const base64 = buffer.toString('base64');
|
| return { success: true, base64 };
|
| } catch (error) {
|
| return { success: false, error: String(error) };
|
| }
|
| }
|
|
|
| |
| |
|
|
| export async function click(selector: string): Promise<{ success: boolean; error?: string }> {
|
| try {
|
| const page = await browserManager.getPage();
|
| await page.click(selector);
|
| return { success: true };
|
| } catch (error) {
|
| return { success: false, error: String(error) };
|
| }
|
| }
|
|
|
| |
| |
|
|
| export async function type(selector: string, text: string): Promise<{ success: boolean; error?: string }> {
|
| try {
|
| const page = await browserManager.getPage();
|
| await page.fill(selector, text);
|
| return { success: true };
|
| } catch (error) {
|
| return { success: false, error: String(error) };
|
| }
|
| }
|
|
|
| |
| |
|
|
| export async function extractText(selector?: string): Promise<{
|
| success: boolean;
|
| text?: string;
|
| error?: string
|
| }> {
|
| try {
|
| const page = await browserManager.getPage();
|
| const element = selector ? await page.$(selector) : await page.$('body');
|
|
|
| if (!element) {
|
| return { success: false, error: `Element not found: ${selector || 'body'}` };
|
| }
|
|
|
| const text = await element.textContent();
|
| return { success: true, text: text?.trim() || '' };
|
| } catch (error) {
|
| return { success: false, error: String(error) };
|
| }
|
| }
|
|
|
| |
| |
|
|
| export async function executeJs(script: string): Promise<{
|
| success: boolean;
|
| result?: unknown;
|
| error?: string
|
| }> {
|
| try {
|
| const page = await browserManager.getPage();
|
| const result = await page.evaluate(script);
|
| return { success: true, result };
|
| } catch (error) {
|
| return { success: false, error: String(error) };
|
| }
|
| }
|
|
|
| |
| |
|
|
| export async function waitFor(selector: string, timeout: number = 5000): Promise<{
|
| success: boolean;
|
| error?: string
|
| }> {
|
| try {
|
| const page = await browserManager.getPage();
|
| await page.waitForSelector(selector, { timeout });
|
| return { success: true };
|
| } catch (error) {
|
| return { success: false, error: String(error) };
|
| }
|
| }
|
|
|
| |
| |
|
|
| export async function getCurrentUrl(): Promise<{ url: string; error?: string }> {
|
| try {
|
| const page = await browserManager.getPage();
|
| return { url: page.url() };
|
| } catch (error) {
|
| return { url: '', error: String(error) };
|
| }
|
| }
|
|
|
| |
| |
|
|
| export async function newTab(url?: string): Promise<{ index: number; success: boolean; error?: string }> {
|
| try {
|
| const page = await browserManager.getPage();
|
| const context = page.context();
|
| const newPage = await context.newPage();
|
|
|
|
|
| await browserManager.setActivePage(newPage);
|
|
|
| if (url) {
|
| await newPage.goto(url);
|
| await newPage.waitForLoadState('networkidle');
|
| }
|
|
|
| const index = context.pages().length - 1;
|
| return { index, success: true };
|
| } catch (error) {
|
| return { index: -1, success: false, error: String(error) };
|
| }
|
| }
|
|
|
| |
| |
|
|
| export async function closeTab(): Promise<{ success: boolean; error?: string }> {
|
| try {
|
| const page = await browserManager.getPage();
|
| const context = page.context();
|
| const pages = context.pages();
|
|
|
| if (pages.length <= 1) {
|
| return { success: false, error: 'Cannot close the last remaining tab' };
|
| }
|
|
|
| await page.close();
|
|
|
|
|
| const remainingPages = context.pages();
|
| await browserManager.setActivePage(remainingPages[remainingPages.length - 1]);
|
|
|
| return { success: true };
|
| } catch (error) {
|
| return { success: false, error: String(error) };
|
| }
|
| }
|
|
|
| |
| |
|
|
| export async function listTabs(): Promise<{
|
| tabs: { title: string; url: string; index: number }[];
|
| error?: string
|
| }> {
|
| try {
|
| const page = await browserManager.getPage();
|
| const context = page.context();
|
| const pages = context.pages();
|
|
|
| const tabs = await Promise.all(pages.map(async (p, i) => ({
|
| title: await p.title(),
|
| url: p.url(),
|
| index: i
|
| })));
|
|
|
| return { tabs };
|
| } catch (error) {
|
| return { tabs: [], error: String(error) };
|
| }
|
| }
|
|
|
| |
| |
|
|
| export async function switchTab(index: number): Promise<{ success: boolean; error?: string }> {
|
| try {
|
| const page = await browserManager.getPage();
|
| const context = page.context();
|
| const pages = context.pages();
|
|
|
| if (index < 0 || index >= pages.length) {
|
| return { success: false, error: `Invalid tab index: ${index}` };
|
| }
|
|
|
|
|
|
|
| await browserManager.setActivePage(pages[index]);
|
|
|
| return { success: true };
|
| } catch (error) {
|
| return { success: false, error: String(error) };
|
| }
|
| }
|
|
|
| |
| |
|
|
| export async function getAccessibilitySnapshot(): Promise<{
|
| success: boolean;
|
| snapshot?: unknown;
|
| error?: string
|
| }> {
|
| try {
|
| const page = await browserManager.getPage();
|
|
|
| const bodyHandle = await page.$('body');
|
| if (!bodyHandle) {
|
| return { success: false, error: 'Could not get page body' };
|
| }
|
|
|
|
|
| const snapshot = await page.evaluate(() => {
|
| const getAccessibleTree = (element: Element, depth = 0): object | null => {
|
| if (depth > 5) return null;
|
|
|
| const role = element.getAttribute('role') || element.tagName.toLowerCase();
|
| const label = element.getAttribute('aria-label') ||
|
| element.getAttribute('title') ||
|
| (element as HTMLElement).innerText?.slice(0, 100);
|
|
|
| const children: object[] = [];
|
| for (const child of element.children) {
|
| const childTree = getAccessibleTree(child as Element, depth + 1);
|
| if (childTree) children.push(childTree);
|
| }
|
|
|
| return {
|
| role,
|
| name: label || undefined,
|
| children: children.length > 0 ? children : undefined,
|
| };
|
| };
|
|
|
| return getAccessibleTree(document.body);
|
| });
|
|
|
| return { success: true, snapshot };
|
| } catch (error) {
|
| return { success: false, error: String(error) };
|
| }
|
| }
|
|
|