"""Tool: record/inspect tool health (DB-backed, replaces .a0/tool_registry.json).""" from __future__ import annotations import json import os from datetime import datetime from typing import Any, Dict from helpers.tool import Tool, Response from usr.plugins.a0_software_orchestrator.helpers.db_state_store import get_project_id, set_kv, get_kv from usr.plugins.a0_software_orchestrator.helpers.exceptions import PluginDBError def _resolve_name(project_name: str = "", project_root: str = "") -> str: if project_name: return project_name.strip() if project_root: return os.path.basename(project_root.rstrip("/")) return os.path.basename(os.getcwd()) def _now() -> str: return datetime.utcnow().isoformat() + "Z" class ToolRegistry(Tool): """Register, list, or deregister tools in `orch_kv["tool_registry"]`. Args (kwargs): project_name (str): Projektname (bevorzugt). project_root (str): Legacy – wird zu basename() aufgelöst. action (str): "register" (default), "list", "deregister". tool_name (str): Name des Tools (für register/deregister). status (str): Status – "ok", "degraded", "failed" (für register). details (str): Optionale Details (für register). """ async def execute( self, project_name: str = "", project_root: str = "", action: str = "list", tool_name: str = "", status: str = "ok", details: str = "", **kwargs, ): name = _resolve_name(project_name, project_root) try: pid = get_project_id(name) if pid is None: raise LookupError(f"project '{name}' is not registered") except (ValueError, LookupError) as e: return Response(message=f"ERROR: {e}", break_loop=False) except PluginDBError as e: return Response(message=f"Database error: {e}", break_loop=False) # Wir nutzen orch_kv mit key="tool_registry" als JSON-Blob reg: Dict[str, Any] = get_kv(pid, "tool_registry", default={"tools": {}}) if "tools" not in reg: reg = {"tools": {}} if action == "register": if not tool_name: return Response( message="ERROR: 'tool_name' required for action=register", break_loop=False, ) reg["tools"][tool_name] = { "status": status, "last_checked": _now(), } if details: reg["tools"][tool_name]["details"] = details set_kv(pid, "tool_registry", reg) return Response( message=f"Registered {tool_name!r} as {status!r} for project {name!r}.", break_loop=False, ) if action == "deregister": if not tool_name: return Response( message="ERROR: 'tool_name' required for action=deregister", break_loop=False, ) if tool_name in reg["tools"]: del reg["tools"][tool_name] set_kv(pid, "tool_registry", reg) return Response( message=f"Deregistered {tool_name!r} for project {name!r}.", break_loop=False, ) return Response( message=f"Tool {tool_name!r} not registered for project {name!r}.", break_loop=False, ) if action == "list": if not reg["tools"]: return Response( message=f"No tools registered for project {name!r}.", break_loop=False, ) return Response( message=json.dumps(reg, indent=2, ensure_ascii=False), break_loop=False, ) return Response( message=f"Unknown action {action!r}. Supported: register, deregister, list", break_loop=False, )