"""Entry point. python -m wsai # mock pipeline (no deps, no keys) — runs a demo python -m wsai --voice # eyes-free voice loop demo (STT -> Brain -> TTS) python -m wsai --live # capture this screen + Claude eyes/brain python -m wsai --env # build from WSAI_* environment variables The mock run is bounded (a few frames + a scripted conversation) so it exits on its own; --live/--env run until Ctrl-C. """ from __future__ import annotations import argparse import asyncio import logging from .config import Settings from .factory import build async def _run(settings: Settings, demo: bool) -> None: if demo: # Bounded demo so CI / a quick check terminates. from .backends.mock import MockFrameSource, MockSTT from .pipeline import Pipeline pipe = build(settings) if pipe.source is not None: # keep eyes-free configs eyes-free pipe.source = MockFrameSource(interval=0.3, limit=4) if pipe.stt is not None: pipe.stt = MockSTT(interval=0.4) await pipe.run() return await build(settings).run() def main() -> None: ap = argparse.ArgumentParser(prog="wsai") ap.add_argument("--voice", action="store_true", help="eyes-free voice loop (STT -> Brain -> TTS)") ap.add_argument("--live", action="store_true", help="capture screen + Claude backends") ap.add_argument("--env", action="store_true", help="build from WSAI_* env vars") ap.add_argument("-v", "--verbose", action="store_true") args = ap.parse_args() logging.basicConfig( level=logging.DEBUG if args.verbose else logging.INFO, format="%(levelname)s %(name)s: %(message)s", ) if args.voice: settings, demo = Settings.voice(), True elif args.live: settings, demo = Settings.live(), False elif args.env: settings, demo = Settings.from_env(), False else: settings, demo = Settings.mock(), True try: asyncio.run(_run(settings, demo)) except KeyboardInterrupt: pass if __name__ == "__main__": main()