@dynamicu/chromedebug-mcp 2.5.14 → 2.6.1
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/package.json +3 -1
- package/scripts/webpack.config.pro.cjs +2 -2
- package/src/capture/memory-manager.js +3 -3
- package/src/cli.js +4 -0
- package/src/commands/install-pro.js +37 -37
- package/src/firebase-license-manager.js +4 -4
- package/src/http-server.js +144 -140
- package/src/index-modular.js +1 -1
- package/src/index.js +6 -1
- package/src/logger.js +102 -11
- package/src/mcp/handlers/chrome-tool-handler.js +3 -3
- package/src/mcp/handlers/frame-tool-handler.js +10 -10
- package/src/mcp/handlers/workflow-tool-handler.js +2 -2
- package/src/middleware/security.js +4 -4
- package/src/services/browser-daemon.js +17 -17
- package/src/services/git-safety-service.js +3 -3
- package/src/services/heartbeat-manager.js +7 -7
- package/src/services/process-manager.js +1 -1
- package/src/services/process-tracker.js +22 -22
- package/src/services/profile-manager.js +18 -18
- package/src/services/session-manager.js +21 -21
- package/src/services/session-registry.js +21 -21
- package/src/services/unified-session-manager.js +13 -13
- package/src/standalone-server.js +21 -17
- package/src/utils/extension-path.js +9 -9
- package/src/utils.js +2 -2
- package/src/validation/schemas.js +2 -2
package/src/index-modular.js
CHANGED
package/src/index.js
CHANGED
|
@@ -17,6 +17,11 @@ import {
|
|
|
17
17
|
findActiveSessions
|
|
18
18
|
} from './services/unified-session-manager.js';
|
|
19
19
|
import logger from './utils/logger.js';
|
|
20
|
+
import { interceptStdout } from './logger.js';
|
|
21
|
+
|
|
22
|
+
// CRITICAL: Intercept stdout FIRST to prevent MCP JSON-RPC pollution
|
|
23
|
+
// This must happen before any other code runs
|
|
24
|
+
interceptStdout();
|
|
20
25
|
|
|
21
26
|
/**
|
|
22
27
|
* Main application class that orchestrates all components
|
|
@@ -201,7 +206,7 @@ class ChromePilotApp {
|
|
|
201
206
|
* Shows help information
|
|
202
207
|
*/
|
|
203
208
|
showHelp() {
|
|
204
|
-
console.
|
|
209
|
+
console.error(`
|
|
205
210
|
Chrome Debug MCP Server - Modular Architecture with Session Isolation
|
|
206
211
|
|
|
207
212
|
Usage: node src/index.js [options]
|
package/src/logger.js
CHANGED
|
@@ -1,11 +1,102 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Structured logging for ChromeDebug MCP
|
|
3
|
+
* Uses pino for performance and structured output
|
|
4
|
+
* All logs go to stderr to preserve MCP stdout for JSON-RPC
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import pino from 'pino';
|
|
8
|
+
|
|
9
|
+
// Determine log level based on environment
|
|
10
|
+
const getLogLevel = () => {
|
|
11
|
+
if (process.env.LOG_LEVEL) {
|
|
12
|
+
return process.env.LOG_LEVEL;
|
|
13
|
+
}
|
|
14
|
+
if (process.env.DEBUG === 'true' || process.env.DEBUG === '1') {
|
|
15
|
+
return 'debug';
|
|
16
|
+
}
|
|
17
|
+
if (process.env.NODE_ENV === 'production') {
|
|
18
|
+
return 'warn';
|
|
19
|
+
}
|
|
20
|
+
return 'info'; // Default for development
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// Create base pino instance
|
|
24
|
+
const baseLogger = pino({
|
|
25
|
+
level: getLogLevel(),
|
|
26
|
+
transport: {
|
|
27
|
+
target: 'pino-pretty',
|
|
28
|
+
options: {
|
|
29
|
+
destination: 2, // stderr
|
|
30
|
+
colorize: true,
|
|
31
|
+
ignore: 'pid,hostname',
|
|
32
|
+
translateTime: 'HH:MM:ss',
|
|
33
|
+
singleLine: false
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Create a child logger with component context
|
|
40
|
+
* @param {string} component - Component name for log context
|
|
41
|
+
* @returns {pino.Logger} Configured pino logger
|
|
42
|
+
*/
|
|
43
|
+
export function createLogger(component) {
|
|
44
|
+
return baseLogger.child({ component });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* HTTP request logger helper
|
|
49
|
+
* Only logs if DEBUG_HTTP is enabled or in debug level
|
|
50
|
+
* @param {pino.Logger} logger - Pino logger instance
|
|
51
|
+
* @param {object} req - Express request object
|
|
52
|
+
* @param {object} options - Logging options
|
|
53
|
+
*/
|
|
54
|
+
export function logHttpRequest(logger, req, options = {}) {
|
|
55
|
+
const shouldLog = process.env.DEBUG_HTTP === 'true' ||
|
|
56
|
+
process.env.DEBUG === 'true' ||
|
|
57
|
+
logger.level === 'debug';
|
|
58
|
+
|
|
59
|
+
if (!shouldLog) return;
|
|
60
|
+
|
|
61
|
+
const requestData = {
|
|
62
|
+
method: req.method,
|
|
63
|
+
url: req.originalUrl || req.url,
|
|
64
|
+
ip: req.ip,
|
|
65
|
+
userAgent: req.get('user-agent')
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (options.verbose) {
|
|
69
|
+
requestData.headers = req.headers;
|
|
70
|
+
requestData.contentType = req.get('content-type');
|
|
71
|
+
requestData.contentLength = req.get('content-length');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
logger.debug(requestData, 'HTTP Request');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Intercept stdout to prevent MCP JSON-RPC pollution
|
|
79
|
+
* Call this at application startup for MCP servers
|
|
80
|
+
*/
|
|
81
|
+
export function interceptStdout() {
|
|
82
|
+
const originalWrite = process.stdout.write;
|
|
83
|
+
|
|
84
|
+
process.stdout.write = function(chunk, encoding, callback) {
|
|
85
|
+
// Redirect all stdout to stderr with a warning prefix
|
|
86
|
+
const message = chunk.toString();
|
|
87
|
+
const redirectedMessage = `[STDOUT-REDIRECT] ${message}`;
|
|
88
|
+
|
|
89
|
+
return process.stderr.write(redirectedMessage, encoding, callback);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
baseLogger.info('Stdout interception enabled - all output redirected to stderr for MCP safety');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Default export for backward compatibility
|
|
96
|
+
export default {
|
|
97
|
+
error: (...args) => baseLogger.error(...args),
|
|
98
|
+
warn: (...args) => baseLogger.warn(...args),
|
|
99
|
+
info: (...args) => baseLogger.info(...args),
|
|
100
|
+
debug: (...args) => baseLogger.debug(...args),
|
|
101
|
+
log: (...args) => baseLogger.info(...args)
|
|
102
|
+
};
|
|
@@ -229,7 +229,7 @@ export class ChromeToolHandler {
|
|
|
229
229
|
|
|
230
230
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
231
231
|
try {
|
|
232
|
-
console.
|
|
232
|
+
console.error(`[ChromeToolHandler] Attempt ${attempt}/${maxRetries} for ${name} (Session: ${this.chromeController.sessionId})`);
|
|
233
233
|
|
|
234
234
|
// Execute the operation based on name
|
|
235
235
|
switch (name) {
|
|
@@ -254,7 +254,7 @@ export class ChromeToolHandler {
|
|
|
254
254
|
const isSessionConflict = this.isSessionConflictError(error);
|
|
255
255
|
|
|
256
256
|
if (isSessionConflict && attempt < maxRetries) {
|
|
257
|
-
console.
|
|
257
|
+
console.error(`[ChromeToolHandler] Detected session conflict, attempting recovery...`);
|
|
258
258
|
|
|
259
259
|
try {
|
|
260
260
|
// Force cleanup current session and create new one
|
|
@@ -263,7 +263,7 @@ export class ChromeToolHandler {
|
|
|
263
263
|
// Wait a bit before retrying to let system stabilize
|
|
264
264
|
await new Promise(resolve => setTimeout(resolve, 1000 + (attempt * 500)));
|
|
265
265
|
|
|
266
|
-
console.
|
|
266
|
+
console.error(`[ChromeToolHandler] Session recovery completed, retrying...`);
|
|
267
267
|
} catch (recoveryError) {
|
|
268
268
|
console.error(`[ChromeToolHandler] Session recovery failed:`, recoveryError.message);
|
|
269
269
|
}
|
|
@@ -96,7 +96,7 @@ export class FrameToolHandler {
|
|
|
96
96
|
}
|
|
97
97
|
} catch (error) {
|
|
98
98
|
// Fall back to direct database access if HTTP client fails
|
|
99
|
-
console.
|
|
99
|
+
console.error('[FrameToolHandler] HTTP client failed, falling back to direct access:', error.message);
|
|
100
100
|
sessionInfo = await this.chromeController.getFrameSessionInfo(args.sessionId);
|
|
101
101
|
if (!sessionInfo) {
|
|
102
102
|
availableSessions = await this.chromeController.listFrameSessions();
|
|
@@ -167,7 +167,7 @@ export class FrameToolHandler {
|
|
|
167
167
|
result.note = 'No frame data or interactions found. This may indicate the recording was started but stopped immediately.';
|
|
168
168
|
}
|
|
169
169
|
} catch (error) {
|
|
170
|
-
console.
|
|
170
|
+
console.error('[FrameToolHandler] Failed to check interactions:', error.message);
|
|
171
171
|
result.note = 'No frame data found. Unable to check for interactions.';
|
|
172
172
|
}
|
|
173
173
|
|
|
@@ -285,7 +285,7 @@ export class FrameToolHandler {
|
|
|
285
285
|
frameData.interactionMetadata = { totalFound: 0, displayed: 0, timeWindow: 500 };
|
|
286
286
|
}
|
|
287
287
|
} catch (error) {
|
|
288
|
-
console.
|
|
288
|
+
console.error('[FrameToolHandler] Failed to get frame interactions:', error.message);
|
|
289
289
|
frameData.interactions = [];
|
|
290
290
|
frameData.associatedInteractions = [];
|
|
291
291
|
frameData.interactionMetadata = { totalFound: 0, displayed: 0, timeWindow: 500 };
|
|
@@ -329,7 +329,7 @@ export class FrameToolHandler {
|
|
|
329
329
|
}
|
|
330
330
|
}
|
|
331
331
|
} catch (error) {
|
|
332
|
-
console.
|
|
332
|
+
console.error('[FrameToolHandler] HTTP client failed, falling back to direct access:', error.message);
|
|
333
333
|
info = await this.chromeController.getFrameSessionInfo(args.sessionId);
|
|
334
334
|
if (!info) {
|
|
335
335
|
availableSessions = await this.chromeController.listFrameSessions();
|
|
@@ -386,7 +386,7 @@ export class FrameToolHandler {
|
|
|
386
386
|
interactionSummary.interactionTypes = Object.entries(typeCount).map(([type, count]) => `${type}: ${count}`);
|
|
387
387
|
}
|
|
388
388
|
} catch (error) {
|
|
389
|
-
console.
|
|
389
|
+
console.error('[FrameToolHandler] Failed to get interaction summary:', error.message);
|
|
390
390
|
// Keep default empty summary
|
|
391
391
|
}
|
|
392
392
|
|
|
@@ -424,7 +424,7 @@ export class FrameToolHandler {
|
|
|
424
424
|
frame = await this.chromeController.getFrame(args.sessionId, args.frameIndex);
|
|
425
425
|
}
|
|
426
426
|
} catch (error) {
|
|
427
|
-
console.
|
|
427
|
+
console.error('[FrameToolHandler] HTTP client failed, falling back to direct access:', error.message);
|
|
428
428
|
frame = await this.chromeController.getFrame(args.sessionId, args.frameIndex);
|
|
429
429
|
}
|
|
430
430
|
|
|
@@ -534,7 +534,7 @@ export class FrameToolHandler {
|
|
|
534
534
|
results = await this.chromeController.searchFrameLogs(args.sessionId, args.searchText, logLevel, maxResults);
|
|
535
535
|
}
|
|
536
536
|
} catch (error) {
|
|
537
|
-
console.
|
|
537
|
+
console.error('[FrameToolHandler] HTTP client failed, falling back to direct access:', error.message);
|
|
538
538
|
results = await this.chromeController.searchFrameLogs(args.sessionId, args.searchText, logLevel, maxResults);
|
|
539
539
|
}
|
|
540
540
|
|
|
@@ -577,7 +577,7 @@ export class FrameToolHandler {
|
|
|
577
577
|
results = await this.chromeController.getFrameLogsPaginated(args.sessionId, args.frameIndex, offset, limit, logLevel, searchText);
|
|
578
578
|
}
|
|
579
579
|
} catch (error) {
|
|
580
|
-
console.
|
|
580
|
+
console.error('[FrameToolHandler] HTTP client failed, falling back to direct access:', error.message);
|
|
581
581
|
results = await this.chromeController.getFrameLogsPaginated(args.sessionId, args.frameIndex, offset, limit, logLevel, searchText);
|
|
582
582
|
}
|
|
583
583
|
|
|
@@ -698,7 +698,7 @@ export class FrameToolHandler {
|
|
|
698
698
|
}
|
|
699
699
|
}
|
|
700
700
|
} catch (error) {
|
|
701
|
-
console.
|
|
701
|
+
console.error('[FrameToolHandler] HTTP client failed, falling back to direct access:', error.message);
|
|
702
702
|
if (args.frameIndex !== undefined) {
|
|
703
703
|
interactions = await this.chromeController.getFrameInteractions(args.sessionId, args.frameIndex);
|
|
704
704
|
} else {
|
|
@@ -936,7 +936,7 @@ export class FrameToolHandler {
|
|
|
936
936
|
}
|
|
937
937
|
}
|
|
938
938
|
} catch (error) {
|
|
939
|
-
console.
|
|
939
|
+
console.error(`Error processing snapshot ${snapshot.id}:`, error.message);
|
|
940
940
|
}
|
|
941
941
|
}
|
|
942
942
|
|
|
@@ -77,7 +77,7 @@ export class WorkflowToolHandler {
|
|
|
77
77
|
recording = await this.chromeController.getWorkflowRecording(args.sessionId);
|
|
78
78
|
}
|
|
79
79
|
} catch (error) {
|
|
80
|
-
console.
|
|
80
|
+
console.error('[WorkflowToolHandler] HTTP client failed, falling back to direct access:', error.message);
|
|
81
81
|
recording = await this.chromeController.getWorkflowRecording(args.sessionId);
|
|
82
82
|
}
|
|
83
83
|
|
|
@@ -112,7 +112,7 @@ export class WorkflowToolHandler {
|
|
|
112
112
|
result = await this.chromeController.listWorkflowRecordings();
|
|
113
113
|
}
|
|
114
114
|
} catch (error) {
|
|
115
|
-
console.
|
|
115
|
+
console.error('[WorkflowToolHandler] HTTP client failed, falling back to direct access:', error.message);
|
|
116
116
|
result = await this.chromeController.listWorkflowRecordings();
|
|
117
117
|
}
|
|
118
118
|
|
|
@@ -38,7 +38,7 @@ export const corsOptions = {
|
|
|
38
38
|
if (isAllowed) {
|
|
39
39
|
callback(null, true);
|
|
40
40
|
} else {
|
|
41
|
-
console.
|
|
41
|
+
console.error(`CORS: Blocked origin ${origin}`);
|
|
42
42
|
callback(new Error('Not allowed by CORS policy'));
|
|
43
43
|
}
|
|
44
44
|
},
|
|
@@ -165,14 +165,14 @@ export function securityLogger(req, res, next) {
|
|
|
165
165
|
);
|
|
166
166
|
|
|
167
167
|
if (isSensitive) {
|
|
168
|
-
console.
|
|
168
|
+
console.error(`[Security] ${req.method} ${req.path} - User: ${req.user?.name || 'unauthenticated'} - IP: ${req.ip}`);
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
// Track response time for monitoring
|
|
172
172
|
res.on('finish', () => {
|
|
173
173
|
const duration = Date.now() - start;
|
|
174
174
|
if (duration > 5000) { // Log slow requests
|
|
175
|
-
console.
|
|
175
|
+
console.error(`[Performance] Slow request: ${req.method} ${req.path} - ${duration}ms`);
|
|
176
176
|
}
|
|
177
177
|
});
|
|
178
178
|
|
|
@@ -245,7 +245,7 @@ export function createIPWhitelist(allowedIPs = []) {
|
|
|
245
245
|
const clientIP = req.ip || req.connection.remoteAddress;
|
|
246
246
|
|
|
247
247
|
if (!allowedIPs.includes(clientIP)) {
|
|
248
|
-
console.
|
|
248
|
+
console.error(`[Security] Blocked IP: ${clientIP}`);
|
|
249
249
|
return res.status(403).json({
|
|
250
250
|
error: 'IP not whitelisted'
|
|
251
251
|
});
|
|
@@ -67,7 +67,7 @@ export class BrowserDaemon {
|
|
|
67
67
|
const signals = ['SIGINT', 'SIGTERM', 'SIGQUIT'];
|
|
68
68
|
signals.forEach(signal => {
|
|
69
69
|
process.on(signal, async () => {
|
|
70
|
-
console.
|
|
70
|
+
console.error(`[BrowserDaemon] Received ${signal}, initiating graceful shutdown...`);
|
|
71
71
|
await this.gracefulShutdown();
|
|
72
72
|
process.exit(0);
|
|
73
73
|
});
|
|
@@ -96,7 +96,7 @@ export class BrowserDaemon {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
try {
|
|
99
|
-
console.
|
|
99
|
+
console.error(`[BrowserDaemon] Starting daemon ${this.daemonId}...`);
|
|
100
100
|
|
|
101
101
|
// Initialize core managers
|
|
102
102
|
await this.profileManager.initialize();
|
|
@@ -116,7 +116,7 @@ export class BrowserDaemon {
|
|
|
116
116
|
// Start health monitoring
|
|
117
117
|
this.startHealthCheck();
|
|
118
118
|
|
|
119
|
-
console.
|
|
119
|
+
console.error(`[BrowserDaemon] Daemon started successfully on port ${this.config.daemonPort}`);
|
|
120
120
|
|
|
121
121
|
return {
|
|
122
122
|
success: true,
|
|
@@ -167,9 +167,9 @@ export class BrowserDaemon {
|
|
|
167
167
|
`--disable-extensions-except=${extensionPath}`,
|
|
168
168
|
`--load-extension=${extensionPath}`
|
|
169
169
|
);
|
|
170
|
-
console.
|
|
170
|
+
console.error(`[BrowserDaemon] Loading ChromeDebug extension from: ${extensionPath}`);
|
|
171
171
|
} else {
|
|
172
|
-
console.
|
|
172
|
+
console.error('[BrowserDaemon] ChromeDebug extension not found. Visual selection features will be unavailable.');
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
const launchOptions = {
|
|
@@ -182,7 +182,7 @@ export class BrowserDaemon {
|
|
|
182
182
|
userDataDir: await this.createSharedUserDataDir()
|
|
183
183
|
};
|
|
184
184
|
|
|
185
|
-
console.
|
|
185
|
+
console.error(`[BrowserDaemon] Launching Chrome with debugging port ${this.config.daemonPort}`);
|
|
186
186
|
return await puppeteer.launch(launchOptions);
|
|
187
187
|
}
|
|
188
188
|
|
|
@@ -196,7 +196,7 @@ export class BrowserDaemon {
|
|
|
196
196
|
const sharedUserDataDir = path.join(tmpDir, sharedDirName);
|
|
197
197
|
|
|
198
198
|
await fs.mkdir(sharedUserDataDir, { recursive: true });
|
|
199
|
-
console.
|
|
199
|
+
console.error(`[BrowserDaemon] Created shared user data directory: ${sharedUserDataDir}`);
|
|
200
200
|
|
|
201
201
|
return sharedUserDataDir;
|
|
202
202
|
}
|
|
@@ -262,7 +262,7 @@ export class BrowserDaemon {
|
|
|
262
262
|
lastActivity: sessionData.lastActivity
|
|
263
263
|
});
|
|
264
264
|
|
|
265
|
-
console.
|
|
265
|
+
console.error(`[BrowserDaemon] Allocated session ${sessionId} for Claude instance ${claudeInstanceId}`);
|
|
266
266
|
|
|
267
267
|
return {
|
|
268
268
|
success: true,
|
|
@@ -290,7 +290,7 @@ export class BrowserDaemon {
|
|
|
290
290
|
*/
|
|
291
291
|
async deallocateSession(sessionId) {
|
|
292
292
|
if (!sessionId || !this.activeSessions.has(sessionId)) {
|
|
293
|
-
console.
|
|
293
|
+
console.error(`[BrowserDaemon] Attempted to deallocate non-existent session: ${sessionId}`);
|
|
294
294
|
return false;
|
|
295
295
|
}
|
|
296
296
|
|
|
@@ -311,7 +311,7 @@ export class BrowserDaemon {
|
|
|
311
311
|
// Remove from persistent registry
|
|
312
312
|
await this.sessionRegistry.removeSession(sessionId);
|
|
313
313
|
|
|
314
|
-
console.
|
|
314
|
+
console.error(`[BrowserDaemon] Deallocated session ${sessionId}`);
|
|
315
315
|
return true;
|
|
316
316
|
} catch (error) {
|
|
317
317
|
console.error(`[BrowserDaemon] Error deallocating session ${sessionId}:`, error);
|
|
@@ -369,7 +369,7 @@ export class BrowserDaemon {
|
|
|
369
369
|
}
|
|
370
370
|
|
|
371
371
|
this.healthCheckTimer = setInterval(this.performHealthCheck, this.config.healthCheckInterval);
|
|
372
|
-
console.
|
|
372
|
+
console.error(`[BrowserDaemon] Started health check with ${this.config.healthCheckInterval}ms interval`);
|
|
373
373
|
}
|
|
374
374
|
|
|
375
375
|
/**
|
|
@@ -381,7 +381,7 @@ export class BrowserDaemon {
|
|
|
381
381
|
try {
|
|
382
382
|
// Check browser health - log status but don't restart
|
|
383
383
|
if (!this.browser || this.browser.process()?.killed) {
|
|
384
|
-
console.
|
|
384
|
+
console.error('[BrowserDaemon] Browser process is down. Claude can launch a new browser when needed.');
|
|
385
385
|
// Don't restart - let Claude handle recovery via MCP tools
|
|
386
386
|
return;
|
|
387
387
|
}
|
|
@@ -399,12 +399,12 @@ export class BrowserDaemon {
|
|
|
399
399
|
|
|
400
400
|
// Clean up expired sessions
|
|
401
401
|
for (const sessionId of expiredSessions) {
|
|
402
|
-
console.
|
|
402
|
+
console.error(`[BrowserDaemon] Cleaning up expired session: ${sessionId}`);
|
|
403
403
|
await this.deallocateSession(sessionId);
|
|
404
404
|
}
|
|
405
405
|
|
|
406
406
|
if (expiredSessions.length > 0) {
|
|
407
|
-
console.
|
|
407
|
+
console.error(`[BrowserDaemon] Cleaned up ${expiredSessions.length} expired sessions`);
|
|
408
408
|
}
|
|
409
409
|
|
|
410
410
|
} catch (error) {
|
|
@@ -437,7 +437,7 @@ export class BrowserDaemon {
|
|
|
437
437
|
if (this.isShuttingDown) return;
|
|
438
438
|
|
|
439
439
|
this.isShuttingDown = true;
|
|
440
|
-
console.
|
|
440
|
+
console.error('[BrowserDaemon] Initiating graceful shutdown...');
|
|
441
441
|
|
|
442
442
|
try {
|
|
443
443
|
// Stop health checks
|
|
@@ -447,7 +447,7 @@ export class BrowserDaemon {
|
|
|
447
447
|
}
|
|
448
448
|
|
|
449
449
|
// Clean up all active sessions
|
|
450
|
-
console.
|
|
450
|
+
console.error(`[BrowserDaemon] Cleaning up ${this.activeSessions.size} active sessions...`);
|
|
451
451
|
for (const sessionId of this.activeSessions.keys()) {
|
|
452
452
|
await this.deallocateSession(sessionId);
|
|
453
453
|
}
|
|
@@ -462,7 +462,7 @@ export class BrowserDaemon {
|
|
|
462
462
|
await this.cleanup();
|
|
463
463
|
|
|
464
464
|
this.isRunning = false;
|
|
465
|
-
console.
|
|
465
|
+
console.error('[BrowserDaemon] Graceful shutdown completed');
|
|
466
466
|
} catch (error) {
|
|
467
467
|
console.error('[BrowserDaemon] Error during graceful shutdown:', error);
|
|
468
468
|
}
|
|
@@ -157,7 +157,7 @@ export class GitSafetyService {
|
|
|
157
157
|
backupInfo.created = true;
|
|
158
158
|
} else {
|
|
159
159
|
// Fallback to file copy if git stash fails
|
|
160
|
-
console.
|
|
160
|
+
console.error('Git stash failed, falling back to file copy:', gitBackup.error);
|
|
161
161
|
const fileBackup = await this._createFileCopyBackup(projectPath, operationId);
|
|
162
162
|
backupInfo.strategy = 'file-copy';
|
|
163
163
|
backupInfo.backupPath = fileBackup.backupPath;
|
|
@@ -502,7 +502,7 @@ export class GitSafetyService {
|
|
|
502
502
|
mkdirSync(destDir, { recursive: true });
|
|
503
503
|
copyFileSync(file, destPath);
|
|
504
504
|
} catch (copyError) {
|
|
505
|
-
console.
|
|
505
|
+
console.error(`Failed to copy file ${file}:`, copyError.message);
|
|
506
506
|
}
|
|
507
507
|
}
|
|
508
508
|
|
|
@@ -670,6 +670,6 @@ export class GitSafetyService {
|
|
|
670
670
|
// Clean up any temporary resources
|
|
671
671
|
this.activeBackups.clear();
|
|
672
672
|
this.operationHistory = [];
|
|
673
|
-
console.
|
|
673
|
+
console.error('GitSafetyService cleanup completed');
|
|
674
674
|
}
|
|
675
675
|
}
|
|
@@ -41,7 +41,7 @@ export class HeartbeatManager {
|
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
this.heartbeats.set(sessionId, heartbeatData);
|
|
44
|
-
console.
|
|
44
|
+
console.error(`[HeartbeatManager] Started heartbeat for session ${sessionId}`);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
@@ -53,7 +53,7 @@ export class HeartbeatManager {
|
|
|
53
53
|
if (heartbeatData) {
|
|
54
54
|
clearInterval(heartbeatData.interval);
|
|
55
55
|
this.heartbeats.delete(sessionId);
|
|
56
|
-
console.
|
|
56
|
+
console.error(`[HeartbeatManager] Stopped heartbeat for session ${sessionId}`);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -75,9 +75,9 @@ export class HeartbeatManager {
|
|
|
75
75
|
const heartbeatData = this.heartbeats.get(sessionId);
|
|
76
76
|
if (heartbeatData) {
|
|
77
77
|
heartbeatData.lastUpdate = new Date();
|
|
78
|
-
console.
|
|
78
|
+
console.error(`[HeartbeatManager] Manual heartbeat update for session ${sessionId}`);
|
|
79
79
|
} else {
|
|
80
|
-
console.
|
|
80
|
+
console.error(`[HeartbeatManager] Cannot update heartbeat for unknown session ${sessionId}`);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
@@ -108,7 +108,7 @@ export class HeartbeatManager {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
if (staleSessionIds.length > 0) {
|
|
111
|
-
console.
|
|
111
|
+
console.error(`[HeartbeatManager] Found ${staleSessionIds.length} stale heartbeats:`, staleSessionIds);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
return staleSessionIds;
|
|
@@ -152,7 +152,7 @@ export class HeartbeatManager {
|
|
|
152
152
|
* Stops all heartbeats (cleanup method)
|
|
153
153
|
*/
|
|
154
154
|
stopAllHeartbeats() {
|
|
155
|
-
console.
|
|
155
|
+
console.error(`[HeartbeatManager] Stopping all heartbeats (${this.heartbeats.size} sessions)`);
|
|
156
156
|
|
|
157
157
|
for (const [sessionId, heartbeatData] of this.heartbeats.entries()) {
|
|
158
158
|
clearInterval(heartbeatData.interval);
|
|
@@ -173,7 +173,7 @@ export class HeartbeatManager {
|
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
if (staleSessionIds.length > 0) {
|
|
176
|
-
console.
|
|
176
|
+
console.error(`[HeartbeatManager] Auto-cleaned ${staleSessionIds.length} stale heartbeats`);
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
return staleSessionIds;
|
|
@@ -67,7 +67,7 @@ async function findChromePilotProcesses() {
|
|
|
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
|
|
69
69
|
if (process.platform === 'win32') {
|
|
70
|
-
console.
|
|
70
|
+
console.error('[ChromeDebug] Process cleanup skipped on Windows (not yet implemented)');
|
|
71
71
|
return { pidsToKill: [], processDescriptions: [] };
|
|
72
72
|
}
|
|
73
73
|
|