@aluvia/sdk 1.0.0 → 1.3.0

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 (77) hide show
  1. package/README.md +425 -256
  2. package/dist/cjs/api/account.js +10 -74
  3. package/dist/cjs/api/apiUtils.js +80 -0
  4. package/dist/cjs/api/geos.js +2 -63
  5. package/dist/cjs/api/request.js +8 -2
  6. package/dist/cjs/bin/account.js +31 -0
  7. package/dist/cjs/bin/api-helpers.js +58 -0
  8. package/dist/cjs/bin/cli.js +245 -0
  9. package/dist/cjs/bin/close.js +120 -0
  10. package/dist/cjs/bin/geos.js +10 -0
  11. package/dist/cjs/bin/mcp-helpers.js +57 -0
  12. package/dist/cjs/bin/mcp-server.js +220 -0
  13. package/dist/cjs/bin/mcp-tools.js +90 -0
  14. package/dist/cjs/bin/open.js +293 -0
  15. package/dist/cjs/bin/session.js +259 -0
  16. package/dist/cjs/client/AluviaClient.js +411 -150
  17. package/dist/cjs/client/BlockDetection.js +486 -0
  18. package/dist/cjs/client/ConfigManager.js +26 -23
  19. package/dist/cjs/client/PageLoadDetection.js +175 -0
  20. package/dist/cjs/client/ProxyServer.js +4 -2
  21. package/dist/cjs/client/logger.js +4 -0
  22. package/dist/cjs/client/rules.js +38 -49
  23. package/dist/cjs/connect.js +117 -0
  24. package/dist/cjs/errors.js +12 -1
  25. package/dist/cjs/index.js +5 -1
  26. package/dist/cjs/session/lock.js +186 -0
  27. package/dist/esm/api/account.js +2 -66
  28. package/dist/esm/api/apiUtils.js +71 -0
  29. package/dist/esm/api/geos.js +2 -63
  30. package/dist/esm/api/request.js +8 -2
  31. package/dist/esm/bin/account.js +28 -0
  32. package/dist/esm/bin/api-helpers.js +53 -0
  33. package/dist/esm/bin/cli.js +242 -0
  34. package/dist/esm/bin/close.js +117 -0
  35. package/dist/esm/bin/geos.js +7 -0
  36. package/dist/esm/bin/mcp-helpers.js +51 -0
  37. package/dist/esm/bin/mcp-server.js +185 -0
  38. package/dist/esm/bin/mcp-tools.js +78 -0
  39. package/dist/esm/bin/open.js +256 -0
  40. package/dist/esm/bin/session.js +252 -0
  41. package/dist/esm/client/AluviaClient.js +384 -156
  42. package/dist/esm/client/BlockDetection.js +482 -0
  43. package/dist/esm/client/ConfigManager.js +21 -18
  44. package/dist/esm/client/PageLoadDetection.js +171 -0
  45. package/dist/esm/client/ProxyServer.js +5 -3
  46. package/dist/esm/client/logger.js +4 -0
  47. package/dist/esm/client/rules.js +36 -49
  48. package/dist/esm/connect.js +81 -0
  49. package/dist/esm/errors.js +10 -0
  50. package/dist/esm/index.js +5 -3
  51. package/dist/esm/session/lock.js +142 -0
  52. package/dist/types/api/AluviaApi.d.ts +2 -7
  53. package/dist/types/api/account.d.ts +1 -16
  54. package/dist/types/api/apiUtils.d.ts +28 -0
  55. package/dist/types/api/geos.d.ts +1 -1
  56. package/dist/types/bin/account.d.ts +1 -0
  57. package/dist/types/bin/api-helpers.d.ts +20 -0
  58. package/dist/types/bin/cli.d.ts +2 -0
  59. package/dist/types/bin/close.d.ts +1 -0
  60. package/dist/types/bin/geos.d.ts +1 -0
  61. package/dist/types/bin/mcp-helpers.d.ts +28 -0
  62. package/dist/types/bin/mcp-server.d.ts +2 -0
  63. package/dist/types/bin/mcp-tools.d.ts +46 -0
  64. package/dist/types/bin/open.d.ts +21 -0
  65. package/dist/types/bin/session.d.ts +11 -0
  66. package/dist/types/client/AluviaClient.d.ts +51 -4
  67. package/dist/types/client/BlockDetection.d.ts +96 -0
  68. package/dist/types/client/ConfigManager.d.ts +6 -1
  69. package/dist/types/client/PageLoadDetection.d.ts +93 -0
  70. package/dist/types/client/logger.d.ts +2 -0
  71. package/dist/types/client/rules.d.ts +18 -0
  72. package/dist/types/client/types.d.ts +69 -46
  73. package/dist/types/connect.d.ts +18 -0
  74. package/dist/types/errors.d.ts +6 -0
  75. package/dist/types/index.d.ts +7 -5
  76. package/dist/types/session/lock.d.ts +43 -0
  77. package/package.json +11 -2
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ // PageLoadDetection - Enhanced page load and blocking detection
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.PageLoadDetection = void 0;
5
+ const DEFAULT_BLOCKING_KEYWORDS = [
6
+ "captcha",
7
+ "blocked",
8
+ "access denied",
9
+ "forbidden",
10
+ "cloudflare",
11
+ "please verify",
12
+ "recaptcha",
13
+ "hcaptcha",
14
+ "bot detection",
15
+ "automated access",
16
+ "unusual activity",
17
+ "verify you are human",
18
+ "security check",
19
+ "access restricted",
20
+ ];
21
+ const DEFAULT_BLOCKING_STATUS_CODES = [403, 429, 503];
22
+ const DEFAULT_MIN_CONTENT_LENGTH = 100;
23
+ /**
24
+ * PageLoadDetection handles enhanced detection of page load failures and blocking
25
+ */
26
+ class PageLoadDetection {
27
+ constructor(config, logger) {
28
+ this.blockedHostnames = new Set();
29
+ this.logger = logger;
30
+ this.config = {
31
+ enabled: config.enabled ?? true,
32
+ blockingKeywords: config.blockingKeywords ?? DEFAULT_BLOCKING_KEYWORDS,
33
+ blockingStatusCodes: config.blockingStatusCodes ?? DEFAULT_BLOCKING_STATUS_CODES,
34
+ minContentLength: config.minContentLength ?? DEFAULT_MIN_CONTENT_LENGTH,
35
+ autoAddRules: config.autoAddRules ?? false,
36
+ onBlockingDetected: config.onBlockingDetected,
37
+ };
38
+ }
39
+ /**
40
+ * Update detection configuration
41
+ */
42
+ updateConfig(config) {
43
+ if (config.enabled !== undefined) {
44
+ this.config.enabled = config.enabled;
45
+ }
46
+ if (config.blockingKeywords !== undefined) {
47
+ this.config.blockingKeywords = config.blockingKeywords;
48
+ }
49
+ if (config.blockingStatusCodes !== undefined) {
50
+ this.config.blockingStatusCodes = config.blockingStatusCodes;
51
+ }
52
+ if (config.minContentLength !== undefined) {
53
+ this.config.minContentLength = config.minContentLength;
54
+ }
55
+ if (config.autoAddRules !== undefined) {
56
+ this.config.autoAddRules = config.autoAddRules;
57
+ }
58
+ if (config.onBlockingDetected !== undefined) {
59
+ this.config.onBlockingDetected = config.onBlockingDetected;
60
+ }
61
+ }
62
+ /**
63
+ * Check if a hostname is already marked as blocked
64
+ */
65
+ isHostnameBlocked(hostname) {
66
+ return this.blockedHostnames.has(hostname);
67
+ }
68
+ /**
69
+ * Get all blocked hostnames
70
+ */
71
+ getBlockedHostnames() {
72
+ return Array.from(this.blockedHostnames);
73
+ }
74
+ /**
75
+ * Clear blocked hostnames cache
76
+ */
77
+ clearBlockedHostnames() {
78
+ this.blockedHostnames.clear();
79
+ }
80
+ /**
81
+ * Analyze a page load and detect if it was blocked
82
+ */
83
+ async analyzePage(page, response) {
84
+ if (!this.config.enabled) {
85
+ return {
86
+ url: page.url(),
87
+ hostname: this.extractHostname(page.url()),
88
+ success: true,
89
+ blocked: false,
90
+ };
91
+ }
92
+ this.logger.debug("Analyzing page load for URL: " + page.url());
93
+ const url = page.url();
94
+ const hostname = this.extractHostname(url);
95
+ try {
96
+ // Check HTTP status code
97
+ const statusCode = response?.status?.() ?? 0;
98
+ if (statusCode > 0 &&
99
+ this.config.blockingStatusCodes.includes(statusCode)) {
100
+ const reason = {
101
+ type: "status_code",
102
+ details: `HTTP status code ${statusCode} indicates blocking`,
103
+ statusCode,
104
+ };
105
+ await this.handleBlocking(hostname, reason, page);
106
+ return { url, hostname, success: false, blocked: true, reason };
107
+ }
108
+ // Get page content
109
+ const content = await page.content().catch(() => "");
110
+ // Check for blocking keywords first (more specific than content length)
111
+ const contentLower = content.toLowerCase();
112
+ const title = (await page.title().catch(() => "")).toLowerCase();
113
+ const combinedText = `${contentLower} ${title}`;
114
+ for (const keyword of this.config.blockingKeywords) {
115
+ if (combinedText.includes(keyword.toLowerCase())) {
116
+ const reason = {
117
+ type: "keyword",
118
+ details: `Blocking keyword detected: "${keyword}"`,
119
+ keyword,
120
+ };
121
+ await this.handleBlocking(hostname, reason, page);
122
+ return { url, hostname, success: false, blocked: true, reason };
123
+ }
124
+ }
125
+ // Check content length (less critical than keywords)
126
+ if (!content || content.length < this.config.minContentLength) {
127
+ const reason = {
128
+ type: "content_length",
129
+ details: `Page content too short (${content.length} < ${this.config.minContentLength})`,
130
+ };
131
+ this.logger.warn(`Page may have failed to load: ${url} (${reason.details})`);
132
+ return { url, hostname, success: false, blocked: false, reason };
133
+ }
134
+ // Page loaded successfully
135
+ return { url, hostname, success: true, blocked: false };
136
+ }
137
+ catch (error) {
138
+ const reason = {
139
+ type: "error",
140
+ details: `Error analyzing page: ${error.message}`,
141
+ };
142
+ this.logger.warn(`Error checking page load status for ${url}: ${error.message}`);
143
+ return { url, hostname, success: false, blocked: false, reason };
144
+ }
145
+ }
146
+ /**
147
+ * Handle detected blocking
148
+ */
149
+ async handleBlocking(hostname, reason, page) {
150
+ this.blockedHostnames.add(hostname);
151
+ this.logger.warn(`Blocking detected for ${hostname}: ${reason.details}`);
152
+ // Trigger callback if provided
153
+ if (this.config.onBlockingDetected) {
154
+ try {
155
+ await this.config.onBlockingDetected(hostname, reason, page);
156
+ }
157
+ catch (error) {
158
+ this.logger.warn(`Error in onBlockingDetected callback: ${error.message}`);
159
+ }
160
+ }
161
+ }
162
+ /**
163
+ * Extract hostname from URL
164
+ */
165
+ extractHostname(url) {
166
+ try {
167
+ const parsed = new URL(url);
168
+ return parsed.hostname;
169
+ }
170
+ catch {
171
+ return url;
172
+ }
173
+ }
174
+ }
175
+ exports.PageLoadDetection = PageLoadDetection;
@@ -97,8 +97,10 @@ class ProxyServer {
97
97
  this.logger.debug('Could not extract hostname, going direct');
98
98
  return undefined;
99
99
  }
