Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

223 lines
7.5 KiB
Python

"""Single-command CLI entrypoints that do not need their own groups."""
from __future__ import annotations
import json
import sys
import time
import click
import platform
from config.version import get_opensre_version
from platform.analytics.cli import (
capture_update_completed,
capture_update_failed,
capture_update_started,
track_investigation,
)
from platform.analytics.source import EntrypointSource, TriggerMode
from platform.common.exit_codes import ERROR, SUCCESS
from platform.common.runtime_flags import is_json_output, is_yes
from surfaces.cli.constants import ALERT_TEMPLATE_CHOICES
@click.command(name="uninstall")
@click.option("--yes", "-y", "local_yes", is_flag=True, help="Skip the confirmation prompt.")
def uninstall_command(local_yes: bool) -> None:
"""Remove opensre and all local data from this machine."""
from surfaces.cli.lifecycle.uninstall import run_uninstall
raise SystemExit(run_uninstall(yes=local_yes or is_yes()))
@click.command(name="update")
@click.option(
"--check",
"check_only",
is_flag=True,
help="Report whether an update is available without installing.",
)
@click.option("--yes", "-y", "local_yes", is_flag=True, help="Skip the confirmation prompt.")
def update_command(check_only: bool, local_yes: bool) -> None:
"""Check for a newer main build and update if one is available."""
from surfaces.cli.lifecycle.update import run_update
capture_update_started(check_only=check_only)
try:
exit_code = run_update(check_only=check_only, yes=local_yes or is_yes())
except Exception as exc:
capture_update_failed(check_only=check_only, reason=type(exc).__name__)
raise
capture_update_completed(
check_only=check_only,
updated=exit_code == 0 and not check_only,
)
raise SystemExit(exit_code)
@click.command(name="version")
def version_command() -> None:
"""Print detailed version, Python and OS info."""
if is_json_output():
click.echo(
json.dumps(
{
"opensre": get_opensre_version(),
"python": platform.python_version(),
"os": platform.system().lower(),
"arch": platform.machine(),
}
)
)
return
click.echo(f"opensre {get_opensre_version()}")
click.echo(f"Python {platform.python_version()}")
click.echo(f"OS {platform.system().lower()} ({platform.machine()})")
@click.command(name="health")
@click.option("--watch", is_flag=True, help="Continuously refresh the health report.")
@click.option(
"--rate", default=5, show_default=True, help="Refresh interval in seconds (with --watch)."
)
def health_command(watch: bool, rate: int) -> None:
"""Show a quick health summary of the local agent setup."""
from config.config import get_environment
from integrations.store import STORE_PATH
from integrations.verify import verify_integrations
from surfaces.interactive_shell.ui.health import render_health_json, render_health_report
def _run_once() -> int:
results = verify_integrations()
environment = get_environment().value
if is_json_output():
render_health_json(
environment=environment,
integration_store_path=STORE_PATH,
results=results,
)
else:
from rich.console import Console
render_health_report(
console=Console(highlight=False),
environment=environment,
integration_store_path=STORE_PATH,
results=results,
)
if any(result.get("status") == "failed" for result in results):
return ERROR
return SUCCESS
if not watch:
raise SystemExit(_run_once())
try:
while True:
click.clear()
_run_once()
time.sleep(rate)
except KeyboardInterrupt:
raise SystemExit(SUCCESS) from None
@click.command(name="investigate")
@click.argument(
"alert_file",
required=False,
type=click.Path(),
)
@click.option(
"--input",
"-i",
"input_path",
default=None,
type=click.Path(),
help="Path to an alert file (.json, .md, .txt, ...). Use '-' to read from stdin.",
)
@click.option("--input-json", default=None, help="Inline alert JSON string.")
@click.option("--interactive", is_flag=True, help="Paste an alert JSON payload into the terminal.")
@click.option(
"--print-template",
type=click.Choice(ALERT_TEMPLATE_CHOICES),
default=None,
help="Print a starter alert JSON template and exit.",
)
@click.option(
"--output", "-o", default=None, type=click.Path(), help="Output JSON file (default: stdout)."
)
@click.option(
"--evaluate",
is_flag=True,
help="After final diagnosis, LLM-judge vs scoring_points rubric (rubric stripped from agent alert).",
)
def investigate_command(
alert_file: str | None,
input_path: str | None,
input_json: str | None,
interactive: bool,
print_template: str | None,
output: str | None,
evaluate: bool,
) -> None:
"""Run an RCA investigation against an alert payload."""
# Treat a bare positional path the same as ``-i <path>``. Lets users type
# ``opensre investigate alert.json`` instead of the more verbose
# ``opensre investigate -i alert.json``. If both are given, the explicit
# flag wins to keep the behaviour predictable.
if alert_file and not input_path:
input_path = alert_file
from surfaces.cli import write_json
from surfaces.cli.investigation import run_investigation_cli, run_investigation_cli_streaming
from surfaces.cli.investigation.payload import load_payload
from tools.investigation.alert_templates import build_alert_template
try:
if print_template:
write_json(build_alert_template(print_template), output)
raise SystemExit(SUCCESS)
payload = load_payload(
input_path=input_path,
input_json=input_json,
interactive=interactive,
)
trigger_mode = (
TriggerMode.PASTE
if interactive
else (TriggerMode.INLINE_JSON if input_json is not None else TriggerMode.FILE)
)
with track_investigation(
entrypoint=EntrypointSource.CLI_COMMAND,
trigger_mode=trigger_mode,
input_path=input_path,
input_json=input_json,
interactive=interactive,
evaluate_requested=evaluate,
) as tracker:
# Only stream the live UI when the user is interactively watching stdout
# and hasn't asked for machine-readable JSON. Otherwise the spinner and
# ANSI control codes corrupt the JSON payload that consumers expect on
# stdout (pipes, redirection, --json, CI logs).
# --evaluate forces the non-streaming path because the streaming runner
# does not yet wire opensre_evaluate scoring through the renderer.
stream_to_stdout = (
sys.stdout.isatty() and not is_json_output() and output is None and not evaluate
)
if stream_to_stdout:
run_investigation_cli_streaming(raw_alert=payload, tracker=tracker)
else:
result = run_investigation_cli(raw_alert=payload, opensre_evaluate=evaluate)
write_json(result, output)
except SystemExit:
raise
except KeyboardInterrupt:
raise SystemExit(SUCCESS) from None
raise SystemExit(SUCCESS)