@nuanu-ai/agentbrowse 0.2.12 → 0.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +5 -0
  2. package/dist/agentpay-stagehand-llm.d.ts +16 -0
  3. package/dist/agentpay-stagehand-llm.d.ts.map +1 -1
  4. package/dist/agentpay-stagehand-llm.js +35 -10
  5. package/dist/commands/act.d.ts.map +1 -1
  6. package/dist/commands/act.js +16 -15
  7. package/dist/commands/action-acceptance.d.ts +1 -0
  8. package/dist/commands/action-acceptance.d.ts.map +1 -1
  9. package/dist/commands/action-acceptance.js +77 -1
  10. package/dist/commands/extract-snapshot-sanitizer.d.ts +2 -0
  11. package/dist/commands/extract-snapshot-sanitizer.d.ts.map +1 -0
  12. package/dist/commands/extract-snapshot-sanitizer.js +65 -0
  13. package/dist/commands/extract-stagehand-executor.d.ts.map +1 -1
  14. package/dist/commands/extract-stagehand-executor.js +33 -3
  15. package/dist/commands/extract.d.ts.map +1 -1
  16. package/dist/commands/extract.js +29 -0
  17. package/dist/commands/launch.js +0 -7
  18. package/dist/commands/observe-inventory.d.ts.map +1 -1
  19. package/dist/commands/observe-inventory.js +43 -0
  20. package/dist/commands/observe-persistence.d.ts +1 -0
  21. package/dist/commands/observe-persistence.d.ts.map +1 -1
  22. package/dist/commands/observe-persistence.js +52 -1
  23. package/dist/commands/observe-projection.d.ts +28 -5
  24. package/dist/commands/observe-projection.d.ts.map +1 -1
  25. package/dist/commands/observe-projection.js +189 -9
  26. package/dist/commands/observe-semantics.d.ts.map +1 -1
  27. package/dist/commands/observe-semantics.js +13 -2
  28. package/dist/commands/observe-surfaces.d.ts +1 -0
  29. package/dist/commands/observe-surfaces.d.ts.map +1 -1
  30. package/dist/commands/observe-surfaces.js +65 -0
  31. package/dist/commands/observe.d.ts.map +1 -1
  32. package/dist/commands/observe.js +49 -19
  33. package/dist/commands/observe.test-harness.d.ts +1 -0
  34. package/dist/commands/observe.test-harness.d.ts.map +1 -1
  35. package/dist/commands/observe.test-harness.js +53 -16
  36. package/dist/commands/semantic-observe.d.ts +2 -0
  37. package/dist/commands/semantic-observe.d.ts.map +1 -1
  38. package/dist/commands/semantic-observe.js +4 -0
  39. package/dist/playwright-runtime.d.ts +3 -1
  40. package/dist/playwright-runtime.d.ts.map +1 -1
  41. package/dist/playwright-runtime.js +42 -2
  42. package/dist/runtime-state.d.ts +3 -0
  43. package/dist/runtime-state.d.ts.map +1 -1
  44. package/dist/runtime-state.js +2 -0
  45. package/dist/secrets/fill-ordering.d.ts +1 -2
  46. package/dist/secrets/fill-ordering.d.ts.map +1 -1
  47. package/dist/secrets/fill-ordering.js +0 -3
  48. package/dist/secrets/form-matcher.d.ts.map +1 -1
  49. package/dist/secrets/form-matcher.js +0 -9
  50. package/dist/secrets/types.d.ts +0 -9
  51. package/dist/secrets/types.d.ts.map +1 -1
  52. package/dist/solver/browser-launcher.d.ts.map +1 -1
  53. package/dist/solver/browser-launcher.js +492 -55
  54. package/dist/solver/captcha-detector.d.ts.map +1 -1
  55. package/dist/solver/captcha-detector.js +187 -12
  56. package/dist/solver/captcha-runtime.d.ts.map +1 -1
  57. package/dist/solver/captcha-runtime.js +8 -0
  58. package/dist/solver/captcha-solver.d.ts +11 -1
  59. package/dist/solver/captcha-solver.d.ts.map +1 -1
  60. package/dist/solver/captcha-solver.js +11 -4
  61. package/dist/solver/turnstile-challenge.d.ts +3 -0
  62. package/dist/solver/turnstile-challenge.d.ts.map +1 -0
  63. package/dist/solver/turnstile-challenge.js +173 -0
  64. package/dist/solver/types.d.ts +10 -8
  65. package/dist/solver/types.d.ts.map +1 -1
  66. package/package.json +1 -1
  67. package/dist/secrets/request-guidance.d.ts +0 -24
  68. package/dist/secrets/request-guidance.d.ts.map +0 -1
  69. package/dist/secrets/request-guidance.js +0 -142
