Files
claude-brains/setup.sh
T

519 lines
25 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# =============================================================
# Claude на чужих мозгах — настройщик (Мак и Windows, один файл)
#
# Запуск (Мак, Терминал):
# curl -fsSL {URL}/setup.sh | bash -s ollama только Ollama
# curl -fsSL {URL}/setup.sh | bash -s zai только Z.ai
# curl -fsSL {URL}/setup.sh | bash -s both обе конфигурации
# curl -fsSL {URL}/setup.sh | bash спросит, что ставить
# curl -fsSL {URL}/setup.sh | bash -s restore вернуть родного Claude
#
# Что делает: находит УЖЕ УСТАНОВЛЕННЫЕ приложения, кладёт конфиги
# в приложение Claude и честно его перезапускает.
# НИЧЕГО НЕ СКАЧИВАЕТ И НЕ УСТАНАВЛИВАЕТ: Claude и Ollama ставятся
# руками по ссылкам из гайда. Трогает только приложение Claude,
# терминальный Claude Code не затрагивается.
#
# Флаг --no-restart: не перезапускать приложение.
# =============================================================
set -euo pipefail
# ---------- ОС и пути ----------
case "$(uname -s)" in
Darwin) OS=mac ;;
MINGW*|MSYS*|CYGWIN*) OS=win ;;
*) echo "Эта система не поддерживается (нужен Мак или Windows с Git Bash)."; exit 1 ;;
esac
if [ "$OS" = "mac" ]; then
C1P="$HOME/Library/Application Support/Claude"
else
C1P="$LOCALAPPDATA/Claude"
fi
# Папка стороннего режима это та же папка с суффиксом -3p.
C3P="${C1P}-3p"
LIB="$C3P/configLibrary"
ID_OLLAMA="a11a0000-0000-4000-8000-0000000000aa"
ID_ZAI="a11a0000-0000-4000-8000-0000000000bb"
ID_DEFAULT="a11a0000-0000-4000-8000-0000000000dd"
ZAI_PLACEHOLDER="ВСТАВЬ_СЮДА_СВОЙ_КЛЮЧ_ZAI"
MODE=install
BRAIN="${AK:-}"
NO_RESTART=false
for a in "$@"; do
case "$a" in
restore) MODE=restore ;;
ollama|zai|both) BRAIN="$a" ;;
--no-restart) NO_RESTART=true ;;
esac
done
[ "$BRAIN" = "restore" ] && { MODE=restore; BRAIN=""; }
say() { printf '\n\033[1m%s\033[0m\n' "$*"; }
ok() { printf ' \033[32m[ok]\033[0m %s\n' "$*"; }
warn() { printf ' \033[33m[!]\033[0m %s\n' "$*"; }
die() { printf ' \033[31m[стоп]\033[0m %s\n' "$*"; exit 1; }
# ---------- JSON-хелпер ----------
PY=""
if command -v python3 >/dev/null 2>&1; then PY=python3
elif command -v python >/dev/null 2>&1; then PY=python
fi
have_ps() { command -v powershell.exe >/dev/null 2>&1; }
[ -n "$PY" ] || have_ps || die "Нужен python3 (Мак) или powershell (Windows) для работы с JSON."
# ---------- Флаг разработчика ----------
# ВАЖНО. Приложение читает developer_settings.json из ТЕКУЩЕЙ папки данных,
# а она переключается на -3p только когда сторонний режим уже взлетел.
# Конфиги при этом всегда читаются из -3p. Замкнутый круг: если положить
# флаг только в -3p, до переключения приложение его не увидит и пункта
# Developer не будет. Поэтому пишем в ОБЕ папки.
write_dev_flag() {
local d
for d in "$C1P" "$C3P"; do
mkdir -p "$d"
printf '{"allowDevTools": true}\n' > "$d/developer_settings.json"
done
ok "Режим разработчика включён в обеих папках приложения."
}
# ---------- deploymentMode ----------
# Ключ "deploymentMode": "1p" намертво запрещает сторонний режим.
clear_deployment_pin() {
local f="$C3P/claude_desktop_config.json"
[ -f "$f" ] || return 0
[ -n "$PY" ] || return 0
"$PY" - "$f" <<'PY'
import json, sys
p = sys.argv[1]
try:
d = json.load(open(p, encoding="utf-8"))
except Exception:
sys.exit(0)
if isinstance(d, dict) and d.get("deploymentMode") == "1p":
d.pop("deploymentMode", None)
json.dump(d, open(p, "w", encoding="utf-8"), indent=2, ensure_ascii=False)
print("снят")
PY
}
# ---------- Чистка BOM ----------
# Приложение падает на BOM: в логе видно
# [managedConfig] Failed to read config library meta: Unexpected token
# и тогда ВСЯ библиотека конфигов не читается. BOM приезжает из Блокнота на
# Windows (он по умолчанию сохраняет UTF-8 именно так) и из старых скриптов.
repair_json_bom() {
[ -n "$PY" ] || return 0
"$PY" - "$LIB" "$C1P" "$C3P" <<'PY'
import os, sys
lib, c1p, c3p = sys.argv[1:4]
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 n in ("developer_settings.json", "claude_desktop_config.json"):
p = os.path.join(d, n)
if os.path.isfile(p): targets.append(p)
fixed = 0
for p in targets:
try:
b = open(p, "rb").read()
if b.startswith(b"\xef\xbb\xbf"):
open(p, "wb").write(b[3:]); fixed += 1
except Exception:
pass
if fixed: print(fixed)
PY
}
# merge_meta <applied: ollama|zai|default> <routes: ollama|zai|both|none>
merge_meta() {
local applied="$1" routes="$2"
if [ -n "$PY" ]; then
"$PY" - "$LIB" "$ID_OLLAMA" "$ID_ZAI" "$ID_DEFAULT" "$applied" "$routes" <<'PY'
import json, os, sys
lib, id_o, id_z, id_d, applied, routes = sys.argv[1:7]
p = os.path.join(lib, "_meta.json")
try:
meta = json.load(open(p, encoding="utf-8"))
if not isinstance(meta, dict): meta = {}
except Exception:
meta = {}
ent = meta.get("entries") or []
def ensure(i, n):
e = next((x for x in ent if x.get("id") == i), None)
if e: e["name"] = n
else: ent.append({"id": i, "name": n})
# Default обязан существовать как пустой конфиг: это точка возврата.
dflt = next((x for x in ent if x.get("name") == "Default"), None)
if dflt is None:
open(os.path.join(lib, id_d + ".json"), "w", encoding="utf-8").write("{}")
ensure(id_d, "Default")
dflt_id = id_d
else:
dflt_id = dflt["id"]
if routes in ("ollama", "both"): ensure(id_o, "ollama-cloud")
if routes in ("zai", "both"): ensure(id_z, "zai-glm")
# Выкидываем записи, у которых файла на диске нет: они ломают выбор в панели.
ent = [x for x in ent if os.path.exists(os.path.join(lib, x.get("id", "") + ".json"))]
meta["entries"] = ent
meta["appliedId"] = {"ollama": id_o, "zai": id_z}.get(applied, dflt_id)
json.dump(meta, open(p, "w", encoding="utf-8"), indent=2, ensure_ascii=False)
PY
else
MERGE_LIB="$(cygpath -w "$LIB")" MERGE_APPLIED="$applied" MERGE_ROUTES="$routes" \
MERGE_IDO="$ID_OLLAMA" MERGE_IDZ="$ID_ZAI" MERGE_IDD="$ID_DEFAULT" \
powershell.exe -NoProfile -Command '
$lib=$env:MERGE_LIB; $p=Join-Path $lib "_meta.json"
if (Test-Path $p) { $meta=Get-Content $p -Raw -Encoding UTF8 | ConvertFrom-Json }
else { $meta=[pscustomobject]@{appliedId="";entries=@()} }
function Ensure($id,$name){
$e=$script:meta.entries | Where-Object { $_.id -eq $id }
if($e){$e.name=$name}
else{$script:meta.entries=@($script:meta.entries)+[pscustomobject]@{id=$id;name=$name}}
}
if(-not ($meta.entries | Where-Object {$_.name -eq "Default"})){
"{}" | Set-Content (Join-Path $lib ($env:MERGE_IDD+".json")) -Encoding UTF8
Ensure $env:MERGE_IDD "Default"
}
if($env:MERGE_ROUTES -eq "ollama" -or $env:MERGE_ROUTES -eq "both"){ Ensure $env:MERGE_IDO "ollama-cloud" }
if($env:MERGE_ROUTES -eq "zai" -or $env:MERGE_ROUTES -eq "both"){ Ensure $env:MERGE_IDZ "zai-glm" }
$meta.entries = @($meta.entries | Where-Object { Test-Path (Join-Path $lib ($_.id+".json")) })
$def=($meta.entries | Where-Object {$_.name -eq "Default"} | Select-Object -First 1).id
switch($env:MERGE_APPLIED){
"ollama" { $meta.appliedId=$env:MERGE_IDO }
"zai" { $meta.appliedId=$env:MERGE_IDZ }
default { $meta.appliedId=$def }
}
$meta | ConvertTo-Json -Depth 5 | Set-Content $p -Encoding UTF8
'
fi
}
# Проверяем, что применённый конфиг действительно включает сторонний режим.
check_applied() {
[ -n "$PY" ] || return 0
"$PY" - "$LIB" <<'PY'
import json, os, sys
lib = sys.argv[1]
try:
meta = json.load(open(os.path.join(lib, "_meta.json"), encoding="utf-8"))
applied = meta.get("appliedId", "")
cfg = json.load(open(os.path.join(lib, applied + ".json"), encoding="utf-8"))
except Exception as e:
print("НЕТ: не смог прочитать конфиг (%s)" % e); sys.exit(0)
name = next((x.get("name") for x in meta.get("entries", []) if x.get("id") == applied), applied)
print("ДА: %s" % name if cfg.get("inferenceProvider") else "НЕТ: применён пустой конфиг «%s»" % name)
PY
}
# ---------- Чистка кэшей ----------
# Повторяет пункт меню Developer -> Clear Cache and Restart. В коде приложения он
# чистит ТОЛЬКО кэши: HTTP, скомпилированный JS, шейдеры, service workers, DNS.
# Ни cookies, ни Local Storage там нет, поэтому вход в аккаунт, чаты, MCP,
# скиллы и наши конфиги остаются на месте. Делать это можно лишь при закрытом
# приложении, иначе файлы заняты.
# ВАЖНО: не путать с соседним пунктом Reset App Data, тот сносит папку целиком.
clear_caches() {
local d
for d in "$C1P" "$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
done
ok "Кэши очищены (чаты, входы и конфиги не тронуты)."
}
# ---------- Обнаружение приложения (Мак) ----------
# pgrep тут ненадёжен: под песочницей он не видит процесс, хотя тот живой.
# Единственный путь приложения, который нам нужен, это главный бинарь в
# /Applications. Рядом крутится встроенный агент по похожему пути внутри
# папки данных, и его трогать нельзя, поэтому сравниваем строго целиком.
CLAUDE_BIN="/Applications/Claude.app/Contents/MacOS/Claude"
claude_pids() { ps -Ao pid=,comm= | awk -v b="$CLAUDE_BIN" '$2==b {print $1}'; }
claude_running() {
case "$(osascript -e 'application "Claude" is running' 2>/dev/null)" in
true) return 0 ;;
esac
[ -n "$(claude_pids)" ]
}
# ---------- Честный перезапуск ----------
# open -a на живом процессе только активирует окно: приложение не завершается
# при закрытии окна и настройки перечитывает лишь при настоящем старте.
restart_claude() {
if [ "$NO_RESTART" = "true" ]; then warn "Перезапуск пропущен (--no-restart). Настройки применятся после Cmd+Q и запуска."; return 0; fi
say "Перезапускаю приложение Claude..."
if [ "$OS" = "mac" ]; then
if claude_running; then
osascript -e 'tell application "Claude" to quit' >/dev/null 2>&1 || true
i=0; while claude_running && [ "$i" -lt 12 ]; do sleep 1; i=$((i+1)); done
if claude_running; then
for pid in $(claude_pids); do kill "$pid" 2>/dev/null || true; done
sleep 3
fi
if claude_running; then
warn "Claude не закрылся сам (обычно это запрет на автоматизацию:"
echo " Системные настройки -> Конфиденциальность и безопасность -> Автоматизация)."
echo " Конфиги уже на месте. Закрой Claude вручную через Cmd+Q и открой заново."
return 0
fi
fi
clear_caches
open -a "Claude" >/dev/null 2>&1 || { warn "Не смог открыть Claude, открой вручную."; return 0; }
# Проверяем, что процесс реально поднялся: рапортовать успех вслепую нельзя.
j=0; while ! claude_running && [ "$j" -lt 10 ]; do sleep 1; j=$((j+1)); done
if ! claude_running; then
warn "Claude не поднялся автоматически. Конфиги на месте, открой приложение вручную."
return 0
fi
else
powershell.exe -NoProfile -Command '
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; }
fi
ok "Claude перезапущен."
}
# ---------- TLS-фикс песочницы (только Мак, для вкладки Code) ----------
apply_tls_fix() {
[ "$OS" = "mac" ] || return 0
[ -n "$PY" ] || return 0
local sj="$HOME/.claude/settings.json"
mkdir -p "$HOME/.claude"
[ -f "$sj" ] || echo '{}' > "$sj"
"$PY" - "$sj" <<'PY'
import json, sys
p = sys.argv[1]
try:
d = json.load(open(p, encoding="utf-8"))
if not isinstance(d, dict): d = {}
except Exception:
d = {}
sb = d.get("sandbox")
if not isinstance(sb, dict): sb = {}
if sb.get("enableWeakerNetworkIsolation") is not True:
sb["enableWeakerNetworkIsolation"] = True
d["sandbox"] = sb
json.dump(d, open(p, "w", encoding="utf-8"), indent=2, ensure_ascii=False)
print("добавлен")
else:
print("уже был")
PY
}
# ---------- ОТКАТ ----------
if [ "$MODE" = "restore" ]; then
say "Откат на родного Claude..."
if [ ! -f "$LIB/_meta.json" ]; then ok "Конфигов нет, откатывать нечего."; exit 0; fi
merge_meta default none
repair_json_bom >/dev/null 2>&1 || true
ok "Применён пустой профиль Default."
echo " Если приложение всё равно открывается на чужом мозге: меню аккаунта внизу слева -> Sign out,"
echo " затем на экране входа выбери обычный вход по почте или Google."
write_dev_flag
restart_claude
echo
echo "Если что-то осталось сломанным, полный сброс (сотрёт чаты стороннего режима):"
echo " rm -rf \"$C3P\""
exit 0
fi
# ---------- 1. Приложение Claude ----------
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 [ "$CLAUDE_OK" = "true" ]; then
ok "Claude на месте."
else
warn "Приложение Claude не найдено, а без него настраивать нечего."
echo
echo " Поставь его руками, это одна минута:"
echo " 1. Открой https://claude.com/download и скачай версию для своей системы."
echo " 2. Установи и запусти приложение хотя бы один раз."
echo " 3. Вернись сюда и запусти эту же команду снова."
echo
echo " Логиниться в аккаунт Anthropic не обязательно."
exit 1
fi
# ---------- 2. Выбор мозга ----------
if [ -z "$BRAIN" ]; then
say "[2/4] Какой мозг подключаем?"
echo " 1) Ollama бесплатный старт, программа Ollama должна быть установлена и запущена"
echo " 2) Z.ai по подписке от \$18, локально ничего не нужно, только ключ"
echo " 3) оба положу обе конфигурации, переключаться будешь в приложении"
printf " Твой выбор [1/2/3, по умолчанию 3]: "
choice=""
[ -r /dev/tty ] && { read -r choice < /dev/tty || choice=""; }
case "${choice:-3}" in
1) BRAIN=ollama ;;
2) BRAIN=zai ;;
*) BRAIN=both ;;
esac
else
say "[2/4] Режим: $BRAIN"
fi
case "$BRAIN" in
ollama) ok "Ставлю только Ollama." ;;
zai) ok "Ставлю только Z.ai." ;;
both) ok "Ставлю обе конфигурации." ;;
*) warn "Непонятный режим «$BRAIN», беру оба."; BRAIN=both ;;
esac
# ---------- 3. Ollama и псевдонимы ----------
HAVE_OLLAMA=false
if [ "$BRAIN" = "ollama" ] || [ "$BRAIN" = "both" ]; then
say "[3/4] Проверяю программу Ollama..."
OLLAMA_BIN=""
if command -v ollama >/dev/null 2>&1; then OLLAMA_BIN="ollama"
elif [ "$OS" = "mac" ] && [ -x "/Applications/Ollama.app/Contents/Resources/ollama" ]; then OLLAMA_BIN="/Applications/Ollama.app/Contents/Resources/ollama"
fi
if [ -z "$OLLAMA_BIN" ]; then
warn "Ollama не найдена."
echo
echo " Поставь её руками:"
echo " 1. Открой https://ollama.com/download и скачай версию для своей системы."
echo " 2. Установи, запусти, войди в аккаунт (иконка ламы -> Sign in)."
echo " 3. Вернись сюда и запусти команду снова."
echo
if [ "$BRAIN" = "ollama" ]; then
echo " Или подключи Z.ai, ему Ollama не нужна: та же команда со словом zai вместо ollama."
exit 1
fi
warn "Пропускаю Ollama, кладу только конфиг Z.ai."
BRAIN=zai
else
ok "Ollama на месте."
[ "$OS" = "mac" ] && { open -a "Ollama" >/dev/null 2>&1 || true; }
HAVE_OLLAMA=true
say " Создаю псевдонимы облачных моделей (нужен вход в аккаунт Ollama)..."
# Приложение пускает только точные идентификаторы моделей Claude,
# поэтому облачная модель получает второе, «правильное» имя.
make_alias() {
"$OLLAMA_BIN" pull "$1" >/dev/null 2>&1 || {
if [ "${3:-}" = "optional" ]; then
warn "$1 недоступна на твоём тарифе Ollama, пропускаю (нужен Pro или Max)."
return 0
fi
warn "Не получилось скачать $1. Обычно это нет входа в аккаунт Ollama."
echo " Открой окно Ollama (иконка ламы) -> Sign in, затем запусти команду снова."
exit 1
}
"$OLLAMA_BIN" cp "$1" "$2" >/dev/null
ok "$2 -> $1"
}
make_alias "glm-5.3:cloud" "claude-sonnet-4-6"
make_alias "kimi-k3:cloud" "claude-opus-4-6"
make_alias "glm-5.3-flash:cloud" "claude-haiku-4-5"
make_alias "qwen3.5:397b-cloud" "claude-sonnet-4-5" optional
make_alias "deepseek-v4-pro:0813-cloud" "claude-opus-4-1" optional
make_alias "gpt-oss:120b-cloud" "claude-haiku-3-5" optional
make_alias "minimax-m3:cloud" "claude-3-7-sonnet" optional
make_alias "nemotron-3-super:cloud" "claude-3-5-haiku" optional
fi
else
say "[3/4] Маршрут Z.ai, Ollama не нужна."
fi
# ---------- 4. Конфиги ----------
say "[4/4] Кладу конфиги в приложение Claude..."
mkdir -p "$LIB"
write_dev_flag
pin="$(clear_deployment_pin || true)"
[ -n "$pin" ] && ok "Снят запрет на сторонний режим (deploymentMode)."
if [ "$BRAIN" = "ollama" ] || [ "$BRAIN" = "both" ]; then
cat > "$LIB/$ID_OLLAMA.json" <<'JSON'
{
"inferenceProvider": "gateway",
"inferenceGatewayBaseUrl": "http://127.0.0.1:11434",
"inferenceGatewayApiKey": "ollama",
"inferenceGatewayAuthScheme": "bearer",
"inferenceCredentialKind": "static",
"modelDiscoveryEnabled": false,
"inferenceModels": [
{ "name": "claude-sonnet-4-6", "labelOverride": "GLM-5.3 (Ollama)", "anthropicFamilyTier": "sonnet", "isFamilyDefault": true },
{ "name": "claude-opus-4-6", "labelOverride": "Kimi K3 (Ollama)", "anthropicFamilyTier": "opus", "isFamilyDefault": true },
{ "name": "claude-haiku-4-5", "labelOverride": "GLM-5.3 Flash (Ollama)", "anthropicFamilyTier": "haiku", "isFamilyDefault": true },
{ "name": "claude-sonnet-4-5", "labelOverride": "Qwen3.5 397B (Ollama)", "anthropicFamilyTier": "sonnet", "isFamilyDefault": false },
{ "name": "claude-opus-4-1", "labelOverride": "DeepSeek V4 Pro (Ollama)", "anthropicFamilyTier": "opus", "isFamilyDefault": false },
{ "name": "claude-haiku-3-5", "labelOverride": "GPT-OSS 120B (Ollama)", "anthropicFamilyTier": "haiku", "isFamilyDefault": false },
{ "name": "claude-3-7-sonnet", "labelOverride": "MiniMax M3 (Ollama)", "anthropicFamilyTier": "sonnet", "isFamilyDefault": false },
{ "name": "claude-3-5-haiku", "labelOverride": "Nemotron 3 Super (Ollama)", "anthropicFamilyTier": "haiku", "isFamilyDefault": false }
]
}
JSON
ok "Конфиг ollama-cloud на месте."
fi
if [ "$BRAIN" = "zai" ] || [ "$BRAIN" = "both" ]; then
if [ -f "$LIB/$ID_ZAI.json" ] && ! grep -q "$ZAI_PLACEHOLDER" "$LIB/$ID_ZAI.json"; then
ok "Конфиг zai-glm уже настроен, ключ не трогаю."
else
cat > "$LIB/$ID_ZAI.json" <<JSON
{
"inferenceProvider": "gateway",
"inferenceGatewayBaseUrl": "https://api.z.ai/api/anthropic",
"inferenceGatewayApiKey": "$ZAI_PLACEHOLDER",
"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)", "anthropicFamilyTier": "haiku", "isFamilyDefault": true }
]
}
JSON
ok "Конфиг zai-glm на месте, ждёт твой ключ."
fi
fi
# Активной делаем ту конфигурацию, которая реально готова работать.
if [ "$HAVE_OLLAMA" = "true" ]; then merge_meta ollama "$BRAIN"; else merge_meta zai "$BRAIN"; fi
nbom="$(repair_json_bom || true)"
[ -n "$nbom" ] && ok "Убран BOM у $nbom файлов конфигурации (приложение их не читало)."
if [ "$OS" = "mac" ]; then
ok "TLS-фикс песочницы для вкладки Code: $(apply_tls_fix)"
fi
# ---------- Самопроверка ----------
say "Проверяю, что получилось..."
res="$(check_applied || true)"
case "$res" in
ДА:*) ok "Сторонний режим включится: ${res#ДА: }" ;;
НЕТ:*) warn "${res#НЕТ: }"
echo " Открой Developer -> Configure Third-Party Inference и выбери конфигурацию вручную." ;;
esac
restart_claude
# ---------- Что дальше ----------
echo
echo "Что дальше:"
if [ "$HAVE_OLLAMA" = "true" ]; then
echo " · Открой новый чат и кликни по имени модели рядом с полем ввода:"
echo " там GLM-5.3 и GLM-5.3 Flash, а на платном тарифе Ollama ещё и Kimi K3."
echo " · Программа Ollama должна быть запущена: закрыл её, мост отвалился."
fi
if [ "$BRAIN" = "zai" ] || [ "$BRAIN" = "both" ]; then
echo " · Z.ai: возьми ключ на z.ai/manage-apikey/apikey-list, затем в приложении"
echo " Developer -> Configure Third-Party Inference -> конфигурация zai-glm ->"
echo " вставь ключ в Gateway API key -> Apply Changes -> перезапусти Claude."
fi
echo " · Нет пункта Developer? Полностью выйди из Claude (Cmd+Q) и открой заново."
echo " · Вернуть родного Claude: та же команда со словом restore."