feat: agregar display name, health check y mejorar notificacion — issue 0044
Pipeline de creacion formalizado con 3 mejoras:
1. Display name automatico (paso 7): create-full.sh ahora configura
el display name en Matrix via PUT al profile API con el token
del bot recien creado. Evita que los bots aparezcan como @id:server.
2. Health check post-arranque: nuevo script health-check.sh que busca
en los logs del launcher mensajes de arranque exitoso ("e2ee ready",
"runner started", etc.) con timeout configurable (default 30s).
3. Notificacion enriquecida: notify-developer.sh ahora incluye
descripcion del agente (leida de config.yaml), tools habilitadas,
formato markdown con estructura clara, y reintentos con backoff
(hasta 3 intentos) si el envio de DM falla.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
# create-full.sh — pipeline completo para crear un agente o robot funcional
|
||||
#
|
||||
# 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
|
||||
# Pipeline de 7 pasos:
|
||||
# 1. SCAFFOLD → crear archivos base desde template
|
||||
# 2. BUILD → go build -tags goolm ./...
|
||||
# 3. REGISTER → crear usuario Matrix + token
|
||||
# 4. VERIFY E2EE → cross-signing + recovery key
|
||||
# 5. CONVERT (robot) → eliminar LLM/prompts si type=robot
|
||||
# 6. AUTO-AVATAR → generar y aplicar foto de perfil
|
||||
# 7. DISPLAY NAME → configurar nombre visible en Matrix
|
||||
#
|
||||
# Pasos posteriores (manuales o via father-bot):
|
||||
# 8. PERSONALIZE → config.yaml, agent.go, system prompt
|
||||
# 9. REBUILD → recompilar tras personalizacion
|
||||
# 10. START/RESTART → arrancar el launcher con el bot
|
||||
# 11. HEALTH CHECK → ./dev-scripts/agent/health-check.sh <id>
|
||||
# 12. SELF-INTRODUCE → ./dev-scripts/agent/notify-developer.sh <id>
|
||||
#
|
||||
# Uso:
|
||||
# ./dev-scripts/agent/create-full.sh <agent-id> "Display Name" # agente (default)
|
||||
@@ -56,8 +69,8 @@ echo -e "${BLU}═════════════════════
|
||||
echo ""
|
||||
|
||||
# ── Paso 1: Scaffold ─────────────────────────────────────────────────────
|
||||
TOTAL_STEPS=6
|
||||
[[ "$TYPE" == "robot" ]] && TOTAL_STEPS=7
|
||||
TOTAL_STEPS=7
|
||||
[[ "$TYPE" == "robot" ]] && TOTAL_STEPS=8
|
||||
|
||||
info "Paso 1/${TOTAL_STEPS} — Scaffold (agent.go, config.yaml, prompts, launcher)"
|
||||
echo ""
|
||||
@@ -115,7 +128,7 @@ if [[ "$TYPE" == "robot" ]]; then
|
||||
fi
|
||||
|
||||
# ── Paso auto-avatar: Generar avatar automatico ─────────────────────────
|
||||
AVATAR_STEP=$((TOTAL_STEPS - 1))
|
||||
AVATAR_STEP=$((TOTAL_STEPS - 2))
|
||||
info "Paso ${AVATAR_STEP}/${TOTAL_STEPS} — Generando avatar automatico..."
|
||||
echo ""
|
||||
|
||||
@@ -134,14 +147,39 @@ fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ── Paso display name: Configurar nombre visible en Matrix ──────────────
|
||||
DISPLAYNAME_STEP=$((TOTAL_STEPS - 1))
|
||||
info "Paso ${DISPLAYNAME_STEP}/${TOTAL_STEPS} — Configurando display name en Matrix..."
|
||||
echo ""
|
||||
|
||||
# Reload .env to pick up token from register.sh
|
||||
load_env
|
||||
|
||||
TOKEN_VAR="MATRIX_TOKEN_${NORM}"
|
||||
BOT_TOKEN="${!TOKEN_VAR:-}"
|
||||
|
||||
if [[ -n "$BOT_TOKEN" ]]; then
|
||||
USER_ID="@${ID}:${MATRIX_SERVER_NAME}"
|
||||
if curl -sf -X PUT \
|
||||
"${MATRIX_HOMESERVER}/_matrix/client/v3/profile/${USER_ID}/displayname" \
|
||||
-H "Authorization: Bearer $BOT_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"displayname\": \"${DISPLAYNAME}\"}" >/dev/null 2>&1; then
|
||||
ok "Display name configurado: $DISPLAYNAME"
|
||||
else
|
||||
warn "No se pudo configurar display name (se puede hacer despues manualmente)"
|
||||
fi
|
||||
else
|
||||
warn "Token del bot no encontrado — display name no configurado"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ── 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 ""
|
||||
@@ -167,21 +205,28 @@ echo ""
|
||||
echo -e " ${BLU}Launcher actualizado:${RST}"
|
||||
echo -e " cmd/launcher/main.go (import)"
|
||||
echo ""
|
||||
echo -e "${YLW}Siguiente paso:${RST}"
|
||||
echo -e "${YLW}Siguientes pasos (8-12 del pipeline):${RST}"
|
||||
echo ""
|
||||
if [[ "$TYPE" == "robot" ]]; then
|
||||
echo -e " 1. Añadir comandos custom:"
|
||||
echo -e " ${BLU}8. PERSONALIZE${RST} — 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}"
|
||||
echo -e " ${DIM}cmd/launcher/main.go${RST} (registrar comandos)"
|
||||
else
|
||||
echo -e " 1. Personalizar los archivos del agente:"
|
||||
echo -e " ${BLU}8. PERSONALIZE${RST} — 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 " Arrancar:"
|
||||
echo -e " ${BLU}9. REBUILD${RST}:"
|
||||
echo -e " ${DIM}go build -tags goolm ./...${RST}"
|
||||
echo ""
|
||||
echo -e " ${BLU}10. START${RST}:"
|
||||
echo -e " ${DIM}./dev-scripts/server/start.sh${RST}"
|
||||
echo ""
|
||||
echo -e " ${BLU}11. HEALTH CHECK${RST}:"
|
||||
echo -e " ${DIM}./dev-scripts/agent/health-check.sh $ID${RST}"
|
||||
echo ""
|
||||
echo -e " ${BLU}12. SELF-INTRODUCE${RST} (tras health check ok):"
|
||||
echo -e " ${DIM}./dev-scripts/agent/notify-developer.sh $ID $TYPE \"$DISPLAYNAME\"${RST}"
|
||||
echo ""
|
||||
|
||||
Executable
+69
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
# health-check.sh — verifica que un agente/robot esta activo tras arrancar
|
||||
#
|
||||
# Busca en los logs del launcher mensajes que indiquen arranque exitoso:
|
||||
# - "e2ee ready" o "starting matrix sync"
|
||||
# - "runner started" o "agent running"
|
||||
#
|
||||
# Uso:
|
||||
# ./dev-scripts/agent/health-check.sh <agent-id> [timeout_secs]
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 — agente activo y saludable
|
||||
# 1 — agente no encontrado o no arranco en el timeout
|
||||
|
||||
source "$(dirname "$0")/../_common.sh"
|
||||
|
||||
need_arg "${1:-}"
|
||||
|
||||
ID="$1"
|
||||
TIMEOUT="${2:-30}"
|
||||
LOG_FILE="$REPO_ROOT/run/launcher.log"
|
||||
|
||||
[[ -f "$LOG_FILE" ]] || fail "Log del launcher no encontrado: $LOG_FILE"
|
||||
|
||||
info "Verificando salud de $ID (timeout: ${TIMEOUT}s)..."
|
||||
|
||||
# Buscar mensajes de arranque exitoso del agente en los logs
|
||||
# Los mensajes contienen el agent ID en el campo "agent" del JSON
|
||||
check_health() {
|
||||
local found_sync=false
|
||||
local found_running=false
|
||||
|
||||
# Buscar en las ultimas 200 lineas del log (suficiente para un arranque reciente)
|
||||
local recent_logs
|
||||
recent_logs="$(tail -200 "$LOG_FILE" 2>/dev/null || true)"
|
||||
|
||||
# Buscar mensajes especificos del agente
|
||||
if echo "$recent_logs" | grep -q "\"agent\":\"${ID}\".*starting matrix sync\|\"agent\":\"${ID}\".*e2ee ready"; then
|
||||
found_sync=true
|
||||
fi
|
||||
|
||||
if echo "$recent_logs" | grep -q "\"agent\":\"${ID}\".*runner started\|\"agent\":\"${ID}\".*agent running"; then
|
||||
found_running=true
|
||||
fi
|
||||
|
||||
if $found_sync || $found_running; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Esperar hasta timeout
|
||||
elapsed=0
|
||||
while [[ $elapsed -lt $TIMEOUT ]]; do
|
||||
if check_health; then
|
||||
ok "Agente $ID esta activo y saludable"
|
||||
exit 0
|
||||
fi
|
||||
sleep 2
|
||||
elapsed=$((elapsed + 2))
|
||||
done
|
||||
|
||||
# Timeout — mostrar ultimas lineas relevantes del log para diagnostico
|
||||
warn "Agente $ID no confirmo arranque exitoso en ${TIMEOUT}s"
|
||||
echo ""
|
||||
echo "Ultimas lineas del log con $ID:"
|
||||
tail -50 "$LOG_FILE" 2>/dev/null | grep "$ID" | tail -10 || echo " (sin mensajes del agente)"
|
||||
echo ""
|
||||
exit 1
|
||||
@@ -1,6 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# notify-developer.sh — envía DM a los desarrolladores al crear un bot/agente
|
||||
#
|
||||
# El propio bot recién creado envía un mensaje de bienvenida enriquecido con:
|
||||
# - Nombre y tipo (agent/robot)
|
||||
# - Descripción (leída de config.yaml)
|
||||
# - Tools habilitadas (si es agent con tools)
|
||||
# - Instrucciones de uso
|
||||
#
|
||||
# Reintenta hasta 3 veces con backoff si el envío falla.
|
||||
#
|
||||
# Uso:
|
||||
# ./dev-scripts/agent/notify-developer.sh <agent-id> <type> <display-name>
|
||||
#
|
||||
@@ -35,18 +43,132 @@ if [[ -z "${DEVELOPER_MATRIX_USERS:-}" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ── Construir mensaje ────────────────────────────────────────────────────
|
||||
# ── Leer descripcion del config.yaml ────────────────────────────────────
|
||||
CONFIG_FILE=""
|
||||
for candidate in "agents/${ID}/config.yaml" "agents/_specials/${ID}/config.yaml"; do
|
||||
if [[ -f "$candidate" ]]; then
|
||||
CONFIG_FILE="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
DESCRIPTION=""
|
||||
TOOLS_LIST=""
|
||||
if [[ -n "$CONFIG_FILE" ]]; then
|
||||
# Extraer descripcion (entre comillas si las tiene)
|
||||
DESCRIPTION=$(grep -m1 '^\s*description:' "$CONFIG_FILE" | sed 's/.*description:\s*"\?\(.*\)"\?$/\1/' | sed 's/"$//')
|
||||
|
||||
# Extraer tools habilitadas (buscar lineas "enabled: true" dentro de secciones de tools)
|
||||
if grep -q 'tool_use:' "$CONFIG_FILE" 2>/dev/null; then
|
||||
TOOL_USE_ENABLED=$(awk '/tool_use:/,/^[^ ]/' "$CONFIG_FILE" | grep -m1 'enabled:' | awk '{print $2}')
|
||||
if [[ "$TOOL_USE_ENABLED" == "true" ]]; then
|
||||
# Listar secciones de tools habilitadas
|
||||
TOOLS_LIST=$(awk '/^tools:/,/^[a-z]/' "$CONFIG_FILE" | grep -B1 'enabled: true' | grep -v 'enabled' | grep -v '^--$' | sed 's/://g' | xargs 2>/dev/null || true)
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Construir mensaje enriquecido ────────────────────────────────────────
|
||||
if [[ "$TYPE" == "robot" ]]; then
|
||||
EMOJI="🤖"
|
||||
TYPE_LABEL="Robot"
|
||||
COMMANDS_MSG="Mis comandos: help, ping, status, info, version"
|
||||
USAGE_MSG="Mis comandos built-in: \`help\`, \`ping\`, \`status\`, \`info\`, \`version\`."
|
||||
USAGE_MSG="${USAGE_MSG}\nEscribeme directamente con un comando (sin prefijo \`!\`)."
|
||||
else
|
||||
EMOJI="🧠"
|
||||
TYPE_LABEL="Agente"
|
||||
COMMANDS_MSG="Escríbeme directamente o usa !help para ver mis comandos"
|
||||
USAGE_MSG="Escríbeme por DM o mencioname en un room."
|
||||
USAGE_MSG="${USAGE_MSG}\nUsa \`!help\` para ver mis comandos disponibles."
|
||||
fi
|
||||
|
||||
MSG="${EMOJI} ¡Hola! Soy **${DISPLAYNAME}** (${TYPE_LABEL}). Acabo de ser creado. ${COMMANDS_MSG}."
|
||||
# Construir mensaje markdown
|
||||
MSG="${EMOJI} **¡Hola! Soy ${DISPLAYNAME}** (${TYPE_LABEL})"
|
||||
MSG="${MSG}\n"
|
||||
|
||||
if [[ -n "$DESCRIPTION" ]]; then
|
||||
MSG="${MSG}\n${DESCRIPTION}"
|
||||
MSG="${MSG}\n"
|
||||
fi
|
||||
|
||||
if [[ -n "$TOOLS_LIST" ]]; then
|
||||
MSG="${MSG}\n**Herramientas:** ${TOOLS_LIST}"
|
||||
MSG="${MSG}\n"
|
||||
fi
|
||||
|
||||
MSG="${MSG}\n${USAGE_MSG}"
|
||||
|
||||
# ── Funcion de envio con reintentos ──────────────────────────────────────
|
||||
send_dm() {
|
||||
local user_id="$1"
|
||||
local max_retries=3
|
||||
local retry=0
|
||||
local backoff=2
|
||||
|
||||
while [[ $retry -lt $max_retries ]]; do
|
||||
# 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) || {
|
||||
retry=$((retry + 1))
|
||||
if [[ $retry -lt $max_retries ]]; then
|
||||
warn " Intento $retry/$max_retries fallo al crear room con $user_id — reintentando en ${backoff}s..."
|
||||
sleep "$backoff"
|
||||
backoff=$((backoff * 2))
|
||||
continue
|
||||
fi
|
||||
warn " No se pudo crear DM room con $user_id tras $max_retries intentos"
|
||||
return 1
|
||||
}
|
||||
|
||||
ROOM_ID=$(echo "$ROOM_RESP" | grep -o '"room_id":"[^"]*"' | cut -d'"' -f4)
|
||||
if [[ -z "$ROOM_ID" ]]; then
|
||||
retry=$((retry + 1))
|
||||
if [[ $retry -lt $max_retries ]]; then
|
||||
warn " Respuesta inesperada — reintentando en ${backoff}s..."
|
||||
sleep "$backoff"
|
||||
backoff=$((backoff * 2))
|
||||
continue
|
||||
fi
|
||||
warn " Respuesta inesperada al crear room: $ROOM_RESP"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Enviar mensaje con formato markdown
|
||||
TXN_ID="notify-$(date +%s%N)"
|
||||
# Escapar newlines para el JSON
|
||||
MSG_ESCAPED=$(echo -e "$MSG")
|
||||
MSG_JSON=$(echo -e "$MSG" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read().rstrip()))" 2>/dev/null || echo "\"${MSG}\"")
|
||||
|
||||
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_JSON},
|
||||
\"format\": \"org.matrix.custom.html\",
|
||||
\"formatted_body\": ${MSG_JSON}
|
||||
}" 2>&1) || {
|
||||
retry=$((retry + 1))
|
||||
if [[ $retry -lt $max_retries ]]; then
|
||||
warn " Intento $retry/$max_retries fallo al enviar mensaje — reintentando en ${backoff}s..."
|
||||
sleep "$backoff"
|
||||
backoff=$((backoff * 2))
|
||||
continue
|
||||
fi
|
||||
warn " No se pudo enviar mensaje a $user_id tras $max_retries intentos"
|
||||
return 1
|
||||
}
|
||||
|
||||
ok "DM enviado a $user_id"
|
||||
return 0
|
||||
done
|
||||
}
|
||||
|
||||
# ── Enviar DM a cada desarrollador ───────────────────────────────────────
|
||||
IFS=',' read -ra DEVS <<< "$DEVELOPER_MATRIX_USERS"
|
||||
@@ -58,38 +180,5 @@ for dev in "${DEVS[@]}"; do
|
||||
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"
|
||||
send_dm "$USER_ID"
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user