@dynamicu/chromedebug-mcp 2.5.5 → 2.5.6

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.
@@ -0,0 +1,542 @@
1
+ /**
2
+ * ChromeDebug PRO License Installer
3
+ *
4
+ * Handles installation of PRO extension after purchase:
5
+ * - Validates license key format
6
+ * - Extracts PRO extension ZIP to ~/.chromedebug-pro/extension/
7
+ * - Saves license information to ~/.chromedebug-pro/license.json
8
+ * - Configures shell environment for CHROMEDEBUG_EXTENSION_PATH
9
+ */
10
+
11
+ import fs from 'fs/promises';
12
+ import fsSync from 'fs';
13
+ import path from 'path';
14
+ import os from 'os';
15
+ import { execSync } from 'child_process';
16
+ import AdmZip from 'adm-zip';
17
+
18
+ // Constants
19
+ const CHROMEDEBUG_DIR = path.join(os.homedir(), '.chromedebug-pro');
20
+ const EXTENSION_DIR = path.join(CHROMEDEBUG_DIR, 'extension');
21
+ const LICENSE_FILE = path.join(CHROMEDEBUG_DIR, 'license.json');
22
+ const INSTALL_LOG = path.join(CHROMEDEBUG_DIR, 'install.log');
23
+ const ENV_VAR_NAME = 'CHROMEDEBUG_EXTENSION_PATH';
24
+
25
+ // License key format: LICENSEID-HASH (e.g., ABC12345-0123456789ABCDEF)
26
+ const LICENSE_KEY_REGEX = /^[A-Z0-9]{8,}-[A-Z0-9]{16,}$/i;
27
+
28
+ /**
29
+ * Log installation activity
30
+ */
31
+ async function logInstall(message, isError = false) {
32
+ const timestamp = new Date().toISOString();
33
+ const logMessage = `[${timestamp}] ${isError ? 'ERROR: ' : ''}${message}\n`;
34
+
35
+ try {
36
+ await fs.appendFile(INSTALL_LOG, logMessage);
37
+ } catch (err) {
38
+ // Ignore log errors - don't let logging break installation
39
+ }
40
+
41
+ if (isError) {
42
+ console.error(message);
43
+ } else {
44
+ console.log(message);
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Validate command-line arguments
50
+ */
51
+ function validateArguments(args) {
52
+ const licenseIndex = args.indexOf('--license');
53
+ const pathIndex = args.indexOf('--extension-path');
54
+
55
+ if (licenseIndex === -1 || pathIndex === -1) {
56
+ console.error(`
57
+ ChromeDebug PRO License Installer
58
+
59
+ Usage:
60
+ chromedebug-mcp install-pro --license YOUR_LICENSE_KEY --extension-path /path/to/chromedebug-pro-extension.zip
61
+
62
+ Required Arguments:
63
+ --license Your PRO license key (received after purchase)
64
+ --extension-path Path to the PRO extension ZIP file
65
+
66
+ Example:
67
+ chromedebug-mcp install-pro \\
68
+ --license ABC12345-0123456789ABCDEF \\
69
+ --extension-path ~/Downloads/chromedebug-pro-extension.zip
70
+
71
+ Get PRO: https://chromedebug.com/pro
72
+ `);
73
+ return null;
74
+ }
75
+
76
+ const license = args[licenseIndex + 1];
77
+ const extensionPath = args[pathIndex + 1];
78
+
79
+ if (!license || !extensionPath) {
80
+ console.error('Error: Missing values for --license or --extension-path');
81
+ return null;
82
+ }
83
+
84
+ return { license, extensionPath };
85
+ }
86
+
87
+ /**
88
+ * Validate license key format
89
+ */
90
+ function validateLicenseKey(key) {
91
+ if (!LICENSE_KEY_REGEX.test(key)) {
92
+ console.error(`
93
+ Error: Invalid license key format
94
+
95
+ Expected format: LICENSEID-HASH
96
+ Example: ABC12345-0123456789ABCDEF
97
+
98
+ Your key: ${maskLicenseKey(key)}
99
+
100
+ Please check your purchase confirmation email for the correct license key.
101
+ Need help? https://github.com/dynamicupgrade/ChromeDebug/issues
102
+ `);
103
+ return false;
104
+ }
105
+ return true;
106
+ }
107
+
108
+ /**
109
+ * Mask license key for display (show first 6 and last 3 chars)
110
+ */
111
+ function maskLicenseKey(key) {
112
+ if (key.length <= 9) return '***';
113
+ return `${key.substring(0, 6)}...${key.substring(key.length - 3)}`;
114
+ }
115
+
116
+ /**
117
+ * Check pre-flight conditions
118
+ */
119
+ async function preflightChecks() {
120
+ // Check write permission to home directory
121
+ try {
122
+ const testFile = path.join(os.homedir(), '.chromedebug-test');
123
+ await fs.writeFile(testFile, 'test');
124
+ await fs.unlink(testFile);
125
+ } catch (err) {
126
+ console.error(`
127
+ Error: Cannot write to home directory
128
+
129
+ Possible causes:
130
+ 1. Permission denied - check: ls -ld ~
131
+ 2. Disk full - check: df -h ~
132
+ 3. Home directory not writable
133
+
134
+ Need help? https://github.com/dynamicupgrade/ChromeDebug/issues
135
+ `);
136
+ return false;
137
+ }
138
+
139
+ return true;
140
+ }
141
+
142
+ /**
143
+ * Check for existing installation
144
+ */
145
+ async function checkExistingInstallation() {
146
+ try {
147
+ const stats = await fs.stat(LICENSE_FILE);
148
+ if (stats.isFile()) {
149
+ const licenseData = JSON.parse(await fs.readFile(LICENSE_FILE, 'utf-8'));
150
+ return {
151
+ exists: true,
152
+ installed_at: licenseData.installed_at,
153
+ extension_version: licenseData.extension_version,
154
+ license_key: licenseData.license_key
155
+ };
156
+ }
157
+ } catch (err) {
158
+ // No existing installation
159
+ }
160
+ return { exists: false };
161
+ }
162
+
163
+ /**
164
+ * Prompt for confirmation (synchronous for CLI)
165
+ */
166
+ function promptConfirmation(message) {
167
+ // For now, we'll auto-continue with a warning
168
+ // In future, could add readline for interactive prompts
169
+ console.log(`\n${message}`);
170
+ console.log('Continuing with installation...\n');
171
+ return true;
172
+ }
173
+
174
+ /**
175
+ * Extract and validate ZIP file
176
+ */
177
+ async function extractExtension(zipPath, targetPath) {
178
+ // Resolve path (handle ~ and relative paths)
179
+ const resolvedZipPath = zipPath.startsWith('~')
180
+ ? path.join(os.homedir(), zipPath.slice(1))
181
+ : path.resolve(zipPath);
182
+
183
+ // Check if ZIP exists
184
+ try {
185
+ await fs.access(resolvedZipPath);
186
+ } catch (err) {
187
+ console.error(`
188
+ Error: Extension ZIP file not found
189
+
190
+ Path: ${resolvedZipPath}
191
+
192
+ Please check:
193
+ 1. File path is correct
194
+ 2. File exists at the specified location
195
+ 3. You have read permission
196
+
197
+ Need help? https://github.com/dynamicupgrade/ChromeDebug/issues
198
+ `);
199
+ throw err;
200
+ }
201
+
202
+ // Create target directory (clean if exists)
203
+ try {
204
+ await fs.rm(targetPath, { recursive: true, force: true });
205
+ } catch (err) {
206
+ // Ignore - directory might not exist
207
+ }
208
+
209
+ await fs.mkdir(targetPath, { recursive: true });
210
+
211
+ // Extract ZIP
212
+ try {
213
+ const zip = new AdmZip(resolvedZipPath);
214
+ zip.extractAllTo(targetPath, true);
215
+ } catch (err) {
216
+ console.error(`
217
+ Error: Failed to extract ZIP file
218
+
219
+ Possible causes:
220
+ 1. ZIP file is corrupted - try re-downloading
221
+ 2. Not a valid ZIP file
222
+ 3. Disk space full
223
+
224
+ Error details: ${err.message}
225
+
226
+ Need help? https://github.com/dynamicupgrade/ChromeDebug/issues
227
+ `);
228
+ throw err;
229
+ }
230
+
231
+ // Validate extracted contents
232
+ const manifestPath = path.join(targetPath, 'manifest.json');
233
+ try {
234
+ const manifestContent = await fs.readFile(manifestPath, 'utf-8');
235
+ const manifest = JSON.parse(manifestContent);
236
+
237
+ if (!manifest.name || !manifest.name.toLowerCase().includes('chromedebug')) {
238
+ throw new Error('Extension does not appear to be ChromeDebug');
239
+ }
240
+
241
+ return {
242
+ version: manifest.version,
243
+ name: manifest.name
244
+ };
245
+ } catch (err) {
246
+ console.error(`
247
+ Error: Invalid extension package
248
+
249
+ The extracted files do not appear to be a valid ChromeDebug extension.
250
+ Expected manifest.json with ChromeDebug name.
251
+
252
+ Please verify you downloaded the correct PRO extension ZIP.
253
+
254
+ Need help? https://github.com/dynamicupgrade/ChromeDebug/issues
255
+ `);
256
+ throw err;
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Save license information
262
+ */
263
+ async function saveLicenseInfo(license, extensionVersion) {
264
+ const licenseData = {
265
+ license_key: license,
266
+ installed_at: new Date().toISOString(),
267
+ extension_version: extensionVersion,
268
+ extension_path: EXTENSION_DIR
269
+ };
270
+
271
+ await fs.writeFile(LICENSE_FILE, JSON.stringify(licenseData, null, 2), 'utf-8');
272
+
273
+ // Set restrictive permissions (owner read/write only)
274
+ await fs.chmod(LICENSE_FILE, 0o600);
275
+ }
276
+
277
+ /**
278
+ * Detect user's shell
279
+ */
280
+ function detectShell() {
281
+ try {
282
+ const shell = process.env.SHELL || '';
283
+
284
+ if (shell.includes('zsh')) return 'zsh';
285
+ if (shell.includes('bash')) return 'bash';
286
+ if (shell.includes('fish')) return 'fish';
287
+
288
+ // Default to bash
289
+ return 'bash';
290
+ } catch (err) {
291
+ return 'bash';
292
+ }
293
+ }
294
+
295
+ /**
296
+ * Get shell configuration file path
297
+ */
298
+ function getShellConfigFile(shell) {
299
+ const home = os.homedir();
300
+
301
+ switch (shell) {
302
+ case 'zsh':
303
+ return path.join(home, '.zshrc');
304
+ case 'bash':
305
+ // Prefer .bashrc on Linux, .bash_profile on macOS
306
+ if (process.platform === 'darwin') {
307
+ return path.join(home, '.bash_profile');
308
+ }
309
+ return path.join(home, '.bashrc');
310
+ case 'fish':
311
+ return path.join(home, '.config', 'fish', 'config.fish');
312
+ default:
313
+ return path.join(home, '.bashrc');
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Check if environment variable is already configured
319
+ */
320
+ async function checkIfAlreadyConfigured(configFile) {
321
+ try {
322
+ const content = await fs.readFile(configFile, 'utf-8');
323
+ const envVarLine = `export ${ENV_VAR_NAME}=`;
324
+
325
+ if (content.includes(envVarLine)) {
326
+ // Check if it's the correct path
327
+ const regex = new RegExp(`export ${ENV_VAR_NAME}=["']?([^"'\\n]+)["']?`);
328
+ const match = content.match(regex);
329
+
330
+ if (match && match[1]) {
331
+ const currentPath = match[1].replace(/^~/, os.homedir());
332
+ const targetPath = EXTENSION_DIR;
333
+
334
+ if (path.resolve(currentPath) === path.resolve(targetPath)) {
335
+ return { configured: true, needsUpdate: false, currentPath };
336
+ } else {
337
+ return { configured: true, needsUpdate: true, currentPath };
338
+ }
339
+ }
340
+ }
341
+ } catch (err) {
342
+ // File doesn't exist or can't be read
343
+ }
344
+
345
+ return { configured: false, needsUpdate: false };
346
+ }
347
+
348
+ /**
349
+ * Update shell configuration
350
+ */
351
+ async function updateShellConfig(configFile, envVarValue) {
352
+ // Create backup
353
+ const backupFile = `${configFile}.chromedebug-backup`;
354
+ try {
355
+ await fs.copyFile(configFile, backupFile);
356
+ } catch (err) {
357
+ // Config file might not exist yet
358
+ }
359
+
360
+ // Read existing content
361
+ let content = '';
362
+ try {
363
+ content = await fs.readFile(configFile, 'utf-8');
364
+ } catch (err) {
365
+ // File doesn't exist, create new
366
+ if (configFile.includes('.bash')) {
367
+ content = '#!/bin/bash\n\n';
368
+ } else if (configFile.includes('.zsh')) {
369
+ content = '#!/bin/zsh\n\n';
370
+ }
371
+ }
372
+
373
+ // Check if already configured
374
+ const status = await checkIfAlreadyConfigured(configFile);
375
+
376
+ if (status.configured && !status.needsUpdate) {
377
+ return { updated: false, reason: 'already_configured' };
378
+ }
379
+
380
+ // Add or update environment variable
381
+ const envVarLine = `export ${ENV_VAR_NAME}="${envVarValue}"`;
382
+ const commentLine = '# ChromeDebug PRO extension path';
383
+
384
+ if (status.configured && status.needsUpdate) {
385
+ // Update existing line
386
+ const regex = new RegExp(`export ${ENV_VAR_NAME}=["']?[^"'\\n]+["']?`, 'g');
387
+ content = content.replace(regex, envVarLine);
388
+ } else {
389
+ // Add new lines
390
+ if (!content.endsWith('\n')) {
391
+ content += '\n';
392
+ }
393
+ content += `\n${commentLine}\n${envVarLine}\n`;
394
+ }
395
+
396
+ // Write updated content
397
+ await fs.writeFile(configFile, content, 'utf-8');
398
+
399
+ // Ensure proper permissions
400
+ await fs.chmod(configFile, 0o644);
401
+
402
+ return { updated: true, reason: status.needsUpdate ? 'updated' : 'added' };
403
+ }
404
+
405
+ /**
406
+ * Configure shell environment
407
+ */
408
+ async function configureShell() {
409
+ const shell = detectShell();
410
+ const configFile = getShellConfigFile(shell);
411
+
412
+ await logInstall(`Detected shell: ${shell}`);
413
+ await logInstall(`Config file: ${configFile}`);
414
+
415
+ // Ensure config directory exists (for fish)
416
+ const configDir = path.dirname(configFile);
417
+ await fs.mkdir(configDir, { recursive: true });
418
+
419
+ // Update configuration
420
+ const result = await updateShellConfig(configFile, EXTENSION_DIR);
421
+
422
+ return { shell, configFile, ...result };
423
+ }
424
+
425
+ /**
426
+ * Main installation flow
427
+ */
428
+ export async function installPro(args) {
429
+ console.log('\nChromeDebug PRO License Installer\n');
430
+
431
+ try {
432
+ // Create ChromeDebug directory
433
+ await fs.mkdir(CHROMEDEBUG_DIR, { recursive: true });
434
+
435
+ // Start logging
436
+ await logInstall('=== Installation Started ===');
437
+
438
+ // 1. Validate arguments
439
+ const parsedArgs = validateArguments(args);
440
+ if (!parsedArgs) {
441
+ return process.exit(1);
442
+ }
443
+
444
+ const { license, extensionPath } = parsedArgs;
445
+
446
+ // 2. Validate license key
447
+ console.log('Validating license key...');
448
+ if (!validateLicenseKey(license)) {
449
+ await logInstall(`Invalid license key: ${maskLicenseKey(license)}`, true);
450
+ return process.exit(1);
451
+ }
452
+ await logInstall(`License key validated: ${maskLicenseKey(license)}`);
453
+ console.log(` License key validated: ${maskLicenseKey(license)}\n`);
454
+
455
+ // 3. Pre-flight checks
456
+ if (!(await preflightChecks())) {
457
+ await logInstall('Pre-flight checks failed', true);
458
+ return process.exit(1);
459
+ }
460
+
461
+ // 4. Check for existing installation
462
+ const existing = await checkExistingInstallation();
463
+ if (existing.exists) {
464
+ const message = `
465
+ Existing PRO installation found:
466
+ - Installed: ${new Date(existing.installed_at).toLocaleDateString()}
467
+ - Version: ${existing.extension_version}
468
+ - License: ${maskLicenseKey(existing.license_key)}
469
+
470
+ This will overwrite the existing installation.`;
471
+
472
+ promptConfirmation(message);
473
+ await logInstall('Overwriting existing installation');
474
+ }
475
+
476
+ // 5. Extract extension
477
+ console.log('Extracting PRO extension...');
478
+ const extensionInfo = await extractExtension(extensionPath, EXTENSION_DIR);
479
+ await logInstall(`Extension extracted: ${extensionInfo.name} v${extensionInfo.version}`);
480
+ console.log(` PRO extension extracted to ${EXTENSION_DIR}`);
481
+ console.log(` Version: ${extensionInfo.version}\n`);
482
+
483
+ // Set extension directory permissions
484
+ await fs.chmod(EXTENSION_DIR, 0o755);
485
+
486
+ // 6. Save license information
487
+ console.log('Saving license information...');
488
+ await saveLicenseInfo(license, extensionInfo.version);
489
+ await logInstall(`License saved: ${maskLicenseKey(license)}`);
490
+ console.log(` License saved to ${LICENSE_FILE}\n`);
491
+
492
+ // 7. Configure shell environment
493
+ console.log('Configuring shell environment...');
494
+ let shellResult;
495
+ let shellConfigFailed = false;
496
+
497
+ try {
498
+ shellResult = await configureShell();
499
+ await logInstall(`Shell configured: ${shellResult.shell} (${shellResult.configFile})`);
500
+
501
+ if (shellResult.updated) {
502
+ console.log(` Shell configured: export ${ENV_VAR_NAME}="${EXTENSION_DIR}"`);
503
+ console.log(` Config file: ${shellResult.configFile}`);
504
+ console.log(` Backup saved: ${shellResult.configFile}.chromedebug-backup\n`);
505
+ } else {
506
+ console.log(` Shell already configured correctly\n`);
507
+ }
508
+ } catch (shellErr) {
509
+ shellConfigFailed = true;
510
+ await logInstall(`Shell configuration failed: ${shellErr.message}`, false);
511
+ console.log(`⚠ Could not automatically configure shell environment`);
512
+ console.log(` Error: ${shellErr.message}\n`);
513
+ }
514
+
515
+ // 8. Success!
516
+ await logInstall('=== Installation Completed Successfully ===');
517
+
518
+ console.log('');
519
+ console.log('PRO features are now active!');
520
+ console.log('\n');
521
+
522
+ console.log('Next steps:');
523
+ console.log(`1. Restart your terminal or run: source ${shellResult.configFile}`);
524
+ console.log('2. Close any running Chrome instances');
525
+ console.log('3. Launch ChromeDebug MCP: chromedebug-mcp');
526
+ console.log('4. The PRO extension will be loaded automatically\n');
527
+
528
+ console.log('Verify installation:');
529
+ console.log(` echo $${ENV_VAR_NAME}`);
530
+ console.log(` (should output: ${EXTENSION_DIR})\n`);
531
+
532
+ console.log('Installation log: ' + INSTALL_LOG);
533
+ console.log('\nNeed help? https://github.com/dynamicupgrade/ChromeDebug/issues\n');
534
+
535
+ } catch (err) {
536
+ await logInstall(`Installation failed: ${err.message}`, true);
537
+ console.error('\n Installation failed\n');
538
+ console.error('Check the installation log for details: ' + INSTALL_LOG);
539
+ console.error('\nNeed help? https://github.com/dynamicupgrade/ChromeDebug/issues\n');
540
+ process.exit(1);
541
+ }
542
+ }
@@ -1,73 +0,0 @@
1
- {
2
- "manifest_version": 3,
3
- "name": "ChromeDebug MCP Assistant v2.1.3",
4
- "version": "2.1.3",
5
- "description": "ChromeDebug MCP visual element selector with function tracing [Build: 2025-10-16-v2.1.3]",
6
- "permissions": [
7
- "activeTab",
8
- "scripting",
9
- "tabs",
10
- "storage",
11
- "tabCapture",
12
- "desktopCapture",
13
- "notifications",
14
- "offscreen"
15
- ],
16
- "host_permissions": [
17
- "https://*.cloudfunctions.net/*"
18
- ],
19
- "action": {
20
- "default_popup": "popup.html",
21
- "default_icon": {
22
- "16": "icon16.png",
23
- "48": "icon48.png",
24
- "128": "icon128.png"
25
- }
26
- },
27
- "options_page": "options.html",
28
- "content_scripts": [
29
- {
30
- "matches": [
31
- "<all_urls>"
32
- ],
33
- "exclude_matches": [
34
- "*://youtube.com/*",
35
- "*://www.youtube.com/*",
36
- "*://m.youtube.com/*",
37
- "*://google.com/*",
38
- "*://www.google.com/*",
39
- "*://gmail.com/*",
40
- "*://www.gmail.com/*",
41
- "*://facebook.com/*",
42
- "*://www.facebook.com/*",
43
- "*://twitter.com/*",
44
- "*://www.twitter.com/*",
45
- "*://x.com/*",
46
- "*://www.x.com/*",
47
- "*://instagram.com/*",
48
- "*://www.instagram.com/*"
49
- ],
50
- "js": [
51
- "pako.min.js",
52
- "web-vitals.iife.js",
53
- "pii-redactor.js",
54
- "data-buffer.js",
55
- "performance-monitor.js",
56
- "dom-tracker.js",
57
- "network-tracker.js",
58
- "function-tracker.js",
59
- "content.js"
60
- ],
61
- "css": ["content.css"],
62
- "run_at": "document_idle"
63
- }
64
- ],
65
- "background": {
66
- "service_worker": "background.js"
67
- },
68
- "icons": {
69
- "16": "icon16.png",
70
- "48": "icon48.png",
71
- "128": "icon128.png"
72
- }
73
- }