bookworm-boot/Bookworm-Setup.sh

552 lines
19 KiB
Bash
Raw Normal View History

#!/bin/bash
# ============================================================
# Bookworm Portable - macOS Setup (从 boot 仓库内运行)
2026-04-13 23:19:28 +08:00
# Version: 2.2.4
#
# 用法: cd ~/bookworm-boot && bash Bookworm-Setup.sh
#
# 前提: 已 git clone bookworm-boot 到本地
# 功能: 检查依赖 → 代理检测 → 克隆配置 → 解密凭证 → 配置别名
# ============================================================
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
BOLD='\033[1m'
# 配置
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 " | |_) | (_) | (_) | < \\ V V / (_) | | | | | | | |"
echo " |____/ \\___/ \\___/|_|\\_\\ \\_/\\_/ \\___/|_| |_| |_| |_|"
echo ""
echo -e " ${BOLD}Portable macOS Setup v2.3.1${NC}"
echo -e " ${BLUE}92 Skills | 18 Agents | 34 Hooks${NC}"
echo -e "${NC}"
}
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}"; }
# ============================================================
# Banner
# ============================================================
banner
# ============================================================
# Step 1: 检查并安装依赖
# ============================================================
step 1 "检查依赖软件"
# Homebrew
if ! command -v brew &>/dev/null; then
warn "Homebrew 未安装, 正在安装 (可能需要输入系统密码)..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
if [ -f /opt/homebrew/bin/brew ]; then
eval "$(/opt/homebrew/bin/brew shellenv)"
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 | awk '{print $2}')"
fi
# Node.js
if ! command -v node &>/dev/null; then
info "通过 Homebrew 安装 Node.js..."
brew install node
success "Node.js $(node -v) 安装完成"
else
success "Node.js $(node -v)"
fi
# Git
if ! command -v git &>/dev/null; then
info "通过 Homebrew 安装 Git..."
brew install git
success "Git $(git --version | awk '{print $3}') 安装完成"
else
success "Git $(git --version | awk '{print $3}')"
fi
# OpenSSL
OPENSSL_CMD=""
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
info "通过 Homebrew 安装 OpenSSL..."
brew install openssl
OPENSSL_CMD="/opt/homebrew/opt/openssl/bin/openssl"
success "OpenSSL 安装完成"
else
success "OpenSSL: $($OPENSSL_CMD version 2>/dev/null | head -1)"
fi
# Claude Code
if ! command -v claude &>/dev/null; then
info "通过 npm 安装 Claude Code..."
npm i -g @anthropic-ai/claude-code
success "Claude Code 安装完成"
else
success "Claude Code $(claude --version 2>/dev/null || echo 'installed')"
fi
# ============================================================
# Step 2: 检测代理
# ============================================================
step 2 "检测网络代理"
export NO_PROXY="bww.letcareme.com,code.letcareme.com,letcareme.com,localhost,127.0.0.1"
export no_proxy="$NO_PROXY"
PROXY_FOUND=""
# 环境变量
if [ -n "$HTTPS_PROXY" ] || [ -n "$https_proxy" ]; then
PROXY_FOUND="${HTTPS_PROXY:-$https_proxy}"
success "环境变量代理: $PROXY_FOUND"
fi
# 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
# 常见端口扫描 (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 身份 (auto-resolve 需要)
git config user.email "bookworm@auto.local" 2>/dev/null
git config user.name "Bookworm" 2>/dev/null
# 清除冲突状态 (运行时文件不重要, 后续会重新渲染)
git reset --hard HEAD 2>/dev/null || true
if git pull --rebase --autostash 2>/dev/null; then
success "配置仓库已更新"
else
warn "git pull 失败, 使用本地版本"
fi
cd "$BOOT_DIR"
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
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: 解密凭证 (含 Keychain 本日免密)
# ============================================================
step 4 "解密凭证"
# Keychain 缓存相关
KEYCHAIN_SERVICE="bookworm-secrets"
KEYCHAIN_ACCOUNT="$(whoami)"
CACHE_LOADED=false
# 尝试从 Keychain 加载缓存
load_cached_secrets() {
local cached
cached=$(security find-generic-password -s "$KEYCHAIN_SERVICE" -a "$KEYCHAIN_ACCOUNT" -w 2>/dev/null) || return 1
# 检查是否过期 (缓存格式: EXPIRY=ISO日期\nKEY=VALUE\n...)
local expiry
expiry=$(echo "$cached" | head -1)
local expiry_date="${expiry#EXPIRY=}"
local today
today=$(date +%Y-%m-%d)
if [ "$expiry_date" != "$today" ]; then
# 已过期,删除缓存
security delete-generic-password -s "$KEYCHAIN_SERVICE" -a "$KEYCHAIN_ACCOUNT" 2>/dev/null || true
return 1
fi
# 加载环境变量 (跳过 EXPIRY 行)
local count=0
while IFS= read -r line; do
[ -z "$line" ] && continue
[[ "$line" == EXPIRY=* ]] && continue
local key="${line%%=*}"
local value="${line#*=}"
key=$(echo "$key" | tr -d ' ')
if [ -n "$key" ] && [ -n "$value" ]; then
export "$key=$value"
count=$((count + 1))
fi
done <<< "$cached"
if [ $count -gt 0 ] && [ -n "$ANTHROPIC_API_KEY" ]; then
success "从 Keychain 缓存加载 $count 个凭证 (免密)"
CACHE_LOADED=true
return 0
fi
return 1
}
# 保存凭证到 Keychain
save_secrets_to_cache() {
local today
today=$(date +%Y-%m-%d)
local data="EXPIRY=$today"
local env_keys="ANTHROPIC_API_KEY ANTHROPIC_BASE_URL GITHUB_PERSONAL_ACCESS_TOKEN SLACK_BOT_TOKEN ATLASSIAN_API_TOKEN BROWSERBASE_API_KEY FIRECRAWL_API_KEY GEMINI_API_KEY"
for k in $env_keys; do
local v="${!k}"
if [ -n "$v" ]; then
data="$data
$k=$v"
fi
done
security add-generic-password -s "$KEYCHAIN_SERVICE" -a "$KEYCHAIN_ACCOUNT" -w "$data" -U 2>/dev/null && \
success "凭证已缓存至今日 23:59 (下次免密)" || \
warn "Keychain 缓存失败 (不影响使用)"
}
# 解密工具: 优先 node crypto-helper.js (BWENC1 格式), 回退 openssl
CRYPTO_HELPER="$BOOT_DIR/crypto-helper.js"
_decrypt_secrets() {
local pass="$1" enc="$2"
if command -v node &>/dev/null && [ -f "$CRYPTO_HELPER" ]; then
node "$CRYPTO_HELPER" decrypt "$pass" "$enc" 2>/dev/null
elif [ -n "$OPENSSL_CMD" ]; then
$OPENSSL_CMD enc -aes-256-cbc -d -pbkdf2 -iter 600000 -in "$enc" -pass pass:"$pass" 2>/dev/null
else
return 1
fi
}
# 按 token 前8位定位 .enc 文件 (多用户模式),回退 secrets.enc
resolve_secrets_file() {
local token="$1"
local file_id="${token:0:8}"
local per_user="$BOOT_DIR/secrets-${file_id}.enc"
if [ -f "$per_user" ]; then
echo "$per_user"
elif [ -f "$SECRETS_ENC" ]; then
echo "$SECRETS_ENC"
else
echo ""
fi
}
# 解析授权码: BW-YYYYMMDD-TOKEN (24位Hex)
# 返回: 小写 token (成功) | "EXPIRED" (已过期) | "" (格式错误)
parse_authcode() {
local code
code=$(echo "$1" | tr '[:lower:]' '[:upper:]' | tr -d ' ')
# 格式校验: BW-8位数字-24位Hex
if [[ ! "$code" =~ ^BW-([0-9]{8})-([A-F0-9]{24})$ ]]; then
echo ""
return
fi
local expiry_str="${BASH_REMATCH[1]}"
local token_upper="${BASH_REMATCH[2]}"
local today
today=$(date +%Y%m%d)
if [ "$expiry_str" -lt "$today" ]; then
echo "EXPIRED"
return
fi
echo "$token_upper" | tr '[:upper:]' '[:lower:]' # 兼容 bash 3.2 (macOS 默认)
}
# 先尝试缓存
if load_cached_secrets 2>/dev/null; then
: # 缓存加载成功
else
# 优先级 3: 调用 change-key.js 验证+持久化 (stdin 管道, 无 argv 泄露)
CHANGE_KEY_JS="$CLAUDE_DIR/change-key.js"
if [ -f "$CHANGE_KEY_JS" ] && command -v node &>/dev/null; then
echo ""
info "配置中转站凭证 (https://bww.letcareme.com)"
for attempt in 1 2 3; do
echo ""
read -rs -p " 粘贴凭证 (第 $attempt/3 次, 输入不显示, 留空跳过): " UCRED
echo ""
[ -z "$UCRED" ] && { warn "已跳过"; break; }
if printf '%s' "$UCRED" | node "$CHANGE_KEY_JS"; then
UCRED=""
success "凭证已验证并持久化"
echo ""
info "换凭证方式:"
info " 1. 重跑安装器"
info " 2. bash ~/.claude/change-key.sh"
info " 3. Claude Code 里: /change-key"
break
else
UCRED=""
[ $attempt -lt 3 ] && warn "验证失败, 剩余 $((3-attempt))" || fail "3 次失败"
fi
done
fi
fi
# 优先级 4: 授权码模式 (向后兼容旧用户)
if [ -z "$ANTHROPIC_API_KEY" ] && { [ -f "$SECRETS_ENC" ] || ls "$BOOT_DIR"/secrets-*.enc 2>/dev/null | head -1 | grep -q .; }; then
DECRYPTED=""
valid_attempts=0
total_attempts=0
while [ $valid_attempts -lt 3 ] && [ $total_attempts -lt 10 ]; do
echo ""
read -p " 输入授权码 (BW-YYYYMMDD-XXXXXX, 第 $((valid_attempts+1))/3 次): " AUTH_CODE
total_attempts=$((total_attempts + 1))
TOKEN=$(parse_authcode "$AUTH_CODE")
AUTH_CODE=""
if [ "$TOKEN" = "EXPIRED" ]; then
warn "授权码已过期, 请联系管理员获取新授权码"
continue # 不消耗有效次数
elif [ -z "$TOKEN" ]; then
warn "授权码格式错误 (格式: BW-YYYYMMDD-XXXXXXXXXXXXXXXXXXXXXXXX)"
continue # 不消耗有效次数
fi
valid_attempts=$((valid_attempts + 1))
ENC_FILE=$(resolve_secrets_file "$TOKEN")
if [ -z "$ENC_FILE" ]; then
FILE_ID="${TOKEN:0:8}"
warn "未找到对应凭证文件 (secrets-${FILE_ID}.enc / secrets.enc)"
warn "请联系管理员确认已推送对应文件,然后重新运行安装器"
TOKEN=""
continue
fi
DECRYPTED=$(_decrypt_secrets "$TOKEN" "$ENC_FILE") || true
TOKEN=""
if [ -n "$DECRYPTED" ]; then
# 白名单校验 (与 Windows 版对齐)
ALLOWED_KEYS="ANTHROPIC_API_KEY ANTHROPIC_BASE_URL GITHUB_PERSONAL_ACCESS_TOKEN SLACK_BOT_TOKEN ATLASSIAN_API_TOKEN BROWSERBASE_API_KEY FIRECRAWL_API_KEY GEMINI_API_KEY"
while IFS= read -r line; do
[ -z "$line" ] && continue
key="${line%%=*}"
value="${line#*=}"
key=$(echo "$key" | tr -d ' ')
if [ -n "$key" ] && [ -n "$value" ]; then
# 白名单 + 长度校验
if echo "$ALLOWED_KEYS" | grep -qw "$key" && [ ${#value} -lt 512 ]; then
export "$key=$value"
success "已注入: $key"
else
warn "跳过未知 key: $key"
fi
fi
done <<< "$DECRYPTED"
DECRYPTED=""
# 自动缓存 (不再询问, 与 Windows 版对齐)
save_secrets_to_cache
break
else
if [ $valid_attempts -lt 3 ]; then
warn "授权码无效 (解密失败), 剩余重试: $((3 - valid_attempts))"
else
fail "3 次授权码均无效, 凭证未解密"
warn "可稍后手动配置 API Key"
fi
fi
done
else
if [ ! -f "$SECRETS_ENC" ]; then
warn "secrets.enc 不存在, 跳过凭证解密"
info "请联系管理员获取授权码"
fi
fi
# ── MCP 注入到 ~/.claude.json (Claude Code v2.1+ 正确位置) ──
INJECT_SCRIPT="$CLAUDE_DIR/inject-mcp.js"
MCP_INJECTED=false
# 方案 A: 调用 config 仓库里的 inject-mcp.js
if [ -f "$INJECT_SCRIPT" ] && command -v node &>/dev/null; then
MCP_OUT=$(node "$INJECT_SCRIPT" 2>&1) && {
success "$MCP_OUT"
MCP_INJECTED=true
} || warn "inject-mcp.js 执行失败"
fi
# 方案 B: 内嵌 fallback (git pull 失败时)
if [ "$MCP_INJECTED" = false ] && command -v node &>/dev/null; then
info "inject-mcp.js 不可用, 使用内嵌 MCP 注入..."
FALLBACK_JS=$(mktemp /tmp/bw-mcp-XXXXXX.js)
cat > "$FALLBACK_JS" << 'MCPEOF'
var fs=require("fs"),p=require("path");
var H=process.env.HOME||process.env.USERPROFILE;
var f=p.join(H,".claude.json");
var d={};try{d=JSON.parse(fs.readFileSync(f,"utf8"))}catch(e){}
var N="npx",Y="--yes",S={};
S.context7={command:N,args:[Y,"@upstash/context7-mcp@2.1.1"],type:"stdio"};
S.playwright={command:N,args:[Y,"@playwright/mcp@0.0.68","--headless"],type:"stdio"};
S["session-continuity"]={command:N,args:[Y,"claude-session-continuity-mcp@1.13.0"],type:"stdio"};
S["browser-mcp"]={command:N,args:[Y,"@browsermcp/mcp@latest"],type:"stdio"};
S["desktop-commander"]={command:N,args:[Y,"@wonderwhy-er/desktop-commander@latest"],type:"stdio"};
S["chrome-devtools"]={command:N,args:[Y,"chrome-devtools-mcp@0.18.1"],type:"stdio"};
S.github={command:N,args:[Y,"@modelcontextprotocol/server-github"],type:"stdio"};
S.slack={command:N,args:[Y,"@modelcontextprotocol/server-slack"],type:"stdio"};
S.firecrawl={command:N,args:[Y,"firecrawl-mcp"],type:"stdio"};
S["mcp-image"]={command:N,args:[Y,"mcp-image"],type:"stdio"};
S["google-drive"]={command:N,args:[Y,"@piotr-agier/google-drive-mcp"],type:"stdio"};
S.browserbase={command:N,args:[Y,"@anthropic-ai/browserbase-mcp"],type:"stdio"};
S.notebooklm={command:N,args:[Y,"notebooklm-mcp@latest"],type:"stdio"};
S.cloudflare={command:N,args:[Y,"mcp-remote","https://docs.mcp.cloudflare.com/sse"],type:"stdio"};
S.mobile={command:N,args:[Y,"@mobilenext/mobile-mcp@0.0.35"],type:"stdio"};
var K="@modelcontextprotocol/server-sequential-thinking";
S["sequential-thinking"]={command:N,args:[Y,K+"@2025.12.18"],type:"stdio"};
S.linear={type:"http",url:"https://mcp.linear.app/mcp"};
S.supabase={type:"http",url:"https://mcp.supabase.com/mcp?project_ref=oepmihbtoylosbsxlmfo"};
S.figma={type:"http",url:"https://mcp.figma.com/mcp"};
S["windows-mcp"]={command:"uvx",args:["--python","3.13","windows-mcp"],type:"stdio"};
S.atlassian={command:"uvx",args:["mcp-atlassian"],type:"stdio"};
S["computer-control-mcp"]={command:"uvx",args:["computer-control-mcp@latest"],type:"stdio"};
d.mcpServers=S;
fs.writeFileSync(f,JSON.stringify(d,null,2));
console.log("OK: "+Object.keys(S).length+" MCP servers (fallback)");
MCPEOF
MCP_OUT=$(node "$FALLBACK_JS" 2>&1) && success "$MCP_OUT" || warn "MCP fallback 注入失败"
rm -f "$FALLBACK_JS"
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')
SHELL_BIN="${SHELL:-/bin/zsh}"
sed "s|{{CLAUDE_ROOT}}|$CLAUDE_ROOT|g; s|{{HOME}}|$HOME|g; s|{{PWSH_PATH}}|$SHELL_BIN|g" "$TEMPLATE_FILE" > "$SETTINGS_FILE"
success "settings.json 已渲染 (SHELL=$SHELL_BIN)"
fi
# ============================================================
# Step 5: 配置终端别名
# ============================================================
step 5 "配置终端快捷命令"
SHELL_RC="$HOME/.zshrc"
if [ -n "$BASH_VERSION" ] && [ -f "$HOME/.bashrc" ]; then
SHELL_RC="$HOME/.bashrc"
fi
ALIAS_MARKER="# Bookworm Portable aliases"
if ! grep -q "$ALIAS_MARKER" "$SHELL_RC" 2>/dev/null; then
cat >> "$SHELL_RC" << 'ALIASES'
# Bookworm Portable aliases
alias bw='NO_PROXY="bww.letcareme.com,code.letcareme.com,localhost,127.0.0.1" claude --dangerously-skip-permissions'
alias bw-update='cd ~/bookworm-boot && git pull && cd ~/.claude && git pull && echo "Updated!"'
ALIASES
success "已添加到 $SHELL_RC:"
info " bw -- 启动 Bookworm"
info " bw-update -- 更新 Bookworm"
else
# 更新旧别名 (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 --dangerously-skip-permissions'
alias bw-update='cd ~/bookworm-boot && git pull && cd ~/.claude && git pull && echo "Updated!"'
ALIASES
success "终端别名已更新 (bookworm → bw)"
else
success "终端别名已配置"
fi
fi
# ============================================================
# Step 6: 完成
# ============================================================
step 6 "安装完成"
echo ""
echo -e "${GREEN} ============================================================${NC}"
echo -e "${GREEN} Bookworm Smart Assistant for macOS 安装完成!${NC}"
echo -e "${GREEN} ============================================================${NC}"
echo ""
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 " ${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 --dangerously-skip-permissions
fi