arn-browser 0.0.14 → 0.1.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arn-browser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A lightweight, browser autmation helper.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"test": "test"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@aws-sdk/client-ec2": "^3.
|
|
14
|
+
"@aws-sdk/client-ec2": "^3.953.0",
|
|
15
15
|
"@ghostery/adblocker": "^2.13.0",
|
|
16
16
|
"arn-knexjs": "^0.0.3",
|
|
17
17
|
"camoufox-js": "^0.8.4",
|
|
@@ -174,24 +174,32 @@ export interface LaunchOptions {
|
|
|
174
174
|
CapSolver?: boolean;
|
|
175
175
|
|
|
176
176
|
/**
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
* -
|
|
180
|
-
*
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Whether to spoof browser fingerprint with FingerprintInjector.
|
|
186
|
-
* When true: Applies userAgent, viewport, locale from fingerprintData and uses FingerprintInjector.
|
|
187
|
-
* When false: Uses null viewport and applies stealth script only.
|
|
177
|
+
* Configuration for browser fingerprint spoofing.
|
|
178
|
+
*
|
|
179
|
+
* - `false`: No fingerprint spoofing (default)
|
|
180
|
+
* - `true`: Spoof fingerprint with viewport: null (maximized window)
|
|
181
|
+
* - `{ minWidth?, maxWidth?, minHeight?, maxHeight? }`: Spoof fingerprint with screen constraints
|
|
182
|
+
* and use the generated screen dimensions as viewport.
|
|
188
183
|
*
|
|
189
184
|
* **For persistent Brave profiles:** The value is locked on first launch.
|
|
190
185
|
* Subsequent launches will use the saved value, ignoring the current parameter.
|
|
191
186
|
*
|
|
187
|
+
* @example
|
|
188
|
+
* // Spoof with maximized window (viewport: null)
|
|
189
|
+
* spoof_fingerprint: true
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* // Spoof with specific screen constraints
|
|
193
|
+
* spoof_fingerprint: { minWidth: 1366, maxWidth: 1920, minHeight: 768 }
|
|
194
|
+
*
|
|
192
195
|
* Default: false
|
|
193
196
|
*/
|
|
194
|
-
spoof_fingerprint?: boolean
|
|
197
|
+
spoof_fingerprint?: boolean | {
|
|
198
|
+
minWidth?: number;
|
|
199
|
+
maxWidth?: number;
|
|
200
|
+
minHeight?: number;
|
|
201
|
+
maxHeight?: number;
|
|
202
|
+
};
|
|
195
203
|
|
|
196
204
|
// ========================================================================
|
|
197
205
|
// 5. ENGINE SPECIFIC GROUPS
|
|
@@ -137,8 +137,10 @@ function cleanUpTempProfiles(ageLimitMinutes) {
|
|
|
137
137
|
|
|
138
138
|
/**
|
|
139
139
|
* Helper to generate consistent fingerprint options.
|
|
140
|
+
* @param {string} browserType - 'chromium' | 'firefox' | 'brave'
|
|
141
|
+
* @param {object|null} screenOptions - Optional screen constraints { minWidth, maxWidth, minHeight, maxHeight }
|
|
140
142
|
*/
|
|
141
|
-
function getFingerprintConfig(browserType,
|
|
143
|
+
function getFingerprintConfig(browserType, screenOptions = null) {
|
|
142
144
|
const config = {
|
|
143
145
|
devices: ["desktop"],
|
|
144
146
|
operatingSystems: [detectedOs],
|
|
@@ -148,16 +150,19 @@ function getFingerprintConfig(browserType, maxWidth) {
|
|
|
148
150
|
};
|
|
149
151
|
|
|
150
152
|
if (browserType === "chromium") {
|
|
151
|
-
config.browsers.push({ name: "chrome", minVersion:
|
|
153
|
+
config.browsers.push({ name: "chrome", minVersion: 141 });
|
|
152
154
|
} else if (browserType === "firefox") {
|
|
153
|
-
config.browsers.push({ name: "firefox", minVersion:
|
|
155
|
+
config.browsers.push({ name: "firefox", minVersion: 141 });
|
|
154
156
|
} else if (browserType === "brave") {
|
|
155
|
-
config.browsers.push({ name: "chrome", minVersion:
|
|
157
|
+
config.browsers.push({ name: "chrome", minVersion: 141 });
|
|
156
158
|
}
|
|
157
159
|
|
|
158
|
-
if
|
|
159
|
-
|
|
160
|
-
config.screen.
|
|
160
|
+
// Apply screen filter options if provided
|
|
161
|
+
if (screenOptions && typeof screenOptions === 'object') {
|
|
162
|
+
if (screenOptions.minWidth) config.screen.minWidth = screenOptions.minWidth;
|
|
163
|
+
if (screenOptions.maxWidth) config.screen.maxWidth = screenOptions.maxWidth;
|
|
164
|
+
if (screenOptions.minHeight) config.screen.minHeight = screenOptions.minHeight;
|
|
165
|
+
if (screenOptions.maxHeight) config.screen.maxHeight = screenOptions.maxHeight;
|
|
161
166
|
}
|
|
162
167
|
|
|
163
168
|
return config;
|
|
@@ -171,7 +176,6 @@ export async function launchBrowser({
|
|
|
171
176
|
// Common
|
|
172
177
|
timezoneId = null,
|
|
173
178
|
proxy,
|
|
174
|
-
maxWidth = 1,
|
|
175
179
|
|
|
176
180
|
// Path & Storage
|
|
177
181
|
profile_path = null,
|
|
@@ -183,6 +187,9 @@ export async function launchBrowser({
|
|
|
183
187
|
humanize_options = {}, // { humanize, maxTime, minTime, showCursor } - defaults to enabled for non-camoufox
|
|
184
188
|
|
|
185
189
|
// Spoof Fingerprint
|
|
190
|
+
// - false: No spoofing
|
|
191
|
+
// - true: Spoof fingerprint with viewport: null (no screen constraint)
|
|
192
|
+
// - { minWidth?, maxWidth?, minHeight?, maxHeight? }: Spoof with screen constraints
|
|
186
193
|
spoof_fingerprint = false,
|
|
187
194
|
|
|
188
195
|
// Browser Specific Grouped Options
|
|
@@ -214,7 +221,6 @@ export async function launchBrowser({
|
|
|
214
221
|
proxy,
|
|
215
222
|
timezoneId,
|
|
216
223
|
CapSolver,
|
|
217
|
-
maxWidth,
|
|
218
224
|
humanize_options: effectiveHumanizeOptions,
|
|
219
225
|
spoof_fingerprint,
|
|
220
226
|
});
|
|
@@ -224,7 +230,6 @@ export async function launchBrowser({
|
|
|
224
230
|
profilePath: fullPath,
|
|
225
231
|
proxy,
|
|
226
232
|
timezoneId,
|
|
227
|
-
maxWidth,
|
|
228
233
|
humanize_options: effectiveHumanizeOptions,
|
|
229
234
|
spoof_fingerprint,
|
|
230
235
|
});
|
|
@@ -236,7 +241,6 @@ export async function launchBrowser({
|
|
|
236
241
|
proxy,
|
|
237
242
|
timezoneId,
|
|
238
243
|
CapSolver,
|
|
239
|
-
maxWidth,
|
|
240
244
|
humanize_options: effectiveHumanizeOptions,
|
|
241
245
|
spoof_fingerprint,
|
|
242
246
|
});
|
|
@@ -247,7 +251,6 @@ export async function launchBrowser({
|
|
|
247
251
|
profilePath: fullPath,
|
|
248
252
|
proxy,
|
|
249
253
|
timezoneId,
|
|
250
|
-
maxWidth,
|
|
251
254
|
camoufox_options,
|
|
252
255
|
// Spoof Fingerprint not needed for Camoufox
|
|
253
256
|
});
|
|
@@ -274,9 +277,7 @@ export async function launchBrowser({
|
|
|
274
277
|
// ==========================================================================
|
|
275
278
|
// 4. ENGINE: CHROMIUM
|
|
276
279
|
// ==========================================================================
|
|
277
|
-
async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver,
|
|
278
|
-
, spoof_fingerprint
|
|
279
|
-
}) {
|
|
280
|
+
async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, humanize_options, spoof_fingerprint }) {
|
|
280
281
|
const isPersistent = !!profilePath;
|
|
281
282
|
|
|
282
283
|
// 1. Determine Path (Temp needs it for fingerprint storage, Persistent needs it for data)
|
|
@@ -324,12 +325,15 @@ async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, max
|
|
|
324
325
|
const proxyObj = formatProxy(proxy);
|
|
325
326
|
const tz = timezoneId || undefined;
|
|
326
327
|
|
|
328
|
+
// Determine screen options from spoof_fingerprint if it's an object
|
|
329
|
+
const screenOptions = (spoof_fingerprint && typeof spoof_fingerprint === 'object') ? spoof_fingerprint : null;
|
|
330
|
+
|
|
327
331
|
// ==================================================================
|
|
328
332
|
// BRANCH A: TEMP PROFILE (launch + newInjectedContext)
|
|
329
333
|
// ==================================================================
|
|
330
334
|
if (!isPersistent) {
|
|
331
335
|
try {
|
|
332
|
-
const fpConfig = getFingerprintConfig("chromium",
|
|
336
|
+
const fpConfig = getFingerprintConfig("chromium", screenOptions);
|
|
333
337
|
const fingerprintData = new FingerprintGenerator().getFingerprint(fpConfig);
|
|
334
338
|
|
|
335
339
|
// Launch standard browser (not persistent context)
|
|
@@ -350,6 +354,7 @@ async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, max
|
|
|
350
354
|
} else {
|
|
351
355
|
context = await browser.newContext({
|
|
352
356
|
timezoneId: tz,
|
|
357
|
+
viewport: null,
|
|
353
358
|
});
|
|
354
359
|
// Manual Stealth Script
|
|
355
360
|
await addStealthScript(context);
|
|
@@ -398,7 +403,7 @@ async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, max
|
|
|
398
403
|
// ==========================================================================
|
|
399
404
|
// 5. ENGINE: FIREFOX
|
|
400
405
|
// ==========================================================================
|
|
401
|
-
async function firefoxLauncher({ profilePath, proxy, timezoneId,
|
|
406
|
+
async function firefoxLauncher({ profilePath, proxy, timezoneId, humanize_options, spoof_fingerprint }) {
|
|
402
407
|
const isPersistent = !!profilePath;
|
|
403
408
|
|
|
404
409
|
// 1. Determine Path
|
|
@@ -408,6 +413,9 @@ async function firefoxLauncher({ profilePath, proxy, timezoneId, maxWidth, human
|
|
|
408
413
|
const proxyObj = formatProxy(proxy);
|
|
409
414
|
const tz = timezoneId || undefined;
|
|
410
415
|
|
|
416
|
+
// Determine screen options from spoof_fingerprint if it's an object
|
|
417
|
+
const screenOptions = (spoof_fingerprint && typeof spoof_fingerprint === 'object') ? spoof_fingerprint : null;
|
|
418
|
+
|
|
411
419
|
// Firefox specific preferences
|
|
412
420
|
const firefoxUserPrefs = {
|
|
413
421
|
"dom.webdriver.enabled": false,
|
|
@@ -420,7 +428,7 @@ async function firefoxLauncher({ profilePath, proxy, timezoneId, maxWidth, human
|
|
|
420
428
|
// ==================================================================
|
|
421
429
|
if (!isPersistent) {
|
|
422
430
|
try {
|
|
423
|
-
const fpConfig = getFingerprintConfig("firefox",
|
|
431
|
+
const fpConfig = getFingerprintConfig("firefox", screenOptions);
|
|
424
432
|
const fingerprintData = new FingerprintGenerator().getFingerprint(fpConfig);
|
|
425
433
|
|
|
426
434
|
const browser = await firefox.launch({
|
|
@@ -441,6 +449,7 @@ async function firefoxLauncher({ profilePath, proxy, timezoneId, maxWidth, human
|
|
|
441
449
|
else {
|
|
442
450
|
context = await browser.newContext({
|
|
443
451
|
timezoneId: tz,
|
|
452
|
+
viewport: null,
|
|
444
453
|
});
|
|
445
454
|
// Manual Stealth Script
|
|
446
455
|
await addStealthScript(context);
|
|
@@ -488,7 +497,7 @@ async function firefoxLauncher({ profilePath, proxy, timezoneId, maxWidth, human
|
|
|
488
497
|
// ==========================================================================
|
|
489
498
|
// 6. ENGINE: BRAVE
|
|
490
499
|
// ==========================================================================
|
|
491
|
-
async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId,
|
|
500
|
+
async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, humanize_options, spoof_fingerprint }) {
|
|
492
501
|
const isPersistent = !!profilePath;
|
|
493
502
|
const activePath = isPersistent ? profilePath : path.join(TEMP_DIR, crypto.randomUUID());
|
|
494
503
|
|
|
@@ -507,22 +516,31 @@ async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, maxWid
|
|
|
507
516
|
// Load saved settings from first launch
|
|
508
517
|
const savedSettings = JSON.parse(fs.readFileSync(settingsFilePath, "utf-8"));
|
|
509
518
|
effectiveSpoofFingerprint = savedSettings.spoof_fingerprint;
|
|
510
|
-
console.log(`📋 Using saved spoof_fingerprint: ${effectiveSpoofFingerprint} (ignoring current: ${spoof_fingerprint})`);
|
|
519
|
+
console.log(`📋 Using saved spoof_fingerprint: ${JSON.stringify(effectiveSpoofFingerprint)} (ignoring current: ${JSON.stringify(spoof_fingerprint)})`);
|
|
511
520
|
} else {
|
|
512
521
|
// First launch - save the current setting
|
|
513
522
|
const settings = { spoof_fingerprint: spoof_fingerprint };
|
|
514
523
|
fs.writeFileSync(settingsFilePath, JSON.stringify(settings, null, 2), "utf-8");
|
|
515
|
-
console.log(`📝 Saved spoof_fingerprint setting: ${spoof_fingerprint}`);
|
|
524
|
+
console.log(`📝 Saved spoof_fingerprint setting: ${JSON.stringify(spoof_fingerprint)}`);
|
|
516
525
|
}
|
|
517
526
|
}
|
|
518
527
|
|
|
528
|
+
// Determine screen options from spoof_fingerprint if it's an object
|
|
529
|
+
const screenOptions = (effectiveSpoofFingerprint && typeof effectiveSpoofFingerprint === 'object') ? effectiveSpoofFingerprint : null;
|
|
530
|
+
|
|
531
|
+
// Determine if we should use screen viewport or null
|
|
532
|
+
// - spoof_fingerprint = true (boolean) -> spoof with viewport: null
|
|
533
|
+
// - spoof_fingerprint = { minWidth, maxWidth, ... } (object) -> spoof with generated screen viewport
|
|
534
|
+
const useScreenViewport = screenOptions !== null;
|
|
535
|
+
|
|
519
536
|
let fingerprintData;
|
|
520
537
|
const fingerprintFilePath = path.join(activePath, "fingerprint.json");
|
|
521
538
|
|
|
522
539
|
if (fs.existsSync(fingerprintFilePath)) {
|
|
523
540
|
fingerprintData = JSON.parse(fs.readFileSync(fingerprintFilePath, "utf-8"));
|
|
524
541
|
} else {
|
|
525
|
-
|
|
542
|
+
// Generate fingerprint with screen options (null if spoof_fingerprint is just true)
|
|
543
|
+
const fpConfig = getFingerprintConfig("brave", screenOptions);
|
|
526
544
|
fingerprintData = new FingerprintGenerator().getFingerprint(fpConfig);
|
|
527
545
|
|
|
528
546
|
if (isPersistent) {
|
|
@@ -601,6 +619,9 @@ async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, maxWid
|
|
|
601
619
|
|
|
602
620
|
let context;
|
|
603
621
|
if (effectiveSpoofFingerprint) {
|
|
622
|
+
// Determine viewport: use screen from fingerprint if object provided, otherwise null
|
|
623
|
+
const viewport = useScreenViewport ? fingerprintData.fingerprint.screen : null;
|
|
624
|
+
|
|
604
625
|
context = await chromium.launchPersistentContext(activePath, {
|
|
605
626
|
headless: false,
|
|
606
627
|
executablePath: braveBin,
|
|
@@ -609,7 +630,9 @@ async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, maxWid
|
|
|
609
630
|
ignoreDefaultArgs: ignoreDefaultArgs,
|
|
610
631
|
args: args,
|
|
611
632
|
userAgent: fingerprintData.fingerprint.navigator.userAgent,
|
|
612
|
-
viewport:
|
|
633
|
+
// viewport: viewport,
|
|
634
|
+
// For Brave browser viewport set not working
|
|
635
|
+
viewport: null,
|
|
613
636
|
locale: fingerprintData.fingerprint.navigator.language,
|
|
614
637
|
});
|
|
615
638
|
|
|
@@ -653,7 +676,7 @@ async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, maxWid
|
|
|
653
676
|
// ==========================================================================
|
|
654
677
|
// 7. ENGINE: CAMOUFOX
|
|
655
678
|
// ==========================================================================
|
|
656
|
-
async function camoufoxLauncher({ profilePath, proxy, timezoneId,
|
|
679
|
+
async function camoufoxLauncher({ profilePath, proxy, timezoneId, camoufox_options = {} }) {
|
|
657
680
|
const isPersistent = !!profilePath;
|
|
658
681
|
const activePath = isPersistent ? profilePath : path.join(TEMP_DIR, crypto.randomUUID());
|
|
659
682
|
|
|
@@ -686,7 +709,6 @@ async function camoufoxLauncher({ profilePath, proxy, timezoneId, maxWidth, camo
|
|
|
686
709
|
const randomIndex = Math.floor(Math.random() * validMajorVersions.length);
|
|
687
710
|
return validMajorVersions[randomIndex];
|
|
688
711
|
}
|
|
689
|
-
const screenWidth = maxWidth ? (maxWidth <= 10 ? Math.round(maxWidth * 1920) : maxWidth) : 1920;
|
|
690
712
|
|
|
691
713
|
// Prepare the base options
|
|
692
714
|
const optionsGenerator = {
|
|
@@ -704,12 +726,9 @@ async function camoufoxLauncher({ profilePath, proxy, timezoneId, maxWidth, camo
|
|
|
704
726
|
...camoufox_options, // Spread user overrides
|
|
705
727
|
};
|
|
706
728
|
|
|
729
|
+
// Use screen from camoufox_options if provided
|
|
707
730
|
if (camoufox_options.screen) {
|
|
708
731
|
optionsGenerator.screen = camoufox_options.screen;
|
|
709
|
-
} else {
|
|
710
|
-
optionsGenerator.screen = {
|
|
711
|
-
maxWidth: screenWidth,
|
|
712
|
-
};
|
|
713
732
|
}
|
|
714
733
|
// If GeoIP is TRUE, pass proxy inside launchOptions
|
|
715
734
|
if (isGeoIpEnabled && proxyObj) {
|
|
@@ -506,7 +506,7 @@ export async function getActiveProxy({ instance_name }) {
|
|
|
506
506
|
}
|
|
507
507
|
|
|
508
508
|
await sleep(15000); // Wait for boot
|
|
509
|
-
const proxyAlive = await isProxyAlive(publicIp,
|
|
509
|
+
const proxyAlive = await isProxyAlive(publicIp, 10001);
|
|
510
510
|
await sleep(5000);
|
|
511
511
|
|
|
512
512
|
if (!proxyAlive) {
|
|
@@ -536,7 +536,7 @@ export async function getActiveProxyWithStartStop({ instance_name }) {
|
|
|
536
536
|
let publicIp = await getPublicIpAddress({ instance_name });
|
|
537
537
|
|
|
538
538
|
await sleep(15000); // Wait for boot
|
|
539
|
-
const proxyAlive = await isProxyAlive(publicIp,
|
|
539
|
+
const proxyAlive = await isProxyAlive(publicIp, 10001);
|
|
540
540
|
|
|
541
541
|
if (!proxyAlive) {
|
|
542
542
|
console.log("No Unique IP Found or Proxy Dead");
|
|
@@ -577,7 +577,7 @@ export async function getActiveProxyConditional({ instance_name }) {
|
|
|
577
577
|
await sleep(2000);
|
|
578
578
|
|
|
579
579
|
let publicIp = await getPublicIpAddress({ instance_name });
|
|
580
|
-
const proxyAlive = await isProxyAlive(publicIp,
|
|
580
|
+
const proxyAlive = await isProxyAlive(publicIp, 10001);
|
|
581
581
|
|
|
582
582
|
if (!proxyAlive) {
|
|
583
583
|
console.log("Proxy is not alive or no unique IP found.");
|
|
@@ -597,7 +597,7 @@ export async function getActiveProxyConditional({ instance_name }) {
|
|
|
597
597
|
* Uses native Node.js HTTP/HTTPS modules via 'nativeGet'.
|
|
598
598
|
*
|
|
599
599
|
* @param {string} proxyHost - IP address of the proxy.
|
|
600
|
-
* @param {number} proxyPort - Port of the proxy (usually
|
|
600
|
+
* @param {number} proxyPort - Port of the proxy (usually 10001).
|
|
601
601
|
*/
|
|
602
602
|
export async function isProxyAlive(proxyHost, proxyPort) {
|
|
603
603
|
const agent = new SocksProxyAgent(`socks5://${proxyHost}:${proxyPort}`);
|