diff --git a/setup.ps1 b/setup.ps1 new file mode 100644 index 0000000..56442bb --- /dev/null +++ b/setup.ps1 @@ -0,0 +1,172 @@ +# ============================================================= +# Claude на чужих мозгах — установщик (Windows) +# Что делает: проверяет Claude и Ollama, создаёт псевдонимы +# моделей, кладёт два конфига (Ollama Cloud + Z.ai) в приложение +# Claude и перезапускает его. +# Запуск одной командой (Windows, PowerShell): +# irm {URL}/setup.ps1 | iex +# Откат: +# $env:SETUP_MODE='restore'; irm {URL}/setup.ps1 | iex +# ============================================================= +param([string]$Mode = "install") +if ($env:SETUP_MODE) { $Mode = $env:SETUP_MODE; $env:SETUP_MODE = $null } +$ErrorActionPreference = "Stop" + +$C3P = Join-Path $env:LOCALAPPDATA "Claude-3p" +$LIB = Join-Path $C3P "configLibrary" +$ID_OLLAMA = "a11a0000-0000-4000-8000-0000000000aa" +$ID_ZAI = "a11a0000-0000-4000-8000-0000000000bb" +$ID_DEFAULT = "a11a0000-0000-4000-8000-0000000000dd" + +function Say($t) { Write-Host "`n$t" -ForegroundColor White } +function Ok($t) { Write-Host " [ok] $t" -ForegroundColor Green } +function Warn($t) { Write-Host " [!] $t" -ForegroundColor Yellow } + +function Read-Meta { + $metaPath = Join-Path $LIB "_meta.json" + if (Test-Path $metaPath) { Get-Content $metaPath -Raw | ConvertFrom-Json } + else { [pscustomobject]@{ appliedId = ""; entries = @() } } +} +function Write-Meta($meta) { + $meta | ConvertTo-Json -Depth 5 | Set-Content (Join-Path $LIB "_meta.json") -Encoding UTF8 +} +function Ensure-Entry($meta, $id, $name) { + $e = $meta.entries | Where-Object { $_.id -eq $id } + if ($e) { $e.name = $name } + else { $meta.entries = @($meta.entries) + [pscustomobject]@{ id = $id; name = $name } } + return $meta +} +function Restart-Claude { + Say "Перезапускаю приложение Claude..." + Get-Process -Name "Claude*" -ErrorAction SilentlyContinue | Stop-Process -Force + Start-Sleep 3 + Start-Process "claude://" -ErrorAction SilentlyContinue + if (-not $?) { + $exe = Get-ChildItem "$env:LOCALAPPDATA\Programs" -Recurse -Filter "Claude.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 + if ($exe) { Start-Process $exe.FullName } else { Warn "Открой Claude вручную." } + } + Ok "Готово. Открой выбор модели в чате: там GLM-5.3, Kimi K3 и Flash." +} + +# ---------- ОТКАТ ---------- +if ($Mode -eq "restore") { + Say "Откат на родной Claude..." + if (-not (Test-Path (Join-Path $LIB "_meta.json"))) { Ok "Конфигов нет, откатывать нечего."; exit } + $meta = Read-Meta + $def = $meta.entries | Where-Object { $_.name -eq "Default" } | Select-Object -First 1 + if (-not $def) { + "{}" | Set-Content (Join-Path $LIB "$ID_DEFAULT.json") -Encoding UTF8 + $meta = Ensure-Entry $meta $ID_DEFAULT "Default" + $def = $meta.entries | Where-Object { $_.id -eq $ID_DEFAULT } + } + $meta.appliedId = $def.id + Write-Meta $meta + Ok "Применён пустой конфиг Default: приложение вернётся к родному входу." + Restart-Claude + Write-Host "`nЕсли что-то осталось сломанным, полный сброс (сотрёт чаты стороннего режима):" + Write-Host " удали папки $env:LOCALAPPDATA\Claude-3p и $env:LOCALAPPDATA\Claude Nest-3p" + exit +} + +# ---------- 1. Claude ---------- +Say "[1/5] Проверяю приложение Claude..." +$claudeExe = Get-ChildItem "$env:LOCALAPPDATA\Programs" -Recurse -Filter "Claude.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 +if ($claudeExe) { Ok "Claude установлен." } +else { + Warn "Claude не найден. Открываю страницу загрузки..." + Start-Process "https://claude.com/download" + Write-Host " Установи Claude, запусти его один раз, войди в аккаунт, затем запусти скрипт снова." + exit +} + +# ---------- 2. Ollama ---------- +Say "[2/5] Проверяю программу Ollama..." +$haveOllama = [bool](Get-Command ollama -ErrorAction SilentlyContinue) +if ($haveOllama) { Ok "Ollama установлена." } +else { + $ans = Read-Host " Ollama не найдена. Скачать? Нужна для маршрута Ollama Cloud [Y/n]" + if ($ans -ne "n" -and $ans -ne "N") { + Start-Process "https://ollama.com/download/OllamaSetup.exe" + Write-Host " Установи Ollama, запусти, войди в аккаунт (Sign in), затем запусти скрипт снова." + exit + } else { Warn "Пропускаю Ollama: поставлю только конфиг Z.ai." } +} + +# ---------- 3. Псевдонимы ---------- +if ($haveOllama) { + Say "[3/5] Создаю псевдонимы облачных моделей (нужен вход в аккаунт Ollama)..." + $pairs = @( + @("glm-5.3:cloud", "claude-sonnet-4-6"), + @("kimi-k3:cloud", "claude-opus-4-6"), + @("glm-5.3-flash:cloud", "claude-haiku-4-5") + ) + foreach ($p in $pairs) { + & ollama pull $p[0] 2>$null + if ($LASTEXITCODE -ne 0) { + Warn "Не получилось скачать $($p[0]). Скорее всего, нет входа в аккаунт." + Write-Host " Открой окно Ollama -> Settings -> Sign in, затем запусти скрипт снова." + exit 1 + } + & ollama cp $p[0] $p[1] | Out-Null + Ok "$($p[1]) -> $($p[0])" + } +} else { Say "[3/5] Ollama нет, псевдонимы пропускаю." } + +# ---------- 4. Конфиги ---------- +Say "[4/5] Кладу конфиги в приложение Claude..." +New-Item -ItemType Directory -Force -Path $LIB | Out-Null +$devSettings = Join-Path $C3P "developer_settings.json" +if (-not (Test-Path $devSettings)) { '{"allowDevTools": true}' | Set-Content $devSettings -Encoding UTF8 } + +@' +{ + "inferenceProvider": "gateway", + "inferenceGatewayBaseUrl": "http://localhost:11434", + "inferenceGatewayApiKey": "ollama", + "inferenceGatewayAuthScheme": "bearer", + "inferenceCredentialKind": "static", + "modelDiscoveryEnabled": false, + "inferenceModels": [ + { "name": "claude-sonnet-4-6", "labelOverride": "GLM-5.3 (Ollama Cloud)", "anthropicFamilyTier": "sonnet", "isFamilyDefault": true }, + { "name": "claude-opus-4-6", "labelOverride": "Kimi K3 (Ollama Cloud)", "anthropicFamilyTier": "opus", "isFamilyDefault": true }, + { "name": "claude-haiku-4-5", "labelOverride": "GLM-5.3 Flash (Ollama Cloud)", "anthropicFamilyTier": "haiku", "isFamilyDefault": true } + ] +} +'@ | Set-Content (Join-Path $LIB "$ID_OLLAMA.json") -Encoding UTF8 + +@' +{ + "inferenceProvider": "gateway", + "inferenceGatewayBaseUrl": "https://api.z.ai/api/anthropic", + "inferenceGatewayApiKey": "ВСТАВЬ_СЮДА_СВОЙ_КЛЮЧ_ZAI", + "inferenceGatewayAuthScheme": "bearer", + "inferenceCredentialKind": "static", + "modelDiscoveryEnabled": false, + "inferenceModels": [ + { "name": "claude-sonnet-4-6", "labelOverride": "GLM-5.3 (Z.ai Coding Plan)", "anthropicFamilyTier": "sonnet", "isFamilyDefault": true }, + { "name": "claude-haiku-4-5", "labelOverride": "GLM-5.3 Flash (Z.ai, vision)", "anthropicFamilyTier": "haiku", "isFamilyDefault": true } + ] +} +'@ | Set-Content (Join-Path $LIB "$ID_ZAI.json") -Encoding UTF8 + +$meta = Read-Meta +if (-not ($meta.entries | Where-Object { $_.name -eq "Default" })) { + "{}" | Set-Content (Join-Path $LIB "$ID_DEFAULT.json") -Encoding UTF8 + $meta = Ensure-Entry $meta $ID_DEFAULT "Default" +} +$meta = Ensure-Entry $meta $ID_OLLAMA "ollama-cloud" +$meta = Ensure-Entry $meta $ID_ZAI "zai-glm" +if ($haveOllama) { $meta.appliedId = $ID_OLLAMA } +elseif (-not $meta.appliedId) { $meta.appliedId = $ID_DEFAULT } +Write-Meta $meta +Ok "Конфиги на месте." + +# ---------- 5. Рестарт ---------- +if ($Mode -eq "no-restart") { Warn "Перезапуск Claude пропущен." } else { Restart-Claude } + +Write-Host "" +Write-Host "Что дальше:" +Write-Host " · Модель выбирается в чате, рядом с полем ввода." +Write-Host " · Конфиг Z.ai лежит рядом (панель Developer -> Configure Third-Party Inference):" +Write-Host " выбери zai-glm, вставь свой ключ в Gateway API key, Apply Changes, перезапуск." +Write-Host " · Вернуть родной Claude: powershell -File setup-windows.ps1 restore"