diff --git a/auto-setup.ps1 b/auto-setup.ps1 index 57395a6..f0f886d 100644 --- a/auto-setup.ps1 +++ b/auto-setup.ps1 @@ -16,7 +16,7 @@ param( $ErrorActionPreference = "Stop" # ─── 版本号 (每次更新递增, build.ps1 自动读取) ────── -$BWVersion = "2.1.0" +$BWVersion = "2.2.0" # ─── B4: 单实例保护 (防止双击两次导致竞态) ───────── $mutexCreated = $false @@ -881,14 +881,39 @@ Log-Phase 3 "同步 Bookworm 配置" git config --global credential.helper manager 2>$null # 克隆/更新 config 仓库 (.claude/) — 使用 Run-CmdWithUI 防止 UI 冻结 +# 辅助函数: clone 后缓存凭证到 Windows Credential Manager +function Cache-GitCredentials($credObj) { + if (-not $credObj) { return } + try { + $approveInput = "protocol=https`nhost=code.letcareme.com`nusername=$($credObj.User)`npassword=$($credObj.Pass)`n`n" + $approveInput | & git credential approve 2>$null + Bw-Log "OK" "Gitea 凭证已缓存到 Windows Credential Manager" + } catch { Bw-Log "WARN" "凭证缓存失败: $_" } +} + if (Test-Path (Join-Path $ClaudeDir ".git")) { Log-Info "配置仓库已存在, 更新中..." + # 设置 git 身份 (auto-resolve commit 需要) + & git -C $ClaudeDir config user.email "bookworm@auto.local" 2>$null + & git -C $ClaudeDir config user.name "Bookworm" 2>$null try { - Run-CmdWithUI "git" @("-C", $ClaudeDir, "stash") "git stash" 15000 | Out-Null - $r = Run-CmdWithUI "git" @("-C", $ClaudeDir, "pull", "--rebase") "同步配置仓库" 120000 - if ($r.OK) { Log-OK "配置仓库已更新" } else { Log-Warn "git pull 失败, 使用本地版本" } - Run-CmdWithUI "git" @("-C", $ClaudeDir, "stash", "pop") "git stash pop" 15000 | Out-Null - } catch { Log-Warn "git pull 失败, 使用本地版本" } + # 强制清除冲突状态 (运行时文件不重要, Phase 5 会重新渲染) + & git -C $ClaudeDir reset --hard HEAD 2>&1 | Out-Null + $r = Run-CmdWithUI "git" @("-C", $ClaudeDir, "pull", "--rebase", "--autostash") "同步配置仓库" 120000 + if ($r.OK) { + Log-OK "配置仓库已更新" + } else { + # pull 失败可能是认证问题, 尝试重新输入凭证 + Log-Warn "git pull 失败, 尝试重新认证..." + $cred = Show-GiteaCredentialDialog + if ($cred) { + Cache-GitCredentials $cred + $r2 = Run-CmdWithUI "git" @("-C", $ClaudeDir, "pull", "--rebase", "--autostash") "重试同步" 120000 + if ($r2.OK) { Log-OK "配置仓库已更新 (重新认证成功)" } + else { Log-Warn "git pull 仍失败, 使用本地版本" } + } else { Log-Warn "用户取消认证, 使用本地版本" } + } + } catch { Log-Warn "git pull 异常: $_, 使用本地版本" } } elseif (Test-Path $ClaudeDir) { Log-Info "备份现有 .claude/ 并克隆..." @@ -900,6 +925,7 @@ elseif (Test-Path $ClaudeDir) { $r = Run-CmdWithUI "git" @("clone", "--depth", "1", $cloneUrl, $ClaudeDir) "克隆配置仓库" 180000 if (Test-Path (Join-Path $ClaudeDir "CLAUDE.md")) { Log-OK "配置仓库克隆成功 (旧目录已备份)" + Cache-GitCredentials $cred } else { Log-Fail "克隆失败" if (Test-Path $BackupDir) { Rename-Item $BackupDir $ClaudeDir } @@ -914,6 +940,7 @@ else { $r = Run-CmdWithUI "git" @("clone", "--depth", "1", $cloneUrl, $ClaudeDir) "克隆配置仓库" 180000 if (Test-Path (Join-Path $ClaudeDir "CLAUDE.md")) { Log-OK "配置仓库克隆成功" + Cache-GitCredentials $cred } else { Log-Fail "克隆失败" Show-MsgBox "配置仓库克隆失败。`n请检查网络连接和 Gitea 账号。" "克隆失败" "OK" "Error" @@ -1103,18 +1130,63 @@ if (Test-Path $templateFile) { } # ── ~/.claude.json (Claude Code v2.1+ MCP 服务器配置的正确位置) ── - # 直接调用 config 仓库里的 inject-mcp.js (包含全部 22 个 portable MCP) + # 优先用 config 仓库的 inject-mcp.js, 如果 git pull 失败则内嵌 fallback $injectScript = Join-Path $ClaudeDir "inject-mcp.js" - try { - if (Test-Path $injectScript) { + $mcpInjected = $false + # 方案 A: 调用 config 仓库里的 inject-mcp.js + if (Test-Path $injectScript) { + try { $nodeOut = & node $injectScript 2>&1 $firstLine = ($nodeOut | Select-Object -First 1).ToString().Trim() Log-OK $firstLine - } else { - Bw-Log "WARN" "inject-mcp.js 不存在, 跳过 MCP 注入" - } - } catch { - Bw-Log "WARN" ".claude.json 生成失败: $_" + $mcpInjected = $true + } catch { Bw-Log "WARN" "inject-mcp.js 执行失败: $_" } + } + # 方案 B: 内嵌 fallback (git pull 失败时 inject-mcp.js 不存在) + if (-not $mcpInjected) { + Log-Info "inject-mcp.js 不可用, 使用内嵌 MCP 注入..." + $fallbackJs = Join-Path $env:TEMP "bw-mcp-fallback.js" + try { + $fbLines = @( + 'var fs=require("fs"),p=require("path");' + 'var H=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.cmd",Y="--yes";' + 'var 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.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)");' + ) + $fbLines -join "`n" | Set-Content $fallbackJs -Encoding UTF8 + $nodeOut = & node $fallbackJs 2>&1 + $firstLine = ($nodeOut | Select-Object -First 1).ToString().Trim() + Remove-Item $fallbackJs -Force -ErrorAction SilentlyContinue + Log-OK $firstLine + } catch { Bw-Log "WARN" "MCP fallback 注入失败: $_" } } } else { Log-Warn "settings.template.json 不存在, 跳过渲染"