diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d61a739 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,27 @@ +name: CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install pnpm + uses: pnpm/action-setup@v4 + - name: Use Node.js 22 + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'pnpm' + - name: Install dependencies + run: pnpm install --frozen-lockfile + - run: pnpm run build + - name: Run ESLint + run: pnpm run lint + - name: Ensure no changes + run: git diff --exit-code \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..9ac15ea --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,24 @@ +name: Publish +on: + release: + types: [published] +jobs: + publish-npm: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + registry-url: https://registry.npmjs.org/ + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 8 + - run: pnpm install --frozen-lockfile + - run: pnpm publish --provenance + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7a55993..15fe3ba 100644 --- a/.gitignore +++ b/.gitignore @@ -292,13 +292,5 @@ cython_debug/ .DS_Store -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ - - # Smithery -/browserbase/.smithery \ No newline at end of file +/.smithery \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..0312b76 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npx lint-staged \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..ca2770b --- /dev/null +++ b/.npmrc @@ -0,0 +1,4 @@ +# Use pnpm for package management +engine-strict=true +auto-install-peers=true +strict-peer-dependencies=false diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..867e7cc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,33 @@ +# ----- Build Stage ----- +FROM node:lts-alpine AS builder +WORKDIR /app + +# Copy package files first for dependency caching +COPY package.json pnpm-lock.yaml ./ +RUN corepack enable && pnpm install --frozen-lockfile + +# Copy configuration and source code +COPY tsconfig.json ./ +COPY src ./src +COPY config.d.ts index.d.ts ./ + +# Build the application +RUN pnpm build + +# ----- Production Stage ----- +FROM node:lts-alpine +WORKDIR /app + +# Copy package files and install production dependencies +COPY package.json pnpm-lock.yaml ./ +RUN corepack enable && pnpm install --prod --frozen-lockfile --ignore-scripts + +# Copy built artifacts and required files +COPY --from=builder /app/dist ./dist +COPY index.js config.d.ts index.d.ts cli.js ./ + +# Expose HTTP port +EXPOSE 8080 + +# Default command using CLI flags +CMD ["node", "cli.js", "--port", "8080"] \ No newline at end of file diff --git a/README.md b/README.md index de0de0b..e66dbbf 100644 --- a/README.md +++ b/README.md @@ -4,38 +4,635 @@ ![cover](assets/cover-mcp.png) -[The Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is an open protocol that enables seamless integration between LLM applications and external data sources and tools. Whether you’re building an AI-powered IDE, enhancing a chat interface, or creating custom AI workflows, MCP provides a standardized way to connect LLMs with the context they need. +[The Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is an open protocol that enables seamless integration between LLM applications and external data sources and tools. Whether you're building an AI-powered IDE, enhancing a chat interface, or creating custom AI workflows, MCP provides a standardized way to connect LLMs with the context they need. -This server provides cloud browser automation capabilities using [Browserbase](https://www.browserbase.com/) and [Stagehand](https://github.com/browserbase/stagehand). This server enables LLMs to interact with web pages, take screenshots, and execute JavaScript in a cloud browser environment. +This server provides cloud browser automation capabilities using [Browserbase](https://www.browserbase.com/) and [Stagehand](https://github.com/browserbase/stagehand). It enables LLMs to interact with web pages, take screenshots, extract information, and perform automated actions with atomic precision. -To learn to get started with Browserbase, check out [Browserbase MCP](./browserbase/README.md) or [Stagehand MCP](./stagehand/README.md). +## Features -## Getting Started with available MCPs +| Feature | Description | +| ------------------ | ----------------------------------------------------------- | +| Browser Automation | Control and orchestrate cloud browsers via Browserbase | +| Data Extraction | Extract structured data from any webpage | +| Web Interaction | Navigate, click, and fill forms with ease | +| Screenshots | Capture full-page and element screenshots | +| Model Flexibility | Supports multiple models (OpenAI, Claude, Gemini, and more) | +| Vision Support | Use annotated screenshots for complex DOMs | +| Session Management | Create, manage, and close browser sessions | +| Multi-Session | Run multiple browser sessions in parallel | -🌐 **Browserbase MCP** - Located in [`browserbase/`](./browserbase/) +### Alternative Installation Methods -| Feature | Description | -| ------------------ | ----------------------------------------- | -| Browser Automation | Control and orchestrate cloud browsers | -| Data Extraction | Extract structured data from any webpage | -| Console Monitoring | Track and analyze browser console logs | -| Screenshots | Capture full-page and element screenshots | -| Web Interaction | Navigate, click, and fill forms with ease | +[Smithery](https://smithery.ai/server/@browserbasehq/mcp-browserbase) -🀘 **Stagehand MCP** - Located in [`stagehand/`](./stagehand/) +## Prerequisites -| Feature | Description | -| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Atomic Instructions | Execute precise actions like `act("click the login button")` or `extract("find the red shoes")` | -| Model Flexibility | Supports multiple models, including OpenAI's GPT-4 and Anthropic's Claude-3.7 Sonnet | -| Modular Design | Easily integrate new models with minimal changes | -| Vision Support | Use annotated screenshots for complex DOMs | -| Open Source | Contribute to the project and join the [Slack community](https://join.slack.com/t/stagehand-dev/shared_invite/zt-2uvuobu50-~wVSx2Si75CPa3332hwVEw) for support | +This project uses [pnpm](https://pnpm.io/) as the package manager. If you don't have pnpm installed, you can install it via: -### Alternative Installation Methods +```bash +npm install -g pnpm +# or +curl -fsSL https://get.pnpm.io/install.sh | sh +``` -[Smithery](https://smithery.ai/server/@browserbasehq/mcp-browserbase) +## How to Setup + +### Quickstarts: + +[![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.png)](cursor://anysphere.cursor-deeplink/mcp/install?name=browserbase&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyJAYnJvd3NlcmJhc2VocS9tY3AiXSwiZW52Ijp7IkJST1dTRVJCQVNFX0FQSV9LRVkiOiIiLCJCUk9XU0VSQkFTRV9QUk9KRUNUX0lEIjoiIn19) + +You can either use our Server hosted on NPM or run it completely locally by cloning this repo. + +### To run on NPM (Recommended) + +Go into your MCP Config JSON and add the Browserbase Server: + +```json +{ + "mcpServers": { + "browserbase": { + "command": "npx", + "args": ["@browserbasehq/mcp"], + "env": { + "BROWSERBASE_API_KEY": "", + "BROWSERBASE_PROJECT_ID": "" + } + } + } +} +``` + +That's it! Reload your MCP client and Claude will be able to use Browserbase. + +### To run 100% local: + +```bash +# Clone the Repo +git clone https://github.com/browserbase/mcp-server-browserbase.git +cd mcp-server-browserbase + +# Install the dependencies and build the project +pnpm install && pnpm build +``` + +Then in your MCP Config JSON run the server. To run locally we can use STDIO or self-host SHTTP. + +### STDIO: + +To your MCP Config JSON file add the following: + +```json +{ + "mcpServers": { + "browserbase": { + "command": "node", + "args": ["/path/to/mcp-server-browserbase/cli.js"], + "env": { + "BROWSERBASE_API_KEY": "", + "BROWSERBASE_PROJECT_ID": "" + } + } + } +} +``` + +### SSE: + +Run the following command in your terminal. You can add any flags (see options below) that you see fit to customize your configuration. + +```bash + node cli.js --port 8931 +``` + +Then in your MCP Config JSON file put the following: + +```json +{ + "mcpServers": { + "browserbase": { + "url": "http://localhost:8931/mcp", + "env": { + "BROWSERBASE_API_KEY": "", + "BROWSERBASE_PROJECT_ID": "" + } + } + } +} +``` + +Then reload your MCP client and you should be good to go! + +## Development + +### Getting Started + +1. **Clone the repository:** + + ```bash + git clone https://github.com/browserbase/mcp-server-browserbase.git + cd mcp-server-browserbase + ``` + +2. **Install dependencies:** + + ```bash + pnpm install + ``` + +3. **Build the project:** + + ```bash + pnpm run build + ``` + +4. **Run in development mode:** + ```bash + pnpm run watch + ``` + +### Available Scripts + +- `pnpm build` - Build the TypeScript project +- `pnpm watch` - Watch for changes and rebuild +- `pnpm lint` - Run ESLint +- `pnpm prettier:check` - Check code formatting +- `pnpm prettier:fix` - Fix code formatting +- `pnpm clean` - Clean build artifacts +- `pnpm publish` - Build and publish to npm registry + +### Publishing + +To publish a new version: + +```bash +pnpm run publish +``` + +This will clean, build, and publish the package using pnpm's built-in publishing capabilities. + +## Configuration + +The Browserbase MCP server accepts the following command-line flags: + +| Flag | Description | +| ----------------------------- | --------------------------------------------------------------------------- | +| `--browserbaseApiKey ` | Your Browserbase API key for authentication | +| `--browserbaseProjectId ` | Your Browserbase project ID | +| `--proxies` | Enable Browserbase proxies for the session | +| `--advancedStealth` | Enable Browserbase Advanced Stealth (Only for Scale Plan Users) | +| `--contextId ` | Specify a Browserbase Context ID to use | +| `--persist [boolean]` | Whether to persist the Browserbase context (default: true) | +| `--port ` | Port to listen on for HTTP/SHTTP transport | +| `--host ` | Host to bind server to (default: localhost, use 0.0.0.0 for all interfaces) | +| `--cookies [json]` | JSON array of cookies to inject into the browser | +| `--browserWidth ` | Browser viewport width (default: 1024) | +| `--browserHeight ` | Browser viewport height (default: 768) | +| `--modelName ` | The model to use for Stagehand (default: google/gemini-2.0-flash) | +| `--modelApiKey ` | API key for the custom model provider (required when using custom models) | + +These flags can be passed directly to the CLI or configured in your MCP configuration file. + +### NOTE: + +Currently, these flags can only be used with the local server (npx @browserbasehq/mcp). + +## Configuration Examples + +### Proxies + +Here are our docs on [Proxies](https://docs.browserbase.com/features/proxies). + +To use proxies, set the --proxies flag in your MCP Config: + +```json +{ + "mcpServers": { + "browserbase": { + "command": "npx", + "args": ["@browserbasehq/mcp", "--proxies"], + "env": { + "BROWSERBASE_API_KEY": "", + "BROWSERBASE_PROJECT_ID": "" + } + } + } +} +``` + +### Advanced Stealth + +Here are our docs on [Advanced Stealth](https://docs.browserbase.com/features/stealth-mode#advanced-stealth-mode). + +To use advanced stealth, set the --advancedStealth flag in your MCP Config: + +```json +{ + "mcpServers": { + "browserbase": { + "command": "npx", + "args": ["@browserbasehq/mcp", "--advancedStealth"], + "env": { + "BROWSERBASE_API_KEY": "", + "BROWSERBASE_PROJECT_ID": "" + } + } + } +} +``` + +### Contexts + +Here are our docs on [Contexts](https://docs.browserbase.com/features/contexts) + +To use contexts, set the --contextId flag in your MCP Config: + +```json +{ + "mcpServers": { + "browserbase": { + "command": "npx", + "args": ["@browserbasehq/mcp", "--contextId", ""], + "env": { + "BROWSERBASE_API_KEY": "", + "BROWSERBASE_PROJECT_ID": "" + } + } + } +} +``` + +### Cookie Injection + +Why would you need to inject cookies? Our context API currently works on persistent cookies, but not session cookies. So sometimes our persistent auth might not work (we're working hard to add this functionality). + +You can inject cookies into the MCP by adding them to your MCP Config. Your cookies JSON must be in the format of [Playwright Cookies](https://playwright.dev/docs/api/class-browsercontext#browser-context-cookies) + +```json +{ + "mcpServers": { + "browserbase": { + "command": "npx", + "args": [ + "@browserbasehq/mcp", + "--cookies", + "[{\"name\": \"session\", \"value\": \"abc123\", \"domain\": \".example.com\"}]" + ], + "env": { + "BROWSERBASE_API_KEY": "", + "BROWSERBASE_PROJECT_ID": "" + } + } + } +} +``` + +### Browser Viewport Sizing + +The default viewport sizing for a browser session is 1024 x 768. You can adjust the Browser viewport sizing with browserWidth and browserHeight flags. + +Here's how to use it for custom browser sizing. We recommend to stick with 16:9 aspect ratios (ie: 1920 x 1080, 1280 x 720, 1024 x 768) + +```json +{ + "mcpServers": { + "browserbase": { + "command": "npx", + "args": [ + "@browserbasehq/mcp", + "--browserHeight 1080", + "--browserWidth 1920" + ], + "env": { + "BROWSERBASE_API_KEY": "", + "BROWSERBASE_PROJECT_ID": "" + } + } + } +} +``` + +### Model Configuration + +Stagehand defaults to using Google's Gemini 2.0 Flash model, but you can configure it to use other models like GPT-4o, Claude, or other providers. + +**Important**: When using any custom model (non-default), you must provide your own API key for that model provider using the `--modelApiKey` flag. + +Here's how to configure different models: + +```json +{ + "mcpServers": { + "browserbase": { + "command": "npx", + "args": [ + "@browserbasehq/mcp", + "--modelName", + "gpt-4o", + "--modelApiKey", + "your-openai-api-key" + ], + "env": { + "BROWSERBASE_API_KEY": "", + "BROWSERBASE_PROJECT_ID": "" + } + } + } +} +``` + +For Claude models: + +```json +{ + "mcpServers": { + "browserbase": { + "command": "npx", + "args": [ + "@browserbasehq/mcp", + "--modelName", + "claude-3-5-sonnet-latest", + "--modelApiKey", + "your-anthropic-api-key" + ], + "env": { + "BROWSERBASE_API_KEY": "", + "BROWSERBASE_PROJECT_ID": "" + } + } + } +} +``` + +Available models include: + +- **Gemini**: `google/gemini-2.0-flash` (default), `google/gemini-1.5-pro`, `google/gemini-1.5-flash` +- **OpenAI**: `gpt-4o`, `gpt-4o-mini`, `o1-mini`, `o1-preview`, `o3-mini` +- **Claude**: `claude-3-5-sonnet-latest`, `claude-3-7-sonnet-latest` +- **Other providers**: Cerebras, Groq, and more + +_Note: The model must be supported in Stagehand. Check out the docs [here](https://docs.stagehand.dev/examples/custom_llms#supported-llms). When using any custom model, you must provide your own API key for that provider._ + +## Tools + +The Browserbase MCP server provides the following tools for browser automation: + +### Browser Automation Tools + +- **browserbase_stagehand_navigate** + - Navigate to any URL in the browser + - Input: + - `url` (string): The URL to navigate to + +- **browserbase_stagehand_act** + - Perform an action on the web page using natural language + - Inputs: + - `action` (string): The action to perform (e.g., "click the login button") + - `variables` (object, optional): Variables used in the action template for sensitive data + +- **browserbase_stagehand_extract** + - Extract all text content from the current page (filters out CSS and JavaScript) + - No inputs required + +- **browserbase_stagehand_observe** + - Observe and find actionable elements on the web page + - Input: + - `instruction` (string): Specific instruction for observation (e.g., "find the login button") + +- **browserbase_screenshot** + - Capture a PNG screenshot of the current page + - No inputs required + - Output: + - `text`: Friendly confirmation message with the screenshot name + - `image`: Base-64 encoded PNG data + +### Session Management Tools + +- **browserbase_session_create** + - Create or reuse a cloud browser session using Browserbase with fully initialized Stagehand + - Applies all configuration flags (proxies, stealth, viewport, cookies, etc.) + - Initializes Stagehand instance connected to the browser session + - Input: + - `sessionId` (string, optional): Optional session ID to use/reuse. If not provided, creates new session + - Output: + - Live debugger URL for the Browserbase session + +- **browserbase_session_close** + - Close the current Browserbase session, disconnect the browser, and cleanup Stagehand instance + - Input: + - `random_string` (string, optional): Dummy parameter for consistent tool call format + - Output: + - Confirmation message and session replay URL + +### Multi-Session Management Tools + +The server supports managing multiple independent browser sessions in parallel, allowing you to control multiple browsers simultaneously for complex automation workflows: + +#### Session Lifecycle Management + +- **multi_browserbase_stagehand_session_create** + - Create a new independent Stagehand browser session with full web automation capabilities + - Each session is isolated with its own browser instance, cookies, and state + - Inputs: + - `name` (string, optional): Human-readable name for tracking (e.g., 'login-flow', 'data-scraping') + - `browserbaseSessionID` (string, optional): Resume an existing Browserbase session by ID + - `browserbaseSessionCreateParams` (object, optional): Advanced Browserbase configuration + - Output: + - Session ID and Browserbase session ID with live debugger URL + +- **multi_browserbase_stagehand_session_list** + - List all currently active Stagehand browser sessions with detailed metadata + - Shows session IDs, names, Browserbase session IDs, creation time, and age + - No inputs required + - Output: + - Comprehensive list of active sessions with status information + +- **multi_browserbase_stagehand_session_close** + - Close and clean up a specific Stagehand browser session + - Terminates browser instance, ends Browserbase session, and frees resources + - Input: + - `sessionId` (string): Exact session ID to close (cannot be undone) + - Output: + - Confirmation message with session replay URL + +#### Session-Specific Browser Automation + +All core browser automation tools are available with session-specific variants: + +- **multi_browserbase_stagehand_navigate_session** + - Navigate to a URL in a specific browser session + - Inputs: + - `sessionId` (string): The session ID to use + - `url` (string): The URL to navigate to + +- **multi_browserbase_stagehand_act_session** + - Perform an action in a specific browser session using natural language + - Inputs: + - `sessionId` (string): The session ID to use + - `action` (string): The action to perform (e.g., "click the login button") + - `variables` (object, optional): Variables for sensitive data in action templates + +- **multi_browserbase_stagehand_extract_session** + - Extract structured information from a specific browser session + - Inputs: + - `sessionId` (string): The session ID to use + - `instruction` (string): What to extract from the page + +- **multi_browserbase_stagehand_observe_session** + - Observe and find actionable elements in a specific browser session + - Inputs: + - `sessionId` (string): The session ID to use + - `instruction` (string): What to observe (e.g., "find the login button") + - `returnAction` (boolean, optional): Whether to return the action to perform + +#### Multi-Session Use Cases + +- **Parallel Data Collection**: Run multiple scraping sessions simultaneously across different websites +- **A/B Testing**: Compare user flows across different browser sessions with varying configurations +- **Authentication Workflows**: Maintain separate authenticated sessions for different user accounts +- **Cross-Site Operations**: Perform coordinated actions across multiple websites or applications +- **Load Testing**: Simulate multiple users interacting with web applications concurrently +- **Backup Sessions**: Keep fallback sessions ready in case primary sessions encounter issues + +### Resources + +The server provides access to screenshot resources: + +1. **Screenshots** (`screenshot://`) + - PNG images of captured screenshots + +## Project Structure + +``` +mcp-server-browserbase/ +β”œβ”€β”€ src/ # TypeScript source code +β”‚ β”œβ”€β”€ index.ts # Main entry point and Smithery default export +β”‚ β”œβ”€β”€ config.ts # Configuration management and CLI parsing +β”‚ β”œβ”€β”€ context.ts # Context class managing Stagehand instances +β”‚ β”œβ”€β”€ sessionManager.ts # Browserbase session lifecycle management +β”‚ β”œβ”€β”€ stagehandStore.ts # Multi-session store for managing parallel browser sessions +β”‚ β”œβ”€β”€ program.ts # CLI program setup using Commander.js +β”‚ β”œβ”€β”€ transport.ts # HTTP/SHTTP and STDIO transport handlers +β”‚ β”œβ”€β”€ server.ts # Server list management +β”‚ β”œβ”€β”€ utils.ts # Utility functions +β”‚ β”œβ”€β”€ mcp/ # MCP protocol implementations +β”‚ β”‚ β”œβ”€β”€ prompts.ts # Prompt templates and handlers for MCP clients +β”‚ β”‚ └── resources.ts # Resource management (screenshots) with URI-based access +β”‚ β”œβ”€β”€ tools/ # Tool definitions and implementations +β”‚ β”‚ β”œβ”€β”€ act.ts # Stagehand action execution tool +β”‚ β”‚ β”œβ”€β”€ extract.ts # Page content extraction tool +β”‚ β”‚ β”œβ”€β”€ navigate.ts # URL navigation tool +β”‚ β”‚ β”œβ”€β”€ observe.ts # Element observation tool +β”‚ β”‚ β”œβ”€β”€ screenshot.ts # Screenshot capture tool +β”‚ β”‚ β”œβ”€β”€ session.ts # Single session management tools +β”‚ β”‚ β”œβ”€β”€ multiSession.ts # Multi-session management and session-aware tools +β”‚ β”‚ β”œβ”€β”€ tool.ts # Tool type definitions and interfaces +β”‚ β”‚ └── index.ts # Tool exports and registration +β”‚ └── types/ # TypeScript type definitions +β”‚ └── types.ts # Shared type definitions for sessions and configurations +β”œβ”€β”€ dist/ # Compiled JavaScript output +β”œβ”€β”€ assets/ # Images and documentation assets +β”œβ”€β”€ cli.js # Executable entry point for CLI usage +β”œβ”€β”€ config.d.ts # TypeScript type definitions +β”œβ”€β”€ index.d.ts & index.js # Module exports for programmatic usage +β”œβ”€β”€ package.json # Package metadata and dependencies +β”œβ”€β”€ smithery.config.js # Smithery configuration +β”œβ”€β”€ tsconfig.json # TypeScript compiler configuration +└── README.md # This file +``` + +## Module Descriptions + +### Core Modules + +**index.ts** - The main Smithery export function that creates and configures the MCP server with Zod schema validation, tool registration, and proper metadata. + +**config.ts** - Configuration management handling CLI options, environment variables, defaults, and Browserbase API validation. + +**context.ts** - The Context class that manages Stagehand instances per session, executes tool actions with error handling, and coordinates between tools and sessions. + +**sessionManager.ts** - Creates and manages Browserbase sessions via CDP, handles session lifecycle, cookie injection, and tracks active sessions. + +### Infrastructure + +**program.ts** - CLI program setup using Commander.js with all command-line options, argument parsing, and transport initialization. + +**transport.ts** - Transport layer implementations for STDIO and HTTP/SHTTP communication with session management across different connection types. + +**server.ts** - Server list management providing factory patterns for server creation and handling multiple concurrent connections. + +**stagehandStore.ts** - Multi-session store managing parallel browser sessions with lifecycle tracking, automatic cleanup, and session metadata. + +### MCP Protocol Specifics Implementation + +**mcp/prompts.ts** - Prompt template definitions and handlers implementing the MCP prompts specification with argument substitution. + +**mcp/resources.ts** - Resource management implementing the MCP resources specification, handling screenshot storage, URI resolution, and base64-encoded data serving. + +### Tools & Types + +**tools/** - Individual tool implementations with type-safe Zod schemas including: + +- Core browser automation tools (navigate, act, extract, observe, screenshot) +- Single session management tools (session.ts) +- Multi-session management and session-aware tool variants (multiSession.ts) +- Tool type definitions and interfaces (tool.ts) +- Centralized tool exports and registration (index.ts) + +**types/types.ts** - Shared TypeScript type definitions for sessions, configurations, and MCP protocol structures. + +**utils.ts** - Message sanitization utilities ensuring proper JSON formatting for MCP messages. + +## Environment Variables + +- `BROWSERBASE_API_KEY`: API key for Browserbase authentication (required) +- `BROWSERBASE_PROJECT_ID`: Project ID for Browserbase (required) +- `DEBUG`: Enable debug logging (optional) + +## MCP Capabilities + +This server implements the following MCP capabilities: + +- **Tools**: 14 tools for comprehensive browser automation + - 5 Core Stagehand tools: navigate, act, extract, observe, screenshot + - 2 Single-session management tools: create and close Browserbase sessions + - 7 Multi-session tools: create, list, close, navigate, act, extract, observe with specific sessions +- **Prompts**: Prompt templates for common automation tasks +- **Resources**: Screenshot resource management with URI-based access + +### Session Management Architecture + +The server supports two session management approaches: + +1. **Single Session Mode**: Traditional approach with one active browser session + - Tools: `browserbase_session_create`, `browserbase_session_close` + - Simpler for basic automation tasks + - Automatically manages the active session + +2. **Multi-Session Mode**: Advanced approach with multiple parallel browser sessions + - Tools: `multi_browserbase_stagehand_session_create`, `multi_browserbase_stagehand_session_close`, `multi_browserbase_stagehand_session_list` + - Session-specific variants of all core tools (with `_session` suffix) + - Ideal for complex workflows requiring parallel browser instances + - Each session maintains independent state, cookies, and browser context + +## Key Features + +- **AI-Powered Automation**: Natural language commands for web interactions +- **Multi-Model Support**: Works with OpenAI, Claude, Gemini, and more +- **Advanced Session Management**: Single and multi-session support for parallel browser automation +- **Screenshot Capture**: Full-page and element-specific screenshots +- **Data Extraction**: Intelligent content extraction from web pages +- **Proxy Support**: Enterprise-grade proxy capabilities +- **Stealth Mode**: Advanced anti-detection features +- **Context Persistence**: Maintain authentication and state across sessions +- **Parallel Workflows**: Run multiple browser sessions simultaneously for complex automation tasks + +For more information about the Model Context Protocol, visit: + +- [MCP Documentation](https://modelcontextprotocol.io/docs) +- [MCP Specification](https://spec.modelcontextprotocol.io/) + +## License -### Credits +Licensed under the Apache 2.0 License. -Huge thanks and shoutout to the Playwright team for their contributions to the framework, and their work on the [Playwright MCP Server](https://github.com/microsoft/playwright-mcp) \ No newline at end of file +Copyright 2025 Browserbase, Inc. diff --git a/assets/stagehand-mcp.png b/assets/stagehand-mcp.png deleted file mode 100644 index 1e280f8..0000000 Binary files a/assets/stagehand-mcp.png and /dev/null differ diff --git a/browserbase/.npmignore b/browserbase/.npmignore deleted file mode 100644 index 24c1dd0..0000000 --- a/browserbase/.npmignore +++ /dev/null @@ -1,15 +0,0 @@ -# Ignore node_modules, build output, logs, env files, etc. -node_modules -dist -*.log -.env* - -# Ignore IDE/editor files -.vscode -.idea -*.swp -*.swo - -# Ignore OS files -.DS_Store -Thumbs.db \ No newline at end of file diff --git a/browserbase/README.md b/browserbase/README.md deleted file mode 100644 index e30f58a..0000000 --- a/browserbase/README.md +++ /dev/null @@ -1,316 +0,0 @@ -# Playwright Browserbase MCP Server - -![cover](../assets/browserbase-mcp.png) - -The Model Context Protocol (MCP) is an open protocol that enables seamless integration between LLM applications and external data sources and tools. Whether you’re building an AI-powered IDE, enhancing a chat interface, or creating custom AI workflows, MCP provides a standardized way to connect LLMs with the context they need. - - - -## How to Setup - -### Quickstarts: - -[![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.png)](cursor://anysphere.cursor-deeplink/mcp/install?name=browserbase&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyJAYnJvd3NlcmJhc2VocS9tY3AiXSwiZW52Ijp7IkJST1dTRVJCQVNFX0FQSV9LRVkiOiIiLCJCUk9XU0VSQkFTRV9QUk9KRUNUX0lEIjoiIn19) - -You can either use our Server hosted on NPM or run it completely locally by cloning this repo. - -### To run on NPM (Recommended) - -Go into your MCP Config JSON and add the Browserbase Server: - -```json -{ - "mcpServers": { - "browserbase": { - "command": "npx", - "args" : ["@browserbasehq/mcp"], - "env": { - "BROWSERBASE_API_KEY": "", - "BROWSERBASE_PROJECT_ID": "" - } - } - } -} -``` - -Thats it! Reload your MCP client and Claude will be able to use Browserbase. - -### To run 100% local: - -```bash -# Clone the Repo -git clone https://github.com/browserbase/mcp-server-browserbase.git - -# Install the dependencies in the proper directory and build the project -cd browserbase -npm install && npm run build - -``` - -Then in your MCP Config JSON run the server. To run locally we can use STDIO or self-host over SSE. - -### STDIO: - -To your MCP Config JSON file add the following: - -```json -{ -"mcpServers": { - "browserbase": { - "command" : "node", - "args" : ["/path/to/mcp-server-browserbase/browserbase/cli.js"], - "env": { - "BROWSERBASE_API_KEY": "", - "BROWSERBASE_PROJECT_ID": "" - } - } - } -} -``` - -### SSE: - -Run the following command in your terminal. You can add any flags (see options below) that you see fit to customize your configuration. - -```bash - node cli.js --port 8931 -``` - -Then in your MCP Config JSON file put the following: - -```json - { - "mcpServers": { - "browserbase": { - "url": "http://localhost:8931/sse", - "env": { - "BROWSERBASE_API_KEY": "", - "BROWSERBASE_PROJECT_ID": "" - } - } - } - } -``` - -Then reload your MCP client and you should be good to go! - -## Flags Explained: - -The Browserbase MCP server accepts the following command-line flags: - -| Flag | Description | -|------|-------------| -| `--browserbaseApiKey ` | Your Browserbase API key for authentication | -| `--browserbaseProjectId ` | Your Browserbase project ID | -| `--proxies` | Enable Browserbase proxies for the session | -| `--advancedStealth` | Enable Browserbase Advanced Stealth (Only for Scale Plan Users) | -| `--contextId ` | Specify a Browserbase Context ID to use | -| `--persist [boolean]` | Whether to persist the Browserbase context (default: true) | -| `--port ` | Port to listen on for HTTP/SSE transport | -| `--host ` | Host to bind server to (default: localhost, use 0.0.0.0 for all interfaces) | -| `--cookies [json]` | JSON array of cookies to inject into the browser | -| `--browserWidth ` | Browser viewport width (default: 1024) | -| `--browserHeight ` | Browser viewport height (default: 768) | - -These flags can be passed directly to the CLI or configured in your MCP configuration file. - -### NOTE: - -Currently, these flags can only be used with the local server (npx @browserbasehq/mcp). - -____ - -## Flags & Example Configs - -### Proxies - -Here are our docs on [Proxies](https://docs.browserbase.com/features/proxies). - -To use proxies in STDIO, set the --proxies flag in your MCP Config: - -```json -{ - "mcpServers": { - "browserbase": { - "command" : "npx", - "args" : ["@browserbasehq/mcp", "--proxies"], - "env": { - "BROWSERBASE_API_KEY": "", - "BROWSERBASE_PROJECT_ID": "" - } - } - } -} -``` -### Advanced Stealth - -Here are our docs on [Advanced Stealth](https://docs.browserbase.com/features/stealth-mode#advanced-stealth-mode). - -To use proxies in STDIO, set the --advancedStealth flag in your MCP Config: - -```json -{ - "mcpServers": { - "browserbase": { - "command" : "npx", - "args" : ["@browserbasehq/mcp", "--advancedStealth"], - "env": { - "BROWSERBASE_API_KEY": "", - "BROWSERBASE_PROJECT_ID": "" - } - } - } -} -``` - -### Contexts - -Here are our docs on [Contexts](https://docs.browserbase.com/features/contexts) - -To use contexts in STDIO, set the --contextId flag in your MCP Config: - -```json -{ - "mcpServers": { - "browserbase": { - "command" : "npx", - "args" : ["@browserbasehq/mcp", "--contextId", ""], - "env": { - "BROWSERBASE_API_KEY": "", - "BROWSERBASE_PROJECT_ID": "" - } - } - } -} -``` - -### Cookie Injection - -Why would you need to inject cookies? Our context API currently works on persistent cookies, but not session cookies. So sometimes our persistent auth might not work (we're working hard to add this functionality). - -You can flag cookies into the MCP by adding the cookies.json to your MCP Config. - -To use proxies in STDIO, set the --proxies flag in your MCP Config. Your cookies JSON must be in the type of [Playwright Cookies](https://playwright.dev/docs/api/class-browsercontext#browser-context-cookies) - -```json -{ - "mcpServers": { - "browserbase" { - "command" : "npx", - "args" : [ - "@browserbasehq/mcp", "--cookies", - '{ - "cookies": json, - }' - ], - "env": { - "BROWSERBASE_API_KEY": "", - "BROWSERBASE_PROJECT_ID": "" - } - } - } -} -``` - -### Browser Viewport Sizing - -The default viewport sizing for a browser session is 1024 x 768. You can adjust the Browser viewport sizing with browserWidth and browserHeight flags. - -Here's how to use it for custom browser sizing. We recommend to stick with 16:9 aspect ratios (ie: 1920 x 1080, 1280, 720, 1024 x 768) - -```json -{ - "mcpServers": { - "browserbase": { - "command" : "npx", - "args" : [ - "@browserbasehq/mcp", - "--browserHeight 1080", - "--browserWidth 1920", - ], - "env": { - "BROWSERBASE_API_KEY": "", - "BROWSERBASE_PROJECT_ID": "" - } - } - } -} -``` - -## Structure - -* `src/`: TypeScript source code - * `index.ts`: Main entry point, env checks, shutdown - * `server.ts`: MCP Server setup and request routing - * `sessionManager.ts`: Handles Browserbase session creation/management - * `tools/`: Tool definitions and implementations - * `resources/`: Resource (screenshot) handling - * `types.ts`: Shared TypeScript types -* `dist/`: Compiled JavaScript output -* `tests/`: Placeholder for tests -* `utils/`: Placeholder for utility scripts -* `Dockerfile`: For building a Docker image -* Configuration files (`.json`, `.ts`, `.mjs`, `.npmignore`) - -## Contexts for Persistence - -This server supports Browserbase's Contexts feature, which allows persisting cookies, authentication, and cached data across browser sessions: - -1. **Creating a Context**: - ``` - browserbase_context_create: Creates a new context, optionally with a friendly name - ``` - -2. **Using a Context with a Session**: - ``` - browserbase_session_create: Now accepts a 'context' parameter with: - - id: The context ID to use - - name: Alternative to ID, the friendly name of the context - - persist: Whether to save changes (cookies, cache) back to the context (default: true) - ``` - -3. **Deleting a Context**: - ``` - browserbase_context_delete: Deletes a context when you no longer need it - ``` - -Contexts make it much easier to: -- Maintain login state across sessions -- Reduce page load times by preserving cache -- Avoid CAPTCHAs and detection by reusing browser fingerprints - -## Cookie Management - -This server also provides direct cookie management capabilities: - -1. **Adding Cookies**: - ``` - browserbase_cookies_add: Add cookies to the current browser session with full control over properties - ``` - -2. **Getting Cookies**: - ``` - browserbase_cookies_get: View all cookies in the current session (optionally filtered by URLs) - ``` - -3. **Deleting Cookies**: - ``` - browserbase_cookies_delete: Delete specific cookies or clear all cookies from the session - ``` - -These tools are useful for: -- Setting authentication cookies without navigating to login pages -- Backing up and restoring cookie state -- Debugging cookie-related issues -- Manipulating cookie attributes (expiration, security flags, etc.) - -## TODO/Roadmap - -* Implement true `ref`-based interaction logic for click, type, drag, hover, select_option. -* Implement element-specific screenshots using `ref`. -* Add more standard MCP tools (tabs, navigation, etc.). -* Add tests. diff --git a/browserbase/cli.js b/browserbase/cli.js deleted file mode 100755 index 9610f3e..0000000 --- a/browserbase/cli.js +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env node -import './dist/program.js'; \ No newline at end of file diff --git a/browserbase/config.d.ts b/browserbase/config.d.ts deleted file mode 100644 index 4950123..0000000 --- a/browserbase/config.d.ts +++ /dev/null @@ -1,85 +0,0 @@ -import type { Cookie } from "playwright-core"; - -export type Config = { - /** - * The Browserbase API Key to use - */ - browserbaseApiKey?: string; - /** - * The Browserbase Project ID to use - */ - browserbaseProjectId?: string; - /** - * Whether or not to use Browserbase proxies - * https://docs.browserbase.com/features/proxies - * - * @default false - */ - proxies?: boolean; - /** - * Use advanced stealth mode. Only available to Browserbase Scale Plan users. - * - * @default false - */ - advancedStealth?: boolean; - /** - * Potential Browserbase Context to use - * Would be a context ID - */ - context?: { - /** - * The ID of the context to use - */ - contextId?: string; - /** - * Whether or not to persist the context - * - * @default true - */ - persist?: boolean; - }; - /** - * - */ - viewPort?: { - /** - * The width of the browser - */ - browserWidth?: number; - /** - * The height of the browser - */ - browserHeight?: number; - }; - /** - * Cookies to inject into the Browserbase context - * Format: Array of cookie objects with name, value, domain, and optional path, expires, httpOnly, secure, sameSite - */ - cookies?: Cookie[]; - /** - * Whether or not to port to a server - * - */ - server?: { - /** - * The port to listen on for SSE or MCP transport. - */ - port?: number; - /** - * The host to bind the server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces. - */ - host?: string; - }; - tools?: { - /** - * Configuration for the browser_take_screenshot tool. - */ - browserbase_take_screenshot?: { - /** - * Whether to disable base64-encoded image responses to the clients that - * don't support binary data or prefer to save on tokens. - */ - omitBase64?: boolean; - } - } -}; \ No newline at end of file diff --git a/browserbase/index.d.ts b/browserbase/index.d.ts deleted file mode 100644 index f499530..0000000 --- a/browserbase/index.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { Server } from '@modelcontextprotocol/sdk/server/index.js'; - -import type { Config } from './config'; - -export declare function createServer(config?: Config): Promise; -export {}; \ No newline at end of file diff --git a/browserbase/index.js b/browserbase/index.js deleted file mode 100644 index cecbc9e..0000000 --- a/browserbase/index.js +++ /dev/null @@ -1,2 +0,0 @@ -import { createServer } from './dist/index.js'; -export default { createServer }; \ No newline at end of file diff --git a/browserbase/package-lock.json b/browserbase/package-lock.json deleted file mode 100644 index 3496fc9..0000000 --- a/browserbase/package-lock.json +++ /dev/null @@ -1,2911 +0,0 @@ -{ - "name": "@browserbasehq/mcp", - "version": "1.0.6", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@browserbasehq/mcp", - "version": "1.0.6", - "dependencies": { - "@browserbasehq/sdk": "^2.5.0", - "@modelcontextprotocol/sdk": "^1.10.2", - "@types/yaml": "^1.9.6", - "chromium-bidi": "^5.3.1", - "commander": "^13.1.0", - "dotenv": "^16.5.0", - "playwright": "^1.52.0", - "yaml": "^2.7.1", - "zod": "^3.24.3", - "zod-to-json-schema": "^3.24.5" - }, - "bin": { - "mcp-server-browserbase": "cli.js" - }, - "devDependencies": { - "@smithery/cli": "^1.2.4", - "shx": "^0.3.4", - "tsx": "^4.19.4", - "typescript": "^5.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ai-sdk/provider": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-1.1.3.tgz", - "integrity": "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ai-sdk/provider-utils": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-2.2.8.tgz", - "integrity": "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "1.1.3", - "nanoid": "^3.3.8", - "secure-json-parse": "^2.7.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.23.8" - } - }, - "node_modules/@ai-sdk/react": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-1.2.12.tgz", - "integrity": "sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider-utils": "2.2.8", - "@ai-sdk/ui-utils": "1.2.11", - "swr": "^2.2.5", - "throttleit": "2.1.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react": "^18 || ^19 || ^19.0.0-rc", - "zod": "^3.23.8" - }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } - } - }, - "node_modules/@ai-sdk/ui-utils": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@ai-sdk/ui-utils/-/ui-utils-1.2.11.tgz", - "integrity": "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "1.1.3", - "@ai-sdk/provider-utils": "2.2.8", - "zod-to-json-schema": "^3.24.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.23.8" - } - }, - "node_modules/@anthropic-ai/sdk": { - "version": "0.32.1", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.32.1.tgz", - "integrity": "sha512-U9JwTrDvdQ9iWuABVsMLj8nJVwAyQz6QXvgLsVhryhCEPkLsbcP/MXxm+jYcAwLoV8ESbaTTjnD4kuAFa+Hyjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7" - } - }, - "node_modules/@browserbasehq/sdk": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@browserbasehq/sdk/-/sdk-2.5.0.tgz", - "integrity": "sha512-bcnbYZvm5Ht1nrHUfWDK4crspiTy1ESJYMApsMiOTUnlKOan0ocRD6m7hZH34iSC2c2XWsoryR80cwsYgCBWzQ==", - "license": "Apache-2.0", - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", - "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.10.2", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "cors": "^2.8.5", - "cross-spawn": "^7.0.3", - "eventsource": "^3.0.2", - "express": "^5.0.1", - "express-rate-limit": "^7.5.0", - "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", - "zod": "^3.23.8", - "zod-to-json-schema": "^3.24.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ngrok/ngrok": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok/-/ngrok-1.5.1.tgz", - "integrity": "sha512-sfcgdpiAJHqmuO3e6QjQGbavIrR3E72do/NAsnGhm+7SGstLj1aM3Sd8mkfTORb2Hj7ATMuoBYuED5ylKuRQCg==", - "dev": true, - "license": "(MIT OR Apache-2.0)", - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@ngrok/ngrok-android-arm64": "1.5.1", - "@ngrok/ngrok-darwin-arm64": "1.5.1", - "@ngrok/ngrok-darwin-universal": "1.5.1", - "@ngrok/ngrok-darwin-x64": "1.5.1", - "@ngrok/ngrok-freebsd-x64": "1.5.1", - "@ngrok/ngrok-linux-arm-gnueabihf": "1.5.1", - "@ngrok/ngrok-linux-arm64-gnu": "1.5.1", - "@ngrok/ngrok-linux-arm64-musl": "1.5.1", - "@ngrok/ngrok-linux-x64-gnu": "1.5.1", - "@ngrok/ngrok-linux-x64-musl": "1.5.1", - "@ngrok/ngrok-win32-arm64-msvc": "1.5.1", - "@ngrok/ngrok-win32-ia32-msvc": "1.5.1", - "@ngrok/ngrok-win32-x64-msvc": "1.5.1" - } - }, - "node_modules/@ngrok/ngrok-darwin-arm64": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-darwin-arm64/-/ngrok-darwin-arm64-1.5.1.tgz", - "integrity": "sha512-HNOhrPDP+nJJY7Bh45DOeh6jmcGASWINGbUuseZM0C8psQMp7crPywjRh0inkRegUrb4K8y06sfmgt2fmsF6jQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-darwin-universal": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-darwin-universal/-/ngrok-darwin-universal-1.5.1.tgz", - "integrity": "sha512-EsMxYC/tY+ZqhjbeZtVq5MFIuD8SEPgAlHINEszsHd8ZRICc2U9Xl15CbDrew3pcfEg/ZVFrOH9CyC4aZ/V/cA==", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@smithery/cli": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@smithery/cli/-/cli-1.2.4.tgz", - "integrity": "sha512-cXQoV6sTdOaBKQLx0UTTkHjRRD19p+tU93CqK0uOU2yfgTiG7iYYZ0/50lLbvrMWByB/80Xnk1b+atbC8F1rKQ==", - "dev": true, - "dependencies": { - "@modelcontextprotocol/sdk": "^1.10.1", - "@ngrok/ngrok": "^1.5.1", - "@smithery/registry": "^0.3.7", - "@smithery/sdk": "^1.4.3", - "@types/uuid": "^10.0.0", - "chalk": "^4.1.2", - "commander": "^14.0.0", - "cors": "^2.8.5", - "cross-fetch": "^4.1.0", - "esbuild": "^0.25.5", - "express": "^5.1.0", - "inquirer": "^8.2.4", - "inquirer-autocomplete-prompt": "^2.0.0", - "lodash": "^4.17.21", - "ora": "^8.2.0", - "uuid": "^11.1.0", - "uuidv7": "^1.0.2" - }, - "bin": { - "cli": "dist/index.js" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "bufferutil": "^4.0.9" - } - }, - "node_modules/@smithery/cli/node_modules/commander": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz", - "integrity": "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "node_modules/@smithery/registry": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/@smithery/registry/-/registry-0.3.7.tgz", - "integrity": "sha512-lfVsxsxF/RdQmaLTleAL2/K+blCGiEeJseFoHYWfqk8K/vZc8ypN4inVA4ABMW/cAS31VZS8inFMYeVTYgLZdQ==", - "dev": true, - "peerDependencies": { - "zod": ">= 3" - } - }, - "node_modules/@smithery/sdk": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@smithery/sdk/-/sdk-1.4.3.tgz", - "integrity": "sha512-Lq4F/d6aoZABR5DJdcewXzS/6FC9ov/nScykHJnALwGBX5R79AfTHXIs/egMccL9rTxlCvlYI2XhuJPBiEATMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@anthropic-ai/sdk": "^0.32.1", - "@modelcontextprotocol/sdk": "^1.10.2", - "ai": "^4.3.15", - "express": "^5.1.0", - "json-schema": "^0.4.0", - "lodash": "^4.17.21", - "okay-error": "^1.0.2", - "openai": "^4.0.0", - "uuid": "^11.0.3", - "zod": "^3.23.8", - "zod-to-json-schema": "^3.24.1" - } - }, - "node_modules/@types/diff-match-patch": { - "version": "1.0.36", - "resolved": "https://registry.npmjs.org/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz", - "integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "18.19.67", - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.6.12", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/yaml": { - "version": "1.9.6", - "resolved": "https://registry.npmjs.org/@types/yaml/-/yaml-1.9.6.tgz", - "integrity": "sha512-VKOCuDN57wngmyQnRqcn4vuGWCXViISHv+UCCjrKcf1yt4zyfMmOGlZDI2ucTHK72V8ki+sd7h21OZL6O5S52A==", - "license": "MIT", - "dependencies": { - "yaml": "*" - } - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/accepts": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/mime-db": { - "version": "1.54.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/mime-types": { - "version": "3.0.1", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/agentkeepalive": { - "version": "4.5.0", - "license": "MIT", - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/ai": { - "version": "4.3.16", - "resolved": "https://registry.npmjs.org/ai/-/ai-4.3.16.tgz", - "integrity": "sha512-KUDwlThJ5tr2Vw0A1ZkbDKNME3wzWhuVfAOwIvFUzl1TPVDFAXDFTXio3p+jaKneB+dKNCvFFlolYmmgHttG1g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "1.1.3", - "@ai-sdk/provider-utils": "2.2.8", - "@ai-sdk/react": "1.2.12", - "@ai-sdk/ui-utils": "1.2.11", - "@opentelemetry/api": "1.9.0", - "jsondiffpatch": "0.6.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react": "^18 || ^19 || ^19.0.0-rc", - "zod": "^3.23.8" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - } - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "license": "MIT" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/body-parser": { - "version": "2.2.0", - "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.0", - "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", - "on-finished": "^2.4.1", - "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/bufferutil": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz", - "integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true, - "license": "MIT" - }, - "node_modules/chromium-bidi": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-5.3.1.tgz", - "integrity": "sha512-fbkgn0/m6RIRknVEez+QOYuvukUomBC0XnS8fgdbl9FeunjR3vUvPN6iYrbzXIuaJXYOwGU8FZgOTDzBImGvLw==", - "license": "Apache-2.0", - "dependencies": { - "mitt": "^3.0.1", - "zod": "^3.24.1" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 10" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", - "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/content-disposition": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/cors": { - "version": "2.8.5", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/cross-fetch": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", - "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "node-fetch": "^2.7.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.0", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/devtools-protocol": { - "version": "0.0.1467305", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1467305.tgz", - "integrity": "sha512-LxwMLqBoPPGpMdRL4NkLFRNy3QLp6Uqa7GNp1v6JaBheop2QrB9Q7q0A/q/CYYP9sBfZdHOyszVx4gc9zyk7ow==", - "license": "BSD-3-Clause", - "peer": true - }, - "node_modules/diff-match-patch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", - "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/dotenv": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", - "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", - "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.5", - "@esbuild/android-arm": "0.25.5", - "@esbuild/android-arm64": "0.25.5", - "@esbuild/android-x64": "0.25.5", - "@esbuild/darwin-arm64": "0.25.5", - "@esbuild/darwin-x64": "0.25.5", - "@esbuild/freebsd-arm64": "0.25.5", - "@esbuild/freebsd-x64": "0.25.5", - "@esbuild/linux-arm": "0.25.5", - "@esbuild/linux-arm64": "0.25.5", - "@esbuild/linux-ia32": "0.25.5", - "@esbuild/linux-loong64": "0.25.5", - "@esbuild/linux-mips64el": "0.25.5", - "@esbuild/linux-ppc64": "0.25.5", - "@esbuild/linux-riscv64": "0.25.5", - "@esbuild/linux-s390x": "0.25.5", - "@esbuild/linux-x64": "0.25.5", - "@esbuild/netbsd-arm64": "0.25.5", - "@esbuild/netbsd-x64": "0.25.5", - "@esbuild/openbsd-arm64": "0.25.5", - "@esbuild/openbsd-x64": "0.25.5", - "@esbuild/sunos-x64": "0.25.5", - "@esbuild/win32-arm64": "0.25.5", - "@esbuild/win32-ia32": "0.25.5", - "@esbuild/win32-x64": "0.25.5" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/eventsource": { - "version": "3.0.6", - "license": "MIT", - "dependencies": { - "eventsource-parser": "^3.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/eventsource-parser": { - "version": "3.0.1", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/express": { - "version": "5.1.0", - "license": "MIT", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.0", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "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.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express-rate-limit": { - "version": "7.5.0", - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/express-rate-limit" - }, - "peerDependencies": { - "express": "^4.11 || 5 || ^5.0.0-beta.1" - } - }, - "node_modules/express/node_modules/mime-db": { - "version": "1.54.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/mime-types": { - "version": "3.0.1", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/external-editor/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/finalhandler": { - "version": "2.1.0", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/form-data": { - "version": "4.0.1", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data-encoder": { - "version": "1.7.2", - "license": "MIT" - }, - "node_modules/formdata-node": { - "version": "4.4.1", - "license": "MIT", - "dependencies": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" - }, - "engines": { - "node": ">= 12.20" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", - "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "license": "MIT", - "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" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-tsconfig": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", - "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/inflight": { - "version": "1.0.6", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "license": "ISC" - }, - "node_modules/inquirer": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", - "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^6.0.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/inquirer-autocomplete-prompt": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inquirer-autocomplete-prompt/-/inquirer-autocomplete-prompt-2.0.1.tgz", - "integrity": "sha512-jUHrH0btO7j5r8DTQgANf2CBkTZChoVySD8zF/wp5fZCOLIuUbleXhf4ZY5jNBOc1owA3gdfWtfZuppfYBhcUg==", - "dev": true, - "license": "ISC", - "dependencies": { - "ansi-escapes": "^4.3.2", - "figures": "^3.2.0", - "picocolors": "^1.0.0", - "run-async": "^2.4.1", - "rxjs": "^7.5.4" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "inquirer": "^8.0.0" - } - }, - "node_modules/inquirer/node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inquirer/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inquirer/node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inquirer/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/interpret": { - "version": "1.4.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-core-module": { - "version": "2.15.1", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "license": "MIT" - }, - "node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "license": "ISC" - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true, - "license": "(AFL-2.1 OR BSD-3-Clause)" - }, - "node_modules/jsondiffpatch": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.6.0.tgz", - "integrity": "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/diff-match-patch": "^1.0.36", - "chalk": "^5.3.0", - "diff-match-patch": "^1.0.5" - }, - "bin": { - "jsondiffpatch": "bin/jsondiffpatch.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/jsondiffpatch/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, - "node_modules/log-symbols": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", - "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^5.3.0", - "is-unicode-supported": "^1.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "1.1.0", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.1.3", - "license": "MIT" - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true, - "license": "ISC" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/negotiator": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "dev": true, - "license": "MIT", - "optional": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/okay-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/okay-error/-/okay-error-1.0.2.tgz", - "integrity": "sha512-jbaUyzfqiOlVmrqNzxJhjNwdB2zDCtXFfIfYz49CHemPbOldo6mA7iDELiSZJkbxYj4KCmS8GdmGE6hhcjZ+Qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/openai": { - "version": "4.104.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", - "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7" - }, - "bin": { - "openai": "bin/cli" - }, - "peerDependencies": { - "ws": "^8.18.0", - "zod": "^3.23.8" - }, - "peerDependenciesMeta": { - "ws": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/ora": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", - "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^5.3.0", - "cli-cursor": "^5.0.0", - "cli-spinners": "^2.9.2", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^2.0.0", - "log-symbols": "^6.0.0", - "stdin-discarder": "^0.2.2", - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "dev": true, - "license": "MIT" - }, - "node_modules/ora/node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ora/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "dev": true, - "license": "MIT" - }, - "node_modules/path-to-regexp": { - "version": "8.2.0", - "license": "MIT", - "engines": { - "node": ">=16" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/pkce-challenge": { - "version": "5.0.0", - "license": "MIT", - "engines": { - "node": ">=16.20.0" - } - }, - "node_modules/playwright": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", - "integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==", - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.52.0" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz", - "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==", - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.14.0", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.6.3", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/rechoir": { - "version": "0.6.2", - "dev": true, - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/router": { - "version": "2.2.0", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "license": "MIT" - }, - "node_modules/secure-json-parse": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", - "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/send": { - "version": "1.2.0", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/send/node_modules/mime-db": { - "version": "1.54.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/send/node_modules/mime-types": { - "version": "3.0.1", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-static": { - "version": "2.2.0", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "license": "ISC" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shelljs": { - "version": "0.8.5", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/shx": { - "version": "0.3.4", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.3", - "shelljs": "^0.8.5" - }, - "bin": { - "shx": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/statuses": { - "version": "2.0.1", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stdin-discarder": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", - "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/swr": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.3.tgz", - "integrity": "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "dequal": "^2.0.3", - "use-sync-external-store": "^1.4.0" - }, - "peerDependencies": { - "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/throttleit": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-2.1.0.tgz", - "integrity": "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/through": { - "version": "2.3.8", - "dev": true, - "license": "MIT" - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "license": "MIT" - }, - "node_modules/tslib": { - "version": "2.8.1", - "dev": true, - "license": "0BSD" - }, - "node_modules/tsx": { - "version": "4.19.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.4.tgz", - "integrity": "sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.25.0", - "get-tsconfig": "^4.7.5" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/tsx/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/type-is/node_modules/mime-db": { - "version": "1.54.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/type-is/node_modules/mime-types": { - "version": "3.0.1", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typescript": { - "version": "5.7.2", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/use-sync-external-store": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", - "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/uuidv7": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uuidv7/-/uuidv7-1.0.2.tgz", - "integrity": "sha512-8JQkH4ooXnm1JCIhqTMbtmdnYEn6oKukBxHn1Ic9878jMkL7daTI7anTExfY18VRCX7tcdn5quzvCb6EWrR8PA==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "uuidv7": "cli.js" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/web-streams-polyfill": { - "version": "4.0.0-beta.3", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.18.0", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "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 - } - } - }, - "node_modules/yaml": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", - "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/zod": { - "version": "3.24.3", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-to-json-schema": { - "version": "3.24.5", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", - "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.24.1" - } - } - } -} diff --git a/browserbase/package.json b/browserbase/package.json deleted file mode 100644 index 0725c98..0000000 --- a/browserbase/package.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "name": "@browserbasehq/mcp", - "version": "1.0.6", - "description": "MCP server for browser automation using browserbase", - "author": "Browserbase, Inc. (https://browserbase.com)", - "homepage": "https://browserbase.com", - "module": "./src/index.ts", - "type": "module", - "main": "./cli.js", - "engines": { - "node": ">=18" - }, - "files": [ - "../assets/browserbase-mcp.png", - "dist", - "cli.js", - "index.d.ts", - "index.js", - "config.d.ts", - "config.js" - ], - "scripts": { - "build": "tsc && shx chmod +x dist/*.js && shx chmod +x cli.js", - "prepare": "npm run build", - "watch": "tsc --watch", - "smithery": "npx @smithery/cli dev", - "inspector": "npx @modelcontextprotocol/inspector build/index.js", - "test-local": "npm pack && npm install -g $(pwd)/$(ls -t *.tgz | head -1) && mcp-server-browserbase" - }, - "exports": { - "./package.json": "./package.json", - ".": { - "types": "./index.d.ts", - "default": "./cli.js" - } - }, - "dependencies": { - "@browserbasehq/sdk": "^2.5.0", - "@modelcontextprotocol/sdk": "^1.10.2", - "@types/yaml": "^1.9.6", - "chromium-bidi": "^5.3.1", - "commander": "^13.1.0", - "dotenv": "^16.5.0", - "playwright": "^1.52.0", - "yaml": "^2.7.1", - "zod": "^3.24.3", - "zod-to-json-schema": "^3.24.5" - }, - "devDependencies": { - "@smithery/cli": "^1.2.4", - "shx": "^0.3.4", - "tsx": "^4.19.4", - "typescript": "^5.6.2" - }, - "bin": { - "mcp-server-browserbase": "cli.js" - }, - "publishConfig": { - "access": "public" - } -} diff --git a/browserbase/playwright.config.ts b/browserbase/playwright.config.ts deleted file mode 100644 index a747d67..0000000 --- a/browserbase/playwright.config.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { defineConfig } from '@playwright/test'; - -/** - * Basic Playwright config - primarily useful if adding actual - * tests later - */ -export default defineConfig({ - testDir: './tests', - fullyParallel: true, - forbidOnly: !!process.env.CI, - retries: process.env.CI ? 2 : 0, - workers: process.env.CI ? 1 : undefined, - reporter: 'html', - use: { - trace: 'on-first-retry', - // Base URL to use in actions like `await page.goto('/')` - // baseURL: 'http://127.0.0.1:3000', - }, - - /* Configure projects for major browsers */ - // projects: [ - // { - // name: 'chromium', - // use: { ...devices['Desktop Chrome'] }, - // }, - // ], - - /* Run your local dev server before starting the tests */ - // webServer: { - // command: 'npm run start', - // url: 'http://127.0.0.1:3000', - // reuseExistingServer: !process.env.CI, - // }, -}); \ No newline at end of file diff --git a/browserbase/smithery.yaml b/browserbase/smithery.yaml deleted file mode 100644 index 8fa57d3..0000000 --- a/browserbase/smithery.yaml +++ /dev/null @@ -1 +0,0 @@ -runtime: "typescript" \ No newline at end of file diff --git a/browserbase/src/context.ts b/browserbase/src/context.ts deleted file mode 100644 index e315990..0000000 --- a/browserbase/src/context.ts +++ /dev/null @@ -1,601 +0,0 @@ -import type { Server } from "@modelcontextprotocol/sdk/server/index.js"; -import type { BrowserSession } from "./sessionManager.js"; -import { - getSession, - defaultSessionId, - getSessionReadOnly, -} from "./sessionManager.js"; -import type { Tool, ToolResult } from "./tools/tool.js"; -import type { Config } from "../config.js"; -import { - Resource, - CallToolResult, - TextContent, - ImageContent, -} from "@modelcontextprotocol/sdk/types.js"; -import { z } from "zod"; -import { PageSnapshot } from "./pageSnapshot.js"; -import type { Page, Locator } from "playwright"; - -export type ToolActionResult = - | { content?: (ImageContent | TextContent)[] } - | undefined - | void; - -/** - * Manages the context for tool execution within a specific Browserbase session. - */ - -export class Context { - private server: Server; - public readonly config: Config; - public currentSessionId: string = defaultSessionId; - private latestSnapshots = new Map(); - private screenshotResources = new Map< - string, - { format: string; bytes: string; uri: string } - >(); - - constructor(server: Server, config: Config) { - this.server = server; - this.config = config; - this.screenshotResources = new Map(); - } - - // --- Snapshot State Handling (Using PageSnapshot) --- - - /** - * Returns the latest PageSnapshot for the currently active session. - * Throws an error if no snapshot is available for the active session. - */ - snapshotOrDie(): PageSnapshot { - const snapshot = this.latestSnapshots.get(this.currentSessionId); - if (!snapshot) { - throw new Error( - `No snapshot available for the current session (${this.currentSessionId}). Capture a snapshot first.` - ); - } - return snapshot; - } - - /** - * Clears the snapshot for the currently active session. - */ - clearLatestSnapshot(): void { - this.latestSnapshots.delete(this.currentSessionId); - } - - /** - * Captures a new PageSnapshot for the currently active session and stores it. - * Returns the captured snapshot or undefined if capture failed. - */ - async captureSnapshot(): Promise { - const logPrefix = `[Context.captureSnapshot] ${new Date().toISOString()} Session ${ - this.currentSessionId - }:`; - let page; - try { - page = await this.getActivePage(); - } catch (error) { - this.clearLatestSnapshot(); - return undefined; - } - - if (!page) { - this.clearLatestSnapshot(); - return undefined; - } - - try { - await this.waitForTimeout(100); // Small delay for UI settlement - const snapshot = await PageSnapshot.create(page); - this.latestSnapshots.set(this.currentSessionId, snapshot); - return snapshot; - } catch (error) { - process.stderr.write( - `${logPrefix} Failed to capture snapshot: ${ - error instanceof Error ? error.message : String(error) - }\\n` - ); // Enhanced logging - this.clearLatestSnapshot(); - return undefined; - } - } - - // --- Resource Handling Methods --- - - listResources(): Resource[] { - const resources: Resource[] = []; - for (const [name, data] of this.screenshotResources.entries()) { - resources.push({ - uri: data.uri, - mimeType: `image/${data.format}`, // Ensure correct mime type - name: `Screenshot: ${name}`, - }); - } - return resources; - } - - readResource(uri: string): { uri: string; mimeType: string; blob: string } { - const prefix = "mcp://screenshots/"; - if (uri.startsWith(prefix)) { - const name = uri.split("/").pop() || ""; - const data = this.screenshotResources.get(name); - if (data) { - return { - uri, - mimeType: `image/${data.format}`, // Ensure correct mime type - blob: data.bytes, - }; - } else { - throw new Error(`Screenshot resource not found: ${name}`); - } - } else { - throw new Error(`Resource URI format not recognized: ${uri}`); - } - } - - addScreenshot(name: string, format: "png" | "jpeg", bytes: string): void { - const uri = `mcp://screenshots/${name}`; - this.screenshotResources.set(name, { format, bytes, uri }); - this.server.notification({ - method: "resources/list_changed", - params: {}, - }); - } - - // --- Session and Tool Execution --- - - public async getActivePage(): Promise { - const session = await getSession(this.currentSessionId, this.config); - if (!session || !session.page || session.page.isClosed()) { - try { - // getSession does not support a refresh flag currently. - // If a session is invalid, it needs to be recreated or re-established upstream. - // For now, just return null if the fetched session is invalid. - const currentSession = await getSession( - this.currentSessionId, - this.config - ); - if ( - !currentSession || - !currentSession.page || - currentSession.page.isClosed() - ) { - return null; - } - return currentSession.page; - } catch (refreshError) { - return null; - } - } - return session.page; - } - - public async getActiveBrowser(): Promise { - const session = await getSession(this.currentSessionId, this.config); - if (!session || !session.browser || !session.browser.isConnected()) { - try { - // getSession does not support a refresh flag currently. - const currentSession = await getSession( - this.currentSessionId, - this.config - ); - if ( - !currentSession || - !currentSession.browser || - !currentSession.browser.isConnected() - ) { - return null; - } - return currentSession.browser; - } catch (refreshError) { - return null; - } - } - return session.browser; - } - - /** - * Get the active browser without triggering session creation. - * This is a read-only operation used when we need to check for an existing browser - * without side effects (e.g., during close operations). - * @returns The browser if it exists and is connected, null otherwise - */ - public getActiveBrowserReadOnly(): BrowserSession["browser"] | null { - const session = getSessionReadOnly(this.currentSessionId); - if (!session || !session.browser || !session.browser.isConnected()) { - return null; - } - return session.browser; - } - - /** - * Get the active page without triggering session creation. - * This is a read-only operation used when we need to check for an existing page - * without side effects. - * @returns The page if it exists and is not closed, null otherwise - */ - public getActivePageReadOnly(): BrowserSession["page"] | null { - const session = getSessionReadOnly(this.currentSessionId); - if (!session || !session.page || session.page.isClosed()) { - return null; - } - return session.page; - } - - public async waitForTimeout(timeoutMillis: number): Promise { - return new Promise((resolve) => setTimeout(resolve, timeoutMillis)); - } - - private createErrorResult(message: string, toolName: string): CallToolResult { - return { - content: [{ type: "text", text: `Error: ${message}` }], - isError: true, - }; - } - - // --- Refactored Action Execution with Retries --- - private async executeRefAction( - toolName: string, - validatedArgs: any, - actionFn: ( - page: Page, - identifier: string | undefined, - args: any, - locator: Locator | undefined, - identifierType: "ref" | "selector" | "none" - ) => Promise, - requiresIdentifier: boolean = true - ): Promise<{ resultText: string; actionResult?: ToolActionResult | void }> { - let lastError: Error | null = null; - let page: Page | null = null; - let actionResult: ToolActionResult | void | undefined; - let resultText = ""; - let identifier: string | undefined = undefined; - let identifierType: "ref" | "selector" | "none" = "none"; - - // --- Get page and snapshot BEFORE the loop --- - page = await this.getActivePage(); - if (!page) { - throw new Error("Failed to get active page before action attempts."); - } - - // Get the CURRENT latest snapshot - DO NOT capture a new one here. - const snapshot = this.latestSnapshots.get(this.currentSessionId); - const initialSnapshotIdentifier = - snapshot?.text().substring(0, 60).replace(/\\n/g, "\\\\n") ?? - "[No Snapshot]"; - - let locator: Locator | undefined; - - // --- Resolve locator: Prioritize selector, then ref --- - if (validatedArgs?.selector) { - identifier = validatedArgs.selector; - identifierType = "selector"; - if (!identifier) { - throw new Error( - `Missing required 'selector' argument for tool ${toolName}.` - ); - } - try { - locator = page.locator(identifier); - } catch (locatorError) { - throw new Error( - `Failed to create locator for selector '${identifier}': ${ - locatorError instanceof Error - ? locatorError.message - : String(locatorError) - }` - ); - } - } else if (validatedArgs?.ref) { - identifier = validatedArgs.ref; - identifierType = "ref"; - if (!identifier) { - throw new Error( - `Missing required 'ref' argument for tool ${toolName}.` - ); - } - if (!snapshot) { - throw new Error( - `Cannot resolve ref '${identifier}' because no snapshot is available for session ${this.currentSessionId}. Capture a snapshot or ensure one exists.` - ); - } - try { - // Resolve using the snapshot we just retrieved - locator = snapshot.refLocator(identifier); - } catch (locatorError) { - // Use the existing snapshot identifier in the error - throw new Error( - `Failed to resolve ref ${identifier} using existing snapshot ${initialSnapshotIdentifier} before action attempt: ${ - locatorError instanceof Error - ? locatorError.message - : String(locatorError) - }` - ); - } - } else if (requiresIdentifier) { - // If neither ref nor selector is provided, but one is required - throw new Error( - `Missing required 'ref' or 'selector' argument for tool ${toolName}.` - ); - } else { - // No identifier needed or provided - identifierType = "none"; // Explicitly set to none - } - - // --- Single Attempt --- - try { - // Pass page, the used identifier (selector or ref), args, the resolved locator, and identifierType - const actionFnResult = await actionFn( - page, - identifier, - validatedArgs, - locator, - identifierType - ); - - if (typeof actionFnResult === "string") { - resultText = actionFnResult; - actionResult = undefined; - } else { - actionResult = actionFnResult; - const content = actionResult?.content; - if (Array.isArray(content) && content.length > 0) { - resultText = - content - .map((c: { type: string; text?: string }) => - c.type === "text" ? c.text : `[${c.type}]` - ) - .filter(Boolean) - .join(" ") || `${toolName} action completed.`; - } else { - resultText = `${toolName} action completed successfully.`; - } - } - lastError = null; - return { resultText, actionResult }; - } catch (error: any) { - throw new Error( - `Action ${toolName} failed: ${ - error instanceof Error ? error.message : String(error) - }` - ); - } - } - - async run(tool: Tool, args: any): Promise { - const toolName = tool.schema.name; - let initialPage: Page | null = null; - let initialBrowser: BrowserSession["browser"] | null = null; - let toolResultFromHandle: ToolResult | null = null; // Legacy handle result - let finalResult: CallToolResult = { - // Initialize finalResult here - content: [{ type: "text", text: `Initialization error for ${toolName}` }], - isError: true, - }; - - const logPrefix = `[Context.run ${toolName}] ${new Date().toISOString()}:`; - - let validatedArgs: any; - try { - validatedArgs = tool.schema.inputSchema.parse(args); - } catch (error) { - if (error instanceof z.ZodError) { - const errorMsg = error.issues.map((issue) => issue.message).join(", "); - return this.createErrorResult( - `Input validation failed: ${errorMsg}`, - toolName - ); - } - return this.createErrorResult( - `Input validation failed: ${ - error instanceof Error ? error.message : String(error) - }`, - toolName - ); - } - - const previousSessionId = this.currentSessionId; - if ( - validatedArgs?.sessionId && - validatedArgs.sessionId !== this.currentSessionId - ) { - this.currentSessionId = validatedArgs.sessionId; - this.clearLatestSnapshot(); - } - - if (toolName !== "browserbase_session_create") { - try { - const session = await getSession(this.currentSessionId, this.config); - if ( - !session || - !session.page || - session.page.isClosed() || - !session.browser || - !session.browser.isConnected() - ) { - if (this.currentSessionId !== previousSessionId) { - this.currentSessionId = previousSessionId; - } - throw new Error( - `Session ${this.currentSessionId} is invalid or browser/page is not available.` - ); - } - initialPage = session.page; - initialBrowser = session.browser; - } catch (sessionError) { - return this.createErrorResult( - `Error retrieving or validating session ${this.currentSessionId}: ${ - sessionError instanceof Error - ? sessionError.message - : String(sessionError) - }`, - toolName - ); - } - } - - let toolActionOutput: ToolActionResult | undefined = undefined; // New variable to store direct tool action output - let actionSucceeded = false; - let shouldCaptureSnapshotAfterAction = false; - let postActionSnapshot: PageSnapshot | undefined = undefined; - - try { - let actionToRun: (() => Promise) | undefined = - undefined; - let shouldCaptureSnapshot = false; - - try { - if ("handle" in tool && typeof tool.handle === "function") { - toolResultFromHandle = await tool.handle(this as any, validatedArgs); - actionToRun = toolResultFromHandle?.action; - shouldCaptureSnapshot = - toolResultFromHandle?.captureSnapshot ?? false; - shouldCaptureSnapshotAfterAction = shouldCaptureSnapshot; - } else { - throw new Error( - `Tool ${toolName} could not be handled (no handle method).` - ); - } - - if (actionToRun) { - toolActionOutput = await actionToRun(); - actionSucceeded = true; - } else { - throw new Error(`Tool ${toolName} handled without action.`); - } - } catch (error) { - process.stderr.write( - `${logPrefix} Error executing tool ${toolName}: ${ - error instanceof Error ? error.message : String(error) - }\\n` - ); - if (error instanceof Error && error.stack) { - process.stderr.write(`${logPrefix} Stack Trace: ${error.stack}\\n`); - } - // ----------------------- - finalResult = this.createErrorResult( - `Execution failed: ${ - error instanceof Error ? error.message : String(error) - }`, - toolName - ); - actionSucceeded = false; - shouldCaptureSnapshotAfterAction = false; - if ( - this.currentSessionId !== previousSessionId && - toolName !== "browserbase_session_create" - ) { - this.currentSessionId = previousSessionId; - } - } finally { - if (actionSucceeded && shouldCaptureSnapshotAfterAction) { - const preSnapshotDelay = 500; - await this.waitForTimeout(preSnapshotDelay); - try { - postActionSnapshot = await this.captureSnapshot(); - if (postActionSnapshot) { - process.stderr.write( - `[Context.run ${toolName}] Added snapshot to final result text.\n` - ); - } else { - process.stderr.write( - `[Context.run ${toolName}] WARN: Snapshot was expected after action but failed to capture.\n` - ); // Keep warning - } - } catch (postSnapError) { - process.stderr.write( - `[Context.run ${toolName}] WARN: Error capturing post-action snapshot: ${ - postSnapError instanceof Error - ? postSnapError.message - : String(postSnapError) - }\n` - ); // Keep warning - } - } else if ( - actionSucceeded && - toolName === "browserbase_snapshot" && - !postActionSnapshot - ) { - postActionSnapshot = this.latestSnapshots.get(this.currentSessionId); - } - - if (actionSucceeded) { - const finalContentItems: (TextContent | ImageContent)[] = []; - - // 1. Add content from the tool action itself - if (toolActionOutput?.content && toolActionOutput.content.length > 0) { - finalContentItems.push(...toolActionOutput.content); - } else { - // If toolActionOutput.content is empty/undefined but action succeeded, - // provide a generic success message. - finalContentItems.push({ type: "text", text: `${toolName} action completed successfully.` }); - } - - // 2. Prepare and add additional textual information (URL, Title, Snapshot) - const additionalInfoParts: string[] = []; - // Use read-only version to avoid creating sessions after close - const currentPage = this.getActivePageReadOnly(); - - if (currentPage) { - try { - const url = currentPage.url(); - const title = await currentPage - .title() - .catch(() => "[Error retrieving title]"); - additionalInfoParts.push(`- Page URL: ${url}`); - additionalInfoParts.push(`- Page Title: ${title}`); - } catch (pageStateError) { - additionalInfoParts.push( - "- [Error retrieving page state after action]" - ); - } - } else { - additionalInfoParts.push("- [Page unavailable after action]"); - } - - const snapshotToAdd = postActionSnapshot; - if (snapshotToAdd) { - additionalInfoParts.push( - `- Page Snapshot\n\`\`\`yaml\n${snapshotToAdd.text()}\n\`\`\`\n` - ); - } else { - additionalInfoParts.push( - `- [No relevant snapshot available after action]` - ); - } - - // 3. Add the additional information as a new TextContent item if it's not empty - if (additionalInfoParts.length > 0) { - // Add leading newlines if there's preceding content, to maintain separation - const additionalInfoText = (finalContentItems.length > 0 ? "\\n\\n" : "") + additionalInfoParts.join("\\n"); - finalContentItems.push({ type: "text", text: additionalInfoText }); - } - - finalResult = { - content: finalContentItems, - isError: false, - }; - } else { - // Error result is already set in catch block, but ensure it IS set. - if (!finalResult || !finalResult.isError) { - finalResult = this.createErrorResult( - `Unknown error occurred during ${toolName}`, - toolName - ); - } - } - return finalResult; - } - } catch (error) { - process.stderr.write( - `${logPrefix} Error running tool ${toolName}: ${ - error instanceof Error ? error.message : String(error) - }\n` - ); - throw error; - } - } -} diff --git a/browserbase/src/index.ts b/browserbase/src/index.ts deleted file mode 100644 index 8769336..0000000 --- a/browserbase/src/index.ts +++ /dev/null @@ -1,109 +0,0 @@ -import dotenv from "dotenv"; -dotenv.config(); - -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { z } from 'zod'; -import type { Tool } from "./tools/tool.js"; - -import navigate from "./tools/navigate.js"; -import snapshot from "./tools/snapshot.js"; -import keyboard from "./tools/keyboard.js"; -import getText from "./tools/getText.js"; -import session from "./tools/session.js"; -import common from "./tools/common.js"; -import contextTools from "./tools/context.js"; - -import { Context } from "./context.js"; -import type { Config } from "./config.js"; - -// Configuration schema for Smithery - matches existing Config interface -export const configSchema = z.object({ - browserbaseApiKey: z.string().describe("The Browserbase API Key to use"), - browserbaseProjectId: z.string().describe("The Browserbase Project ID to use"), - proxies: z.boolean().optional().describe("Whether or not to use Browserbase proxies"), - advancedStealth: z.boolean().optional().describe("Use advanced stealth mode. Only available to Browserbase Scale Plan users"), - context: z.object({ - contextId: z.string().optional().describe("The ID of the context to use"), - persist: z.boolean().optional().describe("Whether or not to persist the context") - }).optional(), - viewPort: z.object({ - browserWidth: z.number().optional().describe("The width of the browser"), - browserHeight: z.number().optional().describe("The height of the browser") - }).optional(), - cookies: z.array(z.object({ // Playwright Cookies Type in Zod format - name: z.string(), - value: z.string(), - domain: z.string(), - path: z.string().optional(), - expires: z.number().optional(), - httpOnly: z.boolean().optional(), - secure: z.boolean().optional(), - sameSite: z.enum(['Strict', 'Lax', 'None']).optional() - })).optional().describe("Cookies to inject into the Browserbase context"), - server: z.object({ - port: z.number().optional().describe("The port to listen on for SSE or MCP transport"), - host: z.string().optional().describe("The host to bind the server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces") - }).optional(), - tools: z.object({ - browserbase_take_screenshot: z.object({ - omitBase64: z.boolean().optional().describe("Whether to disable base64-encoded image responses") - }).optional() - }).optional() -}); - -// Default function for Smithery -export default function ({ config }: { config: z.infer }) { - if (!config.browserbaseApiKey) { - throw new Error('browserbaseApiKey is required'); - } - if (!config.browserbaseProjectId) { - throw new Error('browserbaseProjectId is required'); - } - - const server = new McpServer({ - name: 'Browserbase MCP Server', - version: '1.0.6' - }); - - const internalConfig: Config = config as Config; - - // Create the context, passing server instance and config - const context = new Context(server.server, internalConfig); - - const tools: Tool[] = [ - ...common, - ...snapshot, - ...keyboard, - ...getText, - ...navigate, - ...session, - ...contextTools, - ]; - - // Register each tool with the Smithery server - tools.forEach(tool => { - if (tool.schema.inputSchema instanceof z.ZodObject) { - server.tool( - tool.schema.name, - tool.schema.description, - tool.schema.inputSchema.shape, - async (params: z.infer) => { - try { - const result = await context.run(tool, params); - return result; - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - process.stderr.write(`[Smithery Error] ${new Date().toISOString()} Error running tool ${tool.schema.name}: ${errorMessage}\n`); - throw new Error(`Failed to run tool '${tool.schema.name}': ${errorMessage}`); - } - } - ); - } else { - console.warn( - `Tool "${tool.schema.name}" has an input schema that is not a ZodObject. Schema type: ${tool.schema.inputSchema.constructor.name}` - ); - } - }); - - return server.server; -} \ No newline at end of file diff --git a/browserbase/src/pageSnapshot.ts b/browserbase/src/pageSnapshot.ts deleted file mode 100644 index a32da2e..0000000 --- a/browserbase/src/pageSnapshot.ts +++ /dev/null @@ -1,112 +0,0 @@ -import type { Page, FrameLocator, Locator } from 'playwright-core'; -import yaml from 'yaml'; - -type PageOrFrameLocator = Page | FrameLocator; - -export class PageSnapshot { - private _frameLocators: PageOrFrameLocator[] = []; - private _text!: string; - - constructor() { - } - - static async create(page: Page): Promise { - const snapshot = new PageSnapshot(); - await snapshot._build(page); - return snapshot; - } - - text(): string { - return this._text; - } - - private async _build(page: Page) { - const yamlDocument = await this._snapshotFrame(page); - this._text = [ - `- Page Snapshot`, - '```yaml', - // Generate text directly from the returned document - yamlDocument.toString({ indentSeq: false }).trim(), - '```', - ].join('\n'); - } - - private async _snapshotFrame(frame: Page | FrameLocator) { - const frameIndex = this._frameLocators.push(frame) - 1; - let snapshotString = ''; - try { - snapshotString = await (frame.locator('body') as any).ariaSnapshot({ ref: true, emitGeneric: true }); - } catch (e) { - snapshotString = `error: Could not take snapshot. Error: ${e instanceof Error ? e.message : String(e)}`; - } - - const snapshot = yaml.parseDocument(snapshotString); - - const visit = async (node: any): Promise => { - if (yaml.isPair(node)) { - await Promise.all([ - visit(node.key).then(k => node.key = k), - visit(node.value).then(v => node.value = v) - ]); - } else if (yaml.isSeq(node) || yaml.isMap(node)) { - const items = [...node.items]; - node.items = await Promise.all(items.map(visit)); - } else if (yaml.isScalar(node)) { - if (typeof node.value === 'string') { - const value = node.value; - if (frameIndex > 0) - node.value = value.replace('[ref=', `[ref=f${frameIndex}`); - - if (value.startsWith('iframe ')) { - const ref = value.match(/\[ref=(.*)\]/)?.[1]; - if (ref) { - try { - const childFrameLocator = frame.frameLocator(`aria-ref=${ref}`); - const childSnapshot = await this._snapshotFrame(childFrameLocator); - return snapshot.createPair(node.value, childSnapshot); - } catch (error) { - return snapshot.createPair(node.value, ''); - } - } - } - } - } - return node; - }; - - - if (snapshot.contents) { - await visit(snapshot.contents); - } else { - const emptyMapDoc = yaml.parseDocument('{}'); - snapshot.contents = emptyMapDoc.contents; - } - return snapshot; - } - - refLocator(ref: string): Locator { - let frameIndex = 0; - let frame: PageOrFrameLocator; - let targetRef = ref; - - const match = ref.match(/^f(\d+)(.*)/); - if (match) { - frameIndex = parseInt(match[1], 10); - targetRef = match[2]; - } - - if (this._frameLocators.length === 0) { - throw new Error(`Frame locators not initialized. Cannot find frame for ref '${ref}'.`); - } - - if (frameIndex < 0 || frameIndex >= this._frameLocators.length) { - throw new Error(`Validation Error: Frame index ${frameIndex} derived from ref '${ref}' is out of bounds (found ${this._frameLocators.length} frames).`); - } - frame = this._frameLocators[frameIndex]; - - if (!frame) - throw new Error(`Frame (index ${frameIndex}) could not be determined. Provide ref from the most current snapshot.`); - - return frame.locator(`aria-ref=${targetRef}`); - } -} diff --git a/browserbase/src/program.ts b/browserbase/src/program.ts deleted file mode 100644 index b6dfb8b..0000000 --- a/browserbase/src/program.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { program } from 'commander'; -import * as fs from 'fs'; -import * as path from 'path'; -import { fileURLToPath } from 'url'; - -import createServerFunction from './index.js'; -import { ServerList } from './server.js'; -import { startHttpTransport, startStdioTransport } from './transport.js'; - -import { resolveConfig } from './config.js'; - -let __filename: string; -let __dirname: string; - -try { - // Try ES modules first - __filename = fileURLToPath(import.meta.url); - __dirname = path.dirname(__filename); -} catch { - // Fallback for CommonJS or when import.meta is not available - __filename = (globalThis as any).__filename || process.cwd() + '/dist/program.js'; - __dirname = path.dirname(__filename); -} - -// Load package.json using fs -const packageJSONPath = path.resolve(__dirname, '../package.json'); -const packageJSONBuffer = fs.readFileSync(packageJSONPath); -const packageJSON = JSON.parse(packageJSONBuffer.toString()); - -program - .version('Version ' + packageJSON.version) - .name(packageJSON.name) - .option('--browserbaseApiKey ', 'The Browserbase API Key to use') - .option('--browserbaseProjectId ', 'The Browserbase Project ID to use') - .option('--proxies', 'Use Browserbase proxies.') - .option('--advancedStealth', 'Use advanced stealth mode. Only available to Browserbase Scale Plan users.') - .option('--contextId ', 'Browserbase Context ID to use.') - .option('--persist [boolean]', 'Whether to persist the Browserbase context', true) - .option('--port ', 'Port to listen on for SSE transport.') - .option('--host ', 'Host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.') - .option('--cookies [json]', 'JSON array of cookies to inject into the browser. Format: [{"name":"cookie1","value":"val1","domain":"example.com"}, ...]') - .option('--browserWidth ', 'Browser width to use for the browser.') - .option('--browserHeight ', 'Browser height to use for the browser.') - .action(async options => { - const config = await resolveConfig(options); - const serverList = new ServerList(async() => createServerFunction( - { config: config as Required> & typeof config } - )); - setupExitWatchdog(serverList); - - if (options.port) - startHttpTransport(+options.port, options.host, serverList); - else - await startStdioTransport(serverList); - }); - -function setupExitWatchdog(serverList: ServerList) { - const handleExit = async () => { - setTimeout(() => process.exit(0), 15000); - await serverList.closeAll(); - process.exit(0); - }; - - process.stdin.on('close', handleExit); - process.on('SIGINT', handleExit); - process.on('SIGTERM', handleExit); -} - -program.parse(process.argv); \ No newline at end of file diff --git a/browserbase/src/tools/common.ts b/browserbase/src/tools/common.ts deleted file mode 100644 index acc06a1..0000000 --- a/browserbase/src/tools/common.ts +++ /dev/null @@ -1,111 +0,0 @@ -export {}; // Ensure file is treated as a module - -import { z } from 'zod'; -import type { Tool, ToolSchema, ToolResult } from "./tool.js"; -import type { Context } from '../context.js'; -import type { ToolActionResult } from '../context.js'; - -// --- Tool: Wait --- -const WaitInputSchema = z.object({ - time: z.number().describe("Time in seconds") -}); -type WaitInput = z.infer; - -const waitSchema: ToolSchema = { - name: "browserbase_wait", - description: "Wait for a specified time in seconds", - inputSchema: WaitInputSchema, -}; - -// Handle function for Wait -async function handleWait(context: Context, params: WaitInput): Promise { // Uses Context, returns ToolResult - const action = async (): Promise => { - await new Promise(resolve => setTimeout(resolve, params.time * 1000)); - return { content: [{ type: 'text', text: `Waited for ${params.time} seconds.` }] }; - }; - return { action, code: [], captureSnapshot: false, waitForNetwork: false }; -} - -// Define tool using handle -const waitTool: Tool = { - capability: 'core', - schema: waitSchema, - handle: handleWait, -}; - - -// --- Tool: Close --- -const CloseInputSchema = z.object({ - random_string: z.string().optional().describe("Dummy parameter") -}); -type CloseInput = z.infer; - -const closeSchema: ToolSchema = { - name: "browserbase_close", - description: "Close the current page...", - inputSchema: CloseInputSchema, -}; - -// Handle function for Close -async function handleClose(context: Context, params: CloseInput): Promise { - const action = async (): Promise => { - const page = await context.getActivePage(); - if (page && !page.isClosed()) { - await page.close(); - return { content: [{ type: 'text', text: `Page closed.` }] }; - } else { - return { content: [{ type: 'text', text: `No active page to close.` }] }; - } - }; - return { action, code: [], captureSnapshot: false, waitForNetwork: false }; -} - -// Define tool using handle -const closeTool: Tool = { - capability: 'core', // Add capability - schema: closeSchema, - handle: handleClose, -}; - - -// --- Tool: Resize --- -const ResizeInputSchema = z.object({ - width: z.number(), - height: z.number() -}); -type ResizeInput = z.infer; - -const resizeSchema: ToolSchema = { - name: "browserbase_resize", - description: "Resize window...", - inputSchema: ResizeInputSchema, -}; - -// Handle function for Resize -async function handleResize(context: Context, params: ResizeInput): Promise { - const action = async (): Promise => { - const page = await context.getActivePage(); - if (page && !page.isClosed()) { - await page.setViewportSize({ width: params.width, height: params.height }); - return { content: [{ type: 'text', text: `Resized page to ${params.width}x${params.height}.` }] }; - } else { - return { content: [{ type: 'text', text: `No active page to resize.` }] }; - } - }; - return { action, code: [], captureSnapshot: true, waitForNetwork: false }; -} - -// Define tool using handle -const resizeTool: Tool = { - capability: 'core', // Add capability - schema: resizeSchema, - handle: handleResize, -}; - - -// Export array of tools directly -export default [ - waitTool, - closeTool, - resizeTool, -]; \ No newline at end of file diff --git a/browserbase/src/tools/context.ts b/browserbase/src/tools/context.ts deleted file mode 100644 index 95b3422..0000000 --- a/browserbase/src/tools/context.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { z } from "zod"; -import type { Tool, ToolSchema, ToolResult } from "./tool.js"; -import type { Context } from "../context.js"; -import type { ToolActionResult } from "../context.js"; -import { Browserbase } from "@browserbasehq/sdk"; - -// Store contexts in memory -const contexts = new Map(); - -// --- Tool: Create Context --- -const CreateContextInputSchema = z.object({ - name: z - .string() - .optional() - .describe("Optional friendly name to reference this context later (otherwise, you'll need to use the returned ID)"), -}); -type CreateContextInput = z.infer; - -const createContextSchema: ToolSchema = { - name: "browserbase_context_create", - description: "Create a new Browserbase context for reusing cookies, authentication, and cached data across browser sessions", - inputSchema: CreateContextInputSchema, -}; - -async function handleCreateContext( - context: Context, - params: CreateContextInput -): Promise { - try { - const config = context.config; - - if (!config.browserbaseApiKey || !config.browserbaseProjectId) { - throw new Error("Browserbase API Key or Project ID is missing in the configuration"); - } - - const bb = new Browserbase({ - apiKey: config.browserbaseApiKey, - }); - - console.error("Creating new Browserbase context"); - const bbContext = await bb.contexts.create({ - projectId: config.browserbaseProjectId, - }); - - console.error(`Successfully created context: ${bbContext.id}`); - - // Store context ID with optional name if provided - const contextName = params.name || bbContext.id; - contexts.set(contextName, bbContext.id); - - const result: ToolActionResult = { - content: [ - { - type: "text", - text: `Created new Browserbase context with ID: ${bbContext.id}${params.name ? ` and name: ${params.name}` : ''}`, - }, - ], - }; - - return { - resultOverride: result, - action: async () => { - console.error("Create Context action"); - return result; - }, - code: [], - captureSnapshot: false, - waitForNetwork: false, - }; - } catch (error: any) { - console.error(`CreateContext handle failed: ${error.message || error}`); - throw new Error(`Failed to create Browserbase context: ${error.message || error}`); - } -} - -// --- Tool: Delete Context --- -const DeleteContextInputSchema = z.object({ - contextId: z - .string() - .optional() - .describe("The context ID to delete (required if name not provided)"), - name: z - .string() - .optional() - .describe("The friendly name of the context to delete (required if contextId not provided)"), -}); -type DeleteContextInput = z.infer; - -const deleteContextSchema: ToolSchema = { - name: "browserbase_context_delete", - description: "Delete a Browserbase context when you no longer need it", - inputSchema: DeleteContextInputSchema, -}; - -async function handleDeleteContext( - context: Context, - params: DeleteContextInput -): Promise { - try { - const config = context.config; - - if (!config.browserbaseApiKey) { - throw new Error("Browserbase API Key is missing in the configuration"); - } - - if (!params.contextId && !params.name) { - throw new Error("Missing required argument: either contextId or name must be provided"); - } - - // Resolve context ID either directly or by name - let contextId = params.contextId; - if (!contextId && params.name) { - contextId = contexts.get(params.name); - if (!contextId) { - throw new Error(`Context with name "${params.name}" not found`); - } - } - - console.error(`Deleting Browserbase context: ${contextId}`); - - // Delete using Browserbase API - const response = await fetch(`https://api.browserbase.com/v1/contexts/${contextId}`, { - method: 'DELETE', - headers: { - 'X-BB-API-Key': config.browserbaseApiKey, - }, - }); - - if (response.status !== 204) { - const errorText = await response.text(); - throw new Error(`Failed to delete context with status ${response.status}: ${errorText}`); - } - - // Remove from local store - if (params.name) { - contexts.delete(params.name); - } - - // Delete by ID too (in case it was stored multiple ways) - for (const [name, id] of contexts.entries()) { - if (id === contextId) { - contexts.delete(name); - } - } - - console.error(`Successfully deleted context: ${contextId}`); - - const result: ToolActionResult = { - content: [ - { - type: "text", - text: `Deleted Browserbase context with ID: ${contextId}`, - }, - ], - }; - - return { - resultOverride: result, - action: async () => { - console.error("Delete Context action"); - return result; - }, - code: [], - captureSnapshot: false, - waitForNetwork: false, - }; - } catch (error: any) { - console.error(`DeleteContext handle failed: ${error.message || error}`); - throw new Error(`Failed to delete Browserbase context: ${error.message || error}`); - } -} - -// Helper function to get a context ID from name or direct ID (exported for use by session.ts) -export function getContextId(nameOrId: string): string | undefined { - // First check if it's a direct context ID - if (nameOrId.length == 32) { // 32 char uuid - return nameOrId; - } - - // Otherwise, look it up by name - return contexts.get(nameOrId); -} - -// Define tools -const createContextTool: Tool = { - capability: "core", - schema: createContextSchema, - handle: handleCreateContext, -}; - -const deleteContextTool: Tool = { - capability: "core", - schema: deleteContextSchema, - handle: handleDeleteContext, -}; - -// Export as an array of tools -export default [createContextTool, deleteContextTool]; \ No newline at end of file diff --git a/browserbase/src/tools/getText.ts b/browserbase/src/tools/getText.ts deleted file mode 100644 index c1f6ec6..0000000 --- a/browserbase/src/tools/getText.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { z } from 'zod'; -import type { Tool, ToolSchema, ToolResult } from "./tool.js"; -import type { Context } from '../context.js'; -import type { ToolActionResult } from '../context.js'; - -// --- Tool: Get Text --- -const GetTextInputSchema = z.object({ - selector: z.string().optional().describe("Optional CSS selector to get text from. If omitted, gets text from the whole body."), -}); -type GetTextInput = z.infer; - -const getTextSchema: ToolSchema = { - name: "browserbase_get_text", - description: "Extract text content from the page or a specific element.", - inputSchema: GetTextInputSchema, -}; - -// Handle function for GetText -async function handleGetText(context: Context, params: GetTextInput): Promise { - const action = async (): Promise => { - const page = await context.getActivePage(); - if (!page) { - throw new Error('No active page found for getText'); - } - try { - let textContent: string | null; - if (params.selector) { - textContent = await page.textContent(params.selector, { timeout: 10000 }); - } else { - textContent = await page.textContent('body', { timeout: 10000 }); - } - return { content: [{ type: 'text', text: textContent ?? "" }] }; - } catch (error) { - console.error(`GetText action failed: ${error}`); - throw error; // Rethrow to be caught by Context.run's try/catch around handle/action - } - }; - - return { - action, - code: [], - captureSnapshot: false, - waitForNetwork: false, - }; -} - -// Define tool using handle -const getTextTool: Tool = { - capability: 'core', // Add capability - schema: getTextSchema, - handle: handleGetText, -}; - -export default [getTextTool]; \ No newline at end of file diff --git a/browserbase/src/tools/hover.ts b/browserbase/src/tools/hover.ts deleted file mode 100644 index a01265c..0000000 --- a/browserbase/src/tools/hover.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { Tool } from "./tool.js"; - -// Placeholder function for hover tool, accepting the flag -export function hover(captureSnapshot: boolean): Tool[] { - // TODO: Implement hoverTool and potentially use flag - return []; -} -export default hover; \ No newline at end of file diff --git a/browserbase/src/tools/keyboard.ts b/browserbase/src/tools/keyboard.ts deleted file mode 100644 index f989426..0000000 --- a/browserbase/src/tools/keyboard.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { z } from 'zod'; -import { defineTool, type ToolFactory } from './tool.js'; - -const pressKey: ToolFactory = captureSnapshot => defineTool({ - capability: 'core', - - schema: { - name: 'browserbase_press_key', - description: 'Press a key on the keyboard', - inputSchema: z.object({ - key: z.string().describe('Name of the key to press or a character to generate, such as `ArrowLeft` or `a`'), - }), - }, - - handle: async (context, params) => { - const page = await context.getActivePage(); - if (!page) { - throw new Error('No active page found for pressKey'); - } - - const code = [ - `// Press ${params.key}`, - `await page.keyboard.press('${params.key.replace(/'/g, "\\'")}');`, - ]; - - const action = () => page.keyboard.press(params.key); // Changed from tab.page to page - - return { - code, - action, - captureSnapshot, - waitForNetwork: true - }; - }, -}); - -const captureSnapshotValue = true; - -export default [ - pressKey(captureSnapshotValue), -]; \ No newline at end of file diff --git a/browserbase/src/tools/navigate.ts b/browserbase/src/tools/navigate.ts deleted file mode 100644 index f925c51..0000000 --- a/browserbase/src/tools/navigate.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { z } from 'zod'; -import { defineTool, type ToolFactory } from './tool.js'; -import type { ToolActionResult } from '../context.js'; - -const navigate: ToolFactory = captureSnapshot => defineTool({ - capability: 'core', - - schema: { - name: 'browserbase_navigate', - description: 'Navigate to a URL', - inputSchema: z.object({ - url: z.string().describe('The URL to navigate to'), - }), - }, - - handle: async (context, params) => { - const page = await context.getActivePage(); - if (!page) { - throw new Error('No active page found for navigate'); - } - const action = async (): Promise => { - await page.goto(params.url); - return { content: [{ type: 'text', text: `Navigated to ${params.url}` }] }; - }; - - const code = [ - `// Navigate to ${params.url}`, - `await page.goto('${params.url}');`, - ]; - - return { - action, - code, - captureSnapshot, - waitForNetwork: false, - }; - }, -}); - -const goBack: ToolFactory = captureSnapshot => defineTool({ - capability: 'history', - schema: { - name: 'browserbase_navigate_back', - description: 'Go back to the previous page', - inputSchema: z.object({}), - }, - - handle: async context => { - const page = await context.getActivePage(); - if (!page) { - throw new Error('No active page found for goBack'); - } - const action = async (): Promise => { - await page.goBack(); - return { content: [{ type: 'text', text: 'Navigated back' }] }; - }; - const code = [ - `// Navigate back`, - `await page.goBack();`, - ]; - - return { - action, - code, - captureSnapshot, - waitForNetwork: true, - }; - }, -}); - -const goForward: ToolFactory = captureSnapshot => defineTool({ - capability: 'history', - schema: { - name: 'browserbase_navigate_forward', - description: 'Go forward to the next page', - inputSchema: z.object({}), - }, - handle: async context => { - const page = await context.getActivePage(); - if (!page) { - throw new Error('No active page found for goForward'); - } - const action = async (): Promise => { - await page.goForward(); - return { content: [{ type: 'text', text: 'Navigated forward' }] }; - }; - const code = [ - `// Navigate forward`, - `await page.goForward();`, - ]; - return { - action, - code, - captureSnapshot, - waitForNetwork: true, - }; - }, -}); - -const captureSnapshotValue = true; - -export default [ - navigate(captureSnapshotValue), - goBack(captureSnapshotValue), - goForward(captureSnapshotValue), -]; \ No newline at end of file diff --git a/browserbase/src/tools/selectOption.ts b/browserbase/src/tools/selectOption.ts deleted file mode 100644 index 66ff6d9..0000000 --- a/browserbase/src/tools/selectOption.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { Tool } from "./tool.js"; - -// Placeholder function for select option tool, accepting the flag -export function selectOption(captureSnapshot: boolean): Tool[] { - // TODO: Implement selectOptionTool and potentially use flag - return []; -} -export default selectOption; \ No newline at end of file diff --git a/browserbase/src/tools/session.ts b/browserbase/src/tools/session.ts deleted file mode 100644 index 7565822..0000000 --- a/browserbase/src/tools/session.ts +++ /dev/null @@ -1,239 +0,0 @@ -import { z } from "zod"; -import type { Tool, ToolSchema, ToolResult } from "./tool.js"; -import type { Context } from "../context.js"; -import type { ToolActionResult } from "../context.js"; - -// Import SessionManager functions -import { - createNewBrowserSession, - defaultSessionId, - ensureDefaultSessionInternal, - cleanupSession, - type BrowserSession, -} from "../sessionManager.js"; - -// --- Tool: Create Session --- -const CreateSessionInputSchema = z.object({ - // Keep sessionId optional, but clarify its role - sessionId: z - .string() - .optional() - .describe( - "Optional session ID to use/reuse. If not provided or invalid, a new session is created." - ), -}); -type CreateSessionInput = z.infer; - -const createSessionSchema: ToolSchema = { - name: "browserbase_session_create", - description: - "Create or reuse a cloud browser session using Browserbase. Updates the active session.", - inputSchema: CreateSessionInputSchema, -}; - - -// Handle function for CreateSession using SessionManager -async function handleCreateSession( - context: Context, - params: CreateSessionInput -): Promise { - const action = async (): Promise => { - try { - const config = context.config; // Get config from context - let targetSessionId: string; - - if (params.sessionId) { - const projectId = config.browserbaseProjectId || ''; - targetSessionId = `${params.sessionId}_${projectId}`; - process.stderr.write( - `[tool.createSession] Attempting to create/assign session with specified ID: ${targetSessionId}` - ); - } else { - targetSessionId = defaultSessionId; - } - - let session: BrowserSession; - if (targetSessionId === defaultSessionId) { - session = await ensureDefaultSessionInternal(config); - } else { - session = await createNewBrowserSession(targetSessionId, config); - } - - if (!session || !session.browser || !session.page || !session.sessionId) { - throw new Error( - `SessionManager failed to return a valid session object with actualSessionId for ID: ${targetSessionId}` - ); - } - - context.currentSessionId = targetSessionId; - process.stderr.write( - `[tool.connected] Successfully connected to Browserbase session. Internal ID: ${targetSessionId}, Actual ID: ${session.sessionId}` - ); - - process.stderr.write(`[SessionManager] Browserbase Live Debugger URL: https://www.browserbase.com/sessions/${session.sessionId}`); - - return { - content: [ - { - type: "text", - text: `https://www.browserbase.com/sessions/${session.sessionId}`, - }, - ], - }; - } catch (error: any) { - process.stderr.write( - `[tool.createSession] Action failed: ${ - error.message || String(error) - }` - ); - // Re-throw to be caught by Context.run's error handling for actions - throw new Error( - `Failed to create Browserbase session: ${ - error.message || String(error) - }` - ); - } - }; - - // Return the ToolResult structure expected by Context.run - return { - action: action, - captureSnapshot: false, - code: [], - waitForNetwork: false, - }; -} - -// Define tool using handle -const createSessionTool: Tool = { - capability: "core", // Add capability - schema: createSessionSchema, - handle: handleCreateSession, -}; - -// --- Tool: Close Session --- -const CloseSessionInputSchema = z.object({ - random_string: z - .string() - .optional() - .describe("Dummy parameter to ensure consistent tool call format."), -}); -type CloseSessionInput = z.infer; - -const closeSessionSchema: ToolSchema = { - name: "browserbase_session_close", - description: - "Closes the current Browserbase session by disconnecting the Playwright browser. This will terminate the recording for the session.", - inputSchema: CloseSessionInputSchema, -}; - -async function handleCloseSession( - context: Context, - _params: CloseSessionInput -): Promise { - const code = [`// Attempting to close the current Browserbase session.`]; - - const action = async (): Promise => { - // Store the current session ID before it's potentially changed. - // This allows us to reference the original session ID later if needed. - const previousSessionId = context.currentSessionId; // Capture the ID before any changes - let browser: BrowserSession["browser"] | null = null; - let browserClosedSuccessfully = false; - let browserCloseErrorMessage = ""; - - // Step 1: Attempt to get the active browser instance WITHOUT creating a new one - try { - // Use read-only version to avoid creating new sessions - browser = context.getActiveBrowserReadOnly(); - } catch (error: any) { - process.stderr.write( - `[tool.closeSession] Error retrieving active browser (session ID was ${previousSessionId || 'default/unknown'}): ${error.message || String(error)}` - ); - // If we can't even get the browser, we can't close it. - // We will still proceed to reset context. - } - - // Step 2: If a browser instance was retrieved, attempt to close it - if (browser) { - try { - process.stderr.write( - `[tool.closeSession] Attempting to close browser for session: ${previousSessionId || 'default (actual might differ)'}` - ); - await browser.close(); - browserClosedSuccessfully = true; - process.stderr.write( - `[tool.closeSession] Browser connection for session (was ${previousSessionId}) closed.` - ); - - // Clean up the session from tracking - cleanupSession(previousSessionId); - - process.stderr.write( - `[tool.closeSession] View session replay at https://www.browserbase.com/sessions/${previousSessionId}` - ); - - } catch (error: any) { - browserCloseErrorMessage = error.message || String(error); - process.stderr.write( - `[tool.closeSession] Error during browser.close() for session (was ${previousSessionId}): ${browserCloseErrorMessage}` - ); - } - } else { - process.stderr.write( - `[tool.closeSession] No active browser instance found to close. (Session ID in context was: ${previousSessionId || 'default/unknown'}).` - ); - } - - // Step 3: Always reset the context's current session ID to default - // and clear snapshot if the previous session was a specific one. - const oldContextSessionId = context.currentSessionId; // This should effectively be 'previousSessionId' - context.currentSessionId = defaultSessionId; - if (oldContextSessionId && oldContextSessionId !== defaultSessionId) { - context.clearLatestSnapshot(); - process.stderr.write( - `[tool.closeSession] Snapshot cleared for previous session: ${oldContextSessionId}.` - ); - } - process.stderr.write( - `[tool.closeSession] Session context reset to default. Previous context session ID was ${oldContextSessionId || 'default/unknown'}.` - ); - - // Step 4: Determine the result message - if (browser && !browserClosedSuccessfully) { // An attempt was made to close, but it failed - throw new Error( - `Failed to close the Browserbase browser (session ID in context was ${previousSessionId || 'default/unknown'}). Error: ${browserCloseErrorMessage}. Session context has been reset to default.` - ); - } - - if (browserClosedSuccessfully) { // Browser was present and closed - let successMessage = `Browserbase session (associated with context ID ${previousSessionId || 'default'}) closed successfully. Context reset to default.`; - if (previousSessionId && previousSessionId !== defaultSessionId) { - successMessage += ` If this was a uniquely named session (${previousSessionId}), view replay (if available) at https://browserbase.com/sessions`; - } - return { content: [{ type: "text", text: successMessage }] }; - } - - // No browser was found, or browser was null initially. - let infoMessage = "No active browser instance was found to close. Session context has been reset to default."; - if (previousSessionId && previousSessionId !== defaultSessionId) { - // This means a specific session was in context, but no browser for it. - infoMessage = `No active browser found for session ID '${previousSessionId}' in context. The context has been reset to default.`; - } - return { content: [{ type: "text", text: infoMessage }] }; - }; - - return { - action: action, - code: code, - captureSnapshot: false, - waitForNetwork: false, - }; -} - -const closeSessionTool: Tool = { - capability: "core", - schema: closeSessionSchema, - handle: handleCloseSession, -}; - -export default [createSessionTool, closeSessionTool]; \ No newline at end of file diff --git a/browserbase/src/tools/snapshot.ts b/browserbase/src/tools/snapshot.ts deleted file mode 100644 index 5ec80e5..0000000 --- a/browserbase/src/tools/snapshot.ts +++ /dev/null @@ -1,499 +0,0 @@ -import { z } from "zod"; -import type { - TextContent, - ImageContent, -} from "@modelcontextprotocol/sdk/types.js"; -import type { Locator, PageScreenshotOptions } from "playwright-core"; - -import { defineTool, type ToolResult, } from "./tool.js"; -import type { Context, ToolActionResult } from "../context.js"; -import { PageSnapshot } from "../pageSnapshot.js"; -import { outputFile } from "../config.js"; - -// --- Tool: Snapshot --- -const SnapshotInputSchema = z.object({}); -type SnapshotInput = z.infer; - -const snapshot = defineTool({ - capability: "core", - schema: { - name: "browserbase_snapshot", - description: - "Capture a new accessibility snapshot of the current page state. Use this if the page has changed to ensure subsequent actions use an up-to-date page representation.", - inputSchema: SnapshotInputSchema, - }, - - handle: async ( - context: Context, - params: SnapshotInput - ): Promise => { - const action = async (): Promise => { - const content: (TextContent | ImageContent)[] = [ - { type: "text", text: "Accessibility snapshot captured." }, - ]; - return { content }; - }; - - return { - action, - code: [`// Request accessibility snapshot`], - captureSnapshot: true, - waitForNetwork: false, - resultOverride: { - content: [{ type: "text", text: "Accessibility snapshot initiated." }], - }, - }; - }, -}); - -// --- Element Schema & Types --- -const elementSchema = z.object({ - element: z.string().describe("Human-readable element description"), - ref: z - .string() - .describe("Exact target element reference from the page snapshot"), -}); -type ElementInput = z.infer; - -// --- Tool: Click (Adapted Handle, Example Action) --- -const click = defineTool({ - capability: "core", - schema: { - name: "browserbase_click", - description: "Perform click on a web page using ref", - inputSchema: elementSchema, - }, - handle: async ( - context: Context, - params: ElementInput - ): Promise => { - // Get locator directly from snapshot - const snapshot = context.snapshotOrDie(); - const locator = snapshot.refLocator(params.ref); - - const code = [ - `// Click ${params.element}`, - // Use generateLocator for code string - `// await page.${await generateLocator(locator)}.click();`, - ]; - - const action = async (): Promise => { - try { - // Use the locator directly for the action - await locator.click({ force: true, timeout: 30000 }); // Increased timeout like logs - } catch (actionError) { - const errorMessage = - actionError instanceof Error - ? actionError.message - : String(actionError); - throw new Error( - `Failed to click element '${params.element}'. Error: ${errorMessage}` - ); - } - return { - content: [{ type: "text", text: `Clicked ${params.element}` }], - }; - }; - - return { - code, - action, - captureSnapshot: true, - waitForNetwork: true, - }; - }, -}); - -// --- Tool: Drag (Adapted Handle, Example Action) --- -const dragInputSchema = z.object({ - startElement: z.string().describe("Source element description"), - startRef: z - .string() - .describe("Exact source element reference from the page snapshot"), - endElement: z.string().describe("Target element description"), - endRef: z - .string() - .describe("Exact target element reference from the page snapshot"), -}); -type DragInput = z.infer; - -const drag = defineTool({ - capability: "core", - schema: { - name: "browserbase_drag", - description: "Perform drag and drop between two elements using ref.", - inputSchema: dragInputSchema, - }, - handle: async (context: Context, params: DragInput): Promise => { - // Get locators directly from snapshot - const snapshot = context.snapshotOrDie(); - const startLocator = snapshot.refLocator(params.startRef); - const endLocator = snapshot.refLocator(params.endRef); - - const code = [ - `// Drag ${params.startElement} to ${params.endElement}`, - // Use generateLocator for code string - `// await page.${await generateLocator( - startLocator - )}.dragTo(page.${await generateLocator(endLocator)});`, - ]; - - const action = async (): Promise => { - try { - // Use locators directly for the action - await startLocator.dragTo(endLocator, { timeout: 5000 }); - } catch (dragError) { - const errorMsg = - dragError instanceof Error ? dragError.message : String(dragError); - throw new Error( - `Failed to drag '${params.startElement}' to '${params.endElement}'. Error: ${errorMsg}` - ); - } - return { - content: [ - { - type: "text", - text: `Dragged ${params.startElement} to ${params.endElement}`, - }, - ], - }; - }; - - return { action, code, captureSnapshot: true, waitForNetwork: true }; - }, -}); - -// --- Tool: Hover (Adapted Handle, Example Action) --- -const hover = defineTool({ - capability: "core", - schema: { - name: "browserbase_hover", - description: "Hover over element on page using ref.", - inputSchema: elementSchema, - }, - handle: async ( - context: Context, - params: ElementInput - ): Promise => { - // Get locator directly from snapshot - const snapshot = context.snapshotOrDie(); - const locator = snapshot.refLocator(params.ref); - - const code = [ - `// Hover over ${params.element}`, - // Use generateLocator for code string - `// await page.${await generateLocator(locator)}.hover();`, - ]; - - const action = async (): Promise => { - try { - // Use locator directly for the action - await locator.hover({ timeout: 5000 }); - } catch (hoverError) { - const errorMsg = - hoverError instanceof Error ? hoverError.message : String(hoverError); - throw new Error( - `Failed to hover over element '${params.element}'. Error: ${errorMsg}` - ); - } - return { - content: [{ type: "text", text: `Hovered over: ${params.element}` }], - }; - }; - - return { action, code, captureSnapshot: true, waitForNetwork: true }; - }, -}); - -// --- Tool: Type (Adapted Handle, Example Action) --- -const typeSchema = elementSchema.extend({ - text: z.string().describe("Text to type into the element"), - submit: z - .boolean() - .optional() - .describe("Whether to submit entered text (press Enter after)"), - slowly: z - .boolean() - .optional() - .default(true) - .describe("Whether to type one character at a time."), -}); -type TypeInput = z.infer; - -const type = defineTool({ - capability: "core", - schema: { - name: "browserbase_type", - description: "Type text into editable element using ref.", - inputSchema: typeSchema, - }, - handle: async (context: Context, params: TypeInput): Promise => { - // Get locator directly from snapshot - const snapshot = context.snapshotOrDie(); - const locator = snapshot.refLocator(params.ref); - - const code: string[] = []; - const steps: (() => Promise)[] = []; - - if (params.slowly) { - code.push( - `// Press "${params.text}" sequentially into "${params.element}"` - ); - code.push( - `// await page.${await generateLocator( - locator - )}.pressSequentially('${params.text.replace(/'/g, "\\'")}');` - ); - steps.push(() => - locator.pressSequentially(params.text, { delay: 50 }) - ); - } else { - code.push(`// Fill "${params.text}" into "${params.element}"`); - code.push( - `// await page.${await generateLocator( - locator - )}.fill('${params.text.replace(/'/g, "\\'")}');` - ); - steps.push(async () => { - await locator.waitFor({ state: "visible"}); - if (!(await locator.isEditable())) { - throw new Error( - `Element '${params.element}' was visible but not editable.` - ); - } - await locator.fill("", { force: true, timeout: 5000 }); // Force empty fill first - await locator.fill(params.text, { force: true, timeout: 5000 }); // Force fill with text - }); - } - - if (params.submit) { - code.push(`// Submit text`); - code.push( - `// await page.${await generateLocator(locator)}.press('Enter');` - ); - steps.push(() => locator.press("Enter", { timeout: 5000 })); - } - - const action = async (): Promise => { - try { - // Execute the steps sequentially - await steps.reduce((acc, step) => acc.then(step), Promise.resolve()); - } catch (typeError) { - const errorMsg = - typeError instanceof Error ? typeError.message : String(typeError); - throw new Error( - `Failed to type into or submit element '${params.element}'. Error: ${errorMsg}` - ); - } - return { - content: [ - { - type: "text", - text: `Typed "${params.text}" into: ${params.element}${ - params.submit ? " and submitted" : "" - }`, - }, - ], - }; - }; - - return { action, code, captureSnapshot: true, waitForNetwork: true }; - }, -}); - -// --- Tool: Select Option (Adapted Handle, Example Action) --- -const selectOptionSchema = elementSchema.extend({ - values: z - .array(z.string()) - .describe("Array of values to select in the dropdown."), -}); -type SelectOptionInput = z.infer; - -const selectOption = defineTool({ - capability: "core", - schema: { - name: "browserbase_select_option", - description: "Select an option in a dropdown using ref.", - inputSchema: selectOptionSchema, - }, - handle: async ( - context: Context, - params: SelectOptionInput - ): Promise => { - // Get locator directly from snapshot - const snapshot = context.snapshotOrDie(); - const locator = snapshot.refLocator(params.ref); - - const code = [ - `// Select options [${params.values.join(", ")}] in ${params.element}`, - // Remove javascript.formatObject, use simple JSON.stringify for code comment - `// await page.${await generateLocator( - locator - )}.selectOption(${JSON.stringify(params.values)});`, - ]; - - const action = async (): Promise => { - try { - // Use locator directly for the action - await locator.waitFor({ state: "visible", timeout: 5000 }); - await locator.selectOption(params.values, { timeout: 5000 }); - } catch (selectError) { - const errorMsg = - selectError instanceof Error - ? selectError.message - : String(selectError); - throw new Error( - `Failed to select option(s) in element '${params.element}'. Error: ${errorMsg}` - ); - } - return { - content: [ - { type: "text", text: `Selected options in: ${params.element}` }, - ], - }; - }; - - return { action, code, captureSnapshot: true, waitForNetwork: true }; - }, -}); - -// --- Tool: Screenshot (Adapted Handle, Example Action) --- -const screenshotSchema = z.object({ - raw: z - .boolean() - .optional() - .describe( - "Whether to return without compression (PNG). Default is false (JPEG)." - ), - element: z - .string() - .optional() - .describe("Human-readable element description."), - ref: z - .string() - .optional() - .describe("Exact target element reference from the page snapshot.") -}); - -type ScreenshotInput = z.infer; - -const screenshot = defineTool({ - capability: "core", - schema: { - name: "browserbase_take_screenshot", - description: `Take a screenshot of the current page or element using ref.`, - inputSchema: screenshotSchema, - }, - handle: async ( - context: Context, - params: ScreenshotInput - ): Promise => { - if (!!params.element !== !!params.ref) { - throw new Error("Both element and ref must be provided or neither."); - } - - const page = await context.getActivePage(); - if (!page) { - throw new Error("No active page found for screenshot"); - } - // Conditionally get snapshot only if ref is provided - let pageSnapshot: PageSnapshot | null = null; - if (params.ref) { - pageSnapshot = context.snapshotOrDie(); - } - const fileType = params.raw ? "png" : "jpeg"; - const fileName = await outputFile( - context.config, - `screenshot-${Date.now()}.${fileType}` - ); - - const baseOptions: PageScreenshotOptions = { - scale: "css", - timeout: 15000, // Kept existing timeout - }; - - let options: PageScreenshotOptions; - - if (fileType === "jpeg") { - options = { - ...baseOptions, - type: "jpeg", - quality: 50, // Quality is only for jpeg - path: fileName, - }; - } else { - options = { - ...baseOptions, - type: "png", - path: fileName, - }; - } - - const isElementScreenshot = params.element && params.ref; - const code: string[] = []; - code.push( - `// Screenshot ${ - isElementScreenshot ? params.element : "viewport" - } and save it as ${fileName}` - ); - - // Conditionally get locator only if ref and snapshot are available - const locator = - params.ref && pageSnapshot ? pageSnapshot.refLocator(params.ref) : null; - - // Use JSON.stringify for code generation as javascript.formatObject is not available - const optionsForCode = { ...options }; - // delete optionsForCode.path; // Path is an internal detail for saving, not usually part of the "command" log - - if (locator) { - code.push( - `// await page.${await generateLocator( - locator - )}.screenshot(${JSON.stringify(optionsForCode)});` - ); - } else { - code.push(`// await page.screenshot(${JSON.stringify(optionsForCode)});`); - } - - const action = async (): Promise => { - // Access config via context.config - const includeBase64 = - !context.config.tools?.browserbase_take_screenshot?.omitBase64; - - // Use the page directly for full page screenshots if locator is null - const screenshotBuffer = locator - ? await locator.screenshot(options) - : await page.screenshot(options); - - if (includeBase64) { - const rawBase64 = screenshotBuffer.toString("base64"); - return { - content: [ - { - type: "image", - format: fileType, // format might be redundant if mimeType is present, but kept for now - mimeType: fileType === "png" ? `image/png` : `image/jpeg`, - data: rawBase64, - }, - ], - }; - } else { - // If base64 is not included, return an empty content array - return { content: [] }; - } - }; - - return { - code, - action, - captureSnapshot: true, - waitForNetwork: false, - }; - }, -}); - -export async function generateLocator(locator: Locator): Promise { - return (locator as any)._generateLocatorString(); -} - -export default [snapshot, click, drag, hover, type, selectOption, screenshot]; \ No newline at end of file diff --git a/browserbase/src/tools/tool.ts b/browserbase/src/tools/tool.ts deleted file mode 100644 index 197b01c..0000000 --- a/browserbase/src/tools/tool.ts +++ /dev/null @@ -1,67 +0,0 @@ -import type { ImageContent, TextContent } from '@modelcontextprotocol/sdk/types.js'; -import type { z } from 'zod'; -import type { Context } from '../context.js'; -import type * as playwright from 'playwright'; -import type { ToolCapability } from '../config.js'; -import type { BrowserSession } from '../sessionManager.js'; -import type { Server } from '@modelcontextprotocol/sdk/server/index.js'; -import type { Config } from '../config.js'; - -export type ToolSchema = { - name: string; - description: string; - inputSchema: Input; -}; - -// Export InputType -export type InputType = z.Schema; - -export type FileUploadModalState = { - type: 'fileChooser'; - description: string; - fileChooser: playwright.FileChooser; -}; - -export type DialogModalState = { - type: 'dialog'; - description: string; - dialog: playwright.Dialog; -}; - -export type ModalState = FileUploadModalState | DialogModalState; - -export type ToolActionResult = { content?: (ImageContent | TextContent)[] } | undefined | void; - -export type ToolResult = { - code: string[]; - action?: () => Promise; - captureSnapshot: boolean; - waitForNetwork: boolean; - resultOverride?: ToolActionResult; -}; - -export type Tool = { - capability: ToolCapability; - schema: ToolSchema; - clearsModalState?: ModalState['type']; - handle: (context: Context, params: z.output) => Promise; - }; - - export type ToolFactory = (snapshot: boolean) => Tool; - - export function defineTool(tool: Tool): Tool { - return tool; - } - -export {}; // Ensure this is treated as a module - -// Represents the execution context for a tool -// Might include the page, server instance for notifications, etc. -export interface ToolContext { - page: BrowserSession['page']; - browser: BrowserSession['browser']; - server: Server; - sessionId: string; - config: Config; - context: Context; // The main context instance -} \ No newline at end of file diff --git a/browserbase/src/tools/toolUtils.ts b/browserbase/src/tools/toolUtils.ts deleted file mode 100644 index fd09fc9..0000000 --- a/browserbase/src/tools/toolUtils.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { CallToolResult, TextContent } from "@modelcontextprotocol/sdk/types.js"; - -/** - * Creates a standardized error result for tool calls. - * @param message The error message text. - * @param toolName Optional tool name for logging/context. - * @returns CallToolResult object indicating an error. - */ -export function createErrorResult(message: string, toolName?: string): CallToolResult { - const prefix = toolName ? `[${toolName}] Error: ` : "Error: "; - // console.error(prefix + message); - return { - content: [{ type: "text", text: prefix + message } as TextContent], - isError: true, - }; -} - -/** - * Creates a standardized success result with text content. - * @param message The success message text. - * @param toolName Optional tool name for logging/context. - * @returns CallToolResult object indicating success. - */ -export function createSuccessResult(message: string, toolName?: string): CallToolResult { - const prefix = toolName ? `[${toolName}] Success: ` : "Success: "; - // console.log(prefix + message); // Log success - return { - content: [{ type: "text", text: message } as TextContent], - isError: false, - }; -} \ No newline at end of file diff --git a/browserbase/src/tools/utils.ts b/browserbase/src/tools/utils.ts deleted file mode 100644 index 4cdc7e3..0000000 --- a/browserbase/src/tools/utils.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type * as playwright from 'playwright'; -import type { Context } from '../context.js'; - -export async function waitForCompletion(context: Context, page: playwright.Page, callback: () => Promise): Promise { - const requests = new Set(); - let frameNavigated = false; - let waitCallback: () => void = () => {}; - const waitBarrier = new Promise(f => { waitCallback = f; }); - - const requestListener = (request: playwright.Request) => requests.add(request); - const requestFinishedListener = (request: playwright.Request) => { - requests.delete(request); - if (!requests.size) - waitCallback(); - }; - - const frameNavigateListener = (frame: playwright.Frame) => { - if (frame.parentFrame()) - return; - frameNavigated = true; - dispose(); - clearTimeout(timeout); - void frame.waitForLoadState('load').then(() => { - waitCallback(); - }); - }; - - const onTimeout = () => { - dispose(); - waitCallback(); - }; - - page.on('request', requestListener); - page.on('requestfinished', requestFinishedListener); - page.on('framenavigated', frameNavigateListener); - const timeout = setTimeout(onTimeout, 10000); - - const dispose = () => { - page.off('request', requestListener); - page.off('requestfinished', requestFinishedListener); - page.off('framenavigated', frameNavigateListener); - clearTimeout(timeout); - }; - - try { - const result = await callback(); - if (!requests.size && !frameNavigated) - waitCallback(); - await waitBarrier; - await context.waitForTimeout(1000); - return result; - } finally { - dispose(); - } -} - -export function sanitizeForFilePath(s: string) { - return s.replace(/[^a-zA-Z0-9_.-]/g, '_'); // More robust sanitization -} \ No newline at end of file diff --git a/browserbase/src/transport.ts b/browserbase/src/transport.ts deleted file mode 100644 index 82a703a..0000000 --- a/browserbase/src/transport.ts +++ /dev/null @@ -1,118 +0,0 @@ -import http from 'node:http'; -import assert from 'node:assert'; -import crypto from 'node:crypto'; - -import { ServerList } from './server.js'; -import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; -import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; -import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; - -export async function startStdioTransport(serverList: ServerList) { - const server = await serverList.create(); - await server.connect(new StdioServerTransport()); -} - -async function handleSSE(req: http.IncomingMessage, res: http.ServerResponse, url: URL, serverList: ServerList, sessions: Map) { - if (req.method === 'POST') { - const sessionId = url.searchParams.get('sessionId'); - if (!sessionId) { - res.statusCode = 400; - return res.end('Missing sessionId'); - } - - const transport = sessions.get(sessionId); - if (!transport) { - res.statusCode = 404; - return res.end('Session not found'); - } - - return await transport.handlePostMessage(req, res); - } else if (req.method === 'GET') { - const transport = new SSEServerTransport('/sse', res); - sessions.set(transport.sessionId, transport); - const server = await serverList.create(); - res.on('close', () => { - sessions.delete(transport.sessionId); - serverList.close(server).catch(e => { - // eslint-disable-next-line no-console - // console.error(e); - }); - }); - return await server.connect(transport); - } - - res.statusCode = 405; - res.end('Method not allowed'); -} - -async function handleStreamable(req: http.IncomingMessage, res: http.ServerResponse, serverList: ServerList, sessions: Map) { - const sessionId = req.headers['mcp-session-id'] as string | undefined; - if (sessionId) { - const transport = sessions.get(sessionId); - if (!transport) { - res.statusCode = 404; - res.end('Session not found'); - return; - } - return await transport.handleRequest(req, res); - } - - if (req.method === 'POST') { - const transport = new StreamableHTTPServerTransport({ - sessionIdGenerator: () => crypto.randomUUID(), - onsessioninitialized: sessionId => { - sessions.set(sessionId, transport); - } - }); - transport.onclose = () => { - if (transport.sessionId) - sessions.delete(transport.sessionId); - }; - const server = await serverList.create(); - await server.connect(transport); - return await transport.handleRequest(req, res); - } - - res.statusCode = 400; - res.end('Invalid request'); -} - -export function startHttpTransport(port: number, hostname: string | undefined, serverList: ServerList) { - const sseSessions = new Map(); - const streamableSessions = new Map(); - const httpServer = http.createServer(async (req, res) => { - const url = new URL(`http://localhost${req.url}`); - if (url.pathname.startsWith('/mcp')) - await handleStreamable(req, res, serverList, streamableSessions); - else - await handleSSE(req, res, url, serverList, sseSessions); - }); - httpServer.listen(port, hostname, () => { - const address = httpServer.address(); - assert(address, 'Could not bind server socket'); - let url: string; - if (typeof address === 'string') { - url = address; - } else { - const resolvedPort = address.port; - let resolvedHost = address.family === 'IPv4' ? address.address : `[${address.address}]`; - if (resolvedHost === '0.0.0.0' || resolvedHost === '[::]') - resolvedHost = 'localhost'; - url = `http://${resolvedHost}:${resolvedPort}`; - } - const message = [ - `Listening on ${url}`, - 'Put this in your client config:', - JSON.stringify({ - 'mcpServers': { - 'browserbase': { - 'url': `${url}/sse` - } - } - }, undefined, 2), - 'If your client supports streamable HTTP, you can use the /mcp endpoint instead.', - ].join('\n'); - // eslint-disable-next-line no-console - console.log(message); - }); -} \ No newline at end of file diff --git a/browserbase/tests/.gitkeep b/browserbase/tests/.gitkeep deleted file mode 100644 index 5c3383d..0000000 --- a/browserbase/tests/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -# Placeholder for tests \ No newline at end of file diff --git a/browserbase/tsconfig.json b/browserbase/tsconfig.json deleted file mode 100644 index 3067ef0..0000000 --- a/browserbase/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "outDir": "dist", - "rootDir": "src" - }, - "include": ["src/**/*.ts"], - "exclude": ["node_modules"] -} \ No newline at end of file diff --git a/browserbase/utils/.gitkeep b/browserbase/utils/.gitkeep deleted file mode 100644 index 00b6d7b..0000000 --- a/browserbase/utils/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -# Placeholder for utility scripts \ No newline at end of file diff --git a/cli.js b/cli.js new file mode 100755 index 0000000..1a2c54a --- /dev/null +++ b/cli.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +import "./dist/program.js"; diff --git a/config.d.ts b/config.d.ts new file mode 100644 index 0000000..6e1692e --- /dev/null +++ b/config.d.ts @@ -0,0 +1,101 @@ +import type { Cookie } from "playwright-core"; +import type { AvailableModelSchema } from "@browserbasehq/stagehand"; + +export type Config = { + /** + * The Browserbase API Key to use + */ + browserbaseApiKey?: string; + /** + * The Browserbase Project ID to use + */ + browserbaseProjectId?: string; + /** + * Whether or not to use Browserbase proxies + * https://docs.browserbase.com/features/proxies + * + * @default false + */ + proxies?: boolean; + /** + * Use advanced stealth mode. Only available to Browserbase Scale Plan users. + * + * @default false + */ + advancedStealth?: boolean; + /** + * Potential Browserbase Context to use + * Would be a context ID + */ + context?: { + /** + * The ID of the context to use + */ + contextId?: string; + /** + * Whether or not to persist the context + * + * @default true + */ + persist?: boolean; + }; + /** + * The viewport of the browser + * @default { browserWidth: 1024, browserHeight: 768 } + */ + viewPort?: { + /** + * The width of the browser + */ + browserWidth?: number; + /** + * The height of the browser + */ + browserHeight?: number; + }; + /** + * Cookies to inject into the Browserbase context + * Format: Array of cookie objects with name, value, domain, and optional path, expires, httpOnly, secure, sameSite + */ + cookies?: Cookie[]; + /** + * Server configuration for MCP transport layer + * + * Controls how the MCP server binds and listens for connections. + * When port is specified, the server will start an SHTTP transport. + * When both port and host are undefined, the server uses stdio transport. + * + * Security considerations: + * - Use localhost (default) for local development + * - Use 0.0.0.0 only when you need external access and have proper security measures + * - Consider firewall rules and network security when exposing the server + */ + server?: { + /** + * The port to listen on for SHTTP or MCP transport. + * If undefined, uses stdio transport instead of HTTP. + * + * @example 3000 + */ + port?: number; + /** + * The host to bind the server to. + * + * @default "localhost" - Only accepts local connections + * @example "0.0.0.0" - Accepts connections from any interface (use with caution) + */ + host?: string; + }; + /** + * The Model that Stagehand uses + * Available models: OpenAI, Claude, Gemini, Cerebras, Groq, and other providers + * + * @default "google/gemini-2.0-flash" + */ + modelName?: AvailableModelSchema; + /** + * API key for the custom model provider + * Required when using a model other than the default google/gemini-2.0-flash + */ + modelApiKey?: string; +}; diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..b4c556f --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,30 @@ +import js from "@eslint/js"; +import globals from "globals"; +import tseslint from "typescript-eslint"; +import { defineConfig } from "eslint/config"; + +export default defineConfig([ + { + files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], + plugins: { js }, + extends: ["js/recommended"], + ignores: ["dist/**/*"], + }, + { + files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], + languageOptions: { globals: { ...globals.browser, ...globals.node } }, + ignores: ["dist/**/*"], + }, + ...tseslint.configs.recommended, + { + files: ["src/types/**/*.ts", "src/mcp/**/*.ts"], + rules: { + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/ban-ts-comment": "off", + }, + }, + { + ignores: ["dist/**/*", "node_modules/**/*"], + }, +]); diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..fea6154 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,6 @@ +import type { Server } from "@modelcontextprotocol/sdk/server/index.js"; + +import type { Config } from "./config"; + +export declare function createServer(config?: Config): Promise; +export {}; diff --git a/index.js b/index.js new file mode 100644 index 0000000..d918704 --- /dev/null +++ b/index.js @@ -0,0 +1,2 @@ +import { createServer } from "./dist/index.js"; +export default { createServer }; diff --git a/package.json b/package.json new file mode 100644 index 0000000..b2bf7b6 --- /dev/null +++ b/package.json @@ -0,0 +1,62 @@ +{ + "name": "@browserbasehq/mcp", + "version": "2.0.0", + "description": "MCP server for AI web browser automation using Browserbase and Stagehand", + "license": "Apache-2.0", + "author": "Browserbase, Inc. (https://www.browserbase.com/)", + "homepage": "https://www.browserbase.com", + "bugs": "https://github.com/modelcontextprotocol/servers/issues", + "type": "module", + "main": "./cli.js", + "bin": { + "mcp-server-browserbase": "cli.js" + }, + "files": [ + "dist", + "cli.js", + "index.d.ts", + "index.js", + "config.d.ts" + ], + "scripts": { + "build": "tsc && shx chmod +x dist/*.js", + "prepare": "husky", + "watch": "tsc --watch", + "smithery": "npx @smithery/cli dev src/index.ts", + "inspector": "npx @modelcontextprotocol/inspector build/index.js", + "lint": "eslint . --ext .ts", + "prettier:check": "prettier --check .", + "prettier:fix": "prettier --write .", + "clean": "rm -rf dist", + "prepublishOnly": "pnpm clean && pnpm build" + }, + "lint-staged": { + "*.{js,jsx,ts,tsx,json,css,scss,md}": [ + "prettier --write", + "eslint --fix" + ] + }, + "dependencies": { + "@browserbasehq/stagehand": "^2.4.0", + "@modelcontextprotocol/sdk": "^1.13.1", + "@playwright/test": "^1.49.0", + "commander": "^14.0.0", + "dotenv": "^16.4.6", + "playwright-core": "^1.53.2", + "zod": "^3.25.67" + }, + "devDependencies": { + "@eslint/js": "^9.29.0", + "eslint": "^9.29.0", + "eslint-plugin-react": "^7.37.5", + "globals": "^16.2.0", + "husky": "^9.1.7", + "lint-staged": "^16.1.2", + "prettier": "^3.6.1", + "shx": "^0.3.4", + "tsx": "^4.20.3", + "typescript": "^5.6.2", + "typescript-eslint": "^8.35.0" + }, + "packageManager": "pnpm@10.12.4+sha512.5ea8b0deed94ed68691c9bad4c955492705c5eeb8a87ef86bc62c74a26b037b08ff9570f108b2e4dbd1dd1a9186fea925e527f141c648e85af45631074680184" +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..e9cd0e5 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,4357 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@browserbasehq/stagehand': + specifier: ^2.4.0 + version: 2.4.0(deepmerge@4.3.1)(dotenv@16.6.1)(react@19.1.0)(zod@3.25.67) + '@modelcontextprotocol/sdk': + specifier: ^1.13.1 + version: 1.13.1 + '@playwright/test': + specifier: ^1.49.0 + version: 1.53.1 + commander: + specifier: ^14.0.0 + version: 14.0.0 + dotenv: + specifier: ^16.4.6 + version: 16.6.1 + playwright-core: + specifier: ^1.53.2 + version: 1.53.2 + zod: + specifier: ^3.25.67 + version: 3.25.67 + devDependencies: + '@eslint/js': + specifier: ^9.29.0 + version: 9.29.0 + eslint: + specifier: ^9.29.0 + version: 9.29.0 + eslint-plugin-react: + specifier: ^7.37.5 + version: 7.37.5(eslint@9.29.0) + globals: + specifier: ^16.2.0 + version: 16.2.0 + husky: + specifier: ^9.1.7 + version: 9.1.7 + lint-staged: + specifier: ^16.1.2 + version: 16.1.2 + prettier: + specifier: ^3.6.1 + version: 3.6.1 + shx: + specifier: ^0.3.4 + version: 0.3.4 + tsx: + specifier: ^4.20.3 + version: 4.20.3 + typescript: + specifier: ^5.6.2 + version: 5.8.3 + typescript-eslint: + specifier: ^8.35.0 + version: 8.35.0(eslint@9.29.0)(typescript@5.8.3) + +packages: + + '@ai-sdk/anthropic@1.2.12': + resolution: {integrity: sha512-YSzjlko7JvuiyQFmI9RN1tNZdEiZxc+6xld/0tq/VkJaHpEzGAb1yiNxxvmYVcjvfu/PcvCxAAYXmTYQQ63IHQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/azure@1.3.23': + resolution: {integrity: sha512-vpsaPtU24RBVk/IMM5UylR/N4RtAuL2NZLWc7LJ3tvMTHu6pI46a7w+1qIwR3F6yO9ehWR8qvfLaBefJNFxaVw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/cerebras@0.2.14': + resolution: {integrity: sha512-BIDf9hfgAEEu3xhNUyBWvAzeSgt7A7cSMj7UcKS4jhBDybRoKjoEGWHC7916j8LS/5Hkdmo6jwaxWxn/gZJYbQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/deepseek@0.2.14': + resolution: {integrity: sha512-TISD1FzBWuQkHEHoVustoJILV33ZNgfYxeTkq1xU2vHEZuWTGZV7/IlXixyFsfqDCdVgrbLeIABk5FuCw7niLg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/google@1.2.19': + resolution: {integrity: sha512-Xgl6eftIRQ4srUdCzxM112JuewVMij5q4JLcNmHcB68Bxn9dpr3MVUSPlJwmameuiQuISIA8lMB+iRiRbFsaqA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/groq@1.2.9': + resolution: {integrity: sha512-7MoDaxm8yWtiRbD1LipYZG0kBl+Xe0sv/EeyxnHnGPZappXdlgtdOgTZVjjXkT3nWP30jjZi9A45zoVrBMb3Xg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/mistral@1.2.8': + resolution: {integrity: sha512-lv857D9UJqCVxiq2Fcu7mSPTypEHBUqLl1K+lCaP6X/7QAkcaxI36QDONG+tOhGHJOXTsS114u8lrUTaEiGXbg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/openai-compatible@0.2.14': + resolution: {integrity: sha512-icjObfMCHKSIbywijaoLdZ1nSnuRnWgMEMLgwoxPJgxsUHMx0aVORnsLUid4SPtdhHI3X2masrt6iaEQLvOSFw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/openai@1.3.22': + resolution: {integrity: sha512-QwA+2EkG0QyjVR+7h6FE7iOu2ivNqAVMm9UJZkVxxTk5OIq5fFJDTEI/zICEMuHImTTXR2JjsL6EirJ28Jc4cw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/perplexity@1.1.9': + resolution: {integrity: sha512-Ytolh/v2XupXbTvjE18EFBrHLoNMH0Ueji3lfSPhCoRUfkwrgZ2D9jlNxvCNCCRiGJG5kfinSHvzrH5vGDklYA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/provider-utils@2.2.8': + resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.23.8 + + '@ai-sdk/provider@1.1.3': + resolution: {integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==} + engines: {node: '>=18'} + + '@ai-sdk/react@1.2.12': + resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + zod: + optional: true + + '@ai-sdk/togetherai@0.2.14': + resolution: {integrity: sha512-tdCe5kawsQrnMZB8aF1iL1P3+NfrPwILH9UudAZ5cNVY5dTFl+mXk3R37YJS+mkWeAoedr8Cb7mAuGxR47bsFw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/ui-utils@1.2.11': + resolution: {integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.23.8 + + '@ai-sdk/xai@1.2.16': + resolution: {integrity: sha512-UOZT8td9PWwMi2dF9a0U44t/Oltmf6QmIJdSvrOcLG4mvpRc1UJn6YJaR0HtXs3YnW6SvY1zRdIDrW4GFpv4NA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@anthropic-ai/sdk@0.39.0': + resolution: {integrity: sha512-eMyDIPRZbt1CCLErRCi3exlAvNkBtRe+kW5vvJyef93PmNr/clstYgHhtvmkxN82nlKgzyGPCyGxrm0JQ1ZIdg==} + + '@browserbasehq/sdk@2.6.0': + resolution: {integrity: sha512-83iXP5D7xMm8Wyn66TUaUrgoByCmAJuoMoZQI3sGg3JAiMlTfnCIMqyVBoNSaItaPIkaCnrsj6LiusmXV2X9YA==} + + '@browserbasehq/stagehand@2.4.0': + resolution: {integrity: sha512-Jna8wG55D6FtI3KpsabrNvGKAS7FuM2cIloHJuGDjRvZ5vis7loRhPBQCI+rrhkBcT4b68Ic8MI9WTvuXi/N8g==} + peerDependencies: + deepmerge: ^4.3.1 + dotenv: ^16.4.5 + zod: ^3.23.8 + + '@esbuild/aix-ppc64@0.25.5': + resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.5': + resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.5': + resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.5': + resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.5': + resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.5': + resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.5': + resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.5': + resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.5': + resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.5': + resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.5': + resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.5': + resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.5': + resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.5': + resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.5': + resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.5': + resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.5': + resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.5': + resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.5': + resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.5': + resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.5': + resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.5': + resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.5': + resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.5': + resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.5': + resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.20.1': + resolution: {integrity: sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.2.3': + resolution: {integrity: sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.14.0': + resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.15.1': + resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.29.0': + resolution: {integrity: sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.3.3': + resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@google/genai@0.8.0': + resolution: {integrity: sha512-Zs+OGyZKyMbFofGJTR9/jTQSv8kITh735N3tEuIZj4VlMQXTC0soCFahysJ9NaeenRlD7xGb6fyqmX+FwrpU6Q==} + engines: {node: '>=18.0.0'} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@modelcontextprotocol/sdk@1.13.1': + resolution: {integrity: sha512-8q6+9aF0yA39/qWT/uaIj6zTpC+Qu07DnN/lb9mjoquCJsAh6l3HyYqc9O3t2j7GilseOQOQimLg7W3By6jqvg==} + engines: {node: '>=18'} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + + '@playwright/test@1.53.1': + resolution: {integrity: sha512-Z4c23LHV0muZ8hfv4jw6HngPJkbbtZxTkxPNIg7cJcTc9C28N/p2q7g3JZS2SiKBBHJ3uM1dgDye66bB7LEk5w==} + engines: {node: '>=18'} + hasBin: true + + '@types/diff-match-patch@1.0.36': + resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node-fetch@2.6.12': + resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} + + '@types/node@18.19.112': + resolution: {integrity: sha512-i+Vukt9POdS/MBI7YrrkkI5fMfwFtOjphSmt4WXYLfwqsfr6z/HdCx7LqT9M7JktGob8WNgj8nFB4TbGNE4Cog==} + + '@typescript-eslint/eslint-plugin@8.35.0': + resolution: {integrity: sha512-ijItUYaiWuce0N1SoSMrEd0b6b6lYkYt99pqCPfybd+HKVXtEvYhICfLdwp42MhiI5mp0oq7PKEL+g1cNiz/Eg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.35.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/parser@8.35.0': + resolution: {integrity: sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/project-service@8.35.0': + resolution: {integrity: sha512-41xatqRwWZuhUMF/aZm2fcUsOFKNcG28xqRSS6ZVr9BVJtGExosLAm5A1OxTjRMagx8nJqva+P5zNIGt8RIgbQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/scope-manager@8.35.0': + resolution: {integrity: sha512-+AgL5+mcoLxl1vGjwNfiWq5fLDZM1TmTPYs2UkyHfFhgERxBbqHlNjRzhThJqz+ktBqTChRYY6zwbMwy0591AA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.35.0': + resolution: {integrity: sha512-04k/7247kZzFraweuEirmvUj+W3bJLI9fX6fbo1Qm2YykuBvEhRTPl8tcxlYO8kZZW+HIXfkZNoasVb8EV4jpA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/type-utils@8.35.0': + resolution: {integrity: sha512-ceNNttjfmSEoM9PW87bWLDEIaLAyR+E6BoYJQ5PfaDau37UGca9Nyq3lBk8Bw2ad0AKvYabz6wxc7DMTO2jnNA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/types@8.35.0': + resolution: {integrity: sha512-0mYH3emanku0vHw2aRLNGqe7EXh9WHEhi7kZzscrMDf6IIRUQ5Jk4wp1QrledE/36KtdZrVfKnE32eZCf/vaVQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.35.0': + resolution: {integrity: sha512-F+BhnaBemgu1Qf8oHrxyw14wq6vbL8xwWKKMwTMwYIRmFFY/1n/9T/jpbobZL8vp7QyEUcC6xGrnAO4ua8Kp7w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/utils@8.35.0': + resolution: {integrity: sha512-nqoMu7WWM7ki5tPgLVsmPM8CkqtoPUG6xXGeefM5t4x3XumOEKMoUZPdi+7F+/EotukN4R9OWdmDxN80fqoZeg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/visitor-keys@8.35.0': + resolution: {integrity: sha512-zTh2+1Y8ZpmeQaQVIc/ZZxsx8UzgKJyNg1PTvjzC7WMhPSVS8bfDX34k1SrwOf016qd5RU3az2UxUNue3IfQ5g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + + agentkeepalive@4.6.0: + resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} + engines: {node: '>= 8.0.0'} + + ai@4.3.16: + resolution: {integrity: sha512-KUDwlThJ5tr2Vw0A1ZkbDKNME3wzWhuVfAOwIvFUzl1TPVDFAXDFTXio3p+jaKneB+dKNCvFFlolYmmgHttG1g==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + react: + optional: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-escapes@7.0.0: + resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} + engines: {node: '>=18'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.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'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + bignumber.js@9.3.0: + resolution: {integrity: sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==} + + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + + 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-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + 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'} + + commander@14.0.0: + resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} + engines: {node: '>=20'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + + 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'} + + 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-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + dateformat@4.6.3: + resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + 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.1464554: + resolution: {integrity: sha512-CAoP3lYfwAGQTaAXYvA6JZR0fjGUb7qec1qf4mToyoH2TZgUFeIqYcjh6f9jNuhHfuZiEdH+PONHYrLhRQX6aw==} + + diff-match-patch@1.0.5: + resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + 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'} + + 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@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + 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==} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + es-abstract@1.24.0: + resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + engines: {node: '>= 0.4'} + + 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-iterator-helpers@1.2.1: + resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} + 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'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + esbuild@0.25.5: + resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} + engines: {node: '>=18'} + hasBin: true + + 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'} + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.29.0: + resolution: {integrity: sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + 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@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + eventsource-parser@3.0.3: + resolution: {integrity: sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==} + engines: {node: '>=20.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.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fast-copy@3.0.2: + resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-redact@3.5.0: + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} + engines: {node: '>=6'} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fetch-cookie@3.1.0: + resolution: {integrity: sha512-s/XhhreJpqH0ftkGVcQt8JE9bqk+zRn4jF5mPJXWZeQMCI5odV9K+wEWYbnzFPHgQZlvPSMjS4n4yawWE8RINw==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + form-data-encoder@1.7.2: + resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} + + form-data@4.0.3: + resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} + engines: {node: '>= 6'} + + formdata-node@4.4.1: + resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} + engines: {node: '>= 12.20'} + + 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'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + 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==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + gaxios@6.7.1: + resolution: {integrity: sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==} + engines: {node: '>=14'} + + gcp-metadata@6.1.1: + resolution: {integrity: sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==} + engines: {node: '>=14'} + + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + + 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-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.10.1: + resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.2.0: + resolution: {integrity: sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + google-auth-library@9.15.1: + resolution: {integrity: sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==} + engines: {node: '>=14'} + + google-logging-utils@0.0.2: + resolution: {integrity: sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==} + engines: {node: '>=14'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + gtoken@7.1.0: + resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==} + engines: {node: '>=14.0.0'} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + 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.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + 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==} + + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + interpret@1.4.0: + resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} + engines: {node: '>= 0.10'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + engines: {node: '>=18'} + + is-generator-function@1.1.0: + resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-bigint@1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + jsondiffpatch@0.6.0: + resolution: {integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} + + jws@4.0.0: + resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lint-staged@16.1.2: + resolution: {integrity: sha512-sQKw2Si2g9KUZNY3XNvRuDq4UJqpHwF0/FQzZR2M7I5MvtpWvibikCjUVJzZdGE0ByurEl3KQNvsGetd1ty1/Q==} + engines: {node: '>=20.17'} + hasBin: true + + listr2@8.3.3: + resolution: {integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==} + engines: {node: '>=18.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + 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'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + 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.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + 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==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nano-spawn@1.0.2: + resolution: {integrity: sha512-21t+ozMQDAL/UGgQVBbZ/xXvNO10++ZPuTmKRO8k9V3AClVRht49ahtDjfY8l1q6nSHOrE5ASfthzH3ol6R/hg==} + engines: {node: '>=20.17'} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + + 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 + + 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'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + ollama-ai-provider@1.2.0: + resolution: {integrity: sha512-jTNFruwe3O/ruJeppI/quoOUxG7NA6blG3ZyQj3lei4+NnJo7bi3eIRWqlVpRlu/mbzbFXeJSBuYQWF6pzGKww==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + peerDependenciesMeta: + zod: + optional: true + + 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==} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + 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 + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + partial-json@0.1.7: + resolution: {integrity: sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-to-regexp@8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + + pino-pretty@13.0.0: + resolution: {integrity: sha512-cQBBIVG3YajgoUjo1FdKVRX6t9XPxwB9lcNJVD5GCnNM4Y6T12YYx8c6zEejxQsU0wrg9TwmDulcE9LR7qcJqA==} + hasBin: true + + pino-std-serializers@7.0.0: + resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} + + pino@9.7.0: + resolution: {integrity: sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg==} + hasBin: true + + pkce-challenge@5.0.0: + resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} + engines: {node: '>=16.20.0'} + + playwright-core@1.53.1: + resolution: {integrity: sha512-Z46Oq7tLAyT0lGoFx4DOuB1IA9D1TPj0QkYxpPVUnGDqHHvDpCftu1J2hM2PiWsNMoZh8+LQaarAWcDfPBc6zg==} + engines: {node: '>=18'} + hasBin: true + + playwright-core@1.53.2: + resolution: {integrity: sha512-ox/OytMy+2w1jcYEYlOo1Hhp8hZkLCximMTUTMBXjGUA1KoFfiSZ+DU+3a739jsPY0yoKH2TFy9S2fsJas8yAw==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.53.1: + resolution: {integrity: sha512-LJ13YLr/ocweuwxyGf1XNFWIU4M2zUSo149Qbp+A4cpwDjsxRPj7k6H25LBrEHiEwxvRbD8HdwvQmRMSvquhYw==} + engines: {node: '>=18'} + hasBin: true + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@3.6.1: + resolution: {integrity: sha512-5xGWRa90Sp2+x1dQtNpIpeOQpTDBs9cZDmA/qs2vDNN2i18PdapqY7CmBeyLlMuGqXJRIOPaCaVZTLNQRWUH/A==} + engines: {node: '>=14'} + hasBin: true + + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + 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.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react@19.1.0: + resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} + engines: {node: '>=0.10.0'} + + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + + rechoir@0.6.2: + resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} + engines: {node: '>= 0.10'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + 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==} + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + 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@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + 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.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + 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'} + + shelljs@0.8.5: + resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} + engines: {node: '>=4'} + hasBin: true + + shx@0.3.4: + resolution: {integrity: sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==} + engines: {node: '>=6'} + hasBin: true + + 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'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.0: + resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + engines: {node: '>=18'} + + sonic-boom@4.2.0: + resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + swr@2.3.3: + resolution: {integrity: sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + + throttleit@2.1.0: + resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} + engines: {node: '>=18'} + + 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 + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + 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==} + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsx@4.20.3: + resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} + engines: {node: '>=18.0.0'} + hasBin: true + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript-eslint@8.35.0: + resolution: {integrity: sha512-uEnz70b7kBz6eg/j0Czy6K5NivaYopgxRjsnAJ2Fx5oTLo3wefTHIbL7AkQr1+7tJCRVpTs/wiM8JR/11Loq9A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-sync-external-store@1.5.0: + resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.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-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.18.2: + resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} + 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 + + yaml@2.8.0: + resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + engines: {node: '>= 14.6'} + hasBin: true + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod-to-json-schema@3.24.6: + resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} + peerDependencies: + zod: ^3.24.1 + + zod@3.25.67: + resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} + +snapshots: + + '@ai-sdk/anthropic@1.2.12(zod@3.25.67)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + zod: 3.25.67 + optional: true + + '@ai-sdk/azure@1.3.23(zod@3.25.67)': + dependencies: + '@ai-sdk/openai': 1.3.22(zod@3.25.67) + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + zod: 3.25.67 + optional: true + + '@ai-sdk/cerebras@0.2.14(zod@3.25.67)': + dependencies: + '@ai-sdk/openai-compatible': 0.2.14(zod@3.25.67) + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + zod: 3.25.67 + optional: true + + '@ai-sdk/deepseek@0.2.14(zod@3.25.67)': + dependencies: + '@ai-sdk/openai-compatible': 0.2.14(zod@3.25.67) + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + zod: 3.25.67 + optional: true + + '@ai-sdk/google@1.2.19(zod@3.25.67)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + zod: 3.25.67 + optional: true + + '@ai-sdk/groq@1.2.9(zod@3.25.67)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + zod: 3.25.67 + optional: true + + '@ai-sdk/mistral@1.2.8(zod@3.25.67)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + zod: 3.25.67 + optional: true + + '@ai-sdk/openai-compatible@0.2.14(zod@3.25.67)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + zod: 3.25.67 + optional: true + + '@ai-sdk/openai@1.3.22(zod@3.25.67)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + zod: 3.25.67 + optional: true + + '@ai-sdk/perplexity@1.1.9(zod@3.25.67)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + zod: 3.25.67 + optional: true + + '@ai-sdk/provider-utils@2.2.8(zod@3.25.67)': + dependencies: + '@ai-sdk/provider': 1.1.3 + nanoid: 3.3.11 + secure-json-parse: 2.7.0 + zod: 3.25.67 + + '@ai-sdk/provider@1.1.3': + dependencies: + json-schema: 0.4.0 + + '@ai-sdk/react@1.2.12(react@19.1.0)(zod@3.25.67)': + dependencies: + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + '@ai-sdk/ui-utils': 1.2.11(zod@3.25.67) + react: 19.1.0 + swr: 2.3.3(react@19.1.0) + throttleit: 2.1.0 + optionalDependencies: + zod: 3.25.67 + + '@ai-sdk/togetherai@0.2.14(zod@3.25.67)': + dependencies: + '@ai-sdk/openai-compatible': 0.2.14(zod@3.25.67) + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + zod: 3.25.67 + optional: true + + '@ai-sdk/ui-utils@1.2.11(zod@3.25.67)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + zod: 3.25.67 + zod-to-json-schema: 3.24.6(zod@3.25.67) + + '@ai-sdk/xai@1.2.16(zod@3.25.67)': + dependencies: + '@ai-sdk/openai-compatible': 0.2.14(zod@3.25.67) + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + zod: 3.25.67 + optional: true + + '@anthropic-ai/sdk@0.39.0': + dependencies: + '@types/node': 18.19.112 + '@types/node-fetch': 2.6.12 + 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.112 + '@types/node-fetch': 2.6.12 + 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@2.4.0(deepmerge@4.3.1)(dotenv@16.6.1)(react@19.1.0)(zod@3.25.67)': + dependencies: + '@anthropic-ai/sdk': 0.39.0 + '@browserbasehq/sdk': 2.6.0 + '@google/genai': 0.8.0 + ai: 4.3.16(react@19.1.0)(zod@3.25.67) + 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.2)(zod@3.25.67) + pino: 9.7.0 + pino-pretty: 13.0.0 + playwright: 1.53.1 + ws: 8.18.2 + zod: 3.25.67 + zod-to-json-schema: 3.24.6(zod@3.25.67) + optionalDependencies: + '@ai-sdk/anthropic': 1.2.12(zod@3.25.67) + '@ai-sdk/azure': 1.3.23(zod@3.25.67) + '@ai-sdk/cerebras': 0.2.14(zod@3.25.67) + '@ai-sdk/deepseek': 0.2.14(zod@3.25.67) + '@ai-sdk/google': 1.2.19(zod@3.25.67) + '@ai-sdk/groq': 1.2.9(zod@3.25.67) + '@ai-sdk/mistral': 1.2.8(zod@3.25.67) + '@ai-sdk/openai': 1.3.22(zod@3.25.67) + '@ai-sdk/perplexity': 1.1.9(zod@3.25.67) + '@ai-sdk/togetherai': 0.2.14(zod@3.25.67) + '@ai-sdk/xai': 1.2.16(zod@3.25.67) + ollama-ai-provider: 1.2.0(zod@3.25.67) + transitivePeerDependencies: + - bufferutil + - encoding + - react + - supports-color + - utf-8-validate + + '@esbuild/aix-ppc64@0.25.5': + optional: true + + '@esbuild/android-arm64@0.25.5': + optional: true + + '@esbuild/android-arm@0.25.5': + optional: true + + '@esbuild/android-x64@0.25.5': + optional: true + + '@esbuild/darwin-arm64@0.25.5': + optional: true + + '@esbuild/darwin-x64@0.25.5': + optional: true + + '@esbuild/freebsd-arm64@0.25.5': + optional: true + + '@esbuild/freebsd-x64@0.25.5': + optional: true + + '@esbuild/linux-arm64@0.25.5': + optional: true + + '@esbuild/linux-arm@0.25.5': + optional: true + + '@esbuild/linux-ia32@0.25.5': + optional: true + + '@esbuild/linux-loong64@0.25.5': + optional: true + + '@esbuild/linux-mips64el@0.25.5': + optional: true + + '@esbuild/linux-ppc64@0.25.5': + optional: true + + '@esbuild/linux-riscv64@0.25.5': + optional: true + + '@esbuild/linux-s390x@0.25.5': + optional: true + + '@esbuild/linux-x64@0.25.5': + optional: true + + '@esbuild/netbsd-arm64@0.25.5': + optional: true + + '@esbuild/netbsd-x64@0.25.5': + optional: true + + '@esbuild/openbsd-arm64@0.25.5': + optional: true + + '@esbuild/openbsd-x64@0.25.5': + optional: true + + '@esbuild/sunos-x64@0.25.5': + optional: true + + '@esbuild/win32-arm64@0.25.5': + optional: true + + '@esbuild/win32-ia32@0.25.5': + optional: true + + '@esbuild/win32-x64@0.25.5': + optional: true + + '@eslint-community/eslint-utils@4.7.0(eslint@9.29.0)': + dependencies: + eslint: 9.29.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/config-array@0.20.1': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.1 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.2.3': {} + + '@eslint/core@0.14.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.15.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.1 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.29.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.3.3': + dependencies: + '@eslint/core': 0.15.1 + levn: 0.4.1 + + '@google/genai@0.8.0': + dependencies: + google-auth-library: 9.15.1 + ws: 8.18.2 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@modelcontextprotocol/sdk@1.13.1': + dependencies: + ajv: 6.12.6 + content-type: 1.0.5 + cors: 2.8.5 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + express: 5.1.0 + express-rate-limit: 7.5.1(express@5.1.0) + pkce-challenge: 5.0.0 + raw-body: 3.0.0 + zod: 3.25.67 + zod-to-json-schema: 3.24.6(zod@3.25.67) + transitivePeerDependencies: + - supports-color + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@opentelemetry/api@1.9.0': {} + + '@playwright/test@1.53.1': + dependencies: + playwright: 1.53.1 + + '@types/diff-match-patch@1.0.36': {} + + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + '@types/node-fetch@2.6.12': + dependencies: + '@types/node': 18.19.112 + form-data: 4.0.3 + + '@types/node@18.19.112': + dependencies: + undici-types: 5.26.5 + + '@typescript-eslint/eslint-plugin@8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0)(typescript@5.8.3))(eslint@9.29.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.35.0(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.35.0 + '@typescript-eslint/type-utils': 8.35.0(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.0(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.35.0 + eslint: 9.29.0 + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.35.0(eslint@9.29.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.35.0 + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.35.0 + debug: 4.4.1 + eslint: 9.29.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.35.0(typescript@5.8.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) + '@typescript-eslint/types': 8.35.0 + debug: 4.4.1 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.35.0': + dependencies: + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/visitor-keys': 8.35.0 + + '@typescript-eslint/tsconfig-utils@8.35.0(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@typescript-eslint/type-utils@8.35.0(eslint@9.29.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.0(eslint@9.29.0)(typescript@5.8.3) + debug: 4.4.1 + eslint: 9.29.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.35.0': {} + + '@typescript-eslint/typescript-estree@8.35.0(typescript@5.8.3)': + dependencies: + '@typescript-eslint/project-service': 8.35.0(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/visitor-keys': 8.35.0 + debug: 4.4.1 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.35.0(eslint@9.29.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0) + '@typescript-eslint/scope-manager': 8.35.0 + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) + eslint: 9.29.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.35.0': + dependencies: + '@typescript-eslint/types': 8.35.0 + eslint-visitor-keys: 4.2.1 + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + agent-base@7.1.3: {} + + agentkeepalive@4.6.0: + dependencies: + humanize-ms: 1.2.1 + + ai@4.3.16(react@19.1.0)(zod@3.25.67): + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + '@ai-sdk/react': 1.2.12(react@19.1.0)(zod@3.25.67) + '@ai-sdk/ui-utils': 1.2.11(zod@3.25.67) + '@opentelemetry/api': 1.9.0 + jsondiffpatch: 0.6.0 + zod: 3.25.67 + optionalDependencies: + react: 19.1.0 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-escapes@7.0.0: + dependencies: + environment: 1.1.0 + + ansi-regex@6.1.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + argparse@2.0.1: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + async-function@1.0.0: {} + + asynckit@0.4.0: {} + + atomic-sleep@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + balanced-match@1.0.2: {} + + base64-js@1.5.1: {} + + bignumber.js@9.3.0: {} + + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.1 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.0 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + buffer-equal-constant-time@1.0.1: {} + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.4.1: {} + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + + 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 + + commander@14.0.0: {} + + concat-map@0.0.1: {} + + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + 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-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + dateformat@4.6.3: {} + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + delayed-stream@1.0.0: {} + + depd@2.0.0: {} + + dequal@2.0.3: {} + + devtools-protocol@0.0.1464554: {} + + diff-match-patch@1.0.5: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + 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 + + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + + ee-first@1.1.1: {} + + emoji-regex@10.4.0: {} + + encodeurl@2.0.0: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + environment@1.1.0: {} + + es-abstract@1.24.0: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.19 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + safe-array-concat: 1.1.3 + + 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 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.25.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.5 + '@esbuild/android-arm': 0.25.5 + '@esbuild/android-arm64': 0.25.5 + '@esbuild/android-x64': 0.25.5 + '@esbuild/darwin-arm64': 0.25.5 + '@esbuild/darwin-x64': 0.25.5 + '@esbuild/freebsd-arm64': 0.25.5 + '@esbuild/freebsd-x64': 0.25.5 + '@esbuild/linux-arm': 0.25.5 + '@esbuild/linux-arm64': 0.25.5 + '@esbuild/linux-ia32': 0.25.5 + '@esbuild/linux-loong64': 0.25.5 + '@esbuild/linux-mips64el': 0.25.5 + '@esbuild/linux-ppc64': 0.25.5 + '@esbuild/linux-riscv64': 0.25.5 + '@esbuild/linux-s390x': 0.25.5 + '@esbuild/linux-x64': 0.25.5 + '@esbuild/netbsd-arm64': 0.25.5 + '@esbuild/netbsd-x64': 0.25.5 + '@esbuild/openbsd-arm64': 0.25.5 + '@esbuild/openbsd-x64': 0.25.5 + '@esbuild/sunos-x64': 0.25.5 + '@esbuild/win32-arm64': 0.25.5 + '@esbuild/win32-ia32': 0.25.5 + '@esbuild/win32-x64': 0.25.5 + + escape-html@1.0.3: {} + + escape-string-regexp@4.0.0: {} + + eslint-plugin-react@7.37.5(eslint@9.29.0): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.2.1 + eslint: 9.29.0 + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.29.0: + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.20.1 + '@eslint/config-helpers': 0.2.3 + '@eslint/core': 0.14.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.29.0 + '@eslint/plugin-kit': 0.3.3 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.1 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + event-target-shim@5.0.1: {} + + eventemitter3@5.0.1: {} + + eventsource-parser@3.0.3: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.3 + + express-rate-limit@7.5.1(express@5.1.0): + dependencies: + express: 5.1.0 + + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + mime-types: 3.0.1 + 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: {} + + fast-copy@3.0.2: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-redact@3.5.0: {} + + fast-safe-stringify@2.1.1: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fetch-cookie@3.1.0: + dependencies: + set-cookie-parser: 2.7.1 + tough-cookie: 5.1.2 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@2.1.0: + dependencies: + debug: 4.4.1 + 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-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + form-data-encoder@1.7.2: {} + + form-data@4.0.3: + 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 + + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + fs.realpath@1.0.0: {} + + fsevents@2.3.2: + optional: true + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + gaxios@6.7.1: + dependencies: + extend: 3.0.2 + https-proxy-agent: 7.0.6 + is-stream: 2.0.1 + node-fetch: 2.7.0 + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + - supports-color + + gcp-metadata@6.1.1: + dependencies: + gaxios: 6.7.1 + google-logging-utils: 0.0.2 + json-bigint: 1.0.0 + transitivePeerDependencies: + - encoding + - supports-color + + get-east-asian-width@1.3.0: {} + + 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-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.10.1: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@14.0.0: {} + + globals@16.2.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + google-auth-library@9.15.1: + dependencies: + base64-js: 1.5.1 + ecdsa-sig-formatter: 1.0.11 + gaxios: 6.7.1 + gcp-metadata: 6.1.1 + gtoken: 7.1.0 + jws: 4.0.0 + transitivePeerDependencies: + - encoding + - supports-color + + google-logging-utils@0.0.2: {} + + gopd@1.2.0: {} + + graphemer@1.4.0: {} + + gtoken@7.1.0: + dependencies: + gaxios: 6.7.1 + jws: 4.0.0 + transitivePeerDependencies: + - encoding + - supports-color + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + 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.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + + husky@9.1.7: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + interpret@1.4.0: {} + + ipaddr.js@1.9.1: {} + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.0.0: + dependencies: + get-east-asian-width: 1.3.0 + + is-generator-function@1.1.0: + dependencies: + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-promise@4.0.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-stream@2.0.1: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.19 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + joycon@3.1.1: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-bigint@1.0.0: + dependencies: + bignumber.js: 9.3.0 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema@0.4.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + jsondiffpatch@0.6.0: + dependencies: + '@types/diff-match-patch': 1.0.36 + chalk: 5.4.1 + diff-match-patch: 1.0.5 + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + 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 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lilconfig@3.1.3: {} + + lint-staged@16.1.2: + dependencies: + chalk: 5.4.1 + commander: 14.0.0 + debug: 4.4.1 + lilconfig: 3.1.3 + listr2: 8.3.3 + micromatch: 4.0.8 + nano-spawn: 1.0.2 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.8.0 + transitivePeerDependencies: + - supports-color + + listr2@8.3.3: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.0.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.0 + strip-ansi: 7.1.0 + wrap-ansi: 9.0.0 + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + math-intrinsics@1.1.0: {} + + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + 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.1: + dependencies: + mime-db: 1.54.0 + + mimic-function@5.0.1: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + ms@2.1.3: {} + + nano-spawn@1.0.2: {} + + nanoid@3.3.11: {} + + natural-compare@1.4.0: {} + + negotiator@1.0.0: {} + + node-domexception@1.0.0: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + ollama-ai-provider@1.2.0(zod@3.25.67): + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + partial-json: 0.1.7 + optionalDependencies: + zod: 3.25.67 + 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 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + openai@4.104.0(ws@8.18.2)(zod@3.25.67): + dependencies: + '@types/node': 18.19.112 + '@types/node-fetch': 2.6.12 + 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.2 + zod: 3.25.67 + transitivePeerDependencies: + - encoding + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parseurl@1.3.3: {} + + partial-json@0.1.7: + optional: true + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-to-regexp@8.2.0: {} + + picomatch@2.3.1: {} + + pidtree@0.6.0: {} + + pino-abstract-transport@2.0.0: + dependencies: + split2: 4.2.0 + + pino-pretty@13.0.0: + dependencies: + colorette: 2.0.20 + dateformat: 4.6.3 + fast-copy: 3.0.2 + 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: 2.0.0 + pump: 3.0.3 + secure-json-parse: 2.7.0 + sonic-boom: 4.2.0 + strip-json-comments: 3.1.1 + + pino-std-serializers@7.0.0: {} + + pino@9.7.0: + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.5.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.0: {} + + playwright-core@1.53.1: {} + + playwright-core@1.53.2: {} + + playwright@1.53.1: + dependencies: + playwright-core: 1.53.1 + optionalDependencies: + fsevents: 2.3.2 + + possible-typed-array-names@1.1.0: {} + + prelude-ls@1.2.1: {} + + prettier@3.6.1: {} + + process-warning@5.0.0: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + pump@3.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + punycode@2.3.1: {} + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + queue-microtask@1.2.3: {} + + quick-format-unescaped@4.0.4: {} + + range-parser@1.2.1: {} + + raw-body@3.0.0: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + unpipe: 1.0.0 + + react-is@16.13.1: {} + + react@19.1.0: {} + + real-require@0.2.0: {} + + rechoir@0.6.2: + dependencies: + resolve: 1.22.10 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@2.0.0-next.5: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + router@2.2.0: + dependencies: + debug: 4.4.1 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.2.0 + transitivePeerDependencies: + - supports-color + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.2.1: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safe-stable-stringify@2.5.0: {} + + safer-buffer@2.1.2: {} + + secure-json-parse@2.7.0: {} + + semver@6.3.1: {} + + semver@7.7.2: {} + + send@1.2.0: + dependencies: + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + 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.1: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + setprototypeof@1.2.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shelljs@0.8.5: + dependencies: + glob: 7.2.3 + interpret: 1.4.0 + rechoir: 0.6.2 + + shx@0.3.4: + dependencies: + minimist: 1.2.8 + shelljs: 0.8.5 + + 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: {} + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 5.0.0 + + sonic-boom@4.2.0: + dependencies: + atomic-sleep: 1.0.0 + + split2@4.2.0: {} + + statuses@2.0.1: {} + + statuses@2.0.2: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string-argv@0.3.2: {} + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.0 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + swr@2.3.3(react@19.1.0): + dependencies: + dequal: 2.0.3 + react: 19.1.0 + use-sync-external-store: 1.5.0(react@19.1.0) + + thread-stream@3.1.0: + dependencies: + real-require: 0.2.0 + + throttleit@2.1.0: {} + + tldts-core@6.1.86: {} + + tldts@6.1.86: + dependencies: + tldts-core: 6.1.86 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + tough-cookie@5.1.2: + dependencies: + tldts: 6.1.86 + + tr46@0.0.3: {} + + ts-api-utils@2.1.0(typescript@5.8.3): + dependencies: + typescript: 5.8.3 + + tsx@4.20.3: + dependencies: + esbuild: 0.25.5 + get-tsconfig: 4.10.1 + optionalDependencies: + fsevents: 2.3.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript-eslint@8.35.0(eslint@9.29.0)(typescript@5.8.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0)(typescript@5.8.3))(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/parser': 8.35.0(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.0(eslint@9.29.0)(typescript@5.8.3) + eslint: 9.29.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + typescript@5.8.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@5.26.5: {} + + unpipe@1.0.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-sync-external-store@1.5.0(react@19.1.0): + dependencies: + react: 19.1.0 + + uuid@9.0.1: {} + + vary@1.1.2: {} + + 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-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.0 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + wrappy@1.0.2: {} + + ws@8.18.2: {} + + yaml@2.8.0: {} + + yocto-queue@0.1.0: {} + + zod-to-json-schema@3.24.6(zod@3.25.67): + dependencies: + zod: 3.25.67 + + zod@3.25.67: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..6bdb532 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - '.' \ No newline at end of file diff --git a/browserbase/smithery.config.js b/smithery.config.js similarity index 96% rename from browserbase/smithery.config.js rename to smithery.config.js index e2dfe2e..c082c3c 100644 --- a/browserbase/smithery.config.js +++ b/smithery.config.js @@ -6,5 +6,5 @@ export default { // Mark playwright-core as external to prevent bundling // This avoids the relative path resolution issue in Docker external: ["playwright-core"], - } -} + }, +}; diff --git a/smithery.yaml b/smithery.yaml new file mode 100644 index 0000000..a4d9ee1 --- /dev/null +++ b/smithery.yaml @@ -0,0 +1,40 @@ +runtime: "container" +build: + dockerfile: "Dockerfile" + dockerBuildPath: "." +startCommand: + type: "http" + configSchema: + type: "object" + required: ["browserbaseApiKey", "browserbaseProjectId"] + properties: + browserbaseApiKey: + type: "string" + title: "Browserbase API Key" + description: "The Browserbase API Key to use" + browserbaseProjectId: + type: "string" + title: "Browserbase Project ID" + description: "The Browserbase Project ID to use" + proxies: + type: "boolean" + default: false + description: "Whether or not to use Browserbase proxies" + advancedStealth: + type: "boolean" + default: false + description: "Use advanced stealth mode. Only available to Browserbase Scale Plan users" + modelApiKey: + type: "string" + description: "API key for the custom model provider (e.g., GEMINI_API_KEY)" + modelName: + type: "string" + default: "google/gemini-2.0-flash" + description: "The model to use for Stagehand" + exampleConfig: + browserbaseApiKey: "bb_api_key_example" + browserbaseProjectId: "bb_project_id_example" + proxies: false + advancedStealth: false + modelApiKey: "your_gemini_api_key" + modelName: "google/gemini-2.0-flash" \ No newline at end of file diff --git a/browserbase/src/config.ts b/src/config.ts similarity index 64% rename from browserbase/src/config.ts rename to src/config.ts index be94737..2035eb1 100644 --- a/browserbase/src/config.ts +++ b/src/config.ts @@ -1,30 +1,8 @@ -import os from 'os'; -import fs from 'fs'; -import path from 'path'; -import { sanitizeForFilePath } from './tools/utils.js'; import type { Cookie } from "playwright-core"; +import type { Config } from "../config.d.ts"; +import { AvailableModelSchema } from "@browserbasehq/stagehand"; -export type ToolCapability = 'core' | string; - -export interface Config { - browserbaseApiKey?: string; - browserbaseProjectId?: string; - server?: { - port?: number; - host?: string; - }; - proxies?: boolean; - advancedStealth?: boolean; - context?: { - contextId?: string; - persist?: boolean; - }; - viewPort?: { - browserWidth?: number; - browserHeight?: number; - }; - cookies?: Cookie[]; -} +export type ToolCapability = "core" | string; // Define Command Line Options Structure export type CLIOptions = { @@ -39,6 +17,8 @@ export type CLIOptions = { cookies?: Cookie[]; browserWidth?: number; browserHeight?: number; + modelName?: typeof AvailableModelSchema; + modelApiKey?: string; }; // Default Configuration Values @@ -55,6 +35,7 @@ const defaultConfig: Config = { browserHeight: 768, }, cookies: undefined, + modelName: "google/gemini-2.0-flash", // Default Model }; // Resolve final configuration by merging defaults, file config, and CLI options @@ -64,29 +45,47 @@ export async function resolveConfig(cliOptions: CLIOptions): Promise { const mergedConfig = mergeConfig(defaultConfig, cliConfig); // --- Add Browserbase Env Vars --- - // Ensure env vars are read *after* dotenv potentially runs (in index.ts) if (!mergedConfig.browserbaseApiKey) { mergedConfig.browserbaseApiKey = process.env.BROWSERBASE_API_KEY; } if (!mergedConfig.browserbaseProjectId) { mergedConfig.browserbaseProjectId = process.env.BROWSERBASE_PROJECT_ID; } + + if (!mergedConfig.modelApiKey) { + mergedConfig.modelApiKey = process.env.GEMINI_API_KEY; + } + // -------------------------------- - // Basic validation for Browserbase keys + // Basic validation for Browserbase keys - provide dummy values if not set if (!mergedConfig.browserbaseApiKey) { - console.warn("Warning: BROWSERBASE_API_KEY environment variable not set."); + console.warn( + "Warning: BROWSERBASE_API_KEY environment variable not set. Using dummy value.", + ); + mergedConfig.browserbaseApiKey = "dummy-browserbase-api-key"; } if (!mergedConfig.browserbaseProjectId) { - console.warn("Warning: BROWSERBASE_PROJECT_ID environment variable not set."); + console.warn( + "Warning: BROWSERBASE_PROJECT_ID environment variable not set. Using dummy value.", + ); + mergedConfig.browserbaseProjectId = "dummy-browserbase-project-id"; + } + if (!mergedConfig.modelApiKey) { + console.warn( + "Warning: MODEL_API_KEY environment variable not set. Using dummy value.", + ); + mergedConfig.modelApiKey = "dummy-api-key"; } return mergedConfig; } // Create Config structure based on CLI options -export async function configFromCLIOptions(cliOptions: CLIOptions): Promise { - return { +export async function configFromCLIOptions( + cliOptions: CLIOptions, +): Promise { + return { browserbaseApiKey: cliOptions.browserbaseApiKey, browserbaseProjectId: cliOptions.browserbaseProjectId, server: { @@ -104,22 +103,16 @@ export async function configFromCLIOptions(cliOptions: CLIOptions): Promise { - const outputDir = os.tmpdir(); - await fs.promises.mkdir(outputDir, { recursive: true }); - const sanitizedName = sanitizeForFilePath(name); - return path.join(outputDir, sanitizedName); -} - // Helper function to merge config objects, excluding undefined values function pickDefined(obj: T | undefined): Partial { if (!obj) return {}; return Object.fromEntries( - Object.entries(obj).filter(([_, v]) => v !== undefined) + Object.entries(obj).filter(([, v]) => v !== undefined), ) as Partial; } @@ -127,35 +120,35 @@ function pickDefined(obj: T | undefined): Partial { function mergeConfig(base: Config, overrides: Config): Config { const baseFiltered = pickDefined(base); const overridesFiltered = pickDefined(overrides); - + // Create the result object const result = { ...baseFiltered } as Config; - + // For each property in overrides for (const [key, value] of Object.entries(overridesFiltered)) { - if (key === 'context' && value && result.context) { + if (key === "context" && value && result.context) { // Special handling for context object to ensure deep merge result.context = { ...result.context, - ...(value as Config['context']) + ...(value as Config["context"]), }; } else if ( - value && - typeof value === 'object' && - !Array.isArray(value) && - result[key as keyof Config] && - typeof result[key as keyof Config] === 'object' + value && + typeof value === "object" && + !Array.isArray(value) && + result[key as keyof Config] && + typeof result[key as keyof Config] === "object" ) { // Deep merge for other nested objects result[key as keyof Config] = { ...(result[key as keyof Config] as object), - ...value - } as any; + ...value, + } as unknown; } else { // Simple override for primitives, arrays, etc. - result[key as keyof Config] = value as any; + result[key as keyof Config] = value as unknown; } } - + return result; -} \ No newline at end of file +} diff --git a/src/context.ts b/src/context.ts new file mode 100644 index 0000000..3b2abcc --- /dev/null +++ b/src/context.ts @@ -0,0 +1,125 @@ +import type { Stagehand } from "@browserbasehq/stagehand"; +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import type { Config } from "../config.d.ts"; +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { listResources, readResource } from "./mcp/resources.js"; +import { getSession, defaultSessionId } from "./sessionManager.js"; +import type { MCPTool, BrowserSession } from "./types/types.js"; + +export class Context { + public readonly config: Config; + private server: Server; + public currentSessionId: string = defaultSessionId; + + constructor(server: Server, config: Config) { + this.server = server; + this.config = config; + } + + public getServer(): Server { + return this.server; + } + + /** + * Gets the Stagehand instance for the current session from SessionManager + */ + public async getStagehand( + sessionId: string = this.currentSessionId, + ): Promise { + const session = await getSession(sessionId, this.config); + if (!session) { + throw new Error(`No session found for ID: ${sessionId}`); + } + return session.stagehand; + } + + public async getActivePage(): Promise { + // Get page from session manager + const session = await getSession(this.currentSessionId, this.config); + if (session && session.page && !session.page.isClosed()) { + return session.page; + } + + return null; + } + + public async getActiveBrowser( + createIfMissing: boolean = true, + ): Promise { + const session = await getSession( + this.currentSessionId, + this.config, + createIfMissing, + ); + if (!session || !session.browser || !session.browser.isConnected()) { + return null; + } + return session.browser; + } + + async run(tool: MCPTool, args: unknown): Promise { + try { + console.error( + `Executing tool: ${tool.schema.name} with args: ${JSON.stringify(args)}`, + ); + + // Check if this tool has a handle method (new tool system) + if ("handle" in tool && typeof tool.handle === "function") { + const toolResult = await tool.handle(this, args); + + if (toolResult?.action) { + const actionResult = await toolResult.action(); + const content = actionResult?.content || []; + + return { + content: Array.isArray(content) + ? content + : [{ type: "text", text: "Action completed successfully." }], + isError: false, + }; + } else { + return { + content: [ + { + type: "text", + text: `${tool.schema.name} completed successfully.`, + }, + ], + isError: false, + }; + } + } else { + // Fallback for any legacy tools without handle method + throw new Error( + `Tool ${tool.schema.name} does not have a handle method`, + ); + } + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + console.error( + `Tool ${tool.schema?.name || "unknown"} failed: ${errorMessage}`, + ); + return { + content: [{ type: "text", text: `Error: ${errorMessage}` }], + isError: true, + }; + } + } + + /** + * List resources + * Documentation: https://modelcontextprotocol.io/docs/concepts/resources + */ + listResources() { + return listResources(); + } + + /** + * Read a resource by URI + * Documentation: https://modelcontextprotocol.io/docs/concepts/resources + */ + readResource(uri: string) { + return readResource(uri); + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..c6f04df --- /dev/null +++ b/src/index.ts @@ -0,0 +1,221 @@ +import dotenv from "dotenv"; +dotenv.config(); + +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { z } from "zod"; +import type { MCPToolsArray } from "./types/types.js"; + +import { Context } from "./context.js"; +import type { Config } from "../config.d.ts"; +import { TOOLS } from "./tools/index.js"; +import { AvailableModelSchema } from "@browserbasehq/stagehand"; +import { PROMPTS, getPrompt } from "./mcp/prompts.js"; +import { RESOURCE_TEMPLATES } from "./mcp/resources.js"; + +import { + ListResourcesRequestSchema, + ReadResourceRequestSchema, + ListResourceTemplatesRequestSchema, + ListPromptsRequestSchema, + GetPromptRequestSchema, +} from "@modelcontextprotocol/sdk/types.js"; + +const cookieSchema = z.object({ + name: z.string(), + value: z.string(), + domain: z.string(), + path: z.string().optional(), + expires: z.number().optional(), + httpOnly: z.boolean().optional(), + secure: z.boolean().optional(), + sameSite: z.enum(["Strict", "Lax", "None"]).optional(), +}); + +// Configuration schema for Smithery - matches existing Config interface +export const configSchema = z + .object({ + browserbaseApiKey: z.string().describe("The Browserbase API Key to use"), + browserbaseProjectId: z + .string() + .describe("The Browserbase Project ID to use"), + proxies: z + .boolean() + .optional() + .describe("Whether or not to use Browserbase proxies"), + advancedStealth: z + .boolean() + .optional() + .describe( + "Use advanced stealth mode. Only available to Browserbase Scale Plan users", + ), + context: z + .object({ + contextId: z + .string() + .optional() + .describe("The ID of the context to use"), + persist: z + .boolean() + .optional() + .describe("Whether or not to persist the context"), + }) + .optional(), + viewPort: z + .object({ + browserWidth: z + .number() + .optional() + .describe("The width of the browser"), + browserHeight: z + .number() + .optional() + .describe("The height of the browser"), + }) + .optional(), + cookies: z + .array(cookieSchema) + .optional() + .describe("Cookies to inject into the Browserbase context"), + server: z + .object({ + port: z + .number() + .optional() + .describe("The port to listen on for SHTTP or MCP transport"), + host: z + .string() + .optional() + .describe( + "The host to bind the server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces", + ), + }) + .optional(), + modelName: AvailableModelSchema.optional().describe( + "The model to use for Stagehand (default: google/gemini-2.0-flash)", + ), // Already an existing Zod Enum + modelApiKey: z + .string() + .optional() + .describe( + "API key for the custom model provider. Required when using a model other than the default google/gemini-2.0-flash", + ), + }) + .refine( + (data) => { + // If any model is explicitly specified, API key is required + if (data.modelName) { + return data.modelApiKey !== undefined && data.modelApiKey.length > 0; + } + return true; + }, + { + message: "modelApiKey is required when specifying a custom model", + path: ["modelApiKey"], + }, + ); + +// Default function for Smithery +export default function ({ config }: { config: z.infer }) { + if (!config.browserbaseApiKey) { + throw new Error("browserbaseApiKey is required"); + } + if (!config.browserbaseProjectId) { + throw new Error("browserbaseProjectId is required"); + } + + const server = new McpServer({ + name: "Browserbase MCP Server", + version: "2.0.0", + description: + "Cloud browser automation server powered by Browserbase and Stagehand. Enables LLMs to navigate websites, interact with elements, extract data, and capture screenshots using natural language commands.", + capabilities: { + resources: { + subscribe: true, + listChanged: true, + }, + prompts: { + listChanged: true, + }, + sampling: {}, + }, + }); + + const internalConfig: Config = config as Config; + + // Create the context, passing server instance and config + const context = new Context(server.server, internalConfig); + + server.server.registerCapabilities({ + resources: { + subscribe: true, + listChanged: true, + }, + prompts: { + listChanged: true, + }, + sampling: {}, + }); + + // Add resource handlers + server.server.setRequestHandler(ListResourcesRequestSchema, async () => { + return context.listResources(); + }); + + server.server.setRequestHandler( + ReadResourceRequestSchema, + async (request) => { + return context.readResource(request.params.uri); + }, + ); + + server.server.setRequestHandler( + ListResourceTemplatesRequestSchema, + async () => { + return { resourceTemplates: RESOURCE_TEMPLATES }; + }, + ); + + // Add prompt handlers + server.server.setRequestHandler(ListPromptsRequestSchema, async () => { + return { prompts: PROMPTS }; + }); + + server.server.setRequestHandler(GetPromptRequestSchema, async (request) => { + const prompt = getPrompt(request.params.name); + return prompt; + }); + + const tools: MCPToolsArray = [...TOOLS]; + + // Register each tool with the Smithery server + tools.forEach((tool) => { + if (tool.schema.inputSchema instanceof z.ZodObject) { + server.tool( + tool.schema.name, + tool.schema.description, + tool.schema.inputSchema.shape, + async (params: z.infer) => { + try { + const result = await context.run(tool, params); + return result; + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + process.stderr.write( + `[Smithery Error] ${new Date().toISOString()} Error running tool ${tool.schema.name}: ${errorMessage}\n`, + ); + throw new Error( + `Failed to run tool '${tool.schema.name}': ${errorMessage}`, + ); + } + }, + ); + } else { + console.warn( + `Tool "${tool.schema.name}" has an input schema that is not a ZodObject. Schema type: ${tool.schema.inputSchema.constructor.name}`, + ); + } + }); + + return server.server; +} diff --git a/src/mcp/prompts.ts b/src/mcp/prompts.ts new file mode 100644 index 0000000..893c493 --- /dev/null +++ b/src/mcp/prompts.ts @@ -0,0 +1,124 @@ +/** + * Prompts module for the Browserbase MCP server + * Contains prompts definitions and handlers for prompt-related requests + * Docs: https://modelcontextprotocol.io/docs/concepts/prompts + */ + +// Define the prompts +export const PROMPTS = [ + { + name: "browserbase_system", + description: + "System prompt defining the scope and capabilities of Browserbase MCP server", + arguments: [], + }, + { + name: "multi_session_guidance", + description: + "Guidance on when and how to use multi-session browser automation", + arguments: [], + }, +]; + +/** + * Get a prompt by name + * @param name The name of the prompt to retrieve + * @returns The prompt definition or throws an error if not found + */ +export function getPrompt(name: string) { + if (name === "browserbase_system") { + return { + description: "System prompt for Browserbase MCP server capabilities", + messages: [ + { + role: "system", + content: { + type: "text", + text: `You have access to a powerful browser automation server via Browserbase MCP. This server provides: + +CAPABILITIES: +- Cloud browser automation using Browserbase infrastructure +- AI-powered web interactions via Stagehand +- Parallel browser sessions for concurrent tasks +- Advanced stealth mode for anti-detection +- Proxy support for geo-location and privacy +- Context persistence for maintaining authentication +- Screenshot capture and visual analysis +- Structured data extraction from any webpage + +TOOL SELECTION GUIDE: +For SINGLE browser tasks: Use "browserbase_session_create" then regular tools +For MULTIPLE browser tasks: Use "multi_browserbase_stagehand_session_create" then session-specific tools + +MULTI-SESSION INDICATORS - Use multi-session tools when you see: +- "parallel", "multiple", "simultaneously", "concurrent" +- "different accounts", "A/B test", "compare" +- "multiple sites", "batch processing" +- Any task requiring more than one browser instance + +MULTI-SESSION WORKFLOW: +1. Create sessions: "multi_browserbase_stagehand_session_create" (give descriptive names) +2. Track sessions: "multi_browserbase_stagehand_session_list" +3. Use session tools: "multi_browserbase_stagehand_navigate_session", etc. +4. Cleanup: "multi_browserbase_stagehand_session_close" + +BEST PRACTICES: +- Use descriptive session names for easier tracking +- Always close sessions when done to free resources +- Take screenshots for visual confirmation or debugging +- Each session maintains independent state and authentication +- No need to create backup sessions - sessions are reliable and persistent + +When using this server, think of it as controlling real browsers in the cloud. You can navigate, click, type, extract data, and capture screenshots just like a human would, but with the precision and scale of automation.`, + }, + }, + ], + }; + } + + if (name === "multi_session_guidance") { + return { + description: "Comprehensive guidance on multi-session browser automation", + messages: [ + { + role: "system", + content: { + type: "text", + text: `Multi-Session Browser Automation Guidance + +WHEN TO USE MULTI-SESSION TOOLS: +- Parallel data collection from multiple websites +- A/B testing with different user flows +- Authentication with multiple user accounts simultaneously +- Cross-site operations requiring coordination +- Load testing or performance simulation +- Any task requiring more than one browser instance + +TOOL NAMING PATTERNS: +- Session Management: "multi_browserbase_stagehand_session_*" +- Browser Actions: "multi_browserbase_stagehand_*_session" + +RECOMMENDED WORKFLOW: +1. Create sessions: "multi_browserbase_stagehand_session_create" (give each a descriptive name) +2. List sessions: "multi_browserbase_stagehand_session_list" (to track active sessions) +3. Use session-specific tools: "multi_browserbase_stagehand_navigate_session", "multi_browserbase_stagehand_act_session", etc. +4. Clean up: "multi_browserbase_stagehand_session_close" when done + +IMPORTANT RULES: +- Always use session-specific tools (with "_session" suffix) when working with multiple sessions +- Each session maintains independent cookies, authentication, and browser state +- Always close sessions when finished to free resources +- Use descriptive session names for easier tracking +- No need to create backup sessions - sessions are reliable and persistent + +SINGLE VS MULTI-SESSION: +- Single: "browserbase_session_create" β†’ "browserbase_stagehand_navigate" +- Multi: "multi_browserbase_stagehand_session_create" β†’ "multi_browserbase_stagehand_navigate_session"`, + }, + }, + ], + }; + } + + throw new Error(`Invalid prompt name: ${name}`); +} diff --git a/stagehand/src/resources.ts b/src/mcp/resources.ts similarity index 91% rename from stagehand/src/resources.ts rename to src/mcp/resources.ts index 7250966..0aaa1a3 100644 --- a/stagehand/src/resources.ts +++ b/src/mcp/resources.ts @@ -1,6 +1,7 @@ /** - * Resources module for the Stagehand MCP server + * Resources module for the Browserbase MCP server * Contains resources definitions and handlers for resource-related requests + * Docs: https://modelcontextprotocol.io/docs/concepts/resources */ // Define the resources @@ -17,14 +18,14 @@ export const screenshots = new Map(); * @returns A list of available resources including screenshots */ export function listResources() { - return { + return { resources: [ ...Array.from(screenshots.keys()).map((name) => ({ uri: `screenshot://${name}`, mimeType: "image/png", name: `Screenshot: ${name}`, })), - ] + ], }; } @@ -59,4 +60,4 @@ export function readResource(uri: string) { } throw new Error(`Resource not found: ${uri}`); -} \ No newline at end of file +} diff --git a/src/mcp/sampling.ts b/src/mcp/sampling.ts new file mode 100644 index 0000000..d1e0b79 --- /dev/null +++ b/src/mcp/sampling.ts @@ -0,0 +1,204 @@ +/** + * Sampling module for the Browserbase MCP server + * Implements sampling capability to request LLM completions from clients + * Docs: https://modelcontextprotocol.io/docs/concepts/sampling + */ + +/** + * Sampling capability configuration + * This indicates that the server can request LLM completions + */ +export const SAMPLING_CAPABILITY = {}; + +/** + * Note: Sampling in MCP is initiated BY the server TO the client. + * The server sends sampling/createMessage requests to ask the client + * for LLM completions. This is useful for intelligent browser automation + * where the server needs AI assistance to analyze pages and make decisions. + * + * Currently, sampling support depends on the MCP client implementation. + * Not all clients support sampling yet. (ie claude desktop) + */ + +/** + * Type definitions for sampling messages + */ +export type SamplingMessage = { + role: "user" | "assistant"; + content: { + type: "text" | "image"; + text?: string; + data?: string; // base64 for images + mimeType?: string; + }; +}; + +/** + * Pre-built sampling templates for common browser automation scenarios + */ +export const SAMPLING_TEMPLATES = { + /** + * Analyze a page to determine what actions are available + */ + analyzePageActions: ( + pageContent: string, + screenshot?: string, + ): SamplingMessage[] => [ + { + role: "user", + content: { + type: "text", + text: `Analyze this webpage and identify the main interactive elements and possible actions. + +Page content: +${pageContent} + +Please list: +1. Main navigation elements +2. Forms and input fields +3. Buttons and clickable elements +4. Key information displayed +5. Suggested next actions for common automation tasks`, + }, + }, + ...(screenshot + ? [ + { + role: "user" as const, + content: { + type: "image" as const, + data: screenshot, + mimeType: "image/png", + }, + }, + ] + : []), + ], + + /** + * Determine next steps in a multi-step process + */ + determineNextStep: ( + currentState: string, + goal: string, + ): SamplingMessage[] => [ + { + role: "user", + content: { + type: "text", + text: `Current state of the browser automation: +${currentState} + +Goal: ${goal} + +What should be the next action to take? Consider: +1. Are we on the right page? +2. What elements need to be interacted with? +3. Is there any data to extract first? +4. Are there any errors or blockers visible? + +Provide a specific, actionable next step.`, + }, + }, + ], + + /** + * Extract structured data from a page + */ + extractStructuredData: ( + pageContent: string, + dataSchema: string, + ): SamplingMessage[] => [ + { + role: "user", + content: { + type: "text", + text: `Extract structured data from this webpage according to the schema. + +Page content: +${pageContent} + +Expected data schema: +${dataSchema} + +Return the extracted data as valid JSON matching the schema. If any fields cannot be found, use null.`, + }, + }, + ], + + /** + * Handle error or unexpected state + */ + handleError: (error: string, pageState: string): SamplingMessage[] => [ + { + role: "user", + content: { + type: "text", + text: `The browser automation encountered an error: + +Error: ${error} + +Current page state: +${pageState} + +Suggest how to recover from this error: +1. What might have caused this? +2. What alternative actions could be taken? +3. Should we retry, navigate elsewhere, or try a different approach?`, + }, + }, + ], + + /** + * Interpret complex UI patterns + */ + interpretUI: (screenshot: string, instruction: string): SamplingMessage[] => [ + { + role: "user", + content: { + type: "text", + text: `Analyze this screenshot and help with: ${instruction}`, + }, + }, + { + role: "user", + content: { + type: "image", + data: screenshot, + mimeType: "image/png", + }, + }, + ], +}; + +/** + * Helper function to create a sampling request structure + * This shows what a sampling request would look like when sent to the client + */ +export function createSamplingRequest( + messages: SamplingMessage[], + options?: { + systemPrompt?: string; + temperature?: number; + maxTokens?: number; + includeContext?: "none" | "thisServer" | "allServers"; + }, +) { + return { + method: "sampling/createMessage", + params: { + messages, + systemPrompt: + options?.systemPrompt || + "You are an expert browser automation assistant helping to analyze web pages and determine optimal automation strategies.", + temperature: options?.temperature || 0.7, + maxTokens: options?.maxTokens || 1000, + includeContext: options?.includeContext || "thisServer", + modelPreferences: { + hints: [{ name: "claude-3" }, { name: "gpt-4" }], + intelligencePriority: 0.8, + speedPriority: 0.2, + }, + }, + }; +} diff --git a/src/program.ts b/src/program.ts new file mode 100644 index 0000000..ca85cdb --- /dev/null +++ b/src/program.ts @@ -0,0 +1,101 @@ +import { program } from "commander"; +import * as fs from "fs"; +import * as path from "path"; +import { fileURLToPath } from "url"; + +import createServerFunction from "./index.js"; +import { ServerList } from "./server.js"; +import { startHttpTransport, startStdioTransport } from "./transport.js"; +import * as stagehandStore from "./stagehandStore.js"; + +import { resolveConfig } from "./config.js"; + +let __filename: string; +let __dirname: string; + +try { + // Try ES modules first + __filename = fileURLToPath(import.meta.url); + __dirname = path.dirname(__filename); +} catch { + // Fallback for CommonJS or when import.meta is not available + __filename = + (globalThis as { __filename: string }).__filename || + process.cwd() + "/dist/program.js"; + __dirname = path.dirname(__filename); +} + +// Load package.json using fs +const packageJSONPath = path.resolve(__dirname, "../package.json"); +const packageJSONBuffer = fs.readFileSync(packageJSONPath); +const packageJSON = JSON.parse(packageJSONBuffer.toString()); + +program + .version("Version " + packageJSON.version) + .name(packageJSON.name) + .option("--browserbaseApiKey ", "The Browserbase API Key to use") + .option("--browserbaseProjectId ", "The Browserbase Project ID to use") + .option("--proxies", "Use Browserbase proxies.") + .option( + "--advancedStealth", + "Use advanced stealth mode. Only available to Browserbase Scale Plan users.", + ) + .option("--contextId ", "Browserbase Context ID to use.") + .option( + "--persist [boolean]", + "Whether to persist the Browserbase context", + true, + ) + .option("--port ", "Port to listen on for SHTTP transport.") + .option( + "--host ", + "Host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.", + ) + .option( + "--cookies [json]", + 'JSON array of cookies to inject into the browser. Format: [{"name":"cookie1","value":"val1","domain":"example.com"}, ...]', + ) + .option("--browserWidth ", "Browser width to use for the browser.") + .option("--browserHeight ", "Browser height to use for the browser.") + .option( + "--modelName ", + "The model to use for Stagehand (default: google/gemini-2.0-flash)", + ) + .option( + "--modelApiKey ", + "API key for the custom model provider (required when using custom models)", + ) + .action(async (options) => { + const config = await resolveConfig(options); + const serverList = new ServerList(async () => + createServerFunction({ + config: config as Required< + Pick + > & + typeof config, + }), + ); + setupExitWatchdog(serverList); + + if (options.port) + startHttpTransport(+options.port, options.host, serverList); + else await startStdioTransport(serverList, config); + }); + +function setupExitWatchdog(serverList: ServerList) { + const handleExit = async () => { + setTimeout(() => process.exit(0), 15000); + try { + await Promise.all([stagehandStore.removeAll(), serverList.closeAll()]); + } catch (error) { + console.error("Error during cleanup:", error); + } + process.exit(0); + }; + + process.stdin.on("close", handleExit); + process.on("SIGINT", handleExit); + process.on("SIGTERM", handleExit); +} + +program.parse(process.argv); diff --git a/browserbase/src/server.ts b/src/server.ts similarity index 80% rename from browserbase/src/server.ts rename to src/server.ts index be2cf13..1c5b833 100644 --- a/browserbase/src/server.ts +++ b/src/server.ts @@ -15,13 +15,12 @@ export class ServerList { } async close(server: Server) { - const index = this._servers.indexOf(server); - if (index !== -1) - this._servers.splice(index, 1); await server.close(); + const index = this._servers.indexOf(server); + if (index !== -1) this._servers.splice(index, 1); } async closeAll() { - await Promise.all(this._servers.map(server => server.close())); + await Promise.all(this._servers.map((server) => server.close())); } } diff --git a/browserbase/src/sessionManager.ts b/src/sessionManager.ts similarity index 64% rename from browserbase/src/sessionManager.ts rename to src/sessionManager.ts index 58fe132..5bbc00b 100644 --- a/browserbase/src/sessionManager.ts +++ b/src/sessionManager.ts @@ -1,19 +1,9 @@ -import { - chromium, - Browser, - Page, -} from "playwright-core"; -import { Browserbase } from "@browserbasehq/sdk"; -import type { Config } from "./config.js"; -import { SessionCreateParams } from "@browserbasehq/sdk/src/resources/sessions/sessions.js"; +import { Page } from "playwright-core"; +import { BrowserContext } from "@browserbasehq/stagehand"; +import type { Config } from "../config.d.ts"; import type { Cookie } from "playwright-core"; - -// Define the type for a session object -export type BrowserSession = { - browser: Browser; - page: Page; - sessionId: string; -}; +import { createStagehandInstance } from "./stagehandStore.js"; +import type { BrowserSession } from "./types/types.js"; // Global state for managing browser sessions const browsers = new Map(); @@ -22,7 +12,7 @@ const browsers = new Map(); let defaultBrowserSession: BrowserSession | null = null; // Define a specific ID for the default session -export const defaultSessionId = "browserbase_session_main"; +export const defaultSessionId = `browserbase_session_main_${Date.now()}`; // Keep track of the active session ID. Defaults to the main session. let activeSessionId: string = defaultSessionId; @@ -36,7 +26,7 @@ export function setActiveSessionId(id: string): void { activeSessionId = id; } else { process.stderr.write( - `[SessionManager] WARN - Set active session failed for non-existent ID: ${id}\n` + `[SessionManager] WARN - Set active session failed for non-existent ID: ${id}\n`, ); } } @@ -54,28 +44,36 @@ export function getActiveSessionId(): string { * @param context Playwright browser context * @param cookies Array of cookies to add */ -export async function addCookiesToContext(context: any, cookies: Cookie[]): Promise { +export async function addCookiesToContext( + context: BrowserContext, + cookies: Cookie[], +): Promise { if (!cookies || cookies.length === 0) { return; } - + try { - process.stderr.write(`[SessionManager] Adding ${cookies.length} cookies to browser context\n`); + process.stderr.write( + `[SessionManager] Adding ${cookies.length} cookies to browser context\n`, + ); await context.addCookies(cookies); - process.stderr.write(`[SessionManager] Successfully added cookies to browser context\n`); + process.stderr.write( + `[SessionManager] Successfully added cookies to browser context\n`, + ); } catch (error) { process.stderr.write( `[SessionManager] Error adding cookies to browser context: ${ error instanceof Error ? error.message : String(error) - }\n` + }\n`, ); } } -// Function to create a new Browserbase session and connect Playwright +// Function to create a new Browserbase session using Stagehand export async function createNewBrowserSession( newSessionId: string, - config: Config, + config: Config, + resumeSessionId?: string, ): Promise { if (!config.browserbaseApiKey) { throw new Error("Browserbase API Key is missing in the configuration."); @@ -84,48 +82,44 @@ export async function createNewBrowserSession( throw new Error("Browserbase Project ID is missing in the configuration."); } - const bb = new Browserbase({ - apiKey: config.browserbaseApiKey, - }); - - // Prepare session creation options - const sessionOptions: SessionCreateParams = { - // Use non-null assertion after check - projectId: config.browserbaseProjectId!, - proxies: config.proxies, - browserSettings: { - viewport: { - width: config.viewPort?.browserWidth ?? 1024, - height: config.viewPort?.browserHeight ?? 768, - }, - context: config.context?.contextId ? { - id: config.context?.contextId, - persist: config.context?.persist ?? true, - } : undefined, - advancedStealth: config.advancedStealth ?? undefined, - } - }; - try { process.stderr.write( - `[SessionManager] Creating session ${newSessionId}...\n` + `[SessionManager] ${resumeSessionId ? "Resuming" : "Creating"} Stagehand session ${newSessionId}...\n`, ); - const bbSession = await bb.sessions.create(sessionOptions); - process.stderr.write( - `[SessionManager] Browserbase session created: ${bbSession.id}\n` + + // Create and initialize Stagehand instance using shared function + const stagehand = await createStagehandInstance( + config, + { + ...(resumeSessionId && { browserbaseSessionID: resumeSessionId }), + }, + newSessionId, ); - const browser = await chromium.connectOverCDP(bbSession.connectUrl); + // Get the page and browser from Stagehand + const page = stagehand.page as unknown as Page; + const browser = page.context().browser(); + + if (!browser) { + throw new Error("Failed to get browser from Stagehand page context"); + } + + const browserbaseSessionId = stagehand.browserbaseSessionID; + process.stderr.write( - `[SessionManager] Browserbase Live Debugger URL: https://www.browserbase.com/sessions/${bbSession.id}\n` + `[SessionManager] Stagehand initialized with Browserbase session: ${browserbaseSessionId}\n`, + ); + process.stderr.write( + `[SessionManager] Browserbase Live Debugger URL: https://www.browserbase.com/sessions/${browserbaseSessionId}\n`, ); + // Set up disconnect handler browser.on("disconnected", () => { process.stderr.write(`[SessionManager] Disconnected: ${newSessionId}\n`); browsers.delete(newSessionId); if (defaultBrowserSession && defaultBrowserSession.browser === browser) { process.stderr.write( - `[SessionManager] Disconnected (default): ${newSessionId}\n` + `[SessionManager] Disconnected (default): ${newSessionId}\n`, ); defaultBrowserSession = null; } @@ -134,31 +128,29 @@ export async function createNewBrowserSession( newSessionId !== defaultSessionId ) { process.stderr.write( - `[SessionManager] WARN - Active session disconnected, resetting to default: ${newSessionId}\n` + `[SessionManager] WARN - Active session disconnected, resetting to default: ${newSessionId}\n`, ); setActiveSessionId(defaultSessionId); } }); - let context = browser.contexts()[0]; - if (!context) { - context = await browser.newContext(); - } - // Add cookies to the context if they are provided in the config - if (config.cookies && Array.isArray(config.cookies) && config.cookies.length > 0) { - await addCookiesToContext(context, config.cookies); - } - - let page = context.pages()[0]; - if (!page) { - page = await context.newPage(); + if ( + config.cookies && + Array.isArray(config.cookies) && + config.cookies.length > 0 + ) { + await addCookiesToContext( + page.context() as BrowserContext, + config.cookies, + ); } const sessionObj: BrowserSession = { browser, page, - sessionId: bbSession.id, + sessionId: browserbaseSessionId!, + stagehand, }; browsers.set(newSessionId, sessionObj); @@ -169,7 +161,7 @@ export async function createNewBrowserSession( setActiveSessionId(newSessionId); process.stderr.write( - `[SessionManager] Session created and active: ${newSessionId}\n` + `[SessionManager] Session created and active: ${newSessionId}\n`, ); return sessionObj; @@ -179,33 +171,33 @@ export async function createNewBrowserSession( ? creationError.message : String(creationError); process.stderr.write( - `[SessionManager] Creating session ${newSessionId} failed: ${ - creationError instanceof Error - ? creationError.message - : String(creationError) - }` - ); + `[SessionManager] Creating session ${newSessionId} failed: ${errorMessage}\n`, + ); throw new Error( - `Failed to create/connect session ${newSessionId}: ${errorMessage}` + `Failed to create/connect session ${newSessionId}: ${errorMessage}`, ); } } async function closeBrowserGracefully( session: BrowserSession | undefined | null, - sessionIdToLog: string + sessionIdToLog: string, ): Promise { - if (session?.browser?.isConnected()) { - process.stderr.write( - `[SessionManager] Closing browser for session: ${sessionIdToLog}\n` - ); + // Close Stagehand instance which handles browser cleanup + if (session?.stagehand) { try { - await session.browser.close(); + process.stderr.write( + `[SessionManager] Closing Stagehand for session: ${sessionIdToLog}\n`, + ); + await session.stagehand.close(); + process.stderr.write( + `[SessionManager] Successfully closed Stagehand and browser for session: ${sessionIdToLog}\n`, + ); } catch (closeError) { process.stderr.write( - `[SessionManager] WARN - Error closing browser for session ${sessionIdToLog}: ${ + `[SessionManager] WARN - Error closing Stagehand for session ${sessionIdToLog}: ${ closeError instanceof Error ? closeError.message : String(closeError) - }\n` + }\n`, ); } } @@ -213,7 +205,7 @@ async function closeBrowserGracefully( // Internal function to ensure default session export async function ensureDefaultSessionInternal( - config: Config + config: Config, ): Promise { const sessionId = defaultSessionId; let needsRecreation = false; @@ -221,7 +213,7 @@ export async function ensureDefaultSessionInternal( if (!defaultBrowserSession) { needsRecreation = true; process.stderr.write( - `[SessionManager] Default session ${sessionId} not found, creating.\n` + `[SessionManager] Default session ${sessionId} not found, creating.\n`, ); } else if ( !defaultBrowserSession.browser.isConnected() || @@ -229,7 +221,7 @@ export async function ensureDefaultSessionInternal( ) { needsRecreation = true; process.stderr.write( - `[SessionManager] Default session ${sessionId} is stale, recreating.\n` + `[SessionManager] Default session ${sessionId} is stale, recreating.\n`, ); await closeBrowserGracefully(defaultBrowserSession, sessionId); defaultBrowserSession = null; @@ -240,30 +232,33 @@ export async function ensureDefaultSessionInternal( try { defaultBrowserSession = await createNewBrowserSession(sessionId, config); return defaultBrowserSession; - } catch (error) { + } catch (creationError) { // Error during initial creation or recreation process.stderr.write( `[SessionManager] Initial/Recreation attempt for default session ${sessionId} failed. Error: ${ - error instanceof Error ? error.message : String(error) - }\n` + creationError instanceof Error + ? creationError.message + : String(creationError) + }\n`, ); // Attempt one more time after a failure process.stderr.write( - `[SessionManager] Retrying creation of default session ${sessionId} after error...\n` + `[SessionManager] Retrying creation of default session ${sessionId} after error...\n`, ); try { - defaultBrowserSession = await createNewBrowserSession(sessionId, config); + defaultBrowserSession = await createNewBrowserSession( + sessionId, + config, + ); return defaultBrowserSession; } catch (retryError) { const finalErrorMessage = - retryError instanceof Error - ? retryError.message - : String(retryError); + retryError instanceof Error ? retryError.message : String(retryError); process.stderr.write( - `[SessionManager] Failed to recreate default session ${sessionId} after retry: ${finalErrorMessage}\n` + `[SessionManager] Failed to recreate default session ${sessionId} after retry: ${finalErrorMessage}\n`, ); throw new Error( - `Failed to ensure default session ${sessionId} after initial error and retry: ${finalErrorMessage}` + `Failed to ensure default session ${sessionId} after initial error and retry: ${finalErrorMessage}`, ); } } @@ -277,15 +272,15 @@ export async function ensureDefaultSessionInternal( // Get a specific session by ID export async function getSession( sessionId: string, - config: Config + config: Config, + createIfMissing: boolean = true, ): Promise { - if (sessionId === defaultSessionId) { + if (sessionId === defaultSessionId && createIfMissing) { try { return await ensureDefaultSessionInternal(config); - } catch (error) { - // ensureDefaultSessionInternal already logs extensively + } catch { process.stderr.write( - `[SessionManager] Failed to get default session due to error in ensureDefaultSessionInternal for ${sessionId}. See previous messages for details.\n` + `[SessionManager] Failed to get default session due to error in ensureDefaultSessionInternal for ${sessionId}. See previous messages for details.\n`, ); return null; // Or rethrow if getSession failing for default is critical } @@ -293,11 +288,11 @@ export async function getSession( // For non-default sessions process.stderr.write(`[SessionManager] Getting session: ${sessionId}\n`); - let sessionObj = browsers.get(sessionId); + const sessionObj = browsers.get(sessionId); if (!sessionObj) { process.stderr.write( - `[SessionManager] WARN - Session not found in map: ${sessionId}\n` + `[SessionManager] WARN - Session not found in map: ${sessionId}\n`, ); return null; } @@ -305,13 +300,13 @@ export async function getSession( // Validate the found session if (!sessionObj.browser.isConnected() || sessionObj.page.isClosed()) { process.stderr.write( - `[SessionManager] WARN - Found session ${sessionId} is stale, removing.\n` + `[SessionManager] WARN - Found session ${sessionId} is stale, removing.\n`, ); await closeBrowserGracefully(sessionObj, sessionId); browsers.delete(sessionId); if (activeSessionId === sessionId) { process.stderr.write( - `[SessionManager] WARN - Invalidated active session ${sessionId}, resetting to default.\n` + `[SessionManager] WARN - Invalidated active session ${sessionId}, resetting to default.\n`, ); setActiveSessionId(defaultSessionId); } @@ -324,59 +319,32 @@ export async function getSession( return sessionObj; } -/** - * Get a session by ID without creating new sessions. - * This is a read-only operation that never triggers session creation. - * Used for operations like closing sessions where we don't want side effects. - * @param sessionId The session ID to retrieve - * @returns The session if it exists and is valid, null otherwise - */ -export function getSessionReadOnly(sessionId: string): BrowserSession | null { - // Check if it's the default session - if (sessionId === defaultSessionId && defaultBrowserSession) { - // Only return if it's actually connected and valid - if (defaultBrowserSession.browser.isConnected() && !defaultBrowserSession.page.isClosed()) { - return defaultBrowserSession; - } - return null; - } - - // For non-default sessions, check the browsers map - const sessionObj = browsers.get(sessionId); - if (!sessionObj) { - return null; - } - - // Validate the session is still active - if (!sessionObj.browser.isConnected() || sessionObj.page.isClosed()) { - return null; - } - - return sessionObj; -} - /** * Clean up a session by removing it from tracking. * This is called after a browser is closed to ensure proper cleanup. * @param sessionId The session ID to clean up */ -export function cleanupSession(sessionId: string): void { - process.stderr.write( - `[SessionManager] Cleaning up session: ${sessionId}\n` - ); - +export async function cleanupSession(sessionId: string): Promise { + process.stderr.write(`[SessionManager] Cleaning up session: ${sessionId}\n`); + + // Get the session to close it gracefully + const session = browsers.get(sessionId); + if (session) { + await closeBrowserGracefully(session, sessionId); + } + // Remove from browsers map browsers.delete(sessionId); - + // Clear default session reference if this was the default if (sessionId === defaultSessionId && defaultBrowserSession) { defaultBrowserSession = null; } - + // Reset active session to default if this was the active one if (activeSessionId === sessionId) { process.stderr.write( - `[SessionManager] Cleaned up active session ${sessionId}, resetting to default.\n` + `[SessionManager] Cleaned up active session ${sessionId}, resetting to default.\n`, ); setActiveSessionId(defaultSessionId); } @@ -390,15 +358,15 @@ export async function closeAllSessions(): Promise { process.stderr.write(`[SessionManager] Closing session: ${id}\n`); closePromises.push( // Use the helper for consistent logging/error handling - closeBrowserGracefully(session, id) + closeBrowserGracefully(session, id), ); } try { await Promise.all(closePromises); - } catch(e) { + } catch { // Individual errors are caught and logged by closeBrowserGracefully process.stderr.write( - `[SessionManager] WARN - Some errors occurred during batch session closing. See individual messages.\n` + `[SessionManager] WARN - Some errors occurred during batch session closing. See individual messages.\n`, ); } diff --git a/src/stagehandStore.ts b/src/stagehandStore.ts new file mode 100644 index 0000000..0c9c534 --- /dev/null +++ b/src/stagehandStore.ts @@ -0,0 +1,185 @@ +import { randomUUID } from "crypto"; +import { Stagehand, AvailableModel } from "@browserbasehq/stagehand"; +import { Page } from "playwright-core"; +import { StagehandSession, CreateSessionParams } from "./types/types.js"; +import type { Config } from "../config.d.ts"; + +// Store for all active sessions +const store = new Map(); + +/** + * Create a configured Stagehand instance + */ +export const createStagehandInstance = async ( + config: Config, + params: CreateSessionParams = {}, + sessionId: string, +): Promise => { + const apiKey = params.apiKey || config.browserbaseApiKey; + const projectId = params.projectId || config.browserbaseProjectId; + + if (!apiKey || !projectId) { + throw new Error("Browserbase API Key and Project ID are required"); + } + + const stagehand = new Stagehand({ + env: "BROWSERBASE", + apiKey, + projectId, + modelName: (params.modelName || + config.modelName || + "google/gemini-2.0-flash") as AvailableModel, + modelClientOptions: { + apiKey: config.modelApiKey || process.env.GEMINI_API_KEY, + }, + ...(params.browserbaseSessionID && { + browserbaseSessionID: params.browserbaseSessionID, + }), + browserbaseSessionCreateParams: { + projectId, + proxies: config.proxies, + browserSettings: { + viewport: { + width: config.viewPort?.browserWidth ?? 1024, + height: config.viewPort?.browserHeight ?? 768, + }, + context: config.context?.contextId + ? { + id: config.context?.contextId, + persist: config.context?.persist ?? true, + } + : undefined, + advancedStealth: config.advancedStealth ?? undefined, + }, + }, + logger: (logLine) => { + console.error(`Stagehand[${sessionId}]: ${logLine.message}`); + }, + }); + + await stagehand.init(); + return stagehand; +}; + +/** + * Create a new Stagehand session + */ +export const create = async ( + config: Config, + params: CreateSessionParams = {}, +): Promise => { + // Global ID, must be 100% Unique + const id = randomUUID() + "_" + config.browserbaseProjectId; + + process.stderr.write(`[StagehandStore] Creating new session ${id}...\n`); + + const stagehand = await createStagehandInstance(config, params, id); + + const page = stagehand.page as unknown as Page; + const browser = page.context().browser(); + + if (!browser) { + throw new Error("Failed to get browser from Stagehand page context"); + } + + const session: StagehandSession = { + id, + stagehand, + page, + browser, + created: Date.now(), + metadata: { + ...params.meta, + bbSessionId: stagehand.browserbaseSessionID, + }, + }; + + store.set(id, session); + + process.stderr.write( + `[StagehandStore] Session created: ${id} (BB: ${stagehand.browserbaseSessionID})\n`, + ); + process.stderr.write( + `[StagehandStore] Live debugger: https://www.browserbase.com/sessions/${stagehand.browserbaseSessionID}\n`, + ); + + // Set up disconnect handler + const disconnectHandler = () => { + process.stderr.write(`[StagehandStore] Session disconnected: ${id}\n`); + store.delete(id); + }; + + browser.on("disconnected", disconnectHandler); + + // Store the handler for cleanup + session.metadata = { + ...session.metadata, + disconnectHandler, + }; + + return session; +}; + +/** + * Get a session by ID + */ +export const get = (id: string): StagehandSession | null => { + return store.get(id) ?? null; +}; + +/** + * List all active sessions + */ +export const list = (): StagehandSession[] => { + return Array.from(store.values()); +}; + +/** + * Remove and close a session + */ +export const remove = async (id: string): Promise => { + const session = store.get(id); + if (!session) { + process.stderr.write( + `[StagehandStore] Session not found for removal: ${id}\n`, + ); + return; + } + + process.stderr.write(`[StagehandStore] Removing session: ${id}\n`); + + try { + if (session.metadata?.disconnectHandler) { + session.browser.off("disconnected", session.metadata.disconnectHandler); + } + + await session.stagehand.close(); + process.stderr.write(`[StagehandStore] Session closed: ${id}\n`); + } catch (error) { + process.stderr.write( + `[StagehandStore] Error closing session ${id}: ${ + error instanceof Error ? error.message : String(error) + }\n`, + ); + } finally { + store.delete(id); + } +}; + +/** + * Remove all sessions + */ +export const removeAll = async (): Promise => { + process.stderr.write( + `[StagehandStore] Removing all ${store.size} sessions...\n`, + ); + await Promise.all(list().map((s) => remove(s.id))); + process.stderr.write(`[StagehandStore] All sessions removed\n`); +}; + +/** + * Get store size + */ +export const size = (): number => { + return store.size; +}; diff --git a/src/tools/act.ts b/src/tools/act.ts new file mode 100644 index 0000000..09d5aab --- /dev/null +++ b/src/tools/act.ts @@ -0,0 +1,77 @@ +import { z } from "zod"; +import type { Tool, ToolSchema, ToolResult } from "./tool.js"; +import type { Context } from "../context.js"; +import type { ToolActionResult } from "../types/types.js"; + +const ActInputSchema = z.object({ + action: z + .string() + .describe( + "The action to perform. Should be as atomic and specific as possible, " + + "i.e. 'Click the sign in button' or 'Type 'hello' into the search input'. AVOID actions that are more than one " + + "step, i.e. 'Order me pizza' or 'Send an email to Paul asking him to call me'. The instruction should be just as specific as possible, " + + "and have a strong correlation to the text on the page. If unsure, use observe before using act.", + ), + variables: z + .object({}) + .optional() + .describe( + "Variables used in the action template. ONLY use variables if you're dealing " + + "with sensitive data or dynamic content. For example, if you're logging in to a website, " + + "you can use a variable for the password. When using variables, you MUST have the variable " + + 'key in the action template. For example: {"action": "Fill in the password", "variables": {"password": "123456"}}', + ), +}); + +type ActInput = z.infer; + +const actSchema: ToolSchema = { + name: "browserbase_stagehand_act", + description: + "Performs an action on a web page element. Act actions should be as atomic and " + + 'specific as possible, i.e. "Click the sign in button" or "Type \'hello\' into the search input". ' + + 'AVOID actions that are more than one step, i.e. "Order me pizza" or "Send an email to Paul ' + + 'asking him to call me".', + inputSchema: ActInputSchema, +}; + +async function handleAct( + context: Context, + params: ActInput, +): Promise { + const action = async (): Promise => { + try { + const stagehand = await context.getStagehand(); + + await stagehand.page.act({ + action: params.action, + variables: params.variables, + }); + + return { + content: [ + { + type: "text", + text: `Action performed: ${params.action}`, + }, + ], + }; + } catch (error) { + const errorMsg = error instanceof Error ? error.message : String(error); + throw new Error(`Failed to perform action: ${errorMsg}`); + } + }; + + return { + action, + waitForNetwork: false, + }; +} + +const actTool: Tool = { + capability: "core", + schema: actSchema, + handle: handleAct, +}; + +export default actTool; diff --git a/src/tools/extract.ts b/src/tools/extract.ts new file mode 100644 index 0000000..b371cd3 --- /dev/null +++ b/src/tools/extract.ts @@ -0,0 +1,70 @@ +import { z } from "zod"; +import type { Tool, ToolSchema, ToolResult } from "./tool.js"; +import type { Context } from "../context.js"; +import type { ToolActionResult } from "../types/types.js"; + +const ExtractInputSchema = z.object({ + instruction: z + .string() + .describe( + "The specific instruction for what information to extract from the current page. " + + "Be as detailed and specific as possible about what you want to extract. For example: " + + "'Extract all product names and prices from the listing page' or 'Get the article title, " + + "author, and publication date from this blog post'. The more specific your instruction, " + + "the better the extraction results will be. Avoid vague instructions like 'get everything' " + + "or 'extract the data'. Instead, be explicit about the exact elements, text, or information you need.", + ), +}); + +type ExtractInput = z.infer; + +const extractSchema: ToolSchema = { + name: "browserbase_stagehand_extract", + description: + "Extracts structured information and text content from the current web page based on specific instructions " + + "and a defined schema. This tool is ideal for scraping data, gathering information, or pulling specific " + + "content from web pages. Use this tool when you need to get text content, data, or information from a page " + + "rather than interacting with elements. For interactive elements like buttons, forms, or clickable items, " + + "use the observe tool instead. The extraction works best when you provide clear, specific instructions " + + "about what to extract and a well-defined JSON schema for the expected output format. This ensures " + + "the extracted data is properly structured and usable.", + inputSchema: ExtractInputSchema, +}; + +async function handleExtract( + context: Context, + params: ExtractInput, +): Promise { + const action = async (): Promise => { + try { + const stagehand = await context.getStagehand(); + + const extraction = await stagehand.page.extract(params.instruction); + + return { + content: [ + { + type: "text", + text: `Extracted content:\n${JSON.stringify(extraction, null, 2)}`, + }, + ], + }; + } catch (error) { + const errorMsg = error instanceof Error ? error.message : String(error); + throw new Error(`Failed to extract content: ${errorMsg}`); + } + }; + + return { + action, + waitForNetwork: false, + }; +} + +const extractTool: Tool = { + capability: "core", + schema: extractSchema, + handle: handleExtract, +}; + +export default extractTool; diff --git a/src/tools/index.ts b/src/tools/index.ts new file mode 100644 index 0000000..e27feba --- /dev/null +++ b/src/tools/index.ts @@ -0,0 +1,47 @@ +import navigateTool from "./navigate.js"; +import actTool from "./act.js"; +import extractTool from "./extract.js"; +import observeTool from "./observe.js"; +import screenshotTool from "./screenshot.js"; +import sessionTools from "./session.js"; +import { + createSessionTool, + listSessionsTool, + closeSessionTool, + navigateWithSessionTool, + actWithSessionTool, + extractWithSessionTool, + observeWithSessionTool, +} from "./multiSession.js"; + +// Export individual tools +export { default as navigateTool } from "./navigate.js"; +export { default as actTool } from "./act.js"; +export { default as extractTool } from "./extract.js"; +export { default as observeTool } from "./observe.js"; +export { default as screenshotTool } from "./screenshot.js"; +export { default as sessionTools } from "./session.js"; + +// Multi-session tools array +export const multiSessionTools = [ + createSessionTool, + listSessionsTool, + closeSessionTool, + navigateWithSessionTool, + actWithSessionTool, + extractWithSessionTool, + observeWithSessionTool, +]; + +// Export all tools as array +export const TOOLS = [ + ...multiSessionTools, + ...sessionTools, + navigateTool, + actTool, + extractTool, + observeTool, + screenshotTool, +]; + +export const sessionManagementTools = sessionTools; diff --git a/src/tools/multiSession.ts b/src/tools/multiSession.ts new file mode 100644 index 0000000..4428dc6 --- /dev/null +++ b/src/tools/multiSession.ts @@ -0,0 +1,247 @@ +import { z } from "zod"; +import { + defineTool, + type Tool, + type ToolResult, + type InputType, +} from "./tool.js"; +import * as stagehandStore from "../stagehandStore.js"; +import { CreateSessionParams } from "../types/types.js"; +import type { Context } from "../context.js"; +import navigateTool from "./navigate.js"; +import actTool from "./act.js"; +import extractTool from "./extract.js"; +import observeTool from "./observe.js"; + +/** + * Creates a session-aware version of an existing tool + * This wraps the original tool's handler to work with a specific session + */ +function createMultiSessionAwareTool( + originalTool: Tool, + options: { + namePrefix?: string; + nameSuffix?: string; + } = {}, +): Tool { + const { namePrefix = "", nameSuffix = "_session" } = options; + + // Create new input schema that includes sessionId + const originalSchema = originalTool.schema.inputSchema; + let newInputSchema: z.ZodSchema; + + if (originalSchema instanceof z.ZodObject) { + // If it's a ZodObject, we can spread its shape + newInputSchema = z.object({ + sessionId: z.string().describe("The session ID to use"), + ...originalSchema.shape, + }); + } else { + // For other schema types, create an intersection + newInputSchema = z.intersection( + z.object({ sessionId: z.string().describe("The session ID to use") }), + originalSchema, + ); + } + + return defineTool({ + capability: originalTool.capability, + schema: { + name: `${namePrefix}${originalTool.schema.name}${nameSuffix}`, + description: `${originalTool.schema.description} (for a specific session)`, + inputSchema: newInputSchema, + }, + handle: async ( + context: Context, + params: z.infer, + ): Promise => { + const { sessionId, ...originalParams } = params; + + // Get the session + const session = stagehandStore.get(sessionId); + if (!session) { + throw new Error(`Session ${sessionId} not found`); + } + + // Create a temporary context that points to the specific session + const sessionContext = Object.create(context); + sessionContext.currentSessionId = + session.metadata?.bbSessionId || sessionId; + sessionContext.getStagehand = async () => session.stagehand; + sessionContext.getActivePage = async () => session.page; + sessionContext.getActiveBrowser = async () => session.browser; + + // Call the original tool's handler with the session-specific context + return originalTool.handle(sessionContext, originalParams); + }, + }); +} + +// Create session tool +export const createSessionTool = defineTool({ + capability: "create_session", + schema: { + name: "multi_browserbase_stagehand_session_create", + description: + "Create parallel browser session for multi-session workflows. Use this when you need multiple browser instances running simultaneously: parallel data scraping, concurrent automation, A/B testing, multiple user accounts, cross-site operations, batch processing, or any task requiring more than one browser. Creates an isolated browser session with independent cookies, authentication, and state. Always pair with session-specific tools (those ending with '_session'). Perfect for scaling automation tasks that require multiple browsers working in parallel.", + inputSchema: z.object({ + name: z + .string() + .optional() + .describe( + "Highly recommended: Descriptive name for tracking multiple sessions (e.g. 'amazon-scraper', 'user-login-flow', 'checkout-test-1'). Makes debugging and session management much easier!", + ), + browserbaseSessionID: z + .string() + .optional() + .describe( + "Resume an existing Browserbase session by providing its session ID. Use this to continue work in a previously created browser session that may have been paused or disconnected.", + ), + }), + }, + handle: async ( + context: Context, + { name, browserbaseSessionID }, + ): Promise => { + try { + const params: CreateSessionParams = { + browserbaseSessionID, + meta: name ? { name } : undefined, + }; + + const session = await stagehandStore.create(context.config, params); + + return { + action: async () => ({ + content: [ + { + type: "text", + text: `Created session ${session.id}${name ? ` (${name})` : ""}\nBrowserbase session: ${session.metadata?.bbSessionId}`, + }, + ], + }), + waitForNetwork: false, + }; + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + throw new Error( + `Failed to create browser session: ${errorMessage}. Please check your Browserbase credentials and try again.`, + ); + } + }, +}); + +// List sessions tool +export const listSessionsTool = defineTool({ + capability: "list_sessions", + schema: { + name: "multi_browserbase_stagehand_session_list", + description: + "ONLY WORKS WITH MULTI-SESSION TOOLS! Track all parallel sessions: Critical tool for multi-session management! Shows all active browser sessions with their IDs, names, ages, and Browserbase session IDs. Use this frequently to monitor your parallel automation workflows, verify sessions are running, and get session IDs for session-specific tools. Essential for debugging and resource management in complex multi-browser scenarios.", + inputSchema: z.object({}), + }, + handle: async (): Promise => { + const sessions = stagehandStore.list(); + + if (sessions.length === 0) { + return { + action: async () => ({ + content: [ + { + type: "text", + text: "No active sessions", + }, + ], + }), + waitForNetwork: false, + }; + } + + const sessionInfo = sessions.map((s) => ({ + id: s.id, + name: s.metadata?.name, + browserbaseSessionId: s.metadata?.bbSessionId, + created: new Date(s.created).toISOString(), + age: Math.floor((Date.now() - s.created) / 1000), + })); + + return { + action: async () => ({ + content: [ + { + type: "text", + text: `Active sessions (${sessions.length}):\n${sessionInfo + .map( + (s) => + `- ${s.id}${s.name ? ` (${s.name})` : ""} - BB: ${s.browserbaseSessionId} - Age: ${s.age}s`, + ) + .join("\n")}`, + }, + ], + }), + waitForNetwork: false, + }; + }, +}); + +// Close session tool +export const closeSessionTool = defineTool({ + capability: "close_session", + schema: { + name: "multi_browserbase_stagehand_session_close", + description: + "Cleanup parallel session for multi-session workflows. Properly terminates a browser session, ends the Browserbase session, and frees cloud resources. Always use this when finished with a session to avoid resource waste and billing charges. Critical for responsible multi-session automation - each unclosed session continues consuming resources!", + inputSchema: z.object({ + sessionId: z + .string() + .describe( + "Exact session ID to close (get from 'multi_browserbase_stagehand_session_list'). Double-check this ID - once closed, the session cannot be recovered!", + ), + }), + }, + handle: async (_context: Context, { sessionId }): Promise => { + const session = stagehandStore.get(sessionId); + if (!session) { + throw new Error(`Session ${sessionId} not found`); + } + + await stagehandStore.remove(sessionId); + + return { + action: async () => ({ + content: [ + { + type: "text", + text: `Closed session ${sessionId}`, + }, + ], + }), + waitForNetwork: false, + }; + }, +}); + +// Create multi-session-aware versions of the core tools +export const navigateWithSessionTool = createMultiSessionAwareTool( + navigateTool, + { + namePrefix: "multi_", + nameSuffix: "_session", + }, +); + +export const actWithSessionTool = createMultiSessionAwareTool(actTool, { + namePrefix: "multi_", + nameSuffix: "_session", +}); + +export const extractWithSessionTool = createMultiSessionAwareTool(extractTool, { + namePrefix: "multi_", + nameSuffix: "_session", +}); + +export const observeWithSessionTool = createMultiSessionAwareTool(observeTool, { + namePrefix: "multi_", + nameSuffix: "_session", +}); diff --git a/src/tools/navigate.ts b/src/tools/navigate.ts new file mode 100644 index 0000000..0f231f8 --- /dev/null +++ b/src/tools/navigate.ts @@ -0,0 +1,63 @@ +import { z } from "zod"; +import type { Tool, ToolSchema, ToolResult } from "./tool.js"; +import type { Context } from "../context.js"; +import type { ToolActionResult } from "../types/types.js"; + +const NavigateInputSchema = z.object({ + url: z.string().describe("The URL to navigate to"), +}); + +type NavigateInput = z.infer; + +const navigateSchema: ToolSchema = { + name: "browserbase_stagehand_navigate", + description: + "Navigate to a URL in the browser. Only use this tool with URLs you're confident will work and stay up to date. Otherwise, use https://google.com as the starting point", + inputSchema: NavigateInputSchema, +}; + +async function handleNavigate( + context: Context, + params: NavigateInput, +): Promise { + const action = async (): Promise => { + try { + const stagehand = await context.getStagehand(); + const page = await context.getActivePage(); + + if (!page) { + throw new Error("No active page available"); + } + await page.goto(params.url, { waitUntil: "domcontentloaded" }); + + return { + content: [ + { + type: "text", + text: `Navigated to: ${params.url}`, + }, + { + type: "text", + text: `View the live session here: https://www.browserbase.com/sessions/${stagehand.browserbaseSessionID}`, + }, + ], + }; + } catch (error) { + const errorMsg = error instanceof Error ? error.message : String(error); + throw new Error(`Failed to navigate: ${errorMsg}`); + } + }; + + return { + action, + waitForNetwork: false, + }; +} + +const navigateTool: Tool = { + capability: "core", + schema: navigateSchema, + handle: handleNavigate, +}; + +export default navigateTool; diff --git a/src/tools/observe.ts b/src/tools/observe.ts new file mode 100644 index 0000000..4470214 --- /dev/null +++ b/src/tools/observe.ts @@ -0,0 +1,83 @@ +import { z } from "zod"; +import type { Tool, ToolSchema, ToolResult } from "./tool.js"; +import type { Context } from "../context.js"; +import type { ToolActionResult } from "../types/types.js"; + +const ObserveInputSchema = z.object({ + instruction: z + .string() + .describe( + "Detailed instruction for what specific elements or components to observe on the web page. " + + "This instruction must be extremely specific and descriptive. For example: 'Find the red login button " + + "in the top right corner', 'Locate the search input field with placeholder text', or 'Identify all " + + "clickable product cards on the page'. The more specific and detailed your instruction, the better " + + "the observation results will be. Avoid generic instructions like 'find buttons' or 'see elements'. " + + "Instead, describe the visual characteristics, location, text content, or functionality of the elements " + + "you want to observe. This tool is designed to help you identify interactive elements that you can " + + "later use with the act tool for performing actions like clicking, typing, or form submission.", + ), + returnAction: z + .boolean() + .optional() + .describe( + "Whether to return the action to perform on the element. If true, the action will be returned as a string. " + + "If false, the action will not be returned.", + ), +}); + +type ObserveInput = z.infer; + +const observeSchema: ToolSchema = { + name: "browserbase_stagehand_observe", + description: + "Observes and identifies specific interactive elements on the current web page that can be used for subsequent actions. " + + "This tool is specifically designed for finding actionable (interactable) elements such as buttons, links, form fields, " + + "dropdowns, checkboxes, and other UI components that you can interact with. Use this tool when you need to locate " + + "elements before performing actions with the act tool. DO NOT use this tool for extracting text content or data - " + + "use the extract tool instead for that purpose. The observe tool returns detailed information about the identified " + + "elements including their properties, location, and interaction capabilities. This information can then be used " + + "to craft precise actions. The more specific your observation instruction, the more accurate the element identification " + + "will be. Think of this as your 'eyes' on the page to find exactly what you need to interact with.", + inputSchema: ObserveInputSchema, +}; + +async function handleObserve( + context: Context, + params: ObserveInput, +): Promise { + const action = async (): Promise => { + try { + const stagehand = await context.getStagehand(); + + const observations = await stagehand.page.observe({ + instruction: params.instruction, + returnAction: params.returnAction, + }); + + return { + content: [ + { + type: "text", + text: `Observations: ${JSON.stringify(observations)}`, + }, + ], + }; + } catch (error) { + const errorMsg = error instanceof Error ? error.message : String(error); + throw new Error(`Failed to observe: ${errorMsg}`); + } + }; + + return { + action, + waitForNetwork: false, + }; +} + +const observeTool: Tool = { + capability: "core", + schema: observeSchema, + handle: handleObserve, +}; + +export default observeTool; diff --git a/src/tools/screenshot.ts b/src/tools/screenshot.ts new file mode 100644 index 0000000..a680799 --- /dev/null +++ b/src/tools/screenshot.ts @@ -0,0 +1,85 @@ +import { z } from "zod"; +import type { Tool, ToolSchema, ToolResult } from "./tool.js"; +import type { Context } from "../context.js"; +import type { ToolActionResult } from "../types/types.js"; +import { screenshots } from "../mcp/resources.js"; + +const ScreenshotInputSchema = z.object({ + name: z.string().optional().describe("The name of the screenshot"), +}); + +type ScreenshotInput = z.infer; + +const screenshotSchema: ToolSchema = { + name: "browserbase_screenshot", + description: + "Takes a screenshot of the current page. Use this tool to learn where you are on the page when controlling the browser with Stagehand. Only use this tool when the other tools are not sufficient to get the information you need.", + inputSchema: ScreenshotInputSchema, +}; + +async function handleScreenshot( + context: Context, + params: ScreenshotInput, +): Promise { + const action = async (): Promise => { + try { + const page = await context.getActivePage(); + if (!page) { + throw new Error("No active page available"); + } + + const screenshotBuffer = await page.screenshot({ + fullPage: false, + }); + + // Convert buffer to base64 string and store in memory + const screenshotBase64 = screenshotBuffer.toString("base64"); + const name = params.name + ? `screenshot-${params.name}-${new Date() + .toISOString() + .replace(/:/g, "-")}` + : `screenshot-${new Date().toISOString().replace(/:/g, "-")}` + + context.config.browserbaseProjectId; + screenshots.set(name, screenshotBase64); + + // Notify the client that the resources changed + const serverInstance = context.getServer(); + + if (serverInstance) { + serverInstance.notification({ + method: "notifications/resources/list_changed", + }); + } + + return { + content: [ + { + type: "text", + text: `Screenshot taken with name: ${name}`, + }, + { + type: "image", + data: screenshotBase64, + mimeType: "image/png", + }, + ], + }; + } catch (error) { + const errorMsg = error instanceof Error ? error.message : String(error); + throw new Error(`Failed to take screenshot: ${errorMsg}`); + } + }; + + return { + action, + waitForNetwork: false, + }; +} + +const screenshotTool: Tool = { + capability: "core", + schema: screenshotSchema, + handle: handleScreenshot, +}; + +export default screenshotTool; diff --git a/src/tools/session.ts b/src/tools/session.ts new file mode 100644 index 0000000..c5c4e6a --- /dev/null +++ b/src/tools/session.ts @@ -0,0 +1,227 @@ +import { z } from "zod"; +import type { Tool, ToolSchema, ToolResult } from "./tool.js"; +import type { Context } from "../context.js"; +import type { ToolActionResult } from "../types/types.js"; + +// Import SessionManager functions +import { + createNewBrowserSession, + defaultSessionId, + ensureDefaultSessionInternal, + cleanupSession, + getSession, +} from "../sessionManager.js"; +import type { BrowserSession } from "../types/types.js"; + +// --- Tool: Create Session --- +const CreateSessionInputSchema = z.object({ + // Keep sessionId optional, but clarify its role + sessionId: z + .string() + .optional() + .describe( + "Optional session ID to use/reuse. If not provided or invalid, a new session is created.", + ), +}); +type CreateSessionInput = z.infer; + +const createSessionSchema: ToolSchema = { + name: "browserbase_session_create", + description: + "Create or reuse a single cloud browser session using Browserbase with fully initialized Stagehand. WARNING: This tool is for SINGLE browser workflows only. If you need multiple browser sessions running simultaneously (parallel scraping, A/B testing, multiple accounts), use 'multi_browserbase_stagehand_session_create' instead. This creates one browser session with all configuration flags (proxies, stealth, viewport, cookies, etc.) and initializes Stagehand to work with that session. Updates the active session.", + inputSchema: CreateSessionInputSchema, +}; + +// Handle function for CreateSession using SessionManager +async function handleCreateSession( + context: Context, + params: CreateSessionInput, +): Promise { + const action = async (): Promise => { + try { + const config = context.config; // Get config from context + let targetSessionId: string; + + if (params.sessionId) { + const projectId = config.browserbaseProjectId || ""; + targetSessionId = `${params.sessionId}_${projectId}`; + process.stderr.write( + `[tool.createSession] Attempting to create/assign session with specified ID: ${targetSessionId}`, + ); + } else { + targetSessionId = defaultSessionId; + } + + let session: BrowserSession; + if (targetSessionId === defaultSessionId) { + session = await ensureDefaultSessionInternal(config); + } else { + // When user provides a sessionId, we want to resume that Browserbase session + session = await createNewBrowserSession( + targetSessionId, + config, + params.sessionId, + ); + } + + if ( + !session || + !session.browser || + !session.page || + !session.sessionId || + !session.stagehand + ) { + throw new Error( + `SessionManager failed to return a valid session object with actualSessionId for ID: ${targetSessionId}`, + ); + } + + context.currentSessionId = targetSessionId; + process.stderr.write( + `[tool.connected] Successfully connected to Browserbase session. Internal ID: ${targetSessionId}, Actual ID: ${session.sessionId}`, + ); + + process.stderr.write( + `[SessionManager] Browserbase Live Debugger URL: https://www.browserbase.com/sessions/${session.sessionId}`, + ); + + return { + content: [ + { + type: "text", + text: `https://www.browserbase.com/sessions/${session.sessionId}`, + }, + ], + }; + } catch (error: unknown) { + const errorMessage = + error instanceof Error ? error.message : String(error); + process.stderr.write( + `[tool.createSession] Action failed: ${errorMessage}`, + ); + // Re-throw to be caught by Context.run's error handling for actions + throw new Error(`Failed to create Browserbase session: ${errorMessage}`); + } + }; + + // Return the ToolResult structure expected by Context.run + return { + action: action, + waitForNetwork: false, + }; +} + +// Define tool using handle +const createSessionTool: Tool = { + capability: "core", // Add capability + schema: createSessionSchema, + handle: handleCreateSession, +}; + +// --- Tool: Close Session --- +const CloseSessionInputSchema = z.object({}); + +const closeSessionSchema: ToolSchema = { + name: "browserbase_session_close", + description: + "Closes the current Browserbase session by properly shutting down the Stagehand instance, which handles browser cleanup and terminates the session recording.", + inputSchema: CloseSessionInputSchema, +}; + +async function handleCloseSession(context: Context): Promise { + const action = async (): Promise => { + // Store the current session ID before it's potentially changed. + const previousSessionId = context.currentSessionId; + let stagehandClosedSuccessfully = false; + let stagehandCloseErrorMessage = ""; + + // Step 1: Attempt to get the session and close Stagehand + let browserbaseSessionId: string | undefined; + try { + const session = await getSession( + previousSessionId, + context.config, + false, + ); + + if (session && session.stagehand) { + // Store the actual Browserbase session ID for the replay URL + browserbaseSessionId = session.sessionId; + + process.stderr.write( + `[tool.closeSession] Attempting to close Stagehand for session: ${previousSessionId || "default"} (Browserbase ID: ${browserbaseSessionId})`, + ); + + // Use Stagehand's close method which handles browser cleanup properly + await session.stagehand.close(); + stagehandClosedSuccessfully = true; + + process.stderr.write( + `[tool.closeSession] Stagehand and browser connection for session (${previousSessionId}) closed successfully.`, + ); + + // Clean up the session from tracking + await cleanupSession(previousSessionId); + + if (browserbaseSessionId) { + process.stderr.write( + `[tool.closeSession] View session replay at https://www.browserbase.com/sessions/${browserbaseSessionId}`, + ); + } + } else { + process.stderr.write( + `[tool.closeSession] No Stagehand instance found for session: ${previousSessionId || "default/unknown"}`, + ); + } + } catch (error: unknown) { + stagehandCloseErrorMessage = + error instanceof Error ? error.message : String(error); + process.stderr.write( + `[tool.closeSession] Error retrieving or closing Stagehand (session ID was ${previousSessionId || "default/unknown"}): ${stagehandCloseErrorMessage}`, + ); + } + + // Step 2: Always reset the context's current session ID to default + const oldContextSessionId = context.currentSessionId; + context.currentSessionId = defaultSessionId; + process.stderr.write( + `[tool.closeSession] Session context reset to default. Previous context session ID was ${oldContextSessionId || "default/unknown"}.`, + ); + + // Step 3: Determine the result message + if (stagehandCloseErrorMessage && !stagehandClosedSuccessfully) { + throw new Error( + `Failed to close the Stagehand session (session ID in context was ${previousSessionId || "default/unknown"}). Error: ${stagehandCloseErrorMessage}. Session context has been reset to default.`, + ); + } + + if (stagehandClosedSuccessfully) { + let successMessage = `Browserbase session (${previousSessionId || "default"}) closed successfully via Stagehand. Context reset to default.`; + if (browserbaseSessionId && previousSessionId !== defaultSessionId) { + successMessage += ` View replay at https://www.browserbase.com/sessions/${browserbaseSessionId}`; + } + return { content: [{ type: "text", text: successMessage }] }; + } + + // No Stagehand instance was found + let infoMessage = + "No active Stagehand session found to close. Session context has been reset to default."; + if (previousSessionId && previousSessionId !== defaultSessionId) { + infoMessage = `No active Stagehand session found for session ID '${previousSessionId}'. The context has been reset to default.`; + } + return { content: [{ type: "text", text: infoMessage }] }; + }; + + return { + action: action, + waitForNetwork: false, + }; +} + +const closeSessionTool: Tool = { + capability: "core", + schema: closeSessionSchema, + handle: handleCloseSession, +}; + +export default [createSessionTool, closeSessionTool]; diff --git a/src/tools/tool.ts b/src/tools/tool.ts new file mode 100644 index 0000000..e7622d5 --- /dev/null +++ b/src/tools/tool.ts @@ -0,0 +1,39 @@ +import type { + ImageContent, + TextContent, +} from "@modelcontextprotocol/sdk/types.js"; +import type { z } from "zod"; +import type { Context } from "../context.js"; + +export type ToolSchema = { + name: string; + description: string; + inputSchema: Input; +}; + +// Export InputType +export type InputType = z.Schema; + +export type ToolActionResult = + | { content?: (ImageContent | TextContent)[] } + | undefined + | void; + +export type ToolResult = { + action?: () => Promise; + waitForNetwork: boolean; +}; + +export type Tool = { + capability: string; + schema: ToolSchema; + handle: (context: Context, params: z.output) => Promise; +}; + +export function defineTool( + tool: Tool, +): Tool { + return tool; +} + +export {}; // Ensure this is treated as a module diff --git a/src/transport.ts b/src/transport.ts new file mode 100644 index 0000000..aae5354 --- /dev/null +++ b/src/transport.ts @@ -0,0 +1,162 @@ +import http from "node:http"; +import assert from "node:assert"; +import crypto from "node:crypto"; + +import { ServerList } from "./server.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import type { Config } from "../config.d.ts"; + +export async function startStdioTransport( + serverList: ServerList, + config?: Config, +) { + // Check if we're using the default model without an API key + if (config) { + const modelName = config.modelName || "google/gemini-2.0-flash"; + const hasModelApiKey = config.modelApiKey || process.env.GEMINI_API_KEY; + + if (modelName.includes("google/gemini") && !hasModelApiKey) { + console.error(` +⚠️ IMPORTANT: MCP Server Configuration Update Required + +We've made changes to the MCP server that now require model API keys for local STDIO usage. + +You're using the default Gemini model (${modelName}) but no API key is configured. + +To fix this, you have two options: + +1. Set the GEMINI_API_KEY environment variable: + export GEMINI_API_KEY="your-gemini-api-key" + +2. Or add the --modelApiKey flag to your MCP config: + { + "mcpServers": { + "browserbase": { + "command": "npx", + "args": ["@browserbasehq/mcp"], + "env": { + "BROWSERBASE_API_KEY": "your-browserbase-key", + "BROWSERBASE_PROJECT_ID": "your-project-id" + "GEMINI_API_KEY": "your-gemini-api-key" + } + } + } + } + +You can get a Gemini API key from: https://aistudio.google.com/app/apikey + +3. Or choose another supported model: + Available models: https://docs.stagehand.dev/examples/custom_llms#llm-customization + + { + "mcpServers": { + "browserbase": { + "command": "npx", + "args": [ + "@browserbasehq/mcp", + "--modelName", "available-model", + "--modelApiKey", "your-api-key", + ], + "env": { + "BROWSERBASE_API_KEY": "your-browserbase-key", + "BROWSERBASE_PROJECT_ID": "your-project-id" + } + } + } + } + +The server will now attempt to start, but will likely fail without the API key... +`); + } + } + + const server = await serverList.create(); + await server.connect(new StdioServerTransport()); +} + +async function handleStreamable( + req: http.IncomingMessage, + res: http.ServerResponse, + serverList: ServerList, + sessions: Map, +) { + const sessionId = req.headers["mcp-session-id"] as string | undefined; + if (sessionId) { + const transport = sessions.get(sessionId); + if (!transport) { + res.statusCode = 404; + res.end("Session not found"); + return; + } + return await transport.handleRequest(req, res); + } + + if (req.method === "POST") { + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => crypto.randomUUID(), + onsessioninitialized: (sessionId) => { + sessions.set(sessionId, transport); + }, + }); + transport.onclose = () => { + if (transport.sessionId) sessions.delete(transport.sessionId); + }; + const server = await serverList.create(); + await server.connect(transport); + return await transport.handleRequest(req, res); + } + + res.statusCode = 400; + res.end("Invalid request"); +} + +export function startHttpTransport( + port: number, + hostname: string | undefined, + serverList: ServerList, +) { + const streamableSessions = new Map(); + const httpServer = http.createServer(async (req, res) => { + if (!req.url) { + res.statusCode = 400; + res.end("Bad request: missing URL"); + return; + } + const url = new URL(`http://localhost${req.url}`); + if (url.pathname.startsWith("/mcp")) + await handleStreamable(req, res, serverList, streamableSessions); + }); + httpServer.listen(port, hostname, () => { + const address = httpServer.address(); + assert(address, "Could not bind server socket"); + let url: string; + if (typeof address === "string") { + url = address; + } else { + const resolvedPort = address.port; + let resolvedHost = + address.family === "IPv4" ? address.address : `[${address.address}]`; + if (resolvedHost === "0.0.0.0" || resolvedHost === "[::]") + resolvedHost = "localhost"; + url = `http://${resolvedHost}:${resolvedPort}`; + } + const message = [ + `Listening on ${url}`, + "Put this in your client config:", + JSON.stringify( + { + mcpServers: { + browserbase: { + url: `${url}/mcp`, + }, + }, + }, + undefined, + 2, + ), + "If your client supports streamable HTTP, you can use the /mcp endpoint instead.", + ].join("\n"); + console.log(message); + }); +} diff --git a/src/types/types.ts b/src/types/types.ts new file mode 100644 index 0000000..bba3bd7 --- /dev/null +++ b/src/types/types.ts @@ -0,0 +1,40 @@ +import type { Stagehand } from "@browserbasehq/stagehand"; +import type { Browser, Page } from "playwright-core"; +import { ImageContent, TextContent } from "@modelcontextprotocol/sdk/types.js"; +import { Tool } from "../tools/tool.js"; +import { InputType } from "../tools/tool.js"; + +export type StagehandSession = { + id: string; // MCP-side ID + stagehand: Stagehand; // owns the Browserbase session + page: Page; + browser: Browser; + created: number; + metadata?: Record; // optional extras (proxy, contextId, bbSessionId) +}; + +export type CreateSessionParams = { + apiKey?: string; + projectId?: string; + modelName?: string; + modelApiKey?: string; + browserbaseSessionID?: string; + browserbaseSessionCreateParams?: any; + meta?: Record; +}; + +export type BrowserSession = { + browser: Browser; + page: Page; + sessionId: string; + stagehand: Stagehand; +}; + +export type ToolActionResult = + | { content?: (ImageContent | TextContent)[] } + | undefined + | void; + +// Type for the tools array used in MCP server registration +export type MCPTool = Tool; +export type MCPToolsArray = MCPTool[]; diff --git a/stagehand/README.md b/stagehand/README.md deleted file mode 100644 index 816d287..0000000 --- a/stagehand/README.md +++ /dev/null @@ -1,192 +0,0 @@ -# Stagehand MCP Server - -![cover](../assets/stagehand-mcp.png) - -A Model Context Protocol (MCP) server that provides AI-powered web automation capabilities using [Stagehand](https://github.com/browserbase/stagehand). This server enables LLMs to interact with web pages, perform actions, extract data, and observe possible actions in a real browser environment. - -## Get Started - -1. Run `npm install` to install the necessary dependencies, then run `npm run build` to get `dist/index.js`. - -2. Set up your Claude Desktop configuration to use the server. - -```json -{ - "mcpServers": { - "stagehand": { - "command": "node", - "args": ["path/to/mcp-server-browserbase/stagehand/dist/index.js"], - "env": { - "BROWSERBASE_API_KEY": "", - "BROWSERBASE_PROJECT_ID": "", - "OPENAI_API_KEY": "", - "CONTEXT_ID": "" - } - } - } -} -``` -or, for running locally, first [**open Chrome in debug mode**](https://docs.stagehand.dev/examples/customize_browser#use-your-personal-browser) like so: - -`open -a "Google Chrome" --args --remote-debugging-port=9222` -```json -{ - "mcpServers": { - "stagehand": { - "command": "node", - "args": ["path/to/mcp-server-browserbase/stagehand/dist/index.js"], - "env": { - "OPENAI_API_KEY": "", - "LOCAL_CDP_URL": "http://localhost:9222" - } - } - } -} -``` -> πŸ’‘ Check out our [documentation](https://docs.stagehand.dev/examples/customize_browser#use-your-personal-browser) for getting your local CDP url! - -3. Restart your Claude Desktop app and you should see the tools available clicking the πŸ”¨ icon. - -4. Start using the tools! Below is a demo video of Claude doing a Google search for OpenAI using stagehand MCP server and Browserbase for a remote headless browser. - - - -## Tools - -### Stagehand commands - -- **stagehand_navigate** - - Navigate to any URL in the browser - - Input: - - `url` (string): The URL to navigate to - -- **stagehand_act** - - Perform an action on the web page - - Inputs: - - `action` (string): The action to perform (e.g., "click the login button") - - `variables` (object, optional): Variables used in the action template - -- **stagehand_extract** - - Extract data from the web page - -- **stagehand_observe** - - Observe actions that can be performed on the web page - - Input: - - `instruction` (string, optional): Instruction for observation - -### Resources - -The server provides access to one resource: - -1. **Console Logs** (`console://logs`) - - - Browser console output in text format - - Includes all console messages from the browser - -2. **Screenshots** (`screenshot://`) - - PNG images of captured screenshots - - Accessible via the screenshot name specified during capture - -## File Structure - -The codebase is organized into the following modules: - -- **index.ts**: Entry point that initializes and runs the server. -- **server.ts**: Core server logic, including server creation, configuration, and request handling. -- **tools.ts**: Definitions and implementations of tools that can be called by MCP clients. -- **prompts.ts**: Prompt templates that can be used by MCP clients. -- **resources.ts**: Resource definitions and handlers for resource-related requests. -- **logging.ts**: Comprehensive logging system with rotation and formatting capabilities. -- **utils.ts**: Utility functions including JSON Schema to Zod schema conversion and message sanitization. - -## Module Descriptions - -### index.ts - -The main entry point for the application. It: -- Initializes the logging system -- Creates the server instance -- Connects to the stdio transport to receive and respond to requests - -### server.ts - -Contains core server functionality: -- Creates and configures the MCP server -- Defines Stagehand configuration -- Sets up request handlers for all MCP operations -- Manages the Stagehand browser instance - -### tools.ts - -Implements the tools that can be called by MCP clients: -- `stagehand_navigate`: Navigate to URLs -- `stagehand_act`: Perform actions on web elements -- `stagehand_extract`: Extract structured data from web pages -- `stagehand_observe`: Observe elements on the page -- `screenshot`: Take screenshots of the current page - -### prompts.ts - -Defines prompt templates for MCP clients: -- `click_search_button`: Template for clicking search buttons - -### resources.ts - -Manages resources in the MCP protocol: -- Currently provides empty resource and resource template lists - -### logging.ts - -Implements a comprehensive logging system: -- File-based logging with rotation -- In-memory operation logs -- Log formatting and sanitization -- Console logging for debugging - -### utils.ts - -Provides utility functions: -- `jsonSchemaToZod`: Converts JSON Schema to Zod schema for validation -- `sanitizeMessage`: Ensures messages are properly formatted JSON - -## Key Features - -- AI-powered web automation -- Perform actions on web pages -- Extract structured data from web pages -- Observe possible actions on web pages -- Simple and extensible API -- Model-agnostic support for various LLM providers - -## Environment Variables - -- `BROWSERBASE_API_KEY`: API key for BrowserBase authentication -- `BROWSERBASE_PROJECT_ID`: Project ID for BrowserBase -- `OPENAI_API_KEY`: API key for OpenAI (used by Stagehand) -- `DEBUG`: Enable debug logging - -## MCP Capabilities - -This server implements the following MCP capabilities: - -- **Tools**: Allows clients to call tools that control a browser instance -- **Prompts**: Provides prompt templates for common operations -- **Resources**: (Currently empty but structured for future expansion) -- **Logging**: Provides detailed logging capabilities - -For more information about the Model Context Protocol, visit: -- [MCP Documentation](https://modelcontextprotocol.io/docs) -- [MCP Specification](https://spec.modelcontextprotocol.io/) - -## License - -Licensed under the MIT License. - -Copyright 2024 Browserbase, Inc. diff --git a/stagehand/package-lock.json b/stagehand/package-lock.json deleted file mode 100644 index e5b1dab..0000000 --- a/stagehand/package-lock.json +++ /dev/null @@ -1,1991 +0,0 @@ -{ - "name": "@browserbasehq/mcp-stagehand", - "version": "0.5.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@browserbasehq/mcp-stagehand", - "version": "0.5.1", - "license": "MIT", - "dependencies": { - "@browserbasehq/sdk": "^2.0.0", - "@browserbasehq/stagehand": "^2.0.0", - "@modelcontextprotocol/sdk": "^1.0.3", - "@modelcontextprotocol/server-stagehand": "file:", - "@playwright/test": "^1.49.0" - }, - "bin": { - "mcp-server-stagehand": "dist/index.js" - }, - "devDependencies": { - "shx": "^0.3.4", - "typescript": "^5.6.2" - } - }, - "node_modules/@anthropic-ai/sdk": { - "version": "0.39.0", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.39.0.tgz", - "integrity": "sha512-eMyDIPRZbt1CCLErRCi3exlAvNkBtRe+kW5vvJyef93PmNr/clstYgHhtvmkxN82nlKgzyGPCyGxrm0JQ1ZIdg==", - "license": "MIT", - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7" - } - }, - "node_modules/@browserbasehq/sdk": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@browserbasehq/sdk/-/sdk-2.5.0.tgz", - "integrity": "sha512-bcnbYZvm5Ht1nrHUfWDK4crspiTy1ESJYMApsMiOTUnlKOan0ocRD6m7hZH34iSC2c2XWsoryR80cwsYgCBWzQ==", - "license": "Apache-2.0", - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7" - } - }, - "node_modules/@browserbasehq/stagehand": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@browserbasehq/stagehand/-/stagehand-2.0.0.tgz", - "integrity": "sha512-zYSuF/mjq3579ezPWlvMkGTJacSFhz4bEkimstgb6V+ychdFocAMwuF9SrTPmJF5dYUK8FR1nbjZ52+w2sRRoQ==", - "license": "MIT", - "dependencies": { - "@anthropic-ai/sdk": "0.39.0", - "@browserbasehq/sdk": "^2.4.0", - "ws": "^8.18.0", - "zod-to-json-schema": "^3.23.5" - }, - "peerDependencies": { - "@playwright/test": "^1.42.1", - "deepmerge": "^4.3.1", - "dotenv": "^16.4.5", - "openai": "^4.87.1", - "pino": "^9.6.0", - "pino-pretty": "^13.0.0", - "zod": "^3.23.8" - } - }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.6.1.tgz", - "integrity": "sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA==", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "cors": "^2.8.5", - "eventsource": "^3.0.2", - "express": "^5.0.1", - "express-rate-limit": "^7.5.0", - "pkce-challenge": "^4.1.0", - "raw-body": "^3.0.0", - "zod": "^3.23.8", - "zod-to-json-schema": "^3.24.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@modelcontextprotocol/server-stagehand": { - "resolved": "", - "link": true - }, - "node_modules/@playwright/test": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.51.0.tgz", - "integrity": "sha512-dJ0dMbZeHhI+wb77+ljx/FeC8VBP6j/rj9OAojO08JI80wTZy6vRk9KvHKiDCUh4iMpEiseMgqRBIeW+eKX6RA==", - "license": "Apache-2.0", - "dependencies": { - "playwright": "1.51.0" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@types/node": { - "version": "18.19.80", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.80.tgz", - "integrity": "sha512-kEWeMwMeIvxYkeg1gTc01awpwLbfMRZXdIhwRcakd/KlK53jmRC26LqcbIt7fnAQTu5GzlnWmzA3H6+l1u6xxQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "license": "MIT", - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/atomic-sleep": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/body-parser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.1.0.tgz", - "integrity": "sha512-/hPxh61E+ll0Ujp24Ilm64cykicul1ypfwjVttduAiEdtnJFvLePSrIPk+HMImtNv5270wOGCb1Tns2rybMkoQ==", - "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.0", - "http-errors": "^2.0.0", - "iconv-lite": "^0.5.2", - "on-finished": "^2.4.1", - "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "license": "MIT", - "peer": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/dateformat": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", - "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", - "license": "MIT", - "peer": true, - "engines": { - "node": "*" - } - }, - "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "license": "BSD-2-Clause", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "license": "MIT", - "peer": true, - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/eventsource": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.5.tgz", - "integrity": "sha512-LT/5J605bx5SNyE+ITBDiM3FxffBiq9un7Vx0EwMDM3vg8sWKx/tO2zC+LMqZ+smAM0F2hblaDZUVZF0te2pSw==", - "license": "MIT", - "dependencies": { - "eventsource-parser": "^3.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/eventsource-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.0.tgz", - "integrity": "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/express": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.0.1.tgz", - "integrity": "sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==", - "license": "MIT", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.0.1", - "content-disposition": "^1.0.0", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "^1.2.1", - "debug": "4.3.6", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "^2.0.0", - "fresh": "2.0.0", - "http-errors": "2.0.0", - "merge-descriptors": "^2.0.0", - "methods": "~1.1.2", - "mime-types": "^3.0.0", - "on-finished": "2.4.1", - "once": "1.4.0", - "parseurl": "~1.3.3", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "router": "^2.0.0", - "safe-buffer": "5.2.1", - "send": "^1.1.0", - "serve-static": "^2.1.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "^2.0.0", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/express-rate-limit": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", - "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==", - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/express-rate-limit" - }, - "peerDependencies": { - "express": "^4.11 || 5 || ^5.0.0-beta.1" - } - }, - "node_modules/fast-copy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", - "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==", - "license": "MIT", - "peer": true - }, - "node_modules/fast-redact": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", - "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "license": "MIT", - "peer": true - }, - "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data-encoder": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", - "license": "MIT" - }, - "node_modules/form-data/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/form-data/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/formdata-node": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", - "license": "MIT", - "dependencies": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" - }, - "engines": { - "node": ">= 12.20" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "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" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/help-me": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", - "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", - "license": "MIT", - "peer": true - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", - "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" - }, - "node_modules/joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-db": { - "version": "1.53.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", - "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.0.tgz", - "integrity": "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.53.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-exit-leak-free": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", - "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/openai": { - "version": "4.92.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.92.0.tgz", - "integrity": "sha512-vLIBP8gygD5M7XIrdBkUFKnfEq3EmaI+lmGjDDAmjahzmdhwdpzDA+GBA4ZZwj7rgu1WMNh9/SqyTysxMulC2g==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7" - }, - "bin": { - "openai": "bin/cli" - }, - "peerDependencies": { - "ws": "^8.18.0", - "zod": "^3.23.8" - }, - "peerDependenciesMeta": { - "ws": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/path-to-regexp": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", - "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", - "license": "MIT", - "engines": { - "node": ">=16" - } - }, - "node_modules/pino": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-9.6.0.tgz", - "integrity": "sha512-i85pKRCt4qMjZ1+L7sy2Ag4t1atFcdbEt76+7iRJn1g2BvsnRMGu9p8pivl9fs63M2kF/A0OacFZhTub+m/qMg==", - "license": "MIT", - "peer": true, - "dependencies": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^2.0.0", - "pino-std-serializers": "^7.0.0", - "process-warning": "^4.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.2.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^4.0.1", - "thread-stream": "^3.0.0" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/pino-abstract-transport": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", - "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", - "license": "MIT", - "peer": true, - "dependencies": { - "split2": "^4.0.0" - } - }, - "node_modules/pino-pretty": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.0.0.tgz", - "integrity": "sha512-cQBBIVG3YajgoUjo1FdKVRX6t9XPxwB9lcNJVD5GCnNM4Y6T12YYx8c6zEejxQsU0wrg9TwmDulcE9LR7qcJqA==", - "license": "MIT", - "peer": true, - "dependencies": { - "colorette": "^2.0.7", - "dateformat": "^4.6.3", - "fast-copy": "^3.0.2", - "fast-safe-stringify": "^2.1.1", - "help-me": "^5.0.0", - "joycon": "^3.1.1", - "minimist": "^1.2.6", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^2.0.0", - "pump": "^3.0.0", - "secure-json-parse": "^2.4.0", - "sonic-boom": "^4.0.1", - "strip-json-comments": "^3.1.1" - }, - "bin": { - "pino-pretty": "bin.js" - } - }, - "node_modules/pino-std-serializers": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", - "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", - "license": "MIT", - "peer": true - }, - "node_modules/pkce-challenge": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-4.1.0.tgz", - "integrity": "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ==", - "license": "MIT", - "engines": { - "node": ">=16.20.0" - } - }, - "node_modules/playwright": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.51.0.tgz", - "integrity": "sha512-442pTfGM0xxfCYxuBa/Pu6B2OqxqqaYq39JS8QDMGThUvIOCd6s0ANDog3uwA0cHavVlnTQzGCN7Id2YekDSXA==", - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.51.0" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.0.tgz", - "integrity": "sha512-x47yPE3Zwhlil7wlNU/iktF7t2r/URR3VLbH6EknJd/04Qc/PSJ0EY3CMXipmglLG+zyRxW6HNo2EGbKLHPWMg==", - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/process-warning": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz", - "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "peer": true - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pump": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", - "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", - "license": "MIT", - "peer": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/quick-format-unescaped": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", - "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", - "license": "MIT", - "peer": true - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.6.3", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/real-require": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", - "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dev": true, - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/router": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.1.0.tgz", - "integrity": "sha512-/m/NSLxeYEgWNtyC+WtNHCF7jbGxOibVWKnn+1Psff4dJGOfoXP+MuC/f2CwSmyiHdOIzYnYFp4W6GxWfekaLA==", - "license": "MIT", - "dependencies": { - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safe-stable-stringify": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", - "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/secure-json-parse": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", - "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", - "license": "BSD-3-Clause", - "peer": true - }, - "node_modules/send": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.1.0.tgz", - "integrity": "sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "destroy": "^1.2.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^0.5.2", - "http-errors": "^2.0.0", - "mime-types": "^2.1.35", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/send/node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/send/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/send/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.1.0.tgz", - "integrity": "sha512-A3We5UfEjG8Z7VkDv6uItWw6HY2bBSBJT1KtVESn6EOoOr2jAxNhxWCLY3jDE2WcuHXByWju74ck3ZgLwL8xmA==", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/shx": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", - "integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.3", - "shelljs": "^0.8.5" - }, - "bin": { - "shx": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/sonic-boom": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", - "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", - "license": "MIT", - "peer": true, - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/thread-stream": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", - "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", - "license": "MIT", - "peer": true, - "dependencies": { - "real-require": "^0.2.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/type-is": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.0.tgz", - "integrity": "sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw==", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typescript": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", - "license": "MIT", - "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 - } - } - }, - "node_modules/zod": { - "version": "3.24.2", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", - "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-to-json-schema": { - "version": "3.24.3", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.3.tgz", - "integrity": "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.24.1" - } - } - } -} diff --git a/stagehand/package.json b/stagehand/package.json deleted file mode 100644 index 9b8f327..0000000 --- a/stagehand/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "@browserbasehq/mcp-stagehand", - "version": "0.5.1", - "description": "MCP server for AI web browser automation using Stagehand", - "license": "MIT", - "author": "Browserbase, Inc. (https://www.browserbase.com/)", - "homepage": "https://modelcontextprotocol.io", - "bugs": "https://github.com/modelcontextprotocol/servers/issues", - "type": "module", - "bin": { - "mcp-server-stagehand": "dist/index.js" - }, - "files": [ - "dist" - ], - "scripts": { - "build": "tsc && shx chmod +x dist/*.js", - "prepare": "npm run build", - "watch": "tsc --watch" - }, - "dependencies": { - "@browserbasehq/sdk": "^2.0.0", - "@browserbasehq/stagehand": "^2.0.0", - "@modelcontextprotocol/sdk": "^1.0.3", - "@modelcontextprotocol/server-stagehand": "file:", - "@playwright/test": "^1.49.0" - }, - "devDependencies": { - "shx": "^0.3.4", - "typescript": "^5.6.2" - } -} diff --git a/stagehand/src/index.ts b/stagehand/src/index.ts deleted file mode 100644 index 74a35fe..0000000 --- a/stagehand/src/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env node - -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createServer } from "./server.js"; -import { ensureLogDirectory, registerExitHandlers, scheduleLogRotation, setupLogRotation } from "./logging.js"; - -// Run setup for logging -ensureLogDirectory(); -setupLogRotation(); -scheduleLogRotation(); -registerExitHandlers(); - -// Run the server -async function runServer() { - const server = createServer(); - const transport = new StdioServerTransport(); - await server.connect(transport); - server.sendLoggingMessage({ - level: "info", - data: "Stagehand MCP server is ready to accept requests", - }); -} - -runServer().catch((error) => { - const errorMsg = error instanceof Error ? error.message : String(error); - console.error(errorMsg); -}); diff --git a/stagehand/src/logging.ts b/stagehand/src/logging.ts deleted file mode 100644 index f51bfa6..0000000 --- a/stagehand/src/logging.ts +++ /dev/null @@ -1,222 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { fileURLToPath } from 'url'; -import type { LogLine } from "@browserbasehq/stagehand"; -import { Server } from "@modelcontextprotocol/sdk/server/index.js"; - -// Get the directory name for the current module -const __dirname = path.dirname(fileURLToPath(import.meta.url)); - -// Configure logging -const LOG_DIR = path.join(__dirname, '../logs'); -const LOG_FILE = path.join(LOG_DIR, `stagehand-${new Date().toISOString().split('T')[0]}.log`); -const MAX_LOG_FILES = 10; // Maximum number of log files to keep -const MAX_LOG_SIZE = 10 * 1024 * 1024; // 10MB max log file size - -// Queue for batching log writes -let logQueue: string[] = []; -let logWriteTimeout: NodeJS.Timeout | null = null; -const LOG_FLUSH_INTERVAL = 1000; // Flush logs every second -const MAX_OPERATION_LOGS = 1000; // Prevent operation logs from growing too large - -// Operation logs stored in memory -export const operationLogs: string[] = []; -export const consoleLogs: string[] = []; - -// Reference to server instance for logging -let serverInstance: Server | undefined; - -// Set server for logging -export function setServerInstance(server: Server) { - serverInstance = server; -} - -// Get server instance for notifications and logging -export function getServerInstance() { - return serverInstance; -} - -// Ensure log directory exists -export function ensureLogDirectory() { - if (!fs.existsSync(LOG_DIR)) { - fs.mkdirSync(LOG_DIR, { recursive: true }); - } -} - -// Setup log rotation management -export function setupLogRotation() { - try { - // Check if current log file exceeds max size - if (fs.existsSync(LOG_FILE) && fs.statSync(LOG_FILE).size > MAX_LOG_SIZE) { - const timestamp = new Date().toISOString().replace(/:/g, '-'); - const rotatedLogFile = path.join(LOG_DIR, `stagehand-${timestamp}.log`); - fs.renameSync(LOG_FILE, rotatedLogFile); - } - - // Clean up old log files if we have too many - const logFiles = fs.readdirSync(LOG_DIR) - .filter(file => file.startsWith('stagehand-') && file.endsWith('.log')) - .map(file => path.join(LOG_DIR, file)) - .sort((a, b) => fs.statSync(b).mtime.getTime() - fs.statSync(a).mtime.getTime()); - - if (logFiles.length > MAX_LOG_FILES) { - logFiles.slice(MAX_LOG_FILES).forEach(file => { - try { - fs.unlinkSync(file); - } catch (err) { - console.error(`Failed to delete old log file ${file}:`, err); - } - }); - } - } catch (err) { - console.error('Error in log rotation:', err); - } -} - -// Flush logs to disk asynchronously -export async function flushLogs() { - if (logQueue.length === 0) return; - - const logsToWrite = logQueue.join('\n') + '\n'; - logQueue = []; - logWriteTimeout = null; - - try { - await fs.promises.appendFile(LOG_FILE, logsToWrite); - - // Check if we need to rotate logs after write - const stats = await fs.promises.stat(LOG_FILE); - if (stats.size > MAX_LOG_SIZE) { - setupLogRotation(); - } - } catch (err) { - console.error('Failed to write logs to file:', err); - // If write fails, try to use sync version as fallback - try { - fs.appendFileSync(LOG_FILE, logsToWrite); - } catch (syncErr) { - console.error('Failed to write logs synchronously:', syncErr); - } - } -} - -// Helper function to convert LogLine to string -export function logLineToString(logLine: LogLine): string { - const timestamp = logLine.timestamp ? new Date(logLine.timestamp).toISOString() : new Date().toISOString(); - const level = logLine.level !== undefined ? - (logLine.level === 0 ? 'DEBUG' : - logLine.level === 1 ? 'INFO' : - logLine.level === 2 ? 'ERROR' : 'UNKNOWN') : 'UNKNOWN'; - return `[${timestamp}] [${level}] ${logLine.message || ''}`; -} - -// Main logging function -export function log(message: string, level: 'info' | 'error' | 'debug' = 'info') { - const timestamp = new Date().toISOString(); - const logMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}`; - - // Manage operation logs with size limit - operationLogs.push(logMessage); - if (operationLogs.length > MAX_OPERATION_LOGS) { - // Keep most recent logs but trim the middle to maintain context - const half = Math.floor(MAX_OPERATION_LOGS / 2); - // Keep first 100 and last (MAX_OPERATION_LOGS - 100) logs - const firstLogs = operationLogs.slice(0, 100); - const lastLogs = operationLogs.slice(operationLogs.length - (MAX_OPERATION_LOGS - 100)); - operationLogs.length = 0; - operationLogs.push(...firstLogs); - operationLogs.push(`[...${operationLogs.length - MAX_OPERATION_LOGS} logs truncated...]`); - operationLogs.push(...lastLogs); - } - - // Queue log for async writing - logQueue.push(logMessage); - - // Setup timer to flush logs if not already scheduled - if (!logWriteTimeout) { - logWriteTimeout = setTimeout(flushLogs, LOG_FLUSH_INTERVAL); - } - - // Console output to stderr for debugging - if (process.env.DEBUG || level === 'error') { - console.error(logMessage); - } - - // Send logging message to client for important events - if (serverInstance && (level === 'info' || level === 'error')) { - serverInstance.sendLoggingMessage({ - level: level, - data: message, - }); - } -} - -// Format logs for response -export function formatLogResponse(logs: string[]): string { - if (logs.length <= 100) { - return logs.join("\n"); - } - - // For very long logs, include first and last parts with truncation notice - const first = logs.slice(0, 50); - const last = logs.slice(-50); - return [ - ...first, - `\n... ${logs.length - 100} more log entries (truncated) ...\n`, - ...last - ].join("\n"); -} - -// Log request -export function logRequest(type: string, params: any) { - const requestLog = { - timestamp: new Date().toISOString(), - type, - params, - }; - log(`REQUEST: ${JSON.stringify(requestLog, null, 2)}`, 'debug'); -} - -// Log response -export function logResponse(type: string, response: any) { - const responseLog = { - timestamp: new Date().toISOString(), - type, - response, - }; - log(`RESPONSE: ${JSON.stringify(responseLog, null, 2)}`, 'debug'); -} - -// Register handlers for process exit -export function registerExitHandlers() { - // Make sure logs are flushed when the process exits - process.on('exit', () => { - if (logQueue.length > 0) { - try { - fs.appendFileSync(LOG_FILE, logQueue.join('\n') + '\n'); - } catch (err) { - console.error('Failed to flush logs on exit:', err); - } - } - }); - - process.on('SIGINT', () => { - // Flush logs and exit - if (logQueue.length > 0) { - try { - fs.appendFileSync(LOG_FILE, logQueue.join('\n') + '\n'); - } catch (err) { - console.error('Failed to flush logs on SIGINT:', err); - } - } - process.exit(0); - }); -} - -// Schedule periodic log rotation -export function scheduleLogRotation() { - // Add log rotation check periodically - setInterval(() => { - setupLogRotation(); - }, 15 * 60 * 1000); // Check every 15 minutes -} \ No newline at end of file diff --git a/stagehand/src/prompts.ts b/stagehand/src/prompts.ts deleted file mode 100644 index e9cf6e0..0000000 --- a/stagehand/src/prompts.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Prompts module for the Stagehand MCP server - * Contains prompts definitions and handlers for prompt-related requests - */ - -// Define the prompts -export const PROMPTS = [ - { - name: "click_search_button", - description: "A prompt template for clicking on a search button", - arguments: [] // No arguments required for this specific prompt - } -]; - -/** - * Get a prompt by name - * @param name The name of the prompt to retrieve - * @returns The prompt definition or throws an error if not found - */ -export function getPrompt(name: string) { - if (name === "click_search_button") { - return { - description: "This prompt provides instructions for clicking on a search button", - messages: [ - { - role: "user", - content: { - type: "text", - text: "Please click on the search button" - } - } - ] - }; - } - - throw new Error(`Invalid prompt name: ${name}`); -} \ No newline at end of file diff --git a/stagehand/src/server.ts b/stagehand/src/server.ts deleted file mode 100644 index b05a7e9..0000000 --- a/stagehand/src/server.ts +++ /dev/null @@ -1,320 +0,0 @@ -import { Server } from "@modelcontextprotocol/sdk/server/index.js"; -import { - CallToolRequestSchema, - ListToolsRequestSchema, - ListResourcesRequestSchema, - ListResourceTemplatesRequestSchema, - ListPromptsRequestSchema, - GetPromptRequestSchema, - ReadResourceRequestSchema, -} from "@modelcontextprotocol/sdk/types.js"; -import { Stagehand } from "@browserbasehq/stagehand"; -import type { ConstructorParams } from "@browserbasehq/stagehand"; - -import { sanitizeMessage } from "./utils.js"; -import { - log, - logRequest, - logResponse, - operationLogs, - setServerInstance, -} from "./logging.js"; -import { TOOLS, handleToolCall } from "./tools.js"; -import { PROMPTS, getPrompt } from "./prompts.js"; -import { - listResources, - listResourceTemplates, - readResource, -} from "./resources.js"; - -// Define Stagehand configuration -export const stagehandConfig: ConstructorParams = { - env: - process.env.BROWSERBASE_API_KEY && process.env.BROWSERBASE_PROJECT_ID - ? "BROWSERBASE" - : "LOCAL", - apiKey: process.env.BROWSERBASE_API_KEY /* API key for authentication */, - projectId: process.env.BROWSERBASE_PROJECT_ID /* Project identifier */, - logger: (message) => - console.error( - logLineToString(message) - ) /* Custom logging function to stderr */, - domSettleTimeoutMs: 30_000 /* Timeout for DOM to settle in milliseconds */, - browserbaseSessionCreateParams: - process.env.BROWSERBASE_API_KEY && process.env.BROWSERBASE_PROJECT_ID - ? { - projectId: process.env.BROWSERBASE_PROJECT_ID!, - browserSettings: process.env.CONTEXT_ID - ? { - context: { - id: process.env.CONTEXT_ID, - persist: true, - }, - } - : undefined, - } - : undefined, - localBrowserLaunchOptions: process.env.LOCAL_CDP_URL - ? { - cdpUrl: process.env.LOCAL_CDP_URL, - } - : undefined, - enableCaching: true /* Enable caching functionality */, - browserbaseSessionID: - undefined /* Session ID for resuming Browserbase sessions */, - modelName: "gpt-4o" /* Name of the model to use */, - modelClientOptions: { - apiKey: process.env.OPENAI_API_KEY, - } /* Configuration options for the model client */, - useAPI: false, -}; - -// Global state -let stagehand: Stagehand | undefined; - -// Ensure Stagehand is initialized -export async function ensureStagehand() { - if ( - stagehandConfig.env === "LOCAL" && - !stagehandConfig.localBrowserLaunchOptions?.cdpUrl - ) { - throw new Error( - 'Using a local browser without providing a CDP URL is not supported. Please provide a CDP URL using the LOCAL_CDP_URL environment variable.\n\nTo launch your browser in "debug", see our documentation.\n\nhttps://docs.stagehand.dev/examples/customize_browser#use-your-personal-browser' - ); - } - - try { - if (!stagehand) { - stagehand = new Stagehand(stagehandConfig); - await stagehand.init(); - return stagehand; - } - - // Try to perform a simple operation to check if the session is still valid - try { - await stagehand.page.evaluate(() => document.title); - return stagehand; - } catch (error) { - // If we get an error indicating the session is invalid, reinitialize - if ( - error instanceof Error && - (error.message.includes( - "Target page, context or browser has been closed" - ) || - error.message.includes("Session expired") || - error.message.includes("context destroyed")) - ) { - log("Browser session expired, reinitializing Stagehand...", "info"); - stagehand = new Stagehand(stagehandConfig); - await stagehand.init(); - return stagehand; - } - throw error; // Re-throw if it's a different type of error - } - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - log(`Failed to initialize/reinitialize Stagehand: ${errorMsg}`, "error"); - throw error; - } -} - -// Create the server -export function createServer() { - const server = new Server( - { - name: "stagehand", - version: "0.1.0", - }, - { - capabilities: { - resources: {}, - tools: {}, - logging: {}, - prompts: {}, - }, - } - ); - - // Store server instance for logging - setServerInstance(server); - - // Setup request handlers - server.setRequestHandler(ListToolsRequestSchema, async (request) => { - try { - logRequest("ListTools", request.params); - const response = { tools: TOOLS }; - const sanitizedResponse = sanitizeMessage(response); - logResponse("ListTools", JSON.parse(sanitizedResponse)); - return JSON.parse(sanitizedResponse); - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - return { - error: { - code: -32603, - message: `Internal error: ${errorMsg}`, - }, - }; - } - }); - - server.setRequestHandler(CallToolRequestSchema, async (request) => { - try { - logRequest("CallTool", request.params); - operationLogs.length = 0; // Clear logs for new operation - - if ( - !request.params?.name || - !TOOLS.find((t) => t.name === request.params.name) - ) { - throw new Error(`Invalid tool name: ${request.params?.name}`); - } - - // Ensure Stagehand is initialized - try { - stagehand = await ensureStagehand(); - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - return { - content: [ - { - type: "text", - text: `Failed to initialize Stagehand: ${errorMsg}.\n\nConfig: ${JSON.stringify( - stagehandConfig, - null, - 2 - )}`, - }, - { - type: "text", - text: `Operation logs:\n${operationLogs.join("\n")}`, - }, - ], - isError: true, - }; - } - - const result = await handleToolCall( - request.params.name, - request.params.arguments ?? {}, - stagehand - ); - - const sanitizedResult = sanitizeMessage(result); - logResponse("CallTool", JSON.parse(sanitizedResult)); - return JSON.parse(sanitizedResult); - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - return { - error: { - code: -32603, - message: `Internal error: ${errorMsg}`, - }, - }; - } - }); - - server.setRequestHandler(ListResourcesRequestSchema, async (request) => { - try { - logRequest("ListResources", request.params); - const response = listResources(); - const sanitizedResponse = sanitizeMessage(response); - logResponse("ListResources", JSON.parse(sanitizedResponse)); - return JSON.parse(sanitizedResponse); - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - return { - error: { - code: -32603, - message: `Internal error: ${errorMsg}`, - }, - }; - } - }); - - server.setRequestHandler( - ListResourceTemplatesRequestSchema, - async (request) => { - try { - logRequest("ListResourceTemplates", request.params); - const response = listResourceTemplates(); - const sanitizedResponse = sanitizeMessage(response); - logResponse("ListResourceTemplates", JSON.parse(sanitizedResponse)); - return JSON.parse(sanitizedResponse); - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - return { - error: { - code: -32603, - message: `Internal error: ${errorMsg}`, - }, - }; - } - } - ); - - server.setRequestHandler(ReadResourceRequestSchema, async (request) => { - try { - logRequest("ReadResource", request.params); - const uri = request.params.uri.toString(); - const response = readResource(uri); - const sanitizedResponse = sanitizeMessage(response); - logResponse("ReadResource", JSON.parse(sanitizedResponse)); - return JSON.parse(sanitizedResponse); - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - return { - error: { - code: -32603, - message: `Internal error: ${errorMsg}`, - }, - }; - } - }); - - server.setRequestHandler(ListPromptsRequestSchema, async (request) => { - try { - logRequest("ListPrompts", request.params); - const response = { prompts: PROMPTS }; - const sanitizedResponse = sanitizeMessage(response); - logResponse("ListPrompts", JSON.parse(sanitizedResponse)); - return JSON.parse(sanitizedResponse); - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - return { - error: { - code: -32603, - message: `Internal error: ${errorMsg}`, - }, - }; - } - }); - - server.setRequestHandler(GetPromptRequestSchema, async (request) => { - try { - logRequest("GetPrompt", request.params); - - // Check if prompt name is valid and get the prompt - try { - const prompt = getPrompt(request.params?.name || ""); - const sanitizedResponse = sanitizeMessage(prompt); - logResponse("GetPrompt", JSON.parse(sanitizedResponse)); - return JSON.parse(sanitizedResponse); - } catch (error) { - throw new Error(`Invalid prompt name: ${request.params?.name}`); - } - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - return { - error: { - code: -32603, - message: `Internal error: ${errorMsg}`, - }, - }; - } - }); - - return server; -} - -// Import missing function from logging -import { formatLogResponse, logLineToString } from "./logging.js"; diff --git a/stagehand/src/tools.ts b/stagehand/src/tools.ts deleted file mode 100644 index e04a39e..0000000 --- a/stagehand/src/tools.ts +++ /dev/null @@ -1,303 +0,0 @@ -import { Stagehand } from "@browserbasehq/stagehand"; -import { CallToolResult, Tool } from "@modelcontextprotocol/sdk/types.js"; -import { getServerInstance, operationLogs } from "./logging.js"; -import { screenshots } from "./resources.js"; - -// Define the Stagehand tools -export const TOOLS: Tool[] = [ - { - name: "stagehand_navigate", - description: - "Navigate to a URL in the browser. Only use this tool with URLs you're confident will work and stay up to date. Otheriwse use https://google.com as the starting point", - inputSchema: { - type: "object", - properties: { - url: { type: "string", description: "The URL to navigate to" }, - }, - required: ["url"], - }, - }, - { - name: "stagehand_act", - description: `Performs an action on a web page element. Act actions should be as atomic and - specific as possible, i.e. "Click the sign in button" or "Type 'hello' into the search input". - AVOID actions that are more than one step, i.e. "Order me pizza" or "Send an email to Paul - asking him to call me". `, - inputSchema: { - type: "object", - properties: { - action: { - type: "string", - description: `The action to perform. Should be as atomic and specific as possible, - i.e. 'Click the sign in button' or 'Type 'hello' into the search input'. AVOID actions that are more than one - step, i.e. 'Order me pizza' or 'Send an email to Paul asking him to call me'. The instruction should be just as specific as possible, - and have a strong correlation to the text on the page. If unsure, use observe before using act."`, - }, - variables: { - type: "object", - additionalProperties: true, - description: `Variables used in the action template. ONLY use variables if you're dealing - with sensitive data or dynamic content. For example, if you're logging in to a website, - you can use a variable for the password. When using variables, you MUST have the variable - key in the action template. For example: {"action": "Fill in the password", "variables": {"password": "123456"}}`, - }, - }, - required: ["action"], - }, - }, - { - name: "stagehand_extract", - description: `Extracts all of the text from the current page.`, - inputSchema: { - type: "object", - properties: {}, - }, - }, - { - name: "stagehand_observe", - description: - "Observes elements on the web page. Use this tool to observe elements that you can later use in an action. Use observe instead of extract when dealing with actionable (interactable) elements rather than text. More often than not, you'll want to use extract instead of observe when dealing with scraping or extracting structured text.", - inputSchema: { - type: "object", - properties: { - instruction: { - type: "string", - description: - "Instruction for observation (e.g., 'find the login button'). This instruction must be extremely specific.", - }, - }, - required: ["instruction"], - }, - }, - { - name: "screenshot", - description: - "Takes a screenshot of the current page. Use this tool to learn where you are on the page when controlling the browser with Stagehand. Only use this tool when the other tools are not sufficient to get the information you need.", - inputSchema: { - type: "object", - properties: {}, - }, - }, -]; - -// Handle tool calls -export async function handleToolCall( - name: string, - args: any, - stagehand: Stagehand -): Promise { - switch (name) { - case "stagehand_navigate": - try { - await stagehand.page.goto(args.url); - return { - content: [ - { - type: "text", - text: `Navigated to: ${args.url}`, - }, - { - type: "text", - text: `View the live session here: https://browserbase.com/sessions/${stagehand.browserbaseSessionID}`, - }, - ], - isError: false, - }; - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - return { - content: [ - { - type: "text", - text: `Failed to navigate: ${errorMsg}`, - }, - { - type: "text", - text: `Operation logs:\n${operationLogs.join("\n")}`, - }, - ], - isError: true, - }; - } - - case "stagehand_act": - try { - await stagehand.page.act({ - action: args.action, - variables: args.variables, - }); - return { - content: [ - { - type: "text", - text: `Action performed: ${args.action}`, - }, - ], - isError: false, - }; - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - return { - content: [ - { - type: "text", - text: `Failed to perform action: ${errorMsg}`, - }, - { - type: "text", - text: `Operation logs:\n${operationLogs.join("\n")}`, - }, - ], - isError: true, - }; - } - - case "stagehand_extract": { - try { - const bodyText = await stagehand.page.evaluate( - () => document.body.innerText - ); - const content = bodyText - .split("\n") - .map((line) => line.trim()) - .filter((line) => { - if (!line) return false; - - if ( - (line.includes("{") && line.includes("}")) || - line.includes("@keyframes") || // Remove CSS animations - line.match(/^\.[a-zA-Z0-9_-]+\s*{/) || // Remove CSS lines starting with .className { - line.match(/^[a-zA-Z-]+:[a-zA-Z0-9%\s\(\)\.,-]+;$/) // Remove lines like "color: blue;" or "margin: 10px;" - ) { - return false; - } - return true; - }) - .map((line) => { - return line.replace(/\\u([0-9a-fA-F]{4})/g, (_, hex) => - String.fromCharCode(parseInt(hex, 16)) - ); - }); - - return { - content: [ - { - type: "text", - text: `Extracted content:\n${content.join("\n")}`, - }, - ], - isError: false, - }; - } catch (error) { - return { - content: [ - { - type: "text", - text: `Failed to extract content: ${(error as Error).message}`, - }, - ], - isError: true, - }; - } - } - - case "stagehand_observe": - try { - const observations = await stagehand.page.observe({ - instruction: args.instruction, - returnAction: false, - }); - return { - content: [ - { - type: "text", - text: `Observations: ${JSON.stringify(observations)}`, - }, - ], - isError: false, - }; - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - return { - content: [ - { - type: "text", - text: `Failed to observe: ${errorMsg}`, - }, - { - type: "text", - text: `Operation logs:\n${operationLogs.join("\n")}`, - }, - ], - isError: true, - }; - } - - case "screenshot": - try { - const screenshotBuffer = await stagehand.page.screenshot({ - fullPage: false, - }); - - // Convert buffer to base64 string and store in memory - const screenshotBase64 = screenshotBuffer.toString("base64"); - const name = `screenshot-${new Date() - .toISOString() - .replace(/:/g, "-")}`; - screenshots.set(name, screenshotBase64); - - // Notify the client that the resources changed - const serverInstance = getServerInstance(); - if (serverInstance) { - serverInstance.notification({ - method: "notifications/resources/list_changed", - }); - } - - return { - content: [ - { - type: "text", - text: `Screenshot taken with name: ${name}`, - }, - { - type: "image", - data: screenshotBase64, - mimeType: "image/png", - }, - ], - isError: false, - }; - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - return { - content: [ - { - type: "text", - text: `Failed to take screenshot: ${errorMsg}`, - }, - { - type: "text", - text: `Operation logs:\n${operationLogs.join("\n")}`, - }, - ], - isError: true, - }; - } - - default: - return { - content: [ - { - type: "text", - text: `Unknown tool: ${name}`, - }, - { - type: "text", - text: `Operation logs:\n${operationLogs.join("\n")}`, - }, - ], - isError: true, - }; - } -} diff --git a/stagehand/src/utils.ts b/stagehand/src/utils.ts deleted file mode 100644 index 47f5036..0000000 --- a/stagehand/src/utils.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Sanitizes a message to ensure it's properly formatted JSON - * @param message The message to sanitize - * @returns A sanitized JSON string - */ -export function sanitizeMessage(message: any): string { - try { - // Ensure the message is properly stringified JSON - if (typeof message === 'string') { - JSON.parse(message); // Validate JSON structure - return message; - } - return JSON.stringify(message); - } catch (error) { - return JSON.stringify({ - jsonrpc: '2.0', - error: { - code: -32700, - message: 'Parse error', - }, - id: null, - }); - } -} \ No newline at end of file diff --git a/tests/.gitkeep b/tests/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/stagehand/tsconfig.json b/tsconfig.json similarity index 86% rename from stagehand/tsconfig.json rename to tsconfig.json index 316a446..7177886 100644 --- a/stagehand/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,8 @@ "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "outDir": "dist", - "rootDir": "src" + "rootDir": "src", + "noErrorTruncation": false }, "include": ["src/**/*.ts"], "exclude": ["node_modules"]