#!/usr/bin/env python3 """ WEVAL Meeting Rooms Auto-Populator — FIXED 18avr 2026 Pipeline: Paperclip → Registry → Enterprise → Meeting Rooms Runs via cron every 6h30: updates room agents from live infrastructure data FIX: regex updated to match enterprise-model.html actual agent format: {n:'NAME',rm:'ROOM',d:'DESC',p:'...',...,F:0|1,...} """ import json, subprocess, re, os, datetime LOG = [] def log(msg): LOG.append(f"[{datetime.datetime.now().strftime('%H:%M:%S')}] {msg}") print(msg) # 1. Read enterprise-model agents (source of truth for active agents) def get_enterprise_agents(): try: f = '/var/www/html/enterprise-model.html' c = open(f).read() agents = [] # Correct format: {n:'NAME',rm:'DEPT',d:'DESC',p:'PURPOSE',...F:0|1,...} # rm = room/dept (ceo, sal, con, dev, qa, l99, sec, srv, ops, dock, mta, pha, saas, ai, wevia, plat, cron, dead, meet, lean, dir) pattern = r"\{n:'([^']+)',rm:'([^']+)'[^}]*?F:(\d)" for m in re.finditer(pattern, c): agents.append({ 'name': m.group(1), 'dept': m.group(2), # rm value (room/dept id) 'active': int(m.group(3)) }) return agents except Exception as e: log(f"ERR enterprise: {e}") return [] # 2. Read registry for crons/pipelines def get_registry(): try: r = subprocess.run(['curl','-s','--max-time','5','http://localhost/api/registry.php'], capture_output=True, text=True, timeout=10) return json.loads(r.stdout) if r.stdout else {} except: return {} # 3. Read Docker containers def get_docker(): try: r = subprocess.run(['docker','ps','--format','{{.Names}}'], capture_output=True, text=True, timeout=10) return r.stdout.strip().split('\n') if r.stdout.strip() else [] except: return [] # 4. Map agents to meeting rooms based on: # (a) Direct room/dept alias mapping (rm value → room_id) # (b) Keyword matching on name/description # # An agent CAN appear in multiple rooms. # Direct rm (dept) → meeting_room mapping DEPT_TO_ROOM = { 'ceo': ['strat', 'biz'], # strategy + business 'dir': ['strat'], # directors = strategic 'sal': ['biz'], # sales 'con': ['biz', 'strat'], # consultants 'sdlc': ['dev'], # dev pipeline 'dev': ['dev'], # dev lab 'qa': ['dev'], # QA 'l99': ['dev'], # L99 pilot 'sec': ['sec'], # security 'srv': ['infra'], # infra/servers 'ops': ['infra', 'transit'], # monitoring + transit 'dock': ['infra'], # Docker services 'mta': ['infra'], # mail transport 'pha': ['biz'], # pharma 'saas': ['biz'], # SaaS 'ai': ['ia'], # AI engine 'wevia': ['ia', 'strat'], # WEVIA core = IA + strategic 'plat': ['transit'], # platform transversal 'cron': ['transit', 'infra'], # crons = ops/transit 'meet': ['strat'], # meeting rooms = strategic 'lean': ['strat'], # lean = strategic 'dead': [], # archived — no room 'wire': ['transit'], # to wire = transit 'intg': ['transit'], # to integrate = transit 'dorm': ['transit'], # dormants = transit } ROOM_KEYWORDS = { 'strat': ['master','opus','life','blade','mirofish','maitre','chef','lead','ceo','strategy','director','weval','plan','visio'], 'infra': ['cortex','prometheus','kuma','sentinel','docker','infra','monitor','disk','server','pmta','kumo','ops','watchdog','guard'], 'dev': ['nonreg','l99','wedroid','wevcode','evomaster','langfuse','playwright','git','code','test','qa','debug','dev','claude','claw','goose','blueprint','forge'], 'sec': ['dark','crowd','authentik','aegis','vault','strix','nuclei','security','scan','sso','ban','rgpd','audit','verifier'], 'biz': ['paperclip','enterprise','ethica','crm','n8n','twenty','active','lead','pipeline','hcp','b2b','sales','proposal','contract','kaouther','marketing','revenue','stripe'], 'ia': ['ollama','qdrant','deerflow','dify','searx','supermemory','mastra','goose','skillsmith','aios','ai','llm','rag','cerebras','groq','gemini','cog','brain','anything','crewai','autogen','cognitive'], 'transit': ['scanner','factory','rnd','browser','mattermost','plausible','wiki','github','automation','backup','sync','relay','bridge','oss','archi'], } def assign_rooms(agents, docker_containers): rooms = {k: [] for k in ROOM_KEYWORDS} seen = {k: set() for k in ROOM_KEYWORDS} for agent in agents: name_lower = agent['name'].lower() dept_lower = agent['dept'].lower() added = False # Strategy A: direct dept → room mapping if dept_lower in DEPT_TO_ROOM: for room_id in DEPT_TO_ROOM[dept_lower]: if agent['name'] not in seen[room_id]: rooms[room_id].append(agent) seen[room_id].add(agent['name']) added = True # Strategy B: keyword fallback for room_id, keywords in ROOM_KEYWORDS.items(): if agent['name'] in seen[room_id]: continue for kw in keywords: if kw in name_lower or kw in dept_lower: rooms[room_id].append(agent) seen[room_id].add(agent['name']) break # Add Docker containers as infra/ia agents docker_room_map = { 'prometheus': 'infra', 'node-exporter': 'infra', 'uptime-kuma': 'infra', 'loki': 'infra', 'plausible': 'transit', 'mattermost': 'transit', 'twenty': 'biz', 'searxng': 'ia', 'qdrant': 'ia', 'langfuse': 'ia', 'n8n': 'transit', 'authentik-server': 'sec', 'vaultwarden': 'sec', 'deerflow': 'ia', 'gitea': 'transit', } for container in docker_containers: cname = container.strip().lower() if not cname: continue for key, room_id in docker_room_map.items(): if key in cname and f"[docker] {container}" not in seen[room_id]: rooms[room_id].append({'name': f"[docker] {container}", 'dept': 'docker', 'active': 1}) seen[room_id].add(f"[docker] {container}") break return rooms # 5. Generate meeting room JSON for the page to consume def generate_meeting_data(): log("=== MEETING ROOMS AUTO-POPULATOR (FIXED 18avr) ===") agents = get_enterprise_agents() log(f"Enterprise: {len(agents)} agents") registry = get_registry() log(f"Registry: {len(registry)} sections" if isinstance(registry, dict) else "Registry: unavailable") docker = get_docker() log(f"Docker: {len(docker)} containers") rooms = assign_rooms(agents, docker) result = { 'timestamp': datetime.datetime.now().isoformat(), 'source': 'auto-populator-v2-fixed-18avr', 'enterprise_total_agents': len(agents), 'docker_total': len(docker), 'rooms': {} } for room_id, room_agents in rooms.items(): active_count = sum(1 for a in room_agents if a.get('active', 0) == 1) result['rooms'][room_id] = { 'count': len(room_agents), 'agents': [a['name'] for a in room_agents[:25]], # cap 25/room (was 15) 'active': active_count, 'dead': len(room_agents) - active_count, } log(f" {room_id}: {len(room_agents)} agents ({active_count} active)") out = '/var/www/html/api/meeting-rooms-data.json' with open(out, 'w') as f: json.dump(result, f, indent=2, ensure_ascii=False) log(f"Output: {out}") with open('/var/www/html/api/meeting-rooms-populator.log', 'w') as f: f.write('\n'.join(LOG)) return result if __name__ == '__main__': generate_meeting_data()