arn-browser 0.0.4 → 0.0.5

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.
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Pure JavaScript easing functions (no external dependencies)
3
+ * Ported from pytweening for human-like cursor movement
4
+ */
5
+
6
+ /**
7
+ * Linear easing - constant speed
8
+ * @param {number} n - Progress (0 to 1)
9
+ * @returns {number}
10
+ */
11
+ export function linear(n) {
12
+ return n;
13
+ }
14
+
15
+ /**
16
+ * Quadratic ease out - decelerating
17
+ * @param {number} n - Progress (0 to 1)
18
+ * @returns {number}
19
+ */
20
+ export function easeOutQuad(n) {
21
+ return -n * (n - 2);
22
+ }
23
+
24
+ /**
25
+ * Quadratic ease in - accelerating
26
+ * @param {number} n - Progress (0 to 1)
27
+ * @returns {number}
28
+ */
29
+ export function easeInQuad(n) {
30
+ return n * n;
31
+ }
32
+
33
+ /**
34
+ * Quadratic ease in-out
35
+ * @param {number} n - Progress (0 to 1)
36
+ * @returns {number}
37
+ */
38
+ export function easeInOutQuad(n) {
39
+ if (n < 0.5) {
40
+ return 2 * n * n;
41
+ }
42
+ return -2 * n * n + 4 * n - 1;
43
+ }
44
+
45
+ /**
46
+ * Cubic ease out - decelerating
47
+ * @param {number} n - Progress (0 to 1)
48
+ * @returns {number}
49
+ */
50
+ export function easeOutCubic(n) {
51
+ const n1 = n - 1;
52
+ return n1 * n1 * n1 + 1;
53
+ }
54
+
55
+ /**
56
+ * Cubic ease in - accelerating
57
+ * @param {number} n - Progress (0 to 1)
58
+ * @returns {number}
59
+ */
60
+ export function easeInCubic(n) {
61
+ return n * n * n;
62
+ }
63
+
64
+ /**
65
+ * Cubic ease in-out
66
+ * @param {number} n - Progress (0 to 1)
67
+ * @returns {number}
68
+ */
69
+ export function easeInOutCubic(n) {
70
+ if (n < 0.5) {
71
+ return 4 * n * n * n;
72
+ }
73
+ const p = 2 * n - 2;
74
+ return 0.5 * p * p * p + 1;
75
+ }
76
+
77
+ /**
78
+ * Quartic ease out - decelerating
79
+ * @param {number} n - Progress (0 to 1)
80
+ * @returns {number}
81
+ */
82
+ export function easeOutQuart(n) {
83
+ const n1 = n - 1;
84
+ return -(n1 * n1 * n1 * n1 - 1);
85
+ }
86
+
87
+ /**
88
+ * Quartic ease in - accelerating
89
+ * @param {number} n - Progress (0 to 1)
90
+ * @returns {number}
91
+ */
92
+ export function easeInQuart(n) {
93
+ return n * n * n * n;
94
+ }
95
+
96
+ /**
97
+ * Quartic ease in-out
98
+ * @param {number} n - Progress (0 to 1)
99
+ * @returns {number}
100
+ */
101
+ export function easeInOutQuart(n) {
102
+ if (n < 0.5) {
103
+ return 8 * n * n * n * n;
104
+ }
105
+ const n1 = n - 1;
106
+ return -8 * n1 * n1 * n1 * n1 + 1;
107
+ }
108
+
109
+ /**
110
+ * Quintic ease out - decelerating
111
+ * @param {number} n - Progress (0 to 1)
112
+ * @returns {number}
113
+ */
114
+ export function easeOutQuint(n) {
115
+ const n1 = n - 1;
116
+ return n1 * n1 * n1 * n1 * n1 + 1;
117
+ }
118
+
119
+ /**
120
+ * Quintic ease in - accelerating
121
+ * @param {number} n - Progress (0 to 1)
122
+ * @returns {number}
123
+ */
124
+ export function easeInQuint(n) {
125
+ return n * n * n * n * n;
126
+ }
127
+
128
+ /**
129
+ * Quintic ease in-out
130
+ * @param {number} n - Progress (0 to 1)
131
+ * @returns {number}
132
+ */
133
+ export function easeInOutQuint(n) {
134
+ if (n < 0.5) {
135
+ return 16 * n * n * n * n * n;
136
+ }
137
+ const p = 2 * n - 2;
138
+ return 0.5 * p * p * p * p * p + 1;
139
+ }
140
+
141
+ /**
142
+ * Sinusoidal ease out - decelerating
143
+ * @param {number} n - Progress (0 to 1)
144
+ * @returns {number}
145
+ */
146
+ export function easeOutSine(n) {
147
+ return Math.sin(n * Math.PI / 2);
148
+ }
149
+
150
+ /**
151
+ * Sinusoidal ease in - accelerating
152
+ * @param {number} n - Progress (0 to 1)
153
+ * @returns {number}
154
+ */
155
+ export function easeInSine(n) {
156
+ return -Math.cos(n * Math.PI / 2) + 1;
157
+ }
158
+
159
+ /**
160
+ * Sinusoidal ease in-out
161
+ * @param {number} n - Progress (0 to 1)
162
+ * @returns {number}
163
+ */
164
+ export function easeInOutSine(n) {
165
+ return -0.5 * (Math.cos(Math.PI * n) - 1);
166
+ }
167
+
168
+ /**
169
+ * Exponential ease out - decelerating
170
+ * @param {number} n - Progress (0 to 1)
171
+ * @returns {number}
172
+ */
173
+ export function easeOutExpo(n) {
174
+ if (n === 1) return 1;
175
+ return -(Math.pow(2, -10 * n)) + 1;
176
+ }
177
+
178
+ /**
179
+ * Exponential ease in - accelerating
180
+ * @param {number} n - Progress (0 to 1)
181
+ * @returns {number}
182
+ */
183
+ export function easeInExpo(n) {
184
+ if (n === 0) return 0;
185
+ return Math.pow(2, 10 * (n - 1));
186
+ }
187
+
188
+ /**
189
+ * Exponential ease in-out
190
+ * @param {number} n - Progress (0 to 1)
191
+ * @returns {number}
192
+ */
193
+ export function easeInOutExpo(n) {
194
+ if (n === 0) return 0;
195
+ if (n === 1) return 1;
196
+ if (n < 0.5) {
197
+ return 0.5 * Math.pow(2, 20 * n - 10);
198
+ }
199
+ return 1 - 0.5 * Math.pow(2, -20 * n + 10);
200
+ }
201
+
202
+ /**
203
+ * Circular ease out - decelerating
204
+ * @param {number} n - Progress (0 to 1)
205
+ * @returns {number}
206
+ */
207
+ export function easeOutCirc(n) {
208
+ const n1 = n - 1;
209
+ return Math.sqrt(1 - n1 * n1);
210
+ }
211
+
212
+ /**
213
+ * Circular ease in - accelerating
214
+ * @param {number} n - Progress (0 to 1)
215
+ * @returns {number}
216
+ */
217
+ export function easeInCirc(n) {
218
+ return -(Math.sqrt(1 - n * n) - 1);
219
+ }
220
+
221
+ /**
222
+ * Circular ease in-out
223
+ * @param {number} n - Progress (0 to 1)
224
+ * @returns {number}
225
+ */
226
+ export function easeInOutCirc(n) {
227
+ if (n < 0.5) {
228
+ return -0.5 * (Math.sqrt(1 - 4 * n * n) - 1);
229
+ }
230
+ const n1 = 2 * n - 2;
231
+ return 0.5 * (Math.sqrt(1 - n1 * n1) + 1);
232
+ }
233
+
234
+ /**
235
+ * All available easing functions
236
+ */
237
+ export const easingFunctions = {
238
+ linear,
239
+ easeOutQuad,
240
+ easeInQuad,
241
+ easeInOutQuad,
242
+ easeOutCubic,
243
+ easeInCubic,
244
+ easeInOutCubic,
245
+ easeOutQuart,
246
+ easeInQuart,
247
+ easeInOutQuart,
248
+ easeOutQuint,
249
+ easeInQuint,
250
+ easeInOutQuint,
251
+ easeOutSine,
252
+ easeInSine,
253
+ easeInOutSine,
254
+ easeOutExpo,
255
+ easeInExpo,
256
+ easeInOutExpo,
257
+ easeOutCirc,
258
+ easeInCirc,
259
+ easeInOutCirc
260
+ };
@@ -1,5 +1,6 @@
1
1
  import { Browser, BrowserContext, Page } from "playwright";
