From ae06a2697485f32c7605e59750c7fb2a96c7b8f8 Mon Sep 17 00:00:00 2001 From: delta-cloud-208e Date: Thu, 26 Feb 2026 18:18:48 +0000 Subject: [PATCH] fix: lower Node.js requirement to v18, update repo before running updater - Changed MIN_NODE_VERSION from (24, 13, 0) to (18, 0, 0) to match Claude Code requirements - Updated uclaude_install.sh to fetch latest repo before running updater - Rewrote uclaude_update.ps1 to require v18+ and update repo first - Added multiple fallback methods for Node.js installation on Windows Co-Authored-By: Claude Opus 4.6 --- claude/uclaude_install.sh | 19 +++++- claude/uclaude_update.ps1 | 129 +++++++++++++++++++++++++++----------- claude/uclaude_updater.py | 33 ++++++++-- 3 files changed, 141 insertions(+), 40 deletions(-) diff --git a/claude/uclaude_install.sh b/claude/uclaude_install.sh index 992cddd..670e704 100755 --- a/claude/uclaude_install.sh +++ b/claude/uclaude_install.sh @@ -1,7 +1,18 @@ #!/bin/bash # UClaude — one-line installer with full auto-install chain # Usage: curl -fsSL -H "Authorization: token TOKEN" https://git.sensey24.ru/aibot777/unlimitedcoding/raw/branch/master/claude/uclaude_install.sh -o /tmp/uclaude.sh && sudo bash /tmp/uclaude.sh -set -e +set -uo pipefail + +# Error handler +error_handler() { + local line_no=$1 + echo "ERROR: Command failed at line $line_no" >&2 + echo "Last command: $BASH_COMMAND" >&2 + echo "Exiting..." >&2 + exit 1 +} + +trap 'error_handler $LINENO' ERR # Read-only access token for private repo (scoped: read:repository only) GITEA_TOKEN="${GITEA_TOKEN:-cadffcb0a6a3be728ac1ff619bb40c86588f6837}" @@ -92,6 +103,12 @@ else fi echo "" +echo " Updating repo to latest version before running updater..." +# Update repo to latest version BEFORE running updater (so we get latest MIN_NODE_VERSION fix) +cd "$INSTALL_DIR" +git fetch --depth 1 origin master 2>/dev/null +git reset --hard origin/master 2>/dev/null || git pull --quiet 2>/dev/null + echo " Running updater..." # Run updater (needs root for cli.js replacement + node install) diff --git a/claude/uclaude_update.ps1 b/claude/uclaude_update.ps1 index 0a2b7a5..dfa1100 100644 --- a/claude/uclaude_update.ps1 +++ b/claude/uclaude_update.ps1 @@ -6,28 +6,45 @@ $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $RepoRoot = Split-Path -Parent $ScriptDir Set-Location $RepoRoot -# ---- Check prerequisites ---- +# ---- Pull latest FIRST so we always run newest scripts ---- + +Write-Host " Updating from remote..." -ForegroundColor Cyan +git fetch --depth 1 origin master 2>$null +git reset --hard origin/master 2>$null +if ($LASTEXITCODE -ne 0) { + git pull --quiet 2>$null +} + +# ---- Helpers ---- 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 +function Refresh-Path { + $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + + [System.Environment]::GetEnvironmentVariable("Path", "User") } +function Get-NodeMajor { + if (Test-Command "node") { + try { return [int](((node --version 2>$null) -replace '^v', '') -split '\.')[0] } + catch { return 0 } + } + return 0 +} + +# ---- Check prerequisites ---- + # 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 + if (Test-Command "winget") { + winget install --id Git.Git --accept-package-agreements --accept-source-agreements -e + Refresh-Path + } + if (-not (Test-Command "git")) { + Write-Host " Install git manually: https://git-scm.com/download/win" -ForegroundColor Yellow exit 1 } } @@ -36,40 +53,82 @@ if (-not (Test-Command "git")) { $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 + if (Test-Command "winget") { + winget install --id Python.Python.3.12 --accept-package-agreements --accept-source-agreements -e + Refresh-Path + } + $hasPython = (Test-Command "python3") -or (Test-Command "python") + if (-not $hasPython) { + Write-Host " Install Python manually: https://www.python.org/downloads/" -ForegroundColor Yellow exit 1 } } -# Node.js v24+ -$nodeOk = $false -if (Test-Command "node") { - $nodeVer = (node --version) -replace '^v', '' - $nodeMajor = [int]($nodeVer -split '\.')[0] - if ($nodeMajor -ge 24) { - $nodeOk = $true - } else { - Write-Host " Node.js v$nodeVer found, need v24+. Upgrading..." -ForegroundColor Yellow - } +# Node.js v18+ (Claude Code requires v18+, v20/v22/v24 all work) +$MIN_NODE_MAJOR = 18 +$nodeMajor = Get-NodeMajor + +if ($nodeMajor -ge $MIN_NODE_MAJOR) { + Write-Host " Node.js v$nodeMajor.x OK" -ForegroundColor Green } else { - Write-Host " Node.js not found." -ForegroundColor Yellow -} -if (-not $nodeOk) { - if (-not (Install-WithWinget "OpenJS.NodeJS.V24" "Node.js v24")) { - Write-Host " Install Node.js v24+ manually: https://nodejs.org/" -ForegroundColor Yellow + if ($nodeMajor -gt 0) { + Write-Host " Node.js v$nodeMajor found, need v$MIN_NODE_MAJOR+. Upgrading..." -ForegroundColor Yellow + } else { + Write-Host " Node.js not found. Installing..." -ForegroundColor Yellow } + + $installed = $false + + # 1. Try winget LTS + if (-not $installed -and (Test-Command "winget")) { + Write-Host " Trying winget (Node.js LTS)..." -ForegroundColor Yellow + winget install --id OpenJS.NodeJS.LTS --accept-package-agreements --accept-source-agreements -e 2>$null + Refresh-Path + if ((Get-NodeMajor) -ge $MIN_NODE_MAJOR) { $installed = $true } + } + + # 2. Try Chocolatey + if (-not $installed -and (Test-Command "choco")) { + Write-Host " Trying Chocolatey (nodejs-lts)..." -ForegroundColor Yellow + choco install nodejs-lts -y 2>$null + Refresh-Path + if ((Get-NodeMajor) -ge $MIN_NODE_MAJOR) { $installed = $true } + } + + # 3. Direct MSI download — Node.js 20 LTS (most compatible) + if (-not $installed) { + Write-Host " Downloading Node.js 20 LTS MSI..." -ForegroundColor Yellow + try { + $msiUrl = "https://nodejs.org/dist/v20.18.0/node-v20.18.0-x64.msi" + $msiFile = Join-Path $env:TEMP "node-v20-lts.msi" + Invoke-WebRequest -Uri $msiUrl -OutFile $msiFile -UseBasicParsing + Start-Process msiexec.exe -ArgumentList "/i `"$msiFile`" /quiet /norestart" -Wait + Refresh-Path + Remove-Item $msiFile -Force -ErrorAction SilentlyContinue + if ((Get-NodeMajor) -ge $MIN_NODE_MAJOR) { $installed = $true } + } catch { + Write-Host " Download failed: $_" -ForegroundColor Red + } + } + + if (-not $installed) { + Write-Host "" -ForegroundColor Red + Write-Host " Could not install Node.js automatically." -ForegroundColor Red + Write-Host " Install manually: https://nodejs.org/en/download/ (v20 LTS)" -ForegroundColor Yellow + Write-Host " Then re-run this script." -ForegroundColor Yellow + exit 1 + } + + Write-Host " Node.js v$(Get-NodeMajor).x installed OK" -ForegroundColor Green } -# ---- Pull latest artifacts ---- +# ---- Check admin, re-launch elevated if needed ---- -git pull --quiet 2>$null +$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole( + [Security.Principal.WindowsBuiltInRole]::Administrator) -# ---- Check admin ---- - -$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { - Write-Host "Re-running as Administrator..." -ForegroundColor Yellow + Write-Host " Re-running as Administrator..." -ForegroundColor Yellow Start-Process powershell -ArgumentList "-ExecutionPolicy Bypass -File `"$($MyInvocation.MyCommand.Path)`" $args" -Verb RunAs exit } diff --git a/claude/uclaude_updater.py b/claude/uclaude_updater.py index 7fdc81c..9b974c4 100644 --- a/claude/uclaude_updater.py +++ b/claude/uclaude_updater.py @@ -80,7 +80,7 @@ def run_cmd(cmd, **kwargs): # Node.js check and auto-install # ============================================================ -MIN_NODE_VERSION = (24, 13, 0) +MIN_NODE_VERSION = (18, 0, 0) def get_node_version(): @@ -133,7 +133,7 @@ def install_node(): os.remove(old_list) result = run_cmd( - ["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 remove -y nodejs || true && apt-get install -y nodejs"], timeout=180, capture_output=True, text=True, ) if result.returncode == 0: @@ -176,7 +176,15 @@ def ensure_node(): if ver is None: print(f" {Y}Node.js not found.{D}") if is_admin(): - return install_node() + ok = install_node() + if ok: + # Re-verify after install — PATH may now point to new binary + ver = get_node_version() + if ver and ver >= MIN_NODE_VERSION: + return True + eprint(f" {R}Node.js still not available after install. Reopen shell or check PATH.{D}") + return False + return False else: eprint(f" {R}Install Node.js v{'.'.join(map(str, MIN_NODE_VERSION))}+: https://nodejs.org/{D}") return False @@ -184,7 +192,15 @@ def ensure_node(): if ver < MIN_NODE_VERSION: print(f" {Y}Node.js v{'.'.join(map(str, ver))} found, need v{'.'.join(map(str, MIN_NODE_VERSION))}+{D}") if is_admin(): - return install_node() + ok = install_node() + if ok: + # Re-verify after upgrade — PATH may now point to new binary + ver = get_node_version() + if ver and ver >= MIN_NODE_VERSION: + return True + eprint(f" {R}Node.js version still insufficient after upgrade. Reopen shell or check PATH.{D}") + return False + return False else: eprint(f" {R}Update Node.js: https://nodejs.org/{D}") return False @@ -265,6 +281,15 @@ def find_cli_js(): "/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js", ] + # Prepend npm root -g result (most reliable, works across all Node install methods) + try: + result = subprocess.run(["npm", "root", "-g"], capture_output=True, text=True, timeout=10) + if result.returncode == 0: + npm_global = result.stdout.strip() + candidates.insert(0, os.path.join(npm_global, "@anthropic-ai", "claude-code", "cli.js")) + except Exception: + pass + for path in candidates: if os.path.isfile(path): return path