skills & mcp

This commit is contained in:
ropzislaw
2026-03-02 00:20:53 +08:00
parent 2c55f9f317
commit 0ffd6b1bde
17 changed files with 2755 additions and 2 deletions

View File

@@ -60,6 +60,68 @@ AIPex ist die Antwort. Installieren Sie die Erweiterung, bringen Sie Ihren eigen
---
## Verwendung mit KI-Coding-Agenten (MCP)
AIPex unterstützt jetzt das [Model Context Protocol (MCP)](https://modelcontextprotocol.io), sodass KI-Agenten wie Cursor, Claude Code und VS Code Copilot Ihren Browser direkt steuern können.
```
AI Agent ──stdio──▶ aipex-mcp-bridge ──WebSocket──▶ AIPex Extension ──▶ Browser
```
### Schritt 1: Agenten konfigurieren
**Cursor** (`.cursor/mcp.json`) · **Claude Desktop** (`claude_desktop_config.json`) · **Windsurf** (`mcp_config.json`):
```json
{
"mcpServers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
**Claude Code**:
```bash
claude mcp add aipex-browser -- npx -y aipex-mcp-bridge
```
**VS Code Copilot** (`.vscode/mcp.json`):
```json
{
"servers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
### Schritt 2: Erweiterung verbinden
1. Chrome öffnen → AIPex-Symbol → **Optionen**
2. WebSocket-URL auf `ws://localhost:9223` setzen
3. Auf **Verbinden** klicken
Ihr Agent verfügt jetzt über 30+ Browser-Automatisierungstools via MCP. Siehe [mcp-bridge/README.md](mcp-bridge/README.md) für erweiterte Optionen.
---
## Skill
AIPex liefert ein **`aipex-browser`**-Skill — ein sofort einsatzbereites Skill-Paket für Agenten, die das Skill-Protokoll unterstützen (wie [Claude Code](https://claude.ai/code) und [OpenClaw](https://openclaw.dev)-kompatible Runtimes).
Das Skill enthält eine Werkzeugstrategie, vollständige Parameterschemata für alle 30+ Browser-Tools und gängige Automatisierungsmuster — sodass der Agent den Browser sofort effizient steuern kann.
Siehe [`skill/SKILL.md`](skill/SKILL.md) für die vollständige Definition.
---
## Demos
### "Ich habe 100 Tabs offen. Hilfe."

View File

@@ -60,6 +60,68 @@ AIPex es la respuesta. Instala la extensión, trae tu propia clave API y automat
---
## Uso con agentes de IA (MCP)
AIPex ahora soporta el [Model Context Protocol (MCP)](https://modelcontextprotocol.io), permitiendo que agentes de IA como Cursor, Claude Code y VS Code Copilot controlen tu navegador directamente.
```
AI Agent ──stdio──▶ aipex-mcp-bridge ──WebSocket──▶ AIPex Extension ──▶ Browser
```
### Paso 1: Configura tu agente
**Cursor** (`.cursor/mcp.json`) · **Claude Desktop** (`claude_desktop_config.json`) · **Windsurf** (`mcp_config.json`):
```json
{
"mcpServers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
**Claude Code**:
```bash
claude mcp add aipex-browser -- npx -y aipex-mcp-bridge
```
**VS Code Copilot** (`.vscode/mcp.json`):
```json
{
"servers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
### Paso 2: Conecta la extensión
1. Abre Chrome → icono de AIPex → **Opciones**
2. Establece la URL de WebSocket en `ws://localhost:9223`
3. Haz clic en **Conectar**
Tu agente ahora tiene 30+ herramientas de automatización de navegador disponibles via MCP. Ver [mcp-bridge/README.md](mcp-bridge/README.md) para opciones avanzadas.
---
## Skill
AIPex incluye un skill **`aipex-browser`** — un paquete listo para usar para agentes que soporten el protocolo de skill (como [Claude Code](https://claude.ai/code) y runtimes compatibles con [OpenClaw](https://openclaw.dev)).
El skill incluye estrategia de uso de herramientas, esquemas completos de parámetros para las 30+ herramientas de navegador y patrones de automatización comunes — permitiendo al agente controlar el navegador eficazmente sin exploración previa.
Ver [`skill/SKILL.md`](skill/SKILL.md) para la definición completa.
---
## Demos
### "Tengo 100 pestañas abiertas. Ayuda."

View File

@@ -60,6 +60,68 @@ AIPex est la réponse. Installez l'extension, apportez votre propre clé API et
---
## Utilisation avec des agents IA (MCP)
AIPex prend désormais en charge le [Model Context Protocol (MCP)](https://modelcontextprotocol.io), permettant aux agents IA comme Cursor, Claude Code et VS Code Copilot de contrôler votre navigateur directement.
```
AI Agent ──stdio──▶ aipex-mcp-bridge ──WebSocket──▶ AIPex Extension ──▶ Browser
```
### Étape 1 : Configurer votre agent
**Cursor** (`.cursor/mcp.json`) · **Claude Desktop** (`claude_desktop_config.json`) · **Windsurf** (`mcp_config.json`) :
```json
{
"mcpServers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
**Claude Code** :
```bash
claude mcp add aipex-browser -- npx -y aipex-mcp-bridge
```
**VS Code Copilot** (`.vscode/mcp.json`) :
```json
{
"servers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
### Étape 2 : Connecter l'extension
1. Ouvrez Chrome → icône AIPex → **Options**
2. Définissez l'URL WebSocket sur `ws://localhost:9223`
3. Cliquez sur **Connecter**
Votre agent dispose maintenant de 30+ outils d'automatisation de navigateur via MCP. Voir [mcp-bridge/README.md](mcp-bridge/README.md) pour les options avancées.
---
## Skill
AIPex fournit un skill **`aipex-browser`** — un package prêt à l'emploi pour les agents supportant le protocole de skill (comme [Claude Code](https://claude.ai/code) et les runtimes compatibles [OpenClaw](https://openclaw.dev)).
Le skill intègre une stratégie d'utilisation des outils, les schémas complets des paramètres des 30+ outils de navigateur et des patterns d'automatisation courants — permettant à l'agent de contrôler le navigateur efficacement sans exploration préalable.
Voir [`skill/SKILL.md`](skill/SKILL.md) pour la définition complète.
---
## Démos
### "J'ai 100 onglets ouverts. À l'aide."

View File

@@ -60,6 +60,68 @@ AIPexがその答えです。拡張機能をインストールし、独自のAPI
---
## AIエージェントとの連携MCP
AIPexは[Model Context ProtocolMCP](https://modelcontextprotocol.io)をサポートし、Cursor・Claude Code・VS Code CopilotなどのAIエージェントがブラウザを直接操作できるようになりました。
```
AI Agent ──stdio──▶ aipex-mcp-bridge ──WebSocket──▶ AIPex Extension ──▶ Browser
```
### ステップ1エージェントを設定する
**Cursor** (`.cursor/mcp.json`) · **Claude Desktop** (`claude_desktop_config.json`) · **Windsurf** (`mcp_config.json`):
```json
{
"mcpServers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
**Claude Code**:
```bash
claude mcp add aipex-browser -- npx -y aipex-mcp-bridge
```
**VS Code Copilot** (`.vscode/mcp.json`):
```json
{
"servers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
### ステップ2拡張機能を接続する
1. Chrome → AIPexアイコン → **オプション**を開く
2. WebSocket URLを `ws://localhost:9223` に設定
3. **接続**をクリック
これでエージェントはMCP経由で30以上のブラウザ自動化ツールを使用できます。詳細は [mcp-bridge/README.md](mcp-bridge/README.md) を参照してください。
---
## スキルSkill
AIPexは**`aipex-browser`**スキルを提供します。[Claude Code](https://claude.ai/code)や[OpenClaw](https://openclaw.dev)互換ランタイムなど、スキルプロトコルに対応したエージェントがすぐに使えるブラウザ自動化スキルパッケージです。
スキルには、ツール使用戦略・30以上のブラウザツールの完全なパラメータスキーマ・よく使われるパターンが含まれており、エージェントが試行錯誤なしにブラウザを効率よく操作できます。
詳細は [`skill/SKILL.md`](skill/SKILL.md) をご参照ください。
---
## デモ
### "100個のタブが開いています。助けて。"

View File

@@ -60,6 +60,68 @@ AIPex가 그 대답입니다. 확장 프로그램을 설치하고, 자신의 API
---
## AI 에이전트와 연동 (MCP)
AIPex는 이제 [Model Context Protocol(MCP)](https://modelcontextprotocol.io)을 지원하여 Cursor, Claude Code, VS Code Copilot 등 AI 에이전트가 브라우저를 직접 제어할 수 있습니다.
```
AI Agent ──stdio──▶ aipex-mcp-bridge ──WebSocket──▶ AIPex Extension ──▶ Browser
```
### 1단계: 에이전트 설정
**Cursor** (`.cursor/mcp.json`) · **Claude Desktop** (`claude_desktop_config.json`) · **Windsurf** (`mcp_config.json`):
```json
{
"mcpServers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
**Claude Code**:
```bash
claude mcp add aipex-browser -- npx -y aipex-mcp-bridge
```
**VS Code Copilot** (`.vscode/mcp.json`):
```json
{
"servers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
### 2단계: 확장 프로그램 연결
1. Chrome → AIPex 아이콘 → **옵션** 열기
2. WebSocket URL을 `ws://localhost:9223`으로 설정
3. **연결** 클릭
이제 에이전트는 MCP를 통해 30개 이상의 브라우저 자동화 도구를 사용할 수 있습니다. 자세한 설정은 [mcp-bridge/README.md](mcp-bridge/README.md)를 참고하세요.
---
## 스킬 (Skill)
AIPex는 **`aipex-browser`** 스킬을 제공합니다. [Claude Code](https://claude.ai/code) 및 [OpenClaw](https://openclaw.dev) 호환 런타임 등 스킬 프로토콜을 지원하는 에이전트가 바로 사용할 수 있는 브라우저 자동화 스킬 패키지입니다.
스킬에는 도구 사용 전략, 30개 이상의 브라우저 도구 파라미터 스키마, 자주 쓰이는 자동화 패턴이 포함되어 있어 에이전트가 시행착오 없이 효율적으로 브라우저를 제어할 수 있습니다.
자세한 내용은 [`skill/SKILL.md`](skill/SKILL.md)를 참고하세요.
---
## 데모
### "탭이 100개 열려 있습니다. 도와주세요."

View File

@@ -61,6 +61,68 @@ AIPex is the answer. Install the extension, bring your own API key, and automate
---
## Use with AI Coding Agents (MCP)
AIPex now supports the [Model Context Protocol (MCP)](https://modelcontextprotocol.io), giving AI coding agents like Cursor, Claude Code, and VS Code Copilot direct control over your browser.
```
AI Agent ──stdio──▶ aipex-mcp-bridge ──WebSocket──▶ AIPex Extension ──▶ Browser
```
### Step 1: Configure your agent
**Cursor** (`.cursor/mcp.json`) · **Claude Desktop** (`claude_desktop_config.json`) · **Windsurf** (`mcp_config.json`):
```json
{
"mcpServers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
**Claude Code**:
```bash
claude mcp add aipex-browser -- npx -y aipex-mcp-bridge
```
**VS Code Copilot** (`.vscode/mcp.json`):
```json
{
"servers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
### Step 2: Connect the extension
1. Open Chrome → AIPex icon → **Options**
2. Set WebSocket URL to `ws://localhost:9223`
3. Click **Connect**
Your agent now has 30+ browser automation tools available via MCP. See [mcp-bridge/README.md](mcp-bridge/README.md) for advanced options.
---
## Skill
AIPex ships an **`aipex-browser`** skill — a ready-to-use skill package for agents that support the skill protocol (such as [Claude Code](https://claude.ai/code) and [OpenClaw](https://openclaw.dev)-compatible runtimes).
The skill bundles tool usage strategy, complete parameter schemas for all 30+ browser tools, and common automation patterns — so an agent can control the browser effectively without discovering tools from scratch.
See [`skill/SKILL.md`](skill/SKILL.md) for the full skill definition.
---
## Demos
### "I have 100 tabs open. Help."
@@ -102,9 +164,9 @@ https://github.com/user-attachments/assets/ba454715-c759-41df-bf87-e835f76be365
- Integration
- [ ] Cursor
- [x] Cursor
- [ ] Claude Code
- [x] Claude Code
- Skills

View File

@@ -60,6 +60,68 @@ AIPex é a resposta. Instale a extensão, traga sua própria chave de API e auto
---
## Uso com Agentes de IA (MCP)
AIPex agora suporta o [Model Context Protocol (MCP)](https://modelcontextprotocol.io), permitindo que agentes de IA como Cursor, Claude Code e VS Code Copilot controlem seu navegador diretamente.
```
AI Agent ──stdio──▶ aipex-mcp-bridge ──WebSocket──▶ AIPex Extension ──▶ Browser
```
### Passo 1: Configurar seu agente
**Cursor** (`.cursor/mcp.json`) · **Claude Desktop** (`claude_desktop_config.json`) · **Windsurf** (`mcp_config.json`):
```json
{
"mcpServers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
**Claude Code**:
```bash
claude mcp add aipex-browser -- npx -y aipex-mcp-bridge
```
**VS Code Copilot** (`.vscode/mcp.json`):
```json
{
"servers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
### Passo 2: Conectar a extensão
1. Abra o Chrome → ícone AIPex → **Opções**
2. Defina a URL WebSocket como `ws://localhost:9223`
3. Clique em **Conectar**
Seu agente agora tem 30+ ferramentas de automação de navegador disponíveis via MCP. Veja [mcp-bridge/README.md](mcp-bridge/README.md) para opções avançadas.
---
## Skill
AIPex fornece um skill **`aipex-browser`** — um pacote pronto para uso para agentes que suportam o protocolo de skill (como [Claude Code](https://claude.ai/code) e runtimes compatíveis com [OpenClaw](https://openclaw.dev)).
O skill inclui estratégia de uso de ferramentas, esquemas completos de parâmetros para as 30+ ferramentas de navegador e padrões comuns de automação — permitindo ao agente controlar o navegador eficientemente sem exploração prévia.
Veja [`skill/SKILL.md`](skill/SKILL.md) para a definição completa.
---
## Demos
### "Tenho 100 abas abertas. Socorro."

View File

@@ -60,6 +60,68 @@ AIPex — это ответ. Установите расширение, введ
---
## Использование с ИИ-агентами (MCP)
AIPex теперь поддерживает [Model Context Protocol (MCP)](https://modelcontextprotocol.io), позволяя ИИ-агентам, таким как Cursor, Claude Code и VS Code Copilot, напрямую управлять вашим браузером.
```
AI Agent ──stdio──▶ aipex-mcp-bridge ──WebSocket──▶ AIPex Extension ──▶ Browser
```
### Шаг 1: Настройка агента
**Cursor** (`.cursor/mcp.json`) · **Claude Desktop** (`claude_desktop_config.json`) · **Windsurf** (`mcp_config.json`):
```json
{
"mcpServers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
**Claude Code**:
```bash
claude mcp add aipex-browser -- npx -y aipex-mcp-bridge
```
**VS Code Copilot** (`.vscode/mcp.json`):
```json
{
"servers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
### Шаг 2: Подключение расширения
1. Откройте Chrome → иконка AIPex → **Настройки**
2. Установите WebSocket URL: `ws://localhost:9223`
3. Нажмите **Подключить**
Ваш агент теперь имеет доступ к 30+ инструментам автоматизации браузера через MCP. См. [mcp-bridge/README.md](mcp-bridge/README.md) для расширенных настроек.
---
## Skill
AIPex предоставляет skill **`aipex-browser`** — готовый к использованию пакет для агентов, поддерживающих протокол skill (таких как [Claude Code](https://claude.ai/code) и среды выполнения, совместимые с [OpenClaw](https://openclaw.dev)).
Skill включает стратегию использования инструментов, полные схемы параметров для 30+ инструментов управления браузером и типовые паттерны автоматизации — агент может сразу эффективно управлять браузером без предварительного изучения.
Подробнее см. [`skill/SKILL.md`](skill/SKILL.md).
---
## Демонстрации
### "У меня открыто 100 вкладок. Помогите."

View File

@@ -59,6 +59,68 @@ AIPex 就是答案。安装扩展,输入你自己的 API Key然后直接开
---
## 与 AI 编程助手集成MCP
AIPex 现已支持 [模型上下文协议MCP](https://modelcontextprotocol.io),让 Cursor、Claude Code、VS Code Copilot 等 AI 助手能够直接控制你的浏览器。
```
AI Agent ──stdio──▶ aipex-mcp-bridge ──WebSocket──▶ AIPex Extension ──▶ Browser
```
### 第一步:配置你的 AI 工具
**Cursor** (`.cursor/mcp.json`) · **Claude Desktop** (`claude_desktop_config.json`) · **Windsurf** (`mcp_config.json`):
```json
{
"mcpServers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
**Claude Code**:
```bash
claude mcp add aipex-browser -- npx -y aipex-mcp-bridge
```
**VS Code Copilot** (`.vscode/mcp.json`):
```json
{
"servers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
### 第二步:连接扩展程序
1. 打开 Chrome → 点击 AIPex 图标 → **选项**
2. 将 WebSocket URL 设置为 `ws://localhost:9223`
3. 点击 **连接**
你的 AI 助手现在可以通过 MCP 使用 30+ 个浏览器自动化工具。更多配置详见 [mcp-bridge/README.md](mcp-bridge/README.md)。
---
## Skill
AIPex 提供了一个 **`aipex-browser`** skill —— 供支持 skill 协议的 agent如 [Claude Code](https://claude.ai/code) 及兼容 [OpenClaw](https://openclaw.dev) 的运行时)使用的即开即用自动化技能包。
该 skill 内置了工具使用策略、所有 30+ 浏览器工具的完整参数 schema以及常用自动化模式让 agent 无需自行摸索即可高效控制浏览器。
详见 [`skill/SKILL.md`](skill/SKILL.md)。
---
## 演示
### "我开了100个标签页救命"

119
mcp-bridge/README.md Normal file
View File

@@ -0,0 +1,119 @@
# aipex-mcp-bridge
MCP bridge that connects AI agents to the [AIPex](https://aipex.ai) browser extension via WebSocket.
Works with **any** MCP client that supports stdio transport — Cursor, Claude Desktop, Claude Code, VS Code Copilot, Windsurf, Zed, and more.
## How it works
```
AI Agent (MCP client) ──stdio──▶ aipex-mcp-bridge ──WebSocket──▶ AIPex Chrome Extension
```
The bridge starts a WebSocket server on `localhost:9223` (configurable) and communicates with your AI agent over stdio using the MCP protocol. The AIPex extension connects to the WebSocket server to expose browser control tools.
## Quick start
### 1. Configure your AI agent
Add the following to your agent's MCP configuration:
**Cursor** (`.cursor/mcp.json`):
```json
{
"mcpServers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
**Claude Desktop** (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
**Claude Code**:
```bash
claude mcp add aipex-browser -- npx -y aipex-mcp-bridge
```
**VS Code Copilot** (`.vscode/mcp.json`):
```json
{
"servers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
**Windsurf** (`mcp_config.json`):
```json
{
"mcpServers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
### 2. Connect AIPex extension
1. Open Chrome → AIPex extension → Options page
2. Set WebSocket URL to `ws://localhost:9223`
3. Click **Connect**
Your AI agent can now control the browser through AIPex.
## Options
```
npx aipex-mcp-bridge [--port <port>]
```
| Option | Default | Description |
| ----------------- | ------- | ---------------------------------- |
| `--port <port>` | `9223` | WebSocket port for AIPex extension |
| `--help`, `-h` | | Show help message |
| `--version`, `-v` | | Show version |
### Custom port example
```json
{
"mcpServers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge", "--port", "8080"]
}
}
}
```
## Requirements
- Node.js >= 18
- AIPex Chrome extension installed
## License
MIT

47
mcp-bridge/package.json Normal file
View File

@@ -0,0 +1,47 @@
{
"name": "aipex-mcp-bridge",
"version": "1.0.0",
"description": "MCP bridge that connects AI agents (Cursor, Claude, VS Code Copilot, etc.) to the AIPex browser extension via WebSocket",
"type": "module",
"bin": {
"aipex-mcp-bridge": "./dist/bridge.js"
},
"files": [
"dist",
"README.md"
],
"scripts": {
"build": "tsup",
"dev": "tsx src/bridge.ts"
},
"dependencies": {
"ws": "^8.18.0"
},
"devDependencies": {
"@types/ws": "^8.5.13",
"@types/node": "^22.0.0",
"tsup": "^8.0.0",
"tsx": "^4.21.0",
"typescript": "^5.3.0"
},
"keywords": [
"mcp",
"model-context-protocol",
"aipex",
"browser",
"cursor",
"claude",
"copilot",
"websocket",
"bridge"
],
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/AIPexStudio/aipex-whole.git",
"directory": "aipex/mcp-bridge"
},
"engines": {
"node": ">=18.0.0"
}
}

969
mcp-bridge/pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,969 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
ws:
specifier: ^8.18.0
version: 8.19.0
devDependencies:
'@types/node':
specifier: ^22.0.0
version: 22.19.13
'@types/ws':
specifier: ^8.5.13
version: 8.18.1
tsup:
specifier: ^8.0.0
version: 8.5.1(tsx@4.21.0)(typescript@5.9.3)
tsx:
specifier: ^4.21.0
version: 4.21.0
typescript:
specifier: ^5.3.0
version: 5.9.3
packages:
'@esbuild/aix-ppc64@0.27.3':
resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.27.3':
resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.27.3':
resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.27.3':
resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.27.3':
resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.27.3':
resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.27.3':
resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.27.3':
resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.27.3':
resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.27.3':
resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.27.3':
resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.27.3':
resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.27.3':
resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.27.3':
resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.27.3':
resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.27.3':
resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.27.3':
resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
'@esbuild/netbsd-arm64@0.27.3':
resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-x64@0.27.3':
resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
'@esbuild/openbsd-arm64@0.27.3':
resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.27.3':
resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
'@esbuild/openharmony-arm64@0.27.3':
resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
'@esbuild/sunos-x64@0.27.3':
resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.27.3':
resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.27.3':
resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.27.3':
resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
'@jridgewell/gen-mapping@0.3.13':
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
'@jridgewell/resolve-uri@3.1.2':
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
engines: {node: '>=6.0.0'}
'@jridgewell/sourcemap-codec@1.5.5':
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
'@rollup/rollup-android-arm-eabi@4.59.0':
resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==}
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm64@4.59.0':
resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==}
cpu: [arm64]
os: [android]
'@rollup/rollup-darwin-arm64@4.59.0':
resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==}
cpu: [arm64]
os: [darwin]
'@rollup/rollup-darwin-x64@4.59.0':
resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==}
cpu: [x64]
os: [darwin]
'@rollup/rollup-freebsd-arm64@4.59.0':
resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==}
cpu: [arm64]
os: [freebsd]
'@rollup/rollup-freebsd-x64@4.59.0':
resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==}
cpu: [x64]
os: [freebsd]
'@rollup/rollup-linux-arm-gnueabihf@4.59.0':
resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm-musleabihf@4.59.0':
resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm64-gnu@4.59.0':
resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-arm64-musl@4.59.0':
resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-loong64-gnu@4.59.0':
resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==}
cpu: [loong64]
os: [linux]
'@rollup/rollup-linux-loong64-musl@4.59.0':
resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==}
cpu: [loong64]
os: [linux]
'@rollup/rollup-linux-ppc64-gnu@4.59.0':
resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-ppc64-musl@4.59.0':
resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-riscv64-gnu@4.59.0':
resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-riscv64-musl@4.59.0':
resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-s390x-gnu@4.59.0':
resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==}
cpu: [s390x]
os: [linux]
'@rollup/rollup-linux-x64-gnu@4.59.0':
resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==}
cpu: [x64]
os: [linux]
'@rollup/rollup-linux-x64-musl@4.59.0':
resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==}
cpu: [x64]
os: [linux]
'@rollup/rollup-openbsd-x64@4.59.0':
resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==}
cpu: [x64]
os: [openbsd]
'@rollup/rollup-openharmony-arm64@4.59.0':
resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==}
cpu: [arm64]
os: [openharmony]
'@rollup/rollup-win32-arm64-msvc@4.59.0':
resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==}
cpu: [arm64]
os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.59.0':
resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==}
cpu: [ia32]
os: [win32]
'@rollup/rollup-win32-x64-gnu@4.59.0':
resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==}
cpu: [x64]
os: [win32]
'@rollup/rollup-win32-x64-msvc@4.59.0':
resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==}
cpu: [x64]
os: [win32]
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
'@types/node@22.19.13':
resolution: {integrity: sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw==}
'@types/ws@8.18.1':
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
acorn@8.16.0:
resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
engines: {node: '>=0.4.0'}
hasBin: true
any-promise@1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
bundle-require@5.1.0:
resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
peerDependencies:
esbuild: '>=0.18'
cac@6.7.14:
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
engines: {node: '>=8'}
chokidar@4.0.3:
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
engines: {node: '>= 14.16.0'}
commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
confbox@0.1.8:
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
consola@3.4.2:
resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}
engines: {node: ^14.18.0 || >=16.10.0}
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
esbuild@0.27.3:
resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==}
engines: {node: '>=18'}
hasBin: true
fdir@6.5.0:
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
engines: {node: '>=12.0.0'}
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
fix-dts-default-cjs-exports@1.0.1:
resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==}
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
get-tsconfig@4.13.6:
resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==}
joycon@3.1.1:
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
engines: {node: '>=10'}
lilconfig@3.1.3:
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
engines: {node: '>=14'}
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
load-tsconfig@0.2.5:
resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
mlly@1.8.0:
resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
mz@2.7.0:
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
pathe@2.0.3:
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
picomatch@4.0.3:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
pirates@4.0.7:
resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
engines: {node: '>= 6'}
pkg-types@1.3.1:
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
postcss-load-config@6.0.1:
resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}
engines: {node: '>= 18'}
peerDependencies:
jiti: '>=1.21.0'
postcss: '>=8.0.9'
tsx: ^4.8.1
yaml: ^2.4.2
peerDependenciesMeta:
jiti:
optional: true
postcss:
optional: true
tsx:
optional: true
yaml:
optional: true
readdirp@4.1.2:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
resolve-from@5.0.0:
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
engines: {node: '>=8'}
resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
rollup@4.59.0:
resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
source-map@0.7.6:
resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}
engines: {node: '>= 12'}
sucrase@3.35.1:
resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==}
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
thenify-all@1.6.0:
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
engines: {node: '>=0.8'}
thenify@3.3.1:
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
tinyexec@0.3.2:
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
tinyglobby@0.2.15:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
tree-kill@1.2.2:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
tsup@8.5.1:
resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==}
engines: {node: '>=18'}
hasBin: true
peerDependencies:
'@microsoft/api-extractor': ^7.36.0
'@swc/core': ^1
postcss: ^8.4.12
typescript: '>=4.5.0'
peerDependenciesMeta:
'@microsoft/api-extractor':
optional: true
'@swc/core':
optional: true
postcss:
optional: true
typescript:
optional: true
tsx@4.21.0:
resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==}
engines: {node: '>=18.0.0'}
hasBin: true
typescript@5.9.3:
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
engines: {node: '>=14.17'}
hasBin: true
ufo@1.6.3:
resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==}
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
ws@8.19.0:
resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==}
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
snapshots:
'@esbuild/aix-ppc64@0.27.3':
optional: true
'@esbuild/android-arm64@0.27.3':
optional: true
'@esbuild/android-arm@0.27.3':
optional: true
'@esbuild/android-x64@0.27.3':
optional: true
'@esbuild/darwin-arm64@0.27.3':
optional: true
'@esbuild/darwin-x64@0.27.3':
optional: true
'@esbuild/freebsd-arm64@0.27.3':
optional: true
'@esbuild/freebsd-x64@0.27.3':
optional: true
'@esbuild/linux-arm64@0.27.3':
optional: true
'@esbuild/linux-arm@0.27.3':
optional: true
'@esbuild/linux-ia32@0.27.3':
optional: true
'@esbuild/linux-loong64@0.27.3':
optional: true
'@esbuild/linux-mips64el@0.27.3':
optional: true
'@esbuild/linux-ppc64@0.27.3':
optional: true
'@esbuild/linux-riscv64@0.27.3':
optional: true
'@esbuild/linux-s390x@0.27.3':
optional: true
'@esbuild/linux-x64@0.27.3':
optional: true
'@esbuild/netbsd-arm64@0.27.3':
optional: true
'@esbuild/netbsd-x64@0.27.3':
optional: true
'@esbuild/openbsd-arm64@0.27.3':
optional: true
'@esbuild/openbsd-x64@0.27.3':
optional: true
'@esbuild/openharmony-arm64@0.27.3':
optional: true
'@esbuild/sunos-x64@0.27.3':
optional: true
'@esbuild/win32-arm64@0.27.3':
optional: true
'@esbuild/win32-ia32@0.27.3':
optional: true
'@esbuild/win32-x64@0.27.3':
optional: true
'@jridgewell/gen-mapping@0.3.13':
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
'@jridgewell/trace-mapping': 0.3.31
'@jridgewell/resolve-uri@3.1.2': {}
'@jridgewell/sourcemap-codec@1.5.5': {}
'@jridgewell/trace-mapping@0.3.31':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
'@rollup/rollup-android-arm-eabi@4.59.0':
optional: true
'@rollup/rollup-android-arm64@4.59.0':
optional: true
'@rollup/rollup-darwin-arm64@4.59.0':
optional: true
'@rollup/rollup-darwin-x64@4.59.0':
optional: true
'@rollup/rollup-freebsd-arm64@4.59.0':
optional: true
'@rollup/rollup-freebsd-x64@4.59.0':
optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.59.0':
optional: true
'@rollup/rollup-linux-arm-musleabihf@4.59.0':
optional: true
'@rollup/rollup-linux-arm64-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-arm64-musl@4.59.0':
optional: true
'@rollup/rollup-linux-loong64-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-loong64-musl@4.59.0':
optional: true
'@rollup/rollup-linux-ppc64-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-ppc64-musl@4.59.0':
optional: true
'@rollup/rollup-linux-riscv64-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-riscv64-musl@4.59.0':
optional: true
'@rollup/rollup-linux-s390x-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-x64-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-x64-musl@4.59.0':
optional: true
'@rollup/rollup-openbsd-x64@4.59.0':
optional: true
'@rollup/rollup-openharmony-arm64@4.59.0':
optional: true
'@rollup/rollup-win32-arm64-msvc@4.59.0':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.59.0':
optional: true
'@rollup/rollup-win32-x64-gnu@4.59.0':
optional: true
'@rollup/rollup-win32-x64-msvc@4.59.0':
optional: true
'@types/estree@1.0.8': {}
'@types/node@22.19.13':
dependencies:
undici-types: 6.21.0
'@types/ws@8.18.1':
dependencies:
'@types/node': 22.19.13
acorn@8.16.0: {}
any-promise@1.3.0: {}
bundle-require@5.1.0(esbuild@0.27.3):
dependencies:
esbuild: 0.27.3
load-tsconfig: 0.2.5
cac@6.7.14: {}
chokidar@4.0.3:
dependencies:
readdirp: 4.1.2
commander@4.1.1: {}
confbox@0.1.8: {}
consola@3.4.2: {}
debug@4.4.3:
dependencies:
ms: 2.1.3
esbuild@0.27.3:
optionalDependencies:
'@esbuild/aix-ppc64': 0.27.3
'@esbuild/android-arm': 0.27.3
'@esbuild/android-arm64': 0.27.3
'@esbuild/android-x64': 0.27.3
'@esbuild/darwin-arm64': 0.27.3
'@esbuild/darwin-x64': 0.27.3
'@esbuild/freebsd-arm64': 0.27.3
'@esbuild/freebsd-x64': 0.27.3
'@esbuild/linux-arm': 0.27.3
'@esbuild/linux-arm64': 0.27.3
'@esbuild/linux-ia32': 0.27.3
'@esbuild/linux-loong64': 0.27.3
'@esbuild/linux-mips64el': 0.27.3
'@esbuild/linux-ppc64': 0.27.3
'@esbuild/linux-riscv64': 0.27.3
'@esbuild/linux-s390x': 0.27.3
'@esbuild/linux-x64': 0.27.3
'@esbuild/netbsd-arm64': 0.27.3
'@esbuild/netbsd-x64': 0.27.3
'@esbuild/openbsd-arm64': 0.27.3
'@esbuild/openbsd-x64': 0.27.3
'@esbuild/openharmony-arm64': 0.27.3
'@esbuild/sunos-x64': 0.27.3
'@esbuild/win32-arm64': 0.27.3
'@esbuild/win32-ia32': 0.27.3
'@esbuild/win32-x64': 0.27.3
fdir@6.5.0(picomatch@4.0.3):
optionalDependencies:
picomatch: 4.0.3
fix-dts-default-cjs-exports@1.0.1:
dependencies:
magic-string: 0.30.21
mlly: 1.8.0
rollup: 4.59.0
fsevents@2.3.3:
optional: true
get-tsconfig@4.13.6:
dependencies:
resolve-pkg-maps: 1.0.0
joycon@3.1.1: {}
lilconfig@3.1.3: {}
lines-and-columns@1.2.4: {}
load-tsconfig@0.2.5: {}
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
mlly@1.8.0:
dependencies:
acorn: 8.16.0
pathe: 2.0.3
pkg-types: 1.3.1
ufo: 1.6.3
ms@2.1.3: {}
mz@2.7.0:
dependencies:
any-promise: 1.3.0
object-assign: 4.1.1
thenify-all: 1.6.0
object-assign@4.1.1: {}
pathe@2.0.3: {}
picocolors@1.1.1: {}
picomatch@4.0.3: {}
pirates@4.0.7: {}
pkg-types@1.3.1:
dependencies:
confbox: 0.1.8
mlly: 1.8.0
pathe: 2.0.3
postcss-load-config@6.0.1(tsx@4.21.0):
dependencies:
lilconfig: 3.1.3
optionalDependencies:
tsx: 4.21.0
readdirp@4.1.2: {}
resolve-from@5.0.0: {}
resolve-pkg-maps@1.0.0: {}
rollup@4.59.0:
dependencies:
'@types/estree': 1.0.8
optionalDependencies:
'@rollup/rollup-android-arm-eabi': 4.59.0
'@rollup/rollup-android-arm64': 4.59.0
'@rollup/rollup-darwin-arm64': 4.59.0
'@rollup/rollup-darwin-x64': 4.59.0
'@rollup/rollup-freebsd-arm64': 4.59.0
'@rollup/rollup-freebsd-x64': 4.59.0
'@rollup/rollup-linux-arm-gnueabihf': 4.59.0
'@rollup/rollup-linux-arm-musleabihf': 4.59.0
'@rollup/rollup-linux-arm64-gnu': 4.59.0
'@rollup/rollup-linux-arm64-musl': 4.59.0
'@rollup/rollup-linux-loong64-gnu': 4.59.0
'@rollup/rollup-linux-loong64-musl': 4.59.0
'@rollup/rollup-linux-ppc64-gnu': 4.59.0
'@rollup/rollup-linux-ppc64-musl': 4.59.0
'@rollup/rollup-linux-riscv64-gnu': 4.59.0
'@rollup/rollup-linux-riscv64-musl': 4.59.0
'@rollup/rollup-linux-s390x-gnu': 4.59.0
'@rollup/rollup-linux-x64-gnu': 4.59.0
'@rollup/rollup-linux-x64-musl': 4.59.0
'@rollup/rollup-openbsd-x64': 4.59.0
'@rollup/rollup-openharmony-arm64': 4.59.0
'@rollup/rollup-win32-arm64-msvc': 4.59.0
'@rollup/rollup-win32-ia32-msvc': 4.59.0
'@rollup/rollup-win32-x64-gnu': 4.59.0
'@rollup/rollup-win32-x64-msvc': 4.59.0
fsevents: 2.3.3
source-map@0.7.6: {}
sucrase@3.35.1:
dependencies:
'@jridgewell/gen-mapping': 0.3.13
commander: 4.1.1
lines-and-columns: 1.2.4
mz: 2.7.0
pirates: 4.0.7
tinyglobby: 0.2.15
ts-interface-checker: 0.1.13
thenify-all@1.6.0:
dependencies:
thenify: 3.3.1
thenify@3.3.1:
dependencies:
any-promise: 1.3.0
tinyexec@0.3.2: {}
tinyglobby@0.2.15:
dependencies:
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
tree-kill@1.2.2: {}
ts-interface-checker@0.1.13: {}
tsup@8.5.1(tsx@4.21.0)(typescript@5.9.3):
dependencies:
bundle-require: 5.1.0(esbuild@0.27.3)
cac: 6.7.14
chokidar: 4.0.3
consola: 3.4.2
debug: 4.4.3
esbuild: 0.27.3
fix-dts-default-cjs-exports: 1.0.1
joycon: 3.1.1
picocolors: 1.1.1
postcss-load-config: 6.0.1(tsx@4.21.0)
resolve-from: 5.0.0
rollup: 4.59.0
source-map: 0.7.6
sucrase: 3.35.1
tinyexec: 0.3.2
tinyglobby: 0.2.15
tree-kill: 1.2.2
optionalDependencies:
typescript: 5.9.3
transitivePeerDependencies:
- jiti
- supports-color
- tsx
- yaml
tsx@4.21.0:
dependencies:
esbuild: 0.27.3
get-tsconfig: 4.13.6
optionalDependencies:
fsevents: 2.3.3
typescript@5.9.3: {}
ufo@1.6.3: {}
undici-types@6.21.0: {}
ws@8.19.0: {}

351
mcp-bridge/src/bridge.ts Normal file
View File

@@ -0,0 +1,351 @@
/**
* AIPex MCP Bridge
*
* A stdio MCP server that bridges AI agents to the AIPex Chrome extension via WebSocket.
*
* Agent (MCP client) ──stdio──▶ this bridge ──WebSocket──▶ AIPex extension (MCP server)
*
* Usage:
* npx aipex-mcp-bridge [--port 9223]
*
* Works with any MCP client that supports stdio transport:
* - Cursor, Claude Desktop, Claude Code, VS Code Copilot, Windsurf, Zed, etc.
*/
import { createServer } from "node:http"
import { createInterface } from "node:readline"
import { WebSocket, WebSocketServer } from "ws"
// ── CLI args ────────────────────────────────────────────────────────────────
const cliArgs = process.argv.slice(2)
if (cliArgs.includes("--help") || cliArgs.includes("-h")) {
process.stderr.write(`
AIPex MCP Bridge — connect AI agents to AIPex browser extension
Usage:
npx aipex-mcp-bridge [--port <port>]
Options:
--port <port> WebSocket port for AIPex extension (default: 9223)
--help, -h Show this help message
--version, -v Show version
After starting, open AIPex extension Options and connect to:
ws://localhost:<port>
`)
process.exit(0)
}
if (cliArgs.includes("--version") || cliArgs.includes("-v")) {
process.stderr.write("aipex-mcp-bridge 1.0.0\n")
process.exit(0)
}
const portIdx = cliArgs.indexOf("--port")
const WS_PORT = portIdx !== -1 ? parseInt(cliArgs[portIdx + 1], 10) : 9223
if (isNaN(WS_PORT) || WS_PORT < 1 || WS_PORT > 65535) {
process.stderr.write(`Invalid port number. Must be between 1 and 65535.\n`)
process.exit(1)
}
// ── Logging (stderr only — stdout is reserved for MCP protocol) ─────────────
function log(msg: string) {
process.stderr.write(`[aipex-bridge] ${msg}\n`)
}
// ── JSON-RPC types ──────────────────────────────────────────────────────────
interface JSONRPCRequest {
jsonrpc: "2.0"
id: number | string | null
method: string
params?: unknown
}
interface JSONRPCResponse {
jsonrpc: "2.0"
id: number | string | null
result?: unknown
error?: { code: number; message: string }
}
type JSONRPCMessage = JSONRPCRequest | JSONRPCResponse
interface McpTool {
name: string
description?: string
inputSchema?: unknown
}
// ── AIPex WebSocket connection state ────────────────────────────────────────
let aipexSocket: WebSocket | null = null
let aipexReady = false
let cachedTools: McpTool[] = []
let nextAipexId = 1
const aipexPending = new Map<
number | string,
{ resolve: (v: unknown) => void; reject: (e: Error) => void }
>()
// ── Respond to MCP client (stdout, JSON-RPC 2.0) ───────────────────────────
function respond(id: number | string | null, result: unknown) {
const msg: JSONRPCResponse = { jsonrpc: "2.0", id, result }
process.stdout.write(JSON.stringify(msg) + "\n")
}
function respondError(
id: number | string | null,
code: number,
message: string
) {
const msg: JSONRPCResponse = { jsonrpc: "2.0", id, error: { code, message } }
process.stdout.write(JSON.stringify(msg) + "\n")
}
// ── Send requests to AIPex (WebSocket) ──────────────────────────────────────
function sendToAipex(method: string, params: unknown = {}): Promise<unknown> {
if (!aipexSocket || aipexSocket.readyState !== WebSocket.OPEN) {
return Promise.reject(new Error("AIPex extension not connected"))
}
const id = nextAipexId++
const msg = { jsonrpc: "2.0", id, method, params }
aipexSocket.send(JSON.stringify(msg))
return new Promise((resolve, reject) => {
aipexPending.set(id, { resolve, reject })
})
}
// ── Handle messages from AIPex ──────────────────────────────────────────────
function handleAipexMessage(raw: string) {
let msg: JSONRPCMessage
try {
msg = JSON.parse(raw)
} catch {
log(`Failed to parse AIPex message: ${raw.slice(0, 100)}`)
return
}
if ("result" in msg || "error" in msg) {
const res = msg as JSONRPCResponse
const p = aipexPending.get(res.id!)
if (p) {
aipexPending.delete(res.id!)
if (res.error) {
p.reject(new Error(res.error.message))
} else {
p.resolve(res.result)
}
}
}
}
// ── MCP handshake (runs automatically when AIPex connects) ──────────────────
async function doAipexHandshake(socket: WebSocket) {
log("Starting MCP handshake with AIPex...")
const initResult = (await sendToAipex("initialize", {
protocolVersion: "2024-11-05",
capabilities: {},
clientInfo: { name: "aipex-mcp-bridge", version: "1.0.0" }
})) as Record<string, unknown>
const serverInfo = initResult?.serverInfo as
| Record<string, string>
| undefined
log(`AIPex server: ${serverInfo?.name ?? "?"} v${serverInfo?.version ?? "?"}`)
socket.send(
JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" })
)
const toolsResult = (await sendToAipex("tools/list")) as Record<
string,
unknown
>
cachedTools = (toolsResult?.tools as McpTool[]) ?? []
aipexReady = true
log(`Handshake complete. ${cachedTools.length} tools available.`)
}
// ── Handle MCP requests from the agent (stdin) ──────────────────────────────
async function handleAgentRequest(req: JSONRPCRequest) {
const { id, method, params } = req
if (method === "initialize") {
respond(id, {
protocolVersion: "2024-11-05",
capabilities: { tools: {} },
serverInfo: { name: "aipex-mcp-bridge", version: "1.0.0" }
})
return
}
if (method === "notifications/initialized") {
return
}
if (method === "tools/list") {
if (aipexReady && cachedTools.length > 0) {
respond(id, { tools: cachedTools })
} else {
respond(id, {
tools: [
{
name: "check_aipex_connection",
description: [
"AIPex extension is not connected. To enable browser control:",
`1. Open Chrome → AIPex extension → Options page`,
`2. Set WebSocket URL to: ws://localhost:${WS_PORT}`,
`3. Click Connect`,
`Then reload this MCP server.`
].join("\n"),
inputSchema: { type: "object", properties: {} }
}
]
})
}
return
}
if (method === "tools/call") {
if (
!aipexReady ||
!aipexSocket ||
aipexSocket.readyState !== WebSocket.OPEN
) {
respondError(
id,
-32000,
`AIPex extension not connected. Open AIPex Options and connect to ws://localhost:${WS_PORT}`
)
return
}
try {
const result = await sendToAipex(
"tools/call",
params as Record<string, unknown>
)
respond(id, result)
} catch (e) {
respondError(id, -32000, e instanceof Error ? e.message : String(e))
}
return
}
if (method === "ping") {
if (aipexReady && aipexSocket?.readyState === WebSocket.OPEN) {
try {
const result = await sendToAipex("ping")
respond(id, result)
} catch {
respond(id, {})
}
} else {
respond(id, {})
}
return
}
respondError(id, -32601, `Method not found: ${method}`)
}
// ── Read MCP requests from stdin ────────────────────────────────────────────
const stdinRl = createInterface({ input: process.stdin })
stdinRl.on("line", (line) => {
const trimmed = line.trim()
if (!trimmed) return
let req: JSONRPCRequest
try {
req = JSON.parse(trimmed)
} catch {
log(`Failed to parse stdin: ${trimmed.slice(0, 100)}`)
return
}
handleAgentRequest(req).catch((e) => {
log(`Error handling request: ${e instanceof Error ? e.message : String(e)}`)
if (req.id != null) {
respondError(req.id, -32603, "Internal error")
}
})
})
stdinRl.on("close", () => {
log("stdin closed, shutting down")
process.exit(0)
})
// ── WebSocket server (waits for AIPex extension to connect) ─────────────────
const httpServer = createServer()
const wss = new WebSocketServer({ server: httpServer })
wss.on("connection", (socket, req) => {
const addr = req.socket.remoteAddress ?? "unknown"
if (aipexSocket && aipexSocket.readyState === WebSocket.OPEN) {
log(`New connection from ${addr}, closing previous`)
aipexSocket.close()
}
aipexSocket = socket
aipexReady = false
cachedTools = []
log(`AIPex extension connected from ${addr}`)
socket.on("message", (data) => {
handleAipexMessage(data.toString())
})
socket.on("close", () => {
log("AIPex extension disconnected")
if (aipexSocket === socket) {
aipexSocket = null
aipexReady = false
cachedTools = []
}
})
socket.on("error", (err) => {
log(`Socket error: ${err.message}`)
})
doAipexHandshake(socket).catch((err: Error) => {
log(`Handshake failed: ${err.message}`)
})
})
wss.on("error", (err) => {
log(`WebSocket server error: ${err.message}`)
})
// ── Start ───────────────────────────────────────────────────────────────────
httpServer.listen(WS_PORT, () => {
log(`AIPex MCP Bridge started`)
log(`WebSocket server listening on ws://localhost:${WS_PORT}`)
log(`Waiting for AIPex extension to connect...`)
log(`Open AIPex Options → set URL to ws://localhost:${WS_PORT} → Connect`)
})
process.on("SIGINT", () => {
log("Shutting down...")
wss.close()
httpServer.close()
process.exit(0)
})

14
mcp-bridge/tsconfig.json Normal file
View File

@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "dist",
"rootDir": "src",
"declaration": false
},
"include": ["src"]
}

11
mcp-bridge/tsup.config.ts Normal file
View File

@@ -0,0 +1,11 @@
import { defineConfig } from "tsup"
export default defineConfig({
entry: ["src/bridge.ts"],
format: ["esm"],
target: "node18",
clean: true,
banner: {
js: "#!/usr/bin/env node"
}
})

257
skill/SKILL.md Normal file
View File

@@ -0,0 +1,257 @@
---
name: aipex-browser
description: AI-powered browser automation using the AIPex Chrome Extension via MCP bridge. Use this skill when the agent needs to control a Chrome browser — navigating pages, clicking elements, filling forms, capturing screenshots, managing tabs, or downloading content — by connecting to the AIPex MCP bridge.
version: 1.0.0
metadata:
openclaw:
requires:
bins:
- npx
emoji: "🌐"
homepage: https://aipex.ai
os: [macos, linux, windows]
---
# AIPex Browser Control
AIPex is a Chrome extension that exposes 30+ browser automation tools over the Model Context Protocol (MCP). Once connected, the agent can control any Chrome tab using natural language — clicking, typing, navigating, capturing screenshots, downloading content, and more.
**Architecture:**
```
Agent (MCP client) ──stdio──▶ aipex-mcp-bridge ──WebSocket──▶ AIPex Chrome Extension ──▶ Browser APIs
```
---
## When to Use This Skill
Use this skill when the user wants to:
- Navigate to URLs, click links, fill forms, or interact with any web page
- Automate multi-step browser workflows
- Extract or download data from web pages
- Capture screenshots of browser tabs
- Manage multiple tabs across browser windows
- Perform browser-assisted testing (accessibility, UX, regression)
---
## Prerequisites
- **AIPex Chrome extension** installed (available on the Chrome Web Store or via developer build)
- **Node.js >= 18** installed on the local machine
The user is assumed to have AIPex installed. The agent only needs to complete the two connection steps below.
---
## Step 1: Register the MCP Server
Add the following to the agent's MCP configuration. No manual installation is needed — `npx` downloads and runs `aipex-mcp-bridge` automatically.
### Cursor (`.cursor/mcp.json`)
```json
{
"mcpServers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
### Claude Desktop (`claude_desktop_config.json`)
```json
{
"mcpServers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
### Claude Code (CLI)
```bash
claude mcp add aipex-browser -- npx -y aipex-mcp-bridge
```
### VS Code Copilot (`.vscode/mcp.json`)
```json
{
"servers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
### Windsurf (`mcp_config.json`)
```json
{
"mcpServers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge"]
}
}
}
```
### Custom port (optional)
The bridge listens on `localhost:9223` by default. To use a different port:
```json
{
"mcpServers": {
"aipex-browser": {
"command": "npx",
"args": ["-y", "aipex-mcp-bridge", "--port", "9224"]
}
}
}
```
Then use `ws://localhost:9224` in Step 2.
---
## Step 2: Connect the AIPex Extension to the Bridge
After the MCP server is registered and running:
1. Open Chrome and click the **AIPex** extension icon
2. Go to **Options** (or right-click the icon → "Extension options")
3. Find the **WebSocket Connection** section
4. Enter: `ws://localhost:9223`
5. Click **Connect**
The bridge and extension will handshake, and all browser tools will become available to the agent.
**Verifying the connection:** If only a single tool called `check_aipex_connection` is visible, the extension has not yet connected. Follow Step 2 again, then reload the MCP server in agent settings.
---
## Tool Usage Strategy (IMPORTANT)
Always follow this priority order to minimize token cost and latency:
### Priority 1 — `search_elements` (always try first)
Query the page's accessibility tree to find elements and get their UIDs. Fast, cheap, requires no screenshot.
```
search_elements(tabId, "{button,input,textarea,select,a}*")
```
### Priority 2 — UID-based interaction (preferred)
Use UIDs returned by `search_elements` to interact directly:
- `click(tabId, uid)` — click any element
- `fill_element_by_uid(tabId, uid, value)` — type into inputs
- `hover_element_by_uid(tabId, uid)` — reveal menus or tooltips
### Priority 3 — `capture_screenshot` + `computer` (high-cost fallback only)
Use only when `search_elements` fails after two different query attempts, or when pixel-level interaction is required (canvas, drag-and-drop, sliders).
1. `capture_screenshot(sendToLLM=true)` — see the page
2. `computer(action, coordinate)` — click/type at pixel coordinates
### Standard Workflow
```
get_all_tabs()
→ search_elements(tabId, "<pattern>")
→ click(tabId, uid) OR fill_element_by_uid(tabId, uid, value)
→ [capture_screenshot(sendToLLM=true) to verify if needed]
```
---
## Available Tool Categories
| Category | Tools | Description |
|---|---|---|
| Tab Management | 8 tools | Open, close, switch, pin, group tabs |
| UI Interaction | 7 tools | Click, fill, hover, keyboard, coordinate-based |
| Page Content | 4 tools | Metadata, scroll, highlight elements/text |
| Screenshots | 2 tools | Capture visible tab or specific tab |
| Downloads | 3 tools | Save text as markdown, download images |
| Human Intervention | 4 tools | Request user input mid-automation |
**Key tools by category:**
| Category | Key Tools |
|---|---|
| Tab | `get_all_tabs`, `switch_to_tab`, `create_new_tab`, `close_tab` |
| UI | `search_elements`, `click`, `fill_element_by_uid`, `computer` |
| Page | `get_page_metadata`, `scroll_to_element`, `highlight_element` |
| Screenshot | `capture_screenshot`, `capture_tab_screenshot` |
| Download | `download_text_as_markdown`, `download_image` |
| Intervention | `request_intervention`, `list_interventions` |
To load complete parameter schemas and examples for every tool:
```
read_skill_reference("aipex-browser", "references/tools-reference.md")
```
---
## Common Patterns
### Navigate to a URL and click a button
```
create_new_tab("https://example.com")
→ search_elements(tabId, "*[Ss]ubmit*")
→ click(tabId, uid)
```
### Fill a login form
```
get_all_tabs()
→ search_elements(tabId, "{input,textbox}*")
→ fill_element_by_uid(tabId, emailUid, "user@example.com")
→ fill_element_by_uid(tabId, passwordUid, "secret")
→ search_elements(tabId, "*[Ll]ogin*")
→ click(tabId, uid)
```
### Extract page content to markdown
```
get_page_metadata()
→ download_text_as_markdown(content, "page-extract")
```
### Visual verification
```
capture_screenshot(sendToLLM=true)
```
---
## Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
| Only `check_aipex_connection` visible | Extension not connected to bridge | Open AIPex Options → set WebSocket URL → Connect |
| Port 9223 already in use | Port conflict on machine | Use `--port 9224` in MCP config and `ws://localhost:9224` in extension |
| `search_elements` returns 0 results | Page uses canvas or non-semantic HTML | Fall back to `capture_screenshot(sendToLLM=true)` + `computer` tool |
| Connection drops frequently | Service worker sleep cycle | AIPex uses keepalive pings; reconnect extension from Options if needed |
| Tools appear but calls time out | Bridge not receiving WebSocket messages | Restart bridge: reload MCP server in agent settings |

