import os import re import shlex import subprocess import gradio as gr # ── 1. Commands dynamisch ermitteln ────────────────────────────── def discover_commands() -> list[str]: """ Ruft `openclaw --help` auf und extrahiert die Sub-Commands. Fällt bei Problemen auf eine kleine Default-Liste zurück. """ try: res = subprocess.run( ["openclaw", "--help"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, timeout=30, ) commands, parsing = [], False for line in res.stdout.splitlines(): if re.match(r"^\s*Commands?:", line): parsing = True continue if parsing: m = re.match(r"^\s{2,}([a-zA-Z0-9_-]+)\s", line) if m: commands.append(m.group(1)) elif line.strip() == "": break # Ende des Command-Blocks return sorted(set(commands)) except Exception: # Fallback, falls openclaw nicht installiert oder Help-Format unbekannt return ["status", "onboard", "gateway", "tui", "dashboard"] COMMANDS = discover_commands() # ── 2. Ausgewählten Befehl ausführen ───────────────────────────── def run_cmd(command: str, extra_args: str) -> str: """ Führt `openclaw [extra_args]` aus und gibt formatierten Output zurück. """ cmd = ["openclaw", command] + shlex.split(extra_args or "") try: res = subprocess.run( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, timeout=120, cwd="/app", ) return f"$ {' '.join(cmd)}\n\nExit code: {res.returncode}\n\n{res.stdout}" except FileNotFoundError: return "Error: `openclaw` ist nicht im Container installiert." except subprocess.TimeoutExpired: return "Error: Befehl lief länger als 120 s." except Exception as e: return f"Unerwarteter Fehler: {e}" # ── 3. Minimales UI mit Gradio ─────────────────────────────────── with gr.Blocks() as demo: gr.Markdown("# OpenClaw Docker Space – automatisiert") with gr.Row(): cmd_dd = gr.Dropdown( choices=COMMANDS, value=COMMANDS[0] if COMMANDS else None, label="OpenClaw-Command", ) extra_tb = gr.Textbox( label="Optionale Zusatz-Argumente", placeholder="z. B. --help oder --verbose", ) output_tb = gr.Textbox(lines=20, label="Ausgabe") run_btn = gr.Button("Ausführen") run_btn.click(run_cmd, inputs=[cmd_dd, extra_tb], outputs=output_tb) # Hugging Face setzt $PORT automatisch; Fallback 7860 für Lokaltests demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))