@loxia-labs/loxia-autopilot-one 1.0.1 → 1.0.4

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 (120) hide show
  1. package/README.md +44 -54
  2. package/bin/cli.js +1 -115
  3. package/bin/loxia-terminal-v2.js +3 -0
  4. package/bin/loxia-terminal.js +3 -0
  5. package/bin/start-with-terminal.js +3 -0
  6. package/package.json +15 -15
  7. package/scripts/install-scanners.js +1 -235
  8. package/src/analyzers/CSSAnalyzer.js +1 -297
  9. package/src/analyzers/ConfigValidator.js +1 -690
  10. package/src/analyzers/ESLintAnalyzer.js +1 -320
  11. package/src/analyzers/JavaScriptAnalyzer.js +1 -261
  12. package/src/analyzers/PrettierFormatter.js +1 -247
  13. package/src/analyzers/PythonAnalyzer.js +1 -266
  14. package/src/analyzers/SecurityAnalyzer.js +1 -729
  15. package/src/analyzers/TypeScriptAnalyzer.js +1 -247
  16. package/src/analyzers/codeCloneDetector/analyzer.js +1 -344
  17. package/src/analyzers/codeCloneDetector/detector.js +1 -203
  18. package/src/analyzers/codeCloneDetector/index.js +1 -160
  19. package/src/analyzers/codeCloneDetector/parser.js +1 -199
  20. package/src/analyzers/codeCloneDetector/reporter.js +1 -148
  21. package/src/analyzers/codeCloneDetector/scanner.js +1 -59
  22. package/src/core/agentPool.js +1 -1474
  23. package/src/core/agentScheduler.js +1 -2147
  24. package/src/core/contextManager.js +1 -709
  25. package/src/core/messageProcessor.js +1 -732
  26. package/src/core/orchestrator.js +1 -548
  27. package/src/core/stateManager.js +1 -877
  28. package/src/index.js +1 -631
  29. package/src/interfaces/cli.js +1 -549
  30. package/src/interfaces/terminal/__tests__/smoke/advancedFeatures.test.js +1 -0
  31. package/src/interfaces/terminal/__tests__/smoke/agentControl.test.js +1 -0
  32. package/src/interfaces/terminal/__tests__/smoke/agents.test.js +1 -0
  33. package/src/interfaces/terminal/__tests__/smoke/components.test.js +1 -0
  34. package/src/interfaces/terminal/__tests__/smoke/connection.test.js +1 -0
  35. package/src/interfaces/terminal/__tests__/smoke/enhancements.test.js +1 -0
  36. package/src/interfaces/terminal/__tests__/smoke/imports.test.js +1 -0
  37. package/src/interfaces/terminal/__tests__/smoke/messages.test.js +1 -0
  38. package/src/interfaces/terminal/__tests__/smoke/tools.test.js +1 -0
  39. package/src/interfaces/terminal/api/apiClient.js +1 -0
  40. package/src/interfaces/terminal/api/messageRouter.js +1 -0
  41. package/src/interfaces/terminal/api/session.js +1 -0
  42. package/src/interfaces/terminal/api/websocket.js +1 -0
  43. package/src/interfaces/terminal/components/AgentCreator.js +1 -0
  44. package/src/interfaces/terminal/components/AgentEditor.js +1 -0
  45. package/src/interfaces/terminal/components/AgentSwitcher.js +1 -0
  46. package/src/interfaces/terminal/components/ErrorBoundary.js +1 -0
  47. package/src/interfaces/terminal/components/ErrorPanel.js +1 -0
  48. package/src/interfaces/terminal/components/Header.js +1 -0
  49. package/src/interfaces/terminal/components/HelpPanel.js +1 -0
  50. package/src/interfaces/terminal/components/InputBox.js +1 -0
  51. package/src/interfaces/terminal/components/Layout.js +1 -0
  52. package/src/interfaces/terminal/components/LoadingSpinner.js +1 -0
  53. package/src/interfaces/terminal/components/MessageList.js +1 -0
  54. package/src/interfaces/terminal/components/MultilineTextInput.js +1 -0
  55. package/src/interfaces/terminal/components/SearchPanel.js +1 -0
  56. package/src/interfaces/terminal/components/SettingsPanel.js +1 -0
  57. package/src/interfaces/terminal/components/StatusBar.js +1 -0
  58. package/src/interfaces/terminal/components/TextInput.js +1 -0
  59. package/src/interfaces/terminal/config/agentEditorConstants.js +1 -0
  60. package/src/interfaces/terminal/config/constants.js +1 -0
  61. package/src/interfaces/terminal/index.js +1 -0
  62. package/src/interfaces/terminal/state/useAgentControl.js +1 -0
  63. package/src/interfaces/terminal/state/useAgents.js +1 -0
  64. package/src/interfaces/terminal/state/useConnection.js +1 -0
  65. package/src/interfaces/terminal/state/useMessages.js +1 -0
  66. package/src/interfaces/terminal/state/useTools.js +1 -0
  67. package/src/interfaces/terminal/utils/debugLogger.js +1 -0
  68. package/src/interfaces/terminal/utils/settingsStorage.js +1 -0
  69. package/src/interfaces/terminal/utils/theme.js +1 -0
  70. package/src/interfaces/webServer.js +1 -2162
  71. package/src/modules/fileExplorer/controller.js +1 -280
  72. package/src/modules/fileExplorer/index.js +1 -37
  73. package/src/modules/fileExplorer/middleware.js +1 -92
  74. package/src/modules/fileExplorer/routes.js +1 -125
  75. package/src/modules/fileExplorer/types.js +1 -44
  76. package/src/services/aiService.js +1 -1232
  77. package/src/services/apiKeyManager.js +1 -164
  78. package/src/services/benchmarkService.js +1 -366
  79. package/src/services/budgetService.js +1 -539
  80. package/src/services/contextInjectionService.js +1 -247
  81. package/src/services/conversationCompactionService.js +1 -637
  82. package/src/services/errorHandler.js +1 -810
  83. package/src/services/fileAttachmentService.js +1 -544
  84. package/src/services/modelRouterService.js +1 -366
  85. package/src/services/modelsService.js +1 -322
  86. package/src/services/qualityInspector.js +1 -796
  87. package/src/services/tokenCountingService.js +1 -536
  88. package/src/tools/agentCommunicationTool.js +1 -1344
  89. package/src/tools/agentDelayTool.js +1 -485
  90. package/src/tools/asyncToolManager.js +1 -604
  91. package/src/tools/baseTool.js +1 -800
  92. package/src/tools/browserTool.js +1 -920
  93. package/src/tools/cloneDetectionTool.js +1 -621
  94. package/src/tools/dependencyResolverTool.js +1 -1215
  95. package/src/tools/fileContentReplaceTool.js +1 -875
  96. package/src/tools/fileSystemTool.js +1 -1107
  97. package/src/tools/fileTreeTool.js +1 -853
  98. package/src/tools/imageTool.js +1 -901
  99. package/src/tools/importAnalyzerTool.js +1 -1060
  100. package/src/tools/jobDoneTool.js +1 -248
  101. package/src/tools/seekTool.js +1 -956
  102. package/src/tools/staticAnalysisTool.js +1 -1778
  103. package/src/tools/taskManagerTool.js +1 -2873
  104. package/src/tools/terminalTool.js +1 -2304
  105. package/src/tools/webTool.js +1 -1430
  106. package/src/types/agent.js +1 -519
  107. package/src/types/contextReference.js +1 -972
  108. package/src/types/conversation.js +1 -730
  109. package/src/types/toolCommand.js +1 -747
  110. package/src/utilities/attachmentValidator.js +1 -292
  111. package/src/utilities/configManager.js +1 -582
  112. package/src/utilities/constants.js +1 -722
  113. package/src/utilities/directoryAccessManager.js +1 -535
  114. package/src/utilities/fileProcessor.js +1 -307
  115. package/src/utilities/logger.js +1 -436
  116. package/src/utilities/tagParser.js +1 -1246
  117. package/src/utilities/toolConstants.js +1 -317
  118. package/web-ui/build/index.html +2 -2
  119. package/web-ui/build/static/{index-Dy2bYbOa.css → index-CClD1090.css} +1 -1
  120. package/web-ui/build/static/{index-CjkkcnFA.js → index-lCBai6dX.js} +66 -67
