fix(install): close MEDIUM/LOW from RULE 0.26 audit

- preflight failure handling: вместо `|| true` (молчаливое продолжение
  при упавшем preflight) — явный prompt «продолжить? [y/N]» с return 1
  при отказе. Без TTY печатает warning и продолжает. Это закрывает
  HIGH bug-9: «.onboarded флаг выставляется при нерабочей конфигурации».

- lib-preflight.sh::preflight_check_cli — общий helper (command -v +
  offer-install + version echo). Убирает 6-file boilerplate (хотя сами
  per-provider файлы пока не переписаны под него — это отдельный шаг).

- onboarding_fallback_providers: расширен с 3 до 14 провайдеров,
  покрывает все 7 транспортов. Был дрейф vs providers.toml (14 vs 3),
  юзер без submodule видел только anthropic+openai+ollama.

- STR_PICK_PROVIDER plural mismatch: whiptail и plain ветки теперь
  используют один fallback "Provider within" (раньше plain имел
  "Providers within", whiptail — "Provider within").

- STR_DONE_NEXT удалён из en.sh + ru.sh (мёртвый ключ).

- Новые ключи: STR_MENU_* (для lib-menu.sh) + STR_PREFLIGHT_FAILED +
  STR_PREFLIGHT_CONTINUE. lib-menu.sh начал использовать
  STR_MENU_TITLE / STR_MENU_SUBSTRATE (частичная локализация, остальное
  меню — отдельной задачей).

Тесты: bash -n чисто, i18n round-trip EN/RU работает, non-TTY smoke
install --no-execute проходит.
This commit is contained in:
Parfii-bot 2026-05-17 16:37:28 +08:00
parent a3ffaed374
commit 305140f20b
5 changed files with 84 additions and 6 deletions

View file

@ -30,4 +30,13 @@ STR_AUTH_CURRENT_HINT="(current: <hidden>)"
STR_DONE_TITLE="Onboarding complete" STR_DONE_TITLE="Onboarding complete"
STR_DONE_CONFIG="config:" STR_DONE_CONFIG="config:"
STR_DONE_SECRETS="secrets:" STR_DONE_SECRETS="secrets:"
STR_DONE_NEXT="Next: run ./install.sh or restart this script to apply profile"
# Profile menu (lib-menu.sh strings)
STR_MENU_TITLE="KeiSeiKit Installer"
STR_MENU_SUBSTRATE="Substrate baseline (always installed):"
STR_MENU_PROFILE_PROMPT="Choose install profile:"
STR_MENU_CONFIRM="Confirm selection?"
# Preflight warnings
STR_PREFLIGHT_FAILED="Preflight failed — provider may not work."
STR_PREFLIGHT_CONTINUE="Continue anyway? [y/N]"

View file

@ -30,4 +30,13 @@ STR_AUTH_CURRENT_HINT="(текущее: <скрыто>)"
STR_DONE_TITLE="Первичная настройка завершена" STR_DONE_TITLE="Первичная настройка завершена"
STR_DONE_CONFIG="конфиг:" STR_DONE_CONFIG="конфиг:"
STR_DONE_SECRETS="секреты:" STR_DONE_SECRETS="секреты:"
STR_DONE_NEXT="Дальше: запустите ./install.sh или перезапустите этот скрипт для установки профиля"
# Меню профилей (lib-menu.sh)
STR_MENU_TITLE="Установщик KeiSeiKit"
STR_MENU_SUBSTRATE="Базовая часть (ставится всегда):"
STR_MENU_PROFILE_PROMPT="Выберите профиль установки:"
STR_MENU_CONFIRM="Подтвердить выбор?"
# Preflight-предупреждения
STR_PREFLIGHT_FAILED="Preflight упал — провайдер может не работать."
STR_PREFLIGHT_CONTINUE="Продолжить всё равно? [y/N]"

View file

