#!/bin/bash # XCodeCLI Setup Launcher (macOS / Linux) # 一站式安装和配置 Claude Code, Gemini CLI, Codex set -e # 确保 PATH 包含常见的包管理器路径 export PATH="$HOME/.bun/bin:$HOME/.local/bin:$HOME/.npm-global/bin:/usr/local/bin:$PATH" export BUN_INSTALL="$HOME/.bun" # ========== 颜色定义 ========== RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' # No Color # ========== 输出函数 ========== info() { echo -e "${BLUE}[INFO]${NC} $1"; } success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; } error() { echo -e "${RED}[ERROR]${NC} $1"; } # ========== 检测 shell 配置文件 ========== get_shell_rc() { if [ -n "${ZSH_VERSION:-}" ] || [ "${SHELL##*/}" = "zsh" ]; then echo "$HOME/.zshrc" elif [ -n "${BASH_VERSION:-}" ] || [ "${SHELL##*/}" = "bash" ]; then if [ -f "$HOME/.bashrc" ]; then echo "$HOME/.bashrc" else echo "$HOME/.bash_profile" fi else error "不支持当前 shell: ${SHELL##*/}" error "仅支持 bash 和 zsh" exit 1 fi } # ========== 刷新环境变量 ========== reload_env() { local rc_file rc_file=$(get_shell_rc) # 重新加载 PATH export PATH="$HOME/.bun/bin:$HOME/.local/bin:$HOME/.npm-global/bin:/usr/local/bin:$PATH" export BUN_INSTALL="$HOME/.bun" # 从配置文件中提取并加载环境变量 if [ -f "$rc_file" ]; then # 提取 export 行并执行 while IFS= read -r line; do if [[ "$line" =~ ^export\ ([A-Za-z_][A-Za-z0-9_]*)= ]]; then eval "$line" 2>/dev/null || true fi done < "$rc_file" fi # 刷新命令缓存 hash -r 2>/dev/null || true } # ========== 工具配置(兼容 bash 3.x)========== TOOL_NAMES_1="Claude Code" TOOL_NAMES_2="Gemini CLI" TOOL_NAMES_3="Codex" TOOL_CMDS_1="claude" TOOL_CMDS_2="gemini" TOOL_CMDS_3="codex" TOOL_PKGS_1="@anthropic-ai/claude-code" TOOL_PKGS_2="@google/gemini-cli@latest" TOOL_PKGS_3="@openai/codex" TOOL_URLS_1="https://gitea.sususu.cf/sususu/xcodecli-shells/raw/branch/main/ClaudeCode/setup-claude-code.sh" TOOL_URLS_2="https://gitea.sususu.cf/sususu/xcodecli-shells/raw/branch/main/GeminiCLI/setup-gemini.sh" TOOL_URLS_3="https://gitea.sususu.cf/sususu/xcodecli-shells/raw/branch/main/codex/setup-codex.sh" # Helper functions to get tool properties get_tool_name() { eval echo \"\$TOOL_NAMES_$1\"; } get_tool_cmd() { eval echo \"\$TOOL_CMDS_$1\"; } get_tool_pkg() { eval echo \"\$TOOL_PKGS_$1\"; } get_tool_url() { eval echo \"\$TOOL_URLS_$1\"; } # ========== 检测系统包管理器 ========== get_sys_pkg_manager() { if command_exists apt-get; then echo "apt" elif command_exists yum; then echo "yum" elif command_exists dnf; then echo "dnf" elif command_exists apk; then echo "apk" elif command_exists brew; then echo "brew" elif command_exists pacman; then echo "pacman" else echo "" fi } # ========== 安装系统依赖 ========== install_sys_deps() { local deps=("$@") local pkg_mgr=$(get_sys_pkg_manager) if [[ -z "$pkg_mgr" ]]; then error "无法检测系统包管理器,请手动安装: ${deps[*]}" return 1 fi info "正在安装: ${deps[*]} ..." case "$pkg_mgr" in apt) apt-get update -qq && apt-get install -y "${deps[@]}" ;; yum) yum install -y "${deps[@]}" ;; dnf) dnf install -y "${deps[@]}" ;; apk) apk add "${deps[@]}" ;; brew) brew install "${deps[@]}" ;; pacman) pacman -S --noconfirm "${deps[@]}" ;; esac # 验证所有依赖是否安装成功 local failed=() for dep in "${deps[@]}"; do if command_exists "$dep"; then success "$dep 安装成功!" else failed+=("$dep") fi done if [[ ${#failed[@]} -gt 0 ]]; then error "以下依赖安装失败: ${failed[*]}" return 1 fi return 0 } # ========== 检测并安装系统依赖 ========== check_system_deps() { local missing_deps=() # 检测 curl if ! command_exists curl; then missing_deps+=("curl") fi # 检测 unzip (bun 安装需要) if ! command_exists unzip; then missing_deps+=("unzip") fi # 检测 jq (Claude Code 配置需要) if ! command_exists jq; then missing_deps+=("jq") fi if [[ ${#missing_deps[@]} -eq 0 ]]; then return 0 fi echo "" warning "检测到缺少以下系统依赖: ${missing_deps[*]}" echo "" read -p "是否自动安装这些依赖? (Y/n) " -n 1 -r echo "" if [[ $REPLY =~ ^[Nn]$ ]]; then error "请手动安装依赖后重新运行脚本" echo "" local pkg_mgr=$(get_sys_pkg_manager) case "$pkg_mgr" in apt) echo -e " ${CYAN}apt-get update && apt-get install -y ${missing_deps[*]}${NC}" ;; yum) echo -e " ${CYAN}yum install -y ${missing_deps[*]}${NC}" ;; dnf) echo -e " ${CYAN}dnf install -y ${missing_deps[*]}${NC}" ;; apk) echo -e " ${CYAN}apk add ${missing_deps[*]}${NC}" ;; brew) echo -e " ${CYAN}brew install ${missing_deps[*]}${NC}" ;; pacman) echo -e " ${CYAN}pacman -S ${missing_deps[*]}${NC}" ;; *) echo -e " 请使用系统包管理器安装: ${missing_deps[*]}" ;; esac echo "" return 1 fi # 一次性安装所有缺失的依赖 if ! install_sys_deps "${missing_deps[@]}"; then return 1 fi return 0 } # ========== 帮助信息 ========== show_help() { cat << 'EOF' XCodeCLI Setup Launcher (macOS / Linux) 一站式安装和配置 Claude Code, Gemini CLI, Codex Usage: ./setup.sh [OPTIONS] Options: --key 预设 API 密钥(必填) --help 显示此帮助信息 一行命令快速使用: API_KEY='YOUR_API_KEY' bash -c "$(curl -fsSL https://gitea.sususu.cf/sususu/xcodecli-shells/raw/branch/main/setup.sh)" EOF exit 0 } # ========== 命令检测 ========== command_exists() { command -v "$1" &> /dev/null } # ========== 获取包管理器 ========== get_package_manager() { if command_exists node; then local node_version=$(node --version 2>/dev/null | sed 's/v//') info "检测到 Node.js v$node_version" >&2 echo "npm" return 0 fi if command_exists bun; then local bun_version=$(bun --version 2>/dev/null) info "检测到 Bun v$bun_version" >&2 echo "bun" return 0 fi return 1 } # ========== 安装 Bun ========== install_bun() { echo "" warning "未检测到 Node.js 或 Bun" info "推荐安装 Bun(轻量级 JavaScript 运行时)" echo "" echo -e " 安装命令: ${CYAN}curl -fsSL https://bun.sh/install | bash${NC}" echo "" read -p "是否立即安装 Bun? (Y/n) " -n 1 -r echo "" if [[ $REPLY =~ ^[Nn]$ ]]; then return 1 fi info "正在安装 Bun..." if curl -fsSL https://bun.sh/install | bash; then # 刷新 PATH export BUN_INSTALL="$HOME/.bun" export PATH="$BUN_INSTALL/bin:$PATH" if command_exists bun; then success "Bun 安装成功!" return 0 else warning "Bun 可能已安装,但需要重新打开终端才能生效" info "请重新打开终端后再运行此脚本" return 1 fi else error "Bun 安装失败" return 1 fi } # ========== 安装工具 ========== install_tool() { local tool_num=$1 local pm=$2 local tool_name=$(get_tool_name "$tool_num") local tool_pkg=$(get_tool_pkg "$tool_num") info "安装 $tool_name..." local install_cmd="" if [[ "$pm" == "npm" ]]; then install_cmd="npm install -g $tool_pkg" else install_cmd="bun add -g $tool_pkg" fi echo -e " 执行: ${CYAN}$install_cmd${NC}" if eval "$install_cmd"; then hash -r 2>/dev/null || true # 如果使用 bun 安装,创建 node 符号链接(如果不存在) if [[ "$pm" == "bun" ]] && ! command_exists node; then if [[ -f "$HOME/.bun/bin/bun" ]]; then ln -sf "$HOME/.bun/bin/bun" "$HOME/.bun/bin/node" 2>/dev/null || true info "已创建 node 符号链接 (bun -> node)" fi fi success "$tool_name 安装成功!" return 0 else error "$tool_name 安装失败" return 1 fi } # ========== 配置工具 ========== configure_tool() { local tool_num=$1 local api_key=$2 local tool_name=$(get_tool_name "$tool_num") local setup_url=$(get_tool_url "$tool_num") info "配置 $tool_name..." if API_KEY="$api_key" bash -c "$(curl -fsSL "$setup_url")"; then success "$tool_name 配置完成!" return 0 else error "$tool_name 配置失败" return 1 fi } # ========== 显示状态 ========== show_status() { echo "" echo -e "${CYAN}========================================${NC}" echo -e "${CYAN} XCodeCLI Setup Launcher${NC}" echo -e "${CYAN}========================================${NC}" echo "" echo -e "获取 API 密钥: ${CYAN}https://api2.xcodecli.com/console/token${NC}" echo -e "步骤: 添加令牌 → 输入令牌名称 → 提交" echo "" echo -e "${YELLOW}工具安装状态:${NC}" echo "" for i in 1 2 3; do local tool_name=$(get_tool_name "$i") local tool_cmd=$(get_tool_cmd "$i") local status local color if command_exists "$tool_cmd"; then status="[已安装]" color="${GREEN}" else status="[未安装]" color="${RED}" fi printf " [%d] %-12s %b%s%b\n" "$i" "$tool_name" "$color" "$status" "$NC" done echo "" echo -e " [${CYAN}a${NC}] 配置全部工具" echo -e " [${CYAN}0${NC}] 退出" echo "" } # ========== 主函数 ========== main() { local api_key="${API_KEY:-}" # 解析参数 while [[ $# -gt 0 ]]; do case $1 in --key) api_key="$2" shift 2 ;; --help|-h) show_help ;; *) shift ;; esac done # 检测并安装系统依赖 (curl, unzip, jq) if ! check_system_deps; then exit 1 fi # 显示状态 show_status # 选择要配置的工具 echo -e "${YELLOW}请选择要配置的工具 (1/2/3/a/0):${NC}" read -r choice # 退出 if [[ "$choice" == "0" ]]; then info "再见!" exit 0 fi # 验证选择 local tools_to_configure="" if [[ "$choice" == "a" || "$choice" == "A" ]]; then tools_to_configure="1 2 3" elif [[ "$choice" =~ ^[1-3]$ ]]; then tools_to_configure="$choice" else error "无效的选择" exit 1 fi # 检查 API Key if [[ -z "$api_key" ]]; then echo "" echo -e "${YELLOW}请输入 API 密钥:${NC}" read -r api_key if [[ -z "$api_key" ]]; then error "API 密钥不能为空" exit 1 fi fi info "API 密钥: ${api_key:0:8}..." echo "" # 获取包管理器 local pm=$(get_package_manager) if [[ -z "$pm" ]]; then if ! install_bun; then exit 1 fi pm=$(get_package_manager) if [[ -z "$pm" ]]; then error "仍未检测到可用的包管理器" exit 1 fi fi echo "" echo -e "${CYAN}========================================${NC}" echo -e "${CYAN} 开始安装和配置工具${NC}" echo -e "${CYAN}========================================${NC}" echo "" local failed=0 local total=$(echo $tools_to_configure | wc -w | tr -d ' ') local current=0 # 安装和配置选中的工具 for i in $tools_to_configure; do current=$((current + 1)) local tool_name=$(get_tool_name "$i") local tool_cmd=$(get_tool_cmd "$i") echo "" echo -e "${YELLOW}>>> [$current/$total] $tool_name${NC}" echo "" # 检测是否已安装 if ! command_exists "$tool_cmd"; then if ! install_tool "$i" "$pm"; then failed=$((failed + 1)) continue fi else info "$tool_name 已安装,跳过安装步骤" fi # 配置工具 if ! configure_tool "$i" "$api_key"; then failed=$((failed + 1)) fi done # 显示结果 echo "" echo -e "${CYAN}========================================${NC}" echo -e "${CYAN} 配置完成${NC}" echo -e "${CYAN}========================================${NC}" echo "" # 自动刷新环境变量 info "正在刷新环境变量..." reload_env if [[ $failed -eq 0 ]]; then success "所有选中的工具配置成功!" echo "" # 验证工具是否可用 local all_ok=true for i in $tools_to_configure; do local tool_cmd=$(get_tool_cmd "$i") local tool_name=$(get_tool_name "$i") if command_exists "$tool_cmd"; then echo -e " ${GREEN}✓${NC} $tool_name ($tool_cmd) 可用" else echo -e " ${RED}✗${NC} $tool_name ($tool_cmd) 未找到" all_ok=false fi done echo "" if [[ "$all_ok" == "false" ]]; then warning "部分工具在当前会话中不可用" echo -e "${YELLOW}请执行以下命令刷新环境:${NC}" echo "" local rc_file=$(get_shell_rc) echo -e " ${CYAN}source $rc_file${NC}" echo "" echo -e "或者重新打开终端" else success "所有工具已就绪,可以直接使用!" fi else warning "有 $failed 个工具配置失败" fi # 询问是否启动新 shell 以使环境变量生效 echo "" echo -e "${YELLOW}是否启动新的 shell 会话以使环境变量立即生效? (Y/n)${NC}" read -r -n 1 start_new_shell echo "" if [[ ! "$start_new_shell" =~ ^[Nn]$ ]]; then info "正在启动新的 shell..." exec "$SHELL" -l fi } main "$@"