feat(install): preflight модуль — проверка CLI по выбранному провайдеру
Добавлен шаг между выбором модели и сбором ключей: для провайдеров
требующих внешний CLI/daemon — проверка наличия, инструкция по
установке, опциональный авто-install (TTY only).
install/lib-preflight.sh — диспетчер:
preflight_run <provider-id>
- ищет install/preflight/<id>.sh, source'ит, вызывает
preflight_check_<sanitized_id>
- функция возвращает 0/1, печатает инструкцию в stderr
- non-TTY: только печать, без вопросов
preflight_offer_install <cli> <install-cmd>:
- TTY: спрашивает [y/N/skip], выполняет install-cmd
- non-TTY: печатает и пропускает
install/preflight/ — 6 файлов (только для провайдеров с CLI):
anthropic-bedrock.sh — aws CLI + sts get-caller-identity
google-vertex.sh — gcloud CLI + project config
codex.sh — codex CLI (npm) + login status
ollama-local.sh — ollama binary + 127.0.0.1:11434 daemon
mlx-local.sh — mlx_lm.server (arm64 only) + 127.0.0.1:8080
lmstudio-local.sh — порт 127.0.0.1:1234 (desktop app)
Direct-api провайдеры (anthropic, openai, xai, deepseek, google) +
proxy (litellm, openrouter) + openai-azure — preflight-файла нет,
диспетчер тихо пропускает, ключ собирается обычно.
Тесты: bash -n чисто на всех 8 файлах, unit dispatcher показывает
silent-pass для anthropic, warn+exit-1 для bedrock без aws на PATH.
This commit is contained in:
parent
ab260f429e
commit
0a8c93561f
9 changed files with 220 additions and 0 deletions
|
|
@ -45,6 +45,8 @@ source "$LIB_DIR/lib-menu.sh"
|
|||
source "$LIB_DIR/lib-i18n.sh"
|
||||
# Загружаем английский словарь по умолчанию — welcome banner идёт до выбора языка.
|
||||
i18n_load_default
|
||||
# shellcheck source=install/lib-preflight.sh
|
||||
source "$LIB_DIR/lib-preflight.sh"
|
||||
# shellcheck source=install/lib-onboarding.sh
|
||||
source "$LIB_DIR/lib-onboarding.sh"
|
||||
# shellcheck source=install/lib-plan.sh
|
||||
|
|
|
|||
|
|
@ -319,6 +319,11 @@ onboarding_run() {
|
|||
onboarding_pick_transport
|
||||
onboarding_pick_provider
|
||||
onboarding_pick_model
|
||||
# Preflight — проверка CLI/daemon до сбора ключей.
|
||||
# Для direct-api провайдеров файла preflight нет → silent pass.
|
||||
if command -v preflight_run >/dev/null 2>&1; then
|
||||
preflight_run "$ONBOARDING_PROVIDER" || true
|
||||
fi
|
||||
onboarding_collect_auth
|
||||
onboarding_write_secrets
|
||||
onboarding_write_config
|
||||
|
|
|
|||
66
install/lib-preflight.sh
Normal file
66
install/lib-preflight.sh
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# shellcheck shell=bash
|
||||
# lib-preflight.sh — диспетчер preflight-проверок CLI.
|
||||
#
|
||||
# Контракт:
|
||||
# preflight_run <provider-id>
|
||||
# 1. Ищет файл install/preflight/<provider-id>.sh
|
||||
# 2. Если есть — source'ит и вызывает `preflight_check_<sanitized-id>`
|
||||
# 3. Функция возвращает 0 (ok) / 1 (missing, инструкция напечатана)
|
||||
# 4. Если файла нет — провайдеру CLI не нужен, тихо exit 0
|
||||
#
|
||||
# Файл per-provider должен экспортировать ОДНУ функцию:
|
||||
# preflight_check_<id>() — печатает инструкцию в stderr, exit 0/1
|
||||
#
|
||||
# Sanitize: dashes в id заменяются на underscores для имени функции
|
||||
# (bash не любит dashes в идентификаторах).
|
||||
|
||||
PREFLIGHT_DIR="${LIB_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}/preflight"
|
||||
|
||||
# Печатает инструкцию по установке, спрашивает действие.
|
||||
# Аргументы: $1 — имя CLI, $2 — команда установки.
|
||||
preflight_offer_install() {
|
||||
local cli="$1"
|
||||
local install_cmd="$2"
|
||||
echo "" >&2
|
||||
echo " ⚠ $cli не найден." >&2
|
||||
echo " Установить: $install_cmd" >&2
|
||||
echo "" >&2
|
||||
if [ -t 0 ] && [ -t 1 ]; then
|
||||
read -r -p " Поставить сейчас? [y/N/skip] " ans
|
||||
case "$ans" in
|
||||
y|Y|yes)
|
||||
eval "$install_cmd"
|
||||
return $?
|
||||
;;
|
||||
skip|s|S)
|
||||
echo " пропускаю — поставите вручную позже." >&2
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
echo " пропуск (по умолчанию)." >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
else
|
||||
# non-TTY: только печатаем инструкцию.
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Главный диспетчер. Вызывается из onboarding между pick_model и collect_auth.
|
||||
preflight_run() {
|
||||
local provider="$1"
|
||||
[ -z "$provider" ] && return 0
|
||||
local script="$PREFLIGHT_DIR/${provider}.sh"
|
||||
if [ ! -f "$script" ]; then
|
||||
return 0 # CLI не нужен — direct-api, ключ собирается ниже
|
||||
fi
|
||||
# shellcheck disable=SC1090
|
||||
source "$script"
|
||||
local fn="preflight_check_${provider//-/_}"
|
||||
if command -v "$fn" >/dev/null 2>&1; then
|
||||
"$fn"
|
||||
return $?
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
26
install/preflight/anthropic-bedrock.sh
Normal file
26
install/preflight/anthropic-bedrock.sh
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# shellcheck shell=bash
|
||||
# preflight/anthropic-bedrock.sh — AWS CLI + Bedrock региональный доступ.
|
||||
|
||||
preflight_check_anthropic_bedrock() {
|
||||
if ! command -v aws >/dev/null 2>&1; then
|
||||
local cmd
|
||||
case "$(uname -s)" in
|
||||
Darwin) cmd="brew install awscli" ;;
|
||||
Linux) cmd="curl 'https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip' -o /tmp/awscliv2.zip && unzip -q /tmp/awscliv2.zip -d /tmp && sudo /tmp/aws/install" ;;
|
||||
*) cmd="см. https://aws.amazon.com/cli/" ;;
|
||||
esac
|
||||
preflight_offer_install "aws CLI" "$cmd" || return 1
|
||||
fi
|
||||
# Проверяем что credentials хоть как-то настроены (env, ~/.aws/credentials, IAM role).
|
||||
if ! aws sts get-caller-identity >/dev/null 2>&1; then
|
||||
echo "" >&2
|
||||
echo " ⚠ AWS credentials не настроены." >&2
|
||||
echo " Запустите: aws configure" >&2
|
||||
echo " Или экспортируйте AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY + AWS_REGION." >&2
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
echo " ✓ aws CLI: $(aws --version 2>&1 | head -1)" >&2
|
||||
echo " ✓ identity: $(aws sts get-caller-identity --query Arn --output text 2>&1)" >&2
|
||||
return 0
|
||||
}
|
||||
29
install/preflight/codex.sh
Normal file
29
install/preflight/codex.sh
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# shellcheck shell=bash
|
||||
# preflight/codex.sh — OpenAI Codex CLI через ChatGPT OAuth.
|
||||
|
||||
preflight_check_codex() {
|
||||
if ! command -v codex >/dev/null 2>&1; then
|
||||
if ! command -v npm >/dev/null 2>&1; then
|
||||
echo "" >&2
|
||||
echo " ⚠ npm требуется для установки codex." >&2
|
||||
echo " Сначала: brew install node (macOS) или apt install nodejs npm (Linux)" >&2
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
preflight_offer_install "codex CLI" "npm install -g @openai/codex" || return 1
|
||||
fi
|
||||
# Проверяем что OAuth активен.
|
||||
local status
|
||||
status="$(codex login status 2>&1 || true)"
|
||||
if ! echo "$status" | grep -qiE "logged.in|active"; then
|
||||
echo "" >&2
|
||||
echo " ⚠ codex не залогинен в ChatGPT." >&2
|
||||
echo " Запустите: codex login" >&2
|
||||
echo " (требуется ChatGPT Plus/Pro/Team подписка)" >&2
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
echo " ✓ codex CLI: $(codex --version 2>&1 | head -1)" >&2
|
||||
echo " ✓ OAuth: $status" >&2
|
||||
return 0
|
||||
}
|
||||
28
install/preflight/google-vertex.sh
Normal file
28
install/preflight/google-vertex.sh
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# shellcheck shell=bash
|
||||
# preflight/google-vertex.sh — gcloud CLI + service-account JSON.
|
||||
|
||||
preflight_check_google_vertex() {
|
||||
if ! command -v gcloud >/dev/null 2>&1; then
|
||||
local cmd
|
||||
case "$(uname -s)" in
|
||||
Darwin) cmd="brew install --cask google-cloud-sdk" ;;
|
||||
Linux) cmd="curl https://sdk.cloud.google.com | bash" ;;
|
||||
*) cmd="см. https://cloud.google.com/sdk/docs/install" ;;
|
||||
esac
|
||||
preflight_offer_install "gcloud CLI" "$cmd" || return 1
|
||||
fi
|
||||
# Проверяем что выбран project.
|
||||
local project
|
||||
project="$(gcloud config get-value project 2>/dev/null)"
|
||||
if [ -z "$project" ] || [ "$project" = "(unset)" ]; then
|
||||
echo "" >&2
|
||||
echo " ⚠ GCP project не выбран." >&2
|
||||
echo " Запустите: gcloud auth login && gcloud config set project YOUR_PROJECT_ID" >&2
|
||||
echo " Также установите GOOGLE_APPLICATION_CREDENTIALS на путь к service-account JSON." >&2
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
echo " ✓ gcloud CLI: $(gcloud --version 2>&1 | head -1)" >&2
|
||||
echo " ✓ project: $project" >&2
|
||||
return 0
|
||||
}
|
||||
16
install/preflight/lmstudio-local.sh
Normal file
16
install/preflight/lmstudio-local.sh
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# shellcheck shell=bash
|
||||
# preflight/lmstudio-local.sh — LM Studio desktop GUI на 127.0.0.1:1234.
|
||||
|
||||
preflight_check_lmstudio_local() {
|
||||
# LM Studio это desktop-приложение, не CLI — проверяем только порт.
|
||||
if ! curl -fsS --max-time 3 http://127.0.0.1:1234/v1/models >/dev/null 2>&1; then
|
||||
echo "" >&2
|
||||
echo " ⚠ LM Studio сервер не запущен на 1234." >&2
|
||||
echo " Скачайте: https://lmstudio.ai/" >&2
|
||||
echo " В GUI: Local Server → Start Server (порт 1234 по умолчанию)" >&2
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
echo " ✓ LM Studio: 127.0.0.1:1234 отвечает" >&2
|
||||
return 0
|
||||
}
|
||||
23
install/preflight/mlx-local.sh
Normal file
23
install/preflight/mlx-local.sh
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# shellcheck shell=bash
|
||||
# preflight/mlx-local.sh — MLX inference server (Apple silicon).
|
||||
|
||||
preflight_check_mlx_local() {
|
||||
if [ "$(uname -s)" != "Darwin" ] || [ "$(uname -m)" != "arm64" ]; then
|
||||
echo "" >&2
|
||||
echo " ⚠ MLX доступен только на Apple silicon (arm64 macOS)." >&2
|
||||
echo " Текущая платформа: $(uname -s) $(uname -m)" >&2
|
||||
return 1
|
||||
fi
|
||||
if ! command -v mlx_lm.server >/dev/null 2>&1; then
|
||||
preflight_offer_install "mlx_lm" "pip install mlx-lm" || return 1
|
||||
fi
|
||||
if ! curl -fsS --max-time 3 http://127.0.0.1:8080/v1/models >/dev/null 2>&1; then
|
||||
echo "" >&2
|
||||
echo " ⚠ MLX server не запущен на 8080." >&2
|
||||
echo " Запустите: mlx_lm.server --model mlx-community/Qwen2.5-Coder-32B-Instruct-4bit" >&2
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
echo " ✓ mlx_lm.server: 127.0.0.1:8080 отвечает" >&2
|
||||
return 0
|
||||
}
|
||||
25
install/preflight/ollama-local.sh
Normal file
25
install/preflight/ollama-local.sh
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# shellcheck shell=bash
|
||||
# preflight/ollama-local.sh — Ollama daemon на 127.0.0.1:11434.
|
||||
|
||||
preflight_check_ollama_local() {
|
||||
if ! command -v ollama >/dev/null 2>&1; then
|
||||
local cmd
|
||||
case "$(uname -s)" in
|
||||
Darwin) cmd="brew install ollama" ;;
|
||||
Linux) cmd="curl -fsSL https://ollama.com/install.sh | sh" ;;
|
||||
*) cmd="см. https://ollama.com/download" ;;
|
||||
esac
|
||||
preflight_offer_install "ollama" "$cmd" || return 1
|
||||
fi
|
||||
# Проверяем что daemon запущен.
|
||||
if ! curl -fsS --max-time 3 http://127.0.0.1:11434/api/tags >/dev/null 2>&1; then
|
||||
echo "" >&2
|
||||
echo " ⚠ ollama daemon не запущен." >&2
|
||||
echo " Запустите: ollama serve (или brew services start ollama на macOS)" >&2
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
echo " ✓ ollama: $(ollama --version 2>&1 | head -1)" >&2
|
||||
echo " ✓ daemon: 127.0.0.1:11434 отвечает" >&2
|
||||
return 0
|
||||
}
|
||||
Loading…
Reference in a new issue