agentcrew-ai 0.8.7__py3-none-any.whl → 0.8.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- AgentCrew/__init__.py +1 -1
- AgentCrew/modules/agents/manager.py +7 -0
- AgentCrew/modules/browser_automation/js/click_element.js +1 -2
- AgentCrew/modules/browser_automation/js/extract_elements_by_text.js +37 -45
- AgentCrew/modules/browser_automation/js/filter_hidden_elements.js +77 -57
- AgentCrew/modules/browser_automation/js/scroll_to_element.js +25 -0
- AgentCrew/modules/browser_automation/js_loader.py +3 -9
- AgentCrew/modules/browser_automation/service.py +20 -37
- AgentCrew/modules/browser_automation/tool.py +22 -43
- AgentCrew/modules/command_execution/tool.py +6 -2
- AgentCrew/modules/console/command_handlers.py +5 -1
- AgentCrew/modules/console/console_ui.py +3 -3
- AgentCrew/modules/console/input_handler.py +6 -6
- {agentcrew_ai-0.8.7.dist-info → agentcrew_ai-0.8.8.dist-info}/METADATA +1 -1
- {agentcrew_ai-0.8.7.dist-info → agentcrew_ai-0.8.8.dist-info}/RECORD +19 -19
- AgentCrew/modules/browser_automation/js/scroll_page.js +0 -146
- {agentcrew_ai-0.8.7.dist-info → agentcrew_ai-0.8.8.dist-info}/WHEEL +0 -0
- {agentcrew_ai-0.8.7.dist-info → agentcrew_ai-0.8.8.dist-info}/entry_points.txt +0 -0
- {agentcrew_ai-0.8.7.dist-info → agentcrew_ai-0.8.8.dist-info}/licenses/LICENSE +0 -0
- {agentcrew_ai-0.8.7.dist-info → agentcrew_ai-0.8.8.dist-info}/top_level.txt +0 -0
AgentCrew/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.8.
|
|
1
|
+
__version__ = "0.8.8"
|
|
@@ -25,11 +25,18 @@ class AgentManager:
|
|
|
25
25
|
|
|
26
26
|
Args:
|
|
27
27
|
config_path: Path to the configuration file.
|
|
28
|
+
Supports @hub/ prefix which converts to https://agentplace.cloud/
|
|
28
29
|
|
|
29
30
|
Returns:
|
|
30
31
|
List of agent dictionaries.
|
|
31
32
|
"""
|
|
32
33
|
|
|
34
|
+
if config_uri.startswith("@hub/"):
|
|
35
|
+
import os
|
|
36
|
+
|
|
37
|
+
hub_host = os.environ.get("AGENTCREW_HUB_HOST", "https://agentplace.cloud")
|
|
38
|
+
config_uri = hub_host.rstrip("/") + "/" + config_uri[5:]
|
|
39
|
+
|
|
33
40
|
if config_uri.startswith(("http://", "https://")):
|
|
34
41
|
import requests
|
|
35
42
|
import tempfile
|
|
@@ -20,8 +20,7 @@ function clickElement(xpath) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// Check if element is visible and enabled
|
|
23
|
-
|
|
24
|
-
if (style.display === "none" || style.visibility === "hidden") {
|
|
23
|
+
if (!element.checkVisibility()) {
|
|
25
24
|
return { success: false, error: "Element is not visible" };
|
|
26
25
|
}
|
|
27
26
|
|
|
@@ -8,20 +8,6 @@
|
|
|
8
8
|
function extractElementsByText(text) {
|
|
9
9
|
const elementsFound = [];
|
|
10
10
|
|
|
11
|
-
function isInViewport(rect) {
|
|
12
|
-
const viewportWidth =
|
|
13
|
-
window.innerWidth || document.documentElement.clientWidth;
|
|
14
|
-
const viewportHeight =
|
|
15
|
-
window.innerHeight || document.documentElement.clientHeight;
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
rect.top < viewportHeight &&
|
|
19
|
-
rect.bottom > 0 &&
|
|
20
|
-
rect.left < viewportWidth &&
|
|
21
|
-
rect.right > 0
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
11
|
// Utility function to check if element is truly visible (including parent chain)
|
|
26
12
|
function isElementVisible(element) {
|
|
27
13
|
if (!element || !element.nodeType === 1) {
|
|
@@ -32,14 +18,6 @@ function extractElementsByText(text) {
|
|
|
32
18
|
return false;
|
|
33
19
|
}
|
|
34
20
|
|
|
35
|
-
bounding_box = element.getBoundingClientRect();
|
|
36
|
-
if (!isInViewport(bounding_box)) {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
if (bounding_box.width <= 1 && bounding_box.height <= 1) {
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
21
|
return true;
|
|
44
22
|
}
|
|
45
23
|
|
|
@@ -68,8 +46,18 @@ function extractElementsByText(text) {
|
|
|
68
46
|
}
|
|
69
47
|
}
|
|
70
48
|
|
|
49
|
+
function getDirectTextContent(element) {
|
|
50
|
+
let directText = "";
|
|
51
|
+
for (const node of element.childNodes) {
|
|
52
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
53
|
+
directText += node.textContent;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return directText.trim();
|
|
57
|
+
}
|
|
58
|
+
|
|
71
59
|
try {
|
|
72
|
-
const xpath =
|
|
60
|
+
const xpath = `//*[contains(., '${text}')]`;
|
|
73
61
|
const result = document.evaluate(
|
|
74
62
|
xpath,
|
|
75
63
|
document,
|
|
@@ -80,32 +68,36 @@ function extractElementsByText(text) {
|
|
|
80
68
|
|
|
81
69
|
let element = result.iterateNext();
|
|
82
70
|
const seenElements = new Set();
|
|
71
|
+
const searchTextLower = text.toLowerCase();
|
|
83
72
|
|
|
84
73
|
while (element) {
|
|
85
|
-
// Check if element is truly visible (including parent chain)
|
|
86
74
|
if (isElementVisible(element)) {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
75
|
+
const directText = getDirectTextContent(element);
|
|
76
|
+
const ariaLabel = element.getAttribute("aria-label") || "";
|
|
77
|
+
|
|
78
|
+
if (
|
|
79
|
+
directText.toLowerCase().includes(searchTextLower) ||
|
|
80
|
+
ariaLabel.toLowerCase().includes(searchTextLower)
|
|
81
|
+
) {
|
|
82
|
+
const elementXPath = getXPath(element);
|
|
83
|
+
|
|
84
|
+
if (!seenElements.has(elementXPath)) {
|
|
85
|
+
seenElements.add(elementXPath);
|
|
86
|
+
|
|
87
|
+
let displayText = ariaLabel || directText || "";
|
|
88
|
+
displayText = displayText.trim().replace(/\s+/g, " ");
|
|
89
|
+
if (displayText.length > 100) {
|
|
90
|
+
displayText = displayText.substring(0, 100) + "...";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
elementsFound.push({
|
|
94
|
+
xpath: elementXPath,
|
|
95
|
+
text: displayText,
|
|
96
|
+
tagName: element.tagName.toLowerCase(),
|
|
97
|
+
className: element.className || "",
|
|
98
|
+
id: element.id || "",
|
|
99
|
+
});
|
|
100
100
|
}
|
|
101
|
-
|
|
102
|
-
elementsFound.push({
|
|
103
|
-
xpath: elementXPath,
|
|
104
|
-
text: elementText,
|
|
105
|
-
tagName: element.tagName.toLowerCase(),
|
|
106
|
-
className: element.className || "",
|
|
107
|
-
id: element.id || "",
|
|
108
|
-
});
|
|
109
101
|
}
|
|
110
102
|
}
|
|
111
103
|
|
|
@@ -1,93 +1,113 @@
|
|
|
1
1
|
function filterHiddenElements() {
|
|
2
2
|
try {
|
|
3
|
-
function
|
|
4
|
-
if (element.nodeType !==
|
|
3
|
+
function isElementVisible(element) {
|
|
4
|
+
if (!element || element.nodeType !== 1) {
|
|
5
5
|
return false;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
tagName === "script" ||
|
|
11
|
-
tagName === "style" ||
|
|
12
|
-
tagName === "noscript"
|
|
13
|
-
) {
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const computedStyle = window.getComputedStyle(element);
|
|
18
|
-
|
|
19
|
-
if (
|
|
20
|
-
computedStyle.display === "none" ||
|
|
21
|
-
computedStyle.visibility === "hidden" ||
|
|
22
|
-
computedStyle.opacity === "0"
|
|
23
|
-
) {
|
|
24
|
-
return true;
|
|
8
|
+
if (element.disabled) {
|
|
9
|
+
return false;
|
|
25
10
|
}
|
|
26
11
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return true;
|
|
12
|
+
if (!element.checkVisibility()) {
|
|
13
|
+
return false;
|
|
30
14
|
}
|
|
31
15
|
|
|
32
|
-
return
|
|
16
|
+
return true;
|
|
33
17
|
}
|
|
34
18
|
|
|
35
|
-
function
|
|
36
|
-
if (!
|
|
37
|
-
return;
|
|
19
|
+
function getXPath(element) {
|
|
20
|
+
if (!element || element.nodeType !== 1) {
|
|
21
|
+
return null;
|
|
38
22
|
}
|
|
39
23
|
|
|
40
|
-
|
|
24
|
+
if (element === document.documentElement) {
|
|
25
|
+
return "/html[1]";
|
|
26
|
+
}
|
|
41
27
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
`[ag-data-temp-id="${child.getAttribute("ag-data-temp-id")}"]`,
|
|
45
|
-
);
|
|
28
|
+
const parts = [];
|
|
29
|
+
let current = element;
|
|
46
30
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
31
|
+
while (current && current.nodeType === 1) {
|
|
32
|
+
if (current === document.documentElement) {
|
|
33
|
+
parts.unshift("/html[1]");
|
|
34
|
+
break;
|
|
50
35
|
}
|
|
51
36
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
37
|
+
let index = 1;
|
|
38
|
+
let sibling = current.previousElementSibling;
|
|
39
|
+
while (sibling) {
|
|
40
|
+
if (sibling.tagName === current.tagName) {
|
|
41
|
+
index++;
|
|
42
|
+
}
|
|
43
|
+
sibling = sibling.previousElementSibling;
|
|
56
44
|
}
|
|
45
|
+
|
|
46
|
+
const tagName = current.tagName.toLowerCase();
|
|
47
|
+
parts.unshift(`${tagName}[${index}]`);
|
|
48
|
+
current = current.parentElement;
|
|
57
49
|
}
|
|
50
|
+
|
|
51
|
+
return parts.join("/");
|
|
58
52
|
}
|
|
59
53
|
|
|
60
|
-
function
|
|
61
|
-
if (!
|
|
54
|
+
function traverseAndMarkHidden(element, hiddenXPaths) {
|
|
55
|
+
if (!element || element.nodeType !== 1) {
|
|
62
56
|
return;
|
|
63
57
|
}
|
|
64
58
|
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
59
|
+
const tagName = element.tagName.toLowerCase();
|
|
60
|
+
if (
|
|
61
|
+
tagName === "script" ||
|
|
62
|
+
tagName === "style" ||
|
|
63
|
+
tagName === "noscript"
|
|
64
|
+
) {
|
|
65
|
+
const xpath = getXPath(element);
|
|
66
|
+
if (xpath) {
|
|
67
|
+
hiddenXPaths.push(xpath);
|
|
68
|
+
}
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
72
71
|
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
if (!isElementVisible(element)) {
|
|
73
|
+
const xpath = getXPath(element);
|
|
74
|
+
if (xpath) {
|
|
75
|
+
hiddenXPaths.push(xpath);
|
|
76
|
+
}
|
|
75
77
|
return;
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
const children = Array.from(element.children);
|
|
81
|
+
for (const child of children) {
|
|
82
|
+
traverseAndMarkHidden(child, hiddenXPaths);
|
|
83
|
+
}
|
|
80
84
|
}
|
|
81
85
|
|
|
82
|
-
addTempIds(document.documentElement);
|
|
83
86
|
const documentClone = document.documentElement.cloneNode(true);
|
|
87
|
+
const cloneDoc = document.implementation.createHTMLDocument("");
|
|
88
|
+
cloneDoc.replaceChild(documentClone, cloneDoc.documentElement);
|
|
89
|
+
|
|
90
|
+
const hiddenXPaths = [];
|
|
91
|
+
traverseAndMarkHidden(document.documentElement, hiddenXPaths);
|
|
92
|
+
|
|
93
|
+
for (const xpath of hiddenXPaths) {
|
|
94
|
+
const result = cloneDoc.evaluate(
|
|
95
|
+
xpath,
|
|
96
|
+
cloneDoc,
|
|
97
|
+
null,
|
|
98
|
+
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
|
99
|
+
null,
|
|
100
|
+
);
|
|
101
|
+
const cloneElement = result.singleNodeValue;
|
|
102
|
+
if (cloneElement) {
|
|
103
|
+
cloneElement.setAttribute("data-hidden", "true");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
84
106
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
removeTempIds(document.documentElement);
|
|
88
|
-
removeTempIds(documentClone);
|
|
107
|
+
const hiddenElements = cloneDoc.querySelectorAll("[data-hidden='true']");
|
|
108
|
+
hiddenElements.forEach((el) => el.remove());
|
|
89
109
|
|
|
90
|
-
const filteredHTML =
|
|
110
|
+
const filteredHTML = cloneDoc.documentElement.outerHTML;
|
|
91
111
|
|
|
92
112
|
return {
|
|
93
113
|
success: true,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
function scrollToElement(xpath) {
|
|
2
|
+
const result = document.evaluate(
|
|
3
|
+
xpath,
|
|
4
|
+
document,
|
|
5
|
+
null,
|
|
6
|
+
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
|
7
|
+
null,
|
|
8
|
+
);
|
|
9
|
+
const element = result.singleNodeValue;
|
|
10
|
+
|
|
11
|
+
if (!element) {
|
|
12
|
+
return { success: false, error: "Element not found with provided xpath" };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
element.scrollIntoView({
|
|
16
|
+
behavior: "instant",
|
|
17
|
+
block: "center",
|
|
18
|
+
inline: "center",
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
success: true,
|
|
23
|
+
message: "Scrolled to element using scrollIntoView()",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -430,19 +430,13 @@ class JavaScriptLoader:
|
|
|
430
430
|
"""
|
|
431
431
|
return js_code + "\n" + wrapper
|
|
432
432
|
|
|
433
|
-
def
|
|
434
|
-
|
|
435
|
-
) -> str:
|
|
436
|
-
js_code = self.load_js_file("scroll_page.js")
|
|
433
|
+
def get_scroll_to_element_js(self, xpath: str) -> str:
|
|
434
|
+
js_code = self.load_js_file("scroll_to_element.js")
|
|
437
435
|
escaped_xpath = xpath.replace("`", "\\`").replace("\\", "\\\\")
|
|
438
|
-
escaped_uuid = element_uuid.replace("`", "\\`").replace("\\", "\\\\")
|
|
439
436
|
wrapper = f"""
|
|
440
437
|
(() => {{
|
|
441
|
-
const direction = '{direction}';
|
|
442
|
-
const distance = {distance};
|
|
443
438
|
const xpath = `{escaped_xpath}`;
|
|
444
|
-
|
|
445
|
-
return scrollPage(direction, distance, xpath, elementUuid);
|
|
439
|
+
return scrollToElement(xpath);
|
|
446
440
|
}})();
|
|
447
441
|
"""
|
|
448
442
|
return js_code + "\n" + wrapper
|
|
@@ -189,19 +189,19 @@ class BrowserAutomationService:
|
|
|
189
189
|
"xpath": xpath,
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
time.sleep(0.
|
|
192
|
+
time.sleep(0.2)
|
|
193
193
|
|
|
194
194
|
self.chrome_interface.Input.dispatchMouseEvent(
|
|
195
195
|
type="mousePressed", x=x, y=y, button="left", clickCount=1
|
|
196
196
|
)
|
|
197
197
|
|
|
198
|
-
time.sleep(0.
|
|
198
|
+
time.sleep(0.05)
|
|
199
199
|
|
|
200
200
|
self.chrome_interface.Input.dispatchMouseEvent(
|
|
201
201
|
type="mouseReleased", x=x, y=y, button="left", clickCount=1
|
|
202
202
|
)
|
|
203
203
|
|
|
204
|
-
time.sleep(
|
|
204
|
+
time.sleep(0.5)
|
|
205
205
|
|
|
206
206
|
return {
|
|
207
207
|
"success": True,
|
|
@@ -222,16 +222,12 @@ class BrowserAutomationService:
|
|
|
222
222
|
"xpath": xpath,
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
def
|
|
226
|
-
self, direction: str, amount: int = 3, element_uuid: Optional[str] = None
|
|
227
|
-
) -> Dict[str, Any]:
|
|
225
|
+
def scroll_to_element(self, element_uuid: str) -> Dict[str, Any]:
|
|
228
226
|
"""
|
|
229
|
-
Scroll
|
|
227
|
+
Scroll to bring a specific element into view.
|
|
230
228
|
|
|
231
229
|
Args:
|
|
232
|
-
|
|
233
|
-
amount: Number of scroll units (default: 3)
|
|
234
|
-
element_uuid: Optional UUID of element to scroll (defaults to document)
|
|
230
|
+
element_uuid: UUID of the element to scroll to
|
|
235
231
|
|
|
236
232
|
Returns:
|
|
237
233
|
Dict containing scroll result
|
|
@@ -242,45 +238,32 @@ class BrowserAutomationService:
|
|
|
242
238
|
if self.chrome_interface is None:
|
|
243
239
|
raise RuntimeError("Chrome interface is not initialized")
|
|
244
240
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
"success": False,
|
|
253
|
-
"error": f"Element UUID '{element_uuid}' not found. Please use browser_get_content to get current element UUIDs.",
|
|
254
|
-
"uuid": element_uuid,
|
|
255
|
-
"direction": direction,
|
|
256
|
-
"amount": amount,
|
|
257
|
-
}
|
|
241
|
+
xpath = self.uuid_to_xpath_mapping.get(element_uuid)
|
|
242
|
+
if not xpath:
|
|
243
|
+
return {
|
|
244
|
+
"success": False,
|
|
245
|
+
"error": f"Element UUID '{element_uuid}' not found. Please use browser_get_content to get current element UUIDs.",
|
|
246
|
+
"uuid": element_uuid,
|
|
247
|
+
}
|
|
258
248
|
|
|
259
|
-
js_code = js_loader.
|
|
260
|
-
direction, scroll_distance, xpath or "", element_uuid or ""
|
|
261
|
-
)
|
|
249
|
+
js_code = js_loader.get_scroll_to_element_js(xpath)
|
|
262
250
|
|
|
263
251
|
scroll_result = JavaScriptExecutor.execute_and_parse_result(
|
|
264
252
|
self.chrome_interface, js_code
|
|
265
253
|
)
|
|
266
254
|
|
|
267
|
-
time.sleep(
|
|
255
|
+
time.sleep(0.5)
|
|
268
256
|
|
|
269
|
-
result_data = {"
|
|
270
|
-
if element_uuid:
|
|
271
|
-
result_data["uuid"] = element_uuid
|
|
272
|
-
result_data["xpath"] = xpath
|
|
257
|
+
result_data = {"uuid": element_uuid, "xpath": xpath, **scroll_result}
|
|
273
258
|
return result_data
|
|
274
259
|
|
|
275
260
|
except Exception as e:
|
|
276
|
-
logger.error(f"Scroll error: {e}")
|
|
277
|
-
|
|
261
|
+
logger.error(f"Scroll to element error: {e}")
|
|
262
|
+
return {
|
|
278
263
|
"success": False,
|
|
279
|
-
"error": f"Scroll error: {str(e)}",
|
|
280
|
-
"
|
|
281
|
-
"amount": amount,
|
|
264
|
+
"error": f"Scroll to element error: {str(e)}",
|
|
265
|
+
"uuid": element_uuid,
|
|
282
266
|
}
|
|
283
|
-
return error_data
|
|
284
267
|
|
|
285
268
|
def get_page_content(self) -> Dict[str, Any]:
|
|
286
269
|
"""
|
|
@@ -27,7 +27,7 @@ def get_browser_navigate_tool_definition(provider="claude") -> Dict[str, Any]:
|
|
|
27
27
|
|
|
28
28
|
if provider == "claude":
|
|
29
29
|
return {
|
|
30
|
-
"name": "
|
|
30
|
+
"name": "open_browser_url",
|
|
31
31
|
"description": tool_description,
|
|
32
32
|
"input_schema": {
|
|
33
33
|
"type": "object",
|
|
@@ -39,7 +39,7 @@ def get_browser_navigate_tool_definition(provider="claude") -> Dict[str, Any]:
|
|
|
39
39
|
return {
|
|
40
40
|
"type": "function",
|
|
41
41
|
"function": {
|
|
42
|
-
"name": "
|
|
42
|
+
"name": "open_browser_url",
|
|
43
43
|
"description": tool_description,
|
|
44
44
|
"parameters": {
|
|
45
45
|
"type": "object",
|
|
@@ -86,35 +86,22 @@ def get_browser_click_tool_definition(provider="claude") -> Dict[str, Any]:
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
|
|
89
|
-
def
|
|
90
|
-
"""Get tool definition for
|
|
89
|
+
def get_browser_scroll_to_element_tool_definition(provider="claude") -> Dict[str, Any]:
|
|
90
|
+
"""Get tool definition for scrolling to a specific element."""
|
|
91
91
|
tool_description = (
|
|
92
|
-
"Scroll
|
|
92
|
+
"Scroll to bring a specific element into view at the center of the viewport."
|
|
93
93
|
)
|
|
94
94
|
tool_arguments = {
|
|
95
|
-
"direction": {
|
|
96
|
-
"type": "string",
|
|
97
|
-
"enum": ["up", "down", "left", "right"],
|
|
98
|
-
"description": "Scroll direction.",
|
|
99
|
-
},
|
|
100
|
-
"amount": {
|
|
101
|
-
"type": "integer",
|
|
102
|
-
"description": "Scroll units (default: 3). Range 1-10.",
|
|
103
|
-
"default": 3,
|
|
104
|
-
"minimum": 1,
|
|
105
|
-
"maximum": 10,
|
|
106
|
-
},
|
|
107
95
|
"element_uuid": {
|
|
108
96
|
"type": "string",
|
|
109
|
-
"description": "
|
|
110
|
-
"default": "document",
|
|
97
|
+
"description": "UUID of the element to scroll to. Get this from browser_get_content.",
|
|
111
98
|
},
|
|
112
99
|
}
|
|
113
|
-
tool_required = ["
|
|
100
|
+
tool_required = ["element_uuid"]
|
|
114
101
|
|
|
115
102
|
if provider == "claude":
|
|
116
103
|
return {
|
|
117
|
-
"name": "
|
|
104
|
+
"name": "scroll_to_browser_element",
|
|
118
105
|
"description": tool_description,
|
|
119
106
|
"input_schema": {
|
|
120
107
|
"type": "object",
|
|
@@ -122,11 +109,11 @@ def get_browser_scroll_tool_definition(provider="claude") -> Dict[str, Any]:
|
|
|
122
109
|
"required": tool_required,
|
|
123
110
|
},
|
|
124
111
|
}
|
|
125
|
-
else:
|
|
112
|
+
else:
|
|
126
113
|
return {
|
|
127
114
|
"type": "function",
|
|
128
115
|
"function": {
|
|
129
|
-
"name": "
|
|
116
|
+
"name": "scroll_to_browser_element",
|
|
130
117
|
"description": tool_description,
|
|
131
118
|
"parameters": {
|
|
132
119
|
"type": "object",
|
|
@@ -253,35 +240,27 @@ def get_browser_click_tool_handler(
|
|
|
253
240
|
return handle_browser_click
|
|
254
241
|
|
|
255
242
|
|
|
256
|
-
def
|
|
243
|
+
def get_browser_scroll_to_element_tool_handler(
|
|
257
244
|
browser_service: BrowserAutomationService,
|
|
258
245
|
) -> Callable:
|
|
259
|
-
"""Get the handler function for the
|
|
246
|
+
"""Get the handler function for the scroll to element tool."""
|
|
260
247
|
|
|
261
|
-
def
|
|
262
|
-
direction = params.get("direction")
|
|
263
|
-
amount = params.get("amount", 3)
|
|
248
|
+
def handle_browser_scroll_to_element(**params) -> str:
|
|
264
249
|
element_uuid = params.get("element_uuid")
|
|
265
250
|
|
|
266
|
-
if not
|
|
267
|
-
return "Error: No
|
|
251
|
+
if not element_uuid:
|
|
252
|
+
return "Error: No element_uuid provided."
|
|
268
253
|
|
|
269
|
-
|
|
270
|
-
return (
|
|
271
|
-
"Error: Invalid scroll direction. Use 'up', 'down', 'left', or 'right'."
|
|
272
|
-
)
|
|
273
|
-
|
|
274
|
-
result = browser_service.scroll_page(
|
|
275
|
-
direction, amount, element_uuid if element_uuid != "document" else None
|
|
276
|
-
)
|
|
254
|
+
result = browser_service.scroll_to_element(element_uuid)
|
|
277
255
|
|
|
278
256
|
if result.get("success", True):
|
|
279
257
|
return f"{result.get('message', 'Success')}, Call `get_browser_content` tool to get the updated content."
|
|
280
258
|
else:
|
|
281
|
-
|
|
282
|
-
|
|
259
|
+
raise RuntimeError(
|
|
260
|
+
f"Scroll to element failed: {result['error']}\nUUID: {element_uuid}"
|
|
261
|
+
)
|
|
283
262
|
|
|
284
|
-
return
|
|
263
|
+
return handle_browser_scroll_to_element
|
|
285
264
|
|
|
286
265
|
|
|
287
266
|
def get_browser_input_tool_definition(provider="claude") -> Dict[str, Any]:
|
|
@@ -665,8 +644,8 @@ def register(service_instance=None, agent=None):
|
|
|
665
644
|
agent,
|
|
666
645
|
)
|
|
667
646
|
register_tool(
|
|
668
|
-
|
|
669
|
-
|
|
647
|
+
get_browser_scroll_to_element_tool_definition,
|
|
648
|
+
get_browser_scroll_to_element_tool_handler,
|
|
670
649
|
service_instance,
|
|
671
650
|
agent,
|
|
672
651
|
)
|
|
@@ -31,6 +31,8 @@ def get_run_command_tool_definition(provider="claude") -> Dict[str, Any]:
|
|
|
31
31
|
"timeout": {
|
|
32
32
|
"type": "integer",
|
|
33
33
|
"description": "Seconds (default: 5, max: 60). Returns command_id if still running.",
|
|
34
|
+
"minimum": 5,
|
|
35
|
+
"maximum": 60,
|
|
34
36
|
"default": 5,
|
|
35
37
|
},
|
|
36
38
|
"working_dir": {
|
|
@@ -220,8 +222,10 @@ def get_run_command_tool_handler(command_service: CommandExecutionService) -> Ca
|
|
|
220
222
|
|
|
221
223
|
if not command:
|
|
222
224
|
raise ValueError("Missing required parameter: command")
|
|
223
|
-
if timeout <
|
|
224
|
-
|
|
225
|
+
if timeout < 5:
|
|
226
|
+
timeout = 5
|
|
227
|
+
elif timeout > 60:
|
|
228
|
+
timeout = 60
|
|
225
229
|
|
|
226
230
|
result = command_service.execute_command(
|
|
227
231
|
command=command, timeout=timeout, working_dir=working_dir, env_vars=env_vars
|
|
@@ -227,9 +227,13 @@ class CommandHandlers:
|
|
|
227
227
|
|
|
228
228
|
Args:
|
|
229
229
|
file_or_url: Path to local file or URL to fetch agent configuration
|
|
230
|
+
Supports @hub/ prefix which converts to https://agentplace.cloud/
|
|
230
231
|
"""
|
|
231
232
|
try:
|
|
232
|
-
|
|
233
|
+
if file_or_url.startswith("@hub/"):
|
|
234
|
+
hub_host = os.environ.get("AGENTCREW_HUB_HOST", "https://agentplace.cloud")
|
|
235
|
+
file_or_url = hub_host.rstrip("/") + "/" + file_or_url[5:]
|
|
236
|
+
|
|
233
237
|
if file_or_url.startswith(("http://", "https://")):
|
|
234
238
|
self.console.print(
|
|
235
239
|
Text(
|
|
@@ -190,7 +190,7 @@ class ConsoleUI(Observer):
|
|
|
190
190
|
)
|
|
191
191
|
preview_text = Text("Conversation rewound to: ", style=RICH_STYLE_YELLOW)
|
|
192
192
|
preview_text.append(data["preview"])
|
|
193
|
-
self.
|
|
193
|
+
self._clear_and_reprint_chat()
|
|
194
194
|
|
|
195
195
|
self.display_handlers.display_message(jump_text)
|
|
196
196
|
self.display_handlers.display_message(preview_text)
|
|
@@ -292,7 +292,7 @@ class ConsoleUI(Observer):
|
|
|
292
292
|
return # Ignore resize during message processing
|
|
293
293
|
self._is_resizing = True
|
|
294
294
|
time.sleep(0.5) # brief pause to allow resize to complete
|
|
295
|
-
self.
|
|
295
|
+
self._clear_and_reprint_chat()
|
|
296
296
|
self.display_handlers.print_divider("👤 YOU: ", with_time=True)
|
|
297
297
|
prompt = Text(
|
|
298
298
|
PROMPT_CHAR,
|
|
@@ -307,7 +307,7 @@ class ConsoleUI(Observer):
|
|
|
307
307
|
self.console.print(prompt, end="")
|
|
308
308
|
self._is_resizing = False
|
|
309
309
|
|
|
310
|
-
def
|
|
310
|
+
def _clear_and_reprint_chat(self):
|
|
311
311
|
"""Clear and reprint the chat display."""
|
|
312
312
|
|
|
313
313
|
import os
|
|
@@ -105,9 +105,9 @@ class InputHandler:
|
|
|
105
105
|
file_command = paste_result["content"]
|
|
106
106
|
|
|
107
107
|
# Insert the file command into the current buffer
|
|
108
|
-
event.current_buffer.
|
|
109
|
-
event.current_buffer.
|
|
110
|
-
|
|
108
|
+
self._jumped_user_message = event.current_buffer.text
|
|
109
|
+
event.current_buffer.reset()
|
|
110
|
+
self._input_queue.put(file_command)
|
|
111
111
|
return
|
|
112
112
|
|
|
113
113
|
# For regular text content, use default paste behavior
|
|
@@ -268,13 +268,13 @@ class InputHandler:
|
|
|
268
268
|
self._current_prompt_session.app.current_buffer.reset()
|
|
269
269
|
if self._jumped_user_message:
|
|
270
270
|
self._current_prompt_session.app.current_buffer.insert_text(
|
|
271
|
-
self._jumped_user_message
|
|
271
|
+
self._jumped_user_message, overwrite=True
|
|
272
272
|
)
|
|
273
273
|
self._jumped_user_message = ""
|
|
274
|
-
if not self.is_message_processing:
|
|
275
|
-
self.display_handlers.print_divider("👤 YOU: ", with_time=True)
|
|
276
274
|
self._current_prompt_session.message = HTML(PROMPT_CHAR)
|
|
277
275
|
self._current_prompt_session.app.invalidate()
|
|
276
|
+
if not self.is_message_processing:
|
|
277
|
+
self.display_handlers.print_divider("👤 YOU: ", with_time=True)
|
|
278
278
|
|
|
279
279
|
def get_choice_input(self, message: str, values: list[str], default=None) -> str:
|
|
280
280
|
from prompt_toolkit.shortcuts import choice
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
AgentCrew/__init__.py,sha256=
|
|
1
|
+
AgentCrew/__init__.py,sha256=S5bBAK8bL7bybaXGJQuNE98fa3H65zGjTASMiyKGJGw,22
|
|
2
2
|
AgentCrew/app.py,sha256=s3pPfZfaZYxCNUC3HYWVQScK1iQZil6_Xguht3XX6mc,36603
|
|
3
3
|
AgentCrew/main.py,sha256=tjyw4Z29YKKyzVKqDgF5icI1IM59DUiJrXZw6Sv_xN8,11187
|
|
4
4
|
AgentCrew/main_docker.py,sha256=1jpB-XOm-t3GWTKYidGHHkCSzJ-p39ua0E6-_Nj8_9Y,5472
|
|
@@ -23,7 +23,7 @@ AgentCrew/modules/agents/__init__.py,sha256=rMwchcGZzapLLZdUQUaJtCLowHoG-dOn8_Vl
|
|
|
23
23
|
AgentCrew/modules/agents/base.py,sha256=i7w0X7mcTRWwgyQwqN-iOIniLce9cSM2wGG3njlZpHk,2641
|
|
24
24
|
AgentCrew/modules/agents/example.py,sha256=_-Nd7EKHprKXZLN9_ava0b9vR7wGyUzsXSEV7_js7Ho,6894
|
|
25
25
|
AgentCrew/modules/agents/local_agent.py,sha256=dVm4DBceUqxICgAM0_5gni2F94vEt2cdqxrakpDEX5s,31828
|
|
26
|
-
AgentCrew/modules/agents/manager.py,sha256=
|
|
26
|
+
AgentCrew/modules/agents/manager.py,sha256=DDFReehIWCjJLZImqR2VXOOMCfrrXOr8EeLrOkUHHJk,25177
|
|
27
27
|
AgentCrew/modules/agents/remote_agent.py,sha256=bBgO8E3dXZuu9ofklXLfL5-8mxF_XZOLyqj6rrwTXR8,6432
|
|
28
28
|
AgentCrew/modules/agents/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
29
|
AgentCrew/modules/agents/tools/ask.py,sha256=MAv1HOjIdXDIMHmQ7J6Jvlr9r9wBFSMBVYX6MsG14rI,5161
|
|
@@ -34,19 +34,19 @@ AgentCrew/modules/anthropic/service.py,sha256=olpiiptIVubc11zmbALOoAMFU-XVx_bIHc
|
|
|
34
34
|
AgentCrew/modules/browser_automation/__init__.py,sha256=1Qq6Rqz4Mv4arPKWKSDgIOV-G72R5Jey2-qdkLMLVKA,86
|
|
35
35
|
AgentCrew/modules/browser_automation/chrome_manager.py,sha256=iBFcImVoeUF5qc8eRzSsihuBSiPhDF0DOSYfda7OHas,11901
|
|
36
36
|
AgentCrew/modules/browser_automation/element_extractor.py,sha256=J1Gp6xDSWFDt7690JgBhX6ryXfNeVUCLwYBA3IGtCqo,15914
|
|
37
|
-
AgentCrew/modules/browser_automation/js_loader.py,sha256=
|
|
38
|
-
AgentCrew/modules/browser_automation/service.py,sha256=
|
|
39
|
-
AgentCrew/modules/browser_automation/tool.py,sha256=
|
|
40
|
-
AgentCrew/modules/browser_automation/js/click_element.js,sha256=
|
|
37
|
+
AgentCrew/modules/browser_automation/js_loader.py,sha256=gw5ifmsenH85V2KOgtqsQpyunvWNazEeH7u_NESDK40,18010
|
|
38
|
+
AgentCrew/modules/browser_automation/service.py,sha256=uG-ltAqtJwiS8KVAdVzf7STbYh6Rt2_4lVFIXDTSws8,27546
|
|
39
|
+
AgentCrew/modules/browser_automation/tool.py,sha256=oKhrEeE4pld4GHbxxz6Ia3eyOq11AOZyO8tv6GbjCEQ,23263
|
|
40
|
+
AgentCrew/modules/browser_automation/js/click_element.js,sha256=7xoSo6tYiUNupWuVARmKsNEsGnJXyOSMAYNQhsTgvW8,1571
|
|
41
41
|
AgentCrew/modules/browser_automation/js/draw_element_boxes.js,sha256=4ToM8UYy2HUyXCC2l219Zghjj2lG3JZD1X2mjsLgdtU,5781
|
|
42
42
|
AgentCrew/modules/browser_automation/js/extract_clickable_elements.js,sha256=UlOZ68-SyJtSc9GBYTHF-caDDKoxaJ-4UXvedPO8U7Q,5818
|
|
43
|
-
AgentCrew/modules/browser_automation/js/extract_elements_by_text.js,sha256=
|
|
43
|
+
AgentCrew/modules/browser_automation/js/extract_elements_by_text.js,sha256=_dKahrzNncwWKt4wbCq6tQyb5mV3wbVaZTiOKBcFIp0,3138
|
|
44
44
|
AgentCrew/modules/browser_automation/js/extract_input_elements.js,sha256=UIZRK5PbVAKvOn7YdBZNNA1exGwo1XiiRGWkmCH71ME,6994
|
|
45
45
|
AgentCrew/modules/browser_automation/js/extract_scrollable_elements.js,sha256=7V4dK9B0q0JfUv6NJYsRgx7YhujGByuy8ETSNVL1o6Q,5915
|
|
46
|
-
AgentCrew/modules/browser_automation/js/filter_hidden_elements.js,sha256=
|
|
46
|
+
AgentCrew/modules/browser_automation/js/filter_hidden_elements.js,sha256=vmOoz90KyX1DB-oJKfJnbFCthp4fQVT17pslmycsDEo,3049
|
|
47
47
|
AgentCrew/modules/browser_automation/js/focus_and_clear_element.js,sha256=fxN26bwmCTWq2nMM0PaBA3FElrSL1JCh0Oxj57MwCAM,2057
|
|
48
48
|
AgentCrew/modules/browser_automation/js/remove_element_boxes.js,sha256=qLFuLyWMiRM4-CI2aMYnYBeOVXDgOJVPq0JhgI6zfQo,647
|
|
49
|
-
AgentCrew/modules/browser_automation/js/
|
|
49
|
+
AgentCrew/modules/browser_automation/js/scroll_to_element.js,sha256=xeQw5F-IpRUarGBVqYdkPXd9Yfm_D8_-P2FVtj_N34w,504
|
|
50
50
|
AgentCrew/modules/browser_automation/js/trigger_input_events.js,sha256=WtPOmebgBmlP1xgkclSna30rLv5SJjvrqg6Vv-MSgd8,1347
|
|
51
51
|
AgentCrew/modules/chat/__init__.py,sha256=WCIbdcpiuFfMmR4RpyNwG3SpjDbjPZTe233VAjFtVAM,189
|
|
52
52
|
AgentCrew/modules/chat/consolidation.py,sha256=pCRsA4mdzH_ryHxHrFX2jI5aDTXuPon7ySe0aZpcnUw,10635
|
|
@@ -68,20 +68,20 @@ AgentCrew/modules/code_analysis/tool.py,sha256=BRkouK2radiW0_IAz8ptPdTLodDMsGzjE
|
|
|
68
68
|
AgentCrew/modules/command_execution/__init__.py,sha256=BIy7TSuz6yFaUQ0GEvXZO-ZSlkSwcRxforfIIhHCMTM,380
|
|
69
69
|
AgentCrew/modules/command_execution/constants.py,sha256=zG2pwmA3MqBhnJgPrcBgzxzH2sWCbXQ4NyidX8lKQ6I,4693
|
|
70
70
|
AgentCrew/modules/command_execution/service.py,sha256=cd0AkBKee_0oj4WezJo_ZICXTJ-kkcMT4J-oPN7aHz0,24814
|
|
71
|
-
AgentCrew/modules/command_execution/tool.py,sha256=
|
|
71
|
+
AgentCrew/modules/command_execution/tool.py,sha256=v2nQMlcp3uA-GXpjNM_COxzrMfSrsEf1_yu37730tz0,14049
|
|
72
72
|
AgentCrew/modules/command_execution/types.py,sha256=kosDGBMF7DFGLdhxYxhOOZHf0URKWejLluUeCCfP5EI,2477
|
|
73
73
|
AgentCrew/modules/config/__init__.py,sha256=ehO0aAkK98f9BmMjG9uR15Hc9Lwj9CnJZl24XUtUT_M,80
|
|
74
74
|
AgentCrew/modules/config/config_management.py,sha256=yxmfx1nQAZxTzJ4g3F3ybYlmTTFpMmKxfNETyzsQ9uA,33648
|
|
75
75
|
AgentCrew/modules/console/__init__.py,sha256=nO53lUaMEAshdIqDEmgNZ_r35jyg6CuMa7Tsj55Y09g,66
|
|
76
|
-
AgentCrew/modules/console/command_handlers.py,sha256=
|
|
76
|
+
AgentCrew/modules/console/command_handlers.py,sha256=1r1iW_zWFZMAqRwyYk9vM6N_2hLnLr2zPMx2013nrow,16495
|
|
77
77
|
AgentCrew/modules/console/completers.py,sha256=WeP5rJvCWq4Ept2_ajK9wjpDiTT4C5blcX5TeV3GSzU,17682
|
|
78
78
|
AgentCrew/modules/console/confirmation_handler.py,sha256=sOhJVmrMgiqTlUI6G9xjzseeeWn54G-aOc6WevWcXL8,10221
|
|
79
|
-
AgentCrew/modules/console/console_ui.py,sha256=
|
|
79
|
+
AgentCrew/modules/console/console_ui.py,sha256=jfA3X_70fyCrE1f8zsG0b1wiNpxYCEVp8dhYb2ZM2KE,29836
|
|
80
80
|
AgentCrew/modules/console/constants.py,sha256=fwLj52O96_t6m1qb0SOiaotM2dMLwXH83KAERm9ltLA,704
|
|
81
81
|
AgentCrew/modules/console/conversation_handler.py,sha256=vVtGxQJ4sEZJ77svBFJMIGiWiEfE47yDxvt7gZ9bRCA,3632
|
|
82
82
|
AgentCrew/modules/console/diff_display.py,sha256=HTFy5H0sx6OHN_yjMPUeF8aum7esdgIg9Xd4_gCvPaw,7193
|
|
83
83
|
AgentCrew/modules/console/display_handlers.py,sha256=ZSpBZxvX5X9VB2dvAB679KQwPTVhRPHRrmsgCMhANyA,17651
|
|
84
|
-
AgentCrew/modules/console/input_handler.py,sha256=
|
|
84
|
+
AgentCrew/modules/console/input_handler.py,sha256=Nn8H0RZiuF2asw8uum5n1otGOEb19U-_SMxVCTx3QTQ,18208
|
|
85
85
|
AgentCrew/modules/console/tool_display.py,sha256=aTjNWcfzmJXwpXU64PqMfNFRCiVrtMD-JFn_Wd2cNYY,7306
|
|
86
86
|
AgentCrew/modules/console/ui_effects.py,sha256=n5w863kI0VKU_afGmXjscYg8FmsaX4IJyNQRGCDoOLI,8218
|
|
87
87
|
AgentCrew/modules/console/utils.py,sha256=TFIyyYVFlWMB0FCAq4H09Yp6UCezUEHg3HNEZXuVmiA,273
|
|
@@ -186,9 +186,9 @@ AgentCrew/modules/voice/text_cleaner.py,sha256=NgAVBPkP2hFI330nJOyMK_oqP3R2AGZ22
|
|
|
186
186
|
AgentCrew/modules/web_search/__init__.py,sha256=sVf_z6nH2JghK46pG92PUtDghPIkceiX1F0Ul30euXU,111
|
|
187
187
|
AgentCrew/modules/web_search/service.py,sha256=DKcOdRSHB5AEvbK8pvTXdffSnk6rFRTzaM1FXf2B70E,4006
|
|
188
188
|
AgentCrew/modules/web_search/tool.py,sha256=GV4xleVFs0UwiPS9toSzPzZei3ehsDZWxTQCJCRaEs8,6655
|
|
189
|
-
agentcrew_ai-0.8.
|
|
190
|
-
agentcrew_ai-0.8.
|
|
191
|
-
agentcrew_ai-0.8.
|
|
192
|
-
agentcrew_ai-0.8.
|
|
193
|
-
agentcrew_ai-0.8.
|
|
194
|
-
agentcrew_ai-0.8.
|
|
189
|
+
agentcrew_ai-0.8.8.dist-info/licenses/LICENSE,sha256=O51CIaOUcxVLNf0_PE_a8ap5bf3iXe4SrWN_5NO1PSU,11348
|
|
190
|
+
agentcrew_ai-0.8.8.dist-info/METADATA,sha256=cz6FnFH2CvTU0-GLEgqO0eEzO8K3wfw-J0q0QjvzMSg,18056
|
|
191
|
+
agentcrew_ai-0.8.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
192
|
+
agentcrew_ai-0.8.8.dist-info/entry_points.txt,sha256=N9R5jslrBYA8dTaNMRJ_JdSosJ6jkpGEtnJFzlcZD6k,54
|
|
193
|
+
agentcrew_ai-0.8.8.dist-info/top_level.txt,sha256=bSsmhCOn6g-JytoVMpxB_QAnCgb7lV56fcnxUhbfrvg,10
|
|
194
|
+
agentcrew_ai-0.8.8.dist-info/RECORD,,
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Scroll the page or a specific element in specified direction.
|
|
3
|
-
*
|
|
4
|
-
* @param {string} direction - Direction to scroll ('up', 'down', 'left', 'right')
|
|
5
|
-
* @param {number} distance - Distance to scroll in pixels
|
|
6
|
-
* @param {string} xpath - Optional XPath of specific element to scroll
|
|
7
|
-
* @param {string} elementUuid - Optional UUID of the element for identification
|
|
8
|
-
* @returns {Object} Result object with success status, message, and scroll info
|
|
9
|
-
*/
|
|
10
|
-
function scrollPage(direction, distance, xpath = "", elementUuid = "") {
|
|
11
|
-
let scrollX = 0;
|
|
12
|
-
let scrollY = 0;
|
|
13
|
-
|
|
14
|
-
switch (direction.toLowerCase()) {
|
|
15
|
-
case "up":
|
|
16
|
-
scrollY = -distance;
|
|
17
|
-
break;
|
|
18
|
-
case "down":
|
|
19
|
-
scrollY = distance;
|
|
20
|
-
break;
|
|
21
|
-
case "left":
|
|
22
|
-
scrollX = -distance;
|
|
23
|
-
break;
|
|
24
|
-
case "right":
|
|
25
|
-
scrollX = distance;
|
|
26
|
-
break;
|
|
27
|
-
default:
|
|
28
|
-
return {
|
|
29
|
-
success: false,
|
|
30
|
-
error: "Invalid direction. Use 'up', 'down', 'left', or 'right'",
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Determine scroll target (specific element or document)
|
|
35
|
-
let scrollTarget = document.documentElement || document.body;
|
|
36
|
-
let targetDescription = "the whole document";
|
|
37
|
-
|
|
38
|
-
if (xpath && elementUuid) {
|
|
39
|
-
const result = document.evaluate(
|
|
40
|
-
xpath,
|
|
41
|
-
document,
|
|
42
|
-
null,
|
|
43
|
-
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
|
44
|
-
null,
|
|
45
|
-
);
|
|
46
|
-
const element = result.singleNodeValue;
|
|
47
|
-
|
|
48
|
-
if (!element) {
|
|
49
|
-
return { success: false, error: "Element not found" };
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Check if element is scrollable
|
|
53
|
-
const style = window.getComputedStyle(element);
|
|
54
|
-
const hasScrollableOverflow =
|
|
55
|
-
["auto", "scroll"].includes(style.overflow) ||
|
|
56
|
-
["auto", "scroll"].includes(style.overflowY) ||
|
|
57
|
-
["auto", "scroll"].includes(style.overflowX);
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
hasScrollableOverflow ||
|
|
61
|
-
element.scrollHeight > element.clientHeight ||
|
|
62
|
-
element.scrollWidth > element.clientWidth
|
|
63
|
-
) {
|
|
64
|
-
scrollTarget = element;
|
|
65
|
-
targetDescription = "element " + elementUuid;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Get current scroll position
|
|
70
|
-
const currentX =
|
|
71
|
-
scrollTarget === document.documentElement
|
|
72
|
-
? window.pageXOffset || document.documentElement.scrollLeft
|
|
73
|
-
: scrollTarget.scrollLeft;
|
|
74
|
-
const currentY =
|
|
75
|
-
scrollTarget === document.documentElement
|
|
76
|
-
? window.pageYOffset || document.documentElement.scrollTop
|
|
77
|
-
: scrollTarget.scrollTop;
|
|
78
|
-
|
|
79
|
-
// Create wheel event for smooth scrolling
|
|
80
|
-
const wheelEventOptions = {
|
|
81
|
-
view: window,
|
|
82
|
-
bubbles: true,
|
|
83
|
-
cancelable: true,
|
|
84
|
-
deltaX: scrollX,
|
|
85
|
-
deltaY: scrollY,
|
|
86
|
-
deltaMode: WheelEvent.DOM_DELTA_PIXEL,
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
// Dispatch wheel event to mimic true user scroll
|
|
91
|
-
const wheelEvent = new WheelEvent("wheel", wheelEventOptions);
|
|
92
|
-
|
|
93
|
-
if (scrollTarget === document.documentElement) {
|
|
94
|
-
document.dispatchEvent(wheelEvent);
|
|
95
|
-
// Also perform actual scroll for fallback
|
|
96
|
-
window.scrollBy(scrollX, scrollY);
|
|
97
|
-
} else {
|
|
98
|
-
scrollTarget.dispatchEvent(wheelEvent);
|
|
99
|
-
// Perform actual scroll on the element
|
|
100
|
-
scrollTarget.scrollBy(scrollX, scrollY);
|
|
101
|
-
}
|
|
102
|
-
} catch (eventError) {
|
|
103
|
-
// Fallback to direct scrolling if wheel event fails
|
|
104
|
-
if (scrollTarget === document.documentElement) {
|
|
105
|
-
window.scrollBy(scrollX, scrollY);
|
|
106
|
-
} else {
|
|
107
|
-
scrollTarget.scrollBy(scrollX, scrollY);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Get new scroll position
|
|
112
|
-
const newX =
|
|
113
|
-
scrollTarget === document.documentElement
|
|
114
|
-
? window.pageXOffset || document.documentElement.scrollLeft
|
|
115
|
-
: scrollTarget.scrollLeft;
|
|
116
|
-
const newY =
|
|
117
|
-
scrollTarget === document.documentElement
|
|
118
|
-
? window.pageYOffset || document.documentElement.scrollTop
|
|
119
|
-
: scrollTarget.scrollTop;
|
|
120
|
-
|
|
121
|
-
return {
|
|
122
|
-
success: true,
|
|
123
|
-
message:
|
|
124
|
-
"Scrolled " +
|
|
125
|
-
targetDescription +
|
|
126
|
-
" " +
|
|
127
|
-
direction +
|
|
128
|
-
" by " +
|
|
129
|
-
Math.abs(scrollX || scrollY) +
|
|
130
|
-
"px using dispatchEvent",
|
|
131
|
-
previous_position: { x: currentX, y: currentY },
|
|
132
|
-
new_position: { x: newX, y: newY },
|
|
133
|
-
target: targetDescription,
|
|
134
|
-
scroll_method: "wheel_event_with_fallback",
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Export the function - when used in browser automation, wrap with IIFE and pass parameters
|
|
139
|
-
// (() => {
|
|
140
|
-
// const direction = '{DIRECTION_PLACEHOLDER}';
|
|
141
|
-
// const distance = {DISTANCE_PLACEHOLDER};
|
|
142
|
-
// const xpath = '{XPATH_PLACEHOLDER}';
|
|
143
|
-
// const elementUuid = '{ELEMENT_UUID_PLACEHOLDER}';
|
|
144
|
-
// return scrollPage(direction, distance, xpath, elementUuid);
|
|
145
|
-
// })();
|
|
146
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|