@nuanu-ai/agentbrowse 0.1.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.
Files changed (68) hide show
  1. package/README.md +104 -0
  2. package/dist/agentpay-gateway.d.ts +8 -0
  3. package/dist/agentpay-gateway.d.ts.map +1 -0
  4. package/dist/agentpay-gateway.js +58 -0
  5. package/dist/commands/act.d.ts +5 -0
  6. package/dist/commands/act.d.ts.map +1 -0
  7. package/dist/commands/act.js +30 -0
  8. package/dist/commands/captcha-solve.d.ts +6 -0
  9. package/dist/commands/captcha-solve.d.ts.map +1 -0
  10. package/dist/commands/captcha-solve.js +36 -0
  11. package/dist/commands/close.d.ts +5 -0
  12. package/dist/commands/close.d.ts.map +1 -0
  13. package/dist/commands/close.js +32 -0
  14. package/dist/commands/extract.d.ts +5 -0
  15. package/dist/commands/extract.d.ts.map +1 -0
  16. package/dist/commands/extract.js +59 -0
  17. package/dist/commands/launch.d.ts +10 -0
  18. package/dist/commands/launch.d.ts.map +1 -0
  19. package/dist/commands/launch.js +132 -0
  20. package/dist/commands/navigate.d.ts +5 -0
  21. package/dist/commands/navigate.d.ts.map +1 -0
  22. package/dist/commands/navigate.js +26 -0
  23. package/dist/commands/observe.d.ts +5 -0
  24. package/dist/commands/observe.d.ts.map +1 -0
  25. package/dist/commands/observe.js +36 -0
  26. package/dist/commands/screenshot.d.ts +5 -0
  27. package/dist/commands/screenshot.d.ts.map +1 -0
  28. package/dist/commands/screenshot.js +27 -0
  29. package/dist/commands/status.d.ts +5 -0
  30. package/dist/commands/status.d.ts.map +1 -0
  31. package/dist/commands/status.js +47 -0
  32. package/dist/index.d.ts +3 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +247 -0
  35. package/dist/output.d.ts +20 -0
  36. package/dist/output.d.ts.map +1 -0
  37. package/dist/output.js +48 -0
  38. package/dist/session.d.ts +22 -0
  39. package/dist/session.d.ts.map +1 -0
  40. package/dist/session.js +63 -0
  41. package/dist/solver/browser-launcher.d.ts +12 -0
  42. package/dist/solver/browser-launcher.d.ts.map +1 -0
  43. package/dist/solver/browser-launcher.js +265 -0
  44. package/dist/solver/captcha-detector.d.ts +22 -0
  45. package/dist/solver/captcha-detector.d.ts.map +1 -0
  46. package/dist/solver/captcha-detector.js +463 -0
  47. package/dist/solver/captcha-runtime.d.ts +18 -0
  48. package/dist/solver/captcha-runtime.d.ts.map +1 -0
  49. package/dist/solver/captcha-runtime.js +152 -0
  50. package/dist/solver/captcha-solver.d.ts +24 -0
  51. package/dist/solver/captcha-solver.d.ts.map +1 -0
  52. package/dist/solver/captcha-solver.js +88 -0
  53. package/dist/solver/config.d.ts +12 -0
  54. package/dist/solver/config.d.ts.map +1 -0
  55. package/dist/solver/config.js +67 -0
  56. package/dist/solver/fingerprint.d.ts +9 -0
  57. package/dist/solver/fingerprint.d.ts.map +1 -0
  58. package/dist/solver/fingerprint.js +96 -0
  59. package/dist/solver/profile-manager.d.ts +8 -0
  60. package/dist/solver/profile-manager.d.ts.map +1 -0
  61. package/dist/solver/profile-manager.js +74 -0
  62. package/dist/solver/types.d.ts +66 -0
  63. package/dist/solver/types.d.ts.map +1 -0
  64. package/dist/solver/types.js +1 -0
  65. package/dist/stagehand.d.ts +20 -0
  66. package/dist/stagehand.d.ts.map +1 -0
  67. package/dist/stagehand.js +46 -0
  68. package/package.json +66 -0
