@dynamicu/chromedebug-mcp 2.6.4 → 2.6.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.
@@ -0,0 +1,101 @@
1
+ {
2
+ "serverPorts": {
3
+ "httpServer": {
4
+ "preferredPorts": [
5
+ 3001,
6
+ 3000,
7
+ 3002,
8
+ 3028,
9
+ 3029,
10
+ 3030,
11
+ 3031,
12
+ 3032,
13
+ 3033,
14
+ 3034,
15
+ 3035,
16
+ 3036,
17
+ 8080,
18
+ 8081,
19
+ 8082,
20
+ 8083,
21
+ 8084,
22
+ 8085,
23
+ 8086,
24
+ 8087,
25
+ 8088,
26
+ 8089,
27
+ 8090,
28
+ 9000,
29
+ 9001,
30
+ 9002,
31
+ 9003,
32
+ 9004,
33
+ 9005,
34
+ 9006,
35
+ 9007,
36
+ 9008,
37
+ 9009,
38
+ 9010
39
+ ],
40
+ "description": "Ports to try for HTTP server discovery, in priority order"
41
+ },
42
+ "mcpServer": {
43
+ "preferredPorts": [
44
+ 3028,
45
+ 3023,
46
+ 3002,
47
+ 3003,
48
+ 3004,
49
+ 3005,
50
+ 3029,
51
+ 3030,
52
+ 3031,
53
+ 3032,
54
+ 3033,
55
+ 3034,
56
+ 3035,
57
+ 3036,
58
+ 8080,
59
+ 8081,
60
+ 8082,
61
+ 8083,
62
+ 8084,
63
+ 8085,
64
+ 8086,
65
+ 8087,
66
+ 8088,
67
+ 8089,
68
+ 8090,
69
+ 9000,
70
+ 9001,
71
+ 9002,
72
+ 9003,
73
+ 9004,
74
+ 9005,
75
+ 9006,
76
+ 9007,
77
+ 9008,
78
+ 9009,
79
+ 9010
80
+ ],
81
+ "description": "Ports to try for MCP server discovery, in priority order"
82
+ },
83
+ "discoveryRange": {
84
+ "start": 3000,
85
+ "end": 9010,
86
+ "description": "Port range for automatic discovery when preferred ports are not available"
87
+ }
88
+ },
89
+ "chromeExtension": {
90
+ "serverDiscovery": {
91
+ "timeoutMs": 3000,
92
+ "description": "Timeout for server discovery requests in milliseconds"
93
+ },
94
+ "portScanOrder": "httpFirst",
95
+ "description": "Order to try ports: 'httpFirst' tries HTTP ports first, 'mcpFirst' tries MCP ports first, 'sequential' tries all ports in numerical order"
96
+ },
97
+ "logging": {
98
+ "level": "info",
99
+ "description": "Logging level: debug, info, warn, error"
100
+ }
101
+ }
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamicu/chromedebug-mcp",
3
- "version": "2.6.4",
3
+ "version": "2.6.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",
@@ -14,7 +14,7 @@ import archiver from 'archiver';
14
14
  const __filename = fileURLToPath(import.meta.url);
15
15
  const __dirname = dirname(__filename);
16
16
  const rootDir = join(__dirname, '..');
17
- const proExtensionDir = join(rootDir, 'scripts', 'dist', 'pro');
17
+ const proExtensionDir = join(rootDir, 'dist', 'pro');
18
18
  const outputDir = join(rootDir, 'dist');
19
19
  const outputFile = join(outputDir, 'chromedebug-extension-pro.zip');
20
20
 
@@ -11,7 +11,7 @@ module.exports = {
11
11
  options: './chrome-extension/options.js'
12
12
  },
13
13
  output: {
14
- path: path.resolve(__dirname, 'dist/free'),
14
+ path: path.resolve(__dirname, '../dist/free'),
15
15
  filename: '[name].js',
16
16
  clean: true
17
17
  },
@@ -67,6 +67,9 @@ module.exports = {
67
67
  { from: 'chrome-extension/frame-capture.js', to: 'frame-capture.js' },
68
68
  { from: 'chrome-extension/chrome-session-manager.js', to: 'chrome-session-manager.js' },
69
69
 
70
+ // Browser-only recording mode files
71
+ { from: 'chrome-extension/browser-recording-manager.js', to: 'browser-recording-manager.js' },
72
+
70
73
  // Copy README
71
74
  { from: 'chrome-extension/README.md', to: 'README.md' }
72
75
  ]
