feat(01-03): add /archive command to compress and remove sessions
Archives session directory with tar+pigz to sessions_archive/, terminates any running subprocess first, clears active session if needed. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
3cc97adcd0
commit
a27ac010ec
2 changed files with 79 additions and 0 deletions
|
|
@ -140,6 +140,7 @@ async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||
*Claude Sessions:*
|
||||
/new <name> [persona] - Create new Claude session
|
||||
/session <name> - Switch to a session
|
||||
/archive <name> - Archive and remove a session
|
||||
|
||||
*Status & Monitoring:*
|
||||
/status - Quick service overview
|
||||
|
|
@ -407,6 +408,38 @@ async def switch_session_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE)
|
|||
logger.error(f"Error switching session: {e}")
|
||||
await update.message.reply_text(f"Error switching session: {e}")
|
||||
|
||||
async def archive_session_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"""Archive a Claude session (compress and remove)."""
|
||||
if not is_authorized(update.effective_user.id):
|
||||
return
|
||||
|
||||
if not context.args:
|
||||
await update.message.reply_text("Usage: /archive <name>")
|
||||
return
|
||||
|
||||
name = context.args[0]
|
||||
|
||||
try:
|
||||
# Terminate subprocess if running
|
||||
if name in subprocesses:
|
||||
if subprocesses[name].is_alive:
|
||||
await subprocesses[name].terminate()
|
||||
del subprocesses[name]
|
||||
|
||||
# Archive the session
|
||||
archive_path = session_manager.archive_session(name)
|
||||
size_mb = archive_path.stat().st_size / (1024 * 1024)
|
||||
await update.message.reply_text(
|
||||
f"Session '{name}' archived ({size_mb:.1f} MB)\n{archive_path.name}"
|
||||
)
|
||||
logger.info(f"Archived session '{name}' to {archive_path}")
|
||||
|
||||
except ValueError as e:
|
||||
await update.message.reply_text(str(e))
|
||||
except Exception as e:
|
||||
logger.error(f"Error archiving session: {e}")
|
||||
await update.message.reply_text(f"Error archiving session: {e}")
|
||||
|
||||
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"""Handle free text messages - route to active Claude session."""
|
||||
if not is_authorized(update.effective_user.id):
|
||||
|
|
@ -535,6 +568,7 @@ def main():
|
|||
app.add_handler(CommandHandler("chatid", chatid))
|
||||
app.add_handler(CommandHandler("new", new_session))
|
||||
app.add_handler(CommandHandler("session", switch_session_cmd))
|
||||
app.add_handler(CommandHandler("archive", archive_session_cmd))
|
||||
app.add_handler(CommandHandler("status", status))
|
||||
app.add_handler(CommandHandler("pbs", pbs))
|
||||
app.add_handler(CommandHandler("pbs_status", pbs_status))
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ Directory structure:
|
|||
import json
|
||||
import logging
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
|
@ -316,6 +318,49 @@ class SessionManager:
|
|||
"""
|
||||
return self.base_dir / name
|
||||
|
||||
def archive_session(self, name: str) -> Path:
|
||||
"""
|
||||
Archive a session by compressing it with tar+pigz and removing the original.
|
||||
|
||||
Args:
|
||||
name: Session name to archive
|
||||
|
||||
Returns:
|
||||
Path to the archive file
|
||||
|
||||
Raises:
|
||||
ValueError: If session does not exist
|
||||
"""
|
||||
if not self.session_exists(name):
|
||||
raise ValueError(f"Session '{name}' does not exist")
|
||||
|
||||
# Clear active session if archiving the active one
|
||||
if self.active_session == name:
|
||||
self.active_session = None
|
||||
|
||||
# Create archive directory
|
||||
archive_dir = self.base_dir.parent / "sessions_archive"
|
||||
archive_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Build archive filename with timestamp
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
archive_name = f"{name}_{timestamp}.tar.gz"
|
||||
archive_path = archive_dir / archive_name
|
||||
|
||||
# Compress with tar + pigz
|
||||
session_dir = self.base_dir / name
|
||||
subprocess.run(
|
||||
["tar", "--use-compress-program=pigz", "-cf", str(archive_path),
|
||||
"-C", str(self.base_dir), name],
|
||||
check=True,
|
||||
)
|
||||
|
||||
# Remove original session directory
|
||||
shutil.rmtree(session_dir)
|
||||
|
||||
logger.info(f"Archived session '{name}' to {archive_path}")
|
||||
return archive_path
|
||||
|
||||
def load_persona(self, name: str) -> dict:
|
||||
"""
|
||||
Load persona from library.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue