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.
Files changed (231) hide show
  1. code_puppy/__init__.py +7 -1
  2. code_puppy/agents/__init__.py +2 -0
  3. code_puppy/agents/agent_c_reviewer.py +59 -6
  4. code_puppy/agents/agent_code_puppy.py +7 -1
  5. code_puppy/agents/agent_code_reviewer.py +12 -2
  6. code_puppy/agents/agent_cpp_reviewer.py +73 -6
  7. code_puppy/agents/agent_creator_agent.py +45 -4
  8. code_puppy/agents/agent_golang_reviewer.py +92 -3
  9. code_puppy/agents/agent_javascript_reviewer.py +101 -8
  10. code_puppy/agents/agent_manager.py +81 -4
  11. code_puppy/agents/agent_pack_leader.py +383 -0
  12. code_puppy/agents/agent_planning.py +163 -0
  13. code_puppy/agents/agent_python_programmer.py +165 -0
  14. code_puppy/agents/agent_python_reviewer.py +28 -6
  15. code_puppy/agents/agent_qa_expert.py +98 -6
  16. code_puppy/agents/agent_qa_kitten.py +12 -7
  17. code_puppy/agents/agent_security_auditor.py +113 -3
  18. code_puppy/agents/agent_terminal_qa.py +323 -0
  19. code_puppy/agents/agent_typescript_reviewer.py +106 -7
  20. code_puppy/agents/base_agent.py +802 -176
  21. code_puppy/agents/event_stream_handler.py +350 -0
  22. code_puppy/agents/pack/__init__.py +34 -0
  23. code_puppy/agents/pack/bloodhound.py +304 -0
  24. code_puppy/agents/pack/husky.py +321 -0
  25. code_puppy/agents/pack/retriever.py +393 -0
  26. code_puppy/agents/pack/shepherd.py +348 -0
  27. code_puppy/agents/pack/terrier.py +287 -0
  28. code_puppy/agents/pack/watchdog.py +367 -0
  29. code_puppy/agents/prompt_reviewer.py +145 -0
  30. code_puppy/agents/subagent_stream_handler.py +276 -0
  31. code_puppy/api/__init__.py +13 -0
  32. code_puppy/api/app.py +169 -0
  33. code_puppy/api/main.py +21 -0
  34. code_puppy/api/pty_manager.py +446 -0
  35. code_puppy/api/routers/__init__.py +12 -0
  36. code_puppy/api/routers/agents.py +36 -0
  37. code_puppy/api/routers/commands.py +217 -0
  38. code_puppy/api/routers/config.py +74 -0
  39. code_puppy/api/routers/sessions.py +232 -0
  40. code_puppy/api/templates/terminal.html +361 -0
  41. code_puppy/api/websocket.py +154 -0
  42. code_puppy/callbacks.py +142 -4
  43. code_puppy/chatgpt_codex_client.py +283 -0
  44. code_puppy/claude_cache_client.py +586 -0
  45. code_puppy/cli_runner.py +916 -0
  46. code_puppy/command_line/add_model_menu.py +1079 -0
  47. code_puppy/command_line/agent_menu.py +395 -0
  48. code_puppy/command_line/attachments.py +10 -5
  49. code_puppy/command_line/autosave_menu.py +605 -0
  50. code_puppy/command_line/clipboard.py +527 -0
  51. code_puppy/command_line/colors_menu.py +520 -0
  52. code_puppy/command_line/command_handler.py +176 -738
  53. code_puppy/command_line/command_registry.py +150 -0
  54. code_puppy/command_line/config_commands.py +715 -0
  55. code_puppy/command_line/core_commands.py +792 -0
  56. code_puppy/command_line/diff_menu.py +863 -0
  57. code_puppy/command_line/load_context_completion.py +15 -22
  58. code_puppy/command_line/mcp/base.py +0 -3
  59. code_puppy/command_line/mcp/catalog_server_installer.py +175 -0
  60. code_puppy/command_line/mcp/custom_server_form.py +688 -0
  61. code_puppy/command_line/mcp/custom_server_installer.py +195 -0
  62. code_puppy/command_line/mcp/edit_command.py +148 -0
  63. code_puppy/command_line/mcp/handler.py +9 -4
  64. code_puppy/command_line/mcp/help_command.py +6 -5
  65. code_puppy/command_line/mcp/install_command.py +15 -26
  66. code_puppy/command_line/mcp/install_menu.py +685 -0
  67. code_puppy/command_line/mcp/list_command.py +2 -2
  68. code_puppy/command_line/mcp/logs_command.py +174 -65
  69. code_puppy/command_line/mcp/remove_command.py +2 -2
  70. code_puppy/command_line/mcp/restart_command.py +12 -4
  71. code_puppy/command_line/mcp/search_command.py +16 -10
  72. code_puppy/command_line/mcp/start_all_command.py +18 -6
  73. code_puppy/command_line/mcp/start_command.py +47 -25
  74. code_puppy/command_line/mcp/status_command.py +4 -5
  75. code_puppy/command_line/mcp/stop_all_command.py +7 -1
  76. code_puppy/command_line/mcp/stop_command.py +8 -4
  77. code_puppy/command_line/mcp/test_command.py +2 -2
  78. code_puppy/command_line/mcp/wizard_utils.py +20 -16
  79. code_puppy/command_line/mcp_completion.py +174 -0
  80. code_puppy/command_line/model_picker_completion.py +75 -25
  81. code_puppy/command_line/model_settings_menu.py +884 -0
  82. code_puppy/command_line/motd.py +14 -8
  83. code_puppy/command_line/onboarding_slides.py +179 -0
  84. code_puppy/command_line/onboarding_wizard.py +340 -0
  85. code_puppy/command_line/pin_command_completion.py +329 -0
  86. code_puppy/command_line/prompt_toolkit_completion.py +463 -63
  87. code_puppy/command_line/session_commands.py +296 -0
  88. code_puppy/command_line/utils.py +54 -0
  89. code_puppy/config.py +898 -112
  90. code_puppy/error_logging.py +118 -0
  91. code_puppy/gemini_code_assist.py +385 -0
  92. code_puppy/gemini_model.py +602 -0
  93. code_puppy/http_utils.py +210 -148
  94. code_puppy/keymap.py +128 -0
  95. code_puppy/main.py +5 -698
  96. code_puppy/mcp_/__init__.py +17 -0
  97. code_puppy/mcp_/async_lifecycle.py +35 -4
  98. code_puppy/mcp_/blocking_startup.py +70 -43
  99. code_puppy/mcp_/captured_stdio_server.py +2 -2
  100. code_puppy/mcp_/config_wizard.py +4 -4
  101. code_puppy/mcp_/dashboard.py +15 -6
  102. code_puppy/mcp_/managed_server.py +65 -38
  103. code_puppy/mcp_/manager.py +146 -52
  104. code_puppy/mcp_/mcp_logs.py +224 -0
  105. code_puppy/mcp_/registry.py +6 -6
  106. code_puppy/mcp_/server_registry_catalog.py +24 -5
  107. code_puppy/messaging/__init__.py +199 -2
  108. code_puppy/messaging/bus.py +610 -0
  109. code_puppy/messaging/commands.py +167 -0
  110. code_puppy/messaging/markdown_patches.py +57 -0
  111. code_puppy/messaging/message_queue.py +17 -48
  112. code_puppy/messaging/messages.py +500 -0
  113. code_puppy/messaging/queue_console.py +1 -24
  114. code_puppy/messaging/renderers.py +43 -146
  115. code_puppy/messaging/rich_renderer.py +1027 -0
  116. code_puppy/messaging/spinner/__init__.py +21 -5
  117. code_puppy/messaging/spinner/console_spinner.py +86 -51
  118. code_puppy/messaging/subagent_console.py +461 -0
  119. code_puppy/model_factory.py +634 -83
  120. code_puppy/model_utils.py +167 -0
  121. code_puppy/models.json +66 -68
  122. code_puppy/models_dev_api.json +1 -0
  123. code_puppy/models_dev_parser.py +592 -0
  124. code_puppy/plugins/__init__.py +164 -10
  125. code_puppy/plugins/antigravity_oauth/__init__.py +10 -0
  126. code_puppy/plugins/antigravity_oauth/accounts.py +406 -0
  127. code_puppy/plugins/antigravity_oauth/antigravity_model.py +704 -0
  128. code_puppy/plugins/antigravity_oauth/config.py +42 -0
  129. code_puppy/plugins/antigravity_oauth/constants.py +136 -0
  130. code_puppy/plugins/antigravity_oauth/oauth.py +478 -0
  131. code_puppy/plugins/antigravity_oauth/register_callbacks.py +406 -0
  132. code_puppy/plugins/antigravity_oauth/storage.py +271 -0
  133. code_puppy/plugins/antigravity_oauth/test_plugin.py +319 -0
  134. code_puppy/plugins/antigravity_oauth/token.py +167 -0
  135. code_puppy/plugins/antigravity_oauth/transport.py +767 -0
  136. code_puppy/plugins/antigravity_oauth/utils.py +169 -0
  137. code_puppy/plugins/chatgpt_oauth/__init__.py +8 -0
  138. code_puppy/plugins/chatgpt_oauth/config.py +52 -0
  139. code_puppy/plugins/chatgpt_oauth/oauth_flow.py +328 -0
  140. code_puppy/plugins/chatgpt_oauth/register_callbacks.py +94 -0
  141. code_puppy/plugins/chatgpt_oauth/test_plugin.py +293 -0
  142. code_puppy/plugins/chatgpt_oauth/utils.py +489 -0
  143. code_puppy/plugins/claude_code_oauth/README.md +167 -0
  144. code_puppy/plugins/claude_code_oauth/SETUP.md +93 -0
  145. code_puppy/plugins/claude_code_oauth/__init__.py +6 -0
  146. code_puppy/plugins/claude_code_oauth/config.py +50 -0
  147. code_puppy/plugins/claude_code_oauth/register_callbacks.py +308 -0
  148. code_puppy/plugins/claude_code_oauth/test_plugin.py +283 -0
  149. code_puppy/plugins/claude_code_oauth/utils.py +518 -0
  150. code_puppy/plugins/customizable_commands/__init__.py +0 -0
  151. code_puppy/plugins/customizable_commands/register_callbacks.py +169 -0
  152. code_puppy/plugins/example_custom_command/README.md +280 -0
  153. code_puppy/plugins/example_custom_command/register_callbacks.py +2 -2
  154. code_puppy/plugins/file_permission_handler/__init__.py +4 -0
  155. code_puppy/plugins/file_permission_handler/register_callbacks.py +523 -0
  156. code_puppy/plugins/frontend_emitter/__init__.py +25 -0
  157. code_puppy/plugins/frontend_emitter/emitter.py +121 -0
  158. code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
  159. code_puppy/plugins/oauth_puppy_html.py +228 -0
  160. code_puppy/plugins/shell_safety/__init__.py +6 -0
  161. code_puppy/plugins/shell_safety/agent_shell_safety.py +69 -0
  162. code_puppy/plugins/shell_safety/command_cache.py +156 -0
  163. code_puppy/plugins/shell_safety/register_callbacks.py +202 -0
  164. code_puppy/prompts/antigravity_system_prompt.md +1 -0
  165. code_puppy/prompts/codex_system_prompt.md +310 -0
  166. code_puppy/pydantic_patches.py +131 -0
  167. code_puppy/reopenable_async_client.py +8 -8
  168. code_puppy/round_robin_model.py +9 -12
  169. code_puppy/session_storage.py +2 -1
  170. code_puppy/status_display.py +21 -4
  171. code_puppy/summarization_agent.py +41 -13
  172. code_puppy/terminal_utils.py +418 -0
  173. code_puppy/tools/__init__.py +37 -1
  174. code_puppy/tools/agent_tools.py +536 -52
  175. code_puppy/tools/browser/__init__.py +37 -0
  176. code_puppy/tools/browser/browser_control.py +19 -23
  177. code_puppy/tools/browser/browser_interactions.py +41 -48
  178. code_puppy/tools/browser/browser_locators.py +36 -38
  179. code_puppy/tools/browser/browser_manager.py +316 -0
  180. code_puppy/tools/browser/browser_navigation.py +16 -16
  181. code_puppy/tools/browser/browser_screenshot.py +79 -143
  182. code_puppy/tools/browser/browser_scripts.py +32 -42
  183. code_puppy/tools/browser/browser_workflows.py +44 -27
  184. code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
  185. code_puppy/tools/browser/terminal_command_tools.py +521 -0
  186. code_puppy/tools/browser/terminal_screenshot_tools.py +556 -0
  187. code_puppy/tools/browser/terminal_tools.py +525 -0
  188. code_puppy/tools/command_runner.py +930 -147
  189. code_puppy/tools/common.py +1113 -5
  190. code_puppy/tools/display.py +84 -0
  191. code_puppy/tools/file_modifications.py +288 -89
  192. code_puppy/tools/file_operations.py +226 -154
  193. code_puppy/tools/subagent_context.py +158 -0
  194. code_puppy/uvx_detection.py +242 -0
  195. code_puppy/version_checker.py +30 -11
  196. code_puppy-0.0.366.data/data/code_puppy/models.json +110 -0
  197. code_puppy-0.0.366.data/data/code_puppy/models_dev_api.json +1 -0
  198. {code_puppy-0.0.214.dist-info โ†’ code_puppy-0.0.366.dist-info}/METADATA +149 -75
  199. code_puppy-0.0.366.dist-info/RECORD +217 -0
  200. {code_puppy-0.0.214.dist-info โ†’ code_puppy-0.0.366.dist-info}/WHEEL +1 -1
  201. code_puppy/command_line/mcp/add_command.py +0 -183
  202. code_puppy/messaging/spinner/textual_spinner.py +0 -106
  203. code_puppy/tools/browser/camoufox_manager.py +0 -216
  204. code_puppy/tools/browser/vqa_agent.py +0 -70
  205. code_puppy/tui/__init__.py +0 -10
  206. code_puppy/tui/app.py +0 -1105
  207. code_puppy/tui/components/__init__.py +0 -21
  208. code_puppy/tui/components/chat_view.py +0 -551
  209. code_puppy/tui/components/command_history_modal.py +0 -218
  210. code_puppy/tui/components/copy_button.py +0 -139
  211. code_puppy/tui/components/custom_widgets.py +0 -63
  212. code_puppy/tui/components/human_input_modal.py +0 -175
  213. code_puppy/tui/components/input_area.py +0 -167
  214. code_puppy/tui/components/sidebar.py +0 -309
  215. code_puppy/tui/components/status_bar.py +0 -185
  216. code_puppy/tui/messages.py +0 -27
  217. code_puppy/tui/models/__init__.py +0 -8
  218. code_puppy/tui/models/chat_message.py +0 -25
  219. code_puppy/tui/models/command_history.py +0 -89
  220. code_puppy/tui/models/enums.py +0 -24
  221. code_puppy/tui/screens/__init__.py +0 -17
  222. code_puppy/tui/screens/autosave_picker.py +0 -175
  223. code_puppy/tui/screens/help.py +0 -130
  224. code_puppy/tui/screens/mcp_install_wizard.py +0 -803
  225. code_puppy/tui/screens/settings.py +0 -306
  226. code_puppy/tui/screens/tools.py +0 -74
  227. code_puppy/tui_state.py +0 -55
  228. code_puppy-0.0.214.data/data/code_puppy/models.json +0 -112
  229. code_puppy-0.0.214.dist-info/RECORD +0 -131
  230. {code_puppy-0.0.214.dist-info โ†’ code_puppy-0.0.366.dist-info}/entry_points.txt +0 -0
  231. {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 .camoufox_manager import get_camoufox_manager
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"[bold white on blue] BROWSER INITIALIZE [/bold white on blue] ๐ŸŒ {browser_type} โ†’ {homepage}",
21
+ f"BROWSER INITIALIZE ๐ŸŒ {browser_type} โ†’ {homepage}",
22
22
  message_group=group_id,
23
23
  )
