diff --git a/.gitignore b/.gitignore index a0b8260..8c59990 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,7 @@ secrets.txt users.txt auto-setup.ps1.bak-* .tmp-authcodes.json +.tmp-release.json +.tmp-*.png 管理员SOP.html dist/ diff --git a/auto-setup.ps1 b/auto-setup.ps1 index 9837de3..ff47b75 100644 --- a/auto-setup.ps1 +++ b/auto-setup.ps1 @@ -39,15 +39,109 @@ Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing [System.Windows.Forms.Application]::EnableVisualStyles() -# ─── 颜色输出 ──────────────────────────────────────── -function Log-OK($msg) { Write-Host " [OK] $msg" -ForegroundColor Green } -function Log-Info($msg) { Write-Host " [..] $msg" -ForegroundColor Cyan } -function Log-Warn($msg) { Write-Host " [!] $msg" -ForegroundColor Yellow } -function Log-Fail($msg) { Write-Host " [!!] $msg" -ForegroundColor Red } +# ─── 日志 + 进度 (PS2EXE -NoConsole -NoOutput 模式: console 全静默) ── +# 所有 Log-X 输出走文件 + GUI 进度窗口 (避免被 PS2EXE 弹窗化) +$BWLogFile = Join-Path $env:TEMP "bookworm-setup-$(Get-Date -Format 'yyyyMMdd-HHmmss').log" +function Bw-Log($level, $msg) { + try { Add-Content -Path $BWLogFile -Value "[$(Get-Date -Format 'HH:mm:ss')] [$level] $msg" -Encoding utf8 } catch {} +} +function Log-OK($msg) { Bw-Log "OK" $msg; Update-Progress-SubStatus "$msg" } +function Log-Info($msg) { Bw-Log "INFO" $msg; Update-Progress-SubStatus "$msg" } +function Log-Warn($msg) { Bw-Log "WARN" $msg } +function Log-Fail($msg) { Bw-Log "FAIL" $msg } function Log-Phase($n, $title) { - Write-Host "" - Write-Host " [$n/$TOTAL_PHASES] $title" -ForegroundColor White -BackgroundColor DarkBlue - Write-Progress -Activity "Bookworm 自动安装" -Status "$title" -PercentComplete ([int]($n / $TOTAL_PHASES * 100)) + Bw-Log "PHASE" "[$n/$TOTAL_PHASES] $title" + Update-Progress $n $title +} + +# ─── GUI 进度窗口 (常驻顶部, 替代 console 输出) ──── +$global:BWProgressForm = $null +$global:BWPhaseLabel = $null +$global:BWStatusLabel = $null +$global:BWProgressBar = $null + +function Show-ProgressForm { + $global:BWProgressForm = New-Object System.Windows.Forms.Form + $global:BWProgressForm.Text = "Bookworm Portable Setup" + $global:BWProgressForm.Size = New-Object System.Drawing.Size(520, 200) + $global:BWProgressForm.StartPosition = "CenterScreen" + $global:BWProgressForm.FormBorderStyle = "FixedDialog" + $global:BWProgressForm.MaximizeBox = $false + $global:BWProgressForm.MinimizeBox = $false + $global:BWProgressForm.TopMost = $true + $global:BWProgressForm.ControlBox = $false + + $titleLabel = New-Object System.Windows.Forms.Label + $titleLabel.Location = New-Object System.Drawing.Point(20, 18) + $titleLabel.Size = New-Object System.Drawing.Size(480, 24) + $titleLabel.Text = "Bookworm 智能助手 — 自动安装中, 请稍候..." + $titleLabel.Font = New-Object System.Drawing.Font("Segoe UI", 11, [System.Drawing.FontStyle]::Bold) + $global:BWProgressForm.Controls.Add($titleLabel) + + $global:BWPhaseLabel = New-Object System.Windows.Forms.Label + $global:BWPhaseLabel.Location = New-Object System.Drawing.Point(20, 50) + $global:BWPhaseLabel.Size = New-Object System.Drawing.Size(480, 22) + $global:BWPhaseLabel.Text = "[0/$TOTAL_PHASES] 初始化..." + $global:BWPhaseLabel.Font = New-Object System.Drawing.Font("Segoe UI", 10) + $global:BWPhaseLabel.ForeColor = [System.Drawing.Color]::DarkBlue + $global:BWProgressForm.Controls.Add($global:BWPhaseLabel) + + $global:BWStatusLabel = New-Object System.Windows.Forms.Label + $global:BWStatusLabel.Location = New-Object System.Drawing.Point(20, 78) + $global:BWStatusLabel.Size = New-Object System.Drawing.Size(480, 22) + $global:BWStatusLabel.Text = "" + $global:BWStatusLabel.Font = New-Object System.Drawing.Font("Segoe UI", 9) + $global:BWStatusLabel.ForeColor = [System.Drawing.Color]::DimGray + $global:BWProgressForm.Controls.Add($global:BWStatusLabel) + + $global:BWProgressBar = New-Object System.Windows.Forms.ProgressBar + $global:BWProgressBar.Location = New-Object System.Drawing.Point(20, 110) + $global:BWProgressBar.Size = New-Object System.Drawing.Size(480, 22) + $global:BWProgressBar.Minimum = 0 + $global:BWProgressBar.Maximum = $TOTAL_PHASES + $global:BWProgressBar.Value = 0 + $global:BWProgressForm.Controls.Add($global:BWProgressBar) + + $hint = New-Object System.Windows.Forms.Label + $hint.Location = New-Object System.Drawing.Point(20, 140) + $hint.Size = New-Object System.Drawing.Size(480, 18) + $hint.Text = "首次安装约 5-10 分钟 (依赖下载) | 日志: $BWLogFile" + $hint.Font = New-Object System.Drawing.Font("Segoe UI", 8) + $hint.ForeColor = [System.Drawing.Color]::Gray + $global:BWProgressForm.Controls.Add($hint) + + $global:BWProgressForm.Show() | Out-Null + $global:BWProgressForm.Refresh() + [System.Windows.Forms.Application]::DoEvents() +} + +function Update-Progress($phase, $title) { + if ($global:BWProgressForm -and -not $global:BWProgressForm.IsDisposed) { + try { + $global:BWPhaseLabel.Text = "[$phase/$TOTAL_PHASES] $title" + $global:BWStatusLabel.Text = "" + $global:BWProgressBar.Value = [Math]::Min($phase, $TOTAL_PHASES) + $global:BWProgressForm.Refresh() + [System.Windows.Forms.Application]::DoEvents() + } catch {} + } +} + +function Update-Progress-SubStatus($msg) { + if ($global:BWProgressForm -and -not $global:BWProgressForm.IsDisposed -and $global:BWStatusLabel) { + try { + $shortMsg = if ($msg.Length -gt 70) { $msg.Substring(0, 67) + "..." } else { $msg } + $global:BWStatusLabel.Text = $shortMsg + $global:BWStatusLabel.Refresh() + [System.Windows.Forms.Application]::DoEvents() + } catch {} + } +} + +function Close-ProgressForm { + if ($global:BWProgressForm -and -not $global:BWProgressForm.IsDisposed) { + try { $global:BWProgressForm.Close(); $global:BWProgressForm.Dispose() } catch {} + } } function Test-Cmd($cmd) { [bool](Get-Command $cmd -ErrorAction SilentlyContinue) } @@ -248,13 +342,21 @@ function New-DesktopShortcuts { $shell = New-Object -ComObject WScript.Shell $desktop = $shell.SpecialFolders("Desktop") + # 桌面专用图标 (Bookworm 蓝紫渐变 B 圆, 多尺寸 ICO) + $iconPath = Join-Path $BootDir "bookworm-desktop.ico" + if (-not (Test-Path $iconPath)) { + # 回退到 EXE 图标 (bookworm.ico) + $iconPath = Join-Path $BootDir "bookworm.ico" + } + # 快速启动 (bat 文件位于 bookworm-boot 仓库内) $shortcut = $shell.CreateShortcut("$desktop\Bookworm.lnk") $batPath = Join-Path $BootDir "启动Bookworm.bat" if (-not (Test-Path $batPath)) { $batPath = Join-Path $BootDir "Bookworm-OneClick.bat" } $shortcut.TargetPath = $batPath $shortcut.WorkingDirectory = $BootDir - $shortcut.Description = "Bookworm Smart Assistant" + $shortcut.Description = "Bookworm Smart Assistant - 智能助手" + if (Test-Path $iconPath) { $shortcut.IconLocation = "$iconPath,0" } $shortcut.Save() # 更新启动 @@ -264,23 +366,18 @@ function New-DesktopShortcuts { $shortcut2.TargetPath = $updateBat $shortcut2.WorkingDirectory = $BootDir $shortcut2.Description = "更新并启动 Bookworm" + if (Test-Path $iconPath) { $shortcut2.IconLocation = "$iconPath,0" } $shortcut2.Save() } - Log-OK "桌面快捷方式已创建" + Log-OK "桌面快捷方式已创建 (含 Bookworm 图标)" } catch { Log-Warn "快捷方式创建失败: $_" } } # ======================================================================== -# Banner +# 启动: 显示 GUI 进度窗口 (替代 console banner, PS2EXE -NoOutput 兼容) # ======================================================================== -Write-Host "" -Write-Host " +---------------------------------------------------+" -ForegroundColor Cyan -Write-Host " | |" -ForegroundColor Cyan -Write-Host " | Bookworm Portable - 全自动安装器 |" -ForegroundColor Cyan -Write-Host " | 92 Skills / 18 Agents / 34 Hooks |" -ForegroundColor Cyan -Write-Host " | |" -ForegroundColor Cyan -Write-Host " +---------------------------------------------------+" -ForegroundColor Cyan -Write-Host "" +Bw-Log "INIT" "Bookworm Portable Setup 启动 - 日志: $BWLogFile" +Show-ProgressForm # ======================================================================== # Phase 1: 环境检测 + 依赖自动安装 @@ -350,29 +447,70 @@ if (-not (Test-Cmd "claude") -and (Test-Cmd "npm")) { if (Test-Cmd "claude") { Log-OK "Claude Code 安装成功" } else { Log-Fail "Claude Code 安装失败" } } -# uv 依赖 Python, 需要在 Python 安装后再检查 -if (Test-Cmd "python") { - if (Test-Cmd "uv") { - $uvVer = try { & uv --version 2>$null | Select-Object -First 1 } catch { "installed" } - Log-OK "uv $uvVer" - } else { - Log-Info "安装 uv (Python 包管理器)..." +# uv (Python 包管理器, 可选依赖) - 完全静默, 失败不阻断不弹窗 +# 安装策略: 1) winget astral-sh.uv 2) Astral 官方脚本 3) pip fallback +$uvLogFile = Join-Path $env:TEMP "bookworm-uv-install.log" +$uvInstalled = $false + +if (Test-Cmd "uv") { + $uvVer = try { (& uv --version 2>$null | Select-Object -First 1) } catch { "installed" } + Log-OK "uv $uvVer (已存在)" + $uvInstalled = $true +} else { + Log-Info "安装 uv (Python 包管理器, 可选)..." + + # 静默执行子进程, 错误全部捕获到日志文件, 绝不冒泡到 PS2EXE + $prevErrPref = $ErrorActionPreference + $ErrorActionPreference = "SilentlyContinue" + + # 方案 A: winget (最可靠) + if (Test-Cmd "winget") { + try { + $null = & winget install --id=astral-sh.uv -e --silent --accept-source-agreements --accept-package-agreements 2>&1 | + Out-File -FilePath $uvLogFile -Encoding utf8 -Append + } catch { "[winget] $_" | Out-File -FilePath $uvLogFile -Encoding utf8 -Append } + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + # winget 装到 %LOCALAPPDATA%\Microsoft\WinGet\Packages\astral-sh.uv_* + $uvCargoBin = "$env:LOCALAPPDATA\Microsoft\WinGet\Links" + if (Test-Path $uvCargoBin) { $env:Path += ";$uvCargoBin" } + if (Test-Cmd "uv") { $uvInstalled = $true } + } + + # 方案 B: Astral 官方一行脚本 (走 https://astral.sh/uv/install.ps1) + if (-not $uvInstalled) { + try { + $null = & powershell -NoProfile -ExecutionPolicy Bypass -Command "irm https://astral.sh/uv/install.ps1 | iex" 2>&1 | + Out-File -FilePath $uvLogFile -Encoding utf8 -Append + } catch { "[astral] $_" | Out-File -FilePath $uvLogFile -Encoding utf8 -Append } + # Astral 脚本默认装到 %USERPROFILE%\.local\bin + $localBin = Join-Path $env:USERPROFILE ".local\bin" + if (Test-Path $localBin) { $env:Path += ";$localBin" } + if (Test-Cmd "uv") { $uvInstalled = $true } + } + + # 方案 C: pip fallback (Python 已装且前两个都失败) + if (-not $uvInstalled -and (Test-Cmd "python")) { + try { + $null = & python -m pip install --quiet uv 2>&1 | + Out-File -FilePath $uvLogFile -Encoding utf8 -Append + } catch { "[pip] $_" | Out-File -FilePath $uvLogFile -Encoding utf8 -Append } try { - & python -m pip install uv --quiet 2>&1 | Select-Object -Last 2 | ForEach-Object { Write-Host " $_" -ForegroundColor Gray } - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - # pip install 的 Scripts 目录可能不在 PATH, 追加 $pyScripts = Join-Path (Split-Path (& python -c "import sys; print(sys.executable)") -Parent) "Scripts" if (Test-Path $pyScripts) { $env:Path += ";$pyScripts" } - if (Test-Cmd "uv") { - Log-OK "uv 安装成功" - $installed += "uv" - } else { - Log-Warn "uv 安装后未找到, 尝试 uvx 替代检查..." - } - } catch { Log-Warn "uv 安装失败: $_" } + } catch {} + if (Test-Cmd "uv") { $uvInstalled = $true } + } + + $ErrorActionPreference = $prevErrPref + + if ($uvInstalled) { + Log-OK "uv 安装成功" + $installed += "uv" + } else { + # 静默 fallback: 仅写日志文件, 不调 Log-Warn 避免 PS2EXE 弹窗 + "[fail] uv 三种安装方式均失败, Python MCP 将不可用. 详见上方日志." | Out-File -FilePath $uvLogFile -Encoding utf8 -Append + Log-Info "uv 未就绪 (可选, 不影响核心功能, 详情: $uvLogFile)" } -} else { - Log-Warn "Python 未安装, 跳过 uv (部分 MCP 不可用)" } # OpenSSL (随 Git 安装) @@ -408,12 +546,12 @@ if ($PwshPath) { $PwshPath = "pwsh" } -# 可选依赖警告 (不阻断) +# 可选依赖检查 (不阻断, 用 Log-Info 避免 PS2EXE 弹窗化) $optionalMissing = @() if (-not (Test-Cmd "python")) { $optionalMissing += "Python 3.12" } if (-not (Test-Cmd "uv")) { $optionalMissing += "uv" } if ($optionalMissing.Count -gt 0) { - Log-Warn "可选依赖未就绪: $($optionalMissing -join ', ') (部分 MCP 不可用)" + Log-Info "可选依赖未就绪: $($optionalMissing -join ', ') — 仅影响 Python 类 MCP, 核心功能正常" } if ($installed.Count -gt 0) { @@ -921,40 +1059,30 @@ if ($missingOpt.Count -gt 0) { # ======================================================================== Log-Phase 7 "安装完成" -Write-Progress -Activity "Bookworm 自动安装" -Completed +# 关闭 GUI 进度窗口 (后续由 Show-MsgBox 显示成功/失败) +Close-ProgressForm # 创建桌面快捷方式 New-DesktopShortcuts -Write-Host "" if ($allOK -and $env:ANTHROPIC_API_KEY) { - Write-Host " +---------------------------------------------------+" -ForegroundColor Green - Write-Host " | |" -ForegroundColor Green - Write-Host " | Bookworm Portable 安装成功! |" -ForegroundColor Green - Write-Host " | $skillCount Skills / $hookCount Hooks / 全部就绪 |" -ForegroundColor Green - Write-Host " | |" -ForegroundColor Green - Write-Host " +---------------------------------------------------+" -ForegroundColor Green + Bw-Log "DONE" "安装成功 ($skillCount Skills / $hookCount Hooks)" + Show-MsgBox "Bookworm Portable 安装成功!`n`n$skillCount Skills / $hookCount Hooks 全部就绪`n`n点击确定后将自动启动 Claude Code。`n`n日志: $BWLogFile" "安装成功" "OK" "Information" | Out-Null if (-not $SkipLaunch) { - Write-Host "" - Log-Info "正在启动 Claude Code..." - Write-Host "" - & claude --dangerously-skip-permissions + # 启动 Claude Code: 用户的 Claude Code 由 cmd 窗口启动 (PS2EXE -NoConsole 下会创建新窗口) + Start-Process -FilePath "cmd.exe" -ArgumentList "/k", "claude --dangerously-skip-permissions" } } else { - Write-Host " +---------------------------------------------------+" -ForegroundColor Yellow - Write-Host " | |" -ForegroundColor Yellow - Write-Host " | 安装完成 (部分功能可能受限) |" -ForegroundColor Yellow - Write-Host " | |" -ForegroundColor Yellow - Write-Host " +---------------------------------------------------+" -ForegroundColor Yellow + Bw-Log "DONE" "安装完成但部分受限 allOK=$allOK hasKey=$($env:ANTHROPIC_API_KEY -ne $null)" $issues = @() if (-not $allOK) { $issues += "- Bookworm 配置不完整" } if (-not $env:ANTHROPIC_API_KEY) { $issues += "- API 凭证未解密" } $issueText = $issues -join "`n" - $launchResult = Show-MsgBox "安装完成, 但存在以下问题:`n$issueText`n`n是否仍然启动 Claude Code?`n(将以受限模式运行)" "安装警告" "YesNo" "Warning" + $launchResult = Show-MsgBox "安装完成, 但存在以下问题:`n$issueText`n`n是否仍然启动 Claude Code?`n(将以受限模式运行)`n`n日志: $BWLogFile" "安装警告" "YesNo" "Warning" if ($launchResult -eq "Yes" -and -not $SkipLaunch) { - & claude --dangerously-skip-permissions + Start-Process -FilePath "cmd.exe" -ArgumentList "/k", "claude --dangerously-skip-permissions" } } diff --git a/bookworm-desktop.ico b/bookworm-desktop.ico new file mode 100644 index 0000000..d3c719d Binary files /dev/null and b/bookworm-desktop.ico differ diff --git a/build.ps1 b/build.ps1 index 56982c4..df5c8d1 100644 --- a/build.ps1 +++ b/build.ps1 @@ -72,8 +72,10 @@ if ($buildSetup) { Title = "Bookworm Portable Setup" Description = "Bookworm Smart Assistant 安装向导" Company = "Bookworm" - Version = "1.5.0.0" + Version = "1.5.1.0" NoConsole = $true + NoOutput = $true # 抑制所有 Write-Host 弹窗化, 只保留 Show-MsgBox / GUI dialog + NoError = $true # 抑制 stderr 弹窗化, 异常仍走 try-catch } if (Test-Path $iconFile) { $ps2exeArgs.IconFile = $iconFile