Ctrl K
ComunidadGuías…

agent-server/nodejs/CLAUDE.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

The eval-server is a **thin HTTP API wrapper for Browser Operator**. It provides:
- WebSocket server for browser agent connections
- REST APIs for browser automation via Chrome DevTools Protocol (CDP)
- JSON-RPC 2.0 bidirectional communication
- Screenshot capture and page content retrieval

**Important:** Evaluation orchestration and LLM-as-a-judge logic lives in the separate `evals/` Python project, which calls these APIs.

## Architecture

### Separation of Concerns

**eval-server (Node.js)**: Thin API layer
- WebSocket server for agent connections
- JSON-RPC 2.0 bidirectional communication
- REST APIs for CDP operations (screenshots, page content, tab management)
- NO evaluation logic, NO judges, NO test orchestration

**evals (Python)**: Evaluation orchestration and judging
- LLM judges (LLMJudge, VisionJudge) in `lib/judge.py`
- Evaluation runners that call eval-server APIs
- Test case definitions (YAML files in `data/`)
- Result reporting and analysis

## Core Components

### WebSocket Server (src/lib/EvalServer.js)
- Accepts connections from browser agents (DevTools clients)
- Manages agent lifecycle (connect, ready, disconnect)
- Handles bidirectional RPC communication
- Integrates directly with Chrome DevTools Protocol

### HTTP API Server (src/api-server.js)
- Exposes REST endpoints for external callers (e.g., Python evals)
- Main endpoint: `POST /v1/responses` - Send task to agent
- CDP endpoints: screenshot, page content, JavaScript execution, tab management
- Returns metadata (clientId, tabId) for subsequent operations

### RPC Client (src/rpc-client.js)
- Implements JSON-RPC 2.0 protocol for bidirectional communication
- Manages request/response correlation with unique IDs
- Handles timeouts and error conditions
- Calls `evaluate(params)` method on connected agents

### Client Manager (src/client-manager.js)
- Tracks WebSocket client connections
- Manages tab-level connections (composite clientId:tabId)
- Maintains client state (connected, ready)

### CDP Integration (src/lib/EvalServer.js)
- Direct Chrome DevTools Protocol communication
- Screenshot capture via `Page.captureScreenshot`
- Page content access via `Runtime.evaluate`
- JavaScript execution via `Runtime.evaluate` (with configurable options)
- Tab management via `Target.createTarget` / `Target.closeTarget`

### Logger (src/logger.js)
- Structured logging using Winston
- Separate log files for different event types
- JSON format for easy parsing and analysis

## Key API Endpoints

### POST /v1/responses

Primary endpoint for sending tasks to browser agents.

**Request (OpenAI):**
```json
{
  "input": "Click the submit button",
  "url": "https://example.com",
  "wait_timeout": 5000,
  "model": {
    "main_model": {"provider": "openai", "model": "gpt-5-mini", "api_key": "sk-..."},
    "mini_model": {"provider": "openai", "model": "gpt-5-nano", "api_key": "sk-..."},
    "nano_model": {"provider": "openai", "model": "gpt-5-nano", "api_key": "sk-..."}
  }
}
```

**Request (LiteLLM with endpoint):**
```json
{
  "input": "Navigate to google.com",
  "url": "about:blank",
  "wait_timeout": 5000,
  "model": {
    "main_model": {
      "provider": "litellm",
      "model": "qwen3:14b",
      "endpoint": "http://localhost:4000"
    },
    "mini_model": {
      "provider": "litellm",
      "model": "qwen3:14b",
      "endpoint": "http://localhost:4000"
    },
    "nano_model": {
      "provider": "litellm",
      "model": "qwen3:14b",
      "endpoint": "http://localhost:4000"
    }
  }
}
```

**Note:** The `endpoint` field can be:
- Specified per model tier (e.g., `main_model.endpoint`)
- Specified at top-level (`model.endpoint`) to apply to all tiers
- Omitted to use `LITELLM_ENDPOINT` environment variable

