chromex-mcp 1.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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +411 -0
  3. package/package.json +50 -0
  4. package/plugins/chromex/skills/chromex/scripts/chromex.mjs +343 -0
  5. package/plugins/chromex/skills/chromex/scripts/lib/browser.mjs +66 -0
  6. package/plugins/chromex/skills/chromex/scripts/lib/client.mjs +98 -0
  7. package/plugins/chromex/skills/chromex/scripts/lib/commands/console.mjs +37 -0
  8. package/plugins/chromex/skills/chromex/scripts/lib/commands/cookies.mjs +77 -0
  9. package/plugins/chromex/skills/chromex/scripts/lib/commands/coverage.mjs +95 -0
  10. package/plugins/chromex/skills/chromex/scripts/lib/commands/cpu.mjs +14 -0
  11. package/plugins/chromex/skills/chromex/scripts/lib/commands/dialog.mjs +38 -0
  12. package/plugins/chromex/skills/chromex/scripts/lib/commands/domsnapshot.mjs +84 -0
  13. package/plugins/chromex/skills/chromex/scripts/lib/commands/download.mjs +25 -0
  14. package/plugins/chromex/skills/chromex/scripts/lib/commands/drag.mjs +71 -0
  15. package/plugins/chromex/skills/chromex/scripts/lib/commands/emulate.mjs +44 -0
  16. package/plugins/chromex/skills/chromex/scripts/lib/commands/evaluate.mjs +31 -0
  17. package/plugins/chromex/skills/chromex/scripts/lib/commands/form.mjs +163 -0
  18. package/plugins/chromex/skills/chromex/scripts/lib/commands/geo.mjs +37 -0
  19. package/plugins/chromex/skills/chromex/scripts/lib/commands/har.mjs +101 -0
  20. package/plugins/chromex/skills/chromex/scripts/lib/commands/heap.mjs +24 -0
  21. package/plugins/chromex/skills/chromex/scripts/lib/commands/highlight.mjs +36 -0
  22. package/plugins/chromex/skills/chromex/scripts/lib/commands/html.mjs +10 -0
  23. package/plugins/chromex/skills/chromex/scripts/lib/commands/inject.mjs +39 -0
  24. package/plugins/chromex/skills/chromex/scripts/lib/commands/interact.mjs +88 -0
  25. package/plugins/chromex/skills/chromex/scripts/lib/commands/intercept.mjs +99 -0
  26. package/plugins/chromex/skills/chromex/scripts/lib/commands/navigate.mjs +45 -0
  27. package/plugins/chromex/skills/chromex/scripts/lib/commands/network.mjs +13 -0
  28. package/plugins/chromex/skills/chromex/scripts/lib/commands/pdf.mjs +16 -0
  29. package/plugins/chromex/skills/chromex/scripts/lib/commands/perf.mjs +98 -0
  30. package/plugins/chromex/skills/chromex/scripts/lib/commands/refs.mjs +67 -0
  31. package/plugins/chromex/skills/chromex/scripts/lib/commands/screenshot.mjs +54 -0
  32. package/plugins/chromex/skills/chromex/scripts/lib/commands/scroll.mjs +44 -0
  33. package/plugins/chromex/skills/chromex/scripts/lib/commands/snapshot.mjs +100 -0
  34. package/plugins/chromex/skills/chromex/scripts/lib/commands/storage.mjs +47 -0
  35. package/plugins/chromex/skills/chromex/scripts/lib/commands/tab.mjs +31 -0
  36. package/plugins/chromex/skills/chromex/scripts/lib/commands/throttle.mjs +38 -0
  37. package/plugins/chromex/skills/chromex/scripts/lib/commands/touch.mjs +62 -0
  38. package/plugins/chromex/skills/chromex/scripts/lib/commands/trace.mjs +51 -0
  39. package/plugins/chromex/skills/chromex/scripts/lib/commands/upload.mjs +43 -0
  40. package/plugins/chromex/skills/chromex/scripts/lib/commands/wait.mjs +84 -0
  41. package/plugins/chromex/skills/chromex/scripts/lib/commands/webauthn.mjs +47 -0
  42. package/plugins/chromex/skills/chromex/scripts/lib/config.mjs +100 -0
  43. package/plugins/chromex/skills/chromex/scripts/lib/daemon.mjs +368 -0
  44. package/plugins/chromex/skills/chromex/scripts/lib/ipc.mjs +178 -0
  45. package/plugins/chromex/skills/chromex/scripts/lib/launcher.mjs +111 -0
  46. package/plugins/chromex/skills/chromex/scripts/lib/security.mjs +48 -0
  47. package/plugins/chromex/skills/chromex/scripts/lib/utils.mjs +47 -0
  48. package/plugins/chromex/skills/chromex/scripts/mcp-server.mjs +726 -0
