#!/usr/bin/env bash # Tumpai Handbook · Codex CLI macOS / Linux 一键安装脚本 # # 推荐用法(最稳): # bash <(curl -fsSL https://tumpai-handbook.pages.dev/install.sh) # # 也支持(管道方式,已加固兜底): # curl -fsSL https://tumpai-handbook.pages.dev/install.sh | bash # # 全自动(CI / 无人值守): # CODEX_API_KEY=sk-xxx CODEX_YES=1 bash <(curl -fsSL https://tumpai-handbook.pages.dev/install.sh) set -e BASE_URL="${CODEX_BASE_URL:-https://api.tumpai.site:2053/v1}" MODEL_DEFAULT="${CODEX_MODEL:-gpt-5.5}" ENV_KEY_NAME="TUMPAI_API_KEY" PROVIDER_NAME="tumpai" # --- 参数解析 --- ASSUME_YES="${CODEX_YES:-0}" for arg in "$@"; do case "$arg" in -y|--yes) ASSUME_YES=1 ;; -h|--help) cat <&2; exit 1; } title() { echo; echo "${C_BLD}${C_BLU}==> $*${C_RST}"; } # --- stdin 兜底:管道执行时把 stdin 切回真终端 --- NON_INTERACTIVE=0 if [ ! -t 0 ]; then if [ -r /dev/tty ] && exec /dev/null; then : else warn "未检测到可交互终端,进入非交互模式(需提前设置 CODEX_API_KEY 环境变量)" NON_INTERACTIVE=1 ASSUME_YES=1 fi fi # --- 系统识别 --- OS="$(uname -s)" ARCH="$(uname -m)" case "$OS" in Darwin) PLATFORM="mac" ;; Linux) PLATFORM="linux" ;; *) fail "暂不支持的系统:$OS(仅支持 macOS / Linux,Windows 请使用 install.ps1)" ;; esac # --- BANNER(不再 clear,避免提示被滚出屏幕)--- echo cat </dev/null 2>&1; then local cur cur="$(npm config get registry 2>/dev/null || echo '')" if [ "$cur" = "https://registry.npmjs.org/" ]; then info "未检测到代理;提示:如果 npm 安装很慢,可执行:npm config set registry https://registry.npmmirror.com" fi fi fi } # --- 1. 准备 Node --- title "1/5 检查 Node.js" if command -v node >/dev/null 2>&1; then NODE_V="$(node -v)" ok "已安装 Node $NODE_V" else warn "未检测到 Node.js,开始安装..." if [ "$PLATFORM" = "mac" ]; then if ! command -v brew >/dev/null 2>&1; then info "未发现 Homebrew,开始安装 Homebrew(约 2-5 分钟,可能需要输入 sudo 密码)" info "提示:如果在中国大陆且没开代理,Homebrew 安装可能极慢;建议先开 Clash/V2Ray TUN 模式后重试" /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" if [ -d /opt/homebrew/bin ]; then eval "$(/opt/homebrew/bin/brew shellenv)" fi fi brew install node else if command -v apt-get >/dev/null 2>&1; then info "使用 apt 安装 Node 20" curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt-get install -y nodejs elif command -v dnf >/dev/null 2>&1; then curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo -E bash - sudo dnf install -y nodejs elif command -v yum >/dev/null 2>&1; then curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo -E bash - sudo yum install -y nodejs else fail "无法识别的 Linux 发行版,请手动安装 Node.js 18+ 后重试:https://nodejs.org/" fi fi ok "Node 安装完成:$(node -v)" fi maybe_setup_npm_mirror # --- 2. 装 codex --- title "2/5 安装 Codex CLI" if command -v codex >/dev/null 2>&1; then ok "已安装 codex($(codex --version 2>/dev/null || echo 'unknown')),将更新到最新版" fi if [ "$PLATFORM" = "linux" ] && [ "$(id -u)" != "0" ]; then if [ ! -w "$(npm prefix -g 2>/dev/null || echo /usr/lib)" ]; then info "全局 npm 目录不可写,将使用 sudo" sudo npm install -g @openai/codex || fail "安装 codex 失败" else npm install -g @openai/codex || fail "安装 codex 失败" fi else npm install -g @openai/codex || fail "安装 codex 失败" fi ok "Codex 已安装:$(codex --version 2>/dev/null || echo '已就绪')" # --- 3. 让用户输入 key --- title "3/5 录入 API 密钥" API_KEY="${CODEX_API_KEY:-}" if [ -n "$API_KEY" ]; then ok "已从环境变量 CODEX_API_KEY 读取密钥(${#API_KEY} 字符)" elif [ "$NON_INTERACTIVE" = "1" ]; then fail "非交互模式但未提供 CODEX_API_KEY,请重试:CODEX_API_KEY=sk-xxx bash <(curl -fsSL .../install.sh)" else echo "请把服务方发放的 API Key 粘贴进来(输入会显示在屏幕上):" echo " · 这是访问 https://api.tumpai.site:2053/v1 的客户端密钥" echo " · 不是管理台登录密钥" echo " · 不会上传,只写到本机 ~/.codex/config.toml" echo while [ -z "$API_KEY" ]; do read -rp "${C_BLD}🔑 API Key: ${C_RST}" API_KEY if [ -z "$API_KEY" ]; then warn "密钥为空,请重新输入" continue fi if [ "${#API_KEY}" -lt 10 ]; then warn "密钥太短(${#API_KEY} 字符),看起来不太像,确定吗?" read -rp "继续使用此密钥?[y/N] " YN case "$YN" in [Yy]*) break ;; *) API_KEY="" ;; esac fi done ok "已收到密钥(${#API_KEY} 字符)" fi # 选模型 MODEL="$MODEL_DEFAULT" if [ "$NON_INTERACTIVE" = "1" ] || [ "$ASSUME_YES" = "1" ]; then ok "使用默认模型:$MODEL" else echo echo "选择默认模型:" echo " ${C_BLD}1${C_RST}) gpt-5.5 ← 推荐(综合最强)" echo " ${C_BLD}2${C_RST}) gpt-5.4 (高性价比)" if read -t 30 -rp "请选择 [1](30s 内不输入默认 1): " MSEL; then :; else echo; MSEL=""; fi MSEL="${MSEL:-1}" case "$MSEL" in 2) MODEL="gpt-5.4" ;; *) MODEL="gpt-5.5" ;; esac ok "默认模型:$MODEL" fi # --- 4. 写配置 --- title "4/5 写入配置" CFG_DIR="$HOME/.codex" CFG_FILE="$CFG_DIR/config.toml" mkdir -p "$CFG_DIR" if [ -f "$CFG_FILE" ]; then BAK="$CFG_FILE.bak-$(date +%Y%m%d-%H%M%S)" cp "$CFG_FILE" "$BAK" ok "已备份旧配置到 $BAK" fi cat > "$CFG_FILE" <>> codex-tumpai >>>/,/# <<< codex-tumpai <</dev/null || true else sed -i "/# >>> codex-tumpai >>>/,/# <<< codex-tumpai <</dev/null || true fi done # 写入 export case "$SHELL" in *fish) mkdir -p "$(dirname "$SHELL_RC")" cat >> "$SHELL_RC" <>> codex-tumpai >>> set -gx $ENV_KEY_NAME "$API_KEY" # <<< codex-tumpai <<< EOF ;; *) for rc in "${RC_FILES[@]}"; do mkdir -p "$(dirname "$rc")" cat >> "$rc" <>> codex-tumpai >>> export $ENV_KEY_NAME="$API_KEY" # <<< codex-tumpai <<< EOF done ;; esac if [ "${#RC_FILES[@]}" -gt 1 ]; then ok "已写入 ${RC_FILES[*]}(开新终端即可生效)" else ok "已写入 $SHELL_RC(开新终端或运行 'source $SHELL_RC' 生效)" fi export $ENV_KEY_NAME="$API_KEY" # --- 5. 验证 --- title "5/5 验证连通性" HTTP_CODE="$(curl -s -o /tmp/codex_check.txt -w "%{http_code}" \ --max-time 15 \ -H "Authorization: Bearer $API_KEY" \ "$BASE_URL/models" || echo "000")" case "$HTTP_CODE" in 200) ok "服务可达,密钥有效 ✅" ;; 401|403) warn "服务可达,但密钥被拒绝(HTTP $HTTP_CODE)。请检查密钥是否正确或联系服务方。" ;; 000) warn "无法连接 $BASE_URL,请检查网络(也可能是临时波动,可稍后再试)" ;; *) warn "返回 HTTP $HTTP_CODE,未必有问题,可继续测试" ;; esac rm -f /tmp/codex_check.txt echo cat <