@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,278 @@
1
+ import { FUNCTIONS_URL } from './firebase-config.module.js';
2
+
3
+ /**
4
+ * Firebase License Client
5
+ * Handles license validation, usage tracking, and caching
6
+ *
7
+ * Privacy: Only transmits userId (anonymous UUID) and usage count
8
+ * NO recording data, NO PII, NO browsing history
9
+ */
10
+ class FirebaseLicenseClient {
11
+ constructor() {
12
+ this.functionsUrl = FUNCTIONS_URL;
13
+ this.cacheKey = 'chromedebug_license_cache';
14
+ this.instanceIdKey = 'chromedebug_instance_id';
15
+ console.log('[License] Initialized with FUNCTIONS_URL:', this.functionsUrl);
16
+ }
17
+
18
+ /**
19
+ * Generate unique instance ID (once per installation)
20
+ * Used for device-based license validation
21
+ * @returns {Promise<string>} Instance ID
22
+ */
23
+ async generateInstanceId() {
24
+ const stored = await chrome.storage.local.get(this.instanceIdKey);
25
+ if (stored[this.instanceIdKey]) return stored[this.instanceIdKey];
26
+
27
+ const instanceId = crypto.randomUUID();
28
+ await chrome.storage.local.set({[this.instanceIdKey]: instanceId});
29
+ return instanceId;
30
+ }
31
+
32
+ /**
33
+ * Activate license with LemonSqueezy (first-time activation)
34
+ * @param {string} licenseKey - License key to activate
35
+ * @returns {Promise<{activated: boolean, instance?: object, error?: string}>}
36
+ */
37
+ async activateLicense(licenseKey) {
38
+ const deviceId = await this.generateInstanceId();
39
+
40
+ try {
41
+ console.log(`[License] Activating license ${licenseKey} with device ${deviceId}`);
42
+
43
+ const response = await fetch(`${this.functionsUrl}/activateLicense`, {
44
+ method: 'POST',
45
+ headers: {'Content-Type': 'application/json'},
46
+ body: JSON.stringify({
47
+ licenseKey: licenseKey.trim(),
48
+ instanceName: deviceId
49
+ })
50
+ });
51
+
52
+ if (!response.ok) {
53
+ const errorData = await response.json();
54
+ throw new Error(errorData.error || `HTTP ${response.status}`);
55
+ }
56
+
57
+ const data = await response.json();
58
+ console.log('[License] Activation response:', data);
59
+
60
+ // If activation successful, save the LemonSqueezy instance ID
61
+ if (data.activated && data.instance) {
62
+ await chrome.storage.local.set({
63
+ 'ls_instance_id': data.instance.id,
64
+ 'ls_license_key': licenseKey.trim(),
65
+ 'activated_at': Date.now()
66
+ });
67
+ console.log(`[License] Saved LS instance ID: ${data.instance.id}`);
68
+ }
69
+
70
+ return data;
71
+ } catch (error) {
72
+ console.error('[License] Activation failed:', error);
73
+ return {
74
+ activated: false,
75
+ error: error.message
76
+ };
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Validate license with Cloud Function
82
+ * This should be called AFTER activateLicense() on first use
83
+ * @param {string} licenseKey - License key to validate
84
+ * @returns {Promise<{valid: boolean, tier?: string, error?: string}>}
85
+ */
86
+ async validateLicense(licenseKey) {
87
+ try {
88
+ // Get LemonSqueezy instance ID (set during activation)
89
+ const stored = await chrome.storage.local.get(['ls_instance_id', 'ls_license_key']);
90
+ const instanceId = stored.ls_instance_id;
91
+
92
+ // If no instance ID yet, this license needs activation first
93
+ if (!instanceId) {
94
+ console.log('[License] No instance ID found - activating license first');
95
+ const activationResult = await this.activateLicense(licenseKey);
96
+
97
+ if (!activationResult.activated) {
98
+ return {
99
+ valid: false,
100
+ error: activationResult.error || 'Activation failed',
101
+ needsActivation: true
102
+ };
103
+ }
104
+
105
+ // Activation successful, get the new instance ID
106
+ const newStored = await chrome.storage.local.get('ls_instance_id');
107
+ const newInstanceId = newStored.ls_instance_id;
108
+
109
+ // Now validate with the new instance ID
110
+ return await this.validateWithInstanceId(licenseKey, newInstanceId);
111
+ }
112
+
113
+ // Instance ID exists, just validate
114
+ return await this.validateWithInstanceId(licenseKey, instanceId);
115
+
116
+ } catch (error) {
117
+ console.error('License validation failed:', error);
118
+ // Check cache for offline grace period
119
+ return await this.getCachedLicenseStatus();
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Validate license with LemonSqueezy API (via Firebase proxy)
125
+ * @param {string} licenseKey - License key
126
+ * @param {string} instanceId - LemonSqueezy instance ID
127
+ * @returns {Promise<{valid: boolean, tier?: string}>}
128
+ */
129
+ async validateWithInstanceId(licenseKey, instanceId) {
130
+ console.log(`[License] Validating ${licenseKey} with instance ${instanceId}`);
131
+
132
+ const response = await fetch(`${this.functionsUrl}/validateLicense`, {
133
+ method: 'POST',
134
+ headers: {'Content-Type': 'application/json'},
135
+ body: JSON.stringify({licenseKey, instanceId})
136
+ });
137
+
138
+ if (!response.ok) {
139
+ throw new Error(`HTTP ${response.status}`);
140
+ }
141
+
142
+ const data = await response.json();
143
+ console.log('[License] Validation response:', data);
144
+
145
+ // Map LemonSqueezy response to our format
146
+ const result = {
147
+ valid: data.valid === true && data.status === 'active',
148
+ tier: data.valid ? 'pro' : 'free',
149
+ status: data.status,
150
+ activationUsage: data.activationUsage,
151
+ activationLimit: data.activationLimit
152
+ };
153
+
154
+ // Cache result for 24 hours with 30-day grace period
155
+ if (result.valid) {
156
+ await this.cacheLicenseStatus({
157
+ valid: true,
158
+ tier: result.tier,
159
+ licenseKey: licenseKey,
160
+ cachedAt: Date.now(),
161
+ graceUntil: Date.now() + (30 * 24 * 60 * 60 * 1000) // 30 days
162
+ });
163
+ }
164
+
165
+ return result;
166
+ }
167
+
168
+ /**
169
+ * Check usage limit for user
170
+ * @param {string} userId - Anonymous user ID
171
+ * @returns {Promise<{allowed: boolean, count?: number, limit?: number, offline?: boolean}>}
172
+ */
173
+ async checkUsageLimit(userId) {
174
+ // First check if user has valid pro license
175
+ const licenseStatus = await this.getCachedLicenseStatus();
176
+ if (licenseStatus.valid && licenseStatus.tier === 'pro') {
177
+ return {allowed: true, unlimited: true, tier: 'pro'};
178
+ }
179
+
180
+ const url = `${this.functionsUrl}/checkUsageLimit`;
181
+ try {
182
+ console.log('[License] Calling checkUsageLimit at:', url);
183
+ const response = await fetch(url, {
184
+ method: 'POST',
185
+ headers: {'Content-Type': 'application/json'},
186
+ body: JSON.stringify({userId})
187
+ });
188
+
189
+ if (!response.ok) {
190
+ throw new Error(`HTTP ${response.status}`);
191
+ }
192
+
193
+ return await response.json();
194
+ } catch (error) {
195
+ console.error('[License] Full error details:');
196
+ console.error(' URL:', url);
197
+ console.error(' Error message:', error.message);
198
+ console.error(' Error name:', error.name);
199
+ console.error(' Error stack:', error.stack);
200
+ console.warn('[License] Cloud Functions not deployed yet - using offline mode:', error.message);
201
+ // Offline: allow limited usage (5 recordings/day default)
202
+ return {allowed: true, offline: true, count: 0, limit: 5};
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Increment usage count (call AFTER recording completes)
208
+ * @param {string} userId - Anonymous user ID
209
+ * @returns {Promise<{success: boolean, count?: number, offline?: boolean}>}
210
+ */
211
+ async incrementUsage(userId) {
212
+ // Don't increment for pro users
213
+ const licenseStatus = await this.getCachedLicenseStatus();
214
+ if (licenseStatus.valid && licenseStatus.tier === 'pro') {
215
+ return {success: true, tier: 'pro', skipped: true};
216
+ }
217
+
218
+ try {
219
+ const response = await fetch(`${this.functionsUrl}/incrementUsage`, {
220
+ method: 'POST',
221
+ headers: {'Content-Type': 'application/json'},
222
+ body: JSON.stringify({userId})
223
+ });
224
+
225
+ if (!response.ok) {
226
+ throw new Error(`HTTP ${response.status}`);
227
+ }
228
+
229
+ return await response.json();
230
+ } catch (error) {
231
+ console.error('Failed to increment usage:', error);
232
+ return {success: false, offline: true};
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Cache license status locally
238
+ * @param {{valid: boolean, tier: string, cachedAt: number, graceUntil: number}} status
239
+ */
240
+ async cacheLicenseStatus(status) {
241
+ await chrome.storage.local.set({[this.cacheKey]: status});
242
+ }
243
+
244
+ /**
245
+ * Get cached license status with grace period handling
246
+ * @returns {Promise<{valid: boolean, tier?: string, offline?: boolean, expired?: boolean}>}
247
+ */
248
+ async getCachedLicenseStatus() {
249
+ const result = await chrome.storage.local.get(this.cacheKey);
250
+ const cached = result[this.cacheKey];
251
+
252
+ if (!cached) return {valid: false};
253
+
254
+ // Check if cache is stale (>24 hours)
255
+ const cacheAge = Date.now() - cached.cachedAt;
256
+ const cacheExpired = cacheAge > (24 * 60 * 60 * 1000);
257
+
258
+ if (cacheExpired) {
259
+ // Check if within grace period
260
+ if (Date.now() < cached.graceUntil) {
261
+ return {valid: cached.valid, tier: cached.tier, offline: true, gracePeriod: true};
262
+ }
263
+
264
+ return {valid: false, expired: true};
265
+ }
266
+
267
+ return {valid: cached.valid, tier: cached.tier};
268
+ }
269
+
270
+ /**
271
+ * Clear cached license (for logout/deactivation)
272
+ */
273
+ async clearLicenseCache() {
274
+ await chrome.storage.local.remove(this.cacheKey);
275
+ }
276
+ }
277
+
278
+ export { FirebaseLicenseClient };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Firebase Cloud Functions configuration
3
+ * Compatible with both service workers (importScripts) and ES6 modules
4
+ *
5
+ * SECURITY: This file is gitignored and contains your API key.
6
+ * The API key is restricted in Google Cloud Console.
7
+ */
8
+
9
+ // Firebase configuration
10
+ const FIREBASE_CONFIG = {
11
+ projectId: 'chrome-debug-mcp',
12
+ apiKey: 'AIzaSyDu4r5k7J4gb76CVN9evBNg3DV6KjworCE',
13
+ authDomain: 'chromedebug.com'
14
+ };
15
+
16
+ // Cloud Functions base URL
17
+ const FUNCTIONS_URL = `https://us-central1-${FIREBASE_CONFIG.projectId}.cloudfunctions.net`;
18
+
19
+ // LemonSqueezy checkout URL (Production)
20
+ const LEMONSQUEEZY_CHECKOUT_URL = 'https://chromedebug.com/buy/996773cb-682b-430f-b9e3-9ce2130bd967';
21
+
22
+ // Make available globally for service worker context
23
+ if (typeof self !== 'undefined') {
24
+ self.FIREBASE_CONFIG = FIREBASE_CONFIG;
25
+ self.FUNCTIONS_URL = FUNCTIONS_URL;
26
+ self.LEMONSQUEEZY_CHECKOUT_URL = LEMONSQUEEZY_CHECKOUT_URL;
27
+ }
28
+
29
+ // For ES6 module imports (popup.js)
30
+ if (typeof module !== 'undefined' && module.exports) {
31
+ module.exports = { FIREBASE_CONFIG, FUNCTIONS_URL, LEMONSQUEEZY_CHECKOUT_URL };
32
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Firebase Cloud Functions configuration
3
+ * ES6 module version for popup.js and firebase-client.js
4
+ *
5
+ * SECURITY: This file is gitignored and contains your API key.
6
+ * The API key is restricted in Google Cloud Console.
7
+ */
8
+
9
+ // Firebase configuration
10
+ const FIREBASE_CONFIG = {
11
+ projectId: 'chrome-debug-mcp',
12
+ apiKey: 'AIzaSyDu4r5k7J4gb76CVN9evBNg3DV6KjworCE',
13
+ authDomain: 'chromedebug.com'
14
+ };
15
+
16
+ // Cloud Functions base URL
17
+ const FUNCTIONS_URL = `https://us-central1-${FIREBASE_CONFIG.projectId}.cloudfunctions.net`;
18
+
19
+ // LemonSqueezy checkout URL (Production)
20
+ const LEMONSQUEEZY_CHECKOUT_URL = 'https://chromedebug.com/buy/996773cb-682b-430f-b9e3-9ce2130bd967';
21
+
22
+ export { FIREBASE_CONFIG, FUNCTIONS_URL, LEMONSQUEEZY_CHECKOUT_URL };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Firebase Cloud Functions configuration TEMPLATE
3
+ * ES6 module version for popup.js and firebase-client.js
4
+ *
5
+ * IMPORTANT: This is a template file. Do NOT commit actual API keys.
6
+ *
7
+ * Setup Instructions:
8
+ * 1. Copy this file to firebase-config.module.js
9
+ * 2. Replace placeholder values with your actual Firebase configuration
10
+ * 3. Ensure firebase-config.module.js is in .gitignore
11
+ */
12
+
13
+ // Firebase configuration (populate after Firebase project creation)
14
+ const FIREBASE_CONFIG = {
15
+ projectId: 'YOUR_PROJECT_ID',
16
+ apiKey: 'YOUR_API_KEY_HERE',
17
+ authDomain: 'your-domain.com'
18
+ };
19
+
20
+ // Cloud Functions base URL (update after deployment)
21
+ const FUNCTIONS_URL = `https://us-central1-${FIREBASE_CONFIG.projectId}.cloudfunctions.net`;
22
+
23
+ // LemonSqueezy checkout URL
24
+ // Production example: https://chromedebug.com/buy/996773cb-682b-430f-b9e3-9ce2130bd967
25
+ const LEMONSQUEEZY_CHECKOUT_URL = 'https://your-store.lemonsqueezy.com/checkout';
26
+
27
+ export { FIREBASE_CONFIG, FUNCTIONS_URL, LEMONSQUEEZY_CHECKOUT_URL };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Firebase Cloud Functions configuration TEMPLATE
3
+ *
4
+ * IMPORTANT: This is a template file. Do NOT commit actual API keys.
5
+ *
6
+ * Setup Instructions:
7
+ * 1. Copy this file to firebase-config.js
8
+ * 2. Replace placeholder values with your actual Firebase configuration
9
+ * 3. Ensure firebase-config.js is in .gitignore
10
+ */
11
+
12
+ // Firebase configuration (populate after Firebase project creation)
13
+ const FIREBASE_CONFIG = {
14
+ projectId: 'YOUR_PROJECT_ID',
15
+ apiKey: 'YOUR_API_KEY_HERE',
16
+ authDomain: 'your-domain.com'
17
+ };
18
+
19
+ // Cloud Functions base URL (update after deployment)
20
+ const FUNCTIONS_URL = `https://us-central1-${FIREBASE_CONFIG.projectId}.cloudfunctions.net`;
21
+
22
+ // LemonSqueezy checkout URL
23
+ // Production example: https://chromedebug.com/buy/996773cb-682b-430f-b9e3-9ce2130bd967
24
+ const LEMONSQUEEZY_CHECKOUT_URL = 'https://your-store.lemonsqueezy.com/checkout';
25
+
26
+ // Make available globally for service worker context
27
+ if (typeof self !== 'undefined') {
28
+ self.FIREBASE_CONFIG = FIREBASE_CONFIG;
29
+ self.FUNCTIONS_URL = FUNCTIONS_URL;
30
+ self.LEMONSQUEEZY_CHECKOUT_URL = LEMONSQUEEZY_CHECKOUT_URL;
31
+ }
32
+
33
+ // For ES6 module imports (popup.js)
34
+ if (typeof module !== 'undefined' && module.exports) {
35
+ module.exports = { FIREBASE_CONFIG, FUNCTIONS_URL, LEMONSQUEEZY_CHECKOUT_URL };
36
+ }