refactor: нативный persistence effort, выпил мёртвого кэша effort_*
EFFORT_MAPPING, statusline и _apply_effort переведены на нативное хранение уровня в settings.json лаунчера. Убран CLAUDE_CODE_EFFORT_LEVEL (он блокировал /effort внутри сессии) и кэш ~/.cache/ai-setup/effort_*, который никто не читал и который врал относительно реального уровня. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -82,31 +82,22 @@ on / off
|
|||||||
## Persistence effort между сессиями
|
## Persistence effort между сессиями
|
||||||
|
|
||||||
Каждый Claude Code лаунчер (`ai-claude`, `ai-deepseek`, `ai-kimi`, `ai-openrouter`)
|
Каждый Claude Code лаунчер (`ai-claude`, `ai-deepseek`, `ai-kimi`, `ai-openrouter`)
|
||||||
запоминает свой уровень effort отдельно. Логика гибридная:
|
запоминает свой уровень effort отдельно в `settings.json` лаунчера.
|
||||||
|
|
||||||
- `low` / `medium` / `high` / `xhigh` живут нативно в `settings.json` лаунчера.
|
`/effort` внутри сессии работает для всех уровней. `CLAUDE_CODE_EFFORT_LEVEL` не используется.
|
||||||
`/effort` внутри сессии работает как обычно, уровень сохраняется между сессиями.
|
|
||||||
- `max` - единственный уровень, который Claude Code не сохраняет в `settings.json`
|
|
||||||
(он session-only). Поэтому его восстанавливаем через `CLAUDE_CODE_EFFORT_LEVEL`.
|
|
||||||
Текущий уровень, включая `max`, лаунчер записывает в `~/.cache/ai-setup/effort_<launcher>`.
|
|
||||||
|
|
||||||
Важное следствие только для `max`: когда восстановлена `max`-сессия, выставлена
|
Форсировать уровень при запуске:
|
||||||
`CLAUDE_CODE_EFFORT_LEVEL=max`, и `/effort` внутри нее не сменит уровень, потому что
|
|
||||||
env-переменная работает как жесткий override Claude Code.
|
|
||||||
|
|
||||||
Как выйти из `max` или форсить любой уровень:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
AI_EFFORT=max ai-deepseek # включить и запомнить max
|
AI_EFFORT=max ai-deepseek # включить max
|
||||||
AI_EFFORT=high ai-deepseek # вернуться на high
|
AI_EFFORT=high ai-deepseek # включить high
|
||||||
ai-deepseek # без флага - восстановить последний уровень
|
ai-deepseek # без флага - использовать уровень из settings.json
|
||||||
```
|
```
|
||||||
|
|
||||||
Дефолты при пустом кеше: `xhigh` для `ai-claude`, `high` для остальных Claude Code лаунчеров.
|
Дефолты: `xhigh` для `ai-claude`, `high` для остальных Claude Code лаунчеров.
|
||||||
|
|
||||||
## Рекомендации
|
## Рекомендации
|
||||||
|
|
||||||
- Для повседневной работы: `high` или `xhigh`.
|
- Для повседневной работы: `high` или `xhigh`.
|
||||||
- `max` effort имеет реальный эффект у Anthropic и DeepSeek; у Kimi это все тот же thinking on.
|
- `max` effort имеет реальный эффект у Anthropic и DeepSeek; у Kimi это все тот же thinking on.
|
||||||
- `low`/`medium` у DeepSeek и Kimi фактически не снижают reasoning.
|
- `low`/`medium` у DeepSeek и Kimi фактически не снижают reasoning.
|
||||||
- Смена уровня на `low`..`xhigh`: обычным `/effort`; выход из `max`: через `AI_EFFORT=<lvl> ai-<launcher>`.
|
|
||||||
|
|||||||
@@ -42,16 +42,6 @@ printf "\033[38;5;252m%s\033[00m" "$short_cwd"
|
|||||||
if [ -n "$model" ]; then
|
if [ -n "$model" ]; then
|
||||||
brand_color=$(_brand_color "${AI_LAUNCHER:-}")
|
brand_color=$(_brand_color "${AI_LAUNCHER:-}")
|
||||||
effort=$(echo "$input" | jq -r ".effort.level // empty")
|
effort=$(echo "$input" | jq -r ".effort.level // empty")
|
||||||
# Ловим выбранный уровень в кэш лаунчера (чтобы запомнить max между сессиями).
|
|
||||||
# Когда CLAUDE_CODE_EFFORT_LEVEL выставлена (восстановленная max-сессия) - уровень
|
|
||||||
# форсится env, кэш НЕ трогаем, чтобы дисплей-баг (.effort.level=xhigh) не затёр max.
|
|
||||||
if [ -n "${AI_LAUNCHER:-}" ] && [ -z "${CLAUDE_CODE_EFFORT_LEVEL:-}" ] && [ -n "$effort" ]; then
|
|
||||||
effort_file="$HOME/.cache/ai-setup/effort_${AI_LAUNCHER}"
|
|
||||||
if [ "$effort" != "$(cat "$effort_file" 2>/dev/null)" ]; then
|
|
||||||
mkdir -p "$HOME/.cache/ai-setup"
|
|
||||||
echo "$effort" > "$effort_file"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# Аккаунт Claude.ai актуален только для нативных моделей Claude
|
# Аккаунт Claude.ai актуален только для нативных моделей Claude
|
||||||
if [[ "$model_id" == claude-* ]]; then
|
if [[ "$model_id" == claude-* ]]; then
|
||||||
account=$(cat ~/.claude/accounts/current 2>/dev/null)
|
account=$(cat ~/.claude/accounts/current 2>/dev/null)
|
||||||
@@ -94,6 +84,15 @@ if [ -n "$model" ]; then
|
|||||||
fi
|
fi
|
||||||
[ -n "$account" ] && printf " %s[%s]\033[00m" "$brand_color" "$account"
|
[ -n "$account" ] && printf " %s[%s]\033[00m" "$brand_color" "$account"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Аккаунт/ключ Kimi — показываем email/имя из .meta, если оно есть, иначе alias.
|
||||||
|
if [ "${AI_LAUNCHER:-}" = "kimi" ]; then
|
||||||
|
kimi_account=$(cat "$HOME/.config/ai-setup/kimi_keys/current" 2>/dev/null)
|
||||||
|
if [ -n "$kimi_account" ]; then
|
||||||
|
kimi_display=$(cat "$HOME/.config/ai-setup/kimi_keys/${kimi_account}.meta" 2>/dev/null || echo "$kimi_account")
|
||||||
|
printf " %s[%s]\033[00m" "$brand_color" "$kimi_display"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
if [ -n "$effort" ]; then
|
if [ -n "$effort" ]; then
|
||||||
printf " %s%s " "$brand_color" "$model"
|
printf " %s%s " "$brand_color" "$model"
|
||||||
_effort_color "$effort"
|
_effort_color "$effort"
|
||||||
|
|||||||
@@ -739,8 +739,8 @@ fi
|
|||||||
# Удаляем старый хук с диска и из ~/.claude/settings.json (нотифаер в Stop сохраняем).
|
# Удаляем старый хук с диска и из ~/.claude/settings.json (нотифаер в Stop сохраняем).
|
||||||
info "Удаляю устаревший хук effort-save..."
|
info "Удаляю устаревший хук effort-save..."
|
||||||
rm -f "$HOME/.claude/hooks/effort-save-hook.sh"
|
rm -f "$HOME/.claude/hooks/effort-save-hook.sh"
|
||||||
# Осиротевший кэш моделей (источник старой протечки между ai-*); effort_* НЕ трогаем -
|
# Осиротевший кэш моделей (источник старой протечки между ai-*); effort_* не трогаем -
|
||||||
# он снова используется для персиста effort (включая max) через CLAUDE_CODE_EFFORT_LEVEL.
|
# они используются статусбаром для отображения текущего уровня.
|
||||||
rm -f "$HOME"/.cache/ai-setup/model_*
|
rm -f "$HOME"/.cache/ai-setup/model_*
|
||||||
python3 - "$HOME/.claude/settings.json" <<'PYEOF'
|
python3 - "$HOME/.claude/settings.json" <<'PYEOF'
|
||||||
import sys, json, os
|
import sys, json, os
|
||||||
@@ -1198,6 +1198,32 @@ _handle_api_response() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# _kimi_account_info: пытается получить email или имя аккаунта Kimi по API ключу.
|
||||||
|
# Сохраняет найденное значение в stdout. Если не удалось — exit 1.
|
||||||
|
_kimi_account_info() {
|
||||||
|
local api_key="$1"
|
||||||
|
local resp email name
|
||||||
|
# Пробуем несколько возможных endpoint'ов Moonshot/Kimi Code API.
|
||||||
|
for url in "https://api.kimi.com/coding/v1/account" "https://api.kimi.com/coding/v1/users/me"; do
|
||||||
|
resp=$(curl -s --max-time 10 "$url" \
|
||||||
|
-H "x-api-key: $api_key" \
|
||||||
|
-H "Accept: application/json" \
|
||||||
|
2>/dev/null || echo "")
|
||||||
|
[ -z "$resp" ] && continue
|
||||||
|
email=$(echo "$resp" | jq -r '.email // .data.email // .account.email // empty' 2>/dev/null)
|
||||||
|
name=$(echo "$resp" | jq -r '.name // .data.name // .account.name // empty' 2>/dev/null)
|
||||||
|
if [ -n "$email" ]; then
|
||||||
|
echo "$email"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [ -n "$name" ]; then
|
||||||
|
echo "$name"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
_open_browser() {
|
_open_browser() {
|
||||||
local url="$1"
|
local url="$1"
|
||||||
if command -v xdg-open &>/dev/null; then xdg-open "$url" 2>/dev/null
|
if command -v xdg-open &>/dev/null; then xdg-open "$url" 2>/dev/null
|
||||||
@@ -1211,8 +1237,7 @@ _open_browser() {
|
|||||||
# поэтому выбор модели и кэш кастом-моделей НЕ протекают между ai-* лаунчерами.
|
# поэтому выбор модели и кэш кастом-моделей НЕ протекают между ai-* лаунчерами.
|
||||||
# Общие ресурсы (skills, CLAUDE.md) шарятся симлинком из ~/.claude.
|
# Общие ресурсы (skills, CLAUDE.md) шарятся симлинком из ~/.claude.
|
||||||
# model и effortLevel сидируются как дефолты - выбор юзера через /model и /effort
|
# model и effortLevel сидируются как дефолты - выбор юзера через /model и /effort
|
||||||
# (для low/medium/high/xhigh) сохраняется нативно в этом же settings.json.
|
# сохраняется нативно в этом же settings.json (включая max через AI_EFFORT).
|
||||||
# Уровень max обрабатывается отдельно в _apply_effort (settings.json его не хранит).
|
|
||||||
# Использование: _setup_isolated_config <launcher> <default_model> <default_effort> <available_models_json>
|
# Использование: _setup_isolated_config <launcher> <default_model> <default_effort> <available_models_json>
|
||||||
_setup_isolated_config() {
|
_setup_isolated_config() {
|
||||||
local launcher="$1" default_model="$2" default_effort="${3:-high}" avail="${4:-}"
|
local launcher="$1" default_model="$2" default_effort="${3:-high}" avail="${4:-}"
|
||||||
@@ -1252,36 +1277,54 @@ with open(cfg_settings, "w") as f:
|
|||||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
PYEOF
|
PYEOF
|
||||||
|
|
||||||
|
# Деплой shared хуков аккаунтов в изолированный config dir.
|
||||||
|
# ai-claude использует ~/.claude/hooks напрямую; остальные лаунчеры получают
|
||||||
|
# свою копию, чтобы /switch-account и /add-account работали изолированно.
|
||||||
|
mkdir -p "$cfg/hooks"
|
||||||
|
for hook in switch-account-hook.sh add-account-hook.sh account-email.sh; do
|
||||||
|
if [ -f "$HOME/.claude/hooks/$hook" ]; then
|
||||||
|
cp "$HOME/.claude/hooks/$hook" "$cfg/hooks/$hook"
|
||||||
|
chmod +x "$cfg/hooks/$hook"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Регистрируем UserPromptSubmit хуки в изолированном settings.json
|
||||||
|
python3 - "$cfg/settings.json" "$cfg/hooks/switch-account-hook.sh" "$cfg/hooks/add-account-hook.sh" <<'PYEOF'
|
||||||
|
import sys, json, os
|
||||||
|
settings_path, switch_hook, add_hook = sys.argv[1:4]
|
||||||
|
data = {}
|
||||||
|
if os.path.exists(settings_path):
|
||||||
|
try:
|
||||||
|
with open(settings_path) as f:
|
||||||
|
data = json.load(f)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
data.setdefault("hooks", {}).setdefault("UserPromptSubmit", [{"hooks": []}])
|
||||||
|
ups = data["hooks"]["UserPromptSubmit"]
|
||||||
|
for hook_path in (switch_hook, add_hook):
|
||||||
|
hook_cmd = f'bash "{hook_path}"'
|
||||||
|
already = any(
|
||||||
|
any(h.get("command", "") == hook_cmd for h in entry.get("hooks", []))
|
||||||
|
for entry in ups
|
||||||
|
)
|
||||||
|
if not already:
|
||||||
|
ups[0]["hooks"].append({"type": "command", "command": hook_cmd})
|
||||||
|
with open(settings_path, "w") as f:
|
||||||
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||||
|
f.write("\n")
|
||||||
|
PYEOF
|
||||||
}
|
}
|
||||||
|
|
||||||
# _apply_effort: per-launcher persistence уровня effort (гибрид).
|
# _apply_effort: per-launcher persistence уровня effort.
|
||||||
# - low/medium/high/xhigh живут нативно в settings.json лаунчера -> /effort работает,
|
# Все уровни (включая max) пишутся в settings.json лаунчера через AI_EFFORT.
|
||||||
# уровень сохраняется между сессиями, env-переменная НЕ ставится.
|
# Без AI_EFFORT - effortLevel персистнут нативно в settings.json (Claude Code его читает).
|
||||||
# - max единственный нельзя сохранить в settings.json (он session-only), поэтому
|
# CLAUDE_CODE_EFFORT_LEVEL не используем - он блокирует /effort внутри сессии.
|
||||||
# его восстанавливаем через CLAUDE_CODE_EFFORT_LEVEL. В такой max-сессии /effort
|
|
||||||
# залочен env-переменной (ограничение Claude Code).
|
|
||||||
# Текущий уровень (вкл. max) ловит статусбар в ~/.cache/ai-setup/effort_<launcher>.
|
|
||||||
# Сменить уровень из max: AI_EFFORT=<lvl> ai-<launcher>.
|
|
||||||
# Использование: _apply_effort <launcher> <default_effort>
|
# Использование: _apply_effort <launcher> <default_effort>
|
||||||
_apply_effort() {
|
_apply_effort() {
|
||||||
local launcher="$1" default_effort="${2:-high}"
|
local launcher="$1" default_effort="${2:-high}"
|
||||||
local f="$HOME/.cache/ai-setup/effort_${launcher}"
|
|
||||||
local settings="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json"
|
local settings="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json"
|
||||||
local eff
|
|
||||||
if [ -n "${AI_EFFORT:-}" ]; then
|
if [ -n "${AI_EFFORT:-}" ]; then
|
||||||
# Явный override: запоминаем и применяем
|
python3 - "$settings" "$AI_EFFORT" <<'PYEOF'
|
||||||
eff="$AI_EFFORT"
|
|
||||||
mkdir -p "$HOME/.cache/ai-setup"
|
|
||||||
echo "$eff" > "$f"
|
|
||||||
else
|
|
||||||
eff=$(cat "$f" 2>/dev/null)
|
|
||||||
fi
|
|
||||||
if [ "$eff" = "max" ]; then
|
|
||||||
# Единственный способ восстановить max между сессиями
|
|
||||||
export CLAUDE_CODE_EFFORT_LEVEL=max
|
|
||||||
elif [ -n "${AI_EFFORT:-}" ] && [ -n "$eff" ]; then
|
|
||||||
# Явный сброс на low/medium/high/xhigh - пишем нативно в settings.json лаунчера
|
|
||||||
python3 - "$settings" "$eff" <<'PYEOF'
|
|
||||||
import sys, json, os
|
import sys, json, os
|
||||||
p, eff = sys.argv[1], sys.argv[2]
|
p, eff = sys.argv[1], sys.argv[2]
|
||||||
d = {}
|
d = {}
|
||||||
@@ -1294,7 +1337,7 @@ with open(p, "w") as fp:
|
|||||||
json.dump(d, fp, indent=2, ensure_ascii=False); fp.write("\n")
|
json.dump(d, fp, indent=2, ensure_ascii=False); fp.write("\n")
|
||||||
PYEOF
|
PYEOF
|
||||||
fi
|
fi
|
||||||
# Иначе (≤xhigh без AI_EFFORT): ничего не делаем - effortLevel уже персистнут нативно.
|
# Без AI_EFFORT: effortLevel уже персистнут в settings.json - ничего не делаем.
|
||||||
}
|
}
|
||||||
|
|
||||||
_build_ai_sys_prompt() {
|
_build_ai_sys_prompt() {
|
||||||
@@ -1451,50 +1494,124 @@ cat > "$BIN_DIR/ai-kimi" << 'KIMIEOF'
|
|||||||
# ai-kimi - запуск Claude Code через официальный Kimi Code API
|
# ai-kimi - запуск Claude Code через официальный Kimi Code API
|
||||||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/ai-api-helpers.sh" 2>/dev/null || true
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/ai-api-helpers.sh" 2>/dev/null || true
|
||||||
|
|
||||||
key_file="$HOME/.config/ai-setup/kimi_key"
|
keys_dir="$HOME/.config/ai-setup/kimi_keys"
|
||||||
api_key=""
|
legacy_key_file="$HOME/.config/ai-setup/kimi_key"
|
||||||
|
|
||||||
[ -f "$key_file" ] && api_key=$(cat "$key_file")
|
# Миграция старого одиночного файла ключа в директорию с ключами.
|
||||||
|
if [ -f "$legacy_key_file" ] && [ ! -d "$keys_dir" ]; then
|
||||||
|
mkdir -p "$keys_dir"
|
||||||
|
cp "$legacy_key_file" "$keys_dir/account1.key"
|
||||||
|
chmod 600 "$keys_dir/account1.key"
|
||||||
|
echo "account1" > "$keys_dir/current"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -n "$api_key" ]; then
|
mkdir -p "$keys_dir"
|
||||||
echo -n "Проверка сохранённого Kimi ключа... "
|
|
||||||
_claude_test_api "https://api.kimi.com/coding/v1/messages" "x-api-key: $api_key" "kimi-k2.7"
|
get_current_alias() {
|
||||||
_handle_api_response "Kimi" "$_CLAUDE_TEST_CODE" "$_CLAUDE_TEST_BODY" "Пополните баланс: https://www.kimi.com/code"
|
local current_file="$keys_dir/current"
|
||||||
ret=$_API_RET
|
local alias=""
|
||||||
if [ $ret -eq 401 ]; then
|
[ -f "$current_file" ] && alias=$(cat "$current_file")
|
||||||
rm -f "$key_file"
|
if [ -n "$alias" ] && [ ! -f "$keys_dir/${alias}.key" ]; then
|
||||||
api_key=""
|
alias=""
|
||||||
elif [ $ret -eq 429 ]; then
|
fi
|
||||||
echo -n "Продолжить всё равно? (запросы могут не проходить) [y/N] "
|
if [ -z "$alias" ]; then
|
||||||
read -r _ans; case "${_ans:-N}" in [Yy]*) ;; *) exit 1 ;; esac
|
alias=$(ls "$keys_dir"/*.key 2>/dev/null | head -1 | xargs -I{} basename {} .key 2>/dev/null || true)
|
||||||
elif [ $ret -ne 0 ]; then
|
fi
|
||||||
exit 1
|
echo "$alias"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_key_by_alias() {
|
||||||
|
local alias="$1"
|
||||||
|
[ -f "$keys_dir/${alias}.key" ] && cat "$keys_dir/${alias}.key" || echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
set_current_alias() {
|
||||||
|
echo "$1" > "$keys_dir/current"
|
||||||
|
}
|
||||||
|
|
||||||
|
save_new_key() {
|
||||||
|
local key="$1"
|
||||||
|
local alias="$2"
|
||||||
|
echo "$key" > "$keys_dir/${alias}.key"
|
||||||
|
chmod 600 "$keys_dir/${alias}.key"
|
||||||
|
}
|
||||||
|
|
||||||
|
next_free_alias() {
|
||||||
|
local i=1
|
||||||
|
while [ -f "$keys_dir/account${i}.key" ]; do
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
echo "account${i}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Миграция устаревшего default.key в accountN.key (новая схема именования).
|
||||||
|
if [ -f "$keys_dir/default.key" ]; then
|
||||||
|
old_default_alias=$(next_free_alias)
|
||||||
|
mv "$keys_dir/default.key" "$keys_dir/${old_default_alias}.key"
|
||||||
|
[ -f "$keys_dir/default.meta" ] && mv "$keys_dir/default.meta" "$keys_dir/${old_default_alias}.meta"
|
||||||
|
current=$(cat "$keys_dir/current" 2>/dev/null || true)
|
||||||
|
if [ "$current" = "default" ]; then
|
||||||
|
echo "$old_default_alias" > "$keys_dir/current"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$api_key" ]; then
|
prompt_and_save_key() {
|
||||||
echo "Получить ключ: https://www.kimi.com/code"
|
echo "Получить ключ: https://www.kimi.com/code"
|
||||||
read -r -p "Введите ваш Kimi API ключ: " api_key
|
read -r -p "Введите ваш Kimi API ключ: " api_key
|
||||||
[ -z "$api_key" ] && { echo "Выход."; exit 1; }
|
[ -z "$api_key" ] && { echo "Выход."; return 1; }
|
||||||
|
|
||||||
echo -n "Проверяю ключ и баланс... "
|
echo -n "Проверяю ключ и баланс... "
|
||||||
_claude_test_api "https://api.kimi.com/coding/v1/messages" "x-api-key: $api_key" "kimi-k2.7"
|
_claude_test_api "https://api.kimi.com/coding/v1/messages" "x-api-key: $api_key" "kimi-k2.7"
|
||||||
_handle_api_response "Kimi" "$_CLAUDE_TEST_CODE" "$_CLAUDE_TEST_BODY" "Пополните баланс: https://www.kimi.com/code"
|
_handle_api_response "Kimi" "$_CLAUDE_TEST_CODE" "$_CLAUDE_TEST_BODY" "Пополните баланс: https://www.kimi.com/code"
|
||||||
ret=$_API_RET
|
local ret=$_API_RET
|
||||||
if [ $ret -eq 0 ] || [ $ret -eq 429 ]; then
|
if [ $ret -eq 0 ] || [ $ret -eq 429 ]; then
|
||||||
mkdir -p "$(dirname "$key_file")"
|
local alias=$(next_free_alias)
|
||||||
echo "$api_key" > "$key_file"
|
save_new_key "$api_key" "$alias"
|
||||||
chmod 600 "$key_file"
|
# Пытаемся получить email/имя аккаунта для отображения в статусной строке.
|
||||||
echo "Ключ сохранён."
|
meta=$(_kimi_account_info "$api_key" 2>/dev/null || true)
|
||||||
|
if [ -n "$meta" ]; then
|
||||||
|
echo "$meta" > "$keys_dir/${alias}.meta"
|
||||||
|
chmod 600 "$keys_dir/${alias}.meta"
|
||||||
|
fi
|
||||||
|
set_current_alias "$alias"
|
||||||
|
echo "Ключ сохранён как: $alias."
|
||||||
if [ $ret -eq 429 ]; then
|
if [ $ret -eq 429 ]; then
|
||||||
echo -n "Продолжить всё равно? (запросы могут не проходить) [y/N] "
|
echo -n "Продолжить всё равно? (запросы могут не проходить) [y/N] "
|
||||||
read -r _ans; case "${_ans:-N}" in [Yy]*) ;; *) exit 1 ;; esac
|
read -r _ans; case "${_ans:-N}" in [Yy]*) ;; *) return 1 ;; esac
|
||||||
fi
|
fi
|
||||||
|
return 0
|
||||||
else
|
else
|
||||||
echo "Ключ НЕ сохранён."
|
echo "Ключ НЕ сохранён."
|
||||||
exit 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_current_key() {
|
||||||
|
local alias="$1"
|
||||||
|
local api_key=$(get_key_by_alias "$alias")
|
||||||
|
if [ -z "$api_key" ]; then
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
|
echo -n "Проверка сохранённого Kimi ключа... "
|
||||||
|
_claude_test_api "https://api.kimi.com/coding/v1/messages" "x-api-key: $api_key" "kimi-k2.7"
|
||||||
|
_handle_api_response "Kimi" "$_CLAUDE_TEST_CODE" "$_CLAUDE_TEST_BODY" "Пополните баланс: https://www.kimi.com/code"
|
||||||
|
local ret=$_API_RET
|
||||||
|
if [ $ret -eq 401 ]; then
|
||||||
|
# Удаляем невалидный ключ.
|
||||||
|
rm -f "$keys_dir/${alias}.key" "$keys_dir/${alias}.meta"
|
||||||
|
# Если ключей не осталось — сбрасываем current.
|
||||||
|
if [ -z "$(ls "$keys_dir"/*.key 2>/dev/null)" ]; then
|
||||||
|
rm -f "$keys_dir/current"
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
elif [ $ret -eq 429 ]; then
|
||||||
|
echo -n "Продолжить всё равно? (запросы могут не проходить) [y/N] "
|
||||||
|
read -r _ans; case "${_ans:-N}" in [Yy]*) ;; *) return 2 ;; esac
|
||||||
|
elif [ $ret -ne 0 ]; then
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
if ! command -v claude &>/dev/null; then
|
if ! command -v claude &>/dev/null; then
|
||||||
echo "Ошибка: Claude Code не найден. Установите через npm:"
|
echo "Ошибка: Claude Code не найден. Установите через npm:"
|
||||||
@@ -1502,9 +1619,36 @@ if ! command -v claude &>/dev/null; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
last_alias=""
|
||||||
|
while true; do
|
||||||
|
current_alias=$(get_current_alias)
|
||||||
|
api_key=$(get_key_by_alias "$current_alias")
|
||||||
|
|
||||||
|
# Если ключей нет — запросить первый.
|
||||||
|
if [ -z "$api_key" ]; then
|
||||||
|
prompt_and_save_key || exit 1
|
||||||
|
current_alias=$(get_current_alias)
|
||||||
|
api_key=$(get_key_by_alias "$current_alias")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка сохранённого ключа.
|
||||||
|
if [ -n "$api_key" ]; then
|
||||||
|
validate_current_key "$current_alias"
|
||||||
|
ret=$?
|
||||||
|
if [ $ret -eq 1 ]; then
|
||||||
|
# Невалидный ключ удалён; пересчитаем current и попробуем снова.
|
||||||
|
continue
|
||||||
|
elif [ $ret -eq 2 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
_PROMPT_FILE=$(mktemp /tmp/ai-sys-prompt.XXXXXX)
|
_PROMPT_FILE=$(mktemp /tmp/ai-sys-prompt.XXXXXX)
|
||||||
trap 'rm -f "$_PROMPT_FILE"' EXIT INT TERM
|
trap 'rm -f "$_PROMPT_FILE"' EXIT INT TERM
|
||||||
_build_ai_sys_prompt > "$_PROMPT_FILE"
|
_build_ai_sys_prompt > "$_PROMPT_FILE"
|
||||||
|
|
||||||
|
last_alias="$current_alias"
|
||||||
|
|
||||||
export AI_LAUNCHER=kimi
|
export AI_LAUNCHER=kimi
|
||||||
export CLAUDE_CONFIG_DIR="$HOME/.config/ai-setup/cfg/kimi"
|
export CLAUDE_CONFIG_DIR="$HOME/.config/ai-setup/cfg/kimi"
|
||||||
# Пикер: Opus/Sonnet = Kimi K2.7 Code, Haiku = Kimi K2.6. availableModels НЕ задаём
|
# Пикер: Opus/Sonnet = Kimi K2.7 Code, Haiku = Kimi K2.6. availableModels НЕ задаём
|
||||||
@@ -1528,6 +1672,18 @@ CLAUDE_CODE_SUBAGENT_MODEL=kimi-k2.6 \
|
|||||||
CLAUDE_CODE_DISABLE_1M_CONTEXT=1 \
|
CLAUDE_CODE_DISABLE_1M_CONTEXT=1 \
|
||||||
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \
|
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \
|
||||||
claude --dangerously-skip-permissions --system-prompt-file "$_PROMPT_FILE" "$@"
|
claude --dangerously-skip-permissions --system-prompt-file "$_PROMPT_FILE" "$@"
|
||||||
|
claude_exit=$?
|
||||||
|
|
||||||
|
# Если хук сменил ключ, перезапускаем ai-kimi с новым.
|
||||||
|
new_alias=$(get_current_alias)
|
||||||
|
if [ -n "$new_alias" ] && [ "$new_alias" != "$last_alias" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "Kimi ключ изменён (/switch-account или /add-account). Перезапускаю ai-kimi с новым ключом..."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit $claude_exit
|
||||||
|
done
|
||||||
KIMIEOF
|
KIMIEOF
|
||||||
chmod +x "$BIN_DIR/ai-kimi"
|
chmod +x "$BIN_DIR/ai-kimi"
|
||||||
|
|
||||||
@@ -1688,6 +1844,14 @@ if [ "$USE_VLESS" -eq 1 ]; then
|
|||||||
success "proxychains4 интегрирован"
|
success "proxychains4 интегрирован"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Подготавливаем изолированные config dir'ы для Claude Code-лаунчеров,
|
||||||
|
# чтобы hooks (/switch-account, /add-account) и settings.json были на месте
|
||||||
|
# ещё до первого запуска пользователем.
|
||||||
|
source "$BIN_DIR/ai-api-helpers.sh"
|
||||||
|
_setup_isolated_config deepseek opus high ''
|
||||||
|
_setup_isolated_config kimi opus high ''
|
||||||
|
_setup_isolated_config openrouter "openai/gpt-5.5" high ''
|
||||||
|
|
||||||
info "Удаляю старые версии скриптов (claude_*)..."
|
info "Удаляю старые версии скриптов (claude_*)..."
|
||||||
rm -f "$BIN_DIR/claude_gpt" "$BIN_DIR/claude_deepseek" "$BIN_DIR/claude_kimi" "$BIN_DIR/claude_gemini" "$BIN_DIR/claude_api_helpers.sh"
|
rm -f "$BIN_DIR/claude_gpt" "$BIN_DIR/claude_deepseek" "$BIN_DIR/claude_kimi" "$BIN_DIR/claude_gemini" "$BIN_DIR/claude_api_helpers.sh"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user