From 7edbbad6b35bbaece5035be22190dbcd0940d291 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Thu, 9 Apr 2026 20:35:44 +0000 Subject: [PATCH] feat: scripts para automatizar creacion de robots y notificar developers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nuevos scripts: - convert-to-robot.sh: convierte scaffold de agente a robot (config minimo, agent.go con nil Rules, sin prompts, command_prefix vacio) - notify-developer.sh: envia DM a los developers (DEVELOPER_MATRIX_USERS) al crear un bot o agente, presentandose con nombre y tipo Mejorado: - create-full.sh: acepta --type robot para pipeline completo de robots (scaffold → build → register → verify → convert → notify) - .env.example: añade DEVELOPER_MATRIX_USERS para lista de developers Co-Authored-By: Claude Opus 4.6 (1M context) --- .env.example | 4 + dev-scripts/agent/convert-to-robot.sh | 132 ++++++++++++++++++++++++++ dev-scripts/agent/create-full.sh | 104 ++++++++++++++++---- dev-scripts/agent/notify-developer.sh | 95 ++++++++++++++++++ 4 files changed, 316 insertions(+), 19 deletions(-) create mode 100755 dev-scripts/agent/convert-to-robot.sh create mode 100755 dev-scripts/agent/notify-developer.sh diff --git a/.env.example b/.env.example index 6bfdc60..51960d2 100644 --- a/.env.example +++ b/.env.example @@ -49,6 +49,10 @@ STAGING_HOST=10.0.2.10 MONITORING_HOST=10.0.3.10 BASTION_HOST=bastion.example.com +# ── Desarrolladores (notificación al crear bots/agentes) ───── +# Lista separada por comas de usernames Matrix (sin @ ni :server) +DEVELOPER_MATRIX_USERS=egutierrez + # ── Matrix rooms (opcionales — el assistant-bot opera en DMs) ─ MATRIX_ROOM_DEVOPS= MATRIX_ROOM_ALERTS= diff --git a/dev-scripts/agent/convert-to-robot.sh b/dev-scripts/agent/convert-to-robot.sh new file mode 100755 index 0000000..7a21ac2 --- /dev/null +++ b/dev-scripts/agent/convert-to-robot.sh @@ -0,0 +1,132 @@ +#!/usr/bin/env bash +# convert-to-robot.sh — convierte un scaffold de agente a robot +# +# Uso: +# ./dev-scripts/agent/convert-to-robot.sh +# +# Cambios: +# 1. Reescribe config.yaml desde _template_robot (manteniendo Matrix/E2EE) +# 2. Simplifica agent.go (Rules() retorna nil) +# 3. Elimina prompts/ (robots no necesitan system prompt) +# 4. Pone command_prefix: "" por defecto (sin prefijo) + +source "$(dirname "$0")/../_common.sh" +load_env + +need_arg "${1:-}" + +ID="$1" +NORM="$(normalize_id "$ID")" +PACKAGE="$(echo "$ID" | tr '-' '_' | sed 's/_bot//')" +DIR="agents/$ID" +DEVICE_ID="" + +[[ -d "$DIR" ]] || fail "No existe agents/$ID — ejecuta create-full.sh primero" + +info "Convirtiendo $ID a robot..." + +# ── Extraer device_id del config actual ────────────────────────────────── +if [[ -f "$DIR/config.yaml" ]]; then + DEVICE_ID="$(grep -m1 'device_id:' "$DIR/config.yaml" | awk '{print $2}' | tr -d '"')" +fi + +# ── Reescribir config.yaml ─────────────────────────────────────────────── +cat > "$DIR/config.yaml" << YAML +# ============================================ +# ${ID} — Robot (command-only, sin LLM) +# ============================================ +agent: + id: ${ID} + name: "${2:-$ID}" + version: "0.1.0" + type: robot + enabled: true + template: false + description: "" + tags: [robot] + +personality: + prefix: "" + language: es + +matrix: + homeserver: "\${MATRIX_HOMESERVER}" + user_id: "@${ID}:\${MATRIX_SERVER_NAME}" + access_token_env: MATRIX_TOKEN_${NORM} + device_id: "${DEVICE_ID}" + + encryption: + enabled: true + store_path: "./agents/${ID}/data/crypto/" + pickle_key_env: PICKLE_KEY_${NORM} + trust_mode: tofu + recovery_key_env: SSSS_RECOVERY_KEY_${NORM} + + rooms: + listen: [] + respond: [] + admin: [] + + filters: + command_prefix: "" + mention_respond: false + dm_respond: false + ignore_bots: true + ignore_users: [] + unauthorized_response: silent + min_power_level: 0 + + threads: + enabled: true + auto_thread: false + +security: + audit: + enabled: false + log_file: "" + log_to_room: "" + include: [] + secrets: + provider: env + sanitize: + enabled: false + mode: warn + min_severity: medium + disabled_patterns: [] + tool_rate_limit: + enabled: false + max_calls_per_min: 10 + cleanup_interval_s: 60 +YAML + +ok "config.yaml reescrito como robot (command_prefix: \"\", sin LLM)" + +# ── Simplificar agent.go ───────────────────────────────────────────────── +cat > "$DIR/agent.go" << GO +package ${PACKAGE} + +import ( + "github.com/enmanuel/agents/agents" + "github.com/enmanuel/agents/pkg/decision" +) + +func init() { + agents.Register("${ID}", Rules) +} + +// Rules returns nil — robots only respond to commands. +func Rules() []decision.Rule { + return nil +} +GO + +ok "agent.go simplificado (Rules() retorna nil)" + +# ── Eliminar prompts/ ──────────────────────────────────────────────────── +if [[ -d "$DIR/prompts" ]]; then + rm -rf "$DIR/prompts" + ok "prompts/ eliminado (robots no necesitan system prompt)" +fi + +echo "" +ok "$ID convertido a robot" diff --git a/dev-scripts/agent/create-full.sh b/dev-scripts/agent/create-full.sh index 311aef7..dcbd918 100755 --- a/dev-scripts/agent/create-full.sh +++ b/dev-scripts/agent/create-full.sh @@ -1,17 +1,16 @@ #!/usr/bin/env bash -# create-full.sh — pipeline completo para crear un agente funcional +# create-full.sh — pipeline completo para crear un agente o robot funcional # -# Ejecuta en orden: scaffold → build → register → verify E2EE +# Ejecuta en orden: scaffold → build → register → verify E2EE → [convert robot] → [notify dev] # NO arranca el agente — primero personalizar agent.go, config.yaml y prompts/system.md # # Uso: -# ./dev-scripts/agent/create-full.sh "Display Name" -# -# Ejemplo: -# ./dev-scripts/agent/create-full.sh monitor-bot "Monitor Agent" +# ./dev-scripts/agent/create-full.sh "Display Name" # agente (default) +# ./dev-scripts/agent/create-full.sh "Display Name" --type robot # robot # # Requisitos en .env: # MATRIX_ADMIN_TOKEN, MATRIX_HOMESERVER, MATRIX_SERVER_NAME +# DEVELOPER_MATRIX_USERS (opcional, para notificación al developer) source "$(dirname "$0")/../_common.sh" load_env @@ -20,17 +19,47 @@ need_arg "${1:-}" ID="$1" DISPLAYNAME="${2:-$ID}" +TYPE="agent" NORM="$(normalize_id "$ID")" SCRIPT_DIR="$(dirname "$0")" +# Parse --type flag +shift 2 2>/dev/null || shift 1 2>/dev/null || true +while [[ $# -gt 0 ]]; do + case "$1" in + --type) + TYPE="${2:-agent}" + shift 2 + ;; + --type=*) + TYPE="${1#--type=}" + shift + ;; + *) + shift + ;; + esac +done + +if [[ "$TYPE" == "robot" ]]; then + TYPE_LABEL="robot" + TYPE_EMOJI="🤖" +else + TYPE_LABEL="agente" + TYPE_EMOJI="🧠" +fi + echo "" echo -e "${BLU}═══════════════════════════════════════════════════════${RST}" -echo -e "${BLU} Creando agente: ${GRN}$ID${BLU} ($DISPLAYNAME)${RST}" +echo -e "${BLU} Creando ${TYPE_LABEL}: ${GRN}$ID${BLU} ($DISPLAYNAME) ${TYPE_EMOJI}${RST}" echo -e "${BLU}═══════════════════════════════════════════════════════${RST}" echo "" # ── Paso 1: Scaffold ───────────────────────────────────────────────────── -info "Paso 1/4 — Scaffold (agent.go, config.yaml, prompts, launcher)" +TOTAL_STEPS=5 +[[ "$TYPE" == "robot" ]] && TOTAL_STEPS=6 + +info "Paso 1/${TOTAL_STEPS} — Scaffold (agent.go, config.yaml, prompts, launcher)" echo "" "$SCRIPT_DIR/new-agent.sh" "$ID" "$DISPLAYNAME" @@ -38,7 +67,7 @@ echo "" echo "" # ── Paso 2: Verificar compilación ───────────────────────────────────────── -info "Paso 2/4 — Verificando compilación..." +info "Paso 2/${TOTAL_STEPS} — Verificando compilación..." if "$GO" build -tags goolm ./... 2>&1; then ok "Compilación exitosa" @@ -49,7 +78,7 @@ fi echo "" # ── Paso 3: Registrar en Matrix ────────────────────────────────────────── -info "Paso 3/4 — Registrando en Matrix..." +info "Paso 3/${TOTAL_STEPS} — Registrando en Matrix..." echo "" # Reload .env in case new-agent.sh or previous steps changed it @@ -60,7 +89,7 @@ load_env echo "" # ── Paso 4: Verificar E2EE ─────────────────────────────────────────────── -info "Paso 4/4 — Verificación E2EE (cross-signing + recovery key)..." +info "Paso 4/${TOTAL_STEPS} — Verificación E2EE (cross-signing + recovery key)..." echo "" # Reload .env to pick up token, password, pickle key from register.sh @@ -70,15 +99,44 @@ load_env echo "" +# ── Paso 5 (robots): Convertir a robot ─────────────────────────────────── +if [[ "$TYPE" == "robot" ]]; then + info "Paso 5/${TOTAL_STEPS} — Convirtiendo a robot..." + echo "" + + "$SCRIPT_DIR/convert-to-robot.sh" "$ID" "$DISPLAYNAME" + + # Rebuild after conversion + info "Recompilando tras conversión..." + "$GO" build -tags goolm ./... 2>&1 || fail "Error de compilación tras conversión a robot" + ok "Recompilación exitosa" + + echo "" +fi + +# ── Paso final: Notificar al developer ─────────────────────────────────── +NOTIFY_STEP=$TOTAL_STEPS +info "Paso ${NOTIFY_STEP}/${TOTAL_STEPS} — Notificando a desarrolladores..." +echo "" + +# Reload .env (verify.sh may have added recovery key) +load_env + +"$SCRIPT_DIR/notify-developer.sh" "$ID" "$TYPE" "$DISPLAYNAME" || true + +echo "" + # ── Resumen ────────────────────────────────────────────────────────────── echo -e "${GRN}═══════════════════════════════════════════════════════${RST}" -echo -e "${GRN} ✓ Agente $ID creado exitosamente${RST}" +echo -e "${GRN} ✓ ${TYPE_LABEL^} $ID creado exitosamente ${TYPE_EMOJI}${RST}" echo -e "${GRN}═══════════════════════════════════════════════════════${RST}" echo "" echo -e " ${BLU}Archivos creados:${RST}" echo -e " agents/$ID/agent.go" echo -e " agents/$ID/config.yaml" -echo -e " agents/$ID/prompts/system.md" +if [[ "$TYPE" != "robot" ]]; then + echo -e " agents/$ID/prompts/system.md" +fi echo "" echo -e " ${BLU}Variables en .env:${RST}" echo -e " MATRIX_TOKEN_${NORM}" @@ -87,15 +145,23 @@ echo -e " PICKLE_KEY_${NORM}" echo -e " SSSS_RECOVERY_KEY_${NORM}" echo "" echo -e " ${BLU}Launcher actualizado:${RST}" -echo -e " cmd/launcher/main.go (import + rulesRegistry)" +echo -e " cmd/launcher/main.go (import)" echo "" echo -e "${YLW}Siguiente paso:${RST}" echo "" -echo -e " 1. Personalizar los archivos del agente:" -echo -e " ${DIM}agents/$ID/agent.go${RST} — reglas de decisión" -echo -e " ${DIM}agents/$ID/config.yaml${RST} — LLM, tools, personalidad" -echo -e " ${DIM}agents/$ID/prompts/system.md${RST} — system prompt" +if [[ "$TYPE" == "robot" ]]; then + echo -e " 1. Añadir comandos custom:" + echo -e " ${DIM}agents/$ID/commands.go${RST}" + echo "" + echo -e " 2. Registrar comandos en el launcher:" + echo -e " ${DIM}cmd/launcher/main.go${RST}" +else + echo -e " 1. Personalizar los archivos del agente:" + echo -e " ${DIM}agents/$ID/agent.go${RST} — reglas de decisión" + echo -e " ${DIM}agents/$ID/config.yaml${RST} — LLM, tools, personalidad" + echo -e " ${DIM}agents/$ID/prompts/system.md${RST} — system prompt" +fi echo "" -echo -e " 2. Arrancar:" +echo -e " Arrancar:" echo -e " ${DIM}./dev-scripts/server/start.sh${RST}" echo "" diff --git a/dev-scripts/agent/notify-developer.sh b/dev-scripts/agent/notify-developer.sh new file mode 100755 index 0000000..9e0abd6 --- /dev/null +++ b/dev-scripts/agent/notify-developer.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash +# notify-developer.sh — envía DM a los desarrolladores al crear un bot/agente +# +# Uso: +# ./dev-scripts/agent/notify-developer.sh +# +# Requisitos en .env: +# DEVELOPER_MATRIX_USERS — lista separada por comas de usernames Matrix +# Ejemplo: DEVELOPER_MATRIX_USERS=egutierrez,admin +# MATRIX_TOKEN_ — token del bot recién creado +# MATRIX_HOMESERVER, MATRIX_SERVER_NAME + +source "$(dirname "$0")/../_common.sh" +load_env + +ID="${1:-}" +TYPE="${2:-agent}" +DISPLAYNAME="${3:-$ID}" +NORM="$(normalize_id "$ID")" + +[[ -z "$ID" ]] && { warn "notify-developer: se necesita agent-id"; exit 0; } + +# ── Obtener token del bot ──────────────────────────────────────────────── +TOKEN_VAR="MATRIX_TOKEN_${NORM}" +TOKEN="${!TOKEN_VAR:-}" + +if [[ -z "$TOKEN" ]]; then + warn "notify-developer: $TOKEN_VAR no encontrado en .env — saltando notificación" + exit 0 +fi + +# ── Obtener lista de desarrolladores ───────────────────────────────────── +if [[ -z "${DEVELOPER_MATRIX_USERS:-}" ]]; then + warn "notify-developer: DEVELOPER_MATRIX_USERS no definido en .env — saltando" + exit 0 +fi + +# ── Construir mensaje ──────────────────────────────────────────────────── +if [[ "$TYPE" == "robot" ]]; then + EMOJI="🤖" + TYPE_LABEL="Robot" + COMMANDS_MSG="Mis comandos: help, ping, status, info, version" +else + EMOJI="🧠" + TYPE_LABEL="Agente" + COMMANDS_MSG="Escríbeme directamente o usa !help para ver mis comandos" +fi + +MSG="${EMOJI} ¡Hola! Soy **${DISPLAYNAME}** (${TYPE_LABEL}). Acabo de ser creado. ${COMMANDS_MSG}." + +# ── Enviar DM a cada desarrollador ─────────────────────────────────────── +IFS=',' read -ra DEVS <<< "$DEVELOPER_MATRIX_USERS" + +for dev in "${DEVS[@]}"; do + dev="$(echo "$dev" | xargs)" # trim spaces + [[ -z "$dev" ]] && continue + + USER_ID="@${dev}:${MATRIX_SERVER_NAME}" + info "Enviando DM de $ID a $USER_ID..." + + # Crear DM room (o reutilizar existente) + ROOM_RESP=$(curl -sf -X POST "${MATRIX_HOMESERVER}/_matrix/client/v3/createRoom" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d "{ + \"is_direct\": true, + \"invite\": [\"${USER_ID}\"], + \"preset\": \"trusted_private_chat\" + }" 2>&1) || { + warn " No se pudo crear DM room con $USER_ID" + continue + } + + ROOM_ID=$(echo "$ROOM_RESP" | grep -o '"room_id":"[^"]*"' | cut -d'"' -f4) + if [[ -z "$ROOM_ID" ]]; then + warn " Respuesta inesperada al crear room: $ROOM_RESP" + continue + fi + + # Enviar mensaje + TXN_ID="notify-$(date +%s%N)" + SEND_RESP=$(curl -sf -X PUT \ + "${MATRIX_HOMESERVER}/_matrix/client/v3/rooms/${ROOM_ID}/send/m.room.message/${TXN_ID}" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d "{ + \"msgtype\": \"m.text\", + \"body\": \"${MSG}\" + }" 2>&1) || { + warn " No se pudo enviar mensaje a $USER_ID" + continue + } + + ok "DM enviado a $USER_ID" +done