Files
loyal_companion/cli/session.py
latte d957120eb3
All checks were successful
Enterprise AI Code Review / ai-review (pull_request) Successful in 38s
i forgot too commit
2026-02-01 15:57:45 +01:00

165 lines
4.1 KiB
Python

"""Session management for CLI."""
import json
import secrets
from dataclasses import asdict, dataclass
from datetime import datetime
from pathlib import Path
from typing import Any
@dataclass
class SessionData:
"""Local session data."""
session_id: str
name: str
created_at: str
last_active: str
message_count: int = 0
def to_dict(self) -> dict[str, Any]:
"""Convert to dictionary.
Returns:
dict: Session data as dictionary
"""
return asdict(self)
@classmethod
def from_dict(cls, data: dict[str, Any]) -> "SessionData":
"""Create from dictionary.
Args:
data: Dictionary data
Returns:
SessionData: Session instance
"""
return cls(**data)
class SessionManager:
"""Manages local CLI sessions."""
def __init__(self, sessions_file: Path):
"""Initialize session manager.
Args:
sessions_file: Path to sessions file
"""
self.sessions_file = sessions_file
self.sessions: dict[str, SessionData] = {}
self._load()
def _load(self) -> None:
"""Load sessions from file."""
if self.sessions_file.exists():
try:
with open(self.sessions_file, "r") as f:
data = json.load(f)
self.sessions = {
name: SessionData.from_dict(session_data) for name, session_data in data.items()
}
except (json.JSONDecodeError, IOError) as e:
print(f"Warning: Could not load sessions: {e}")
self.sessions = {}
def _save(self) -> None:
"""Save sessions to file."""
self.sessions_file.parent.mkdir(parents=True, exist_ok=True)
data = {name: session.to_dict() for name, session in self.sessions.items()}
with open(self.sessions_file, "w") as f:
json.dump(data, f, indent=2)
def create_session(self, name: str = "default") -> SessionData:
"""Create or get a session.
Args:
name: Session name
Returns:
SessionData: Created or existing session
"""
if name in self.sessions:
return self.sessions[name]
# Generate unique session ID
session_id = f"cli_{name}_{secrets.token_hex(8)}"
now = datetime.utcnow().isoformat()
session = SessionData(
session_id=session_id,
name=name,
created_at=now,
last_active=now,
message_count=0,
)
self.sessions[name] = session
self._save()
return session
def get_session(self, name: str) -> SessionData | None:
"""Get a session by name.
Args:
name: Session name
Returns:
SessionData | None: Session or None if not found
"""
return self.sessions.get(name)
def get_or_create_session(self, name: str = "default") -> SessionData:
"""Get or create a session.
Args:
name: Session name
Returns:
SessionData: Session
"""
session = self.get_session(name)
if session:
return session
return self.create_session(name)
def update_last_active(self, name: str) -> None:
"""Update session's last active time.
Args:
name: Session name
"""
if name in self.sessions:
self.sessions[name].last_active = datetime.utcnow().isoformat()
self.sessions[name].message_count += 1
self._save()
def list_sessions(self) -> list[SessionData]:
"""List all sessions.
Returns:
list[SessionData]: All sessions
"""
return sorted(self.sessions.values(), key=lambda s: s.last_active, reverse=True)
def delete_session(self, name: str) -> bool:
"""Delete a session.
Args:
name: Session name
Returns:
bool: True if deleted, False if not found
"""
if name in self.sessions:
del self.sessions[name]
self._save()
return True
return False