feat: configure ai-kimi with Artemox API
This commit is contained in:
@@ -19,7 +19,7 @@ bash ai-setup.sh
|
|||||||
* `ai-claude` — оригинальный Claude Code (от Anthropic)
|
* `ai-claude` — оригинальный Claude Code (от Anthropic)
|
||||||
* `ai-gpt` — OpenAI Codex
|
* `ai-gpt` — OpenAI Codex
|
||||||
* `ai-deepseek` — DeepSeek (проверяет и сохраняет ваш API-ключ)
|
* `ai-deepseek` — DeepSeek (проверяет и сохраняет ваш API-ключ)
|
||||||
* `ai-kimi` — Kimi K2.6 (от Moonshot)
|
* `ai-kimi` — Kimi K2.6 через Artemox API
|
||||||
* `ai-gemini` — Gemini (через Antigravity CLI)
|
* `ai-gemini` — Gemini (через Antigravity CLI)
|
||||||
|
|
||||||
> **💡 Совет по Gemini:** Для `ai-gemini` настоятельно рекомендуется использовать отдельный Google-аккаунт, чтобы избежать потенциальных блокировок основного аккаунта.
|
> **💡 Совет по Gemini:** Для `ai-gemini` настоятельно рекомендуется использовать отдельный Google-аккаунт, чтобы избежать потенциальных блокировок основного аккаунта.
|
||||||
|
|||||||
159
ai-setup.sh
159
ai-setup.sh
@@ -500,7 +500,9 @@ chmod +x "$BIN_DIR/ai-deepseek"
|
|||||||
# === ai-kimi ===
|
# === ai-kimi ===
|
||||||
cat > "$BIN_DIR/ai-kimi" << 'KIMIEOF'
|
cat > "$BIN_DIR/ai-kimi" << 'KIMIEOF'
|
||||||
#!/usr/bin/env bash
|
#!/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"
|
kimi_bin="$HOME/.kimi-code/bin/kimi"
|
||||||
[ ! -f "$kimi_bin" ] && kimi_bin="$(command -v kimi 2>/dev/null)"
|
[ ! -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
|
exit 1
|
||||||
fi
|
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" "$@"
|
exec "$kimi_bin" "$@"
|
||||||
KIMIEOF
|
KIMIEOF
|
||||||
chmod +x "$BIN_DIR/ai-kimi"
|
chmod +x "$BIN_DIR/ai-kimi"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ fail() { echo "[FAIL] $1"; FAIL=$((FAIL+1)); }
|
|||||||
# Extract sections
|
# Extract sections
|
||||||
GPT_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-gpt"/,/^GPTEOF/' "$SCRIPT")
|
GPT_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-gpt"/,/^GPTEOF/' "$SCRIPT")
|
||||||
KIMI_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-kimi"/,/^KIMIEOF/' "$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 ────────────────────────────────────────────
|
# ── ai-gpt: auto-install codex ────────────────────────────────────────────
|
||||||
test_gpt_autoinstall() {
|
test_gpt_autoinstall() {
|
||||||
@@ -51,14 +51,24 @@ test_kimi_no_proxy() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Fix 5: re-validate after claude_gemini reauth (both 401 and 429) ─────────
|
# ── ai-kimi: Artemox config generation ───────────────────────────────────
|
||||||
test_fix5_gemini_revalidate() {
|
test_kimi_artemox_config() {
|
||||||
local count
|
if echo "$KIMI_SECTION" | grep -q 'api.artemox.com/v1' \
|
||||||
count=$(echo "$GEMINI_SECTION" | grep -c '_claude_test_api' || true)
|
&& echo "$KIMI_SECTION" | grep -q 'config.toml' \
|
||||||
if [ "$count" -ge 2 ]; then
|
&& echo "$KIMI_SECTION" | grep -q 'default_model = "artemox/kimi-k2.6"'; then
|
||||||
ok "Fix5: _claude_test_api present in gemini reauth flow ($count occurrences)"
|
ok "ai-kimi: configures Artemox provider and kimi-k2.6 model"
|
||||||
else
|
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
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +96,8 @@ test_gpt_autoinstall
|
|||||||
test_gpt_no_proxy
|
test_gpt_no_proxy
|
||||||
test_kimi_autoinstall
|
test_kimi_autoinstall
|
||||||
test_kimi_no_proxy
|
test_kimi_no_proxy
|
||||||
test_fix5_gemini_revalidate
|
test_kimi_artemox_config
|
||||||
|
test_gemini_native_launcher
|
||||||
test_fix7_trap_tmp
|
test_fix7_trap_tmp
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
Reference in New Issue
Block a user