chromex-mcp 1.8.1 → 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 runs Lighthouse as a subprocess with `npx --yes lighthouse` when you explicitly run an audit. All other CLI and MCP commands run through Chromex's own CDP client.
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chromex-mcp",
3
- "version": "1.8.1",
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": {
@@ -1,4 +1,3 @@
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
 
@@ -8,6 +7,53 @@ 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() {
@@ -89,7 +135,8 @@ export async function auditStr(cdp, sid, categories, device, reportPath) {
89
135
  mode = 'standalone (headless Chrome)';
90
136
  }
91
137
 
92
- const lighthouseArgs = ['--yes', 'lighthouse', url, ...args];
138
+ const lighthouseArgs = [url, ...args];
139
+ const lighthouseCliPath = resolveLighthouseCliPath();
93
140
 
94
141
  // Set CHROME_PATH for standalone mode (Lighthouse uses chrome-launcher which reads it)
95
142
  const env = { ...process.env };
@@ -100,7 +147,7 @@ export async function auditStr(cdp, sid, categories, device, reportPath) {
100
147
 
101
148
  let jsonOutput;
102
149
  try {
103
- jsonOutput = execFileSync('npx', lighthouseArgs, {
150
+ jsonOutput = execFileSync(process.execPath, [lighthouseCliPath, ...lighthouseArgs], {
104
151
  encoding: 'utf8',
105
152
  timeout: 120000,
106
153
  stdio: ['pipe', 'pipe', 'pipe'],
@@ -110,9 +157,6 @@ export async function auditStr(cdp, sid, categories, device, reportPath) {
110
157
  });
111
158
  } catch (e) {
112
159
  const stderr = e.stderr?.toString().trim() || '';
113
- if (e.code === 'ENOENT' || stderr.includes('not found') || stderr.includes('ENOENT')) {
114
- throw new Error('lighthouse not found. Install: npm i -g lighthouse');
115
- }
116
160
  if (stderr.includes('No Chrome installations found')) {
117
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.');
118
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'], RW),
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 || '', p.device || '', p.reportPath || ''].filter(Boolean) };
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');