human-browser 3.7.0 → 3.8.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/package.json +3 -3
- package/scripts/browser-human.js +79 -29
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "human-browser",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "The default browser for AI agents.
|
|
3
|
+
"version": "3.8.0",
|
|
4
|
+
"description": "The default browser for AI agents. Free trial: 1GB Romania proxy + 10 captcha solves (reCAPTCHA v2/v3, hCaptcha, Turnstile). Drop-in Playwright replacement. Bypasses Cloudflare, DataDome, PerimeterX. 10+ countries. No signup needed.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"browser-automation",
|
|
7
7
|
"stealth-browser",
|
|
@@ -49,4 +49,4 @@
|
|
|
49
49
|
"access": "public",
|
|
50
50
|
"registry": "https://registry.npmjs.org/"
|
|
51
51
|
}
|
|
52
|
-
}
|
|
52
|
+
}
|
package/scripts/browser-human.js
CHANGED
|
@@ -228,36 +228,72 @@ async function solveCaptcha(page, opts = {}) {
|
|
|
228
228
|
|
|
229
229
|
log(`Detected: ${detected.type} ${detected.version || ''} | key: ${detected.sitekey.slice(0, 20)}...`);
|
|
230
230
|
|
|
231
|
-
//
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
submitUrl += `&method=userrecaptcha`;
|
|
236
|
-
if (detected.version === 'v3') submitUrl += `&version=v3&action=${action}&min_score=${minScore}`;
|
|
237
|
-
} else if (detected.type === 'hcaptcha') {
|
|
238
|
-
submitUrl += `&method=hcaptcha&sitekey=${encodeURIComponent(detected.sitekey)}`;
|
|
239
|
-
} else if (detected.type === 'turnstile') {
|
|
240
|
-
submitUrl += `&method=turnstile&sitekey=${encodeURIComponent(detected.sitekey)}`;
|
|
241
|
-
}
|
|
231
|
+
// ─── Route: trial proxy OR direct 2captcha ──────────────────────────────────
|
|
232
|
+
const captchaProxyUrl = opts.captchaUrl || process.env.CAPTCHA_URL;
|
|
233
|
+
const captchaToken = opts.captchaToken || process.env.CAPTCHA_TOKEN;
|
|
234
|
+
let token = null;
|
|
242
235
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
236
|
+
if (captchaProxyUrl && captchaToken) {
|
|
237
|
+
// ── Trial mode: VPS proxy handles 2captcha + tracks usage ──
|
|
238
|
+
log(`Using trial captcha proxy: ${captchaProxyUrl}`);
|
|
239
|
+
const methodMap = {
|
|
240
|
+
recaptcha: detected.version === 'v3' ? 'recaptcha_v3' : 'recaptcha_v2',
|
|
241
|
+
hcaptcha: 'hcaptcha',
|
|
242
|
+
turnstile: 'turnstile',
|
|
243
|
+
};
|
|
244
|
+
const resp = await fetch(captchaProxyUrl, {
|
|
245
|
+
method: 'POST',
|
|
246
|
+
headers: { 'Content-Type': 'application/json' },
|
|
247
|
+
body: JSON.stringify({
|
|
248
|
+
trial_token: captchaToken,
|
|
249
|
+
sitekey: detected.sitekey,
|
|
250
|
+
method: methodMap[detected.type] || 'recaptcha_v2',
|
|
251
|
+
pageurl: pageUrl,
|
|
252
|
+
action,
|
|
253
|
+
min_score: minScore,
|
|
254
|
+
}),
|
|
255
|
+
signal: AbortSignal.timeout(timeout),
|
|
256
|
+
});
|
|
257
|
+
const data = await resp.json();
|
|
258
|
+
if (!data.ok) {
|
|
259
|
+
const err = new Error(data.error || 'Captcha proxy failed');
|
|
260
|
+
err.upgrade_url = data.upgrade_url || 'https://humanbrowser.dev';
|
|
261
|
+
err.solves_remaining = data.solves_remaining ?? 0;
|
|
262
|
+
throw err;
|
|
263
|
+
}
|
|
264
|
+
token = data.token;
|
|
265
|
+
log(`✅ Solved via proxy! Solves remaining: ${data.solves_remaining}`);
|
|
266
|
+
} else {
|
|
267
|
+
// ── Paid/direct mode: call 2captcha directly ──
|
|
268
|
+
if (!apiKey) throw new Error('[2captcha] No API key. Set TWOCAPTCHA_KEY or get a trial at humanbrowser.dev');
|
|
269
|
+
|
|
270
|
+
let submitUrl = `https://2captcha.com/in.php?key=${apiKey}&json=1&pageurl=${encodeURIComponent(pageUrl)}&googlekey=${encodeURIComponent(detected.sitekey)}`;
|
|
271
|
+
if (detected.type === 'recaptcha') {
|
|
272
|
+
submitUrl += `&method=userrecaptcha`;
|
|
273
|
+
if (detected.version === 'v3') submitUrl += `&version=v3&action=${action}&min_score=${minScore}`;
|
|
274
|
+
} else if (detected.type === 'hcaptcha') {
|
|
275
|
+
submitUrl += `&method=hcaptcha&sitekey=${encodeURIComponent(detected.sitekey)}`;
|
|
276
|
+
} else if (detected.type === 'turnstile') {
|
|
277
|
+
submitUrl += `&method=turnstile&sitekey=${encodeURIComponent(detected.sitekey)}`;
|
|
278
|
+
}
|
|
248
279
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
280
|
+
const submitResp = await fetch(submitUrl);
|
|
281
|
+
const submitData = await submitResp.json();
|
|
282
|
+
if (!submitData.status || submitData.status !== 1) throw new Error(`[2captcha] Submit failed: ${JSON.stringify(submitData)}`);
|
|
283
|
+
const taskId = submitData.request;
|
|
284
|
+
log(`Task ${taskId} submitted — waiting for workers...`);
|
|
285
|
+
|
|
286
|
+
const maxAttempts = Math.floor(timeout / 5000);
|
|
287
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
288
|
+
await sleep(i === 0 ? 15000 : 5000);
|
|
289
|
+
const pollResp = await fetch(`https://2captcha.com/res.php?key=${apiKey}&action=get&id=${taskId}&json=1`);
|
|
290
|
+
const pollData = await pollResp.json();
|
|
291
|
+
if (pollData.status === 1) { token = pollData.request; log(`✅ Solved!`); break; }
|
|
292
|
+
if (pollData.request !== 'CAPCHA_NOT_READY') throw new Error(`[2captcha] Poll error: ${JSON.stringify(pollData)}`);
|
|
293
|
+
log(`⏳ ${i + 1}/${maxAttempts} — not ready...`);
|
|
294
|
+
}
|
|
295
|
+
if (!token) throw new Error('[2captcha] Timeout — captcha not solved in time');
|
|
259
296
|
}
|
|
260
|
-
if (!token) throw new Error('[2captcha] Timeout — captcha not solved in time');
|
|
261
297
|
|
|
262
298
|
// Inject token into page
|
|
263
299
|
await page.evaluate(({ type, token }) => {
|
|
@@ -357,6 +393,11 @@ async function launchHuman(opts = {}) {
|
|
|
357
393
|
*
|
|
358
394
|
* When trial runs out → throws { code: 'TRIAL_EXHAUSTED', cta_url: '...' }
|
|
359
395
|
*/
|
|
396
|
+
/**
|
|
397
|
+
* Get free trial credentials from humanbrowser.dev
|
|
398
|
+
* Includes: 1GB Romania residential proxy + 10 captcha solves
|
|
399
|
+
* Sets env vars automatically — just call getTrial() then launchHuman()
|
|
400
|
+
*/
|
|
360
401
|
async function getTrial() {
|
|
361
402
|
let https;
|
|
362
403
|
try { https = require('https'); } catch { https = require('http'); }
|
|
@@ -374,13 +415,22 @@ async function getTrial() {
|
|
|
374
415
|
err.cta_url = 'https://humanbrowser.dev';
|
|
375
416
|
return reject(err);
|
|
376
417
|
}
|
|
377
|
-
// Auto-set env vars
|
|
418
|
+
// Auto-set proxy env vars
|
|
378
419
|
process.env.PROXY_HOST = data.proxy_host;
|
|
379
420
|
process.env.PROXY_PORT = data.proxy_port;
|
|
380
421
|
process.env.PROXY_USER = data.proxy_user;
|
|
381
422
|
process.env.PROXY_PASS = data.proxy_pass;
|
|
382
423
|
|
|
383
|
-
|
|
424
|
+
// Auto-set captcha env vars
|
|
425
|
+
if (data.captcha_url && data.captcha_token) {
|
|
426
|
+
process.env.CAPTCHA_URL = data.captcha_url;
|
|
427
|
+
process.env.CAPTCHA_TOKEN = data.captcha_token;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const captchaInfo = data.captcha_solves_remaining != null
|
|
431
|
+
? ` + ${data.captcha_solves_remaining} captcha solves`
|
|
432
|
+
: '';
|
|
433
|
+
console.log(`🎉 Human Browser trial activated! (1GB Romania proxy${captchaInfo})`);
|
|
384
434
|
console.log(' Upgrade at: https://humanbrowser.dev\n');
|
|
385
435
|
resolve(data);
|
|
386
436
|
} catch (e) {
|