feat(rpg): add day/night ambient lighting, multi-coder NPCs, thought bubbles, XP/level system, CI incident alarm, and interactive Kanban modal

This commit is contained in:
2026-10-03 22:45:07 +02:00
parent 36616420e3
commit 954961b26e
2 changed files with 821 additions and 144 deletions
+758 -141
View File
File diff suppressed because it is too large Load Diff
+63 -3
View File
@@ -25,6 +25,44 @@ class RealtimeRPGHandler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, directory=DIRECTORY, **kwargs) super().__init__(*args, directory=DIRECTORY, **kwargs)
def do_POST(self):
parsed_url = urllib.parse.urlparse(self.path)
if parsed_url.path == '/api/issue':
content_length = int(self.headers.get('Content-Length', 0))
post_data = self.rfile.read(content_length).decode('utf-8')
try:
payload = json.loads(post_data)
project = payload.get('project', 'camper-knowledge-graph')
title = payload.get('title', '').strip()
body = payload.get('body', 'Erstellt über Agent RPG Terminal').strip()
if not title:
self.send_response(400)
self.end_headers()
self.wfile.write(json.dumps({"error": "Title is required"}).encode('utf-8'))
return
req = urllib.request.Request(
f"{GITEA_BASE_URL}/repos/openclaw/{project}/issues",
data=json.dumps({"title": title, "body": body}).encode('utf-8'),
headers={'Authorization': f'token {GITEA_TOKEN}', 'Content-Type': 'application/json'}
)
with urllib.request.urlopen(req, timeout=3) as resp:
resp_data = json.loads(resp.read().decode())
self.send_response(201)
self.send_header('Content-Type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
self.wfile.write(json.dumps({"success": True, "issue": resp_data}).encode('utf-8'))
except Exception as e:
self.send_response(500)
self.send_header('Content-Type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
self.wfile.write(json.dumps({"error": str(e)}).encode('utf-8'))
else:
self.send_response(404)
self.end_headers()
def do_GET(self): def do_GET(self):
parsed_url = urllib.parse.urlparse(self.path) parsed_url = urllib.parse.urlparse(self.path)
if parsed_url.path == '/api/status': if parsed_url.path == '/api/status':
@@ -66,6 +104,11 @@ class RealtimeRPGHandler(http.server.SimpleHTTPRequestHandler):
"ci_status": "PASSING", "ci_status": "PASSING",
"connected": True "connected": True
}, },
"metrics": {
"active_agents": 0,
"token_velocity": 0,
"system_health": 100
},
"agents": { "agents": {
"coder1": { "coder1": {
"name": f"Coder #{target_project[:4].upper()}", "name": f"Coder #{target_project[:4].upper()}",
@@ -144,8 +187,9 @@ class RealtimeRPGHandler(http.server.SimpleHTTPRequestHandler):
# 2. Collect Gitea Live PRs & Issues for Whiteboard Kanban strictly for target_project # 2. Collect Gitea Live PRs & Issues for Whiteboard Kanban strictly for target_project
try: try:
# Check Gitea Actions API for actively running workflows # Check Gitea Actions API for actively running workflows and recent failure
active_runs_count = 0 active_runs_count = 0
latest_run_failed = False
try: try:
req_runs = urllib.request.Request( req_runs = urllib.request.Request(
f"{GITEA_BASE_URL}/repos/openclaw/{target_project}/actions/runs?limit=10", f"{GITEA_BASE_URL}/repos/openclaw/{target_project}/actions/runs?limit=10",
@@ -153,14 +197,22 @@ class RealtimeRPGHandler(http.server.SimpleHTTPRequestHandler):
) )
with urllib.request.urlopen(req_runs, timeout=2) as resp: with urllib.request.urlopen(req_runs, timeout=2) as resp:
runs_data = json.loads(resp.read().decode()) runs_data = json.loads(resp.read().decode())
for r in runs_data.get("workflow_runs", []): runs = runs_data.get("workflow_runs", [])
for r in runs:
if r.get("status") in ["running", "waiting", "queued"]: if r.get("status") in ["running", "waiting", "queued"]:
active_runs_count += 1 active_runs_count += 1
if runs and runs[0].get("status") == "completed" and runs[0].get("conclusion") in ["failure", "cancelled"]:
latest_run_failed = True
except Exception: except Exception:
pass pass
status["gitea"]["active_ci_jobs"] = active_runs_count status["gitea"]["active_ci_jobs"] = active_runs_count
status["gitea"]["ci_status"] = "BUILDING" if active_runs_count > 0 else "PASSING" if active_runs_count > 0:
status["gitea"]["ci_status"] = "BUILDING"
elif latest_run_failed:
status["gitea"]["ci_status"] = "FAILURE"
else:
status["gitea"]["ci_status"] = "PASSING"
# Fetch Open Pull Requests # Fetch Open Pull Requests
req_prs = urllib.request.Request( req_prs = urllib.request.Request(
@@ -389,6 +441,14 @@ class RealtimeRPGHandler(http.server.SimpleHTTPRequestHandler):
status["agents"]["reviewer"]["lastAction"] = "Idle / Bereit" status["agents"]["reviewer"]["lastAction"] = "Idle / Bereit"
status["agents"]["reviewer"]["liveLog"] = ["PR Watcher aktiv"] status["agents"]["reviewer"]["liveLog"] = ["PR Watcher aktiv"]
status["agents"]["coder_instances"] = coder_sessions
# Calculate live system metrics
active_count = sum(1 for a in status["agents"].values() if isinstance(a, dict) and a.get("state") == "WORKING")
status["metrics"]["active_agents"] = active_count
status["metrics"]["token_velocity"] = (active_count * 185) + (total_active * 240)
status["metrics"]["system_health"] = 90 if status["gitea"]["ci_status"] == "FAILURE" else 100
return status return status
if __name__ == '__main__': if __name__ == '__main__':