/** * Write commands — navigate and interact with pages (side effects) * * goto, back, forward, reload, click, fill, select, hover, type, * press, scroll, wait, viewport, cookie, header, useragent */ import type { BrowserManager } from './browser-manager'; export async function handleWriteCommand( command: string, args: string[], bm: BrowserManager ): Promise { const page = bm.getPage(); switch (command) { case 'goto': { const url = args[0]; if (!url) throw new Error('Usage: browse goto '); const response = await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 15000 }); const status = response?.status() || 'unknown'; return `Navigated to ${url} (${status})`; } case 'back': { await page.goBack({ waitUntil: 'domcontentloaded', timeout: 15000 }); return `Back → ${page.url()}`; } case 'forward': { await page.goForward({ waitUntil: 'domcontentloaded', timeout: 15000 }); return `Forward → ${page.url()}`; } case 'reload': { await page.reload({ waitUntil: 'domcontentloaded', timeout: 15000 }); return `Reloaded ${page.url()}`; } case 'click': { const selector = args[0]; if (!selector) throw new Error('Usage: browse click '); const resolved = bm.resolveRef(selector); if ('locator' in resolved) { await resolved.locator.click({ timeout: 5000 }); } else { await page.click(resolved.selector, { timeout: 5000 }); } // Wait briefly for any navigation/DOM update await page.waitForLoadState('domcontentloaded').catch(() => {}); return `Clicked ${selector} → now at ${page.url()}`; } case 'fill': { const [selector, ...valueParts] = args; const value = valueParts.join(' '); if (!selector || !value) throw new Error('Usage: browse fill '); const resolved = bm.resolveRef(selector); if ('locator' in resolved) { await resolved.locator.fill(value, { timeout: 5000 }); } else { await page.fill(resolved.selector, value, { timeout: 5000 }); } return `Filled ${selector}`; } case 'select': { const [selector, ...valueParts] = args; const value = valueParts.join(' '); if (!selector || !value) throw new Error('Usage: browse select '); const resolved = bm.resolveRef(selector); if ('locator' in resolved) { await resolved.locator.selectOption(value, { timeout: 5000 }); } else { await page.selectOption(resolved.selector, value, { timeout: 5000 }); } return `Selected "${value}" in ${selector}`; } case 'hover': { const selector = args[0]; if (!selector) throw new Error('Usage: browse hover '); const resolved = bm.resolveRef(selector); if ('locator' in resolved) { await resolved.locator.hover({ timeout: 5000 }); } else { await page.hover(resolved.selector, { timeout: 5000 }); } return `Hovered ${selector}`; } case 'type': { const text = args.join(' '); if (!text) throw new Error('Usage: browse type '); await page.keyboard.type(text); return `Typed "${text}"`; } case 'press': { const key = args[0]; if (!key) throw new Error('Usage: browse press (e.g., Enter, Tab, Escape)'); await page.keyboard.press(key); return `Pressed ${key}`; } case 'scroll': { const selector = args[0]; if (selector) { const resolved = bm.resolveRef(selector); if ('locator' in resolved) { await resolved.locator.scrollIntoViewIfNeeded({ timeout: 5000 }); } else { await page.locator(resolved.selector).scrollIntoViewIfNeeded({ timeout: 5000 }); } return `Scrolled ${selector} into view`; } await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); return 'Scrolled to bottom'; } case 'wait': { const selector = args[0]; if (!selector) throw new Error('Usage: browse wait '); const timeout = args[1] ? parseInt(args[1], 10) : 15000; const resolved = bm.resolveRef(selector); if ('locator' in resolved) { await resolved.locator.waitFor({ state: 'visible', timeout }); } else { await page.waitForSelector(resolved.selector, { timeout }); } return `Element ${selector} appeared`; } case 'viewport': { const size = args[0]; if (!size || !size.includes('x')) throw new Error('Usage: browse viewport (e.g., 375x812)'); const [w, h] = size.split('x').map(Number); await bm.setViewport(w, h); return `Viewport set to ${w}x${h}`; } case 'cookie': { const cookieStr = args[0]; if (!cookieStr || !cookieStr.includes('=')) throw new Error('Usage: browse cookie ='); const eq = cookieStr.indexOf('='); const name = cookieStr.slice(0, eq); const value = cookieStr.slice(eq + 1); const url = new URL(page.url()); await page.context().addCookies([{ name, value, domain: url.hostname, path: '/', }]); return `Cookie set: ${name}=${value}`; } case 'header': { const headerStr = args[0]; if (!headerStr || !headerStr.includes(':')) throw new Error('Usage: browse header :'); const sep = headerStr.indexOf(':'); const name = headerStr.slice(0, sep).trim(); const value = headerStr.slice(sep + 1).trim(); await bm.setExtraHeader(name, value); return `Header set: ${name}: ${value}`; } case 'useragent': { const ua = args.join(' '); if (!ua) throw new Error('Usage: browse useragent '); bm.setUserAgent(ua); return `User agent set (applies on next restart): ${ua}`; } default: throw new Error(`Unknown write command: ${command}`); } }