diff --git a/amc_server/context.py b/amc_server/context.py index 57d0b8e..a75c083 100644 --- a/amc_server/context.py +++ b/amc_server/context.py @@ -1,3 +1,4 @@ +import secrets import shutil from pathlib import Path import threading @@ -68,3 +69,49 @@ _DISMISSED_MAX = 500 # Serialize state collection because it mutates session files/caches. _state_lock = threading.Lock() + +# Projects directory for spawning agents +PROJECTS_DIR = Path.home() / 'projects' + +# Default Zellij session for spawning +ZELLIJ_SESSION = 'infra' + +# Lock for serializing spawn operations (prevents Zellij race conditions) +_spawn_lock = threading.Lock() + +# Rate limiting: track last spawn time per project (prevents spam) +_spawn_timestamps: dict[str, float] = {} +SPAWN_COOLDOWN_SEC = 10.0 + +# Auth token for spawn endpoint +_auth_token: str = '' + + +def generate_auth_token(): + """Generate a one-time auth token for this server instance.""" + global _auth_token + _auth_token = secrets.token_urlsafe(32) + return _auth_token + + +def validate_auth_token(request_token: str) -> bool: + """Validate the Authorization header token.""" + return request_token == f'Bearer {_auth_token}' + + +def start_projects_watcher(): + """Start background thread to refresh projects cache every 5 minutes.""" + import logging + from amc_server.mixins.spawn import load_projects_cache + + def _watch_loop(): + import time + while True: + try: + time.sleep(300) + load_projects_cache() + except Exception: + logging.exception('Projects cache refresh failed') + + thread = threading.Thread(target=_watch_loop, daemon=True) + thread.start()