diff --git a/home-configs/claude/statusline-command.sh b/home-configs/claude/statusline-command.sh index ad1b1d9..1a17e0a 100644 --- a/home-configs/claude/statusline-command.sh +++ b/home-configs/claude/statusline-command.sh @@ -2,6 +2,7 @@ input=$(cat) cwd=$(echo "$input" | jq -r '.cwd') model=$(echo "$input" | jq -r '.model.display_name // empty') +model_id=$(echo "$input" | jq -r '.model.id // empty') five_pct=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty') five_reset=$(echo "$input" | jq -r '.rate_limits.five_hour.resets_at // empty') week_pct=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty') @@ -47,13 +48,72 @@ pct_color() { fi } -if [[ "$model" == *[Dd]eep[Ss]eek* ]]; then +# --- Баланс DeepSeek --- +# Моментально показываем кэшированный баланс, в фоне обновляем через API. +if [[ "$model_id" == *deepseek* ]]; then cache_file="$HOME/.cache/ai-setup/deepseek_balance" if [ -f "$cache_file" ]; then balance=$(head -1 "$cache_file") [ -n "$balance" ] && printf " \033[00;35m\$%s\033[00m" "$balance" fi + + # Фоновое обновление баланса (не чаще раза в 30 секунд) + refresh_ts="$HOME/.cache/ai-setup/deepseek_balance_refresh_ts" + now=$(date +%s) + last=$(cat "$refresh_ts" 2>/dev/null || echo 0) + if [ $(( now - last )) -gt 30 ]; then + key_file="$HOME/.config/ai-setup/deepseek_key" + if [ -f "$key_file" ]; then + echo "$now" > "$refresh_ts" 2>/dev/null + ( + api_key=$(cat "$key_file") + resp=$(curl -s --max-time 10 "https://api.deepseek.com/user/balance" \ + -H "Authorization: Bearer $api_key" \ + -H "Accept: application/json" 2>/dev/null) + if [ -n "$resp" ]; then + new_balance=$(echo "$resp" | python3 -c " +import sys, json +d = json.load(sys.stdin) +infos = d.get('balance_infos', []) +if infos: + curr = infos[0].get('currency', '') + total = infos[0].get('total_balance', '0') + print(f'{total} {curr}') +" 2>/dev/null) + if [ -n "$new_balance" ]; then + echo "$new_balance" > "$cache_file" + fi + fi + ) & + fi + fi else + # Рейт-лимиты для НЕ-DeepSeek провайдеров + # Кеш специфичен для провайдера (по model_id) чтобы не смешивать claude/kimi/openrouter + _cache_key=$(echo "${model_id:-unknown}" | sed 's/[^a-zA-Z0-9._-]/_/g') + RATE_CACHE="$HOME/.cache/ai-setup/rate_limits_${_cache_key}.cache" + mkdir -p "$HOME/.cache/ai-setup" + + # Если есть свежие данные - сохранить в кеш + if [ -n "$five_pct" ] || [ -n "$week_pct" ]; then + { + echo "FIVE_PCT=${five_pct}" + echo "FIVE_RESET=${five_reset}" + echo "WEEK_PCT=${week_pct}" + echo "WEEK_RESET=${week_reset}" + } > "$RATE_CACHE" + fi + + # Если нет данных - читать из кеша (старт сессии до первого запроса) + if [ -z "$five_pct" ] && [ -z "$week_pct" ] && [ -f "$RATE_CACHE" ]; then + # shellcheck source=/dev/null + source "$RATE_CACHE" 2>/dev/null + five_pct="${FIVE_PCT:-}" + five_reset="${FIVE_RESET:-}" + week_pct="${WEEK_PCT:-}" + week_reset="${WEEK_RESET:-}" + fi + if [ -n "$five_pct" ] && [ -n "$five_reset" ]; then five_int=$(printf '%.0f' "$five_pct") remaining=$(fmt_remaining "$five_reset") @@ -68,6 +128,9 @@ else fi fi +# ctx:0% при старте новой сессии (нет данных от API) +[ -z "$ctx_pct" ] && ctx_pct="0" + if [ -n "$ctx_pct" ]; then ctx_int=$(printf '%.0f' "$ctx_pct") color=$(pct_color "$ctx_int") diff --git a/scripts/ai-setup.sh b/scripts/ai-setup.sh index 64d6d29..c6be810 100755 --- a/scripts/ai-setup.sh +++ b/scripts/ai-setup.sh @@ -658,6 +658,11 @@ if os.path.exists(settings_path): except json.JSONDecodeError: pass data["statusLine"] = {"type": "command", "command": f"bash {script_path}"} +# SessionStart хук - триггерит вызов statusLine при старте сессии +if "hooks" not in data: + data["hooks"] = {} +if "SessionStart" not in data["hooks"]: + data["hooks"]["SessionStart"] = [{"hooks": [{"type": "command", "command": "true"}]}] with open(settings_path, "w") as f: json.dump(data, f, indent=2, ensure_ascii=False) f.write("\n") @@ -1107,7 +1112,9 @@ if [ -z "$api_key" ]; then fi fi -SYS_PROMPT=$(_build_ai_sys_prompt) +_PROMPT_FILE=$(mktemp /tmp/ai-sys-prompt.XXXXXX) +trap 'rm -f "$_PROMPT_FILE"' EXIT INT TERM +_build_ai_sys_prompt > "$_PROMPT_FILE" ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic \ ANTHROPIC_AUTH_TOKEN="$api_key" \ ANTHROPIC_MODEL=deepseek-v4-pro \ @@ -1116,7 +1123,7 @@ ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-v4-pro \ ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-v4-flash \ CLAUDE_CODE_SUBAGENT_MODEL=deepseek-v4-flash \ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \ -claude --dangerously-skip-permissions --system-prompt "$SYS_PROMPT" "$@" +claude --dangerously-skip-permissions --system-prompt-file "$_PROMPT_FILE" "$@" DEEPSEEKEOF chmod +x "$BIN_DIR/ai-deepseek" @@ -1177,7 +1184,9 @@ if ! command -v claude &>/dev/null; then exit 1 fi -SYS_PROMPT=$(_build_ai_sys_prompt) +_PROMPT_FILE=$(mktemp /tmp/ai-sys-prompt.XXXXXX) +trap 'rm -f "$_PROMPT_FILE"' EXIT INT TERM +_build_ai_sys_prompt > "$_PROMPT_FILE" ANTHROPIC_BASE_URL=https://api.kimi.com/coding \ ANTHROPIC_AUTH_TOKEN="$api_key" \ ANTHROPIC_MODEL=kimi-k2.6 \ @@ -1186,7 +1195,7 @@ ANTHROPIC_DEFAULT_SONNET_MODEL=kimi-k2.6 \ ANTHROPIC_DEFAULT_HAIKU_MODEL=kimi-k2.6 \ CLAUDE_CODE_SUBAGENT_MODEL=kimi-k2.6 \ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \ -claude --dangerously-skip-permissions --system-prompt "$SYS_PROMPT" "$@" +claude --dangerously-skip-permissions --system-prompt-file "$_PROMPT_FILE" "$@" KIMIEOF chmod +x "$BIN_DIR/ai-kimi" @@ -1247,7 +1256,9 @@ if ! command -v claude &>/dev/null; then exit 1 fi -SYS_PROMPT=$(_build_ai_sys_prompt) +_PROMPT_FILE=$(mktemp /tmp/ai-sys-prompt.XXXXXX) +trap 'rm -f "$_PROMPT_FILE"' EXIT INT TERM +_build_ai_sys_prompt > "$_PROMPT_FILE" ANTHROPIC_BASE_URL=https://openrouter.ai/api \ ANTHROPIC_AUTH_TOKEN="$api_key" \ ANTHROPIC_MODEL=openai/gpt-5.5 \ @@ -1256,7 +1267,7 @@ ANTHROPIC_DEFAULT_SONNET_MODEL=anthropic/claude-4.6-sonnet \ ANTHROPIC_DEFAULT_HAIKU_MODEL=openai/gpt-5.5 \ CLAUDE_CODE_SUBAGENT_MODEL=openai/gpt-5.5 \ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \ -claude --dangerously-skip-permissions --system-prompt "$SYS_PROMPT" "$@" +claude --dangerously-skip-permissions --system-prompt-file "$_PROMPT_FILE" "$@" OPENROUTEREOF chmod +x "$BIN_DIR/ai-openrouter" @@ -1295,8 +1306,10 @@ cat > "$BIN_DIR/ai-claude" << 'CLAUDEEOF' #!/usr/bin/env bash # ai-claude - запуск оригинального Claude Code (Anthropic) source "$HOME/.local/bin/ai-api-helpers.sh" 2>/dev/null || true -SYS_PROMPT=$(_build_ai_sys_prompt) -exec claude --dangerously-skip-permissions --system-prompt "$SYS_PROMPT" "$@" +_PROMPT_FILE=$(mktemp /tmp/ai-sys-prompt.XXXXXX) +trap 'rm -f "$_PROMPT_FILE"' EXIT INT TERM +_build_ai_sys_prompt > "$_PROMPT_FILE" +claude --dangerously-skip-permissions --model sonnet --system-prompt-file "$_PROMPT_FILE" "$@" CLAUDEEOF chmod +x "$BIN_DIR/ai-claude" @@ -1308,7 +1321,7 @@ if [ "$USE_VLESS" -eq 1 ]; then sed -i 's/^claude --dangerously-skip-permissions/proxychains4 -f "\$HOME\/\.proxychains-xray\.conf" claude --dangerously-skip-permissions/' "$BIN_DIR/ai-kimi" sed -i 's/^claude --dangerously-skip-permissions/proxychains4 -f "\$HOME\/\.proxychains-xray\.conf" claude --dangerously-skip-permissions/' "$BIN_DIR/ai-openrouter" sed -i 's/^\([[:space:]]*\)exec "\$agy_bin"/\1exec proxychains4 -f "\$HOME\/\.proxychains-xray\.conf" "\$agy_bin"/' "$BIN_DIR/ai-gemini" - sed -i 's/^exec claude/exec proxychains4 -f "\$HOME\/\.proxychains-xray\.conf" claude/' "$BIN_DIR/ai-claude" + sed -i 's/^claude --dangerously-skip-permissions/proxychains4 -f "\$HOME\/\.proxychains-xray\.conf" claude --dangerously-skip-permissions/' "$BIN_DIR/ai-claude" success "proxychains4 интегрирован" fi