browser-ipc-cdp 1.0.0 → 1.2.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/bin/cli.js CHANGED
@@ -12,7 +12,7 @@
12
12
  * npx browser-ipc-cdp --status # Ver estado actual
13
13
  * npx browser-ipc-cdp --uninstall # Desinstalar
14
14
  */
15
- const { detectBrowsers, findBrowser, launchBrowser, detectExistingCDP } = require('../lib/browser');
15
+ const { detectBrowsers, findBrowser, launchBrowser, detectExistingCDP, IS_WSL, IS_WIN, IS_MAC } = require('../lib/browser');
16
16
  const { setupPortproxy, setupFirewall } = require('../lib/network');
17
17
  const { updateMcpJson, getWslHostIp } = require('../lib/mcp');
18
18
  const { saveCdpInfo, loadCdpInfo } = require('../lib/config');
@@ -30,6 +30,8 @@ for (let i = 0; i < args.length; i++) {
30
30
 
31
31
  async function main() {
32
32
  banner();
33
+ const platform = IS_WIN ? 'Windows' : IS_WSL ? 'WSL (Windows host)' : IS_MAC ? 'macOS' : 'Linux';
34
+ log(`Plataforma: ${platform}`);
33
35
 
34
36
  // --list: listar navegadores
35
37
  if (flags.list) {
package/lib/browser.js CHANGED
@@ -4,43 +4,203 @@ const path = require('path');
4
4
  const http = require('http');
5
5
  const { log, success, warn } = require('./logger');
6
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
- };
7
+ const IS_WIN = process.platform === 'win32';
8
+ const IS_MAC = process.platform === 'darwin';
9
+
10
+ // Detectar WSL: Linux pero con acceso a Windows
11
+ function isWSL() {
12
+ if (IS_WIN || IS_MAC) return false;
13
+ try {
14
+ const version = fs.readFileSync('/proc/version', 'utf-8').toLowerCase();
15
+ if (version.includes('microsoft') || version.includes('wsl')) return true;
16
+ } catch (e) {}
17
+ try {
18
+ return fs.existsSync('/proc/sys/fs/binfmt_misc/WSLInterop');
19
+ } catch (e) {}
20
+ return false;
21
+ }
22
+
23
+ const IS_WSL = isWSL();
24
+ const HOME = process.env.HOME || process.env.USERPROFILE || '';
25
+
26
+ // En WSL: obtener paths de Windows via /mnt/c/
27
+ function getWindowsEnv(varName) {
28
+ if (IS_WIN) return process.env[varName] || '';
29
+ if (IS_WSL) {
30
+ try {
31
+ const result = execSync(`cmd.exe /c echo %${varName}%`, { encoding: 'utf-8', timeout: 5000, stdio: 'pipe' });
32
+ const winPath = result.trim();
33
+ if (winPath && !winPath.includes('%')) {
34
+ // Convertir C:\Users\... a /mnt/c/Users/...
35
+ return winPath.replace(/\\/g, '/').replace(/^([A-Za-z]):/, (_, d) => `/mnt/${d.toLowerCase()}`);
36
+ }
37
+ } catch (e) {}
38
+ }
39
+ return '';
40
+ }
41
+
42
+ const LOCALAPPDATA = IS_WIN ? (process.env.LOCALAPPDATA || '') : getWindowsEnv('LOCALAPPDATA');
43
+ const USERPROFILE = IS_WIN ? (process.env.USERPROFILE || '') : getWindowsEnv('USERPROFILE');
23
44
 
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'),
45
+ // Program Files: en WSL usar /mnt/c/
46
+ const PROGRAMFILES = IS_WIN ? (process.env.PROGRAMFILES || 'C:\\Program Files')
47
+ : IS_WSL ? '/mnt/c/Program Files' : '';
48
+ const PROGRAMFILES86 = IS_WIN ? (process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)')
49
+ : IS_WSL ? '/mnt/c/Program Files (x86)' : '';
50
+
51
+ // Rutas dinámicas por plataforma
52
+ const BROWSER_REGISTRY = {
53
+ brave: {
54
+ win: [
55
+ path.join(PROGRAMFILES, 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
56
+ path.join(PROGRAMFILES86, 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
57
+ path.join(LOCALAPPDATA, 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
58
+ ],
59
+ wsl: [
60
+ path.join(PROGRAMFILES, 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
61
+ path.join(PROGRAMFILES86, 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
62
+ ...(LOCALAPPDATA ? [path.join(LOCALAPPDATA, 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe')] : []),
63
+ ],
64
+ mac: [
65
+ '/Applications/Brave Browser.app/Contents/MacOS/Brave Browser',
66
+ path.join(HOME, 'Applications', 'Brave Browser.app', 'Contents', 'MacOS', 'Brave Browser'),
67
+ ],
68
+ linux: [
69
+ '/usr/bin/brave-browser',
70
+ '/usr/bin/brave',
71
+ '/snap/bin/brave',
72
+ '/opt/brave.com/brave/brave-browser',
73
+ ],
74
+ userData: {
75
+ win: path.join(LOCALAPPDATA, 'BraveSoftware', 'Brave-Browser', 'User Data'),
76
+ wsl: LOCALAPPDATA ? path.join(LOCALAPPDATA, 'BraveSoftware', 'Brave-Browser', 'User Data') : '',
77
+ mac: path.join(HOME, 'Library', 'Application Support', 'BraveSoftware', 'Brave-Browser'),
78
+ linux: path.join(HOME, '.config', 'BraveSoftware', 'Brave-Browser'),
79
+ },
80
+ processName: { win: 'brave.exe', wsl: 'brave.exe', mac: 'Brave Browser', linux: 'brave' },
81
+ },
82
+ chrome: {
83
+ win: [
84
+ path.join(PROGRAMFILES, 'Google', 'Chrome', 'Application', 'chrome.exe'),
85
+ path.join(PROGRAMFILES86, 'Google', 'Chrome', 'Application', 'chrome.exe'),
86
+ path.join(LOCALAPPDATA, 'Google', 'Chrome', 'Application', 'chrome.exe'),
87
+ ],
88
+ wsl: [
89
+ path.join(PROGRAMFILES, 'Google', 'Chrome', 'Application', 'chrome.exe'),
90
+ path.join(PROGRAMFILES86, 'Google', 'Chrome', 'Application', 'chrome.exe'),
91
+ ...(LOCALAPPDATA ? [path.join(LOCALAPPDATA, 'Google', 'Chrome', 'Application', 'chrome.exe')] : []),
92
+ ],
93
+ mac: [
94
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
95
+ path.join(HOME, 'Applications', 'Google Chrome.app', 'Contents', 'MacOS', 'Google Chrome'),
96
+ ],
97
+ linux: [
98
+ '/usr/bin/google-chrome',
99
+ '/usr/bin/google-chrome-stable',
100
+ '/snap/bin/chromium',
101
+ '/usr/bin/chromium-browser',
102
+ '/usr/bin/chromium',
103
+ ],
104
+ userData: {
105
+ win: path.join(LOCALAPPDATA, 'Google', 'Chrome', 'User Data'),
106
+ wsl: LOCALAPPDATA ? path.join(LOCALAPPDATA, 'Google', 'Chrome', 'User Data') : '',
107
+ mac: path.join(HOME, 'Library', 'Application Support', 'Google', 'Chrome'),
108
+ linux: path.join(HOME, '.config', 'google-chrome'),
109
+ },
110
+ processName: { win: 'chrome.exe', wsl: 'chrome.exe', mac: 'Google Chrome', linux: 'chrome' },
111
+ },
112
+ edge: {
113
+ win: [
114
+ path.join(PROGRAMFILES86, 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
115
+ path.join(PROGRAMFILES, 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
116
+ ],
117
+ wsl: [
118
+ path.join(PROGRAMFILES86, 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
119
+ path.join(PROGRAMFILES, 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
120
+ ],
121
+ mac: [
122
+ '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
123
+ ],
124
+ linux: [
125
+ '/usr/bin/microsoft-edge',
126
+ '/usr/bin/microsoft-edge-stable',
127
+ ],
128
+ userData: {
129
+ win: path.join(LOCALAPPDATA, 'Microsoft', 'Edge', 'User Data'),
130
+ wsl: LOCALAPPDATA ? path.join(LOCALAPPDATA, 'Microsoft', 'Edge', 'User Data') : '',
131
+ mac: path.join(HOME, 'Library', 'Application Support', 'Microsoft Edge'),
132
+ linux: path.join(HOME, '.config', 'microsoft-edge'),
133
+ },
134
+ processName: { win: 'msedge.exe', wsl: 'msedge.exe', mac: 'Microsoft Edge', linux: 'msedge' },
135
+ },
136
+ chromium: {
137
+ win: [
138
+ path.join(LOCALAPPDATA, 'Chromium', 'Application', 'chrome.exe'),
139
+ ],
140
+ wsl: [
141
+ ...(LOCALAPPDATA ? [path.join(LOCALAPPDATA, 'Chromium', 'Application', 'chrome.exe')] : []),
142
+ ],
143
+ mac: [
144
+ '/Applications/Chromium.app/Contents/MacOS/Chromium',
145
+ ],
146
+ linux: [
147
+ '/usr/bin/chromium',
148
+ '/usr/bin/chromium-browser',
149
+ '/snap/bin/chromium',
150
+ ],
151
+ userData: {
152
+ win: path.join(LOCALAPPDATA, 'Chromium', 'User Data'),
153
+ wsl: LOCALAPPDATA ? path.join(LOCALAPPDATA, 'Chromium', 'User Data') : '',
154
+ mac: path.join(HOME, 'Library', 'Application Support', 'Chromium'),
155
+ linux: path.join(HOME, '.config', 'chromium'),
156
+ },
157
+ processName: { win: 'chrome.exe', wsl: 'chrome.exe', mac: 'Chromium', linux: 'chromium' },
158
+ },
28
159
  };
29
160
 
161
+ function getPlatform() {
162
+ if (IS_WIN) return 'win';
163
+ if (IS_WSL) return 'wsl';
164
+ if (IS_MAC) return 'mac';
165
+ return 'linux';
166
+ }
167
+
168
+ function getBrowserPaths(name) {
169
+ const entry = BROWSER_REGISTRY[name];
170
+ if (!entry) return { paths: [], userData: '', processName: '' };
171
+ const plat = getPlatform();
172
+ return {
173
+ paths: entry[plat] || [],
174
+ userData: (entry.userData && entry.userData[plat]) || '',
175
+ processName: (entry.processName && entry.processName[plat]) || name,
176
+ };
177
+ }
178
+
30
179
  function detectBrowsers() {
31
180
  const found = [];
32
- for (const [name, paths] of Object.entries(BROWSER_PATHS)) {
181
+ for (const name of Object.keys(BROWSER_REGISTRY)) {
182
+ const { paths, userData } = getBrowserPaths(name);
33
183
  for (const p of paths) {
34
184
  if (fs.existsSync(p)) {
35
- found.push({
36
- name,
37
- exe: p,
38
- userData: BROWSER_USER_DATA[name] || '',
39
- });
185
+ found.push({ name, exe: p, userData });
40
186
  break;
41
187
  }
42
188
  }
43
189
  }
190
+ // Fallback: buscar en PATH
191
+ const cmds = IS_WIN
192
+ ? ['brave', 'chrome', 'msedge']
193
+ : ['brave-browser', 'brave', 'google-chrome', 'google-chrome-stable', 'chromium', 'chromium-browser', 'microsoft-edge'];
194
+ for (const cmd of cmds) {
195
+ try {
196
+ const which = IS_WIN
197
+ ? execSync(`where ${cmd} 2>nul`, { encoding: 'utf-8', timeout: 5000 }).trim().split('\n')[0]
198
+ : execSync(`which ${cmd} 2>/dev/null`, { encoding: 'utf-8', timeout: 5000 }).trim();
199
+ if (which && fs.existsSync(which) && !found.some(b => b.exe === which)) {
200
+ found.push({ name: cmd, exe: which, userData: '' });
201
+ }
202
+ } catch (e) {}
203
+ }
44
204
  return found;
45
205
  }
46
206
 
@@ -85,13 +245,19 @@ function testCdp(port, timeout = 3000) {
85
245
  }
86
246
 
87
247
  function isBrowserRunning(exe) {
88
- const exeName = path.basename(exe).toLowerCase();
248
+ const exeName = path.basename(exe);
89
249
  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);
250
+ if (IS_WIN || IS_WSL) {
251
+ // Windows y WSL: usar tasklist de Windows
252
+ const cmd = IS_WSL
253
+ ? `cmd.exe /c tasklist /FI "IMAGENAME eq ${exeName}" /FO CSV /NH`
254
+ : `tasklist /FI "IMAGENAME eq ${exeName}" /FO CSV /NH`;
255
+ const result = execSync(cmd, { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' });
256
+ return result.toLowerCase().includes(exeName.toLowerCase());
257
+ } else {
258
+ const result = execSync(`pgrep -f "${exeName}"`, { timeout: 5000, stdio: 'pipe' });
259
+ return result.length > 0;
260
+ }
95
261
  } catch (e) {
96
262
  return false;
97
263
  }
@@ -115,14 +281,22 @@ async function detectExistingCDP(browser) {
115
281
  }
116
282
  }
117
283
 
118
- // 2. Command line
284
+ // 2. Command line scan
119
285
  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})/);
286
+ const exeName = path.basename(browser.exe);
287
+ let cmdOutput = '';
288
+ if (IS_WIN || IS_WSL) {
289
+ const wmicCmd = `wmic process where "name='${exeName}'" get commandline /format:list`;
290
+ cmdOutput = IS_WSL
291
+ ? execSync(`cmd.exe /c ${wmicCmd}`, { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' })
292
+ : execSync(wmicCmd, { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' });
293
+ } else {
294
+ cmdOutput = execSync(
295
+ `ps aux | grep "${exeName}" | grep -v grep`,
296
+ { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' }
297
+ );
298
+ }
299
+ const match = cmdOutput.match(/--remote-debugging-port[=\s](\d{2,5})/);
126
300
  if (match) {
127
301
  const port = parseInt(match[1]);
128
302
  if (port > 0) {
@@ -132,37 +306,70 @@ async function detectExistingCDP(browser) {
132
306
  }
133
307
  } catch (e) {}
134
308
 
135
- // 3. Scan ports via netstat
309
+ // 3. Scan ports
136
310
  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
- );
311
+ const exeName = path.basename(browser.exe);
142
312
  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]);
313
+
314
+ if (IS_WIN || IS_WSL) {
315
+ const taskCmd = `tasklist /FI "IMAGENAME eq ${exeName}" /FO CSV /NH`;
316
+ const taskResult = IS_WSL
317
+ ? execSync(`cmd.exe /c ${taskCmd}`, { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' })
318
+ : execSync(taskCmd, { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' });
319
+ taskResult.split('\n').forEach(line => {
320
+ const parts = line.trim().replace(/"/g, '').split(',');
321
+ if (parts.length >= 2) {
322
+ const pid = parseInt(parts[1]);
323
+ if (!isNaN(pid)) pids.add(pid);
324
+ }
325
+ });
326
+ } else {
327
+ const pgrepResult = execSync(`pgrep -f "${exeName}"`,
328
+ { timeout: 5000, encoding: 'utf-8', stdio: 'pipe' });
329
+ pgrepResult.trim().split('\n').forEach(p => {
330
+ const pid = parseInt(p.trim());
147
331
  if (!isNaN(pid)) pids.add(pid);
148
- }
149
- });
332
+ });
333
+ }
150
334
 
151
335
  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;
336
+ if (IS_WIN || IS_WSL) {
337
+ const netstatCmd = 'netstat -ano -p tcp';
338
+ const netstat = IS_WSL
339
+ ? execSync(`cmd.exe /c ${netstatCmd}`, { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' })
340
+ : execSync(netstatCmd, { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' });
341
+ for (const line of netstat.split('\n')) {
342
+ const tokens = line.trim().split(/\s+/);
343
+ if (tokens.length >= 5 && tokens[3] === 'LISTENING') {
344
+ const pid = parseInt(tokens[4]);
345
+ if (pids.has(pid)) {
346
+ const portStr = tokens[1].split(':').pop();
347
+ const port = parseInt(portStr);
348
+ if (port > 1024) {
349
+ const cdp = await testCdp(port);
350
+ if (cdp) return port;
351
+ }
163
352
  }
164
353
  }
165
354
  }
355
+ } else {
356
+ // Mac/Linux: use lsof or ss
357
+ for (const pid of pids) {
358
+ try {
359
+ const lsof = execSync(`lsof -i -P -n -p ${pid} 2>/dev/null | grep LISTEN`,
360
+ { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' });
361
+ for (const line of lsof.split('\n')) {
362
+ const match = line.match(/:(\d+)\s/);
363
+ if (match) {
364
+ const port = parseInt(match[1]);
365
+ if (port > 1024) {
366
+ const cdp = await testCdp(port);
367
+ if (cdp) return port;
368
+ }
369
+ }
370
+ }
371
+ } catch (e) {}
372
+ }
166
373
  }
167
374
  }
168
375
  } catch (e) {}
@@ -173,7 +380,13 @@ async function detectExistingCDP(browser) {
173
380
  function killBrowser(exe) {
174
381
  const exeName = path.basename(exe);
175
382
  try {
176
- execSync(`taskkill /F /IM ${exeName}`, { timeout: 10000, stdio: 'pipe' });
383
+ if (IS_WIN) {
384
+ execSync(`taskkill /F /IM ${exeName}`, { timeout: 10000, stdio: 'pipe' });
385
+ } else if (IS_WSL) {
386
+ execSync(`cmd.exe /c taskkill /F /IM ${exeName}`, { timeout: 10000, stdio: 'pipe' });
387
+ } else {
388
+ execSync(`pkill -f "${exeName}"`, { timeout: 10000, stdio: 'pipe' });
389
+ }
177
390
  } catch (e) {}
178
391
  }
179
392
 
@@ -198,10 +411,22 @@ function launchBrowser(browser, { port = 0, clean = false } = {}) {
198
411
  ];
199
412
  if (clean) args.push(`--user-data-dir=${userData}`);
200
413
 
201
- const child = spawn(browser.exe, args, {
202
- detached: true,
203
- stdio: 'ignore',
204
- });
414
+ const spawnOpts = { detached: true, stdio: 'ignore' };
415
+ let child;
416
+
417
+ if (IS_WSL) {
418
+ // WSL: convertir ruta /mnt/c/... a C:\... y lanzar via cmd.exe
419
+ const winExe = browser.exe.replace(/^\/mnt\/([a-z])\//, (_, d) => `${d.toUpperCase()}:\\`).replace(/\//g, '\\');
420
+ const winArgs = args.map(a => {
421
+ if (a.startsWith('--user-data-dir=/mnt/')) {
422
+ return a.replace(/^--user-data-dir=\/mnt\/([a-z])\//, (_, d) => `--user-data-dir=${d.toUpperCase()}:\\`).replace(/\//g, '\\');
423
+ }
424
+ return a;
425
+ });
426
+ child = spawn('cmd.exe', ['/c', 'start', '', winExe, ...winArgs], spawnOpts);
427
+ } else {
428
+ child = spawn(browser.exe, args, spawnOpts);
429
+ }
205
430
  child.unref();
206
431
 
207
432
  log(` PID: ${child.pid}`);
@@ -243,4 +468,4 @@ function launchBrowser(browser, { port = 0, clean = false } = {}) {
243
468
  });
244
469
  }
245
470
 
246
- module.exports = { detectBrowsers, findBrowser, detectExistingCDP, launchBrowser, testCdp };
471
+ module.exports = { detectBrowsers, findBrowser, detectExistingCDP, launchBrowser, testCdp, IS_WSL, IS_WIN, IS_MAC };
package/lib/mcp.js CHANGED
@@ -4,6 +4,13 @@ const path = require('path');
4
4
  const { log, success, warn } = require('./logger');
5
5
 
6
6
  function getWslHostIp() {
7
+ const IS_WIN = process.platform === 'win32';
8
+
9
+ // Mac/Linux: localhost funciona directo, no necesita IP especial
10
+ if (!IS_WIN) return '127.0.0.1';
11
+
12
+ // Windows: detectar IP para WSL
13
+
7
14
  // 1. Desde WSL: leer resolv.conf directamente
8
15
  try {
9
16
  if (fs.existsSync('/etc/resolv.conf')) {
@@ -14,26 +21,24 @@ function getWslHostIp() {
14
21
  } catch (e) {}
15
22
 
16
23
  // 2. Desde Windows: via wsl.exe
17
- if (process.platform === 'win32') {
24
+ try {
25
+ const result = execSync('wsl.exe -e grep nameserver /etc/resolv.conf',
26
+ { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' });
27
+ const match = result.match(/nameserver\s+(\d+\.\d+\.\d+\.\d+)/);
28
+ if (match) return match[1];
29
+ } catch (e) {}
30
+
31
+ // 3. Via \\wsl$
32
+ const distros = ['Ubuntu', 'Ubuntu-22.04', 'Ubuntu-24.04', 'Debian'];
33
+ for (const distro of distros) {
34
+ const resolvPath = `\\\\wsl$\\${distro}\\etc\\resolv.conf`;
18
35
  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];
36
+ if (fs.existsSync(resolvPath)) {
37
+ const content = fs.readFileSync(resolvPath, 'utf-8');
38
+ const match = content.match(/nameserver\s+(\d+\.\d+\.\d+\.\d+)/);
39
+ if (match) return match[1];
40
+ }
23
41
  } 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
42
  }
38
43
 
39
44
  return '127.0.0.1';
package/lib/network.js CHANGED
@@ -1,12 +1,50 @@
1
1
  const { execSync } = require('child_process');
2
2
  const { log, success, warn } = require('./logger');
3
3
 
4
+ const IS_WIN = process.platform === 'win32';
5
+ 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
+ }
13
+
14
+ const NEEDS_NETSH = IS_WIN; // WSL no necesita netsh, usa host IP directo
4
15
  const FIREWALL_RULE = 'CDP All Ports (IPC)';
5
16
 
6
17
  function setupFirewall() {
7
- if (process.platform !== 'win32') return false;
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
+ try {
33
+ execSync(
34
+ `cmd.exe /c netsh advfirewall firewall add rule name="${FIREWALL_RULE}" dir=in action=allow protocol=TCP localport=1024-65535`,
35
+ { timeout: 10000, stdio: 'pipe' }
36
+ );
37
+ success('Firewall: regla universal creada (via WSL)');
38
+ return true;
39
+ } catch (e) {
40
+ warn('Firewall: no se pudo crear regla desde WSL (ejecuta como Admin en Windows)');
41
+ return false;
42
+ }
43
+ }
44
+ return true;
45
+ }
8
46
 
9
- // Verificar si ya existe
47
+ // Windows nativo
10
48
  try {
11
49
  const check = execSync(
12
50
  `netsh advfirewall firewall show rule name="${FIREWALL_RULE}"`,
@@ -15,7 +53,6 @@ function setupFirewall() {
15
53
  if (check.includes(FIREWALL_RULE)) return true;
16
54
  } catch (e) {}
17
55
 
18
- // Crear regla universal
19
56
  try {
20
57
  execSync(
21
58
  `netsh advfirewall firewall add rule name="${FIREWALL_RULE}" dir=in action=allow protocol=TCP localport=1024-65535`,
@@ -30,9 +67,36 @@ function setupFirewall() {
30
67
  }
31
68
 
32
69
  function setupPortproxy(port) {
33
- if (process.platform !== 'win32') return false;
70
+ // Mac/Linux nativo: no necesita portproxy
71
+ if (!IS_WIN && !IS_WSL) {
72
+ return true;
73
+ }
74
+
75
+ // WSL: ejecutar netsh via cmd.exe
76
+ if (IS_WSL) {
77
+ try {
78
+ const check = execSync('cmd.exe /c netsh interface portproxy show all',
79
+ { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' });
80
+ if (check.includes(`0.0.0.0 ${port}`)) {
81
+ log(` Portproxy ya existe para puerto ${port}`);
82
+ return true;
83
+ }
84
+ } catch (e) {}
85
+
86
+ try {
87
+ execSync(
88
+ `cmd.exe /c netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=${port} connectaddress=127.0.0.1 connectport=${port}`,
89
+ { timeout: 10000, stdio: 'pipe' }
90
+ );
91
+ success(`Portproxy: 0.0.0.0:${port} -> 127.0.0.1:${port} (via WSL)`);
92
+ return true;
93
+ } catch (e) {
94
+ warn(`Portproxy: fallo desde WSL (ejecuta como Admin en Windows)`);
95
+ return false;
96
+ }
97
+ }
34
98
 
35
- // Verificar si ya existe
99
+ // Windows nativo
36
100
  try {
37
101
  const check = execSync('netsh interface portproxy show all',
38
102
  { timeout: 10000, encoding: 'utf-8', stdio: 'pipe' });
@@ -42,7 +106,6 @@ function setupPortproxy(port) {
42
106
  }
43
107
  } catch (e) {}
44
108
 
45
- // Crear portproxy
46
109
  try {
47
110
  execSync(
48
111
  `netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=${port} connectaddress=127.0.0.1 connectport=${port}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-ipc-cdp",
3
- "version": "1.0.0",
3
+ "version": "1.2.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"