mirror of
https://github.com/rookiestar28/ComfyUI-OpenClaw.git
synced 2026-08-14 00:48:07 +00:00
156 lines
5.2 KiB
Python
156 lines
5.2 KiB
Python
"""
|
|
Capabilities Service (R19).
|
|
Provides capability probing for frontend version compatibility.
|
|
"""
|
|
|
|
import os
|
|
|
|
if __package__ and "." in __package__:
|
|
from ..config import PACK_NAME, PACK_VERSION
|
|
else: # pragma: no cover (test-only import mode)
|
|
from config import PACK_NAME, PACK_VERSION
|
|
|
|
try:
|
|
if __package__ and "." in __package__:
|
|
from ..services.pnginfo import pnginfo_available
|
|
else: # pragma: no cover (test-only import mode)
|
|
from services.pnginfo import pnginfo_available # type: ignore
|
|
except Exception: # pragma: no cover
|
|
|
|
def pnginfo_available() -> bool:
|
|
return False
|
|
|
|
|
|
from .runtime_profile import get_runtime_profile
|
|
|
|
API_VERSION = 1
|
|
|
|
|
|
def _get_installed_posture():
|
|
try:
|
|
from .effective_security_posture import get_effective_security_posture
|
|
|
|
return get_effective_security_posture(required=False)
|
|
except ImportError:
|
|
return None
|
|
|
|
|
|
def _get_control_plane_info() -> dict:
|
|
"""Build control-plane status for capabilities response."""
|
|
try:
|
|
import os
|
|
|
|
from .control_plane import get_blocked_surfaces, resolve_control_plane_mode
|
|
|
|
posture = _get_installed_posture()
|
|
profile = (
|
|
posture.deployment_profile
|
|
if posture is not None
|
|
else os.environ.get("OPENCLAW_DEPLOYMENT_PROFILE", "local")
|
|
)
|
|
mode = resolve_control_plane_mode(profile, posture=posture)
|
|
blocked = get_blocked_surfaces(profile, mode, posture=posture)
|
|
info = {
|
|
"mode": mode.value,
|
|
"blocked_surfaces": [sid for sid, _ in blocked],
|
|
}
|
|
# Include adapter health in split mode
|
|
if mode.value == "split":
|
|
try:
|
|
from .control_plane_adapter import ControlPlaneAdapter
|
|
|
|
adapter = ControlPlaneAdapter.from_env()
|
|
info["adapter_health"] = adapter.get_health()
|
|
except Exception:
|
|
info["adapter_health"] = {"configured": False}
|
|
return info
|
|
except Exception:
|
|
return {"mode": "embedded", "blocked_surfaces": []}
|
|
|
|
|
|
def get_capabilities() -> dict:
|
|
"""
|
|
Return capability surface for frontend probing.
|
|
"""
|
|
cp_info = _get_control_plane_info()
|
|
posture = _get_installed_posture()
|
|
result = {
|
|
"api_version": API_VERSION,
|
|
"runtime_profile": (
|
|
posture.runtime_profile
|
|
if posture is not None
|
|
else get_runtime_profile().value
|
|
),
|
|
"control_plane": cp_info,
|
|
"pack": {
|
|
"name": PACK_NAME,
|
|
"version": PACK_VERSION,
|
|
},
|
|
"features": {
|
|
"webhook_submit": True,
|
|
"logs_tail": True,
|
|
# Legacy flag (kept for older frontends/tests).
|
|
# Do not remove without a migration window + frontend update.
|
|
"doctor": True,
|
|
"job_monitor": True,
|
|
"callback_delivery": True,
|
|
"presets": True,
|
|
"approvals": True,
|
|
"assist_planner": True,
|
|
"assist_refiner": True,
|
|
"assist_streaming": True, # R38 optional SSE-style assist streaming path
|
|
"assist_automation_compose": True,
|
|
"scheduler": True,
|
|
"triggers": True,
|
|
"packs": True,
|
|
# R42/F28/R47: Explorer + Preflight + Checkpoints
|
|
"explorer": True,
|
|
"preflight": True,
|
|
"checkpoints": True,
|
|
"rewrite_recipes": True, # F53
|
|
"model_manager": True, # F54
|
|
# R70/F39/R73: Settings contract + UX degradation + Provider governance
|
|
"settings_contract": True,
|
|
"provider_governance": True,
|
|
# F40/R71/R72: Webhook mapping + Job events + Operator doctor
|
|
"webhook_mapping": True,
|
|
"job_events": True,
|
|
"operator_doctor": True,
|
|
"png_info": pnginfo_available(),
|
|
},
|
|
}
|
|
|
|
# F51: Action Capability Matrix
|
|
# H2 (F55): Cross-match blocked surfaces with action names for frontend UX
|
|
_surface_action_map = {
|
|
"webhook_execute": "queue",
|
|
"callback_egress": "queue",
|
|
"secrets_write": "settings",
|
|
"tool_execution": "doctor_fix",
|
|
}
|
|
blocked_surfaces = set(cp_info.get("blocked_surfaces", []))
|
|
|
|
base_actions = {
|
|
"doctor": {"enabled": True, "mutating": False},
|
|
"doctor_fix": {"enabled": True, "mutating": True},
|
|
"inspect": {"enabled": True, "mutating": False},
|
|
"queue": {"enabled": True, "mutating": False},
|
|
"settings": {"enabled": True, "mutating": False},
|
|
"install_node": {"enabled": False, "mutating": True},
|
|
"update_pack": {"enabled": False, "mutating": True},
|
|
}
|
|
|
|
for surface_id, action_name in _surface_action_map.items():
|
|
if surface_id in blocked_surfaces and action_name in base_actions:
|
|
base_actions[action_name] = {
|
|
**base_actions[action_name],
|
|
"enabled": False,
|
|
"blocked_reason": (
|
|
f"Surface '{surface_id}' is blocked in split mode. "
|
|
"Use the external control plane or switch to local profile."
|
|
),
|
|
}
|
|
|
|
result["actions"] = base_actions
|
|
return result
|