From e50876f616f5f6c793150054149df7e55aaad427 Mon Sep 17 00:00:00 2001 From: vitaly Date: Sun, 31 May 2026 20:02:05 +0700 Subject: [PATCH] feat: configure ai-kimi with Artemox API --- README.md | 2 +- ai-setup.sh | 159 +++++++++++++++++++++++++++++++++++++++++++- tests/test_fixes.sh | 29 +++++--- 3 files changed, 179 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1faca3f..a998d9f 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ bash ai-setup.sh * `ai-claude` — оригинальный Claude Code (от Anthropic) * `ai-gpt` — OpenAI Codex * `ai-deepseek` — DeepSeek (проверяет и сохраняет ваш API-ключ) -* `ai-kimi` — Kimi K2.6 (от Moonshot) +* `ai-kimi` — Kimi K2.6 через Artemox API * `ai-gemini` — Gemini (через Antigravity CLI) > **💡 Совет по Gemini:** Для `ai-gemini` настоятельно рекомендуется использовать отдельный Google-аккаунт, чтобы избежать потенциальных блокировок основного аккаунта. diff --git a/ai-setup.sh b/ai-setup.sh index 902c5b7..f820146 100755 --- a/ai-setup.sh +++ b/ai-setup.sh @@ -500,7 +500,9 @@ chmod +x "$BIN_DIR/ai-deepseek" # === ai-kimi === cat > "$BIN_DIR/ai-kimi" << 'KIMIEOF' #!/usr/bin/env bash -# ai-kimi — запуск нативного Kimi Code +# ai-kimi — запуск нативного Kimi Code через Artemox API + +source "$HOME/.local/bin/ai-api-helpers.sh" 2>/dev/null || true kimi_bin="$HOME/.kimi-code/bin/kimi" [ ! -f "$kimi_bin" ] && kimi_bin="$(command -v kimi 2>/dev/null)" @@ -517,6 +519,161 @@ if [ -z "$kimi_bin" ] || [ ! -f "$kimi_bin" ]; then exit 1 fi +case "${1:-}" in + --version|-V|--help|-h) + exec "$kimi_bin" "$@" + ;; +esac + +config_file="${KIMI_CODE_HOME:-$HOME/.kimi-code}/config.toml" +key_file="$HOME/.config/claude-launcher/kimi_key" +model_alias="artemox/kimi-k2.6" +model_name="kimi-k2.6" +base_url="https://api.artemox.com/v1" + +_extract_artemox_key() { + [ -f "$config_file" ] || return 0 + python3 - "$config_file" <<'PYEOF' +import re +import sys + +path = sys.argv[1] +try: + lines = open(path, encoding="utf-8").read().splitlines() +except OSError: + raise SystemExit(0) + +inside = False +for line in lines: + stripped = line.strip() + if re.match(r'^\[providers\.(?:"artemox"|artemox)\]$', stripped): + inside = True + continue + if inside and stripped.startswith("["): + inside = False + if inside: + match = re.match(r'^api_key\s*=\s*"(.*)"\s*$', stripped) + if match: + print(match.group(1)) + break +PYEOF +} + +_write_artemox_config() { + mkdir -p "$(dirname "$config_file")" + ARTEMOX_API_KEY="$api_key" python3 - "$config_file" <<'PYEOF' +import json +import os +import re +import sys + +path = sys.argv[1] +api_key = os.environ["ARTEMOX_API_KEY"] + +try: + content = open(path, encoding="utf-8").read() +except OSError: + content = "" + +lines = content.splitlines() +filtered = [] +skip = False +for line in lines: + stripped = line.strip() + if re.match(r'^\[providers\.(?:"artemox"|artemox)\]$', stripped): + skip = True + continue + if re.match(r'^\[models\."artemox/kimi-k2\.6"\]$', stripped): + skip = True + continue + if skip and stripped.startswith("["): + skip = False + if skip: + continue + if re.match(r'^\s*default_model\s*=', line): + continue + filtered.append(line) + +body = "\n".join(filtered).strip() +managed = f'''default_model = "artemox/kimi-k2.6" + +# Managed by ai-kimi. Re-run ai-kimi to refresh Artemox settings. +[providers.artemox] +type = "openai" +base_url = "https://api.artemox.com/v1" +api_key = {json.dumps(api_key)} + +[models."artemox/kimi-k2.6"] +provider = "artemox" +model = "kimi-k2.6" +max_context_size = 262144 +capabilities = ["thinking", "tool_use"] +''' + +new_content = managed +if body: + new_content += "\n" + body + "\n" + +with open(path, "w", encoding="utf-8") as fh: + fh.write(new_content) +PYEOF + chmod 600 "$config_file" +} + +_prompt_artemox_key() { + echo "Получить/проверить ключ: https://artemox.com/dashboard" + if [ ! -t 0 ]; then + echo "Artemox API ключ не найден. Запустите ai-kimi в интерактивном терминале и введите ключ." + exit 1 + fi + read -r -p "Введите ваш Artemox API ключ: " api_key + [ -z "$api_key" ] && { echo "Выход."; exit 1; } +} + +api_key="" +[ -f "$key_file" ] && api_key=$(cat "$key_file") +[ -z "$api_key" ] && api_key=$(_extract_artemox_key) +[ -z "$api_key" ] && _prompt_artemox_key + +if declare -F _claude_test_openai_api >/dev/null && declare -F _handle_openai_api_response >/dev/null; then + echo -n "Проверка Artemox ключа... " + _claude_test_openai_api "$base_url/chat/completions" "$api_key" "$model_name" + _handle_openai_api_response "Artemox/Kimi" "$_CLAUDE_TEST_CODE" "$_CLAUDE_TEST_BODY" "Пополните баланс: https://artemox.com/dashboard" + + case "$_CLAUDE_TEST_CODE" in + 200|000) + ;; + 401|403) + rm -f "$key_file" + api_key="" + echo "Сохранённый Artemox ключ недействителен." + _prompt_artemox_key + echo -n "Повторная проверка Artemox ключа... " + _claude_test_openai_api "$base_url/chat/completions" "$api_key" "$model_name" + _handle_openai_api_response "Artemox/Kimi" "$_CLAUDE_TEST_CODE" "$_CLAUDE_TEST_BODY" "Пополните баланс: https://artemox.com/dashboard" + case "$_CLAUDE_TEST_CODE" in + 200|000) ;; + *) echo "Ключ НЕ сохранён."; exit 1 ;; + esac + ;; + 429) + echo -n "Продолжить всё равно? (запросы могут не проходить) [y/N] " + read -r _ans + case "${_ans:-N}" in [Yy]*) ;; *) exit 1 ;; esac + ;; + *) + echo "Ключ НЕ сохранён." + exit 1 + ;; + esac +fi + +mkdir -p "$(dirname "$key_file")" +echo "$api_key" > "$key_file" +chmod 600 "$key_file" +_write_artemox_config +echo "Kimi настроен на Artemox: $model_alias" + exec "$kimi_bin" "$@" KIMIEOF chmod +x "$BIN_DIR/ai-kimi" diff --git a/tests/test_fixes.sh b/tests/test_fixes.sh index 6409c34..30724e5 100755 --- a/tests/test_fixes.sh +++ b/tests/test_fixes.sh @@ -13,7 +13,7 @@ fail() { echo "[FAIL] $1"; FAIL=$((FAIL+1)); } # Extract sections GPT_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-gpt"/,/^GPTEOF/' "$SCRIPT") KIMI_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-kimi"/,/^KIMIEOF/' "$SCRIPT") -GEMINI_SECTION=$(awk '/^cat > "\$BIN_DIR\/claude_gemini"/,/^GEMINIEOF/' "$SCRIPT") +GEMINI_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-gemini"/,/^GEMINIEOF/' "$SCRIPT") # ── ai-gpt: auto-install codex ──────────────────────────────────────────── test_gpt_autoinstall() { @@ -51,14 +51,24 @@ test_kimi_no_proxy() { fi } -# ── Fix 5: re-validate after claude_gemini reauth (both 401 and 429) ───────── -test_fix5_gemini_revalidate() { - local count - count=$(echo "$GEMINI_SECTION" | grep -c '_claude_test_api' || true) - if [ "$count" -ge 2 ]; then - ok "Fix5: _claude_test_api present in gemini reauth flow ($count occurrences)" +# ── ai-kimi: Artemox config generation ─────────────────────────────────── +test_kimi_artemox_config() { + if echo "$KIMI_SECTION" | grep -q 'api.artemox.com/v1' \ + && echo "$KIMI_SECTION" | grep -q 'config.toml' \ + && echo "$KIMI_SECTION" | grep -q 'default_model = "artemox/kimi-k2.6"'; then + ok "ai-kimi: configures Artemox provider and kimi-k2.6 model" else - fail "Fix5: _claude_test_api missing or only in one branch (found $count)" + fail "ai-kimi: missing Artemox config generation" + fi +} + +# ── ai-gemini: native launcher generation ───────────────────────────────── +test_gemini_native_launcher() { + if echo "$GEMINI_SECTION" | grep -q 'antigravity CLI (agy)' \ + && echo "$GEMINI_SECTION" | grep -q 'https://antigravity.google/cli/install.sh'; then + ok "ai-gemini: native agy launcher is generated" + else + fail "ai-gemini: missing native agy launcher generation" fi } @@ -86,7 +96,8 @@ test_gpt_autoinstall test_gpt_no_proxy test_kimi_autoinstall test_kimi_no_proxy -test_fix5_gemini_revalidate +test_kimi_artemox_config +test_gemini_native_launcher test_fix7_trap_tmp echo ""