# -*- coding: utf-8 -*- """Minimal supervisor for scripted tasks and long-running utilities.""" from datetime import datetime from typing import Any, Dict, Iterable, Optional from .models import ProcessRun, ProcessRunState from .registry import RunRegistry class ProcessSupervisor: """Tracks supervised runs without executing real processes yet.""" def __init__(self, registry: Optional[RunRegistry] = None) -> None: self.registry = registry or RunRegistry() def spawn( self, run_id: str, command: str, scope_key: str, metadata: Optional[Dict[str, Any]] = None, ) -> ProcessRun: run = ProcessRun( run_id=run_id, command=command, scope_key=scope_key, metadata=metadata or {}, ) run.state = ProcessRunState.RUNNING run.updated_at = datetime.utcnow() self.registry.add(run) return run def update_state( self, run_id: str, state: ProcessRunState, metadata: Optional[Dict[str, Any]] = None, ) -> Optional[ProcessRun]: run = self.registry.get(run_id) if not run: return None run.state = state run.metadata.update(metadata or {}) run.updated_at = datetime.utcnow() self.registry.update(run) return run def cancel(self, run_id: str, reason: Optional[str] = None) -> Optional[ProcessRun]: run = self.registry.get(run_id) if not run: return None run.state = ProcessRunState.CANCELLED run.metadata.setdefault("cancel_reason", reason or "manual") run.updated_at = datetime.utcnow() self.registry.update(run) return run def list_runs(self) -> Iterable[ProcessRun]: return self.registry.list()