**Request (Conversation State - OpenAI Responses API format):**
```json
{
  "input": [
    {
      "role": "system",
      "content": "You are a web automation expert."
    },
    {
      "role": "user",
      "content": "Navigate to bloomberg.com"
    },
    {
      "role": "assistant",
      "content": "I've navigated to bloomberg.com. I can see the homepage."
    },
    {
      "role": "user",
      "content": "Summarize todays news"
    }
  ],
  "url": "https://bloomberg.com",
  "model": {
    "main_model": {
      "provider": "litellm",
      "model": "gemma3:12b",
      "endpoint": "http://localhost:4000"
    }
  }
}
```

**Input Format Options:**
1. **String format**: `"input": "Your message"` (simple, single message)
2. **Conversation array**: `"input": [{role, content}, ...]` (multi-turn with history)

**Message Requirements:**
- Each message needs `role` (`system`, `user`, or `assistant`) and `content` (string)
- At least one `user` message required
- System messages are extracted as system prompt
- Maximum 100 messages, 10,000 characters each

**Response (OpenAI-compatible format):**
```json
[
  {
    "id": "msg_abc123",
    "type": "message",
    "role": "assistant",
    "content": [{"type": "output_text", "text": "Done", "annotations": []}],
    "metadata": {
      "clientId": "9907fd8d-92a8-4a6a-bce9-458ec8c57306",
      "tabId": "482D56EE57B1931A3B9D1BFDAF935429"
    }
  }
]
```

**Important:** The `metadata` field contains `clientId` and `tabId` which are used by the evals framework for screenshot capture.

### POST /page/screenshot

Capture screenshot of a browser tab via CDP.

**Request:**
```json
{
  "clientId": "9907fd8d-92a8-4a6a-bce9-458ec8c57306",
  "tabId": "482D56EE57B1931A3B9D1BFDAF935429",
  "fullPage": false
}
```

**Response:**
```json
{
  "clientId": "...",
  "tabId": "...",
  "imageData": "data:image/png;base64,iVBORw0KG...",
  "format": "png",
  "timestamp": 1234567890
}
```

### POST /page/content

Get HTML or text content of a page.

**Request:**
```json
{
  "clientId": "9907fd8d-92a8-4a6a-bce9-458ec8c57306",
  "tabId": "482D56EE57B1931A3B9D1BFDAF935429",
  "format": "html",
  "includeIframes": true
}
```

**Parameters:**
- `clientId` (required): The client ID from `/v1/responses` metadata
- `tabId` (required): The tab ID from `/v1/responses` metadata
- `format` (optional, default: `"html"`): Content format - either `"html"` or `"text"`
- `includeIframes` (optional, default: `false`): Whether to include HTML content from iframes. When `true`, recursively captures content from all iframe elements on the page.

**Response:**
```json
{
  "clientId": "9907fd8d-92a8-4a6a-bce9-458ec8c57306",
  "tabId": "482D56EE57B1931A3B9D1BFDAF935429",
  "content": "<html>...</html>",
  "format": "html",
  "length": 12345,
  "frameCount": 3,
  "timestamp": 1234567890
}
```

**Response fields:**
- `frameCount` (number, optional): Number of frames included in the content. Only present when `includeIframes: true` is used.

### POST /page/execute

Execute JavaScript code in the context of a specific browser tab via Chrome DevTools Protocol.

**Request:**
```json
{
  "clientId": "9907fd8d-92a8-4a6a-bce9-458ec8c57306",
  "tabId": "482D56EE57B1931A3B9D1BFDAF935429",
  "expression": "document.title",
  "returnByValue": true,
  "awaitPromise": false
}
```

**Parameters:**
- `clientId` (required): The client ID from `/v1/responses` metadata
- `tabId` (required): The tab ID from `/v1/responses` metadata
- `expression` (required): JavaScript code to execute (string)
- `returnByValue` (optional, default: `true`): Whether to return result by value or as object reference
- `awaitPromise` (optional, default: `false`): Whether to await if the result is a Promise

**Response:**
```json
{
  "clientId": "9907fd8d-92a8-4a6a-bce9-458ec8c57306",
  "tabId": "482D56EE57B1931A3B9D1BFDAF935429",
  "result": {
    "type": "string",
    "value": "Example Page Title"
  },
  "exceptionDetails": null,
  "timestamp": 1234567890
}
```

