Files
claude-brains/setup.sh
T

292 lines
14 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
# Откат:
# curl -fsSL {URL}/setup.sh | bash -s -- restore
# Что делает: проверяет Claude и Ollama, создаёт псевдонимы
# моделей, кладёт два конфига (Ollama Cloud + Z.ai), перезапускает
# Claude. Скрипт открыт и короток: прочитай перед запуском.
# =============================================================
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
C3P="$HOME/Library/Application Support/Claude-3p"
else
C3P="$LOCALAPPDATA/Claude-3p"
fi
LIB="$C3P/configLibrary"
ID_OLLAMA="a11a0000-0000-4000-8000-0000000000aa"
ID_ZAI="a11a0000-0000-4000-8000-0000000000bb"
ID_DEFAULT="a11a0000-0000-4000-8000-0000000000dd"
NO_RESTART=false
for a in "$@"; do [ "$a" = "--no-restart" ] && NO_RESTART=true; done
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' "$*"; }
# ---------- JSON-хелпер: python3 на Маке, powershell на Windows ----------
json_tool() {
if command -v python3 >/dev/null 2>&1; then echo python3
elif command -v powershell.exe >/dev/null 2>&1; then echo powershell
else echo none; fi
}
# merge_meta <applied: ollama|default|keep>
merge_meta() {
local applied="$1" tool; tool="$(json_tool)"
if [ "$tool" = "python3" ]; then
python3 - "$LIB" "$ID_OLLAMA" "$ID_ZAI" "$ID_DEFAULT" "$applied" <<'PY'
import json, os, sys
lib, id_o, id_z, id_d, applied = sys.argv[1:6]
p = os.path.join(lib, "_meta.json")
meta = json.load(open(p)) if os.path.exists(p) else {"appliedId": "", "entries": []}
ent = meta.get("entries", [])
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})
if not any(x.get("name") == "Default" for x in ent):
open(os.path.join(lib, id_d + ".json"), "w").write("{}")
ensure(id_d, "Default")
ensure(id_o, "ollama-cloud"); ensure(id_z, "zai-glm")
meta["entries"] = ent
if applied == "ollama": meta["appliedId"] = id_o
elif applied == "default": meta["appliedId"] = next(x["id"] for x in ent if x["name"] == "Default")
elif not meta.get("appliedId"): meta["appliedId"] = next(x["id"] for x in ent if x["name"] == "Default")
json.dump(meta, open(p, "w"), indent=2, ensure_ascii=False)
PY
elif [ "$tool" = "powershell" ]; then
MERGE_LIB="$(cygpath -w "$LIB")" MERGE_APPLIED="$applied" \
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"
}
Ensure $env:MERGE_IDO "ollama-cloud"
Ensure $env:MERGE_IDZ "zai-glm"
$def=($meta.entries | Where-Object {$_.name -eq "Default"} | Select-Object -First 1).id
switch($env:MERGE_APPLIED){
"ollama" { $meta.appliedId=$env:MERGE_IDO }
"default" { $meta.appliedId=$def }
default { if(-not $meta.appliedId){ $meta.appliedId=$def } }
}
$meta | ConvertTo-Json -Depth 5 | Set-Content $p -Encoding UTF8
'
else
warn "Не нашёл ни python3, ни powershell для работы с JSON."; exit 1
fi
}
restart_claude() {
if [ "$NO_RESTART" = "true" ]; then warn "Перезапуск Claude пропущен (--no-restart)."; return; fi
say "Перезапускаю приложение Claude..."
if [ "$OS" = "mac" ]; then
osascript -e 'tell application "Claude" to quit' >/dev/null 2>&1 || true
sleep 3
open -a "Claude"
else
powershell.exe -NoProfile -Command '
Get-Process -Name "Claude*" -ErrorAction SilentlyContinue | Stop-Process -Force
Start-Sleep 3
$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 }
' || warn "Открой Claude вручную."
fi
ok "Готово. Открой выбор модели в чате: там GLM-5.3 и Flash, а на платном тарифе Ollama ещё и Kimi K3."
}
# ---------- TLS-фикс песочницы (только Мак): для вкладки Code ----------
apply_tls_fix() {
[ "$OS" = "mac" ] || return 0 # на Windows песочницы нет, фикс не нужен
local sj="$HOME/.claude/settings.json"
mkdir -p "$HOME/.claude"
[ -f "$sj" ] || echo '{}' > "$sj"
python3 - "$sj" <<'PY'
import json, sys
p = sys.argv[1]
try:
d = json.load(open(p))
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"), indent=2, ensure_ascii=False)
print(" added")
else:
print(" present")
PY
}
# ---------- ОТКАТ ----------
if [ "${1:-}" = "restore" ]; then
say "Откат на родной Claude..."
if [ ! -f "$LIB/_meta.json" ]; then ok "Конфигов нет, откатывать нечего."; exit 0; fi
merge_meta default
ok "Применён конфиг Default: приложение вернётся к родному входу."
restart_claude
echo
echo "Если что-то осталось сломанным, полный сброс (сотрёт чаты стороннего режима):"
if [ "$OS" = "mac" ]; then
echo " rm -rf \"$HOME/Library/Application Support/Claude-3p\" \"$HOME/Library/Application Support/Claude Nest-3p\""
else
echo " удали папки Claude-3p и \"Claude Nest-3p\" в %LOCALAPPDATA% (Проводник)"
fi
exit 0
fi
# ---------- 1. Claude ----------
say "[1/5] Проверяю приложение 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 не найден. Качаю установщик..."
if [ "$OS" = "mac" ]; then
curl -fsSL -o "$HOME/Downloads/Claude.dmg" "https://claude.ai/download/dmg" && open "$HOME/Downloads/Claude.dmg"
echo " Перетащи Claude в Applications, запусти, войди в аккаунт, затем запусти скрипт снова."
else
DL="$(cygpath -w "$HOME/Downloads/ClaudeSetup.exe" 2>/dev/null || echo "$USERPROFILE\\Downloads\\ClaudeSetup.exe")"
curl -fsSL -o "$HOME/Downloads/ClaudeSetup.exe" "https://storage.googleapis.com/osprey-downloads-c02f6a0d-347c-492b-a752-3e0651722e97/nest-win-x64/Claude-Setup-x64.exe"
powershell.exe -NoProfile -Command "Start-Process '$DL'"
echo " Установщик Claude запущен, дождись установки и входа в аккаунт, затем запусти скрипт снова."
fi
exit 0
fi
# ---------- 2. Ollama ----------
say "[2/5] Проверяю программу 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 [ -n "$OLLAMA_BIN" ]; then
ok "Ollama установлена."
if [ "$OS" = "mac" ]; then open -a "Ollama" >/dev/null 2>&1 || true; fi
else
printf " Ollama не найдена. Скачать? Нужна для маршрута Ollama Cloud [Y/n]: "
read -r ans < /dev/tty || ans=y
if [ "${ans:-y}" != "n" ] && [ "${ans:-y}" != "N" ]; then
warn "Качаю установщик Ollama..."
if [ "$OS" = "mac" ]; then
curl -fsSL -o "$HOME/Downloads/Ollama.dmg" "https://ollama.com/download/Ollama.dmg" && open "$HOME/Downloads/Ollama.dmg"
else
DLO="$(cygpath -w "$HOME/Downloads/OllamaSetup.exe" 2>/dev/null || echo "$USERPROFILE\\Downloads\\OllamaSetup.exe")"
curl -fsSL -o "$HOME/Downloads/OllamaSetup.exe" "https://ollama.com/download/OllamaSetup.exe"
powershell.exe -NoProfile -Command "Start-Process '$DLO'"
fi
echo " Установи Ollama, запусти, войди в аккаунт (Sign in), затем запусти скрипт снова."
exit 0
else
warn "Пропускаю Ollama: поставлю только конфиг Z.ai."
fi
fi
# ---------- 3. Псевдонимы ----------
HAVE_OLLAMA=false
if [ -n "$OLLAMA_BIN" ]; then
HAVE_OLLAMA=true
say "[3/5] Создаю псевдонимы облачных моделей (нужен вход в аккаунт Ollama)..."
# make_alias <модель> <псевдоним> [optional]
# optional: модель платная (Kimi K3) -> не валим установку, просто предупреждаем
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 (иконка ламы) -> Settings -> 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 "glm-5.3-flash:cloud" "claude-haiku-4-5"
make_alias "kimi-k3:cloud" "claude-opus-4-6" optional
else
say "[3/5] Ollama нет, псевдонимы пропускаю."
fi
# ---------- 4. Конфиги ----------
say "[4/5] Кладу конфиги в приложение Claude..."
mkdir -p "$LIB"
[ -f "$C3P/developer_settings.json" ] || printf '{"allowDevTools": true}' > "$C3P/developer_settings.json"
cat > "$LIB/$ID_OLLAMA.json" <<'JSON'
{
"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 }
]
}
JSON
# Не затираем уже вставленный ключ: перезаписываем только заглушку или отсутствующий файл
if [ -f "$LIB/$ID_ZAI.json" ] && ! grep -q "ВСТАВЬ_СЮДА_СВОЙ_КЛЮЧ_ZAI" "$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",
"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 }
]
}
JSON
fi
if [ "$HAVE_OLLAMA" = "true" ]; then merge_meta ollama; else merge_meta keep; fi
ok "Конфиги на месте: ollama-cloud (применён), zai-glm (ждёт твой ключ)."
ok "Developer-режим включён."
if [ "$OS" = "mac" ]; then say "[4.5/5] TLS-фикс песочницы для вкладки Code..."; res=$(apply_tls_fix); ok "sandbox.enableWeakerNetworkIsolation:$res"; fi
# ---------- 5. Финал ----------
say "[5/5] Финал."
restart_claude
echo
echo "Что дальше:"
echo " · Модель выбирается в чате, рядом с полем ввода."
echo " · Z.ai: панель Developer -> Configure Third-Party Inference, конфигурация zai-glm,"
echo " вставь свой ключ в Gateway API key, Apply Changes, перезапуск."
echo " · Вернуть родной Claude: bash setup.sh restore"