@dynamicu/chromedebug-mcp 2.7.1 → 2.7.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.
@@ -1375,12 +1375,26 @@ chrome.runtime.onInstalled.addListener(async (details) => {
1375
1375
  chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
1376
1376
  // Inject console logging on navigation during recording
1377
1377
  if (changeInfo.status === 'loading') {
1378
- // Use session manager validation if available
1378
+ // Skip restricted URLs
1379
+ if (!tab.url || tab.url.startsWith('chrome://') || tab.url.startsWith('chrome-extension://')) {
1380
+ return;
1381
+ }
1382
+
1383
+ // Check for WORKFLOW recording first (takes priority)
1384
+ if (workflowRecordingTabs.has(tabId) && workflowIncludeLogs.get(tabId)) {
1385
+ // console.log('[Navigation] Re-injecting workflow console logging for tab:', tabId);
1386
+ self.ConsoleInterceptionLibrary.startConsoleInterception(tabId, WORKFLOW_RECORDING_CONSOLE_CONFIG).catch(err => {
1387
+ console.error('Failed to re-inject workflow console logging:', err);
1388
+ });
1389
+ return; // Don't also inject screen recording logs
1390
+ }
1391
+
1392
+ // Check for SCREEN recording
1379
1393
  if (sessionManager) {
1380
1394
  isCurrentlyRecordingAsync().then(async (recording) => {
1381
1395
  const currentTabId = await getCurrentTabIdAsync();
1382
- if (recording && tabId === currentTabId && tab.url && !tab.url.startsWith('chrome://') && !tab.url.startsWith('chrome-extension://')) {
1383
- // console.log('Re-injecting console logging for navigation during recording');
1396
+ if (recording && tabId === currentTabId) {
1397
+ // console.log('[Navigation] Re-injecting screen recording console logging for tab:', tabId);
1384
1398
  startCapturingLogs(tabId).catch(err => {
1385
1399
  console.error('Failed to re-inject console logging:', err);
1386
1400
  });
@@ -1389,9 +1403,9 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
1389
1403
  console.error('Error checking recording state for navigation:', error);
1390
1404
  });
1391
1405
  } else {
1392
- // Legacy fallback
1393
- if (tabId === recordingTabId && isCurrentlyRecording && tab.url && !tab.url.startsWith('chrome://') && !tab.url.startsWith('chrome-extension://')) {
1394
- // console.log('Re-injecting console logging for navigation during recording');
1406
+ // Legacy fallback for screen recording
1407
+ if (tabId === recordingTabId && isCurrentlyRecording) {
1408
+ // console.log('[Navigation] Re-injecting screen recording console logging (legacy) for tab:', tabId);
1395
1409
  startCapturingLogs(tabId).catch(err => {
1396
1410
  console.error('Failed to re-inject console logging:', err);
1397
1411
  });
@@ -1522,7 +1536,7 @@ let frameCounter = new Map(); // DEPRECATED: Session manager handles frame count
1522
1536
  // =============================================================================
1523
1537
  // INACTIVITY AUTO-STOP - Stop recording if user is inactive
1524
1538
  // =============================================================================
1525
- const DEFAULT_INACTIVITY_TIMEOUT_MS = 10 * 1000; // 10 seconds default
1539
+ const DEFAULT_INACTIVITY_TIMEOUT_MS = 60 * 1000; // 60 seconds default (1 minute)
1526
1540
  let lastUserActivityTime = Date.now();
1527
1541
  let inactivityCheckInterval = null;
1528
1542
  let currentInactivityTimeout = DEFAULT_INACTIVITY_TIMEOUT_MS;
@@ -1546,7 +1560,7 @@ async function startInactivityMonitoring(tabId) {
1546
1560
 
1547
1561
  // Load user's configured timeout from storage
1548
1562
  const settings = await chrome.storage.sync.get(['inactivityTimeout']);
1549
- currentInactivityTimeout = (settings.inactivityTimeout || 10) * 1000; // Convert seconds to ms (default 10s)
1563
+ currentInactivityTimeout = (settings.inactivityTimeout || 60) * 1000; // Convert seconds to ms (default 60s)
1550
1564
 
1551
1565
  lastUserActivityTime = Date.now();
1552
1566
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "ChromeDebug MCP Assistant FREE",
4
- "version": "2.7.1",
4
+ "version": "2.7.2",
5
5
  "description": "AI-powered browser debugging with visual element selection and screen recording for developers",
6
6
  "permissions": [
7
7
  "activeTab",
@@ -156,10 +156,10 @@
156
156
  <div class="setting-group">
157
157
  <h3>Recording Safety</h3>
158
158
  <label for="inactivity-timeout">Auto-Stop After Inactivity (seconds)</label>
159
- <input type="number" id="inactivity-timeout" min="5" max="3600" value="10">
159
+ <input type="number" id="inactivity-timeout" min="5" max="3600" value="60">
160
160
  <div class="help-text">
161
161
  Screen recording will automatically stop if no mouse or keyboard activity is detected.
162
- Default: 10 seconds. Increase for long demo recordings or presentations.
162
+ Default: 60 seconds. Adjust for long demo recordings or presentations.
163
163
  </div>
164
164
  </div>
165
165
 
@@ -9,8 +9,8 @@ chrome.storage.sync.get(['serverPort', 'chromePilotMode', 'chromePilotAllowedSit
9
9
  document.getElementById('server-port').value = data.serverPort;
10
10
  }
11
11
 
12
- // Load inactivity timeout (default 10 seconds)
13
- document.getElementById('inactivity-timeout').value = data.inactivityTimeout || 10;
12
+ // Load inactivity timeout (default 60 seconds)
13
+ document.getElementById('inactivity-timeout').value = data.inactivityTimeout || 60;
14
14
 
15
15
  // Load site restriction settings
16
16
  const mode = data.chromePilotMode || 'whitelist';
@@ -38,7 +38,7 @@ document.getElementById('save').addEventListener('click', async () => {
38
38
 
39
39
  // Get inactivity timeout
40
40
  const inactivityTimeoutInput = document.getElementById('inactivity-timeout');
41
- const inactivityTimeout = parseInt(inactivityTimeoutInput.value) || 10;
41
+ const inactivityTimeout = parseInt(inactivityTimeoutInput.value) || 60;
42
42
  if (inactivityTimeout < 5 || inactivityTimeout > 3600) {
43
43
  showStatus('error', 'Inactivity timeout must be between 5 and 3600 seconds');
44
44
  return;
@@ -68,7 +68,7 @@ document.getElementById('save').addEventListener('click', async () => {
68
68
  // Reset to default
69
69
  document.getElementById('reset').addEventListener('click', async () => {
70
70
  document.getElementById('server-port').value = '';
71
- document.getElementById('inactivity-timeout').value = '10';
71
+ document.getElementById('inactivity-timeout').value = '60';
72
72
  document.querySelector('input[value="whitelist"]').checked = true;
73
73
  document.getElementById('allowed-sites').value = 'localhost:*\n127.0.0.1:*\n*.local\n*.test\n*.dev';
74
74
  document.getElementById('blocked-sites').value = 'youtube.com\n*.youtube.com\ngoogle.com\n*.google.com\nfacebook.com\ntwitter.com\nx.com';
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamicu/chromedebug-mcp",
3
- "version": "2.7.1",
3
+ "version": "2.7.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",
package/src/database.js CHANGED
@@ -132,9 +132,20 @@ class ChromeDebugDatabase {
132
132
  // Migrate from old location if needed
133
133
  this.migrateIfNeeded();
134
134
 
135
+ // Check if database exists and is corrupted
136
+ if (fs.existsSync(this.dbPath)) {
137
+ const integrityStatus = this.checkDatabaseIntegrity();
138
+ if (!integrityStatus.ok) {
139
+ logger.warn(`[Database] Database corruption detected: ${integrityStatus.error}`);
140
+ this.handleCorruptedDatabase();
141
+ }
142
+ }
143
+
135
144
  // Open database connection
136
145
  this.db = new Database(this.dbPath);
137
146
  this.db.pragma('journal_mode = WAL'); // Enable WAL mode for better concurrent access
147
+ this.db.pragma('synchronous = NORMAL'); // Balance between safety and performance
148
+ this.db.pragma('foreign_keys = ON'); // Enforce foreign key constraints
138
149
 
139
150
  // Create tables
140
151
  this.createTables();
@@ -142,6 +153,64 @@ class ChromeDebugDatabase {
142
153
  logger.debug(`ChromeDebug MCP database initialized at: ${this.dbPath}`);
143
154
  }
144
155
 
156
+ /**
157
+ * Check database integrity before opening
158
+ * @returns {{ ok: boolean, error?: string }}
159
+ */
160
+ checkDatabaseIntegrity() {
161
+ try {
162
+ const tempDb = new Database(this.dbPath, { readonly: true });
163
+ const result = tempDb.pragma('integrity_check');
164
+ tempDb.close();
165
+
166
+ if (result && result[0] && result[0].integrity_check === 'ok') {
167
+ return { ok: true };
168
+ } else {
169
+ const errorMsg = result?.[0]?.integrity_check || 'Unknown integrity error';
170
+ return { ok: false, error: errorMsg };
171
+ }
172
+ } catch (error) {
173
+ return { ok: false, error: error.message };
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Handle corrupted database by backing it up and creating a fresh one
179
+ */
180
+ handleCorruptedDatabase() {
181
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
182
+ const backupPath = `${this.dbPath}.corrupted_${timestamp}`;
183
+
184
+ logger.warn(`[Database] Backing up corrupted database to: ${backupPath}`);
185
+
186
+ try {
187
+ // Move corrupted database and associated files
188
+ if (fs.existsSync(this.dbPath)) {
189
+ fs.renameSync(this.dbPath, backupPath);
190
+ }
191
+ if (fs.existsSync(`${this.dbPath}-shm`)) {
192
+ fs.renameSync(`${this.dbPath}-shm`, `${backupPath}-shm`);
193
+ }
194
+ if (fs.existsSync(`${this.dbPath}-wal`)) {
195
+ fs.renameSync(`${this.dbPath}-wal`, `${backupPath}-wal`);
196
+ }
197
+
198
+ logger.info(`[Database] Corrupted database backed up. A fresh database will be created.`);
199
+ } catch (error) {
200
+ logger.error(`[Database] Failed to backup corrupted database: ${error.message}`);
201
+ // Try to delete instead of rename
202
+ try {
203
+ fs.unlinkSync(this.dbPath);
204
+ if (fs.existsSync(`${this.dbPath}-shm`)) fs.unlinkSync(`${this.dbPath}-shm`);
205
+ if (fs.existsSync(`${this.dbPath}-wal`)) fs.unlinkSync(`${this.dbPath}-wal`);
206
+ logger.info(`[Database] Corrupted database removed. A fresh database will be created.`);
207
+ } catch (deleteError) {
208
+ logger.error(`[Database] Failed to remove corrupted database: ${deleteError.message}`);
209
+ throw new Error(`Cannot recover from database corruption: ${deleteError.message}`);
210
+ }
211
+ }
212
+ }
213
+
145
214
  createTables() {
146
215
  // Recordings table for frame capture sessions
147
216
  this.db.exec(`
@@ -1,5 +1,10 @@
1
1
  import Joi from 'joi';
2
2
 
3
+ // Valid console log levels - must match what Chrome extension captures
4
+ // Chrome extension captures: log, error, warn, info, debug, trace, table, dir, group, groupEnd, time, timeEnd, count
5
+ // See: chrome-extension/console-interception-library.js line 228
6
+ const VALID_LOG_LEVELS = ['log', 'info', 'warn', 'error', 'debug', 'trace', 'table', 'dir', 'group', 'groupEnd', 'time', 'timeEnd', 'count'];
7
+
3
8
  // Common validation patterns
4
9
  const patterns = {
5
10
  sessionId: Joi.string().pattern(/^[a-zA-Z0-9_-]+$/).min(1).max(100),
@@ -53,7 +58,7 @@ export const workflowRecordingSchema = Joi.object({
53
58
  ).required(),
54
59
  logs: Joi.array().items(
55
60
  Joi.object({
56
- level: Joi.string().valid('log', 'info', 'warn', 'error', 'debug').required(),
61
+ level: Joi.string().valid(...VALID_LOG_LEVELS).required(),
57
62
  message: Joi.string().required(),
58
63
  timestamp: Joi.number().required(),
59
64
  args: Joi.array().optional()
@@ -93,7 +98,7 @@ export const frameBatchSchema = Joi.object({
93
98
  index: Joi.number().integer().min(0).optional(), // Chrome extension sends this
94
99
  logs: Joi.array().items(
95
100
  Joi.object({
96
- level: Joi.string().valid('log', 'info', 'warn', 'error', 'debug').required(),
101
+ level: Joi.string().valid(...VALID_LOG_LEVELS).required(),
97
102
  message: Joi.string().required(),
98
103
  timestamp: Joi.number().required(),
99
104
  args: Joi.array().optional()
@@ -121,7 +126,7 @@ export const associateLogsSchema = Joi.object({
121
126
  sessionId: patterns.sessionId.required(),
122
127
  logs: Joi.array().items(
123
128
  Joi.object({
124
- level: Joi.string().valid('log', 'info', 'warn', 'error', 'debug').required(),
129
+ level: Joi.string().valid(...VALID_LOG_LEVELS).required(),
125
130
  message: Joi.string().required(),
126
131
  timestamp: Joi.number().required(),
127
132
  args: Joi.array().optional()
@@ -134,7 +139,7 @@ export const streamLogsSchema = Joi.object({
134
139
  sessionId: patterns.sessionId.required(),
135
140
  logs: Joi.array().items(
136
141
  Joi.object({
137
- level: Joi.string().valid('log', 'info', 'warn', 'error', 'debug', 'trace', 'table', 'dir', 'group', 'groupEnd', 'time', 'timeEnd', 'count').required(),
142
+ level: Joi.string().valid(...VALID_LOG_LEVELS).required(),
138
143
  message: Joi.string().required(),
139
144
  timestamp: Joi.number().required(),
140
145
  sequence: Joi.number().integer().min(0).required(),