feat: Mac v2.2.4 - MCP inject + git robustness + credential whitelist

This commit is contained in:
bookworm 2026-04-13 22:32:02 +08:00
parent b2d5e4aa7b
commit 9c595fae86
2 changed files with 78 additions and 13 deletions

View File

@ -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}"

View File

@ -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"