agentsys 5.3.7 → 5.4.0
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.
- package/.agnix.toml +17 -7
- package/.claude-plugin/marketplace.json +13 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.gitmodules +3 -0
- package/AGENTS.md +4 -4
- package/CHANGELOG.md +21 -0
- package/README.md +46 -5
- package/lib/adapter-transforms.js +3 -1
- package/package.json +1 -1
- package/site/assets/css/main.css +39 -1
- package/site/assets/js/main.js +24 -0
- package/site/content.json +4 -4
- package/site/index.html +82 -7
- package/site/ux-spec.md +5 -5
- package/agent-knowledge/AGENTS.md +0 -231
- package/agent-knowledge/acp-with-codex-gemini-copilot-claude.md +0 -504
- package/agent-knowledge/ai-cli-advanced-integration-patterns.md +0 -670
- package/agent-knowledge/ai-cli-non-interactive-programmatic-usage.md +0 -1394
- package/agent-knowledge/all-in-one-plus-modular-packages.md +0 -576
- package/agent-knowledge/cli-browser-automation-agents.md +0 -936
- package/agent-knowledge/github-org-project-management.md +0 -319
- package/agent-knowledge/github-org-structure-patterns.md +0 -268
- package/agent-knowledge/kiro-supervised-autopilot.md +0 -400
- package/agent-knowledge/multi-product-org-docs.md +0 -622
- package/agent-knowledge/oss-org-naming-patterns.md +0 -368
- package/agent-knowledge/resources/acp-with-codex-gemini-copilot-claude-sources.json +0 -408
- package/agent-knowledge/resources/ai-cli-non-interactive-programmatic-usage-sources.json +0 -500
- package/agent-knowledge/resources/all-in-one-plus-modular-packages-sources.json +0 -310
- package/agent-knowledge/resources/cli-browser-automation-agents-sources.json +0 -428
- package/agent-knowledge/resources/github-org-project-management-sources.json +0 -239
- package/agent-knowledge/resources/github-org-structure-patterns-sources.json +0 -293
- package/agent-knowledge/resources/kiro-supervised-autopilot-sources.json +0 -135
- package/agent-knowledge/resources/multi-product-org-docs-sources.json +0 -514
- package/agent-knowledge/resources/oss-org-naming-patterns-sources.json +0 -458
- package/agent-knowledge/resources/skill-plugin-distribution-patterns-sources.json +0 -290
- package/agent-knowledge/resources/terminal-browsers-agent-automation-sources.json +0 -758
- package/agent-knowledge/resources/web-session-persistence-cli-agents-sources.json +0 -528
- package/agent-knowledge/skill-plugin-distribution-patterns.md +0 -661
- package/agent-knowledge/terminal-browsers-agent-automation.md +0 -776
- package/agent-knowledge/web-session-persistence-cli-agents.md +0 -1352
|
@@ -1,776 +0,0 @@
|
|
|
1
|
-
# Learning Guide: Terminal Browsers - AI Agent Scripting and Automation
|
|
2
|
-
|
|
3
|
-
**Generated**: 2026-02-20
|
|
4
|
-
**Sources**: 40 resources analyzed (from training knowledge, cutoff Aug 2025)
|
|
5
|
-
**Depth**: deep
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Prerequisites
|
|
10
|
-
|
|
11
|
-
- Basic shell scripting (bash/zsh)
|
|
12
|
-
- Familiarity with HTTP concepts (cookies, sessions, headers)
|
|
13
|
-
- Understanding of what an AI agent loop looks like (observe → act → observe)
|
|
14
|
-
- Linux/macOS environment (most tools are Unix-first)
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## TL;DR
|
|
19
|
-
|
|
20
|
-
- **lynx** and **w3m** are the best choices for pure shell-driven agent control: single-flag dump mode (`lynx -dump URL`, `w3m -dump URL`) returns clean readable text with numbered links in one command, no scripts needed.
|
|
21
|
-
- **browsh** renders full modern CSS/JS pages in a terminal but requires a running Firefox instance and is hard to automate headlessly; avoid for pure CLI agents.
|
|
22
|
-
- **carbonyl** (Chromium in terminal) is the most modern option and supports full Chrome DevTools Protocol (CDP) automation, making it scriptable via standard Playwright/Puppeteer toolchains without any GUI.
|
|
23
|
-
- For an agent that needs to "click a selector" and "read a page" without writing Python/Node.js, the **w3m + shell pipeline** pattern beats everything else in simplicity. For JavaScript-heavy sites, **carbonyl** is the right tool.
|
|
24
|
-
- Cookie/session persistence works natively in lynx (`-accept_all_cookies`, `-cookie_file`) and w3m (`.w3m/cookie` file). carbonyl inherits full Chromium cookie handling.
|
|
25
|
-
|
|
26
|
-
---
|
|
27
|
-
|
|
28
|
-
## Core Concepts
|
|
29
|
-
|
|
30
|
-
### 1. The Dump Mode Pattern
|
|
31
|
-
|
|
32
|
-
Every major text browser has a "dump mode" that reads a URL, renders it, and exits. This is the foundation of agent-friendly terminal browsing:
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
browser -dump URL
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
The output is plain text (or markdown-like text) with link references at the bottom. An agent can:
|
|
39
|
-
1. Issue the command
|
|
40
|
-
2. Read stdout
|
|
41
|
-
3. Parse references
|
|
42
|
-
4. Follow links by index or URL
|
|
43
|
-
|
|
44
|
-
This is purely stdin/stdout - no PTY, no interactive session, no expect scripts.
|
|
45
|
-
|
|
46
|
-
### 2. Link-Numbered Navigation
|
|
47
|
-
|
|
48
|
-
Both lynx and w3m number all hyperlinks in dump output:
|
|
49
|
-
|
|
50
|
-
```
|
|
51
|
-
[1] Home [2] About [3] Download
|
|
52
|
-
...body text...
|
|
53
|
-
|
|
54
|
-
References
|
|
55
|
-
1. https://example.com/
|
|
56
|
-
2. https://example.com/about
|
|
57
|
-
3. https://example.com/download
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
An agent can extract the reference list, decide which link to follow, and issue a new dump command with that URL. This implements "click" as a pure string operation.
|
|
61
|
-
|
|
62
|
-
### 3. Form Submission Without Interaction
|
|
63
|
-
|
|
64
|
-
Both lynx and w3m can submit HTML forms non-interactively:
|
|
65
|
-
|
|
66
|
-
- **lynx**: `lynx -post_data` or `-cmd_script` (a file of keystrokes)
|
|
67
|
-
- **w3m**: Pipe form data with `-post URL data`
|
|
68
|
-
- **curl**: Often better for raw POST but lacks page rendering
|
|
69
|
-
|
|
70
|
-
For agents, `curl` handles auth/POST better than text browsers. A common pattern is: curl for POST/auth, then lynx/w3m for reading rendered pages.
|
|
71
|
-
|
|
72
|
-
### 4. Three Automation Tiers
|
|
73
|
-
|
|
74
|
-
| Tier | Tools | Complexity | JS Support |
|
|
75
|
-
|------|-------|------------|-----------|
|
|
76
|
-
| Shell-native | lynx, w3m, links2, elinks | Zero setup | None / minimal |
|
|
77
|
-
| Hybrid | browsh | Medium (needs Firefox) | Full |
|
|
78
|
-
| Full headless | carbonyl, playwright-chromium | Medium | Full |
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## Tool-by-Tool Deep Dive
|
|
83
|
-
|
|
84
|
-
### lynx
|
|
85
|
-
|
|
86
|
-
**Best for**: Reading static HTML pages, following links, simple form submission.
|
|
87
|
-
|
|
88
|
-
**Dump mode** (most agent-useful):
|
|
89
|
-
```bash
|
|
90
|
-
# Render page to plain text, exit
|
|
91
|
-
lynx -dump https://example.com
|
|
92
|
-
|
|
93
|
-
# Dump with numbered links reference list
|
|
94
|
-
lynx -dump -listonly https://example.com # only URLs, no body
|
|
95
|
-
lynx -dump -nonumbers https://example.com # no link numbering
|
|
96
|
-
|
|
97
|
-
# Dump to file
|
|
98
|
-
lynx -dump https://example.com > page.txt
|
|
99
|
-
|
|
100
|
-
# Render and pipe to grep
|
|
101
|
-
lynx -dump https://news.ycombinator.com | grep "Ask HN"
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
**Cookie handling**:
|
|
105
|
-
```bash
|
|
106
|
-
# Accept all cookies, persist to file
|
|
107
|
-
lynx -accept_all_cookies -cookie_file=cookies.txt https://example.com/login
|
|
108
|
-
|
|
109
|
-
# Reuse saved cookies
|
|
110
|
-
lynx -dump -cookie_file=cookies.txt https://example.com/dashboard
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
**Login form submission** (cmd_script approach):
|
|
114
|
-
```bash
|
|
115
|
-
# Write a keystroke script
|
|
116
|
-
cat > /tmp/login.cmd << 'EOF'
|
|
117
|
-
# Navigate to login field, type username
|
|
118
|
-
key Down
|
|
119
|
-
key Down
|
|
120
|
-
key Return # activate form field
|
|
121
|
-
stuff "myusername"
|
|
122
|
-
key Tab
|
|
123
|
-
stuff "mypassword"
|
|
124
|
-
key Return # submit
|
|
125
|
-
EOF
|
|
126
|
-
lynx -cmd_script=/tmp/login.cmd -accept_all_cookies -cookie_file=cookies.txt https://example.com/login
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
**Headers**:
|
|
130
|
-
```bash
|
|
131
|
-
lynx -dump -useragent="MyBot/1.0" https://example.com
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
**Cross-platform**: Linux (native), macOS (Homebrew), Windows (WSL2 only - no native Windows build).
|
|
135
|
-
|
|
136
|
-
**Agent verdict**: Excellent for static sites. The `-dump` flag is a single-command solution. Avoid for JS-heavy sites.
|
|
137
|
-
|
|
138
|
-
**Key limitations**:
|
|
139
|
-
- No JavaScript execution
|
|
140
|
-
- No CSS layout (renders raw HTML structure)
|
|
141
|
-
- No WebSockets
|
|
142
|
-
- Form handling is awkward for complex SPAs
|
|
143
|
-
|
|
144
|
-
---
|
|
145
|
-
|
|
146
|
-
### w3m
|
|
147
|
-
|
|
148
|
-
**Best for**: Clean text rendering, image display in compatible terminals (sixel), inline image support in iTerm2/kitty.
|
|
149
|
-
|
|
150
|
-
**Dump mode**:
|
|
151
|
-
```bash
|
|
152
|
-
# Basic text dump
|
|
153
|
-
w3m -dump https://example.com
|
|
154
|
-
|
|
155
|
-
# Dump with raw HTML output
|
|
156
|
-
w3m -dump -T text/html https://example.com
|
|
157
|
-
|
|
158
|
-
# Read from stdin (pipe HTML to w3m)
|
|
159
|
-
curl -s https://example.com | w3m -dump -T text/html
|
|
160
|
-
|
|
161
|
-
# Dump with target encoding
|
|
162
|
-
w3m -dump -O UTF-8 https://example.com
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
**Cookie handling**:
|
|
166
|
-
w3m stores cookies in `~/.w3m/cookie` automatically. For agent use:
|
|
167
|
-
```bash
|
|
168
|
-
# First request sets cookies
|
|
169
|
-
w3m -dump https://example.com/login
|
|
170
|
-
|
|
171
|
-
# Subsequent requests reuse ~/.w3m/cookie
|
|
172
|
-
w3m -dump https://example.com/dashboard
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
**Piping HTML into w3m** - extremely useful for agents that already have HTML:
|
|
176
|
-
```bash
|
|
177
|
-
# Convert HTML to readable text
|
|
178
|
-
echo '<h1>Hello</h1><p>World <a href="/link">click</a></p>' | w3m -dump -T text/html
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
**w3m vs lynx for agents**:
|
|
182
|
-
- w3m renders tables significantly better than lynx
|
|
183
|
-
- w3m accepts HTML on stdin (no temp file needed)
|
|
184
|
-
- w3m handles character encoding more robustly
|
|
185
|
-
- lynx has better cookie/session management flags
|
|
186
|
-
- lynx has `-listonly` for extracting just links
|
|
187
|
-
|
|
188
|
-
**Cross-platform**: Linux (native), macOS (Homebrew, some rendering quirks), Windows (WSL2 only).
|
|
189
|
-
|
|
190
|
-
**Agent verdict**: Slightly cleaner output than lynx for complex tables. The `curl | w3m -dump -T text/html` pipeline is a powerful agent pattern.
|
|
191
|
-
|
|
192
|
-
---
|
|
193
|
-
|
|
194
|
-
### links2
|
|
195
|
-
|
|
196
|
-
**Best for**: Slightly better rendering than links, color support, minimal footprint.
|
|
197
|
-
|
|
198
|
-
**Dump mode**:
|
|
199
|
-
```bash
|
|
200
|
-
links2 -dump https://example.com
|
|
201
|
-
links2 -dump -width 120 https://example.com
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
**Graphics mode** (requires framebuffer or X11):
|
|
205
|
-
```bash
|
|
206
|
-
links2 -g https://example.com # graphical mode, not useful for agents
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
**Agent verdict**: Functionally similar to lynx/w3m in dump mode. No compelling advantage for agent use. Less commonly maintained.
|
|
210
|
-
|
|
211
|
-
---
|
|
212
|
-
|
|
213
|
-
### elinks
|
|
214
|
-
|
|
215
|
-
**Best for**: Color rendering, slightly better CSS layout understanding than lynx/w3m, Lua scripting hooks.
|
|
216
|
-
|
|
217
|
-
**Dump mode**:
|
|
218
|
-
```bash
|
|
219
|
-
elinks -dump https://example.com
|
|
220
|
-
elinks -dump 1 https://example.com # explicit dump flag on some versions
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
**Scripting via Lua**:
|
|
224
|
-
elinks has a built-in Lua scripting interface that allows:
|
|
225
|
-
- Hooking into page load events
|
|
226
|
-
- Modifying requests/responses
|
|
227
|
-
- Automating navigation
|
|
228
|
-
|
|
229
|
-
```lua
|
|
230
|
-
-- hooks.lua (place in ~/.elinks/)
|
|
231
|
-
function follow_url_hook(url)
|
|
232
|
-
-- intercept every URL load
|
|
233
|
-
io.write("LOADING: " .. url .. "\n")
|
|
234
|
-
return nil -- nil = proceed normally
|
|
235
|
-
end
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
**ECMAScript (partial)**:
|
|
239
|
-
elinks has limited JavaScript support via SpiderMonkey (optional compile-time dependency). It handles simple DOM manipulation but not modern ES6+ or React/Vue/Angular.
|
|
240
|
-
|
|
241
|
-
**Cookie handling**:
|
|
242
|
-
elinks maintains `~/.elinks/cookies.db` automatically.
|
|
243
|
-
|
|
244
|
-
**Cross-platform**: Linux (best support), macOS (Homebrew), Windows (WSL2).
|
|
245
|
-
|
|
246
|
-
**Agent verdict**: The Lua hooks are interesting for advanced agents but add complexity. For simple dump-and-read, it offers nothing over lynx/w3m. Development has been slow since ~2012.
|
|
247
|
-
|
|
248
|
-
---
|
|
249
|
-
|
|
250
|
-
### browsh
|
|
251
|
-
|
|
252
|
-
**What it is**: A text-based browser that wraps a real Firefox instance, renders pages using full WebGL/CSS, then converts the rendered output to colored Unicode characters in the terminal.
|
|
253
|
-
|
|
254
|
-
**Architecture**:
|
|
255
|
-
```
|
|
256
|
-
browsh CLI → WebSocket → Firefox (headless) → renders → browsh converts to text
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
**Key difference**: browsh renders what Firefox renders, including JavaScript-heavy SPAs, React apps, etc. The result looks like a low-res screenshot made of characters.
|
|
260
|
-
|
|
261
|
-
**Running browsh**:
|
|
262
|
-
```bash
|
|
263
|
-
browsh # interactive
|
|
264
|
-
browsh --startup-url https://example.com # open URL on start
|
|
265
|
-
browsh --startup-url https://example.com --run-once-stdin-read # stdin control
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
**Headless/dump mode** - THIS IS IMPORTANT:
|
|
269
|
-
```bash
|
|
270
|
-
# browsh can dump page as plain text
|
|
271
|
-
browsh --startup-url https://example.com --dump-stdin-on-idle
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
However, browsh's "dump" functionality is less mature than lynx/w3m. The typical automation approach uses its stdin JSON protocol:
|
|
275
|
-
|
|
276
|
-
**JSON stdin protocol**:
|
|
277
|
-
browsh accepts JSON commands on stdin:
|
|
278
|
-
```json
|
|
279
|
-
{"type": "command", "command": "navigate", "args": ["https://example.com"]}
|
|
280
|
-
{"type": "command", "command": "screenshot"}
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
This is more complex than a single CLI flag but enables an agent to drive a full browser.
|
|
284
|
-
|
|
285
|
-
**Automation difficulty**: browsh requires Firefox to be installed and running. In Docker/CI:
|
|
286
|
-
```bash
|
|
287
|
-
docker run --rm browsh/browsh browsh --startup-url https://example.com
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
**Cross-platform**: Linux (best), macOS (needs Firefox), Windows (WSL2 with Firefox). Docker image available.
|
|
291
|
-
|
|
292
|
-
**Agent verdict**: Valuable when you need JavaScript rendering with terminal output. More complex to automate than lynx/w3m. The Docker approach is most reliable for agents.
|
|
293
|
-
|
|
294
|
-
---
|
|
295
|
-
|
|
296
|
-
### carbonyl
|
|
297
|
-
|
|
298
|
-
**What it is**: A Chromium fork that renders web pages inside a terminal using sixel graphics or Unicode block characters. Unlike browsh (which wraps Firefox), carbonyl is Chromium compiled to render to terminal output.
|
|
299
|
-
|
|
300
|
-
**Architecture**:
|
|
301
|
-
```
|
|
302
|
-
carbonyl → modified Chromium → renders to terminal (sixel/unicode) or exposes CDP
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
**Key feature for agents**: carbonyl exposes Chrome DevTools Protocol (CDP), meaning it can be driven by **Playwright, Puppeteer, or any CDP-compatible tool** without needing a display.
|
|
306
|
-
|
|
307
|
-
**Running carbonyl**:
|
|
308
|
-
```bash
|
|
309
|
-
# Interactive terminal browsing
|
|
310
|
-
carbonyl https://example.com
|
|
311
|
-
|
|
312
|
-
# Headless CDP mode (AGENT-CRITICAL)
|
|
313
|
-
carbonyl --remote-debugging-port=9222 https://about:blank
|
|
314
|
-
# Now connect Playwright/Puppeteer to localhost:9222
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
**CDP automation** (the killer feature):
|
|
318
|
-
```bash
|
|
319
|
-
# Start carbonyl with CDP exposed
|
|
320
|
-
carbonyl --headless=new --remote-debugging-port=9222
|
|
321
|
-
|
|
322
|
-
# In another process, use any CDP client
|
|
323
|
-
# e.g., with playwright-chromium pointed at carbonyl's CDP endpoint
|
|
324
|
-
# or with raw WebSocket CDP calls
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
**CLI-level control without writing scripts** - carbonyl ships with a companion tool that accepts high-level commands:
|
|
328
|
-
```bash
|
|
329
|
-
# Navigate
|
|
330
|
-
carbonyl navigate https://example.com
|
|
331
|
-
|
|
332
|
-
# Take screenshot (output to terminal or file)
|
|
333
|
-
carbonyl screenshot > page.png
|
|
334
|
-
|
|
335
|
-
# Get page text
|
|
336
|
-
carbonyl get-text https://example.com
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
(Note: the `carbonyl` subcommand interface was in active development as of mid-2025; check current docs.)
|
|
340
|
-
|
|
341
|
-
**Cross-platform**: Linux (best support, pre-built binaries), macOS (experimental), Windows (WSL2). Docker image: `fathyb/carbonyl`.
|
|
342
|
-
|
|
343
|
-
**Agent verdict**: Best option when JavaScript is required AND you want to avoid writing Node.js/Python. The CDP endpoint means any scripting language can drive it, but the `carbonyl get-text` pattern approaches single-command convenience.
|
|
344
|
-
|
|
345
|
-
---
|
|
346
|
-
|
|
347
|
-
## Agent-Optimal Patterns
|
|
348
|
-
|
|
349
|
-
### Pattern 1: Static Site Reader (lynx/w3m + shell)
|
|
350
|
-
|
|
351
|
-
Zero dependencies. Agent calls shell, gets text, parses links.
|
|
352
|
-
|
|
353
|
-
```bash
|
|
354
|
-
#!/bin/bash
|
|
355
|
-
# agent-browse.sh - give agent a URL, get back text + links
|
|
356
|
-
|
|
357
|
-
URL="$1"
|
|
358
|
-
|
|
359
|
-
# Get page text
|
|
360
|
-
TEXT=$(w3m -dump "$URL" 2>/dev/null)
|
|
361
|
-
|
|
362
|
-
# Get all links on page
|
|
363
|
-
LINKS=$(lynx -dump -listonly "$URL" 2>/dev/null)
|
|
364
|
-
|
|
365
|
-
echo "=== PAGE TEXT ==="
|
|
366
|
-
echo "$TEXT"
|
|
367
|
-
echo ""
|
|
368
|
-
echo "=== LINKS ==="
|
|
369
|
-
echo "$LINKS"
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
Agent invocation: `bash agent-browse.sh https://example.com`
|
|
373
|
-
|
|
374
|
-
### Pattern 2: curl + w3m Pipeline (auth + read)
|
|
375
|
-
|
|
376
|
-
```bash
|
|
377
|
-
# Step 1: Login with curl, save cookies
|
|
378
|
-
curl -c /tmp/agent-cookies.txt \
|
|
379
|
-
-d "username=user&password=pass" \
|
|
380
|
-
-X POST https://example.com/login \
|
|
381
|
-
-L -o /dev/null -s
|
|
382
|
-
|
|
383
|
-
# Step 2: Access authenticated page with w3m, reuse cookies
|
|
384
|
-
curl -b /tmp/agent-cookies.txt -s https://example.com/dashboard \
|
|
385
|
-
| w3m -dump -T text/html
|
|
386
|
-
|
|
387
|
-
# Or with lynx reading the cookie jar (Netscape format required)
|
|
388
|
-
lynx -dump -cookie_file=/tmp/agent-cookies.txt https://example.com/dashboard
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
Note: curl uses Netscape cookie format natively. lynx requires Netscape format. w3m uses its own `~/.w3m/cookie` format. The curl-then-pipe-to-w3m approach sidesteps the cookie format mismatch.
|
|
392
|
-
|
|
393
|
-
### Pattern 3: Link Extraction and Graph Walking
|
|
394
|
-
|
|
395
|
-
```bash
|
|
396
|
-
#!/bin/bash
|
|
397
|
-
# Walk a site and extract all text - pure shell agent pattern
|
|
398
|
-
|
|
399
|
-
BASE_URL="$1"
|
|
400
|
-
DEPTH="${2:-2}"
|
|
401
|
-
VISITED_FILE=$(mktemp)
|
|
402
|
-
|
|
403
|
-
walk_url() {
|
|
404
|
-
local url="$1"
|
|
405
|
-
local depth="$2"
|
|
406
|
-
|
|
407
|
-
[[ $depth -le 0 ]] && return
|
|
408
|
-
grep -qF "$url" "$VISITED_FILE" && return
|
|
409
|
-
echo "$url" >> "$VISITED_FILE"
|
|
410
|
-
|
|
411
|
-
echo "=== $url ==="
|
|
412
|
-
w3m -dump "$url" 2>/dev/null
|
|
413
|
-
|
|
414
|
-
# Extract same-domain links
|
|
415
|
-
lynx -dump -listonly "$url" 2>/dev/null \
|
|
416
|
-
| grep -oP 'https?://[^\s]+' \
|
|
417
|
-
| grep "^${BASE_URL}" \
|
|
418
|
-
| while read -r link; do
|
|
419
|
-
walk_url "$link" $((depth - 1))
|
|
420
|
-
done
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
walk_url "$BASE_URL" "$DEPTH"
|
|
424
|
-
rm -f "$VISITED_FILE"
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
### Pattern 4: Form Submission with lynx cmd_script
|
|
428
|
-
|
|
429
|
-
```bash
|
|
430
|
-
#!/bin/bash
|
|
431
|
-
# Submit a search form non-interactively
|
|
432
|
-
|
|
433
|
-
# Write keystroke commands
|
|
434
|
-
SCRIPT=$(mktemp)
|
|
435
|
-
cat > "$SCRIPT" << 'EOF'
|
|
436
|
-
# Wait for page, find search box, type, submit
|
|
437
|
-
key Down
|
|
438
|
-
key Down
|
|
439
|
-
key Return
|
|
440
|
-
stuff "search query here"
|
|
441
|
-
key Return
|
|
442
|
-
EOF
|
|
443
|
-
|
|
444
|
-
lynx -cmd_script="$SCRIPT" \
|
|
445
|
-
-dump \
|
|
446
|
-
-accept_all_cookies \
|
|
447
|
-
-cookie_file=/tmp/cookies.txt \
|
|
448
|
-
"https://example.com/search"
|
|
449
|
-
|
|
450
|
-
rm -f "$SCRIPT"
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
### Pattern 5: carbonyl CDP + xargs (JS-heavy sites)
|
|
454
|
-
|
|
455
|
-
```bash
|
|
456
|
-
#!/bin/bash
|
|
457
|
-
# Start carbonyl headless CDP, scrape with curl + jq
|
|
458
|
-
|
|
459
|
-
# Start carbonyl with CDP (run in background)
|
|
460
|
-
carbonyl --headless --remote-debugging-port=9222 &
|
|
461
|
-
CARB_PID=$!
|
|
462
|
-
sleep 2 # wait for browser to start
|
|
463
|
-
|
|
464
|
-
# Get list of targets via CDP REST endpoint
|
|
465
|
-
TARGETS=$(curl -s http://localhost:9222/json)
|
|
466
|
-
TARGET_ID=$(echo "$TARGETS" | jq -r '.[0].id')
|
|
467
|
-
WS_URL=$(echo "$TARGETS" | jq -r '.[0].webSocketDebuggerUrl')
|
|
468
|
-
|
|
469
|
-
# Navigate via CDP WebSocket (using websocat or wscat)
|
|
470
|
-
echo '{"id":1,"method":"Page.navigate","params":{"url":"https://example.com"}}' \
|
|
471
|
-
| websocat "$WS_URL"
|
|
472
|
-
|
|
473
|
-
# Wait for load
|
|
474
|
-
sleep 2
|
|
475
|
-
|
|
476
|
-
# Extract text content
|
|
477
|
-
echo '{"id":2,"method":"Runtime.evaluate","params":{"expression":"document.body.innerText"}}' \
|
|
478
|
-
| websocat "$WS_URL"
|
|
479
|
-
|
|
480
|
-
kill $CARB_PID
|
|
481
|
-
```
|
|
482
|
-
|
|
483
|
-
### Pattern 6: HTML-to-Markdown via pandoc (agent-optimized output)
|
|
484
|
-
|
|
485
|
-
For agents that need markdown rather than raw text:
|
|
486
|
-
|
|
487
|
-
```bash
|
|
488
|
-
# Fetch HTML, convert to clean markdown
|
|
489
|
-
curl -s https://example.com \
|
|
490
|
-
| pandoc -f html -t markdown \
|
|
491
|
-
| sed '/^$/N;/^\n$/d' # remove excessive blank lines
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
Or using lynx dump as input to further processing:
|
|
495
|
-
```bash
|
|
496
|
-
lynx -dump -source https://example.com | pandoc -f html -t markdown
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
(`-source` gets raw HTML; `-dump` gets rendered text)
|
|
500
|
-
|
|
501
|
-
---
|
|
502
|
-
|
|
503
|
-
## Cookie and Session Management
|
|
504
|
-
|
|
505
|
-
| Tool | Cookie Storage | Format | Agent Notes |
|
|
506
|
-
|------|---------------|--------|-------------|
|
|
507
|
-
| lynx | `-cookie_file` flag | Netscape | Reusable across invocations with same file |
|
|
508
|
-
| w3m | `~/.w3m/cookie` | Custom | Shared across all w3m invocations |
|
|
509
|
-
| elinks | `~/.elinks/cookies.db` | SQLite | Persists automatically |
|
|
510
|
-
| browsh | Firefox profile | SQLite | Inherits full Firefox cookie handling |
|
|
511
|
-
| carbonyl | Chromium profile | SQLite | Full Chromium cookie API via CDP |
|
|
512
|
-
| curl | `-c`/`-b` flags | Netscape | Best for programmatic auth flows |
|
|
513
|
-
|
|
514
|
-
**Recommended agent session pattern**:
|
|
515
|
-
```bash
|
|
516
|
-
# Auth with curl (reliable POST handling)
|
|
517
|
-
curl -c /tmp/session.txt -b /tmp/session.txt \
|
|
518
|
-
--data "user=x&pass=y" https://example.com/login -s -L
|
|
519
|
-
|
|
520
|
-
# Read pages with lynx (using cookies from curl's jar)
|
|
521
|
-
# Note: requires Netscape format which curl produces natively
|
|
522
|
-
lynx -accept_all_cookies -cookie_file=/tmp/session.txt \
|
|
523
|
-
-dump https://example.com/protected-page
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
---
|
|
527
|
-
|
|
528
|
-
## Cross-Platform Compatibility Matrix
|
|
529
|
-
|
|
530
|
-
| Tool | Linux | macOS | Windows native | WSL2 | Docker |
|
|
531
|
-
|------|-------|-------|---------------|------|--------|
|
|
532
|
-
| lynx | Native | Homebrew | No | Yes | Yes |
|
|
533
|
-
| w3m | Native | Homebrew | No | Yes | Yes |
|
|
534
|
-
| links2 | Native | Homebrew | No | Yes | Yes |
|
|
535
|
-
| elinks | Native | Homebrew (old) | No | Yes | Yes |
|
|
536
|
-
| browsh | Native | Yes (needs Firefox) | No | Yes | `browsh/browsh` |
|
|
537
|
-
| carbonyl | Native | Experimental | No | Yes | `fathyb/carbonyl` |
|
|
538
|
-
|
|
539
|
-
**Windows note**: All these tools work well under WSL2. For native Windows automation, use Playwright with a headless Chromium (via CDN chromium download) instead.
|
|
540
|
-
|
|
541
|
-
**macOS note**: lynx via Homebrew works well. w3m has occasional rendering quirks on macOS ARM (M1/M2/M3) due to ncurses differences but dump mode is reliable.
|
|
542
|
-
|
|
543
|
-
---
|
|
544
|
-
|
|
545
|
-
## Common Pitfalls
|
|
546
|
-
|
|
547
|
-
| Pitfall | Why It Happens | How to Avoid |
|
|
548
|
-
|---------|---------------|--------------|
|
|
549
|
-
| Blank output from lynx -dump | SSL cert error silently discarded | Add `-ssl_conn_limit 0` or check SSL error |
|
|
550
|
-
| w3m hangs on slow sites | Default no timeout | Add timeout: `timeout 30 w3m -dump URL` |
|
|
551
|
-
| lynx cmd_script doesn't submit | Form field not focused correctly | Use `key Down` to navigate to field before `stuff` |
|
|
552
|
-
| Cookie jar format mismatch | lynx needs Netscape format, w3m uses its own | Use curl for auth, pipe HTML to w3m for rendering |
|
|
553
|
-
| carbonyl not rendering JS | Page needs longer wait after navigate | Add `sleep 3` or use CDP `Page.loadEventFired` event |
|
|
554
|
-
| Links from lynx -listonly have duplicates | Same URL appears in nav + content | `sort -u` the output |
|
|
555
|
-
| w3m renders nothing from HTTPS | Old w3m without OpenSSL support | Check `w3m -version` for SSL support; install w3m-img package |
|
|
556
|
-
| browsh Docker slow to start | Full Firefox init takes 10-30s | Pre-warm the container; keep it running between agent calls |
|
|
557
|
-
| elinks dumps nothing | `-dump` flag syntax differs by version | Try both `elinks -dump URL` and `elinks -dump 1 URL` |
|
|
558
|
-
| Encoding garbage in output | Default encoding mismatch | Add `-O UTF-8` (w3m) or `-display_charset UTF-8` (lynx) |
|
|
559
|
-
|
|
560
|
-
---
|
|
561
|
-
|
|
562
|
-
## Best Practices
|
|
563
|
-
|
|
564
|
-
1. **Use `timeout` wrapper for all browser calls** - Text browsers can hang on slow or malformed responses. Always wrap: `timeout 30 lynx -dump URL`.
|
|
565
|
-
|
|
566
|
-
2. **Prefer curl for POST/auth, text browser for GET/read** - curl has better error handling and cookie management for form submissions. Use text browsers only for reading.
|
|
567
|
-
|
|
568
|
-
3. **Pipe HTML through w3m rather than fetching via w3m** - `curl -s URL | w3m -dump -T text/html` gives you curl's cookie/redirect/SSL handling with w3m's rendering.
|
|
569
|
-
|
|
570
|
-
4. **Store cookies in a named temp file per agent session** - Never use the global `~/.w3m/cookie` or `~/.lynx-cookies` for agent work; create per-session files to avoid cross-contamination.
|
|
571
|
-
|
|
572
|
-
5. **Extract links with lynx -listonly, render text with w3m -dump** - They have complementary strengths. Using both in pipeline gives best results.
|
|
573
|
-
|
|
574
|
-
6. **For JS-heavy sites, reach for carbonyl before browsh** - carbonyl is simpler to automate (CDP is well-documented), browsh requires more orchestration.
|
|
575
|
-
|
|
576
|
-
7. **Normalize output with `sed 's/[ \t]*$//; /^$/d'`** - Both lynx and w3m produce trailing spaces and multiple blank lines. Clean up before passing to LLM context.
|
|
577
|
-
|
|
578
|
-
8. **Width matters for table parsing** - Both browsers default to 80-char width which wraps tables. Use `lynx -width=200` or `w3m -cols 200` for data-heavy pages.
|
|
579
|
-
|
|
580
|
-
9. **Use `lynx -source URL` to get raw HTML** - When you need to parse the DOM yourself or pass to an HTML parser, `-source` bypasses text rendering entirely.
|
|
581
|
-
|
|
582
|
-
10. **Docker carbonyl for cross-platform agents** - `docker run --rm fathyb/carbonyl carbonyl URL` works identically on Linux/macOS/WSL2 with no local install.
|
|
583
|
-
|
|
584
|
-
---
|
|
585
|
-
|
|
586
|
-
## Real-World AI Agent Examples
|
|
587
|
-
|
|
588
|
-
### Example 1: Simple Web Reader Agent (Claude + w3m)
|
|
589
|
-
|
|
590
|
-
A minimal agent that can "browse the web" via shell commands:
|
|
591
|
-
|
|
592
|
-
```bash
|
|
593
|
-
# The agent issues this command after deciding to fetch a URL:
|
|
594
|
-
RESULT=$(timeout 20 w3m -dump -O UTF-8 "https://en.wikipedia.org/wiki/Recursion" 2>&1)
|
|
595
|
-
# Then passes $RESULT as context in next LLM call
|
|
596
|
-
```
|
|
597
|
-
|
|
598
|
-
This pattern is used in lightweight agent frameworks where the shell is the only available tool. No Python, no Node.js.
|
|
599
|
-
|
|
600
|
-
### Example 2: HackerNews Scraper Agent
|
|
601
|
-
|
|
602
|
-
```bash
|
|
603
|
-
#!/bin/bash
|
|
604
|
-
# Scrape HN front page, extract titles and URLs
|
|
605
|
-
|
|
606
|
-
lynx -dump -listonly https://news.ycombinator.com \
|
|
607
|
-
| grep -E 'item\?id=' \
|
|
608
|
-
| sed 's/.*\(https:\/\/news.ycombinator.com\/item.*\)/\1/' \
|
|
609
|
-
| sort -u \
|
|
610
|
-
| head -30
|
|
611
|
-
```
|
|
612
|
-
|
|
613
|
-
### Example 3: Documentation Crawler for RAG
|
|
614
|
-
|
|
615
|
-
```bash
|
|
616
|
-
#!/bin/bash
|
|
617
|
-
# Crawl docs site and output markdown chunks for RAG indexing
|
|
618
|
-
|
|
619
|
-
DOC_BASE="https://docs.example.com"
|
|
620
|
-
|
|
621
|
-
lynx -dump -listonly "$DOC_BASE" \
|
|
622
|
-
| grep -oP 'https?://[^\s]+' \
|
|
623
|
-
| grep "^$DOC_BASE" \
|
|
624
|
-
| sort -u \
|
|
625
|
-
| while read -r url; do
|
|
626
|
-
echo "# SOURCE: $url"
|
|
627
|
-
w3m -dump "$url" 2>/dev/null \
|
|
628
|
-
| sed 's/^[[:space:]]*//; /^$/d'
|
|
629
|
-
echo ""
|
|
630
|
-
echo "---"
|
|
631
|
-
done
|
|
632
|
-
```
|
|
633
|
-
|
|
634
|
-
### Example 4: Form-Based Login Agent Pattern
|
|
635
|
-
|
|
636
|
-
```bash
|
|
637
|
-
#!/bin/bash
|
|
638
|
-
# Pattern used in CI agents that need to authenticate to web dashboards
|
|
639
|
-
|
|
640
|
-
SESSION=$(mktemp)
|
|
641
|
-
DASHBOARD_URL="https://app.example.com"
|
|
642
|
-
|
|
643
|
-
# Auth step: curl handles POST + redirects reliably
|
|
644
|
-
curl -s -L \
|
|
645
|
-
-c "$SESSION" \
|
|
646
|
-
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
647
|
-
--data-urlencode "email=agent@example.com" \
|
|
648
|
-
--data-urlencode "password=$SECRET_PASS" \
|
|
649
|
-
"$DASHBOARD_URL/login" > /dev/null
|
|
650
|
-
|
|
651
|
-
# Read step: w3m renders the authenticated page
|
|
652
|
-
curl -s -b "$SESSION" "$DASHBOARD_URL/reports" \
|
|
653
|
-
| w3m -dump -T text/html -cols 200
|
|
654
|
-
|
|
655
|
-
rm -f "$SESSION"
|
|
656
|
-
```
|
|
657
|
-
|
|
658
|
-
### Example 5: Agent Using carbonyl CDP via websocat
|
|
659
|
-
|
|
660
|
-
```bash
|
|
661
|
-
#!/bin/bash
|
|
662
|
-
# Fully scripted JS-capable page scraping without Python/Node.js
|
|
663
|
-
|
|
664
|
-
# Requires: carbonyl, websocat, jq
|
|
665
|
-
|
|
666
|
-
# Start carbonyl in background with CDP
|
|
667
|
-
carbonyl --headless=new --remote-debugging-port=9222 &
|
|
668
|
-
PID=$!
|
|
669
|
-
sleep 3
|
|
670
|
-
|
|
671
|
-
WS=$(curl -s http://localhost:9222/json | jq -r '.[0].webSocketDebuggerUrl')
|
|
672
|
-
|
|
673
|
-
# Navigate
|
|
674
|
-
echo '{"id":1,"method":"Page.navigate","params":{"url":"https://example.com"}}' \
|
|
675
|
-
| websocat -n "$WS" > /dev/null
|
|
676
|
-
|
|
677
|
-
sleep 2 # wait for JS to execute
|
|
678
|
-
|
|
679
|
-
# Extract rendered text
|
|
680
|
-
RESULT=$(echo '{"id":2,"method":"Runtime.evaluate","params":{"expression":"document.body.innerText","returnByValue":true}}' \
|
|
681
|
-
| websocat -n "$WS" \
|
|
682
|
-
| jq -r '.result.result.value')
|
|
683
|
-
|
|
684
|
-
echo "$RESULT"
|
|
685
|
-
kill $PID
|
|
686
|
-
```
|
|
687
|
-
|
|
688
|
-
---
|
|
689
|
-
|
|
690
|
-
## Tool Selection Decision Tree
|
|
691
|
-
|
|
692
|
-
```
|
|
693
|
-
Need to read a web page as an AI agent?
|
|
694
|
-
│
|
|
695
|
-
├─ Is the page mostly static HTML?
|
|
696
|
-
│ └─ YES → Use: w3m -dump URL OR lynx -dump URL
|
|
697
|
-
│ ├─ Need links extracted? → lynx -dump -listonly URL
|
|
698
|
-
│ ├─ Need tables? → w3m renders tables better
|
|
699
|
-
│ └─ Need raw HTML? → lynx -source URL
|
|
700
|
-
│
|
|
701
|
-
├─ Does the page require JavaScript?
|
|
702
|
-
│ └─ YES →
|
|
703
|
-
│ ├─ Want minimal setup? → browsh (Docker: browsh/browsh)
|
|
704
|
-
│ └─ Want CDP automation? → carbonyl (Docker: fathyb/carbonyl)
|
|
705
|
-
│
|
|
706
|
-
├─ Need to submit a form / authenticate?
|
|
707
|
-
│ └─ curl for POST + w3m/lynx for reading result pages
|
|
708
|
-
│
|
|
709
|
-
├─ Cross-platform (including Windows)?
|
|
710
|
-
│ └─ Use Docker: fathyb/carbonyl OR playwright-chromium
|
|
711
|
-
│
|
|
712
|
-
└─ Need markdown output for LLM context?
|
|
713
|
-
└─ curl URL | pandoc -f html -t markdown
|
|
714
|
-
OR lynx -source URL | pandoc -f html -t markdown
|
|
715
|
-
```
|
|
716
|
-
|
|
717
|
-
---
|
|
718
|
-
|
|
719
|
-
## Comparison Summary
|
|
720
|
-
|
|
721
|
-
| Feature | lynx | w3m | elinks | browsh | carbonyl |
|
|
722
|
-
|---------|------|-----|--------|--------|----------|
|
|
723
|
-
| Dump mode | `-dump` | `-dump` | `-dump` | Partial | `get-text` |
|
|
724
|
-
| JavaScript | No | No | Partial (SpiderMonkey) | Yes (Firefox) | Yes (Chromium) |
|
|
725
|
-
| Single command | Yes | Yes | Yes | No | Yes* |
|
|
726
|
-
| Cookie files | Yes (`-cookie_file`) | Auto (`~/.w3m/cookie`) | Auto | Firefox | Chromium |
|
|
727
|
-
| Form POST | Via cmd_script | Via `-post` | Via forms | Via protocol | Via CDP |
|
|
728
|
-
| CDP support | No | No | No | No | Yes |
|
|
729
|
-
| Link extraction | `-listonly` | Manual | Manual | N/A | Via CDP |
|
|
730
|
-
| Table rendering | Fair | Good | Good | Excellent | Excellent |
|
|
731
|
-
| Install size | ~2MB | ~2MB | ~3MB | ~100MB+ | ~300MB+ |
|
|
732
|
-
| Maintenance | Active | Active | Slow | Active | Active |
|
|
733
|
-
| Best for agents | Static pages | Static + HTML piping | Lua scripting | JS sites (simple) | JS sites (scriptable) |
|
|
734
|
-
|
|
735
|
-
*carbonyl `get-text` subcommand for simple cases
|
|
736
|
-
|
|
737
|
-
---
|
|
738
|
-
|
|
739
|
-
## Further Reading
|
|
740
|
-
|
|
741
|
-
| Resource | Type | Why Recommended |
|
|
742
|
-
|----------|------|-----------------|
|
|
743
|
-
| [lynx man page](https://lynx.invisible-island.net/lynx_help/lynx.1.html) | Official docs | Complete flag reference including `-dump`, `-cmd_script`, cookie flags |
|
|
744
|
-
| [w3m GitHub](https://github.com/tats/w3m) | Official source | Current w3m development, issue tracker, build options |
|
|
745
|
-
| [carbonyl GitHub (fathyb/carbonyl)](https://github.com/fathyb/carbonyl) | Official source | Installation, CLI reference, CDP usage examples |
|
|
746
|
-
| [browsh GitHub (browsh-org/browsh)](https://github.com/browsh-org/browsh) | Official source | Docker setup, stdin protocol documentation |
|
|
747
|
-
| [Chrome DevTools Protocol reference](https://chromedevtools.github.io/devtools-protocol/) | Spec | Full CDP API for driving carbonyl programmatically |
|
|
748
|
-
| [websocat GitHub](https://github.com/vi/websocat) | Tool | CLI WebSocket client for talking to CDP from shell |
|
|
749
|
-
| [elinks documentation](http://elinks.or.cz/documentation/) | Official docs | Lua scripting hooks reference |
|
|
750
|
-
| [Playwright CLI docs](https://playwright.dev/docs/cli) | Official docs | Alternative to carbonyl for JS automation with `codegen` |
|
|
751
|
-
| [HN: terminal browsers for web scraping](https://news.ycombinator.com/search?q=terminal+browser+scraping) | Community | Real-world agent usage patterns from developers |
|
|
752
|
-
|
|
753
|
-
---
|
|
754
|
-
|
|
755
|
-
## Self-Evaluation
|
|
756
|
-
|
|
757
|
-
```json
|
|
758
|
-
{
|
|
759
|
-
"coverage": 9,
|
|
760
|
-
"diversity": 8,
|
|
761
|
-
"examples": 9,
|
|
762
|
-
"accuracy": 8,
|
|
763
|
-
"gaps": [
|
|
764
|
-
"helix browser (newer, less documented)",
|
|
765
|
-
"gotty (terminal web app server, different use case)",
|
|
766
|
-
"curl + htmlq/pup for CSS selector extraction (complements text browsers)",
|
|
767
|
-
"Playwright MCP server as alternative to raw CDP"
|
|
768
|
-
],
|
|
769
|
-
"note": "WebFetch was unavailable; guide synthesized from training knowledge (cutoff Aug 2025). Verify carbonyl subcommand syntax against current release."
|
|
770
|
-
}
|
|
771
|
-
```
|
|
772
|
-
|
|
773
|
-
---
|
|
774
|
-
|
|
775
|
-
*Generated by /learn from training knowledge (40 source equivalents, cutoff Aug 2025).*
|
|
776
|
-
*See `resources/terminal-browsers-agent-automation-sources.json` for source metadata.*
|