arn-browser 0.1.32 → 0.1.33

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.1.32",
3
+ "version": "0.1.33",
4
4
  "description": "A lightweight, browser autmation helper.",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -20,14 +20,14 @@
20
20
  "dotenv": "^17.2.3",
21
21
  "fingerprint-generator": "^2.1.78",
22
22
  "fingerprint-injector": "^2.1.78",
23
- "https-proxy-agent": "^8.0.0",
23
+ "https-proxy-agent": "^9.0.0",
24
24
  "node-cache": "^5.1.2",
25
25
  "node-fetch": "^3.3.2",
26
26
  "playwright-core": "1.42.1",
27
27
  "proxy-chain": "^2.6.0",
28
28
  "puppeteer-core": "^24.40.0",
29
29
  "randomstring": "^1.3.1",
30
- "socks-proxy-agent": "^9.0.0",
30
+ "socks-proxy-agent": "^10.0.0",
31
31
  "speakeasy": "^2.0.0",
32
32
  "superagent": "^10.2.3"
33
33
  },
@@ -268,7 +268,24 @@ export interface PwLaunchOptions {
268
268
  humanize_options?: HumanizeOptions;
269
269
 
270
270
  // ========================================================================
271
- // 6. LOGGING
271
+ // 6. NOTIFICATIONS
272
+ // ========================================================================
273
+
274
+ /**
275
+ * List of hostnames to auto-grant notification permissions for.
276
+ * Pass plain hostnames — they are auto-converted to `https://host:443` internally.
277
+ *
278
+ * If empty (default), Chrome's default "Ask" behavior is used.
279
+ *
280
+ * @example
281
+ * notification_hosts: ["web.whatsapp.com", "messages.google.com"]
282
+ *
283
+ * Default: []
284
+ */
285
+ notification_hosts?: string[];
286
+
287
+ // ========================================================================
288
+ // 7. LOGGING
272
289
  // ========================================================================
273
290
 
274
291
  /**
@@ -71,7 +71,8 @@ function resolveProfilePath(nameOrPath, browserName) {
71
71
 
72
72
  let prefix = browserName.toLowerCase();
73
73
  if (prefix.includes("brave")) prefix = "brave";
74
- else if (prefix.includes("chrome") || prefix.includes("chromium")) prefix = "chromium";
74
+ else if (prefix.includes("chromium")) prefix = "chromium";
75
+ else if (prefix.includes("chrome")) prefix = "chrome";
75
76
  else if (prefix.includes("firefox")) prefix = "firefox";
76
77
  else if (prefix.includes("camoufox")) prefix = "camoufox";
77
78
 
@@ -114,6 +115,31 @@ function writeProfileMeta(dirPath, type, cleanupMinutes) {
114
115
  }
115
116
  }
116
117
 
118
+ /**
119
+ * Writes per-host notification permissions into the Chrome Preferences object.
120
+ * Accepts plain hostnames (e.g. "web.whatsapp.com") and converts them
121
+ * to Chrome's internal format: "https://host:443,*" with setting=1 (Allow).
122
+ * If hosts array is empty, does nothing (Chrome default "Ask" behavior).
123
+ */
124
+ function writeNotificationPermissions(prefs, hosts) {
125
+ if (!hosts || hosts.length === 0) return;
126
+
127
+ if (!prefs.profile) prefs.profile = {};
128
+ if (!prefs.profile.content_settings) prefs.profile.content_settings = {};
129
+ if (!prefs.profile.content_settings.exceptions) prefs.profile.content_settings.exceptions = {};
130
+ if (!prefs.profile.content_settings.exceptions.notifications) prefs.profile.content_settings.exceptions.notifications = {};
131
+
132
+ const now = String(Date.now());
133
+ for (const host of hosts) {
134
+ const origin = host.startsWith("http") ? host : `https://${host}:443`;
135
+ const key = `${origin},*`;
136
+ prefs.profile.content_settings.exceptions.notifications[key] = {
137
+ last_modified: now,
138
+ setting: 1, // 1 = Allow
139
+ };
140
+ }
141
+ }
142
+
117
143
  /**
118
144
  * Scans both persistent and temp profile directories.
119
145
  * Deletes profiles whose `_profile_meta.json` indicates they are expired.
@@ -278,6 +304,10 @@ export async function pwLaunch({
278
304
  camoufox_options = {}, // { geoip, humanize, ... }
279
305
  multilogin_options = {}, // { profileId, os_type, canvas_noise, ... }
280
306
 
307
+ // Notifications — per-host allow list
308
+ // Pass plain hostnames: ["web.whatsapp.com", "messages.google.com"]
309
+ notification_hosts = [],
310
+
281
311
  // Logging
282
312
  launch_logs = false,
283
313
  cleanup_logs = false,
@@ -325,6 +355,7 @@ export async function pwLaunch({
325
355
  spoof_fingerprint,
326
356
  cleanupMinutes: effectiveCleanupMinutes,
327
357
  browserType: which_browser, // "chrome" or "chromium"
358
+ notification_hosts,
328
359
  });
329
360
  break;
330
361
  case "firefox":
@@ -347,6 +378,7 @@ export async function pwLaunch({
347
378
  humanize_options: effectiveHumanizeOptions,
348
379
  spoof_fingerprint,
349
380
  cleanupMinutes: effectiveCleanupMinutes,
381
+ notification_hosts,
350
382
  });
351
383
  break;
352
384
  case "camoufox":
@@ -382,7 +414,7 @@ export async function pwLaunch({
382
414
  // ==========================================================================
383
415
  // 4. ENGINE: CHROMIUM
384
416
  // ==========================================================================
385
- async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, humanize_options, spoof_fingerprint, cleanupMinutes, browserType = "chromium" }) {
417
+ async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, humanize_options, spoof_fingerprint, cleanupMinutes, browserType = "chromium", notification_hosts = [] }) {
386
418
  const isPersistent = !!profilePath;
387
419
 
388
420
  // 1. Determine Path (Temp needs it for fingerprint storage, Persistent needs it for data)
@@ -411,6 +443,8 @@ async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, hum
411
443
  // --- Silence & Networking ---
412
444
  "--disable-background-networking",
413
445
  "--disable-background-timer-throttling",
446
+ "--disable-backgrounding-occluded-windows",
447
+ "--disable-renderer-backgrounding",
414
448
  "--disable-breakpad",
415
449
  "--disable-crash-reporter",
416
450
  "--disable-component-update",
@@ -498,6 +532,7 @@ async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, hum
498
532
 
499
533
  if (!prefs.profile) prefs.profile = {};
500
534
  prefs.profile.exit_type = "Normal";
535
+ writeNotificationPermissions(prefs, notification_hosts);
501
536
 
502
537
  if (!prefs.session) prefs.session = {};
503
538
  prefs.session.restore_on_startup = 4;
@@ -631,7 +666,7 @@ async function firefoxLauncher({ profilePath, proxy, timezoneId, humanize_option
631
666
  // ==========================================================================
632
667
  // 6. ENGINE: BRAVE
633
668
  // ==========================================================================
634
- async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, humanize_options, spoof_fingerprint, cleanupMinutes }) {
669
+ async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, humanize_options, spoof_fingerprint, cleanupMinutes, notification_hosts = [] }) {
635
670
  const isPersistent = !!profilePath;
636
671
  const activePath = isPersistent ? profilePath : path.join(TEMP_DIR, crypto.randomUUID());
637
672
 
@@ -706,6 +741,7 @@ async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, humani
706
741
  // Prevent tab restore (saves proxy bandwidth)
707
742
  if (!prefs.profile) prefs.profile = {};
708
743
  prefs.profile.exit_type = "Normal";
744
+ writeNotificationPermissions(prefs, notification_hosts);
709
745
 
710
746
  if (!prefs.session) prefs.session = {};
711
747
  prefs.session.restore_on_startup = 4;
@@ -759,6 +795,8 @@ async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, humani
759
795
  // --- Silence & Networking ---
760
796
  "--disable-background-networking",
761
797
  "--disable-background-timer-throttling",
798
+ "--disable-backgrounding-occluded-windows",
799
+ "--disable-renderer-backgrounding",
762
800
  "--disable-breakpad",
763
801
  "--disable-crash-reporter",
764
802
  "--disable-component-update",
@@ -180,7 +180,24 @@ export interface PpLaunchOptions {
180
180
  };
181
181
 
182
182
  // ========================================================================
183
- // 8. LOGGING
183
+ // 8. NOTIFICATIONS
184
+ // ========================================================================
185
+
186
+ /**
187
+ * List of hostnames to auto-grant notification permissions for.
188
+ * Pass plain hostnames — they are auto-converted to `https://host:443` internally.
189
+ *
190
+ * If empty (default), Chrome's default "Ask" behavior is used.
191
+ *
192
+ * @example
193
+ * notification_hosts: ["web.whatsapp.com", "messages.google.com"]
194
+ *
195
+ * Default: []
196
+ */
197
+ notification_hosts?: string[];
198
+
199
+ // ========================================================================
200
+ // 9. LOGGING
184
201
  // ========================================================================
185
202
 
186
203
  /**
@@ -128,7 +128,8 @@ function resolveProfilePath(nameOrPath, browserName) {
128
128
 
129
129
  let prefix = browserName.toLowerCase();
130
130
  if (prefix.includes("brave")) prefix = "brave";
131
- else if (prefix.includes("chrome") || prefix.includes("chromium")) prefix = "chrome";
131
+ else if (prefix.includes("chromium")) prefix = "chromium";
132
+ else if (prefix.includes("chrome")) prefix = "chrome";
132
133
 
133
134
  const folderName = `${prefix}_${nameOrPath}`;
134
135
  return path.join(PERSISTENT_DIR, folderName);
@@ -163,6 +164,31 @@ function writeProfileMeta(dirPath, type, cleanupMinutes) {
163
164
  }
164
165
  }
165
166
 
167
+ /**
168
+ * Writes per-host notification permissions into the Chrome Preferences object.
169
+ * Accepts plain hostnames (e.g. "web.whatsapp.com") and converts them
170
+ * to Chrome's internal format: "https://host:443,*" with setting=1 (Allow).
171
+ * If hosts array is empty, does nothing (Chrome default "Ask" behavior).
172
+ */
173
+ function writeNotificationPermissions(prefs, hosts) {
174
+ if (!hosts || hosts.length === 0) return;
175
+
176
+ if (!prefs.profile) prefs.profile = {};
177
+ if (!prefs.profile.content_settings) prefs.profile.content_settings = {};
178
+ if (!prefs.profile.content_settings.exceptions) prefs.profile.content_settings.exceptions = {};
179
+ if (!prefs.profile.content_settings.exceptions.notifications) prefs.profile.content_settings.exceptions.notifications = {};
180
+
181
+ const now = String(Date.now());
182
+ for (const host of hosts) {
183
+ const origin = host.startsWith("http") ? host : `https://${host}:443`;
184
+ const key = `${origin},*`;
185
+ prefs.profile.content_settings.exceptions.notifications[key] = {
186
+ last_modified: now,
187
+ setting: 1, // 1 = Allow
188
+ };
189
+ }
190
+ }
191
+
166
192
  /**
167
193
  * Scans profile directories and deletes expired profiles.
168
194
  */
@@ -262,6 +288,10 @@ export async function ppLaunch({
262
288
  // Multilogin
263
289
  multilogin_options = {},
264
290
 
291
+ // Notifications — per-host allow list
292
+ // Pass plain hostnames: ["web.whatsapp.com", "messages.google.com"]
293
+ notification_hosts = [],
294
+
265
295
  // Logging
266
296
  launch_logs = false,
267
297
  cleanup_logs = false,
@@ -295,6 +325,7 @@ export async function ppLaunch({
295
325
  spoof_fingerprint,
296
326
  cleanupMinutes: effectiveCleanupMinutes,
297
327
  browserType: "chrome",
328
+ notification_hosts,
298
329
  });
299
330
  break;
300
331
  case "chromium":
@@ -306,6 +337,7 @@ export async function ppLaunch({
306
337
  spoof_fingerprint,
307
338
  cleanupMinutes: effectiveCleanupMinutes,
308
339
  browserType: "chromium",
340
+ notification_hosts,
309
341
  });
310
342
  break;
311
343
  case "brave":
@@ -316,6 +348,7 @@ export async function ppLaunch({
316
348
  extraArgs,
317
349
  spoof_fingerprint,
318
350
  cleanupMinutes: effectiveCleanupMinutes,
351
+ notification_hosts,
319
352
  });
320
353
  break;
321
354
  case "multilogin":
@@ -339,7 +372,7 @@ export async function ppLaunch({
339
372
  // 4. ENGINE: CHROME (CDP)
340
373
  // ==========================================================================
341
374
 
342
- async function chromeLauncher({ profilePath, proxy, timezoneId, extraArgs, spoof_fingerprint, cleanupMinutes, browserType = "chrome" }) {
375
+ async function chromeLauncher({ profilePath, proxy, timezoneId, extraArgs, spoof_fingerprint, cleanupMinutes, browserType = "chrome", notification_hosts = [] }) {
343
376
  const isPersistent = !!profilePath;
344
377
  const activePath = isPersistent ? profilePath : path.join(TEMP_DIR, crypto.randomUUID());
345
378
 
@@ -357,6 +390,7 @@ async function chromeLauncher({ profilePath, proxy, timezoneId, extraArgs, spoof
357
390
  extraArgs,
358
391
  spoof_fingerprint,
359
392
  browserLabel: browserType === "chromium" ? "Chromium" : "Chrome",
393
+ notification_hosts,
360
394
  });
361
395
  }
362
396
 
@@ -364,7 +398,7 @@ async function chromeLauncher({ profilePath, proxy, timezoneId, extraArgs, spoof
364
398
  // 5. ENGINE: BRAVE (CDP)
365
399
  // ==========================================================================
366
400
 
367
- async function braveLauncher({ profilePath, proxy, timezoneId, extraArgs, spoof_fingerprint, cleanupMinutes }) {
401
+ async function braveLauncher({ profilePath, proxy, timezoneId, extraArgs, spoof_fingerprint, cleanupMinutes, notification_hosts = [] }) {
368
402
  const isPersistent = !!profilePath;
369
403
  const activePath = isPersistent ? profilePath : path.join(TEMP_DIR, crypto.randomUUID());
370
404
 
@@ -391,6 +425,7 @@ async function braveLauncher({ profilePath, proxy, timezoneId, extraArgs, spoof_
391
425
  // Prevent tab restore (saves proxy bandwidth)
392
426
  if (!prefs.profile) prefs.profile = {};
393
427
  prefs.profile.exit_type = "Normal";
428
+ writeNotificationPermissions(prefs, notification_hosts);
394
429
 
395
430
  if (!prefs.session) prefs.session = {};
396
431
  prefs.session.restore_on_startup = 4;
@@ -438,6 +473,7 @@ async function braveLauncher({ profilePath, proxy, timezoneId, extraArgs, spoof_
438
473
  extraArgs: [...braveArgs, ...extraArgs],
439
474
  spoof_fingerprint,
440
475
  browserLabel: "Brave",
476
+ notification_hosts,
441
477
  });
442
478
  }
443
479
 
@@ -445,7 +481,7 @@ async function braveLauncher({ profilePath, proxy, timezoneId, extraArgs, spoof_
445
481
  // 6. CDP SPAWN & CONNECT (Shared between Chrome & Brave)
446
482
  // ==========================================================================
447
483
 
448
- async function spawnAndConnect({ binaryPath, profilePath, isPersistent, proxy, timezoneId = null, extraArgs = [], spoof_fingerprint = false, browserLabel = "Browser" }) {
484
+ async function spawnAndConnect({ binaryPath, profilePath, isPersistent, proxy, timezoneId = null, extraArgs = [], spoof_fingerprint = false, browserLabel = "Browser", notification_hosts = [] }) {
449
485
  let browser;
450
486
  let closing = false;
451
487
  let signalHandler;
@@ -468,6 +504,7 @@ async function spawnAndConnect({ binaryPath, profilePath, isPersistent, proxy, t
468
504
  // Prevent "Restore pages?" prompt after crash
469
505
  if (!prefs.profile) prefs.profile = {};
470
506
  prefs.profile.exit_type = "Normal";
507
+ writeNotificationPermissions(prefs, notification_hosts);
471
508
 
472
509
  // Set startup to open about:blank instead of restoring tabs
473
510
  if (!prefs.session) prefs.session = {};
@@ -508,6 +545,8 @@ async function spawnAndConnect({ binaryPath, profilePath, isPersistent, proxy, t
508
545
  // --- Silence & Networking ---
509
546
  "--disable-background-networking",
510
547
  "--disable-background-timer-throttling",
548
+ "--disable-backgrounding-occluded-windows",
549
+ "--disable-renderer-backgrounding",
511
550
  "--disable-breakpad",
512
551
  "--disable-crash-reporter",
513
552
  "--disable-component-update",