@dynamicu/chromedebug-mcp 2.2.0 → 2.2.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
@@ -21,8 +21,8 @@ npm install @dynamicu/chromedebug-mcp
21
21
 
22
22
  ```bash
23
23
  # Clone and install from source
24
- git clone https://github.com/dynamicupgrade/ChromePilot.git
25
- cd ChromePilot
24
+ git clone https://github.com/dynamicupgrade/ChromeDebug.git
25
+ cd ChromeDebug
26
26
  npm install
27
27
  ```
28
28
 
@@ -33,7 +33,7 @@ npm install
33
33
  To add Chrome Debug to your Claude desktop app:
34
34
 
35
35
  ```bash
36
- claude mcp add chromepilot -s user -- chrome-pilot
36
+ claude mcp add chromedebug -s user -- chromedebug-mcp
37
37
  ```
38
38
 
39
39
  ### Embedded HTTP Server
@@ -42,35 +42,27 @@ Chrome Debug automatically starts an embedded HTTP server for Chrome extension c
42
42
 
43
43
  | Endpoint | Method | Description | Body |
44
44
  |----------|--------|-------------|------|
45
- | `/chrome-pilot/pause` | POST | Pause execution | - |
46
- | `/chrome-pilot/resume` | POST | Resume execution | - |
47
- | `/chrome-pilot/step-over` | POST | Step over | - |
48
- | `/chrome-pilot/evaluate` | POST | Evaluate expression | `{ "expression": "window.user" }` |
49
- | `/chrome-pilot/scopes` | GET | Get scope variables | - |
50
- | `/chrome-pilot/set-breakpoint` | POST | Set breakpoint | `{ "url": "file.js", "lineNumber": 10 }` |
51
- | `/chrome-pilot/logs` | GET | Get recent console logs | - |
52
- | `/chrome-pilot/screenshot` | POST | Take screenshot | `{ "type": "jpeg", "fullPage": true, "path": "/path/to/save.jpg" }` |
53
- | `/chrome-pilot/dom-intent` | POST | Process natural language DOM commands | `{ "selector": "#element", "instruction": "make it blue" }` |
54
- | `/chrome-pilot/status` | GET | Check server and Chrome connection status | - |
55
- | `/chrome-pilot/recording` | POST | Upload screen recording with logs | FormData with video file, logs JSON, and duration |
56
- | `/chrome-pilot/recording/:id` | GET | Retrieve a recording by ID | - |
57
- | `/chrome-pilot/workflow-recording` | POST | Save workflow recording | `{ "sessionId": "...", "actions": [...], "logs": [...] }` |
58
- | `/chrome-pilot/workflow-recording/:id` | GET | Get workflow recording | - |
59
- | `/chrome-pilot/restore-point` | POST | Save restore point | `{ "workflowId": "...", "domSnapshot": {...}, ... }` |
60
- | `/chrome-pilot/restore-point/:id` | GET/DELETE | Get or delete restore point | - |
61
- | `/chrome-pilot/restore-points/:workflowId` | GET | List restore points for workflow | - |
45
+ | `/chromedebug/evaluate` | POST | Evaluate expression | `{ "expression": "window.user" }` |
46
+ | `/chromedebug/scopes` | GET | Get scope variables | - |
47
+ | `/chromedebug/logs` | GET | Get recent console logs | - |
48
+ | `/chromedebug/screenshot` | GET | Take screenshot | `{ "type": "jpeg", "fullPage": true, "path": "/path/to/save.jpg" }` |
49
+ | `/chromedebug/dom-intent` | POST | Process natural language DOM commands | `{ "selector": "#element", "instruction": "make it blue" }` |
50
+ | `/chromedebug/status` | GET | Check server and Chrome connection status | - |
51
+ | `/chromedebug/upload/:dataType` | POST | Upload screen recording with logs | FormData with video file, logs JSON, and duration |
52
+ | `/chromedebug/recording/:id` | GET | Retrieve a recording by ID | - |
53
+ | `/chromedebug/workflow-recording` | POST | Save workflow recording | `{ "sessionId": "...", "actions": [...], "logs": [...] }` |
54
+ | `/chromedebug/workflow-recording/:sessionId` | GET | Get workflow recording | - |
55
+ | `/chromedebug/restore-point` | POST | Save restore point | `{ "workflowId": "...", "domSnapshot": {...}, ... }` |
56
+ | `/chromedebug/restore-point/:id` | GET/DELETE | Get or delete restore point | - |
57
+ | `/chromedebug/restore-points/:workflowId` | GET | List restore points for workflow | - |
62
58
 
63
59
  ### MCP Tools
64
60
 
65
61
  When used as an MCP server, Chrome Debug exposes these tools:
66
62
 
67
63
  - `launch_chrome` - Launch a Chrome browser instance
