@dynamicu/chromedebug-mcp 2.6.7 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CLAUDE.md +1 -1
  2. package/README.md +1 -1
  3. package/chrome-extension/background.js +611 -505
  4. package/chrome-extension/browser-recording-manager.js +1 -1
  5. package/chrome-extension/chrome-debug-logger.js +168 -0
  6. package/chrome-extension/console-interception-library.js +430 -0
  7. package/chrome-extension/content.css +16 -16
  8. package/chrome-extension/content.js +458 -126
  9. package/chrome-extension/extension-config.js +1 -1
  10. package/chrome-extension/license-helper.js +26 -0
  11. package/chrome-extension/manifest.free.json +0 -3
  12. package/chrome-extension/options.js +1 -1
  13. package/chrome-extension/popup.html +221 -191
  14. package/chrome-extension/popup.js +88 -379
  15. package/chrome-extension/pro/enhanced-capture.js +406 -0
  16. package/chrome-extension/pro/frame-editor.html +410 -0
  17. package/chrome-extension/pro/frame-editor.js +1496 -0
  18. package/chrome-extension/pro/function-tracker.js +843 -0
  19. package/chrome-extension/pro/jszip.min.js +13 -0
  20. package/dist/chromedebug-extension-free.zip +0 -0
  21. package/package.json +3 -1
  22. package/scripts/webpack.config.free.cjs +8 -8
  23. package/scripts/webpack.config.pro.cjs +2 -0
  24. package/src/cli.js +2 -2
  25. package/src/database.js +55 -7
  26. package/src/index.js +9 -6
  27. package/src/mcp/server.js +2 -2
  28. package/src/services/process-manager.js +10 -6
  29. package/src/services/process-tracker.js +10 -5
  30. package/src/services/profile-manager.js +17 -2
  31. package/src/validation/schemas.js +2 -2
  32. package/src/index-direct.js +0 -157
  33. package/src/index-modular.js +0 -219
  34. package/src/index-monolithic-backup.js +0 -2230
  35. package/src/legacy/chrome-controller-old.js +0 -1406
  36. package/src/legacy/index-express.js +0 -625
  37. package/src/legacy/index-old.js +0 -977
  38. package/src/legacy/routes.js +0 -260
  39. package/src/legacy/shared-storage.js +0 -101
@@ -2,29 +2,9 @@
2
2
  const EXTENSION_VERSION = '2.0.4-BUILD-20250119';
3
3
  console.log(`[popup.js] Loaded version: ${EXTENSION_VERSION}`);
4
4
 
5
- // Import Firebase license client with graceful fallback
6
- let FirebaseLicenseClient = null;
7
- let LEMONSQUEEZY_CHECKOUT_URL = 'https://chromedebug.com/buy/996773cb-682b-430f-b9e3-9ce2130bd967';
8
- let licenseClient = null;
9
- let currentUserId = null;
10
-
11
- // Load Firebase asynchronously to avoid blocking module execution
12
- (async () => {
13
- try {
14
- const firebaseClientModule = await import('./firebase-client.js');
15
- FirebaseLicenseClient = firebaseClientModule.FirebaseLicenseClient;
16
-
17
- const firebaseConfigModule = await import('./firebase-config.public.js');
18
- LEMONSQUEEZY_CHECKOUT_URL = firebaseConfigModule.LEMONSQUEEZY_CHECKOUT_URL;
19
-
20
- // Initialize license client if Firebase loaded successfully
21
- licenseClient = new FirebaseLicenseClient();
22
- console.log('[popup.js] Firebase license client initialized');
23
- } catch (error) {
24
- console.warn('[popup.js] Firebase not available - license features disabled:', error);
25
- // Extension will work without Firebase, just no license validation
26
- }
27
- })();
5
+ // Chrome Web Store URLs for upgrade flow
6
+ const CHROME_STORE_PRO_URL = 'https://chrome.google.com/webstore/detail/chromedebug-mcp-pro/[PRO_EXTENSION_ID]';
7
+ const IS_PRO_VERSION = chrome.runtime.getManifest().name.includes('PRO');
28
8
 
