From 9c595fae86d38966a66d65e24ea023baf5ed8262 Mon Sep 17 00:00:00 2001 From: bookworm Date: Mon, 13 Apr 2026 22:32:02 +0800 Subject: [PATCH] feat: Mac v2.2.4 - MCP inject + git robustness + credential whitelist --- Bookworm-OneClick-Mac.sh | 4 +- Bookworm-Setup.sh | 87 +++++++++++++++++++++++++++++++++++----- 2 files changed, 78 insertions(+), 13 deletions(-) diff --git a/Bookworm-OneClick-Mac.sh b/Bookworm-OneClick-Mac.sh index 6a55d13..251e1d2 100644 --- a/Bookworm-OneClick-Mac.sh +++ b/Bookworm-OneClick-Mac.sh @@ -1,6 +1,6 @@ #!/bin/bash # ============================================================ -# Bookworm Smart Assistant - macOS 全自动安装 v2.0 +# Bookworm Smart Assistant - macOS 全自动安装 v2.2.4 # # 用法 (任选一种): # 方式1: 下载后运行 @@ -39,7 +39,7 @@ echo " | _ \\ / _ \\ / _ \\| |/ /\\ \\ /\\ / / _ \\| '__| '\`_ \` _ \\" echo " | |_) | (_) | (_) | < \\ V V / (_) | | | | | | | |" echo " |____/ \\___/ \\___/|_|\\_\\ \\_/\\_/ \\___/|_| |_| |_| |_|" echo "" -echo -e " ${BOLD}全自动安装 v2.0 — macOS${NC}" +echo -e " ${BOLD}全自动安装 v2.2.4 — macOS${NC}" echo -e " ${BLUE}92 Skills | 18 Agents | 34 Hooks${NC}" echo -e "${NC}" diff --git a/Bookworm-Setup.sh b/Bookworm-Setup.sh index 5712c64..e0d12ad 100644 --- a/Bookworm-Setup.sh +++ b/Bookworm-Setup.sh @@ -35,7 +35,7 @@ banner() { echo " | |_) | (_) | (_) | < \\ V V / (_) | | | | | | | |" echo " |____/ \\___/ \\___/|_|\\_\\ \\_/\\_/ \\___/|_| |_| |_| |_|" echo "" - echo -e " ${BOLD}Portable macOS Setup v1.5${NC}" + echo -e " ${BOLD}Portable macOS Setup v2.2.4${NC}" echo -e " ${BLUE}92 Skills | 18 Agents | 34 Hooks${NC}" echo -e "${NC}" } @@ -177,9 +177,18 @@ 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 "$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" - success "配置仓库已更新" elif [ -f "$CLAUDE_DIR/CLAUDE.md" ]; then warn "~/.claude 已存在但非 git 仓库, 备份后克隆..." mv "$CLAUDE_DIR" "$CLAUDE_DIR.bak.$(date +%s)" @@ -341,24 +350,27 @@ elif [ -f "$SECRETS_ENC" ] || ls "$BOOT_DIR"/secrets-*.enc 2>/dev/null | head -1 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 - export "$key=$value" - success "已注入: $key" + # 白名单 + 长度校验 + 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="" - # 询问是否缓存 - echo "" - read -p " 今日内免密启动? (y/n): " CACHE_CHOICE - if [ "$CACHE_CHOICE" = "y" ] || [ "$CACHE_CHOICE" = "Y" ]; then - save_secrets_to_cache - fi + # 自动缓存 (不再询问, 与 Windows 版对齐) + save_secrets_to_cache break else if [ $valid_attempts -lt 3 ]; then @@ -376,6 +388,59 @@ else 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"