View File

@@ -0,0 +1,427 @@
# AIPex Browser Tools Reference
Complete parameter schemas and usage examples for all AIPex MCP tools exposed via `aipex-mcp-bridge`.
---
## Tab Management
### `get_all_tabs`
Get all open tabs across all browser windows.
**Parameters:** none
**Returns:** Array of tab objects — `id`, `title`, `url`, `windowId`, `active`, `pinned`
---
### `get_current_tab`
Get the currently active tab.
**Parameters:** none
**Returns:** Tab object — `id`, `title`, `url`, `windowId`
---
### `switch_to_tab`
Switch browser focus to a specific tab.
| Parameter | Type | Required | Description |
|---|---|---|---|
| `tabId` | number | yes | ID of the tab to switch to |
---
### `create_new_tab`
Open a new tab at the specified URL.
| Parameter | Type | Required | Description |
|---|---|---|---|
| `url` | string | yes | URL to open |
**Returns:** New tab object including its `id`
---
### `get_tab_info`
Get detailed info about a specific tab.
| Parameter | Type | Required | Description |
|---|---|---|---|
| `tabId` | number | yes | ID of the tab |
---
### `close_tab`
Close a tab by ID.
| Parameter | Type | Required | Description |
|---|---|---|---|
| `tabId` | number | yes | ID of the tab to close |
---
### `organize_tabs`
Use AI to automatically group all open tabs by topic or domain.
**Parameters:** none
---
### `ungroup_tabs`
Remove all tab groups in the current window.
**Parameters:** none
---
## UI Interaction
### `search_elements` ← **Use First**
Search the page's accessibility tree using glob patterns. Returns matching elements with `uid` values for direct interaction. Fast and token-efficient — no screenshot needed.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `tabId` | number | yes | — | ID of the tab to search |
| `query` | string | yes | — | Glob pattern (see below) |
| `contextLevels` | number | no | 1 | Lines of surrounding context to include |
**Glob syntax quick reference:**
| Pattern | Matches |
|---|---|
| `*` | Any sequence of characters |
| `?` | Exactly one character |
| `[abc]` | Any of those characters |
| `{a,b,c}` | Any of those alternatives |
**Starter queries:**
```
{button,input,textarea,select,a}* — all interactive elements
{button,link,a}* — clickable elements only
*[Ss]ubmit*, *[Ss]ave*, *[Cc]onfirm* — action buttons
*[Ll]ogin*, *[Ss]ign* — auth elements
*[Ss]earch* — search inputs
{input,textbox,combobox}* — text inputs
```
**Returns:** Accessibility tree excerpt with `uid=` attributes on matched elements. Use those UIDs directly with `click`, `fill_element_by_uid`, and `hover_element_by_uid`.
---
### `click`
Click an element by UID.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `tabId` | number | yes | — | ID of the tab |
| `uid` | string | yes | — | Element UID from `search_elements` |
| `dblClick` | boolean | no | false | Set to `true` for double-click |
---
### `fill_element_by_uid`
Type text into an input element by UID.
| Parameter | Type | Required | Description |
|---|---|---|---|
| `tabId` | number | yes | ID of the tab |
| `uid` | string | yes | Element UID from `search_elements` |
| `value` | string | yes | Text to type into the element |
---
### `fill_form`
Fill multiple form fields at once.
| Parameter | Type | Required | Description |
|---|---|---|---|
| `tabId` | number | yes | ID of the tab |
| `elements` | array | yes | Array of `{ uid, value }` objects |
**Example:**
```json
{
"tabId": 42,
"elements": [
{ "uid": "input-email-3", "value": "user@example.com" },
{ "uid": "input-pass-4", "value": "password123" }
]
}
```
---
### `get_editor_value`
Read the full text content of a code editor (Monaco, CodeMirror, ACE) or textarea without truncation. Call before overwriting to avoid data loss.
| Parameter | Type | Required | Description |
|---|---|---|---|
| `tabId` | number | yes | ID of the tab |
| `uid` | string | yes | UID of the editor element |
---
### `hover_element_by_uid`
Hover over an element to reveal tooltips, dropdown menus, or hover states.
| Parameter | Type | Required | Description |
|---|---|---|---|
| `tabId` | number | yes | ID of the tab |
| `uid` | string | yes | Element UID from `search_elements` |
---
### `computer` ← **High-Cost Fallback**
Coordinate-based mouse and keyboard interaction. Use only when `search_elements` fails after two different query attempts, or when the task requires pixel-level interaction (canvas, drag-and-drop, custom sliders).
**Prerequisite:** Call `capture_screenshot(sendToLLM=true)` first. Coordinates are pixel positions from the screenshot.
| Parameter | Type | Required | Description |
|---|---|---|---|
| `action` | enum | yes | See action table below |
| `coordinate` | [x, y] | conditional | Pixel coords. Required for most actions. |
| `text` | string | conditional | Text to type or key name(s) to press |
| `start_coordinate` | [x, y] | conditional | Drag start position (for `left_click_drag`) |
| `scroll_direction` | enum | no | `up`, `down`, `left`, `right` |
| `scroll_amount` | number | no | Pixels to scroll |
| `tabId` | number | no | Tab ID (defaults to active tab) |
| `uid` | string | no | Element UID (for `scroll_to` action) |
**Actions:**
| Action | Description |
|---|---|
| `left_click` | Single left click at coordinate |
| `right_click` | Right-click (context menu) at coordinate |
| `double_click` | Double-click at coordinate |
| `triple_click` | Triple-click (select all in field) at coordinate |
| `hover` | Move mouse to coordinate without clicking |
| `type` | Type a text string at the current cursor position |
| `key` | Press a keyboard key or combination |
| `scroll` | Scroll at coordinate in given direction |
| `scroll_to` | Scroll a UID element into view |
| `left_click_drag` | Drag from `start_coordinate` to `coordinate` |
**Common `key` values:**
```
"Enter" — submit / confirm
"Tab" — move focus forward
"shift+Tab" — move focus backward
"Escape" — close dialogs / cancel
"cmd+a" — select all (macOS)
"ctrl+a" — select all (Windows/Linux)
"Backspace" — delete character before cursor
"Delete" — delete character after cursor
"ArrowDown" — navigate list down
"ArrowUp" — navigate list up
"Space" — toggle checkbox / activate button
```
---
## Page Content
### `get_page_metadata`
Get page metadata from the active tab.
**Parameters:** none
**Returns:** `{ title, description, keywords, url, favicon }`
---
### `scroll_to_element`
Scroll a DOM element into view and center it in the viewport.
| Parameter | Type | Required | Description |
|---|---|---|---|
| `selector` | string | yes | CSS selector of the element |
---
### `highlight_element`
Add a persistent visual highlight (drop shadow) to DOM elements. Useful for audit reports or before capturing screenshots.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `selector` | string | yes | — | CSS selector |
| `color` | string | no | — | Shadow color, e.g. `"#00d4ff"` |
| `duration` | number | no | — | Duration in ms (`0` = permanent) |
| `intensity` | enum | no | `"normal"` | `"subtle"`, `"normal"`, or `"strong"` |
| `persist` | boolean | no | `true` | Keep highlight after page interaction |
---
### `highlight_text_inline`
Highlight specific words or phrases within text content on the page.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `selector` | string | yes | — | CSS selector of container element(s) |
| `searchText` | string | yes | — | Text or phrase to highlight |
| `caseSensitive` | boolean | no | `false` | Case-sensitive match |
| `wholeWords` | boolean | no | `false` | Match whole words only |
| `highlightColor` | string | no | `"#DC143C"` | Text color |
| `backgroundColor` | string | no | `"transparent"` | Background color |
| `fontWeight` | string | no | `"bold"` | Font weight |
| `persist` | boolean | no | `true` | Keep highlight permanently |
---
## Screenshots
### `capture_screenshot`
Capture the current visible area of the active tab.
**When to use:** Only when `search_elements` cannot find the target after two query attempts, or when visual layout, images, charts, or canvas content must be analyzed. Adding `sendToLLM=true` increases token cost.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `sendToLLM` | boolean | no | `false` | Send image to LLM for visual analysis. When `true`, enables the `computer` tool for coordinate-based follow-up actions. |
---
### `capture_tab_screenshot`
Capture a screenshot of a specific tab by ID.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `tabId` | number | yes | — | ID of the tab to capture |
| `sendToLLM` | boolean | no | `false` | Send image to LLM. Use sparingly. |
---
## Downloads
### `download_text_as_markdown`
Save text content as a `.md` file to the user's local filesystem.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `text` | string | yes | — | Text content to save |
| `filename` | string | no | — | Filename without `.md` extension |
| `folderPath` | string | no | — | Optional subfolder path |
| `displayResults` | boolean | no | `true` | Show download confirmation |
---
### `download_image`
Download an image from base64 data to the local filesystem.
| Parameter | Type | Required | Description |
|---|---|---|---|
| `imageData` | string | yes | Base64 image data URL (must start with `data:image/`) |
| `filename` | string | no | Filename without extension |
| `folderPath` | string | no | Optional subfolder path |
---
### `download_chat_images`
Download multiple images from chat messages to the local filesystem.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `messages` | array | yes | — | Array of `{ id, parts: [{ type, imageData, imageTitle }] }` |
| `folderPrefix` | string | no | — | Folder name for organizing downloads |
| `filenamingStrategy` | enum | no | `"descriptive"` | `"descriptive"`, `"sequential"`, or `"timestamp"` |
| `displayResults` | boolean | no | `true` | Show download results |
---
## Human Intervention
Use these tools to pause automation and request user input when the agent cannot proceed autonomously (e.g., CAPTCHA, 2FA, ambiguous choices).
### `list_interventions`
List all available human intervention types.
| Parameter | Type | Required | Description |
|---|---|---|---|
| `enabledOnly` | boolean | no | Return only enabled intervention types |
---
### `get_intervention_info`
Get detailed schema and examples for a specific intervention type.
| Parameter | Type | Required | Description |
|---|---|---|---|
| `type` | string | yes | Intervention type (e.g. `"voice-input"`, `"user-selection"`, `"monitor-operation"`) |
---
### `request_intervention`
Pause automation and request human input.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `type` | string | yes | — | Intervention type to request |
| `params` | any | no | — | Type-specific parameters |
| `timeout` | number | no | `300` | Timeout in seconds before auto-cancelling |
| `reason` | string | no | — | Explanation shown to the user |
---
### `cancel_intervention`
Cancel the currently active intervention request.
| Parameter | Type | Required | Description |
|---|---|---|---|
| `id` | string | no | Intervention ID. If omitted, cancels the active intervention. |
---
## Tool Selection Decision Tree
```
Need to interact with a page element?
├── YES ──▶ search_elements(tabId, pattern)
│ │
│ ├── Found UIDs? ──▶ YES ──▶ click / fill_element_by_uid / hover_element_by_uid
│ │
│ └── No results after 2 different queries?
│ └──▶ capture_screenshot(sendToLLM=true)
│ └──▶ computer(action, coordinate)
└── NO ──▶ What kind of task?
├── Tab operation ──▶ get_all_tabs / switch_to_tab / create_new_tab / close_tab
├── Download content ──▶ download_text_as_markdown / download_image
├── Visual capture ──▶ capture_screenshot / capture_tab_screenshot
├── Page info ──▶ get_page_metadata / scroll_to_element
└── Need user input ──▶ request_intervention
```