"""מעטפת-תשובה קנונית לכלי-ה-MCP (GAP-48 / INV-TOOL1). מקור-אמת יחיד לצורת-הפלט של כל כלי. כל כלי שעבר מיגרציה מחזיר מחרוזת-JSON אחידה ``{status, data, message}`` — לא list-לפעמים, string-לפעמים, ``{error}`` לפעמים. שלושת המצבים מובחנים מפורשות (INV-TOOL1): status — "ok" הצלחה עם payload ב-``data``. "empty" שאילתה תקינה שלא החזירה תוצאות (מובחן מ-error). "error" הקריאה נכשלה; ``message`` מסביר. data — ה-payload (list/dict/scalar) או null. message — הערה קריאה-לאדם (עברית), בעיקר ל-empty/error. מחליף את ה-``_ok``/``_err`` שהשתכפלו ב-5 קבצי-כלים עם 3 מוסכמות שונות (G2). צרכן-API ב-``web/`` שמעביר פלט-כלי ל-HTTP חייב **לפרק** את המעטפת (להחזיר ``data``) כדי לשמר את חוזה-ה-UI↔API (X6) — ראה ``envelope_unwrap``. """ from __future__ import annotations import json from typing import Any def _dump(obj: dict) -> str: return json.dumps(obj, ensure_ascii=False, indent=2, default=str) def ok(data: Any = None, message: str = "") -> str: """הצלחה עם payload.""" return _dump({"status": "ok", "data": data, "message": message}) def empty(message: str = "", data: Any = None) -> str: """שאילתה תקינה ללא תוצאות — מובחן מהצלחה (data לא-ריק) ומשגיאה.""" return _dump({"status": "empty", "data": [] if data is None else data, "message": message}) def err(message: str, *, data: Any = None) -> str: """כשל בקריאה.""" return _dump({"status": "error", "data": data, "message": message}) def envelope_unwrap(parsed: Any) -> Any: """פירוק מעטפת לצורך צרכן-API שמשמר חוזה-HTTP ישן. בהינתן dict-מעטפת מפורסר (``json.loads`` של פלט-כלי שעבר מיגרציה): status=="ok" → מחזיר ``data`` (ה-payload המקורי, למשל list). status in {empty,error}→ מחזיר ``{"message": message}`` (תאימות-לאחור לצורה שצרכני-ה-API החזירו על מחרוזת-פרוזה). קלט שאינו מעטפת (אין מפתח ``status``) מוחזר כמות-שהוא — בטוח לתקופת-המעבר שבה רק חלק מהכלים עברו מיגרציה. """ if isinstance(parsed, dict) and "status" in parsed and "data" in parsed: if parsed["status"] == "ok": return parsed["data"] return {"message": parsed.get("message", "")} return parsed