Initial commit — Agent HAL v1.0

Agent système complet remplaçant agent_debian :
- 20 skills : apt, systemd, cron, process, network, user, sysinfo,
  journal, container, shell, filesystem (enhanced), git, ssh,
  web_fetch, todo, script, mqtt_send, mqtt_subscribe, muc_send, agents_status
- filesystem : read avec numéros de lignes, edit, multiedit (style SHAI)
- git : status, log, diff, add, commit, push, pull, clone, branch, checkout
- ssh : exécution distante + SCP (password ou clé)
- web_fetch : GET/HEAD/POST avec nettoyage HTML
- todo : liste de tâches en mémoire
This commit is contained in:
2026-03-22 21:53:00 +00:00
commit ea1c67b33f
24 changed files with 2467 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
#!/usr/bin/env python3
"""
Agent HAL — Contrôle système complet + édition de code/fichiers.
Remplace agent_debian avec des capacités étendues (git, ssh distant, web, todo).
"""
import os
import sys
import threading
import subprocess
import logging
from agents_core import BaseAgent, AgentContext, Message, MessageType
logger = logging.getLogger(__name__)
class AgentHal(BaseAgent):
AGENT_TYPE = "hal"
DESCRIPTION = (
"Contrôle système complet et édition de code/fichiers : "
"gestion des paquets (apt), services systemd, réseau, processus, "
"utilisateurs, conteneurs Docker/LXC, logs journald, cron, "
"édition avancée de fichiers (read/write/edit/multiedit), git, "
"exécution SSH distante, fetch web. "
"À utiliser pour toute tâche système, devops ou édition de code sur CE serveur ou en SSH."
)
DEFAULT_CONFIG_PATH = "/opt/agent_hal/config/config.json"
def get_skills_dir(self) -> str:
return os.path.join(os.path.dirname(__file__), "skills")
def on_start(self):
self.mqtt.send_to("nexus", f"Agent HAL ({self.agent_id}) en ligne.")
self._start_monitoring()
def setup_extra_subscriptions(self):
self.mqtt.subscribe(
f"agents/{self.agent_id}/control",
self._on_control_message,
)
def _on_control_message(self, msg, topic: str):
from agents_core.message_bus import Message as Msg
payload = msg.payload if isinstance(msg, Msg) else str(msg)
result = self._handle_system_command(payload)
if result and isinstance(msg, Msg):
self.mqtt.reply(msg, result)
def handle_custom_command(self, cmd: str, args: str, source_msg=None):
if cmd == "report":
return self._build_report()
if cmd == "update":
return self._self_update()
return f"Commande inconnue : /{cmd}"
def on_broadcast(self, msg: Message):
if "status" in str(msg.payload).lower():
self.mqtt.reply(msg, self._build_report())
def _build_report(self) -> str:
context = AgentContext(self)
lines = [f"── Rapport {self.agent_id} ──"]
stats = self.queue.daily_stats()
lines.append(
f"Tâches : {stats['total']} total / "
f"{stats['completed']} OK / {stats['failed']} erreurs / "
f"durée moy. {stats['avg_duration_s']}s"
)
try:
uptime = subprocess.check_output("uptime -p", shell=True, text=True).strip()
disk = subprocess.check_output(
"df -h / | tail -1 | awk '{print $3\"/\"$2\" (\"$5\" utilisé)\"}'",
shell=True, text=True
).strip()
mem = subprocess.check_output(
"free -h | awk '/^Mem:/{print $3\"/\"$2}'", shell=True, text=True
).strip()
lines.append(f"Uptime : {uptime} | RAM : {mem} | Disque / : {disk}")
except Exception:
pass
return "\n".join(lines)
def _self_update(self) -> str:
try:
out = subprocess.check_output(
"cd /opt/agent_hal && git pull",
shell=True, text=True, stderr=subprocess.STDOUT
)
subprocess.Popen(["systemctl", "restart", "agent_hal"])
return f"Mise à jour effectuée :\n{out}\nRedémarrage en cours..."
except subprocess.CalledProcessError as e:
return f"Erreur mise à jour : {e.output}"
def _start_monitoring(self):
t = threading.Thread(target=self._monitor_loop, daemon=True)
t.start()
def _monitor_loop(self):
import time
while self._running:
try:
self._check_disk_usage()
self._check_memory()
except Exception as e:
logger.debug(f"[Monitor] {e}")
time.sleep(300)
def _check_disk_usage(self):
result = subprocess.run(
"df -h | awk 'NR>1 && $5+0 > 85 {print $0}'",
shell=True, capture_output=True, text=True
)
if result.stdout.strip():
self.mqtt.alert(
f"Espace disque critique :\n{result.stdout.strip()}",
severity="critical"
)
def _check_memory(self):
result = subprocess.run(
"free | awk '/^Mem:/{if ($3/$2*100 > 90) print \"RAM utilisée à \"int($3/$2*100)\"%\"}'",
shell=True, capture_output=True, text=True
)
if result.stdout.strip():
self.mqtt.alert(result.stdout.strip(), severity="warning")
if __name__ == "__main__":
AgentHal().run()