Initial commit: Metro Warden TUI network operations center

This commit is contained in:
2026-03-22 21:33:40 -04:00
commit 98a17d9b7e
45 changed files with 4215 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
#!/usr/bin/env python3
"""
Metro Warden — entry point.
Usage:
python main.py [--debug] [--log-file PATH]
The application reads config/defaults.toml on startup and can be
overridden via CLI flags.
"""
from __future__ import annotations
import argparse
import logging
import sys
from pathlib import Path
def _configure_logging(level: str, log_file: str) -> None:
fmt = "%(asctime)s %(levelname)-8s %(name)s %(message)s"
handlers: list[logging.Handler] = [logging.StreamHandler(sys.stderr)]
if log_file:
handlers.append(logging.FileHandler(log_file))
logging.basicConfig(level=getattr(logging, level.upper(), logging.INFO), format=fmt, handlers=handlers)
def _parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
prog="metro-warden",
description="Metro Warden — Industrial Dark Network Operations Centre",
)
parser.add_argument(
"--debug",
action="store_true",
help="Enable DEBUG log level",
)
parser.add_argument(
"--log-file",
default="",
metavar="PATH",
help="Write logs to this file in addition to stderr",
)
parser.add_argument(
"--config",
default="",
metavar="PATH",
help="Path to TOML config file (default: config/defaults.toml)",
)
return parser.parse_args()
def _load_config(path: str) -> dict:
config_path = Path(path) if path else Path(__file__).parent / "config" / "defaults.toml"
if not config_path.exists():
return {}
try:
import toml
return toml.loads(config_path.read_text())
except Exception as exc:
logging.warning("could not load config %s: %s", config_path, exc)
return {}
def main() -> None:
args = _parse_args()
log_level = "DEBUG" if args.debug else "INFO"
_configure_logging(log_level, args.log_file)
raw_config = _load_config(args.config)
# Flatten config sections into state-key style for the app
config: dict = {}
polling = raw_config.get("polling", {})
display = raw_config.get("display", {})
for k, v in polling.items():
config[f"settings.{k}"] = v
for k, v in display.items():
config[f"settings.{k}"] = v
from core.app import MetroWarden
app = MetroWarden(config=config)
app.run()
if __name__ == "__main__":
main()