2
2
  import type { FingerprintGeneratorOptions } from "fingerprint-generator";
3
+ import type { HumanPage, CreateCursorOptions } from "../human-cursor/index";
3
4
  type Screen = FingerprintGeneratorOptions["screen"];
4
5
  /**
5
6
  * Proxy configuration object.
@@ -12,6 +13,22 @@ export interface ProxyConfig {
12
13
  pass?: string;
13
14
  }
14
15
 
16
+
17
+ /**
18
+ * Configuration options for human-like cursor movement.
19
+ * Only applicable for non-camoufox browsers (camoufox has its own humanize option).
20
+ */
21
+ export interface HumanizeOptions extends CreateCursorOptions {
22
+ /** Enable/disable human-like cursor movement (default: true) */
23
+ humanize?: boolean;
24
+ /** Maximum movement time in seconds (default: 1.5) */
25
+ maxTime?: number;
26
+ /** Minimum movement time in seconds (default: 0.5) */
27
+ minTime?: number;
28
+ /** Auto-show cursor indicator after goto (default: true) */
29
+ showCursor?: boolean;
30
+ }
31
+
15
32
  /**
16
33
  * Configuration options specific to Camoufox.
17
34
  * These are passed directly to camoufox-js.
@@ -177,6 +194,14 @@ export interface LaunchOptions {
177
194
  * Options specific to the Multilogin engine.
178
195
  */
