#!/usr/bin/env python3 """ Serveur HTTP minimal pour déclencher hermes-auto-improve depuis n8n. Écoute sur 0.0.0.0:9095. POST /trigger → lance auto-improve.py (run standard, MAX_FILES fichiers) POST /trigger/all → lance avec --all POST /trigger/subdir → body JSON {"subdir": "ia"} → lance sur un sous-dossier POST /trigger/dry-run → analyse sans commit GET /status → dernier état (state.json) GET /health → {"ok": true} """ import json import subprocess import threading from datetime import datetime from http.server import BaseHTTPRequestHandler, HTTPServer from pathlib import Path SCRIPT = "/usr/local/bin/hermes-auto-improve" STATE_FILE = Path("/var/lib/hermes-auto-improve/state.json") LOG_FILE = Path("/var/log/hermes-auto-improve.log") _lock = threading.Lock() _running = False _last_result = None def run_script(extra_args: list = None) -> dict: global _running, _last_result with _lock: if _running: return {"error": "already_running"} _running = True try: cmd = [SCRIPT] + (extra_args or []) LOG_FILE.parent.mkdir(parents=True, exist_ok=True) with open(LOG_FILE, "a") as log: log.write(f"\n=== {datetime.now().isoformat()} — {' '.join(cmd)} ===\n") result = subprocess.run( cmd, capture_output=True, text=True, timeout=600 ) log.write(result.stdout) if result.stderr: log.write(f"STDERR: {result.stderr}") # Extraire le JSON du dernier bloc de stdout out = result.stdout start = out.rfind("{") end = out.rfind("}") + 1 if start >= 0 and end > start: _last_result = json.loads(out[start:end]) else: _last_result = {"error": "no_json", "stdout": out[-500:]} return _last_result except subprocess.TimeoutExpired: _last_result = {"error": "timeout"} return _last_result except Exception as e: _last_result = {"error": str(e)} return _last_result finally: _running = False class Handler(BaseHTTPRequestHandler): def log_message(self, fmt, *args): pass # silencer les logs HTTP par défaut def send_json(self, code: int, data: dict): body = json.dumps(data, ensure_ascii=False, indent=2).encode() self.send_response(code) self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) def do_GET(self): if self.path == "/health": self.send_json(200, {"ok": True, "running": _running}) elif self.path == "/status": if STATE_FILE.exists(): state = json.loads(STATE_FILE.read_text()) self.send_json(200, {"state": state, "last_result": _last_result, "running": _running}) else: self.send_json(200, {"state": None, "last_result": _last_result, "running": _running}) else: self.send_json(404, {"error": "not_found"}) def do_POST(self): length = int(self.headers.get("Content-Length", 0)) body = json.loads(self.rfile.read(length)) if length > 0 else {} if self.path == "/trigger": result = run_script() self.send_json(200, result) elif self.path == "/trigger/all": result = run_script(["--all"]) self.send_json(200, result) elif self.path == "/trigger/subdir": subdir = body.get("subdir") if not subdir: self.send_json(400, {"error": "missing 'subdir' in body"}) return result = run_script(["--subdir", subdir]) self.send_json(200, result) elif self.path == "/trigger/dry-run": result = run_script(["--dry-run"]) self.send_json(200, result) elif self.path == "/trigger/reset": result = run_script(["--reset"]) self.send_json(200, result) else: self.send_json(404, {"error": "not_found"}) if __name__ == "__main__": server = HTTPServer(("0.0.0.0", 9095), Handler) print("hermes-auto-improve trigger server — port 9095", flush=True) server.serve_forever()