mirror of
https://github.com/rookiestar28/ComfyUI-OpenClaw.git
synced 2026-08-14 00:48:07 +00:00
86 lines
2.2 KiB
Python
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}"
|