**Response Fields:**
- `clientId`: Base client ID (without tab suffix)
- `tabId`: The tab ID where JavaScript was executed
- `result`: CDP `Runtime.evaluate` result object containing:
  - `type`: Result type (string, number, object, etc.)
  - `value`: The actual value (if `returnByValue: true`)
- `exceptionDetails`: Error details if execution failed, otherwise `null`
- `timestamp`: Unix timestamp in milliseconds

**Implementation:**
- Uses CDP `Runtime.evaluate` via `browserAgentServer.evaluateJavaScript()`
- Executes code in the page's main JavaScript context
- First 100 characters of expression logged for debugging

**Example Usage:**

```bash
# Get page title
curl -X POST http://localhost:8080/page/execute \
  -H "Content-Type: application/json" \
  -d '{
    "clientId": "9907fd8d-92a8-4a6a-bce9-458ec8c57306",
    "tabId": "482D56EE57B1931A3B9D1BFDAF935429",
    "expression": "document.title"
  }'

# Count elements
curl -X POST http://localhost:8080/page/execute \
  -H "Content-Type: application/json" \
  -d '{
    "clientId": "9907fd8d-92a8-4a6a-bce9-458ec8c57306",
    "tabId": "482D56EE57B1931A3B9D1BFDAF935429",
    "expression": "document.querySelectorAll(\"button\").length"
  }'

# Execute async code with await
curl -X POST http://localhost:8080/page/execute \
  -H "Content-Type: application/json" \
  -d '{
    "clientId": "9907fd8d-92a8-4a6a-bce9-458ec8c57306",
    "tabId": "482D56EE57B1931A3B9D1BFDAF935429",
    "expression": "fetch(\"https://api.example.com/data\").then(r => r.json())",
    "awaitPromise": true
  }'
```

**Use Cases:**
- Extract specific data from the page (e.g., element counts, text content)
- Verify JavaScript state/variables for evaluations
- Check DOM state programmatically
- Execute custom validation logic
- Interact with page APIs directly

This endpoint complements `/page/content` by allowing precise JavaScript execution rather than just fetching full HTML/text content.

### POST /tabs/open, POST /tabs/close

Tab management via CDP.

## Configuration

All configuration is managed through environment variables and `src/config.js`:

```bash
# .env file
PORT=8080              # WebSocket server port
API_PORT=8081          # HTTP API server port
AUTH_KEY=hello         # Authentication key
CDP_HOST=localhost     # Chrome DevTools Protocol host
CDP_PORT=9223          # Chrome DevTools Protocol port
```

## Model Configuration Schema

The server uses a canonical nested model configuration format:

```typescript
interface ModelTierConfig {
  provider: string;  // "openai" | "groq" | "openrouter" | "litellm"
  model: string;     // Model name (e.g., "gpt-5-mini")
  api_key: string;   // API key for this tier
}

interface ModelConfig {
  main_model: ModelTierConfig;  // Primary model
  mini_model: ModelTierConfig;  // Secondary model
  nano_model: ModelTierConfig;  // Tertiary model
}
```

## Tab Management

Each browser tab is identified by a **composite client ID**: `baseClientId:tabId`

Example:
- Base Client ID: `9907fd8d-92a8-4a6a-bce9-458ec8c57306`
- Tab ID: `482D56EE57B1931A3B9D1BFDAF935429`
- Composite: `9907fd8d-92a8-4a6a-bce9-458ec8c57306:482D56EE57B1931A3B9D1BFDAF935429`

## CDP Communication Pattern

The server uses **direct CDP communication**:

1. Discover CDP WebSocket endpoint via `http://localhost:9223/json/version`
2. For each command, establish WebSocket connection to CDP endpoint
3. Send commands using JSON-RPC 2.0:
   - **Browser-level**: `Target.createTarget`, `Target.closeTarget`
   - **Tab-level**: `Runtime.evaluate`, `Page.captureScreenshot`
4. For tab-level operations: attach → execute → detach
5. Close WebSocket after receiving response

