From ba59e3b4c55b3113523e90e424b64c8119fca217 Mon Sep 17 00:00:00 2001 From: sylvain Date: Mon, 23 Mar 2026 19:11:23 +0000 Subject: [PATCH] fix: script.py guillemets, cron bad minute, system_prompt scripts bash Co-Authored-By: Claude Sonnet 4.6 --- config/system_prompt.txt | 27 ++++++++++++++++++++------- scripts/publish_updates.sh | 12 ++++++++++++ skills/cron.py | 28 +++++++++++++++++++++------- skills/script.py | 2 +- 4 files changed, 54 insertions(+), 15 deletions(-) create mode 100755 scripts/publish_updates.sh diff --git a/config/system_prompt.txt b/config/system_prompt.txt index 9272d07..3c06361 100644 --- a/config/system_prompt.txt +++ b/config/system_prompt.txt @@ -22,17 +22,30 @@ Tu reçois des instructions via MQTT (depuis Nexus) ou XMPP (directement). 1. Utilise toujours le skill le plus spécifique disponible 2. Préfère plusieurs appels de skills atomiques plutôt qu'une commande shell complexe 3. Après chaque action importante (install, restart, delete), vérifie le résultat -4. Si une tâche génère un script, utilise SKILL:script pour le créer et l'exécuter, - et le résultat sera automatiquement renvoyé via MQTT +4. **OBLIGATOIRE : tout script bash doit être créé via `SKILL:script ARGS:save | ` et jamais via `filesystem write`.** N'utilise JAMAIS filesystem pour écrire un fichier .sh. 5. En cas d'erreur, diagnostique avant de réessayer 6. Réponds toujours en français 7. Sois concis dans tes réponses — l'essentiel, pas tout le stdout brut -## Communication MQTT +## Écriture de scripts bash — règles strictes + +### ❌ Interdit dans les scripts bash +- `muc_send`, `mqtt_send`, `shell` et tous les noms de skills — ce ne sont PAS des commandes bash +- Les guillemets échappés : écris `"texte"` et non `\"texte\"` + +### ✅ Pour envoyer un message depuis un script +Variables injectées automatiquement : `$MQTT_BROKER`, `$MQTT_REPLY_TOPIC`, `$AGENT_ID` + +```bash +mosquitto_pub -h "$MQTT_BROKER" -t "$MQTT_REPLY_TOPIC" -m "mon résultat" +``` + +### ✅ Bonnes pratiques +- Commence toujours par `#!/bin/bash` et `set -euo pipefail` +- Guillemets doubles autour des variables : `"$VAR"` +- Gère les cas d'erreur avec des messages explicites + +## Communication MQTT (depuis le LLM, pas depuis un script) Tu peux envoyer des messages à d'autres agents : 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" diff --git a/scripts/publish_updates.sh b/scripts/publish_updates.sh new file mode 100755 index 0000000..5e0696d --- /dev/null +++ b/scripts/publish_updates.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# Récupération des mises à jour en filtrant l'en‑tête \"Listing...\" +updates=$(apt list --upgradable 2>/dev/null | grep -v \"^Listing\") + +if [ -z \"$updates\" ]; then + payload='{\"type\":\"updates\",\"payload\":\"Aucune mise à jour disponible\"}' +else + payload=$(jq -nc --arg p \"$updates\" '{\"type\":\"updates\",\"payload\":$p}') +fi + +mosquitto_pub -h \"${MQTT_BROKER}\" -t \"${MQTT_REPLY_TOPIC}\" -m \"${payload}\" diff --git a/skills/cron.py b/skills/cron.py index cf8d52a..9462947 100644 --- a/skills/cron.py +++ b/skills/cron.py @@ -35,6 +35,16 @@ def _run(cmd: str, timeout: int = 10) -> str: 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: parts = args.strip().split(None, 1) action = parts[0].lower() if parts else "list" @@ -54,26 +64,28 @@ def run(args: str, context) -> str: command = " ".join(words[5:]) entry = f"{cron_expr} {command}" - current = _run("crontab -l 2>/dev/null") + current = _get_current_crontab() if entry in current: return f"Cette entrée existe déjà : {entry}" def _do_add(): 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(entry + "\n") tmpfile = f.name - out = _run(f"crontab {tmpfile}") + result = subprocess.run(f"crontab {tmpfile}", shell=True, text=True, capture_output=True) 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) if action == "remove": if not rest: 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] removed_count = len(current.splitlines()) - len(lines) 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: f.write(new_cron + "\n") tmpfile = f.name - out = _run(f"crontab {tmpfile}") + result = subprocess.run(f"crontab {tmpfile}", shell=True, text=True, capture_output=True) 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) diff --git a/skills/script.py b/skills/script.py index 112307a..ce58ee8 100644 --- a/skills/script.py +++ b/skills/script.py @@ -147,7 +147,7 @@ def run(args: str, context) -> str: return "Format : save | " name_raw, content = rest.split("|", 1) name = _safe_name(name_raw) - content = content.strip().replace("\\n", "\n") + content = content.strip().replace("\\n", "\n").replace('\\"', '"').replace("\\'", "'") if not name: return "Nom de script invalide."