#!/bin/sh set -eu REPO_GIT_URL="git+https://github.com/Alishahryar1/free-claude-code.git" PYTHON_VERSION="3.14.0" UV_INSTALL_URL="https://astral.sh/uv/install.sh" dry_run=0 voice_nim=0 voice_local=0 voice_all=0 torch_backend="" show_usage() { cat <<'USAGE' Usage: install.sh [options] Installs Claude Code if missing, installs or updates uv, Python 3.14.0, and Free Claude Code. Options: --voice-nim Install NVIDIA NIM voice transcription support. --voice-local Install local Whisper voice transcription support. --voice-all Install all voice transcription backends. --torch-backend VALUE Use a uv PyTorch backend, such as cu130. Requires local voice. --dry-run Print commands without running them. --help Show this help text. USAGE } fail() { printf 'error: %s\n' "$*" >&2 exit 1 } step() { printf '\n==> %s\n' "$1" } quote_arg() { case "$1" in *[!A-Za-z0-9_./:@%+=,-]*|"") escaped=$(printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g') printf '"%s"' "$escaped" ;; *) printf '%s' "$1" ;; esac } print_command() { printf '+' for arg in "$@"; do printf ' ' quote_arg "$arg" done printf '\n' } run() { print_command "$@" if [ "$dry_run" -eq 0 ]; then "$@" fi } run_uv_installer() { printf '+ curl -LsSf %s | sh\n' "$UV_INSTALL_URL" if [ "$dry_run" -eq 0 ]; then command -v curl >/dev/null 2>&1 || fail "curl is required to install uv." curl -LsSf "$UV_INSTALL_URL" | sh fi } add_path_entry() { [ -n "$1" ] || return 0 case ":$PATH:" in *":$1:"*) ;; *) PATH="$1:$PATH" ;; esac } add_uv_to_path() { if [ -n "${XDG_BIN_HOME:-}" ]; then add_path_entry "$XDG_BIN_HOME" fi if [ -n "${HOME:-}" ]; then add_path_entry "$HOME/.local/bin" add_path_entry "$HOME/.cargo/bin" fi export PATH } require_command() { if [ "$dry_run" -eq 0 ] && ! command -v "$1" >/dev/null 2>&1; then fail "$1 is required. Install it first, then rerun this installer." fi } install_claude_if_missing() { if command -v claude >/dev/null 2>&1; then printf 'Claude Code already found on PATH; skipping install.\n' return 0 fi require_command npm run npm install -g @anthropic-ai/claude-code } install_or_update_uv() { add_uv_to_path if command -v uv >/dev/null 2>&1; then run uv self update return 0 fi run_uv_installer add_uv_to_path if [ "$dry_run" -eq 0 ] && ! command -v uv >/dev/null 2>&1; then fail "uv was installed, but it is not available on PATH. Open a new terminal or add uv's bin directory to PATH." fi } parse_args() { while [ "$#" -gt 0 ]; do case "$1" in --voice-nim) voice_nim=1 ;; --voice-local) voice_local=1 ;; --voice-all) voice_all=1 ;; --torch-backend) shift [ "$#" -gt 0 ] || fail "--torch-backend requires a value." torch_backend="$1" [ -n "$torch_backend" ] || fail "--torch-backend requires a non-empty value." ;; --torch-backend=*) torch_backend="${1#*=}" [ -n "$torch_backend" ] || fail "--torch-backend requires a non-empty value." ;; --dry-run) dry_run=1 ;; --help|-h) show_usage exit 0 ;; *) show_usage >&2 fail "unknown option: $1" ;; esac shift done } validate_args() { include_local=$voice_local if [ "$voice_all" -eq 1 ]; then include_local=1 fi if [ -n "$torch_backend" ] && [ "$include_local" -ne 1 ]; then fail "--torch-backend requires --voice-local or --voice-all." fi } package_spec() { include_nim=$voice_nim include_local=$voice_local if [ "$voice_all" -eq 1 ]; then include_nim=1 include_local=1 fi if [ -n "$torch_backend" ] && [ "$include_local" -ne 1 ]; then fail "--torch-backend requires --voice-local or --voice-all." fi if [ "$include_nim" -eq 1 ] && [ "$include_local" -eq 1 ]; then printf 'free-claude-code[voice,voice_local] @ %s' "$REPO_GIT_URL" elif [ "$include_nim" -eq 1 ]; then printf 'free-claude-code[voice] @ %s' "$REPO_GIT_URL" elif [ "$include_local" -eq 1 ]; then printf 'free-claude-code[voice_local] @ %s' "$REPO_GIT_URL" else printf '%s' "$REPO_GIT_URL" fi } install_free_claude_code() { spec=$(package_spec) if [ -n "$torch_backend" ]; then run uv tool install --force --torch-backend "$torch_backend" "$spec" else run uv tool install --force "$spec" fi } parse_args "$@" validate_args step "Installing Claude Code if missing" install_claude_if_missing step "Installing uv if missing, updating if present" install_or_update_uv step "Installing Python $PYTHON_VERSION" run uv python install "$PYTHON_VERSION" step "Installing or updating Free Claude Code" install_free_claude_code printf '\nFree Claude Code is installed. Start the proxy with: fcc-server\n'