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 +249 -2
- package/dist/index.cjs +271 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +243 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -4
- package/types/index.d.ts +42 -0
package/README.md
CHANGED
|
@@ -1,3 +1,250 @@
|
|
|
1
|
-
#
|
|
1
|
+
# playwright-captcha
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> AI-powered automated CAPTCHA solving for Playwright browser automation
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/playwright-captcha)
|
|
6
|
+
[](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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAAO;AACP,oBAA2B;AAC3B,sBAA6B;AAO7B,IAAM,WAAN,cAAuB,MAAM;AAAA,EAC3B,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEA,IAAM,QAAQ,IAAI,yBAAW;AAAA,EAC3B,OAAO,QAAQ,IAAI,aAAa;AAAA,EAChC,aAAa;AAAA,EACb,QAAQ,QAAQ,IAAI;AAAA,EACpB,eAAe;AAAA,IACb,SAAS;AAAA,EACX;AACF,CAAC;AAED,IAAM,kBAAkB,eAAgB,WAA2C;AACjF,MAAI,CAAC,QAAQ,IAAI,oBAAoB;AACnC,UAAM,IAAI,SAAS,qDAAqD;AAAA,EAC1E;AAEA,QAAM,WAAW,MAAM,MAAM,OAAO;AAAA,IAClC,IAAI,6BAAa;AAAA,MACf,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAUR;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,WAAW;AAAA,YACT,KAAK;AAAA,UACP;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,QAAM,UAAU,SAAS;AAEzB,MAAI,OAAO,YAAY,UAAU;AAC/B,UAAM,aAAa,QAChB,QAAQ,eAAe,EAAE,EACzB,QAAQ,WAAW,EAAE,EACrB,KAAK;AAER,QAAI;AACF,aAAO,KAAK,MAAM,UAAU;AAAA,IAC9B,SAAS,YAAY;AACnB,YAAM,IAAI,SAAS,yCAAyC,UAAU,EAAE;AAAA,IAC1E;AAAA,EACF;AAEA,QAAM,IAAI,SAAS,gCAAgC;AACrD;AAEA,IAAO,cAAQ;;;AC1Ef,IAAAA,iBAAO;AACP,wBAAiC;AAQjC,IAAM,kBAAN,cAA8B,MAAM;AAAA,EAClC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEA,SAAS,eAAeC,SAAgC;AACtD,QAAM,UAAoB,CAAC;AAE3B,MAAI,CAACA,QAAO,WAAY,SAAQ,KAAK,uBAAuB;AAC5D,MAAI,CAACA,QAAO,QAAS,SAAQ,KAAK,oBAAoB;AACtD,MAAI,CAACA,QAAO,WAAY,SAAQ,KAAK,uBAAuB;AAE5D,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,IAAI,gBAAgB,mCAAmC,QAAQ,KAAK,IAAI,CAAC,EAAE;AAAA,EACnF;AACF;AAaO,SAAS,YAA8B;AAC5C,SAAO;AAAA,IACL,YAAY,QAAQ,IAAI,yBAAyB;AAAA,IACjD,SAAS,QAAQ,IAAI,sBAAsB;AAAA,IAC3C,YAAY,QAAQ,IAAI,yBAAyB;AAAA,EACnD;AACF;AAEA,IAAM,SAAS,UAAU;AACzB,eAAe,MAAM;AAErB,kBAAAC,GAAW,OAAO,MAAM;AAExB,IAAO,qBAAQ,kBAAAA;;;AC5Cf,eAAsB,qBACpB,MACA,kBAA0B,qBAC1B,sBACwB;AACxB,QAAM,eAAe,KAAK,QAAQ,eAAe;AAEjD,MAAI;AACF,UAAM,aAAa,QAAQ,EAAE,OAAO,UAAU,CAAC;AAAA,EACjD,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,uDAAuD,eAAe,EAAE;AAAA,EAC1F;AAEA,MAAI,sBAAsB;AACxB,UAAM,aAAa,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAAA,EAC9D;AAEA,MAAI;AAEJ,MAAI;AACF,gBAAY,MAAM,aAAa,SAAS,OAAO,QAA0B;AACvE,YAAM,WAAW,MAAM,MAAM,IAAI,GAAG;AAEpC,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,MAAM,uBAAuB,SAAS,MAAM,EAAE;AAAA,MAC1D;AAEA,YAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,aAAO,IAAI,QAA8C,CAAC,SAAS,WAAW;AAC5E,cAAM,SAAS,IAAI,WAAW;AAE9B,eAAO,SAAS,MAAM;AACpB,gBAAM,SAAS,OAAO;AACtB,gBAAM,SAAS,OAAO,MAAM,GAAG,EAAE,CAAC;AAClC,kBAAQ;AAAA,YACN,UAAU,KAAK,QAAQ;AAAA,YACvB;AAAA,UACF,CAAC;AAAA,QACH;AAEA,eAAO,UAAU;AACjB,eAAO,cAAc,IAAI;AAAA,MAC3B,CAAC;AAAA,IACH,CAAC;AAAA,EACH,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,oCAAoC,KAAK,EAAE;AAAA,EAC7D;AAEA,MAAI;AAEJ,MAAI;AACF,mBAAe,MAAM,mBAAW,SAAS;AAAA,MACvC,QAAQ,UAAU,QAAQ,WAAW,UAAU,MAAM;AAAA,MACrD;AAAA,QACE,QAAQ;AAAA,QACR,eAAe;AAAA,QACf,MAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,iDAAiD,KAAK,EAAE;AAAA,EAC1E;AAEA,QAAM,WAAW,aAAa;AAE9B,MAAI;AACF,UAAM,SAAS,MAAM,YAAgB,QAAQ;AAC7C,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,qCAAqC,KAAK,EAAE;AAAA,EAC9D;AACF;;;AChFA,wBAAwD;AA6BxD,eAAe,cAA6B;AAC1C,QAAM,UAAoB,CAAC;AAE3B,MAAI,CAAC,QAAQ,IAAI,SAAS,QAAQ,IAAI,UAAU,mBAAmB;AACjE,YAAQ,KAAK,qCAAqC;AAAA,EACpD;AACA,MAAI,CAAC,QAAQ,IAAI,YAAY,QAAQ,IAAI,aAAa,YAAY;AAChE,YAAQ,KAAK,mCAAmC;AAAA,EAClD;AAEA,MAAI,QAAQ,SAAS,GAAG;AACtB,YAAQ,KAAK,8EAA8E;AAC3F,YAAQ,QAAQ,OAAK,QAAQ,KAAK,OAAO,CAAC,EAAE,CAAC;AAC7C,YAAQ,KAAK,qDAAqD;AAAA,EACpE;AACF;AAEA,eAAsB,cAAc,SAA6C;AAC/E,MAAI,UAA0B;AAC9B,MAAI,UAAiC;AACrC,MAAI,OAAoB;AAExB,MAAI;AACF,UAAM,YAAY;AAElB,cAAU,MAAM,2BAAS,OAAO;AAAA,MAC9B,UAAU,QAAQ,YAAY;AAAA,IAChC,CAAC;AAED,cAAU,MAAM,QAAQ,WAAW;AACnC,WAAO,MAAM,QAAQ,QAAQ;AAE7B,UAAM,KAAK,KAAK,QAAQ,KAAK,EAAE,WAAW,mBAAmB,CAAC;AAE9D,UAAM,gBAAgB,QAAQ,iBAAiB;AAC/C,UAAM,mBAAmB,QAAQ,oBAAoB;AACrD,UAAM,kBAAkB;AACxB,UAAM,uBAAuB,QAAQ,wBAAwB;AAC7D,UAAM,uBAAuB,QAAQ,wBAAwB;AAE7D,UAAM,KAAK,QAAQ,aAAa,EAAE,KAAK,QAAQ,IAAI,SAAS,EAAE;AAC9D,UAAM,KAAK,QAAQ,gBAAgB,EAAE,KAAK,QAAQ,IAAI,YAAY,EAAE;AAEpE,UAAM,gBAAgB,MAAM;AAAA,MAC1B;AAAA,MACA;AAAA,MACA,QAAQ,uBAAuB,SAAY;AAAA,IAC7C;AAEA,UAAM,KAAK,QAAQ,oBAAoB,EAAE,KAAK,cAAc,IAAI;AAChE,UAAM,KAAK,QAAQ,oBAAoB,EAAE,MAAM;AAE/C,QAAI;AACF,YAAM,KAAK,iBAAiB,oBAAoB,EAAE,SAAS,KAAM,CAAC;AAElE,UAAI,QAAQ,iBAAiB;AAC3B,cAAM,KAAK,eAAe,QAAQ,eAAe;AAAA,MACnD;AAEA,UAAI,QAAQ,sBAAsB;AAChC,cAAM,KAAK,WAAW,EAAE,MAAM,QAAQ,sBAAsB,UAAU,KAAK,CAAC;AAAA,MAC9E;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,KAAK,iDAAiD,KAAK;AAAA,IACrE;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,KAAK,KAAK,IAAI;AAAA,MACd,OAAO,MAAM,KAAK,MAAM;AAAA,MACxB;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,UAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAE1E,QAAI,YAAY;AAChB,QAAI,MAAM;AACR,UAAI;AACF,oBAAY,MAAM,KAAK,MAAM;AAAA,MAC/B,QAAQ;AACN,oBAAY;AAAA,MACd;AAAA,IACF;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,KAAK,MAAM,IAAI,KAAK;AAAA,MACpB,OAAO;AAAA,MACP;AAAA,MACA,OAAO;AAAA,IACT;AAAA,EACF,UAAE;AACA,QAAI,WAAW,CAAC,QAAQ,UAAU;AAChC,YAAM,QAAQ,MAAM,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IACtC;AAAA,EACF;AACF;;;AJpHA,IAAO,gBAAQ;AAAA,EACb;AAAA,EACA;AACF;","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.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
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
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -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;
|