@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/browserTool.js
CHANGED
|
@@ -1,920 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BrowserTool - Handle browser automation and web interaction tasks
|
|
3
|
-
*
|
|
4
|
-
* Purpose:
|
|
5
|
-
* - Automate web browser interactions
|
|
6
|
-
* - Navigate websites and extract data
|
|
7
|
-
* - Fill forms and interact with web elements
|
|
8
|
-
* - Take screenshots and analyze web content
|
|
9
|
-
* - Handle authentication and sessions
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { BaseTool } from './baseTool.js';
|
|
13
|
-
import TagParser from '../utilities/tagParser.js';
|
|
14
|
-
import puppeteer from 'puppeteer';
|
|
15
|
-
import fs from 'fs/promises';
|
|
16
|
-
import path from 'path';
|
|
17
|
-
|
|
18
|
-
import {
|
|
19
|
-
TOOL_STATUS,
|
|
20
|
-
SYSTEM_DEFAULTS
|
|
21
|
-
} from '../utilities/constants.js';
|
|
22
|
-
|
|
23
|
-
class BrowserTool extends BaseTool {
|
|
24
|
-
constructor(config = {}, logger = null) {
|
|
25
|
-
super(config, logger);
|
|
26
|
-
|
|
27
|
-
// Tool metadata
|
|
28
|
-
this.requiresProject = false;
|
|
29
|
-
this.isAsync = true; // Browser operations can be slow
|
|
30
|
-
this.timeout = config.timeout || 60000; // 1 minute default
|
|
31
|
-
this.maxConcurrentOperations = config.maxConcurrentOperations || 2;
|
|
32
|
-
|
|
33
|
-
// Browser configuration
|
|
34
|
-
this.browserConfig = {
|
|
35
|
-
headless: config.headless !== false, // Default to headless
|
|
36
|
-
viewport: config.viewport || { width: 1280, height: 720 },
|
|
37
|
-
userAgent: config.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
|
38
|
-
timeout: config.pageTimeout || 30000
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
// Active browser instances
|
|
42
|
-
this.browserInstances = new Map();
|
|
43
|
-
|
|
44
|
-
// Security settings
|
|
45
|
-
this.allowedDomains = config.allowedDomains || null; // null = all allowed
|
|
46
|
-
this.blockedDomains = config.blockedDomains || [];
|
|
47
|
-
this.maxScreenshotSize = config.maxScreenshotSize || 5 * 1024 * 1024; // 5MB
|
|
48
|
-
|
|
49
|
-
// Operation history
|
|
50
|
-
this.operationHistory = [];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Get tool description for LLM consumption
|
|
55
|
-
* @returns {string} Tool description
|
|
56
|
-
*/
|
|
57
|
-
getDescription() {
|
|
58
|
-
return `
|
|
59
|
-
Browser Tool: Automate web browser interactions, navigation, and data extraction.
|
|
60
|
-
|
|
61
|
-
IMPORTANT - SYNTAX: Use self-closing tags with angle brackets < >, not square brackets [ ].
|
|
62
|
-
|
|
63
|
-
XML USAGE (Recommended):
|
|
64
|
-
<browser>
|
|
65
|
-
<navigate url="https://example.com" />
|
|
66
|
-
<wait wait-time="2000" />
|
|
67
|
-
<click selector="button.submit" />
|
|
68
|
-
<screenshot output-path="screenshot.png" />
|
|
69
|
-
</browser>
|
|
70
|
-
|
|
71
|
-
JSON USAGE (Alternative):
|
|
72
|
-
\`\`\`json
|
|
73
|
-
{
|
|
74
|
-
"toolId": "browser",
|
|
75
|
-
"actions": [
|
|
76
|
-
{ "type": "navigate", "url": "https://example.com" },
|
|
77
|
-
{ "type": "wait", "waitTime": 2000 },
|
|
78
|
-
{ "type": "click", "selector": "button.submit" },
|
|
79
|
-
{ "type": "screenshot", "outputPath": "screenshot.png" }
|
|
80
|
-
]
|
|
81
|
-
}
|
|
82
|
-
\`\`\`
|
|
83
|
-
|
|
84
|
-
SUPPORTED ACTIONS:
|
|
85
|
-
- navigate: Navigate to a URL (requires: url)
|
|
86
|
-
- click: Click on an element (requires: selector)
|
|
87
|
-
- type: Type text into an input field (requires: selector, text)
|
|
88
|
-
- wait: Wait for element or time (requires: wait-time OR wait-selector)
|
|
89
|
-
- screenshot: Take screenshot of page (requires: output-path)
|
|
90
|
-
- extract: Extract text/data from elements (requires: selector, attribute)
|
|
91
|
-
- scroll: Scroll page or element (requires: direction)
|
|
92
|
-
- close: Close browser instance (no parameters)
|
|
93
|
-
|
|
94
|
-
PARAMETER REFERENCE:
|
|
95
|
-
- url: Target URL for navigation
|
|
96
|
-
- selector: CSS selector for element targeting (e.g., "#id", ".class", "button[type='submit']")
|
|
97
|
-
- text: Text content to type
|
|
98
|
-
- output-path: File path for screenshots (kebab-case in XML, outputPath in JSON)
|
|
99
|
-
- wait-time: Time to wait in milliseconds (100-30000, kebab-case in XML, waitTime in JSON)
|
|
100
|
-
- wait-selector: Element selector to wait for (kebab-case in XML, waitSelector in JSON)
|
|
101
|
-
- wait-type: Wait type - "visible" or "hidden" (default: "visible")
|
|
102
|
-
- attribute: HTML attribute to extract (default: "text")
|
|
103
|
-
- direction: Scroll direction - "up", "down", "left", "right"
|
|
104
|
-
- pixels: Scroll distance in pixels (default: 300)
|
|
105
|
-
- full-page: Take full page screenshot - "true" or "false"
|
|
106
|
-
|
|
107
|
-
EXAMPLES:
|
|
108
|
-
|
|
109
|
-
Example 1 - Research and screenshot:
|
|
110
|
-
<browser>
|
|
111
|
-
<navigate url="https://github.com/trending" />
|
|
112
|
-
<wait wait-time="2000" />
|
|
113
|
-
<screenshot output-path="trending.png" />
|
|
114
|
-
<extract selector="h2.h3" attribute="text" />
|
|
115
|
-
<close />
|
|
116
|
-
</browser>
|
|
117
|
-
|
|
118
|
-
Example 2 - Form automation:
|
|
119
|
-
<browser>
|
|
120
|
-
<navigate url="https://example.com/login" />
|
|
121
|
-
<wait wait-selector="input#username" />
|
|
122
|
-
<type selector="input#username" text="myuser" />
|
|
123
|
-
<type selector="input#password" text="mypass" />
|
|
124
|
-
<click selector="button[type='submit']" />
|
|
125
|
-
<wait wait-selector=".success-message" />
|
|
126
|
-
<close />
|
|
127
|
-
</browser>
|
|
128
|
-
|
|
129
|
-
Example 3 - Data extraction:
|
|
130
|
-
<browser>
|
|
131
|
-
<navigate url="https://example.com/products" />
|
|
132
|
-
<wait wait-time="1000" />
|
|
133
|
-
<extract selector="h1.product-title" attribute="text" />
|
|
134
|
-
<extract selector="span.price" attribute="text" />
|
|
135
|
-
<extract selector="img.product" attribute="src" />
|
|
136
|
-
<close />
|
|
137
|
-
</browser>
|
|
138
|
-
|
|
139
|
-
Example 4 - Scrolling and waiting:
|
|
140
|
-
<browser>
|
|
141
|
-
<navigate url="https://example.com/long-page" />
|
|
142
|
-
<scroll direction="down" pixels="500" />
|
|
143
|
-
<wait wait-time="1000" />
|
|
144
|
-
<scroll direction="down" pixels="500" />
|
|
145
|
-
<screenshot output-path="scrolled.png" full-page="true" />
|
|
146
|
-
<close />
|
|
147
|
-
</browser>
|
|
148
|
-
|
|
149
|
-
SYNTAX RULES:
|
|
150
|
-
✅ DO: <navigate url="..." /> (self-closing with />)
|
|
151
|
-
✅ DO: <navigate url="..."> (can omit /> if preferred)
|
|
152
|
-
❌ DON'T: <navigate url="..."></navigate> (no closing tag needed)
|
|
153
|
-
❌ DON'T: [navigate url="..." /] (square brackets not supported)
|
|
154
|
-
|
|
155
|
-
ATTRIBUTE NAMING:
|
|
156
|
-
- XML uses kebab-case: wait-time, output-path, wait-selector
|
|
157
|
-
- JSON uses camelCase: waitTime, outputPath, waitSelector
|
|
158
|
-
- Parser auto-converts kebab-case to camelCase internally
|
|
159
|
-
|
|
160
|
-
SECURITY:
|
|
161
|
-
- Restricted to allowed domains (if configured)
|
|
162
|
-
- Blocked domains enforced for security
|
|
163
|
-
- Screenshot size limits enforced (max 5MB)
|
|
164
|
-
- Automatic cleanup of browser instances after 10 minutes
|
|
165
|
-
- Sandboxed browser execution
|
|
166
|
-
|
|
167
|
-
BROWSER SESSION:
|
|
168
|
-
The tool maintains browser instances per agent context.
|
|
169
|
-
Use <close /> action to explicitly cleanup, or instances auto-cleanup after timeout.
|
|
170
|
-
|
|
171
|
-
VIEWPORT: ${this.browserConfig.viewport.width}x${this.browserConfig.viewport.height}
|
|
172
|
-
TIMEOUT: ${this.browserConfig.timeout}ms per operation
|
|
173
|
-
MAX WAIT TIME: 30 seconds (30000ms)
|
|
174
|
-
`;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Parse parameters from tool command content
|
|
179
|
-
* @param {string} content - Raw tool command content
|
|
180
|
-
* @returns {Object} Parsed parameters
|
|
181
|
-
*/
|
|
182
|
-
parseParameters(content) {
|
|
183
|
-
try {
|
|
184
|
-
// Try JSON first (for JSON in code blocks)
|
|
185
|
-
if (content.trim().startsWith('{')) {
|
|
186
|
-
const parsed = JSON.parse(content);
|
|
187
|
-
// Ensure actions is an array
|
|
188
|
-
if (!parsed.actions) {
|
|
189
|
-
parsed.actions = [];
|
|
190
|
-
}
|
|
191
|
-
if (!Array.isArray(parsed.actions)) {
|
|
192
|
-
parsed.actions = [parsed.actions];
|
|
193
|
-
}
|
|
194
|
-
return parsed;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const params = {};
|
|
198
|
-
const actions = [];
|
|
199
|
-
|
|
200
|
-
// Extract self-closing tags (e.g., <navigate url="..." />)
|
|
201
|
-
const selfClosingTags = [
|
|
202
|
-
'navigate', 'click', 'type', 'wait', 'screenshot',
|
|
203
|
-
'extract', 'scroll', 'close'
|
|
204
|
-
];
|
|
205
|
-
|
|
206
|
-
for (const tagName of selfClosingTags) {
|
|
207
|
-
// Extract self-closing tags using regex
|
|
208
|
-
const selfClosingPattern = new RegExp(
|
|
209
|
-
`<${tagName}\\s+([^>]*?)\\s*\\/?>`,
|
|
210
|
-
'gi'
|
|
211
|
-
);
|
|
212
|
-
|
|
213
|
-
let match;
|
|
214
|
-
while ((match = selfClosingPattern.exec(content)) !== null) {
|
|
215
|
-
const attributeString = match[1];
|
|
216
|
-
const attributes = this.parseAttributes(attributeString);
|
|
217
|
-
|
|
218
|
-
const action = {
|
|
219
|
-
type: tagName,
|
|
220
|
-
...attributes
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
// Normalize common attribute names (kebab-case to camelCase)
|
|
224
|
-
if (action['output-path']) {
|
|
225
|
-
action.outputPath = action['output-path'];
|
|
226
|
-
delete action['output-path'];
|
|
227
|
-
}
|
|
228
|
-
if (action['wait-time']) {
|
|
229
|
-
action.waitTime = parseInt(action['wait-time'], 10);
|
|
230
|
-
delete action['wait-time'];
|
|
231
|
-
}
|
|
232
|
-
if (action['wait-selector']) {
|
|
233
|
-
action.waitSelector = action['wait-selector'];
|
|
234
|
-
delete action['wait-selector'];
|
|
235
|
-
}
|
|
236
|
-
if (action['wait-type']) {
|
|
237
|
-
action.waitType = action['wait-type'];
|
|
238
|
-
delete action['wait-type'];
|
|
239
|
-
}
|
|
240
|
-
if (action['full-page']) {
|
|
241
|
-
action.fullPage = action['full-page'] === 'true';
|
|
242
|
-
delete action['full-page'];
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
actions.push(action);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Ensure actions is always an array (even if empty)
|
|
250
|
-
params.actions = Array.isArray(actions) ? actions : [];
|
|
251
|
-
params.rawContent = content.trim();
|
|
252
|
-
|
|
253
|
-
return params;
|
|
254
|
-
|
|
255
|
-
} catch (error) {
|
|
256
|
-
// If JSON parsing fails, return a structure with empty actions
|
|
257
|
-
// This prevents "actions is not iterable" errors
|
|
258
|
-
this.logger?.error('Failed to parse browser parameters', { error: error.message });
|
|
259
|
-
return {
|
|
260
|
-
actions: [], // Always return an array
|
|
261
|
-
rawContent: content.trim(),
|
|
262
|
-
parseError: error.message
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Parse attributes from attribute string
|
|
269
|
-
* @param {string} attributeString - String containing attributes
|
|
270
|
-
* @returns {Object} Parsed attributes object
|
|
271
|
-
* @private
|
|
272
|
-
*/
|
|
273
|
-
parseAttributes(attributeString) {
|
|
274
|
-
const attributes = {};
|
|
275
|
-
|
|
276
|
-
if (!attributeString) return attributes;
|
|
277
|
-
|
|
278
|
-
// Match attribute="value" or attribute='value'
|
|
279
|
-
const attrPattern = /([\w-]+)\s*=\s*["']([^"']*)["']/g;
|
|
280
|
-
let match;
|
|
281
|
-
|
|
282
|
-
while ((match = attrPattern.exec(attributeString)) !== null) {
|
|
283
|
-
const attrName = match[1];
|
|
284
|
-
const attrValue = match[2];
|
|
285
|
-
attributes[attrName] = attrValue;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
return attributes;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Get required parameters
|
|
293
|
-
* @returns {Array<string>} Array of required parameter names
|
|
294
|
-
*/
|
|
295
|
-
getRequiredParameters() {
|
|
296
|
-
return ['actions'];
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Custom parameter validation
|
|
301
|
-
* @param {Object} params - Parameters to validate
|
|
302
|
-
* @returns {Object} Validation result
|
|
303
|
-
*/
|
|
304
|
-
customValidateParameters(params) {
|
|
305
|
-
const errors = [];
|
|
306
|
-
|
|
307
|
-
if (!params.actions || !Array.isArray(params.actions) || params.actions.length === 0) {
|
|
308
|
-
errors.push('At least one action is required');
|
|
309
|
-
} else {
|
|
310
|
-
// Validate each action
|
|
311
|
-
for (const [index, action] of params.actions.entries()) {
|
|
312
|
-
if (!action.type) {
|
|
313
|
-
errors.push(`Action ${index + 1}: type is required`);
|
|
314
|
-
continue;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
switch (action.type) {
|
|
318
|
-
case 'navigate':
|
|
319
|
-
if (!action.url) {
|
|
320
|
-
errors.push(`Action ${index + 1}: url is required for navigate`);
|
|
321
|
-
} else if (!this.isAllowedUrl(action.url)) {
|
|
322
|
-
errors.push(`Action ${index + 1}: URL not allowed: ${action.url}`);
|
|
323
|
-
}
|
|
324
|
-
break;
|
|
325
|
-
|
|
326
|
-
case 'click':
|
|
327
|
-
case 'extract':
|
|
328
|
-
if (!action.selector) {
|
|
329
|
-
errors.push(`Action ${index + 1}: selector is required for ${action.type}`);
|
|
330
|
-
}
|
|
331
|
-
break;
|
|
332
|
-
|
|
333
|
-
case 'type':
|
|
334
|
-
if (!action.selector) {
|
|
335
|
-
errors.push(`Action ${index + 1}: selector is required for type`);
|
|
336
|
-
}
|
|
337
|
-
if (!action.text && action.text !== '') {
|
|
338
|
-
errors.push(`Action ${index + 1}: text is required for type`);
|
|
339
|
-
}
|
|
340
|
-
break;
|
|
341
|
-
|
|
342
|
-
case 'wait':
|
|
343
|
-
if (!action.waitTime && !action.waitSelector) {
|
|
344
|
-
errors.push(`Action ${index + 1}: either wait-time or wait-selector is required`);
|
|
345
|
-
}
|
|
346
|
-
if (action.waitTime && (action.waitTime < 100 || action.waitTime > 30000)) {
|
|
347
|
-
errors.push(`Action ${index + 1}: wait-time must be between 100 and 30000 milliseconds`);
|
|
348
|
-
}
|
|
349
|
-
break;
|
|
350
|
-
|
|
351
|
-
case 'screenshot':
|
|
352
|
-
if (!action.outputPath) {
|
|
353
|
-
errors.push(`Action ${index + 1}: output-path is required for screenshot`);
|
|
354
|
-
}
|
|
355
|
-
break;
|
|
356
|
-
|
|
357
|
-
case 'scroll':
|
|
358
|
-
if (!action.direction) {
|
|
359
|
-
errors.push(`Action ${index + 1}: direction is required for scroll`);
|
|
360
|
-
} else if (!['up', 'down', 'left', 'right'].includes(action.direction)) {
|
|
361
|
-
errors.push(`Action ${index + 1}: invalid scroll direction: ${action.direction}`);
|
|
362
|
-
}
|
|
363
|
-
break;
|
|
364
|
-
|
|
365
|
-
case 'close':
|
|
366
|
-
// No additional validation needed
|
|
367
|
-
break;
|
|
368
|
-
|
|
369
|
-
default:
|
|
370
|
-
errors.push(`Action ${index + 1}: unknown action type: ${action.type}`);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
return {
|
|
376
|
-
valid: errors.length === 0,
|
|
377
|
-
errors
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Execute tool with parsed parameters
|
|
383
|
-
* @param {Object} params - Parsed parameters
|
|
384
|
-
* @param {Object} context - Execution context
|
|
385
|
-
* @returns {Promise<Object>} Execution result
|
|
386
|
-
*/
|
|
387
|
-
async execute(params, context) {
|
|
388
|
-
const { actions } = params;
|
|
389
|
-
const { agentId, projectDir } = context;
|
|
390
|
-
|
|
391
|
-
const browserKey = `${agentId}-${projectDir || 'default'}`;
|
|
392
|
-
const results = [];
|
|
393
|
-
|
|
394
|
-
for (const action of actions) {
|
|
395
|
-
try {
|
|
396
|
-
let result;
|
|
397
|
-
|
|
398
|
-
switch (action.type) {
|
|
399
|
-
case 'navigate':
|
|
400
|
-
result = await this.navigate(action.url, browserKey);
|
|
401
|
-
break;
|
|
402
|
-
|
|
403
|
-
case 'click':
|
|
404
|
-
result = await this.click(action.selector, browserKey);
|
|
405
|
-
break;
|
|
406
|
-
|
|
407
|
-
case 'type':
|
|
408
|
-
result = await this.type(action.selector, action.text, browserKey);
|
|
409
|
-
break;
|
|
410
|
-
|
|
411
|
-
case 'wait':
|
|
412
|
-
result = await this.wait(action, browserKey);
|
|
413
|
-
break;
|
|
414
|
-
|
|
415
|
-
case 'screenshot':
|
|
416
|
-
result = await this.screenshot(action.outputPath, projectDir, browserKey, action);
|
|
417
|
-
break;
|
|
418
|
-
|
|
419
|
-
case 'extract':
|
|
420
|
-
result = await this.extract(action.selector, action.attribute || 'text', browserKey);
|
|
421
|
-
break;
|
|
422
|
-
|
|
423
|
-
case 'scroll':
|
|
424
|
-
result = await this.scroll(action, browserKey);
|
|
425
|
-
break;
|
|
426
|
-
|
|
427
|
-
case 'close':
|
|
428
|
-
result = await this.closeBrowser(browserKey);
|
|
429
|
-
break;
|
|
430
|
-
|
|
431
|
-
default:
|
|
432
|
-
throw new Error(`Unknown action type: ${action.type}`);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
results.push(result);
|
|
436
|
-
this.addToHistory(action, result, agentId);
|
|
437
|
-
|
|
438
|
-
} catch (error) {
|
|
439
|
-
const errorResult = {
|
|
440
|
-
success: false,
|
|
441
|
-
action: action.type,
|
|
442
|
-
error: error.message,
|
|
443
|
-
selector: action.selector || action.url
|
|
444
|
-
};
|
|
445
|
-
|
|
446
|
-
results.push(errorResult);
|
|
447
|
-
this.addToHistory(action, errorResult, agentId);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
return {
|
|
452
|
-
success: true,
|
|
453
|
-
actions: results,
|
|
454
|
-
executedActions: actions.length,
|
|
455
|
-
toolUsed: 'browser'
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Navigate to URL
|
|
461
|
-
* @private
|
|
462
|
-
*/
|
|
463
|
-
async navigate(url, browserKey) {
|
|
464
|
-
const browser = await this.getBrowser(browserKey);
|
|
465
|
-
const page = await this.getPage(browserKey);
|
|
466
|
-
|
|
467
|
-
const startTime = Date.now();
|
|
468
|
-
|
|
469
|
-
try {
|
|
470
|
-
await page.goto(url, {
|
|
471
|
-
waitUntil: 'networkidle0',
|
|
472
|
-
timeout: this.browserConfig.timeout
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
const title = await page.title();
|
|
476
|
-
const currentUrl = page.url();
|
|
477
|
-
|
|
478
|
-
return {
|
|
479
|
-
success: true,
|
|
480
|
-
action: 'navigate',
|
|
481
|
-
url: currentUrl,
|
|
482
|
-
originalUrl: url,
|
|
483
|
-
title,
|
|
484
|
-
loadTime: Date.now() - startTime,
|
|
485
|
-
message: `Navigated to ${url}`
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
} catch (error) {
|
|
489
|
-
throw new Error(`Navigation failed: ${error.message}`);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* Click on element
|
|
495
|
-
* @private
|
|
496
|
-
*/
|
|
497
|
-
async click(selector, browserKey) {
|
|
498
|
-
const page = await this.getPage(browserKey);
|
|
499
|
-
|
|
500
|
-
try {
|
|
501
|
-
await page.waitForSelector(selector, { timeout: 5000 });
|
|
502
|
-
await page.click(selector);
|
|
503
|
-
|
|
504
|
-
return {
|
|
505
|
-
success: true,
|
|
506
|
-
action: 'click',
|
|
507
|
-
selector,
|
|
508
|
-
message: `Clicked on ${selector}`
|
|
509
|
-
};
|
|
510
|
-
|
|
511
|
-
} catch (error) {
|
|
512
|
-
throw new Error(`Click failed on ${selector}: ${error.message}`);
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* Type text into element
|
|
518
|
-
* @private
|
|
519
|
-
*/
|
|
520
|
-
async type(selector, text, browserKey) {
|
|
521
|
-
const page = await this.getPage(browserKey);
|
|
522
|
-
|
|
523
|
-
try {
|
|
524
|
-
await page.waitForSelector(selector, { timeout: 5000 });
|
|
525
|
-
await page.focus(selector);
|
|
526
|
-
await page.keyboard.selectAll();
|
|
527
|
-
await page.type(selector, text);
|
|
528
|
-
|
|
529
|
-
return {
|
|
530
|
-
success: true,
|
|
531
|
-
action: 'type',
|
|
532
|
-
selector,
|
|
533
|
-
text,
|
|
534
|
-
textLength: text.length,
|
|
535
|
-
message: `Typed ${text.length} characters into ${selector}`
|
|
536
|
-
};
|
|
537
|
-
|
|
538
|
-
} catch (error) {
|
|
539
|
-
throw new Error(`Type failed on ${selector}: ${error.message}`);
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
/**
|
|
544
|
-
* Wait for condition
|
|
545
|
-
* @private
|
|
546
|
-
*/
|
|
547
|
-
async wait(action, browserKey) {
|
|
548
|
-
const page = await this.getPage(browserKey);
|
|
549
|
-
|
|
550
|
-
try {
|
|
551
|
-
if (action.waitTime) {
|
|
552
|
-
await page.waitForTimeout(action.waitTime);
|
|
553
|
-
return {
|
|
554
|
-
success: true,
|
|
555
|
-
action: 'wait',
|
|
556
|
-
waitTime: action.waitTime,
|
|
557
|
-
message: `Waited ${action.waitTime}ms`
|
|
558
|
-
};
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
if (action.waitSelector) {
|
|
562
|
-
const waitType = action.waitType || 'visible';
|
|
563
|
-
const options = { timeout: 10000 };
|
|
564
|
-
|
|
565
|
-
if (waitType === 'hidden') {
|
|
566
|
-
await page.waitForSelector(action.waitSelector, { ...options, hidden: true });
|
|
567
|
-
} else {
|
|
568
|
-
await page.waitForSelector(action.waitSelector, options);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
return {
|
|
572
|
-
success: true,
|
|
573
|
-
action: 'wait',
|
|
574
|
-
waitSelector: action.waitSelector,
|
|
575
|
-
waitType,
|
|
576
|
-
message: `Waited for ${action.waitSelector} to be ${waitType}`
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
} catch (error) {
|
|
581
|
-
throw new Error(`Wait failed: ${error.message}`);
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
/**
|
|
586
|
-
* Take screenshot
|
|
587
|
-
* @private
|
|
588
|
-
*/
|
|
589
|
-
async screenshot(outputPath, projectDir, browserKey, options = {}) {
|
|
590
|
-
const page = await this.getPage(browserKey);
|
|
591
|
-
|
|
592
|
-
try {
|
|
593
|
-
const fullPath = projectDir ? path.resolve(projectDir, outputPath) : path.resolve(outputPath);
|
|
594
|
-
|
|
595
|
-
// Ensure directory exists
|
|
596
|
-
const dir = path.dirname(fullPath);
|
|
597
|
-
await fs.mkdir(dir, { recursive: true });
|
|
598
|
-
|
|
599
|
-
const screenshotOptions = {
|
|
600
|
-
path: fullPath,
|
|
601
|
-
type: 'png'
|
|
602
|
-
};
|
|
603
|
-
|
|
604
|
-
if (options.fullPage) {
|
|
605
|
-
screenshotOptions.fullPage = true;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
await page.screenshot(screenshotOptions);
|
|
609
|
-
|
|
610
|
-
const stats = await fs.stat(fullPath);
|
|
611
|
-
|
|
612
|
-
if (stats.size > this.maxScreenshotSize) {
|
|
613
|
-
throw new Error(`Screenshot too large: ${stats.size} bytes (max ${this.maxScreenshotSize})`);
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
return {
|
|
617
|
-
success: true,
|
|
618
|
-
action: 'screenshot',
|
|
619
|
-
outputPath: path.relative(projectDir || process.cwd(), fullPath),
|
|
620
|
-
size: stats.size,
|
|
621
|
-
fullPage: !!options.fullPage,
|
|
622
|
-
message: `Screenshot saved: ${outputPath}`
|
|
623
|
-
};
|
|
624
|
-
|
|
625
|
-
} catch (error) {
|
|
626
|
-
throw new Error(`Screenshot failed: ${error.message}`);
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
/**
|
|
631
|
-
* Extract data from elements
|
|
632
|
-
* @private
|
|
633
|
-
*/
|
|
634
|
-
async extract(selector, attribute, browserKey) {
|
|
635
|
-
const page = await this.getPage(browserKey);
|
|
636
|
-
|
|
637
|
-
try {
|
|
638
|
-
await page.waitForSelector(selector, { timeout: 5000 });
|
|
639
|
-
|
|
640
|
-
let data;
|
|
641
|
-
if (attribute === 'text') {
|
|
642
|
-
data = await page.$eval(selector, el => el.textContent.trim());
|
|
643
|
-
} else {
|
|
644
|
-
data = await page.$eval(selector, (el, attr) => el.getAttribute(attr), attribute);
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
return {
|
|
648
|
-
success: true,
|
|
649
|
-
action: 'extract',
|
|
650
|
-
selector,
|
|
651
|
-
attribute,
|
|
652
|
-
data,
|
|
653
|
-
message: `Extracted ${attribute} from ${selector}`
|
|
654
|
-
};
|
|
655
|
-
|
|
656
|
-
} catch (error) {
|
|
657
|
-
throw new Error(`Extract failed on ${selector}: ${error.message}`);
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
/**
|
|
662
|
-
* Scroll page or element
|
|
663
|
-
* @private
|
|
664
|
-
*/
|
|
665
|
-
async scroll(action, browserKey) {
|
|
666
|
-
const page = await this.getPage(browserKey);
|
|
667
|
-
|
|
668
|
-
try {
|
|
669
|
-
const { direction, pixels = 300, selector } = action;
|
|
670
|
-
|
|
671
|
-
if (selector) {
|
|
672
|
-
// Scroll specific element
|
|
673
|
-
await page.evaluate((sel, dir, px) => {
|
|
674
|
-
const element = document.querySelector(sel);
|
|
675
|
-
if (element) {
|
|
676
|
-
if (dir === 'down') element.scrollTop += px;
|
|
677
|
-
else if (dir === 'up') element.scrollTop -= px;
|
|
678
|
-
else if (dir === 'right') element.scrollLeft += px;
|
|
679
|
-
else if (dir === 'left') element.scrollLeft -= px;
|
|
680
|
-
}
|
|
681
|
-
}, selector, direction, pixels);
|
|
682
|
-
} else {
|
|
683
|
-
// Scroll page
|
|
684
|
-
await page.evaluate((dir, px) => {
|
|
685
|
-
if (dir === 'down') window.scrollBy(0, px);
|
|
686
|
-
else if (dir === 'up') window.scrollBy(0, -px);
|
|
687
|
-
else if (dir === 'right') window.scrollBy(px, 0);
|
|
688
|
-
else if (dir === 'left') window.scrollBy(-px, 0);
|
|
689
|
-
}, direction, pixels);
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
return {
|
|
693
|
-
success: true,
|
|
694
|
-
action: 'scroll',
|
|
695
|
-
direction,
|
|
696
|
-
pixels,
|
|
697
|
-
selector: selector || 'page',
|
|
698
|
-
message: `Scrolled ${direction} by ${pixels}px`
|
|
699
|
-
};
|
|
700
|
-
|
|
701
|
-
} catch (error) {
|
|
702
|
-
throw new Error(`Scroll failed: ${error.message}`);
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
/**
|
|
707
|
-
* Get or create browser instance
|
|
708
|
-
* @private
|
|
709
|
-
*/
|
|
710
|
-
async getBrowser(browserKey) {
|
|
711
|
-
if (this.browserInstances.has(browserKey)) {
|
|
712
|
-
const instance = this.browserInstances.get(browserKey);
|
|
713
|
-
if (!instance.browser.isConnected()) {
|
|
714
|
-
this.browserInstances.delete(browserKey);
|
|
715
|
-
} else {
|
|
716
|
-
return instance.browser;
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
const browser = await puppeteer.launch({
|
|
721
|
-
headless: this.browserConfig.headless,
|
|
722
|
-
args: [
|
|
723
|
-
'--no-sandbox',
|
|
724
|
-
'--disable-setuid-sandbox',
|
|
725
|
-
'--disable-dev-shm-usage',
|
|
726
|
-
'--disable-gpu'
|
|
727
|
-
]
|
|
728
|
-
});
|
|
729
|
-
|
|
730
|
-
const page = await browser.newPage();
|
|
731
|
-
await page.setViewport(this.browserConfig.viewport);
|
|
732
|
-
await page.setUserAgent(this.browserConfig.userAgent);
|
|
733
|
-
|
|
734
|
-
this.browserInstances.set(browserKey, {
|
|
735
|
-
browser,
|
|
736
|
-
page,
|
|
737
|
-
created: Date.now()
|
|
738
|
-
});
|
|
739
|
-
|
|
740
|
-
// Auto-cleanup after 10 minutes of inactivity
|
|
741
|
-
setTimeout(() => {
|
|
742
|
-
this.cleanupBrowser(browserKey);
|
|
743
|
-
}, 10 * 60 * 1000);
|
|
744
|
-
|
|
745
|
-
return browser;
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
/**
|
|
749
|
-
* Get page instance
|
|
750
|
-
* @private
|
|
751
|
-
*/
|
|
752
|
-
async getPage(browserKey) {
|
|
753
|
-
await this.getBrowser(browserKey);
|
|
754
|
-
return this.browserInstances.get(browserKey).page;
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
/**
|
|
758
|
-
* Close browser instance
|
|
759
|
-
* @private
|
|
760
|
-
*/
|
|
761
|
-
async closeBrowser(browserKey) {
|
|
762
|
-
const instance = this.browserInstances.get(browserKey);
|
|
763
|
-
|
|
764
|
-
if (instance) {
|
|
765
|
-
try {
|
|
766
|
-
await instance.browser.close();
|
|
767
|
-
this.browserInstances.delete(browserKey);
|
|
768
|
-
|
|
769
|
-
return {
|
|
770
|
-
success: true,
|
|
771
|
-
action: 'close',
|
|
772
|
-
browserKey,
|
|
773
|
-
message: 'Browser instance closed'
|
|
774
|
-
};
|
|
775
|
-
} catch (error) {
|
|
776
|
-
throw new Error(`Failed to close browser: ${error.message}`);
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
return {
|
|
781
|
-
success: true,
|
|
782
|
-
action: 'close',
|
|
783
|
-
browserKey,
|
|
784
|
-
message: 'Browser instance not found (already closed)'
|
|
785
|
-
};
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
/**
|
|
789
|
-
* Cleanup browser instance
|
|
790
|
-
* @private
|
|
791
|
-
*/
|
|
792
|
-
async cleanupBrowser(browserKey) {
|
|
793
|
-
const instance = this.browserInstances.get(browserKey);
|
|
794
|
-
|
|
795
|
-
if (instance) {
|
|
796
|
-
try {
|
|
797
|
-
await instance.browser.close();
|
|
798
|
-
} catch (error) {
|
|
799
|
-
// Ignore cleanup errors
|
|
800
|
-
} finally {
|
|
801
|
-
this.browserInstances.delete(browserKey);
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
/**
|
|
807
|
-
* Check if URL is allowed
|
|
808
|
-
* @private
|
|
809
|
-
*/
|
|
810
|
-
isAllowedUrl(url) {
|
|
811
|
-
try {
|
|
812
|
-
const urlObj = new URL(url);
|
|
813
|
-
const hostname = urlObj.hostname.toLowerCase();
|
|
814
|
-
|
|
815
|
-
// Check blocked domains
|
|
816
|
-
if (this.blockedDomains.some(blocked => hostname.includes(blocked.toLowerCase()))) {
|
|
817
|
-
return false;
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
// Check allowed domains if specified
|
|
821
|
-
if (this.allowedDomains && this.allowedDomains.length > 0) {
|
|
822
|
-
return this.allowedDomains.some(allowed => hostname.includes(allowed.toLowerCase()));
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
return true;
|
|
826
|
-
} catch {
|
|
827
|
-
return false;
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
/**
|
|
832
|
-
* Add operation to history
|
|
833
|
-
* @private
|
|
834
|
-
*/
|
|
835
|
-
addToHistory(action, result, agentId) {
|
|
836
|
-
const historyEntry = {
|
|
837
|
-
timestamp: new Date().toISOString(),
|
|
838
|
-
agentId,
|
|
839
|
-
action: action.type,
|
|
840
|
-
selector: action.selector || action.url,
|
|
841
|
-
success: result.success,
|
|
842
|
-
loadTime: result.loadTime || 0
|
|
843
|
-
};
|
|
844
|
-
|
|
845
|
-
this.operationHistory.push(historyEntry);
|
|
846
|
-
|
|
847
|
-
// Keep only last 100 entries
|
|
848
|
-
if (this.operationHistory.length > 100) {
|
|
849
|
-
this.operationHistory = this.operationHistory.slice(-100);
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
/**
|
|
854
|
-
* Get supported actions for this tool
|
|
855
|
-
* @returns {Array<string>} Array of supported action names
|
|
856
|
-
*/
|
|
857
|
-
getSupportedActions() {
|
|
858
|
-
return ['navigate', 'click', 'type', 'wait', 'screenshot', 'extract', 'scroll', 'close'];
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
/**
|
|
862
|
-
* Get parameter schema for validation
|
|
863
|
-
* @returns {Object} Parameter schema
|
|
864
|
-
*/
|
|
865
|
-
getParameterSchema() {
|
|
866
|
-
return {
|
|
867
|
-
type: 'object',
|
|
868
|
-
properties: {
|
|
869
|
-
actions: {
|
|
870
|
-
type: 'array',
|
|
871
|
-
minItems: 1,
|
|
872
|
-
items: {
|
|
873
|
-
type: 'object',
|
|
874
|
-
properties: {
|
|
875
|
-
type: {
|
|
876
|
-
type: 'string',
|
|
877
|
-
enum: this.getSupportedActions()
|
|
878
|
-
},
|
|
879
|
-
url: { type: 'string' },
|
|
880
|
-
selector: { type: 'string' },
|
|
881
|
-
text: { type: 'string' },
|
|
882
|
-
outputPath: { type: 'string' },
|
|
883
|
-
waitTime: { type: 'integer' },
|
|
884
|
-
waitSelector: { type: 'string' },
|
|
885
|
-
attribute: { type: 'string' },
|
|
886
|
-
direction: { type: 'string' },
|
|
887
|
-
pixels: { type: 'integer' }
|
|
888
|
-
},
|
|
889
|
-
required: ['type']
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
},
|
|
893
|
-
required: ['actions']
|
|
894
|
-
};
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
/**
|
|
898
|
-
* Resource cleanup
|
|
899
|
-
* @param {string} operationId - Operation identifier
|
|
900
|
-
*/
|
|
901
|
-
async cleanup(operationId) {
|
|
902
|
-
// Close all browser instances
|
|
903
|
-
for (const [browserKey] of this.browserInstances) {
|
|
904
|
-
await this.cleanupBrowser(browserKey);
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
/**
|
|
909
|
-
* Get operation history for debugging
|
|
910
|
-
* @returns {Array} Operation history
|
|
911
|
-
*/
|
|
912
|
-
getOperationHistory(agentId = null) {
|
|
913
|
-
if (agentId) {
|
|
914
|
-
return this.operationHistory.filter(entry => entry.agentId === agentId);
|
|
915
|
-
}
|
|
916
|
-
return [...this.operationHistory];
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
export default BrowserTool;
|
|
1
|
+
const a0_0x26aab3=a0_0x4cbd;(function(_0x1490e4,_0x498d3c){const _0x352f5a=a0_0x4cbd,_0x1ba6b0=_0x1490e4();while(!![]){try{const _0x583a4a=parseInt(_0x352f5a(0x191))/0x1*(parseInt(_0x352f5a(0x16c))/0x2)+-parseInt(_0x352f5a(0x165))/0x3+-parseInt(_0x352f5a(0x186))/0x4+parseInt(_0x352f5a(0x16b))/0x5*(parseInt(_0x352f5a(0x162))/0x6)+-parseInt(_0x352f5a(0x1c2))/0x7*(-parseInt(_0x352f5a(0x1ae))/0x8)+parseInt(_0x352f5a(0x169))/0x9+parseInt(_0x352f5a(0x1b0))/0xa*(-parseInt(_0x352f5a(0x1bc))/0xb);if(_0x583a4a===_0x498d3c)break;else _0x1ba6b0['push'](_0x1ba6b0['shift']());}catch(_0x1a40ae){_0x1ba6b0['push'](_0x1ba6b0['shift']());}}}(a0_0x1f06,0x9aedb));import{BaseTool}from'./baseTool.js';function a0_0x1f06(){const _0x533863=['vhLWzwqG','CMLNAhq','y2XVC2vcCM93C2vY','u2nYzwvUC2HVDcbZyxzLzdOG','Bg9Nz2vY','ywrKvg9iAxn0B3j5','u2nYB2XSzwqG','C2v0vxnLCKfNzw50','C2XPy2u','Dg9ju09tDhjPBMC','AxndB25Uzwn0zwq','zNvSBc1WywDL','ihrVigjLia','zgLYBMfTzq','zxHLy3v0zq','AxnbCNjHEq','ywXSB3DLzerVBwfPBNm','DhLWzq','oIbKAxjLy3rPB24GAxmGCMvXDwLYzwqGzM9YihnJCM9SBa','zg93BG','B3v0Chv0ugf0Aa','yNjVD3nLCG','zgLYzwn0Aw9U','y2XLyw51CejYB3DZzxi','z2v0ugfYyw1LDgvYu2nOzw1H','oda4sw1zAgfs','D2fPDfnLBgvJDg9Y','nZa2nZi2mfbXtNn2zW','DhjPBq','BM93','C2v0vMLLD3bVCNq','zNvSBfbHz2u','C2vSzwn0qwXS','ls1KAxnHyMXLlxnLDhvPzc1Zyw5KyM94','qxqGBgvHC3qGB25LigfJDgLVBIbPCYbYzxf1AxjLza','C3rYAw5N','D2fPDfr5Cgu','qwn0Aw9Uia','rMfPBgvKihrVignSB3nLigjYB3DZzxi6ia','mJj2vKvwzLC','CgfYC2vqyxjHBwv0zxjZ','zgvSzxrL','D2fPDezVCLnLBgvJDg9Y','igzYB20G','z2v0rgvZy3jPChrPB24','odm1mdnQELnTBxm','yxr0CMLIDxrL','CxvLCNLtzwXLy3rVCG','Bg9HzfrPBwu','z2v0uMvXDwLYzwrqyxjHBwv0zxjZ','DMLLD3bVCNq','igj5DgvZicHTyxGG','DxnLCKfNzw50','tMf2AwDHDgvKihrVia','D2fPDc1ZzwXLy3rVCG','AgfZ','oIbLAxrOzxiGD2fPDc10Aw1Lig9YihDHAxqTC2vSzwn0B3iGAxmGCMvXDwLYzwq','A2v5yM9HCMq','C2nYB2XSqNK','Aw5JBhvKzxm','z290BW','D2fPDc10ExbL','z2v0ugfNzq','C2L6zq','y2XVC2u','D2fPDc10Aw1L','zxzHBhvHDgu','CMvXDwLYzxnqCM9Qzwn0','C29Tzq','BwvZC2fNzq','C2vSzwn0B3i','ChvZAa','Bwf4u2nYzwvUC2HVDfnPEMu','D2fPDfrPBwu','zM9JDxm','Dgv4Da','y2XPy2S','CgfNzq','vhLWzsbMywLSzwqGB24G','AgvHzgXLC3m','oIbPBNzHBgLKihnJCM9SBcbKAxjLy3rPB246ia','yNjVD3nLCKLUC3rHBMnLCW','C2nYB2XS','ywn0Aw9UCW','yxjYyxK','zxH0CMfJDa','rxH0CMfJDgvKia','mtjyvwj5A0y','tMf2AwDHDgLVBIbMywLSzwq6ia','CMvSyxrPDMu','mtq4odyWnLDwwK9KyW','C2nYB2XStgvMDa','AxnbBgXVD2vKvxjS','D2fPDa','oti3nJK2nK9uvK15tG','qNjVD3nLCIbPBNn0yw5JzsbJBg9Zzwq','ndq0mtqWweHWBKjp','mty0ChnSrgLk','Cg5N','Dgv4DenVBNrLBNq','z2v0','oIbVDxrWDxqTCgf0AcbPCYbYzxf1AxjLzcbMB3iGC2nYzwvUC2HVDa','AxnbC3LUyW','zxHLyW','C2nYzwvUC2HVDa','DgLTzw91Da','oIb0zxH0igLZihjLCxvPCMvKigzVCIb0ExbL','Dg9mB3DLCKnHC2u','B3bLCMf0Aw9UsgLZDg9YEq','Bwf4q29Uy3vYCMvUDe9WzxjHDgLVBNm','Dhj1zq','oIb0ExbLigLZihjLCxvPCMvK','zw50CMLLCW','AgvPz2H0','BwTKAxi','AgLKzgvU','BgvMDa','CMf3q29UDgvUDa','yNjVD3nLCKnVBMzPzW','ls1KAxnHyMXLlwDWDq','ls1UBY1Zyw5KyM94','CMvZB2X2zq','D2LKDgG','mJu1nZi4wfHRBwXI','qNjVD3nLCIbPBNn0yw5JzsbUB3qGzM91BMqGkgfSCMvHzhKGy2XVC2vKkq','vw5RBM93BIbHy3rPB24GDhLWztOG','zxjYB3i','C3rHCNrZv2L0Aa','DMLZAwjSzq','BMv0D29YA2LKBguW','B3v0Chv0lxbHDgG','z2v0qNjVD3nLCG','y3vZDg9TvMfSAwrHDgvqyxjHBwv0zxjZ','BgvUz3rO','mJm3n2PXCM1xEG','yMXVy2TLzerVBwfPBNm','BMf2AwDHDgu','DxjS'];a0_0x1f06=function(){return _0x533863;};return a0_0x1f06();}import a0_0x8d43c4 from'../utilities/tagParser.js';import a0_0x469b6e from'puppeteer';function a0_0x4cbd(_0x2d1771,_0x5e1cca){_0x2d1771=_0x2d1771-0x14e;const _0x1f06f5=a0_0x1f06();let _0x4cbdca=_0x1f06f5[_0x2d1771];if(a0_0x4cbd['AFieWr']===undefined){var _0x268ba1=function(_0x4fb485){const _0x555399='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x8d43c4='',_0x469b6e='';for(let _0x1ca9bb=0x0,_0x29d635,_0xe88967,_0xffa158=0x0;_0xe88967=_0x4fb485['charAt'](_0xffa158++);~_0xe88967&&(_0x29d635=_0x1ca9bb%0x4?_0x29d635*0x40+_0xe88967:_0xe88967,_0x1ca9bb++%0x4)?_0x8d43c4+=String['fromCharCode'](0xff&_0x29d635>>(-0x2*_0x1ca9bb&0x6)):0x0){_0xe88967=_0x555399['indexOf'](_0xe88967);}for(let _0x448648=0x0,_0x41e8eb=_0x8d43c4['length'];_0x448648<_0x41e8eb;_0x448648++){_0x469b6e+='%'+('00'+_0x8d43c4['charCodeAt'](_0x448648)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x469b6e);};a0_0x4cbd['TAvHHF']=_0x268ba1,a0_0x4cbd['rvbawh']={},a0_0x4cbd['AFieWr']=!![];}const _0x34d446=_0x1f06f5[0x0],_0x5eaad4=_0x2d1771+_0x34d446,_0x2d8ddb=a0_0x4cbd['rvbawh'][_0x5eaad4];return!_0x2d8ddb?(_0x4cbdca=a0_0x4cbd['TAvHHF'](_0x4cbdca),a0_0x4cbd['rvbawh'][_0x5eaad4]=_0x4cbdca):_0x4cbdca=_0x2d8ddb,_0x4cbdca;}import a0_0x1ca9bb from'fs/promises';import a0_0x29d635 from'path';import{TOOL_STATUS,SYSTEM_DEFAULTS}from'../utilities/constants.js';class BrowserTool extends BaseTool{constructor(_0xe88967={},_0xffa158=null){const _0x79f755=a0_0x4cbd;super(_0xe88967,_0xffa158),this[_0x79f755(0x14e)]=![],this[_0x79f755(0x171)]=!![],this[_0x79f755(0x174)]=_0xe88967['timeout']||0xea60,this['maxConcurrentOperations']=_0xe88967[_0x79f755(0x178)]||0x2,this[_0x79f755(0x181)]={'headless':_0xe88967[_0x79f755(0x15a)]!==![],'viewport':_0xe88967[_0x79f755(0x1c7)]||{'width':0x500,'height':0x2d0},'userAgent':_0xe88967[_0x79f755(0x1c9)]||'Mozilla/5.0\x20(Windows\x20NT\x2010.0;\x20Win64;\x20x64)\x20AppleWebKit/537.36','timeout':_0xe88967['pageTimeout']||0x7530},this[_0x79f755(0x15c)]=new Map(),this[_0x79f755(0x1a5)]=_0xe88967[_0x79f755(0x1a5)]||null,this[_0x79f755(0x192)]=_0xe88967['blockedDomains']||[],this['maxScreenshotSize']=_0xe88967[_0x79f755(0x153)]||0x5*0x400*0x400,this[_0x79f755(0x177)]=[];}[a0_0x26aab3(0x1c1)](){const _0x35c1e7=a0_0x26aab3;return'\x0aBrowser\x20Tool:\x20Automate\x20web\x20browser\x20interactions,\x20navigation,\x20and\x20data\x20extraction.\x0a\x0aIMPORTANT\x20-\x20SYNTAX:\x20Use\x20self-closing\x20tags\x20with\x20angle\x20brackets\x20<\x20>,\x20not\x20square\x20brackets\x20[\x20].\x0a\x0aXML\x20USAGE\x20(Recommended):\x0a<browser>\x0a\x20\x20<navigate\x20url=\x22https://example.com\x22\x20/>\x0a\x20\x20<wait\x20wait-time=\x222000\x22\x20/>\x0a\x20\x20<click\x20selector=\x22button.submit\x22\x20/>\x0a\x20\x20<screenshot\x20output-path=\x22screenshot.png\x22\x20/>\x0a</browser>\x0a\x0aJSON\x20USAGE\x20(Alternative):\x0a```json\x0a{\x0a\x20\x20\x22toolId\x22:\x20\x22browser\x22,\x0a\x20\x20\x22actions\x22:\x20[\x0a\x20\x20\x20\x20{\x20\x22type\x22:\x20\x22navigate\x22,\x20\x22url\x22:\x20\x22https://example.com\x22\x20},\x0a\x20\x20\x20\x20{\x20\x22type\x22:\x20\x22wait\x22,\x20\x22waitTime\x22:\x202000\x20},\x0a\x20\x20\x20\x20{\x20\x22type\x22:\x20\x22click\x22,\x20\x22selector\x22:\x20\x22button.submit\x22\x20},\x0a\x20\x20\x20\x20{\x20\x22type\x22:\x20\x22screenshot\x22,\x20\x22outputPath\x22:\x20\x22screenshot.png\x22\x20}\x0a\x20\x20]\x0a}\x0a```\x0a\x0aSUPPORTED\x20ACTIONS:\x0a-\x20navigate:\x20Navigate\x20to\x20a\x20URL\x20(requires:\x20url)\x0a-\x20click:\x20Click\x20on\x20an\x20element\x20(requires:\x20selector)\x0a-\x20type:\x20Type\x20text\x20into\x20an\x20input\x20field\x20(requires:\x20selector,\x20text)\x0a-\x20wait:\x20Wait\x20for\x20element\x20or\x20time\x20(requires:\x20wait-time\x20OR\x20wait-selector)\x0a-\x20screenshot:\x20Take\x20screenshot\x20of\x20page\x20(requires:\x20output-path)\x0a-\x20extract:\x20Extract\x20text/data\x20from\x20elements\x20(requires:\x20selector,\x20attribute)\x0a-\x20scroll:\x20Scroll\x20page\x20or\x20element\x20(requires:\x20direction)\x0a-\x20close:\x20Close\x20browser\x20instance\x20(no\x20parameters)\x0a\x0aPARAMETER\x20REFERENCE:\x0a-\x20url:\x20Target\x20URL\x20for\x20navigation\x0a-\x20selector:\x20CSS\x20selector\x20for\x20element\x20targeting\x20(e.g.,\x20\x22#id\x22,\x20\x22.class\x22,\x20\x22button[type=\x27submit\x27]\x22)\x0a-\x20text:\x20Text\x20content\x20to\x20type\x0a-\x20output-path:\x20File\x20path\x20for\x20screenshots\x20(kebab-case\x20in\x20XML,\x20outputPath\x20in\x20JSON)\x0a-\x20wait-time:\x20Time\x20to\x20wait\x20in\x20milliseconds\x20(100-30000,\x20kebab-case\x20in\x20XML,\x20waitTime\x20in\x20JSON)\x0a-\x20wait-selector:\x20Element\x20selector\x20to\x20wait\x20for\x20(kebab-case\x20in\x20XML,\x20waitSelector\x20in\x20JSON)\x0a-\x20wait-type:\x20Wait\x20type\x20-\x20\x22visible\x22\x20or\x20\x22hidden\x22\x20(default:\x20\x22visible\x22)\x0a-\x20attribute:\x20HTML\x20attribute\x20to\x20extract\x20(default:\x20\x22text\x22)\x0a-\x20direction:\x20Scroll\x20direction\x20-\x20\x22up\x22,\x20\x22down\x22,\x20\x22left\x22,\x20\x22right\x22\x0a-\x20pixels:\x20Scroll\x20distance\x20in\x20pixels\x20(default:\x20300)\x0a-\x20full-page:\x20Take\x20full\x20page\x20screenshot\x20-\x20\x22true\x22\x20or\x20\x22false\x22\x0a\x0aEXAMPLES:\x0a\x0aExample\x201\x20-\x20Research\x20and\x20screenshot:\x0a<browser>\x0a\x20\x20<navigate\x20url=\x22https://github.com/trending\x22\x20/>\x0a\x20\x20<wait\x20wait-time=\x222000\x22\x20/>\x0a\x20\x20<screenshot\x20output-path=\x22trending.png\x22\x20/>\x0a\x20\x20<extract\x20selector=\x22h2.h3\x22\x20attribute=\x22text\x22\x20/>\x0a\x20\x20<close\x20/>\x0a</browser>\x0a\x0aExample\x202\x20-\x20Form\x20automation:\x0a<browser>\x0a\x20\x20<navigate\x20url=\x22https://example.com/login\x22\x20/>\x0a\x20\x20<wait\x20wait-selector=\x22input#username\x22\x20/>\x0a\x20\x20<type\x20selector=\x22input#username\x22\x20text=\x22myuser\x22\x20/>\x0a\x20\x20<type\x20selector=\x22input#password\x22\x20text=\x22mypass\x22\x20/>\x0a\x20\x20<click\x20selector=\x22button[type=\x27submit\x27]\x22\x20/>\x0a\x20\x20<wait\x20wait-selector=\x22.success-message\x22\x20/>\x0a\x20\x20<close\x20/>\x0a</browser>\x0a\x0aExample\x203\x20-\x20Data\x20extraction:\x0a<browser>\x0a\x20\x20<navigate\x20url=\x22https://example.com/products\x22\x20/>\x0a\x20\x20<wait\x20wait-time=\x221000\x22\x20/>\x0a\x20\x20<extract\x20selector=\x22h1.product-title\x22\x20attribute=\x22text\x22\x20/>\x0a\x20\x20<extract\x20selector=\x22span.price\x22\x20attribute=\x22text\x22\x20/>\x0a\x20\x20<extract\x20selector=\x22img.product\x22\x20attribute=\x22src\x22\x20/>\x0a\x20\x20<close\x20/>\x0a</browser>\x0a\x0aExample\x204\x20-\x20Scrolling\x20and\x20waiting:\x0a<browser>\x0a\x20\x20<navigate\x20url=\x22https://example.com/long-page\x22\x20/>\x0a\x20\x20<scroll\x20direction=\x22down\x22\x20pixels=\x22500\x22\x20/>\x0a\x20\x20<wait\x20wait-time=\x221000\x22\x20/>\x0a\x20\x20<scroll\x20direction=\x22down\x22\x20pixels=\x22500\x22\x20/>\x0a\x20\x20<screenshot\x20output-path=\x22scrolled.png\x22\x20full-page=\x22true\x22\x20/>\x0a\x20\x20<close\x20/>\x0a</browser>\x0a\x0aSYNTAX\x20RULES:\x0a✅\x20DO:\x20<navigate\x20url=\x22...\x22\x20/>\x20\x20(self-closing\x20with\x20/>)\x0a✅\x20DO:\x20<navigate\x20url=\x22...\x22>\x20\x20\x20\x20\x20(can\x20omit\x20/>\x20if\x20preferred)\x0a❌\x20DON\x27T:\x20<navigate\x20url=\x22...\x22></navigate>\x20\x20(no\x20closing\x20tag\x20needed)\x0a❌\x20DON\x27T:\x20[navigate\x20url=\x22...\x22\x20/]\x20\x20(square\x20brackets\x20not\x20supported)\x0a\x0aATTRIBUTE\x20NAMING:\x0a-\x20XML\x20uses\x20kebab-case:\x20wait-time,\x20output-path,\x20wait-selector\x0a-\x20JSON\x20uses\x20camelCase:\x20waitTime,\x20outputPath,\x20waitSelector\x0a-\x20Parser\x20auto-converts\x20kebab-case\x20to\x20camelCase\x20internally\x0a\x0aSECURITY:\x0a-\x20Restricted\x20to\x20allowed\x20domains\x20(if\x20configured)\x0a-\x20Blocked\x20domains\x20enforced\x20for\x20security\x0a-\x20Screenshot\x20size\x20limits\x20enforced\x20(max\x205MB)\x0a-\x20Automatic\x20cleanup\x20of\x20browser\x20instances\x20after\x2010\x20minutes\x0a-\x20Sandboxed\x20browser\x20execution\x0a\x0aBROWSER\x20SESSION:\x0aThe\x20tool\x20maintains\x20browser\x20instances\x20per\x20agent\x20context.\x0aUse\x20<close\x20/>\x20action\x20to\x20explicitly\x20cleanup,\x20or\x20instances\x20auto-cleanup\x20after\x20timeout.\x0a\x0aVIEWPORT:\x20'+this[_0x35c1e7(0x181)][_0x35c1e7(0x1c7)][_0x35c1e7(0x185)]+'x'+this['browserConfig'][_0x35c1e7(0x1c7)][_0x35c1e7(0x17c)]+'\x0aTIMEOUT:\x20'+this[_0x35c1e7(0x181)][_0x35c1e7(0x174)]+'ms\x20per\x20operation\x0aMAX\x20WAIT\x20TIME:\x2030\x20seconds\x20(30000ms)\x0a\x20\x20\x20\x20';}[a0_0x26aab3(0x1bd)](_0x448648){const _0x5cb4a1=a0_0x26aab3;try{if(_0x448648[_0x5cb4a1(0x1b1)]()[_0x5cb4a1(0x18a)]('{')){const _0x25b7b9=JSON['parse'](_0x448648);return!_0x25b7b9['actions']&&(_0x25b7b9[_0x5cb4a1(0x15e)]=[]),!Array[_0x5cb4a1(0x1a4)](_0x25b7b9['actions'])&&(_0x25b7b9['actions']=[_0x25b7b9[_0x5cb4a1(0x15e)]]),_0x25b7b9;}const _0x41e8eb={},_0x2b43a4=[],_0x5cc2dd=[_0x5cb4a1(0x193),_0x5cb4a1(0x157),_0x5cb4a1(0x1a6),'wait',_0x5cb4a1(0x173),_0x5cb4a1(0x160),_0x5cb4a1(0x15d),'close'];for(const _0x105601 of _0x5cc2dd){const _0x332511=new RegExp('<'+_0x105601+'\x5cs+([^>]*?)\x5cs*\x5c/?>','gi');let _0x274fe9;while((_0x274fe9=_0x332511[_0x5cb4a1(0x172)](_0x448648))!==null){const _0x448983=_0x274fe9[0x1],_0x5c9a9a=this['parseAttributes'](_0x448983),_0x495bea={'type':_0x105601,..._0x5c9a9a};_0x495bea[_0x5cb4a1(0x18d)]&&(_0x495bea['outputPath']=_0x495bea[_0x5cb4a1(0x18d)],delete _0x495bea[_0x5cb4a1(0x18d)]),_0x495bea['wait-time']&&(_0x495bea['waitTime']=parseInt(_0x495bea[_0x5cb4a1(0x1d6)],0xa),delete _0x495bea[_0x5cb4a1(0x1d6)]),_0x495bea['wait-selector']&&(_0x495bea['waitSelector']=_0x495bea[_0x5cb4a1(0x1cb)],delete _0x495bea[_0x5cb4a1(0x1cb)]),_0x495bea[_0x5cb4a1(0x1d2)]&&(_0x495bea['waitType']=_0x495bea['wait-type'],delete _0x495bea['wait-type']),_0x495bea['full-page']&&(_0x495bea[_0x5cb4a1(0x1b4)]=_0x495bea[_0x5cb4a1(0x1a0)]===_0x5cb4a1(0x179),delete _0x495bea['full-page']),_0x2b43a4['push'](_0x495bea);}}return _0x41e8eb[_0x5cb4a1(0x15e)]=Array[_0x5cb4a1(0x1a4)](_0x2b43a4)?_0x2b43a4:[],_0x41e8eb[_0x5cb4a1(0x180)]=_0x448648[_0x5cb4a1(0x1b1)](),_0x41e8eb;}catch(_0x19a875){return this[_0x5cb4a1(0x199)]?.[_0x5cb4a1(0x189)]('Failed\x20to\x20parse\x20browser\x20parameters',{'error':_0x19a875['message']}),{'actions':[],'rawContent':_0x448648['trim'](),'parseError':_0x19a875['message']};}}['parseAttributes'](_0x5140f0){const _0x947b0={};if(!_0x5140f0)return _0x947b0;const _0x2bc92d=/([\w-]+)\s*=\s*["']([^"']*)["']/g;let _0x1d4608;while((_0x1d4608=_0x2bc92d['exec'](_0x5140f0))!==null){const _0x4b2a45=_0x1d4608[0x1],_0x5ccdf7=_0x1d4608[0x2];_0x947b0[_0x4b2a45]=_0x5ccdf7;}return _0x947b0;}[a0_0x26aab3(0x1c6)](){return['actions'];}[a0_0x26aab3(0x18f)](_0x466147){const _0x50b16d=a0_0x26aab3,_0x5bfece=[];if(!_0x466147['actions']||!Array['isArray'](_0x466147[_0x50b16d(0x15e)])||_0x466147['actions']['length']===0x0)_0x5bfece['push'](_0x50b16d(0x1b7));else for(const [_0x1c61ed,_0xb6d3de]of _0x466147['actions'][_0x50b16d(0x17b)]()){if(!_0xb6d3de[_0x50b16d(0x1a6)]){_0x5bfece[_0x50b16d(0x152)]('Action\x20'+(_0x1c61ed+0x1)+_0x50b16d(0x17a));continue;}switch(_0xb6d3de[_0x50b16d(0x1a6)]){case _0x50b16d(0x193):if(!_0xb6d3de[_0x50b16d(0x194)])_0x5bfece['push']('Action\x20'+(_0x1c61ed+0x1)+':\x20url\x20is\x20required\x20for\x20navigate');else!this['isAllowedUrl'](_0xb6d3de['url'])&&_0x5bfece['push'](_0x50b16d(0x1ba)+(_0x1c61ed+0x1)+':\x20URL\x20not\x20allowed:\x20'+_0xb6d3de['url']);break;case'click':case _0x50b16d(0x160):!_0xb6d3de['selector']&&_0x5bfece[_0x50b16d(0x152)]('Action\x20'+(_0x1c61ed+0x1)+':\x20selector\x20is\x20required\x20for\x20'+_0xb6d3de[_0x50b16d(0x1a6)]);break;case'type':!_0xb6d3de[_0x50b16d(0x151)]&&_0x5bfece[_0x50b16d(0x152)]('Action\x20'+(_0x1c61ed+0x1)+':\x20selector\x20is\x20required\x20for\x20type');!_0xb6d3de['text']&&_0xb6d3de['text']!==''&&_0x5bfece['push'](_0x50b16d(0x1ba)+(_0x1c61ed+0x1)+_0x50b16d(0x175));break;case'wait':!_0xb6d3de[_0x50b16d(0x154)]&&!_0xb6d3de[_0x50b16d(0x1af)]&&_0x5bfece[_0x50b16d(0x152)](_0x50b16d(0x1ba)+(_0x1c61ed+0x1)+_0x50b16d(0x1cd));_0xb6d3de[_0x50b16d(0x154)]&&(_0xb6d3de['waitTime']<0x64||_0xb6d3de['waitTime']>0x7530)&&_0x5bfece[_0x50b16d(0x152)](_0x50b16d(0x1ba)+(_0x1c61ed+0x1)+':\x20wait-time\x20must\x20be\x20between\x20100\x20and\x2030000\x20milliseconds');break;case _0x50b16d(0x173):!_0xb6d3de[_0x50b16d(0x1a9)]&&_0x5bfece['push']('Action\x20'+(_0x1c61ed+0x1)+_0x50b16d(0x170));break;case'scroll':if(!_0xb6d3de['direction'])_0x5bfece[_0x50b16d(0x152)]('Action\x20'+(_0x1c61ed+0x1)+_0x50b16d(0x1a7));else!['up','down','left',_0x50b16d(0x196)]['includes'](_0xb6d3de[_0x50b16d(0x1ab)])&&_0x5bfece['push']('Action\x20'+(_0x1c61ed+0x1)+_0x50b16d(0x15b)+_0xb6d3de['direction']);break;case'close':break;default:_0x5bfece[_0x50b16d(0x152)](_0x50b16d(0x1ba)+(_0x1c61ed+0x1)+':\x20unknown\x20action\x20type:\x20'+_0xb6d3de[_0x50b16d(0x1a6)]);}}return{'valid':_0x5bfece[_0x50b16d(0x190)]===0x0,'errors':_0x5bfece};}async[a0_0x26aab3(0x1a3)](_0x129e25,_0x343fe4){const _0x4bb0a4=a0_0x26aab3,{actions:_0x341bd6}=_0x129e25,{agentId:_0x400717,projectDir:_0x305cbe}=_0x343fe4,_0x24cf8f=_0x400717+'-'+(_0x305cbe||'default'),_0x25cc99=[];for(const _0x5af6a2 of _0x341bd6){try{let _0x3b8ffe;switch(_0x5af6a2['type']){case _0x4bb0a4(0x193):_0x3b8ffe=await this['navigate'](_0x5af6a2[_0x4bb0a4(0x194)],_0x24cf8f);break;case _0x4bb0a4(0x157):_0x3b8ffe=await this['click'](_0x5af6a2['selector'],_0x24cf8f);break;case _0x4bb0a4(0x1a6):_0x3b8ffe=await this[_0x4bb0a4(0x1a6)](_0x5af6a2['selector'],_0x5af6a2[_0x4bb0a4(0x156)],_0x24cf8f);break;case'wait':_0x3b8ffe=await this[_0x4bb0a4(0x168)](_0x5af6a2,_0x24cf8f);break;case'screenshot':_0x3b8ffe=await this['screenshot'](_0x5af6a2['outputPath'],_0x305cbe,_0x24cf8f,_0x5af6a2);break;case _0x4bb0a4(0x160):_0x3b8ffe=await this['extract'](_0x5af6a2['selector'],_0x5af6a2[_0x4bb0a4(0x1c3)]||'text',_0x24cf8f);break;case'scroll':_0x3b8ffe=await this['scroll'](_0x5af6a2,_0x24cf8f);break;case _0x4bb0a4(0x1d5):_0x3b8ffe=await this[_0x4bb0a4(0x197)](_0x24cf8f);break;default:throw new Error(_0x4bb0a4(0x188)+_0x5af6a2[_0x4bb0a4(0x1a6)]);}_0x25cc99['push'](_0x3b8ffe),this['addToHistory'](_0x5af6a2,_0x3b8ffe,_0x400717);}catch(_0x19a811){const _0x3b1d55={'success':![],'action':_0x5af6a2[_0x4bb0a4(0x1a6)],'error':_0x19a811['message'],'selector':_0x5af6a2['selector']||_0x5af6a2[_0x4bb0a4(0x194)]};_0x25cc99[_0x4bb0a4(0x152)](_0x3b1d55),this['addToHistory'](_0x5af6a2,_0x3b1d55,_0x400717);}}return{'success':!![],'actions':_0x25cc99,'executedActions':_0x341bd6[_0x4bb0a4(0x190)],'toolUsed':'browser'};}async[a0_0x26aab3(0x193)](_0x1b3346,_0x26c671){const _0x49b93f=a0_0x26aab3,_0x4e98d0=await this['getBrowser'](_0x26c671),_0x358e8b=await this[_0x49b93f(0x1d3)](_0x26c671),_0x2c700e=Date[_0x49b93f(0x1b2)]();try{await _0x358e8b[_0x49b93f(0x1d1)](_0x1b3346,{'waitUntil':_0x49b93f(0x18c),'timeout':this['browserConfig']['timeout']});const _0x36e202=await _0x358e8b['title'](),_0x1cf153=_0x358e8b['url']();return{'success':!![],'action':_0x49b93f(0x193),'url':_0x1cf153,'originalUrl':_0x1b3346,'title':_0x36e202,'loadTime':Date['now']()-_0x2c700e,'message':_0x49b93f(0x1ca)+_0x1b3346};}catch(_0x4e0bec){throw new Error(_0x49b93f(0x163)+_0x4e0bec['message']);}}async[a0_0x26aab3(0x157)](_0x2f6aec,_0x2e1e73){const _0x2db8a1=a0_0x26aab3,_0x5eeb0c=await this[_0x2db8a1(0x1d3)](_0x2e1e73);try{return await _0x5eeb0c[_0x2db8a1(0x1bf)](_0x2f6aec,{'timeout':0x1388}),await _0x5eeb0c[_0x2db8a1(0x157)](_0x2f6aec),{'success':!![],'action':_0x2db8a1(0x157),'selector':_0x2f6aec,'message':'Clicked\x20on\x20'+_0x2f6aec};}catch(_0xc495b6){throw new Error('Click\x20failed\x20on\x20'+_0x2f6aec+':\x20'+_0xc495b6['message']);}}async['type'](_0x2ed0ab,_0x101436,_0x3697dc){const _0x2c9586=a0_0x26aab3,_0x20c069=await this['getPage'](_0x3697dc);try{return await _0x20c069['waitForSelector'](_0x2ed0ab,{'timeout':0x1388}),await _0x20c069[_0x2c9586(0x155)](_0x2ed0ab),await _0x20c069[_0x2c9586(0x1ce)][_0x2c9586(0x1b5)](),await _0x20c069[_0x2c9586(0x1a6)](_0x2ed0ab,_0x101436),{'success':!![],'action':'type','selector':_0x2ed0ab,'text':_0x101436,'textLength':_0x101436['length'],'message':_0x2c9586(0x195)+_0x101436['length']+'\x20characters\x20into\x20'+_0x2ed0ab};}catch(_0x20e52d){throw new Error(_0x2c9586(0x159)+_0x2ed0ab+':\x20'+_0x20e52d['message']);}}async[a0_0x26aab3(0x168)](_0x31adbe,_0x36a39d){const _0x3e9cff=a0_0x26aab3,_0x199dd0=await this[_0x3e9cff(0x1d3)](_0x36a39d);try{if(_0x31adbe['waitTime'])return await _0x199dd0['waitForTimeout'](_0x31adbe['waitTime']),{'success':!![],'action':'wait','waitTime':_0x31adbe['waitTime'],'message':'Waited\x20'+_0x31adbe[_0x3e9cff(0x154)]+'ms'};if(_0x31adbe[_0x3e9cff(0x1af)]){const _0x2c8b8b=_0x31adbe[_0x3e9cff(0x1b9)]||_0x3e9cff(0x18b),_0x3a5272={'timeout':0x2710};return _0x2c8b8b===_0x3e9cff(0x17e)?await _0x199dd0['waitForSelector'](_0x31adbe[_0x3e9cff(0x1af)],{..._0x3a5272,'hidden':!![]}):await _0x199dd0['waitForSelector'](_0x31adbe['waitSelector'],_0x3a5272),{'success':!![],'action':'wait','waitSelector':_0x31adbe[_0x3e9cff(0x1af)],'waitType':_0x2c8b8b,'message':'Waited\x20for\x20'+_0x31adbe['waitSelector']+_0x3e9cff(0x1a1)+_0x2c8b8b};}}catch(_0x329c61){throw new Error('Wait\x20failed:\x20'+_0x329c61['message']);}}async[a0_0x26aab3(0x173)](_0x2772c8,_0x5867dd,_0x11c7e3,_0x5f11ae={}){const _0x18bfa8=a0_0x26aab3,_0x581c40=await this['getPage'](_0x11c7e3);try{const _0x14c67f=_0x5867dd?a0_0x29d635[_0x18bfa8(0x184)](_0x5867dd,_0x2772c8):a0_0x29d635[_0x18bfa8(0x184)](_0x2772c8),_0x4a423a=a0_0x29d635[_0x18bfa8(0x1a2)](_0x14c67f);await a0_0x1ca9bb[_0x18bfa8(0x17d)](_0x4a423a,{'recursive':!![]});const _0x34c769={'path':_0x14c67f,'type':_0x18bfa8(0x16d)};_0x5f11ae['fullPage']&&(_0x34c769['fullPage']=!![]);await _0x581c40[_0x18bfa8(0x173)](_0x34c769);const _0x17281b=await a0_0x1ca9bb['stat'](_0x14c67f);if(_0x17281b[_0x18bfa8(0x1d4)]>this[_0x18bfa8(0x153)])throw new Error('Screenshot\x20too\x20large:\x20'+_0x17281b[_0x18bfa8(0x1d4)]+_0x18bfa8(0x1c8)+this[_0x18bfa8(0x153)]+')');return{'success':!![],'action':_0x18bfa8(0x173),'outputPath':a0_0x29d635[_0x18bfa8(0x164)](_0x5867dd||process['cwd'](),_0x14c67f),'size':_0x17281b[_0x18bfa8(0x1d4)],'fullPage':!!_0x5f11ae[_0x18bfa8(0x1b4)],'message':_0x18bfa8(0x198)+_0x2772c8};}catch(_0x4c84b8){throw new Error('Screenshot\x20failed:\x20'+_0x4c84b8['message']);}}async['extract'](_0x1993d0,_0x2193ad,_0x30e8c9){const _0x279352=a0_0x26aab3,_0x12ce22=await this['getPage'](_0x30e8c9);try{await _0x12ce22['waitForSelector'](_0x1993d0,{'timeout':0x1388});let _0x376f97;return _0x2193ad==='text'?_0x376f97=await _0x12ce22['$eval'](_0x1993d0,_0x4359f9=>_0x4359f9[_0x279352(0x16e)]['trim']()):_0x376f97=await _0x12ce22['$eval'](_0x1993d0,(_0x5b28c2,_0x1b46c0)=>_0x5b28c2['getAttribute'](_0x1b46c0),_0x2193ad),{'success':!![],'action':_0x279352(0x160),'selector':_0x1993d0,'attribute':_0x2193ad,'data':_0x376f97,'message':_0x279352(0x161)+_0x2193ad+_0x279352(0x1c0)+_0x1993d0};}catch(_0x48bef9){throw new Error('Extract\x20failed\x20on\x20'+_0x1993d0+':\x20'+_0x48bef9['message']);}}async[a0_0x26aab3(0x15d)](_0x39e0cd,_0xbde1e3){const _0x2d954b=a0_0x26aab3,_0x1dbb6c=await this['getPage'](_0xbde1e3);try{const {direction:_0x5c5625,pixels:pixels=0x12c,selector:_0xbb5adb}=_0x39e0cd;return _0xbb5adb?await _0x1dbb6c[_0x2d954b(0x1d7)]((_0x3b53cd,_0x816d44,_0xbe560b)=>{const _0x3ebcd3=_0x2d954b,_0x10332b=document[_0x3ebcd3(0x1c4)](_0x3b53cd);if(_0x10332b){if(_0x816d44===_0x3ebcd3(0x1a8))_0x10332b['scrollTop']+=_0xbe560b;else{if(_0x816d44==='up')_0x10332b['scrollTop']-=_0xbe560b;else{if(_0x816d44==='right')_0x10332b[_0x3ebcd3(0x166)]+=_0xbe560b;else{if(_0x816d44===_0x3ebcd3(0x17f))_0x10332b['scrollLeft']-=_0xbe560b;}}}}},_0xbb5adb,_0x5c5625,pixels):await _0x1dbb6c[_0x2d954b(0x1d7)]((_0x9c1fce,_0x21414e)=>{const _0x630fa4=_0x2d954b;if(_0x9c1fce==='down')window[_0x630fa4(0x1cf)](0x0,_0x21414e);else{if(_0x9c1fce==='up')window['scrollBy'](0x0,-_0x21414e);else{if(_0x9c1fce===_0x630fa4(0x196))window['scrollBy'](_0x21414e,0x0);else{if(_0x9c1fce===_0x630fa4(0x17f))window[_0x630fa4(0x1cf)](-_0x21414e,0x0);}}}},_0x5c5625,pixels),{'success':!![],'action':_0x2d954b(0x15d),'direction':_0x5c5625,'pixels':pixels,'selector':_0xbb5adb||'page','message':_0x2d954b(0x19b)+_0x5c5625+'\x20by\x20'+pixels+'px'};}catch(_0x25310b){throw new Error('Scroll\x20failed:\x20'+_0x25310b[_0x2d954b(0x150)]);}}async[a0_0x26aab3(0x18e)](_0x22ebfc){const _0x24ede5=a0_0x26aab3;if(this['browserInstances'][_0x24ede5(0x1cc)](_0x22ebfc)){const _0x289e5d=this[_0x24ede5(0x15c)]['get'](_0x22ebfc);if(!_0x289e5d[_0x24ede5(0x1aa)][_0x24ede5(0x19f)]())this[_0x24ede5(0x15c)]['delete'](_0x22ebfc);else return _0x289e5d['browser'];}const _0x37a9ec=await a0_0x469b6e['launch']({'headless':this[_0x24ede5(0x181)][_0x24ede5(0x15a)],'args':[_0x24ede5(0x183),_0x24ede5(0x1b6),'--disable-dev-shm-usage',_0x24ede5(0x182)]}),_0x2f863b=await _0x37a9ec['newPage']();return await _0x2f863b[_0x24ede5(0x1b3)](this['browserConfig']['viewport']),await _0x2f863b[_0x24ede5(0x19c)](this[_0x24ede5(0x181)][_0x24ede5(0x1c9)]),this[_0x24ede5(0x15c)]['set'](_0x22ebfc,{'browser':_0x37a9ec,'page':_0x2f863b,'created':Date[_0x24ede5(0x1b2)]()}),setTimeout(()=>{this['cleanupBrowser'](_0x22ebfc);},0xa*0x3c*0x3e8),_0x37a9ec;}async['getPage'](_0x9ba9d5){const _0x4bb0e7=a0_0x26aab3;return await this[_0x4bb0e7(0x18e)](_0x9ba9d5),this['browserInstances']['get'](_0x9ba9d5)[_0x4bb0e7(0x158)];}async['closeBrowser'](_0x49bdb7){const _0x3bf325=a0_0x26aab3,_0x1af2e3=this['browserInstances'][_0x3bf325(0x16f)](_0x49bdb7);if(_0x1af2e3)try{return await _0x1af2e3[_0x3bf325(0x1aa)][_0x3bf325(0x1d5)](),this[_0x3bf325(0x15c)]['delete'](_0x49bdb7),{'success':!![],'action':'close','browserKey':_0x49bdb7,'message':_0x3bf325(0x16a)};}catch(_0x26fe9f){throw new Error(_0x3bf325(0x1bb)+_0x26fe9f['message']);}return{'success':!![],'action':'close','browserKey':_0x49bdb7,'message':_0x3bf325(0x187)};}async[a0_0x26aab3(0x1ac)](_0x19f920){const _0x360d60=a0_0x26aab3,_0xa9d346=this[_0x360d60(0x15c)][_0x360d60(0x16f)](_0x19f920);if(_0xa9d346)try{await _0xa9d346[_0x360d60(0x1aa)]['close']();}catch(_0x1569d7){}finally{this['browserInstances'][_0x360d60(0x1be)](_0x19f920);}}[a0_0x26aab3(0x167)](_0x474614){const _0x2c30ba=a0_0x26aab3;try{const _0x3607be=new URL(_0x474614),_0x440f39=_0x3607be['hostname'][_0x2c30ba(0x176)]();if(this['blockedDomains']['some'](_0x485c2a=>_0x440f39['includes'](_0x485c2a['toLowerCase']())))return![];if(this['allowedDomains']&&this['allowedDomains']['length']>0x0)return this[_0x2c30ba(0x1a5)][_0x2c30ba(0x14f)](_0x30f1b4=>_0x440f39[_0x2c30ba(0x1d0)](_0x30f1b4['toLowerCase']()));return!![];}catch{return![];}}[a0_0x26aab3(0x19a)](_0x513560,_0x213284,_0x5411ae){const _0x596d97=a0_0x26aab3,_0x4a9aef={'timestamp':new Date()[_0x596d97(0x19e)](),'agentId':_0x5411ae,'action':_0x513560['type'],'selector':_0x513560['selector']||_0x513560[_0x596d97(0x194)],'success':_0x213284['success'],'loadTime':_0x213284[_0x596d97(0x1c5)]||0x0};this['operationHistory'][_0x596d97(0x152)](_0x4a9aef),this[_0x596d97(0x177)][_0x596d97(0x190)]>0x64&&(this[_0x596d97(0x177)]=this['operationHistory'][_0x596d97(0x19d)](-0x64));}['getSupportedActions'](){const _0x17259e=a0_0x26aab3;return['navigate','click',_0x17259e(0x1a6),'wait','screenshot','extract',_0x17259e(0x15d),'close'];}[a0_0x26aab3(0x1ad)](){const _0x5ed5d6=a0_0x26aab3;return{'type':'object','properties':{'actions':{'type':_0x5ed5d6(0x15f),'minItems':0x1,'items':{'type':'object','properties':{'type':{'type':_0x5ed5d6(0x1b8),'enum':this['getSupportedActions']()},'url':{'type':_0x5ed5d6(0x1b8)},'selector':{'type':_0x5ed5d6(0x1b8)},'text':{'type':'string'},'outputPath':{'type':_0x5ed5d6(0x1b8)},'waitTime':{'type':'integer'},'waitSelector':{'type':_0x5ed5d6(0x1b8)},'attribute':{'type':_0x5ed5d6(0x1b8)},'direction':{'type':_0x5ed5d6(0x1b8)},'pixels':{'type':'integer'}},'required':[_0x5ed5d6(0x1a6)]}}},'required':['actions']};}async['cleanup'](_0x8a7248){const _0x45bbce=a0_0x26aab3;for(const [_0x40b98e]of this['browserInstances']){await this[_0x45bbce(0x1ac)](_0x40b98e);}}['getOperationHistory'](_0x3d7e9c=null){if(_0x3d7e9c)return this['operationHistory']['filter'](_0x4a17d6=>_0x4a17d6['agentId']===_0x3d7e9c);return[...this['operationHistory']];}}export default BrowserTool;
|