#!/bin/bash


# macOS 自带 Bash 3.2，因此脚本不使用关联数组、mapfile 等新语法。
set -u
set -o pipefail

# 保留原始终端输出。进入 Claude Code 前会停止安装日志捕获，
# 避免把用户之后的 Claude 会话写入安装日志。
exec 3>&1
exec 4>&2


# ============================================================
# CubeAI 固定配置
# ============================================================

CUBEAI_BASE_URL="https://icooku.com"

# 首次运行只输入密钥。没有历史选择时，默认使用 Opus 5。
CUBEAI_DEFAULT_MODEL="claude-opus-5"

# Claude Code /model 允许选择的模型。
CUBEAI_AVAILABLE_MODELS=(
  "claude-opus-5"
  "claude-sonnet-5"
  "claude-fable-5-1"
  "claude-fable-5"
  "claude-opus-4-8"
  "claude-opus-4-7"
  "claude-opus-4-6"
  "claude-sonnet-4-6"
  "claude-sonnet-4-5"
  "claude-haiku-4-5"
)

PACKAGE_RELEASE_TAG="stable"
MINIMUM_CLAUDE_VERSION="2.1.257"
DEBUG_VERBOSE=0

NETWORK_CONNECT_TIMEOUT=12
METADATA_TOTAL_TIMEOUT=35
PACKAGE_DOWNLOAD_TIMEOUT=300
SCRIPT_DOWNLOAD_TIMEOUT=60
INSTALL_PROCESS_TIMEOUT=420
VERIFY_PROCESS_TIMEOUT=45

OFFICIAL_INSTALL_ROUNDS=2
OFFICIAL_INSTALLER_URL="https://claude.ai/install.sh"

PACKAGE_SCOPE="@anthropic-ai"

USER_HOME="${HOME}"
CUBEAI_DIR="${USER_HOME}/Library/Application Support/CubeAI"
CUBEAI_CONFIG_FILE="${CUBEAI_DIR}/config.json"
CUBEAI_BIN_DIR="${CUBEAI_DIR}/bin"
CUBEAI_MANAGED_CLAUDE="${CUBEAI_BIN_DIR}/claude"

LOG_DIR="${CUBEAI_DIR}/logs"
LOG_FILE=""

CLAUDE_DIR="${USER_HOME}/.claude"
CLAUDE_SETTINGS_FILE="${CLAUDE_DIR}/settings.json"
CLAUDE_LOCAL_SETTINGS_FILE="${CLAUDE_DIR}/settings.local.json"
CLAUDE_LEGACY_FILE="${USER_HOME}/.claude.json"

LOCAL_BIN_DIR="${USER_HOME}/.local/bin"
LOCK_DIR="${CUBEAI_DIR}/.installer.lock"
TEMP_DIR=""

ORIGINAL_WORKING_DIRECTORY="${PWD}"
SCRIPT_PATH="$0"
SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" 2>/dev/null && pwd -P)"

# 国内镜像优先，官方源最后兜底。
NPM_SOURCE_NAMES=(
  "NPMirror 国内镜像"
  "腾讯云国内镜像"
  "华为云国内镜像"
  "NPM 官方源"
)

NPM_SOURCE_URLS=(
  "https://registry.npmmirror.com"
  "https://mirrors.tencent.com/npm"
  "https://repo.huaweicloud.com/repository/npm"
  "https://registry.npmjs.org"
)

CONFLICTING_ENV_VARS=(
  "CLAUDE_CONFIG_DIR"
  "ANTHROPIC_DEFAULT_MODEL"
  "CLAUDE_CODE_SUBAGENT_MODEL_FORCE"
  "CLAUDE_CODE_API_KEY_HELPER_TTL_MS"
  "ANTHROPIC_API_KEY"
  "ANTHROPIC_AUTH_TOKEN"
  "ANTHROPIC_BASE_URL"
  "ANTHROPIC_CUSTOM_HEADERS"
  "CLAUDE_CODE_OAUTH_TOKEN"
  "CLAUDE_CODE_USE_BEDROCK"
  "CLAUDE_CODE_USE_VERTEX"
  "CLAUDE_CODE_USE_FOUNDRY"
  "CLAUDE_CODE_USE_ANTHROPIC_AWS"
  "ANTHROPIC_BEDROCK_BASE_URL"
  "ANTHROPIC_VERTEX_BASE_URL"
  "ANTHROPIC_VERTEX_PROJECT_ID"
  "ANTHROPIC_FOUNDRY_BASE_URL"
  "ANTHROPIC_FOUNDRY_RESOURCE"
  "ANTHROPIC_FOUNDRY_API_KEY"
  "ANTHROPIC_AWS_BASE_URL"
  "ANTHROPIC_AWS_WORKSPACE_ID"

  # DeepSeek、CC Switch 或旧脚本可能留下的模型锁定。
  "ANTHROPIC_MODEL"
  "ANTHROPIC_REASONING_MODEL"
  "ANTHROPIC_SMALL_FAST_MODEL"
  "ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION"

  "ANTHROPIC_DEFAULT_OPUS_MODEL"
  "ANTHROPIC_DEFAULT_OPUS_MODEL_NAME"
  "ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION"
  "ANTHROPIC_DEFAULT_OPUS_MODEL_SUPPORTED_CAPABILITIES"

  "ANTHROPIC_DEFAULT_FABLE_MODEL"
  "ANTHROPIC_DEFAULT_FABLE_MODEL_NAME"
  "ANTHROPIC_DEFAULT_FABLE_MODEL_DESCRIPTION"
  "ANTHROPIC_DEFAULT_FABLE_MODEL_SUPPORTED_CAPABILITIES"

  "ANTHROPIC_DEFAULT_SONNET_MODEL"
  "ANTHROPIC_DEFAULT_SONNET_MODEL_NAME"
  "ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION"
  "ANTHROPIC_DEFAULT_SONNET_MODEL_SUPPORTED_CAPABILITIES"

  "ANTHROPIC_DEFAULT_HAIKU_MODEL"
  "ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME"
  "ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION"
  "ANTHROPIC_DEFAULT_HAIKU_MODEL_SUPPORTED_CAPABILITIES"

  "ANTHROPIC_CUSTOM_MODEL_OPTION"
  "ANTHROPIC_CUSTOM_MODEL_OPTION_NAME"
  "ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION"
  "ANTHROPIC_CUSTOM_MODEL_OPTION_SUPPORTED_CAPABILITIES"

  "CLAUDE_CODE_SUBAGENT_MODEL"
  "CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY"
)


DETECTED_ARCH=""
PLATFORM_PACKAGE_NAME=""
LOCK_ACQUIRED=0

PACKAGE_VERSION=""
PACKAGE_TARBALL=""
PACKAGE_INTEGRITY=""
PACKAGE_SHASUM=""

LAST_COMMAND_STDOUT=""
LAST_COMMAND_STDERR=""
LAST_COMMAND_RETURN_CODE=0
LAST_COMMAND_TIMED_OUT=0

UI_PROGRESS_PID=""
UI_PROGRESS_STARTED=0
STALLED_PROGRESS_TIMEOUT=900


# ============================================================
# 基础辅助函数
# ============================================================

timestamp_now() {
  /bin/date "+%H:%M:%S"
}

date_stamp_now() {
  /bin/date "+%Y%m%d-%H%M%S"
}

log() {
  level="$1"
  shift
  message="$*"

  if [ -n "${LOG_FILE:-}" ]; then
    printf '[%s] [%s] %s\n' \
      "$(timestamp_now)" \
      "$level" \
      "$message" >> "$LOG_FILE" 2>/dev/null || true
  fi
}

log_info() {
  log "INFO" "$*"
}

log_ok() {
  log "OK" "$*"
}

log_warn() {
  log "WARN" "$*"
}

log_error() {
  log "ERROR" "$*"
}

show_step() {
  number="$1"
  title="$2"
  log "STEP" "步骤 ${number}：${title}"
}

print_banner() {
  :
}

ui_clear() {
  /usr/bin/clear >&3 2>/dev/null || true
}

ui_draw_progress() {
  percent="$1"
  status="${2:-}"

  if [ "$percent" -lt 0 ]; then
    percent=0
  elif [ "$percent" -gt 100 ]; then
    percent=100
  fi

  width=36
  filled=$((percent * width / 100))
  empty=$((width - filled))
  bar=""
  index=0

  while [ "$index" -lt "$filled" ]; do
    bar="${bar}█"
    index=$((index + 1))
  done

  index=0
  while [ "$index" -lt "$empty" ]; do
    bar="${bar}░"
    index=$((index + 1))
  done

  if [ -n "$status" ]; then
    printf '\r[%s] %3d%%  %s' "$bar" "$percent" "$status" >&3
  else
    printf '\r[%s] %3d%%' "$bar" "$percent" >&3
  fi
}

progress_worker() {
  percent=3
  stalled_seconds=0

  while true; do
    ui_draw_progress "$percent"

    if [ "$percent" -lt 70 ]; then
      percent=$((percent + 1))
      /bin/sleep 0.4
    elif [ "$percent" -lt 85 ]; then
      percent=$((percent + 1))
      /bin/sleep 1.5
    elif [ "$percent" -lt 92 ]; then
      percent=$((percent + 1))
      /bin/sleep 4
    else
      if [ "$stalled_seconds" -ge "$STALLED_PROGRESS_TIMEOUT" ]; then
        ui_draw_progress "$percent" "15 分钟未完成，请复制下方日志"
        printf '\n\n===== 原始运行日志（可截图或复制用于调试）=====\n' >&3
        /bin/cat "$LOG_FILE" >&3 2>/dev/null || true
        printf '===== 原始运行日志结束 =====\n' >&3
        return 0
      fi

      /bin/sleep 2
      stalled_seconds=$((stalled_seconds + 2))
    fi
  done
}

