From de5e94922b7b1a4b97aa61a8a61fda82475cb940 Mon Sep 17 00:00:00 2001 From: vitaly Date: Sat, 30 May 2026 23:02:37 +0700 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D0=B8=20DeepSeek,=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82=D1=8C=20CLAUDE.md=20?= =?UTF-8?q?=D0=B8=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D1=81=D0=B8=D0=B3?= =?UTF-8?q?=D0=BD=D0=B0=D0=BB=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - claude_setup.sh: обновлён эндпоинт DeepSeek API (/v1/models вместо /anthropic/v1/models), исправлена аутентификация (Authorization: Bearer вместо x-api-key), обновлены модели (deepseek-v4-pro, deepseek-v4-flash), убрано предложение сделать , добавлен авто-перезапуск shell после выполнения скрипта - CLAUDE.md: правила для агентов (запрет самостоятельных коммитов) - test_interactive.py: тест обработки SIGINT в интерактивном bash (PTY) - test_sigint.sh: тест сигналов для фоновых процессов Co-Authored-By: Claude Opus 4.8 --- CLAUDE.md | 5 ++ claude_setup.sh | 28 +++++---- test_interactive.py | 135 ++++++++++++++++++++++++++++++++++++++++++++ test_sigint.sh | 18 ++++++ 4 files changed, 174 insertions(+), 12 deletions(-) create mode 100644 CLAUDE.md create mode 100644 test_interactive.py create mode 100644 test_sigint.sh diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..62b48f4 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +# Правила для агентов (Agent Instructions) + +Данные правила применяются ко всем агентам (Claude, Gemini, DeepSeek и др.), работающим в этом проекте. + +1. **Никаких самостоятельных коммитов (No unauthorized commits):** ЗАПРЕЩЕНО выполнять команду `git commit`, если пользователь прямо и недвусмысленно не попросил об этом. Вы можете изменять файлы, выполнять `git add` (если это часть подготовки), но финальный коммит делает пользователь или вы — строго по его команде. diff --git a/claude_setup.sh b/claude_setup.sh index 5a4e64b..3e9e095 100644 --- a/claude_setup.sh +++ b/claude_setup.sh @@ -226,9 +226,9 @@ claude_deepseek() { echo "Проверяю ключ..." local http_code - http_code=$(curl -sf -o /dev/null -w "%{http_code}" \ - https://api.deepseek.com/anthropic/v1/models \ - -H "x-api-key: $api_key" 2>/dev/null || echo "000") + http_code=$(curl -s -o /dev/null -w "%{http_code}" \ + https://api.deepseek.com/v1/models \ + -H "Authorization: Bearer $api_key" 2>/dev/null || echo "000") if [ "$http_code" = "200" ]; then mkdir -p "$(dirname "$key_file")" @@ -248,11 +248,11 @@ claude_deepseek() { ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic \ ANTHROPIC_AUTH_TOKEN="$api_key" \ - ANTHROPIC_MODEL=deepseek-chat \ - ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek-chat \ - ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-chat \ - ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-chat \ - CLAUDE_CODE_SUBAGENT_MODEL=deepseek-chat \ + ANTHROPIC_MODEL=deepseek-v4-pro \ + ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek-v4-pro \ + 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 "$@" } @@ -338,10 +338,6 @@ echo -e "${GREEN}═════════════════════ echo -e "${GREEN} Установка завершена!${NC}" echo -e "${GREEN}════════════════════════════════════════════════════${NC}" echo "" -echo "Примените изменения:" -echo "" -echo -e " ${CYAN}source ~/.bashrc${NC}" -echo "" echo "Доступные команды:" echo -e " ${CYAN}claude_anthropic${NC} — оригинальный Claude (Anthropic API)" echo -e " ${CYAN}claude_gpt${NC} — GPT-5.5 (ChatGPT Plus/Pro, браузерная авторизация)" @@ -359,3 +355,11 @@ echo -e " GPT статус: ${CYAN}claude-code-proxy codex auth status${NC} echo -e " GPT выйти: ${CYAN}claude-code-proxy codex auth logout${NC}" echo -e " Gemini WebUI: ${CYAN}http://localhost:8080${NC} (когда прокси запущен)" echo "" + +if [ "$0" != "${BASH_SOURCE[0]}" ]; then + return 0 2>/dev/null || exit 0 +fi + +echo -e "${CYAN}Перезапускаю shell для применения функций...${NC}" +exec bash +echo "" diff --git a/test_interactive.py b/test_interactive.py new file mode 100644 index 0000000..cf229a5 --- /dev/null +++ b/test_interactive.py @@ -0,0 +1,135 @@ +import os +import pty +import time +import subprocess +import signal + +def run_test(): + # Create pseudo-terminal + master, slave = pty.openpty() + + # Start bash in interactive mode + p = subprocess.Popen( + ['bash', '-i'], + stdin=slave, + stdout=slave, + stderr=slave, + close_fds=True, + preexec_fn=os.setsid # Create a new session/process group + ) + + # Close slave end in parent + os.close(slave) + + # Set master to non-blocking + os.set_blocking(master, False) + + # Helper to write to bash + def write_to_bash(cmd): + os.write(master, cmd.encode()) + time.sleep(0.5) + + # Define function in the interactive shell + func_def = """ +my_func() { + sleep 100 & + local bg_pid=$! + echo "ACTUAL_BG_PID:$bg_pid" + sleep 10 + echo "RUNNING CLEANUP" + kill $bg_pid 2>/dev/null + wait $bg_pid 2>/dev/null + echo "CLEANUP DONE" +} +""" + write_to_bash(func_def + "\n") + + # Run the function + write_to_bash("my_func\n") + + # Read output to wait for "ACTUAL_BG_PID:" + output = b"" + start_time = time.time() + bg_pid = None + while time.time() - start_time < 5: + try: + r = os.read(master, 1024) + if r: + output += r + except BlockingIOError: + pass + + if b"ACTUAL_BG_PID:" in output: + parts = output.split(b"ACTUAL_BG_PID:") + if len(parts) > 1: + # Get the part after ACTUAL_BG_PID: + rest = parts[-1].strip() + # Split by newline or carriage return or spaces + potential_pid = rest.split(b"\r")[0].split(b"\n")[0].split(b" ")[0].decode().strip() + if potential_pid.isdigit(): + bg_pid = int(potential_pid) + break + time.sleep(0.1) + + print("Found BG PID:", bg_pid) + print("Output so far:", output.decode('utf-8', errors='ignore')) + + if bg_pid is None: + print("Error: Could not find BG PID.") + p.terminate() + return + + # Wait a bit to ensure it is sleeping + time.sleep(1) + + # In a terminal, Ctrl+C sends SIGINT to the foreground process group. + # The shell sets the foreground process group of the terminal to the active job. + # Let's get the foreground process group of the master terminal. + # We can use os.tcgetpgrp(master) to get the process group currently in the foreground! + try: + fore_pgid = os.tcgetpgrp(master) + print(f"Foreground process group of tty: {fore_pgid}") + # Send SIGINT to the foreground process group + os.killpg(fore_pgid, signal.SIGINT) + except Exception as e: + print("Failed to get foreground pgid via tcgetpgrp or killpg:", e) + # Fallback: kill the bash process group + pgid = os.getpgid(p.pid) + print(f"Fallback: sending SIGINT to bash process group {pgid}") + os.killpg(pgid, signal.SIGINT) + + # Give it some time to process + time.sleep(2) + + # Let's read the remaining output to see if "RUNNING CLEANUP" was printed + try: + time.sleep(1) + remaining = b"" + start_time = time.time() + while time.time() - start_time < 2: + try: + r = os.read(master, 4096) + if r: + remaining += r + except BlockingIOError: + pass + time.sleep(0.1) + print("Remaining output:", remaining.decode('utf-8', errors='ignore')) + except Exception as e: + print("Read error or no more output:", e) + + # Check if bg_pid is still running + try: + os.kill(bg_pid, 0) + print(f"VERDICT: Process {bg_pid} is STILL RUNNING! It was orphaned!") + # Clean it up + os.kill(bg_pid, signal.SIGKILL) + except OSError: + print(f"VERDICT: Process {bg_pid} is NOT running. Cleanup worked!") + + # Clean up bash + p.terminate() + p.wait() + +if __name__ == '__main__': + run_test() diff --git a/test_sigint.sh b/test_sigint.sh new file mode 100644 index 0000000..430c805 --- /dev/null +++ b/test_sigint.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +test_func() { + echo "Starting background process..." + sleep 100 & + local pid=$! + echo "Background PID: $pid" + + echo "Starting foreground sleep (simulate claude)..." + sleep 10 + + echo "Foreground sleep finished. Cleaning up background process $pid..." + kill "$pid" + wait "$pid" 2>/dev/null + echo "Cleanup done." +} + +test_func