spawn/sprite/lib/common.sh
A dfaf9370d2
fix: Improve sprite CLI detection on Termux (#76)
Check additional Termux-specific paths when detecting existing sprite
CLI installation, preventing unnecessary reinstalls.

Fixes #75

Agent: community-coordinator

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-09 03:55:46 -08:00

221 lines
8 KiB
Bash

#!/bin/bash
set -eo pipefail
# Common bash functions shared between spawn scripts
# Source shared provider-agnostic functions (local or remote fallback)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)"
if [[ -n "${SCRIPT_DIR}" && -f "${SCRIPT_DIR}/../../shared/common.sh" ]]; then
source "${SCRIPT_DIR}/../../shared/common.sh"
else
eval "$(curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/shared/common.sh)"
fi
# Configurable timeout/delay constants
SPRITE_CONNECTIVITY_POLL_DELAY=${SPRITE_CONNECTIVITY_POLL_DELAY:-5} # Delay between sprite connectivity checks
# Check if sprite CLI is installed, install if not
ensure_sprite_installed() {
# Check if sprite is already in PATH
if command -v sprite &> /dev/null; then
# sprite is already installed, check version
local installed_version
installed_version=$(sprite version 2>/dev/null | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)?' || echo "unknown")
log_info "sprite ${installed_version} already installed, skipping installation"
return 0
fi
# Check common installation paths (especially for Termux)
local common_paths=(
"${HOME}/.local/bin/sprite"
"/data/data/com.termux/files/usr/bin/sprite"
"/usr/local/bin/sprite"
"/usr/bin/sprite"
)
for sprite_path in "${common_paths[@]}"; do
if [[ -x "${sprite_path}" ]]; then
# Found sprite binary, add its directory to PATH
local sprite_dir
sprite_dir=$(dirname "${sprite_path}")
export PATH="${sprite_dir}:${PATH}"
local installed_version
installed_version=$(sprite version 2>/dev/null | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)?' || echo "unknown")
log_info "sprite ${installed_version} already installed at ${sprite_path}, skipping installation"
return 0
fi
done
# sprite not found, install it
log_warn "Installing sprite CLI..."
if ! curl -fsSL https://sprites.dev/install.sh | bash; then
log_error "Failed to install sprite CLI"
log_error ""
log_error "Possible causes:"
log_error " - Network connectivity issues"
log_error " - Installation script download failed"
log_error " - Insufficient permissions for installation"
log_error ""
log_error "Manual installation:"
log_error " Visit https://sprites.dev for installation instructions"
log_error " Or try: curl -fsSL https://sprites.dev/install.sh | bash"
return 1
fi
export PATH="${HOME}/.local/bin:${PATH}"
# Verify installation succeeded
if ! command -v sprite &> /dev/null; then
log_error "Sprite CLI installation completed but command not found in PATH"
log_error "Try adding to PATH: export PATH=\"\$HOME/.local/bin:\$PATH\""
return 1
fi
}
# Check if already authenticated with sprite
ensure_sprite_authenticated() {
if ! sprite org list &> /dev/null; then
log_warn "Logging in to sprite..."
sprite login || true
fi
}
# Prompt for sprite name
get_sprite_name() {
local sprite_name
sprite_name=$(get_resource_name "SPRITE_NAME" "Enter sprite name: ") || return 1
if ! validate_server_name "${sprite_name}"; then
return 1
fi
echo "${sprite_name}"
}
# Check if sprite exists, create if not
ensure_sprite_exists() {
local sprite_name=${1}
local sleep_time=${2:-3}
if sprite list 2>/dev/null | grep -qE "^${sprite_name}( |$)"; then
log_info "Sprite '${sprite_name}' already exists"
else
log_warn "Creating sprite '${sprite_name}'..."
sprite create -skip-console "${sprite_name}" || true
log_warn "Waiting for sprite to be ready..."
sleep "${sleep_time}"
fi
}
# Verify sprite is accessible (retry up to max_attempts)
verify_sprite_connectivity() {
local sprite_name=${1}
local max_attempts=${2:-6}
local attempt=1
log_warn "Verifying sprite connectivity..."
while [[ "${attempt}" -le "${max_attempts}" ]]; do
if sprite exec -s "${sprite_name}" -- echo "ok" >/dev/null 2>&1; then
log_info "Sprite '${sprite_name}' is ready"
return 0
fi
log_warn "Sprite not ready, retrying (${attempt}/${max_attempts})..."
sleep "${SPRITE_CONNECTIVITY_POLL_DELAY}"
((attempt++))
done
log_error "Sprite '${sprite_name}' failed to respond after ${max_attempts} attempts"
log_error ""
log_error "Troubleshooting:"
log_error " 1. Check sprite status: sprite list"
log_error " 2. View sprite logs: sprite logs ${sprite_name}"
log_error " 3. Try recreating the sprite: sprite delete ${sprite_name} && sprite create ${sprite_name}"
log_error " 4. Verify network connectivity to sprites.dev"
log_error ""
log_error "If issues persist, contact Sprite support: https://sprites.dev/support"
return 1
}
# Helper function to run commands on sprite
# SECURITY: Uses printf %q to properly escape commands to prevent injection
run_sprite() {
local sprite_name=${1}
local command=${2}
# Use printf %q for proper shell escaping to prevent command injection
local escaped_command
escaped_command=$(printf '%q' "${command}")
sprite exec -s "${sprite_name}" -- bash -c "${escaped_command}"
}
# Configure shell environment (PATH, zsh setup)
setup_shell_environment() {
local sprite_name=${1}
log_warn "Configuring shell environment..."
# Create temp file with path config
local path_temp
path_temp=$(mktemp)
trap 'rm -f "${path_temp}"' EXIT
cat > "${path_temp}" << 'EOF'
# [spawn:path]
export PATH="${HOME}/.bun/bin:/.sprite/languages/bun/bin:${PATH}"
EOF
# Upload and append to shell configs
sprite exec -s "${sprite_name}" -file "${path_temp}:/tmp/path_config" -- bash -c "cat /tmp/path_config >> ~/.zprofile && cat /tmp/path_config >> ~/.zshrc && rm /tmp/path_config"
# Switch bash to zsh
local bash_temp
bash_temp=$(mktemp)
trap 'rm -f "${path_temp}" "${bash_temp}"' EXIT
cat > "${bash_temp}" << 'EOF'
# [spawn:bash]
exec /usr/bin/zsh -l
EOF
sprite exec -s "${sprite_name}" -file "${bash_temp}:/tmp/bash_config" -- bash -c "cat /tmp/bash_config > ~/.bash_profile && cat /tmp/bash_config > ~/.bashrc && rm /tmp/bash_config"
}
# Inject environment variables into sprite's shell config
# Usage: inject_env_vars_sprite SPRITE_NAME KEY1=val1 KEY2=val2 ...
# Example: inject_env_vars_sprite "$SPRITE_NAME" \
# "OPENROUTER_API_KEY=$OPENROUTER_API_KEY" \
# "ANTHROPIC_BASE_URL=https://openrouter.ai/api"
inject_env_vars_sprite() {
local sprite_name="${1}"
shift
local env_temp
env_temp=$(mktemp)
trap 'rm -f "${env_temp}"' EXIT
chmod 600 "${env_temp}"
generate_env_config "$@" > "${env_temp}"
# Upload and append to .zshrc using sprite exec with -file flag
sprite exec -s "${sprite_name}" -file "${env_temp}:/tmp/env_config" -- bash -c "cat /tmp/env_config >> ~/.zshrc && rm /tmp/env_config"
trap - EXIT
}
# Upload file to sprite (for use with setup_claude_code_config callback)
# Usage: upload_file_sprite SPRITE_NAME LOCAL_PATH REMOTE_PATH
# Example: upload_file_sprite "$SPRITE_NAME" "/tmp/settings.json" "/root/.claude/settings.json"
# SECURITY: Uses proper quoting to prevent path injection
upload_file_sprite() {
local sprite_name="${1}"
local local_path="${2}"
local remote_path="${3}"
# Generate a unique temp path to avoid collisions
local temp_remote
temp_remote="/tmp/sprite_upload_$(basename "${remote_path}")_$$"
# Use printf %q for proper shell escaping of paths to prevent injection
local escaped_remote
escaped_remote=$(printf '%q' "${remote_path}")
local escaped_temp
escaped_temp=$(printf '%q' "${temp_remote}")
sprite exec -s "${sprite_name}" -file "${local_path}:${temp_remote}" -- bash -c "mkdir -p \$(dirname ${escaped_remote}) && mv ${escaped_temp} ${escaped_remote}"
}
# Note: Provider-agnostic functions (nc_listen, open_browser, OAuth helpers, validate_model_id) are now in shared/common.sh