feat: amélioration scripts bash, scan réseau, fix cron, README
- system_prompt: section scripts bash (commandes interdites, mosquitto_pub, bonnes pratiques) - script.py: nettoyage guillemets échappés à la sauvegarde - network.py: nouvelle action scan (nmap/arp-scan/arp fallback), auto-détection subnet - cron.py: _get_current_crontab() évite d'écrire "(aucune sortie)" dans le crontab - README créé Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,124 @@
|
|||||||
|
# agent_hal
|
||||||
|
|
||||||
|
Agent de contrôle système complet et d'édition de code/fichiers. Remplace et étend `agent_debian` avec des capacités supplémentaires : édition avancée de fichiers, git, SSH distant, fetch web, todo list. Dispose également d'une surveillance proactive disque/RAM.
|
||||||
|
|
||||||
|
## Rôle
|
||||||
|
|
||||||
|
HAL gère **ce serveur** (et les machines distantes via SSH) pour toute tâche système, devops ou édition de code. Pour l'automatisation multi-serveurs, utiliser `agent_ansible`. Pour déployer de nouveaux agents, utiliser `agent_deploy`.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/agent_hal
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
systemctl enable --now agent_hal
|
||||||
|
```
|
||||||
|
|
||||||
|
## Skills disponibles
|
||||||
|
|
||||||
|
| Skill | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `sysinfo` | CPU, RAM, disque, uptime |
|
||||||
|
| `apt` | Gestion des paquets (install, remove, update, upgrade, search) |
|
||||||
|
| `systemd` | Contrôle des services (start, stop, restart, status, enable) |
|
||||||
|
| `cron` | Gestion des tâches cron (list, add, remove, clear) |
|
||||||
|
| `process` | Liste, kill, surveillance des processus |
|
||||||
|
| `network` | Interfaces, scan réseau, ping, traceroute, DNS, ports, firewall |
|
||||||
|
| `user` | Gestion des utilisateurs et groupes |
|
||||||
|
| `container` | Gestion Docker/LXC (ps, start, stop, logs, exec, stats) |
|
||||||
|
| `journal` | Consultation des logs systemd (journalctl) |
|
||||||
|
| `filesystem` | Lecture, écriture, édition, recherche de fichiers (`read`, `write`, `edit`, `multiedit`) |
|
||||||
|
| `git` | Opérations git (status, log, diff, add, commit, push, pull, clone, branch) |
|
||||||
|
| `ssh` | Exécution de commandes sur une machine distante + transfert de fichiers (`COPY`) |
|
||||||
|
| `shell` | Commandes bash directes (fallback) |
|
||||||
|
| `script` | Bibliothèque de scripts bash (save/list/show/edit/exec/run/delete) |
|
||||||
|
| `web_fetch` | Récupérer le contenu d'une URL HTTP/HTTPS |
|
||||||
|
| `todo` | Liste de tâches en mémoire (add, list, done, delete, clear) |
|
||||||
|
| `agents_status` | Statut des agents du système |
|
||||||
|
| `mqtt_send` | Publication sur un topic MQTT |
|
||||||
|
| `mqtt_subscribe` | Souscription dynamique à un topic MQTT |
|
||||||
|
| `muc_send` | Message dans le groupe XMPP |
|
||||||
|
|
||||||
|
## Scan réseau
|
||||||
|
|
||||||
|
```
|
||||||
|
"Liste les machines connectées au réseau"
|
||||||
|
→ SKILL:network ARGS:scan 192.168.7.0/24
|
||||||
|
```
|
||||||
|
|
||||||
|
Utilise nmap (ping scan) en priorité, sinon arp-scan, sinon table ARP. La détection du subnet est automatique si non précisé.
|
||||||
|
|
||||||
|
## Bibliothèque de scripts
|
||||||
|
|
||||||
|
Les scripts bash sont stockés dans `/opt/agent_hal/scripts/`. Toujours créés via `SKILL:script ARGS:save`, jamais via `filesystem write`.
|
||||||
|
|
||||||
|
Variables disponibles dans les scripts : `$MQTT_BROKER`, `$MQTT_REPLY_TOPIC`, `$AGENT_ID`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Exemple d'envoi de résultat depuis un script
|
||||||
|
mosquitto_pub -h "$MQTT_BROKER" -t "$MQTT_REPLY_TOPIC" -m "résultat"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Confirmations requises
|
||||||
|
|
||||||
|
Les actions suivantes demandent confirmation avant exécution (requêtes XMPP directes uniquement) :
|
||||||
|
|
||||||
|
- **cron** : `add`, `remove`, `clear`
|
||||||
|
- **systemd** : `start`, `stop`, `restart`, `enable`, `disable`, `mask`, `unmask`, `daemon-reload`
|
||||||
|
|
||||||
|
## Surveillance proactive
|
||||||
|
|
||||||
|
Toutes les 5 minutes :
|
||||||
|
- **Disque** : alerte si un volume dépasse 85%
|
||||||
|
- **RAM** : alerte si la mémoire utilisée dépasse 90%
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
`config/config.json` :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"agent_id": "hal",
|
||||||
|
"xmpp": {
|
||||||
|
"jid": "hal@xmpp.ovh",
|
||||||
|
"password": "...",
|
||||||
|
"admin_jid": "sylvain@xmpp.ovh",
|
||||||
|
"muc_room": "agents@muc.xmpp.ovh",
|
||||||
|
"use_omemo": true
|
||||||
|
},
|
||||||
|
"mqtt": { "host": "localhost", "port": 1883 },
|
||||||
|
"llm": {
|
||||||
|
"base_url": "http://192.168.7.119:11434",
|
||||||
|
"model": "qwen3:8b",
|
||||||
|
"temperature": 0.3
|
||||||
|
},
|
||||||
|
"llm_profiles": {
|
||||||
|
"local": "qwen3:8b",
|
||||||
|
"code": "qwen2.5-coder:7b",
|
||||||
|
"cloud": "gpt-oss:120b-cloud"
|
||||||
|
},
|
||||||
|
"use_llm_coordinator": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commandes
|
||||||
|
|
||||||
|
```
|
||||||
|
/report — Rapport système (uptime, RAM, disque, stats tâches)
|
||||||
|
/update — Git pull + redémarrage du service
|
||||||
|
/status — État de la queue de tâches
|
||||||
|
/pause — Pause du traitement des tâches
|
||||||
|
/resume — Reprise
|
||||||
|
/script — Gestion de la bibliothèque de scripts bash
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fichiers
|
||||||
|
|
||||||
|
```
|
||||||
|
agent_hal.py — Point d'entrée
|
||||||
|
skills/ — 20 skills
|
||||||
|
scripts/ — Scripts bash persistants
|
||||||
|
config/ — Configuration et system prompt
|
||||||
|
agent_hal.service — Unit systemd
|
||||||
|
```
|
||||||
+4
-3
@@ -16,15 +16,16 @@
|
|||||||
},
|
},
|
||||||
"llm": {
|
"llm": {
|
||||||
"base_url": "http://192.168.7.119:11434",
|
"base_url": "http://192.168.7.119:11434",
|
||||||
"model": "qwen3:8b",
|
"model": "gpt-oss:120b-cloud",
|
||||||
"temperature": 0.3
|
"temperature": 0.3
|
||||||
},
|
},
|
||||||
"work_hours": "00:00-23:59",
|
"work_hours": "00:00-23:59",
|
||||||
"queue_db": "/opt/agent_hal/data/queue.db",
|
"queue_db": "/opt/agent_hal/data/queue.db",
|
||||||
"system_prompt": "/opt/agent_hal/config/system_prompt.txt",
|
"system_prompt": "/opt/agent_hal/config/system_prompt.txt",
|
||||||
"llm_profiles": {
|
"llm_profiles": {
|
||||||
"local": "qwen3:8b",
|
"local": "ministral-3:14b",
|
||||||
"code": "qwen2.5-coder:7b"
|
"code": "qwen2.5-coder:7b",
|
||||||
|
"cloud": "gpt-oss:120b-cloud"
|
||||||
},
|
},
|
||||||
"use_llm_coordinator": true
|
"use_llm_coordinator": true
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,8 @@ Tu reçois des instructions via MQTT (depuis Nexus) ou XMPP (directement).
|
|||||||
- **systemd** : gestion des services (start, stop, restart, status, logs, enable, disable)
|
- **systemd** : gestion des services (start, stop, restart, status, logs, enable, disable)
|
||||||
- **cron** : tâches planifiées (list, add, remove)
|
- **cron** : tâches planifiées (list, add, remove)
|
||||||
- **process** : processus (list, kill, top, find, tree)
|
- **process** : processus (list, kill, top, find, tree)
|
||||||
- **network** : réseau (ip, ping, traceroute, DNS, ports, firewall, bandwidth)
|
- **network** : réseau (scan hôtes, arp, ip, ping, traceroute, DNS, ports, firewall, bandwidth)
|
||||||
|
- `scan [subnet]` : découvrir les machines connectées (ex: `scan 192.168.7.0/24`) — **à utiliser obligatoirement** pour lister les hôtes du réseau, ne jamais inventer une liste
|
||||||
- **user** : utilisateurs (add, delete, passwd, groups, sudo, ssh-key)
|
- **user** : utilisateurs (add, delete, passwd, groups, sudo, ssh-key)
|
||||||
- **container** : Docker et LXC (ps, start, stop, logs, exec, stats, images)
|
- **container** : Docker et LXC (ps, start, stop, logs, exec, stats, images)
|
||||||
- **journal** : logs système (tail, service, errors, since, grep, kernel)
|
- **journal** : logs système (tail, service, errors, since, grep, kernel)
|
||||||
@@ -38,17 +39,52 @@ Tu reçois des instructions via MQTT (depuis Nexus) ou XMPP (directement).
|
|||||||
1. Utilise toujours le skill le plus spécifique disponible
|
1. Utilise toujours le skill le plus spécifique disponible
|
||||||
2. Pour éditer du code, préfère `filesystem edit` ou `filesystem multiedit` plutôt que `shell sed`
|
2. Pour éditer du code, préfère `filesystem edit` ou `filesystem multiedit` plutôt que `shell sed`
|
||||||
3. Après chaque action importante (install, restart, delete, commit), vérifie le résultat
|
3. Après chaque action importante (install, restart, delete, commit), vérifie le résultat
|
||||||
4. Pour les scripts complexes, utilise SKILL:script pour les créer puis les exécuter
|
4. **OBLIGATOIRE : tout script bash doit être créé via `SKILL:script ARGS:save <nom> | <contenu>` et jamais via `filesystem write`.** C'est la seule façon de le rendre accessible et exécutable dans la bibliothèque de scripts. N'utilise JAMAIS filesystem pour écrire un fichier .sh.
|
||||||
5. En cas d'erreur, diagnostique avant de réessayer
|
5. En cas d'erreur, diagnostique avant de réessayer
|
||||||
6. Réponds toujours en français
|
6. Réponds toujours en français
|
||||||
7. Sois concis dans tes réponses — l'essentiel, pas tout le stdout brut
|
7. Sois concis dans tes réponses — l'essentiel, pas tout le stdout brut
|
||||||
8. Pour les opérations git, vérifie toujours le status avant de committer
|
8. Pour les opérations git, vérifie toujours le status avant de committer
|
||||||
|
|
||||||
## Communication MQTT
|
## Écriture de scripts bash — règles strictes
|
||||||
|
|
||||||
|
Quand tu écris du contenu pour `SKILL:script ARGS:save`, respecte ces règles :
|
||||||
|
|
||||||
|
### ❌ Interdit dans les scripts bash
|
||||||
|
- `muc_send`, `mqtt_send`, `shell`, et tous les autres noms de skills — ce ne sont PAS des commandes bash
|
||||||
|
- Les guillemets échappés : écris `"texte"` et non `\"texte\"`
|
||||||
|
- Les backslashes inutiles dans les chaînes
|
||||||
|
|
||||||
|
### ✅ Pour envoyer un message depuis un script
|
||||||
|
Les variables d'environnement suivantes sont injectées automatiquement :
|
||||||
|
- `$MQTT_BROKER` — hôte du broker MQTT
|
||||||
|
- `$MQTT_REPLY_TOPIC` — topic de retour vers Nexus
|
||||||
|
- `$AGENT_ID` — identifiant de l'agent
|
||||||
|
|
||||||
|
Envoyer un résultat à Nexus :
|
||||||
|
```bash
|
||||||
|
mosquitto_pub -h "$MQTT_BROKER" -t "$MQTT_REPLY_TOPIC" -m "mon résultat"
|
||||||
|
```
|
||||||
|
|
||||||
|
Envoyer une alerte vers le MUC via Nexus :
|
||||||
|
```bash
|
||||||
|
mosquitto_pub -h "$MQTT_BROKER" -t "agents/nexus/inbox" -m "ALERTE : quelque chose s'est passé"
|
||||||
|
```
|
||||||
|
|
||||||
|
### ✅ Bonnes pratiques
|
||||||
|
- Commence toujours par `#!/bin/bash` et `set -euo pipefail`
|
||||||
|
- Utilise des guillemets doubles autour des variables : `"$VAR"`
|
||||||
|
- Gère les cas d'erreur avec des messages explicites
|
||||||
|
- Pour les scans réseau longs, ajoute un timeout global ou parallélise avec `&` + `wait`
|
||||||
|
|
||||||
|
### Exemple de script correct
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
RESULT=$(df -h / | tail -1)
|
||||||
|
mosquitto_pub -h "$MQTT_BROKER" -t "$MQTT_REPLY_TOPIC" -m "Disque : $RESULT"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Communication MQTT (depuis le LLM, pas depuis un script)
|
||||||
|
|
||||||
Tu peux envoyer des messages à d'autres agents :
|
Tu peux envoyer des messages à d'autres agents :
|
||||||
SKILL:mqtt_send ARGS:agents/nexus/inbox | {"type":"result","payload":"mon résultat"}
|
SKILL:mqtt_send ARGS:agents/nexus/inbox | {"type":"result","payload":"mon résultat"}
|
||||||
|
|
||||||
Pour les scripts qui doivent retourner un résultat :
|
|
||||||
Les variables $MQTT_BROKER et $MQTT_REPLY_TOPIC sont disponibles dans l'environnement.
|
|
||||||
mosquitto_pub -h $MQTT_BROKER -t $MQTT_REPLY_TOPIC -m "résultat"
|
|
||||||
|
|||||||
Executable
+37
@@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Sous‑réseau à surveiller
|
||||||
|
SUBNET="192.168.7.0/24"
|
||||||
|
|
||||||
|
# Fichier de cache contenant la liste des hôtes vus lors du dernier scan
|
||||||
|
CACHE="/tmp/monitor_disconnect_cache.txt"
|
||||||
|
|
||||||
|
# Fonction de scan : ping‑scan avec nmap, on ne garde que les adresses IP actives
|
||||||
|
scan_hosts() {
|
||||||
|
nmap -sn "$SUBNET" -oG - | awk '/Up/{print $2}'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Première exécution → créer le cache et quitter
|
||||||
|
if [[ ! -f "$CACHE" ]]; then
|
||||||
|
scan_hosts > "$CACHE"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Scan actuel
|
||||||
|
CURRENT="$(mktemp)"
|
||||||
|
scan_hosts > "$CURRENT"
|
||||||
|
|
||||||
|
# Déterminer les hôtes qui ont disparu depuis le dernier scan
|
||||||
|
MISSING=$(comm -23 <(sort "$CACHE") <(sort "$CURRENT")) || true
|
||||||
|
|
||||||
|
# Si des hôtes sont manquants, envoyer une alerte XMPP via MQTT (le broker relayera vers Nexus)
|
||||||
|
if [[ -n "$MISSING" ]]; then
|
||||||
|
while IFS= read -r ip; do
|
||||||
|
MSG="ALERTE : machine $ip déconnectée du réseau $SUBNET"
|
||||||
|
mosquitto_pub -h "$MQTT_BROKER" -t "agents/nexus/inbox" -m "$MSG"
|
||||||
|
done <<< "$MISSING"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Mettre à jour le cache pour le prochain cycle
|
||||||
|
mv "$CURRENT" "$CACHE"
|
||||||
Executable
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Met à jour la liste des paquets (silencieux)
|
||||||
|
apt-get update -qq
|
||||||
|
|
||||||
|
# Récupère la liste des paquets pouvant être mis à jour (simulation)
|
||||||
|
UPDATES=$(apt-get -s upgrade 2>/dev/null | grep -E '^Inst' | awk '{print $2 " " $3 " -> " $4}' || true)
|
||||||
|
|
||||||
|
if [[ -z "$UPDATES" ]]; then
|
||||||
|
MESSAGE="Aucune mise à jour disponible"
|
||||||
|
else
|
||||||
|
MESSAGE="Mises à jour disponibles :\
|
||||||
|
$UPDATES"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Envoie le résultat via MQTT
|
||||||
|
mosquitto_pub -h "$MQTT_BROKER" -t "$MQTT_REPLY_TOPIC" -m "$MESSAGE"
|
||||||
+21
-7
@@ -35,6 +35,16 @@ def _run(cmd: str, timeout: int = 10) -> str:
|
|||||||
return str(e)
|
return str(e)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_current_crontab() -> str:
|
||||||
|
"""Retourne le crontab actuel, ou chaîne vide si inexistant."""
|
||||||
|
result = subprocess.run(
|
||||||
|
"crontab -l", shell=True, text=True, capture_output=True
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
return ""
|
||||||
|
return result.stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
def run(args: str, context) -> str:
|
def run(args: str, context) -> str:
|
||||||
parts = args.strip().split(None, 1)
|
parts = args.strip().split(None, 1)
|
||||||
action = parts[0].lower() if parts else "list"
|
action = parts[0].lower() if parts else "list"
|
||||||
@@ -54,26 +64,28 @@ def run(args: str, context) -> str:
|
|||||||
command = " ".join(words[5:])
|
command = " ".join(words[5:])
|
||||||
entry = f"{cron_expr} {command}"
|
entry = f"{cron_expr} {command}"
|
||||||
|
|
||||||
current = _run("crontab -l 2>/dev/null")
|
current = _get_current_crontab()
|
||||||
if entry in current:
|
if entry in current:
|
||||||
return f"Cette entrée existe déjà : {entry}"
|
return f"Cette entrée existe déjà : {entry}"
|
||||||
|
|
||||||
def _do_add():
|
def _do_add():
|
||||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".cron", delete=False) as f:
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".cron", delete=False) as f:
|
||||||
if current and "no crontab" not in current.lower():
|
if current:
|
||||||
f.write(current + "\n")
|
f.write(current + "\n")
|
||||||
f.write(entry + "\n")
|
f.write(entry + "\n")
|
||||||
tmpfile = f.name
|
tmpfile = f.name
|
||||||
out = _run(f"crontab {tmpfile}")
|
result = subprocess.run(f"crontab {tmpfile}", shell=True, text=True, capture_output=True)
|
||||||
os.unlink(tmpfile)
|
os.unlink(tmpfile)
|
||||||
return f"Entrée ajoutée : {entry}\n{out}"
|
if result.returncode != 0:
|
||||||
|
return f"❌ Erreur crontab : {(result.stdout + result.stderr).strip()}"
|
||||||
|
return f"✅ Entrée ajoutée : {entry}"
|
||||||
|
|
||||||
return _confirm_or_execute(context, f"Ajouter cron : {entry}", _do_add)
|
return _confirm_or_execute(context, f"Ajouter cron : {entry}", _do_add)
|
||||||
|
|
||||||
if action == "remove":
|
if action == "remove":
|
||||||
if not rest:
|
if not rest:
|
||||||
return "Précise le pattern à supprimer."
|
return "Précise le pattern à supprimer."
|
||||||
current = _run("crontab -l 2>/dev/null")
|
current = _get_current_crontab()
|
||||||
lines = [l for l in current.splitlines() if rest not in l]
|
lines = [l for l in current.splitlines() if rest not in l]
|
||||||
removed_count = len(current.splitlines()) - len(lines)
|
removed_count = len(current.splitlines()) - len(lines)
|
||||||
if removed_count == 0:
|
if removed_count == 0:
|
||||||
@@ -84,9 +96,11 @@ def run(args: str, context) -> str:
|
|||||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".cron", delete=False) as f:
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".cron", delete=False) as f:
|
||||||
f.write(new_cron + "\n")
|
f.write(new_cron + "\n")
|
||||||
tmpfile = f.name
|
tmpfile = f.name
|
||||||
out = _run(f"crontab {tmpfile}")
|
result = subprocess.run(f"crontab {tmpfile}", shell=True, text=True, capture_output=True)
|
||||||
os.unlink(tmpfile)
|
os.unlink(tmpfile)
|
||||||
return f"{removed_count} entrée(s) supprimée(s) contenant '{rest}'.\n{out}"
|
if result.returncode != 0:
|
||||||
|
return f"❌ Erreur crontab : {(result.stdout + result.stderr).strip()}"
|
||||||
|
return f"✅ {removed_count} entrée(s) supprimée(s) contenant '{rest}'."
|
||||||
|
|
||||||
return _confirm_or_execute(context, f"Supprimer {removed_count} cron contenant '{rest}'", _do_remove)
|
return _confirm_or_execute(context, f"Supprimer {removed_count} cron contenant '{rest}'", _do_remove)
|
||||||
|
|
||||||
|
|||||||
+29
-2
@@ -2,6 +2,8 @@
|
|||||||
Skill NETWORK — administration réseau.
|
Skill NETWORK — administration réseau.
|
||||||
|
|
||||||
Usage LLM :
|
Usage LLM :
|
||||||
|
SKILL:network ARGS:scan [subnet] — découverte des hôtes (ex: scan 192.168.7.0/24)
|
||||||
|
SKILL:network ARGS:arp — table ARP locale (hôtes récents)
|
||||||
SKILL:network ARGS:ip [show|route|link]
|
SKILL:network ARGS:ip [show|route|link]
|
||||||
SKILL:network ARGS:ping <hôte> [count]
|
SKILL:network ARGS:ping <hôte> [count]
|
||||||
SKILL:network ARGS:traceroute <hôte>
|
SKILL:network ARGS:traceroute <hôte>
|
||||||
@@ -20,8 +22,8 @@ Usage LLM :
|
|||||||
"""
|
"""
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
DESCRIPTION = "Administration réseau : ip, ping, traceroute, DNS, ports, firewall ufw/iptables"
|
DESCRIPTION = "Administration réseau : scan réseau, table ARP, ip, ping, traceroute, DNS, ports, firewall ufw/iptables"
|
||||||
USAGE = "SKILL:network ARGS:ip | ping <host> | traceroute <host> | dns <host> | ports | connections | firewall status|allow|deny|list | wget <url>"
|
USAGE = "SKILL:network ARGS:scan [subnet] | arp | ip | ping <host> | traceroute <host> | dns <host> | ports | connections | firewall status|allow|deny|list | wget <url>"
|
||||||
|
|
||||||
|
|
||||||
def _run(cmd: str, timeout: int = 20) -> str:
|
def _run(cmd: str, timeout: int = 20) -> str:
|
||||||
@@ -167,6 +169,31 @@ def run(args: str, context) -> str:
|
|||||||
return "Précise l'URL."
|
return "Précise l'URL."
|
||||||
return _run(f"curl -sI {url} | head -10")
|
return _run(f"curl -sI {url} | head -10")
|
||||||
|
|
||||||
|
if action == "scan":
|
||||||
|
# Détermine le subnet à scanner
|
||||||
|
subnet = rest.strip()
|
||||||
|
if not subnet:
|
||||||
|
# Auto-détecte le subnet depuis l'IP locale
|
||||||
|
iface_out = _run("ip -br addr show | grep -v '^lo' | head -1")
|
||||||
|
# Extrait le CIDR (ex: 192.168.7.5/24 → 192.168.7.0/24)
|
||||||
|
import re
|
||||||
|
m = re.search(r'(\d+\.\d+\.\d+)\.\d+/(\d+)', iface_out)
|
||||||
|
if m:
|
||||||
|
subnet = f"{m.group(1)}.0/{m.group(2)}"
|
||||||
|
else:
|
||||||
|
subnet = "192.168.0.0/24"
|
||||||
|
# Préfère nmap si dispo, sinon ping sweep
|
||||||
|
nmap_check = _run("which nmap")
|
||||||
|
if nmap_check and "nmap" in nmap_check:
|
||||||
|
return _run(f"nmap -sn --host-timeout 3s {subnet} -oG - | grep 'Up$' | awk '{{print $2, $3}}'", timeout=60)
|
||||||
|
# Fallback : arp-scan
|
||||||
|
arpscan_check = _run("which arp-scan")
|
||||||
|
if arpscan_check and "arp-scan" in arpscan_check:
|
||||||
|
return _run(f"arp-scan {subnet}", timeout=30)
|
||||||
|
# Fallback : table ARP après ping broadcast
|
||||||
|
_run(f"ping -c 1 -b {subnet.rsplit('.',1)[0]}.255 2>/dev/null || true", timeout=5)
|
||||||
|
return _run("arp -n")
|
||||||
|
|
||||||
if action == "arp":
|
if action == "arp":
|
||||||
return _run("arp -n")
|
return _run("arp -n")
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -147,7 +147,7 @@ def run(args: str, context) -> str:
|
|||||||
return "Format : save <nom> | <contenu du script>"
|
return "Format : save <nom> | <contenu du script>"
|
||||||
name_raw, content = rest.split("|", 1)
|
name_raw, content = rest.split("|", 1)
|
||||||
name = _safe_name(name_raw)
|
name = _safe_name(name_raw)
|
||||||
content = content.strip().replace("\\n", "\n")
|
content = content.strip().replace("\\n", "\n").replace('\\"', '"').replace("\\'", "'")
|
||||||
|
|
||||||
if not name:
|
if not name:
|
||||||
return "Nom de script invalide."
|
return "Nom de script invalide."
|
||||||
|
|||||||
Reference in New Issue
Block a user