Files
javis_bot/docker/entrypoint.sh
javis-bot 086dd5cde7
Some checks failed
Release / semantic-release (push) Successful in 34s
tests / Unit tests (Linux, Python 3.11) (push) Failing after 5m17s
Release / build-linux (push) Failing after 7m9s
Release / build-windows (push) Has been cancelled
Release / build-macos (arm64, macos-latest) (push) Has been cancelled
Release / build-macos (x64, macos-15-intel) (push) Has been cancelled
Release / release-main (push) Has been cancelled
Release / release-develop (push) Has been cancelled
fix: accept edge as a valid tts_engine and migrate stale persisted engines
load_settings() coerced any tts_engine outside {piper, chatterbox} to piper, so
with TTS_ENGINE=edge the reply engine saw "piper" and treated the voice as
English-only in reply_language_directive() (only the OUTPUT_LANGUAGE lock kept
replies Korean). Add "edge" (and "melo") to the accepted set so the engine is
labelled multilingual correctly.

Also: a stale tts_engine in the persistent /data/jarvis-settings.json (melo/xtts
from an earlier voice, no longer built) would override the configured engine via
the entrypoint merge and leave the bot silent. Reset those to the env engine
during the merge.

Verified: load_settings() with tts_engine=edge now returns "edge"; the merge
maps melo/xtts -> edge; reply_language_directive("edge") is multilingual; 27
tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-23 03:49:53 +09:00

92 lines
4.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# Container entrypoint: render config from env, set the VNC password, ensure the
# Piper voice exists, then hand off to supervisord (which runs the desktop,
# bridge, and bot).
set -euo pipefail
# --- Defaults (override via .env / compose) ---
: "${VNC_PASSWORD:=javis123}"
: "${VNC_RESOLUTION:=1920x1080}"
: "${OLLAMA_BASE_URL:=http://ollama:11434}"
: "${OLLAMA_CHAT_MODEL:=qwen3:8b}"
# Auxiliary small-model calls (intent judge, tool router, weather place
# extraction, query decomposition). The code default is gemma4:e2b, which this
# stack does not pull, so those calls would silently fail and fall open —
# crippling tool routing and arg extraction. Reuse the (already warm) chat model
# by default so everything runs on one resident model; override if you pull a
# dedicated small model.
: "${OLLAMA_INTENT_MODEL:=${OLLAMA_CHAT_MODEL}}"
: "${OLLAMA_EMBED_MODEL:=nomic-embed-text}"
: "${WHISPER_MODEL:=small}"
: "${WHISPER_DEVICE:=cuda}"
: "${WHISPER_COMPUTE_TYPE:=float16}"
: "${JARVIS_DB_PATH:=/data/jarvis.db}"
: "${BRIDGE_HOST:=127.0.0.1}"
: "${BRIDGE_PORT:=8765}"
: "${PIPER_VOICE:=en_GB-alan-medium}"
: "${PIPER_VOICE_DIR:=/opt/piper-voices}"
: "${TTS_PIPER_MODEL_PATH:=${PIPER_VOICE_DIR}/${PIPER_VOICE}.onnx}"
# Audio: shared PulseAudio socket so Chrome (playback) and the selfbot ffmpeg
# (capture of @DEFAULT_MONITOR@) reach the same headless daemon (run-pulse.sh).
: "${XDG_RUNTIME_DIR:=/run/user/0}"
: "${PULSE_SERVER:=unix:${XDG_RUNTIME_DIR}/pulse/native}"
export VNC_RESOLUTION OLLAMA_BASE_URL OLLAMA_CHAT_MODEL OLLAMA_INTENT_MODEL OLLAMA_EMBED_MODEL \
WHISPER_MODEL WHISPER_DEVICE WHISPER_COMPUTE_TYPE JARVIS_DB_PATH \
PIPER_VOICE PIPER_VOICE_DIR TTS_PIPER_MODEL_PATH BRIDGE_HOST BRIDGE_PORT \
XDG_RUNTIME_DIR PULSE_SERVER
mkdir -p /data /app/config "$(dirname "$JARVIS_DB_PATH")"
# --- VNC password file ---
mkdir -p /root/.vnc
echo "$VNC_PASSWORD" | tigervncpasswd -f > /root/.vnc/passwd
chmod 600 /root/.vnc/passwd
# --- Render jarvis brain config from template ---
envsubst < /app/docker/jarvis-config.template.json > /app/config/jarvis.json
export JARVIS_CONFIG_PATH=/app/config/jarvis.json
# Merge persistent settings from the settings UI (on the /data volume) on top of
# the env-rendered config, so changes survive container recreate.
if [ -f /data/jarvis-settings.json ]; then
python3 - <<'PY' || true
import json, os
try:
base = json.load(open("/app/config/jarvis.json"))
ov = json.load(open("/data/jarvis-settings.json"))
if isinstance(base, dict) and isinstance(ov, dict):
base.update(ov)
# A stale persisted tts_engine from an earlier voice (melo/xtts, no
# longer built into the image) would override the configured engine and
# leave the bot silent. Reset those to the env-configured engine.
if base.get("tts_engine") in ("melo", "xtts"):
base["tts_engine"] = os.environ.get("TTS_ENGINE", "edge")
print(f"[entrypoint] reset stale tts_engine -> {base['tts_engine']}")
json.dump(base, open("/app/config/jarvis.json", "w"), ensure_ascii=False, indent=2)
print("[entrypoint] merged persistent settings overrides")
except Exception as e:
print(f"[entrypoint] settings merge skipped: {e}")
PY
fi
# --- Ensure the Piper voice exists (best effort) ---
bash /app/docker/download-piper.sh || echo "[entrypoint] piper download failed; TTS may be unavailable"
# --- Gemini OAuth login check (GEMINI_AUTH=oauth real-time search) ---
# The browser-only role never runs the reply engine / web search, so skip the
# check there. Otherwise warn (don't fail) when oauth is selected but no login
# has been seeded into the mounted ~/.gemini, since the path silently degrades
# to the DDG/Brave cascade.
if [ "${JARVIS_ROLE:-full}" != "browser" ] \
&& [ "${GEMINI_AUTH:-oauth}" = "oauth" ] \
&& [ ! -f /root/.gemini/oauth_creds.json ]; then
echo "[entrypoint] 🔑 GEMINI_AUTH=oauth but no Gemini login at /root/.gemini/oauth_creds.json"
echo "[entrypoint] Real-time search will fall back to DDG/Brave until you seed the login."
echo "[entrypoint] Seed it: copy a logged-in ~/.gemini into the host's gemini-oauth dir"
echo "[entrypoint] (default ./docker/gemini-oauth, or set GEMINI_OAUTH_DIR). See docs/DEPLOY.md."
fi
echo "[entrypoint] display=$DISPLAY ollama=$OLLAMA_BASE_URL whisper=$WHISPER_MODEL/$WHISPER_DEVICE"
exec supervisord -c /app/docker/supervisord.conf