browser-ipc-cdp 1.4.0 → 1.6.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/lib/browser.js CHANGED
@@ -396,13 +396,16 @@ function launchBrowser(browser, { port = 0, clean = false } = {}) {
396
396
 
397
397
  // Wait for process to die
398
398
  setTimeout(() => {
399
+ const defaultUserData = browser.userData || getBrowserPaths(browser.name).userData || '';
399
400
  const userData = clean
400
- ? path.join(process.env.USERPROFILE || '', 'browser-cdp-profile')
401
- : browser.userData;
401
+ ? path.join(process.env.USERPROFILE || process.env.HOME || '', 'browser-cdp-profile')
402
+ : defaultUserData;
402
403
 
403
404
  // Clean DevToolsActivePort
404
- const portFile = path.join(userData, 'DevToolsActivePort');
405
- try { if (fs.existsSync(portFile)) fs.unlinkSync(portFile); } catch (e) {}
405
+ if (userData) {
406
+ const portFile = path.join(userData, 'DevToolsActivePort');
407
+ try { if (fs.existsSync(portFile)) fs.unlinkSync(portFile); } catch (e) {}
408
+ }
406
409
 
407
410
  const args = [
408
411
  `--remote-debugging-port=${port}`,
@@ -434,14 +437,15 @@ function launchBrowser(browser, { port = 0, clean = false } = {}) {
434
437
 
435
438
  // Wait for DevToolsActivePort
436
439
  if (port === 0) {
440
+ const activePortFile = userData ? path.join(userData, 'DevToolsActivePort') : null;
437
441
  const deadline = Date.now() + 30000;
438
442
  const check = () => {
439
443
  if (Date.now() > deadline) {
440
444
  return reject(new Error('Timeout esperando DevToolsActivePort'));
441
445
  }
442
- if (fs.existsSync(portFile)) {
446
+ if (activePortFile && fs.existsSync(activePortFile)) {
443
447
  try {
444
- const lines = fs.readFileSync(portFile, 'utf-8').trim().split('\n');
448
+ const lines = fs.readFileSync(activePortFile, 'utf-8').trim().split('\n');
445
449
  const detected = parseInt(lines[0].trim());
446
450
  if (detected > 0) {
447
451
  return resolve({ port: detected, pid: child.pid });
package/lib/mcp.js CHANGED
@@ -47,17 +47,51 @@ function getWslHostIp() {
47
47
  }
48
48
 
49
49
  function updateMcpJson(port, wslIp) {
50
+ // Estrategia: ejecutar el MCP desde Windows via cmd.exe
51
+ // Asi usa 127.0.0.1 directo, sin portproxy ni firewall
50
52
  const braveEntry = {
51
- command: 'npx',
52
- args: ['-y', 'chrome-devtools-mcp@latest', '--browserUrl', `http://${wslIp}:${port}`],
53
+ command: 'cmd.exe',
54
+ args: ['/c', 'npx', '-y', 'chrome-devtools-mcp@latest', '--browserUrl', `http://127.0.0.1:${port}`],
53
55
  };
54
56
 
55
- // Actualizar .mcp.json en home y directorio actual
56
- const mcpPaths = [
57
- path.join(process.env.USERPROFILE || process.env.HOME || '', '.mcp.json'),
58
- path.join(process.cwd(), '.mcp.json'),
59
- ];
57
+ // Rutas base donde buscar .mcp.json
58
+ const home = process.env.USERPROFILE || process.env.HOME || '';
59
+ const cwd = process.cwd();
60
+ const scriptDir = path.join(__dirname, '..');
60
61
 
62
+ // 1. Rutas fijas conocidas
63
+ const mcpPaths = new Set([
64
+ path.join(home, '.mcp.json'), // Home del usuario
65
+ path.join(cwd, '.mcp.json'), // Donde ejecuto npx
66
+ path.join(scriptDir, '.mcp.json'), // Carpeta del paquete IPC
67
+ ]);
68
+
69
+ // 2. Buscar .mcp.json existentes en subdirectorios del home (1 nivel)
70
+ try {
71
+ const homeItems = fs.readdirSync(home);
72
+ for (const item of homeItems) {
73
+ const candidate = path.join(home, item, '.mcp.json');
74
+ if (fs.existsSync(candidate)) {
75
+ mcpPaths.add(candidate);
76
+ }
77
+ }
78
+ } catch (e) {}
79
+
80
+ // 3. En Windows: buscar en Desktop y sus subcarpetas
81
+ const desktop = path.join(home, 'Desktop');
82
+ try {
83
+ if (fs.existsSync(desktop)) {
84
+ const desktopItems = fs.readdirSync(desktop);
85
+ for (const item of desktopItems) {
86
+ const candidate = path.join(desktop, item, '.mcp.json');
87
+ if (fs.existsSync(candidate)) {
88
+ mcpPaths.add(candidate);
89
+ }
90
+ }
91
+ }
92
+ } catch (e) {}
93
+
94
+ // 4. Actualizar todos los .mcp.json encontrados
61
95
  let updated = 0;
62
96
  for (const mcpPath of mcpPaths) {
63
97
  try {
@@ -66,9 +100,15 @@ function updateMcpJson(port, wslIp) {
66
100
  data = JSON.parse(fs.readFileSync(mcpPath, 'utf-8'));
67
101
  }
68
102
  if (!data.mcpServers) data.mcpServers = {};
103
+
104
+ // Solo actualizar si el archivo existe o es el home
105
+ const isHome = mcpPath === path.join(home, '.mcp.json');
106
+ if (!fs.existsSync(mcpPath) && !isHome) continue;
107
+
69
108
  data.mcpServers.brave = braveEntry;
70
109
  fs.writeFileSync(mcpPath, JSON.stringify(data, null, 2));
71
110
  updated++;
111
+ log(` -> ${mcpPath}`);
72
112
  } catch (e) {}
73
113
  }
74
114
 
package/lib/network.js CHANGED
@@ -1,147 +1,128 @@
1
1
  const { execSync } = require('child_process');
2
+ const fs = require('fs');
3
+ const path = require('path');
2
4
  const { log, success, warn } = require('./logger');
3
5
 
4
6
  const IS_WIN = process.platform === 'win32';
5
7
  let IS_WSL = false;
6
- try { const { IS_WSL: w } = require('./browser'); IS_WSL = w; } catch (e) {
7
- try {
8
- const fs = require('fs');
9
- const v = fs.readFileSync('/proc/version', 'utf-8').toLowerCase();
10
- IS_WSL = v.includes('microsoft') || v.includes('wsl');
11
- } catch (e2) {}
12
- }
8
+ try {
9
+ const v = fs.readFileSync('/proc/version', 'utf-8').toLowerCase();
10
+ IS_WSL = v.includes('microsoft') || v.includes('wsl');
11
+ } catch (e) {}
13
12
 
14
- const NEEDS_NETSH = IS_WIN; // WSL no necesita netsh, usa host IP directo
15
13
  const FIREWALL_RULE = 'CDP All Ports (IPC)';
16
14
 
17
- function setupFirewall() {
18
- // Solo Windows nativo necesita firewall
19
- // WSL: el navegador corre en Windows pero WSL accede via host IP, firewall ya debe estar abierto
20
- // Mac/Linux: localhost directo
21
- if (!NEEDS_NETSH) {
22
- if (IS_WSL) {
23
- // En WSL: ejecutar netsh via cmd.exe para crear la regla en Windows
24
- try {
25
- const check = execSync(
26
- `cmd.exe /c netsh advfirewall firewall show rule name="${FIREWALL_RULE}"`,
27
- { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' }
28
- );
29
- if (check.includes(FIREWALL_RULE)) return true;
30
- } catch (e) {}
31
-
32
- // Intento 1: cmd.exe directo
33
- try {
34
- execSync(
35
- `cmd.exe /c netsh advfirewall firewall add rule name="${FIREWALL_RULE}" dir=in action=allow protocol=TCP localport=1024-65535`,
36
- { timeout: 10000, stdio: 'pipe' }
37
- );
38
- success('Firewall: regla universal creada (via WSL)');
39
- return true;
40
- } catch (e) {}
41
-
42
- // Intento 2: elevar con powershell
43
- try {
44
- const fwCmd = `netsh advfirewall firewall add rule name="${FIREWALL_RULE}" dir=in action=allow protocol=TCP localport=1024-65535`;
45
- execSync(
46
- `powershell.exe -Command "Start-Process cmd -ArgumentList '/c ${fwCmd}' -Verb RunAs -Wait"`,
47
- { timeout: 30000, stdio: 'pipe' }
48
- );
49
- success('Firewall: regla universal creada (elevado)');
50
- return true;
51
- } catch (e) {
52
- warn('Firewall: no se pudo crear regla (ejecuta como Admin en Windows)');
53
- return false;
54
- }
55
- }
15
+ /**
16
+ * Ejecuta netsh con permisos elevados usando Python ctypes.
17
+ * Python puede usar ctypes.windll.shell32.ShellExecuteW para elevar sin .bat
18
+ * O si ya tiene permisos, ejecuta directo.
19
+ */
20
+ function runNetshElevated(netshCommand) {
21
+ // Python script inline que ejecuta como admin sin UAC visible
22
+ const pyScript = `
23
+ import subprocess, sys, os
24
+ cmd = r'${netshCommand}'
25
+ try:
26
+ r = subprocess.run(cmd, shell=True, capture_output=True, timeout=10)
27
+ if r.returncode == 0:
28
+ sys.exit(0)
29
+ except:
30
+ pass
31
+ # Si fallo, intentar con ctypes (elevation silenciosa)
32
+ try:
33
+ import ctypes
34
+ if not ctypes.windll.shell32.IsUserAnAdmin():
35
+ ctypes.windll.shell32.ShellExecuteW(None, "runas", "cmd.exe", f"/c {cmd}", None, 0)
36
+ import time; time.sleep(3)
37
+ sys.exit(0)
38
+ except:
39
+ pass
40
+ sys.exit(1)
41
+ `.trim();
42
+
43
+ try {
44
+ const pythonCmd = IS_WSL ? 'python3' : 'python';
45
+ execSync(`${pythonCmd} -c "${pyScript.replace(/"/g, '\\"').replace(/\n/g, ';')}"`,
46
+ { timeout: 15000, stdio: 'pipe' });
56
47
  return true;
57
- }
48
+ } catch (e) {}
58
49
 
59
- // Windows nativo
50
+ // Fallback: powershell directo
60
51
  try {
61
- const check = execSync(
62
- `netsh advfirewall firewall show rule name="${FIREWALL_RULE}"`,
63
- { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' }
64
- );
52
+ const ps = IS_WSL ? 'powershell.exe' : 'powershell';
53
+ execSync(`${ps} -Command "Start-Process cmd -ArgumentList '/c ${netshCommand}' -Verb RunAs -Wait -WindowStyle Hidden"`,
54
+ { timeout: 30000, stdio: 'pipe' });
55
+ return true;
56
+ } catch (e) {}
57
+
58
+ return false;
59
+ }
60
+
61
+ function setupFirewall() {
62
+ if (!IS_WIN && !IS_WSL) return true;
63
+
64
+ // Verificar si ya existe
65
+ try {
66
+ const cmd = IS_WSL
67
+ ? `cmd.exe /c netsh advfirewall firewall show rule name="${FIREWALL_RULE}"`
68
+ : `netsh advfirewall firewall show rule name="${FIREWALL_RULE}"`;
69
+ const check = execSync(cmd, { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' });
65
70
  if (check.includes(FIREWALL_RULE)) return true;
66
71
  } catch (e) {}
67
72
 
73
+ const fwCmd = `netsh advfirewall firewall add rule name="${FIREWALL_RULE}" dir=in action=allow protocol=TCP localport=1024-65535`;
74
+
75
+ // Intento 1: directo
68
76
  try {
69
- execSync(
70
- `netsh advfirewall firewall add rule name="${FIREWALL_RULE}" dir=in action=allow protocol=TCP localport=1024-65535`,
71
- { timeout: 10000, stdio: 'pipe' }
72
- );
73
- success('Firewall: regla universal creada');
77
+ const cmd = IS_WSL ? `cmd.exe /c ${fwCmd}` : fwCmd;
78
+ execSync(cmd, { timeout: 10000, stdio: 'pipe' });
79
+ success('Firewall: regla creada');
74
80
  return true;
75
- } catch (e) {
76
- warn('Firewall: no se pudo crear regla (ejecuta como Admin)');
77
- return false;
78
- }
79
- }
81
+ } catch (e) {}
80
82
 
81
- function setupPortproxy(port) {
82
- // Mac/Linux nativo: no necesita portproxy
83
- if (!IS_WIN && !IS_WSL) {
83
+ // Intento 2: elevado
84
+ if (runNetshElevated(fwCmd)) {
85
+ success('Firewall: regla creada (elevado)');
84
86
  return true;
85
87
  }
86
88
 
87
- // WSL: ejecutar netsh via cmd.exe
88
- if (IS_WSL) {
89
- try {
90
- const check = execSync('cmd.exe /c netsh interface portproxy show all',
91
- { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' });
92
- if (check.includes(`0.0.0.0 ${port}`)) {
93
- log(` Portproxy ya existe para puerto ${port}`);
94
- return true;
95
- }
96
- } catch (e) {}
97
-
98
- // Intento 1: cmd.exe directo (si WSL tiene permisos)
99
- try {
100
- execSync(
101
- `cmd.exe /c netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=${port} connectaddress=127.0.0.1 connectport=${port}`,
102
- { timeout: 10000, stdio: 'pipe' }
103
- );
104
- success(`Portproxy: 0.0.0.0:${port} -> 127.0.0.1:${port} (via WSL)`);
105
- return true;
106
- } catch (e) {}
107
-
108
- // Intento 2: elevar con powershell -Verb RunAs
109
- try {
110
- const netshCmd = `netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=${port} connectaddress=127.0.0.1 connectport=${port}`;
111
- execSync(
112
- `powershell.exe -Command "Start-Process cmd -ArgumentList '/c ${netshCmd}' -Verb RunAs -Wait"`,
113
- { timeout: 30000, stdio: 'pipe' }
114
- );
115
- success(`Portproxy: 0.0.0.0:${port} -> 127.0.0.1:${port} (elevado)`);
116
- return true;
117
- } catch (e) {
118
- warn(`Portproxy: fallo. Ejecuta manualmente como Admin en Windows:`);
119
- warn(` netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=${port} connectaddress=127.0.0.1 connectport=${port}`);
120
- return false;
121
- }
122
- }
89
+ warn('Firewall: no se pudo crear regla');
90
+ return false;
91
+ }
92
+
93
+ function setupPortproxy(port) {
94
+ if (!IS_WIN && !IS_WSL) return true;
123
95
 
124
- // Windows nativo
96
+ // Verificar si ya existe
125
97
  try {
126
- const check = execSync('netsh interface portproxy show all',
127
- { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' });
98
+ const cmd = IS_WSL
99
+ ? 'cmd.exe /c netsh interface portproxy show all'
100
+ : 'netsh interface portproxy show all';
101
+ const check = execSync(cmd, { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' });
128
102
  if (check.includes(`0.0.0.0 ${port}`)) {
129
103
  log(` Portproxy ya existe para puerto ${port}`);
130
104
  return true;
131
105
  }
132
106
  } catch (e) {}
133
107
 
108
+ const proxyCmd = `netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=${port} connectaddress=127.0.0.1 connectport=${port}`;
109
+
110
+ // Intento 1: directo
134
111
  try {
135
- execSync(
136
- `netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=${port} connectaddress=127.0.0.1 connectport=${port}`,
137
- { timeout: 10000, stdio: 'pipe' }
138
- );
112
+ const cmd = IS_WSL ? `cmd.exe /c ${proxyCmd}` : proxyCmd;
113
+ execSync(cmd, { timeout: 10000, stdio: 'pipe' });
139
114
  success(`Portproxy: 0.0.0.0:${port} -> 127.0.0.1:${port}`);
140
115
  return true;
141
- } catch (e) {
142
- warn(`Portproxy: fallo (ejecuta como Admin)`);
143
- return false;
116
+ } catch (e) {}
117
+
118
+ // Intento 2: elevado
119
+ if (runNetshElevated(proxyCmd)) {
120
+ success(`Portproxy: 0.0.0.0:${port} -> 127.0.0.1:${port} (elevado)`);
121
+ return true;
144
122
  }
123
+
124
+ warn(`Portproxy: fallo para puerto ${port}`);
125
+ return false;
145
126
  }
146
127
 
147
128
  module.exports = { setupFirewall, setupPortproxy };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-ipc-cdp",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
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
5
  "bin": {
6
6
  "browser-ipc-cdp": "bin/cli.js"