24
24
  try:
25
- browser_manager = get_camoufox_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
- "[green]Browser initialized successfully[/green]", message_group=group_id
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
- emit_info(
59
- f"[red]Browser initialization failed: {str(e)}[/red]",
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
- "[bold white on blue] BROWSER CLOSE [/bold white on blue] ๐Ÿ”’",
74
+ "BROWSER CLOSE ๐Ÿ”’",
75
75
  message_group=group_id,
76
76
  )
77
77
  try:
78
- browser_manager = get_camoufox_manager()
78
+ browser_manager = get_session_browser_manager()
79
79
  await browser_manager.close()
80
80
 
81
- emit_info(
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
- "[bold white on blue] BROWSER STATUS [/bold white on blue] ๐Ÿ“Š",
93
+ "BROWSER STATUS ๐Ÿ“Š",
96
94
  message_group=group_id,
97
95
  )
98
96
  try:
99
- browser_manager = get_camoufox_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"[bold white on blue] BROWSER NEW PAGE [/bold white on blue] ๐Ÿ“„ {url or 'blank page'}",
138
+ f"BROWSER NEW PAGE ๐Ÿ“„ {url or 'blank page'}",
141
139
  message_group=group_id,
142
140
  )
143
141
  try:
144
- browser_manager = get_camoufox_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
- emit_info(
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
- "[bold white on blue] BROWSER LIST PAGES [/bold white on blue] ๐Ÿ“‹",
167
+ "BROWSER LIST PAGES ๐Ÿ“‹",
172
168
  message_group=group_id,
173
169
  )