@ -69,10 +69,10 @@ menu_whiptail_custom() {
# plain-text profile picker → profile name. Exits 1 on cancel. # plain-text profile picker → profile name. Exits 1 on cancel.
menu_plain_profile() { menu_plain_profile() {
echo "============================================================" >&2 echo "============================================================" >&2
echo " KeiSeiKit Installer" >&2 echo " ${STR_MENU_TITLE:-KeiSeiKit Installer}" >&2
echo "============================================================" >&2 echo "============================================================" >&2
echo >&2 echo >&2
echo " Substrate baseline (ALWAYS installed, regardless of profile):" >&2 echo " ${STR_MENU_SUBSTRATE:-Substrate baseline (ALWAYS installed):}" >&2
echo " • 37 agent manifests • 67 skills • 39 hooks" >&2 echo " • 37 agent manifests • 67 skills • 39 hooks" >&2
echo " • 82 blocks • 16 caps • 7 roles" >&2 echo " • 82 blocks • 16 caps • 7 roles" >&2
echo " • 11 cross-tool bridges (Cursor / Copilot / Codex / Aider / …)" >&2 echo " • 11 cross-tool bridges (Cursor / Copilot / Codex / Aider / …)" >&2

View file

@ -59,10 +59,25 @@ onboarding_list_providers() {
} }
# Fallback если submodule не подтянут. # Fallback если submodule не подтянут.
# Покрывает 7 транспортов (direct-api / aws / azure / vertex / local / proxy
# / subscription) минимальными представителями. Используется только когда
# providers.toml отсутствует — синхронизировать ручно если добавится новый
# транспорт-тип в реестр.
onboarding_fallback_providers() { onboarding_fallback_providers() {
printf "anthropic\tdirect-api\tAnthropic (Direct API)\tANTHROPIC_API_KEY\n" printf "anthropic\tdirect-api\tAnthropic (Direct API)\tANTHROPIC_API_KEY\n"
printf "anthropic-bedrock\taws-bedrock\tAnthropic (AWS Bedrock)\tAWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_REGION\n"
printf "openai\tdirect-api\tOpenAI (Direct API)\tOPENAI_API_KEY\n" printf "openai\tdirect-api\tOpenAI (Direct API)\tOPENAI_API_KEY\n"
printf "openai-azure\tazure-openai\tOpenAI (Azure)\tAZURE_OPENAI_API_KEY,AZURE_OPENAI_ENDPOINT,AZURE_OPENAI_DEPLOYMENT\n"
printf "xai\tdirect-api\txAI\tXAI_API_KEY\n"
printf "deepseek\tdirect-api\tDeepSeek\tDEEPSEEK_API_KEY\n"
printf "google\tdirect-api\tGoogle Gemini (Direct API)\tGEMINI_API_KEY\n"
printf "google-vertex\tgoogle-vertex\tGoogle Gemini (Vertex AI)\tGOOGLE_APPLICATION_CREDENTIALS,GCP_PROJECT_ID,GCP_REGION\n"
printf "ollama-local\tlocal\tOllama (local)\t_\n" printf "ollama-local\tlocal\tOllama (local)\t_\n"
printf "mlx-local\tlocal\tMLX (Apple silicon local)\t_\n"
printf "lmstudio-local\tlocal\tLM Studio (local)\t_\n"
printf "litellm-proxy\tproxy\tLiteLLM proxy (keisei.app)\tKEI_LITELLM_KEY\n"
printf "openrouter\tproxy\tOpenRouter\tOPENROUTER_API_KEY\n"
printf "codex\tsubscription\tOpenAI Codex (ChatGPT OAuth)\t_\n"
} }
# Уникальные транспорты — для первого экрана выбора. # Уникальные транспорты — для первого экрана выбора.
@ -183,7 +198,8 @@ onboarding_pick_provider() {
|| ONBOARDING_PROVIDER=$(echo "$rows" | head -1 | awk -F'\t' '{print $1}') || ONBOARDING_PROVIDER=$(echo "$rows" | head -1 | awk -F'\t' '{print $1}')
else else
echo "" >&2 echo "" >&2
echo "${STR_PICK_PROVIDER:-Providers within} $ONBOARDING_TRANSPORT:" >&2 # Используем единый fallback что и для whiptail — устраняем plural mismatch.
echo "${STR_PICK_PROVIDER:-Provider within} $ONBOARDING_TRANSPORT:" >&2
declare -a ids=() declare -a ids=()
local i=1 local i=1
while IFS=$'\t' read -r id dn ae; do while IFS=$'\t' read -r id dn ae; do
@ -337,7 +353,27 @@ onboarding_run() {
# Preflight — проверка CLI/daemon до сбора ключей. # Preflight — проверка CLI/daemon до сбора ключей.
# Для direct-api провайдеров файла preflight нет → silent pass. # Для direct-api провайдеров файла preflight нет → silent pass.
if command -v preflight_run >/dev/null 2>&1; then if command -v preflight_run >/dev/null 2>&1; then
preflight_run "$ONBOARDING_PROVIDER" || true if ! preflight_run "$ONBOARDING_PROVIDER"; then
# Provider preflight failed (CLI missing / daemon down / no creds).
# Не молчим — спрашиваем юзера, иначе onboarding закончится
# с .onboarded флагом для нерабочей конфигурации (HIGH аудит-9).
echo "" >&2
echo "${STR_PREFLIGHT_FAILED:-Preflight failed — provider may not work.}" >&2
if [ -t 0 ] && [ -t 1 ]; then
read -r -p " ${STR_PREFLIGHT_CONTINUE:-Continue anyway? [y/N]} " _ans
case "$_ans" in
y|Y|yes|да|Да)
echo " → продолжаю; ключи запишутся но runtime может упасть." >&2
;;
*)
echo " → прервано; флаг .onboarded НЕ выставляется, перезапустите." >&2
return 1
;;
esac
else
echo " → non-TTY, продолжаю — настройте CLI вручную потом." >&2
fi
fi
fi fi
onboarding_collect_auth onboarding_collect_auth
onboarding_write_secrets onboarding_write_secrets

View file

@ -50,6 +50,30 @@ preflight_offer_install() {
fi fi
} }
# Универсальный helper для типового CLI-чека (command -v + offer-install + version).
# Используется per-provider preflight файлами чтобы убрать boilerplate.
#
# Аргументы:
# $1 — имя CLI (для сообщений), например "aws CLI"
# $2 — бинарь (для command -v), например "aws"
# $3 — install_cmd (для preflight_offer_install)
# $4 — version_cmd (для печати при success), например "aws --version"
#
# Возврат: 0 если CLI есть, 1 если нет и юзер не поставил.
preflight_check_cli() {
local label="$1"
local bin="$2"
local install_cmd="$3"
local version_cmd="$4"
if ! command -v "$bin" >/dev/null 2>&1; then
preflight_offer_install "$label" "$install_cmd" || return 1
# После install проверяем что бинарь появился в PATH.
command -v "$bin" >/dev/null 2>&1 || return 1
fi
echo "$label: $(eval "$version_cmd" 2>&1 | head -1)" >&2
return 0
}
# Главный диспетчер. Вызывается из onboarding между pick_model и collect_auth. # Главный диспетчер. Вызывается из onboarding между pick_model и collect_auth.
preflight_run() { preflight_run() {
local provider="$1" local provider="$1"