camel-ai 0.2.72a10__py3-none-any.whl → 0.2.73a0__py3-none-any.whl

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.

Potentially problematic release.


This version of camel-ai might be problematic. Click here for more details.

Files changed (36) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +113 -338
  3. camel/memories/agent_memories.py +18 -17
  4. camel/societies/workforce/prompts.py +10 -4
  5. camel/societies/workforce/single_agent_worker.py +7 -5
  6. camel/toolkits/__init__.py +4 -1
  7. camel/toolkits/base.py +57 -1
  8. camel/toolkits/hybrid_browser_toolkit/config_loader.py +136 -413
  9. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +796 -1631
  10. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +4356 -0
  11. camel/toolkits/hybrid_browser_toolkit/ts/package.json +33 -0
  12. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
  13. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +916 -0
  14. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +226 -0
  15. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +522 -0
  16. camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
  17. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +110 -0
  18. camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +26 -0
  19. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +210 -0
  20. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +533 -0
  21. camel/toolkits/message_integration.py +592 -0
  22. camel/toolkits/screenshot_toolkit.py +116 -31
  23. camel/toolkits/search_toolkit.py +20 -2
  24. camel/toolkits/terminal_toolkit.py +16 -2
  25. camel/toolkits/video_analysis_toolkit.py +13 -13
  26. camel/toolkits/video_download_toolkit.py +11 -11
  27. {camel_ai-0.2.72a10.dist-info → camel_ai-0.2.73a0.dist-info}/METADATA +10 -4
  28. {camel_ai-0.2.72a10.dist-info → camel_ai-0.2.73a0.dist-info}/RECORD +30 -24
  29. camel/toolkits/hybrid_browser_toolkit/actions.py +0 -417
  30. camel/toolkits/hybrid_browser_toolkit/agent.py +0 -311
  31. camel/toolkits/hybrid_browser_toolkit/browser_session.py +0 -740
  32. camel/toolkits/hybrid_browser_toolkit/snapshot.py +0 -227
  33. camel/toolkits/hybrid_browser_toolkit/stealth_script.js +0 -0
  34. camel/toolkits/hybrid_browser_toolkit/unified_analyzer.js +0 -1002
  35. {camel_ai-0.2.72a10.dist-info → camel_ai-0.2.73a0.dist-info}/WHEEL +0 -0
  36. {camel_ai-0.2.72a10.dist-info → camel_ai-0.2.73a0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,522 @@
1
+ import {HybridBrowserSession} from './browser-session';
2
+ import {ActionResult, BrowserAction, BrowserToolkitConfig, SnapshotResult, TabInfo, VisualMarkResult} from './types';
3
+ import {ConfigLoader} from './config-loader';
4
+
5
+ export class HybridBrowserToolkit {
6
+ private session: HybridBrowserSession;
7
+ private config: BrowserToolkitConfig;
8
+ private configLoader: ConfigLoader;
9
+ private viewportLimit: boolean;
10
+
11
+ constructor(config: BrowserToolkitConfig = {}) {
12
+ this.configLoader = ConfigLoader.fromPythonConfig(config);
13
+ this.config = config; // Store original config for backward compatibility
14
+ this.session = new HybridBrowserSession(this.configLoader.getBrowserConfig()); // Pass processed config
15
+ this.viewportLimit = this.configLoader.getWebSocketConfig().viewport_limit;
16
+ }
17
+
18
+ async openBrowser(startUrl?: string): Promise<ActionResult> {
19
+ const startTime = Date.now();
20
+
21
+ try {
22
+ await this.session.ensureBrowser();
23
+
24
+ const url = startUrl || this.config.defaultStartUrl || 'https://google.com/';
25
+ const result = await this.session.visitPage(url);
26
+
27
+ const snapshotStart = Date.now();
28
+ const snapshot = await this.getPageSnapshot(this.viewportLimit);
29
+ const snapshotTime = Date.now() - snapshotStart;
30
+
31
+ const totalTime = Date.now() - startTime;
32
+
33
+ return {
34
+ success: true,
35
+ message: `Browser opened and navigated to ${url}`,
36
+ snapshot,
37
+ timing: {
38
+ total_time_ms: totalTime,
39
+ ...result.timing,
40
+ snapshot_time_ms: snapshotTime,
41
+ },
42
+ };
43
+ } catch (error) {
44
+ const totalTime = Date.now() - startTime;
45
+ return {
46
+ success: false,
47
+ message: `Failed to open browser: ${error}`,
48
+ timing: {
49
+ total_time_ms: totalTime,
50
+ },
51
+ };
52
+ }
53
+ }
54
+
55
+ async closeBrowser(): Promise<ActionResult> {
56
+ try {
57
+ await this.session.close();
58
+ return {
59
+ success: true,
60
+ message: 'Browser closed successfully',
61
+ };
62
+ } catch (error) {
63
+ return {
64
+ success: false,
65
+ message: `Failed to close browser: ${error}`,
66
+ };
67
+ }
68
+ }
69
+
70
+ async visitPage(url: string): Promise<any> {
71
+ const result = await this.session.visitPage(url);
72
+
73
+ // Format response for Python layer compatibility
74
+ const response: any = {
75
+ result: result.message,
76
+ snapshot: '',
77
+ };
78
+
79
+ if (result.success) {
80
+ const snapshotStart = Date.now();
81
+ response.snapshot = await this.getPageSnapshot(this.viewportLimit);
82
+ const snapshotTime = Date.now() - snapshotStart;
83
+
84
+ if (result.timing) {
85
+ result.timing.snapshot_time_ms = snapshotTime;
86
+ }
87
+ }
88
+
89
+ // Include timing if available
90
+ if (result.timing) {
91
+ response.timing = result.timing;
92
+ }
93
+
94
+ // Include newTabId if present
95
+ if (result.newTabId) {
96
+ response.newTabId = result.newTabId;
97
+ }
98
+
99
+ return response;
100
+ }
101
+
102
+ async getPageSnapshot(viewportLimit: boolean = false): Promise<string> {
103
+ try {
104
+ // If viewport limiting is enabled, we need coordinates for filtering
105
+ const snapshotResult = await this.session.getSnapshotForAI(viewportLimit, viewportLimit);
106
+ return snapshotResult.snapshot;
107
+ } catch (error) {
108
+ return `Error capturing snapshot: ${error}`;
109
+ }
110
+ }
111
+
112
+
113
+ async getSnapshotForAI(): Promise<SnapshotResult> {
114
+ return await this.session.getSnapshotForAI();
115
+ }
116
+
117
+ async getSomScreenshot(): Promise<VisualMarkResult & { timing: any }> {
118
+ const startTime = Date.now();
119
+
120
+ try {
121
+ const screenshotResult = await this.session.takeScreenshot();
122
+ const snapshotResult = await this.session.getSnapshotForAI(true); // Include coordinates for SOM_mark
123
+
124
+ // Add visual marks using improved method
125
+ const markingStart = Date.now();
126
+ const markedImageBuffer = await this.addVisualMarksOptimized(screenshotResult.buffer, snapshotResult);
127
+ const markingTime = Date.now() - markingStart;
128
+
129
+ const base64Image = markedImageBuffer.toString('base64');
130
+ const dataUrl = `data:image/png;base64,${base64Image}`;
131
+
132
+ const totalTime = Date.now() - startTime;
133
+
134
+ // Count elements with coordinates
135
+ const elementsWithCoords = Object.values(snapshotResult.elements).filter(el => el.coordinates).length;
136
+
137
+ return {
138
+ text: `Visual webpage screenshot captured with ${Object.keys(snapshotResult.elements).length} interactive elements (${elementsWithCoords} marked visually)`,
139
+ images: [dataUrl],
140
+ timing: {
141
+ total_time_ms: totalTime,
142
+ screenshot_time_ms: screenshotResult.timing.screenshot_time_ms,
143
+ snapshot_time_ms: snapshotResult.timing.snapshot_time_ms,
144
+ coordinate_enrichment_time_ms: snapshotResult.timing.coordinate_enrichment_time_ms,
145
+ visual_marking_time_ms: markingTime,
146
+ },
147
+ };
148
+ } catch (error) {
149
+ const totalTime = Date.now() - startTime;
150
+ return {
151
+ text: `Error capturing screenshot: ${error}`,
152
+ images: [],
153
+ timing: {
154
+ total_time_ms: totalTime,
155
+ screenshot_time_ms: 0,
156
+ snapshot_time_ms: 0,
157
+ coordinate_enrichment_time_ms: 0,
158
+ visual_marking_time_ms: 0,
159
+ },
160
+ };
161
+ }
162
+ }
163
+
164
+ private async addVisualMarksOptimized(screenshotBuffer: Buffer, snapshotResult: SnapshotResult): Promise<Buffer> {
165
+ try {
166
+
167
+ // Check if we have any elements with coordinates
168
+ const elementsWithCoords = Object.entries(snapshotResult.elements)
169
+ .filter(([ref, element]) => element.coordinates);
170
+
171
+ if (elementsWithCoords.length === 0) {
172
+ return screenshotBuffer;
173
+ }
174
+
175
+ // Parse clickable elements from snapshot text
176
+ const clickableElements = this.parseClickableElements(snapshotResult.snapshot);
177
+
178
+ // Use sharp for image processing
179
+ const sharp = require('sharp');
180
+ const page = await this.session.getCurrentPage();
181
+ const viewport = page.viewportSize() || { width: 1280, height: 720 };
182
+
183
+ // Filter elements visible in viewport
184
+ const visibleElements = elementsWithCoords.filter(([ref, element]) => {
185
+ const coords = element.coordinates!;
186
+ return coords.x < viewport.width &&
187
+ coords.y < viewport.height &&
188
+ coords.x + coords.width > 0 &&
189
+ coords.y + coords.height > 0;
190
+ });
191
+
192
+ // Remove overlapped elements (only keep topmost)
193
+ const nonOverlappedElements = this.removeOverlappedElements(visibleElements);
194
+
195
+ // Create SVG overlay with all the marks
196
+ const marks = nonOverlappedElements.map(([ref, element]) => {
197
+ const coords = element.coordinates!;
198
+ const isClickable = clickableElements.has(ref);
199
+
200
+ // Use original coordinates for elements within viewport
201
+ // Clamp only to prevent marks from extending beyond screenshot bounds
202
+ const x = Math.max(0, coords.x);
203
+ const y = Math.max(0, coords.y);
204
+ const maxWidth = viewport.width - x;
205
+ const maxHeight = viewport.height - y;
206
+ const width = Math.min(coords.width, maxWidth);
207
+ const height = Math.min(coords.height, maxHeight);
208
+
209
+ // Position text to be visible even if element is partially cut off
210
+ const textX = Math.max(2, Math.min(x + 2, viewport.width - 40));
211
+ const textY = Math.max(14, Math.min(y + 14, viewport.height - 4));
212
+
213
+ // Different colors for clickable vs non-clickable elements
214
+ const colors = isClickable ? {
215
+ fill: 'rgba(0, 150, 255, 0.15)', // Blue for clickable
216
+ stroke: '#0096FF',
217
+ textFill: '#0096FF'
218
+ } : {
219
+ fill: 'rgba(255, 107, 107, 0.1)', // Red for non-clickable
220
+ stroke: '#FF6B6B',
221
+ textFill: '#FF6B6B'
222
+ };
223
+
224
+ return `
225
+ <rect x="${x}" y="${y}" width="${width}" height="${height}"
226
+ fill="${colors.fill}" stroke="${colors.stroke}" stroke-width="2" rx="2"/>
227
+ <text x="${textX}" y="${textY}" font-family="Arial, sans-serif"
228
+ font-size="12" fill="${colors.textFill}" font-weight="bold">${ref}</text>
229
+ `;
230
+ }).join('');
231
+
232
+ const svgOverlay = `
233
+ <svg width="${viewport.width}" height="${viewport.height}" xmlns="http://www.w3.org/2000/svg">
234
+ ${marks}
235
+ </svg>
236
+ `;
237
+
238
+ // Composite the overlay onto the screenshot
239
+ const markedImageBuffer = await sharp(screenshotBuffer)
240
+ .composite([{
241
+ input: Buffer.from(svgOverlay),
242
+ top: 0,
243
+ left: 0
244
+ }])
245
+ .png()
246
+ .toBuffer();
247
+
248
+ return markedImageBuffer;
249
+
250
+ } catch (error) {
251
+ // Error adding visual marks, falling back to original screenshot
252
+ // Return original screenshot if marking fails
253
+ return screenshotBuffer;
254
+ }
255
+ }
256
+
257
+ /**
258
+ * Parse clickable elements from snapshot text
259
+ */
260
+ private parseClickableElements(snapshotText: string): Set<string> {
261
+ const clickableElements = new Set<string>();
262
+ const lines = snapshotText.split('\n');
263
+
264
+ for (const line of lines) {
265
+ // Look for lines containing [cursor=pointer] and extract ref
266
+ if (line.includes('[cursor=pointer]')) {
267
+ const refMatch = line.match(/\[ref=([^\]]+)\]/);
268
+ if (refMatch) {
269
+ clickableElements.add(refMatch[1]);
270
+ }
271
+ }
272
+ }
273
+
274
+ return clickableElements;
275
+ }
276
+
277
+ /**
278
+ * Remove overlapped elements, keeping only the topmost (last in DOM order)
279
+ */
280
+ private removeOverlappedElements(elements: Array<[string, any]>): Array<[string, any]> {
281
+ const result: Array<[string, any]> = [];
282
+
283
+ for (let i = 0; i < elements.length; i++) {
284
+ const [refA, elementA] = elements[i];
285
+ const coordsA = elementA.coordinates!;
286
+ let isOverlapped = false;
287
+
288
+ // Check if this element is completely overlapped by any later element
289
+ for (let j = i + 1; j < elements.length; j++) {
290
+ const [refB, elementB] = elements[j];
291
+ const coordsB = elementB.coordinates!;
292
+
293
+ // Check if element A is completely covered by element B
294
+ if (this.isCompletelyOverlapped(coordsA, coordsB)) {
295
+ isOverlapped = true;
296
+ break;
297
+ }
298
+ }
299
+
300
+ if (!isOverlapped) {
301
+ result.push(elements[i]);
302
+ }
303
+ }
304
+
305
+ return result;
306
+ }
307
+
308
+ /**
309
+ * Check if element A is completely overlapped by element B
310
+ */
311
+ private isCompletelyOverlapped(
312
+ coordsA: { x: number; y: number; width: number; height: number },
313
+ coordsB: { x: number; y: number; width: number; height: number }
314
+ ): boolean {
315
+ // A is completely overlapped by B if:
316
+ // B's left edge is <= A's left edge AND
317
+ // B's top edge is <= A's top edge AND
318
+ // B's right edge is >= A's right edge AND
319
+ // B's bottom edge is >= A's bottom edge
320
+ return (
321
+ coordsB.x <= coordsA.x &&
322
+ coordsB.y <= coordsA.y &&
323
+ coordsB.x + coordsB.width >= coordsA.x + coordsA.width &&
324
+ coordsB.y + coordsB.height >= coordsA.y + coordsA.height
325
+ );
326
+ }
327
+
328
+ private async executeActionWithSnapshot(action: BrowserAction): Promise<any> {
329
+ const result = await this.session.executeAction(action);
330
+
331
+ // Format response for Python layer compatibility
332
+ const response: any = {
333
+ result: result.message,
334
+ snapshot: '',
335
+ };
336
+
337
+ if (result.success) {
338
+ const snapshotStart = Date.now();
339
+ response.snapshot = await this.getPageSnapshot(this.viewportLimit);
340
+ const snapshotTime = Date.now() - snapshotStart;
341
+
342
+ if (result.timing) {
343
+ result.timing.snapshot_time_ms = snapshotTime;
344
+ }
345
+ }
346
+
347
+ // Include timing if available
348
+ if (result.timing) {
349
+ response.timing = result.timing;
350
+ }
351
+
352
+ // Include newTabId if present
353
+ if (result.newTabId) {
354
+ response.newTabId = result.newTabId;
355
+ }
356
+
357
+ return response;
358
+ }
359
+
360
+ async click(ref: string): Promise<any> {
361
+ const action: BrowserAction = { type: 'click', ref };
362
+ return this.executeActionWithSnapshot(action);
363
+ }
364
+
365
+ async type(ref: string, text: string): Promise<any> {
366
+ const action: BrowserAction = { type: 'type', ref, text };
367
+ return this.executeActionWithSnapshot(action);
368
+ }
369
+
370
+ async select(ref: string, value: string): Promise<any> {
371
+ const action: BrowserAction = { type: 'select', ref, value };
372
+ return this.executeActionWithSnapshot(action);
373
+ }
374
+
375
+ async scroll(direction: 'up' | 'down', amount: number): Promise<any> {
376
+ const action: BrowserAction = { type: 'scroll', direction, amount };
377
+ return this.executeActionWithSnapshot(action);
378
+ }
379
+
380
+ async enter(): Promise<any> {
381
+ const action: BrowserAction = { type: 'enter' };
382
+ return this.executeActionWithSnapshot(action);
383
+ }
384
+
385
+ async back(): Promise<ActionResult> {
386
+ const startTime = Date.now();
387
+
388
+ try {
389
+ const page = await this.session.getCurrentPage();
390
+
391
+ const navigationStart = Date.now();
392
+ await page.goBack({ waitUntil: 'domcontentloaded' });
393
+ const navigationTime = Date.now() - navigationStart;
394
+
395
+ const snapshotStart = Date.now();
396
+ const snapshot = await this.getPageSnapshot(this.viewportLimit);
397
+ const snapshotTime = Date.now() - snapshotStart;
398
+
399
+ const totalTime = Date.now() - startTime;
400
+
401
+ return {
402
+ success: true,
403
+ message: 'Navigated back successfully',
404
+ snapshot,
405
+ timing: {
406
+ total_time_ms: totalTime,
407
+ navigation_time_ms: navigationTime,
408
+ snapshot_time_ms: snapshotTime,
409
+ },
410
+ };
411
+ } catch (error) {
412
+ const totalTime = Date.now() - startTime;
413
+ return {
414
+ success: false,
415
+ message: `Back navigation failed: ${error}`,
416
+ timing: {
417
+ total_time_ms: totalTime,
418
+ navigation_time_ms: 0,
419
+ snapshot_time_ms: 0,
420
+ },
421
+ };
422
+ }
423
+ }
424
+
425
+ async forward(): Promise<ActionResult> {
426
+ const startTime = Date.now();
427
+
428
+ try {
429
+ const page = await this.session.getCurrentPage();
430
+
431
+ const navigationStart = Date.now();
432
+ await page.goForward({ waitUntil: 'domcontentloaded' });
433
+ const navigationTime = Date.now() - navigationStart;
434
+
435
+ const snapshotStart = Date.now();
436
+ const snapshot = await this.getPageSnapshot(this.viewportLimit);
437
+ const snapshotTime = Date.now() - snapshotStart;
438
+
439
+ const totalTime = Date.now() - startTime;
440
+
441
+ return {
442
+ success: true,
443
+ message: 'Navigated forward successfully',
444
+ snapshot,
445
+ timing: {
446
+ total_time_ms: totalTime,
447
+ navigation_time_ms: navigationTime,
448
+ snapshot_time_ms: snapshotTime,
449
+ },
450
+ };
451
+ } catch (error) {
452
+ const totalTime = Date.now() - startTime;
453
+ return {
454
+ success: false,
455
+ message: `Forward navigation failed: ${error}`,
456
+ timing: {
457
+ total_time_ms: totalTime,
458
+ navigation_time_ms: 0,
459
+ snapshot_time_ms: 0,
460
+ },
461
+ };
462
+ }
463
+ }
464
+
465
+
466
+ async switchTab(tabId: string): Promise<any> {
467
+ const startTime = Date.now();
468
+
469
+ try {
470
+ const success = await this.session.switchToTab(tabId);
471
+
472
+ if (success) {
473
+ const snapshotStart = Date.now();
474
+ const snapshot = await this.getPageSnapshot(this.viewportLimit);
475
+ const snapshotTime = Date.now() - snapshotStart;
476
+
477
+ const totalTime = Date.now() - startTime;
478
+
479
+ return {
480
+ result: `Switched to tab ${tabId}`,
481
+ snapshot: snapshot,
482
+ timing: {
483
+ total_time_ms: totalTime,
484
+ snapshot_time_ms: snapshotTime,
485
+ },
486
+ };
487
+ } else {
488
+ return {
489
+ result: `Failed to switch to tab ${tabId}`,
490
+ snapshot: '',
491
+ };
492
+ }
493
+ } catch (error) {
494
+ return {
495
+ result: `Error switching tab: ${error}`,
496
+ snapshot: '',
497
+ };
498
+ }
499
+ }
500
+
501
+ async closeTab(tabId: string): Promise<ActionResult> {
502
+ const success = await this.session.closeTab(tabId);
503
+
504
+ if (success) {
505
+ return {
506
+ success: true,
507
+ message: `Closed tab ${tabId}`,
508
+ snapshot: await this.getPageSnapshot(this.viewportLimit),
509
+ };
510
+ } else {
511
+ return {
512
+ success: false,
513
+ message: `Failed to close tab ${tabId}`,
514
+ };
515
+ }
516
+ }
517
+
518
+ async getTabInfo(): Promise<TabInfo[]> {
519
+ return await this.session.getTabInfo();
520
+ }
521
+
522
+ }
@@ -0,0 +1,7 @@
1
+ export { HybridBrowserToolkit } from './hybrid-browser-toolkit';
2
+ export { HybridBrowserSession } from './browser-session';
3
+ export { ConfigLoader, StealthConfig, BrowserConfig, WebSocketConfig } from './config-loader';
4
+ export * from './types';
5
+
6
+ // Default export for convenience
7
+ export { HybridBrowserToolkit as default } from './hybrid-browser-toolkit';
@@ -0,0 +1,110 @@
1
+ export interface SnapshotElement {
2
+ ref: string;
3
+ role: string;
4
+ name: string;
5
+ coordinates?: {
6
+ x: number;
7
+ y: number;
8
+ width: number;
9
+ height: number;
10
+ };
11
+ disabled?: boolean;
12
+ checked?: boolean;
13
+ expanded?: boolean;
14
+ tagName?: string;
15
+ [key: string]: any;
16
+ }
17
+
18
+
19
+ export interface SnapshotResult {
20
+ snapshot: string;
21
+ elements: Record<string, SnapshotElement>;
22
+ metadata: {
23
+ elementCount: number;
24
+ url: string;
25
+ timestamp: string;
26
+ };
27
+ }
28
+
29
+ export interface DetailedTiming {
30
+ total_time_ms: number;
31
+ navigation_time_ms?: number;
32
+ page_load_time_ms?: number;
33
+ stability_wait_time_ms?: number;
34
+ dom_content_loaded_time_ms?: number;
35
+ network_idle_time_ms?: number;
36
+ snapshot_time_ms?: number;
37
+ element_search_time_ms?: number;
38
+ action_execution_time_ms?: number;
39
+ screenshot_time_ms?: number;
40
+ coordinate_enrichment_time_ms?: number;
41
+ visual_marking_time_ms?: number;
42
+ aria_mapping_time_ms?: number;
43
+ }
44
+
45
+ export interface ActionResult {
46
+ success: boolean;
47
+ message: string;
48
+ snapshot?: string;
49
+ details?: Record<string, any>;
50
+ timing?: DetailedTiming;
51
+ newTabId?: string; // ID of newly opened tab if click opened a new tab
52
+ }
53
+
54
+ export interface TabInfo {
55
+ tab_id: string;
56
+ title: string;
57
+ url: string;
58
+ is_current: boolean;
59
+ }
60
+
61
+ import { StealthConfig } from './config-loader';
62
+
63
+ export interface BrowserToolkitConfig {
64
+ headless?: boolean;
65
+ userDataDir?: string;
66
+ stealth?: boolean | StealthConfig; // Support both legacy boolean and new object format
67
+ defaultStartUrl?: string;
68
+ navigationTimeout?: number;
69
+ networkIdleTimeout?: number;
70
+ screenshotTimeout?: number;
71
+ pageStabilityTimeout?: number;
72
+ useNativePlaywrightMapping?: boolean; // New option to control mapping implementation
73
+ connectOverCdp?: boolean; // Whether to connect to existing browser via CDP
74
+ cdpUrl?: string; // WebSocket endpoint URL for CDP connection
75
+ }
76
+
77
+ export interface ClickAction {
78
+ type: 'click';
79
+ ref: string;
80
+ }
81
+
82
+ export interface TypeAction {
83
+ type: 'type';
84
+ ref: string;
85
+ text: string;
86
+ }
87
+
88
+ export interface SelectAction {
89
+ type: 'select';
90
+ ref: string;
91
+ value: string;
92
+ }
93
+
94
+ export interface ScrollAction {
95
+ type: 'scroll';
96
+ direction: 'up' | 'down';
97
+ amount: number;
98
+ }
99
+
100
+ export interface EnterAction {
101
+ type: 'enter';
102
+ }
103
+
104
+ export type BrowserAction = ClickAction | TypeAction | SelectAction | ScrollAction | EnterAction;
105
+
106
+ export interface VisualMarkResult {
107
+ text: string;
108
+ images: string[];
109
+ }
110
+
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "lib": ["ES2020", "DOM"],
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "sourceMap": true,
15
+ "moduleResolution": "node",
16
+ "resolveJsonModule": true
17
+ },
18
+ "include": [
19
+ "src/**/*"
20
+ ],
21
+ "exclude": [
22
+ "node_modules",
23
+ "dist",
24
+ "**/*.test.ts"
25
+ ]
26
+ }