feat: UX v1.3 - bat launchers + password retry + update detection

This commit is contained in:
leesu 2026-04-01 20:50:12 +08:00
parent dc05d25230
commit 2a6bb08abb
4 changed files with 125 additions and 50 deletions

View File

@ -68,42 +68,51 @@ function Decrypt-Secrets {
return return
} }
$password = Read-Host " 输入主密码解密凭证" -AsSecureString $maxRetries = 3
$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password) for ($attempt = 1; $attempt -le $maxRetries; $attempt++) {
$plainPwd = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr) $label = if ($attempt -gt 1) { " 重新输入主密码 (第 $attempt/$maxRetries 次)" } else { " 输入主密码解密凭证" }
$password = Read-Host $label -AsSecureString
$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)
$plainPwd = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)
try { $prevEAP = $ErrorActionPreference
# 通过 stdin 传入密码,避免进程列表泄露 $ErrorActionPreference = "Continue"
$decrypted = $plainPwd | & $opensslCmd enc -aes-256-cbc -d -pbkdf2 -iter 600000 -in $SecretsEnc -pass stdin 2>&1 $decrypted = $plainPwd | & $opensslCmd enc -aes-256-cbc -d -pbkdf2 -iter 600000 -in $SecretsEnc -pass stdin 2>&1
if ($LASTEXITCODE -ne 0) { $decExit = $LASTEXITCODE
throw "解密失败,密码错误?" $ErrorActionPreference = $prevEAP
}
# 使用 IndexOf 正确分割 key=value (value 中可能含 = 号) # 清除内存中的密码
$decrypted -split "`n" | ForEach-Object {
$line = $_.Trim()
if ($line -and $line.Contains('=')) {
$eqIdx = $line.IndexOf('=')
$key = $line.Substring(0, $eqIdx).Trim()
$val = $line.Substring($eqIdx + 1).Trim()
[System.Environment]::SetEnvironmentVariable($key, $val, "Process")
Write-Host " [OK] 已注入: $key" -ForegroundColor Green
}
}
}
catch {
Write-Host " [ERROR] 凭证解密失败: $_" -ForegroundColor Red
# 检查关键凭证是否存在
if (-not $env:ANTHROPIC_API_KEY) {
Write-Host " [WARN] ANTHROPIC_API_KEY 未设置Claude Code 可能无法使用中转站" -ForegroundColor Yellow
$continue = Read-Host " 继续启动? (y/n)"
if ($continue -ne 'y') { exit 1 }
}
}
finally {
$plainPwd = $null $plainPwd = $null
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
if ($decExit -eq 0 -and $decrypted) {
# 解密成功,注入环境变量
$decrypted -split "`n" | ForEach-Object {
$line = $_.Trim()
if ($line -and $line.Contains('=')) {
$eqIdx = $line.IndexOf('=')
$key = $line.Substring(0, $eqIdx).Trim()
$val = $line.Substring($eqIdx + 1).Trim()
[System.Environment]::SetEnvironmentVariable($key, $val, "Process")
Write-Host " [OK] 已注入: $key" -ForegroundColor Green
}
}
return
}
# 解密失败
$remaining = $maxRetries - $attempt
if ($remaining -gt 0) {
Write-Host " [!!] 密码错误,剩余重试: $remaining" -ForegroundColor Red
}
} }
# 3次全部失败
Write-Host ""
Write-Host " [ABORT] 3 次密码均错误" -ForegroundColor Red
Write-Host " 请确认主密码是否正确 (区分大小写至少12位)" -ForegroundColor Yellow
Write-Host " 如忘记密码,请联系管理员重新生成 secrets.enc" -ForegroundColor Yellow
exit 1
} }
function Render-SettingsTemplate { function Render-SettingsTemplate {
@ -202,7 +211,7 @@ function Detect-SystemProxy {
Write-Banner Write-Banner
# 步骤 0: 前置检查 # 步骤 0: 前置检查
Write-Host "[0/7] 前置检查..." -ForegroundColor White Write-Host "[1/6] 前置检查..." -ForegroundColor White
$checks = @( $checks = @(
@{ Name = "Claude Code"; OK = (Test-Command "claude") } @{ Name = "Claude Code"; OK = (Test-Command "claude") }
@{ Name = "Node.js"; OK = (Test-Command "node") } @{ Name = "Node.js"; OK = (Test-Command "node") }
@ -222,17 +231,23 @@ if (-not (Test-Command "node")) {
exit 1 exit 1
} }
# 步骤 0.5: 代理检测 (国内必须) # 步骤 2: 代理检测 (国内必须)
Write-Host "`n[0.5/7] 代理检测..." -ForegroundColor White Write-Host "`n[2/6] 代理检测..." -ForegroundColor White
Detect-SystemProxy Detect-SystemProxy
# 步骤 1: 解密凭证 (进程级环境变量,不写磁盘) # 步骤 3: 解密凭证 (进程级环境变量,不写磁盘)
Write-Host "`n[1/7] 解密凭证..." -ForegroundColor White Write-Host "`n[3/6] 解密凭证..." -ForegroundColor White
Decrypt-Secrets Decrypt-Secrets
# 自动配置 git credential helper (避免 clone 时反复要密码)
$prevEAP = $ErrorActionPreference
$ErrorActionPreference = "Continue"
git config --global credential.helper store 2>$null
$ErrorActionPreference = $prevEAP
# 步骤 2: 克隆/更新仓库 # 步骤 2: 克隆/更新仓库
if (-not $StartOnly) { if (-not $StartOnly) {
Write-Host "`n[2/7] 同步 Bookworm 配置..." -ForegroundColor White Write-Host "`n[4/6] 同步 Bookworm 配置..." -ForegroundColor White
if (Test-Path $ClaudeTarget) { if (Test-Path $ClaudeTarget) {
$isGit = Test-Path (Join-Path $ClaudeTarget ".git") $isGit = Test-Path (Join-Path $ClaudeTarget ".git")
@ -303,13 +318,26 @@ if (-not $StartOnly) {
} }
} }
else { else {
Write-Host "`n[2/7] StartOnly 模式,跳过同步" -ForegroundColor Gray Write-Host "`n[4/6] StartOnly 模式,跳过同步" -ForegroundColor Gray
# 静默检测远程更新
$configDir = Join-Path $env:USERPROFILE ".claude"
if (Test-Path (Join-Path $configDir ".git")) {
$prevEAP2 = $ErrorActionPreference
$ErrorActionPreference = "Continue"
git -C $configDir fetch --quiet 2>$null
$behind = git -C $configDir rev-list "HEAD..origin/main" --count 2>$null
$ErrorActionPreference = $prevEAP2
if ($behind -and [int]$behind -gt 0) {
Write-Host " [!] Bookworm 有 $behind 个新更新可用" -ForegroundColor Yellow
Write-Host " 双击 '更新并启动Bookworm.bat' 可同步最新版本" -ForegroundColor Yellow
}
}
} }
# 步骤 3: 完整性校验 # 步骤 3: 完整性校验
$integrityFile = Join-Path $ClaudeTarget "integrity.sha256" $integrityFile = Join-Path $ClaudeTarget "integrity.sha256"
if (Test-Path $integrityFile) { if (Test-Path $integrityFile) {
Write-Host "`n[4/7] 完整性校验..." -ForegroundColor White Write-Host "`n[5/6] 完整性校验..." -ForegroundColor White
$failures = @() $failures = @()
Get-Content $integrityFile | ForEach-Object { Get-Content $integrityFile | ForEach-Object {
if ($_ -match '^([a-f0-9]{64})\s+(.+)$') { if ($_ -match '^([a-f0-9]{64})\s+(.+)$') {
@ -333,15 +361,15 @@ if (Test-Path $integrityFile) {
Write-Host " [OK] 所有文件完整性校验通过" -ForegroundColor Green Write-Host " [OK] 所有文件完整性校验通过" -ForegroundColor Green
} }
} else { } else {
Write-Host "`n[4/7] 跳过完整性校验 (无 integrity.sha256)" -ForegroundColor Gray Write-Host "`n[5/6] 跳过完整性校验 (无 integrity.sha256)" -ForegroundColor Gray
} }
# 步骤 4: 渲染 settings.json # 步骤 4: 渲染 settings.json
Write-Host "`n[5/7] 渲染配置模板..." -ForegroundColor White Write-Host "`n[5/6] 渲染配置模板..." -ForegroundColor White
Render-SettingsTemplate Render-SettingsTemplate
# 步骤 5: 确保必要目录存在 # 步骤 5: 确保必要目录存在
Write-Host "`n[6/7] 初始化本地目录..." -ForegroundColor White Write-Host "`n[5/6] 初始化本地目录..." -ForegroundColor White
$localDirs = @("debug", "sessions", "cache", "backups", "telemetry", "shell-snapshots", "projects", "memory") $localDirs = @("debug", "sessions", "cache", "backups", "telemetry", "shell-snapshots", "projects", "memory")
foreach ($d in $localDirs) { foreach ($d in $localDirs) {
$dirPath = Join-Path $ClaudeTarget $d $dirPath = Join-Path $ClaudeTarget $d
@ -364,7 +392,7 @@ if ($nodeCheck -eq $ClaudeTarget) {
} }
# 步骤 6: Bookworm 完整性验证 + MCP 检查 # 步骤 6: Bookworm 完整性验证 + MCP 检查
Write-Host "`n[7/8] Bookworm 系统验证..." -ForegroundColor White Write-Host "`n[5/6] Bookworm 系统验证..." -ForegroundColor White
# --- Bookworm vs 原生 Claude Code 检测 --- # --- Bookworm vs 原生 Claude Code 检测 ---
$bwChecks = @() $bwChecks = @()
@ -495,7 +523,7 @@ if ($mcpWarnings.Count -eq 0) {
} }
# 步骤 7: 启动 Claude Code # 步骤 7: 启动 Claude Code
Write-Host "`n[8/8] 启动 Claude Code..." -ForegroundColor White Write-Host "`n[6/6] 启动 Claude Code..." -ForegroundColor White
Write-Host "" Write-Host ""
if ($allOK) { if ($allOK) {
Write-Host " ╔══════════════════════════════════════════╗" -ForegroundColor Green Write-Host " ╔══════════════════════════════════════════╗" -ForegroundColor Green

View File

@ -5,10 +5,10 @@
================================================================================ ================================================================================
Gitea 地址: https://code.letcareme.com Gitea 地址: https://code.letcareme.com
管理员账号: bookworm 管理员账号: [由管理员提供]
管理员密码: [REDACTED_OLD_PASSWORD] 管理员密码: [由管理员提供]
中转站地址: https://bww.letcareme.com/v1 中转站地址: [由管理员提供]
ECS 服务器: 8.138.11.105 (Gitea 端口 3300, 仅本地) ECS 服务器: [由管理员提供]
================================================================================ ================================================================================
一、首次安装 (目标机) 一、首次安装 (目标机)
@ -109,7 +109,7 @@
---- 5.1 部署 Gitea (仅首次) ---- ---- 5.1 部署 Gitea (仅首次) ----
scp C:\Users\leesu\Desktop\bookworm-portable\deploy-gitea.sh root@8.138.11.105:/tmp/ scp C:\Users\leesu\Desktop\bookworm-portable\deploy-gitea.sh root@8.138.11.105:/tmp/
ssh root@8.138.11.105 "GITEA_ADMIN_PASS='[REDACTED_OLD_PASSWORD]' bash /tmp/deploy-gitea.sh" ssh root@8.138.11.105 "GITEA_ADMIN_PASS='[密码]' bash /tmp/deploy-gitea.sh"
---- 5.2 配置 HTTPS (仅首次) ---- ---- 5.2 配置 HTTPS (仅首次) ----
@ -126,7 +126,7 @@
cd C:\Users\leesu\.claude cd C:\Users\leesu\.claude
git add -A git add -A
git commit -m "update bookworm config" git commit -m "update bookworm config"
git push https://bookworm:[REDACTED_OLD_PASSWORD]@code.letcareme.com/bookworm/bookworm-config.git main git push https://bookworm:[密码]@code.letcareme.com/bookworm/bookworm-config.git main
---- 5.5 更新 boot 仓库脚本 ---- ---- 5.5 更新 boot 仓库脚本 ----
@ -137,7 +137,7 @@
cp C:\Users\leesu\Desktop\bookworm-portable\secrets.enc . cp C:\Users\leesu\Desktop\bookworm-portable\secrets.enc .
git add -A git add -A
git commit -m "update boot scripts" git commit -m "update boot scripts"
git push https://bookworm:[REDACTED_OLD_PASSWORD]@code.letcareme.com/bookworm/bookworm-boot.git main git push https://bookworm:[密码]@code.letcareme.com/bookworm/bookworm-boot.git main
---- 5.6 重新加密凭证 (更换 API Key 后) ---- ---- 5.6 重新加密凭证 (更换 API Key 后) ----

23
启动Bookworm.bat Normal file
View File

@ -0,0 +1,23 @@
@echo off
chcp 65001 > nul
title Bookworm Portable - 启动
cd /d "%~dp0"
echo.
echo ====================================
echo Bookworm Portable - 快速启动
echo ====================================
echo.
where pwsh >nul 2>nul
if %errorlevel% equ 0 (
pwsh -ExecutionPolicy Bypass -File install.ps1 -StartOnly
) else (
powershell -ExecutionPolicy Bypass -File install.ps1 -StartOnly
)
if %errorlevel% neq 0 (
echo.
echo 启动失败,按任意键退出...
pause > nul
)

View File

@ -0,0 +1,24 @@
@echo off
chcp 65001 > nul
title Bookworm Portable - 更新并启动
cd /d "%~dp0"
echo.
echo ====================================
echo Bookworm Portable - 更新并启动
echo (同步最新 Skills/Hooks 后启动)
echo ====================================
echo.
where pwsh >nul 2>nul
if %errorlevel% equ 0 (
pwsh -ExecutionPolicy Bypass -File install.ps1
) else (
powershell -ExecutionPolicy Bypass -File install.ps1
)
if %errorlevel% neq 0 (
echo.
echo 启动失败,按任意键退出...
pause > nul
)