Files
unlimitedcoding/claude/uclaude_install.sh
delta-cloud-208e 696d8b4ced fix(installer): verbose diagnostics — show CLAUDE_CUSTOM_MODELS count
User reported: after install, claude /model picker shows only 5 models
(built-ins) instead of 19 — config DID land somewhere but CLAUDE_CUSTOM_
MODELS env block in settings.json was empty/missing.

Hard to debug without seeing user's settings.json. Add verbose diagnostic:

uclaude_updater.py patch_all_users():
- Pre-check: warn if config has 0 models OR placeholder api_key
- Post-patch per user: read back the JUST-WRITTEN settings.json, count
  CLAUDE_CUSTOM_MODELS entries, print:
    "Patched root: /root/.claude/settings.json (env.CLAUDE_CUSTOM_MODELS=19 models)"

uclaude_install.sh — final verification block:
- Show ANTHROPIC_BASE_URL + AUTH_TOKEN prefix + custom_models count
- Loud warning if 0 models or auth_token missing

Now user can immediately see "0 models" or "19 models" without manually
inspecting settings.json. If 0 — knows config fetch failed silently.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 18:35:47 +00:00

202 lines
7.3 KiB
Bash
Executable File

#!/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 -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}"
REPO_URL="https://x-token:${GITEA_TOKEN}@git.sensey24.ru/aibot777/unlimitedcoding.git"
INSTALL_DIR="${UCLAUDE_DIR:-$HOME/unlimitedcoding}"
echo "=== UClaude Installer ==="
echo " Install dir: $INSTALL_DIR"
# ---- Auto-install prerequisites ----
install_pkg() {
# Try apt, then yum/dnf, then brew
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 with sudo: curl -fsSL -H 'Authorization: token ${GITEA_TOKEN}' https://git.sensey24.ru/aibot777/unlimitedcoding/raw/branch/master/claude/uclaude_install.sh -o /tmp/uclaude.sh && sudo bash /tmp/uclaude.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 ----
# Retry helper for git ops — gitea sporadically returns 502 (HTTP) or
# closes RPC mid-packfile. Plain `2>/dev/null` masks stderr but the ERR
# trap still fires on non-zero exit. Run with retries + clear diagnostic
# instead of silent fail.
retry_git() {
local desc="$1"; shift
local attempt
for attempt in 1 2 3; do
if "$@" 2>&1; then
return 0
fi
echo " $desc failed (attempt $attempt/3), retrying in 5s..." >&2
sleep 5
done
echo " ERROR: $desc failed after 3 attempts" >&2
return 1
}
if [ -d "$INSTALL_DIR/.git" ]; then
echo " Already cloned, updating..."
cd "$INSTALL_DIR"
if ! retry_git "git fetch" git fetch --depth 1 origin master; then
# Fallback: nuke and re-clone fresh (gitea RPC consistently failing
# for incremental fetch happens occasionally; full re-clone is more
# robust).
echo " Fetch failed permanently — falling back to fresh clone" >&2
cd /
rm -rf "$INSTALL_DIR"
retry_git "git clone (fresh)" git clone --depth 1 --no-checkout "$REPO_URL" "$INSTALL_DIR" || exit 1
cd "$INSTALL_DIR"
git sparse-checkout init --no-cone
git sparse-checkout set '/*' 'claude/*' '!claude/releases/v*' 'claude/releases/index.json' 'codex/*'
retry_git "git checkout (after fresh clone)" git checkout || exit 1
else
retry_git "git reset --hard" git reset --hard origin/master || exit 1
fi
else
echo " Cloning (shallow, sparse — only latest version)..."
# Shallow clone without checkout — retry on transient gitea 502
retry_git "git clone" git clone --depth 1 --no-checkout "$REPO_URL" "$INSTALL_DIR" || exit 1
cd "$INSTALL_DIR"
# Enable sparse checkout: root + claude/ + codex/ (so optional codex
# install works) + index.json (first pass)
git sparse-checkout init --no-cone
git sparse-checkout set '/*' 'claude/*' '!claude/releases/v*' 'claude/releases/index.json' 'codex/*'
# Allow checkout to fail on transient errors; trap won't catch
# because we wrap in `|| true`.
git checkout 2>/dev/null || git checkout || exit 1
# Read latest version from index.json and add only that release dir
if [ -f claude/releases/index.json ]; then
VER=$(python3 -c "import json; print(json.load(open('claude/releases/index.json'))['latest'])")
echo " Latest version: v${VER}"
git sparse-checkout add "claude/releases/v${VER}"
git checkout 2>/dev/null || git checkout || true
fi
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). Tolerant of transient gitea 502 — `|| true`
# because we already have a working clone, fresh updater can run with
# slightly older code if the secondary fetch fails.
cd "$INSTALL_DIR"
git fetch --depth 1 origin master 2>/dev/null || echo " (secondary fetch failed; continuing with existing clone)" >&2
git reset --hard origin/master 2>/dev/null || git pull --quiet 2>/dev/null || true
echo " Running updater..."
# Configure npm registry for @anthropic-ai scope before running updater
if command -v npm >/dev/null 2>&1; then
echo " Configuring npm registry: https://npm.sensey24.ru/"
npm config set "@anthropic-ai:registry" "https://npm.sensey24.ru/" 2>/dev/null || true
fi
# Run updater (needs root for cli.js replacement + node install)
if [ "$(id -u)" -eq 0 ]; then
python3 claude/uclaude_updater.py --force
else
echo " Root privileges required to install cli.js."
sudo python3 claude/uclaude_updater.py --force
fi
echo ""
echo "=== Installation complete ==="
echo ""
# Sanity check what landed in settings.json (helps diagnose "only 5 models
# in /model picker" case — usually means CLAUDE_CUSTOM_MODELS missing
# from settings.json env block).
ME_HOME="${HOME:-/root}"
SETTINGS="$ME_HOME/.claude/settings.json"
if [ -f "$SETTINGS" ]; then
echo " Verification: $SETTINGS"
python3 - "$SETTINGS" <<'PYEOF'
import json, sys
try:
s = json.load(open(sys.argv[1]))
env = s.get("env", {})
ccm = env.get("CLAUDE_CUSTOM_MODELS", "")
n = len(ccm.split(",")) if ccm else 0
print(f" base_url: {env.get('ANTHROPIC_BASE_URL','-')}")
auth = env.get("ANTHROPIC_AUTH_TOKEN", "")
print(f" auth_token: {auth[:8]}{'...' if auth else ' (NOT SET — auth will fail!)'}")
print(f" custom_models: {n} models {'⚠ ZERO — /model will show built-ins only' if n==0 else 'OK'}")
if n > 0:
print(f" sample: {','.join(ccm.split(',')[:3])}...")
except Exception as e:
print(f" ERROR reading settings.json: {e}")
PYEOF
else
echo " (no $SETTINGS to verify — patcher may not have run)"
fi
echo ""
echo " Run claude: claude # interactive"
echo " Update later: cd $INSTALL_DIR && sudo bash claude/uclaude_update.sh"
echo ""
echo " To install Codex CLI separately, see README codex section:"
echo " https://git.sensey24.ru/aibot777/unlimitedcoding/raw/branch/master/codex/ucodex_install.sh"