code-puppy 0.0.341__py3-none-any.whl → 0.0.361__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/agents/__init__.py +2 -0
- code_puppy/agents/agent_manager.py +49 -0
- code_puppy/agents/agent_pack_leader.py +383 -0
- code_puppy/agents/agent_qa_kitten.py +12 -7
- code_puppy/agents/agent_terminal_qa.py +323 -0
- code_puppy/agents/base_agent.py +34 -252
- 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/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 +73 -0
- code_puppy/claude_cache_client.py +249 -34
- code_puppy/cli_runner.py +4 -3
- code_puppy/command_line/add_model_menu.py +8 -9
- code_puppy/command_line/core_commands.py +85 -0
- code_puppy/command_line/mcp/catalog_server_installer.py +5 -6
- code_puppy/command_line/mcp/custom_server_form.py +54 -19
- code_puppy/command_line/mcp/custom_server_installer.py +8 -9
- code_puppy/command_line/mcp/handler.py +0 -2
- code_puppy/command_line/mcp/help_command.py +1 -5
- code_puppy/command_line/mcp/start_command.py +36 -18
- code_puppy/command_line/onboarding_slides.py +0 -1
- code_puppy/command_line/prompt_toolkit_completion.py +16 -10
- code_puppy/command_line/utils.py +54 -0
- code_puppy/config.py +66 -62
- code_puppy/mcp_/async_lifecycle.py +35 -4
- code_puppy/mcp_/managed_server.py +49 -20
- code_puppy/mcp_/manager.py +81 -52
- code_puppy/messaging/__init__.py +15 -0
- code_puppy/messaging/message_queue.py +11 -23
- code_puppy/messaging/messages.py +27 -0
- code_puppy/messaging/queue_console.py +1 -1
- code_puppy/messaging/rich_renderer.py +36 -1
- code_puppy/messaging/spinner/__init__.py +20 -2
- code_puppy/messaging/subagent_console.py +461 -0
- code_puppy/model_utils.py +54 -0
- code_puppy/plugins/antigravity_oauth/antigravity_model.py +90 -19
- code_puppy/plugins/antigravity_oauth/transport.py +1 -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/prompts/antigravity_system_prompt.md +1 -0
- code_puppy/status_display.py +6 -2
- code_puppy/tools/__init__.py +37 -1
- code_puppy/tools/agent_tools.py +139 -36
- code_puppy/tools/browser/__init__.py +37 -0
- code_puppy/tools/browser/browser_control.py +6 -6
- code_puppy/tools/browser/browser_interactions.py +21 -20
- code_puppy/tools/browser/browser_locators.py +9 -9
- code_puppy/tools/browser/browser_navigation.py +7 -7
- code_puppy/tools/browser/browser_screenshot.py +78 -140
- code_puppy/tools/browser/browser_scripts.py +15 -13
- code_puppy/tools/browser/camoufox_manager.py +226 -64
- 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 +292 -101
- code_puppy/tools/common.py +176 -1
- code_puppy/tools/display.py +84 -0
- code_puppy/tools/subagent_context.py +158 -0
- {code_puppy-0.0.341.dist-info → code_puppy-0.0.361.dist-info}/METADATA +13 -11
- {code_puppy-0.0.341.dist-info → code_puppy-0.0.361.dist-info}/RECORD +84 -53
- code_puppy/command_line/mcp/add_command.py +0 -170
- code_puppy/tools/browser/vqa_agent.py +0 -90
- {code_puppy-0.0.341.data → code_puppy-0.0.361.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.341.data → code_puppy-0.0.361.data}/data/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.341.dist-info → code_puppy-0.0.361.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.341.dist-info → code_puppy-0.0.361.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.341.dist-info → code_puppy-0.0.361.dist-info}/licenses/LICENSE +0 -0
code_puppy/tools/agent_tools.py
CHANGED
|
@@ -7,6 +7,7 @@ import pickle
|
|
|
7
7
|
import re
|
|
8
8
|
import traceback
|
|
9
9
|
from datetime import datetime
|
|
10
|
+
from functools import partial
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from typing import List, Set
|
|
12
13
|
|
|
@@ -21,18 +22,20 @@ from code_puppy.config import (
|
|
|
21
22
|
DATA_DIR,
|
|
22
23
|
get_message_limit,
|
|
23
24
|
get_use_dbos,
|
|
25
|
+
get_value,
|
|
24
26
|
)
|
|
25
27
|
from code_puppy.messaging import (
|
|
26
28
|
SubAgentInvocationMessage,
|
|
27
29
|
SubAgentResponseMessage,
|
|
28
30
|
emit_error,
|
|
29
31
|
emit_info,
|
|
32
|
+
emit_success,
|
|
30
33
|
get_message_bus,
|
|
31
34
|
get_session_context,
|
|
32
35
|
set_session_context,
|
|
33
36
|
)
|
|
34
|
-
from code_puppy.model_factory import ModelFactory, make_model_settings
|
|
35
37
|
from code_puppy.tools.common import generate_group_id
|
|
38
|
+
from code_puppy.tools.subagent_context import subagent_context
|
|
36
39
|
|
|
37
40
|
# Set to track active subagent invocation tasks
|
|
38
41
|
_active_subagent_tasks: Set[asyncio.Task] = set()
|
|
@@ -412,6 +415,9 @@ def register_invoke_agent(agent):
|
|
|
412
415
|
session_id = f"{session_id}-{hash_suffix}"
|
|
413
416
|
# else: continuing existing session, use session_id as-is
|
|
414
417
|
|
|
418
|
+
# Lazy imports to avoid circular dependency
|
|
419
|
+
from code_puppy.agents.subagent_stream_handler import subagent_stream_handler
|
|
420
|
+
|
|
415
421
|
# Emit structured invocation message via MessageBus
|
|
416
422
|
bus = get_message_bus()
|
|
417
423
|
bus.emit(
|
|
@@ -428,7 +434,27 @@ def register_invoke_agent(agent):
|
|
|
428
434
|
previous_session_id = get_session_context()
|
|
429
435
|
set_session_context(session_id)
|
|
430
436
|
|
|
437
|
+
# Set terminal session for browser-based terminal tools
|
|
438
|
+
# This uses contextvars which properly propagate through async tasks
|
|
439
|
+
from code_puppy.tools.browser.terminal_tools import (
|
|
440
|
+
_terminal_session_var,
|
|
441
|
+
set_terminal_session,
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
terminal_session_token = set_terminal_session(f"terminal-{session_id}")
|
|
445
|
+
|
|
446
|
+
# Set browser session for Camoufox browser tools (qa-kitten, etc.)
|
|
447
|
+
# This allows parallel agent invocations to each have their own browser
|
|
448
|
+
from code_puppy.tools.browser.camoufox_manager import (
|
|
449
|
+
set_browser_session,
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
browser_session_token = set_browser_session(f"browser-{session_id}")
|
|
453
|
+
|
|
431
454
|
try:
|
|
455
|
+
# Lazy import to break circular dependency with messaging module
|
|
456
|
+
from code_puppy.model_factory import ModelFactory, make_model_settings
|
|
457
|
+
|
|
432
458
|
# Load the specified agent config
|
|
433
459
|
agent_config = load_agent(agent_name)
|
|
434
460
|
|
|
@@ -471,59 +497,118 @@ def register_invoke_agent(agent):
|
|
|
471
497
|
subagent_name = f"temp-invoke-agent-{session_id}"
|
|
472
498
|
model_settings = make_model_settings(model_name)
|
|
473
499
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
instructions=instructions,
|
|
477
|
-
output_type=str,
|
|
478
|
-
retries=3,
|
|
479
|
-
history_processors=[agent_config.message_history_accumulator],
|
|
480
|
-
model_settings=model_settings,
|
|
481
|
-
)
|
|
482
|
-
|
|
483
|
-
# Register the tools that the agent needs
|
|
484
|
-
from code_puppy.tools import register_tools_for_agent
|
|
500
|
+
# Get MCP servers for sub-agents (same as main agent)
|
|
501
|
+
from code_puppy.mcp_ import get_mcp_manager
|
|
485
502
|
|
|
486
|
-
|
|
487
|
-
|
|
503
|
+
mcp_servers = []
|
|
504
|
+
mcp_disabled = get_value("disable_mcp_servers")
|
|
505
|
+
if not (
|
|
506
|
+
mcp_disabled and str(mcp_disabled).lower() in ("1", "true", "yes", "on")
|
|
507
|
+
):
|
|
508
|
+
manager = get_mcp_manager()
|
|
509
|
+
mcp_servers = manager.get_servers_for_agent()
|
|
488
510
|
|
|
489
511
|
if get_use_dbos():
|
|
490
512
|
from pydantic_ai.durable_exec.dbos import DBOSAgent
|
|
491
513
|
|
|
492
|
-
|
|
514
|
+
# For DBOS, create agent without MCP servers (to avoid serialization issues)
|
|
515
|
+
# and add them at runtime
|
|
516
|
+
temp_agent = Agent(
|
|
517
|
+
model=model,
|
|
518
|
+
instructions=instructions,
|
|
519
|
+
output_type=str,
|
|
520
|
+
retries=3,
|
|
521
|
+
toolsets=[], # MCP servers added separately for DBOS
|
|
522
|
+
history_processors=[agent_config.message_history_accumulator],
|
|
523
|
+
model_settings=model_settings,
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
# Register the tools that the agent needs
|
|
527
|
+
from code_puppy.tools import register_tools_for_agent
|
|
528
|
+
|
|
529
|
+
agent_tools = agent_config.get_available_tools()
|
|
530
|
+
register_tools_for_agent(temp_agent, agent_tools)
|
|
531
|
+
|
|
532
|
+
# Wrap with DBOS - no streaming for sub-agents
|
|
533
|
+
dbos_agent = DBOSAgent(
|
|
534
|
+
temp_agent,
|
|
535
|
+
name=subagent_name,
|
|
536
|
+
)
|
|
493
537
|
temp_agent = dbos_agent
|
|
494
538
|
|
|
539
|
+
# Store MCP servers to add at runtime
|
|
540
|
+
subagent_mcp_servers = mcp_servers
|
|
541
|
+
else:
|
|
542
|
+
# Non-DBOS path - include MCP servers directly in the agent
|
|
543
|
+
temp_agent = Agent(
|
|
544
|
+
model=model,
|
|
545
|
+
instructions=instructions,
|
|
546
|
+
output_type=str,
|
|
547
|
+
retries=3,
|
|
548
|
+
toolsets=mcp_servers,
|
|
549
|
+
history_processors=[agent_config.message_history_accumulator],
|
|
550
|
+
model_settings=model_settings,
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
# Register the tools that the agent needs
|
|
554
|
+
from code_puppy.tools import register_tools_for_agent
|
|
555
|
+
|
|
556
|
+
agent_tools = agent_config.get_available_tools()
|
|
557
|
+
register_tools_for_agent(temp_agent, agent_tools)
|
|
558
|
+
|
|
559
|
+
subagent_mcp_servers = None
|
|
560
|
+
|
|
495
561
|
# Run the temporary agent with the provided prompt as an asyncio task
|
|
496
562
|
# Pass the message_history from the session to continue the conversation
|
|
497
563
|
workflow_id = None # Track for potential cancellation
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
564
|
+
|
|
565
|
+
# Always use subagent_stream_handler to silence output and update console manager
|
|
566
|
+
# This ensures all sub-agent output goes through the aggregated dashboard
|
|
567
|
+
stream_handler = partial(subagent_stream_handler, session_id=session_id)
|
|
568
|
+
|
|
569
|
+
# Wrap the agent run in subagent context for tracking
|
|
570
|
+
with subagent_context(agent_name):
|
|
571
|
+
if get_use_dbos():
|
|
572
|
+
# Generate a unique workflow ID for DBOS - ensures no collisions in back-to-back calls
|
|
573
|
+
workflow_id = _generate_dbos_workflow_id(group_id)
|
|
574
|
+
|
|
575
|
+
# Add MCP servers to the DBOS agent's toolsets
|
|
576
|
+
# (temp_agent is discarded after this invocation, so no need to restore)
|
|
577
|
+
if subagent_mcp_servers:
|
|
578
|
+
temp_agent._toolsets = (
|
|
579
|
+
temp_agent._toolsets + subagent_mcp_servers
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
with SetWorkflowID(workflow_id):
|
|
583
|
+
task = asyncio.create_task(
|
|
584
|
+
temp_agent.run(
|
|
585
|
+
prompt,
|
|
586
|
+
message_history=message_history,
|
|
587
|
+
usage_limits=UsageLimits(
|
|
588
|
+
request_limit=get_message_limit()
|
|
589
|
+
),
|
|
590
|
+
event_stream_handler=stream_handler,
|
|
591
|
+
)
|
|
592
|
+
)
|
|
593
|
+
_active_subagent_tasks.add(task)
|
|
594
|
+
else:
|
|
502
595
|
task = asyncio.create_task(
|
|
503
596
|
temp_agent.run(
|
|
504
597
|
prompt,
|
|
505
598
|
message_history=message_history,
|
|
506
599
|
usage_limits=UsageLimits(request_limit=get_message_limit()),
|
|
600
|
+
event_stream_handler=stream_handler,
|
|
507
601
|
)
|
|
508
602
|
)
|
|
509
603
|
_active_subagent_tasks.add(task)
|
|
510
|
-
else:
|
|
511
|
-
task = asyncio.create_task(
|
|
512
|
-
temp_agent.run(
|
|
513
|
-
prompt,
|
|
514
|
-
message_history=message_history,
|
|
515
|
-
usage_limits=UsageLimits(request_limit=get_message_limit()),
|
|
516
|
-
)
|
|
517
|
-
)
|
|
518
|
-
_active_subagent_tasks.add(task)
|
|
519
604
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
605
|
+
try:
|
|
606
|
+
result = await task
|
|
607
|
+
finally:
|
|
608
|
+
_active_subagent_tasks.discard(task)
|
|
609
|
+
if task.cancelled():
|
|
610
|
+
if get_use_dbos() and workflow_id:
|
|
611
|
+
DBOS.cancel_workflow(workflow_id)
|
|
527
612
|
|
|
528
613
|
# Extract the response from the result
|
|
529
614
|
response = result.output
|
|
@@ -550,13 +635,23 @@ def register_invoke_agent(agent):
|
|
|
550
635
|
)
|
|
551
636
|
)
|
|
552
637
|
|
|
638
|
+
# Emit clean completion summary
|
|
639
|
+
emit_success(
|
|
640
|
+
f"✓ {agent_name} completed successfully", message_group=group_id
|
|
641
|
+
)
|
|
642
|
+
|
|
553
643
|
return AgentInvokeOutput(
|
|
554
644
|
response=response, agent_name=agent_name, session_id=session_id
|
|
555
645
|
)
|
|
556
646
|
|
|
557
|
-
except Exception:
|
|
647
|
+
except Exception as e:
|
|
648
|
+
# Emit clean failure summary
|
|
649
|
+
emit_error(f"✗ {agent_name} failed: {str(e)}", message_group=group_id)
|
|
650
|
+
|
|
651
|
+
# Full traceback for debugging
|
|
558
652
|
error_msg = f"Error invoking agent '{agent_name}': {traceback.format_exc()}"
|
|
559
653
|
emit_error(error_msg, message_group=group_id)
|
|
654
|
+
|
|
560
655
|
return AgentInvokeOutput(
|
|
561
656
|
response=None,
|
|
562
657
|
agent_name=agent_name,
|
|
@@ -567,5 +662,13 @@ def register_invoke_agent(agent):
|
|
|
567
662
|
finally:
|
|
568
663
|
# Restore the previous session context
|
|
569
664
|
set_session_context(previous_session_id)
|
|
665
|
+
# Reset terminal session context
|
|
666
|
+
_terminal_session_var.reset(terminal_session_token)
|
|
667
|
+
# Reset browser session context
|
|
668
|
+
from code_puppy.tools.browser.camoufox_manager import (
|
|
669
|
+
_browser_session_var,
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
_browser_session_var.reset(browser_session_token)
|
|
570
673
|
|
|
571
674
|
return invoke_agent
|
|
@@ -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 .camoufox_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
|
+
]
|
|
@@ -7,7 +7,7 @@ from pydantic_ai import RunContext
|
|
|
7
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
|
|
10
|
+
from .camoufox_manager import get_session_browser_manager
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
async def initialize_browser(
|
|
@@ -22,7 +22,7 @@ async def initialize_browser(
|
|
|
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
|
|
@@ -75,7 +75,7 @@ async def close_browser() -> Dict[str, Any]:
|
|
|
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
81
|
emit_warning("Browser closed successfully", message_group=group_id)
|
|
@@ -94,7 +94,7 @@ async def get_browser_status() -> Dict[str, Any]:
|
|
|
94
94
|
message_group=group_id,
|
|
95
95
|
)
|
|
96
96
|
try:
|
|
97
|
-
browser_manager =
|
|
97
|
+
browser_manager = get_session_browser_manager()
|
|
98
98
|
|
|
99
99
|
if not browser_manager._initialized:
|
|
100
100
|
return {
|
|
@@ -139,7 +139,7 @@ async def create_new_page(url: Optional[str] = None) -> Dict[str, Any]:
|
|
|
139
139
|
message_group=group_id,
|
|
140
140
|
)
|
|
141
141
|
try:
|
|
142
|
-
browser_manager =
|
|
142
|
+
browser_manager = get_session_browser_manager()
|
|
143
143
|
|
|
144
144
|
if not browser_manager._initialized:
|
|
145
145
|
return {
|
|
@@ -168,7 +168,7 @@ async def list_pages() -> Dict[str, Any]:
|
|
|
168
168
|
message_group=group_id,
|
|
169
169
|
)
|
|
170
170
|
try:
|
|
171
|
-
browser_manager =
|
|
171
|
+
browser_manager = get_session_browser_manager()
|
|
172
172
|
|
|
173
173
|
if not browser_manager._initialized:
|
|
174
174
|
return {"success": False, "error": "Browser not initialized"}
|
|
@@ -7,7 +7,7 @@ from pydantic_ai import RunContext
|
|
|
7
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
|
|
10
|
+
from .camoufox_manager import get_session_browser_manager
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
async def click_element(
|
|
@@ -24,14 +24,15 @@ async def click_element(
|
|
|
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)
|
|
@@ -69,13 +70,13 @@ async def double_click_element(
|
|
|
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
|
|
|
@@ -99,13 +100,13 @@ async def hover_element(
|
|
|
99
100
|
message_group=group_id,
|
|
100
101
|
)
|
|
101
102
|
try:
|
|
102
|
-
browser_manager =
|
|
103
|
+
browser_manager = get_session_browser_manager()
|
|
103
104
|
page = await browser_manager.get_current_page()
|
|
104
105
|
|
|
105
106
|
if not page:
|
|
106
107
|
return {"success": False, "error": "No active browser page available"}
|
|
107
108
|
|
|
108
|
-
element = page.locator(selector)
|
|
109
|
+
element = page.locator(selector).first
|
|
109
110
|
await element.wait_for(state="visible", timeout=timeout)
|
|
110
111
|
await element.hover(force=force, timeout=timeout)
|
|
111
112
|
|
|
@@ -130,13 +131,13 @@ async def set_element_text(
|
|
|
130
131
|
message_group=group_id,
|
|
131
132
|
)
|
|
132
133
|
try:
|
|
133
|
-
browser_manager =
|
|
134
|
+
browser_manager = get_session_browser_manager()
|
|
134
135
|
page = await browser_manager.get_current_page()
|
|
135
136
|
|
|
136
137
|
if not page:
|
|
137
138
|
return {"success": False, "error": "No active browser page available"}
|
|
138
139
|
|
|
139
|
-
element = page.locator(selector)
|
|
140
|
+
element = page.locator(selector).first
|
|
140
141
|
await element.wait_for(state="visible", timeout=timeout)
|
|
141
142
|
|
|
142
143
|
if clear_first:
|
|
@@ -169,13 +170,13 @@ async def get_element_text(
|
|
|
169
170
|
message_group=group_id,
|
|
170
171
|
)
|
|
171
172
|
try:
|
|
172
|
-
browser_manager =
|
|
173
|
+
browser_manager = get_session_browser_manager()
|
|
173
174
|
page = await browser_manager.get_current_page()
|
|
174
175
|
|
|
175
176
|
if not page:
|
|
176
177
|
return {"success": False, "error": "No active browser page available"}
|
|
177
178
|
|
|
178
|
-
element = page.locator(selector)
|
|
179
|
+
element = page.locator(selector).first
|
|
179
180
|
await element.wait_for(state="visible", timeout=timeout)
|
|
180
181
|
|
|
181
182
|
text = await element.text_content()
|
|
@@ -197,13 +198,13 @@ async def get_element_value(
|
|
|
197
198
|
message_group=group_id,
|
|
198
199
|
)
|
|
199
200
|
try:
|
|
200
|
-
browser_manager =
|
|
201
|
+
browser_manager = get_session_browser_manager()
|
|
201
202
|
page = await browser_manager.get_current_page()
|
|
202
203
|
|
|
203
204
|
if not page:
|
|
204
205
|
return {"success": False, "error": "No active browser page available"}
|
|
205
206
|
|
|
206
|
-
element = page.locator(selector)
|
|
207
|
+
element = page.locator(selector).first
|
|
207
208
|
await element.wait_for(state="visible", timeout=timeout)
|
|
208
209
|
|
|
209
210
|
value = await element.input_value()
|
|
@@ -231,13 +232,13 @@ async def select_option(
|
|
|
231
232
|
message_group=group_id,
|
|
232
233
|
)
|
|
233
234
|
try:
|
|
234
|
-
browser_manager =
|
|
235
|
+
browser_manager = get_session_browser_manager()
|
|
235
236
|
page = await browser_manager.get_current_page()
|
|
236
237
|
|
|
237
238
|
if not page:
|
|
238
239
|
return {"success": False, "error": "No active browser page available"}
|
|
239
240
|
|
|
240
|
-
element = page.locator(selector)
|
|
241
|
+
element = page.locator(selector).first
|
|
241
242
|
await element.wait_for(state="visible", timeout=timeout)
|
|
242
243
|
|
|
243
244
|
if value is not None:
|
|
@@ -278,13 +279,13 @@ async def check_element(
|
|
|
278
279
|
message_group=group_id,
|
|
279
280
|
)
|
|
280
281
|
try:
|
|
281
|
-
browser_manager =
|
|
282
|
+
browser_manager = get_session_browser_manager()
|
|
282
283
|
page = await browser_manager.get_current_page()
|
|
283
284
|
|
|
284
285
|
if not page:
|
|
285
286
|
return {"success": False, "error": "No active browser page available"}
|
|
286
287
|
|
|
287
|
-
element = page.locator(selector)
|
|
288
|
+
element = page.locator(selector).first
|
|
288
289
|
await element.wait_for(state="visible", timeout=timeout)
|
|
289
290
|
await element.check(timeout=timeout)
|
|
290
291
|
|
|
@@ -307,13 +308,13 @@ async def uncheck_element(
|
|
|
307
308
|
message_group=group_id,
|
|
308
309
|
)
|
|
309
310
|
try:
|
|
310
|
-
browser_manager =
|
|
311
|
+
browser_manager = get_session_browser_manager()
|
|
311
312
|
page = await browser_manager.get_current_page()
|
|
312
313
|
|
|
313
314
|
if not page:
|
|
314
315
|
return {"success": False, "error": "No active browser page available"}
|
|
315
316
|
|
|
316
|
-
element = page.locator(selector)
|
|
317
|
+
element = page.locator(selector).first
|
|
317
318
|
await element.wait_for(state="visible", timeout=timeout)
|
|
318
319
|
await element.uncheck(timeout=timeout)
|
|
319
320
|
|
|
@@ -7,7 +7,7 @@ from pydantic_ai import RunContext
|
|
|
7
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
|
|
10
|
+
from .camoufox_manager import get_session_browser_manager
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
async def find_by_role(
|
|
@@ -23,7 +23,7 @@ async def find_by_role(
|
|
|
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:
|
|
@@ -75,7 +75,7 @@ async def find_by_text(
|
|
|
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:
|
|
@@ -127,7 +127,7 @@ async def find_by_label(
|
|
|
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:
|
|
@@ -190,7 +190,7 @@ async def find_by_placeholder(
|
|
|
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:
|
|
@@ -248,7 +248,7 @@ async def find_by_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:
|
|
@@ -304,7 +304,7 @@ async def run_xpath_query(
|
|
|
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:
|
|
@@ -359,7 +359,7 @@ async def find_buttons(
|
|
|
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:
|
|
@@ -410,7 +410,7 @@ async def find_links(
|
|
|
410
410
|
message_group=group_id,
|
|
411
411
|
)
|
|
412
412
|
try:
|
|
413
|
-
browser_manager =
|
|
413
|
+
browser_manager = get_session_browser_manager()
|
|
414
414
|
page = await browser_manager.get_current_page()
|
|
415
415
|
|
|
416
416
|
if not page:
|
|
@@ -7,7 +7,7 @@ from pydantic_ai import RunContext
|
|
|
7
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
|
|
10
|
+
from .camoufox_manager import get_session_browser_manager
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
async def navigate_to_url(url: str) -> Dict[str, Any]:
|
|
@@ -18,7 +18,7 @@ async def navigate_to_url(url: str) -> Dict[str, Any]:
|
|
|
18
18
|
message_group=group_id,
|
|
19
19
|
)
|
|
20
20
|
try:
|
|
21
|
-
browser_manager =
|
|
21
|
+
browser_manager = get_session_browser_manager()
|
|
22
22
|
page = await browser_manager.get_current_page()
|
|
23
23
|
|
|
24
24
|
if not page:
|
|
@@ -48,7 +48,7 @@ async def get_page_info() -> Dict[str, Any]:
|
|
|
48
48
|
message_group=group_id,
|
|
49
49
|
)
|
|
50
50
|
try:
|
|
51
|
-
browser_manager =
|
|
51
|
+
browser_manager = get_session_browser_manager()
|
|
52
52
|
page = await browser_manager.get_current_page()
|
|
53
53
|
|
|
54
54
|
if not page:
|
|
@@ -71,7 +71,7 @@ async def go_back() -> Dict[str, Any]:
|
|
|
71
71
|
message_group=group_id,
|
|
72
72
|
)
|
|
73
73
|
try:
|
|
74
|
-
browser_manager =
|
|
74
|
+
browser_manager = get_session_browser_manager()
|
|
75
75
|
page = await browser_manager.get_current_page()
|
|
76
76
|
|
|
77
77
|
if not page:
|
|
@@ -93,7 +93,7 @@ async def go_forward() -> Dict[str, Any]:
|
|
|
93
93
|
message_group=group_id,
|
|
94
94
|
)
|
|
95
95
|
try:
|
|
96
|
-
browser_manager =
|
|
96
|
+
browser_manager = get_session_browser_manager()
|
|
97
97
|
page = await browser_manager.get_current_page()
|
|
98
98
|
|
|
99
99
|
if not page:
|
|
@@ -115,7 +115,7 @@ async def reload_page(wait_until: str = "domcontentloaded") -> Dict[str, Any]:
|
|
|
115
115
|
message_group=group_id,
|
|
116
116
|
)
|
|
117
117
|
try:
|
|
118
|
-
browser_manager =
|
|
118
|
+
browser_manager = get_session_browser_manager()
|
|
119
119
|
page = await browser_manager.get_current_page()
|
|
120
120
|
|
|
121
121
|
if not page:
|
|
@@ -139,7 +139,7 @@ async def wait_for_load_state(
|
|
|
139
139
|
message_group=group_id,
|
|
140
140
|
)
|
|
141
141
|
try:
|
|
142
|
-
browser_manager =
|
|
142
|
+
browser_manager = get_session_browser_manager()
|
|
143
143
|
page = await browser_manager.get_current_page()
|
|
144
144
|
|
|
145
145
|
if not page:
|