arn-browser 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/bin/cli.js +43 -0
  2. package/bin/install.js +420 -0
  3. package/package.json +8 -3
  4. package/src/index.d.ts +16 -6
  5. package/src/index.js +7 -4
  6. package/src/utility/{multilogin_token_manager.js → mlx_token.js} +32 -43
  7. package/src/utility/{launchBrowser.d.ts → playwright/pwLaunch.d.ts} +15 -7
  8. package/src/utility/{launchBrowser.js → playwright/pwLaunch.js} +61 -30
  9. package/src/{all_routes/routeWithSuperagent.d.ts → utility/playwright/routes/pwRoute.d.ts} +4 -4
  10. package/src/{all_routes/routeWithSuperagent.js → utility/playwright/routes/pwRoute.js} +2 -2
  11. package/src/utility/proxy-utility/proxy-chain.js +4 -3
  12. package/src/utility/proxy-utility/proxy-helper.js +1 -1
  13. package/src/utility/puppeteer/ppLaunch.d.ts +199 -0
  14. package/src/utility/puppeteer/ppLaunch.js +723 -0
  15. package/src/utility/puppeteer/routes/ppRoute.d.ts +64 -0
  16. package/src/utility/puppeteer/routes/ppRoute.js +326 -0
  17. /package/src/{human-cursor → utility/playwright/human-cursor}/HumanCursor.js +0 -0
  18. /package/src/{human-cursor → utility/playwright/human-cursor}/bezier.js +0 -0
  19. /package/src/{human-cursor → utility/playwright/human-cursor}/index.d.ts +0 -0
  20. /package/src/{human-cursor → utility/playwright/human-cursor}/index.js +0 -0
  21. /package/src/{human-cursor → utility/playwright/human-cursor}/randomizer.js +0 -0
  22. /package/src/{human-cursor → utility/playwright/human-cursor}/tweening.js +0 -0
  23. /package/src/utility/{playwright-helper.d.ts → playwright/playwright-helper.d.ts} +0 -0
  24. /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,420 @@
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
+ // ==========================================================================
10
+ // INSTALL TOGGLES — set to false to skip a browser
11
+ // ==========================================================================
12
+ const INSTALL_BRAVE = true;
13
+ const INSTALL_CAMOUFOX = true;
14
+ const INSTALL_CHROMIUM = true;
15
+ const INSTALL_FIREFOX = true;
16
+
17
+ import fs from "fs";
18
+ import path from "path";
19
+ import os from "os";
20
+ import { execSync } from "child_process";
21
+ import { createWriteStream } from "fs";
22
+
23
+ // ==========================================================================
24
+ // CONFIGURATION URLS
25
+ // ==========================================================================
26
+ // OS identifiers used in our dictionaries
27
+ // process.platform: 'win32', 'darwin', 'linux'
28
+ // os.arch(): 'x64', 'arm64'
29
+
30
+ const BRAVE_URLS = {
31
+ linux: {
32
+ x64: "https://github.com/brave/brave-browser/releases/download/v1.87.192/brave-browser-1.87.192-linux-amd64.zip",
33
+ arm64: "https://github.com/brave/brave-browser/releases/download/v1.87.192/brave-browser-1.87.192-linux-arm64.zip",
34
+ },
35
+ darwin: {
36
+ arm64: "https://github.com/brave/brave-browser/releases/download/v1.87.192/Brave-Browser-arm64.dmg",
37
+ },
38
+ win32: {
39
+ x64: "https://github.com/brave/brave-browser/releases/download/v1.87.192/brave-v1.87.192-win32-x64.zip",
40
+ }
41
+ };
42
+
43
+ const CAM_URLS = {
44
+ linux: {
45
+ x64: "https://github.com/daijro/camoufox/releases/download/v135.0.1-beta.24/camoufox-135.0.1-beta.24-lin.x86_64.zip",
46
+ arm64: "https://github.com/daijro/camoufox/releases/download/v135.0.1-beta.24/camoufox-135.0.1-beta.24-lin.arm64.zip",
47
+ },
48
+ darwin: {
49
+ arm64: "https://github.com/daijro/camoufox/releases/download/v135.0.1-beta.24/camoufox-135.0.1-beta.24-mac.arm64.zip",
50
+ },
51
+ win32: {
52
+ x64: "https://github.com/daijro/camoufox/releases/download/v135.0.1-beta.24/camoufox-135.0.1-beta.24-win.x86_64.zip",
53
+ }
54
+ };
55
+
56
+ // Chromium x64 uses Chrome for Testing (CFT) URLs with browserVersion.
57
+ // Chromium arm64 uses legacy revision-based URLs.
58
+ // CDN: cdn.playwright.dev
59
+ const CHROMIUM_REVISION = "1214";
60
+ const CHROMIUM_BROWSER_VERSION = "146.0.7680.31";
61
+
62
+ const CHROMIUM_URLS = {
63
+ linux: {
64
+ x64: `https://cdn.playwright.dev/builds/cft/${CHROMIUM_BROWSER_VERSION}/linux64/chrome-linux64.zip`,
65
+
66
+ arm64: `https://cdn.playwright.dev/dbazure/download/playwright/builds/chromium/${CHROMIUM_REVISION}/chromium-linux-arm64.zip`,
67
+ },
68
+ darwin: {
69
+ arm64: `https://cdn.playwright.dev/builds/cft/${CHROMIUM_BROWSER_VERSION}/mac-arm64/chrome-mac-arm64.zip`,
70
+ },
71
+ win32: {
72
+ x64: `https://cdn.playwright.dev/builds/cft/${CHROMIUM_BROWSER_VERSION}/win64/chrome-win64.zip`,
73
+ }
74
+ };
75
+
76
+ // Firefox uses revision-based URLs on the new CDN.
77
+ const FIREFOX_REVISION = "1509";
78
+
79
+ const FIREFOX_URLS = {
80
+ linux: {
81
+ x64: `https://cdn.playwright.dev/dbazure/download/playwright/builds/firefox/${FIREFOX_REVISION}/firefox-ubuntu-22.04.zip`,
82
+ arm64: `https://cdn.playwright.dev/dbazure/download/playwright/builds/firefox/${FIREFOX_REVISION}/firefox-ubuntu-22.04-arm64.zip`,
83
+ },
84
+ darwin: {
85
+ arm64: `https://cdn.playwright.dev/dbazure/download/playwright/builds/firefox/${FIREFOX_REVISION}/firefox-mac-arm64.zip`,
86
+ },
87
+ win32: {
88
+ x64: `https://cdn.playwright.dev/dbazure/download/playwright/builds/firefox/${FIREFOX_REVISION}/firefox-win64.zip`,
89
+ }
90
+ };
91
+
92
+ const CAM_VERSION = { version: "135.0.1", release: "beta.24" };
93
+
94
+ // ==========================================================================
95
+ // PATHS
96
+ // ==========================================================================
97
+
98
+ const ARN_BROWSERS_DIR = path.join(os.homedir(), ".arn-browser", "browsers");
99
+ const BRAVE_DIR = path.join(ARN_BROWSERS_DIR, "brave");
100
+ const CHROMIUM_DIR = path.join(ARN_BROWSERS_DIR, "chromium");
101
+ const FIREFOX_DIR = path.join(ARN_BROWSERS_DIR, "firefox");
102
+ const CAM_DIR = path.join(os.homedir(), ".cache", "camoufox");
103
+ const TEMP_DIR = path.join(os.tmpdir(), "arn-browser-install");
104
+
105
+ // ==========================================================================
106
+ // HELPERS
107
+ // ==========================================================================
108
+
109
+ function getArch() {
110
+ const arch = os.arch(); // "x64", "arm64", etc.
111
+ if (arch === "x64" || arch === "arm64") return arch;
112
+ throw new Error(`❌ Unsupported architecture: ${arch}`);
113
+ }
114
+
115
+ /**
116
+ * Download a file with redirect following (GitHub releases use 302 redirects).
117
+ */
118
+ function formatBytes(bytes) {
119
+ if (!Number.isFinite(bytes) || bytes < 0) return "0 B";
120
+ const units = ["B", "KB", "MB", "GB", "TB"];
121
+ let value = bytes;
122
+ let unit = 0;
123
+ while (value >= 1024 && unit < units.length - 1) {
124
+ value /= 1024;
125
+ unit += 1;
126
+ }
127
+ return `${value.toFixed(value >= 100 || unit === 0 ? 0 : 1)} ${units[unit]}`;
128
+ }
129
+
130
+ function formatEta(seconds) {
131
+ if (!Number.isFinite(seconds) || seconds < 0) return "--:--";
132
+ const mins = Math.floor(seconds / 60);
133
+ const secs = Math.floor(seconds % 60);
134
+ return `${String(mins).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
135
+ }
136
+
137
+ function printProgress(label, downloaded, total, startMs) {
138
+ const elapsedSec = Math.max((Date.now() - startMs) / 1000, 0.001);
139
+ const speed = downloaded / elapsedSec;
140
+ if (total > 0) {
141
+ const percent = ((downloaded / total) * 100).toFixed(1);
142
+ const etaSec = (total - downloaded) / Math.max(speed, 1);
143
+ process.stdout.write(
144
+ `\r ${label}: ${percent}% (${formatBytes(downloaded)}/${formatBytes(total)}) ${formatBytes(speed)}/s ETA ${formatEta(etaSec)}`
145
+ );
146
+ return;
147
+ }
148
+ process.stdout.write(`\r ${label}: ${formatBytes(downloaded)} ${formatBytes(speed)}/s`);
149
+ }
150
+
151
+ async function downloadFile(url, destPath, label) {
152
+ const fetch = (await import("node-fetch")).default;
153
+
154
+ const response = await fetch(url, { redirect: "follow" });
155
+ if (!response.ok) throw new Error(`Failed to download ${url}: ${response.status}`);
156
+ if (!response.body) throw new Error(`No response body for ${url}`);
157
+
158
+ const totalBytes = Number(response.headers.get("content-length")) || 0;
159
+ const fileStream = createWriteStream(destPath);
160
+ const startMs = Date.now();
161
+ let downloaded = 0;
162
+ let lastPrint = 0;
163
+
164
+ await new Promise((resolve, reject) => {
165
+ response.body.on("data", (chunk) => {
166
+ downloaded += chunk.length;
167
+ const now = Date.now();
168
+ if (now - lastPrint >= 200) {
169
+ printProgress(label, downloaded, totalBytes, startMs);
170
+ lastPrint = now;
171
+ }
172
+ });
173
+
174
+ response.body.on("error", reject);
175
+ fileStream.on("error", reject);
176
+ fileStream.on("finish", resolve);
177
+ response.body.pipe(fileStream);
178
+ });
179
+
180
+ printProgress(label, downloaded, totalBytes, startMs);
181
+ process.stdout.write("\n");
182
+ }
183
+
184
+ /**
185
+ * Cross-platform unzip.
186
+ */
187
+ function unzipFile(zipPath, destDir) {
188
+ const isWindows = process.platform === "win32";
189
+
190
+ if (isWindows) {
191
+ execSync(`powershell -Command "Expand-Archive -Force -Path '${zipPath}' -DestinationPath '${destDir}'"`, { stdio: "inherit" });
192
+ } else {
193
+ execSync(`unzip -qo "${zipPath}" -d "${destDir}"`, { stdio: "inherit" });
194
+ }
195
+ }
196
+
197
+ /**
198
+ * If the extracted zip contains a single top-level directory,
199
+ * move its contents up to destDir (flatten).
200
+ */
201
+ function flattenExtract(destDir) {
202
+ const entries = fs.readdirSync(destDir, { withFileTypes: true });
203
+ if (entries.length === 1 && entries[0].isDirectory()) {
204
+ const subDir = path.join(destDir, entries[0].name);
205
+ // Rename subfolder to a temp name to avoid name collisions
206
+ // (e.g. firefox/firefox where the dir and executable share a name)
207
+ const tmpDir = subDir + "__tmp";
208
+ fs.renameSync(subDir, tmpDir);
209
+ const subEntries = fs.readdirSync(tmpDir);
210
+ for (const item of subEntries) {
211
+ fs.renameSync(path.join(tmpDir, item), path.join(destDir, item));
212
+ }
213
+ fs.rmdirSync(tmpDir);
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Recursively delete a directory.
219
+ */
220
+ function rmDir(dirPath) {
221
+ if (fs.existsSync(dirPath)) {
222
+ fs.rmSync(dirPath, { recursive: true, force: true });
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Set executable permission (no-op on Windows).
228
+ */
229
+ function setExecutable(dirPath, filename) {
230
+ if (process.platform === "win32") return;
231
+
232
+ const files = findFiles(dirPath, filename);
233
+ for (const f of files) {
234
+ fs.chmodSync(f, 0o755);
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Recursively find files by name.
240
+ */
241
+ function findFiles(dir, name) {
242
+ const results = [];
243
+ if (!fs.existsSync(dir)) return results;
244
+
245
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
246
+ for (const entry of entries) {
247
+ const fullPath = path.join(dir, entry.name);
248
+ if (entry.isDirectory()) {
249
+ results.push(...findFiles(fullPath, name));
250
+ } else if (entry.name === name) {
251
+ results.push(fullPath);
252
+ }
253
+ }
254
+ return results;
255
+ }
256
+
257
+ /**
258
+ * Helper to get the correct URL for the current platform and architecture.
259
+ */
260
+ function getDownloadUrl(urlMap, osName, arch) {
261
+ if (!urlMap[osName]) return null;
262
+ return urlMap[osName][arch] || null;
263
+ }
264
+
265
+
266
+ // ==========================================================================
267
+ // BROWSER INSTALLERS
268
+ // ==========================================================================
269
+
270
+ async function installBrave(osName, arch) {
271
+ const url = getDownloadUrl(BRAVE_URLS, osName, arch);
272
+ if (!url) {
273
+ console.log(`⚠️ Brave not available for ${osName} ${arch}`);
274
+ return;
275
+ }
276
+
277
+ // Clean previous install
278
+ rmDir(BRAVE_DIR);
279
+
280
+ const ext = url.endsWith(".dmg") ? ".dmg" : ".zip";
281
+ const dlPath = path.join(TEMP_DIR, `brave${ext}`);
282
+
283
+ console.log("⬇️ Downloading Brave...");
284
+ await downloadFile(url, dlPath, "Brave");
285
+
286
+ fs.mkdirSync(BRAVE_DIR, { recursive: true });
287
+
288
+ console.log(`📦 Extracting Brave to ${BRAVE_DIR}...`);
289
+ if (ext === ".zip") {
290
+ unzipFile(dlPath, BRAVE_DIR);
291
+ flattenExtract(BRAVE_DIR);
292
+ } else if (ext === ".dmg") {
293
+ console.log(`⚠️ Brave downloaded as DMG to ${dlPath}. Automatic extraction of DMG is not fully supported in this script. Recommend manual installation.`);
294
+ }
295
+
296
+ console.log("✅ Brave installed!");
297
+ }
298
+
299
+ async function installCamoufox(osName, arch) {
300
+ const url = getDownloadUrl(CAM_URLS, osName, arch);
301
+ if (!url) {
302
+ console.log(`⚠️ Camoufox not available for ${osName} ${arch}`);
303
+ return;
304
+ }
305
+
306
+ // Clean previous install
307
+ rmDir(CAM_DIR);
308
+
309
+ const zipPath = path.join(TEMP_DIR, "camoufox.zip");
310
+
311
+ console.log("⬇️ Downloading Camoufox...");
312
+ await downloadFile(url, zipPath, "Camoufox");
313
+
314
+ fs.mkdirSync(CAM_DIR, { recursive: true });
315
+
316
+ console.log(`📦 Extracting Camoufox to ${CAM_DIR}...`);
317
+ unzipFile(zipPath, CAM_DIR);
318
+
319
+ // Set executable permissions (Linux/Mac)
320
+ setExecutable(CAM_DIR, "camoufox");
321
+ setExecutable(CAM_DIR, "camoufox-bin");
322
+
323
+ // Create version.json
324
+ const versionFile = path.join(CAM_DIR, "version.json");
325
+ if (!fs.existsSync(versionFile)) {
326
+ fs.writeFileSync(versionFile, JSON.stringify(CAM_VERSION, null, 2), "utf-8");
327
+ }
328
+
329
+ console.log("✅ Camoufox installed!");
330
+ }
331
+
332
+ async function installChromium(osName, arch) {
333
+ const url = getDownloadUrl(CHROMIUM_URLS, osName, arch);
334
+ if (!url) {
335
+ console.log(`⚠️ Chromium not available for ${osName} ${arch}`);
336
+ return;
337
+ }
338
+
339
+ // Clean previous install
340
+ rmDir(CHROMIUM_DIR);
341
+
342
+ const ext = url.endsWith(".dmg") ? ".dmg" : ".zip";
343
+ const dlPath = path.join(TEMP_DIR, `chromium${ext}`);
344
+
345
+ console.log("⬇️ Downloading Chromium...");
346
+ await downloadFile(url, dlPath, "Chromium");
347
+
348
+ fs.mkdirSync(CHROMIUM_DIR, { recursive: true });
349
+
350
+ console.log(`📦 Extracting Chromium to ${CHROMIUM_DIR}...`);
351
+ if (ext === ".zip") {
352
+ unzipFile(dlPath, CHROMIUM_DIR);
353
+ flattenExtract(CHROMIUM_DIR);
354
+ } else {
355
+ console.log(`⚠️ Chromium downloaded as DMG to ${dlPath}. Manual extraction required.`);
356
+ }
357
+
358
+ console.log("✅ Chromium installed!");
359
+ }
360
+
361
+ async function installFirefox(osName, arch) {
362
+ const url = getDownloadUrl(FIREFOX_URLS, osName, arch);
363
+ if (!url) {
364
+ console.log(`⚠️ Firefox not available for ${osName} ${arch}`);
365
+ return;
366
+ }
367
+
368
+ // Clean previous install
369
+ rmDir(FIREFOX_DIR);
370
+
371
+ const ext = url.endsWith(".dmg") ? ".dmg" : ".zip";
372
+ const dlPath = path.join(TEMP_DIR, `firefox${ext}`);
373
+
374
+ console.log("⬇️ Downloading Firefox...");
375
+ await downloadFile(url, dlPath, "Firefox");
376
+
377
+ fs.mkdirSync(FIREFOX_DIR, { recursive: true });
378
+
379
+ console.log(`📦 Extracting Firefox to ${FIREFOX_DIR}...`);
380
+ if (ext === ".zip") {
381
+ unzipFile(dlPath, FIREFOX_DIR);
382
+ flattenExtract(FIREFOX_DIR);
383
+ } else {
384
+ console.log(`⚠️ Firefox downloaded as DMG to ${dlPath}. Manual extraction required.`);
385
+ }
386
+
387
+ console.log("✅ Firefox installed!");
388
+ }
389
+
390
+ // ==========================================================================
391
+ // MAIN
392
+ // ==========================================================================
393
+
394
+ export async function installBrowsers() {
395
+ const arch = getArch();
396
+ const osName = process.platform;
397
+
398
+ console.log(`\n🚀 arn-browser: Installing browser binaries`);
399
+ console.log(` Platform: ${osName} | Architecture: ${arch}\n`);
400
+
401
+ // Prepare temp directory
402
+ fs.mkdirSync(TEMP_DIR, { recursive: true });
403
+
404
+ try {
405
+ if (INSTALL_BRAVE) await installBrave(osName, arch);
406
+ if (INSTALL_CAMOUFOX) await installCamoufox(osName, arch);
407
+ if (INSTALL_CHROMIUM) await installChromium(osName, arch);
408
+ if (INSTALL_FIREFOX) await installFirefox(osName, arch);
409
+ } finally {
410
+ // Cleanup temp directory
411
+ rmDir(TEMP_DIR);
412
+ }
413
+
414
+ console.log(`\n🎉 Done!`);
415
+ console.log(` Brave: ${BRAVE_DIR}`);
416
+ console.log(` Camoufox: ${CAM_DIR}`);
417
+ console.log(` Chromium: ${CHROMIUM_DIR}`);
418
+ console.log(` Firefox: ${FIREFOX_DIR}`);
419
+ console.log(` Version: ${CAM_DIR}/version.json\n`);
420
+ }
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "arn-browser",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
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.57.0",
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 "./all_routes/routeWithSuperagent";
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
- launchBrowser
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/launchBrowser";
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
- // Export the browser launcher (and any other exports from launchBrowser.js)
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 "./all_routes/routeWithSuperagent.js";
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";
@@ -1,61 +1,51 @@
1
- // multilogin_token_manager.js
1
+ // mlx_token.js
2
2
 
3
3
  import crypto from "crypto";
4
4
  import { arn, query } from "arn-knexjs";
5
5
 
6
- // Environment variables for Multilogin
7
- const MULTILOGIN_EMAIL = process.env.MULTILOGIN_EMAIL;
8
- const MULTILOGIN_PASSWORD = process.env.MULTILOGIN_PASSWORD;
9
- const MULTILOGIN_WORKSPACE_ID = process.env.MULTILOGIN_WORKSPACE_ID;
10
- const MULTILOGIN_ROW_ID = process.env.MULTILOGIN_ROW_ID;
6
+ // Module-level cache for credentials (fetched once per process)
7
+ let _credentials = null;
11
8
 
12
9
  /**
13
- * Validates that all required environment variables are set.
10
+ * Loads Multilogin credentials from the database.
11
+ * Queries `api_multilogin_token` for a single row where `status = 1`.
12
+ * Results are cached in-memory so the DB is only hit once per process.
13
+ * @returns {Promise<{id: string, email: string, password: string, workspace_id: string, data: object}>}
14
14
  */
15
- function validateEnv() {
16
- const required = {
17
- MULTILOGIN_EMAIL,
18
- MULTILOGIN_PASSWORD,
19
- MULTILOGIN_WORKSPACE_ID,
20
- MULTILOGIN_ROW_ID,
21
- };
15
+ async function loadCredentials() {
16
+ if (_credentials) return _credentials;
22
17
 
23
- const missing = Object.entries(required)
24
- .filter(([, value]) => !value)
25
- .map(([key]) => key);
18
+ const { data: [row] = [], error } = await arn.single(
19
+ query("api_multilogin_token").select("*").where({ status: 1 }).orderByRaw("random()").limit(1)
20
+ );
26
21
 
27
- if (missing.length > 0) {
28
- throw new Error(`[TokenManager] Missing environment variables: ${missing.join(", ")}`);
22
+ if (error || !row) {
23
+ throw new Error("[TokenManager] No active Multilogin credentials found (status = 1).");
29
24
  }
25
+
26
+ _credentials = row;
27
+ return _credentials;
30
28
  }
31
29
 
32
30
  async function saveTokens(tokens) {
33
- validateEnv();
31
+ const creds = await loadCredentials();
34
32
 
35
33
  await arn.single(
36
34
  query("api_multilogin_token")
37
35
  .update({
38
36
  data: JSON.stringify(tokens, null, 2),
39
37
  })
40
- .where({ id: MULTILOGIN_ROW_ID })
38
+ .where({ id: creds.id })
41
39
  );
42
40
  }
43
41
 
44
42
  async function loadTokens() {
45
43
  try {
46
- validateEnv();
47
-
48
- const { data: [data] = [], error } = await arn.single(
49
- query("api_multilogin_token").select("*").where({ id: MULTILOGIN_ROW_ID }).limit(1)
50
- );
51
- if (error) {
52
- console.error("Error loading tokens:", error);
53
- return null;
54
- }
55
- if (!data || !data.data) {
44
+ const creds = await loadCredentials();
45
+ if (!creds.data || Object.keys(creds.data).length === 0) {
56
46
  return null;
57
47
  }
58
- return data.data;
48
+ return creds.data;
59
49
  } catch (err) {
60
50
  return null;
61
51
  }
@@ -73,11 +63,11 @@ function isJwtExpired(token) {
73
63
  }
74
64
 
75
65
  async function loginAndSaveTokens() {
76
- validateEnv();
66
+ const creds = await loadCredentials();
77
67
 
78
- const passwordHash = crypto.createHash("md5").update(MULTILOGIN_PASSWORD).digest("hex");
68
+ const passwordHash = crypto.createHash("md5").update(creds.password).digest("hex");
79
69
  const data = {
80
- email: MULTILOGIN_EMAIL,
70
+ email: creds.email,
81
71
  password: passwordHash,
82
72
  };
83
73
 
@@ -98,8 +88,8 @@ async function loginAndSaveTokens() {
98
88
  await saveTokens({
99
89
  token,
100
90
  refresh_token,
101
- email: MULTILOGIN_EMAIL,
102
- workspace_id: MULTILOGIN_WORKSPACE_ID,
91
+ email: creds.email,
92
+ workspace_id: creds.workspace_id,
103
93
  });
104
94
 
105
95
  console.log("[LOGIN] Tokens updated and saved.");
@@ -107,12 +97,12 @@ async function loginAndSaveTokens() {
107
97
  }
108
98
 
109
99
  async function refreshAndSaveTokens(refresh_token) {
110
- validateEnv();
100
+ const creds = await loadCredentials();
111
101
 
112
102
  const data = {
113
- email: MULTILOGIN_EMAIL,
103
+ email: creds.email,
114
104
  refresh_token: refresh_token,
115
- workspace_id: MULTILOGIN_WORKSPACE_ID,
105
+ workspace_id: creds.workspace_id,
116
106
  };
117
107
 
118
108
  const res = await fetch("https://api.multilogin.com/user/refresh_token", {
@@ -132,8 +122,8 @@ async function refreshAndSaveTokens(refresh_token) {
132
122
  await saveTokens({
133
123
  token,
134
124
  refresh_token: new_refresh,
135
- email: MULTILOGIN_EMAIL,
136
- workspace_id: MULTILOGIN_WORKSPACE_ID,
125
+ email: creds.email,
126
+ workspace_id: creds.workspace_id,
137
127
  });
138
128
 
139
129
  console.log("[REFRESH] Tokens refreshed and saved.");
@@ -141,7 +131,6 @@ async function refreshAndSaveTokens(refresh_token) {
141
131
  }
142
132
 
143
133
  async function getMultiloginToken() {
144
- validateEnv();
145
134
  let tokens = await loadTokens();
146
135
 
147
136
  if (tokens && tokens.token && !isJwtExpired(tokens.token)) {