174
170
  try:
175
- browser_manager = get_camoufox_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 .camoufox_manager import get_camoufox_manager
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"[bold white on blue] BROWSER CLICK [/bold white on blue] ๐Ÿ–ฑ๏ธ selector='{selector}' button={button}",
23
+ f"BROWSER CLICK ๐Ÿ–ฑ๏ธ selector='{selector}' button={button}",
24
24
  message_group=group_id,
25
25
  )
26
26
  try:
27
- browser_manager = get_camoufox_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
- element = page.locator(selector)
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
- emit_info(f"[green]Clicked element: {selector}[/green]", message_group=group_id)
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
- emit_info(f"[red]Click failed: {str(e)}[/red]", message_group=group_id)
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"[bold white on blue] BROWSER DOUBLE CLICK [/bold white on blue] ๐Ÿ–ฑ๏ธ๐Ÿ–ฑ๏ธ selector='{selector}'",
69
+ f"BROWSER DOUBLE CLICK ๐Ÿ–ฑ๏ธ๐Ÿ–ฑ๏ธ selector='{selector}'",
69
70
  message_group=group_id,
70
71
  )
71
72
  try:
72
- browser_manager = get_camoufox_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
- emit_info(
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"[bold white on blue] BROWSER HOVER [/bold white on blue] ๐Ÿ‘† selector='{selector}'",
99
+ f"BROWSER HOVER ๐Ÿ‘† selector='{selector}'",
101
100
  message_group=group_id,
102
101
  )
