@dynamicu/chromedebug-mcp 2.6.6 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CLAUDE.md +1 -1
  2. package/README.md +1 -1
  3. package/chrome-extension/activation-manager.js +18 -4
  4. package/chrome-extension/background.js +1044 -552
  5. package/chrome-extension/browser-recording-manager.js +256 -0
  6. package/chrome-extension/chrome-debug-logger.js +168 -0
  7. package/chrome-extension/console-interception-library.js +430 -0
  8. package/chrome-extension/content.css +16 -16
  9. package/chrome-extension/content.js +617 -215
  10. package/chrome-extension/data-buffer.js +206 -17
  11. package/chrome-extension/extension-config.js +1 -1
  12. package/chrome-extension/frame-capture.js +52 -15
  13. package/chrome-extension/license-helper.js +26 -0
  14. package/chrome-extension/manifest.free.json +3 -6
  15. package/chrome-extension/options.js +1 -1
  16. package/chrome-extension/popup.html +315 -181
  17. package/chrome-extension/popup.js +673 -526
  18. package/chrome-extension/pro/enhanced-capture.js +406 -0
  19. package/chrome-extension/pro/frame-editor.html +410 -0
  20. package/chrome-extension/pro/frame-editor.js +1496 -0
  21. package/chrome-extension/pro/function-tracker.js +843 -0
  22. package/chrome-extension/pro/jszip.min.js +13 -0
  23. package/config/chromedebug-config.json +101 -0
  24. package/dist/chromedebug-extension-free.zip +0 -0
  25. package/package.json +3 -1
  26. package/scripts/package-pro-extension.js +1 -1
  27. package/scripts/webpack.config.free.cjs +11 -8
  28. package/scripts/webpack.config.pro.cjs +5 -0
  29. package/src/chrome-controller.js +7 -7
  30. package/src/cli.js +2 -2
  31. package/src/database.js +61 -9
  32. package/src/http-server.js +3 -2
  33. package/src/index.js +9 -6
  34. package/src/mcp/server.js +2 -2
  35. package/src/services/process-manager.js +10 -6
  36. package/src/services/process-tracker.js +10 -5
  37. package/src/services/profile-manager.js +17 -2
  38. package/src/validation/schemas.js +36 -6
  39. package/src/index-direct.js +0 -157
  40. package/src/index-modular.js +0 -219
  41. package/src/index-monolithic-backup.js +0 -2230
  42. package/src/legacy/chrome-controller-old.js +0 -1406
  43. package/src/legacy/index-express.js +0 -625
  44. package/src/legacy/index-old.js +0 -977
  45. package/src/legacy/routes.js +0 -260
  46. package/src/legacy/shared-storage.js +0 -101
