arn-browser 0.1.5 → 0.1.7
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/bin/cli.js +43 -0
- package/bin/install.js +412 -0
- package/package.json +8 -3
- package/src/index.d.ts +16 -6
- package/src/index.js +7 -4
- package/src/utility/deleteDirectory.js +2 -2
- package/src/utility/{multilogin_token_manager.js → mlx_token.js} +32 -43
- package/src/utility/{launchBrowser.d.ts → playwright/pwLaunch.d.ts} +25 -18
- package/src/utility/{launchBrowser.js → playwright/pwLaunch.js} +221 -137
- package/src/{all_routes/routeWithSuperagent.d.ts → utility/playwright/routes/pwRoute.d.ts} +4 -4
- package/src/{all_routes/routeWithSuperagent.js → utility/playwright/routes/pwRoute.js} +2 -2
- package/src/utility/proxy-utility/proxy-chain.js +30 -3
- package/src/utility/proxy-utility/proxy-helper.js +1 -1
- package/src/utility/puppeteer/ppLaunch.d.ts +199 -0
- package/src/utility/puppeteer/ppLaunch.js +723 -0
- package/src/utility/puppeteer/routes/ppRoute.d.ts +64 -0
- package/src/utility/puppeteer/routes/ppRoute.js +326 -0
- /package/src/{human-cursor → utility/playwright/human-cursor}/HumanCursor.js +0 -0
- /package/src/{human-cursor → utility/playwright/human-cursor}/bezier.js +0 -0
- /package/src/{human-cursor → utility/playwright/human-cursor}/index.d.ts +0 -0
- /package/src/{human-cursor → utility/playwright/human-cursor}/index.js +0 -0
- /package/src/{human-cursor → utility/playwright/human-cursor}/randomizer.js +0 -0
- /package/src/{human-cursor → utility/playwright/human-cursor}/tweening.js +0 -0
- /package/src/utility/{playwright-helper.d.ts → playwright/playwright-helper.d.ts} +0 -0
- /package/src/utility/{playwright-helper.js → playwright/playwright-helper.js} +0 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file CLI for arn-browser
|
|
5
|
+
* @description Provides `npx arn-browser install` to download browser binaries.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* npx arn-browser install - Install all browser binaries (Brave, Camoufox, Chromium, Firefox)
|
|
9
|
+
* npx arn-browser help - Show help
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const command = process.argv[2];
|
|
13
|
+
|
|
14
|
+
function showHelp() {
|
|
15
|
+
console.log(`
|
|
16
|
+
arn-browser CLI
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
npx arn-browser <command>
|
|
20
|
+
|
|
21
|
+
Commands:
|
|
22
|
+
install Download and install browser binaries (Brave, Camoufox, Chromium, Firefox)
|
|
23
|
+
help Show this help message
|
|
24
|
+
`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
switch (command) {
|
|
28
|
+
case "install": {
|
|
29
|
+
const { installBrowsers } = await import("./install.js");
|
|
30
|
+
await installBrowsers();
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
case "help":
|
|
34
|
+
case "--help":
|
|
35
|
+
case "-h":
|
|
36
|
+
case undefined:
|
|
37
|
+
showHelp();
|
|
38
|
+
break;
|
|
39
|
+
default:
|
|
40
|
+
console.error(`Unknown command: "${command}"`);
|
|
41
|
+
showHelp();
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
package/bin/install.js
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file install.js
|
|
3
|
+
* @description Cross-platform browser installer for arn-browser.
|
|
4
|
+
* Downloads and extracts Brave, Camoufox, and optionally Chromium to ~/.cache/
|
|
5
|
+
*
|
|
6
|
+
* Works on Linux, macOS, and Windows.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from "fs";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import os from "os";
|
|
12
|
+
import { execSync } from "child_process";
|
|
13
|
+
import { createWriteStream } from "fs";
|
|
14
|
+
|
|
15
|
+
// ==========================================================================
|
|
16
|
+
// CONFIGURATION URLS
|
|
17
|
+
// ==========================================================================
|
|
18
|
+
// OS identifiers used in our dictionaries
|
|
19
|
+
// process.platform: 'win32', 'darwin', 'linux'
|
|
20
|
+
// os.arch(): 'x64', 'arm64'
|
|
21
|
+
|
|
22
|
+
const BRAVE_URLS = {
|
|
23
|
+
linux: {
|
|
24
|
+
x64: "https://github.com/brave/brave-browser/releases/download/v1.87.192/brave-browser-1.87.192-linux-amd64.zip",
|
|
25
|
+
arm64: "https://github.com/brave/brave-browser/releases/download/v1.87.192/brave-browser-1.87.192-linux-arm64.zip",
|
|
26
|
+
},
|
|
27
|
+
darwin: {
|
|
28
|
+
arm64: "https://github.com/brave/brave-browser/releases/download/v1.87.192/Brave-Browser-arm64.dmg",
|
|
29
|
+
},
|
|
30
|
+
win32: {
|
|
31
|
+
x64: "https://github.com/brave/brave-browser/releases/download/v1.87.192/brave-v1.87.192-win32-x64.zip",
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const CAM_URLS = {
|
|
36
|
+
linux: {
|
|
37
|
+
x64: "https://github.com/daijro/camoufox/releases/download/v135.0.1-beta.24/camoufox-135.0.1-beta.24-lin.x86_64.zip",
|
|
38
|
+
arm64: "https://github.com/daijro/camoufox/releases/download/v135.0.1-beta.24/camoufox-135.0.1-beta.24-lin.arm64.zip",
|
|
39
|
+
},
|
|
40
|
+
darwin: {
|
|
41
|
+
arm64: "https://github.com/daijro/camoufox/releases/download/v135.0.1-beta.24/camoufox-135.0.1-beta.24-mac.arm64.zip",
|
|
42
|
+
},
|
|
43
|
+
win32: {
|
|
44
|
+
x64: "https://github.com/daijro/camoufox/releases/download/v135.0.1-beta.24/camoufox-135.0.1-beta.24-win.x86_64.zip",
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Chromium x64 uses Chrome for Testing (CFT) URLs with browserVersion.
|
|
49
|
+
// Chromium arm64 uses legacy revision-based URLs.
|
|
50
|
+
// CDN: cdn.playwright.dev
|
|
51
|
+
const CHROMIUM_REVISION = "1214";
|
|
52
|
+
const CHROMIUM_BROWSER_VERSION = "146.0.7680.31";
|
|
53
|
+
|
|
54
|
+
const CHROMIUM_URLS = {
|
|
55
|
+
linux: {
|
|
56
|
+
x64: `https://cdn.playwright.dev/builds/cft/${CHROMIUM_BROWSER_VERSION}/linux64/chrome-linux64.zip`,
|
|
57
|
+
|
|
58
|
+
arm64: `https://cdn.playwright.dev/dbazure/download/playwright/builds/chromium/${CHROMIUM_REVISION}/chromium-linux-arm64.zip`,
|
|
59
|
+
},
|
|
60
|
+
darwin: {
|
|
61
|
+
arm64: `https://cdn.playwright.dev/builds/cft/${CHROMIUM_BROWSER_VERSION}/mac-arm64/chrome-mac-arm64.zip`,
|
|
62
|
+
},
|
|
63
|
+
win32: {
|
|
64
|
+
x64: `https://cdn.playwright.dev/builds/cft/${CHROMIUM_BROWSER_VERSION}/win64/chrome-win64.zip`,
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Firefox uses revision-based URLs on the new CDN.
|
|
69
|
+
const FIREFOX_REVISION = "1509";
|
|
70
|
+
|
|
71
|
+
const FIREFOX_URLS = {
|
|
72
|
+
linux: {
|
|
73
|
+
x64: `https://cdn.playwright.dev/dbazure/download/playwright/builds/firefox/${FIREFOX_REVISION}/firefox-ubuntu-22.04.zip`,
|
|
74
|
+
arm64: `https://cdn.playwright.dev/dbazure/download/playwright/builds/firefox/${FIREFOX_REVISION}/firefox-ubuntu-22.04-arm64.zip`,
|
|
75
|
+
},
|
|
76
|
+
darwin: {
|
|
77
|
+
arm64: `https://cdn.playwright.dev/dbazure/download/playwright/builds/firefox/${FIREFOX_REVISION}/firefox-mac-arm64.zip`,
|
|
78
|
+
},
|
|
79
|
+
win32: {
|
|
80
|
+
x64: `https://cdn.playwright.dev/dbazure/download/playwright/builds/firefox/${FIREFOX_REVISION}/firefox-win64.zip`,
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const CAM_VERSION = { version: "135.0.1", release: "beta.24" };
|
|
85
|
+
|
|
86
|
+
// ==========================================================================
|
|
87
|
+
// PATHS
|
|
88
|
+
// ==========================================================================
|
|
89
|
+
|
|
90
|
+
const ARN_BROWSERS_DIR = path.join(os.homedir(), ".arn-browser", "browsers");
|
|
91
|
+
const BRAVE_DIR = path.join(ARN_BROWSERS_DIR, "brave");
|
|
92
|
+
const CHROMIUM_DIR = path.join(ARN_BROWSERS_DIR, "chromium");
|
|
93
|
+
const FIREFOX_DIR = path.join(ARN_BROWSERS_DIR, "firefox");
|
|
94
|
+
const CAM_DIR = path.join(os.homedir(), ".cache", "camoufox");
|
|
95
|
+
const TEMP_DIR = path.join(os.tmpdir(), "arn-browser-install");
|
|
96
|
+
|
|
97
|
+
// ==========================================================================
|
|
98
|
+
// HELPERS
|
|
99
|
+
// ==========================================================================
|
|
100
|
+
|
|
101
|
+
function getArch() {
|
|
102
|
+
const arch = os.arch(); // "x64", "arm64", etc.
|
|
103
|
+
if (arch === "x64" || arch === "arm64") return arch;
|
|
104
|
+
throw new Error(`❌ Unsupported architecture: ${arch}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Download a file with redirect following (GitHub releases use 302 redirects).
|
|
109
|
+
*/
|
|
110
|
+
function formatBytes(bytes) {
|
|
111
|
+
if (!Number.isFinite(bytes) || bytes < 0) return "0 B";
|
|
112
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
113
|
+
let value = bytes;
|
|
114
|
+
let unit = 0;
|
|
115
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
116
|
+
value /= 1024;
|
|
117
|
+
unit += 1;
|
|
118
|
+
}
|
|
119
|
+
return `${value.toFixed(value >= 100 || unit === 0 ? 0 : 1)} ${units[unit]}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function formatEta(seconds) {
|
|
123
|
+
if (!Number.isFinite(seconds) || seconds < 0) return "--:--";
|
|
124
|
+
const mins = Math.floor(seconds / 60);
|
|
125
|
+
const secs = Math.floor(seconds % 60);
|
|
126
|
+
return `${String(mins).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function printProgress(label, downloaded, total, startMs) {
|
|
130
|
+
const elapsedSec = Math.max((Date.now() - startMs) / 1000, 0.001);
|
|
131
|
+
const speed = downloaded / elapsedSec;
|
|
132
|
+
if (total > 0) {
|
|
133
|
+
const percent = ((downloaded / total) * 100).toFixed(1);
|
|
134
|
+
const etaSec = (total - downloaded) / Math.max(speed, 1);
|
|
135
|
+
process.stdout.write(
|
|
136
|
+
`\r ${label}: ${percent}% (${formatBytes(downloaded)}/${formatBytes(total)}) ${formatBytes(speed)}/s ETA ${formatEta(etaSec)}`
|
|
137
|
+
);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
process.stdout.write(`\r ${label}: ${formatBytes(downloaded)} ${formatBytes(speed)}/s`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function downloadFile(url, destPath, label) {
|
|
144
|
+
const fetch = (await import("node-fetch")).default;
|
|
145
|
+
|
|
146
|
+
const response = await fetch(url, { redirect: "follow" });
|
|
147
|
+
if (!response.ok) throw new Error(`Failed to download ${url}: ${response.status}`);
|
|
148
|
+
if (!response.body) throw new Error(`No response body for ${url}`);
|
|
149
|
+
|
|
150
|
+
const totalBytes = Number(response.headers.get("content-length")) || 0;
|
|
151
|
+
const fileStream = createWriteStream(destPath);
|
|
152
|
+
const startMs = Date.now();
|
|
153
|
+
let downloaded = 0;
|
|
154
|
+
let lastPrint = 0;
|
|
155
|
+
|
|
156
|
+
await new Promise((resolve, reject) => {
|
|
157
|
+
response.body.on("data", (chunk) => {
|
|
158
|
+
downloaded += chunk.length;
|
|
159
|
+
const now = Date.now();
|
|
160
|
+
if (now - lastPrint >= 200) {
|
|
161
|
+
printProgress(label, downloaded, totalBytes, startMs);
|
|
162
|
+
lastPrint = now;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
response.body.on("error", reject);
|
|
167
|
+
fileStream.on("error", reject);
|
|
168
|
+
fileStream.on("finish", resolve);
|
|
169
|
+
response.body.pipe(fileStream);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
printProgress(label, downloaded, totalBytes, startMs);
|
|
173
|
+
process.stdout.write("\n");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Cross-platform unzip.
|
|
178
|
+
*/
|
|
179
|
+
function unzipFile(zipPath, destDir) {
|
|
180
|
+
const isWindows = process.platform === "win32";
|
|
181
|
+
|
|
182
|
+
if (isWindows) {
|
|
183
|
+
execSync(`powershell -Command "Expand-Archive -Force -Path '${zipPath}' -DestinationPath '${destDir}'"`, { stdio: "inherit" });
|
|
184
|
+
} else {
|
|
185
|
+
execSync(`unzip -qo "${zipPath}" -d "${destDir}"`, { stdio: "inherit" });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* If the extracted zip contains a single top-level directory,
|
|
191
|
+
* move its contents up to destDir (flatten).
|
|
192
|
+
*/
|
|
193
|
+
function flattenExtract(destDir) {
|
|
194
|
+
const entries = fs.readdirSync(destDir, { withFileTypes: true });
|
|
195
|
+
if (entries.length === 1 && entries[0].isDirectory()) {
|
|
196
|
+
const subDir = path.join(destDir, entries[0].name);
|
|
197
|
+
// Rename subfolder to a temp name to avoid name collisions
|
|
198
|
+
// (e.g. firefox/firefox where the dir and executable share a name)
|
|
199
|
+
const tmpDir = subDir + "__tmp";
|
|
200
|
+
fs.renameSync(subDir, tmpDir);
|
|
201
|
+
const subEntries = fs.readdirSync(tmpDir);
|
|
202
|
+
for (const item of subEntries) {
|
|
203
|
+
fs.renameSync(path.join(tmpDir, item), path.join(destDir, item));
|
|
204
|
+
}
|
|
205
|
+
fs.rmdirSync(tmpDir);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Recursively delete a directory.
|
|
211
|
+
*/
|
|
212
|
+
function rmDir(dirPath) {
|
|
213
|
+
if (fs.existsSync(dirPath)) {
|
|
214
|
+
fs.rmSync(dirPath, { recursive: true, force: true });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Set executable permission (no-op on Windows).
|
|
220
|
+
*/
|
|
221
|
+
function setExecutable(dirPath, filename) {
|
|
222
|
+
if (process.platform === "win32") return;
|
|
223
|
+
|
|
224
|
+
const files = findFiles(dirPath, filename);
|
|
225
|
+
for (const f of files) {
|
|
226
|
+
fs.chmodSync(f, 0o755);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Recursively find files by name.
|
|
232
|
+
*/
|
|
233
|
+
function findFiles(dir, name) {
|
|
234
|
+
const results = [];
|
|
235
|
+
if (!fs.existsSync(dir)) return results;
|
|
236
|
+
|
|
237
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
238
|
+
for (const entry of entries) {
|
|
239
|
+
const fullPath = path.join(dir, entry.name);
|
|
240
|
+
if (entry.isDirectory()) {
|
|
241
|
+
results.push(...findFiles(fullPath, name));
|
|
242
|
+
} else if (entry.name === name) {
|
|
243
|
+
results.push(fullPath);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return results;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Helper to get the correct URL for the current platform and architecture.
|
|
251
|
+
*/
|
|
252
|
+
function getDownloadUrl(urlMap, osName, arch) {
|
|
253
|
+
if (!urlMap[osName]) return null;
|
|
254
|
+
return urlMap[osName][arch] || null;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
// ==========================================================================
|
|
259
|
+
// BROWSER INSTALLERS
|
|
260
|
+
// ==========================================================================
|
|
261
|
+
|
|
262
|
+
async function installBrave(osName, arch) {
|
|
263
|
+
const url = getDownloadUrl(BRAVE_URLS, osName, arch);
|
|
264
|
+
if (!url) {
|
|
265
|
+
console.log(`⚠️ Brave not available for ${osName} ${arch}`);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Clean previous install
|
|
270
|
+
rmDir(BRAVE_DIR);
|
|
271
|
+
|
|
272
|
+
const ext = url.endsWith(".dmg") ? ".dmg" : ".zip";
|
|
273
|
+
const dlPath = path.join(TEMP_DIR, `brave${ext}`);
|
|
274
|
+
|
|
275
|
+
console.log("⬇️ Downloading Brave...");
|
|
276
|
+
await downloadFile(url, dlPath, "Brave");
|
|
277
|
+
|
|
278
|
+
fs.mkdirSync(BRAVE_DIR, { recursive: true });
|
|
279
|
+
|
|
280
|
+
console.log(`📦 Extracting Brave to ${BRAVE_DIR}...`);
|
|
281
|
+
if (ext === ".zip") {
|
|
282
|
+
unzipFile(dlPath, BRAVE_DIR);
|
|
283
|
+
flattenExtract(BRAVE_DIR);
|
|
284
|
+
} else if (ext === ".dmg") {
|
|
285
|
+
console.log(`⚠️ Brave downloaded as DMG to ${dlPath}. Automatic extraction of DMG is not fully supported in this script. Recommend manual installation.`);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
console.log("✅ Brave installed!");
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function installCamoufox(osName, arch) {
|
|
292
|
+
const url = getDownloadUrl(CAM_URLS, osName, arch);
|
|
293
|
+
if (!url) {
|
|
294
|
+
console.log(`⚠️ Camoufox not available for ${osName} ${arch}`);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Clean previous install
|
|
299
|
+
rmDir(CAM_DIR);
|
|
300
|
+
|
|
301
|
+
const zipPath = path.join(TEMP_DIR, "camoufox.zip");
|
|
302
|
+
|
|
303
|
+
console.log("⬇️ Downloading Camoufox...");
|
|
304
|
+
await downloadFile(url, zipPath, "Camoufox");
|
|
305
|
+
|
|
306
|
+
fs.mkdirSync(CAM_DIR, { recursive: true });
|
|
307
|
+
|
|
308
|
+
console.log(`📦 Extracting Camoufox to ${CAM_DIR}...`);
|
|
309
|
+
unzipFile(zipPath, CAM_DIR);
|
|
310
|
+
|
|
311
|
+
// Set executable permissions (Linux/Mac)
|
|
312
|
+
setExecutable(CAM_DIR, "camoufox");
|
|
313
|
+
setExecutable(CAM_DIR, "camoufox-bin");
|
|
314
|
+
|
|
315
|
+
// Create version.json
|
|
316
|
+
const versionFile = path.join(CAM_DIR, "version.json");
|
|
317
|
+
if (!fs.existsSync(versionFile)) {
|
|
318
|
+
fs.writeFileSync(versionFile, JSON.stringify(CAM_VERSION, null, 2), "utf-8");
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
console.log("✅ Camoufox installed!");
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async function installChromium(osName, arch) {
|
|
325
|
+
const url = getDownloadUrl(CHROMIUM_URLS, osName, arch);
|
|
326
|
+
if (!url) {
|
|
327
|
+
console.log(`⚠️ Chromium not available for ${osName} ${arch}`);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Clean previous install
|
|
332
|
+
rmDir(CHROMIUM_DIR);
|
|
333
|
+
|
|
334
|
+
const ext = url.endsWith(".dmg") ? ".dmg" : ".zip";
|
|
335
|
+
const dlPath = path.join(TEMP_DIR, `chromium${ext}`);
|
|
336
|
+
|
|
337
|
+
console.log("⬇️ Downloading Chromium...");
|
|
338
|
+
await downloadFile(url, dlPath, "Chromium");
|
|
339
|
+
|
|
340
|
+
fs.mkdirSync(CHROMIUM_DIR, { recursive: true });
|
|
341
|
+
|
|
342
|
+
console.log(`📦 Extracting Chromium to ${CHROMIUM_DIR}...`);
|
|
343
|
+
if (ext === ".zip") {
|
|
344
|
+
unzipFile(dlPath, CHROMIUM_DIR);
|
|
345
|
+
flattenExtract(CHROMIUM_DIR);
|
|
346
|
+
} else {
|
|
347
|
+
console.log(`⚠️ Chromium downloaded as DMG to ${dlPath}. Manual extraction required.`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
console.log("✅ Chromium installed!");
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async function installFirefox(osName, arch) {
|
|
354
|
+
const url = getDownloadUrl(FIREFOX_URLS, osName, arch);
|
|
355
|
+
if (!url) {
|
|
356
|
+
console.log(`⚠️ Firefox not available for ${osName} ${arch}`);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Clean previous install
|
|
361
|
+
rmDir(FIREFOX_DIR);
|
|
362
|
+
|
|
363
|
+
const ext = url.endsWith(".dmg") ? ".dmg" : ".zip";
|
|
364
|
+
const dlPath = path.join(TEMP_DIR, `firefox${ext}`);
|
|
365
|
+
|
|
366
|
+
console.log("⬇️ Downloading Firefox...");
|
|
367
|
+
await downloadFile(url, dlPath, "Firefox");
|
|
368
|
+
|
|
369
|
+
fs.mkdirSync(FIREFOX_DIR, { recursive: true });
|
|
370
|
+
|
|
371
|
+
console.log(`📦 Extracting Firefox to ${FIREFOX_DIR}...`);
|
|
372
|
+
if (ext === ".zip") {
|
|
373
|
+
unzipFile(dlPath, FIREFOX_DIR);
|
|
374
|
+
flattenExtract(FIREFOX_DIR);
|
|
375
|
+
} else {
|
|
376
|
+
console.log(`⚠️ Firefox downloaded as DMG to ${dlPath}. Manual extraction required.`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
console.log("✅ Firefox installed!");
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// ==========================================================================
|
|
383
|
+
// MAIN
|
|
384
|
+
// ==========================================================================
|
|
385
|
+
|
|
386
|
+
export async function installBrowsers() {
|
|
387
|
+
const arch = getArch();
|
|
388
|
+
const osName = process.platform;
|
|
389
|
+
|
|
390
|
+
console.log(`\n🚀 arn-browser: Installing browser binaries`);
|
|
391
|
+
console.log(` Platform: ${osName} | Architecture: ${arch}\n`);
|
|
392
|
+
|
|
393
|
+
// Prepare temp directory
|
|
394
|
+
fs.mkdirSync(TEMP_DIR, { recursive: true });
|
|
395
|
+
|
|
396
|
+
try {
|
|
397
|
+
await installBrave(osName, arch);
|
|
398
|
+
await installCamoufox(osName, arch);
|
|
399
|
+
await installChromium(osName, arch);
|
|
400
|
+
await installFirefox(osName, arch);
|
|
401
|
+
} finally {
|
|
402
|
+
// Cleanup temp directory
|
|
403
|
+
rmDir(TEMP_DIR);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
console.log(`\n🎉 Done!`);
|
|
407
|
+
console.log(` Brave: ${BRAVE_DIR}`);
|
|
408
|
+
console.log(` Camoufox: ${CAM_DIR}`);
|
|
409
|
+
console.log(` Chromium: ${CHROMIUM_DIR}`);
|
|
410
|
+
console.log(` Firefox: ${FIREFOX_DIR}`);
|
|
411
|
+
console.log(` Version: ${CAM_DIR}/version.json\n`);
|
|
412
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arn-browser",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "A lightweight, browser autmation helper.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
7
7
|
"files": [
|
|
8
|
-
"src"
|
|
8
|
+
"src",
|
|
9
|
+
"bin"
|
|
9
10
|
],
|
|
10
11
|
"directories": {
|
|
11
12
|
"test": "test"
|
|
@@ -21,13 +22,17 @@
|
|
|
21
22
|
"https-proxy-agent": "^7.0.6",
|
|
22
23
|
"node-cache": "^5.1.2",
|
|
23
24
|
"node-fetch": "^3.3.2",
|
|
24
|
-
"playwright": "^1.
|
|
25
|
+
"playwright": "^1.42.1",
|
|
25
26
|
"proxy-chain": "^2.6.0",
|
|
27
|
+
"puppeteer-core": "^24.38.0",
|
|
26
28
|
"randomstring": "^1.3.1",
|
|
27
29
|
"socks-proxy-agent": "^8.0.5",
|
|
28
30
|
"speakeasy": "^2.0.0",
|
|
29
31
|
"superagent": "^10.2.3"
|
|
30
32
|
},
|
|
33
|
+
"bin": {
|
|
34
|
+
"arn-browser": "bin/cli.js"
|
|
35
|
+
},
|
|
31
36
|
"scripts": {
|
|
32
37
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
33
38
|
},
|
package/src/index.d.ts
CHANGED
|
@@ -2,18 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
export * from "./utility/proxy-utility/custom-proxy";
|
|
4
4
|
export * from "./utility/proxy-utility/proxy-chain";
|
|
5
|
-
export * from "./
|
|
5
|
+
export * from "./utility/playwright/routes/pwRoute";
|
|
6
|
+
export * from "./utility/puppeteer/routes/ppRoute";
|
|
6
7
|
export {
|
|
7
8
|
type CamoufoxOptions,
|
|
8
9
|
type MultiloginOptions,
|
|
9
|
-
type LaunchOptions,
|
|
10
|
-
type BrowserController,
|
|
11
|
-
|
|
10
|
+
type PwLaunchOptions as LaunchOptions, // Keeping backward compatibility if needed, though we should probably switch them out in tests
|
|
11
|
+
type PwBrowserController as BrowserController,
|
|
12
|
+
pwLaunch
|
|
12
13
|
// ProxyConfig is excluded - already exported from "./utility/proxy-utility/proxy-chain"
|
|
13
|
-
} from "./utility/
|
|
14
|
+
} from "./utility/playwright/pwLaunch";
|
|
14
15
|
export * from "./utility/proxy-utility/proxy-helper";
|
|
15
16
|
|
|
16
17
|
export * from "./others/totp-generator";
|
|
17
18
|
|
|
18
|
-
export * from "./utility/playwright-helper";
|
|
19
|
+
export * from "./utility/playwright/playwright-helper";
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
type PpLaunchOptions as LaunchPuppeteerOptions,
|
|
23
|
+
type PpBrowserController as PuppeteerController,
|
|
24
|
+
type PpBrowserControllerSuccess as PuppeteerControllerSuccess,
|
|
25
|
+
type PpBrowserControllerError as PuppeteerControllerError,
|
|
26
|
+
type PpMultiloginOptions as PuppeteerMultiloginOptions,
|
|
27
|
+
ppLaunch
|
|
28
|
+
} from "./utility/puppeteer/ppLaunch";
|
|
19
29
|
//
|
package/src/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
export * from "./utility/launchBrowser.js";
|
|
1
|
+
export * from "./utility/playwright/pwLaunch.js";
|
|
3
2
|
|
|
4
3
|
// Export the routing/request interception logic
|
|
5
|
-
export * from "./
|
|
4
|
+
export * from "./utility/playwright/routes/pwRoute.js";
|
|
6
5
|
|
|
7
6
|
export * from "./utility/proxy-utility/proxy-chain.js";
|
|
8
7
|
|
|
@@ -12,4 +11,8 @@ export * from "./utility/proxy-utility/proxy-helper.js";
|
|
|
12
11
|
|
|
13
12
|
export * from "./others/totp-generator.js";
|
|
14
13
|
|
|
15
|
-
export * from "./utility/playwright-helper.js";
|
|
14
|
+
export * from "./utility/playwright/playwright-helper.js";
|
|
15
|
+
|
|
16
|
+
export * from "./utility/puppeteer/ppLaunch.js";
|
|
17
|
+
|
|
18
|
+
export * from "./utility/puppeteer/routes/ppRoute.js";
|
|
@@ -31,14 +31,14 @@ export async function deleteDirectoryWithRetries(targetPath, maxRetries = 5, ret
|
|
|
31
31
|
|
|
32
32
|
// If it's the last attempt, log error
|
|
33
33
|
if (isLastAttempt) {
|
|
34
|
-
console.error(
|
|
34
|
+
console.error(`░░░░░ Failed to delete directory after ${maxRetries} attempts: ${targetPath}`);
|
|
35
35
|
console.error(` Error: ${error.message}`);
|
|
36
36
|
return false;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
// Log warning and wait
|
|
40
40
|
console.warn(
|
|
41
|
-
|
|
41
|
+
`░░░░░ Delete failed (Attempt ${attempt + 1}/${maxRetries}). File might be locked. Retrying in ${retryDelayMs / 1000
|
|
42
42
|
}s...`
|
|
43
43
|
);
|
|
44
44
|
await sleep(retryDelayMs);
|