code-puppy 0.0.214__py3-none-any.whl โ 0.0.366__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.
- code_puppy/__init__.py +7 -1
- code_puppy/agents/__init__.py +2 -0
- code_puppy/agents/agent_c_reviewer.py +59 -6
- code_puppy/agents/agent_code_puppy.py +7 -1
- code_puppy/agents/agent_code_reviewer.py +12 -2
- code_puppy/agents/agent_cpp_reviewer.py +73 -6
- code_puppy/agents/agent_creator_agent.py +45 -4
- code_puppy/agents/agent_golang_reviewer.py +92 -3
- code_puppy/agents/agent_javascript_reviewer.py +101 -8
- code_puppy/agents/agent_manager.py +81 -4
- code_puppy/agents/agent_pack_leader.py +383 -0
- code_puppy/agents/agent_planning.py +163 -0
- code_puppy/agents/agent_python_programmer.py +165 -0
- code_puppy/agents/agent_python_reviewer.py +28 -6
- code_puppy/agents/agent_qa_expert.py +98 -6
- code_puppy/agents/agent_qa_kitten.py +12 -7
- code_puppy/agents/agent_security_auditor.py +113 -3
- code_puppy/agents/agent_terminal_qa.py +323 -0
- code_puppy/agents/agent_typescript_reviewer.py +106 -7
- code_puppy/agents/base_agent.py +802 -176
- code_puppy/agents/event_stream_handler.py +350 -0
- code_puppy/agents/pack/__init__.py +34 -0
- code_puppy/agents/pack/bloodhound.py +304 -0
- code_puppy/agents/pack/husky.py +321 -0
- code_puppy/agents/pack/retriever.py +393 -0
- code_puppy/agents/pack/shepherd.py +348 -0
- code_puppy/agents/pack/terrier.py +287 -0
- code_puppy/agents/pack/watchdog.py +367 -0
- code_puppy/agents/prompt_reviewer.py +145 -0
- code_puppy/agents/subagent_stream_handler.py +276 -0
- code_puppy/api/__init__.py +13 -0
- code_puppy/api/app.py +169 -0
- code_puppy/api/main.py +21 -0
- code_puppy/api/pty_manager.py +446 -0
- code_puppy/api/routers/__init__.py +12 -0
- code_puppy/api/routers/agents.py +36 -0
- code_puppy/api/routers/commands.py +217 -0
- code_puppy/api/routers/config.py +74 -0
- code_puppy/api/routers/sessions.py +232 -0
- code_puppy/api/templates/terminal.html +361 -0
- code_puppy/api/websocket.py +154 -0
- code_puppy/callbacks.py +142 -4
- code_puppy/chatgpt_codex_client.py +283 -0
- code_puppy/claude_cache_client.py +586 -0
- code_puppy/cli_runner.py +916 -0
- code_puppy/command_line/add_model_menu.py +1079 -0
- code_puppy/command_line/agent_menu.py +395 -0
- code_puppy/command_line/attachments.py +10 -5
- code_puppy/command_line/autosave_menu.py +605 -0
- code_puppy/command_line/clipboard.py +527 -0
- code_puppy/command_line/colors_menu.py +520 -0
- code_puppy/command_line/command_handler.py +176 -738
- code_puppy/command_line/command_registry.py +150 -0
- code_puppy/command_line/config_commands.py +715 -0
- code_puppy/command_line/core_commands.py +792 -0
- code_puppy/command_line/diff_menu.py +863 -0
- code_puppy/command_line/load_context_completion.py +15 -22
- code_puppy/command_line/mcp/base.py +0 -3
- code_puppy/command_line/mcp/catalog_server_installer.py +175 -0
- code_puppy/command_line/mcp/custom_server_form.py +688 -0
- code_puppy/command_line/mcp/custom_server_installer.py +195 -0
- code_puppy/command_line/mcp/edit_command.py +148 -0
- code_puppy/command_line/mcp/handler.py +9 -4
- code_puppy/command_line/mcp/help_command.py +6 -5
- code_puppy/command_line/mcp/install_command.py +15 -26
- code_puppy/command_line/mcp/install_menu.py +685 -0
- code_puppy/command_line/mcp/list_command.py +2 -2
- code_puppy/command_line/mcp/logs_command.py +174 -65
- code_puppy/command_line/mcp/remove_command.py +2 -2
- code_puppy/command_line/mcp/restart_command.py +12 -4
- code_puppy/command_line/mcp/search_command.py +16 -10
- code_puppy/command_line/mcp/start_all_command.py +18 -6
- code_puppy/command_line/mcp/start_command.py +47 -25
- code_puppy/command_line/mcp/status_command.py +4 -5
- code_puppy/command_line/mcp/stop_all_command.py +7 -1
- code_puppy/command_line/mcp/stop_command.py +8 -4
- code_puppy/command_line/mcp/test_command.py +2 -2
- code_puppy/command_line/mcp/wizard_utils.py +20 -16
- code_puppy/command_line/mcp_completion.py +174 -0
- code_puppy/command_line/model_picker_completion.py +75 -25
- code_puppy/command_line/model_settings_menu.py +884 -0
- code_puppy/command_line/motd.py +14 -8
- code_puppy/command_line/onboarding_slides.py +179 -0
- code_puppy/command_line/onboarding_wizard.py +340 -0
- code_puppy/command_line/pin_command_completion.py +329 -0
- code_puppy/command_line/prompt_toolkit_completion.py +463 -63
- code_puppy/command_line/session_commands.py +296 -0
- code_puppy/command_line/utils.py +54 -0
- code_puppy/config.py +898 -112
- code_puppy/error_logging.py +118 -0
- code_puppy/gemini_code_assist.py +385 -0
- code_puppy/gemini_model.py +602 -0
- code_puppy/http_utils.py +210 -148
- code_puppy/keymap.py +128 -0
- code_puppy/main.py +5 -698
- code_puppy/mcp_/__init__.py +17 -0
- code_puppy/mcp_/async_lifecycle.py +35 -4
- code_puppy/mcp_/blocking_startup.py +70 -43
- code_puppy/mcp_/captured_stdio_server.py +2 -2
- code_puppy/mcp_/config_wizard.py +4 -4
- code_puppy/mcp_/dashboard.py +15 -6
- code_puppy/mcp_/managed_server.py +65 -38
- code_puppy/mcp_/manager.py +146 -52
- code_puppy/mcp_/mcp_logs.py +224 -0
- code_puppy/mcp_/registry.py +6 -6
- code_puppy/mcp_/server_registry_catalog.py +24 -5
- code_puppy/messaging/__init__.py +199 -2
- code_puppy/messaging/bus.py +610 -0
- code_puppy/messaging/commands.py +167 -0
- code_puppy/messaging/markdown_patches.py +57 -0
- code_puppy/messaging/message_queue.py +17 -48
- code_puppy/messaging/messages.py +500 -0
- code_puppy/messaging/queue_console.py +1 -24
- code_puppy/messaging/renderers.py +43 -146
- code_puppy/messaging/rich_renderer.py +1027 -0
- code_puppy/messaging/spinner/__init__.py +21 -5
- code_puppy/messaging/spinner/console_spinner.py +86 -51
- code_puppy/messaging/subagent_console.py +461 -0
- code_puppy/model_factory.py +634 -83
- code_puppy/model_utils.py +167 -0
- code_puppy/models.json +66 -68
- code_puppy/models_dev_api.json +1 -0
- code_puppy/models_dev_parser.py +592 -0
- code_puppy/plugins/__init__.py +164 -10
- code_puppy/plugins/antigravity_oauth/__init__.py +10 -0
- code_puppy/plugins/antigravity_oauth/accounts.py +406 -0
- code_puppy/plugins/antigravity_oauth/antigravity_model.py +704 -0
- code_puppy/plugins/antigravity_oauth/config.py +42 -0
- code_puppy/plugins/antigravity_oauth/constants.py +136 -0
- code_puppy/plugins/antigravity_oauth/oauth.py +478 -0
- code_puppy/plugins/antigravity_oauth/register_callbacks.py +406 -0
- code_puppy/plugins/antigravity_oauth/storage.py +271 -0
- code_puppy/plugins/antigravity_oauth/test_plugin.py +319 -0
- code_puppy/plugins/antigravity_oauth/token.py +167 -0
- code_puppy/plugins/antigravity_oauth/transport.py +767 -0
- code_puppy/plugins/antigravity_oauth/utils.py +169 -0
- code_puppy/plugins/chatgpt_oauth/__init__.py +8 -0
- code_puppy/plugins/chatgpt_oauth/config.py +52 -0
- code_puppy/plugins/chatgpt_oauth/oauth_flow.py +328 -0
- code_puppy/plugins/chatgpt_oauth/register_callbacks.py +94 -0
- code_puppy/plugins/chatgpt_oauth/test_plugin.py +293 -0
- code_puppy/plugins/chatgpt_oauth/utils.py +489 -0
- code_puppy/plugins/claude_code_oauth/README.md +167 -0
- code_puppy/plugins/claude_code_oauth/SETUP.md +93 -0
- code_puppy/plugins/claude_code_oauth/__init__.py +6 -0
- code_puppy/plugins/claude_code_oauth/config.py +50 -0
- code_puppy/plugins/claude_code_oauth/register_callbacks.py +308 -0
- code_puppy/plugins/claude_code_oauth/test_plugin.py +283 -0
- code_puppy/plugins/claude_code_oauth/utils.py +518 -0
- code_puppy/plugins/customizable_commands/__init__.py +0 -0
- code_puppy/plugins/customizable_commands/register_callbacks.py +169 -0
- code_puppy/plugins/example_custom_command/README.md +280 -0
- code_puppy/plugins/example_custom_command/register_callbacks.py +2 -2
- code_puppy/plugins/file_permission_handler/__init__.py +4 -0
- code_puppy/plugins/file_permission_handler/register_callbacks.py +523 -0
- code_puppy/plugins/frontend_emitter/__init__.py +25 -0
- code_puppy/plugins/frontend_emitter/emitter.py +121 -0
- code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
- code_puppy/plugins/oauth_puppy_html.py +228 -0
- code_puppy/plugins/shell_safety/__init__.py +6 -0
- code_puppy/plugins/shell_safety/agent_shell_safety.py +69 -0
- code_puppy/plugins/shell_safety/command_cache.py +156 -0
- code_puppy/plugins/shell_safety/register_callbacks.py +202 -0
- code_puppy/prompts/antigravity_system_prompt.md +1 -0
- code_puppy/prompts/codex_system_prompt.md +310 -0
- code_puppy/pydantic_patches.py +131 -0
- code_puppy/reopenable_async_client.py +8 -8
- code_puppy/round_robin_model.py +9 -12
- code_puppy/session_storage.py +2 -1
- code_puppy/status_display.py +21 -4
- code_puppy/summarization_agent.py +41 -13
- code_puppy/terminal_utils.py +418 -0
- code_puppy/tools/__init__.py +37 -1
- code_puppy/tools/agent_tools.py +536 -52
- code_puppy/tools/browser/__init__.py +37 -0
- code_puppy/tools/browser/browser_control.py +19 -23
- code_puppy/tools/browser/browser_interactions.py +41 -48
- code_puppy/tools/browser/browser_locators.py +36 -38
- code_puppy/tools/browser/browser_manager.py +316 -0
- code_puppy/tools/browser/browser_navigation.py +16 -16
- code_puppy/tools/browser/browser_screenshot.py +79 -143
- code_puppy/tools/browser/browser_scripts.py +32 -42
- code_puppy/tools/browser/browser_workflows.py +44 -27
- code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
- code_puppy/tools/browser/terminal_command_tools.py +521 -0
- code_puppy/tools/browser/terminal_screenshot_tools.py +556 -0
- code_puppy/tools/browser/terminal_tools.py +525 -0
- code_puppy/tools/command_runner.py +930 -147
- code_puppy/tools/common.py +1113 -5
- code_puppy/tools/display.py +84 -0
- code_puppy/tools/file_modifications.py +288 -89
- code_puppy/tools/file_operations.py +226 -154
- code_puppy/tools/subagent_context.py +158 -0
- code_puppy/uvx_detection.py +242 -0
- code_puppy/version_checker.py +30 -11
- code_puppy-0.0.366.data/data/code_puppy/models.json +110 -0
- code_puppy-0.0.366.data/data/code_puppy/models_dev_api.json +1 -0
- {code_puppy-0.0.214.dist-info โ code_puppy-0.0.366.dist-info}/METADATA +149 -75
- code_puppy-0.0.366.dist-info/RECORD +217 -0
- {code_puppy-0.0.214.dist-info โ code_puppy-0.0.366.dist-info}/WHEEL +1 -1
- code_puppy/command_line/mcp/add_command.py +0 -183
- code_puppy/messaging/spinner/textual_spinner.py +0 -106
- code_puppy/tools/browser/camoufox_manager.py +0 -216
- code_puppy/tools/browser/vqa_agent.py +0 -70
- code_puppy/tui/__init__.py +0 -10
- code_puppy/tui/app.py +0 -1105
- code_puppy/tui/components/__init__.py +0 -21
- code_puppy/tui/components/chat_view.py +0 -551
- code_puppy/tui/components/command_history_modal.py +0 -218
- code_puppy/tui/components/copy_button.py +0 -139
- code_puppy/tui/components/custom_widgets.py +0 -63
- code_puppy/tui/components/human_input_modal.py +0 -175
- code_puppy/tui/components/input_area.py +0 -167
- code_puppy/tui/components/sidebar.py +0 -309
- code_puppy/tui/components/status_bar.py +0 -185
- code_puppy/tui/messages.py +0 -27
- code_puppy/tui/models/__init__.py +0 -8
- code_puppy/tui/models/chat_message.py +0 -25
- code_puppy/tui/models/command_history.py +0 -89
- code_puppy/tui/models/enums.py +0 -24
- code_puppy/tui/screens/__init__.py +0 -17
- code_puppy/tui/screens/autosave_picker.py +0 -175
- code_puppy/tui/screens/help.py +0 -130
- code_puppy/tui/screens/mcp_install_wizard.py +0 -803
- code_puppy/tui/screens/settings.py +0 -306
- code_puppy/tui/screens/tools.py +0 -74
- code_puppy/tui_state.py +0 -55
- code_puppy-0.0.214.data/data/code_puppy/models.json +0 -112
- code_puppy-0.0.214.dist-info/RECORD +0 -131
- {code_puppy-0.0.214.dist-info โ code_puppy-0.0.366.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.214.dist-info โ code_puppy-0.0.366.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Browser tools for terminal automation.
|
|
2
|
+
|
|
3
|
+
This module provides browser-based terminal automation tools.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from code_puppy.config import get_banner_color
|
|
7
|
+
|
|
8
|
+
from .browser_manager import (
|
|
9
|
+
cleanup_all_browsers,
|
|
10
|
+
get_browser_session,
|
|
11
|
+
get_session_browser_manager,
|
|
12
|
+
set_browser_session,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def format_terminal_banner(text: str) -> str:
|
|
17
|
+
"""Format a terminal tool banner with the configured terminal_tool color.
|
|
18
|
+
|
|
19
|
+
Returns Rich markup string that can be used with Text.from_markup().
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
text: The banner text (e.g., "TERMINAL OPEN ๐ฅ๏ธ localhost:8765")
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Rich markup formatted string
|
|
26
|
+
"""
|
|
27
|
+
color = get_banner_color("terminal_tool")
|
|
28
|
+
return f"[bold white on {color}] {text} [/bold white on {color}]"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"format_terminal_banner",
|
|
33
|
+
"cleanup_all_browsers",
|
|
34
|
+
"get_browser_session",
|
|
35
|
+
"get_session_browser_manager",
|
|
36
|
+
"set_browser_session",
|
|
37
|
+
]
|
|
@@ -4,10 +4,10 @@ from typing import Any, Dict, Optional
|
|
|
4
4
|
|
|
5
5
|
from pydantic_ai import RunContext
|
|
6
6
|
|
|
7
|
-
from code_puppy.messaging import emit_info
|
|
7
|
+
from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
|
|
8
8
|
from code_puppy.tools.common import generate_group_id
|
|
9
9
|
|
|
10
|
-
from .
|
|
10
|
+
from .browser_manager import get_session_browser_manager
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
async def initialize_browser(
|
|
@@ -18,11 +18,11 @@ async def initialize_browser(
|
|
|
18
18
|
"""Initialize the browser with specified settings."""
|
|
19
19
|
group_id = generate_group_id("browser_initialize", f"{browser_type}_{homepage}")
|
|
20
20
|
emit_info(
|
|
21
|
-
f"
|
|
21
|
+
f"BROWSER INITIALIZE ๐ {browser_type} โ {homepage}",
|
|
22
22
|
message_group=group_id,
|
|
23
23
|
)
|
|
24
24
|
try:
|
|
25
|
-
browser_manager =
|
|
25
|
+
browser_manager = get_session_browser_manager()
|
|
26
26
|
|
|
27
27
|
# Configure browser settings
|
|
28
28
|
browser_manager.headless = headless
|
|
@@ -41,9 +41,9 @@ async def initialize_browser(
|
|
|
41
41
|
url = "Unknown"
|
|
42
42
|
title = "Unknown"
|
|
43
43
|
|
|
44
|
-
emit_info(
|
|
45
|
-
|
|
46
|
-
)
|
|
44
|
+
# emit_info(
|
|
45
|
+
# "[green]Browser initialized successfully[/green]", message_group=group_id
|
|
46
|
+
# ) # Removed to reduce console spam
|
|
47
47
|
|
|
48
48
|
return {
|
|
49
49
|
"success": True,
|
|
@@ -55,8 +55,8 @@ async def initialize_browser(
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
except Exception as e:
|
|
58
|
-
|
|
59
|
-
f"
|
|
58
|
+
emit_error(
|
|
59
|
+
f"Browser initialization failed: {str(e)}",
|
|
60
60
|
message_group=group_id,
|
|
61
61
|
)
|
|
62
62
|
return {
|
|
@@ -71,16 +71,14 @@ async def close_browser() -> Dict[str, Any]:
|
|
|
71
71
|
"""Close the browser and clean up resources."""
|
|
72
72
|
group_id = generate_group_id("browser_close")
|
|
73
73
|
emit_info(
|
|
74
|
-
"
|
|
74
|
+
"BROWSER CLOSE ๐",
|
|
75
75
|
message_group=group_id,
|
|
76
76
|
)
|
|
77
77
|
try:
|
|
78
|
-
browser_manager =
|
|
78
|
+
browser_manager = get_session_browser_manager()
|
|
79
79
|
await browser_manager.close()
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
"[yellow]Browser closed successfully[/yellow]", message_group=group_id
|
|
83
|
-
)
|
|
81
|
+
emit_warning("Browser closed successfully", message_group=group_id)
|
|
84
82
|
|
|
85
83
|
return {"success": True, "message": "Browser closed"}
|
|
86
84
|
|
|
@@ -92,11 +90,11 @@ async def get_browser_status() -> Dict[str, Any]:
|
|
|
92
90
|
"""Get current browser status and information."""
|
|
93
91
|
group_id = generate_group_id("browser_status")
|
|
94
92
|
emit_info(
|
|
95
|
-
"
|
|
93
|
+
"BROWSER STATUS ๐",
|
|
96
94
|
message_group=group_id,
|
|
97
95
|
)
|
|
98
96
|
try:
|
|
99
|
-
browser_manager =
|
|
97
|
+
browser_manager = get_session_browser_manager()
|
|
100
98
|
|
|
101
99
|
if not browser_manager._initialized:
|
|
102
100
|
return {
|
|
@@ -137,11 +135,11 @@ async def create_new_page(url: Optional[str] = None) -> Dict[str, Any]:
|
|
|
137
135
|
"""Create a new browser page/tab."""
|
|
138
136
|
group_id = generate_group_id("browser_new_page", url or "blank")
|
|
139
137
|
emit_info(
|
|
140
|
-
f"
|
|
138
|
+
f"BROWSER NEW PAGE ๐ {url or 'blank page'}",
|
|
141
139
|
message_group=group_id,
|
|
142
140
|
)
|
|
143
141
|
try:
|
|
144
|
-
browser_manager =
|
|
142
|
+
browser_manager = get_session_browser_manager()
|
|
145
143
|
|
|
146
144
|
if not browser_manager._initialized:
|
|
147
145
|
return {
|
|
@@ -154,9 +152,7 @@ async def create_new_page(url: Optional[str] = None) -> Dict[str, Any]:
|
|
|
154
152
|
final_url = page.url
|
|
155
153
|
title = await page.title()
|
|
156
154
|
|
|
157
|
-
|
|
158
|
-
f"[green]Created new page: {final_url}[/green]", message_group=group_id
|
|
159
|
-
)
|
|
155
|
+
emit_success(f"Created new page: {final_url}", message_group=group_id)
|
|
160
156
|
|
|
161
157
|
return {"success": True, "url": final_url, "title": title, "requested_url": url}
|
|
162
158
|
|
|
@@ -168,11 +164,11 @@ async def list_pages() -> Dict[str, Any]:
|
|
|
168
164
|
"""List all open browser pages/tabs."""
|
|
169
165
|
group_id = generate_group_id("browser_list_pages")
|
|
170
166
|
emit_info(
|
|
171
|
-
"
|
|
167
|
+
"BROWSER LIST PAGES ๐",
|
|
172
168
|
message_group=group_id,
|
|
173
169
|
)
|
|
174
170
|
try:
|
|
175
|
-
browser_manager =
|
|
171
|
+
browser_manager = get_session_browser_manager()
|
|
176
172
|
|
|
177
173
|
if not browser_manager._initialized:
|
|
178
174
|
return {"success": False, "error": "Browser not initialized"}
|
|
@@ -4,10 +4,10 @@ from typing import Any, Dict, List, Optional
|
|
|
4
4
|
|
|
5
5
|
from pydantic_ai import RunContext
|
|
6
6
|
|
|
7
|
-
from code_puppy.messaging import emit_info
|
|
7
|
+
from code_puppy.messaging import emit_error, emit_info, emit_success
|
|
8
8
|
from code_puppy.tools.common import generate_group_id
|
|
9
9
|
|
|
10
|
-
from .
|
|
10
|
+
from .browser_manager import get_session_browser_manager
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
async def click_element(
|
|
@@ -20,18 +20,19 @@ async def click_element(
|
|
|
20
20
|
"""Click on an element."""
|
|
21
21
|
group_id = generate_group_id("browser_click", selector[:100])
|
|
22
22
|
emit_info(
|
|
23
|
-
f"
|
|
23
|
+
f"BROWSER CLICK ๐ฑ๏ธ selector='{selector}' button={button}",
|
|
24
24
|
message_group=group_id,
|
|
25
25
|
)
|
|
26
26
|
try:
|
|
27
|
-
browser_manager =
|
|
27
|
+
browser_manager = get_session_browser_manager()
|
|
28
28
|
page = await browser_manager.get_current_page()
|
|
29
29
|
|
|
30
30
|
if not page:
|
|
31
31
|
return {"success": False, "error": "No active browser page available"}
|
|
32
32
|
|
|
33
|
-
# Find element
|
|
34
|
-
|
|
33
|
+
# Find element - use .first to handle cases where selector matches multiple elements
|
|
34
|
+
# This avoids Playwright's strict mode violation errors
|
|
35
|
+
element = page.locator(selector).first
|
|
35
36
|
|
|
36
37
|
# Wait for element to be visible and enabled
|
|
37
38
|
await element.wait_for(state="visible", timeout=timeout)
|
|
@@ -48,12 +49,12 @@ async def click_element(
|
|
|
48
49
|
|
|
49
50
|
await element.click(**click_options)
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
emit_success(f"Clicked element: {selector}", message_group=group_id)
|
|
52
53
|
|
|
53
54
|
return {"success": True, "selector": selector, "action": f"{button}_click"}
|
|
54
55
|
|
|
55
56
|
except Exception as e:
|
|
56
|
-
|
|
57
|
+
emit_error(f"Click failed: {str(e)}", message_group=group_id)
|
|
57
58
|
return {"success": False, "error": str(e), "selector": selector}
|
|
58
59
|
|
|
59
60
|
|
|
@@ -65,23 +66,21 @@ async def double_click_element(
|
|
|
65
66
|
"""Double-click on an element."""
|
|
66
67
|
group_id = generate_group_id("browser_double_click", selector[:100])
|
|
67
68
|
emit_info(
|
|
68
|
-
f"
|
|
69
|
+
f"BROWSER DOUBLE CLICK ๐ฑ๏ธ๐ฑ๏ธ selector='{selector}'",
|
|
69
70
|
message_group=group_id,
|
|
70
71
|
)
|
|
71
72
|
try:
|
|
72
|
-
browser_manager =
|
|
73
|
+
browser_manager = get_session_browser_manager()
|
|
73
74
|
page = await browser_manager.get_current_page()
|
|
74
75
|
|
|
75
76
|
if not page:
|
|
76
77
|
return {"success": False, "error": "No active browser page available"}
|
|
77
78
|
|
|
78
|
-
element = page.locator(selector)
|
|
79
|
+
element = page.locator(selector).first
|
|
79
80
|
await element.wait_for(state="visible", timeout=timeout)
|
|
80
81
|
await element.dblclick(force=force, timeout=timeout)
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
f"[green]Double-clicked element: {selector}[/green]", message_group=group_id
|
|
84
|
-
)
|
|
83
|
+
emit_success(f"Double-clicked element: {selector}", message_group=group_id)
|
|
85
84
|
|
|
86
85
|
return {"success": True, "selector": selector, "action": "double_click"}
|
|
87
86
|
|
|
@@ -97,23 +96,21 @@ async def hover_element(
|
|
|
97
96
|
"""Hover over an element."""
|
|
98
97
|
group_id = generate_group_id("browser_hover", selector[:100])
|
|
99
98
|
emit_info(
|
|
100
|
-
f"
|
|
99
|
+
f"BROWSER HOVER ๐ selector='{selector}'",
|
|
101
100
|
message_group=group_id,
|
|
102
101
|
)
|
|
103
102
|
try:
|
|
104
|
-
browser_manager =
|
|
103
|
+
browser_manager = get_session_browser_manager()
|
|
105
104
|
page = await browser_manager.get_current_page()
|
|
106
105
|
|
|
107
106
|
if not page:
|
|
108
107
|
return {"success": False, "error": "No active browser page available"}
|
|
109
108
|
|
|
110
|
-
element = page.locator(selector)
|
|
109
|
+
element = page.locator(selector).first
|
|
111
110
|
await element.wait_for(state="visible", timeout=timeout)
|
|
112
111
|
await element.hover(force=force, timeout=timeout)
|
|
113
112
|
|
|
114
|
-
|
|
115
|
-
f"[green]Hovered over element: {selector}[/green]", message_group=group_id
|
|
116
|
-
)
|
|
113
|
+
emit_success(f"Hovered over element: {selector}", message_group=group_id)
|
|
117
114
|
|
|
118
115
|
return {"success": True, "selector": selector, "action": "hover"}
|
|
119
116
|
|
|
@@ -130,17 +127,17 @@ async def set_element_text(
|
|
|
130
127
|
"""Set text in an input element."""
|
|
131
128
|
group_id = generate_group_id("browser_set_text", f"{selector[:50]}_{text[:30]}")
|
|
132
129
|
emit_info(
|
|
133
|
-
f"
|
|
130
|
+
f"BROWSER SET TEXT โ๏ธ selector='{selector}' text='{text[:50]}{'...' if len(text) > 50 else ''}'",
|
|
134
131
|
message_group=group_id,
|
|
135
132
|
)
|
|
136
133
|
try:
|
|
137
|
-
browser_manager =
|
|
134
|
+
browser_manager = get_session_browser_manager()
|
|
138
135
|
page = await browser_manager.get_current_page()
|
|
139
136
|
|
|
140
137
|
if not page:
|
|
141
138
|
return {"success": False, "error": "No active browser page available"}
|
|
142
139
|
|
|
143
|
-
element = page.locator(selector)
|
|
140
|
+
element = page.locator(selector).first
|
|
144
141
|
await element.wait_for(state="visible", timeout=timeout)
|
|
145
142
|
|
|
146
143
|
if clear_first:
|
|
@@ -148,9 +145,7 @@ async def set_element_text(
|
|
|
148
145
|
|
|
149
146
|
await element.fill(text, timeout=timeout)
|
|
150
147
|
|
|
151
|
-
|
|
152
|
-
f"[green]Set text in element: {selector}[/green]", message_group=group_id
|
|
153
|
-
)
|
|
148
|
+
emit_success(f"Set text in element: {selector}", message_group=group_id)
|
|
154
149
|
|
|
155
150
|
return {
|
|
156
151
|
"success": True,
|
|
@@ -160,7 +155,7 @@ async def set_element_text(
|
|
|
160
155
|
}
|
|
161
156
|
|
|
162
157
|
except Exception as e:
|
|
163
|
-
|
|
158
|
+
emit_error(f"Set text failed: {str(e)}", message_group=group_id)
|
|
164
159
|
return {"success": False, "error": str(e), "selector": selector, "text": text}
|
|
165
160
|
|
|
166
161
|
|
|
@@ -171,17 +166,17 @@ async def get_element_text(
|
|
|
171
166
|
"""Get text content from an element."""
|
|
172
167
|
group_id = generate_group_id("browser_get_text", selector[:100])
|
|
173
168
|
emit_info(
|
|
174
|
-
f"
|
|
169
|
+
f"BROWSER GET TEXT ๐ selector='{selector}'",
|
|
175
170
|
message_group=group_id,
|
|
176
171
|
)
|
|
177
172
|
try:
|
|
178
|
-
browser_manager =
|
|
173
|
+
browser_manager = get_session_browser_manager()
|
|
179
174
|
page = await browser_manager.get_current_page()
|
|
180
175
|
|
|
181
176
|
if not page:
|
|
182
177
|
return {"success": False, "error": "No active browser page available"}
|
|
183
178
|
|
|
184
|
-
element = page.locator(selector)
|
|
179
|
+
element = page.locator(selector).first
|
|
185
180
|
await element.wait_for(state="visible", timeout=timeout)
|
|
186
181
|
|
|
187
182
|
text = await element.text_content()
|
|
@@ -199,17 +194,17 @@ async def get_element_value(
|
|
|
199
194
|
"""Get value from an input element."""
|
|
200
195
|
group_id = generate_group_id("browser_get_value", selector[:100])
|
|
201
196
|
emit_info(
|
|
202
|
-
f"
|
|
197
|
+
f"BROWSER GET VALUE ๐ selector='{selector}'",
|
|
203
198
|
message_group=group_id,
|
|
204
199
|
)
|
|
205
200
|
try:
|
|
206
|
-
browser_manager =
|
|
201
|
+
browser_manager = get_session_browser_manager()
|
|
207
202
|
page = await browser_manager.get_current_page()
|
|
208
203
|
|
|
209
204
|
if not page:
|
|
210
205
|
return {"success": False, "error": "No active browser page available"}
|
|
211
206
|
|
|
212
|
-
element = page.locator(selector)
|
|
207
|
+
element = page.locator(selector).first
|
|
213
208
|
await element.wait_for(state="visible", timeout=timeout)
|
|
214
209
|
|
|
215
210
|
value = await element.input_value()
|
|
@@ -233,17 +228,17 @@ async def select_option(
|
|
|
233
228
|
"browser_select_option", f"{selector[:50]}_{option_desc}"
|
|
234
229
|
)
|
|
235
230
|
emit_info(
|
|
236
|
-
f"
|
|
231
|
+
f"BROWSER SELECT OPTION ๐ selector='{selector}' option='{option_desc}'",
|
|
237
232
|
message_group=group_id,
|
|
238
233
|
)
|
|
239
234
|
try:
|
|
240
|
-
browser_manager =
|
|
235
|
+
browser_manager = get_session_browser_manager()
|
|
241
236
|
page = await browser_manager.get_current_page()
|
|
242
237
|
|
|
243
238
|
if not page:
|
|
244
239
|
return {"success": False, "error": "No active browser page available"}
|
|
245
240
|
|
|
246
|
-
element = page.locator(selector)
|
|
241
|
+
element = page.locator(selector).first
|
|
247
242
|
await element.wait_for(state="visible", timeout=timeout)
|
|
248
243
|
|
|
249
244
|
if value is not None:
|
|
@@ -262,8 +257,8 @@ async def select_option(
|
|
|
262
257
|
"selector": selector,
|
|
263
258
|
}
|
|
264
259
|
|
|
265
|
-
|
|
266
|
-
f"
|
|
260
|
+
emit_success(
|
|
261
|
+
f"Selected option in {selector}: {selection}",
|
|
267
262
|
message_group=group_id,
|
|
268
263
|
)
|
|
269
264
|
|
|
@@ -280,21 +275,21 @@ async def check_element(
|
|
|
280
275
|
"""Check a checkbox or radio button."""
|
|
281
276
|
group_id = generate_group_id("browser_check", selector[:100])
|
|
282
277
|
emit_info(
|
|
283
|
-
f"
|
|
278
|
+
f"BROWSER CHECK โ๏ธ selector='{selector}'",
|
|
284
279
|
message_group=group_id,
|
|
285
280
|
)
|
|
286
281
|
try:
|
|
287
|
-
browser_manager =
|
|
282
|
+
browser_manager = get_session_browser_manager()
|
|
288
283
|
page = await browser_manager.get_current_page()
|
|
289
284
|
|
|
290
285
|
if not page:
|
|
291
286
|
return {"success": False, "error": "No active browser page available"}
|
|
292
287
|
|
|
293
|
-
element = page.locator(selector)
|
|
288
|
+
element = page.locator(selector).first
|
|
294
289
|
await element.wait_for(state="visible", timeout=timeout)
|
|
295
290
|
await element.check(timeout=timeout)
|
|
296
291
|
|
|
297
|
-
|
|
292
|
+
emit_success(f"Checked element: {selector}", message_group=group_id)
|
|
298
293
|
|
|
299
294
|
return {"success": True, "selector": selector, "action": "check"}
|
|
300
295
|
|
|
@@ -309,23 +304,21 @@ async def uncheck_element(
|
|
|
309
304
|
"""Uncheck a checkbox."""
|
|
310
305
|
group_id = generate_group_id("browser_uncheck", selector[:100])
|
|
311
306
|
emit_info(
|
|
312
|
-
f"
|
|
307
|
+
f"BROWSER UNCHECK โ๏ธ selector='{selector}'",
|
|
313
308
|
message_group=group_id,
|
|
314
309
|
)
|
|
315
310
|
try:
|
|
316
|
-
browser_manager =
|
|
311
|
+
browser_manager = get_session_browser_manager()
|
|
317
312
|
page = await browser_manager.get_current_page()
|
|
318
313
|
|
|
319
314
|
if not page:
|
|
320
315
|
return {"success": False, "error": "No active browser page available"}
|
|
321
316
|
|
|
322
|
-
element = page.locator(selector)
|
|
317
|
+
element = page.locator(selector).first
|
|
323
318
|
await element.wait_for(state="visible", timeout=timeout)
|
|
324
319
|
await element.uncheck(timeout=timeout)
|
|
325
320
|
|
|
326
|
-
|
|
327
|
-
f"[green]Unchecked element: {selector}[/green]", message_group=group_id
|
|
328
|
-
)
|
|
321
|
+
emit_success(f"Unchecked element: {selector}", message_group=group_id)
|
|
329
322
|
|
|
330
323
|
return {"success": True, "selector": selector, "action": "uncheck"}
|
|
331
324
|
|
|
@@ -4,10 +4,10 @@ from typing import Any, Dict, Optional
|
|
|
4
4
|
|
|
5
5
|
from pydantic_ai import RunContext
|
|
6
6
|
|
|
7
|
-
from code_puppy.messaging import emit_info
|
|
7
|
+
from code_puppy.messaging import emit_info, emit_success
|
|
8
8
|
from code_puppy.tools.common import generate_group_id
|
|
9
9
|
|
|
10
|
-
from .
|
|
10
|
+
from .browser_manager import get_session_browser_manager
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
async def find_by_role(
|
|
@@ -19,11 +19,11 @@ async def find_by_role(
|
|
|
19
19
|
"""Find elements by ARIA role."""
|
|
20
20
|
group_id = generate_group_id("browser_find_by_role", f"{role}_{name or 'any'}")
|
|
21
21
|
emit_info(
|
|
22
|
-
f"
|
|
22
|
+
f"BROWSER FIND BY ROLE ๐จ role={role} name={name}",
|
|
23
23
|
message_group=group_id,
|
|
24
24
|
)
|
|
25
25
|
try:
|
|
26
|
-
browser_manager =
|
|
26
|
+
browser_manager = get_session_browser_manager()
|
|
27
27
|
page = await browser_manager.get_current_page()
|
|
28
28
|
|
|
29
29
|
if not page:
|
|
@@ -46,8 +46,8 @@ async def find_by_role(
|
|
|
46
46
|
text = await element.text_content()
|
|
47
47
|
elements.append({"index": i, "text": text, "visible": True})
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
f"
|
|
49
|
+
emit_success(
|
|
50
|
+
f"Found {count} elements with role '{role}'",
|
|
51
51
|
message_group=group_id,
|
|
52
52
|
)
|
|
53
53
|
|
|
@@ -71,11 +71,11 @@ async def find_by_text(
|
|
|
71
71
|
"""Find elements containing specific text."""
|
|
72
72
|
group_id = generate_group_id("browser_find_by_text", text[:50])
|
|
73
73
|
emit_info(
|
|
74
|
-
f"
|
|
74
|
+
f"BROWSER FIND BY TEXT ๐ text='{text}' exact={exact}",
|
|
75
75
|
message_group=group_id,
|
|
76
76
|
)
|
|
77
77
|
try:
|
|
78
|
-
browser_manager =
|
|
78
|
+
browser_manager = get_session_browser_manager()
|
|
79
79
|
page = await browser_manager.get_current_page()
|
|
80
80
|
|
|
81
81
|
if not page:
|
|
@@ -98,8 +98,8 @@ async def find_by_text(
|
|
|
98
98
|
{"index": i, "tag": tag_name, "text": full_text, "visible": True}
|
|
99
99
|
)
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
f"
|
|
101
|
+
emit_success(
|
|
102
|
+
f"Found {count} elements containing text '{text}'",
|
|
103
103
|
message_group=group_id,
|
|
104
104
|
)
|
|
105
105
|
|
|
@@ -123,11 +123,11 @@ async def find_by_label(
|
|
|
123
123
|
"""Find form elements by their associated label text."""
|
|
124
124
|
group_id = generate_group_id("browser_find_by_label", text[:50])
|
|
125
125
|
emit_info(
|
|
126
|
-
f"
|
|
126
|
+
f"BROWSER FIND BY LABEL ๐ท๏ธ label='{text}' exact={exact}",
|
|
127
127
|
message_group=group_id,
|
|
128
128
|
)
|
|
129
129
|
try:
|
|
130
|
-
browser_manager =
|
|
130
|
+
browser_manager = get_session_browser_manager()
|
|
131
131
|
page = await browser_manager.get_current_page()
|
|
132
132
|
|
|
133
133
|
if not page:
|
|
@@ -161,8 +161,8 @@ async def find_by_label(
|
|
|
161
161
|
}
|
|
162
162
|
)
|
|
163
163
|
|
|
164
|
-
|
|
165
|
-
f"
|
|
164
|
+
emit_success(
|
|
165
|
+
f"Found {count} elements with label '{text}'",
|
|
166
166
|
message_group=group_id,
|
|
167
167
|
)
|
|
168
168
|
|
|
@@ -186,11 +186,11 @@ async def find_by_placeholder(
|
|
|
186
186
|
"""Find elements by placeholder text."""
|
|
187
187
|
group_id = generate_group_id("browser_find_by_placeholder", text[:50])
|
|
188
188
|
emit_info(
|
|
189
|
-
f"
|
|
189
|
+
f"BROWSER FIND BY PLACEHOLDER ๐ placeholder='{text}' exact={exact}",
|
|
190
190
|
message_group=group_id,
|
|
191
191
|
)
|
|
192
192
|
try:
|
|
193
|
-
browser_manager =
|
|
193
|
+
browser_manager = get_session_browser_manager()
|
|
194
194
|
page = await browser_manager.get_current_page()
|
|
195
195
|
|
|
196
196
|
if not page:
|
|
@@ -220,8 +220,8 @@ async def find_by_placeholder(
|
|
|
220
220
|
}
|
|
221
221
|
)
|
|
222
222
|
|
|
223
|
-
|
|
224
|
-
f"
|
|
223
|
+
emit_success(
|
|
224
|
+
f"Found {count} elements with placeholder '{text}'",
|
|
225
225
|
message_group=group_id,
|
|
226
226
|
)
|
|
227
227
|
|
|
@@ -244,11 +244,11 @@ async def find_by_test_id(
|
|
|
244
244
|
"""Find elements by test ID attribute."""
|
|
245
245
|
group_id = generate_group_id("browser_find_by_test_id", test_id)
|
|
246
246
|
emit_info(
|
|
247
|
-
f"
|
|
247
|
+
f"BROWSER FIND BY TEST ID ๐งช test_id='{test_id}'",
|
|
248
248
|
message_group=group_id,
|
|
249
249
|
)
|
|
250
250
|
try:
|
|
251
|
-
browser_manager =
|
|
251
|
+
browser_manager = get_session_browser_manager()
|
|
252
252
|
page = await browser_manager.get_current_page()
|
|
253
253
|
|
|
254
254
|
if not page:
|
|
@@ -277,8 +277,8 @@ async def find_by_test_id(
|
|
|
277
277
|
}
|
|
278
278
|
)
|
|
279
279
|
|
|
280
|
-
|
|
281
|
-
f"
|
|
280
|
+
emit_success(
|
|
281
|
+
f"Found {count} elements with test-id '{test_id}'",
|
|
282
282
|
message_group=group_id,
|
|
283
283
|
)
|
|
284
284
|
|
|
@@ -300,11 +300,11 @@ async def run_xpath_query(
|
|
|
300
300
|
"""Find elements using XPath selector."""
|
|
301
301
|
group_id = generate_group_id("browser_xpath_query", xpath[:100])
|
|
302
302
|
emit_info(
|
|
303
|
-
f"
|
|
303
|
+
f"BROWSER XPATH QUERY ๐ xpath='{xpath}'",
|
|
304
304
|
message_group=group_id,
|
|
305
305
|
)
|
|
306
306
|
try:
|
|
307
|
-
browser_manager =
|
|
307
|
+
browser_manager = get_session_browser_manager()
|
|
308
308
|
page = await browser_manager.get_current_page()
|
|
309
309
|
|
|
310
310
|
if not page:
|
|
@@ -338,8 +338,8 @@ async def run_xpath_query(
|
|
|
338
338
|
}
|
|
339
339
|
)
|
|
340
340
|
|
|
341
|
-
|
|
342
|
-
f"
|
|
341
|
+
emit_success(
|
|
342
|
+
f"Found {count} elements with XPath '{xpath}'",
|
|
343
343
|
message_group=group_id,
|
|
344
344
|
)
|
|
345
345
|
|
|
@@ -355,11 +355,11 @@ async def find_buttons(
|
|
|
355
355
|
"""Find all button elements on the page."""
|
|
356
356
|
group_id = generate_group_id("browser_find_buttons", text_filter or "all")
|
|
357
357
|
emit_info(
|
|
358
|
-
f"
|
|
358
|
+
f"BROWSER FIND BUTTONS ๐ filter='{text_filter or 'none'}'",
|
|
359
359
|
message_group=group_id,
|
|
360
360
|
)
|
|
361
361
|
try:
|
|
362
|
-
browser_manager =
|
|
362
|
+
browser_manager = get_session_browser_manager()
|
|
363
363
|
page = await browser_manager.get_current_page()
|
|
364
364
|
|
|
365
365
|
if not page:
|
|
@@ -382,10 +382,9 @@ async def find_buttons(
|
|
|
382
382
|
|
|
383
383
|
filtered_count = len(buttons)
|
|
384
384
|
|
|
385
|
-
|
|
386
|
-
f"
|
|
387
|
-
+ (f" containing '{text_filter}'" if text_filter else "")
|
|
388
|
-
+ "[/green]",
|
|
385
|
+
emit_success(
|
|
386
|
+
f"Found {filtered_count} buttons"
|
|
387
|
+
+ (f" containing '{text_filter}'" if text_filter else ""),
|
|
389
388
|
message_group=group_id,
|
|
390
389
|
)
|
|
391
390
|
|
|
@@ -407,11 +406,11 @@ async def find_links(
|
|
|
407
406
|
"""Find all link elements on the page."""
|
|
408
407
|
group_id = generate_group_id("browser_find_links", text_filter or "all")
|
|
409
408
|
emit_info(
|
|
410
|
-
f"
|
|
409
|
+
f"BROWSER FIND LINKS ๐ filter='{text_filter or 'none'}'",
|
|
411
410
|
message_group=group_id,
|
|
412
411
|
)
|
|
413
412
|
try:
|
|
414
|
-
browser_manager =
|
|
413
|
+
browser_manager = get_session_browser_manager()
|
|
415
414
|
page = await browser_manager.get_current_page()
|
|
416
415
|
|
|
417
416
|
if not page:
|
|
@@ -436,10 +435,9 @@ async def find_links(
|
|
|
436
435
|
|
|
437
436
|
filtered_count = len(links)
|
|
438
437
|
|
|
439
|
-
|
|
440
|
-
f"
|
|
441
|
-
+ (f" containing '{text_filter}'" if text_filter else "")
|
|
442
|
-
+ "[/green]",
|
|
438
|
+
emit_success(
|
|
439
|
+
f"Found {filtered_count} links"
|
|
440
|
+
+ (f" containing '{text_filter}'" if text_filter else ""),
|
|
443
441
|
message_group=group_id,
|
|
444
442
|
)
|
|
445
443
|
|