feat: full auto-install chain for all platforms
- Wire ensure_claude_code() into cmd_update() — auto npm install if missing - uclaude_install.sh: auto-install git, python3, curl via apt/dnf/yum/brew - uclaude_update.bat: prereq checks with winget install suggestions - uclaude_update.ps1: auto-install via winget (git, python, node) - install_node(): macOS support via brew, RHEL/Fedora via rpm.nodesource - Increased npm install timeout to 300s for slow connections
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# UClaude — one-line installer
|
# UClaude — one-line installer with full auto-install chain
|
||||||
# Usage: bash <(curl -s https://git.sensey24.ru/aibot777/unlimitedcoding/raw/branch/master/claude/uclaude_install.sh)
|
# Usage: bash <(curl -s https://git.sensey24.ru/aibot777/unlimitedcoding/raw/branch/master/claude/uclaude_install.sh)
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
@@ -9,11 +9,59 @@ INSTALL_DIR="${UCLAUDE_DIR:-$HOME/unlimitedcoding}"
|
|||||||
echo "=== UClaude Installer ==="
|
echo "=== UClaude Installer ==="
|
||||||
echo " Install dir: $INSTALL_DIR"
|
echo " Install dir: $INSTALL_DIR"
|
||||||
|
|
||||||
# Check prerequisites
|
# ---- Auto-install prerequisites ----
|
||||||
command -v git >/dev/null 2>&1 || { echo "ERROR: git not found. Install git first."; exit 1; }
|
|
||||||
command -v python3 >/dev/null 2>&1 || { echo "ERROR: python3 not found. Install Python 3 first."; exit 1; }
|
install_pkg() {
|
||||||
# Node.js check — will be auto-installed by updater if needed
|
# Try apt, then yum/dnf, then brew
|
||||||
command -v node >/dev/null 2>&1 || echo "WARNING: node not found. Updater will attempt auto-install."
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
|
apt-get update -qq && apt-get install -y -qq "$@"
|
||||||
|
elif command -v dnf >/dev/null 2>&1; then
|
||||||
|
dnf install -y -q "$@"
|
||||||
|
elif command -v yum >/dev/null 2>&1; then
|
||||||
|
yum install -y -q "$@"
|
||||||
|
elif command -v brew >/dev/null 2>&1; then
|
||||||
|
brew install "$@"
|
||||||
|
else
|
||||||
|
echo "ERROR: No package manager found. Install $* manually."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
need_sudo() {
|
||||||
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
|
echo " Root privileges required to install packages."
|
||||||
|
echo " Re-run: sudo bash <(curl -s $REPO_URL/raw/branch/master/claude/uclaude_install.sh)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Git
|
||||||
|
if ! command -v git >/dev/null 2>&1; then
|
||||||
|
echo " git not found, installing..."
|
||||||
|
need_sudo
|
||||||
|
install_pkg git
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Python 3
|
||||||
|
if ! command -v python3 >/dev/null 2>&1; then
|
||||||
|
echo " python3 not found, installing..."
|
||||||
|
need_sudo
|
||||||
|
install_pkg python3
|
||||||
|
fi
|
||||||
|
|
||||||
|
# curl (needed for nodesource)
|
||||||
|
if ! command -v curl >/dev/null 2>&1; then
|
||||||
|
echo " curl not found, installing..."
|
||||||
|
need_sudo
|
||||||
|
install_pkg curl
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Node.js — updater handles version check and auto-install
|
||||||
|
if ! command -v node >/dev/null 2>&1; then
|
||||||
|
echo " Node.js not found. Updater will auto-install."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---- Clone / Update repo ----
|
||||||
|
|
||||||
if [ -d "$INSTALL_DIR/.git" ]; then
|
if [ -d "$INSTALL_DIR/.git" ]; then
|
||||||
echo " Already cloned, updating..."
|
echo " Already cloned, updating..."
|
||||||
@@ -44,7 +92,7 @@ fi
|
|||||||
echo ""
|
echo ""
|
||||||
echo " Running updater..."
|
echo " Running updater..."
|
||||||
|
|
||||||
# Run updater (needs root for cli.js replacement)
|
# Run updater (needs root for cli.js replacement + node install)
|
||||||
if [ "$(id -u)" -eq 0 ]; then
|
if [ "$(id -u)" -eq 0 ]; then
|
||||||
python3 claude/uclaude_updater.py --force
|
python3 claude/uclaude_updater.py --force
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -6,11 +6,49 @@ setlocal enabledelayedexpansion
|
|||||||
set "SCRIPT_DIR=%~dp0"
|
set "SCRIPT_DIR=%~dp0"
|
||||||
cd /d "%SCRIPT_DIR%\.."
|
cd /d "%SCRIPT_DIR%\.."
|
||||||
|
|
||||||
REM Pull latest artifacts
|
REM ---- Check prerequisites ----
|
||||||
|
|
||||||
|
where git >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo ERROR: git not found.
|
||||||
|
echo Install: winget install Git.Git
|
||||||
|
echo or: https://git-scm.com/download/win
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
where python >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
where python3 >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo ERROR: Python not found.
|
||||||
|
echo Install: winget install Python.Python.3.12
|
||||||
|
echo or: https://www.python.org/downloads/
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
where node >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo WARNING: Node.js not found. Updater will attempt to guide you.
|
||||||
|
echo Install: winget install OpenJS.NodeJS
|
||||||
|
echo or: https://nodejs.org/
|
||||||
|
)
|
||||||
|
|
||||||
|
REM ---- Pull latest artifacts ----
|
||||||
git pull --quiet 2>nul
|
git pull --quiet 2>nul
|
||||||
|
|
||||||
REM Run updater
|
REM ---- Run updater ----
|
||||||
python claude\uclaude_updater.py %*
|
|
||||||
|
REM Try python3 first, fallback to python
|
||||||
|
where python3 >nul 2>&1
|
||||||
|
if not errorlevel 1 (
|
||||||
|
python3 claude\uclaude_updater.py %*
|
||||||
|
) else (
|
||||||
|
python claude\uclaude_updater.py %*
|
||||||
|
)
|
||||||
|
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
echo.
|
echo.
|
||||||
echo If you see permission errors, run this script as Administrator.
|
echo If you see permission errors, run this script as Administrator.
|
||||||
|
|||||||
@@ -6,10 +6,56 @@ $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|||||||
$RepoRoot = Split-Path -Parent $ScriptDir
|
$RepoRoot = Split-Path -Parent $ScriptDir
|
||||||
Set-Location $RepoRoot
|
Set-Location $RepoRoot
|
||||||
|
|
||||||
# Pull latest artifacts
|
# ---- Check prerequisites ----
|
||||||
|
|
||||||
|
function Test-Command($cmd) {
|
||||||
|
return [bool](Get-Command $cmd -ErrorAction SilentlyContinue)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Install-WithWinget($id, $name) {
|
||||||
|
if (Test-Command "winget") {
|
||||||
|
Write-Host " Installing $name via winget..." -ForegroundColor Yellow
|
||||||
|
winget install --id $id --accept-package-agreements --accept-source-agreements -e
|
||||||
|
# Refresh PATH
|
||||||
|
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Git
|
||||||
|
if (-not (Test-Command "git")) {
|
||||||
|
Write-Host " git not found." -ForegroundColor Red
|
||||||
|
if (-not (Install-WithWinget "Git.Git" "Git")) {
|
||||||
|
Write-Host " Install manually: https://git-scm.com/download/win" -ForegroundColor Yellow
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Python
|
||||||
|
$hasPython = (Test-Command "python3") -or (Test-Command "python")
|
||||||
|
if (-not $hasPython) {
|
||||||
|
Write-Host " Python not found." -ForegroundColor Red
|
||||||
|
if (-not (Install-WithWinget "Python.Python.3.12" "Python 3.12")) {
|
||||||
|
Write-Host " Install manually: https://www.python.org/downloads/" -ForegroundColor Yellow
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Node.js
|
||||||
|
if (-not (Test-Command "node")) {
|
||||||
|
Write-Host " Node.js not found." -ForegroundColor Yellow
|
||||||
|
if (-not (Install-WithWinget "OpenJS.NodeJS" "Node.js")) {
|
||||||
|
Write-Host " Install manually: https://nodejs.org/" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- Pull latest artifacts ----
|
||||||
|
|
||||||
git pull --quiet 2>$null
|
git pull --quiet 2>$null
|
||||||
|
|
||||||
# Check admin
|
# ---- Check admin ----
|
||||||
|
|
||||||
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||||
if (-not $isAdmin) {
|
if (-not $isAdmin) {
|
||||||
Write-Host "Re-running as Administrator..." -ForegroundColor Yellow
|
Write-Host "Re-running as Administrator..." -ForegroundColor Yellow
|
||||||
@@ -17,5 +63,7 @@ if (-not $isAdmin) {
|
|||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
|
||||||
# Run updater
|
# ---- Run updater ----
|
||||||
& python claude\uclaude_updater.py @args
|
|
||||||
|
$pythonCmd = if (Test-Command "python3") { "python3" } else { "python" }
|
||||||
|
& $pythonCmd claude\uclaude_updater.py @args
|
||||||
|
|||||||
@@ -92,16 +92,34 @@ def get_node_version():
|
|||||||
|
|
||||||
|
|
||||||
def install_node():
|
def install_node():
|
||||||
"""Auto-install Node.js v24+ using the official nodesource setup."""
|
"""Auto-install Node.js v24+ using the official nodesource setup (Linux) or brew (macOS)."""
|
||||||
print(f" {Y}Node.js v{'.'.join(map(str, MIN_NODE_VERSION))}+ required.{D}")
|
print(f" {Y}Node.js v{'.'.join(map(str, MIN_NODE_VERSION))}+ required.{D}")
|
||||||
|
|
||||||
if IS_WINDOWS:
|
if IS_WINDOWS:
|
||||||
print(f" {R}Please install Node.js manually: https://nodejs.org/{D}")
|
print(f" {R}Please install Node.js manually: https://nodejs.org/{D}")
|
||||||
|
print(f" Or run: winget install OpenJS.NodeJS")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if IS_MACOS:
|
||||||
|
print(f" Installing Node.js via Homebrew...")
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["brew", "install", "node"],
|
||||||
|
timeout=120, capture_output=True, text=True,
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
ver = get_node_version()
|
||||||
|
if ver and ver >= MIN_NODE_VERSION:
|
||||||
|
print(f" {G}Node.js v{'.'.join(map(str, ver))} installed{D}")
|
||||||
|
return True
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
eprint(f" {R}Install Homebrew first: https://brew.sh/ then: brew install node{D}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Linux — nodesource
|
||||||
print(f" Installing Node.js v24 via nodesource...")
|
print(f" Installing Node.js v24 via nodesource...")
|
||||||
try:
|
try:
|
||||||
# Use nodesource setup script
|
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
["bash", "-c", "curl -fsSL https://deb.nodesource.com/setup_24.x | bash - && apt-get install -y nodejs"],
|
["bash", "-c", "curl -fsSL https://deb.nodesource.com/setup_24.x | bash - && apt-get install -y nodejs"],
|
||||||
timeout=120, capture_output=True, text=True,
|
timeout=120, capture_output=True, text=True,
|
||||||
@@ -112,7 +130,20 @@ def install_node():
|
|||||||
print(f" {G}Node.js v{'.'.join(map(str, ver))} installed{D}")
|
print(f" {G}Node.js v{'.'.join(map(str, ver))} installed{D}")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Fallback: try nvm-like approach or direct download
|
# Try dnf/yum fallback for RHEL/Fedora
|
||||||
|
for pkg_mgr in ["dnf", "yum"]:
|
||||||
|
if shutil.which(pkg_mgr):
|
||||||
|
result = subprocess.run(
|
||||||
|
["bash", "-c", f"curl -fsSL https://rpm.nodesource.com/setup_24.x | bash - && {pkg_mgr} install -y nodejs"],
|
||||||
|
timeout=120, capture_output=True, text=True,
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
ver = get_node_version()
|
||||||
|
if ver:
|
||||||
|
print(f" {G}Node.js v{'.'.join(map(str, ver))} installed{D}")
|
||||||
|
return True
|
||||||
|
break
|
||||||
|
|
||||||
eprint(f" {R}Auto-install failed. Install manually: https://nodejs.org/{D}")
|
eprint(f" {R}Auto-install failed. Install manually: https://nodejs.org/{D}")
|
||||||
if result.stderr:
|
if result.stderr:
|
||||||
eprint(f" {result.stderr.strip()[:200]}")
|
eprint(f" {result.stderr.strip()[:200]}")
|
||||||
@@ -145,6 +176,43 @@ def ensure_node():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Claude Code auto-install
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
def ensure_claude_code():
|
||||||
|
"""Install Claude Code via npm if not present. Returns True if OK."""
|
||||||
|
cli_js = find_cli_js()
|
||||||
|
if cli_js:
|
||||||
|
return True
|
||||||
|
|
||||||
|
print(f" {Y}Claude Code not found. Installing via npm...{D}")
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["npm", "install", "-g", "@anthropic-ai/claude-code"],
|
||||||
|
capture_output=True, text=True, timeout=300,
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
cli_js = find_cli_js()
|
||||||
|
if cli_js:
|
||||||
|
print(f" {G}Claude Code installed{D}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
eprint(f" {R}npm install failed{D}")
|
||||||
|
if result.stderr:
|
||||||
|
eprint(f" {result.stderr.strip()[:300]}")
|
||||||
|
return False
|
||||||
|
except FileNotFoundError:
|
||||||
|
eprint(f" {R}npm not found. Install Node.js first.{D}")
|
||||||
|
return False
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
eprint(f" {R}npm install timed out{D}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
eprint(f" {R}Error: {e}{D}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Version detection
|
# Version detection
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -589,7 +657,7 @@ def cmd_check():
|
|||||||
print(f" Latest: {latest or 'unknown'}")
|
print(f" Latest: {latest or 'unknown'}")
|
||||||
|
|
||||||
if not installed:
|
if not installed:
|
||||||
print(f" {Y}Claude Code not found. Install it first: npm install -g @anthropic-ai/claude-code{D}")
|
print(f" {Y}Claude Code not found. Run without --check to auto-install.{D}")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
if not latest:
|
if not latest:
|
||||||
@@ -612,6 +680,11 @@ def cmd_update(force=False, settings_only=False):
|
|||||||
if not ensure_node():
|
if not ensure_node():
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
# Ensure Claude Code is installed (npm install if missing)
|
||||||
|
if not settings_only:
|
||||||
|
if not ensure_claude_code():
|
||||||
|
return 1
|
||||||
|
|
||||||
installed, cli_js = get_installed_version()
|
installed, cli_js = get_installed_version()
|
||||||
|
|
||||||
# Git pull to get latest artifacts
|
# Git pull to get latest artifacts
|
||||||
@@ -642,7 +715,7 @@ def cmd_update(force=False, settings_only=False):
|
|||||||
if not cli_js:
|
if not cli_js:
|
||||||
cli_js = find_cli_js()
|
cli_js = find_cli_js()
|
||||||
if not cli_js:
|
if not cli_js:
|
||||||
eprint(f" {R}Claude Code cli.js not found. Install Claude Code first.{D}")
|
eprint(f" {R}Claude Code cli.js not found even after install attempt.{D}")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
if not is_admin():
|
if not is_admin():
|
||||||
|
|||||||
Reference in New Issue
Block a user