executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
headless-fallbackUse when a static fetch returns nothing useful and the page needs a real browser. Covers `--browser-mode auto|always|never`, external CDP via `--browser-endpoint`, symptoms of JS-only pages and WAF blocks, and the performance cost.
Independent GitHub snapshot · Automated classification · Original documentation governs
--browser-mode auto|always|never, external CDP via
--browser-endpoint, symptoms of JS-only pages and WAF blocks, and the
performance cost.Some pages are unscrapable without a real browser — SPA shells, infinite scroll, Cloudflare interstitials, JS-rendered article bodies. Kreuzcrawl ships with an optional headless-Chrome backend driven by chromiumoxide.
--browser-mode auto # default — try static first, fall back to browser on JS/WAF
--browser-mode always # skip static, go straight to browser
--browser-mode never # static only, fail closed
auto (default)The engine fetches statically, then inspects the response. It launches headless Chrome and re-fetches when it sees:
<noscript> warnings, near-empty <body> with heavy JS.This is the right default. The browser only spins up when needed.
alwaysSkip the static probe entirely. Use when:
<script>-emitted state that never lands in static HTML.kreuzcrawl scrape https://spa.example.com --browser-mode always --format markdown
neverStatic only — the browser path is disabled. Use when:
In never mode, JS-only pages return empty/stub content. Inspect
markdown.content and markdown.warnings before treating the result as
final.
In --browser-mode never or when you suspect the auto detector missed a
signal:
markdown.content is short, nav-only, or just a loading message.status_code is 200 but metadata.headings is empty on a page that
clearly has headings.markdown.warnings mentions JS-render-required or WAF detection.server: cloudflare,
cf-mitigated, x-amz-cf-id, set-cookie: __cf_bm=…).Re-run with --browser-mode always. If that succeeds, leave it set for
that host.
Point at an already-running Chrome (Browserless, Steel, your own) instead of launching locally:
kreuzcrawl scrape https://example.com \
--browser-mode always \
--browser-endpoint ws://browser.internal:9222/devtools/browser/<id> \
--format markdown
The endpoint must be a WebSocket URL — ws:// or wss://. The CLI
rejects anything else with a clear error.
Use external CDP when:
Headless Chrome is expensive relative to a static fetch:
NetworkIdle wait, plus the page's own
JS load time.--concurrent.Mitigations:
--browser-mode auto — the engine only pays the cost when it
needs to.--browser-endpoint to share one warm browser across jobs.--concurrent when you know the crawl will route through Chrome.Pass via --config JSON when you need control:
kreuzcrawl scrape https://example.com --browser-mode always \
--config '{"browser":{"wait_strategy":{"type":"Selector","selector":".article-body"}}}'
Supported strategies:
NetworkIdle (default) — wait until the network goes quiet.Selector — wait until a CSS selector resolves.Fixed — wait a fixed duration.extra_wait adds milliseconds on top of the wait strategy if the page
keeps loading content after the primary signal.
kreuzcrawl scrape https://app.example.com --browser-mode always \
--config '{"browser_profile":"prod","save_browser_profile":true}'
Profile names are path-traversal-validated. Use them to keep cookies, localStorage, and login state across runs without re-authenticating.