@dynamicu/chromedebug-mcp 2.5.14 → 2.5.15
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 +1 -1
- package/src/capture/memory-manager.js +3 -3
- package/src/commands/install-pro.js +37 -37
- package/src/firebase-license-manager.js +4 -4
- package/src/http-server.js +100 -100
- package/src/index-modular.js +1 -1
- package/src/index.js +1 -1
- 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 +17 -17
- package/src/utils/extension-path.js +9 -9
- package/src/utils.js +2 -2
- package/src/validation/schemas.js +2 -2
|
@@ -47,9 +47,9 @@ export class SessionManager {
|
|
|
47
47
|
if (this.sessionData && this.sessionData.portLockFile) {
|
|
48
48
|
try {
|
|
49
49
|
await releasePortLock(this.sessionData.portLockFile);
|
|
50
|
-
console.
|
|
50
|
+
console.error(`[SessionManager] Released port lock on process exit for session ${this.sessionData.sessionId}`);
|
|
51
51
|
} catch (error) {
|
|
52
|
-
console.
|
|
52
|
+
console.error('[SessionManager] Error releasing port lock on exit:', error);
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
};
|
|
@@ -72,12 +72,12 @@ export class SessionManager {
|
|
|
72
72
|
const projectInfo = this.projectManager.detectProject();
|
|
73
73
|
if (projectInfo) {
|
|
74
74
|
await this.projectManager.initializeProjectStructure();
|
|
75
|
-
console.
|
|
75
|
+
console.error(`[SessionManager] Using project-local storage: ${this.projectManager.getCurrentMode()}`);
|
|
76
76
|
} else {
|
|
77
|
-
console.
|
|
77
|
+
console.error('[SessionManager] Using global storage mode');
|
|
78
78
|
}
|
|
79
79
|
} catch (error) {
|
|
80
|
-
console.
|
|
80
|
+
console.error('[SessionManager] Failed to initialize project mode, falling back to global:', error.message);
|
|
81
81
|
} finally {
|
|
82
82
|
this._projectModeInitialized = true;
|
|
83
83
|
}
|
|
@@ -136,7 +136,7 @@ export class SessionManager {
|
|
|
136
136
|
portLockFile: portLockResult.lockFile // Store lock file for cleanup
|
|
137
137
|
};
|
|
138
138
|
|
|
139
|
-
console.
|
|
139
|
+
console.error(`[SessionManager] Created session ${sessionId} with profile: ${profilePath}`);
|
|
140
140
|
|
|
141
141
|
// Start heartbeat automatically
|
|
142
142
|
this.startHeartbeat();
|
|
@@ -155,7 +155,7 @@ export class SessionManager {
|
|
|
155
155
|
async updateHeartbeat() {
|
|
156
156
|
if (this.sessionData) {
|
|
157
157
|
this.sessionData.lastHeartbeat = new Date();
|
|
158
|
-
console.
|
|
158
|
+
console.error(`[SessionManager] Updated heartbeat for session ${this.sessionData.sessionId}`);
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
|
|
@@ -171,7 +171,7 @@ export class SessionManager {
|
|
|
171
171
|
await this.updateHeartbeat();
|
|
172
172
|
}, this.HEARTBEAT_INTERVAL);
|
|
173
173
|
|
|
174
|
-
console.
|
|
174
|
+
console.error(`[SessionManager] Started heartbeat for session ${this.sessionId}`);
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
/**
|
|
@@ -181,7 +181,7 @@ export class SessionManager {
|
|
|
181
181
|
if (this.heartbeatInterval) {
|
|
182
182
|
clearInterval(this.heartbeatInterval);
|
|
183
183
|
this.heartbeatInterval = null;
|
|
184
|
-
console.
|
|
184
|
+
console.error(`[SessionManager] Stopped heartbeat for session ${this.sessionId}`);
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
187
|
|
|
@@ -240,7 +240,7 @@ export class SessionManager {
|
|
|
240
240
|
setProcessId(processId) {
|
|
241
241
|
if (this.sessionData) {
|
|
242
242
|
this.sessionData.processId = processId;
|
|
243
|
-
console.
|
|
243
|
+
console.error(`[SessionManager] Set process ID ${processId} for session ${this.sessionId}`);
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
|
|
@@ -279,11 +279,11 @@ export class SessionManager {
|
|
|
279
279
|
if (this.projectManager.isProjectModeAvailable()) {
|
|
280
280
|
const projectSessionsDir = path.resolve(this.projectManager.getProjectSessionsDir());
|
|
281
281
|
if (!resolvedPath.startsWith(projectSessionsDir)) {
|
|
282
|
-
console.
|
|
282
|
+
console.error(`[SessionManager] Refusing to clean unsafe project directory: ${dirPath}`);
|
|
283
283
|
return false;
|
|
284
284
|
}
|
|
285
285
|
} else {
|
|
286
|
-
console.
|
|
286
|
+
console.error(`[SessionManager] Cannot clean project directory when not in project mode: ${dirPath}`);
|
|
287
287
|
return false;
|
|
288
288
|
}
|
|
289
289
|
} else {
|
|
@@ -293,13 +293,13 @@ export class SessionManager {
|
|
|
293
293
|
|
|
294
294
|
if (!resolvedPath.startsWith(resolvedTmpDir) ||
|
|
295
295
|
!path.basename(resolvedPath).startsWith('chromedebug-')) {
|
|
296
|
-
console.
|
|
296
|
+
console.error(`[SessionManager] Refusing to clean unsafe global directory: ${dirPath}`);
|
|
297
297
|
return false;
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
300
|
|
|
301
301
|
await fs.rm(resolvedPath, { recursive: true, force: true });
|
|
302
|
-
console.
|
|
302
|
+
console.error(`[SessionManager] Cleaned up ${isProjectLocal ? 'project-local' : 'global'} directory: ${resolvedPath}`);
|
|
303
303
|
return true;
|
|
304
304
|
} catch (error) {
|
|
305
305
|
if (error.code === 'ENOENT') {
|
|
@@ -336,7 +336,7 @@ export class SessionManager {
|
|
|
336
336
|
isProjectLocal: true
|
|
337
337
|
})));
|
|
338
338
|
} catch (error) {
|
|
339
|
-
console.
|
|
339
|
+
console.error('[SessionManager] Could not read project sessions directory:', error.message);
|
|
340
340
|
}
|
|
341
341
|
}
|
|
342
342
|
|
|
@@ -350,7 +350,7 @@ export class SessionManager {
|
|
|
350
350
|
isProjectLocal: false
|
|
351
351
|
})));
|
|
352
352
|
|
|
353
|
-
console.
|
|
353
|
+
console.error(`[SessionManager] Found ${dirsToCheck.length} directories to evaluate`);
|
|
354
354
|
|
|
355
355
|
for (const dirInfo of dirsToCheck) {
|
|
356
356
|
try {
|
|
@@ -403,7 +403,7 @@ export class SessionManager {
|
|
|
403
403
|
}
|
|
404
404
|
}
|
|
405
405
|
|
|
406
|
-
console.
|
|
406
|
+
console.error(`[SessionManager] Cleanup complete: ${cleanupResult.cleanedSessions.length} cleaned, ${cleanupResult.activeSessionsRemaining} active remaining`);
|
|
407
407
|
} catch (error) {
|
|
408
408
|
console.error('[SessionManager] Error during stale session cleanup:', error);
|
|
409
409
|
throw error;
|
|
@@ -437,7 +437,7 @@ export class SessionManager {
|
|
|
437
437
|
process.kill(this.sessionData.processId, 'SIGKILL');
|
|
438
438
|
}
|
|
439
439
|
} catch (error) {
|
|
440
|
-
console.
|
|
440
|
+
console.error(`[SessionManager] Error killing process ${this.sessionData.processId}:`, error);
|
|
441
441
|
}
|
|
442
442
|
}
|
|
443
443
|
|
|
@@ -447,9 +447,9 @@ export class SessionManager {
|
|
|
447
447
|
if (this.sessionData.portLockFile) {
|
|
448
448
|
try {
|
|
449
449
|
await releasePortLock(this.sessionData.portLockFile);
|
|
450
|
-
console.
|
|
450
|
+
console.error(`[SessionManager] Released port lock for session ${sessionId}`);
|
|
451
451
|
} catch (error) {
|
|
452
|
-
console.
|
|
452
|
+
console.error(`[SessionManager] Error releasing port lock for session ${sessionId}:`, error);
|
|
453
453
|
}
|
|
454
454
|
}
|
|
455
455
|
|
|
@@ -486,7 +486,7 @@ export class SessionManager {
|
|
|
486
486
|
* Cleanup method called when SessionManager is destroyed
|
|
487
487
|
*/
|
|
488
488
|
async cleanup() {
|
|
489
|
-
console.
|
|
489
|
+
console.error(`[SessionManager] Cleaning up session manager for ${this.sessionId}`);
|
|
490
490
|
|
|
491
491
|
this.stopHeartbeat();
|
|
492
492
|
|
|
@@ -39,7 +39,7 @@ export class PersistentRegistry {
|
|
|
39
39
|
...options
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
-
console.
|
|
42
|
+
console.error(`[SessionRegistry] Initialized with registry directory: ${this.registryDir}`);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
|
@@ -59,7 +59,7 @@ export class PersistentRegistry {
|
|
|
59
59
|
await this.cleanupStaleSessions();
|
|
60
60
|
|
|
61
61
|
this.initialized = true;
|
|
62
|
-
console.
|
|
62
|
+
console.error(`[SessionRegistry] Initialized successfully with ${this.sessions.size} existing sessions`);
|
|
63
63
|
} catch (error) {
|
|
64
64
|
console.error('[SessionRegistry] Initialization failed:', error);
|
|
65
65
|
throw error;
|
|
@@ -107,7 +107,7 @@ export class PersistentRegistry {
|
|
|
107
107
|
// Persist to disk
|
|
108
108
|
await this.saveSessions();
|
|
109
109
|
|
|
110
|
-
console.
|
|
110
|
+
console.error(`[SessionRegistry] Updated session ${sessionId}`);
|
|
111
111
|
});
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -135,7 +135,7 @@ export class PersistentRegistry {
|
|
|
135
135
|
try {
|
|
136
136
|
await fs.access(sessionData.profilePath);
|
|
137
137
|
} catch (error) {
|
|
138
|
-
console.
|
|
138
|
+
console.error(`[SessionRegistry] Profile no longer exists for session ${sessionId}, removing from registry`);
|
|
139
139
|
await this.removeSession(sessionId);
|
|
140
140
|
return null;
|
|
141
141
|
}
|
|
@@ -161,7 +161,7 @@ export class PersistentRegistry {
|
|
|
161
161
|
await this.withFileLock(async () => {
|
|
162
162
|
this.sessions.delete(sessionId);
|
|
163
163
|
await this.saveSessions();
|
|
164
|
-
console.
|
|
164
|
+
console.error(`[SessionRegistry] Removed session ${sessionId}`);
|
|
165
165
|
});
|
|
166
166
|
|
|
167
167
|
return true;
|
|
@@ -241,7 +241,7 @@ export class PersistentRegistry {
|
|
|
241
241
|
const lockAge = Date.now() - existingLockData.timestamp;
|
|
242
242
|
|
|
243
243
|
if (lockAge > this.config.lockTimeoutMs) {
|
|
244
|
-
console.
|
|
244
|
+
console.error('[SessionRegistry] Removing stale lock file');
|
|
245
245
|
await fs.unlink(this.lockFile);
|
|
246
246
|
continue; // Retry
|
|
247
247
|
}
|
|
@@ -253,14 +253,14 @@ export class PersistentRegistry {
|
|
|
253
253
|
} catch (processError) {
|
|
254
254
|
if (processError.code === 'ESRCH') {
|
|
255
255
|
// Process doesn't exist, remove stale lock
|
|
256
|
-
console.
|
|
256
|
+
console.error('[SessionRegistry] Removing lock file from dead process');
|
|
257
257
|
await fs.unlink(this.lockFile);
|
|
258
258
|
continue; // Retry
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
} catch (parseError) {
|
|
262
262
|
// Corrupted lock file, remove it
|
|
263
|
-
console.
|
|
263
|
+
console.error('[SessionRegistry] Removing corrupted lock file');
|
|
264
264
|
await fs.unlink(this.lockFile);
|
|
265
265
|
continue; // Retry
|
|
266
266
|
}
|
|
@@ -284,7 +284,7 @@ export class PersistentRegistry {
|
|
|
284
284
|
await fs.unlink(this.lockFile);
|
|
285
285
|
} catch (error) {
|
|
286
286
|
if (error.code !== 'ENOENT') {
|
|
287
|
-
console.
|
|
287
|
+
console.error('[SessionRegistry] Error releasing lock:', error);
|
|
288
288
|
}
|
|
289
289
|
}
|
|
290
290
|
}
|
|
@@ -310,13 +310,13 @@ export class PersistentRegistry {
|
|
|
310
310
|
}
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
console.
|
|
313
|
+
console.error(`[SessionRegistry] Loaded ${this.sessions.size} sessions from disk`);
|
|
314
314
|
} catch (error) {
|
|
315
315
|
if (error.code === 'ENOENT') {
|
|
316
|
-
console.
|
|
316
|
+
console.error('[SessionRegistry] No existing registry file found, starting fresh');
|
|
317
317
|
this.sessions.clear();
|
|
318
318
|
} else {
|
|
319
|
-
console.
|
|
319
|
+
console.error('[SessionRegistry] Error loading sessions, attempting backup recovery:', error);
|
|
320
320
|
await this.loadFromBackup();
|
|
321
321
|
}
|
|
322
322
|
}
|
|
@@ -339,7 +339,7 @@ export class PersistentRegistry {
|
|
|
339
339
|
await fs.writeFile(tempFile, data, 'utf-8');
|
|
340
340
|
await fs.rename(tempFile, this.registryFile);
|
|
341
341
|
|
|
342
|
-
console.
|
|
342
|
+
console.error(`[SessionRegistry] Saved ${this.sessions.size} sessions to disk`);
|
|
343
343
|
} catch (error) {
|
|
344
344
|
console.error('[SessionRegistry] Error saving sessions:', error);
|
|
345
345
|
throw error;
|
|
@@ -364,7 +364,7 @@ export class PersistentRegistry {
|
|
|
364
364
|
await this.cleanupOldBackups();
|
|
365
365
|
} catch (error) {
|
|
366
366
|
if (error.code !== 'ENOENT') {
|
|
367
|
-
console.
|
|
367
|
+
console.error('[SessionRegistry] Error creating backup:', error);
|
|
368
368
|
}
|
|
369
369
|
}
|
|
370
370
|
}
|
|
@@ -381,7 +381,7 @@ export class PersistentRegistry {
|
|
|
381
381
|
.reverse(); // Most recent first
|
|
382
382
|
|
|
383
383
|
if (backupFiles.length === 0) {
|
|
384
|
-
console.
|
|
384
|
+
console.error('[SessionRegistry] No backup files found, starting fresh');
|
|
385
385
|
this.sessions.clear();
|
|
386
386
|
return;
|
|
387
387
|
}
|
|
@@ -397,7 +397,7 @@ export class PersistentRegistry {
|
|
|
397
397
|
}
|
|
398
398
|
}
|
|
399
399
|
|
|
400
|
-
console.
|
|
400
|
+
console.error(`[SessionRegistry] Recovered ${this.sessions.size} sessions from backup: ${backupFiles[0]}`);
|
|
401
401
|
} catch (error) {
|
|
402
402
|
console.error('[SessionRegistry] Error loading from backup:', error);
|
|
403
403
|
this.sessions.clear();
|
|
@@ -423,10 +423,10 @@ export class PersistentRegistry {
|
|
|
423
423
|
}
|
|
424
424
|
|
|
425
425
|
if (filesToDelete.length > 0) {
|
|
426
|
-
console.
|
|
426
|
+
console.error(`[SessionRegistry] Cleaned up ${filesToDelete.length} old backup files`);
|
|
427
427
|
}
|
|
428
428
|
} catch (error) {
|
|
429
|
-
console.
|
|
429
|
+
console.error('[SessionRegistry] Error cleaning up old backups:', error);
|
|
430
430
|
}
|
|
431
431
|
}
|
|
432
432
|
|
|
@@ -456,7 +456,7 @@ export class PersistentRegistry {
|
|
|
456
456
|
await this.saveSessions();
|
|
457
457
|
});
|
|
458
458
|
|
|
459
|
-
console.
|
|
459
|
+
console.error(`[SessionRegistry] Cleaned up ${staleSessions.length} stale sessions`);
|
|
460
460
|
}
|
|
461
461
|
}
|
|
462
462
|
|
|
@@ -478,7 +478,7 @@ export class PersistentRegistry {
|
|
|
478
478
|
* Cleanup method for graceful shutdown
|
|
479
479
|
*/
|
|
480
480
|
async cleanup() {
|
|
481
|
-
console.
|
|
481
|
+
console.error('[SessionRegistry] Performing final save...');
|
|
482
482
|
|
|
483
483
|
if (this.sessions.size > 0) {
|
|
484
484
|
await this.withFileLock(async () => {
|
|
@@ -486,6 +486,6 @@ export class PersistentRegistry {
|
|
|
486
486
|
});
|
|
487
487
|
}
|
|
488
488
|
|
|
489
|
-
console.
|
|
489
|
+
console.error('[SessionRegistry] Cleanup completed');
|
|
490
490
|
}
|
|
491
491
|
}
|
|
@@ -81,10 +81,10 @@ export class UnifiedSessionManager {
|
|
|
81
81
|
this.initialized = true;
|
|
82
82
|
|
|
83
83
|
if (this.verbose) {
|
|
84
|
-
console.
|
|
85
|
-
console.
|
|
86
|
-
console.
|
|
87
|
-
console.
|
|
84
|
+
console.error(`Session initialized: ${this.sessionId}`);
|
|
85
|
+
console.error(` PID: ${this.state.pid}`);
|
|
86
|
+
console.error(` Port: ${this.state.port}`);
|
|
87
|
+
console.error(` Session file: ${this.sessionFile}`);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
return this.state;
|
|
@@ -338,7 +338,7 @@ export class UnifiedSessionManager {
|
|
|
338
338
|
await this.updateSessionFile();
|
|
339
339
|
|
|
340
340
|
if (this.verbose) {
|
|
341
|
-
console.
|
|
341
|
+
console.error(`Registered process: PID ${pid} (${type}) [Session: ${this.sessionId}]`);
|
|
342
342
|
}
|
|
343
343
|
}
|
|
344
344
|
|
|
@@ -350,7 +350,7 @@ export class UnifiedSessionManager {
|
|
|
350
350
|
await this.updateSessionFile();
|
|
351
351
|
|
|
352
352
|
if (this.verbose) {
|
|
353
|
-
console.
|
|
353
|
+
console.error(`Unregistered process: PID ${pid} [Session: ${this.sessionId}]`);
|
|
354
354
|
}
|
|
355
355
|
}
|
|
356
356
|
|
|
@@ -365,7 +365,7 @@ export class UnifiedSessionManager {
|
|
|
365
365
|
// Handle specific error cases that indicate session cleanup
|
|
366
366
|
if (error.code === 'ENOENT' || error.message.includes('Session file does not exist')) {
|
|
367
367
|
if (this.verbose) {
|
|
368
|
-
console.
|
|
368
|
+
console.error(`Session file removed, stopping heartbeat for session ${this.sessionId}`);
|
|
369
369
|
}
|
|
370
370
|
this.stopHeartbeat();
|
|
371
371
|
return;
|
|
@@ -373,7 +373,7 @@ export class UnifiedSessionManager {
|
|
|
373
373
|
|
|
374
374
|
// Handle permission errors gracefully
|
|
375
375
|
if (error.code === 'EPERM' || error.code === 'EACCES' || error.message.includes('Permission denied')) {
|
|
376
|
-
console.
|
|
376
|
+
console.error(`Heartbeat permission error for session ${this.sessionId}:`, error.message);
|
|
377
377
|
this.stopHeartbeat();
|
|
378
378
|
return;
|
|
379
379
|
}
|
|
@@ -396,7 +396,7 @@ export class UnifiedSessionManager {
|
|
|
396
396
|
this.initialized = false;
|
|
397
397
|
|
|
398
398
|
if (this.verbose) {
|
|
399
|
-
console.
|
|
399
|
+
console.error(`Heartbeat stopped for session ${this.sessionId}`);
|
|
400
400
|
}
|
|
401
401
|
}
|
|
402
402
|
}
|
|
@@ -471,7 +471,7 @@ export class UnifiedSessionManager {
|
|
|
471
471
|
cleanedCount++;
|
|
472
472
|
|
|
473
473
|
if (this.verbose) {
|
|
474
|
-
console.
|
|
474
|
+
console.error(`Cleaned up stale session: ${sessionData.sessionId}`);
|
|
475
475
|
}
|
|
476
476
|
}
|
|
477
477
|
} catch (error) {
|
|
@@ -487,7 +487,7 @@ export class UnifiedSessionManager {
|
|
|
487
487
|
await this.cleanupStalePortLocks();
|
|
488
488
|
|
|
489
489
|
if (cleanedCount > 0 && this.verbose) {
|
|
490
|
-
console.
|
|
490
|
+
console.error(`Cleaned up ${cleanedCount} stale sessions`);
|
|
491
491
|
}
|
|
492
492
|
} catch (error) {
|
|
493
493
|
// Directory might not exist
|
|
@@ -539,7 +539,7 @@ export class UnifiedSessionManager {
|
|
|
539
539
|
|
|
540
540
|
const cleanup = async (reason) => {
|
|
541
541
|
if (this.verbose) {
|
|
542
|
-
console.
|
|
542
|
+
console.error(`Session ${this.sessionId} exiting (${reason}), cleaning up...`);
|
|
543
543
|
}
|
|
544
544
|
await this.cleanup();
|
|
545
545
|
};
|
|
@@ -617,7 +617,7 @@ export class UnifiedSessionManager {
|
|
|
617
617
|
} catch {}
|
|
618
618
|
|
|
619
619
|
if (this.verbose) {
|
|
620
|
-
console.
|
|
620
|
+
console.error(`Session ${this.sessionId} cleaned up`);
|
|
621
621
|
}
|
|
622
622
|
} catch (error) {
|
|
623
623
|
console.error(`Error during session cleanup:`, error.message);
|
package/src/standalone-server.js
CHANGED
|
@@ -6,31 +6,31 @@
|
|
|
6
6
|
import { startHttpServer, startWebSocketServer } from './http-server.js';
|
|
7
7
|
import { removePortFile } from './port-discovery.js';
|
|
8
8
|
|
|
9
|
-
console.
|
|
10
|
-
console.
|
|
11
|
-
console.
|
|
12
|
-
console.
|
|
13
|
-
console.
|
|
14
|
-
console.
|
|
15
|
-
console.
|
|
16
|
-
console.
|
|
17
|
-
console.
|
|
18
|
-
console.
|
|
19
|
-
console.
|
|
9
|
+
console.error('╔════════════════════════════════════════════════════════════════╗');
|
|
10
|
+
console.error('║ Chrome Debug HTTP Server ║');
|
|
11
|
+
console.error('║ ║');
|
|
12
|
+
console.error('║ This server enables Chrome extension features like: ║');
|
|
13
|
+
console.error('║ - Frame recording ║');
|
|
14
|
+
console.error('║ - Workflow recording ║');
|
|
15
|
+
console.error('║ - Visual element selection ║');
|
|
16
|
+
console.error('║ ║');
|
|
17
|
+
console.error('║ Keep this running while using Chrome Debug through Claude ║');
|
|
18
|
+
console.error('╚════════════════════════════════════════════════════════════════╝');
|
|
19
|
+
console.error('');
|
|
20
20
|
|
|
21
21
|
async function main() {
|
|
22
22
|
try {
|
|
23
23
|
// Start HTTP server
|
|
24
24
|
const httpPort = await startHttpServer();
|
|
25
|
-
console.
|
|
25
|
+
console.error(`✓ HTTP server started on port ${httpPort}`);
|
|
26
26
|
|
|
27
27
|
// Start WebSocket server
|
|
28
28
|
await startWebSocketServer();
|
|
29
|
-
console.
|
|
29
|
+
console.error('✓ WebSocket server started');
|
|
30
30
|
|
|
31
|
-
console.
|
|
32
|
-
console.
|
|
33
|
-
console.
|
|
31
|
+
console.error('');
|
|
32
|
+
console.error('Server is ready! Chrome extension features are now available.');
|
|
33
|
+
console.error('Press Ctrl+C to stop the server.');
|
|
34
34
|
|
|
35
35
|
} catch (error) {
|
|
36
36
|
console.error('Failed to start server:', error.message);
|
|
@@ -40,7 +40,7 @@ async function main() {
|
|
|
40
40
|
|
|
41
41
|
// Cleanup on exit
|
|
42
42
|
process.on('SIGINT', () => {
|
|
43
|
-
console.
|
|
43
|
+
console.error('\nShutting down server...');
|
|
44
44
|
removePortFile();
|
|
45
45
|
process.exit(0);
|
|
46
46
|
});
|
|
@@ -52,18 +52,18 @@ export function getExtensionPath(config = {}) {
|
|
|
52
52
|
const resolvedPath = path.resolve(searchPath);
|
|
53
53
|
|
|
54
54
|
if (validateExtensionPath(resolvedPath)) {
|
|
55
|
-
console.
|
|
55
|
+
console.error(`[ExtensionPath] ✓ Found ChromeDebug extension at: ${resolvedPath}`);
|
|
56
56
|
return resolvedPath;
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
console.
|
|
61
|
-
console.
|
|
62
|
-
searchPaths.filter(Boolean).forEach(p => console.
|
|
63
|
-
console.
|
|
64
|
-
console.
|
|
65
|
-
console.
|
|
66
|
-
console.
|
|
60
|
+
console.error('[ExtensionPath] ⚠ ChromeDebug extension not found in any standard location');
|
|
61
|
+
console.error('[ExtensionPath] Searched locations:');
|
|
62
|
+
searchPaths.filter(Boolean).forEach(p => console.error(`[ExtensionPath] - ${p}`));
|
|
63
|
+
console.error('[ExtensionPath]');
|
|
64
|
+
console.error('[ExtensionPath] To specify a custom location, use:');
|
|
65
|
+
console.error('[ExtensionPath] • Environment variable: CHROMEDEBUG_EXTENSION_PATH=/path/to/extension');
|
|
66
|
+
console.error('[ExtensionPath] • Config file: Set "extensionPath" in config/chrome-pilot-config.json');
|
|
67
67
|
|
|
68
68
|
return null;
|
|
69
69
|
}
|
|
@@ -114,7 +114,7 @@ export function validateExtensionPath(extensionPath) {
|
|
|
114
114
|
|
|
115
115
|
// Verify it's the ChromeDebug extension
|
|
116
116
|
if (!manifest.name?.includes('ChromeDebug')) {
|
|
117
|
-
console.
|
|
117
|
+
console.error(`[ExtensionPath] ⚠ Extension at ${extensionPath} is not ChromeDebug (found: ${manifest.name})`);
|
|
118
118
|
return false;
|
|
119
119
|
}
|
|
120
120
|
|
package/src/utils.js
CHANGED
|
@@ -88,7 +88,7 @@ async function handleExistingLock(lockFile, port) {
|
|
|
88
88
|
// Try to remove stale lock
|
|
89
89
|
try {
|
|
90
90
|
await fs.promises.unlink(lockFile);
|
|
91
|
-
console.
|
|
91
|
+
console.error(`Cleaned up stale port lock for port ${port}`);
|
|
92
92
|
return true;
|
|
93
93
|
} catch (cleanupError) {
|
|
94
94
|
// Another process might have cleaned it up, that's fine
|
|
@@ -127,7 +127,7 @@ async function cleanupStaleLocks(lockDir) {
|
|
|
127
127
|
|
|
128
128
|
if (lockAge > STALE_LOCK_THRESHOLD) {
|
|
129
129
|
await fs.promises.unlink(lockFile);
|
|
130
|
-
console.
|
|
130
|
+
console.error(`Cleaned up stale lock file: ${file}`);
|
|
131
131
|
}
|
|
132
132
|
} catch (error) {
|
|
133
133
|
// Corrupted lock file, remove it
|
|
@@ -269,8 +269,8 @@ export function createValidator(schema, target = 'body') {
|
|
|
269
269
|
|
|
270
270
|
if (error) {
|
|
271
271
|
// Diagnostic logging for validation failures
|
|
272
|
-
console.
|
|
273
|
-
console.
|
|
272
|
+
console.error(`[Validation] Failed for ${target}:`, JSON.stringify(data, null, 2));
|
|
273
|
+
console.error(`[Validation] Errors:`, error.details.map(d => ({ field: d.path.join('.'), message: d.message, value: d.context?.value })));
|
|
274
274
|
|
|
275
275
|
return res.status(400).json({
|
|
276
276
|
error: 'Validation failed',
|