Coverage for src / lilbee / server / routes / setup.py: 100%
30 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-04-29 19:16 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-04-29 19:16 +0000
1"""Setup routes: status and bootstrap for optional runtime components.
3Currently exposes Playwright Chromium bootstrap (needed for /crawl). The
4bb-wq8g contract mirrors what the TUI does in ``TaskBarController.ensure_chromium``
5so the Obsidian plugin's Task Center can render a matching ``setup`` pill.
7Endpoints:
8 GET /setup/crawler/status → { installed, component, browsers_path }
9 POST /setup/crawler → text/event-stream of setup_start →
10 setup_progress → setup_done → done
11"""
13from __future__ import annotations
15import asyncio
16from collections.abc import AsyncGenerator
17from typing import Any
19from litestar import get, post
20from litestar.response import Stream
22from lilbee.crawler import (
23 bootstrap_chromium,
24 chromium_installed,
25 crawler_browsers_path,
26)
27from lilbee.server.handlers import SseStream, sse_done, sse_error
30@get("/setup/crawler/status")
31async def setup_crawler_status_route() -> dict[str, Any]:
32 """Return whether the Chromium browser is installed."""
33 return {
34 "installed": chromium_installed(),
35 "component": "chromium",
36 "browsers_path": str(crawler_browsers_path()),
37 }
40async def _bootstrap_crawler_stream() -> AsyncGenerator[str, None]:
41 sse = SseStream()
43 async def _run() -> None:
44 try:
45 await bootstrap_chromium(on_progress=sse.callback)
46 finally:
47 sse.queue.put_nowait(None)
49 task = asyncio.create_task(_run())
50 async for event in sse.drain(task, "Crawler setup stream"):
51 yield event
52 if task.done() and not task.cancelled():
53 exc = task.exception()
54 if exc is not None:
55 yield sse_error(str(exc))
56 return
57 yield sse_done({})
60@post("/setup/crawler")
61async def setup_crawler_route() -> Stream:
62 """Stream the Chromium bootstrap subprocess as SSE events."""
63 return Stream(_bootstrap_crawler_stream(), media_type="text/event-stream")
66__all__ = [
67 "setup_crawler_route",
68 "setup_crawler_status_route",
69]