From fa5a869401838197161ef0a41f483cc00d05d4e7 Mon Sep 17 00:00:00 2001 From: "Matt Johnson (via Claude)" Date: Sun, 7 Jun 2026 16:50:45 +0000 Subject: [PATCH] feat(dashboard): add debug endpoint to clear dispatcher cooldowns POST /api/debug/clear-cooldowns clears both in-memory toggle cooldown map and SQLite dispatcher_cooldowns table. Co-Authored-By: Claude Opus 4.6 --- meshai/dashboard/api/debug_routes.py | 33 ++++++++++++++++++++++++++++ meshai/dashboard/server.py | 3 +++ 2 files changed, 36 insertions(+) create mode 100644 meshai/dashboard/api/debug_routes.py diff --git a/meshai/dashboard/api/debug_routes.py b/meshai/dashboard/api/debug_routes.py new file mode 100644 index 0000000..e4f83d3 --- /dev/null +++ b/meshai/dashboard/api/debug_routes.py @@ -0,0 +1,33 @@ +"""Debug API endpoints for development/ops use.""" +import logging +from fastapi import APIRouter, Request + +logger = logging.getLogger(__name__) +router = APIRouter(tags=["debug"]) + + +@router.post("/debug/clear-cooldowns") +async def clear_cooldowns(request: Request): + """Delete all dispatcher cooldown state (in-memory + SQLite).""" + # Get dispatcher from pipeline components on the event bus + bus = getattr(request.app.state, "bus", None) + if bus is None: + return {"cleared": False, "error": "event bus not available"} + comps = getattr(bus, "_pipeline_components", None) or {} + dispatcher = comps.get("dispatcher") + if dispatcher is None: + return {"cleared": False, "error": "dispatcher not available"} + + # Clear in-memory cooldown map + dispatcher._toggle_cooldown.clear() + + # Clear SQLite cooldowns table + try: + from meshai.persistence import get_db + conn = get_db() + conn.execute("DELETE FROM dispatcher_cooldowns") + logger.info("debug: cleared all dispatcher cooldowns") + except Exception: + logger.exception("debug: failed to clear dispatcher_cooldowns table") + + return {"cleared": True} diff --git a/meshai/dashboard/server.py b/meshai/dashboard/server.py index 9359961..a5a396f 100644 --- a/meshai/dashboard/server.py +++ b/meshai/dashboard/server.py @@ -55,6 +55,7 @@ def create_app() -> FastAPI: from .api.env_routes import router as env_router from .api.alert_routes import router as alert_router from .api.notification_routes import router as notification_router + from .api.debug_routes import router as debug_router app.include_router(system_router, prefix="/api") app.include_router(adapter_config_router, prefix="/api") @@ -65,6 +66,7 @@ def create_app() -> FastAPI: app.include_router(alert_router, prefix="/api") app.include_router(notification_router, prefix="/api") + app.include_router(debug_router, prefix="/api") # WebSocket router (no prefix, path is /ws/live) app.include_router(ws_router) @@ -118,6 +120,7 @@ async def start_dashboard(meshai_instance: "MeshAI") -> DashboardBroadcaster: app.state.subscription_manager = meshai_instance.subscription_manager app.state.notification_router = getattr(meshai_instance, "notification_router", None) app.state.connector = meshai_instance.connector + app.state.bus = getattr(meshai_instance, "event_bus", None) # Create broadcaster and attach to app state broadcaster = DashboardBroadcaster()