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 +21 -0
- package/README.md +267 -0
- package/bin/cli.js +191 -0
- package/lib/browser.js +246 -0
- package/lib/config.js +34 -0
- package/lib/logger.js +13 -0
- package/lib/mcp.js +72 -0
- package/lib/network.js +59 -0
- package/package.json +41 -0
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
|
+
}
|