@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.
- package/CLAUDE.md +1 -1
- package/README.md +1 -1
- package/chrome-extension/activation-manager.js +18 -4
- package/chrome-extension/background.js +1044 -552
- package/chrome-extension/browser-recording-manager.js +256 -0
- package/chrome-extension/chrome-debug-logger.js +168 -0
- package/chrome-extension/console-interception-library.js +430 -0
- package/chrome-extension/content.css +16 -16
- package/chrome-extension/content.js +617 -215
- package/chrome-extension/data-buffer.js +206 -17
- package/chrome-extension/extension-config.js +1 -1
- package/chrome-extension/frame-capture.js +52 -15
- package/chrome-extension/license-helper.js +26 -0
- package/chrome-extension/manifest.free.json +3 -6
- package/chrome-extension/options.js +1 -1
- package/chrome-extension/popup.html +315 -181
- package/chrome-extension/popup.js +673 -526
- package/chrome-extension/pro/enhanced-capture.js +406 -0
- package/chrome-extension/pro/frame-editor.html +410 -0
- package/chrome-extension/pro/frame-editor.js +1496 -0
- package/chrome-extension/pro/function-tracker.js +843 -0
- package/chrome-extension/pro/jszip.min.js +13 -0
- package/config/chromedebug-config.json +101 -0
- package/dist/chromedebug-extension-free.zip +0 -0
- package/package.json +3 -1
- package/scripts/package-pro-extension.js +1 -1
- package/scripts/webpack.config.free.cjs +11 -8
- package/scripts/webpack.config.pro.cjs +5 -0
- package/src/chrome-controller.js +7 -7
- package/src/cli.js +2 -2
- package/src/database.js +61 -9
- package/src/http-server.js +3 -2
- package/src/index.js +9 -6
- package/src/mcp/server.js +2 -2
- package/src/services/process-manager.js +10 -6
- package/src/services/process-tracker.js +10 -5
- package/src/services/profile-manager.js +17 -2
- package/src/validation/schemas.js +36 -6
- package/src/index-direct.js +0 -157
- package/src/index-modular.js +0 -219
- package/src/index-monolithic-backup.js +0 -2230
- package/src/legacy/chrome-controller-old.js +0 -1406
- package/src/legacy/index-express.js +0 -625
- package/src/legacy/index-old.js +0 -977
- package/src/legacy/routes.js +0 -260
- 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
|
+
}
|