CI / Lint + tests (push) Has been cancelled
Ship a shared markdown/prompt core used by a Brave/Chrome MV3 extension and a Python automation API, with pytest coverage, CI, and packaging smoke.
75 lines
2.6 KiB
Python
75 lines
2.6 KiB
Python
"""Headless CLI: fetch a URL, capture console/network/errors, print an AI-ready prompt.
|
|
|
|
Usage:
|
|
context-extractor https://example.com
|
|
context-extractor https://example.com --selector "#main" --engine camoufox
|
|
context-extractor https://example.com --wait 2000 --out prompt.md
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import sys
|
|
|
|
from .session import ExtractorSession
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(prog="context-extractor", description=__doc__)
|
|
parser.add_argument("url", help="URL to load")
|
|
parser.add_argument("--selector", default=None, help="CSS selector to extract (default: body)")
|
|
parser.add_argument("--wait", type=int, default=0, help="Extra milliseconds to wait after load")
|
|
parser.add_argument(
|
|
"--max-chars", type=int, default=None,
|
|
help="Cap page content in the prompt to this many chars (default: 20000). "
|
|
"SPA pages can extract hundreds of thousands of chars; this keeps output LLM-sized.",
|
|
)
|
|
parser.add_argument(
|
|
"--engine", choices=["chromium", "firefox", "webkit", "camoufox"], default="chromium",
|
|
help="Which browser engine to drive (default: chromium)",
|
|
)
|
|
parser.add_argument("--headed", action="store_true", help="Show the browser window")
|
|
parser.add_argument("--out", default=None, help="Write the prompt to a file instead of stdout")
|
|
args = parser.parse_args()
|
|
|
|
if args.engine == "camoufox":
|
|
try:
|
|
from camoufox import Camoufox
|
|
except ImportError:
|
|
print(
|
|
"camoufox is not installed. Install with: pip install 'context-extractor[camoufox]'",
|
|
file=sys.stderr,
|
|
)
|
|
return 1
|
|
with Camoufox(headless=not args.headed) as browser:
|
|
page = browser.new_page()
|
|
prompt = _run(page, args)
|
|
else:
|
|
from playwright.sync_api import sync_playwright
|
|
|
|
with sync_playwright() as p:
|
|
browser = getattr(p, args.engine).launch(headless=not args.headed)
|
|
page = browser.new_page()
|
|
prompt = _run(page, args)
|
|
browser.close()
|
|
|
|
if args.out:
|
|
with open(args.out, "w", encoding="utf-8") as f:
|
|
f.write(prompt)
|
|
print(f"Wrote {len(prompt):,} chars to {args.out}", file=sys.stderr)
|
|
else:
|
|
print(prompt)
|
|
return 0
|
|
|
|
|
|
def _run(page, args) -> str:
|
|
session = ExtractorSession(page)
|
|
page.goto(args.url, wait_until="domcontentloaded")
|
|
if args.wait:
|
|
page.wait_for_timeout(args.wait)
|
|
return session.build_ai_prompt(args.selector, max_chars=args.max_chars)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|