@@ -1,1430 +1 @@
1
- /**
2
- * WebTool - Web browsing and automation with Puppeteer
3
- *
4
- * Purpose:
5
- * - Search the web using known search engines
6
- * - Fetch web content in various formats
7
- * - Interactive browser automation with command chaining
8
- * - Tab management with agent isolation
9
- * - Screenshot capture and AI-powered analysis
10
- * - Mouse and keyboard event simulation
11
- */
12
-
13
- import { BaseTool } from './baseTool.js';
14
- import TagParser from '../utilities/tagParser.js';
15
- import puppeteer from 'puppeteer';
16
- import path from 'path';
17
- import fs from 'fs/promises';
18
- import os from 'os';
19
-
20
- import {
21
- TOOL_STATUS,
22
- SYSTEM_DEFAULTS
23
- } from '../utilities/constants.js';
24
-
25
- class WebTool extends BaseTool {
26
- constructor(config = {}, logger = null) {
27
- super(config, logger);
28
-
29
- // Tool metadata
30
- this.requiresProject = false;
31
- this.isAsync = true;
32
-
33
- // Browser instance (singleton per system)
34
- this.browser = null;
35
- this.browserInitializing = false;
36
-
37
- // Tab tracking: Map<agentId, Map<tabName, tabInfo>>
38
- this.agentTabs = new Map();
39
-
40
- // Known search engines
41
- this.searchEngines = [
42
- {
43
- name: 'google',
44
- url: 'https://www.google.com/search?q=',
45
- searchSelector: 'input[name="q"]',
46
- submitSelector: 'input[type="submit"], button[type="submit"]',
47
- resultsSelector: '#search .g a, #search a[href]',
48
- waitSelector: '#search'
49
- },
50
- {
51
- name: 'bing',
52
- url: 'https://www.bing.com/search?q=',
53
- searchSelector: 'input[name="q"]',
54
- submitSelector: 'input[type="submit"]',
55
- resultsSelector: '.b_algo a',
56
- waitSelector: '#b_results'
57
- },
58
- {
59
- name: 'duckduckgo',
60
- url: 'https://duckduckgo.com/?q=',
61
- searchSelector: 'input[name="q"]',
62
- submitSelector: 'button[type="submit"]',
63
- resultsSelector: '.result__a, a[data-testid="result-title-a"]',
64
- waitSelector: '#links, [data-testid="mainline"]'
65
- }
66
- ];
67
-
68
- // Configuration
69
- this.TAB_IDLE_TIMEOUT = config.tabIdleTimeout || 60 * 60 * 1000; // 1 hour
70
- this.CLEANUP_INTERVAL = config.cleanupInterval || 5 * 60 * 1000; // 5 minutes
71
- this.DEFAULT_TIMEOUT = config.defaultTimeout || 60000; // 60 seconds
72
- this.TEMP_DIR = config.tempDir || path.join(os.tmpdir(), 'webtool-screenshots');
73
-
74
- // Start cleanup timer
75
- this.cleanupTimer = null;
76
- this.startCleanupTimer();
77
-
78
- // Ensure temp directory exists
79
- this.ensureTempDir();
80
- }
81
-
82
- /**
83
- * Get tool description for LLM consumption
84
- * @returns {string} Tool description
85
- */
86
- getDescription() {
87
- return `
88
- Web Tool: Browse, search, and automate web interactions using a real browser (Puppeteer).
89
-
90
- IMPORTANT: This tool supports command chaining - nest multiple actions together to execute them sequentially without waiting for responses between each action.
91
-
92
- USAGE:
93
- [tool id="web"]
94
- <operation>search|fetch|interactive</operation>
95
- <!-- Operation-specific content -->
96
- [/tool]
97
-
98
- ALTERNATIVE JSON FORMAT:
99
- \`\`\`json
100
- {
101
- "toolId": "web",
102
- "operation": "search|fetch|interactive",
103
- "parameters": { ... }
104
- }
105
- \`\`\`
106
-
107
- ═══════════════════════════════════════════════════════════════
108
- OPERATION 1: SEARCH THE WEB
109
- ═══════════════════════════════════════════════════════════════
110
-
111
- Search using real browsers on known search engines (Google, Bing, DuckDuckGo).
112
-
113
- XML SYNTAX:
114
- [tool id="web"]
115
- <operation>search</operation>
116
- <query>puppeteer web scraping tutorial</query>
117
- <engine>google</engine> <!-- Optional: google|bing|duckduckgo, default: google -->
118
- <max-results>10</max-results> <!-- Optional, default: 10 -->
119
- [/tool]
120
-
121
- JSON SYNTAX:
122
- \`\`\`json
123
- {
124
- "toolId": "web",
125
- "operation": "search",
126
- "query": "puppeteer web scraping tutorial",
127
- "engine": "google",
128
- "maxResults": 10
129
- }
130
- \`\`\`
131
-
132
- OUTPUT: List of URLs with titles and descriptions
133
-
134
- ═══════════════════════════════════════════════════════════════
135
- OPERATION 2: FETCH WEB CONTENT
136
- ═══════════════════════════════════════════════════════════════
137
-
138
- Fetch content from a URL in various formats.
139
-
140
- XML SYNTAX:
141
- [tool id="web"]
142
- <operation>fetch</operation>
143
- <url>https://example.com</url>
144
- <format>title,text,links</format> <!-- Options: title|text|links|html|console -->
145
- [/tool]
146
-
147
- JSON SYNTAX:
148
- \`\`\`json
149
- {
150
- "toolId": "web",
151
- "operation": "fetch",
152
- "url": "https://example.com",
153
- "formats": ["title", "text", "links", "html", "console"]
154
- }
155
- \`\`\`
156
-
157
- FORMAT OPTIONS:
158
- - title: Page title
159
- - text: Plain text content (no HTML)
160
- - links: All links on page
161
- - html: Full HTML source
162
- - console: Browser console messages
163
-
164
- OUTPUT: Object with requested content formats
165
-
166
- ═══════════════════════════════════════════════════════════════
167
- OPERATION 3: INTERACTIVE BROWSER AUTOMATION
168
- ═══════════════════════════════════════════════════════════════
169
-
170
- Control a real browser with command chaining for complex workflows.
171
-
172
- XML SYNTAX (RECOMMENDED FOR CHAINING):
173
- [tool id="web"]
174
- <operation>interactive</operation>
175
- <headless>true</headless> <!-- true|false, default: true -->
176
- <actions>
177
- <open-tab name="search">
178
- <navigate>https://github.com/trending</navigate>
179
- <wait-for selector=".Box-row" timeout="5000" />
180
- <click selector=".Box-row:first-child a" />
181
- <wait-for selector="#readme" />
182
- <screenshot format="file" path="readme.png" />
183
- <analyze-screenshot>What is the main topic of this README?</analyze-screenshot>
184
- <extract-text selector="#readme" />
185
- <get-source />
186
- </open-tab>
187
- <open-tab name="docs">
188
- <navigate>https://docs.example.com</navigate>
189
- <type selector="input.search" text="installation" />
190
- <press key="Enter" />
191
- <extract-links selector="a.doc-link" />
192
- </open-tab>
193
- <list-tabs />
194
- <close-tab name="search" />
195
- </actions>
196
- [/tool]
197
-
198
- JSON SYNTAX (ALTERNATIVE):
199
- \`\`\`json
200
- {
201
- "toolId": "web",
202
- "operation": "interactive",
203
- "headless": true,
204
- "actions": [
205
- {
206
- "type": "open-tab",
207
- "name": "search",
208
- "url": "https://github.com/trending",
209
- "nestedActions": [
210
- {"type": "wait-for", "selector": ".Box-row", "timeout": 5000},
211
- {"type": "click", "selector": ".Box-row:first-child a"},
212
- {"type": "screenshot", "format": "base64"},
213
- {"type": "extract-text", "selector": "#readme"}
214
- ]
215
- },
216
- {"type": "list-tabs"},
217
- {"type": "close-tab", "name": "search"}
218
- ]
219
- }
220
- \`\`\`
221
-
222
- SUPPORTED ACTIONS:
223
- - open-tab: Open new tab with nested actions
224
- - close-tab: Close specific tab
225
- - switch-tab: Switch to existing tab
226
- - list-tabs: List all active tabs for this agent
227
- - navigate: Go to URL
228
- - click: Click element (left|right|middle)
229
- - type: Type text into element
230
- - press: Press keyboard key
231
- - wait-for: Wait for element to appear
232
- - screenshot: Capture screenshot (file|base64)
233
- - analyze-screenshot: AI analysis of current page
234
- - extract-text: Extract text from selector
235
- - extract-links: Extract all links
236
- - get-source: Get HTML source
237
- - get-console: Get console messages
238
- - scroll: Scroll page
239
- - hover: Hover over element
240
-
241
- MOUSE EVENTS:
242
- <click selector=".button" button="left" /> <!-- left|right|middle -->
243
- <hover selector=".menu" />
244
- <mouse-move selector=".element" />
245
-
246
- KEYBOARD EVENTS:
247
- <type selector="input" text="Hello World" />
248
- <press key="Enter" />
249
- <press key="Control+C" /> <!-- Supports modifier keys -->
250
-
251
- SCREENSHOT OPTIONS:
252
- <screenshot format="file" path="screenshot.png" /> <!-- Save to project dir -->
253
- <screenshot format="file" /> <!-- Save to temp dir -->
254
- <screenshot format="base64" /> <!-- Return as base64 string -->
255
-
256
- AI SCREENSHOT ANALYSIS:
257
- <analyze-screenshot>What products are visible on this page?</analyze-screenshot>
258
- <analyze-screenshot model="gpt-4-vision">Describe the layout</analyze-screenshot>
259
-
260
- TAB MANAGEMENT:
261
- - Tabs are agent-isolated (each agent has its own tabs)
262
- - Tabs auto-close after 1 hour of inactivity
263
- - Tab names must be unique per agent
264
- - Use descriptive names for easy identification
265
-
266
- ═══════════════════════════════════════════════════════════════
267
- COMMAND CHAINING BENEFITS
268
- ═══════════════════════════════════════════════════════════════
269
-
270
- Instead of:
271
- 1. Open tab → wait for response
272
- 2. Navigate → wait for response
273
- 3. Click → wait for response
274
- 4. Screenshot → wait for response
275
-
276
- Do this (ONE REQUEST):
277
- <open-tab name="task">
278
- <navigate>URL</navigate>
279
- <click selector=".button" />
280
- <screenshot />
281
- </open-tab>
282
-
283
- All actions execute sequentially in one operation!
284
-
285
- ═══════════════════════════════════════════════════════════════
286
- EXAMPLES
287
- ═══════════════════════════════════════════════════════════════
288
-
289
- EXAMPLE 1: Search and analyze results
290
- [tool id="web"]
291
- <operation>interactive</operation>
292
- <headless>true</headless>
293
- <actions>
294
- <open-tab name="search">
295
- <navigate>https://google.com</navigate>
296
- <type selector="input[name=q]">best web scraping tools 2025</type>
297
- <press key="Enter" />
298
- <wait-for selector="#search" />
299
- <screenshot format="file" path="search-results.png" />
300
- <analyze-screenshot>List the top 3 tools mentioned</analyze-screenshot>
301
- <extract-links selector="#search .g a" />
302
- </open-tab>
303
- </actions>
304
- [/tool]
305
-
306
- EXAMPLE 2: Multi-tab workflow
307
- [tool id="web"]
308
- <operation>interactive</operation>
309
- <actions>
310
- <open-tab name="github">
311
- <navigate>https://github.com/trending</navigate>
312
- <extract-links selector=".Box-row a" />
313
- </open-tab>
314
- <open-tab name="npm">
315
- <navigate>https://npmjs.com/package/puppeteer</navigate>
316
- <extract-text selector=".package-description" />
317
- </open-tab>
318
- <list-tabs />
319
- </actions>
320
- [/tool]
321
-
322
- EXAMPLE 3: Form interaction
323
- [tool id="web"]
324
- <operation>interactive</operation>
325
- <headless>false</headless> <!-- Visible browser for debugging -->
326
- <actions>
327
- <open-tab name="form">
328
- <navigate>https://example.com/contact</navigate>
329
- <type selector="#name" text="John Doe" />
330
- <type selector="#email" text="john@example.com" />
331
- <type selector="#message" text="Hello!" />
332
- <click selector="#submit" />
333
- <wait-for selector=".success-message" />
334
- <screenshot format="base64" />
335
- </open-tab>
336
- </actions>
337
- [/tool]
338
-
339
- EXAMPLE 4: Simple fetch
340
- [tool id="web"]
341
- <operation>fetch</operation>
342
- <url>https://example.com</url>
343
- <format>title,text,links</format>
344
- [/tool]
345
-
346
- EXAMPLE 5: Quick search
347
- [tool id="web"]
348
- <operation>search</operation>
349
- <query>openai gpt-4 api documentation</query>
350
- <engine>google</engine>
351
- <max-results>5</max-results>
352
- [/tool]
353
-
354
- SECURITY NOTES:
355
- - Browser runs in isolated context per agent
356
- - Tabs auto-close after 1 hour of inactivity
357
- - Screenshots stored in temp directory with auto-cleanup
358
- - No access to local file system beyond allowed directories
359
-
360
- BEST PRACTICES:
361
- - Use command chaining to minimize round-trips
362
- - Use descriptive tab names
363
- - Close tabs when done to free resources
364
- - Use headless mode for better performance
365
- - Use visible mode only for debugging
366
- `;
367
- }
368
-
369
- /**
370
- * Parse parameters from tool command content
371
- * @param {string} content - Raw tool command content
372
- * @returns {Object} Parsed parameters
373
- */
374
- parseParameters(content) {
375
- try {
376
- // Try JSON first
377
- if (content.trim().startsWith('{')) {
378
- return JSON.parse(content);
379
- }
380
-
381
- // Parse XML-style tags
382
- const params = {};
383
-
384
- // Extract operation
385
- const operationMatches = TagParser.extractContent(content, 'operation');
386
- if (operationMatches.length > 0) {
387
- params.operation = operationMatches[0].trim();
388
- }
389
-
390
- // Extract based on operation
391
- switch (params.operation) {
392
- case 'search':
393
- params.query = TagParser.extractContent(content, 'query')[0]?.trim();
394
- params.engine = TagParser.extractContent(content, 'engine')[0]?.trim() || 'google';
395
- const maxResults = TagParser.extractContent(content, 'max-results')[0]?.trim();
396
- params.maxResults = maxResults ? parseInt(maxResults, 10) : 10;
397
- break;
398
-
399
- case 'fetch':
400
- params.url = TagParser.extractContent(content, 'url')[0]?.trim();
401
- const formatStr = TagParser.extractContent(content, 'format')[0]?.trim();
402
- params.formats = formatStr ? formatStr.split(',').map(f => f.trim()) : ['title', 'text'];
403
- break;
404
-
405
- case 'interactive':
406
- const headlessStr = TagParser.extractContent(content, 'headless')[0]?.trim();
407
- params.headless = headlessStr !== 'false'; // Default true
408
-
409
- // Extract actions block
410
- const actionsContent = TagParser.extractContent(content, 'actions')[0];
411
- if (actionsContent) {
412
- params.actions = this.parseActions(actionsContent);
413
- }
414
- break;
415
- }
416
-
417
- params.rawContent = content.trim();
418
- return params;
419
-
420
- } catch (error) {
421
- throw new Error(`Failed to parse web tool parameters: ${error.message}`);
422
- }
423
- }
424
-
425
- /**
426
- * Parse actions from XML content
427
- * @param {string} content - Actions XML content
428
- * @returns {Array} Parsed actions
429
- * @private
430
- */
431
- parseActions(content) {
432
- const actions = [];
433
-
434
- // Parse open-tab actions
435
- const openTabRegex = /<open-tab[^>]*name="([^"]+)"[^>]*>([\s\S]*?)<\/open-tab>/g;
436
- let match;
437
-
438
- while ((match = openTabRegex.exec(content)) !== null) {
439
- const [, name, nestedContent] = match;
440
- const url = TagParser.extractContent(nestedContent, 'navigate')[0]?.trim();
441
-
442
- actions.push({
443
- type: 'open-tab',
444
- name,
445
- url,
446
- nestedActions: this.parseNestedActions(nestedContent)
447
- });
448
- }
449
-
450
- // Parse other actions (close-tab, switch-tab, list-tabs, etc.)
451
- const simpleActions = [
452
- 'close-tab', 'switch-tab', 'list-tabs', 'navigate',
453
- 'click', 'type', 'press', 'wait-for', 'screenshot',
454
- 'analyze-screenshot', 'extract-text', 'extract-links',
455
- 'get-source', 'get-console', 'scroll', 'hover', 'mouse-move'
456
- ];
457
-
458
- for (const actionType of simpleActions) {
459
- const regex = new RegExp(`<${actionType}([^>]*)>([^<]*)<\/${actionType}>`, 'g');
460
- let actionMatch;
461
-
462
- while ((actionMatch = regex.exec(content)) !== null) {
463
- const [, attrs, value] = actionMatch;
464
- const action = { type: actionType };
465
-
466
- // Parse attributes
467
- const attrRegex = /(\w+(?:-\w+)*)="([^"]*)"/g;
468
- let attrMatch;
469
- while ((attrMatch = attrRegex.exec(attrs)) !== null) {
470
- action[attrMatch[1]] = attrMatch[2];
471
- }
472
-
473
- // Add value if present
474
- if (value && value.trim()) {
475
- action.value = value.trim();
476
- }
477
-
478
- actions.push(action);
479
- }
480
- }
481
-
482
- return actions;
483
- }
484
-
485
- /**
486
- * Parse nested actions within a tab
487
- * @param {string} content - Nested actions content
488
- * @returns {Array} Parsed nested actions
489
- * @private
490
- */
491
- parseNestedActions(content) {
492
- const actions = [];
493
-
494
- const actionTypes = [
495
- 'navigate', 'click', 'type', 'press', 'wait-for', 'screenshot',
496
- 'analyze-screenshot', 'extract-text', 'extract-links',
497
- 'get-source', 'get-console', 'scroll', 'hover', 'mouse-move'
498
- ];
499
-
500
- for (const actionType of actionTypes) {
501
- const regex = new RegExp(`<${actionType}([^>]*)>([^<]*)<\/${actionType}>|<${actionType}([^>]*)\/>`, 'g');
502
- let match;
503
-
504
- while ((match = regex.exec(content)) !== null) {
505
- const [, attrs1, value, attrs2] = match;
506
- const attrs = attrs1 || attrs2 || '';
507
- const action = { type: actionType };
508
-
509
- // Parse attributes
510
- const attrRegex = /(\w+(?:-\w+)*)="([^"]*)"/g;
511
- let attrMatch;
512
- while ((attrMatch = attrRegex.exec(attrs)) !== null) {
513
- action[attrMatch[1]] = attrMatch[2];
514
- }
515
-
516
- // Add value if present
517
- if (value && value.trim()) {
518
- action.value = value.trim();
519
- }
520
-
521
- actions.push(action);
522
- }
523
- }
524
-
525
- return actions;
526
- }
527
-
528
- /**
529
- * Get required parameters based on operation
530
- * @returns {Array<string>} Array of required parameter names
531
- */
532
- getRequiredParameters() {
533
- return ['operation'];
534
- }
535
-
536
- /**
537
- * Custom parameter validation
538
- * @param {Object} params - Parameters to validate
539
- * @returns {Object} Validation result
540
- */
541
- customValidateParameters(params) {
542
- const errors = [];
543
-
544
- if (!['search', 'fetch', 'interactive'].includes(params.operation)) {
545
- errors.push('operation must be one of: search, fetch, interactive');
546
- return { valid: false, errors };
547
- }
548
-
549
- switch (params.operation) {
550
- case 'search':
551
- if (!params.query) {
552
- errors.push('query is required for search operation');
553
- }
554
- break;
555
-
556
- case 'fetch':
557
- if (!params.url) {
558
- errors.push('url is required for fetch operation');
559
- }
560
- break;
561
-
562
- case 'interactive':
563
- if (!params.actions || !Array.isArray(params.actions) || params.actions.length === 0) {
564
- errors.push('actions array is required for interactive operation');
565
- }
566
- break;
567
- }
568
-
569
- return {
570
- valid: errors.length === 0,
571
- errors
572
- };
573
- }
574
-
575
- /**
576
- * Execute tool with parsed parameters
577
- * @param {Object} params - Parsed parameters
578
- * @param {Object} context - Execution context
579
- * @returns {Promise<Object>} Execution result
580
- */
581
- async execute(params, context) {
582
- const { operation } = params;
583
- const { agentId } = context;
584
-
585
- try {
586
- // Ensure browser is initialized
587
- await this.ensureBrowser();
588
-
589
- let result;
590
-
591
- switch (operation) {
592
- case 'search':
593
- result = await this.search(params.query, {
594
- engine: params.engine || 'google',
595
- maxResults: params.maxResults || 10,
596
- agentId
597
- });
598
- break;
599
-
600
- case 'fetch':
601
- result = await this.fetch(params.url, {
602
- formats: params.formats || ['title', 'text'],
603
- agentId
604
- });
605
- break;
606
-
607
- case 'interactive':
608
- result = await this.interactive(params.actions, {
609
- headless: params.headless !== false, // Default true
610
- agentId,
611
- context
612
- });
613
- break;
614
-
615
- default:
616
- throw new Error(`Unknown operation: ${operation}`);
617
- }
618
-
619
- return {
620
- success: true,
621
- operation,
622
- result,
623
- toolUsed: 'web'
624
- };
625
-
626
- } catch (error) {
627
- this.logger?.error('Web tool execution failed', {
628
- operation,
629
- error: error.message,
630
- agentId
631
- });
632
-
633
- return {
634
- success: false,
635
- operation,
636
- error: error.message,
637
- toolUsed: 'web'
638
- };
639
- }
640
- }
641
-
642
- /**
643
- * Ensure browser is initialized
644
- * @private
645
- */
646
- async ensureBrowser() {
647
- if (this.browser && this.browser.isConnected()) {
648
- return;
649
- }
650
-
651
- if (this.browserInitializing) {
652
- // Wait for browser to finish initializing
653
- while (this.browserInitializing) {
654
- await new Promise(resolve => setTimeout(resolve, 100));
655
- }
656
- return;
657
- }
658
-
659
- this.browserInitializing = true;
660
-
661
- try {
662
- this.logger?.info('Initializing Puppeteer browser');
663
-
664
- this.browser = await puppeteer.launch({
665
- headless: 'new', // Use new headless mode
666
- args: [
667
- '--no-sandbox',
668
- '--disable-setuid-sandbox',
669
- '--disable-dev-shm-usage',
670
- '--disable-accelerated-2d-canvas',
671
- '--disable-gpu',
672
- '--disable-blink-features=AutomationControlled',
673
- '--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
674
- ]
675
- });
676
-
677
- this.logger?.info('Puppeteer browser initialized successfully');
678
-
679
- } catch (error) {
680
- this.logger?.error('Failed to initialize browser', { error: error.message });
681
- throw new Error(`Browser initialization failed: ${error.message}`);
682
- } finally {
683
- this.browserInitializing = false;
684
- }
685
- }
686
-
687
- /**
688
- * Search the web using a known search engine
689
- * @param {string} query - Search query
690
- * @param {Object} options - Search options
691
- * @returns {Promise<Object>} Search results
692
- */
693
- async search(query, options = {}) {
694
- const { engine = 'google', maxResults = 10, agentId } = options;
695
-
696
- // Validate query
697
- if (!query || typeof query !== 'string' || query.trim().length === 0) {
698
- throw new Error('Search query is required and must be a non-empty string');
699
- }
700
-
701
- const searchEngine = this.searchEngines.find(e => e.name === engine);
702
- if (!searchEngine) {
703
- throw new Error(`Unknown search engine: ${engine}. Available: ${this.searchEngines.map(e => e.name).join(', ')}`);
704
- }
705
-
706
- // Ensure browser is initialized
707
- await this.ensureBrowser();
708
-
709
- this.logger?.info('Performing web search', { query, engine, agentId });
710
-
711
- // Create temporary page for search
712
- const page = await this.browser.newPage();
713
-
714
- try {
715
- // Navigate to search engine
716
- const searchUrl = `${searchEngine.url}${encodeURIComponent(query)}`;
717
- await page.goto(searchUrl, { waitUntil: 'networkidle2', timeout: this.DEFAULT_TIMEOUT });
718
-
719
- // Wait for results
720
- await page.waitForSelector(searchEngine.waitSelector, { timeout: this.DEFAULT_TIMEOUT });
721
-
722
- // Extract results
723
- const results = await page.evaluate((selector, max) => {
724
- const links = Array.from(document.querySelectorAll(selector));
725
- return links.slice(0, max).map(link => ({
726
- url: link.href,
727
- title: link.textContent.trim(),
728
- description: link.closest('.g, .b_algo, .result')?.textContent.trim() || ''
729
- })).filter(result => result.url && result.url.startsWith('http'));
730
- }, searchEngine.resultsSelector, maxResults);
731
-
732
- this.logger?.info('Search completed', { resultsCount: results.length, agentId });
733
-
734
- return {
735
- success: true,
736
- query,
737
- engine,
738
- resultsCount: results.length,
739
- results
740
- };
741
-
742
- } finally {
743
- await page.close();
744
- }
745
- }
746
-
747
- /**
748
- * Fetch web content in various formats
749
- * @param {string} url - URL to fetch
750
- * @param {Object} options - Fetch options
751
- * @returns {Promise<Object>} Fetched content
752
- */
753
- async fetch(url, options = {}) {
754
- const { formats = ['title', 'text'], agentId } = options;
755
-
756
- // Ensure browser is initialized
757
- await this.ensureBrowser();
758
-
759
- this.logger?.info('Fetching web content', { url, formats, agentId });
760
-
761
- // Create temporary page
762
- const page = await this.browser.newPage();
763
-
764
- try {
765
- // Listen for console messages if requested
766
- const consoleMessages = [];
767
- if (formats.includes('console')) {
768
- page.on('console', msg => {
769
- consoleMessages.push({
770
- type: msg.type(),
771
- text: msg.text()
772
- });
773
- });
774
- }
775
-
776
- // Navigate to URL
777
- await page.goto(url, { waitUntil: 'networkidle2', timeout: this.DEFAULT_TIMEOUT });
778
-
779
- const result = { url };
780
-
781
- // Extract requested formats
782
- for (const format of formats) {
783
- switch (format) {
784
- case 'title':
785
- result.title = await page.title();
786
- break;
787
-
788
- case 'text':
789
- result.text = await page.evaluate(() => document.body.innerText);
790
- break;
791
-
792
- case 'links':
793
- result.links = await page.evaluate(() => {
794
- return Array.from(document.querySelectorAll('a[href]')).map(a => ({
795
- href: a.href,
796
- text: a.textContent.trim()
797
- }));
798
- });
799
- break;
800
-
801
- case 'html':
802
- result.html = await page.content();
803
- break;
804
-
805
- case 'console':
806
- result.consoleMessages = consoleMessages;
807
- break;
808
- }
809
- }
810
-
811
- this.logger?.info('Fetch completed', { url, formats, agentId });
812
-
813
- return {
814
- success: true,
815
- ...result
816
- };
817
-
818
- } finally {
819
- await page.close();
820
- }
821
- }
822
-
823
- /**
824
- * Interactive browser automation with command chaining
825
- * @param {Array} actions - Array of actions to execute
826
- * @param {Object} options - Options
827
- * @returns {Promise<Object>} Results of all actions
828
- */
829
- async interactive(actions, options = {}) {
830
- const { headless = true, agentId, context } = options;
831
-
832
- // Ensure browser is initialized
833
- await this.ensureBrowser();
834
-
835
- this.logger?.info('Starting interactive session', {
836
- actionsCount: actions.length,
837
- headless,
838
- agentId
839
- });
840
-
841
- const results = [];
842
-
843
- // Initialize agent tabs if not exists
844
- if (!this.agentTabs.has(agentId)) {
845
- this.agentTabs.set(agentId, new Map());
846
- }
847
-
848
- const agentTabsMap = this.agentTabs.get(agentId);
849
-
850
- for (const action of actions) {
851
- try {
852
- let actionResult;
853
-
854
- switch (action.type) {
855
- case 'open-tab':
856
- actionResult = await this.openTab(agentId, action.name, action.url, headless, action.nestedActions, context);
857
- break;
858
-
859
- case 'close-tab':
860
- actionResult = await this.closeTab(agentId, action.name);
861
- break;
862
-
863
- case 'switch-tab':
864
- actionResult = await this.switchTab(agentId, action.name);
865
- break;
866
-
867
- case 'list-tabs':
868
- actionResult = await this.listTabs(agentId);
869
- break;
870
-
871
- default:
872
- // For actions that need a tab context, we need to specify which tab
873
- // For now, we'll skip these at the top level
874
- actionResult = {
875
- success: false,
876
- error: `Action ${action.type} must be executed within a tab context (use open-tab with nestedActions)`
877
- };
878
- }
879
-
880
- results.push({
881
- action: action.type,
882
- ...actionResult
883
- });
884
-
885
- } catch (error) {
886
- this.logger?.error('Action failed', {
887
- action: action.type,
888
- error: error.message,
889
- agentId
890
- });
891
-
892
- results.push({
893
- action: action.type,
894
- success: false,
895
- error: error.message
896
- });
897
- }
898
- }
899
-
900
- return {
901
- success: results.every(r => r.success !== false),
902
- actionsExecuted: results.length,
903
- results
904
- };
905
- }
906
-
907
- /**
908
- * Open a new tab with nested actions
909
- * @param {string} agentId - Agent identifier
910
- * @param {string} tabName - Unique tab name
911
- * @param {string} url - Initial URL
912
- * @param {boolean} headless - Headless mode
913
- * @param {Array} nestedActions - Actions to execute in this tab
914
- * @param {Object} context - Execution context
915
- * @returns {Promise<Object>} Result
916
- */
917
- async openTab(agentId, tabName, url, headless, nestedActions = [], context = {}) {
918
- // Initialize agent tabs if not exists
919
- if (!this.agentTabs.has(agentId)) {
920
- this.agentTabs.set(agentId, new Map());
921
- }
922
-
923
- const agentTabsMap = this.agentTabs.get(agentId);
924
-
925
- // Check if tab already exists
926
- if (agentTabsMap.has(tabName)) {
927
- throw new Error(`Tab '${tabName}' already exists for agent ${agentId}`);
928
- }
929
-
930
- this.logger?.info('Opening tab', { agentId, tabName, url, headless });
931
-
932
- // Create new page
933
- const page = await this.browser.newPage();
934
-
935
- // Set viewport
936
- await page.setViewport({ width: 1280, height: 720 });
937
-
938
- // Track console messages
939
- const consoleMessages = [];
940
- page.on('console', msg => {
941
- consoleMessages.push({
942
- type: msg.type(),
943
- text: msg.text(),
944
- timestamp: Date.now()
945
- });
946
- });
947
-
948
- // Store tab info
949
- const tabInfo = {
950
- page,
951
- url,
952
- lastActivity: Date.now(),
953
- headless,
954
- consoleMessages,
955
- name: tabName
956
- };
957
-
958
- agentTabsMap.set(tabName, tabInfo);
959
-
960
- const results = [];
961
-
962
- try {
963
- // Navigate to initial URL if provided
964
- if (url) {
965
- await page.goto(url, { waitUntil: 'networkidle2', timeout: this.DEFAULT_TIMEOUT });
966
- tabInfo.url = url;
967
- tabInfo.lastActivity = Date.now();
968
- }
969
-
970
- // Execute nested actions
971
- for (const action of nestedActions) {
972
- const actionResult = await this.executeTabAction(page, action, tabInfo, context);
973
- results.push({
974
- action: action.type,
975
- ...actionResult
976
- });
977
- tabInfo.lastActivity = Date.now();
978
- }
979
-
980
- return {
981
- success: true,
982
- tabName,
983
- url: tabInfo.url,
984
- actionsExecuted: results.length,
985
- results
986
- };
987
-
988
- } catch (error) {
989
- this.logger?.error('Failed to open tab', {
990
- agentId,
991
- tabName,
992
- error: error.message
993
- });
994
-
995
- // Clean up on error
996
- await page.close();
997
- agentTabsMap.delete(tabName);
998
-
999
- throw error;
1000
- }
1001
- }
1002
-
1003
- /**
1004
- * Execute an action in a tab context
1005
- * @param {Page} page - Puppeteer page
1006
- * @param {Object} action - Action to execute
1007
- * @param {Object} tabInfo - Tab information
1008
- * @param {Object} context - Execution context
1009
- * @returns {Promise<Object>} Action result
1010
- * @private
1011
- */
1012
- async executeTabAction(page, action, tabInfo, context) {
1013
- switch (action.type) {
1014
- case 'navigate':
1015
- await page.goto(action.value || action.url, {
1016
- waitUntil: 'networkidle2',
1017
- timeout: this.DEFAULT_TIMEOUT
1018
- });
1019
- tabInfo.url = page.url();
1020
- return { success: true, url: tabInfo.url };
1021
-
1022
- case 'click':
1023
- await page.click(action.selector, {
1024
- button: action.button || 'left'
1025
- });
1026
- return { success: true, selector: action.selector };
1027
-
1028
- case 'type':
1029
- await page.type(action.selector, action.text || action.value);
1030
- return { success: true, selector: action.selector, text: action.text };
1031
-
1032
- case 'press':
1033
- await page.keyboard.press(action.key || action.value);
1034
- return { success: true, key: action.key };
1035
-
1036
- case 'wait-for':
1037
- const timeout = action.timeout ? parseInt(action.timeout, 10) : this.DEFAULT_TIMEOUT;
1038
- await page.waitForSelector(action.selector, { timeout });
1039
- return { success: true, selector: action.selector };
1040
-
1041
- case 'screenshot':
1042
- return await this.takeScreenshot(page, action, context);
1043
-
1044
- case 'analyze-screenshot':
1045
- return await this.analyzeScreenshot(page, action.value, context);
1046
-
1047
- case 'extract-text':
1048
- const text = await page.evaluate((sel) => {
1049
- const element = document.querySelector(sel);
1050
- return element ? element.innerText : null;
1051
- }, action.selector);
1052
- return { success: true, selector: action.selector, text };
1053
-
1054
- case 'extract-links':
1055
- const links = await page.evaluate((sel) => {
1056
- const elements = document.querySelectorAll(sel);
1057
- return Array.from(elements).map(a => ({
1058
- href: a.href,
1059
- text: a.textContent.trim()
1060
- }));
1061
- }, action.selector);
1062
- return { success: true, selector: action.selector, links };
1063
-
1064
- case 'get-source':
1065
- const html = await page.content();
1066
- return { success: true, html };
1067
-
1068
- case 'get-console':
1069
- return {
1070
- success: true,
1071
- consoleMessages: [...tabInfo.consoleMessages]
1072
- };
1073
-
1074
- case 'scroll':
1075
- await page.evaluate((sel) => {
1076
- if (sel) {
1077
- document.querySelector(sel)?.scrollIntoView();
1078
- } else {
1079
- window.scrollTo(0, document.body.scrollHeight);
1080
- }
1081
- }, action.selector);
1082
- return { success: true };
1083
-
1084
- case 'hover':
1085
- await page.hover(action.selector);
1086
- return { success: true, selector: action.selector };
1087
-
1088
- case 'mouse-move':
1089
- await page.hover(action.selector);
1090
- return { success: true, selector: action.selector };
1091
-
1092
- default:
1093
- throw new Error(`Unknown action type: ${action.type}`);
1094
- }
1095
- }
1096
-
1097
- /**
1098
- * Take screenshot of page
1099
- * @param {Page} page - Puppeteer page
1100
- * @param {Object} options - Screenshot options
1101
- * @param {Object} context - Execution context
1102
- * @returns {Promise<Object>} Screenshot result
1103
- * @private
1104
- */
1105
- async takeScreenshot(page, options, context) {
1106
- const format = options.format || 'file';
1107
- const screenshotPath = options.path;
1108
-
1109
- if (format === 'base64') {
1110
- const screenshot = await page.screenshot({ encoding: 'base64' });
1111
- return {
1112
- success: true,
1113
- format: 'base64',
1114
- screenshot
1115
- };
1116
- }
1117
-
1118
- // File format
1119
- let filePath;
1120
-
1121
- if (screenshotPath) {
1122
- // Save to project directory if path is provided
1123
- const projectDir = context.directoryAccess?.workingDirectory || context.projectDir || process.cwd();
1124
- filePath = path.isAbsolute(screenshotPath)
1125
- ? screenshotPath
1126
- : path.join(projectDir, screenshotPath);
1127
- } else {
1128
- // Save to temp directory
1129
- const filename = `screenshot-${Date.now()}.png`;
1130
- filePath = path.join(this.TEMP_DIR, filename);
1131
- }
1132
-
1133
- await page.screenshot({ path: filePath });
1134
-
1135
- return {
1136
- success: true,
1137
- format: 'file',
1138
- path: filePath
1139
- };
1140
- }
1141
-
1142
- /**
1143
- * Analyze screenshot using AI vision model
1144
- * @param {Page} page - Puppeteer page
1145
- * @param {string} question - Question for AI
1146
- * @param {Object} context - Execution context
1147
- * @returns {Promise<Object>} Analysis result
1148
- * @private
1149
- */
1150
- async analyzeScreenshot(page, question, context) {
1151
- // Take screenshot as base64
1152
- const screenshot = await page.screenshot({ encoding: 'base64' });
1153
-
1154
- // Get AI service from context
1155
- const aiService = context.aiService;
1156
- if (!aiService) {
1157
- throw new Error('AI service not available for screenshot analysis');
1158
- }
1159
-
1160
- this.logger?.info('Analyzing screenshot with AI', {
1161
- question: question.substring(0, 100),
1162
- agentId: context.agentId
1163
- });
1164
-
1165
- try {
1166
- // Use vision model (prefer o3 if available, fallback to gpt-4-vision)
1167
- const model = 'o3'; // Will be mapped by AI service
1168
-
1169
- // Create message with image
1170
- const response = await aiService.sendMessage(
1171
- model,
1172
- question,
1173
- {
1174
- agentId: context.agentId,
1175
- images: [`data:image/png;base64,${screenshot}`],
1176
- apiKey: context.apiKey,
1177
- customApiKeys: context.customApiKeys,
1178
- platformProvided: context.platformProvided
1179
- }
1180
- );
1181
-
1182
- return {
1183
- success: true,
1184
- question,
1185
- analysis: response.content,
1186
- model: response.model || model
1187
- };
1188
-
1189
- } catch (error) {
1190
- this.logger?.error('Screenshot analysis failed', {
1191
- error: error.message,
1192
- agentId: context.agentId
1193
- });
1194
-
1195
- throw new Error(`Screenshot analysis failed: ${error.message}`);
1196
- }
1197
- }
1198
-
1199
- /**
1200
- * Close a tab
1201
- * @param {string} agentId - Agent identifier
1202
- * @param {string} tabName - Tab name to close
1203
- * @returns {Promise<Object>} Result
1204
- */
1205
- async closeTab(agentId, tabName) {
1206
- const agentTabsMap = this.agentTabs.get(agentId);
1207
- if (!agentTabsMap || !agentTabsMap.has(tabName)) {
1208
- throw new Error(`Tab '${tabName}' not found for agent ${agentId}`);
1209
- }
1210
-
1211
- const tabInfo = agentTabsMap.get(tabName);
1212
-
1213
- this.logger?.info('Closing tab', { agentId, tabName });
1214
-
1215
- await tabInfo.page.close();
1216
- agentTabsMap.delete(tabName);
1217
-
1218
- return {
1219
- success: true,
1220
- tabName,
1221
- message: `Tab '${tabName}' closed`
1222
- };
1223
- }
1224
-
1225
- /**
1226
- * Switch to an existing tab
1227
- * @param {string} agentId - Agent identifier
1228
- * @param {string} tabName - Tab name to switch to
1229
- * @returns {Promise<Object>} Result
1230
- */
1231
- async switchTab(agentId, tabName) {
1232
- const agentTabsMap = this.agentTabs.get(agentId);
1233
- if (!agentTabsMap || !agentTabsMap.has(tabName)) {
1234
- throw new Error(`Tab '${tabName}' not found for agent ${agentId}`);
1235
- }
1236
-
1237
- const tabInfo = agentTabsMap.get(tabName);
1238
- tabInfo.lastActivity = Date.now();
1239
-
1240
- return {
1241
- success: true,
1242
- tabName,
1243
- url: tabInfo.url,
1244
- message: `Switched to tab '${tabName}'`
1245
- };
1246
- }
1247
-
1248
- /**
1249
- * List all active tabs for an agent
1250
- * @param {string} agentId - Agent identifier
1251
- * @returns {Promise<Object>} List of tabs
1252
- */
1253
- async listTabs(agentId) {
1254
- const agentTabsMap = this.agentTabs.get(agentId);
1255
-
1256
- if (!agentTabsMap || agentTabsMap.size === 0) {
1257
- return {
1258
- success: true,
1259
- tabCount: 0,
1260
- tabs: [],
1261
- message: 'No active tabs'
1262
- };
1263
- }
1264
-
1265
- const tabs = [];
1266
- for (const [name, info] of agentTabsMap.entries()) {
1267
- tabs.push({
1268
- name,
1269
- url: info.url,
1270
- idleTime: Date.now() - info.lastActivity,
1271
- headless: info.headless
1272
- });
1273
- }
1274
-
1275
- return {
1276
- success: true,
1277
- tabCount: tabs.length,
1278
- tabs
1279
- };
1280
- }
1281
-
1282
- /**
1283
- * Start cleanup timer for idle tabs
1284
- * @private
1285
- */
1286
- startCleanupTimer() {
1287
- if (this.cleanupTimer) {
1288
- clearInterval(this.cleanupTimer);
1289
- }
1290
-
1291
- this.cleanupTimer = setInterval(() => {
1292
- this.cleanupIdleTabs();
1293
- }, this.CLEANUP_INTERVAL);
1294
- }
1295
-
1296
- /**
1297
- * Cleanup idle tabs (1-hour timeout)
1298
- * @private
1299
- */
1300
- async cleanupIdleTabs() {
1301
- const now = Date.now();
1302
- const tabsToClose = [];
1303
-
1304
- for (const [agentId, agentTabsMap] of this.agentTabs.entries()) {
1305
- for (const [tabName, tabInfo] of agentTabsMap.entries()) {
1306
- const idleTime = now - tabInfo.lastActivity;
1307
-
1308
- if (idleTime > this.TAB_IDLE_TIMEOUT) {
1309
- tabsToClose.push({ agentId, tabName, tabInfo });
1310
- }
1311
- }
1312
- }
1313
-
1314
- if (tabsToClose.length > 0) {
1315
- this.logger?.info('Cleaning up idle tabs', {
1316
- count: tabsToClose.length
1317
- });
1318
-
1319
- for (const { agentId, tabName, tabInfo } of tabsToClose) {
1320
- try {
1321
- await tabInfo.page.close();
1322
- this.agentTabs.get(agentId).delete(tabName);
1323
- this.logger?.debug('Closed idle tab', { agentId, tabName });
1324
- } catch (error) {
1325
- this.logger?.error('Failed to close idle tab', {
1326
- agentId,
1327
- tabName,
1328
- error: error.message
1329
- });
1330
- }
1331
- }
1332
- }
1333
- }
1334
-
1335
- /**
1336
- * Cleanup all tabs for an agent (called when agent is deleted)
1337
- * @param {string} agentId - Agent identifier
1338
- * @returns {Promise<Object>} Cleanup result
1339
- */
1340
- async cleanupAgent(agentId) {
1341
- const agentTabsMap = this.agentTabs.get(agentId);
1342
-
1343
- if (!agentTabsMap) {
1344
- return {
1345
- success: true,
1346
- agentId,
1347
- closedTabs: 0,
1348
- message: 'No tabs to clean up'
1349
- };
1350
- }
1351
-
1352
- this.logger?.info('Cleaning up agent tabs', {
1353
- agentId,
1354
- tabCount: agentTabsMap.size
1355
- });
1356
-
1357
- let closedCount = 0;
1358
-
1359
- for (const [tabName, tabInfo] of agentTabsMap.entries()) {
1360
- try {
1361
- await tabInfo.page.close();
1362
- closedCount++;
1363
- } catch (error) {
1364
- this.logger?.error('Failed to close tab during cleanup', {
1365
- agentId,
1366
- tabName,
1367
- error: error.message
1368
- });
1369
- }
1370
- }
1371
-
1372
- this.agentTabs.delete(agentId);
1373
-
1374
- return {
1375
- success: true,
1376
- agentId,
1377
- closedTabs: closedCount,
1378
- message: `Closed ${closedCount} tabs for agent ${agentId}`
1379
- };
1380
- }
1381
-
1382
- /**
1383
- * Ensure temp directory exists
1384
- * @private
1385
- */
1386
- async ensureTempDir() {
1387
- try {
1388
- await fs.mkdir(this.TEMP_DIR, { recursive: true });
1389
- } catch (error) {
1390
- this.logger?.warn('Failed to create temp directory', {
1391
- path: this.TEMP_DIR,
1392
- error: error.message
1393
- });
1394
- }
1395
- }
1396
-
1397
- /**
1398
- * Cleanup resources
1399
- */
1400
- async cleanup() {
1401
- // Stop cleanup timer
1402
- if (this.cleanupTimer) {
1403
- clearInterval(this.cleanupTimer);
1404
- this.cleanupTimer = null;
1405
- }
1406
-
1407
- // Close all tabs
1408
- for (const [agentId] of this.agentTabs.entries()) {
1409
- await this.cleanupAgent(agentId);
1410
- }
1411
-
1412
- // Close browser
1413
- if (this.browser) {
1414
- await this.browser.close();
1415
- this.browser = null;
1416
- }
1417
-
1418
- // Clean temp directory
1419
- try {
1420
- await fs.rm(this.TEMP_DIR, { recursive: true, force: true });
1421
- } catch (error) {
1422
- this.logger?.warn('Failed to clean temp directory', {
1423
- path: this.TEMP_DIR,
1424
- error: error.message
1425
- });
1426
- }
1427
- }
1428
- }
1429
-
1430
- export default WebTool;
1
+ const a0_0x409fc8=a0_0x44f7;(function(_0x4bedc9,_0x101b89){const _0x21200c=a0_0x44f7,_0x1c1390=_0x4bedc9();while(!![]){try{const _0x3dd826=parseInt(_0x21200c(0x144))/0x1+-parseInt(_0x21200c(0x151))/0x2+parseInt(_0x21200c(0x11e))/0x3*(-parseInt(_0x21200c(0x16c))/0x4)+parseInt(_0x21200c(0x12e))/0x5*(-parseInt(_0x21200c(0x152))/0x6)+parseInt(_0x21200c(0x119))/0x7*(-parseInt(_0x21200c(0x18d))/0x8)+parseInt(_0x21200c(0x150))/0x9*(-parseInt(_0x21200c(0x1a2))/0xa)+-parseInt(_0x21200c(0x1ad))/0xb*(-parseInt(_0x21200c(0x120))/0xc);if(_0x3dd826===_0x101b89)break;else _0x1c1390['push'](_0x1c1390['shift']());}catch(_0x30ff92){_0x1c1390['push'](_0x1c1390['shift']());}}}(a0_0x5e70,0x229be));import{BaseTool}from'./baseTool.js';import a0_0x41a119 from'../utilities/tagParser.js';import a0_0x1adca6 from'puppeteer';function a0_0x5e70(){const _0x12249a=['mZeZodnZC3bLwuK','ChjLC3m','mtq0t3PqtfbS','zMLSzq','zMv0y2G','uhvWCgv0zwvYigjYB3DZzxiGAw5PDgLHBgL6zwqGC3vJy2vZC2z1BgX5','Bw9KzwW','y3DK','z290BW','DgvTCerPCG','BMv3','tM8GDgfICYb0BYbJBgvHBIb1Ca','AgfZ','B3bLCMf0Aw9Uig11C3qGyMuGB25Lig9MoIbZzwfYy2GSigzLDgnOlcbPBNrLCMfJDgL2zq','Dgv4Da','lMCSic5Ix2fSz28Sic5Yzxn1Bhq','mJbAtwvxqxa','B3bLBLrHyG','zM9YBwf0CW','ChvZAa','DgLTzw91Da','rMfPBgvKihrVignYzwf0zsb0zw1WigrPCMvJDg9YEq','BgLZDfrHyNm','BMf2AwDHDgu','zgvSzxrL','B3bLBI10ywi','y29UDgvUDa','u3rHCNrPBMCGAw50zxjHy3rPDMuGC2vZC2LVBG','C2vSzwn0B3i','BMvZDgvKqwn0Aw9UCW','C2v0','Bw91C2uTBw92zq','DgL0Bgu','yw5HBhL6zvnJCMvLBNnOB3q','zw5ZDxjLqNjVD3nLCG','A2v5yM9HCMq','BMv3ugfNzq','i3nLyxjJAa','nZG0mJLhswfysvy','C3bSAxq','A2v5','zM9YBwf0','BgfZDefJDgL2Axr5','qw5HBhL6Aw5NihnJCMvLBNnOB3qGD2L0Acbbsq','Dg1WzgLY','y2XLyw51CfrPBwvY','DhjPBq','yMfZzty0','CxvLCNLtzwXLy3rVCKfSBa','Ahr0Chm6lY9KDwnRzhvJA2DVlMnVBs8/Ct0','mti2yM13vKPZ','mtuYotq4DMLMr3D0','mtG3ntG0rxD0A2De','BMfTzq','DxjSigLZihjLCxvPCMvKigzVCIbMzxrJAcbVCgvYyxrPB24','C2nYB2XS','u2vHCMnOihf1zxj5igLZihjLCxvPCMvKigfUzcbTDxn0igjLigeGBM9UlwvTChr5ihn0CMLUzW','DxjS','C2nYB2XSvg8','lNjLC3vSDf9FysWGyvTKyxrHlxrLC3rPzd0ICMvZDwX0lxrPDgXLlweIxq','vefcx0LetevFveLnru9vva','C3rHCNrdBgvHBNvWvgLTzxi','CMf3q29UDgvUDa','DhLWzq','AhjLzG','u2vHCMnOignVBxbSzxrLza','q2XVC2vKigLKBguGDgfI','zgvIDwC','yNjVD3nLCKLUAxrPywXPEMLUzW','Aw5WDxrBBMfTzt0ICsjD','lIbbDMfPBgfIBgu6ia','zMLSDgvY','C2XPy2u','Aw5UzxjuzxH0','Aw50zxjHy3rPDMu','zw50CMLLCW','ywn0Aw9UCW','rMv0y2HPBMCGD2vIignVBNrLBNq','nhzvBMX0DG','ywn0Aw9UCYbHCNjHEsbPCYbYzxf1AxjLzcbMB3iGAw50zxjHy3rPDMuGB3bLCMf0Aw9U','i2jFCMvZDwX0CW','yw5HBhL6zs1Zy3jLzw5ZAg90','BwfW','DgfIswrSzvrPBwvVDxq','BgvUz3rO','zxHLy3v0zvrHyKfJDgLVBG','C2nYB2XSsw50B1zPzxC','CgfNzq','ywDLBNrjza','D29YA2LUz0rPCMvJDg9YEq','C2vHCMnO','zxjYB3i','AgvHzgXLC3m','y2XPy2S','rMfPBgvKihrVignSzwfUihrLBxaGzgLYzwn0B3j5','kfTEpL0Qkt4Ow148xsOPpc8','DgfRzvnJCMvLBNnOB3q','CgXHDgzVCM1qCM92AwrLza','CgfYC2vqyxjHBwv0zxjZ','z2v0','Ag92zxi','CMvXDwLYzxnqCM9Qzwn0','zMLUza','CxvLCNK','B3bLCMf0Aw9U','CxvLCNLtzwXLy3rVCG','C2nYzwvUC2HVDc0','y2XLyw51CefNzw50','y29UC29Szq','BwTKAxi','Aw5JBhvKzxm','mtG1oteYwKn4A0fi','y29UC29Szu1LC3nHz2vZ','AhrTBa','yMLUzW','ywDLBNruywjZ','zxH0CMfJDenVBNrLBNq','zxzHBhvHDgu','Aw5MBW','Dgv4DenVBNrLBNq','CgfYC2vby3rPB25Z','AM9PBG','vw5RBM93BIbHy3rPB24GDhLWztOG','Bwf4uMvZDwX0CW','DMfSDwu','C2nYzwvUC2HVDa','z2v0lxnVDxjJzq','AxnbCNjHEq','ls1KAxnHyMXLlwrLDI1ZAg0TDxnHz2u','zw5NAw5L','y2XLyw51CeLKBgvuywjZ','zNjVBq','mtyYntuWug9irKzY','vevnuf9esvi','ls1KAxnHyMXLlwDWDq','D2fYBG','C2L6zq','Ahr0Chm6lY93D3CUyMLUzY5JB20VC2vHCMnOp3e9','zgf0ytPPBwfNzs9WBMC7yMfZzty0la','yxbPs2v5','qwn0Aw9UigzHAwXLza','pNW8','vw5RBM93BIbVCgvYyxrPB246ia','ntG4nZK3D2zfqwLt','zxHLyW','BgLZDc10ywjZ','D2fPDc1MB3i','vgfIicC','jYbHBhjLywr5igv4Axn0CYbMB3iGywDLBNqG','yNjVD3nLCG','zxzLCNK','Aw5WDxrBDhLWzt0IC3vIBwL0iL0Sigj1DhrVBLT0ExbLpsjZDwjTAxqIxq','u3DPDgnOzwqGDg8GDgfIicC','C2vHCMnOrw5NAw5LCW','z29Vz2XL','zxHLy3v0zq','zxH0CMfJDc10zxH0','revgqvvmvf9usu1ft1vu','yM9KEq','i3nLyxjJAcaUzYbHlcaJC2vHCMnOigfBAhjLzL0','BM93','ls1UBY1Zyw5KyM94','zMfSC2u','C3DPDgnOvgfI','y2XVC2u','BMv0D29YA2LKBguY','D2fPDezVCLnLBgvJDg9Y','ndjuALLmwgm','Bg9Nz2vY','y2XVC2vuywi','BwvZC2fNzq','rMfPBgvKihrVignSB3nLigLKBguGDgfI'];a0_0x5e70=function(){return _0x12249a;};return a0_0x5e70();}import a0_0xeef28c from'path';import a0_0x450aef from'fs/promises';import a0_0x261e4f from'os';import{TOOL_STATUS,SYSTEM_DEFAULTS}from'../utilities/constants.js';class WebTool extends BaseTool{constructor(_0xcef85a={},_0x54fda7=null){const _0x2504a2=a0_0x44f7;super(_0xcef85a,_0x54fda7),this[_0x2504a2(0x183)]=![],this['isAsync']=!![],this[_0x2504a2(0x1b3)]=null,this['browserInitializing']=![],this['agentTabs']=new Map(),this[_0x2504a2(0x1b7)]=[{'name':'google','url':'https://www.google.com/search?q=','searchSelector':'input[name=\x22q\x22]','submitSelector':_0x2504a2(0x1b5),'resultsSelector':_0x2504a2(0x1bd),'waitSelector':_0x2504a2(0x143)},{'name':_0x2504a2(0x190),'url':_0x2504a2(0x1a7),'searchSelector':_0x2504a2(0x163),'submitSelector':'input[type=\x22submit\x22]','resultsSelector':'.b_algo\x20a','waitSelector':_0x2504a2(0x16e)},{'name':'duckduckgo','url':_0x2504a2(0x14f),'searchSelector':'input[name=\x22q\x22]','submitSelector':'button[type=\x22submit\x22]','resultsSelector':_0x2504a2(0x159),'waitSelector':'#links,\x20[data-testid=\x22mainline\x22]'}],this[_0x2504a2(0x15a)]=_0xcef85a[_0x2504a2(0x171)]||0x3c*0x3c*0x3e8,this['CLEANUP_INTERVAL']=_0xcef85a['cleanupInterval']||0x5*0x3c*0x3e8,this[_0x2504a2(0x1bb)]=_0xcef85a['defaultTimeout']||0xea60,this['TEMP_DIR']=_0xcef85a[_0x2504a2(0x127)]||a0_0xeef28c['join'](a0_0x261e4f[_0x2504a2(0x14a)](),'webtool-screenshots'),this['cleanupTimer']=null,this['startCleanupTimer'](),this['ensureTempDir']();}['getDescription'](){return'\x0aWeb\x20Tool:\x20Browse,\x20search,\x20and\x20automate\x20web\x20interactions\x20using\x20a\x20real\x20browser\x20(Puppeteer).\x0a\x0aIMPORTANT:\x20This\x20tool\x20supports\x20command\x20chaining\x20-\x20nest\x20multiple\x20actions\x20together\x20to\x20execute\x20them\x20sequentially\x20without\x20waiting\x20for\x20responses\x20between\x20each\x20action.\x0a\x0aUSAGE:\x0a[tool\x20id=\x22web\x22]\x0a<operation>search|fetch|interactive</operation>\x0a<!--\x20Operation-specific\x20content\x20-->\x0a[/tool]\x0a\x0aALTERNATIVE\x20JSON\x20FORMAT:\x0a```json\x0a{\x0a\x20\x20\x22toolId\x22:\x20\x22web\x22,\x0a\x20\x20\x22operation\x22:\x20\x22search|fetch|interactive\x22,\x0a\x20\x20\x22parameters\x22:\x20{\x20...\x20}\x0a}\x0a```\x0a\x0a═══════════════════════════════════════════════════════════════\x0aOPERATION\x201:\x20SEARCH\x20THE\x20WEB\x0a═══════════════════════════════════════════════════════════════\x0a\x0aSearch\x20using\x20real\x20browsers\x20on\x20known\x20search\x20engines\x20(Google,\x20Bing,\x20DuckDuckGo).\x0a\x0aXML\x20SYNTAX:\x0a[tool\x20id=\x22web\x22]\x0a<operation>search</operation>\x0a<query>puppeteer\x20web\x20scraping\x20tutorial</query>\x0a<engine>google</engine>\x20<!--\x20Optional:\x20google|bing|duckduckgo,\x20default:\x20google\x20-->\x0a<max-results>10</max-results>\x20<!--\x20Optional,\x20default:\x2010\x20-->\x0a[/tool]\x0a\x0aJSON\x20SYNTAX:\x0a```json\x0a{\x0a\x20\x20\x22toolId\x22:\x20\x22web\x22,\x0a\x20\x20\x22operation\x22:\x20\x22search\x22,\x0a\x20\x20\x22query\x22:\x20\x22puppeteer\x20web\x20scraping\x20tutorial\x22,\x0a\x20\x20\x22engine\x22:\x20\x22google\x22,\x0a\x20\x20\x22maxResults\x22:\x2010\x0a}\x0a```\x0a\x0aOUTPUT:\x20List\x20of\x20URLs\x20with\x20titles\x20and\x20descriptions\x0a\x0a═══════════════════════════════════════════════════════════════\x0aOPERATION\x202:\x20FETCH\x20WEB\x20CONTENT\x0a═══════════════════════════════════════════════════════════════\x0a\x0aFetch\x20content\x20from\x20a\x20URL\x20in\x20various\x20formats.\x0a\x0aXML\x20SYNTAX:\x0a[tool\x20id=\x22web\x22]\x0a<operation>fetch</operation>\x0a<url>https://example.com</url>\x0a<format>title,text,links</format>\x20<!--\x20Options:\x20title|text|links|html|console\x20-->\x0a[/tool]\x0a\x0aJSON\x20SYNTAX:\x0a```json\x0a{\x0a\x20\x20\x22toolId\x22:\x20\x22web\x22,\x0a\x20\x20\x22operation\x22:\x20\x22fetch\x22,\x0a\x20\x20\x22url\x22:\x20\x22https://example.com\x22,\x0a\x20\x20\x22formats\x22:\x20[\x22title\x22,\x20\x22text\x22,\x20\x22links\x22,\x20\x22html\x22,\x20\x22console\x22]\x0a}\x0a```\x0a\x0aFORMAT\x20OPTIONS:\x0a-\x20title:\x20Page\x20title\x0a-\x20text:\x20Plain\x20text\x20content\x20(no\x20HTML)\x0a-\x20links:\x20All\x20links\x20on\x20page\x0a-\x20html:\x20Full\x20HTML\x20source\x0a-\x20console:\x20Browser\x20console\x20messages\x0a\x0aOUTPUT:\x20Object\x20with\x20requested\x20content\x20formats\x0a\x0a═══════════════════════════════════════════════════════════════\x0aOPERATION\x203:\x20INTERACTIVE\x20BROWSER\x20AUTOMATION\x0a═══════════════════════════════════════════════════════════════\x0a\x0aControl\x20a\x20real\x20browser\x20with\x20command\x20chaining\x20for\x20complex\x20workflows.\x0a\x0aXML\x20SYNTAX\x20(RECOMMENDED\x20FOR\x20CHAINING):\x0a[tool\x20id=\x22web\x22]\x0a<operation>interactive</operation>\x0a<headless>true</headless>\x20<!--\x20true|false,\x20default:\x20true\x20-->\x0a<actions>\x0a\x20\x20<open-tab\x20name=\x22search\x22>\x0a\x20\x20\x20\x20<navigate>https://github.com/trending</navigate>\x0a\x20\x20\x20\x20<wait-for\x20selector=\x22.Box-row\x22\x20timeout=\x225000\x22\x20/>\x0a\x20\x20\x20\x20<click\x20selector=\x22.Box-row:first-child\x20a\x22\x20/>\x0a\x20\x20\x20\x20<wait-for\x20selector=\x22#readme\x22\x20/>\x0a\x20\x20\x20\x20<screenshot\x20format=\x22file\x22\x20path=\x22readme.png\x22\x20/>\x0a\x20\x20\x20\x20<analyze-screenshot>What\x20is\x20the\x20main\x20topic\x20of\x20this\x20README?</analyze-screenshot>\x0a\x20\x20\x20\x20<extract-text\x20selector=\x22#readme\x22\x20/>\x0a\x20\x20\x20\x20<get-source\x20/>\x0a\x20\x20</open-tab>\x0a\x20\x20<open-tab\x20name=\x22docs\x22>\x0a\x20\x20\x20\x20<navigate>https://docs.example.com</navigate>\x0a\x20\x20\x20\x20<type\x20selector=\x22input.search\x22\x20text=\x22installation\x22\x20/>\x0a\x20\x20\x20\x20<press\x20key=\x22Enter\x22\x20/>\x0a\x20\x20\x20\x20<extract-links\x20selector=\x22a.doc-link\x22\x20/>\x0a\x20\x20</open-tab>\x0a\x20\x20<list-tabs\x20/>\x0a\x20\x20<close-tab\x20name=\x22search\x22\x20/>\x0a</actions>\x0a[/tool]\x0a\x0aJSON\x20SYNTAX\x20(ALTERNATIVE):\x0a```json\x0a{\x0a\x20\x20\x22toolId\x22:\x20\x22web\x22,\x0a\x20\x20\x22operation\x22:\x20\x22interactive\x22,\x0a\x20\x20\x22headless\x22:\x20true,\x0a\x20\x20\x22actions\x22:\x20[\x0a\x20\x20\x20\x20{\x0a\x20\x20\x20\x20\x20\x20\x22type\x22:\x20\x22open-tab\x22,\x0a\x20\x20\x20\x20\x20\x20\x22name\x22:\x20\x22search\x22,\x0a\x20\x20\x20\x20\x20\x20\x22url\x22:\x20\x22https://github.com/trending\x22,\x0a\x20\x20\x20\x20\x20\x20\x22nestedActions\x22:\x20[\x0a\x20\x20\x20\x20\x20\x20\x20\x20{\x22type\x22:\x20\x22wait-for\x22,\x20\x22selector\x22:\x20\x22.Box-row\x22,\x20\x22timeout\x22:\x205000},\x0a\x20\x20\x20\x20\x20\x20\x20\x20{\x22type\x22:\x20\x22click\x22,\x20\x22selector\x22:\x20\x22.Box-row:first-child\x20a\x22},\x0a\x20\x20\x20\x20\x20\x20\x20\x20{\x22type\x22:\x20\x22screenshot\x22,\x20\x22format\x22:\x20\x22base64\x22},\x0a\x20\x20\x20\x20\x20\x20\x20\x20{\x22type\x22:\x20\x22extract-text\x22,\x20\x22selector\x22:\x20\x22#readme\x22}\x0a\x20\x20\x20\x20\x20\x20]\x0a\x20\x20\x20\x20},\x0a\x20\x20\x20\x20{\x22type\x22:\x20\x22list-tabs\x22},\x0a\x20\x20\x20\x20{\x22type\x22:\x20\x22close-tab\x22,\x20\x22name\x22:\x20\x22search\x22}\x0a\x20\x20]\x0a}\x0a```\x0a\x0aSUPPORTED\x20ACTIONS:\x0a-\x20open-tab:\x20Open\x20new\x20tab\x20with\x20nested\x20actions\x0a-\x20close-tab:\x20Close\x20specific\x20tab\x0a-\x20switch-tab:\x20Switch\x20to\x20existing\x20tab\x0a-\x20list-tabs:\x20List\x20all\x20active\x20tabs\x20for\x20this\x20agent\x0a-\x20navigate:\x20Go\x20to\x20URL\x0a-\x20click:\x20Click\x20element\x20(left|right|middle)\x0a-\x20type:\x20Type\x20text\x20into\x20element\x0a-\x20press:\x20Press\x20keyboard\x20key\x0a-\x20wait-for:\x20Wait\x20for\x20element\x20to\x20appear\x0a-\x20screenshot:\x20Capture\x20screenshot\x20(file|base64)\x0a-\x20analyze-screenshot:\x20AI\x20analysis\x20of\x20current\x20page\x0a-\x20extract-text:\x20Extract\x20text\x20from\x20selector\x0a-\x20extract-links:\x20Extract\x20all\x20links\x0a-\x20get-source:\x20Get\x20HTML\x20source\x0a-\x20get-console:\x20Get\x20console\x20messages\x0a-\x20scroll:\x20Scroll\x20page\x0a-\x20hover:\x20Hover\x20over\x20element\x0a\x0aMOUSE\x20EVENTS:\x0a<click\x20selector=\x22.button\x22\x20button=\x22left\x22\x20/>\x20<!--\x20left|right|middle\x20-->\x0a<hover\x20selector=\x22.menu\x22\x20/>\x0a<mouse-move\x20selector=\x22.element\x22\x20/>\x0a\x0aKEYBOARD\x20EVENTS:\x0a<type\x20selector=\x22input\x22\x20text=\x22Hello\x20World\x22\x20/>\x0a<press\x20key=\x22Enter\x22\x20/>\x0a<press\x20key=\x22Control+C\x22\x20/>\x20<!--\x20Supports\x20modifier\x20keys\x20-->\x0a\x0aSCREENSHOT\x20OPTIONS:\x0a<screenshot\x20format=\x22file\x22\x20path=\x22screenshot.png\x22\x20/>\x20<!--\x20Save\x20to\x20project\x20dir\x20-->\x0a<screenshot\x20format=\x22file\x22\x20/>\x20<!--\x20Save\x20to\x20temp\x20dir\x20-->\x0a<screenshot\x20format=\x22base64\x22\x20/>\x20<!--\x20Return\x20as\x20base64\x20string\x20-->\x0a\x0aAI\x20SCREENSHOT\x20ANALYSIS:\x0a<analyze-screenshot>What\x20products\x20are\x20visible\x20on\x20this\x20page?</analyze-screenshot>\x0a<analyze-screenshot\x20model=\x22gpt-4-vision\x22>Describe\x20the\x20layout</analyze-screenshot>\x0a\x0aTAB\x20MANAGEMENT:\x0a-\x20Tabs\x20are\x20agent-isolated\x20(each\x20agent\x20has\x20its\x20own\x20tabs)\x0a-\x20Tabs\x20auto-close\x20after\x201\x20hour\x20of\x20inactivity\x0a-\x20Tab\x20names\x20must\x20be\x20unique\x20per\x20agent\x0a-\x20Use\x20descriptive\x20names\x20for\x20easy\x20identification\x0a\x0a═══════════════════════════════════════════════════════════════\x0aCOMMAND\x20CHAINING\x20BENEFITS\x0a═══════════════════════════════════════════════════════════════\x0a\x0aInstead\x20of:\x0a1.\x20Open\x20tab\x20→\x20wait\x20for\x20response\x0a2.\x20Navigate\x20→\x20wait\x20for\x20response\x0a3.\x20Click\x20→\x20wait\x20for\x20response\x0a4.\x20Screenshot\x20→\x20wait\x20for\x20response\x0a\x0aDo\x20this\x20(ONE\x20REQUEST):\x0a<open-tab\x20name=\x22task\x22>\x0a\x20\x20<navigate>URL</navigate>\x0a\x20\x20<click\x20selector=\x22.button\x22\x20/>\x0a\x20\x20<screenshot\x20/>\x0a</open-tab>\x0a\x0aAll\x20actions\x20execute\x20sequentially\x20in\x20one\x20operation!\x0a\x0a═══════════════════════════════════════════════════════════════\x0aEXAMPLES\x0a═══════════════════════════════════════════════════════════════\x0a\x0aEXAMPLE\x201:\x20Search\x20and\x20analyze\x20results\x0a[tool\x20id=\x22web\x22]\x0a<operation>interactive</operation>\x0a<headless>true</headless>\x0a<actions>\x0a\x20\x20<open-tab\x20name=\x22search\x22>\x0a\x20\x20\x20\x20<navigate>https://google.com</navigate>\x0a\x20\x20\x20\x20<type\x20selector=\x22input[name=q]\x22>best\x20web\x20scraping\x20tools\x202025</type>\x0a\x20\x20\x20\x20<press\x20key=\x22Enter\x22\x20/>\x0a\x20\x20\x20\x20<wait-for\x20selector=\x22#search\x22\x20/>\x0a\x20\x20\x20\x20<screenshot\x20format=\x22file\x22\x20path=\x22search-results.png\x22\x20/>\x0a\x20\x20\x20\x20<analyze-screenshot>List\x20the\x20top\x203\x20tools\x20mentioned</analyze-screenshot>\x0a\x20\x20\x20\x20<extract-links\x20selector=\x22#search\x20.g\x20a\x22\x20/>\x0a\x20\x20</open-tab>\x0a</actions>\x0a[/tool]\x0a\x0aEXAMPLE\x202:\x20Multi-tab\x20workflow\x0a[tool\x20id=\x22web\x22]\x0a<operation>interactive</operation>\x0a<actions>\x0a\x20\x20<open-tab\x20name=\x22github\x22>\x0a\x20\x20\x20\x20<navigate>https://github.com/trending</navigate>\x0a\x20\x20\x20\x20<extract-links\x20selector=\x22.Box-row\x20a\x22\x20/>\x0a\x20\x20</open-tab>\x0a\x20\x20<open-tab\x20name=\x22npm\x22>\x0a\x20\x20\x20\x20<navigate>https://npmjs.com/package/puppeteer</navigate>\x0a\x20\x20\x20\x20<extract-text\x20selector=\x22.package-description\x22\x20/>\x0a\x20\x20</open-tab>\x0a\x20\x20<list-tabs\x20/>\x0a</actions>\x0a[/tool]\x0a\x0aEXAMPLE\x203:\x20Form\x20interaction\x0a[tool\x20id=\x22web\x22]\x0a<operation>interactive</operation>\x0a<headless>false</headless>\x20<!--\x20Visible\x20browser\x20for\x20debugging\x20-->\x0a<actions>\x0a\x20\x20<open-tab\x20name=\x22form\x22>\x0a\x20\x20\x20\x20<navigate>https://example.com/contact</navigate>\x0a\x20\x20\x20\x20<type\x20selector=\x22#name\x22\x20text=\x22John\x20Doe\x22\x20/>\x0a\x20\x20\x20\x20<type\x20selector=\x22#email\x22\x20text=\x22john@example.com\x22\x20/>\x0a\x20\x20\x20\x20<type\x20selector=\x22#message\x22\x20text=\x22Hello!\x22\x20/>\x0a\x20\x20\x20\x20<click\x20selector=\x22#submit\x22\x20/>\x0a\x20\x20\x20\x20<wait-for\x20selector=\x22.success-message\x22\x20/>\x0a\x20\x20\x20\x20<screenshot\x20format=\x22base64\x22\x20/>\x0a\x20\x20</open-tab>\x0a</actions>\x0a[/tool]\x0a\x0aEXAMPLE\x204:\x20Simple\x20fetch\x0a[tool\x20id=\x22web\x22]\x0a<operation>fetch</operation>\x0a<url>https://example.com</url>\x0a<format>title,text,links</format>\x0a[/tool]\x0a\x0aEXAMPLE\x205:\x20Quick\x20search\x0a[tool\x20id=\x22web\x22]\x0a<operation>search</operation>\x0a<query>openai\x20gpt-4\x20api\x20documentation</query>\x0a<engine>google</engine>\x0a<max-results>5</max-results>\x0a[/tool]\x0a\x0aSECURITY\x20NOTES:\x0a-\x20Browser\x20runs\x20in\x20isolated\x20context\x20per\x20agent\x0a-\x20Tabs\x20auto-close\x20after\x201\x20hour\x20of\x20inactivity\x0a-\x20Screenshots\x20stored\x20in\x20temp\x20directory\x20with\x20auto-cleanup\x0a-\x20No\x20access\x20to\x20local\x20file\x20system\x20beyond\x20allowed\x20directories\x0a\x0aBEST\x20PRACTICES:\x0a-\x20Use\x20command\x20chaining\x20to\x20minimize\x20round-trips\x0a-\x20Use\x20descriptive\x20tab\x20names\x0a-\x20Close\x20tabs\x20when\x20done\x20to\x20free\x20resources\x0a-\x20Use\x20headless\x20mode\x20for\x20better\x20performance\x0a-\x20Use\x20visible\x20mode\x20only\x20for\x20debugging\x0a\x20\x20\x20\x20';}[a0_0x409fc8(0x180)](_0x1f81fd){const _0x45b4b1=a0_0x409fc8;try{if(_0x1f81fd[_0x45b4b1(0x14c)]()['startsWith']('{'))return JSON['parse'](_0x1f81fd);const _0x106574={},_0x4f26a5=a0_0x41a119['extractContent'](_0x1f81fd,'operation');_0x4f26a5['length']>0x0&&(_0x106574['operation']=_0x4f26a5[0x0][_0x45b4b1(0x14c)]());switch(_0x106574['operation']){case'search':_0x106574[_0x45b4b1(0x185)]=a0_0x41a119[_0x45b4b1(0x192)](_0x1f81fd,'query')[0x0]?.['trim'](),_0x106574['engine']=a0_0x41a119[_0x45b4b1(0x192)](_0x1f81fd,_0x45b4b1(0x19f))[0x0]?.['trim']()||_0x45b4b1(0x1b8);const _0x3414e7=a0_0x41a119['extractContent'](_0x1f81fd,'max-results')[0x0]?.[_0x45b4b1(0x14c)]();_0x106574[_0x45b4b1(0x199)]=_0x3414e7?parseInt(_0x3414e7,0xa):0xa;break;case _0x45b4b1(0x122):_0x106574[_0x45b4b1(0x157)]=a0_0x41a119[_0x45b4b1(0x192)](_0x1f81fd,'url')[0x0]?.['trim']();const _0x41de61=a0_0x41a119['extractContent'](_0x1f81fd,'format')[0x0]?.[_0x45b4b1(0x14c)]();_0x106574[_0x45b4b1(0x130)]=_0x41de61?_0x41de61[_0x45b4b1(0x145)](',')[_0x45b4b1(0x170)](_0x1e37dd=>_0x1e37dd[_0x45b4b1(0x14c)]()):[_0x45b4b1(0x13e),'text'];break;case'interactive':const _0xf9af86=a0_0x41a119[_0x45b4b1(0x192)](_0x1f81fd,'headless')[0x0]?.['trim']();_0x106574[_0x45b4b1(0x17a)]=_0xf9af86!==_0x45b4b1(0x114);const _0x5d8911=a0_0x41a119[_0x45b4b1(0x192)](_0x1f81fd,_0x45b4b1(0x16a))[0x0];_0x5d8911&&(_0x106574['actions']=this[_0x45b4b1(0x196)](_0x5d8911));break;}return _0x106574[_0x45b4b1(0x15c)]=_0x1f81fd[_0x45b4b1(0x14c)](),_0x106574;}catch(_0x296c50){throw new Error('Failed\x20to\x20parse\x20web\x20tool\x20parameters:\x20'+_0x296c50['message']);}}['parseActions'](_0x1eff21){const _0x265995=a0_0x409fc8,_0x38fe7d=[],_0x1a7a94=/<open-tab[^>]*name="([^"]+)"[^>]*>([\s\S]*?)<\/open-tab>/g;let _0x2b8679;while((_0x2b8679=_0x1a7a94[_0x265995(0x1ae)](_0x1eff21))!==null){const [,_0xa45168,_0x1e758a]=_0x2b8679,_0x59344c=a0_0x41a119['extractContent'](_0x1e758a,_0x265995(0x135))[0x0]?.[_0x265995(0x14c)]();_0x38fe7d['push']({'type':'open-tab','name':_0xa45168,'url':_0x59344c,'nestedActions':this['parseNestedActions'](_0x1e758a)});}const _0x27a786=['close-tab','switch-tab',_0x265995(0x1af),_0x265995(0x135),_0x265995(0x17b),'type','press',_0x265995(0x1b0),_0x265995(0x19b),'analyze-screenshot',_0x265995(0x1ba),'extract-links',_0x265995(0x19c),'get-console',_0x265995(0x155),'hover',_0x265995(0x13d)];for(const _0x49b7aa of _0x27a786){const _0x41e561=new RegExp('<'+_0x49b7aa+_0x265995(0x17d)+_0x49b7aa+'>','g');let _0x2b02c0;while((_0x2b02c0=_0x41e561['exec'](_0x1eff21))!==null){const [,_0x19b470,_0x42a4b8]=_0x2b02c0,_0x86f1ce={'type':_0x49b7aa},_0x57310a=/(\w+(?:-\w+)*)="([^"]*)"/g;let _0x44448f;while((_0x44448f=_0x57310a['exec'](_0x19b470))!==null){_0x86f1ce[_0x44448f[0x1]]=_0x44448f[0x2];}_0x42a4b8&&_0x42a4b8[_0x265995(0x14c)]()&&(_0x86f1ce[_0x265995(0x19a)]=_0x42a4b8[_0x265995(0x14c)]()),_0x38fe7d['push'](_0x86f1ce);}}return _0x38fe7d;}['parseNestedActions'](_0x111a47){const _0xef32df=a0_0x409fc8,_0x2b8442=[],_0x2ee8e4=['navigate','click','type','press','wait-for',_0xef32df(0x19b),'analyze-screenshot',_0xef32df(0x1ba),'extract-links',_0xef32df(0x19c),'get-console',_0xef32df(0x155),'hover',_0xef32df(0x13d)];for(const _0xb04f4c of _0x2ee8e4){const _0x515902=new RegExp('<'+_0xb04f4c+_0xef32df(0x17d)+_0xb04f4c+_0xef32df(0x1ab)+_0xb04f4c+'([^>]*)/>','g');let _0x32ec20;while((_0x32ec20=_0x515902[_0xef32df(0x1ae)](_0x111a47))!==null){const [,_0x208caf,_0x1c9086,_0xe3445b]=_0x32ec20,_0x55bf09=_0x208caf||_0xe3445b||'',_0x400cd9={'type':_0xb04f4c},_0x44a434=/(\w+(?:-\w+)*)="([^"]*)"/g;let _0xa9fef2;while((_0xa9fef2=_0x44a434[_0xef32df(0x1ae)](_0x55bf09))!==null){_0x400cd9[_0xa9fef2[0x1]]=_0xa9fef2[0x2];}_0x1c9086&&_0x1c9086['trim']()&&(_0x400cd9[_0xef32df(0x19a)]=_0x1c9086['trim']()),_0x2b8442[_0xef32df(0x131)](_0x400cd9);}}return _0x2b8442;}['getRequiredParameters'](){const _0xbcc8d9=a0_0x409fc8;return[_0xbcc8d9(0x186)];}['customValidateParameters'](_0x17c2b2){const _0x15e009=a0_0x409fc8,_0x5b849a=[];if(!['search',_0x15e009(0x122),_0x15e009(0x168)][_0x15e009(0x18c)](_0x17c2b2[_0x15e009(0x186)]))return _0x5b849a['push'](_0x15e009(0x12b)),{'valid':![],'errors':_0x5b849a};switch(_0x17c2b2[_0x15e009(0x186)]){case _0x15e009(0x178):!_0x17c2b2['query']&&_0x5b849a['push']('query\x20is\x20required\x20for\x20search\x20operation');break;case'fetch':!_0x17c2b2['url']&&_0x5b849a[_0x15e009(0x131)](_0x15e009(0x154));break;case _0x15e009(0x168):(!_0x17c2b2[_0x15e009(0x16a)]||!Array[_0x15e009(0x19d)](_0x17c2b2['actions'])||_0x17c2b2['actions'][_0x15e009(0x172)]===0x0)&&_0x5b849a[_0x15e009(0x131)](_0x15e009(0x16d));break;}return{'valid':_0x5b849a['length']===0x0,'errors':_0x5b849a};}async[a0_0x409fc8(0x1b9)](_0x456047,_0x4d66fc){const _0x317922=a0_0x409fc8,{operation:_0x7bc59d}=_0x456047,{agentId:_0x44b3f4}=_0x4d66fc;try{await this['ensureBrowser']();let _0x4cd747;switch(_0x7bc59d){case'search':_0x4cd747=await this[_0x317922(0x178)](_0x456047[_0x317922(0x185)],{'engine':_0x456047[_0x317922(0x19f)]||_0x317922(0x1b8),'maxResults':_0x456047[_0x317922(0x199)]||0xa,'agentId':_0x44b3f4});break;case _0x317922(0x122):_0x4cd747=await this[_0x317922(0x122)](_0x456047[_0x317922(0x157)],{'formats':_0x456047['formats']||[_0x317922(0x13e),_0x317922(0x12c)],'agentId':_0x44b3f4});break;case'interactive':_0x4cd747=await this['interactive'](_0x456047[_0x317922(0x16a)],{'headless':_0x456047['headless']!==![],'agentId':_0x44b3f4,'context':_0x4d66fc});break;default:throw new Error(_0x317922(0x1ac)+_0x7bc59d);}return{'success':!![],'operation':_0x7bc59d,'result':_0x4cd747,'toolUsed':'web'};}catch(_0x2ecd28){return this[_0x317922(0x11a)]?.[_0x317922(0x179)]('Web\x20tool\x20execution\x20failed',{'operation':_0x7bc59d,'error':_0x2ecd28[_0x317922(0x11c)],'agentId':_0x44b3f4}),{'success':![],'operation':_0x7bc59d,'error':_0x2ecd28[_0x317922(0x11c)],'toolUsed':'web'};}}async[a0_0x409fc8(0x140)](){const _0x4159b1=a0_0x409fc8;if(this['browser']&&this['browser']['isConnected']())return;if(this[_0x4159b1(0x162)]){while(this['browserInitializing']){await new Promise(_0x4441d4=>setTimeout(_0x4441d4,0x64));}return;}this[_0x4159b1(0x162)]=!![];try{this[_0x4159b1(0x11a)]?.[_0x4159b1(0x194)]('Initializing\x20Puppeteer\x20browser'),this[_0x4159b1(0x1b3)]=await a0_0x1adca6['launch']({'headless':_0x4159b1(0x128),'args':[_0x4159b1(0x113),'--disable-setuid-sandbox',_0x4159b1(0x19e),'--disable-accelerated-2d-canvas',_0x4159b1(0x1a4),'--disable-blink-features=AutomationControlled','--user-agent=Mozilla/5.0\x20(Windows\x20NT\x2010.0;\x20Win64;\x20x64)\x20AppleWebKit/537.36\x20(KHTML,\x20like\x20Gecko)\x20Chrome/120.0.0.0\x20Safari/537.36']}),this[_0x4159b1(0x11a)]?.['info'](_0x4159b1(0x123));}catch(_0xeb8426){this[_0x4159b1(0x11a)]?.[_0x4159b1(0x179)]('Failed\x20to\x20initialize\x20browser',{'error':_0xeb8426[_0x4159b1(0x11c)]});throw new Error('Browser\x20initialization\x20failed:\x20'+_0xeb8426['message']);}finally{this['browserInitializing']=![];}}async[a0_0x409fc8(0x178)](_0x6a1d20,_0x264f3a={}){const _0x1b3323=a0_0x409fc8,{engine:engine='google',maxResults:maxResults=0xa,agentId:_0x1e11f5}=_0x264f3a;if(!_0x6a1d20||typeof _0x6a1d20!=='string'||_0x6a1d20[_0x1b3323(0x14c)]()['length']===0x0)throw new Error(_0x1b3323(0x156));const _0x9adb31=this[_0x1b3323(0x1b7)][_0x1b3323(0x184)](_0xeb21fe=>_0xeb21fe[_0x1b3323(0x153)]===engine);if(!_0x9adb31)throw new Error('Unknown\x20search\x20engine:\x20'+engine+_0x1b3323(0x164)+this['searchEngines'][_0x1b3323(0x170)](_0x533874=>_0x533874['name'])[_0x1b3323(0x197)](',\x20'));await this['ensureBrowser'](),this['logger']?.['info']('Performing\x20web\x20search',{'query':_0x6a1d20,'engine':engine,'agentId':_0x1e11f5});const _0x582043=await this['browser'][_0x1b3323(0x142)]();try{const _0x2742bc=''+_0x9adb31['url']+encodeURIComponent(_0x6a1d20);await _0x582043[_0x1b3323(0x126)](_0x2742bc,{'waitUntil':'networkidle2','timeout':this[_0x1b3323(0x1bb)]}),await _0x582043[_0x1b3323(0x118)](_0x9adb31['waitSelector'],{'timeout':this['DEFAULT_TIMEOUT']});const _0x18d986=await _0x582043[_0x1b3323(0x193)]((_0x2aef7e,_0x123fdb)=>{const _0x340d6a=_0x1b3323,_0x3314b0=Array[_0x340d6a(0x1a1)](document['querySelectorAll'](_0x2aef7e));return _0x3314b0[_0x340d6a(0x166)](0x0,_0x123fdb)[_0x340d6a(0x170)](_0x5e7aec=>({'url':_0x5e7aec['href'],'title':_0x5e7aec['textContent']['trim'](),'description':_0x5e7aec['closest'](_0x340d6a(0x12d))?.[_0x340d6a(0x195)][_0x340d6a(0x14c)]()||''}))[_0x340d6a(0x165)](_0x519e20=>_0x519e20[_0x340d6a(0x157)]&&_0x519e20[_0x340d6a(0x157)]['startsWith']('http'));},_0x9adb31['resultsSelector'],maxResults);return this[_0x1b3323(0x11a)]?.['info'](_0x1b3323(0x15f),{'resultsCount':_0x18d986[_0x1b3323(0x172)],'agentId':_0x1e11f5}),{'success':!![],'query':_0x6a1d20,'engine':engine,'resultsCount':_0x18d986[_0x1b3323(0x172)],'results':_0x18d986};}finally{await _0x582043[_0x1b3323(0x116)]();}}async[a0_0x409fc8(0x122)](_0x47f115,_0x46cc80={}){const _0x3cd829=a0_0x409fc8,{formats:formats=[_0x3cd829(0x13e),'text'],agentId:_0x4c085e}=_0x46cc80;await this[_0x3cd829(0x140)](),this[_0x3cd829(0x11a)]?.[_0x3cd829(0x194)](_0x3cd829(0x16b),{'url':_0x47f115,'formats':formats,'agentId':_0x4c085e});const _0x1c1b8e=await this['browser']['newPage']();try{const _0x599aee=[];formats['includes']('console')&&_0x1c1b8e['on'](_0x3cd829(0x18a),_0x28cb26=>{const _0x4f072b=_0x3cd829;_0x599aee[_0x4f072b(0x131)]({'type':_0x28cb26[_0x4f072b(0x15d)](),'text':_0x28cb26[_0x4f072b(0x12c)]()});});await _0x1c1b8e[_0x3cd829(0x126)](_0x47f115,{'waitUntil':'networkidle2','timeout':this['DEFAULT_TIMEOUT']});const _0x4ff901={'url':_0x47f115};for(const _0x5ab41c of formats){switch(_0x5ab41c){case _0x3cd829(0x13e):_0x4ff901[_0x3cd829(0x13e)]=await _0x1c1b8e['title']();break;case _0x3cd829(0x12c):_0x4ff901['text']=await _0x1c1b8e[_0x3cd829(0x193)](()=>document[_0x3cd829(0x1bc)]['innerText']);break;case'links':_0x4ff901['links']=await _0x1c1b8e['evaluate'](()=>{const _0x442718=_0x3cd829;return Array['from'](document['querySelectorAll']('a[href]'))['map'](_0x3f4d78=>({'href':_0x3f4d78[_0x442718(0x15e)],'text':_0x3f4d78['textContent'][_0x442718(0x14c)]()}));});break;case _0x3cd829(0x18f):_0x4ff901[_0x3cd829(0x18f)]=await _0x1c1b8e[_0x3cd829(0x138)]();break;case _0x3cd829(0x18a):_0x4ff901[_0x3cd829(0x18e)]=_0x599aee;break;}}return this[_0x3cd829(0x11a)]?.['info']('Fetch\x20completed',{'url':_0x47f115,'formats':formats,'agentId':_0x4c085e}),{'success':!![],..._0x4ff901};}finally{await _0x1c1b8e['close']();}}async['interactive'](_0x4f1fed,_0x47f3c4={}){const _0x51acff=a0_0x409fc8,{headless:headless=!![],agentId:_0x22c6d9,context:_0x5eb35f}=_0x47f3c4;await this['ensureBrowser'](),this['logger']?.[_0x51acff(0x194)](_0x51acff(0x139),{'actionsCount':_0x4f1fed[_0x51acff(0x172)],'headless':headless,'agentId':_0x22c6d9});const _0x11f030=[];!this[_0x51acff(0x191)]['has'](_0x22c6d9)&&this[_0x51acff(0x191)]['set'](_0x22c6d9,new Map());const _0x5bba75=this[_0x51acff(0x191)][_0x51acff(0x181)](_0x22c6d9);for(const _0x3d0aea of _0x4f1fed){try{let _0x5405ee;switch(_0x3d0aea['type']){case _0x51acff(0x137):_0x5405ee=await this['openTab'](_0x22c6d9,_0x3d0aea['name'],_0x3d0aea[_0x51acff(0x157)],headless,_0x3d0aea[_0x51acff(0x13b)],_0x5eb35f);break;case'close-tab':_0x5405ee=await this['closeTab'](_0x22c6d9,_0x3d0aea['name']);break;case'switch-tab':_0x5405ee=await this['switchTab'](_0x22c6d9,_0x3d0aea['name']);break;case _0x51acff(0x1af):_0x5405ee=await this[_0x51acff(0x134)](_0x22c6d9);break;default:_0x5405ee={'success':![],'error':'Action\x20'+_0x3d0aea['type']+'\x20must\x20be\x20executed\x20within\x20a\x20tab\x20context\x20(use\x20open-tab\x20with\x20nestedActions)'};}_0x11f030['push']({'action':_0x3d0aea['type'],..._0x5405ee});}catch(_0x2461b6){this[_0x51acff(0x11a)]?.['error'](_0x51acff(0x1aa),{'action':_0x3d0aea['type'],'error':_0x2461b6['message'],'agentId':_0x22c6d9}),_0x11f030['push']({'action':_0x3d0aea['type'],'success':![],'error':_0x2461b6['message']});}}return{'success':_0x11f030[_0x51acff(0x1b4)](_0x4c7838=>_0x4c7838['success']!==![]),'actionsExecuted':_0x11f030['length'],'results':_0x11f030};}async[a0_0x409fc8(0x12f)](_0x214188,_0x581415,_0x12af71,_0x3e20a1,_0x3dc139=[],_0x32f47={}){const _0x59ec3d=a0_0x409fc8;!this['agentTabs'][_0x59ec3d(0x12a)](_0x214188)&&this['agentTabs'][_0x59ec3d(0x13c)](_0x214188,new Map());const _0x59c5ca=this[_0x59ec3d(0x191)][_0x59ec3d(0x181)](_0x214188);if(_0x59c5ca['has'](_0x581415))throw new Error(_0x59ec3d(0x1b1)+_0x581415+_0x59ec3d(0x1b2)+_0x214188);this['logger']?.[_0x59ec3d(0x194)]('Opening\x20tab',{'agentId':_0x214188,'tabName':_0x581415,'url':_0x12af71,'headless':_0x3e20a1});const _0x29afaf=await this[_0x59ec3d(0x1b3)]['newPage']();await _0x29afaf['setViewport']({'width':0x500,'height':0x2d0});const _0x24331a=[];_0x29afaf['on']('console',_0x3acb4a=>{const _0x59382c=_0x59ec3d;_0x24331a[_0x59382c(0x131)]({'type':_0x3acb4a['type'](),'text':_0x3acb4a[_0x59382c(0x12c)](),'timestamp':Date['now']()});});const _0x5c8b45={'page':_0x29afaf,'url':_0x12af71,'lastActivity':Date['now'](),'headless':_0x3e20a1,'consoleMessages':_0x24331a,'name':_0x581415};_0x59c5ca['set'](_0x581415,_0x5c8b45);const _0x532f29=[];try{_0x12af71&&(await _0x29afaf['goto'](_0x12af71,{'waitUntil':_0x59ec3d(0x117),'timeout':this[_0x59ec3d(0x1bb)]}),_0x5c8b45['url']=_0x12af71,_0x5c8b45['lastActivity']=Date['now']());for(const _0x32dd1e of _0x3dc139){const _0xf99672=await this[_0x59ec3d(0x173)](_0x29afaf,_0x32dd1e,_0x5c8b45,_0x32f47);_0x532f29['push']({'action':_0x32dd1e['type'],..._0xf99672}),_0x5c8b45[_0x59ec3d(0x148)]=Date[_0x59ec3d(0x1be)]();}return{'success':!![],'tabName':_0x581415,'url':_0x5c8b45['url'],'actionsExecuted':_0x532f29[_0x59ec3d(0x172)],'results':_0x532f29};}catch(_0x2cb2bd){this[_0x59ec3d(0x11a)]?.['error']('Failed\x20to\x20open\x20tab',{'agentId':_0x214188,'tabName':_0x581415,'error':_0x2cb2bd['message']}),await _0x29afaf[_0x59ec3d(0x116)](),_0x59c5ca['delete'](_0x581415);throw _0x2cb2bd;}}async[a0_0x409fc8(0x173)](_0x4fb083,_0x37c02a,_0x11146b,_0x359214){const _0x499c19=a0_0x409fc8;switch(_0x37c02a[_0x499c19(0x15d)]){case'navigate':await _0x4fb083['goto'](_0x37c02a[_0x499c19(0x19a)]||_0x37c02a[_0x499c19(0x157)],{'waitUntil':'networkidle2','timeout':this[_0x499c19(0x1bb)]}),_0x11146b['url']=_0x4fb083['url']();return{'success':!![],'url':_0x11146b[_0x499c19(0x157)]};case'click':await _0x4fb083['click'](_0x37c02a[_0x499c19(0x13a)],{'button':_0x37c02a['button']||'left'});return{'success':!![],'selector':_0x37c02a[_0x499c19(0x13a)]};case'type':await _0x4fb083[_0x499c19(0x15d)](_0x37c02a['selector'],_0x37c02a['text']||_0x37c02a[_0x499c19(0x19a)]);return{'success':!![],'selector':_0x37c02a[_0x499c19(0x13a)],'text':_0x37c02a[_0x499c19(0x12c)]};case'press':await _0x4fb083[_0x499c19(0x141)][_0x499c19(0x11f)](_0x37c02a['key']||_0x37c02a['value']);return{'success':!![],'key':_0x37c02a[_0x499c19(0x146)]};case _0x499c19(0x1b0):const _0x3bbbc2=_0x37c02a[_0x499c19(0x132)]?parseInt(_0x37c02a[_0x499c19(0x132)],0xa):this[_0x499c19(0x1bb)];await _0x4fb083['waitForSelector'](_0x37c02a[_0x499c19(0x13a)],{'timeout':_0x3bbbc2});return{'success':!![],'selector':_0x37c02a['selector']};case _0x499c19(0x19b):return await this[_0x499c19(0x17e)](_0x4fb083,_0x37c02a,_0x359214);case _0x499c19(0x16f):return await this[_0x499c19(0x13f)](_0x4fb083,_0x37c02a[_0x499c19(0x19a)],_0x359214);case'extract-text':const _0x2f0ea1=await _0x4fb083['evaluate'](_0x1da18b=>{const _0x2f8a7c=_0x499c19,_0x2878b5=document['querySelector'](_0x1da18b);return _0x2878b5?_0x2878b5[_0x2f8a7c(0x167)]:null;},_0x37c02a[_0x499c19(0x13a)]);return{'success':!![],'selector':_0x37c02a[_0x499c19(0x13a)],'text':_0x2f0ea1};case'extract-links':const _0x43a237=await _0x4fb083[_0x499c19(0x193)](_0x129abf=>{const _0x3aefcc=_0x499c19,_0x482e2a=document[_0x3aefcc(0x14e)](_0x129abf);return Array['from'](_0x482e2a)['map'](_0x582f2a=>({'href':_0x582f2a[_0x3aefcc(0x15e)],'text':_0x582f2a['textContent']['trim']()}));},_0x37c02a['selector']);return{'success':!![],'selector':_0x37c02a['selector'],'links':_0x43a237};case _0x499c19(0x19c):const _0x267b35=await _0x4fb083['content']();return{'success':!![],'html':_0x267b35};case'get-console':return{'success':!![],'consoleMessages':[..._0x11146b['consoleMessages']]};case'scroll':await _0x4fb083['evaluate'](_0x17f7bc=>{const _0x4e504e=_0x499c19;_0x17f7bc?document[_0x4e504e(0x187)](_0x17f7bc)?.[_0x4e504e(0x174)]():window[_0x4e504e(0x158)](0x0,document['body']['scrollHeight']);},_0x37c02a[_0x499c19(0x13a)]);return{'success':!![]};case'hover':await _0x4fb083[_0x499c19(0x182)](_0x37c02a['selector']);return{'success':!![],'selector':_0x37c02a['selector']};case'mouse-move':await _0x4fb083[_0x499c19(0x182)](_0x37c02a['selector']);return{'success':!![],'selector':_0x37c02a[_0x499c19(0x13a)]};default:throw new Error(_0x499c19(0x198)+_0x37c02a[_0x499c19(0x15d)]);}}async['takeScreenshot'](_0xb7e772,_0x1e9c6d,_0x14de72){const _0x475365=a0_0x409fc8,_0x440419=_0x1e9c6d[_0x475365(0x147)]||'file',_0x366443=_0x1e9c6d['path'];if(_0x440419==='base64'){const _0x52774b=await _0xb7e772[_0x475365(0x19b)]({'encoding':'base64'});return{'success':!![],'format':_0x475365(0x14d),'screenshot':_0x52774b};}let _0x35eee9;if(_0x366443){const _0x5cc8f6=_0x14de72['directoryAccess']?.[_0x475365(0x177)]||_0x14de72['projectDir']||process[_0x475365(0x125)]();_0x35eee9=a0_0xeef28c['isAbsolute'](_0x366443)?_0x366443:a0_0xeef28c['join'](_0x5cc8f6,_0x366443);}else{const _0x34dc13=_0x475365(0x188)+Date['now']()+'.png';_0x35eee9=a0_0xeef28c[_0x475365(0x197)](this['TEMP_DIR'],_0x34dc13);}return await _0xb7e772['screenshot']({'path':_0x35eee9}),{'success':!![],'format':_0x475365(0x121),'path':_0x35eee9};}async[a0_0x409fc8(0x13f)](_0x496a7a,_0x4402e0,_0x3555d3){const _0x491792=a0_0x409fc8,_0x19c726=await _0x496a7a['screenshot']({'encoding':_0x491792(0x14d)}),_0x46443b=_0x3555d3['aiService'];if(!_0x46443b)throw new Error('AI\x20service\x20not\x20available\x20for\x20screenshot\x20analysis');this['logger']?.[_0x491792(0x194)](_0x491792(0x149),{'question':_0x4402e0['substring'](0x0,0x64),'agentId':_0x3555d3[_0x491792(0x176)]});try{const _0x43232c='o3',_0x1be0f0=await _0x46443b['sendMessage'](_0x43232c,_0x4402e0,{'agentId':_0x3555d3[_0x491792(0x176)],'images':[_0x491792(0x1a8)+_0x19c726],'apiKey':_0x3555d3[_0x491792(0x1a9)],'customApiKeys':_0x3555d3['customApiKeys'],'platformProvided':_0x3555d3[_0x491792(0x17f)]});return{'success':!![],'question':_0x4402e0,'analysis':_0x1be0f0['content'],'model':_0x1be0f0[_0x491792(0x124)]||_0x43232c};}catch(_0x18c3a6){this['logger']?.[_0x491792(0x179)]('Screenshot\x20analysis\x20failed',{'error':_0x18c3a6[_0x491792(0x11c)],'agentId':_0x3555d3['agentId']});throw new Error('Screenshot\x20analysis\x20failed:\x20'+_0x18c3a6[_0x491792(0x11c)]);}}async[a0_0x409fc8(0x11b)](_0x4f2fa6,_0x1ebf95){const _0x380262=a0_0x409fc8,_0x1fa482=this[_0x380262(0x191)]['get'](_0x4f2fa6);if(!_0x1fa482||!_0x1fa482[_0x380262(0x12a)](_0x1ebf95))throw new Error('Tab\x20\x27'+_0x1ebf95+'\x27\x20not\x20found\x20for\x20agent\x20'+_0x4f2fa6);const _0x4c8241=_0x1fa482[_0x380262(0x181)](_0x1ebf95);return this['logger']?.['info']('Closing\x20tab',{'agentId':_0x4f2fa6,'tabName':_0x1ebf95}),await _0x4c8241['page'][_0x380262(0x116)](),_0x1fa482[_0x380262(0x136)](_0x1ebf95),{'success':!![],'tabName':_0x1ebf95,'message':_0x380262(0x1b1)+_0x1ebf95+'\x27\x20closed'};}async[a0_0x409fc8(0x115)](_0x3bfdaf,_0x313da0){const _0x233136=a0_0x409fc8,_0x5c290e=this['agentTabs'][_0x233136(0x181)](_0x3bfdaf);if(!_0x5c290e||!_0x5c290e['has'](_0x313da0))throw new Error('Tab\x20\x27'+_0x313da0+'\x27\x20not\x20found\x20for\x20agent\x20'+_0x3bfdaf);const _0x18d755=_0x5c290e['get'](_0x313da0);return _0x18d755['lastActivity']=Date[_0x233136(0x1be)](),{'success':!![],'tabName':_0x313da0,'url':_0x18d755['url'],'message':_0x233136(0x1b6)+_0x313da0+'\x27'};}async[a0_0x409fc8(0x134)](_0x4b0a6f){const _0x33578b=a0_0x409fc8,_0x343e9b=this['agentTabs']['get'](_0x4b0a6f);if(!_0x343e9b||_0x343e9b[_0x33578b(0x1a6)]===0x0)return{'success':!![],'tabCount':0x0,'tabs':[],'message':'No\x20active\x20tabs'};const _0x45be7c=[];for(const [_0x1e1583,_0x1ab9a2]of _0x343e9b['entries']()){_0x45be7c[_0x33578b(0x131)]({'name':_0x1e1583,'url':_0x1ab9a2['url'],'idleTime':Date['now']()-_0x1ab9a2[_0x33578b(0x148)],'headless':_0x1ab9a2['headless']});}return{'success':!![],'tabCount':_0x45be7c[_0x33578b(0x172)],'tabs':_0x45be7c};}[a0_0x409fc8(0x15b)](){const _0x497f44=a0_0x409fc8;this[_0x497f44(0x14b)]&&clearInterval(this['cleanupTimer']),this[_0x497f44(0x14b)]=setInterval(()=>{const _0x32f6d6=_0x497f44;this[_0x32f6d6(0x1a0)]();},this['CLEANUP_INTERVAL']);}async['cleanupIdleTabs'](){const _0x5b526f=a0_0x409fc8,_0x8af9a2=Date[_0x5b526f(0x1be)](),_0x468233=[];for(const [_0x3d2e07,_0x271c0a]of this['agentTabs'][_0x5b526f(0x169)]()){for(const [_0x31f81d,_0x272a04]of _0x271c0a['entries']()){const _0xa16ad4=_0x8af9a2-_0x272a04['lastActivity'];_0xa16ad4>this['TAB_IDLE_TIMEOUT']&&_0x468233['push']({'agentId':_0x3d2e07,'tabName':_0x31f81d,'tabInfo':_0x272a04});}}if(_0x468233['length']>0x0){this['logger']?.['info']('Cleaning\x20up\x20idle\x20tabs',{'count':_0x468233['length']});for(const {agentId:_0x185bdc,tabName:_0x49c03c,tabInfo:_0x234ddf}of _0x468233){try{await _0x234ddf[_0x5b526f(0x175)][_0x5b526f(0x116)](),this[_0x5b526f(0x191)]['get'](_0x185bdc)[_0x5b526f(0x136)](_0x49c03c),this['logger']?.[_0x5b526f(0x161)](_0x5b526f(0x160),{'agentId':_0x185bdc,'tabName':_0x49c03c});}catch(_0x166d1b){this['logger']?.['error'](_0x5b526f(0x11d),{'agentId':_0x185bdc,'tabName':_0x49c03c,'error':_0x166d1b['message']});}}}}async[a0_0x409fc8(0x189)](_0x10b04f){const _0x28e36c=a0_0x409fc8,_0x4d4dab=this[_0x28e36c(0x191)]['get'](_0x10b04f);if(!_0x4d4dab)return{'success':!![],'agentId':_0x10b04f,'closedTabs':0x0,'message':_0x28e36c(0x129)};this[_0x28e36c(0x11a)]?.[_0x28e36c(0x194)]('Cleaning\x20up\x20agent\x20tabs',{'agentId':_0x10b04f,'tabCount':_0x4d4dab[_0x28e36c(0x1a6)]});let _0x36821c=0x0;for(const [_0x4dedc3,_0x58607e]of _0x4d4dab[_0x28e36c(0x169)]()){try{await _0x58607e['page'][_0x28e36c(0x116)](),_0x36821c++;}catch(_0x241bde){this[_0x28e36c(0x11a)]?.[_0x28e36c(0x179)]('Failed\x20to\x20close\x20tab\x20during\x20cleanup',{'agentId':_0x10b04f,'tabName':_0x4dedc3,'error':_0x241bde['message']});}}return this['agentTabs'][_0x28e36c(0x136)](_0x10b04f),{'success':!![],'agentId':_0x10b04f,'closedTabs':_0x36821c,'message':'Closed\x20'+_0x36821c+'\x20tabs\x20for\x20agent\x20'+_0x10b04f};}async['ensureTempDir'](){const _0x263fd4=a0_0x409fc8;try{await a0_0x450aef[_0x263fd4(0x18b)](this['TEMP_DIR'],{'recursive':!![]});}catch(_0x26262b){this[_0x263fd4(0x11a)]?.['warn'](_0x263fd4(0x133),{'path':this[_0x263fd4(0x1a3)],'error':_0x26262b[_0x263fd4(0x11c)]});}}async['cleanup'](){const _0x218e11=a0_0x409fc8;this[_0x218e11(0x14b)]&&(clearInterval(this[_0x218e11(0x14b)]),this['cleanupTimer']=null);for(const [_0x1cb819]of this[_0x218e11(0x191)][_0x218e11(0x169)]()){await this[_0x218e11(0x189)](_0x1cb819);}this['browser']&&(await this['browser']['close'](),this['browser']=null);try{await a0_0x450aef['rm'](this['TEMP_DIR'],{'recursive':!![],'force':!![]});}catch(_0x3fb471){this['logger']?.[_0x218e11(0x1a5)](_0x218e11(0x17c),{'path':this['TEMP_DIR'],'error':_0x3fb471['message']});}}}function a0_0x44f7(_0x3a4a50,_0x3ba84a){_0x3a4a50=_0x3a4a50-0x113;const _0x5e70bf=a0_0x5e70();let _0x44f71d=_0x5e70bf[_0x3a4a50];if(a0_0x44f7['yFtHPA']===undefined){var _0x47e279=function(_0x21753f){const _0xb54f0f='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x41a119='',_0x1adca6='';for(let _0xeef28c=0x0,_0x450aef,_0x261e4f,_0xcef85a=0x0;_0x261e4f=_0x21753f['charAt'](_0xcef85a++);~_0x261e4f&&(_0x450aef=_0xeef28c%0x4?_0x450aef*0x40+_0x261e4f:_0x261e4f,_0xeef28c++%0x4)?_0x41a119+=String['fromCharCode'](0xff&_0x450aef>>(-0x2*_0xeef28c&0x6)):0x0){_0x261e4f=_0xb54f0f['indexOf'](_0x261e4f);}for(let _0x54fda7=0x0,_0x1f81fd=_0x41a119['length'];_0x54fda7<_0x1f81fd;_0x54fda7++){_0x1adca6+='%'+('00'+_0x41a119['charCodeAt'](_0x54fda7)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x1adca6);};a0_0x44f7['fKDzgx']=_0x47e279,a0_0x44f7['EumYpg']={},a0_0x44f7['yFtHPA']=!![];}const _0x4dd58c=_0x5e70bf[0x0],_0x49c895=_0x3a4a50+_0x4dd58c,_0x4f9805=a0_0x44f7['EumYpg'][_0x49c895];return!_0x4f9805?(_0x44f71d=a0_0x44f7['fKDzgx'](_0x44f71d),a0_0x44f7['EumYpg'][_0x49c895]=_0x44f71d):_0x44f71d=_0x4f9805,_0x44f71d;}export default WebTool;