@dynamicu/chromedebug-mcp 2.6.6 → 2.7.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 (46) hide show
  1. package/CLAUDE.md +1 -1
  2. package/README.md +1 -1
  3. package/chrome-extension/activation-manager.js +18 -4
  4. package/chrome-extension/background.js +1044 -552
  5. package/chrome-extension/browser-recording-manager.js +256 -0
  6. package/chrome-extension/chrome-debug-logger.js +168 -0
  7. package/chrome-extension/console-interception-library.js +430 -0
  8. package/chrome-extension/content.css +16 -16
  9. package/chrome-extension/content.js +617 -215
  10. package/chrome-extension/data-buffer.js +206 -17
  11. package/chrome-extension/extension-config.js +1 -1
  12. package/chrome-extension/frame-capture.js +52 -15
  13. package/chrome-extension/license-helper.js +26 -0
  14. package/chrome-extension/manifest.free.json +3 -6
  15. package/chrome-extension/options.js +1 -1
  16. package/chrome-extension/popup.html +315 -181
  17. package/chrome-extension/popup.js +673 -526
  18. package/chrome-extension/pro/enhanced-capture.js +406 -0
  19. package/chrome-extension/pro/frame-editor.html +410 -0
  20. package/chrome-extension/pro/frame-editor.js +1496 -0
  21. package/chrome-extension/pro/function-tracker.js +843 -0
  22. package/chrome-extension/pro/jszip.min.js +13 -0
  23. package/config/chromedebug-config.json +101 -0
  24. package/dist/chromedebug-extension-free.zip +0 -0
  25. package/package.json +3 -1
  26. package/scripts/package-pro-extension.js +1 -1
  27. package/scripts/webpack.config.free.cjs +11 -8
  28. package/scripts/webpack.config.pro.cjs +5 -0
  29. package/src/chrome-controller.js +7 -7
  30. package/src/cli.js +2 -2
  31. package/src/database.js +61 -9
  32. package/src/http-server.js +3 -2
  33. package/src/index.js +9 -6
  34. package/src/mcp/server.js +2 -2
  35. package/src/services/process-manager.js +10 -6
  36. package/src/services/process-tracker.js +10 -5
  37. package/src/services/profile-manager.js +17 -2
  38. package/src/validation/schemas.js +36 -6
  39. package/src/index-direct.js +0 -157
  40. package/src/index-modular.js +0 -219
  41. package/src/index-monolithic-backup.js +0 -2230
  42. package/src/legacy/chrome-controller-old.js +0 -1406
  43. package/src/legacy/index-express.js +0 -625
  44. package/src/legacy/index-old.js +0 -977
  45. package/src/legacy/routes.js +0 -260
  46. package/src/legacy/shared-storage.js +0 -101