179
196
  multilogin_options?: MultiloginOptions;
197
+
198
+ /**
199
+ * Options for human-like cursor movement.
200
+ * ENABLED BY DEFAULT for non-camoufox browsers!
201
+ * To disable: set { humanize: false }
202
+ * Camoufox uses its own humanize option in camoufox_options.
203
+ */
204
+ humanize_options?: HumanizeOptions;
180
205
  }
181
206
 
182
207
  /**
@@ -196,8 +221,10 @@ export interface BrowserController {
196
221
 
197
222
  /**
198
223
  * The initial Page object.
224
+ * When humanize_options is provided, this will be a HumanPage with human-like cursor methods.
225
+ * All standard Playwright Page methods are available.
199
226
  */
200
- page: Page | null;
227
+ page: Page | HumanPage | null;
201
228
 
202
229
  /**
203
230
  * Checks if the browser context is currently active.
@@ -20,6 +20,9 @@ import { FingerprintGenerator } from "fingerprint-generator";
20
20
  import { getMultiloginToken } from "./multilogin_token_manager.js";
21
21
  import { deleteDirectoryWithRetries } from "./deleteDirectory.js";
22
22
 
23
+ // Human Cursor - for human-like mouse movements
24
+ import { createCursor } from "../human-cursor/index.js";
25
+
23
26
  // Camoufox Special
24
27
  import { launchOptions } from "camoufox-js";
25
28
 
@@ -99,7 +102,7 @@ function getBinaryPath(browserName) {
99
102
  if (!binaryPath || !fs.existsSync(binaryPath)) {
100
103
  throw new Error(
101
104
  `❌ [LaunchBrowser] Binary not found for ${browserName} at: ${binaryPath}\n` +
102
- ` Linux checked: ~/.cache/brave/brave AND ~/Downloads/brave/brave`
105
+ ` Linux checked: ~/.cache/brave/brave AND ~/Downloads/brave/brave`
103
106
  );
104
107
  }
105
108
 
@@ -181,6 +184,7 @@ export async function launchBrowser({
181
184
  // Browser Specific Grouped Options
182
185
  camoufox_options = {}, // { geoip, humanize, ... }
183
186
  multilogin_options = {}, // { profileId, os_type, canvas_noise, ... }
187
+ humanize_options = {}, // { humanize, maxTime, minTime, showCursor } - defaults to enabled for non-camoufox
184
188
  }) {
185
189
  try {
186
190
  if (custom_profile_path) throw new Error("Please use profile_path");
@@ -191,6 +195,11 @@ export async function launchBrowser({
191
195
 
192
196
  let browserInstance;
193
197
 
198
+ // Humanize is ON by default for non-camoufox browsers
199
+ // Only disabled if explicitly set to false: humanize_options: { humanize: false }
200
+ const shouldHumanize = which_browser !== "camoufox" && humanize_options?.humanize !== false;
201
+ const effectiveHumanizeOptions = shouldHumanize ? humanize_options : null;
202
+
194
203
  switch (which_browser) {
195
204
  case "chromium":
196
205
  case "chrome":
@@ -200,6 +209,7 @@ export async function launchBrowser({
200
209
  timezoneId,
201
210
  CapSolver,
202
211
  maxWidth,
212
+ humanize_options: effectiveHumanizeOptions,
203
213
  });
204
214
  break;
205
215
  case "firefox":
@@ -208,6 +218,7 @@ export async function launchBrowser({
208
218
  proxy,
209
219
  timezoneId,
210
220
  maxWidth,
221
+ humanize_options: effectiveHumanizeOptions,
211
222
  });
212
223
  break;
213
224
  case "brave":
@@ -218,9 +229,11 @@ export async function launchBrowser({
218
229
  timezoneId,
219
230
  CapSolver,
220
231
  maxWidth,
232
+ humanize_options: effectiveHumanizeOptions,
221
233
  });
222
234
  break;
223
235
  case "camoufox":
236
+ // Camoufox already has its own humanize in camoufox_options
224
237
  browserInstance = await camoufoxLauncher({
225
238
  profilePath: fullPath,
226
239
  proxy,
@@ -233,6 +246,7 @@ export async function launchBrowser({
233
246
  browserInstance = await multiloginLauncher({
234
247
  proxy,
235
248
  multilogin_options,
249
+ humanize_options: effectiveHumanizeOptions,
236
250
  });
237
251
  break;
238
252
  default:
@@ -242,14 +256,14 @@ export async function launchBrowser({
242
256
  return browserInstance;
243
257
  } catch (error) {
244
258
  console.error("❌ [LaunchBrowser] Critical Error:", error.message || error);
245
- return { data: null, error: error, closeBrowser: async () => {} };
259
+ return { data: null, error: error, closeBrowser: async () => { } };
246
260
  }
247
261
  }
248
262
 
249
263
  // ==========================================================================
250
264
  // 4. ENGINE: CHROMIUM
251
265
  // ==========================================================================
252
- async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, maxWidth }) {
266
+ async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, maxWidth, humanize_options }) {
253
267
  const isPersistent = !!profilePath;
254
268
 
255
269
  // 1. Determine Path (Temp needs it for fingerprint storage, Persistent needs it for data)
@@ -317,7 +331,7 @@ async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, max
317
331
  const page = context.pages()[0] || (await context.newPage());
318
332
 
319
333
  // Pass 'activePath' so the temp folder (containing fingerprint.json) gets deleted on close
320
- return createBrowserController(browser, context, page, activePath);
334
+ return createBrowserController(browser, context, page, activePath, humanize_options);
321
335
  } catch (err) {
322
336
  console.error("Chromium Temp Launch Error:", err);
323
337
  throw err;
@@ -345,7 +359,7 @@ async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, max
345
359
 
346
360
  const page = context.pages()[0];
347
361
 
348
- return createBrowserController(context, context, page, null);
362
+ return createBrowserController(context, context, page, null, humanize_options);
349
363
  } catch (err) {
350
364
  console.error("Chromium Persistent Launch Error:", err);
351
365
  throw err;
@@ -356,7 +370,7 @@ async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, max
356
370
  // ==========================================================================
357
371
  // 5. ENGINE: FIREFOX
358
372
  // ==========================================================================
359
- async function firefoxLauncher({ profilePath, proxy, timezoneId, maxWidth }) {
373
+ async function firefoxLauncher({ profilePath, proxy, timezoneId, maxWidth, humanize_options }) {
360
374
  const isPersistent = !!profilePath;
361
375
 
362
376
  // 1. Determine Path
@@ -396,7 +410,7 @@ async function firefoxLauncher({ profilePath, proxy, timezoneId, maxWidth }) {
396
410
  const page = context.pages()[0] || (await context.newPage());
397
411
 
398
412
  // Pass 'activePath' to delete temp folder later
399
- return createBrowserController(browser, context, page, activePath);
413
+ return createBrowserController(browser, context, page, activePath, humanize_options);
400
414
  } catch (err) {
401
415
  console.error("Firefox Temp Launch Error:", err);
402
416
  throw err;
@@ -423,7 +437,7 @@ async function firefoxLauncher({ profilePath, proxy, timezoneId, maxWidth }) {
423
437
 
424
438
  const page = context.pages()[0];
425
439
 
426
- return createBrowserController(context, context, page, null);
440
+ return createBrowserController(context, context, page, null, humanize_options);
427
441
  } catch (err) {
428
442
  console.error("Firefox Persistent Launch Error:", err);
429
443
  throw err;
@@ -434,7 +448,7 @@ async function firefoxLauncher({ profilePath, proxy, timezoneId, maxWidth }) {
434
448
  // ==========================================================================
435
449
  // 6. ENGINE: BRAVE
436
450
  // ==========================================================================
437
- async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, maxWidth }) {
451
+ async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, maxWidth, humanize_options }) {
438
452
  const isPersistent = !!profilePath;
439
453
  const activePath = isPersistent ? profilePath : path.join(TEMP_DIR, crypto.randomUUID());
440
454
 
@@ -527,7 +541,7 @@ async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, maxWid
527
541
 
528
542
  const dirToDelete = isPersistent ? null : activePath;
529
543
  // Return the remaining open page (context.pages() might change, so we grab index 0 after cleanup)
530
- return createBrowserController(context, context, page, dirToDelete);
544
+ return createBrowserController(context, context, page, dirToDelete, humanize_options);
531
545
  }
532
546
 
533
547
  // ==========================================================================
@@ -632,7 +646,7 @@ async function camoufoxLauncher({ profilePath, proxy, timezoneId, maxWidth, camo
632
646
  // ==========================================================================
633
647
  // 8. ENGINE: MULTILOGIN
634
648
  // ==========================================================================
635
- async function multiloginLauncher({ proxy, multilogin_options = {} }) {
649
+ async function multiloginLauncher({ proxy, multilogin_options = {}, humanize_options = null }) {
636
650
  // Destructure defaults from multilogin_options
637
651
  const {
638
652
  profileId = null,
@@ -644,7 +658,7 @@ async function multiloginLauncher({ proxy, multilogin_options = {} }) {
644
658
  } = multilogin_options;
645
659
 
646
660
  if (profileId) {
647
- return await launchExistingMultiloginProfile(profileId);
661
+ return await launchExistingMultiloginProfile(profileId, humanize_options);
648
662
  } else {
649
663
  return await launchQuickMultiloginProfile({
650
664
  os_type,
@@ -652,11 +666,12 @@ async function multiloginLauncher({ proxy, multilogin_options = {} }) {
652
666
  canvas_noise,
653
667
  media_masking,
654
668
  audio_masking,
669
+ humanize_options,
655
670
  });
656
671
  }
657
672
  }
658
673
 
659
- async function launchExistingMultiloginProfile(profileId) {
674
+ async function launchExistingMultiloginProfile(profileId, humanize_options = null) {
660
675
  const startUrl = `${MULTILOGIN_LAUNCHER_URL}/api/v2/profile/f/${MULTILOGIN_FOLDER_ID}/p/${profileId}/start?automation_type=playwright&headless_mode=false`;
661
676
  let browser, context, page;
662
677
 
@@ -696,8 +711,13 @@ async function launchExistingMultiloginProfile(profileId) {
696
711
  context = browser.contexts()[0];
697
712
  page = context.pages()[0];
698
713
 
714
+ // Apply human cursor if options provided
715
+ if (humanize_options && page) {
716
+ page = createCursor(page, humanize_options);
717
+ }
718
+
699
719
  const closeBrowser = async () => {
700
- if (browser) await browser.close().catch(() => {});
720
+ if (browser) await browser.close().catch(() => { });
701
721
  return await stopMultiloginProfile(profileId);
702
722
  };
703
723
 
@@ -706,12 +726,12 @@ async function launchExistingMultiloginProfile(profileId) {
706
726
  console.error("Multilogin Launch Error:", error.message);
707
727
  try {
708
728
  await stopMultiloginProfile(profileId);
709
- } catch (e) {}
710
- return { data: null, error, closeBrowser: async () => {} };
729
+ } catch (e) { }
730
+ return { data: null, error, closeBrowser: async () => { } };
711
731
  }
712
732
  }
713
733
 
714
- async function launchQuickMultiloginProfile({ os_type, proxy, canvas_noise, media_masking, audio_masking }) {
734
+ async function launchQuickMultiloginProfile({ os_type, proxy, canvas_noise, media_masking, audio_masking, humanize_options = null }) {
715
735
  const createUrl = `${MULTILOGIN_LAUNCHER_URL}/api/v3/profile/quick`;
716
736
  let browser, context, page, profileId;
717
737
 
@@ -765,8 +785,13 @@ async function launchQuickMultiloginProfile({ os_type, proxy, canvas_noise, medi
765
785
  context = browser.contexts()[0];
766
786
  page = context.pages()[0];
767
787
 
788
+ // Apply human cursor if options provided
789
+ if (humanize_options && page) {
790
+ page = createCursor(page, humanize_options);
791
+ }
792
+
768
793
  const closeBrowser = async () => {
769
- if (browser) await browser.close().catch(() => {});
794
+ if (browser) await browser.close().catch(() => { });
770
795
  return await stopMultiloginProfile(profileId);
771
796
  };
772
797
 
@@ -774,7 +799,7 @@ async function launchQuickMultiloginProfile({ os_type, proxy, canvas_noise, medi
774
799
  } catch (error) {
775
800
  console.error("Quick Profile Error:", error);
776
801
  if (profileId) await stopMultiloginProfile(profileId);
777
- return { data: null, error, closeBrowser: async () => {} };
802
+ return { data: null, error, closeBrowser: async () => { } };
778
803
  }
779
804
  }
780
805
 
@@ -840,7 +865,13 @@ function formatProxy(proxy) {
840
865
  return p;
841
866
  }
842
867
 
843
- function createBrowserController(browser, context, page, dirToDelete = null) {
868
+ function createBrowserController(browser, context, page, dirToDelete = null, humanize_options = null) {
869
+ // Apply human cursor if options provided
870
+ let humanPage = page;
871
+ if (humanize_options && page) {
872
+ humanPage = createCursor(page, humanize_options);
873
+ }
874
+
844
875
  const closeBrowser = async () => {
845
876
  try {
846
877
  console.log("🔒 Closing browser session...");
@@ -848,7 +879,7 @@ function createBrowserController(browser, context, page, dirToDelete = null) {
848
879
  if (browser && typeof browser.close === "function" && browser !== context) {
849
880
  try {
850
881
  await browser.close();
851
- } catch (e) {}
882
+ } catch (e) { }
852
883
  }
853
884
  if (dirToDelete) {
854
885
  if (dirToDelete.includes("persistent")) {
@@ -864,5 +895,5 @@ function createBrowserController(browser, context, page, dirToDelete = null) {
864
895
  return false;
865
896
  }
866
897
  };
867
- return { browser, context, page, isBrowserRunning: () => !!context, closeBrowser, launchError: null };
898
+ return { browser, context, page: humanPage, isBrowserRunning: () => !!context, closeBrowser, launchError: null };
868
899
  }
@@ -1,73 +1,52 @@
1
1
  // multilogin_token_manager.js
2
2
 
3
3
  import crypto from "crypto";
4
- import path from "path";
5
- import { pathToFileURL } from "url";
6
- import fs from "fs";
4
+ import { arn, query } from "arn-knexjs";
7
5
 
8
- let config = null;
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;
9
11
 
10
12
  /**
11
- * Dynamically loads 'browser_automation_env.js' from the user's project root.
13
+ * Validates that all required environment variables are set.
12
14
  */
