From fe439fd4a637c04d0d1cdcd9fa66c13eff555762 Mon Sep 17 00:00:00 2001 From: vitaly Date: Fri, 12 Jun 2026 08:19:46 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B0=D0=B2=D1=82=D0=BE=D0=B4=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20Claude-?= =?UTF-8?q?=D0=B0=D0=BA=D0=BA=D0=B0=D1=83=D0=BD=D1=82=D0=B0=20=D1=87=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=B7=20/add-account?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - новый хук add-account-hook.sh: сохраняет текущий аккаунт по реальному email (claude auth status), запускает OAuth-логин в фоне и после успеха сам сохраняет новый аккаунт в ~/.claude/accounts + делает его current - switch-account-hook.sh: активный аккаунт определяется через claude auth status, а не через хрупкий файл current - защита от порчи сохранённых credentials при рассинхроне токена - скилл add-account: краткая инструкция после срабатывания хука - ai-setup.sh: деплой add-account-hook + регистрация в UserPromptSubmit Co-Authored-By: Claude Opus 4.8 --- home-configs/claude/hooks/add-account-hook.sh | 45 +++++++++++++++++++ .../claude/hooks/switch-account-hook.sh | 5 ++- .../claude/skills/add-account/SKILL.md | 8 ++++ scripts/ai-setup.sh | 35 +++++++++++++++ 4 files changed, 92 insertions(+), 1 deletion(-) create mode 100755 home-configs/claude/hooks/add-account-hook.sh create mode 100644 home-configs/claude/skills/add-account/SKILL.md diff --git a/home-configs/claude/hooks/add-account-hook.sh b/home-configs/claude/hooks/add-account-hook.sh new file mode 100755 index 0000000..ad9c3fc --- /dev/null +++ b/home-configs/claude/hooks/add-account-hook.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# UserPromptSubmit hook: перехватывает /add-account. +# 1) сохраняет текущий аккаунт по его реальному email (claude auth status) +# 2) запускает oauth-логин в фоне (открывает браузер) +# 3) после логина фоновый процесс сам сохраняет новый аккаунт и делает его current + +input=$(cat) +prompt=$(echo "$input" | jq -r '.user_prompt // .prompt // empty' 2>/dev/null) +normalized=$(echo "$prompt" | sed 's|^[[:space:]]*/||; s|[[:space:]]*$||') + +[ "$normalized" != "add-account" ] && exit 0 + +CREDS="$HOME/.claude/.credentials.json" +ACCOUNTS_DIR="$HOME/.claude/accounts" +CURRENT_FILE="$ACCOUNTS_DIR/current" + +mkdir -p "$ACCOUNTS_DIR" + +# Сохраняем текущий активный аккаунт под его реальным email (источник истины — auth status) +if [ -f "$CREDS" ]; then + cur_email=$(claude auth status 2>/dev/null | jq -r '.email // empty' 2>/dev/null) + if [ -n "$cur_email" ]; then + cp "$CREDS" "$ACCOUNTS_DIR/${cur_email}.credentials.json" + chmod 600 "$ACCOUNTS_DIR/${cur_email}.credentials.json" + echo "$cur_email" > "$CURRENT_FILE" + fi +fi + +# Фоновый процесс: логин нового аккаунта + автосохранение после успеха. +# claude auth login ждёт авторизации в браузере и завершается после неё, +# затем мы читаем новый email и сохраняем credentials под ним. +( + claude auth login --claudeai /tmp/claude-add-account.log 2>&1 + new_email=$(claude auth status 2>/dev/null | jq -r '.email // empty' 2>/dev/null) + if [ -n "$new_email" ] && [ -f "$CREDS" ]; then + cp "$CREDS" "$ACCOUNTS_DIR/${new_email}.credentials.json" + chmod 600 "$ACCOUNTS_DIR/${new_email}.credentials.json" + echo "$new_email" > "$CURRENT_FILE" + echo "SAVED: $new_email" >> /tmp/claude-add-account.log + fi +) & +disown + +# exit 0: Claude загружает скилл add-account и говорит что делать +exit 0 diff --git a/home-configs/claude/hooks/switch-account-hook.sh b/home-configs/claude/hooks/switch-account-hook.sh index caa521e..eb44d21 100755 --- a/home-configs/claude/hooks/switch-account-hook.sh +++ b/home-configs/claude/hooks/switch-account-hook.sh @@ -26,7 +26,10 @@ if [ ${#accounts[@]} -eq 0 ]; then exit 2 fi -current=$(cat "$CURRENT_FILE" 2>/dev/null || echo "") +# Реальный активный аккаунт — источник истины claude auth status (а не хрупкий +# файл current). Это защищает от порчи сохранённых credentials при рассинхроне. +current=$(claude auth status 2>/dev/null | jq -r '.email // empty' 2>/dev/null) +[ -z "$current" ] && current=$(cat "$CURRENT_FILE" 2>/dev/null || echo "") # Найти следующий по кругу idx=-1 diff --git a/home-configs/claude/skills/add-account/SKILL.md b/home-configs/claude/skills/add-account/SKILL.md new file mode 100644 index 0000000..415fd1f --- /dev/null +++ b/home-configs/claude/skills/add-account/SKILL.md @@ -0,0 +1,8 @@ +--- +name: add-account +description: Add a new Claude.ai account (handled by UserPromptSubmit hook, no LLM needed) +--- + +Хук сохранил текущий аккаунт и открыл браузер для логина нового. Ответь ТОЛЬКО этим текстом (без markdown, без лишних слов): + +Браузер открыт — авторизуйся там. После авторизации новый аккаунт сохранится автоматически (никаких ручных шагов). Затем перезапусти ai-claude — он подхватит новый аккаунт, и /switch-account будет переключать между всеми. diff --git a/scripts/ai-setup.sh b/scripts/ai-setup.sh index 8ead554..bf8e4fb 100755 --- a/scripts/ai-setup.sh +++ b/scripts/ai-setup.sh @@ -752,6 +752,41 @@ else warn "Файл $SWITCH_HOOK_SRC не найден, пропускаю" fi +# ── 6.7.2. Хук add-account ────────────────────────────────────── +info "Деплою хук add-account..." +ADD_HOOK_SRC="$SCRIPT_DIR/home-configs/claude/hooks/add-account-hook.sh" +ADD_HOOK_DST="$HOME/.claude/hooks/add-account-hook.sh" +mkdir -p "$HOME/.claude/hooks" +if [ -f "$ADD_HOOK_SRC" ]; then + cp "$ADD_HOOK_SRC" "$ADD_HOOK_DST" + chmod +x "$ADD_HOOK_DST" + # Прописываем хук в settings.json (идемпотентно) + python3 - "$HOME/.claude/settings.json" "$ADD_HOOK_DST" <<'PYEOF' +import sys, json, os +settings_path, hook_path = sys.argv[1], sys.argv[2] +data = {} +if os.path.exists(settings_path): + with open(settings_path) as f: + try: data = json.load(f) + except json.JSONDecodeError: pass +data.setdefault("hooks", {}).setdefault("UserPromptSubmit", [{"hooks": []}]) +hook_cmd = f'bash "{hook_path}"' +ups = data["hooks"]["UserPromptSubmit"] +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 + success "Хук add-account установлен" +else + warn "Файл $ADD_HOOK_SRC не найден, пропускаю" +fi + # ── 6.8. Регистрация официального маркетплейса плагинов Claude ── info "Настраиваю маркетплейс плагинов Claude Code..." if ! command -v claude &>/dev/null; then