start_progress() {
  if [ "$UI_PROGRESS_STARTED" -eq 1 ]; then
    return 0
  fi

  ui_clear
  UI_PROGRESS_STARTED=1
  progress_worker &
  UI_PROGRESS_PID=$!
}

stop_progress_worker() {
  if [ -n "${UI_PROGRESS_PID:-}" ]; then
    /bin/kill "$UI_PROGRESS_PID" 2>/dev/null || true
    wait "$UI_PROGRESS_PID" 2>/dev/null || true
    UI_PROGRESS_PID=""
  fi
}

finish_progress() {
  stop_progress_worker
  ui_draw_progress 100
  printf '\n' >&3
}

fail_progress() {
  stop_progress_worker
  ui_draw_progress 0 "请稍后重试"
  printf '\n' >&3
}

format_bytes() {
  value="$1"

  if [ "$value" -lt 1024 ]; then
    printf '%sB' "$value"
  elif [ "$value" -lt 1048576 ]; then
    /usr/bin/awk -v n="$value" 'BEGIN { printf "%.1fKB", n / 1024 }'
  elif [ "$value" -lt 1073741824 ]; then
    /usr/bin/awk -v n="$value" 'BEGIN { printf "%.1fMB", n / 1048576 }'
  else
    /usr/bin/awk -v n="$value" 'BEGIN { printf "%.1fGB", n / 1073741824 }'
  fi
}

file_size() {
  target="$1"

  if /usr/bin/stat -f "%z" "$target" >/dev/null 2>&1; then
    /usr/bin/stat -f "%z" "$target"
    return 0
  fi

  if /usr/bin/stat -c "%s" "$target" >/dev/null 2>&1; then
    /usr/bin/stat -c "%s" "$target"
    return 0
  fi

  /usr/bin/wc -c < "$target" | /usr/bin/tr -d ' '
}

command_exists() {
  command -v "$1" >/dev/null 2>&1
}

safe_remove() {
  target="$1"

  if [ -e "$target" ] || [ -L "$target" ]; then
    /bin/rm -rf "$target" 2>/dev/null || true
  fi
}

pause_on_failure() {
  :
}

cleanup() {
  stop_progress_worker

  if [ -n "${TEMP_DIR:-}" ] && [ -d "${TEMP_DIR:-}" ]; then
    /bin/rm -rf "$TEMP_DIR" 2>/dev/null || true
  fi

  if [ "${LOCK_ACQUIRED:-0}" -eq 1 ] && [ -d "${LOCK_DIR:-}" ]; then
    /bin/rm -rf "$LOCK_DIR" 2>/dev/null || true
    LOCK_ACQUIRED=0
  fi
}

fail_and_exit() {
  message="$1"
  log_error "$message"
  cleanup
  fail_progress
  /bin/sleep 5
  exit 1
}

trap 'cleanup' EXIT
trap 'log_warn "检测到中断信号，正在清理临时文件。"; cleanup; exit 130' INT TERM HUP


# ============================================================
# 初始化日志与运行锁
# ============================================================

init_directories_and_logging() {
  /bin/mkdir -p "$CUBEAI_DIR" "$LOG_DIR" "$CUBEAI_BIN_DIR" \
    >/dev/null 2>&1 || {
      LOG_FILE="/tmp/cubeai-mac-$(date_stamp_now).log"
      /usr/bin/touch "$LOG_FILE" 2>/dev/null || true
      fail_and_exit "无法初始化 Claude Code 目录。"
    }

  /bin/chmod 700 "$CUBEAI_DIR" "$CUBEAI_BIN_DIR" 2>/dev/null || true

  LOG_FILE="${LOG_DIR}/cubeai-mac-$(date_stamp_now).log"
  /usr/bin/touch "$LOG_FILE" 2>/dev/null || {
    LOG_FILE="/tmp/cubeai-mac-$(date_stamp_now).log"
    /usr/bin/touch "$LOG_FILE" 2>/dev/null || true
  }
  /bin/chmod 600 "$LOG_FILE" 2>/dev/null || true

  # 从这里开始，所有安装输出只进入内部日志。
  # 启动 Claude Code 前恢复原始终端输出。
  exec >>"$LOG_FILE" 2>&1

  log_info "Claude Code 最终版开始运行"
  log_info "日志文件：$LOG_FILE"
}

acquire_lock() {
  if /bin/mkdir "$LOCK_DIR" 2>/dev/null; then
    LOCK_ACQUIRED=1
    printf '%s\n' "$$" > "${LOCK_DIR}/pid"
    log_info "已取得单实例运行锁：$LOCK_DIR"
    return 0
  fi

  stale_pid=""
  if [ -f "${LOCK_DIR}/pid" ]; then
    stale_pid="$(/bin/cat "${LOCK_DIR}/pid" 2>/dev/null || true)"
  fi

  if [ -n "$stale_pid" ] && /bin/kill -0 "$stale_pid" 2>/dev/null; then
    fail_and_exit "检测到另一个 Claude Code 安装或启动进程仍在运行。"
  fi

  log_warn "发现上次异常退出留下的运行锁，正在自动清理。"
  /bin/rm -rf "$LOCK_DIR" 2>/dev/null || \
    fail_and_exit "无法清理旧运行锁：$LOCK_DIR"

  if /bin/mkdir "$LOCK_DIR" 2>/dev/null; then
    LOCK_ACQUIRED=1
    printf '%s\n' "$$" > "${LOCK_DIR}/pid"
    log_ok "旧运行锁已清理，新的运行锁创建成功。"
    return 0
  fi

  fail_and_exit "无法创建 Claude Code 运行锁。"
}

create_temp_directory() {
  TEMP_DIR="$(/usr/bin/mktemp -d "${TMPDIR:-/tmp}/CubeAI-Mac.XXXXXX")" || \
    fail_and_exit "无法创建临时目录。"

  /bin/chmod 700 "$TEMP_DIR" 2>/dev/null || true
  log_info "临时目录：$TEMP_DIR"
}


# ============================================================
# 系统检测
# ============================================================

version_at_least_macos_10_15() {
  version="$1"
  major="$(printf '%s' "$version" | /usr/bin/awk -F. '{print $1 + 0}')"
  minor="$(printf '%s' "$version" | /usr/bin/awk -F. '{print $2 + 0}')"

  if [ "$major" -gt 10 ]; then
    return 0
  fi

  if [ "$major" -eq 10 ] && [ "$minor" -ge 15 ]; then
    return 0
  fi

  return 1
}

detect_architecture() {
  machine="$(/usr/bin/uname -m 2>/dev/null || true)"
  translated="0"

  if [ -x /usr/sbin/sysctl ]; then
    translated="$(/usr/sbin/sysctl -in sysctl.proc_translated 2>/dev/null || printf '0')"
  fi

  log_info "uname -m：${machine:-未知}"
  log_info "Rosetta 转译状态：${translated:-0}"

  if [ "$translated" = "1" ]; then
    DETECTED_ARCH="arm64"
  elif [ "$machine" = "arm64" ] || [ "$machine" = "aarch64" ]; then
    DETECTED_ARCH="arm64"
  elif [ "$machine" = "x86_64" ] || [ "$machine" = "amd64" ]; then
    DETECTED_ARCH="x64"
  else
    fail_and_exit "不支持的 Mac CPU 架构：${machine:-未知}"
  fi

  PLATFORM_PACKAGE_NAME="${PACKAGE_SCOPE}/claude-code-darwin-${DETECTED_ARCH}"
  log_ok "识别出的安装架构：$DETECTED_ARCH"
  log_info "对应 NPM 原生二进制包：$PLATFORM_PACKAGE_NAME"
}

log_system_information() {
  macos_version="$(/usr/bin/sw_vers -productVersion 2>/dev/null || printf '未知')"
  macos_build="$(/usr/bin/sw_vers -buildVersion 2>/dev/null || printf '未知')"
  shell_name="${SHELL:-未知}"

  log_info "系统名称：$(/usr/bin/sw_vers -productName 2>/dev/null || printf 'macOS')"
  log_info "系统版本：$macos_version"
  log_info "系统构建号：$macos_build"
  log_info "当前 Shell：$shell_name"
  log_info "Bash 版本：${BASH_VERSION:-未知}"
  log_info "当前用户：$(/usr/bin/id -un 2>/dev/null || printf '未知')"
  log_info "当前用户目录：$USER_HOME"
  log_info "脚本目录：$SCRIPT_DIR"
  log_info "启动时工作目录：$ORIGINAL_WORKING_DIRECTORY"
  log_info "PATH：${PATH:-}"

  if ! version_at_least_macos_10_15 "$macos_version"; then
    fail_and_exit "当前系统版本过低；Claude Code 需要 macOS 10.15 或更高版本。"
  fi

  if [ "$(id -u)" -eq 0 ]; then
    fail_and_exit "请不要使用 sudo 或 root 运行 Claude Code；直接双击脚本即可。"
  fi

  detect_architecture
}


# ============================================================
# JSON 读取与写入
# ============================================================

