Пульт · подписки Claude Code

Как пульт переключает аккаунты Claude Code

На одной машине работают несколько подписок Claude Code. Пульт сам решает, на какой из них запускать каждую новую сессию агента, и переходит на следующую, когда текущая почти исчерпана. Ниже описано, как это устроено и почему именно так.

Состояние на 1 октября 2026 года. Источник — код пульта (cockpit/shared/claude-accounts.ts, cockpit/server/) и его доска задач.

Коротко

Работа тратит один аккаунт, пока он не израсходован на 98 % по недельному лимиту или по пятичасовому окну. Потом новая работа уходит на следующий аккаунт по списку. Сессии на двух аккаунтах одновременно не работают никогда: пока на аккаунте идёт хоть одна живая сессия, новая работа встаёт туда же или ждёт. Начатая сессия продолжается на том аккаунте, где началась. Владелец может вручную закрепить новую работу за конкретным аккаунтом.

Словарь

Пульт
Локальный сервер владельца (кокпит) с веб-панелью. Видит все проекты на машине, запускает и останавливает сессии агентов, считает лимиты.
Сессия
Один запущенный Claude Code: процесс CLI в окне tmux, работающий в папке проекта.
Наряд
Заявка пульту «подними сессию в таком-то проекте с такой-то задачей» или «продолжи эту сессию». Наряды ставят владелец из панели и штаб.
Штаб
Агент-диспетчер, который ставит машинную работу без участия человека: просыпается по расписанию и раздаёт наряды.
Вотчер
Отдельный процесс рядом с пультом. Сервер пульта открыт в домашнюю сеть, поэтому сам процессы не запускает: он записывает намерение, а сессию поднимает вотчер.
Аккаунт
Одна подписка Claude (Pro или Max) со своими лимитами. В пульте у каждого аккаунта есть короткое имя; основной называется main.

Зачем это нужно

У подписки Claude два ограничения: пятичасовое окно и недельный лимит. Пульт часто держит несколько агентов одновременно, и даже самый большой тариф выгорает раньше конца недели. Раньше в этот момент вся машинная работа останавливалась до сброса. С несколькими аккаунтами она переезжает на следующий и продолжается.

Что такое аккаунт технически

Claude Code хранит вход и настройки в каталоге конфигурации. По умолчанию это ~/.claude, но переменная окружения CLAUDE_CONFIG_DIR указывает CLI на другой каталог. На этом и построена схема:

Реестр аккаунтов — файл ~/.cockpit/claude-accounts.json, в нём имя, путь к каталогу и ожидаемая почта. Секретов там нет: токены хранит сам CLI в Связке ключей macOS, пульт знает только путь. Чтобы запустить сессию на втором аккаунте, вотчер поднимает tmux с -e CLAUDE_CONFIG_DIR=<каталог>. Если в окружении tmux или вотчера лежит ключ вроде ANTHROPIC_API_KEY, наряд проваливается с именем ключа: CLI молча предпочёл бы ключ входу каталога, и работа ушла бы на чужой счёт.

Владелец выбирал из двух способов: этот (отдельный каталог на аккаунт) и долгоживущий токен через CLAUDE_CODE_OAUTH_TOKEN. Выбран каталог. В этом варианте пульт не хранит учётных данных вовсе: в сессию передаётся только путь.

Откуда пульт знает остаток

Раз в 15 минут пульт собирает сводку лимитов по каждому аккаунту. Сначала он читает кэш, который CLI сам держит в .claude.json каталога. Если кэш устарел, пульт запускает claude -p /usage с окружением этого аккаунта и разбирает вывод. Для каждого аккаунта получается четыре числа: процент пятичасового окна, процент недели и время сброса каждого.

Сводка — снимок, поэтому в ней может стоять 100 % у окна, которое уже сбросилось. Правило ротации это учитывает: окно, у которого прошло время сброса, аккаунт не блокирует.

