personal memory agent
at main 111 lines 3.1 kB view raw
1# SPDX-License-Identifier: AGPL-3.0-only 2# Copyright (c) 2026 sol pbc 3 4"""CLI entry points for Convey web interface.""" 5 6from __future__ import annotations 7 8import argparse 9import logging 10import os 11 12from flask import Flask 13 14from apps.events import discover_handlers, start_dispatcher, stop_dispatcher 15 16from .bridge import start_bridge, stop_bridge 17 18logger = logging.getLogger(__name__) 19 20 21def _resolve_config_password_hash() -> str: 22 """Return the configured Convey password hash from journal config.""" 23 from think.utils import get_config 24 25 try: 26 config = get_config() 27 convey_config = config.get("convey", {}) 28 return convey_config.get("password_hash", "") 29 except Exception: 30 return "" 31 32 33def run_service( 34 app: Flask, 35 *, 36 host: str = "0.0.0.0", 37 port: int, 38 debug: bool = False, 39 start_watcher: bool = True, 40) -> None: 41 """Run the Convey service, optionally starting the Cortex watcher.""" 42 43 if start_watcher: 44 # In debug mode with reloader, only start in child process 45 # In non-debug mode, always start (no reloader) 46 # WERKZEUG_RUN_MAIN is set to 'true' only in the child/main process 47 should_start = not debug or os.environ.get("WERKZEUG_RUN_MAIN") == "true" 48 if should_start: 49 # Discover and start event handlers before bridge 50 discover_handlers() 51 start_dispatcher() 52 logger.info("Starting Callosum bridge") 53 start_bridge() 54 else: 55 logger.debug("Skipping bridge start in reloader parent process") 56 57 try: 58 app.run(host=host, port=port, debug=debug) 59 finally: 60 stop_bridge() 61 stop_dispatcher() 62 63 64def main() -> None: 65 """Main CLI entry point for convey command.""" 66 from pathlib import Path 67 68 from think.utils import ( 69 get_journal, 70 setup_cli, 71 write_service_port, 72 ) 73 74 from . import create_app 75 from .maint import run_pending_tasks 76 77 parser = argparse.ArgumentParser(description="Convey web interface") 78 parser.add_argument( 79 "--port", 80 type=int, 81 required=True, 82 help="Port to serve on", 83 ) 84 parser.add_argument( 85 "--skip-maint", 86 action="store_true", 87 help="Skip running pending maintenance tasks", 88 ) 89 args = setup_cli(parser) 90 journal = get_journal() 91 92 # Run pending maintenance tasks before starting 93 if not args.skip_maint: 94 ran, succeeded = run_pending_tasks(Path(journal)) 95 if ran > 0: 96 logger.info(f"Completed {succeeded}/{ran} maintenance task(s)") 97 98 app = create_app(journal) 99 password = _resolve_config_password_hash() 100 if password: 101 logger.info("Password authentication enabled") 102 else: 103 logger.warning( 104 "No password configured - run 'sol password set' to enable authentication" 105 ) 106 107 # Write port to health directory for discovery by other tools 108 write_service_port("convey", args.port) 109 logger.info(f"Convey starting on port {args.port}") 110 111 run_service(app, host="0.0.0.0", port=args.port, debug=args.debug)