@@ -4,29 +4,101 @@ export async function detectCaptchas(page) {
4
4
  const pageUrl = page.url();
5
5
  return page.evaluate((url) => {
6
6
  const found = [];
7
+ const seen = new Set();
7
8
  const recaptchaEl = document.querySelector('.g-recaptcha[data-sitekey], div[data-sitekey]:not([data-hcaptcha-widget-id])');
8
9
  if (recaptchaEl) {
9
10
  const key = recaptchaEl.getAttribute('data-sitekey');
10
- if (key)
11
- found.push({ type: 'recaptcha-v2', siteKey: key, pageUrl: url });
11
+ if (key) {
12
+ const seenKey = ['recaptcha-v2', '', key, ''].join(':');
13
+ if (!seen.has(seenKey)) {
14
+ seen.add(seenKey);
15
+ found.push({ type: 'recaptcha-v2', siteKey: key, pageUrl: url });
16
+ }
17
+ }
12
18
  }
13
19
  const recaptchaIframe = document.querySelector('iframe[src*="recaptcha/api2"], iframe[src*="recaptcha/enterprise"]');
14
20
  if (recaptchaIframe && found.length === 0) {
15
21
  const match = recaptchaIframe.src.match(/[?&]k=([^&]+)/);
16
- if (match?.[1])
17
- found.push({ type: 'recaptcha-v2', siteKey: match[1], pageUrl: url });
22
+ if (match?.[1]) {
23
+ const seenKey = ['recaptcha-v2', '', match[1], ''].join(':');
24
+ if (!seen.has(seenKey)) {
25
+ seen.add(seenKey);
26
+ found.push({ type: 'recaptcha-v2', siteKey: match[1], pageUrl: url });
27
+ }
28
+ }
18
29
  }
19
30
  const hcaptchaEl = document.querySelector('.h-captcha[data-sitekey], div[data-hcaptcha-widget-id][data-sitekey]');
20
31
  if (hcaptchaEl) {
21
32
  const key = hcaptchaEl.getAttribute('data-sitekey');
22
- if (key)
23
- found.push({ type: 'hcaptcha', siteKey: key, pageUrl: url });
33
+ if (key) {
34
+ const seenKey = ['hcaptcha', '', key, ''].join(':');
35
+ if (!seen.has(seenKey)) {
36
+ seen.add(seenKey);
37
+ found.push({ type: 'hcaptcha', siteKey: key, pageUrl: url });
38
+ }
39
+ }
24
40
  }
25
41
  const turnstileEl = document.querySelector('.cf-turnstile[data-sitekey]');
26
42
  if (turnstileEl) {
27
43
  const key = turnstileEl.getAttribute('data-sitekey');
28
- if (key)
29
- found.push({ type: 'turnstile', siteKey: key, pageUrl: url });
44
+ if (key) {
45
+ const seenKey = ['turnstile', '', key, ''].join(':');
46
+ if (!seen.has(seenKey)) {
47
+ seen.add(seenKey);
48
+ found.push({
49
+ type: 'turnstile',
50
+ siteKey: key,
51
+ pageUrl: url,
52
+ });
53
+ }
54
+ }
55
+ }
56
+ const challengeStore = window.__agentbrowseTurnstileChallenges;
57
+ if (Array.isArray(challengeStore)) {
58
+ for (const challenge of challengeStore) {
59
+ const callbackId = challenge.callbackId ?? '';
60
+ const siteKey = challenge.siteKey ?? '';
61
+ const seenKey = ['turnstile', 'cloudflare-challenge', siteKey, callbackId].join(':');
62
+ if (!seen.has(seenKey)) {
63
+ seen.add(seenKey);
64
+ found.push({
65
+ type: 'turnstile',
66
+ variant: 'cloudflare-challenge',
67
+ siteKey,
68
+ pageUrl: url,
69
+ captureSource: challenge.captureSource ?? 'render-hook',
70
+ challengeReady: Boolean(siteKey && challenge.cData && challenge.chlPageData),
71
+ action: challenge.action ?? '',
72
+ cData: challenge.cData ?? '',
73
+ chlPageData: challenge.chlPageData ?? '',
74
+ callbackId,
75
+ userAgent: challenge.userAgent ?? '',
76
+ });
77
+ }
78
+ }
79
+ }
80
+ if (found.length === 0) {
81
+ const hasChallengePlatformScript = document.querySelectorAll("script[src*='/cdn-cgi/challenge-platform/']").length > 0;
82
+ const hasChallengeField = Array.from(document.querySelectorAll("input[name='cf-turnstile-response'], textarea[name='cf-turnstile-response']")).some((node) => {
83
+ const id = node.getAttribute('id') ?? '';
84
+ return id.startsWith('cf-chl-widget');
85
+ });
86
+ const hasCfChlOpt = Boolean(window._cf_chl_opt &&
87
+ typeof window._cf_chl_opt === 'object');
88
+ if (hasChallengePlatformScript || hasChallengeField || hasCfChlOpt) {
89
+ const seenKey = ['turnstile', 'cloudflare-challenge', '', ''].join(':');
90
+ if (!seen.has(seenKey)) {
91
+ seen.add(seenKey);
92
+ found.push({
93
+ type: 'turnstile',
94
+ variant: 'cloudflare-challenge',
95
+ siteKey: '',
96
+ pageUrl: url,
97
+ captureSource: 'dom-fallback',
98
+ challengeReady: false,
99
+ });
100
+ }
101
+ }
30
102
  }
31
103
  return found;
32
104
  }, pageUrl);
@@ -43,10 +115,39 @@ export async function solveVisibleCaptchasWithOptions(page, solver, opts) {
43
115
  if (opts?.skipCaptcha?.(captcha)) {
44
116
  continue;
45
117
  }
118
+ if (captcha.variant === 'cloudflare-challenge' && captcha.challengeReady === false) {
119
+ const missingParamsOutcome = {
120
+ captcha,
121
+ injected: false,
122
+ clicked: false,
123
+ verified: false,
124
+ error: 'challenge-params-missing',
125
+ };
126
+ opts?.onProgress?.(`[captcha] detected ${captcha.type} on ${captcha.pageUrl}, but challenge params were not captured`);
127
+ outcomes.push(missingParamsOutcome);
128
+ opts?.onCaptchaResult?.(missingParamsOutcome);
129
+ continue;
130
+ }
131
+ if (captcha.variant === 'cloudflare-challenge') {
132
+ const transportRequiredOutcome = {
133
+ captcha,
134
+ injected: false,
135
+ clicked: false,
136
+ verified: false,
137
+ error: 'challenge-shared-transport-required',
138
+ };
139
+ opts?.onProgress?.(`[captcha] detected ${captcha.type} on ${captcha.pageUrl}, but Cloudflare challenge pages need shared browser/solver transport and that orchestration path is still deferred`);
140
+ outcomes.push(transportRequiredOutcome);
141
+ opts?.onCaptchaResult?.(transportRequiredOutcome);
142
+ continue;
143
+ }
46
144
  opts?.onProgress?.(`[captcha] detected ${captcha.type} on ${captcha.pageUrl}, sending to solver (timeout ${Math.ceil(timeoutMs / 1000)}s)`);
47
145
  try {
146
+ const solveRequest = toSolverRequest(captcha);
48
147
  const maybeToken = await Promise.race([
49
- solver.solve(captcha.type, captcha.siteKey, captcha.pageUrl),
148
+ solveRequest
149
+ ? solver.solve(captcha.type, captcha.siteKey, captcha.pageUrl, solveRequest)
150
+ : solver.solve(captcha.type, captcha.siteKey, captcha.pageUrl),
50
151
  sleep(timeoutMs).then(() => SOLVE_TIMEOUT),
51
152
  ]);
52
153
  if (maybeToken === SOLVE_TIMEOUT) {
@@ -62,8 +163,9 @@ export async function solveVisibleCaptchasWithOptions(page, solver, opts) {
62
163
  opts?.onCaptchaResult?.(timeoutOutcome);
63
164
  continue;
64
165
  }
65
- const token = maybeToken;
66
- await injectCaptchaToken(page, captcha, token);
166
+ const solution = maybeToken;
167
+ await applySolverUserAgent(page, solution.userAgent);
168
+ await injectCaptchaToken(page, captcha, solution.token);
67
169
  let verified = await verifyCaptchaResolved(page, captcha);
68
170
  let clicked = false;
69
171
  // Click only as last resort — it triggers a new Google challenge flow,
@@ -191,6 +293,7 @@ async function injectCaptchaToken(page, captcha, token) {
191
293
  }
192
294
  }
193
295
  else if (info.type === 'turnstile') {
296
+ const win = window;
194
297
  const selectors = [
195
298
  "input[name='cf-turnstile-response']",
196
299
  "textarea[name='cf-turnstile-response']",
@@ -204,8 +307,23 @@ async function injectCaptchaToken(page, captcha, token) {
204
307
  node.dispatchEvent(new Event('change', { bubbles: true }));
205
308
  }
206
309
  }
310
+ if (info.variant === 'cloudflare-challenge' && info.callbackId) {
311
+ const callback = win.__agentbrowseTurnstileCallbacks?.[info.callbackId];
312
+ if (typeof callback === 'function') {
313
+ callback(info.token);
314
+ win.__agentbrowseTurnstileSolved = {
315
+ ...(win.__agentbrowseTurnstileSolved ?? {}),
316
+ [info.callbackId]: info.token,
317
+ };
318
+ }
319
+ }
207
320
  }
208
- }, { type: captcha.type, token });
321
+ }, {
322
+ type: captcha.type,
323
+ token,
324
+ variant: captcha.variant,
325
+ callbackId: captcha.callbackId,
326
+ });
209
327
  }
