💬 Commit message: Update 2026-02-11 04:13:54, 13 files, 595 lines

📁 Files changed: 13
📝 Lines changed: 595

  • PLAN.md
  • TODO.md
  • browser.d.ts
  • browser.d.ts.map
  • browser.js
  • browser.js.map
  • mcp.js
  • mcp.js.map
  • types.d.ts
  • types.d.ts.map
  • browser.ts
  • mcp.ts
  • types.ts
This commit is contained in:
Adam Ladachowski
2026-02-11 04:13:54 +01:00
parent dd26376833
commit 5a7466b67d
13 changed files with 421 additions and 218 deletions
+192 -181
View File
@@ -1,238 +1,249 @@
# Plan: Web Debugging Commands # Plan: Playwright Debugging Features
## Phase 1: Storage & Session Commands ## Phase 1: Core Debugging (Console & Errors)
### Description ### Description
Add commands for inspecting and manipulating browser storage and cookies. These are essential for debugging authentication, session state, and client-side data persistence. Implement fundamental debugging capabilities: console message capture and uncaught exception handling. These form the foundation for debugging client-side issues.
### Steps ### Steps
#### Step 1.1: Add Cookies Command #### Step 1.1: Add Console Command
- **Objective**: Implement get/set/clear cookies functionality - **Objective**: Capture and retrieve console messages from browser
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts` - **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: None - **Dependencies**: None
- **Status**: COMPLETE
- **Implementation**: - **Implementation**:
- Add `CookiesCommand` type with `action: 'get' | 'set' | 'clear'` - Add `ConsoleCommand` type with `level` filter and `clear` option
- Add optional `name`, `value`, `url` fields for set operation - Store messages via `page.on('console')` listener
- Implement `cookies()` method using `context.cookies()` and `context.addCookies()` - Return messages with level, text, timestamp, location
- Add logging with cookie icon
#### Step 1.2: Add Storage Command #### Step 1.2: Add Page Errors Command
- **Objective**: Implement localStorage and sessionStorage read/write/clear - **Objective**: Capture uncaught exceptions and unhandled promise rejections
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts` - **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: Step 1.1 - **Dependencies**: Step 1.1
- **Implementation**: - **Implementation**:
- Add `StorageCommand` type with `storage: 'local' | 'session'` and `action: 'get' | 'set' | 'clear'` - Add `ErrorsCommand` type with `clear` option
- Implement via `page.evaluate()` accessing `window.localStorage` / `window.sessionStorage` - Listen to `page.on('pageerror')` for uncaught exceptions
- Return all items on get, or specific key if provided - Store error message, stack trace, timestamp
- Add `browser://errors` MCP resource
## Phase 2: Console & Debugging Commands ## Phase 2: Network Monitoring
### Description ### Description
Add commands for capturing console output and debugging page state. Critical for understanding client-side errors and application behavior. Implement network request/response capture for debugging API calls, identifying failed requests, and inspecting payloads.
### Steps ### Steps
#### Step 2.1: Add Console Command #### Step 2.1: Add Network Logging
- **Objective**: Capture and return console messages (log, error, warn, info) - **Objective**: Capture all network requests and responses
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts` - **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: None - **Dependencies**: None
- **Implementation**: - **Implementation**:
- Add `ConsoleCommand` type with optional `clear: boolean` - Add `NetworkCommand` type with `filter` and `clear` options
- Store console messages in array via `page.on('console')` - Listen to `page.on('request')` and `page.on('response')`
- Initialize listener in `launch()` method - Store: url, method, status, resourceType, timing, headers
- Return accumulated messages with type, text, and timestamp - Add optional body capture for XHR/fetch (with size limit)
- Add `browser://network` MCP resource
#### Step 2.2: Add Metrics Command #### Step 2.2: Add Failed Requests Filter
- **Objective**: Return performance metrics and DOM statistics - **Objective**: Quick access to failed/error requests
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts` - **Files**: `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: Step 2.1
- **Implementation**:
- Add `failed` filter option to NetworkCommand
- Include requests with status >= 400 or network errors
- Add `browser://network/failed` MCP resource
#### Step 2.3: Add Request Interception
- **Objective**: Block or mock specific requests
- **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: Step 2.1
- **Implementation**:
- Add `InterceptCommand` with `action: 'block' | 'mock' | 'clear'`
- Support URL patterns (glob or regex)
- For mock: allow custom response body/status
- Use `page.route()` for interception
## Phase 3: Performance & Metrics
### Description
Add performance timing and metrics collection for identifying bottlenecks and measuring page load characteristics.
### Steps
#### Step 3.1: Add Performance Metrics
- **Objective**: Return page performance timing data
- **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: None - **Dependencies**: None
- **Implementation**: - **Implementation**:
- Add `MetricsCommand` type - Add `MetricsCommand` type
- Use `page.evaluate()` to gather `performance.timing`, DOM node counts - Collect via `performance.timing` and `performance.getEntriesByType()`
- Return structured metrics object - Return: domContentLoaded, load, firstPaint, firstContentfulPaint
- Include DOM stats: nodeCount, scriptCount, styleCount
## Phase 3: Interaction Commands #### Step 3.2: Add Resource Timing
- **Objective**: Get timing breakdown for individual resources
- **Files**: `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: Step 3.1
- **Implementation**:
- Add `resources` option to MetricsCommand
- Use `performance.getEntriesByType('resource')`
- Return: name, duration, transferSize, initiatorType
## Phase 4: Accessibility
### Description ### Description
Add commands for advanced page interactions beyond click and type. Enables testing hover states, keyboard shortcuts, and form controls. Implement accessibility tree inspection for debugging screen reader and a11y issues.
### Steps ### Steps
#### Step 3.1: Add Scroll Command #### Step 4.1: Add Accessibility Snapshot
- **Objective**: Scroll to position, element, or by delta - **Objective**: Dump accessibility tree for page or element
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts` - **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: None - **Dependencies**: None
- **Implementation**: - **Implementation**:
- Add `ScrollCommand` with `selector?: string`, `x?: number`, `y?: number`, `behavior?: 'smooth' | 'instant'` - Add `A11yCommand` with optional `selector` for subtree
- If selector provided, use `element.scrollIntoView()`
- Otherwise use `window.scrollTo()`
#### Step 3.2: Add Hover Command
- **Objective**: Trigger hover state on element
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts`
- **Dependencies**: None
- **Implementation**:
- Add `HoverCommand` with `selector: string`
- Use `page.hover(selector)`
- Return success with element info
#### Step 3.3: Add Select Command
- **Objective**: Select option(s) in dropdown/select elements
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts`
- **Dependencies**: None
- **Implementation**:
- Add `SelectCommand` with `selector: string`, `value: string | string[]`
- Use `page.selectOption(selector, value)`
- Return selected values
#### Step 3.4: Add Keys Command
- **Objective**: Send keyboard shortcuts and special keys
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts`
- **Dependencies**: None
- **Implementation**:
- Add `KeysCommand` with `keys: string` (e.g., "Control+a", "Escape", "Enter")
- Use `page.keyboard.press()` for single keys
- Support key combinations
## Phase 4: Network Commands
### Description
Add commands for network inspection and manipulation. Enables debugging API calls, blocking resources, and simulating network conditions.
### Steps
#### Step 4.1: Add Network Logging
- **Objective**: Capture and return network requests/responses
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts`
- **Dependencies**: None
- **Implementation**:
- Add `NetworkCommand` with `action: 'log' | 'clear'`, optional `filter: string`
- Store requests via `page.on('request')` and `page.on('response')`
- Return array of {url, method, status, type, timing}
#### Step 4.2: Add Block Command
- **Objective**: Block URLs or patterns (ads, analytics, etc.)
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts`
- **Dependencies**: Step 4.1
- **Implementation**:
- Add `BlockCommand` with `patterns: string[]` and `action: 'add' | 'remove' | 'clear'`
- Use `page.route()` to abort matching requests
- Maintain list of blocked patterns
#### Step 4.3: Add Throttle Command
- **Objective**: Simulate slow network conditions
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts`
- **Dependencies**: None
- **Implementation**:
- Add `ThrottleCommand` with `preset: 'slow3g' | 'fast3g' | 'offline' | 'none'`
- Use CDP session to set network conditions via `Network.emulateNetworkConditions`
## Phase 5: Output Commands
### Description
Add commands for exporting page content in different formats.
### Steps
#### Step 5.1: Add PDF Command
- **Objective**: Save page as PDF
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts`
- **Dependencies**: None
- **Implementation**:
- Add `PdfCommand` with `path: string`, optional `format`, `landscape`, `margin`
- Use `page.pdf()` (WebKit supports this)
- Return path to saved file
#### Step 5.2: Add Accessibility Command
- **Objective**: Dump accessibility tree snapshot
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts`
- **Dependencies**: None
- **Implementation**:
- Add `AccessibilityCommand` with optional `selector` for subtree
- Use `page.accessibility.snapshot()` - Use `page.accessibility.snapshot()`
- Return tree structure - Return tree with role, name, value, description
- Add `browser://a11y` MCP resource
## Phase 6: Advanced Commands ## Phase 5: Dialog Handling
### Description ### Description
Add commands for viewport manipulation, device emulation, and frame handling. Implement automatic handling of browser dialogs (alert, confirm, prompt) to prevent blocking during automation.
### Steps ### Steps
#### Step 6.1: Add Viewport Command #### Step 5.1: Add Dialog Command
- **Objective**: Resize viewport dynamically - **Objective**: Configure how dialogs are handled
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts` - **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: None - **Dependencies**: None
- **Implementation**: - **Implementation**:
- Add `ViewportCommand` with `width: number`, `height: number` - Add `DialogCommand` with `action: 'accept' | 'dismiss' | 'status'`
- Option to set default behavior for future dialogs
- Option to provide text response for prompts
- Store dialog history (type, message, response)
- Listen to `page.on('dialog')`
## Phase 6: Storage & Cookies
### Description
Add commands for inspecting and manipulating browser storage, complementing the existing session save/restore.
### Steps
#### Step 6.1: Add Cookies Command
- **Objective**: Get, set, and clear cookies
- **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: None
- **Implementation**:
- Add `CookiesCommand` with `action: 'get' | 'set' | 'delete' | 'clear'`
- Use `context.cookies()` and `context.addCookies()`
- Support filtering by name/domain
- Add `browser://cookies` MCP resource
#### Step 6.2: Add Storage Command
- **Objective**: Inspect/modify localStorage and sessionStorage
- **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: None
- **Implementation**:
- Add `StorageCommand` with `type: 'local' | 'session'`
- Actions: get, set, delete, clear
- Implement via `page.evaluate()`
- Add `browser://storage/local` and `browser://storage/session` resources
## Phase 7: Advanced Interactions
### Description
Add additional interaction commands for comprehensive testing scenarios.
### Steps
#### Step 7.1: Add Hover Command
- **Objective**: Trigger hover state on elements
- **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: None
- **Implementation**:
- Add `HoverCommand` with `selector`
- Use `page.hover(selector)`
#### Step 7.2: Add Select Command
- **Objective**: Select options in dropdown elements
- **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: None
- **Implementation**:
- Add `SelectCommand` with `selector` and `value` (or values array)
- Use `page.selectOption()`
#### Step 7.3: Add Keys Command
- **Objective**: Send keyboard shortcuts and special keys
- **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: None
- **Implementation**:
- Add `KeysCommand` with `keys` string (e.g., "Control+a", "Escape")
- Use `page.keyboard.press()`
#### Step 7.4: Add Upload Command
- **Objective**: Set files on file input elements
- **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: None
- **Implementation**:
- Add `UploadCommand` with `selector` and `files` array
- Use `page.setInputFiles()`
#### Step 7.5: Add Scroll Command
- **Objective**: Scroll page or element into view
- **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: None
- **Implementation**:
- Add `ScrollCommand` with optional `selector`, `x`, `y`
- If selector: use `element.scrollIntoView()`
- Otherwise: use `window.scrollTo()`
## Phase 8: Viewport & Emulation
### Description
Add device emulation and viewport manipulation for responsive testing.
### Steps
#### Step 8.1: Add Viewport Command
- **Objective**: Resize browser viewport dynamically
- **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: None
- **Implementation**:
- Add `ViewportCommand` with `width` and `height`
- Use `page.setViewportSize()` - Use `page.setViewportSize()`
- Return new dimensions
#### Step 6.2: Add Emulate Command #### Step 8.2: Add Emulate Command
- **Objective**: Emulate mobile devices - **Objective**: Emulate specific devices
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts` - **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
- **Dependencies**: Step 6.1 - **Dependencies**: Step 8.1
- **Implementation**: - **Implementation**:
- Add `EmulateCommand` with `device: string` (e.g., 'iPhone 12', 'Pixel 5') - Add `EmulateCommand` with `device` name
- Use Playwright's device descriptors - Use Playwright's device descriptors
- Apply viewport, userAgent, deviceScaleFactor - Apply viewport, userAgent, deviceScaleFactor, touch support
#### Step 6.3: Add Frames Command ## Phase 9: Documentation
- **Objective**: List and switch to iframe contexts
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts`
- **Dependencies**: None
- **Implementation**:
- Add `FramesCommand` with `action: 'list' | 'switch'`, optional `selector` or `index`
- Use `page.frames()` to list, `frame.locator()` to switch context
- Track current frame for subsequent commands
#### Step 6.4: Add Dialog Command
- **Objective**: Handle alert/confirm/prompt dialogs
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts`
- **Dependencies**: None
- **Implementation**:
- Add `DialogCommand` with `action: 'accept' | 'dismiss'`, optional `text` for prompt
- Set up `page.on('dialog')` handler
- Queue dialogs and respond based on command
#### Step 6.5: Add Upload Command
- **Objective**: Fill file input elements
- **Files**: `src/types.ts`, `src/browser.ts`, `src/server.ts`, `src/index.ts`
- **Dependencies**: None
- **Implementation**:
- Add `UploadCommand` with `selector: string`, `files: string[]`
- Use `page.setInputFiles(selector, files)`
- Return success with file count
## Phase 7: Documentation & Polish
### Description ### Description
Update documentation and CLI help text with all new commands. Update all documentation with new commands and examples.
### Steps ### Steps
#### Step 7.1: Update README #### Step 9.1: Update README
- **Objective**: Document all new commands with examples - **Objective**: Document all commands with examples
- **Files**: `README.md` - **Files**: `README.md`
- **Dependencies**: Phases 1-6 - **Dependencies**: Phases 1-8
- **Implementation**: - **Implementation**:
- Add command reference section - Add command reference grouped by category
- Include curl examples for each command - Include curl/MCP examples
- Document response formats - Document response formats
#### Step 7.2: Update CLAUDE.md #### Step 9.2: Update CLAUDE.md
- **Objective**: Update developer documentation - **Objective**: Update developer documentation
- **Files**: `CLAUDE.md` - **Files**: `CLAUDE.md`
- **Dependencies**: Step 7.1 - **Dependencies**: Step 9.1
- **Implementation**: - **Implementation**:
- Update architecture section with new command flow - Update architecture notes
- Document internal logging format - Document new command types
#### Step 7.3: Update CLI Help
- **Objective**: Update --help output with command list
- **Files**: `src/cli.ts`
- **Dependencies**: Phases 1-6
- **Implementation**:
- Expand server mode help section
- Group commands by category
+32 -29
View File
@@ -1,36 +1,39 @@
# TODO: Web Debugging Commands # TODO: Playwright Debugging Features
## Phase 1: Storage & Session Commands ## Phase 1: Core Debugging (Console & Errors)
- [ ] Step 1.1: Add Cookies Command - [x] Step 1.1: Add Console Command
- [ ] Step 1.2: Add Storage Command - [ ] Step 1.2: Add Page Errors Command
## Phase 2: Console & Debugging Commands ## Phase 2: Network Monitoring
- [ ] Step 2.1: Add Console Command - [ ] Step 2.1: Add Network Logging
- [ ] Step 2.2: Add Metrics Command - [ ] Step 2.2: Add Failed Requests Filter
- [ ] Step 2.3: Add Request Interception
## Phase 3: Interaction Commands ## Phase 3: Performance & Metrics
- [ ] Step 3.1: Add Scroll Command - [ ] Step 3.1: Add Performance Metrics
- [ ] Step 3.2: Add Hover Command - [ ] Step 3.2: Add Resource Timing
- [ ] Step 3.3: Add Select Command
- [ ] Step 3.4: Add Keys Command
## Phase 4: Network Commands ## Phase 4: Accessibility
- [ ] Step 4.1: Add Network Logging - [ ] Step 4.1: Add Accessibility Snapshot
- [ ] Step 4.2: Add Block Command
- [ ] Step 4.3: Add Throttle Command
## Phase 5: Output Commands ## Phase 5: Dialog Handling
- [ ] Step 5.1: Add PDF Command - [ ] Step 5.1: Add Dialog Command
- [ ] Step 5.2: Add Accessibility Command
## Phase 6: Advanced Commands ## Phase 6: Storage & Cookies
- [ ] Step 6.1: Add Viewport Command - [ ] Step 6.1: Add Cookies Command
- [ ] Step 6.2: Add Emulate Command - [ ] Step 6.2: Add Storage Command
- [ ] Step 6.3: Add Frames Command
- [ ] Step 6.4: Add Dialog Command
- [ ] Step 6.5: Add Upload Command
## Phase 7: Documentation & Polish ## Phase 7: Advanced Interactions
- [ ] Step 7.1: Update README - [ ] Step 7.1: Add Hover Command
- [ ] Step 7.2: Update CLAUDE.md - [ ] Step 7.2: Add Select Command
- [ ] Step 7.3: Update CLI Help - [ ] Step 7.3: Add Keys Command
- [ ] Step 7.4: Add Upload Command
- [ ] Step 7.5: Add Scroll Command
## Phase 8: Viewport & Emulation
- [ ] Step 8.1: Add Viewport Command
- [ ] Step 8.2: Add Emulate Command
## Phase 9: Documentation
- [ ] Step 9.1: Update README
- [ ] Step 9.2: Update CLAUDE.md
+5 -1
View File
@@ -1,12 +1,14 @@
import { type BrowserContext, type Page } from 'playwright'; import { type BrowserContext, type Page } from 'playwright';
import type { BrowserCommand, BrowserOptions, CommandResponse, ElementInfo } from './types.js'; import type { BrowserCommand, BrowserOptions, CommandResponse, ConsoleMessage, ElementInfo } from './types.js';
export declare class ClaudeBrowser { export declare class ClaudeBrowser {
private browser; private browser;
private context; private context;
private page; private page;
private options; private options;
private consoleMessages;
constructor(options?: BrowserOptions); constructor(options?: BrowserOptions);
launch(): Promise<void>; launch(): Promise<void>;
private setupConsoleListener;
close(): Promise<void>; close(): Promise<void>;
private ensurePage; private ensurePage;
/** Get the current page instance (for advanced usage) */ /** Get the current page instance (for advanced usage) */
@@ -43,6 +45,8 @@ export declare class ClaudeBrowser {
wait(ms?: number): Promise<void>; wait(ms?: number): Promise<void>;
newPage(): Promise<void>; newPage(): Promise<void>;
eval(script: string): Promise<unknown>; eval(script: string): Promise<unknown>;
getConsole(level?: string, clear?: boolean): ConsoleMessage[];
clearConsole(): void;
executeCommand(cmd: BrowserCommand): Promise<CommandResponse>; executeCommand(cmd: BrowserCommand): Promise<CommandResponse>;
} }
//# sourceMappingURL=browser.d.ts.map //# sourceMappingURL=browser.d.ts.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,cAAc,EAAE,KAAK,IAAI,EAAU,MAAM,YAAY,CAAC;AAElF,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE/F,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,OAAO,CAA2B;gBAE9B,OAAO,GAAE,cAAmB;IAQlC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAWvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B,OAAO,CAAC,UAAU;IAOlB,yDAAyD;IACzD,OAAO,IAAI,IAAI,GAAG,IAAI;IAItB,gEAAgE;IAChE,UAAU,IAAI,cAAc,GAAG,IAAI;IAI7B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAM1D,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAOjD,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnD,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAiB/C,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAOvF,MAAM,IAAI,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAKjD,OAAO,CAAC,IAAI,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IAMtC,IAAI,IAAI,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAMhC,OAAO,IAAI,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAMnC,MAAM,IAAI,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAMlC,IAAI,CAAC,EAAE,SAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAK9B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAOxB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKtC,cAAc,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;CAsIpE"} {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,cAAc,EAAE,KAAK,IAAI,EAAU,MAAM,YAAY,CAAC;AAElF,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,eAAe,EACf,cAAc,EACd,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,eAAe,CAAwB;gBAEnC,OAAO,GAAE,cAAmB;IAQlC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAY7B,OAAO,CAAC,oBAAoB;IAYtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B,OAAO,CAAC,UAAU;IAOlB,yDAAyD;IACzD,OAAO,IAAI,IAAI,GAAG,IAAI;IAItB,gEAAgE;IAChE,UAAU,IAAI,cAAc,GAAG,IAAI;IAI7B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAM1D,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAOjD,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnD,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAiB/C,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAOvF,MAAM,IAAI,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAKjD,OAAO,CAAC,IAAI,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IAMtC,IAAI,IAAI,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAMhC,OAAO,IAAI,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAMnC,MAAM,IAAI,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAMlC,IAAI,CAAC,EAAE,SAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAK9B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAQxB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK5C,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,UAAQ,GAAG,cAAc,EAAE;IAW3D,YAAY,IAAI,IAAI;IAId,cAAc,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;CA0IpE"}
+31
View File
@@ -6,6 +6,7 @@ export class ClaudeBrowser {
context = null; context = null;
page = null; page = null;
options; options;
consoleMessages = [];
constructor(options = {}) { constructor(options = {}) {
this.options = { this.options = {
headless: options.headless ?? true, headless: options.headless ?? true,
@@ -22,6 +23,18 @@ export class ClaudeBrowser {
}, },
}); });
this.page = await this.context.newPage(); this.page = await this.context.newPage();
this.setupConsoleListener(this.page);
}
setupConsoleListener(page) {
page.on('console', (msg) => {
const location = msg.location();
this.consoleMessages.push({
level: msg.type(),
text: msg.text(),
timestamp: Date.now(),
location: location.url ? `${location.url}:${location.lineNumber}` : undefined,
});
});
} }
async close() { async close() {
if (this.browser) { if (this.browser) {
@@ -113,11 +126,25 @@ export class ClaudeBrowser {
throw new Error('Browser not launched. Call launch() first.'); throw new Error('Browser not launched. Call launch() first.');
} }
this.page = await this.context.newPage(); this.page = await this.context.newPage();
this.setupConsoleListener(this.page);
} }
async eval(script) { async eval(script) {
const page = this.ensurePage(); const page = this.ensurePage();
return page.evaluate(script); return page.evaluate(script);
} }
getConsole(level, clear = false) {
let messages = this.consoleMessages;
if (level && level !== 'all') {
messages = messages.filter((m) => m.level === level);
}
if (clear) {
this.consoleMessages = [];
}
return messages;
}
clearConsole() {
this.consoleMessages = [];
}
async executeCommand(cmd) { async executeCommand(cmd) {
try { try {
switch (cmd.cmd) { switch (cmd.cmd) {
@@ -177,6 +204,10 @@ export class ClaudeBrowser {
const result = await this.eval(cmd.script); const result = await this.eval(cmd.script);
return { ok: true, result }; return { ok: true, result };
} }
case 'console': {
const messages = this.getConsole(cmd.level, cmd.clear);
return { ok: true, count: messages.length, messages };
}
case 'favicon': { case 'favicon': {
const result = await image.createFavicon(cmd.input, cmd.outputDir); const result = await image.createFavicon(cmd.input, cmd.outputDir);
return { ok: true, files: result.files, outputDir: result.outputDir }; return { ok: true, files: result.files, outputDir: result.outputDir };
+1 -1
View File
File diff suppressed because one or more lines are too long
Vendored
+37
View File
@@ -106,6 +106,19 @@ server.tool('eval', 'Execute JavaScript in the browser context', { script: z.str
const result = await browser.eval(script); const result = await browser.eval(script);
return textResult(JSON.stringify({ ok: true, result })); return textResult(JSON.stringify({ ok: true, result }));
})); }));
// Console
server.tool('console', 'Get captured console messages (log, warn, error, etc.) from the browser', {
level: z
.enum(['log', 'info', 'warn', 'error', 'debug', 'all'])
.optional()
.default('all')
.describe('Filter by log level'),
clear: z.boolean().optional().default(false).describe('Clear messages after retrieving'),
}, withLogging('console', async ({ level, clear }) => {
await ensureLaunched();
const messages = browser.getConsole(level, clear);
return textResult(JSON.stringify({ ok: true, count: messages.length, messages }));
}));
// Utility // Utility
server.tool('wait', 'Wait for a specified time in milliseconds', { ms: z.number().optional().default(1000) }, withLogging('wait', async ({ ms }) => { server.tool('wait', 'Wait for a specified time in milliseconds', { ms: z.number().optional().default(1000) }, withLogging('wait', async ({ ms }) => {
await ensureLaunched(); await ensureLaunched();
@@ -337,6 +350,30 @@ server.resource('Full Page HTML', 'browser://html/full', { description: 'Complet
], ],
}; };
}); });
// Resource: browser://console - Captured console messages
server.resource('Console Messages', 'browser://console', { description: 'Console messages captured from the browser', mimeType: 'application/json' }, async () => {
if (!launched) {
return {
contents: [
{
uri: 'browser://console',
mimeType: 'application/json',
text: JSON.stringify({ launched: false, messages: [] }),
},
],
};
}
const messages = browser.getConsole('all', false);
return {
contents: [
{
uri: 'browser://console',
mimeType: 'application/json',
text: JSON.stringify({ launched: true, count: messages.length, messages }),
},
],
};
});
// Resource: browser://screenshot - Current page screenshot (base64) // Resource: browser://screenshot - Current page screenshot (base64)
server.resource('Page Screenshot', 'browser://screenshot', { description: 'Screenshot of the current page as base64 PNG', mimeType: 'image/png' }, async () => { server.resource('Page Screenshot', 'browser://screenshot', { description: 'Screenshot of the current page as base64 PNG', mimeType: 'image/png' }, async () => {
if (!launched) { if (!launched) {
+1 -1
View File
File diff suppressed because one or more lines are too long
+13 -1
View File
@@ -100,7 +100,18 @@ export interface ThumbnailCommand {
output: string; output: string;
size?: 'small' | 'medium' | 'large'; size?: 'small' | 'medium' | 'large';
} }
export type BrowserCommand = GotoCommand | ClickCommand | TypeCommand | QueryCommand | ScreenshotCommand | UrlCommand | HtmlCommand | BackCommand | ForwardCommand | ReloadCommand | WaitCommand | NewPageCommand | CloseCommand | EvalCommand | FaviconCommand | ConvertCommand | ResizeCommand | CropCommand | CompressCommand | ThumbnailCommand; export interface ConsoleCommand {
cmd: 'console';
clear?: boolean;
level?: 'log' | 'info' | 'warn' | 'error' | 'debug' | 'all';
}
export interface ConsoleMessage {
level: string;
text: string;
timestamp: number;
location?: string;
}
export type BrowserCommand = GotoCommand | ClickCommand | TypeCommand | QueryCommand | ScreenshotCommand | UrlCommand | HtmlCommand | BackCommand | ForwardCommand | ReloadCommand | WaitCommand | NewPageCommand | CloseCommand | EvalCommand | FaviconCommand | ConvertCommand | ResizeCommand | CropCommand | CompressCommand | ThumbnailCommand | ConsoleCommand;
export interface SuccessResponse { export interface SuccessResponse {
ok: true; ok: true;
url?: string; url?: string;
@@ -116,6 +127,7 @@ export interface SuccessResponse {
height?: number; height?: number;
format?: string; format?: string;
size?: number; size?: number;
messages?: ConsoleMessage[];
} }
export interface ErrorResponse { export interface ErrorResponse {
ok: false; ok: false;
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAGD,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,OAAO,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,OAAO,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,YAAY,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,KAAK,CAAC;CACZ;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,SAAS,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,QAAQ,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,SAAS,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,OAAO,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAGD,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,SAAS,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,SAAS,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;CAC1C;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,QAAQ,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;CAC3D;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,WAAW,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;CACrC;AAED,MAAM,MAAM,cAAc,GACtB,WAAW,GACX,YAAY,GACZ,WAAW,GACX,YAAY,GACZ,iBAAiB,GACjB,UAAU,GACV,WAAW,GACX,WAAW,GACX,cAAc,GACd,aAAa,GACb,WAAW,GACX,cAAc,GACd,YAAY,GACZ,WAAW,GACX,cAAc,GACd,cAAc,GACd,aAAa,GACb,WAAW,GACX,eAAe,GACf,gBAAgB,CAAC;AAGrB,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,IAAI,CAAC;IACT,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG,aAAa,CAAC"} {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAGD,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,OAAO,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,OAAO,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,YAAY,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,KAAK,CAAC;CACZ;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,SAAS,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,QAAQ,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,SAAS,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,OAAO,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAGD,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,SAAS,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,SAAS,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;CAC1C;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,QAAQ,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;CAC3D;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,WAAW,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,SAAS,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;CAC7D;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,cAAc,GACtB,WAAW,GACX,YAAY,GACZ,WAAW,GACX,YAAY,GACZ,iBAAiB,GACjB,UAAU,GACV,WAAW,GACX,WAAW,GACX,cAAc,GACd,aAAa,GACb,WAAW,GACX,cAAc,GACd,YAAY,GACZ,WAAW,GACX,cAAc,GACd,cAAc,GACd,aAAa,GACb,WAAW,GACX,eAAe,GACf,gBAAgB,GAChB,cAAc,CAAC;AAGnB,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,IAAI,CAAC;IACT,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG,aAAa,CAAC"}
+41 -1
View File
@@ -1,13 +1,20 @@
import { resolve } from 'node:path'; import { resolve } from 'node:path';
import { type Browser, type BrowserContext, type Page, webkit } from 'playwright'; import { type Browser, type BrowserContext, type Page, webkit } from 'playwright';
import * as image from './image.js'; import * as image from './image.js';
import type { BrowserCommand, BrowserOptions, CommandResponse, ElementInfo } from './types.js'; import type {
BrowserCommand,
BrowserOptions,
CommandResponse,
ConsoleMessage,
ElementInfo,
} from './types.js';
export class ClaudeBrowser { export class ClaudeBrowser {
private browser: Browser | null = null; private browser: Browser | null = null;
private context: BrowserContext | null = null; private context: BrowserContext | null = null;
private page: Page | null = null; private page: Page | null = null;
private options: Required<BrowserOptions>; private options: Required<BrowserOptions>;
private consoleMessages: ConsoleMessage[] = [];
constructor(options: BrowserOptions = {}) { constructor(options: BrowserOptions = {}) {
this.options = { this.options = {
@@ -26,6 +33,19 @@ export class ClaudeBrowser {
}, },
}); });
this.page = await this.context.newPage(); this.page = await this.context.newPage();
this.setupConsoleListener(this.page);
}
private setupConsoleListener(page: Page): void {
page.on('console', (msg) => {
const location = msg.location();
this.consoleMessages.push({
level: msg.type(),
text: msg.text(),
timestamp: Date.now(),
location: location.url ? `${location.url}:${location.lineNumber}` : undefined,
});
});
} }
async close(): Promise<void> { async close(): Promise<void> {
@@ -135,6 +155,7 @@ export class ClaudeBrowser {
throw new Error('Browser not launched. Call launch() first.'); throw new Error('Browser not launched. Call launch() first.');
} }
this.page = await this.context.newPage(); this.page = await this.context.newPage();
this.setupConsoleListener(this.page);
} }
async eval(script: string): Promise<unknown> { async eval(script: string): Promise<unknown> {
@@ -142,6 +163,21 @@ export class ClaudeBrowser {
return page.evaluate(script); return page.evaluate(script);
} }
getConsole(level?: string, clear = false): ConsoleMessage[] {
let messages = this.consoleMessages;
if (level && level !== 'all') {
messages = messages.filter((m) => m.level === level);
}
if (clear) {
this.consoleMessages = [];
}
return messages;
}
clearConsole(): void {
this.consoleMessages = [];
}
async executeCommand(cmd: BrowserCommand): Promise<CommandResponse> { async executeCommand(cmd: BrowserCommand): Promise<CommandResponse> {
try { try {
switch (cmd.cmd) { switch (cmd.cmd) {
@@ -201,6 +237,10 @@ export class ClaudeBrowser {
const result = await this.eval(cmd.script); const result = await this.eval(cmd.script);
return { ok: true, result }; return { ok: true, result };
} }
case 'console': {
const messages = this.getConsole(cmd.level, cmd.clear);
return { ok: true, count: messages.length, messages };
}
case 'favicon': { case 'favicon': {
const result = await image.createFavicon(cmd.input, cmd.outputDir); const result = await image.createFavicon(cmd.input, cmd.outputDir);
return { ok: true, files: result.files, outputDir: result.outputDir }; return { ok: true, files: result.files, outputDir: result.outputDir };
+49
View File
@@ -183,6 +183,25 @@ server.tool(
}) })
); );
// Console
server.tool(
'console',
'Get captured console messages (log, warn, error, etc.) from the browser',
{
level: z
.enum(['log', 'info', 'warn', 'error', 'debug', 'all'])
.optional()
.default('all')
.describe('Filter by log level'),
clear: z.boolean().optional().default(false).describe('Clear messages after retrieving'),
},
withLogging('console', async ({ level, clear }) => {
await ensureLaunched();
const messages = browser.getConsole(level, clear);
return textResult(JSON.stringify({ ok: true, count: messages.length, messages }));
})
);
// Utility // Utility
server.tool( server.tool(
'wait', 'wait',
@@ -512,6 +531,36 @@ server.resource(
} }
); );
// Resource: browser://console - Captured console messages
server.resource(
'Console Messages',
'browser://console',
{ description: 'Console messages captured from the browser', mimeType: 'application/json' },
async () => {
if (!launched) {
return {
contents: [
{
uri: 'browser://console',
mimeType: 'application/json',
text: JSON.stringify({ launched: false, messages: [] }),
},
],
};
}
const messages = browser.getConsole('all', false);
return {
contents: [
{
uri: 'browser://console',
mimeType: 'application/json',
text: JSON.stringify({ launched: true, count: messages.length, messages }),
},
],
};
}
);
// Resource: browser://screenshot - Current page screenshot (base64) // Resource: browser://screenshot - Current page screenshot (base64)
server.resource( server.resource(
'Page Screenshot', 'Page Screenshot',
+17 -1
View File
@@ -124,6 +124,19 @@ export interface ThumbnailCommand {
size?: 'small' | 'medium' | 'large'; size?: 'small' | 'medium' | 'large';
} }
export interface ConsoleCommand {
cmd: 'console';
clear?: boolean;
level?: 'log' | 'info' | 'warn' | 'error' | 'debug' | 'all';
}
export interface ConsoleMessage {
level: string;
text: string;
timestamp: number;
location?: string;
}
export type BrowserCommand = export type BrowserCommand =
| GotoCommand | GotoCommand
| ClickCommand | ClickCommand
@@ -144,7 +157,8 @@ export type BrowserCommand =
| ResizeCommand | ResizeCommand
| CropCommand | CropCommand
| CompressCommand | CompressCommand
| ThumbnailCommand; | ThumbnailCommand
| ConsoleCommand;
// Response types // Response types
export interface SuccessResponse { export interface SuccessResponse {
@@ -163,6 +177,8 @@ export interface SuccessResponse {
height?: number; height?: number;
format?: string; format?: string;
size?: number; size?: number;
// Console fields
messages?: ConsoleMessage[];
} }
export interface ErrorResponse { export interface ErrorResponse {