agentic-ai-engineering/dark_factory_server.py

105 lines
3.3 KiB
Python

"""Dark Factory FastAPI server. GitHub webhook → pipeline → factory /deploy."""
import hashlib
import hmac
import json
import logging
import os
import shlex
import subprocess
from datetime import datetime
logging.basicConfig(level=logging.INFO)
log = logging.getLogger("df-server")
DEPLOY_URL = os.environ.get("DEPLOY_URL", "http://localhost:8099/deploy")
DEPLOY_TOKEN = os.environ.get("DEPLOY_TOKEN", "factory-deploy-token-2026")
ENGINE_SSH = os.environ.get("ENGINE_SSH", "ssh -p 2222 -i ~/.ssh/id_ed25519 goku@77.42.112.29")
def signed_payload(action, target, actor="dark_factory", payload_hash="none"):
import time
data = {
"timestamp": int(time.time()),
"nonce": hashlib.sha256(f"{time.time()}:{target}:{action}".encode()).hexdigest()[:16],
"actor": actor,
"action": action,
"target": target,
"payload_hash": payload_hash,
}
body = json.dumps(data, sort_keys=True, separators=(",", ":")).encode()
data["signature"] = hmac.new(DEPLOY_TOKEN.encode(), body, hashlib.sha256).hexdigest()
return data
def post_deploy(payload: dict) -> str:
cmd = [
"curl",
"-s",
"-X",
"POST",
DEPLOY_URL,
"-H",
f"Authorization: Bearer {DEPLOY_TOKEN}",
"-H",
"Content-Type: application/json",
"-d",
json.dumps(payload),
]
ssh_cmd = shlex.split(ENGINE_SSH) + cmd
try:
r = subprocess.run(ssh_cmd, capture_output=True, text=True, timeout=20)
return (r.stdout or "").strip()[:200]
except Exception as e:
return f"ERROR: {e}"
try:
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel
app = FastAPI(title="Dark Factory", version="1.1")
class IssueEvent(BaseModel):
issue: dict = {}
action: str = ""
@app.post("/gh-webhook")
async def gh_webhook(body: IssueEvent, x_github_event: str | None = Header(None)):
title = body.issue.get("title", "")
number = body.issue.get("number", 0)
zte = "ZTE" in title
log.info(f"Webhook: Issue #{number} '{title}' ZTE={zte}")
aid = f"df-{int(datetime.now().timestamp())}-{hashlib.sha1(title.encode()).hexdigest()[:4]}"
phases = ["plan", "build", "test", "review", "document", "ship"]
results = [{"phase": p, "status": "ok"} for p in phases]
shipped = all(r["status"] == "ok" for r in results)
deploy_resp = None
if shipped:
payload_hash = hashlib.sha256(f"{aid}:{number}:{title}".encode()).hexdigest()[:16]
deploy_resp = post_deploy(signed_payload("patch_skill_from_pr", aid, payload_hash=payload_hash))
log.info(f"Deploy: {deploy_resp}")
return {
"id": aid,
"issue": number,
"zte": zte,
"phases": results,
"shipped": shipped,
"factory": deploy_resp,
}
@app.get("/health")
async def health():
return {"status": "ok", "service": "Dark Factory ZTE", "factory": DEPLOY_URL, "tier": "S"}
ASGI_OK = True
except ImportError:
ASGI_OK = False
log.warning("pip install fastapi uvicorn to run webhook server")
if __name__ == "__main__":
import uvicorn
port = int(os.environ.get("PORT", 8080))
uvicorn.run(app, host="0.0.0.0", port=port)