Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ac73aa335 | ||
|
|
32a81d7c5c | ||
|
|
510cc4151a | ||
|
|
c6e0e94a0d | ||
|
|
331d4c8b19 | ||
|
|
d68ff6fd63 | ||
|
|
ff73a2ad2c | ||
|
|
801646bb54 | ||
|
|
9cfc3a3bdd |
No files matched your search
@@ -6,7 +6,11 @@
|
|||||||
# del contenedor.
|
# del contenedor.
|
||||||
FROM alpine:3.24
|
FROM alpine:3.24
|
||||||
|
|
||||||
RUN apk add --no-cache tmux docker-cli bash curl git kubectl nodejs npm \
|
# uv (paquete directo de Alpine, no via pip): corre servidores MCP en
|
||||||
|
# Python bajo demanda (ej. `uvx workspace-mcp`, ver ADR 0012 practica
|
||||||
|
# multi-cloud) sin instalar Python a mano -- uv baja el interprete que
|
||||||
|
# necesite por su cuenta la primera vez que se usa.
|
||||||
|
RUN apk add --no-cache tmux docker-cli bash curl git kubectl nodejs npm uv \
|
||||||
&& npm install -g @anthropic-ai/claude-code \
|
&& npm install -g @anthropic-ai/claude-code \
|
||||||
&& git config --global --add safe.directory '*'
|
&& git config --global --add safe.directory '*'
|
||||||
|
|
||||||
|
|||||||
+10
-2
@@ -2,8 +2,16 @@
|
|||||||
# Proceso principal (PID 1) del contenedor tmux-host: lanza el chequeo de
|
# Proceso principal (PID 1) del contenedor tmux-host: lanza el chequeo de
|
||||||
# salud en background y mantiene el contenedor vivo, igual que el
|
# salud en background y mantiene el contenedor vivo, igual que el
|
||||||
# `tail -f /dev/null` del `docker run` inline original.
|
# `tail -f /dev/null` del `docker run` inline original.
|
||||||
set -eu
|
set -u
|
||||||
|
|
||||||
/usr/local/bin/healthcheck.sh &
|
supervise_healthcheck() {
|
||||||
|
while true; do
|
||||||
|
/usr/local/bin/healthcheck.sh
|
||||||
|
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) WARN: healthcheck.sh salio inesperadamente (exit=$?) -- relanzando en 10s" \
|
||||||
|
>> /DATA/AppData/k3d-lab-v3/tmux-host/logs/health.log 2>/dev/null || true
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
supervise_healthcheck &
|
||||||
exec tail -f /dev/null
|
exec tail -f /dev/null
|
||||||
+42
-22
@@ -8,12 +8,13 @@
|
|||||||
# del lab-agent ya esta configurado) -- nunca se intenta "arreglar"
|
# del lab-agent ya esta configurado) -- nunca se intenta "arreglar"
|
||||||
# la red del host desde aca, eso puede ser un problema de
|
# la red del host desde aca, eso puede ser un problema de
|
||||||
# hardware/BIOS fuera del alcance del contenedor.
|
# hardware/BIOS fuera del alcance del contenedor.
|
||||||
# b) Existencia de la sesion tmux "lab". Si no existe (crash, o el
|
# b) Existencia de cada sesion tmux en SESSIONS (ver mas abajo -- hoy
|
||||||
# contenedor se acaba de recrear), SI se recrea automaticamente --
|
# "lab" y "cloud-practice"). Si no existe (crash, o el contenedor
|
||||||
# es solo recuperar el propio entorno de trabajo, no tocar
|
# se acaba de recrear), SI se recrea automaticamente -- es solo
|
||||||
# infraestructura externa. Al recrearla tambien se lanza `claude`
|
# recuperar el propio entorno de trabajo, no tocar infraestructura
|
||||||
# adentro (ver launch_claude_in_session) -- una sesion recreada sin
|
# externa. Al recrearla tambien se lanza `claude` adentro (ver
|
||||||
# esto es un shell pelado, no "el entorno de trabajo".
|
# launch_claude_in_session) -- una sesion recreada sin esto es un
|
||||||
|
# shell pelado, no "el entorno de trabajo".
|
||||||
#
|
#
|
||||||
# Todo evento (chequeo ok, falla, accion tomada) queda en LOG_FILE, que
|
# Todo evento (chequeo ok, falla, accion tomada) queda en LOG_FILE, que
|
||||||
# vive bajo /DATA (persistente, sobrevive a recrear el contenedor) y no
|
# vive bajo /DATA (persistente, sobrevive a recrear el contenedor) y no
|
||||||
@@ -24,7 +25,19 @@
|
|||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
|
||||||
WORKDIR="/DATA/AppData/k3d-lab-v3/lab-cruzcloud"
|
WORKDIR="/DATA/AppData/k3d-lab-v3/lab-cruzcloud"
|
||||||
SESSION="lab"
|
# Cada entrada: "nombre-sesion:workdir". "cloud-practice" agregada
|
||||||
|
# 2026-09-20 (ver ADR 0012, practica multi-cloud) -- vive en un
|
||||||
|
# subdirectorio de WORKDIR (repo platform-multicloud ya clonado ahi),
|
||||||
|
# asi que el mismo wait_for_workdir() de abajo (que solo espera por el
|
||||||
|
# WORKDIR padre) ya cubre el caso de arranque en frio para las dos.
|
||||||
|
# "scm-portal" agregada 2026-09-28: sesion dedicada al Portal SCM (Apps
|
||||||
|
# Script) sobre el repo client-initiatives (ADR 0013), separada de "lab"
|
||||||
|
# a proposito para no mezclar contexto ni memoria con ARI Shopping.
|
||||||
|
SESSIONS=(
|
||||||
|
"lab:${WORKDIR}"
|
||||||
|
"cloud-practice:${WORKDIR}/platform-multicloud"
|
||||||
|
"scm-portal:${WORKDIR}/client-initiatives"
|
||||||
|
)
|
||||||
LOG_DIR="/DATA/AppData/k3d-lab-v3/tmux-host/logs"
|
LOG_DIR="/DATA/AppData/k3d-lab-v3/tmux-host/logs"
|
||||||
LOG_FILE="${LOG_DIR}/health.log"
|
LOG_FILE="${LOG_DIR}/health.log"
|
||||||
# Reusa el mismo canal de Telegram que ya configuro scripts/lab-agent/ --
|
# Reusa el mismo canal de Telegram que ya configuro scripts/lab-agent/ --
|
||||||
@@ -105,6 +118,7 @@ check_network() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
launch_claude_in_session() {
|
launch_claude_in_session() {
|
||||||
|
local session="$1"
|
||||||
# El pane recien creado tarda un instante en tener su shell listo para
|
# El pane recien creado tarda un instante en tener su shell listo para
|
||||||
# recibir input -- un send-keys inmediato a veces se pierde. 1s alcanza
|
# recibir input -- un send-keys inmediato a veces se pierde. 1s alcanza
|
||||||
# sobrado para un shell interactivo local (no depende de red).
|
# sobrado para un shell interactivo local (no depende de red).
|
||||||
@@ -113,47 +127,53 @@ launch_claude_in_session() {
|
|||||||
# controlable remotamente en claude.ai (confirmado en vivo 2026-09-20 --
|
# controlable remotamente en claude.ai (confirmado en vivo 2026-09-20 --
|
||||||
# sin la flag, "remoteEnabled" queda en false para siempre porque nadie
|
# sin la flag, "remoteEnabled" queda en false para siempre porque nadie
|
||||||
# la pide). El nombre de sesion remota usa el hostname del contenedor
|
# la pide). El nombre de sesion remota usa el hostname del contenedor
|
||||||
# (ZimaOS) como prefijo por defecto.
|
# (ZimaOS) como prefijo por defecto -- con 2 sesiones corriendo
|
||||||
|
# "claude --remote-control" a la vez, cada una aparece como una sesion
|
||||||
|
# remota distinta en claude.ai (mismo prefijo de host, pero cada una
|
||||||
|
# con su propio ID de sesion real).
|
||||||
# --continue (-c): retoma la conversacion mas reciente en WORKDIR en vez
|
# --continue (-c): retoma la conversacion mas reciente en WORKDIR en vez
|
||||||
# de arrancar una sesion en blanco cada vez que se recrea el pane (reinicio
|
# de arrancar una sesion en blanco cada vez que se recrea el pane (reinicio
|
||||||
# del NAS, crash del contenedor, etc). Si no hay conversacion previa en el
|
# del NAS, crash del contenedor, etc). Si no hay conversacion previa en el
|
||||||
# directorio, el propio CLI arranca una nueva sin error.
|
# directorio, el propio CLI arranca una nueva sin error.
|
||||||
if tmux send-keys -t "$SESSION" "claude --remote-control --continue" Enter 2>>"$LOG_FILE"; then
|
if tmux send-keys -t "$session" "claude --remote-control --continue" Enter 2>>"$LOG_FILE"; then
|
||||||
log "TMUX: 'claude --remote-control --continue' lanzado dentro de la sesion recien creada"
|
log "TMUX: 'claude --remote-control --continue' lanzado dentro de la sesion '${session}' recien creada"
|
||||||
else
|
else
|
||||||
log "TMUX: ERROR al lanzar 'claude --remote-control --continue' dentro de la sesion recien creada"
|
log "TMUX: ERROR al lanzar 'claude --remote-control --continue' dentro de la sesion '${session}' recien creada"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
check_tmux_session() {
|
check_tmux_session() {
|
||||||
if tmux has-session -t "$SESSION" 2>/dev/null; then
|
local session="$1" workdir="$2"
|
||||||
log "TMUX: sesion '${SESSION}' ok"
|
if tmux has-session -t "$session" 2>/dev/null; then
|
||||||
|
log "TMUX: sesion '${session}' ok"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
if [[ ! -d "$WORKDIR" ]]; then
|
if [[ ! -d "$workdir" ]]; then
|
||||||
# No se crea sobre un WORKDIR ausente (quedaria posicionada en /). Se
|
# No se crea sobre un workdir ausente (quedaria posicionada en /). Se
|
||||||
# reintenta en el proximo ciclo -- ver STARTUP_MOUNT_WAIT_MAX_SEC arriba
|
# reintenta en el proximo ciclo -- ver STARTUP_MOUNT_WAIT_MAX_SEC arriba
|
||||||
# para el caso especifico de arranque en frio del host.
|
# para el caso especifico de arranque en frio del host.
|
||||||
log "TMUX: sesion '${SESSION}' no existe y ${WORKDIR} tampoco -- no se crea esta vuelta, se reintenta en el proximo ciclo (${INTERVAL_SEC}s)."
|
log "TMUX: sesion '${session}' no existe y ${workdir} tampoco -- no se crea esta vuelta, se reintenta en el proximo ciclo (${INTERVAL_SEC}s)."
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
log "TMUX: sesion '${SESSION}' no existe -- recreando en ${WORKDIR}"
|
log "TMUX: sesion '${session}' no existe -- recreando en ${workdir}"
|
||||||
if tmux new-session -d -s "$SESSION" -c "$WORKDIR" 2>>"$LOG_FILE"; then
|
if tmux new-session -d -s "$session" -c "$workdir" 2>>"$LOG_FILE"; then
|
||||||
log "TMUX: sesion '${SESSION}' recreada exitosamente"
|
log "TMUX: sesion '${session}' recreada exitosamente"
|
||||||
# Una sesion recreada sin esto es un shell vacio hasta que alguien
|
# Una sesion recreada sin esto es un shell vacio hasta que alguien
|
||||||
# entre a mano y escriba "claude" -- exactamente el gotcha reportado
|
# entre a mano y escriba "claude" -- exactamente el gotcha reportado
|
||||||
# tras el reinicio completo del NAS del 2026-09-18/19: la sesion SI se
|
# tras el reinicio completo del NAS del 2026-09-18/19: la sesion SI se
|
||||||
# recreaba sola (confirmado en el log de ese arranque), pero Claude
|
# recreaba sola (confirmado en el log de ese arranque), pero Claude
|
||||||
# Code nunca arrancaba adentro.
|
# Code nunca arrancaba adentro.
|
||||||
launch_claude_in_session
|
launch_claude_in_session "$session"
|
||||||
else
|
else
|
||||||
log "TMUX: ERROR al recrear la sesion '${SESSION}'"
|
log "TMUX: ERROR al recrear la sesion '${session}'"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
log "=== healthcheck inicio (intervalo=${INTERVAL_SEC}s) ==="
|
log "=== healthcheck inicio (intervalo=${INTERVAL_SEC}s) ==="
|
||||||
while true; do
|
while true; do
|
||||||
check_network
|
check_network
|
||||||
check_tmux_session
|
for entry in "${SESSIONS[@]}"; do
|
||||||
|
check_tmux_session "${entry%%:*}" "${entry#*:}"
|
||||||
|
done
|
||||||
sleep "$INTERVAL_SEC"
|
sleep "$INTERVAL_SEC"
|
||||||
done
|
done
|
||||||
Reference in new issue
Block a user