#!/usr/bin/env bash set -euo pipefail # ======================== # 常量定义 # ======================== SCRIPT_NAME=$(basename "$0") NODE_MIN_VERSION=18 NODE_INSTALL_VERSION=22 NVM_VERSION="v0.40.3" CLAUDE_PACKAGE="@anthropic-ai/claude-code" CONFIG_DIR="$HOME/.claude" CONFIG_FILE="$CONFIG_DIR/settings.json" API_BASE_URL="https://api.z.ai/api/anthropic" API_KEY_URL="https://z.ai/manage-apikey/apikey-list" API_TIMEOUT_MS=3000000 # Detect current shell CURRENT_SHELL=$(basename "$SHELL") # ======================== # 工具函数 # ======================== log_info() { echo "🔹 $*" } log_success() { echo "✅ $*" } log_error() { echo "❌ $*" >&2 } ensure_dir_exists() { local dir="$1" if [ ! -d "$dir" ]; then mkdir -p "$dir" || { log_error "Failed to create directory: $dir" exit 1 } fi } # ======================== # 检测包管理器 # ======================== detect_package_manager() { if command -v pacman &>/dev/null; then echo "pacman" elif command -v apt-get &>/dev/null; then echo "apt" elif command -v dnf &>/dev/null; then echo "dnf" elif command -v brew &>/dev/null; then echo "brew" else echo "unknown" fi } # ======================== # Node.js 安装函数 # ======================== install_nodejs_via_package_manager() { local pkg_manager=$(detect_package_manager) log_info "Detected package manager: $pkg_manager" case "$pkg_manager" in pacman) log_info "Installing Node.js via pacman..." if ! sudo pacman -S --noconfirm nodejs npm; then log_error "Failed to install Node.js via pacman" return 1 fi ;; apt) log_info "Installing Node.js via apt..." sudo apt-get update sudo apt-get install -y nodejs npm || return 1 ;; dnf) log_info "Installing Node.js via dnf..." sudo dnf install -y nodejs npm || return 1 ;; brew) log_info "Installing Node.js via Homebrew..." brew install node || return 1 ;; *) return 1 ;; esac return 0 } install_nodejs_via_nvm() { log_info "Installing Node.js via nvm..." # 安装 nvm log_info "Installing nvm ($NVM_VERSION)..." curl -s https://raw.githubusercontent.com/nvm-sh/nvm/"$NVM_VERSION"/install.sh | bash # 加载 nvm - 兼容多种 shell export NVM_DIR="$HOME/.nvm" if [ -s "$NVM_DIR/nvm.sh" ]; then log_info "Loading nvm for bash/zsh..." \. "$NVM_DIR/nvm.sh" fi # 验证 nvm 是否加载 if ! command -v nvm &>/dev/null; then # 尝试直接通过脚本使用 nvm if [ -s "$NVM_DIR/nvm.sh" ]; then source "$NVM_DIR/nvm.sh" else log_error "nvm installation failed" exit 1 fi fi # 安装 Node.js log_info "Installing Node.js $NODE_INSTALL_VERSION via nvm..." nvm install "$NODE_INSTALL_VERSION" nvm use "$NODE_INSTALL_VERSION" nvm alias default "$NODE_INSTALL_VERSION" } install_nodejs() { local platform=$(uname -s) log_info "Installing Node.js on $platform..." # 首先尝试通过包管理器安装(更适合 Manjaro) if install_nodejs_via_package_manager; then log_success "Node.js installed via package manager" else log_info "Package manager installation failed or not available. Trying nvm..." install_nodejs_via_nvm fi # 验证安装 if ! command -v node &>/dev/null; then log_error "Node.js installation failed" exit 1 fi log_success "Node.js installed: $(node -v)" log_success "npm version: $(npm -v)" } # ======================== # Node.js 检查函数 # ======================== check_nodejs() { if command -v node &>/dev/null; then current_version=$(node -v | sed 's/v//') major_version=$(echo "$current_version" | cut -d. -f1) if [ "$major_version" -ge "$NODE_MIN_VERSION" ]; then log_success "Node.js is already installed: v$current_version" return 0 else log_info "Node.js v$current_version is installed but version < $NODE_MIN_VERSION. Upgrading..." install_nodejs fi else log_info "Node.js not found. Installing..." install_nodejs fi } # ======================== # Claude Code 安装 # ======================== install_claude_code() { if command -v claude &>/dev/null; then log_success "Claude Code is already installed: $(claude --version)" else log_info "Installing Claude Code..." npm install -g "$CLAUDE_PACKAGE" || { log_error "Failed to install claude-code" exit 1 } log_success "Claude Code installed successfully" fi } configure_claude_json() { log_info "Configuring onboarding settings..." node --eval ' const os = require("os"); const fs = require("fs"); const path = require("path"); const homeDir = os.homedir(); const filePath = path.join(homeDir, ".claude.json"); if (fs.existsSync(filePath)) { const content = JSON.parse(fs.readFileSync(filePath, "utf-8")); fs.writeFileSync(filePath, JSON.stringify({ ...content, hasCompletedOnboarding: true }, null, 2), "utf-8"); } else { fs.writeFileSync(filePath, JSON.stringify({ hasCompletedOnboarding: true }, null, 2), "utf-8"); } ' || { log_error "Failed to configure onboarding" exit 1 } } # ======================== # API Key 配置 # ======================== configure_claude() { log_info "Configuring Claude Code..." echo " You can get your API key from: $API_KEY_URL" read -s -p "🔑 Please enter your ZHIPU API key: " api_key echo if [ -z "$api_key" ]; then log_error "API key cannot be empty. Please run the script again." exit 1 fi ensure_dir_exists "$CONFIG_DIR" # 写入配置文件 node --eval ' const fs = require("fs"); const path = require("path"); const configDir = "'"$CONFIG_DIR"'"; const filePath = path.join(configDir, "settings.json"); const apiKey = "'"$api_key"'"; const content = fs.existsSync(filePath) ? JSON.parse(fs.readFileSync(filePath, "utf-8")) : {}; fs.writeFileSync(filePath, JSON.stringify({ ...content, env: { ANTHROPIC_AUTH_TOKEN: apiKey, ANTHROPIC_BASE_URL: "'"$API_BASE_URL"'", API_TIMEOUT_MS: '"$API_TIMEOUT_MS"' } }, null, 2), "utf-8"); ' || { log_error "Failed to write settings.json" exit 1 } log_success "Claude Code configured successfully" } # ======================== # Shell 配置提示 # ======================== print_shell_instructions() { echo "" log_info "Shell-specific instructions:" if [ "$CURRENT_SHELL" = "fish" ]; then echo " For Fish shell, if you used nvm, add to ~/.config/fish/config.fish:" echo " set -gx NVM_DIR ~/.nvm" echo " bass source ~/.nvm/nvm.sh" echo "" echo " Or install 'fisher' and 'nvm.fish' plugin:" echo " fisher install jorgebucaran/fisher" echo " fisher install jorgebucaran/nvm.fish" elif [ "$CURRENT_SHELL" = "bash" ]; then echo " For Bash, nvm should be automatically configured in ~/.bashrc" elif [ "$CURRENT_SHELL" = "zsh" ]; then echo " For Zsh, nvm should be automatically configured in ~/.zshrc" fi } # ======================== # 主流程 # ======================== main() { echo "🚀 Starting $SCRIPT_NAME" echo " Detected shell: $CURRENT_SHELL" echo "" check_nodejs install_claude_code configure_claude_json configure_claude echo "" log_success "🎉 Installation completed successfully!" print_shell_instructions echo "" echo "🚀 You can now start using Claude Code with:" echo " claude" echo "" echo " Note: If 'claude' command is not found, try:" echo " - Restart your terminal" echo " - Or run: hash -r (bash) / rehash (zsh) / hash -r (fish)" } main "$@"