## Integration with Evals Framework

The eval-server is designed to work with the separate `evals/` Python project:

**Flow:**
1. Python evals runner sends request to `/v1/responses`
2. Eval-server forwards to DevTools agent via WebSocket/JSON-RPC
3. Agent performs browser automation task
4. Response includes `metadata.clientId` and `metadata.tabId`
5. Python evals uses these IDs to capture screenshot via `/page/screenshot`
6. Python evals uses VisionJudge to evaluate with screenshot
7. Python evals generates report and saves screenshot

See `evals/README.md` for detailed evaluation framework documentation.

## Development Commands

```bash
# Install dependencies
npm install

# Start server
npm start

# Check status
curl http://localhost:8081/status

# Test screenshot
curl -X POST http://localhost:8081/page/screenshot \
  -H "Content-Type: application/json" \
  -d '{"clientId":"CLIENT_ID","tabId":"TAB_ID","fullPage":false}'
```

## Chrome Setup

The browser must be started with remote debugging enabled:

```bash
chromium --remote-debugging-port=9223
```

CDP endpoint: `http://localhost:9223/json/version`

## File Structure

```
nodejs/
├── package.json
├── .env.example
└── src/
    ├── api-server.js           # HTTP REST API endpoints
    ├── client-manager.js       # WebSocket client management
    ├── rpc-client.js           # JSON-RPC 2.0 communication
    ├── config.js               # Configuration management
    ├── logger.js               # Winston logging
    └── lib/
        ├── EvalServer.js       # Core server + CDP integration
        └── HTTPWrapper.js      # HTTP wrapper around EvalServer
```

## Key Implementation Details

### formatResponse() Method

Located in `src/api-server.js:706`

Converts agent responses to OpenAI-compatible format and **adds metadata**:

```javascript
formatResponse(responseText, clientId = null, tabId = null) {
  const messageId = `msg_${uuidv4().replace(/-/g, '')}`;

  const response = [{
    id: messageId,
    type: 'message',
    role: 'assistant',
    content: [{ type: 'output_text', text: responseText, annotations: [] }]
  }];

  // Add metadata for screenshot capture
  if (clientId && tabId) {
    response[0].metadata = { clientId, tabId };
  }

  return response;
}
```

This metadata is critical for the evals framework to capture screenshots.

### Screenshot Capture Flow

1. Evals calls `/v1/responses` with task
2. Server returns response with `metadata: {clientId, tabId}`
3. Evals extracts metadata from response
4. Evals calls `/page/screenshot` with extracted IDs
5. Server uses CDP to capture screenshot
6. Returns base64-encoded PNG
7. Evals saves screenshot and uses for VisionJudge evaluation

## Logging

Logs are written to `logs/` directory (auto-created):
- `combined.log` - All log events
- `error.log` - Error events only
- `api.log` - API request/response logs

## Docker Integration

The eval-server runs inside `kernel-browser-extended` Docker container.

Volume mount for live development:
```yaml
volumes:
  - "./eval-server/nodejs:/opt/eval-server"
```

## Dependencies

Core dependencies:
- `ws` - WebSocket server
- `uuid` - ID generation
- `winston` - Structured logging
- `dotenv` - Environment variable management

Removed dependencies:
- ~~`openai`~~ - Not needed (evals handles judging)
- ~~`js-yaml`~~ - Not needed (evals handles YAML loading)

## What This Server Does NOT Do

- ❌ Load YAML evaluation definitions (handled by evals/)
- ❌ LLM-as-a-judge evaluation (handled by evals/)
- ❌ Test orchestration (handled by evals/)
- ❌ Result reporting (handled by evals/)
- ❌ Screenshot analysis (handled by evals/)

## What This Server DOES Do

- ✅ WebSocket server for browser agent connections
- ✅ JSON-RPC 2.0 bidirectional communication
- ✅ HTTP REST API endpoints
- ✅ CDP screenshot capture
- ✅ CDP page content retrieval
- ✅ CDP JavaScript execution
- ✅ CDP tab management
- ✅ Return metadata (clientId, tabId) for screenshot capture