navcli 0.1.0__tar.gz → 0.2.1__tar.gz

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 (49) hide show
  1. navcli-0.2.1/PKG-INFO +81 -0
  2. navcli-0.2.1/README.md +180 -0
  3. navcli-0.2.1/docs/README.md +48 -0
  4. {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/app.py +4 -0
  5. {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/client.py +34 -8
  6. {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/commands/control.py +16 -16
  7. {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/commands/explore.py +6 -6
  8. {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/commands/interaction.py +6 -6
  9. {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/commands/navigation.py +31 -9
  10. {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/commands/query.py +54 -10
  11. {navcli-0.1.0 → navcli-0.2.1}/navcli/core/models/__init__.py +2 -1
  12. {navcli-0.1.0 → navcli-0.2.1}/navcli/core/models/state.py +14 -3
  13. navcli-0.2.1/navcli/server/__init__.py +128 -0
  14. {navcli-0.1.0 → navcli-0.2.1}/navcli/server/browser.py +61 -22
  15. {navcli-0.1.0 → navcli-0.2.1}/navcli/server/routes/explore.py +1 -1
  16. {navcli-0.1.0 → navcli-0.2.1}/navcli/server/routes/query.py +166 -7
  17. {navcli-0.1.0 → navcli-0.2.1}/navcli/server/routes/session.py +38 -9
  18. navcli-0.2.1/navcli.egg-info/PKG-INFO +81 -0
  19. {navcli-0.1.0 → navcli-0.2.1}/navcli.egg-info/SOURCES.txt +1 -0
  20. {navcli-0.1.0 → navcli-0.2.1}/pyproject.toml +1 -1
  21. navcli-0.1.0/PKG-INFO +0 -79
  22. navcli-0.1.0/docs/README.md +0 -46
  23. navcli-0.1.0/navcli/server/__init__.py +0 -86
  24. navcli-0.1.0/navcli.egg-info/PKG-INFO +0 -79
  25. {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/__init__.py +0 -0
  26. {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/commands/__init__.py +0 -0
  27. {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/commands/base.py +0 -0
  28. {navcli-0.1.0 → navcli-0.2.1}/navcli/core/__init__.py +0 -0
  29. {navcli-0.1.0 → navcli-0.2.1}/navcli/core/models/dom.py +0 -0
  30. {navcli-0.1.0 → navcli-0.2.1}/navcli/core/models/element.py +0 -0
  31. {navcli-0.1.0 → navcli-0.2.1}/navcli/core/models/feedback.py +0 -0
  32. {navcli-0.1.0 → navcli-0.2.1}/navcli/server/app.py +0 -0
  33. {navcli-0.1.0 → navcli-0.2.1}/navcli/server/middleware/__init__.py +0 -0
  34. {navcli-0.1.0 → navcli-0.2.1}/navcli/server/routes/__init__.py +0 -0
  35. {navcli-0.1.0 → navcli-0.2.1}/navcli/server/routes/control.py +0 -0
  36. {navcli-0.1.0 → navcli-0.2.1}/navcli/server/routes/interaction.py +0 -0
  37. {navcli-0.1.0 → navcli-0.2.1}/navcli/server/routes/navigation.py +0 -0
  38. {navcli-0.1.0 → navcli-0.2.1}/navcli/utils/__init__.py +0 -0
  39. {navcli-0.1.0 → navcli-0.2.1}/navcli/utils/image.py +0 -0
  40. {navcli-0.1.0 → navcli-0.2.1}/navcli/utils/js.py +0 -0
  41. {navcli-0.1.0 → navcli-0.2.1}/navcli/utils/selector.py +0 -0
  42. {navcli-0.1.0 → navcli-0.2.1}/navcli/utils/text.py +0 -0
  43. {navcli-0.1.0 → navcli-0.2.1}/navcli/utils/time.py +0 -0
  44. {navcli-0.1.0 → navcli-0.2.1}/navcli/utils/url.py +0 -0
  45. {navcli-0.1.0 → navcli-0.2.1}/navcli.egg-info/dependency_links.txt +0 -0
  46. {navcli-0.1.0 → navcli-0.2.1}/navcli.egg-info/entry_points.txt +0 -0
  47. {navcli-0.1.0 → navcli-0.2.1}/navcli.egg-info/requires.txt +0 -0
  48. {navcli-0.1.0 → navcli-0.2.1}/navcli.egg-info/top_level.txt +0 -0
  49. {navcli-0.1.0 → navcli-0.2.1}/setup.cfg +0 -0
navcli-0.2.1/PKG-INFO ADDED
@@ -0,0 +1,81 @@
1
+ Metadata-Version: 2.4
2
+ Name: navcli
3
+ Version: 0.2.1
4
+ Summary: 可交互、可探索的浏览器命令行工具,专为 AI Agent 设计
5
+ Author: NavCLI Team
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/navcli/navcli
8
+ Project-URL: Repository, https://github.com/navcli/navcli
9
+ Project-URL: Issues, https://github.com/navcli/navcli/issues
10
+ Keywords: browser,cli,automation,ai-agent,playwright
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: playwright>=1.40.0
22
+ Requires-Dist: cmd2>=2.4.0
23
+ Requires-Dist: fastapi>=0.109.0
24
+ Requires-Dist: uvicorn<1.0.0,>=0.25.0
25
+ Requires-Dist: pydantic>=2.5.0
26
+ Requires-Dist: cssify>=1.0.0
27
+ Requires-Dist: aiohttp>=3.9.0
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=7.4.0; extra == "dev"
30
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
31
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
32
+ Requires-Dist: mypy>=1.8.0; extra == "dev"
33
+
34
+ # NavCLI - Goals & Vision
35
+
36
+ ## Core Goal
37
+
38
+ **Enable AI Agents to browse the web like humans.**
39
+
40
+ Existing solutions (HTTP APIs, headless browser scripts, Playwright MCP) have limitations:
41
+ - No support for JS-rendered SPAs
42
+ - No session persistence
43
+ - Lack of interactive exploration
44
+
45
+ NavCLI's positioning: **An interactive, explorable browser CLI**
46
+
47
+ ## Core Value
48
+
49
+ | Feature | What NavCLI Solves |
50
+ |---------|-------------------|
51
+ | JS Rendering | Full SPA support |
52
+ | Session Persistence | Cookies, session maintained |
53
+ | Interactive CLI | Agent can explore while operating |
54
+ | Token Optimization | Lightweight elements + on-demand text/html |
55
+
56
+ ## Typical Workflow
57
+
58
+ ```bash
59
+ > g https://example.com # Navigate
60
+ > elements # Observe interactive elements
61
+ > c .btn-login # Click login
62
+ > t #email "test@example.com" # Type email
63
+ > t #password "123456" # Type password
64
+ > c button[type="submit"] # Submit
65
+ > text # Confirm result
66
+ ```
67
+
68
+ Agent can: **Navigate → Observe → Interact → Feedback → Continue**
69
+
70
+ ## Vision
71
+
72
+ Become the **standard browser interaction layer** for AI Agents, enabling any Agent to control browsers via command-line interface:
73
+ - Form filling, login authentication
74
+ - Information scraping, content exploration
75
+ - Complex multi-step business processes
76
+
77
+ ## Related Documentation
78
+
79
+ - [Skill File Download](https://github.com/wumu2013/navcli/blob/main/docs/skill.md)
80
+ - [Project Homepage](https://make.datavoid.fun/navcli/)
81
+ - [PRD Product Requirements](./NAVCLI_PRD.md)
navcli-0.2.1/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # NavCLI - Browser Automation CLI for AI Agents
2
+
3
+ [![PyPI Version](https://img.shields.io/pypi/v/navcli)](https://pypi.org/project/navcli/)
4
+ [![GitHub](https://img.shields.io/badge/GitHub-wumu2013/navcli-blue)](https://github.com/wumu2013/navcli)
5
+
6
+ **Enable AI Agents to browse the web like humans.**
7
+
8
+ ## What is NavCLI?
9
+
10
+ NavCLI is an interactive, explorable browser CLI designed for AI Agents. Unlike HTTP APIs or headless browser scripts, NavCLI provides:
11
+
12
+ - **Full SPA Support** - JavaScript-rendered pages work seamlessly
13
+ - **Session Persistence** - Cookies and login sessions maintained across requests
14
+ - **Interactive CLI** - Agents can explore and interact dynamically
15
+ - **Token Optimization** - Lightweight element queries with on-demand content
16
+
17
+ ## Quick Start
18
+
19
+ ```bash
20
+ pip install navcli
21
+ ```
22
+
23
+ ### Start Server
24
+
25
+ ```bash
26
+ nav-server
27
+ ```
28
+
29
+ ### Use CLI
30
+
31
+ ```bash
32
+ nav
33
+ ```
34
+
35
+ Or run commands directly:
36
+
37
+ ```bash
38
+ nav g https://example.com
39
+ nav tables
40
+ nav paragraphs
41
+ ```
42
+
43
+ ## Data Extraction
44
+
45
+ > **IMPORTANT**: For data extraction, focus on `tables` and `paragraphs`.
46
+
47
+ | Command | Description |
48
+ |---------|-------------|
49
+ | `nav tables` | Extract all `<table>` elements with structured data |
50
+ | `nav paragraphs` | Extract large text paragraphs (200+ chars) |
51
+ | `nav g <url>` | Navigate + auto-fetch paragraphs on first call |
52
+
53
+ ## Core Commands
54
+
55
+ ### Navigation
56
+
57
+ ```bash
58
+ nav g <url> # Navigate to URL
59
+ nav b # Go back
60
+ nav f # Go forward
61
+ nav r # Reload
62
+ ```
63
+
64
+ ### Interaction
65
+
66
+ ```bash
67
+ nav c <sel> [--force] # Click element
68
+ nav t <sel> <txt> # Type text
69
+ nav clear <sel> # Clear input
70
+ nav upload <sel> <path> # Upload file
71
+ ```
72
+
73
+ ### Query
74
+
75
+ ```bash
76
+ nav elements # Interactive elements
77
+ nav text # Page text content
78
+ nav html # Page HTML
79
+ nav screenshot # Screenshot (base64)
80
+ nav state # Full page state
81
+ nav url # Current URL
82
+ nav title # Page title
83
+ nav tables # All tables with data
84
+ nav paragraphs [len] # Large text paragraphs
85
+ nav links # All hyperlinks
86
+ nav forms # All forms
87
+ ```
88
+
89
+ ### Explore
90
+
91
+ ```bash
92
+ nav find <text> # Find element by text
93
+ nav findall <text> # Find all elements by text
94
+ nav inspect <sel> # Inspect element details
95
+ nav wait_idle [sec] # Wait for network idle
96
+ nav scroll top|bottom|up|down # Scroll page
97
+ ```
98
+
99
+ ### Session
100
+
101
+ ```bash
102
+ nav cookies # Show cookies
103
+ nav save_session [name] # Save session
104
+ nav load_session [name] # Load session
105
+ ```
106
+
107
+ ### Control
108
+
109
+ ```bash
110
+ nav quit # Close browser
111
+ nav exit # Exit CLI
112
+ ```
113
+
114
+ ## Agent Workflow
115
+
116
+ ```
117
+ Navigate → Observe → Interact → Feedback → Continue
118
+ ```
119
+
120
+ ```bash
121
+ # 1 Navigate - Go to target page
122
+ nav g https://example.com/data-page
123
+
124
+ # 2 Observe - Check what data is available
125
+ nav tables # Extract tabular data
126
+ nav paragraphs # Extract text content
127
+
128
+ # 3 Interact - Navigate/paginate if needed
129
+ nav find "Next"
130
+ nav c a.next-page
131
+ nav wait_idle
132
+
133
+ # 4 Feedback - Verify data extraction
134
+ nav tables
135
+ nav paragraphs
136
+
137
+ # 5 Continue - Proceed to next data source
138
+ nav g https://example.com/page2
139
+ ```
140
+
141
+ ## API
142
+
143
+ NavCLI uses a client-server architecture with HTTP API:
144
+
145
+ | Method | Endpoint | Description |
146
+ |--------|----------|-------------|
147
+ | POST | `/cmd/goto?url=<url>` | Navigate to URL |
148
+ | POST | `/cmd/click?selector=<s>` | Click element |
149
+ | GET | `/query/tables` | Get all tables |
150
+ | GET | `/query/paragraphs` | Get large text paragraphs |
151
+ | GET | `/query/elements` | Get interactive elements |
152
+ | POST | `/cmd/session/save?name=<n>` | Save session |
153
+ | POST | `/cmd/session/load?name=<n>` | Load session |
154
+
155
+ See [docs/skill.md](docs/skill.md) for complete API reference.
156
+
157
+ ## Session Persistence
158
+
159
+ Save login sessions for authenticated access:
160
+
161
+ ```bash
162
+ # Save after login
163
+ nav save_session jisilu
164
+
165
+ # Load session for future headless access
166
+ nav load_session jisilu
167
+ ```
168
+
169
+ Sessions are stored in `~/.navcli/sessions/`.
170
+
171
+ ## Documentation
172
+
173
+ - [CLI & API Reference](docs/skill.md)
174
+ - [Skill File Download](https://github.com/wumu2013/navcli/blob/main/docs/skill.md)
175
+ - [Project Homepage](https://make.datavoid.fun/navcli/)
176
+ - [Product Requirements](docs/NAVCLI_PRD.md)
177
+
178
+ ## License
179
+
180
+ MIT
@@ -0,0 +1,48 @@
1
+ # NavCLI - Goals & Vision
2
+
3
+ ## Core Goal
4
+
5
+ **Enable AI Agents to browse the web like humans.**
6
+
7
+ Existing solutions (HTTP APIs, headless browser scripts, Playwright MCP) have limitations:
8
+ - No support for JS-rendered SPAs
9
+ - No session persistence
10
+ - Lack of interactive exploration
11
+
12
+ NavCLI's positioning: **An interactive, explorable browser CLI**
13
+
14
+ ## Core Value
15
+
16
+ | Feature | What NavCLI Solves |
17
+ |---------|-------------------|
18
+ | JS Rendering | Full SPA support |
19
+ | Session Persistence | Cookies, session maintained |
20
+ | Interactive CLI | Agent can explore while operating |
21
+ | Token Optimization | Lightweight elements + on-demand text/html |
22
+
23
+ ## Typical Workflow
24
+
25
+ ```bash
26
+ > g https://example.com # Navigate
27
+ > elements # Observe interactive elements
28
+ > c .btn-login # Click login
29
+ > t #email "test@example.com" # Type email
30
+ > t #password "123456" # Type password
31
+ > c button[type="submit"] # Submit
32
+ > text # Confirm result
33
+ ```
34
+
35
+ Agent can: **Navigate → Observe → Interact → Feedback → Continue**
36
+
37
+ ## Vision
38
+
39
+ Become the **standard browser interaction layer** for AI Agents, enabling any Agent to control browsers via command-line interface:
40
+ - Form filling, login authentication
41
+ - Information scraping, content exploration
42
+ - Complex multi-step business processes
43
+
44
+ ## Related Documentation
45
+
46
+ - [Skill File Download](https://github.com/wumu2013/navcli/blob/main/docs/skill.md)
47
+ - [Project Homepage](https://make.datavoid.fun/navcli/)
48
+ - [PRD Product Requirements](./NAVCLI_PRD.md)
@@ -54,6 +54,10 @@ class NavCLI(
54
54
  self.poutput(" state Get full page state")
55
55
  self.poutput(" url Get current URL")
56
56
  self.poutput(" title Get page title")
57
+ self.poutput(" tables Get all tables with data")
58
+ self.poutput(" paragraphs Get large text paragraphs (200+ chars)")
59
+ self.poutput(" links Get all links")
60
+ self.poutput(" forms Get all forms")
57
61
 
58
62
  self.poutput("\nExplore Commands:")
59
63
  self.poutput(" find <text> Find element by text")
@@ -123,6 +123,18 @@ class NavClient:
123
123
  """Get all forms on the page."""
124
124
  return await self._request("GET", "/query/forms")
125
125
 
126
+ async def get_tables(self):
127
+ """Get all tables on the page with their data."""
128
+ return await self._request("GET", "/query/tables")
129
+
130
+ async def get_paragraphs(self, min_length: int = 200):
131
+ """Get large text paragraphs (200+ chars) from the page.
132
+
133
+ Args:
134
+ min_length: Minimum paragraph length (default: 200)
135
+ """
136
+ return await self._request("GET", f"/query/paragraphs?min_length={min_length}")
137
+
126
138
  # Explore commands
127
139
  async def find(self, text: str):
128
140
  """Find element by text."""
@@ -234,25 +246,39 @@ class NavClient:
234
246
 
235
247
  return await self._request("POST", "/cmd/cookies/set", json=body)
236
248
 
237
- async def save_session(self, path: str = ".navcli_session.json"):
249
+ async def save_session(self, name: str = None, path: str = None):
238
250
  """Save session to file.
239
251
 
240
252
  Args:
241
- path: File path to save session
253
+ name: Session name (saves to ~/.navcli/sessions/<name>.json)
254
+ path: Full file path (alternative to name)
242
255
  """
243
256
  import urllib.parse
244
- encoded_path = urllib.parse.quote(path)
245
- return await self._request("POST", f"/cmd/session/save?path={encoded_path}")
246
257
 
247
- async def load_session(self, path: str = ".navcli_session.json"):
258
+ if name:
259
+ return await self._request("POST", f"/cmd/session/save?name={name}")
260
+ elif path:
261
+ encoded_path = urllib.parse.quote(path)
262
+ return await self._request("POST", f"/cmd/session/save?path={encoded_path}")
263
+ else:
264
+ return await self._request("POST", "/cmd/session/save")
265
+
266
+ async def load_session(self, name: str = None, path: str = None):
248
267
  """Load session from file.
249
268
 
250
269
  Args:
251
- path: File path to load session from
270
+ name: Session name (loads from ~/.navcli/sessions/<name>.json)
271
+ path: Full file path (alternative to name)
252
272
  """
253
273
  import urllib.parse
254
- encoded_path = urllib.parse.quote(path)
255
- return await self._request("POST", f"/cmd/session/load?path={encoded_path}")
274
+
275
+ if name:
276
+ return await self._request("POST", f"/cmd/session/load?name={name}")
277
+ elif path:
278
+ encoded_path = urllib.parse.quote(path)
279
+ return await self._request("POST", f"/cmd/session/load?path={encoded_path}")
280
+ else:
281
+ return await self._request("POST", "/cmd/session/load")
256
282
 
257
283
 
258
284
  async def main():
@@ -21,7 +21,7 @@ class ControlCommands(BaseBrowserCommand):
21
21
  async def _quit():
22
22
  return await self.client.quit()
23
23
 
24
- result = asyncio.run(self.run_async_client(_quit))
24
+ result = asyncio.run(self.run_async_client(_quit()))
25
25
  self.pretty_result(result)
26
26
 
27
27
  @cmd2.with_argparser(cmd2.Cmd2ArgumentParser())
@@ -35,7 +35,7 @@ class ControlCommands(BaseBrowserCommand):
35
35
  async def _shutdown():
36
36
  return await self.client.shutdown()
37
37
 
38
- result = asyncio.run(self.run_async_client(_shutdown))
38
+ result = asyncio.run(self.run_async_client(_shutdown()))
39
39
  self.pretty_result(result)
40
40
 
41
41
  @cmd2.with_argparser(cmd2.Cmd2ArgumentParser())
@@ -50,41 +50,41 @@ class ControlCommands(BaseBrowserCommand):
50
50
  # Session commands
51
51
 
52
52
  save_parser = cmd2.Cmd2ArgumentParser()
53
- save_parser.add_argument("path", nargs="?", default=".navcli_session.json",
54
- help="Path to save session file")
53
+ save_parser.add_argument("name", nargs="?", default=None,
54
+ help="Session name (saves to ~/.navcli/sessions/<name>.json)")
55
55
 
56
56
  @cmd2.with_argparser(save_parser)
57
57
  def do_save_session(self, args):
58
58
  """Save session (cookies, storage) to file.
59
59
 
60
- Usage: save_session [path]
61
- Example: save_session my_session.json
60
+ Usage: save_session [name]
61
+ Example: save_session jisilu
62
62
  """
63
63
  import asyncio
64
64
 
65
65
  async def _save():
66
- return await self.client.save_session(args.path)
66
+ return await self.client.save_session(name=args.name)
67
67
 
68
- result = asyncio.run(self.run_async_client(_save))
68
+ result = asyncio.run(self.run_async_client(_save()))
69
69
  self.pretty_result(result)
70
70
 
71
71
  load_parser = cmd2.Cmd2ArgumentParser()
72
- load_parser.add_argument("path", nargs="?", default=".navcli_session.json",
73
- help="Path to load session file from")
72
+ load_parser.add_argument("name", nargs="?", default=None,
73
+ help="Session name (loads from ~/.navcli/sessions/<name>.json)")
74
74
 
75
75
  @cmd2.with_argparser(load_parser)
76
76
  def do_load_session(self, args):
77
77
  """Load session (cookies, storage) from file.
78
78
 
79
- Usage: load_session [path]
80
- Example: load_session my_session.json
79
+ Usage: load_session [name]
80
+ Example: load_session jisilu
81
81
  """
82
82
  import asyncio
83
83
 
84
84
  async def _load():
85
- return await self.client.load_session(args.path)
85
+ return await self.client.load_session(name=args.name)
86
86
 
87
- result = asyncio.run(self.run_async_client(_load))
87
+ result = asyncio.run(self.run_async_client(_load()))
88
88
  self.pretty_result(result)
89
89
 
90
90
  @cmd2.with_argparser(cmd2.Cmd2ArgumentParser())
@@ -98,7 +98,7 @@ class ControlCommands(BaseBrowserCommand):
98
98
  async def _get():
99
99
  return await self.client.get_cookies()
100
100
 
101
- result = asyncio.run(self.run_async_client(_get))
101
+ result = asyncio.run(self.run_async_client(_get()))
102
102
  if result.get("success"):
103
103
  self.poutput("[OK] got cookies")
104
104
  else:
@@ -115,5 +115,5 @@ class ControlCommands(BaseBrowserCommand):
115
115
  async def _clear():
116
116
  return await self.client.clear_cookies()
117
117
 
118
- result = asyncio.run(self.run_async_client(_clear))
118
+ result = asyncio.run(self.run_async_client(_clear()))
119
119
  self.pretty_result(result)
@@ -22,7 +22,7 @@ class ExploreCommands(BaseBrowserCommand):
22
22
  async def _find():
23
23
  return await self.client.find(args.text)
24
24
 
25
- result = asyncio.run(self.run_async_client(_find))
25
+ result = asyncio.run(self.run_async_client(_find()))
26
26
  self.pretty_result(result)
27
27
 
28
28
  @cmd2.with_argparser(cmd2.Cmd2ArgumentParser())
@@ -37,7 +37,7 @@ class ExploreCommands(BaseBrowserCommand):
37
37
  async def _findall():
38
38
  return await self.client.findall(args.text)
39
39
 
40
- result = asyncio.run(self.run_async_client(_findall))
40
+ result = asyncio.run(self.run_async_client(_findall()))
41
41
  self.pretty_result(result)
42
42
 
43
43
  @cmd2.with_argparser(cmd2.Cmd2ArgumentParser())
@@ -52,7 +52,7 @@ class ExploreCommands(BaseBrowserCommand):
52
52
  async def _inspect():
53
53
  return await self.client.inspect(args.selector)
54
54
 
55
- result = asyncio.run(self.run_async_client(_inspect))
55
+ result = asyncio.run(self.run_async_client(_inspect()))
56
56
  self.pretty_result(result)
57
57
 
58
58
  @cmd2.with_argparser(cmd2.Cmd2ArgumentParser())
@@ -73,7 +73,7 @@ class ExploreCommands(BaseBrowserCommand):
73
73
  else:
74
74
  return await self.client.wait()
75
75
 
76
- result = asyncio.run(self.run_async_client(_wait))
76
+ result = asyncio.run(self.run_async_client(_wait()))
77
77
  self.pretty_result(result)
78
78
 
79
79
  @cmd2.with_argparser(cmd2.Cmd2ArgumentParser())
@@ -89,7 +89,7 @@ class ExploreCommands(BaseBrowserCommand):
89
89
  timeout = args.timeout if args.timeout else 3.0
90
90
  return await self.client.wait_idle(timeout=timeout)
91
91
 
92
- result = asyncio.run(self.run_async_client(_wait_idle))
92
+ result = asyncio.run(self.run_async_client(_wait_idle()))
93
93
  self.pretty_result(result)
94
94
 
95
95
  @cmd2.with_argparser(cmd2.Cmd2ArgumentParser())
@@ -121,5 +121,5 @@ class ExploreCommands(BaseBrowserCommand):
121
121
  else:
122
122
  return await self.client.scroll(direction="down")
123
123
 
124
- result = asyncio.run(self.run_async_client(_scroll))
124
+ result = asyncio.run(self.run_async_client(_scroll()))
125
125
  self.pretty_result(result)
@@ -33,7 +33,7 @@ class InteractionCommands(BaseBrowserCommand):
33
33
  async def _click():
34
34
  return await self.client.click(args.selector, force=args.force)
35
35
 
36
- result = asyncio.run(self.run_async_client(_click))
36
+ result = asyncio.run(self.run_async_client(_click()))
37
37
  self.pretty_result(result)
38
38
 
39
39
  @cmd2.with_argparser(cmd2.Cmd2ArgumentParser())
@@ -48,7 +48,7 @@ class InteractionCommands(BaseBrowserCommand):
48
48
  async def _type():
49
49
  return await self.client.type(args.selector, args.text)
50
50
 
51
- result = asyncio.run(self.run_async_client(_type))
51
+ result = asyncio.run(self.run_async_client(_type()))
52
52
  self.pretty_result(result)
53
53
 
54
54
  @cmd2.with_argparser(cmd2.Cmd2ArgumentParser())
@@ -63,7 +63,7 @@ class InteractionCommands(BaseBrowserCommand):
63
63
  async def _dblclick():
64
64
  return await self.client.dblclick(args.selector)
65
65
 
66
- result = asyncio.run(self.run_async_client(_dblclick))
66
+ result = asyncio.run(self.run_async_client(_dblclick()))
67
67
  self.pretty_result(result)
68
68
 
69
69
  @cmd2.with_argparser(cmd2.Cmd2ArgumentParser())
@@ -78,7 +78,7 @@ class InteractionCommands(BaseBrowserCommand):
78
78
  async def _rightclick():
79
79
  return await self.client.rightclick(args.selector)
80
80
 
81
- result = asyncio.run(self.run_async_client(_rightclick))
81
+ result = asyncio.run(self.run_async_client(_rightclick()))
82
82
  self.pretty_result(result)
83
83
 
84
84
  # Aliases
@@ -110,7 +110,7 @@ class InteractionCommands(BaseBrowserCommand):
110
110
  async def _clear():
111
111
  return await self.client.clear(args.selector)
112
112
 
113
- result = asyncio.run(self.run_async_client(_clear))
113
+ result = asyncio.run(self.run_async_client(_clear()))
114
114
  self.pretty_result(result)
115
115
 
116
116
  @cmd2.with_argparser(cmd2.Cmd2ArgumentParser())
@@ -125,5 +125,5 @@ class InteractionCommands(BaseBrowserCommand):
125
125
  async def _upload():
126
126
  return await self.client.upload(args.selector, args.file_path)
127
127
 
128
- result = asyncio.run(self.run_async_client(_upload))
128
+ result = asyncio.run(self.run_async_client(_upload()))
129
129
  self.pretty_result(result)
@@ -17,7 +17,7 @@ class NavigationCommands(BaseBrowserCommand):
17
17
 
18
18
  @cmd2.with_argparser(_goto_parser)
19
19
  def do_goto(self, args):
20
- """Navigate to a URL.
20
+ """Navigate to a URL and show page summary.
21
21
 
22
22
  Usage: goto <url>
23
23
  Example: goto https://example.com
@@ -28,9 +28,17 @@ class NavigationCommands(BaseBrowserCommand):
28
28
  url = "https://" + url
29
29
 
30
30
  async def _goto():
31
- return await self.client.goto(url)
32
-
33
- result = asyncio.run(self.run_async_client(_goto))
31
+ # Navigate first
32
+ result = await self.client.goto(url)
33
+ # Then get paragraphs and merge into state
34
+ para_result = await self.client.get_paragraphs(min_length=200)
35
+ if para_result.get("success") and result.get("success"):
36
+ # Extract paragraphs from state and merge
37
+ para_data = para_result.get("state", {}).get("paragraphs", [])
38
+ result["state"]["paragraphs"] = para_data
39
+ return result
40
+
41
+ result = asyncio.run(self.run_async_client(_goto()))
34
42
  self.pretty_result(result)
35
43
 
36
44
  @cmd2.with_argparser(cmd2.Cmd2ArgumentParser())
@@ -44,7 +52,7 @@ class NavigationCommands(BaseBrowserCommand):
44
52
  async def _back():
45
53
  return await self.client.back()
46
54
 
47
- result = asyncio.run(self.run_async_client(_back))
55
+ result = asyncio.run(self.run_async_client(_back()))
48
56
  self.pretty_result(result)
49
57
 
50
58
  @cmd2.with_argparser(cmd2.Cmd2ArgumentParser())
@@ -58,7 +66,7 @@ class NavigationCommands(BaseBrowserCommand):
58
66
  async def _forward():
59
67
  return await self.client.forward()
60
68
 
61
- result = asyncio.run(self.run_async_client(_forward))
69
+ result = asyncio.run(self.run_async_client(_forward()))
62
70
  self.pretty_result(result)
63
71
 
64
72
  @cmd2.with_argparser(cmd2.Cmd2ArgumentParser())
@@ -72,7 +80,7 @@ class NavigationCommands(BaseBrowserCommand):
72
80
  async def _reload():
73
81
  return await self.client.reload()
74
82
 
75
- result = asyncio.run(self.run_async_client(_reload))
83
+ result = asyncio.run(self.run_async_client(_reload()))
76
84
  self.pretty_result(result)
77
85
 
78
86
  # Aliases
@@ -80,11 +88,25 @@ class NavigationCommands(BaseBrowserCommand):
80
88
  do_f = do_forward # forward -> f
81
89
  do_r = do_reload # reload -> r
82
90
 
83
- # goto -> g (requires separate parser)
91
+ # goto -> g (shortcut with same logic as do_goto)
84
92
  @cmd2.with_argparser(_goto_parser)
85
93
  def do_g(self, args):
86
94
  """Navigate to a URL (shortcut).
87
95
 
88
96
  Usage: g <url>
89
97
  """
90
- return self.do_goto(args)
98
+ import asyncio
99
+ url = args.url
100
+ if not url.startswith(("http://", "https://")):
101
+ url = "https://" + url
102
+
103
+ async def _goto():
104
+ result = await self.client.goto(url)
105
+ para_result = await self.client.get_paragraphs(min_length=200)
106
+ if para_result.get("success") and result.get("success"):
107
+ para_data = para_result.get("state", {}).get("paragraphs", [])
108
+ result["state"]["paragraphs"] = para_data
109
+ return result
110
+
111
+ result = asyncio.run(self.run_async_client(_goto()))
112
+ self.pretty_result(result)