json_get() {
  json_file="$1"
  key_path="$2"

  if [ ! -f "$json_file" ]; then
    return 1
  fi

  if [ -x /usr/bin/plutil ]; then
    value="$(/usr/bin/plutil -extract "$key_path" raw -o - "$json_file" 2>/dev/null)" && {
      printf '%s' "$value"
      return 0
    }
  fi

  if [ -x /usr/bin/osascript ]; then
    /usr/bin/osascript -l JavaScript \
      -e '
        function run(argv) {
          ObjC.import("Foundation");
          var path = argv[0];
          var keyPath = argv[1];
          var text = ObjC.unwrap(
            $.NSString.stringWithContentsOfFileEncodingError(
              path,
              $.NSUTF8StringEncoding,
              null
            )
          );
          var value = JSON.parse(text);
          var parts = keyPath.split(".");
          for (var i = 0; i < parts.length; i++) {
            if (value === null || value === undefined) {
              throw new Error("missing key");
            }
            value = value[parts[i]];
          }
          if (value === null || value === undefined) {
            throw new Error("missing key");
          }
          if (typeof value === "object") {
            return JSON.stringify(value);
          }
          return String(value);
        }
      ' "$json_file" "$key_path" 2>/dev/null
    return $?
  fi

  return 1
}

normalize_base_url() {
  value="$1"
  printf '%s' "$value" | /usr/bin/sed -E 's/[[:space:]]+$//; s#/*$##'
}

load_cubeai_config() {
  LOADED_BASE_URL=""
  LOADED_TOKEN=""

  log_info "检查 CubeAI 配置文件：$CUBEAI_CONFIG_FILE"

  if [ ! -f "$CUBEAI_CONFIG_FILE" ]; then
    log_info "未发现 CubeAI 自有配置文件。"
    return 1
  fi

  base_url="$(json_get "$CUBEAI_CONFIG_FILE" "base_url" 2>/dev/null || true)"
  token="$(json_get "$CUBEAI_CONFIG_FILE" "token" 2>/dev/null || true)"

  base_url="$(normalize_base_url "$base_url")"
  token="$(printf '%s' "$token" | /usr/bin/sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//')"

  if [ "$base_url" != "$CUBEAI_BASE_URL" ]; then
    log_warn "配置中的网关地址不是 Claude Code：${base_url:-空}"
    return 1
  fi

  if [ -z "$token" ]; then
    log_warn "Claude Code 配置中的密钥为空。"
    return 1
  fi

  LOADED_BASE_URL="$base_url"
  LOADED_TOKEN="$token"

  log_ok "Claude Code 自有配置检查通过；密钥内容不会显示或写入日志。"
  return 0
}

save_cubeai_config() {
  token="$1"
  temporary_file="${CUBEAI_CONFIG_FILE}.tmp.$$"

  log_info "正在保存 Claude Code 自有配置。"

  /bin/mkdir -p "$CUBEAI_DIR" || return 1

  CUBEAI_CONFIG_PATH="$temporary_file" \
  CUBEAI_BASE_VALUE="$CUBEAI_BASE_URL" \
  CUBEAI_TOKEN_VALUE="$token" \
  /usr/bin/osascript -l JavaScript <<'JXA'
ObjC.import("Foundation");

function envValue(name) {
  var value = $.NSProcessInfo.processInfo.environment.objectForKey(name);
  if (value === null || value === undefined) {
    throw new Error("missing environment value: " + name);
  }
  return ObjC.unwrap(value);
}

var outputPath = envValue("CUBEAI_CONFIG_PATH");
var payload = {
  product: "CubeAI",
  base_url: envValue("CUBEAI_BASE_VALUE"),
  token: envValue("CUBEAI_TOKEN_VALUE")
};

var text = JSON.stringify(payload, null, 2) + "\n";
var ok = $(text).writeToFileAtomicallyEncodingError(
  outputPath,
  true,
  $.NSUTF8StringEncoding,
  null
);

if (!ok) {
  throw new Error("failed to write CubeAI config");
}
JXA

  result=$?
  if [ "$result" -ne 0 ] || [ ! -s "$temporary_file" ]; then
    safe_remove "$temporary_file"
    return 1
  fi

  /bin/chmod 600 "$temporary_file" 2>/dev/null || true
  /bin/mv -f "$temporary_file" "$CUBEAI_CONFIG_FILE" || {
    safe_remove "$temporary_file"
    return 1
  }

  /bin/chmod 600 "$CUBEAI_CONFIG_FILE" 2>/dev/null || true
  log_ok "CubeAI 配置已保存：$CUBEAI_CONFIG_FILE"
  return 0
}