13
- async function loadUserConfig() {
14
- if (config) return config;
15
-
16
- try {
17
- const projectRoot = process.cwd();
18
- const envPath = path.join(projectRoot, "utility", "browser_automation_env.js");
19
-
20
- // Check if file exists before trying to import (cleaner error handling)
21
- if (!fs.existsSync(envPath)) {
22
- throw new Error(`Could not find configuration file at: ${envPath}`);
23
- }
24
-
25
- // Import dynamically
26
- const envUrl = pathToFileURL(envPath).href;
27
- const userEnv = await import(envUrl);
28
-
29
- // Validate required fields
30
- const requiredKeys = [
31
- "arn",
32
- "query",
33
- "MULTILOGIN_EMAIL",
34
- "MULTILOGIN_PASSWORD",
35
- "MULTILOGIN_WORKSPACE_ID",
36
- "ROW_ID",
37
- ];
38
-
39
- const missing = requiredKeys.filter((key) => !userEnv[key]);
15
+ function validateEnv() {
16
+ const required = {
17
+ MULTILOGIN_EMAIL,
18
+ MULTILOGIN_PASSWORD,
19
+ MULTILOGIN_WORKSPACE_ID,
20
+ MULTILOGIN_ROW_ID,
21
+ };
40
22
 
41
- if (missing.length > 0) {
42
- throw new Error(`[TokenManager] 'browser_automation_env.js' is missing exports: ${missing.join(", ")}`);
43
- }
23
+ const missing = Object.entries(required)
24
+ .filter(([, value]) => !value)
25
+ .map(([key]) => key);
44
26
 
45
- config = userEnv;
46
- return config;
47
- } catch (error) {
48
- console.error("❌ Config Error:", error.message);
49
- throw error;
27
+ if (missing.length > 0) {
28
+ throw new Error(`[TokenManager] Missing environment variables: ${missing.join(", ")}`);
50
29
  }
51
30
  }
52
31
 
53
32
  async function saveTokens(tokens) {
54
- const { arn, query, ROW_ID } = await loadUserConfig();
33
+ validateEnv();
55
34
 
56
35
  await arn.single(
57
36
  query("api_multilogin_token")
58
37
  .update({
59
38
  data: JSON.stringify(tokens, null, 2),
60
39
  })
61
- .where({ id: ROW_ID })
40
+ .where({ id: MULTILOGIN_ROW_ID })
62
41
  );
63
42
  }
64
43
 
65
44
  async function loadTokens() {
66
45
  try {
67
- const { arn, query, ROW_ID } = await loadUserConfig();
46
+ validateEnv();
68
47
 
69
48
  const { data: [data] = [], error } = await arn.single(
70
- query("api_multilogin_token").select("*").where({ id: ROW_ID }).limit(1)
49
+ query("api_multilogin_token").select("*").where({ id: MULTILOGIN_ROW_ID }).limit(1)
71
50
  );
72
51
  if (error) {
73
52
  console.error("Error loading tokens:", error);
@@ -94,8 +73,7 @@ function isJwtExpired(token) {
94
73
  }
95
74
 
96
75
  async function loginAndSaveTokens() {
97
- // Destructure using the NEW names
98
- const { MULTILOGIN_EMAIL, MULTILOGIN_PASSWORD, MULTILOGIN_WORKSPACE_ID } = await loadUserConfig();
76
+ validateEnv();
99
77
 
100
78
  const passwordHash = crypto.createHash("md5").update(MULTILOGIN_PASSWORD).digest("hex");
101
79
  const data = {
@@ -129,7 +107,7 @@ async function loginAndSaveTokens() {
129
107
  }
130
108
 
131
109
  async function refreshAndSaveTokens(refresh_token) {
132
- const { MULTILOGIN_EMAIL, MULTILOGIN_WORKSPACE_ID } = await loadUserConfig();
110
+ validateEnv();
133
111
 
134
112
  const data = {
135
113
  email: MULTILOGIN_EMAIL,
@@ -163,7 +141,7 @@ async function refreshAndSaveTokens(refresh_token) {
163
141
  }
164
142
 
165
143
  async function getMultiloginToken() {
166
- await loadUserConfig();
144
+ validateEnv();
167
145
  let tokens = await loadTokens();
168
146
 
169
147
  if (tokens && tokens.token && !isJwtExpired(tokens.token)) {