@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,462 @@
1
+ /**
2
+ * Firebase License Manager
3
+ *
4
+ * Handles license validation and management with Firebase
5
+ *
6
+ * Features:
7
+ * - Server-side license validation via Cloud Functions
8
+ * - Offline caching (24 hour default)
9
+ * - Grace period (7 days default)
10
+ * - Privacy guard integration (NO recording data to Firebase)
11
+ * - Instance tracking for anti-sharing
12
+ */
13
+
14
+ import admin from 'firebase-admin';
15
+ import { FirebasePrivacyGuard } from './firebase-privacy-guard.js';
16
+ import crypto from 'crypto';
17
+ import fs from 'fs';
18
+ import path from 'path';
19
+ import { fileURLToPath } from 'url';
20
+
21
+ const __filename = fileURLToPath(import.meta.url);
22
+ const __dirname = path.dirname(__filename);
23
+
24
+ /**
25
+ * License cache for offline operation
26
+ */
27
+ class LicenseCache {
28
+ constructor(cacheDurationHours = 24) {
29
+ this.cachePath = path.join(__dirname, '../data/license-cache.json');
30
+ this.cacheDurationMs = cacheDurationHours * 60 * 60 * 1000;
31
+ this.cache = this.loadCache();
32
+ }
33
+
34
+ loadCache() {
35
+ try {
36
+ if (fs.existsSync(this.cachePath)) {
37
+ const data = fs.readFileSync(this.cachePath, 'utf8');
38
+ return JSON.parse(data);
39
+ }
40
+ } catch (error) {
41
+ console.error('Error loading license cache:', error);
42
+ }
43
+ return {};
44
+ }
45
+
46
+ saveCache() {
47
+ try {
48
+ const dir = path.dirname(this.cachePath);
49
+ if (!fs.existsSync(dir)) {
50
+ fs.mkdirSync(dir, { recursive: true });
51
+ }
52
+ fs.writeFileSync(this.cachePath, JSON.stringify(this.cache, null, 2));
53
+ } catch (error) {
54
+ console.error('Error saving license cache:', error);
55
+ }
56
+ }
57
+
58
+ get(licenseKey) {
59
+ const entry = this.cache[licenseKey];
60
+ if (!entry) return null;
61
+
62
+ const age = Date.now() - entry.timestamp;
63
+ if (age > this.cacheDurationMs) {
64
+ // Cache expired
65
+ delete this.cache[licenseKey];
66
+ this.saveCache();
67
+ return null;
68
+ }
69
+
70
+ return entry.data;
71
+ }
72
+
73
+ set(licenseKey, data) {
74
+ this.cache[licenseKey] = {
75
+ data,
76
+ timestamp: Date.now()
77
+ };
78
+ this.saveCache();
79
+ }
80
+
81
+ clear() {
82
+ this.cache = {};
83
+ this.saveCache();
84
+ }
85
+
86
+ isExpired(licenseKey) {
87
+ const entry = this.cache[licenseKey];
88
+ if (!entry) return true;
89
+
90
+ const age = Date.now() - entry.timestamp;
91
+ return age > this.cacheDurationMs;
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Firebase License Manager
97
+ */
98
+ export class FirebaseLicenseManager {
99
+ constructor(config = {}) {
100
+ // Initialize Firebase Admin if not already initialized
101
+ if (!admin.apps.length) {
102
+ const firebaseConfig = {
103
+ projectId: process.env.FIREBASE_PROJECT_ID,
104
+ credential: admin.credential.cert({
105
+ projectId: process.env.FIREBASE_PROJECT_ID,
106
+ privateKey: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, '\n'),
107
+ clientEmail: process.env.FIREBASE_CLIENT_EMAIL
108
+ })
109
+ };
110
+
111
+ admin.initializeApp(firebaseConfig);
112
+ }
113
+
114
+ this.db = admin.firestore();
115
+ this.cache = new LicenseCache(config.cacheDurationHours || 24);
116
+ this.gracePeriodDays = config.gracePeriodDays || 7;
117
+ this.instanceId = this.getOrCreateInstanceId();
118
+
119
+ // Cloud Functions base URL
120
+ this.functionsUrl = process.env.FIREBASE_FUNCTIONS_URL ||
121
+ `https://us-central1-${process.env.FIREBASE_PROJECT_ID}.cloudfunctions.net`;
122
+ }
123
+
124
+ /**
125
+ * Get or create unique instance ID for this installation
126
+ */
127
+ getOrCreateInstanceId() {
128
+ const instancePath = path.join(__dirname, '../data/instance-id.txt');
129
+
130
+ try {
131
+ if (fs.existsSync(instancePath)) {
132
+ return fs.readFileSync(instancePath, 'utf8').trim();
133
+ }
134
+ } catch (error) {
135
+ console.error('Error reading instance ID:', error);
136
+ }
137
+
138
+ // Create new instance ID
139
+ const instanceId = `inst_${crypto.randomBytes(16).toString('hex')}`;
140
+
141
+ try {
142
+ const dir = path.dirname(instancePath);
143
+ if (!fs.existsSync(dir)) {
144
+ fs.mkdirSync(dir, { recursive: true });
145
+ }
146
+ fs.writeFileSync(instancePath, instanceId);
147
+ } catch (error) {
148
+ console.error('Error saving instance ID:', error);
149
+ }
150
+
151
+ return instanceId;
152
+ }
153
+
154
+ /**
155
+ * Validate license key
156
+ *
157
+ * @param {string} licenseKey - License key to validate
158
+ * @param {boolean} forceRefresh - Force refresh from server
159
+ * @returns {Promise<Object>} Validation result
160
+ */
161
+ async validateLicense(licenseKey, forceRefresh = false) {
162
+ // Sanitize input to prevent any data leakage
163
+ const sanitizedInput = FirebasePrivacyGuard.sanitize({
164
+ licenseKey,
165
+ instanceId: this.instanceId
166
+ }, 'validateLicense');
167
+
168
+ // Check cache first (unless forced refresh)
169
+ if (!forceRefresh) {
170
+ const cached = this.cache.get(licenseKey);
171
+ if (cached) {
172
+ console.log('[License] Using cached validation result');
173
+ return {
174
+ ...cached,
175
+ fromCache: true
176
+ };
177
+ }
178
+ }
179
+
180
+ try {
181
+ // Call Cloud Function for server-side validation
182
+ const response = await fetch(`${this.functionsUrl}/validateLicense`, {
183
+ method: 'POST',
184
+ headers: {
185
+ 'Content-Type': 'application/json'
186
+ },
187
+ body: JSON.stringify({
188
+ licenseKey: sanitizedInput.licenseKey,
189
+ instanceId: sanitizedInput.instanceId,
190
+ platform: process.platform,
191
+ deviceName: require('os').hostname()
192
+ })
193
+ });
194
+
195
+ const result = await response.json();
196
+
197
+ // Sanitize response before storing
198
+ const sanitizedResult = FirebasePrivacyGuard.sanitize(result, 'validateLicenseResponse');
199
+
200
+ if (result.valid) {
201
+ // Cache valid result
202
+ this.cache.set(licenseKey, sanitizedResult);
203
+ }
204
+
205
+ return {
206
+ ...sanitizedResult,
207
+ fromCache: false
208
+ };
209
+ } catch (error) {
210
+ console.error('[License] Validation error:', error);
211
+
212
+ // Check if we have a cached result (even if expired)
213
+ const cached = this.cache.get(licenseKey);
214
+ if (cached) {
215
+ // Check grace period
216
+ const cacheAge = Date.now() - (this.cache.cache[licenseKey]?.timestamp || 0);
217
+ const gracePeriodMs = this.gracePeriodDays * 24 * 60 * 60 * 1000;
218
+
219
+ if (cacheAge <= gracePeriodMs) {
220
+ console.log('[License] Using grace period validation');
221
+ return {
222
+ ...cached,
223
+ fromCache: true,
224
+ gracePeriod: true,
225
+ gracePeriodEndsAt: new Date(
226
+ (this.cache.cache[licenseKey]?.timestamp || 0) + gracePeriodMs
227
+ ).toISOString()
228
+ };
229
+ }
230
+ }
231
+
232
+ return {
233
+ valid: false,
234
+ error: 'License validation failed and no cached result available',
235
+ networkError: true
236
+ };
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Check usage limit for user
242
+ *
243
+ * @param {string} userId - User ID
244
+ * @returns {Promise<Object>} Usage status
245
+ */
246
+ async checkUsageLimit(userId) {
247
+ // Sanitize input
248
+ const sanitizedInput = FirebasePrivacyGuard.sanitize({ userId }, 'checkUsageLimit');
249
+
250
+ try {
251
+ const response = await fetch(`${this.functionsUrl}/checkUsageLimit`, {
252
+ method: 'POST',
253
+ headers: {
254
+ 'Content-Type': 'application/json'
255
+ },
256
+ body: JSON.stringify(sanitizedInput)
257
+ });
258
+
259
+ const result = await response.json();
260
+ return FirebasePrivacyGuard.sanitize(result, 'checkUsageLimitResponse');
261
+ } catch (error) {
262
+ console.error('[License] Usage check error:', error);
263
+ return {
264
+ withinLimit: false,
265
+ error: 'Usage check failed',
266
+ networkError: true
267
+ };
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Increment usage counter
273
+ *
274
+ * @param {string} userId - User ID
275
+ * @param {number} count - Count to increment
276
+ * @returns {Promise<Object>} Result
277
+ */
278
+ async incrementUsage(userId, count = 1) {
279
+ // Sanitize input
280
+ const sanitizedInput = FirebasePrivacyGuard.sanitize({
281
+ userId,
282
+ count
283
+ }, 'incrementUsage');
284
+
285
+ try {
286
+ const response = await fetch(`${this.functionsUrl}/incrementUsage`, {
287
+ method: 'POST',
288
+ headers: {
289
+ 'Content-Type': 'application/json'
290
+ },
291
+ body: JSON.stringify(sanitizedInput)
292
+ });
293
+
294
+ const result = await response.json();
295
+ return FirebasePrivacyGuard.sanitize(result, 'incrementUsageResponse');
296
+ } catch (error) {
297
+ console.error('[License] Usage increment error:', error);
298
+ return {
299
+ success: false,
300
+ error: 'Usage increment failed',
301
+ networkError: true
302
+ };
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Get user license information
308
+ *
309
+ * @param {string} userId - User ID
310
+ * @returns {Promise<Object>} License info
311
+ */
312
+ async getUserLicense(userId) {
313
+ // Sanitize input
314
+ const sanitizedUserId = FirebasePrivacyGuard.sanitize({ userId }, 'getUserLicense').userId;
315
+
316
+ try {
317
+ const licenseQuery = await this.db
318
+ .collection('licenses')
319
+ .where('userId', '==', sanitizedUserId)
320
+ .where('status', '==', 'active')
321
+ .limit(1)
322
+ .get();
323
+
324
+ if (licenseQuery.empty) {
325
+ return { error: 'No active license found' };
326
+ }
327
+
328
+ const licenseDoc = licenseQuery.docs[0];
329
+ const licenseData = licenseDoc.data();
330
+
331
+ // Sanitize response
332
+ return FirebasePrivacyGuard.sanitize({
333
+ id: licenseDoc.id,
334
+ ...licenseData
335
+ }, 'getUserLicenseResponse');
336
+ } catch (error) {
337
+ console.error('[License] Get user license error:', error);
338
+ return {
339
+ error: 'Failed to retrieve license',
340
+ networkError: true
341
+ };
342
+ }
343
+ }
344
+
345
+ /**
346
+ * List user's active instances
347
+ *
348
+ * @param {string} licenseKey - License key
349
+ * @returns {Promise<Array>} Active instances
350
+ */
351
+ async listInstances(licenseKey) {
352
+ // Sanitize input
353
+ const sanitizedKey = FirebasePrivacyGuard.sanitize({ licenseKey }, 'listInstances').licenseKey;
354
+
355
+ try {
356
+ const instancesQuery = await this.db
357
+ .collection('instances')
358
+ .where('licenseKey', '==', sanitizedKey)
359
+ .where('active', '==', true)
360
+ .get();
361
+
362
+ const instances = instancesQuery.docs.map(doc => {
363
+ const data = doc.data();
364
+ return FirebasePrivacyGuard.sanitize({
365
+ id: doc.id,
366
+ ...data,
367
+ createdAt: data.createdAt?.toDate().toISOString(),
368
+ lastSeen: data.lastSeen?.toDate().toISOString()
369
+ }, 'listInstancesResponse');
370
+ });
371
+
372
+ return instances;
373
+ } catch (error) {
374
+ console.error('[License] List instances error:', error);
375
+ return [];
376
+ }
377
+ }
378
+
379
+ /**
380
+ * Deactivate an instance
381
+ *
382
+ * @param {string} instanceId - Instance ID to deactivate
383
+ * @returns {Promise<Object>} Result
384
+ */
385
+ async deactivateInstance(instanceId) {
386
+ // Sanitize input
387
+ const sanitizedId = FirebasePrivacyGuard.sanitize({ instanceId }, 'deactivateInstance').instanceId;
388
+
389
+ try {
390
+ await this.db.collection('instances').doc(sanitizedId).update({
391
+ active: false,
392
+ deactivatedAt: admin.firestore.FieldValue.serverTimestamp()
393
+ });
394
+
395
+ return { success: true };
396
+ } catch (error) {
397
+ console.error('[License] Deactivate instance error:', error);
398
+ return {
399
+ success: false,
400
+ error: 'Failed to deactivate instance'
401
+ };
402
+ }
403
+ }
404
+
405
+ /**
406
+ * Clear license cache
407
+ */
408
+ clearCache() {
409
+ this.cache.clear();
410
+ }
411
+
412
+ /**
413
+ * Get cache status
414
+ */
415
+ getCacheStatus() {
416
+ return {
417
+ cacheSize: Object.keys(this.cache.cache).length,
418
+ instanceId: this.instanceId,
419
+ gracePeriodDays: this.gracePeriodDays
420
+ };
421
+ }
422
+
423
+ /**
424
+ * Privacy check - ensure NO recording data can be sent
425
+ *
426
+ * This method validates that any data being sent to Firebase
427
+ * is free of recording/sensitive data
428
+ */
429
+ static validateDataPrivacy(data, context = 'unknown') {
430
+ return FirebasePrivacyGuard.assertSafe(data, context);
431
+ }
432
+ }
433
+
434
+ // Export singleton instance
435
+ let licenseManagerInstance = null;
436
+
437
+ export function getLicenseManager(config = {}) {
438
+ // Check if Firebase is configured
439
+ if (!process.env.FIREBASE_PROJECT_ID) {
440
+ console.warn('[License] Firebase not configured - licensing disabled');
441
+ console.warn('[License] Set FIREBASE_PROJECT_ID to enable licensing');
442
+
443
+ // Return stub that allows all operations (development mode)
444
+ return {
445
+ isConfigured: false,
446
+ validateLicense: async () => ({ valid: true, tier: 'dev', dev: true }),
447
+ checkUsageLimit: async () => ({ allowed: true, unlimited: true, dev: true }),
448
+ incrementUsage: async () => ({ success: true, dev: true }),
449
+ activateLicense: async () => ({ success: true, dev: true }),
450
+ deactivateLicense: async () => ({ success: true, dev: true }),
451
+ deactivateInstance: async () => ({ success: true, dev: true }),
452
+ clearCache: () => {},
453
+ getCacheStatus: () => ({ dev: true, licensing: 'disabled' }),
454
+ validateDataPrivacy: () => true
455
+ };
456
+ }
457
+
458
+ if (!licenseManagerInstance) {
459
+ licenseManagerInstance = new FirebaseLicenseManager(config);
460
+ }
461
+ return licenseManagerInstance;
462
+ }