feat: add assistant bot with LLM integration and configuration
- Implemented the assistant bot with basic command handling and LLM routing. - Created configuration file for the assistant bot with personality, behavior, and LLM settings. - Added system prompt for the assistant bot to define its capabilities and limitations. - Developed registration script for creating Matrix bot users via Synapse admin API. - Introduced common development scripts for agent management (start, stop, list, logs). - Scaffolded new agent creation script to streamline the addition of new agents. - Implemented agent removal script to disable agents without deleting data.
This commit is contained in:
Executable
+86
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env bash
|
||||
# _common.sh — sourced by all dev-scripts. Do not run directly.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── Colores ────────────────────────────────────────────────────────────────
|
||||
RED='\033[0;31m'
|
||||
GRN='\033[0;32m'
|
||||
YLW='\033[0;33m'
|
||||
BLU='\033[0;34m'
|
||||
DIM='\033[2m'
|
||||
RST='\033[0m'
|
||||
|
||||
ok() { echo -e "${GRN}✓${RST} $*"; }
|
||||
info() { echo -e "${BLU}→${RST} $*"; }
|
||||
warn() { echo -e "${YLW}!${RST} $*"; }
|
||||
fail() { echo -e "${RED}✗${RST} $*" >&2; exit 1; }
|
||||
dim() { echo -e "${DIM}$*${RST}"; }
|
||||
|
||||
# ── Repo root ──────────────────────────────────────────────────────────────
|
||||
# Scripts can be called from any directory; we always operate from repo root.
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
# ── .env loader ───────────────────────────────────────────────────────────
|
||||
load_env() {
|
||||
local env_file="${1:-.env}"
|
||||
[[ -f "$env_file" ]] || fail ".env not found at $REPO_ROOT/$env_file — copy .env.example and fill it in"
|
||||
# Export only lines that are KEY=VALUE (skip comments and blanks)
|
||||
set -o allexport
|
||||
# shellcheck disable=SC1090
|
||||
source <(grep -E '^[A-Z_]+=.+' "$env_file")
|
||||
set +o allexport
|
||||
}
|
||||
|
||||
# ── Go tooling ─────────────────────────────────────────────────────────────
|
||||
GO=${GO_BIN:-go}
|
||||
export PATH="$PATH:/usr/local/go/bin"
|
||||
command -v "$GO" &>/dev/null || fail "go not found — install Go or set GO_BIN"
|
||||
|
||||
# ── Process helpers ────────────────────────────────────────────────────────
|
||||
RUN_DIR="$REPO_ROOT/run"
|
||||
mkdir -p "$RUN_DIR"
|
||||
|
||||
pid_file() { echo "$RUN_DIR/$1.pid"; }
|
||||
log_file() { echo "$RUN_DIR/$1.log"; }
|
||||
|
||||
read_pid() {
|
||||
local f; f="$(pid_file "$1")"
|
||||
[[ -f "$f" ]] && cat "$f" || echo 0
|
||||
}
|
||||
|
||||
is_running() {
|
||||
local pid; pid="$(read_pid "$1")"
|
||||
[[ "$pid" -gt 0 ]] && kill -0 "$pid" 2>/dev/null
|
||||
}
|
||||
|
||||
agent_status() {
|
||||
local id="$1" enabled="$2"
|
||||
if [[ "$enabled" != "true" ]]; then
|
||||
echo "disabled"
|
||||
elif is_running "$id"; then
|
||||
echo "running"
|
||||
else
|
||||
echo "stopped"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Agent discovery ────────────────────────────────────────────────────────
|
||||
# Prints: id|version|enabled|description (one line per agent)
|
||||
list_agents_raw() {
|
||||
for cfg in agents/*/config.yaml; do
|
||||
[[ -f "$cfg" ]] || continue
|
||||
local id version enabled desc
|
||||
id=$(grep -m1 '^ id:' "$cfg" | awk '{print $2}')
|
||||
version=$(grep -m1 '^ version:' "$cfg" | awk '{print $2}' | tr -d '"')
|
||||
enabled=$(grep -m1 '^ enabled:' "$cfg" | awk '{print $2}')
|
||||
desc=$(grep -m1 '^ description:' "$cfg" | cut -d'"' -f2)
|
||||
[[ -n "$id" ]] && echo "${id}|${version}|${enabled}|${desc}|${cfg}"
|
||||
done
|
||||
}
|
||||
|
||||
# ── Usage helper ──────────────────────────────────────────────────────────
|
||||
need_arg() {
|
||||
[[ -n "${1:-}" ]] || { echo "Usage: $0 <agent-id>"; exit 1; }
|
||||
}
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
# list.sh — muestra todos los agentes y su estado actual
|
||||
# Uso: ./dev-scripts/list.sh
|
||||
|
||||
source "$(dirname "$0")/_common.sh"
|
||||
|
||||
printf "%-22s %-12s %-8s %s\n" "ID" "STATUS" "VERSION" "DESCRIPTION"
|
||||
printf '%s\n' "$(printf '─%.0s' {1..70})"
|
||||
|
||||
while IFS='|' read -r id version enabled desc _cfg; do
|
||||
status=$(agent_status "$id" "$enabled")
|
||||
|
||||
case "$status" in
|
||||
running) label="${GRN}● running${RST}" ;;
|
||||
stopped) label="${DIM}○ stopped${RST}" ;;
|
||||
disabled) label="${YLW} disabled${RST}" ;;
|
||||
*) label="$status" ;;
|
||||
esac
|
||||
|
||||
# Truncate description
|
||||
[[ ${#desc} -gt 38 ]] && desc="${desc:0:37}…"
|
||||
|
||||
printf "%-22s " "$id"
|
||||
printf "${label}"
|
||||
printf " %-8s %s\n" "$version" "$desc"
|
||||
|
||||
done < <(list_agents_raw)
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
# logs.sh — sigue los logs de uno o todos los agentes
|
||||
#
|
||||
# Uso:
|
||||
# ./dev-scripts/logs.sh # tail -f de todos los logs activos
|
||||
# ./dev-scripts/logs.sh assistant-bot # solo ese agente
|
||||
# ./dev-scripts/logs.sh assistant-bot 100 # últimas 100 líneas
|
||||
|
||||
source "$(dirname "$0")/_common.sh"
|
||||
|
||||
TARGET="${1:-}"
|
||||
LINES="${2:-50}"
|
||||
|
||||
log_files=()
|
||||
|
||||
while IFS='|' read -r id _version _enabled _desc _cfg; do
|
||||
[[ -n "$TARGET" && "$id" != "$TARGET" ]] && continue
|
||||
local_log="$(log_file "$id")"
|
||||
[[ -f "$local_log" ]] && log_files+=("$local_log")
|
||||
done < <(list_agents_raw)
|
||||
|
||||
if [[ "${#log_files[@]}" -eq 0 ]]; then
|
||||
[[ -n "$TARGET" ]] && fail "No hay logs para '$TARGET' (¿ha sido iniciado alguna vez?)"
|
||||
fail "No hay logs todavía — inicia algún agente primero"
|
||||
fi
|
||||
|
||||
info "Siguiendo logs: ${log_files[*]}"
|
||||
dim " Ctrl+C para salir"
|
||||
echo ""
|
||||
|
||||
tail -n "$LINES" -f "${log_files[@]}"
|
||||
Executable
+358
@@ -0,0 +1,358 @@
|
||||
#!/usr/bin/env bash
|
||||
# new-agent.sh — genera el scaffold de un nuevo agente
|
||||
#
|
||||
# Uso:
|
||||
# ./dev-scripts/new-agent.sh <agent-id> [displayname]
|
||||
#
|
||||
# Ejemplo:
|
||||
# ./dev-scripts/new-agent.sh monitor-bot "Monitor Agent"
|
||||
#
|
||||
# Crea:
|
||||
# agents/<agent-id>/config.yaml (basado en el assistant como plantilla)
|
||||
# agents/<agent-id>/agent.go (reglas puras vacías, listo para extender)
|
||||
# agents/<agent-id>/prompts/ (directorio para system prompt)
|
||||
# agents/<agent-id>/data/ (directorio de datos, en .gitignore)
|
||||
#
|
||||
# También te recuerda los dos pasos manuales que quedan.
|
||||
|
||||
source "$(dirname "$0")/_common.sh"
|
||||
load_env
|
||||
|
||||
need_arg "${1:-}"
|
||||
|
||||
ID="$1"
|
||||
DISPLAYNAME="${2:-$ID}"
|
||||
PACKAGE="$(echo "$ID" | tr '-' '_' | sed 's/_bot//')" # "monitor-bot" → "monitor"
|
||||
DIR="agents/$ID"
|
||||
|
||||
[[ -d "$DIR" ]] && fail "Ya existe agents/$ID — ¿ya fue creado?"
|
||||
|
||||
info "Creando scaffold para $ID..."
|
||||
|
||||
mkdir -p "$DIR/prompts" "$DIR/data"
|
||||
|
||||
# ── config.yaml ────────────────────────────────────────────────────────────
|
||||
cat > "$DIR/config.yaml" <<YAML
|
||||
# ============================================
|
||||
# IDENTIDAD
|
||||
# ============================================
|
||||
agent:
|
||||
id: $ID
|
||||
name: "$DISPLAYNAME"
|
||||
version: "1.0.0"
|
||||
enabled: true
|
||||
description: "Descripción del agente $DISPLAYNAME"
|
||||
tags: [$(echo "$ID" | tr '-' ',')]
|
||||
|
||||
# ============================================
|
||||
# PERSONALIDAD Y COMPORTAMIENTO
|
||||
# ============================================
|
||||
personality:
|
||||
tone: friendly
|
||||
verbosity: concise
|
||||
language: es
|
||||
languages_supported: [es, en]
|
||||
emoji_style: minimal
|
||||
prefix: "🤖"
|
||||
error_style: helpful
|
||||
|
||||
templates:
|
||||
greeting: "Hola, soy $DISPLAYNAME. ¿En qué puedo ayudarte?"
|
||||
unknown_command: "No reconozco ese comando. Escríbeme directamente."
|
||||
permission_denied: "No tengo permiso para hacer eso."
|
||||
error: "Algo salió mal: {{.Error}}"
|
||||
success: "{{.Summary}}"
|
||||
busy: "Procesando, dame un momento..."
|
||||
|
||||
behavior:
|
||||
proactive: false
|
||||
ask_confirmation: false
|
||||
show_reasoning: false
|
||||
thread_replies: true
|
||||
typing_indicator: true
|
||||
acknowledge_receipt: false
|
||||
|
||||
# ============================================
|
||||
# LLM
|
||||
# ============================================
|
||||
llm:
|
||||
primary:
|
||||
provider: openai
|
||||
model: gpt-4o
|
||||
api_key_env: OPENAI_API_KEY
|
||||
base_url: ""
|
||||
max_tokens: 4096
|
||||
temperature: 0.7
|
||||
|
||||
fallback:
|
||||
provider: ""
|
||||
model: ""
|
||||
api_key_env: ""
|
||||
base_url: ""
|
||||
max_tokens: 0
|
||||
temperature: 0
|
||||
|
||||
reasoning:
|
||||
system_prompt_file: "prompts/system.md"
|
||||
context_window: 16384
|
||||
memory_messages: 20
|
||||
|
||||
tool_use:
|
||||
enabled: false
|
||||
max_iterations: 3
|
||||
parallel_calls: false
|
||||
|
||||
rate_limit:
|
||||
requests_per_minute: 30
|
||||
tokens_per_minute: 100000
|
||||
concurrent_requests: 3
|
||||
|
||||
# ============================================
|
||||
# TOOLS — ajustar según necesidades del agente
|
||||
# ============================================
|
||||
tools:
|
||||
ssh:
|
||||
enabled: false
|
||||
allowed_targets: []
|
||||
forbidden_commands: []
|
||||
timeout: 0s
|
||||
max_concurrent: 0
|
||||
require_confirmation: []
|
||||
http:
|
||||
enabled: false
|
||||
allowed_domains: []
|
||||
timeout: 0s
|
||||
max_retries: 0
|
||||
scripts:
|
||||
enabled: false
|
||||
scripts_dir: ""
|
||||
allowed: []
|
||||
timeout: 0s
|
||||
sandbox: false
|
||||
file_ops:
|
||||
enabled: false
|
||||
allowed_paths: []
|
||||
read_only: true
|
||||
mcp:
|
||||
enabled: false
|
||||
servers: []
|
||||
expose:
|
||||
port: 0
|
||||
tools: []
|
||||
|
||||
# ============================================
|
||||
# MATRIX
|
||||
# ============================================
|
||||
matrix:
|
||||
homeserver: "${MATRIX_HOMESERVER}"
|
||||
user_id: "@$ID:${MATRIX_SERVER_NAME}"
|
||||
access_token_env: MATRIX_TOKEN_$(echo "$ID" | tr '[:lower:]-' '[:upper:]_')
|
||||
device_id: "$(echo "$ID" | tr '[:lower:]-' '[:upper:]_')01"
|
||||
|
||||
encryption:
|
||||
enabled: false
|
||||
store_path: "./data/crypto/"
|
||||
trust_mode: tofu
|
||||
|
||||
rooms:
|
||||
listen: []
|
||||
respond: []
|
||||
admin: []
|
||||
|
||||
filters:
|
||||
command_prefix: "!"
|
||||
mention_respond: true
|
||||
dm_respond: true
|
||||
ignore_bots: true
|
||||
ignore_users: []
|
||||
min_power_level: 0
|
||||
|
||||
# ============================================
|
||||
# INTER-AGENTES
|
||||
# ============================================
|
||||
agents:
|
||||
peers: []
|
||||
delegation:
|
||||
enabled: false
|
||||
can_delegate_to: []
|
||||
can_receive_from: []
|
||||
max_delegation_depth: 1
|
||||
timeout: 30s
|
||||
protocol:
|
||||
format: json
|
||||
channel: matrix
|
||||
heartbeat_interval: 60s
|
||||
|
||||
# ============================================
|
||||
# SSH
|
||||
# ============================================
|
||||
ssh:
|
||||
defaults:
|
||||
user: ""
|
||||
port: 22
|
||||
key_file_env: ""
|
||||
known_hosts: ""
|
||||
keepalive_interval: 0s
|
||||
timeout: 0s
|
||||
targets: {}
|
||||
|
||||
# ============================================
|
||||
# SEGURIDAD
|
||||
# ============================================
|
||||
security:
|
||||
roles:
|
||||
admin:
|
||||
users: ["@admin:\${MATRIX_SERVER_NAME}"]
|
||||
actions: ["*"]
|
||||
user:
|
||||
users: ["*"]
|
||||
actions: ["help"]
|
||||
audit:
|
||||
enabled: false
|
||||
log_file: "./data/audit.log"
|
||||
log_to_room: ""
|
||||
include: []
|
||||
secrets:
|
||||
provider: env
|
||||
|
||||
# ============================================
|
||||
# SCHEDULING
|
||||
# ============================================
|
||||
schedules: []
|
||||
|
||||
# ============================================
|
||||
# OBSERVABILIDAD
|
||||
# ============================================
|
||||
observability:
|
||||
logging:
|
||||
level: info
|
||||
format: json
|
||||
output: stdout
|
||||
file: "./data/$ID.log"
|
||||
metrics:
|
||||
enabled: false
|
||||
port: 0
|
||||
path: /metrics
|
||||
export: prometheus
|
||||
health:
|
||||
enabled: true
|
||||
port: 0
|
||||
path: /healthz
|
||||
tracing:
|
||||
enabled: false
|
||||
provider: ""
|
||||
endpoint: ""
|
||||
|
||||
# ============================================
|
||||
# RESILIENCIA
|
||||
# ============================================
|
||||
resilience:
|
||||
circuit_breaker:
|
||||
failure_threshold: 5
|
||||
timeout: 30s
|
||||
half_open_max: 2
|
||||
retry:
|
||||
max_attempts: 2
|
||||
backoff: exponential
|
||||
initial_delay: 1s
|
||||
max_delay: 10s
|
||||
shutdown:
|
||||
timeout: 10s
|
||||
drain_messages: true
|
||||
save_state: false
|
||||
state_file: ""
|
||||
queue:
|
||||
enabled: true
|
||||
max_size: 50
|
||||
priority_users: ["@admin:\${MATRIX_SERVER_NAME}"]
|
||||
|
||||
# ============================================
|
||||
# ALMACENAMIENTO
|
||||
# ============================================
|
||||
storage:
|
||||
state:
|
||||
backend: sqlite
|
||||
path: "./data/$ID.db"
|
||||
cache:
|
||||
enabled: true
|
||||
backend: memory
|
||||
ttl: 5m
|
||||
max_entries: 200
|
||||
history:
|
||||
backend: sqlite
|
||||
path: "./data/history.db"
|
||||
retention: 168h
|
||||
YAML
|
||||
|
||||
# ── agent.go ───────────────────────────────────────────────────────────────
|
||||
cat > "$DIR/agent.go" <<GO
|
||||
// Package $PACKAGE defines the pure rules for the $DISPLAYNAME.
|
||||
package $PACKAGE
|
||||
|
||||
import "github.com/enmanuel/agents/pkg/decision"
|
||||
|
||||
// Rules returns the decision rules for the $ID.
|
||||
func Rules() []decision.Rule {
|
||||
return []decision.Rule{
|
||||
{
|
||||
Name: "help",
|
||||
Match: decision.MatchCommand("help"),
|
||||
Actions: []decision.Action{{
|
||||
Kind: decision.ActionKindReply,
|
||||
Reply: &decision.ReplyAction{
|
||||
Content: "Soy $DISPLAYNAME. Escríbeme lo que necesitas.",
|
||||
},
|
||||
}},
|
||||
},
|
||||
// Catch-all: DMs y menciones van al LLM
|
||||
{
|
||||
Name: "llm-fallback",
|
||||
Match: func(ctx decision.MessageContext) bool {
|
||||
return ctx.IsDirectMsg || ctx.IsMention
|
||||
},
|
||||
Actions: []decision.Action{{
|
||||
Kind: decision.ActionKindLLM,
|
||||
LLM: &decision.LLMAction{},
|
||||
}},
|
||||
},
|
||||
}
|
||||
}
|
||||
GO
|
||||
|
||||
# ── system prompt ──────────────────────────────────────────────────────────
|
||||
cat > "$DIR/prompts/system.md" <<MD
|
||||
# $DISPLAYNAME — System Prompt
|
||||
|
||||
Eres $DISPLAYNAME. Describe aquí el rol, capacidades y restricciones del agente.
|
||||
|
||||
## Rol
|
||||
...
|
||||
|
||||
## Capacidades
|
||||
...
|
||||
|
||||
## Restricciones
|
||||
...
|
||||
MD
|
||||
|
||||
ok "Scaffold creado en $DIR/"
|
||||
echo ""
|
||||
|
||||
# ── Pasos siguientes ──────────────────────────────────────────────────────
|
||||
echo -e "${YLW}Quedan 2 pasos manuales:${RST}"
|
||||
echo ""
|
||||
echo -e " ${BLU}1.${RST} Añade una línea en ${BLU}cmd/launcher/main.go${RST}:"
|
||||
echo ""
|
||||
echo -e ' import ('
|
||||
echo -e " ${GRN}${PACKAGE}agent \"github.com/enmanuel/agents/agents/$ID\"${RST}"
|
||||
echo -e ' )'
|
||||
echo ""
|
||||
echo -e ' var rulesRegistry = map[string]func() []decision.Rule{'
|
||||
echo -e " ${GRN}\"$ID\": ${PACKAGE}agent.Rules,${RST}"
|
||||
echo -e ' ...'
|
||||
echo -e ' }'
|
||||
echo ""
|
||||
echo -e " ${BLU}2.${RST} Registra el bot en Matrix y añade el token a .env:"
|
||||
echo ""
|
||||
echo -e " ${DIM}./dev-scripts/register.sh $ID \"$DISPLAYNAME\"${RST}"
|
||||
echo ""
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
# register.sh — registra un nuevo bot en el servidor Matrix via Synapse admin API
|
||||
#
|
||||
# Uso:
|
||||
# ./dev-scripts/register.sh <username> [displayname] [env-var-name]
|
||||
#
|
||||
# Ejemplos:
|
||||
# ./dev-scripts/register.sh assistant-bot "Assistant" MATRIX_TOKEN_ASSISTANT
|
||||
# ./dev-scripts/register.sh devops-bot "DevOps Agent" MATRIX_TOKEN_DEVOPS
|
||||
#
|
||||
# Requiere en .env:
|
||||
# MATRIX_ADMIN_TOKEN=syt_...
|
||||
# MATRIX_HOMESERVER=https://...
|
||||
|
||||
source "$(dirname "$0")/_common.sh"
|
||||
load_env
|
||||
|
||||
need_arg "${1:-}"
|
||||
|
||||
USERNAME="$1"
|
||||
DISPLAYNAME="${2:-$USERNAME}"
|
||||
ENV_VAR="${3:-MATRIX_TOKEN_$(echo "$USERNAME" | tr '[:lower:]-' '[:upper:]_')}"
|
||||
|
||||
[[ -n "${MATRIX_ADMIN_TOKEN:-}" ]] || fail "MATRIX_ADMIN_TOKEN no está en .env"
|
||||
[[ -n "${MATRIX_HOMESERVER:-}" ]] || fail "MATRIX_HOMESERVER no está en .env"
|
||||
|
||||
info "Registrando @${USERNAME}:${MATRIX_SERVER_NAME:-$MATRIX_HOMESERVER}..."
|
||||
echo ""
|
||||
|
||||
"$GO" run ./cmd/register \
|
||||
--homeserver "$MATRIX_HOMESERVER" \
|
||||
--username "$USERNAME" \
|
||||
--displayname "$DISPLAYNAME" \
|
||||
--env-var "$ENV_VAR"
|
||||
|
||||
echo ""
|
||||
dim " Copia las líneas de arriba a tu .env y luego corre:"
|
||||
dim " ./dev-scripts/start.sh $USERNAME"
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
# remove.sh — deshabilita un agente (enabled: false). No borra datos.
|
||||
#
|
||||
# Uso:
|
||||
# ./dev-scripts/remove.sh assistant-bot
|
||||
|
||||
source "$(dirname "$0")/_common.sh"
|
||||
|
||||
need_arg "${1:-}"
|
||||
TARGET="$1"
|
||||
|
||||
found=false
|
||||
while IFS='|' read -r id _version _enabled _desc cfg; do
|
||||
[[ "$id" != "$TARGET" ]] && continue
|
||||
found=true
|
||||
|
||||
# Detener si está corriendo
|
||||
if is_running "$id"; then
|
||||
local_pid="$(read_pid "$id")"
|
||||
info "Deteniendo $id (PID $local_pid)..."
|
||||
kill -TERM "$local_pid" 2>/dev/null || true
|
||||
sleep 1
|
||||
kill -0 "$local_pid" 2>/dev/null && kill -9 "$local_pid" 2>/dev/null || true
|
||||
rm -f "$(pid_file "$id")"
|
||||
ok "$id detenido"
|
||||
fi
|
||||
|
||||
# Marcar como disabled en el config (reemplaza solo la primera ocurrencia)
|
||||
if grep -q 'enabled: true' "$cfg"; then
|
||||
# sed compatible con Linux y macOS
|
||||
sed -i 's/enabled: true/enabled: false/' "$cfg"
|
||||
ok "$id marcado como disabled en $cfg"
|
||||
else
|
||||
warn "$id ya estaba marcado como disabled"
|
||||
fi
|
||||
|
||||
dim " Datos preservados en agents/$id/data/"
|
||||
|
||||
done < <(list_agents_raw)
|
||||
|
||||
"$found" || fail "Agente '$TARGET' no encontrado"
|
||||
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
# start.sh — inicia uno o todos los agentes habilitados en background
|
||||
#
|
||||
# Uso:
|
||||
# ./dev-scripts/start.sh # inicia todos los habilitados
|
||||
# ./dev-scripts/start.sh assistant-bot # inicia uno específico
|
||||
|
||||
source "$(dirname "$0")/_common.sh"
|
||||
load_env
|
||||
|
||||
TARGET="${1:-}"
|
||||
|
||||
start_agent() {
|
||||
local id="$1" cfg="$2"
|
||||
local log; log="$(log_file "$id")"
|
||||
local pid_f; pid_f="$(pid_file "$id")"
|
||||
|
||||
info "Iniciando $id..."
|
||||
|
||||
# Lanza el launcher en background, desacoplado del terminal
|
||||
nohup "$GO" run ./cmd/launcher -c "$cfg" \
|
||||
>> "$log" 2>&1 &
|
||||
|
||||
local pid=$!
|
||||
echo "$pid" > "$pid_f"
|
||||
|
||||
# Espera un momento y verifica que el proceso siga vivo
|
||||
sleep 1
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
ok "$id PID $pid → logs: $log"
|
||||
else
|
||||
rm -f "$pid_f"
|
||||
fail "$id arrancó pero murió — revisa: tail -f $log"
|
||||
fi
|
||||
}
|
||||
|
||||
started=0
|
||||
|
||||
while IFS='|' read -r id version enabled desc cfg; do
|
||||
# Filtrar por TARGET si se especificó uno
|
||||
[[ -n "$TARGET" && "$id" != "$TARGET" ]] && continue
|
||||
|
||||
if [[ "$enabled" != "true" ]]; then
|
||||
warn "$id (disabled en config, saltar)"
|
||||
continue
|
||||
fi
|
||||
|
||||
if is_running "$id"; then
|
||||
warn "$id (ya corriendo, PID $(read_pid "$id"))"
|
||||
continue
|
||||
fi
|
||||
|
||||
start_agent "$id" "$cfg"
|
||||
((started++)) || true
|
||||
|
||||
done < <(list_agents_raw)
|
||||
|
||||
[[ "$started" -eq 0 && -z "$TARGET" ]] && warn "Ningún agente iniciado."
|
||||
[[ -n "$TARGET" && "$started" -eq 0 ]] && fail "Agente '$TARGET' no encontrado o ya está corriendo."
|
||||
|
||||
true
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
# stop.sh — detiene uno o todos los agentes en ejecución
|
||||
#
|
||||
# Uso:
|
||||
# ./dev-scripts/stop.sh # detiene todos los que estén corriendo
|
||||
# ./dev-scripts/stop.sh assistant-bot # detiene uno específico
|
||||
|
||||
source "$(dirname "$0")/_common.sh"
|
||||
|
||||
TARGET="${1:-}"
|
||||
stopped=0
|
||||
|
||||
while IFS='|' read -r id _version _enabled _desc _cfg; do
|
||||
[[ -n "$TARGET" && "$id" != "$TARGET" ]] && continue
|
||||
|
||||
if ! is_running "$id"; then
|
||||
dim " $id (no está corriendo)"
|
||||
continue
|
||||
fi
|
||||
|
||||
local_pid="$(read_pid "$id")"
|
||||
kill -TERM "$local_pid" 2>/dev/null || true
|
||||
|
||||
# Espera hasta 5s a que muera limpiamente
|
||||
for _ in {1..10}; do
|
||||
kill -0 "$local_pid" 2>/dev/null || break
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
# SIGKILL si todavía sigue vivo
|
||||
if kill -0 "$local_pid" 2>/dev/null; then
|
||||
warn "$id no respondió a SIGTERM, enviando SIGKILL..."
|
||||
kill -9 "$local_pid" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
rm -f "$(pid_file "$id")"
|
||||
ok "$id detenido (PID $local_pid)"
|
||||
((stopped++)) || true
|
||||
|
||||
done < <(list_agents_raw)
|
||||
|
||||
[[ "$stopped" -eq 0 && -z "$TARGET" ]] && dim "Ningún agente estaba corriendo."
|
||||
[[ -n "$TARGET" && "$stopped" -eq 0 ]] && fail "Agente '$TARGET' no encontrado o no estaba corriendo."
|
||||
|
||||
true
|
||||
Reference in New Issue
Block a user