#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" WORKSPACE_ROOT="${COMBRABO_VOICE_RUNTIME_HELPER_WORKDIR:-${SCRIPT_DIR}}" MANIFEST_PATH="${SCRIPT_DIR}/Cargo.toml" BINARY_PATH="${SCRIPT_DIR}/target/release/combrabo-voice-runtime-helper" DOCKERFILE_PATH="${SCRIPT_DIR}/Dockerfile" IMAGE_NAME="${COMBRABO_VOICE_RUNTIME_HELPER_IMAGE:-combrabo-voice-runtime-helper:local}" MODE="${COMBRABO_VOICE_RUNTIME_HELPER_MODE:-auto}" usage() { cat <<'EOF' Usage: run-local.sh [--prepare] [--rebuild] Options: --prepare Prepare the current helper runtime mode without starting the helper. --rebuild Force docker image rebuild in docker mode. EOF } PREPARE_ONLY=false FORCE_REBUILD=false while [[ $# -gt 0 ]]; do case "$1" in --prepare) PREPARE_ONLY=true shift ;; --rebuild) FORCE_REBUILD=true shift ;; -h|--help) usage exit 0 ;; *) echo "Unknown argument: $1" >&2 usage >&2 exit 1 ;; esac done resolve_mode() { local requested="$1" if [[ "${requested}" != "auto" ]]; then printf '%s' "${requested}" return 0 fi if [[ "$(uname -s)" == "Darwin" ]] && command -v docker >/dev/null 2>&1; then printf '%s' "docker" return 0 fi printf '%s' "host" } prepare_host() { (cd "${SCRIPT_DIR}" && cargo build --release) } prepare_docker() { if ! command -v docker >/dev/null 2>&1; then echo "Docker is required for docker helper mode, but docker is not installed." >&2 exit 1 fi if [[ "${FORCE_REBUILD}" == "true" ]] || ! docker image inspect "${IMAGE_NAME}" >/dev/null 2>&1; then docker build -t "${IMAGE_NAME}" "${SCRIPT_DIR}" fi } rewrite_livekit_url_for_docker() { local url="$1" if [[ "${url}" =~ ^(ws|wss)://(127\.0\.0\.1|localhost)([:/].*)?$ ]]; then printf '%s://host.docker.internal%s' "${BASH_REMATCH[1]}" "${BASH_REMATCH[3]:-}" return 0 fi printf '%s' "${url}" } run_host() { if [[ ! -x "${BINARY_PATH}" ]]; then prepare_host fi exec "${BINARY_PATH}" } run_docker() { prepare_docker local livekit_url="${CV_LIVEKIT_URL:-}" local -a docker_args docker_args=( --rm --name "cv-helper-${CV_CALL_ID:-manual}" -v "${WORKSPACE_ROOT}:${WORKSPACE_ROOT}" -w "${WORKSPACE_ROOT}" -e CV_CALL_ID -e CV_TRACE_ID -e CV_GAME_MATCH_ID -e CV_LIVEKIT_URL -e CV_LIVEKIT_ROOM_ID -e CV_LIVEKIT_BOT_TOKEN -e CV_LIVEKIT_BOT_PARTICIPANT_IDENTITY -e CV_LIVEKIT_USER_PARTICIPANT_IDENTITY -e CV_FIRST_AUDIO_SOURCE -e CV_GREETING_SOURCE -e CV_GREETING_AUDIO_FILE -e CV_GREETING_AUDIO_URL -e CV_GREETING_TEXT -e CV_ROLE_ID -e CV_DEVICE_OUTPUT_SMOKE_ENABLED -e CV_DEVICE_OUTPUT_DESTINATION_IDENTITIES ) if [[ -n "${livekit_url}" ]]; then export CV_LIVEKIT_URL="$(rewrite_livekit_url_for_docker "${livekit_url}")" fi if [[ -n "${CV_GREETING_AUDIO_FILE:-}" && -f "${CV_GREETING_AUDIO_FILE}" ]]; then local audio_parent audio_parent="$(cd "$(dirname "${CV_GREETING_AUDIO_FILE}")" && pwd)" if [[ "${audio_parent}" != "${WORKSPACE_ROOT}" && "${audio_parent}" != "${WORKSPACE_ROOT}/"* ]]; then docker_args+=(-v "${audio_parent}:${audio_parent}:ro") fi fi exec docker run "${docker_args[@]}" "${IMAGE_NAME}" } RUNTIME_MODE="$(resolve_mode "${MODE}")" if [[ "${PREPARE_ONLY}" == "true" ]]; then case "${RUNTIME_MODE}" in host) prepare_host ;; docker) prepare_docker ;; *) echo "Unsupported helper mode: ${RUNTIME_MODE}" >&2 exit 1 ;; esac exit 0 fi case "${RUNTIME_MODE}" in host) run_host ;; docker) run_docker ;; *) echo "Unsupported helper mode: ${RUNTIME_MODE}" >&2 exit 1 ;; esac