browser-ipc-cdp 1.8.3 → 2.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/brave_ipc.py +9 -2
- package/brave_mcp_launcher.js +132 -173
- package/package.json +7 -3
package/brave_ipc.py
CHANGED
|
@@ -539,11 +539,18 @@ def launch_browser_ipc(
|
|
|
539
539
|
print()
|
|
540
540
|
return result
|
|
541
541
|
else:
|
|
542
|
-
# Navegador corriendo pero SIN CDP
|
|
543
|
-
print(f" CDP no detectado. Reiniciando {browser_name} con CDP...")
|
|
542
|
+
# Navegador corriendo pero SIN CDP
|
|
544
543
|
if kill_existing:
|
|
544
|
+
print(f" CDP no detectado. Reiniciando {browser_name} con CDP...")
|
|
545
545
|
kill_browser(browser_exe)
|
|
546
546
|
time.sleep(2)
|
|
547
|
+
else:
|
|
548
|
+
# --no-kill activo. No podemos reusar user-data-dir real (lock).
|
|
549
|
+
# Forzar perfil separado para evitar conflicto.
|
|
550
|
+
print(f" CDP no detectado. --no-kill activo: lanzando 2da")
|
|
551
|
+
print(f" instancia en perfil separado (no toca tu sesion).")
|
|
552
|
+
clean = True
|
|
553
|
+
USER_DATA_DIR = CLEAN_USER_DATA
|
|
547
554
|
elif kill_existing:
|
|
548
555
|
print(f" {browser_name} no esta corriendo.")
|
|
549
556
|
|
package/brave_mcp_launcher.js
CHANGED
|
@@ -1,111 +1,77 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Brave MCP
|
|
2
|
+
* Brave MCP Launcher (refactored)
|
|
3
3
|
*
|
|
4
|
-
* Resuelve
|
|
5
|
-
* chrome-devtools-mcp apuntando al puerto correcto. Si CDP no responde,
|
|
6
|
-
* lanza brave_ipc.py automaticamente para iniciar el navegador.
|
|
4
|
+
* Resuelve el puerto CDP de Brave/Chrome/Edge y arranca chrome-devtools-mcp.
|
|
7
5
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* "command": "node",
|
|
13
|
-
* "args": ["C:\\Users\\NyGsoft\\Desktop\\ipc\\brave_mcp_launcher.js"]
|
|
14
|
-
* }
|
|
15
|
-
* }
|
|
16
|
-
* }
|
|
6
|
+
* Estrategia (rápida → lenta):
|
|
7
|
+
* 1. Leer DevToolsActivePort de cada User Data dir conocido (instantáneo, autoritativo).
|
|
8
|
+
* 2. Auto-discovery via tasklist + netstat (fallback si no hay archivo o stale).
|
|
9
|
+
* 3. Auto-launch via brave_ipc.py --no-kill (último recurso).
|
|
17
10
|
*
|
|
18
|
-
*
|
|
19
|
-
* 1. Lee cdp_info.json -> puerto candidato
|
|
20
|
-
* 2. Verifica CDP (/json/version) en hostIP:puerto
|
|
21
|
-
* 3. Si responde -> lanza chrome-devtools-mcp con ese URL
|
|
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)
|
|
11
|
+
* Usa binario global de chrome-devtools-mcp (sin npx cold cache).
|
|
25
12
|
*/
|
|
13
|
+
|
|
26
14
|
const { execSync, spawn, spawnSync } = require('child_process');
|
|
27
15
|
const fs = require('fs');
|
|
28
16
|
const path = require('path');
|
|
29
17
|
const http = require('http');
|
|
30
18
|
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const BRAVE_IPC_PY = path.join(
|
|
19
|
+
const HERE = __dirname;
|
|
20
|
+
const CDP_INFO = path.join(HERE, 'cdp_info.json');
|
|
21
|
+
const BRAVE_IPC_PY = path.join(HERE, 'brave_ipc.py');
|
|
34
22
|
|
|
35
23
|
const IS_WIN = process.platform === 'win32';
|
|
36
24
|
let IS_WSL = false;
|
|
37
25
|
try {
|
|
38
26
|
const v = fs.readFileSync('/proc/version', 'utf-8').toLowerCase();
|
|
39
27
|
IS_WSL = v.includes('microsoft') || v.includes('wsl');
|
|
40
|
-
} catch
|
|
41
|
-
|
|
42
|
-
function logErr(msg) {
|
|
43
|
-
process.stderr.write(`[brave-mcp] ${msg}\n`);
|
|
44
|
-
}
|
|
28
|
+
} catch {}
|
|
45
29
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (fs.existsSync(p)) {
|
|
49
|
-
try { return JSON.parse(fs.readFileSync(p, 'utf-8')); } catch (e) {}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
30
|
+
const HOME = process.env.USERPROFILE || process.env.HOME || '';
|
|
31
|
+
const LOCALAPPDATA = process.env.LOCALAPPDATA || path.join(HOME, 'AppData', 'Local');
|
|
54
32
|
|
|
55
|
-
|
|
56
|
-
|
|
33
|
+
const USER_DATA_DIRS = [
|
|
34
|
+
{ name: 'brave', path: path.join(LOCALAPPDATA, 'BraveSoftware', 'Brave-Browser', 'User Data') },
|
|
35
|
+
{ name: 'chrome', path: path.join(LOCALAPPDATA, 'Google', 'Chrome', 'User Data') },
|
|
36
|
+
{ name: 'edge', path: path.join(LOCALAPPDATA, 'Microsoft', 'Edge', 'User Data') },
|
|
37
|
+
{ name: 'chromium', path: path.join(LOCALAPPDATA, 'Chromium', 'User Data') },
|
|
38
|
+
];
|
|
57
39
|
|
|
58
|
-
|
|
59
|
-
const candidates = [];
|
|
60
|
-
const add = (ip) => { if (ip && !seen.has(ip)) { seen.add(ip); candidates.push(ip); } };
|
|
61
|
-
|
|
62
|
-
// 1. Gateway IP via /proc/net/route — IP real del host Windows en WSL2 NAT
|
|
40
|
+
function resolveChromeDevtoolsMcpBin() {
|
|
63
41
|
try {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
break;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
} catch (e) {}
|
|
79
|
-
|
|
80
|
-
// 2. Localhost (WSL2 mirrored networking mode)
|
|
81
|
-
add('127.0.0.1');
|
|
82
|
-
|
|
83
|
-
// 3. Nameservers de /etc/resolv.conf (último recurso)
|
|
84
|
-
try {
|
|
85
|
-
const resolv = fs.readFileSync('/etc/resolv.conf', 'utf-8');
|
|
86
|
-
const re = /nameserver\s+(\d+\.\d+\.\d+\.\d+)/g;
|
|
87
|
-
let m;
|
|
88
|
-
while ((m = re.exec(resolv))) add(m[1]);
|
|
89
|
-
} catch (e) {}
|
|
42
|
+
return require.resolve('chrome-devtools-mcp/build/src/bin/chrome-devtools-mcp.js');
|
|
43
|
+
} catch {}
|
|
44
|
+
const candidates = [
|
|
45
|
+
path.join('C:', 'nvm4w', 'nodejs', 'node_modules', 'chrome-devtools-mcp', 'build', 'src', 'bin', 'chrome-devtools-mcp.js'),
|
|
46
|
+
path.join(process.env.APPDATA || '', 'npm', 'node_modules', 'chrome-devtools-mcp', 'build', 'src', 'bin', 'chrome-devtools-mcp.js'),
|
|
47
|
+
path.join('/usr', 'local', 'lib', 'node_modules', 'chrome-devtools-mcp', 'build', 'src', 'bin', 'chrome-devtools-mcp.js'),
|
|
48
|
+
];
|
|
49
|
+
for (const p of candidates) { if (p && fs.existsSync(p)) return p; }
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const CHROME_DEVTOOLS_MCP_BIN = resolveChromeDevtoolsMcpBin();
|
|
90
53
|
|
|
91
|
-
|
|
54
|
+
function log(msg) {
|
|
55
|
+
process.stderr.write(`[brave-mcp] ${msg}\n`);
|
|
92
56
|
}
|
|
93
57
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const url = `http://${host}:${port}`;
|
|
97
|
-
if (await testCdp(url, timeoutMs)) return url;
|
|
98
|
-
}
|
|
99
|
-
return null;
|
|
58
|
+
function readJsonSafe(p) {
|
|
59
|
+
try { return JSON.parse(fs.readFileSync(p, 'utf-8')); } catch { return null; }
|
|
100
60
|
}
|
|
101
61
|
|
|
102
|
-
function testCdp(url, timeoutMs =
|
|
62
|
+
function testCdp(url, timeoutMs = 1500) {
|
|
103
63
|
return new Promise((resolve) => {
|
|
104
64
|
const req = http.get(`${url}/json/version`, { timeout: timeoutMs }, (res) => {
|
|
65
|
+
if (res.statusCode !== 200) { res.resume(); return resolve(null); }
|
|
105
66
|
let data = '';
|
|
106
67
|
res.on('data', c => data += c);
|
|
107
68
|
res.on('end', () => {
|
|
108
|
-
try {
|
|
69
|
+
try {
|
|
70
|
+
const json = JSON.parse(data);
|
|
71
|
+
if (json && typeof json.webSocketDebuggerUrl === 'string' && json.webSocketDebuggerUrl) {
|
|
72
|
+
resolve(json);
|
|
73
|
+
} else { resolve(null); }
|
|
74
|
+
} catch { resolve(null); }
|
|
109
75
|
});
|
|
110
76
|
});
|
|
111
77
|
req.on('error', () => resolve(null));
|
|
@@ -113,16 +79,33 @@ function testCdp(url, timeoutMs = 3000) {
|
|
|
113
79
|
});
|
|
114
80
|
}
|
|
115
81
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
82
|
+
function readDevToolsActivePort(userDataDir) {
|
|
83
|
+
const file = path.join(userDataDir, 'DevToolsActivePort');
|
|
84
|
+
if (!fs.existsSync(file)) return null;
|
|
85
|
+
try {
|
|
86
|
+
const txt = fs.readFileSync(file, 'utf-8');
|
|
87
|
+
const lines = txt.split(/\r?\n/);
|
|
88
|
+
const port = parseInt(lines[0], 10);
|
|
89
|
+
if (Number.isFinite(port) && port > 0) return port;
|
|
90
|
+
} catch {}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function tryDevToolsActivePort() {
|
|
95
|
+
for (const ud of USER_DATA_DIRS) {
|
|
96
|
+
const port = readDevToolsActivePort(ud.path);
|
|
97
|
+
if (!port) continue;
|
|
98
|
+
const version = await testCdp(`http://127.0.0.1:${port}`, 1500);
|
|
99
|
+
if (version) {
|
|
100
|
+
log(`DevToolsActivePort hit: ${ud.name} -> :${port}`);
|
|
101
|
+
return { port, version };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
125
106
|
|
|
107
|
+
async function discoverViaNetstat() {
|
|
108
|
+
if (!IS_WIN) return null;
|
|
126
109
|
const images = ['brave.exe', 'chrome.exe', 'msedge.exe', 'chromium.exe'];
|
|
127
110
|
const pids = new Set();
|
|
128
111
|
|
|
@@ -139,21 +122,21 @@ async function discoverBrowserCdp() {
|
|
|
139
122
|
if (Number.isFinite(pid) && pid > 0) pids.add(pid);
|
|
140
123
|
}
|
|
141
124
|
}
|
|
142
|
-
} catch
|
|
125
|
+
} catch {}
|
|
143
126
|
}
|
|
144
127
|
|
|
145
128
|
if (pids.size === 0) {
|
|
146
|
-
|
|
129
|
+
log('Discovery: no Chromium processes');
|
|
147
130
|
return null;
|
|
148
131
|
}
|
|
149
132
|
|
|
150
|
-
let netstatOut
|
|
133
|
+
let netstatOut;
|
|
151
134
|
try {
|
|
152
135
|
netstatOut = execSync('netstat -ano', {
|
|
153
136
|
encoding: 'utf-8', timeout: 20000, maxBuffer: 64 * 1024 * 1024, stdio: ['ignore', 'pipe', 'ignore'],
|
|
154
137
|
});
|
|
155
138
|
} catch (e) {
|
|
156
|
-
|
|
139
|
+
log(`Discovery netstat failed: ${e.message}`);
|
|
157
140
|
return null;
|
|
158
141
|
}
|
|
159
142
|
|
|
@@ -169,120 +152,96 @@ async function discoverBrowserCdp() {
|
|
|
169
152
|
if (port > 1024 && port < 65536) ports.add(port);
|
|
170
153
|
}
|
|
171
154
|
|
|
172
|
-
|
|
155
|
+
log(`Discovery: ${ports.size} candidate ports across ${pids.size} processes`);
|
|
173
156
|
|
|
174
157
|
for (const port of ports) {
|
|
175
158
|
const version = await testCdp(`http://127.0.0.1:${port}`, 1500);
|
|
176
159
|
if (version) {
|
|
177
|
-
|
|
160
|
+
log(`Discovery hit: :${port}`);
|
|
178
161
|
return { port, version };
|
|
179
162
|
}
|
|
180
163
|
}
|
|
181
|
-
|
|
182
164
|
return null;
|
|
183
165
|
}
|
|
184
166
|
|
|
185
|
-
function
|
|
186
|
-
const ws = (version && version.webSocketDebuggerUrl) || '';
|
|
187
|
-
const browser = (version && version.Browser) || 'Unknown';
|
|
167
|
+
function writeCdpInfo(port, version, mode) {
|
|
188
168
|
const data = {
|
|
189
169
|
DEBUG_PORT: port,
|
|
190
|
-
DEBUG_WS:
|
|
191
|
-
BROWSER:
|
|
170
|
+
DEBUG_WS: version.webSocketDebuggerUrl || '',
|
|
171
|
+
BROWSER: version.Browser || 'Unknown',
|
|
192
172
|
CDP_URL: `http://127.0.0.1:${port}`,
|
|
193
|
-
MODE:
|
|
173
|
+
MODE: mode,
|
|
174
|
+
UPDATED_AT: new Date().toISOString(),
|
|
194
175
|
};
|
|
195
|
-
|
|
196
|
-
|
|
176
|
+
try { fs.writeFileSync(CDP_INFO, JSON.stringify(data, null, 2), 'utf-8'); } catch (e) {
|
|
177
|
+
log(`Write cdp_info failed: ${e.message}`);
|
|
197
178
|
}
|
|
198
179
|
}
|
|
199
180
|
|
|
200
|
-
async function
|
|
201
|
-
const candidates = getHostCandidates();
|
|
202
|
-
logErr(`Candidatos host: ${candidates.join(', ')}`);
|
|
203
|
-
let info = readCdpInfo();
|
|
204
|
-
let port = info && info.DEBUG_PORT;
|
|
205
|
-
|
|
206
|
-
// Intento 1: probar candidatos con el puerto en cdp_info.json
|
|
207
|
-
if (port) {
|
|
208
|
-
const url = await tryCandidates(port, candidates, 3000);
|
|
209
|
-
if (url) {
|
|
210
|
-
logErr(`CDP activo en ${url}`);
|
|
211
|
-
return url;
|
|
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
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Intento 3: auto-launch via brave_ipc.py
|
|
228
|
-
logErr('CDP no responde. Auto-lanzando Brave via brave_ipc.py...');
|
|
181
|
+
async function autoLaunch() {
|
|
229
182
|
if (!fs.existsSync(BRAVE_IPC_PY)) {
|
|
230
|
-
|
|
183
|
+
log(`brave_ipc.py not found at ${BRAVE_IPC_PY}`);
|
|
231
184
|
return null;
|
|
232
185
|
}
|
|
233
|
-
|
|
234
|
-
const
|
|
235
|
-
const launchCmd = IS_WSL
|
|
186
|
+
log('Auto-launch via brave_ipc.py --no-kill');
|
|
187
|
+
const cmd = IS_WSL
|
|
236
188
|
? `cmd.exe /c python "${BRAVE_IPC_PY.replace(/^\/mnt\/([a-z])\//, (_, d) => `${d.toUpperCase()}:\\`).replace(/\//g, '\\\\')}" --no-kill`
|
|
237
|
-
:
|
|
238
|
-
|
|
189
|
+
: `python "${BRAVE_IPC_PY}" --no-kill`;
|
|
239
190
|
try {
|
|
240
|
-
spawnSync(
|
|
191
|
+
spawnSync(cmd, { shell: true, timeout: 60000, stdio: 'pipe' });
|
|
241
192
|
} catch (e) {
|
|
242
|
-
|
|
193
|
+
log(`Auto-launch failed: ${e.message}`);
|
|
194
|
+
return null;
|
|
243
195
|
}
|
|
196
|
+
return await tryDevToolsActivePort();
|
|
197
|
+
}
|
|
244
198
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (port) {
|
|
249
|
-
const url = await tryCandidates(port, candidates, 5000);
|
|
250
|
-
if (url) {
|
|
251
|
-
logErr(`CDP activo despues de auto-launch en ${url}`);
|
|
252
|
-
return url;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
199
|
+
async function ensureCdp() {
|
|
200
|
+
let result = await tryDevToolsActivePort();
|
|
201
|
+
if (result) return result;
|
|
255
202
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
203
|
+
log('DevToolsActivePort miss. Falling back to netstat discovery.');
|
|
204
|
+
result = await discoverViaNetstat();
|
|
205
|
+
if (result) return result;
|
|
259
206
|
|
|
260
|
-
|
|
261
|
-
|
|
207
|
+
log('No live CDP found. Auto-launching browser.');
|
|
208
|
+
result = await autoLaunch();
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
262
211
|
|
|
263
|
-
|
|
264
|
-
|
|
212
|
+
function pickRunner() {
|
|
213
|
+
if (CHROME_DEVTOOLS_MCP_BIN && fs.existsSync(CHROME_DEVTOOLS_MCP_BIN)) {
|
|
214
|
+
return { cmd: process.execPath, args: [CHROME_DEVTOOLS_MCP_BIN] };
|
|
265
215
|
}
|
|
216
|
+
log(`chrome-devtools-mcp bin not resolvable. Falling back to npx (slow first run).`);
|
|
217
|
+
return IS_WIN
|
|
218
|
+
? { cmd: 'cmd.exe', args: ['/c', 'npx', '-y', 'chrome-devtools-mcp@latest'], shell: true }
|
|
219
|
+
: { cmd: 'npx', args: ['-y', 'chrome-devtools-mcp@latest'], shell: true };
|
|
220
|
+
}
|
|
266
221
|
|
|
267
|
-
|
|
268
|
-
|
|
222
|
+
async function main() {
|
|
223
|
+
const cdp = await ensureCdp();
|
|
224
|
+
|
|
225
|
+
let browserUrl;
|
|
226
|
+
if (cdp) {
|
|
227
|
+
writeCdpInfo(cdp.port, cdp.version, 'RESOLVED');
|
|
228
|
+
browserUrl = `http://127.0.0.1:${cdp.port}`;
|
|
229
|
+
log(`CDP ready: ${browserUrl}`);
|
|
230
|
+
} else {
|
|
231
|
+
browserUrl = 'http://127.0.0.1:9222';
|
|
232
|
+
log(`CDP unresolved. MCP will retry against fallback ${browserUrl}.`);
|
|
233
|
+
}
|
|
269
234
|
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
? ['/c', 'npx', '-y', 'chrome-devtools-mcp@latest', '--browserUrl', finalUrl]
|
|
274
|
-
: ['-y', 'chrome-devtools-mcp@latest', '--browserUrl', finalUrl],
|
|
275
|
-
{ stdio: ['inherit', 'inherit', 'inherit'], shell: !IS_WIN }
|
|
276
|
-
);
|
|
235
|
+
const runner = pickRunner();
|
|
236
|
+
const args = [...runner.args, '--browserUrl', browserUrl];
|
|
237
|
+
log(`Spawn: ${runner.cmd} ${args.join(' ')}`);
|
|
277
238
|
|
|
278
|
-
child
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
process.exit(1);
|
|
239
|
+
const child = spawn(runner.cmd, args, {
|
|
240
|
+
stdio: ['inherit', 'inherit', 'inherit'],
|
|
241
|
+
shell: !!runner.shell,
|
|
282
242
|
});
|
|
243
|
+
child.on('exit', (code) => process.exit(code || 0));
|
|
244
|
+
child.on('error', (e) => { log(`spawn error: ${e.message}`); process.exit(1); });
|
|
283
245
|
}
|
|
284
246
|
|
|
285
|
-
main().catch(e => {
|
|
286
|
-
logErr(`fatal: ${e.message}`);
|
|
287
|
-
process.exit(1);
|
|
288
|
-
});
|
|
247
|
+
main().catch(e => { log(`fatal: ${e.message}`); process.exit(1); });
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "browser-ipc-cdp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.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
|
-
"browser-ipc-cdp": "bin/cli.js"
|
|
6
|
+
"browser-ipc-cdp": "bin/cli.js",
|
|
7
|
+
"browser-ipc-cdp-mcp": "brave_mcp_launcher.js"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"chrome-devtools-mcp": "^0.25.0"
|
|
7
11
|
},
|
|
8
12
|
"keywords": [
|
|
9
13
|
"cdp",
|
|
@@ -29,7 +33,7 @@
|
|
|
29
33
|
},
|
|
30
34
|
"homepage": "https://github.com/alexis14kl/browser-ipc-cdp#readme",
|
|
31
35
|
"engines": {
|
|
32
|
-
"node": ">=
|
|
36
|
+
"node": ">=18.0.0"
|
|
33
37
|
},
|
|
34
38
|
"files": [
|
|
35
39
|
"bin/",
|