@loxia-labs/loxia-autopilot-one 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -54
- package/bin/cli.js +1 -115
- package/bin/loxia-terminal-v2.js +3 -0
- package/bin/loxia-terminal.js +3 -0
- package/bin/start-with-terminal.js +3 -0
- package/package.json +14 -15
- package/scripts/install-scanners.js +1 -235
- package/src/analyzers/CSSAnalyzer.js +1 -297
- package/src/analyzers/ConfigValidator.js +1 -690
- package/src/analyzers/ESLintAnalyzer.js +1 -320
- package/src/analyzers/JavaScriptAnalyzer.js +1 -261
- package/src/analyzers/PrettierFormatter.js +1 -247
- package/src/analyzers/PythonAnalyzer.js +1 -266
- package/src/analyzers/SecurityAnalyzer.js +1 -729
- package/src/analyzers/TypeScriptAnalyzer.js +1 -247
- package/src/analyzers/codeCloneDetector/analyzer.js +1 -344
- package/src/analyzers/codeCloneDetector/detector.js +1 -203
- package/src/analyzers/codeCloneDetector/index.js +1 -160
- package/src/analyzers/codeCloneDetector/parser.js +1 -199
- package/src/analyzers/codeCloneDetector/reporter.js +1 -148
- package/src/analyzers/codeCloneDetector/scanner.js +1 -59
- package/src/core/agentPool.js +1 -1474
- package/src/core/agentScheduler.js +1 -2147
- package/src/core/contextManager.js +1 -709
- package/src/core/messageProcessor.js +1 -732
- package/src/core/orchestrator.js +1 -548
- package/src/core/stateManager.js +1 -877
- package/src/index.js +1 -631
- package/src/interfaces/cli.js +1 -549
- package/src/interfaces/terminal/__tests__/smoke/advancedFeatures.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/agentControl.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/agents.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/components.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/connection.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/enhancements.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/imports.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/messages.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/tools.test.js +1 -0
- package/src/interfaces/terminal/api/apiClient.js +1 -0
- package/src/interfaces/terminal/api/messageRouter.js +1 -0
- package/src/interfaces/terminal/api/session.js +1 -0
- package/src/interfaces/terminal/api/websocket.js +1 -0
- package/src/interfaces/terminal/components/AgentCreator.js +1 -0
- package/src/interfaces/terminal/components/AgentEditor.js +1 -0
- package/src/interfaces/terminal/components/AgentSwitcher.js +1 -0
- package/src/interfaces/terminal/components/ErrorBoundary.js +1 -0
- package/src/interfaces/terminal/components/ErrorPanel.js +1 -0
- package/src/interfaces/terminal/components/Header.js +1 -0
- package/src/interfaces/terminal/components/HelpPanel.js +1 -0
- package/src/interfaces/terminal/components/InputBox.js +1 -0
- package/src/interfaces/terminal/components/Layout.js +1 -0
- package/src/interfaces/terminal/components/LoadingSpinner.js +1 -0
- package/src/interfaces/terminal/components/MessageList.js +1 -0
- package/src/interfaces/terminal/components/MultilineTextInput.js +1 -0
- package/src/interfaces/terminal/components/SearchPanel.js +1 -0
- package/src/interfaces/terminal/components/SettingsPanel.js +1 -0
- package/src/interfaces/terminal/components/StatusBar.js +1 -0
- package/src/interfaces/terminal/components/TextInput.js +1 -0
- package/src/interfaces/terminal/config/agentEditorConstants.js +1 -0
- package/src/interfaces/terminal/config/constants.js +1 -0
- package/src/interfaces/terminal/index.js +1 -0
- package/src/interfaces/terminal/state/useAgentControl.js +1 -0
- package/src/interfaces/terminal/state/useAgents.js +1 -0
- package/src/interfaces/terminal/state/useConnection.js +1 -0
- package/src/interfaces/terminal/state/useMessages.js +1 -0
- package/src/interfaces/terminal/state/useTools.js +1 -0
- package/src/interfaces/terminal/utils/debugLogger.js +1 -0
- package/src/interfaces/terminal/utils/settingsStorage.js +1 -0
- package/src/interfaces/terminal/utils/theme.js +1 -0
- package/src/interfaces/webServer.js +1 -2162
- package/src/modules/fileExplorer/controller.js +1 -280
- package/src/modules/fileExplorer/index.js +1 -37
- package/src/modules/fileExplorer/middleware.js +1 -92
- package/src/modules/fileExplorer/routes.js +1 -125
- package/src/modules/fileExplorer/types.js +1 -44
- package/src/services/aiService.js +1 -1232
- package/src/services/apiKeyManager.js +1 -164
- package/src/services/benchmarkService.js +1 -366
- package/src/services/budgetService.js +1 -539
- package/src/services/contextInjectionService.js +1 -247
- package/src/services/conversationCompactionService.js +1 -637
- package/src/services/errorHandler.js +1 -810
- package/src/services/fileAttachmentService.js +1 -544
- package/src/services/modelRouterService.js +1 -366
- package/src/services/modelsService.js +1 -322
- package/src/services/qualityInspector.js +1 -796
- package/src/services/tokenCountingService.js +1 -536
- package/src/tools/agentCommunicationTool.js +1 -1344
- package/src/tools/agentDelayTool.js +1 -485
- package/src/tools/asyncToolManager.js +1 -604
- package/src/tools/baseTool.js +1 -800
- package/src/tools/browserTool.js +1 -920
- package/src/tools/cloneDetectionTool.js +1 -621
- package/src/tools/dependencyResolverTool.js +1 -1215
- package/src/tools/fileContentReplaceTool.js +1 -875
- package/src/tools/fileSystemTool.js +1 -1107
- package/src/tools/fileTreeTool.js +1 -853
- package/src/tools/imageTool.js +1 -901
- package/src/tools/importAnalyzerTool.js +1 -1060
- package/src/tools/jobDoneTool.js +1 -248
- package/src/tools/seekTool.js +1 -956
- package/src/tools/staticAnalysisTool.js +1 -1778
- package/src/tools/taskManagerTool.js +1 -2873
- package/src/tools/terminalTool.js +1 -2304
- package/src/tools/webTool.js +1 -1430
- package/src/types/agent.js +1 -519
- package/src/types/contextReference.js +1 -972
- package/src/types/conversation.js +1 -730
- package/src/types/toolCommand.js +1 -747
- package/src/utilities/attachmentValidator.js +1 -292
- package/src/utilities/configManager.js +1 -582
- package/src/utilities/constants.js +1 -722
- package/src/utilities/directoryAccessManager.js +1 -535
- package/src/utilities/fileProcessor.js +1 -307
- package/src/utilities/logger.js +1 -436
- package/src/utilities/tagParser.js +1 -1246
- package/src/utilities/toolConstants.js +1 -317
- package/web-ui/build/index.html +2 -2
- package/web-ui/build/static/{index-Dy2bYbOa.css → index-CClD1090.css} +1 -1
- package/web-ui/build/static/{index-CjkkcnFA.js → index-lCBai6dX.js} +66 -67
package/src/tools/webTool.js
CHANGED
|
@@ -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_0x26eeb0=a0_0x49e3;(function(_0x25b59c,_0x450802){const _0x3ec189=a0_0x49e3,_0x4afea4=_0x25b59c();while(!![]){try{const _0x483bb0=-parseInt(_0x3ec189(0x21f))/0x1*(parseInt(_0x3ec189(0x25a))/0x2)+parseInt(_0x3ec189(0x21d))/0x3+-parseInt(_0x3ec189(0x256))/0x4+parseInt(_0x3ec189(0x299))/0x5+-parseInt(_0x3ec189(0x278))/0x6*(-parseInt(_0x3ec189(0x272))/0x7)+-parseInt(_0x3ec189(0x292))/0x8*(parseInt(_0x3ec189(0x2a0))/0x9)+parseInt(_0x3ec189(0x245))/0xa*(-parseInt(_0x3ec189(0x220))/0xb);if(_0x483bb0===_0x450802)break;else _0x4afea4['push'](_0x4afea4['shift']());}catch(_0x2591e0){_0x4afea4['push'](_0x4afea4['shift']());}}}(a0_0x1744,0x1cea0));import{BaseTool}from'./baseTool.js';function a0_0x1744(){const _0x3b5a4e=['AgvHzgXLC3m','C2nYB2XSvg8','BwvZC2fNzq','y3vZDg9TqxbPs2v5CW','yNjVD3nLCG','zxH0CMfJDenVBNrLBNq','yw5HBhL6zvnJCMvLBNnOB3q','pNW8','zMfSC2u','i2jFCMvZDwX0CW','mteZodmYu3D1tvH3','Ag92zxi','C3vJy2vZCW','tM8GDgfICYb0BYbJBgvHBIb1Ca','nNLovgDJqW','zxjYB3i','DgfIswrSzvrPBwvVDxq','C2nYB2XSsgvPz2H0','ls1KAxnHyMXLlwfJy2vSzxjHDgvKltjKlwnHBNzHCW','vevnuf9esvi','DhLWzq','BgLZDfrHyNm','sw5PDgLHBgL6Aw5Nifb1ChbLDgvLCIbICM93C2vY','y2XLyw51CfrPBwvY','vgfIicC','zxzHBhvHDgu','C2L6zq','C2vSzwn0B3i','Dgv4Da','BMf2AwDHDgu','CxvLCNLtzwXLy3rVCG','C2nYzwvUC2HVDa','C2nYzwvUC2HVDc0','D2fYBG','D2fPDc1MB3i','y2XPy2S','tM8Gywn0AxzLihrHyNm','zxH0CMfJDc1SAw5RCW','mJeWvxjwwLH1','D2vI','u2nYzwvUC2HVDcbHBMfSExnPCYbMywLSzwq6ia','yMfZzty0','DgfRzvnJCMvLBNnOB3q','vw5RBM93BIbVCgvYyxrPB246ia','ndaYmtHYr2TsEM8','B3bLBI10ywi','ls1KAxnHyMXLlwrLDI1ZAg0TDxnHz2u','zgvIDwC','BMfTzq','zxH0CMfJDc10zxH0','DgL0Bgu','rMfPBgvKihrVihbHCNnLihDLyIb0B29SihbHCMfTzxrLCNm6ia','Dg1WzgLY','B3bLCMf0Aw9U','zgvSzxrL','yNjVD3nLCKLUAxrPywXPEMLUzW','C2vHCMnO','CgfYC2u','BgfZDefJDgL2Axr5','Aw5UzxjuzxH0','yNv0Dg9U','zxHLy3v0zvrHyKfJDgLVBG','z2v0','ls1KAxnHyMXLlwDWDq','BMv3ugfNzq','q0Xfqu5vuf9jtLrfuLzbta','yM9KEq','jYbUB3qGzM91BMqGzM9YigfNzw50ia','C3rYAw5N','zw50CMLLCW','nZm3nLvUAxfHCq','DhjPBq','y2XVC2uTDgfI','z2v0lwnVBNnVBgu','rMfPBgvKihrVigLUAxrPywXPEMuGyNjVD3nLCG','CgfNzq','AhjLzG','mta5mZGYnwT1sMfktq','zw5NAw5L','yw5HBhL6zs1Zy3jLzw5ZAg90','cLDLyIbuB29SoIbcCM93C2uSihnLyxjJAcWGyw5Kigf1Dg9TyxrLihDLyIbPBNrLCMfJDgLVBNmGDxnPBMCGysbYzwfSigjYB3DZzxiGkfb1ChbLDgvLCIKUcGPjtvbpuLrbtLq6ifrOAxmGDg9VBcbZDxbWB3j0CYbJB21Tyw5KignOywLUAw5Nic0GBMvZDcbTDwX0AxbSzsbHy3rPB25ZihrVz2v0AgvYihrVigv4zwn1DguGDgHLBsbZzxf1zw50AwfSBhKGD2L0Ag91Dcb3ywL0Aw5NigzVCIbYzxnWB25ZzxmGyMv0D2vLBIbLywnOigfJDgLVBI4kcLvtquDfoGPBDg9VBcbPzd0ID2vIiL0kpg9WzxjHDgLVBJ5ZzwfYy2H8zMv0y2H8Aw50zxjHy3rPDMu8l29WzxjHDgLVBJ4kpceTlsbpCgvYyxrPB24TC3bLy2LMAwmGy29UDgvUDcaTlt4kwY90B29SxqOkquXurvjoqvrjvKuGsLnptIbgt1jnqvq6cMbGygPZB24kEWOGicj0B29SswqIoIaID2vIiIWkicaIB3bLCMf0Aw9UiJOGiNnLyxjJAhXMzxrJAhXPBNrLCMfJDgL2zsiScIaGiNbHCMfTzxrLCNmIoIb7ic4UlIb9cN0kygbGcGRILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzakt1bfuKfusu9oide6ifnfqvjdscbuseuGv0vccUkvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokvKokv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','C2nYB2XS','D2fPDezVCLnLBgvJDg9Y','y29UC29Szq','mtuZyMPkCwHZ','DxjS','zgf0ytPPBwfNzs9WBMC7yMfZzty0la','qNjVD3nLCIbPBML0AwfSAxPHDgLVBIbMywLSzwq6ia','CxvLCNK','Aw5MBW','C2v0','B3bLBLrHyG','C3DPDgnOvgfI','Bg9Nz2vY','qwn0Aw9UigzHAwXLza','CgfYC2vozxn0zwrby3rPB25Z','CgfYC2vqyxjHBwv0zxjZ','Ahr0Ca','y2XVC2vZDa','zgvMyxvSDfrPBwvVDxq','Aw50zxjHy3rPDMu','Ahr0Chm6lY9KDwnRzhvJA2DVlMnVBs8/Ct0','y2XLyw51Ca','y2XVC2u','z29Vz2XL','z2v0uMvXDwLYzwrqyxjHBwv0zxjZ','Bwf4lxjLC3vSDhm','AhrTBa','i2XPBMTZlcbBzgf0ys10zxn0Awq9iM1HAw5SAw5LiL0','revgqvvmvf9usu1ft1vu','rMfPBgvKihrVignYzwf0zsb0zw1WigrPCMvJDg9YEq','zMLSzq','C2vHCMnOrw5NAw5LCW','Aw5WDxrBBMfTzt0ICsjD','zM9YBwf0CW','CxvLCNLtzwXLy3rVCKfSBa','z290BW','q2XVC2vKigLKBguGDgfI','C3rHCNrdBgvHBNvWvgLTzxi','BMv0D29YA2LKBguY','zw5ZDxjLqNjVD3nLCG','BgLUA3m','jYbHBhjLywr5igv4Axn0CYbMB3iGywDLBNqG','AxnbC3LUyW','zxHLyW','BgvUz3rO','ywDLBNruywjZ','nZaXmZqZuwDKqMjZ','DMfSDwu','nZa1mtLlugzIwfu','mZa3ndy2nwXXDgrQsG','Bw91C2uTBw92zq','y2XVC2vuywi','AM9PBG','yNv0Dg9Uw3r5Cgu9iNn1yM1PDcjD','DgvTCerPCG','BgvMDa','Dgv4DenVBNrLBNq','D2fPDfnLBgvJDg9Y','CgXHDgzVCM1qCM92AwrLza','zw5ZDxjLvgvTCerPCG','ls1KAxnHyMXLlwjSAw5RlwzLyxr1CMvZpuf1Dg9TyxrPB25dB250CM9SBgvK','Bwf4uMvZDwX0CW','BgLZDc10ywjZ','y3vZDg9TvMfSAwrHDgvqyxjHBwv0zxjZ','zgLYzwn0B3j5qwnJzxnZ','ywLtzxj2AwnL','DgLTzw91Da','ChvZAa','lNjLC3vSDf9FysWGyvTKyxrHlxrLC3rPzd0ICMvZDwX0lxrPDgXLlweIxq','BM93','DxjSigLZihjLCxvPCMvKigzVCIbMzxrJAcbVCgvYyxrPB24','rMfPBgvKihrVignSB3nLihrHyIbKDxjPBMCGy2XLyw51Ca','z2v0lxnVDxjJzq','y2XLyw51CefNzw50','yvTOCMvMxq','ls1UBY1Zyw5KyM94','Aw5JBhvKzxm','AxndB25Uzwn0zwq','BMv3','y2XLyw51CeLUDgvYDMfS','BMvZDgvKqwn0Aw9UCW','C3DPDgnOlxrHyG','ywn0Aw9UCW','zMv0y2G','ChjLC3m','rMv0y2HPBMCGD2vIignVBNrLBNq','mtbdvvDOv3y','CgfYC2vby3rPB25Z','BwfW','C2v0vMLLD3bVCNq','AgfZ','Cgf0Aa','CxvLCNKGAxmGCMvXDwLYzwqGzM9YihnLyxjJAcbVCgvYyxrPB24'];a0_0x1744=function(){return _0x3b5a4e;};return a0_0x1744();}import a0_0x54bfb9 from'../utilities/tagParser.js';import a0_0x56d882 from'puppeteer';import a0_0x4fcc97 from'path';import a0_0x2afc71 from'fs/promises';import a0_0x4272f3 from'os';import{TOOL_STATUS,SYSTEM_DEFAULTS}from'../utilities/constants.js';function a0_0x49e3(_0x24bd08,_0x1e348f){_0x24bd08=_0x24bd08-0x1f4;const _0x174406=a0_0x1744();let _0x49e360=_0x174406[_0x24bd08];if(a0_0x49e3['VpqZLv']===undefined){var _0x254efa=function(_0xd1d270){const _0x23ebf4='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x54bfb9='',_0x56d882='';for(let _0x4fcc97=0x0,_0x2afc71,_0x4272f3,_0x5051b3=0x0;_0x4272f3=_0xd1d270['charAt'](_0x5051b3++);~_0x4272f3&&(_0x2afc71=_0x4fcc97%0x4?_0x2afc71*0x40+_0x4272f3:_0x4272f3,_0x4fcc97++%0x4)?_0x54bfb9+=String['fromCharCode'](0xff&_0x2afc71>>(-0x2*_0x4fcc97&0x6)):0x0){_0x4272f3=_0x23ebf4['indexOf'](_0x4272f3);}for(let _0x538b23=0x0,_0x542ee8=_0x54bfb9['length'];_0x538b23<_0x542ee8;_0x538b23++){_0x56d882+='%'+('00'+_0x54bfb9['charCodeAt'](_0x538b23)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x56d882);};a0_0x49e3['yFBIIu']=_0x254efa,a0_0x49e3['CrWOJu']={},a0_0x49e3['VpqZLv']=!![];}const _0x30f09e=_0x174406[0x0],_0x1a58de=_0x24bd08+_0x30f09e,_0x1a0fa8=a0_0x49e3['CrWOJu'][_0x1a58de];return!_0x1a0fa8?(_0x49e360=a0_0x49e3['yFBIIu'](_0x49e360),a0_0x49e3['CrWOJu'][_0x1a58de]=_0x49e360):_0x49e360=_0x1a0fa8,_0x49e360;}class WebTool extends BaseTool{constructor(_0x5051b3={},_0x538b23=null){const _0x15327b=a0_0x49e3;super(_0x5051b3,_0x538b23),this['requiresProject']=![],this[_0x15327b(0x219)]=!![],this[_0x15327b(0x250)]=null,this['browserInitializing']=![],this[_0x15327b(0x21c)]=new Map(),this['searchEngines']=[{'name':_0x15327b(0x206),'url':'https://www.google.com/search?q=','searchSelector':_0x15327b(0x20f),'submitSelector':'input[type=\x22submit\x22],\x20button[type=\x22submit\x22]','resultsSelector':'#search\x20.g\x20a,\x20#search\x20a[href]','waitSelector':'#search'},{'name':'bing','url':'https://www.bing.com/search?q=','searchSelector':_0x15327b(0x20f),'submitSelector':'input[type=\x22submit\x22]','resultsSelector':'.b_algo\x20a','waitSelector':_0x15327b(0x255)},{'name':'duckduckgo','url':_0x15327b(0x203),'searchSelector':_0x15327b(0x20f),'submitSelector':_0x15327b(0x224),'resultsSelector':_0x15327b(0x233),'waitSelector':_0x15327b(0x20a)}],this['TAB_IDLE_TIMEOUT']=_0x5051b3[_0x15327b(0x25c)]||0x3c*0x3c*0x3e8,this[_0x15327b(0x28d)]=_0x5051b3[_0x15327b(0x23e)]||0x5*0x3c*0x3e8,this['DEFAULT_TIMEOUT']=_0x5051b3[_0x15327b(0x201)]||0xea60,this['TEMP_DIR']=_0x5051b3[_0x15327b(0x225)]||a0_0x4fcc97[_0x15327b(0x223)](a0_0x4272f3[_0x15327b(0x280)](),'webtool-screenshots'),this['cleanupTimer']=null,this[_0x15327b(0x214)](),this[_0x15327b(0x22a)]();}['getDescription'](){const _0x111fa1=a0_0x49e3;return _0x111fa1(0x29c);}[a0_0x26eeb0(0x1fe)](_0x542ee8){const _0x4a979f=a0_0x26eeb0;try{if(_0x542ee8['trim']()['startsWith']('{'))return JSON[_0x4a979f(0x285)](_0x542ee8);const _0x24849f={},_0x1a41d9=a0_0x54bfb9[_0x4a979f(0x251)](_0x542ee8,'operation');_0x1a41d9[_0x4a979f(0x21b)]>0x0&&(_0x24849f[_0x4a979f(0x281)]=_0x1a41d9[0x0]['trim']());switch(_0x24849f['operation']){case _0x4a979f(0x284):_0x24849f['query']=a0_0x54bfb9[_0x4a979f(0x251)](_0x542ee8,_0x4a979f(0x1f6))[0x0]?.['trim'](),_0x24849f[_0x4a979f(0x29a)]=a0_0x54bfb9['extractContent'](_0x542ee8,_0x4a979f(0x29a))[0x0]?.['trim']()||_0x4a979f(0x206);const _0xaf14f6=a0_0x54bfb9['extractContent'](_0x542ee8,_0x4a979f(0x208))[0x0]?.[_0x4a979f(0x293)]();_0x24849f[_0x4a979f(0x22c)]=_0xaf14f6?parseInt(_0xaf14f6,0xa):0xa;break;case'fetch':_0x24849f[_0x4a979f(0x2a1)]=a0_0x54bfb9[_0x4a979f(0x251)](_0x542ee8,'url')[0x0]?.['trim']();const _0x511664=a0_0x54bfb9[_0x4a979f(0x251)](_0x542ee8,'format')[0x0]?.[_0x4a979f(0x293)]();_0x24849f[_0x4a979f(0x210)]=_0x511664?_0x511664['split'](',')[_0x4a979f(0x247)](_0x330c4d=>_0x330c4d['trim']()):['title','text'];break;case'interactive':const _0x49ca53=a0_0x54bfb9['extractContent'](_0x542ee8,_0x4a979f(0x24c))[0x0]?.['trim']();_0x24849f['headless']=_0x49ca53!==_0x4a979f(0x254);const _0x46db5e=a0_0x54bfb9[_0x4a979f(0x251)](_0x542ee8,'actions')[0x0];_0x46db5e&&(_0x24849f['actions']=this['parseActions'](_0x46db5e));break;}return _0x24849f['rawContent']=_0x542ee8[_0x4a979f(0x293)](),_0x24849f;}catch(_0xf043bc){throw new Error(_0x4a979f(0x27f)+_0xf043bc[_0x4a979f(0x24e)]);}}[a0_0x26eeb0(0x246)](_0x1ac870){const _0x1886c8=a0_0x26eeb0,_0x321f25=[],_0x1a349e=/<open-tab[^>]*name="([^"]+)"[^>]*>([\s\S]*?)<\/open-tab>/g;let _0x2e54bd;while((_0x2e54bd=_0x1a349e[_0x1886c8(0x21a)](_0x1ac870))!==null){const [,_0x12797e,_0x4b8578]=_0x2e54bd,_0x150cb1=a0_0x54bfb9['extractContent'](_0x4b8578,_0x1886c8(0x269))[0x0]?.['trim']();_0x321f25[_0x1886c8(0x232)]({'type':_0x1886c8(0x279),'name':_0x12797e,'url':_0x150cb1,'nestedActions':this[_0x1886c8(0x1fd)](_0x4b8578)});}const _0x13a48b=[_0x1886c8(0x294),'switch-tab','list-tabs','navigate','click',_0x1886c8(0x260),_0x1886c8(0x243),_0x1886c8(0x26e),_0x1886c8(0x26b),'analyze-screenshot',_0x1886c8(0x27d),'extract-links',_0x1886c8(0x237),_0x1886c8(0x295),'scroll','hover',_0x1886c8(0x221)];for(const _0x59932d of _0x13a48b){const _0x2e4321=new RegExp('<'+_0x59932d+'([^>]*)>([^<]*)</'+_0x59932d+'>','g');let _0xf522ea;while((_0xf522ea=_0x2e4321['exec'](_0x1ac870))!==null){const [,_0x52d6fb,_0x184d78]=_0xf522ea,_0x1dcf11={'type':_0x59932d},_0x545934=/(\w+(?:-\w+)*)="([^"]*)"/g;let _0x4eb493;while((_0x4eb493=_0x545934['exec'](_0x52d6fb))!==null){_0x1dcf11[_0x4eb493[0x1]]=_0x4eb493[0x2];}_0x184d78&&_0x184d78['trim']()&&(_0x1dcf11['value']=_0x184d78[_0x1886c8(0x293)]()),_0x321f25['push'](_0x1dcf11);}}return _0x321f25;}[a0_0x26eeb0(0x1fd)](_0x42c3f8){const _0x5f305c=a0_0x26eeb0,_0x53d81d=[],_0xd35f41=['navigate','click',_0x5f305c(0x260),_0x5f305c(0x243),_0x5f305c(0x26e),'screenshot',_0x5f305c(0x29b),_0x5f305c(0x27d),_0x5f305c(0x271),_0x5f305c(0x237),_0x5f305c(0x295),'scroll',_0x5f305c(0x257),_0x5f305c(0x221)];for(const _0x4a16b5 of _0xd35f41){const _0x2910d1=new RegExp('<'+_0x4a16b5+'([^>]*)>([^<]*)</'+_0x4a16b5+_0x5f305c(0x253)+_0x4a16b5+'([^>]*)/>','g');let _0x35c688;while((_0x35c688=_0x2910d1[_0x5f305c(0x21a)](_0x42c3f8))!==null){const [,_0x53a6b5,_0x2cedb3,_0x48f4e1]=_0x35c688,_0x22abc7=_0x53a6b5||_0x48f4e1||'',_0x39bd08={'type':_0x4a16b5},_0x160148=/(\w+(?:-\w+)*)="([^"]*)"/g;let _0x261e36;while((_0x261e36=_0x160148['exec'](_0x22abc7))!==null){_0x39bd08[_0x261e36[0x1]]=_0x261e36[0x2];}_0x2cedb3&&_0x2cedb3[_0x5f305c(0x293)]()&&(_0x39bd08[_0x5f305c(0x21e)]=_0x2cedb3[_0x5f305c(0x293)]()),_0x53d81d['push'](_0x39bd08);}}return _0x53d81d;}[a0_0x26eeb0(0x207)](){const _0x109bb9=a0_0x26eeb0;return[_0x109bb9(0x281)];}[a0_0x26eeb0(0x22e)](_0x4a096d){const _0x90420d=a0_0x26eeb0,_0x2e85ef=[];if(![_0x90420d(0x284),_0x90420d(0x242),'interactive'][_0x90420d(0x23b)](_0x4a096d[_0x90420d(0x281)]))return _0x2e85ef[_0x90420d(0x232)]('operation\x20must\x20be\x20one\x20of:\x20search,\x20fetch,\x20interactive'),{'valid':![],'errors':_0x2e85ef};switch(_0x4a096d['operation']){case'search':!_0x4a096d[_0x90420d(0x1f6)]&&_0x2e85ef['push'](_0x90420d(0x24b));break;case'fetch':!_0x4a096d['url']&&_0x2e85ef[_0x90420d(0x232)](_0x90420d(0x235));break;case'interactive':(!_0x4a096d['actions']||!Array['isArray'](_0x4a096d['actions'])||_0x4a096d[_0x90420d(0x241)][_0x90420d(0x21b)]===0x0)&&_0x2e85ef[_0x90420d(0x232)]('actions\x20array\x20is\x20required\x20for\x20interactive\x20operation');break;}return{'valid':_0x2e85ef['length']===0x0,'errors':_0x2e85ef};}async['execute'](_0x2f108c,_0x38f353){const _0x47b260=a0_0x26eeb0,{operation:_0x5cf177}=_0x2f108c,{agentId:_0x18fe26}=_0x38f353;try{await this[_0x47b260(0x216)]();let _0x4b22b3;switch(_0x5cf177){case _0x47b260(0x284):_0x4b22b3=await this[_0x47b260(0x284)](_0x2f108c['query'],{'engine':_0x2f108c['engine']||'google','maxResults':_0x2f108c[_0x47b260(0x22c)]||0xa,'agentId':_0x18fe26});break;case _0x47b260(0x242):_0x4b22b3=await this['fetch'](_0x2f108c['url'],{'formats':_0x2f108c[_0x47b260(0x210)]||[_0x47b260(0x27e),_0x47b260(0x268)],'agentId':_0x18fe26});break;case _0x47b260(0x202):_0x4b22b3=await this[_0x47b260(0x202)](_0x2f108c[_0x47b260(0x241)],{'headless':_0x2f108c[_0x47b260(0x24c)]!==![],'agentId':_0x18fe26,'context':_0x38f353});break;default:throw new Error(_0x47b260(0x277)+_0x5cf177);}return{'success':!![],'operation':_0x5cf177,'result':_0x4b22b3,'toolUsed':_0x47b260(0x273)};}catch(_0x28ccab){return this[_0x47b260(0x1fb)]?.[_0x47b260(0x25b)]('Web\x20tool\x20execution\x20failed',{'operation':_0x5cf177,'error':_0x28ccab[_0x47b260(0x24e)],'agentId':_0x18fe26}),{'success':![],'operation':_0x5cf177,'error':_0x28ccab[_0x47b260(0x24e)],'toolUsed':'web'};}}async[a0_0x26eeb0(0x216)](){const _0xca9717=a0_0x26eeb0;if(this[_0xca9717(0x250)]&&this['browser'][_0xca9717(0x23c)]())return;if(this['browserInitializing']){while(this[_0xca9717(0x283)]){await new Promise(_0x1c8185=>setTimeout(_0x1c8185,0x64));}return;}this['browserInitializing']=!![];try{this[_0xca9717(0x1fb)]?.['info'](_0xca9717(0x262)),this[_0xca9717(0x250)]=await a0_0x56d882['launch']({'headless':_0xca9717(0x23d),'args':[_0xca9717(0x23a),'--disable-setuid-sandbox',_0xca9717(0x27a),_0xca9717(0x25e),_0xca9717(0x28b),_0xca9717(0x22b),'--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['logger']?.[_0xca9717(0x1f7)]('Puppeteer\x20browser\x20initialized\x20successfully');}catch(_0x4d02cc){this[_0xca9717(0x1fb)]?.[_0xca9717(0x25b)](_0xca9717(0x296),{'error':_0x4d02cc['message']});throw new Error(_0xca9717(0x1f5)+_0x4d02cc[_0xca9717(0x24e)]);}finally{this['browserInitializing']=![];}}async['search'](_0x10534b,_0x34e159={}){const _0x2896ff=a0_0x26eeb0,{engine:engine='google',maxResults:maxResults=0xa,agentId:_0x598273}=_0x34e159;if(!_0x10534b||typeof _0x10534b!==_0x2896ff(0x290)||_0x10534b[_0x2896ff(0x293)]()['length']===0x0)throw new Error('Search\x20query\x20is\x20required\x20and\x20must\x20be\x20a\x20non-empty\x20string');const _0x4fcb1f=this[_0x2896ff(0x20e)]['find'](_0x50e3e2=>_0x50e3e2['name']===engine);if(!_0x4fcb1f)throw new Error('Unknown\x20search\x20engine:\x20'+engine+'.\x20Available:\x20'+this['searchEngines']['map'](_0x1126f9=>_0x1126f9['name'])['join'](',\x20'));await this[_0x2896ff(0x216)](),this['logger']?.['info']('Performing\x20web\x20search',{'query':_0x10534b,'engine':engine,'agentId':_0x598273});const _0x173700=await this['browser'][_0x2896ff(0x28c)]();try{const _0x4f62c5=''+_0x4fcb1f[_0x2896ff(0x2a1)]+encodeURIComponent(_0x10534b);await _0x173700['goto'](_0x4f62c5,{'waitUntil':'networkidle2','timeout':this['DEFAULT_TIMEOUT']}),await _0x173700[_0x2896ff(0x29e)](_0x4fcb1f[_0x2896ff(0x228)],{'timeout':this['DEFAULT_TIMEOUT']});const _0x53b897=await _0x173700[_0x2896ff(0x265)]((_0x2e469c,_0x2de35a)=>{const _0x3b6682=_0x2896ff,_0x11f5a1=Array['from'](document['querySelectorAll'](_0x2e469c));return _0x11f5a1['slice'](0x0,_0x2de35a)['map'](_0x4a473f=>({'url':_0x4a473f[_0x3b6682(0x298)],'title':_0x4a473f['textContent'][_0x3b6682(0x293)](),'description':_0x4a473f[_0x3b6682(0x200)]('.g,\x20.b_algo,\x20.result')?.['textContent'][_0x3b6682(0x293)]()||''}))['filter'](_0x54a2fd=>_0x54a2fd['url']&&_0x54a2fd[_0x3b6682(0x2a1)]['startsWith'](_0x3b6682(0x1ff)));},_0x4fcb1f['resultsSelector'],maxResults);return this['logger']?.['info']('Search\x20completed',{'resultsCount':_0x53b897[_0x2896ff(0x21b)],'agentId':_0x598273}),{'success':!![],'query':_0x10534b,'engine':engine,'resultsCount':_0x53b897[_0x2896ff(0x21b)],'results':_0x53b897};}finally{await _0x173700[_0x2896ff(0x205)]();}}async['fetch'](_0x5b2d26,_0x308c0f={}){const _0xc68ea5=a0_0x26eeb0,{formats:formats=['title','text'],agentId:_0x348b0e}=_0x308c0f;await this[_0xc68ea5(0x216)](),this[_0xc68ea5(0x1fb)]?.[_0xc68ea5(0x1f7)](_0xc68ea5(0x244),{'url':_0x5b2d26,'formats':formats,'agentId':_0x348b0e});const _0x9c3d5e=await this['browser']['newPage']();try{const _0x47a029=[];formats[_0xc68ea5(0x23b)](_0xc68ea5(0x29f))&&_0x9c3d5e['on']('console',_0x2d95fb=>{const _0x2d24b6=_0xc68ea5;_0x47a029['push']({'type':_0x2d95fb[_0x2d24b6(0x260)](),'text':_0x2d95fb[_0x2d24b6(0x268)]()});});await _0x9c3d5e[_0xc68ea5(0x212)](_0x5b2d26,{'waitUntil':'networkidle2','timeout':this['DEFAULT_TIMEOUT']});const _0x2c1acd={'url':_0x5b2d26};for(const _0x29b693 of formats){switch(_0x29b693){case'title':_0x2c1acd[_0xc68ea5(0x27e)]=await _0x9c3d5e['title']();break;case'text':_0x2c1acd['text']=await _0x9c3d5e[_0xc68ea5(0x265)](()=>document[_0xc68ea5(0x28e)]['innerText']);break;case _0xc68ea5(0x217):_0x2c1acd['links']=await _0x9c3d5e[_0xc68ea5(0x265)](()=>{const _0x1b33c3=_0xc68ea5;return Array['from'](document['querySelectorAll'](_0x1b33c3(0x239)))[_0x1b33c3(0x247)](_0x104de4=>({'href':_0x104de4['href'],'text':_0x104de4['textContent']['trim']()}));});break;case'html':_0x2c1acd[_0xc68ea5(0x209)]=await _0x9c3d5e['content']();break;case _0xc68ea5(0x29f):_0x2c1acd['consoleMessages']=_0x47a029;break;}}return this[_0xc68ea5(0x1fb)]?.[_0xc68ea5(0x1f7)]('Fetch\x20completed',{'url':_0x5b2d26,'formats':formats,'agentId':_0x348b0e}),{'success':!![],..._0x2c1acd};}finally{await _0x9c3d5e['close']();}}async['interactive'](_0x4aa965,_0x8f654b={}){const _0x24bcaa=a0_0x26eeb0,{headless:headless=!![],agentId:_0x3a2e9a,context:_0x4afe3b}=_0x8f654b;await this['ensureBrowser'](),this[_0x24bcaa(0x1fb)]?.[_0x24bcaa(0x1f7)]('Starting\x20interactive\x20session',{'actionsCount':_0x4aa965['length'],'headless':headless,'agentId':_0x3a2e9a});const _0x180005=[];!this['agentTabs']['has'](_0x3a2e9a)&&this[_0x24bcaa(0x21c)][_0x24bcaa(0x1f8)](_0x3a2e9a,new Map());const _0x5ba215=this[_0x24bcaa(0x21c)][_0x24bcaa(0x28a)](_0x3a2e9a);for(const _0x26f9a8 of _0x4aa965){try{let _0x273cb2;switch(_0x26f9a8[_0x24bcaa(0x260)]){case _0x24bcaa(0x279):_0x273cb2=await this[_0x24bcaa(0x1f9)](_0x3a2e9a,_0x26f9a8[_0x24bcaa(0x27c)],_0x26f9a8[_0x24bcaa(0x2a1)],headless,_0x26f9a8[_0x24bcaa(0x23f)],_0x4afe3b);break;case'close-tab':_0x273cb2=await this['closeTab'](_0x3a2e9a,_0x26f9a8['name']);break;case _0x24bcaa(0x240):_0x273cb2=await this[_0x24bcaa(0x1fa)](_0x3a2e9a,_0x26f9a8['name']);break;case _0x24bcaa(0x22d):_0x273cb2=await this[_0x24bcaa(0x261)](_0x3a2e9a);break;default:_0x273cb2={'success':![],'error':'Action\x20'+_0x26f9a8['type']+'\x20must\x20be\x20executed\x20within\x20a\x20tab\x20context\x20(use\x20open-tab\x20with\x20nestedActions)'};}_0x180005['push']({'action':_0x26f9a8['type'],..._0x273cb2});}catch(_0x2cb17e){this['logger']?.[_0x24bcaa(0x25b)](_0x24bcaa(0x1fc),{'action':_0x26f9a8[_0x24bcaa(0x260)],'error':_0x2cb17e[_0x24bcaa(0x24e)],'agentId':_0x3a2e9a}),_0x180005['push']({'action':_0x26f9a8['type'],'success':![],'error':_0x2cb17e[_0x24bcaa(0x24e)]});}}return{'success':_0x180005['every'](_0x27deb2=>_0x27deb2[_0x24bcaa(0x258)]!==![]),'actionsExecuted':_0x180005[_0x24bcaa(0x21b)],'results':_0x180005};}async['openTab'](_0x51871d,_0x23544f,_0x3e1683,_0x181e9f,_0x15fc36=[],_0x420ae9={}){const _0x2e70bb=a0_0x26eeb0;!this[_0x2e70bb(0x21c)][_0x2e70bb(0x249)](_0x51871d)&&this['agentTabs']['set'](_0x51871d,new Map());const _0x17049e=this['agentTabs']['get'](_0x51871d);if(_0x17049e['has'](_0x23544f))throw new Error(_0x2e70bb(0x264)+_0x23544f+_0x2e70bb(0x218)+_0x51871d);this[_0x2e70bb(0x1fb)]?.['info']('Opening\x20tab',{'agentId':_0x51871d,'tabName':_0x23544f,'url':_0x3e1683,'headless':_0x181e9f});const _0x5202b0=await this['browser']['newPage']();await _0x5202b0[_0x2e70bb(0x248)]({'width':0x500,'height':0x2d0});const _0x4d9319=[];_0x5202b0['on']('console',_0x9bcf64=>{const _0x34c268=_0x2e70bb;_0x4d9319[_0x34c268(0x232)]({'type':_0x9bcf64[_0x34c268(0x260)](),'text':_0x9bcf64['text'](),'timestamp':Date['now']()});});const _0x58de8f={'page':_0x5202b0,'url':_0x3e1683,'lastActivity':Date['now'](),'headless':_0x181e9f,'consoleMessages':_0x4d9319,'name':_0x23544f};_0x17049e['set'](_0x23544f,_0x58de8f);const _0x33fa0e=[];try{_0x3e1683&&(await _0x5202b0['goto'](_0x3e1683,{'waitUntil':'networkidle2','timeout':this[_0x2e70bb(0x20b)]}),_0x58de8f[_0x2e70bb(0x2a1)]=_0x3e1683,_0x58de8f['lastActivity']=Date['now']());for(const _0x2b6457 of _0x15fc36){const _0x104c38=await this[_0x2e70bb(0x289)](_0x5202b0,_0x2b6457,_0x58de8f,_0x420ae9);_0x33fa0e[_0x2e70bb(0x232)]({'action':_0x2b6457['type'],..._0x104c38}),_0x58de8f[_0x2e70bb(0x286)]=Date['now']();}return{'success':!![],'tabName':_0x23544f,'url':_0x58de8f['url'],'actionsExecuted':_0x33fa0e[_0x2e70bb(0x21b)],'results':_0x33fa0e};}catch(_0x4264ea){this['logger']?.[_0x2e70bb(0x25b)]('Failed\x20to\x20open\x20tab',{'agentId':_0x51871d,'tabName':_0x23544f,'error':_0x4264ea[_0x2e70bb(0x24e)]}),await _0x5202b0[_0x2e70bb(0x205)](),_0x17049e['delete'](_0x23544f);throw _0x4264ea;}}async[a0_0x26eeb0(0x289)](_0x3e9e12,_0x2ed60d,_0x50ab34,_0x1bfcc3){const _0x356d0a=a0_0x26eeb0;switch(_0x2ed60d['type']){case _0x356d0a(0x269):await _0x3e9e12[_0x356d0a(0x212)](_0x2ed60d['value']||_0x2ed60d[_0x356d0a(0x2a1)],{'waitUntil':_0x356d0a(0x215),'timeout':this['DEFAULT_TIMEOUT']}),_0x50ab34[_0x356d0a(0x2a1)]=_0x3e9e12[_0x356d0a(0x2a1)]();return{'success':!![],'url':_0x50ab34['url']};case'click':await _0x3e9e12[_0x356d0a(0x26f)](_0x2ed60d[_0x356d0a(0x267)],{'button':_0x2ed60d[_0x356d0a(0x288)]||_0x356d0a(0x226)});return{'success':!![],'selector':_0x2ed60d[_0x356d0a(0x267)]};case _0x356d0a(0x260):await _0x3e9e12['type'](_0x2ed60d[_0x356d0a(0x267)],_0x2ed60d[_0x356d0a(0x268)]||_0x2ed60d[_0x356d0a(0x21e)]);return{'success':!![],'selector':_0x2ed60d[_0x356d0a(0x267)],'text':_0x2ed60d[_0x356d0a(0x268)]};case _0x356d0a(0x243):await _0x3e9e12['keyboard']['press'](_0x2ed60d['key']||_0x2ed60d[_0x356d0a(0x21e)]);return{'success':!![],'key':_0x2ed60d['key']};case'wait-for':const _0x4754d7=_0x2ed60d[_0x356d0a(0x231)]?parseInt(_0x2ed60d[_0x356d0a(0x231)],0xa):this[_0x356d0a(0x20b)];await _0x3e9e12['waitForSelector'](_0x2ed60d[_0x356d0a(0x267)],{'timeout':_0x4754d7});return{'success':!![],'selector':_0x2ed60d[_0x356d0a(0x267)]};case _0x356d0a(0x26b):return await this[_0x356d0a(0x276)](_0x3e9e12,_0x2ed60d,_0x1bfcc3);case _0x356d0a(0x29b):return await this['analyzeScreenshot'](_0x3e9e12,_0x2ed60d['value'],_0x1bfcc3);case'extract-text':const _0x1d4f5d=await _0x3e9e12['evaluate'](_0x55faab=>{const _0x164473=_0x356d0a,_0x89e3c7=document[_0x164473(0x26a)](_0x55faab);return _0x89e3c7?_0x89e3c7[_0x164473(0x287)]:null;},_0x2ed60d['selector']);return{'success':!![],'selector':_0x2ed60d[_0x356d0a(0x267)],'text':_0x1d4f5d};case'extract-links':const _0x3ba554=await _0x3e9e12['evaluate'](_0xc2cc7a=>{const _0x18cda5=_0x356d0a,_0x2942c0=document[_0x18cda5(0x211)](_0xc2cc7a);return Array['from'](_0x2942c0)['map'](_0x212311=>({'href':_0x212311[_0x18cda5(0x298)],'text':_0x212311[_0x18cda5(0x227)]['trim']()}));},_0x2ed60d[_0x356d0a(0x267)]);return{'success':!![],'selector':_0x2ed60d[_0x356d0a(0x267)],'links':_0x3ba554};case _0x356d0a(0x237):const _0x332ffd=await _0x3e9e12['content']();return{'success':!![],'html':_0x332ffd};case _0x356d0a(0x295):return{'success':!![],'consoleMessages':[..._0x50ab34['consoleMessages']]};case _0x356d0a(0x29d):await _0x3e9e12['evaluate'](_0x5606c8=>{const _0x4b218f=_0x356d0a;_0x5606c8?document['querySelector'](_0x5606c8)?.['scrollIntoView']():window[_0x4b218f(0x24d)](0x0,document['body'][_0x4b218f(0x25d)]);},_0x2ed60d['selector']);return{'success':!![]};case'hover':await _0x3e9e12['hover'](_0x2ed60d['selector']);return{'success':!![],'selector':_0x2ed60d[_0x356d0a(0x267)]};case _0x356d0a(0x221):await _0x3e9e12[_0x356d0a(0x257)](_0x2ed60d['selector']);return{'success':!![],'selector':_0x2ed60d['selector']};default:throw new Error('Unknown\x20action\x20type:\x20'+_0x2ed60d['type']);}}async['takeScreenshot'](_0x148dc6,_0x2ccfc1,_0x4769f8){const _0xe3e346=a0_0x26eeb0,_0x52764f=_0x2ccfc1['format']||_0xe3e346(0x20d),_0x21cb18=_0x2ccfc1[_0xe3e346(0x24a)];if(_0x52764f==='base64'){const _0xb1338a=await _0x148dc6[_0xe3e346(0x26b)]({'encoding':'base64'});return{'success':!![],'format':_0xe3e346(0x275),'screenshot':_0xb1338a};}let _0x16ec5b;if(_0x21cb18){const _0x4538f6=_0x4769f8[_0xe3e346(0x22f)]?.['workingDirectory']||_0x4769f8['projectDir']||process['cwd']();_0x16ec5b=a0_0x4fcc97['isAbsolute'](_0x21cb18)?_0x21cb18:a0_0x4fcc97[_0xe3e346(0x223)](_0x4538f6,_0x21cb18);}else{const _0x1c8308=_0xe3e346(0x26c)+Date[_0xe3e346(0x234)]()+'.png';_0x16ec5b=a0_0x4fcc97[_0xe3e346(0x223)](this[_0xe3e346(0x25f)],_0x1c8308);}return await _0x148dc6[_0xe3e346(0x26b)]({'path':_0x16ec5b}),{'success':!![],'format':'file','path':_0x16ec5b};}async[a0_0x26eeb0(0x252)](_0x11fb65,_0x50c008,_0x5c200f){const _0xe95dc6=a0_0x26eeb0,_0x5d5b7e=await _0x11fb65[_0xe95dc6(0x26b)]({'encoding':_0xe95dc6(0x275)}),_0x4a4220=_0x5c200f[_0xe95dc6(0x230)];if(!_0x4a4220)throw new Error('AI\x20service\x20not\x20available\x20for\x20screenshot\x20analysis');this['logger']?.[_0xe95dc6(0x1f7)]('Analyzing\x20screenshot\x20with\x20AI',{'question':_0x50c008['substring'](0x0,0x64),'agentId':_0x5c200f['agentId']});try{const _0x5f0738='o3',_0x27a698=await _0x4a4220['sendMessage'](_0x5f0738,_0x50c008,{'agentId':_0x5c200f['agentId'],'images':[_0xe95dc6(0x1f4)+_0x5d5b7e],'apiKey':_0x5c200f['apiKey'],'customApiKeys':_0x5c200f[_0xe95dc6(0x24f)],'platformProvided':_0x5c200f[_0xe95dc6(0x229)]});return{'success':!![],'question':_0x50c008,'analysis':_0x27a698['content'],'model':_0x27a698['model']||_0x5f0738};}catch(_0x596d32){this['logger']?.[_0xe95dc6(0x25b)]('Screenshot\x20analysis\x20failed',{'error':_0x596d32[_0xe95dc6(0x24e)],'agentId':_0x5c200f['agentId']});throw new Error(_0xe95dc6(0x274)+_0x596d32[_0xe95dc6(0x24e)]);}}async[a0_0x26eeb0(0x222)](_0x472795,_0x54a882){const _0x1fa9a9=a0_0x26eeb0,_0x55f5da=this[_0x1fa9a9(0x21c)][_0x1fa9a9(0x28a)](_0x472795);if(!_0x55f5da||!_0x55f5da[_0x1fa9a9(0x249)](_0x54a882))throw new Error('Tab\x20\x27'+_0x54a882+_0x1fa9a9(0x28f)+_0x472795);const _0x38b9c7=_0x55f5da['get'](_0x54a882);return this['logger']?.['info']('Closing\x20tab',{'agentId':_0x472795,'tabName':_0x54a882}),await _0x38b9c7['page'][_0x1fa9a9(0x205)](),_0x55f5da['delete'](_0x54a882),{'success':!![],'tabName':_0x54a882,'message':'Tab\x20\x27'+_0x54a882+'\x27\x20closed'};}async['switchTab'](_0x3bbebc,_0x1f2a45){const _0x4b56a5=a0_0x26eeb0,_0x4f6ddb=this[_0x4b56a5(0x21c)][_0x4b56a5(0x28a)](_0x3bbebc);if(!_0x4f6ddb||!_0x4f6ddb[_0x4b56a5(0x249)](_0x1f2a45))throw new Error('Tab\x20\x27'+_0x1f2a45+'\x27\x20not\x20found\x20for\x20agent\x20'+_0x3bbebc);const _0x137416=_0x4f6ddb['get'](_0x1f2a45);return _0x137416['lastActivity']=Date['now'](),{'success':!![],'tabName':_0x1f2a45,'url':_0x137416[_0x4b56a5(0x2a1)],'message':'Switched\x20to\x20tab\x20\x27'+_0x1f2a45+'\x27'};}async[a0_0x26eeb0(0x261)](_0x5f409b){const _0x33919b=a0_0x26eeb0,_0xeb9aa2=this[_0x33919b(0x21c)][_0x33919b(0x28a)](_0x5f409b);if(!_0xeb9aa2||_0xeb9aa2[_0x33919b(0x266)]===0x0)return{'success':!![],'tabCount':0x0,'tabs':[],'message':_0x33919b(0x270)};const _0x16fbec=[];for(const [_0x50757b,_0x500eb8]of _0xeb9aa2[_0x33919b(0x291)]()){_0x16fbec[_0x33919b(0x232)]({'name':_0x50757b,'url':_0x500eb8['url'],'idleTime':Date[_0x33919b(0x234)]()-_0x500eb8[_0x33919b(0x286)],'headless':_0x500eb8['headless']});}return{'success':!![],'tabCount':_0x16fbec[_0x33919b(0x21b)],'tabs':_0x16fbec};}[a0_0x26eeb0(0x214)](){const _0x500887=a0_0x26eeb0;this[_0x500887(0x263)]&&clearInterval(this[_0x500887(0x263)]),this['cleanupTimer']=setInterval(()=>{this['cleanupIdleTabs']();},this['CLEANUP_INTERVAL']);}async['cleanupIdleTabs'](){const _0x296fff=a0_0x26eeb0,_0x55a467=Date['now'](),_0x347417=[];for(const [_0x4e66c4,_0x5993aa]of this['agentTabs'][_0x296fff(0x291)]()){for(const [_0x287f9d,_0x1bf15c]of _0x5993aa['entries']()){const _0xd50663=_0x55a467-_0x1bf15c[_0x296fff(0x286)];_0xd50663>this['TAB_IDLE_TIMEOUT']&&_0x347417[_0x296fff(0x232)]({'agentId':_0x4e66c4,'tabName':_0x287f9d,'tabInfo':_0x1bf15c});}}if(_0x347417[_0x296fff(0x21b)]>0x0){this[_0x296fff(0x1fb)]?.[_0x296fff(0x1f7)]('Cleaning\x20up\x20idle\x20tabs',{'count':_0x347417['length']});for(const {agentId:_0x4b87ca,tabName:_0xf0695a,tabInfo:_0x3cb55f}of _0x347417){try{await _0x3cb55f[_0x296fff(0x297)]['close'](),this['agentTabs'][_0x296fff(0x28a)](_0x4b87ca)[_0x296fff(0x282)](_0xf0695a),this[_0x296fff(0x1fb)]?.[_0x296fff(0x27b)](_0x296fff(0x213),{'agentId':_0x4b87ca,'tabName':_0xf0695a});}catch(_0x183831){this[_0x296fff(0x1fb)]?.[_0x296fff(0x25b)]('Failed\x20to\x20close\x20idle\x20tab',{'agentId':_0x4b87ca,'tabName':_0xf0695a,'error':_0x183831[_0x296fff(0x24e)]});}}}}async[a0_0x26eeb0(0x238)](_0x348352){const _0x3b895c=a0_0x26eeb0,_0x560ce8=this['agentTabs']['get'](_0x348352);if(!_0x560ce8)return{'success':!![],'agentId':_0x348352,'closedTabs':0x0,'message':_0x3b895c(0x259)};this[_0x3b895c(0x1fb)]?.[_0x3b895c(0x1f7)]('Cleaning\x20up\x20agent\x20tabs',{'agentId':_0x348352,'tabCount':_0x560ce8['size']});let _0x3994f1=0x0;for(const [_0x383396,_0x3d1c98]of _0x560ce8[_0x3b895c(0x291)]()){try{await _0x3d1c98[_0x3b895c(0x297)][_0x3b895c(0x205)](),_0x3994f1++;}catch(_0x339858){this[_0x3b895c(0x1fb)]?.['error'](_0x3b895c(0x236),{'agentId':_0x348352,'tabName':_0x383396,'error':_0x339858[_0x3b895c(0x24e)]});}}return this['agentTabs'][_0x3b895c(0x282)](_0x348352),{'success':!![],'agentId':_0x348352,'closedTabs':_0x3994f1,'message':'Closed\x20'+_0x3994f1+'\x20tabs\x20for\x20agent\x20'+_0x348352};}async['ensureTempDir'](){const _0x847c1c=a0_0x26eeb0;try{await a0_0x2afc71['mkdir'](this[_0x847c1c(0x25f)],{'recursive':!![]});}catch(_0x23788e){this['logger']?.['warn'](_0x847c1c(0x20c),{'path':this[_0x847c1c(0x25f)],'error':_0x23788e['message']});}}async[a0_0x26eeb0(0x204)](){const _0x4365d1=a0_0x26eeb0;this[_0x4365d1(0x263)]&&(clearInterval(this[_0x4365d1(0x263)]),this['cleanupTimer']=null);for(const [_0x2ee585]of this['agentTabs'][_0x4365d1(0x291)]()){await this[_0x4365d1(0x238)](_0x2ee585);}this['browser']&&(await this['browser']['close'](),this['browser']=null);try{await a0_0x2afc71['rm'](this['TEMP_DIR'],{'recursive':!![],'force':!![]});}catch(_0x2e56f7){this['logger']?.[_0x4365d1(0x26d)]('Failed\x20to\x20clean\x20temp\x20directory',{'path':this[_0x4365d1(0x25f)],'error':_0x2e56f7['message']});}}}export default WebTool;
|