103
102
  try:
104
- browser_manager = get_camoufox_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
- emit_info(
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"[bold white on blue] BROWSER SET TEXT [/bold white on blue] โœ๏ธ selector='{selector}' text='{text[:50]}{'...' if len(text) > 50 else ''}'",
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 = get_camoufox_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
- emit_info(
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
- emit_info(f"[red]Set text failed: {str(e)}[/red]", message_group=group_id)
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"[bold white on blue] BROWSER GET TEXT [/bold white on blue] ๐Ÿ“ selector='{selector}'",
169
+ f"BROWSER GET TEXT ๐Ÿ“ selector='{selector}'",
175
170
  message_group=group_id,
176
171
  )
177
172
  try:
178
- browser_manager = get_camoufox_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"[bold white on blue] BROWSER GET VALUE [/bold white on blue] ๐Ÿ“Ž selector='{selector}'",
197
+ f"BROWSER GET VALUE ๐Ÿ“Ž selector='{selector}'",
203
198
  message_group=group_id,
204
199
  )
205
200
  try:
206
- browser_manager = get_camoufox_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"[bold white on blue] BROWSER SELECT OPTION [/bold white on blue] ๐Ÿ“„ selector='{selector}' option='{option_desc}'",
231
+ f"BROWSER SELECT OPTION ๐Ÿ“„ selector='{selector}' option='{option_desc}'",
237
232
  message_group=group_id,