@@ -4,7 +4,7 @@
4
4
  class DataBuffer {
5
5
  constructor() {
6
6
  this.dbName = 'ChromePilotDataBuffer';
7
- this.version = 1;
7
+ this.version = 2; // Incremented for new object stores
8
8
  this.db = null;
9
9
  this.maxBufferSize = 100 * 1024 * 1024; // 100MB default
10
10
  this.currentSize = 0;
@@ -31,30 +31,48 @@ class DataBuffer {
31
31
 
32
32
  request.onupgradeneeded = (event) => {
33
33
  const db = event.target.result;
34
-
34
+
35
35
  // Create object stores if they don't exist
36
36
  if (!db.objectStoreNames.contains('events')) {
37
- const eventsStore = db.createObjectStore('events', {
38
- keyPath: 'id',
39
- autoIncrement: true
37
+ const eventsStore = db.createObjectStore('events', {
38
+ keyPath: 'id',
39
+ autoIncrement: true
40
40
  });
41
41
  eventsStore.createIndex('timestamp', 'timestamp', { unique: false });
42
42
  eventsStore.createIndex('type', 'type', { unique: false });
43
43
  eventsStore.createIndex('recording_id', 'recording_id', { unique: false });
44
44
  }
45
-
45
+
46
46
  if (!db.objectStoreNames.contains('batches')) {
47
- const batchesStore = db.createObjectStore('batches', {
48
- keyPath: 'id',
49
- autoIncrement: true
47
+ const batchesStore = db.createObjectStore('batches', {
48
+ keyPath: 'id',
49
+ autoIncrement: true
50
50
  });
51
51
  batchesStore.createIndex('timestamp', 'timestamp', { unique: false });
52
52
  batchesStore.createIndex('status', 'status', { unique: false });
53
53
  }
54
-
54
+
55
55
  if (!db.objectStoreNames.contains('metadata')) {
56
56
  db.createObjectStore('metadata', { keyPath: 'key' });
57
57
  }
58
+
59
+ // New object stores for browser-only recording mode
60
+ if (!db.objectStoreNames.contains('browser_recordings')) {
61
+ const recordingsStore = db.createObjectStore('browser_recordings', {
62
+ keyPath: 'sessionId'
63
+ });
64
+ recordingsStore.createIndex('timestamp', 'startTime', { unique: false });
65
+ recordingsStore.createIndex('status', 'status', { unique: false });
66
+ }
67
+
68
+ if (!db.objectStoreNames.contains('browser_frames')) {
69
+ const framesStore = db.createObjectStore('browser_frames', {
70
+ keyPath: 'id',
71
+ autoIncrement: true
72
+ });
73
+ framesStore.createIndex('sessionId', 'sessionId', { unique: false });
74
+ framesStore.createIndex('frameIndex', 'frameIndex', { unique: false });
75
+ }
58
76
  };
59
77
  });
60
78
  }
@@ -390,11 +408,11 @@ class DataBuffer {
390
408
 
391
409
  async getStats() {
392
410
  if (!this.db) await this.init();
393
-
411
+
394
412
  const transaction = this.db.transaction(['events', 'batches'], 'readonly');
395
413
  const eventsStore = transaction.objectStore('events');
396
414
  const batchesStore = transaction.objectStore('batches');
397
-
415
+
398
416
  return new Promise((resolve) => {
399
417
  let stats = {
400
418
  eventCount: 0,
@@ -404,29 +422,200 @@ class DataBuffer {
404
422
  totalSize: this.currentSize,
405
423
  maxSize: this.maxBufferSize
406
424
  };
407
-
425
+
408
426
  eventsStore.count().onsuccess = (e) => {
409
427
  stats.eventCount = e.target.result;
410
428
  };
411
-
429
+
412
430
  batchesStore.count().onsuccess = (e) => {
413
431
  stats.batchCount = e.target.result;
414
432
  };
415
-
433
+
416
434
  const pendingIndex = batchesStore.index('status');
417
435
  pendingIndex.count(IDBKeyRange.only('pending')).onsuccess = (e) => {
418
436
  stats.pendingBatches = e.target.result;
419
437
  };
420
-
438
+
421
439
  pendingIndex.count(IDBKeyRange.only('uploaded')).onsuccess = (e) => {
422
440
  stats.uploadedBatches = e.target.result;
423
441
  };
424
-
442
+
425
443
  transaction.oncomplete = () => {
426
444
  resolve(stats);
427
445
  };
428
446
  });
429
447
  }
448
+
449
+ // Browser-only recording methods
450
+ async addBrowserRecording(recording) {
451
+ if (!this.db) await this.init();
452
+ const transaction = this.db.transaction(['browser_recordings'], 'readwrite');
453
+ const store = transaction.objectStore('browser_recordings');
454
+
455
+ return new Promise((resolve, reject) => {
456
+ const request = store.add(recording);
457
+ request.onsuccess = () => resolve(request.result);
458
+ request.onerror = () => reject(request.error);
459
+ });
460
+ }
461
+
462
+ async updateBrowserRecording(sessionId, updates) {
463
+ if (!this.db) await this.init();
464
+ const transaction = this.db.transaction(['browser_recordings'], 'readwrite');
465
+ const store = transaction.objectStore('browser_recordings');
466
+
467
+ return new Promise((resolve, reject) => {
468
+ const getRequest = store.get(sessionId);
469
+ getRequest.onsuccess = () => {
470
+ const recording = getRequest.result;
471
+ if (recording) {
472
+ Object.assign(recording, updates);
473
+ const putRequest = store.put(recording);
474
+ putRequest.onsuccess = () => resolve();
475
+ putRequest.onerror = () => reject(putRequest.error);
476
+ } else {
477
+ reject(new Error('Recording not found'));
478
+ }
479
+ };
480
+ getRequest.onerror = () => reject(getRequest.error);
481
+ });
482
+ }
483
+
484
+ async getBrowserRecording(sessionId) {
485
+ if (!this.db) await this.init();
486
+ const transaction = this.db.transaction(['browser_recordings'], 'readonly');
487
+ const store = transaction.objectStore('browser_recordings');
488
+
489
+ return new Promise((resolve, reject) => {
490
+ const request = store.get(sessionId);
491
+ request.onsuccess = () => resolve(request.result);
492
+ request.onerror = () => reject(request.error);
493
+ });
494
+ }
495
+
496
+ async getAllBrowserRecordings() {
497
+ if (!this.db) await this.init();
498
+ const transaction = this.db.transaction(['browser_recordings'], 'readonly');
499
+ const store = transaction.objectStore('browser_recordings');
500
+
501
+ return new Promise((resolve, reject) => {
502
+ const request = store.getAll();
503
+ request.onsuccess = () => resolve(request.result || []);
504
+ request.onerror = () => reject(request.error);
505
+ });
506
+ }
507
+
508
+ async deleteBrowserRecording(sessionId) {
509
+ if (!this.db) await this.init();
510
+
511
+ return new Promise((resolve, reject) => {
512
+ const transaction = this.db.transaction(['browser_recordings'], 'readwrite');
513
+ const store = transaction.objectStore('browser_recordings');
514
+
515
+ // Delete the recording
516
+ const deleteRequest = store.delete(sessionId);
517
+ deleteRequest.onerror = () => reject(deleteRequest.error);
518
+
519
+ // Wait for transaction to COMPLETE (commit to disk), not just request success
520
+ // This prevents race condition where add() happens before delete() commits
521
+ transaction.oncomplete = () => resolve();
522
+ transaction.onerror = () => reject(transaction.error);
523
+ });
524
+ }
525
+
526
+ async addBrowserFrame(frame) {
527
+ if (!this.db) await this.init();
528
+ const transaction = this.db.transaction(['browser_frames'], 'readwrite');
529
+ const store = transaction.objectStore('browser_frames');
530
+
531
+ return new Promise((resolve, reject) => {
532
+ const request = store.add(frame);
533
+ request.onsuccess = () => resolve(request.result);
534
+ request.onerror = () => reject(request.error);
535
+ });
536
+ }
537
+
538
+ async getBrowserFrames(sessionId) {
539
+ if (!this.db) await this.init();
540
+ const transaction = this.db.transaction(['browser_frames'], 'readonly');
541
+ const store = transaction.objectStore('browser_frames');
542
+ const index = store.index('sessionId');
543
+
544
+ return new Promise((resolve, reject) => {
545
+ const request = index.getAll(IDBKeyRange.only(sessionId));
546
+ request.onsuccess = () => resolve(request.result || []);
547
+ request.onerror = () => reject(request.error);
548
+ });
549
+ }
550
+
551
+ async getBrowserFrameCount(sessionId) {
552
+ if (!this.db) await this.init();
553
+ const transaction = this.db.transaction(['browser_frames'], 'readonly');
554
+ const store = transaction.objectStore('browser_frames');
555
+ const index = store.index('sessionId');
556
+
557
+ return new Promise((resolve, reject) => {
558
+ const request = index.count(IDBKeyRange.only(sessionId));
559
+ request.onsuccess = () => resolve(request.result);
560
+ request.onerror = () => reject(request.error);
561
+ });
562
+ }
563
+
564
+ async updateBrowserFrame(frameId, updates) {
565
+ if (!this.db) await this.init();
566
+
567
+ return new Promise((resolve, reject) => {
568
+ const transaction = this.db.transaction(['browser_frames'], 'readwrite');
569
+ const store = transaction.objectStore('browser_frames');
570
+
571
+ // Get the existing frame
572
+ const getRequest = store.get(frameId);
573
+ getRequest.onsuccess = () => {
574
+ const frame = getRequest.result;
575
+ if (!frame) {
576
+ reject(new Error(`Frame ${frameId} not found`));
577
+ return;
578
+ }
579
+
580
+ // Apply updates
581
+ Object.assign(frame, updates);
582
+
583
+ // Put the updated frame back
584
+ const putRequest = store.put(frame);
585
+ putRequest.onerror = () => reject(putRequest.error);
586
+ };
587
+ getRequest.onerror = () => reject(getRequest.error);
588
+
589
+ // Wait for transaction to complete
590
+ transaction.oncomplete = () => resolve();
591
+ transaction.onerror = () => reject(transaction.error);
592
+ });
593
+ }
594
+
595
+ async deleteBrowserFrames(sessionId) {
596
+ if (!this.db) await this.init();
597
+ const transaction = this.db.transaction(['browser_frames'], 'readwrite');
598
+ const store = transaction.objectStore('browser_frames');
599
+ const index = store.index('sessionId');
600
+
601
+ return new Promise((resolve, reject) => {
602
+ let deletedCount = 0;
603
+ const request = index.openCursor(IDBKeyRange.only(sessionId));
604
+
605
+ request.onsuccess = (event) => {
606
+ const cursor = event.target.result;
607
+ if (cursor) {
608
+ store.delete(cursor.primaryKey);
609
+ deletedCount++;
610
+ cursor.continue();
611
+ } else {
612
+ resolve(deletedCount);
613
+ }
614
+ };
615
+
616
+ request.onerror = () => reject(request.error);
617
+ });
618
+ }
430
619
  }
431
620
 
432
621
  // Export for use in background script
@@ -64,7 +64,7 @@ const CHROMEDEBUG_CONFIG = {
64
64
  3027
65
65
  ],
66
66
  discoveryTimeout: 3000,
67
- lastGenerated: '2025-10-17T23:32:00.030Z'
67
+ lastGenerated: '2025-11-19T23:54:34.802Z'
68
68
  };
69
69
 
70
70
  // Export for use in extension scripts
@@ -10,6 +10,7 @@ class FrameCapture {
10
10
  this.sessionId = null;
11
11
  this.startTime = null;
12
12
  this.totalFramesCaptured = 0;
13
+ this.mode = 'server'; // 'server' or 'browser-only'
13
14
  this.settings = {
14
15
  frameRate: 1, // seconds between frames
15
16
  imageQuality: 30, // JPEG quality (1-100)
@@ -23,6 +24,16 @@ class FrameCapture {
23
24
 
24
25
  }
25
26
 
27
+ setMode(mode) {
28
+ if (mode !== 'server' && mode !== 'browser-only') {
29
+ console.warn('[FrameCapture] Invalid mode:', mode, '- defaulting to server');
30
+ this.mode = 'server';
31
+ } else {
32
+ this.mode = mode;
33
+ console.log('[FrameCapture] Mode set to:', this.mode);
34
+ }
35
+ }
36
+
26
37
  async startCapture(streamId, tabId, sessionId, scheduledStartTime, settings = {}, ownerId = null) {
27
38
  if (this.isCapturing) return;
28
39
 
@@ -38,12 +49,22 @@ class FrameCapture {
38
49
  // Initialize lease tracking - null forces immediate first check
39
50
  this.lastLeaseCheck = null;
40
51
 
41
- // Update settings with passed values
42
- this.settings = {
43
- frameRate: settings.frameRate || 1,
44
- imageQuality: settings.imageQuality || 30,
45
- frameFlash: settings.frameFlash !== false
46
- };
52
+ // Update settings with passed values or apply mode-specific defaults
53
+ if (this.mode === 'browser-only') {
54
+ // Browser-only mode: ultra-low resolution and quality for storage efficiency
55
+ this.settings = {
56
+ frameRate: settings.frameRate || 1,
57
+ imageQuality: settings.imageQuality || 15, // Lower quality for browser storage
58
+ frameFlash: settings.frameFlash !== false
59
+ };
60
+ } else {
61
+ // Server mode: standard settings
62
+ this.settings = {
63
+ frameRate: settings.frameRate || 1,
64
+ imageQuality: settings.imageQuality || 30,
65
+ frameFlash: settings.frameFlash !== false
66
+ };
67
+ }
47
68
 
48
69
  console.log('Starting frame capture session:', this.sessionId);
49
70
  console.log('Frame capture scheduled to start at:', new Date(scheduledStartTime));
@@ -68,16 +89,27 @@ class FrameCapture {
68
89
  this.totalFramesCaptured++;
69
90
  console.log('Created synthetic first frame at scheduled start time');
70
91
 
71
- // Get the media stream with very low resolution optimized for Claude reading
72
- const media = await navigator.mediaDevices.getUserMedia({
73
- video: {
74
- mandatory: {
75
- chromeMediaSource: 'tab',
76
- chromeMediaSourceId: streamId,
77
- maxWidth: 640, // Reduced from 960 for smaller files
78
- maxHeight: 360 // Reduced from 540 for smaller files
92
+ // Get the media stream with resolution based on mode
93
+ const videoConstraints = this.mode === 'browser-only'
94
+ ? {
95
+ mandatory: {
96
+ chromeMediaSource: 'tab',
97
+ chromeMediaSourceId: streamId,
98
+ maxWidth: 480, // Ultra-low resolution for browser storage
99
+ maxHeight: 270 // Ultra-low resolution for browser storage
100
+ }
79
101
  }
80
- },
102
+ : {
103
+ mandatory: {
104
+ chromeMediaSource: 'tab',
105
+ chromeMediaSourceId: streamId,
106
+ maxWidth: 640, // Standard resolution for server storage
107
+ maxHeight: 360 // Standard resolution for server storage
108
+ }
109
+ };
110
+
111
+ const media = await navigator.mediaDevices.getUserMedia({
112
+ video: videoConstraints,
81
113
  audio: false
82
114
  });
83
115
 
@@ -382,6 +414,11 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
382
414
  if (message.target !== 'offscreen') return;
383
415
 
384
416
  if (message.type === 'start-frame-capture') {
417
+ // Set mode before starting capture
418
+ if (message.mode) {
419
+ frameCapture.setMode(message.mode);
420
+ }
421
+
385
422
  frameCapture.startCapture(message.streamId, message.tabId, message.sessionId, message.scheduledStartTime, message.settings, message.ownerId)
386
423
  .then(() => sendResponse({ success: true }))
387
424
  .catch(error => {
@@ -12,12 +12,27 @@ const LicenseHelper = {
12
12
  instanceIdKey: 'chromedebug_instance_id',
13
13
  userIdKey: 'chromedebug_user_id',
14
14
 
15
+ /**
16
+ * Check if this is the PRO version by checking the manifest
17
+ * @returns {boolean} True if PRO version
18
+ */
19
+ isProVersion() {
20
+ const manifest = chrome.runtime.getManifest();
21
+ return manifest.name.includes('PRO');
22
+ },
23
+
15
24
  /**
16
25
  * Check if user can start a recording (license + usage limit check)
17
26
  * @returns {Promise<{allowed: boolean, message?: string, tier?: string}>}
18
27
  */
19
28
  async checkLicenseBeforeRecording() {
20
29
  try {
30
+ // PRO version: always allow, no restrictions
31
+ if (this.isProVersion()) {
32
+ console.log('[License] PRO version - bypassing all license checks');
33
+ return {allowed: true, tier: 'pro', proVersion: true};
34
+ }
35
+
21
36
  // Get userId
22
37
  const stored = await chrome.storage.local.get(this.userIdKey);
23
38
  let userId = stored[this.userIdKey];
@@ -70,6 +85,12 @@ const LicenseHelper = {
70
85
  */
71
86
  async trackUsageAfterRecording(userId) {
72
87
  try {
88
+ // PRO version: skip all usage tracking
89
+ if (this.isProVersion()) {
90
+ console.log('[License] PRO version - skipping usage tracking');
91
+ return {success: true, tier: 'pro', proVersion: true, skipped: true};
92
+ }
93
+
73
94
  if (!userId) {
74
95
  const stored = await chrome.storage.local.get(this.userIdKey);
75
96
  userId = stored[this.userIdKey];
@@ -155,6 +176,11 @@ const LicenseHelper = {
155
176
  * @returns {Promise<{valid: boolean, tier?: string}>}
156
177
  */
157
178
  async getCachedLicenseStatus() {
179
+ // PRO version: always return pro tier
180
+ if (this.isProVersion()) {
181
+ return {valid: true, tier: 'pro', proVersion: true};
182
+ }
183
+
158
184
  const result = await chrome.storage.local.get(this.cacheKey);
159
185
  const cached = result[this.cacheKey];
160
186
 
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "manifest_version": 3,
3
- "name": "ChromeDebug MCP Assistant FREE v2.6.1",
4
- "version": "2.6.1",
5
- "description": "ChromeDebug MCP visual element selector [FREE Edition] [Build: 2025-10-28-v2.6.1]",
3
+ "name": "ChromeDebug MCP Assistant FREE v2.6.7",
4
+ "version": "2.6.7",
5
+ "description": "ChromeDebug MCP visual element selector [FREE Edition] [Build: 2025-11-10-v2.6.7]",
6
6
  "permissions": [
7
7
  "activeTab",
8
8
  "scripting",
@@ -12,9 +12,6 @@
12
12
  "notifications",
13
13
  "offscreen"
14
14
  ],
15
- "host_permissions": [
16
- "https://*.cloudfunctions.net/*"
17
- ],
18
15
  "action": {
19
16
  "default_popup": "popup.html",
20
17
  "default_icon": {
@@ -217,7 +217,7 @@ document.addEventListener('DOMContentLoaded', () => {
217
217
 
218
218
  const a = document.createElement('a');
219
219
  a.href = url;
220
- a.download = 'chrome-pilot-site-settings.json';
220
+ a.download = 'chrome-debug-site-settings.json';
221
221
  a.click();
222
222
 
223
223
  URL.revokeObjectURL(url);