@@ -0,0 +1,111 @@
1
+ // Browser launcher -- abre Chrome/Brave/Edge com remote debugging habilitado
2
+ import { existsSync } from 'fs';
3
+ import { spawn } from 'child_process';
4
+ import { resolve } from 'path';
5
+ import { homedir } from 'os';
6
+ import { sleep } from './utils.mjs';
7
+ import { getWsUrl } from './browser.mjs';
8
+
9
+ const BROWSER_PATHS = {
10
+ darwin: {
11
+ chrome: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
12
+ brave: '/Applications/Brave Browser.app/Contents/MacOS/Brave Browser',
13
+ edge: '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
14
+ chromium: '/Applications/Chromium.app/Contents/MacOS/Chromium',
15
+ 'chrome-canary': '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
16
+ vivaldi: '/Applications/Vivaldi.app/Contents/MacOS/Vivaldi',
17
+ },
18
+ linux: {
19
+ chrome: 'google-chrome',
20
+ brave: 'brave-browser',
21
+ edge: 'microsoft-edge',
22
+ chromium: 'chromium-browser',
23
+ vivaldi: 'vivaldi',
24
+ },
25
+ };
26
+
27
+ const PROFILES_DIR = resolve(homedir(), '.chromex/profiles');
28
+
29
+ export async function launchBrowser(options = {}) {
30
+ const platform = process.platform === 'darwin' ? 'darwin' : 'linux';
31
+ const paths = BROWSER_PATHS[platform];
32
+ if (!paths) throw new Error(`Unsupported platform: ${process.platform}`);
33
+
34
+ // Encontrar browser
35
+ let browserPath;
36
+ if (options.browser) {
37
+ browserPath = paths[options.browser.toLowerCase()];
38
+ if (!browserPath) {
39
+ throw new Error(`Unknown browser: ${options.browser}. Available: ${Object.keys(paths).join(', ')}`);
40
+ }
41
+ if (platform === 'darwin' && !existsSync(browserPath)) {
42
+ throw new Error(`Browser not found: ${browserPath}`);
43
+ }
44
+ } else {
45
+ // Auto-detect: tentar na ordem
46
+ for (const path of Object.values(paths)) {
47
+ if (platform === 'darwin' ? existsSync(path) : true) {
48
+ browserPath = path;
49
+ break;
50
+ }
51
+ }
52
+ if (!browserPath) throw new Error('No supported browser found.');
53
+ }
54
+
55
+ const flags = [
56
+ '--remote-debugging-port=0', // Porta aleatória, Chrome escreve no DevToolsActivePort
57
+ '--no-first-run',
58
+ '--no-default-browser-check',
59
+ ];
60
+
61
+ if (options.incognito) flags.push('--incognito');
62
+
63
+ if (options.profile) {
64
+ const profilePath = resolve(PROFILES_DIR, options.profile);
65
+ flags.push(`--user-data-dir=${profilePath}`);
66
+ }
67
+
68
+ if (options.url) flags.push(options.url);
69
+
70
+ const child = spawn(browserPath, flags, {
71
+ detached: true,
72
+ stdio: 'ignore',
73
+ });
74
+ child.unref();
75
+
76
+ // Aguardar DevToolsActivePort aparecer
77
+ const maxWait = 10000;
78
+ const start = Date.now();
79
+ while (Date.now() - start < maxWait) {
80
+ try {
81
+ const wsUrl = getWsUrl();
82
+ if (wsUrl) {
83
+ const lines = [`Browser launched (PID: ${child.pid})`];
84
+ if (options.incognito) lines.push('Mode: incognito');
85
+ if (options.profile) lines.push(`Profile: ${options.profile}`);
86
+ lines.push(`Remote debugging active`);
87
+ return lines.join('\n');
88
+ }
89
+ } catch { /* aguardando */ }
90
+ await sleep(500);
91
+ }
92
+
93
+ return `Browser launched (PID: ${child.pid}) but DevToolsActivePort not found yet. Wait a moment and try "chromex list".`;
94
+ }
95
+
96
+ export async function incognitoContext(cdp, url) {
97
+ const { browserContextId } = await cdp.send('Target.createBrowserContext', {
98
+ disposeOnDetach: true,
99
+ });
100
+
101
+ const { targetId } = await cdp.send('Target.createTarget', {
102
+ url: url || 'about:blank',
103
+ browserContextId,
104
+ });
105
+
106
+ return {
107
+ targetId,
108
+ browserContextId,
109
+ message: `Incognito context created (targetId: ${targetId.slice(0, 8)}). Isolated cookies/storage.${url ? ` Navigated to ${url}.` : ''}`,
110
+ };
111
+ }
@@ -0,0 +1,48 @@
1
+ // Segurança: domain filtering, CDP method blocklist, audit log
2
+ import { appendFileSync } from 'fs';
3
+
4
+ export function checkDomain(url, config) {
5
+ if (!url) return null;
6
+ try {
7
+ const { hostname } = new URL(url);
8
+
9
+ if (config.allowedDomains?.length > 0) {
10
+ const allowed = config.allowedDomains.some(d =>
11
+ hostname === d || hostname.endsWith('.' + d)
12
+ );
13
+ if (!allowed) return `Domain "${hostname}" not in allowedDomains. Configure ~/.chromex/config.json`;
14
+ }
15
+
16
+ if (config.blockedDomains?.length > 0) {
17
+ const blocked = config.blockedDomains.find(d =>
18
+ hostname === d || hostname.endsWith('.' + d)
19
+ );
20
+ if (blocked) return `Domain "${hostname}" is blocked. Configure ~/.chromex/config.json`;
21
+ }
22
+
23
+ return null;
24
+ } catch {
25
+ return null;
26
+ }
27
+ }
28
+
29
+ export function isCdpMethodBlocked(method, config) {
30
+ if (!method) return false;
31
+ return config.blockedCdpMethods.some(m =>
32
+ m.toLowerCase() === method.toLowerCase()
33
+ );
34
+ }
35
+
36
+ export function audit(cmd, target, args, result, config) {
37
+ if (!config.auditLog) return;
38
+ const entry = {
39
+ ts: new Date().toISOString(),
40
+ cmd,
41
+ target: target?.slice(0, 12) || null,
42
+ args: (args || []).map(a => typeof a === 'string' && a.length > 120 ? a.slice(0, 120) + '...' : a).slice(0, 3),
43
+ ok: result?.ok ?? true,
44
+ };
45
+ try {
46
+ appendFileSync(config._auditLogPath, JSON.stringify(entry) + '\n');
47
+ } catch { /* falha no audit não deve quebrar o comando */ }
48
+ }
@@ -0,0 +1,47 @@
1
+ // Utilitários compartilhados
2
+ import { readdirSync } from 'fs';
3
+ import { resolve } from 'path';
4
+
5
+ const MIN_TARGET_PREFIX_LEN = 8;
6
+
7
+ export const sleep = (ms) => new Promise(r => setTimeout(r, ms));
8
+
9
+ export function resolvePrefix(prefix, candidates, noun = 'target', missingHint = '') {
10
+ const upper = prefix.toUpperCase();
11
+ const matches = candidates.filter(c => c.toUpperCase().startsWith(upper));
12
+ if (matches.length === 0) {
13
+ const hint = missingHint ? ` ${missingHint}` : '';
14
+ throw new Error(`No ${noun} matching prefix "${prefix}".${hint}`);
15
+ }
16
+ if (matches.length > 1) {
17
+ throw new Error(`Ambiguous prefix "${prefix}" — matches ${matches.length} ${noun}s. Use more characters.`);
18
+ }
19
+ return matches[0];
20
+ }
21
+
22
+ export function getDisplayPrefixLength(targetIds) {
23
+ if (targetIds.length === 0) return MIN_TARGET_PREFIX_LEN;
24
+ const maxLen = Math.max(...targetIds.map(id => id.length));
25
+ for (let len = MIN_TARGET_PREFIX_LEN; len <= maxLen; len++) {
26
+ const prefixes = new Set(targetIds.map(id => id.slice(0, len).toUpperCase()));
27
+ if (prefixes.size === targetIds.length) return len;
28
+ }
29
+ return maxLen;
30
+ }
31
+
32
+ export function listDaemonSockets(socketDir) {
33
+ try {
34
+ return readdirSync(socketDir)
35
+ .filter(f => f.endsWith('.sock') && f !== '.token')
36
+ .map(f => ({
37
+ targetId: f.slice(0, -5),
38
+ socketPath: resolve(socketDir, f),
39
+ }));
40
+ } catch {
41
+ return [];
42
+ }
43
+ }
44
+
45
+ export function sockPath(socketDir, targetId) {
46
+ return resolve(socketDir, `${targetId}.sock`);
47
+ }