Windows: поддержка сборки MSIX (Microsoft Store и установщик с claude.com)

Скрипт не находил приложение, если оно стоит как пакет MSIX в
C:\Program Files\WindowsApps\Claude_<версия>_x64__pzs8sxrjxfjjc.

- Поиск через Get-AppxPackage с запасным путём по папке Packages.
- Запуск пакетного приложения по shell:AppsFolder\<PackageFamilyName>!Claude,
  запасные варианты: explorer.exe, alias в WindowsApps, протокол claude://.
- Сторонний режим у обеих сборок в %LOCALAPPDATA%\Claude-3p (по коду
  приложения 1.44121.2, в манифесте MSIX папка исключена из виртуализации).
- Флаг разработчика кладётся во все родные папки: %APPDATA%\Claude,
  LocalCache\Roaming\Claude у MSIX, %LOCALAPPDATA%\Claude.
- Честная проверка перезапуска и в setup.sh для Windows.
This commit is contained in:
2026-09-08 08:15:53 +03:00
parent fcf78a774f
commit ee06a11172
2 changed files with 216 additions and 30 deletions
+113 -14
View File
@@ -12,6 +12,13 @@
# в приложение Claude и перезапускает его. Ничего не скачивает # в приложение Claude и перезапускает его. Ничего не скачивает
# и не устанавливает. Терминальный Claude Code не затрагивается. # и не устанавливает. Терминальный Claude Code не затрагивается.
# #
# Понимает обе сборки приложения Claude для Windows:
# · классическую: %LOCALAPPDATA%\AnthropicClaude\app-<версия>\claude.exe;
# · MSIX (Microsoft Store и установщик с claude.com):
# C:\Program Files\WindowsApps\Claude_<версия>_x64__pzs8sxrjxfjjc\app\Claude.exe.
# Сторонний режим у обеих живёт в %LOCALAPPDATA%\Claude-3p (так в коде приложения),
# родная папка данных зависит от сборки, см. Get-ClaudeInstall.
#
# ВНИМАНИЕ при правке: сохранять в UTF-8 БЕЗ BOM. Gitea отдаёт файл # ВНИМАНИЕ при правке: сохранять в UTF-8 БЕЗ BOM. Gitea отдаёт файл
# с заголовком charset=utf-8, irm декодирует кириллицу правильно, # с заголовком charset=utf-8, irm декодирует кириллицу правильно,
# а BOM в конвейере irm | iex становится лишней командой и роняет разбор. # а BOM в конвейере irm | iex становится лишней командой и роняет разбор.
@@ -23,9 +30,6 @@ $Mode = ""
if ($env:AK) { $Mode = $env:AK; $env:AK = $null } if ($env:AK) { $Mode = $env:AK; $env:AK = $null }
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
$C1P = Join-Path $env:LOCALAPPDATA "Claude"
$C3P = "$C1P-3p"
$LIB = Join-Path $C3P "configLibrary"
$ID_OLLAMA = "a11a0000-0000-4000-8000-0000000000aa" $ID_OLLAMA = "a11a0000-0000-4000-8000-0000000000aa"
$ID_ZAI = "a11a0000-0000-4000-8000-0000000000bb" $ID_ZAI = "a11a0000-0000-4000-8000-0000000000bb"
$ID_DEFAULT = "a11a0000-0000-4000-8000-0000000000dd" $ID_DEFAULT = "a11a0000-0000-4000-8000-0000000000dd"
@@ -45,21 +49,110 @@ function Read-Json($path) {
try { Get-Content $path -Raw -Encoding UTF8 | ConvertFrom-Json } catch { $null } try { Get-Content $path -Raw -Encoding UTF8 | ConvertFrom-Json } catch { $null }
} }
# ---------- Где приложение и где его данные ----------
# Сборок две, и ведут они себя по-разному.
# · Классическая (Squirrel): %LOCALAPPDATA%\AnthropicClaude\app-<версия>\claude.exe,
# родные данные в %APPDATA%\Claude, логи в %LOCALAPPDATA%\Claude\logs.
# · MSIX (Microsoft Store и установщик с claude.com):
# C:\Program Files\WindowsApps\Claude_<версия>_x64__pzs8sxrjxfjjc\app\Claude.exe.
# Папка WindowsApps закрыта системой, exe оттуда напрямую не запускается, а всё,
# что приложение пишет в %APPDATA%, Windows подменяет на
# %LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude.
# Сторонний режим у ОБЕИХ сборок живёт в %LOCALAPPDATA%\Claude-3p: так прописано
# в коде приложения, а в манифесте MSIX эта папка исключена из подмены.
function Find-ClaudeExe { function Find-ClaudeExe {
@("$env:LOCALAPPDATA\AnthropicClaude", "$env:LOCALAPPDATA\Programs") | @("$env:LOCALAPPDATA\AnthropicClaude", "$env:LOCALAPPDATA\Programs") |
Where-Object { Test-Path $_ } |
ForEach-Object { Get-ChildItem $_ -Recurse -Filter "claude.exe" -ErrorAction SilentlyContinue } | ForEach-Object { Get-ChildItem $_ -Recurse -Filter "claude.exe" -ErrorAction SilentlyContinue } |
Select-Object -First 1 Select-Object -First 1
} }
function Find-ClaudeMsix {
$pkg = $null
try {
if (-not (Get-Command Get-AppxPackage -ErrorAction SilentlyContinue) -and $PSVersionTable.PSVersion.Major -ge 7) {
Import-Module Appx -UseWindowsPowerShell -ErrorAction SilentlyContinue
}
$all = @(Get-AppxPackage -Name "*Claude*" -ErrorAction SilentlyContinue |
Where-Object { $_.Name -eq "Claude" -or $_.Name -like "*ClaudeDesktop*" -or $_.Publisher -like "*Anthropic*" })
$pkg = $all | Where-Object { $_.Name -eq "Claude" } | Select-Object -First 1
if (-not $pkg) { $pkg = $all | Select-Object -First 1 }
} catch { $pkg = $null }
if ($pkg) {
$appId = "Claude"
try {
$ids = @((Get-AppxPackageManifest $pkg).Package.Applications.Application.Id)
if ($ids.Count -gt 0 -and $ids[0]) { $appId = "$($ids[0])" }
} catch { }
return [pscustomobject]@{ Family = "$($pkg.PackageFamilyName)"; Aumid = "$($pkg.PackageFamilyName)!$appId"; Version = "$($pkg.Version)" }
}
# Запасной путь: Get-AppxPackage бывает недоступен (PowerShell 7 без модуля Appx),
# но папка данных пакета остаётся на месте после первого запуска приложения.
$d = Get-ChildItem (Join-Path $env:LOCALAPPDATA "Packages") -Directory -Filter "Claude_*" -ErrorAction SilentlyContinue |
Select-Object -First 1
if ($d) { return [pscustomobject]@{ Family = $d.Name; Aumid = "$($d.Name)!Claude"; Version = "" } }
return $null
}
function Get-ClaudeInstall {
$msix = Find-ClaudeMsix
$exe = Find-ClaudeExe
if ($msix) {
$home1p = Join-Path $env:LOCALAPPDATA "Packages\$($msix.Family)\LocalCache\Roaming\Claude"
$launched = Test-Path $home1p
# Если рядом осталась и классическая сборка, верим той, которой пользовались.
if ($launched -or -not $exe) {
$label = "сборка MSIX (Microsoft Store или установщик с claude.com)"
if ($msix.Version) { $label += ", версия $($msix.Version)" }
return [pscustomobject]@{ Kind = "msix"; Label = $label; Aumid = $msix.Aumid; Exe = $null; Home = $home1p; Launched = $launched }
}
}
if ($exe) {
$home1p = Join-Path $env:APPDATA "Claude"
return [pscustomobject]@{ Kind = "classic"; Label = "классическая сборка"; Aumid = $null; Exe = $exe.FullName; Home = $home1p; Launched = (Test-Path $home1p) }
}
return $null
}
$INST = Get-ClaudeInstall
# Сторонний режим: конфиги, библиотека, флаг разработчика для стороннего режима.
$C3P = Join-Path $env:LOCALAPPDATA "Claude-3p"
$LIB = Join-Path $C3P "configLibrary"
# Родные папки: сюда кладём только флаг разработчика и чистим кэши. Их несколько,
# потому что настоящая зависит от сборки, а лишний флаг ничему не вредит.
$P1_DIRS = @()
if ($INST) { $P1_DIRS += $INST.Home }
foreach ($d in @((Join-Path $env:APPDATA "Claude"), (Join-Path $env:LOCALAPPDATA "Claude"), (Join-Path $env:LOCALAPPDATA "Claude-Data"))) {
if ($P1_DIRS -notcontains $d -and ((Test-Path $d) -or $P1_DIRS.Count -eq 0)) { $P1_DIRS += $d }
}
if ($P1_DIRS.Count -eq 0) { $P1_DIRS += (Join-Path $env:APPDATA "Claude") }
$ALL_DIRS = @($P1_DIRS) + @($C3P)
# Пакетное приложение запускают по идентификатору, а не по пути к exe.
function Start-Claude {
if ($INST.Kind -eq "msix") {
$target = "shell:AppsFolder\$($INST.Aumid)"
try { Start-Process $target -ErrorAction Stop; return } catch { }
try { Start-Process explorer.exe $target -ErrorAction Stop; return } catch { }
$alias = Join-Path $env:LOCALAPPDATA "Microsoft\WindowsApps\Claude.exe"
if (Test-Path $alias) { try { Start-Process $alias -ErrorAction Stop; return } catch { } }
try { Start-Process "claude://" -ErrorAction Stop } catch { }
return
}
if ($INST.Exe) { Start-Process $INST.Exe }
}
# ВАЖНО. Приложение читает developer_settings.json из ТЕКУЩЕЙ папки данных, # ВАЖНО. Приложение читает developer_settings.json из ТЕКУЩЕЙ папки данных,
# а она переключается на -3p только когда сторонний режим уже взлетел. # а она переключается на -3p только когда сторонний режим уже взлетел.
# Кладём флаг в ОБЕ папки, иначе пункт Developer не появится. # Кладём флаг и в родные папки, и в -3p, иначе пункт Developer не появится.
function Write-DevFlag { function Write-DevFlag {
foreach ($d in @($C1P, $C3P)) { foreach ($d in $ALL_DIRS) {
New-Item -ItemType Directory -Force -Path $d | Out-Null New-Item -ItemType Directory -Force -Path $d | Out-Null
Write-Utf8 (Join-Path $d "developer_settings.json") '{"allowDevTools": true}' Write-Utf8 (Join-Path $d "developer_settings.json") '{"allowDevTools": true}'
} }
Ok "Режим разработчика включён в обеих папках приложения." Ok "Режим разработчика включён во всех папках приложения."
} }
# "deploymentMode": "1p" намертво запрещает сторонний режим. # "deploymentMode": "1p" намертво запрещает сторонний режим.
@@ -81,7 +174,7 @@ function Clear-DeploymentPin {
function Repair-JsonBom { function Repair-JsonBom {
$files = @() $files = @()
if (Test-Path $LIB) { $files += Get-ChildItem $LIB -Filter *.json -ErrorAction SilentlyContinue } if (Test-Path $LIB) { $files += Get-ChildItem $LIB -Filter *.json -ErrorAction SilentlyContinue }
foreach ($d in @($C1P, $C3P)) { foreach ($d in $ALL_DIRS) {
foreach ($n in @("developer_settings.json", "claude_desktop_config.json")) { foreach ($n in @("developer_settings.json", "claude_desktop_config.json")) {
$f = Join-Path $d $n $f = Join-Path $d $n
if (Test-Path $f) { $files += Get-Item $f } if (Test-Path $f) { $files += Get-Item $f }
@@ -159,7 +252,7 @@ function Check-Applied {
# скиллы и наши конфиги остаются на месте. Делать только при закрытом приложении. # скиллы и наши конфиги остаются на месте. Делать только при закрытом приложении.
# ВАЖНО: не путать с соседним пунктом Reset App Data, тот сносит папку целиком. # ВАЖНО: не путать с соседним пунктом Reset App Data, тот сносит папку целиком.
function Clear-Caches { function Clear-Caches {
foreach ($d in @($C1P, $C3P)) { foreach ($d in $ALL_DIRS) {
if (-not (Test-Path $d)) { continue } if (-not (Test-Path $d)) { continue }
foreach ($c in @("Cache", "Code Cache", "GPUCache", "DawnGraphiteCache", "DawnWebGPUCache", "Service Worker")) { foreach ($c in @("Cache", "Code Cache", "GPUCache", "DawnGraphiteCache", "DawnWebGPUCache", "Service Worker")) {
Remove-Item (Join-Path $d $c) -Recurse -Force -ErrorAction SilentlyContinue Remove-Item (Join-Path $d $c) -Recurse -Force -ErrorAction SilentlyContinue
@@ -174,12 +267,12 @@ function Restart-Claude {
$i = 0 $i = 0
while ((Get-Process -Name "Claude*" -ErrorAction SilentlyContinue) -and $i -lt 12) { Start-Sleep 1; $i++ } while ((Get-Process -Name "Claude*" -ErrorAction SilentlyContinue) -and $i -lt 12) { Start-Sleep 1; $i++ }
Clear-Caches Clear-Caches
$exe = Find-ClaudeExe if (-not $INST) { Warn "Не нашёл приложение Claude, открой его вручную."; return }
if (-not $exe) { Warn "Не нашёл claude.exe, открой Claude вручную."; return } Start-Claude
Start-Process $exe.FullName
# Проверяем, что процесс реально поднялся: рапортовать успех вслепую нельзя. # Проверяем, что процесс реально поднялся: рапортовать успех вслепую нельзя.
# Пакетной сборке на старт нужно больше времени.
$j = 0 $j = 0
while (-not (Get-Process -Name "Claude*" -ErrorAction SilentlyContinue) -and $j -lt 8) { Start-Sleep 1; $j++ } while (-not (Get-Process -Name "Claude*" -ErrorAction SilentlyContinue) -and $j -lt 12) { Start-Sleep 1; $j++ }
if (Get-Process -Name "Claude*" -ErrorAction SilentlyContinue) { if (Get-Process -Name "Claude*" -ErrorAction SilentlyContinue) {
Ok "Claude перезапущен." Ok "Claude перезапущен."
} else { } else {
@@ -205,10 +298,16 @@ if ($Mode -eq "restore") {
# ---------- 1. Приложение Claude ---------- # ---------- 1. Приложение Claude ----------
Say "[1/4] Ищу приложение Claude..." Say "[1/4] Ищу приложение Claude..."
if (Find-ClaudeExe) { if ($INST) {
Ok "Claude на месте." Ok "Claude на месте: $($INST.Label)."
Ok "Конфиги стороннего режима: $C3P"
if (-not $INST.Launched) {
Warn "Похоже, приложение ещё ни разу не запускалось. Это не страшно, но если после"
Write-Host " запуска пункта Developer не будет, запусти эту же команду ещё раз."
}
} else { } else {
Warn "Приложение Claude не найдено, а без него настраивать нечего." Warn "Приложение Claude не найдено, а без него настраивать нечего."
Write-Host " Смотрел классическую сборку в AppData\Local и пакеты Microsoft Store."
Write-Host "" Write-Host ""
Write-Host " Поставь его руками, это одна минута:" Write-Host " Поставь его руками, это одна минута:"
Write-Host " 1. Открой https://claude.com/download и скачай версию для Windows." Write-Host " 1. Открой https://claude.com/download и скачай версию для Windows."
+103 -16
View File
@@ -26,13 +26,70 @@ case "$(uname -s)" in
*) echo "Эта система не поддерживается (нужен Мак или Windows с Git Bash)."; exit 1 ;; *) echo "Эта система не поддерживается (нужен Мак или Windows с Git Bash)."; exit 1 ;;
esac esac
# ---------- Обнаружение приложения (Windows) ----------
# Сборок две, и ведут они себя по-разному.
# · Классическая (Squirrel): %LOCALAPPDATA%\AnthropicClaude\app-<версия>\claude.exe,
# родные данные в %APPDATA%\Claude.
# · MSIX (Microsoft Store и установщик с claude.com): C:\Program Files\WindowsApps\
# Claude_<версия>_x64__pzs8sxrjxfjjc\app\Claude.exe. Папка закрыта системой, exe
# оттуда напрямую не запускается, приложение поднимают по идентификатору пакета,
# а родные данные Windows подменяет на
# %LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude.
# Сторонний режим у ОБЕИХ сборок живёт в %LOCALAPPDATA%\Claude-3p: так прописано
# в коде приложения, а в манифесте MSIX эта папка исключена из подмены.
WIN_KIND=""; WIN_AUMID=""; WIN_PFN=""; WIN_EXE=""; WIN_VER=""
WIN_PROBE='
$ErrorActionPreference = "SilentlyContinue"
$exe = @("$env:LOCALAPPDATA\AnthropicClaude", "$env:LOCALAPPDATA\Programs") | Where-Object { Test-Path $_ } |
ForEach-Object { Get-ChildItem $_ -Recurse -Filter "claude.exe" -ErrorAction SilentlyContinue } | Select-Object -First 1
$pkg = $null
try {
if (-not (Get-Command Get-AppxPackage -ErrorAction SilentlyContinue) -and $PSVersionTable.PSVersion.Major -ge 7) { Import-Module Appx -UseWindowsPowerShell -ErrorAction SilentlyContinue }
$all = @(Get-AppxPackage -Name "*Claude*" | Where-Object { $_.Name -eq "Claude" -or $_.Name -like "*ClaudeDesktop*" -or $_.Publisher -like "*Anthropic*" })
$pkg = $all | Where-Object { $_.Name -eq "Claude" } | Select-Object -First 1
if (-not $pkg) { $pkg = $all | Select-Object -First 1 }
} catch { }
$pfn = ""; $aumid = ""; $ver = ""
if ($pkg) {
$pfn = "$($pkg.PackageFamilyName)"; $ver = "$($pkg.Version)"; $id = "Claude"
try { $ids = @((Get-AppxPackageManifest $pkg).Package.Applications.Application.Id); if ($ids.Count -gt 0 -and $ids[0]) { $id = "$($ids[0])" } } catch { }
$aumid = "$pfn!$id"
} else {
$d = Get-ChildItem (Join-Path $env:LOCALAPPDATA "Packages") -Directory -Filter "Claude_*" | Select-Object -First 1
if ($d) { $pfn = $d.Name; $aumid = "$pfn!Claude" }
}
$kind = ""
if ($pfn) { $launched = Test-Path (Join-Path $env:LOCALAPPDATA "Packages\$pfn\LocalCache\Roaming\Claude"); if ($launched -or -not $exe) { $kind = "msix" } }
if (-not $kind -and $exe) { $kind = "classic" }
$exePath = ""; if ($exe) { $exePath = $exe.FullName }
Write-Output "$kind|$aumid|$pfn|$exePath|$ver"
'
win_probe() {
local out
out="$(powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "$WIN_PROBE" 2>/dev/null | tr -d '\r' | tail -n 1)" || out=""
IFS='|' read -r WIN_KIND WIN_AUMID WIN_PFN WIN_EXE WIN_VER <<EOF
$out
EOF
}
# ---------- Пути ----------
# P1_DIRS: родные папки приложения, туда идёт только флаг разработчика и чистка
# кэшей. Их несколько, потому что настоящая зависит от сборки, а лишний флаг
# ничему не вредит. C3P: папка стороннего режима, туда идут конфиги.
if [ "$OS" = "mac" ]; then if [ "$OS" = "mac" ]; then
C1P="$HOME/Library/Application Support/Claude" C3P="$HOME/Library/Application Support/Claude-3p"
P1_DIRS=("$HOME/Library/Application Support/Claude")
else else
C1P="$LOCALAPPDATA/Claude" win_probe
WLA="$(cygpath -u "$LOCALAPPDATA")"
WAP="$(cygpath -u "$APPDATA")"
C3P="$WLA/Claude-3p"
P1_DIRS=()
if [ "$WIN_KIND" = "msix" ]; then P1_DIRS+=("$WLA/Packages/$WIN_PFN/LocalCache/Roaming/Claude"); fi
for d in "$WAP/Claude" "$WLA/Claude" "$WLA/Claude-Data"; do
if [ -d "$d" ] || [ "${#P1_DIRS[@]}" -eq 0 ]; then P1_DIRS+=("$d"); fi
done
fi fi
# Папка стороннего режима это та же папка с суффиксом -3p.
C3P="${C1P}-3p"
LIB="$C3P/configLibrary" LIB="$C3P/configLibrary"
ID_OLLAMA="a11a0000-0000-4000-8000-0000000000aa" ID_OLLAMA="a11a0000-0000-4000-8000-0000000000aa"
@@ -73,11 +130,11 @@ have_ps() { command -v powershell.exe >/dev/null 2>&1; }
# Developer не будет. Поэтому пишем в ОБЕ папки. # Developer не будет. Поэтому пишем в ОБЕ папки.
write_dev_flag() { write_dev_flag() {
local d local d
for d in "$C1P" "$C3P"; do for d in "${P1_DIRS[@]}" "$C3P"; do
mkdir -p "$d" mkdir -p "$d"
printf '{"allowDevTools": true}\n' > "$d/developer_settings.json" printf '{"allowDevTools": true}\n' > "$d/developer_settings.json"
done done
ok "Режим разработчика включён в обеих папках приложения." ok "Режим разработчика включён во всех папках приложения."
} }
# ---------- deploymentMode ---------- # ---------- deploymentMode ----------
@@ -107,13 +164,14 @@ PY
# Windows (он по умолчанию сохраняет UTF-8 именно так) и из старых скриптов. # Windows (он по умолчанию сохраняет UTF-8 именно так) и из старых скриптов.
repair_json_bom() { repair_json_bom() {
[ -n "$PY" ] || return 0 [ -n "$PY" ] || return 0
"$PY" - "$LIB" "$C1P" "$C3P" <<'PY' "$PY" - "$LIB" "${P1_DIRS[@]}" "$C3P" <<'PY'
import os, sys import os, sys
lib, c1p, c3p = sys.argv[1:4] lib = sys.argv[1]
dirs = sys.argv[2:]
targets = [] targets = []
if os.path.isdir(lib): if os.path.isdir(lib):
targets += [os.path.join(lib, f) for f in os.listdir(lib) if f.endswith(".json")] targets += [os.path.join(lib, f) for f in os.listdir(lib) if f.endswith(".json")]
for d in (c1p, c3p): for d in dirs:
for n in ("developer_settings.json", "claude_desktop_config.json"): for n in ("developer_settings.json", "claude_desktop_config.json"):
p = os.path.join(d, n) p = os.path.join(d, n)
if os.path.isfile(p): targets.append(p) if os.path.isfile(p): targets.append(p)
@@ -225,7 +283,7 @@ PY
# ВАЖНО: не путать с соседним пунктом Reset App Data, тот сносит папку целиком. # ВАЖНО: не путать с соседним пунктом Reset App Data, тот сносит папку целиком.
clear_caches() { clear_caches() {
local d local d
for d in "$C1P" "$C3P"; do for d in "${P1_DIRS[@]}" "$C3P"; do
[ -d "$d" ] || continue [ -d "$d" ] || continue
rm -rf "$d/Cache" "$d/Code Cache" "$d/GPUCache" \ rm -rf "$d/Cache" "$d/Code Cache" "$d/GPUCache" \
"$d/DawnGraphiteCache" "$d/DawnWebGPUCache" "$d/Service Worker" 2>/dev/null || true "$d/DawnGraphiteCache" "$d/DawnWebGPUCache" "$d/Service Worker" 2>/dev/null || true
@@ -283,10 +341,33 @@ restart_claude() {
Get-Process -Name "Claude*" -ErrorAction SilentlyContinue | Stop-Process -Force Get-Process -Name "Claude*" -ErrorAction SilentlyContinue | Stop-Process -Force
Start-Sleep 3' 2>/dev/null || true Start-Sleep 3' 2>/dev/null || true
clear_caches clear_caches
powershell.exe -NoProfile -Command ' if [ "$WIN_KIND" = "msix" ]; then
$exe=@("$env:LOCALAPPDATA\AnthropicClaude","$env:LOCALAPPDATA\Programs") | ForEach-Object { Get-ChildItem $_ -Recurse -Filter "claude.exe" -ErrorAction SilentlyContinue } | Select-Object -First 1 # Пакетное приложение запускают по идентификатору, а не по пути к exe.
if($exe){ Start-Process $exe.FullName } else { exit 1 } AK_AUMID="$WIN_AUMID" powershell.exe -NoProfile -Command '
' || { warn "Не смог перезапустить Claude, закрой и открой его вручную."; return 0; } $t = "shell:AppsFolder\" + $env:AK_AUMID
try { Start-Process $t -ErrorAction Stop; exit 0 } catch { }
try { Start-Process explorer.exe $t -ErrorAction Stop; exit 0 } catch { }
$a = Join-Path $env:LOCALAPPDATA "Microsoft\WindowsApps\Claude.exe"
if (Test-Path $a) { try { Start-Process $a -ErrorAction Stop; exit 0 } catch { } }
try { Start-Process "claude://" -ErrorAction Stop; exit 0 } catch { }
exit 1
' || { warn "Не смог запустить Claude, открой его вручную из меню Пуск."; return 0; }
elif [ -n "$WIN_EXE" ]; then
AK_EXE="$WIN_EXE" powershell.exe -NoProfile -Command 'Start-Process $env:AK_EXE' \
|| { warn "Не смог перезапустить Claude, закрой и открой его вручную."; return 0; }
else
warn "Не нашёл приложение Claude, открой его вручную."; return 0
fi
# Проверяем, что процесс реально поднялся: рапортовать успех вслепую нельзя.
j=0
while [ "$j" -lt 12 ]; do
if powershell.exe -NoProfile -Command 'if (Get-Process -Name "Claude*" -ErrorAction SilentlyContinue) { exit 0 } else { exit 1 }' 2>/dev/null; then break; fi
sleep 1; j=$((j+1))
done
if [ "$j" -ge 12 ]; then
warn "Claude не поднялся автоматически. Конфиги на месте, открой приложение вручную."
return 0
fi
fi fi
ok "Claude перезапущен." ok "Claude перезапущен."
} }
@@ -339,11 +420,17 @@ fi
say "[1/4] Ищу приложение Claude..." say "[1/4] Ищу приложение Claude..."
CLAUDE_OK=false CLAUDE_OK=false
if [ "$OS" = "mac" ] && [ -d "/Applications/Claude.app" ]; then CLAUDE_OK=true; fi if [ "$OS" = "mac" ] && [ -d "/Applications/Claude.app" ]; then CLAUDE_OK=true; fi
if [ "$OS" = "win" ] && find "$LOCALAPPDATA/AnthropicClaude" "$LOCALAPPDATA/Programs" -maxdepth 3 -iname "claude.exe" 2>/dev/null | grep -q .; then CLAUDE_OK=true; fi if [ "$OS" = "win" ] && [ -n "$WIN_KIND" ]; then CLAUDE_OK=true; fi
if [ "$CLAUDE_OK" = "true" ]; then if [ "$CLAUDE_OK" = "true" ]; then
ok "Claude на месте." case "$WIN_KIND" in
msix) ok "Claude на месте: сборка MSIX (Microsoft Store или установщик с claude.com)${WIN_VER:+, версия $WIN_VER}." ;;
classic) ok "Claude на месте: классическая сборка." ;;
*) ok "Claude на месте." ;;
esac
ok "Конфиги стороннего режима: $C3P"
else else
warn "Приложение Claude не найдено, а без него настраивать нечего." warn "Приложение Claude не найдено, а без него настраивать нечего."
[ "$OS" = "win" ] && echo " Смотрел классическую сборку в AppData\\Local и пакеты Microsoft Store."
echo echo
echo " Поставь его руками, это одна минута:" echo " Поставь его руками, это одна минута:"
echo " 1. Открой https://claude.com/download и скачай версию для своей системы." echo " 1. Открой https://claude.com/download и скачай версию для своей системы."