@dynamicu/chromedebug-mcp 2.2.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.
Files changed (95) hide show
  1. package/CLAUDE.md +344 -0
  2. package/LICENSE +21 -0
  3. package/README.md +250 -0
  4. package/chrome-extension/README.md +41 -0
  5. package/chrome-extension/background.js +3917 -0
  6. package/chrome-extension/chrome-session-manager.js +706 -0
  7. package/chrome-extension/content.css +181 -0
  8. package/chrome-extension/content.js +3022 -0
  9. package/chrome-extension/data-buffer.js +435 -0
  10. package/chrome-extension/dom-tracker.js +411 -0
  11. package/chrome-extension/extension-config.js +78 -0
  12. package/chrome-extension/firebase-client.js +278 -0
  13. package/chrome-extension/firebase-config.js +32 -0
  14. package/chrome-extension/firebase-config.module.js +22 -0
  15. package/chrome-extension/firebase-config.module.template.js +27 -0
  16. package/chrome-extension/firebase-config.template.js +36 -0
  17. package/chrome-extension/frame-capture.js +407 -0
  18. package/chrome-extension/icon128.png +1 -0
  19. package/chrome-extension/icon16.png +1 -0
  20. package/chrome-extension/icon48.png +1 -0
  21. package/chrome-extension/license-helper.js +181 -0
  22. package/chrome-extension/logger.js +23 -0
  23. package/chrome-extension/manifest.json +73 -0
  24. package/chrome-extension/network-tracker.js +510 -0
  25. package/chrome-extension/offscreen.html +10 -0
  26. package/chrome-extension/options.html +203 -0
  27. package/chrome-extension/options.js +282 -0
  28. package/chrome-extension/pako.min.js +2 -0
  29. package/chrome-extension/performance-monitor.js +533 -0
  30. package/chrome-extension/pii-redactor.js +405 -0
  31. package/chrome-extension/popup.html +532 -0
  32. package/chrome-extension/popup.js +2446 -0
  33. package/chrome-extension/upload-manager.js +323 -0
  34. package/chrome-extension/web-vitals.iife.js +1 -0
  35. package/config/api-keys.json +11 -0
  36. package/config/chrome-pilot-config.json +45 -0
  37. package/package.json +126 -0
  38. package/scripts/cleanup-processes.js +109 -0
  39. package/scripts/config-manager.js +280 -0
  40. package/scripts/generate-extension-config.js +53 -0
  41. package/scripts/setup-security.js +64 -0
  42. package/src/capture/architecture.js +426 -0
  43. package/src/capture/error-handling-tests.md +38 -0
  44. package/src/capture/error-handling-types.ts +360 -0
  45. package/src/capture/index.js +508 -0
  46. package/src/capture/interfaces.js +625 -0
  47. package/src/capture/memory-manager.js +713 -0
  48. package/src/capture/types.js +342 -0
  49. package/src/chrome-controller.js +2658 -0
  50. package/src/cli.js +19 -0
  51. package/src/config-loader.js +303 -0
  52. package/src/database.js +2178 -0
  53. package/src/firebase-license-manager.js +462 -0
  54. package/src/firebase-privacy-guard.js +397 -0
  55. package/src/http-server.js +1516 -0
  56. package/src/index-direct.js +157 -0
  57. package/src/index-modular.js +219 -0
  58. package/src/index-monolithic-backup.js +2230 -0
  59. package/src/index.js +305 -0
  60. package/src/legacy/chrome-controller-old.js +1406 -0
  61. package/src/legacy/index-express.js +625 -0
  62. package/src/legacy/index-old.js +977 -0
  63. package/src/legacy/routes.js +260 -0
  64. package/src/legacy/shared-storage.js +101 -0
  65. package/src/logger.js +10 -0
  66. package/src/mcp/handlers/chrome-tool-handler.js +306 -0
  67. package/src/mcp/handlers/element-tool-handler.js +51 -0
  68. package/src/mcp/handlers/frame-tool-handler.js +957 -0
  69. package/src/mcp/handlers/request-handler.js +104 -0
  70. package/src/mcp/handlers/workflow-tool-handler.js +636 -0
  71. package/src/mcp/server.js +68 -0
  72. package/src/mcp/tools/index.js +701 -0
  73. package/src/middleware/auth.js +371 -0
  74. package/src/middleware/security.js +267 -0
  75. package/src/port-discovery.js +258 -0
  76. package/src/routes/admin.js +182 -0
  77. package/src/services/browser-daemon.js +494 -0
  78. package/src/services/chrome-service.js +375 -0
  79. package/src/services/failover-manager.js +412 -0
  80. package/src/services/git-safety-service.js +675 -0
  81. package/src/services/heartbeat-manager.js +200 -0
  82. package/src/services/http-client.js +195 -0
  83. package/src/services/process-manager.js +318 -0
  84. package/src/services/process-tracker.js +574 -0
  85. package/src/services/profile-manager.js +449 -0
  86. package/src/services/project-manager.js +415 -0
  87. package/src/services/session-manager.js +497 -0
  88. package/src/services/session-registry.js +491 -0
  89. package/src/services/unified-session-manager.js +678 -0
  90. package/src/shared-storage-old.js +267 -0
  91. package/src/standalone-server.js +53 -0
  92. package/src/utils/extension-path.js +145 -0
  93. package/src/utils.js +187 -0
  94. package/src/validation/log-transformer.js +125 -0
  95. package/src/validation/schemas.js +391 -0
