@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.
- package/CLAUDE.md +25 -0
- package/chrome-extension/activation-manager.js +18 -4
- package/chrome-extension/background.js +439 -53
- package/chrome-extension/browser-recording-manager.js +256 -0
- package/chrome-extension/content.js +167 -97
- package/chrome-extension/data-buffer.js +206 -17
- package/chrome-extension/frame-capture.js +52 -15
- package/chrome-extension/manifest.free.json +3 -4
- package/chrome-extension/popup.html +109 -5
- package/chrome-extension/popup.js +597 -159
- package/config/chromedebug-config.json +101 -0
- package/dist/chromedebug-extension-free.zip +0 -0
- package/package.json +1 -1
- package/scripts/package-pro-extension.js +1 -1
- package/scripts/webpack.config.free.cjs +4 -1
- package/scripts/webpack.config.pro.cjs +4 -1
- package/src/chrome-controller.js +7 -7
- package/src/cli.js +6 -0
- package/src/database.js +6 -2
- package/src/http-server.js +3 -2
- package/src/index.js +6 -1
- package/src/services/unified-session-manager.js +22 -2
- package/src/validation/schemas.js +36 -6
|
@@ -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.
|
|
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, '
|
|
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' },
|
package/src/chrome-controller.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
package/src/http-server.js
CHANGED
|
@@ -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 } =
|
|
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
|
-
|
|
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
|