brave-real-launcher 1.2.32 → 1.2.34
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/.github/workflows/chrome-launcher-sync.yml +2 -2
- package/README.md +75 -100
- package/dist/brave-installer.d.ts +57 -0
- package/dist/brave-installer.js +352 -0
- package/dist/brave-installer.mjs +323 -0
- package/dist/brave-launcher.d.ts +21 -0
- package/dist/brave-launcher.js +207 -15
- package/dist/brave-launcher.mjs +207 -15
- package/dist/extension-manager.d.ts +58 -0
- package/dist/extension-manager.js +333 -0
- package/dist/extension-manager.mjs +304 -0
- package/dist/flags.js +15 -1
- package/dist/flags.mjs +15 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +15 -2
- package/dist/index.mjs +7 -1
- package/dist/stealth-utils.d.ts +128 -0
- package/dist/stealth-utils.js +210 -0
- package/dist/stealth-utils.mjs +206 -0
- package/package.json +1 -1
- package/console.log(ESM build works!))' +0 -0
- package/workflow-test-report.md +0 -44
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright 2024 Brave Real Launcher Contributors.
|
|
3
|
+
* Licensed under the Apache License, Version 2.0
|
|
4
|
+
*
|
|
5
|
+
* Extension Manager for Brave Real Launcher
|
|
6
|
+
* Handles downloading, caching, and loading browser extensions
|
|
7
|
+
*/
|
|
8
|
+
'use strict';
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
26
|
+
if (mod && mod.__esModule) return mod;
|
|
27
|
+
var result = {};
|
|
28
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
29
|
+
__setModuleDefault(result, mod);
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
33
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
34
|
+
};
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ExtensionManager = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const https = __importStar(require("https"));
|
|
40
|
+
const http = __importStar(require("http"));
|
|
41
|
+
const child_process_1 = require("child_process");
|
|
42
|
+
const os_1 = require("os");
|
|
43
|
+
const logger_js_1 = __importDefault(require("./logger.js"));
|
|
44
|
+
const UBLOCK_GITHUB_API = 'https://api.github.com/repos/gorhill/uBlock/releases/latest';
|
|
45
|
+
const UBLOCK_DOWNLOAD_BASE = 'https://github.com/gorhill/uBlock/releases/download';
|
|
46
|
+
class ExtensionManager {
|
|
47
|
+
constructor(options = {}) {
|
|
48
|
+
this.cacheDir = options.cacheDir || this.getDefaultCacheDir();
|
|
49
|
+
this.autoUpdate = options.autoUpdate !== false;
|
|
50
|
+
this.silent = options.silent || false;
|
|
51
|
+
// Ensure cache directory exists
|
|
52
|
+
if (!fs.existsSync(this.cacheDir)) {
|
|
53
|
+
fs.mkdirSync(this.cacheDir, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
getDefaultCacheDir() {
|
|
57
|
+
const homeDir = (0, os_1.homedir)();
|
|
58
|
+
return path.join(homeDir, '.brave-real-launcher', 'extensions');
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get uBlock Origin extension, downloading if necessary
|
|
62
|
+
*/
|
|
63
|
+
async getUBlockOrigin() {
|
|
64
|
+
try {
|
|
65
|
+
const extensionDir = path.join(this.cacheDir, 'ublock-origin');
|
|
66
|
+
const versionFile = path.join(extensionDir, 'version.json');
|
|
67
|
+
// Check if we need to update
|
|
68
|
+
let needsUpdate = !fs.existsSync(extensionDir);
|
|
69
|
+
let currentVersion = '0.0.0';
|
|
70
|
+
if (fs.existsSync(versionFile)) {
|
|
71
|
+
try {
|
|
72
|
+
const versionData = JSON.parse(fs.readFileSync(versionFile, 'utf-8'));
|
|
73
|
+
currentVersion = versionData.version;
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
needsUpdate = true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
needsUpdate = true;
|
|
81
|
+
}
|
|
82
|
+
// Get latest version info
|
|
83
|
+
const latestInfo = await this.getLatestUBlockVersion();
|
|
84
|
+
if (latestInfo && this.autoUpdate) {
|
|
85
|
+
if (this.compareVersions(latestInfo.version, currentVersion) > 0) {
|
|
86
|
+
needsUpdate = true;
|
|
87
|
+
if (!this.silent) {
|
|
88
|
+
logger_js_1.default.log('ExtensionManager', `uBlock Origin update available: ${currentVersion} → ${latestInfo.version}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (needsUpdate && latestInfo) {
|
|
93
|
+
await this.downloadAndExtractUBlock(latestInfo.downloadUrl, extensionDir, latestInfo.version);
|
|
94
|
+
}
|
|
95
|
+
// Find the extension manifest
|
|
96
|
+
const manifestPath = this.findManifest(extensionDir);
|
|
97
|
+
if (manifestPath) {
|
|
98
|
+
const manifestDir = path.dirname(manifestPath);
|
|
99
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
100
|
+
return {
|
|
101
|
+
name: 'uBlock Origin',
|
|
102
|
+
version: manifest.version || currentVersion,
|
|
103
|
+
path: manifestDir
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
if (!this.silent) {
|
|
110
|
+
logger_js_1.default.error('ExtensionManager', `Failed to get uBlock Origin: ${error.message}`);
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get latest uBlock Origin version from GitHub
|
|
117
|
+
*/
|
|
118
|
+
async getLatestUBlockVersion() {
|
|
119
|
+
return new Promise((resolve) => {
|
|
120
|
+
const options = {
|
|
121
|
+
hostname: 'api.github.com',
|
|
122
|
+
path: '/repos/gorhill/uBlock/releases/latest',
|
|
123
|
+
method: 'GET',
|
|
124
|
+
headers: {
|
|
125
|
+
'User-Agent': 'brave-real-launcher',
|
|
126
|
+
'Accept': 'application/vnd.github.v3+json'
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
const req = https.request(options, (res) => {
|
|
130
|
+
let data = '';
|
|
131
|
+
res.on('data', chunk => data += chunk);
|
|
132
|
+
res.on('end', () => {
|
|
133
|
+
var _a, _b;
|
|
134
|
+
try {
|
|
135
|
+
const release = JSON.parse(data);
|
|
136
|
+
const version = ((_a = release.tag_name) === null || _a === void 0 ? void 0 : _a.replace('v', '')) || release.name;
|
|
137
|
+
// Find chromium zip asset
|
|
138
|
+
const chromiumAsset = (_b = release.assets) === null || _b === void 0 ? void 0 : _b.find((asset) => asset.name.includes('chromium') && asset.name.endsWith('.zip'));
|
|
139
|
+
if (chromiumAsset) {
|
|
140
|
+
resolve({
|
|
141
|
+
version,
|
|
142
|
+
downloadUrl: chromiumAsset.browser_download_url
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
// Fallback to constructed URL
|
|
147
|
+
resolve({
|
|
148
|
+
version,
|
|
149
|
+
downloadUrl: `${UBLOCK_DOWNLOAD_BASE}/${release.tag_name}/uBlock0_${version}.chromium.zip`
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (e) {
|
|
154
|
+
resolve(null);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
req.on('error', () => resolve(null));
|
|
159
|
+
req.setTimeout(10000, () => {
|
|
160
|
+
req.destroy();
|
|
161
|
+
resolve(null);
|
|
162
|
+
});
|
|
163
|
+
req.end();
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Download and extract uBlock Origin
|
|
168
|
+
*/
|
|
169
|
+
async downloadAndExtractUBlock(url, targetDir, version) {
|
|
170
|
+
if (!this.silent) {
|
|
171
|
+
logger_js_1.default.log('ExtensionManager', `Downloading uBlock Origin v${version}...`);
|
|
172
|
+
}
|
|
173
|
+
const zipPath = path.join(this.cacheDir, 'ublock-temp.zip');
|
|
174
|
+
// Download the file
|
|
175
|
+
await this.downloadFile(url, zipPath);
|
|
176
|
+
// Clean target directory
|
|
177
|
+
if (fs.existsSync(targetDir)) {
|
|
178
|
+
this.removeDir(targetDir);
|
|
179
|
+
}
|
|
180
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
181
|
+
// Extract the zip
|
|
182
|
+
await this.extractZip(zipPath, targetDir);
|
|
183
|
+
// Save version info
|
|
184
|
+
const versionFile = path.join(targetDir, 'version.json');
|
|
185
|
+
fs.writeFileSync(versionFile, JSON.stringify({ version, downloadedAt: new Date().toISOString() }));
|
|
186
|
+
// Cleanup zip
|
|
187
|
+
try {
|
|
188
|
+
fs.unlinkSync(zipPath);
|
|
189
|
+
}
|
|
190
|
+
catch (e) { }
|
|
191
|
+
if (!this.silent) {
|
|
192
|
+
logger_js_1.default.log('ExtensionManager', `uBlock Origin v${version} installed successfully`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Download a file from URL
|
|
197
|
+
*/
|
|
198
|
+
downloadFile(url, destPath) {
|
|
199
|
+
return new Promise((resolve, reject) => {
|
|
200
|
+
const followRedirect = (url, redirectCount = 0) => {
|
|
201
|
+
if (redirectCount > 5) {
|
|
202
|
+
reject(new Error('Too many redirects'));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const protocol = url.startsWith('https') ? https : http;
|
|
206
|
+
protocol.get(url, {
|
|
207
|
+
headers: { 'User-Agent': 'brave-real-launcher' }
|
|
208
|
+
}, (response) => {
|
|
209
|
+
// Handle redirects
|
|
210
|
+
if (response.statusCode === 301 || response.statusCode === 302) {
|
|
211
|
+
const redirectUrl = response.headers.location;
|
|
212
|
+
if (redirectUrl) {
|
|
213
|
+
followRedirect(redirectUrl, redirectCount + 1);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (response.statusCode !== 200) {
|
|
218
|
+
reject(new Error(`Download failed with status ${response.statusCode}`));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const file = fs.createWriteStream(destPath);
|
|
222
|
+
response.pipe(file);
|
|
223
|
+
file.on('finish', () => {
|
|
224
|
+
file.close();
|
|
225
|
+
resolve();
|
|
226
|
+
});
|
|
227
|
+
file.on('error', (err) => {
|
|
228
|
+
fs.unlinkSync(destPath);
|
|
229
|
+
reject(err);
|
|
230
|
+
});
|
|
231
|
+
}).on('error', reject);
|
|
232
|
+
};
|
|
233
|
+
followRedirect(url);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Extract zip file
|
|
238
|
+
*/
|
|
239
|
+
async extractZip(zipPath, targetDir) {
|
|
240
|
+
const platform = process.platform;
|
|
241
|
+
try {
|
|
242
|
+
if (platform === 'win32') {
|
|
243
|
+
// Use PowerShell on Windows
|
|
244
|
+
(0, child_process_1.execSync)(`powershell -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${targetDir}' -Force"`, {
|
|
245
|
+
stdio: 'ignore'
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
// Use unzip on Linux/macOS
|
|
250
|
+
(0, child_process_1.execSync)(`unzip -o "${zipPath}" -d "${targetDir}"`, {
|
|
251
|
+
stdio: 'ignore'
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
throw new Error(`Failed to extract zip: ${error.message}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Find manifest.json in extension directory
|
|
261
|
+
*/
|
|
262
|
+
findManifest(dir) {
|
|
263
|
+
if (!fs.existsSync(dir))
|
|
264
|
+
return null;
|
|
265
|
+
// Check current directory
|
|
266
|
+
const directManifest = path.join(dir, 'manifest.json');
|
|
267
|
+
if (fs.existsSync(directManifest)) {
|
|
268
|
+
return directManifest;
|
|
269
|
+
}
|
|
270
|
+
// Check subdirectories (zip might have a nested folder)
|
|
271
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
272
|
+
for (const entry of entries) {
|
|
273
|
+
if (entry.isDirectory()) {
|
|
274
|
+
const nestedManifest = path.join(dir, entry.name, 'manifest.json');
|
|
275
|
+
if (fs.existsSync(nestedManifest)) {
|
|
276
|
+
return nestedManifest;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Compare semantic versions
|
|
284
|
+
*/
|
|
285
|
+
compareVersions(a, b) {
|
|
286
|
+
const partsA = a.replace(/[^\d.]/g, '').split('.').map(Number);
|
|
287
|
+
const partsB = b.replace(/[^\d.]/g, '').split('.').map(Number);
|
|
288
|
+
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
|
|
289
|
+
const numA = partsA[i] || 0;
|
|
290
|
+
const numB = partsB[i] || 0;
|
|
291
|
+
if (numA > numB)
|
|
292
|
+
return 1;
|
|
293
|
+
if (numA < numB)
|
|
294
|
+
return -1;
|
|
295
|
+
}
|
|
296
|
+
return 0;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Remove directory recursively
|
|
300
|
+
*/
|
|
301
|
+
removeDir(dir) {
|
|
302
|
+
const rmSync = fs.rmSync || fs.rmdirSync;
|
|
303
|
+
try {
|
|
304
|
+
rmSync(dir, { recursive: true, force: true });
|
|
305
|
+
}
|
|
306
|
+
catch (e) { }
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Get extension load flags for browser
|
|
310
|
+
*/
|
|
311
|
+
getExtensionFlags(extensionPaths) {
|
|
312
|
+
if (extensionPaths.length === 0)
|
|
313
|
+
return [];
|
|
314
|
+
return [
|
|
315
|
+
`--load-extension=${extensionPaths.join(',')}`,
|
|
316
|
+
'--enable-extensions'
|
|
317
|
+
];
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Clean extension cache
|
|
321
|
+
*/
|
|
322
|
+
cleanCache() {
|
|
323
|
+
if (fs.existsSync(this.cacheDir)) {
|
|
324
|
+
this.removeDir(this.cacheDir);
|
|
325
|
+
if (!this.silent) {
|
|
326
|
+
logger_js_1.default.log('ExtensionManager', 'Extension cache cleaned');
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
exports.ExtensionManager = ExtensionManager;
|
|
332
|
+
exports.default = ExtensionManager;
|
|
333
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXh0ZW5zaW9uLW1hbmFnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvZXh0ZW5zaW9uLW1hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HO0FBQ0gsWUFBWSxDQUFDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUViLHVDQUF5QjtBQUN6QiwyQ0FBNkI7QUFDN0IsNkNBQStCO0FBQy9CLDJDQUE2QjtBQUM3QixpREFBeUM7QUFDekMsMkJBQTZCO0FBQzdCLDREQUE4QjtBQWM5QixNQUFNLGlCQUFpQixHQUFHLDZEQUE2RCxDQUFDO0FBQ3hGLE1BQU0sb0JBQW9CLEdBQUcscURBQXFELENBQUM7QUFFbkYsTUFBYSxnQkFBZ0I7SUFLekIsWUFBWSxVQUFtQyxFQUFFO1FBQzdDLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxDQUFDLFFBQVEsSUFBSSxJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztRQUM5RCxJQUFJLENBQUMsVUFBVSxHQUFHLE9BQU8sQ0FBQyxVQUFVLEtBQUssS0FBSyxDQUFDO1FBQy9DLElBQUksQ0FBQyxNQUFNLEdBQUcsT0FBTyxDQUFDLE1BQU0sSUFBSSxLQUFLLENBQUM7UUFFdEMsZ0NBQWdDO1FBQ2hDLElBQUksQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRTtZQUMvQixFQUFFLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztTQUNwRDtJQUNMLENBQUM7SUFFTyxrQkFBa0I7UUFDdEIsTUFBTSxPQUFPLEdBQUcsSUFBQSxZQUFPLEdBQUUsQ0FBQztRQUMxQixPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLHNCQUFzQixFQUFFLFlBQVksQ0FBQyxDQUFDO0lBQ3BFLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxlQUFlO1FBQ2pCLElBQUk7WUFDQSxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsZUFBZSxDQUFDLENBQUM7WUFDL0QsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsY0FBYyxDQUFDLENBQUM7WUFFNUQsNkJBQTZCO1lBQzdCLElBQUksV0FBVyxHQUFHLENBQUMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxZQUFZLENBQUMsQ0FBQztZQUMvQyxJQUFJLGNBQWMsR0FBRyxPQUFPLENBQUM7WUFFN0IsSUFBSSxFQUFFLENBQUMsVUFBVSxDQUFDLFdBQVcsQ0FBQyxFQUFFO2dCQUM1QixJQUFJO29CQUNBLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxXQUFXLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQztvQkFDdEUsY0FBYyxHQUFHLFdBQVcsQ0FBQyxPQUFPLENBQUM7aUJBQ3hDO2dCQUFDLE9BQU8sQ0FBQyxFQUFFO29CQUNSLFdBQVcsR0FBRyxJQUFJLENBQUM7aUJBQ3RCO2FBQ0o7aUJBQU07Z0JBQ0gsV0FBVyxHQUFHLElBQUksQ0FBQzthQUN0QjtZQUVELDBCQUEwQjtZQUMxQixNQUFNLFVBQVUsR0FBRyxNQUFNLElBQUksQ0FBQyxzQkFBc0IsRUFBRSxDQUFDO1lBRXZELElBQUksVUFBVSxJQUFJLElBQUksQ0FBQyxVQUFVLEVBQUU7Z0JBQy9CLElBQUksSUFBSSxDQUFDLGVBQWUsQ0FBQyxVQUFVLENBQUMsT0FBTyxFQUFFLGNBQWMsQ0FBQyxHQUFHLENBQUMsRUFBRTtvQkFDOUQsV0FBVyxHQUFHLElBQUksQ0FBQztvQkFDbkIsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUU7d0JBQ2QsbUJBQUcsQ0FBQyxHQUFHLENBQUMsa0JBQWtCLEVBQUUsbUNBQW1DLGNBQWMsTUFBTSxVQUFVLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztxQkFDNUc7aUJBQ0o7YUFDSjtZQUVELElBQUksV0FBVyxJQUFJLFVBQVUsRUFBRTtnQkFDM0IsTUFBTSxJQUFJLENBQUMsd0JBQXdCLENBQUMsVUFBVSxDQUFDLFdBQVcsRUFBRSxZQUFZLEVBQUUsVUFBVSxDQUFDLE9BQU8sQ0FBQyxDQUFDO2FBQ2pHO1lBRUQsOEJBQThCO1lBQzlCLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsWUFBWSxDQUFDLENBQUM7WUFDckQsSUFBSSxZQUFZLEVBQUU7Z0JBQ2QsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQztnQkFDL0MsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsWUFBWSxDQUFDLFlBQVksRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDO2dCQUVwRSxPQUFPO29CQUNILElBQUksRUFBRSxlQUFlO29CQUNyQixPQUFPLEVBQUUsUUFBUSxDQUFDLE9BQU8sSUFBSSxjQUFjO29CQUMzQyxJQUFJLEVBQUUsV0FBVztpQkFDcEIsQ0FBQzthQUNMO1lBRUQsT0FBTyxJQUFJLENBQUM7U0FDZjtRQUFDLE9BQU8sS0FBSyxFQUFFO1lBQ1osSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUU7Z0JBQ2QsbUJBQUcsQ0FBQyxLQUFLLENBQUMsa0JBQWtCLEVBQUUsZ0NBQWdDLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO2FBQ2xGO1lBQ0QsT0FBTyxJQUFJLENBQUM7U0FDZjtJQUNMLENBQUM7SUFFRDs7T0FFRztJQUNLLEtBQUssQ0FBQyxzQkFBc0I7UUFDaEMsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1lBQzNCLE1BQU0sT0FBTyxHQUFHO2dCQUNaLFFBQVEsRUFBRSxnQkFBZ0I7Z0JBQzFCLElBQUksRUFBRSx1Q0FBdUM7Z0JBQzdDLE1BQU0sRUFBRSxLQUFLO2dCQUNiLE9BQU8sRUFBRTtvQkFDTCxZQUFZLEVBQUUscUJBQXFCO29CQUNuQyxRQUFRLEVBQUUsZ0NBQWdDO2lCQUM3QzthQUNKLENBQUM7WUFFRixNQUFNLEdBQUcsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFO2dCQUN2QyxJQUFJLElBQUksR0FBRyxFQUFFLENBQUM7Z0JBQ2QsR0FBRyxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsS0FBSyxDQUFDLEVBQUUsQ0FBQyxJQUFJLElBQUksS0FBSyxDQUFDLENBQUM7Z0JBQ3ZDLEdBQUcsQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRTs7b0JBQ2YsSUFBSTt3QkFDQSxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO3dCQUNqQyxNQUFNLE9BQU8sR0FBRyxDQUFBLE1BQUEsT0FBTyxDQUFDLFFBQVEsMENBQUUsT0FBTyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsS0FBSSxPQUFPLENBQUMsSUFBSSxDQUFDO3dCQUVuRSwwQkFBMEI7d0JBQzFCLE1BQU0sYUFBYSxHQUFHLE1BQUEsT0FBTyxDQUFDLE1BQU0sMENBQUUsSUFBSSxDQUFDLENBQUMsS0FBVSxFQUFFLEVBQUUsQ0FDdEQsS0FBSyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLElBQUksS0FBSyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQ2pFLENBQUM7d0JBRUYsSUFBSSxhQUFhLEVBQUU7NEJBQ2YsT0FBTyxDQUFDO2dDQUNKLE9BQU87Z0NBQ1AsV0FBVyxFQUFFLGFBQWEsQ0FBQyxvQkFBb0I7NkJBQ2xELENBQUMsQ0FBQzt5QkFDTjs2QkFBTTs0QkFDSCw4QkFBOEI7NEJBQzlCLE9BQU8sQ0FBQztnQ0FDSixPQUFPO2dDQUNQLFdBQVcsRUFBRSxHQUFHLG9CQUFvQixJQUFJLE9BQU8sQ0FBQyxRQUFRLFlBQVksT0FBTyxlQUFlOzZCQUM3RixDQUFDLENBQUM7eUJBQ047cUJBQ0o7b0JBQUMsT0FBTyxDQUFDLEVBQUU7d0JBQ1IsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO3FCQUNqQjtnQkFDTCxDQUFDLENBQUMsQ0FBQztZQUNQLENBQUMsQ0FBQyxDQUFDO1lBRUgsR0FBRyxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsR0FBRyxFQUFFLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDckMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxLQUFLLEVBQUUsR0FBRyxFQUFFO2dCQUN2QixHQUFHLENBQUMsT0FBTyxFQUFFLENBQUM7Z0JBQ2QsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ2xCLENBQUMsQ0FBQyxDQUFDO1lBQ0gsR0FBRyxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ2QsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxLQUFLLENBQUMsd0JBQXdCLENBQUMsR0FBVyxFQUFFLFNBQWlCLEVBQUUsT0FBZTtRQUNsRixJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRTtZQUNkLG1CQUFHLENBQUMsR0FBRyxDQUFDLGtCQUFrQixFQUFFLDhCQUE4QixPQUFPLEtBQUssQ0FBQyxDQUFDO1NBQzNFO1FBRUQsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLGlCQUFpQixDQUFDLENBQUM7UUFFNUQsb0JBQW9CO1FBQ3BCLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFFdEMseUJBQXlCO1FBQ3pCLElBQUksRUFBRSxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsRUFBRTtZQUMxQixJQUFJLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1NBQzdCO1FBQ0QsRUFBRSxDQUFDLFNBQVMsQ0FBQyxTQUFTLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUU3QyxrQkFBa0I7UUFDbEIsTUFBTSxJQUFJLENBQUMsVUFBVSxDQUFDLE9BQU8sRUFBRSxTQUFTLENBQUMsQ0FBQztRQUUxQyxvQkFBb0I7UUFDcEIsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsY0FBYyxDQUFDLENBQUM7UUFDekQsRUFBRSxDQUFDLGFBQWEsQ0FBQyxXQUFXLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxZQUFZLEVBQUUsSUFBSSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUVuRyxjQUFjO1FBQ2QsSUFBSTtZQUNBLEVBQUUsQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLENBQUM7U0FDMUI7UUFBQyxPQUFPLENBQUMsRUFBRSxHQUFHO1FBRWYsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUU7WUFDZCxtQkFBRyxDQUFDLEdBQUcsQ0FBQyxrQkFBa0IsRUFBRSxrQkFBa0IsT0FBTyx5QkFBeUIsQ0FBQyxDQUFDO1NBQ25GO0lBQ0wsQ0FBQztJQUVEOztPQUVHO0lBQ0ssWUFBWSxDQUFDLEdBQVcsRUFBRSxRQUFnQjtRQUM5QyxPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFO1lBQ25DLE1BQU0sY0FBYyxHQUFHLENBQUMsR0FBVyxFQUFFLGFBQWEsR0FBRyxDQUFDLEVBQUUsRUFBRTtnQkFDdEQsSUFBSSxhQUFhLEdBQUcsQ0FBQyxFQUFFO29CQUNuQixNQUFNLENBQUMsSUFBSSxLQUFLLENBQUMsb0JBQW9CLENBQUMsQ0FBQyxDQUFDO29CQUN4QyxPQUFPO2lCQUNWO2dCQUVELE1BQU0sUUFBUSxHQUFHLEdBQUcsQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO2dCQUV4RCxRQUFRLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRTtvQkFDZCxPQUFPLEVBQUUsRUFBRSxZQUFZLEVBQUUscUJBQXFCLEVBQUU7aUJBQ25ELEVBQUUsQ0FBQyxRQUFRLEVBQUUsRUFBRTtvQkFDWixtQkFBbUI7b0JBQ25CLElBQUksUUFBUSxDQUFDLFVBQVUsS0FBSyxHQUFHLElBQUksUUFBUSxDQUFDLFVBQVUsS0FBSyxHQUFHLEVBQUU7d0JBQzVELE1BQU0sV0FBVyxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDO3dCQUM5QyxJQUFJLFdBQVcsRUFBRTs0QkFDYixjQUFjLENBQUMsV0FBVyxFQUFFLGFBQWEsR0FBRyxDQUFDLENBQUMsQ0FBQzs0QkFDL0MsT0FBTzt5QkFDVjtxQkFDSjtvQkFFRCxJQUFJLFFBQVEsQ0FBQyxVQUFVLEtBQUssR0FBRyxFQUFFO3dCQUM3QixNQUFNLENBQUMsSUFBSSxLQUFLLENBQUMsK0JBQStCLFFBQVEsQ0FBQyxVQUFVLEVBQUUsQ0FBQyxDQUFDLENBQUM7d0JBQ3hFLE9BQU87cUJBQ1Y7b0JBRUQsTUFBTSxJQUFJLEdBQUcsRUFBRSxDQUFDLGlCQUFpQixDQUFDLFFBQVEsQ0FBQyxDQUFDO29CQUM1QyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO29CQUVwQixJQUFJLENBQUMsRUFBRSxDQUFDLFFBQVEsRUFBRSxHQUFHLEVBQUU7d0JBQ25CLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQzt3QkFDYixPQUFPLEVBQUUsQ0FBQztvQkFDZCxDQUFDLENBQUMsQ0FBQztvQkFFSCxJQUFJLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFO3dCQUNyQixFQUFFLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDO3dCQUN4QixNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7b0JBQ2hCLENBQUMsQ0FBQyxDQUFDO2dCQUNQLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUM7WUFDM0IsQ0FBQyxDQUFDO1lBRUYsY0FBYyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3hCLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVEOztPQUVHO0lBQ0ssS0FBSyxDQUFDLFVBQVUsQ0FBQyxPQUFlLEVBQUUsU0FBaUI7UUFDdkQsTUFBTSxRQUFRLEdBQUcsT0FBTyxDQUFDLFFBQVEsQ0FBQztRQUVsQyxJQUFJO1lBQ0EsSUFBSSxRQUFRLEtBQUssT0FBTyxFQUFFO2dCQUN0Qiw0QkFBNEI7Z0JBQzVCLElBQUEsd0JBQVEsRUFBQyw4Q0FBOEMsT0FBTyx1QkFBdUIsU0FBUyxXQUFXLEVBQUU7b0JBQ3ZHLEtBQUssRUFBRSxRQUFRO2lCQUNsQixDQUFDLENBQUM7YUFDTjtpQkFBTTtnQkFDSCwyQkFBMkI7Z0JBQzNCLElBQUEsd0JBQVEsRUFBQyxhQUFhLE9BQU8sU0FBUyxTQUFTLEdBQUcsRUFBRTtvQkFDaEQsS0FBSyxFQUFFLFFBQVE7aUJBQ2xCLENBQUMsQ0FBQzthQUNOO1NBQ0o7UUFBQyxPQUFPLEtBQUssRUFBRTtZQUNaLE1BQU0sSUFBSSxLQUFLLENBQUMsMEJBQTBCLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1NBQzlEO0lBQ0wsQ0FBQztJQUVEOztPQUVHO0lBQ0ssWUFBWSxDQUFDLEdBQVc7UUFDNUIsSUFBSSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDO1lBQUUsT0FBTyxJQUFJLENBQUM7UUFFckMsMEJBQTBCO1FBQzFCLE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLGVBQWUsQ0FBQyxDQUFDO1FBQ3ZELElBQUksRUFBRSxDQUFDLFVBQVUsQ0FBQyxjQUFjLENBQUMsRUFBRTtZQUMvQixPQUFPLGNBQWMsQ0FBQztTQUN6QjtRQUVELHdEQUF3RDtRQUN4RCxNQUFNLE9BQU8sR0FBRyxFQUFFLENBQUMsV0FBVyxDQUFDLEdBQUcsRUFBRSxFQUFFLGFBQWEsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQzdELEtBQUssTUFBTSxLQUFLLElBQUksT0FBTyxFQUFFO1lBQ3pCLElBQUksS0FBSyxDQUFDLFdBQVcsRUFBRSxFQUFFO2dCQUNyQixNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsSUFBSSxFQUFFLGVBQWUsQ0FBQyxDQUFDO2dCQUNuRSxJQUFJLEVBQUUsQ0FBQyxVQUFVLENBQUMsY0FBYyxDQUFDLEVBQUU7b0JBQy9CLE9BQU8sY0FBYyxDQUFDO2lCQUN6QjthQUNKO1NBQ0o7UUFFRCxPQUFPLElBQUksQ0FBQztJQUNoQixDQUFDO0lBRUQ7O09BRUc7SUFDSyxlQUFlLENBQUMsQ0FBUyxFQUFFLENBQVM7UUFDeEMsTUFBTSxNQUFNLEdBQUcsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMvRCxNQUFNLE1BQU0sR0FBRyxDQUFDLENBQUMsT0FBTyxDQUFDLFNBQVMsRUFBRSxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBRS9ELEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFO1lBQzdELE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDNUIsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUM1QixJQUFJLElBQUksR0FBRyxJQUFJO2dCQUFFLE9BQU8sQ0FBQyxDQUFDO1lBQzFCLElBQUksSUFBSSxHQUFHLElBQUk7Z0JBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQztTQUM5QjtRQUNELE9BQU8sQ0FBQyxDQUFDO0lBQ2IsQ0FBQztJQUVEOztPQUVHO0lBQ0ssU0FBUyxDQUFDLEdBQVc7UUFDekIsTUFBTSxNQUFNLEdBQUksRUFBVSxDQUFDLE1BQU0sSUFBSyxFQUFVLENBQUMsU0FBUyxDQUFDO1FBQzNELElBQUk7WUFDQSxNQUFNLENBQUMsR0FBRyxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztTQUNqRDtRQUFDLE9BQU8sQ0FBQyxFQUFFLEdBQUc7SUFDbkIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsaUJBQWlCLENBQUMsY0FBd0I7UUFDdEMsSUFBSSxjQUFjLENBQUMsTUFBTSxLQUFLLENBQUM7WUFBRSxPQUFPLEVBQUUsQ0FBQztRQUUzQyxPQUFPO1lBQ0gsb0JBQW9CLGNBQWMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUU7WUFDOUMscUJBQXFCO1NBQ3hCLENBQUM7SUFDTixDQUFDO0lBRUQ7O09BRUc7SUFDSCxVQUFVO1FBQ04sSUFBSSxFQUFFLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRTtZQUM5QixJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUM5QixJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRTtnQkFDZCxtQkFBRyxDQUFDLEdBQUcsQ0FBQyxrQkFBa0IsRUFBRSx5QkFBeUIsQ0FBQyxDQUFDO2FBQzFEO1NBQ0o7SUFDTCxDQUFDO0NBQ0o7QUFoVUQsNENBZ1VDO0FBRUQsa0JBQWUsZ0JBQWdCLENBQUMifQ==
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright 2024 Brave Real Launcher Contributors.
|
|
3
|
+
* Licensed under the Apache License, Version 2.0
|
|
4
|
+
*
|
|
5
|
+
* Extension Manager for Brave Real Launcher
|
|
6
|
+
* Handles downloading, caching, and loading browser extensions
|
|
7
|
+
*/
|
|
8
|
+
'use strict';
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import * as https from 'https';
|
|
12
|
+
import * as http from 'http';
|
|
13
|
+
import { execSync } from 'child_process';
|
|
14
|
+
import { homedir } from 'os';
|
|
15
|
+
import log from './logger.mjs';
|
|
16
|
+
const UBLOCK_GITHUB_API = 'https://api.github.com/repos/gorhill/uBlock/releases/latest';
|
|
17
|
+
const UBLOCK_DOWNLOAD_BASE = 'https://github.com/gorhill/uBlock/releases/download';
|
|
18
|
+
export class ExtensionManager {
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
this.cacheDir = options.cacheDir || this.getDefaultCacheDir();
|
|
21
|
+
this.autoUpdate = options.autoUpdate !== false;
|
|
22
|
+
this.silent = options.silent || false;
|
|
23
|
+
// Ensure cache directory exists
|
|
24
|
+
if (!fs.existsSync(this.cacheDir)) {
|
|
25
|
+
fs.mkdirSync(this.cacheDir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
getDefaultCacheDir() {
|
|
29
|
+
const homeDir = homedir();
|
|
30
|
+
return path.join(homeDir, '.brave-real-launcher', 'extensions');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get uBlock Origin extension, downloading if necessary
|
|
34
|
+
*/
|
|
35
|
+
async getUBlockOrigin() {
|
|
36
|
+
try {
|
|
37
|
+
const extensionDir = path.join(this.cacheDir, 'ublock-origin');
|
|
38
|
+
const versionFile = path.join(extensionDir, 'version.json');
|
|
39
|
+
// Check if we need to update
|
|
40
|
+
let needsUpdate = !fs.existsSync(extensionDir);
|
|
41
|
+
let currentVersion = '0.0.0';
|
|
42
|
+
if (fs.existsSync(versionFile)) {
|
|
43
|
+
try {
|
|
44
|
+
const versionData = JSON.parse(fs.readFileSync(versionFile, 'utf-8'));
|
|
45
|
+
currentVersion = versionData.version;
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
needsUpdate = true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
needsUpdate = true;
|
|
53
|
+
}
|
|
54
|
+
// Get latest version info
|
|
55
|
+
const latestInfo = await this.getLatestUBlockVersion();
|
|
56
|
+
if (latestInfo && this.autoUpdate) {
|
|
57
|
+
if (this.compareVersions(latestInfo.version, currentVersion) > 0) {
|
|
58
|
+
needsUpdate = true;
|
|
59
|
+
if (!this.silent) {
|
|
60
|
+
log.log('ExtensionManager', `uBlock Origin update available: ${currentVersion} → ${latestInfo.version}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (needsUpdate && latestInfo) {
|
|
65
|
+
await this.downloadAndExtractUBlock(latestInfo.downloadUrl, extensionDir, latestInfo.version);
|
|
66
|
+
}
|
|
67
|
+
// Find the extension manifest
|
|
68
|
+
const manifestPath = this.findManifest(extensionDir);
|
|
69
|
+
if (manifestPath) {
|
|
70
|
+
const manifestDir = path.dirname(manifestPath);
|
|
71
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
72
|
+
return {
|
|
73
|
+
name: 'uBlock Origin',
|
|
74
|
+
version: manifest.version || currentVersion,
|
|
75
|
+
path: manifestDir
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (!this.silent) {
|
|
82
|
+
log.error('ExtensionManager', `Failed to get uBlock Origin: ${error.message}`);
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get latest uBlock Origin version from GitHub
|
|
89
|
+
*/
|
|
90
|
+
async getLatestUBlockVersion() {
|
|
91
|
+
return new Promise((resolve) => {
|
|
92
|
+
const options = {
|
|
93
|
+
hostname: 'api.github.com',
|
|
94
|
+
path: '/repos/gorhill/uBlock/releases/latest',
|
|
95
|
+
method: 'GET',
|
|
96
|
+
headers: {
|
|
97
|
+
'User-Agent': 'brave-real-launcher',
|
|
98
|
+
'Accept': 'application/vnd.github.v3+json'
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
const req = https.request(options, (res) => {
|
|
102
|
+
let data = '';
|
|
103
|
+
res.on('data', chunk => data += chunk);
|
|
104
|
+
res.on('end', () => {
|
|
105
|
+
var _a, _b;
|
|
106
|
+
try {
|
|
107
|
+
const release = JSON.parse(data);
|
|
108
|
+
const version = ((_a = release.tag_name) === null || _a === void 0 ? void 0 : _a.replace('v', '')) || release.name;
|
|
109
|
+
// Find chromium zip asset
|
|
110
|
+
const chromiumAsset = (_b = release.assets) === null || _b === void 0 ? void 0 : _b.find((asset) => asset.name.includes('chromium') && asset.name.endsWith('.zip'));
|
|
111
|
+
if (chromiumAsset) {
|
|
112
|
+
resolve({
|
|
113
|
+
version,
|
|
114
|
+
downloadUrl: chromiumAsset.browser_download_url
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// Fallback to constructed URL
|
|
119
|
+
resolve({
|
|
120
|
+
version,
|
|
121
|
+
downloadUrl: `${UBLOCK_DOWNLOAD_BASE}/${release.tag_name}/uBlock0_${version}.chromium.zip`
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
resolve(null);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
req.on('error', () => resolve(null));
|
|
131
|
+
req.setTimeout(10000, () => {
|
|
132
|
+
req.destroy();
|
|
133
|
+
resolve(null);
|
|
134
|
+
});
|
|
135
|
+
req.end();
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Download and extract uBlock Origin
|
|
140
|
+
*/
|
|
141
|
+
async downloadAndExtractUBlock(url, targetDir, version) {
|
|
142
|
+
if (!this.silent) {
|
|
143
|
+
log.log('ExtensionManager', `Downloading uBlock Origin v${version}...`);
|
|
144
|
+
}
|
|
145
|
+
const zipPath = path.join(this.cacheDir, 'ublock-temp.zip');
|
|
146
|
+
// Download the file
|
|
147
|
+
await this.downloadFile(url, zipPath);
|
|
148
|
+
// Clean target directory
|
|
149
|
+
if (fs.existsSync(targetDir)) {
|
|
150
|
+
this.removeDir(targetDir);
|
|
151
|
+
}
|
|
152
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
153
|
+
// Extract the zip
|
|
154
|
+
await this.extractZip(zipPath, targetDir);
|
|
155
|
+
// Save version info
|
|
156
|
+
const versionFile = path.join(targetDir, 'version.json');
|
|
157
|
+
fs.writeFileSync(versionFile, JSON.stringify({ version, downloadedAt: new Date().toISOString() }));
|
|
158
|
+
// Cleanup zip
|
|
159
|
+
try {
|
|
160
|
+
fs.unlinkSync(zipPath);
|
|
161
|
+
}
|
|
162
|
+
catch (e) { }
|
|
163
|
+
if (!this.silent) {
|
|
164
|
+
log.log('ExtensionManager', `uBlock Origin v${version} installed successfully`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Download a file from URL
|
|
169
|
+
*/
|
|
170
|
+
downloadFile(url, destPath) {
|
|
171
|
+
return new Promise((resolve, reject) => {
|
|
172
|
+
const followRedirect = (url, redirectCount = 0) => {
|
|
173
|
+
if (redirectCount > 5) {
|
|
174
|
+
reject(new Error('Too many redirects'));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const protocol = url.startsWith('https') ? https : http;
|
|
178
|
+
protocol.get(url, {
|
|
179
|
+
headers: { 'User-Agent': 'brave-real-launcher' }
|
|
180
|
+
}, (response) => {
|
|
181
|
+
// Handle redirects
|
|
182
|
+
if (response.statusCode === 301 || response.statusCode === 302) {
|
|
183
|
+
const redirectUrl = response.headers.location;
|
|
184
|
+
if (redirectUrl) {
|
|
185
|
+
followRedirect(redirectUrl, redirectCount + 1);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (response.statusCode !== 200) {
|
|
190
|
+
reject(new Error(`Download failed with status ${response.statusCode}`));
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const file = fs.createWriteStream(destPath);
|
|
194
|
+
response.pipe(file);
|
|
195
|
+
file.on('finish', () => {
|
|
196
|
+
file.close();
|
|
197
|
+
resolve();
|
|
198
|
+
});
|
|
199
|
+
file.on('error', (err) => {
|
|
200
|
+
fs.unlinkSync(destPath);
|
|
201
|
+
reject(err);
|
|
202
|
+
});
|
|
203
|
+
}).on('error', reject);
|
|
204
|
+
};
|
|
205
|
+
followRedirect(url);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Extract zip file
|
|
210
|
+
*/
|
|
211
|
+
async extractZip(zipPath, targetDir) {
|
|
212
|
+
const platform = process.platform;
|
|
213
|
+
try {
|
|
214
|
+
if (platform === 'win32') {
|
|
215
|
+
// Use PowerShell on Windows
|
|
216
|
+
execSync(`powershell -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${targetDir}' -Force"`, {
|
|
217
|
+
stdio: 'ignore'
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
// Use unzip on Linux/macOS
|
|
222
|
+
execSync(`unzip -o "${zipPath}" -d "${targetDir}"`, {
|
|
223
|
+
stdio: 'ignore'
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
throw new Error(`Failed to extract zip: ${error.message}`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Find manifest.json in extension directory
|
|
233
|
+
*/
|
|
234
|
+
findManifest(dir) {
|
|
235
|
+
if (!fs.existsSync(dir))
|
|
236
|
+
return null;
|
|
237
|
+
// Check current directory
|
|
238
|
+
const directManifest = path.join(dir, 'manifest.json');
|
|
239
|
+
if (fs.existsSync(directManifest)) {
|
|
240
|
+
return directManifest;
|
|
241
|
+
}
|
|
242
|
+
// Check subdirectories (zip might have a nested folder)
|
|
243
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
244
|
+
for (const entry of entries) {
|
|
245
|
+
if (entry.isDirectory()) {
|
|
246
|
+
const nestedManifest = path.join(dir, entry.name, 'manifest.json');
|
|
247
|
+
if (fs.existsSync(nestedManifest)) {
|
|
248
|
+
return nestedManifest;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Compare semantic versions
|
|
256
|
+
*/
|
|
257
|
+
compareVersions(a, b) {
|
|
258
|
+
const partsA = a.replace(/[^\d.]/g, '').split('.').map(Number);
|
|
259
|
+
const partsB = b.replace(/[^\d.]/g, '').split('.').map(Number);
|
|
260
|
+
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
|
|
261
|
+
const numA = partsA[i] || 0;
|
|
262
|
+
const numB = partsB[i] || 0;
|
|
263
|
+
if (numA > numB)
|
|
264
|
+
return 1;
|
|
265
|
+
if (numA < numB)
|
|
266
|
+
return -1;
|
|
267
|
+
}
|
|
268
|
+
return 0;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Remove directory recursively
|
|
272
|
+
*/
|
|
273
|
+
removeDir(dir) {
|
|
274
|
+
const rmSync = fs.rmSync || fs.rmdirSync;
|
|
275
|
+
try {
|
|
276
|
+
rmSync(dir, { recursive: true, force: true });
|
|
277
|
+
}
|
|
278
|
+
catch (e) { }
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Get extension load flags for browser
|
|
282
|
+
*/
|
|
283
|
+
getExtensionFlags(extensionPaths) {
|
|
284
|
+
if (extensionPaths.length === 0)
|
|
285
|
+
return [];
|
|
286
|
+
return [
|
|
287
|
+
`--load-extension=${extensionPaths.join(',')}`,
|
|
288
|
+
'--enable-extensions'
|
|
289
|
+
];
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Clean extension cache
|
|
293
|
+
*/
|
|
294
|
+
cleanCache() {
|
|
295
|
+
if (fs.existsSync(this.cacheDir)) {
|
|
296
|
+
this.removeDir(this.cacheDir);
|
|
297
|
+
if (!this.silent) {
|
|
298
|
+
log.log('ExtensionManager', 'Extension cache cleaned');
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
export default ExtensionManager;
|
|
304
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXh0ZW5zaW9uLW1hbmFnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvZXh0ZW5zaW9uLW1hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HO0FBQ0gsWUFBWSxDQUFDO0FBRWIsT0FBTyxLQUFLLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFDekIsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFDN0IsT0FBTyxLQUFLLEtBQUssTUFBTSxPQUFPLENBQUM7QUFDL0IsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFDN0IsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN6QyxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQzdCLE9BQU8sR0FBRyxNQUFNLGFBQWEsQ0FBQztBQWM5QixNQUFNLGlCQUFpQixHQUFHLDZEQUE2RCxDQUFDO0FBQ3hGLE1BQU0sb0JBQW9CLEdBQUcscURBQXFELENBQUM7QUFFbkYsTUFBTSxPQUFPLGdCQUFnQjtJQUt6QixZQUFZLFVBQW1DLEVBQUU7UUFDN0MsSUFBSSxDQUFDLFFBQVEsR0FBRyxPQUFPLENBQUMsUUFBUSxJQUFJLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1FBQzlELElBQUksQ0FBQyxVQUFVLEdBQUcsT0FBTyxDQUFDLFVBQVUsS0FBSyxLQUFLLENBQUM7UUFDL0MsSUFBSSxDQUFDLE1BQU0sR0FBRyxPQUFPLENBQUMsTUFBTSxJQUFJLEtBQUssQ0FBQztRQUV0QyxnQ0FBZ0M7UUFDaEMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFO1lBQy9CLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1NBQ3BEO0lBQ0wsQ0FBQztJQUVPLGtCQUFrQjtRQUN0QixN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
|