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.
- navcli-0.2.1/PKG-INFO +81 -0
- navcli-0.2.1/README.md +180 -0
- navcli-0.2.1/docs/README.md +48 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/app.py +4 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/client.py +34 -8
- {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/commands/control.py +16 -16
- {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/commands/explore.py +6 -6
- {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/commands/interaction.py +6 -6
- {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/commands/navigation.py +31 -9
- {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/commands/query.py +54 -10
- {navcli-0.1.0 → navcli-0.2.1}/navcli/core/models/__init__.py +2 -1
- {navcli-0.1.0 → navcli-0.2.1}/navcli/core/models/state.py +14 -3
- navcli-0.2.1/navcli/server/__init__.py +128 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/server/browser.py +61 -22
- {navcli-0.1.0 → navcli-0.2.1}/navcli/server/routes/explore.py +1 -1
- {navcli-0.1.0 → navcli-0.2.1}/navcli/server/routes/query.py +166 -7
- {navcli-0.1.0 → navcli-0.2.1}/navcli/server/routes/session.py +38 -9
- navcli-0.2.1/navcli.egg-info/PKG-INFO +81 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli.egg-info/SOURCES.txt +1 -0
- {navcli-0.1.0 → navcli-0.2.1}/pyproject.toml +1 -1
- navcli-0.1.0/PKG-INFO +0 -79
- navcli-0.1.0/docs/README.md +0 -46
- navcli-0.1.0/navcli/server/__init__.py +0 -86
- navcli-0.1.0/navcli.egg-info/PKG-INFO +0 -79
- {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/__init__.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/commands/__init__.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/cli/commands/base.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/core/__init__.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/core/models/dom.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/core/models/element.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/core/models/feedback.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/server/app.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/server/middleware/__init__.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/server/routes/__init__.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/server/routes/control.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/server/routes/interaction.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/server/routes/navigation.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/utils/__init__.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/utils/image.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/utils/js.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/utils/selector.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/utils/text.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/utils/time.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli/utils/url.py +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli.egg-info/dependency_links.txt +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli.egg-info/entry_points.txt +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli.egg-info/requires.txt +0 -0
- {navcli-0.1.0 → navcli-0.2.1}/navcli.egg-info/top_level.txt +0 -0
- {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
|
+
[](https://pypi.org/project/navcli/)
|
|
4
|
+
[](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 =
|
|
249
|
+
async def save_session(self, name: str = None, path: str = None):
|
|
238
250
|
"""Save session to file.
|
|
239
251
|
|
|
240
252
|
Args:
|
|
241
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
255
|
-
|
|
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("
|
|
54
|
-
|
|
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 [
|
|
61
|
-
Example: save_session
|
|
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.
|
|
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("
|
|
73
|
-
|
|
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 [
|
|
80
|
-
Example: load_session
|
|
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.
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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 (
|
|
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
|
-
|
|
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)
|