diff --git a/Bookworm-Setup.sh b/Bookworm-Setup.sh index 899aeae..4cb5f88 100644 --- a/Bookworm-Setup.sh +++ b/Bookworm-Setup.sh @@ -1,7 +1,12 @@ #!/bin/bash # ============================================================ -# Bookworm Portable - macOS One-Click Setup +# Bookworm Portable - macOS Setup (从 boot 仓库内运行) # Version: 1.5 +# +# 用法: cd ~/bookworm-boot && bash Bookworm-Setup.sh +# +# 前提: 已 git clone bookworm-boot 到本地 +# 功能: 检查依赖 → 代理检测 → 克隆配置 → 解密凭证 → 配置别名 # ============================================================ set -e @@ -12,18 +17,21 @@ GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' -NC='\033[0m' # No Color +NC='\033[0m' BOLD='\033[1m' # 配置 -GITEA_URL="https://code.letcareme.com/bookworm/bookworm-boot.git" -BOOT_DIR="$HOME/bookworm-boot" +BOOT_DIR="$(cd "$(dirname "$0")" && pwd)" +CLAUDE_DIR="$HOME/.claude" +CONFIG_REPO="https://code.letcareme.com/bookworm/bookworm-config.git" +SECRETS_ENC="$BOOT_DIR/secrets.enc" +TOTAL_STEPS=6 banner() { echo "" echo -e "${CYAN} ____ _" echo " | __ ) ___ ___ | | ____ _____ _ __ _ __ ___" - echo " | _ \\ / _ \\ / _ \\| |/ /\\ \\ /\\ / / _ \\| '__| '_ \` _ \\" + echo " | _ \\ / _ \\ / _ \\| |/ /\\ \\ /\\ / / _ \\| '__| '\` _ \\" echo " | |_) | (_) | (_) | < \\ V V / (_) | | | | | | | |" echo " |____/ \\___/ \\___/|_|\\_\\ \\_/\\_/ \\___/|_| |_| |_| |_|" echo "" @@ -32,16 +40,14 @@ banner() { echo -e "${NC}" } -info() { echo -e "${BLUE}[INFO]${NC} $1"; } -success() { echo -e "${GREEN}[OK]${NC} $1"; } -warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } -error() { echo -e "${RED}[ERROR]${NC} $1"; } -step() { echo -e "\n${BOLD}[$1/$TOTAL_STEPS]${NC} ${CYAN}$2${NC}"; } - -TOTAL_STEPS=6 +info() { echo -e " ${BLUE}[INFO]${NC} $1"; } +success() { echo -e " ${GREEN}[OK]${NC} $1"; } +warn() { echo -e " ${YELLOW}[!]${NC} $1"; } +fail() { echo -e " ${RED}[!!]${NC} $1"; } +step() { echo -e "\n${BOLD} [$1/$TOTAL_STEPS]${NC} ${CYAN}$2${NC}"; } # ============================================================ -# Step 0: Banner +# Banner # ============================================================ banner @@ -51,59 +57,60 @@ banner step 1 "检查依赖软件" # Homebrew -if ! command -v brew &> /dev/null; then - warn "Homebrew 未安装,正在安装..." +if ! command -v brew &>/dev/null; then + warn "Homebrew 未安装, 正在安装 (可能需要输入系统密码)..." /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - # Apple Silicon PATH if [ -f /opt/homebrew/bin/brew ]; then eval "$(/opt/homebrew/bin/brew shellenv)" - echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> "$HOME/.zprofile" + PROFILE="$HOME/.zprofile" + if ! grep -q 'homebrew' "$PROFILE" 2>/dev/null; then + echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> "$PROFILE" + fi fi success "Homebrew 安装完成" else - success "Homebrew $(brew --version | head -1)" + success "Homebrew $(brew --version | head -1 | awk '{print $2}')" fi # Node.js -if ! command -v node &> /dev/null; then - warn "Node.js 未安装,正在通过 Homebrew 安装..." +if ! command -v node &>/dev/null; then + info "通过 Homebrew 安装 Node.js..." brew install node - success "Node.js 安装完成" + success "Node.js $(node -v) 安装完成" else success "Node.js $(node -v)" fi # Git -if ! command -v git &> /dev/null; then - warn "Git 未安装,正在通过 Homebrew 安装..." +if ! command -v git &>/dev/null; then + info "通过 Homebrew 安装 Git..." brew install git - success "Git 安装完成" + success "Git $(git --version | awk '{print $3}') 安装完成" else - success "Git $(git --version)" + success "Git $(git --version | awk '{print $3}')" fi -# openssl +# OpenSSL OPENSSL_CMD="" -if command -v /opt/homebrew/opt/openssl/bin/openssl &> /dev/null; then - OPENSSL_CMD="/opt/homebrew/opt/openssl/bin/openssl" -elif command -v /usr/local/opt/openssl/bin/openssl &> /dev/null; then - OPENSSL_CMD="/usr/local/opt/openssl/bin/openssl" -elif command -v openssl &> /dev/null; then - OPENSSL_CMD="openssl" -fi +for p in /opt/homebrew/opt/openssl/bin/openssl /usr/local/opt/openssl/bin/openssl openssl; do + if command -v "$p" &>/dev/null; then + OPENSSL_CMD="$p" + break + fi +done if [ -z "$OPENSSL_CMD" ]; then - warn "OpenSSL 未找到,正在安装..." + info "通过 Homebrew 安装 OpenSSL..." brew install openssl OPENSSL_CMD="/opt/homebrew/opt/openssl/bin/openssl" success "OpenSSL 安装完成" else - success "OpenSSL: $($OPENSSL_CMD version)" + success "OpenSSL: $($OPENSSL_CMD version 2>/dev/null | head -1)" fi # Claude Code -if ! command -v claude &> /dev/null; then - warn "Claude Code 未安装,正在通过 npm 安装..." +if ! command -v claude &>/dev/null; then + info "通过 npm 安装 Claude Code..." npm i -g @anthropic-ai/claude-code success "Claude Code 安装完成" else @@ -115,82 +122,130 @@ fi # ============================================================ step 2 "检测网络代理" -# macOS 系统代理检测 -PROXY_DETECTED="" -if [ -n "$https_proxy" ] || [ -n "$HTTPS_PROXY" ]; then - PROXY_DETECTED="${HTTPS_PROXY:-$https_proxy}" - success "环境变量代理: $PROXY_DETECTED" -elif [ -n "$http_proxy" ] || [ -n "$HTTP_PROXY" ]; then - PROXY_DETECTED="${HTTP_PROXY:-$http_proxy}" - success "环境变量代理: $PROXY_DETECTED" -else - # 尝试从 macOS 网络设置检测 - SCUTIL_PROXY=$(scutil --proxy 2>/dev/null | grep -E "HTTPSPort|HTTPSProxy" || true) - if [ -n "$SCUTIL_PROXY" ]; then - PROXY_HOST=$(scutil --proxy 2>/dev/null | grep "HTTPSProxy" | awk '{print $3}') - PROXY_PORT=$(scutil --proxy 2>/dev/null | grep "HTTPSPort" | awk '{print $3}') - if [ -n "$PROXY_HOST" ] && [ "$PROXY_HOST" != "0" ]; then - PROXY_DETECTED="http://$PROXY_HOST:$PROXY_PORT" - export HTTPS_PROXY="$PROXY_DETECTED" - export HTTP_PROXY="$PROXY_DETECTED" - success "macOS 系统代理: $PROXY_DETECTED" - fi - fi - # 尝试常见端口 - if [ -z "$PROXY_DETECTED" ]; then - for PORT in 7890 7893 1087 1080 8118; do - if nc -z 127.0.0.1 $PORT 2>/dev/null; then - PROXY_DETECTED="http://127.0.0.1:$PORT" - export HTTPS_PROXY="$PROXY_DETECTED" - export HTTP_PROXY="$PROXY_DETECTED" - success "本地代理端口: $PROXY_DETECTED" - break - fi - done - fi -fi - -if [ -z "$PROXY_DETECTED" ]; then - warn "未检测到代理。如果在国内,Claude Code 可能无法启动。" - warn "请启动代理软件 (ClashX / Surge / V2Ray) 后重试。" -fi - -# NO_PROXY 设置 -export NO_PROXY="bww.letcareme.com,code.letcareme.com,localhost,127.0.0.1" +export NO_PROXY="bww.letcareme.com,code.letcareme.com,letcareme.com,localhost,127.0.0.1" export no_proxy="$NO_PROXY" -success "NO_PROXY 已设置: bww.letcareme.com,code.letcareme.com" -# ============================================================ -# Step 3: 克隆/更新引导仓库 -# ============================================================ -step 3 "同步引导仓库" +PROXY_FOUND="" -if [ -d "$BOOT_DIR/.git" ]; then - info "引导仓库已存在,正在更新..." - cd "$BOOT_DIR" - git pull --ff-only 2>/dev/null || git pull - success "引导仓库已更新" -else - info "正在克隆引导仓库..." - git clone "$GITEA_URL" "$BOOT_DIR" - cd "$BOOT_DIR" - success "引导仓库克隆完成" +# 环境变量 +if [ -n "$HTTPS_PROXY" ] || [ -n "$https_proxy" ]; then + PROXY_FOUND="${HTTPS_PROXY:-$https_proxy}" + success "环境变量代理: $PROXY_FOUND" fi -# ============================================================ -# Step 4: 运行安装脚本 -# ============================================================ -step 4 "执行安装" +# macOS 系统代理 +if [ -z "$PROXY_FOUND" ]; then + PROXY_HOST=$(scutil --proxy 2>/dev/null | grep "HTTPSProxy" | awk '{print $3}') + PROXY_PORT=$(scutil --proxy 2>/dev/null | grep "HTTPSPort" | awk '{print $3}') + if [ -n "$PROXY_HOST" ] && [ "$PROXY_HOST" != "0" ] && [ -n "$PROXY_PORT" ] && [ "$PROXY_PORT" != "0" ]; then + PROXY_FOUND="http://$PROXY_HOST:$PROXY_PORT" + export HTTPS_PROXY="$PROXY_FOUND" + export HTTP_PROXY="$PROXY_FOUND" + success "macOS 系统代理: $PROXY_FOUND" + fi +fi -if [ -f "$BOOT_DIR/install-mac.sh" ]; then - info "检测到 install-mac.sh,正在执行..." +# 常见端口扫描 (500ms 超时) +if [ -z "$PROXY_FOUND" ]; then + for PORT in 7890 7893 7891 1087 1080 8118; do + if nc -z -w1 127.0.0.1 $PORT 2>/dev/null; then + PROXY_FOUND="http://127.0.0.1:$PORT" + export HTTPS_PROXY="$PROXY_FOUND" + export HTTP_PROXY="$PROXY_FOUND" + success "本地代理端口: $PORT" + break + fi + done +fi + +if [ -z "$PROXY_FOUND" ]; then + warn "未检测到代理。在国内 Claude Code 可能无法启动。" + warn "请启动代理软件 (ClashX / Surge / V2Ray) 后重试。" + echo "" + read -p " 无代理继续? (y/n): " CONTINUE + if [ "$CONTINUE" != "y" ]; then exit 1; fi +fi + +success "NO_PROXY: bww.letcareme.com,code.letcareme.com" + +# ============================================================ +# Step 3: 克隆/更新配置仓库到 ~/.claude +# ============================================================ +step 3 "同步 Bookworm 配置" + +git config --global credential.helper osxkeychain 2>/dev/null || true + +if [ -d "$CLAUDE_DIR/.git" ]; then + info "配置仓库已存在, 更新..." + cd "$CLAUDE_DIR" && git pull --ff-only 2>/dev/null || git pull 2>/dev/null || true cd "$BOOT_DIR" - bash install-mac.sh + success "配置仓库已更新" +elif [ -f "$CLAUDE_DIR/CLAUDE.md" ]; then + warn "~/.claude 已存在但非 git 仓库, 备份后克隆..." + mv "$CLAUDE_DIR" "$CLAUDE_DIR.bak.$(date +%s)" + git clone --depth 1 "$CONFIG_REPO" "$CLAUDE_DIR" + success "配置仓库克隆完成 (旧目录已备份)" else - # 如果还没有 mac 专用脚本,提示用户 - warn "macOS 安装脚本尚未就绪。" - info "请联系管理员获取 install-mac.sh,或手动参考安装手册操作。" - info "安装手册: https://portable.bookwormweb.com/mac" + info "首次安装, 克隆配置仓库 (需输入 Gitea 密码)..." + mkdir -p "$(dirname "$CLAUDE_DIR")" + git clone --depth 1 "$CONFIG_REPO" "$CLAUDE_DIR" + success "配置仓库克隆完成" +fi + +# 创建本地运行时目录 +for d in debug sessions cache backups telemetry memory projects; do + mkdir -p "$CLAUDE_DIR/$d" 2>/dev/null +done + +# ============================================================ +# Step 4: 解密凭证 +# ============================================================ +step 4 "解密凭证" + +if [ -f "$SECRETS_ENC" ] && [ -n "$OPENSSL_CMD" ]; then + DECRYPTED="" + for attempt in 1 2 3; do + echo "" + read -rs -p " 输入主密码解密凭证 (第 $attempt/3 次): " PASSWORD + echo "" + DECRYPTED=$($OPENSSL_CMD enc -aes-256-cbc -d -pbkdf2 -iter 600000 -in "$SECRETS_ENC" -pass pass:"$PASSWORD" 2>/dev/null) || true + PASSWORD="" + if [ -n "$DECRYPTED" ]; then + while IFS= read -r line; do + [ -z "$line" ] && continue + key="${line%%=*}" + value="${line#*=}" + key=$(echo "$key" | tr -d ' ') + if [ -n "$key" ] && [ -n "$value" ]; then + export "$key=$value" + success "已注入: $key" + fi + done <<< "$DECRYPTED" + DECRYPTED="" + break + else + if [ $attempt -lt 3 ]; then + warn "密码错误, 剩余重试: $((3 - attempt)) 次" + else + fail "3 次密码均错误, 凭证未解密" + warn "可稍后手动配置 API Key" + fi + fi + done +else + if [ ! -f "$SECRETS_ENC" ]; then + warn "secrets.enc 不存在, 跳过凭证解密" + info "请联系管理员获取加密凭证文件" + fi +fi + +# 渲染 settings.json (替换占位符) +TEMPLATE_FILE="$CLAUDE_DIR/settings.template.json" +SETTINGS_FILE="$CLAUDE_DIR/settings.json" +if [ -f "$TEMPLATE_FILE" ]; then + CLAUDE_ROOT=$(echo "$CLAUDE_DIR" | sed 's/\\/\//g') + sed "s|{{CLAUDE_ROOT}}|$CLAUDE_ROOT|g; s|{{HOME}}|$HOME|g" "$TEMPLATE_FILE" > "$SETTINGS_FILE" + success "settings.json 已渲染" fi # ============================================================ @@ -198,20 +253,37 @@ fi # ============================================================ step 5 "配置终端快捷命令" -ZSHRC="$HOME/.zshrc" -ALIAS_MARKER="# Bookworm Portable aliases" +SHELL_RC="$HOME/.zshrc" +if [ -n "$BASH_VERSION" ] && [ -f "$HOME/.bashrc" ]; then + SHELL_RC="$HOME/.bashrc" +fi -if ! grep -q "$ALIAS_MARKER" "$ZSHRC" 2>/dev/null; then - cat >> "$ZSHRC" << 'ALIASES' +ALIAS_MARKER="# Bookworm Portable aliases" +if ! grep -q "$ALIAS_MARKER" "$SHELL_RC" 2>/dev/null; then + cat >> "$SHELL_RC" << 'ALIASES' # Bookworm Portable aliases -alias bookworm='cd ~/bookworm-boot && bash start-mac.sh' -alias bookworm-update='cd ~/bookworm-boot && bash install-mac.sh' -alias bookworm-stop='cd ~/bookworm-boot && bash stop-mac.sh' +alias bw='NO_PROXY="bww.letcareme.com,code.letcareme.com,localhost,127.0.0.1" claude' +alias bw-update='cd ~/bookworm-boot && git pull && cd ~/.claude && git pull && echo "Updated!"' ALIASES - success "已添加别名到 ~/.zshrc (bookworm / bookworm-update / bookworm-stop)" + success "已添加到 $SHELL_RC:" + info " bw -- 启动 Bookworm" + info " bw-update -- 更新 Bookworm" else - success "终端别名已配置" + # 更新旧别名 (bookworm → bw) + if grep -q "alias bookworm=" "$SHELL_RC" 2>/dev/null; then + # 删除旧别名块,然后追加新的 + sed -i '' '/# Bookworm Portable aliases/,/^$/d' "$SHELL_RC" 2>/dev/null || true + cat >> "$SHELL_RC" << 'ALIASES' + +# Bookworm Portable aliases +alias bw='NO_PROXY="bww.letcareme.com,code.letcareme.com,localhost,127.0.0.1" claude' +alias bw-update='cd ~/bookworm-boot && git pull && cd ~/.claude && git pull && echo "Updated!"' +ALIASES + success "终端别名已更新 (bookworm → bw)" + else + success "终端别名已配置" + fi fi # ============================================================ @@ -220,13 +292,28 @@ fi step 6 "安装完成" echo "" -echo -e "${GREEN}============================================================${NC}" -echo -e "${GREEN} Bookworm Portable for macOS 安装完成!${NC}" -echo -e "${GREEN}============================================================${NC}" +echo -e "${GREEN} ============================================================${NC}" +echo -e "${GREEN} Bookworm Smart Assistant for macOS 安装完成!${NC}" +echo -e "${GREEN} ============================================================${NC}" echo "" -echo -e " ${BOLD}日常启动:${NC} 在终端输入 ${CYAN}bookworm${NC}" -echo -e " ${BOLD}同步更新:${NC} 在终端输入 ${CYAN}bookworm-update${NC}" -echo -e " ${BOLD}卸载清理:${NC} 在终端输入 ${CYAN}bookworm-stop${NC}" +echo -e " 已安装:" +echo -e " ${GREEN}[v]${NC} Homebrew ${GREEN}[v]${NC} Node.js $(node -v 2>/dev/null)" +echo -e " ${GREEN}[v]${NC} Git ${GREEN}[v]${NC} OpenSSL" +echo -e " ${GREEN}[v]${NC} Claude Code ${GREEN}[v]${NC} Bookworm (92 Skills)" echo "" -echo -e " ${BLUE}安装手册:${NC} https://portable.bookwormweb.com/mac" +echo -e " ${BOLD}启动方式:${NC}" +echo -e " 终端输入: ${CYAN}bw${NC}" echo "" +echo -e " ${BOLD}更新:${NC}" +echo -e " 终端输入: ${CYAN}bw-update${NC}" +echo "" + +# 询问是否立即启动 +read -p " 立即启动 Bookworm? (y/n): " START_NOW +if [ "$START_NOW" = "y" ] || [ "$START_NOW" = "Y" ]; then + echo "" + info "正在启动 Claude Code..." + cd "$HOME" + export NO_PROXY="bww.letcareme.com,code.letcareme.com,letcareme.com,localhost,127.0.0.1" + exec claude +fi diff --git a/README.txt b/README.txt index 3740988..b316869 100644 --- a/README.txt +++ b/README.txt @@ -6,8 +6,8 @@ deploy-gitea.sh ECS Gitea 部署 (服务端,执行一次) prepare-repo.ps1 仓库准备 (本机执行一次) encrypt-secrets.ps1 凭证加密 (本机执行一次) - settings.template.json settings.json 模板 settings.local.template.json settings.local.json 模板 (权限白名单) + (settings.template.json 已由 build-portable.js 管理,存于 config 仓库) install.ps1 安装/启动 (目标机执行) stop.ps1 清理/卸载 (目标机执行) diff --git a/generate-integrity.ps1 b/generate-integrity.ps1 index 77e6ffc..98a7506 100644 --- a/generate-integrity.ps1 +++ b/generate-integrity.ps1 @@ -25,7 +25,7 @@ $patterns = @( "hooks/lib/*.js", "scripts/paths.config.js", "CLAUDE.md", - "settings.template.json", + "settings.json", "settings.local.template.json" ) diff --git a/guide-mac.html b/guide-mac.html index 695c820..9f13633 100644 --- a/guide-mac.html +++ b/guide-mac.html @@ -439,9 +439,9 @@
bash install-mac.sh
+ bash Bookworm-Setup.sh
如果提示权限不足:chmod +x install-mac.sh && ./install-mac.sh
如果提示权限不足:chmod +x Bookworm-Setup.sh && ./Bookworm-Setup.sh
脚本会显示步骤进度 [1/8] 到 [8/8],自动完成:
安装脚本已自动添加别名到 ~/.zshrc,直接在终端输入:
bookworm # 快速启动
-bookworm-update # 同步更新后启动
+ bw # 快速启动
+bw-update # 同步更新
cd ~/bookworm-boot && bash start-mac.shbwcd ~/bookworm-boot && bash install-mac.shbw-updatebookworm-update 即可同步。
+ 说明管理员更新了 Skills 或 Hooks。执行 bw-update 即可同步。
bash stop-mac.shrm -rf ~/.claudebash stop-mac.sh --restorerm -rf ~/.claude ~/bookworm-bootbash stop-mac.sh --restore --deeprm -rf ~/.claude ~/bookworm-boot && sed -i '' '/Bookworm Portable/,+2d' ~/.zshrc && git credential-osxkeychain erase <<< "host=code.letcareme.com"cd ~/bookworm-boot && bash stop-mac.sh --restore --deep
+ 执行深度清理命令,确保不留下任何凭证或配置
或在代理软件中将 *.letcareme.com 加入直连规则。
原因:API 凭证是进程级环境变量,只在安装脚本启动的进程中有效。
解决:不要直接运行 claude,必须通过以下方式启动:
bookworm(推荐)cd ~/bookworm-boot && bash start-mac.shbw(推荐)cd ~/bookworm-boot && bash Bookworm-Setup.sh| 操作 | 快捷方式 | 完整命令 |
|---|---|---|
| 首次安装 | git clone + bash install-mac.sh | cd ~/bookworm-boot && bash install-mac.sh |
| 快速启动 | bookworm | cd ~/bookworm-boot && bash start-mac.sh |
| 同步更新 | bookworm-update | cd ~/bookworm-boot && bash install-mac.sh |
| 基础清理 | cd ~/bookworm-boot && bash stop-mac.sh | |
| 完整恢复 | cd ~/bookworm-boot && bash stop-mac.sh --restore | |
| 深度清理 | cd ~/bookworm-boot && bash stop-mac.sh --restore --deep | |
| 首次安装 | git clone + bash Bookworm-Setup.sh | cd ~/bookworm-boot && bash Bookworm-Setup.sh |
| 快速启动 | bw | NO_PROXY="bww.letcareme.com,code.letcareme.com,localhost,127.0.0.1" claude |
| 同步更新 | bw-update | cd ~/bookworm-boot && git pull && cd ~/.claude && git pull |
| 基础清理 | rm -rf ~/.claude | |
| 完整恢复 | rm -rf ~/.claude ~/bookworm-boot | |
| 深度清理 | rm -rf ~/.claude ~/bookworm-boot && sed -i '' '/Bookworm/,+2d' ~/.zshrc | |