entrox-bypass 0.0.0-stage → 0.2.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.
package/README.md CHANGED
@@ -1,3 +1,250 @@
1
- # Temporary Holding Version
1
+ # playwright-captcha
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ > AI-powered automated CAPTCHA solving for Playwright browser automation
4
+
5
+ [![npm version](https://img.shields.io/npm/v/playwright-captcha.svg)](https://www.npmjs.com/package/playwright-captcha)
6
+ [![License: ISC](https://img.shields.io/badge/license-ISC-blue.svg)](LICENSE)
7
+
8
+ Automated login & CAPTCHA solving built on [Playwright](https://playwright.dev/), [LangChain](https://www.langchain.com/), and Cloudinary. Solves CAPTCHAs in real-time using AI image recognition via OpenRouter (Google Gemma).
9
+
10
+ ## Features
11
+
12
+ - 🤖 **AI-Powered CAPTCHA Solving** — Uses LLMs to read and solve CAPTCHA images automatically
13
+ - 🔐 **Automated Login Flow** — Full end-to-end browser automation with credentials
14
+ - 📦 **Dual Module Support** — Works with both ESM (`import`) and CJS (`require`)
15
+ - ✍️ **TypeScript First** — Full type definitions included
16
+ - ☁️ **Cloudinary Integration** — Efficient image upload & processing pipeline
17
+ - 🧩 **Modular Design** — Use individual functions or the full automation suite
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install playwright-captcha playwright
23
+ npx playwright install chromium
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ ### Environment Variables
29
+
30
+ Create a `.env` file with your credentials:
31
+
32
+ ```env
33
+ # Required: Login credentials
34
+ EMAIL=your_email@example.com
35
+ PASSWORD=your_password
36
+
37
+ # Required: AI CAPTCHA solver
38
+ OPENROUTER_API_KEY=sk-or-v1-your-api-key
39
+
40
+ # Required: Image hosting
41
+ CLOUDINARY_CLOUD_NAME=your_cloud_name
42
+ CLOUDINARY_API_KEY=your_api_key
43
+ CLOUDINARY_API_SECRET=your_api_secret
44
+ ```
45
+
46
+ ### Full Automated Login
47
+
48
+ ```javascript
49
+ import { automateLogin } from 'playwright-captcha';
50
+
51
+ (async () => {
52
+ const result = await automateLogin({
53
+ url: 'https://example.com/login',
54
+ headless: false, // Set true for server environments
55
+ outputScreenshotPath: './result.png' // Optional screenshot after login
56
+ });
57
+
58
+ if (result.success) {
59
+ console.log('Logged in successfully!');
60
+ console.log('Current URL:', result.url);
61
+ console.log('Page title:', result.title);
62
+ } else {
63
+ console.error('Login failed:', result.error);
64
+ }
65
+ })();
66
+ ```
67
+
68
+ ### Solve CAPTCHA Manually
69
+
70
+ ```javascript
71
+ import { chromium } from 'playwright';
72
+ import { solveCaptchaFromPage } from 'playwright-captcha';
73
+
74
+ const browser = await chromium.launch();
75
+ const page = await browser.newPage();
76
+
77
+ await page.goto('https://example.com/login');
78
+ // ... fill form fields ...
79
+
80
+ // Extract CAPTCHA text from the page
81
+ const captcha = await solveCaptchaFromPage(page);
82
+ console.log('CAPTCHA text:', captcha.text); // e.g., "X7K9P"
83
+ console.log('Description:', captcha.description);
84
+
85
+ await browser.close();
86
+ ```
87
+
88
+ ## API Reference
89
+
90
+ ### `automateLogin(options)`
91
+
92
+ Automates the entire login process including CAPTCHA solving.
93
+
94
+ **Parameters:**
95
+
96
+ | Option | Type | Required | Default | Description |
97
+ |--------|------|----------|---------|-------------|
98
+ | `url` | `string` | ✅ Yes | — | Target login page URL |
99
+ | `loginSelector` | `string` | ❌ No | `#login` | CSS selector for email/login field |
100
+ | `passwordSelector` | `string` | ❌ No | `#password` | CSS selector for password field |
101
+ | `captchaInputSelector` | `string` | ❌ No | `#captcha` | CSS selector for CAPTCHA input field |
102
+ | `submitButtonSelector` | `string` | ❌ No | `button[type='submit']` | CSS selector for submit button |
103
+ | `headless` | `boolean` | ❌ No | `false` | Run browser in headless mode |
104
+ | `outputScreenshotPath` | `string` | ❌ No | — | Path to save post-login screenshot |
105
+ | `screenshotDelay` | `number` | ❌ No | — | Milliseconds to wait before screenshot |
106
+
107
+ **Returns:** `Promise<LoginResult>`
108
+
109
+ ```typescript
110
+ interface LoginResult {
111
+ success: boolean;
112
+ url: string;
113
+ title: string;
114
+ page: Page; // Playwright page instance (for further interactions)
115
+ error?: string; // Error message if failed
116
+ }
117
+ ```
118
+
119
+ ---
120
+
121
+ ### `solveCaptchaFromPage(page, captchaSelector, outputScreenshotPath?)`
122
+
123
+ Extracts and solves a CAPTCHA image displayed on the page using AI.
124
+
125
+ **Parameters:**
126
+
127
+ | Parameter | Type | Required | Default | Description |
128
+ |-----------|------|----------|---------|-------------|
129
+ | `page` | `Page` | ✅ Yes | — | Playwright Page instance |
130
+ | `captchaSelector` | `string` | ❌ No | `img.captcha-image` | CSS selector for the CAPTCHA image |
131
+ | `outputScreenshotPath` | `string` | ❌ No | — | Path to save CAPTCHA screenshot |
132
+
133
+ **Returns:** `Promise<CaptchaResult>`
134
+
135
+ ```typescript
136
+ interface CaptchaResult {
137
+ text: string; // The extracted CAPTCHA text (e.g., "X7K9P")
138
+ description: string; // Brief description of the image content
139
+ }
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Customization
145
+
146
+ ### Change AI Model
147
+
148
+ ```env
149
+ # In your .env file
150
+ LLM_MODEL=google/gemma-4-26b-a4b-it
151
+ ```
152
+
153
+ Supported models (via OpenRouter):
154
+ - `google/gemma-4-26b-a4b-it` (default)
155
+ - `openai/gpt-4o`
156
+ - `anthropic/claude-3.5-sonnet`
157
+ - See [OpenRouter](https://openrouter.ai/models) for more options
158
+
159
+ ### Custom Selectors
160
+
161
+ If your login page uses different element selectors:
162
+
163
+ ```javascript
164
+ await automateLogin({
165
+ url: 'https://example.com/signin',
166
+ loginSelector: '#email-input',
167
+ passwordSelector: '#password-input',
168
+ captchaInputSelector: '#captcha-code',
169
+ submitButtonSelector: '.btn-signin'
170
+ });
171
+ ```
172
+
173
+ ## Architecture
174
+
175
+ ```
176
+ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ ┌──────────┐
177
+ │ Playwright │───▶│ Cloudinary │───▶│ OpenRouter │───▶│ JSON │
178
+ │ Browser │ │ (Upload IMG) │ │ (Gemma LLM) │ │ Response │
179
+ └─────────────┘ └──────────────┘ └─────────────┘ └──────────┘
180
+ │ │
181
+ ▼ ▼
182
+ ┌─────────────┐ ┌──────────────────┐
183
+ │ Fill Form │◀─────────────────────────────────────│ Extract Text │
184
+ │ Submit │ └──────────────────┘
185
+ └─────────────┘
186
+ ```
187
+
188
+ ## Error Handling
189
+
190
+ All functions throw descriptive errors:
191
+
192
+ ```javascript
193
+ try {
194
+ await automateLogin({ url: 'https://example.com/login' });
195
+ } catch (error) {
196
+ if (error.name === 'LlmError') {
197
+ console.error('AI service error:', error.message);
198
+ } else if (error.name === 'CloudinaryError') {
199
+ console.error('Upload failed:', error.message);
200
+ } else {
201
+ console.error('Browser automation error:', error.message);
202
+ }
203
+ }
204
+ ```
205
+
206
+ ## Development
207
+
208
+ ```bash
209
+ # Clone and install dependencies
210
+ git clone <repo-url>
211
+ cd playwright-captcha
212
+ npm install
213
+
214
+ # Build bundles (ESM + CJS)
215
+ npm run build
216
+
217
+ # Run tests
218
+ npm test
219
+
220
+ # TypeScript type checking
221
+ npx tsc --noEmit
222
+ ```
223
+
224
+ ## Project Structure
225
+
226
+ ```
227
+ playwright-captcha/
228
+ ├── src/
229
+ │ ├── index.ts # Public API exports
230
+ │ ├── login.ts # automateLogin implementation
231
+ │ ├── captchaSolve.ts # CAPTCHA extraction & solving
232
+ │ ├── cloudinary.ts # Cloudinary upload config
233
+ │ └── llm.ts # OpenRouter / Gemma integration
234
+ ├── types/
235
+ │ └── index.d.ts # TypeScript declarations
236
+ ├── dist/ # Built packages (ESM + CJS)
237
+ └── test/ # Test suite
238
+ ```
239
+
240
+ ## License
241
+
242
+ ISC
243
+
244
+ ## Contributing
245
+
246
+ Contributions, issues, and feature requests are welcome! Feel free to submit a PR.
247
+
248
+ ---
249
+
250
+ > ⚠️ **Note**: Always use this library responsibly. Ensure you have authorization to automate interactions with any website you're targeting. Respect robots.txt and terms of service.
package/dist/index.cjs ADDED
@@ -0,0 +1,271 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ automateLogin: () => automateLogin,
24
+ default: () => index_default,
25
+ solveCaptchaFromPage: () => solveCaptchaFromPage
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/llm.ts
30
+ var import_config = require("dotenv/config");
31
+ var import_openai = require("@langchain/openai");
32
+ var import_messages = require("@langchain/core/messages");
33
+ var LlmError = class extends Error {
34
+ constructor(message) {
35
+ super(message);
36
+ this.name = "LlmError";
37
+ }
38
+ };
39
+ var model = new import_openai.ChatOpenAI({
40
+ model: process.env.LLM_MODEL || "google/gemma-4-26b-a4b-it",
41
+ temperature: 0,
42
+ apiKey: process.env.OPENROUTER_API_KEY,
43
+ configuration: {
44
+ baseURL: "https://openrouter.ai/api/v1"
45
+ }
46
+ });
47
+ var getImageDetails = async function(image_url) {
48
+ if (!process.env.OPENROUTER_API_KEY) {
49
+ throw new LlmError("OPENROUTER_API_KEY environment variable is required");
50
+ }
51
+ const response = await model.invoke([
52
+ new import_messages.HumanMessage({
53
+ content: [
54
+ {
55
+ type: "text",
56
+ text: `You are a CAPTCHA solving assistant. Analyze the provided image and return the following in strict JSON format:
57
+ - "text": The exact text or characters shown in the CAPTCHA
58
+ - "description": A brief description of what appears in the image
59
+
60
+ If there is no text in the image, set "text" to "no text found".
61
+
62
+ Example response:
63
+ {"text": "X7K9P", "description": "Black text on white background showing alphanumeric characters"}
64
+
65
+ Return ONLY valid JSON with no markdown formatting.`
66
+ },
67
+ {
68
+ type: "image_url",
69
+ image_url: {
70
+ url: image_url
71
+ }
72
+ }
73
+ ]
74
+ })
75
+ ]);
76
+ const content = response.content;
77
+ if (typeof content === "string") {
78
+ const jsonString = content.replace(/```json\s*/i, "").replace(/```\s*$/, "").trim();
79
+ try {
80
+ return JSON.parse(jsonString);
81
+ } catch (parseError) {
82
+ throw new LlmError(`Failed to parse LLM response as JSON: ${parseError}`);
83
+ }
84
+ }
85
+ throw new LlmError("Unexpected LLM response format");
86
+ };
87
+ var llm_default = getImageDetails;
88
+
89
+ // src/cloudinary.ts
90
+ var import_config2 = require("dotenv/config");
91
+ var import_cloudinary = require("cloudinary");
92
+ var CloudinaryError = class extends Error {
93
+ constructor(message) {
94
+ super(message);
95
+ this.name = "CloudinaryError";
96
+ }
97
+ };
98
+ function validateConfig(config2) {
99
+ const missing = [];
100
+ if (!config2.cloud_name) missing.push("CLOUDINARY_CLOUD_NAME");
101
+ if (!config2.api_key) missing.push("CLOUDINARY_API_KEY");
102
+ if (!config2.api_secret) missing.push("CLOUDINARY_API_SECRET");
103
+ if (missing.length > 0) {
104
+ throw new CloudinaryError(`Missing Cloudinary credentials: ${missing.join(", ")}`);
105
+ }
106
+ }
107
+ function getConfig() {
108
+ return {
109
+ cloud_name: process.env.CLOUDINARY_CLOUD_NAME || "",
110
+ api_key: process.env.CLOUDINARY_API_KEY || "",
111
+ api_secret: process.env.CLOUDINARY_API_SECRET || ""
112
+ };
113
+ }
114
+ var config = getConfig();
115
+ validateConfig(config);
116
+ import_cloudinary.v2.config(config);
117
+ var cloudinary_default = import_cloudinary.v2;
118
+
119
+ // src/captchaSolve.ts
120
+ async function solveCaptchaFromPage(page, captchaSelector = "img.captcha-image", outputScreenshotPath) {
121
+ const captchaImage = page.locator(captchaSelector);
122
+ try {
123
+ await captchaImage.waitFor({ state: "visible" });
124
+ } catch (error) {
125
+ throw new Error(`CAPTCHA element not found or not visible. Selector: ${captchaSelector}`);
126
+ }
127
+ if (outputScreenshotPath) {
128
+ await captchaImage.screenshot({ path: outputScreenshotPath });
129
+ }
130
+ let imageData;
131
+ try {
132
+ imageData = await captchaImage.evaluate(async (img) => {
133
+ const response = await fetch(img.src);
134
+ if (!response.ok) {
135
+ throw new Error(`Image fetch failed: ${response.status}`);
136
+ }
137
+ const blob = await response.blob();
138
+ return new Promise((resolve, reject) => {
139
+ const reader = new FileReader();
140
+ reader.onload = () => {
141
+ const result = reader.result;
142
+ const base64 = result.split(",")[1];
143
+ resolve({
144
+ mimeType: blob.type || "image/png",
145
+ base64
146
+ });
147
+ };
148
+ reader.onerror = reject;
149
+ reader.readAsDataURL(blob);
150
+ });
151
+ });
152
+ } catch (error) {
153
+ throw new Error(`Failed to extract CAPTCHA image: ${error}`);
154
+ }
155
+ let uploadResult;
156
+ try {
157
+ uploadResult = await cloudinary_default.uploader.upload(
158
+ `data:${imageData.mimeType};base64,${imageData.base64}`,
159
+ {
160
+ folder: "playwright-captcha-images",
161
+ resource_type: "image",
162
+ type: "auth"
163
+ }
164
+ );
165
+ } catch (error) {
166
+ throw new Error(`Failed to upload CAPTCHA image to Cloudinary: ${error}`);
167
+ }
168
+ const imageUrl = uploadResult.secure_url;
169
+ try {
170
+ const result = await llm_default(imageUrl);
171
+ return result;
172
+ } catch (error) {
173
+ throw new Error(`Failed to solve CAPTCHA using AI: ${error}`);
174
+ }
175
+ }
176
+
177
+ // src/login.ts
178
+ var import_playwright = require("playwright");
179
+ async function validateEnv() {
180
+ const missing = [];
181
+ if (!process.env.EMAIL || process.env.EMAIL === "dev@example.com") {
182
+ missing.push("EMAIL - Set to actual email address");
183
+ }
184
+ if (!process.env.PASSWORD || process.env.PASSWORD === "22222222") {
185
+ missing.push("PASSWORD - Set to actual password");
186
+ }
187
+ if (missing.length > 0) {
188
+ console.warn("Warning: The following environment variables are set to defaults or missing:");
189
+ missing.forEach((m) => console.warn(` - ${m}`));
190
+ console.warn("Please update your .env file with real credentials.");
191
+ }
192
+ }
193
+ async function automateLogin(options) {
194
+ let browser = null;
195
+ let context = null;
196
+ let page = null;
197
+ try {
198
+ await validateEnv();
199
+ browser = await import_playwright.chromium.launch({
200
+ headless: options.headless ?? false
201
+ });
202
+ context = await browser.newContext();
203
+ page = await context.newPage();
204
+ await page.goto(options.url, { waitUntil: "domcontentloaded" });
205
+ const loginSelector = options.loginSelector || "#login";
206
+ const passwordSelector = options.passwordSelector || "#password";
207
+ const captchaSelector = "img.captcha-image";
208
+ const captchaInputSelector = options.captchaInputSelector || "#captcha";
209
+ const submitButtonSelector = options.submitButtonSelector || "button[type='submit']";
210
+ await page.locator(loginSelector).fill(process.env.EMAIL || "");
211
+ await page.locator(passwordSelector).fill(process.env.PASSWORD || "");
212
+ const captchaResult = await solveCaptchaFromPage(
213
+ page,
214
+ captchaSelector,
215
+ options.outputScreenshotPath ? void 0 : void 0
216
+ );
217
+ await page.locator(captchaInputSelector).fill(captchaResult.text);
218
+ await page.locator(submitButtonSelector).click();
219
+ try {
220
+ await page.waitForLoadState("domcontentloaded", { timeout: 15e3 });
221
+ if (options.screenshotDelay) {
222
+ await page.waitForTimeout(options.screenshotDelay);
223
+ }
224
+ if (options.outputScreenshotPath) {
225
+ await page.screenshot({ path: options.outputScreenshotPath, fullPage: true });
226
+ }
227
+ } catch (error) {
228
+ console.warn("Warning: Could not wait for post-login state:", error);
229
+ }
230
+ return {
231
+ success: true,
232
+ url: page.url(),
233
+ title: await page.title(),
234
+ page
235
+ };
236
+ } catch (error) {
237
+ const errorMessage = error instanceof Error ? error.message : String(error);
238
+ let pageTitle = "";
239
+ if (page) {
240
+ try {
241
+ pageTitle = await page.title();
242
+ } catch {
243
+ pageTitle = "failed to get title";
244
+ }
245
+ }
246
+ return {
247
+ success: false,
248
+ url: page?.url() || "",
249
+ title: pageTitle,
250
+ page,
251
+ error: errorMessage
252
+ };
253
+ } finally {
254
+ if (browser && !options.headless) {
255
+ await browser.close().catch(() => {
256
+ });
257
+ }
258
+ }
259
+ }
260
+
261
+ // src/index.ts
262
+ var index_default = {
263
+ solveCaptchaFromPage,
264
+ automateLogin
265
+ };
266
+ // Annotate the CommonJS export names for ESM import in node:
267
+ 0 && (module.exports = {
268
+ automateLogin,
269
+ solveCaptchaFromPage
270
+ });
271
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/llm.ts","../src/cloudinary.ts","../src/captchaSolve.ts","../src/login.ts"],"sourcesContent":["import { solveCaptchaFromPage } from \"./captchaSolve.js\";\nimport { automateLogin, LoginOptions, LoginResult } from \"./login.js\";\n\nexport type { CaptchaResult } from \"./llm.js\";\nexport type { LoginOptions, LoginResult } from \"./login.js\";\n\nexport { solveCaptchaFromPage };\nexport { automateLogin };\n\nexport default {\n solveCaptchaFromPage,\n automateLogin,\n};\n","import \"dotenv/config\";\nimport { ChatOpenAI } from \"@langchain/openai\";\nimport { HumanMessage } from \"@langchain/core/messages\";\n\ninterface CaptchaResult {\n text: string;\n description: string;\n}\n\nclass LlmError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'LlmError';\n }\n}\n\nconst model = new ChatOpenAI({\n model: process.env.LLM_MODEL || \"google/gemma-4-26b-a4b-it\",\n temperature: 0,\n apiKey: process.env.OPENROUTER_API_KEY,\n configuration: {\n baseURL: \"https://openrouter.ai/api/v1\",\n },\n});\n\nconst getImageDetails = async function (image_url: string): Promise<CaptchaResult> {\n if (!process.env.OPENROUTER_API_KEY) {\n throw new LlmError('OPENROUTER_API_KEY environment variable is required');\n }\n\n const response = await model.invoke([\n new HumanMessage({\n content: [\n {\n type: \"text\",\n text: `You are a CAPTCHA solving assistant. Analyze the provided image and return the following in strict JSON format:\n- \"text\": The exact text or characters shown in the CAPTCHA\n- \"description\": A brief description of what appears in the image\n\nIf there is no text in the image, set \"text\" to \"no text found\".\n\nExample response:\n{\"text\": \"X7K9P\", \"description\": \"Black text on white background showing alphanumeric characters\"}\n\nReturn ONLY valid JSON with no markdown formatting.`,\n },\n {\n type: \"image_url\",\n image_url: {\n url: image_url,\n },\n },\n ],\n }),\n ]);\n\n const content = response.content as string | Array<any>;\n \n if (typeof content === 'string') {\n const jsonString = content\n .replace(/```json\\s*/i, '')\n .replace(/```\\s*$/, '')\n .trim();\n \n try {\n return JSON.parse(jsonString);\n } catch (parseError) {\n throw new LlmError(`Failed to parse LLM response as JSON: ${parseError}`);\n }\n }\n \n throw new LlmError('Unexpected LLM response format');\n};\n\nexport default getImageDetails;\nexport type { CaptchaResult };\nexport { LlmError };\n","import 'dotenv/config';\nimport { v2 as cloudinary } from 'cloudinary';\n\ninterface CloudinaryConfig {\n cloud_name: string;\n api_key: string;\n api_secret: string;\n}\n\nclass CloudinaryError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CloudinaryError';\n }\n}\n\nfunction validateConfig(config: CloudinaryConfig): void {\n const missing: string[] = [];\n \n if (!config.cloud_name) missing.push('CLOUDINARY_CLOUD_NAME');\n if (!config.api_key) missing.push('CLOUDINARY_API_KEY');\n if (!config.api_secret) missing.push('CLOUDINARY_API_SECRET');\n \n if (missing.length > 0) {\n throw new CloudinaryError(`Missing Cloudinary credentials: ${missing.join(', ')}`);\n }\n}\n\nexport interface UploadResult {\n secure_url: string;\n public_id: string;\n format: string;\n width: number;\n height: number;\n bytes: number;\n resource_type: string;\n original_filename: string;\n}\n\nexport function getConfig(): CloudinaryConfig {\n return {\n cloud_name: process.env.CLOUDINARY_CLOUD_NAME || '',\n api_key: process.env.CLOUDINARY_API_KEY || '',\n api_secret: process.env.CLOUDINARY_API_SECRET || '',\n };\n}\n\nconst config = getConfig();\nvalidateConfig(config);\n\ncloudinary.config(config);\n\nexport default cloudinary;\nexport { CloudinaryError };\n","import { Page } from \"playwright\";\nimport getImageDetails, { CaptchaResult } from \"./llm.js\";\nimport cloudinary from \"./cloudinary.js\";\n\ninterface ScreenshotError extends Error {\n code: string;\n}\n\nexport async function solveCaptchaFromPage(\n page: Page,\n captchaSelector: string = \"img.captcha-image\",\n outputScreenshotPath?: string\n): Promise<CaptchaResult> {\n const captchaImage = page.locator(captchaSelector);\n\n try {\n await captchaImage.waitFor({ state: \"visible\" });\n } catch (error) {\n throw new Error(`CAPTCHA element not found or not visible. Selector: ${captchaSelector}`);\n }\n\n if (outputScreenshotPath) {\n await captchaImage.screenshot({ path: outputScreenshotPath });\n }\n\n let imageData: { mimeType: string; base64: string };\n\n try {\n imageData = await captchaImage.evaluate(async (img: HTMLImageElement) => {\n const response = await fetch(img.src);\n\n if (!response.ok) {\n throw new Error(`Image fetch failed: ${response.status}`);\n }\n\n const blob = await response.blob();\n\n return new Promise<{ mimeType: string; base64: string }>((resolve, reject) => {\n const reader = new FileReader();\n\n reader.onload = () => {\n const result = reader.result as string;\n const base64 = result.split(\",\")[1];\n resolve({\n mimeType: blob.type || \"image/png\",\n base64,\n });\n };\n\n reader.onerror = reject;\n reader.readAsDataURL(blob);\n });\n });\n } catch (error) {\n throw new Error(`Failed to extract CAPTCHA image: ${error}`);\n }\n\n let uploadResult;\n\n try {\n uploadResult = await cloudinary.uploader.upload(\n `data:${imageData.mimeType};base64,${imageData.base64}`,\n {\n folder: \"playwright-captcha-images\",\n resource_type: \"image\",\n type: \"auth\",\n }\n );\n } catch (error) {\n throw new Error(`Failed to upload CAPTCHA image to Cloudinary: ${error}`);\n }\n\n const imageUrl = uploadResult.secure_url;\n\n try {\n const result = await getImageDetails(imageUrl);\n return result;\n } catch (error) {\n throw new Error(`Failed to solve CAPTCHA using AI: ${error}`);\n }\n}\n","import { chromium, Browser, Page, BrowserContext } from \"playwright\";\nimport { solveCaptchaFromPage } from \"./captchaSolve.js\";\n\nexport interface LoginOptions {\n url: string;\n loginSelector?: string;\n passwordSelector?: string;\n captchaInputSelector?: string;\n submitButtonSelector?: string;\n headless?: boolean;\n outputScreenshotPath?: string;\n screenshotDelay?: number;\n}\n\nexport interface LoginResult {\n success: boolean;\n url: string;\n title: string;\n page: Page;\n error?: string;\n}\n\nclass BrowserError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'BrowserError';\n }\n}\n\nasync function validateEnv(): Promise<void> {\n const missing: string[] = [];\n \n if (!process.env.EMAIL || process.env.EMAIL === 'dev@example.com') {\n missing.push('EMAIL - Set to actual email address');\n }\n if (!process.env.PASSWORD || process.env.PASSWORD === '22222222') {\n missing.push('PASSWORD - Set to actual password');\n }\n \n if (missing.length > 0) {\n console.warn('Warning: The following environment variables are set to defaults or missing:');\n missing.forEach(m => console.warn(` - ${m}`));\n console.warn('Please update your .env file with real credentials.');\n }\n}\n\nexport async function automateLogin(options: LoginOptions): Promise<LoginResult> {\n let browser: Browser | null = null;\n let context: BrowserContext | null = null;\n let page: Page | null = null;\n\n try {\n await validateEnv();\n\n browser = await chromium.launch({\n headless: options.headless ?? false,\n });\n\n context = await browser.newContext();\n page = await context.newPage();\n\n await page.goto(options.url, { waitUntil: \"domcontentloaded\" });\n\n const loginSelector = options.loginSelector || \"#login\";\n const passwordSelector = options.passwordSelector || \"#password\";\n const captchaSelector = \"img.captcha-image\";\n const captchaInputSelector = options.captchaInputSelector || \"#captcha\";\n const submitButtonSelector = options.submitButtonSelector || \"button[type='submit']\";\n\n await page.locator(loginSelector).fill(process.env.EMAIL || \"\");\n await page.locator(passwordSelector).fill(process.env.PASSWORD || \"\");\n\n const captchaResult = await solveCaptchaFromPage(\n page,\n captchaSelector,\n options.outputScreenshotPath ? undefined : undefined\n );\n\n await page.locator(captchaInputSelector).fill(captchaResult.text);\n await page.locator(submitButtonSelector).click();\n\n try {\n await page.waitForLoadState(\"domcontentloaded\", { timeout: 15000 });\n \n if (options.screenshotDelay) {\n await page.waitForTimeout(options.screenshotDelay);\n }\n\n if (options.outputScreenshotPath) {\n await page.screenshot({ path: options.outputScreenshotPath, fullPage: true });\n }\n } catch (error) {\n console.warn('Warning: Could not wait for post-login state:', error);\n }\n\n return {\n success: true,\n url: page.url(),\n title: await page.title(),\n page,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n \n let pageTitle = '';\n if (page) {\n try {\n pageTitle = await page.title();\n } catch {\n pageTitle = 'failed to get title';\n }\n }\n \n return {\n success: false,\n url: page?.url() || '',\n title: pageTitle,\n page: page!,\n error: errorMessage,\n };\n } finally {\n if (browser && !options.headless) {\n await browser.close().catch(() => {});\n }\n }\n}\n"],"mappings":"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","names":["import_config","config","cloudinary"]}
package/dist/index.js ADDED
@@ -0,0 +1,243 @@
1
+ // src/llm.ts
2
+ import "dotenv/config";
3
+ import { ChatOpenAI } from "@langchain/openai";
4
+ import { HumanMessage } from "@langchain/core/messages";
5
+ var LlmError = class extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "LlmError";
9
+ }
10
+ };
11
+ var model = new ChatOpenAI({
12
+ model: process.env.LLM_MODEL || "google/gemma-4-26b-a4b-it",
13
+ temperature: 0,
14
+ apiKey: process.env.OPENROUTER_API_KEY,
15
+ configuration: {
16
+ baseURL: "https://openrouter.ai/api/v1"
17
+ }
18
+ });
19
+ var getImageDetails = async function(image_url) {
20
+ if (!process.env.OPENROUTER_API_KEY) {
21
+ throw new LlmError("OPENROUTER_API_KEY environment variable is required");
22
+ }
23
+ const response = await model.invoke([
24
+ new HumanMessage({
25
+ content: [
26
+ {
27
+ type: "text",
28
+ text: `You are a CAPTCHA solving assistant. Analyze the provided image and return the following in strict JSON format:
29
+ - "text": The exact text or characters shown in the CAPTCHA
30
+ - "description": A brief description of what appears in the image
31
+
32
+ If there is no text in the image, set "text" to "no text found".
33
+
34
+ Example response:
35
+ {"text": "X7K9P", "description": "Black text on white background showing alphanumeric characters"}
36
+
37
+ Return ONLY valid JSON with no markdown formatting.`
38
+ },
39
+ {
40
+ type: "image_url",
41
+ image_url: {
42
+ url: image_url
43
+ }
44
+ }
45
+ ]
46
+ })
47
+ ]);
48
+ const content = response.content;
49
+ if (typeof content === "string") {
50
+ const jsonString = content.replace(/```json\s*/i, "").replace(/```\s*$/, "").trim();
51
+ try {
52
+ return JSON.parse(jsonString);
53
+ } catch (parseError) {
54
+ throw new LlmError(`Failed to parse LLM response as JSON: ${parseError}`);
55
+ }
56
+ }
57
+ throw new LlmError("Unexpected LLM response format");
58
+ };
59
+ var llm_default = getImageDetails;
60
+
61
+ // src/cloudinary.ts
62
+ import "dotenv/config";
63
+ import { v2 as cloudinary } from "cloudinary";
64
+ var CloudinaryError = class extends Error {
65
+ constructor(message) {
66
+ super(message);
67
+ this.name = "CloudinaryError";
68
+ }
69
+ };
70
+ function validateConfig(config2) {
71
+ const missing = [];
72
+ if (!config2.cloud_name) missing.push("CLOUDINARY_CLOUD_NAME");
73
+ if (!config2.api_key) missing.push("CLOUDINARY_API_KEY");
74
+ if (!config2.api_secret) missing.push("CLOUDINARY_API_SECRET");
75
+ if (missing.length > 0) {
76
+ throw new CloudinaryError(`Missing Cloudinary credentials: ${missing.join(", ")}`);
77
+ }
78
+ }
79
+ function getConfig() {
80
+ return {
81
+ cloud_name: process.env.CLOUDINARY_CLOUD_NAME || "",
82
+ api_key: process.env.CLOUDINARY_API_KEY || "",
83
+ api_secret: process.env.CLOUDINARY_API_SECRET || ""
84
+ };
85
+ }
86
+ var config = getConfig();
87
+ validateConfig(config);
88
+ cloudinary.config(config);
89
+ var cloudinary_default = cloudinary;
90
+
91
+ // src/captchaSolve.ts
92
+ async function solveCaptchaFromPage(page, captchaSelector = "img.captcha-image", outputScreenshotPath) {
93
+ const captchaImage = page.locator(captchaSelector);
94
+ try {
95
+ await captchaImage.waitFor({ state: "visible" });
96
+ } catch (error) {
97
+ throw new Error(`CAPTCHA element not found or not visible. Selector: ${captchaSelector}`);
98
+ }
99
+ if (outputScreenshotPath) {
100
+ await captchaImage.screenshot({ path: outputScreenshotPath });
101
+ }
102
+ let imageData;
103
+ try {
104
+ imageData = await captchaImage.evaluate(async (img) => {
105
+ const response = await fetch(img.src);
106
+ if (!response.ok) {
107
+ throw new Error(`Image fetch failed: ${response.status}`);
108
+ }
109
+ const blob = await response.blob();
110
+ return new Promise((resolve, reject) => {
111
+ const reader = new FileReader();
112
+ reader.onload = () => {
113
+ const result = reader.result;
114
+ const base64 = result.split(",")[1];
115
+ resolve({
116
+ mimeType: blob.type || "image/png",
117
+ base64
118
+ });
119
+ };
120
+ reader.onerror = reject;
121
+ reader.readAsDataURL(blob);
122
+ });
123
+ });
124
+ } catch (error) {
125
+ throw new Error(`Failed to extract CAPTCHA image: ${error}`);
126
+ }
127
+ let uploadResult;
128
+ try {
129
+ uploadResult = await cloudinary_default.uploader.upload(
130
+ `data:${imageData.mimeType};base64,${imageData.base64}`,
131
+ {
132
+ folder: "playwright-captcha-images",
133
+ resource_type: "image",
134
+ type: "auth"
135
+ }
136
+ );
137
+ } catch (error) {
138
+ throw new Error(`Failed to upload CAPTCHA image to Cloudinary: ${error}`);
139
+ }
140
+ const imageUrl = uploadResult.secure_url;
141
+ try {
142
+ const result = await llm_default(imageUrl);
143
+ return result;
144
+ } catch (error) {
145
+ throw new Error(`Failed to solve CAPTCHA using AI: ${error}`);
146
+ }
147
+ }
148
+
149
+ // src/login.ts
150
+ import { chromium } from "playwright";
151
+ async function validateEnv() {
152
+ const missing = [];
153
+ if (!process.env.EMAIL || process.env.EMAIL === "dev@example.com") {
154
+ missing.push("EMAIL - Set to actual email address");
155
+ }
156
+ if (!process.env.PASSWORD || process.env.PASSWORD === "22222222") {
157
+ missing.push("PASSWORD - Set to actual password");
158
+ }
159
+ if (missing.length > 0) {
160
+ console.warn("Warning: The following environment variables are set to defaults or missing:");
161
+ missing.forEach((m) => console.warn(` - ${m}`));
162
+ console.warn("Please update your .env file with real credentials.");
163
+ }
164
+ }
165
+ async function automateLogin(options) {
166
+ let browser = null;
167
+ let context = null;
168
+ let page = null;
169
+ try {
170
+ await validateEnv();
171
+ browser = await chromium.launch({
172
+ headless: options.headless ?? false
173
+ });
174
+ context = await browser.newContext();
175
+ page = await context.newPage();
176
+ await page.goto(options.url, { waitUntil: "domcontentloaded" });
177
+ const loginSelector = options.loginSelector || "#login";
178
+ const passwordSelector = options.passwordSelector || "#password";
179
+ const captchaSelector = "img.captcha-image";
180
+ const captchaInputSelector = options.captchaInputSelector || "#captcha";
181
+ const submitButtonSelector = options.submitButtonSelector || "button[type='submit']";
182
+ await page.locator(loginSelector).fill(process.env.EMAIL || "");
183
+ await page.locator(passwordSelector).fill(process.env.PASSWORD || "");
184
+ const captchaResult = await solveCaptchaFromPage(
185
+ page,
186
+ captchaSelector,
187
+ options.outputScreenshotPath ? void 0 : void 0
188
+ );
189
+ await page.locator(captchaInputSelector).fill(captchaResult.text);
190
+ await page.locator(submitButtonSelector).click();
191
+ try {
192
+ await page.waitForLoadState("domcontentloaded", { timeout: 15e3 });
193
+ if (options.screenshotDelay) {
194
+ await page.waitForTimeout(options.screenshotDelay);
195
+ }
196
+ if (options.outputScreenshotPath) {
197
+ await page.screenshot({ path: options.outputScreenshotPath, fullPage: true });
198
+ }
199
+ } catch (error) {
200
+ console.warn("Warning: Could not wait for post-login state:", error);
201
+ }
202
+ return {
203
+ success: true,
204
+ url: page.url(),
205
+ title: await page.title(),
206
+ page
207
+ };
208
+ } catch (error) {
209
+ const errorMessage = error instanceof Error ? error.message : String(error);
210
+ let pageTitle = "";
211
+ if (page) {
212
+ try {
213
+ pageTitle = await page.title();
214
+ } catch {
215
+ pageTitle = "failed to get title";
216
+ }
217
+ }
218
+ return {
219
+ success: false,
220
+ url: page?.url() || "",
221
+ title: pageTitle,
222
+ page,
223
+ error: errorMessage
224
+ };
225
+ } finally {
226
+ if (browser && !options.headless) {
227
+ await browser.close().catch(() => {
228
+ });
229
+ }
230
+ }
231
+ }
232
+
233
+ // src/index.ts
234
+ var index_default = {
235
+ solveCaptchaFromPage,
236
+ automateLogin
237
+ };
238
+ export {
239
+ automateLogin,
240
+ index_default as default,
241
+ solveCaptchaFromPage
242
+ };
243
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/llm.ts","../src/cloudinary.ts","../src/captchaSolve.ts","../src/login.ts","../src/index.ts"],"sourcesContent":["import \"dotenv/config\";\nimport { ChatOpenAI } from \"@langchain/openai\";\nimport { HumanMessage } from \"@langchain/core/messages\";\n\ninterface CaptchaResult {\n text: string;\n description: string;\n}\n\nclass LlmError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'LlmError';\n }\n}\n\nconst model = new ChatOpenAI({\n model: process.env.LLM_MODEL || \"google/gemma-4-26b-a4b-it\",\n temperature: 0,\n apiKey: process.env.OPENROUTER_API_KEY,\n configuration: {\n baseURL: \"https://openrouter.ai/api/v1\",\n },\n});\n\nconst getImageDetails = async function (image_url: string): Promise<CaptchaResult> {\n if (!process.env.OPENROUTER_API_KEY) {\n throw new LlmError('OPENROUTER_API_KEY environment variable is required');\n }\n\n const response = await model.invoke([\n new HumanMessage({\n content: [\n {\n type: \"text\",\n text: `You are a CAPTCHA solving assistant. Analyze the provided image and return the following in strict JSON format:\n- \"text\": The exact text or characters shown in the CAPTCHA\n- \"description\": A brief description of what appears in the image\n\nIf there is no text in the image, set \"text\" to \"no text found\".\n\nExample response:\n{\"text\": \"X7K9P\", \"description\": \"Black text on white background showing alphanumeric characters\"}\n\nReturn ONLY valid JSON with no markdown formatting.`,\n },\n {\n type: \"image_url\",\n image_url: {\n url: image_url,\n },\n },\n ],\n }),\n ]);\n\n const content = response.content as string | Array<any>;\n \n if (typeof content === 'string') {\n const jsonString = content\n .replace(/```json\\s*/i, '')\n .replace(/```\\s*$/, '')\n .trim();\n \n try {\n return JSON.parse(jsonString);\n } catch (parseError) {\n throw new LlmError(`Failed to parse LLM response as JSON: ${parseError}`);\n }\n }\n \n throw new LlmError('Unexpected LLM response format');\n};\n\nexport default getImageDetails;\nexport type { CaptchaResult };\nexport { LlmError };\n","import 'dotenv/config';\nimport { v2 as cloudinary } from 'cloudinary';\n\ninterface CloudinaryConfig {\n cloud_name: string;\n api_key: string;\n api_secret: string;\n}\n\nclass CloudinaryError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CloudinaryError';\n }\n}\n\nfunction validateConfig(config: CloudinaryConfig): void {\n const missing: string[] = [];\n \n if (!config.cloud_name) missing.push('CLOUDINARY_CLOUD_NAME');\n if (!config.api_key) missing.push('CLOUDINARY_API_KEY');\n if (!config.api_secret) missing.push('CLOUDINARY_API_SECRET');\n \n if (missing.length > 0) {\n throw new CloudinaryError(`Missing Cloudinary credentials: ${missing.join(', ')}`);\n }\n}\n\nexport interface UploadResult {\n secure_url: string;\n public_id: string;\n format: string;\n width: number;\n height: number;\n bytes: number;\n resource_type: string;\n original_filename: string;\n}\n\nexport function getConfig(): CloudinaryConfig {\n return {\n cloud_name: process.env.CLOUDINARY_CLOUD_NAME || '',\n api_key: process.env.CLOUDINARY_API_KEY || '',\n api_secret: process.env.CLOUDINARY_API_SECRET || '',\n };\n}\n\nconst config = getConfig();\nvalidateConfig(config);\n\ncloudinary.config(config);\n\nexport default cloudinary;\nexport { CloudinaryError };\n","import { Page } from \"playwright\";\nimport getImageDetails, { CaptchaResult } from \"./llm.js\";\nimport cloudinary from \"./cloudinary.js\";\n\ninterface ScreenshotError extends Error {\n code: string;\n}\n\nexport async function solveCaptchaFromPage(\n page: Page,\n captchaSelector: string = \"img.captcha-image\",\n outputScreenshotPath?: string\n): Promise<CaptchaResult> {\n const captchaImage = page.locator(captchaSelector);\n\n try {\n await captchaImage.waitFor({ state: \"visible\" });\n } catch (error) {\n throw new Error(`CAPTCHA element not found or not visible. Selector: ${captchaSelector}`);\n }\n\n if (outputScreenshotPath) {\n await captchaImage.screenshot({ path: outputScreenshotPath });\n }\n\n let imageData: { mimeType: string; base64: string };\n\n try {\n imageData = await captchaImage.evaluate(async (img: HTMLImageElement) => {\n const response = await fetch(img.src);\n\n if (!response.ok) {\n throw new Error(`Image fetch failed: ${response.status}`);\n }\n\n const blob = await response.blob();\n\n return new Promise<{ mimeType: string; base64: string }>((resolve, reject) => {\n const reader = new FileReader();\n\n reader.onload = () => {\n const result = reader.result as string;\n const base64 = result.split(\",\")[1];\n resolve({\n mimeType: blob.type || \"image/png\",\n base64,\n });\n };\n\n reader.onerror = reject;\n reader.readAsDataURL(blob);\n });\n });\n } catch (error) {\n throw new Error(`Failed to extract CAPTCHA image: ${error}`);\n }\n\n let uploadResult;\n\n try {\n uploadResult = await cloudinary.uploader.upload(\n `data:${imageData.mimeType};base64,${imageData.base64}`,\n {\n folder: \"playwright-captcha-images\",\n resource_type: \"image\",\n type: \"auth\",\n }\n );\n } catch (error) {\n throw new Error(`Failed to upload CAPTCHA image to Cloudinary: ${error}`);\n }\n\n const imageUrl = uploadResult.secure_url;\n\n try {\n const result = await getImageDetails(imageUrl);\n return result;\n } catch (error) {\n throw new Error(`Failed to solve CAPTCHA using AI: ${error}`);\n }\n}\n","import { chromium, Browser, Page, BrowserContext } from \"playwright\";\nimport { solveCaptchaFromPage } from \"./captchaSolve.js\";\n\nexport interface LoginOptions {\n url: string;\n loginSelector?: string;\n passwordSelector?: string;\n captchaInputSelector?: string;\n submitButtonSelector?: string;\n headless?: boolean;\n outputScreenshotPath?: string;\n screenshotDelay?: number;\n}\n\nexport interface LoginResult {\n success: boolean;\n url: string;\n title: string;\n page: Page;\n error?: string;\n}\n\nclass BrowserError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'BrowserError';\n }\n}\n\nasync function validateEnv(): Promise<void> {\n const missing: string[] = [];\n \n if (!process.env.EMAIL || process.env.EMAIL === 'dev@example.com') {\n missing.push('EMAIL - Set to actual email address');\n }\n if (!process.env.PASSWORD || process.env.PASSWORD === '22222222') {\n missing.push('PASSWORD - Set to actual password');\n }\n \n if (missing.length > 0) {\n console.warn('Warning: The following environment variables are set to defaults or missing:');\n missing.forEach(m => console.warn(` - ${m}`));\n console.warn('Please update your .env file with real credentials.');\n }\n}\n\nexport async function automateLogin(options: LoginOptions): Promise<LoginResult> {\n let browser: Browser | null = null;\n let context: BrowserContext | null = null;\n let page: Page | null = null;\n\n try {\n await validateEnv();\n\n browser = await chromium.launch({\n headless: options.headless ?? false,\n });\n\n context = await browser.newContext();\n page = await context.newPage();\n\n await page.goto(options.url, { waitUntil: \"domcontentloaded\" });\n\n const loginSelector = options.loginSelector || \"#login\";\n const passwordSelector = options.passwordSelector || \"#password\";\n const captchaSelector = \"img.captcha-image\";\n const captchaInputSelector = options.captchaInputSelector || \"#captcha\";\n const submitButtonSelector = options.submitButtonSelector || \"button[type='submit']\";\n\n await page.locator(loginSelector).fill(process.env.EMAIL || \"\");\n await page.locator(passwordSelector).fill(process.env.PASSWORD || \"\");\n\n const captchaResult = await solveCaptchaFromPage(\n page,\n captchaSelector,\n options.outputScreenshotPath ? undefined : undefined\n );\n\n await page.locator(captchaInputSelector).fill(captchaResult.text);\n await page.locator(submitButtonSelector).click();\n\n try {\n await page.waitForLoadState(\"domcontentloaded\", { timeout: 15000 });\n \n if (options.screenshotDelay) {\n await page.waitForTimeout(options.screenshotDelay);\n }\n\n if (options.outputScreenshotPath) {\n await page.screenshot({ path: options.outputScreenshotPath, fullPage: true });\n }\n } catch (error) {\n console.warn('Warning: Could not wait for post-login state:', error);\n }\n\n return {\n success: true,\n url: page.url(),\n title: await page.title(),\n page,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n \n let pageTitle = '';\n if (page) {\n try {\n pageTitle = await page.title();\n } catch {\n pageTitle = 'failed to get title';\n }\n }\n \n return {\n success: false,\n url: page?.url() || '',\n title: pageTitle,\n page: page!,\n error: errorMessage,\n };\n } finally {\n if (browser && !options.headless) {\n await browser.close().catch(() => {});\n }\n }\n}\n","import { solveCaptchaFromPage } from \"./captchaSolve.js\";\nimport { automateLogin, LoginOptions, LoginResult } from \"./login.js\";\n\nexport type { CaptchaResult } from \"./llm.js\";\nexport type { LoginOptions, LoginResult } from \"./login.js\";\n\nexport { solveCaptchaFromPage };\nexport { automateLogin };\n\nexport default {\n solveCaptchaFromPage,\n automateLogin,\n};\n"],"mappings":"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","names":["config"]}
package/package.json CHANGED
@@ -1,6 +1,56 @@
1
1
  {
2
2
  "name": "entrox-bypass",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "description": "Bypass bot detection & CAPTCHAs using AI-powered browser automation",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.js",
7
+ "types": "./types/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./types/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist/",
17
+ "types/"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "dev": "tsc --watch",
22
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
23
+ "prepublishOnly": "npm run build"
24
+ },
25
+ "keywords": [
26
+ "playwright",
27
+ "captcha",
28
+ "bypass",
29
+ "automation",
30
+ "browser",
31
+ "ai",
32
+ "bot-detection",
33
+ "cloudinary",
34
+ "llm",
35
+ "screenshot"
36
+ ],
37
+ "author": "",
38
+ "license": "ISC",
39
+ "type": "module",
40
+ "dependencies": {
41
+ "@langchain/core": "^1.2.20",
42
+ "@langchain/openai": "^1.6.3",
43
+ "cloudinary": "^2.11.0",
44
+ "dotenv": "^18.0.8",
45
+ "langchain": "^1.5.19",
46
+ "playwright": "^1.64.1"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^20.0.0",
50
+ "@types/jest": "^29.0.0",
51
+ "jest": "^29.0.0",
52
+ "ts-jest": "^29.0.0",
53
+ "tsup": "^8.0.0",
54
+ "typescript": "^5.0.0"
55
+ }
56
+ }
@@ -0,0 +1,42 @@
1
+ import { Page } from 'playwright';
2
+
3
+ export interface CaptchaResult {
4
+ text: string;
5
+ description: string;
6
+ }
7
+
8
+ export interface LoginOptions {
9
+ url: string;
10
+ loginSelector?: string;
11
+ passwordSelector?: string;
12
+ captchaInputSelector?: string;
13
+ submitButtonSelector?: string;
14
+ headless?: boolean;
15
+ outputScreenshotPath?: string;
16
+ screenshotDelay?: number;
17
+ }
18
+
19
+ export interface LoginResult {
20
+ success: boolean;
21
+ url: string;
22
+ title: string;
23
+ page: Page;
24
+ error?: string;
25
+ }
26
+
27
+ declare function solveCaptchaFromPage(
28
+ page: Page,
29
+ captchaSelector?: string,
30
+ outputScreenshotPath?: string
31
+ ): Promise<CaptchaResult>;
32
+
33
+ declare function automateLogin(options: LoginOptions): Promise<LoginResult>;
34
+
35
+ declare const _default: {
36
+ solveCaptchaFromPage: typeof solveCaptchaFromPage;
37
+ automateLogin: typeof automateLogin;
38
+ };
39
+
40
+ export { solveCaptchaFromPage };
41
+ export { automateLogin };
42
+ export default _default;