@@ -0,0 +1,415 @@
1
+ /**
2
+ * ProjectManager - Project-local directory management for Chrome Debug
3
+ *
4
+ * Provides intelligent project detection and creates project-local directory
5
+ * structures for Chrome Debug data (.chromedebug/ in project root).
6
+ *
7
+ * Key Features:
8
+ * - Automatic project type detection (npm, git, python, etc.)
9
+ * - Project-local directory structure creation
10
+ * - Automatic .gitignore management
11
+ * - Fallback to global mode when not in a valid project
12
+ * - Path validation and security
13
+ */
14
+
15
+ import { promises as fs, existsSync as fsExistsSync } from 'fs';
16
+ import path from 'path';
17
+ import os from 'os';
18
+
19
+ export class ProjectManager {
20
+ constructor() {
21
+ this.projectInfo = null;
22
+ this.projectStructure = null;
23
+ this.initialized = false;
24
+ }
25
+
26
+ /**
27
+ * Detects if current working directory is part of a valid project
28
+ * @param {string} startPath - Starting path for project detection (defaults to cwd)
29
+ * @returns {ProjectInfo|null} Project information or null if not in project
30
+ */
31
+ detectProject(startPath = process.cwd()) {
32
+ try {
33
+ const resolvedPath = path.resolve(startPath);
34
+ const projectRoot = this.findProjectRoot(resolvedPath);
35
+
36
+ if (!projectRoot) {
37
+ console.log('[ProjectManager] No valid project detected from:', resolvedPath);
38
+ return null;
39
+ }
40
+
41
+ // Determine project type based on markers
42
+ const markers = this.getProjectMarkers(projectRoot);
43
+ const projectType = this.determineProjectType(markers);
44
+
45
+ this.projectInfo = {
46
+ projectDir: projectRoot,
47
+ type: projectType,
48
+ hasMarkers: markers
49
+ };
50
+
51
+ console.log(`[ProjectManager] Detected ${projectType} project at: ${projectRoot}`);
52
+ console.log(`[ProjectManager] Project markers found: ${markers.join(', ')}`);
53
+
54
+ return this.projectInfo;
55
+ } catch (error) {
56
+ console.warn('[ProjectManager] Error during project detection:', error.message);
57
+ return null;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Recursively searches up directory tree for project markers
63
+ * @param {string} currentPath - Current path to check
64
+ * @returns {string|null} Project root path or null if not found
65
+ */
66
+ findProjectRoot(currentPath) {
67
+ const rootPath = path.parse(currentPath).root;
68
+
69
+ // Don't go beyond root directory
70
+ if (currentPath === rootPath) {
71
+ return null;
72
+ }
73
+
74
+ try {
75
+ // Check for project markers in current directory
76
+ const markers = this.getProjectMarkers(currentPath);
77
+
78
+ if (markers.length > 0) {
79
+ return currentPath;
80
+ }
81
+
82
+ // Move up one directory and continue searching
83
+ const parentPath = path.dirname(currentPath);
84
+
85
+ // Avoid infinite recursion at root
86
+ if (parentPath === currentPath) {
87
+ return null;
88
+ }
89
+
90
+ return this.findProjectRoot(parentPath);
91
+ } catch (error) {
92
+ // Directory might not exist or be accessible
93
+ return null;
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Gets list of project markers found in a directory
99
+ * @param {string} dirPath - Directory to check for markers
100
+ * @returns {string[]} Array of found marker file/directory names
101
+ */
102
+ getProjectMarkers(dirPath) {
103
+ const markers = [
104
+ 'package.json', // Node.js
105
+ '.git', // Git repository (directory or file for worktrees)
106
+ 'requirements.txt', // Python
107
+ 'pyproject.toml', // Python (modern)
108
+ 'Pipfile', // Python pipenv
109
+ 'Cargo.toml', // Rust
110
+ 'pom.xml', // Maven (Java)
111
+ 'build.gradle', // Gradle (Java/Kotlin)
112
+ 'go.mod', // Go
113
+ 'composer.json', // PHP Composer
114
+ 'Gemfile', // Ruby
115
+ '.project', // Eclipse project
116
+ 'CMakeLists.txt', // CMake
117
+ 'Makefile' // Make
118
+ ];
119
+
120
+ const found = [];
121
+
122
+ for (const marker of markers) {
123
+ try {
124
+ const markerPath = path.join(dirPath, marker);
125
+
126
+ if (this.existsSync(markerPath)) {
127
+ found.push(marker);
128
+ }
129
+ } catch (error) {
130
+ // Ignore access errors for individual markers
131
+ continue;
132
+ }
133
+ }
134
+
135
+ return found;
136
+ }
137
+
138
+ /**
139
+ * Synchronously check if path exists (safer than fs.existsSync)
140
+ * @param {string} filePath - Path to check
141
+ * @returns {boolean} True if path exists
142
+ */
143
+ existsSync(filePath) {
144
+ try {
145
+ return fsExistsSync(filePath);
146
+ } catch {
147
+ return false;
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Determines project type based on found markers
153
+ * @param {string[]} markers - Found project markers
154
+ * @returns {string} Project type identifier
155
+ */
156
+ determineProjectType(markers) {
157
+ // Priority order for type detection
158
+ if (markers.includes('package.json')) return 'npm';
159
+ if (markers.includes('requirements.txt') || markers.includes('pyproject.toml') || markers.includes('Pipfile')) return 'python';
160
+ if (markers.includes('Cargo.toml')) return 'rust';
161
+ if (markers.includes('pom.xml') || markers.includes('build.gradle')) return 'java';
162
+ if (markers.includes('go.mod')) return 'go';
163
+ if (markers.includes('composer.json')) return 'php';
164
+ if (markers.includes('Gemfile')) return 'ruby';
165
+ if (markers.includes('.git')) return 'git';
166
+ if (markers.includes('CMakeLists.txt') || markers.includes('Makefile')) return 'native';
167
+
168
+ return 'generic';
169
+ }
170
+
171
+ /**
172
+ * Gets the project directory (must call detectProject first)
173
+ * @returns {string} Project directory path
174
+ * @throws {Error} If no project detected
175
+ */
176
+ getProjectDir() {
177
+ if (!this.projectInfo) {
178
+ throw new Error('No project detected. Call detectProject() first.');
179
+ }
180
+ return this.projectInfo.projectDir;
181
+ }
182
+
183
+ /**
184
+ * Validates if a path is a valid project directory
185
+ * @param {string} dirPath - Path to validate
186
+ * @returns {boolean} True if valid project directory
187
+ */
188
+ isValidProject(dirPath) {
189
+ if (!dirPath || typeof dirPath !== 'string') {
190
+ return false;
191
+ }
192
+
193
+ try {
194
+ const resolvedPath = path.resolve(dirPath);
195
+ const markers = this.getProjectMarkers(resolvedPath);
196
+ return markers.length > 0;
197
+ } catch {
198
+ return false;
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Initializes project-local directory structure
204
+ * Creates .chromedebug/ directory with subdirectories and .gitignore entry
205
+ * @returns {Promise<void>}
206
+ */
207
+ async initializeProjectStructure() {
208
+ if (!this.projectInfo) {
209
+ throw new Error('No project detected. Call detectProject() first.');
210
+ }
211
+
212
+ const projectDir = this.projectInfo.projectDir;
213
+
214
+ // Create project structure configuration
215
+ this.projectStructure = {
216
+ baseDir: path.join(projectDir, '.chromedebug'),
217
+ sessionsDir: path.join(projectDir, '.chromedebug', 'sessions'),
218
+ databaseDir: path.join(projectDir, '.chromedebug', 'database'),
219
+ configDir: path.join(projectDir, '.chromedebug', 'config'),
220
+ logsDir: path.join(projectDir, '.chromedebug', 'logs')
221
+ };
222
+
223
+ console.log(`[ProjectManager] Initializing project structure in: ${this.projectStructure.baseDir}`);
224
+
225
+ try {
226
+ // Create all necessary directories
227
+ await fs.mkdir(this.projectStructure.baseDir, { recursive: true });
228
+ await fs.mkdir(this.projectStructure.sessionsDir, { recursive: true });
229
+ await fs.mkdir(this.projectStructure.databaseDir, { recursive: true });
230
+ await fs.mkdir(this.projectStructure.configDir, { recursive: true });
231
+ await fs.mkdir(this.projectStructure.logsDir, { recursive: true });
232
+
233
+ // Add .chromedebug to .gitignore if project has git
234
+ if (this.projectInfo.hasMarkers.includes('.git')) {
235
+ await this.updateGitignore(projectDir);
236
+ }
237
+
238
+ console.log('[ProjectManager] Project structure initialized successfully');
239
+ console.log('[ProjectManager] Structure:', this.projectStructure);
240
+
241
+ this.initialized = true;
242
+ } catch (error) {
243
+ console.error('[ProjectManager] Failed to initialize project structure:', error);
244
+ throw new Error(`Project structure initialization failed: ${error.message}`);
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Updates .gitignore to exclude .chromedebug directory
250
+ * @param {string} projectDir - Project directory path
251
+ * @returns {Promise<void>}
252
+ */
253
+ async updateGitignore(projectDir) {
254
+ const gitignorePath = path.join(projectDir, '.gitignore');
255
+ const chromePilotEntry = '.chromedebug/';
256
+
257
+ try {
258
+ let gitignoreContent = '';
259
+ let needsUpdate = true;
260
+
261
+ // Read existing .gitignore if it exists
262
+ try {
263
+ gitignoreContent = await fs.readFile(gitignorePath, 'utf8');
264
+
265
+ // Check if .chromedebug is already ignored
266
+ const lines = gitignoreContent.split('\n');
267
+ const hasEntry = lines.some(line =>
268
+ line.trim() === chromePilotEntry ||
269
+ line.trim() === '.chromedebug' ||
270
+ line.trim() === '.chromedebug/*'
271
+ );
272
+
273
+ if (hasEntry) {
274
+ needsUpdate = false;
275
+ console.log('[ProjectManager] .chromedebug already in .gitignore');
276
+ }
277
+ } catch (error) {
278
+ // .gitignore doesn't exist, will create it
279
+ console.log('[ProjectManager] Creating new .gitignore file');
280
+ }
281
+
282
+ if (needsUpdate) {
283
+ // Ensure content ends with newline before adding entry
284
+ if (gitignoreContent && !gitignoreContent.endsWith('\n')) {
285
+ gitignoreContent += '\n';
286
+ }
287
+
288
+ // Add Chrome Debug section
289
+ const chromePilotSection = `
290
+ # Chrome Debug project-local data
291
+ ${chromePilotEntry}
292
+ `;
293
+
294
+ gitignoreContent += chromePilotSection;
295
+
296
+ await fs.writeFile(gitignorePath, gitignoreContent, 'utf8');
297
+ console.log('[ProjectManager] Added .chromedebug to .gitignore');
298
+ }
299
+ } catch (error) {
300
+ console.warn('[ProjectManager] Warning: Could not update .gitignore:', error.message);
301
+ // Don't fail initialization if .gitignore update fails
302
+ }
303
+ }
304
+
305
+ /**
306
+ * Gets the project structure paths (must initialize first)
307
+ * @returns {ProjectStructure} Project structure object
308
+ * @throws {Error} If not initialized
309
+ */
310
+ getProjectStructure() {
311
+ if (!this.projectStructure) {
312
+ throw new Error('Project structure not initialized. Call initializeProjectStructure() first.');
313
+ }
314
+ return { ...this.projectStructure };
315
+ }
316
+
317
+ /**
318
+ * Gets project-local database path
319
+ * @returns {string} Database file path
320
+ */
321
+ getProjectDatabasePath() {
322
+ if (!this.projectStructure) {
323
+ throw new Error('Project structure not initialized');
324
+ }
325
+ return path.join(this.projectStructure.databaseDir, 'chromedebug.db');
326
+ }
327
+
328
+ /**
329
+ * Gets project-local config path
330
+ * @returns {string} Config file path
331
+ */
332
+ getProjectConfigPath() {
333
+ if (!this.projectStructure) {
334
+ throw new Error('Project structure not initialized');
335
+ }
336
+ return path.join(this.projectStructure.configDir, 'config.json');
337
+ }
338
+
339
+ /**
340
+ * Gets project-local sessions directory path
341
+ * @returns {string} Sessions directory path
342
+ */
343
+ getProjectSessionsDir() {
344
+ if (!this.projectStructure) {
345
+ throw new Error('Project structure not initialized');
346
+ }
347
+ return this.projectStructure.sessionsDir;
348
+ }
349
+
350
+ /**
351
+ * Gets project-local logs directory path
352
+ * @returns {string} Logs directory path
353
+ */
354
+ getProjectLogsDir() {
355
+ if (!this.projectStructure) {
356
+ throw new Error('Project structure not initialized');
357
+ }
358
+ return this.projectStructure.logsDir;
359
+ }
360
+
361
+ /**
362
+ * Checks if project mode is available (project detected and initialized)
363
+ * @returns {boolean} True if project mode is available
364
+ */
365
+ isProjectModeAvailable() {
366
+ return this.projectInfo !== null && this.initialized;
367
+ }
368
+
369
+ /**
370
+ * Gets fallback global directories for when not in project mode
371
+ * @returns {ProjectStructure} Global structure paths
372
+ */
373
+ getGlobalStructure() {
374
+ const globalBaseDir = path.join(os.tmpdir(), 'chromedebug-global');
375
+
376
+ return {
377
+ baseDir: globalBaseDir,
378
+ sessionsDir: path.join(globalBaseDir, 'sessions'),
379
+ databaseDir: path.join(process.cwd(), 'data'), // Existing global database location
380
+ configDir: path.join(process.cwd(), 'config'), // Existing global config location
381
+ logsDir: globalBaseDir
382
+ };
383
+ }
384
+
385
+ /**
386
+ * Resets the project manager state (useful for testing)
387
+ */
388
+ reset() {
389
+ this.projectInfo = null;
390
+ this.projectStructure = null;
391
+ this.initialized = false;
392
+ }
393
+
394
+ /**
395
+ * Gets current mode ('project' or 'global')
396
+ * @returns {string} Current operational mode
397
+ */
398
+ getCurrentMode() {
399
+ return this.isProjectModeAvailable() ? 'project' : 'global';
400
+ }
401
+
402
+ /**
403
+ * Gets debug information about current state
404
+ * @returns {object} Debug information
405
+ */
406
+ getDebugInfo() {
407
+ return {
408
+ mode: this.getCurrentMode(),
409
+ projectInfo: this.projectInfo,
410
+ projectStructure: this.projectStructure,
411
+ initialized: this.initialized,
412
+ cwd: process.cwd()
413
+ };
414
+ }
415
+ }