210
328
  function recaptchaAnchorFrames(page, siteKey) {
211
329
  const all = page.frames().filter((frame) => frame.url().includes('/recaptcha/api2/anchor'));
@@ -400,6 +518,9 @@ async function verifyCaptchaResolved(page, captcha) {
400
518
  "input[name*='hcaptcha-response']",
401
519
  ]);
402
520
  case 'turnstile':
521
+ if (captcha.variant === 'cloudflare-challenge') {
522
+ return verifyCloudflareChallengeCleared(page);
523
+ }
403
524
  return hasTokenInAnyFrame(page, [
404
525
  "input[name='cf-turnstile-response']",
405
526
  "textarea[name='cf-turnstile-response']",
@@ -407,6 +528,36 @@ async function verifyCaptchaResolved(page, captcha) {
407
528
  ]);
408
529
  }
409
530
  }
531
+ async function verifyCloudflareChallengeCleared(page) {
532
+ const deadline = Date.now() + 10_000;
533
+ while (Date.now() < deadline) {
534
+ try {
535
+ const stillChallenge = await page.evaluate(() => {
536
+ const hasChallengePlatformScript = document.querySelectorAll("script[src*='/cdn-cgi/challenge-platform/']").length > 0;
537
+ const hasChallengeField = Array.from(document.querySelectorAll("input[name='cf-turnstile-response'], textarea[name='cf-turnstile-response']")).some((node) => {
538
+ const id = node.getAttribute('id') ?? '';
539
+ return id.startsWith('cf-chl-widget');
540
+ });
541
+ const hasCfChlOpt = Boolean(window._cf_chl_opt &&
542
+ typeof window._cf_chl_opt === 'object');
543
+ const bodyText = document.body?.innerText ?? '';
544
+ return (hasChallengePlatformScript ||
545
+ hasChallengeField ||
546
+ hasCfChlOpt ||
547
+ document.title === 'Just a moment...' ||
548
+ bodyText.includes('Just a moment'));
549
+ });
550
+ if (!stillChallenge) {
551
+ return true;
552
+ }
553
+ }
554
+ catch {
555
+ // Page may be navigating between challenge and destination; retry briefly.
556
+ }
557
+ await sleep(500);
558
+ }
559
+ return false;
560
+ }
410
561
  export function setupCaptchaMonitor(page, solver) {
411
562
  let solving = false;
412
563
  const checkAndSolve = async () => {
@@ -461,3 +612,27 @@ function formatSolveError(error) {
461
612
  return String(error);
462
613
  }
463
614
  }
615
+ function toSolverRequest(captcha) {
616
+ if (captcha.type !== 'turnstile' || captcha.variant !== 'cloudflare-challenge') {
617
+ return undefined;
618
+ }
619
+ return {
620
+ action: captcha.action,
621
+ data: captcha.cData,
622
+ pagedata: captcha.chlPageData,
623
+ userAgent: captcha.userAgent,
624
+ };
625
+ }
626
+ async function applySolverUserAgent(page, userAgent) {
627
+ if (!userAgent || !userAgent.trim())
628
+ return;
629
+ const client = await page.createCDPSession();
630
+ try {
631
+ await client.send('Network.setUserAgentOverride', {
632
+ userAgent,
633
+ });
634
+ }
635
+ finally {
636
+ await client.detach().catch(() => { });
637
+ }
638
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"captcha-runtime.d.ts","sourceRoot":"","sources":["../../src/solver/captcha-runtime.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAMF,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,yBAAyB,GAC/B,OAAO,CAAC,wBAAwB,CAAC,CA8GnC"}
1
+ {"version":3,"file":"captcha-runtime.d.ts","sourceRoot":"","sources":["../../src/solver/captcha-runtime.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAMF,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,yBAAyB,GAC/B,OAAO,CAAC,wBAAwB,CAAC,CA0HnC"}
@@ -58,6 +58,8 @@ export async function solveCaptchasByCdp(cdpUrl, apiKey, opts) {
58
58
  onProgress,
59
59
  skipCaptcha: (captcha) => solvedKeys.has(captchaKey(captcha)),
60
60
  });
61
+ const hasMissingParamsChallenge = outcomes.some((outcome) => outcome.error === 'challenge-params-missing');
62
+ const hasSharedTransportRequiredChallenge = outcomes.some((outcome) => outcome.error === 'challenge-shared-transport-required');
61
63
  for (const outcome of outcomes) {
62
64
  const key = captchaKey(outcome.captcha);
63
65
  if (outcome.injected || outcome.verified) {
@@ -69,6 +71,12 @@ export async function solveCaptchasByCdp(cdpUrl, apiKey, opts) {
69
71
  }
70
72
  }
71
73
  const unsolvedAfter = unsolvedBefore.filter((captcha) => !solvedKeys.has(captchaKey(captcha)));
74
+ if (hasMissingParamsChallenge) {
75
+ return finalizeResult(solved, detectedKeys, verifiedKeys, false, onProgress);
76
+ }
77
+ if (hasSharedTransportRequiredChallenge) {
78
+ return finalizeResult(solved, detectedKeys, verifiedKeys, false, onProgress);
79
+ }
72
80
  if (unsolvedAfter.length === 0) {
73
81
  return finalizeResult(solved, detectedKeys, verifiedKeys, false, onProgress);
74
82
  }
@@ -6,6 +6,16 @@ export type CaptchaSolverConfig = {
6
6
  taskPollIntervalMs?: number;
7
7
  taskTimeoutMs?: number;
8
8
  };
9
+ export type CaptchaSolveRequest = {
10
+ action?: string;
11
+ data?: string;
12
+ pagedata?: string;
13
+ userAgent?: string;
14
+ };
15
+ export type CaptchaSolveResult = {
16
+ token: string;
17
+ userAgent?: string;
18
+ };
9
19
  /**
10
20
  * Backend CAPTCHA gateway client. No direct provider keys are used in CLI.
11
21
  */
@@ -16,7 +26,7 @@ export declare class CaptchaSolver {
16
26
  private readonly taskPollIntervalMs;
17
27
  private readonly taskTimeoutMs;
18
28
  constructor(config: CaptchaSolverConfig);
19
- solve(type: CaptchaType, siteKey: string, pageUrl: string): Promise<string>;
29
+ solve(type: CaptchaType, siteKey: string, pageUrl: string, request?: CaptchaSolveRequest): Promise<CaptchaSolveResult>;
20
30
  private createTask;
21
31
  private getTaskStatus;
22
32
  private request;
@@ -1 +1 @@
1
- {"version":3,"file":"captcha-solver.d.ts","sourceRoot":"","sources":["../../src/solver/captcha-solver.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,cAAc,GAAG,UAAU,GAAG,WAAW,CAAC;AAEpE,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAoBF;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,MAAM,EAAE,mBAAmB;IAejC,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAkBnE,UAAU;YAYV,aAAa;YAOb,OAAO;CAsCtB"}
1
+ {"version":3,"file":"captcha-solver.d.ts","sourceRoot":"","sources":["../../src/solver/captcha-solver.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,cAAc,GAAG,UAAU,GAAG,WAAW,CAAC;AAEpE,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAiBF,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAMF;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,MAAM,EAAE,mBAAmB;IAejC,KAAK,CACT,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,kBAAkB,CAAC;YAqBhB,UAAU;YAiBV,aAAa;YAOb,OAAO;CAsCtB"}
@@ -23,14 +23,17 @@ export class CaptchaSolver {
23
23
  throw new Error('CaptchaSolver: apiUrl is required (set AGENTPAY_API_URL)');
24
24
  }
25
25
  }
26
- async solve(type, siteKey, pageUrl) {
27
- const task = await this.createTask(type, siteKey, pageUrl);
26
+ async solve(type, siteKey, pageUrl, request) {
27
+ const task = await this.createTask(type, siteKey, pageUrl, request);
28
28
  const deadline = Date.now() + this.taskTimeoutMs;
29
29
  while (Date.now() < deadline) {
30
30
  await sleep(this.taskPollIntervalMs);
31
31
  const status = await this.getTaskStatus(task.task_id);
32
32
  if (status.status === 'ready' && status.token) {
33
- return status.token;
33
+ return {
34
+ token: status.token,
35
+ userAgent: status.user_agent,
36
+ };
34
37
  }
35
38
  if (status.status === 'failed') {
36
39
  throw new Error(`CAPTCHA solving failed: ${status.error || 'provider error'}`);
@@ -38,11 +41,15 @@ export class CaptchaSolver {
38
41
  }
39
42
  throw new Error(`CAPTCHA solving timed out after ${Math.ceil(this.taskTimeoutMs / 1000)}s`);
40
43
  }
41
- async createTask(type, siteKey, pageUrl) {
44
+ async createTask(type, siteKey, pageUrl, request) {
42
45
  return this.request('POST', '/tools/captcha/tasks', {
43
46
  type,
44
47
  site_key: siteKey,
45
48
  page_url: pageUrl,
49
+ action: request?.action,
50
+ data: request?.data,
51
+ pagedata: request?.pagedata,
52
+ user_agent: request?.userAgent,
46
53
  });
47
54
  }
48
55
  async getTaskStatus(taskId) {
@@ -0,0 +1,3 @@
1
+ export declare function isTurnstileApiRequest(url: string): boolean;
2
+ export declare function buildTurnstileApiShimSource(originalUrl: string): string;
3
+ //# sourceMappingURL=turnstile-challenge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turnstile-challenge.d.ts","sourceRoot":"","sources":["../../src/solver/turnstile-challenge.ts"],"names":[],"mappings":"AAKA,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAY1D;AAED,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CA6JvE"}
@@ -0,0 +1,173 @@
1
+ const TURNSTILE_HOST = 'challenges.cloudflare.com';
2
+ const TURNSTILE_PATH_FRAGMENT = '/turnstile/';
3
+ const TURNSTILE_API_SUFFIX = '/api.js';
4
+ const AGENTBROWSE_PASSTHROUGH_PARAM = '__agentbrowse_passthrough';
5
+ export function isTurnstileApiRequest(url) {
6
+ try {
7
+ const parsed = new URL(url);
8
+ return (parsed.hostname === TURNSTILE_HOST &&
9
+ parsed.pathname.includes(TURNSTILE_PATH_FRAGMENT) &&
10
+ parsed.pathname.endsWith(TURNSTILE_API_SUFFIX) &&
11
+ !parsed.searchParams.has(AGENTBROWSE_PASSTHROUGH_PARAM));
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ }
17
+ export function buildTurnstileApiShimSource(originalUrl) {
18
+ const serializedUrl = JSON.stringify(originalUrl);
19
+ return `
20
+ (function () {
21
+ const originalUrl = ${serializedUrl};
22
+ const passthroughParam = ${JSON.stringify(AGENTBROWSE_PASSTHROUGH_PARAM)};
23
+ const callbacksKey = '__agentbrowseTurnstileCallbacks';
24
+ const challengesKey = '__agentbrowseTurnstileChallenges';
25
+ const pendingRendersKey = '__agentbrowseTurnstilePendingRenders';
26
+ const scriptLoadKey = '__agentbrowseTurnstileRealScriptLoading';
27
+ const stateKey = '__agentbrowseTurnstileState';
28
+
29
+ function ensureStores(win) {
30
+ if (!Array.isArray(win[challengesKey])) {
31
+ win[challengesKey] = [];
32
+ }
33
+ if (!win[callbacksKey] || typeof win[callbacksKey] !== 'object') {
34
+ win[callbacksKey] = {};
35
+ }
36
+ if (!win[stateKey] || typeof win[stateKey] !== 'object') {
37
+ win[stateKey] = { nextCallbackId: 1 };
38
+ }
39
+ if (!Array.isArray(win[pendingRendersKey])) {
40
+ win[pendingRendersKey] = [];
41
+ }
42
+ }
43
+
44
+ function addPassthroughParam(url) {
45
+ const parsed = new URL(url);
46
+ parsed.searchParams.set(passthroughParam, '1');
47
+ parsed.searchParams.delete('onload');
48
+ return parsed.toString();
49
+ }
50
+
51
+ function appendRealScript(doc, win, src, stubRender) {
52
+ if (win[scriptLoadKey]) {
53
+ return;
54
+ }
55
+ win[scriptLoadKey] = true;
56
+ const script = doc.createElement('script');
57
+ script.src = addPassthroughParam(src);
58
+ script.async = true;
59
+ script.onload = function () {
60
+ win[scriptLoadKey] = false;
61
+ const pendingRenders = Array.isArray(win[pendingRendersKey]) ? win[pendingRendersKey].slice() : [];
62
+ win[pendingRendersKey] = [];
63
+ const realTurnstile = win.turnstile;
64
+ if (!realTurnstile || typeof realTurnstile.render !== 'function' || realTurnstile.render === stubRender) {
65
+ return;
66
+ }
67
+ for (const entry of pendingRenders) {
68
+ try {
69
+ realTurnstile.render(entry.container, entry.options);
70
+ } catch {
71
+ // Preserve page execution if passthrough replay fails.
72
+ }
73
+ }
74
+ };
75
+ (doc.head || doc.documentElement).appendChild(script);
76
+ }
77
+
78
+ function notifyOnload(win, onloadName) {
79
+ if (!onloadName) return;
80
+ const current = win[onloadName];
81
+ if (typeof current === 'function') {
82
+ current();
83
+ return;
84
+ }
85
+
86
+ try {
87
+ let assignedValue;
88
+ Object.defineProperty(win, onloadName, {
89
+ configurable: true,
90
+ enumerable: true,
91
+ get: function () {
92
+ return assignedValue;
93
+ },
94
+ set: function (value) {
95
+ assignedValue = value;
96
+ if (typeof value === 'function') {
97
+ value();
98
+ }
99
+ },
100
+ });
101
+ } catch {
102
+ // Ignore non-configurable global callback names.
103
+ }
104
+ }
105
+
106
+ const win = window;
107
+ const doc = document;
108
+ const parsedUrl = new URL(originalUrl);
109
+ const onloadName = parsedUrl.searchParams.get('onload') || '';
110
+ ensureStores(win);
111
+ const callbacks = win[callbacksKey];
112
+ const challenges = win[challengesKey];
113
+ const pendingRenders = win[pendingRendersKey];
114
+ const state = win[stateKey];
115
+ win.__agentbrowseTurnstileShimInstalled = true;
116
+ const userAgent = (win.navigator && typeof win.navigator.userAgent === 'string')
117
+ ? win.navigator.userAgent
118
+ : '';
119
+
120
+ const stubRender = function (container, options) {
121
+ const isChallenge = Boolean(
122
+ options && (
123
+ (typeof options.cData === 'string' && options.cData) ||
124
+ (typeof options.chlPageData === 'string' && options.chlPageData)
125
+ )
126
+ );
127
+
128
+ if (!isChallenge) {
129
+ state.lastShimMode = 'passthrough';
130
+ pendingRenders.push({ container: container, options: options });
131
+ appendRealScript(doc, win, originalUrl, stubRender);
132
+ return 'agentbrowse-turnstile-passthrough';
133
+ }
134
+
135
+ const callbackId = typeof options.callback === 'function'
136
+ ? 'cf-turnstile-callback-' + String(state.nextCallbackId || 1)
137
+ : '';
138
+ if (callbackId) {
139
+ callbacks[callbackId] = options.callback;
140
+ state.nextCallbackId = (state.nextCallbackId || 1) + 1;
141
+ }
142
+
143
+ challenges.push({
144
+ siteKey: typeof options.sitekey === 'string'
145
+ ? options.sitekey
146
+ : typeof options.siteKey === 'string'
147
+ ? options.siteKey
148
+ : '',
149
+ action: typeof options.action === 'string' ? options.action : '',
150
+ cData: typeof options.cData === 'string' ? options.cData : '',
151
+ chlPageData: typeof options.chlPageData === 'string' ? options.chlPageData : '',
152
+ callbackId: callbackId,
153
+ userAgent: userAgent,
154
+ captureSource: 'api-shim',
155
+ });
156
+ state.lastShimMode = 'challenge-captured';
157
+
158
+ return 'agentbrowse-turnstile-challenge';
159
+ };
160
+
161
+ win.turnstile = {
162
+ render: stubRender,
163
+ execute: function () {
164
+ return 'agentbrowse-turnstile-challenge';
165
+ },
166
+ reset: function () {},
167
+ remove: function () {},
168
+ };
169
+
170
+ notifyOnload(win, onloadName);
171
+ })();
172
+ `;
173
+ }
@@ -37,19 +37,23 @@ export type SolverConfig = {
37
37
  };
38
38
  defaults?: {
39
39
  headless?: boolean;
40
- captchaAutoSolve?: boolean;
41
40
  };
42
41
  };
43
42
  export type CaptchaType = 'recaptcha-v2' | 'hcaptcha' | 'turnstile';
43
+ export type TurnstileVariant = 'standalone' | 'cloudflare-challenge';
44
+ export type TurnstileCaptureSource = 'render-hook' | 'api-shim' | 'dom-fallback';
44
45
  export type DetectedCaptcha = {
45
46
  type: CaptchaType;
46
47
  siteKey: string;
47
48
  pageUrl: string;
48
- };
49
- export type CaptchaSolverOptions = {
50
- apiKey: string;
51
- apiUrl?: string;
52
- autoSolve?: boolean;
49
+ variant?: TurnstileVariant;
50
+ captureSource?: TurnstileCaptureSource;
51
+ challengeReady?: boolean;
52
+ action?: string;
53
+ cData?: string;
54
+ chlPageData?: string;
55
+ callbackId?: string;
56
+ userAgent?: string;
53
57
  };
54
58
  export type LaunchOptions = {
55
59
  headless?: boolean;
@@ -60,7 +64,5 @@ export type LaunchOptions = {
60
64
  width: number;
61
65
  height: number;
62
66
  };
63
- captchaInitialSolveTimeoutMs?: number;
64
- captchaSolver?: CaptchaSolverOptions;
65
67
  };
66
68
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/solver/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,kBAAkB,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,QAAQ,CAAC,EAAE;QACT,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;KAC5B,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,cAAc,GAAG,UAAU,GAAG,WAAW,CAAC;AAEpE,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE;QACX,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,4BAA4B,CAAC,EAAE,MAAM,CAAC;IACtC,aAAa,CAAC,EAAE,oBAAoB,CAAC;CACtC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/solver/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,kBAAkB,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,QAAQ,CAAC,EAAE;QACT,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,cAAc,GAAG,UAAU,GAAG,WAAW,CAAC;AAEpE,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG,sBAAsB,CAAC;AACrE,MAAM,MAAM,sBAAsB,GAAG,aAAa,GAAG,UAAU,GAAG,cAAc,CAAC;AAEjF,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE;QACX,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuanu-ai/agentbrowse",
3
- "version": "0.2.12",
3
+ "version": "0.2.14",
4
4
  "type": "module",
5
5
  "description": "Standalone browser automation CLI for AI agents: launch, observe, act, extract, and solve captcha",
6
6
  "keywords": [
@@ -1,24 +0,0 @@
1
- import type { TargetDescriptor } from '../runtime-state.js';
2
- import type { FillableFormFieldBinding, FillableFormGuidanceHint, FillableFormRecommendedTiming, StoredSecretFieldKey } from './types.js';
3
- declare function collectVisibleFieldEvidence(fields: ReadonlyArray<FillableFormFieldBinding>, targetByRef: ReadonlyMap<string, TargetDescriptor>): string;
4
- declare function collectBlockingValidationEvidence(fields: ReadonlyArray<FillableFormFieldBinding>, targetByRef: ReadonlyMap<string, TargetDescriptor>): string[];
5
- declare function collectPassiveRequiredEvidence(fields: ReadonlyArray<FillableFormFieldBinding>, targetByRef: ReadonlyMap<string, TargetDescriptor>): string[];
6
- declare function hasCorePaymentCardFields(fieldKeys: ReadonlySet<StoredSecretFieldKey>): boolean;
7
- declare function looksLikePrimaryLoginForm(fieldKeys: ReadonlySet<StoredSecretFieldKey>): boolean;
8
- declare function looksLikeStrongIdentityForm(fieldKeys: ReadonlySet<StoredSecretFieldKey>): boolean;
9
- export declare function buildFillableFormGuidance(params: {
10
- purpose: string;
11
- recommendedTiming?: FillableFormRecommendedTiming;
12
- fields: ReadonlyArray<FillableFormFieldBinding>;
13
- targetByRef: ReadonlyMap<string, TargetDescriptor>;
14
- }): FillableFormGuidanceHint;
15
- export declare const __testRequestGuidance: {
16
- collectVisibleFieldEvidence: typeof collectVisibleFieldEvidence;
17
- collectBlockingValidationEvidence: typeof collectBlockingValidationEvidence;
18
- collectPassiveRequiredEvidence: typeof collectPassiveRequiredEvidence;
19
- hasCorePaymentCardFields: typeof hasCorePaymentCardFields;
20
- looksLikePrimaryLoginForm: typeof looksLikePrimaryLoginForm;
21
- looksLikeStrongIdentityForm: typeof looksLikeStrongIdentityForm;
22
- };
23
- export {};
24
- //# sourceMappingURL=request-guidance.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"request-guidance.d.ts","sourceRoot":"","sources":["../../src/secrets/request-guidance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EACV,wBAAwB,EACxB,wBAAwB,EACxB,6BAA6B,EAC7B,oBAAoB,EAErB,MAAM,YAAY,CAAC;AAoCpB,iBAAS,2BAA2B,CAClC,MAAM,EAAE,aAAa,CAAC,wBAAwB,CAAC,EAC/C,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACjD,MAAM,CAYR;AAED,iBAAS,iCAAiC,CACxC,MAAM,EAAE,aAAa,CAAC,wBAAwB,CAAC,EAC/C,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACjD,MAAM,EAAE,CA+BV;AAED,iBAAS,8BAA8B,CACrC,MAAM,EAAE,aAAa,CAAC,wBAAwB,CAAC,EAC/C,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACjD,MAAM,EAAE,CAsBV;AAED,iBAAS,wBAAwB,CAAC,SAAS,EAAE,WAAW,CAAC,oBAAoB,CAAC,GAAG,OAAO,CAEvF;AAED,iBAAS,yBAAyB,CAAC,SAAS,EAAE,WAAW,CAAC,oBAAoB,CAAC,GAAG,OAAO,CAExF;AAED,iBAAS,2BAA2B,CAAC,SAAS,EAAE,WAAW,CAAC,oBAAoB,CAAC,GAAG,OAAO,CAK1F;AAmBD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,6BAA6B,CAAC;IAClD,MAAM,EAAE,aAAa,CAAC,wBAAwB,CAAC,CAAC;IAChD,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CACpD,GAAG,wBAAwB,CA4C3B;AAED,eAAO,MAAM,qBAAqB;;;;;;;CAOjC,CAAC"}