| | |
| | |
| | |
| |
|
| |
|
| | 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) };
|
| | }
|
| | }
|
| |
|