@dynamicu/chromedebug-mcp 2.3.0 â 2.4.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/README.md +50 -0
- package/chrome-extension/activation-manager.html +208 -0
- package/chrome-extension/activation-manager.js +187 -0
- package/chrome-extension/background.js +5 -5
- package/chrome-extension/firebase-client.js +135 -1
- package/chrome-extension/popup.js +68 -2
- package/dist/chromedebug-extension-free.zip +0 -0
- package/package.json +1 -1
- package/scripts/postinstall.js +13 -4
- package/scripts/webpack.config.free.cjs +3 -0
- package/scripts/webpack.config.pro.cjs +3 -0
- package/src/chrome-controller.js +107 -106
- package/src/config-loader.js +18 -17
- package/src/database.js +81 -80
- package/src/index.js +35 -24
- package/src/middleware/auth.js +30 -29
- package/src/port-discovery.js +7 -6
- package/src/services/failover-manager.js +19 -18
- package/src/services/project-manager.js +16 -15
- package/src/utils/logger.js +63 -0
package/src/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
registerProcess,
|
|
15
15
|
findActiveSessions
|
|
16
16
|
} from './services/unified-session-manager.js';
|
|
17
|
+
import logger from './utils/logger.js';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Main application class that orchestrates all components
|
|
@@ -46,13 +47,23 @@ class ChromePilotApp {
|
|
|
46
47
|
this.processManager = this.chromeService.getProcessManager();
|
|
47
48
|
|
|
48
49
|
// Initialize Chrome service (starts HTTP server if needed)
|
|
50
|
+
logger.info('đ Starting ChromeDebug MCP services...');
|
|
51
|
+
|
|
49
52
|
const serviceResult = await this.chromeService.initialize({
|
|
50
53
|
...finalOptions,
|
|
51
54
|
sessionManager: this.sessionManager
|
|
52
55
|
});
|
|
53
|
-
|
|
56
|
+
|
|
54
57
|
if (serviceResult.httpServer) {
|
|
55
|
-
|
|
58
|
+
logger.info(`â
HTTP Server Started on port ${serviceResult.httpServer.port}`);
|
|
59
|
+
logger.debug(` Status: ${serviceResult.httpServer.status}`);
|
|
60
|
+
} else if (finalOptions.singleServer) {
|
|
61
|
+
logger.info('âšī¸ HTTP server disabled (--single-server mode)');
|
|
62
|
+
} else {
|
|
63
|
+
logger.error('â ī¸ HTTP server failed to start - Chrome extension will not be able to connect');
|
|
64
|
+
if (serviceResult.httpServer?.error) {
|
|
65
|
+
logger.error(` Error: ${serviceResult.httpServer.error}`);
|
|
66
|
+
}
|
|
56
67
|
}
|
|
57
68
|
|
|
58
69
|
// Create and configure MCP server
|
|
@@ -65,7 +76,7 @@ class ChromePilotApp {
|
|
|
65
76
|
serviceResult
|
|
66
77
|
};
|
|
67
78
|
} catch (error) {
|
|
68
|
-
|
|
79
|
+
logger.error('Failed to initialize Chrome Debug:', error);
|
|
69
80
|
throw error;
|
|
70
81
|
}
|
|
71
82
|
}
|
|
@@ -82,13 +93,13 @@ class ChromePilotApp {
|
|
|
82
93
|
// Clean up any existing Chrome Debug processes
|
|
83
94
|
const cleanupResult = await this.processManager.cleanupChromePilotProcesses();
|
|
84
95
|
if (cleanupResult.total > 0) {
|
|
85
|
-
|
|
96
|
+
logger.debug(`Cleaned up ${cleanupResult.killed.length} existing processes`);
|
|
86
97
|
}
|
|
87
98
|
|
|
88
99
|
// Start the MCP server
|
|
89
100
|
await this.mcpServer.start();
|
|
90
101
|
} catch (error) {
|
|
91
|
-
|
|
102
|
+
logger.error('Failed to start Chrome Debug MCP server:', error);
|
|
92
103
|
process.exit(1);
|
|
93
104
|
}
|
|
94
105
|
}
|
|
@@ -147,7 +158,7 @@ class ChromePilotApp {
|
|
|
147
158
|
if (i + 1 < argv.length) {
|
|
148
159
|
args.sessionId = argv[++i];
|
|
149
160
|
} else {
|
|
150
|
-
|
|
161
|
+
logger.error('--session-id requires a value');
|
|
151
162
|
process.exit(1);
|
|
152
163
|
}
|
|
153
164
|
break;
|
|
@@ -165,7 +176,7 @@ class ChromePilotApp {
|
|
|
165
176
|
if (arg.startsWith('--session-id=')) {
|
|
166
177
|
args.sessionId = arg.split('=')[1];
|
|
167
178
|
} else if (arg.startsWith('--')) {
|
|
168
|
-
|
|
179
|
+
logger.error(`Unknown argument: ${arg}`);
|
|
169
180
|
this.showHelp();
|
|
170
181
|
process.exit(1);
|
|
171
182
|
}
|
|
@@ -215,20 +226,20 @@ For more information, see the documentation in CLAUDE.md
|
|
|
215
226
|
* Graceful shutdown
|
|
216
227
|
*/
|
|
217
228
|
async shutdown() {
|
|
218
|
-
|
|
219
|
-
|
|
229
|
+
logger.info('Shutting down Chrome Debug...');
|
|
230
|
+
|
|
220
231
|
try {
|
|
221
232
|
if (this.chromeService) {
|
|
222
233
|
await this.chromeService.cleanup();
|
|
223
234
|
}
|
|
224
|
-
|
|
235
|
+
|
|
225
236
|
if (this.processManager) {
|
|
226
237
|
await this.processManager.killAllManagedProcesses();
|
|
227
238
|
}
|
|
228
|
-
|
|
229
|
-
|
|
239
|
+
|
|
240
|
+
logger.info('Chrome Debug shutdown complete');
|
|
230
241
|
} catch (error) {
|
|
231
|
-
|
|
242
|
+
logger.error('Error during shutdown:', error);
|
|
232
243
|
}
|
|
233
244
|
}
|
|
234
245
|
}
|
|
@@ -256,18 +267,18 @@ async function main() {
|
|
|
256
267
|
// Log session information if verbose
|
|
257
268
|
if (args.verbose) {
|
|
258
269
|
const sessionInfo = getSessionInfo();
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
270
|
+
logger.debug('Session Information:');
|
|
271
|
+
logger.debug(` Session ID: ${sessionInfo.sessionId}`);
|
|
272
|
+
logger.debug(` PID: ${sessionInfo.pid}`);
|
|
273
|
+
logger.debug(` Port: ${sessionInfo.port}`);
|
|
274
|
+
logger.debug(` Session File: ${sessionInfo.sessionFile}`);
|
|
264
275
|
|
|
265
276
|
// Show other active sessions
|
|
266
277
|
const activeSessions = await findActiveSessions();
|
|
267
278
|
if (activeSessions.length > 1) {
|
|
268
|
-
|
|
279
|
+
logger.debug(` Other active sessions: ${activeSessions.length - 1}`);
|
|
269
280
|
activeSessions.filter(s => s.sessionId !== sessionInfo.sessionId).forEach(session => {
|
|
270
|
-
|
|
281
|
+
logger.debug(` - ${session.sessionId} (PID: ${session.pid}, Port: ${session.port})`);
|
|
271
282
|
});
|
|
272
283
|
}
|
|
273
284
|
}
|
|
@@ -277,19 +288,19 @@ async function main() {
|
|
|
277
288
|
|
|
278
289
|
await app.start();
|
|
279
290
|
} catch (error) {
|
|
280
|
-
|
|
291
|
+
logger.error('Fatal error starting Chrome Debug:', error);
|
|
281
292
|
process.exit(1);
|
|
282
293
|
}
|
|
283
294
|
}
|
|
284
295
|
|
|
285
296
|
// Handle graceful shutdown
|
|
286
297
|
process.on('SIGINT', async () => {
|
|
287
|
-
|
|
298
|
+
logger.info('Received SIGINT, shutting down gracefully...');
|
|
288
299
|
process.exit(0);
|
|
289
300
|
});
|
|
290
301
|
|
|
291
302
|
process.on('SIGTERM', async () => {
|
|
292
|
-
|
|
303
|
+
logger.info('Received SIGTERM, shutting down gracefully...');
|
|
293
304
|
process.exit(0);
|
|
294
305
|
});
|
|
295
306
|
|
|
@@ -299,7 +310,7 @@ export { ChromePilotApp };
|
|
|
299
310
|
// Run if this is the main module
|
|
300
311
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
301
312
|
main().catch((error) => {
|
|
302
|
-
|
|
313
|
+
logger.error('Unhandled error:', error);
|
|
303
314
|
process.exit(1);
|
|
304
315
|
});
|
|
305
316
|
}
|
package/src/middleware/auth.js
CHANGED
|
@@ -4,6 +4,7 @@ import { v4 as uuidv4 } from 'uuid';
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import fs from 'fs';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
|
+
import logger from '../utils/logger.js';
|
|
7
8
|
|
|
8
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
10
|
const __dirname = path.dirname(__filename);
|
|
@@ -26,7 +27,7 @@ try {
|
|
|
26
27
|
apiKeys = JSON.parse(fs.readFileSync(API_KEYS_FILE, 'utf8'));
|
|
27
28
|
}
|
|
28
29
|
} catch (error) {
|
|
29
|
-
|
|
30
|
+
logger.error('Error loading API keys:', error);
|
|
30
31
|
apiKeys = [];
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -35,7 +36,7 @@ function saveApiKeys() {
|
|
|
35
36
|
try {
|
|
36
37
|
fs.writeFileSync(API_KEYS_FILE, JSON.stringify(apiKeys, null, 2));
|
|
37
38
|
} catch (error) {
|
|
38
|
-
|
|
39
|
+
logger.error('Error saving API keys:', error);
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
|
|
@@ -134,17 +135,17 @@ function verifyJWT(token) {
|
|
|
134
135
|
// Authentication middleware
|
|
135
136
|
export function authenticate(req, res, next) {
|
|
136
137
|
// Debug logging for authentication flow
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
138
|
+
logger.debug(`[AUTH-DEBUG] ${new Date().toISOString()} - ${req.method} ${req.originalUrl}`);
|
|
139
|
+
logger.debug(`[AUTH-DEBUG] Client IP: ${req.ip}`);
|
|
140
|
+
logger.debug(`[AUTH-DEBUG] X-Forwarded-For: ${req.headers['x-forwarded-for'] || 'none'}`);
|
|
141
|
+
logger.debug(`[AUTH-DEBUG] User-Agent: ${req.headers['user-agent'] || 'none'}`);
|
|
142
|
+
logger.debug(`[AUTH-DEBUG] Authorization header present: ${!!req.headers.authorization}`);
|
|
143
|
+
logger.debug(`[AUTH-DEBUG] X-API-Key header present: ${!!req.headers['x-api-key']}`);
|
|
144
|
+
logger.debug(`[AUTH-DEBUG] X-Service-Token header present: ${!!req.headers['x-service-token']}`);
|
|
145
|
+
|
|
145
146
|
// Allow localhost requests from Chrome extension
|
|
146
147
|
const isLocalhost = req.ip === '::1' || req.ip === '127.0.0.1' || req.ip === '::ffff:127.0.0.1';
|
|
147
|
-
|
|
148
|
+
logger.debug(`[AUTH-DEBUG] Is localhost: ${isLocalhost}`);
|
|
148
149
|
|
|
149
150
|
if (isLocalhost) {
|
|
150
151
|
// Set a synthetic user for localhost requests with explicit permissions
|
|
@@ -162,7 +163,7 @@ export function authenticate(req, res, next) {
|
|
|
162
163
|
PERMISSIONS.FRAME_WRITE
|
|
163
164
|
]
|
|
164
165
|
};
|
|
165
|
-
|
|
166
|
+
logger.debug(`[AUTH-DEBUG] â
Localhost bypass granted for user:`, req.user);
|
|
166
167
|
return next();
|
|
167
168
|
}
|
|
168
169
|
|
|
@@ -208,7 +209,7 @@ export function authenticate(req, res, next) {
|
|
|
208
209
|
}
|
|
209
210
|
|
|
210
211
|
if (!user) {
|
|
211
|
-
|
|
212
|
+
logger.debug(`[AUTH-DEBUG] â Authentication failed - no valid credentials found`);
|
|
212
213
|
return res.status(401).json({
|
|
213
214
|
error: 'Authentication required',
|
|
214
215
|
message: 'Please provide a valid API key, Bearer token, or service token',
|
|
@@ -219,8 +220,8 @@ export function authenticate(req, res, next) {
|
|
|
219
220
|
}
|
|
220
221
|
});
|
|
221
222
|
}
|
|
222
|
-
|
|
223
|
-
|
|
223
|
+
|
|
224
|
+
logger.debug(`[AUTH-DEBUG] â
Authentication successful for user:`, {
|
|
224
225
|
id: user.id,
|
|
225
226
|
name: user.name,
|
|
226
227
|
role: user.role,
|
|
@@ -316,11 +317,11 @@ export function deleteApiKey(keyId) {
|
|
|
316
317
|
// Initialize with default admin key if no keys exist
|
|
317
318
|
if (apiKeys.length === 0) {
|
|
318
319
|
const defaultAdmin = generateApiKey('Default Admin', ROLES.ADMIN);
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
320
|
+
logger.info('=== Chrome Debug Security Initialization ===');
|
|
321
|
+
logger.info('Generated default admin API key:');
|
|
322
|
+
logger.info(`API Key: ${defaultAdmin.apiKey}`);
|
|
323
|
+
logger.info('Please save this key securely and create additional keys as needed.');
|
|
324
|
+
logger.info('============================================');
|
|
324
325
|
}
|
|
325
326
|
|
|
326
327
|
// Generate MCP service token
|
|
@@ -333,7 +334,7 @@ export function generateServiceToken() {
|
|
|
333
334
|
function validateServiceToken(token) {
|
|
334
335
|
const expectedToken = process.env.MCP_SERVICE_TOKEN;
|
|
335
336
|
if (!expectedToken) {
|
|
336
|
-
|
|
337
|
+
logger.warn('MCP_SERVICE_TOKEN not set. Service authentication disabled.');
|
|
337
338
|
return false;
|
|
338
339
|
}
|
|
339
340
|
return token === expectedToken;
|
|
@@ -344,23 +345,23 @@ function initializeMcpServiceToken() {
|
|
|
344
345
|
if (!process.env.MCP_SERVICE_TOKEN) {
|
|
345
346
|
const serviceToken = generateServiceToken();
|
|
346
347
|
process.env.MCP_SERVICE_TOKEN = serviceToken;
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
348
|
+
logger.info('=== ChromeDebug MCP Service Token ===');
|
|
349
|
+
logger.info(`Generated MCP service token: ${serviceToken}`);
|
|
350
|
+
logger.info('This token allows MCP clients to access recordings.');
|
|
351
|
+
logger.info('======================================');
|
|
352
|
+
|
|
352
353
|
// Optionally save to .env file for persistence
|
|
353
354
|
try {
|
|
354
355
|
const envPath = path.join(__dirname, '../../.env');
|
|
355
356
|
const envContent = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf8') : '';
|
|
356
|
-
|
|
357
|
+
|
|
357
358
|
if (!envContent.includes('MCP_SERVICE_TOKEN=')) {
|
|
358
359
|
const newEnvContent = envContent + (envContent.endsWith('\n') ? '' : '\n') + `MCP_SERVICE_TOKEN=${serviceToken}\n`;
|
|
359
360
|
fs.writeFileSync(envPath, newEnvContent);
|
|
360
|
-
|
|
361
|
+
logger.info('MCP service token saved to .env file');
|
|
361
362
|
}
|
|
362
363
|
} catch (error) {
|
|
363
|
-
|
|
364
|
+
logger.debug('Could not save MCP service token to .env file:', error.message);
|
|
364
365
|
}
|
|
365
366
|
}
|
|
366
367
|
}
|
package/src/port-discovery.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import fs from 'fs';
|
|
13
13
|
import path from 'path';
|
|
14
14
|
import { fileURLToPath } from 'url';
|
|
15
|
+
import logger from './utils/logger.js';
|
|
15
16
|
|
|
16
17
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
18
|
|
|
@@ -31,9 +32,9 @@ export function writePortFile(port) {
|
|
|
31
32
|
|
|
32
33
|
try {
|
|
33
34
|
fs.writeFileSync(portFilePath, port.toString());
|
|
34
|
-
|
|
35
|
+
logger.debug(`Port ${port} written to ${portFilePath}`);
|
|
35
36
|
} catch (error) {
|
|
36
|
-
|
|
37
|
+
logger.error('Failed to write port file:', error);
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
|
|
@@ -52,7 +53,7 @@ export function readPortFile() {
|
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
} catch (error) {
|
|
55
|
-
|
|
56
|
+
logger.error('Failed to read port file:', error);
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
return null;
|
|
@@ -67,10 +68,10 @@ export function removePortFile() {
|
|
|
67
68
|
try {
|
|
68
69
|
if (fs.existsSync(portFilePath)) {
|
|
69
70
|
fs.unlinkSync(portFilePath);
|
|
70
|
-
|
|
71
|
+
logger.debug(`Removed port file: ${portFilePath}`);
|
|
71
72
|
}
|
|
72
73
|
} catch (error) {
|
|
73
|
-
|
|
74
|
+
logger.error('Failed to remove port file:', error);
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
|
|
@@ -114,7 +115,7 @@ export async function discoverServer(preferredPorts = [3000, 3001, 3002, 3028],
|
|
|
114
115
|
if (response.ok) {
|
|
115
116
|
const data = await response.json();
|
|
116
117
|
if (data.status === 'online') {
|
|
117
|
-
|
|
118
|
+
logger.debug(`Discovered ChromeDebug server on port ${port}`);
|
|
118
119
|
return port;
|
|
119
120
|
}
|
|
120
121
|
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { EventEmitter } from 'events';
|
|
17
|
+
import logger from '../utils/logger.js';
|
|
17
18
|
|
|
18
19
|
export class FailoverManager extends EventEmitter {
|
|
19
20
|
constructor(options = {}) {
|
|
@@ -51,7 +52,7 @@ export class FailoverManager extends EventEmitter {
|
|
|
51
52
|
this.healthCheckTimer = null;
|
|
52
53
|
this.recoveryTimer = null;
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
logger.debug('[FailoverManager] Initialized with failover management');
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
/**
|
|
@@ -65,9 +66,9 @@ export class FailoverManager extends EventEmitter {
|
|
|
65
66
|
this.startHealthMonitoring();
|
|
66
67
|
|
|
67
68
|
this.initialized = true;
|
|
68
|
-
|
|
69
|
+
logger.debug('[FailoverManager] Initialized successfully');
|
|
69
70
|
} catch (error) {
|
|
70
|
-
|
|
71
|
+
logger.error('[FailoverManager] Initialization failed:', error);
|
|
71
72
|
throw error;
|
|
72
73
|
}
|
|
73
74
|
}
|
|
@@ -111,7 +112,7 @@ export class FailoverManager extends EventEmitter {
|
|
|
111
112
|
this.daemonHealthy = false;
|
|
112
113
|
this.lastDaemonCheck = new Date();
|
|
113
114
|
|
|
114
|
-
|
|
115
|
+
logger.warn(`[FailoverManager] Daemon health check failed: ${error.message}`);
|
|
115
116
|
return false;
|
|
116
117
|
}
|
|
117
118
|
}
|
|
@@ -145,7 +146,7 @@ export class FailoverManager extends EventEmitter {
|
|
|
145
146
|
async shouldUseFallback(operation = 'unknown') {
|
|
146
147
|
// Always use fallback if daemon is known to be unhealthy
|
|
147
148
|
if (!this.daemonHealthy) {
|
|
148
|
-
|
|
149
|
+
logger.debug(`[FailoverManager] Using fallback for ${operation}: daemon is unhealthy`);
|
|
149
150
|
return true;
|
|
150
151
|
}
|
|
151
152
|
|
|
@@ -153,14 +154,14 @@ export class FailoverManager extends EventEmitter {
|
|
|
153
154
|
if (this.metrics.lastFailureTime) {
|
|
154
155
|
const timeSinceFailure = Date.now() - this.metrics.lastFailureTime;
|
|
155
156
|
if (timeSinceFailure < this.config.fallbackCooldownMs) {
|
|
156
|
-
|
|
157
|
+
logger.debug(`[FailoverManager] Using fallback for ${operation}: in cooldown period`);
|
|
157
158
|
return true;
|
|
158
159
|
}
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
// Check if we've had too many consecutive failures
|
|
162
163
|
if (this.metrics.consecutiveFailures >= this.config.maxConsecutiveFailures) {
|
|
163
|
-
|
|
164
|
+
logger.debug(`[FailoverManager] Using fallback for ${operation}: too many consecutive failures`);
|
|
164
165
|
return true;
|
|
165
166
|
}
|
|
166
167
|
|
|
@@ -170,7 +171,7 @@ export class FailoverManager extends EventEmitter {
|
|
|
170
171
|
// Perform a quick health check before critical operations
|
|
171
172
|
const isHealthy = await this.checkDaemonHealth();
|
|
172
173
|
if (!isHealthy) {
|
|
173
|
-
|
|
174
|
+
logger.debug(`[FailoverManager] Using fallback for critical ${operation}: health check failed`);
|
|
174
175
|
return true;
|
|
175
176
|
}
|
|
176
177
|
}
|
|
@@ -222,7 +223,7 @@ export class FailoverManager extends EventEmitter {
|
|
|
222
223
|
this.fallbackActive = true;
|
|
223
224
|
this.metrics.totalFallbacks++;
|
|
224
225
|
|
|
225
|
-
|
|
226
|
+
logger.warn('[FailoverManager] Triggering fallback to individual browsers');
|
|
226
227
|
|
|
227
228
|
// Start recovery monitoring
|
|
228
229
|
this.startRecoveryMonitoring();
|
|
@@ -247,7 +248,7 @@ export class FailoverManager extends EventEmitter {
|
|
|
247
248
|
}
|
|
248
249
|
}, this.config.healthCheckIntervalMs);
|
|
249
250
|
|
|
250
|
-
|
|
251
|
+
logger.debug('[FailoverManager] Started daemon health monitoring');
|
|
251
252
|
}
|
|
252
253
|
|
|
253
254
|
/**
|
|
@@ -264,7 +265,7 @@ export class FailoverManager extends EventEmitter {
|
|
|
264
265
|
}
|
|
265
266
|
}, this.config.recoveryCheckIntervalMs);
|
|
266
267
|
|
|
267
|
-
|
|
268
|
+
logger.debug('[FailoverManager] Started recovery monitoring');
|
|
268
269
|
}
|
|
269
270
|
|
|
270
271
|
/**
|
|
@@ -276,7 +277,7 @@ export class FailoverManager extends EventEmitter {
|
|
|
276
277
|
this.recoveryInProgress = true;
|
|
277
278
|
|
|
278
279
|
try {
|
|
279
|
-
|
|
280
|
+
logger.debug('[FailoverManager] Checking for daemon recovery...');
|
|
280
281
|
|
|
281
282
|
// Perform multiple health checks to ensure stability
|
|
282
283
|
const healthChecks = [];
|
|
@@ -289,13 +290,13 @@ export class FailoverManager extends EventEmitter {
|
|
|
289
290
|
const allHealthy = results.every(result => result === true);
|
|
290
291
|
|
|
291
292
|
if (allHealthy) {
|
|
292
|
-
|
|
293
|
+
logger.debug('[FailoverManager] Daemon has recovered, disabling fallback');
|
|
293
294
|
this.recoveredFromFallback();
|
|
294
295
|
} else {
|
|
295
|
-
|
|
296
|
+
logger.debug('[FailoverManager] Daemon still unhealthy, continuing fallback');
|
|
296
297
|
}
|
|
297
298
|
} catch (error) {
|
|
298
|
-
|
|
299
|
+
logger.warn('[FailoverManager] Error during recovery check:', error);
|
|
299
300
|
} finally {
|
|
300
301
|
this.recoveryInProgress = false;
|
|
301
302
|
}
|
|
@@ -354,7 +355,7 @@ export class FailoverManager extends EventEmitter {
|
|
|
354
355
|
this.recoveryTimer = null;
|
|
355
356
|
}
|
|
356
357
|
|
|
357
|
-
|
|
358
|
+
logger.debug('[FailoverManager] Failover state reset');
|
|
358
359
|
this.emit('state-reset');
|
|
359
360
|
}
|
|
360
361
|
|
|
@@ -391,7 +392,7 @@ export class FailoverManager extends EventEmitter {
|
|
|
391
392
|
* Cleanup method for graceful shutdown
|
|
392
393
|
*/
|
|
393
394
|
async cleanup() {
|
|
394
|
-
|
|
395
|
+
logger.debug('[FailoverManager] Cleaning up...');
|
|
395
396
|
|
|
396
397
|
// Stop all timers
|
|
397
398
|
if (this.healthCheckTimer) {
|
|
@@ -407,6 +408,6 @@ export class FailoverManager extends EventEmitter {
|
|
|
407
408
|
// Remove all listeners
|
|
408
409
|
this.removeAllListeners();
|
|
409
410
|
|
|
410
|
-
|
|
411
|
+
logger.debug('[FailoverManager] Cleanup completed');
|
|
411
412
|
}
|
|
412
413
|
}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import { promises as fs, existsSync as fsExistsSync } from 'fs';
|
|
16
16
|
import path from 'path';
|
|
17
17
|
import os from 'os';
|
|
18
|
+
import logger from '../utils/logger.js';
|
|
18
19
|
|
|
19
20
|
export class ProjectManager {
|
|
20
21
|
constructor() {
|
|
@@ -34,7 +35,7 @@ export class ProjectManager {
|
|
|
34
35
|
const projectRoot = this.findProjectRoot(resolvedPath);
|
|
35
36
|
|
|
36
37
|
if (!projectRoot) {
|
|
37
|
-
|
|
38
|
+
logger.debug('[ProjectManager] No valid project detected from:', resolvedPath);
|
|
38
39
|
return null;
|
|
39
40
|
}
|
|
40
41
|
|
|
@@ -48,12 +49,12 @@ export class ProjectManager {
|
|
|
48
49
|
hasMarkers: markers
|
|
49
50
|
};
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
logger.debug(`[ProjectManager] Detected ${projectType} project at: ${projectRoot}`);
|
|
53
|
+
logger.debug(`[ProjectManager] Project markers found: ${markers.join(', ')}`);
|
|
54
|
+
|
|
54
55
|
return this.projectInfo;
|
|
55
56
|
} catch (error) {
|
|
56
|
-
|
|
57
|
+
logger.warn('[ProjectManager] Error during project detection:', error.message);
|
|
57
58
|
return null;
|
|
58
59
|
}
|
|
59
60
|
}
|
|
@@ -220,7 +221,7 @@ export class ProjectManager {
|
|
|
220
221
|
logsDir: path.join(projectDir, '.chromedebug', 'logs')
|
|
221
222
|
};
|
|
222
223
|
|
|
223
|
-
|
|
224
|
+
logger.debug(`[ProjectManager] Initializing project structure in: ${this.projectStructure.baseDir}`);
|
|
224
225
|
|
|
225
226
|
try {
|
|
226
227
|
// Create all necessary directories
|
|
@@ -235,12 +236,12 @@ export class ProjectManager {
|
|
|
235
236
|
await this.updateGitignore(projectDir);
|
|
236
237
|
}
|
|
237
238
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
239
|
+
logger.debug('[ProjectManager] Project structure initialized successfully');
|
|
240
|
+
logger.debug('[ProjectManager] Structure:', this.projectStructure);
|
|
241
|
+
|
|
241
242
|
this.initialized = true;
|
|
242
243
|
} catch (error) {
|
|
243
|
-
|
|
244
|
+
logger.error('[ProjectManager] Failed to initialize project structure:', error);
|
|
244
245
|
throw new Error(`Project structure initialization failed: ${error.message}`);
|
|
245
246
|
}
|
|
246
247
|
}
|
|
@@ -272,11 +273,11 @@ export class ProjectManager {
|
|
|
272
273
|
|
|
273
274
|
if (hasEntry) {
|
|
274
275
|
needsUpdate = false;
|
|
275
|
-
|
|
276
|
+
logger.debug('[ProjectManager] .chromedebug already in .gitignore');
|
|
276
277
|
}
|
|
277
278
|
} catch (error) {
|
|
278
279
|
// .gitignore doesn't exist, will create it
|
|
279
|
-
|
|
280
|
+
logger.debug('[ProjectManager] Creating new .gitignore file');
|
|
280
281
|
}
|
|
281
282
|
|
|
282
283
|
if (needsUpdate) {
|
|
@@ -290,14 +291,14 @@ export class ProjectManager {
|
|
|
290
291
|
# Chrome Debug project-local data
|
|
291
292
|
${chromePilotEntry}
|
|
292
293
|
`;
|
|
293
|
-
|
|
294
|
+
|
|
294
295
|
gitignoreContent += chromePilotSection;
|
|
295
296
|
|
|
296
297
|
await fs.writeFile(gitignorePath, gitignoreContent, 'utf8');
|
|
297
|
-
|
|
298
|
+
logger.debug('[ProjectManager] Added .chromedebug to .gitignore');
|
|
298
299
|
}
|
|
299
300
|
} catch (error) {
|
|
300
|
-
|
|
301
|
+
logger.warn('[ProjectManager] Warning: Could not update .gitignore:', error.message);
|
|
301
302
|
// Don't fail initialization if .gitignore update fails
|
|
302
303
|
}
|
|
303
304
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger utility for ChromeDebug MCP
|
|
3
|
+
*
|
|
4
|
+
* Logging Levels:
|
|
5
|
+
* - ERROR: Critical errors, always shown
|
|
6
|
+
* - WARN: Warnings and fallback scenarios, always shown
|
|
7
|
+
* - INFO: Important events (startup, connections), shown in development/debug mode
|
|
8
|
+
* - DEBUG: Verbose traces, shown only with DEBUG=true
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* import logger from './utils/logger.js';
|
|
12
|
+
* logger.error('Connection failed:', error);
|
|
13
|
+
* logger.warn('Falling back to default port');
|
|
14
|
+
* logger.info('Server started on port', port);
|
|
15
|
+
* logger.debug('Query executed:', sql);
|
|
16
|
+
*
|
|
17
|
+
* Environment Variables:
|
|
18
|
+
* NODE_ENV=development - Shows INFO level and above
|
|
19
|
+
* DEBUG=true or DEBUG=1 - Shows DEBUG level (all logs)
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const isDevelopment = process.env.NODE_ENV === 'development';
|
|
23
|
+
const isDebug = process.env.DEBUG === 'true' || process.env.DEBUG === '1';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Logger with level-based filtering
|
|
27
|
+
* All output goes to stderr to keep stdout clean for MCP JSON-RPC protocol
|
|
28
|
+
*/
|
|
29
|
+
const logger = {
|
|
30
|
+
/**
|
|
31
|
+
* Log critical errors - always shown
|
|
32
|
+
*/
|
|
33
|
+
error: (...args) => {
|
|
34
|
+
console.error('[ERROR]', ...args);
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Log warnings and fallback scenarios - always shown
|
|
39
|
+
*/
|
|
40
|
+
warn: (...args) => {
|
|
41
|
+
console.error('[WARN]', ...args);
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Log important events - shown in development or debug mode
|
|
46
|
+
*/
|
|
47
|
+
info: (...args) => {
|
|
48
|
+
if (isDevelopment || isDebug) {
|
|
49
|
+
console.error('[INFO]', ...args);
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Log verbose traces - shown only with DEBUG=true
|
|
55
|
+
*/
|
|
56
|
+
debug: (...args) => {
|
|
57
|
+
if (isDebug) {
|
|
58
|
+
console.error('[DEBUG]', ...args);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export default logger;
|