100
- // Check if we should proxy this hostname
101
- const useProxy = (0, rules_js_1.shouldProxy)(hostname, config.rules);
100
+ // Check if we should proxy this hostname (use pre-normalized rules if available)
101
+ const useProxy = config.normalizedRules
102
+ ? (0, rules_js_1.shouldProxyNormalized)(hostname, config.normalizedRules)
103
+ : (0, rules_js_1.shouldProxy)(hostname, config.rules);
102
104
  if (!useProxy) {
103
105
  this.logger.debug(`Hostname ${hostname} bypassing proxy (direct)`);
104
106
  return undefined;
@@ -32,6 +32,10 @@ class Logger {
32
32
  console.debug('[aluvia][debug]', ...args);
33
33
  }
34
34
  }
35
+ /** Check if debug logging is enabled. */
36
+ get isDebug() {
37
+ return this.level === 'debug';
38
+ }
35
39
  /**
36
40
  * Log warning messages.
37
41
  * Logs when level is not 'silent'.
@@ -2,6 +2,8 @@
2
2
  // Rule engine for hostname matching and proxy decision
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.matchPattern = matchPattern;
5
+ exports.normalizeRules = normalizeRules;
6
+ exports.shouldProxyNormalized = shouldProxyNormalized;
5
7
  exports.shouldProxy = shouldProxy;
6
8
  /**
7
9
  * Match a hostname against a pattern.
@@ -55,48 +57,26 @@ function matchPattern(hostname, pattern) {
55
57
  return false;
56
58
  }
57
59
  /**
58
- * Determine if a hostname should be proxied based on rules.
59
- *
60
- * Rules semantics:
61
- * - [] (empty) → no proxy (return false)
62
- * - ['*'] → proxy everything
63
- * - ['example.com'] → proxy only example.com
64
- * - ['*.google.com'] → proxy subdomains of google.com
65
- * - ['*', '-example.com'] → proxy everything except example.com
66
- * - ['AUTO', 'example.com'] → AUTO is placeholder (ignored), proxy example.com
67
- *
68
- * Negative patterns (prefixed with '-') exclude hosts from proxying.
69
- * If '*' is in rules, default is to proxy unless excluded.
70
- * Without '*', only explicitly matched patterns are proxied.
71
- *
72
- * @param hostname - The hostname to check
73
- * @param rules - Array of rule patterns
74
- * @returns true if the hostname should be proxied
60
+ * Pre-process raw rule strings into a NormalizedRules structure.
61
+ * Call once when config is loaded, then use shouldProxyNormalized() per request.
75
62
  */
