rename secrets.py to credential_store.py, enhance /status, add usage tracking

- Rename src/secrets.py → src/credential_store.py (avoid stdlib conflict)
- Enhanced /status command: uptime, tokens, cost, context window usage
- Session metadata now tracks input/output tokens, cost, duration
- _safe_env() changed from allowlist to blocklist approach
- Better Claude CLI error logging

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
MoltBot Service
2026-02-13 17:54:59 +00:00
parent 0ecfa630eb
commit 85c72e4b3d
7 changed files with 155 additions and 39 deletions

View File

@@ -483,23 +483,113 @@ def create_bot(config: Config) -> discord.Client:
@tree.command(name="status", description="Show session status")
async def status(interaction: discord.Interaction) -> None:
from datetime import datetime, timezone
import subprocess
channel_id = str(interaction.channel_id)
now = datetime.now(timezone.utc)
# Version info
try:
commit = subprocess.run(
["git", "log", "--format=%h", "-1"],
capture_output=True, text=True, cwd=str(PROJECT_ROOT),
).stdout.strip() or "?"
except Exception:
commit = "?"
# Latency
try:
lat = round(client.latency * 1000)
except (ValueError, TypeError):
lat = 0
# Uptime
uptime = ""
if hasattr(client, "_ready_at"):
elapsed = now - client._ready_at
secs = int(elapsed.total_seconds())
if secs < 60:
uptime = f"{secs}s"
elif secs < 3600:
uptime = f"{secs // 60}m"
else:
uptime = f"{secs // 3600}h {(secs % 3600) // 60}m"
# Channel count
channels_count = len(config.get("channels", {}))
# Session info
session = get_active_session(channel_id)
if session is None:
await interaction.response.send_message(
"No active session.", ephemeral=True
)
return
sid = session.get("session_id", "?")
truncated_sid = sid[:8] + "..." if len(sid) > 8 else sid
model = session.get("model", "?")
count = session.get("message_count", 0)
await interaction.response.send_message(
f"**Model:** {model}\n"
f"**Session:** `{truncated_sid}`\n"
f"**Messages:** {count}",
ephemeral=True,
)
if session:
sid = session.get("session_id", "?")[:8]
model = session.get("model", "?")
count = session.get("message_count", 0)
created = session.get("created_at", "")
last_msg = session.get("last_message_at", "")
age = ""
if created:
try:
el = now - datetime.fromisoformat(created)
m = int(el.total_seconds() // 60)
age = f"{m}m" if m < 60 else f"{m // 60}h {m % 60}m"
except (ValueError, TypeError):
pass
updated = ""
if last_msg:
try:
el = now - datetime.fromisoformat(last_msg)
s = int(el.total_seconds())
if s < 60:
updated = "just now"
elif s < 3600:
updated = f"{s // 60}m ago"
else:
updated = f"{s // 3600}h ago"
except (ValueError, TypeError):
pass
# Token usage
in_tok = session.get("total_input_tokens", 0)
out_tok = session.get("total_output_tokens", 0)
cost = session.get("total_cost_usd", 0)
def _fmt_tokens(n):
if n >= 1_000_000:
return f"{n / 1_000_000:.1f}M"
if n >= 1_000:
return f"{n / 1_000:.1f}k"
return str(n)
tokens_line = f"Tokens: {_fmt_tokens(in_tok)} in / {_fmt_tokens(out_tok)} out"
if cost > 0:
tokens_line += f" | ${cost:.4f}"
# Context window usage
ctx = session.get("context_tokens", 0)
max_ctx = 200_000
pct = round(ctx / max_ctx * 100) if ctx else 0
context_line = f"Context: {_fmt_tokens(ctx)}/{_fmt_tokens(max_ctx)} ({pct}%)"
session_line = f"Session: `{sid}` | {count} msgs | {age}" + (f" | updated {updated}" if updated else "")
else:
model = config.get("bot", {}).get("default_model", "?")
session_line = "No active session"
tokens_line = ""
context_line = ""
lines = [
f"Echo Core ({commit})",
f"Model: {model} | Latency: {lat}ms",
f"Channels: {channels_count} | Uptime: {uptime}",
tokens_line,
context_line,
session_line,
]
text = "\n".join(l for l in lines if l)
await interaction.response.send_message(text, ephemeral=True)
@tree.command(name="model", description="View or change the AI model")
@app_commands.describe(choice="Model to switch to")
@@ -610,6 +700,8 @@ def create_bot(config: Config) -> discord.Client:
scheduler = getattr(client, "scheduler", None)
if scheduler is not None:
await scheduler.start()
from datetime import datetime, timezone
client._ready_at = datetime.now(timezone.utc)
logger.info("Echo Core online as %s", client.user)
async def _handle_chat(message: discord.Message) -> None: