chromex-mcp 1.8.0 → 1.8.2
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/README.md
CHANGED
|
@@ -78,7 +78,7 @@ Chromex is a direct CDP layer for coding agents. It sits between raw Chrome DevT
|
|
|
78
78
|
|
|
79
79
|
The core runtime uses only Node.js built-in modules. Chromex does not install heavy browser automation runtimes, Selenium, browser drivers, telemetry SDKs, update checkers, or bundled browsers.
|
|
80
80
|
|
|
81
|
-
The only exception is the optional `audit` command: it
|
|
81
|
+
The only exception is the optional `audit` command: it uses `npx` to resolve Lighthouse on demand, then runs the Lighthouse JavaScript CLI directly with Node when you explicitly run an audit. All other CLI and MCP commands run through Chromex's own CDP client.
|
|
82
82
|
|
|
83
83
|
Development dependencies are used only for tests and token benchmarks.
|
|
84
84
|
|
package/docs/security.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Chromex is designed for AI agents interacting with real browsers. Security is a first-class concern.
|
|
4
4
|
|
|
5
|
+
## Reporting Vulnerabilities
|
|
6
|
+
|
|
7
|
+
Report suspected vulnerabilities privately to `whallysson.dev@gmail.com`. Do not open a public issue for an undisclosed vulnerability.
|
|
8
|
+
|
|
9
|
+
Include the affected versions and platforms, reproducible steps or a proof of concept, the expected impact, and any suggested remediation. Keep the report and technical details private until a fixed release is available or a disclosure date is agreed.
|
|
10
|
+
|
|
5
11
|
## Config File
|
|
6
12
|
|
|
7
13
|
All security settings live in `~/.chromex/config.json` (auto-created on first run).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chromex-mcp",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.2",
|
|
4
4
|
"description": "Agent-first Chrome DevTools toolkit with a token-efficient CLI, 85 typed MCP tools, modal-free pipe transport, diagnostics, memory analysis, extensions, and WebMCP.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -50,6 +50,13 @@
|
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"js-tiktoken": "^1.0.21",
|
|
53
|
-
"vitest": "^
|
|
53
|
+
"vitest": "^4.1.11"
|
|
54
|
+
},
|
|
55
|
+
"overrides": {
|
|
56
|
+
"esbuild": "^0.28.1",
|
|
57
|
+
"nanoid": "^3.3.16",
|
|
58
|
+
"picomatch": "^4.0.4",
|
|
59
|
+
"postcss": "^8.5.23",
|
|
60
|
+
"vite": "^7.3.5"
|
|
54
61
|
}
|
|
55
62
|
}
|
|
@@ -1,13 +1,59 @@
|
|
|
1
|
-
// Lighthouse audit via subprocess (zero deps -- invokes npx lighthouse externally)
|
|
2
1
|
// Chrome: connects to existing browser via --port (reuses session)
|
|
3
2
|
// Other browsers (Brave, Edge, etc.): Lighthouse launches its own headless Chrome
|
|
4
3
|
|
|
5
|
-
import {
|
|
4
|
+
import { execFileSync } from 'child_process';
|
|
6
5
|
import { existsSync } from 'fs';
|
|
7
6
|
import { resolveArtifactPath } from '../artifacts.mjs';
|
|
8
7
|
import { evalStr } from './evaluate.mjs';
|
|
9
8
|
|
|
10
9
|
const VALID_CATEGORIES = ['performance', 'accessibility', 'seo', 'best-practices'];
|
|
10
|
+
const LIGHTHOUSE_PACKAGE = 'lighthouse';
|
|
11
|
+
const LIGHTHOUSE_RESOLVER = `
|
|
12
|
+
const { existsSync, realpathSync } = require('node:fs');
|
|
13
|
+
const { delimiter, join } = require('node:path');
|
|
14
|
+
const executablePath = (process.env.PATH || '')
|
|
15
|
+
.split(delimiter)
|
|
16
|
+
.map(directory => join(directory, 'lighthouse'))
|
|
17
|
+
.find(existsSync);
|
|
18
|
+
if (!executablePath) throw new Error('Lighthouse executable not found');
|
|
19
|
+
process.stdout.write(realpathSync(executablePath));
|
|
20
|
+
`;
|
|
21
|
+
let cachedLighthouseCliPath;
|
|
22
|
+
|
|
23
|
+
function resolveLighthouseCliPath() {
|
|
24
|
+
if (cachedLighthouseCliPath && existsSync(cachedLighthouseCliPath)) {
|
|
25
|
+
return cachedLighthouseCliPath;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const cliPath = execFileSync('npx', [
|
|
30
|
+
'--yes',
|
|
31
|
+
`--package=${LIGHTHOUSE_PACKAGE}`,
|
|
32
|
+
'--',
|
|
33
|
+
process.execPath,
|
|
34
|
+
'-e',
|
|
35
|
+
LIGHTHOUSE_RESOLVER,
|
|
36
|
+
], {
|
|
37
|
+
encoding: 'utf8',
|
|
38
|
+
timeout: 120000,
|
|
39
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
40
|
+
shell: false,
|
|
41
|
+
}).trim();
|
|
42
|
+
|
|
43
|
+
if (!cliPath || !existsSync(cliPath)) {
|
|
44
|
+
throw new Error('Lighthouse executable not found after installation');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
cachedLighthouseCliPath = cliPath;
|
|
48
|
+
return cachedLighthouseCliPath;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
const stderr = error.stderr?.toString().trim() || '';
|
|
51
|
+
if (error.code === 'ENOENT') {
|
|
52
|
+
throw new Error('npx not found. Install Node.js with npm.');
|
|
53
|
+
}
|
|
54
|
+
throw new Error(`Lighthouse setup failed: ${stderr || error.message}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
11
57
|
|
|
12
58
|
// Find any Chromium-based browser for CHROME_PATH env var
|
|
13
59
|
function findChromiumPath() {
|
|
@@ -30,8 +76,9 @@ function findChromiumPath() {
|
|
|
30
76
|
// Check if Chrome's HTTP debug endpoint is available (Brave/Edge don't expose it)
|
|
31
77
|
function isHttpDebugAvailable(port) {
|
|
32
78
|
try {
|
|
33
|
-
const result =
|
|
79
|
+
const result = execFileSync('curl', ['-sf', `http://127.0.0.1:${port}/json/version`], {
|
|
34
80
|
encoding: 'utf8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'],
|
|
81
|
+
shell: false,
|
|
35
82
|
});
|
|
36
83
|
return result.length > 0;
|
|
37
84
|
} catch {
|
|
@@ -88,7 +135,8 @@ export async function auditStr(cdp, sid, categories, device, reportPath) {
|
|
|
88
135
|
mode = 'standalone (headless Chrome)';
|
|
89
136
|
}
|
|
90
137
|
|
|
91
|
-
const
|
|
138
|
+
const lighthouseArgs = [url, ...args];
|
|
139
|
+
const lighthouseCliPath = resolveLighthouseCliPath();
|
|
92
140
|
|
|
93
141
|
// Set CHROME_PATH for standalone mode (Lighthouse uses chrome-launcher which reads it)
|
|
94
142
|
const env = { ...process.env };
|
|
@@ -99,18 +147,16 @@ export async function auditStr(cdp, sid, categories, device, reportPath) {
|
|
|
99
147
|
|
|
100
148
|
let jsonOutput;
|
|
101
149
|
try {
|
|
102
|
-
jsonOutput =
|
|
150
|
+
jsonOutput = execFileSync(process.execPath, [lighthouseCliPath, ...lighthouseArgs], {
|
|
103
151
|
encoding: 'utf8',
|
|
104
152
|
timeout: 120000,
|
|
105
153
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
106
154
|
maxBuffer: 50 * 1024 * 1024,
|
|
107
155
|
env,
|
|
156
|
+
shell: false,
|
|
108
157
|
});
|
|
109
158
|
} catch (e) {
|
|
110
159
|
const stderr = e.stderr?.toString().trim() || '';
|
|
111
|
-
if (stderr.includes('not found') || stderr.includes('ENOENT')) {
|
|
112
|
-
throw new Error('lighthouse not found. Install: npm i -g lighthouse');
|
|
113
|
-
}
|
|
114
160
|
if (stderr.includes('No Chrome installations found')) {
|
|
115
161
|
throw new Error('Lighthouse needs Chrome installed to run in standalone mode. Install Google Chrome or run against a Chrome instance with debug port.');
|
|
116
162
|
}
|
|
@@ -801,7 +801,7 @@ const TOOLS = [
|
|
|
801
801
|
categories: { type: 'string', description: 'Comma-separated: performance,accessibility,seo,best-practices (default: all)' },
|
|
802
802
|
device: { type: 'string', enum: ['mobile', 'desktop'], description: 'Device preset (default: mobile)' },
|
|
803
803
|
reportPath: { type: 'string', description: 'Path to save full HTML report' },
|
|
804
|
-
}, ['target'],
|
|
804
|
+
}, ['target'], DESTRUCTIVE),
|
|
805
805
|
|
|
806
806
|
tool('chromex_stats',
|
|
807
807
|
'Session analytics: command counts, average timing, error rates, action timeline. All data is local, never sent externally.',
|
|
@@ -1053,7 +1053,7 @@ function toolToCmd(name, p) {
|
|
|
1053
1053
|
return { cmd: 'webmcp', args: a };
|
|
1054
1054
|
}
|
|
1055
1055
|
case 'chromex_webauthn': return { cmd: 'webauthn', args: [p.action] };
|
|
1056
|
-
case 'chromex_audit': return { cmd: 'audit', args: [p.categories
|
|
1056
|
+
case 'chromex_audit': return { cmd: 'audit', args: [p.categories ?? '', p.device ?? '', p.reportPath ?? ''] };
|
|
1057
1057
|
case 'chromex_stats': {
|
|
1058
1058
|
const a = [];
|
|
1059
1059
|
if (p.full) a.push('--full');
|