persist_cubeai_runtime_config() {
  token="$1"
  temporary_file="${CLAUDE_SETTINGS_FILE}.tmp.$$"
  backup_suffix="$(date_stamp_now)"
  invalid_backup="${CLAUDE_SETTINGS_FILE}.invalid-${backup_suffix}.bak"

  log_info "正在写入 Claude Code 用户配置。"

  /bin/mkdir -p "$CLAUDE_DIR" || return 1
  /bin/chmod 700 "$CLAUDE_DIR" 2>/dev/null || true

  CLAUDE_SETTINGS_PATH="$CLAUDE_SETTINGS_FILE" \
  CLAUDE_SETTINGS_TEMP_PATH="$temporary_file" \
  CLAUDE_SETTINGS_BACKUP_PATH="$invalid_backup" \
  CLAUDE_LOCAL_SETTINGS_PATH="$CLAUDE_LOCAL_SETTINGS_FILE" \
  CLAUDE_LEGACY_PATH="$CLAUDE_LEGACY_FILE" \
  PROJECT_SETTINGS_PATH="${ORIGINAL_WORKING_DIRECTORY}/.claude/settings.json" \
  PROJECT_LOCAL_SETTINGS_PATH="${ORIGINAL_WORKING_DIRECTORY}/.claude/settings.local.json" \
  CUBEAI_BASE_VALUE="$CUBEAI_BASE_URL" \
  CUBEAI_TOKEN_VALUE="$token" \
  CUBEAI_DEFAULT_MODEL_VALUE="$CUBEAI_DEFAULT_MODEL" \
  CUBEAI_MODELS_VALUE="$(printf '%s\n' "${CUBEAI_AVAILABLE_MODELS[@]}")" \
  CUBEAI_CLAUDE_DIR_VALUE="$CLAUDE_DIR" \
  /usr/bin/osascript -l JavaScript <<'JXA'
ObjC.import("Foundation");

function envValue(name) {
  var value = $.NSProcessInfo.processInfo.environment.objectForKey(name);
  if (value === null || value === undefined) {
    throw new Error("missing environment value: " + name);
  }
  return String(ObjC.unwrap(value));
}

function fileExists(path) {
  return $.NSFileManager.defaultManager.fileExistsAtPath(path);
}

function readText(path) {
  if (!fileExists(path)) {
    return null;
  }

  var object = $.NSString.stringWithContentsOfFileEncodingError(
    path,
    $.NSUTF8StringEncoding,
    null
  );
  var value = ObjC.unwrap(object);

  if (value === null || value === undefined) {
    return null;
  }

  return String(value);
}

function writeText(path, text) {
  return $(text).writeToFileAtomicallyEncodingError(
    path,
    true,
    $.NSUTF8StringEncoding,
    null
  );
}

function readObject(path) {
  var text = readText(path);

  if (typeof text !== "string" || text.trim() === "") {
    return null;
  }

  try {
    var value = JSON.parse(text);

    if (
      value === null ||
      Array.isArray(value) ||
      typeof value !== "object"
    ) {
      return null;
    }

    return value;
  } catch (error) {
    return null;
  }
}

var conflicting = [
  "CLAUDE_CONFIG_DIR",
  "ANTHROPIC_DEFAULT_MODEL",
  "CLAUDE_CODE_SUBAGENT_MODEL_FORCE",
  "CLAUDE_CODE_API_KEY_HELPER_TTL_MS",
  "ANTHROPIC_API_KEY",
  "ANTHROPIC_AUTH_TOKEN",
  "ANTHROPIC_BASE_URL",
  "ANTHROPIC_CUSTOM_HEADERS",
  "CLAUDE_CODE_OAUTH_TOKEN",
  "CLAUDE_CODE_USE_BEDROCK",
  "CLAUDE_CODE_USE_VERTEX",
  "CLAUDE_CODE_USE_FOUNDRY",
  "CLAUDE_CODE_USE_ANTHROPIC_AWS",
  "ANTHROPIC_BEDROCK_BASE_URL",
  "ANTHROPIC_VERTEX_BASE_URL",
  "ANTHROPIC_VERTEX_PROJECT_ID",
  "ANTHROPIC_FOUNDRY_BASE_URL",
  "ANTHROPIC_FOUNDRY_RESOURCE",
  "ANTHROPIC_FOUNDRY_API_KEY",
  "ANTHROPIC_AWS_BASE_URL",
  "ANTHROPIC_AWS_WORKSPACE_ID",
  "ANTHROPIC_MODEL",
  "ANTHROPIC_REASONING_MODEL",
  "ANTHROPIC_SMALL_FAST_MODEL",
  "ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION",
  "ANTHROPIC_DEFAULT_OPUS_MODEL",
  "ANTHROPIC_DEFAULT_OPUS_MODEL_NAME",
  "ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION",
  "ANTHROPIC_DEFAULT_OPUS_MODEL_SUPPORTED_CAPABILITIES",
  "ANTHROPIC_DEFAULT_FABLE_MODEL",
  "ANTHROPIC_DEFAULT_FABLE_MODEL_NAME",
  "ANTHROPIC_DEFAULT_FABLE_MODEL_DESCRIPTION",
  "ANTHROPIC_DEFAULT_FABLE_MODEL_SUPPORTED_CAPABILITIES",
  "ANTHROPIC_DEFAULT_SONNET_MODEL",
  "ANTHROPIC_DEFAULT_SONNET_MODEL_NAME",
  "ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION",
  "ANTHROPIC_DEFAULT_SONNET_MODEL_SUPPORTED_CAPABILITIES",
  "ANTHROPIC_DEFAULT_HAIKU_MODEL",
  "ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME",
  "ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION",
  "ANTHROPIC_DEFAULT_HAIKU_MODEL_SUPPORTED_CAPABILITIES",
  "ANTHROPIC_CUSTOM_MODEL_OPTION",
  "ANTHROPIC_CUSTOM_MODEL_OPTION_NAME",
  "ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION",
  "ANTHROPIC_CUSTOM_MODEL_OPTION_SUPPORTED_CAPABILITIES",
  "CLAUDE_CODE_SUBAGENT_MODEL",
  "CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY"
];

var routingKeys = [
  "apiKeyHelper",
  "modelOverrides",
  "availableModels",
  "teammateDefaultModel",
  "fallbackModel"
];

var allowedModels = envValue("CUBEAI_MODELS_VALUE").split("\n");

function cleanRoutingFile(path) {
  // 双击脚本时工作目录可能就是 HOME，不能把用户默认模型当作项目配置删除。
  if (path === envValue("CLAUDE_SETTINGS_PATH")) {
    return;
  }
  var settings = readObject(path);

  if (settings === null) {
    return;
  }

  var changed = false;

  if (Object.prototype.hasOwnProperty.call(settings, "model")) {
    delete settings.model;
    changed = true;
  }

  for (var i = 0; i < routingKeys.length; i++) {
    if (Object.prototype.hasOwnProperty.call(settings, routingKeys[i])) {
      delete settings[routingKeys[i]];
      changed = true;
    }
  }

  if (
    settings.env !== null &&
    !Array.isArray(settings.env) &&
    typeof settings.env === "object"
  ) {
    for (var j = 0; j < conflicting.length; j++) {
      if (
        Object.prototype.hasOwnProperty.call(
          settings.env,
          conflicting[j]
        )
      ) {
        delete settings.env[conflicting[j]];
        changed = true;
      }
    }

    if (Object.keys(settings.env).length === 0) {
      delete settings.env;
    }
  }

  if (changed && !writeText(path, JSON.stringify(settings, null, 2) + "\n")) {
    throw new Error("failed to clean routing settings: " + path);
  }
}

// local/project 设置的优先级高于用户 settings.json。
// 这里只清除路由字段，不触碰 MCP、权限、插件或 Hooks。
cleanRoutingFile(envValue("CLAUDE_LOCAL_SETTINGS_PATH"));
cleanRoutingFile(envValue("CLAUDE_LEGACY_PATH"));
cleanRoutingFile(envValue("PROJECT_SETTINGS_PATH"));
cleanRoutingFile(envValue("PROJECT_LOCAL_SETTINGS_PATH"));

var settingsPath = envValue("CLAUDE_SETTINGS_PATH");
var temporaryPath = envValue("CLAUDE_SETTINGS_TEMP_PATH");
var invalidBackupPath = envValue("CLAUDE_SETTINGS_BACKUP_PATH");
var settings = {};
var originalText = readText(settingsPath);

if (typeof originalText === "string" && originalText.trim() !== "") {
  try {
    settings = JSON.parse(originalText);

    if (
      settings === null ||
      Array.isArray(settings) ||
      typeof settings !== "object"
    ) {
      settings = {};
    }
  } catch (error) {
    writeText(invalidBackupPath, originalText);
    settings = {};
  }
}

var selectedModel = settings.model;
var aliases = ["fable", "best", "opus", "sonnet", "haiku", "opusplan"];
if (typeof selectedModel === "string") {
  selectedModel = selectedModel.trim().toLowerCase();
}
var baseModel = typeof selectedModel === "string"
  ? selectedModel.replace(/\[1m\]$/, "") : "";

if (
  typeof selectedModel !== "string" ||
  (allowedModels.indexOf(baseModel) === -1 && aliases.indexOf(baseModel) === -1)
) {
  selectedModel = envValue("CUBEAI_DEFAULT_MODEL_VALUE");
}

for (var r = 0; r < routingKeys.length; r++) {
  delete settings[routingKeys[r]];
}

if (
  settings.env === null ||
  Array.isArray(settings.env) ||
  typeof settings.env !== "object"
) {
  settings.env = {};
}

for (var k = 0; k < conflicting.length; k++) {
  delete settings.env[conflicting[k]];
}

var flagshipCapabilities = (
  "effort,xhigh_effort,max_effort," +
  "thinking,adaptive_thinking,interleaved_thinking"
);

settings.env.ANTHROPIC_BASE_URL = envValue("CUBEAI_BASE_VALUE");
settings.env.ANTHROPIC_AUTH_TOKEN = envValue("CUBEAI_TOKEN_VALUE");
settings.env.CLAUDE_CONFIG_DIR = envValue("CUBEAI_CLAUDE_DIR_VALUE");
settings.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = "1";
settings.env.API_TIMEOUT_MS = "600000";

settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL = "claude-opus-5";
settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL_NAME = "Opus 5";
settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION =
  "Claude flagship model";
settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL_SUPPORTED_CAPABILITIES =
  flagshipCapabilities;

settings.env.ANTHROPIC_DEFAULT_FABLE_MODEL = "claude-fable-5-1";
settings.env.ANTHROPIC_DEFAULT_FABLE_MODEL_NAME = "Fable 5.1";
settings.env.ANTHROPIC_DEFAULT_FABLE_MODEL_DESCRIPTION =
  "Claude flagship model";
settings.env.ANTHROPIC_DEFAULT_FABLE_MODEL_SUPPORTED_CAPABILITIES =
  flagshipCapabilities;

settings.env.ANTHROPIC_CUSTOM_MODEL_OPTION = "claude-fable-5";
settings.env.ANTHROPIC_CUSTOM_MODEL_OPTION_NAME = "Fable 5";
settings.env.ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION = "Powerful reasoning";
settings.env.ANTHROPIC_CUSTOM_MODEL_OPTION_SUPPORTED_CAPABILITIES = flagshipCapabilities;

settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL = "claude-sonnet-5";
settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL_NAME = "Sonnet 5";
settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION =
  "Claude balanced model";
settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL_SUPPORTED_CAPABILITIES =
  flagshipCapabilities;

settings.env.ANTHROPIC_DEFAULT_HAIKU_MODEL = "claude-haiku-4-5";
settings.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME = "Haiku 4.5";
settings.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION =
  "Claude fast model";
settings.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_SUPPORTED_CAPABILITIES =
  "thinking,adaptive_thinking";

settings.env.CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY = "0";

// 不写 ANTHROPIC_MODEL，避免锁死 /model 选择。
delete settings.env.ANTHROPIC_MODEL;

settings.model = selectedModel;
settings.availableModels = allowedModels;

var output = JSON.stringify(settings, null, 2) + "\n";

if (!writeText(temporaryPath, output)) {
  throw new Error("failed to write Claude settings");
}
JXA

  result=$?

  if [ "$result" -ne 0 ] || [ ! -s "$temporary_file" ]; then
    safe_remove "$temporary_file"
    return 1
  fi

  /bin/chmod 600 "$temporary_file" 2>/dev/null || true
  /bin/mv -f "$temporary_file" "$CLAUDE_SETTINGS_FILE" || {
    safe_remove "$temporary_file"
    return 1
  }

  /bin/chmod 600 "$CLAUDE_SETTINGS_FILE" 2>/dev/null || true
  if [ -f "$invalid_backup" ]; then
    /bin/chmod 600 "$invalid_backup" 2>/dev/null || true
  fi

  log_ok "Claude Code 配置已写入：$CLAUDE_SETTINGS_FILE"
  return 0
}


# ============================================================
# 带超时的命令执行
# ============================================================

terminate_process_tree() {
  process_id="$1"

  if command_exists pkill; then
    /usr/bin/pkill -TERM -P "$process_id" 2>/dev/null || true
  fi

  /bin/kill -TERM "$process_id" 2>/dev/null || true
  /bin/sleep 2

  if command_exists pkill; then
    /usr/bin/pkill -KILL -P "$process_id" 2>/dev/null || true
  fi

  /bin/kill -KILL "$process_id" 2>/dev/null || true
}

run_command_with_timeout() {
  label="$1"
  timeout_seconds="$2"
  shift 2

  stdout_file="${TEMP_DIR}/command-stdout-$$.txt"
  stderr_file="${TEMP_DIR}/command-stderr-$$.txt"
  safe_remove "$stdout_file"
  safe_remove "$stderr_file"

  log_info "执行命令：$(printf '%q ' "$@")"

  "$@" </dev/null >"$stdout_file" 2>"$stderr_file" &
  process_id=$!
  started="$(/bin/date +%s)"
  timed_out=0

  while /bin/kill -0 "$process_id" 2>/dev/null; do
    now="$(/bin/date +%s)"
    elapsed=$((now - started))

    printf '\r%s：已等待 %s 秒' "$label" "$elapsed"

    if [ "$elapsed" -ge "$timeout_seconds" ]; then
      timed_out=1
      terminate_process_tree "$process_id"
      break
    fi

    /bin/sleep 1
  done

  wait "$process_id" 2>/dev/null
  return_code=$?
  printf '\n'

  LAST_COMMAND_STDOUT="$(/bin/cat "$stdout_file" 2>/dev/null || true)"
  LAST_COMMAND_STDERR="$(/bin/cat "$stderr_file" 2>/dev/null || true)"
  LAST_COMMAND_TIMED_OUT="$timed_out"

  if [ "$timed_out" -eq 1 ]; then
    LAST_COMMAND_RETURN_CODE=124
  else
    LAST_COMMAND_RETURN_CODE="$return_code"
  fi

  if [ -n "$LAST_COMMAND_STDOUT" ]; then
    log_info "命令标准输出："
    printf '%s\n' "$LAST_COMMAND_STDOUT"
  fi

  if [ -n "$LAST_COMMAND_STDERR" ]; then
    log_warn "命令错误输出："
    printf '%s\n' "$LAST_COMMAND_STDERR"
  fi

  if [ "$timed_out" -eq 1 ]; then
    log_warn "${label}超过 ${timeout_seconds} 秒，已自动终止。"
    return 124
  fi

  return "$return_code"
}


