@dynamicu/chromedebug-mcp 2.3.1 ā 2.4.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/README.md +76 -1
- package/chrome-extension/activation-manager.html +208 -0
- package/chrome-extension/activation-manager.js +187 -0
- package/chrome-extension/firebase-client.js +135 -1
- package/chrome-extension/manifest.free.json +3 -3
- package/chrome-extension/popup.js +68 -2
- package/package.json +4 -2
- package/scripts/package-pro-extension.js +81 -0
- package/scripts/package-webstore-extension.js +89 -0
- package/scripts/webpack.config.free.cjs +3 -0
- package/scripts/webpack.config.pro.cjs +3 -0
package/README.md
CHANGED
|
@@ -102,10 +102,35 @@ Chrome Debug includes a Chrome extension that enables visual element selection f
|
|
|
102
102
|
|
|
103
103
|
### Installing the Extension
|
|
104
104
|
|
|
105
|
+
#### Option 1: Chrome Web Store (Recommended - FREE Version)
|
|
106
|
+
|
|
107
|
+
Install the FREE version directly from the Chrome Web Store:
|
|
108
|
+
|
|
109
|
+
š **[Install from Chrome Web Store](https://chromewebstore.google.com/detail/lemgbmdnephoaniipapgeciebfeakffn?utm_source=item-share-cb)**
|
|
110
|
+
|
|
111
|
+
- ā
5 recordings per day
|
|
112
|
+
- ā
Full MCP integration
|
|
113
|
+
- ā
Visual element selection
|
|
114
|
+
- ā
Automatic updates
|
|
115
|
+
- ā
No manual installation needed
|
|
116
|
+
|
|
117
|
+
#### Option 2: Manual Installation (Development)
|
|
118
|
+
|
|
105
119
|
1. Open Chrome and navigate to `chrome://extensions/`
|
|
106
|
-
2. Enable "Developer mode"
|
|
120
|
+
2. Enable "Developer mode"
|
|
107
121
|
3. Click "Load unpacked" and select the `chrome-extension` directory
|
|
108
122
|
|
|
123
|
+
#### Option 3: PRO Version (Unlimited)
|
|
124
|
+
|
|
125
|
+
For unlimited recordings and advanced features, purchase the PRO version:
|
|
126
|
+
|
|
127
|
+
š **[Get PRO Version](https://dynamicu.lemonsqueezy.com/buy/641143)**
|
|
128
|
+
|
|
129
|
+
- ā
Unlimited recordings
|
|
130
|
+
- ā
Advanced function tracing
|
|
131
|
+
- ā
Enhanced capture features
|
|
132
|
+
- ā
Priority support
|
|
133
|
+
|
|
109
134
|
### Using the Extension
|
|
110
135
|
|
|
111
136
|
1. **Element Selection**
|
|
@@ -123,6 +148,56 @@ Chrome Debug includes a Chrome extension that enables visual element selection f
|
|
|
123
148
|
- Console logs can be included in the recording
|
|
124
149
|
- Workflows can be replayed later for debugging
|
|
125
150
|
|
|
151
|
+
### Managing License Activations
|
|
152
|
+
|
|
153
|
+
Chrome Debug uses a license activation system to prevent activation limit exhaustion when reinstalling the extension.
|
|
154
|
+
|
|
155
|
+
#### The Problem
|
|
156
|
+
|
|
157
|
+
When you uninstall and reinstall the Chrome extension, it clears local storage and generates a new activation instance. With a typical 3-activation limit, frequent reinstalls can exhaust your available slots.
|
|
158
|
+
|
|
159
|
+
#### The Solution: Activation Manager
|
|
160
|
+
|
|
161
|
+
Chrome Debug now includes an **Activation Manager** that automatically handles this scenario:
|
|
162
|
+
|
|
163
|
+
**How It Works:**
|
|
164
|
+
1. When activation limit is reached, the Activation Manager opens automatically
|
|
165
|
+
2. Shows all your current activations with device info (e.g., "macOS 14.0 ⢠Chrome 121")
|
|
166
|
+
3. Identifies your current device (highlighted in green)
|
|
167
|
+
4. You select which activation to deactivate
|
|
168
|
+
5. Extension automatically retries activation on current device
|
|
169
|
+
|
|
170
|
+
**Example: Reinstalling on Same Machine**
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
You reinstall the extension on your laptop:
|
|
174
|
+
ā Try to activate license
|
|
175
|
+
ā Activation Manager shows:
|
|
176
|
+
⢠Activation 1: "macOS 14.0 ⢠Chrome 120" (Oct 10) ā OLD instance
|
|
177
|
+
⢠Activation 2: "Windows 11 ⢠Chrome 121" (Oct 12)
|
|
178
|
+
⢠Activation 3: "Linux ⢠Firefox 119" (Oct 14)
|
|
179
|
+
ā You recognize #1 is this same laptop
|
|
180
|
+
ā Click "Deactivate" on #1
|
|
181
|
+
ā Extension automatically activates new instance
|
|
182
|
+
ā Result: Still 3/3 slots, seamless experience
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Best Practices:**
|
|
186
|
+
- Before uninstalling: No action needed - Activation Manager handles it
|
|
187
|
+
- Multiple devices: Activation Manager helps track which is which
|
|
188
|
+
- Unused devices: Periodically deactivate machines you no longer use
|
|
189
|
+
|
|
190
|
+
#### FAQ
|
|
191
|
+
|
|
192
|
+
**Q: What happens if I forget which activation is which?**
|
|
193
|
+
A: The Activation Manager shows device information (OS, browser version) and activation dates. Your current device is highlighted in green.
|
|
194
|
+
|
|
195
|
+
**Q: Can I deactivate my current device?**
|
|
196
|
+
A: No, the button is disabled for your current device to prevent accidental lockout.
|
|
197
|
+
|
|
198
|
+
**Q: Does reinstalling the browser consume an activation?**
|
|
199
|
+
A: Yes, because local storage is cleared. Use the Activation Manager to deactivate the old instance first or after reinstalling.
|
|
200
|
+
|
|
126
201
|
|
|
127
202
|
## Development
|
|
128
203
|
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>Manage License Activations</title>
|
|
6
|
+
<style>
|
|
7
|
+
body {
|
|
8
|
+
width: 450px;
|
|
9
|
+
padding: 20px;
|
|
10
|
+
font-family: Arial, sans-serif;
|
|
11
|
+
background: #f5f5f5;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
h2 {
|
|
15
|
+
margin: 0 0 10px 0;
|
|
16
|
+
font-size: 20px;
|
|
17
|
+
color: #333;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.subtitle {
|
|
21
|
+
font-size: 14px;
|
|
22
|
+
color: #666;
|
|
23
|
+
margin-bottom: 20px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.limit-info {
|
|
27
|
+
background: #fff3cd;
|
|
28
|
+
border: 1px solid #ffc107;
|
|
29
|
+
padding: 15px;
|
|
30
|
+
border-radius: 4px;
|
|
31
|
+
margin-bottom: 20px;
|
|
32
|
+
font-size: 14px;
|
|
33
|
+
color: #856404;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.limit-info strong {
|
|
37
|
+
color: #333;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.activations-list {
|
|
41
|
+
background: white;
|
|
42
|
+
border-radius: 4px;
|
|
43
|
+
border: 1px solid #e0e0e0;
|
|
44
|
+
max-height: 400px;
|
|
45
|
+
overflow-y: auto;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.activation-item {
|
|
49
|
+
padding: 15px;
|
|
50
|
+
border-bottom: 1px solid #e0e0e0;
|
|
51
|
+
transition: background 0.2s;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.activation-item:last-child {
|
|
55
|
+
border-bottom: none;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.activation-item:hover {
|
|
59
|
+
background: #f9f9f9;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.activation-item.current-device {
|
|
63
|
+
background: #e8f5e9;
|
|
64
|
+
border-left: 4px solid #4caf50;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.activation-header {
|
|
68
|
+
display: flex;
|
|
69
|
+
justify-content: space-between;
|
|
70
|
+
align-items: center;
|
|
71
|
+
margin-bottom: 10px;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.activation-number {
|
|
75
|
+
font-weight: bold;
|
|
76
|
+
font-size: 16px;
|
|
77
|
+
color: #333;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.current-badge {
|
|
81
|
+
background: #4caf50;
|
|
82
|
+
color: white;
|
|
83
|
+
padding: 2px 8px;
|
|
84
|
+
border-radius: 12px;
|
|
85
|
+
font-size: 11px;
|
|
86
|
+
font-weight: bold;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.device-info {
|
|
90
|
+
font-size: 14px;
|
|
91
|
+
color: #666;
|
|
92
|
+
margin-bottom: 8px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.device-info strong {
|
|
96
|
+
color: #333;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.activation-date {
|
|
100
|
+
font-size: 12px;
|
|
101
|
+
color: #999;
|
|
102
|
+
margin-bottom: 10px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.deactivate-btn {
|
|
106
|
+
padding: 8px 16px;
|
|
107
|
+
background: #f44336;
|
|
108
|
+
color: white;
|
|
109
|
+
border: none;
|
|
110
|
+
border-radius: 4px;
|
|
111
|
+
cursor: pointer;
|
|
112
|
+
font-size: 13px;
|
|
113
|
+
font-weight: 500;
|
|
114
|
+
transition: background 0.2s;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.deactivate-btn:hover {
|
|
118
|
+
background: #d32f2f;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.deactivate-btn:disabled {
|
|
122
|
+
background: #ccc;
|
|
123
|
+
cursor: not-allowed;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.actions {
|
|
127
|
+
margin-top: 20px;
|
|
128
|
+
display: flex;
|
|
129
|
+
gap: 10px;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.cancel-btn {
|
|
133
|
+
flex: 1;
|
|
134
|
+
padding: 10px;
|
|
135
|
+
background: #9e9e9e;
|
|
136
|
+
color: white;
|
|
137
|
+
border: none;
|
|
138
|
+
border-radius: 4px;
|
|
139
|
+
cursor: pointer;
|
|
140
|
+
font-size: 14px;
|
|
141
|
+
font-weight: 500;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.cancel-btn:hover {
|
|
145
|
+
background: #757575;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.loading {
|
|
149
|
+
text-align: center;
|
|
150
|
+
padding: 40px;
|
|
151
|
+
font-size: 14px;
|
|
152
|
+
color: #666;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.error {
|
|
156
|
+
background: #ffebee;
|
|
157
|
+
border: 1px solid #f44336;
|
|
158
|
+
padding: 15px;
|
|
159
|
+
border-radius: 4px;
|
|
160
|
+
color: #c62828;
|
|
161
|
+
font-size: 14px;
|
|
162
|
+
margin-bottom: 20px;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.spinner {
|
|
166
|
+
border: 3px solid #f3f3f3;
|
|
167
|
+
border-top: 3px solid #2196F3;
|
|
168
|
+
border-radius: 50%;
|
|
169
|
+
width: 30px;
|
|
170
|
+
height: 30px;
|
|
171
|
+
animation: spin 1s linear infinite;
|
|
172
|
+
margin: 20px auto;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
@keyframes spin {
|
|
176
|
+
0% { transform: rotate(0deg); }
|
|
177
|
+
100% { transform: rotate(360deg); }
|
|
178
|
+
}
|
|
179
|
+
</style>
|
|
180
|
+
</head>
|
|
181
|
+
<body>
|
|
182
|
+
<h2>ā ļø Activation Limit Reached</h2>
|
|
183
|
+
<p class="subtitle">Please deactivate an existing activation to continue.</p>
|
|
184
|
+
|
|
185
|
+
<div id="error-message" class="error" style="display: none;"></div>
|
|
186
|
+
|
|
187
|
+
<div id="limit-info" class="limit-info" style="display: none;">
|
|
188
|
+
<strong>License Status:</strong> <span id="activation-status">Loading...</span>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<div id="loading" class="loading">
|
|
192
|
+
<div class="spinner"></div>
|
|
193
|
+
<p>Loading activations...</p>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<div id="activations-container" style="display: none;">
|
|
197
|
+
<div class="activations-list" id="activations-list">
|
|
198
|
+
<!-- Activations will be populated here -->
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
<div class="actions">
|
|
202
|
+
<button class="cancel-btn" id="cancel-btn">Cancel</button>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
|
|
206
|
+
<script type="module" src="activation-manager.js"></script>
|
|
207
|
+
</body>
|
|
208
|
+
</html>
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// Activation Manager for Chrome Debug Extension
|
|
2
|
+
import { FirebaseLicenseClient } from './firebase-client.js';
|
|
3
|
+
|
|
4
|
+
const licenseClient = new FirebaseLicenseClient();
|
|
5
|
+
|
|
6
|
+
let licenseKey = '';
|
|
7
|
+
let currentInstanceId = '';
|
|
8
|
+
let activations = [];
|
|
9
|
+
|
|
10
|
+
// Initialize on page load
|
|
11
|
+
document.addEventListener('DOMContentLoaded', async () => {
|
|
12
|
+
await loadActivations();
|
|
13
|
+
|
|
14
|
+
// Set up event listeners
|
|
15
|
+
document.getElementById('cancel-btn').addEventListener('click', () => {
|
|
16
|
+
window.close();
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Load activations from storage and Firebase
|
|
22
|
+
*/
|
|
23
|
+
async function loadActivations() {
|
|
24
|
+
try {
|
|
25
|
+
// Get license key and instance ID from storage
|
|
26
|
+
const stored = await chrome.storage.local.get(['ls_license_key', 'ls_instance_id', 'chromedebug_instance_id']);
|
|
27
|
+
licenseKey = stored.ls_license_key;
|
|
28
|
+
currentInstanceId = stored.chromedebug_instance_id;
|
|
29
|
+
|
|
30
|
+
if (!licenseKey) {
|
|
31
|
+
showError('License key not found. Please activate your license first.');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Fetch activations from Firebase
|
|
36
|
+
const data = await licenseClient.listActivations(licenseKey);
|
|
37
|
+
activations = data.activations || [];
|
|
38
|
+
|
|
39
|
+
// Show activation status
|
|
40
|
+
document.getElementById('activation-status').textContent =
|
|
41
|
+
`${data.activationUsage} of ${data.activationLimit} activations used`;
|
|
42
|
+
document.getElementById('limit-info').style.display = 'block';
|
|
43
|
+
|
|
44
|
+
// Hide loading, show activations
|
|
45
|
+
document.getElementById('loading').style.display = 'none';
|
|
46
|
+
document.getElementById('activations-container').style.display = 'block';
|
|
47
|
+
|
|
48
|
+
// Render activations list
|
|
49
|
+
renderActivations();
|
|
50
|
+
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error('[Activation Manager] Error loading activations:', error);
|
|
53
|
+
showError(`Failed to load activations: ${error.message}`);
|
|
54
|
+
document.getElementById('loading').style.display = 'none';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Render the list of activations
|
|
60
|
+
*/
|
|
61
|
+
function renderActivations() {
|
|
62
|
+
const container = document.getElementById('activations-list');
|
|
63
|
+
container.innerHTML = '';
|
|
64
|
+
|
|
65
|
+
if (activations.length === 0) {
|
|
66
|
+
container.innerHTML = '<div style="padding: 20px; text-align: center; color: #666;">No activations found.</div>';
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
activations.forEach((activation, index) => {
|
|
71
|
+
const item = document.createElement('div');
|
|
72
|
+
item.className = 'activation-item';
|
|
73
|
+
|
|
74
|
+
// Check if this is the current device
|
|
75
|
+
const isCurrentDevice = activation.name === currentInstanceId ||
|
|
76
|
+
activation.identifier === currentInstanceId;
|
|
77
|
+
|
|
78
|
+
if (isCurrentDevice) {
|
|
79
|
+
item.classList.add('current-device');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Format date
|
|
83
|
+
const date = new Date(activation.createdAt);
|
|
84
|
+
const formattedDate = date.toLocaleString('en-US', {
|
|
85
|
+
month: 'short',
|
|
86
|
+
day: 'numeric',
|
|
87
|
+
year: 'numeric',
|
|
88
|
+
hour: '2-digit',
|
|
89
|
+
minute: '2-digit'
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Build device info
|
|
93
|
+
let deviceDisplay = 'Unknown Device';
|
|
94
|
+
if (activation.deviceInfo) {
|
|
95
|
+
deviceDisplay = activation.deviceInfo.deviceName ||
|
|
96
|
+
`${activation.deviceInfo.platform || 'Unknown'} ⢠${activation.deviceInfo.browser || 'Unknown'}`;
|
|
97
|
+
} else if (activation.name && activation.name !== currentInstanceId) {
|
|
98
|
+
deviceDisplay = activation.name;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
item.innerHTML = `
|
|
102
|
+
<div class="activation-header">
|
|
103
|
+
<span class="activation-number">Activation ${index + 1}</span>
|
|
104
|
+
${isCurrentDevice ? '<span class="current-badge">CURRENT DEVICE</span>' : ''}
|
|
105
|
+
</div>
|
|
106
|
+
<div class="device-info">
|
|
107
|
+
<strong>${deviceDisplay}</strong>
|
|
108
|
+
</div>
|
|
109
|
+
<div class="activation-date">
|
|
110
|
+
Activated on ${formattedDate}
|
|
111
|
+
</div>
|
|
112
|
+
<button
|
|
113
|
+
class="deactivate-btn"
|
|
114
|
+
data-instance-id="${activation.identifier}"
|
|
115
|
+
${isCurrentDevice ? 'disabled title="Cannot deactivate current device"' : ''}
|
|
116
|
+
>
|
|
117
|
+
${isCurrentDevice ? 'ā Current Device' : 'Deactivate'}
|
|
118
|
+
</button>
|
|
119
|
+
`;
|
|
120
|
+
|
|
121
|
+
// Add click handler for deactivate button
|
|
122
|
+
if (!isCurrentDevice) {
|
|
123
|
+
const deactivateBtn = item.querySelector('.deactivate-btn');
|
|
124
|
+
deactivateBtn.addEventListener('click', () => {
|
|
125
|
+
handleDeactivate(activation.identifier, index + 1);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
container.appendChild(item);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Handle deactivation of an instance
|
|
135
|
+
*/
|
|
136
|
+
async function handleDeactivate(instanceId, activationNumber) {
|
|
137
|
+
const confirmed = confirm(
|
|
138
|
+
`Are you sure you want to deactivate Activation ${activationNumber}?\n\n` +
|
|
139
|
+
`This will free up an activation slot, allowing you to activate on this device.`
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
if (!confirmed) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
// Disable all deactivate buttons immediately
|
|
148
|
+
const buttons = document.querySelectorAll('.deactivate-btn');
|
|
149
|
+
buttons.forEach(btn => btn.disabled = true);
|
|
150
|
+
|
|
151
|
+
// Call deactivate API
|
|
152
|
+
const result = await licenseClient.deactivateInstance(licenseKey, instanceId);
|
|
153
|
+
|
|
154
|
+
if (result.deactivated) {
|
|
155
|
+
// Show success message
|
|
156
|
+
alert('Instance deactivated successfully! Click OK to retry activation.');
|
|
157
|
+
|
|
158
|
+
// Close this window and retry activation
|
|
159
|
+
// Send message to popup to retry activation
|
|
160
|
+
chrome.runtime.sendMessage({
|
|
161
|
+
type: 'RETRY_ACTIVATION',
|
|
162
|
+
licenseKey: licenseKey
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
window.close();
|
|
166
|
+
} else {
|
|
167
|
+
throw new Error('Deactivation failed');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error('[Activation Manager] Deactivation error:', error);
|
|
172
|
+
showError(`Failed to deactivate: ${error.message}`);
|
|
173
|
+
|
|
174
|
+
// Re-enable buttons
|
|
175
|
+
const buttons = document.querySelectorAll('.deactivate-btn:not([data-current])');
|
|
176
|
+
buttons.forEach(btn => btn.disabled = false);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Show error message
|
|
182
|
+
*/
|
|
183
|
+
function showError(message) {
|
|
184
|
+
const errorDiv = document.getElementById('error-message');
|
|
185
|
+
errorDiv.textContent = message;
|
|
186
|
+
errorDiv.style.display = 'block';
|
|
187
|
+
}
|
|
@@ -29,6 +29,68 @@ class FirebaseLicenseClient {
|
|
|
29
29
|
return instanceId;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Collect device context information for activation tracking
|
|
34
|
+
* @returns {Object} Device information
|
|
35
|
+
*/
|
|
36
|
+
getDeviceInfo() {
|
|
37
|
+
const ua = navigator.userAgent;
|
|
38
|
+
let browser = "Unknown";
|
|
39
|
+
let browserVersion = "Unknown";
|
|
40
|
+
let platform = "Unknown";
|
|
41
|
+
|
|
42
|
+
// Detect browser
|
|
43
|
+
if (ua.indexOf("Chrome") > -1 && ua.indexOf("Edg") === -1) {
|
|
44
|
+
browser = "Chrome";
|
|
45
|
+
const match = ua.match(/Chrome\/(\d+)/);
|
|
46
|
+
browserVersion = match ? match[1] : "Unknown";
|
|
47
|
+
} else if (ua.indexOf("Edg") > -1) {
|
|
48
|
+
browser = "Edge";
|
|
49
|
+
const match = ua.match(/Edg\/(\d+)/);
|
|
50
|
+
browserVersion = match ? match[1] : "Unknown";
|
|
51
|
+
} else if (ua.indexOf("Firefox") > -1) {
|
|
52
|
+
browser = "Firefox";
|
|
53
|
+
const match = ua.match(/Firefox\/(\d+)/);
|
|
54
|
+
browserVersion = match ? match[1] : "Unknown";
|
|
55
|
+
} else if (ua.indexOf("Safari") > -1) {
|
|
56
|
+
browser = "Safari";
|
|
57
|
+
const match = ua.match(/Version\/(\d+)/);
|
|
58
|
+
browserVersion = match ? match[1] : "Unknown";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Detect platform
|
|
62
|
+
if (ua.indexOf("Win") > -1) {
|
|
63
|
+
platform = "Windows";
|
|
64
|
+
// Try to extract Windows version
|
|
65
|
+
if (ua.indexOf("Windows NT 10.0") > -1) platform = "Windows 10/11";
|
|
66
|
+
else if (ua.indexOf("Windows NT 6.3") > -1) platform = "Windows 8.1";
|
|
67
|
+
else if (ua.indexOf("Windows NT 6.2") > -1) platform = "Windows 8";
|
|
68
|
+
else if (ua.indexOf("Windows NT 6.1") > -1) platform = "Windows 7";
|
|
69
|
+
} else if (ua.indexOf("Mac") > -1) {
|
|
70
|
+
platform = "macOS";
|
|
71
|
+
// Try to extract macOS version from user agent
|
|
72
|
+
const match = ua.match(/Mac OS X (\d+[._]\d+)/);
|
|
73
|
+
if (match) {
|
|
74
|
+
const version = match[1].replace('_', '.');
|
|
75
|
+
platform = `macOS ${version}`;
|
|
76
|
+
}
|
|
77
|
+
} else if (ua.indexOf("Linux") > -1) {
|
|
78
|
+
platform = "Linux";
|
|
79
|
+
} else if (ua.indexOf("CrOS") > -1) {
|
|
80
|
+
platform = "Chrome OS";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
platform,
|
|
85
|
+
browser,
|
|
86
|
+
browserVersion,
|
|
87
|
+
userAgent: ua,
|
|
88
|
+
screenResolution: `${screen.width}x${screen.height}`,
|
|
89
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
90
|
+
language: navigator.language
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
32
94
|
/**
|
|
33
95
|
* Activate license with LemonSqueezy (first-time activation)
|
|
34
96
|
* @param {string} licenseKey - License key to activate
|
|
@@ -36,16 +98,24 @@ class FirebaseLicenseClient {
|
|
|
36
98
|
*/
|
|
37
99
|
async activateLicense(licenseKey) {
|
|
38
100
|
const deviceId = await this.generateInstanceId();
|
|
101
|
+
const deviceInfo = this.getDeviceInfo();
|
|
39
102
|
|
|
40
103
|
try {
|
|
41
104
|
console.log(`[License] Activating license ${licenseKey} with device ${deviceId}`);
|
|
105
|
+
console.log('[License] Device info:', deviceInfo);
|
|
42
106
|
|
|
43
107
|
const response = await fetch(`${this.functionsUrl}/activateLicense`, {
|
|
44
108
|
method: 'POST',
|
|
45
109
|
headers: {'Content-Type': 'application/json'},
|
|
46
110
|
body: JSON.stringify({
|
|
47
111
|
licenseKey: licenseKey.trim(),
|
|
48
|
-
instanceName: deviceId
|
|
112
|
+
instanceName: deviceId,
|
|
113
|
+
deviceInfo: {
|
|
114
|
+
platform: deviceInfo.platform,
|
|
115
|
+
browser: deviceInfo.browser,
|
|
116
|
+
browserVersion: deviceInfo.browserVersion,
|
|
117
|
+
deviceName: `${deviceInfo.platform} ⢠${deviceInfo.browser} ${deviceInfo.browserVersion}`
|
|
118
|
+
}
|
|
49
119
|
})
|
|
50
120
|
});
|
|
51
121
|
|
|
@@ -273,6 +343,70 @@ class FirebaseLicenseClient {
|
|
|
273
343
|
async clearLicenseCache() {
|
|
274
344
|
await chrome.storage.local.remove(this.cacheKey);
|
|
275
345
|
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* List all activations for a license key
|
|
349
|
+
* @param {string} licenseKey - License key
|
|
350
|
+
* @returns {Promise<{activations: Array, activationLimit: number, activationUsage: number, availableSlots: number}>}
|
|
351
|
+
*/
|
|
352
|
+
async listActivations(licenseKey) {
|
|
353
|
+
try {
|
|
354
|
+
console.log(`[License] Listing activations for ${licenseKey}`);
|
|
355
|
+
|
|
356
|
+
const response = await fetch(`${this.functionsUrl}/listActivations`, {
|
|
357
|
+
method: 'POST',
|
|
358
|
+
headers: {'Content-Type': 'application/json'},
|
|
359
|
+
body: JSON.stringify({ licenseKey: licenseKey.trim() })
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
if (!response.ok) {
|
|
363
|
+
const errorData = await response.json();
|
|
364
|
+
throw new Error(errorData.error || `HTTP ${response.status}`);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const data = await response.json();
|
|
368
|
+
console.log('[License] Activations:', data);
|
|
369
|
+
|
|
370
|
+
return data;
|
|
371
|
+
} catch (error) {
|
|
372
|
+
console.error('[License] Failed to list activations:', error);
|
|
373
|
+
throw error;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Deactivate a specific license instance
|
|
379
|
+
* @param {string} licenseKey - License key
|
|
380
|
+
* @param {string} instanceId - Instance ID to deactivate
|
|
381
|
+
* @returns {Promise<{deactivated: boolean, message?: string}>}
|
|
382
|
+
*/
|
|
383
|
+
async deactivateInstance(licenseKey, instanceId) {
|
|
384
|
+
try {
|
|
385
|
+
console.log(`[License] Deactivating instance ${instanceId}`);
|
|
386
|
+
|
|
387
|
+
const response = await fetch(`${this.functionsUrl}/deactivateInstance`, {
|
|
388
|
+
method: 'POST',
|
|
389
|
+
headers: {'Content-Type': 'application/json'},
|
|
390
|
+
body: JSON.stringify({
|
|
391
|
+
licenseKey: licenseKey.trim(),
|
|
392
|
+
instanceId
|
|
393
|
+
})
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
if (!response.ok) {
|
|
397
|
+
const errorData = await response.json();
|
|
398
|
+
throw new Error(errorData.error || `HTTP ${response.status}`);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const data = await response.json();
|
|
402
|
+
console.log('[License] Deactivation result:', data);
|
|
403
|
+
|
|
404
|
+
return data;
|
|
405
|
+
} catch (error) {
|
|
406
|
+
console.error('[License] Failed to deactivate instance:', error);
|
|
407
|
+
throw error;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
276
410
|
}
|
|
277
411
|
|
|
278
412
|
export { FirebaseLicenseClient };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
|
-
"name": "ChromeDebug MCP Assistant FREE v2.
|
|
4
|
-
"version": "2.
|
|
5
|
-
"description": "ChromeDebug MCP visual element selector [FREE Edition] [Build: 2025-01-20-v2.
|
|
3
|
+
"name": "ChromeDebug MCP Assistant FREE v2.4.0",
|
|
4
|
+
"version": "2.4.0",
|
|
5
|
+
"description": "ChromeDebug MCP visual element selector [FREE Edition] [Build: 2025-01-20-v2.4.0]",
|
|
6
6
|
"permissions": [
|
|
7
7
|
"activeTab",
|
|
8
8
|
"scripting",
|
|
@@ -880,6 +880,8 @@ async function handleLicenseActivation() {
|
|
|
880
880
|
|
|
881
881
|
const result = await licenseClient.validateLicense(licenseKey);
|
|
882
882
|
console.log('[License] Validation result:', result);
|
|
883
|
+
console.log('[License] Error value:', result.error);
|
|
884
|
+
console.log('[License] Error type:', typeof result.error);
|
|
883
885
|
|
|
884
886
|
if (result.valid) {
|
|
885
887
|
messageDiv.textContent = 'License activated successfully!';
|
|
@@ -887,8 +889,54 @@ async function handleLicenseActivation() {
|
|
|
887
889
|
document.getElementById('license-key-input').value = '';
|
|
888
890
|
await initializeLicenseUI(); // Refresh UI
|
|
889
891
|
} else {
|
|
890
|
-
|
|
891
|
-
|
|
892
|
+
// Check if activation limit reached
|
|
893
|
+
console.log('[License] Checking if activation limit reached...');
|
|
894
|
+
console.log('[License] result.error === "ACTIVATION_LIMIT_REACHED":', result.error === 'ACTIVATION_LIMIT_REACHED');
|
|
895
|
+
console.log('[License] result.error includes "activation limit":', result.error && result.error.toLowerCase().includes('activation limit'));
|
|
896
|
+
|
|
897
|
+
if (result.error === 'ACTIVATION_LIMIT_REACHED' ||
|
|
898
|
+
(result.error && result.error.toLowerCase().includes('activation limit'))) {
|
|
899
|
+
console.log('[License] Activation limit reached, opening activation manager');
|
|
900
|
+
messageDiv.textContent = 'Opening activation manager...';
|
|
901
|
+
messageDiv.style.color = '#ff9800';
|
|
902
|
+
|
|
903
|
+
// Store the license key temporarily so activation manager can access it
|
|
904
|
+
await chrome.storage.local.set({
|
|
905
|
+
'ls_license_key': licenseKey.trim(),
|
|
906
|
+
'activation_manager_open': true
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
// Open activation manager in a new window
|
|
910
|
+
const width = 500;
|
|
911
|
+
const height = 600;
|
|
912
|
+
const left = Math.round((screen.width - width) / 2);
|
|
913
|
+
const top = Math.round((screen.height - height) / 2);
|
|
914
|
+
|
|
915
|
+
const activationManagerUrl = chrome.runtime.getURL('activation-manager.html');
|
|
916
|
+
console.log('[License] Opening activation manager at URL:', activationManagerUrl);
|
|
917
|
+
|
|
918
|
+
chrome.windows.create({
|
|
919
|
+
url: activationManagerUrl,
|
|
920
|
+
type: 'popup',
|
|
921
|
+
width: width,
|
|
922
|
+
height: height,
|
|
923
|
+
left: left,
|
|
924
|
+
top: top
|
|
925
|
+
}, (window) => {
|
|
926
|
+
if (chrome.runtime.lastError) {
|
|
927
|
+
console.error('[License] Failed to open activation manager:', chrome.runtime.lastError);
|
|
928
|
+
messageDiv.textContent = 'Error: Could not open activation manager. See console.';
|
|
929
|
+
messageDiv.style.color = '#f44336';
|
|
930
|
+
} else {
|
|
931
|
+
console.log('[License] Activation manager window created:', window);
|
|
932
|
+
messageDiv.textContent = 'Please deactivate an existing activation to continue';
|
|
933
|
+
messageDiv.style.color = '#ff9800';
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
} else {
|
|
937
|
+
messageDiv.textContent = result.error || 'Invalid license key';
|
|
938
|
+
messageDiv.style.color = '#f44336';
|
|
939
|
+
}
|
|
892
940
|
}
|
|
893
941
|
}
|
|
894
942
|
|
|
@@ -898,6 +946,24 @@ function handleUpgradeClick() {
|
|
|
898
946
|
chrome.tabs.create({url: LEMONSQUEEZY_CHECKOUT_URL});
|
|
899
947
|
}
|
|
900
948
|
|
|
949
|
+
// Listen for messages from activation manager
|
|
950
|
+
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
951
|
+
if (message.type === 'RETRY_ACTIVATION' && message.licenseKey) {
|
|
952
|
+
console.log('[License] Retrying activation after deactivation');
|
|
953
|
+
|
|
954
|
+
// Set the license key in the input
|
|
955
|
+
const licenseKeyInput = document.getElementById('license-key-input');
|
|
956
|
+
if (licenseKeyInput) {
|
|
957
|
+
licenseKeyInput.value = message.licenseKey;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
// Retry activation
|
|
961
|
+
handleLicenseActivation().then(() => {
|
|
962
|
+
console.log('[License] Retry activation complete');
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
|
|
901
967
|
document.addEventListener('DOMContentLoaded', () => {
|
|
902
968
|
console.log('DOM loaded, initializing popup');
|
|
903
969
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamicu/chromedebug-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.1",
|
|
4
4
|
"description": "ChromeDebug MCP - MCP server that provides full control over a Chrome browser instance for debugging and automation with AI assistants like Claude Code",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -56,7 +56,9 @@
|
|
|
56
56
|
"tbv:microtest": "cd chrome-extension && npm run tbv:microtest",
|
|
57
57
|
"build:free": "webpack --config scripts/webpack.config.free.cjs",
|
|
58
58
|
"build:pro": "webpack --config scripts/webpack.config.pro.cjs",
|
|
59
|
-
"build:both": "npm run build:free && npm run build:pro"
|
|
59
|
+
"build:both": "npm run build:free && npm run build:pro",
|
|
60
|
+
"package-pro": "node scripts/package-pro-extension.js",
|
|
61
|
+
"package-webstore": "node scripts/package-webstore-extension.js"
|
|
60
62
|
},
|
|
61
63
|
"keywords": [
|
|
62
64
|
"mcp",
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Package PRO Chrome extension for LemonSqueezy distribution
|
|
5
|
+
* Creates a .zip file with the PRO version including activation manager
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createWriteStream, promises as fs } from 'fs';
|
|
9
|
+
import { join, dirname } from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import { execSync } from 'child_process';
|
|
12
|
+
import archiver from 'archiver';
|
|
13
|
+
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = dirname(__filename);
|
|
16
|
+
const rootDir = join(__dirname, '..');
|
|
17
|
+
const proExtensionDir = join(rootDir, 'scripts', 'dist', 'pro');
|
|
18
|
+
const outputDir = join(rootDir, 'dist');
|
|
19
|
+
const outputFile = join(outputDir, 'chromedebug-extension-pro.zip');
|
|
20
|
+
|
|
21
|
+
async function packageProExtension() {
|
|
22
|
+
console.log('š¦ Building and packaging PRO Chrome extension...');
|
|
23
|
+
|
|
24
|
+
// Step 1: Build the pro version
|
|
25
|
+
console.log('šØ Building PRO version...');
|
|
26
|
+
try {
|
|
27
|
+
execSync('npm run build:pro', {
|
|
28
|
+
cwd: rootDir,
|
|
29
|
+
stdio: 'inherit'
|
|
30
|
+
});
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error('ā Failed to build PRO version');
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Step 2: Verify the build output exists
|
|
37
|
+
try {
|
|
38
|
+
await fs.access(proExtensionDir);
|
|
39
|
+
} catch {
|
|
40
|
+
throw new Error(`Build output not found at ${proExtensionDir}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Step 3: Ensure output directory exists
|
|
44
|
+
try {
|
|
45
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
46
|
+
} catch (error) {
|
|
47
|
+
// Directory might already exist, that's fine
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Step 4: Create zip file
|
|
51
|
+
console.log('š¦ Creating PRO zip package...');
|
|
52
|
+
|
|
53
|
+
const output = createWriteStream(outputFile);
|
|
54
|
+
const archive = archiver('zip', { zlib: { level: 9 } });
|
|
55
|
+
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
output.on('close', () => {
|
|
58
|
+
const sizeKB = (archive.pointer() / 1024).toFixed(2);
|
|
59
|
+
console.log(`ā
PRO extension packaged successfully!`);
|
|
60
|
+
console.log(` Size: ${sizeKB} KB`);
|
|
61
|
+
console.log(` Location: ${outputFile}`);
|
|
62
|
+
console.log(` š This is the PRO version with activation manager`);
|
|
63
|
+
console.log(` š For LemonSqueezy distribution only`);
|
|
64
|
+
resolve();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
archive.on('error', reject);
|
|
68
|
+
|
|
69
|
+
archive.pipe(output);
|
|
70
|
+
|
|
71
|
+
// Add all files from the pro build directory
|
|
72
|
+
archive.directory(proExtensionDir, false);
|
|
73
|
+
|
|
74
|
+
archive.finalize();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
packageProExtension().catch(err => {
|
|
79
|
+
console.error('ā Failed to package PRO extension:', err);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Package FREE Chrome extension for Chrome Web Store distribution
|
|
5
|
+
* Creates a .zip file optimized for Chrome Web Store submission
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createWriteStream, promises as fs } from 'fs';
|
|
9
|
+
import { join, dirname } from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import { execSync } from 'child_process';
|
|
12
|
+
import archiver from 'archiver';
|
|
13
|
+
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = dirname(__filename);
|
|
16
|
+
const rootDir = join(__dirname, '..');
|
|
17
|
+
const freeExtensionDir = join(rootDir, 'scripts', 'dist', 'free');
|
|
18
|
+
const outputDir = join(rootDir, 'dist');
|
|
19
|
+
const outputFile = join(outputDir, 'chromedebug-webstore-free.zip');
|
|
20
|
+
|
|
21
|
+
async function packageWebStoreExtension() {
|
|
22
|
+
console.log('š¦ Building and packaging FREE Chrome extension for Web Store...');
|
|
23
|
+
|
|
24
|
+
// Step 1: Build the free version
|
|
25
|
+
console.log('šØ Building free version...');
|
|
26
|
+
try {
|
|
27
|
+
execSync('npm run build:free', {
|
|
28
|
+
cwd: rootDir,
|
|
29
|
+
stdio: 'inherit'
|
|
30
|
+
});
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error('ā Failed to build free version');
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Step 2: Verify the build output exists
|
|
37
|
+
try {
|
|
38
|
+
await fs.access(freeExtensionDir);
|
|
39
|
+
} catch {
|
|
40
|
+
throw new Error(`Build output not found at ${freeExtensionDir}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Step 3: Ensure output directory exists
|
|
44
|
+
try {
|
|
45
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
46
|
+
} catch (error) {
|
|
47
|
+
// Directory might already exist, that's fine
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Step 4: Create zip file optimized for Chrome Web Store
|
|
51
|
+
console.log('š¦ Creating Chrome Web Store package...');
|
|
52
|
+
|
|
53
|
+
const output = createWriteStream(outputFile);
|
|
54
|
+
const archive = archiver('zip', {
|
|
55
|
+
zlib: { level: 9 }
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
output.on('close', () => {
|
|
60
|
+
const sizeKB = (archive.pointer() / 1024).toFixed(2);
|
|
61
|
+
console.log(`ā
Chrome Web Store package created successfully!`);
|
|
62
|
+
console.log(` Size: ${sizeKB} KB`);
|
|
63
|
+
console.log(` Location: ${outputFile}`);
|
|
64
|
+
console.log(` š This is the FREE version for Chrome Web Store`);
|
|
65
|
+
console.log(` š Ready to upload to: https://chrome.google.com/webstore/devconsole`);
|
|
66
|
+
console.log(`\nš Next steps:`);
|
|
67
|
+
console.log(` 1. Go to Chrome Web Store Developer Dashboard`);
|
|
68
|
+
console.log(` 2. Upload this zip file`);
|
|
69
|
+
console.log(` 3. Fill in store listing details`);
|
|
70
|
+
console.log(` 4. Submit for review`);
|
|
71
|
+
resolve();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
archive.on('error', reject);
|
|
75
|
+
|
|
76
|
+
archive.pipe(output);
|
|
77
|
+
|
|
78
|
+
// Add all files from the free build directory
|
|
79
|
+
// Chrome Web Store expects all files to be in the root of the zip
|
|
80
|
+
archive.directory(freeExtensionDir, false);
|
|
81
|
+
|
|
82
|
+
archive.finalize();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
packageWebStoreExtension().catch(err => {
|
|
87
|
+
console.error('ā Failed to package Chrome Web Store extension:', err);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
});
|
|
@@ -37,6 +37,7 @@ module.exports = {
|
|
|
37
37
|
{ from: 'chrome-extension/popup.html', to: 'popup.html' },
|
|
38
38
|
{ from: 'chrome-extension/options.html', to: 'options.html' },
|
|
39
39
|
{ from: 'chrome-extension/offscreen.html', to: 'offscreen.html' },
|
|
40
|
+
{ from: 'chrome-extension/activation-manager.html', to: 'activation-manager.html' },
|
|
40
41
|
|
|
41
42
|
// Copy icons
|
|
42
43
|
{ from: 'chrome-extension/*.png', to: '[name][ext]' },
|
|
@@ -60,6 +61,8 @@ module.exports = {
|
|
|
60
61
|
{ from: 'chrome-extension/license-helper.js', to: 'license-helper.js' },
|
|
61
62
|
{ from: 'chrome-extension/firebase-client.js', to: 'firebase-client.js' },
|
|
62
63
|
{ from: 'chrome-extension/firebase-config.js', to: 'firebase-config.js' },
|
|
64
|
+
{ from: 'chrome-extension/firebase-config.module.js', to: 'firebase-config.module.js' },
|
|
65
|
+
{ from: 'chrome-extension/activation-manager.js', to: 'activation-manager.js' },
|
|
63
66
|
{ from: 'chrome-extension/frame-capture.js', to: 'frame-capture.js' },
|
|
64
67
|
{ from: 'chrome-extension/chrome-session-manager.js', to: 'chrome-session-manager.js' },
|
|
65
68
|
|
|
@@ -38,6 +38,7 @@ module.exports = {
|
|
|
38
38
|
{ from: 'chrome-extension/popup.html', to: 'popup.html' },
|
|
39
39
|
{ from: 'chrome-extension/options.html', to: 'options.html' },
|
|
40
40
|
{ from: 'chrome-extension/offscreen.html', to: 'offscreen.html' },
|
|
41
|
+
{ from: 'chrome-extension/activation-manager.html', to: 'activation-manager.html' },
|
|
41
42
|
{ from: 'chrome-extension/pro/frame-editor.html', to: 'pro/frame-editor.html' },
|
|
42
43
|
|
|
43
44
|
// Copy icons
|
|
@@ -62,6 +63,8 @@ module.exports = {
|
|
|
62
63
|
{ from: 'chrome-extension/license-helper.js', to: 'license-helper.js' },
|
|
63
64
|
{ from: 'chrome-extension/firebase-client.js', to: 'firebase-client.js' },
|
|
64
65
|
{ from: 'chrome-extension/firebase-config.js', to: 'firebase-config.js' },
|
|
66
|
+
{ from: 'chrome-extension/firebase-config.module.js', to: 'firebase-config.module.js' },
|
|
67
|
+
{ from: 'chrome-extension/activation-manager.js', to: 'activation-manager.js' },
|
|
65
68
|
{ from: 'chrome-extension/frame-capture.js', to: 'frame-capture.js' },
|
|
66
69
|
{ from: 'chrome-extension/chrome-session-manager.js', to: 'chrome-session-manager.js' },
|
|
67
70
|
|