#!/usr/bin/env python3 """ OpenClaw Cron Isolation Wrapper - Updatesicherer Workaround. Persistent: Tasks und Logs liegen im Workspace, nicht in /tmp. """ import os import sys import json import subprocess import tempfile from pathlib import Path from datetime import datetime, timezone # --- Konfiguration (persistent) --- WORKSPACE = Path("/root/.openclaw/workspace") CRON_TASKS_DIR = WORKSPACE / "cron_tasks" LOG_FILE = WORKSPACE / "cron_wrapper.log" BRAIN_DIR = WORKSPACE / "second-brain" def log(msg: str): ts = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S") line = f"[{ts}] {msg}\n" with open(LOG_FILE, "a") as f: f.write(line) print(line.strip()) def run_isolated(task_name: str, task_args: dict = None) -> dict: """ Führt einen Task in echt isolierter Umgebung aus. Kein Zugriff auf Session-Files, nur stdout/stderr. """ CRON_TASKS_DIR.mkdir(parents=True, exist_ok=True) task_script = CRON_TASKS_DIR / f"{task_name}.py" if not task_script.exists(): return {"success": False, "error": f"Task nicht gefunden: {task_script}"} # Temp-Verzeichnis für Output (flüchtig ist OK, Ergebnis kommt via stdout) temp_dir = tempfile.mkdtemp(prefix=f"cron_{task_name}_") output_file = Path(temp_dir) / "output.json" # Saubere Env: Keine OpenClaw-Session-Variablen env = os.environ.copy() for key in list(env.keys()): if "OPENCLAW" in key.upper() or "SESSION" in key.upper(): del env[key] env["CRON_TASK_NAME"] = task_name env["CRON_OUTPUT_FILE"] = str(output_file) env["BRAIN_DB"] = str(BRAIN_DIR / "data" / "brain.sqlite") try: result = subprocess.run( [sys.executable, str(task_script)] + ([json.dumps(task_args)] if task_args else []), capture_output=True, text=True, timeout=300, cwd=str(temp_dir), env=env, ) stdout = result.stdout.strip() stderr = result.stderr.strip() output_data = {} if output_file.exists(): try: output_data = json.loads(output_file.read_text()) except Exception: output_data = {"raw": output_file.read_text()} return { "success": result.returncode == 0, "returncode": result.returncode, "stdout": stdout[-2000:] if stdout else "", "stderr": stderr[-1000:] if stderr else "", "output": output_data, } except subprocess.TimeoutExpired: return {"success": False, "error": "Timeout nach 300s"} except Exception as e: return {"success": False, "error": str(e)} def main(): import argparse parser = argparse.ArgumentParser(description="OpenClaw Cron Isolation Wrapper") parser.add_argument("task", help="Task-Name aus cron_tasks/") parser.add_argument("--args", help="JSON-Args für den Task") args = parser.parse_args() task_args = json.loads(args.args) if args.args else None result = run_isolated(args.task, task_args) print(json.dumps(result, indent=2, default=str)) return 0 if result["success"] else 1 if __name__ == "__main__": sys.exit(main())