# ============================================================
# Claude Code 定位与验证
# ============================================================

add_candidate_if_executable() {
  candidate="$1"

  if [ -n "$candidate" ] && [ -x "$candidate" ]; then
    printf '%s\n' "$candidate"
  fi
}

candidate_claude_paths() {
  add_candidate_if_executable "$CUBEAI_MANAGED_CLAUDE"
  add_candidate_if_executable "${USER_HOME}/.local/bin/claude"
  add_candidate_if_executable "${USER_HOME}/.claude/bin/claude"
  add_candidate_if_executable "/opt/homebrew/bin/claude"
  add_candidate_if_executable "/usr/local/bin/claude"
  add_candidate_if_executable "${USER_HOME}/.volta/bin/claude"
  add_candidate_if_executable "${USER_HOME}/.npm-global/bin/claude"

  for candidate in "${USER_HOME}"/.nvm/versions/node/*/bin/claude; do
    if [ -x "$candidate" ]; then
      printf '%s\n' "$candidate"
    fi
  done

  located="$(command -v claude 2>/dev/null || true)"
  if [ -n "$located" ] && [ -x "$located" ]; then
    printf '%s\n' "$located"
  fi
}

supported_claude_version() {
  # 使用 macOS 自带 awk，兼容 Bash 3.2；只接受正式版版本号。
  printf '%s\n' "$1" | /usr/bin/awk -v minimum="$MINIMUM_CLAUDE_VERSION" '
    BEGIN { split(minimum, required, "."); valid = 0 }
    /^[0-9]+\.[0-9]+\.[0-9]+( \(Claude Code\))?$/ {
      split($1, actual, ".")
      for (i = 1; i <= 3; i++) {
        if (actual[i] + 0 > required[i] + 0) { valid = 1; exit }
        if (actual[i] + 0 < required[i] + 0) { exit }
      }
      valid = 1
    }
    END { exit !valid }
  '
}

verify_claude_path() {
  candidate="$1"
  detailed="${2:-1}"

  if [ ! -x "$candidate" ]; then
    [ "$detailed" -eq 1 ] && log_warn "候选文件不可执行：$candidate"
    return 1
  fi

  log_info "准备验证 Claude Code：$candidate"

  if run_command_with_timeout \
    "验证 Claude Code 版本" \
    "$VERIFY_PROCESS_TIMEOUT" \
    "$candidate" "--version"; then

    output="$(printf '%s\n%s' "$LAST_COMMAND_STDOUT" "$LAST_COMMAND_STDERR" | \
      /usr/bin/sed '/^[[:space:]]*$/d' | /usr/bin/head -n 1)"

    if supported_claude_version "$output"; then
      log_ok "Claude Code 验证通过：$output"
      VERIFIED_CLAUDE_PATH="$candidate"
      return 0
    fi
  fi

  [ "$detailed" -eq 1 ] && \
    log_warn "Claude Code 验证失败：${candidate}；需要 ${MINIMUM_CLAUDE_VERSION}+；返回码=${LAST_COMMAND_RETURN_CODE}"

  return 1
}

locate_and_verify_claude() {
  VERIFIED_CLAUDE_PATH=""
  seen_file="${TEMP_DIR}/candidate-seen.txt"
  : > "$seen_file"

  while IFS= read -r candidate; do
    [ -z "$candidate" ] && continue

    canonical="$candidate"
    if [ -L "$candidate" ]; then
      resolved="$(/usr/bin/readlink "$candidate" 2>/dev/null || true)"
      if [ -n "$resolved" ]; then
        case "$resolved" in
          /*) canonical="$resolved" ;;
          *) canonical="$(cd "$(dirname "$candidate")" 2>/dev/null && pwd -P)/$resolved" ;;
        esac
      fi
    fi

    if /usr/bin/grep -Fqx "$canonical" "$seen_file" 2>/dev/null; then
      continue
    fi

    printf '%s\n' "$canonical" >> "$seen_file"
    log_info "发现 Claude Code 候选文件：$candidate"

    if verify_claude_path "$candidate" 0; then
      return 0
    fi
  done <<EOF
$(candidate_claude_paths)
EOF

  log_info "未发现可通过版本验证的 Claude Code。"
  return 1
}


# ============================================================
# curl 请求与文件下载
# ============================================================

download_metadata() {
  url="$1"
  destination="$2"

  log_info "请求包元数据：$url"

  /usr/bin/curl \
    --fail \
    --location \
    --connect-timeout "$NETWORK_CONNECT_TIMEOUT" \
    --retry 1 \
    --retry-delay 2 \
    --retry-connrefused \
    --user-agent "CubeAI-Installer/1.0 (macOS; Bash)" \
    --header "Cache-Control: no-cache" \
    --silent \
    --show-error \
    --max-time "$METADATA_TOTAL_TIMEOUT" \
    --output "$destination" \
    "$url"

  result=$?

  if [ "$result" -ne 0 ]; then
    safe_remove "$destination"
    log_warn "元数据请求失败；curl 返回码=$result"
    return 1
  fi

  if [ ! -s "$destination" ]; then
    safe_remove "$destination"
    log_warn "元数据响应为空。"
    return 1
  fi

  version="$(json_get "$destination" "version" 2>/dev/null || true)"
  tarball="$(json_get "$destination" "dist.tarball" 2>/dev/null || true)"

  if [ -z "$version" ] || [ -z "$tarball" ]; then
    log_warn "元数据缺少 version 或 dist.tarball。"
    safe_remove "$destination"
    return 1
  fi

  log_ok "元数据读取成功：版本=$version"
  return 0
}

download_package_file() {
  url="$1"
  destination="$2"
  label="$3"
  partial="${destination}.part"

  safe_remove "$partial"
  log_info "开始下载：$url"

  /usr/bin/curl \
    --fail \
    --location \
    --connect-timeout "$NETWORK_CONNECT_TIMEOUT" \
    --max-time "$PACKAGE_DOWNLOAD_TIMEOUT" \
    --retry 1 \
    --retry-delay 2 \
    --retry-connrefused \
    --speed-time 30 \
    --speed-limit 1024 \
    --user-agent "CubeAI-Installer/1.0 (macOS; Bash)" \
    --header "Cache-Control: no-cache" \
    --progress-bar \
    --output "$partial" \
    "$url"

  result=$?
  printf '\n'

  if [ "$result" -ne 0 ]; then
    safe_remove "$partial"
    log_warn "${label}失败；curl 返回码=$result"
    return 1
  fi

  if [ ! -s "$partial" ]; then
    safe_remove "$partial"
    log_warn "${label}结果为空。"
    return 1
  fi

  /bin/mv -f "$partial" "$destination" || {
    safe_remove "$partial"
    return 1
  }

  size="$(file_size "$destination")"
  log_ok "${label}完成：$(format_bytes "$size")"
  return 0
}


# ============================================================
# NPM 原生二进制包
# ============================================================

fetch_package_info() {
  source_name="$1"
  registry_base="${2%/}"
  metadata_file="${TEMP_DIR}/package-metadata.json"

  PACKAGE_VERSION=""
  PACKAGE_TARBALL=""
  PACKAGE_INTEGRITY=""
  PACKAGE_SHASUM=""

  encoded_package="$(printf '%s' "$PLATFORM_PACKAGE_NAME" | \
    /usr/bin/sed 's#/#%2F#g')"

  for tag in "$PACKAGE_RELEASE_TAG" "latest"; do
    for metadata_url in \
      "${registry_base}/${encoded_package}/${tag}" \
      "${registry_base}/${PLATFORM_PACKAGE_NAME}/${tag}"; do

      safe_remove "$metadata_file"

      if download_metadata "$metadata_url" "$metadata_file"; then
        PACKAGE_VERSION="$(json_get "$metadata_file" "version" 2>/dev/null || true)"
        PACKAGE_TARBALL="$(json_get "$metadata_file" "dist.tarball" 2>/dev/null || true)"
        PACKAGE_INTEGRITY="$(json_get "$metadata_file" "dist.integrity" 2>/dev/null || true)"
        PACKAGE_SHASUM="$(json_get "$metadata_file" "dist.shasum" 2>/dev/null || true)"

        if supported_claude_version "$PACKAGE_VERSION" && [ -n "$PACKAGE_TARBALL" ]; then
          log_ok "${source_name} 可用版本：${PACKAGE_VERSION}；标签=${tag}"
          return 0
        fi
        log_warn "${source_name} 的 ${tag} 版本不支持 Fable 5.1，继续查找 ${MINIMUM_CLAUDE_VERSION}+。"
      fi
    done
  done

  return 1
}

verify_package_integrity() {
  archive="$1"
  integrity="$2"
  shasum="$3"

  log_info "正在校验安装包完整性。"

  if [ -n "$integrity" ]; then
    sri="$(printf '%s' "$integrity" | /usr/bin/awk '{print $1}')"
    algorithm="${sri%%-*}"
    expected="${sri#*-}"

    case "$algorithm" in
      sha512|sha384|sha256|sha1)
        if [ -x /usr/bin/openssl ]; then
          actual="$(/usr/bin/openssl dgst "-${algorithm}" -binary "$archive" 2>/dev/null | \
            /usr/bin/openssl base64 -A 2>/dev/null || true)"

          if [ -n "$actual" ] && [ "$actual" = "$expected" ]; then
            log_ok "完整性校验通过：$algorithm"
            return 0
          fi

          log_warn "SRI 完整性校验不一致：$algorithm"
          return 1
        fi
        ;;
    esac
  fi

  if [ -n "$shasum" ] && [ -x /usr/bin/shasum ]; then
    actual_sha1="$(/usr/bin/shasum -a 1 "$archive" 2>/dev/null | /usr/bin/awk '{print $1}')"

    if [ -n "$actual_sha1" ] && [ "$actual_sha1" = "$shasum" ]; then
      log_ok "完整性校验通过：sha1"
      return 0
    fi

    log_warn "SHA1 完整性校验不一致。"
    return 1
  fi

  size="$(file_size "$archive")"
  file_description="$(/usr/bin/file -b "$archive" 2>/dev/null || true)"

  if [ "$size" -gt 1048576 ] && \
     printf '%s' "$file_description" | /usr/bin/grep -qi "gzip"; then
    log_warn "镜像未提供可用哈希；已通过 gzip 格式和文件体积检查。"
    return 0
  fi

  log_warn "安装包既没有可用哈希，也未通过基础格式检查。"
  return 1
}

tar_member_exists() {
  member_list_file="$1"
  member_name="$2"
  /usr/bin/grep -Fqx "$member_name" "$member_list_file" 2>/dev/null
}

find_claude_member() {
  archive="$1"
  FOUND_TAR_MEMBER=""
  member_list_file="${TEMP_DIR}/package-members.txt"
  package_json_file="${TEMP_DIR}/package.json"

  /usr/bin/tar -tzf "$archive" > "$member_list_file" 2>/dev/null || {
    log_warn "无法读取 tgz 安装包目录。"
    return 1
  }

  member_count="$(/usr/bin/wc -l < "$member_list_file" | /usr/bin/tr -d ' ')"
  log_info "安装包内共有 ${member_count:-0} 个条目。"

  package_json_member="$(/usr/bin/awk '/(^|\/)package\.json$/ { print; exit }' "$member_list_file")"

  if [ -n "$package_json_member" ]; then
    if /usr/bin/tar -xOzf "$archive" "$package_json_member" > "$package_json_file" 2>/dev/null; then
      declared_bin="$(json_get "$package_json_file" "bin.claude" 2>/dev/null || true)"

      if [ -z "$declared_bin" ]; then
        possible_bin="$(json_get "$package_json_file" "bin" 2>/dev/null || true)"
        case "$possible_bin" in
          \{*|\[*|"") ;;
          *) declared_bin="$possible_bin" ;;
        esac
      fi

      if [ -n "$declared_bin" ]; then
        declared_bin="$(printf '%s' "$declared_bin" | /usr/bin/sed 's#^\./##')"
        package_root="${package_json_member%/package.json}"
        declared_member="${package_root}/${declared_bin}"

        case "$declared_member" in
          /*|*../*)
            log_warn "package.json 中的 bin 路径不安全，已忽略。"
            ;;
          *)
            if tar_member_exists "$member_list_file" "$declared_member"; then
              log_ok "根据 package.json 找到 Claude 命令入口：$declared_member"
              FOUND_TAR_MEMBER="$declared_member"
              return 0
            fi
            ;;
        esac
      fi
    fi
  fi

  for exact_member in \
    "package/claude" \
    "claude" \
    "package/bin/claude" \
    "bin/claude"; do
    if tar_member_exists "$member_list_file" "$exact_member"; then
      log_ok "通过常用路径找到 Claude Code：$exact_member"
      FOUND_TAR_MEMBER="$exact_member"
      return 0
    fi
  done

  fallback_member="$(/usr/bin/awk '
    {
      name=$0
      sub(/^.*\//, "", name)
      if (name == "claude" || name == "claude-code") {
        print length($0) "\t" $0
      }
    }
  ' "$member_list_file" | /usr/bin/sort -n | /usr/bin/head -n 1 | /usr/bin/cut -f2-)"

  if [ -n "$fallback_member" ]; then
    log_ok "通过文件扫描找到 Claude Code：$fallback_member"
    FOUND_TAR_MEMBER="$fallback_member"
    return 0
  fi

  log_warn "安装包中没有找到 Claude Code 可执行文件。"
  log_warn "安装包前 30 个条目如下："
  /usr/bin/head -n 30 "$member_list_file"
  return 1
}

extract_claude_executable() {
  archive="$1"
  destination="$2"
  temporary="${destination}.new.$$"

  log_info "正在从安装包提取 Claude Code：$archive"

  FOUND_TAR_MEMBER=""
  if ! find_claude_member "$archive"; then
    return 1
  fi

  member="$FOUND_TAR_MEMBER"
  if [ -z "$member" ]; then
    return 1
  fi

  log_info "准备提取归档成员：$member"
  /bin/mkdir -p "$(dirname "$destination")" || return 1
  safe_remove "$temporary"

  /usr/bin/tar -xOzf "$archive" "$member" > "$temporary" 2>/dev/null || {
    safe_remove "$temporary"
    log_warn "从 tgz 中提取 Claude Code 失败。"
    return 1
  }

  size="$(file_size "$temporary")"
  if [ "$size" -lt 5242880 ]; then
    safe_remove "$temporary"
    log_warn "提取出的 Claude Code 体积异常：$size 字节"
    return 1
  fi

  description="$(/usr/bin/file -b "$temporary" 2>/dev/null || true)"
  log_info "提取文件类型：$description"

  if ! printf '%s' "$description" | /usr/bin/grep -qi "Mach-O"; then
    safe_remove "$temporary"
    log_warn "提取文件不是有效的 macOS Mach-O 可执行文件。"
    return 1
  fi

  if [ "$DETECTED_ARCH" = "arm64" ]; then
    if ! printf '%s' "$description" | /usr/bin/grep -Eqi "arm64|universal"; then
      safe_remove "$temporary"
      log_warn "提取文件不包含 arm64 架构。"
      return 1
    fi
  else
    if ! printf '%s' "$description" | /usr/bin/grep -Eqi "x86_64|universal"; then
      safe_remove "$temporary"
      log_warn "提取文件不包含 x86_64 架构。"
      return 1
    fi
  fi

  /bin/chmod 755 "$temporary" || {
    safe_remove "$temporary"
    return 1
  }

  if [ -x /usr/bin/xattr ]; then
    /usr/bin/xattr -d com.apple.quarantine "$temporary" 2>/dev/null || true
  fi

  /bin/mv -f "$temporary" "$destination" || {
    safe_remove "$temporary"
    return 1
  }

  /bin/chmod 755 "$destination" 2>/dev/null || true
  log_ok "Claude Code 已提取：${destination}；大小=$(format_bytes "${size}")"
  return 0
}

rewrite_tarball_for_registry() {
  registry_base="${1%/}"
  tarball_url="$2"
  path="$(printf '%s' "$tarball_url" | /usr/bin/sed -E 's#^https?://[^/]+##')"

  if [ -n "$path" ] && [ "$path" != "$tarball_url" ]; then
    printf '%s%s' "$registry_base" "$path"
    return 0
  fi

  return 1
}

install_from_registry_source() {
  source_name="$1"
  registry_base="${2%/}"

  log_info "开始尝试镜像：$source_name"
  log_info "平台包名：$PLATFORM_PACKAGE_NAME"

  if ! fetch_package_info "$source_name" "$registry_base"; then
    log_warn "$source_name 无法取得可用包元数据。"
    return 10
  fi

  archive="${TEMP_DIR}/claude-${PACKAGE_VERSION}.tgz"
  safe_remove "$archive"

  package_leaf="${PLATFORM_PACKAGE_NAME##*/}"
  rewritten_url="$(rewrite_tarball_for_registry "$registry_base" "$PACKAGE_TARBALL" 2>/dev/null || true)"
  constructed_url="${registry_base}/${PLATFORM_PACKAGE_NAME}/-/${package_leaf}-${PACKAGE_VERSION}.tgz"

  download_urls_file="${TEMP_DIR}/download-urls.txt"
  : > "$download_urls_file"

  for url in "$rewritten_url" "$constructed_url" "$PACKAGE_TARBALL"; do
    [ -z "$url" ] && continue

    if ! /usr/bin/grep -Fqx "$url" "$download_urls_file" 2>/dev/null; then
      printf '%s\n' "$url" >> "$download_urls_file"
    fi
  done

  valid_package_downloaded=0
  download_index=0
  download_total="$(/usr/bin/wc -l < "$download_urls_file" | /usr/bin/tr -d ' ')"

  while IFS= read -r tarball_url; do
    [ -z "$tarball_url" ] && continue
    download_index=$((download_index + 1))

    log_info "$source_name 下载候选 ${download_index}/${download_total}"

    if download_package_file \
      "$tarball_url" \
      "$archive" \
      "下载 Claude ${PACKAGE_VERSION}"; then

      if verify_package_integrity \
        "$archive" \
        "$PACKAGE_INTEGRITY" \
        "$PACKAGE_SHASUM"; then

        valid_package_downloaded=1
        log_ok "$source_name 已成功下载并校验安装包；停止尝试其他下载地址。"
        break
      fi
    fi

    safe_remove "$archive"
  done < "$download_urls_file"

  if [ "$valid_package_downloaded" -ne 1 ]; then
    log_warn "$source_name 的所有下载地址均未取得有效安装包。"
    return 10
  fi

  # 已经取得并校验同版本的有效包。若本地提取或执行失败，
  # 继续从其他镜像重复下载相同文件没有意义。
  if ! extract_claude_executable "$archive" "$CUBEAI_MANAGED_CLAUDE"; then
    log_warn "有效安装包下载成功，但本地提取失败；不再重复下载其他镜像。"
    return 20
  fi

  if verify_claude_path "$CUBEAI_MANAGED_CLAUDE" 1; then
    log_ok "$source_name 安装并验证成功；版本=$PACKAGE_VERSION"
    return 0
  fi

  log_warn "有效安装包已提取，但 claude --version 未通过；不再重复下载其他镜像。"
  return 20
}

