mirror of
https://github.com/Alishahryar1/free-claude-code.git
synced 2026-06-02 06:13:46 +02:00
331 lines
8.2 KiB
Bash
331 lines
8.2 KiB
Bash
#!/bin/sh
|
|
set -eu
|
|
|
|
REPO_GIT_URL="git+https://github.com/Alishahryar1/free-claude-code.git"
|
|
PYTHON_VERSION="3.14.0"
|
|
MIN_UV_VERSION="0.11.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
|
|
}
|
|
|
|
current_uv_version() {
|
|
version=$(uv self version --short 2>/dev/null || true)
|
|
if [ -z "$version" ]; then
|
|
version=$(uv --version 2>/dev/null | sed 's/^uv //; s/ .*//' || true)
|
|
fi
|
|
|
|
[ -n "$version" ] || return 1
|
|
printf '%s\n' "$version"
|
|
}
|
|
|
|
version_ge() {
|
|
current=${1%%[-+]*}
|
|
minimum=${2%%[-+]*}
|
|
|
|
old_ifs=$IFS
|
|
IFS=.
|
|
set -- $current
|
|
current_major=${1:-0}
|
|
current_minor=${2:-0}
|
|
current_patch=${3:-0}
|
|
set -- $minimum
|
|
minimum_major=${1:-0}
|
|
minimum_minor=${2:-0}
|
|
minimum_patch=${3:-0}
|
|
IFS=$old_ifs
|
|
|
|
[ "$current_major" -gt "$minimum_major" ] && return 0
|
|
[ "$current_major" -lt "$minimum_major" ] && return 1
|
|
[ "$current_minor" -gt "$minimum_minor" ] && return 0
|
|
[ "$current_minor" -lt "$minimum_minor" ] && return 1
|
|
[ "$current_patch" -ge "$minimum_patch" ]
|
|
}
|
|
|
|
uv_version_satisfies_minimum() {
|
|
version=$(current_uv_version) || return 1
|
|
version_ge "$version" "$MIN_UV_VERSION"
|
|
}
|
|
|
|
validate_uv_version() {
|
|
[ "$dry_run" -eq 1 ] && return 0
|
|
|
|
version=$(current_uv_version) || fail "Unable to determine uv version."
|
|
if ! version_ge "$version" "$MIN_UV_VERSION"; then
|
|
fail "uv $MIN_UV_VERSION or newer is required; found uv $version. Upgrade uv with its installer or package manager, then rerun this installer."
|
|
fi
|
|
}
|
|
|
|
uv_self_update_supported() {
|
|
uv self update --dry-run >/dev/null 2>&1
|
|
}
|
|
|
|
uv_installed_by_homebrew() {
|
|
command -v brew >/dev/null 2>&1 && brew list --versions uv >/dev/null 2>&1
|
|
}
|
|
|
|
uv_installed_by_pipx() {
|
|
command -v pipx >/dev/null 2>&1 && pipx list 2>/dev/null | grep -Eq '(^|[[:space:]])package uv([[:space:]]|$)'
|
|
}
|
|
|
|
uv_installed_in_active_virtualenv() {
|
|
[ -n "${VIRTUAL_ENV:-}" ] || return 1
|
|
|
|
uv_path=$(command -v uv)
|
|
case "$uv_path" in
|
|
"$VIRTUAL_ENV"/*) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
update_existing_uv() {
|
|
if uv_self_update_supported; then
|
|
run uv self update
|
|
return 0
|
|
fi
|
|
|
|
if uv_installed_by_homebrew; then
|
|
run brew upgrade uv
|
|
return 0
|
|
fi
|
|
|
|
if uv_installed_by_pipx; then
|
|
run pipx upgrade uv
|
|
return 0
|
|
fi
|
|
|
|
if uv_installed_in_active_virtualenv; then
|
|
run python -m pip install --upgrade uv
|
|
return 0
|
|
fi
|
|
|
|
if uv_version_satisfies_minimum; then
|
|
printf 'uv is already installed and satisfies >=%s; skipping automatic uv update because the install source was not detected.\n' "$MIN_UV_VERSION"
|
|
return 0
|
|
fi
|
|
|
|
version=$(current_uv_version 2>/dev/null || printf 'unknown')
|
|
fail "uv $MIN_UV_VERSION or newer is required; found uv $version. The existing uv install source was not detected. Upgrade uv manually with the package manager that installed it, then rerun this installer."
|
|
}
|
|
|
|
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
|
|
update_existing_uv
|
|
validate_uv_version
|
|
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
|
|
|
|
validate_uv_version
|
|
}
|
|
|
|
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'
|