238
233
  )
239
234
  try:
240
- browser_manager = get_camoufox_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
- emit_info(
266
- f"[green]Selected option in {selector}: {selection}[/green]",
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"[bold white on blue] BROWSER CHECK [/bold white on blue] โ˜‘๏ธ selector='{selector}'",
278
+ f"BROWSER CHECK โ˜‘๏ธ selector='{selector}'",
284
279
  message_group=group_id,
285
280
  )
286
281
  try:
287
- browser_manager = get_camoufox_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
- emit_info(f"[green]Checked element: {selector}[/green]", message_group=group_id)
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"[bold white on blue] BROWSER UNCHECK [/bold white on blue] โ˜๏ธ selector='{selector}'",
307
+ f"BROWSER UNCHECK โ˜๏ธ selector='{selector}'",
313
308
  message_group=group_id,
314
309
  )
315
310
  try:
316
- browser_manager = get_camoufox_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
- emit_info(
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 .camoufox_manager import get_camoufox_manager
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"[bold white on blue] BROWSER FIND BY ROLE [/bold white on blue] ๐ŸŽจ role={role} name={name}",
22
+ f"BROWSER FIND BY ROLE ๐ŸŽจ role={role} name={name}",
23
23
  message_group=group_id,
24
24
  )
25
25
  try:
