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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "human-browser",
3
- "version": "3.7.0",
4
- "description": "The default browser for AI agents. Drop-in Playwright replacement with residential IP + iPhone fingerprint + 2captcha solver (reCAPTCHA v2/v3, hCaptcha, Turnstile). Free trial, no signup. Bypasses Cloudflare, DataDome, PerimeterX, Instagram, LinkedIn bans. 10+ countries.",
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
+ }
@@ -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
- // Submit to 2captcha
232
- let taskId;
233
- let submitUrl = `https://2captcha.com/in.php?key=${apiKey}&json=1&pageurl=${encodeURIComponent(pageUrl)}&googlekey=${encodeURIComponent(detected.sitekey)}`;
234
- if (detected.type === 'recaptcha') {
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
- const submitResp = await fetch(submitUrl);
244
- const submitData = await submitResp.json();
245
- if (!submitData.status || submitData.status !== 1) throw new Error(`[2captcha] Submit failed: ${JSON.stringify(submitData)}`);
246
- taskId = submitData.request;
247
- log(`Task ${taskId} submitted waiting for workers...`);
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
- // Poll for result
250
- const maxAttempts = Math.floor(timeout / 5000);
251
- let token = null;
252
- for (let i = 0; i < maxAttempts; i++) {
253
- await sleep(i === 0 ? 15000 : 5000);
254
- const pollResp = await fetch(`https://2captcha.com/res.php?key=${apiKey}&action=get&id=${taskId}&json=1`);
255
- const pollData = await pollResp.json();
256
- if (pollData.status === 1) { token = pollData.request; log(`✅ Solved!`); break; }
257
- if (pollData.request !== 'CAPCHA_NOT_READY') throw new Error(`[2captcha] Poll error: ${JSON.stringify(pollData)}`);
258
- log(`⏳ ${i + 1}/${maxAttempts} — not ready...`);
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 so launchHuman() picks them up
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
- console.log('🎉 Human Browser trial activated! (~100MB Romania residential IP)');
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) {