import os, subprocess, json from pathlib import Path from fastapi import FastAPI, BackgroundTasks, HTTPException from fastapi.responses import FileResponse, JSONResponse from pydantic import BaseModel app = FastAPI(title="Hermes Serverless") SESSIONS_DIR = os.environ.get("SESSIONS_DIR", "/data/sessions") Path(SESSIONS_DIR).mkdir(parents=True, exist_ok=True) task_registry = {} class AgentTask(BaseModel): session_id: str task: str def execute_hermes(session_id: str, task: str): task_registry[session_id] = "running" workspace = Path(SESSIONS_DIR) / session_id workspace.mkdir(parents=True, exist_ok=True) log_path = workspace / "output.log" env = os.environ.copy() env.update({"HOME": "/root", "HERMES_HOME": "/root/.hermes"}) try: with open(log_path, "a") as f: p = subprocess.Popen(["hermes", "chat", "-q", task, "--resume", session_id, "--yolo"], cwd=str(workspace), stdout=f, stderr=subprocess.STDOUT, env=env, text=True) rc = p.wait(timeout=1800) task_registry[session_id] = "completed" if rc == 0 else f"failed:{rc}" except Exception as e: task_registry[session_id] = f"error:{str(e)}" @app.post("/agent/execute") async def run(payload: AgentTask, bg: BackgroundTasks): if not payload.session_id or not payload.task: raise HTTPException(400, "Missing session_id or task") bg.add_task(execute_hermes, payload.session_id, payload.task) return {"status": "queued", "session_id": payload.session_id} @app.get("/agent/status/{session_id}") def status(session_id: str): st = task_registry.get(session_id, "unknown") log = Path(SESSIONS_DIR) / session_id / "output.log" logs = log.read_text()[-2000:] if log.exists() else "" return {"session_id": session_id, "status": st, "logs": logs} @app.get("/agent/files/{session_id}/{filename}") def file(session_id: str, filename: str): if "/" in filename or ".." in filename: raise HTTPException(400, "Invalid filename") path = Path(SESSIONS_DIR) / session_id / filename if not path.exists(): raise HTTPException(404, "File not found") return FileResponse(str(path)) @app.get("/health") def health(): return {"status": "ok", "engine": "hermes-serverless"} @app.get("/") def root(): return {"message": "Hermes Serverless API", "docs": "/docs"}