68
- - `pause_execution` - Pause Chrome execution
69
- - `resume_execution` - Resume Chrome execution
70
- - `step_over` - Step over in debugger
71
64
  - `evaluate_expression` - Evaluate JavaScript expression
72
65
  - `get_scopes` - Get scope variables
73
- - `set_breakpoint` - Set a breakpoint
74
66
  - `get_logs` - Get console logs
75
67
  - `take_screenshot` - Take a screenshot
76
68
  - `get_page_content` - Get page content (text, HTML, and DOM structure)
@@ -98,8 +90,8 @@ npm start
98
90
  # 🔧 MCP tools will use this port for API calls
99
91
 
100
92
  # In another terminal, test the API (use the port shown in startup)
101
- curl -X POST http://localhost:3000/chrome-pilot/pause
102
- curl -X POST http://localhost:3000/chrome-pilot/evaluate \
93
+ curl -X POST http://localhost:3000/chromedebug/pause
94
+ curl -X POST http://localhost:3000/chromedebug/evaluate \
103
95
  -H "Content-Type: application/json" \
104
96
  -d '{"expression": "document.title"}'
105
97
  ```
@@ -131,12 +123,6 @@ Chrome Debug includes a Chrome extension that enables visual element selection f
131
123
  - Console logs can be included in the recording
132
124
  - Workflows can be replayed later for debugging
133
125
 
134
- 3. **Restore Points** (NEW)
135
- - During workflow recording, click "📍 Save Restore Point" to capture browser state
136
- - Captures DOM, form values, storage, cookies, and scroll position
137
- - Restore to any saved point to retry workflows from that state
138
- - Perfect for debugging multi-step forms or complex interactions
139
- - See [RESTORE_POINTS.md](RESTORE_POINTS.md) for details
140
126
 
141
127
  ## Development
142
128
 
@@ -154,12 +140,6 @@ Once Chrome Debug is installed as an MCP server, you can use these prompts with
154
140
  - "Get all the links on the current page"
155
141
  - "Take a screenshot of the current page"
156
142
 
157
- ### Debugging JavaScript
158
- - "Set a breakpoint on line 42 of app.js and tell me what variables are in scope when it hits"
159
- - "Pause execution and show me the current call stack"
160
- - "Step through the code and watch how the 'counter' variable changes"
161
- - "Evaluate localStorage.getItem('user') in the browser console"
162
-
163
143
  ### Web Scraping & Automation
164
144
  - "Navigate to https://news.ycombinator.com and get the titles of the top 5 stories"
165
145
  - "Fill out the search form with 'JavaScript' and submit it"
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "manifest_version": 3,
3
- "name": "ChromeDebug MCP Assistant v2.1.1",
4
- "version": "2.1.1",
5
- "description": "ChromeDebug MCP visual element selector with function tracing [Build: 2025-01-20-v2.1.1]",
3
+ "name": "ChromeDebug MCP Assistant v2.1.2",
4
+ "version": "2.1.2",
5
+ "description": "ChromeDebug MCP visual element selector with function tracing [Build: 2025-10-14-v2.1.2]",
6
6
  "permissions": [
7
7
  "activeTab",
8
8
  "scripting",
@@ -32,6 +32,7 @@
32
32
  border-radius: 4px;
33
33
  margin-bottom: 15px;
34
34
  font-size: 14px;
35
+ min-height: 40px;
35
36
  }
36
37
 
37
38
  .instructions {
@@ -129,7 +130,7 @@
129
130
  </style>
130
131
  </head>
131
132
  <body>
132
- <div style="position: absolute; top: 5px; right: 5px; font-size: 10px; color: #999; font-family: monospace;">v2.0.8</div>
133
+ <div style="position: absolute; top: 5px; right: 5px; font-size: 10px; color: #999; font-family: monospace;">v2.1.2</div>
133
134
  <h2>Chrome Debug Assistant</h2>
134
135
 
135
136
  <!-- License Section -->
@@ -288,12 +288,53 @@ async function checkServerStatus() {
288
288
 
289
289
  if (connected) {
290
290
  statusEl.className = 'server-status connected';
291
- statusTextEl.textContent = `Server connected (port ${connectedPort})`;
291
+ statusTextEl.innerHTML = `Server connected (port ${connectedPort})`;
292
292
  } else {
293
293
  statusEl.className = 'server-status disconnected';
294
- statusTextEl.textContent = 'Server not running';
294
+
295
+ // Enhanced disconnected state with install instructions
296
+ statusTextEl.innerHTML = `
297
+ <span style="display: block; margin-bottom: 8px;">Server not installed</span>
298
+ <div style="display: flex; gap: 5px; align-items: center;">
299
+ <button id="copyInstallCmd" style="padding: 4px 8px; font-size: 11px; background: #2196F3; color: white; border: none; border-radius: 3px; cursor: pointer; flex-shrink: 0;">
300
+ 📋 Copy Install Command
301
+ </button>
302
+ <a href="https://www.npmjs.com/package/@dynamicu/chromedebug-mcp" target="_blank" style="font-size: 11px; white-space: nowrap;">
303
+ View Installation Guide
304
+ </a>
305
+ </div>
306
+ `;
307
+
308
+ // Add click handler for copy button
309
+ setTimeout(() => {
310
+ const copyBtn = document.getElementById('copyInstallCmd');
311
+ if (copyBtn) {
312
+ copyBtn.addEventListener('click', async (e) => {
313
+ e.preventDefault();
314
+ const installCommand = 'npm install -g @dynamicu/chromedebug-mcp';
315
+
316
+ try {
317
+ await navigator.clipboard.writeText(installCommand);
318
+ const originalText = copyBtn.innerHTML;
319
+ copyBtn.innerHTML = '✅ Copied!';
320
+ copyBtn.style.background = '#4CAF50';
321
+
322
+ setTimeout(() => {
323
+ copyBtn.innerHTML = originalText;
324
+ copyBtn.style.background = '#2196F3';
325
+ }, 2000);
326
+ } catch (err) {
327
+ console.error('Failed to copy:', err);
328
+ copyBtn.innerHTML = '❌ Failed';
329
+ setTimeout(() => {
330
+ copyBtn.innerHTML = '📋 Copy Install Command';
331
+ }, 2000);
332
+ }
333
+ });
334
+ }
335
+ }, 0);
295
336
  }
296
-
337
+
297
338
  // Return connection status and port for other functions to use
298
339
  return { connected, connectedPort };
299
340
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamicu/chromedebug-mcp",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
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",
@@ -12,12 +12,12 @@
12
12
  },
13
13
  "repository": {
14
14
  "type": "git",
15
- "url": "git+https://github.com/dynamicupgrade/ChromePilot.git"
15
+ "url": "git+https://github.com/dynamicupgrade/ChromeDebug.git"
16
16
  },
17
17
  "bugs": {
18
- "url": "https://github.com/dynamicupgrade/ChromePilot/issues"
18
+ "url": "https://github.com/dynamicupgrade/ChromeDebug/issues"
19
19
  },
20
- "homepage": "https://github.com/dynamicupgrade/ChromePilot#readme",
20
+ "homepage": "https://github.com/dynamicupgrade/ChromeDebug#readme",
21
21
  "scripts": {
22
22
  "cleanup": "node scripts/cleanup-processes.js",
23
23
  "cleanup-force": "node scripts/cleanup-processes.js --force",
@@ -49,11 +49,11 @@
49
49
  "test:migration": "NODE_OPTIONS='--experimental-vm-modules' jest tests/dual-id-migration.test.js",
50
50
  "test:regression": "NODE_OPTIONS='--experimental-vm-modules' jest tests/recording-regression.test.js",
51
51
  "test:recording-all": "NODE_OPTIONS='--experimental-vm-modules' jest tests/recording-system-comprehensive.test.js tests/chrome-extension-recording.test.js tests/dual-id-migration.test.js tests/recording-regression.test.js tests/dual-recording-id.test.js",
52
- "run-migration": "node migrate-dual-ids.js",
52
+ "run-migration": "node scripts/migrate-dual-ids.js",
53
53
  "test:mcp": "NODE_OPTIONS='--experimental-vm-modules' jest tests/mcp-server.test.js",
54
54
  "tbv:microtest": "cd chrome-extension && npm run tbv:microtest",
55
- "build:free": "webpack --config webpack.config.free.cjs",
56
- "build:pro": "webpack --config webpack.config.pro.cjs",
55
+ "build:free": "webpack --config scripts/webpack.config.free.cjs",
56
+ "build:pro": "webpack --config scripts/webpack.config.pro.cjs",
57
57
  "build:both": "npm run build:free && npm run build:pro"
58
58
  },
59
59
  "keywords": [
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env node
2
+ // Migration script to convert from dual ID system to base ID system
3
+ // This script updates existing recordings with prefixed IDs to use base IDs
4
+
5
+ import Database from 'better-sqlite3';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+ const DB_PATH = path.join(__dirname, 'data/chrome-pilot.db');
11
+
12
+ console.log('Chrome Debug ID Migration Script');
13
+ console.log('================================');
14
+ console.log(`Database path: ${DB_PATH}`);
15
+
16
+ try {
17
+ const db = new Database(DB_PATH);
18
+
19
+ // Disable foreign key constraints during migration
20
+ db.pragma('foreign_keys = OFF');
21
+ console.log('Foreign key constraints temporarily disabled');
22
+
23
+ console.log('\n1. Analyzing current database state...');
24
+
25
+ // Get all recordings with prefixed IDs
26
+ const prefixedRecordings = db.prepare(`
27
+ SELECT id, session_id, type, timestamp, total_frames
28
+ FROM recordings
29
+ WHERE id LIKE 'frame_session_%'
30
+ `).all();
31
+
32
+ console.log(`Found ${prefixedRecordings.length} recordings with prefixed IDs`);
33
+
34
+ if (prefixedRecordings.length === 0) {
35
+ console.log('No prefixed recordings found. Migration not needed.');
36
+ process.exit(0);
37
+ }
38
+
39
+ // Show what will be migrated
40
+ console.log('\nRecordings to migrate:');
41
+ for (const record of prefixedRecordings) {
42
+ const baseId = record.id.replace('frame_session_', '');
43
+ console.log(` ${record.id} -> ${baseId} (${record.type}, ${record.total_frames} frames)`);
44
+ }
45
+
46
+ console.log('\n2. Starting migration transaction...');
47
+
48
+ const migration = db.transaction(() => {
49
+ let migratedCount = 0;
50
+
51
+ for (const record of prefixedRecordings) {
52
+ const oldId = record.id;
53
+ const newId = record.id.replace('frame_session_', '');
54
+
55
+ // Check if base ID already exists
56
+ const existingBase = db.prepare('SELECT id FROM recordings WHERE id = ?').get(newId);
57
+
58
+ if (existingBase) {
59
+ console.log(` WARNING: Base ID ${newId} already exists. Checking frame counts...`);
60
+
61
+ // Count frames for both records
62
+ const oldFrameCount = db.prepare('SELECT COUNT(*) as count FROM frames WHERE recording_id = ?').get(oldId)?.count || 0;
63
+ const newFrameCount = db.prepare('SELECT COUNT(*) as count FROM frames WHERE recording_id = ?').get(newId)?.count || 0;
64
+
65
+ console.log(` Old record frames: ${oldFrameCount}, New record frames: ${newFrameCount}`);
66
+
67
+ if (oldFrameCount > 0 && newFrameCount === 0) {
68
+ // Old record has frames, new doesn't - migrate frames and delete old
69
+ console.log(` Migrating frames from ${oldId} to ${newId}`);
70
+ db.prepare('UPDATE frames SET recording_id = ? WHERE recording_id = ?').run(newId, oldId);
71
+ db.prepare('UPDATE recordings SET total_frames = ? WHERE id = ?').run(oldFrameCount, newId);
72
+ db.prepare('DELETE FROM recordings WHERE id = ?').run(oldId);
73
+ migratedCount++;
74
+ } else if (oldFrameCount === 0) {
75
+ // Old record has no frames - just delete it
76
+ console.log(` Deleting empty old record ${oldId}`);
77
+ db.prepare('DELETE FROM recordings WHERE id = ?').run(oldId);
78
+ migratedCount++;
79
+ } else {
80
+ // Both have frames - manual resolution needed
81
+ console.log(` ERROR: Both records have frames. Manual resolution needed for ${oldId} vs ${newId}`);
82
+ }
83
+ } else {
84
+ // Simple case - just update the recording ID
85
+ console.log(` Migrating ${oldId} -> ${newId}`);
86
+
87
+ // Update the recording ID
88
+ db.prepare('UPDATE recordings SET id = ? WHERE id = ?').run(newId, oldId);
89
+
90
+ // Update related frames
91
+ db.prepare('UPDATE frames SET recording_id = ? WHERE recording_id = ?').run(newId, oldId);
92
+
93
+ // Update related screen interactions (if any)
94
+ db.prepare('UPDATE screen_interactions SET recording_id = ? WHERE recording_id = ?').run(newId, oldId);
95
+
96
+ migratedCount++;
97
+ }
98
+ }
99
+
100
+ return migratedCount;
101
+ });
102
+
103
+ const migratedCount = migration();
104
+
105
+ console.log(`\n3. Migration completed successfully!`);
106
+ console.log(` Migrated ${migratedCount} recordings`);
107
+
108
+ // Verify migration
109
+ console.log('\n4. Verifying migration...');
110
+ const remainingPrefixed = db.prepare(`
111
+ SELECT COUNT(*) as count FROM recordings WHERE id LIKE 'frame_session_%'
112
+ `).get().count;
113
+
114
+ console.log(`Remaining prefixed recordings: ${remainingPrefixed}`);
115
+
116
+ if (remainingPrefixed === 0) {
117
+ console.log('✅ Migration verification passed - no prefixed IDs remain');
118
+ } else {
119
+ console.log('⚠️ Migration verification failed - some prefixed IDs still exist');
120
+ }
121
+
122
+ // Re-enable foreign key constraints
123
+ db.pragma('foreign_keys = ON');
124
+ console.log('Foreign key constraints re-enabled');
125
+
126
+ console.log('\nMigration completed successfully!');
127
+
128
+
129
+ // Close database connection
130
+ db.close();
131
+
132
+ } catch (error) {
133
+ console.error('Migration failed:', error);
134
+ process.exit(1);
135
+ }
@@ -0,0 +1,74 @@
1
+ const path = require('path');
2
+ const CopyPlugin = require('copy-webpack-plugin');
3
+ const webpack = require('webpack');
4
+
5
+ module.exports = {
6
+ mode: 'production',
7
+ entry: {
8
+ background: './chrome-extension/background.js',
9
+ content: './chrome-extension/content.js',
10
+ popup: './chrome-extension/popup.js',
11
+ options: './chrome-extension/options.js'
12
+ },
13
+ output: {
14
+ path: path.resolve(__dirname, 'dist/free'),
15
+ filename: '[name].js',
16
+ clean: true
17
+ },
18
+ plugins: [
19
+ new webpack.DefinePlugin({
20
+ '__TIER__': JSON.stringify('free'),
21
+ '__FEATURES__': JSON.stringify({
22
+ enhancedCapture: false,
23
+ frameEditor: false,
24
+ advancedSettings: false,
25
+ functionTracing: false
26
+ })
27
+ }),
28
+ new CopyPlugin({
29
+ patterns: [
30
+ // Use free manifest
31
+ {
32
+ from: 'chrome-extension/manifest.free.json',
33
+ to: 'manifest.json'
34
+ },
35
+
36
+ // Copy HTML files
37
+ { from: 'chrome-extension/popup.html', to: 'popup.html' },
38
+ { from: 'chrome-extension/options.html', to: 'options.html' },
39
+ { from: 'chrome-extension/offscreen.html', to: 'offscreen.html' },
40
+
41
+ // Copy icons
42
+ { from: 'chrome-extension/*.png', to: '[name][ext]' },
43
+
44
+ // Copy CSS
45
+ { from: 'chrome-extension/*.css', to: '[name][ext]' },
46
+
47
+ // Copy third-party libraries (as-is, no bundling)
48
+ { from: 'chrome-extension/pako.min.js', to: 'pako.min.js' },
49
+ { from: 'chrome-extension/web-vitals.iife.js', to: 'web-vitals.iife.js' },
50
+
51
+ // Copy supporting modules (these are imported by bundled scripts)
52
+ { from: 'chrome-extension/pii-redactor.js', to: 'pii-redactor.js' },
53
+ { from: 'chrome-extension/data-buffer.js', to: 'data-buffer.js' },
54
+ { from: 'chrome-extension/performance-monitor.js', to: 'performance-monitor.js' },
55
+ { from: 'chrome-extension/dom-tracker.js', to: 'dom-tracker.js' },
56
+ { from: 'chrome-extension/network-tracker.js', to: 'network-tracker.js' },
57
+ { from: 'chrome-extension/upload-manager.js', to: 'upload-manager.js' },
58
+ { from: 'chrome-extension/extension-config.js', to: 'extension-config.js' },
59
+ { from: 'chrome-extension/logger.js', to: 'logger.js' },
60
+ { from: 'chrome-extension/license-helper.js', to: 'license-helper.js' },
61
+ { from: 'chrome-extension/firebase-client.js', to: 'firebase-client.js' },
62
+ { from: 'chrome-extension/firebase-config.js', to: 'firebase-config.js' },
63
+ { from: 'chrome-extension/frame-capture.js', to: 'frame-capture.js' },
64
+ { from: 'chrome-extension/chrome-session-manager.js', to: 'chrome-session-manager.js' },
65
+
66
+ // Copy README
67
+ { from: 'chrome-extension/README.md', to: 'README.md' }
68
+ ]
69
+ })
70
+ ],
71
+ optimization: {
72
+ minimize: false // Don't minimize for easier debugging
73
+ }
74
+ };
@@ -0,0 +1,80 @@
1
+ const path = require('path');
2
+ const CopyPlugin = require('copy-webpack-plugin');
3
+ const webpack = require('webpack');
4
+
5
+ module.exports = {
6
+ mode: 'production',
7
+ entry: {
8
+ background: './chrome-extension/background.js',
9
+ content: './chrome-extension/content.js',
10
+ popup: './chrome-extension/popup.js',
11
+ options: './chrome-extension/options.js',
12
+ 'pro/frame-editor': './chrome-extension/pro/frame-editor.js'
13
+ },
14
+ output: {
15
+ path: path.resolve(__dirname, 'dist/pro'),
16
+ filename: '[name].js',
17
+ clean: true
18
+ },
19
+ plugins: [
20
+ new webpack.DefinePlugin({
21
+ '__TIER__': JSON.stringify('pro'),
22
+ '__FEATURES__': JSON.stringify({
23
+ enhancedCapture: true,
24
+ frameEditor: true,
25
+ advancedSettings: true,
26
+ functionTracing: true
27
+ })
28
+ }),
29
+ new CopyPlugin({
30
+ patterns: [
31
+ // Use pro manifest
32
+ {
33
+ from: 'chrome-extension/manifest.pro.json',
34
+ to: 'manifest.json'
35
+ },
36
+
37
+ // Copy HTML files
38
+ { from: 'chrome-extension/popup.html', to: 'popup.html' },
39
+ { from: 'chrome-extension/options.html', to: 'options.html' },
40
+ { from: 'chrome-extension/offscreen.html', to: 'offscreen.html' },
41
+ { from: 'chrome-extension/pro/frame-editor.html', to: 'pro/frame-editor.html' },
42
+
43
+ // Copy icons
44
+ { from: 'chrome-extension/*.png', to: '[name][ext]' },
45
+
46
+ // Copy CSS
47
+ { from: 'chrome-extension/*.css', to: '[name][ext]' },
48
+
49
+ // Copy third-party libraries (as-is, no bundling)
50
+ { from: 'chrome-extension/pako.min.js', to: 'pako.min.js' },
51
+ { from: 'chrome-extension/web-vitals.iife.js', to: 'web-vitals.iife.js' },
52
+
53
+ // Copy supporting modules (these are imported by bundled scripts)
54
+ { from: 'chrome-extension/pii-redactor.js', to: 'pii-redactor.js' },
55
+ { from: 'chrome-extension/data-buffer.js', to: 'data-buffer.js' },
56
+ { from: 'chrome-extension/performance-monitor.js', to: 'performance-monitor.js' },
57
+ { from: 'chrome-extension/dom-tracker.js', to: 'dom-tracker.js' },
58
+ { from: 'chrome-extension/network-tracker.js', to: 'network-tracker.js' },
59
+ { from: 'chrome-extension/upload-manager.js', to: 'upload-manager.js' },
60
+ { from: 'chrome-extension/extension-config.js', to: 'extension-config.js' },
61
+ { from: 'chrome-extension/logger.js', to: 'logger.js' },
62
+ { from: 'chrome-extension/license-helper.js', to: 'license-helper.js' },
63
+ { from: 'chrome-extension/firebase-client.js', to: 'firebase-client.js' },
64
+ { from: 'chrome-extension/firebase-config.js', to: 'firebase-config.js' },
65
+ { from: 'chrome-extension/frame-capture.js', to: 'frame-capture.js' },
66
+ { from: 'chrome-extension/chrome-session-manager.js', to: 'chrome-session-manager.js' },
67
+
68
+ // Copy Pro features
69
+ { from: 'chrome-extension/pro/function-tracker.js', to: 'pro/function-tracker.js' },
70
+ { from: 'chrome-extension/pro/enhanced-capture.js', to: 'pro/enhanced-capture.js' },
71
+
72
+ // Copy README
73
+ { from: 'chrome-extension/README.md', to: 'README.md' }
74
+ ]
75
+ })
76
+ ],
77
+ optimization: {
78
+ minimize: false // Don't minimize for easier debugging
79
+ }
80
+ };
@@ -1,110 +1,49 @@
1
1
  // Port discovery utilities for Chrome Debug
2
2
  // This helps Chrome extension and MCP clients find the running HTTP server
3
3
  //
4
- // Note: Claude Code (MCP client) may create .chromedebug-port-[UUID] files
5
- // to track multiple sessions. Our code only uses .chromedebug-port (no UUID).
6
- // We don't clean these files as we didn't create them.
4
+ // Uses a single .chromedebug-port file that gets overwritten by each server startup.
5
+ // The last-started server's port is what gets discovered.
6
+ //
7
+ // Note: Session isolation is handled by unified-session-manager.js, which uses
8
+ // atomic file-based locks in /tmp/chromedebug-sessions/port-locks/ to ensure
9
+ // each Claude session gets a unique port. This port file is just a hint for
10
+ // external discovery and is not used for session isolation.
7
11
 
8
12
  import fs from 'fs';
9
13
  import path from 'path';
10
14
  import { fileURLToPath } from 'url';
11
- import crypto from 'crypto';
12
15
 
13
16
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
17
 
15
- // Session management for port discovery
16
- let currentSessionId = null;
17
-
18
- /**
19
- * Get or generate a session ID for port discovery
20
- */
21
- function getSessionId() {
22
- if (!currentSessionId) {
23
- currentSessionId = crypto.randomBytes(8).toString('hex');
24
- }
25
- return currentSessionId;
26
- }
27
-
28
- /**
29
- * Set a custom session ID for port discovery
30
- */
31
- export function setPortDiscoverySessionId(sessionId) {
32
- if (typeof sessionId === 'string' && sessionId.match(/^[a-zA-Z0-9_-]+$/)) {
33
- currentSessionId = sessionId;
34
- console.log(`Port discovery session ID set to: ${sessionId}`);
35
- } else {
36
- throw new Error('Session ID must be alphanumeric with dashes/underscores only');
37
- }
38
- }
39
-
40
18
  /**
41
- * Get the session-specific port file path
19
+ * Get the port file path
42
20
  */
43
21
  function getPortFilePath() {
44
- const sessionId = getSessionId();
45
- return path.join(__dirname, `../.chromedebug-port-${sessionId}`);
46
- }
47
-
48
- /**
49
- * Get the legacy port file path (for backward compatibility)
50
- */
51
- function getLegacyPortFilePath() {
52
22
  return path.join(__dirname, '../.chromedebug-port');
53
23
  }
54
24
 
55
25
  /**
56
- * Write the current port to a session-specific file for discovery
26
+ * Write the current port to file for discovery
27
+ * @param {number} port - The port number to write
57
28
  */
58
- export function writePortFile(port, options = {}) {
59
- const { sessionId, writeLegacy = true } = options;
60
-
61
- // Set session ID if provided
62
- if (sessionId) {
63
- setPortDiscoverySessionId(sessionId);
64
- }
65
-
29
+ export function writePortFile(port) {
66
30
  const portFilePath = getPortFilePath();
67
- const legacyPortFilePath = getLegacyPortFilePath();
68
31
 
69
32
  try {
70
- // Write to session-specific file
71
33
  fs.writeFileSync(portFilePath, port.toString());
72
- console.log(`Port ${port} written to ${portFilePath} [Session: ${getSessionId()}]`);
73
-
74
- // Optionally write to legacy file for backward compatibility
75
- if (writeLegacy) {
76
- try {
77
- fs.writeFileSync(legacyPortFilePath, port.toString());
78
- } catch (legacyError) {
79
- console.warn(`Could not write legacy port file: ${legacyError.message}`);
80
- }
81
- }
34
+ console.log(`Port ${port} written to ${portFilePath}`);
82
35
  } catch (error) {
83
36
  console.error('Failed to write port file:', error);
84
37
  }
85
38
  }
86
39
 
87
40
  /**
88
- * Read the port from session-specific or legacy file
41
+ * Read the port from file
42
+ * @returns {number|null} The port number, or null if not found
89
43
  */
90
- export function readPortFile(sessionId = null) {
91
- // If session ID provided, use that specific session
92
- if (sessionId) {
93
- try {
94
- const sessionPortFile = path.join(__dirname, `../.chromedebug-port-${sessionId}`);
95
- if (fs.existsSync(sessionPortFile)) {
96
- const port = parseInt(fs.readFileSync(sessionPortFile, 'utf8').trim());
97
- if (!isNaN(port)) {
98
- return port;
99
- }
100
- }
101
- } catch (error) {
102
- console.error(`Failed to read session port file for ${sessionId}:`, error);
103
- }
104
- }
105
-
106
- // Try current session file
44
+ export function readPortFile() {
107
45
  const portFilePath = getPortFilePath();
46
+
108
47
  try {
109
48
  if (fs.existsSync(portFilePath)) {
110
49
  const port = parseInt(fs.readFileSync(portFilePath, 'utf8').trim());
@@ -113,63 +52,37 @@ export function readPortFile(sessionId = null) {
113
52
  }
114
53
  }
115
54
  } catch (error) {
116
- console.error('Failed to read session port file:', error);
117
- }
118
-
119
- // Fall back to legacy file
120
- const legacyPortFilePath = getLegacyPortFilePath();
121
- try {
122
- if (fs.existsSync(legacyPortFilePath)) {
123
- const port = parseInt(fs.readFileSync(legacyPortFilePath, 'utf8').trim());
124
- if (!isNaN(port)) {
125
- return port;
126
- }
127
- }
128
- } catch (error) {
129
- console.error('Failed to read legacy port file:', error);
55
+ console.error('Failed to read port file:', error);
130
56
  }
131
57
 
132
58
  return null;
133
59
  }
134
60
 
135
61
  /**
136
- * Remove the port files on shutdown
62
+ * Remove the port file on shutdown
137
63
  */
138
64
  export function removePortFile() {
139
65
  const portFilePath = getPortFilePath();
140
- const legacyPortFilePath = getLegacyPortFilePath();
141
66
 
142
67
  try {
143
68
  if (fs.existsSync(portFilePath)) {
144
69
  fs.unlinkSync(portFilePath);
145
- console.log(`Removed session port file: ${portFilePath}`);
70
+ console.log(`Removed port file: ${portFilePath}`);
146
71
  }
147
72
  } catch (error) {
148
- console.error('Failed to remove session port file:', error);
149
- }
150
-
151
- // Only remove legacy file if no other sessions are using it
152
- try {
153
- if (fs.existsSync(legacyPortFilePath)) {
154
- // Check if there are other session port files
155
- const portFiles = fs.readdirSync(__dirname + '/..')
156
- .filter(f => f.startsWith('.chromedebug-port-') && f !== `.chromedebug-port-${getSessionId()}`);
157
-
158
- if (portFiles.length === 0) {
159
- fs.unlinkSync(legacyPortFilePath);
160
- console.log('Removed legacy port file (no other sessions)');
161
- }
162
- }
163
- } catch (error) {
164
- console.error('Failed to remove legacy port file:', error);
73
+ console.error('Failed to remove port file:', error);
165
74
  }
166
75
  }
167
76
 
168
77
  /**
169
78
  * Try to discover the Chrome Debug server by checking multiple ports
79
+ * @param {number[]} preferredPorts - Array of ports to check
80
+ * @param {Object} options - Discovery options
81
+ * @param {boolean} options.extendedRange - Whether to extend the port range
82
+ * @returns {Promise<number|null>} The discovered port, or null if not found
170
83
  */
171
84
  export async function discoverServer(preferredPorts = [3000, 3001, 3002, 3028], options = {}) {
172
- const { sessionId, extendedRange = true } = options;
85
+ const { extendedRange = true } = options;
173
86
 
174
87
  // Extend port range to reduce conflicts in multi-session environments
175
88
  if (extendedRange) {
@@ -183,13 +96,13 @@ export async function discoverServer(preferredPorts = [3000, 3001, 3002, 3028],
183
96
  ];
184
97
  }
185
98
 
186
- // First check session-specific port file
187
- const filePort = readPortFile(sessionId);
99
+ // First check port file
100
+ const filePort = readPortFile();
188
101
  if (filePort) {
189
102
  preferredPorts.unshift(filePort);
190
103
  }
191
104
 
192
- // Try each port with exponential backoff
105
+ // Try each port
193
106
  for (let i = 0; i < preferredPorts.length; i++) {
194
107
  const port = preferredPorts[i];
195
108
  try {
@@ -216,43 +129,3 @@ export async function discoverServer(preferredPorts = [3000, 3001, 3002, 3028],
216
129
 
217
130
  return null;
218
131
  }
219
-
220
- /**
221
- * Get session information for port discovery
222
- */
223
- export function getPortDiscoveryInfo() {
224
- return {
225
- sessionId: getSessionId(),
226
- portFile: getPortFilePath(),
227
- legacyPortFile: getLegacyPortFilePath(),
228
- currentPort: readPortFile()
229
- };
230
- }
231
-
232
- /**
233
- * Find all active ChromeDebug sessions by scanning port files
234
- */
235
- export function findActiveSessions() {
236
- const sessions = [];
237
-
238
- try {
239
- const files = fs.readdirSync(__dirname + '/..');
240
- const portFiles = files.filter(f => f.startsWith('.chromedebug-port-'));
241
-
242
- for (const file of portFiles) {
243
- const sessionId = file.replace('.chromedebug-port-', '');
244
- try {
245
- const port = parseInt(fs.readFileSync(path.join(__dirname, '..', file), 'utf8').trim());
246
- if (!isNaN(port)) {
247
- sessions.push({ sessionId, port, portFile: file });
248
- }
249
- } catch (error) {
250
- console.warn(`Could not read port file ${file}: ${error.message}`);
251
- }
252
- }
253
- } catch (error) {
254
- console.error('Failed to scan for active sessions:', error);
255
- }
256
-
257
- return sessions;
258
- }
@@ -1,41 +0,0 @@
1
- # Chrome Debug Assistant Extension
2
-
3
- This Chrome extension enables visual element selection for the Chrome Debug MCP server.
4
-
5
- ## Installation
6
-
7
- 1. Open Chrome and navigate to `chrome://extensions/`
8
- 2. Enable "Developer mode" (toggle in the top right)
9
- 3. Click "Load unpacked"
10
- 4. Select the `chrome-extension` directory
11
-
12
- ## Usage
13
-
14
- 1. Ensure the Chrome Debug MCP server is running (`npm start`)
15
- 2. Navigate to any webpage
16
- 3. Click on any element to select it
17
- 4. The element will be:
18
- - Highlighted with a pink outline
19
- - Sent to Chrome Debug server
20
- - Available for modification via Claude MCP tools
21
-
22
- ## How it Works
23
-
24
- 1. Click any element on the page
25
- 2. The extension sends the element's selector to Chrome Debug
26
- 3. Use Claude in your terminal to modify the selected element:
27
- ```
28
- "Show me what element is currently selected"
29
- "Make the selected element blue"
30
- "Apply CSS: padding: 20px !important; border-radius: 8px !important"
31
- "Execute JavaScript to get the element's text content"
32
- "Clear the selected element"
33
- ```
34
-
35
- ## Features
36
-
37
- - Visual element selection with hover highlighting
38
- - Automatic server detection (scans ports 3000-3025)
39
- - Pink outline shows currently selected element
40
- - Server connection status indicator
41
- - One-click element selection