Split the 860+ line bin/amc-server into a modular Python package:
amc_server/
__init__.py - Package marker
context.py - Shared constants (DATA_DIR, PORT, CLAUDE_PROJECTS_DIR, etc.)
handler.py - AMCHandler class using mixin composition
logging_utils.py - Structured logging setup with signal handlers
server.py - Main entry point (ThreadingHTTPServer)
mixins/
__init__.py - Mixin package marker
control.py - Session control (dismiss, respond via Zellij)
conversation.py - Conversation history parsing (Claude JSONL format)
discovery.py - Session discovery (Codex pane inspection, Zellij cache)
http.py - HTTP response helpers (CORS, JSON, static files)
parsing.py - Session state parsing and aggregation
state.py - Session state endpoint logic
The monolithic bin/amc-server becomes a thin launcher that just imports
and calls main(). This separation enables:
- Easier testing of individual components
- Better IDE support (proper Python package structure)
- Cleaner separation of concerns (discovery vs parsing vs control)
- ThreadingHTTPServer instead of single-threaded (handles concurrent requests)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
48 lines
1.7 KiB
Python
48 lines
1.7 KiB
Python
from pathlib import Path
|
|
import threading
|
|
|
|
# Claude Code conversation directory
|
|
CLAUDE_PROJECTS_DIR = Path.home() / ".claude" / "projects"
|
|
|
|
# Codex conversation directory
|
|
CODEX_SESSIONS_DIR = Path.home() / ".codex" / "sessions"
|
|
|
|
# Plugin path for zellij-send-keys
|
|
ZELLIJ_PLUGIN = Path.home() / ".config" / "zellij" / "plugins" / "zellij-send-keys.wasm"
|
|
|
|
# Runtime data lives in XDG data dir
|
|
DATA_DIR = Path.home() / ".local" / "share" / "amc"
|
|
SESSIONS_DIR = DATA_DIR / "sessions"
|
|
EVENTS_DIR = DATA_DIR / "events"
|
|
|
|
# Source files live in project directory (relative to this module)
|
|
PROJECT_DIR = Path(__file__).resolve().parent.parent
|
|
DASHBOARD_DIR = PROJECT_DIR / "dashboard"
|
|
|
|
PORT = 7400
|
|
STALE_EVENT_AGE = 86400 # 24 hours in seconds
|
|
STALE_STARTING_AGE = 3600 # 1 hour - sessions stuck in "starting" are orphans
|
|
CODEX_ACTIVE_WINDOW = 600 # 10 minutes - only discover recently-active Codex sessions
|
|
|
|
# Cache for Zellij session list (avoid calling zellij on every request)
|
|
_zellij_cache = {"sessions": None, "expires": 0}
|
|
|
|
# Cache for Codex pane info (avoid running pgrep/ps/lsof on every request)
|
|
_codex_pane_cache = {"pid_info": {}, "cwd_map": {}, "expires": 0}
|
|
|
|
# Cache for parsed context usage by transcript file path + mtime/size
|
|
# Limited to prevent unbounded memory growth
|
|
_context_usage_cache = {}
|
|
_CONTEXT_CACHE_MAX = 100
|
|
|
|
# Cache mapping Codex session IDs to transcript paths (or None when missing)
|
|
_codex_transcript_cache = {}
|
|
_CODEX_CACHE_MAX = 200
|
|
|
|
# Codex sessions dismissed during this server lifetime (prevents re-discovery)
|
|
_dismissed_codex_ids = set()
|
|
_DISMISSED_MAX = 500
|
|
|
|
# Serialize state collection because it mutates session files/caches.
|
|
_state_lock = threading.Lock()
|