UNPKG

donobu

Version:

Create browser automations with an LLM agent and replay them as Playwright scripts.

256 lines 10.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.webTargetRuntimePlugin = exports.WebTargetRuntime = void 0; const fs = __importStar(require("fs/promises")); const os_1 = require("os"); const path = __importStar(require("path")); const InvalidParamValueException_1 = require("../exceptions/InvalidParamValueException"); const WebTargetInspector_1 = require("../managers/WebTargetInspector"); const ControlPanel_1 = require("../models/ControlPanel"); const GoToWebpageTool_1 = require("../tools/GoToWebpageTool"); const BrowserUtils_1 = require("../utils/BrowserUtils"); const Logger_1 = require("../utils/Logger"); const PlaywrightUtils_1 = require("../utils/PlaywrightUtils"); // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- /** * Returns a browser config that points to BrowserBase if the * BROWSERBASE_PROJECT_ID and BROWSERBASE_API_KEY environment variables are * present, otherwise, returns back a basic config that uses local Chromium. */ function getDefaultBrowserConfig(environ) { const browserBaseProjectId = environ.data.BROWSERBASE_PROJECT_ID; if (browserBaseProjectId && environ.data.BROWSERBASE_API_KEY) { return { initialState: undefined, persistState: false, using: { type: 'browserBase', sessionArgs: { projectId: browserBaseProjectId, browserSettings: { advancedStealth: false, }, keepAlive: false, proxies: false, }, }, }; } else { return { initialState: undefined, persistState: false, using: { type: 'device', deviceName: 'Desktop Chromium', headless: false, }, }; } } async function createTempDirectoryForFlow(flowId) { const tempDir = path.join((0, os_1.tmpdir)(), flowId); await fs.mkdir(tempDir); return tempDir; } // --------------------------------------------------------------------------- // WebTargetRuntime // --------------------------------------------------------------------------- /** * {@link TargetRuntime} implementation for web (Playwright browser) targets. * * Encapsulates browser config resolution, browser context creation, device * name validation, control panel setup, video directory management, and the * `GoToWebpage` initial tool call. */ class WebTargetRuntime { constructor(browserContext, inspector, controlPanel, browserConfig, isControlPanelEnabled, targetWebsite, videoDir) { this.targetType = 'web'; this.browserContext = browserContext; this.inspector = inspector; this.controlPanel = controlPanel; this.browserConfig = browserConfig; this.isControlPanelEnabled = isControlPanelEnabled; this.targetWebsite = targetWebsite; this.videoDir = videoDir; } /** * Create a {@link WebTargetRuntime} from the given parameters. * * Resolves browser config, validates device name, creates the browser * context (or uses the override), sets up the control panel, and prepares * the video directory. */ static async create(params) { const { flowParams, flowId, interactionVisualizer, controlPanelFactory } = params; const webConfig = flowParams.web; const targetWebsite = webConfig?.targetWebsite ?? ''; // --- Browser config resolution --- const defaultBrowserConfig = getDefaultBrowserConfig(params.environ); const defaultDeviceName = defaultBrowserConfig.using.type === 'device' ? defaultBrowserConfig.using.deviceName : ''; const browserConfig = webConfig?.browser ?? defaultBrowserConfig; if (browserConfig.using.type === 'device') { if (!browserConfig.using.deviceName) { browserConfig.using.deviceName = defaultDeviceName; } if (!BrowserUtils_1.BrowserUtils.getSupportedDevices().has(browserConfig.using.deviceName ?? defaultDeviceName ?? '')) { throw new InvalidParamValueException_1.InvalidParamValueException('deviceName', browserConfig.using.deviceName); } } // --- Control panel --- const isControlPanelEnabled = !(browserConfig.using.type === 'device' ? browserConfig.using.headless : false) && (flowParams.isControlPanelEnabled ?? true); const controlPanel = isControlPanelEnabled ? await controlPanelFactory(flowId) : new ControlPanel_1.NoOpControlPanel(); try { // --- Video directory --- const videoDir = flowParams.videoDisabled ? undefined : await createTempDirectoryForFlow(flowId); // --- Browser readiness (lazy install for non-Chromium browsers) --- if (browserConfig.using.type === 'device' && browserConfig.using.deviceName) { const browserType = BrowserUtils_1.BrowserUtils.getBrowserTypeForDeviceName(browserConfig.using.deviceName); if (browserType !== 'chromium') { const isReady = await PlaywrightUtils_1.PlaywrightUtils.isBrowserInstalled(browserType); if (!isReady) { Logger_1.appLogger.info(`Installing ${browserType} for first-time use — this may take a few minutes...`); await PlaywrightUtils_1.PlaywrightUtils.ensureBrowserReady(browserType); } } } // --- Browser context --- const browserContext = params.browserContextOverride ? params.browserContextOverride : await BrowserUtils_1.BrowserUtils.create(browserConfig, videoDir, webConfig?.browser?.initialState && params.getBrowserStorageState ? await params.getBrowserStorageState(webConfig.browser.initialState) : undefined); // --- Inspector --- const webTarget = { type: 'web', current: null }; const inspector = new WebTargetInspector_1.WebTargetInspector(webTarget, browserContext, interactionVisualizer); return new WebTargetRuntime(browserContext, inspector, controlPanel, browserConfig, isControlPanelEnabled, targetWebsite, videoDir); } catch (error) { controlPanel.close(); throw error; } } getMetadataFields() { return { target: 'web', web: { browser: this.browserConfig, targetWebsite: this.targetWebsite, }, isControlPanelEnabled: this.isControlPanelEnabled, }; } getInitialToolCalls(flowParams) { const targetWebsite = flowParams.web?.targetWebsite; if (targetWebsite) { return [ { name: GoToWebpageTool_1.GoToWebpageTool.NAME, parameters: { rationale: 'Initializing web navigation.', url: targetWebsite.toString(), }, }, ]; } return []; } async destroy() { const browser = this.browserContext?.browser(); try { await this.browserContext?.close(); } catch (error) { Logger_1.appLogger.error('Error when attempting to close browser context:', error); } try { await browser?.close(); } catch (error) { Logger_1.appLogger.error('Error when attempting to close browser:', error); } } } exports.WebTargetRuntime = WebTargetRuntime; // --------------------------------------------------------------------------- // WebTargetRuntimePlugin // --------------------------------------------------------------------------- /** * Built-in {@link TargetRuntimePlugin} for web (Playwright browser) targets. * * Registered as `type: 'web'` at startup. */ exports.webTargetRuntimePlugin = { type: 'web', describe() { return { label: 'Web Browser', supportedDevices: Array.from(BrowserUtils_1.BrowserUtils.getSupportedDevices().keys()), }; }, async validate(flowParams) { const validProtocols = ['https:', 'http:']; const targetWebsite = flowParams.web?.targetWebsite; if (targetWebsite) { let parsedUrl; try { parsedUrl = new URL(targetWebsite); } catch { throw new InvalidParamValueException_1.InvalidParamValueException('web.targetWebsite', targetWebsite, 'the URL is malformed'); } if (!validProtocols.includes(parsedUrl.protocol)) { throw new InvalidParamValueException_1.InvalidParamValueException('web.targetWebsite', targetWebsite, 'the URL must start with a supported protocol (example: "https://")'); } } }, async createRuntime(params) { return WebTargetRuntime.create(params); }, }; //# sourceMappingURL=WebTargetRuntime.js.map