webctl 0.1.2__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.
- webctl-0.1.2/.github/workflows/ci.yml +92 -0
- webctl-0.1.2/.github/workflows/publish.yml +29 -0
- webctl-0.1.2/.gitignore +41 -0
- webctl-0.1.2/.python-version +1 -0
- webctl-0.1.2/PKG-INFO +318 -0
- webctl-0.1.2/README.md +281 -0
- webctl-0.1.2/pyproject.toml +81 -0
- webctl-0.1.2/src/webctl/__init__.py +14 -0
- webctl-0.1.2/src/webctl/__main__.py +6 -0
- webctl-0.1.2/src/webctl/cli/__init__.py +13 -0
- webctl-0.1.2/src/webctl/cli/app.py +1095 -0
- webctl-0.1.2/src/webctl/cli/commands/__init__.py +0 -0
- webctl-0.1.2/src/webctl/cli/output.py +313 -0
- webctl-0.1.2/src/webctl/config.py +154 -0
- webctl-0.1.2/src/webctl/daemon/__init__.py +14 -0
- webctl-0.1.2/src/webctl/daemon/detectors/__init__.py +15 -0
- webctl-0.1.2/src/webctl/daemon/detectors/action.py +177 -0
- webctl-0.1.2/src/webctl/daemon/detectors/auth.py +260 -0
- webctl-0.1.2/src/webctl/daemon/detectors/cookie_banner.py +356 -0
- webctl-0.1.2/src/webctl/daemon/detectors/view_change.py +229 -0
- webctl-0.1.2/src/webctl/daemon/event_emitter.py +134 -0
- webctl-0.1.2/src/webctl/daemon/handlers/__init__.py +11 -0
- webctl-0.1.2/src/webctl/daemon/handlers/console.py +90 -0
- webctl-0.1.2/src/webctl/daemon/handlers/error_screenshot.py +51 -0
- webctl-0.1.2/src/webctl/daemon/handlers/hitl.py +161 -0
- webctl-0.1.2/src/webctl/daemon/handlers/interact.py +777 -0
- webctl-0.1.2/src/webctl/daemon/handlers/navigation.py +171 -0
- webctl-0.1.2/src/webctl/daemon/handlers/observe.py +310 -0
- webctl-0.1.2/src/webctl/daemon/handlers/registry.py +31 -0
- webctl-0.1.2/src/webctl/daemon/handlers/session.py +280 -0
- webctl-0.1.2/src/webctl/daemon/handlers/wait.py +290 -0
- webctl-0.1.2/src/webctl/daemon/server.py +200 -0
- webctl-0.1.2/src/webctl/daemon/session_manager.py +391 -0
- webctl-0.1.2/src/webctl/exceptions.py +119 -0
- webctl-0.1.2/src/webctl/protocol/__init__.py +51 -0
- webctl-0.1.2/src/webctl/protocol/client.py +76 -0
- webctl-0.1.2/src/webctl/protocol/messages.py +133 -0
- webctl-0.1.2/src/webctl/protocol/transport.py +415 -0
- webctl-0.1.2/src/webctl/query/__init__.py +37 -0
- webctl-0.1.2/src/webctl/query/ast.py +97 -0
- webctl-0.1.2/src/webctl/query/grammar.py +47 -0
- webctl-0.1.2/src/webctl/query/parser.py +136 -0
- webctl-0.1.2/src/webctl/query/resolver.py +215 -0
- webctl-0.1.2/src/webctl/security/__init__.py +8 -0
- webctl-0.1.2/src/webctl/security/domain_policy.py +135 -0
- webctl-0.1.2/src/webctl/views/__init__.py +29 -0
- webctl-0.1.2/src/webctl/views/a11y.py +352 -0
- webctl-0.1.2/src/webctl/views/dom_lite.py +179 -0
- webctl-0.1.2/src/webctl/views/export.py +130 -0
- webctl-0.1.2/src/webctl/views/filters.py +142 -0
- webctl-0.1.2/src/webctl/views/markdown.py +86 -0
- webctl-0.1.2/src/webctl/views/redaction.py +61 -0
- webctl-0.1.2/tests/__init__.py +0 -0
- webctl-0.1.2/tests/integration/__init__.py +0 -0
- webctl-0.1.2/tests/test_smoke.py +273 -0
- webctl-0.1.2/tests/unit/__init__.py +0 -0
- webctl-0.1.2/uv.lock +813 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, master]
|
|
8
|
+
workflow_dispatch: # Allow manual trigger
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
lint:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Install uv
|
|
17
|
+
uses: astral-sh/setup-uv@v4
|
|
18
|
+
with:
|
|
19
|
+
version: "latest"
|
|
20
|
+
|
|
21
|
+
- name: Set up Python
|
|
22
|
+
run: uv python install 3.12
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: uv sync --extra dev
|
|
26
|
+
|
|
27
|
+
- name: Run ruff check
|
|
28
|
+
run: uv run ruff check src/
|
|
29
|
+
|
|
30
|
+
- name: Run ruff format check
|
|
31
|
+
run: uv run ruff format --check src/
|
|
32
|
+
|
|
33
|
+
- name: Run mypy
|
|
34
|
+
run: uv run mypy src/
|
|
35
|
+
|
|
36
|
+
test:
|
|
37
|
+
runs-on: ${{ matrix.os }}
|
|
38
|
+
strategy:
|
|
39
|
+
fail-fast: false
|
|
40
|
+
matrix:
|
|
41
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
42
|
+
python-version: ["3.11", "3.12"]
|
|
43
|
+
|
|
44
|
+
steps:
|
|
45
|
+
- uses: actions/checkout@v4
|
|
46
|
+
|
|
47
|
+
- name: Install uv
|
|
48
|
+
uses: astral-sh/setup-uv@v4
|
|
49
|
+
with:
|
|
50
|
+
version: "latest"
|
|
51
|
+
|
|
52
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
53
|
+
run: uv python install ${{ matrix.python-version }}
|
|
54
|
+
|
|
55
|
+
- name: Install dependencies
|
|
56
|
+
run: uv sync --extra dev
|
|
57
|
+
|
|
58
|
+
- name: Install Playwright browsers
|
|
59
|
+
run: uv run playwright install chromium
|
|
60
|
+
|
|
61
|
+
- name: Install Playwright system deps (Linux)
|
|
62
|
+
if: runner.os == 'Linux'
|
|
63
|
+
run: uv run playwright install-deps chromium
|
|
64
|
+
|
|
65
|
+
- name: Run tests
|
|
66
|
+
run: uv run pytest tests/ -v --tb=short
|
|
67
|
+
env:
|
|
68
|
+
CI: "true"
|
|
69
|
+
NO_COLOR: "1"
|
|
70
|
+
|
|
71
|
+
build:
|
|
72
|
+
runs-on: ubuntu-latest
|
|
73
|
+
needs: [lint, test]
|
|
74
|
+
steps:
|
|
75
|
+
- uses: actions/checkout@v4
|
|
76
|
+
|
|
77
|
+
- name: Install uv
|
|
78
|
+
uses: astral-sh/setup-uv@v4
|
|
79
|
+
with:
|
|
80
|
+
version: "latest"
|
|
81
|
+
|
|
82
|
+
- name: Set up Python
|
|
83
|
+
run: uv python install 3.12
|
|
84
|
+
|
|
85
|
+
- name: Build package
|
|
86
|
+
run: uv build
|
|
87
|
+
|
|
88
|
+
- name: Upload artifact
|
|
89
|
+
uses: actions/upload-artifact@v4
|
|
90
|
+
with:
|
|
91
|
+
name: dist
|
|
92
|
+
path: dist/
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
# Semver: v1.0.0, v1.0.0-alpha, v1.0.0-rc.1, v1.0.0+build
|
|
7
|
+
# GitHub Actions only supports glob patterns, not the full semver regex
|
|
8
|
+
- 'v[0-9]*.[0-9]*.[0-9]*'
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
publish:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
environment: pypi
|
|
14
|
+
permissions:
|
|
15
|
+
id-token: write # Required for trusted publishing
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v4
|
|
22
|
+
|
|
23
|
+
- name: Build package
|
|
24
|
+
run: |
|
|
25
|
+
uv python install 3.12
|
|
26
|
+
uv build
|
|
27
|
+
|
|
28
|
+
- name: Publish to PyPI
|
|
29
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
webctl-0.1.2/.gitignore
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
venv/
|
|
12
|
+
|
|
13
|
+
# Testing
|
|
14
|
+
.pytest_cache/
|
|
15
|
+
.coverage
|
|
16
|
+
htmlcov/
|
|
17
|
+
.ruff_cache/
|
|
18
|
+
|
|
19
|
+
# IDE/Editor
|
|
20
|
+
.vscode/
|
|
21
|
+
.idea/
|
|
22
|
+
*.swp
|
|
23
|
+
*.swo
|
|
24
|
+
|
|
25
|
+
# Claude Code
|
|
26
|
+
.claude/
|
|
27
|
+
|
|
28
|
+
# OS
|
|
29
|
+
.DS_Store
|
|
30
|
+
Thumbs.db
|
|
31
|
+
|
|
32
|
+
# Generated docs
|
|
33
|
+
README.html
|
|
34
|
+
README_files/
|
|
35
|
+
|
|
36
|
+
# Temp files
|
|
37
|
+
tmpclaude-*
|
|
38
|
+
*.tmp
|
|
39
|
+
|
|
40
|
+
# mypy
|
|
41
|
+
.mypy_cache/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
webctl-0.1.2/PKG-INFO
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: webctl
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Browser automation via CLI — for humans and agents
|
|
5
|
+
Project-URL: Homepage, https://github.com/cosinusalpha/webctl
|
|
6
|
+
Project-URL: Repository, https://github.com/cosinusalpha/webctl
|
|
7
|
+
Project-URL: Issues, https://github.com/cosinusalpha/webctl/issues
|
|
8
|
+
Author-email: cosinusalpha <42695699+cosinusalpha@users.noreply.github.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: agent,automation,browser,cli,web
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
19
|
+
Classifier: Topic :: Software Development :: Testing
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: aiofiles>=24.0.0
|
|
22
|
+
Requires-Dist: deepdiff>=7.0.0
|
|
23
|
+
Requires-Dist: lark>=1.1.0
|
|
24
|
+
Requires-Dist: markdownify>=0.13.0
|
|
25
|
+
Requires-Dist: playwright>=1.40.0
|
|
26
|
+
Requires-Dist: pydantic>=2.0.0
|
|
27
|
+
Requires-Dist: rich>=13.0.0
|
|
28
|
+
Requires-Dist: typer>=0.12.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: mypy>=1.10.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-playwright>=0.5.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: types-aiofiles>=24.0.0; extra == 'dev'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# webctl
|
|
39
|
+
|
|
40
|
+
**Browser automation for AI agents and humans, built on the command line.**
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
webctl start
|
|
44
|
+
webctl navigate "https://google.com"
|
|
45
|
+
webctl type 'role=combobox name~="Search"' "best restaurants nearby" --submit
|
|
46
|
+
webctl snapshot --interactive-only --limit 20
|
|
47
|
+
webctl stop --daemon
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Why CLI Instead of MCP?
|
|
51
|
+
|
|
52
|
+
MCP browser tools have a fundamental problem: **the server controls what enters your context**. With Playwright MCP, every response includes the full accessibility tree plus console messages (default: "info" level). After a few page queries, your context is full.
|
|
53
|
+
|
|
54
|
+
CLI flips this around: **you control what enters context**.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Filter before context
|
|
58
|
+
webctl snapshot --interactive-only --limit 30 # Only buttons, links, inputs
|
|
59
|
+
webctl snapshot --within "role=main" # Skip nav, footer, ads
|
|
60
|
+
|
|
61
|
+
# Pipe through Unix tools
|
|
62
|
+
webctl snapshot | grep -i "submit" # Find specific elements
|
|
63
|
+
webctl --format jsonl snapshot | jq '.data.role' # Extract with jq
|
|
64
|
+
webctl snapshot | head -50 # Truncate output
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Beyond filtering, CLI gives you:
|
|
68
|
+
|
|
69
|
+
| Capability | CLI | MCP |
|
|
70
|
+
|------------|-----|-----|
|
|
71
|
+
| **Filter output** | Built-in flags + grep/jq/head | Server decides |
|
|
72
|
+
| **Debug** | Run same command as agent | Opaque |
|
|
73
|
+
| **Cache** | `webctl snapshot > cache.txt` | Every call hits server |
|
|
74
|
+
| **Script** | Save to .sh, version control | Ephemeral |
|
|
75
|
+
| **Timeout** | `timeout 30 webctl ...` | Internal only |
|
|
76
|
+
| **Parallelize** | `parallel`, `xargs`, `&` | Server-dependent |
|
|
77
|
+
| **Human takeover** | Same commands | Different interface |
|
|
78
|
+
|
|
79
|
+
## Quick Start
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install webctl # Requires Python 3.11+
|
|
83
|
+
webctl setup # Downloads Chromium (~150MB)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Verify it works:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
webctl start
|
|
90
|
+
webctl navigate "https://example.com"
|
|
91
|
+
webctl snapshot --interactive-only
|
|
92
|
+
webctl stop --daemon
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
<details>
|
|
96
|
+
<summary>Install from source</summary>
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
git clone https://github.com/cosinusalpha/webctl
|
|
100
|
+
cd webctl
|
|
101
|
+
uv sync && uv run webctl setup
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
</details>
|
|
105
|
+
|
|
106
|
+
<details>
|
|
107
|
+
<summary>Linux system dependencies</summary>
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
playwright install-deps chromium
|
|
111
|
+
# Or manually: sudo apt-get install libnss3 libatk1.0-0 libatk-bridge2.0-0 ...
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
</details>
|
|
115
|
+
|
|
116
|
+
## Core Concepts
|
|
117
|
+
|
|
118
|
+
### Sessions
|
|
119
|
+
|
|
120
|
+
Browser stays open across commands. Cookies persist to disk.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
webctl start # Visible browser
|
|
124
|
+
webctl start --mode unattended # Headless
|
|
125
|
+
webctl -s work start # Named profile (separate cookies)
|
|
126
|
+
webctl stop --daemon # Shutdown everything
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Element Queries
|
|
130
|
+
|
|
131
|
+
Semantic targeting based on ARIA roles - stable across CSS refactors:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
role=button # Any button
|
|
135
|
+
role=button name="Submit" # Exact match
|
|
136
|
+
role=button name~="Submit" # Contains (preferred)
|
|
137
|
+
role=textbox name~="Email" # Input field
|
|
138
|
+
role=link name~="Sign in" # Link
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Output Control
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
webctl snapshot # Human-readable
|
|
145
|
+
webctl --quiet navigate "..." # Suppress events
|
|
146
|
+
webctl --result-only --format jsonl navigate "..." # Pure JSON, final result only
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Commands
|
|
152
|
+
|
|
153
|
+
### Navigation
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
webctl navigate "https://..." # Go to URL
|
|
157
|
+
webctl back # History back
|
|
158
|
+
webctl forward # History forward
|
|
159
|
+
webctl reload # Refresh
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Observation
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
webctl snapshot # Full a11y tree
|
|
166
|
+
webctl snapshot --interactive-only # Buttons, links, inputs only
|
|
167
|
+
webctl snapshot --limit 30 # Cap output
|
|
168
|
+
webctl snapshot --within "role=main" # Scope to container
|
|
169
|
+
webctl snapshot --roles "button,link" # Filter by role
|
|
170
|
+
webctl query "role=button name~=Submit" # Debug query, get suggestions
|
|
171
|
+
webctl screenshot --path shot.png # Screenshot
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Interaction
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
webctl click 'role=button name~="Submit"'
|
|
178
|
+
webctl type 'role=textbox name~="Email"' "user@example.com"
|
|
179
|
+
webctl type 'role=textbox name~="Search"' "query" --submit # Type + Enter
|
|
180
|
+
webctl select 'role=combobox name~="Country"' --label "Germany"
|
|
181
|
+
webctl check 'role=checkbox name~="Remember"'
|
|
182
|
+
webctl press Enter
|
|
183
|
+
webctl scroll down
|
|
184
|
+
webctl upload 'role=button name~="Upload"' --file ./doc.pdf
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Wait Conditions
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
webctl wait network-idle
|
|
191
|
+
webctl wait 'exists:role=button name~="Continue"'
|
|
192
|
+
webctl wait 'visible:role=dialog'
|
|
193
|
+
webctl wait 'hidden:role=progressbar'
|
|
194
|
+
webctl wait 'url-contains:"/dashboard"'
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Session Management
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
webctl status # Current state (includes console error counts)
|
|
201
|
+
webctl save # Persist cookies now
|
|
202
|
+
webctl sessions # List profiles
|
|
203
|
+
webctl pages # List tabs
|
|
204
|
+
webctl focus p2 # Switch tab
|
|
205
|
+
webctl close-page p1 # Close tab
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Console Logs
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
webctl console # Get last 100 logs
|
|
212
|
+
webctl console --count # Just counts by level (LLM-friendly)
|
|
213
|
+
webctl console --level error # Filter to errors only
|
|
214
|
+
webctl console --follow # Stream new logs continuously
|
|
215
|
+
webctl console -n 50 -l warn # Last 50 warnings
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Setup & Config
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
webctl setup # Install browser
|
|
222
|
+
webctl doctor # Diagnose installation
|
|
223
|
+
webctl init # Add to agent configs (CLAUDE.md, etc.)
|
|
224
|
+
webctl config show # Show settings
|
|
225
|
+
webctl config set idle_timeout 1800
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Agent Integration
|
|
231
|
+
|
|
232
|
+
Tell your AI agent to use webctl. The easiest way:
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
webctl init # Creates CLAUDE.md, GEMINI.md, etc.
|
|
236
|
+
webctl init --agents claude # Only specific agents
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Or manually add to your agent's config:
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
For web browsing, use webctl CLI. Run `webctl agent-prompt` for instructions.
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## For AI Agents
|
|
248
|
+
|
|
249
|
+
*This section is designed to be read by AI agents directly.*
|
|
250
|
+
|
|
251
|
+
### webctl Quick Reference
|
|
252
|
+
|
|
253
|
+
Control a browser via CLI. Start with `webctl start`, end with `webctl stop --daemon`.
|
|
254
|
+
|
|
255
|
+
**Commands:**
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
webctl start # Open browser
|
|
259
|
+
webctl navigate "URL" # Go to URL
|
|
260
|
+
webctl snapshot --interactive-only # See clickable elements
|
|
261
|
+
webctl click 'role=button name~="Text"' # Click element
|
|
262
|
+
webctl type 'role=textbox name~="Field"' "text" # Type
|
|
263
|
+
webctl type 'role=textbox name~="Field"' "text" --submit # Type + Enter
|
|
264
|
+
webctl select 'role=combobox' --label "Option" # Dropdown
|
|
265
|
+
webctl wait 'exists:role=button name~="..."' # Wait for element
|
|
266
|
+
webctl stop --daemon # Close browser
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**Query syntax:**
|
|
270
|
+
|
|
271
|
+
- `role=button` - By ARIA role (button, link, textbox, combobox, checkbox)
|
|
272
|
+
- `name~="partial"` - Partial match (preferred, more robust)
|
|
273
|
+
- `name="exact"` - Exact match
|
|
274
|
+
|
|
275
|
+
**Example - Login:**
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
webctl start
|
|
279
|
+
webctl navigate "https://site.com/login"
|
|
280
|
+
webctl type 'role=textbox name~="Email"' "user@example.com"
|
|
281
|
+
webctl type 'role=textbox name~="Password"' "secret" --submit
|
|
282
|
+
webctl wait 'url-contains:"/dashboard"'
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Tips:**
|
|
286
|
+
|
|
287
|
+
- Use `--interactive-only` to reduce output (only buttons, links, inputs)
|
|
288
|
+
- Use `name~=` for partial matching (handles minor text changes)
|
|
289
|
+
- Use `webctl query "..."` if element not found - shows suggestions
|
|
290
|
+
- Use `--quiet` to suppress event output
|
|
291
|
+
- Sessions persist cookies - login once, stay logged in
|
|
292
|
+
- Check `webctl status` for console error counts before investigating
|
|
293
|
+
- Use `webctl console --count` for log summary, `--level error` for details
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Architecture
|
|
298
|
+
|
|
299
|
+
```
|
|
300
|
+
┌─────────────┐ TCP/IPC ┌─────────────┐
|
|
301
|
+
│ CLI │ ◄──────────────► │ Daemon │
|
|
302
|
+
│ (webctl) │ JSON-RPC │ (browser) │
|
|
303
|
+
└─────────────┘ └─────────────┘
|
|
304
|
+
│ │
|
|
305
|
+
▼ ▼
|
|
306
|
+
Agent/User Chromium + Playwright
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
- **CLI**: Stateless, sends commands to daemon
|
|
310
|
+
- **Daemon**: Manages browser, auto-starts on first command
|
|
311
|
+
- **Profiles**: `~/.local/share/webctl/profiles/`
|
|
312
|
+
- **Config**: `~/.config/webctl/config.json`
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## License
|
|
317
|
+
|
|
318
|
+
MIT
|