feat: script skill with MQTT execution notifications

- Publish to agents/scripts/execution after exec/run actions
- Dynamic scripts_dir from queue_db config or scripts_dir key
- Directory traversal prevention with _safe_name()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-15 19:49:07 +00:00
parent 6f87ee908c
commit b70e95b71f
+19 -1
View File
@@ -18,10 +18,12 @@ Usage LLM :
SKILL:script ARGS:run | <contenu inline> SKILL:script ARGS:run | <contenu inline>
SKILL:script ARGS:delete <nom> SKILL:script ARGS:delete <nom>
""" """
import json
import os import os
import stat import stat
import subprocess import subprocess
import tempfile import tempfile
from datetime import datetime
DESCRIPTION = "Bibliothèque de scripts bash : sauvegarder, lister, afficher, exécuter" DESCRIPTION = "Bibliothèque de scripts bash : sauvegarder, lister, afficher, exécuter"
USAGE = ( USAGE = (
@@ -67,6 +69,19 @@ def _build_env(context, scripts_dir: str) -> dict:
return env return env
def _notify(context, script_name: str, result: str):
"""Publie un événement d'exécution sur MQTT pour que Nexus notifie l'utilisateur."""
try:
context.mqtt.publish_raw("agents/scripts/execution", json.dumps({
"agent_id": context.agent_id,
"script": script_name,
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"result": result[:1000],
}))
except Exception:
pass
def _run_script(cmd: str, env: dict, timeout: int = 120) -> str: def _run_script(cmd: str, env: dict, timeout: int = 120) -> str:
try: try:
result = subprocess.run( result = subprocess.run(
@@ -145,7 +160,9 @@ def run(args: str, context) -> str:
if not os.path.exists(path): if not os.path.exists(path):
return f"Script '{name}' introuvable. Utilise 'list' pour voir les scripts disponibles." return f"Script '{name}' introuvable. Utilise 'list' pour voir les scripts disponibles."
env = _build_env(context, d) env = _build_env(context, d)
return _run_script(f'"{path}" {sargs}', env=env, timeout=120) out = _run_script(f'"{path}" {sargs}', env=env, timeout=120)
_notify(context, name, out)
return out
# ── run (inline) ────────────────────────────────────────────────────── # ── run (inline) ──────────────────────────────────────────────────────
if action == "run": if action == "run":
@@ -162,6 +179,7 @@ def run(args: str, context) -> str:
env = _build_env(context, d) env = _build_env(context, d)
out = _run_script(tmpfile, env=env, timeout=60) out = _run_script(tmpfile, env=env, timeout=60)
os.unlink(tmpfile) os.unlink(tmpfile)
_notify(context, "<inline>", out)
return out return out
# ── delete ──────────────────────────────────────────────────────────── # ── delete ────────────────────────────────────────────────────────────