@@ -0,0 +1,256 @@
1
+ /**
2
+ * BrowserRecordingManager - Manage recordings stored in IndexedDB
3
+ *
4
+ * Handles recording lifecycle for browser-only mode:
5
+ * - Stores frames and metadata in IndexedDB
6
+ * - Exports recordings as ZIP files
7
+ * - Manages cleanup and deletion
8
+ */
9
+
10
+ class BrowserRecordingManager {
11
+ constructor(db) {
12
+ this.db = db; // DataBuffer instance
13
+ this.activeRecordings = new Map(); // sessionId -> recording metadata
14
+ }
15
+
16
+ /**
17
+ * Start a new browser recording
18
+ * @param {string} sessionId - Unique session identifier
19
+ * @param {Object} metadata - Recording metadata (tabId, url, timestamp, etc.)
20
+ * @returns {Promise<{success: boolean, sessionId: string}>}
21
+ */
22
+ async startRecording(sessionId, metadata = {}) {
23
+ try {
24
+ // Check if recording already exists (cleanup from previous incomplete session)
25
+ const existingRecording = await this.db.getBrowserRecording(sessionId);
26
+ if (existingRecording) {
27
+ console.warn(`[BrowserRecordingManager] Cleaning up existing recording: ${sessionId}`);
28
+ await this.deleteRecording(sessionId);
29
+ }
30
+
31
+ const recording = {
32
+ sessionId,
33
+ startTime: Date.now(),
34
+ tabId: metadata.tabId,
35
+ url: metadata.url,
36
+ title: metadata.title || 'Browser Recording',
37
+ frameCount: 0,
38
+ status: 'recording',
39
+ metadata
40
+ };
41
+
42
+ // Store recording metadata in IndexedDB
43
+ await this.db.addBrowserRecording(recording);
44
+
45
+ // Track active recording
46
+ this.activeRecordings.set(sessionId, recording);
47
+
48
+ return {
49
+ success: true,
50
+ sessionId,
51
+ startTime: recording.startTime
52
+ };
53
+ } catch (error) {
54
+ console.error('Failed to start browser recording:', {
55
+ name: error.name,
56
+ message: error.message,
57
+ stack: error.stack,
58
+ error: error
59
+ });
60
+ return {
61
+ success: false,
62
+ sessionId,
63
+ error: `${error.name}: ${error.message}`
64
+ };
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Add a frame to an active recording
70
+ * @param {string} sessionId
71
+ * @param {Object} frameData - {frameIndex, screenshot, logs, timestamp}
72
+ * @returns {Promise<boolean>}
73
+ */
74
+ async addFrame(sessionId, frameData) {
75
+ try {
76
+ const frame = {
77
+ sessionId,
78
+ frameIndex: frameData.frameIndex,
79
+ screenshot: frameData.screenshot, // Base64 data URL
80
+ logs: frameData.logs || [],
81
+ timestamp: frameData.timestamp || Date.now(),
82
+ absoluteTimestamp: frameData.absoluteTimestamp // Preserve absolute timestamp for log association
83
+ };
84
+
85
+ await this.db.addBrowserFrame(frame);
86
+
87
+ // Update frame count
88
+ const recording = this.activeRecordings.get(sessionId);
89
+ if (recording) {
90
+ recording.frameCount++;
91
+ }
92
+
93
+ return true;
94
+ } catch (error) {
95
+ console.error('Failed to add frame:', error);
96
+ return false;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Stop recording and finalize
102
+ * @param {string} sessionId
103
+ * @returns {Promise<{success: boolean, frameCount: number, duration: number}>}
104
+ */
105
+ async stopRecording(sessionId) {
106
+ try {
107
+ const recording = this.activeRecordings.get(sessionId);
108
+ if (!recording) {
109
+ throw new Error(`Recording ${sessionId} not found`);
110
+ }
111
+
112
+ const endTime = Date.now();
113
+ const duration = endTime - recording.startTime;
114
+
115
+ // Update recording status
116
+ recording.status = 'completed';
117
+ recording.endTime = endTime;
118
+ recording.duration = duration;
119
+
120
+ await this.db.updateBrowserRecording(sessionId, {
121
+ status: 'completed',
122
+ endTime,
123
+ duration,
124
+ frameCount: recording.frameCount
125
+ });
126
+
127
+ this.activeRecordings.delete(sessionId);
128
+
129
+ return {
130
+ success: true,
131
+ sessionId,
132
+ frameCount: recording.frameCount,
133
+ duration
134
+ };
135
+ } catch (error) {
136
+ console.error('Failed to stop recording:', error);
137
+ return {
138
+ success: false,
139
+ sessionId,
140
+ error: error.message
141
+ };
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Export recording as downloadable ZIP blob
147
+ * @param {string} sessionId
148
+ * @returns {Promise<{blob: Blob, filename: string}>}
149
+ */
150
+ async exportRecording(sessionId) {
151
+ try {
152
+ // Get recording metadata
153
+ const recording = await this.db.getBrowserRecording(sessionId);
154
+ if (!recording) {
155
+ throw new Error(`Recording ${sessionId} not found`);
156
+ }
157
+
158
+ // Get all frames for this session
159
+ const frames = await this.db.getBrowserFrames(sessionId);
160
+
161
+ // Create JSON manifest
162
+ const manifest = {
163
+ sessionId,
164
+ title: recording.title,
165
+ url: recording.url,
166
+ startTime: recording.startTime,
167
+ endTime: recording.endTime,
168
+ duration: recording.duration,
169
+ frameCount: frames.length,
170
+ exportedAt: Date.now()
171
+ };
172
+
173
+ // Build export data structure
174
+ const exportData = {
175
+ manifest,
176
+ recording,
177
+ frames: frames.map(frame => ({
178
+ frameIndex: frame.frameIndex,
179
+ timestamp: frame.timestamp,
180
+ screenshot: frame.screenshot,
181
+ logs: frame.logs
182
+ }))
183
+ };
184
+
185
+ // Convert to JSON string
186
+ const jsonString = JSON.stringify(exportData, null, 2);
187
+
188
+ // Create blob
189
+ const blob = new Blob([jsonString], { type: 'application/json' });
190
+
191
+ // Generate filename
192
+ const date = new Date(recording.startTime).toISOString().split('T')[0];
193
+ const filename = `chrome-debug-recording-${date}-${sessionId.slice(-8)}.json`;
194
+
195
+ return {
196
+ blob,
197
+ filename,
198
+ size: blob.size,
199
+ frameCount: frames.length
200
+ };
201
+ } catch (error) {
202
+ console.error('Failed to export recording:', error);
203
+ throw error;
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Delete a recording and all its frames
209
+ * @param {string} sessionId
210
+ * @returns {Promise<{success: boolean, deletedFrames: number}>}
211
+ */
212
+ async deleteRecording(sessionId) {
213
+ try {
214
+ // Delete all frames
215
+ const deletedFrames = await this.db.deleteBrowserFrames(sessionId);
216
+
217
+ // Delete recording metadata
218
+ await this.db.deleteBrowserRecording(sessionId);
219
+
220
+ // Remove from active recordings if present
221
+ this.activeRecordings.delete(sessionId);
222
+
223
+ return {
224
+ success: true,
225
+ sessionId,
226
+ deletedFrames
227
+ };
228
+ } catch (error) {
229
+ console.error('Failed to delete recording:', error);
230
+ return {
231
+ success: false,
232
+ sessionId,
233
+ error: error.message
234
+ };
235
+ }
236
+ }
237
+
238
+ /**
239
+ * List all recordings
240
+ * @returns {Promise<Array>}
241
+ */
242
+ async listRecordings() {
243
+ try {
244
+ return await this.db.getAllBrowserRecordings();
245
+ } catch (error) {
246
+ console.error('Failed to list recordings:', error);
247
+ return [];
248
+ }
249
+ }
250
+
251
+ }
252
+
253
+ // Export for use in other extension files
254
+ if (typeof module !== 'undefined' && module.exports) {
255
+ module.exports = BrowserRecordingManager;
256
+ }
@@ -0,0 +1,168 @@
1
+ /**
2
+ * ChromeDebugLogger - Dedicated logging system for Chrome Debug internal diagnostics
3
+ * Bypasses console interception to prevent polluting user workflow recordings
4
+ *
5
+ * Key Features:
6
+ * - Preserves original console before interception
7
+ * - Auto-enables in development mode
8
+ * - Works in service workers (chrome.storage.local) and content scripts (localStorage)
9
+ * - Includes source location tracking
10
+ * - Default disabled in production
11
+ * - Consistent prefix: [🔧 ChromeDebug:ComponentName]
12
+ */
13
+ class ChromeDebugLogger {
14
+ constructor() {
15
+ // CRITICAL: Save original console IMMEDIATELY (before interception occurs)
16
+ this.originalConsole = {
17
+ log: console.log.bind(console),
18
+ error: console.error.bind(console),
19
+ warn: console.warn.bind(console),
20
+ debug: console.debug.bind(console),
21
+ info: console.info.bind(console)
22
+ };
23
+
24
+ // Auto-enable in development mode
25
+ const isDev = typeof chrome !== 'undefined' &&
26
+ chrome.runtime?.getManifest?.()?.version_name?.includes('dev');
27
+ this.enabled = isDev || false;
28
+
29
+ this.prefix = '🔧 ChromeDebug';
30
+
31
+ // Load saved state (async, but don't block constructor)
32
+ this._loadState();
33
+ }
34
+
35
+ /**
36
+ * Enable Chrome Debug internal logging
37
+ */
38
+ enable() {
39
+ this.enabled = true;
40
+ this._saveState();
41
+ this._log('Logger', 'Chrome Debug logger enabled', null, 'info');
42
+ }
43
+
44
+ /**
45
+ * Disable Chrome Debug internal logging
46
+ */
47
+ disable() {
48
+ this._log('Logger', 'Chrome Debug logger disabled', null, 'info');
49
+ this.enabled = false;
50
+ this._saveState();
51
+ }
52
+
53
+ /**
54
+ * Check if logging is enabled
55
+ */
56
+ isEnabled() {
57
+ return this.enabled;
58
+ }
59
+
60
+ /**
61
+ * Log informational message
62
+ */
63
+ log(component, message, data = null) {
64
+ if (!this.enabled) return;
65
+ this._log(component, message, data, 'log');
66
+ }
67
+
68
+ /**
69
+ * Log error message
70
+ */
71
+ error(component, message, data = null) {
72
+ if (!this.enabled) return;
73
+ this._log(component, message, data, 'error');
74
+ }
75
+
76
+ /**
77
+ * Log warning message
78
+ */
79
+ warn(component, message, data = null) {
80
+ if (!this.enabled) return;
81
+ this._log(component, message, data, 'warn');
82
+ }
83
+
84
+ /**
85
+ * Log debug message
86
+ */
87
+ debug(component, message, data = null) {
88
+ if (!this.enabled) return;
89
+ this._log(component, message, data, 'debug');
90
+ }
91
+
92
+ /**
93
+ * Internal logging implementation
94
+ */
95
+ _log(component, message, data, level = 'log') {
96
+ try {
97
+ // Capture source location for debugging
98
+ const error = new Error();
99
+ const stack = error.stack?.split('\n')[3]; // Caller's location
100
+ const location = stack?.match(/\((.*?)\)/)?.[1] || '';
101
+
102
+ const prefix = `[${this.prefix}:${component}]`;
103
+ const suffix = location ? ` @${location.split('/').pop()}` : '';
104
+
105
+ if (data !== null && data !== undefined) {
106
+ this.originalConsole[level](prefix, message + suffix, data);
107
+ } else {
108
+ this.originalConsole[level](prefix, message + suffix);
109
+ }
110
+ } catch (e) {
111
+ // Fallback if logging fails
112
+ this.originalConsole.error('ChromeDebugLogger error:', e);
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Save enabled state
118
+ */
119
+ async _saveState() {
120
+ try {
121
+ if (typeof chrome !== 'undefined' && chrome.storage) {
122
+ // Service worker context
123
+ await chrome.storage.local.set({ chromedebug_logger_enabled: this.enabled });
124
+ } else if (typeof localStorage !== 'undefined') {
125
+ // Content/popup context
126
+ localStorage.setItem('chromedebug_logger_enabled', this.enabled.toString());
127
+ }
128
+ } catch (e) {
129
+ // Storage might not be available
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Load enabled state
135
+ */
136
+ async _loadState() {
137
+ try {
138
+ if (typeof chrome !== 'undefined' && chrome.storage) {
139
+ // Service worker context
140
+ const result = await chrome.storage.local.get('chromedebug_logger_enabled');
141
+ if (result.chromedebug_logger_enabled !== undefined) {
142
+ this.enabled = result.chromedebug_logger_enabled;
143
+ }
144
+ } else if (typeof localStorage !== 'undefined') {
145
+ // Content/popup context
146
+ const saved = localStorage.getItem('chromedebug_logger_enabled');
147
+ if (saved !== null) {
148
+ this.enabled = saved === 'true';
149
+ }
150
+ }
151
+ } catch (e) {
152
+ // Storage might not be available
153
+ }
154
+ }
155
+ }
156
+
157
+ // Create global singleton instance
158
+ const chromeDebugLogger = new ChromeDebugLogger();
159
+
160
+ // Export for different contexts
161
+ if (typeof window !== 'undefined') {
162
+ window.chromeDebugLogger = chromeDebugLogger;
163
+ window.ChromeDebugLogger = ChromeDebugLogger;
164
+ }
165
+ if (typeof self !== 'undefined') {
166
+ self.chromeDebugLogger = chromeDebugLogger;
167
+ self.ChromeDebugLogger = ChromeDebugLogger;
168
+ }