mainMax 20×упёрся
Пятичасовое окно
99 %
Неделя
71 %
secondMax 5×сюда пойдёт новая работа
Пятичасовое окно
12 %
Неделя
40 %

Пример, числа условные. Вертикальная риска на каждой шкале — порог 98 %. Хватает одного окна за риской, чтобы аккаунт перестал брать новую работу. Если на main ещё идут живые сессии, новая работа не уйдёт на second, а будет ждать (правило 2 ниже).

Как выбирается аккаунт для новой работы

Решение принимает одна функция сервера, newWorkPick. Её вызывают запуск наряда, будильник штаба и панель, которая показывает, куда пойдёт следующая сессия. Функция одна, чтобы панель не обещала один аккаунт, пока работа садится на другой. Проверки идут по порядку, первая сработавшая решает:

  1. Ручной выбор владельца

    Если в панели закреплён конкретный аккаунт, новая работа идёт туда. Если этот аккаунт ждёт входа или на другом аккаунте идут живые сессии, будет отказ с причиной, а тихого перехода на другой аккаунт не будет. Свою работу владелец может запустить на выбранном вручную аккаунте даже за порогом: это его решение. Машинной работе штаба исчерпанный аккаунт не достаётся никогда.

  2. Где сейчас идёт работа

    Если на каком-то аккаунте живут сессии, новая работа идёт только туда. Если этот аккаунт упёрся, ответ — отказ 409 с текстом «на “main” живут 3 сессии, а “main”: пятичасовое окно 100 % (сброс 18:30)». Запуска на соседнем аккаунте при этом нет.

  3. Текущий аккаунт

    Если живой работы нет, берётся «текущий» — тот, где запускался последний наряд Claude Code. Проваленные наряды не считаются. Пока текущий ниже 98 % по обоим окнам, работа остаётся на нём.

  4. Следующий по кругу

    Текущий упёрся — берётся следующий годный аккаунт в порядке реестра, по кругу. Аккаунты с известными обоими окнами идут раньше тех, чей остаток пульт пока не знает.

  5. Отказ всем

    Если упёрлись все, пульт отказывает с причиной по каждому аккаунту. Запуск на исчерпанном аккаунте бессмыслен: сессия умирает на первом же запросе к модели.

При одном аккаунте вся логика молчит, и поведение такое же, как до мульти-аккаунта: машинную работу держит порог проекта (по умолчанию 95 % недели), а работа владельца идёт без ограничений.

Почему один аккаунт за раз

Это прямое требование владельца: одновременно использовать два аккаунта небезопасно, и параллельные сессии на разных аккаунтах допускать нельзя. Второй аккаунт служит переливом, когда первый кончился, и не используется для удвоения параллельной работы.

Чтобы правило работало, пульт должен знать, где живёт работа. При каждом решении модуль account-live.ts смотрит процессы Claude Code через ps eww (результат кэшируется на несколько секунд): в окружении процесса видно, есть ли CLAUDE_CONFIG_DIR и какой каталог в нём указан. Поэтому учитываются и сессии, которые владелец запустил руками в терминале. К ним добавляются наряды, которые аккаунт уже заняли, но процесс ещё не поднялся.

Единственное исключение

Если владелец пишет реплику агенту в чате панели, а аккаунт с живой работой упёрся, реплика уходит на аккаунт, где есть запас: человек ждёт ответа. Если запаса нет нигде, реплика получает отказ с причиной, и эту причину видно в чате.

Продолжение и перенос сессии

Сессия, которую продолжают, едет на тот аккаунт, где она реально шла последний раз. Пульт берёт его из своих нарядов. Если этот аккаунт упёрся, продолжение получает отказ «ждёт сброса»: на другой аккаунт сессия сама не переезжает.

