88 lines
2.8 KiB
Python
88 lines
2.8 KiB
Python
|
|
"""Source discovery helpers for local agent session providers."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import os
|
||
|
|
from datetime import UTC, datetime
|
||
|
|
from pathlib import Path
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
from app.services import codex_session_reader
|
||
|
|
|
||
|
|
|
||
|
|
def _claude_projects_dir() -> Path:
|
||
|
|
override = os.environ.get("CLAUDE_PROJECTS_PATH", "").strip()
|
||
|
|
if override:
|
||
|
|
return Path(override)
|
||
|
|
return Path.home() / ".claude" / "projects"
|
||
|
|
|
||
|
|
|
||
|
|
def _claude_source_card(scanned_at: datetime) -> dict[str, Any]:
|
||
|
|
root = _claude_projects_dir()
|
||
|
|
files = (
|
||
|
|
sorted(root.rglob("*.jsonl"), key=lambda path: path.stat().st_mtime, reverse=True)
|
||
|
|
if root.exists()
|
||
|
|
else []
|
||
|
|
)
|
||
|
|
last_activity = None
|
||
|
|
if files:
|
||
|
|
last_activity = datetime.fromtimestamp(files[0].stat().st_mtime, UTC).replace(tzinfo=None)
|
||
|
|
return {
|
||
|
|
"source": "claude_code",
|
||
|
|
"provider_label": "Claude Code",
|
||
|
|
"source_status": "available" if files else "unavailable",
|
||
|
|
"source_path": str(root),
|
||
|
|
"session_count": len(files),
|
||
|
|
"last_activity_at": last_activity,
|
||
|
|
"last_scanned_at": scanned_at,
|
||
|
|
"unavailable_reason": (
|
||
|
|
None
|
||
|
|
if files
|
||
|
|
else "Claude Code session history was not found or contains no readable sessions."
|
||
|
|
),
|
||
|
|
"setup_hint": (
|
||
|
|
None
|
||
|
|
if files
|
||
|
|
else "Run Claude Code locally, or set CLAUDE_PROJECTS_PATH to a readable projects directory."
|
||
|
|
),
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def list_sources() -> list[dict[str, Any]]:
|
||
|
|
"""Return availability cards for supported agent session sources."""
|
||
|
|
scanned_at = datetime.now(UTC).replace(tzinfo=None)
|
||
|
|
|
||
|
|
codex = codex_session_reader.source_metadata()
|
||
|
|
|
||
|
|
openai_reason = "Pipeline does not yet have an owned OpenAI API session event source."
|
||
|
|
openai_hint = (
|
||
|
|
"Connect gateway logs, stored API traces, an import file, or a future collector before "
|
||
|
|
"OpenAI API history can be shown."
|
||
|
|
)
|
||
|
|
|
||
|
|
return [
|
||
|
|
_claude_source_card(scanned_at),
|
||
|
|
{
|
||
|
|
"source": codex.source,
|
||
|
|
"provider_label": codex.provider_label,
|
||
|
|
"source_status": codex.source_status,
|
||
|
|
"source_path": codex.source_path,
|
||
|
|
"session_count": codex.session_count,
|
||
|
|
"last_activity_at": codex.last_activity_at,
|
||
|
|
"last_scanned_at": codex.last_scanned_at,
|
||
|
|
"unavailable_reason": codex.unavailable_reason,
|
||
|
|
"setup_hint": codex.setup_hint,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"source": "openai_api",
|
||
|
|
"provider_label": "OpenAI API",
|
||
|
|
"source_status": "unavailable",
|
||
|
|
"source_path": None,
|
||
|
|
"session_count": 0,
|
||
|
|
"last_activity_at": None,
|
||
|
|
"last_scanned_at": scanned_at,
|
||
|
|
"unavailable_reason": openai_reason,
|
||
|
|
"setup_hint": openai_hint,
|
||
|
|
},
|
||
|
|
]
|