From 65a31bd0d38e15052c583df9d4e1971e1f7b1d8a Mon Sep 17 00:00:00 2001 From: giteaMyFirstvdsRoot Date: Tue, 1 Sep 2026 15:50:10 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20dev-=D1=84=D0=BB=D0=B0=D0=B3=20=D0=B2=20?= =?UTF-8?q?=D0=BE=D0=B1=D0=B5=20=D0=BF=D0=B0=D0=BF=D0=BA=D0=B8,=20=D1=87?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D0=BD=D1=8B=D0=B9=20=D1=80=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D1=80=D1=82,=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D0=BA=D0=B0=20applied,=20=D0=B0=D1=80=D0=B3=D1=83=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.sh | 425 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 283 insertions(+), 142 deletions(-) diff --git a/setup.sh b/setup.sh index 4ded788..1d3f39a 100644 --- a/setup.sh +++ b/setup.sh @@ -1,17 +1,25 @@ #!/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. Скрипт открыт и короток: прочитай перед запуском. +# 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 ;; @@ -19,55 +27,115 @@ case "$(uname -s)" in esac if [ "$OS" = "mac" ]; then - C3P="$HOME/Library/Application Support/Claude-3p" + C1P="$HOME/Library/Application Support/Claude" else - C3P="$LOCALAPPDATA/Claude-3p" + 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 [ "$a" = "--no-restart" ] && NO_RESTART=true; done +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-хелпер: 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 +# ---------- 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 "Режим разработчика включён в обеих папках приложения." } -# merge_meta +# ---------- 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 +} + +# merge_meta merge_meta() { - local applied="$1" tool; tool="$(json_tool)" - if [ "$tool" = "python3" ]; then - python3 - "$LIB" "$ID_OLLAMA" "$ID_ZAI" "$ID_DEFAULT" "$applied" <<'PY' + 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 = sys.argv[1:6] +lib, id_o, id_z, id_d, applied, routes = sys.argv[1:7] p = os.path.join(lib, "_meta.json") -meta = json.load(open(p)) if os.path.exists(p) else {"appliedId": "", "entries": []} -ent = meta.get("entries", []) +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}) -if not any(x.get("name") == "Default" for x in ent): - open(os.path.join(lib, id_d + ".json"), "w").write("{}") +# 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") -ensure(id_o, "ollama-cloud"); ensure(id_z, "zai-glm") + 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 -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) +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 - elif [ "$tool" = "powershell" ]; then - MERGE_LIB="$(cygpath -w "$LIB")" MERGE_APPLIED="$applied" \ + 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" @@ -82,50 +150,82 @@ PY "{}" | 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" + 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 } - "default" { $meta.appliedId=$def } - default { if(-not $meta.appliedId){ $meta.appliedId=$def } } + "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 ' - else - warn "Не нашёл ни python3, ни powershell для работы с JSON."; exit 1 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 +} + +# ---------- Честный перезапуск ---------- +# open -a на живом процессе только активирует окно: приложение не завершается +# при закрытии окна и настройки перечитывает лишь при настоящем старте. restart_claude() { - if [ "$NO_RESTART" = "true" ]; then warn "Перезапуск Claude пропущен (--no-restart)."; return; fi + if [ "$NO_RESTART" = "true" ]; then warn "Перезапуск пропущен (--no-restart). Настройки применятся после Cmd+Q и запуска."; return 0; fi say "Перезапускаю приложение Claude..." if [ "$OS" = "mac" ]; then - osascript -e 'tell application "Claude" to quit' >/dev/null 2>&1 || true - sleep 3 - open -a "Claude" + if pgrep -x "Claude" >/dev/null 2>&1; then + osascript -e 'tell application "Claude" to quit' >/dev/null 2>&1 || true + i=0; while pgrep -x "Claude" >/dev/null 2>&1 && [ "$i" -lt 12 ]; do sleep 1; i=$((i+1)); done + if pgrep -x "Claude" >/dev/null 2>&1; then + pkill -x "Claude" >/dev/null 2>&1 || true + sleep 2 + fi + if pgrep -x "Claude" >/dev/null 2>&1; then + warn "Claude не закрылся сам (обычно это запрет на автоматизацию:" + echo " Системные настройки -> Конфиденциальность и безопасность -> Автоматизация)." + echo " Конфиги уже на месте. Закрой Claude вручную через Cmd+Q и открой заново." + return 0 + fi + fi + open -a "Claude" >/dev/null 2>&1 || { warn "Не смог открыть Claude, открой вручную."; return 0; } 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 вручную." + if($exe){ Start-Process $exe.FullName } else { exit 1 } + ' || { warn "Не смог перезапустить Claude, закрой и открой его вручную."; return 0; } fi - ok "Готово. Открой выбор модели в чате: там GLM-5.3 и Flash, а на платном тарифе Ollama ещё и Kimi K3." + ok "Claude перезапущен." } -# ---------- TLS-фикс песочницы (только Мак): для вкладки Code ---------- +# ---------- TLS-фикс песочницы (только Мак, для вкладки Code) ---------- apply_tls_fix() { - [ "$OS" = "mac" ] || return 0 # на Windows песочницы нет, фикс не нужен + [ "$OS" = "mac" ] || return 0 + [ -n "$PY" ] || return 0 local sj="$HOME/.claude/settings.json" mkdir -p "$HOME/.claude" [ -f "$sj" ] || echo '{}' > "$sj" - python3 - "$sj" <<'PY' + "$PY" - "$sj" <<'PY' import json, sys p = sys.argv[1] try: - d = json.load(open(p)) + d = json.load(open(p, encoding="utf-8")) if not isinstance(d, dict): d = {} except Exception: d = {} @@ -134,110 +234,129 @@ 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") + json.dump(d, open(p, "w", encoding="utf-8"), indent=2, ensure_ascii=False) + print("добавлен") else: - print(" present") + print("уже был") PY } # ---------- ОТКАТ ---------- -if [ "${1:-}" = "restore" ]; then - say "Откат на родной Claude..." +if [ "$MODE" = "restore" ]; then + say "Откат на родного Claude..." if [ ! -f "$LIB/_meta.json" ]; then ok "Конфигов нет, откатывать нечего."; exit 0; fi - merge_meta default - ok "Применён конфиг Default: приложение вернётся к родному входу." + merge_meta default none + ok "Применён пустой конфиг Default: приложение вернётся к родному входу." + write_dev_flag 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 + echo " rm -rf \"$C3P\"" exit 0 fi -# ---------- 1. Claude ---------- -say "[1/5] Проверяю приложение Claude..." +# ---------- 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 установлен." +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 + warn "Приложение Claude не найдено, а без него настраивать нечего." + echo + echo " Поставь его руками, это одна минута:" + echo " 1. Открой https://claude.com/download и скачай версию для своей системы." + echo " 2. Установи и запусти приложение хотя бы один раз." + echo " 3. Вернись сюда и запусти эту же команду снова." + echo + echo " Логиниться в аккаунт Anthropic не обязательно." + exit 1 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 +# ---------- 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 - 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 + say "[2/4] Режим: $BRAIN" fi +case "$BRAIN" in + ollama) ok "Ставлю только Ollama." ;; + zai) ok "Ставлю только Z.ai." ;; + both) ok "Ставлю обе конфигурации." ;; + *) warn "Непонятный режим «$BRAIN», беру оба."; BRAIN=both ;; +esac -# ---------- 3. Псевдонимы ---------- +# ---------- 3. Ollama и псевдонимы ---------- 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, затем запусти скрипт снова." +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" } - "$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 + 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 + fi else - say "[3/5] Ollama нет, псевдонимы пропускаю." + say "[3/4] Маршрут Z.ai, Ollama не нужна." fi # ---------- 4. Конфиги ---------- -say "[4/5] Кладу конфиги в приложение Claude..." +say "[4/4] Кладу конфиги в приложение Claude..." mkdir -p "$LIB" -[ -f "$C3P/developer_settings.json" ] || printf '{"allowDevTools": true}' > "$C3P/developer_settings.json" +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", @@ -253,39 +372,61 @@ cat > "$LIB/$ID_OLLAMA.json" <<'JSON' ] } JSON +ok "Конфиг ollama-cloud на месте." +fi -# Не затираем уже вставленный ключ: перезаписываем только заглушку или отсутствующий файл -if [ -f "$LIB/$ID_ZAI.json" ] && ! grep -q "ВСТАВЬ_СЮДА_СВОЙ_КЛЮЧ_ZAI" "$LIB/$ID_ZAI.json"; then - ok "Конфиг zai-glm уже настроен, ключ не трогаю." -else -cat > "$LIB/$ID_ZAI.json" <<'JSON' +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" < Configure Third-Party Inference и выбери конфигурацию вручную." ;; +esac -# ---------- 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" +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."