install_from_registry_mirrors() {
  count="${#NPM_SOURCE_NAMES[@]}"
  index=0

  log_info "进入国内镜像安装流程；共有 $count 个来源。"

  while [ "$index" -lt "$count" ]; do
    source_name="${NPM_SOURCE_NAMES[$index]}"
    source_url="${NPM_SOURCE_URLS[$index]}"

    printf '\n'
    log "STEP" "镜像来源 $((index + 1))/${count}：$source_name"

    install_from_registry_source "$source_name" "$source_url"
    result=$?

    if [ "$result" -eq 0 ]; then
      return 0
    fi

    if [ "$result" -eq 20 ]; then
      log_warn "已取得有效包但本地安装失败；结束镜像循环，转入官方安装方案。"
      return 1
    fi

    log_warn "$source_name 未完成安装，自动切换下一个来源。"
    index=$((index + 1))
  done

  return 1
}


# ============================================================
# 官方安装方案与 Homebrew 兜底
# ============================================================

install_via_official_script() {
  installer="${TEMP_DIR}/claude-install.sh"
  safe_remove "$installer"

  log_info "正在下载 Claude Code 官方 macOS 安装器。"

  /usr/bin/curl \
    --fail \
    --location \
    --connect-timeout "$NETWORK_CONNECT_TIMEOUT" \
    --max-time "$SCRIPT_DOWNLOAD_TIMEOUT" \
    --retry 2 \
    --retry-delay 2 \
    --retry-connrefused \
    --user-agent "CubeAI-Installer/1.0 (macOS; Bash)" \
    --progress-bar \
    --output "$installer" \
    "$OFFICIAL_INSTALLER_URL"

  result=$?
  printf '\n'

  if [ "$result" -ne 0 ] || [ ! -s "$installer" ]; then
    safe_remove "$installer"
    log_warn "官方安装器下载失败；curl 返回码=$result"
    return 1
  fi

  size="$(file_size "$installer")"
  if [ "$size" -lt 100 ]; then
    safe_remove "$installer"
    log_warn "官方安装器文件体积异常：$size 字节"
    return 1
  fi

  /bin/chmod 700 "$installer" 2>/dev/null || true

  if ! run_command_with_timeout \
    "官方安装 Claude Code" \
    "$INSTALL_PROCESS_TIMEOUT" \
    /bin/bash "$installer" latest; then
    return 1
  fi

  if locate_and_verify_claude; then
    log_ok "Claude Code 官方安装方案验证成功：$VERIFIED_CLAUDE_PATH"
    return 0
  fi

  log_warn "官方安装器执行结束，但未找到可验证的 Claude Code。"
  return 1
}