26
- browser_manager = get_camoufox_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
- emit_info(
50
- f"[green]Found {count} elements with role '{role}'[/green]",
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"[bold white on blue] BROWSER FIND BY TEXT [/bold white on blue] ๐Ÿ” text='{text}' exact={exact}",
74
+ f"BROWSER FIND BY TEXT ๐Ÿ” text='{text}' exact={exact}",
75
75
  message_group=group_id,
76
76
  )
77
77
  try:
78
- browser_manager = get_camoufox_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
- emit_info(
102
- f"[green]Found {count} elements containing text '{text}'[/green]",
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"[bold white on blue] BROWSER FIND BY LABEL [/bold white on blue] ๐Ÿท๏ธ label='{text}' exact={exact}",
126
+ f"BROWSER FIND BY LABEL ๐Ÿท๏ธ label='{text}' exact={exact}",
127
127
  message_group=group_id,
128
128
  )
129
129
  try:
130
- browser_manager = get_camoufox_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
- emit_info(
165
- f"[green]Found {count} elements with label '{text}'[/green]",
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"[bold white on blue] BROWSER FIND BY PLACEHOLDER [/bold white on blue] ๐Ÿ“ placeholder='{text}' exact={exact}",
189
+ f"BROWSER FIND BY PLACEHOLDER ๐Ÿ“ placeholder='{text}' exact={exact}",
190
190
  message_group=group_id,
191
191
  )
192
192
  try:
193
- browser_manager = get_camoufox_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
- emit_info(
224
- f"[green]Found {count} elements with placeholder '{text}'[/green]",
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"[bold white on blue] BROWSER FIND BY TEST ID [/bold white on blue] ๐Ÿงช test_id='{test_id}'",
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 = get_camoufox_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
- emit_info(
281
- f"[green]Found {count} elements with test-id '{test_id}'[/green]",
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"[bold white on blue] BROWSER XPATH QUERY [/bold white on blue] ๐Ÿ” xpath='{xpath}'",
303
+ f"BROWSER XPATH QUERY ๐Ÿ” xpath='{xpath}'",
304
304
  message_group=group_id,
305
305
  )
306
306
  try:
307
- browser_manager = get_camoufox_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
- emit_info(
342
- f"[green]Found {count} elements with XPath '{xpath}'[/green]",
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"[bold white on blue] BROWSER FIND BUTTONS [/bold white on blue] ๐Ÿ”˜ filter='{text_filter or 'none'}'",
358
+ f"BROWSER FIND BUTTONS ๐Ÿ”˜ filter='{text_filter or 'none'}'",
359
359
  message_group=group_id,
360
360
  )
361
361
  try:
362
- browser_manager = get_camoufox_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
- emit_info(
386
- f"[green]Found {filtered_count} buttons"
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"[bold white on blue] BROWSER FIND LINKS [/bold white on blue] ๐Ÿ”— filter='{text_filter or 'none'}'",
409
+ f"BROWSER FIND LINKS ๐Ÿ”— filter='{text_filter or 'none'}'",
411
410
  message_group=group_id,
412
411
  )
413
412
  try:
414
- browser_manager = get_camoufox_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
- emit_info(
440
- f"[green]Found {filtered_count} links"
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