@@ -0,0 +1,265 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync, mkdirSync } from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import puppeteerExtra from 'puppeteer-extra';
6
+ import StealthPlugin from 'puppeteer-extra-plugin-stealth';
7
+ const enhancedPuppeteer = puppeteerExtra;
8
+ enhancedPuppeteer.use(StealthPlugin());
9
+ const DEFAULT_CDP_PORT = 9222;
10
+ const CHROME_PATHS = [
11
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
12
+ '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
13
+ '/Applications/Chromium.app/Contents/MacOS/Chromium',
14
+ '/usr/bin/google-chrome',
15
+ '/usr/bin/google-chrome-stable',
16
+ '/usr/bin/chromium-browser',
17
+ '/usr/bin/chromium',
18
+ '/snap/bin/chromium',
19
+ ];
20
+ export async function launchSolver(profile, opts) {
21
+ const fp = withWindowSize(profile.fingerprint, opts?.windowSize);
22
+ const cdpPort = opts?.cdpPort ?? DEFAULT_CDP_PORT;
23
+ const executablePath = resolveChromeExecutable(opts?.executablePath);
24
+ const chromePid = spawnChromeProcess(executablePath, profile, fp, cdpPort, opts?.windowSize, opts?.headless, opts?.url);
25
+ try {
26
+ const cdpUrl = await discoverCdpUrl(cdpPort);
27
+ if (!cdpUrl) {
28
+ throw new Error(`Chrome launched but CDP not reachable on port ${cdpPort}`);
29
+ }
30
+ const browser = await enhancedPuppeteer.connect({ browserWSEndpoint: cdpUrl });
31
+ let page;
32
+ if (opts?.url) {
33
+ // Chrome was launched with the URL — reuse the existing tab.
34
+ const existingPages = await browser.pages();
35
+ page = existingPages[0] ?? (await browser.newPage());
36
+ await applyFingerprint(page, fp);
37
+ if (fp.proxy?.username && fp.proxy.password) {
38
+ await page.authenticate({ username: fp.proxy.username, password: fp.proxy.password });
39
+ }
40
+ // If Chrome hasn't finished loading yet, wait for it.
41
+ const currentUrl = page.url();
42
+ if (!currentUrl || currentUrl === 'about:blank' || currentUrl === 'chrome://newtab/') {
43
+ await page
44
+ .waitForNavigation({ waitUntil: 'domcontentloaded', timeout: 15_000 })
45
+ .catch(() => { });
46
+ }
47
+ }
48
+ else {
49
+ page = await createConfiguredPage(browser, fp);
50
+ }
51
+ // Close leftover blank tabs (if any).
52
+ const allPages = await browser.pages();
53
+ for (const p of allPages) {
54
+ if (p !== page) {
55
+ try {
56
+ await p.close();
57
+ }
58
+ catch {
59
+ /* already closed */
60
+ }
61
+ }
62
+ }
63
+ return {
64
+ browser,
65
+ page,
66
+ cdpUrl,
67
+ profile,
68
+ close: async () => {
69
+ try {
70
+ await browser.disconnect();
71
+ }
72
+ finally {
73
+ terminateProcessGroup(chromePid);
74
+ }
75
+ },
76
+ disconnect: async () => {
77
+ await browser.disconnect();
78
+ },
79
+ };
80
+ }
81
+ catch (error) {
82
+ terminateProcessGroup(chromePid);
83
+ throw error;
84
+ }
85
+ }
86
+ async function discoverCdpUrl(port) {
87
+ for (let i = 0; i < 10; i++) {
88
+ try {
89
+ const res = await fetch(`http://localhost:${port}/json/version`);
90
+ if (!res.ok)
91
+ throw new Error(`CDP endpoint returned ${res.status}`);
92
+ const json = (await res.json());
93
+ if (json.webSocketDebuggerUrl) {
94
+ return json.webSocketDebuggerUrl;
95
+ }
96
+ }
97
+ catch {
98
+ // Retry while Chrome is initializing.
99
+ }
100
+ await new Promise((resolve) => setTimeout(resolve, 500));
101
+ }
102
+ return null;
103
+ }
104
+ async function createConfiguredPage(browser, fp) {
105
+ const page = await browser.newPage();
106
+ await applyFingerprint(page, fp);
107
+ if (fp.proxy?.username && fp.proxy.password) {
108
+ await page.authenticate({
109
+ username: fp.proxy.username,
110
+ password: fp.proxy.password,
111
+ });
112
+ }
113
+ return page;
114
+ }
115
+ async function navigateConfiguredPage(browser, page, fp, url) {
116
+ try {
117
+ await page.goto(url, { waitUntil: 'domcontentloaded' });
118
+ return page;
119
+ }
120
+ catch (err) {
121
+ if (!isDetachedFrameError(err)) {
122
+ throw err;
123
+ }
124
+ }
125
+ // Chrome may recycle the initial page during startup; retry on a fresh page once.
126
+ try {
127
+ if (!page.isClosed()) {
128
+ await page.close({ runBeforeUnload: false });
129
+ }
130
+ }
131
+ catch {
132
+ // Ignore close failures for detached/closed pages.
133
+ }
134
+ const retryPage = await createConfiguredPage(browser, fp);
135
+ await retryPage.goto(url, { waitUntil: 'domcontentloaded' });
136
+ return retryPage;
137
+ }
138
+ function isDetachedFrameError(err) {
139
+ const message = err instanceof Error ? err.message : String(err);
140
+ return message.toLowerCase().includes('detached frame');
141
+ }
142
+ function resolveChromeExecutable(explicitPath) {
143
+ if (explicitPath) {
144
+ if (!existsSync(explicitPath)) {
145
+ throw new Error(`Chrome executable does not exist: ${explicitPath}`);
146
+ }
147
+ return explicitPath;
148
+ }
149
+ if (process.env.CHROME_PATH && existsSync(process.env.CHROME_PATH)) {
150
+ return process.env.CHROME_PATH;
151
+ }
152
+ for (const chromePath of CHROME_PATHS) {
153
+ if (existsSync(chromePath))
154
+ return chromePath;
155
+ }
156
+ throw new Error(`Chrome executable not found. Set CHROME_PATH or pass executablePath explicitly. Checked: ${CHROME_PATHS.join(', ')}`);
157
+ }
158
+ function spawnChromeProcess(executablePath, profile, fp, cdpPort, windowSize, headless, url) {
159
+ mkdirSync(profile.userDataDir, { recursive: true });
160
+ const width = windowSize?.width ?? fp.viewport.width;
161
+ const height = windowSize?.height ?? fp.viewport.height;
162
+ const args = [
163
+ `--remote-debugging-port=${cdpPort}`,
164
+ `--user-data-dir=${profile.userDataDir}`,
165
+ `--window-size=${width},${height}`,
166
+ '--disable-blink-features=AutomationControlled',
167
+ '--no-first-run',
168
+ '--no-default-browser-check',
169
+ ];
170
+ if (headless) {
171
+ args.push('--headless=new');
172
+ }
173
+ if (fp.proxy?.server) {
174
+ args.push(`--proxy-server=${fp.proxy.server}`);
175
+ }
176
+ if (url) {
177
+ args.push(url);
178
+ }
179
+ const proc = spawn(executablePath, args, {
180
+ stdio: 'ignore',
181
+ detached: true,
182
+ env: process.env,
183
+ cwd: path.join(os.homedir()),
184
+ });
185
+ proc.unref();
186
+ if (!proc.pid) {
187
+ throw new Error('Failed to launch Chrome process');
188
+ }
189
+ return proc.pid;
190
+ }
191
+ function terminateProcessGroup(pid) {
192
+ try {
193
+ process.kill(-pid, 'SIGTERM');
194
+ return;
195
+ }
196
+ catch {
197
+ // Fall back to killing a single process.
198
+ }
199
+ try {
200
+ process.kill(pid, 'SIGTERM');
201
+ }
202
+ catch {
203
+ // Ignore already terminated process.
204
+ }
205
+ }
206
+ function withWindowSize(fingerprint, windowSize) {
207
+ if (!windowSize)
208
+ return fingerprint;
209
+ const width = Math.max(800, Math.floor(windowSize.width));
210
+ const height = Math.max(600, Math.floor(windowSize.height));
211
+ const viewportHeight = Math.max(500, Math.min(height - 80, height));
212
+ return {
213
+ ...fingerprint,
214
+ viewport: {
215
+ width,
216
+ height: viewportHeight,
217
+ },
218
+ screen: {
219
+ ...fingerprint.screen,
220
+ width,
221
+ height,
222
+ },
223
+ };
224
+ }
225
+ async function applyFingerprint(page, fp) {
226
+ const client = await page.createCDPSession();
227
+ await client.send('Network.setUserAgentOverride', {
228
+ userAgent: fp.userAgent,
229
+ platform: fp.platform,
230
+ acceptLanguage: fp.locale,
231
+ });
232
+ await page.setViewport({
233
+ width: fp.viewport.width,
234
+ height: fp.viewport.height,
235
+ });
236
+ await page.emulateTimezone(fp.timezone);
237
+ await page.evaluateOnNewDocument((fingerprint) => {
238
+ const getParameter = WebGLRenderingContext.prototype.getParameter;
239
+ WebGLRenderingContext.prototype.getParameter = function (parameter) {
240
+ const UNMASKED_VENDOR = 0x9245;
241
+ const UNMASKED_RENDERER = 0x9246;
242
+ if (parameter === UNMASKED_VENDOR)
243
+ return fingerprint.webglVendor;
244
+ if (parameter === UNMASKED_RENDERER)
245
+ return fingerprint.webglRenderer;
246
+ return getParameter.call(this, parameter);
247
+ };
248
+ Object.defineProperty(navigator, 'hardwareConcurrency', {
249
+ get: () => fingerprint.hardwareConcurrency,
250
+ });
251
+ Object.defineProperty(navigator, 'deviceMemory', {
252
+ get: () => fingerprint.deviceMemory,
253
+ });
254
+ Object.defineProperty(screen, 'width', { get: () => fingerprint.screen.width });
255
+ Object.defineProperty(screen, 'height', { get: () => fingerprint.screen.height });
256
+ Object.defineProperty(screen, 'colorDepth', { get: () => fingerprint.screen.colorDepth });
257
+ }, {
258
+ webglVendor: fp.webglVendor,
259
+ webglRenderer: fp.webglRenderer,
260
+ hardwareConcurrency: fp.hardwareConcurrency,
261
+ deviceMemory: fp.deviceMemory,
262
+ screen: fp.screen,
263
+ });
264
+ await client.detach();
265
+ }
@@ -0,0 +1,22 @@
1
+ import type { Page } from 'puppeteer';
2
+ import type { CaptchaSolver } from './captcha-solver.js';
3
+ import type { DetectedCaptcha } from './types.js';
4
+ export type SolveVisibleCaptchasOptions = {
5
+ solveTimeoutMs?: number;
6
+ onProgress?: (message: string) => void;
7
+ skipCaptcha?: (captcha: DetectedCaptcha) => boolean;
8
+ onSolvedCaptcha?: (captcha: DetectedCaptcha) => void;
9
+ onCaptchaResult?: (result: CaptchaSolveOutcome) => void;
10
+ };
11
+ export type CaptchaSolveOutcome = {
12
+ captcha: DetectedCaptcha;
13
+ injected: boolean;
14
+ clicked: boolean;
15
+ verified: boolean;
16
+ error?: string;
17
+ };
18
+ export declare function detectCaptchas(page: Page): Promise<DetectedCaptcha[]>;
19
+ export declare function solveVisibleCaptchas(page: Page, solver: CaptchaSolver): Promise<number>;
20
+ export declare function solveVisibleCaptchasWithOptions(page: Page, solver: CaptchaSolver, opts?: SolveVisibleCaptchasOptions): Promise<CaptchaSolveOutcome[]>;
21
+ export declare function setupCaptchaMonitor(page: Page, solver: CaptchaSolver): void;
22
+ //# sourceMappingURL=captcha-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"captcha-detector.d.ts","sourceRoot":"","sources":["../../src/solver/captcha-detector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,MAAM,2BAA2B,GAAG;IACxC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC;IACpD,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACrD,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;CACzD,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,eAAe,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAKF,wBAAsB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAqC3E;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAG7F;AAED,wBAAsB,+BAA+B,CACnD,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,aAAa,EACrB,IAAI,CAAC,EAAE,2BAA2B,GACjC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAmFhC;AAyUD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CA4B3E"}