76
- function shouldProxy(hostname, rules) {
77
- const normalizedHostname = hostname.trim();
78
- if (!normalizedHostname)
79
- return false;
80
- // Empty rules means no proxy
63
+ function normalizeRules(rules) {
81
64
  if (!rules || rules.length === 0) {
82
- return false;
65
+ return { positiveRules: [], negativeRules: [], hasCatchAll: false, empty: true };
83
66
  }
84
- const normalizedRules = rules
67
+ const trimmed = rules
85
68
  .filter((r) => typeof r === 'string')
86
- .map((r) => r.trim())
87
- .filter((r) => r.length > 0);
88
- // Filter out AUTO placeholder
89
- const effectiveRules = normalizedRules.filter((r) => r.toUpperCase() !== 'AUTO');
90
- // If no effective rules after filtering, no proxy
91
- if (effectiveRules.length === 0) {
92
- return false;
69
+ .map((r) => r.trim().toLowerCase())
70
+ .filter((r) => r.length > 0)
71
+ .filter((r) => r !== 'auto');
72
+ if (trimmed.length === 0) {
73
+ return { positiveRules: [], negativeRules: [], hasCatchAll: false, empty: true };
93
74
  }
94
- // Separate positive and negative rules
95
75
  const negativeRules = [];
96
76
  const positiveRules = [];
97
- for (const rule of effectiveRules) {
77
+ for (const rule of trimmed) {
98
78
  if (rule.startsWith('-')) {
99
- const neg = rule.slice(1).trim(); // Remove the '-' prefix
79
+ const neg = rule.slice(1).trim();
100
80
  if (neg.length > 0)
101
81
  negativeRules.push(neg);
102
82
  }
@@ -104,25 +84,34 @@ function shouldProxy(hostname, rules) {
104
84
  positiveRules.push(rule);
105
85
  }
106
86
  }
107
- // Check if hostname matches any negative rule
108
- for (const negRule of negativeRules) {
109
- if (matchPattern(normalizedHostname, negRule)) {
110
- // Excluded by negative rule
87
+ return {
88
+ positiveRules,
89
+ negativeRules,
90
+ hasCatchAll: positiveRules.includes('*'),
91
+ empty: false,
92
+ };
93
+ }
94
+ /**
95
+ * Fast proxy decision using pre-normalized rules.
96
+ */
97
+ function shouldProxyNormalized(hostname, rules) {
98
+ const normalizedHostname = hostname.trim().toLowerCase();
99
+ if (!normalizedHostname)
100
+ return false;
101
+ if (rules.empty)
102
+ return false;
103
+ for (const negRule of rules.negativeRules) {
104
+ if (matchPattern(normalizedHostname, negRule))
111
105
  return false;
112
- }
113
106
  }
114
- // Check if we have a catch-all '*'
115
- const hasCatchAll = positiveRules.includes('*');
116
- if (hasCatchAll) {
117
- // With catch-all, proxy everything not excluded by negative rules
107
+ if (rules.hasCatchAll)
118
108
  return true;
119
- }
120
- // Without catch-all, check if hostname matches any positive rule
121
- for (const posRule of positiveRules) {
122
- if (matchPattern(normalizedHostname, posRule)) {
109
+ for (const posRule of rules.positiveRules) {
110
+ if (matchPattern(normalizedHostname, posRule))
123
111
  return true;
124
- }
125
112
  }
126
- // No match found
127
113
  return false;
128
114
  }
115
+ function shouldProxy(hostname, rules) {
116
+ return shouldProxyNormalized(hostname, normalizeRules(rules));
117
+ }
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.connect = connect;
37
+ const lock_js_1 = require("./session/lock.js");
38
+ const errors_js_1 = require("./errors.js");
39
+ /**
40
+ * Connect to a running Aluvia browser session via CDP.
41
+ *
42
+ * - No args: auto-discovers a single running session.
43
+ * - With session name: connects to that specific session.
44
+ *
45
+ * Requires `playwright` as a peer dependency.
46
+ */
47
+ async function connect(sessionName) {
48
+ // 1. Import Playwright
49
+ let pw;
50
+ try {
51
+ pw = await Promise.resolve().then(() => __importStar(require('playwright')));
52
+ }
53
+ catch {
54
+ throw new errors_js_1.ConnectError('Playwright is required for connect(). Install it: npm install playwright');
55
+ }
56
+ // 2. Resolve session
57
+ let resolvedName;
58
+ if (sessionName) {
59
+ resolvedName = sessionName;
60
+ }
61
+ else {
62
+ const sessions = (0, lock_js_1.listSessions)();
63
+ if (sessions.length === 0) {
64
+ throw new errors_js_1.ConnectError('No running Aluvia sessions found. Start one with: npx aluvia-sdk session start <url>');
65
+ }
66
+ if (sessions.length > 1) {
67
+ const names = sessions.map((s) => s.session).join(', ');
68
+ throw new errors_js_1.ConnectError(`Multiple Aluvia sessions running (${names}). Specify which one: connect('${sessions[0].session}')`);
69
+ }
70
+ resolvedName = sessions[0].session;
71
+ }
72
+ // 3. Validate session state
73
+ const lock = (0, lock_js_1.readLock)(resolvedName);
74
+ if (!lock) {
75
+ throw new errors_js_1.ConnectError(`No Aluvia session found named '${resolvedName}'. Run 'npx aluvia-sdk session list' to list sessions.`);
76
+ }
77
+ if (!(0, lock_js_1.isProcessAlive)(lock.pid)) {
78
+ (0, lock_js_1.removeLock)(resolvedName);
79
+ throw new errors_js_1.ConnectError(`Session '${resolvedName}' is no longer running. Stale lock file removed.`);
80
+ }
81
+ if (!lock.ready) {
82
+ throw new errors_js_1.ConnectError(`Session '${resolvedName}' is still starting up. Try again shortly.`);
83
+ }
84
+ if (!lock.cdpUrl) {
85
+ throw new errors_js_1.ConnectError(`Session '${resolvedName}' has no CDP URL.`);
86
+ }
87
+ // 4. Connect over CDP
88
+ let browser;
89
+ try {
90
+ browser = await pw.chromium.connectOverCDP(lock.cdpUrl);
91
+ }
92
+ catch (err) {
93
+ throw new errors_js_1.ConnectError(`Failed to connect to session '${resolvedName}' at ${lock.cdpUrl}: ${err.message}`);
94
+ }
95
+ // 5. Get context and page
96
+ let context;
97
+ let page;
98
+ try {
99
+ context = browser.contexts()[0] ?? await browser.newContext();
100
+ page = context.pages()[0] ?? await context.newPage();
101
+ }
102
+ catch (err) {
103
+ await browser.close().catch(() => { });
104
+ throw new errors_js_1.ConnectError(`Connected but failed to get page: ${err.message}`);
105
+ }
106
+ return {
107
+ browser,
108
+ context,
109
+ page,
110
+ sessionName: resolvedName,
111
+ cdpUrl: lock.cdpUrl,
112
+ connectionId: lock.connectionId,
113
+ disconnect: async () => {
114
+ await browser.close();
115
+ },
116
+ };
117
+ }
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  // Error classes for Aluvia Client
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.ProxyStartError = exports.ApiError = exports.InvalidApiKeyError = exports.MissingApiKeyError = void 0;
4
+ exports.ConnectError = exports.ProxyStartError = exports.ApiError = exports.InvalidApiKeyError = exports.MissingApiKeyError = void 0;
5
5
  /**
6
6
  * Thrown when the apiKey is not provided to AluviaClient.
7
7
  */
@@ -47,3 +47,14 @@ class ProxyStartError extends Error {
47
47
  }
48
48
  }
49
49
  exports.ProxyStartError = ProxyStartError;
50
+ /**
51
+ * Thrown by connect() when it cannot establish a CDP connection to a running session.
52
+ */
53
+ class ConnectError extends Error {
54
+ constructor(message) {
55
+ super(message);
56
+ this.name = 'ConnectError';
57
+ Object.setPrototypeOf(this, ConnectError.prototype);
58
+ }
59
+ }
60
+ exports.ConnectError = ConnectError;
package/dist/cjs/index.js CHANGED
@@ -2,15 +2,19 @@
2
2
  // Aluvia Client Node
3
3
  // Main entry point
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.ProxyStartError = exports.ApiError = exports.InvalidApiKeyError = exports.MissingApiKeyError = exports.AluviaApi = exports.AluviaClient = void 0;
5
+ exports.ConnectError = exports.ProxyStartError = exports.ApiError = exports.InvalidApiKeyError = exports.MissingApiKeyError = exports.connect = exports.AluviaApi = exports.AluviaClient = void 0;
6
6
  // Public class
7
7
  var AluviaClient_js_1 = require("./client/AluviaClient.js");
8
8
  Object.defineProperty(exports, "AluviaClient", { enumerable: true, get: function () { return AluviaClient_js_1.AluviaClient; } });
9
9
  var AluviaApi_js_1 = require("./api/AluviaApi.js");
10
10
  Object.defineProperty(exports, "AluviaApi", { enumerable: true, get: function () { return AluviaApi_js_1.AluviaApi; } });
11
+ // Connect helper
12
+ var connect_js_1 = require("./connect.js");
13
+ Object.defineProperty(exports, "connect", { enumerable: true, get: function () { return connect_js_1.connect; } });
11
14
  // Public error classes
12
15
  var errors_js_1 = require("./errors.js");
13
16
  Object.defineProperty(exports, "MissingApiKeyError", { enumerable: true, get: function () { return errors_js_1.MissingApiKeyError; } });
14
17
  Object.defineProperty(exports, "InvalidApiKeyError", { enumerable: true, get: function () { return errors_js_1.InvalidApiKeyError; } });
15
18
  Object.defineProperty(exports, "ApiError", { enumerable: true, get: function () { return errors_js_1.ApiError; } });
16
19
  Object.defineProperty(exports, "ProxyStartError", { enumerable: true, get: function () { return errors_js_1.ProxyStartError; } });
20
+ Object.defineProperty(exports, "ConnectError", { enumerable: true, get: function () { return errors_js_1.ConnectError; } });
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.writeLock = writeLock;
37
+ exports.readLock = readLock;
38
+ exports.removeLock = removeLock;
39
+ exports.isProcessAlive = isProcessAlive;
40
+ exports.getLogFilePath = getLogFilePath;
41
+ exports.validateSessionName = validateSessionName;
42
+ exports.generateSessionName = generateSessionName;
43
+ exports.toLockData = toLockData;
44
+ exports.listSessions = listSessions;
45
+ const fs = __importStar(require("node:fs"));
46
+ const path = __importStar(require("node:path"));
47
+ const os = __importStar(require("node:os"));
48
+ const LOCK_DIR = path.join(os.tmpdir(), 'aluvia-sdk');
49
+ const ADJECTIVES = [
50
+ 'swift', 'bold', 'calm', 'keen', 'warm', 'bright', 'silent', 'rapid', 'steady', 'clever',
51
+ 'vivid', 'agile', 'noble', 'lucid', 'crisp', 'gentle', 'fierce', 'nimble', 'sturdy', 'witty',
52
+ ];
53
+ const NOUNS = [
54
+ 'falcon', 'tiger', 'river', 'maple', 'coral', 'cedar', 'orbit', 'prism', 'flint', 'spark',
55
+ 'ridge', 'ember', 'crane', 'grove', 'stone', 'brook', 'drift', 'crest', 'sage', 'lynx',
56
+ ];
57
+ function lockFileName(sessionName) {
58
+ return `cli-${sessionName ?? 'default'}.lock`;
59
+ }
60
+ function logFileName(sessionName) {
61
+ return `cli-${sessionName ?? 'default'}.log`;
62
+ }
63
+ function writeLock(data, sessionName) {
64
+ fs.mkdirSync(LOCK_DIR, { recursive: true });
65
+ const filePath = path.join(LOCK_DIR, lockFileName(sessionName));
66
+ const tmpPath = filePath + '.tmp';
67
+ try {
68
+ fs.writeFileSync(tmpPath, JSON.stringify(data), 'utf-8');
69
+ try {
70
+ // On Windows, rename may fail to overwrite an existing file; remove it first.
71
+ fs.rmSync(filePath, { force: true });
72
+ }
73
+ catch {
74
+ // Ignore errors removing the existing lock file.
75
+ }
76
+ fs.renameSync(tmpPath, filePath);
77
+ }
78
+ finally {
79
+ try {
80
+ // Ensure no leftover temp file remains if rename fails.
81
+ fs.rmSync(tmpPath, { force: true });
82
+ }
83
+ catch {
84
+ // Ignore cleanup errors.
85
+ }
86
+ }
87
+ }
88
+ function readLock(sessionName) {
89
+ try {
90
+ const filePath = path.join(LOCK_DIR, lockFileName(sessionName));
91
+ const raw = fs.readFileSync(filePath, 'utf-8').trim();
92
+ const parsed = JSON.parse(raw);
93
+ if (typeof parsed.pid === 'number' && Number.isFinite(parsed.pid)) {
94
+ return parsed;
95
+ }
96
+ return null;
97
+ }
98
+ catch {
99
+ return null;
100
+ }
101
+ }
102
+ function removeLock(sessionName) {
103
+ try {
104
+ const filePath = path.join(LOCK_DIR, lockFileName(sessionName));
105
+ fs.unlinkSync(filePath);
106
+ }
107
+ catch {
108
+ // ignore
109
+ }
110
+ }
111
+ function isProcessAlive(pid) {
112
+ try {
113
+ process.kill(pid, 0); // signal 0 = check existence
114
+ return true;
115
+ }
116
+ catch {
117
+ return false;
118
+ }
119
+ }
120
+ function getLogFilePath(sessionName) {
121
+ fs.mkdirSync(LOCK_DIR, { recursive: true });
122
+ return path.join(LOCK_DIR, logFileName(sessionName));
123
+ }
124
+ function validateSessionName(name) {
125
+ return /^[a-zA-Z0-9_-]+$/.test(name);
126
+ }
127
+ function generateSessionName() {
128
+ const maxAttempts = 10;
129
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
130
+ const adj = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)];
131
+ const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)];
132
+ const name = attempt === 0 ? `${adj}-${noun}` : `${adj}-${noun}-${attempt}`;
133
+ const filePath = path.join(LOCK_DIR, lockFileName(name));
134
+ if (!fs.existsSync(filePath)) {
135
+ return name;
136
+ }
137
+ // Lock file exists — check if the process is still alive
138
+ const lock = readLock(name);
139
+ if (!lock || !isProcessAlive(lock.pid)) {
140
+ // Stale lock, we can reuse this name
141
+ removeLock(name);
142
+ return name;
143
+ }
144
+ }
145
+ // Fallback: use timestamp
146
+ return `session-${Date.now()}`;
147
+ }
148
+ function toLockData(info) {
149
+ const { session: _session, ...lock } = info;
150
+ return lock;
151
+ }
152
+ function listSessions() {
153
+ fs.mkdirSync(LOCK_DIR, { recursive: true });
154
+ const files = fs.readdirSync(LOCK_DIR);
155
+ const sessions = [];
156
+ for (const file of files) {
157
+ const match = file.match(/^cli-(.+)\.lock$/);
158
+ if (!match)
159
+ continue;
160
+ const sessionName = match[1];
161
+ const lock = readLock(sessionName);
162
+ if (!lock) {
163
+ // Corrupt lock file, clean up
164
+ removeLock(sessionName);
165
+ continue;
166
+ }
167
+ if (!isProcessAlive(lock.pid)) {
168
+ // Stale lock, clean up
169
+ removeLock(sessionName);
170
+ continue;
171
+ }
172
+ sessions.push({
173
+ session: sessionName,
174
+ pid: lock.pid,
175
+ connectionId: lock.connectionId,
176
+ cdpUrl: lock.cdpUrl,
177
+ proxyUrl: lock.proxyUrl,
178
+ url: lock.url,
179
+ ready: lock.ready,
180
+ blockDetection: lock.blockDetection,
181
+ autoUnblock: lock.autoUnblock,
182
+ lastDetection: lock.lastDetection,
183
+ });
184
+ }
185
+ return sessions;
186
+ }