agentcrew-ai 0.8.2__py3-none-any.whl → 0.8.4__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/main.py +3 -1
- AgentCrew/modules/a2a/agent_cards.py +8 -2
- AgentCrew/modules/a2a/errors.py +72 -0
- AgentCrew/modules/a2a/server.py +21 -2
- AgentCrew/modules/a2a/task_manager.py +180 -39
- AgentCrew/modules/agents/local_agent.py +11 -0
- AgentCrew/modules/browser_automation/element_extractor.py +4 -3
- AgentCrew/modules/browser_automation/js/draw_element_boxes.js +200 -0
- AgentCrew/modules/browser_automation/js/extract_clickable_elements.js +58 -26
- AgentCrew/modules/browser_automation/js/extract_elements_by_text.js +21 -19
- AgentCrew/modules/browser_automation/js/extract_input_elements.js +22 -23
- AgentCrew/modules/browser_automation/js/filter_hidden_elements.js +104 -0
- AgentCrew/modules/browser_automation/js/remove_element_boxes.js +29 -0
- AgentCrew/modules/browser_automation/js_loader.py +385 -92
- AgentCrew/modules/browser_automation/service.py +118 -347
- AgentCrew/modules/browser_automation/tool.py +28 -29
- AgentCrew/modules/chat/message/command_processor.py +7 -1
- AgentCrew/modules/chat/message/conversation.py +9 -8
- AgentCrew/modules/code_analysis/service.py +39 -0
- AgentCrew/modules/code_analysis/tool.py +10 -1
- AgentCrew/modules/console/command_handlers.py +186 -1
- AgentCrew/modules/console/completers.py +67 -0
- AgentCrew/modules/console/console_ui.py +59 -5
- AgentCrew/modules/console/display_handlers.py +12 -0
- AgentCrew/modules/console/input_handler.py +2 -0
- AgentCrew/modules/console/ui_effects.py +3 -4
- AgentCrew/modules/custom_llm/service.py +25 -3
- AgentCrew/modules/file_editing/tool.py +9 -11
- AgentCrew/modules/google/native_service.py +13 -0
- AgentCrew/modules/gui/widgets/message_bubble.py +1 -6
- AgentCrew/modules/llm/constants.py +38 -1
- AgentCrew/modules/llm/model_registry.py +9 -0
- AgentCrew/modules/llm/types.py +12 -1
- AgentCrew/modules/memory/base_service.py +2 -2
- AgentCrew/modules/memory/chroma_service.py +79 -138
- AgentCrew/modules/memory/context_persistent.py +10 -4
- AgentCrew/modules/memory/tool.py +17 -18
- AgentCrew/modules/openai/response_service.py +19 -11
- AgentCrew/modules/openai/service.py +15 -0
- AgentCrew/modules/prompts/constants.py +27 -14
- {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.4.dist-info}/METADATA +3 -3
- {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.4.dist-info}/RECORD +47 -43
- {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.4.dist-info}/WHEEL +0 -0
- {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.4.dist-info}/entry_points.txt +0 -0
- {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.4.dist-info}/licenses/LICENSE +0 -0
- {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.4.dist-info}/top_level.txt +0 -0
|
@@ -6,14 +6,367 @@ for use with Chrome DevTools Protocol.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from typing import Dict
|
|
9
|
+
from typing import Dict, Any, Optional
|
|
10
|
+
import time
|
|
11
|
+
from loguru import logger
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class JavaScriptExecutor:
|
|
15
|
+
"""Handles JavaScript code execution and result parsing for browser automation."""
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
def execute_and_parse_result(chrome_interface: Any, js_code: str) -> Dict[str, Any]:
|
|
19
|
+
"""
|
|
20
|
+
Execute JavaScript code and parse the result.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
chrome_interface: Chrome DevTools Protocol interface
|
|
24
|
+
js_code: JavaScript code to execute
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Parsed result dictionary
|
|
28
|
+
"""
|
|
29
|
+
try:
|
|
30
|
+
result = (None, [])
|
|
31
|
+
retried = 0
|
|
32
|
+
while result[0] is None and retried < 10:
|
|
33
|
+
result = chrome_interface.Runtime.evaluate(
|
|
34
|
+
expression=js_code,
|
|
35
|
+
returnByValue=True,
|
|
36
|
+
awaitPromise=True,
|
|
37
|
+
timeout=60000,
|
|
38
|
+
)
|
|
39
|
+
retried += 1
|
|
40
|
+
time.sleep(0.4)
|
|
41
|
+
|
|
42
|
+
if isinstance(result, tuple) and len(result) >= 2:
|
|
43
|
+
if isinstance(result[1], dict):
|
|
44
|
+
return (
|
|
45
|
+
result[1].get("result", {}).get("result", {}).get("value", {})
|
|
46
|
+
)
|
|
47
|
+
elif isinstance(result[1], list) and len(result[1]) > 0:
|
|
48
|
+
return (
|
|
49
|
+
result[1][0]
|
|
50
|
+
.get("result", {})
|
|
51
|
+
.get("result", {})
|
|
52
|
+
.get("value", {})
|
|
53
|
+
)
|
|
54
|
+
else:
|
|
55
|
+
return {
|
|
56
|
+
"success": False,
|
|
57
|
+
"error": "Invalid response format from JavaScript execution",
|
|
58
|
+
}
|
|
59
|
+
else:
|
|
60
|
+
return {
|
|
61
|
+
"success": False,
|
|
62
|
+
"error": "No response from JavaScript execution",
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
except Exception as e:
|
|
66
|
+
logger.error(f"JavaScript execution error: {e}")
|
|
67
|
+
return {"success": False, "error": f"JavaScript execution error: {str(e)}"}
|
|
68
|
+
|
|
69
|
+
@staticmethod
|
|
70
|
+
def get_current_url(chrome_interface: Any) -> str:
|
|
71
|
+
"""
|
|
72
|
+
Get the current page URL.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
chrome_interface: Chrome DevTools Protocol interface
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Current URL or "Unknown" if retrieval fails
|
|
79
|
+
"""
|
|
80
|
+
try:
|
|
81
|
+
runtime_result = chrome_interface.Runtime.evaluate(
|
|
82
|
+
expression="window.location.href"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if isinstance(runtime_result, tuple) and len(runtime_result) >= 2:
|
|
86
|
+
if isinstance(runtime_result[1], dict):
|
|
87
|
+
current_url = (
|
|
88
|
+
runtime_result[1]
|
|
89
|
+
.get("result", {})
|
|
90
|
+
.get("result", {})
|
|
91
|
+
.get("value", "Unknown")
|
|
92
|
+
)
|
|
93
|
+
elif isinstance(runtime_result[1], list) and len(runtime_result[1]) > 0:
|
|
94
|
+
current_url = (
|
|
95
|
+
runtime_result[1][0]
|
|
96
|
+
.get("result", {})
|
|
97
|
+
.get("result", {})
|
|
98
|
+
.get("value", "Unknown")
|
|
99
|
+
)
|
|
100
|
+
else:
|
|
101
|
+
current_url = "Unknown"
|
|
102
|
+
else:
|
|
103
|
+
current_url = "Unknown"
|
|
104
|
+
|
|
105
|
+
return current_url
|
|
106
|
+
|
|
107
|
+
except Exception as e:
|
|
108
|
+
logger.warning(f"Could not get current URL: {e}")
|
|
109
|
+
return "Unknown"
|
|
110
|
+
|
|
111
|
+
@staticmethod
|
|
112
|
+
def focus_and_clear_element(chrome_interface: Any, xpath: str) -> Dict[str, Any]:
|
|
113
|
+
"""
|
|
114
|
+
Focus an element and clear its content.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
chrome_interface: Chrome DevTools Protocol interface
|
|
118
|
+
xpath: XPath selector for the element
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Result dictionary with success status
|
|
122
|
+
"""
|
|
123
|
+
js_code = js_loader.get_focus_and_clear_element_js(xpath)
|
|
124
|
+
return JavaScriptExecutor.execute_and_parse_result(chrome_interface, js_code)
|
|
125
|
+
|
|
126
|
+
@staticmethod
|
|
127
|
+
def draw_element_boxes(
|
|
128
|
+
chrome_interface: Any, uuid_xpath_dict: Dict[str, str]
|
|
129
|
+
) -> Dict[str, Any]:
|
|
130
|
+
"""
|
|
131
|
+
Draw colored rectangle boxes with UUID labels over elements.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
uuid_xpath_dict: Dictionary mapping UUIDs to XPath selectors
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
Dict containing the result of the drawing operation
|
|
138
|
+
"""
|
|
139
|
+
try:
|
|
140
|
+
js_code = js_loader.get_draw_element_boxes_js(uuid_xpath_dict)
|
|
141
|
+
eval_result = JavaScriptExecutor.execute_and_parse_result(
|
|
142
|
+
chrome_interface, js_code
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if not eval_result:
|
|
146
|
+
return {
|
|
147
|
+
"success": False,
|
|
148
|
+
"error": "No result from drawing element boxes",
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return eval_result
|
|
152
|
+
|
|
153
|
+
except Exception as e:
|
|
154
|
+
logger.error(f"Draw element boxes error: {e}")
|
|
155
|
+
return {"success": False, "error": f"Draw element boxes error: {str(e)}"}
|
|
156
|
+
|
|
157
|
+
@staticmethod
|
|
158
|
+
def remove_element_boxes(chrome_interface: Any) -> Dict[str, Any]:
|
|
159
|
+
"""
|
|
160
|
+
Remove the overlay container with element boxes.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Dict containing the result of the removal operation
|
|
164
|
+
"""
|
|
165
|
+
try:
|
|
166
|
+
js_code = js_loader.get_remove_element_boxes_js()
|
|
167
|
+
eval_result = JavaScriptExecutor.execute_and_parse_result(
|
|
168
|
+
chrome_interface, js_code
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
if not eval_result:
|
|
172
|
+
return {
|
|
173
|
+
"success": False,
|
|
174
|
+
"error": "No result from removing element boxes",
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return eval_result
|
|
178
|
+
|
|
179
|
+
except Exception as e:
|
|
180
|
+
logger.error(f"Remove element boxes error: {e}")
|
|
181
|
+
return {"success": False, "error": f"Remove element boxes error: {str(e)}"}
|
|
182
|
+
|
|
183
|
+
@staticmethod
|
|
184
|
+
def trigger_input_events(
|
|
185
|
+
chrome_interface: Any, xpath: str, value: str
|
|
186
|
+
) -> Dict[str, Any]:
|
|
187
|
+
"""
|
|
188
|
+
Trigger input and change events on an element.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
chrome_interface: Chrome DevTools Protocol interface
|
|
192
|
+
xpath: XPath selector for the element
|
|
193
|
+
value: Value to set
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Result dictionary with success status
|
|
197
|
+
"""
|
|
198
|
+
js_code = js_loader.get_trigger_input_events_js(xpath, value)
|
|
199
|
+
return JavaScriptExecutor.execute_and_parse_result(chrome_interface, js_code)
|
|
200
|
+
|
|
201
|
+
@staticmethod
|
|
202
|
+
def simulate_typing(chrome_interface: Any, text: str) -> Dict[str, Any]:
|
|
203
|
+
"""
|
|
204
|
+
Simulate keyboard typing character by character.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
chrome_interface: Chrome DevTools Protocol interface
|
|
208
|
+
text: Text to type
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Result dictionary with success status and characters typed
|
|
212
|
+
"""
|
|
213
|
+
try:
|
|
214
|
+
for char in text:
|
|
215
|
+
time.sleep(0.05)
|
|
216
|
+
|
|
217
|
+
if char == "\n":
|
|
218
|
+
chrome_interface.Input.dispatchKeyEvent(
|
|
219
|
+
**{
|
|
220
|
+
"type": "rawKeyDown",
|
|
221
|
+
"windowsVirtualKeyCode": 13,
|
|
222
|
+
"unmodifiedText": "\r",
|
|
223
|
+
"text": "\r",
|
|
224
|
+
}
|
|
225
|
+
)
|
|
226
|
+
chrome_interface.Input.dispatchKeyEvent(
|
|
227
|
+
**{
|
|
228
|
+
"type": "char",
|
|
229
|
+
"windowsVirtualKeyCode": 13,
|
|
230
|
+
"unmodifiedText": "\r",
|
|
231
|
+
"text": "\r",
|
|
232
|
+
}
|
|
233
|
+
)
|
|
234
|
+
chrome_interface.Input.dispatchKeyEvent(
|
|
235
|
+
**{
|
|
236
|
+
"type": "keyUp",
|
|
237
|
+
"windowsVirtualKeyCode": 13,
|
|
238
|
+
"unmodifiedText": "\r",
|
|
239
|
+
"text": "\r",
|
|
240
|
+
}
|
|
241
|
+
)
|
|
242
|
+
elif char == "\t":
|
|
243
|
+
chrome_interface.Input.dispatchKeyEvent(type="char", text="\t")
|
|
244
|
+
else:
|
|
245
|
+
chrome_interface.Input.dispatchKeyEvent(type="char", text=char)
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
"success": True,
|
|
249
|
+
"message": f"Successfully typed {len(text)} characters",
|
|
250
|
+
"characters_typed": len(text),
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
except Exception as e:
|
|
254
|
+
logger.error(f"Error during typing simulation: {e}")
|
|
255
|
+
return {"success": False, "error": f"Typing simulation failed: {str(e)}"}
|
|
256
|
+
|
|
257
|
+
@staticmethod
|
|
258
|
+
def dispatch_key_event(
|
|
259
|
+
chrome_interface: Any, key: str, modifiers: Optional[list] = None
|
|
260
|
+
) -> Dict[str, Any]:
|
|
261
|
+
"""
|
|
262
|
+
Dispatch key events using CDP.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
chrome_interface: Chrome DevTools Protocol interface
|
|
266
|
+
key: Key to dispatch (e.g., 'Enter', 'Up', 'Down')
|
|
267
|
+
modifiers: Optional list of modifiers ('ctrl', 'alt', 'shift')
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
Result dictionary with success status
|
|
271
|
+
"""
|
|
272
|
+
if modifiers is None:
|
|
273
|
+
modifiers = []
|
|
274
|
+
|
|
275
|
+
try:
|
|
276
|
+
key_name = key.lower().strip()
|
|
277
|
+
key_code = key_codes.get(key_name)
|
|
278
|
+
|
|
279
|
+
if key_code is None:
|
|
280
|
+
return {
|
|
281
|
+
"success": False,
|
|
282
|
+
"error": f"Unknown key '{key}'. Supported keys: {', '.join(sorted(key_codes.keys()))}",
|
|
283
|
+
"key": key,
|
|
284
|
+
"modifiers": modifiers,
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
modifier_flags = 0
|
|
288
|
+
if modifiers:
|
|
289
|
+
modifier_names = [m.strip().lower() for m in modifiers]
|
|
290
|
+
for mod in modifier_names:
|
|
291
|
+
if mod in ["alt"]:
|
|
292
|
+
modifier_flags |= 1
|
|
293
|
+
elif mod in ["ctrl", "control"]:
|
|
294
|
+
modifier_flags |= 2
|
|
295
|
+
elif mod in ["meta", "cmd", "command"]:
|
|
296
|
+
modifier_flags |= 4
|
|
297
|
+
elif mod in ["shift"]:
|
|
298
|
+
modifier_flags |= 8
|
|
299
|
+
|
|
300
|
+
chrome_interface.Input.dispatchKeyEvent(
|
|
301
|
+
type="rawKeyDown",
|
|
302
|
+
windowsVirtualKeyCode=key_code,
|
|
303
|
+
modifiers=modifier_flags,
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
printable_keys = {"space", "spacebar", "enter", "return", "tab"}
|
|
307
|
+
if key_name in printable_keys:
|
|
308
|
+
if key_name in ["space", "spacebar"]:
|
|
309
|
+
char_text = " "
|
|
310
|
+
elif key_name in ["enter", "return"]:
|
|
311
|
+
char_text = "\r"
|
|
312
|
+
elif key_name == "tab":
|
|
313
|
+
char_text = "\t"
|
|
314
|
+
else:
|
|
315
|
+
char_text = ""
|
|
316
|
+
|
|
317
|
+
if char_text:
|
|
318
|
+
chrome_interface.Input.dispatchKeyEvent(
|
|
319
|
+
type="char",
|
|
320
|
+
windowsVirtualKeyCode=key_code,
|
|
321
|
+
text=char_text,
|
|
322
|
+
unmodifiedText=char_text,
|
|
323
|
+
modifiers=modifier_flags,
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
chrome_interface.Input.dispatchKeyEvent(
|
|
327
|
+
type="keyUp", windowsVirtualKeyCode=key_code, modifiers=modifier_flags
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
time.sleep(0.1)
|
|
331
|
+
|
|
332
|
+
return {
|
|
333
|
+
"success": True,
|
|
334
|
+
"message": f"Successfully dispatched key '{key}' with modifiers '{modifiers}'",
|
|
335
|
+
"key": key,
|
|
336
|
+
"key_code": key_code,
|
|
337
|
+
"modifiers": modifiers,
|
|
338
|
+
"modifier_flags": modifier_flags,
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
except Exception as e:
|
|
342
|
+
logger.error(f"Key dispatch error: {e}")
|
|
343
|
+
return {
|
|
344
|
+
"success": False,
|
|
345
|
+
"error": f"Key dispatch error: {str(e)}",
|
|
346
|
+
"key": key,
|
|
347
|
+
"modifiers": modifiers,
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
@staticmethod
|
|
351
|
+
def filter_hidden_elements(chrome_interface: Any) -> Dict[str, Any]:
|
|
352
|
+
"""
|
|
353
|
+
Filter hidden elements from HTML using computed styles.
|
|
354
|
+
Does not modify the actual page, returns filtered HTML string.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
chrome_interface: Chrome DevTools Protocol interface
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
Result dictionary with filtered HTML string
|
|
361
|
+
"""
|
|
362
|
+
js_code = js_loader.get_filter_hidden_elements_js()
|
|
363
|
+
return JavaScriptExecutor.execute_and_parse_result(chrome_interface, js_code)
|
|
10
364
|
|
|
11
365
|
|
|
12
366
|
class JavaScriptLoader:
|
|
13
367
|
"""Loads and processes JavaScript files for browser automation."""
|
|
14
368
|
|
|
15
369
|
def __init__(self):
|
|
16
|
-
"""Initialize the JavaScript loader with the js directory path."""
|
|
17
370
|
self.js_dir = Path(__file__).parent / "js"
|
|
18
371
|
self._js_cache: Dict[str, str] = {}
|
|
19
372
|
|
|
@@ -30,11 +383,9 @@ class JavaScriptLoader:
|
|
|
30
383
|
Raises:
|
|
31
384
|
FileNotFoundError: If the JavaScript file doesn't exist
|
|
32
385
|
"""
|
|
33
|
-
# Ensure .js extension
|
|
34
386
|
if not filename.endswith(".js"):
|
|
35
387
|
filename += ".js"
|
|
36
388
|
|
|
37
|
-
# Check cache first
|
|
38
389
|
if filename in self._js_cache:
|
|
39
390
|
return self._js_cache[filename]
|
|
40
391
|
|
|
@@ -45,95 +396,46 @@ class JavaScriptLoader:
|
|
|
45
396
|
with open(file_path, "r", encoding="utf-8") as f:
|
|
46
397
|
js_code = f.read()
|
|
47
398
|
|
|
48
|
-
# Cache the loaded code
|
|
49
399
|
self._js_cache[filename] = js_code
|
|
50
400
|
return js_code
|
|
51
401
|
|
|
52
402
|
def get_extract_clickable_elements_js(self) -> str:
|
|
53
|
-
"""Get JavaScript code for extracting clickable elements."""
|
|
54
403
|
return self.load_js_file("extract_clickable_elements.js")
|
|
55
404
|
|
|
56
405
|
def get_extract_input_elements_js(self) -> str:
|
|
57
|
-
"""Get JavaScript code for extracting input elements."""
|
|
58
406
|
return self.load_js_file("extract_input_elements.js")
|
|
59
407
|
|
|
60
408
|
def get_extract_scrollable_elements_js(self) -> str:
|
|
61
|
-
"""Get JavaScript code for extracting scrollable elements."""
|
|
62
409
|
return self.load_js_file("extract_scrollable_elements.js")
|
|
63
410
|
|
|
64
411
|
def get_extract_elements_by_text_js(self, text: str) -> str:
|
|
65
|
-
"""
|
|
66
|
-
Get JavaScript code for extracting elements by text.
|
|
67
|
-
|
|
68
|
-
Args:
|
|
69
|
-
text: Text to search for in elements
|
|
70
|
-
|
|
71
|
-
Returns:
|
|
72
|
-
JavaScript code with text parameter injected
|
|
73
|
-
"""
|
|
74
|
-
# Load the base function
|
|
75
412
|
js_code = self.load_js_file("extract_elements_by_text.js")
|
|
76
|
-
|
|
77
|
-
# Escape the text for JavaScript
|
|
78
413
|
escaped_text = text.replace("'", "\\'").replace("\\", "\\\\")
|
|
79
|
-
|
|
80
|
-
# Wrap with IIFE and inject the text parameter
|
|
81
414
|
wrapper = f"""
|
|
82
415
|
(() => {{
|
|
83
416
|
const text = `{escaped_text}`;
|
|
84
417
|
return extractElementsByText(text);
|
|
85
418
|
}})();
|
|
86
419
|
"""
|
|
87
|
-
|
|
88
420
|
return js_code + "\n" + wrapper
|
|
89
421
|
|
|
90
422
|
def get_click_element_js(self, xpath: str) -> str:
|
|
91
|
-
"""
|
|
92
|
-
Get JavaScript code for clicking an element.
|
|
93
|
-
|
|
94
|
-
Args:
|
|
95
|
-
xpath: XPath selector for the element to click
|
|
96
|
-
|
|
97
|
-
Returns:
|
|
98
|
-
JavaScript code with xpath parameter injected
|
|
99
|
-
"""
|
|
100
423
|
js_code = self.load_js_file("click_element.js")
|
|
101
|
-
|
|
102
|
-
# Escape the xpath for JavaScript
|
|
103
424
|
escaped_xpath = xpath.replace("`", "\\`").replace("\\", "\\\\")
|
|
104
|
-
|
|
105
|
-
# Wrap with IIFE and inject the xpath parameter
|
|
106
425
|
wrapper = f"""
|
|
107
426
|
(() => {{
|
|
108
427
|
const xpath = `{escaped_xpath}`;
|
|
109
428
|
return clickElement(xpath);
|
|
110
429
|
}})();
|
|
111
430
|
"""
|
|
112
|
-
|
|
113
431
|
return js_code + "\n" + wrapper
|
|
114
432
|
|
|
115
433
|
def get_scroll_page_js(
|
|
116
434
|
self, direction: str, distance: int, xpath: str = "", element_uuid: str = ""
|
|
117
435
|
) -> str:
|
|
118
|
-
"""
|
|
119
|
-
Get JavaScript code for scrolling the page or element.
|
|
120
|
-
|
|
121
|
-
Args:
|
|
122
|
-
direction: Direction to scroll ('up', 'down', 'left', 'right')
|
|
123
|
-
distance: Distance to scroll in pixels
|
|
124
|
-
xpath: Optional XPath of specific element to scroll
|
|
125
|
-
element_uuid: Optional UUID of the element for identification
|
|
126
|
-
|
|
127
|
-
Returns:
|
|
128
|
-
JavaScript code with parameters injected
|
|
129
|
-
"""
|
|
130
436
|
js_code = self.load_js_file("scroll_page.js")
|
|
131
|
-
|
|
132
|
-
# Escape parameters for JavaScript
|
|
133
437
|
escaped_xpath = xpath.replace("`", "\\`").replace("\\", "\\\\")
|
|
134
438
|
escaped_uuid = element_uuid.replace("`", "\\`").replace("\\", "\\\\")
|
|
135
|
-
|
|
136
|
-
# Wrap with IIFE and inject parameters
|
|
137
439
|
wrapper = f"""
|
|
138
440
|
(() => {{
|
|
139
441
|
const direction = '{direction}';
|
|
@@ -143,50 +445,22 @@ class JavaScriptLoader:
|
|
|
143
445
|
return scrollPage(direction, distance, xpath, elementUuid);
|
|
144
446
|
}})();
|
|
145
447
|
"""
|
|
146
|
-
|
|
147
448
|
return js_code + "\n" + wrapper
|
|
148
449
|
|
|
149
450
|
def get_focus_and_clear_element_js(self, xpath: str) -> str:
|
|
150
|
-
"""
|
|
151
|
-
Get JavaScript code for focusing and clearing an element.
|
|
152
|
-
|
|
153
|
-
Args:
|
|
154
|
-
xpath: XPath selector for the element
|
|
155
|
-
|
|
156
|
-
Returns:
|
|
157
|
-
JavaScript code with xpath parameter injected
|
|
158
|
-
"""
|
|
159
451
|
js_code = self.load_js_file("focus_and_clear_element.js")
|
|
160
|
-
|
|
161
|
-
# Escape the xpath for JavaScript
|
|
162
452
|
escaped_xpath = xpath.replace("`", "\\`").replace("\\", "\\\\")
|
|
163
|
-
|
|
164
|
-
# Wrap with IIFE and inject the xpath parameter
|
|
165
453
|
wrapper = f"""
|
|
166
454
|
(() => {{
|
|
167
455
|
const xpath = `{escaped_xpath}`;
|
|
168
456
|
return focusAndClearElement(xpath);
|
|
169
457
|
}})();
|
|
170
458
|
"""
|
|
171
|
-
|
|
172
459
|
return js_code + "\n" + wrapper
|
|
173
460
|
|
|
174
461
|
def get_trigger_input_events_js(self, xpath: str, value: str) -> str:
|
|
175
|
-
"""
|
|
176
|
-
Get JavaScript code for triggering input events.
|
|
177
|
-
|
|
178
|
-
Args:
|
|
179
|
-
xpath: XPath selector for the element
|
|
180
|
-
|
|
181
|
-
Returns:
|
|
182
|
-
JavaScript code with xpath parameter injected
|
|
183
|
-
"""
|
|
184
462
|
js_code = self.load_js_file("trigger_input_events.js")
|
|
185
|
-
|
|
186
|
-
# Escape the xpath for JavaScript
|
|
187
463
|
escaped_xpath = xpath.replace("`", "\\`").replace("\\", "\\\\")
|
|
188
|
-
|
|
189
|
-
# Wrap with IIFE and inject the xpath parameter
|
|
190
464
|
wrapper = f"""
|
|
191
465
|
(() => {{
|
|
192
466
|
const xpath = `{escaped_xpath}`;
|
|
@@ -194,37 +468,60 @@ class JavaScriptLoader:
|
|
|
194
468
|
return triggerInputEvents(xpath, value);
|
|
195
469
|
}})();
|
|
196
470
|
"""
|
|
471
|
+
return js_code + "\n" + wrapper
|
|
472
|
+
|
|
473
|
+
def get_draw_element_boxes_js(self, uuid_xpath_dict: Dict[str, str]) -> str:
|
|
474
|
+
import json
|
|
475
|
+
|
|
476
|
+
js_code = self.load_js_file("draw_element_boxes.js")
|
|
477
|
+
json_str = json.dumps(uuid_xpath_dict)
|
|
478
|
+
wrapper = f"""
|
|
479
|
+
(() => {{
|
|
480
|
+
const uuidXpathMap = {json_str};
|
|
481
|
+
return drawElementBoxes(uuidXpathMap);
|
|
482
|
+
}})();
|
|
483
|
+
"""
|
|
484
|
+
return js_code + "\n" + wrapper
|
|
197
485
|
|
|
486
|
+
def get_remove_element_boxes_js(self) -> str:
|
|
487
|
+
js_code = self.load_js_file("remove_element_boxes.js")
|
|
488
|
+
wrapper = """
|
|
489
|
+
(() => {
|
|
490
|
+
return removeElementBoxes();
|
|
491
|
+
})();
|
|
492
|
+
"""
|
|
493
|
+
return js_code + "\n" + wrapper
|
|
494
|
+
|
|
495
|
+
def get_filter_hidden_elements_js(self) -> str:
|
|
496
|
+
js_code = self.load_js_file("filter_hidden_elements.js")
|
|
497
|
+
wrapper = """
|
|
498
|
+
(() => {
|
|
499
|
+
return filterHiddenElements();
|
|
500
|
+
})();
|
|
501
|
+
"""
|
|
198
502
|
return js_code + "\n" + wrapper
|
|
199
503
|
|
|
200
504
|
def clear_cache(self):
|
|
201
|
-
"""Clear the JavaScript file cache."""
|
|
202
505
|
self._js_cache.clear()
|
|
203
506
|
|
|
204
507
|
|
|
205
|
-
# Global instance for convenience
|
|
206
508
|
js_loader = JavaScriptLoader()
|
|
207
509
|
|
|
208
|
-
# Key code mapping for common keys
|
|
209
510
|
key_codes = {
|
|
210
|
-
# Arrow Keys
|
|
211
511
|
"up": 38,
|
|
212
512
|
"down": 40,
|
|
213
513
|
"left": 37,
|
|
214
514
|
"right": 39,
|
|
215
|
-
# Navigation Keys
|
|
216
515
|
"home": 36,
|
|
217
516
|
"end": 35,
|
|
218
517
|
"pageup": 33,
|
|
219
518
|
"pagedown": 34,
|
|
220
|
-
# Control Keys
|
|
221
519
|
"enter": 13,
|
|
222
520
|
"escape": 27,
|
|
223
521
|
"tab": 9,
|
|
224
522
|
"backspace": 8,
|
|
225
523
|
"delete": 46,
|
|
226
524
|
"space": 32,
|
|
227
|
-
# Function Keys
|
|
228
525
|
"f1": 112,
|
|
229
526
|
"f2": 113,
|
|
230
527
|
"f3": 114,
|
|
@@ -237,7 +534,6 @@ key_codes = {
|
|
|
237
534
|
"f10": 121,
|
|
238
535
|
"f11": 122,
|
|
239
536
|
"f12": 123,
|
|
240
|
-
# Numpad
|
|
241
537
|
"numpad0": 96,
|
|
242
538
|
"numpad1": 97,
|
|
243
539
|
"numpad2": 98,
|
|
@@ -248,18 +544,15 @@ key_codes = {
|
|
|
248
544
|
"numpad7": 103,
|
|
249
545
|
"numpad8": 104,
|
|
250
546
|
"numpad9": 105,
|
|
251
|
-
# Media Keys
|
|
252
547
|
"volumeup": 175,
|
|
253
548
|
"volume_up": 175,
|
|
254
549
|
"volumedown": 174,
|
|
255
550
|
"volume_down": 174,
|
|
256
551
|
"volumemute": 173,
|
|
257
552
|
"volume_mute": 173,
|
|
258
|
-
# Lock Keys
|
|
259
553
|
"capslock": 20,
|
|
260
554
|
"numlock": 144,
|
|
261
555
|
"scrolllock": 145,
|
|
262
|
-
# Modifier Keys (for key events, not just modifiers)
|
|
263
556
|
"shift": 16,
|
|
264
557
|
"ctrl": 17,
|
|
265
558
|
"control": 17,
|