<# .SYNOPSIS Bookworm Portable — 打包工具 (管理员使用) .DESCRIPTION 将 auto-setup.ps1 打包为 Bookworm-Setup.exe (PS2EXE) 输出到 dist\ 目录 .USAGE .\build.ps1 # 打包用户安装器 Bookworm-Setup.exe .NOTES [Path-A 2026-06-30] admin 授权码工具 (gen-authcode.js / admin-authcode-gui.ps1) 已从公开 boot 仓剥离; 授权码生成改由服务器端 issuer 容器 /app/admin/gen-authcode.js 负责, -Admin 打包路径已移除。 #> param( [switch]$Setup # 保留参数兼容旧调用; 当前仅产出 Bookworm-Setup.exe ) $ErrorActionPreference = "Stop" $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $DistDir = Join-Path $ScriptDir "dist" # 仅产出用户安装器 (admin 授权码工具已剥离, 见 .NOTES) $buildSetup = $true function Write-Step($msg) { Write-Host "" Write-Host " ── $msg" -ForegroundColor Cyan } function Write-OK($msg) { Write-Host " [OK] $msg" -ForegroundColor Green } function Write-Warn($msg) { Write-Host " [!] $msg" -ForegroundColor Yellow } function Write-Fail($msg) { Write-Host " [!!] $msg" -ForegroundColor Red } Write-Host "" Write-Host " +------------------------------------------+" -ForegroundColor Cyan Write-Host " | Bookworm Portable — Build Script |" -ForegroundColor Cyan Write-Host " +------------------------------------------+" -ForegroundColor Cyan if (-not (Test-Path $DistDir)) { New-Item -ItemType Directory $DistDir | Out-Null Write-OK "创建 dist\ 目录" } # ════════════════════════════════════════════════════════ # 1. Bookworm-Setup.exe (auto-setup.ps1 → PS2EXE) # ════════════════════════════════════════════════════════ if ($buildSetup) { Write-Step "打包 Bookworm-Setup.exe (PS2EXE)" # 安装/检查 PS2EXE if (-not (Get-Command Invoke-ps2exe -ErrorAction SilentlyContinue)) { Write-Warn "PS2EXE 未安装,正在安装..." Install-Module ps2exe -Scope CurrentUser -Force -AllowClobber Import-Module ps2exe } $inputPs1 = Join-Path $ScriptDir "auto-setup.ps1" $outputExe = Join-Path $DistDir "Bookworm-Setup.exe" if (-not (Test-Path $inputPs1)) { Write-Fail "找不到 auto-setup.ps1" exit 1 } Write-Host " 输入: $inputPs1" -ForegroundColor Gray Write-Host " 输出: $outputExe" -ForegroundColor Gray # 从 auto-setup.ps1 读取版本号 $versionLine = Select-String -Path $inputPs1 -Pattern '^\$BWVersion\s*=\s*"([^"]+)"' | Select-Object -First 1 $bwVer = if ($versionLine) { $versionLine.Matches[0].Groups[1].Value } else { "0.0.0" } Write-Host " 版本: $bwVer" -ForegroundColor Gray # ── [Path-A] 注入 token-exchange 载荷 base64 (单文件自包含 Setup.exe; 每次 build 与源同步) ── # 把三个公开加密载荷文件读出 → base64 → 替换 auto-setup.ps1 内 BEGIN/END 标记之间的内容。 # 注意: 载荷全是公开加密代码, 不含任何 secret (authcode 运行时用户输入, pepper 永远服务器端)。 Write-Step "注入 token-exchange 载荷 (base64 内嵌)" $PayloadSrcRoot = Join-Path $env:USERPROFILE "bookworm-pathA-design\phase1-skeleton" # 文件名 → 候选源路径 (优先 boot\issuer\ 暂存副本, 回退设计骨架仓) $payloadMap = [ordered]@{ 'seal-cli.js' = @((Join-Path $ScriptDir "issuer\seal-cli.js"), (Join-Path $PayloadSrcRoot "issuer\src\seal-cli.js")) 'crypto-handshake.js' = @((Join-Path $ScriptDir "issuer\crypto-handshake.js"), (Join-Path $PayloadSrcRoot "issuer\src\crypto-handshake.js")) 'bw-tokenexchange.ps1' = @((Join-Path $ScriptDir "issuer\bw-tokenexchange.ps1"), (Join-Path $PayloadSrcRoot "auto-setup-tokenexchange-draft\bw-tokenexchange.ps1")) } $b64Map = [ordered]@{} foreach ($name in $payloadMap.Keys) { $resolved = $null foreach ($cand in $payloadMap[$name]) { if (Test-Path $cand) { $resolved = $cand; break } } if (-not $resolved) { Write-Fail "载荷源缺失: $name (查找: $($payloadMap[$name] -join ' | '))" exit 1 } $bytes = [IO.File]::ReadAllBytes($resolved) $b64Map[$name] = [Convert]::ToBase64String($bytes) Write-OK "$name <- $resolved ($($bytes.Length) B -> base64 $($b64Map[$name].Length) ch)" } # 生成 BEGIN..END 之间的内容 (4 空格缩进, 与 auto-setup.ps1 内 if 块对齐) $nl = "`r`n" $blockLines = New-Object System.Collections.Generic.List[string] $blockLines.Add("# —— 由 build.ps1 从载荷源文件自动注入, 请勿手改") $blockLines.Add(" `$BwPayloadB64 = @{") foreach ($name in $b64Map.Keys) { $blockLines.Add(" '$name' = '$($b64Map[$name])'") } $blockLines.Add(" }") $blockLines.Add(" # ") $newBlock = $blockLines -join $nl # 整文件替换 BEGIN..END (含标记) —— 用 MatchEvaluator 避免 $ 被当作正则分组引用 $content = [IO.File]::ReadAllText($inputPs1) $pattern = '(?s)# .*?# ' if ($content -notmatch $pattern) { Write-Fail "auto-setup.ps1 缺少 BW_PAYLOAD_BASE64 标记, 无法注入 (检查 BEGIN/END 注释)" exit 1 } $evaluator = [System.Text.RegularExpressions.MatchEvaluator]{ param($m) $newBlock } $content = [regex]::Replace($content, $pattern, $evaluator) # 保留 auto-setup.ps1 原编码: UTF-8 无 BOM (含中文; PS2EXE/pwsh 默认按 UTF-8 读) [IO.File]::WriteAllText($inputPs1, $content, (New-Object System.Text.UTF8Encoding($false))) Write-OK "base64 载荷已注入 auto-setup.ps1 (3 文件, 共 $($newBlock.Length) ch)" # 优先用桌面专用 B 圆图标, 回退到 galaxy $iconFile = Join-Path $ScriptDir "bookworm-desktop.ico" if (-not (Test-Path $iconFile)) { $iconFile = Join-Path $ScriptDir "bookworm.ico" } $ps2exeArgs = @{ InputFile = $inputPs1 OutputFile = $outputExe Title = "Bookworm Portable Setup v$bwVer" Description = "Bookworm Smart Assistant 安装向导 v$bwVer" Company = "Bookworm" Version = "$bwVer.0" NoConsole = $true NoOutput = $true NoError = $true } if (Test-Path $iconFile) { $ps2exeArgs.IconFile = $iconFile Write-Host " 图标: $iconFile" -ForegroundColor Gray } Invoke-ps2exe @ps2exeArgs if (Test-Path $outputExe) { $sizeKB = [math]::Round((Get-Item $outputExe).Length / 1KB) Write-OK "Bookworm-Setup.exe 打包完成 (${sizeKB} KB)" } else { Write-Fail "Bookworm-Setup.exe 打包失败" exit 1 } } # ════════════════════════════════════════════════════════ # 完成 # ════════════════════════════════════════════════════════ Write-Host "" Write-Host " ============================================" -ForegroundColor Green Write-Host " 打包完成!输出目录: dist\" -ForegroundColor Green Write-Host " ============================================" -ForegroundColor Green Write-Host "" # v3.1.1: build 后自动跑 E2E 行为测试 (闭合 L8: 防 v3.0.10 -or 类运行时 bug) $e2eTest = Join-Path $ScriptDir "tools\test-launcher-e2e.ps1" if (Test-Path $e2eTest) { Write-Host " ── 运行 E2E 行为测试 (build 后自动护栏)" -ForegroundColor Cyan & pwsh -NoProfile -File $e2eTest 2>&1 | ForEach-Object { Write-Host " $_" } if ($LASTEXITCODE -ne 0) { Write-Host "" Write-Host " [!] E2E 测试失败 (exit $LASTEXITCODE)" -ForegroundColor Red Write-Host " EXE 已生成但启动器契约/wrapper 有问题, 修复后重打包" -ForegroundColor Yellow exit 1 } Write-Host "" } Get-ChildItem $DistDir | ForEach-Object { $sizeMB = [math]::Round($_.Length / 1MB, 1) Write-Host " $($_.Name.PadRight(30)) ${sizeMB} MB" -ForegroundColor White } Write-Host "" Write-Host " 分发说明:" -ForegroundColor Gray Write-Host " Bookworm-Setup.exe → 用户安装器 (公开下载)" -ForegroundColor Gray Write-Host ""