
Interceptor
- 35 installs
- 17.2k repo stars
- Updated August 1, 2026
- danielmiessler/personal_ai_infrastructure
Real Chrome browser automation via an extension that controls the actual browser from inside, staying logged in and passing bot detection, for visual verification and flow record/replay.
About
Drives the real Chrome browser through the Interceptor extension with compound open/read/act/inspect commands, session reuse, network logging, and record-then-replay for regression. Developers use it to verify deploys, reproduce bugs, and test authenticated flows.
- Zero CDP fingerprint, passes major bot-detection checks
- Record/replay flows and auto-captured network logs
Interceptor by the numbers
- 35 all-time installs (skills.sh)
- Ranked #1,312 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/danielmiessler/personal_ai_infrastructure --skill interceptorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 35 |
|---|---|
| repo stars | ★ 17.2k |
| Last updated | August 1, 2026 |
| Repository | danielmiessler/personal_ai_infrastructure ↗ |
What it does
Real Chrome browser automation via an extension that controls the actual browser from inside, staying logged in and passing bot detection, for visual verification and flow record/replay.
Files
Customization
Before executing, check for user customizations at: ~/.claude/PAI/USER/SKILLCUSTOMIZATIONS/Interceptor/
If this directory exists, load and apply any PREFERENCES.md, configurations, or resources found there. These override default behavior. If the directory does not exist, proceed with skill defaults.
MANDATORY: Voice Notification (REQUIRED BEFORE ANY ACTION)
You MUST send this notification BEFORE doing anything else when this skill is invoked.
1. Send voice notification:
curl -s -X POST http://localhost:31337/notify \
-H "Content-Type: application/json" \
-d '{"message": "Running the WORKFLOWNAME workflow in the Interceptor skill to ACTION"}' \
> /dev/null 2>&1 &2. Output text notification:
Running the **WorkflowName** workflow in the **Interceptor** skill to ACTION...This is not optional. Execute this curl command immediately upon skill invocation.
Interceptor — Stealth Browser Automation
Tool: interceptor CLI — Chrome extension that controls the real browser from the inside. Repo: https://github.com/Hacker-Valley-Media/slop-browser Install: ~/Projects/interceptor (built from source — see Workflows/Update.md)
Why Interceptor?
agent-browser (the Browser skill) uses CDP — sites can detect it. Interceptor is a Chrome extension that operates through the actual browser UI. No debugger, no automation flags, no separate browser instance. You stay logged in, you pass bot detection, the agent sees what you see.
Prerequisites
- Chrome or Brave running with the Interceptor extension loaded
interceptorCLI in PATH (/opt/homebrew/bin/interceptor)interceptor-daemonin PATH (/opt/homebrew/bin/interceptor-daemon)- Native messaging manifest registered (
bash ~/Projects/interceptor/scripts/install.sh --chrome --skip-extension) - (Optional, macOS)
interceptor-bridgehelper app — see "Bridge" section below
Bridge — macOS Native Helper App
The bridge is an optional Swift helper that runs as a LaunchAgent and unlocks capabilities the Chrome extension can't provide on its own: OS-level synthetic input (act --os), accessibility tree of native macOS apps, app control, clipboard, screen capture beyond Chrome, audio/speech, files, notifications, HealthKit, Apple Intelligence. ~95% of typical work doesn't need it.
Status check: interceptor status reports bridge: running with PID + socket when it's up, or bridge: not running when it isn't.
Lifecycle (install / verify / troubleshoot / uninstall) lives entirely in `Workflows/Update.md` section 6. Future updates flow through the Update workflow — never reach for the upstream install-bridge.sh directly; that script breaks on Apple Silicon (/usr/local/bin needs sudo, but sudoing the whole script makes launchctl bootstrap target uid 0 instead of the user). The skill's procedure is the canonical one.
Security model — read before installing:
- Transport is a UNIX domain socket at
/tmp/interceptor-bridge.sock.
Local-only; no network listener; not reachable from another machine.
- No authentication on the socket. Any local process running as your user
can connect and execute every bridge action — including synthetic input, clipboard read, audio capture, screenshots. macOS TCC permissions (Accessibility, Input Monitoring, Screen Recording, Microphone) are granted to the bridge once and inherited by every socket client.
- Marginal risk is supply-chain: a malicious package installed via
bun/brew/npm gains a one-step path to OS-level input/screen/clipboard without needing its own permission grants — the bridge has them.
- Single-user Mac threat model: acceptable, since anything running as you can
already do this with effort. Multi-user Macs need socket hardening (see Update.md section 6c).
- Binary built locally from
~/Projects/interceptor/interceptor-bridge/Sources/,
not a downloaded prebuilt. Provenance is Swift source we just compiled.
---
Compound Commands (Preferred)
These collapse multi-step patterns into single invocations — fewer tool calls, fewer tokens:
interceptor open "https://example.com" # Open URL, wait, return tree + text
interceptor open <url> --tree-only|--text-only|--full|--no-wait
interceptor read # Tree + text for active tab
interceptor read <ref> # Tree + text for element subtree
interceptor read --include-style|--include-frames
interceptor act <ref> # Click + wait + return updated tree + diff
interceptor act <ref> "value" # Type + wait + return updated tree
interceptor act <ref> --os # OS-level trusted input (requires bridge)
interceptor act <ref> --keys "Enter" # Send keyboard shortcut
interceptor inspect # Tree + text + network log + headers
interceptor inspect --net-only # Network onlyCore Commands
# State + discovery
interceptor state [--full] # DOM tree + metadata
interceptor tree [--filter all] [--depth N] [--max-chars N]
interceptor diff # Changes since last state/tree read
interceptor find "query" [--role button] # Find elements by name
interceptor text [<index|ref>]
interceptor html <index|ref>
# Element interaction
interceptor click <ref> # Click by ref (eN)
interceptor click <ref> --at X,Y # Click at coordinates
interceptor dblclick <ref> --at X,Y
interceptor rightclick <ref> --at X,Y
interceptor type <ref> <text> [--append]
interceptor type "role:name" <text> # Semantic selector
interceptor select <ref> <value> # Dropdown
interceptor focus|hover <ref>
interceptor drag <ref> --from X,Y --to X,Y [--steps N] [--duration MS]
interceptor keys "<combo>" # e.g. "Control+A"
# Navigation + tabs
interceptor navigate <url>
interceptor back | forward
interceptor scroll <up|down|top|bottom>
interceptor wait <ms> | wait-stable [--ms N] [--timeout N]
interceptor tabs
interceptor tab new [url] | tab close [id] | tab switch <id>
# Capture
interceptor screenshot [--save] [--format png|jpeg] [--full] [--clip X,Y,W,H] [--element N]
interceptor eval <code> [--main] # JS in isolated or main world
interceptor capture start | frame | stop # tabCapture stream
# Style injection (test redesigns live)
interceptor style inject --css "<rules>" [--top-only]
interceptor style remove <handle>
# Cookies
interceptor cookies <domain>
interceptor cookies set <json>
interceptor cookies delete <url> <name>Network
# Passive capture (always-on, no CDP fingerprint)
interceptor net log [--filter <pat>] [--limit N] [--since <ts>]
interceptor net headers [--filter <pat>] # CSRF, auth headers
interceptor net clear
# Request override (passive, no CDP)
interceptor override "*pattern*" key=value
interceptor override clear
# CDP-attached interception (explicit opt-in — leaves debugger banner)
interceptor network on [patterns...]
interceptor network off
interceptor network log
interceptor network override on '<json>'
interceptor network override off
# SSE streams (LLM responses, live feeds)
interceptor sse log [--filter <pat>] [--limit N]
interceptor sse streams
interceptor sse tail [--filter <pat>]
# Header rewriting
interceptor headers add <name> <value>
interceptor headers remove <name>
interceptor headers clearRecording (Session Monitor)
Record real user actions on the active tab, replay as a deterministic plan script.
interceptor monitor start ["instruction"] # Start recording
interceptor monitor pause | resume
interceptor monitor stop # End + emit summary
interceptor monitor status [--all]
interceptor monitor list # All sessions
interceptor monitor tail [--current] [--raw] # Live tail
interceptor monitor export <sessionId> # Aligned text
interceptor monitor export <sessionId> --plan # Replay script
interceptor monitor export <sessionId> --jsonCanvas (Rich Web Apps)
For apps that render to <canvas> (Figma, Excalidraw, in-house editors):
interceptor canvas list | status
interceptor canvas log [N] [--kind fillText]
interceptor canvas objects [N] [--kind text]
interceptor canvas model | routes
interceptor canvas ocr N [--region X,Y,W,H]
interceptor canvas read N [--format png] [--region X,Y,W,H] [--webgl]
interceptor canvas diff <url1> <url2> [--threshold 10] [--image]Scene Graph (Rich Editors — Google Docs/Slides, Canva)
interceptor scene profile [--verbose]
interceptor scene list [--type shape|text|image|page|embed|slide]
interceptor scene click <id> | dblclick <id> | select <id>
interceptor scene hit <x> <y> # ID object at coordinates
interceptor scene selected | text [--with-html]
interceptor scene insert "<text>"
interceptor scene cursor-to <x> <y>
interceptor scene slide list | current | goto <index> | notes [--slide N]
interceptor scene render <id> [--save]
interceptor scene zoom
interceptor scene ... --profile <name> # Force profile, bypass detectioninterceptor linkedin event [url] # Event + post data via DOM + network
interceptor linkedin attendees [url] # Attendees with override + enrichmentChatGPT Agentic Bridge
Drive chatgpt.com from CLI without an API key:
interceptor chatgpt send "<prompt>" [--stream]
interceptor chatgpt read | status
interceptor chatgpt conversations | switch <id>
interceptor chatgpt model [name]
interceptor chatgpt stopBatch + Meta
interceptor batch '<json_array>' [--stop-on-error] [--timeout MS]
interceptor status # Daemon + bridge state (local check)
interceptor helpKey Rules
- Requires Chrome running — it's an extension, not a standalone binary.
- Refs use eN syntax —
e12not@e12. No@prefix. - Cross-frame refs —
read --include-framesreturns refs likee<frameId>_<n>for non-top frames. - `--json` is a global flag for structured output.
- Daemon auto-starts — first command launches it; no manual start needed.
- Bridge is optional — only needed for
act --osand OS-trusted input. Without it, interceptor falls back to in-page synthetic events.
Delegating to Agents
When spawning agents for Interceptor work:
Agent(subagent_type="general-purpose", prompt="
Use interceptor CLI for all browser work.
Commands: open <url>, read, act eN, act eN 'text', inspect, screenshot.
Compound commands preferred — they return tree + text in one call.
Refs use eN syntax (no @ prefix) from tree output.
Use --json for structured output.
[your specific task instructions here]
")Gotchas
- Screenshot ignores scroll position.
interceptor screenshot(with or without--full) captures from y=0 of the document — it does not honorwindow.scrollTo,scrollIntoView,scroll bottom, orkeys End. For tall pages, content below the fold is unreachable through screenshot. Workaround: render the section of interest at its own short URL (/problems.html,/section-3.html) and screenshot that page directly. The--clip "x,y,w,h"flag returns "Cannot read properties of undefined" — broken in the current build. (2026-04-27) - Multiple tabs at the same URL confuse routing. When two tabs both load
localhost:5180/,tab switch <id>reportsokbut the visually-active Chrome tab may not change, andscreenshotcaptures whatever Chrome is showing — not whatinterceptor textandinterceptor navigateare routing to. Close duplicate tabs before screenshotting, or always work from a freshly-opened single tab. - `eval` is CSP-blocked on most sites. Use
eval --mainto run in the page's main world instead of the isolated extension world. Even with--main, sites with strict CSP (script-src 'self') will still block string-eval; pass small expressions only and avoidFunction-constructor patterns.
Stealth Verification
Passes all major bot detection:
| Check | Result |
|---|---|
| BrowserScan | Normal |
| Pixelscan | Definitely Human |
| Sannysoft | All pass |
| CreepJS | 0% headless |
| Fingerprint.com | notDetected |
| AreyouHeadless | Not headless |
---
Workflow Routing
| Trigger Words | Workflow | What It Does |
|---|---|---|
| "verify deploy", "check deploy", "confirm deploy", "deploy verification" | Workflows/VerifyDeploy.md | Open URL in real Chrome, check for errors, capture screenshot evidence |
| "reproduce", "reproduce bug", "debug page", "check page", "blank screen" | Workflows/Reproduce.md | Open affected page BEFORE code analysis, capture console errors and network 404s |
| "record flow", "record workflow", "capture flow", "monitor start" | Workflows/RecordFlow.md | Record user actions via monitor system, export replayable plan script |
| "replay flow", "replay", "regression check", "run flow" | Workflows/ReplayFlow.md | Execute a recorded plan script step-by-step, verify each step, report regressions |
| "test form", "fill form", "form test", "check form" | Workflows/TestForm.md | Discover form fields, fill with test data, submit, verify result |
| "update", "check version", "rebuild" | Workflows/Update.md | Rebuild interceptor from source and verify |
---
Execution Log
After completing any workflow, append a single JSONL entry:
echo '{"ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","skill":"Interceptor","workflow":"WORKFLOW_USED","input":"8_WORD_SUMMARY","status":"ok|error","duration_s":SECONDS}' >> ~/.claude/PAI/MEMORY/SKILLS/execution.jsonlInterceptor Recorded Flows
This directory stores replay plan scripts exported from interceptor monitor export <sid> --plan.
Each file is a shell script of interceptor commands that can be replayed via the ReplayFlow workflow.
RecordFlow Workflow
Voice Notification
curl -s -X POST http://localhost:31337/notify \
-H "Content-Type: application/json" \
-d '{"message": "Running the RecordFlow workflow in the Interceptor skill to record a user flow"}' \
> /dev/null 2>&1 &Running RecordFlow in Interceptor...
---
Record a user workflow by capturing browser actions into a replayable script. Uses Interceptor's monitor system to observe clicks, typing, navigation, and network requests, then exports a replay plan using semantic selectors.
When to Use
- Capturing a critical user flow for regression testing (signup, payment, onboarding)
- Creating a repeatable QA check from a manual walkthrough
- Building a baseline for API contract verification (which endpoints fire during a flow)
- Documenting a complex multi-step interaction for future replay
Steps
1. Navigate to the Starting Page
interceptor open "<START_URL>"Confirm you're on the correct starting page before recording.
2. Start Recording
interceptor monitor start --instruction "<FLOW_DESCRIPTION>"The instruction is stored with the session and appears in exports. Be specific: "Signup flow from landing page through onboarding" is better than "signup test".
3. Walk Through the Flow
Execute the flow manually using interceptor commands:
interceptor act e5 # Click a button
interceptor act e12 "user@example.com" # Type into a field
interceptor act e8 --keys "Enter" # Press EnterOr walk through the flow manually in Chrome — the monitor captures real user actions too (clicks, typing, scrolling, form submissions).
The monitor records:
- Every click, double-click, right-click (with element ref, role, and accessible name)
- Every input/change event (with values — passwords are auto-masked)
- Keyboard shortcuts (Enter, Tab, Escape, arrows)
- Form submissions
- Network requests correlated to the user action that triggered them
- DOM mutations caused by each action
4. Stop Recording
interceptor monitor stopReturns a session summary with event counts (events, mutations, network requests, duration).
5. Export the Replay Plan
interceptor monitor listFind the session ID from the list, then:
interceptor monitor export <SESSION_ID> --planThis generates a replayable script using semantic selectors (role:name format) that survive DOM changes better than ref IDs. The script includes wait-stable commands between actions that trigger DOM mutations.
To include network verification cues:
interceptor monitor export <SESSION_ID> --plan --with-bodies6. Save the Plan
Save the exported plan to skills/Interceptor/Flows/<flow-name>.sh for future replay via the ReplayFlow workflow.
7. Review the Plan
Read the generated script. Check for:
# TODOcomments where password fields were masked — these need manual value substitution# ref eN (no accessible name)fallbacks — these may be fragile; consider adding accessible names to the UI- Commented network cues showing which API calls each action triggered
Notes
- The monitor records from Chrome's content script — it sees real user events, not just interceptor-injected ones.
- Password and credit card fields are automatically masked in recordings (
***N***format). - Recordings are stored in JSONL format at the interceptor events path. Use
interceptor monitor export <sid> --jsonfor raw data. - Session recordings persist across interceptor restarts but are per-machine (not synced).
- For live observation during recording:
interceptor monitor tailstreams events in real time.
ReplayFlow Workflow
Voice Notification
curl -s -X POST http://localhost:31337/notify \
-H "Content-Type: application/json" \
-d '{"message": "Running the ReplayFlow workflow in the Interceptor skill to replay a recorded flow"}' \
> /dev/null 2>&1 &Running ReplayFlow in Interceptor...
---
Replay a previously recorded user flow to verify it still works after a deploy or code change. Executes the plan script step-by-step, captures the result at each stage, and reports any regressions.
When to Use
- After deploying changes to a page that has a recorded flow
- As a regression check before merging UI changes
- To verify a bug fix by replaying the flow that exposed the bug
- As part of a deploy verification pipeline alongside VerifyDeploy
Steps
1. Locate the Flow Plan
Recorded flows live in skills/Interceptor/Flows/. List available flows:
ls ~/.claude/skills/Interceptor/Flows/Or regenerate from a monitor session:
interceptor monitor export <SESSION_ID> --plan2. Open the Starting URL
The plan script starts with a interceptor tab new "<url>" or interceptor navigate "<url>" command. Execute it:
interceptor open "<START_URL>"3. Execute the Plan Step-by-Step
Read the plan file and execute each command sequentially. For each action:
# Example: click a button
interceptor act "button:Sign In"
# Example: type into a field
interceptor act "textbox:Email" "user@example.com"
# Example: wait for page update
interceptor wait-stableAfter each action that triggers a page change, verify the expected state:
interceptor read --text-onlyCheck that the expected content appears. If an element is missing or content differs from expectations, flag it as a regression.
4. Verify Network Contracts (Optional)
If the plan includes commented network cues (# correlated fetch GET /api/...), verify those endpoints still fire:
interceptor net log --jsonCompare against the baseline network log from the original recording. Look for:
- Endpoints that no longer fire (removed API calls)
- Changed response status codes
- New unexpected requests
5. Capture Final State
( cd /tmp/pai-screenshots && interceptor screenshot --save )Compare the final screenshot against the expected end state of the flow.
6. Report Results
For each step in the plan, report:
- PASS: action succeeded, expected state confirmed
- FAIL: action failed or unexpected state detected
- REGRESSION: behavior changed from baseline
Using Batch for Known Flows
For well-tested flows where you trust the commands, use batch execution:
interceptor batch '[
{"type": "navigate", "url": "https://example.com"},
{"type": "wait_stable"},
{"type": "click", "ref": "button:Sign In"},
{"type": "wait_stable"},
{"type": "type", "ref": "textbox:Email", "value": "user@example.com"},
{"type": "click", "ref": "button:Submit"}
]' --stop-on-errorThe --stop-on-error flag halts on the first failure so you can diagnose the exact regression point.
Notes
- Semantic selectors (
role:name) are more resilient than ref IDs (e5) — prefer them in flow plans. - If a selector fails, use
interceptor find "<name>"to locate the element under its new name. - Flows recorded on one environment may need URL adjustments for another (staging vs production).
- For flows with password fields, the plan will have
# TODOcomments — substitute values before replay. - Screenshots from replays can be compared against baseline screenshots for visual regression detection.
Reproduce Workflow
Voice Notification
curl -s -X POST http://localhost:31337/notify \
-H "Content-Type: application/json" \
-d '{"message": "Running the Reproduce workflow in the Interceptor skill to reproduce a bug"}' \
> /dev/null 2>&1 &Running Reproduce in Interceptor...
---
Reproduce a reported bug by opening the affected page in real Chrome BEFORE reading any code. Captures console errors, network failures, and visual state as primary evidence. Code analysis comes after reproduction, never before.
When to Use
- Any time a UI or page bug is reported ("blank screen", "broken layout", "page won't load")
- Before writing any fix for a web-facing issue
- When someone reports something looks wrong on a deployed site
- As the mandatory first step in the Algorithm's Diagnostic preflight gate
Steps
1. Open the Affected Page
interceptor open "<BUG_URL>"Do NOT read code first. Do NOT form theories. Open the page and look at it.
2. Capture Visual State
( cd /tmp/pai-screenshots && interceptor screenshot --save )Read the screenshot. Is the reported bug visible? Document what you see vs what's expected.
3. Check Console Errors
interceptor eval "(() => {
const entries = performance.getEntriesByType('resource').filter(e => e.name.includes('.js') || e.name.includes('.css'));
const failed = entries.filter(e => e.transferSize === 0 && e.decodedBodySize === 0);
return JSON.stringify({ consoleCheck: 'done', failedResources: failed.map(e => e.name) });
})()" --mainAlso check for JS errors in the network log:
interceptor net log --jsonLook for:
- 404s on JS/CSS bundles (missing build artifacts — a common root cause of blank-screen deploys)
- Failed API calls (500s, timeouts)
- CORS errors
- Mixed content warnings
4. Check Page Content
interceptor read --text-onlyCompare visible text content against what's expected. Empty or missing sections indicate rendering failures.
5. Document Findings
Before touching any code, document:
- What the page actually shows (screenshot evidence)
- Console errors found (with specific error messages)
- Network failures (with specific URLs and status codes)
- Gap between expected and actual state
Only THEN proceed to code analysis with specific hypotheses grounded in the browser evidence.
Notes
- This workflow exists because of real production incidents where hours were spent on code analysis and wrong-theory fixes were shipped to prod — when the actual cause (missing JS chunks, 404s on bundles, CORS errors) was visible in the browser console in under a minute. Reproduce first, theorize second.
- "curl returns 200" is NOT reproduction. You must SEE the rendered page.
- Code analysis without reproduction is speculation, not debugging.
- For authenticated pages, Interceptor uses your real Chrome sessions automatically.
TestForm Workflow
Voice Notification
curl -s -X POST http://localhost:31337/notify \
-H "Content-Type: application/json" \
-d '{"message": "Running the TestForm workflow in the Interceptor skill to test a form"}' \
> /dev/null 2>&1 &Running TestForm in Interceptor...
---
Discover, fill, submit, and verify a form on any page. Uses Interceptor's semantic element finding to locate form fields by role and name, fills them with test data, submits, and verifies the result.
When to Use
- Testing signup, login, or contact forms after changes
- Verifying form validation behavior (required fields, email format, etc.)
- Checking that form submissions reach the correct API endpoint
- Testing forms on authenticated pages where agent-browser can't reach
Steps
1. Open the Page with the Form
interceptor open "<PAGE_URL>"2. Discover Form Fields
Use the find command to locate input fields:
interceptor find "" --role textbox
interceptor find "" --role combobox
interceptor find "" --role checkboxOr get the full element tree and identify form elements:
interceptor treeLook for elements with roles: textbox, combobox, checkbox, radio, spinbutton, slider, switch.
3. Fill Form Fields
Fill each field using its semantic selector or ref:
# By semantic selector (preferred — survives DOM changes)
interceptor type "textbox:Email" "test@example.com"
interceptor type "textbox:Name" "Test User"
interceptor select "combobox:Country" "United States"
# By element ref (from tree output)
interceptor act e5 "test@example.com"
interceptor act e8 "Test User"For checkboxes and radio buttons:
interceptor click "checkbox:Terms and Conditions"
interceptor click "radio:Monthly Plan"4. Verify Pre-Submit State
Before submitting, verify the form looks correct:
( cd /tmp/pai-screenshots && interceptor screenshot --save )Read the screenshot to confirm fields are populated correctly and no validation errors are showing.
5. Submit the Form
interceptor click "button:Submit"
interceptor wait-stableOr use the keyboard:
interceptor keys "Enter"
interceptor wait-stable6. Verify Submission Result
Check what happened after submission:
# Check the page content for success/error messages
interceptor read --text-only
# Check network for the API call
interceptor net log --json
# Capture the result page
( cd /tmp/pai-screenshots && interceptor screenshot --save )Look for:
- Success confirmation message or redirect
- API call to the expected endpoint with correct method (POST/PUT)
- Response status code (200/201 for success)
- Any error messages or validation failures
7. Test Edge Cases (Optional)
For thorough form testing, repeat with edge case inputs:
# Empty required fields — submit without filling
interceptor click "button:Submit"
interceptor read --text-only # Check for validation messages
# Invalid email format
interceptor type "textbox:Email" "not-an-email"
interceptor click "button:Submit"
interceptor read --text-only
# Very long input
interceptor type "textbox:Name" "A very long name that might break layout assumptions in the form"
( cd /tmp/pai-screenshots && interceptor screenshot --save )Notes
- Semantic selectors (
"textbox:Email") use accessible role + name. If a form field has no accessible name, it will only be findable by ref ID — consider fixing the accessibility. interceptor typeclears the field before typing. Useinterceptor type <ref> "text" --appendto add to existing content.- For dropdowns/selects, use
interceptor select <ref> "value"instead of click-based selection. - Network log captures the actual API request triggered by form submission — useful for verifying the correct endpoint and payload shape.
- For password fields, use
interceptor act <ref> "value" --osfor OS-level trusted input that bypasses autocomplete detection.
Update Workflow
Voice Notification
curl -s -X POST http://localhost:31337/notify \
-H "Content-Type: application/json" \
-d '{"message": "Running the Update workflow in the Interceptor skill to rebuild interceptor"}' \
> /dev/null 2>&1 &Running Update in Interceptor...
---
Rebuild interceptor from latest source and verify the full pipeline.
When to Use
- After pulling new commits from slop-browser repo
- If interceptor commands fail unexpectedly
- Periodic capability check
Steps
1. Pull Latest
cd ~/Projects/interceptor && git fetch origin && git status -unoIf upstream force-pushed (common with this repo), git pull will refuse. Check for local modifications first (git status), preserve any patches by hand, then:
cd ~/Projects/interceptor && git reset --hard origin/main2. Install New Dependencies
cd ~/Projects/interceptor && bun installAlways run before build — upstream may add deps (e.g. ocrad.js for canvas OCR arrived in v0.8.0). Build will fail with "Could not resolve" otherwise.
3. Build
cd ~/Projects/interceptor && bash scripts/build.shProduces:
dist/interceptor— CLIdaemon/interceptor-daemon— native messaging hostextension/dist/— Chrome extension (manifest reflects upstream version)dist/interceptor-bridge— Swift binary for OS-level input simulation (macOS only, optional)
4. Install Binaries
cp ~/Projects/interceptor/dist/interceptor /opt/homebrew/bin/
cp ~/Projects/interceptor/daemon/interceptor-daemon /opt/homebrew/bin/5. Re-register Native Messaging
cd ~/Projects/interceptor && bash scripts/install.sh --chrome --skip-extension--skip-extension is the right path for Chrome — branded Chrome ignores --load-extension anyway, and the extension reload is a manual step (see "Extension Reload" below). The script regenerates ~/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.interceptor.host.json with the current allowed extension IDs.
6. (Optional) Bridge — macOS Native Helper App
The bridge is the macOS-native helper that gives Interceptor capabilities the Chrome extension cannot provide on its own. Documented end-to-end here so the skill owns the full lifecycle — install, verify, security model, troubleshoot, uninstall — without referencing the upstream README.
What it adds (35 actions across 23 domains): OS-level synthetic input (act --os keystrokes/mouse/scroll/drag), accessibility tree of native macOS apps, app control, screen capture beyond Chrome, clipboard read/write, audio listen + speech recognition, file ops, system notifications, vision, NLP, Apple Intelligence, HealthKit access, display info, monitor (event subscription).
Skip the bridge entirely if all you do is in-page web automation — the extension covers ~95% of typical work without it.
6a. Install (use this procedure, not upstream install-bridge.sh)
The upstream script breaks on Apple Silicon: /usr/local/bin requires sudo, but invoking the whole script with sudo makes launchctl bootstrap "gui/$(id -u)" target uid 0 instead of the user. Three commands handle it correctly:
# 1. Binary into /usr/local/bin (needs sudo — root:wheel 755)
sudo cp ~/Projects/interceptor/dist/interceptor-bridge /usr/local/bin/interceptor-bridge
sudo chmod +x /usr/local/bin/interceptor-bridge
# 2. Write LaunchAgent plist into $HOME (no sudo)
cat > ~/Library/LaunchAgents/com.interceptor.bridge.plist <<'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.interceptor.bridge</string>
<key>ProgramArguments</key><array><string>/usr/local/bin/interceptor-bridge</string></array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict>
<key>StandardOutPath</key><string>/tmp/interceptor-bridge.stdout.log</string>
<key>StandardErrorPath</key><string>/tmp/interceptor-bridge.stderr.log</string>
<key>ThrottleInterval</key><integer>5</integer>
</dict>
</plist>
PLIST
# 3. Load it as the user (no sudo — uid is captured at script-call time)
launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/com.interceptor.bridge.plistlaunchctl bootstrap exits 1 on success (its diagnostic output is on stderr) — that's fine; verify with the next step.
6b. Verify
launchctl list | grep com.interceptor.bridge # → expect: <PID> 0 com.interceptor.bridge
ls -la /tmp/interceptor-bridge.sock # → expect: srwxr-xr-x <user> staff
interceptor status # → expect: bridge: running with pid + socketIf interceptor status shows bridge: not running despite launchctl listing the agent, the helper crashed on startup — check /tmp/interceptor-bridge.stderr.log.
6c. Security model — read this before you install
Honest disclosure, not reassurance:
- Transport is a UNIX domain socket at
/tmp/interceptor-bridge.sock— local
only, no network listener. Cannot be reached from another machine.
- There is ZERO authentication on the socket.
Transport.swiftaccepts any
client connection and processes any framed JSON payload. The socket is created with mode 755 (the default umask result), meaning any local process — your shell scripts, any installed app, anything you run as your user — can connect and execute every bridge action.
- Action surface includes: synthetic keystrokes/mouse, screenshots, clipboard
read/write, audio capture/listen, file ops, system notifications, accessibility tree of every running app. macOS will prompt for Accessibility / Input Monitoring / Screen Recording / Microphone the first time those are exercised; once granted, every client of the socket inherits them.
- Threat model on a single-user Mac: anything running as you can already
keylog you and read your clipboard with sufficient effort. The bridge makes this scriptable from anything that can write to /tmp. The marginal risk is supply-chain — a malicious package installed by bun, brew, or npm no longer needs its own Accessibility grant; the bridge has the grants for it.
- Multi-user Macs: the socket's
o+rxbit means other local users can
connect. If this matters, harden by either (a) running the bridge with a restrictive umask, or (b) adding a chmod 700 of the socket as a post-start hook in the plist. the principal's Mac is single-user; not addressed here.
- Binary provenance: built locally from the slop-browser source we just
pulled. Not a downloaded prebuilt — provenance is the Swift source under ~/Projects/interceptor/interceptor-bridge/Sources/.
6d. Troubleshoot
| Symptom | Cause | Fix |
|---|---|---|
launchctl bootstrap says "service already loaded" | Prior install lingering | launchctl bootout "gui/$(id -u)/com.interceptor.bridge" then re-bootstrap |
interceptor status shows bridge not running | First-action TCC prompt blocked | Trigger any act --os once, accept macOS prompts in System Settings → Privacy & Security |
| Socket exists but writes fail | macOS quarantine on the binary | xattr -d com.apple.quarantine /usr/local/bin/interceptor-bridge |
| Bridge restart-loops every ~5s | Crash on launch | tail /tmp/interceptor-bridge.stderr.log; usually missing entitlement or unsigned-binary block |
6e. Uninstall
launchctl bootout "gui/$(id -u)/com.interceptor.bridge"
rm ~/Library/LaunchAgents/com.interceptor.bridge.plist
sudo rm /usr/local/bin/interceptor-bridge
rm -f /tmp/interceptor-bridge.sock /tmp/interceptor-bridge.pid
rm -f /tmp/interceptor-bridge.stdout.log /tmp/interceptor-bridge.stderr.logOptional: revoke macOS permissions in System Settings → Privacy & Security (Accessibility, Input Monitoring, Screen Recording, Microphone, etc.) by removing the interceptor-bridge entry from each list.
7. Extension Reload (manual — Chrome won't auto-refresh unpacked extensions)
If extension/dist/manifest.json changed (especially version or key):
1. Open chrome://extensions, enable Developer Mode 2. Delete the existing Interceptor card (don't just hit reload — if the manifest key changed, the extension ID changed and the old card is dead) 3. Load unpacked → ~/Projects/interceptor/extension/dist 4. Quit Chrome fully (⌘Q, not just close window) and relaunch — service worker needs a clean restart, especially with userScripts permission added 5. Accept any new permission prompts (e.g. userScripts)
If only JS/HTML inside extension/dist/ changed (no manifest changes), clicking the reload arrow on the existing card is enough.
8. Verify
interceptor status
interceptor open "https://example.com"status reports both daemon and bridge lines (bridge shows "not running" if you skipped step 6 — that's fine). open should return tree + extracted text.
Notes
- Native messaging manifest's
allowed_originsis regenerated byinstall.sh—
the current build allows three extension IDs including the keyed hkjbaciefhhgekldhncknbjkofbpenng (the deterministic ID baked in by extension/manifest.json's key field).
- Force-push from upstream is normal —
slop-browserrewrites main on releases
(v0.5.0 → v0.8.0 was a single force-push touching 155 files).
- Watch
extension/src/content/data/extract.ts— the body/HTML extract limits
default to 10K/10K/50K. We patch them to 10M to support large-page reads; re-apply after each upstream pull.
VerifyDeploy Workflow
Voice Notification
curl -s -X POST http://localhost:31337/notify \
-H "Content-Type: application/json" \
-d '{"message": "Running the VerifyDeploy workflow in the Interceptor skill to verify a deployment"}' \
> /dev/null 2>&1 &Running VerifyDeploy in Interceptor...
---
Verify a deployment by opening the target URL in real Chrome, checking for errors, and capturing screenshot evidence. Works with both authenticated and public pages since Interceptor uses your real browser sessions.
When to Use
- After deploying any web project
- When the Algorithm's Verification Doctrine Rule 1 requires live-probe evidence
- After CSS/layout/content changes that need visual confirmation
- When agent-browser can't reach the page (auth wall, bot detection)
Steps
1. Open the Target URL
interceptor open "<DEPLOY_URL>"This navigates, waits for DOM stability, and returns the element tree + visible text in one call. If the page requires authentication, Interceptor uses your real Chrome sessions automatically.
For pages that load slowly (heavy SPAs, SSR hydration):
interceptor open "<DEPLOY_URL>" --timeout 100002. Check for Errors
Run JS in the page context to capture console errors:
interceptor eval "JSON.stringify(window.__interceptor_errors || [])" --mainCheck for visible error indicators in the element tree from Step 1. Look for:
- Error banners, modals, or toast messages
- "404", "500", "not found", "error" in visible text
- Blank/empty content areas that should have content
3. Check Network for Failed Requests
interceptor net log --jsonLook for non-200 status codes, especially:
- 404s on JS/CSS chunks (missing build artifacts)
- 500s on API endpoints
- CORS errors
4. Capture Screenshot Evidence
( cd /tmp/pai-screenshots && interceptor screenshot --save )Read the screenshot image to visually confirm the page renders correctly. This is the live-probe evidence the Verification Doctrine requires.
For full-page captures (long pages, below-the-fold content):
( cd /tmp/pai-screenshots && interceptor screenshot --full --save )5. Report
If everything passes: mark the ISC criterion as [x] with the screenshot as evidence.
If errors found: report the specific errors (console, network, visual) before attempting fixes. Do NOT theorize from code — the browser evidence is primary.
Notes
- For authenticated pages, Interceptor uses your real Chrome login sessions. No profile setup needed.
- For public pages where speed matters and auth isn't needed, agent-browser (Browser skill) is acceptable.
- Always use
http://localhost:PORTinstead oflocalhost:PORTfor local dev URLs. - If Chrome is not running, start it first. Interceptor requires an active Chrome instance with the extension loaded.