2026-04-07 00:01:46 +08:00
|
|
|
<#
|
|
|
|
|
.SYNOPSIS
|
|
|
|
|
Bookworm Portable — 打包工具 (管理员使用)
|
|
|
|
|
.DESCRIPTION
|
|
|
|
|
将 auto-setup.ps1 打包为 Bookworm-Setup.exe (PS2EXE)
|
|
|
|
|
将 gen-authcode.js 打包为 gen-authcode.exe (pkg)
|
|
|
|
|
输出到 dist\ 目录
|
|
|
|
|
.USAGE
|
|
|
|
|
.\build.ps1 # 打包两个
|
|
|
|
|
.\build.ps1 -Setup # 只打包用户安装器
|
|
|
|
|
.\build.ps1 -Admin # 只打包管理员工具
|
|
|
|
|
#>
|
|
|
|
|
param(
|
|
|
|
|
[switch]$Setup, # 只打 Bookworm-Setup.exe
|
|
|
|
|
[switch]$Admin # 只打 gen-authcode.exe
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
|
|
|
$DistDir = Join-Path $ScriptDir "dist"
|
|
|
|
|
|
|
|
|
|
# 默认两个都打
|
|
|
|
|
$buildSetup = $Setup -or (-not $Setup -and -not $Admin)
|
|
|
|
|
$buildAdmin = $Admin -or (-not $Setup -and -not $Admin)
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
2026-04-10 11:11:13 +08:00
|
|
|
# 从 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
|
|
|
|
|
|
2026-06-29 19:50:04 +08:00
|
|
|
# ── [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("# <BW_PAYLOAD_BASE64_BEGIN> —— 由 build.ps1 从载荷源文件自动注入, 请勿手改")
|
|
|
|
|
$blockLines.Add(" `$BwPayloadB64 = @{")
|
|
|
|
|
foreach ($name in $b64Map.Keys) {
|
|
|
|
|
$blockLines.Add(" '$name' = '$($b64Map[$name])'")
|
|
|
|
|
}
|
|
|
|
|
$blockLines.Add(" }")
|
|
|
|
|
$blockLines.Add(" # <BW_PAYLOAD_BASE64_END>")
|
|
|
|
|
$newBlock = $blockLines -join $nl
|
|
|
|
|
# 整文件替换 BEGIN..END (含标记) —— 用 MatchEvaluator 避免 $ 被当作正则分组引用
|
|
|
|
|
$content = [IO.File]::ReadAllText($inputPs1)
|
|
|
|
|
$pattern = '(?s)# <BW_PAYLOAD_BASE64_BEGIN>.*?# <BW_PAYLOAD_BASE64_END>'
|
|
|
|
|
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)"
|
|
|
|
|
|
2026-04-10 11:11:13 +08:00
|
|
|
# 优先用桌面专用 B 圆图标, 回退到 galaxy
|
2026-04-07 20:52:18 +08:00
|
|
|
$iconFile = Join-Path $ScriptDir "bookworm-desktop.ico"
|
|
|
|
|
if (-not (Test-Path $iconFile)) {
|
|
|
|
|
$iconFile = Join-Path $ScriptDir "bookworm.ico"
|
|
|
|
|
}
|
2026-04-07 01:08:41 +08:00
|
|
|
$ps2exeArgs = @{
|
|
|
|
|
InputFile = $inputPs1
|
|
|
|
|
OutputFile = $outputExe
|
2026-04-10 11:11:13 +08:00
|
|
|
Title = "Bookworm Portable Setup v$bwVer"
|
|
|
|
|
Description = "Bookworm Smart Assistant 安装向导 v$bwVer"
|
2026-04-07 01:08:41 +08:00
|
|
|
Company = "Bookworm"
|
2026-04-10 11:11:13 +08:00
|
|
|
Version = "$bwVer.0"
|
2026-04-07 01:08:41 +08:00
|
|
|
NoConsole = $true
|
2026-04-10 11:11:13 +08:00
|
|
|
NoOutput = $true
|
|
|
|
|
NoError = $true
|
2026-04-07 01:08:41 +08:00
|
|
|
}
|
|
|
|
|
if (Test-Path $iconFile) {
|
|
|
|
|
$ps2exeArgs.IconFile = $iconFile
|
|
|
|
|
Write-Host " 图标: $iconFile" -ForegroundColor Gray
|
|
|
|
|
}
|
|
|
|
|
Invoke-ps2exe @ps2exeArgs
|
2026-04-07 00:01:46 +08:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# ════════════════════════════════════════════════════════
|
2026-04-10 01:59:27 +08:00
|
|
|
# 2. Bookworm-AuthGen.exe (admin-authcode-gui.ps1 → PS2EXE)
|
2026-04-07 00:01:46 +08:00
|
|
|
# ════════════════════════════════════════════════════════
|
|
|
|
|
if ($buildAdmin) {
|
2026-04-10 01:59:27 +08:00
|
|
|
Write-Step "打包 Bookworm-AuthGen.exe (PS2EXE GUI)"
|
2026-04-07 00:01:46 +08:00
|
|
|
|
2026-04-10 01:59:27 +08:00
|
|
|
$inputPs1 = Join-Path $ScriptDir "admin-authcode-gui.ps1"
|
|
|
|
|
$outputExe = Join-Path $DistDir "Bookworm-AuthGen.exe"
|
2026-04-07 00:01:46 +08:00
|
|
|
|
2026-04-10 01:59:27 +08:00
|
|
|
if (-not (Test-Path $inputPs1)) {
|
|
|
|
|
Write-Fail "找不到 admin-authcode-gui.ps1"
|
2026-04-07 00:01:46 +08:00
|
|
|
exit 1
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-10 01:59:27 +08:00
|
|
|
Write-Host " 输入: $inputPs1" -ForegroundColor Gray
|
2026-04-07 00:01:46 +08:00
|
|
|
Write-Host " 输出: $outputExe" -ForegroundColor Gray
|
|
|
|
|
|
2026-04-10 01:59:27 +08:00
|
|
|
# 优先用书虫学者图标, 回退到 B 圆
|
|
|
|
|
$adminIcon = Join-Path $ScriptDir "admin-authcode.ico"
|
|
|
|
|
if (-not (Test-Path $adminIcon)) { $adminIcon = Join-Path $ScriptDir "bookworm-desktop.ico" }
|
2026-04-07 00:01:46 +08:00
|
|
|
|
2026-04-10 01:59:27 +08:00
|
|
|
$ps2exeArgs = @{
|
|
|
|
|
InputFile = $inputPs1
|
|
|
|
|
OutputFile = $outputExe
|
|
|
|
|
Title = "Bookworm AuthCode Generator"
|
|
|
|
|
Description = "Bookworm 授权码生成器 (管理员工具)"
|
|
|
|
|
Company = "Bookworm"
|
|
|
|
|
Version = "1.5.1.0"
|
|
|
|
|
NoConsole = $true
|
|
|
|
|
NoOutput = $true
|
|
|
|
|
NoError = $true
|
|
|
|
|
}
|
|
|
|
|
if (Test-Path $adminIcon) {
|
|
|
|
|
$ps2exeArgs.IconFile = $adminIcon
|
|
|
|
|
Write-Host " 图标: $adminIcon" -ForegroundColor Gray
|
|
|
|
|
}
|
|
|
|
|
Invoke-ps2exe @ps2exeArgs
|
2026-04-07 01:08:41 +08:00
|
|
|
|
2026-04-10 01:59:27 +08:00
|
|
|
if (Test-Path $outputExe) {
|
|
|
|
|
$sizeKB = [math]::Round((Get-Item $outputExe).Length / 1KB)
|
|
|
|
|
Write-OK "Bookworm-AuthGen.exe 打包完成 (${sizeKB} KB)"
|
2026-04-07 00:01:46 +08:00
|
|
|
} else {
|
2026-04-10 01:59:27 +08:00
|
|
|
Write-Fail "Bookworm-AuthGen.exe 打包失败"
|
2026-04-07 00:01:46 +08:00
|
|
|
exit 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# ════════════════════════════════════════════════════════
|
|
|
|
|
# 完成
|
|
|
|
|
# ════════════════════════════════════════════════════════
|
|
|
|
|
Write-Host ""
|
|
|
|
|
Write-Host " ============================================" -ForegroundColor Green
|
|
|
|
|
Write-Host " 打包完成!输出目录: dist\" -ForegroundColor Green
|
|
|
|
|
Write-Host " ============================================" -ForegroundColor Green
|
|
|
|
|
Write-Host ""
|
|
|
|
|
|
2026-04-25 23:16:08 +08:00
|
|
|
# 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 ""
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-07 00:01:46 +08:00
|
|
|
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
|
2026-04-10 01:59:27 +08:00
|
|
|
Write-Host " Bookworm-Setup.exe → 用户安装器 (公开下载)" -ForegroundColor Gray
|
|
|
|
|
Write-Host " Bookworm-AuthGen.exe → 管理员授权码生成器 (勿对外分发)" -ForegroundColor Gray
|
2026-04-07 00:01:46 +08:00
|
|
|
Write-Host ""
|