Files

86 lines
2.2 KiB
Python

"""
Operator guidance contracts used by frontend recovery UX.
"""
from dataclasses import dataclass
from enum import Enum
from typing import Any, Dict, Optional
class BannerSeverity(str, Enum):
INFO = "info"
WARNING = "warning"
ERROR = "error"
CRITICAL = "critical"
SUCCESS = "success"
@dataclass
class OperatorAction:
label: str
type: str # "url", "tab", "action"
payload: str # URL, tab ID, or action ID
def to_dict(self) -> Dict[str, str]:
return {"label": self.label, "type": self.type, "payload": self.payload}
@dataclass
class OperatorBanner:
"""
Standardized banner for operator guidance.
Idempotency: calculated from {source}:{id}.
"""
id: str
severity: BannerSeverity
message: str
source: str
ttl_ms: int = 0
action: Optional[Dict[str, str]] = None # Raw dict or OperatorAction
dismissible: bool = True
def to_dict(self) -> Dict[str, Any]:
data = {
"id": self.id,
"severity": self.severity.value,
"message": self.message,
"source": self.source,
"ttl_ms": self.ttl_ms,
"dismissible": self.dismissible,
"dedupe_key": f"{self.source}:{self.id}",
}
if self.action:
if isinstance(self.action, OperatorAction):
data["action"] = self.action.to_dict()
else:
data["action"] = self.action
return data
def resolve_deep_link(target: str, base_path: str = "") -> str:
"""
Resolve a deep link (e.g. openclaw://settings/api) to a deploy-relative URL.
Handles 'openclaw://' scheme and absolute paths.
"""
if not target:
return ""
# Strip scheme
if target.startswith("openclaw://"):
path = target[len("openclaw://") :]
# Ensure path starts with / if not empty
if path and not path.startswith("/"):
path = "/" + path
elif target.startswith("/"):
path = target
else:
# Relative path? Treat as relative to root
path = "/" + target
# Clean base_path (remove trailing slash)
base = base_path.rstrip("/")
# Construct result
return f"{base}{path}"