@dynamicu/chromedebug-mcp 2.7.6 → 2.7.7

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
@@ -126,17 +126,35 @@ Add to `~/.claude/config.json`:
126
126
  </details>
127
127
 
128
128
  <details>
129
- <summary><strong>PRO Installation</strong></summary>
129
+ <summary><strong>Troubleshooting</strong></summary>
130
130
 
131
- After purchasing, run:
131
+ ### Running the Server Manually
132
+
133
+ If the MCP server isn't starting automatically, you can run it directly:
132
134
 
133
135
  ```bash
134
- chromedebug-mcp install-pro \
135
- --license YOUR_LICENSE_KEY \
136
- --extension-path ~/Downloads/chromedebug-extension-pro.zip
136
+ # Start the MCP server
137
+ chromedebug-mcp
138
+
139
+ # With verbose logging (shows connection details)
140
+ chromedebug-mcp --verbose
141
+
142
+ # View all options
143
+ chromedebug-mcp --help
137
144
  ```
138
145
 
139
- [Watch PRO Installation Video](https://youtu.be/TBjj5FA8DRg)
146
+ ### Common Issues
147
+
148
+ **Server not connecting?**
149
+ 1. Ensure Chrome is closed before launching
150
+ 2. Run `chromedebug-mcp --verbose` to see detailed logs
151
+ 3. Check that port 3001 (or configured port) is available
152
+
153
+ **Extension not communicating?**
154
+ 1. Check the extension popup shows "Connected" status
155
+ 2. Reload the extension in `chrome://extensions/`
156
+ 3. Restart Chrome with a fresh profile
157
+
140
158
  </details>
141
159
 
142
160
  ---
@@ -154,8 +172,6 @@ chromedebug-mcp install-pro \
154
172
  - 📺 [Demo Video](https://youtu.be/2Y9nIsvEjks)
155
173
  - 🌐 [Chrome Extension](https://chromewebstore.google.com/detail/chromedebug-mcp-assistant/lemgbmdnephoaniipapgeciebfeakffn)
156
174
  - 💎 [Get PRO](https://chromedebug.com/checkout/buy/996773cb-682b-430f-b9e3-9ce2130bd967)
157
- - 🐛 [Report Issues](https://github.com/dynamicupgrade/ChromeDebug/issues)
158
- - 💬 [Discussions](https://github.com/dynamicupgrade/ChromeDebug/discussions)
159
175
 
160
176
  ---
161
177
 
@@ -10,6 +10,7 @@ importScripts('upload-manager.js');
10
10
  importScripts('chrome-session-manager.js');
11
11
  importScripts('console-interception-library.js'); // Shared console interception library
12
12
  importScripts('browser-recording-manager.js');
13
+ importScripts('plugin-registry.js'); // Plugin system for extensible site-specific features
13
14
 
14
15
  const CONFIG_PORTS = CHROMEDEBUG_CONFIG.ports;
15
16
  const DISCOVERY_TIMEOUT = CHROMEDEBUG_CONFIG.discoveryTimeout;
@@ -769,6 +770,7 @@ let logStreamer = null;
769
770
  let logBuffer = null;
770
771
  let browserRecordingManager = null;
771
772
  let serverMode = 'unknown'; // 'server', 'browser-only', or 'unknown'
773
+ let pluginRegistry = null; // Plugin system registry for site-specific features
772
774
 
773
775
  // Error handling state and configuration
774
776
  // IMPORTANT: These must be declared before initializeServices()
@@ -891,6 +893,16 @@ async function initializeServices() {
891
893
  browserRecordingManager = null;
892
894
  }
893
895
 
896
+ // Initialize plugin registry for site-specific features
897
+ try {
898
+ pluginRegistry = new PluginRegistry();
899
+ await pluginRegistry.initialize();
900
+ console.log('[Background] Plugin registry initialized with', pluginRegistry.getAllPlugins().length, 'plugins');
901
+ } catch (pluginErr) {
902
+ console.error('[Background] Failed to initialize plugin registry:', pluginErr);
903
+ pluginRegistry = null;
904
+ }
905
+
894
906
  // Initialize session manager with recovery
895
907
  try {
896
908
  if (!sessionManager) {
@@ -1390,6 +1402,214 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
1390
1402
  });
1391
1403
  return true; // Async response
1392
1404
 
1405
+ // ============================================================
1406
+ // Plugin System Message Handlers
1407
+ // ============================================================
1408
+
1409
+ case 'GET_PLUGINS_FOR_URL':
1410
+ // Get plugins that match the current page URL
1411
+ if (pluginRegistry && message.url) {
1412
+ const matchingPlugins = pluginRegistry.getPluginsForUrl(message.url);
1413
+ sendResponse({
1414
+ success: true,
1415
+ plugins: matchingPlugins.map(p => ({
1416
+ pluginId: p.pluginId,
1417
+ name: p.manifest.name,
1418
+ version: p.manifest.version,
1419
+ menuItems: p.manifest.menuItems || [],
1420
+ site: p.site
1421
+ }))
1422
+ });
1423
+ } else {
1424
+ sendResponse({ success: false, error: 'Plugin registry not initialized or URL missing' });
1425
+ }
1426
+ break;
1427
+
1428
+ case 'REGISTER_PLUGIN':
1429
+ // Register a new plugin from its manifest
1430
+ if (pluginRegistry && message.manifest) {
1431
+ pluginRegistry.registerPlugin(message.manifest).then(result => {
1432
+ sendResponse(result);
1433
+ }).catch(error => {
1434
+ console.error('[Background] Failed to register plugin:', error);
1435
+ sendResponse({ success: false, error: error.message });
1436
+ });
1437
+ return true; // Async response
1438
+ } else {
1439
+ sendResponse({ success: false, error: 'Plugin registry not initialized or manifest missing' });
1440
+ }
1441
+ break;
1442
+
1443
+ case 'UNREGISTER_PLUGIN':
1444
+ // Unregister a plugin by ID
1445
+ if (pluginRegistry && message.pluginId) {
1446
+ pluginRegistry.unregisterPlugin(message.pluginId).then(result => {
1447
+ sendResponse(result);
1448
+ }).catch(error => {
1449
+ console.error('[Background] Failed to unregister plugin:', error);
1450
+ sendResponse({ success: false, error: error.message });
1451
+ });
1452
+ return true; // Async response
1453
+ } else {
1454
+ sendResponse({ success: false, error: 'Plugin registry not initialized or pluginId missing' });
1455
+ }
1456
+ break;
1457
+
1458
+ case 'GET_ALL_PLUGINS':
1459
+ case 'GET_REGISTERED_PLUGINS':
1460
+ // Get all registered plugins
1461
+ if (pluginRegistry) {
1462
+ const allPlugins = pluginRegistry.getAllPlugins();
1463
+ sendResponse({
1464
+ success: true,
1465
+ plugins: allPlugins.map(p => ({
1466
+ pluginId: p.pluginId,
1467
+ name: p.name,
1468
+ version: p.version,
1469
+ status: p.status || 'enabled',
1470
+ sitePatterns: p.sitePatterns,
1471
+ menuItems: p.menuItems,
1472
+ serverLoaded: p.serverLoaded,
1473
+ registeredAt: p.registeredAt
1474
+ })),
1475
+ registryState: pluginRegistry.exportState()
1476
+ });
1477
+ } else {
1478
+ sendResponse({ success: false, error: 'Plugin registry not initialized' });
1479
+ }
1480
+ break;
1481
+
1482
+ case 'ENABLE_PLUGIN':
1483
+ // Enable a plugin by ID
1484
+ if (pluginRegistry && message.pluginId) {
1485
+ pluginRegistry.enablePlugin(message.pluginId).then(result => {
1486
+ sendResponse(result);
1487
+ }).catch(error => {
1488
+ console.error('[Background] Failed to enable plugin:', error);
1489
+ sendResponse({ success: false, error: error.message });
1490
+ });
1491
+ return true; // Async response
1492
+ } else {
1493
+ sendResponse({ success: false, error: 'Plugin registry not initialized or pluginId missing' });
1494
+ }
1495
+ break;
1496
+
1497
+ case 'DISABLE_PLUGIN':
1498
+ // Disable a plugin by ID
1499
+ if (pluginRegistry && message.pluginId) {
1500
+ pluginRegistry.disablePlugin(message.pluginId).then(result => {
1501
+ sendResponse(result);
1502
+ }).catch(error => {
1503
+ console.error('[Background] Failed to disable plugin:', error);
1504
+ sendResponse({ success: false, error: error.message });
1505
+ });
1506
+ return true; // Async response
1507
+ } else {
1508
+ sendResponse({ success: false, error: 'Plugin registry not initialized or pluginId missing' });
1509
+ }
1510
+ break;
1511
+
1512
+ case 'SYNC_PLUGINS_WITH_SERVER':
1513
+ // Sync plugins with Chrome Debug MCP server
1514
+ if (pluginRegistry) {
1515
+ const port = message.port || CONFIG_PORTS[0];
1516
+ pluginRegistry.syncWithServer(port).then(result => {
1517
+ sendResponse(result);
1518
+ }).catch(error => {
1519
+ console.error('[Background] Failed to sync plugins with server:', error);
1520
+ sendResponse({ success: false, error: error.message });
1521
+ });
1522
+ return true; // Async response
1523
+ } else {
1524
+ sendResponse({ success: false, error: 'Plugin registry not initialized' });
1525
+ }
1526
+ break;
1527
+
1528
+ case 'EXECUTE_PLUGIN_ACTION':
1529
+ // Execute a plugin menu action (routes to server)
1530
+ if (message.pluginId && message.actionId) {
1531
+ // Find the active server port and route the action
1532
+ discoverActiveServerPort().then(async (serverPort) => {
1533
+ if (!serverPort) {
1534
+ sendResponse({ success: false, error: 'Chrome Debug server not available' });
1535
+ return;
1536
+ }
1537
+
1538
+ try {
1539
+ const response = await fetch(`http://localhost:${serverPort}/chromedebug/plugins/${message.pluginId}/execute`, {
1540
+ method: 'POST',
1541
+ headers: { 'Content-Type': 'application/json' },
1542
+ body: JSON.stringify({
1543
+ actionId: message.actionId,
1544
+ context: message.context || {},
1545
+ tabId: sender.tab?.id
1546
+ })
1547
+ });
1548
+
1549
+ const result = await response.json();
1550
+ sendResponse(result);
1551
+ } catch (error) {
1552
+ console.error('[Background] Failed to execute plugin action:', error);
1553
+ sendResponse({ success: false, error: error.message });
1554
+ }
1555
+ });
1556
+ return true; // Async response
1557
+ } else {
1558
+ sendResponse({ success: false, error: 'Missing pluginId or actionId' });
1559
+ }
1560
+ break;
1561
+
1562
+ case 'IMPORT_PLUGIN_FROM_FILE':
1563
+ // Import plugin manifest from a file (via popup)
1564
+ // Accepts either: manifest (object) or manifestJson (string)
1565
+ {
1566
+ let manifest = null;
1567
+ if (message.manifest && typeof message.manifest === 'object') {
1568
+ manifest = message.manifest;
1569
+ } else if (message.manifestJson) {
1570
+ try {
1571
+ manifest = JSON.parse(message.manifestJson);
1572
+ } catch (parseError) {
1573
+ sendResponse({ success: false, error: 'Invalid JSON: ' + parseError.message });
1574
+ break;
1575
+ }
1576
+ }
1577
+
1578
+ if (!manifest) {
1579
+ sendResponse({ success: false, error: 'No manifest provided' });
1580
+ break;
1581
+ }
1582
+
1583
+ if (pluginRegistry) {
1584
+ pluginRegistry.registerPlugin(manifest).then(result => {
1585
+ if (result.success) {
1586
+ console.log('[Background] Plugin imported:', result.pluginId);
1587
+ }
1588
+ sendResponse(result);
1589
+ }).catch(error => {
1590
+ sendResponse({ success: false, error: error.message });
1591
+ });
1592
+ return true; // Async response
1593
+ } else {
1594
+ sendResponse({ success: false, error: 'Plugin registry not initialized' });
1595
+ }
1596
+ }
1597
+ break;
1598
+
1599
+ case 'CLEAR_ALL_PLUGINS':
1600
+ // Clear all plugins (for testing/reset)
1601
+ if (pluginRegistry) {
1602
+ pluginRegistry.clearAll().then(() => {
1603
+ sendResponse({ success: true, message: 'All plugins cleared' });
1604
+ }).catch(error => {
1605
+ sendResponse({ success: false, error: error.message });
1606
+ });
1607
+ return true; // Async response
1608
+ } else {
1609
+ sendResponse({ success: false, error: 'Plugin registry not initialized' });
1610
+ }
1611
+ break;
1612
+
1393
1613
  default:
1394
1614
  // Don't warn or respond for messages that should be handled by other listener (v2.0.6)
1395
1615
  // This prevents intercepting workflow recording messages
@@ -240,6 +240,41 @@
240
240
  </div>
241
241
  </div>
242
242
 
243
+ <!-- Plugins Section -->
244
+ <div id="plugins-section" style="padding: 8px; border: 1px solid #ccc; background: #f9f9f9; border-radius: 4px; margin-bottom: 15px;">
245
+ <details id="plugins-details">
246
+ <summary style="cursor: pointer; font-size: 12px; font-weight: bold; color: #333; display: flex; align-items: center; gap: 8px;">
247
+ <span>🔌 Plugins</span>
248
+ <span id="plugins-count-badge" style="background: #e0e0e0; color: #666; font-size: 10px; padding: 2px 6px; border-radius: 10px; font-weight: normal;">0</span>
249
+ <span style="flex: 1;"></span>
250
+ <button id="sync-plugins-btn" style="font-size: 9px; padding: 2px 6px; background: #2196F3; color: white; border: none; border-radius: 3px; cursor: pointer;" title="Sync with server">🔄 Sync</button>
251
+ </summary>
252
+
253
+ <div style="margin-top: 8px;">
254
+ <!-- Import Plugin Button -->
255
+ <div style="display: flex; gap: 6px; margin-bottom: 8px;">
256
+ <button id="import-plugin-btn" style="flex: 1; padding: 6px; font-size: 10px; background: linear-gradient(135deg, #4285f4 0%, #34a853 100%); color: white; border: none; border-radius: 4px; cursor: pointer;">
257
+ 📥 Import Plugin
258
+ </button>
259
+ <input type="file" id="plugin-file-input" accept=".json" style="display: none;">
260
+ </div>
261
+
262
+ <!-- Plugins List -->
263
+ <div id="plugins-list" style="max-height: 200px; overflow-y: auto;">
264
+ <div id="no-plugins-message" style="text-align: center; padding: 12px; color: #888; font-size: 11px;">
265
+ No plugins installed. Import a plugin manifest to get started.
266
+ </div>
267
+ <div id="plugins-container"></div>
268
+ </div>
269
+
270
+ <!-- Sync Status -->
271
+ <div id="plugins-sync-status" style="font-size: 9px; color: #888; margin-top: 6px; text-align: center; display: none;">
272
+ Last synced: <span id="plugins-last-sync">Never</span>
273
+ </div>
274
+ </div>
275
+ </details>
276
+ </div>
277
+
243
278
  <!-- 2-Column Layout for Recordings -->
244
279
  <div style="display: grid; grid-template-columns: 50% 50%; gap: 6px; box-sizing: border-box; overflow: hidden; max-width: 100%; margin-bottom: 60px;">
245
280
 
@@ -2933,8 +2933,267 @@ document.addEventListener('DOMContentLoaded', () => {
2933
2933
  clearTimeout(screenshotQualityDebounceTimer);
2934
2934
  }
2935
2935
  });
2936
+
2937
+ // ========================================
2938
+ // Plugin Management UI Initialization
2939
+ // ========================================
2940
+ initializePluginUI();
2936
2941
  });
2937
2942
 
2943
+ // ========================================
2944
+ // Plugin Management Functions
2945
+ // ========================================
2946
+
2947
+ /**
2948
+ * Initialize plugin UI event handlers and load plugins
2949
+ */
2950
+ function initializePluginUI() {
2951
+ const importBtn = document.getElementById('import-plugin-btn');
2952
+ const fileInput = document.getElementById('plugin-file-input');
2953
+ const syncBtn = document.getElementById('sync-plugins-btn');
2954
+
2955
+ // Import button -> trigger file input
2956
+ if (importBtn && fileInput) {
2957
+ importBtn.addEventListener('click', () => {
2958
+ fileInput.click();
2959
+ });
2960
+
2961
+ // Handle file selection
2962
+ fileInput.addEventListener('change', async (e) => {
2963
+ const file = e.target.files[0];
2964
+ if (!file) return;
2965
+
2966
+ try {
2967
+ const text = await file.text();
2968
+ const manifest = JSON.parse(text);
2969
+
2970
+ // Validate required fields
2971
+ if (!manifest.pluginId || !manifest.name) {
2972
+ showToast('Invalid plugin manifest: missing pluginId or name', 3000, 'error');
2973
+ return;
2974
+ }
2975
+
2976
+ // Send to background for registration
2977
+ const response = await chrome.runtime.sendMessage({
2978
+ action: 'IMPORT_PLUGIN_FROM_FILE',
2979
+ manifest: manifest
2980
+ });
2981
+
2982
+ if (response && response.success) {
2983
+ showToast('Plugin "' + manifest.name + '" imported successfully!', 3000, 'success');
2984
+ loadPluginsList();
2985
+ } else {
2986
+ showToast(response?.error || 'Failed to import plugin', 3000, 'error');
2987
+ }
2988
+ } catch (err) {
2989
+ console.error('Error importing plugin:', err);
2990
+ showToast('Invalid JSON file', 3000, 'error');
2991
+ }
2992
+
2993
+ // Reset file input for next import
2994
+ fileInput.value = '';
2995
+ });
2996
+ }
2997
+
2998
+ // Sync button -> sync with server
2999
+ if (syncBtn) {
3000
+ syncBtn.addEventListener('click', async () => {
3001
+ syncBtn.disabled = true;
3002
+ syncBtn.textContent = '\u231B';
3003
+
3004
+ try {
3005
+ const response = await chrome.runtime.sendMessage({
3006
+ action: 'SYNC_PLUGINS_WITH_SERVER'
3007
+ });
3008
+
3009
+ if (response && response.success) {
3010
+ const syncStatus = document.getElementById('plugins-sync-status');
3011
+ const lastSync = document.getElementById('plugins-last-sync');
3012
+ if (syncStatus && lastSync) {
3013
+ syncStatus.style.display = 'block';
3014
+ lastSync.textContent = new Date().toLocaleTimeString();
3015
+ }
3016
+ showToast('Synced ' + (response.count || 0) + ' plugins', 2000, 'success');
3017
+ loadPluginsList();
3018
+ } else {
3019
+ showToast(response?.error || 'Sync failed', 3000, 'error');
3020
+ }
3021
+ } catch (err) {
3022
+ console.error('Error syncing plugins:', err);
3023
+ showToast('Failed to connect to server', 3000, 'error');
3024
+ }
3025
+
3026
+ syncBtn.disabled = false;
3027
+ syncBtn.textContent = '\uD83D\uDD04 Sync';
3028
+ });
3029
+ }
3030
+
3031
+ // Load plugins on startup
3032
+ loadPluginsList();
3033
+ }
3034
+
3035
+ /**
3036
+ * Load and display registered plugins
3037
+ */
3038
+ async function loadPluginsList() {
3039
+ const pluginsContainer = document.getElementById('plugins-container');
3040
+ const noPluginsMessage = document.getElementById('no-plugins-message');
3041
+ const pluginsCountBadge = document.getElementById('plugins-count-badge');
3042
+
3043
+ if (!pluginsContainer) return;
3044
+
3045
+ try {
3046
+ const response = await chrome.runtime.sendMessage({
3047
+ action: 'GET_REGISTERED_PLUGINS'
3048
+ });
3049
+
3050
+ const plugins = response?.plugins || [];
3051
+
3052
+ // Update count badge
3053
+ if (pluginsCountBadge) {
3054
+ pluginsCountBadge.textContent = plugins.length.toString();
3055
+ pluginsCountBadge.style.background = plugins.length > 0 ? '#4CAF50' : '#e0e0e0';
3056
+ pluginsCountBadge.style.color = plugins.length > 0 ? 'white' : '#666';
3057
+ }
3058
+
3059
+ // Show/hide no plugins message
3060
+ if (noPluginsMessage) {
3061
+ noPluginsMessage.style.display = plugins.length === 0 ? 'block' : 'none';
3062
+ }
3063
+
3064
+ // Clear container safely
3065
+ while (pluginsContainer.firstChild) {
3066
+ pluginsContainer.removeChild(pluginsContainer.firstChild);
3067
+ }
3068
+
3069
+ plugins.forEach(plugin => {
3070
+ const pluginEl = createPluginElement(plugin);
3071
+ pluginsContainer.appendChild(pluginEl);
3072
+ });
3073
+ } catch (err) {
3074
+ console.error('Error loading plugins:', err);
3075
+ }
3076
+ }
3077
+
3078
+ /**
3079
+ * Create DOM element for a plugin using safe DOM methods
3080
+ */
3081
+ function createPluginElement(plugin) {
3082
+ const el = document.createElement('div');
3083
+ el.style.cssText = 'padding: 8px; border: 1px solid #ddd; border-radius: 4px; margin-bottom: 6px; background: white;';
3084
+
3085
+ const isEnabled = plugin.status !== 'disabled';
3086
+ const statusColor = isEnabled ? '#4CAF50' : '#999';
3087
+
3088
+ // Main row container
3089
+ const mainRow = document.createElement('div');
3090
+ mainRow.style.cssText = 'display: flex; justify-content: space-between; align-items: center;';
3091
+
3092
+ // Left side: name and version
3093
+ const infoDiv = document.createElement('div');
3094
+
3095
+ const nameSpan = document.createElement('span');
3096
+ nameSpan.style.cssText = 'font-weight: bold; font-size: 11px;';
3097
+ nameSpan.textContent = plugin.name || plugin.pluginId;
3098
+
3099
+ const versionSpan = document.createElement('span');
3100
+ versionSpan.style.cssText = 'font-size: 9px; color: #888; margin-left: 4px;';
3101
+ versionSpan.textContent = 'v' + (plugin.version || '1.0.0');
3102
+
3103
+ infoDiv.appendChild(nameSpan);
3104
+ infoDiv.appendChild(versionSpan);
3105
+
3106
+ // Right side: status and buttons
3107
+ const buttonsDiv = document.createElement('div');
3108
+ buttonsDiv.style.cssText = 'display: flex; gap: 4px; align-items: center;';
3109
+
3110
+ // Status indicator
3111
+ const statusDot = document.createElement('span');
3112
+ statusDot.style.cssText = 'width: 8px; height: 8px; border-radius: 50%; background: ' + statusColor + ';';
3113
+
3114
+ // Toggle button
3115
+ const toggleBtn = document.createElement('button');
3116
+ toggleBtn.className = 'plugin-toggle-btn';
3117
+ toggleBtn.dataset.pluginId = plugin.pluginId;
3118
+ toggleBtn.dataset.enabled = isEnabled.toString();
3119
+ toggleBtn.style.cssText = 'font-size: 9px; padding: 2px 6px; background: ' + (isEnabled ? '#ff9800' : '#4CAF50') + '; color: white; border: none; border-radius: 3px; cursor: pointer;';
3120
+ toggleBtn.textContent = isEnabled ? 'Disable' : 'Enable';
3121
+
3122
+ // Remove button
3123
+ const removeBtn = document.createElement('button');
3124
+ removeBtn.className = 'plugin-remove-btn';
3125
+ removeBtn.dataset.pluginId = plugin.pluginId;
3126
+ removeBtn.style.cssText = 'font-size: 9px; padding: 2px 6px; background: #f44336; color: white; border: none; border-radius: 3px; cursor: pointer;';
3127
+ removeBtn.textContent = '\u2715';
3128
+
3129
+ buttonsDiv.appendChild(statusDot);
3130
+ buttonsDiv.appendChild(toggleBtn);
3131
+ buttonsDiv.appendChild(removeBtn);
3132
+
3133
+ mainRow.appendChild(infoDiv);
3134
+ mainRow.appendChild(buttonsDiv);
3135
+ el.appendChild(mainRow);
3136
+
3137
+ // Site patterns info (if any)
3138
+ if (plugin.sitePatterns && plugin.sitePatterns.length > 0) {
3139
+ const sitesDiv = document.createElement('div');
3140
+ sitesDiv.style.cssText = 'font-size: 9px; color: #666; margin-top: 4px;';
3141
+ const sitesText = plugin.sitePatterns.slice(0, 2).join(', ');
3142
+ sitesDiv.textContent = 'Sites: ' + sitesText + (plugin.sitePatterns.length > 2 ? '...' : '');
3143
+ el.appendChild(sitesDiv);
3144
+ }
3145
+
3146
+ // Toggle button handler
3147
+ toggleBtn.addEventListener('click', async () => {
3148
+ const pluginId = toggleBtn.dataset.pluginId;
3149
+ const currentlyEnabled = toggleBtn.dataset.enabled === 'true';
3150
+
3151
+ try {
3152
+ const response = await chrome.runtime.sendMessage({
3153
+ action: currentlyEnabled ? 'DISABLE_PLUGIN' : 'ENABLE_PLUGIN',
3154
+ pluginId: pluginId
3155
+ });
3156
+
3157
+ if (response && response.success) {
3158
+ loadPluginsList();
3159
+ showToast('Plugin ' + (currentlyEnabled ? 'disabled' : 'enabled'), 2000, 'success');
3160
+ } else {
3161
+ showToast(response?.error || 'Operation failed', 3000, 'error');
3162
+ }
3163
+ } catch (err) {
3164
+ console.error('Error toggling plugin:', err);
3165
+ }
3166
+ });
3167
+
3168
+ // Remove button handler
3169
+ removeBtn.addEventListener('click', async () => {
3170
+ const pluginId = removeBtn.dataset.pluginId;
3171
+ const pluginName = plugin.name || pluginId;
3172
+
3173
+ if (!confirm('Remove plugin "' + pluginName + '"?')) {
3174
+ return;
3175
+ }
3176
+
3177
+ try {
3178
+ const response = await chrome.runtime.sendMessage({
3179
+ action: 'UNREGISTER_PLUGIN',
3180
+ pluginId: pluginId
3181
+ });
3182
+
3183
+ if (response && response.success) {
3184
+ loadPluginsList();
3185
+ showToast('Plugin removed', 2000, 'success');
3186
+ } else {
3187
+ showToast(response?.error || 'Failed to remove plugin', 3000, 'error');
3188
+ }
3189
+ } catch (err) {
3190
+ console.error('Error removing plugin:', err);
3191
+ }
3192
+ });
3193
+
3194
+ return el;
3195
+ }
3196
+
2938
3197
  // Function to load and display workflow recordings
2939
3198
  async function loadWorkflowRecordings(autoShow = false) {
2940
3199
  const workflowRecordingsList = document.getElementById('workflowRecordingsList');
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamicu/chromedebug-mcp",
3
- "version": "2.7.6",
3
+ "version": "2.7.7",
4
4
  "description": "ChromeDebug MCP - MCP server that provides full control over a Chrome browser instance for debugging and automation with AI assistants like Claude Code",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -11,14 +11,7 @@
11
11
  "publishConfig": {
12
12
  "access": "public"
13
13
  },
14
- "repository": {
15
- "type": "git",
16
- "url": "git+https://github.com/dynamicupgrade/ChromeDebug.git"
17
- },
18
- "bugs": {
19
- "url": "https://github.com/dynamicupgrade/ChromeDebug/issues"
20
- },
21
- "homepage": "https://github.com/dynamicupgrade/ChromeDebug#readme",
14
+ "homepage": "https://chromedebug.com",
22
15
  "scripts": {
23
16
  "cleanup": "node scripts/cleanup-processes.js",
24
17
  "cleanup-force": "node scripts/cleanup-processes.js --force",
@@ -45,8 +45,8 @@ if (process.platform === 'win32' && !process.env.WSL_DISTRO_NAME) {
45
45
  }
46
46
  console.error('');
47
47
 
48
- console.error('For more information:');
49
- console.error(' https://github.com/dynamicupgrade/ChromeDebug#windows-setup');
48
+ console.error('For more information, see the npm package documentation:');
49
+ console.error(' https://www.npmjs.com/package/@dynamicu/chromedebug-mcp');
50
50
  console.error('');
51
51
 
52
52
  // Exit successfully
@@ -48,4 +48,4 @@ console.log(' Claude Code automatically starts both servers when it runs chrom
48
48
  console.log('💎 Want unlimited recordings?');
49
49
  console.log(' Upgrade to Pro: https://chromedebug.com/checkout/buy/996773cb-682b-430f-b9e3-9ce2130bd967\n');
50
50
 
51
- console.log('📚 Full documentation: https://github.com/dynamicupgrade/ChromeDebug#readme\n');
51
+ console.log('📚 Full documentation: https://www.npmjs.com/package/@dynamicu/chromedebug-mcp\n');
package/src/cli.js CHANGED
@@ -68,7 +68,7 @@ Windows users have two options:
68
68
  • Then run: chromedebug-mcp
69
69
  • Note: MCP stdio communication may fail due to cmd.exe limitations
70
70
 
71
- For details: https://github.com/dynamicupgrade/ChromeDebug#windows-setup
71
+ For details, see the npm package documentation at: https://www.npmjs.com/package/@dynamicu/chromedebug-mcp
72
72
  `);
73
73
  process.exit(1);
74
74
  }
@@ -101,7 +101,7 @@ Expected formats:
101
101
  Your key: ${maskLicenseKey(key)}
102
102
 
103
103
  Please check your purchase confirmation email for the correct license key.
104
- Need help? https://github.com/dynamicupgrade/ChromeDebug/issues
104
+ For help, see: https://www.npmjs.com/package/@dynamicu/chromedebug-mcp
105
105
  `);
106
106
  return false;
107
107
  }
@@ -134,7 +134,7 @@ Possible causes:
134
134
  2. Disk full - check: df -h ~
135
135
  3. Home directory not writable
136
136
 
137
- Need help? https://github.com/dynamicupgrade/ChromeDebug/issues
137
+ For help, see: https://www.npmjs.com/package/@dynamicu/chromedebug-mcp
138
138
  `);
139
139
  return false;
140
140
  }
@@ -197,7 +197,7 @@ Please check:
197
197
  2. File exists at the specified location
198
198
  3. You have read permission
199
199
 
200
- Need help? https://github.com/dynamicupgrade/ChromeDebug/issues
200
+ For help, see: https://www.npmjs.com/package/@dynamicu/chromedebug-mcp
201
201
  `);
202
202
  throw err;
203
203
  }
@@ -226,7 +226,7 @@ Possible causes:
226
226
 
227
227
  Error details: ${err.message}
228
228
 
229
- Need help? https://github.com/dynamicupgrade/ChromeDebug/issues
229
+ For help, see: https://www.npmjs.com/package/@dynamicu/chromedebug-mcp
230
230
  `);
231
231
  throw err;
232
232
  }
@@ -254,7 +254,7 @@ Expected manifest.json with ChromeDebug name.
254
254
 
255
255
  Please verify you downloaded the correct PRO extension ZIP.
256
256
 
257
- Need help? https://github.com/dynamicupgrade/ChromeDebug/issues
257
+ For help, see: https://www.npmjs.com/package/@dynamicu/chromedebug-mcp
258
258
  `);
259
259
  throw err;
260
260
  }
@@ -561,12 +561,12 @@ This will overwrite the existing installation.`;
561
561
  console.error(` (should output: ${EXTENSION_DIR})\n`);
562
562
 
563
563
  console.error('Installation log: ' + INSTALL_LOG);
564
- console.error('\nNeed help? https://github.com/dynamicupgrade/ChromeDebug/issues\n');
564
+ console.error('\nFor help, see: https://www.npmjs.com/package/@dynamicu/chromedebug-mcp\n');
565
565
  } catch (err) {
566
566
  await logInstall(`Installation failed: ${err.message}`, true);
567
567
  console.error('\n Installation failed\n');
568
568
  console.error('Check the installation log for details: ' + INSTALL_LOG);
569
- console.error('\nNeed help? https://github.com/dynamicupgrade/ChromeDebug/issues\n');
569
+ console.error('\nFor help, see: https://www.npmjs.com/package/@dynamicu/chromedebug-mcp\n');
570
570
  process.exit(1);
571
571
  }
572
572
  }
@@ -1,542 +0,0 @@
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
- }