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:
@@ -26,13 +26,70 @@ case "$(uname -s)" in
|
||||
*) echo "Эта система не поддерживается (нужен Мак или Windows с Git Bash)."; exit 1 ;;
|
||||
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
|
||||
C1P="$HOME/Library/Application Support/Claude"
|
||||
C3P="$HOME/Library/Application Support/Claude-3p"
|
||||
P1_DIRS=("$HOME/Library/Application Support/Claude")
|
||||
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
|
||||
# Папка стороннего режима это та же папка с суффиксом -3p.
|
||||
C3P="${C1P}-3p"
|
||||
LIB="$C3P/configLibrary"
|
||||
|
||||
ID_OLLAMA="a11a0000-0000-4000-8000-0000000000aa"
|
||||
@@ -73,11 +130,11 @@ have_ps() { command -v powershell.exe >/dev/null 2>&1; }
|
||||
# Developer не будет. Поэтому пишем в ОБЕ папки.
|
||||
write_dev_flag() {
|
||||
local d
|
||||
for d in "$C1P" "$C3P"; do
|
||||
for d in "${P1_DIRS[@]}" "$C3P"; do
|
||||
mkdir -p "$d"
|
||||
printf '{"allowDevTools": true}\n' > "$d/developer_settings.json"
|
||||
done
|
||||
ok "Режим разработчика включён в обеих папках приложения."
|
||||
ok "Режим разработчика включён во всех папках приложения."
|
||||
}
|
||||
|
||||
# ---------- deploymentMode ----------
|
||||
@@ -107,13 +164,14 @@ PY
|
||||
# Windows (он по умолчанию сохраняет UTF-8 именно так) и из старых скриптов.
|
||||
repair_json_bom() {
|
||||
[ -n "$PY" ] || return 0
|
||||
"$PY" - "$LIB" "$C1P" "$C3P" <<'PY'
|
||||
"$PY" - "$LIB" "${P1_DIRS[@]}" "$C3P" <<'PY'
|
||||
import os, sys
|
||||
lib, c1p, c3p = sys.argv[1:4]
|
||||
lib = sys.argv[1]
|
||||
dirs = sys.argv[2:]
|
||||
targets = []
|
||||
if os.path.isdir(lib):
|
||||
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"):
|
||||
p = os.path.join(d, n)
|
||||
if os.path.isfile(p): targets.append(p)
|
||||
@@ -225,7 +283,7 @@ PY
|
||||
# ВАЖНО: не путать с соседним пунктом Reset App Data, тот сносит папку целиком.
|
||||
clear_caches() {
|
||||
local d
|
||||
for d in "$C1P" "$C3P"; do
|
||||
for d in "${P1_DIRS[@]}" "$C3P"; do
|
||||
[ -d "$d" ] || continue
|
||||
rm -rf "$d/Cache" "$d/Code Cache" "$d/GPUCache" \
|
||||
"$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
|
||||
Start-Sleep 3' 2>/dev/null || true
|
||||
clear_caches
|
||||
powershell.exe -NoProfile -Command '
|
||||
$exe=@("$env:LOCALAPPDATA\AnthropicClaude","$env:LOCALAPPDATA\Programs") | ForEach-Object { Get-ChildItem $_ -Recurse -Filter "claude.exe" -ErrorAction SilentlyContinue } | Select-Object -First 1
|
||||
if($exe){ Start-Process $exe.FullName } else { exit 1 }
|
||||
' || { warn "Не смог перезапустить Claude, закрой и открой его вручную."; return 0; }
|
||||
if [ "$WIN_KIND" = "msix" ]; then
|
||||
# Пакетное приложение запускают по идентификатору, а не по пути к exe.
|
||||
AK_AUMID="$WIN_AUMID" powershell.exe -NoProfile -Command '
|
||||
$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
|
||||
ok "Claude перезапущен."
|
||||
}
|
||||
@@ -339,11 +420,17 @@ fi
|
||||
say "[1/4] Ищу приложение Claude..."
|
||||
CLAUDE_OK=false
|
||||
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
|
||||
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
|
||||
warn "Приложение Claude не найдено, а без него настраивать нечего."
|
||||
[ "$OS" = "win" ] && echo " Смотрел классическую сборку в AppData\\Local и пакеты Microsoft Store."
|
||||
echo
|
||||
echo " Поставь его руками, это одна минута:"
|
||||
echo " 1. Открой https://claude.com/download и скачай версию для своей системы."
|
||||
|
||||
Reference in New Issue
Block a user