2026-04-24 20:47:16 +08:00
|
|
|
|
# Bookworm Portable 启动器 bat 生成工具 (v3.0.6)
|
|
|
|
|
|
# 用途: 从单一明文 PowerShell 脚本生成两个 bat, 避免手工同步 Base64 字符串不一致
|
|
|
|
|
|
# 用法: pwsh -NoProfile -File tools/gen-launcher-bats.ps1
|
|
|
|
|
|
# 输出: 启动Bookworm.bat + 更新并启动Bookworm.bat (覆盖写入)
|
|
|
|
|
|
|
|
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
|
|
$repoRoot = Split-Path -Parent $PSScriptRoot
|
|
|
|
|
|
$launchBat = Join-Path $repoRoot "启动Bookworm.bat"
|
|
|
|
|
|
$updateBat = Join-Path $repoRoot "更新并启动Bookworm.bat"
|
|
|
|
|
|
|
2026-04-24 22:39:17 +08:00
|
|
|
|
# ─── 明文: 三层 PATH 修复 + DPAPI 加载 + claude 诊断 + 启动 ─────────
|
|
|
|
|
|
# v3.0.9: 增加 npm config get prefix 动态查询, 兼容 nvm/fnm/Program Files 等非标准 npm 位置
|
2026-04-24 20:47:16 +08:00
|
|
|
|
$plainScript = @'
|
|
|
|
|
|
Add-Type -AssemblyName System.Security
|
2026-04-24 22:39:17 +08:00
|
|
|
|
# 层 1: Machine + User env PATH (标准 Windows 环境变量)
|
2026-04-24 20:47:16 +08:00
|
|
|
|
$env:Path = [Environment]::GetEnvironmentVariable('Path','Machine') + ';' + [Environment]::GetEnvironmentVariable('Path','User')
|
2026-04-24 22:39:17 +08:00
|
|
|
|
# 层 2: npm config get prefix (真实 npm 全局目录, 兼容 nvm/fnm/标准安装/Program Files)
|
|
|
|
|
|
try {
|
|
|
|
|
|
$npmPrefix = (& npm config get prefix 2>$null | Select-Object -First 1).Trim()
|
|
|
|
|
|
if ($npmPrefix -and (Test-Path $npmPrefix) -and ($env:Path -notlike "*$npmPrefix*")) {
|
|
|
|
|
|
$env:Path = "$npmPrefix;$env:Path"
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch {}
|
|
|
|
|
|
# 层 3: 常见 npm global 硬编码兜底 (npm 本身不在 PATH 时无法 query)
|
|
|
|
|
|
$npmCandidates = @(
|
|
|
|
|
|
"$env:APPDATA\npm",
|
|
|
|
|
|
"$env:ProgramFiles\nodejs",
|
|
|
|
|
|
"${env:ProgramFiles(x86)}\nodejs",
|
|
|
|
|
|
"$env:LOCALAPPDATA\npm"
|
|
|
|
|
|
)
|
|
|
|
|
|
foreach ($p in $npmCandidates) {
|
|
|
|
|
|
if ((Test-Path $p) -and (Test-Path (Join-Path $p 'claude.ps1') -or (Test-Path (Join-Path $p 'claude.cmd'))) -and ($env:Path -notlike "*$p*")) {
|
|
|
|
|
|
$env:Path = "$p;$env:Path"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
# DPAPI 加载缓存凭证
|
2026-04-24 20:47:16 +08:00
|
|
|
|
$r = 'HKCU:\Software\Bookworm\CachedEnv'
|
|
|
|
|
|
try {
|
|
|
|
|
|
(Get-ItemProperty $r -EA Stop).PSObject.Properties | Where-Object { $_.Name -match '^[A-Z_]+$' } | ForEach-Object {
|
|
|
|
|
|
$v = $_.Value
|
|
|
|
|
|
try {
|
|
|
|
|
|
$b = [Security.Cryptography.ProtectedData]::Unprotect([Convert]::FromBase64String($v), $null, [Security.Cryptography.DataProtectionScope]::CurrentUser)
|
|
|
|
|
|
$v = [Text.Encoding]::UTF8.GetString($b)
|
|
|
|
|
|
} catch {}
|
|
|
|
|
|
[Environment]::SetEnvironmentVariable($_.Name, $v, 'Process')
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch {}
|
|
|
|
|
|
if (-not (Get-Command claude -ErrorAction SilentlyContinue)) {
|
|
|
|
|
|
Write-Host ''
|
2026-04-24 22:39:17 +08:00
|
|
|
|
Write-Host ' [!] claude 命令未找到 (已尝试 3 层 PATH 修复仍失败)' -ForegroundColor Red
|
2026-04-24 20:47:16 +08:00
|
|
|
|
Write-Host ''
|
2026-04-24 22:39:17 +08:00
|
|
|
|
Write-Host ' 诊断信息:' -ForegroundColor Yellow
|
|
|
|
|
|
Write-Host " npm prefix: $(try { (& npm config get prefix 2>$null) } catch { '(npm 不可用)' })" -ForegroundColor Gray
|
|
|
|
|
|
Write-Host ' PATH 片段 (npm/nodejs/pwsh/Git):' -ForegroundColor Gray
|
|
|
|
|
|
($env:Path -split ';') | Where-Object { $_ -match 'npm|nodejs|pwsh|Git' } | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
|
2026-04-24 20:47:16 +08:00
|
|
|
|
Write-Host ''
|
2026-04-24 22:39:17 +08:00
|
|
|
|
Write-Host ' 修复: 重新运行 Bookworm-Setup.exe (v3.0.9+) 即可自动补全' -ForegroundColor Green
|
2026-04-24 20:47:16 +08:00
|
|
|
|
Write-Host ''
|
2026-04-24 22:39:17 +08:00
|
|
|
|
Read-Host '按回车关闭'
|
2026-04-24 20:47:16 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
& claude --dangerously-skip-permissions
|
|
|
|
|
|
'@
|
|
|
|
|
|
|
|
|
|
|
|
# ─── Base64-UTF-16LE 编码 ─────────────────────────────────
|
|
|
|
|
|
$bytes = [System.Text.Encoding]::Unicode.GetBytes($plainScript)
|
|
|
|
|
|
$enc = [Convert]::ToBase64String($bytes)
|
|
|
|
|
|
|
|
|
|
|
|
# 健康检查
|
|
|
|
|
|
if ($enc.Length -gt 7500) { throw "Base64 长度 $($enc.Length) 超 bat 变量安全上限 7500" }
|
|
|
|
|
|
$bad = $enc -replace '[A-Za-z0-9+/=]', ''
|
|
|
|
|
|
if ($bad) { throw "Base64 含非法字符: [$bad]" }
|
|
|
|
|
|
|
|
|
|
|
|
Write-Host "[gen-launcher-bats] Base64 长度: $($enc.Length), 纯字符集检查 OK" -ForegroundColor Green
|
|
|
|
|
|
|
|
|
|
|
|
# ─── bat 1: 启动Bookworm.bat ──────────────────────────────
|
|
|
|
|
|
$launch = @"
|
|
|
|
|
|
@echo off
|
|
|
|
|
|
chcp 65001 > nul
|
|
|
|
|
|
cd /d "%~dp0"
|
|
|
|
|
|
|
|
|
|
|
|
:: 中转站在国内,不走代理
|
|
|
|
|
|
set NO_PROXY=bww.letcareme.com,code.letcareme.com,letcareme.com,localhost,127.0.0.1
|
|
|
|
|
|
set no_proxy=%NO_PROXY%
|
|
|
|
|
|
|
|
|
|
|
|
:: 静默自动更新 (bookworm-boot + .claude 配置, 失败不阻断启动)
|
|
|
|
|
|
echo [..] 检查更新...
|
|
|
|
|
|
git pull --rebase >nul 2>nul
|
|
|
|
|
|
git -C "%USERPROFILE%\.claude" pull --rebase >nul 2>nul
|
|
|
|
|
|
|
|
|
|
|
|
set USE_WT=0
|
|
|
|
|
|
where wt >nul 2>nul && set USE_WT=1
|
|
|
|
|
|
|
|
|
|
|
|
set USE_PWSH7=0
|
|
|
|
|
|
where pwsh >nul 2>nul && set USE_PWSH7=1
|
|
|
|
|
|
|
|
|
|
|
|
:: v3.0.6: Base64-UTF-16LE (PATH 重载 + DPAPI 凭证加载 + claude 诊断 + 启动)
|
|
|
|
|
|
:: 纯 A-Za-z0-9+/= 字符集, 避免 wt.exe 的 ';' 切 tab 误切 (修复 64856bc 症状一)
|
|
|
|
|
|
:: -d "%CD%" 无尾反斜杠, 避免 -d "%~dp0" 的转义引号 (修复 0c33109 症状二)
|
|
|
|
|
|
:: 重新生成: pwsh -NoProfile -File tools/gen-launcher-bats.ps1
|
|
|
|
|
|
set ENC=$enc
|
|
|
|
|
|
|
|
|
|
|
|
:: 优先路径: wt + pwsh7
|
|
|
|
|
|
if %USE_WT% equ 1 if %USE_PWSH7% equ 1 (
|
|
|
|
|
|
start "" wt new-tab --title "Bookworm Smart Assistant" -d "%CD%" -- pwsh -NoLogo -NoExit -EncodedCommand %ENC%
|
|
|
|
|
|
exit
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
:: 路径 2: wt + powershell 5.1
|
|
|
|
|
|
if %USE_WT% equ 1 if %USE_PWSH7% equ 0 (
|
|
|
|
|
|
start "" wt new-tab --title "Bookworm Smart Assistant" -d "%CD%" -- powershell -NoLogo -ExecutionPolicy Bypass -NoExit -EncodedCommand %ENC%
|
|
|
|
|
|
exit
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
:: 路径 3: conhost + pwsh7 (无 wt 就不会有 ; 切 tab 问题, 但仍用 Base64 统一)
|
|
|
|
|
|
if %USE_PWSH7% equ 1 (
|
|
|
|
|
|
start "Bookworm Smart Assistant" pwsh -NoLogo -NoExit -EncodedCommand %ENC%
|
|
|
|
|
|
exit
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
:: 路径 4: 回退 PowerShell 5.1 (最低保障, 交给 install.ps1 -StartOnly 处理)
|
|
|
|
|
|
title Bookworm Portable
|
|
|
|
|
|
echo.
|
|
|
|
|
|
echo [!] PowerShell 7 未安装, 使用 PowerShell 5.1
|
|
|
|
|
|
echo.
|
|
|
|
|
|
powershell -ExecutionPolicy Bypass -File install.ps1 -StartOnly -AutoAccept
|
|
|
|
|
|
if %errorlevel% neq 0 (
|
|
|
|
|
|
echo.
|
|
|
|
|
|
echo 启动失败,按任意键退出...
|
|
|
|
|
|
pause > nul
|
|
|
|
|
|
)
|
|
|
|
|
|
"@
|
|
|
|
|
|
|
|
|
|
|
|
# ─── bat 2: 更新并启动Bookworm.bat ───────────────────────
|
|
|
|
|
|
$update = @"
|
|
|
|
|
|
@echo off
|
|
|
|
|
|
chcp 65001 > nul
|
|
|
|
|
|
cd /d "%~dp0"
|
|
|
|
|
|
|
|
|
|
|
|
:: 中转站在国内,不走代理
|
|
|
|
|
|
set NO_PROXY=bww.letcareme.com,code.letcareme.com,letcareme.com,localhost,127.0.0.1
|
|
|
|
|
|
set no_proxy=%NO_PROXY%
|
|
|
|
|
|
|
|
|
|
|
|
:: 静默自动更新 (bookworm-boot + .claude 配置)
|
|
|
|
|
|
echo [..] 同步更新...
|
|
|
|
|
|
git pull --rebase >nul 2>nul
|
|
|
|
|
|
git -C "%USERPROFILE%\.claude" pull --rebase >nul 2>nul
|
|
|
|
|
|
|
|
|
|
|
|
:: v3.0.6: 同启动Bookworm.bat 的 Base64 (DPAPI + PATH 重载 + claude 启动)
|
|
|
|
|
|
set ENC=$enc
|
|
|
|
|
|
|
|
|
|
|
|
:: 检测 pwsh7 可用性
|
|
|
|
|
|
where pwsh >nul 2>nul
|
|
|
|
|
|
if %errorlevel% equ 0 (
|
|
|
|
|
|
:: pwsh7: 先同步配置 (SkipLaunch 不启动 claude), 再用 -EncodedCommand 在新窗口启动
|
|
|
|
|
|
pwsh -NoLogo -ExecutionPolicy Bypass -File "%~dp0install.ps1" -AutoAccept -SkipLaunch
|
|
|
|
|
|
start "Bookworm Smart Assistant" pwsh -NoLogo -NoExit -EncodedCommand %ENC%
|
|
|
|
|
|
exit
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
:: 回退 PowerShell 5.1: 一次调用完成更新+加载凭证+启动 (消除双次调用)
|
|
|
|
|
|
title Bookworm Portable
|
|
|
|
|
|
powershell -ExecutionPolicy Bypass -File "%~dp0install.ps1" -AutoAccept
|
|
|
|
|
|
if %errorlevel% neq 0 (
|
|
|
|
|
|
echo.
|
|
|
|
|
|
echo 启动失败,按任意键退出...
|
|
|
|
|
|
pause > nul
|
|
|
|
|
|
)
|
|
|
|
|
|
"@
|
|
|
|
|
|
|
|
|
|
|
|
# ─── 写入 ─────────────────────────────────────────────────
|
|
|
|
|
|
# bat 文件默认期望 GBK/ANSI, 但脚本顶部 chcp 65001 已切换到 UTF-8, 用无 BOM UTF-8 写入
|
|
|
|
|
|
[System.IO.File]::WriteAllText($launchBat, $launch, [System.Text.UTF8Encoding]::new($false))
|
|
|
|
|
|
[System.IO.File]::WriteAllText($updateBat, $update, [System.Text.UTF8Encoding]::new($false))
|
|
|
|
|
|
Write-Host "[gen-launcher-bats] ✓ 启动Bookworm.bat ($((Get-Item $launchBat).Length) bytes)" -ForegroundColor Green
|
|
|
|
|
|
Write-Host "[gen-launcher-bats] ✓ 更新并启动Bookworm.bat ($((Get-Item $updateBat).Length) bytes)" -ForegroundColor Green
|
|
|
|
|
|
|
|
|
|
|
|
# ─── Round-trip 验证 ─────────────────────────────────────
|
|
|
|
|
|
$decoded = [System.Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($enc))
|
|
|
|
|
|
$err = $null
|
|
|
|
|
|
[void][System.Management.Automation.Language.Parser]::ParseInput($decoded, [ref]$null, [ref]$err)
|
|
|
|
|
|
if ($err) { throw "解码后脚本 PARSE ERR: $($err[0])" }
|
|
|
|
|
|
Write-Host "[gen-launcher-bats] ✓ Round-trip PARSE OK ($($decoded.Length) chars)" -ForegroundColor Green
|