feat: add preview tool — navigate + screenshot in one call with optional POST
New MCP tool `preview` combines goto + screenshot with viewport control. Optionally POSTs result to any HTTP endpoint (e.g. HUD/visor) via previewUrl. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,249 +1,80 @@
|
|||||||
# Plan: Playwright Debugging Features
|
# Plan: Preview Tool — Single-Call Screenshot-to-Visor Pipeline
|
||||||
|
|
||||||
## Phase 1: Core Debugging (Console & Errors)
|
## Context
|
||||||
|
|
||||||
### Description
|
Currently, previewing an HTML mockup or live URL on the MARAUDER VISOR requires 4 sequential tool calls: `launch` → `goto` → `screenshot` → `bash curl POST /image`. This is slow, verbose, and prone to Claude pausing between steps to narrate.
|
||||||
Implement fundamental debugging capabilities: console message capture and uncaught exception handling. These form the foundation for debugging client-side issues.
|
|
||||||
|
|
||||||
### Steps
|
**Goal:** Add a `preview` tool to the browse MCP that does the entire pipeline in one call. Optionally pushes to the visor automatically.
|
||||||
|
|
||||||
#### Step 1.1: Add Console Command
|
## Design
|
||||||
- **Objective**: Capture and retrieve console messages from browser
|
|
||||||
- **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
|
|
||||||
- **Dependencies**: None
|
|
||||||
- **Status**: COMPLETE
|
|
||||||
- **Implementation**:
|
|
||||||
- Add `ConsoleCommand` type with `level` filter and `clear` option
|
|
||||||
- Store messages via `page.on('console')` listener
|
|
||||||
- Return messages with level, text, timestamp, location
|
|
||||||
|
|
||||||
#### Step 1.2: Add Page Errors Command
|
### New Tool: `preview`
|
||||||
- **Objective**: Capture uncaught exceptions and unhandled promise rejections
|
|
||||||
- **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
|
|
||||||
- **Dependencies**: Step 1.1
|
|
||||||
- **Implementation**:
|
|
||||||
- Add `ErrorsCommand` type with `clear` option
|
|
||||||
- Listen to `page.on('pageerror')` for uncaught exceptions
|
|
||||||
- Store error message, stack trace, timestamp
|
|
||||||
- Add `browser://errors` MCP resource
|
|
||||||
|
|
||||||
## Phase 2: Network Monitoring
|
browse is a standalone npm package — it must NOT know about the visor. The `preview` tool is a convenience wrapper for "goto + screenshot with viewport control" in a single call.
|
||||||
|
|
||||||
### Description
|
```typescript
|
||||||
Implement network request/response capture for debugging API calls, identifying failed requests, and inspecting payloads.
|
server.tool('preview', 'Navigate to URL and screenshot in one call with custom viewport', {
|
||||||
|
url: z.string(), // URL or file:///path
|
||||||
|
width: z.number().optional().default(1280), // Viewport width
|
||||||
|
height: z.number().optional().default(800), // Viewport height
|
||||||
|
fullPage: z.boolean().optional().default(false), // Full page capture
|
||||||
|
output: z.string().optional().default('/tmp/preview.png'), // Screenshot path
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
### Steps
|
**Returns:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ok": true,
|
||||||
|
"path": "/tmp/preview.png",
|
||||||
|
"url": "https://kwit.fit",
|
||||||
|
"title": "kwit*fit"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Step 2.1: Add Network Logging
|
### Behavior
|
||||||
- **Objective**: Capture all network requests and responses
|
|
||||||
- **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
|
|
||||||
- **Dependencies**: None
|
|
||||||
- **Implementation**:
|
|
||||||
- Add `NetworkCommand` type with `filter` and `clear` options
|
|
||||||
- Listen to `page.on('request')` and `page.on('response')`
|
|
||||||
- Store: url, method, status, resourceType, timing, headers
|
|
||||||
- Add optional body capture for XHR/fetch (with size limit)
|
|
||||||
- Add `browser://network` MCP resource
|
|
||||||
|
|
||||||
#### Step 2.2: Add Failed Requests Filter
|
1. If browser not launched → launch headless with given viewport dimensions
|
||||||
- **Objective**: Quick access to failed/error requests
|
2. If browser already running with different viewport → resize to requested dimensions
|
||||||
- **Files**: `src/browser.ts`, `src/mcp.ts`
|
3. Navigate to URL (supports `https://`, `http://`, `file:///`)
|
||||||
- **Dependencies**: Step 2.1
|
4. Wait for `networkidle` (with 5s timeout for SPAs)
|
||||||
- **Implementation**:
|
5. Take screenshot → save to `output` path
|
||||||
- Add `failed` filter option to NetworkCommand
|
6. Return result with path, url, and page title
|
||||||
- Include requests with status >= 400 or network errors
|
|
||||||
- Add `browser://network/failed` MCP resource
|
|
||||||
|
|
||||||
#### Step 2.3: Add Request Interception
|
### Visor Integration (marauder-plugin side, NOT in browse)
|
||||||
- **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
|
The visor push lives in the marauder-plugin preview skill as a simple bash curl after the browse tool returns. This keeps browse generic and visor-agnostic.
|
||||||
|
|
||||||
### Description
|
## Files to Modify
|
||||||
Add performance timing and metrics collection for identifying bottlenecks and measuring page load characteristics.
|
|
||||||
|
|
||||||
### Steps
|
| File | Change |
|
||||||
|
|------|--------|
|
||||||
|
| `src/types.ts` | Add `PreviewCommand` interface + add to `BrowserCommand` union |
|
||||||
|
| `src/browser.ts` | Add `preview()` method + `pushToVisor()` helper + case in `executeCommand()` |
|
||||||
|
| `src/mcp.ts` | Register `preview` tool with zod schema |
|
||||||
|
| `src/index.ts` | No change needed (types auto-exported) |
|
||||||
|
|
||||||
#### Step 3.1: Add Performance Metrics
|
## Files Unchanged
|
||||||
- **Objective**: Return page performance timing data
|
|
||||||
- **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
|
|
||||||
- **Dependencies**: None
|
|
||||||
- **Implementation**:
|
|
||||||
- Add `MetricsCommand` type
|
|
||||||
- Collect via `performance.timing` and `performance.getEntriesByType()`
|
|
||||||
- Return: domContentLoaded, load, firstPaint, firstContentfulPaint
|
|
||||||
- Include DOM stats: nodeCount, scriptCount, styleCount
|
|
||||||
|
|
||||||
#### Step 3.2: Add Resource Timing
|
- `src/cli.ts` — CLI doesn't need preview (it's an MCP-first tool)
|
||||||
- **Objective**: Get timing breakdown for individual resources
|
- `src/image.ts` — No image processing needed
|
||||||
- **Files**: `src/browser.ts`, `src/mcp.ts`
|
- `src/safari.ts`, `src/firefox.ts` — Unrelated
|
||||||
- **Dependencies**: Step 3.1
|
|
||||||
- **Implementation**:
|
|
||||||
- Add `resources` option to MetricsCommand
|
|
||||||
- Use `performance.getEntriesByType('resource')`
|
|
||||||
- Return: name, duration, transferSize, initiatorType
|
|
||||||
|
|
||||||
## Phase 4: Accessibility
|
## Risks
|
||||||
|
|
||||||
### Description
|
| Risk | Mitigation |
|
||||||
Implement accessibility tree inspection for debugging screen reader and a11y issues.
|
|------|------------|
|
||||||
|
| Browser already launched with wrong viewport | Resize viewport before navigating |
|
||||||
|
| `file:///` URLs blocked by Playwright | WebKit allows file:// by default |
|
||||||
|
| Visor not running | Silent fail, return `visor: false` in response |
|
||||||
|
| Node `fetch` not available | Node 18+ has native fetch; browse requires Node 18+ |
|
||||||
|
|
||||||
### Steps
|
## Verification
|
||||||
|
|
||||||
#### Step 4.1: Add Accessibility Snapshot
|
1. `npm run build` — compiles
|
||||||
- **Objective**: Dump accessibility tree for page or element
|
2. `npm run check` — passes lint/format
|
||||||
- **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
|
3. Manual test: call `preview` tool from Claude Code with a URL
|
||||||
- **Dependencies**: None
|
4. Manual test: call `preview` with `file:///tmp/mockup.html`
|
||||||
- **Implementation**:
|
5. Verify visor displays the screenshot
|
||||||
- Add `A11yCommand` with optional `selector` for subtree
|
6. `npm run test` if tests exist
|
||||||
- Use `page.accessibility.snapshot()`
|
7. Bump version, publish to npm
|
||||||
- Return tree with role, name, value, description
|
|
||||||
- Add `browser://a11y` MCP resource
|
|
||||||
|
|
||||||
## Phase 5: Dialog Handling
|
|
||||||
|
|
||||||
### Description
|
|
||||||
Implement automatic handling of browser dialogs (alert, confirm, prompt) to prevent blocking during automation.
|
|
||||||
|
|
||||||
### Steps
|
|
||||||
|
|
||||||
#### Step 5.1: Add Dialog Command
|
|
||||||
- **Objective**: Configure how dialogs are handled
|
|
||||||
- **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
|
|
||||||
- **Dependencies**: None
|
|
||||||
- **Implementation**:
|
|
||||||
- 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()`
|
|
||||||
|
|
||||||
#### Step 8.2: Add Emulate Command
|
|
||||||
- **Objective**: Emulate specific devices
|
|
||||||
- **Files**: `src/types.ts`, `src/browser.ts`, `src/mcp.ts`
|
|
||||||
- **Dependencies**: Step 8.1
|
|
||||||
- **Implementation**:
|
|
||||||
- Add `EmulateCommand` with `device` name
|
|
||||||
- Use Playwright's device descriptors
|
|
||||||
- Apply viewport, userAgent, deviceScaleFactor, touch support
|
|
||||||
|
|
||||||
## Phase 9: Documentation
|
|
||||||
|
|
||||||
### Description
|
|
||||||
Update all documentation with new commands and examples.
|
|
||||||
|
|
||||||
### Steps
|
|
||||||
|
|
||||||
#### Step 9.1: Update README
|
|
||||||
- **Objective**: Document all commands with examples
|
|
||||||
- **Files**: `README.md`
|
|
||||||
- **Dependencies**: Phases 1-8
|
|
||||||
- **Implementation**:
|
|
||||||
- Add command reference grouped by category
|
|
||||||
- Include curl/MCP examples
|
|
||||||
- Document response formats
|
|
||||||
|
|
||||||
#### Step 9.2: Update CLAUDE.md
|
|
||||||
- **Objective**: Update developer documentation
|
|
||||||
- **Files**: `CLAUDE.md`
|
|
||||||
- **Dependencies**: Step 9.1
|
|
||||||
- **Implementation**:
|
|
||||||
- Update architecture notes
|
|
||||||
- Document new command types
|
|
||||||
|
|||||||
+36
@@ -2,6 +2,42 @@
|
|||||||
|
|
||||||
Research notes on making Playwright WebKit less detectable by anti-bot systems. Compiled April 2026.
|
Research notes on making Playwright WebKit less detectable by anti-bot systems. Compiled April 2026.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## NOTICE — Scope & Intent
|
||||||
|
|
||||||
|
**This is a web automation research document covering browser fingerprinting and bot detection evasion techniques.** It is compiled as technical reference for the `@saiden/browse` npm package -- a published, open-source headless browser tool for Claude Code.
|
||||||
|
|
||||||
|
### Context
|
||||||
|
|
||||||
|
Bot detection evasion is standard practice across several legitimate industries:
|
||||||
|
|
||||||
|
- **QA and test automation** -- Automated tests that trigger bot detection produce false failures, making stealth patches essential for reliable CI/CD pipelines
|
||||||
|
- **Price monitoring and comparison** -- E-commerce price tracking services (Honey, Camelcamelcamel, Google Shopping) routinely work around anti-bot measures
|
||||||
|
- **Academic web research** -- Researchers scraping public data for studies in computational social science, NLP corpus building, and web accessibility auditing
|
||||||
|
- **Accessibility testing** -- Automated accessibility scanners must behave like real browsers to accurately assess how sites serve content to assistive technologies
|
||||||
|
- **Search engine indexing** -- Crawlers from Google, Bing, and others implement similar techniques to access content behind bot detection layers
|
||||||
|
|
||||||
|
The Playwright, Puppeteer, and Selenium communities have maintained stealth plugins as standard tooling for years. This document compiles publicly known techniques specific to WebKit, which is less covered than Chromium in existing literature.
|
||||||
|
|
||||||
|
### What This Covers
|
||||||
|
|
||||||
|
- Browser fingerprinting vectors and their detection severity
|
||||||
|
- JavaScript-level patches for `addInitScript()` (Playwright's official API)
|
||||||
|
- Context configuration for realistic browser profiles
|
||||||
|
- Limitations of JS-level evasion (TLS fingerprinting, IP reputation, behavioral analysis)
|
||||||
|
- Anti-bot provider comparison for automated testing planning
|
||||||
|
|
||||||
|
### What This Is NOT
|
||||||
|
|
||||||
|
- Not a guide for bypassing paywalls or accessing content you are not authorised to view
|
||||||
|
- Not a credential stuffing or account takeover toolkit
|
||||||
|
- Not intended for scraping personal data in violation of privacy regulations (GDPR, CCPA)
|
||||||
|
|
||||||
|
All techniques referenced are publicly documented in the Playwright ecosystem, security research literature, and the web automation community. Sources are cited at the end of this document.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Current State
|
## Current State
|
||||||
|
|
||||||
Browse uses **Playwright WebKit** with a bare context — no stealth patches. This is trivially detected by every major anti-bot system (Cloudflare, DataDome, PerimeterX/HUMAN, Akamai).
|
Browse uses **Playwright WebKit** with a bare context — no stealth patches. This is trivially detected by every major anti-bot system (Cloudflare, DataDome, PerimeterX/HUMAN, Akamai).
|
||||||
|
|||||||
@@ -1,39 +1,33 @@
|
|||||||
# TODO: Playwright Debugging Features
|
# TODO: Preview Tool
|
||||||
|
|
||||||
## Phase 1: Core Debugging (Console & Errors)
|
## Phase 1: Add `preview` tool
|
||||||
- [x] Step 1.1: Add Console Command
|
- [ ] Add `PreviewCommand` interface to `src/types.ts`
|
||||||
- [x] Step 1.2: Add Page Errors Command
|
- [ ] Add to `BrowserCommand` discriminated union
|
||||||
|
- [ ] Add `pushToVisor()` helper method to `ClaudeBrowser` in `src/browser.ts`
|
||||||
|
- [ ] Add `preview()` method to `ClaudeBrowser`
|
||||||
|
- [ ] Add `case 'preview'` in `executeCommand()` switch
|
||||||
|
- [ ] Register `preview` MCP tool in `src/mcp.ts` with zod schema
|
||||||
|
- [ ] `npm run build` — compiles clean
|
||||||
|
- [ ] `npm run check` — lint/format pass
|
||||||
|
|
||||||
## Phase 2: Network Monitoring
|
## Phase 2: Test & Publish
|
||||||
- [x] Step 2.1: Add Network Logging
|
- [ ] Test with URL: `preview({ url: "https://kwit.fit", title: "TEST" })`
|
||||||
- [x] Step 2.2: Add Failed Requests Filter
|
- [ ] Test with file: `preview({ url: "file:///tmp/test.html" })`
|
||||||
- [x] Step 2.3: Add Request Interception
|
- [ ] Test visor push works
|
||||||
|
- [ ] Test visor-down graceful fallback (`visor: false` in response)
|
||||||
|
- [ ] Test viewport resize when browser already running
|
||||||
|
- [ ] Bump version, publish to npm
|
||||||
|
- [ ] Update marauder-plugin `.mcp.json` if needed
|
||||||
|
|
||||||
## Phase 3: Performance & Metrics
|
## Phase 3: Skill cleanup
|
||||||
- [x] Step 3.1: Add Performance Metrics
|
- [ ] Delete `marauder-plugin/skills/preview/preview.py`
|
||||||
- [x] Step 3.2: Add Resource Timing
|
- [ ] Rewrite `marauder-plugin/skills/preview/SKILL.md` as simple one-liner reference
|
||||||
|
|
||||||
## Phase 4: Accessibility
|
### ETA
|
||||||
- [x] Step 4.1: Add Accessibility Snapshot
|
|
||||||
|
|
||||||
## Phase 5: Dialog Handling
|
| Phase | Naive | Coop | Sessions | Notes |
|
||||||
- [x] Step 5.1: Add Dialog Command
|
|-------|-------|------|----------|-------|
|
||||||
|
| 1. Add tool | 2h | ~30m | 1 | Mechanical — follow existing pattern exactly |
|
||||||
## Phase 6: Storage & Cookies
|
| 2. Test & publish | 1h | ~15m | 1 | Same session |
|
||||||
- [x] Step 6.1: Add Cookies Command
|
| 3. Skill cleanup | 30m | ~10m | 1 | Delete + rewrite |
|
||||||
- [x] Step 6.2: Add Storage Command
|
| **Total** | **3.5h** | **~55m** | **1** | Single session, single commit |
|
||||||
|
|
||||||
## Phase 7: Advanced Interactions
|
|
||||||
- [x] Step 7.1: Add Hover Command
|
|
||||||
- [x] Step 7.2: Add Select Command
|
|
||||||
- [x] Step 7.3: Add Keys Command
|
|
||||||
- [x] Step 7.4: Add Upload Command
|
|
||||||
- [x] Step 7.5: Add Scroll Command
|
|
||||||
|
|
||||||
## Phase 8: Viewport & Emulation
|
|
||||||
- [x] Step 8.1: Add Viewport Command
|
|
||||||
- [x] Step 8.2: Add Emulate Command
|
|
||||||
|
|
||||||
## Phase 9: Documentation
|
|
||||||
- [x] Step 9.1: Update README
|
|
||||||
- [x] Step 9.2: Update CLAUDE.md
|
|
||||||
|
|||||||
Vendored
+7
@@ -118,6 +118,13 @@ export declare class ClaudeBrowser {
|
|||||||
private handleDialogCommand;
|
private handleDialogCommand;
|
||||||
private handleCookiesCommand;
|
private handleCookiesCommand;
|
||||||
private handleStorageCommand;
|
private handleStorageCommand;
|
||||||
|
private handlePreviewCommand;
|
||||||
|
/**
|
||||||
|
* POST a screenshot to a preview endpoint.
|
||||||
|
* Payload: { source: "file:///path", title, caption }
|
||||||
|
* Silent failure — returns false if endpoint is unreachable.
|
||||||
|
*/
|
||||||
|
private postPreview;
|
||||||
private handleImportCommand;
|
private handleImportCommand;
|
||||||
executeCommand(cmd: BrowserCommand): Promise<CommandResponse>;
|
executeCommand(cmd: BrowserCommand): Promise<CommandResponse>;
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
|
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
|
||||||
Vendored
+47
@@ -769,6 +769,51 @@ export class ClaudeBrowser {
|
|||||||
return { ok: false, error: 'Unknown storage action' };
|
return { ok: false, error: 'Unknown storage action' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async handlePreviewCommand(cmd) {
|
||||||
|
const page = this.ensurePage();
|
||||||
|
// Resize viewport if dimensions specified
|
||||||
|
if (cmd.width || cmd.height) {
|
||||||
|
const current = page.viewportSize();
|
||||||
|
const width = cmd.width || current?.width || 1280;
|
||||||
|
const height = cmd.height || current?.height || 800;
|
||||||
|
await page.setViewportSize({ width, height });
|
||||||
|
}
|
||||||
|
// Navigate
|
||||||
|
const nav = await this.goto(cmd.url);
|
||||||
|
// Screenshot
|
||||||
|
const outputPath = resolve(cmd.output || '/tmp/preview.png');
|
||||||
|
await page.screenshot({ path: outputPath, fullPage: cmd.fullPage || false });
|
||||||
|
// Optional: POST to preview endpoint
|
||||||
|
let posted = false;
|
||||||
|
if (cmd.previewUrl) {
|
||||||
|
posted = await this.postPreview(outputPath, cmd.previewUrl, cmd.title, cmd.caption);
|
||||||
|
}
|
||||||
|
return { ok: true, path: outputPath, url: nav.url, title: nav.title, posted };
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* POST a screenshot to a preview endpoint.
|
||||||
|
* Payload: { source: "file:///path", title, caption }
|
||||||
|
* Silent failure — returns false if endpoint is unreachable.
|
||||||
|
*/
|
||||||
|
async postPreview(imagePath, previewUrl, title, caption) {
|
||||||
|
try {
|
||||||
|
const payload = JSON.stringify({
|
||||||
|
source: `file://${resolve(imagePath)}`,
|
||||||
|
title: title || null,
|
||||||
|
caption: caption || null,
|
||||||
|
});
|
||||||
|
const res = await fetch(previewUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: payload,
|
||||||
|
signal: AbortSignal.timeout(3000),
|
||||||
|
});
|
||||||
|
return res.ok;
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
async handleImportCommand(cmd) {
|
async handleImportCommand(cmd) {
|
||||||
const context = this.getContext();
|
const context = this.getContext();
|
||||||
if (!context)
|
if (!context)
|
||||||
@@ -1012,6 +1057,8 @@ export class ClaudeBrowser {
|
|||||||
}
|
}
|
||||||
case 'import':
|
case 'import':
|
||||||
return this.handleImportCommand(cmd);
|
return this.handleImportCommand(cmd);
|
||||||
|
case 'preview':
|
||||||
|
return this.handlePreviewCommand(cmd);
|
||||||
default: {
|
default: {
|
||||||
const _exhaustive = cmd;
|
const _exhaustive = cmd;
|
||||||
return { ok: false, error: `Unknown command: ${_exhaustive.cmd}` };
|
return { ok: false, error: `Unknown command: ${_exhaustive.cmd}` };
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+25
@@ -168,6 +168,31 @@ server.tool('screenshot', 'Take a screenshot of the current page', {
|
|||||||
const result = await browser.screenshot(path, fullPage);
|
const result = await browser.screenshot(path, fullPage);
|
||||||
return textResult(JSON.stringify({ ok: true, path: result.path }));
|
return textResult(JSON.stringify({ ok: true, path: result.path }));
|
||||||
}));
|
}));
|
||||||
|
// Preview — navigate + screenshot in one call, optional POST to preview endpoint
|
||||||
|
server.tool('preview', 'Navigate to a URL and take a screenshot in one call. Optionally POST the result to a preview endpoint.', {
|
||||||
|
url: z.string().describe('URL or file:///path to preview'),
|
||||||
|
width: z.number().optional().default(1280).describe('Viewport width'),
|
||||||
|
height: z.number().optional().default(800).describe('Viewport height'),
|
||||||
|
fullPage: z.boolean().optional().default(false),
|
||||||
|
output: z.string().optional().default('/tmp/preview.png').describe('Screenshot output path'),
|
||||||
|
previewUrl: z.string().optional().describe('HTTP endpoint to POST screenshot result to'),
|
||||||
|
title: z.string().optional().describe('Title sent with preview POST'),
|
||||||
|
caption: z.string().optional().describe('Caption sent with preview POST'),
|
||||||
|
}, withLogging('preview', async ({ url, width, height, fullPage, output, previewUrl, title, caption }) => {
|
||||||
|
await ensureLaunched();
|
||||||
|
const result = await browser.executeCommand({
|
||||||
|
cmd: 'preview',
|
||||||
|
url,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
fullPage,
|
||||||
|
output,
|
||||||
|
previewUrl,
|
||||||
|
title,
|
||||||
|
caption,
|
||||||
|
});
|
||||||
|
return textResult(JSON.stringify(result));
|
||||||
|
}));
|
||||||
// Eval
|
// Eval
|
||||||
server.tool('eval', 'Execute JavaScript in the browser context', { script: z.string() }, withLogging('eval', async ({ script }) => {
|
server.tool('eval', 'Execute JavaScript in the browser context', { script: z.string() }, withLogging('eval', async ({ script }) => {
|
||||||
await ensureLaunched();
|
await ensureLaunched();
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+13
-1
@@ -259,7 +259,18 @@ export interface ImportCommand {
|
|||||||
domain?: string;
|
domain?: string;
|
||||||
profile?: string;
|
profile?: 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 | NetworkCommand | InterceptCommand | ErrorsCommand | MetricsCommand | A11yCommand | DialogCommand | CookiesCommand | StorageCommand | HoverCommand | SelectCommand | KeysCommand | UploadCommand | ScrollCommand | ViewportCommand | EmulateCommand | ImportCommand;
|
export type BrowserCommand = GotoCommand | ClickCommand | TypeCommand | QueryCommand | ScreenshotCommand | UrlCommand | HtmlCommand | BackCommand | ForwardCommand | ReloadCommand | WaitCommand | NewPageCommand | CloseCommand | EvalCommand | FaviconCommand | ConvertCommand | ResizeCommand | CropCommand | CompressCommand | ThumbnailCommand | ConsoleCommand | NetworkCommand | InterceptCommand | ErrorsCommand | MetricsCommand | A11yCommand | DialogCommand | CookiesCommand | StorageCommand | HoverCommand | SelectCommand | KeysCommand | UploadCommand | ScrollCommand | ViewportCommand | EmulateCommand | ImportCommand | PreviewCommand;
|
||||||
|
export interface PreviewCommand {
|
||||||
|
cmd: 'preview';
|
||||||
|
url: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
fullPage?: boolean;
|
||||||
|
output?: string;
|
||||||
|
previewUrl?: string;
|
||||||
|
title?: string;
|
||||||
|
caption?: string;
|
||||||
|
}
|
||||||
export interface SuccessResponse {
|
export interface SuccessResponse {
|
||||||
ok: true;
|
ok: true;
|
||||||
url?: string;
|
url?: string;
|
||||||
@@ -269,6 +280,7 @@ export interface SuccessResponse {
|
|||||||
count?: number;
|
count?: number;
|
||||||
elements?: ElementInfo[];
|
elements?: ElementInfo[];
|
||||||
result?: unknown;
|
result?: unknown;
|
||||||
|
posted?: boolean;
|
||||||
files?: string[];
|
files?: string[];
|
||||||
outputDir?: string;
|
outputDir?: string;
|
||||||
width?: number;
|
width?: number;
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@saiden/browse",
|
"name": "@saiden/browse",
|
||||||
"version": "0.3.3",
|
"version": "0.4.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@saiden/browse",
|
"name": "@saiden/browse",
|
||||||
"version": "0.3.3",
|
"version": "0.4.0",
|
||||||
"license": "BUSL-1.1",
|
"license": "BUSL-1.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.26.0",
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@saiden/browse",
|
"name": "@saiden/browse",
|
||||||
"version": "0.3.3",
|
"version": "0.4.0",
|
||||||
"description": "Headless browser automation for Claude Code using Playwright WebKit",
|
"description": "Headless browser automation for Claude Code using Playwright WebKit",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import type {
|
|||||||
MetricsData,
|
MetricsData,
|
||||||
NetworkEntry,
|
NetworkEntry,
|
||||||
PageError,
|
PageError,
|
||||||
|
PreviewCommand,
|
||||||
StorageCommand,
|
StorageCommand,
|
||||||
} from './types.js';
|
} from './types.js';
|
||||||
|
|
||||||
@@ -878,6 +879,62 @@ export class ClaudeBrowser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async handlePreviewCommand(cmd: PreviewCommand): Promise<CommandResponse> {
|
||||||
|
const page = this.ensurePage();
|
||||||
|
|
||||||
|
// Resize viewport if dimensions specified
|
||||||
|
if (cmd.width || cmd.height) {
|
||||||
|
const current = page.viewportSize();
|
||||||
|
const width = cmd.width || current?.width || 1280;
|
||||||
|
const height = cmd.height || current?.height || 800;
|
||||||
|
await page.setViewportSize({ width, height });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigate
|
||||||
|
const nav = await this.goto(cmd.url);
|
||||||
|
|
||||||
|
// Screenshot
|
||||||
|
const outputPath = resolve(cmd.output || '/tmp/preview.png');
|
||||||
|
await page.screenshot({ path: outputPath, fullPage: cmd.fullPage || false });
|
||||||
|
|
||||||
|
// Optional: POST to preview endpoint
|
||||||
|
let posted = false;
|
||||||
|
if (cmd.previewUrl) {
|
||||||
|
posted = await this.postPreview(outputPath, cmd.previewUrl, cmd.title, cmd.caption);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ok: true, path: outputPath, url: nav.url, title: nav.title, posted };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST a screenshot to a preview endpoint.
|
||||||
|
* Payload: { source: "file:///path", title, caption }
|
||||||
|
* Silent failure — returns false if endpoint is unreachable.
|
||||||
|
*/
|
||||||
|
private async postPreview(
|
||||||
|
imagePath: string,
|
||||||
|
previewUrl: string,
|
||||||
|
title?: string,
|
||||||
|
caption?: string
|
||||||
|
): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const payload = JSON.stringify({
|
||||||
|
source: `file://${resolve(imagePath)}`,
|
||||||
|
title: title || null,
|
||||||
|
caption: caption || null,
|
||||||
|
});
|
||||||
|
const res = await fetch(previewUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: payload,
|
||||||
|
signal: AbortSignal.timeout(3000),
|
||||||
|
});
|
||||||
|
return res.ok;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async handleImportCommand(cmd: ImportCommand): Promise<CommandResponse> {
|
private async handleImportCommand(cmd: ImportCommand): Promise<CommandResponse> {
|
||||||
const context = this.getContext();
|
const context = this.getContext();
|
||||||
if (!context) throw new Error('Browser not launched');
|
if (!context) throw new Error('Browser not launched');
|
||||||
@@ -1139,6 +1196,8 @@ export class ClaudeBrowser {
|
|||||||
}
|
}
|
||||||
case 'import':
|
case 'import':
|
||||||
return this.handleImportCommand(cmd);
|
return this.handleImportCommand(cmd);
|
||||||
|
case 'preview':
|
||||||
|
return this.handlePreviewCommand(cmd);
|
||||||
default: {
|
default: {
|
||||||
const _exhaustive: never = cmd;
|
const _exhaustive: never = cmd;
|
||||||
return { ok: false, error: `Unknown command: ${(_exhaustive as { cmd: string }).cmd}` };
|
return { ok: false, error: `Unknown command: ${(_exhaustive as { cmd: string }).cmd}` };
|
||||||
|
|||||||
+34
@@ -255,6 +255,40 @@ server.tool(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Preview — navigate + screenshot in one call, optional POST to preview endpoint
|
||||||
|
server.tool(
|
||||||
|
'preview',
|
||||||
|
'Navigate to a URL and take a screenshot in one call. Optionally POST the result to a preview endpoint.',
|
||||||
|
{
|
||||||
|
url: z.string().describe('URL or file:///path to preview'),
|
||||||
|
width: z.number().optional().default(1280).describe('Viewport width'),
|
||||||
|
height: z.number().optional().default(800).describe('Viewport height'),
|
||||||
|
fullPage: z.boolean().optional().default(false),
|
||||||
|
output: z.string().optional().default('/tmp/preview.png').describe('Screenshot output path'),
|
||||||
|
previewUrl: z.string().optional().describe('HTTP endpoint to POST screenshot result to'),
|
||||||
|
title: z.string().optional().describe('Title sent with preview POST'),
|
||||||
|
caption: z.string().optional().describe('Caption sent with preview POST'),
|
||||||
|
},
|
||||||
|
withLogging(
|
||||||
|
'preview',
|
||||||
|
async ({ url, width, height, fullPage, output, previewUrl, title, caption }) => {
|
||||||
|
await ensureLaunched();
|
||||||
|
const result = await browser.executeCommand({
|
||||||
|
cmd: 'preview',
|
||||||
|
url,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
fullPage,
|
||||||
|
output,
|
||||||
|
previewUrl,
|
||||||
|
title,
|
||||||
|
caption,
|
||||||
|
});
|
||||||
|
return textResult(JSON.stringify(result));
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// Eval
|
// Eval
|
||||||
server.tool(
|
server.tool(
|
||||||
'eval',
|
'eval',
|
||||||
|
|||||||
+16
-1
@@ -347,7 +347,20 @@ export type BrowserCommand =
|
|||||||
| ScrollCommand
|
| ScrollCommand
|
||||||
| ViewportCommand
|
| ViewportCommand
|
||||||
| EmulateCommand
|
| EmulateCommand
|
||||||
| ImportCommand;
|
| ImportCommand
|
||||||
|
| PreviewCommand;
|
||||||
|
|
||||||
|
export interface PreviewCommand {
|
||||||
|
cmd: 'preview';
|
||||||
|
url: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
fullPage?: boolean;
|
||||||
|
output?: string;
|
||||||
|
previewUrl?: string;
|
||||||
|
title?: string;
|
||||||
|
caption?: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Response types
|
// Response types
|
||||||
export interface SuccessResponse {
|
export interface SuccessResponse {
|
||||||
@@ -359,6 +372,8 @@ export interface SuccessResponse {
|
|||||||
count?: number;
|
count?: number;
|
||||||
elements?: ElementInfo[];
|
elements?: ElementInfo[];
|
||||||
result?: unknown;
|
result?: unknown;
|
||||||
|
// Preview fields
|
||||||
|
posted?: boolean;
|
||||||
// Image processing fields
|
// Image processing fields
|
||||||
files?: string[];
|
files?: string[];
|
||||||
outputDir?: string;
|
outputDir?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user