code-puppy 0.0.177__py3-none-any.whl → 0.0.179__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/agent_golang_reviewer.py +61 -0
- code_puppy/agents/base_agent.py +8 -11
- code_puppy/main.py +2 -0
- code_puppy/tui/app.py +2 -0
- {code_puppy-0.0.177.dist-info → code_puppy-0.0.179.dist-info}/METADATA +1 -1
- {code_puppy-0.0.177.dist-info → code_puppy-0.0.179.dist-info}/RECORD +10 -16
- code_puppy/tools/browser_control.py +0 -293
- code_puppy/tools/browser_interactions.py +0 -552
- code_puppy/tools/browser_locators.py +0 -642
- code_puppy/tools/browser_navigation.py +0 -251
- code_puppy/tools/browser_screenshot.py +0 -278
- code_puppy/tools/browser_scripts.py +0 -472
- code_puppy/tools/browser_workflows.py +0 -223
- {code_puppy-0.0.177.data → code_puppy-0.0.179.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.177.dist-info → code_puppy-0.0.179.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.177.dist-info → code_puppy-0.0.179.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.177.dist-info → code_puppy-0.0.179.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
"""Golang code reviewer agent."""
|
2
|
+
|
3
|
+
from .base_agent import BaseAgent
|
4
|
+
|
5
|
+
|
6
|
+
class GolangReviewerAgent(BaseAgent):
|
7
|
+
"""Golang-focused code reviewer agent."""
|
8
|
+
|
9
|
+
@property
|
10
|
+
def name(self) -> str:
|
11
|
+
return "golang-reviewer"
|
12
|
+
|
13
|
+
@property
|
14
|
+
def display_name(self) -> str:
|
15
|
+
return "Golang Reviewer 🦴"
|
16
|
+
|
17
|
+
@property
|
18
|
+
def description(self) -> str:
|
19
|
+
return "Meticulous reviewer for Go pull requests with idiomatic guidance"
|
20
|
+
|
21
|
+
def get_available_tools(self) -> list[str]:
|
22
|
+
"""Reviewers only need read and reasoning helpers."""
|
23
|
+
return [
|
24
|
+
"agent_share_your_reasoning",
|
25
|
+
"list_files",
|
26
|
+
"read_file",
|
27
|
+
"grep",
|
28
|
+
]
|
29
|
+
|
30
|
+
def get_system_prompt(self) -> str:
|
31
|
+
return """
|
32
|
+
You are an expert Golang reviewer puppy. Sniff only the Go code that changed, bark constructive stuff, and keep it playful but razor sharp without name-dropping any specific humans.
|
33
|
+
|
34
|
+
Mission profile:
|
35
|
+
- Review only tracked `.go` files with real code diffs. If a file is untouched or only whitespace/comments changed, just wag your tail and skip it.
|
36
|
+
- Ignore every non-Go file: `.yml`, `.yaml`, `.md`, `.json`, `.txt`, `Dockerfile`, `LICENSE`, `README.md`, etc. If someone tries to sneak one in, roll over and move on.
|
37
|
+
- Live by `Effective Go` (https://go.dev/doc/effective_go) and the `Google Go Style Guide` (https://google.github.io/styleguide/go/).
|
38
|
+
- Enforce gofmt/goimports cleanliness, make sure go vet and staticcheck would be happy, and flag any missing `//nolint` justifications.
|
39
|
+
- You are the guardian of SOLID, DRY, YAGNI, and the Zen of Python (yes, even here). Call out violations with precision.
|
40
|
+
|
41
|
+
Per Go file that actually matters:
|
42
|
+
1. Give a breezy high-level summary of what changed. No snooze-fests or line-by-line bedtime stories.
|
43
|
+
2. Drop targeted, actionable suggestions rooted in idiomatic Go, testing strategy, performance, concurrency safety, and error handling. No fluff or nitpicks unless they break principles.
|
44
|
+
3. Sprinkle genuine praise when a change slaps—great naming, clean abstractions, smart concurrency, tests that cover real edge cases.
|
45
|
+
|
46
|
+
Review etiquette:
|
47
|
+
- Stay concise, organized, and focused on impact. Group similar findings so the reader doesn’t chase their tail.
|
48
|
+
- Flag missing tests or weak coverage when it matters. Suggest concrete test names or scenarios.
|
49
|
+
- Prefer positive phrasing: "Consider" beats "Don’t". We’re a nice puppy, just ridiculously picky.
|
50
|
+
- If everything looks barking good, say so explicitly and call out strengths.
|
51
|
+
- Always mention residual risks or assumptions you made when you can’t fully verify something.
|
52
|
+
|
53
|
+
Output format (per file with real changes):
|
54
|
+
- File header like `file.go:123` when referencing issues. Avoid line ranges.
|
55
|
+
- Use bullet points for findings and kudos. Severity order: blockers first, then warnings, then nits, then praise.
|
56
|
+
- Close with overall verdict if multiple files: "Ship it", "Needs fixes", or "Mixed bag", plus a short rationale.
|
57
|
+
|
58
|
+
You are the Golang review persona for this CLI pack. Be sassy, precise, and wildly helpful.
|
59
|
+
- When concurrency primitives show up, double-check for race hazards, context cancellation, and proper error propagation.
|
60
|
+
- If performance or allocation pressure might bite, call it out and suggest profiling or benchmarks.
|
61
|
+
"""
|
code_puppy/agents/base_agent.py
CHANGED
@@ -829,15 +829,15 @@ class BaseAgent(ABC):
|
|
829
829
|
|
830
830
|
async def run_agent_task():
|
831
831
|
try:
|
832
|
+
self.set_message_history(
|
833
|
+
self.prune_interrupted_tool_calls(self.get_message_history())
|
834
|
+
)
|
832
835
|
result_ = await pydantic_agent.run(
|
833
836
|
prompt,
|
834
837
|
message_history=self.get_message_history(),
|
835
838
|
usage_limits=usage_limits,
|
836
839
|
**kwargs,
|
837
840
|
)
|
838
|
-
self.set_message_history(
|
839
|
-
self.prune_interrupted_tool_calls(self.get_message_history())
|
840
|
-
)
|
841
841
|
return result_
|
842
842
|
except* UsageLimitExceeded as ule:
|
843
843
|
emit_info(f"Usage limit exceeded: {str(ule)}", group_id=group_id)
|
@@ -883,10 +883,10 @@ class BaseAgent(ABC):
|
|
883
883
|
cancelled_exceptions.append(exc)
|
884
884
|
|
885
885
|
collect_cancelled_exceptions(other_error)
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
886
|
+
finally:
|
887
|
+
self.set_message_history(
|
888
|
+
self.prune_interrupted_tool_calls(self.get_message_history())
|
889
|
+
)
|
890
890
|
|
891
891
|
# Create the task FIRST
|
892
892
|
agent_task = asyncio.create_task(run_agent_task())
|
@@ -909,7 +909,6 @@ class BaseAgent(ABC):
|
|
909
909
|
agent_task.cancel()
|
910
910
|
except Exception as e:
|
911
911
|
emit_info(f"Shell kill error: {e}")
|
912
|
-
# If shell kill failed, still try to cancel the agent task
|
913
912
|
if not agent_task.done():
|
914
913
|
agent_task.cancel()
|
915
914
|
# Don't call the original handler
|
@@ -923,8 +922,7 @@ class BaseAgent(ABC):
|
|
923
922
|
result = await agent_task
|
924
923
|
return result
|
925
924
|
except asyncio.CancelledError:
|
926
|
-
|
927
|
-
raise
|
925
|
+
agent_task.cancel()
|
928
926
|
except KeyboardInterrupt:
|
929
927
|
# Handle direct keyboard interrupt during await
|
930
928
|
if not agent_task.done():
|
@@ -933,7 +931,6 @@ class BaseAgent(ABC):
|
|
933
931
|
await agent_task
|
934
932
|
except asyncio.CancelledError:
|
935
933
|
pass
|
936
|
-
raise asyncio.CancelledError()
|
937
934
|
finally:
|
938
935
|
# Restore original signal handler
|
939
936
|
if original_handler:
|
code_puppy/main.py
CHANGED
@@ -383,6 +383,8 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
383
383
|
if task.strip().lower() in ("clear", "/clear"):
|
384
384
|
from code_puppy.messaging import emit_system_message, emit_warning
|
385
385
|
|
386
|
+
agent = get_current_agent()
|
387
|
+
agent.clear_message_history()
|
386
388
|
emit_warning("Conversation history cleared!")
|
387
389
|
emit_system_message("The agent will not remember previous interactions.\n")
|
388
390
|
continue
|
code_puppy/tui/app.py
CHANGED
@@ -515,6 +515,8 @@ class CodePuppyTUI(App):
|
|
515
515
|
"""Clear the chat history."""
|
516
516
|
chat_view = self.query_one("#chat-view", ChatView)
|
517
517
|
chat_view.clear_messages()
|
518
|
+
agent = get_current_agent()
|
519
|
+
agent.clear_message_history()
|
518
520
|
self.add_system_message("Chat history cleared")
|
519
521
|
|
520
522
|
def action_show_help(self) -> None:
|
@@ -3,7 +3,7 @@ code_puppy/__main__.py,sha256=pDVssJOWP8A83iFkxMLY9YteHYat0EyWDQqMkKHpWp4,203
|
|
3
3
|
code_puppy/callbacks.py,sha256=6wYB6K_fGSCkKKEFaYOYkJT45WaV5W_NhUIzcvVH_nU,5060
|
4
4
|
code_puppy/config.py,sha256=smjGUs18VJQxPeVLvV8U2T_YuGh7E_Sg5ZgaXhdBWWg,20677
|
5
5
|
code_puppy/http_utils.py,sha256=YLd8Y16idbI32JGeBXG8n5rT4o4X_zxk9FgUvK9XFo8,8248
|
6
|
-
code_puppy/main.py,sha256=
|
6
|
+
code_puppy/main.py,sha256=_TvwZFFepyq6UCC29ApFUeXeODsRHV2OyFuzM3xkiwo,20601
|
7
7
|
code_puppy/model_factory.py,sha256=KLyxUrZJual6uo_TncjdPhdDbvIvHiztWvK9bfdQmmc,13759
|
8
8
|
code_puppy/models.json,sha256=iXmLZGflnQcu2DRh4WUlgAhoXdvoxUc7KBhB8YxawXM,3088
|
9
9
|
code_puppy/reopenable_async_client.py,sha256=4UJRaMp5np8cbef9F0zKQ7TPKOfyf5U-Kv-0zYUWDho,8274
|
@@ -15,9 +15,10 @@ code_puppy/version_checker.py,sha256=bjLDmgGPrl7XnYwX1u13O8uFlsfikV90PK6nbA9Z9QU
|
|
15
15
|
code_puppy/agents/__init__.py,sha256=PtPB7Z5MSwmUKipgt_qxvIuGggcuVaYwNbnp1UP4tPc,518
|
16
16
|
code_puppy/agents/agent_code_puppy.py,sha256=vk25J5YSjt30ItfML8KyQc5QR8QumBv5_U8RhqaXUz4,7973
|
17
17
|
code_puppy/agents/agent_creator_agent.py,sha256=IiwVirB6uoIeGOmtetut9eDv6o055ykND3V-fvyA8Lw,23042
|
18
|
+
code_puppy/agents/agent_golang_reviewer.py,sha256=cdJEK0J5oZEIW9Sw5Hj_FiVP0iN-_NoAnjP4b6xB_Rw,3308
|
18
19
|
code_puppy/agents/agent_manager.py,sha256=USzTbsGGZ9CH17LFeb6P6Im8RPY8qZUGKoH9EZZyU3k,11413
|
19
20
|
code_puppy/agents/agent_qa_kitten.py,sha256=5PeFFSwCFlTUvP6h5bGntx0xv5NmRwBiw0HnMqY8nLI,9107
|
20
|
-
code_puppy/agents/base_agent.py,sha256=
|
21
|
+
code_puppy/agents/base_agent.py,sha256=MFyC4YSxlQ1xzQRBnSXgtyO7XPpHZuSJVzRATU8j7J0,36992
|
21
22
|
code_puppy/agents/json_agent.py,sha256=y6AYE3Fx9LhmemcPzt46d7359MNnkGIjU83YBGNer2g,4533
|
22
23
|
code_puppy/command_line/__init__.py,sha256=y7WeRemfYppk8KVbCGeAIiTuiOszIURCDjOMZv_YRmU,45
|
23
24
|
code_puppy/command_line/command_handler.py,sha256=JeJo6kc9-4B7S0_pbMp1LO7jtAT6XMXtSSnOShfoG-Y,27237
|
@@ -74,13 +75,6 @@ code_puppy/messaging/spinner/textual_spinner.py,sha256=Omx9A-FSPkxYDMYgBXgYMBQnK
|
|
74
75
|
code_puppy/plugins/__init__.py,sha256=fksDqMUiXPJ5WNuMsYsVR8ulueQRCXPlvECEyicHPtQ,1312
|
75
76
|
code_puppy/tools/__init__.py,sha256=BVTZ85jLHgDANwOnUSOz3UDlp8VQDq4DoGF23BRlyWw,6032
|
76
77
|
code_puppy/tools/agent_tools.py,sha256=VMTKPfNUiK90ifyJUtEvUpOHmmifpWCxDKv7j6oTaxI,5620
|
77
|
-
code_puppy/tools/browser_control.py,sha256=6E_Kq63cErBk-iM9-03Cq8sTWWLh-Tk2kpdrB_rMmbg,8603
|
78
|
-
code_puppy/tools/browser_interactions.py,sha256=m-bVsAUbdrS-P1GbS0ChO2tMMg4tMIsDX34azn_Olzs,17070
|
79
|
-
code_puppy/tools/browser_locators.py,sha256=ZshjhYuV600vJqXUC23oNX89AZLMvY8rlvj-9oy7GMw,19574
|
80
|
-
code_puppy/tools/browser_navigation.py,sha256=Tj_fNcM3KGpkM2UTKcGQX8BpI373Sv7xZAJf-U7pO5M,7621
|
81
|
-
code_puppy/tools/browser_screenshot.py,sha256=QSwxS37G4LSo-Q9SBiuIofxWKnyInM90TY-_fiWQLrs,9222
|
82
|
-
code_puppy/tools/browser_scripts.py,sha256=MMO5KRjdrhuLOoJGoKGG1jm6UAqhFhUznz02aWqhMAE,15065
|
83
|
-
code_puppy/tools/browser_workflows.py,sha256=SJfiM2SgZr-qeeQJEivGzUNc51AlLIoqj9h1-mTvVsA,6593
|
84
78
|
code_puppy/tools/command_runner.py,sha256=SRhTACgKklHUm_ztbFHnefGbLw4CJOm601or6L1cPGM,22417
|
85
79
|
code_puppy/tools/common.py,sha256=pL-9xcRs3rxU7Fl9X9EUgbDp2-csh2LLJ5DHH_KAHKY,10596
|
86
80
|
code_puppy/tools/file_modifications.py,sha256=EaDWcv6gi8wAvpgyeJdKSKPWg9fTpZoEkxQiLCE6rn4,23218
|
@@ -97,7 +91,7 @@ code_puppy/tools/browser/browser_workflows.py,sha256=jplJ1T60W3G4-dhVJX-CXkm9ssk
|
|
97
91
|
code_puppy/tools/browser/camoufox_manager.py,sha256=RYvLcs0iAoVNtpLjrrA1uu6a5k9tAdBbmhWFGSWjX_A,6106
|
98
92
|
code_puppy/tools/browser/vqa_agent.py,sha256=0GMDgJAK728rIuSQxAVytFSNagjo0LCjCUxBTm3w9Po,1952
|
99
93
|
code_puppy/tui/__init__.py,sha256=XesAxIn32zLPOmvpR2wIDxDAnnJr81a5pBJB4cZp1Xs,321
|
100
|
-
code_puppy/tui/app.py,sha256=
|
94
|
+
code_puppy/tui/app.py,sha256=fiOKrhtYBpruUPM-aqnsDmEiOM_dCbD5DnBRKO219NI,37985
|
101
95
|
code_puppy/tui/messages.py,sha256=zQoToWI0eWdT36NEsY6RdCFzcDfAmfvoPlHv8jiCbgo,720
|
102
96
|
code_puppy/tui/components/__init__.py,sha256=uj5pnk3s6SEN3SbFI0ZnzaA2KK1NNg8TfUj6U-Z732U,455
|
103
97
|
code_puppy/tui/components/chat_view.py,sha256=Ff6uM6J0yENISNAOYroX7F-JL73_ajUUcP5IZSf2mng,19914
|
@@ -117,9 +111,9 @@ code_puppy/tui/screens/help.py,sha256=eJuPaOOCp7ZSUlecearqsuX6caxWv7NQszUh0tZJjB
|
|
117
111
|
code_puppy/tui/screens/mcp_install_wizard.py,sha256=vObpQwLbXjQsxmSg-WCasoev1usEi0pollKnL0SHu9U,27693
|
118
112
|
code_puppy/tui/screens/settings.py,sha256=-WLldnKyWVKUYVPJcfOn1UU6eP9t8lLPUAVI317SOOM,10685
|
119
113
|
code_puppy/tui/screens/tools.py,sha256=3pr2Xkpa9Js6Yhf1A3_wQVRzFOui-KDB82LwrsdBtyk,1715
|
120
|
-
code_puppy-0.0.
|
121
|
-
code_puppy-0.0.
|
122
|
-
code_puppy-0.0.
|
123
|
-
code_puppy-0.0.
|
124
|
-
code_puppy-0.0.
|
125
|
-
code_puppy-0.0.
|
114
|
+
code_puppy-0.0.179.data/data/code_puppy/models.json,sha256=iXmLZGflnQcu2DRh4WUlgAhoXdvoxUc7KBhB8YxawXM,3088
|
115
|
+
code_puppy-0.0.179.dist-info/METADATA,sha256=Ym8ldyh8Omkh0_2kcSDcdDfHRPEZM8ypNVUE_nRN47s,20079
|
116
|
+
code_puppy-0.0.179.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
117
|
+
code_puppy-0.0.179.dist-info/entry_points.txt,sha256=Tp4eQC99WY3HOKd3sdvb22vZODRq0XkZVNpXOag_KdI,91
|
118
|
+
code_puppy-0.0.179.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
|
119
|
+
code_puppy-0.0.179.dist-info/RECORD,,
|
@@ -1,293 +0,0 @@
|
|
1
|
-
"""Browser initialization and control tools."""
|
2
|
-
|
3
|
-
from typing import Any, Dict, Optional
|
4
|
-
|
5
|
-
from pydantic_ai import RunContext
|
6
|
-
|
7
|
-
from code_puppy.messaging import emit_info
|
8
|
-
from code_puppy.tools.common import generate_group_id
|
9
|
-
|
10
|
-
from .camoufox_manager import get_camoufox_manager
|
11
|
-
|
12
|
-
|
13
|
-
async def initialize_browser(
|
14
|
-
headless: bool = False,
|
15
|
-
browser_type: str = "chromium",
|
16
|
-
homepage: str = "https://www.google.com",
|
17
|
-
) -> Dict[str, Any]:
|
18
|
-
"""Initialize the browser with specified settings."""
|
19
|
-
group_id = generate_group_id("browser_initialize", f"{browser_type}_{homepage}")
|
20
|
-
emit_info(
|
21
|
-
f"[bold white on blue] BROWSER INITIALIZE [/bold white on blue] 🌐 {browser_type} → {homepage}",
|
22
|
-
message_group=group_id,
|
23
|
-
)
|
24
|
-
try:
|
25
|
-
browser_manager = get_camoufox_manager()
|
26
|
-
|
27
|
-
# Configure browser settings
|
28
|
-
browser_manager.headless = headless
|
29
|
-
browser_manager.browser_type = browser_type
|
30
|
-
browser_manager.homepage = homepage
|
31
|
-
|
32
|
-
# Initialize browser
|
33
|
-
await browser_manager.async_initialize()
|
34
|
-
|
35
|
-
# Get page info
|
36
|
-
page = await browser_manager.get_current_page()
|
37
|
-
if page:
|
38
|
-
url = page.url
|
39
|
-
title = await page.title()
|
40
|
-
else:
|
41
|
-
url = "Unknown"
|
42
|
-
title = "Unknown"
|
43
|
-
|
44
|
-
emit_info(
|
45
|
-
"[green]Browser initialized successfully[/green]", message_group=group_id
|
46
|
-
)
|
47
|
-
|
48
|
-
return {
|
49
|
-
"success": True,
|
50
|
-
"browser_type": browser_type,
|
51
|
-
"headless": headless,
|
52
|
-
"homepage": homepage,
|
53
|
-
"current_url": url,
|
54
|
-
"current_title": title,
|
55
|
-
}
|
56
|
-
|
57
|
-
except Exception as e:
|
58
|
-
emit_info(
|
59
|
-
f"[red]Browser initialization failed: {str(e)}[/red]",
|
60
|
-
message_group=group_id,
|
61
|
-
)
|
62
|
-
return {
|
63
|
-
"success": False,
|
64
|
-
"error": str(e),
|
65
|
-
"browser_type": browser_type,
|
66
|
-
"headless": headless,
|
67
|
-
}
|
68
|
-
|
69
|
-
|
70
|
-
async def close_browser() -> Dict[str, Any]:
|
71
|
-
"""Close the browser and clean up resources."""
|
72
|
-
group_id = generate_group_id("browser_close")
|
73
|
-
emit_info(
|
74
|
-
"[bold white on blue] BROWSER CLOSE [/bold white on blue] 🔒",
|
75
|
-
message_group=group_id,
|
76
|
-
)
|
77
|
-
try:
|
78
|
-
browser_manager = get_camoufox_manager()
|
79
|
-
await browser_manager.close()
|
80
|
-
|
81
|
-
emit_info(
|
82
|
-
"[yellow]Browser closed successfully[/yellow]", message_group=group_id
|
83
|
-
)
|
84
|
-
|
85
|
-
return {"success": True, "message": "Browser closed"}
|
86
|
-
|
87
|
-
except Exception as e:
|
88
|
-
return {"success": False, "error": str(e)}
|
89
|
-
|
90
|
-
|
91
|
-
async def get_browser_status() -> Dict[str, Any]:
|
92
|
-
"""Get current browser status and information."""
|
93
|
-
group_id = generate_group_id("browser_status")
|
94
|
-
emit_info(
|
95
|
-
"[bold white on blue] BROWSER STATUS [/bold white on blue] 📊",
|
96
|
-
message_group=group_id,
|
97
|
-
)
|
98
|
-
try:
|
99
|
-
browser_manager = get_camoufox_manager()
|
100
|
-
|
101
|
-
if not browser_manager._initialized:
|
102
|
-
return {
|
103
|
-
"success": True,
|
104
|
-
"status": "not_initialized",
|
105
|
-
"browser_type": browser_manager.browser_type,
|
106
|
-
"headless": browser_manager.headless,
|
107
|
-
}
|
108
|
-
|
109
|
-
page = await browser_manager.get_current_page()
|
110
|
-
if page:
|
111
|
-
url = page.url
|
112
|
-
title = await page.title()
|
113
|
-
|
114
|
-
# Get all pages
|
115
|
-
all_pages = await browser_manager.get_all_pages()
|
116
|
-
page_count = len(all_pages)
|
117
|
-
else:
|
118
|
-
url = None
|
119
|
-
title = None
|
120
|
-
page_count = 0
|
121
|
-
|
122
|
-
return {
|
123
|
-
"success": True,
|
124
|
-
"status": "initialized",
|
125
|
-
"browser_type": browser_manager.browser_type,
|
126
|
-
"headless": browser_manager.headless,
|
127
|
-
"current_url": url,
|
128
|
-
"current_title": title,
|
129
|
-
"page_count": page_count,
|
130
|
-
}
|
131
|
-
|
132
|
-
except Exception as e:
|
133
|
-
return {"success": False, "error": str(e)}
|
134
|
-
|
135
|
-
|
136
|
-
async def create_new_page(url: Optional[str] = None) -> Dict[str, Any]:
|
137
|
-
"""Create a new browser page/tab."""
|
138
|
-
group_id = generate_group_id("browser_new_page", url or "blank")
|
139
|
-
emit_info(
|
140
|
-
f"[bold white on blue] BROWSER NEW PAGE [/bold white on blue] 📄 {url or 'blank page'}",
|
141
|
-
message_group=group_id,
|
142
|
-
)
|
143
|
-
try:
|
144
|
-
browser_manager = get_camoufox_manager()
|
145
|
-
|
146
|
-
if not browser_manager._initialized:
|
147
|
-
return {
|
148
|
-
"success": False,
|
149
|
-
"error": "Browser not initialized. Use browser_initialize first.",
|
150
|
-
}
|
151
|
-
|
152
|
-
page = await browser_manager.new_page(url)
|
153
|
-
|
154
|
-
final_url = page.url
|
155
|
-
title = await page.title()
|
156
|
-
|
157
|
-
emit_info(
|
158
|
-
f"[green]Created new page: {final_url}[/green]", message_group=group_id
|
159
|
-
)
|
160
|
-
|
161
|
-
return {"success": True, "url": final_url, "title": title, "requested_url": url}
|
162
|
-
|
163
|
-
except Exception as e:
|
164
|
-
return {"success": False, "error": str(e), "url": url}
|
165
|
-
|
166
|
-
|
167
|
-
async def list_pages() -> Dict[str, Any]:
|
168
|
-
"""List all open browser pages/tabs."""
|
169
|
-
group_id = generate_group_id("browser_list_pages")
|
170
|
-
emit_info(
|
171
|
-
"[bold white on blue] BROWSER LIST PAGES [/bold white on blue] 📋",
|
172
|
-
message_group=group_id,
|
173
|
-
)
|
174
|
-
try:
|
175
|
-
browser_manager = get_camoufox_manager()
|
176
|
-
|
177
|
-
if not browser_manager._initialized:
|
178
|
-
return {"success": False, "error": "Browser not initialized"}
|
179
|
-
|
180
|
-
all_pages = await browser_manager.get_all_pages()
|
181
|
-
|
182
|
-
pages_info = []
|
183
|
-
for i, page in enumerate(all_pages):
|
184
|
-
try:
|
185
|
-
url = page.url
|
186
|
-
title = await page.title()
|
187
|
-
is_closed = page.is_closed()
|
188
|
-
|
189
|
-
pages_info.append(
|
190
|
-
{"index": i, "url": url, "title": title, "closed": is_closed}
|
191
|
-
)
|
192
|
-
except Exception as e:
|
193
|
-
pages_info.append(
|
194
|
-
{
|
195
|
-
"index": i,
|
196
|
-
"url": "Error",
|
197
|
-
"title": "Error",
|
198
|
-
"error": str(e),
|
199
|
-
"closed": True,
|
200
|
-
}
|
201
|
-
)
|
202
|
-
|
203
|
-
return {"success": True, "page_count": len(all_pages), "pages": pages_info}
|
204
|
-
|
205
|
-
except Exception as e:
|
206
|
-
return {"success": False, "error": str(e)}
|
207
|
-
|
208
|
-
|
209
|
-
# Tool registration functions
|
210
|
-
def register_initialize_browser(agent):
|
211
|
-
"""Register the browser initialization tool."""
|
212
|
-
|
213
|
-
@agent.tool
|
214
|
-
async def browser_initialize(
|
215
|
-
context: RunContext,
|
216
|
-
headless: bool = False,
|
217
|
-
browser_type: str = "chromium",
|
218
|
-
homepage: str = "https://www.google.com",
|
219
|
-
) -> Dict[str, Any]:
|
220
|
-
"""
|
221
|
-
Initialize the browser with specified settings. Must be called before using other browser tools.
|
222
|
-
|
223
|
-
Args:
|
224
|
-
headless: Run browser in headless mode (no GUI)
|
225
|
-
browser_type: Browser engine (chromium, firefox, webkit)
|
226
|
-
homepage: Initial page to load
|
227
|
-
|
228
|
-
Returns:
|
229
|
-
Dict with initialization results
|
230
|
-
"""
|
231
|
-
return await initialize_browser(headless, browser_type, homepage)
|
232
|
-
|
233
|
-
|
234
|
-
def register_close_browser(agent):
|
235
|
-
"""Register the browser close tool."""
|
236
|
-
|
237
|
-
@agent.tool
|
238
|
-
async def browser_close(context: RunContext) -> Dict[str, Any]:
|
239
|
-
"""
|
240
|
-
Close the browser and clean up all resources.
|
241
|
-
|
242
|
-
Returns:
|
243
|
-
Dict with close results
|
244
|
-
"""
|
245
|
-
return await close_browser()
|
246
|
-
|
247
|
-
|
248
|
-
def register_get_browser_status(agent):
|
249
|
-
"""Register the browser status tool."""
|
250
|
-
|
251
|
-
@agent.tool
|
252
|
-
async def browser_status(context: RunContext) -> Dict[str, Any]:
|
253
|
-
"""
|
254
|
-
Get current browser status and information.
|
255
|
-
|
256
|
-
Returns:
|
257
|
-
Dict with browser status and metadata
|
258
|
-
"""
|
259
|
-
return await get_browser_status()
|
260
|
-
|
261
|
-
|
262
|
-
def register_create_new_page(agent):
|
263
|
-
"""Register the new page creation tool."""
|
264
|
-
|
265
|
-
@agent.tool
|
266
|
-
async def browser_new_page(
|
267
|
-
context: RunContext,
|
268
|
-
url: Optional[str] = None,
|
269
|
-
) -> Dict[str, Any]:
|
270
|
-
"""
|
271
|
-
Create a new browser page/tab.
|
272
|
-
|
273
|
-
Args:
|
274
|
-
url: Optional URL to navigate to in the new page
|
275
|
-
|
276
|
-
Returns:
|
277
|
-
Dict with new page results
|
278
|
-
"""
|
279
|
-
return await create_new_page(url)
|
280
|
-
|
281
|
-
|
282
|
-
def register_list_pages(agent):
|
283
|
-
"""Register the list pages tool."""
|
284
|
-
|
285
|
-
@agent.tool
|
286
|
-
async def browser_list_pages(context: RunContext) -> Dict[str, Any]:
|
287
|
-
"""
|
288
|
-
List all open browser pages/tabs.
|
289
|
-
|
290
|
-
Returns:
|
291
|
-
Dict with information about all open pages
|
292
|
-
"""
|
293
|
-
return await list_pages()
|