install_via_homebrew() {
  brew_path="$(command -v brew 2>/dev/null || true)"

  if [ -z "$brew_path" ] || [ ! -x "$brew_path" ]; then
    log_warn "系统未安装 Homebrew，跳过 Homebrew 兜底。"
    return 1
  fi

  log_info "检测到 Homebrew：$brew_path"

  if ! run_command_with_timeout \
    "Homebrew 安装 Claude Code" \
    "$INSTALL_PROCESS_TIMEOUT" \
    "$brew_path" install --cask claude-code; then
    return 1
  fi

  if locate_and_verify_claude; then
    log_ok "Homebrew 安装方案验证成功：$VERIFIED_CLAUDE_PATH"
    return 0
  fi

  return 1
}

install_from_official_fallbacks() {
  round=1

  while [ "$round" -le "$OFFICIAL_INSTALL_ROUNDS" ]; do
    log "STEP" "官方安装方案第 ${round}/${OFFICIAL_INSTALL_ROUNDS} 轮"

    if install_via_official_script; then
      return 0
    fi

    round=$((round + 1))
  done

  log "STEP" "开始执行 Homebrew 最终兜底"

  if install_via_homebrew; then
    return 0
  fi

  return 1
}

ensure_claude_installed() {
  if locate_and_verify_claude; then
    log_ok "现有 Claude Code 可用，无需重新安装：$VERIFIED_CLAUDE_PATH"
    return 0
  fi

  log_warn "现有 Claude Code 不可用，开始国内镜像安装。"

  if install_from_registry_mirrors; then
    return 0
  fi

  log_warn "国内镜像未完成安装，开始官方安装器与 Homebrew 兜底。"

  if install_from_official_fallbacks; then
    return 0
  fi

  if locate_and_verify_claude; then
    return 0
  fi

  return 1
}


# ============================================================
# 终端命令兼容
# ============================================================

ensure_path_in_shell_profile() {
  profile_path="$1"
  marker_start="# >>> CubeAI command path >>>"
  marker_end="# <<< CubeAI command path <<<"

  if [ -f "$profile_path" ] && \
     /usr/bin/grep -Fq "$marker_start" "$profile_path" 2>/dev/null; then
    log_info "Shell 配置已包含 CubeAI 命令目录：${profile_path}"
    return 0
  fi

  profile_directory="$(/usr/bin/dirname "$profile_path")"
  /bin/mkdir -p "$profile_directory" 2>/dev/null || {
    log_warn "无法创建 Shell 配置目录：${profile_directory}"
    return 1
  }

  /usr/bin/touch "$profile_path" 2>/dev/null || {
    log_warn "无法创建或写入 Shell 配置：${profile_path}"
    return 1
  }

  {
    printf '\n%s\n' "$marker_start"
    printf '%s\n' 'case ":$PATH:" in'
    printf '%s\n' '  *":$HOME/.local/bin:"*) ;;'
    printf '%s\n' '  *) export PATH="$HOME/.local/bin:$PATH" ;;'
    printf '%s\n' 'esac'
    printf '%s\n' "$marker_end"
  } >> "$profile_path" || {
    log_warn "写入 Shell 配置失败：${profile_path}"
    return 1
  }

  log_ok "已写入 Shell 命令路径：${profile_path}"
  return 0
}

persist_terminal_command_path() {
  # 当前安装进程立即生效。
  case ":${PATH:-}:" in
    *":${LOCAL_BIN_DIR}:"*)
      ;;
    *)
      export PATH="${LOCAL_BIN_DIR}:${PATH:-}"
      ;;
  esac

  # VS Code 的 zsh 集成终端通常读取 .zshrc；macOS Terminal 的
  # 登录终端还会读取 .zprofile。两者都写入，确保重新打开后可用。
  case "${SHELL:-}" in
    */zsh)
      zsh_config_directory="${ZDOTDIR:-${USER_HOME}}"
      ensure_path_in_shell_profile "${zsh_config_directory}/.zshrc" || true
      ensure_path_in_shell_profile "${zsh_config_directory}/.zprofile" || true
      ;;
    */bash)
      ensure_path_in_shell_profile "${USER_HOME}/.bashrc" || true
      ensure_path_in_shell_profile "${USER_HOME}/.bash_profile" || true
      ;;
    *)
      zsh_config_directory="${ZDOTDIR:-${USER_HOME}}"
      ensure_path_in_shell_profile "${zsh_config_directory}/.zshrc" || true
      ensure_path_in_shell_profile "${zsh_config_directory}/.zprofile" || true
      ensure_path_in_shell_profile "${USER_HOME}/.bashrc" || true
      ensure_path_in_shell_profile "${USER_HOME}/.bash_profile" || true
      ;;
  esac

  hash -r 2>/dev/null || true
  log_ok "终端命令路径已持久化：${LOCAL_BIN_DIR}"
}

