fix(v3.0.9): Claude 装完 npm prefix 固化 + 三层 PATH + 快捷方式自验证
用户反馈 (核心): '不要客户端修修补补, EXE 必须一次对'
诊断: v3.0.8 Phase 1 全 OK, 但桌面快捷方式 claude.exe not found
user npm config get prefix = C:\Users\Administrator\AppData\Roaming\npm
但 Get-Command claude 在 PS 7 为空 → User PATH 没含这个目录
Node.js MSI 写 User PATH 时机/行为被企业镜像/防病毒/组策略干扰
三层根治:
F1: auto-setup.ps1 装完 Claude Code 后立即 `npm config get prefix` 查真实路径,
强制写入 User PATH (永久生效). 不依赖 npm 自己的 PATH 注入时机.
F2: gen-launcher-bats Base64 脚本加 3 层 PATH 修复:
层 1 Machine+User env PATH / 层 2 npm config get prefix 动态查询 /
层 3 硬编码候选 (%APPDATA%\npm / Program Files\nodejs / LOCALAPPDATA\npm)
F3: auto-setup.ps1:2256 创建桌面快捷方式前 Test-Cmd claude 自验证,
失败则拒绝创建 + 弹明确错误, 不再静默产出坏快捷方式
EXE 217088 → 220160 bytes (+3072)
Base64 3544 → 5880 chars
产品原则: 点快捷方式要么成功要么给清晰诊断, 不再给 'claude not found' 无头案
This commit is contained in:
parent
9fc7f1c9fa
commit
a3b4ff3a78
@ -48,7 +48,7 @@ trap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ─── 版本号 (每次更新递增, build.ps1 自动读取) ──────
|
# ─── 版本号 (每次更新递增, build.ps1 自动读取) ──────
|
||||||
$BWVersion = "3.0.8" # UX: PS7 升硬核 + per-dep 失败弹窗 + Phase 1 总结 + Claude 诊断增强
|
$BWVersion = "3.0.9" # 根治: Claude 装完固化 npm prefix 到 User PATH + Base64 三层 PATH + 快捷方式前 claude 自验证
|
||||||
|
|
||||||
# DryRun 模式日志标记
|
# DryRun 模式日志标记
|
||||||
if ($DryRun) { $global:BWDryRun = $DryRun } else { $global:BWDryRun = $null }
|
if ($DryRun) { $global:BWDryRun = $DryRun } else { $global:BWDryRun = $null }
|
||||||
@ -998,13 +998,42 @@ if ((Test-Cmd "git") -and -not (Test-Cmd "bash")) {
|
|||||||
|
|
||||||
# Claude Code 依赖 npm, 需要在 Node.js 安装后再检查
|
# Claude Code 依赖 npm, 需要在 Node.js 安装后再检查
|
||||||
# v3.0.8: 失败时写诊断到 bw-crash.log (node -v / npm -v / npm prefix / PATH 片段)
|
# v3.0.8: 失败时写诊断到 bw-crash.log (node -v / npm -v / npm prefix / PATH 片段)
|
||||||
|
# v3.0.9: 装完立即 `npm config get prefix` 拿真实路径 → 强制写入 User PATH (永久生效)
|
||||||
|
# 根治: 用户机器 Node.js MSI 安装时 User PATH 写入异常 → 快捷方式永远找不到 claude
|
||||||
if (-not (Test-Cmd "claude") -and (Test-Cmd "npm")) {
|
if (-not (Test-Cmd "claude") -and (Test-Cmd "npm")) {
|
||||||
Log-Info "安装 Claude Code..."
|
Log-Info "安装 Claude Code..."
|
||||||
$r = Run-CmdWithUI "npm" @("i", "-g", "@anthropic-ai/claude-code") "安装 Claude Code (首次约 2 分钟)" 180000
|
$r = Run-CmdWithUI "npm" @("i", "-g", "@anthropic-ai/claude-code") "安装 Claude Code (首次约 2 分钟)" 180000
|
||||||
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
||||||
# 补 npm 全局路径到 PATH (Windows 默认 %APPDATA%\npm)
|
|
||||||
$npmGlobal = "$env:APPDATA\npm"
|
# v3.0.9: 动态查询 npm 真实 prefix (兼容 nvm/fnm/Program Files/APPDATA 任意位置)
|
||||||
if ((Test-Path $npmGlobal) -and ($env:Path -notlike "*$npmGlobal*")) { $env:Path = "$npmGlobal;$env:Path" }
|
$npmPrefix = $null
|
||||||
|
try {
|
||||||
|
$npmPrefix = (& npm config get prefix 2>$null | Select-Object -First 1).Trim()
|
||||||
|
} catch {}
|
||||||
|
# 如果 npm prefix 查询失败, 回退常见候选位置
|
||||||
|
if (-not $npmPrefix -or -not (Test-Path $npmPrefix)) {
|
||||||
|
foreach ($p in @("$env:APPDATA\npm", "$env:ProgramFiles\nodejs", "$env:LOCALAPPDATA\npm")) {
|
||||||
|
if (Test-Path $p) { $npmPrefix = $p; break }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# 进程级立即生效
|
||||||
|
if ($npmPrefix -and ($env:Path -notlike "*$npmPrefix*")) { $env:Path = "$npmPrefix;$env:Path" }
|
||||||
|
# User 级永久写入 (根治未来任何新 shell 找不到 claude)
|
||||||
|
if ($npmPrefix) {
|
||||||
|
try {
|
||||||
|
$userPath = [System.Environment]::GetEnvironmentVariable("Path", "User")
|
||||||
|
if ($userPath -and $userPath -notlike "*$npmPrefix*") {
|
||||||
|
[System.Environment]::SetEnvironmentVariable("Path", "$userPath;$npmPrefix", "User")
|
||||||
|
Log-OK "npm 全局路径已永久写入 User PATH: $npmPrefix"
|
||||||
|
} elseif (-not $userPath) {
|
||||||
|
[System.Environment]::SetEnvironmentVariable("Path", $npmPrefix, "User")
|
||||||
|
Log-OK "创建 User PATH 含 npm 全局路径: $npmPrefix"
|
||||||
|
} else {
|
||||||
|
Log-Info "npm 全局路径已在 User PATH 中"
|
||||||
|
}
|
||||||
|
} catch { Log-Warn "写入 User PATH 失败 (不阻断, 进程级 PATH 已更新): $_" }
|
||||||
|
}
|
||||||
|
|
||||||
if (Test-Cmd "claude") {
|
if (Test-Cmd "claude") {
|
||||||
Log-OK "Claude Code 安装成功"
|
Log-OK "Claude Code 安装成功"
|
||||||
} else {
|
} else {
|
||||||
@ -2224,8 +2253,32 @@ try {
|
|||||||
# 关闭进度窗口
|
# 关闭进度窗口
|
||||||
Close-ProgressForm
|
Close-ProgressForm
|
||||||
|
|
||||||
# 创建桌面快捷方式
|
# v3.0.9: 创建桌面快捷方式前强制自验证 claude 可达 (防止交付半成品快捷方式)
|
||||||
New-DesktopShortcuts
|
# 已 Phase 1 + 补装 Claude 到这里仍 Test-Cmd false → 真的有问题, 立即弹窗让用户修,
|
||||||
|
# 而不是创建"点了没反应"的坏快捷方式误导用户
|
||||||
|
if (-not (Test-Cmd "claude")) {
|
||||||
|
# 尝试最后一次 PATH 重载 (可能 Phase 1 写 User PATH 后本进程还没 pickup)
|
||||||
|
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 {}
|
||||||
|
}
|
||||||
|
if (-not (Test-Cmd "claude")) {
|
||||||
|
$msg = "[CRITICAL] Claude Code 安装成功但命令不可达`n`n"
|
||||||
|
$msg += "这通常是 npm 全局路径未进 PATH 导致. 尝试修复:`n`n"
|
||||||
|
$msg += "1. 关闭本安装器 + 所有终端窗口`n"
|
||||||
|
$msg += "2. 打开新 PowerShell 跑:`n"
|
||||||
|
$msg += " `$p=[Environment]::GetEnvironmentVariable('Path','User')`n"
|
||||||
|
$msg += " [Environment]::SetEnvironmentVariable('Path',`$p+';'+(npm config get prefix),'User')`n"
|
||||||
|
$msg += "3. 再次关闭所有终端, 重新双击 Bookworm-Setup.exe`n`n"
|
||||||
|
$msg += "为避免交付无法启动的桌面快捷方式, 本次不创建快捷方式."
|
||||||
|
Show-MsgBox $msg "快捷方式创建已拒绝 — Claude 不可达" "OK" "Error"
|
||||||
|
Bw-Log "FAIL" "Claude 命令不可达, 跳过桌面快捷方式创建"
|
||||||
|
} else {
|
||||||
|
New-DesktopShortcuts
|
||||||
|
}
|
||||||
|
|
||||||
if ($allOK -and $env:ANTHROPIC_API_KEY) {
|
if ($allOK -and $env:ANTHROPIC_API_KEY) {
|
||||||
Bw-Log "DONE" "v$BWVersion 安装成功 ($skillCount Skills / $hookCount Hooks)"
|
Bw-Log "DONE" "v$BWVersion 安装成功 ($skillCount Skills / $hookCount Hooks)"
|
||||||
|
|||||||
@ -8,10 +8,32 @@ $repoRoot = Split-Path -Parent $PSScriptRoot
|
|||||||
$launchBat = Join-Path $repoRoot "启动Bookworm.bat"
|
$launchBat = Join-Path $repoRoot "启动Bookworm.bat"
|
||||||
$updateBat = Join-Path $repoRoot "更新并启动Bookworm.bat"
|
$updateBat = Join-Path $repoRoot "更新并启动Bookworm.bat"
|
||||||
|
|
||||||
# ─── 明文: DPAPI 加载 + PATH 重载 + claude 诊断 + 启动 ─────────
|
# ─── 明文: 三层 PATH 修复 + DPAPI 加载 + claude 诊断 + 启动 ─────────
|
||||||
|
# v3.0.9: 增加 npm config get prefix 动态查询, 兼容 nvm/fnm/Program Files 等非标准 npm 位置
|
||||||
$plainScript = @'
|
$plainScript = @'
|
||||||
Add-Type -AssemblyName System.Security
|
Add-Type -AssemblyName System.Security
|
||||||
|
# 层 1: Machine + User env PATH (标准 Windows 环境变量)
|
||||||
$env:Path = [Environment]::GetEnvironmentVariable('Path','Machine') + ';' + [Environment]::GetEnvironmentVariable('Path','User')
|
$env:Path = [Environment]::GetEnvironmentVariable('Path','Machine') + ';' + [Environment]::GetEnvironmentVariable('Path','User')
|
||||||
|
# 层 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 加载缓存凭证
|
||||||
$r = 'HKCU:\Software\Bookworm\CachedEnv'
|
$r = 'HKCU:\Software\Bookworm\CachedEnv'
|
||||||
try {
|
try {
|
||||||
(Get-ItemProperty $r -EA Stop).PSObject.Properties | Where-Object { $_.Name -match '^[A-Z_]+$' } | ForEach-Object {
|
(Get-ItemProperty $r -EA Stop).PSObject.Properties | Where-Object { $_.Name -match '^[A-Z_]+$' } | ForEach-Object {
|
||||||
@ -25,15 +47,16 @@ try {
|
|||||||
} catch {}
|
} catch {}
|
||||||
if (-not (Get-Command claude -ErrorAction SilentlyContinue)) {
|
if (-not (Get-Command claude -ErrorAction SilentlyContinue)) {
|
||||||
Write-Host ''
|
Write-Host ''
|
||||||
Write-Host ' [!] claude 命令未找到' -ForegroundColor Red
|
Write-Host ' [!] claude 命令未找到 (已尝试 3 层 PATH 修复仍失败)' -ForegroundColor Red
|
||||||
Write-Host ''
|
Write-Host ''
|
||||||
Write-Host ' 可能原因:' -ForegroundColor Yellow
|
Write-Host ' 诊断信息:' -ForegroundColor Yellow
|
||||||
Write-Host ' 1. Claude Code 未安装 - 跑: npm i -g @anthropic-ai/claude-code' -ForegroundColor Gray
|
Write-Host " npm prefix: $(try { (& npm config get prefix 2>$null) } catch { '(npm 不可用)' })" -ForegroundColor Gray
|
||||||
Write-Host ' 2. npm 全局路径不在 PATH - 重跑 Bookworm-Setup.exe 修复' -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 }
|
||||||
Write-Host ''
|
Write-Host ''
|
||||||
Write-Host ' 当前 PATH 片段 (诊断用):' -ForegroundColor Gray
|
Write-Host ' 修复: 重新运行 Bookworm-Setup.exe (v3.0.9+) 即可自动补全' -ForegroundColor Green
|
||||||
($env:Path -split ';') | Where-Object { $_ -match 'npm|nodejs|pwsh|Git' } | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
|
|
||||||
Write-Host ''
|
Write-Host ''
|
||||||
|
Read-Host '按回车关闭'
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
& claude --dangerously-skip-permissions
|
& claude --dangerously-skip-permissions
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user