browser-ipc-cdp 1.8.2 → 1.8.3

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.
@@ -17,10 +17,11 @@
17
17
  *
18
18
  * Flujo:
19
19
  * 1. Lee cdp_info.json -> puerto candidato
20
- * 2. Verifica CDP (curl /json/version) en hostIP:puerto
20
+ * 2. Verifica CDP (/json/version) en hostIP:puerto
21
21
  * 3. Si responde -> lanza chrome-devtools-mcp con ese URL
22
- * 4. Si NO responde -> ejecuta brave_ipc.py --no-kill (auto-launch)
23
- * 5. Releer cdp_info.json + reverificar -> conectar
22
+ * 4. Si stale -> auto-discovery via tasklist+netstat de procesos Chromium
23
+ * 5. Si encuentra port vivo -> reescribe cdp_info.json y conecta
24
+ * 6. Ultimo recurso -> brave_ipc.py --no-kill (auto-launch)
24
25
  */
25
26
  const { execSync, spawn, spawnSync } = require('child_process');
26
27
  const fs = require('fs');
@@ -112,6 +113,90 @@ function testCdp(url, timeoutMs = 3000) {
112
113
  });
113
114
  }
114
115
 
116
+ /**
117
+ * Descubre el puerto CDP real de cualquier proceso Chromium activo.
118
+ * Usa tasklist (PIDs por imagen) + netstat (LISTENING por PID) y
119
+ * prueba /json/version en cada candidato.
120
+ *
121
+ * Retorna { port, version } si encuentra uno vivo, o null.
122
+ */
123
+ async function discoverBrowserCdp() {
124
+ if (!IS_WIN) return null;
125
+
126
+ const images = ['brave.exe', 'chrome.exe', 'msedge.exe', 'chromium.exe'];
127
+ const pids = new Set();
128
+
129
+ for (const img of images) {
130
+ try {
131
+ const out = execSync(
132
+ `tasklist /FI "IMAGENAME eq ${img}" /FO CSV /NH`,
133
+ { encoding: 'utf-8', timeout: 8000, maxBuffer: 16 * 1024 * 1024, stdio: ['ignore', 'pipe', 'ignore'] }
134
+ );
135
+ for (const line of out.split('\n')) {
136
+ const parts = line.trim().split('","');
137
+ if (parts.length >= 2) {
138
+ const pid = parseInt(parts[1].replace(/"/g, ''), 10);
139
+ if (Number.isFinite(pid) && pid > 0) pids.add(pid);
140
+ }
141
+ }
142
+ } catch (e) {}
143
+ }
144
+
145
+ if (pids.size === 0) {
146
+ logErr('Auto-discovery: ningun proceso Chromium corriendo');
147
+ return null;
148
+ }
149
+
150
+ let netstatOut = '';
151
+ try {
152
+ netstatOut = execSync('netstat -ano', {
153
+ encoding: 'utf-8', timeout: 20000, maxBuffer: 64 * 1024 * 1024, stdio: ['ignore', 'pipe', 'ignore'],
154
+ });
155
+ } catch (e) {
156
+ logErr(`Auto-discovery netstat fallo: ${e.message}`);
157
+ return null;
158
+ }
159
+
160
+ const ports = new Set();
161
+ for (const line of netstatOut.split('\n')) {
162
+ const tokens = line.trim().split(/\s+/);
163
+ if (tokens.length < 5 || tokens[0] !== 'TCP' || !tokens[3].includes('LISTENING')) continue;
164
+ const pid = parseInt(tokens[4], 10);
165
+ if (!pids.has(pid)) continue;
166
+ const m = tokens[1].match(/:(\d+)$/);
167
+ if (!m) continue;
168
+ const port = parseInt(m[1], 10);
169
+ if (port > 1024 && port < 65536) ports.add(port);
170
+ }
171
+
172
+ logErr(`Auto-discovery: ${ports.size} ports candidatos de ${pids.size} procesos`);
173
+
174
+ for (const port of ports) {
175
+ const version = await testCdp(`http://127.0.0.1:${port}`, 1500);
176
+ if (version) {
177
+ logErr(`Auto-discovery: CDP vivo en puerto ${port}`);
178
+ return { port, version };
179
+ }
180
+ }
181
+
182
+ return null;
183
+ }
184
+
185
+ function rewriteCdpInfo(port, version) {
186
+ const ws = (version && version.webSocketDebuggerUrl) || '';
187
+ const browser = (version && version.Browser) || 'Unknown';
188
+ const data = {
189
+ DEBUG_PORT: port,
190
+ DEBUG_WS: ws,
191
+ BROWSER: browser,
192
+ CDP_URL: `http://127.0.0.1:${port}`,
193
+ MODE: 'AUTO_DISCOVERED',
194
+ };
195
+ for (const p of [HOME_CDP_INFO, CDP_INFO]) {
196
+ try { fs.writeFileSync(p, JSON.stringify(data, null, 2), 'utf-8'); } catch (e) {}
197
+ }
198
+ }
199
+
115
200
  async function ensureCdpReady() {
116
201
  const candidates = getHostCandidates();
117
202
  logErr(`Candidatos host: ${candidates.join(', ')}`);
@@ -125,9 +210,21 @@ async function ensureCdpReady() {
125
210
  logErr(`CDP activo en ${url}`);
126
211
  return url;
127
212
  }
213
+ logErr(`Puerto ${port} de cdp_info.json stale. Auto-discovery...`);
214
+ }
215
+
216
+ // Intento 2: auto-discovery via tasklist+netstat (no reinicia browser)
217
+ const discovered = await discoverBrowserCdp();
218
+ if (discovered) {
219
+ rewriteCdpInfo(discovered.port, discovered.version);
220
+ const url = await tryCandidates(discovered.port, candidates, 3000);
221
+ if (url) {
222
+ logErr(`CDP descubierto y cdp_info.json actualizado: ${url}`);
223
+ return url;
224
+ }
128
225
  }
129
226
 
130
- // Intento 2: auto-launch via brave_ipc.py
227
+ // Intento 3: auto-launch via brave_ipc.py
131
228
  logErr('CDP no responde. Auto-lanzando Brave via brave_ipc.py...');
132
229
  if (!fs.existsSync(BRAVE_IPC_PY)) {
133
230
  logErr(`brave_ipc.py no encontrado en ${BRAVE_IPC_PY}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-ipc-cdp",
3
- "version": "1.8.2",
3
+ "version": "1.8.3",
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"