Sanad_Package_2/entrypoint.sh

74 lines
3.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# Sanad Package 2 (Premium Communication) entrypoint.
# 1) license gate 2) resolve P2 env (env > license > config) 3) preflight 4) launch.
set -u
PKG="P2"
CFG="/app/pkg2_config/p2_config.json"
# ── 1. license gate ──────────────────────────────────────────────────────────
# license_check exits 0 only when entitled. If NOT entitled we exit the CONTAINER
# cleanly (code 0) so a restart policy won't crash-loop.
if ! python3 -m sanad_pkg.license_check "$PKG"; then
echo "[$PKG] not licensed for this robot — container exiting cleanly."
exit 0
fi
# ── 2. resolve config (env wins, then license feature, then config file) ──────
read_cfg() { # read_cfg <key>
python3 - "$CFG" "$1" <<'PY' 2>/dev/null || true
import json, sys
try:
print(json.load(open(sys.argv[1])).get(sys.argv[2], "") or "")
except Exception:
print("")
PY
}
# Language: empty = MULTILINGUAL auto-detect (P2's headline feature). Only set a
# fixed language if the operator/license/config explicitly pins one.
if [ -z "${SANAD_LANGUAGE:-}" ]; then
SANAD_LANGUAGE="$(python3 -c 'from sanad_pkg import license as L; print(L.feature("language","") or "")' 2>/dev/null || true)"
[ -z "$SANAD_LANGUAGE" ] && SANAD_LANGUAGE="$(read_cfg language_default)"
fi
export SANAD_LANGUAGE
export SANAD_VOICE_BRAIN="${SANAD_VOICE_BRAIN:-gemini}"
[ -z "${SANAD_AUDIO_PROFILE:-}" ] && SANAD_AUDIO_PROFILE="$(read_cfg audio_profile_default)"
export SANAD_AUDIO_PROFILE="${SANAD_AUDIO_PROFILE:-builtin}"
export SANAD_DASHBOARD_HOST="${SANAD_DASHBOARD_HOST:-0.0.0.0}"
[ -z "${SANAD_DASHBOARD_PORT:-}" ] && SANAD_DASHBOARD_PORT="$(read_cfg port)"
export SANAD_DASHBOARD_PORT="${SANAD_DASHBOARD_PORT:-8012}"
export SANAD_MASK_DIR="${SANAD_MASK_DIR:-/app/mask}"
export PYTHONUNBUFFERED=1
# Jetson + Unitree SDK OpenMP load-order fix (only if the lib exists; override-able).
if [ -z "${LD_PRELOAD:-}" ] && [ -f /usr/lib/aarch64-linux-gnu/libgomp.so.1 ]; then
export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libgomp.so.1
fi
echo "[$PKG] entitled — lang=${SANAD_LANGUAGE:-<multilingual>} audio=$SANAD_AUDIO_PROFILE port=$SANAD_DASHBOARD_PORT brain=$SANAD_VOICE_BRAIN mask_dir=$SANAD_MASK_DIR"
# ── 3. preflight (clear diagnostics) ─────────────────────────────────────────
python3 - "$SANAD_AUDIO_PROFILE" "$SANAD_MASK_DIR" <<'PY' || true
import importlib.util as u, sys
profile = sys.argv[1] if len(sys.argv) > 1 else "builtin"
mask_dir = sys.argv[2] if len(sys.argv) > 2 else "/app/mask"
def has(m): return u.find_spec(m) is not None
print("[P2] preflight:")
ok = sys.version_info >= (3, 9)
print(" python : %s %s" % (".".join(map(str, sys.version_info[:3])),
"OK" if ok else "TOO OLD — google-genai needs >=3.9"))
print(" google-genai : %s" % ("OK" if has("google.genai") else "MISSING — live conversation will NOT work"))
print(" pyaudio : %s" % ("OK" if has("pyaudio") else "missing — mic/speaker capture limited"))
print(" bleak (mask) : %s" % ("OK" if has("bleak") else "MISSING — LED mask will NOT connect"))
print(" Pillow (face) : %s" % ("OK" if has("PIL") else "missing — LifelikeFace falls back to FaceAnimator"))
sys.path.insert(0, mask_dir)
print(" mask lib : %s (%s)" % ("OK" if has("mask") else "MISSING", mask_dir))
sdk = has("unitree_sdk2py")
print(" unitree SDK : %s" % ("OK" if sdk else "absent"))
if profile == "builtin" and not sdk:
print(" >> NOTE: audio profile 'builtin' (G1 chest) needs the Unitree SDK, which is")
print(" absent. Plug a USB speaker/mic and set SANAD_AUDIO_PROFILE=plugged.")
PY
exec python3 /app/app_p2.py