#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "${ROOT_DIR}" case "$(uname -m)" in x86_64|amd64) BARE="${ROOT_DIR}/node_modules/bare-runtime-linux-x64/bin/bare" ;; aarch64|arm64) BARE="${ROOT_DIR}/node_modules/bare-runtime-linux-arm64/bin/bare" ;; *) BARE="" ;; esac BARE="${BARE:-$(command -v bare || true)}" [[ -x "${BARE}" ]] || { echo "Packaged Bare runtime not found; reinstall the rolling bundle." >&2; exit 1; } echo "JARVIS-QVAC first-run setup" echo "The daemon is GPU-gated and keeps QVAC behind one master owner." echo echo "[1/5] GPU and QVAC preflight" "${BARE}" packaging/bare-run.js daemon/gpu-doctor.js || true echo echo "[2/5] Microphone and PipeWire preflight" "${BARE}" packaging/bare-run.js daemon/voice-doctor.js || true echo echo "[3/5] Wake phrase and model profile" read -r -p "Wake phrase [hey jarvis]: " WAKE_PHRASE WAKE_PHRASE="${WAKE_PHRASE:-hey jarvis}" read -r -p "Model profile [laptop-8gb/laptop-16gb/desktop-gpu] (default: laptop-16gb): " MODEL_PROFILE MODEL_PROFILE="${MODEL_PROFILE:-laptop-16gb}" case "${MODEL_PROFILE}" in laptop-8gb|laptop-16gb|desktop-gpu) ;; *) echo "Unknown model profile: ${MODEL_PROFILE}" >&2; exit 2 ;; esac read -r -p "Play a TTS preview now? [Y/n]: " PREVIEW_CHOICE PLAY_PREVIEW=true [[ "${PREVIEW_CHOICE:-Y}" =~ ^[Nn]$ ]] && PLAY_PREVIEW=false # Spoken replies stay on. Answering n only skips the one-shot preview; it does # not write ttsEnabled=false into config.json. TTS_ENABLED=true mkdir -p "${HOME}/.config/jarvis" "${BARE}" packaging/write-config.js "${HOME}/.config/jarvis/config.json" "${WAKE_PHRASE}" "${MODEL_PROFILE}" "${TTS_ENABLED}" echo "Saved wake phrase: ${WAKE_PHRASE}; model profile: ${MODEL_PROFILE}; TTS: ${TTS_ENABLED}" echo # The daemon owns the session D-Bus name. Start it after writing the config and # wait for that name before sending the preview or smoke-test request. DAEMON_BUS_READY=false if command -v systemctl >/dev/null && command -v busctl >/dev/null && [[ -n "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then systemctl --user daemon-reload || true systemctl --user enable jarvisd.service || true systemctl --user restart jarvisd.service || true for _ in {1..30}; do if busctl --user status io.qvac.Jarvis >/dev/null 2>&1; then DAEMON_BUS_READY=true break fi sleep 0.5 done fi echo "[4/5] TTS preview" if [[ "${PLAY_PREVIEW}" == true && "${DAEMON_BUS_READY}" == true ]]; then busctl --user call io.qvac.Jarvis /io/qvac/Jarvis io.qvac.Jarvis.Session Say s "JARVIS local voice preview" elif [[ "${PLAY_PREVIEW}" == true ]]; then echo "Daemon did not register io.qvac.Jarvis; preview skipped." else echo "TTS preview skipped; spoken replies remain enabled." fi echo echo "[5/5] Typed smoke test" read -r -p "Type a test prompt (default: say hello): " PROMPT PROMPT="${PROMPT:-say hello}" if [[ "${DAEMON_BUS_READY}" == true ]]; then SMOKE_TIMEOUT="${JARVIS_SMOKE_TIMEOUT:-180}" if busctl --timeout="${SMOKE_TIMEOUT}" --user call io.qvac.Jarvis /io/qvac/Jarvis io.qvac.Jarvis.Session Ask s "${PROMPT}"; then echo "Typed smoke test completed." else echo "Typed smoke test did not finish within ${SMOKE_TIMEOUT}s; the daemon is still running." echo "Retry after model warmup with: busctl --user call io.qvac.Jarvis /io/qvac/Jarvis io.qvac.Jarvis.Session Ask s \"${PROMPT}\"" fi else echo "Daemon is not available on the session D-Bus; saved setup and skipped live prompt." echo "Inspect with: systemctl --user status jarvisd.service" echo "Logs: journalctl --user -u jarvisd.service -n 80 --no-pager" fi if [[ "${DAEMON_BUS_READY}" == true ]]; then echo "First-run setup complete; jarvisd is running." else echo "First-run setup saved. Start with: systemctl --user enable --now jarvisd.service" fi