ensure_terminal_command() {
  claude_path="$1"
  token="$2"

  /bin/mkdir -p "$LOCAL_BIN_DIR" || {
    log_warn "无法创建终端命令目录：$LOCAL_BIN_DIR"
    return 1
  }

  /bin/chmod 700 "$LOCAL_BIN_DIR" 2>/dev/null || true

  shim="${LOCAL_BIN_DIR}/claude"
  temporary="${shim}.tmp.$$"

  # 若 Claude 本体本来就在该位置，不覆盖它。
  # settings.json 已包含完整环境配置，仍可正常使用。
  if [ "$claude_path" = "$shim" ]; then
    log_info "Claude Code 已位于标准终端目录，无需创建转发入口。"
    persist_terminal_command_path
    return 0
  fi

  quoted_claude_path="$(printf '%q' "$claude_path")"
  quoted_token="$(printf '%q' "$token")"

  # Reuse the launch environment so terminal model mappings cannot drift.
  {
    printf '%s\n' '#!/bin/bash'
    printf 'CUBEAI_BASE_URL=%q\n' "$CUBEAI_BASE_URL"
    printf 'CLAUDE_DIR=%q\n' "$CLAUDE_DIR"
    printf 'CONFLICTING_ENV_VARS=('
    printf '%q ' "${CONFLICTING_ENV_VARS[@]}"
    printf ')\n'
    declare -f prepare_cubeai_environment
    printf 'prepare_cubeai_environment %s\n' "$quoted_token"
    printf 'exec %s "$@"\n' "$quoted_claude_path"
  } > "$temporary" || return 1

  /bin/chmod 700 "$temporary" || {
    safe_remove "$temporary"
    return 1
  }

  if [ -e "$shim" ] || [ -L "$shim" ]; then
    existing_target="$(/usr/bin/readlink "$shim" 2>/dev/null || true)"

    if [ -z "$existing_target" ]; then
      backup="${shim}.before-cubeai-$(date_stamp_now).bak"
      /bin/mv "$shim" "$backup" 2>/dev/null || {
        safe_remove "$temporary"
        log_warn "无法备份已有 claude 命令入口，跳过创建 shim。"
        return 1
      }
    else
      safe_remove "$shim"
    fi
  fi

  /bin/mv -f "$temporary" "$shim" || {
    safe_remove "$temporary"
    return 1
  }

  log_ok "已创建终端命令入口：$shim"
  persist_terminal_command_path
  return 0
}


# ============================================================
# 启动 Claude Code
# ============================================================

prepare_cubeai_environment() {
  token="$1"

  for name in "${CONFLICTING_ENV_VARS[@]}"; do
    unset "$name" 2>/dev/null || true
  done

  export ANTHROPIC_BASE_URL="$CUBEAI_BASE_URL"
  export ANTHROPIC_AUTH_TOKEN="$token"
  export CLAUDE_CONFIG_DIR="$CLAUDE_DIR"
  export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC="1"
  export API_TIMEOUT_MS="600000"

  export ANTHROPIC_DEFAULT_OPUS_MODEL="claude-opus-5"
  export ANTHROPIC_DEFAULT_OPUS_MODEL_NAME="Opus 5"
  export ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION="Claude flagship model"
  export ANTHROPIC_DEFAULT_OPUS_MODEL_SUPPORTED_CAPABILITIES="effort,xhigh_effort,max_effort,thinking,adaptive_thinking,interleaved_thinking"

  export ANTHROPIC_DEFAULT_FABLE_MODEL="claude-fable-5-1"
  export ANTHROPIC_DEFAULT_FABLE_MODEL_NAME="Fable 5.1"
  export ANTHROPIC_DEFAULT_FABLE_MODEL_DESCRIPTION="Claude flagship model"
  export ANTHROPIC_DEFAULT_FABLE_MODEL_SUPPORTED_CAPABILITIES="effort,xhigh_effort,max_effort,thinking,adaptive_thinking,interleaved_thinking"

  export ANTHROPIC_CUSTOM_MODEL_OPTION="claude-fable-5"
  export ANTHROPIC_CUSTOM_MODEL_OPTION_NAME="Fable 5"
  export ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION="Powerful reasoning"
  export ANTHROPIC_CUSTOM_MODEL_OPTION_SUPPORTED_CAPABILITIES="$ANTHROPIC_DEFAULT_FABLE_MODEL_SUPPORTED_CAPABILITIES"

  export ANTHROPIC_DEFAULT_SONNET_MODEL="claude-sonnet-5"
  export ANTHROPIC_DEFAULT_SONNET_MODEL_NAME="Sonnet 5"
  export ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION="Claude balanced model"
  export ANTHROPIC_DEFAULT_SONNET_MODEL_SUPPORTED_CAPABILITIES="effort,xhigh_effort,max_effort,thinking,adaptive_thinking,interleaved_thinking"

  export ANTHROPIC_DEFAULT_HAIKU_MODEL="claude-haiku-4-5"
  export ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME="Haiku 4.5"
  export ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION="Claude fast model"
  export ANTHROPIC_DEFAULT_HAIKU_MODEL_SUPPORTED_CAPABILITIES="thinking,adaptive_thinking"

  export CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY="0"

  # 不能设置 ANTHROPIC_MODEL，否则会覆盖 /model 保存的选择。
  unset ANTHROPIC_MODEL 2>/dev/null || true
}


release_lock_before_launch() {
  if [ "$LOCK_ACQUIRED" -eq 1 ] && [ -d "$LOCK_DIR" ]; then
    /bin/rm -rf "$LOCK_DIR" 2>/dev/null || true
    LOCK_ACQUIRED=0
  fi
}

remove_temp_before_launch() {
  if [ -n "$TEMP_DIR" ] && [ -d "$TEMP_DIR" ]; then
    /bin/rm -rf "$TEMP_DIR" 2>/dev/null || true
  fi
  TEMP_DIR=""
}

stop_install_logging() {
  # 等待最后一批安装日志写完，然后恢复原始终端。
  /bin/sleep 0.2
  exec 1>&3
  exec 2>&4
  exec 3>&-
  exec 4>&-
}

launch_claude_in_current_terminal() {
  claude_path="$1"
  token="$2"

  prepare_cubeai_environment "$token"

  log_info "准备启动 Claude Code。"
  log_info "工作目录：$ORIGINAL_WORKING_DIRECTORY"
  log_info "Claude 路径：$claude_path"
  log_info "Base URL：$ANTHROPIC_BASE_URL"
  log_info "认证方式：ANTHROPIC_AUTH_TOKEN（内容已隐藏）"

  if [ ! -x "$claude_path" ]; then
    fail_and_exit "启动前发现 Claude Code 文件不可执行：$claude_path"
  fi

  release_lock_before_launch
  remove_temp_before_launch
  finish_progress

  # Claude 会话不写入安装日志。
  stop_install_logging

  cd "$ORIGINAL_WORKING_DIRECTORY" 2>/dev/null || cd "$USER_HOME" || exit 1
  exec "$claude_path"

  exit 1
}


# ============================================================
# 主流程
# ============================================================

main() {
  if [ "$(uname -s 2>/dev/null || true)" != "Darwin" ]; then
    exit 1
  fi

  init_directories_and_logging
  acquire_lock
  create_temp_directory
  log_system_information

  LOADED_BASE_URL=""
  LOADED_TOKEN=""
  config_available=0

  if load_cubeai_config; then
    config_available=1
  fi

  token="$LOADED_TOKEN"

  # 首次使用仍需要输入密钥。除这一项必要输入外，
  # 后续界面只有一条进度条。
  if [ "$config_available" -ne 1 ] || [ -z "$token" ]; then
    exec 1>&3
    exec 2>&4
    ui_clear
    printf '%s' "请粘贴 API密钥 并按回车：" >&3
    IFS= read -r token
    printf '\n' >&3
    exec >>"$LOG_FILE" 2>&1

    token="$(printf '%s' "$token" | /usr/bin/sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//')"

    if [ -z "$token" ]; then
      fail_and_exit "没有输入 Claude Code 密钥。"
    fi
  fi

  start_progress

  claude_path=""
  if locate_and_verify_claude; then
    claude_path="$VERIFIED_CLAUDE_PATH"
  fi

  # 快速启动：配置与 Claude Code 均存在。
  if [ "$config_available" -eq 1 ] && \
     [ -n "$LOADED_TOKEN" ] && \
     [ -n "$claude_path" ]; then

    if ! persist_cubeai_runtime_config "$LOADED_TOKEN"; then
      fail_and_exit "刷新 settings.json 失败，请检查文件权限后重试。"
    fi

    ensure_terminal_command "$claude_path" "$LOADED_TOKEN" || true
    launch_claude_in_current_terminal "$claude_path" "$LOADED_TOKEN"
    return
  fi

  if [ "$config_available" -ne 1 ] || [ -z "$LOADED_TOKEN" ]; then
    if ! save_cubeai_config "$token"; then
      /bin/sleep 2

      if ! save_cubeai_config "$token"; then
        fail_and_exit "Claude Code 配置连续两次保存失败。"
      fi
    fi

    if ! persist_cubeai_runtime_config "$token"; then
      /bin/sleep 2

      if ! persist_cubeai_runtime_config "$token"; then
        fail_and_exit "Claude Code 配置连续两次写入失败。"
      fi
    fi
  else
    if ! persist_cubeai_runtime_config "$token"; then
      fail_and_exit "刷新 Claude settings.json 失败，请检查文件权限后重试。"
    fi
  fi

  if [ -z "$claude_path" ]; then
    if ensure_claude_installed; then
      claude_path="$VERIFIED_CLAUDE_PATH"
    fi
  fi

  if [ -z "$claude_path" ]; then
    fail_and_exit "国内镜像、官方安装器和 Homebrew 均未能完成 Claude Code 安装。"
  fi

  if ! verify_claude_path "$claude_path" 1; then
    log_warn "最终验证失败，自动执行一次完整修复流程。"

    if ensure_claude_installed; then
      claude_path="$VERIFIED_CLAUDE_PATH"
    else
      claude_path=""
    fi
  fi

  if [ -z "$claude_path" ] || ! verify_claude_path "$claude_path" 1; then
    fail_and_exit "Claude Code 最终验证未通过。"
  fi

  ensure_terminal_command "$claude_path" "$token" || true

  if ! persist_cubeai_runtime_config "$token"; then
    fail_and_exit "最终刷新 Claude settings.json 失败，请检查文件权限后重试。"
  fi

  log_ok "全部安装与配置流程完成。"
  launch_claude_in_current_terminal "$claude_path" "$token"
}


main "$@"