Перенести сессию можно только явно: нарядом «продолжить» с остановкой текущего процесса и указанием нового аккаунта (kind: "resume", stopFirst: true, account). У переноса есть цена: на новом аккаунте кэш промптов холодный, и первый ход после переезда стоит дороже. Кнопки «перевести все живые сессии на другой аккаунт» пока нет, она заведена задачей.

Пример дня

09:00Живых сессий нет, последний наряд был на main. Штаб ставит три наряда, все уходят на main.
13:40Сводка показывает у main пятичасовое окно 98 %. Три сессии ещё работают. Четвёртый наряд штаба получает отказ 409: на main живая работа, а сам он упёрся.
14:05Две сессии закончили, третья простаивает больше 20 минут и аккаунт уже не держит. Живой работы нет, текущий main упёрся, поэтому следующий наряд уходит на second. Теперь текущий — second.
18:30Окно main сбросилось. Работа при этом остаётся на second: ротация не возвращается назад, пока second сам не дойдёт до 98 %.

Как добавить аккаунт

  1. Завести запись: npm run account:add -- <имя> в папке пульта или кнопкой в блоке «Лимиты и расходы» панели. Пульт создаёт каталог ~/.claude-<имя> и раскладывает в нём ссылки на общие папки.
  2. Войти. Кнопка «Войти» в панели просит вотчер запустить claude auth login --claudeai для этого каталога. Открывается окно Chrome с отдельным профилем под этот аккаунт, владелец входит своей почтой. Того же можно добиться командой, которую панель показывает для копирования в терминал.
  3. Пульт проверяет вход по файлу каталога и не верит вотчеру на слово. Аккаунт негоден, если в каталог вошёл тот же пользователь, что и в основной (браузер подставил прежний вход, второй ёмкости это не даёт), если почта не совпала с ожидаемой или если не хватает обязательных ссылок. Причина показывается в панели.

Управление и диагностика

В панели, в блоке «Лимиты и расходы», у каждого аккаунта своя строка с обоими окнами. Переключатель «Новая работа: авто / <аккаунт>» задаёт ручной выбор, и отметка показывает, куда уйдёт следующая сессия. Переключатель действует только на сессии, которые запускает пульт. Сессия, запущенная вне пульта, в терминале или десктоп-приложении, идёт на тот аккаунт, который задан у неё в окружении: без CLAUDE_CONFIG_DIR это основной. Правило «один аккаунт за раз» её при этом учитывает.

Агентам то же доступно через API пульта:

ЗапросЧто делает
GET /api/accounts/choiceРучной выбор (или «авто»), куда прямо сейчас уйдёт новая работа владельца и штаба (или почему никуда) и на каких аккаунтах живут сессии.
PUT /api/accounts/choiceПоставить аккаунт или null («авто»). Негодный аккаунт — 400, настройка не меняется.
GET /api/summary → accountsЛимиты по каждому аккаунту: проценты окон, время сброса, тариф, готовность.

Когда работа стоит, хотя запас есть

Ловушка ручного выбора

Если на аккаунте A идут живые сессии и в этот момент вручную выбрать аккаунт B, работа на A никуда не переедет, а вся новая работа, и штаба, и владельца, начнёт получать отказ, пока на A не станет пусто. Переключать стоит, когда на текущем аккаунте нет живой работы, и после переключения проверять GET /api/accounts/choice.

Проверка 1 октября: периметр и живой отказ

Правило «один аккаунт за раз» прошло ревью безопасности и проверку на живом пульте. Нашлись два дефекта с одним исходом: второй аккаунт мог подняться рядом с живой работой. Оба исправлены.

Живой отказ: наряд с явным main, пока работа шла на втором аккаунте, получил на боевом пульте 409 с перечнем живых сессий. Стор нарядов до и после совпал байт в байт. После выкатки пульт 30 секунд опрашивали сразу после рестарта: 168 срезов, ни одного с работой на main, хотя замер лимитов прошёл внутри окна.

Что пока не так, как задумано