@@ -12,7 +12,7 @@ module.exports = {
12
12
  'pro/frame-editor': './chrome-extension/pro/frame-editor.js'
13
13
  },
14
14
  output: {
15
- path: path.resolve(__dirname, 'dist/pro'),
15
+ path: path.resolve(__dirname, '../dist/pro'),
16
16
  filename: '[name].js',
17
17
  clean: true
18
18
  },
@@ -69,6 +69,9 @@ module.exports = {
69
69
  { from: 'chrome-extension/frame-capture.js', to: 'frame-capture.js' },
70
70
  { from: 'chrome-extension/chrome-session-manager.js', to: 'chrome-session-manager.js' },
71
71
 
72
+ // Browser-only recording mode files
73
+ { from: 'chrome-extension/browser-recording-manager.js', to: 'browser-recording-manager.js' },
74
+
72
75
  // Copy Pro features
73
76
  { from: 'chrome-extension/pro/function-tracker.js', to: 'pro/function-tracker.js' },
74
77
  { from: 'chrome-extension/pro/enhanced-capture.js', to: 'pro/enhanced-capture.js' },
@@ -1981,10 +1981,10 @@ export class ChromeController {
1981
1981
  async storeWorkflowRecording(sessionId, url, title, includeLogs, actions, logs, name = null, screenshotSettings = null, functionTraces = null) {
1982
1982
  try {
1983
1983
  const { database } = await import('./database.js');
1984
-
1984
+
1985
1985
  // Store the workflow recording
1986
1986
  const workflowId = database.storeWorkflowRecording(sessionId, url, title, includeLogs, name, screenshotSettings);
1987
-
1987
+
1988
1988
  // Merge function traces into actions if provided
1989
1989
  let allActions = [...actions];
1990
1990
  if (functionTraces && functionTraces.length > 0) {
@@ -1994,23 +1994,23 @@ export class ChromeController {
1994
1994
  // Sort all actions by timestamp to maintain chronological order
1995
1995
  allActions.sort((a, b) => a.timestamp - b.timestamp);
1996
1996
  }
1997
-
1997
+
1998
1998
  // Store actions with proper indexing
1999
1999
  const indexedActions = allActions.map((action, index) => ({
2000
2000
  ...action,
2001
2001
  index: index
2002
2002
  }));
2003
-
2003
+
2004
2004
  const actionsResult = database.storeWorkflowActions(workflowId, indexedActions);
2005
-
2005
+
2006
2006
  // Store logs if provided
2007
2007
  if (includeLogs && logs && logs.length > 0) {
2008
2008
  const logsResult = database.storeWorkflowLogs(workflowId, logs);
2009
2009
  logger.debug(`Stored ${logsResult.storedCount} logs for workflow ${sessionId}`);
2010
2010
  }
2011
-
2011
+
2012
2012
  logger.debug(`Stored workflow recording ${sessionId} with ${actionsResult.storedCount} actions (including ${functionTraces ? functionTraces.length : 0} function traces)`);
2013
-
2013
+
2014
2014
  return {
2015
2015
  success: true,
2016
2016
  sessionId: sessionId,
package/src/cli.js CHANGED
@@ -81,6 +81,12 @@ For details: https://github.com/dynamicupgrade/ChromeDebug#windows-setup
81
81
  verbose: args.verbose
82
82
  });
83
83
 
84
+ // Mark as persistent if requested
85
+ if (args.persistent) {
86
+ await sessionManager.markAsPersistent();
87
+ console.error('Session marked as persistent - will not be killed by other instances');
88
+ }
89
+
84
90
  // Kill other instances unless --allow-multiple is set
85
91
  if (!args.allowMultiple) {
86
92
  await sessionManager.killOtherInstances();
package/src/database.js CHANGED
@@ -1414,6 +1414,8 @@ class ChromePilotDatabase {
1414
1414
  return action[camelCaseName] || action[snakeCaseName] || null;
1415
1415
  };
1416
1416
 
1417
+ const screenshotDataToStore = action.screenshot_data || null;
1418
+
1417
1419
  stmt.run(
1418
1420
  workflowId,
1419
1421
  action.index,
@@ -1425,7 +1427,7 @@ class ChromePilotDatabase {
1425
1427
  action.text || null,
1426
1428
  action.placeholder || null,
1427
1429
  action.timestamp,
1428
- action.screenshot_data || null,
1430
+ screenshotDataToStore,
1429
1431
  action.component || null, // For function traces
1430
1432
  action.args ? JSON.stringify(action.args) : null, // Store args as JSON string
1431
1433
  action.stack || null, // For function traces
@@ -1436,6 +1438,7 @@ class ChromePilotDatabase {
1436
1438
  safeJsonStringify(getEnhancedFieldData(action, 'elementState', 'element_state'), 2048), // 2KB limit
1437
1439
  safeJsonStringify(getEnhancedFieldData(action, 'performanceMetrics', 'performance_metrics'), 1024) // 1KB limit
1438
1440
  );
1441
+
1439
1442
  storedCount++;
1440
1443
  }
1441
1444
 
@@ -1452,6 +1455,7 @@ class ChromePilotDatabase {
1452
1455
  `);
1453
1456
  updateStmt.run(storedCount, workflowId);
1454
1457
 
1458
+ logger.debug('[SCREENSHOT-DEBUG] database.storeWorkflowActions - EXIT');
1455
1459
  return { success: true, storedCount };
1456
1460
  }
1457
1461
 
@@ -1560,7 +1564,7 @@ class ChromePilotDatabase {
1560
1564
  text: action.text,
1561
1565
  placeholder: action.placeholder,
1562
1566
  timestamp: action.timestamp,
1563
- screenshot_data: action.screenshot_data
1567
+ screenshot: action.screenshot_data // Map screenshot_data to screenshot for consistency
1564
1568
  };
1565
1569
 
1566
1570
  // Add function trace fields if they exist
@@ -286,18 +286,19 @@ async function startHttpServer(chromeController = null, targetPort = null) {
286
286
  });
287
287
 
288
288
  // Protected workflow recording endpoints
289
- app.post('/chromedebug/workflow-recording',
289
+ app.post('/chromedebug/workflow-recording',
290
290
  authenticate,
291
291
  authorize(PERMISSIONS.WORKFLOW_WRITE),
292
292
  createValidator(workflowRecordingSchema),
293
293
  async (req, res) => {
294
294
  const { sessionId, url, title, includeLogs, actions, logs, functionTraces, name, screenshotSettings } = req.body;
295
+
295
296
  logger.info('[HTTP Server] Received workflow recording with name:', name);
296
297
  logger.info('[HTTP Server] Function traces count:', functionTraces ? functionTraces.length : 0);
297
298
  if (!sessionId || !actions) {
298
299
  return res.status(400).json({ error: 'sessionId and actions are required' });
299
300
  }
300
-
301
+
301
302
  try {
302
303
  const result = await activeController.storeWorkflowRecording(sessionId, url, title, includeLogs, actions, logs, name, screenshotSettings, functionTraces);
303
304
  res.json(result);
package/src/index.js CHANGED
@@ -153,7 +153,8 @@ class ChromePilotApp {
153
153
  noCleanup: false,
154
154
  verbose: false,
155
155
  dbPath: null,
156
- allowMultiple: false
156
+ allowMultiple: false,
157
+ persistent: false
157
158
  };
158
159
 
159
160
  const argv = process.argv.slice(2);
@@ -195,6 +196,9 @@ class ChromePilotApp {
195
196
  case '--allow-multiple':
196
197
  args.allowMultiple = true;
197
198
  break;
199
+ case '--persistent':
200
+ args.persistent = true;
201
+ break;
198
202
  case '--help':
199
203
  this.showHelp();
200
204
  process.exit(0);
@@ -242,6 +246,7 @@ Options:
242
246
  --no-cleanup Skip cleanup of dead processes on startup
243
247
  --verbose Enable verbose logging including session info
244
248
  --allow-multiple Allow multiple ChromeDebug instances (default: kill others on startup)
249
+ --persistent Mark as persistent server (won't be killed by other instances)
245
250
  --help Show this help message
246
251
 
247
252
  Session Isolation:
@@ -31,7 +31,8 @@ export class UnifiedSessionManager {
31
31
  port: null,
32
32
  processes: [],
33
33
  locks: [],
34
- heartbeat: Date.now()
34
+ heartbeat: Date.now(),
35
+ persistent: false // If true, won't be killed by other instances
35
36
  };
36
37
 
37
38
  this.heartbeatInterval = null;
@@ -354,6 +355,18 @@ export class UnifiedSessionManager {
354
355
  }
355
356
  }
356
357
 
358
+ /**
359
+ * Mark this session as persistent (won't be killed by other instances)
360
+ */
361
+ async markAsPersistent() {
362
+ this.state.persistent = true;
363
+ await this.updateSessionFile();
364
+
365
+ if (this.verbose) {
366
+ console.error(`Session ${this.sessionId} marked as persistent`);
367
+ }
368
+ }
369
+
357
370
  /**
358
371
  * Start heartbeat to keep session alive
359
372
  */
@@ -449,6 +462,7 @@ export class UnifiedSessionManager {
449
462
  /**
450
463
  * Kill all other ChromeDebug instances
451
464
  * Uses session files to find PIDs, then kills them gracefully
465
+ * Skips sessions marked as persistent
452
466
  * @returns {Array} Array of killed PIDs
453
467
  */
454
468
  async killOtherInstances() {
@@ -459,6 +473,12 @@ export class UnifiedSessionManager {
459
473
  for (const session of sessions) {
460
474
  if (session.pid === currentPid) continue; // Don't kill ourselves
461
475
 
476
+ // Skip persistent sessions
477
+ if (session.persistent) {
478
+ console.error(`Skipping persistent session: PID ${session.pid} (session ${session.sessionId})`);
479
+ continue;
480
+ }
481
+
462
482
  try {
463
483
  // Check if process exists
464
484
  process.kill(session.pid, 0); // Signal 0 = check existence
@@ -523,7 +543,7 @@ export class UnifiedSessionManager {
523
543
  */
524
544
  async isChromeDebugProcess(pid) {
525
545
  try {
526
- const { execSync } = await import('child_process');
546
+ const { execSync } = require('child_process');
527
547
 
528
548
  // Get process command line
529
549
  const cmd = execSync(`ps -p ${pid} -o command=`, { encoding: 'utf8' }).trim();
@@ -20,8 +20,35 @@ export const workflowRecordingSchema = Joi.object({
20
20
  Joi.object({
21
21
  type: Joi.string().required(),
22
22
  timestamp: Joi.number().required(),
23
- data: Joi.object().optional()
24
- })
23
+ data: Joi.object().optional(),
24
+ // Screenshot field - base64 encoded image data
25
+ screenshot_data: Joi.string().max(10485760).optional(), // 10MB max for base64 string
26
+ // Click/interaction fields
27
+ selector: Joi.string().optional(),
28
+ x: Joi.number().optional(),
29
+ y: Joi.number().optional(),
30
+ value: Joi.string().optional(),
31
+ text: Joi.string().optional(),
32
+ placeholder: Joi.string().optional(),
33
+ index: Joi.number().optional(),
34
+ // Enhanced click tracking fields
35
+ element_html: Joi.string().max(10240).optional(),
36
+ elementHTML: Joi.string().max(10240).optional(), // camelCase variant
37
+ component_data: Joi.string().max(3072).optional(),
38
+ componentData: Joi.string().max(3072).optional(), // camelCase variant
39
+ event_handlers: Joi.string().max(2048).optional(),
40
+ eventHandlers: Joi.string().max(2048).optional(), // camelCase variant
41
+ element_state: Joi.string().max(2048).optional(),
42
+ elementState: Joi.string().max(2048).optional(), // camelCase variant
43
+ performance_metrics: Joi.string().max(1024).optional(),
44
+ performanceMetrics: Joi.string().max(1024).optional(), // camelCase variant
45
+ // Function trace fields
46
+ component: Joi.string().optional(),
47
+ args: Joi.alternatives().try(Joi.array(), Joi.string()).optional(),
48
+ stack: Joi.string().optional(),
49
+ // Attached logs
50
+ logs: Joi.array().optional()
51
+ }).unknown(true) // Allow additional fields for future extensibility
25
52
  ).required(),
26
53
  logs: Joi.array().items(
27
54
  Joi.object({
@@ -42,10 +69,13 @@ export const workflowRecordingSchema = Joi.object({
42
69
  ).optional(),
43
70
  name: Joi.any().optional(),
44
71
  screenshotSettings: Joi.object({
45
- width: Joi.number().integer().min(100).max(4000).optional(),
46
- height: Joi.number().integer().min(100).max(4000).optional(),
47
- quality: Joi.number().min(10).max(100).optional()
48
- }).optional()
72
+ width: Joi.number().integer().min(100).max(4000).optional().allow(null),
73
+ height: Joi.number().integer().min(100).max(4000).optional().allow(null),
74
+ quality: Joi.number().min(10).max(100).optional().allow(null),
75
+ format: Joi.string().valid('png', 'jpeg').optional().allow(null),
76
+ enabled: Joi.boolean().optional().allow(null),
77
+ maxWidth: Joi.number().integer().optional().allow(null)
78
+ }).optional().allow(null)
49
79
  });
50
80
 
51
81
  // Frame batch schema