@dynamicu/chromedebug-mcp 2.6.7 → 2.7.1
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/CLAUDE.md +17 -1
- package/README.md +1 -1
- package/chrome-extension/activation-manager.js +10 -10
- package/chrome-extension/background.js +1045 -736
- package/chrome-extension/browser-recording-manager.js +1 -1
- package/chrome-extension/chrome-debug-logger.js +168 -0
- package/chrome-extension/chrome-session-manager.js +5 -5
- package/chrome-extension/console-interception-library.js +430 -0
- package/chrome-extension/content.css +16 -16
- package/chrome-extension/content.js +739 -221
- package/chrome-extension/data-buffer.js +5 -5
- package/chrome-extension/dom-tracker.js +9 -9
- package/chrome-extension/extension-config.js +1 -1
- package/chrome-extension/firebase-client.js +13 -13
- package/chrome-extension/frame-capture.js +20 -38
- package/chrome-extension/license-helper.js +33 -7
- package/chrome-extension/manifest.free.json +3 -6
- package/chrome-extension/network-tracker.js +9 -9
- package/chrome-extension/options.html +10 -0
- package/chrome-extension/options.js +21 -8
- package/chrome-extension/performance-monitor.js +17 -17
- package/chrome-extension/popup.html +230 -193
- package/chrome-extension/popup.js +146 -458
- package/chrome-extension/pro/enhanced-capture.js +406 -0
- package/chrome-extension/pro/frame-editor.html +433 -0
- package/chrome-extension/pro/frame-editor.js +1567 -0
- package/chrome-extension/pro/function-tracker.js +843 -0
- package/chrome-extension/pro/jszip.min.js +13 -0
- package/chrome-extension/upload-manager.js +7 -7
- package/dist/chromedebug-extension-free.zip +0 -0
- package/package.json +3 -1
- package/scripts/webpack.config.free.cjs +8 -8
- package/scripts/webpack.config.pro.cjs +2 -0
- package/src/cli.js +2 -2
- package/src/database.js +55 -7
- package/src/index.js +9 -6
- package/src/mcp/server.js +2 -2
- package/src/services/process-manager.js +10 -6
- package/src/services/process-tracker.js +10 -5
- package/src/services/profile-manager.js +17 -2
- package/src/validation/schemas.js +12 -11
- package/src/index-direct.js +0 -157
- package/src/index-modular.js +0 -219
- package/src/index-monolithic-backup.js +0 -2230
- package/src/legacy/chrome-controller-old.js +0 -1406
- package/src/legacy/index-express.js +0 -625
- package/src/legacy/index-old.js +0 -977
- package/src/legacy/routes.js +0 -260
- package/src/legacy/shared-storage.js +0 -101
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Background script to handle server communication and recording
|
|
2
2
|
const EXTENSION_VERSION = '2.0.4-BUILD-20250119';
|
|
3
|
-
console.log(`[background.js] Loaded version: ${EXTENSION_VERSION}`);
|
|
3
|
+
// console.log(`[background.js] Loaded version: ${EXTENSION_VERSION}`);
|
|
4
4
|
|
|
5
5
|
// Import configuration and libraries
|
|
6
6
|
importScripts('extension-config.js');
|
|
@@ -8,13 +8,129 @@ importScripts('pako.min.js');
|
|
|
8
8
|
importScripts('data-buffer.js');
|
|
9
9
|
importScripts('upload-manager.js');
|
|
10
10
|
importScripts('chrome-session-manager.js');
|
|
11
|
-
importScripts('
|
|
12
|
-
importScripts('license-helper.js');
|
|
11
|
+
importScripts('console-interception-library.js'); // Shared console interception library
|
|
13
12
|
importScripts('browser-recording-manager.js');
|
|
14
13
|
|
|
15
14
|
const CONFIG_PORTS = CHROMEDEBUG_CONFIG.ports;
|
|
16
15
|
const DISCOVERY_TIMEOUT = CHROMEDEBUG_CONFIG.discoveryTimeout;
|
|
17
16
|
|
|
17
|
+
// ============================================================
|
|
18
|
+
// FREE TIER USAGE TRACKING (Local Storage - No Firebase)
|
|
19
|
+
// ============================================================
|
|
20
|
+
// Daily limit: 5 recordings per day
|
|
21
|
+
// Resets at midnight local time
|
|
22
|
+
// Tracks both workflow and screen recordings
|
|
23
|
+
// ============================================================
|
|
24
|
+
|
|
25
|
+
const FREE_TIER_DAILY_LIMIT = 5;
|
|
26
|
+
const USAGE_STORAGE_KEY = 'chromedebug_daily_usage';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if this is the PRO version
|
|
30
|
+
*/
|
|
31
|
+
function isProVersion() {
|
|
32
|
+
const manifest = chrome.runtime.getManifest();
|
|
33
|
+
return manifest.name.includes('PRO');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get today's date string in local timezone (YYYY-MM-DD)
|
|
38
|
+
*/
|
|
39
|
+
function getTodayDateString() {
|
|
40
|
+
const now = new Date();
|
|
41
|
+
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get current usage for today
|
|
46
|
+
* @returns {Promise<{count: number, date: string, limit: number}>}
|
|
47
|
+
*/
|
|
48
|
+
async function getUsageToday() {
|
|
49
|
+
// PRO version: unlimited
|
|
50
|
+
if (isProVersion()) {
|
|
51
|
+
return { count: 0, date: getTodayDateString(), limit: Infinity, isPro: true };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const result = await chrome.storage.local.get(USAGE_STORAGE_KEY);
|
|
55
|
+
const usage = result[USAGE_STORAGE_KEY] || { count: 0, date: '' };
|
|
56
|
+
const today = getTodayDateString();
|
|
57
|
+
|
|
58
|
+
// Reset if new day
|
|
59
|
+
if (usage.date !== today) {
|
|
60
|
+
return { count: 0, date: today, limit: FREE_TIER_DAILY_LIMIT };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { count: usage.count, date: today, limit: FREE_TIER_DAILY_LIMIT };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check if user can start a new recording
|
|
68
|
+
* @returns {Promise<{allowed: boolean, count: number, limit: number, message?: string}>}
|
|
69
|
+
*/
|
|
70
|
+
async function checkUsageBeforeRecording() {
|
|
71
|
+
const usage = await getUsageToday();
|
|
72
|
+
|
|
73
|
+
if (usage.isPro) {
|
|
74
|
+
return { allowed: true, count: 0, limit: Infinity, isPro: true };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (usage.count >= usage.limit) {
|
|
78
|
+
return {
|
|
79
|
+
allowed: false,
|
|
80
|
+
count: usage.count,
|
|
81
|
+
limit: usage.limit,
|
|
82
|
+
message: `Daily limit reached (${usage.count}/${usage.limit}). Upgrade to Pro for unlimited recordings.`
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return { allowed: true, count: usage.count, limit: usage.limit };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Increment usage count after successful recording
|
|
91
|
+
* @returns {Promise<{success: boolean, count: number, limit: number}>}
|
|
92
|
+
*/
|
|
93
|
+
async function incrementUsageAfterRecording() {
|
|
94
|
+
// PRO version: skip tracking
|
|
95
|
+
if (isProVersion()) {
|
|
96
|
+
// console.log('[Usage] PRO version - skipping usage tracking');
|
|
97
|
+
return { success: true, count: 0, limit: Infinity, isPro: true };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const today = getTodayDateString();
|
|
101
|
+
const result = await chrome.storage.local.get(USAGE_STORAGE_KEY);
|
|
102
|
+
const usage = result[USAGE_STORAGE_KEY] || { count: 0, date: '' };
|
|
103
|
+
|
|
104
|
+
// Reset if new day, then increment
|
|
105
|
+
const newCount = (usage.date === today) ? usage.count + 1 : 1;
|
|
106
|
+
|
|
107
|
+
await chrome.storage.local.set({
|
|
108
|
+
[USAGE_STORAGE_KEY]: { count: newCount, date: today }
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// console.log(`[Usage] Recording tracked: ${newCount}/${FREE_TIER_DAILY_LIMIT} today`);
|
|
112
|
+
|
|
113
|
+
return { success: true, count: newCount, limit: FREE_TIER_DAILY_LIMIT };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Console interception configuration for screen recording
|
|
117
|
+
const SCREEN_RECORDING_CONSOLE_CONFIG = {
|
|
118
|
+
overrideFlagName: '__chromePilotConsoleOverridden',
|
|
119
|
+
originalConsoleName: '__chromePilotOriginalConsole',
|
|
120
|
+
relayFlagName: '__chromePilotConsoleRelay',
|
|
121
|
+
messageType: 'chrome-debug-console-log',
|
|
122
|
+
backgroundAction: 'consoleLog'
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// Console interception configuration for workflow recording
|
|
126
|
+
const WORKFLOW_RECORDING_CONSOLE_CONFIG = {
|
|
127
|
+
overrideFlagName: '__chromePilotWorkflowConsoleOverridden',
|
|
128
|
+
originalConsoleName: '__chromePilotWorkflowOriginalConsole',
|
|
129
|
+
relayFlagName: '__chromePilotWorkflowConsoleRelay',
|
|
130
|
+
messageType: 'chrome-debug-workflow-console-log',
|
|
131
|
+
backgroundAction: 'workflowConsoleLog'
|
|
132
|
+
};
|
|
133
|
+
|
|
18
134
|
// Frame queue for handling validation race conditions
|
|
19
135
|
// v2.1.2: Prevents frame loss when lease renewal hasn't propagated yet
|
|
20
136
|
const pendingFrameQueue = new Map(); // sessionId -> array of frame batches
|
|
@@ -38,7 +154,7 @@ class LogStreamer {
|
|
|
38
154
|
async init() {
|
|
39
155
|
// Skip initialization in browser-only mode
|
|
40
156
|
if (serverMode === 'browser-only') {
|
|
41
|
-
console.log('[LogStreamer] Skipping initialization - browser-only mode');
|
|
157
|
+
// console.log('[LogStreamer] Skipping initialization - browser-only mode');
|
|
42
158
|
return;
|
|
43
159
|
}
|
|
44
160
|
|
|
@@ -48,7 +164,7 @@ class LogStreamer {
|
|
|
48
164
|
// Start periodic streaming
|
|
49
165
|
this.startPeriodicStreaming();
|
|
50
166
|
|
|
51
|
-
console.log('[LogStreamer] Initialized with server availability:', this.serverAvailable);
|
|
167
|
+
// console.log('[LogStreamer] Initialized with server availability:', this.serverAvailable);
|
|
52
168
|
}
|
|
53
169
|
|
|
54
170
|
async checkServerAvailability() {
|
|
@@ -60,7 +176,7 @@ class LogStreamer {
|
|
|
60
176
|
});
|
|
61
177
|
if (response.ok) {
|
|
62
178
|
this.serverAvailable = true;
|
|
63
|
-
console.log(`[LogStreamer] Server available on port ${port}`);
|
|
179
|
+
// console.log(`[LogStreamer] Server available on port ${port}`);
|
|
64
180
|
return;
|
|
65
181
|
}
|
|
66
182
|
} catch (error) {
|
|
@@ -68,7 +184,7 @@ class LogStreamer {
|
|
|
68
184
|
}
|
|
69
185
|
}
|
|
70
186
|
this.serverAvailable = false;
|
|
71
|
-
console.log('[LogStreamer] No server available');
|
|
187
|
+
// console.log('[LogStreamer] No server available');
|
|
72
188
|
}
|
|
73
189
|
|
|
74
190
|
startPeriodicStreaming() {
|
|
@@ -111,7 +227,7 @@ class LogStreamer {
|
|
|
111
227
|
// This gives frames time to be processed before logs are associated
|
|
112
228
|
const timeSinceLastFrame = Date.now() - this.lastFrameCapture;
|
|
113
229
|
if (this.lastFrameCapture > 0 && timeSinceLastFrame < 100) {
|
|
114
|
-
console.log('[LogStreamer] Waiting for frame processing before streaming logs');
|
|
230
|
+
// console.log('[LogStreamer] Waiting for frame processing before streaming logs');
|
|
115
231
|
return; // Wait for next cycle
|
|
116
232
|
}
|
|
117
233
|
|
|
@@ -140,7 +256,7 @@ class LogStreamer {
|
|
|
140
256
|
// Method to notify LogStreamer when frame is captured
|
|
141
257
|
notifyFrameCapture() {
|
|
142
258
|
this.lastFrameCapture = Date.now();
|
|
143
|
-
console.log('[LogStreamer] Frame capture notification received');
|
|
259
|
+
// console.log('[LogStreamer] Frame capture notification received');
|
|
144
260
|
}
|
|
145
261
|
|
|
146
262
|
async sendToServer(batch) {
|
|
@@ -164,7 +280,7 @@ class LogStreamer {
|
|
|
164
280
|
const result = await response.json();
|
|
165
281
|
// Only log if we had to try multiple ports (first port failed)
|
|
166
282
|
if (port !== this.serverPorts[0]) {
|
|
167
|
-
console.log(`[LogStreamer] Connected to server on port ${port}`);
|
|
283
|
+
// console.log(`[LogStreamer] Connected to server on port ${port}`);
|
|
168
284
|
}
|
|
169
285
|
return result;
|
|
170
286
|
}
|
|
@@ -215,7 +331,7 @@ class LogStreamer {
|
|
|
215
331
|
if (now >= item.nextRetry && item.attempts < 3) {
|
|
216
332
|
try {
|
|
217
333
|
await this.sendToServer(item.batch);
|
|
218
|
-
console.log(`[LogStreamer] Retry successful for batch with ${item.batch.length} logs`);
|
|
334
|
+
// console.log(`[LogStreamer] Retry successful for batch with ${item.batch.length} logs`);
|
|
219
335
|
} catch (error) {
|
|
220
336
|
item.attempts++;
|
|
221
337
|
item.nextRetry = now + (1000 * Math.pow(2, item.attempts)); // Exponential backoff
|
|
@@ -273,7 +389,7 @@ class LogBuffer {
|
|
|
273
389
|
// Flush all buffers to Chrome storage
|
|
274
390
|
async flushAll() {
|
|
275
391
|
if (this.flushInProgress) {
|
|
276
|
-
console.log('[LogBuffer] Flush already in progress, waiting for completion...');
|
|
392
|
+
// console.log('[LogBuffer] Flush already in progress, waiting for completion...');
|
|
277
393
|
// Wait for current flush to complete instead of skipping
|
|
278
394
|
while (this.flushInProgress) {
|
|
279
395
|
await new Promise(resolve => setTimeout(resolve, 10));
|
|
@@ -286,7 +402,7 @@ class LogBuffer {
|
|
|
286
402
|
const startTime = Date.now();
|
|
287
403
|
|
|
288
404
|
try {
|
|
289
|
-
console.log(`[LogBuffer] Starting flushAll for ${this.tabBuffers.size} tabs`);
|
|
405
|
+
// console.log(`[LogBuffer] Starting flushAll for ${this.tabBuffers.size} tabs`);
|
|
290
406
|
|
|
291
407
|
for (const [tabId, tabBuffer] of this.tabBuffers) {
|
|
292
408
|
flushPromises.push(tabBuffer.flush());
|
|
@@ -294,7 +410,7 @@ class LogBuffer {
|
|
|
294
410
|
|
|
295
411
|
await Promise.all(flushPromises);
|
|
296
412
|
const duration = Date.now() - startTime;
|
|
297
|
-
console.log(`[LogBuffer] Successfully flushed all buffers for ${this.tabBuffers.size} tabs in ${duration}ms`);
|
|
413
|
+
// console.log(`[LogBuffer] Successfully flushed all buffers for ${this.tabBuffers.size} tabs in ${duration}ms`);
|
|
298
414
|
} catch (error) {
|
|
299
415
|
console.error('[LogBuffer] Error during flush operation:', error);
|
|
300
416
|
throw error; // Re-throw to ensure proper error handling upstream
|
|
@@ -315,7 +431,7 @@ class LogBuffer {
|
|
|
315
431
|
clearTab(tabId) {
|
|
316
432
|
if (this.tabBuffers.has(tabId)) {
|
|
317
433
|
this.tabBuffers.delete(tabId);
|
|
318
|
-
console.log(`[LogBuffer] Cleared buffer for tab ${tabId}`);
|
|
434
|
+
// console.log(`[LogBuffer] Cleared buffer for tab ${tabId}`);
|
|
319
435
|
}
|
|
320
436
|
}
|
|
321
437
|
|
|
@@ -341,7 +457,7 @@ class LogBuffer {
|
|
|
341
457
|
// Debug method to log current buffer state
|
|
342
458
|
logStats() {
|
|
343
459
|
const stats = this.getStats();
|
|
344
|
-
console.log('[LogBuffer] Current statistics:', stats);
|
|
460
|
+
// console.log('[LogBuffer] Current statistics:', stats);
|
|
345
461
|
return stats;
|
|
346
462
|
}
|
|
347
463
|
}
|
|
@@ -456,7 +572,7 @@ class LogTabBuffer {
|
|
|
456
572
|
}
|
|
457
573
|
|
|
458
574
|
this.totalLogsFlushed += this.buffer.length;
|
|
459
|
-
console.log(`[LogBuffer] Flushed ${this.buffer.length} logs for tab ${this.tabId} (total: ${this.totalLogsFlushed})`);
|
|
575
|
+
// console.log(`[LogBuffer] Flushed ${this.buffer.length} logs for tab ${this.tabId} (total: ${this.totalLogsFlushed})`);
|
|
460
576
|
|
|
461
577
|
// Clear the buffer
|
|
462
578
|
this.buffer = [];
|
|
@@ -481,6 +597,162 @@ class LogTabBuffer {
|
|
|
481
597
|
}
|
|
482
598
|
}
|
|
483
599
|
|
|
600
|
+
/**
|
|
601
|
+
* WorkflowLogBuffer - Race-safe log buffer for workflow recording
|
|
602
|
+
*
|
|
603
|
+
* Mirrors LogTabBuffer but uses workflow-specific storage keys (`workflow_${tabId}`).
|
|
604
|
+
* This prevents race conditions when multiple logs arrive rapidly during workflow recording.
|
|
605
|
+
*
|
|
606
|
+
* Technical Debt Note: This class duplicates LogTabBuffer logic. Future refactoring
|
|
607
|
+
* could make LogTabBuffer accept a configurable storage key prefix to eliminate duplication.
|
|
608
|
+
*/
|
|
609
|
+
class WorkflowLogBuffer {
|
|
610
|
+
constructor(tabId) {
|
|
611
|
+
this.tabId = tabId;
|
|
612
|
+
this.buffer = [];
|
|
613
|
+
this.processing = false;
|
|
614
|
+
this.processingQueue = [];
|
|
615
|
+
this.totalLogsAdded = 0;
|
|
616
|
+
this.totalLogsFlushed = 0;
|
|
617
|
+
this.lastFlushTime = 0;
|
|
618
|
+
this.storageKey = `workflow_${tabId}`;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Add log to buffer with queuing to prevent race conditions
|
|
622
|
+
async addLog(logEntry) {
|
|
623
|
+
return new Promise((resolve, reject) => {
|
|
624
|
+
this.processingQueue.push({
|
|
625
|
+
type: 'add',
|
|
626
|
+
logEntry,
|
|
627
|
+
resolve,
|
|
628
|
+
reject
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
// Process queue if not already processing
|
|
632
|
+
if (!this.processing) {
|
|
633
|
+
this.processQueue();
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// Flush buffer to Chrome storage
|
|
639
|
+
async flush() {
|
|
640
|
+
return new Promise((resolve, reject) => {
|
|
641
|
+
this.processingQueue.push({
|
|
642
|
+
type: 'flush',
|
|
643
|
+
resolve,
|
|
644
|
+
reject
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
// Process queue if not already processing
|
|
648
|
+
if (!this.processing) {
|
|
649
|
+
this.processQueue();
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Sequential processing of queue to prevent race conditions
|
|
655
|
+
async processQueue() {
|
|
656
|
+
if (this.processing) {
|
|
657
|
+
return; // Already processing
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
this.processing = true;
|
|
661
|
+
|
|
662
|
+
try {
|
|
663
|
+
while (this.processingQueue.length > 0) {
|
|
664
|
+
const operation = this.processingQueue.shift();
|
|
665
|
+
|
|
666
|
+
try {
|
|
667
|
+
if (operation.type === 'add') {
|
|
668
|
+
this.buffer.push(operation.logEntry);
|
|
669
|
+
this.totalLogsAdded++;
|
|
670
|
+
|
|
671
|
+
// Auto-flush if buffer gets too large
|
|
672
|
+
if (this.buffer.length >= 100) {
|
|
673
|
+
await this.performFlush();
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
operation.resolve();
|
|
677
|
+
} else if (operation.type === 'flush') {
|
|
678
|
+
await this.performFlush();
|
|
679
|
+
operation.resolve();
|
|
680
|
+
}
|
|
681
|
+
} catch (error) {
|
|
682
|
+
operation.reject(error);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
} finally {
|
|
686
|
+
this.processing = false;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Actual flush operation to Chrome storage
|
|
691
|
+
async performFlush() {
|
|
692
|
+
if (this.buffer.length === 0) {
|
|
693
|
+
return; // Nothing to flush
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
try {
|
|
697
|
+
// Read current logs from storage
|
|
698
|
+
const result = await chrome.storage.session.get(this.storageKey);
|
|
699
|
+
const existingLogs = result[this.storageKey] || [];
|
|
700
|
+
|
|
701
|
+
// Append new logs
|
|
702
|
+
const updatedLogs = [...existingLogs, ...this.buffer];
|
|
703
|
+
|
|
704
|
+
// Check storage size limit
|
|
705
|
+
const estimatedSize = JSON.stringify(updatedLogs).length;
|
|
706
|
+
if (estimatedSize > 500000) { // ~500KB limit
|
|
707
|
+
const beforeCount = updatedLogs.length;
|
|
708
|
+
const retentionPercentage = 0.95;
|
|
709
|
+
const afterCount = Math.floor(updatedLogs.length * retentionPercentage);
|
|
710
|
+
console.warn(`[WorkflowLogBuffer] Storage size limit approached for tab ${this.tabId}, truncating from ${beforeCount} to ${afterCount} logs`);
|
|
711
|
+
const recentLogs = updatedLogs.slice(-afterCount);
|
|
712
|
+
await chrome.storage.session.set({ [this.storageKey]: recentLogs });
|
|
713
|
+
} else {
|
|
714
|
+
await chrome.storage.session.set({ [this.storageKey]: updatedLogs });
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
this.totalLogsFlushed += this.buffer.length;
|
|
718
|
+
// console.log(`[WorkflowLogBuffer] Flushed ${this.buffer.length} logs for tab ${this.tabId} (total: ${this.totalLogsFlushed})`);
|
|
719
|
+
|
|
720
|
+
// Clear the buffer
|
|
721
|
+
this.buffer = [];
|
|
722
|
+
this.lastFlushTime = Date.now();
|
|
723
|
+
|
|
724
|
+
} catch (error) {
|
|
725
|
+
console.error(`[WorkflowLogBuffer] Failed to flush logs for tab ${this.tabId}:`, error);
|
|
726
|
+
throw error;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Get buffer statistics
|
|
731
|
+
getStats() {
|
|
732
|
+
return {
|
|
733
|
+
bufferedLogs: this.buffer.length,
|
|
734
|
+
totalAdded: this.totalLogsAdded,
|
|
735
|
+
totalFlushed: this.totalLogsFlushed,
|
|
736
|
+
processing: this.processing,
|
|
737
|
+
queueLength: this.processingQueue.length,
|
|
738
|
+
lastFlushTime: this.lastFlushTime
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// Clean up buffer when recording stops
|
|
743
|
+
async cleanup() {
|
|
744
|
+
try {
|
|
745
|
+
// Flush any remaining logs
|
|
746
|
+
if (this.buffer.length > 0) {
|
|
747
|
+
await this.performFlush();
|
|
748
|
+
}
|
|
749
|
+
// console.log(`[WorkflowLogBuffer] Cleaned up buffer for tab ${this.tabId}`);
|
|
750
|
+
} catch (error) {
|
|
751
|
+
console.error(`[WorkflowLogBuffer] Error during cleanup for tab ${this.tabId}:`, error);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
484
756
|
// Initialize data buffer and upload manager
|
|
485
757
|
let dataBuffer = null;
|
|
486
758
|
let uploadManager = null;
|
|
@@ -537,10 +809,10 @@ const DATA_PROTECTION_TIERS = {
|
|
|
537
809
|
async function initializeServices() {
|
|
538
810
|
try {
|
|
539
811
|
// FIRST: Initialize comprehensive error handling system
|
|
540
|
-
console.log('[Background] Initializing comprehensive error handling...');
|
|
812
|
+
// console.log('[Background] Initializing comprehensive error handling...');
|
|
541
813
|
const errorHandlingSuccess = await initializeErrorHandling();
|
|
542
814
|
if (errorHandlingSuccess) {
|
|
543
|
-
console.log('[Background] Comprehensive error handling initialized successfully');
|
|
815
|
+
// console.log('[Background] Comprehensive error handling initialized successfully');
|
|
544
816
|
} else {
|
|
545
817
|
console.warn('[Background] Error handling initialization failed - continuing with basic functionality');
|
|
546
818
|
}
|
|
@@ -548,16 +820,16 @@ async function initializeServices() {
|
|
|
548
820
|
// Initialize the data buffer
|
|
549
821
|
dataBuffer = new DataBuffer();
|
|
550
822
|
await dataBuffer.init();
|
|
551
|
-
console.log('[Background] Data buffer initialized');
|
|
823
|
+
// console.log('[Background] Data buffer initialized');
|
|
552
824
|
|
|
553
825
|
// Only create upload manager if data buffer initialized successfully
|
|
554
826
|
try {
|
|
555
827
|
uploadManager = new UploadManager(dataBuffer);
|
|
556
828
|
const serverAvailable = await uploadManager.init();
|
|
557
829
|
if (serverAvailable) {
|
|
558
|
-
console.log('[Background] Upload manager initialized with server');
|
|
830
|
+
// console.log('[Background] Upload manager initialized with server');
|
|
559
831
|
} else {
|
|
560
|
-
console.log('[Background] Upload manager initialized but no server available yet');
|
|
832
|
+
// console.log('[Background] Upload manager initialized but no server available yet');
|
|
561
833
|
}
|
|
562
834
|
} catch (uploadErr) {
|
|
563
835
|
console.error('[Background] Failed to initialize upload manager:', uploadErr);
|
|
@@ -569,7 +841,7 @@ async function initializeServices() {
|
|
|
569
841
|
try {
|
|
570
842
|
logStreamer = new LogStreamer();
|
|
571
843
|
await logStreamer.init();
|
|
572
|
-
console.log('[Background] Log streamer initialized');
|
|
844
|
+
// console.log('[Background] Log streamer initialized');
|
|
573
845
|
} catch (streamErr) {
|
|
574
846
|
console.error('[Background] Failed to initialize log streamer:', streamErr);
|
|
575
847
|
logStreamer = null;
|
|
@@ -578,7 +850,7 @@ async function initializeServices() {
|
|
|
578
850
|
// Initialize log buffer for race-condition-free storage
|
|
579
851
|
try {
|
|
580
852
|
logBuffer = new LogBuffer();
|
|
581
|
-
console.log('[Background] Log buffer initialized');
|
|
853
|
+
// console.log('[Background] Log buffer initialized');
|
|
582
854
|
} catch (bufferErr) {
|
|
583
855
|
console.error('[Background] Failed to initialize log buffer:', bufferErr);
|
|
584
856
|
logBuffer = null;
|
|
@@ -587,7 +859,7 @@ async function initializeServices() {
|
|
|
587
859
|
// Initialize browser recording manager for browser-only mode
|
|
588
860
|
try {
|
|
589
861
|
browserRecordingManager = new BrowserRecordingManager(dataBuffer);
|
|
590
|
-
console.log('[Background] Browser recording manager initialized');
|
|
862
|
+
// console.log('[Background] Browser recording manager initialized');
|
|
591
863
|
} catch (recordingErr) {
|
|
592
864
|
console.error('[Background] Failed to initialize browser recording manager:', recordingErr);
|
|
593
865
|
browserRecordingManager = null;
|
|
@@ -597,12 +869,12 @@ async function initializeServices() {
|
|
|
597
869
|
try {
|
|
598
870
|
if (!sessionManager) {
|
|
599
871
|
sessionManager = new ChromeExtensionSessionManager();
|
|
600
|
-
console.log('[Background] Session manager initialized');
|
|
872
|
+
// console.log('[Background] Session manager initialized');
|
|
601
873
|
}
|
|
602
874
|
|
|
603
875
|
// Recover any stuck sessions on startup
|
|
604
876
|
await sessionManager.recoverSessions();
|
|
605
|
-
console.log('[Background] Session recovery completed');
|
|
877
|
+
// console.log('[Background] Session recovery completed');
|
|
606
878
|
} catch (sessionErr) {
|
|
607
879
|
console.error('[Background] Failed to initialize session manager:', sessionErr);
|
|
608
880
|
// Session manager critical for recording - but don't fail completely
|
|
@@ -655,10 +927,14 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
655
927
|
'restoreFromPoint',
|
|
656
928
|
'getWebSocketPort',
|
|
657
929
|
'startRecording',
|
|
930
|
+
'stopRecording',
|
|
931
|
+
'openFrameEditor',
|
|
658
932
|
'checkConnection',
|
|
659
933
|
'workflowConsoleLog', // v2.0.8: handle workflow console logs
|
|
660
934
|
'workflowAction', // v2.0.8: handle workflow actions
|
|
661
|
-
'renewLease'
|
|
935
|
+
'renewLease', // v2.1.2: session manager lease renewal for frame capture
|
|
936
|
+
'frameSessionComplete', // Frame recording completion notification
|
|
937
|
+
'recordingStopped' // Recording stopped notification for popup refresh
|
|
662
938
|
];
|
|
663
939
|
|
|
664
940
|
if (handledByOtherListener.includes(messageType)) {
|
|
@@ -704,7 +980,27 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
704
980
|
sendResponse({ status: 'error', message: error.message });
|
|
705
981
|
}
|
|
706
982
|
break;
|
|
707
|
-
|
|
983
|
+
|
|
984
|
+
case 'getUsage':
|
|
985
|
+
// Get current usage count for popup display
|
|
986
|
+
getUsageToday().then(usage => {
|
|
987
|
+
sendResponse({ success: true, ...usage });
|
|
988
|
+
}).catch(error => {
|
|
989
|
+
console.error('[Usage] Failed to get usage:', error);
|
|
990
|
+
sendResponse({ success: false, error: error.message });
|
|
991
|
+
});
|
|
992
|
+
return true; // Async response
|
|
993
|
+
|
|
994
|
+
case 'checkUsageLimit':
|
|
995
|
+
// Check if user can start a new recording
|
|
996
|
+
checkUsageBeforeRecording().then(result => {
|
|
997
|
+
sendResponse(result);
|
|
998
|
+
}).catch(error => {
|
|
999
|
+
console.error('[Usage] Failed to check usage limit:', error);
|
|
1000
|
+
sendResponse({ allowed: true, error: error.message }); // Fail-open
|
|
1001
|
+
});
|
|
1002
|
+
return true; // Async response
|
|
1003
|
+
|
|
708
1004
|
case 'UPLOAD_BATCH':
|
|
709
1005
|
// Add events to the background's DataBuffer for upload
|
|
710
1006
|
if (message.events && message.recordingId) {
|
|
@@ -716,12 +1012,12 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
716
1012
|
}));
|
|
717
1013
|
|
|
718
1014
|
dataBuffer.addBatch(eventsWithRecordingId).then(() => {
|
|
719
|
-
console.log(`[Background] Added ${eventsWithRecordingId.length} events to buffer for recording ${message.recordingId}`);
|
|
1015
|
+
// console.log(`[Background] Added ${eventsWithRecordingId.length} events to buffer for recording ${message.recordingId}`);
|
|
720
1016
|
// Create a batch if we have enough events
|
|
721
1017
|
return dataBuffer.createBatch(message.recordingId);
|
|
722
1018
|
}).then(batch => {
|
|
723
1019
|
if (batch) {
|
|
724
|
-
console.log(`[Background] Created batch ${batch.id} with ${batch.events.length} events`);
|
|
1020
|
+
// console.log(`[Background] Created batch ${batch.id} with ${batch.events.length} events`);
|
|
725
1021
|
// Process upload queue if upload manager is available
|
|
726
1022
|
if (uploadManager) {
|
|
727
1023
|
uploadManager.processUploadQueue();
|
|
@@ -790,7 +1086,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
790
1086
|
sessionId: message.sessionId
|
|
791
1087
|
}).catch(() => {
|
|
792
1088
|
// Content script might not be available
|
|
793
|
-
console.log('[Background] Could not forward start-screen-capture-tracking to content script');
|
|
1089
|
+
// console.log('[Background] Could not forward start-screen-capture-tracking to content script');
|
|
794
1090
|
});
|
|
795
1091
|
}
|
|
796
1092
|
sendResponse({ success: true });
|
|
@@ -802,7 +1098,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
802
1098
|
chrome.tabs.sendMessage(sender.tab.id, {
|
|
803
1099
|
type: 'stop-screen-capture-tracking'
|
|
804
1100
|
}).catch(() => {
|
|
805
|
-
console.log('[Background] Could not forward stop-screen-capture-tracking to content script');
|
|
1101
|
+
// console.log('[Background] Could not forward stop-screen-capture-tracking to content script');
|
|
806
1102
|
});
|
|
807
1103
|
}
|
|
808
1104
|
sendResponse({ success: true });
|
|
@@ -832,7 +1128,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
832
1128
|
frameIndex: frameIndex
|
|
833
1129
|
};
|
|
834
1130
|
screenInteractions.push(interaction);
|
|
835
|
-
console.log('[Background] Visual feedback interaction recorded:', interaction.type, 'Total:', screenInteractions.length);
|
|
1131
|
+
// console.log('[Background] Visual feedback interaction recorded:', interaction.type, 'Total:', screenInteractions.length);
|
|
836
1132
|
}
|
|
837
1133
|
}).catch(error => {
|
|
838
1134
|
console.error('Error validating recording state for interaction:', error);
|
|
@@ -845,7 +1141,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
845
1141
|
frameIndex: frameCounter.get(currentRecordingSessionId) || 0
|
|
846
1142
|
};
|
|
847
1143
|
screenInteractions.push(interaction);
|
|
848
|
-
console.log('[Background] Visual feedback interaction recorded:', interaction.type, 'Total:', screenInteractions.length);
|
|
1144
|
+
// console.log('[Background] Visual feedback interaction recorded:', interaction.type, 'Total:', screenInteractions.length);
|
|
849
1145
|
}
|
|
850
1146
|
}
|
|
851
1147
|
}
|
|
@@ -858,7 +1154,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
858
1154
|
chrome.tabs.sendMessage(sender.tab.id, {
|
|
859
1155
|
type: 'screen-capture-frame-captured'
|
|
860
1156
|
}).catch(() => {
|
|
861
|
-
console.log('[Background] Could not forward frame flash to content script');
|
|
1157
|
+
// console.log('[Background] Could not forward frame flash to content script');
|
|
862
1158
|
});
|
|
863
1159
|
}
|
|
864
1160
|
// Don't send response to avoid warnings
|
|
@@ -869,7 +1165,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
869
1165
|
if (logBuffer && sender.tab) {
|
|
870
1166
|
const tabId = sender.tab.id;
|
|
871
1167
|
const testLogsCount = message.count || 1000;
|
|
872
|
-
console.log(`[Background] Starting LogBuffer race condition test with ${testLogsCount} logs for tab ${tabId}`);
|
|
1168
|
+
// console.log(`[Background] Starting LogBuffer race condition test with ${testLogsCount} logs for tab ${tabId}`);
|
|
873
1169
|
|
|
874
1170
|
// Simulate rapid log generation like a real high-volume scenario
|
|
875
1171
|
const startTime = Date.now();
|
|
@@ -892,8 +1188,8 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
892
1188
|
Promise.all(logPromises).then(() => {
|
|
893
1189
|
const endTime = Date.now();
|
|
894
1190
|
const stats = logBuffer.getStats();
|
|
895
|
-
console.log(`[Background] LogBuffer test completed in ${endTime - startTime}ms`);
|
|
896
|
-
console.log(`[Background] Test results:`, stats);
|
|
1191
|
+
// console.log(`[Background] LogBuffer test completed in ${endTime - startTime}ms`);
|
|
1192
|
+
// console.log(`[Background] Test results:`, stats);
|
|
897
1193
|
|
|
898
1194
|
sendResponse({
|
|
899
1195
|
success: true,
|
|
@@ -923,7 +1219,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
923
1219
|
// DEBUG: Get current LogBuffer statistics
|
|
924
1220
|
if (logBuffer) {
|
|
925
1221
|
const stats = logBuffer.getStats();
|
|
926
|
-
console.log('[Background] LogBuffer stats requested:', stats);
|
|
1222
|
+
// console.log('[Background] LogBuffer stats requested:', stats);
|
|
927
1223
|
sendResponse({
|
|
928
1224
|
success: true,
|
|
929
1225
|
stats: stats
|
|
@@ -938,10 +1234,10 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
938
1234
|
|
|
939
1235
|
case 'getBrowserRecordings':
|
|
940
1236
|
// Get all browser-only recordings from IndexedDB
|
|
941
|
-
console.log('[Background] getBrowserRecordings - Manager exists:', !!browserRecordingManager);
|
|
1237
|
+
// console.log('[Background] getBrowserRecordings - Manager exists:', !!browserRecordingManager);
|
|
942
1238
|
if (browserRecordingManager) {
|
|
943
1239
|
browserRecordingManager.listRecordings().then(recordings => {
|
|
944
|
-
console.log('[Background] Recordings retrieved from manager:', recordings);
|
|
1240
|
+
// console.log('[Background] Recordings retrieved from manager:', recordings);
|
|
945
1241
|
sendResponse(recordings);
|
|
946
1242
|
}).catch(error => {
|
|
947
1243
|
console.error('[Background] Failed to get browser recordings:', error);
|
|
@@ -988,7 +1284,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
988
1284
|
// Get frames for a browser-only recording
|
|
989
1285
|
if (dataBuffer && message.sessionId) {
|
|
990
1286
|
dataBuffer.getBrowserFrames(message.sessionId).then(frames => {
|
|
991
|
-
console.log(`[DEBUG] getBrowserRecordingFrames: Retrieved ${frames.length} frames from IndexedDB`);
|
|
1287
|
+
// console.log(`[DEBUG] getBrowserRecordingFrames: Retrieved ${frames.length} frames from IndexedDB`);
|
|
992
1288
|
|
|
993
1289
|
// Debug first frame to see structure
|
|
994
1290
|
if (frames.length > 0) {
|
|
@@ -1002,7 +1298,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
1002
1298
|
frameIndex: firstFrame.frameIndex,
|
|
1003
1299
|
allKeys: Object.keys(firstFrame)
|
|
1004
1300
|
};
|
|
1005
|
-
console.log('[DEBUG] First frame structure:', JSON.stringify(debugInfo, null, 2));
|
|
1301
|
+
// console.log('[DEBUG] First frame structure:', JSON.stringify(debugInfo, null, 2));
|
|
1006
1302
|
}
|
|
1007
1303
|
|
|
1008
1304
|
// Transform frames to match frame editor's expected structure
|
|
@@ -1019,7 +1315,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
1019
1315
|
logsCount: transformedFrames[0].logs ? transformedFrames[0].logs.length : 0,
|
|
1020
1316
|
allKeys: Object.keys(transformedFrames[0])
|
|
1021
1317
|
} : 'No frames';
|
|
1022
|
-
console.log('[DEBUG] Transformed first frame:', JSON.stringify(transformDebug, null, 2));
|
|
1318
|
+
// console.log('[DEBUG] Transformed first frame:', JSON.stringify(transformDebug, null, 2));
|
|
1023
1319
|
|
|
1024
1320
|
sendResponse({ success: true, frames: transformedFrames });
|
|
1025
1321
|
}).catch(error => {
|
|
@@ -1045,107 +1341,23 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
1045
1341
|
default:
|
|
1046
1342
|
// Don't warn or respond for messages that should be handled by other listener (v2.0.6)
|
|
1047
1343
|
// This prevents intercepting workflow recording messages
|
|
1048
|
-
console.log('[Background v2.0.6] Message not handled by this listener:', messageType);
|
|
1344
|
+
// console.log('[Background v2.0.6] Message not handled by this listener:', messageType);
|
|
1049
1345
|
// Don't send response - let other listener handle it
|
|
1050
1346
|
return;
|
|
1051
1347
|
}
|
|
1052
1348
|
});
|
|
1053
1349
|
|
|
1054
|
-
//
|
|
1055
|
-
async function checkAndActivateLicense() {
|
|
1056
|
-
try {
|
|
1057
|
-
console.log('[License Auto-Activation] Checking for activation file...');
|
|
1058
|
-
|
|
1059
|
-
// Try to fetch the activation file from the extension directory
|
|
1060
|
-
const activationUrl = chrome.runtime.getURL('license-activation.json');
|
|
1061
|
-
const response = await fetch(activationUrl);
|
|
1062
|
-
|
|
1063
|
-
if (!response.ok) {
|
|
1064
|
-
console.log('[License Auto-Activation] No activation file found - using normal license flow');
|
|
1065
|
-
return;
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
const activationData = await response.json();
|
|
1069
|
-
console.log('[License Auto-Activation] Found activation file:', {
|
|
1070
|
-
tier: activationData.tier,
|
|
1071
|
-
activated_at: activationData.activated_at
|
|
1072
|
-
});
|
|
1073
|
-
|
|
1074
|
-
// Check if license is already cached and valid
|
|
1075
|
-
const cached = await chrome.storage.local.get('chromedebug_license_cache');
|
|
1076
|
-
if (cached.chromedebug_license_cache?.valid && cached.chromedebug_license_cache?.tier === 'pro') {
|
|
1077
|
-
console.log('[License Auto-Activation] License already activated and cached');
|
|
1078
|
-
return;
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
// Activate the license
|
|
1082
|
-
const licenseKey = activationData.license_key;
|
|
1083
|
-
console.log('[License Auto-Activation] Activating license...');
|
|
1084
|
-
|
|
1085
|
-
// Call LemonSqueezy activation via Firebase Cloud Function
|
|
1086
|
-
const instanceId = crypto.randomUUID();
|
|
1087
|
-
await chrome.storage.local.set({
|
|
1088
|
-
'chromedebug_instance_id': instanceId,
|
|
1089
|
-
'ls_instance_id': instanceId,
|
|
1090
|
-
'ls_license_key': licenseKey
|
|
1091
|
-
});
|
|
1092
|
-
|
|
1093
|
-
const activationResponse = await fetch(`${FUNCTIONS_URL}/activateLicense`, {
|
|
1094
|
-
method: 'POST',
|
|
1095
|
-
headers: {'Content-Type': 'application/json'},
|
|
1096
|
-
body: JSON.stringify({
|
|
1097
|
-
licenseKey,
|
|
1098
|
-
instanceId,
|
|
1099
|
-
instanceName: `ChromeDebug-${navigator.userAgent.match(/Chrome\/(\d+)/)?.[1] || 'Unknown'}`
|
|
1100
|
-
})
|
|
1101
|
-
});
|
|
1102
|
-
|
|
1103
|
-
if (!activationResponse.ok) {
|
|
1104
|
-
throw new Error(`Activation failed: HTTP ${activationResponse.status}`);
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
const activationResult = await activationResponse.json();
|
|
1108
|
-
console.log('[License Auto-Activation] Activation result:', activationResult);
|
|
1109
|
-
|
|
1110
|
-
if (activationResult.activated || activationResult.valid) {
|
|
1111
|
-
// Cache the license status
|
|
1112
|
-
const licenseCache = {
|
|
1113
|
-
valid: true,
|
|
1114
|
-
tier: 'pro',
|
|
1115
|
-
licenseKey: licenseKey.substring(0, 8) + '...',
|
|
1116
|
-
cachedAt: Date.now(),
|
|
1117
|
-
graceUntil: Date.now() + (30 * 24 * 60 * 60 * 1000) // 30 days
|
|
1118
|
-
};
|
|
1119
|
-
|
|
1120
|
-
await chrome.storage.local.set({
|
|
1121
|
-
'chromedebug_license_cache': licenseCache
|
|
1122
|
-
});
|
|
1123
|
-
|
|
1124
|
-
console.log('[License Auto-Activation] ✓ PRO license activated successfully!');
|
|
1125
|
-
console.log('[License Auto-Activation] Cached license status:', licenseCache);
|
|
1126
|
-
} else {
|
|
1127
|
-
console.error('[License Auto-Activation] Activation returned invalid status:', activationResult);
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
} catch (error) {
|
|
1131
|
-
// Don't block extension startup on activation errors
|
|
1132
|
-
console.error('[License Auto-Activation] Failed to auto-activate license:', error);
|
|
1133
|
-
console.log('[License Auto-Activation] Extension will continue with normal license flow');
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1350
|
+
// License checking removed - using separate Chrome Web Store listings for FREE and PRO
|
|
1136
1351
|
|
|
1137
1352
|
// Session recovery on startup - handle any stuck recordings from previous session
|
|
1138
1353
|
chrome.runtime.onStartup.addListener(async () => {
|
|
1139
|
-
console.log('[Background] Extension startup detected - checking for stuck sessions');
|
|
1140
|
-
|
|
1141
|
-
// Check for PRO license auto-activation first
|
|
1142
|
-
await checkAndActivateLicense();
|
|
1354
|
+
// console.log('[Background] Extension startup detected - checking for stuck sessions');
|
|
1143
1355
|
|
|
1144
1356
|
if (sessionManager) {
|
|
1145
1357
|
try {
|
|
1146
1358
|
// Use existing recovery method from session manager
|
|
1147
1359
|
await sessionManager.recoverSessions();
|
|
1148
|
-
console.log('[Background] Startup session recovery completed');
|
|
1360
|
+
// console.log('[Background] Startup session recovery completed');
|
|
1149
1361
|
} catch (error) {
|
|
1150
1362
|
console.error('[Background] Failed to recover sessions on startup:', error);
|
|
1151
1363
|
}
|
|
@@ -1154,12 +1366,9 @@ chrome.runtime.onStartup.addListener(async () => {
|
|
|
1154
1366
|
}
|
|
1155
1367
|
});
|
|
1156
1368
|
|
|
1157
|
-
// Extension install/update
|
|
1369
|
+
// Extension install/update
|
|
1158
1370
|
chrome.runtime.onInstalled.addListener(async (details) => {
|
|
1159
|
-
console.log('[Background] Extension installed/updated:', details.reason);
|
|
1160
|
-
|
|
1161
|
-
// Check for PRO license auto-activation on install/update
|
|
1162
|
-
await checkAndActivateLicense();
|
|
1371
|
+
// console.log('[Background] Extension installed/updated:', details.reason);
|
|
1163
1372
|
});
|
|
1164
1373
|
|
|
1165
1374
|
// Listen for tab updates to handle restore points
|
|
@@ -1171,7 +1380,7 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
|
|
1171
1380
|
isCurrentlyRecordingAsync().then(async (recording) => {
|
|
1172
1381
|
const currentTabId = await getCurrentTabIdAsync();
|
|
1173
1382
|
if (recording && tabId === currentTabId && tab.url && !tab.url.startsWith('chrome://') && !tab.url.startsWith('chrome-extension://')) {
|
|
1174
|
-
console.log('Re-injecting console logging for navigation during recording');
|
|
1383
|
+
// console.log('Re-injecting console logging for navigation during recording');
|
|
1175
1384
|
startCapturingLogs(tabId).catch(err => {
|
|
1176
1385
|
console.error('Failed to re-inject console logging:', err);
|
|
1177
1386
|
});
|
|
@@ -1182,7 +1391,7 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
|
|
1182
1391
|
} else {
|
|
1183
1392
|
// Legacy fallback
|
|
1184
1393
|
if (tabId === recordingTabId && isCurrentlyRecording && tab.url && !tab.url.startsWith('chrome://') && !tab.url.startsWith('chrome-extension://')) {
|
|
1185
|
-
console.log('Re-injecting console logging for navigation during recording');
|
|
1394
|
+
// console.log('Re-injecting console logging for navigation during recording');
|
|
1186
1395
|
startCapturingLogs(tabId).catch(err => {
|
|
1187
1396
|
console.error('Failed to re-inject console logging:', err);
|
|
1188
1397
|
});
|
|
@@ -1257,6 +1466,43 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
|
|
1257
1466
|
}
|
|
1258
1467
|
});
|
|
1259
1468
|
|
|
1469
|
+
// Defensive cleanup when tabs are closed
|
|
1470
|
+
chrome.tabs.onRemoved.addListener((tabId, removeInfo) => {
|
|
1471
|
+
// Clean up any workflow recording state for closed tabs
|
|
1472
|
+
if (workflowRecordingTabs.has(tabId)) {
|
|
1473
|
+
// console.log(`[Workflow] Tab ${tabId} closed during recording, cleaning up`);
|
|
1474
|
+
workflowRecordingTabs.delete(tabId);
|
|
1475
|
+
workflowIncludeLogs.delete(tabId);
|
|
1476
|
+
workflowScreenshotSettings.delete(tabId);
|
|
1477
|
+
workflowSessionNames.delete(tabId);
|
|
1478
|
+
|
|
1479
|
+
// Clean up WorkflowLogBuffer
|
|
1480
|
+
const buffer = workflowLogBuffers.get(tabId);
|
|
1481
|
+
if (buffer) {
|
|
1482
|
+
buffer.cleanup().then(() => {
|
|
1483
|
+
workflowLogBuffers.delete(tabId);
|
|
1484
|
+
// console.log(`[Workflow] Cleaned up WorkflowLogBuffer for closed tab ${tabId}`);
|
|
1485
|
+
}).catch(err => {
|
|
1486
|
+
console.error(`[Workflow] Error cleaning up buffer for closed tab ${tabId}:`, err);
|
|
1487
|
+
workflowLogBuffers.delete(tabId);
|
|
1488
|
+
});
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
// Remove session storage
|
|
1492
|
+
chrome.storage.session.remove(`workflow_${tabId}`).catch(err => {
|
|
1493
|
+
console.error(`[Workflow] Error removing session storage for closed tab ${tabId}:`, err);
|
|
1494
|
+
});
|
|
1495
|
+
|
|
1496
|
+
// Notify popup that recording has stopped (tab was closed)
|
|
1497
|
+
chrome.storage.local.set({
|
|
1498
|
+
workflowRecording: false,
|
|
1499
|
+
workflowStartTime: null
|
|
1500
|
+
}).catch(err => {
|
|
1501
|
+
console.error(`[Workflow] Error updating storage for closed tab ${tabId}:`, err);
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
});
|
|
1505
|
+
|
|
1260
1506
|
// Session Manager for robust recording state management
|
|
1261
1507
|
let sessionManager = null;
|
|
1262
1508
|
let currentSession = null; // Cache current session state
|
|
@@ -1267,17 +1513,154 @@ let hasOffscreenDocument = false;
|
|
|
1267
1513
|
let screenInteractions = [];
|
|
1268
1514
|
let recordingServerPort = null; // Track which port is being used for current recording
|
|
1269
1515
|
|
|
1270
|
-
// Legacy compatibility variables (DEPRECATED - remove after migration is complete)
|
|
1271
|
-
let recordingTabId = null; // DEPRECATED: Use getCurrentTabIdAsync() instead
|
|
1272
|
-
let isCurrentlyRecording = false; // DEPRECATED: Use isCurrentlyRecordingAsync() instead
|
|
1273
|
-
let currentRecordingSessionId = null; // DEPRECATED: Use getCurrentSessionIdAsync() instead
|
|
1274
|
-
let frameCounter = new Map(); // DEPRECATED: Session manager handles frame counting
|
|
1516
|
+
// Legacy compatibility variables (DEPRECATED - remove after migration is complete)
|
|
1517
|
+
let recordingTabId = null; // DEPRECATED: Use getCurrentTabIdAsync() instead
|
|
1518
|
+
let isCurrentlyRecording = false; // DEPRECATED: Use isCurrentlyRecordingAsync() instead
|
|
1519
|
+
let currentRecordingSessionId = null; // DEPRECATED: Use getCurrentSessionIdAsync() instead
|
|
1520
|
+
let frameCounter = new Map(); // DEPRECATED: Session manager handles frame counting
|
|
1521
|
+
|
|
1522
|
+
// =============================================================================
|
|
1523
|
+
// INACTIVITY AUTO-STOP - Stop recording if user is inactive
|
|
1524
|
+
// =============================================================================
|
|
1525
|
+
const DEFAULT_INACTIVITY_TIMEOUT_MS = 10 * 1000; // 10 seconds default
|
|
1526
|
+
let lastUserActivityTime = Date.now();
|
|
1527
|
+
let inactivityCheckInterval = null;
|
|
1528
|
+
let currentInactivityTimeout = DEFAULT_INACTIVITY_TIMEOUT_MS;
|
|
1529
|
+
let inactivityMonitoringStarted = false; // Track if monitoring has started
|
|
1530
|
+
let inactivityFallbackTimer = null; // Fallback timer if countdownComplete not received
|
|
1531
|
+
let inactivityDisabled = false; // User can disable for current session
|
|
1532
|
+
|
|
1533
|
+
/**
|
|
1534
|
+
* Start inactivity monitoring for a recording
|
|
1535
|
+
* @param {number} tabId - Tab ID to monitor
|
|
1536
|
+
*/
|
|
1537
|
+
async function startInactivityMonitoring(tabId) {
|
|
1538
|
+
// Mark as started (for fallback timer check)
|
|
1539
|
+
inactivityMonitoringStarted = true;
|
|
1540
|
+
|
|
1541
|
+
// Clear fallback timer since we're starting properly
|
|
1542
|
+
if (inactivityFallbackTimer) {
|
|
1543
|
+
clearTimeout(inactivityFallbackTimer);
|
|
1544
|
+
inactivityFallbackTimer = null;
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
// Load user's configured timeout from storage
|
|
1548
|
+
const settings = await chrome.storage.sync.get(['inactivityTimeout']);
|
|
1549
|
+
currentInactivityTimeout = (settings.inactivityTimeout || 10) * 1000; // Convert seconds to ms (default 10s)
|
|
1550
|
+
|
|
1551
|
+
lastUserActivityTime = Date.now();
|
|
1552
|
+
|
|
1553
|
+
// Tell content script to start tracking activity AND send config for countdown display
|
|
1554
|
+
try {
|
|
1555
|
+
await chrome.tabs.sendMessage(tabId, {
|
|
1556
|
+
action: 'startActivityTracking',
|
|
1557
|
+
inactivityTimeoutMs: currentInactivityTimeout,
|
|
1558
|
+
disabled: inactivityDisabled
|
|
1559
|
+
});
|
|
1560
|
+
} catch (error) {
|
|
1561
|
+
console.warn('[Inactivity] Could not start activity tracking in content script:', error.message);
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
// Clear any existing interval
|
|
1565
|
+
if (inactivityCheckInterval) {
|
|
1566
|
+
clearInterval(inactivityCheckInterval);
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
// If disabled, don't start the check interval
|
|
1570
|
+
if (inactivityDisabled) {
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
// Check for inactivity every 2 seconds
|
|
1575
|
+
inactivityCheckInterval = setInterval(async () => {
|
|
1576
|
+
// Skip if disabled
|
|
1577
|
+
if (inactivityDisabled) return;
|
|
1578
|
+
|
|
1579
|
+
const timeSinceActivity = Date.now() - lastUserActivityTime;
|
|
1580
|
+
const timeRemaining = Math.max(0, currentInactivityTimeout - timeSinceActivity);
|
|
1581
|
+
|
|
1582
|
+
// Send countdown update to content script
|
|
1583
|
+
try {
|
|
1584
|
+
await chrome.tabs.sendMessage(tabId, {
|
|
1585
|
+
action: 'updateInactivityCountdown',
|
|
1586
|
+
timeRemaining: Math.ceil(timeRemaining / 1000),
|
|
1587
|
+
disabled: inactivityDisabled
|
|
1588
|
+
});
|
|
1589
|
+
} catch (e) {
|
|
1590
|
+
// Tab might be closed, ignore
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
if (timeSinceActivity >= currentInactivityTimeout) {
|
|
1594
|
+
// Stop the recording first
|
|
1595
|
+
await stopRecording();
|
|
1596
|
+
|
|
1597
|
+
// Show notification with link to settings
|
|
1598
|
+
try {
|
|
1599
|
+
await chrome.notifications.create('inactivity-stop', {
|
|
1600
|
+
type: 'basic',
|
|
1601
|
+
iconUrl: 'icon128.png',
|
|
1602
|
+
title: 'Recording Auto-Stopped',
|
|
1603
|
+
message: `Recording stopped after ${Math.round(currentInactivityTimeout / 1000)}s of inactivity. Click to adjust timeout in Settings.`,
|
|
1604
|
+
priority: 2
|
|
1605
|
+
});
|
|
1606
|
+
} catch (e) {
|
|
1607
|
+
console.warn('[Inactivity] Could not show notification:', e.message);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
}, 2000); // Check every 2 seconds
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
/**
|
|
1614
|
+
* Stop inactivity monitoring
|
|
1615
|
+
* @param {number} tabId - Tab ID that was being monitored
|
|
1616
|
+
*/
|
|
1617
|
+
async function stopInactivityMonitoring(tabId) {
|
|
1618
|
+
if (inactivityCheckInterval) {
|
|
1619
|
+
clearInterval(inactivityCheckInterval);
|
|
1620
|
+
inactivityCheckInterval = null;
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
// Clear fallback timer
|
|
1624
|
+
if (inactivityFallbackTimer) {
|
|
1625
|
+
clearTimeout(inactivityFallbackTimer);
|
|
1626
|
+
inactivityFallbackTimer = null;
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
// Reset state for next recording
|
|
1630
|
+
inactivityMonitoringStarted = false;
|
|
1631
|
+
inactivityDisabled = false; // Reset disabled state for next session
|
|
1632
|
+
|
|
1633
|
+
// Tell content script to stop tracking
|
|
1634
|
+
if (tabId) {
|
|
1635
|
+
try {
|
|
1636
|
+
await chrome.tabs.sendMessage(tabId, { action: 'stopActivityTracking' });
|
|
1637
|
+
} catch (error) {
|
|
1638
|
+
// Tab might be closed, that's ok
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
/**
|
|
1644
|
+
* Handle user activity report from content script
|
|
1645
|
+
* @param {number} timestamp - Activity timestamp
|
|
1646
|
+
*/
|
|
1647
|
+
function handleUserActivity(timestamp) {
|
|
1648
|
+
lastUserActivityTime = timestamp || Date.now();
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
// Handle notification clicks - open settings page when inactivity notification is clicked
|
|
1652
|
+
chrome.notifications.onClicked.addListener((notificationId) => {
|
|
1653
|
+
if (notificationId === 'inactivity-stop') {
|
|
1654
|
+
chrome.runtime.openOptionsPage();
|
|
1655
|
+
chrome.notifications.clear(notificationId);
|
|
1656
|
+
}
|
|
1657
|
+
});
|
|
1275
1658
|
|
|
1276
1659
|
// Initialize session manager
|
|
1277
1660
|
(async function initializeSessionManager() {
|
|
1278
1661
|
try {
|
|
1279
1662
|
sessionManager = new ChromeExtensionSessionManager();
|
|
1280
|
-
console.log('[ChromeSessionManager] Initialized successfully');
|
|
1663
|
+
// console.log('[ChromeSessionManager] Initialized successfully');
|
|
1281
1664
|
} catch (error) {
|
|
1282
1665
|
console.error('[ChromeSessionManager] Failed to initialize:', error);
|
|
1283
1666
|
}
|
|
@@ -1399,7 +1782,7 @@ let workflowRecordingTabs = new Map(); // Map of tabId to recording state
|
|
|
1399
1782
|
let workflowIncludeLogs = new Map(); // Map of tabId to includeLogsInExport setting
|
|
1400
1783
|
let workflowScreenshotSettings = new Map(); // Map of tabId to screenshot settings
|
|
1401
1784
|
let workflowSessionNames = new Map(); // Map of tabId to session name
|
|
1402
|
-
let
|
|
1785
|
+
let workflowLogBuffers = new Map(); // Map of tabId to WorkflowLogBuffer instance
|
|
1403
1786
|
|
|
1404
1787
|
//=============================================================================
|
|
1405
1788
|
// COMPREHENSIVE ERROR HANDLING SYSTEM
|
|
@@ -1413,7 +1796,7 @@ let workflowUserIds = new Map(); // Map of tabId to userId for license tracking
|
|
|
1413
1796
|
*/
|
|
1414
1797
|
async function initializeErrorHandling() {
|
|
1415
1798
|
try {
|
|
1416
|
-
console.log('[ErrorHandling] Initializing comprehensive error handling system...');
|
|
1799
|
+
// console.log('[ErrorHandling] Initializing comprehensive error handling system...');
|
|
1417
1800
|
|
|
1418
1801
|
// Generate session ID if not exists
|
|
1419
1802
|
if (!errorHandlingState.sessionId) {
|
|
@@ -1436,11 +1819,11 @@ async function initializeErrorHandling() {
|
|
|
1436
1819
|
errorHandlingState.isInitialized = true;
|
|
1437
1820
|
errorHandlingState.currentState = 'NORMAL_OPERATION';
|
|
1438
1821
|
|
|
1439
|
-
console.log('[ErrorHandling] System initialization complete:', {
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
});
|
|
1822
|
+
// console.log('[ErrorHandling] System initialization complete:', {
|
|
1823
|
+
// sessionId: errorHandlingState.sessionId,
|
|
1824
|
+
// state: errorHandlingState.currentState,
|
|
1825
|
+
// circuitBreaker: errorHandlingState.circuitBreakerState
|
|
1826
|
+
// });
|
|
1444
1827
|
|
|
1445
1828
|
return true;
|
|
1446
1829
|
} catch (error) {
|
|
@@ -1465,7 +1848,7 @@ async function initializeBackupStorage() {
|
|
|
1465
1848
|
};
|
|
1466
1849
|
|
|
1467
1850
|
request.onsuccess = () => {
|
|
1468
|
-
console.log('[ErrorHandling] IndexedDB initialized successfully');
|
|
1851
|
+
// console.log('[ErrorHandling] IndexedDB initialized successfully');
|
|
1469
1852
|
resolve(request.result);
|
|
1470
1853
|
};
|
|
1471
1854
|
|
|
@@ -1495,7 +1878,7 @@ async function initializeBackupStorage() {
|
|
|
1495
1878
|
errorStore.createIndex('timestamp', 'timestamp', { unique: false });
|
|
1496
1879
|
}
|
|
1497
1880
|
|
|
1498
|
-
console.log('[ErrorHandling] IndexedDB schema created');
|
|
1881
|
+
// console.log('[ErrorHandling] IndexedDB schema created');
|
|
1499
1882
|
};
|
|
1500
1883
|
});
|
|
1501
1884
|
}
|
|
@@ -1506,7 +1889,7 @@ async function initializeBackupStorage() {
|
|
|
1506
1889
|
function initializeCircuitBreaker() {
|
|
1507
1890
|
errorHandlingState.circuitBreakerState = 'CLOSED';
|
|
1508
1891
|
errorHandlingState.failedSaveCount = 0;
|
|
1509
|
-
console.log('[ErrorHandling] Circuit breaker initialized in CLOSED state');
|
|
1892
|
+
// console.log('[ErrorHandling] Circuit breaker initialized in CLOSED state');
|
|
1510
1893
|
}
|
|
1511
1894
|
|
|
1512
1895
|
/**
|
|
@@ -1519,7 +1902,7 @@ async function setupNotificationSystem() {
|
|
|
1519
1902
|
await chrome.notifications.clear(notificationId);
|
|
1520
1903
|
}
|
|
1521
1904
|
|
|
1522
|
-
console.log('[ErrorHandling] Notification system ready');
|
|
1905
|
+
// console.log('[ErrorHandling] Notification system ready');
|
|
1523
1906
|
}
|
|
1524
1907
|
|
|
1525
1908
|
/**
|
|
@@ -1558,7 +1941,7 @@ async function cleanupOldBackups() {
|
|
|
1558
1941
|
}
|
|
1559
1942
|
};
|
|
1560
1943
|
|
|
1561
|
-
console.log('[ErrorHandling] Cleanup completed for data older than', ERROR_HANDLING_CONFIG.backupRetentionDays, 'days');
|
|
1944
|
+
// console.log('[ErrorHandling] Cleanup completed for data older than', ERROR_HANDLING_CONFIG.backupRetentionDays, 'days');
|
|
1562
1945
|
};
|
|
1563
1946
|
} catch (error) {
|
|
1564
1947
|
console.error('[ErrorHandling] Failed to cleanup old backups:', error);
|
|
@@ -1585,13 +1968,13 @@ async function handleSaveAttempt(saveFunction, data, context = {}) {
|
|
|
1585
1968
|
if (errorHandlingState.circuitBreakerState === 'OPEN') {
|
|
1586
1969
|
const timeSinceLastFailure = Date.now() - (errorHandlingState.lastError?.timestamp || 0);
|
|
1587
1970
|
if (timeSinceLastFailure < ERROR_HANDLING_CONFIG.resetTimeout) {
|
|
1588
|
-
console.log('[ErrorHandling] Circuit breaker OPEN, attempting backup instead');
|
|
1971
|
+
// console.log('[ErrorHandling] Circuit breaker OPEN, attempting backup instead');
|
|
1589
1972
|
await backupFailedData(data, context);
|
|
1590
1973
|
return { success: false, circuitBreakerOpen: true };
|
|
1591
1974
|
} else {
|
|
1592
1975
|
// Try half-open
|
|
1593
1976
|
errorHandlingState.circuitBreakerState = 'HALF_OPEN';
|
|
1594
|
-
console.log('[ErrorHandling] Circuit breaker moving to HALF_OPEN state');
|
|
1977
|
+
// console.log('[ErrorHandling] Circuit breaker moving to HALF_OPEN state');
|
|
1595
1978
|
}
|
|
1596
1979
|
}
|
|
1597
1980
|
|
|
@@ -1602,7 +1985,7 @@ async function handleSaveAttempt(saveFunction, data, context = {}) {
|
|
|
1602
1985
|
if (result && result.success !== false) {
|
|
1603
1986
|
errorHandlingState.circuitBreakerState = 'CLOSED';
|
|
1604
1987
|
errorHandlingState.failedSaveCount = 0;
|
|
1605
|
-
console.log('[ErrorHandling] Save successful, circuit breaker CLOSED');
|
|
1988
|
+
// console.log('[ErrorHandling] Save successful, circuit breaker CLOSED');
|
|
1606
1989
|
return { success: true, result };
|
|
1607
1990
|
} else {
|
|
1608
1991
|
throw new Error('Save function returned failure');
|
|
@@ -1642,7 +2025,7 @@ async function handleSaveFailure(error, data, context, attempt) {
|
|
|
1642
2025
|
// Attempt backup
|
|
1643
2026
|
try {
|
|
1644
2027
|
await backupFailedData(data, context);
|
|
1645
|
-
console.log('[ErrorHandling] Data backed up successfully');
|
|
2028
|
+
// console.log('[ErrorHandling] Data backed up successfully');
|
|
1646
2029
|
} catch (backupError) {
|
|
1647
2030
|
console.error('[ErrorHandling] CRITICAL: Backup also failed:', backupError);
|
|
1648
2031
|
await createUserNotification('CRITICAL', 'Data loss imminent - manual intervention required');
|
|
@@ -1738,7 +2121,7 @@ async function backupFailedData(data, context) {
|
|
|
1738
2121
|
const store = transaction.objectStore('sessionBackups');
|
|
1739
2122
|
|
|
1740
2123
|
store.add(backupData);
|
|
1741
|
-
console.log('[ErrorHandling] Data backed up with session ID:', errorHandlingState.sessionId);
|
|
2124
|
+
// console.log('[ErrorHandling] Data backed up with session ID:', errorHandlingState.sessionId);
|
|
1742
2125
|
};
|
|
1743
2126
|
|
|
1744
2127
|
// Also add to retained interactions if relevant
|
|
@@ -1779,7 +2162,7 @@ async function createUserNotification(priority, message) {
|
|
|
1779
2162
|
}
|
|
1780
2163
|
|
|
1781
2164
|
await chrome.notifications.create(notificationId, notificationOptions);
|
|
1782
|
-
console.log('[ErrorHandling] User notification created:', notificationId);
|
|
2165
|
+
// console.log('[ErrorHandling] User notification created:', notificationId);
|
|
1783
2166
|
|
|
1784
2167
|
// Set up notification click handlers
|
|
1785
2168
|
chrome.notifications.onButtonClicked.addListener((notifId, buttonIndex) => {
|
|
@@ -1799,16 +2182,16 @@ async function createUserNotification(priority, message) {
|
|
|
1799
2182
|
async function handleNotificationAction(buttonIndex, priority) {
|
|
1800
2183
|
switch (buttonIndex) {
|
|
1801
2184
|
case 0: // Retry Save
|
|
1802
|
-
console.log('[ErrorHandling] User requested retry');
|
|
2185
|
+
// console.log('[ErrorHandling] User requested retry');
|
|
1803
2186
|
await attemptRecovery();
|
|
1804
2187
|
break;
|
|
1805
2188
|
case 1: // View Details
|
|
1806
|
-
console.log('[ErrorHandling] User requested error details');
|
|
2189
|
+
// console.log('[ErrorHandling] User requested error details');
|
|
1807
2190
|
// Could open an options page or log details
|
|
1808
2191
|
break;
|
|
1809
2192
|
case 2: // Download Backup (for critical errors)
|
|
1810
2193
|
if (priority === 'CRITICAL') {
|
|
1811
|
-
console.log('[ErrorHandling] User requested backup download');
|
|
2194
|
+
// console.log('[ErrorHandling] User requested backup download');
|
|
1812
2195
|
await downloadBackupData();
|
|
1813
2196
|
}
|
|
1814
2197
|
break;
|
|
@@ -1828,14 +2211,14 @@ async function attemptRecovery() {
|
|
|
1828
2211
|
|
|
1829
2212
|
store.getAll().onsuccess = (event) => {
|
|
1830
2213
|
const backups = event.target.result;
|
|
1831
|
-
console.log('[ErrorHandling] Found', backups.length, 'backup records for recovery');
|
|
2214
|
+
// console.log('[ErrorHandling] Found', backups.length, 'backup records for recovery');
|
|
1832
2215
|
|
|
1833
2216
|
// Process each backup
|
|
1834
2217
|
backups.forEach(async (backup) => {
|
|
1835
2218
|
try {
|
|
1836
2219
|
// Attempt to save the backed up data
|
|
1837
2220
|
// This would call the original save function with the backed up data
|
|
1838
|
-
console.log('[ErrorHandling] Attempting recovery for session:', backup.sessionId);
|
|
2221
|
+
// console.log('[ErrorHandling] Attempting recovery for session:', backup.sessionId);
|
|
1839
2222
|
} catch (error) {
|
|
1840
2223
|
console.error('[ErrorHandling] Recovery failed for session:', backup.sessionId, error);
|
|
1841
2224
|
}
|
|
@@ -1875,10 +2258,10 @@ async function downloadBackupData() {
|
|
|
1875
2258
|
|
|
1876
2259
|
chrome.downloads.download({
|
|
1877
2260
|
url: url,
|
|
1878
|
-
filename: `chrome-
|
|
2261
|
+
filename: `chrome-debug-backup-${Date.now()}.json`
|
|
1879
2262
|
});
|
|
1880
2263
|
|
|
1881
|
-
console.log('[ErrorHandling] Backup data download initiated');
|
|
2264
|
+
// console.log('[ErrorHandling] Backup data download initiated');
|
|
1882
2265
|
};
|
|
1883
2266
|
};
|
|
1884
2267
|
};
|
|
@@ -1894,7 +2277,7 @@ async function createOffscreenDocument() {
|
|
|
1894
2277
|
const hasDocument = await chrome.offscreen.hasDocument();
|
|
1895
2278
|
if (hasDocument) {
|
|
1896
2279
|
hasOffscreenDocument = true;
|
|
1897
|
-
console.log('Offscreen document already exists');
|
|
2280
|
+
// console.log('Offscreen document already exists');
|
|
1898
2281
|
return;
|
|
1899
2282
|
}
|
|
1900
2283
|
|
|
@@ -1905,12 +2288,12 @@ async function createOffscreenDocument() {
|
|
|
1905
2288
|
justification: 'Recording tab frames for Chrome Debug'
|
|
1906
2289
|
});
|
|
1907
2290
|
hasOffscreenDocument = true;
|
|
1908
|
-
console.log('Created new offscreen document');
|
|
2291
|
+
// console.log('Created new offscreen document');
|
|
1909
2292
|
} catch (error) {
|
|
1910
2293
|
if (error.message.includes('Only a single offscreen document may be created')) {
|
|
1911
2294
|
// Document already exists, just mark it as available
|
|
1912
2295
|
hasOffscreenDocument = true;
|
|
1913
|
-
console.log('Offscreen document already exists (caught error)');
|
|
2296
|
+
// console.log('Offscreen document already exists (caught error)');
|
|
1914
2297
|
} else {
|
|
1915
2298
|
throw error;
|
|
1916
2299
|
}
|
|
@@ -1928,7 +2311,7 @@ async function closeOffscreenDocument() {
|
|
|
1928
2311
|
}
|
|
1929
2312
|
|
|
1930
2313
|
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
1931
|
-
console.log('Received message:', request.action || request.type || 'unknown');
|
|
2314
|
+
// console.log('Received message:', request.action || request.type || 'unknown');
|
|
1932
2315
|
|
|
1933
2316
|
if (request.action === 'sendToServer') {
|
|
1934
2317
|
// Primary port is 3028 - try it first, then fallback to others if needed
|
|
@@ -1936,11 +2319,11 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
1936
2319
|
|
|
1937
2320
|
async function tryPorts() {
|
|
1938
2321
|
let lastError = null;
|
|
1939
|
-
console.log('Trying to connect to server...');
|
|
2322
|
+
// console.log('Trying to connect to server...');
|
|
1940
2323
|
|
|
1941
2324
|
for (const port of ports) {
|
|
1942
2325
|
try {
|
|
1943
|
-
console.log(`Trying port ${port}...`);
|
|
2326
|
+
// console.log(`Trying port ${port}...`);
|
|
1944
2327
|
const controller = new AbortController();
|
|
1945
2328
|
const timeoutId = setTimeout(() => controller.abort(), DISCOVERY_TIMEOUT);
|
|
1946
2329
|
|
|
@@ -1957,7 +2340,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
1957
2340
|
|
|
1958
2341
|
if (response.ok) {
|
|
1959
2342
|
const result = await response.json();
|
|
1960
|
-
console.log(`Success on port ${port}:`, result);
|
|
2343
|
+
// console.log(`Success on port ${port}:`, result);
|
|
1961
2344
|
sendResponse({ success: true, result, port });
|
|
1962
2345
|
return;
|
|
1963
2346
|
} else {
|
|
@@ -1965,7 +2348,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
1965
2348
|
try {
|
|
1966
2349
|
const errorData = await response.json();
|
|
1967
2350
|
if (errorData.message || errorData.error) {
|
|
1968
|
-
console.log(`Port ${port} error:`, errorData);
|
|
2351
|
+
// console.log(`Port ${port} error:`, errorData);
|
|
1969
2352
|
sendResponse({
|
|
1970
2353
|
success: false,
|
|
1971
2354
|
result: errorData,
|
|
@@ -1977,11 +2360,11 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
1977
2360
|
// Response wasn't JSON
|
|
1978
2361
|
}
|
|
1979
2362
|
lastError = `Port ${port}: HTTP ${response.status}`;
|
|
1980
|
-
console.log(lastError);
|
|
2363
|
+
// console.log(lastError);
|
|
1981
2364
|
}
|
|
1982
2365
|
} catch (e) {
|
|
1983
2366
|
lastError = `Port ${port}: ${e.message}`;
|
|
1984
|
-
console.log(lastError);
|
|
2367
|
+
// console.log(lastError);
|
|
1985
2368
|
}
|
|
1986
2369
|
}
|
|
1987
2370
|
|
|
@@ -2015,8 +2398,13 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
2015
2398
|
}
|
|
2016
2399
|
|
|
2017
2400
|
if (request.action === 'stopRecording') {
|
|
2018
|
-
stopRecording().then(() => {
|
|
2019
|
-
|
|
2401
|
+
stopRecording().then(async (result) => {
|
|
2402
|
+
// Track usage for FREE tier after successful screen recording
|
|
2403
|
+
if (result.success) {
|
|
2404
|
+
const usageResult = await incrementUsageAfterRecording();
|
|
2405
|
+
result.usage = usageResult; // Include updated usage in response
|
|
2406
|
+
}
|
|
2407
|
+
sendResponse(result); // Pass through { success: true, sessionId: ..., usage: ... }
|
|
2020
2408
|
}).catch((error) => {
|
|
2021
2409
|
console.error('Error stopping recording:', error);
|
|
2022
2410
|
sendResponse({ success: false, error: error.message });
|
|
@@ -2024,6 +2412,23 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
2024
2412
|
return true;
|
|
2025
2413
|
}
|
|
2026
2414
|
|
|
2415
|
+
if (request.action === 'openFrameEditor') {
|
|
2416
|
+
// Content scripts can't create tabs, so handle it here
|
|
2417
|
+
const sessionId = request.sessionId;
|
|
2418
|
+
const type = request.type; // 'workflow' or undefined for screen recordings
|
|
2419
|
+
const editorUrl = type === 'workflow'
|
|
2420
|
+
? chrome.runtime.getURL(`pro/frame-editor.html?sessionId=${sessionId}&type=workflow`)
|
|
2421
|
+
: chrome.runtime.getURL(`pro/frame-editor.html?sessionId=${sessionId}`);
|
|
2422
|
+
|
|
2423
|
+
chrome.tabs.create({ url: editorUrl }).then(() => {
|
|
2424
|
+
sendResponse({ success: true });
|
|
2425
|
+
}).catch((error) => {
|
|
2426
|
+
console.error('Error opening frame editor:', error);
|
|
2427
|
+
sendResponse({ success: false, error: error.message });
|
|
2428
|
+
});
|
|
2429
|
+
return true;
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2027
2432
|
// Get quota information for browser-only mode
|
|
2028
2433
|
if (request.action === 'getQuotaInfo') {
|
|
2029
2434
|
getQuotaInfo().then((quotaInfo) => {
|
|
@@ -2037,7 +2442,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
2037
2442
|
|
|
2038
2443
|
// Session manager lease renewal handler
|
|
2039
2444
|
if (request.action === 'renewLease') {
|
|
2040
|
-
console.log('[Background] RENEW LEASE - Session:', request.sessionId, 'Manager exists:', !!sessionManager);
|
|
2445
|
+
// console.log('[Background] RENEW LEASE - Session:', request.sessionId, 'Manager exists:', !!sessionManager);
|
|
2041
2446
|
|
|
2042
2447
|
if (!sessionManager) {
|
|
2043
2448
|
console.error('[Background] Session manager not available');
|
|
@@ -2053,7 +2458,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
2053
2458
|
};
|
|
2054
2459
|
|
|
2055
2460
|
sessionManager.renewLease(leaseRequest).then((result) => {
|
|
2056
|
-
console.log('[Background] Lease renewed - Success:', result.success);
|
|
2461
|
+
// console.log('[Background] Lease renewed - Success:', result.success);
|
|
2057
2462
|
sendResponse(result);
|
|
2058
2463
|
}).catch((error) => {
|
|
2059
2464
|
console.error('[Background] Error renewing lease:', error);
|
|
@@ -2087,6 +2492,45 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
2087
2492
|
return true;
|
|
2088
2493
|
}
|
|
2089
2494
|
|
|
2495
|
+
// User activity handler for inactivity tracking
|
|
2496
|
+
if (request.action === 'userActivity') {
|
|
2497
|
+
handleUserActivity(request.timestamp);
|
|
2498
|
+
sendResponse({ success: true });
|
|
2499
|
+
return false; // Synchronous response
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
// Countdown complete handler - content script finished 3-2-1 countdown
|
|
2503
|
+
if (request.action === 'countdownComplete') {
|
|
2504
|
+
// Start inactivity monitoring now that countdown is done
|
|
2505
|
+
// Use isCurrentlyRecording (the actual state variable)
|
|
2506
|
+
if (isCurrentlyRecording && recordingTabId && !inactivityMonitoringStarted) {
|
|
2507
|
+
startInactivityMonitoring(recordingTabId);
|
|
2508
|
+
}
|
|
2509
|
+
sendResponse({ success: true });
|
|
2510
|
+
return false;
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2513
|
+
// Toggle inactivity timeout (user clicked disable/enable button)
|
|
2514
|
+
if (request.action === 'toggleInactivityTimeout') {
|
|
2515
|
+
inactivityDisabled = !inactivityDisabled;
|
|
2516
|
+
|
|
2517
|
+
if (inactivityDisabled) {
|
|
2518
|
+
// Stop the check interval but keep tracking activity
|
|
2519
|
+
if (inactivityCheckInterval) {
|
|
2520
|
+
clearInterval(inactivityCheckInterval);
|
|
2521
|
+
inactivityCheckInterval = null;
|
|
2522
|
+
}
|
|
2523
|
+
} else {
|
|
2524
|
+
// Restart monitoring
|
|
2525
|
+
if (recordingTabId && !inactivityCheckInterval) {
|
|
2526
|
+
startInactivityMonitoring(recordingTabId);
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
sendResponse({ success: true, disabled: inactivityDisabled });
|
|
2531
|
+
return false;
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2090
2534
|
// Force stop session handler (emergency)
|
|
2091
2535
|
if (request.action === 'forceStopSession') {
|
|
2092
2536
|
if (!sessionManager) {
|
|
@@ -2140,7 +2584,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
2140
2584
|
*/
|
|
2141
2585
|
/*
|
|
2142
2586
|
if (request.action === 'takeStandaloneSnapshot') {
|
|
2143
|
-
console.log('Taking standalone snapshot for tab:', request.tabId);
|
|
2587
|
+
// console.log('Taking standalone snapshot for tab:', request.tabId);
|
|
2144
2588
|
takeStandaloneSnapshot(request.tabId, request.note).then((result) => {
|
|
2145
2589
|
sendResponse({ success: true, sessionId: result.sessionId });
|
|
2146
2590
|
}).catch((error) => {
|
|
@@ -2188,7 +2632,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
2188
2632
|
}
|
|
2189
2633
|
|
|
2190
2634
|
if (request.action === 'startRecordingFromContent') {
|
|
2191
|
-
console.log('Starting recording from content script');
|
|
2635
|
+
// console.log('Starting recording from content script');
|
|
2192
2636
|
const tabId = sender.tab.id;
|
|
2193
2637
|
|
|
2194
2638
|
// Store recording state and show notification
|
|
@@ -2212,28 +2656,14 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
2212
2656
|
}
|
|
2213
2657
|
|
|
2214
2658
|
if (request.action === 'deleteRecording') {
|
|
2215
|
-
console.log('Deleting recording:', request.recordingId);
|
|
2659
|
+
// console.log('Deleting recording:', request.recordingId);
|
|
2216
2660
|
deleteRecordingFromServer(request.recordingId, sendResponse);
|
|
2217
2661
|
return true;
|
|
2218
2662
|
}
|
|
2219
2663
|
|
|
2220
|
-
// License check handler for workflow recording
|
|
2221
|
-
if (request.action === 'checkLicenseForWorkflow') {
|
|
2222
|
-
console.log('[License] popup.js requesting license check for workflow recording');
|
|
2223
|
-
LicenseHelper.checkLicenseBeforeRecording().then((licenseCheck) => {
|
|
2224
|
-
console.log('[License] License check result for popup:', licenseCheck);
|
|
2225
|
-
sendResponse(licenseCheck);
|
|
2226
|
-
}).catch((error) => {
|
|
2227
|
-
console.error('[License] Error checking license:', error);
|
|
2228
|
-
// Fail-open: allow recording on error
|
|
2229
|
-
sendResponse({ allowed: true, error: error.message });
|
|
2230
|
-
});
|
|
2231
|
-
return true;
|
|
2232
|
-
}
|
|
2233
|
-
|
|
2234
2664
|
// Workflow recording handlers
|
|
2235
2665
|
if (request.action === 'startWorkflowRecording') {
|
|
2236
|
-
console.log('Starting workflow recording for tab:', request.tabId);
|
|
2666
|
+
// console.log('Starting workflow recording for tab:', request.tabId);
|
|
2237
2667
|
startWorkflowRecording(
|
|
2238
2668
|
request.tabId,
|
|
2239
2669
|
request.includeLogsInExport,
|
|
@@ -2249,9 +2679,17 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
2249
2679
|
}
|
|
2250
2680
|
|
|
2251
2681
|
if (request.action === 'stopWorkflowRecording') {
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2682
|
+
const tabId = request.tabId || sender.tab?.id;
|
|
2683
|
+
// console.log('Stopping workflow recording for tab:', tabId);
|
|
2684
|
+
// stopWorkflowRecording already returns { success: true, workflow: {...}, savedToServer: bool }
|
|
2685
|
+
// so we pass it through directly - don't double-wrap!
|
|
2686
|
+
stopWorkflowRecording(tabId).then(async (result) => {
|
|
2687
|
+
// Track usage for FREE tier after successful recording
|
|
2688
|
+
if (result.success) {
|
|
2689
|
+
const usageResult = await incrementUsageAfterRecording();
|
|
2690
|
+
result.usage = usageResult; // Include updated usage in response
|
|
2691
|
+
}
|
|
2692
|
+
sendResponse(result);
|
|
2255
2693
|
}).catch((error) => {
|
|
2256
2694
|
console.error('Error stopping workflow recording:', error);
|
|
2257
2695
|
sendResponse({ success: false, error: error.message });
|
|
@@ -2338,7 +2776,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
2338
2776
|
frameIndex: frameIndex
|
|
2339
2777
|
};
|
|
2340
2778
|
screenInteractions.push(interaction);
|
|
2341
|
-
console.log('Screen interaction recorded:', interaction.type, 'Total:', screenInteractions.length);
|
|
2779
|
+
// console.log('Screen interaction recorded:', interaction.type, 'Total:', screenInteractions.length);
|
|
2342
2780
|
}
|
|
2343
2781
|
}).catch(error => {
|
|
2344
2782
|
console.error('Error validating recording state for screen interaction:', error);
|
|
@@ -2351,23 +2789,24 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
2351
2789
|
frameIndex: frameCounter.get(currentRecordingSessionId) || 0
|
|
2352
2790
|
};
|
|
2353
2791
|
screenInteractions.push(interaction);
|
|
2354
|
-
console.log('Screen interaction recorded:', interaction.type, 'Total:', screenInteractions.length);
|
|
2792
|
+
// console.log('Screen interaction recorded:', interaction.type, 'Total:', screenInteractions.length);
|
|
2355
2793
|
}
|
|
2356
2794
|
}
|
|
2357
2795
|
}
|
|
2358
2796
|
|
|
2359
|
-
// Buffer workflow console logs
|
|
2797
|
+
// Buffer workflow console logs using race-safe WorkflowLogBuffer
|
|
2360
2798
|
if (request.action === 'workflowConsoleLog' && sender.tab) {
|
|
2361
2799
|
const tabId = sender.tab.id;
|
|
2362
2800
|
if (workflowRecordingTabs.has(tabId) && workflowIncludeLogs.get(tabId)) {
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2801
|
+
const buffer = workflowLogBuffers.get(tabId);
|
|
2802
|
+
if (buffer) {
|
|
2803
|
+
// Use race-safe buffer to prevent read-modify-write race conditions
|
|
2804
|
+
buffer.addLog(request.log).catch(err => {
|
|
2805
|
+
console.error('[WorkflowLogBuffer] Error adding log:', err);
|
|
2806
|
+
});
|
|
2807
|
+
} else {
|
|
2808
|
+
console.warn(`[Workflow] No buffer found for tab ${tabId}, log dropped`);
|
|
2809
|
+
}
|
|
2371
2810
|
}
|
|
2372
2811
|
}
|
|
2373
2812
|
|
|
@@ -2466,18 +2905,18 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
2466
2905
|
|
|
2467
2906
|
// Handle workflow screenshot capture
|
|
2468
2907
|
if (request.action === 'captureWorkflowScreenshot' && sender.tab) {
|
|
2469
|
-
console.log('[SCREENSHOT-DEBUG] background.js - Received captureWorkflowScreenshot message');
|
|
2908
|
+
// console.log('[SCREENSHOT-DEBUG] background.js - Received captureWorkflowScreenshot message');
|
|
2470
2909
|
const tabId = sender.tab.id;
|
|
2471
2910
|
const settings = request.settings || {};
|
|
2472
|
-
console.log('[SCREENSHOT-DEBUG] background.js - tabId:', tabId);
|
|
2473
|
-
console.log('[SCREENSHOT-DEBUG] background.js - settings:', JSON.stringify(settings));
|
|
2911
|
+
// console.log('[SCREENSHOT-DEBUG] background.js - tabId:', tabId);
|
|
2912
|
+
// console.log('[SCREENSHOT-DEBUG] background.js - settings:', JSON.stringify(settings));
|
|
2474
2913
|
|
|
2475
2914
|
captureTabScreenshot(tabId, settings).then((screenshotData) => {
|
|
2476
|
-
console.log('[SCREENSHOT-DEBUG] background.js - captureTabScreenshot succeeded');
|
|
2477
|
-
console.log('[SCREENSHOT-DEBUG] background.js - screenshotData type:', typeof screenshotData);
|
|
2478
|
-
console.log('[SCREENSHOT-DEBUG] background.js - screenshotData length:', screenshotData?.length);
|
|
2479
|
-
console.log('[SCREENSHOT-DEBUG] background.js - screenshotData preview:', screenshotData?.substring(0, 100));
|
|
2480
|
-
console.log('[SCREENSHOT-DEBUG] background.js - Sending response with success: true');
|
|
2915
|
+
// console.log('[SCREENSHOT-DEBUG] background.js - captureTabScreenshot succeeded');
|
|
2916
|
+
// console.log('[SCREENSHOT-DEBUG] background.js - screenshotData type:', typeof screenshotData);
|
|
2917
|
+
// console.log('[SCREENSHOT-DEBUG] background.js - screenshotData length:', screenshotData?.length);
|
|
2918
|
+
// console.log('[SCREENSHOT-DEBUG] background.js - screenshotData preview:', screenshotData?.substring(0, 100));
|
|
2919
|
+
// console.log('[SCREENSHOT-DEBUG] background.js - Sending response with success: true');
|
|
2481
2920
|
sendResponse({ success: true, screenshotData });
|
|
2482
2921
|
}).catch((error) => {
|
|
2483
2922
|
console.error('[SCREENSHOT-DEBUG] background.js - captureTabScreenshot FAILED');
|
|
@@ -2511,7 +2950,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
|
2511
2950
|
/*
|
|
2512
2951
|
async function takeStandaloneSnapshot(tabId, note = '') {
|
|
2513
2952
|
try {
|
|
2514
|
-
console.log('Taking standalone snapshot for tab:', tabId);
|
|
2953
|
+
// console.log('Taking standalone snapshot for tab:', tabId);
|
|
2515
2954
|
|
|
2516
2955
|
// Create a unique session ID for the snapshot
|
|
2517
2956
|
const sessionId = `snapshot_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
@@ -2587,7 +3026,7 @@ async function takeStandaloneSnapshot(tabId, note = '') {
|
|
|
2587
3026
|
|
|
2588
3027
|
if (response.ok) {
|
|
2589
3028
|
const result = await response.json();
|
|
2590
|
-
console.log('Snapshot uploaded successfully:', sessionId);
|
|
3029
|
+
// console.log('Snapshot uploaded successfully:', sessionId);
|
|
2591
3030
|
uploadSuccess = true;
|
|
2592
3031
|
break;
|
|
2593
3032
|
}
|
|
@@ -2611,34 +3050,34 @@ async function takeStandaloneSnapshot(tabId, note = '') {
|
|
|
2611
3050
|
|
|
2612
3051
|
// Capture screenshot for workflow recording
|
|
2613
3052
|
async function captureTabScreenshot(tabId, settings) {
|
|
2614
|
-
console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - ENTRY');
|
|
2615
|
-
console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - tabId:', tabId);
|
|
2616
|
-
console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - settings:', JSON.stringify(settings));
|
|
3053
|
+
// console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - ENTRY');
|
|
3054
|
+
// console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - tabId:', tabId);
|
|
3055
|
+
// console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - settings:', JSON.stringify(settings));
|
|
2617
3056
|
|
|
2618
3057
|
try {
|
|
2619
3058
|
// Get the tab to find its window
|
|
2620
|
-
console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - Getting tab info...');
|
|
3059
|
+
// console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - Getting tab info...');
|
|
2621
3060
|
const tab = await chrome.tabs.get(tabId);
|
|
2622
3061
|
const windowId = tab.windowId;
|
|
2623
|
-
console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - windowId:', windowId);
|
|
2624
|
-
console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - tab.url:', tab.url);
|
|
3062
|
+
// console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - windowId:', windowId);
|
|
3063
|
+
// console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - tab.url:', tab.url);
|
|
2625
3064
|
|
|
2626
3065
|
const captureOptions = {
|
|
2627
3066
|
format: settings.format || 'jpeg',
|
|
2628
3067
|
quality: settings.quality || 30
|
|
2629
3068
|
};
|
|
2630
|
-
console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - captureOptions:', JSON.stringify(captureOptions));
|
|
2631
|
-
console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - Calling chrome.tabs.captureVisibleTab...');
|
|
3069
|
+
// console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - captureOptions:', JSON.stringify(captureOptions));
|
|
3070
|
+
// console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - Calling chrome.tabs.captureVisibleTab...');
|
|
2632
3071
|
|
|
2633
3072
|
const dataUrl = await chrome.tabs.captureVisibleTab(windowId, captureOptions);
|
|
2634
3073
|
|
|
2635
|
-
console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - captureVisibleTab SUCCESS');
|
|
2636
|
-
console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - dataUrl length:', dataUrl?.length);
|
|
2637
|
-
console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - dataUrl prefix:', dataUrl?.substring(0, 100));
|
|
3074
|
+
// console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - captureVisibleTab SUCCESS');
|
|
3075
|
+
// console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - dataUrl length:', dataUrl?.length);
|
|
3076
|
+
// console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - dataUrl prefix:', dataUrl?.substring(0, 100));
|
|
2638
3077
|
|
|
2639
3078
|
// If resolution is specified, resize the image
|
|
2640
3079
|
if (settings.maxWidth || settings.maxHeight) {
|
|
2641
|
-
console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - Resizing required, maxWidth:', settings.maxWidth, 'maxHeight:', settings.maxHeight);
|
|
3080
|
+
// console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - Resizing required, maxWidth:', settings.maxWidth, 'maxHeight:', settings.maxHeight);
|
|
2642
3081
|
// Create an image element to get dimensions
|
|
2643
3082
|
const img = new Image();
|
|
2644
3083
|
const canvas = new OffscreenCanvas(1, 1);
|
|
@@ -2678,8 +3117,8 @@ async function captureTabScreenshot(tabId, settings) {
|
|
|
2678
3117
|
});
|
|
2679
3118
|
}
|
|
2680
3119
|
|
|
2681
|
-
console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - No resize needed, returning dataUrl');
|
|
2682
|
-
console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - Final dataUrl length:', dataUrl?.length);
|
|
3120
|
+
// console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - No resize needed, returning dataUrl');
|
|
3121
|
+
// console.log('[SCREENSHOT-DEBUG] captureTabScreenshot - Final dataUrl length:', dataUrl?.length);
|
|
2683
3122
|
return dataUrl;
|
|
2684
3123
|
} catch (error) {
|
|
2685
3124
|
console.error('[SCREENSHOT-DEBUG] captureTabScreenshot - EXCEPTION CAUGHT');
|
|
@@ -2693,7 +3132,7 @@ async function captureTabScreenshot(tabId, settings) {
|
|
|
2693
3132
|
// Start recording
|
|
2694
3133
|
// Detect if server is available (server mode vs browser-only mode)
|
|
2695
3134
|
async function detectServerMode() {
|
|
2696
|
-
console.log('[ServerDetection] Checking server availability...');
|
|
3135
|
+
// console.log('[ServerDetection] Checking server availability...');
|
|
2697
3136
|
|
|
2698
3137
|
for (const port of CONFIG_PORTS) {
|
|
2699
3138
|
try {
|
|
@@ -2707,7 +3146,7 @@ async function detectServerMode() {
|
|
|
2707
3146
|
]);
|
|
2708
3147
|
|
|
2709
3148
|
if (response.ok) {
|
|
2710
|
-
console.log(`[ServerDetection] Server detected on port ${port}`);
|
|
3149
|
+
// console.log(`[ServerDetection] Server detected on port ${port}`);
|
|
2711
3150
|
serverMode = 'server';
|
|
2712
3151
|
return 'server';
|
|
2713
3152
|
}
|
|
@@ -2716,7 +3155,7 @@ async function detectServerMode() {
|
|
|
2716
3155
|
}
|
|
2717
3156
|
}
|
|
2718
3157
|
|
|
2719
|
-
console.log('[ServerDetection] No server detected - using browser-only mode');
|
|
3158
|
+
// console.log('[ServerDetection] No server detected - using browser-only mode');
|
|
2720
3159
|
serverMode = 'browser-only';
|
|
2721
3160
|
return 'browser-only';
|
|
2722
3161
|
}
|
|
@@ -2742,30 +3181,9 @@ async function getQuotaInfo() {
|
|
|
2742
3181
|
|
|
2743
3182
|
async function startRecording(tabId, settings = {}) {
|
|
2744
3183
|
try {
|
|
2745
|
-
//
|
|
3184
|
+
// Detect server mode before starting
|
|
2746
3185
|
await detectServerMode();
|
|
2747
|
-
console.log(`[Recording] Mode: ${serverMode}`);
|
|
2748
|
-
|
|
2749
|
-
// STEP 2: Check license and usage limits BEFORE recording
|
|
2750
|
-
console.log('[License] Checking license before recording...');
|
|
2751
|
-
const licenseCheck = await LicenseHelper.checkLicenseBeforeRecording();
|
|
2752
|
-
console.log('[License] License check result:', licenseCheck);
|
|
2753
|
-
|
|
2754
|
-
if (!licenseCheck.allowed) {
|
|
2755
|
-
// Show notification to user
|
|
2756
|
-
chrome.notifications.create({
|
|
2757
|
-
type: 'basic',
|
|
2758
|
-
iconUrl: chrome.runtime.getURL('icon48.png'),
|
|
2759
|
-
title: 'Recording Limit Reached',
|
|
2760
|
-
message: licenseCheck.message || 'Upgrade to Pro for unlimited recordings.',
|
|
2761
|
-
priority: 2
|
|
2762
|
-
});
|
|
2763
|
-
|
|
2764
|
-
throw new Error(licenseCheck.message || 'Recording not allowed');
|
|
2765
|
-
}
|
|
2766
|
-
|
|
2767
|
-
// Store userId for later usage tracking
|
|
2768
|
-
const userId = licenseCheck.userId;
|
|
3186
|
+
// console.log(`[Recording] Mode: ${serverMode}`);
|
|
2769
3187
|
|
|
2770
3188
|
// Check if session manager is available
|
|
2771
3189
|
if (!sessionManager) {
|
|
@@ -2786,12 +3204,12 @@ async function startRecording(tabId, settings = {}) {
|
|
|
2786
3204
|
|
|
2787
3205
|
// Start capturing console logs FIRST, before any other setup
|
|
2788
3206
|
const logsStarted = await startCapturingLogs(tabId);
|
|
2789
|
-
console.log('Console log capture started:', logsStarted);
|
|
3207
|
+
// console.log('Console log capture started:', logsStarted);
|
|
2790
3208
|
|
|
2791
3209
|
// Give console interception time to settle (increased from 100ms to 500ms for reliability)
|
|
2792
3210
|
if (logsStarted) {
|
|
2793
3211
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
2794
|
-
console.log('[Recording] Console interception ready');
|
|
3212
|
+
// console.log('[Recording] Console interception ready');
|
|
2795
3213
|
} else {
|
|
2796
3214
|
console.warn('[Recording] Console capture unavailable for this page - recording will proceed without console logs');
|
|
2797
3215
|
}
|
|
@@ -2804,7 +3222,7 @@ async function startRecording(tabId, settings = {}) {
|
|
|
2804
3222
|
targetTabId: tabId
|
|
2805
3223
|
});
|
|
2806
3224
|
|
|
2807
|
-
console.log('Got stream ID:', streamId);
|
|
3225
|
+
// console.log('Got stream ID:', streamId);
|
|
2808
3226
|
|
|
2809
3227
|
// Create session with session manager
|
|
2810
3228
|
const sessionConfig = {
|
|
@@ -2837,7 +3255,7 @@ async function startRecording(tabId, settings = {}) {
|
|
|
2837
3255
|
|
|
2838
3256
|
// STEP 4: Initialize browser-only recording if in browser-only mode
|
|
2839
3257
|
if (serverMode === 'browser-only' && browserRecordingManager) {
|
|
2840
|
-
console.log('[Recording] Initializing browser-only recording in IndexedDB');
|
|
3258
|
+
// console.log('[Recording] Initializing browser-only recording in IndexedDB');
|
|
2841
3259
|
const tab = await chrome.tabs.get(tabId);
|
|
2842
3260
|
await browserRecordingManager.startRecording(sessionId, {
|
|
2843
3261
|
tabId: tabId,
|
|
@@ -2870,7 +3288,19 @@ async function startRecording(tabId, settings = {}) {
|
|
|
2870
3288
|
// Update badge
|
|
2871
3289
|
chrome.action.setBadgeText({ text: 'REC' });
|
|
2872
3290
|
chrome.action.setBadgeBackgroundColor({ color: '#FF0000' });
|
|
2873
|
-
|
|
3291
|
+
|
|
3292
|
+
// DON'T start inactivity monitoring here - wait for countdownComplete message
|
|
3293
|
+
// This ensures the 10s timer starts AFTER the 3s visual countdown, not before
|
|
3294
|
+
// Set a fallback timer in case content script doesn't send countdownComplete
|
|
3295
|
+
inactivityMonitoringStarted = false;
|
|
3296
|
+
if (inactivityFallbackTimer) clearTimeout(inactivityFallbackTimer);
|
|
3297
|
+
inactivityFallbackTimer = setTimeout(async () => {
|
|
3298
|
+
// Use isCurrentlyRecording (the actual state variable)
|
|
3299
|
+
if (isCurrentlyRecording && !inactivityMonitoringStarted) {
|
|
3300
|
+
await startInactivityMonitoring(tabId);
|
|
3301
|
+
}
|
|
3302
|
+
}, 5000); // 5s = 3s countdown + 2s buffer
|
|
3303
|
+
|
|
2874
3304
|
// If on a restricted page, notify user
|
|
2875
3305
|
const tab = await chrome.tabs.get(tabId);
|
|
2876
3306
|
if (tab.url && (tab.url.startsWith('chrome://') || tab.url.startsWith('chrome-extension://'))) {
|
|
@@ -2891,12 +3321,12 @@ async function startRecording(tabId, settings = {}) {
|
|
|
2891
3321
|
sessionId: sessionId
|
|
2892
3322
|
}).catch(async (error) => {
|
|
2893
3323
|
// Content script might not be injected, try to inject it
|
|
2894
|
-
console.log('Could not notify content script, attempting to inject:', error);
|
|
3324
|
+
// console.log('Could not notify content script, attempting to inject:', error);
|
|
2895
3325
|
|
|
2896
3326
|
// First check if this tab allows content script injection
|
|
2897
3327
|
const tab = await chrome.tabs.get(tabId);
|
|
2898
3328
|
if (!tab.url || tab.url.startsWith('chrome://') || tab.url.startsWith('chrome-extension://') || tab.url.startsWith('moz-extension://')) {
|
|
2899
|
-
console.log('Cannot inject content script into restricted URL:', tab.url);
|
|
3329
|
+
// console.log('Cannot inject content script into restricted URL:', tab.url);
|
|
2900
3330
|
return;
|
|
2901
3331
|
}
|
|
2902
3332
|
|
|
@@ -2932,7 +3362,7 @@ async function startRecording(tabId, settings = {}) {
|
|
|
2932
3362
|
url: tab.url,
|
|
2933
3363
|
title: tab.title
|
|
2934
3364
|
});
|
|
2935
|
-
console.log('[Recording] Browser recording initialized for session:', sessionId);
|
|
3365
|
+
// console.log('[Recording] Browser recording initialized for session:', sessionId);
|
|
2936
3366
|
}
|
|
2937
3367
|
|
|
2938
3368
|
// Send stream ID, session info, and settings to offscreen document for frame capture
|
|
@@ -2952,7 +3382,7 @@ async function startRecording(tabId, settings = {}) {
|
|
|
2952
3382
|
}
|
|
2953
3383
|
});
|
|
2954
3384
|
|
|
2955
|
-
console.log('Recording started in', serverMode, 'mode');
|
|
3385
|
+
// console.log('Recording started in', serverMode, 'mode');
|
|
2956
3386
|
} catch (error) {
|
|
2957
3387
|
console.error('Error in startRecording:', error);
|
|
2958
3388
|
throw error;
|
|
@@ -2965,16 +3395,26 @@ async function stopRecording() {
|
|
|
2965
3395
|
// Check if session manager is available
|
|
2966
3396
|
if (!sessionManager) {
|
|
2967
3397
|
console.warn('Session manager not available, stopping recording anyway');
|
|
2968
|
-
return;
|
|
3398
|
+
return { success: false, error: 'Session manager not available' };
|
|
2969
3399
|
}
|
|
2970
3400
|
|
|
2971
3401
|
// Check if currently recording via session manager
|
|
2972
3402
|
const currentlyRecording = await isCurrentlyRecordingAsync();
|
|
2973
3403
|
if (!currentlyRecording) {
|
|
2974
|
-
console.log('No active recording session to stop');
|
|
2975
|
-
return;
|
|
3404
|
+
// console.log('No active recording session to stop');
|
|
3405
|
+
return { success: false, error: 'No active recording' };
|
|
2976
3406
|
}
|
|
2977
3407
|
|
|
3408
|
+
// Store sessionId before clearing it
|
|
3409
|
+
const stoppedSessionId = currentSession?.sessionId;
|
|
3410
|
+
|
|
3411
|
+
// Stop inactivity monitoring
|
|
3412
|
+
await stopInactivityMonitoring(recordingTabId);
|
|
3413
|
+
|
|
3414
|
+
// Capture frame count and duration for the response
|
|
3415
|
+
let recordedFrameCount = 0;
|
|
3416
|
+
let recordedDuration = 0;
|
|
3417
|
+
|
|
2978
3418
|
// Stop recording with session manager
|
|
2979
3419
|
if (currentSession?.sessionId && currentOwnerId) {
|
|
2980
3420
|
const stopResult = await sessionManager.stopRecording(currentSession.sessionId, currentOwnerId);
|
|
@@ -2982,18 +3422,15 @@ async function stopRecording() {
|
|
|
2982
3422
|
console.error('Failed to stop session:', stopResult.error?.message);
|
|
2983
3423
|
// Continue with cleanup anyway
|
|
2984
3424
|
} else {
|
|
2985
|
-
|
|
3425
|
+
recordedFrameCount = stopResult.frameCount || 0;
|
|
3426
|
+
recordedDuration = stopResult.sessionDuration || 0;
|
|
3427
|
+
// console.log(`Recording stopped. Duration: ${recordedDuration}ms, Frames: ${recordedFrameCount}`);
|
|
2986
3428
|
|
|
2987
3429
|
// Finalize browser-only recording if in browser-only mode
|
|
2988
3430
|
if (serverMode === 'browser-only' && browserRecordingManager) {
|
|
2989
|
-
console.log('[Browser-Only] Finalizing browser recording in IndexedDB');
|
|
3431
|
+
// console.log('[Browser-Only] Finalizing browser recording in IndexedDB');
|
|
2990
3432
|
await browserRecordingManager.stopRecording(currentSession.sessionId);
|
|
2991
3433
|
}
|
|
2992
|
-
|
|
2993
|
-
// STEP 2: Track usage AFTER successful recording completion
|
|
2994
|
-
console.log('[License] Tracking usage after recording completion...');
|
|
2995
|
-
const usageResult = await LicenseHelper.trackUsageAfterRecording();
|
|
2996
|
-
console.log('[License] Usage tracking result:', usageResult);
|
|
2997
3434
|
}
|
|
2998
3435
|
}
|
|
2999
3436
|
|
|
@@ -3002,10 +3439,10 @@ async function stopRecording() {
|
|
|
3002
3439
|
let flushCompleted = false;
|
|
3003
3440
|
if (logBuffer) {
|
|
3004
3441
|
try {
|
|
3005
|
-
console.log('[Background] Flushing all buffered logs before stop...');
|
|
3442
|
+
// console.log('[Background] Flushing all buffered logs before stop...');
|
|
3006
3443
|
await logBuffer.flushAll();
|
|
3007
3444
|
flushCompleted = true;
|
|
3008
|
-
console.log('[Background] Successfully flushed all buffered logs');
|
|
3445
|
+
// console.log('[Background] Successfully flushed all buffered logs');
|
|
3009
3446
|
} catch (flushError) {
|
|
3010
3447
|
console.error('[Background] Failed to flush logs during stop:', flushError);
|
|
3011
3448
|
// Continue with stop process even if flush fails
|
|
@@ -3023,6 +3460,29 @@ async function stopRecording() {
|
|
|
3023
3460
|
target: 'offscreen'
|
|
3024
3461
|
});
|
|
3025
3462
|
|
|
3463
|
+
// CRITICAL: Store recordingTabId BEFORE clearing it (for cleanup)
|
|
3464
|
+
const previousRecordingTabId = recordingTabId;
|
|
3465
|
+
// console.log('[Background] previousRecordingTabId:', previousRecordingTabId, 'stoppedSessionId:', stoppedSessionId);
|
|
3466
|
+
|
|
3467
|
+
// CRITICAL FIX: Send stop-screen-capture-tracking message to recording tab WITH sessionId
|
|
3468
|
+
// so it can show the completion UI
|
|
3469
|
+
if (previousRecordingTabId && stoppedSessionId) {
|
|
3470
|
+
try {
|
|
3471
|
+
// console.log('[Background] Sending recording-complete-show-ui message to tab:', previousRecordingTabId, 'with sessionId:', stoppedSessionId);
|
|
3472
|
+
await chrome.tabs.sendMessage(previousRecordingTabId, {
|
|
3473
|
+
type: 'recording-complete-show-ui',
|
|
3474
|
+
sessionId: stoppedSessionId
|
|
3475
|
+
}).catch((err) => {
|
|
3476
|
+
// console.log('[Background] Could not send completion UI message to recording tab:', err);
|
|
3477
|
+
});
|
|
3478
|
+
// console.log('[Background] Successfully sent recording-complete-show-ui message');
|
|
3479
|
+
} catch (error) {
|
|
3480
|
+
console.error('[Background] Failed to send completion UI message:', error);
|
|
3481
|
+
}
|
|
3482
|
+
} else {
|
|
3483
|
+
// console.log('[Background] NOT sending recording-complete-show-ui - missing previousRecordingTabId or stoppedSessionId');
|
|
3484
|
+
}
|
|
3485
|
+
|
|
3026
3486
|
// CRITICAL FIX: Send stop-screen-capture-tracking message to ALL content scripts
|
|
3027
3487
|
try {
|
|
3028
3488
|
const tabs = await chrome.tabs.query({});
|
|
@@ -3032,23 +3492,33 @@ async function stopRecording() {
|
|
|
3032
3492
|
type: 'stop-screen-capture-tracking'
|
|
3033
3493
|
}).catch(() => {
|
|
3034
3494
|
// Ignore errors for tabs that don't have content scripts
|
|
3035
|
-
console.log(`[Background] Could not send cleanup message to tab ${tab.id}`);
|
|
3495
|
+
// console.log(`[Background] Could not send cleanup message to tab ${tab.id}`);
|
|
3036
3496
|
});
|
|
3037
3497
|
}
|
|
3038
3498
|
return Promise.resolve();
|
|
3039
3499
|
});
|
|
3040
3500
|
await Promise.all(cleanupPromises);
|
|
3041
|
-
console.log('[Background] Cleanup messages sent to all content scripts');
|
|
3501
|
+
// console.log('[Background] Cleanup messages sent to all content scripts');
|
|
3042
3502
|
} catch (error) {
|
|
3043
3503
|
console.error('[Background] Failed to send cleanup messages to content scripts:', error);
|
|
3044
3504
|
}
|
|
3045
3505
|
|
|
3506
|
+
// CRITICAL FIX: Restore original console methods and remove event listeners
|
|
3507
|
+
if (previousRecordingTabId) {
|
|
3508
|
+
try {
|
|
3509
|
+
// console.log('[Background] Stopping console log capture for tab:', previousRecordingTabId);
|
|
3510
|
+
await stopCapturingLogs(previousRecordingTabId);
|
|
3511
|
+
} catch (cleanupError) {
|
|
3512
|
+
console.error('[Background] Failed to cleanup console interceptor:', cleanupError);
|
|
3513
|
+
// Continue with stop process even if cleanup fails
|
|
3514
|
+
}
|
|
3515
|
+
}
|
|
3516
|
+
|
|
3046
3517
|
// Clear session cache
|
|
3047
3518
|
currentSession = null;
|
|
3048
3519
|
currentOwnerId = null;
|
|
3049
3520
|
|
|
3050
3521
|
// Update legacy state for backward compatibility (will be removed)
|
|
3051
|
-
const previousRecordingTabId = recordingTabId; // Store for logBuffer cleanup
|
|
3052
3522
|
isCurrentlyRecording = false;
|
|
3053
3523
|
recordingTabId = null;
|
|
3054
3524
|
currentRecordingSessionId = null;
|
|
@@ -3058,11 +3528,56 @@ async function stopRecording() {
|
|
|
3058
3528
|
logBuffer.clearTab(previousRecordingTabId);
|
|
3059
3529
|
}
|
|
3060
3530
|
|
|
3061
|
-
|
|
3531
|
+
// CRITICAL: Clear pending frame queue to prevent infinite retry loop
|
|
3532
|
+
if (stoppedSessionId && pendingFrameQueue.has(stoppedSessionId)) {
|
|
3533
|
+
// console.log(`[FrameQueue] Clearing ${pendingFrameQueue.get(stoppedSessionId).length} pending frames for stopped session ${stoppedSessionId}`);
|
|
3534
|
+
pendingFrameQueue.delete(stoppedSessionId);
|
|
3535
|
+
}
|
|
3536
|
+
|
|
3537
|
+
// console.log('Stop message sent to offscreen document');
|
|
3538
|
+
|
|
3539
|
+
// Notify popup to refresh recordings list
|
|
3540
|
+
chrome.runtime.sendMessage({
|
|
3541
|
+
action: 'recordingStopped',
|
|
3542
|
+
sessionId: stoppedSessionId
|
|
3543
|
+
}).catch(() => {
|
|
3544
|
+
// Popup might not be open, that's okay
|
|
3545
|
+
// console.log('[Background] Popup not available to notify of recording stop');
|
|
3546
|
+
});
|
|
3547
|
+
|
|
3548
|
+
// Format duration as human-readable string (e.g., "1m 30s" or "45s")
|
|
3549
|
+
const formatDurationForDisplay = (ms) => {
|
|
3550
|
+
const seconds = Math.floor(ms / 1000);
|
|
3551
|
+
if (seconds < 60) {
|
|
3552
|
+
return `${seconds}s`;
|
|
3553
|
+
}
|
|
3554
|
+
const minutes = Math.floor(seconds / 60);
|
|
3555
|
+
const remainingSeconds = seconds % 60;
|
|
3556
|
+
return remainingSeconds > 0 ? `${minutes}m ${remainingSeconds}s` : `${minutes}m`;
|
|
3557
|
+
};
|
|
3558
|
+
|
|
3559
|
+
// Return success with sessionId, frameCount and duration for popup display
|
|
3560
|
+
return {
|
|
3561
|
+
success: true,
|
|
3562
|
+
sessionId: stoppedSessionId,
|
|
3563
|
+
frameCount: recordedFrameCount,
|
|
3564
|
+
duration: formatDurationForDisplay(recordedDuration)
|
|
3565
|
+
};
|
|
3062
3566
|
} catch (error) {
|
|
3063
3567
|
console.error('Error in stopRecording:', error);
|
|
3064
3568
|
// Clear session cache even on error
|
|
3065
3569
|
const errorRecordingTabId = recordingTabId; // Store for cleanup
|
|
3570
|
+
|
|
3571
|
+
// CRITICAL FIX: Clean up console interceptor even on error
|
|
3572
|
+
if (errorRecordingTabId) {
|
|
3573
|
+
try {
|
|
3574
|
+
// console.log('[Background] Emergency cleanup of console interceptor for tab:', errorRecordingTabId);
|
|
3575
|
+
await stopCapturingLogs(errorRecordingTabId);
|
|
3576
|
+
} catch (cleanupError) {
|
|
3577
|
+
console.error('[Background] Failed to cleanup console interceptor during error handling:', cleanupError);
|
|
3578
|
+
}
|
|
3579
|
+
}
|
|
3580
|
+
|
|
3066
3581
|
currentSession = null;
|
|
3067
3582
|
currentOwnerId = null;
|
|
3068
3583
|
isCurrentlyRecording = false;
|
|
@@ -3081,238 +3596,27 @@ async function stopRecording() {
|
|
|
3081
3596
|
|
|
3082
3597
|
|
|
3083
3598
|
// Capture console logs from the recording tab
|
|
3599
|
+
// REFACTORED: Now uses shared console-interception-library.js
|
|
3084
3600
|
async function startCapturingLogs(tabId) {
|
|
3085
|
-
//
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
console.log('Cannot inject console logger into restricted URL:', tab.url);
|
|
3089
|
-
console.warn('WARNING: Console logs cannot be captured on restricted pages (chrome://, chrome-extension://, etc.)');
|
|
3090
|
-
console.warn('To capture console logs, please navigate to a regular web page before recording.');
|
|
3091
|
-
return false;
|
|
3092
|
-
}
|
|
3093
|
-
|
|
3094
|
-
// First inject the main world console interceptor
|
|
3095
|
-
try {
|
|
3096
|
-
const results = await chrome.scripting.executeScript({
|
|
3097
|
-
target: { tabId: tabId },
|
|
3098
|
-
world: 'MAIN',
|
|
3099
|
-
func: () => {
|
|
3100
|
-
// Check if we've already overridden console methods
|
|
3101
|
-
if (window.__chromePilotConsoleOverridden) {
|
|
3102
|
-
console.log('[Chrome Debug] Console already overridden, skipping');
|
|
3103
|
-
return 'already_installed';
|
|
3104
|
-
}
|
|
3105
|
-
window.__chromePilotConsoleOverridden = true;
|
|
3106
|
-
console.log('[Chrome Debug] Installing console interceptor');
|
|
3107
|
-
|
|
3108
|
-
// Override console methods to capture logs - complete coverage
|
|
3109
|
-
const originalLog = console.log;
|
|
3110
|
-
const originalError = console.error;
|
|
3111
|
-
const originalWarn = console.warn;
|
|
3112
|
-
const originalInfo = console.info;
|
|
3113
|
-
const originalDebug = console.debug;
|
|
3114
|
-
const originalTrace = console.trace;
|
|
3115
|
-
const originalTable = console.table;
|
|
3116
|
-
const originalDir = console.dir;
|
|
3117
|
-
const originalGroup = console.group;
|
|
3118
|
-
const originalGroupEnd = console.groupEnd;
|
|
3119
|
-
const originalTime = console.time;
|
|
3120
|
-
const originalTimeEnd = console.timeEnd;
|
|
3121
|
-
const originalCount = console.count;
|
|
3122
|
-
|
|
3123
|
-
const sendLog = (level, args) => {
|
|
3124
|
-
try {
|
|
3125
|
-
// Pre-serialize arguments to strings to avoid structured clone errors
|
|
3126
|
-
const serializedArgs = args.map(arg => {
|
|
3127
|
-
try {
|
|
3128
|
-
if (arg === null) return 'null';
|
|
3129
|
-
if (arg === undefined) return 'undefined';
|
|
3130
|
-
if (typeof arg === 'function') return '[Function: ' + (arg.name || 'anonymous') + ']';
|
|
3131
|
-
if (arg instanceof Element) return '[DOM Element: ' + arg.tagName + ']';
|
|
3132
|
-
if (typeof arg === 'object') {
|
|
3133
|
-
// Try to stringify, but limit depth to avoid circular references
|
|
3134
|
-
let stringified = JSON.stringify(arg, null, 2);
|
|
3135
|
-
|
|
3136
|
-
// Check if this looks like a base64 image and truncate it
|
|
3137
|
-
if (stringified.includes('data:image/') && stringified.length > 1000) {
|
|
3138
|
-
const match = stringified.match(/data:image\/([^;]+);base64,(.{0,100})/);
|
|
3139
|
-
if (match) {
|
|
3140
|
-
return `[Base64 Image: ${match[1]}, ${stringified.length} bytes total, truncated...]`;
|
|
3141
|
-
}
|
|
3142
|
-
}
|
|
3143
|
-
|
|
3144
|
-
// Truncate any extremely large strings
|
|
3145
|
-
const maxLength = 5000;
|
|
3146
|
-
if (stringified.length > maxLength) {
|
|
3147
|
-
return stringified.substring(0, maxLength) + `... [TRUNCATED: ${stringified.length} total bytes]`;
|
|
3148
|
-
}
|
|
3149
|
-
|
|
3150
|
-
return stringified;
|
|
3151
|
-
}
|
|
3152
|
-
|
|
3153
|
-
// Also check for base64 strings directly
|
|
3154
|
-
const strValue = String(arg);
|
|
3155
|
-
if (strValue.includes('data:image/') && strValue.length > 1000) {
|
|
3156
|
-
const match = strValue.match(/data:image\/([^;]+);base64,(.{0,100})/);
|
|
3157
|
-
if (match) {
|
|
3158
|
-
return `[Base64 Image: ${match[1]}, ${strValue.length} bytes total, truncated...]`;
|
|
3159
|
-
}
|
|
3160
|
-
}
|
|
3161
|
-
|
|
3162
|
-
// Truncate any extremely large strings
|
|
3163
|
-
if (strValue.length > 5000) {
|
|
3164
|
-
return strValue.substring(0, 5000) + `... [TRUNCATED: ${strValue.length} total bytes]`;
|
|
3165
|
-
}
|
|
3166
|
-
|
|
3167
|
-
return strValue;
|
|
3168
|
-
} catch (e) {
|
|
3169
|
-
return '[Object: could not serialize]';
|
|
3170
|
-
}
|
|
3171
|
-
});
|
|
3172
|
-
|
|
3173
|
-
// Post message to content script
|
|
3174
|
-
window.postMessage({
|
|
3175
|
-
type: 'chrome-pilot-console-log',
|
|
3176
|
-
log: {
|
|
3177
|
-
level,
|
|
3178
|
-
message: serializedArgs.join(' '),
|
|
3179
|
-
timestamp: Date.now()
|
|
3180
|
-
}
|
|
3181
|
-
}, '*');
|
|
3182
|
-
} catch (e) {
|
|
3183
|
-
// Ignore errors when sending logs
|
|
3184
|
-
}
|
|
3185
|
-
};
|
|
3186
|
-
|
|
3187
|
-
console.log = (...args) => {
|
|
3188
|
-
sendLog('log', args);
|
|
3189
|
-
originalLog.apply(console, args);
|
|
3190
|
-
};
|
|
3191
|
-
|
|
3192
|
-
console.error = (...args) => {
|
|
3193
|
-
sendLog('error', args);
|
|
3194
|
-
originalError.apply(console, args);
|
|
3195
|
-
};
|
|
3196
|
-
|
|
3197
|
-
console.warn = (...args) => {
|
|
3198
|
-
sendLog('warn', args);
|
|
3199
|
-
originalWarn.apply(console, args);
|
|
3200
|
-
};
|
|
3201
|
-
|
|
3202
|
-
console.info = (...args) => {
|
|
3203
|
-
sendLog('info', args);
|
|
3204
|
-
originalInfo.apply(console, args);
|
|
3205
|
-
};
|
|
3206
|
-
|
|
3207
|
-
console.debug = (...args) => {
|
|
3208
|
-
sendLog('debug', args);
|
|
3209
|
-
originalDebug.apply(console, args);
|
|
3210
|
-
};
|
|
3211
|
-
|
|
3212
|
-
console.trace = (...args) => {
|
|
3213
|
-
sendLog('trace', args);
|
|
3214
|
-
originalTrace.apply(console, args);
|
|
3215
|
-
};
|
|
3216
|
-
|
|
3217
|
-
console.table = (...args) => {
|
|
3218
|
-
sendLog('table', args);
|
|
3219
|
-
originalTable.apply(console, args);
|
|
3220
|
-
};
|
|
3221
|
-
|
|
3222
|
-
console.dir = (...args) => {
|
|
3223
|
-
sendLog('dir', args);
|
|
3224
|
-
originalDir.apply(console, args);
|
|
3225
|
-
};
|
|
3226
|
-
|
|
3227
|
-
console.group = (...args) => {
|
|
3228
|
-
sendLog('group', args);
|
|
3229
|
-
originalGroup.apply(console, args);
|
|
3230
|
-
};
|
|
3231
|
-
|
|
3232
|
-
console.groupEnd = (...args) => {
|
|
3233
|
-
sendLog('groupEnd', args);
|
|
3234
|
-
originalGroupEnd.apply(console, args);
|
|
3235
|
-
};
|
|
3236
|
-
|
|
3237
|
-
console.time = (...args) => {
|
|
3238
|
-
sendLog('time', args);
|
|
3239
|
-
originalTime.apply(console, args);
|
|
3240
|
-
};
|
|
3241
|
-
|
|
3242
|
-
console.timeEnd = (...args) => {
|
|
3243
|
-
sendLog('timeEnd', args);
|
|
3244
|
-
originalTimeEnd.apply(console, args);
|
|
3245
|
-
};
|
|
3246
|
-
|
|
3247
|
-
console.count = (...args) => {
|
|
3248
|
-
sendLog('count', args);
|
|
3249
|
-
originalCount.apply(console, args);
|
|
3250
|
-
};
|
|
3251
|
-
|
|
3252
|
-
return 'console_installed';
|
|
3253
|
-
}
|
|
3254
|
-
});
|
|
3255
|
-
|
|
3256
|
-
console.log('[Console Injection] MAIN world script injected successfully:', results);
|
|
3257
|
-
|
|
3258
|
-
// Then inject a content script to relay messages from main world to background
|
|
3259
|
-
await chrome.scripting.executeScript({
|
|
3260
|
-
target: { tabId: tabId },
|
|
3261
|
-
func: () => {
|
|
3262
|
-
// Listen for messages from main world
|
|
3263
|
-
window.addEventListener('message', (event) => {
|
|
3264
|
-
if (event.data && event.data.type === 'chrome-pilot-console-log') {
|
|
3265
|
-
// Forward to background script
|
|
3266
|
-
chrome.runtime.sendMessage({
|
|
3267
|
-
action: 'consoleLog',
|
|
3268
|
-
log: event.data.log
|
|
3269
|
-
});
|
|
3270
|
-
}
|
|
3271
|
-
});
|
|
3272
|
-
}
|
|
3273
|
-
});
|
|
3601
|
+
// console.log('[Screen Recording] Starting console interception using shared library');
|
|
3602
|
+
return await self.ConsoleInterceptionLibrary.startConsoleInterception(tabId, SCREEN_RECORDING_CONSOLE_CONFIG);
|
|
3603
|
+
}
|
|
3274
3604
|
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
console.error('[Console Injection] Error details:', error.message, error.stack);
|
|
3280
|
-
return false; // Failed to inject console interceptor
|
|
3281
|
-
}
|
|
3605
|
+
// REFACTORED: Now uses shared console-interception-library.js via proper namespace
|
|
3606
|
+
async function stopCapturingLogs(tabId) {
|
|
3607
|
+
// console.log('[Screen Recording] Stopping console interception using shared library');
|
|
3608
|
+
return await self.ConsoleInterceptionLibrary.stopConsoleInterception(tabId, SCREEN_RECORDING_CONSOLE_CONFIG);
|
|
3282
3609
|
}
|
|
3283
3610
|
|
|
3284
3611
|
// Workflow Recording Functions
|
|
3285
3612
|
async function startWorkflowRecording(tabId, includeLogsInExport, sessionName = null, screenshotSettings = null) {
|
|
3286
3613
|
try {
|
|
3287
|
-
console.log('Starting workflow recording for tab:', tabId);
|
|
3288
|
-
|
|
3289
|
-
// STEP 1: Check license and usage limits BEFORE recording (same pattern as startRecording)
|
|
3290
|
-
console.log('[License] Checking license before workflow recording...');
|
|
3291
|
-
const licenseCheck = await LicenseHelper.checkLicenseBeforeRecording();
|
|
3292
|
-
console.log('[License] License check result:', licenseCheck);
|
|
3293
|
-
|
|
3294
|
-
if (!licenseCheck.allowed) {
|
|
3295
|
-
// Show notification to user (same pattern as screen recording)
|
|
3296
|
-
chrome.notifications.create({
|
|
3297
|
-
type: 'basic',
|
|
3298
|
-
iconUrl: chrome.runtime.getURL('icon128.png'),
|
|
3299
|
-
title: 'Recording Limit Reached',
|
|
3300
|
-
message: licenseCheck.message || 'Daily limit reached. Upgrade to Pro for unlimited workflow recordings.',
|
|
3301
|
-
buttons: [{ title: 'Upgrade to Pro' }],
|
|
3302
|
-
priority: 2
|
|
3303
|
-
});
|
|
3304
|
-
|
|
3305
|
-
throw new Error(licenseCheck.message || 'Workflow recording not allowed');
|
|
3306
|
-
}
|
|
3307
|
-
|
|
3308
|
-
// Store userId for later usage tracking
|
|
3309
|
-
const userId = licenseCheck.userId;
|
|
3310
|
-
workflowUserIds.set(tabId, userId);
|
|
3614
|
+
// console.log('Starting workflow recording for tab:', tabId);
|
|
3311
3615
|
|
|
3312
3616
|
// Check if this tab allows content script injection
|
|
3313
3617
|
const tab = await chrome.tabs.get(tabId);
|
|
3314
3618
|
if (!tab.url || tab.url.startsWith('chrome://') || tab.url.startsWith('chrome-extension://') || tab.url.startsWith('moz-extension://')) {
|
|
3315
|
-
console.log('Cannot start workflow recording on restricted URL:', tab.url);
|
|
3619
|
+
// console.log('Cannot start workflow recording on restricted URL:', tab.url);
|
|
3316
3620
|
throw new Error('Cannot record workflows on restricted pages (chrome:// URLs)');
|
|
3317
3621
|
}
|
|
3318
3622
|
|
|
@@ -3326,107 +3630,18 @@ async function startWorkflowRecording(tabId, includeLogsInExport, sessionName =
|
|
|
3326
3630
|
const workflowId = `workflow_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
3327
3631
|
await chrome.storage.local.set({ currentWorkflowId: workflowId });
|
|
3328
3632
|
|
|
3329
|
-
// Clear any existing logs for this tab
|
|
3633
|
+
// Clear any existing logs for this tab and initialize race-safe buffer
|
|
3330
3634
|
await chrome.storage.session.set({ [`workflow_${tabId}`]: [] });
|
|
3331
|
-
|
|
3332
|
-
//
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
window.__chromePilotWorkflowConsoleOverridden = true;
|
|
3342
|
-
|
|
3343
|
-
// Override console methods to capture logs
|
|
3344
|
-
const originalLog = console.log;
|
|
3345
|
-
const originalError = console.error;
|
|
3346
|
-
const originalWarn = console.warn;
|
|
3347
|
-
const originalInfo = console.info;
|
|
3348
|
-
|
|
3349
|
-
const sendLog = (level, args) => {
|
|
3350
|
-
try {
|
|
3351
|
-
// Pre-serialize arguments to strings to avoid structured clone errors
|
|
3352
|
-
const serializedArgs = args.map(arg => {
|
|
3353
|
-
try {
|
|
3354
|
-
if (arg === null) return 'null';
|
|
3355
|
-
if (arg === undefined) return 'undefined';
|
|
3356
|
-
if (typeof arg === 'function') return '[Function: ' + (arg.name || 'anonymous') + ']';
|
|
3357
|
-
if (arg instanceof Element) return '[DOM Element: ' + arg.tagName + ']';
|
|
3358
|
-
if (typeof arg === 'object') {
|
|
3359
|
-
// Try to stringify, but limit depth to avoid circular references
|
|
3360
|
-
return JSON.stringify(arg, null, 2);
|
|
3361
|
-
}
|
|
3362
|
-
return String(arg);
|
|
3363
|
-
} catch (e) {
|
|
3364
|
-
return '[Object: could not serialize]';
|
|
3365
|
-
}
|
|
3366
|
-
});
|
|
3367
|
-
|
|
3368
|
-
// Post message to content script
|
|
3369
|
-
window.postMessage({
|
|
3370
|
-
type: 'chrome-pilot-workflow-console-log',
|
|
3371
|
-
log: {
|
|
3372
|
-
level,
|
|
3373
|
-
message: serializedArgs.join(' '),
|
|
3374
|
-
timestamp: Date.now()
|
|
3375
|
-
}
|
|
3376
|
-
}, '*');
|
|
3377
|
-
} catch (e) {
|
|
3378
|
-
// Ignore errors when sending logs
|
|
3379
|
-
}
|
|
3380
|
-
};
|
|
3381
|
-
|
|
3382
|
-
console.log = (...args) => {
|
|
3383
|
-
sendLog('log', args);
|
|
3384
|
-
originalLog.apply(console, args);
|
|
3385
|
-
};
|
|
3386
|
-
|
|
3387
|
-
console.error = (...args) => {
|
|
3388
|
-
sendLog('error', args);
|
|
3389
|
-
originalError.apply(console, args);
|
|
3390
|
-
};
|
|
3391
|
-
|
|
3392
|
-
console.warn = (...args) => {
|
|
3393
|
-
sendLog('warn', args);
|
|
3394
|
-
originalWarn.apply(console, args);
|
|
3395
|
-
};
|
|
3396
|
-
|
|
3397
|
-
console.info = (...args) => {
|
|
3398
|
-
sendLog('info', args);
|
|
3399
|
-
originalInfo.apply(console, args);
|
|
3400
|
-
};
|
|
3401
|
-
|
|
3402
|
-
// Also capture window errors
|
|
3403
|
-
window.addEventListener('error', (event) => {
|
|
3404
|
-
sendLog('exception', [`${event.message} at ${event.filename}:${event.lineno}:${event.colno}`]);
|
|
3405
|
-
});
|
|
3406
|
-
|
|
3407
|
-
window.addEventListener('unhandledrejection', (event) => {
|
|
3408
|
-
sendLog('exception', [`Unhandled Promise Rejection: ${event.reason}`]);
|
|
3409
|
-
});
|
|
3410
|
-
}
|
|
3411
|
-
});
|
|
3412
|
-
|
|
3413
|
-
// Inject content script to relay messages and handle workflow recording
|
|
3414
|
-
await chrome.scripting.executeScript({
|
|
3415
|
-
target: { tabId: tabId },
|
|
3416
|
-
func: (includeLogsInExport) => {
|
|
3417
|
-
// Listen for console log messages from main world
|
|
3418
|
-
window.addEventListener('message', (event) => {
|
|
3419
|
-
if (event.data && event.data.type === 'chrome-pilot-workflow-console-log') {
|
|
3420
|
-
// Forward to background script
|
|
3421
|
-
chrome.runtime.sendMessage({
|
|
3422
|
-
action: 'workflowConsoleLog',
|
|
3423
|
-
log: event.data.log
|
|
3424
|
-
});
|
|
3425
|
-
}
|
|
3426
|
-
});
|
|
3427
|
-
},
|
|
3428
|
-
args: [includeLogsInExport]
|
|
3429
|
-
});
|
|
3635
|
+
|
|
3636
|
+
// Initialize WorkflowLogBuffer for race-safe log handling
|
|
3637
|
+
if (includeLogsInExport) {
|
|
3638
|
+
const buffer = new WorkflowLogBuffer(tabId);
|
|
3639
|
+
workflowLogBuffers.set(tabId, buffer);
|
|
3640
|
+
// console.log(`[Workflow] Initialized WorkflowLogBuffer for tab ${tabId}`);
|
|
3641
|
+
}
|
|
3642
|
+
|
|
3643
|
+
// REFACTORED: Use shared console interception library via proper namespace (replaces 150+ lines of duplicate code)
|
|
3644
|
+
await self.ConsoleInterceptionLibrary.startConsoleInterception(tabId, WORKFLOW_RECORDING_CONSOLE_CONFIG);
|
|
3430
3645
|
|
|
3431
3646
|
// Ensure content script is injected
|
|
3432
3647
|
try {
|
|
@@ -3440,16 +3655,18 @@ async function startWorkflowRecording(tabId, includeLogsInExport, sessionName =
|
|
|
3440
3655
|
});
|
|
3441
3656
|
} catch (e) {
|
|
3442
3657
|
// Content script might already be injected
|
|
3443
|
-
console.log('Content script injection attempt:', e.message);
|
|
3658
|
+
// console.log('Content script injection attempt:', e.message);
|
|
3444
3659
|
}
|
|
3445
3660
|
|
|
3446
3661
|
// Tell the content script to start recording
|
|
3662
|
+
// Include tabId so content script can use it when stopping recording
|
|
3447
3663
|
await chrome.tabs.sendMessage(tabId, {
|
|
3448
3664
|
action: 'startWorkflowRecording',
|
|
3449
|
-
screenshotSettings: screenshotSettings
|
|
3665
|
+
screenshotSettings: screenshotSettings,
|
|
3666
|
+
tabId: tabId
|
|
3450
3667
|
});
|
|
3451
3668
|
|
|
3452
|
-
console.log('Workflow recording started successfully');
|
|
3669
|
+
// console.log('Workflow recording started successfully');
|
|
3453
3670
|
} catch (error) {
|
|
3454
3671
|
console.error('Error starting workflow recording:', error);
|
|
3455
3672
|
throw error;
|
|
@@ -3458,58 +3675,84 @@ async function startWorkflowRecording(tabId, includeLogsInExport, sessionName =
|
|
|
3458
3675
|
|
|
3459
3676
|
async function stopWorkflowRecording(tabId) {
|
|
3460
3677
|
try {
|
|
3461
|
-
console.log('Stopping workflow recording for tab:', tabId);
|
|
3678
|
+
// console.log('Stopping workflow recording for tab:', tabId);
|
|
3462
3679
|
|
|
3463
3680
|
// Check if recording was active
|
|
3464
3681
|
if (!workflowRecordingTabs.has(tabId)) {
|
|
3465
3682
|
throw new Error('No workflow recording active for this tab');
|
|
3466
3683
|
}
|
|
3467
3684
|
|
|
3468
|
-
// Get workflow from content script
|
|
3685
|
+
// Get workflow from content script using a different action to avoid circular dependency
|
|
3686
|
+
// (mini menu also sends 'stopWorkflowRecording' to background, creating confusion)
|
|
3469
3687
|
const response = await chrome.tabs.sendMessage(tabId, {
|
|
3470
|
-
action: '
|
|
3688
|
+
action: 'getWorkflowData'
|
|
3471
3689
|
});
|
|
3472
|
-
|
|
3690
|
+
|
|
3473
3691
|
if (!response || !response.success) {
|
|
3474
3692
|
throw new Error('Failed to get workflow from content script');
|
|
3475
3693
|
}
|
|
3476
|
-
|
|
3694
|
+
|
|
3695
|
+
// CRITICAL FIX: Restore original console methods and remove event listeners
|
|
3696
|
+
try {
|
|
3697
|
+
// console.log('[Workflow] Stopping console log capture for tab:', tabId);
|
|
3698
|
+
await stopCapturingWorkflowLogs(tabId);
|
|
3699
|
+
} catch (cleanupError) {
|
|
3700
|
+
console.error('[Workflow] Failed to cleanup console interceptor:', cleanupError);
|
|
3701
|
+
// Continue with stop process even if cleanup fails
|
|
3702
|
+
}
|
|
3703
|
+
|
|
3477
3704
|
// Handle both old format (array) and new format (object with actions and functionTraces)
|
|
3478
3705
|
let workflowData = response.workflow || [];
|
|
3479
3706
|
let workflow = Array.isArray(workflowData) ? workflowData : (workflowData.actions || []);
|
|
3480
3707
|
let functionTraces = Array.isArray(workflowData) ? [] : (workflowData.functionTraces || []);
|
|
3481
3708
|
|
|
3482
|
-
console.log(`[Background] Received ${workflow.length} actions and ${functionTraces.length} function traces`);
|
|
3483
|
-
console.log('[SCREENSHOT-DEBUG] background.stopWorkflowRecording - Checking actions for screenshot_data');
|
|
3709
|
+
// console.log(`[Background] Received ${workflow.length} actions and ${functionTraces.length} function traces`);
|
|
3710
|
+
// console.log('[SCREENSHOT-DEBUG] background.stopWorkflowRecording - Checking actions for screenshot_data');
|
|
3484
3711
|
|
|
3485
3712
|
const actionsWithScreenshots = workflow.filter(a => a.screenshot_data);
|
|
3486
|
-
console.log('[SCREENSHOT-DEBUG] background.stopWorkflowRecording - Actions WITH screenshot_data:', actionsWithScreenshots.length);
|
|
3713
|
+
// console.log('[SCREENSHOT-DEBUG] background.stopWorkflowRecording - Actions WITH screenshot_data:', actionsWithScreenshots.length);
|
|
3487
3714
|
|
|
3488
3715
|
workflow.forEach((action, index) => {
|
|
3489
|
-
console.log(`[SCREENSHOT-DEBUG] background.stopWorkflowRecording - Action ${index}:`, {
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
});
|
|
3716
|
+
// console.log(`[SCREENSHOT-DEBUG] background.stopWorkflowRecording - Action ${index}:`, {
|
|
3717
|
+
// type: action.type,
|
|
3718
|
+
// hasScreenshotData: !!action.screenshot_data,
|
|
3719
|
+
// screenshotDataLength: action.screenshot_data?.length,
|
|
3720
|
+
// screenshotPreview: action.screenshot_data?.substring(0, 50)
|
|
3721
|
+
// });
|
|
3495
3722
|
});
|
|
3496
3723
|
|
|
3497
|
-
// If logs should be included, get them from session storage
|
|
3724
|
+
// If logs should be included, flush buffer and get them from session storage
|
|
3498
3725
|
if (workflowIncludeLogs.get(tabId)) {
|
|
3726
|
+
// CRITICAL: Flush any buffered logs before retrieval to ensure all logs are captured
|
|
3727
|
+
const buffer = workflowLogBuffers.get(tabId);
|
|
3728
|
+
if (buffer) {
|
|
3729
|
+
await buffer.flush();
|
|
3730
|
+
// console.log(`[Workflow] Flushed buffer before retrieval, stats:`, buffer.getStats());
|
|
3731
|
+
}
|
|
3732
|
+
|
|
3499
3733
|
const result = await chrome.storage.session.get(`workflow_${tabId}`);
|
|
3500
3734
|
const logs = result[`workflow_${tabId}`] || [];
|
|
3501
3735
|
|
|
3502
|
-
// Associate logs with workflow actions
|
|
3736
|
+
// Associate logs with workflow actions using NON-OVERLAPPING windows
|
|
3737
|
+
// Each log appears in exactly ONE action to avoid duplicates
|
|
3503
3738
|
workflow = workflow.map((action, index) => {
|
|
3504
|
-
//
|
|
3739
|
+
// Calculate non-overlapping time window:
|
|
3740
|
+
// - Each action owns logs from ITS timestamp until the NEXT action's timestamp
|
|
3741
|
+
// - First action also includes 500ms before it
|
|
3742
|
+
// - Last action includes 5000ms after it
|
|
3743
|
+
const windowStart = index === 0
|
|
3744
|
+
? action.timestamp - 500 // First action: include 500ms before
|
|
3745
|
+
: action.timestamp; // Other actions: start at this action's timestamp
|
|
3746
|
+
|
|
3747
|
+
const windowEnd = index < workflow.length - 1
|
|
3748
|
+
? workflow[index + 1].timestamp // Until next action's timestamp
|
|
3749
|
+
: action.timestamp + 5000; // Last action: 5000ms after
|
|
3750
|
+
|
|
3751
|
+
// Use >= for start and < for end to ensure non-overlapping windows
|
|
3505
3752
|
const actionLogs = logs.filter(log => {
|
|
3506
|
-
|
|
3507
|
-
const prevActionTime = index > 0 ? workflow[index - 1].timestamp : action.timestamp - 500;
|
|
3508
|
-
const nextActionTime = index < workflow.length - 1 ? workflow[index + 1].timestamp : action.timestamp + 5000;
|
|
3509
|
-
|
|
3510
|
-
return log.timestamp > prevActionTime && log.timestamp < nextActionTime;
|
|
3753
|
+
return log.timestamp >= windowStart && log.timestamp < windowEnd;
|
|
3511
3754
|
});
|
|
3512
|
-
|
|
3755
|
+
|
|
3513
3756
|
if (actionLogs.length > 0) {
|
|
3514
3757
|
return { ...action, logs: actionLogs };
|
|
3515
3758
|
}
|
|
@@ -3521,30 +3764,48 @@ async function stopWorkflowRecording(tabId) {
|
|
|
3521
3764
|
const sessionName = workflowSessionNames.get(tabId);
|
|
3522
3765
|
const screenshotSettings = workflowScreenshotSettings.get(tabId);
|
|
3523
3766
|
const includeLogs = workflowIncludeLogs.get(tabId) || false;
|
|
3524
|
-
const userId = workflowUserIds.get(tabId); // Get userId for usage tracking
|
|
3525
3767
|
|
|
3526
3768
|
// Clean up
|
|
3527
3769
|
workflowRecordingTabs.delete(tabId);
|
|
3528
3770
|
workflowIncludeLogs.delete(tabId);
|
|
3529
3771
|
workflowScreenshotSettings.delete(tabId);
|
|
3530
3772
|
workflowSessionNames.delete(tabId);
|
|
3531
|
-
|
|
3773
|
+
|
|
3774
|
+
// Clean up WorkflowLogBuffer
|
|
3775
|
+
const bufferToCleanup = workflowLogBuffers.get(tabId);
|
|
3776
|
+
if (bufferToCleanup) {
|
|
3777
|
+
await bufferToCleanup.cleanup();
|
|
3778
|
+
workflowLogBuffers.delete(tabId);
|
|
3779
|
+
// console.log(`[Workflow] Cleaned up WorkflowLogBuffer for tab ${tabId}`);
|
|
3780
|
+
}
|
|
3781
|
+
|
|
3532
3782
|
await chrome.storage.session.remove(`workflow_${tabId}`);
|
|
3533
|
-
|
|
3534
|
-
|
|
3783
|
+
|
|
3784
|
+
// Notify popup and other extension components that recording has stopped
|
|
3785
|
+
// This is critical for popup UI sync when stopping from mini-menu
|
|
3786
|
+
await chrome.storage.local.set({
|
|
3787
|
+
workflowRecording: false,
|
|
3788
|
+
workflowStartTime: null
|
|
3789
|
+
});
|
|
3790
|
+
|
|
3791
|
+
// console.log('Workflow recording stopped, returning workflow:', workflow);
|
|
3535
3792
|
|
|
3536
3793
|
// Try to save to server
|
|
3537
3794
|
try {
|
|
3538
3795
|
const serverPorts = CONFIG_PORTS.slice(0, 5); // Use first 5 configured ports for workflow recording
|
|
3539
3796
|
let serverResult = null;
|
|
3540
|
-
|
|
3797
|
+
|
|
3541
3798
|
// Get current URL and title
|
|
3542
3799
|
const tab = await chrome.tabs.get(tabId);
|
|
3543
|
-
|
|
3800
|
+
|
|
3801
|
+
// Retrieve the workflow ID that was generated at start
|
|
3802
|
+
const storedData = await chrome.storage.local.get(['currentWorkflowId']);
|
|
3803
|
+
const sessionId = storedData.currentWorkflowId || `workflow_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
3804
|
+
// console.log('[Workflow] Using sessionId for save:', sessionId);
|
|
3544
3805
|
|
|
3545
3806
|
for (const port of serverPorts) {
|
|
3546
3807
|
try {
|
|
3547
|
-
console.log(`[Workflow] Attempting to save to server on port ${port}...`);
|
|
3808
|
+
// console.log(`[Workflow] Attempting to save to server on port ${port}...`);
|
|
3548
3809
|
|
|
3549
3810
|
const payloadData = {
|
|
3550
3811
|
sessionId: sessionId,
|
|
@@ -3558,19 +3819,19 @@ async function stopWorkflowRecording(tabId) {
|
|
|
3558
3819
|
functionTraces: functionTraces // Include function execution traces
|
|
3559
3820
|
};
|
|
3560
3821
|
|
|
3561
|
-
console.log('[SCREENSHOT-DEBUG] background.stopWorkflowRecording - HTTP POST payload being prepared');
|
|
3562
|
-
console.log('[SCREENSHOT-DEBUG] background.stopWorkflowRecording - Payload actions count:', payloadData.actions.length);
|
|
3822
|
+
// console.log('[SCREENSHOT-DEBUG] background.stopWorkflowRecording - HTTP POST payload being prepared');
|
|
3823
|
+
// console.log('[SCREENSHOT-DEBUG] background.stopWorkflowRecording - Payload actions count:', payloadData.actions.length);
|
|
3563
3824
|
|
|
3564
3825
|
const payloadActionsWithScreenshots = payloadData.actions.filter(a => a.screenshot_data);
|
|
3565
|
-
console.log('[SCREENSHOT-DEBUG] background.stopWorkflowRecording - Payload actions WITH screenshot_data:', payloadActionsWithScreenshots.length);
|
|
3826
|
+
// console.log('[SCREENSHOT-DEBUG] background.stopWorkflowRecording - Payload actions WITH screenshot_data:', payloadActionsWithScreenshots.length);
|
|
3566
3827
|
|
|
3567
3828
|
payloadData.actions.forEach((action, index) => {
|
|
3568
|
-
console.log(`[SCREENSHOT-DEBUG] background.stopWorkflowRecording - Payload Action ${index}:`, {
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
});
|
|
3829
|
+
// console.log(`[SCREENSHOT-DEBUG] background.stopWorkflowRecording - Payload Action ${index}:`, {
|
|
3830
|
+
// type: action.type,
|
|
3831
|
+
// hasScreenshotData: !!action.screenshot_data,
|
|
3832
|
+
// screenshotDataLength: action.screenshot_data?.length,
|
|
3833
|
+
// screenshotPreview: action.screenshot_data?.substring(0, 50)
|
|
3834
|
+
// });
|
|
3574
3835
|
});
|
|
3575
3836
|
|
|
3576
3837
|
const response = await fetch(`http://localhost:${port}/chromedebug/workflow-recording`, {
|
|
@@ -3583,31 +3844,26 @@ async function stopWorkflowRecording(tabId) {
|
|
|
3583
3844
|
|
|
3584
3845
|
if (response.ok) {
|
|
3585
3846
|
serverResult = await response.json();
|
|
3586
|
-
console.log(`[Workflow] Successfully saved to server on port ${port}`, serverResult);
|
|
3587
|
-
|
|
3588
|
-
// STEP 2: Track usage AFTER successful workflow recording completion (same pattern as stopRecording)
|
|
3589
|
-
if (userId) {
|
|
3590
|
-
console.log('[License] Tracking usage after workflow recording completion...');
|
|
3591
|
-
const usageResult = await LicenseHelper.trackUsageAfterRecording(userId);
|
|
3592
|
-
console.log('[License] Usage tracking result:', usageResult);
|
|
3593
|
-
}
|
|
3594
|
-
|
|
3847
|
+
// console.log(`[Workflow] Successfully saved to server on port ${port}`, serverResult);
|
|
3595
3848
|
break;
|
|
3596
3849
|
} else {
|
|
3597
|
-
console.log(`[Workflow] Failed on port ${port}: ${response.status} ${response.statusText}`);
|
|
3850
|
+
// console.log(`[Workflow] Failed on port ${port}: ${response.status} ${response.statusText}`);
|
|
3598
3851
|
const errorText = await response.text();
|
|
3599
|
-
console.log(`[Workflow] Error response:`, errorText);
|
|
3852
|
+
// console.log(`[Workflow] Error response:`, errorText);
|
|
3600
3853
|
}
|
|
3601
3854
|
} catch (error) {
|
|
3602
|
-
console.log(`Failed to save to server on port ${port}:`, error.message);
|
|
3855
|
+
// console.log(`Failed to save to server on port ${port}:`, error.message);
|
|
3603
3856
|
}
|
|
3604
3857
|
}
|
|
3605
3858
|
|
|
3606
3859
|
if (serverResult) {
|
|
3860
|
+
// Clean up currentWorkflowId after successful save
|
|
3861
|
+
await chrome.storage.local.remove(['currentWorkflowId']);
|
|
3862
|
+
|
|
3607
3863
|
return {
|
|
3608
3864
|
success: true,
|
|
3609
3865
|
workflow: {
|
|
3610
|
-
sessionId: sessionId,
|
|
3866
|
+
sessionId: serverResult.workflowId || serverResult.sessionId || sessionId,
|
|
3611
3867
|
url: tab.url,
|
|
3612
3868
|
title: tab.title,
|
|
3613
3869
|
actions: workflow,
|
|
@@ -3617,17 +3873,36 @@ async function stopWorkflowRecording(tabId) {
|
|
|
3617
3873
|
serverResult: serverResult
|
|
3618
3874
|
};
|
|
3619
3875
|
} else {
|
|
3876
|
+
// Server not available - clean up but return local sessionId
|
|
3877
|
+
await chrome.storage.local.remove(['currentWorkflowId']);
|
|
3878
|
+
|
|
3620
3879
|
return {
|
|
3621
3880
|
success: true,
|
|
3622
|
-
workflow:
|
|
3881
|
+
workflow: {
|
|
3882
|
+
sessionId: sessionId,
|
|
3883
|
+
url: tab.url,
|
|
3884
|
+
title: tab.title,
|
|
3885
|
+
actions: workflow,
|
|
3886
|
+
logs: []
|
|
3887
|
+
},
|
|
3623
3888
|
savedToServer: false
|
|
3624
3889
|
};
|
|
3625
3890
|
}
|
|
3626
3891
|
} catch (error) {
|
|
3627
3892
|
console.error('Error saving workflow:', error);
|
|
3893
|
+
// Clean up on error too
|
|
3894
|
+
await chrome.storage.local.remove(['currentWorkflowId']);
|
|
3895
|
+
|
|
3896
|
+
// Return structured response even on error
|
|
3628
3897
|
return {
|
|
3629
3898
|
success: true,
|
|
3630
|
-
workflow:
|
|
3899
|
+
workflow: {
|
|
3900
|
+
sessionId: sessionId,
|
|
3901
|
+
url: tab?.url || 'unknown',
|
|
3902
|
+
title: tab?.title || 'Untitled',
|
|
3903
|
+
actions: workflow,
|
|
3904
|
+
logs: []
|
|
3905
|
+
},
|
|
3631
3906
|
savedToServer: false,
|
|
3632
3907
|
error: error.message
|
|
3633
3908
|
};
|
|
@@ -3638,6 +3913,13 @@ async function stopWorkflowRecording(tabId) {
|
|
|
3638
3913
|
}
|
|
3639
3914
|
}
|
|
3640
3915
|
|
|
3916
|
+
// CRITICAL FIX: Stop workflow console log capture and restore original methods
|
|
3917
|
+
// REFACTORED: Now uses shared console-interception-library.js via proper namespace
|
|
3918
|
+
async function stopCapturingWorkflowLogs(tabId) {
|
|
3919
|
+
// console.log('[Workflow Recording] Stopping console interception using shared library');
|
|
3920
|
+
return await self.ConsoleInterceptionLibrary.stopConsoleInterception(tabId, WORKFLOW_RECORDING_CONSOLE_CONFIG);
|
|
3921
|
+
}
|
|
3922
|
+
|
|
3641
3923
|
// Delete recording from server
|
|
3642
3924
|
async function deleteRecordingFromServer(recordingId, sendResponse) {
|
|
3643
3925
|
try {
|
|
@@ -3673,7 +3955,7 @@ async function deleteRecordingFromServer(recordingId, sendResponse) {
|
|
|
3673
3955
|
|
|
3674
3956
|
if (deleteResult.success) {
|
|
3675
3957
|
deleted = true;
|
|
3676
|
-
console.log('Recording deleted successfully');
|
|
3958
|
+
// console.log('Recording deleted successfully');
|
|
3677
3959
|
sendResponse({ success: true });
|
|
3678
3960
|
return;
|
|
3679
3961
|
} else {
|
|
@@ -3695,15 +3977,30 @@ async function retryPendingFrames(sessionId) {
|
|
|
3695
3977
|
const pending = pendingFrameQueue.get(sessionId);
|
|
3696
3978
|
if (!pending || pending.length === 0) return;
|
|
3697
3979
|
|
|
3698
|
-
console.log(`[FrameQueue] Retrying ${pending.length} pending frame batches for session ${sessionId}`);
|
|
3980
|
+
// console.log(`[FrameQueue] Retrying ${pending.length} pending frame batches for session ${sessionId}`);
|
|
3981
|
+
|
|
3982
|
+
// Check if this is still the active recording session
|
|
3983
|
+
if (currentSession?.sessionId !== sessionId) {
|
|
3984
|
+
// console.log(`[FrameQueue] Session ${sessionId} is no longer active, clearing pending frames`);
|
|
3985
|
+
pendingFrameQueue.delete(sessionId);
|
|
3986
|
+
return;
|
|
3987
|
+
}
|
|
3699
3988
|
|
|
3700
3989
|
// Validate session now
|
|
3701
3990
|
if (sessionManager) {
|
|
3702
3991
|
const validationResult = await sessionManager.isSessionValid(sessionId);
|
|
3703
3992
|
if (!validationResult.valid) {
|
|
3704
3993
|
console.warn(`[FrameQueue] Session still invalid during retry: ${validationResult.reason}`);
|
|
3705
|
-
// Try again after another delay
|
|
3706
|
-
|
|
3994
|
+
// Try again after another delay (max 3 retries)
|
|
3995
|
+
const retryCount = pending[0]?.retryCount || 0;
|
|
3996
|
+
if (retryCount < 3) {
|
|
3997
|
+
// Mark all batches with retry count
|
|
3998
|
+
pending.forEach(batch => batch.retryCount = retryCount + 1);
|
|
3999
|
+
setTimeout(() => retryPendingFrames(sessionId), 200);
|
|
4000
|
+
} else {
|
|
4001
|
+
console.error(`[FrameQueue] Max retries (3) exceeded for session ${sessionId}, clearing queue`);
|
|
4002
|
+
pendingFrameQueue.delete(sessionId);
|
|
4003
|
+
}
|
|
3707
4004
|
return;
|
|
3708
4005
|
}
|
|
3709
4006
|
}
|
|
@@ -3716,12 +4013,12 @@ async function retryPendingFrames(sessionId) {
|
|
|
3716
4013
|
await handleFrameBatch({ sessionId, frames }, true); // skipValidation=true
|
|
3717
4014
|
}
|
|
3718
4015
|
|
|
3719
|
-
console.log(`[FrameQueue] Successfully processed ${batches.length} pending batches`);
|
|
4016
|
+
// console.log(`[FrameQueue] Successfully processed ${batches.length} pending batches`);
|
|
3720
4017
|
}
|
|
3721
4018
|
|
|
3722
4019
|
// Handle frame batches from frame capture
|
|
3723
4020
|
async function handleFrameBatch(batchData, skipValidation = false) {
|
|
3724
|
-
console.log(`Processing frame batch for session ${batchData.sessionId}`);
|
|
4021
|
+
// console.log(`Processing frame batch for session ${batchData.sessionId}`);
|
|
3725
4022
|
|
|
3726
4023
|
try {
|
|
3727
4024
|
const sessionId = batchData.sessionId;
|
|
@@ -3775,7 +4072,7 @@ async function handleFrameBatch(batchData, skipValidation = false) {
|
|
|
3775
4072
|
frameCounter.set(sessionId, currentFrameIndex);
|
|
3776
4073
|
}
|
|
3777
4074
|
|
|
3778
|
-
console.log(`Assigning frame indices ${frameCounter.get(sessionId) - indexedFrames.length} to ${currentFrameIndex - 1}`);
|
|
4075
|
+
// console.log(`Assigning frame indices ${frameCounter.get(sessionId) - indexedFrames.length} to ${currentFrameIndex - 1}`);
|
|
3779
4076
|
|
|
3780
4077
|
// Find available server port using settings
|
|
3781
4078
|
const ports = await getServerPorts();
|
|
@@ -3801,7 +4098,7 @@ async function handleFrameBatch(batchData, skipValidation = false) {
|
|
|
3801
4098
|
|
|
3802
4099
|
if (uploadResponse.ok) {
|
|
3803
4100
|
const result = await uploadResponse.json();
|
|
3804
|
-
console.log(`Frame batch uploaded successfully to port ${port}:`, result);
|
|
4101
|
+
// console.log(`Frame batch uploaded successfully to port ${port}:`, result);
|
|
3805
4102
|
recordingServerPort = port; // Store the successful port
|
|
3806
4103
|
uploadSuccess = true;
|
|
3807
4104
|
|
|
@@ -3817,8 +4114,20 @@ async function handleFrameBatch(batchData, skipValidation = false) {
|
|
|
3817
4114
|
// Append new frames
|
|
3818
4115
|
sessionData.frames = sessionData.frames.concat(batchData.frames);
|
|
3819
4116
|
await chrome.storage.local.set({ [storageKey]: sessionData });
|
|
3820
|
-
console.log(`Saved ${batchData.frames.length} frames to local storage, total: ${sessionData.frames.length}`);
|
|
3821
|
-
|
|
4117
|
+
// console.log(`Saved ${batchData.frames.length} frames to local storage, total: ${sessionData.frames.length}`);
|
|
4118
|
+
|
|
4119
|
+
// Send frame count update to content script
|
|
4120
|
+
if (recordingTabId) {
|
|
4121
|
+
try {
|
|
4122
|
+
await chrome.tabs.sendMessage(recordingTabId, {
|
|
4123
|
+
action: 'updateFrameCount',
|
|
4124
|
+
frameCount: sessionData.frames.length
|
|
4125
|
+
});
|
|
4126
|
+
} catch (e) {
|
|
4127
|
+
// Tab might be closed, ignore
|
|
4128
|
+
}
|
|
4129
|
+
}
|
|
4130
|
+
|
|
3822
4131
|
break;
|
|
3823
4132
|
} else {
|
|
3824
4133
|
const errorText = await uploadResponse.text();
|
|
@@ -3827,16 +4136,16 @@ async function handleFrameBatch(batchData, skipValidation = false) {
|
|
|
3827
4136
|
}
|
|
3828
4137
|
} catch (e) {
|
|
3829
4138
|
lastError = e.message;
|
|
3830
|
-
console.log(`Failed to upload frame batch to port ${port}:`, e.message);
|
|
4139
|
+
// console.log(`Failed to upload frame batch to port ${port}:`, e.message);
|
|
3831
4140
|
}
|
|
3832
4141
|
}
|
|
3833
4142
|
|
|
3834
4143
|
if (!uploadSuccess) {
|
|
3835
|
-
console.log(`No server available - checking if browser-only mode`);
|
|
4144
|
+
// console.log(`No server available - checking if browser-only mode`);
|
|
3836
4145
|
|
|
3837
4146
|
// If in browser-only mode, store frames in IndexedDB
|
|
3838
4147
|
if (serverMode === 'browser-only' && browserRecordingManager) {
|
|
3839
|
-
console.log(`[Browser-Only] Storing ${indexedFrames.length} frames in IndexedDB`);
|
|
4148
|
+
// console.log(`[Browser-Only] Storing ${indexedFrames.length} frames in IndexedDB`);
|
|
3840
4149
|
// Use the browser recording session ID, not the frame capture session ID
|
|
3841
4150
|
const recordingSessionId = currentRecordingSessionId || batchData.sessionId;
|
|
3842
4151
|
for (const frame of indexedFrames) {
|
|
@@ -3853,7 +4162,7 @@ async function handleFrameBatch(batchData, skipValidation = false) {
|
|
|
3853
4162
|
absoluteTimestamp: frame.absoluteTimestamp // Don't use Date.now() fallback - preserve actual capture time
|
|
3854
4163
|
});
|
|
3855
4164
|
}
|
|
3856
|
-
console.log(`[Browser-Only] Frames stored successfully in IndexedDB for session ${recordingSessionId}`);
|
|
4165
|
+
// console.log(`[Browser-Only] Frames stored successfully in IndexedDB for session ${recordingSessionId}`);
|
|
3857
4166
|
} else {
|
|
3858
4167
|
// Server mode but no server available - show error
|
|
3859
4168
|
console.error(`CRITICAL: Failed to upload frame batch to any Chrome Debug server port. Last error: ${lastError}`);
|
|
@@ -3893,7 +4202,7 @@ async function getServerPorts() {
|
|
|
3893
4202
|
// Try to discover the active server port
|
|
3894
4203
|
const discoveredPort = await discoverActiveServerPort();
|
|
3895
4204
|
if (discoveredPort) {
|
|
3896
|
-
console.log(`Discovered Chrome Debug server on port ${discoveredPort}`);
|
|
4205
|
+
// console.log(`Discovered Chrome Debug server on port ${discoveredPort}`);
|
|
3897
4206
|
return [discoveredPort, ...defaultPorts.filter(p => p !== discoveredPort)];
|
|
3898
4207
|
}
|
|
3899
4208
|
|
|
@@ -3931,7 +4240,7 @@ async function discoverActiveServerPort() {
|
|
|
3931
4240
|
|
|
3932
4241
|
// Handle frame capture session completion
|
|
3933
4242
|
async function handleFrameCaptureComplete(sessionData) {
|
|
3934
|
-
console.log(`Frame capture session ${sessionData.sessionId} completed with ${sessionData.totalFrames} frames`);
|
|
4243
|
+
// console.log(`Frame capture session ${sessionData.sessionId} completed with ${sessionData.totalFrames} frames`);
|
|
3935
4244
|
|
|
3936
4245
|
try {
|
|
3937
4246
|
// Store the tab ID and session ID before clearing
|
|
@@ -3949,7 +4258,7 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
3949
4258
|
let waitedTime = 0;
|
|
3950
4259
|
let flushCompleted = false;
|
|
3951
4260
|
|
|
3952
|
-
console.log('[Background] Waiting for LogBuffer flush completion...');
|
|
4261
|
+
// console.log('[Background] Waiting for LogBuffer flush completion...');
|
|
3953
4262
|
|
|
3954
4263
|
// Poll for flush completion status
|
|
3955
4264
|
while (waitedTime < maxWaitTime && !flushCompleted) {
|
|
@@ -3963,7 +4272,7 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
3963
4272
|
}
|
|
3964
4273
|
|
|
3965
4274
|
if (flushCompleted) {
|
|
3966
|
-
console.log(`[Background] LogBuffer flush completed after ${waitedTime}ms`);
|
|
4275
|
+
// console.log(`[Background] LogBuffer flush completed after ${waitedTime}ms`);
|
|
3967
4276
|
} else {
|
|
3968
4277
|
console.warn(`[Background] LogBuffer flush verification timed out after ${waitedTime}ms`);
|
|
3969
4278
|
}
|
|
@@ -3971,9 +4280,9 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
3971
4280
|
// Retrieve buffered logs for this tab
|
|
3972
4281
|
const logsResult = await chrome.storage.session.get(String(tabIdToNotify));
|
|
3973
4282
|
bufferedLogs = logsResult[tabIdToNotify] || [];
|
|
3974
|
-
console.log(`Retrieved ${bufferedLogs.length} buffered logs for post-processing`);
|
|
4283
|
+
// console.log(`Retrieved ${bufferedLogs.length} buffered logs for post-processing`);
|
|
3975
4284
|
if (bufferedLogs.length > 0) {
|
|
3976
|
-
console.log('First log:', bufferedLogs[0]);
|
|
4285
|
+
// console.log('First log:', bufferedLogs[0]);
|
|
3977
4286
|
}
|
|
3978
4287
|
|
|
3979
4288
|
// Clean up flush completion marker
|
|
@@ -3984,7 +4293,7 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
3984
4293
|
// Fallback: Try to read logs anyway
|
|
3985
4294
|
const logsResult = await chrome.storage.session.get(String(tabIdToNotify));
|
|
3986
4295
|
bufferedLogs = logsResult[tabIdToNotify] || [];
|
|
3987
|
-
console.log(`Fallback: Retrieved ${bufferedLogs.length} buffered logs`);
|
|
4296
|
+
// console.log(`Fallback: Retrieved ${bufferedLogs.length} buffered logs`);
|
|
3988
4297
|
}
|
|
3989
4298
|
|
|
3990
4299
|
// Clear the buffered logs
|
|
@@ -3993,7 +4302,7 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
3993
4302
|
// Send logs to server for association with frames with retry logic
|
|
3994
4303
|
// Skip server communication in browser-only mode (logs already in IndexedDB)
|
|
3995
4304
|
if (bufferedLogs.length > 0 && serverMode !== 'browser-only') {
|
|
3996
|
-
console.log(`[Server Mode] Associating ${bufferedLogs.length} logs with server...`);
|
|
4305
|
+
// console.log(`[Server Mode] Associating ${bufferedLogs.length} logs with server...`);
|
|
3997
4306
|
|
|
3998
4307
|
// Retry logic to handle race conditions
|
|
3999
4308
|
const maxRetries = 3;
|
|
@@ -4001,15 +4310,15 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
4001
4310
|
let success = false;
|
|
4002
4311
|
let lastErrorDetails = null;
|
|
4003
4312
|
|
|
4004
|
-
console.log(`[LOG-ASSOC-DEBUG] ========== STARTING LOG ASSOCIATION ==========`);
|
|
4005
|
-
console.log(`[LOG-ASSOC-DEBUG] Session ID: ${sessionIdToUse}`);
|
|
4006
|
-
console.log(`[LOG-ASSOC-DEBUG] Number of logs to associate: ${bufferedLogs.length}`);
|
|
4007
|
-
console.log(`[LOG-ASSOC-DEBUG] Tab ID: ${tabIdToNotify}`);
|
|
4313
|
+
// console.log(`[LOG-ASSOC-DEBUG] ========== STARTING LOG ASSOCIATION ==========`);
|
|
4314
|
+
// console.log(`[LOG-ASSOC-DEBUG] Session ID: ${sessionIdToUse}`);
|
|
4315
|
+
// console.log(`[LOG-ASSOC-DEBUG] Number of logs to associate: ${bufferedLogs.length}`);
|
|
4316
|
+
// console.log(`[LOG-ASSOC-DEBUG] Tab ID: ${tabIdToNotify}`);
|
|
4008
4317
|
|
|
4009
4318
|
// PRE-FLIGHT CHECK: Verify frames exist in database before attempting log association
|
|
4010
4319
|
try {
|
|
4011
4320
|
const ports = await getServerPorts();
|
|
4012
|
-
console.log(`[LOG-ASSOC-DEBUG] PRE-FLIGHT: Checking if frames exist for session ${sessionIdToUse}...`);
|
|
4321
|
+
// console.log(`[LOG-ASSOC-DEBUG] PRE-FLIGHT: Checking if frames exist for session ${sessionIdToUse}...`);
|
|
4013
4322
|
for (const port of ports) {
|
|
4014
4323
|
try {
|
|
4015
4324
|
const checkResponse = await fetch(`http://localhost:${port}/chromedebug/frame-session/${sessionIdToUse}`, {
|
|
@@ -4019,7 +4328,7 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
4019
4328
|
if (checkResponse.ok) {
|
|
4020
4329
|
const sessionData = await checkResponse.json();
|
|
4021
4330
|
const frameCount = sessionData.frames?.length || 0;
|
|
4022
|
-
console.log(`[LOG-ASSOC-DEBUG] PRE-FLIGHT: Found ${frameCount} frames on port ${port}`);
|
|
4331
|
+
// console.log(`[LOG-ASSOC-DEBUG] PRE-FLIGHT: Found ${frameCount} frames on port ${port}`);
|
|
4023
4332
|
if (frameCount === 0) {
|
|
4024
4333
|
console.warn(`[LOG-ASSOC-DEBUG] ⚠️ WARNING: Session exists but has 0 frames! This will cause log association to fail.`);
|
|
4025
4334
|
console.warn(`[LOG-ASSOC-DEBUG] Session data:`, sessionData);
|
|
@@ -4027,7 +4336,7 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
4027
4336
|
break;
|
|
4028
4337
|
}
|
|
4029
4338
|
} catch (e) {
|
|
4030
|
-
console.log(`[LOG-ASSOC-DEBUG] PRE-FLIGHT: Could not check port ${port}:`, e.message);
|
|
4339
|
+
// console.log(`[LOG-ASSOC-DEBUG] PRE-FLIGHT: Could not check port ${port}:`, e.message);
|
|
4031
4340
|
}
|
|
4032
4341
|
}
|
|
4033
4342
|
} catch (preflightError) {
|
|
@@ -4037,22 +4346,22 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
4037
4346
|
while (retryCount < maxRetries && !success) {
|
|
4038
4347
|
try {
|
|
4039
4348
|
const ports = await getServerPorts();
|
|
4040
|
-
console.log(`[LOG-ASSOC-DEBUG] Attempt ${retryCount + 1}/${maxRetries}: Trying ports:`, ports);
|
|
4349
|
+
// console.log(`[LOG-ASSOC-DEBUG] Attempt ${retryCount + 1}/${maxRetries}: Trying ports:`, ports);
|
|
4041
4350
|
|
|
4042
4351
|
for (const port of ports) {
|
|
4043
4352
|
try {
|
|
4044
|
-
console.log(`[LOG-ASSOC-DEBUG] Attempting port ${port}...`);
|
|
4353
|
+
// console.log(`[LOG-ASSOC-DEBUG] Attempting port ${port}...`);
|
|
4045
4354
|
const requestBody = {
|
|
4046
4355
|
sessionId: sessionIdToUse,
|
|
4047
4356
|
logs: bufferedLogs
|
|
4048
4357
|
};
|
|
4049
|
-
console.log(`[LOG-ASSOC-DEBUG] Request payload: sessionId=${sessionIdToUse}, logs.length=${bufferedLogs.length}`);
|
|
4358
|
+
// console.log(`[LOG-ASSOC-DEBUG] Request payload: sessionId=${sessionIdToUse}, logs.length=${bufferedLogs.length}`);
|
|
4050
4359
|
if (bufferedLogs.length > 0) {
|
|
4051
|
-
console.log(`[LOG-ASSOC-DEBUG] First log sample:`, {
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
});
|
|
4360
|
+
// console.log(`[LOG-ASSOC-DEBUG] First log sample:`, {
|
|
4361
|
+
// timestamp: bufferedLogs[0].timestamp,
|
|
4362
|
+
// level: bufferedLogs[0].level,
|
|
4363
|
+
// message: bufferedLogs[0].message?.substring(0, 100)
|
|
4364
|
+
// });
|
|
4056
4365
|
}
|
|
4057
4366
|
|
|
4058
4367
|
const response = await fetch(`http://localhost:${port}/chromedebug/associate-logs`, {
|
|
@@ -4063,12 +4372,12 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
4063
4372
|
body: JSON.stringify(requestBody)
|
|
4064
4373
|
});
|
|
4065
4374
|
|
|
4066
|
-
console.log(`[LOG-ASSOC-DEBUG] Port ${port} response status: ${response.status} ${response.statusText}`);
|
|
4375
|
+
// console.log(`[LOG-ASSOC-DEBUG] Port ${port} response status: ${response.status} ${response.statusText}`);
|
|
4067
4376
|
|
|
4068
4377
|
if (response.ok) {
|
|
4069
4378
|
const result = await response.json();
|
|
4070
|
-
console.log(`[LOG-ASSOC-DEBUG] ✅ SUCCESS on port ${port}:`, result);
|
|
4071
|
-
console.log(`Logs associated successfully on port ${port}:`, result);
|
|
4379
|
+
// console.log(`[LOG-ASSOC-DEBUG] ✅ SUCCESS on port ${port}:`, result);
|
|
4380
|
+
// console.log(`Logs associated successfully on port ${port}:`, result);
|
|
4072
4381
|
success = true;
|
|
4073
4382
|
|
|
4074
4383
|
// Fetch the updated session data with logs from server
|
|
@@ -4076,14 +4385,14 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
4076
4385
|
const sessionResponse = await fetch(`http://localhost:${port}/chromedebug/frame-session/${sessionIdToUse}`);
|
|
4077
4386
|
if (sessionResponse.ok) {
|
|
4078
4387
|
const updatedSession = await sessionResponse.json();
|
|
4079
|
-
console.log(`[LOG-ASSOC-DEBUG] Fetched updated session: ${updatedSession.frames?.length || 0} frames`);
|
|
4080
|
-
console.log(`Fetched updated session with ${updatedSession.frames ? updatedSession.frames.length : 0} frames`);
|
|
4388
|
+
// console.log(`[LOG-ASSOC-DEBUG] Fetched updated session: ${updatedSession.frames?.length || 0} frames`);
|
|
4389
|
+
// console.log(`Fetched updated session with ${updatedSession.frames ? updatedSession.frames.length : 0} frames`);
|
|
4081
4390
|
|
|
4082
4391
|
// Update Chrome local storage with the frames that now have logs
|
|
4083
4392
|
const storageKey = sessionIdToUse;
|
|
4084
4393
|
await chrome.storage.local.set({ [storageKey]: updatedSession });
|
|
4085
|
-
console.log('[LOG-ASSOC-DEBUG] Updated local storage with frames containing logs');
|
|
4086
|
-
console.log('Updated local storage with frames containing logs');
|
|
4394
|
+
// console.log('[LOG-ASSOC-DEBUG] Updated local storage with frames containing logs');
|
|
4395
|
+
// console.log('Updated local storage with frames containing logs');
|
|
4087
4396
|
} else {
|
|
4088
4397
|
console.error(`[LOG-ASSOC-DEBUG] Failed to fetch updated session: ${sessionResponse.status}`);
|
|
4089
4398
|
console.error('Failed to fetch updated session data');
|
|
@@ -4119,15 +4428,15 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
4119
4428
|
attempt: retryCount + 1
|
|
4120
4429
|
};
|
|
4121
4430
|
console.error(`[LOG-ASSOC-DEBUG] ❌ Port ${port} exception:`, e);
|
|
4122
|
-
console.log(`Failed to associate logs on port ${port}:`, e.message);
|
|
4431
|
+
// console.log(`Failed to associate logs on port ${port}:`, e.message);
|
|
4123
4432
|
}
|
|
4124
4433
|
}
|
|
4125
4434
|
|
|
4126
4435
|
if (!success) {
|
|
4127
4436
|
retryCount++;
|
|
4128
4437
|
if (retryCount < maxRetries) {
|
|
4129
|
-
console.log(`[LOG-ASSOC-DEBUG] Retrying in 1 second (attempt ${retryCount + 1}/${maxRetries})...`);
|
|
4130
|
-
console.log(`Retrying log association (attempt ${retryCount + 1}/${maxRetries})...`);
|
|
4438
|
+
// console.log(`[LOG-ASSOC-DEBUG] Retrying in 1 second (attempt ${retryCount + 1}/${maxRetries})...`);
|
|
4439
|
+
// console.log(`Retrying log association (attempt ${retryCount + 1}/${maxRetries})...`);
|
|
4131
4440
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
4132
4441
|
}
|
|
4133
4442
|
}
|
|
@@ -4141,8 +4450,8 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
4141
4450
|
}
|
|
4142
4451
|
}
|
|
4143
4452
|
|
|
4144
|
-
console.log(`[LOG-ASSOC-DEBUG] ========== LOG ASSOCIATION COMPLETE ==========`);
|
|
4145
|
-
console.log(`[LOG-ASSOC-DEBUG] Success: ${success}`);
|
|
4453
|
+
// console.log(`[LOG-ASSOC-DEBUG] ========== LOG ASSOCIATION COMPLETE ==========`);
|
|
4454
|
+
// console.log(`[LOG-ASSOC-DEBUG] Success: ${success}`);
|
|
4146
4455
|
if (!success && lastErrorDetails) {
|
|
4147
4456
|
console.error(`[LOG-ASSOC-DEBUG] Last error details:`, lastErrorDetails);
|
|
4148
4457
|
}
|
|
@@ -4173,12 +4482,12 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
4173
4482
|
}
|
|
4174
4483
|
} else if (bufferedLogs.length > 0 && serverMode === 'browser-only') {
|
|
4175
4484
|
// Browser-only mode: Associate logs with frames in IndexedDB
|
|
4176
|
-
console.log(`[Browser-Only] Associating ${bufferedLogs.length} logs with frames in IndexedDB...`);
|
|
4485
|
+
// console.log(`[Browser-Only] Associating ${bufferedLogs.length} logs with frames in IndexedDB...`);
|
|
4177
4486
|
|
|
4178
4487
|
try {
|
|
4179
4488
|
// Get all frames for this session from IndexedDB
|
|
4180
4489
|
const frames = await dataBuffer.getBrowserFrames(sessionIdToUse);
|
|
4181
|
-
console.log(`[Browser-Only] Retrieved ${frames.length} frames from IndexedDB for log association`);
|
|
4490
|
+
// console.log(`[Browser-Only] Retrieved ${frames.length} frames from IndexedDB for log association`);
|
|
4182
4491
|
|
|
4183
4492
|
if (frames.length === 0) {
|
|
4184
4493
|
console.warn('[Browser-Only] No frames found - logs cannot be associated');
|
|
@@ -4222,7 +4531,7 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
4222
4531
|
}
|
|
4223
4532
|
}
|
|
4224
4533
|
|
|
4225
|
-
console.log(`[Browser-Only] ✓ Associated logs with ${updatedCount} frames in IndexedDB`);
|
|
4534
|
+
// console.log(`[Browser-Only] ✓ Associated logs with ${updatedCount} frames in IndexedDB`);
|
|
4226
4535
|
}
|
|
4227
4536
|
} catch (error) {
|
|
4228
4537
|
console.error('[Browser-Only] Failed to associate logs with frames:', error);
|
|
@@ -4248,18 +4557,18 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
4248
4557
|
// Save screen interactions to database
|
|
4249
4558
|
// Skip server communication in browser-only mode
|
|
4250
4559
|
if (screenInteractions.length > 0 && serverMode !== 'browser-only') {
|
|
4251
|
-
console.log(`[Server Mode] Saving ${screenInteractions.length} screen interactions to database`);
|
|
4560
|
+
// console.log(`[Server Mode] Saving ${screenInteractions.length} screen interactions to database`);
|
|
4252
4561
|
|
|
4253
4562
|
// Use comprehensive error handling for screen interactions save
|
|
4254
4563
|
const saveScreenInteractions = async (data, context) => {
|
|
4255
4564
|
// Diagnostic logging for screen interactions save
|
|
4256
|
-
console.log('[ScreenInteractions] Preparing to save interactions:', {
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
});
|
|
4565
|
+
// console.log('[ScreenInteractions] Preparing to save interactions:', {
|
|
4566
|
+
// sessionId: context.sessionId,
|
|
4567
|
+
// dataType: typeof data,
|
|
4568
|
+
// isArray: Array.isArray(data),
|
|
4569
|
+
// interactionCount: Array.isArray(data) ? data.length : 'N/A',
|
|
4570
|
+
// hasComplexData: Array.isArray(data) && data.some(i => i.element_html && i.element_html.length > 100)
|
|
4571
|
+
// });
|
|
4263
4572
|
|
|
4264
4573
|
let lastError = null;
|
|
4265
4574
|
for (const port of CONFIG_PORTS) {
|
|
@@ -4269,7 +4578,7 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
4269
4578
|
try {
|
|
4270
4579
|
// data is already { interactions: screenInteractions } from caller
|
|
4271
4580
|
serializedBody = JSON.stringify(data);
|
|
4272
|
-
console.log('[ScreenInteractions] JSON serialization successful, length:', serializedBody.length);
|
|
4581
|
+
// console.log('[ScreenInteractions] JSON serialization successful, length:', serializedBody.length);
|
|
4273
4582
|
} catch (serError) {
|
|
4274
4583
|
console.error('[ScreenInteractions] JSON serialization failed:', serError);
|
|
4275
4584
|
throw new Error(`JSON serialization failed: ${serError.message}`);
|
|
@@ -4286,7 +4595,7 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
4286
4595
|
if (response.ok) {
|
|
4287
4596
|
// Only log if we had to try multiple ports
|
|
4288
4597
|
if (port !== CONFIG_PORTS[0]) {
|
|
4289
|
-
console.log(`[ScreenInteractions] Connected to server on port ${port}`);
|
|
4598
|
+
// console.log(`[ScreenInteractions] Connected to server on port ${port}`);
|
|
4290
4599
|
}
|
|
4291
4600
|
return { success: true, result: { saved: true } };
|
|
4292
4601
|
} else {
|
|
@@ -4310,13 +4619,13 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
4310
4619
|
);
|
|
4311
4620
|
|
|
4312
4621
|
if (saveResult.success) {
|
|
4313
|
-
console.log('Screen interactions saved successfully with comprehensive error handling');
|
|
4622
|
+
// console.log('Screen interactions saved successfully with comprehensive error handling');
|
|
4314
4623
|
} else {
|
|
4315
4624
|
console.error('[ErrorHandling] Failed to save screen interactions after comprehensive error handling');
|
|
4316
4625
|
}
|
|
4317
4626
|
} else if (screenInteractions.length > 0 && serverMode === 'browser-only') {
|
|
4318
4627
|
// Browser-only mode: screen interactions not needed
|
|
4319
|
-
console.log(`[Browser-Only] ✓ Skipping screen interactions save (browser-only mode)`);
|
|
4628
|
+
// console.log(`[Browser-Only] ✓ Skipping screen interactions save (browser-only mode)`);
|
|
4320
4629
|
}
|
|
4321
4630
|
|
|
4322
4631
|
// Stop session via session manager if available
|
|
@@ -4324,7 +4633,7 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
4324
4633
|
try {
|
|
4325
4634
|
const stopResult = await sessionManager.stopRecording(currentSession.sessionId, currentOwnerId);
|
|
4326
4635
|
if (stopResult.success) {
|
|
4327
|
-
console.log(`Session stopped via session manager. Duration: ${stopResult.sessionDuration}ms, Frames: ${stopResult.frameCount}`);
|
|
4636
|
+
// console.log(`Session stopped via session manager. Duration: ${stopResult.sessionDuration}ms, Frames: ${stopResult.frameCount}`);
|
|
4328
4637
|
} else {
|
|
4329
4638
|
console.warn('Failed to stop session via session manager:', stopResult.error?.message);
|
|
4330
4639
|
}
|
|
@@ -4376,7 +4685,7 @@ async function handleFrameCaptureComplete(sessionData) {
|
|
|
4376
4685
|
serverPort: recordingServerPort
|
|
4377
4686
|
}).catch(() => {});
|
|
4378
4687
|
} else {
|
|
4379
|
-
console.log('[Browser-Only] Skipping frameSessionComplete message - recording is in IndexedDB');
|
|
4688
|
+
// console.log('[Browser-Only] Skipping frameSessionComplete message - recording is in IndexedDB');
|
|
4380
4689
|
}
|
|
4381
4690
|
|
|
4382
4691
|
// Notify content script to hide recording indicator
|