29
9
  // Global variables for recording functionality
30
10
  let isRecording = false;
@@ -445,14 +425,7 @@ async function checkServerStatus() {
445
425
 
446
426
  if (connected) {
447
427
  statusEl.className = 'server-status connected';
448
- statusTextEl.innerHTML = `
449
- Server connected (port ${connectedPort})
450
- <div style="margin-top: 4px;">
451
- <a href="https://www.npmjs.com/package/@dynamicu/chromedebug-mcp" target="_blank" style="font-size: 10px; color: #666; text-decoration: none;">
452
- 📚 Documentation
453
- </a>
454
- </div>
455
- `;
428
+ statusTextEl.textContent = `Server connected (port ${connectedPort})`;
456
429
  } else {
457
430
  statusEl.className = 'server-status disconnected';
458
431
 
@@ -472,9 +445,6 @@ async function checkServerStatus() {
472
445
  <button id="copyInstallCmd" style="padding: 4px 8px; font-size: 11px; background: #2196F3; color: white; border: none; border-radius: 3px; cursor: pointer;">
473
446
  📋 Copy Commands
474
447
  </button>
475
- <a href="https://www.npmjs.com/package/@dynamicu/chromedebug-mcp" target="_blank" style="font-size: 11px; color: #2196F3; text-decoration: none;">
476
- 📚 Full Documentation
477
- </a>
478
448
  </div>
479
449
  `;
480
450
 
@@ -929,7 +899,7 @@ function updateRecordingsDisplay(recordings) {
929
899
  if (type === 'snapshot') {
930
900
  prompt = `Please use the get_frame_screenshot function in Chrome Debug to view the snapshot "${recordingId}"${portText}. This is a single-frame screenshot with console logs captured for debugging.`;
931
901
  } else {
932
- prompt = `Please use the chrome_pilot_show_frames function in Chrome Debug to load the recording "${recordingId}"${portText}.`;
902
+ prompt = `Please use the chrome_debug_show_frames function in Chrome Debug to load the recording "${recordingId}"${portText}.`;
933
903
  }
934
904
 
935
905
  navigator.clipboard.writeText(prompt).then(() => {
@@ -1002,352 +972,71 @@ function updateRecordingsDisplay(recordings) {
1002
972
 
1003
973
 
1004
974
 
1005
- // Initialize when DOM is ready
1006
- // License UI initialization function
1007
- async function initializeLicenseUI() {
1008
- console.log('[License] Initializing license UI');
1009
-
1010
- // Get or create userId
1011
- const stored = await chrome.storage.local.get('chromedebug_user_id');
1012
- currentUserId = stored.chromedebug_user_id || crypto.randomUUID();
1013
- if (!stored.chromedebug_user_id) {
1014
- await chrome.storage.local.set({chromedebug_user_id: currentUserId});
1015
- console.log('[License] Created new user ID:', currentUserId);
1016
- }
1017
-
1018
- // Check for license activation file and pre-fill license key
1019
- try {
1020
- const activationFileUrl = chrome.runtime.getURL('license-activation.json');
1021
- const response = await fetch(activationFileUrl);
1022
- if (response.ok) {
1023
- const activationData = await response.json();
1024
- if (activationData.license_key) {
1025
- const licenseKeyInput = document.getElementById('license-key-input');
1026
- if (licenseKeyInput) {
1027
- licenseKeyInput.value = activationData.license_key;
1028
- console.log('[License UI] Pre-filled license key from activation file');
1029
- }
1030
- }
975
+ // Initialize tier status display for FREE version
976
+ function initializeTierStatus() {
977
+ if (IS_PRO_VERSION) {
978
+ // Hide license section for PRO version
979
+ const licenseSection = document.getElementById('license-section');
980
+ if (licenseSection) {
981
+ licenseSection.style.display = 'none';
982
+ console.log('[Tier] PRO version detected - hiding license section');
1031
983
  }
1032
- } catch (error) {
1033
- // Activation file doesn't exist or is inaccessible - this is normal for FREE version
1034
- console.log('[License UI] No activation file found - normal for FREE version');
1035
- }
1036
-
1037
- // Check license status
1038
- const licenseStatus = await licenseClient.getCachedLicenseStatus();
1039
- console.log('[License] License status:', licenseStatus);
1040
-
1041
- // Update workflow recording PRO badge visibility
1042
- const workflowProBadge = document.getElementById('workflowProBadge');
1043
-
1044
- if (licenseStatus.valid && licenseStatus.tier === 'pro') {
1045
- // Show pro status
1046
- document.getElementById('pro-tier-status').style.display = 'block';
1047
- document.getElementById('free-tier-status').style.display = 'none';
1048
- // Hide license activation input/button when Pro license is active
1049
- document.getElementById('license-activation').style.display = 'none';
1050
- // Hide PRO badge for Pro users
1051
- if (workflowProBadge) workflowProBadge.style.display = 'none';
1052
- console.log('[License] Displaying Pro tier status');
1053
984
  } else {
1054
- // Show free tier + usage
1055
- const usage = await licenseClient.checkUsageLimit(currentUserId);
1056
- console.log('[License] Usage check result:', usage);
1057
-
1058
- // Handle both API response format (currentUsage, dailyLimit) and offline format (count, limit)
1059
- document.getElementById('usage-count').textContent = usage.currentUsage ?? usage.count ?? 0;
1060
- document.getElementById('usage-limit').textContent = usage.dailyLimit ?? usage.limit ?? 50;
1061
- document.getElementById('free-tier-status').style.display = 'block';
1062
- document.getElementById('pro-tier-status').style.display = 'none';
1063
- // Show license activation input/button for free tier
1064
- document.getElementById('license-activation').style.display = 'block';
1065
- // Show PRO badge for free users
1066
- if (workflowProBadge) workflowProBadge.style.display = 'block';
1067
- console.log('[License] Displaying Free tier status');
1068
- }
1069
- }
1070
-
1071
- // Inline Activation Manager Functions
1072
- /**
1073
- * Show the inline activation manager with slide-down animation
1074
- */
1075
- async function showInlineActivationManager(licenseKey) {
1076
- const inlineManager = document.getElementById('inline-activation-manager');
1077
- const activationsList = document.getElementById('activations-list-inline');
1078
-
1079
- // Show the container
1080
- inlineManager.style.display = 'block';
1081
-
1082
- // Trigger animation
1083
- setTimeout(() => {
1084
- inlineManager.style.maxHeight = '400px';
1085
- }, 10);
1086
-
1087
- // Load activations
1088
- try {
1089
- activationsList.innerHTML = '<div style="padding: 15px; text-align: center; color: #666;">Loading activations...</div>';
1090
-
1091
- // Get current instance ID
1092
- // Note: ls_instance_id is the LemonSqueezy instance ID set during activation
1093
- const stored = await chrome.storage.local.get(['ls_instance_id']);
1094
- const currentInstanceId = stored.ls_instance_id;
985
+ // Show FREE tier status (no license validation)
986
+ const freeTierStatus = document.getElementById('free-tier-status');
987
+ const proTierStatus = document.getElementById('pro-tier-status');
988
+ const licenseActivation = document.getElementById('license-activation');
1095
989
 
1096
- // Fetch activations from Firebase
1097
- const data = await licenseClient.listActivations(licenseKey);
1098
- const activations = data.activations || [];
990
+ if (freeTierStatus) freeTierStatus.style.display = 'block';
991
+ if (proTierStatus) proTierStatus.style.display = 'none';
992
+ if (licenseActivation) licenseActivation.style.display = 'none';
1099
993
 
1100
- // Render activations
1101
- renderInlineActivations(activations, currentInstanceId, licenseKey);
994
+ // Fetch and display current usage count
995
+ updateUsageDisplay();
1102
996
 
1103
- } catch (error) {
1104
- console.error('[License] Error loading activations:', error);
1105
- activationsList.innerHTML = `<div style="padding: 15px; text-align: center; color: #f44336;">Failed to load activations: ${error.message}</div>`;
997
+ console.log('[Tier] FREE version - showing usage counter');
1106
998
  }
1107
999
  }
1108
1000
 
1109
1001
  /**
1110
- * Hide the inline activation manager with slide-up animation
1002
+ * Fetch usage from background and update the display
1111
1003
  */
1112
- function hideInlineActivationManager() {
1113
- const inlineManager = document.getElementById('inline-activation-manager');
1114
- inlineManager.style.maxHeight = '0';
1115
-
1116
- setTimeout(() => {
1117
- inlineManager.style.display = 'none';
1118
- }, 300);
1119
- }
1120
-
1121
- /**
1122
- * Render activations in the inline manager
1123
- */
1124
- function renderInlineActivations(activations, currentInstanceId, licenseKey) {
1125
- const container = document.getElementById('activations-list-inline');
1126
- container.innerHTML = '';
1127
-
1128
- if (activations.length === 0) {
1129
- container.innerHTML = '<div style="padding: 15px; text-align: center; color: #666;">No activations found.</div>';
1130
- return;
1131
- }
1132
-
1133
- activations.forEach((activation, index) => {
1134
- const item = document.createElement('div');
1135
- item.style.cssText = 'padding: 12px; border-bottom: 1px solid #e0e0e0; transition: background 0.2s;';
1136
- item.onmouseenter = () => item.style.background = '#f9f9f9';
1137
- item.onmouseleave = () => item.style.background = isCurrentDevice ? '#e8f5e9' : 'white';
1138
-
1139
- // Check if this is the current device
1140
- // Compare against identifier (unique per activation), not name (same for all activations from this device)
1141
- const isCurrentDevice = currentInstanceId && activation.identifier === currentInstanceId;
1142
-
1143
- if (isCurrentDevice) {
1144
- item.style.background = '#e8f5e9';
1145
- item.style.borderLeft = '3px solid #4caf50';
1146
- }
1147
-
1148
- // Format date
1149
- const date = new Date(activation.createdAt);
1150
- const formattedDate = date.toLocaleString('en-US', {
1151
- month: 'short',
1152
- day: 'numeric',
1153
- year: 'numeric',
1154
- hour: '2-digit',
1155
- minute: '2-digit'
1156
- });
1157
-
1158
- // Build device info
1159
- let deviceDisplay = 'Unknown Device';
1160
- if (activation.deviceInfo) {
1161
- deviceDisplay = activation.deviceInfo.deviceName ||
1162
- `${activation.deviceInfo.platform || 'Unknown'} • ${activation.deviceInfo.browser || 'Unknown'}`;
1163
- } else if (activation.name && activation.name !== currentInstanceId) {
1164
- deviceDisplay = activation.name;
1165
- }
1166
-
1167
- item.innerHTML = `
1168
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
1169
- <span style="font-weight: 500; font-size: 13px; color: #333;">Activation ${index + 1}</span>
1170
- ${isCurrentDevice ? '<span style="background: #4caf50; color: white; padding: 2px 6px; border-radius: 10px; font-size: 10px; font-weight: bold;">CURRENT</span>' : ''}
1171
- </div>
1172
- <div style="font-size: 12px; color: #666; margin-bottom: 4px;">
1173
- <strong style="color: #333;">${deviceDisplay}</strong>
1174
- </div>
1175
- <div style="font-size: 11px; color: #999; margin-bottom: 8px;">
1176
- ${formattedDate}
1177
- </div>
1178
- <button
1179
- class="deactivate-btn-inline"
1180
- data-instance-id="${activation.identifier}"
1181
- data-activation-number="${index + 1}"
1182
- data-license-key="${licenseKey}"
1183
- style="padding: 5px 10px; background: ${isCurrentDevice ? '#ccc' : '#f44336'}; color: white; border: none; border-radius: 3px; cursor: ${isCurrentDevice ? 'not-allowed' : 'pointer'}; font-size: 11px; width: 100%;"
1184
- ${isCurrentDevice ? 'disabled' : ''}
1185
- >
1186
- ${isCurrentDevice ? '✓ Current Device' : 'Deactivate'}
1187
- </button>
1188
- `;
1189
-
1190
- // Add click handler for deactivate button
1191
- if (!isCurrentDevice) {
1192
- const deactivateBtn = item.querySelector('.deactivate-btn-inline');
1193
- deactivateBtn.addEventListener('click', () => {
1194
- handleInlineDeactivate(activation.identifier, index + 1, licenseKey);
1195
- });
1196
- }
1197
-
1198
- container.appendChild(item);
1199
- });
1200
- }
1201
-
1202
- /**
1203
- * Handle deactivation of an instance (inline)
1204
- */
1205
- async function handleInlineDeactivate(instanceId, activationNumber, licenseKey) {
1206
- const confirmed = confirm(
1207
- `Are you sure you want to deactivate Activation ${activationNumber}?\n\n` +
1208
- `This will free up an activation slot and automatically activate on this device.`
1209
- );
1210
-
1211
- if (!confirmed) {
1212
- return;
1213
- }
1214
-
1215
- const messageDiv = document.getElementById('license-message');
1216
-
1004
+ async function updateUsageDisplay() {
1217
1005
  try {
1218
- // Disable all deactivate buttons
1219
- const buttons = document.querySelectorAll('.deactivate-btn-inline');
1220
- buttons.forEach(btn => btn.disabled = true);
1221
-
1222
- messageDiv.textContent = 'Deactivating...';
1223
- messageDiv.style.color = '#2196F3';
1006
+ const response = await chrome.runtime.sendMessage({ action: 'getUsage' });
1224
1007
 
1225
- // Deactivate the instance
1226
- const result = await licenseClient.deactivateInstance(licenseKey, instanceId);
1227
- console.log('[License] Deactivation result:', result);
1228
-
1229
- if (result.deactivated) {
1230
- messageDiv.textContent = 'Deactivated! Activating on this device...';
1231
- messageDiv.style.color = '#4caf50';
1232
-
1233
- // Hide inline activation manager
1234
- hideInlineActivationManager();
1235
-
1236
- // Wait a moment, then retry activation
1237
- setTimeout(async () => {
1238
- // Set the license key in the input
1239
- document.getElementById('license-key-input').value = licenseKey;
1008
+ if (response && response.success) {
1009
+ const usageCountEl = document.getElementById('usage-count');
1010
+ const usageLimitEl = document.getElementById('usage-limit');
1240
1011
 
1241
- // Retry activation automatically
1242
- await handleLicenseActivation();
1243
- }, 1000);
1012
+ if (usageCountEl) {
1013
+ usageCountEl.textContent = response.count || 0;
1014
+ }
1015
+ if (usageLimitEl) {
1016
+ usageLimitEl.textContent = response.limit || 5;
1017
+ }
1244
1018
 
1245
- } else {
1246
- throw new Error(result.error || 'Deactivation failed');
1019
+ console.log(`[Usage] Display updated: ${response.count}/${response.limit}`);
1247
1020
  }
1248
-
1249
1021
  } catch (error) {
1250
- console.error('[License] Deactivation error:', error);
1251
- messageDiv.textContent = `Deactivation failed: ${error.message}`;
1252
- messageDiv.style.color = '#f44336';
1253
-
1254
- // Re-enable buttons
1255
- const buttons = document.querySelectorAll('.deactivate-btn-inline:not([disabled])');
1256
- buttons.forEach(btn => btn.disabled = false);
1257
- }
1258
- }
1259
-
1260
- // License activation handler
1261
- async function handleLicenseActivation() {
1262
- const licenseKey = document.getElementById('license-key-input').value.trim();
1263
- const messageDiv = document.getElementById('license-message');
1264
-
1265
- if (!licenseKey) {
1266
- messageDiv.textContent = 'Please enter a license key';
1267
- messageDiv.style.color = '#f44336';
1268
- return;
1269
- }
1270
-
1271
- messageDiv.textContent = 'Validating...';
1272
- messageDiv.style.color = '#2196F3';
1273
- console.log('[License] Validating license key...');
1274
-
1275
- // Clear any existing instance ID to force fresh activation
1276
- // This ensures we don't try to validate with stale/deleted instance IDs
1277
- await chrome.storage.local.remove(['ls_instance_id']);
1278
- console.log('[License] Cleared existing instance ID for fresh activation');
1279
-
1280
- const result = await licenseClient.validateLicense(licenseKey);
1281
- console.log('[License] Validation result:', result);
1282
- console.log('[License] Error value:', result.error);
1283
- console.log('[License] Error type:', typeof result.error);
1284
-
1285
- if (result.valid) {
1286
- messageDiv.textContent = 'License activated successfully!';
1287
- messageDiv.style.color = '#4caf50';
1288
- document.getElementById('license-key-input').value = '';
1289
- await initializeLicenseUI(); // Refresh UI
1290
- } else {
1291
- // Check if activation limit reached
1292
- console.log('[License] Checking if activation limit reached...');
1293
- console.log('[License] result.error === "ACTIVATION_LIMIT_REACHED":', result.error === 'ACTIVATION_LIMIT_REACHED');
1294
- console.log('[License] result.error includes "activation limit":', result.error && result.error.toLowerCase().includes('activation limit'));
1295
-
1296
- if (result.error === 'ACTIVATION_LIMIT_REACHED' ||
1297
- (result.error && result.error.toLowerCase().includes('activation limit'))) {
1298
- console.log('[License] Activation limit reached, showing inline activation manager');
1299
- messageDiv.textContent = 'Please deactivate an existing activation to continue';
1300
- messageDiv.style.color = '#ff9800';
1301
-
1302
- // Show inline activation manager
1303
- await showInlineActivationManager(licenseKey.trim());
1304
- } else {
1305
- messageDiv.textContent = result.error || 'Invalid license key';
1306
- messageDiv.style.color = '#f44336';
1307
- }
1022
+ console.error('[Usage] Failed to fetch usage:', error);
1308
1023
  }
1309
1024
  }
1310
1025
 
1311
- // Upgrade button handler
1026
+ // Upgrade button handler - redirect to Chrome Web Store PRO version
1312
1027
  function handleUpgradeClick() {
1313
- console.log('[License] Opening upgrade page:', LEMONSQUEEZY_CHECKOUT_URL);
1314
- chrome.tabs.create({url: LEMONSQUEEZY_CHECKOUT_URL});
1028
+ console.log('[Upgrade] Opening Chrome Web Store PRO version');
1029
+ chrome.tabs.create({url: CHROME_STORE_PRO_URL});
1315
1030
  }
1316
1031
 
1317
- // Listen for messages from activation manager
1318
- chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
1319
- if (message.type === 'RETRY_ACTIVATION' && message.licenseKey) {
1320
- console.log('[License] Retrying activation after deactivation');
1321
-
1322
- // Set the license key in the input
1323
- const licenseKeyInput = document.getElementById('license-key-input');
1324
- if (licenseKeyInput) {
1325
- licenseKeyInput.value = message.licenseKey;
1326
- }
1327
-
1328
- // Retry activation
1329
- handleLicenseActivation().then(() => {
1330
- console.log('[License] Retry activation complete');
1331
- });
1332
- }
1333
- });
1334
-
1335
1032
  document.addEventListener('DOMContentLoaded', () => {
1336
1033
  console.log('DOM loaded, initializing popup');
1337
1034
 
1338
- // Initialize license UI first
1339
- initializeLicenseUI().catch(error => {
1340
- console.error('[License] Failed to initialize license UI:', error);
1341
- });
1035
+ // Initialize tier status display
1036
+ initializeTierStatus();
1342
1037
 
1343
- // License event handlers
1344
- const activateLicenseBtn = document.getElementById('activate-license-btn');
1038
+ // Upgrade button handler
1345
1039
  const upgradeBtn = document.getElementById('upgrade-btn');
1346
-
1347
- if (activateLicenseBtn) {
1348
- activateLicenseBtn.addEventListener('click', handleLicenseActivation);
1349
- }
1350
-
1351
1040
  if (upgradeBtn) {
1352
1041
  upgradeBtn.addEventListener('click', handleUpgradeClick);
1353
1042
  }
@@ -1711,6 +1400,17 @@ document.addEventListener('DOMContentLoaded', () => {
1711
1400
  console.log('Starting recording with server');
1712
1401
  }
1713
1402
 
1403
+ // Check FREE tier usage limit before starting
1404
+ const usageCheck = await chrome.runtime.sendMessage({ action: 'checkUsageLimit' });
1405
+ if (usageCheck && !usageCheck.allowed) {
1406
+ console.log('[Usage] Daily limit reached:', usageCheck);
1407
+ if (recordingStatus) {
1408
+ recordingStatus.innerHTML = '<strong style="color: #f44336;">Daily limit reached</strong><br>' +
1409
+ `<small>${usageCheck.count}/${usageCheck.limit} recordings used today.<br>Upgrade to Pro for unlimited.</small>`;
1410
+ }
1411
+ return;
1412
+ }
1413
+
1714
1414
  // Get the current active tab
1715
1415
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
1716
1416
  console.log('Current tab:', tab);
@@ -1889,26 +1589,16 @@ document.addEventListener('DOMContentLoaded', () => {
1889
1589
  return;
1890
1590
  }
1891
1591
 
1892
- // Check license before allowing workflow recording (same pattern as strategic plan)
1893
- console.log('[License] Checking license for workflow recording...');
1894
- const licenseCheck = await chrome.runtime.sendMessage({
1895
- action: 'checkLicenseForWorkflow'
1896
- });
1897
- console.log('[License] License check result:', licenseCheck);
1898
-
1899
- // If not Pro or limit reached, show upgrade modal
1900
- if (!licenseCheck || !licenseCheck.allowed || (licenseCheck.tier && licenseCheck.tier !== 'pro')) {
1901
- const message = licenseCheck?.message || 'Workflow Recording is a Pro feature. Upgrade to Pro for unlimited workflow recordings with function tracing.';
1902
-
1903
- // Show modal
1904
- if (confirm(`${message}\n\nWould you like to upgrade now?`)) {
1905
- // Open upgrade page
1906
- chrome.tabs.create({
1907
- url: LEMONSQUEEZY_CHECKOUT_URL
1908
- });
1592
+ // Check FREE tier usage limit before starting
1593
+ const usageCheck = await chrome.runtime.sendMessage({ action: 'checkUsageLimit' });
1594
+ if (usageCheck && !usageCheck.allowed) {
1595
+ console.log('[Usage] Daily limit reached:', usageCheck);
1596
+ const workflowStatus = document.getElementById('workflowRecordingStatus');
1597
+ if (workflowStatus) {
1598
+ workflowStatus.innerHTML = '<strong style="color: #f44336;">Daily limit reached</strong><br>' +
1599
+ `<small>${usageCheck.count}/${usageCheck.limit} recordings used today.<br>Upgrade to Pro for unlimited recordings.</small>`;
1909
1600
  }
1910
-
1911
- return; // Stop execution
1601
+ return;
1912
1602
  }
1913
1603
 
1914
1604
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
@@ -2070,9 +1760,9 @@ document.addEventListener('DOMContentLoaded', () => {
2070
1760
  // Single source of truth - load from server
2071
1761
  await loadWorkflowRecordings(true);
2072
1762
 
2073
- // Show success toast
1763
+ // Show success toast - response.workflow.actions is the array
2074
1764
  const workflowData = response.workflow;
2075
- const actionCount = workflowData?.workflow?.actions?.length || workflowData?.workflow?.length || 0;
1765
+ const actionCount = workflowData?.actions?.length || 0;
2076
1766
  showToast(`Workflow recording saved! ${actionCount} actions recorded`, 3000, 'success');
2077
1767
  } catch (error) {
2078
1768
  console.error('Error loading workflow recordings:', error);
@@ -2972,9 +2662,9 @@ async function loadScreenRecordings(autoShow = false) {
2972
2662
  let prompt;
2973
2663
 
2974
2664
  if (name) {
2975
- prompt = `Please use the chrome_pilot_show_frames function in Chrome Debug to display the screen recording "${name}" (session: ${sessionId}) that was recorded on port ${port}.`;
2665
+ prompt = `Please use the chrome_debug_show_frames function in Chrome Debug to display the screen recording "${name}" (session: ${sessionId}) that was recorded on port ${port}.`;
2976
2666
  } else {
2977
- prompt = `Please use the chrome_pilot_show_frames function in Chrome Debug to display the screen recording "${sessionId}" that was recorded on port ${port}.`;
2667
+ prompt = `Please use the chrome_debug_show_frames function in Chrome Debug to display the screen recording "${sessionId}" that was recorded on port ${port}.`;
2978
2668
  }
2979
2669
 
2980
2670
  navigator.clipboard.writeText(prompt).then(() => {
@@ -3228,6 +2918,17 @@ chrome.storage.onChanged.addListener((changes, namespace) => {
3228
2918
  isWorkflowRecording = changes.workflowRecording.newValue === true;
3229
2919
  updateWorkflowRecordingUI();
3230
2920
  }
2921
+ // Update usage display when usage count changes
2922
+ if (changes.chromedebug_daily_usage) {
2923
+ const usage = changes.chromedebug_daily_usage.newValue;
2924
+ if (usage) {
2925
+ const usageCountEl = document.getElementById('usage-count');
2926
+ const usageLimitEl = document.getElementById('usage-limit');
2927
+ if (usageCountEl) usageCountEl.textContent = usage.count || 0;
2928
+ if (usageLimitEl) usageLimitEl.textContent = '5'; // FREE tier limit
2929
+ console.log(`[Usage] Storage updated: ${usage.count}/5`);
2930
+ }
2931
+ }
3231
2932
  }
3232
2933
  });
3233
2934
 
@@ -3266,12 +2967,20 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
3266
2967
  recordingStatus.innerHTML = `<strong style="color: #4CAF50;">Recording saved!</strong><br>` +
3267
2968
  `<small>${request.totalFrames} frames (${Math.round(request.duration / 1000)}s)</small>`;
3268
2969
  }
3269
- // Add frame session to recordings list
3270
- addRecording(request.sessionId, false, true, request.serverPort, null, request.sessionName);
2970
+ // Reload screen recordings list to show the new recording
2971
+ loadScreenRecordings(true);
3271
2972
  // Clear status after 5 seconds
3272
2973
  setTimeout(() => {
3273
2974
  if (recordingStatus) recordingStatus.textContent = '';
3274
2975
  }, 5000);
2976
+ } else if (request.action === 'workflowRecordingSaved') {
2977
+ console.log('Workflow recording saved:', request.workflowId);
2978
+ // Reload workflow recordings list to show the new recording
2979
+ loadWorkflowRecordings(true);
2980
+ } else if (request.action === 'recordingStopped') {
2981
+ console.log('Recording stopped:', request.sessionId);
2982
+ // Reload screen recordings list immediately
2983
+ loadScreenRecordings(true);
3275
2984
  } else if (request.action === 'uploadError') {
3276
2985
  const recordingStatus = document.getElementById('recordingStatus');
3277
2986
  if (recordingStatus) {