From a107396c0f23fc4ec0746d114979008adfb0d28c Mon Sep 17 00:00:00 2001 From: Filip Michalsky Date: Tue, 2 Dec 2025 18:32:21 +0000 Subject: [PATCH 1/4] fastify server with understudy cdp commands - via cua handler --- .../browser_env/cua-server/.env.example | 20 + .../browser_env/cua-server/.gitignore | 22 + environments/browser_env/cua-server/README.md | 237 ++ .../browser_env/cua-server/actionExecutor.ts | 214 ++ .../browser_env/cua-server/cua-primitives.txt | 33 + environments/browser_env/cua-server/index.ts | 76 + .../browser_env/cua-server/package.json | 23 + .../browser_env/cua-server/pnpm-lock.yaml | 3402 +++++++++++++++++ environments/browser_env/cua-server/server.ts | 149 + .../browser_env/cua-server/sessionManager.ts | 143 + environments/browser_env/cua-server/start.sh | 145 + .../browser_env/cua-server/stateCapture.ts | 51 + environments/browser_env/cua-server/test.sh | 177 + .../browser_env/cua-server/tsconfig.json | 17 + environments/browser_env/cua-server/types.ts | 115 + 15 files changed, 4824 insertions(+) create mode 100644 environments/browser_env/cua-server/.env.example create mode 100644 environments/browser_env/cua-server/.gitignore create mode 100644 environments/browser_env/cua-server/README.md create mode 100644 environments/browser_env/cua-server/actionExecutor.ts create mode 100644 environments/browser_env/cua-server/cua-primitives.txt create mode 100644 environments/browser_env/cua-server/index.ts create mode 100644 environments/browser_env/cua-server/package.json create mode 100644 environments/browser_env/cua-server/pnpm-lock.yaml create mode 100644 environments/browser_env/cua-server/server.ts create mode 100644 environments/browser_env/cua-server/sessionManager.ts create mode 100755 environments/browser_env/cua-server/start.sh create mode 100644 environments/browser_env/cua-server/stateCapture.ts create mode 100755 environments/browser_env/cua-server/test.sh create mode 100644 environments/browser_env/cua-server/tsconfig.json create mode 100644 environments/browser_env/cua-server/types.ts diff --git a/environments/browser_env/cua-server/.env.example b/environments/browser_env/cua-server/.env.example new file mode 100644 index 000000000..e783f48d9 --- /dev/null +++ b/environments/browser_env/cua-server/.env.example @@ -0,0 +1,20 @@ +# CUA Primitives API Server Configuration +# Copy this file to .env and customize as needed + +# Server Configuration +CUA_SERVER_PORT=3000 +CUA_SERVER_HOST=0.0.0.0 + +# Browserbase Configuration (optional, for env: "BROWSERBASE") +# BROWSERBASE_API_KEY=your_api_key_here +# BROWSERBASE_PROJECT_ID=your_project_id_here + +# OpenAI API Key (if using OpenAI models with Stagehand) +# OPENAI_API_KEY=your_openai_api_key_here + +# Anthropic API Key (if using Anthropic models with Stagehand) +# ANTHROPIC_API_KEY=your_anthropic_api_key_here + +# Google API Key (if using Google models with Stagehand) +# GOOGLE_GENERATIVE_AI_API_KEY=your_google_api_key_here + diff --git a/environments/browser_env/cua-server/.gitignore b/environments/browser_env/cua-server/.gitignore new file mode 100644 index 000000000..3458097ca --- /dev/null +++ b/environments/browser_env/cua-server/.gitignore @@ -0,0 +1,22 @@ +# Dependencies +node_modules/ + +# Environment +.env + +# Logs +*.log + +# pnpm +pnpm-debug.log* + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + diff --git a/environments/browser_env/cua-server/README.md b/environments/browser_env/cua-server/README.md new file mode 100644 index 000000000..8f05194a4 --- /dev/null +++ b/environments/browser_env/cua-server/README.md @@ -0,0 +1,237 @@ +# CUA Primitives API Server + +A Fastify server that exposes Stagehand's Computer Use Agent (CUA) browser primitives as REST endpoints, enabling external agents to control browser sessions remotely. + +## Architecture + +``` +External Agent -> Fastify API -> BrowserSessionManager -> Stagehand Page -> Browser +``` + +## Prerequisites + +```bash +npm install @browserbasehq/stagehand fastify +``` + +## Quick Start + +```bash +# Start the server +npx tsx index.ts + +# Or with environment variables +CUA_SERVER_PORT=8080 npx tsx index.ts +``` + +## Environment Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `CUA_SERVER_PORT` | `3000` | Server port | +| `CUA_SERVER_HOST` | `0.0.0.0` | Server host | + +## API Endpoints + +### Health Check + +```bash +GET /health +``` + +Returns server status and active session count. + +### List Sessions + +```bash +GET /sessions +``` + +Returns array of active session IDs. + +### Create Session + +```bash +POST /sessions +Content-Type: application/json + +{ + "env": "LOCAL", // or "BROWSERBASE" + "viewport": { + "width": 1280, + "height": 720 + } +} +``` + +Returns: +```json +{ + "sessionId": "session_1234567890_abc123", + "state": { + "screenshot": "base64...", + "url": "about:blank", + "viewport": { "width": 1280, "height": 720 } + } +} +``` + +### Get Session State + +```bash +GET /sessions/:id/state +``` + +Returns current browser state (screenshot, URL, viewport). + +### Close Session + +```bash +DELETE /sessions/:id +``` + +Closes the browser and removes the session. + +### Execute Action + +```bash +POST /sessions/:id/action +Content-Type: application/json + +{ + "type": "click", + "x": 100, + "y": 200 +} +``` + +Returns: +```json +{ + "success": true, + "state": { + "screenshot": "base64...", + "url": "https://example.com", + "viewport": { "width": 1280, "height": 720 } + } +} +``` + +## Available Actions + +### Mouse Actions + +| Action | Parameters | Description | +|--------|------------|-------------| +| `click` | `x`, `y`, `button?`, `clickCount?` | Click at coordinates | +| `double_click` | `x`, `y` | Double-click at coordinates | +| `tripleClick` | `x`, `y` | Triple-click at coordinates | +| `drag` | `path: [{x, y}, ...]` | Drag along path | +| `move` | - | No-op (cursor visualization) | + +### Keyboard Actions + +| Action | Parameters | Description | +|--------|------------|-------------| +| `type` | `text` | Type text into focused element | +| `keypress` | `keys` (string or array) | Press keyboard keys | + +### Navigation Actions + +| Action | Parameters | Description | +|--------|------------|-------------| +| `goto` | `url` | Navigate to URL | +| `back` | - | Go back in history | +| `forward` | - | Go forward in history | +| `scroll` | `x?`, `y?`, `scroll_x?`, `scroll_y?` | Scroll the page | + +### Utility Actions + +| Action | Parameters | Description | +|--------|------------|-------------| +| `wait` | `timeMs?` (default: 1000) | Wait for duration | +| `screenshot` | - | No-op (always returned in response) | + +## Example Usage + +```bash +# Create a session +SESSION=$(curl -s -X POST http://localhost:3000/sessions | jq -r '.sessionId') + +# Navigate to a website +curl -X POST http://localhost:3000/sessions/$SESSION/action \ + -H "Content-Type: application/json" \ + -d '{"type": "goto", "url": "https://example.com"}' + +# Click a button +curl -X POST http://localhost:3000/sessions/$SESSION/action \ + -H "Content-Type: application/json" \ + -d '{"type": "click", "x": 150, "y": 300}' + +# Type into an input +curl -X POST http://localhost:3000/sessions/$SESSION/action \ + -H "Content-Type: application/json" \ + -d '{"type": "type", "text": "Hello, World!"}' + +# Press Enter +curl -X POST http://localhost:3000/sessions/$SESSION/action \ + -H "Content-Type: application/json" \ + -d '{"type": "keypress", "keys": "Enter"}' + +# Scroll down +curl -X POST http://localhost:3000/sessions/$SESSION/action \ + -H "Content-Type: application/json" \ + -d '{"type": "scroll", "x": 640, "y": 360, "scroll_y": 500}' + +# Close the session +curl -X DELETE http://localhost:3000/sessions/$SESSION +``` + +## Response Format + +All action responses include the full browser state: + +```typescript +interface ActionResponse { + success: boolean; + error?: string; + state: { + screenshot: string; // base64 PNG + url: string; + viewport: { + width: number; + height: number; + }; + }; +} +``` + +## Error Handling + +Errors return appropriate HTTP status codes: + +- `404` - Session not found +- `500` - Action execution failed + +```json +{ + "error": "Session session_123 not found", + "code": "SESSION_NOT_FOUND" +} +``` + +## File Structure + +``` +cua-server/ +├── index.ts # Entry point +├── server.ts # Fastify routes +├── sessionManager.ts # Browser session lifecycle +├── actionExecutor.ts # CUA primitive execution +├── stateCapture.ts # Screenshot & state helpers +├── types.ts # TypeScript types +├── start.sh # Startup script +├── test.sh # Canary test script +└── README.md # This file +``` + diff --git a/environments/browser_env/cua-server/actionExecutor.ts b/environments/browser_env/cua-server/actionExecutor.ts new file mode 100644 index 000000000..1bcea25f5 --- /dev/null +++ b/environments/browser_env/cua-server/actionExecutor.ts @@ -0,0 +1,214 @@ +import type { Page } from "@browserbasehq/stagehand"; +import { ActionRequest, ActionExecutionResult } from "./types"; + +/** + * Key mapping for converting various key representations to Playwright-compatible names + */ +const KEY_MAP: Record = { + ENTER: "Enter", + RETURN: "Enter", + ESCAPE: "Escape", + ESC: "Escape", + BACKSPACE: "Backspace", + TAB: "Tab", + SPACE: " ", + DELETE: "Delete", + DEL: "Delete", + ARROWUP: "ArrowUp", + ARROWDOWN: "ArrowDown", + ARROWLEFT: "ArrowLeft", + ARROWRIGHT: "ArrowRight", + ARROW_UP: "ArrowUp", + ARROW_DOWN: "ArrowDown", + ARROW_LEFT: "ArrowLeft", + ARROW_RIGHT: "ArrowRight", + UP: "ArrowUp", + DOWN: "ArrowDown", + LEFT: "ArrowLeft", + RIGHT: "ArrowRight", + SHIFT: "Shift", + CONTROL: "Control", + CTRL: "Control", + ALT: "Alt", + OPTION: "Alt", + META: "Meta", + COMMAND: "Meta", + CMD: "Meta", + SUPER: "Meta", + WINDOWS: "Meta", + WIN: "Meta", + HOME: "Home", + END: "End", + PAGEUP: "PageUp", + PAGEDOWN: "PageDown", + PAGE_UP: "PageUp", + PAGE_DOWN: "PageDown", + PGUP: "PageUp", + PGDN: "PageDown", +}; + +function mapKeyToPlaywright(key: string): string { + if (!key) return key; + const upperKey = key.toUpperCase(); + return KEY_MAP[upperKey] || key; +} + +/** + * ActionExecutor + * + * Executes CUA browser primitives on a Page object. + * Adapted from V3CuaAgentHandler.executeAction logic. + */ +export async function executeAction( + page: Page, + action: ActionRequest, +): Promise { + try { + switch (action.type) { + case "click": { + const { x, y, button = "left", clickCount = 1 } = action; + if (typeof x !== "number" || typeof y !== "number") { + return { + success: false, + error: "click requires x and y coordinates", + }; + } + await page.click(x, y, { + button: button as "left" | "right" | "middle", + clickCount, + }); + return { success: true }; + } + + case "double_click": + case "doubleClick": { + const { x, y } = action; + if (typeof x !== "number" || typeof y !== "number") { + return { + success: false, + error: "double_click requires x and y coordinates", + }; + } + await page.click(x, y, { + button: "left", + clickCount: 2, + }); + return { success: true }; + } + + case "tripleClick": { + const { x, y } = action; + if (typeof x !== "number" || typeof y !== "number") { + return { + success: false, + error: "tripleClick requires x and y coordinates", + }; + } + await page.click(x, y, { + button: "left", + clickCount: 3, + }); + return { success: true }; + } + + case "type": { + const { text } = action; + if (typeof text !== "string") { + return { success: false, error: "type requires text parameter" }; + } + await page.type(text); + return { success: true }; + } + + case "keypress": { + const { keys } = action; + if (!keys) { + return { success: false, error: "keypress requires keys parameter" }; + } + const keyList = Array.isArray(keys) ? keys : [keys]; + for (const rawKey of keyList) { + const mapped = mapKeyToPlaywright(String(rawKey)); + await page.keyPress(mapped); + } + return { success: true }; + } + + case "scroll": { + const { x = 0, y = 0, scroll_x = 0, scroll_y = 0 } = action; + await page.scroll( + x as number, + y as number, + scroll_x as number, + scroll_y as number, + ); + return { success: true }; + } + + case "drag": { + const { path } = action; + if (!Array.isArray(path) || path.length < 2) { + return { + success: false, + error: "drag requires path array with at least 2 points", + }; + } + const start = path[0]; + const end = path[path.length - 1]; + await page.dragAndDrop(start.x, start.y, end.x, end.y, { + steps: Math.min(20, Math.max(5, path.length)), + delay: 10, + }); + return { success: true }; + } + + case "move": { + // No direct cursor-only move in the Page API + // This is a no-op similar to V3CuaAgentHandler + return { success: true }; + } + + case "wait": { + const time = action.timeMs ?? 1000; + await new Promise((r) => setTimeout(r, time)); + return { success: true }; + } + + case "screenshot": { + // Screenshot is handled separately in state capture + // This is a no-op as the response always includes a screenshot + return { success: true }; + } + + case "goto": { + const { url } = action; + if (typeof url !== "string") { + return { success: false, error: "goto requires url parameter" }; + } + await page.goto(url, { waitUntil: "load" }); + return { success: true }; + } + + case "back": { + await page.goBack(); + return { success: true }; + } + + case "forward": { + await page.goForward(); + return { success: true }; + } + + default: + return { + success: false, + error: `Unknown action type: ${action.type}`, + }; + } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + return { + success: false, + error: errorMessage, + }; + } +} diff --git a/environments/browser_env/cua-server/cua-primitives.txt b/environments/browser_env/cua-server/cua-primitives.txt new file mode 100644 index 000000000..821f5935c --- /dev/null +++ b/environments/browser_env/cua-server/cua-primitives.txt @@ -0,0 +1,33 @@ +CUA Handler Primitives +====================== + +Mouse/Touch Actions +------------------- +- click - Click at coordinates (x, y) with optional button and clickCount +- double_click / doubleClick - Double-click at coordinates (x, y) +- tripleClick - Triple-click at coordinates (x, y) +- drag - Drag and drop along a path array +- move - Cursor move (no-op, visualization handled by overlay) + +Keyboard Actions +---------------- +- type - Type text into the currently active/focused element +- keypress - Press one or more keyboard keys (supports array) + +Navigation Actions +------------------ +- scroll - Scroll the page with optional anchor point and delta values +- goto - Navigate to a URL (waits for page load) +- back - Navigate back in browser history +- forward - Navigate forward in browser history + +Utility Actions +--------------- +- wait - Wait for a specified duration (default: 1000ms) +- screenshot - Capture screenshot (handled automatically around actions) +- open_web_browser - No-op (browser already open) + +Custom Actions +-------------- +- custom_tool - Custom tool execution (handled by agent client directly) + diff --git a/environments/browser_env/cua-server/index.ts b/environments/browser_env/cua-server/index.ts new file mode 100644 index 000000000..7eb280324 --- /dev/null +++ b/environments/browser_env/cua-server/index.ts @@ -0,0 +1,76 @@ +/** + * CUA Primitives API Server + * + * Exposes browser automation primitives as REST endpoints for external agents. + * + * Usage (standalone): + * ./start.sh # Start with defaults + * ./start.sh --port 8080 # Custom port + * ./start.sh --host 127.0.0.1 # Custom host + * + * Environment variables: + * CUA_SERVER_PORT - Server port (default: 3000) + * CUA_SERVER_HOST - Server host (default: 0.0.0.0) + * + * See README.md for full documentation. + */ + +import { createServer } from "./server"; +import { sessionManager } from "./sessionManager"; + +const PORT = parseInt(process.env.CUA_SERVER_PORT || "3000", 10); +const HOST = process.env.CUA_SERVER_HOST || "0.0.0.0"; + +async function main() { + const server = createServer(); + + // Graceful shutdown handling + const shutdown = async (signal: string) => { + console.log(`\nReceived ${signal}. Shutting down gracefully...`); + + try { + // Close all browser sessions + await sessionManager.destroyAllSessions(); + console.log("All browser sessions closed."); + + // Close the server + await server.close(); + console.log("Server closed."); + + process.exit(0); + } catch (error) { + console.error("Error during shutdown:", error); + process.exit(1); + } + }; + + process.on("SIGINT", () => shutdown("SIGINT")); + process.on("SIGTERM", () => shutdown("SIGTERM")); + + try { + await server.listen({ port: PORT, host: HOST }); + console.log(` +╔════════════════════════════════════════════════════════════╗ +║ CUA Primitives API Server ║ +╠════════════════════════════════════════════════════════════╣ +║ Server running at http://${HOST}:${PORT} ║ +║ ║ +║ Endpoints: ║ +║ GET /health - Health check ║ +║ GET /sessions - List active sessions ║ +║ POST /sessions - Create browser session ║ +║ DELETE /sessions/:id - Close browser session ║ +║ GET /sessions/:id/state - Get browser state ║ +║ POST /sessions/:id/action - Execute CUA primitive ║ +║ ║ +║ Press Ctrl+C to stop ║ +╚════════════════════════════════════════════════════════════╝ +`); + } catch (error) { + console.error("Failed to start server:", error); + process.exit(1); + } +} + +main(); + diff --git a/environments/browser_env/cua-server/package.json b/environments/browser_env/cua-server/package.json new file mode 100644 index 000000000..641304436 --- /dev/null +++ b/environments/browser_env/cua-server/package.json @@ -0,0 +1,23 @@ +{ + "name": "cua-primitives-server", + "version": "1.0.0", + "description": "CUA Primitives API Server - Browser automation primitives as REST endpoints", + "type": "module", + "scripts": { + "start": "./start.sh", + "dev": "tsx watch index.ts" + }, + "dependencies": { + "@browserbasehq/stagehand": "^3.0.5", + "fastify": "^5.0.0", + "dotenv": "^16.4.5", + "deepmerge": "^4.3.1", + "zod": "^3.25.76" + }, + "devDependencies": { + "tsx": "^4.10.5", + "typescript": "^5.2.2" + }, + "packageManager": "pnpm@9.15.0" +} + diff --git a/environments/browser_env/cua-server/pnpm-lock.yaml b/environments/browser_env/cua-server/pnpm-lock.yaml new file mode 100644 index 000000000..d3864a5c5 --- /dev/null +++ b/environments/browser_env/cua-server/pnpm-lock.yaml @@ -0,0 +1,3402 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@browserbasehq/stagehand': + specifier: ^3.0.5 + version: 3.0.5(@cfworker/json-schema@4.1.1)(@opentelemetry/api@1.9.0)(deepmerge@4.3.1)(dotenv@16.6.1)(zod@3.25.76) + deepmerge: + specifier: ^4.3.1 + version: 4.3.1 + dotenv: + specifier: ^16.4.5 + version: 16.6.1 + fastify: + specifier: ^5.0.0 + version: 5.6.2 + zod: + specifier: ^3.25.76 + version: 3.25.76 + devDependencies: + tsx: + specifier: ^4.10.5 + version: 4.21.0 + typescript: + specifier: ^5.2.2 + version: 5.9.3 + +packages: + + '@ai-sdk/anthropic@2.0.53': + resolution: {integrity: sha512-ih7NV+OFSNWZCF+tYYD7ovvvM+gv7TRKQblpVohg2ipIwC9Y0TirzocJVREzZa/v9luxUwFbsPji++DUDWWxsg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/azure@2.0.78': + resolution: {integrity: sha512-g/zroxY9UaXP1nOK9UFiArpme1Cce5vFzsL+z7eWHf2B4f+VN2aOQ9+TC0phc2C7brUXTXwzZLg/z0tDoVZDdQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/cerebras@1.0.32': + resolution: {integrity: sha512-FayoFKD/SORxBPV9yq+8q+diYOc9WSvTMkvRfN+VW2P/beQs+T7wblTWiNz/TEbVHx3nkLJAbCgD6X/4wYOT3g==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/deepseek@1.0.31': + resolution: {integrity: sha512-Il7WJp8bA3CmlreYSl1YzCucGTn2e5P81IANYIIEeLtWrbK0Y9CLoOCROj8xKYyUSMKlINyGZX2uP79cKewtSg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/gateway@2.0.18': + resolution: {integrity: sha512-sDQcW+6ck2m0pTIHW6BPHD7S125WD3qNkx/B8sEzJp/hurocmJ5Cni0ybExg6sQMGo+fr/GWOwpHF1cmCdg5rQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/google@2.0.44': + resolution: {integrity: sha512-c5dck36FjqiVoeeMJQLTEmUheoURcGTU/nBT6iJu8/nZiKFT/y8pD85KMDRB7RerRYaaQOtslR2d6/5PditiRw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/groq@2.0.32': + resolution: {integrity: sha512-5kadf9Mjd4Ep6jVhrIy56UL7DV5HDisW8UakwB11IN7lSLi8Qwb1fB9uO34GT7JxYqE4w7qZXVuelOmTH9m2Mg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/mistral@2.0.25': + resolution: {integrity: sha512-JRlmXAgG/vuB3ojWjkGzN5W8ZYM8GX6dEkTFJX5sC1NwDgzfiTBIMDeQ/RA6dYZ06OIXtBZTt4oaruP+1xc92g==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/openai-compatible@1.0.28': + resolution: {integrity: sha512-yKubDxLYtXyGUzkr9lNStf/lE/I+Okc8tmotvyABhsQHHieLKk6oV5fJeRJxhr67Ejhg+FRnwUOxAmjRoFM4dA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/openai@2.0.76': + resolution: {integrity: sha512-ryUkhTDVxe3D1GSAGc94vPZsJlSY8ZuBDLkpf4L81Dm7Ik5AgLfhQrZa8+0hD4kp0dxdVaIoxhpa3QOt1CmncA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/perplexity@2.0.21': + resolution: {integrity: sha512-eyADmcZ2Fz2p3cLEB2Cnm2msW6vxsRQPXuqJ+AWPwSBQLxx58o/wNkF2XRYzipqSxWyOinuIUNBa1Is13GE2ow==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider-utils@3.0.18': + resolution: {integrity: sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider@2.0.0': + resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==} + engines: {node: '>=18'} + + '@ai-sdk/togetherai@1.0.29': + resolution: {integrity: sha512-NUGHmlRTsePi04YC+78gS+Wl2iNP6oNepfuvijgJKVwAZa7xHg4i/lF84OG5aI3r6LlA/779ZZUPoBcj/EHlLg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/xai@2.0.39': + resolution: {integrity: sha512-EtRRHpPb3J6qY8y9C9p1g3FdF8dl6SocmfyS418g+PesK9/bIAbJYWQStdWpJXF/d9VfzeoOp1IhcBgKotAn+A==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@anthropic-ai/sdk@0.39.0': + resolution: {integrity: sha512-eMyDIPRZbt1CCLErRCi3exlAvNkBtRe+kW5vvJyef93PmNr/clstYgHhtvmkxN82nlKgzyGPCyGxrm0JQ1ZIdg==} + + '@browserbasehq/sdk@2.6.0': + resolution: {integrity: sha512-83iXP5D7xMm8Wyn66TUaUrgoByCmAJuoMoZQI3sGg3JAiMlTfnCIMqyVBoNSaItaPIkaCnrsj6LiusmXV2X9YA==} + + '@browserbasehq/stagehand@3.0.5': + resolution: {integrity: sha512-89QPlRKpfq8kJd8oGgodo5I39xDjEPwVIEvdjaICcU33X4yAtkvoR4lM2tEwGiiDu/BPQznB27JFgoGlMjUsTA==} + peerDependencies: + deepmerge: ^4.3.1 + dotenv: ^16.4.5 + zod: 3.25.76 || 4.1.8 + + '@cfworker/json-schema@4.1.1': + resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} + + '@esbuild/aix-ppc64@0.27.0': + resolution: {integrity: sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.0': + resolution: {integrity: sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.0': + resolution: {integrity: sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.0': + resolution: {integrity: sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.0': + resolution: {integrity: sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.0': + resolution: {integrity: sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.0': + resolution: {integrity: sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.0': + resolution: {integrity: sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.0': + resolution: {integrity: sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.0': + resolution: {integrity: sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.0': + resolution: {integrity: sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.0': + resolution: {integrity: sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.0': + resolution: {integrity: sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.0': + resolution: {integrity: sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.0': + resolution: {integrity: sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.0': + resolution: {integrity: sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.0': + resolution: {integrity: sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.0': + resolution: {integrity: sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.0': + resolution: {integrity: sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.0': + resolution: {integrity: sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.0': + resolution: {integrity: sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.0': + resolution: {integrity: sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.0': + resolution: {integrity: sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.0': + resolution: {integrity: sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.0': + resolution: {integrity: sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.0': + resolution: {integrity: sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@fastify/ajv-compiler@4.0.5': + resolution: {integrity: sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==} + + '@fastify/error@4.2.0': + resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==} + + '@fastify/fast-json-stringify-compiler@5.0.3': + resolution: {integrity: sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==} + + '@fastify/forwarded@3.0.1': + resolution: {integrity: sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==} + + '@fastify/merge-json-schemas@0.2.1': + resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==} + + '@fastify/proxy-addr@5.1.0': + resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==} + + '@google/genai@1.30.0': + resolution: {integrity: sha512-3MRcgczBFbUat1wIlZoLJ0vCCfXgm7Qxjh59cZi2X08RgWLtm9hKOspzp7TOg1TV2e26/MLxR2GR5yD5GmBV2w==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@modelcontextprotocol/sdk': ^1.20.1 + peerDependenciesMeta: + '@modelcontextprotocol/sdk': + optional: true + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@langchain/core@0.3.79': + resolution: {integrity: sha512-ZLAs5YMM5N2UXN3kExMglltJrKKoW7hs3KMZFlXUnD7a5DFKBYxPFMeXA4rT+uvTxuJRZPCYX0JKI5BhyAWx4A==} + engines: {node: '>=18'} + + '@langchain/openai@0.4.9': + resolution: {integrity: sha512-NAsaionRHNdqaMjVLPkFCyjUDze+OqRHghA1Cn4fPoAafz+FXcl9c7LlEl9Xo0FH6/8yiCl7Rw2t780C/SBVxQ==} + engines: {node: '>=18'} + peerDependencies: + '@langchain/core': '>=0.3.39 <0.4.0' + + '@modelcontextprotocol/sdk@1.24.0': + resolution: {integrity: sha512-D8h5KXY2vHFW8zTuxn2vuZGN0HGrQ5No6LkHwlEA9trVgNdPL3TF1dSqKA7Dny6BbBYKSW/rOBDXdC8KJAjUCg==} + engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true + + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + + '@pinojs/redact@0.4.0': + resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@puppeteer/browsers@2.3.0': + resolution: {integrity: sha512-ioXoq9gPxkss4MYhD+SFaU9p1IHFUX0ILAWFPyjGaBdjLsYAlZw6j1iLA0N/m12uVHLFDfSYNF7EQccjinIMDA==} + engines: {node: '>=18'} + hasBin: true + + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + + '@tootallnate/quickjs-emscripten@0.23.0': + resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + + '@types/node-fetch@2.6.13': + resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} + + '@types/node@18.19.130': + resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} + + '@types/node@24.10.1': + resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} + + '@types/retry@0.12.0': + resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + + '@types/uuid@10.0.0': + resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} + + '@types/yauzl@2.10.3': + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + + '@vercel/oidc@3.0.5': + resolution: {integrity: sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==} + engines: {node: '>= 20'} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + abstract-logging@2.0.1: + resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + agentkeepalive@4.6.0: + resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} + engines: {node: '>= 8.0.0'} + + ai@5.0.106: + resolution: {integrity: sha512-M5obwavxSJJ3tGlAFqI6eltYNJB0D20X6gIBCFx/KVorb/X1fxVVfiZZpZb+Gslu4340droSOjT0aKQFCarNVg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + ast-types@0.13.4: + resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} + engines: {node: '>=4'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + avvio@9.1.0: + resolution: {integrity: sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==} + + b4a@1.7.3: + resolution: {integrity: sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==} + peerDependencies: + react-native-b4a: '*' + peerDependenciesMeta: + react-native-b4a: + optional: true + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bare-events@2.8.2: + resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==} + peerDependencies: + bare-abort-controller: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true + + bare-fs@4.5.2: + resolution: {integrity: sha512-veTnRzkb6aPHOvSKIOy60KzURfBdUflr5VReI+NSaPL6xf+XLdONQgZgpYvUuZLVQ8dCqxpBAudaOM1+KpAUxw==} + engines: {bare: '>=1.16.0'} + peerDependencies: + bare-buffer: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + + bare-os@3.6.2: + resolution: {integrity: sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==} + engines: {bare: '>=1.14.0'} + + bare-path@3.0.0: + resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} + + bare-stream@2.7.0: + resolution: {integrity: sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==} + peerDependencies: + bare-buffer: '*' + bare-events: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + bare-events: + optional: true + + bare-url@2.3.2: + resolution: {integrity: sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + basic-ftp@5.0.5: + resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} + engines: {node: '>=10.0.0'} + + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + + body-parser@2.2.1: + resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} + engines: {node: '>=18'} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + bufferutil@4.0.9: + resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} + engines: {node: '>=6.14.2'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chrome-launcher@1.2.1: + resolution: {integrity: sha512-qmFR5PLMzHyuNJHwOloHPAHhbaNglkfeV/xDtt5b7xiFFyU1I+AZZX0PYseMuhenJSSirgxELYIbswcoc+5H4A==} + engines: {node: '>=12.13.0'} + hasBin: true + + chromium-bidi@0.6.3: + resolution: {integrity: sha512-qXlsCmpCZJAnoTYI83Iu6EdYQpMYdVkCfq08KDh2pmlVqK5t5IA9mGs4/LwCwp4fqisSOMXZxP3HIh8w8aRn0A==} + peerDependencies: + devtools-protocol: '*' + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + console-table-printer@2.15.0: + resolution: {integrity: sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw==} + + content-disposition@1.0.1: + resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} + engines: {node: '>=18'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + + data-uri-to-buffer@6.0.2: + resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} + engines: {node: '>= 14'} + + dateformat@4.6.3: + resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + degenerator@5.0.1: + resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} + engines: {node: '>= 14'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + devtools-protocol@0.0.1312386: + resolution: {integrity: sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==} + + devtools-protocol@0.0.1464554: + resolution: {integrity: sha512-CAoP3lYfwAGQTaAXYvA6JZR0fjGUb7qec1qf4mToyoH2TZgUFeIqYcjh6f9jNuhHfuZiEdH+PONHYrLhRQX6aw==} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild@0.27.0: + resolution: {integrity: sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + events-universal@1.0.1: + resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} + + eventsource-parser@3.0.6: + resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + + express-rate-limit@7.5.1: + resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + + fast-copy@4.0.0: + resolution: {integrity: sha512-/oA0gx1xyXE9R2YlV4FXwZJXngFdm9Du0zN8FhY38jnLkhp1u35h6bCyKgRhlsA6C9I+1vfXE4KISdt7xc6M9w==} + + fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + + fast-json-stringify@6.1.1: + resolution: {integrity: sha512-DbgptncYEXZqDUOEl4krff4mUiVrTZZVI7BBrQR/T3BqMj/eM1flTC1Uk2uUoLcWCxjT95xKulV/Lc6hhOZsBQ==} + + fast-querystring@1.1.2: + resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fastify@5.6.2: + resolution: {integrity: sha512-dPugdGnsvYkBlENLhCgX8yhyGCsCPrpA8lFWbTNU428l+YOnLgYHR69hzV8HWPC79n536EqzqQtvhtdaCE0dKg==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + fetch-cookie@3.1.0: + resolution: {integrity: sha512-s/XhhreJpqH0ftkGVcQt8JE9bqk+zRn4jF5mPJXWZeQMCI5odV9K+wEWYbnzFPHgQZlvPSMjS4n4yawWE8RINw==} + + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + + find-my-way@9.3.0: + resolution: {integrity: sha512-eRoFWQw+Yv2tuYlK2pjFS2jGXSxSppAs3hSQjfxVKxM5amECzIgYYc1FEI8ZmhSh/Ig+FrKEz43NLRKJjYCZVg==} + engines: {node: '>=20'} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + form-data-encoder@1.7.2: + resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + formdata-node@4.4.1: + resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} + engines: {node: '>= 12.20'} + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gaxios@7.1.3: + resolution: {integrity: sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==} + engines: {node: '>=18'} + + gcp-metadata@8.1.2: + resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==} + engines: {node: '>=18'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + + get-uri@6.0.5: + resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} + engines: {node: '>= 14'} + + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + hasBin: true + + google-auth-library@10.5.0: + resolution: {integrity: sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==} + engines: {node: '>=18'} + + google-logging-utils@1.1.3: + resolution: {integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==} + engines: {node: '>=14'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + gtoken@8.0.0: + resolution: {integrity: sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==} + engines: {node: '>=18'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + help-me@5.0.0: + resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + iconv-lite@0.7.0: + resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ip-address@10.1.0: + resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + ipaddr.js@2.3.0: + resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} + engines: {node: '>= 10'} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jose@6.1.3: + resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + + js-tiktoken@1.0.21: + resolution: {integrity: sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==} + + json-bigint@1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + + json-schema-ref-resolver@3.0.0: + resolution: {integrity: sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + + jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} + + jws@4.0.0: + resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} + + langsmith@0.3.82: + resolution: {integrity: sha512-RTcxtRm0zp2lV+pMesMW7EZSsIlqN7OmR2F6sZ/sOFQwmcLVl+VErMPV4VkX4Sycs4/EIAFT5hpr36EqiHoikQ==} + peerDependencies: + '@opentelemetry/api': '*' + '@opentelemetry/exporter-trace-otlp-proto': '*' + '@opentelemetry/sdk-trace-base': '*' + openai: '*' + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@opentelemetry/exporter-trace-otlp-proto': + optional: true + '@opentelemetry/sdk-trace-base': + optional: true + openai: + optional: true + + light-my-request@6.6.0: + resolution: {integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==} + + lighthouse-logger@2.0.2: + resolution: {integrity: sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + marky@1.3.0: + resolution: {integrity: sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mustache@4.2.0: + resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} + hasBin: true + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + netmask@2.0.2: + resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} + engines: {node: '>= 0.4.0'} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + ollama-ai-provider-v2@1.5.5: + resolution: {integrity: sha512-1YwTFdPjhPNHny/DrOHO+s8oVGGIE5Jib61/KnnjPRNWQhVVimrJJdaAX3e6nNRRDXrY5zbb9cfm2+yVvgsrqw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^4.0.16 + + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + openai@4.104.0: + resolution: {integrity: sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.23.8 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + + p-finally@1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + + p-queue@6.6.2: + resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} + engines: {node: '>=8'} + + p-retry@4.6.2: + resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} + engines: {node: '>=8'} + + p-timeout@3.2.0: + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} + + pac-proxy-agent@7.2.0: + resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} + engines: {node: '>= 14'} + + pac-resolver@7.0.1: + resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} + engines: {node: '>= 14'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + patchright-core@1.57.0: + resolution: {integrity: sha512-um/9Wue7IFAa9UDLacjNgDn62ub5GJe1b1qouvYpELIF9rsFVMNhRo/rRXYajupLwp5xKJ0sSjOV6sw8/HarBQ==} + engines: {node: '>=18'} + hasBin: true + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-to-regexp@8.3.0: + resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + + pino-abstract-transport@3.0.0: + resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} + + pino-pretty@13.1.3: + resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} + hasBin: true + + pino-std-serializers@7.0.0: + resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} + + pino@10.1.0: + resolution: {integrity: sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==} + hasBin: true + + pino@9.14.0: + resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==} + hasBin: true + + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + + playwright-core@1.57.0: + resolution: {integrity: sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.57.0: + resolution: {integrity: sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==} + engines: {node: '>=18'} + hasBin: true + + process-warning@4.0.1: + resolution: {integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==} + + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-agent@6.5.0: + resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} + engines: {node: '>= 14'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + + puppeteer-core@22.15.0: + resolution: {integrity: sha512-cHArnywCiAAVXa3t4GGL2vttNxh7GqXtIYGym99egkNJ3oG//wL9LkvO4WE8W1TJe95t1F1ocu9X4xWaGsOKOA==} + engines: {node: '>=18'} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + ret@0.5.0: + resolution: {integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==} + engines: {node: '>=10'} + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@5.0.10: + resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} + hasBin: true + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex2@5.0.0: + resolution: {integrity: sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + secure-json-parse@4.1.0: + resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-wcswidth@1.1.2: + resolution: {integrity: sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.7: + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + sonic-boom@4.2.0: + resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + streamx@2.23.0: + resolution: {integrity: sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + + strip-json-comments@5.0.3: + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} + engines: {node: '>=14.16'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + tar-fs@3.1.1: + resolution: {integrity: sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==} + + tar-stream@3.1.7: + resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + + text-decoder@1.2.3: + resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} + + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tldts-core@6.1.86: + resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + + tldts@6.1.86: + resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} + hasBin: true + + toad-cache@3.7.0: + resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} + engines: {node: '>=12'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + tough-cookie@5.1.2: + resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} + engines: {node: '>=16'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + unbzip2-stream@1.4.3: + resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + urlpattern-polyfill@10.0.0: + resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==} + + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + web-streams-polyfill@4.0.0-beta.3: + resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} + engines: {node: '>= 14'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + + zod-to-json-schema@3.25.0: + resolution: {integrity: sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==} + peerDependencies: + zod: ^3.25 || ^4 + + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + +snapshots: + + '@ai-sdk/anthropic@2.0.53(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + zod: 3.25.76 + optional: true + + '@ai-sdk/azure@2.0.78(zod@3.25.76)': + dependencies: + '@ai-sdk/openai': 2.0.76(zod@3.25.76) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + zod: 3.25.76 + optional: true + + '@ai-sdk/cerebras@1.0.32(zod@3.25.76)': + dependencies: + '@ai-sdk/openai-compatible': 1.0.28(zod@3.25.76) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + zod: 3.25.76 + optional: true + + '@ai-sdk/deepseek@1.0.31(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + zod: 3.25.76 + optional: true + + '@ai-sdk/gateway@2.0.18(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + '@vercel/oidc': 3.0.5 + zod: 3.25.76 + + '@ai-sdk/google@2.0.44(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + zod: 3.25.76 + optional: true + + '@ai-sdk/groq@2.0.32(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + zod: 3.25.76 + optional: true + + '@ai-sdk/mistral@2.0.25(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + zod: 3.25.76 + optional: true + + '@ai-sdk/openai-compatible@1.0.28(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + zod: 3.25.76 + optional: true + + '@ai-sdk/openai@2.0.76(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + zod: 3.25.76 + optional: true + + '@ai-sdk/perplexity@2.0.21(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + zod: 3.25.76 + optional: true + + '@ai-sdk/provider-utils@3.0.18(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@standard-schema/spec': 1.0.0 + eventsource-parser: 3.0.6 + zod: 3.25.76 + + '@ai-sdk/provider@2.0.0': + dependencies: + json-schema: 0.4.0 + + '@ai-sdk/togetherai@1.0.29(zod@3.25.76)': + dependencies: + '@ai-sdk/openai-compatible': 1.0.28(zod@3.25.76) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + zod: 3.25.76 + optional: true + + '@ai-sdk/xai@2.0.39(zod@3.25.76)': + dependencies: + '@ai-sdk/openai-compatible': 1.0.28(zod@3.25.76) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + zod: 3.25.76 + optional: true + + '@anthropic-ai/sdk@0.39.0': + dependencies: + '@types/node': 18.19.130 + '@types/node-fetch': 2.6.13 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + '@browserbasehq/sdk@2.6.0': + dependencies: + '@types/node': 18.19.130 + '@types/node-fetch': 2.6.13 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + '@browserbasehq/stagehand@3.0.5(@cfworker/json-schema@4.1.1)(@opentelemetry/api@1.9.0)(deepmerge@4.3.1)(dotenv@16.6.1)(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@anthropic-ai/sdk': 0.39.0 + '@browserbasehq/sdk': 2.6.0 + '@google/genai': 1.30.0(@modelcontextprotocol/sdk@1.24.0(@cfworker/json-schema@4.1.1)(zod@3.25.76))(bufferutil@4.0.9) + '@langchain/openai': 0.4.9(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.0.9)) + '@modelcontextprotocol/sdk': 1.24.0(@cfworker/json-schema@4.1.1)(zod@3.25.76) + ai: 5.0.106(zod@3.25.76) + deepmerge: 4.3.1 + devtools-protocol: 0.0.1464554 + dotenv: 16.6.1 + fetch-cookie: 3.1.0 + openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76) + pino: 9.14.0 + pino-pretty: 13.1.3 + playwright: 1.57.0 + ws: 8.18.3(bufferutil@4.0.9) + zod: 3.25.76 + zod-to-json-schema: 3.25.0(zod@3.25.76) + optionalDependencies: + '@ai-sdk/anthropic': 2.0.53(zod@3.25.76) + '@ai-sdk/azure': 2.0.78(zod@3.25.76) + '@ai-sdk/cerebras': 1.0.32(zod@3.25.76) + '@ai-sdk/deepseek': 1.0.31(zod@3.25.76) + '@ai-sdk/google': 2.0.44(zod@3.25.76) + '@ai-sdk/groq': 2.0.32(zod@3.25.76) + '@ai-sdk/mistral': 2.0.25(zod@3.25.76) + '@ai-sdk/openai': 2.0.76(zod@3.25.76) + '@ai-sdk/perplexity': 2.0.21(zod@3.25.76) + '@ai-sdk/togetherai': 1.0.29(zod@3.25.76) + '@ai-sdk/xai': 2.0.39(zod@3.25.76) + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76)) + bufferutil: 4.0.9 + chrome-launcher: 1.2.1 + ollama-ai-provider-v2: 1.5.5(zod@3.25.76) + patchright-core: 1.57.0 + playwright-core: 1.57.0 + puppeteer-core: 22.15.0(bufferutil@4.0.9) + transitivePeerDependencies: + - '@cfworker/json-schema' + - '@opentelemetry/api' + - '@opentelemetry/exporter-trace-otlp-proto' + - '@opentelemetry/sdk-trace-base' + - bare-abort-controller + - bare-buffer + - encoding + - react-native-b4a + - supports-color + - utf-8-validate + + '@cfworker/json-schema@4.1.1': {} + + '@esbuild/aix-ppc64@0.27.0': + optional: true + + '@esbuild/android-arm64@0.27.0': + optional: true + + '@esbuild/android-arm@0.27.0': + optional: true + + '@esbuild/android-x64@0.27.0': + optional: true + + '@esbuild/darwin-arm64@0.27.0': + optional: true + + '@esbuild/darwin-x64@0.27.0': + optional: true + + '@esbuild/freebsd-arm64@0.27.0': + optional: true + + '@esbuild/freebsd-x64@0.27.0': + optional: true + + '@esbuild/linux-arm64@0.27.0': + optional: true + + '@esbuild/linux-arm@0.27.0': + optional: true + + '@esbuild/linux-ia32@0.27.0': + optional: true + + '@esbuild/linux-loong64@0.27.0': + optional: true + + '@esbuild/linux-mips64el@0.27.0': + optional: true + + '@esbuild/linux-ppc64@0.27.0': + optional: true + + '@esbuild/linux-riscv64@0.27.0': + optional: true + + '@esbuild/linux-s390x@0.27.0': + optional: true + + '@esbuild/linux-x64@0.27.0': + optional: true + + '@esbuild/netbsd-arm64@0.27.0': + optional: true + + '@esbuild/netbsd-x64@0.27.0': + optional: true + + '@esbuild/openbsd-arm64@0.27.0': + optional: true + + '@esbuild/openbsd-x64@0.27.0': + optional: true + + '@esbuild/openharmony-arm64@0.27.0': + optional: true + + '@esbuild/sunos-x64@0.27.0': + optional: true + + '@esbuild/win32-arm64@0.27.0': + optional: true + + '@esbuild/win32-ia32@0.27.0': + optional: true + + '@esbuild/win32-x64@0.27.0': + optional: true + + '@fastify/ajv-compiler@4.0.5': + dependencies: + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) + fast-uri: 3.1.0 + + '@fastify/error@4.2.0': {} + + '@fastify/fast-json-stringify-compiler@5.0.3': + dependencies: + fast-json-stringify: 6.1.1 + + '@fastify/forwarded@3.0.1': {} + + '@fastify/merge-json-schemas@0.2.1': + dependencies: + dequal: 2.0.3 + + '@fastify/proxy-addr@5.1.0': + dependencies: + '@fastify/forwarded': 3.0.1 + ipaddr.js: 2.3.0 + + '@google/genai@1.30.0(@modelcontextprotocol/sdk@1.24.0(@cfworker/json-schema@4.1.1)(zod@3.25.76))(bufferutil@4.0.9)': + dependencies: + google-auth-library: 10.5.0 + ws: 8.18.3(bufferutil@4.0.9) + optionalDependencies: + '@modelcontextprotocol/sdk': 1.24.0(@cfworker/json-schema@4.1.1)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76))': + dependencies: + '@cfworker/json-schema': 4.1.1 + ansi-styles: 5.2.0 + camelcase: 6.3.0 + decamelize: 1.2.0 + js-tiktoken: 1.0.21 + langsmith: 0.3.82(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76)) + mustache: 4.2.0 + p-queue: 6.6.2 + p-retry: 4.6.2 + uuid: 10.0.0 + zod: 3.25.76 + zod-to-json-schema: 3.25.0(zod@3.25.76) + transitivePeerDependencies: + - '@opentelemetry/api' + - '@opentelemetry/exporter-trace-otlp-proto' + - '@opentelemetry/sdk-trace-base' + - openai + + '@langchain/openai@0.4.9(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.0.9))': + dependencies: + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76)) + js-tiktoken: 1.0.21 + openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76) + zod: 3.25.76 + zod-to-json-schema: 3.25.0(zod@3.25.76) + transitivePeerDependencies: + - encoding + - ws + + '@modelcontextprotocol/sdk@1.24.0(@cfworker/json-schema@4.1.1)(zod@3.25.76)': + dependencies: + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) + content-type: 1.0.5 + cors: 2.8.5 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.6 + express: 5.2.1 + express-rate-limit: 7.5.1(express@5.2.1) + jose: 6.1.3 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 3.25.76 + zod-to-json-schema: 3.25.0(zod@3.25.76) + optionalDependencies: + '@cfworker/json-schema': 4.1.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/api@1.9.0': {} + + '@pinojs/redact@0.4.0': {} + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@puppeteer/browsers@2.3.0': + dependencies: + debug: 4.4.3 + extract-zip: 2.0.1 + progress: 2.0.3 + proxy-agent: 6.5.0 + semver: 7.7.3 + tar-fs: 3.1.1 + unbzip2-stream: 1.4.3 + yargs: 17.7.2 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + - supports-color + optional: true + + '@standard-schema/spec@1.0.0': {} + + '@tootallnate/quickjs-emscripten@0.23.0': + optional: true + + '@types/node-fetch@2.6.13': + dependencies: + '@types/node': 18.19.130 + form-data: 4.0.5 + + '@types/node@18.19.130': + dependencies: + undici-types: 5.26.5 + + '@types/node@24.10.1': + dependencies: + undici-types: 7.16.0 + optional: true + + '@types/retry@0.12.0': {} + + '@types/uuid@10.0.0': {} + + '@types/yauzl@2.10.3': + dependencies: + '@types/node': 24.10.1 + optional: true + + '@vercel/oidc@3.0.5': {} + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + abstract-logging@2.0.1: {} + + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + + agent-base@7.1.4: {} + + agentkeepalive@4.6.0: + dependencies: + humanize-ms: 1.2.1 + + ai@5.0.106(zod@3.25.76): + dependencies: + '@ai-sdk/gateway': 2.0.18(zod@3.25.76) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + '@opentelemetry/api': 1.9.0 + zod: 3.25.76 + + ajv-formats@3.0.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + ansi-styles@6.2.3: {} + + ast-types@0.13.4: + dependencies: + tslib: 2.8.1 + optional: true + + asynckit@0.4.0: {} + + atomic-sleep@1.0.0: {} + + avvio@9.1.0: + dependencies: + '@fastify/error': 4.2.0 + fastq: 1.19.1 + + b4a@1.7.3: + optional: true + + balanced-match@1.0.2: {} + + bare-events@2.8.2: + optional: true + + bare-fs@4.5.2: + dependencies: + bare-events: 2.8.2 + bare-path: 3.0.0 + bare-stream: 2.7.0(bare-events@2.8.2) + bare-url: 2.3.2 + fast-fifo: 1.3.2 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + optional: true + + bare-os@3.6.2: + optional: true + + bare-path@3.0.0: + dependencies: + bare-os: 3.6.2 + optional: true + + bare-stream@2.7.0(bare-events@2.8.2): + dependencies: + streamx: 2.23.0 + optionalDependencies: + bare-events: 2.8.2 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + optional: true + + bare-url@2.3.2: + dependencies: + bare-path: 3.0.0 + optional: true + + base64-js@1.5.1: {} + + basic-ftp@5.0.5: + optional: true + + bignumber.js@9.3.1: {} + + body-parser@2.2.1: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.3 + http-errors: 2.0.1 + iconv-lite: 0.7.0 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.2 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + buffer-crc32@0.2.13: + optional: true + + buffer-equal-constant-time@1.0.1: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + optional: true + + bufferutil@4.0.9: + dependencies: + node-gyp-build: 4.8.4 + optional: true + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + camelcase@6.3.0: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chrome-launcher@1.2.1: + dependencies: + '@types/node': 24.10.1 + escape-string-regexp: 4.0.0 + is-wsl: 2.2.0 + lighthouse-logger: 2.0.2 + transitivePeerDependencies: + - supports-color + optional: true + + chromium-bidi@0.6.3(devtools-protocol@0.0.1312386): + dependencies: + devtools-protocol: 0.0.1312386 + mitt: 3.0.1 + urlpattern-polyfill: 10.0.0 + zod: 3.23.8 + optional: true + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + optional: true + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colorette@2.0.20: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + console-table-printer@2.15.0: + dependencies: + simple-wcswidth: 1.1.2 + + content-disposition@1.0.1: {} + + content-type@1.0.5: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + cookie@1.1.1: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + data-uri-to-buffer@4.0.1: {} + + data-uri-to-buffer@6.0.2: + optional: true + + dateformat@4.6.3: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decamelize@1.2.0: {} + + deepmerge@4.3.1: {} + + degenerator@5.0.1: + dependencies: + ast-types: 0.13.4 + escodegen: 2.1.0 + esprima: 4.0.1 + optional: true + + delayed-stream@1.0.0: {} + + depd@2.0.0: {} + + dequal@2.0.3: {} + + devtools-protocol@0.0.1312386: + optional: true + + devtools-protocol@0.0.1464554: {} + + dotenv@16.6.1: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + + ee-first@1.1.1: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + encodeurl@2.0.0: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + esbuild@0.27.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.0 + '@esbuild/android-arm': 0.27.0 + '@esbuild/android-arm64': 0.27.0 + '@esbuild/android-x64': 0.27.0 + '@esbuild/darwin-arm64': 0.27.0 + '@esbuild/darwin-x64': 0.27.0 + '@esbuild/freebsd-arm64': 0.27.0 + '@esbuild/freebsd-x64': 0.27.0 + '@esbuild/linux-arm': 0.27.0 + '@esbuild/linux-arm64': 0.27.0 + '@esbuild/linux-ia32': 0.27.0 + '@esbuild/linux-loong64': 0.27.0 + '@esbuild/linux-mips64el': 0.27.0 + '@esbuild/linux-ppc64': 0.27.0 + '@esbuild/linux-riscv64': 0.27.0 + '@esbuild/linux-s390x': 0.27.0 + '@esbuild/linux-x64': 0.27.0 + '@esbuild/netbsd-arm64': 0.27.0 + '@esbuild/netbsd-x64': 0.27.0 + '@esbuild/openbsd-arm64': 0.27.0 + '@esbuild/openbsd-x64': 0.27.0 + '@esbuild/openharmony-arm64': 0.27.0 + '@esbuild/sunos-x64': 0.27.0 + '@esbuild/win32-arm64': 0.27.0 + '@esbuild/win32-ia32': 0.27.0 + '@esbuild/win32-x64': 0.27.0 + + escalade@3.2.0: + optional: true + + escape-html@1.0.3: {} + + escape-string-regexp@4.0.0: + optional: true + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + optional: true + + esprima@4.0.1: + optional: true + + estraverse@5.3.0: + optional: true + + esutils@2.0.3: + optional: true + + etag@1.8.1: {} + + event-target-shim@5.0.1: {} + + eventemitter3@4.0.7: {} + + events-universal@1.0.1: + dependencies: + bare-events: 2.8.2 + transitivePeerDependencies: + - bare-abort-controller + optional: true + + eventsource-parser@3.0.6: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.6 + + express-rate-limit@7.5.1(express@5.2.1): + dependencies: + express: 5.2.1 + + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.1 + content-disposition: 1.0.1 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + extend@3.0.2: {} + + extract-zip@2.0.1: + dependencies: + debug: 4.4.3 + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.3 + transitivePeerDependencies: + - supports-color + optional: true + + fast-copy@4.0.0: {} + + fast-decode-uri-component@1.0.1: {} + + fast-deep-equal@3.1.3: {} + + fast-fifo@1.3.2: + optional: true + + fast-json-stringify@6.1.1: + dependencies: + '@fastify/merge-json-schemas': 0.2.1 + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) + fast-uri: 3.1.0 + json-schema-ref-resolver: 3.0.0 + rfdc: 1.4.1 + + fast-querystring@1.1.2: + dependencies: + fast-decode-uri-component: 1.0.1 + + fast-safe-stringify@2.1.1: {} + + fast-uri@3.1.0: {} + + fastify@5.6.2: + dependencies: + '@fastify/ajv-compiler': 4.0.5 + '@fastify/error': 4.2.0 + '@fastify/fast-json-stringify-compiler': 5.0.3 + '@fastify/proxy-addr': 5.1.0 + abstract-logging: 2.0.1 + avvio: 9.1.0 + fast-json-stringify: 6.1.1 + find-my-way: 9.3.0 + light-my-request: 6.6.0 + pino: 10.1.0 + process-warning: 5.0.0 + rfdc: 1.4.1 + secure-json-parse: 4.1.0 + semver: 7.7.3 + toad-cache: 3.7.0 + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + optional: true + + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + + fetch-cookie@3.1.0: + dependencies: + set-cookie-parser: 2.7.2 + tough-cookie: 5.1.2 + + finalhandler@2.1.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + find-my-way@9.3.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-querystring: 1.1.2 + safe-regex2: 5.0.0 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + form-data-encoder@1.7.2: {} + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + formdata-node@4.4.1: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 4.0.0-beta.3 + + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + fsevents@2.3.2: + optional: true + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gaxios@7.1.3: + dependencies: + extend: 3.0.2 + https-proxy-agent: 7.0.6 + node-fetch: 3.3.2 + rimraf: 5.0.10 + transitivePeerDependencies: + - supports-color + + gcp-metadata@8.1.2: + dependencies: + gaxios: 7.1.3 + google-logging-utils: 1.1.3 + json-bigint: 1.0.0 + transitivePeerDependencies: + - supports-color + + get-caller-file@2.0.5: + optional: true + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@5.2.0: + dependencies: + pump: 3.0.3 + optional: true + + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + get-uri@6.0.5: + dependencies: + basic-ftp: 5.0.5 + data-uri-to-buffer: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + optional: true + + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + google-auth-library@10.5.0: + dependencies: + base64-js: 1.5.1 + ecdsa-sig-formatter: 1.0.11 + gaxios: 7.1.3 + gcp-metadata: 8.1.2 + google-logging-utils: 1.1.3 + gtoken: 8.0.0 + jws: 4.0.0 + transitivePeerDependencies: + - supports-color + + google-logging-utils@1.1.3: {} + + gopd@1.2.0: {} + + gtoken@8.0.0: + dependencies: + gaxios: 7.1.3 + jws: 4.0.0 + transitivePeerDependencies: + - supports-color + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + help-me@5.0.0: {} + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + optional: true + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + + iconv-lite@0.7.0: + dependencies: + safer-buffer: 2.1.2 + + ieee754@1.2.1: + optional: true + + inherits@2.0.4: {} + + ip-address@10.1.0: + optional: true + + ipaddr.js@1.9.1: {} + + ipaddr.js@2.3.0: {} + + is-docker@2.2.1: + optional: true + + is-fullwidth-code-point@3.0.0: {} + + is-promise@4.0.0: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + optional: true + + isexe@2.0.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jose@6.1.3: {} + + joycon@3.1.1: {} + + js-tiktoken@1.0.21: + dependencies: + base64-js: 1.5.1 + + json-bigint@1.0.0: + dependencies: + bignumber.js: 9.3.1 + + json-schema-ref-resolver@3.0.0: + dependencies: + dequal: 2.0.3 + + json-schema-traverse@1.0.0: {} + + json-schema@0.4.0: {} + + jwa@2.0.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@4.0.0: + dependencies: + jwa: 2.0.1 + safe-buffer: 5.2.1 + + langsmith@0.3.82(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76)): + dependencies: + '@types/uuid': 10.0.0 + chalk: 4.1.2 + console-table-printer: 2.15.0 + p-queue: 6.6.2 + semver: 7.7.3 + uuid: 10.0.0 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76) + + light-my-request@6.6.0: + dependencies: + cookie: 1.1.1 + process-warning: 4.0.1 + set-cookie-parser: 2.7.2 + + lighthouse-logger@2.0.2: + dependencies: + debug: 4.4.3 + marky: 1.3.0 + transitivePeerDependencies: + - supports-color + optional: true + + lru-cache@10.4.3: {} + + lru-cache@7.18.3: + optional: true + + marky@1.3.0: + optional: true + + math-intrinsics@1.1.0: {} + + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + minipass@7.1.2: {} + + mitt@3.0.1: + optional: true + + ms@2.1.3: {} + + mustache@4.2.0: {} + + negotiator@1.0.0: {} + + netmask@2.0.2: + optional: true + + node-domexception@1.0.0: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + + node-gyp-build@4.8.4: + optional: true + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + ollama-ai-provider-v2@1.5.5(zod@3.25.76): + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + zod: 3.25.76 + optional: true + + on-exit-leak-free@2.1.2: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76): + dependencies: + '@types/node': 18.19.130 + '@types/node-fetch': 2.6.13 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + optionalDependencies: + ws: 8.18.3(bufferutil@4.0.9) + zod: 3.25.76 + transitivePeerDependencies: + - encoding + + p-finally@1.0.0: {} + + p-queue@6.6.2: + dependencies: + eventemitter3: 4.0.7 + p-timeout: 3.2.0 + + p-retry@4.6.2: + dependencies: + '@types/retry': 0.12.0 + retry: 0.13.1 + + p-timeout@3.2.0: + dependencies: + p-finally: 1.0.0 + + pac-proxy-agent@7.2.0: + dependencies: + '@tootallnate/quickjs-emscripten': 0.23.0 + agent-base: 7.1.4 + debug: 4.4.3 + get-uri: 6.0.5 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + pac-resolver: 7.0.1 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + optional: true + + pac-resolver@7.0.1: + dependencies: + degenerator: 5.0.1 + netmask: 2.0.2 + optional: true + + package-json-from-dist@1.0.1: {} + + parseurl@1.3.3: {} + + patchright-core@1.57.0: + optional: true + + path-key@3.1.1: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-to-regexp@8.3.0: {} + + pend@1.2.0: + optional: true + + pino-abstract-transport@2.0.0: + dependencies: + split2: 4.2.0 + + pino-abstract-transport@3.0.0: + dependencies: + split2: 4.2.0 + + pino-pretty@13.1.3: + dependencies: + colorette: 2.0.20 + dateformat: 4.6.3 + fast-copy: 4.0.0 + fast-safe-stringify: 2.1.1 + help-me: 5.0.0 + joycon: 3.1.1 + minimist: 1.2.8 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 3.0.0 + pump: 3.0.3 + secure-json-parse: 4.1.0 + sonic-boom: 4.2.0 + strip-json-comments: 5.0.3 + + pino-std-serializers@7.0.0: {} + + pino@10.1.0: + dependencies: + '@pinojs/redact': 0.4.0 + atomic-sleep: 1.0.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.0.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.0 + thread-stream: 3.1.0 + + pino@9.14.0: + dependencies: + '@pinojs/redact': 0.4.0 + atomic-sleep: 1.0.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.0.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.0 + thread-stream: 3.1.0 + + pkce-challenge@5.0.1: {} + + playwright-core@1.57.0: {} + + playwright@1.57.0: + dependencies: + playwright-core: 1.57.0 + optionalDependencies: + fsevents: 2.3.2 + + process-warning@4.0.1: {} + + process-warning@5.0.0: {} + + progress@2.0.3: + optional: true + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + proxy-agent@6.5.0: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 7.18.3 + pac-proxy-agent: 7.2.0 + proxy-from-env: 1.1.0 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + optional: true + + proxy-from-env@1.1.0: + optional: true + + pump@3.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + puppeteer-core@22.15.0(bufferutil@4.0.9): + dependencies: + '@puppeteer/browsers': 2.3.0 + chromium-bidi: 0.6.3(devtools-protocol@0.0.1312386) + debug: 4.4.3 + devtools-protocol: 0.0.1312386 + ws: 8.18.3(bufferutil@4.0.9) + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - bufferutil + - react-native-b4a + - supports-color + - utf-8-validate + optional: true + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + quick-format-unescaped@4.0.4: {} + + range-parser@1.2.1: {} + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.0 + unpipe: 1.0.0 + + real-require@0.2.0: {} + + require-directory@2.1.1: + optional: true + + require-from-string@2.0.2: {} + + resolve-pkg-maps@1.0.0: {} + + ret@0.5.0: {} + + retry@0.13.1: {} + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rimraf@5.0.10: + dependencies: + glob: 10.5.0 + + router@2.2.0: + dependencies: + debug: 4.4.3 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.3.0 + transitivePeerDependencies: + - supports-color + + safe-buffer@5.2.1: {} + + safe-regex2@5.0.0: + dependencies: + ret: 0.5.0 + + safe-stable-stringify@2.5.0: {} + + safer-buffer@2.1.2: {} + + secure-json-parse@4.1.0: {} + + semver@7.7.3: {} + + send@1.2.0: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + + set-cookie-parser@2.7.2: {} + + setprototypeof@1.2.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@4.1.0: {} + + simple-wcswidth@1.1.2: {} + + smart-buffer@4.2.0: + optional: true + + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + socks: 2.8.7 + transitivePeerDependencies: + - supports-color + optional: true + + socks@2.8.7: + dependencies: + ip-address: 10.1.0 + smart-buffer: 4.2.0 + optional: true + + sonic-boom@4.2.0: + dependencies: + atomic-sleep: 1.0.0 + + source-map@0.6.1: + optional: true + + split2@4.2.0: {} + + statuses@2.0.2: {} + + streamx@2.23.0: + dependencies: + events-universal: 1.0.1 + fast-fifo: 1.3.2 + text-decoder: 1.2.3 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + optional: true + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.2 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + + strip-json-comments@5.0.3: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + tar-fs@3.1.1: + dependencies: + pump: 3.0.3 + tar-stream: 3.1.7 + optionalDependencies: + bare-fs: 4.5.2 + bare-path: 3.0.0 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + optional: true + + tar-stream@3.1.7: + dependencies: + b4a: 1.7.3 + fast-fifo: 1.3.2 + streamx: 2.23.0 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + optional: true + + text-decoder@1.2.3: + dependencies: + b4a: 1.7.3 + transitivePeerDependencies: + - react-native-b4a + optional: true + + thread-stream@3.1.0: + dependencies: + real-require: 0.2.0 + + through@2.3.8: + optional: true + + tldts-core@6.1.86: {} + + tldts@6.1.86: + dependencies: + tldts-core: 6.1.86 + + toad-cache@3.7.0: {} + + toidentifier@1.0.1: {} + + tough-cookie@5.1.2: + dependencies: + tldts: 6.1.86 + + tr46@0.0.3: {} + + tslib@2.8.1: + optional: true + + tsx@4.21.0: + dependencies: + esbuild: 0.27.0 + get-tsconfig: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 + + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.2 + + typescript@5.9.3: {} + + unbzip2-stream@1.4.3: + dependencies: + buffer: 5.7.1 + through: 2.3.8 + optional: true + + undici-types@5.26.5: {} + + undici-types@7.16.0: + optional: true + + unpipe@1.0.0: {} + + urlpattern-polyfill@10.0.0: + optional: true + + uuid@10.0.0: {} + + vary@1.1.2: {} + + web-streams-polyfill@3.3.3: {} + + web-streams-polyfill@4.0.0-beta.3: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 + + wrappy@1.0.2: {} + + ws@8.18.3(bufferutil@4.0.9): + optionalDependencies: + bufferutil: 4.0.9 + + y18n@5.0.8: + optional: true + + yargs-parser@21.1.1: + optional: true + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + optional: true + + yauzl@2.10.0: + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + optional: true + + zod-to-json-schema@3.25.0(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod@3.23.8: + optional: true + + zod@3.25.76: {} diff --git a/environments/browser_env/cua-server/server.ts b/environments/browser_env/cua-server/server.ts new file mode 100644 index 000000000..9ea58c246 --- /dev/null +++ b/environments/browser_env/cua-server/server.ts @@ -0,0 +1,149 @@ +import Fastify, { FastifyInstance, FastifyRequest, FastifyReply } from "fastify"; +import { sessionManager } from "./sessionManager"; +import { executeAction } from "./actionExecutor"; +import { captureBrowserState } from "./stateCapture"; +import { + ActionRequest, + ActionResponse, + SessionCreateRequest, + SessionCreateResponse, + ErrorResponse, +} from "./types"; + +/** + * Create and configure the Fastify server with CUA primitive routes + */ +export function createServer(): FastifyInstance { + const server = Fastify({ + logger: true, + }); + + // Health check endpoint + server.get("/health", async () => { + return { status: "ok", activeSessions: sessionManager.getActiveSessions().length }; + }); + + // List all active sessions + server.get("/sessions", async () => { + return { sessions: sessionManager.getActiveSessions() }; + }); + + // Create a new browser session + server.post<{ + Body: SessionCreateRequest; + Reply: SessionCreateResponse | ErrorResponse; + }>("/sessions", async (request, reply) => { + try { + const session = await sessionManager.createSession(request.body); + const state = await captureBrowserState(session.page); + + return { + sessionId: session.id, + state, + }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + reply.status(500); + return { error: errorMessage, code: "SESSION_CREATE_FAILED" }; + } + }); + + // Get session state + server.get<{ + Params: { id: string }; + Reply: { state: ReturnType extends Promise ? T : never } | ErrorResponse; + }>("/sessions/:id/state", async (request, reply) => { + const { id } = request.params; + + const page = await sessionManager.getPage(id); + if (!page) { + reply.status(404); + return { error: `Session ${id} not found`, code: "SESSION_NOT_FOUND" }; + } + + try { + const state = await captureBrowserState(page); + return { state }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + reply.status(500); + return { error: errorMessage, code: "STATE_CAPTURE_FAILED" }; + } + }); + + // Delete a session + server.delete<{ + Params: { id: string }; + Reply: { success: boolean } | ErrorResponse; + }>("/sessions/:id", async (request, reply) => { + const { id } = request.params; + + if (!sessionManager.hasSession(id)) { + reply.status(404); + return { error: `Session ${id} not found`, code: "SESSION_NOT_FOUND" }; + } + + try { + await sessionManager.destroySession(id); + return { success: true }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + reply.status(500); + return { error: errorMessage, code: "SESSION_DESTROY_FAILED" }; + } + }); + + // Execute an action on a session + server.post<{ + Params: { id: string }; + Body: ActionRequest; + Reply: ActionResponse | ErrorResponse; + }>("/sessions/:id/action", async (request, reply) => { + const { id } = request.params; + const action = request.body; + + const page = await sessionManager.getPage(id); + if (!page) { + reply.status(404); + return { error: `Session ${id} not found`, code: "SESSION_NOT_FOUND" }; + } + + try { + // Execute the action + const result = await executeAction(page, action); + + // Capture state after action (always includes screenshot) + const state = await captureBrowserState(page); + + return { + success: result.success, + error: result.error, + state, + }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + + // Try to capture state even on error + let state; + try { + state = await captureBrowserState(page); + } catch { + state = { + screenshot: "", + url: "", + viewport: { width: 0, height: 0 }, + }; + } + + reply.status(500); + return { + success: false, + error: errorMessage, + state, + }; + } + }); + + return server; +} + diff --git a/environments/browser_env/cua-server/sessionManager.ts b/environments/browser_env/cua-server/sessionManager.ts new file mode 100644 index 000000000..eeac21370 --- /dev/null +++ b/environments/browser_env/cua-server/sessionManager.ts @@ -0,0 +1,143 @@ +import { Stagehand } from "@browserbasehq/stagehand"; +import type { Page } from "@browserbasehq/stagehand"; +import { BrowserSession, SessionCreateRequest, Viewport } from "./types"; + +/** + * Generates a unique session ID + */ +function generateSessionId(): string { + return `session_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`; +} + +/** + * BrowserSessionManager + * + * Manages multiple Stagehand browser instances by session ID. + * Handles creation, retrieval, and cleanup of browser sessions. + */ +export class BrowserSessionManager { + private sessions: Map = new Map(); + + /** + * Create a new browser session + */ + async createSession(options?: SessionCreateRequest): Promise { + const sessionId = generateSessionId(); + + const stagehand = new Stagehand({ + env: options?.env ?? "LOCAL", + apiKey: options?.browserbaseApiKey, + projectId: options?.browserbaseProjectId, + verbose: 1, + localBrowserLaunchOptions: options?.viewport + ? { + viewport: { + width: options.viewport.width, + height: options.viewport.height, + }, + } + : undefined, + }); + + await stagehand.init(); + + const page = stagehand.context.pages()[0]; + + const session: BrowserSession = { + id: sessionId, + stagehand, + page, + createdAt: new Date(), + }; + + this.sessions.set(sessionId, session); + + return session; + } + + /** + * Get an existing session by ID + */ + getSession(sessionId: string): BrowserSession | undefined { + return this.sessions.get(sessionId); + } + + /** + * Check if a session exists + */ + hasSession(sessionId: string): boolean { + return this.sessions.has(sessionId); + } + + /** + * Destroy a session and close its browser + */ + async destroySession(sessionId: string): Promise { + const session = this.sessions.get(sessionId); + if (!session) { + return false; + } + + try { + await session.stagehand.close(); + } catch (error) { + console.error(`Error closing session ${sessionId}:`, error); + } + + this.sessions.delete(sessionId); + return true; + } + + /** + * Get all active session IDs + */ + getActiveSessions(): string[] { + return Array.from(this.sessions.keys()); + } + + /** + * Get the page for a session + */ + async getPage(sessionId: string): Promise { + const session = this.sessions.get(sessionId); + if (!session) { + return undefined; + } + // Always get the active page in case it changed + return await session.stagehand.context.awaitActivePage(); + } + + /** + * Get the viewport for a session + */ + async getViewport(sessionId: string): Promise { + const session = this.sessions.get(sessionId); + if (!session) { + return undefined; + } + + const page = await session.stagehand.context.awaitActivePage(); + try { + const { w, h } = await page + .mainFrame() + .evaluate<{ + w: number; + h: number; + }>("({ w: window.innerWidth, h: window.innerHeight })"); + return { width: w, height: h }; + } catch { + return { width: 1280, height: 720 }; // Default fallback + } + } + + /** + * Destroy all sessions (cleanup on server shutdown) + */ + async destroyAllSessions(): Promise { + const sessionIds = Array.from(this.sessions.keys()); + await Promise.all(sessionIds.map((id) => this.destroySession(id))); + } +} + +// Singleton instance +export const sessionManager = new BrowserSessionManager(); diff --git a/environments/browser_env/cua-server/start.sh b/environments/browser_env/cua-server/start.sh new file mode 100755 index 000000000..1cdcbd775 --- /dev/null +++ b/environments/browser_env/cua-server/start.sh @@ -0,0 +1,145 @@ +#!/bin/bash + +# CUA Primitives API Server - Startup Script +# +# Usage: +# ./start.sh # Start with defaults +# ./start.sh --port 8080 # Custom port +# ./start.sh --host 127.0.0.1 # Custom host + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Check Node.js version (>= 18 required) +check_node_version() { + if ! command -v node &> /dev/null; then + echo -e "${RED}Error: Node.js is required but not installed.${NC}" + echo "Install it from: https://nodejs.org/ (v18 or higher)" + exit 1 + fi + + NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1) + if [ "$NODE_VERSION" -lt 18 ]; then + echo -e "${RED}Error: Node.js v18 or higher is required.${NC}" + echo "Current version: $(node -v)" + echo "Please upgrade Node.js from: https://nodejs.org/" + exit 1 + fi + echo -e "${GREEN}✓${NC} Node.js $(node -v) detected" +} + +# Check pnpm is installed +check_pnpm() { + if ! command -v pnpm &> /dev/null; then + echo -e "${RED}Error: pnpm is required but not installed.${NC}" + echo "" + echo "Install it with one of these methods:" + echo " npm install -g pnpm" + echo " brew install pnpm" + echo " curl -fsSL https://get.pnpm.io/install.sh | sh -" + echo "" + echo "More info: https://pnpm.io/installation" + exit 1 + fi + echo -e "${GREEN}✓${NC} pnpm $(pnpm -v) detected" +} + +# Install dependencies if needed +install_dependencies() { + if [ ! -d "node_modules" ]; then + echo "" + echo -e "${YELLOW}Installing dependencies...${NC}" + pnpm install + echo -e "${GREEN}✓${NC} Dependencies installed" + fi +} + +# Check for .env file +check_env_file() { + if [ ! -f ".env" ]; then + if [ -f "env.example" ]; then + echo -e "${YELLOW}Note: No .env file found. Using environment variables or defaults.${NC}" + echo " Copy env.example to .env and customize if needed." + fi + else + echo -e "${GREEN}✓${NC} .env file found" + fi +} + +# Default values +PORT="${CUA_SERVER_PORT:-3000}" +HOST="${CUA_SERVER_HOST:-0.0.0.0}" + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --port|-p) + PORT="$2" + shift 2 + ;; + --host|-h) + HOST="$2" + shift 2 + ;; + --help) + echo "CUA Primitives API Server" + echo "" + echo "Usage: ./start.sh [options]" + echo "" + echo "Options:" + echo " --port, -p PORT Server port (default: 3000)" + echo " --host, -h HOST Server host (default: 0.0.0.0)" + echo " --help Show this help message" + echo "" + echo "Environment variables:" + echo " CUA_SERVER_PORT Server port" + echo " CUA_SERVER_HOST Server host" + echo "" + echo "Prerequisites:" + echo " - Node.js v18 or higher" + echo " - pnpm package manager" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +echo "" +echo "╔════════════════════════════════════════════════════════════╗" +echo "║ CUA Primitives API Server - Setup ║" +echo "╚════════════════════════════════════════════════════════════╝" +echo "" + +# Run checks +check_node_version +check_pnpm +install_dependencies +check_env_file + +# Export environment variables +export CUA_SERVER_PORT="$PORT" +export CUA_SERVER_HOST="$HOST" + +echo "" +echo "Starting CUA Primitives API Server..." +echo " Host: $HOST" +echo " Port: $PORT" +echo "" + +# Run the server with .env file support if it exists +if [ -f ".env" ]; then + pnpm tsx --env-file=.env index.ts +else + pnpm tsx index.ts +fi diff --git a/environments/browser_env/cua-server/stateCapture.ts b/environments/browser_env/cua-server/stateCapture.ts new file mode 100644 index 000000000..ec67b3336 --- /dev/null +++ b/environments/browser_env/cua-server/stateCapture.ts @@ -0,0 +1,51 @@ +import type { Page } from "@browserbasehq/stagehand"; +import { BrowserState, Viewport } from "./types"; + +/** + * Get the current viewport dimensions from a page + */ +export async function getViewport(page: Page): Promise { + try { + const { w, h } = await page + .mainFrame() + .evaluate<{ + w: number; + h: number; + }>("({ w: window.innerWidth, h: window.innerHeight })"); + return { width: w, height: h }; + } catch { + // Default fallback if evaluation fails + return { width: 1280, height: 720 }; + } +} + +/** + * Take a screenshot and return as base64 string + */ +export async function takeScreenshot(page: Page): Promise { + const buffer = await page.screenshot({ fullPage: false }); + return buffer.toString("base64"); +} + +/** + * Get the current URL from a page + */ +export function getUrl(page: Page): string { + return page.url(); +} + +/** + * Capture the full browser state (screenshot, URL, viewport) + */ +export async function captureBrowserState(page: Page): Promise { + const [screenshot, viewport] = await Promise.all([ + takeScreenshot(page), + getViewport(page), + ]); + + return { + screenshot, + url: getUrl(page), + viewport, + }; +} diff --git a/environments/browser_env/cua-server/test.sh b/environments/browser_env/cua-server/test.sh new file mode 100755 index 000000000..70f0c269a --- /dev/null +++ b/environments/browser_env/cua-server/test.sh @@ -0,0 +1,177 @@ +#!/bin/bash + +# CUA Primitives API Server - Canary Test +# +# This script tests the basic functionality of the CUA server. +# Run after starting the server with ./start.sh +# +# Usage: +# ./test.sh # Test against localhost:3000 +# ./test.sh http://localhost:8080 # Custom server URL + +set -e + +BASE_URL="${1:-http://localhost:3000}" + +echo "============================================" +echo "CUA Primitives API Server - Canary Test" +echo "============================================" +echo "Server: $BASE_URL" +echo "" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +pass() { + echo -e "${GREEN}✓ PASS${NC}: $1" +} + +fail() { + echo -e "${RED}✗ FAIL${NC}: $1" + exit 1 +} + +info() { + echo -e "${YELLOW}→${NC} $1" +} + +# Test 1: Health check +info "Testing health endpoint..." +HEALTH=$(curl -s "$BASE_URL/health") +if echo "$HEALTH" | grep -q '"status":"ok"'; then + pass "Health check" +else + fail "Health check - unexpected response: $HEALTH" +fi + +# Test 2: List sessions (should be empty or have sessions) +info "Testing list sessions..." +SESSIONS=$(curl -s "$BASE_URL/sessions") +if echo "$SESSIONS" | grep -q '"sessions"'; then + pass "List sessions" +else + fail "List sessions - unexpected response: $SESSIONS" +fi + +# Test 3: Create a session +info "Creating browser session..." +CREATE_RESPONSE=$(curl -s -X POST "$BASE_URL/sessions" \ + -H "Content-Type: application/json" \ + -d '{"env": "LOCAL"}') + +SESSION_ID=$(echo "$CREATE_RESPONSE" | grep -o '"sessionId":"[^"]*"' | cut -d'"' -f4) + +if [ -z "$SESSION_ID" ]; then + fail "Create session - no sessionId in response: $CREATE_RESPONSE" +fi + +if echo "$CREATE_RESPONSE" | grep -q '"screenshot"'; then + pass "Create session (ID: $SESSION_ID)" +else + fail "Create session - missing screenshot in response" +fi + +# Test 4: Get session state +info "Getting session state..." +STATE=$(curl -s "$BASE_URL/sessions/$SESSION_ID/state") +if echo "$STATE" | grep -q '"screenshot"'; then + pass "Get session state" +else + fail "Get session state - unexpected response: $STATE" +fi + +# Test 5: Navigate to example.com +info "Testing goto action..." +GOTO_RESPONSE=$(curl -s -X POST "$BASE_URL/sessions/$SESSION_ID/action" \ + -H "Content-Type: application/json" \ + -d '{"type": "goto", "url": "https://example.com"}') + +if echo "$GOTO_RESPONSE" | grep -q '"success":true'; then + pass "Goto action" +else + fail "Goto action - unexpected response: $GOTO_RESPONSE" +fi + +# Verify URL changed +if echo "$GOTO_RESPONSE" | grep -q 'example.com'; then + pass "URL updated to example.com" +else + fail "URL not updated - response: $GOTO_RESPONSE" +fi + +# Test 6: Click action +info "Testing click action..." +CLICK_RESPONSE=$(curl -s -X POST "$BASE_URL/sessions/$SESSION_ID/action" \ + -H "Content-Type: application/json" \ + -d '{"type": "click", "x": 100, "y": 100}') + +if echo "$CLICK_RESPONSE" | grep -q '"success":true'; then + pass "Click action" +else + fail "Click action - unexpected response: $CLICK_RESPONSE" +fi + +# Test 7: Scroll action +info "Testing scroll action..." +SCROLL_RESPONSE=$(curl -s -X POST "$BASE_URL/sessions/$SESSION_ID/action" \ + -H "Content-Type: application/json" \ + -d '{"type": "scroll", "x": 640, "y": 360, "scroll_y": 100}') + +if echo "$SCROLL_RESPONSE" | grep -q '"success":true'; then + pass "Scroll action" +else + fail "Scroll action - unexpected response: $SCROLL_RESPONSE" +fi + +# Test 8: Wait action +info "Testing wait action..." +WAIT_RESPONSE=$(curl -s -X POST "$BASE_URL/sessions/$SESSION_ID/action" \ + -H "Content-Type: application/json" \ + -d '{"type": "wait", "timeMs": 500}') + +if echo "$WAIT_RESPONSE" | grep -q '"success":true'; then + pass "Wait action" +else + fail "Wait action - unexpected response: $WAIT_RESPONSE" +fi + +# Test 9: Keypress action +info "Testing keypress action..." +KEYPRESS_RESPONSE=$(curl -s -X POST "$BASE_URL/sessions/$SESSION_ID/action" \ + -H "Content-Type: application/json" \ + -d '{"type": "keypress", "keys": "Tab"}') + +if echo "$KEYPRESS_RESPONSE" | grep -q '"success":true'; then + pass "Keypress action" +else + fail "Keypress action - unexpected response: $KEYPRESS_RESPONSE" +fi + +# Test 10: Delete session +info "Deleting session..." +DELETE_RESPONSE=$(curl -s -X DELETE "$BASE_URL/sessions/$SESSION_ID") + +if echo "$DELETE_RESPONSE" | grep -q '"success":true'; then + pass "Delete session" +else + fail "Delete session - unexpected response: $DELETE_RESPONSE" +fi + +# Test 11: Verify session is gone +info "Verifying session deleted..." +GONE_RESPONSE=$(curl -s "$BASE_URL/sessions/$SESSION_ID/state") + +if echo "$GONE_RESPONSE" | grep -q 'SESSION_NOT_FOUND'; then + pass "Session properly deleted" +else + fail "Session still exists after deletion" +fi + +echo "" +echo "============================================" +echo -e "${GREEN}All tests passed!${NC}" +echo "============================================" + diff --git a/environments/browser_env/cua-server/tsconfig.json b/environments/browser_env/cua-server/tsconfig.json new file mode 100644 index 000000000..c0c0f5ca2 --- /dev/null +++ b/environments/browser_env/cua-server/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "declaration": false, + "outDir": "./dist", + "rootDir": "." + }, + "include": ["*.ts"], + "exclude": ["node_modules", "dist"] +} + diff --git a/environments/browser_env/cua-server/types.ts b/environments/browser_env/cua-server/types.ts new file mode 100644 index 000000000..7a046c3c0 --- /dev/null +++ b/environments/browser_env/cua-server/types.ts @@ -0,0 +1,115 @@ +import { Stagehand } from "@browserbasehq/stagehand"; +import type { Page } from "@browserbasehq/stagehand"; + +/** + * CUA Action Types - Browser primitives that can be executed + */ +export type ActionType = + | "click" + | "double_click" + | "doubleClick" + | "tripleClick" + | "type" + | "keypress" + | "scroll" + | "drag" + | "move" + | "goto" + | "back" + | "forward" + | "wait" + | "screenshot"; + +/** + * Action Request - Sent by external agent to execute a browser primitive + */ +export interface ActionRequest { + type: ActionType; + // Mouse/click params + x?: number; + y?: number; + button?: "left" | "right" | "middle"; + clickCount?: number; + // Type/keyboard params + text?: string; + keys?: string | string[]; + // Scroll params + scroll_x?: number; + scroll_y?: number; + // Navigation params + url?: string; + // Wait params + timeMs?: number; + // Drag params + path?: Array<{ x: number; y: number }>; +} + +/** + * Viewport dimensions + */ +export interface Viewport { + width: number; + height: number; +} + +/** + * Browser State - Full state returned after each action + */ +export interface BrowserState { + screenshot: string; // base64 PNG + url: string; + viewport: Viewport; +} + +/** + * Action Execution Result - Internal result from action executor + */ +export interface ActionExecutionResult { + success: boolean; + error?: string; +} + +/** + * Action Response - Full response sent back to external agent + */ +export interface ActionResponse { + success: boolean; + error?: string; + state: BrowserState; +} + +/** + * Session Create Request + */ +export interface SessionCreateRequest { + env?: "LOCAL" | "BROWSERBASE"; + browserbaseApiKey?: string; + browserbaseProjectId?: string; + viewport?: Viewport; +} + +/** + * Session Create Response + */ +export interface SessionCreateResponse { + sessionId: string; + state: BrowserState; +} + +/** + * Browser Session - Internal representation of an active session + */ +export interface BrowserSession { + id: string; + stagehand: Stagehand; + page: Page; + createdAt: Date; +} + +/** + * Error Response + */ +export interface ErrorResponse { + error: string; + code: string; +} From 187ed0039103acc3443aaea36a5c46bee25ae31d Mon Sep 17 00:00:00 2001 From: Filip Michalsky Date: Tue, 2 Dec 2025 20:35:34 +0000 Subject: [PATCH 2/4] more one shot --- environments/browser_env/README.md | 129 ++++++++ environments/browser_env/browser_env.py | 178 +++++++++++ environments/browser_env/pyproject.toml | 22 ++ verifiers/__init__.py | 3 + verifiers/envs/browser_env.py | 398 ++++++++++++++++++++++++ 5 files changed, 730 insertions(+) create mode 100644 environments/browser_env/README.md create mode 100644 environments/browser_env/browser_env.py create mode 100644 environments/browser_env/pyproject.toml create mode 100644 verifiers/envs/browser_env.py diff --git a/environments/browser_env/README.md b/environments/browser_env/README.md new file mode 100644 index 000000000..a0480f9fa --- /dev/null +++ b/environments/browser_env/README.md @@ -0,0 +1,129 @@ +# Browser Environment + +Vision-based browser control environment using CUA (Computer Use Agent) primitives. + +## Overview + +This environment provides browser automation tools for training and evaluating agents that can interact with web pages through: +- **Vision feedback**: Each action returns a screenshot of the page state +- **Tool-based actions**: Click, type, scroll, navigate, etc. +- **Session management**: One browser session per rollout + +## Prerequisites + +1. **Start the CUA Server** + +```bash +cd environments/browser_env/cua-server +pnpm install +pnpm start +``` + +The server runs on `http://localhost:3000` by default. + +2. **Install Dependencies** + +```bash +vf-install browser_env +``` + +## Usage + +### Basic Evaluation + +```bash +vf-eval browser_env -n 5 -m gpt-4o +``` + +### Programmatic Usage + +```python +import verifiers as vf + +env = vf.load_environment("browser_env", server_url="http://localhost:3000") +``` + +### Custom Configuration + +```python +from environments.browser_env.browser_env import load_environment + +env = load_environment( + server_url="http://localhost:3000", + env="LOCAL", # or "BROWSERBASE" for cloud browsers + viewport_width=1280, + viewport_height=720, + max_turns=20, + efficiency_weight=0.1, + task_completion_weight=1.0, +) +``` + +## Available Tools + +| Tool | Description | +|------|-------------| +| `click(x, y, button)` | Click at coordinates | +| `double_click(x, y)` | Double-click at coordinates | +| `type_text(text)` | Type text into focused element | +| `keypress(keys)` | Press keyboard key(s) | +| `scroll(x, y, scroll_x, scroll_y)` | Scroll at position | +| `goto(url)` | Navigate to URL | +| `back()` | Go back in history | +| `forward()` | Go forward in history | +| `wait(time_ms)` | Wait for specified time | +| `screenshot()` | Capture current page | + +## Reward Functions + +### Built-in Rewards + +1. **efficiency_reward** (weight: 0.1): Penalizes long rollouts. Fewer actions = higher reward. + +2. **task_completion_reward** (weight: 1.0): Placeholder for task-specific completion reward. + +### Custom Rewards + +Override `task_completion_reward` or add custom reward functions: + +```python +async def my_custom_reward(state: vf.State, **kwargs) -> float: + # Check if browser is on target URL + browser_state = state.get("browser_state", {}) + current_url = browser_state.get("url", "") + target_url = state.get("answer", "") + + if target_url in current_url: + return 1.0 + return 0.0 + +# Add to rubric +browser_rubric.add_reward_func(my_custom_reward, weight=1.0) +``` + +## Environment Variables + +- `BROWSERBASE_API_KEY`: API key for Browserbase cloud browsers (optional) +- `BROWSERBASE_PROJECT_ID`: Project ID for Browserbase (optional) + +## Architecture + +``` +┌─────────────────┐ HTTP/REST ┌──────────────────┐ +│ BrowserEnv │ ◄──────────────► │ CUA Server │ +│ (Python) │ │ (Fastify/TS) │ +└─────────────────┘ └──────────────────┘ + │ │ + │ ▼ + │ ┌──────────────────┐ + │ │ Stagehand │ + │ │ (Playwright) │ + ▼ └──────────────────┘ +┌─────────────────┐ │ +│ Model (LLM) │ ▼ +│ gpt-4o, etc. │ ┌──────────────────┐ +└─────────────────┘ │ Browser │ + │ (Chrome) │ + └──────────────────┘ +``` + diff --git a/environments/browser_env/browser_env.py b/environments/browser_env/browser_env.py new file mode 100644 index 000000000..6216e3565 --- /dev/null +++ b/environments/browser_env/browser_env.py @@ -0,0 +1,178 @@ +""" +Browser Environment for vision-based browser control. + +This environment uses a CUA (Computer Use Agent) server to provide +browser primitives (click, type, scroll, etc.) with screenshot feedback. + +Usage: + 1. Start the CUA server: + cd environments/browser_env/cua-server && pnpm start + + 2. Run evaluation: + vf-eval browser_env -n 5 -m gpt-4o +""" + +from typing import Literal + +from datasets import Dataset + +import verifiers as vf + +# Import BrowserEnv - will be available after adding to lazy imports +try: + from verifiers.envs.browser_env import BrowserEnv +except ImportError: + raise ImportError( + "BrowserEnv requires aiohttp. Install with: uv pip install aiohttp" + ) + + +# ==================== Custom Reward Functions ==================== + + +def efficiency_reward(state: vf.State, **kwargs) -> float: + """ + Reward for completing task efficiently (fewer actions = higher reward). + + Linear decay from 1.0 at 1 action to 0.0 at max_actions. + """ + max_actions = kwargs.get("max_actions", 20) + trajectory = state.get("trajectory", []) + num_actions = len(trajectory) + + if num_actions == 0: + return 0.0 + + # Linear decay: 1.0 at 1 action, 0.0 at max_actions + return max(0.0, 1.0 - (num_actions - 1) / max_actions) + + +async def task_completion_reward(state: vf.State, **kwargs) -> float: + """ + Placeholder reward for task completion. + + Override this function or add custom reward functions based on your task type: + - URL matching: Check if browser navigated to target URL + - Element presence: Check if specific element is visible in screenshot + - Goal completion: Use a judge model to evaluate task completion + - Text extraction: Check if model extracted correct information + + Returns: + float: 0.0 (placeholder - implement based on task requirements) + """ + # TODO: Implement based on task type + # Examples: + # - Check state["browser_state"]["url"] matches target + # - Use vision model to verify element presence + # - Compare extracted text to expected answer + return 0.0 + + +# ==================== Environment Loader ==================== + + +def load_environment( + server_url: str = "http://localhost:3000", + env: Literal["LOCAL", "BROWSERBASE"] = "LOCAL", + browserbase_api_key: str | None = None, + browserbase_project_id: str | None = None, + viewport_width: int = 1280, + viewport_height: int = 720, + max_turns: int = 20, + system_prompt: str | None = None, + efficiency_weight: float = 0.1, + task_completion_weight: float = 1.0, + **kwargs, +) -> vf.Environment: + """ + Load the Browser environment for vision-based browser control. + + Args: + server_url: URL of the CUA server (default: http://localhost:3000) + env: Browser environment type ("LOCAL" or "BROWSERBASE") + browserbase_api_key: API key for Browserbase (if env="BROWSERBASE") + browserbase_project_id: Project ID for Browserbase (if env="BROWSERBASE") + viewport_width: Browser viewport width in pixels + viewport_height: Browser viewport height in pixels + max_turns: Maximum number of actions per rollout + system_prompt: Custom system prompt (optional) + efficiency_weight: Weight for efficiency reward (default: 0.1) + task_completion_weight: Weight for task completion reward (default: 1.0) + **kwargs: Additional arguments passed to BrowserEnv + + Returns: + BrowserEnv instance configured with tools and rubrics + """ + # Default system prompt for browser agent + if system_prompt is None: + system_prompt = """You are a browser automation agent. You can control a web browser using the provided tools. + +Available tools: +- click(x, y, button): Click at coordinates +- double_click(x, y): Double-click at coordinates +- type_text(text): Type text into focused element +- keypress(keys): Press keyboard keys (e.g., "Enter", "Tab") +- scroll(x, y, scroll_x, scroll_y): Scroll at position +- goto(url): Navigate to URL +- back(): Go back in history +- forward(): Go forward in history +- wait(time_ms): Wait for specified milliseconds +- screenshot(): Capture current page state + +After each action, you will receive a screenshot showing the current page state. +Analyze the screenshot to determine your next action. + +Complete the given task efficiently using the minimum number of actions necessary.""" + + # Create placeholder dataset + # TODO: Replace with actual task dataset + dataset = Dataset.from_dict( + { + "prompt": [ + "Navigate to google.com and search for 'weather today'", + "Go to wikipedia.org and find the main page", + ], + "answer": [ + "weather search results", + "wikipedia main page", + ], + } + ) + + # Create parser (no special parsing needed for browser tasks) + parser = vf.Parser() + + # Create rubrics + # 1. ToolRubric for basic tool usage metrics + # Note: tools will be set by BrowserEnv, so we pass empty list here + # and the BrowserEnv's tools will be used for metrics + tool_rubric = vf.ToolRubric(tools=[]) + + # 2. Custom browser rubric with efficiency and task completion rewards + browser_rubric = vf.Rubric( + funcs=[efficiency_reward, task_completion_reward], + weights=[efficiency_weight, task_completion_weight], + parser=parser, + ) + + # 3. Combine rubrics + rubric = vf.RubricGroup(rubrics=[tool_rubric, browser_rubric]) + + # Create and return the environment + browser_env = BrowserEnv( + server_url=server_url, + env=env, + browserbase_api_key=browserbase_api_key, + browserbase_project_id=browserbase_project_id, + viewport_width=viewport_width, + viewport_height=viewport_height, + max_turns=max_turns, + dataset=dataset, + system_prompt=system_prompt, + parser=parser, + rubric=rubric, + **kwargs, + ) + + return browser_env + diff --git a/environments/browser_env/pyproject.toml b/environments/browser_env/pyproject.toml new file mode 100644 index 000000000..38f9da5bc --- /dev/null +++ b/environments/browser_env/pyproject.toml @@ -0,0 +1,22 @@ +[project] +name = "browser_env" +version = "0.1.0" +description = "Browser environment for vision-based browser control via CUA primitives" +tags = ["browser", "multi-turn", "vision", "tools", "eval"] +requires-python = ">=3.10" +dependencies = [ + "verifiers>=0.1.5.post0", + "aiohttp>=3.9.0", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build] +include = ["browser_env.py", "pyproject.toml"] + +[tool.verifiers.eval] +num_examples = 5 +rollouts_per_example = 1 + diff --git a/verifiers/__init__.py b/verifiers/__init__.py index d62e6562f..9a1f71cb3 100644 --- a/verifiers/__init__.py +++ b/verifiers/__init__.py @@ -92,6 +92,7 @@ def setup_logging( "SingleTurnEnv", "PythonEnv", "SandboxEnv", + "BrowserEnv", "StatefulToolEnv", "ToolEnv", "EnvGroup", @@ -125,6 +126,7 @@ def setup_logging( "lora_defaults": "verifiers.rl.trainer:lora_defaults", "MathRubric": "verifiers.rubrics.math_rubric:MathRubric", "SandboxEnv": "verifiers.envs.sandbox_env:SandboxEnv", + "BrowserEnv": "verifiers.envs.browser_env:BrowserEnv", "PythonEnv": "verifiers.envs.python_env:PythonEnv", "ReasoningGymEnv": "verifiers.envs.reasoninggym_env:ReasoningGymEnv", "TextArenaEnv": "verifiers.envs.textarena_env:TextArenaEnv", @@ -145,6 +147,7 @@ def __getattr__(name: str): if TYPE_CHECKING: + from .envs.browser_env import BrowserEnv # noqa: F401 from .envs.python_env import PythonEnv # noqa: F401 from .envs.reasoninggym_env import ReasoningGymEnv # noqa: F401 from .envs.sandbox_env import SandboxEnv # noqa: F401 diff --git a/verifiers/envs/browser_env.py b/verifiers/envs/browser_env.py new file mode 100644 index 000000000..573485ead --- /dev/null +++ b/verifiers/envs/browser_env.py @@ -0,0 +1,398 @@ +import asyncio +import logging +from typing import Any, Literal + +import tenacity as tc + +import verifiers as vf + +try: + import aiohttp +except ImportError: + raise ImportError( + "aiohttp is not installed. Please install it with `uv pip install aiohttp`." + ) + + +class BrowserEnv(vf.StatefulToolEnv): + """ + Browser environment that connects to a CUA (Computer Use Agent) server + for vision-based browser control via tool calls. + + Each rollout gets its own browser session. Tools return both text summaries + and base64 screenshots as image_url content. + """ + + def __init__( + self, + server_url: str = "http://localhost:3000", + env: Literal["LOCAL", "BROWSERBASE"] = "LOCAL", + browserbase_api_key: str | None = None, + browserbase_project_id: str | None = None, + viewport_width: int = 1280, + viewport_height: int = 720, + max_retries: int = 5, + base_delay: float = 0.5, + backoff_factor: float = 2.0, + max_backoff_seconds: float = 30.0, + jitter: float = 1e-3, + **kwargs, + ): + super().__init__(**kwargs) + self.server_url = server_url.rstrip("/") + self.session_config = { + "env": env, + "browserbaseApiKey": browserbase_api_key, + "browserbaseProjectId": browserbase_project_id, + "viewport": {"width": viewport_width, "height": viewport_height}, + } + # Remove None values from config + self.session_config = { + k: v for k, v in self.session_config.items() if v is not None + } + + self.active_sessions: set[str] = set() + self._http_client: aiohttp.ClientSession | None = None + + # Retry configuration + self.with_retry = tc.AsyncRetrying( + stop=tc.stop_after_attempt(max_retries), + wait=tc.wait_exponential_jitter( + initial=base_delay, + exp_base=backoff_factor, + max=max_backoff_seconds, + jitter=jitter, + ), + before_sleep=tc.before_sleep_log(self.logger, logging.ERROR), + reraise=True, + ).wraps + + # Register browser primitive tools + self.add_tool(self.click, args_to_skip=["session_id"]) + self.add_tool(self.double_click, args_to_skip=["session_id"]) + self.add_tool(self.type_text, args_to_skip=["session_id"]) + self.add_tool(self.keypress, args_to_skip=["session_id"]) + self.add_tool(self.scroll, args_to_skip=["session_id"]) + self.add_tool(self.goto, args_to_skip=["session_id"]) + self.add_tool(self.back, args_to_skip=["session_id"]) + self.add_tool(self.forward, args_to_skip=["session_id"]) + self.add_tool(self.wait, args_to_skip=["session_id"]) + self.add_tool(self.screenshot, args_to_skip=["session_id"]) + + async def _get_client(self) -> aiohttp.ClientSession: + """Get or create the HTTP client session.""" + if self._http_client is None or self._http_client.closed: + self._http_client = aiohttp.ClientSession() + return self._http_client + + async def _create_session(self) -> dict: + """Create a new browser session via the CUA server.""" + client = await self._get_client() + async with client.post( + f"{self.server_url}/sessions", + json=self.session_config, + ) as resp: + if resp.status != 200: + error_text = await resp.text() + raise RuntimeError(f"Failed to create browser session: {error_text}") + return await resp.json() + + async def _destroy_session(self, session_id: str) -> None: + """Destroy a browser session via the CUA server.""" + client = await self._get_client() + async with client.delete(f"{self.server_url}/sessions/{session_id}") as resp: + if resp.status not in (200, 404): + error_text = await resp.text() + self.logger.warning( + f"Failed to destroy session {session_id}: {error_text}" + ) + + async def _execute_action(self, session_id: str, action: dict) -> dict: + """Execute a browser action and return the response with state.""" + client = await self._get_client() + async with client.post( + f"{self.server_url}/sessions/{session_id}/action", + json=action, + ) as resp: + return await resp.json() + + async def _get_state(self, session_id: str) -> dict: + """Get the current browser state (screenshot, URL, viewport).""" + client = await self._get_client() + async with client.get( + f"{self.server_url}/sessions/{session_id}/state" + ) as resp: + return await resp.json() + + def _format_response(self, response: dict) -> list[dict]: + """ + Format action response as multipart content with text and image. + + Returns OpenAI-compatible content array with text summary and screenshot. + """ + success = response.get("success", False) + error = response.get("error") + state = response.get("state", {}) + screenshot_b64 = state.get("screenshot", "") + url = state.get("url", "") + viewport = state.get("viewport", {}) + + # Build text summary + status = "Success" if success else "Failed" + text_parts = [f"Status: {status}"] + if error: + text_parts.append(f"Error: {error}") + if url: + text_parts.append(f"URL: {url}") + if viewport: + text_parts.append(f"Viewport: {viewport.get('width', 0)}x{viewport.get('height', 0)}") + + content = [{"type": "text", "text": "\n".join(text_parts)}] + + # Add screenshot if available + if screenshot_b64: + content.append( + { + "type": "image_url", + "image_url": {"url": f"data:image/png;base64,{screenshot_b64}"}, + } + ) + + return content + + # ==================== Lifecycle Methods ==================== + + async def setup_state(self, state: vf.State, **kwargs) -> vf.State: + """Create a browser session for this rollout.""" + result = await self.with_retry(self._create_session)() + session_id = result.get("sessionId") + if not session_id: + raise RuntimeError("Failed to get session ID from server response") + + self.active_sessions.add(session_id) + self.logger.debug(f"Created browser session {session_id}") + + state["session_id"] = session_id + # Store initial state (screenshot, URL, viewport) + state["browser_state"] = result.get("state", {}) + + return await super().setup_state(state, **kwargs) + + @vf.cleanup + async def destroy_session(self, state: vf.State): + """Destroy the browser session after rollout completion.""" + session_id = state.get("session_id") + if session_id is None: + return + + try: + await self.with_retry(self._destroy_session)(session_id) + self.active_sessions.discard(session_id) + self.logger.debug(f"Destroyed browser session {session_id}") + except Exception as e: + self.logger.warning(f"Failed to destroy session {session_id}: {e}") + + @vf.teardown + async def teardown_sessions(self, max_concurrent: int = 50): + """Destroy all active browser sessions on exit.""" + if len(self.active_sessions) == 0: + return + + self.logger.info(f"Destroying {len(self.active_sessions)} remaining sessions") + + semaphore = asyncio.Semaphore(max_concurrent) + + async def _delete_with_semaphore(session_id: str): + async with semaphore: + try: + await self.with_retry(self._destroy_session)(session_id) + self.active_sessions.discard(session_id) + self.logger.debug(f"Destroyed session {session_id}") + except Exception as e: + self.logger.warning(f"Failed to destroy session {session_id}: {e}") + + await asyncio.gather( + *[ + _delete_with_semaphore(session_id) + for session_id in list(self.active_sessions) + ] + ) + + # Close HTTP client + if self._http_client and not self._http_client.closed: + await self._http_client.close() + + def update_tool_args( + self, + tool_name: str, + tool_args: dict[str, Any], + messages: vf.Messages, + state: vf.State, + **kwargs, + ) -> dict[str, Any]: + """Inject session_id into all browser tool calls.""" + updated_args = dict(tool_args) + updated_args["session_id"] = state["session_id"] + return updated_args + + # ==================== Browser Primitive Tools ==================== + + async def click( + self, + x: int, + y: int, + button: Literal["left", "right", "middle"] = "left", + session_id: str = "", + ) -> list[dict]: + """ + Click at coordinates (x, y) on the page. + + Args: + x: The x coordinate to click + y: The y coordinate to click + button: Mouse button to use (left, right, or middle) + + Returns: + Status message and screenshot of the page after clicking + """ + response = await self._execute_action( + session_id, {"type": "click", "x": x, "y": y, "button": button} + ) + return self._format_response(response) + + async def double_click( + self, x: int, y: int, session_id: str = "" + ) -> list[dict]: + """ + Double-click at coordinates (x, y) on the page. + + Args: + x: The x coordinate to double-click + y: The y coordinate to double-click + + Returns: + Status message and screenshot of the page after double-clicking + """ + response = await self._execute_action( + session_id, {"type": "double_click", "x": x, "y": y} + ) + return self._format_response(response) + + async def type_text(self, text: str, session_id: str = "") -> list[dict]: + """ + Type text into the currently focused element. + + Args: + text: The text to type + + Returns: + Status message and screenshot of the page after typing + """ + response = await self._execute_action( + session_id, {"type": "type", "text": text} + ) + return self._format_response(response) + + async def keypress( + self, keys: str | list[str], session_id: str = "" + ) -> list[dict]: + """ + Press keyboard key(s). + + Args: + keys: Key name(s) to press (e.g., "Enter", "Tab", ["Control", "a"]) + + Returns: + Status message and screenshot of the page after key press + """ + response = await self._execute_action( + session_id, {"type": "keypress", "keys": keys} + ) + return self._format_response(response) + + async def scroll( + self, + x: int = 0, + y: int = 0, + scroll_x: int = 0, + scroll_y: int = 0, + session_id: str = "", + ) -> list[dict]: + """ + Scroll the page at a specific position. + + Args: + x: X coordinate to scroll at (default: 0) + y: Y coordinate to scroll at (default: 0) + scroll_x: Horizontal scroll amount in pixels (positive = right) + scroll_y: Vertical scroll amount in pixels (positive = down) + + Returns: + Status message and screenshot of the page after scrolling + """ + response = await self._execute_action( + session_id, + {"type": "scroll", "x": x, "y": y, "scroll_x": scroll_x, "scroll_y": scroll_y}, + ) + return self._format_response(response) + + async def goto(self, url: str, session_id: str = "") -> list[dict]: + """ + Navigate to a URL. + + Args: + url: The URL to navigate to + + Returns: + Status message and screenshot of the page after navigation + """ + response = await self._execute_action( + session_id, {"type": "goto", "url": url} + ) + return self._format_response(response) + + async def back(self, session_id: str = "") -> list[dict]: + """ + Navigate back in browser history. + + Returns: + Status message and screenshot of the page after going back + """ + response = await self._execute_action(session_id, {"type": "back"}) + return self._format_response(response) + + async def forward(self, session_id: str = "") -> list[dict]: + """ + Navigate forward in browser history. + + Returns: + Status message and screenshot of the page after going forward + """ + response = await self._execute_action(session_id, {"type": "forward"}) + return self._format_response(response) + + async def wait(self, time_ms: int = 1000, session_id: str = "") -> list[dict]: + """ + Wait for a specified amount of time. + + Args: + time_ms: Time to wait in milliseconds (default: 1000) + + Returns: + Status message and screenshot of the page after waiting + """ + response = await self._execute_action( + session_id, {"type": "wait", "timeMs": time_ms} + ) + return self._format_response(response) + + async def screenshot(self, session_id: str = "") -> list[dict]: + """ + Capture a screenshot of the current page state. + + Returns: + Status message and screenshot of the current page + """ + response = await self._execute_action(session_id, {"type": "screenshot"}) + return self._format_response(response) + From 7e9b08319d0c810afd7593b22749aaa70eba51a3 Mon Sep 17 00:00:00 2001 From: Filip Michalsky Date: Tue, 2 Dec 2025 20:42:07 +0000 Subject: [PATCH 3/4] update readme --- environments/browser_env/README.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/environments/browser_env/README.md b/environments/browser_env/README.md index a0480f9fa..e8e28a918 100644 --- a/environments/browser_env/README.md +++ b/environments/browser_env/README.md @@ -106,18 +106,25 @@ browser_rubric.add_reward_func(my_custom_reward, weight=1.0) - `BROWSERBASE_API_KEY`: API key for Browserbase cloud browsers (optional) - `BROWSERBASE_PROJECT_ID`: Project ID for Browserbase (optional) + +# TODO + +- Reward function - overall browser trajectory + "how are we getting closer after each step" (hard-ish) +- Custom LLM client - via vLLM (easy) +- DOM-based option - I started with CUA (vision-based) since there seems to be more near-term market demand with full understanding verifier's has been not focused on multimodal training much yet (afaik) + ## Architecture ``` ┌─────────────────┐ HTTP/REST ┌──────────────────┐ -│ BrowserEnv │ ◄──────────────► │ CUA Server │ +│ BrowserEnv │ ◄──────────────► │ CUA Server │ │ (Python) │ │ (Fastify/TS) │ └─────────────────┘ └──────────────────┘ │ │ │ ▼ │ ┌──────────────────┐ - │ │ Stagehand │ - │ │ (Playwright) │ + │ │ Stagehand V3 │ + │ │ (Direct CDP) │ ▼ └──────────────────┘ ┌─────────────────┐ │ │ Model (LLM) │ ▼ From 20a039a15ba3a036047fdde010eeb2018bfc37a3 Mon Sep 17 00:00:00 2001 From: Filip Michalsky Date: Tue, 2 Dec 2025 20:48:21 +0000 Subject: [PATCH 4/4] update readme --- environments/browser_env/README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/environments/browser_env/README.md b/environments/browser_env/README.md index e8e28a918..49122792f 100644 --- a/environments/browser_env/README.md +++ b/environments/browser_env/README.md @@ -74,7 +74,7 @@ env = load_environment( | `wait(time_ms)` | Wait for specified time | | `screenshot()` | Capture current page | -## Reward Functions +## Reward Functions # TODO ### Built-in Rewards @@ -82,7 +82,7 @@ env = load_environment( 2. **task_completion_reward** (weight: 1.0): Placeholder for task-specific completion reward. -### Custom Rewards +### Custom Rewards Override `task_completion_reward` or add custom reward functions: @@ -108,17 +108,21 @@ browser_rubric.add_reward_func(my_custom_reward, weight=1.0) # TODO +###### - Reward function - overall browser trajectory + "how are we getting closer after each step" (hard-ish) - Custom LLM client - via vLLM (easy) - DOM-based option - I started with CUA (vision-based) since there seems to be more near-term market demand with full understanding verifier's has been not focused on multimodal training much yet (afaik) +- Dataset structure (get some examples from our evals suite in Stagehand) +- ## Architecture ``` ┌─────────────────┐ HTTP/REST ┌──────────────────┐ -│ BrowserEnv │ ◄──────────────► │ CUA Server │ -│ (Python) │ │ (Fastify/TS) │ +│ BrowserEnv │ ◄──────────────► │ CUA Server │ +│ (Python/ │ │ (Fastify/TS) │ +│ verifiers) │ │ │ └─────────────────┘ └──────────────────┘ │ │ │ ▼