browser-ipc-cdp 1.0.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alexis Malambo
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,267 @@
1
+ <<<<<<< HEAD
2
+ # Brave IPC CDP - Documentacion
3
+
4
+ ## Que es esto?
5
+
6
+ Sistema para controlar **tu Brave Browser real** (con todos tus datos, bookmarks, passwords, extensiones y sesiones) desde Claude Code via **IPC (Inter-Process Communication)** + **CDP (Chrome DevTools Protocol)** con puerto dinamico.
7
+
8
+ **Sin puerto fijo, sin sesion separada, sin perder tus datos. Tu Brave de uso diario controlado por IA.**
9
+
10
+ ---
11
+
12
+ ## Arquitectura
13
+
14
+ ```
15
+ brave_ipc.py lanza Brave
16
+ |
17
+ v
18
+ --remote-debugging-port=0 (OS asigna puerto aleatorio)
19
+ |
20
+ v
21
+ Brave escribe DevToolsActivePort (archivo IPC)
22
+ |
23
+ v
24
+ brave_ipc.py lee el puerto → guarda en cdp_info.json
25
+ |
26
+ v
27
+ MCP "brave" (brave_mcp_launcher.js) lee cdp_info.json
28
+ |
29
+ v
30
+ Claude Code usa mcp__brave__* para controlar el navegador
31
+ ```
32
+
33
+ ---
34
+
35
+ ## Archivos
36
+
37
+ | Archivo | Funcion |
38
+ |---------|---------|
39
+ | `brave_ipc.py` | Launcher principal. Abre Brave con CDP dinamico via IPC |
40
+ | `brave_cdp.bat` | Doble-click para ejecutar brave_ipc.py |
41
+ | `brave_mcp_launcher.js` | Wrapper MCP que lee el puerto dinamico y lanza chrome-devtools-mcp |
42
+ | `cdp_info.json` | Se genera al ejecutar. Contiene puerto, WebSocket, PID, etc. |
43
+ | `README.md` | Este archivo |
44
+
45
+ ---
46
+
47
+ ## Como usar
48
+
49
+ ### 1. Abrir Brave con CDP
50
+
51
+ ```bat
52
+ :: Tu Brave real (bookmarks, passwords, extensiones, todo)
53
+ python brave_ipc.py
54
+
55
+ :: Perfil limpio separado (para testing)
56
+ python brave_ipc.py --clean
57
+
58
+ :: Mas opciones
59
+ python brave_ipc.py --port 9222 # Puerto fijo
60
+ python brave_ipc.py --url https://.. # Abre URL al iniciar
61
+ python brave_ipc.py --headless # Sin ventana visible
62
+ python brave_ipc.py --no-kill # No mata Brave existente
63
+ ```
64
+
65
+ **Modos de perfil:**
66
+
67
+ | Comando | Perfil | Datos |
68
+ |---------|--------|-------|
69
+ | `python brave_ipc.py` | **REAL** | Todos tus bookmarks, passwords, extensiones, sesiones activas |
70
+ | `python brave_ipc.py --clean` | Limpio | Sesion vacia, sin datos personales |
71
+
72
+ ### 2. Verificar CDP
73
+
74
+ Despues de ejecutar, el script muestra:
75
+ ```
76
+ Navegador: Chrome/146.0.7680.80
77
+ Puerto CDP: 58553
78
+ CDP URL: http://127.0.0.1:58553
79
+ WebSocket: ws://127.0.0.1:58553/devtools/browser/...
80
+ ```
81
+
82
+ Tambien puedes verificar manualmente:
83
+ ```
84
+ http://127.0.0.1:{PUERTO}/json/version → Info del navegador
85
+ http://127.0.0.1:{PUERTO}/json/list → Pestanas abiertas
86
+ ```
87
+
88
+ ### 3. Conectar desde Claude Code
89
+
90
+ El MCP "brave" esta configurado en `C:\Users\NyGsoft\.mcp.json`:
91
+ ```json
92
+ {
93
+ "mcpServers": {
94
+ "brave": {
95
+ "command": "npx",
96
+ "args": ["-y", "chrome-devtools-mcp@latest", "--browserUrl", "http://172.20.176.1:PUERTO"]
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ > **IMPORTANTE:** El puerto cambia cada vez que ejecutas `brave_ipc.py`.
103
+ > Despues de ejecutar, actualiza el puerto en `.mcp.json` con el valor de `cdp_info.json`.
104
+ > Luego ejecuta `/mcp` en Claude Code para reconectar.
105
+ >
106
+ > **Tip:** La IP de WSL puede cambiar entre reinicios.
107
+ > Verificar con: `grep nameserver /etc/resolv.conf`
108
+
109
+ ### 4. Usar desde Claude Code
110
+
111
+ ```
112
+ mcp__brave__list_pages → Ver pestanas abiertas
113
+ mcp__brave__navigate_page → Navegar a URL
114
+ mcp__brave__take_snapshot → Leer contenido de la pagina
115
+ mcp__brave__take_screenshot → Captura de pantalla
116
+ mcp__brave__click → Click en elemento
117
+ mcp__brave__fill → Escribir en campo de texto
118
+ mcp__brave__press_key → Presionar tecla
119
+ mcp__brave__evaluate_script → Ejecutar JavaScript
120
+ mcp__brave__list_network_requests → Ver requests de red
121
+ ```
122
+
123
+ ---
124
+
125
+ ## Que es IPC?
126
+
127
+ **Inter-Process Communication** — un proceso (brave_ipc.py) se comunica con otro (Brave) sin usar la red.
128
+
129
+ ### Flujo tradicional (malo):
130
+ ```
131
+ Abrir Brave con --remote-debugging-port=9222
132
+ → Puerto fijo → conflictos
133
+ → Necesita regla de firewall
134
+ → Necesita portproxy para WSL
135
+ ```
136
+
137
+ ### Flujo IPC (bueno):
138
+ ```
139
+ Proceso padre lanza Brave con --remote-debugging-port=0
140
+ → OS asigna puerto aleatorio (ej: 58553)
141
+ → Brave escribe puerto en DevToolsActivePort (IPC via filesystem)
142
+ → Proceso padre lee el archivo → sabe el puerto
143
+ → Conexion directa, sin firewall, sin conflictos
144
+ ```
145
+
146
+ ### Por que puerto 0?
147
+
148
+ Cuando pasas `--remote-debugging-port=0`, le dices al sistema operativo:
149
+ "dame cualquier puerto disponible". El OS elige uno libre (ej: 58553, 62301, etc.)
150
+ y Brave lo escribe en `DevToolsActivePort` para que otros procesos lo lean.
151
+
152
+ Es el mismo patron que usa:
153
+ - **Playwright** internamente
154
+ - **Puppeteer** internamente
155
+ - **El bot de publicidad** con DiCloak (inject hook → puerto dinamico)
156
+
157
+ ---
158
+
159
+ ## Que es CDP?
160
+
161
+ **Chrome DevTools Protocol** — protocolo para controlar navegadores Chromium
162
+ (Chrome, Brave, Edge, Opera, ginsbrowser) programaticamente.
163
+
164
+ Permite:
165
+ - Navegar a URLs
166
+ - Hacer click, escribir, scroll
167
+ - Tomar screenshots
168
+ - Ejecutar JavaScript
169
+ - Interceptar requests de red
170
+ - Leer el DOM completo
171
+
172
+ ---
173
+
174
+ ## Conexion desde WSL
175
+
176
+ WSL no puede acceder a `127.0.0.1` de Windows directamente.
177
+ Para que Claude Code (WSL) llegue a Brave (Windows):
178
+
179
+ 1. **Portproxy** (configuracion unica como Admin):
180
+ ```powershell
181
+ netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=58553 connectaddress=127.0.0.1 connectport=58553
182
+ ```
183
+
184
+ 2. **Firewall** (rango dinamico, configuracion unica):
185
+ ```powershell
186
+ netsh advfirewall firewall add rule name="CDP Dynamic Range" dir=in action=allow protocol=TCP localport=50000-65000
187
+ ```
188
+
189
+ 3. **IP de Windows** desde WSL:
190
+ ```bash
191
+ grep nameserver /etc/resolv.conf
192
+ # Resultado: 172.20.176.1 (puede cambiar entre reinicios)
193
+ ```
194
+
195
+ ---
196
+
197
+ ## Troubleshooting
198
+
199
+ | Problema | Solucion |
200
+ |----------|----------|
201
+ | `brave_ipc.py` no encuentra Brave | Verificar ruta: `C:\Program Files\BraveSoftware\Brave-Browser\Application\brave.exe` |
202
+ | Puerto no detectado | Brave tarda en escribir DevToolsActivePort. Esperar 10s y reintentar |
203
+ | MCP "brave" no conecta | Verificar que el puerto en `.mcp.json` coincide con `cdp_info.json` |
204
+ | WSL no llega al puerto | Agregar portproxy + firewall (ver seccion anterior) |
205
+ | IP de Windows cambio | `grep nameserver /etc/resolv.conf` → actualizar `.mcp.json` |
206
+ | Brave ya estaba abierto | Usar `--no-kill` o cerrar Brave antes de ejecutar |
207
+
208
+ ---
209
+
210
+ ## cdp_info.json (ejemplo)
211
+
212
+ ```json
213
+ {
214
+ "DEBUG_PORT": 59152,
215
+ "DEBUG_WS": "ws://127.0.0.1:59152/devtools/browser/...",
216
+ "BROWSER": "Chrome/146.0.7680.80",
217
+ "PID": 25552,
218
+ "USER_DATA": "C:\\Users\\NyGsoft\\AppData\\Local\\BraveSoftware\\Brave-Browser\\User Data",
219
+ "CDP_URL": "http://127.0.0.1:59152",
220
+ "PAGES": 23
221
+ }
222
+ ```
223
+
224
+ Otros scripts pueden leer este archivo para saber el puerto actual.
225
+
226
+ ---
227
+
228
+ ## Ejemplo real: que puedes hacer
229
+
230
+ Con tu Brave real conectado via IPC + CDP, Claude Code puede:
231
+
232
+ - **Ver todas tus tabs abiertas** (Jira, WhatsApp, Facebook, n8n, ChatGPT, etc.)
233
+ - **Navegar a cualquier pagina** en tu sesion logueada
234
+ - **Leer contenido de paginas** donde ya estas autenticado (Jira, n8n, etc.)
235
+ - **Hacer click, escribir, buscar** en cualquier tab
236
+ - **Tomar screenshots** de lo que ves
237
+ - **Ejecutar JavaScript** en cualquier pagina
238
+ - **Interceptar network requests** para debug
239
+
240
+ Todo sin necesidad de login adicional — usa tus sesiones activas.
241
+
242
+ ---
243
+
244
+ ## Flujo completo paso a paso
245
+
246
+ ```
247
+ 1. Cerrar Brave (si esta abierto)
248
+
249
+ 2. Ejecutar:
250
+ python C:\Users\NyGsoft\Desktop\ipc\brave_ipc.py
251
+
252
+ 3. Brave abre con todas tus cosas + CDP activo
253
+ Output: "Puerto CDP: 59152" (o el que sea)
254
+
255
+ 4. Agregar portproxy (solo la primera vez por puerto):
256
+ netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=59152 connectaddress=127.0.0.1 connectport=59152
257
+
258
+ 5. Actualizar .mcp.json con el puerto nuevo
259
+
260
+ 6. En Claude Code: /mcp → Reconnected to brave
261
+
262
+ 7. Listo! Claude Code controla tu Brave real
263
+ ```
264
+ =======
265
+ # browser-ipc-cdp
266
+ browser-ipc-cdp — Control remoto de navegadores Chromium (Brave, Chrome, Edge) via IPC con CDP dinámico. Abre tu navegador real con todas tus sesiones, detecta o asigna puerto automáticamente, configura portproxy/firewall para WSL, y actualiza el MCP de Claude Code. Sin puertos fijos, sin hacks de registry, sin configuración manual.
267
+ >>>>>>> 3ba8518eefb0a7bab267193cab6800ebc9fda58b
package/bin/cli.js ADDED
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * browser-ipc-cdp CLI
4
+ *
5
+ * Un comando para conectar Claude Code a tu navegador real via IPC + CDP.
6
+ *
7
+ * Uso:
8
+ * npx browser-ipc-cdp # Auto-detectar navegador + instalar
9
+ * npx browser-ipc-cdp --browser brave # Forzar Brave
10
+ * npx browser-ipc-cdp --browser chrome # Forzar Chrome
11
+ * npx browser-ipc-cdp --list # Listar navegadores
12
+ * npx browser-ipc-cdp --status # Ver estado actual
13
+ * npx browser-ipc-cdp --uninstall # Desinstalar
14
+ */
15
+ const { detectBrowsers, findBrowser, launchBrowser, detectExistingCDP } = require('../lib/browser');
16
+ const { setupPortproxy, setupFirewall } = require('../lib/network');
17
+ const { updateMcpJson, getWslHostIp } = require('../lib/mcp');
18
+ const { saveCdpInfo, loadCdpInfo } = require('../lib/config');
19
+ const { log, success, warn, error, banner, table } = require('../lib/logger');
20
+
21
+ const args = process.argv.slice(2);
22
+ const flags = {};
23
+ for (let i = 0; i < args.length; i++) {
24
+ if (args[i].startsWith('--')) {
25
+ const key = args[i].replace('--', '');
26
+ flags[key] = args[i + 1] && !args[i + 1].startsWith('--') ? args[i + 1] : true;
27
+ if (flags[key] !== true) i++;
28
+ }
29
+ }
30
+
31
+ async function main() {
32
+ banner();
33
+
34
+ // --list: listar navegadores
35
+ if (flags.list) {
36
+ const browsers = detectBrowsers();
37
+ if (browsers.length === 0) {
38
+ error('No se encontraron navegadores Chromium instalados.');
39
+ process.exit(1);
40
+ }
41
+ log('Navegadores Chromium detectados:');
42
+ browsers.forEach(b => log(` - ${b.name.padEnd(10)} → ${b.exe}`));
43
+ process.exit(0);
44
+ }
45
+
46
+ // --status: ver estado actual
47
+ if (flags.status) {
48
+ const info = loadCdpInfo();
49
+ if (info) {
50
+ log(`Puerto CDP: ${info.DEBUG_PORT}`);
51
+ log(`Navegador: ${info.BROWSER}`);
52
+ log(`Modo: ${info.MODE || 'LAUNCHED'}`);
53
+ log(`Paginas: ${info.PAGES}`);
54
+ } else {
55
+ warn('No hay sesion CDP activa. Ejecuta: npx browser-ipc-cdp');
56
+ }
57
+ process.exit(0);
58
+ }
59
+
60
+ // --uninstall: limpiar
61
+ if (flags.uninstall) {
62
+ log('Limpiando configuracion...');
63
+ // TODO: remove portproxy, firewall rule, .mcp.json entry
64
+ success('Desinstalado.');
65
+ process.exit(0);
66
+ }
67
+
68
+ // ─── FLUJO PRINCIPAL ──────────────────────────────────────────────────
69
+
70
+ const preferredBrowser = flags.browser || '';
71
+ const forcePort = parseInt(flags.port) || 0;
72
+ const clean = !!flags.clean;
73
+
74
+ // 1. Detectar navegador
75
+ log('[1/6] Detectando navegador...');
76
+ const browser = findBrowser(preferredBrowser);
77
+ if (!browser) {
78
+ const available = detectBrowsers();
79
+ if (available.length > 0) {
80
+ error(`Navegador '${preferredBrowser}' no encontrado.`);
81
+ log('Disponibles:');
82
+ available.forEach(b => log(` - ${b.name}`));
83
+ } else {
84
+ error('No se encontro ningun navegador Chromium.');
85
+ log('Instala Brave, Chrome o Edge.');
86
+ }
87
+ process.exit(1);
88
+ }
89
+ success(`${browser.name} encontrado: ${browser.exe}`);
90
+
91
+ // 2. Verificar CDP existente
92
+ log('[2/6] Verificando CDP existente...');
93
+ const existingPort = await detectExistingCDP(browser);
94
+
95
+ let port, mode, pid;
96
+
97
+ if (existingPort) {
98
+ success(`CDP ya activo en puerto ${existingPort}. Sin reiniciar!`);
99
+ port = existingPort;
100
+ mode = 'ATTACHED';
101
+ pid = 0;
102
+ } else {
103
+ // 3. Lanzar navegador con CDP
104
+ log('[3/6] Lanzando navegador con CDP...');
105
+ const result = await launchBrowser(browser, { port: forcePort, clean });
106
+ port = result.port;
107
+ mode = 'LAUNCHED';
108
+ pid = result.pid;
109
+ success(`CDP activo en puerto ${port}`);
110
+ }
111
+
112
+ // 4. Firewall
113
+ log('[4/6] Configurando firewall...');
114
+ setupFirewall();
115
+
116
+ // 5. Portproxy
117
+ log('[5/6] Configurando portproxy para WSL...');
118
+ setupPortproxy(port);
119
+
120
+ // 6. MCP config
121
+ log('[6/6] Configurando MCP para Claude Code...');
122
+ const wslIp = getWslHostIp();
123
+ updateMcpJson(port, wslIp);
124
+
125
+ // Obtener info del CDP
126
+ let browserVersion = 'Unknown';
127
+ let wsUrl = '';
128
+ let pages = 0;
129
+ try {
130
+ const http = require('http');
131
+ const versionData = await fetchJson(`http://127.0.0.1:${port}/json/version`);
132
+ browserVersion = versionData.Browser || 'Unknown';
133
+ wsUrl = versionData.webSocketDebuggerUrl || '';
134
+ const pageList = await fetchJson(`http://127.0.0.1:${port}/json/list`);
135
+ pages = Array.isArray(pageList) ? pageList.length : 0;
136
+ } catch (e) {}
137
+
138
+ // Guardar info
139
+ saveCdpInfo({
140
+ DEBUG_PORT: port,
141
+ DEBUG_WS: wsUrl,
142
+ BROWSER: browserVersion,
143
+ BROWSER_EXE: browser.exe,
144
+ PID: pid,
145
+ CDP_URL: `http://127.0.0.1:${port}`,
146
+ PAGES: pages,
147
+ MODE: mode,
148
+ WSL_IP: wslIp,
149
+ });
150
+
151
+ // Resumen
152
+ console.log('');
153
+ console.log('='.repeat(55));
154
+ console.log(` MODO: ${mode}${mode === 'ATTACHED' ? ' (sin reiniciar)' : ' (nuevo proceso)'}`);
155
+ console.log(` Navegador: ${browserVersion}`);
156
+ console.log(` Puerto CDP: ${port} (dinamico via IPC)`);
157
+ console.log(` Paginas: ${pages}`);
158
+ console.log(` WSL IP: ${wslIp}`);
159
+ console.log(` Portproxy: 0.0.0.0:${port} -> 127.0.0.1:${port}`);
160
+ console.log(` .mcp.json: Actualizado`);
161
+ console.log('='.repeat(55));
162
+ console.log('');
163
+ console.log(' Siguiente paso en Claude Code:');
164
+ console.log(' /mcp (para conectar el MCP brave)');
165
+ console.log('');
166
+ console.log(' Herramientas disponibles:');
167
+ console.log(' mcp__brave__list_pages');
168
+ console.log(' mcp__brave__navigate_page');
169
+ console.log(' mcp__brave__take_snapshot');
170
+ console.log(' mcp__brave__click / fill / press_key');
171
+ console.log('');
172
+ }
173
+
174
+ function fetchJson(url) {
175
+ return new Promise((resolve, reject) => {
176
+ const http = require('http');
177
+ http.get(url, { timeout: 5000 }, (res) => {
178
+ let data = '';
179
+ res.on('data', chunk => data += chunk);
180
+ res.on('end', () => {
181
+ try { resolve(JSON.parse(data)); }
182
+ catch (e) { reject(e); }
183
+ });
184
+ }).on('error', reject);
185
+ });
186
+ }
187
+
188
+ main().catch(e => {
189
+ error(e.message);
190
+ process.exit(1);
191
+ });
package/lib/browser.js ADDED
@@ -0,0 +1,246 @@
1
+ const { execSync, spawn } = require('child_process');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const http = require('http');
5
+ const { log, success, warn } = require('./logger');
6
+
7
+ const BROWSER_PATHS = {
8
+ brave: [
9
+ 'C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe',
10
+ 'C:\\Program Files (x86)\\BraveSoftware\\Brave-Browser\\Application\\brave.exe',
11
+ path.join(process.env.LOCALAPPDATA || '', 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
12
+ ],
13
+ chrome: [
14
+ 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
15
+ 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
16
+ path.join(process.env.LOCALAPPDATA || '', 'Google', 'Chrome', 'Application', 'chrome.exe'),
17
+ ],
18
+ edge: [
19
+ 'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe',
20
+ 'C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe',
21
+ ],
22
+ };
23
+
24
+ const BROWSER_USER_DATA = {
25
+ brave: path.join(process.env.LOCALAPPDATA || '', 'BraveSoftware', 'Brave-Browser', 'User Data'),
26
+ chrome: path.join(process.env.LOCALAPPDATA || '', 'Google', 'Chrome', 'User Data'),
27
+ edge: path.join(process.env.LOCALAPPDATA || '', 'Microsoft', 'Edge', 'User Data'),
28
+ };
29
+
30
+ function detectBrowsers() {
31
+ const found = [];
32
+ for (const [name, paths] of Object.entries(BROWSER_PATHS)) {
33
+ for (const p of paths) {
34
+ if (fs.existsSync(p)) {
35
+ found.push({
36
+ name,
37
+ exe: p,
38
+ userData: BROWSER_USER_DATA[name] || '',
39
+ });
40
+ break;
41
+ }
42
+ }
43
+ }
44
+ return found;
45
+ }
46
+
47
+ function findBrowser(preferred) {
48
+ const browsers = detectBrowsers();
49
+ if (preferred) {
50
+ return browsers.find(b => b.name === preferred.toLowerCase()) || null;
51
+ }
52
+ // Config guardada
53
+ const configPath = path.join(__dirname, '..', 'browser_config.json');
54
+ if (fs.existsSync(configPath)) {
55
+ try {
56
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
57
+ if (config.exe && fs.existsSync(config.exe)) {
58
+ return config;
59
+ }
60
+ } catch (e) {}
61
+ }
62
+ // Primer navegador encontrado
63
+ if (browsers.length > 0) {
64
+ try {
65
+ fs.writeFileSync(configPath, JSON.stringify(browsers[0], null, 2));
66
+ } catch (e) {}
67
+ return browsers[0];
68
+ }
69
+ return null;
70
+ }
71
+
72
+ function testCdp(port, timeout = 3000) {
73
+ return new Promise((resolve) => {
74
+ const req = http.get(`http://127.0.0.1:${port}/json/version`, { timeout }, (res) => {
75
+ let data = '';
76
+ res.on('data', chunk => data += chunk);
77
+ res.on('end', () => {
78
+ try { resolve(JSON.parse(data)); }
79
+ catch (e) { resolve(null); }
80
+ });
81
+ });
82
+ req.on('error', () => resolve(null));
83
+ req.on('timeout', () => { req.destroy(); resolve(null); });
84
+ });
85
+ }
86
+
87
+ function isBrowserRunning(exe) {
88
+ const exeName = path.basename(exe).toLowerCase();
89
+ try {
90
+ const result = execSync(
91
+ `tasklist /FI "IMAGENAME eq ${exeName}" /FO CSV /NH`,
92
+ { timeout: 10000, encoding: 'utf-8' }
93
+ );
94
+ return result.toLowerCase().includes(exeName);
95
+ } catch (e) {
96
+ return false;
97
+ }
98
+ }
99
+
100
+ async function detectExistingCDP(browser) {
101
+ if (!isBrowserRunning(browser.exe)) return null;
102
+
103
+ // 1. DevToolsActivePort
104
+ if (browser.userData) {
105
+ const portFile = path.join(browser.userData, 'DevToolsActivePort');
106
+ if (fs.existsSync(portFile)) {
107
+ try {
108
+ const lines = fs.readFileSync(portFile, 'utf-8').trim().split('\n');
109
+ const port = parseInt(lines[0].trim());
110
+ if (port > 0) {
111
+ const result = await testCdp(port);
112
+ if (result) return port;
113
+ }
114
+ } catch (e) {}
115
+ }
116
+ }
117
+
118
+ // 2. Command line
119
+ try {
120
+ const exeName = path.basename(browser.exe).toLowerCase();
121
+ const result = execSync(
122
+ `wmic process where "name='${exeName}'" get commandline /format:list`,
123
+ { timeout: 10000, encoding: 'utf-8' }
124
+ );
125
+ const match = result.match(/--remote-debugging-port[=\s](\d{2,5})/);
126
+ if (match) {
127
+ const port = parseInt(match[1]);
128
+ if (port > 0) {
129
+ const cdp = await testCdp(port);
130
+ if (cdp) return port;
131
+ }
132
+ }
133
+ } catch (e) {}
134
+
135
+ // 3. Scan ports via netstat
136
+ try {
137
+ const exeName = path.basename(browser.exe).toLowerCase();
138
+ const taskResult = execSync(
139
+ `tasklist /FI "IMAGENAME eq ${exeName}" /FO CSV /NH`,
140
+ { timeout: 10000, encoding: 'utf-8' }
141
+ );
142
+ const pids = new Set();
143
+ taskResult.split('\n').forEach(line => {
144
+ const parts = line.trim().replace(/"/g, '').split(',');
145
+ if (parts.length >= 2) {
146
+ const pid = parseInt(parts[1]);
147
+ if (!isNaN(pid)) pids.add(pid);
148
+ }
149
+ });
150
+
151
+ if (pids.size > 0) {
152
+ const netstat = execSync('netstat -ano -p tcp', { timeout: 10000, encoding: 'utf-8' });
153
+ for (const line of netstat.split('\n')) {
154
+ const tokens = line.trim().split(/\s+/);
155
+ if (tokens.length >= 5 && tokens[3] === 'LISTENING') {
156
+ const pid = parseInt(tokens[4]);
157
+ if (pids.has(pid)) {
158
+ const portStr = tokens[1].split(':').pop();
159
+ const port = parseInt(portStr);
160
+ if (port > 1024) {
161
+ const cdp = await testCdp(port);
162
+ if (cdp) return port;
163
+ }
164
+ }
165
+ }
166
+ }
167
+ }
168
+ } catch (e) {}
169
+
170
+ return null;
171
+ }
172
+
173
+ function killBrowser(exe) {
174
+ const exeName = path.basename(exe);
175
+ try {
176
+ execSync(`taskkill /F /IM ${exeName}`, { timeout: 10000, stdio: 'pipe' });
177
+ } catch (e) {}
178
+ }
179
+
180
+ function launchBrowser(browser, { port = 0, clean = false } = {}) {
181
+ return new Promise((resolve, reject) => {
182
+ killBrowser(browser.exe);
183
+
184
+ // Wait for process to die
185
+ setTimeout(() => {
186
+ const userData = clean
187
+ ? path.join(process.env.USERPROFILE || '', 'browser-cdp-profile')
188
+ : browser.userData;
189
+
190
+ // Clean DevToolsActivePort
191
+ const portFile = path.join(userData, 'DevToolsActivePort');
192
+ try { if (fs.existsSync(portFile)) fs.unlinkSync(portFile); } catch (e) {}
193
+
194
+ const args = [
195
+ `--remote-debugging-port=${port}`,
196
+ '--remote-allow-origins=*',
197
+ '--disable-backgrounding-occluded-windows',
198
+ ];
199
+ if (clean) args.push(`--user-data-dir=${userData}`);
200
+
201
+ const child = spawn(browser.exe, args, {
202
+ detached: true,
203
+ stdio: 'ignore',
204
+ });
205
+ child.unref();
206
+
207
+ log(` PID: ${child.pid}`);
208
+ log(` Perfil: ${clean ? 'LIMPIO' : 'REAL (tus datos)'}`);
209
+
210
+ // Wait for DevToolsActivePort
211
+ if (port === 0) {
212
+ const deadline = Date.now() + 30000;
213
+ const check = () => {
214
+ if (Date.now() > deadline) {
215
+ return reject(new Error('Timeout esperando DevToolsActivePort'));
216
+ }
217
+ if (fs.existsSync(portFile)) {
218
+ try {
219
+ const lines = fs.readFileSync(portFile, 'utf-8').trim().split('\n');
220
+ const detected = parseInt(lines[0].trim());
221
+ if (detected > 0) {
222
+ return resolve({ port: detected, pid: child.pid });
223
+ }
224
+ } catch (e) {}
225
+ }
226
+ setTimeout(check, 500);
227
+ };
228
+ check();
229
+ } else {
230
+ // Fixed port: wait for CDP
231
+ const deadline = Date.now() + 30000;
232
+ const check = async () => {
233
+ if (Date.now() > deadline) {
234
+ return reject(new Error(`Timeout esperando CDP en puerto ${port}`));
235
+ }
236
+ const result = await testCdp(port);
237
+ if (result) return resolve({ port, pid: child.pid });
238
+ setTimeout(check, 500);
239
+ };
240
+ check();
241
+ }
242
+ }, 2000);
243
+ });
244
+ }
245
+
246
+ module.exports = { detectBrowsers, findBrowser, detectExistingCDP, launchBrowser, testCdp };
package/lib/config.js ADDED
@@ -0,0 +1,34 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const CDP_INFO_PATH = path.join(process.env.USERPROFILE || process.env.HOME || '.', 'cdp_info.json');
5
+
6
+ function saveCdpInfo(data) {
7
+ // Guardar en home y en directorio actual
8
+ const paths = [
9
+ CDP_INFO_PATH,
10
+ path.join(process.cwd(), 'cdp_info.json'),
11
+ ];
12
+ for (const p of paths) {
13
+ try {
14
+ fs.writeFileSync(p, JSON.stringify(data, null, 2));
15
+ } catch (e) {}
16
+ }
17
+ }
18
+
19
+ function loadCdpInfo() {
20
+ const paths = [
21
+ path.join(process.cwd(), 'cdp_info.json'),
22
+ CDP_INFO_PATH,
23
+ ];
24
+ for (const p of paths) {
25
+ try {
26
+ if (fs.existsSync(p)) {
27
+ return JSON.parse(fs.readFileSync(p, 'utf-8'));
28
+ }
29
+ } catch (e) {}
30
+ }
31
+ return null;
32
+ }
33
+
34
+ module.exports = { saveCdpInfo, loadCdpInfo };
package/lib/logger.js ADDED
@@ -0,0 +1,13 @@
1
+ const banner = () => {
2
+ console.log('');
3
+ console.log(' browser-ipc-cdp');
4
+ console.log(' Control remoto de navegadores Chromium via IPC + CDP');
5
+ console.log('');
6
+ };
7
+
8
+ const log = (msg) => console.log(` ${msg}`);
9
+ const success = (msg) => console.log(` [OK] ${msg}`);
10
+ const warn = (msg) => console.log(` [!] ${msg}`);
11
+ const error = (msg) => console.error(` [ERROR] ${msg}`);
12
+
13
+ module.exports = { banner, log, success, warn, error };
package/lib/mcp.js ADDED
@@ -0,0 +1,72 @@
1
+ const { execSync } = require('child_process');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const { log, success, warn } = require('./logger');
5
+
6
+ function getWslHostIp() {
7
+ // 1. Desde WSL: leer resolv.conf directamente
8
+ try {
9
+ if (fs.existsSync('/etc/resolv.conf')) {
10
+ const content = fs.readFileSync('/etc/resolv.conf', 'utf-8');
11
+ const match = content.match(/nameserver\s+(\d+\.\d+\.\d+\.\d+)/);
12
+ if (match) return match[1];
13
+ }
14
+ } catch (e) {}
15
+
16
+ // 2. Desde Windows: via wsl.exe
17
+ if (process.platform === 'win32') {
18
+ try {
19
+ const result = execSync('wsl.exe -e grep nameserver /etc/resolv.conf',
20
+ { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' });
21
+ const match = result.match(/nameserver\s+(\d+\.\d+\.\d+\.\d+)/);
22
+ if (match) return match[1];
23
+ } catch (e) {}
24
+
25
+ // 3. Via \\wsl$
26
+ const distros = ['Ubuntu', 'Ubuntu-22.04', 'Ubuntu-24.04', 'Debian'];
27
+ for (const distro of distros) {
28
+ const resolvPath = `\\\\wsl$\\${distro}\\etc\\resolv.conf`;
29
+ try {
30
+ if (fs.existsSync(resolvPath)) {
31
+ const content = fs.readFileSync(resolvPath, 'utf-8');
32
+ const match = content.match(/nameserver\s+(\d+\.\d+\.\d+\.\d+)/);
33
+ if (match) return match[1];
34
+ }
35
+ } catch (e) {}
36
+ }
37
+ }
38
+
39
+ return '127.0.0.1';
40
+ }
41
+
42
+ function updateMcpJson(port, wslIp) {
43
+ const braveEntry = {
44
+ command: 'npx',
45
+ args: ['-y', 'chrome-devtools-mcp@latest', '--browserUrl', `http://${wslIp}:${port}`],
46
+ };
47
+
48
+ // Actualizar .mcp.json en home y directorio actual
49
+ const mcpPaths = [
50
+ path.join(process.env.USERPROFILE || process.env.HOME || '', '.mcp.json'),
51
+ path.join(process.cwd(), '.mcp.json'),
52
+ ];
53
+
54
+ let updated = 0;
55
+ for (const mcpPath of mcpPaths) {
56
+ try {
57
+ let data = { mcpServers: {} };
58
+ if (fs.existsSync(mcpPath)) {
59
+ data = JSON.parse(fs.readFileSync(mcpPath, 'utf-8'));
60
+ }
61
+ if (!data.mcpServers) data.mcpServers = {};
62
+ data.mcpServers.brave = braveEntry;
63
+ fs.writeFileSync(mcpPath, JSON.stringify(data, null, 2));
64
+ updated++;
65
+ } catch (e) {}
66
+ }
67
+
68
+ success(`.mcp.json actualizado (${updated} archivos): brave -> ${wslIp}:${port}`);
69
+ return updated > 0;
70
+ }
71
+
72
+ module.exports = { getWslHostIp, updateMcpJson };
package/lib/network.js ADDED
@@ -0,0 +1,59 @@
1
+ const { execSync } = require('child_process');
2
+ const { log, success, warn } = require('./logger');
3
+
4
+ const FIREWALL_RULE = 'CDP All Ports (IPC)';
5
+
6
+ function setupFirewall() {
7
+ if (process.platform !== 'win32') return false;
8
+
9
+ // Verificar si ya existe
10
+ try {
11
+ const check = execSync(
12
+ `netsh advfirewall firewall show rule name="${FIREWALL_RULE}"`,
13
+ { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' }
14
+ );
15
+ if (check.includes(FIREWALL_RULE)) return true;
16
+ } catch (e) {}
17
+
18
+ // Crear regla universal
19
+ try {
20
+ execSync(
21
+ `netsh advfirewall firewall add rule name="${FIREWALL_RULE}" dir=in action=allow protocol=TCP localport=1024-65535`,
22
+ { timeout: 10000, stdio: 'pipe' }
23
+ );
24
+ success('Firewall: regla universal creada');
25
+ return true;
26
+ } catch (e) {
27
+ warn('Firewall: no se pudo crear regla (ejecuta como Admin)');
28
+ return false;
29
+ }
30
+ }
31
+
32
+ function setupPortproxy(port) {
33
+ if (process.platform !== 'win32') return false;
34
+
35
+ // Verificar si ya existe
36
+ try {
37
+ const check = execSync('netsh interface portproxy show all',
38
+ { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' });
39
+ if (check.includes(`0.0.0.0 ${port}`)) {
40
+ log(` Portproxy ya existe para puerto ${port}`);
41
+ return true;
42
+ }
43
+ } catch (e) {}
44
+
45
+ // Crear portproxy
46
+ try {
47
+ execSync(
48
+ `netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=${port} connectaddress=127.0.0.1 connectport=${port}`,
49
+ { timeout: 10000, stdio: 'pipe' }
50
+ );
51
+ success(`Portproxy: 0.0.0.0:${port} -> 127.0.0.1:${port}`);
52
+ return true;
53
+ } catch (e) {
54
+ warn(`Portproxy: fallo (ejecuta como Admin)`);
55
+ return false;
56
+ }
57
+ }
58
+
59
+ module.exports = { setupFirewall, setupPortproxy };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "browser-ipc-cdp",
3
+ "version": "1.0.0",
4
+ "description": "Control remoto de navegadores Chromium (Brave, Chrome, Edge) via IPC + CDP dinamico. Un comando para conectar Claude Code a tu navegador real.",
5
+ "bin": {
6
+ "browser-ipc-cdp": "bin/cli.js"
7
+ },
8
+ "keywords": [
9
+ "cdp",
10
+ "chrome-devtools-protocol",
11
+ "ipc",
12
+ "brave",
13
+ "chrome",
14
+ "edge",
15
+ "mcp",
16
+ "claude-code",
17
+ "browser-automation",
18
+ "devtools",
19
+ "remote-debugging",
20
+ "wsl",
21
+ "portproxy",
22
+ "chromium"
23
+ ],
24
+ "author": "Alexis Malambo <yilson.malambo@cun.edu.co>",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/alexis14kl/browser-ipc-cdp.git"
29
+ },
30
+ "homepage": "https://github.com/alexis14kl/browser-ipc-cdp#readme",
31
+ "engines": {
32
+ "node": ">=14.0.0"
33
+ },
34
+ "files": [
35
+ "bin/",
36
+ "lib/",
37
+ "templates/",
38
+ "README.md",
39
+ "LICENSE"
40
+ ]
41
+ }