4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
148 lines
5.0 KiB
Python
148 lines
5.0 KiB
Python
"""Live system state helpers for the agent ready-state banner.
|
|
|
|
Queries integration health and alert-listener config without making
|
|
network calls — results are cached-once per banner render and used by
|
|
:func:`interactive_shell.ui.banner.banner._build_ambient_right_column`.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from rich.text import Text
|
|
|
|
from platform.terminal.theme import BRAND, DIM, HIGHLIGHT, SECONDARY, WARNING
|
|
|
|
# Display-name overrides for known integration service slugs.
|
|
_SERVICE_DISPLAY_NAMES: dict[str, str] = {
|
|
"grafana": "Grafana",
|
|
"datadog": "Datadog",
|
|
"honeycomb": "Honeycomb",
|
|
"coralogix": "Coralogix",
|
|
"aws": "AWS",
|
|
"github": "GitHub",
|
|
"sentry": "Sentry",
|
|
"prometheus": "Prometheus",
|
|
"loki": "Loki",
|
|
"elasticsearch": "Elasticsearch",
|
|
"bigquery": "BigQuery",
|
|
"pagerduty": "PagerDuty",
|
|
"slack": "Slack",
|
|
"telegram": "Telegram",
|
|
"signoz": "SigNoz",
|
|
"jira": "Jira",
|
|
"gitlab": "GitLab",
|
|
"vercel": "Vercel",
|
|
"mongodb": "MongoDB",
|
|
"postgresql": "PostgreSQL",
|
|
"mysql": "MySQL",
|
|
"redis": "Redis",
|
|
"kafka": "Kafka",
|
|
"rabbitmq": "RabbitMQ",
|
|
"clickhouse": "ClickHouse",
|
|
"mariadb": "MariaDB",
|
|
"kubernetes": "Kubernetes",
|
|
"betterstack": "Better Stack",
|
|
"snowflake": "Snowflake",
|
|
"newrelic": "New Relic",
|
|
"opsgenie": "OpsGenie",
|
|
"linear": "Linear",
|
|
"supabase": "Supabase",
|
|
}
|
|
|
|
|
|
def integration_display_name(service: str) -> str:
|
|
"""Human-readable label for an integration service slug."""
|
|
return _SERVICE_DISPLAY_NAMES.get(service, service.replace("_", " ").title())
|
|
|
|
|
|
def _load_integration_health() -> list[tuple[str, str]]:
|
|
"""Return ``(display_name, status)`` for each configured integration.
|
|
|
|
``status`` is ``"ok"`` or ``"incomplete"`` (e.g. a hosted MCP record saved
|
|
without an API token). Offline and best-effort: never raises and never makes
|
|
network calls, so the banner reflects health without slowing startup.
|
|
"""
|
|
try:
|
|
from integrations.catalog import ( # lazy — avoids circular deps
|
|
configured_integration_health,
|
|
)
|
|
|
|
return [
|
|
(integration_display_name(service), status)
|
|
for service, status in configured_integration_health()
|
|
]
|
|
except Exception:
|
|
return []
|
|
|
|
|
|
def _is_alert_listener_active() -> bool:
|
|
"""Return True if the alert listener is enabled in config. Never raises."""
|
|
try:
|
|
from config.repl_config import ReplConfig
|
|
|
|
return ReplConfig.load(apply_active_theme=False).alert_listener_enabled
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
def _build_ambient_right_column(session: object = None) -> Text:
|
|
"""Right column for returning users: live integration status and alert listener state."""
|
|
parts: list[Text] = []
|
|
|
|
# Integrations — annotate by offline health so the banner never implies a
|
|
# half-configured integration (e.g. a hosted MCP record with no API token)
|
|
# is connected. A "⚠" + dim name marks an integration missing credentials.
|
|
parts.append(Text("Integrations", style=f"bold {BRAND}"))
|
|
entries = _load_integration_health()
|
|
if entries:
|
|
_MAX_SHOWN = 6
|
|
shown = entries[:_MAX_SHOWN]
|
|
overflow = len(entries) - len(shown)
|
|
name_line = Text(overflow="fold")
|
|
for idx, (name, status) in enumerate(shown):
|
|
if idx:
|
|
name_line.append(" · ", style=DIM)
|
|
if status == "incomplete":
|
|
name_line.append(f"{name} ⚠", style=DIM)
|
|
else:
|
|
name_line.append(name, style=SECONDARY)
|
|
if overflow:
|
|
name_line.append(f" +{overflow}", style=DIM)
|
|
parts.append(name_line)
|
|
if any(status == "incomplete" for _name, status in entries):
|
|
parts.append(Text("⚠ incomplete — run /integrations verify", style=WARNING))
|
|
else:
|
|
parts.append(Text("run /onboard to connect tools", style=DIM))
|
|
|
|
parts.append(Text("───", style=DIM))
|
|
|
|
# Alert listener
|
|
parts.append(Text("Alert listener", style=f"bold {BRAND}"))
|
|
if _is_alert_listener_active():
|
|
listener_line = Text()
|
|
listener_line.append("● ", style=f"bold {HIGHLIGHT}")
|
|
listener_line.append("active", style=SECONDARY)
|
|
parts.append(listener_line)
|
|
else:
|
|
parts.append(Text("○ not configured", style=DIM))
|
|
|
|
# Session summary — only shown when /clear is used mid-session with history
|
|
if session is not None:
|
|
history: list[object] = getattr(session, "history", [])
|
|
if history:
|
|
parts.append(Text("───", style=DIM))
|
|
parts.append(Text("This session", style=f"bold {BRAND}"))
|
|
count = len(history)
|
|
noun = "interaction" if count == 1 else "interactions"
|
|
parts.append(Text(f"{count} {noun}", style=SECONDARY))
|
|
|
|
return Text("\n").join(parts)
|
|
|
|
|
|
__all__ = [
|
|
"_SERVICE_DISPLAY_NAMES",
|
|
"integration_display_name",
|
|
"_build_ambient_right_column",
|
|
"_is_alert_listener_active",
|
|
"_load_integration_health",
|
|
]
|