99ec267c74
The dashboard runs under curses.wrapper and cmd_dashboard only caught KeyboardInterrupt, so failures vanished: - die() prints to stderr, but under curses that lands on the alternate screen and is wiped on exit, so config errors gave no reason. - Die is a SystemExit, so the new-agent flow's `except Exception` never caught config errors; they crashed the TUI. - the startup manifest probe was unguarded. Now: Die carries its message (+ log.error()); cmd_dashboard re-surfaces a Die's reason once the terminal is restored and writes any other crash's traceback to ~/.bot-bottle/logs/dashboard-crash.log; the startup probe and the new-agent flow degrade a bad config to a status-line warning instead of crashing. Closes #100 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
37 lines
953 B
Python
37 lines
953 B
Python
"""Tiny logging wrappers. All output goes to stderr."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from typing import NoReturn
|
|
|
|
|
|
def info(msg: str) -> None:
|
|
print(f"bot-bottle: {msg}", file=sys.stderr)
|
|
|
|
|
|
def warn(msg: str) -> None:
|
|
print(f"bot-bottle: warning: {msg}", file=sys.stderr)
|
|
|
|
|
|
def error(msg: str) -> None:
|
|
print(f"bot-bottle: error: {msg}", file=sys.stderr)
|
|
|
|
|
|
class Die(SystemExit):
|
|
"""Raised by die() so callers (and tests) can distinguish a deliberate
|
|
fatal exit from an unrelated SystemExit.
|
|
|
|
Carries the human-facing message so a caller that suppressed stderr
|
|
— e.g. the curses dashboard, whose alternate screen is wiped when the
|
|
terminal is restored — can re-surface the reason after the fact."""
|
|
|
|
def __init__(self, code: int = 1, message: str = "") -> None:
|
|
super().__init__(code)
|
|
self.message = message
|
|
|
|
|
|
def die(msg: str) -> NoReturn:
|
|
error(msg)
|
|
raise Die(1, msg)
|