Añade launchers de macOS/Linux con un click y hace runtime_control cross-platform

Equivalentes .command (doble-clic en Finder) de los .bat de Windows:
- setup_mac.command: bootstrap con un click (detecta Python 3.10+, crea .venv,
  instala requirements + Chromium de Playwright, copia .env.example -> .env).
- start/stop/restart/start_persistent_profile.command: espejo de los .bat,
  lanzan el server con nohup usando el python del .venv.
- mp_common.sh: helper compartido (raíz, venv, banners).

runtime_control.py ahora es cross-platform (IS_WINDOWS): lsof/ps/pgrep/kill en
POSIX, netstat/PowerShell/taskkill en Windows. _kill_tree_posix mata el árbol
padre+worker de uvicorn con SIGTERM.

.venv/ añadido a .gitignore. Docs actualizadas (CLAUDE.md, AGENTS.md,
PLAYWRIGHT_SESSION.md).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 15:02:47 -06:00
parent a35d26fac0
commit 2a37a4ffbf
11 changed files with 525 additions and 26 deletions
Executable
+96
View File
@@ -0,0 +1,96 @@
#!/usr/bin/env bash
# ---------------------------------------------------------------------------
# mp_common.sh — utilidades compartidas por los launchers de macOS/Linux
# (start.command, stop.command, restart.command, start_persistent_profile.command
# y setup_mac.command).
#
# No se ejecuta directamente: los demás scripts lo cargan con `source`.
# Equivale a la lógica común que en Windows vive embebida en los .bat.
# ---------------------------------------------------------------------------
# Raíz del proyecto = carpeta donde vive este archivo. Resuelve symlinks para
# que funcione aunque el .command se invoque desde otra ruta.
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do
DIR="$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
done
PROJECT_DIR="$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)"
cd "$PROJECT_DIR" || exit 1
VENV_DIR="$PROJECT_DIR/.venv"
VENV_PY="$VENV_DIR/bin/python"
# Versión mínima de Python que exige el proyecto (3.10+).
PY_MIN_MAJOR=3
PY_MIN_MINOR=10
# Colorea solo si la salida es una terminal interactiva.
if [ -t 1 ]; then
C_RESET="\033[0m"; C_BOLD="\033[1m"; C_RED="\033[31m"; C_YEL="\033[33m"; C_GRN="\033[32m"
else
C_RESET=""; C_BOLD=""; C_RED=""; C_YEL=""; C_GRN=""
fi
info() { printf "${C_BOLD}[SISTEMA]${C_RESET} %s\n" "$1"; }
ok() { printf "${C_GRN}[OK]${C_RESET} %s\n" "$1"; }
warn() { printf "${C_YEL}[ADVERTENCIA]${C_RESET} %s\n" "$1"; }
err() { printf "${C_RED}[ERROR]${C_RESET} %s\n" "$1" >&2; }
# Imprime un encabezado tipo banner como los .bat de Windows.
banner() {
echo "==================================================="
echo " $1"
echo "==================================================="
echo
}
# True si "$1 --version" reporta >= PY_MIN.
_py_is_recent_enough() {
local cmd="$1"
"$cmd" -c "import sys; sys.exit(0 if sys.version_info[:2] >= ($PY_MIN_MAJOR, $PY_MIN_MINOR) else 1)" 2>/dev/null
}
# Localiza un intérprete del sistema 3.10+ para crear el venv. Prueba nombres
# versionados explícitos (más fiables que 'python3' a secas en macOS, donde el
# 'python3' del sistema suele ser viejo). Imprime la ruta o vacío.
find_system_python() {
local candidates=(python3.13 python3.12 python3.11 python3.10 python3 python)
for c in "${candidates[@]}"; do
if command -v "$c" >/dev/null 2>&1 && _py_is_recent_enough "$c"; then
command -v "$c"
return 0
fi
done
return 1
}
# Devuelve el python del venv si existe; si no, vacío. Los launchers que
# requieren dependencias instaladas deben llamar a require_venv en su lugar.
venv_python() {
if [ -x "$VENV_PY" ]; then
echo "$VENV_PY"
return 0
fi
return 1
}
# Garantiza que el venv exista; si no, da instrucciones y aborta. Imprime la
# ruta del intérprete del venv por stdout (para capturarla con $(...)).
require_venv() {
if [ ! -x "$VENV_PY" ]; then
err "No se encontró el entorno virtual (.venv)."
err "Ejecuta primero (doble clic): setup_mac.command"
return 1
fi
echo "$VENV_PY"
}
# Pausa al final cuando el script se abrió con doble clic en Finder, para que la
# ventana de Terminal no se cierre de golpe y el usuario pueda leer el resultado.
# Si se ejecutó desde una terminal interactiva (uso avanzado), no estorba.
hold_window() {
echo
read -r -p "Presiona ENTER para cerrar esta ventana..." _ || true
}