cdpilot 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # browserctl
1
+ # cdpilot
2
2
 
3
3
  > Zero-dependency browser automation from your terminal. One command, full control.
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/browserctl.svg)](https://www.npmjs.com/package/browserctl)
5
+ [![npm version](https://img.shields.io/npm/v/cdpilot.svg)](https://www.npmjs.com/package/cdpilot)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
7
7
 
8
8
  <!-- Demo GIF will be added after first release -->
@@ -10,27 +10,27 @@
10
10
  ## Quick Start
11
11
 
12
12
  ```bash
13
- npx browserctl launch # Start browser with CDP
14
- npx browserctl go https://example.com
15
- npx browserctl shot # Take screenshot
13
+ npx cdpilot launch # Start browser with CDP
14
+ npx cdpilot go https://example.com
15
+ npx cdpilot shot # Take screenshot
16
16
  ```
17
17
 
18
18
  No config files. No boilerplate. Just `npx` and go.
19
19
 
20
- ## Why browserctl?
20
+ ## Why cdpilot?
21
21
 
22
22
  AI agents and developers need browser control that **just works**:
23
23
 
24
- - **Zero config** — `npx browserctl launch` starts an isolated browser session
24
+ - **Zero config** — `npx cdpilot launch` starts an isolated browser session
25
25
  - **Zero dependency** — No Puppeteer, no Playwright, no Selenium. Pure CDP over HTTP
26
26
  - **40+ commands** — Navigate, click, type, screenshot, network, console, accessibility, and more
27
27
  - **AI-agent friendly** — Designed for Claude, GPT, Gemini, and any LLM tool-use workflow
28
- - **Isolated sessions** — Your personal browser stays untouched. browserctl runs in its own profile
28
+ - **Isolated sessions** — Your personal browser stays untouched. cdpilot runs in its own profile
29
29
  - **Privacy-first** — Everything runs locally. No data leaves your machine
30
30
 
31
31
  ### Why Brave?
32
32
 
33
- browserctl uses [Brave Browser](https://brave.com) as its engine. Here's why:
33
+ cdpilot uses [Brave Browser](https://brave.com) as its engine. Here's why:
34
34
 
35
35
  | Feature | Brave | Chrome | Why it matters |
36
36
  |---------|-------|--------|---------------|
@@ -43,16 +43,16 @@ browserctl uses [Brave Browser](https://brave.com) as its engine. Here's why:
43
43
 
44
44
  **TL;DR:** Brave = Chrome's power + built-in privacy + less bloat. Perfect for automation.
45
45
 
46
- > browserctl also works with Chrome and Chromium as fallback. Brave is recommended, not required.
46
+ > cdpilot also works with Chrome and Chromium as fallback. Brave is recommended, not required.
47
47
 
48
48
  ## Installation
49
49
 
50
50
  ```bash
51
51
  # Use directly (no install needed)
52
- npx browserctl <command>
52
+ npx cdpilot <command>
53
53
 
54
54
  # Or install globally
55
- npm i -g browserctl
55
+ npm i -g cdpilot
56
56
  ```
57
57
 
58
58
  **Requirements:** Node.js 18+ and one of: Brave Browser, Google Chrome, or Chromium.
@@ -60,9 +60,9 @@ npm i -g browserctl
60
60
  ### First-time setup
61
61
 
62
62
  ```bash
63
- npx browserctl setup # Auto-detect browser, create isolated profile
64
- npx browserctl launch # Start browser with CDP enabled
65
- npx browserctl status # Check connection
63
+ npx cdpilot setup # Auto-detect browser, create isolated profile
64
+ npx cdpilot launch # Start browser with CDP enabled
65
+ npx cdpilot status # Check connection
66
66
  ```
67
67
 
68
68
  ## Commands
@@ -70,126 +70,126 @@ npx browserctl status # Check connection
70
70
  ### Navigation & Content
71
71
 
72
72
  ```bash
73
- browserctl go <url> # Navigate to URL
74
- browserctl content # Get page text content
75
- browserctl html # Get page HTML
76
- browserctl shot [file] # Take screenshot (PNG)
77
- browserctl pdf [file] # Save page as PDF
73
+ cdpilot go <url> # Navigate to URL
74
+ cdpilot content # Get page text content
75
+ cdpilot html # Get page HTML
76
+ cdpilot shot [file] # Take screenshot (PNG)
77
+ cdpilot pdf [file] # Save page as PDF
78
78
  ```
79
79
 
80
80
  ### Interaction
81
81
 
82
82
  ```bash
83
- browserctl click <selector> # Click element
84
- browserctl type <selector> <text># Type into input
85
- browserctl fill <selector> <val> # Set input value (React-compatible)
86
- browserctl submit <form> # Submit form
87
- browserctl hover <selector> # Hover element
88
- browserctl keys <combo> # Keyboard shortcut (ctrl+a, enter, etc.)
89
- browserctl scroll-to <selector> # Scroll element into view
90
- browserctl drag <from> <to> # Drag and drop
83
+ cdpilot click <selector> # Click element
84
+ cdpilot type <selector> <text># Type into input
85
+ cdpilot fill <selector> <val> # Set input value (React-compatible)
86
+ cdpilot submit <form> # Submit form
87
+ cdpilot hover <selector> # Hover element
88
+ cdpilot keys <combo> # Keyboard shortcut (ctrl+a, enter, etc.)
89
+ cdpilot scroll-to <selector> # Scroll element into view
90
+ cdpilot drag <from> <to> # Drag and drop
91
91
  ```
92
92
 
93
93
  ### Debugging
94
94
 
95
95
  ```bash
96
- browserctl console [url] # Capture console logs
97
- browserctl network [url] # Monitor network requests
98
- browserctl debug [url] # Full diagnostic (console+network+perf+shot)
99
- browserctl perf # Performance metrics
100
- browserctl eval <js> # Execute JavaScript
96
+ cdpilot console [url] # Capture console logs
97
+ cdpilot network [url] # Monitor network requests
98
+ cdpilot debug [url] # Full diagnostic (console+network+perf+shot)
99
+ cdpilot perf # Performance metrics
100
+ cdpilot eval <js> # Execute JavaScript
101
101
  ```
102
102
 
103
103
  ### Tab Management
104
104
 
105
105
  ```bash
106
- browserctl tabs # List open tabs
107
- browserctl new-tab [url] # Open new tab
108
- browserctl switch-tab <id> # Switch to tab
109
- browserctl close-tab [id] # Close tab
110
- browserctl close # Close active tab
106
+ cdpilot tabs # List open tabs
107
+ cdpilot new-tab [url] # Open new tab
108
+ cdpilot switch-tab <id> # Switch to tab
109
+ cdpilot close-tab [id] # Close tab
110
+ cdpilot close # Close active tab
111
111
  ```
112
112
 
113
113
  ### Network Control
114
114
 
115
115
  ```bash
116
- browserctl throttle slow3g # Simulate slow 3G
117
- browserctl throttle fast3g # Simulate fast 3G
118
- browserctl throttle offline # Go offline
119
- browserctl throttle off # Back to normal
120
- browserctl proxy <url> # Set proxy
121
- browserctl proxy off # Remove proxy
116
+ cdpilot throttle slow3g # Simulate slow 3G
117
+ cdpilot throttle fast3g # Simulate fast 3G
118
+ cdpilot throttle offline # Go offline
119
+ cdpilot throttle off # Back to normal
120
+ cdpilot proxy <url> # Set proxy
121
+ cdpilot proxy off # Remove proxy
122
122
  ```
123
123
 
124
124
  ### Request Interception
125
125
 
126
126
  ```bash
127
- browserctl intercept block <pattern> # Block requests
128
- browserctl intercept mock <pattern> <json-file> # Mock responses
129
- browserctl intercept headers <pattern> <header:value> # Add headers
130
- browserctl intercept list # List active rules
131
- browserctl intercept clear # Clear all rules
127
+ cdpilot intercept block <pattern> # Block requests
128
+ cdpilot intercept mock <pattern> <json-file> # Mock responses
129
+ cdpilot intercept headers <pattern> <header:value> # Add headers
130
+ cdpilot intercept list # List active rules
131
+ cdpilot intercept clear # Clear all rules
132
132
  ```
133
133
 
134
134
  ### Device Emulation
135
135
 
136
136
  ```bash
137
- browserctl emulate iphone # iPhone emulation
138
- browserctl emulate ipad # iPad emulation
139
- browserctl emulate android # Android emulation
140
- browserctl emulate reset # Back to desktop
137
+ cdpilot emulate iphone # iPhone emulation
138
+ cdpilot emulate ipad # iPad emulation
139
+ cdpilot emulate android # Android emulation
140
+ cdpilot emulate reset # Back to desktop
141
141
  ```
142
142
 
143
143
  ### Geolocation
144
144
 
145
145
  ```bash
146
- browserctl geo istanbul # Set location to Istanbul
147
- browserctl geo london # Set location to London
148
- browserctl geo 41.01 28.97 # Custom coordinates
149
- browserctl geo off # Remove override
146
+ cdpilot geo istanbul # Set location to Istanbul
147
+ cdpilot geo london # Set location to London
148
+ cdpilot geo 41.01 28.97 # Custom coordinates
149
+ cdpilot geo off # Remove override
150
150
  ```
151
151
 
152
152
  ### Accessibility
153
153
 
154
154
  ```bash
155
- browserctl a11y # Full accessibility tree
156
- browserctl a11y summary # Quick summary
157
- browserctl a11y find <role> # Find elements by ARIA role
155
+ cdpilot a11y # Full accessibility tree
156
+ cdpilot a11y summary # Quick summary
157
+ cdpilot a11y find <role> # Find elements by ARIA role
158
158
  ```
159
159
 
160
160
  ### Session Management
161
161
 
162
162
  ```bash
163
- browserctl session # Current session info
164
- browserctl sessions # List all sessions
165
- browserctl session-close [id] # Close session
163
+ cdpilot session # Current session info
164
+ cdpilot sessions # List all sessions
165
+ cdpilot session-close [id] # Close session
166
166
  ```
167
167
 
168
168
  ### Advanced
169
169
 
170
170
  ```bash
171
- browserctl cookies [domain] # List cookies
172
- browserctl storage # localStorage contents
173
- browserctl upload <sel> <file> # Upload file to input
174
- browserctl multi-eval <js> # Execute JS in all tabs
175
- browserctl headless [on|off] # Toggle headless mode
176
- browserctl frame list # List iframes
177
- browserctl dialog auto-accept # Auto-accept dialogs
178
- browserctl permission grant geo # Grant geolocation
171
+ cdpilot cookies [domain] # List cookies
172
+ cdpilot storage # localStorage contents
173
+ cdpilot upload <sel> <file> # Upload file to input
174
+ cdpilot multi-eval <js> # Execute JS in all tabs
175
+ cdpilot headless [on|off] # Toggle headless mode
176
+ cdpilot frame list # List iframes
177
+ cdpilot dialog auto-accept # Auto-accept dialogs
178
+ cdpilot permission grant geo # Grant geolocation
179
179
  ```
180
180
 
181
181
  ## Use with AI Agents
182
182
 
183
- browserctl is designed to be called by AI agents as a tool:
183
+ cdpilot is designed to be called by AI agents as a tool:
184
184
 
185
185
  ### Claude Code (MCP)
186
186
 
187
187
  ```json
188
188
  {
189
189
  "mcpServers": {
190
- "browserctl": {
190
+ "cdpilot": {
191
191
  "command": "npx",
192
- "args": ["browserctl", "mcp"]
192
+ "args": ["cdpilot", "mcp"]
193
193
  }
194
194
  }
195
195
  }
@@ -211,7 +211,7 @@ browserctl is designed to be called by AI agents as a tool:
211
211
 
212
212
  ```python
213
213
  import subprocess
214
- result = subprocess.run(["npx", "browserctl", "go", url], capture_output=True, text=True)
214
+ result = subprocess.run(["npx", "cdpilot", "go", url], capture_output=True, text=True)
215
215
  print(result.stdout)
216
216
  ```
217
217
 
@@ -221,14 +221,14 @@ print(result.stdout)
221
221
  |----------|---------|-------------|
222
222
  | `CDP_PORT` | `9222` | CDP debugging port |
223
223
  | `CHROME_BIN` | Auto-detect | Browser binary path |
224
- | `BROWSERCTL_PROFILE` | `~/.browserctl/profile` | Isolated browser profile |
224
+ | `CDPILOT_PROFILE` | `~/.cdpilot/profile` | Isolated browser profile |
225
225
  | `BROWSER_SESSION` | Auto | Session identifier |
226
226
 
227
227
  ## How It Works
228
228
 
229
229
  ```
230
230
  ┌─────────────┐ HTTP/WebSocket ┌──────────────┐
231
- browserctl │ ◄──────────────────────► │ Brave/Chrome │
231
+ cdpilot │ ◄──────────────────────► │ Brave/Chrome │
232
232
  │ (CLI) │ Chrome DevTools │ (CDP mode) │
233
233
  └─────────────┘ Protocol └──────────────┘
234
234
  │ │
@@ -242,7 +242,7 @@ print(result.stdout)
242
242
 
243
243
  ## Comparison
244
244
 
245
- | Feature | browserctl | Puppeteer | Playwright | Selenium |
245
+ | Feature | cdpilot | Puppeteer | Playwright | Selenium |
246
246
  |---------|-----------|-----------|------------|----------|
247
247
  | Install size | **~50KB** | 400MB+ | 200MB+ | 100MB+ |
248
248
  | Dependencies | **0** | 50+ | 30+ | Java + drivers |
@@ -254,18 +254,18 @@ print(result.stdout)
254
254
 
255
255
  ## Monetization / Pro (Coming Soon)
256
256
 
257
- browserctl CLI is and will always be **free and open source** (MIT).
257
+ cdpilot CLI is and will always be **free and open source** (MIT).
258
258
 
259
259
  Future paid offerings:
260
- - **browserctl cloud** — Remote browser instances, no local browser needed
260
+ - **cdpilot cloud** — Remote browser instances, no local browser needed
261
261
  - **Team dashboard** — Shared sessions, audit logs, usage analytics
262
262
  - **Priority support** — Direct help for enterprise integrations
263
263
 
264
264
  ## Contributing
265
265
 
266
266
  ```bash
267
- git clone https://github.com/user/browserctl.git
268
- cd browserctl
267
+ git clone https://github.com/mehmetnadir/cdpilot.git
268
+ cd cdpilot
269
269
  npm install
270
270
  npm test
271
271
  ```
@@ -279,5 +279,5 @@ MIT — do whatever you want.
279
279
  ---
280
280
 
281
281
  <p align="center">
282
- Built with the <a href="https://github.com/nicedoc/browserctl">browserctl</a> mindset: one tool, one job, done right.
282
+ Built with the <a href="https://github.com/mehmetnadir/cdpilot">cdpilot</a> mindset: one tool, one job, done right.
283
283
  </p>
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * browserctl — Zero-dependency browser automation CLI
5
- * Entry point: detects Python, finds browser, delegates to browserctl.py
4
+ * cdpilot — Zero-dependency browser automation CLI
5
+ * Entry point: detects Python, finds browser, delegates to cdpilot.py
6
6
  */
7
7
 
8
8
  const { execSync, spawn } = require('child_process');
@@ -10,7 +10,7 @@ const path = require('path');
10
10
  const fs = require('fs');
11
11
  const os = require('os');
12
12
 
13
- const SCRIPT = path.join(__dirname, '..', 'src', 'browserctl.py');
13
+ const SCRIPT = path.join(__dirname, '..', 'src', 'cdpilot.py');
14
14
  const VERSION = require('../package.json').version;
15
15
 
16
16
  // ── Browser Detection ──
@@ -85,11 +85,11 @@ function findPython() {
85
85
 
86
86
  function runSetup() {
87
87
  const browser = findBrowser();
88
- const profileDir = process.env.BROWSERCTL_PROFILE
89
- || path.join(os.homedir(), '.browserctl', 'profile');
88
+ const profileDir = process.env.CDPILOT_PROFILE
89
+ || path.join(os.homedir(), '.cdpilot', 'profile');
90
90
  const port = process.env.CDP_PORT || '9222';
91
91
 
92
- console.log('\n browserctl setup\n');
92
+ console.log('\n cdpilot setup\n');
93
93
  console.log(` Browser: ${browser || '❌ Not found'}`);
94
94
  console.log(` Profile: ${profileDir}`);
95
95
  console.log(` CDP Port: ${port}`);
@@ -116,14 +116,14 @@ function runSetup() {
116
116
  console.log(`\n ✓ Profile exists: ${profileDir}`);
117
117
  }
118
118
 
119
- console.log(' ✓ Setup complete! Run: browserctl launch\n');
119
+ console.log(' ✓ Setup complete! Run: cdpilot launch\n');
120
120
  }
121
121
 
122
122
  // ── Status Command ──
123
123
 
124
124
  function runStatus() {
125
125
  const port = process.env.CDP_PORT || '9222';
126
- console.log(`\n browserctl status (port ${port})\n`);
126
+ console.log(`\n cdpilot status (port ${port})\n`);
127
127
 
128
128
  try {
129
129
  const http = require('http');
@@ -144,12 +144,12 @@ function runStatus() {
144
144
  });
145
145
  req.on('error', () => {
146
146
  console.log(' ❌ No browser connected on this port.');
147
- console.log(' Run: browserctl launch\n');
147
+ console.log(' Run: cdpilot launch\n');
148
148
  });
149
149
  req.on('timeout', () => {
150
150
  req.destroy();
151
151
  console.log(' ❌ Connection timeout.');
152
- console.log(' Run: browserctl launch\n');
152
+ console.log(' Run: cdpilot launch\n');
153
153
  });
154
154
  } catch {
155
155
  console.log(' ❌ Could not check status.\n');
@@ -159,17 +159,17 @@ function runStatus() {
159
159
  // ── Version ──
160
160
 
161
161
  function showVersion() {
162
- console.log(`browserctl v${VERSION}`);
162
+ console.log(`cdpilot v${VERSION}`);
163
163
  }
164
164
 
165
165
  // ── Help ──
166
166
 
167
167
  function showHelp() {
168
168
  console.log(`
169
- browserctl v${VERSION} — Zero-dependency browser automation
169
+ cdpilot v${VERSION} — Zero-dependency browser automation
170
170
 
171
171
  USAGE
172
- browserctl <command> [args]
172
+ cdpilot <command> [args]
173
173
 
174
174
  SETUP
175
175
  setup Auto-detect browser, create isolated profile
@@ -203,7 +203,10 @@ function showHelp() {
203
203
  new-tab [url] Open new tab
204
204
  close-tab [id] Close tab
205
205
 
206
- More: https://github.com/user/browserctl#commands
206
+ AI AGENT
207
+ mcp Start MCP server (stdin/stdout JSON-RPC)
208
+
209
+ More: https://github.com/mehmetnadir/cdpilot#commands
207
210
  `);
208
211
  }
209
212
 
@@ -239,14 +242,14 @@ if (cmd === 'status') {
239
242
  }
240
243
 
241
244
  const browser = findBrowser();
242
- const profileDir = process.env.BROWSERCTL_PROFILE
243
- || path.join(os.homedir(), '.browserctl', 'profile');
245
+ const profileDir = process.env.CDPILOT_PROFILE
246
+ || path.join(os.homedir(), '.cdpilot', 'profile');
244
247
  const port = process.env.CDP_PORT || '9222';
245
248
 
246
249
  const env = {
247
250
  ...process.env,
248
251
  CDP_PORT: port,
249
- BROWSERCTL_PROFILE: profileDir,
252
+ CDPILOT_PROFILE: profileDir,
250
253
  };
251
254
 
252
255
  if (browser && !process.env.CHROME_BIN) {
package/package.json CHANGED
@@ -1,36 +1,25 @@
1
1
  {
2
2
  "name": "cdpilot",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Zero-dependency browser automation from your terminal. One command, full control.",
5
5
  "bin": {
6
- "bctl": "./bin/browserctl.js",
7
- "browserctl": "./bin/browserctl.js"
6
+ "cdpilot": "./bin/cdpilot.js",
7
+ "bctl": "./bin/cdpilot.js"
8
8
  },
9
9
  "scripts": {
10
10
  "test": "node test/test.js",
11
- "start": "node bin/browserctl.js"
11
+ "start": "node bin/cdpilot.js"
12
12
  },
13
13
  "keywords": [
14
- "browser",
15
- "automation",
16
- "cdp",
17
- "chrome-devtools-protocol",
18
- "cli",
19
- "devtools",
20
- "headless",
21
- "screenshot",
22
- "testing",
23
- "web-scraping",
24
- "ai-agent",
25
- "mcp",
26
- "claude",
27
- "brave"
14
+ "browser", "automation", "cdp", "chrome-devtools-protocol", "cli",
15
+ "devtools", "headless", "screenshot", "testing", "web-scraping",
16
+ "ai-agent", "mcp", "claude", "brave"
28
17
  ],
29
18
  "author": "",
30
19
  "license": "MIT",
31
20
  "repository": {
32
21
  "type": "git",
33
- "url": "https://github.com/user/browserctl"
22
+ "url": "https://github.com/mehmetnadir/cdpilot"
34
23
  },
35
24
  "engines": {
36
25
  "node": ">=18.0.0"
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- browserctl — Zero-dependency browser automation from your terminal.
3
+ cdpilot — Zero-dependency browser automation from your terminal.
4
4
 
5
5
  Controls any Chromium-based browser (Brave, Chrome, Chromium) via the
6
6
  Chrome DevTools Protocol (CDP). No Puppeteer, no Playwright, no Selenium.
7
7
 
8
8
  Usage:
9
- browserctl <command> [arguments]
9
+ cdpilot <command> [arguments]
10
10
 
11
11
  Environment:
12
12
  CDP_PORT CDP debugging port (default: 9222)
@@ -14,7 +14,7 @@ Environment:
14
14
  BROWSERCTL_PROFILE Isolated browser profile directory
15
15
  """
16
16
 
17
- __version__ = "0.1.0"
17
+ __version__ = "0.1.1"
18
18
 
19
19
  import asyncio
20
20
  import json
@@ -27,13 +27,13 @@ import subprocess
27
27
  import shutil
28
28
 
29
29
  # ─── Session Configuration ───
30
- # browserctl runs in its own Chrome instance on the configured CDP port.
30
+ # cdpilot runs in its own Chrome instance on the configured CDP port.
31
31
  # The user's existing Chrome/browser session is not affected.
32
32
 
33
33
  CDP_PORT = int(os.environ.get("CDP_PORT", "9222"))
34
34
  CDP_BASE = f"http://127.0.0.1:{CDP_PORT}"
35
35
  CHROME_BIN = os.environ.get("CHROME_BIN")
36
- PROFILE_DIR = os.environ.get("BROWSERCTL_PROFILE", os.path.expanduser("~/.browserctl/profile"))
36
+ PROFILE_DIR = os.environ.get("CDPILOT_PROFILE", os.path.expanduser("~/.cdpilot/profile"))
37
37
  SCREENSHOT_DIR = "/tmp"
38
38
 
39
39
  DEV_EXTENSIONS_FILE = os.path.join(PROFILE_DIR, 'dev-extensions.json')
@@ -52,7 +52,7 @@ def _get_session_id():
52
52
  if sid:
53
53
  return sid
54
54
  # Default session — all commands share the same window
55
- return "browserctl-default"
55
+ return "cdpilot-default"
56
56
 
57
57
  def _load_sessions():
58
58
  """Read the session registry."""
@@ -93,11 +93,11 @@ _current_session_id = None # lazy init
93
93
 
94
94
  GLOW_CSS = """
95
95
  (function() {
96
- if (document.getElementById('browserctl-glow')) return 'already active';
96
+ if (document.getElementById('cdpilot-glow')) return 'already active';
97
97
  const style = document.createElement('style');
98
- style.id = 'browserctl-glow';
98
+ style.id = 'cdpilot-glow';
99
99
  style.textContent = `
100
- @keyframes browserctl-pulse {
100
+ @keyframes cdpilot-pulse {
101
101
  0%, 100% { box-shadow: inset 0 0 20px 4px rgba(34, 197, 94, 0.25), inset 0 0 60px 8px rgba(34, 197, 94, 0.08); }
102
102
  50% { box-shadow: inset 0 0 30px 6px rgba(34, 197, 94, 0.35), inset 0 0 80px 12px rgba(34, 197, 94, 0.12); }
103
103
  }
@@ -107,7 +107,7 @@ GLOW_CSS = """
107
107
  top: 0; left: 0; right: 0; bottom: 0;
108
108
  pointer-events: none;
109
109
  z-index: 2147483647;
110
- animation: browserctl-pulse 2s ease-in-out infinite;
110
+ animation: cdpilot-pulse 2s ease-in-out infinite;
111
111
  border: 2px solid rgba(34, 197, 94, 0.3);
112
112
  border-radius: 0;
113
113
  box-shadow: 0 2px 8px rgba(0,0,0,0.3);
@@ -120,7 +120,7 @@ GLOW_CSS = """
120
120
 
121
121
  GLOW_OFF_CSS = """
122
122
  (function() {
123
- const el = document.getElementById('browserctl-glow');
123
+ const el = document.getElementById('cdpilot-glow');
124
124
  if (el) { el.remove(); return 'glow off'; }
125
125
  return 'already off';
126
126
  })()
@@ -130,9 +130,9 @@ GLOW_OFF_CSS = """
130
130
 
131
131
  INPUT_BLOCKER_ON = """
132
132
  (function() {
133
- if (document.getElementById('browserctl-input-blocker')) return 'blocker already active';
133
+ if (document.getElementById('cdpilot-input-blocker')) return 'blocker already active';
134
134
  const overlay = document.createElement('div');
135
- overlay.id = 'browserctl-input-blocker';
135
+ overlay.id = 'cdpilot-input-blocker';
136
136
  overlay.style.cssText = `
137
137
  position: fixed; top: 0; left: 0; right: 0; bottom: 0;
138
138
  z-index: 2147483646; cursor: not-allowed;
@@ -145,21 +145,21 @@ INPUT_BLOCKER_ON = """
145
145
  overlay.addEventListener('contextmenu', e => { e.stopPropagation(); e.preventDefault(); }, true);
146
146
  overlay.addEventListener('wheel', e => { e.stopPropagation(); e.preventDefault(); }, {capture: true, passive: false});
147
147
  document.addEventListener('keydown', function _cb(e) {
148
- if (!document.getElementById('browserctl-input-blocker')) {
148
+ if (!document.getElementById('cdpilot-input-blocker')) {
149
149
  document.removeEventListener('keydown', _cb, true);
150
150
  return;
151
151
  }
152
152
  e.stopPropagation(); e.preventDefault();
153
153
  }, true);
154
154
  document.addEventListener('keyup', function _cb(e) {
155
- if (!document.getElementById('browserctl-input-blocker')) {
155
+ if (!document.getElementById('cdpilot-input-blocker')) {
156
156
  document.removeEventListener('keyup', _cb, true);
157
157
  return;
158
158
  }
159
159
  e.stopPropagation(); e.preventDefault();
160
160
  }, true);
161
161
  document.addEventListener('keypress', function _cb(e) {
162
- if (!document.getElementById('browserctl-input-blocker')) {
162
+ if (!document.getElementById('cdpilot-input-blocker')) {
163
163
  document.removeEventListener('keypress', _cb, true);
164
164
  return;
165
165
  }
@@ -172,7 +172,7 @@ INPUT_BLOCKER_ON = """
172
172
 
173
173
  INPUT_BLOCKER_OFF = """
174
174
  (function() {
175
- const el = document.getElementById('browserctl-input-blocker');
175
+ const el = document.getElementById('cdpilot-input-blocker');
176
176
  if (el) { el.remove(); return 'input blocker off'; }
177
177
  return 'blocker already off';
178
178
  })()
@@ -182,11 +182,11 @@ INPUT_BLOCKER_OFF = """
182
182
 
183
183
  _glow_script_id = None # addScriptToEvaluateOnNewDocument identifier
184
184
 
185
- GLOW_ACTIVATE_JS = "document.documentElement.dataset.browserctlActive = 'true';"
185
+ GLOW_ACTIVATE_JS = "document.documentElement.dataset.cdpilotActive = 'true';"
186
186
  GLOW_DEACTIVATE_JS = """
187
- document.documentElement.removeAttribute('data-browserctl-active');
188
- var _go = document.getElementById('browserctl-glow-overlay');
189
- var _gs = document.getElementById('browserctl-glow-style');
187
+ document.documentElement.removeAttribute('data-cdpilot-active');
188
+ var _go = document.getElementById('cdpilot-glow-overlay');
189
+ var _gs = document.getElementById('cdpilot-glow-style');
190
190
  if (_go) _go.remove();
191
191
  if (_gs) _gs.remove();
192
192
  """
@@ -495,7 +495,7 @@ async def navigate_collect(ws_url, url, network=False, console=False, glow=True)
495
495
  if loaded:
496
496
  break
497
497
 
498
- # Inject visual indicator (sets data-browserctl-active attribute)
498
+ # Inject visual indicator (sets data-cdpilot-active attribute)
499
499
  if glow:
500
500
  await ws.send(json.dumps({
501
501
  "id": 200, "method": "Runtime.evaluate",
@@ -1614,7 +1614,7 @@ def cmd_headless(state=None):
1614
1614
 
1615
1615
 
1616
1616
  def cmd_stop():
1617
- """Stop the browser instance managed by browserctl."""
1617
+ """Stop the browser instance managed by cdpilot."""
1618
1618
  import signal
1619
1619
  try:
1620
1620
  result = subprocess.run(
@@ -1632,8 +1632,8 @@ def cmd_stop():
1632
1632
 
1633
1633
 
1634
1634
  def cmd_version():
1635
- """Show browserctl version."""
1636
- print(f"browserctl v{__version__}")
1635
+ """Show cdpilot version."""
1636
+ print(f"cdpilot v{__version__}")
1637
1637
 
1638
1638
 
1639
1639
  # ─── New CDP Commands ───
@@ -2205,6 +2205,140 @@ async def cmd_permission(subcmd, perm=None):
2205
2205
  sys.exit(1)
2206
2206
 
2207
2207
 
2208
+ # ─── MCP Server ───
2209
+
2210
+ class MCPServer:
2211
+ """Minimal MCP (Model Context Protocol) server over stdin/stdout.
2212
+ Implements JSON-RPC 2.0 for tool discovery and execution.
2213
+ Usage: cdpilot mcp
2214
+ """
2215
+
2216
+ def __init__(self):
2217
+ self.tools = self._register_tools()
2218
+
2219
+ def _register_tools(self):
2220
+ return [
2221
+ {"name": "browser_navigate", "description": "Navigate to a URL",
2222
+ "inputSchema": {"type": "object", "properties": {"url": {"type": "string", "description": "URL to navigate to"}}, "required": ["url"]}},
2223
+ {"name": "browser_screenshot", "description": "Take a screenshot of the current page",
2224
+ "inputSchema": {"type": "object", "properties": {"filename": {"type": "string", "description": "Output filename", "default": "screenshot.png"}}}},
2225
+ {"name": "browser_click", "description": "Click an element by CSS selector",
2226
+ "inputSchema": {"type": "object", "properties": {"selector": {"type": "string", "description": "CSS selector"}}, "required": ["selector"]}},
2227
+ {"name": "browser_type", "description": "Type text into an input element",
2228
+ "inputSchema": {"type": "object", "properties": {"selector": {"type": "string", "description": "CSS selector"}, "text": {"type": "string", "description": "Text to type"}}, "required": ["selector", "text"]}},
2229
+ {"name": "browser_content", "description": "Get text content of the current page",
2230
+ "inputSchema": {"type": "object", "properties": {}}},
2231
+ {"name": "browser_html", "description": "Get HTML source of the current page",
2232
+ "inputSchema": {"type": "object", "properties": {}}},
2233
+ {"name": "browser_eval", "description": "Execute JavaScript in the browser",
2234
+ "inputSchema": {"type": "object", "properties": {"expression": {"type": "string", "description": "JavaScript expression"}}, "required": ["expression"]}},
2235
+ {"name": "browser_tabs", "description": "List all open browser tabs",
2236
+ "inputSchema": {"type": "object", "properties": {}}},
2237
+ {"name": "browser_console", "description": "Get console logs from the browser",
2238
+ "inputSchema": {"type": "object", "properties": {"url": {"type": "string", "description": "URL to navigate and capture logs"}}}},
2239
+ {"name": "browser_network", "description": "Monitor network requests",
2240
+ "inputSchema": {"type": "object", "properties": {"url": {"type": "string", "description": "URL to navigate and monitor"}}}},
2241
+ {"name": "browser_a11y", "description": "Get accessibility tree of the current page",
2242
+ "inputSchema": {"type": "object", "properties": {"mode": {"type": "string", "enum": ["full", "summary"], "default": "full"}}}},
2243
+ {"name": "browser_fill", "description": "Set input value (React-compatible)",
2244
+ "inputSchema": {"type": "object", "properties": {"selector": {"type": "string", "description": "CSS selector"}, "value": {"type": "string", "description": "Value to set"}}, "required": ["selector", "value"]}},
2245
+ {"name": "browser_launch", "description": "Launch browser with CDP enabled",
2246
+ "inputSchema": {"type": "object", "properties": {}}},
2247
+ {"name": "browser_close", "description": "Close the active tab",
2248
+ "inputSchema": {"type": "object", "properties": {}}},
2249
+ ]
2250
+
2251
+ def _handle_request(self, request):
2252
+ method = request.get("method", "")
2253
+ req_id = request.get("id")
2254
+ params = request.get("params", {})
2255
+
2256
+ if method == "initialize":
2257
+ return {"jsonrpc": "2.0", "id": req_id, "result": {
2258
+ "protocolVersion": "2024-11-05",
2259
+ "capabilities": {"tools": {}},
2260
+ "serverInfo": {"name": "cdpilot", "version": __version__}
2261
+ }}
2262
+ elif method == "notifications/initialized":
2263
+ return None
2264
+ elif method == "tools/list":
2265
+ return {"jsonrpc": "2.0", "id": req_id, "result": {"tools": self.tools}}
2266
+ elif method == "tools/call":
2267
+ return self._execute_tool(req_id, params.get("name", ""), params.get("arguments", {}))
2268
+ elif method == "ping":
2269
+ return {"jsonrpc": "2.0", "id": req_id, "result": {}}
2270
+ else:
2271
+ return {"jsonrpc": "2.0", "id": req_id, "error": {"code": -32601, "message": f"Method not found: {method}"}}
2272
+
2273
+ def _execute_tool(self, req_id, tool_name, args):
2274
+ import io, subprocess
2275
+ tool_map = {
2276
+ "browser_navigate": lambda a: ["go", a.get("url", "")],
2277
+ "browser_screenshot": lambda a: ["shot"] + ([a["filename"]] if a.get("filename") else []),
2278
+ "browser_click": lambda a: ["click", a.get("selector", "")],
2279
+ "browser_type": lambda a: ["type", a.get("selector", ""), a.get("text", "")],
2280
+ "browser_content": lambda a: ["content"],
2281
+ "browser_html": lambda a: ["html"],
2282
+ "browser_eval": lambda a: ["eval", a.get("expression", "")],
2283
+ "browser_tabs": lambda a: ["tabs"],
2284
+ "browser_console": lambda a: ["console"] + ([a["url"]] if a.get("url") else []),
2285
+ "browser_network": lambda a: ["network"] + ([a["url"]] if a.get("url") else []),
2286
+ "browser_a11y": lambda a: ["a11y"] + ([a["mode"]] if a.get("mode") and a["mode"] != "full" else []),
2287
+ "browser_fill": lambda a: ["fill", a.get("selector", ""), a.get("value", "")],
2288
+ "browser_launch": lambda a: ["launch"],
2289
+ "browser_close": lambda a: ["close"],
2290
+ }
2291
+ if tool_name not in tool_map:
2292
+ return {"jsonrpc": "2.0", "id": req_id, "error": {"code": -32602, "message": f"Unknown tool: {tool_name}"}}
2293
+
2294
+ cli_args = [a for a in tool_map[tool_name](args) if a]
2295
+ try:
2296
+ result = subprocess.run(
2297
+ [sys.executable, __file__] + cli_args,
2298
+ capture_output=True, text=True, timeout=30
2299
+ )
2300
+ output = result.stdout.strip()
2301
+ errors = result.stderr.strip()
2302
+ content = []
2303
+ if output:
2304
+ content.append({"type": "text", "text": output})
2305
+ if errors:
2306
+ content.append({"type": "text", "text": f"stderr: {errors}"})
2307
+ if not content:
2308
+ content.append({"type": "text", "text": "Command executed successfully"})
2309
+ return {"jsonrpc": "2.0", "id": req_id, "result": {"content": content, "isError": result.returncode != 0}}
2310
+ except subprocess.TimeoutExpired:
2311
+ return {"jsonrpc": "2.0", "id": req_id, "result": {"content": [{"type": "text", "text": "Error: Command timed out (30s)"}], "isError": True}}
2312
+ except Exception as e:
2313
+ return {"jsonrpc": "2.0", "id": req_id, "result": {"content": [{"type": "text", "text": f"Error: {str(e)}"}], "isError": True}}
2314
+
2315
+ def run(self):
2316
+ import json as json_mod
2317
+ sys.stderr.write(f"cdpilot MCP server v{__version__} ready\n")
2318
+ sys.stderr.flush()
2319
+ while True:
2320
+ try:
2321
+ line = sys.stdin.readline()
2322
+ if not line:
2323
+ break
2324
+ line = line.strip()
2325
+ if not line:
2326
+ continue
2327
+ request = json_mod.loads(line)
2328
+ response = self._handle_request(request)
2329
+ if response is not None:
2330
+ sys.stdout.write(json_mod.dumps(response) + "\n")
2331
+ sys.stdout.flush()
2332
+ except json_mod.JSONDecodeError as e:
2333
+ sys.stdout.write(json_mod.dumps({"jsonrpc": "2.0", "id": None, "error": {"code": -32700, "message": f"Parse error: {str(e)}"}}) + "\n")
2334
+ sys.stdout.flush()
2335
+ except KeyboardInterrupt:
2336
+ break
2337
+ except Exception as e:
2338
+ sys.stderr.write(f"MCP error: {str(e)}\n")
2339
+ sys.stderr.flush()
2340
+
2341
+
2208
2342
  # ─── CLI ───
2209
2343
 
2210
2344
  if __name__ == "__main__":
@@ -2228,6 +2362,11 @@ if __name__ == "__main__":
2228
2362
  'session-close': lambda: cmd_session_close(args[0] if args else None),
2229
2363
  }
2230
2364
 
2365
+ if cmd == "mcp":
2366
+ server = MCPServer()
2367
+ server.run()
2368
+ sys.exit(0)
2369
+
2231
2370
  if cmd == "ext-install":
2232
2371
  if not args:
2233
2372
  print("Usage: ext-install <crx-file-or-directory>")