@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
package/src/database.js
CHANGED
|
@@ -12,7 +12,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
12
12
|
// Initialize project manager for path resolution
|
|
13
13
|
const projectManager = new ProjectManager();
|
|
14
14
|
|
|
15
|
-
class
|
|
15
|
+
class ChromeDebugDatabase {
|
|
16
16
|
constructor(options = {}) {
|
|
17
17
|
this.db = null;
|
|
18
18
|
this.initialized = false;
|
|
@@ -27,10 +27,54 @@ class ChromePilotDatabase {
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Gets the centralized database path in user's home directory
|
|
30
|
+
* Performs atomic migration from old chrome-pilot.db to new chromedebug.db
|
|
30
31
|
* @returns {string} Centralized database file path
|
|
31
32
|
*/
|
|
32
33
|
getCentralDbPath() {
|
|
33
|
-
|
|
34
|
+
const dbDir = path.join(os.homedir(), '.chromedebug');
|
|
35
|
+
const newPath = path.join(dbDir, 'chromedebug.db');
|
|
36
|
+
const oldPath = path.join(dbDir, 'chrome-pilot.db');
|
|
37
|
+
const lockFile = path.join(dbDir, '.migration.lock');
|
|
38
|
+
|
|
39
|
+
// Ensure directory exists
|
|
40
|
+
if (!fs.existsSync(dbDir)) {
|
|
41
|
+
fs.mkdirSync(dbDir, { recursive: true });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check if migration is needed
|
|
45
|
+
if (fs.existsSync(oldPath) && !fs.existsSync(newPath)) {
|
|
46
|
+
// Try to acquire lock (simple file-based lock)
|
|
47
|
+
try {
|
|
48
|
+
if (!fs.existsSync(lockFile)) {
|
|
49
|
+
fs.writeFileSync(lockFile, process.pid.toString());
|
|
50
|
+
logger.info('[Database] Migrating chrome-pilot.db to chromedebug.db');
|
|
51
|
+
fs.renameSync(oldPath, newPath);
|
|
52
|
+
fs.unlinkSync(lockFile);
|
|
53
|
+
logger.info('[Database] Migration complete');
|
|
54
|
+
} else {
|
|
55
|
+
// Wait for other process to finish migration
|
|
56
|
+
let attempts = 0;
|
|
57
|
+
while (fs.existsSync(lockFile) && attempts < 10) {
|
|
58
|
+
// Sleep for 100ms (blocking - acceptable for startup migration)
|
|
59
|
+
const start = Date.now();
|
|
60
|
+
while (Date.now() - start < 100) { /* busy wait */ }
|
|
61
|
+
attempts++;
|
|
62
|
+
}
|
|
63
|
+
// Clean up stale lock if needed
|
|
64
|
+
if (fs.existsSync(lockFile) && !fs.existsSync(newPath)) {
|
|
65
|
+
try { fs.unlinkSync(lockFile); } catch {}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} catch (error) {
|
|
69
|
+
logger.error('[Database] Migration failed:', error);
|
|
70
|
+
// Clean up lock file if we created it
|
|
71
|
+
if (fs.existsSync(lockFile)) {
|
|
72
|
+
try { fs.unlinkSync(lockFile); } catch {}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return newPath;
|
|
34
78
|
}
|
|
35
79
|
|
|
36
80
|
/**
|
|
@@ -1366,11 +1410,12 @@ class ChromePilotDatabase {
|
|
|
1366
1410
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
|
1367
1411
|
`);
|
|
1368
1412
|
|
|
1369
|
-
|
|
1413
|
+
// sessionId already contains workflow_ prefix from extension, use it directly
|
|
1414
|
+
const recordingId = sessionId;
|
|
1370
1415
|
const timestamp = Date.now();
|
|
1371
|
-
|
|
1416
|
+
|
|
1372
1417
|
stmt.run(
|
|
1373
|
-
recordingId,
|
|
1418
|
+
recordingId,
|
|
1374
1419
|
sessionId,
|
|
1375
1420
|
name,
|
|
1376
1421
|
url,
|
|
@@ -1414,6 +1459,8 @@ class ChromePilotDatabase {
|
|
|
1414
1459
|
return action[camelCaseName] || action[snakeCaseName] || null;
|
|
1415
1460
|
};
|
|
1416
1461
|
|
|
1462
|
+
const screenshotDataToStore = action.screenshot_data || null;
|
|
1463
|
+
|
|
1417
1464
|
stmt.run(
|
|
1418
1465
|
workflowId,
|
|
1419
1466
|
action.index,
|
|
@@ -1425,7 +1472,7 @@ class ChromePilotDatabase {
|
|
|
1425
1472
|
action.text || null,
|
|
1426
1473
|
action.placeholder || null,
|
|
1427
1474
|
action.timestamp,
|
|
1428
|
-
|
|
1475
|
+
screenshotDataToStore,
|
|
1429
1476
|
action.component || null, // For function traces
|
|
1430
1477
|
action.args ? JSON.stringify(action.args) : null, // Store args as JSON string
|
|
1431
1478
|
action.stack || null, // For function traces
|
|
@@ -1436,6 +1483,7 @@ class ChromePilotDatabase {
|
|
|
1436
1483
|
safeJsonStringify(getEnhancedFieldData(action, 'elementState', 'element_state'), 2048), // 2KB limit
|
|
1437
1484
|
safeJsonStringify(getEnhancedFieldData(action, 'performanceMetrics', 'performance_metrics'), 1024) // 1KB limit
|
|
1438
1485
|
);
|
|
1486
|
+
|
|
1439
1487
|
storedCount++;
|
|
1440
1488
|
}
|
|
1441
1489
|
|
|
@@ -1452,6 +1500,7 @@ class ChromePilotDatabase {
|
|
|
1452
1500
|
`);
|
|
1453
1501
|
updateStmt.run(storedCount, workflowId);
|
|
1454
1502
|
|
|
1503
|
+
logger.debug('[SCREENSHOT-DEBUG] database.storeWorkflowActions - EXIT');
|
|
1455
1504
|
return { success: true, storedCount };
|
|
1456
1505
|
}
|
|
1457
1506
|
|
|
@@ -1560,7 +1609,7 @@ class ChromePilotDatabase {
|
|
|
1560
1609
|
text: action.text,
|
|
1561
1610
|
placeholder: action.placeholder,
|
|
1562
1611
|
timestamp: action.timestamp,
|
|
1563
|
-
|
|
1612
|
+
screenshot: action.screenshot_data // Map screenshot_data to screenshot for consistency
|
|
1564
1613
|
};
|
|
1565
1614
|
|
|
1566
1615
|
// Add function trace fields if they exist
|
|
@@ -2218,7 +2267,10 @@ class ChromePilotDatabase {
|
|
|
2218
2267
|
}
|
|
2219
2268
|
|
|
2220
2269
|
// Export singleton instance (uses default centralized path)
|
|
2221
|
-
export const database = new
|
|
2270
|
+
export const database = new ChromeDebugDatabase();
|
|
2222
2271
|
|
|
2223
2272
|
// Export class for custom instantiation if needed
|
|
2224
|
-
export {
|
|
2273
|
+
export { ChromeDebugDatabase };
|
|
2274
|
+
|
|
2275
|
+
// Backward compatibility alias
|
|
2276
|
+
export { ChromeDebugDatabase as ChromePilotDatabase };
|
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
|
@@ -26,7 +26,7 @@ interceptStdout();
|
|
|
26
26
|
/**
|
|
27
27
|
* Main application class that orchestrates all components
|
|
28
28
|
*/
|
|
29
|
-
class
|
|
29
|
+
class ChromeDebugApp {
|
|
30
30
|
constructor() {
|
|
31
31
|
this.chromeController = null;
|
|
32
32
|
this.chromeService = null;
|
|
@@ -105,7 +105,7 @@ class ChromePilotApp {
|
|
|
105
105
|
|
|
106
106
|
try {
|
|
107
107
|
// Clean up any existing Chrome Debug processes
|
|
108
|
-
const cleanupResult = await this.processManager.
|
|
108
|
+
const cleanupResult = await this.processManager.cleanupChromeDebugProcesses();
|
|
109
109
|
if (cleanupResult.total > 0) {
|
|
110
110
|
logger.debug(`Cleaned up ${cleanupResult.killed.length} existing processes`);
|
|
111
111
|
}
|
|
@@ -242,7 +242,7 @@ Options:
|
|
|
242
242
|
--watch Watch for file changes (development mode)
|
|
243
243
|
--debug Enable debug logging
|
|
244
244
|
--session-id ID Set custom session ID for resource isolation
|
|
245
|
-
--db-path PATH Set custom database file path (default: ~/.chromedebug/
|
|
245
|
+
--db-path PATH Set custom database file path (default: ~/.chromedebug/chromedebug.db)
|
|
246
246
|
--no-cleanup Skip cleanup of dead processes on startup
|
|
247
247
|
--verbose Enable verbose logging including session info
|
|
248
248
|
--allow-multiple Allow multiple ChromeDebug instances (default: kill others on startup)
|
|
@@ -255,7 +255,7 @@ Session Isolation:
|
|
|
255
255
|
without interfering with each other.
|
|
256
256
|
|
|
257
257
|
Database Location:
|
|
258
|
-
By default, ChromeDebug uses a centralized database at ~/.chromedebug/
|
|
258
|
+
By default, ChromeDebug uses a centralized database at ~/.chromedebug/chromedebug.db
|
|
259
259
|
This allows recordings from all projects to be accessible in one place.
|
|
260
260
|
Use --db-path to override, or set CHROMEDEBUG_DB_PATH environment variable.
|
|
261
261
|
|
|
@@ -296,11 +296,14 @@ For more information, see the documentation in CLAUDE.md
|
|
|
296
296
|
}
|
|
297
297
|
|
|
298
298
|
// Export for library usage and testing
|
|
299
|
-
export {
|
|
299
|
+
export { ChromeDebugApp };
|
|
300
|
+
|
|
301
|
+
// Backward compatibility alias
|
|
302
|
+
export { ChromeDebugApp as ChromePilotApp };
|
|
300
303
|
|
|
301
304
|
// Auto-start when run directly (not imported as a module)
|
|
302
305
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
303
|
-
const app = new
|
|
306
|
+
const app = new ChromeDebugApp();
|
|
304
307
|
|
|
305
308
|
// Handle graceful shutdown
|
|
306
309
|
process.on('SIGINT', async () => {
|
package/src/mcp/server.js
CHANGED
|
@@ -62,7 +62,7 @@ async function safeKillProcess(pid, signal = 'SIGTERM') {
|
|
|
62
62
|
* Process cleanup is skipped on Windows to allow the server to start. A proper cross-platform
|
|
63
63
|
* solution will be implemented in a future update.
|
|
64
64
|
*/
|
|
65
|
-
async function
|
|
65
|
+
async function findChromeDebugProcesses() {
|
|
66
66
|
// Skip process cleanup on Windows - ps command doesn't exist
|
|
67
67
|
// This allows the server to start on Windows without crashing
|
|
68
68
|
// TODO: Implement proper Windows process management using tasklist or Node.js process handles
|
|
@@ -107,6 +107,7 @@ async function findChromePilotProcesses() {
|
|
|
107
107
|
if (!pid || pid === currentPid) continue;
|
|
108
108
|
|
|
109
109
|
// Check if it's a ChromeDebug MCP related process
|
|
110
|
+
// Look for both new and old process names for backward compatibility
|
|
110
111
|
let processType = '';
|
|
111
112
|
if (line.includes('src/index.js')) {
|
|
112
113
|
processType = 'MCP server';
|
|
@@ -117,7 +118,7 @@ async function findChromePilotProcesses() {
|
|
|
117
118
|
} else if (line.includes('http-server.js')) {
|
|
118
119
|
processType = 'HTTP server';
|
|
119
120
|
pidsToKill.push(pid);
|
|
120
|
-
} else if (line.includes('chrome-pilot') && line.includes('node')) {
|
|
121
|
+
} else if ((line.includes('chromedebug') || line.includes('chrome-pilot') || line.includes('ChromePilot')) && line.includes('node')) {
|
|
121
122
|
processType = 'ChromeDebug MCP process';
|
|
122
123
|
pidsToKill.push(pid);
|
|
123
124
|
}
|
|
@@ -221,8 +222,8 @@ export class ProcessManager {
|
|
|
221
222
|
* Finds and kills ChromeDebug MCP processes
|
|
222
223
|
* @returns {Promise<Object>} Cleanup results
|
|
223
224
|
*/
|
|
224
|
-
async
|
|
225
|
-
const { pidsToKill, processDescriptions } = await
|
|
225
|
+
async cleanupChromeDebugProcesses() {
|
|
226
|
+
const { pidsToKill, processDescriptions } = await findChromeDebugProcesses();
|
|
226
227
|
|
|
227
228
|
const results = {
|
|
228
229
|
found: processDescriptions,
|
|
@@ -326,5 +327,8 @@ export class ProcessManager {
|
|
|
326
327
|
}
|
|
327
328
|
}
|
|
328
329
|
|
|
329
|
-
// Export utility functions
|
|
330
|
-
export { validateProcessId, safeKillProcess,
|
|
330
|
+
// Export utility functions
|
|
331
|
+
export { validateProcessId, safeKillProcess, findChromeDebugProcesses };
|
|
332
|
+
|
|
333
|
+
// Backward compatibility aliases
|
|
334
|
+
export { findChromeDebugProcesses as findChromePilotProcesses };
|
|
@@ -306,7 +306,7 @@ export async function killAllRegisteredProcesses() {
|
|
|
306
306
|
* Find ChromeDebug processes using pattern matching (for untracked processes)
|
|
307
307
|
* This is a fallback for processes started before tracking was implemented
|
|
308
308
|
*/
|
|
309
|
-
export async function
|
|
309
|
+
export async function findUntrackedChromeDebugProcesses() {
|
|
310
310
|
const currentPid = process.pid;
|
|
311
311
|
const foundProcesses = [];
|
|
312
312
|
|
|
@@ -342,6 +342,7 @@ export async function findUntrackedChromePilotProcesses() {
|
|
|
342
342
|
if (registeredPids.has(pid)) continue;
|
|
343
343
|
|
|
344
344
|
// Check if it's a ChromeDebug/ChromePilot process
|
|
345
|
+
// Look for both new and old process names for backward compatibility
|
|
345
346
|
let processType = '';
|
|
346
347
|
if (line.includes('src/index.js')) {
|
|
347
348
|
processType = 'untracked-mcp-server';
|
|
@@ -349,7 +350,7 @@ export async function findUntrackedChromePilotProcesses() {
|
|
|
349
350
|
processType = 'untracked-http-server';
|
|
350
351
|
} else if (line.includes('standalone-server.js')) {
|
|
351
352
|
processType = 'untracked-standalone-server';
|
|
352
|
-
} else if (line.includes('chromedebug-mcp') && line.includes('node')) {
|
|
353
|
+
} else if ((line.includes('chromedebug-mcp') || line.includes('chrome-pilot') || line.includes('ChromePilot')) && line.includes('node')) {
|
|
353
354
|
processType = 'untracked-chromedebug-process';
|
|
354
355
|
}
|
|
355
356
|
|
|
@@ -380,8 +381,8 @@ export async function findUntrackedChromePilotProcesses() {
|
|
|
380
381
|
/**
|
|
381
382
|
* Kill untracked ChromeDebug processes (emergency cleanup)
|
|
382
383
|
*/
|
|
383
|
-
export async function
|
|
384
|
-
const untrackedProcesses = await
|
|
384
|
+
export async function killUntrackedChromeDebugProcesses() {
|
|
385
|
+
const untrackedProcesses = await findUntrackedChromeDebugProcesses();
|
|
385
386
|
const results = {
|
|
386
387
|
found: untrackedProcesses,
|
|
387
388
|
killed: [],
|
|
@@ -571,4 +572,8 @@ export async function cleanupCurrentSession() {
|
|
|
571
572
|
}
|
|
572
573
|
|
|
573
574
|
return results;
|
|
574
|
-
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Backward compatibility aliases
|
|
578
|
+
export { findUntrackedChromeDebugProcesses as findUntrackedChromePilotProcesses };
|
|
579
|
+
export { killUntrackedChromeDebugProcesses as killUntrackedChromePilotProcesses };
|
|
@@ -13,14 +13,29 @@
|
|
|
13
13
|
* - Profile lifecycle management with proper resource tracking
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { promises as fs } from 'fs';
|
|
16
|
+
import { promises as fs, existsSync } from 'fs';
|
|
17
17
|
import path from 'path';
|
|
18
18
|
import os from 'os';
|
|
19
19
|
import { randomUUID } from 'crypto';
|
|
20
20
|
|
|
21
21
|
export class ProfileManager {
|
|
22
22
|
constructor(options = {}) {
|
|
23
|
-
|
|
23
|
+
// Use new directory name, but check old directory for migration
|
|
24
|
+
const defaultDir = path.join(os.tmpdir(), 'chromedebug-profiles');
|
|
25
|
+
const oldDir = path.join(os.tmpdir(), 'chrome-pilot-profiles');
|
|
26
|
+
|
|
27
|
+
// If old directory exists and new doesn't, use old directory for compatibility
|
|
28
|
+
let profilesDir = defaultDir;
|
|
29
|
+
try {
|
|
30
|
+
if (!options.profilesDir && existsSync(oldDir) && !existsSync(defaultDir)) {
|
|
31
|
+
profilesDir = oldDir;
|
|
32
|
+
console.error(`[ProfileManager] Using existing chrome-pilot-profiles directory for backward compatibility`);
|
|
33
|
+
}
|
|
34
|
+
} catch (error) {
|
|
35
|
+
// Ignore errors, use default
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this.profilesDir = options.profilesDir || profilesDir;
|
|
24
39
|
this.activeProfiles = new Map(); // sessionId -> { profilePath, claudeInstanceId, createdAt, size }
|
|
25
40
|
this.initialized = false;
|
|
26
41
|
|
|
@@ -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().allow('').optional(),
|
|
31
|
+
text: Joi.string().allow('').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
|
package/src/index-direct.js
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
-
import {
|
|
4
|
-
ListToolsRequestSchema,
|
|
5
|
-
CallToolRequestSchema
|
|
6
|
-
} from '@modelcontextprotocol/sdk/types.js';
|
|
7
|
-
import { ChromeController } from './chrome-controller.js';
|
|
8
|
-
|
|
9
|
-
// Create a single Chrome controller instance for this MCP server
|
|
10
|
-
const chromeController = new ChromeController();
|
|
11
|
-
|
|
12
|
-
const server = new Server(
|
|
13
|
-
{
|
|
14
|
-
name: 'chrome-pilot',
|
|
15
|
-
version: '1.0.0',
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
capabilities: {
|
|
19
|
-
tools: {},
|
|
20
|
-
},
|
|
21
|
-
}
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
const tools = [
|
|
25
|
-
{
|
|
26
|
-
name: 'launch_chrome',
|
|
27
|
-
description: 'Launch a Chrome browser instance for this MCP server',
|
|
28
|
-
inputSchema: {
|
|
29
|
-
type: 'object',
|
|
30
|
-
properties: {},
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
name: 'get_chrome_info',
|
|
35
|
-
description: 'Get Chrome debugging port and connection info for extensions',
|
|
36
|
-
inputSchema: {
|
|
37
|
-
type: 'object',
|
|
38
|
-
properties: {},
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
name: 'connect_to_existing_chrome',
|
|
43
|
-
description: 'Connect to an existing Chrome instance with debugging enabled',
|
|
44
|
-
inputSchema: {
|
|
45
|
-
type: 'object',
|
|
46
|
-
properties: {
|
|
47
|
-
port: {
|
|
48
|
-
type: 'number',
|
|
49
|
-
description: 'Debugging port Chrome is running on (default: 9222)',
|
|
50
|
-
default: 9222,
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
// ... other tools remain the same
|
|
56
|
-
];
|
|
57
|
-
|
|
58
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
59
|
-
return { tools };
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
63
|
-
const { name, arguments: args } = request.params;
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
switch (name) {
|
|
67
|
-
case 'launch_chrome': {
|
|
68
|
-
const result = await chromeController.launch();
|
|
69
|
-
|
|
70
|
-
// Write Chrome info to a known location for the extension
|
|
71
|
-
const chromeInfo = {
|
|
72
|
-
debugPort: result.debugPort,
|
|
73
|
-
wsEndpoint: result.browserWSEndpoint,
|
|
74
|
-
timestamp: new Date().toISOString()
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// The extension can read this file or connect directly to debugPort
|
|
78
|
-
await fs.writeFile(
|
|
79
|
-
'/tmp/chrome-pilot-current.json',
|
|
80
|
-
JSON.stringify(chromeInfo, null, 2)
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
content: [
|
|
85
|
-
{
|
|
86
|
-
type: 'text',
|
|
87
|
-
text: `Chrome launched on debugging port ${result.debugPort}\n\n` +
|
|
88
|
-
`The Chrome extension can now connect directly to:\n` +
|
|
89
|
-
`http://localhost:${result.debugPort}\n\n` +
|
|
90
|
-
JSON.stringify(result, null, 2),
|
|
91
|
-
},
|
|
92
|
-
],
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
case 'get_chrome_info': {
|
|
97
|
-
const status = await chromeController.getConnectionStatus();
|
|
98
|
-
if (!status.connected) {
|
|
99
|
-
return {
|
|
100
|
-
content: [
|
|
101
|
-
{
|
|
102
|
-
type: 'text',
|
|
103
|
-
text: 'Chrome is not connected. Launch Chrome first using launch_chrome.',
|
|
104
|
-
},
|
|
105
|
-
],
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
content: [
|
|
111
|
-
{
|
|
112
|
-
type: 'text',
|
|
113
|
-
text: `Chrome debugging info:\n` +
|
|
114
|
-
`Port: ${status.debugPort}\n` +
|
|
115
|
-
`WebSocket: ${status.browserWSEndpoint}\n\n` +
|
|
116
|
-
`Extensions can connect to: http://localhost:${status.debugPort}`,
|
|
117
|
-
},
|
|
118
|
-
],
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// ... rest of the cases
|
|
123
|
-
}
|
|
124
|
-
} catch (error) {
|
|
125
|
-
return {
|
|
126
|
-
content: [
|
|
127
|
-
{
|
|
128
|
-
type: 'text',
|
|
129
|
-
text: `Error: ${error.message}`,
|
|
130
|
-
},
|
|
131
|
-
],
|
|
132
|
-
isError: true,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
async function main() {
|
|
138
|
-
const transport = new StdioServerTransport();
|
|
139
|
-
await server.connect(transport);
|
|
140
|
-
console.error('Chrome Debug MCP server running (direct mode - no Express)');
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Clean shutdown
|
|
144
|
-
process.on('SIGINT', async () => {
|
|
145
|
-
await chromeController.close();
|
|
146
|
-
process.exit(0);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
process.on('SIGTERM', async () => {
|
|
150
|
-
await chromeController.close();
|
|
151
|
-
process.exit(0);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
main().catch((error) => {
|
|
155
|
-
console.error('Fatal error:', error);
|
|
156
|
-
process.exit(1);
|
|
157
|
-
});
|