cdpilot 0.1.2 → 0.2.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
@@ -3,9 +3,15 @@
3
3
  > Zero-dependency browser automation from your terminal. One command, full control.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/cdpilot.svg)](https://www.npmjs.com/package/cdpilot)
6
+ [![npm downloads](https://img.shields.io/npm/dm/cdpilot.svg)](https://www.npmjs.com/package/cdpilot)
6
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
8
+ [![Node.js](https://img.shields.io/badge/Node.js-18%2B-339933)](https://nodejs.org)
9
+ [![MCP Compatible](https://img.shields.io/badge/MCP-Compatible-blue)](https://modelcontextprotocol.io)
10
+ [![cdpilot MCP server](https://glama.ai/mcp/servers/mehmetnadir/cdpilot/badge)](https://glama.ai/mcp/servers/mehmetnadir/cdpilot)
7
11
 
8
- <!-- Demo GIF will be added after first release -->
12
+ <div align="center">
13
+ <img src="cdpilot-demo.gif" alt="cdpilot demo" width="600" />
14
+ </div>
9
15
 
10
16
  ## Quick Start
11
17
 
@@ -26,6 +32,9 @@ AI agents and developers need browser control that **just works**:
26
32
  - **40+ commands** — Navigate, click, type, screenshot, network, console, accessibility, and more
27
33
  - **AI-agent friendly** — Designed for Claude, GPT, Gemini, and any LLM tool-use workflow
28
34
  - **Isolated sessions** — Your personal browser stays untouched. cdpilot runs in its own profile
35
+ - **Visual feedback** — Green glow overlay, cursor visualization, click ripples, and keystroke display keep you informed during automation
36
+ - **Multi-project isolation** — Each project gets its own browser instance and port automatically, no conflicts
37
+ - **AI control warning** — Red toast notification appears when you hover during active automation
29
38
  - **Privacy-first** — Everything runs locally. No data leaves your machine
30
39
 
31
40
  ### Why Brave?
@@ -271,6 +280,41 @@ Future paid offerings:
271
280
 
272
281
  Found a vulnerability? Please email the maintainer directly instead of opening a public issue.
273
282
 
283
+ ## Roadmap
284
+
285
+ The only browser MCP with built-in test assertions. Here's what we've shipped and what's next:
286
+
287
+ ### Shipped
288
+
289
+ - [x] 60+ CLI commands (navigate, click, fill, screenshot, PDF, console, network...)
290
+ - [x] MCP server for AI agent integration (Claude Code, Cursor, etc.)
291
+ - [x] **10 built-in test assertions** — assert, assert-url, assert-title, assert-count, assert-value, assert-attr, assert-visible/hidden, wait-for, check (batch), screenshot-diff
292
+ - [x] **Accessibility tree snapshot** (`a11y-snapshot`) — structured data with @ref references, 500x fewer tokens than screenshots
293
+ - [x] **Token-efficient screenshots** — element-level crop (13x smaller), JPEG quality control, format selection
294
+ - [x] **Vision fallback** (`describe`) — a11y + screenshot + text in one call
295
+ - [x] **Annotated screenshots** — @N badge overlays on interactive elements
296
+ - [x] **Auto-wait** — MutationObserver-based, 5s automatic element waiting
297
+ - [x] **Batch commands** — pipe JSON arrays via stdin for multi-step automation
298
+ - [x] Visual feedback system (persistent green glow, cursor, ripples, keystroke display)
299
+ - [x] AI control warning toast (red warning when user interacts during automation)
300
+ - [x] Multi-project browser isolation (each project gets its own port + profile)
301
+ - [x] Pre-flight wizard (auto-installs dependencies on first run)
302
+ - [x] Persistent MCP glow (stays on during entire AI session, like Claude's orange glow)
303
+ - [x] DevExtension system (native JS injection without browser store)
304
+
305
+ ### Coming Soon
306
+
307
+ - [ ] **iframe & Shadow DOM** support — interact with elements inside iframes and shadow roots
308
+ - [ ] **Session recording & replay** — record browser sessions and replay them deterministically
309
+ - [ ] **Stealth mode** *(Pro)* — human-like mouse/typing, anti-fingerprint, CAPTCHA solving
310
+ - [ ] **cdpilot Cloud** — hosted browser sessions API, REST + WebSocket MCP endpoint
311
+ - [ ] **Chrome Extension** — use cdpilot from any browser without CLI
312
+ - [ ] **Performance audit** — Core Web Vitals (LCP, CLS, INP) via CDP Performance domain
313
+ - [ ] **WCAG accessibility audit** — automated a11y compliance reporting
314
+ - [ ] **Claude Code Skill mode** — run as a `.claude/skills/` skill in addition to MCP
315
+
316
+ Have an idea? [Open an issue](https://github.com/mehmetnadir/cdpilot/issues) or submit a PR!
317
+
274
318
  ## Contributing
275
319
 
276
320
  ```bash
package/bin/cdpilot.js CHANGED
@@ -85,14 +85,13 @@ function findPython() {
85
85
 
86
86
  function runSetup() {
87
87
  const browser = findBrowser();
88
- const profileDir = process.env.CDPILOT_PROFILE
89
- || path.join(os.homedir(), '.cdpilot', 'profile');
90
- const port = process.env.CDP_PORT || '9222';
88
+ const config = resolveProjectConfig();
91
89
 
92
90
  console.log('\n cdpilot setup\n');
93
91
  console.log(` Browser: ${browser || '❌ Not found'}`);
94
- console.log(` Profile: ${profileDir}`);
95
- console.log(` CDP Port: ${port}`);
92
+ console.log(` Profile: ${config.profileDir}`);
93
+ console.log(` CDP Port: ${config.port === '0' ? 'auto' : config.port}`);
94
+ console.log(` Project: ${config.projectId || 'manual mode'}`);
96
95
  console.log(` Python: ${findPython() || '❌ Not found'}`);
97
96
 
98
97
  if (!browser) {
@@ -109,21 +108,97 @@ function runSetup() {
109
108
  }
110
109
 
111
110
  // Create profile directory
112
- if (!fs.existsSync(profileDir)) {
113
- fs.mkdirSync(profileDir, { recursive: true });
114
- console.log(`\n ✓ Created profile: ${profileDir}`);
111
+ if (!fs.existsSync(config.profileDir)) {
112
+ fs.mkdirSync(config.profileDir, { recursive: true });
113
+ console.log(`\n ✓ Created profile: ${config.profileDir}`);
115
114
  } else {
116
- console.log(`\n ✓ Profile exists: ${profileDir}`);
115
+ console.log(`\n ✓ Profile exists: ${config.profileDir}`);
117
116
  }
118
117
 
119
118
  console.log(' ✓ Setup complete! Run: cdpilot launch\n');
120
119
  }
121
120
 
121
+ // ── Pre-flight Check (runs on first launch) ──
122
+
123
+ function checkWebsockets(python) {
124
+ try {
125
+ execSync(`${python} -c "import websockets"`, { stdio: 'pipe' });
126
+ return true;
127
+ } catch {
128
+ return false;
129
+ }
130
+ }
131
+
132
+ function preflight() {
133
+ const markerFile = path.join(os.homedir(), '.cdpilot', '.preflight-done');
134
+
135
+ // Skip if already passed (not first run) and all deps present
136
+ const python = findPython();
137
+ const browser = findBrowser();
138
+ if (fs.existsSync(markerFile) && python && browser && checkWebsockets(python)) {
139
+ return; // All good, skip silently
140
+ }
141
+
142
+ console.log(`\n cdpilot v${VERSION} — Pre-flight Check`);
143
+ console.log(' ' + '─'.repeat(35) + '\n');
144
+
145
+ // 1. Python
146
+ if (python) {
147
+ const ver = execSync(`${python} --version 2>&1`, { stdio: 'pipe' }).toString().trim();
148
+ console.log(` ✓ ${ver}`);
149
+ } else {
150
+ console.log(' ✗ Python 3.8+ not found');
151
+ console.log(' → Install: https://www.python.org/downloads/\n');
152
+ process.exit(1);
153
+ }
154
+
155
+ // 2. websockets
156
+ if (checkWebsockets(python)) {
157
+ console.log(' ✓ websockets');
158
+ } else {
159
+ console.log(' ✗ websockets — installing...');
160
+ try {
161
+ execSync(`${python} -m pip install websockets --quiet --disable-pip-version-check`, { stdio: 'pipe' });
162
+ if (checkWebsockets(python)) {
163
+ console.log(' ✓ websockets (installed)');
164
+ } else {
165
+ console.log(' ✗ websockets install failed');
166
+ console.log(' → Run manually: pip install websockets\n');
167
+ process.exit(1);
168
+ }
169
+ } catch {
170
+ console.log(' ✗ websockets auto-install failed');
171
+ console.log(' → Run manually: pip install websockets\n');
172
+ process.exit(1);
173
+ }
174
+ }
175
+
176
+ // 3. Browser
177
+ if (browser) {
178
+ const name = path.basename(browser).replace(/\.exe$/i, '');
179
+ console.log(` ✓ ${name} (${browser})`);
180
+ } else {
181
+ console.log(' ✗ No compatible browser found');
182
+ console.log(' → Install Brave (recommended): https://brave.com/download/');
183
+ console.log(' → Or Chrome: https://www.google.com/chrome/\n');
184
+ process.exit(1);
185
+ }
186
+
187
+ // Mark as done
188
+ const dir = path.dirname(markerFile);
189
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
190
+ fs.writeFileSync(markerFile, new Date().toISOString());
191
+
192
+ console.log('\n Ready!\n');
193
+ }
194
+
122
195
  // ── Status Command ──
123
196
 
124
197
  function runStatus() {
125
- const port = process.env.CDP_PORT || '9222';
126
- console.log(`\n cdpilot status (port ${port})\n`);
198
+ const config = resolveProjectConfig();
199
+ const port = config.port === '0' ? '9222' : config.port;
200
+ const projLabel = config.projectId ? ` [${config.projectId}]` : '';
201
+ console.log(`\n cdpilot status (port ${port})${projLabel}\n`);
127
202
 
128
203
  try {
129
204
  const http = require('http');
@@ -162,6 +237,52 @@ function showVersion() {
162
237
  console.log(`cdpilot v${VERSION}`);
163
238
  }
164
239
 
240
+ // ── Project-Based Multi-Instance ──
241
+
242
+ function getProjectId() {
243
+ const cwd = process.cwd();
244
+ const dirName = path.basename(cwd).replace(/[^a-zA-Z0-9-]/g, '').slice(0, 20);
245
+ const crypto = require('crypto');
246
+ const hash = crypto.createHash('md5').update(cwd).digest('hex').slice(0, 6);
247
+ return dirName ? `${dirName}-${hash}` : hash;
248
+ }
249
+
250
+ function resolveProjectConfig() {
251
+ const envPort = process.env.CDP_PORT;
252
+ const envProfile = process.env.CDPILOT_PROFILE;
253
+
254
+ // Full manual override
255
+ if (envPort && envProfile) {
256
+ return { port: envPort, profileDir: envProfile, projectId: null };
257
+ }
258
+
259
+ const projectId = getProjectId();
260
+ const registryFile = path.join(os.homedir(), '.cdpilot', 'registry.json');
261
+ const defaultProfile = path.join(os.homedir(), '.cdpilot', 'projects', projectId, 'profile');
262
+
263
+ let registry = {};
264
+ try {
265
+ const data = JSON.parse(fs.readFileSync(registryFile, 'utf-8'));
266
+ registry = data.projects || {};
267
+ } catch {}
268
+
269
+ const info = registry[projectId];
270
+ if (info) {
271
+ return {
272
+ port: envPort || String(info.port || 9222),
273
+ profileDir: envProfile || info.profile_dir || defaultProfile,
274
+ projectId,
275
+ };
276
+ }
277
+
278
+ // New project: let Python allocate port (pass 0 for auto)
279
+ return {
280
+ port: envPort || '0',
281
+ profileDir: envProfile || defaultProfile,
282
+ projectId,
283
+ };
284
+ }
285
+
165
286
  // ── Help ──
166
287
 
167
288
  function showHelp() {
@@ -203,6 +324,11 @@ function showHelp() {
203
324
  new-tab [url] Open new tab
204
325
  close-tab [id] Close tab
205
326
 
327
+ PROJECTS
328
+ projects List all project browser instances
329
+ project-stop <id> Stop a specific project's browser
330
+ stop-all Stop all browser instances
331
+
206
332
  AI AGENT
207
333
  mcp Start MCP server (stdin/stdout JSON-RPC)
208
334
 
@@ -234,6 +360,11 @@ if (cmd === 'status') {
234
360
  runStatus();
235
361
  // Don't exit immediately — let http callback complete
236
362
  } else {
363
+ // Pre-flight check on first run or 'launch' command
364
+ if (cmd === 'launch') {
365
+ preflight();
366
+ }
367
+
237
368
  // Delegate to Python
238
369
  const python = findPython();
239
370
  if (!python) {
@@ -242,16 +373,22 @@ if (cmd === 'status') {
242
373
  }
243
374
 
244
375
  const browser = findBrowser();
245
- const profileDir = process.env.CDPILOT_PROFILE
246
- || path.join(os.homedir(), '.cdpilot', 'profile');
247
- const port = process.env.CDP_PORT || '9222';
376
+ const config = resolveProjectConfig();
248
377
 
249
378
  const env = {
250
379
  ...process.env,
251
- CDP_PORT: port,
252
- CDPILOT_PROFILE: profileDir,
380
+ CDPILOT_PROFILE: config.profileDir,
253
381
  };
254
382
 
383
+ // Only pass CDP_PORT if explicitly set or resolved from registry (not 0)
384
+ if (config.port !== '0') {
385
+ env.CDP_PORT = config.port;
386
+ }
387
+
388
+ if (config.projectId) {
389
+ env.CDPILOT_PROJECT_ID = config.projectId;
390
+ }
391
+
255
392
  if (browser && !process.env.CHROME_BIN) {
256
393
  env.CHROME_BIN = browser;
257
394
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdpilot",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "Zero-dependency browser automation from your terminal. One command, full control.",
5
5
  "bin": {
6
6
  "cdpilot": "./bin/cdpilot.js",
@@ -11,9 +11,12 @@
11
11
  "start": "node bin/cdpilot.js"
12
12
  },
13
13
  "keywords": [
14
- "browser", "automation", "cdp", "chrome-devtools-protocol", "cli",
15
- "devtools", "headless", "screenshot", "testing", "web-scraping",
16
- "ai-agent", "mcp", "claude", "brave"
14
+ "browser-automation", "cdp", "chrome-devtools-protocol", "cli",
15
+ "mcp", "ai-agent", "web-scraping", "zero-dependency",
16
+ "headless-browser", "puppeteer-alternative", "playwright-alternative",
17
+ "screenshot", "pdf", "automation", "devtools", "browser",
18
+ "model-context-protocol", "claude", "brave", "testing",
19
+ "web-testing", "accessibility", "a11y"
17
20
  ],
18
21
  "author": "Mehmet Nadir",
19
22
  "license": "MIT",