Asnip / lib /playwright-tool.mjs
teddyoas's picture
Duplicate from teddyoas/PHANTOM-v2
20f108c
Raw History Blame Contribute Delete
3.83 kB
import { chromium } from 'playwright';
/**
* Perform browser automation actions on a given URL.
* @param {string} url - The URL to navigate to.
* @param {Array} actions - List of action objects: {type, selector, value, code}.
* Supported types: click, type, screenshot, evaluate.
* @returns {Object} Result object with overall success, navigation error, and per-action results.
*/
export default async function runTool(url, actions) {
// --- Input Validation ---
if (typeof url !== 'string' || url.trim() === '') {
throw new Error('A valid URL string is required');
}
if (!Array.isArray(actions)) {
throw new Error('Actions must be an array');
}
// --- Launch Browser ---
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext();
const page = await context.newPage();
// Set default timeout for all page operations (navigation, clicks, etc.)
const DEFAULT_TIMEOUT = 30000;
page.setDefaultTimeout(DEFAULT_TIMEOUT);
const results = [];
let navigationError = null;
try {
// --- Navigation ---
await page.goto(url, { waitUntil: 'networkidle', timeout: DEFAULT_TIMEOUT });
} catch (err) {
navigationError = err.message;
}
// --- Process each action (even if navigation failed) ---
for (const action of actions) {
const { type, selector, value, code } = action;
const actionResult = {
type,
selector: selector || null,
value: value || null,
code: code || null,
success: false,
data: null,
error: null,
};
// Skip action execution if navigation failed
if (navigationError) {
actionResult.error = 'Navigation failed, action skipped';
results.push(actionResult);
continue;
}
try {
switch (type) {
case 'click': {
if (!selector) throw new Error('Selector is required for click');
await page.click(selector);
actionResult.success = true;
actionResult.data = `Clicked on "${selector}"`;
break;
}
case 'type': {
if (!selector) throw new Error('Selector is required for type');
if (value === undefined || value === null) throw new Error('Value is required for type');
await page.fill(selector, String(value));
actionResult.success = true;
actionResult.data = `Typed "${value}" into "${selector}"`;
break;
}
case 'screenshot': {
const buffer = await page.screenshot({ fullPage: true });
actionResult.data = buffer.toString('base64');
actionResult.success = true;
break;
}
case 'evaluate': {
if (typeof code !== 'string' || code.trim() === '') {
throw new Error('Valid JavaScript code string is required for evaluate');
}
const evalResult = await page.evaluate(code);
// Ensure serializable result (basic handling for common cases)
actionResult.data =
evalResult !== undefined && evalResult !== null
? typeof evalResult === 'object'
? JSON.parse(JSON.stringify(evalResult))
: evalResult
: null;
actionResult.success = true;
break;
}
default: {
throw new Error(`Unsupported action type: "${type}"`);
}
}
} catch (err) {
actionResult.success = false;
actionResult.error = err.message;
}
results.push(actionResult);
}
// --- Cleanup ---
await browser.close();
// --- Build final response ---
const overallSuccess = !navigationError && results.every((r) => r.success);
return {
success: overallSuccess,
navigationError: navigationError,
results: results,
};
}