cdpilot 0.1.0
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/LICENSE +21 -0
- package/README.md +283 -0
- package/bin/browserctl.js +264 -0
- package/package.json +44 -0
- package/src/browserctl.py +2335 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 browserctl contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# browserctl
|
|
2
|
+
|
|
3
|
+
> Zero-dependency browser automation from your terminal. One command, full control.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/browserctl)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
<!-- Demo GIF will be added after first release -->
|
|
9
|
+
|
|
10
|
+
## Quick Start
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npx browserctl launch # Start browser with CDP
|
|
14
|
+
npx browserctl go https://example.com
|
|
15
|
+
npx browserctl shot # Take screenshot
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
No config files. No boilerplate. Just `npx` and go.
|
|
19
|
+
|
|
20
|
+
## Why browserctl?
|
|
21
|
+
|
|
22
|
+
AI agents and developers need browser control that **just works**:
|
|
23
|
+
|
|
24
|
+
- **Zero config** — `npx browserctl launch` starts an isolated browser session
|
|
25
|
+
- **Zero dependency** — No Puppeteer, no Playwright, no Selenium. Pure CDP over HTTP
|
|
26
|
+
- **40+ commands** — Navigate, click, type, screenshot, network, console, accessibility, and more
|
|
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
|
|
29
|
+
- **Privacy-first** — Everything runs locally. No data leaves your machine
|
|
30
|
+
|
|
31
|
+
### Why Brave?
|
|
32
|
+
|
|
33
|
+
browserctl uses [Brave Browser](https://brave.com) as its engine. Here's why:
|
|
34
|
+
|
|
35
|
+
| Feature | Brave | Chrome | Why it matters |
|
|
36
|
+
|---------|-------|--------|---------------|
|
|
37
|
+
| **Built-in ad blocker** | Shields (native) | Extension needed | Pages load faster, less noise in DOM |
|
|
38
|
+
| **Tracker blocking** | Default on | Manual config | Cleaner network logs for debugging |
|
|
39
|
+
| **Fingerprint protection** | Native | None | Better privacy for automated sessions |
|
|
40
|
+
| **Chromium-based** | Full CDP support | Full CDP support | Same DevTools Protocol, same power |
|
|
41
|
+
| **Open source** | Yes (MPL 2.0) | Chromium yes, Chrome no | Transparent, auditable |
|
|
42
|
+
| **Resource usage** | Lower memory | Higher memory | Better for running alongside your work |
|
|
43
|
+
|
|
44
|
+
**TL;DR:** Brave = Chrome's power + built-in privacy + less bloat. Perfect for automation.
|
|
45
|
+
|
|
46
|
+
> browserctl also works with Chrome and Chromium as fallback. Brave is recommended, not required.
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Use directly (no install needed)
|
|
52
|
+
npx browserctl <command>
|
|
53
|
+
|
|
54
|
+
# Or install globally
|
|
55
|
+
npm i -g browserctl
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Requirements:** Node.js 18+ and one of: Brave Browser, Google Chrome, or Chromium.
|
|
59
|
+
|
|
60
|
+
### First-time setup
|
|
61
|
+
|
|
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
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Commands
|
|
69
|
+
|
|
70
|
+
### Navigation & Content
|
|
71
|
+
|
|
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
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Interaction
|
|
81
|
+
|
|
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
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Debugging
|
|
94
|
+
|
|
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
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Tab Management
|
|
104
|
+
|
|
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
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Network Control
|
|
114
|
+
|
|
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
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Request Interception
|
|
125
|
+
|
|
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
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Device Emulation
|
|
135
|
+
|
|
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
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Geolocation
|
|
144
|
+
|
|
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
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Accessibility
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
browserctl a11y # Full accessibility tree
|
|
156
|
+
browserctl a11y summary # Quick summary
|
|
157
|
+
browserctl a11y find <role> # Find elements by ARIA role
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Session Management
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
browserctl session # Current session info
|
|
164
|
+
browserctl sessions # List all sessions
|
|
165
|
+
browserctl session-close [id] # Close session
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Advanced
|
|
169
|
+
|
|
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
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Use with AI Agents
|
|
182
|
+
|
|
183
|
+
browserctl is designed to be called by AI agents as a tool:
|
|
184
|
+
|
|
185
|
+
### Claude Code (MCP)
|
|
186
|
+
|
|
187
|
+
```json
|
|
188
|
+
{
|
|
189
|
+
"mcpServers": {
|
|
190
|
+
"browserctl": {
|
|
191
|
+
"command": "npx",
|
|
192
|
+
"args": ["browserctl", "mcp"]
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Any LLM (tool-use)
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"name": "browser",
|
|
203
|
+
"description": "Control a browser via CDP",
|
|
204
|
+
"parameters": {
|
|
205
|
+
"command": "go https://example.com"
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Python (subprocess)
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
import subprocess
|
|
214
|
+
result = subprocess.run(["npx", "browserctl", "go", url], capture_output=True, text=True)
|
|
215
|
+
print(result.stdout)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Environment Variables
|
|
219
|
+
|
|
220
|
+
| Variable | Default | Description |
|
|
221
|
+
|----------|---------|-------------|
|
|
222
|
+
| `CDP_PORT` | `9222` | CDP debugging port |
|
|
223
|
+
| `CHROME_BIN` | Auto-detect | Browser binary path |
|
|
224
|
+
| `BROWSERCTL_PROFILE` | `~/.browserctl/profile` | Isolated browser profile |
|
|
225
|
+
| `BROWSER_SESSION` | Auto | Session identifier |
|
|
226
|
+
|
|
227
|
+
## How It Works
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
┌─────────────┐ HTTP/WebSocket ┌──────────────┐
|
|
231
|
+
│ browserctl │ ◄──────────────────────► │ Brave/Chrome │
|
|
232
|
+
│ (CLI) │ Chrome DevTools │ (CDP mode) │
|
|
233
|
+
└─────────────┘ Protocol └──────────────┘
|
|
234
|
+
│ │
|
|
235
|
+
│ Zero dependencies │ Isolated profile
|
|
236
|
+
│ Pure HTTP + WebSocket │ Separate from your
|
|
237
|
+
│ ~2500 lines, single file │ personal browser
|
|
238
|
+
└────────────────────────────────────────┘
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**No Puppeteer. No Playwright. No Selenium.** Just direct CDP communication.
|
|
242
|
+
|
|
243
|
+
## Comparison
|
|
244
|
+
|
|
245
|
+
| Feature | browserctl | Puppeteer | Playwright | Selenium |
|
|
246
|
+
|---------|-----------|-----------|------------|----------|
|
|
247
|
+
| Install size | **~50KB** | 400MB+ | 200MB+ | 100MB+ |
|
|
248
|
+
| Dependencies | **0** | 50+ | 30+ | Java + drivers |
|
|
249
|
+
| Setup time | **instant** | minutes | minutes | painful |
|
|
250
|
+
| AI-agent ready | **yes** | manual | manual | manual |
|
|
251
|
+
| Browser download | **no** | yes (Chromium) | yes (3 browsers) | no |
|
|
252
|
+
| CLI-first | **yes** | no (library) | no (library) | no |
|
|
253
|
+
| MCP support | **yes** | no | no | no |
|
|
254
|
+
|
|
255
|
+
## Monetization / Pro (Coming Soon)
|
|
256
|
+
|
|
257
|
+
browserctl CLI is and will always be **free and open source** (MIT).
|
|
258
|
+
|
|
259
|
+
Future paid offerings:
|
|
260
|
+
- **browserctl cloud** — Remote browser instances, no local browser needed
|
|
261
|
+
- **Team dashboard** — Shared sessions, audit logs, usage analytics
|
|
262
|
+
- **Priority support** — Direct help for enterprise integrations
|
|
263
|
+
|
|
264
|
+
## Contributing
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
git clone https://github.com/user/browserctl.git
|
|
268
|
+
cd browserctl
|
|
269
|
+
npm install
|
|
270
|
+
npm test
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
PRs welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) first.
|
|
274
|
+
|
|
275
|
+
## License
|
|
276
|
+
|
|
277
|
+
MIT — do whatever you want.
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
<p align="center">
|
|
282
|
+
Built with the <a href="https://github.com/nicedoc/browserctl">browserctl</a> mindset: one tool, one job, done right.
|
|
283
|
+
</p>
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* browserctl — Zero-dependency browser automation CLI
|
|
5
|
+
* Entry point: detects Python, finds browser, delegates to browserctl.py
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { execSync, spawn } = require('child_process');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
|
|
13
|
+
const SCRIPT = path.join(__dirname, '..', 'src', 'browserctl.py');
|
|
14
|
+
const VERSION = require('../package.json').version;
|
|
15
|
+
|
|
16
|
+
// ── Browser Detection ──
|
|
17
|
+
|
|
18
|
+
function findBrowser() {
|
|
19
|
+
// User override
|
|
20
|
+
if (process.env.CHROME_BIN) {
|
|
21
|
+
if (fs.existsSync(process.env.CHROME_BIN)) return process.env.CHROME_BIN;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const platform = os.platform();
|
|
25
|
+
const candidates = [];
|
|
26
|
+
|
|
27
|
+
if (platform === 'darwin') {
|
|
28
|
+
candidates.push(
|
|
29
|
+
'/Applications/Brave Browser.app/Contents/MacOS/Brave Browser',
|
|
30
|
+
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
31
|
+
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
32
|
+
);
|
|
33
|
+
} else if (platform === 'linux') {
|
|
34
|
+
candidates.push(
|
|
35
|
+
'brave-browser',
|
|
36
|
+
'brave',
|
|
37
|
+
'google-chrome',
|
|
38
|
+
'google-chrome-stable',
|
|
39
|
+
'chromium-browser',
|
|
40
|
+
'chromium',
|
|
41
|
+
);
|
|
42
|
+
} else if (platform === 'win32') {
|
|
43
|
+
const programFiles = process.env['PROGRAMFILES'] || 'C:\\Program Files';
|
|
44
|
+
const programFilesX86 = process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)';
|
|
45
|
+
const localAppData = process.env.LOCALAPPDATA || '';
|
|
46
|
+
candidates.push(
|
|
47
|
+
path.join(programFiles, 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
|
|
48
|
+
path.join(programFilesX86, 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
|
|
49
|
+
path.join(localAppData, 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
|
|
50
|
+
path.join(programFiles, 'Google', 'Chrome', 'Application', 'chrome.exe'),
|
|
51
|
+
path.join(programFilesX86, 'Google', 'Chrome', 'Application', 'chrome.exe'),
|
|
52
|
+
path.join(localAppData, 'Google', 'Chrome', 'Application', 'chrome.exe'),
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const bin of candidates) {
|
|
57
|
+
if (bin.startsWith('/') || bin.includes('\\')) {
|
|
58
|
+
if (fs.existsSync(bin)) return bin;
|
|
59
|
+
} else {
|
|
60
|
+
try {
|
|
61
|
+
execSync(`which ${bin} 2>/dev/null`, { stdio: 'pipe' });
|
|
62
|
+
return bin;
|
|
63
|
+
} catch {}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ── Python Detection ──
|
|
70
|
+
|
|
71
|
+
function findPython() {
|
|
72
|
+
for (const cmd of ['python3', 'python']) {
|
|
73
|
+
try {
|
|
74
|
+
const ver = execSync(`${cmd} --version 2>&1`, { stdio: 'pipe' }).toString().trim();
|
|
75
|
+
const match = ver.match(/(\d+)\.(\d+)/);
|
|
76
|
+
if (match && parseInt(match[1]) >= 3 && parseInt(match[2]) >= 8) {
|
|
77
|
+
return cmd;
|
|
78
|
+
}
|
|
79
|
+
} catch {}
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ── Setup Command ──
|
|
85
|
+
|
|
86
|
+
function runSetup() {
|
|
87
|
+
const browser = findBrowser();
|
|
88
|
+
const profileDir = process.env.BROWSERCTL_PROFILE
|
|
89
|
+
|| path.join(os.homedir(), '.browserctl', 'profile');
|
|
90
|
+
const port = process.env.CDP_PORT || '9222';
|
|
91
|
+
|
|
92
|
+
console.log('\n browserctl setup\n');
|
|
93
|
+
console.log(` Browser: ${browser || '❌ Not found'}`);
|
|
94
|
+
console.log(` Profile: ${profileDir}`);
|
|
95
|
+
console.log(` CDP Port: ${port}`);
|
|
96
|
+
console.log(` Python: ${findPython() || '❌ Not found'}`);
|
|
97
|
+
|
|
98
|
+
if (!browser) {
|
|
99
|
+
console.log('\n ❌ No compatible browser found.');
|
|
100
|
+
console.log(' Install Brave (recommended): https://brave.com/download/');
|
|
101
|
+
console.log(' Or Google Chrome: https://www.google.com/chrome/\n');
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!findPython()) {
|
|
106
|
+
console.log('\n ❌ Python 3.8+ not found.');
|
|
107
|
+
console.log(' Install: https://www.python.org/downloads/\n');
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Create profile directory
|
|
112
|
+
if (!fs.existsSync(profileDir)) {
|
|
113
|
+
fs.mkdirSync(profileDir, { recursive: true });
|
|
114
|
+
console.log(`\n ✓ Created profile: ${profileDir}`);
|
|
115
|
+
} else {
|
|
116
|
+
console.log(`\n ✓ Profile exists: ${profileDir}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
console.log(' ✓ Setup complete! Run: browserctl launch\n');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ── Status Command ──
|
|
123
|
+
|
|
124
|
+
function runStatus() {
|
|
125
|
+
const port = process.env.CDP_PORT || '9222';
|
|
126
|
+
console.log(`\n browserctl status (port ${port})\n`);
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const http = require('http');
|
|
130
|
+
const req = http.get(`http://127.0.0.1:${port}/json/version`, { timeout: 2000 }, (res) => {
|
|
131
|
+
let data = '';
|
|
132
|
+
res.on('data', (chunk) => data += chunk);
|
|
133
|
+
res.on('end', () => {
|
|
134
|
+
try {
|
|
135
|
+
const info = JSON.parse(data);
|
|
136
|
+
console.log(` ✓ Connected`);
|
|
137
|
+
console.log(` Browser: ${info.Browser || 'Unknown'}`);
|
|
138
|
+
console.log(` Protocol: ${info['Protocol-Version'] || 'Unknown'}`);
|
|
139
|
+
console.log(` WebSocket: ${info.webSocketDebuggerUrl || 'N/A'}\n`);
|
|
140
|
+
} catch {
|
|
141
|
+
console.log(' ✓ CDP responding but version info unavailable\n');
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
req.on('error', () => {
|
|
146
|
+
console.log(' ❌ No browser connected on this port.');
|
|
147
|
+
console.log(' Run: browserctl launch\n');
|
|
148
|
+
});
|
|
149
|
+
req.on('timeout', () => {
|
|
150
|
+
req.destroy();
|
|
151
|
+
console.log(' ❌ Connection timeout.');
|
|
152
|
+
console.log(' Run: browserctl launch\n');
|
|
153
|
+
});
|
|
154
|
+
} catch {
|
|
155
|
+
console.log(' ❌ Could not check status.\n');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ── Version ──
|
|
160
|
+
|
|
161
|
+
function showVersion() {
|
|
162
|
+
console.log(`browserctl v${VERSION}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ── Help ──
|
|
166
|
+
|
|
167
|
+
function showHelp() {
|
|
168
|
+
console.log(`
|
|
169
|
+
browserctl v${VERSION} — Zero-dependency browser automation
|
|
170
|
+
|
|
171
|
+
USAGE
|
|
172
|
+
browserctl <command> [args]
|
|
173
|
+
|
|
174
|
+
SETUP
|
|
175
|
+
setup Auto-detect browser, create isolated profile
|
|
176
|
+
launch Start browser with CDP enabled
|
|
177
|
+
status Check browser connection
|
|
178
|
+
stop Stop browser
|
|
179
|
+
|
|
180
|
+
NAVIGATION
|
|
181
|
+
go <url> Navigate to URL
|
|
182
|
+
content Get page text content
|
|
183
|
+
html Get page HTML
|
|
184
|
+
shot [file] Take screenshot
|
|
185
|
+
pdf [file] Save page as PDF
|
|
186
|
+
|
|
187
|
+
INTERACTION
|
|
188
|
+
click <sel> Click element
|
|
189
|
+
type <sel> <text> Type into input
|
|
190
|
+
fill <sel> <val> Set input value (React-compatible)
|
|
191
|
+
submit <form> Submit form
|
|
192
|
+
hover <sel> Hover element
|
|
193
|
+
keys <combo> Keyboard shortcut
|
|
194
|
+
|
|
195
|
+
DEBUGGING
|
|
196
|
+
console [url] Capture console logs
|
|
197
|
+
network [url] Monitor network requests
|
|
198
|
+
debug [url] Full diagnostic
|
|
199
|
+
eval <js> Execute JavaScript
|
|
200
|
+
|
|
201
|
+
TABS
|
|
202
|
+
tabs List open tabs
|
|
203
|
+
new-tab [url] Open new tab
|
|
204
|
+
close-tab [id] Close tab
|
|
205
|
+
|
|
206
|
+
More: https://github.com/user/browserctl#commands
|
|
207
|
+
`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ── Main ──
|
|
211
|
+
|
|
212
|
+
const args = process.argv.slice(2);
|
|
213
|
+
const cmd = args[0];
|
|
214
|
+
|
|
215
|
+
if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
|
|
216
|
+
showHelp();
|
|
217
|
+
process.exit(0);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (cmd === '--version' || cmd === '-v') {
|
|
221
|
+
showVersion();
|
|
222
|
+
process.exit(0);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (cmd === 'setup') {
|
|
226
|
+
runSetup();
|
|
227
|
+
process.exit(0);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (cmd === 'status') {
|
|
231
|
+
runStatus();
|
|
232
|
+
// Don't exit immediately — let http callback complete
|
|
233
|
+
} else {
|
|
234
|
+
// Delegate to Python
|
|
235
|
+
const python = findPython();
|
|
236
|
+
if (!python) {
|
|
237
|
+
console.error('Error: Python 3.8+ required. Install: https://www.python.org/downloads/');
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const browser = findBrowser();
|
|
242
|
+
const profileDir = process.env.BROWSERCTL_PROFILE
|
|
243
|
+
|| path.join(os.homedir(), '.browserctl', 'profile');
|
|
244
|
+
const port = process.env.CDP_PORT || '9222';
|
|
245
|
+
|
|
246
|
+
const env = {
|
|
247
|
+
...process.env,
|
|
248
|
+
CDP_PORT: port,
|
|
249
|
+
BROWSERCTL_PROFILE: profileDir,
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
if (browser && !process.env.CHROME_BIN) {
|
|
253
|
+
env.CHROME_BIN = browser;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const child = spawn(python, [SCRIPT, ...args], {
|
|
257
|
+
stdio: 'inherit',
|
|
258
|
+
env,
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
child.on('close', (code) => {
|
|
262
|
+
process.exit(code || 0);
|
|
263
|
+
});
|
|
264
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cdpilot",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Zero-dependency browser automation from your terminal. One command, full control.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"bctl": "./bin/browserctl.js",
|
|
7
|
+
"browserctl": "./bin/browserctl.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node test/test.js",
|
|
11
|
+
"start": "node bin/browserctl.js"
|
|
12
|
+
},
|
|
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"
|
|
28
|
+
],
|
|
29
|
+
"author": "",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/user/browserctl"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18.0.0"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"bin/",
|
|
40
|
+
"src/",
|
|
41
|
+
"README.md",
|
|
42
|
+
"LICENSE"
|
|
43
|
+
]
|
|
44
|
+
}
|