| name | anything-analyzer-cdp |
| description | Electron desktop app that captures web traffic via Chrome DevTools Protocol and uses AI to generate protocol analysis reports |
| triggers | ["analyze web protocol with AI","capture network requests with CDP","use anything-analyzer","generate protocol documentation from browser traffic","hook javascript fetch xhr with electron","analyze authentication flow with AI","capture SSE websocket traffic electron","reverse engineer web API protocol"] |
Anything Analyzer CDP Skill
Skill by ara.so — Daily 2026 Skills collection.
Anything Analyzer is an Electron desktop application that embeds a browser, captures all network traffic via Chrome DevTools Protocol (CDP), injects JS hooks, snapshots storage, and feeds the data to an AI (OpenAI/Anthropic/custom) to generate protocol analysis reports — useful for documenting registration flows, 2API reverse engineering, and general browser protocol analysis.
Installation & Setup
git clone https://github.com/MouseWW/anything-analyzer.git
cd anything-analyzer
pnpm install
pnpm dev
pnpm build
Windows native module build requirement:
pnpm install
pnpm rebuild
Package as installer:
pnpm run build && npx electron-builder --win
Core Architecture
src/
├── main/ # Electron main process
│ ├── ai/ # AI analysis pipeline
│ │ ├── ai-analyzer.ts # orchestrator
│ │ ├── data-assembler.ts# data preparation
│ │ ├── prompt-builder.ts# prompt generation
│ │ └── scene-detector.ts# rule-based scene classification
│ ├── capture/ # Capture engine
│ │ ├── capture-engine.ts# data sink → SQLite + renderer
│ │ ├── js-injector.ts # hook script injection
│ │ └── storage-collector.ts # periodic storage snapshots
│ ├── cdp/
│ │ └── cdp-manager.ts # CDP manager
│ ├── db/ # SQLite via better-sqlite3
│ ├── session/
│ │ └── session-manager.ts # session lifecycle
│ ├── tab-manager.ts # Multi-tab WebContentsView
│ ├── window.ts # Main window layout
│ └── ipc.ts # IPC handlers
├── preload/ # Context bridge + hook script
├── renderer/ # React 19 + Ant Design 5 UI
└── shared/types.ts # Shared TypeScript types
Key Concepts
Sessions
A Session scopes all captured data. Each session has a name, target URL, and contains all requests, JS hook events, and storage snapshots captured during that session.
Capture Engine
The capture engine:
- Attaches CDP to
WebContentsView tabs
- Enables
Fetch.enable for request interception
- Injects JS hooks via
Page.addScriptToEvaluateOnNewDocument
- Collects storage snapshots periodically
AI Analysis Pipeline
- Scene detection — rule-based classification (registration, OAuth, API auth, etc.)
- Data assembly — selects relevant requests, deduplicates, truncates large bodies
- Prompt building — constructs structured prompt with scene context
- LLM call — streams response back to renderer
Configuration
LLM Provider Setup (Settings UI)
Configure via the Settings panel (bottom-left gear icon):
interface LLMConfig {
provider: 'openai' | 'anthropic' | 'custom';
apiKey: string;
model: string;
baseUrl?: string;
}
OpenAI:
- API Key:
$OPENAI_API_KEY
- Model:
gpt-4o or gpt-4o-mini
Anthropic:
- API Key:
$ANTHROPIC_API_KEY
- Model:
claude-sonnet-4-20250514
Custom (OpenAI-compatible):
- Base URL: e.g.
https://api.deepseek.com/v1
- API Key: your provider key
- Model: provider-specific model name
IPC API (Main ↔ Renderer)
Session Management
const session = await window.electron.ipcRenderer.invoke('session:create', {
name: 'My Analysis Session',
url: 'https://example.com'
})
const sessions = await window.electron.ipcRenderer.invoke('session:list')
await window.electron.ipcRenderer.invoke('session:delete', sessionId)
Capture Control
await window.electron.ipcRenderer.invoke('capture:start', { sessionId, tabId })
await window.electron.ipcRenderer.invoke('capture:stop', { sessionId, tabId })
const requests = await window.electron.ipcRenderer.invoke('capture:getRequests', sessionId)
AI Analysis
await window.electron.ipcRenderer.invoke('analyze:start', { sessionId })
window.electron.ipcRenderer.on('analyze:chunk', (_, chunk: string) => {
setReport(prev => prev + chunk)
})
window.electron.ipcRenderer.on('analyze:done', () => {
setAnalyzing(false)
})
Real Code Examples
Extend the Scene Detector
import { CapturedRequest } from '../../shared/types'
export type Scene =
| 'registration'
| 'oauth'
| 'api-auth'
| 'websocket'
| 'general'
export function detectScene(requests: CapturedRequest[]): Scene {
const urls = requests.map(r => r.url.toLowerCase())
const bodies = requests.map(r => r.requestBody?.toLowerCase() ?? '')
if (urls.some(u => u.includes('oauth') || u.includes('authorize') || u.includes('callback'))) {
return 'oauth'
}
if (
bodies.some(b => b.includes() && (b.() || b.())) &&
urls.( u.() || u.() || u.())
) {
}
(requests.( r.)) {
}
(urls.( u.() || u.() || u.())) {
}
}
Custom Prompt Builder
import { Scene } from './scene-detector'
import { AssembledData } from './data-assembler'
export function buildPrompt(scene: Scene, data: AssembledData): string {
const sceneInstructions: Record<Scene, string> = {
registration: `Analyze this registration flow. Extract:
1. Required fields and validation rules
2. Password requirements
3. Captcha/bot protection mechanisms
4. Email verification flow
5. Reproducible curl commands for each step`,
oauth: `Analyze this OAuth flow. Extract:
1. OAuth provider and grant type
2. Authorization URL with all parameters
3. Token exchange endpoint and parameters
4. Token refresh mechanism
5. Scopes requested`,
'api-auth': `Analyze this authentication protocol. Extract:
1. Auth endpoint and method
2. Request payload schema
3. Response token format (JWT/session/etc)
4. Token usage in subsequent requests (header name, format)
5. Expiry and refresh strategy`,
websocket: `Analyze this WebSocket protocol. Extract:
1. Upgrade request headers
2. Initial handshake messages
3. Message format (JSON/binary/custom)
4. Heartbeat/ping-pong mechanism
5. Event types and schemas`,
general: `Analyze this web protocol. Extract:
1. Core API endpoints and their purposes
2. Authentication mechanism
3. Request/response schemas
4. Error handling patterns
5. Rate limiting signals`,
}
return `You are a protocol reverse engineer. ${sceneInstructions[scene]}
## Captured Data
### Network Requests ( total)
### JS Hook Events
### Storage Snapshots
Generate a comprehensive protocol analysis report in Markdown.`
}
Adding a Custom JS Hook
export function buildHookScript(): string {
return `
(function() {
// Hook fetch
const _fetch = window.fetch.bind(window)
window.fetch = async function(...args) {
const [input, init] = args
const url = input instanceof Request ? input.url : String(input)
// Pre-request hook
window.__cdpHook?.({ type: 'fetch:request', url, init: JSON.stringify(init) })
const response = await _fetch(...args)
const clone = response.clone()
// Post-response hook (non-blocking)
clone.text().then(body => {
window.__cdpHook?.({ type: 'fetch:response', url, status: response.status, body })
}).catch(() => {})
return response
}
// Hook XHR
const _open = XMLHttpRequest.prototype.open
const _send = XMLHttpRequest.prototype.send
XMLHttpRequest.prototype.open = function(method, url, ...rest) {
this.__hookData = { method, url }
return _open.apply(this, [method, url, ...rest])
}
XMLHttpRequest.prototype.send = function(body) {
this.addEventListener('load', function() {
window.__cdpHook?.({
type: 'xhr:complete',
method: this.__hookData?.method,
url: this.__hookData?.url,
requestBody: body,
status: this.status,
responseBody: this.responseText
})
})
return _send.apply(this, [body])
}
// Hook crypto.subtle for key detection
if (window.crypto?.subtle) {
const _sign = crypto.subtle.sign.bind(crypto.subtle)
crypto.subtle.sign = async function(algorithm, key, data) {
window.__cdpHook?.({ type: 'crypto:sign', algorithm: JSON.stringify(algorithm) })
return _sign(algorithm, key, data)
}
}
// Hook document.cookie
const cookieDesc = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie')
Object.defineProperty(document, 'cookie', {
get: function() { return cookieDesc.get.call(this) },
set: function(val) {
window.__cdpHook?.({ type: 'cookie:set', value: val })
return cookieDesc.set.call(this, val)
}
})
})()
`
}
Database Schema Access
import Database from 'better-sqlite3'
import path from 'path'
import { app } from 'electron'
const DB_PATH = path.join(app.getPath('userData'), 'analyzer.db')
export function getDb(): Database.Database {
const db = new Database(DB_PATH)
db.pragma('journal_mode = WAL')
return db
}
export function initSchema(db: Database.Database) {
db.exec(`
CREATE TABLE IF NOT EXISTS sessions (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
url TEXT NOT NULL,
created_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS requests (
id TEXT PRIMARY KEY,
session_id TEXT NOT NULL,
url TEXT NOT NULL,
method TEXT NOT NULL,
status_code INTEGER,
request_headers TEXT,
request_body TEXT,
response_headers TEXT,
response_body TEXT,
is_sse INTEGER DEFAULT 0,
is_websocket INTEGER DEFAULT 0,
timestamp INTEGER NOT NULL,
FOREIGN KEY (session_id) REFERENCES sessions(id)
);
CREATE TABLE IF NOT EXISTS hook_events (
id TEXT PRIMARY KEY,
session_id TEXT NOT NULL,
type TEXT NOT NULL,
data TEXT NOT NULL,
timestamp INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS storage_snapshots (
id TEXT PRIMARY KEY,
session_id TEXT NOT NULL,
cookies TEXT,
local_storage TEXT,
session_storage TEXT,
timestamp INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
`)
}
Shared Types Reference
export interface Session {
id: string
name: string
url: string
createdAt: number
}
export interface CapturedRequest {
id: string
sessionId: string
url: string
method: string
statusCode?: number
requestHeaders?: Record<string, string>
requestBody?: string
responseHeaders?: Record<string, string>
responseBody?: string
isSSE: boolean
isWebSocket: boolean
timestamp: number
}
export interface HookEvent {
id: string
sessionId: string
type: 'fetch:request' | 'fetch:response' | 'xhr:complete' | 'crypto:sign' |
: <, >
:
}
{
:
:
:
: <, >
: <, >
:
}
{
: | |
:
:
?:
}
Common Patterns
Pattern: Capture a Full Registration Flow
- Click New Session → enter name + target URL (e.g.
https://example.com/register)
- Click Start Capture
- In the embedded browser, complete the full registration flow
- Click Stop Capture
- Click Analyze → AI generates a report with extracted fields, validation rules, and curl commands
Pattern: OAuth Flow Analysis
- Create session with the OAuth entry URL
- Start capture
- Authorize the OAuth flow including the redirect callback
- Stop capture — the analyzer auto-detects OAuth and focuses prompt on token exchange
Pattern: Adding a New LLM Provider
import Anthropic from '@anthropic-ai/sdk'
import OpenAI from 'openai'
export async function* callLLM(
config: LLMConfig,
prompt: string
): AsyncGenerator<string> {
if (config.provider === 'anthropic') {
const client = new Anthropic({ apiKey: config.apiKey })
const stream = await client.messages.stream({
model: config.model,
max_tokens: 8192,
messages: [{ role: 'user', content: prompt }]
})
for await (const chunk of stream) {
if (chunk.type === 'content_block_delta' && chunk.delta.type === 'text_delta') {
yield chunk.delta.text
}
}
} {
client = ({
: config.,
: config.
})
stream = client...({
: config.,
: [{ : , : prompt }],
:
})
( chunk stream) {
chunk.[]?.?. ??
}
}
}
Pattern: Filter Requests Before Analysis
function filterRelevantRequests(requests: CapturedRequest[]): CapturedRequest[] {
const AUTH_PATTERNS = [
/\/auth/, /\/login/, /\/register/, /\/signup/, /\/token/,
/\/oauth/, /\/session/, /\/verify/, /\/captcha/
]
return requests.filter(r => {
if (r.requestHeaders?.['authorization'] || r.requestHeaders?.['x-auth-token']) {
return true
}
if (AUTH_PATTERNS.some(p => p.test(r.url))) return true
if (r.responseHeaders?.['set-cookie']) return true
if (/\.(js|css|png|jpg|gif|svg|woff|ico)(\?|$)/.(r.))
})
}
Troubleshooting
better-sqlite3 build fails on Windows
npm install --global windows-build-tools
pnpm rebuild
better-sqlite3 wrong Electron version
./node_modules/.bin/electron-rebuild -f -w better-sqlite3
npx @electron/rebuild -f -w better-sqlite3
CDP not attaching to tab
- Ensure
WebContentsView is fully loaded before calling cdpManager.attach()
- Check
webContents.getURL() isn't about:blank before enabling Fetch
- For popups/OAuth windows, listen for
new-window or setWindowOpenHandler and capture the new WebContents
AI response truncated
- Increase
max_tokens in the LLM call (default 8192, increase to 16384)
- Reduce request body size in
data-assembler.ts — truncate large response bodies to first 2000 chars
Requests missing response bodies
- CDP
Fetch.getResponseBody must be called before Fetch.continueRequest
- Binary/gzip responses need base64 decoding: check
base64Encoded field in CDP response
- Some streaming responses (SSE) can't have body captured synchronously — mark as SSE and capture chunks via
Network.eventSourceMessageReceived
HTTPS interception not working
- CDP Fetch interception works on all HTTPS by default in Electron's WebContentsView
- If a site uses certificate pinning, it may reject interception — look for
ERR_CERT_* in request errors
App window blank on startup
pnpm dev
Development Tips
- Hot reload:
pnpm dev uses electron-vite with HMR for renderer and restart for main
- Devtools: In dev mode, DevTools auto-opens for renderer; use
Ctrl+Shift+I for embedded browser webview devtools
- SQLite inspection: Use DB Browser for SQLite on
%APPDATA%/anything-analyzer/analyzer.db (Windows) or ~/Library/Application Support/anything-analyzer/analyzer.db (macOS)
- IPC debugging: Add
console.log in ipc.ts handlers — logs appear in Electron main process terminal
- CDP raw events: Enable
cdp.on('*', console.log) in cdp-manager.ts during development to see all CDP events