CNN TürkÖzgür Özel'in Cemil Tugay öfkesi dinmiyor: "Burnundan fitil fitil getireceğim"Bollywood HungamaSCOOP: Tukaram Mundhe's FDA crackdown reportedly shuts food counters at INOX R City Mall for 5 daysPunchExpert raises fuel tanker safety concerns following explosion at Edo marketZDF heuteAktuelle Pressemitteilungen des ZDFالنهارالحرس الثوري يعلن اشتعال ناقلة نفط في هرمز بعد اصطدامها بلغمMalay MailHome minister: Immigration depots facing capacity pressure amid daily enforcement operationsGhaflaKRA Issues Agency Notice To Recover KSh 623 Million Tax Debt From Mike SonkoTimes of India EntertainmentKamal Haasan mourns music composer Deva, recalls their film collaborationsCBS NewsIran's president denies nuclear ambitions, says he's ready for talks매일경제‘만리장성 코카콜라 소년’으로 유명해진 8살 아이… 47년 뒤 놀라운 근황Daily MailRio Ferdinand embroiled in fat-shaming row after telling Man United influencer to 'pick up your t**s' following Instagram digMeduzaВ Конгресс США внесли законопроект, блокирующий «дизельную сделку» Трампа и Путина
The Daily Newsstand · Free, Always
Saturday, October 17, 2026

ccswap: переключаю Claude Code между своими подписками без прокси и перезапуска

Translate

У меня несколько своих подписок Claude, и Claude Code работает почти весь день: я пишу сам, параллельно крутятся агенты. Пятичасовой лимит заканчивается в самый неудобный момент, недельный — задолго до конца недели. Дальше всё одинаково: открыть терминал, выйти, залогиниться другим аккаунтом в браузере, вернуться в сессию и вспомнить, на чём остановился. В какой-то момент мне надоело делать это руками.

Так появился ccswap — демон на Go для macOS и Linux. Когда текущий аккаунт подходит к лимиту (или когда я прошу), он перелогинивает Claude Code в другой. Запущенные сессии подхватывают новый аккаунт примерно за полминуты, ничего не перезапуская. Ниже — как он устроен, на чём я спотыкался и почему пользоваться им стоит с открытыми глазами: у Anthropic есть правила, которые касаются этой затеи напрямую.

Страница ccswap: аккаунт в работе и его лимиты, прогноз следующей смены, лента за сутки и таблица аккаунтов

Страница ccswap: аккаунт в работе и его лимиты, прогноз следующей смены, лента за сутки и таблица аккаунтов

Почему не прокси

Первое, что приходит в голову, — прокси вроде CLIProxyAPI. Claude Code шлёт запросы на локальный адрес, прокси держит токены нескольких аккаунтов и сам решает, от чьего имени пойдёт запрос.

Я какое-то время так и жил. Проект большой и живой, и свою задачу — отдать подписки как API любым клиентам — он решает. Мне нужно было другое, и для моего случая у прокси нашлось три минуса.

Он стоит посреди каждого запроса. Весь трафик Claude Code, с кодом и токенами, идёт через ещё один сервер. Его надо поднимать, обновлять и чинить, а когда он лежит, Claude Code не работает совсем.

Claude Code перестаёт быть единственным клиентом. Через прокси подписку может тратить любая программа, которая говорит на API OpenAI, Gemini или Claude. Чтобы такие запросы проходили, CLIProxyAPI по умолчанию выдаёт их за Claude Code: подставляет его user agent и заголовки и меняет системный промпт. Это написано в его же config.example.yaml (опция disable-claude-cloak-mode, по умолчанию false). А в условиях Claude Code сказано, что OAuth-вход подписки рассчитан на Claude Code и другие родные приложения Anthropic, а сторонним разработчикам нельзя пропускать запросы через учётные данные Free, Pro и Max и быть посредником для токенов. Там же Anthropic оставляет за собой право применять эти ограничения без предупреждения.

Кэш промпта делится на аккаунты. По умолчанию CLIProxyAPI раздаёт запросы по аккаунтам по кругу (round-robin). Кэш у Anthropic не общий между организациями, а у каждой подписки организация своя. Если соседние запросы одной сессии уходят в разные аккаунты, каждый заново читает контекст без кэша. Привязку сессии к аккаунту в CLIProxyAPI включить можно (session-affinity), но по умолчанию она выключена. ccswap кэш тоже сбрасывает, но один раз на переключение, и старается переключаться реже.

Распределять запросы мне не нужно. Нужно, чтобы в нужный момент Claude Code оказался залогинен в другой аккаунт. Поэтому ccswap запросы не трогает вообще: он подменяет то, что Claude Code хранит как свой логин, а с Anthropic говорит сам Claude Code, без изменений. Безгрешным это ccswap не делает, о его отношениях с правилами Anthropic ниже, в отдельном разделе. Это моё мнение, и оно не для всех: если нескольким людям нужен общий доступ, это задача для командных тарифов Anthropic, а не для личных подписок, и ccswap для такого не годится.

Где Claude Code держит токены

Логин в Claude Code — это JSON с ключом claudeAiOauth: access-токен, refresh-токен, момент истечения access-токена и scopes. Хранится он так:

Что

macOS

Linux

Токены

связка ключей, запись Claude Code-credentials

~/.claude/.credentials.json, права 0600

Чей это аккаунт (email, организация)

oauthAccount в ~/.claude.json

там же

В том же JSON рядом с claudeAiOauth лежат и другие ключи, например mcpOAuth с логинами MCP-серверов. Их ccswap не трогает: меняет только значение claudeAiOauth, остальное сохраняет байт в байт. Перезапишете запись целиком — потеряете логины MCP.

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

Токены остальных аккаунтов ccswap держит у себя: на macOS в связке ключей (сервис ccswap), на Linux в файлах 0600 в своей папке. Попадают они туда через обычный claude auth login, который ccswap запускает во временной папке, так что пароль ccswap не видит никогда.

Refresh-токен работает один раз

Из этого ограничения растут почти все решения внутри ccswap.

Когда access-токен истекает, клиент меняет refresh-токен на новую пару, и старый refresh-токен сразу сгорает. Следствий три.

Нельзя обновлять токены аккаунта, который сейчас в Claude Code. Если ccswap потратит refresh-токен, лежащий у Claude Code, его собственное следующее обновление упадёт, и Claude Code разлогинится. Поэтому ccswap обновляет только аккаунты, которые сейчас не в работе, минут за 10 до истечения их access-токена.

Нельзя скопировать логин на вторую машину. Одни и те же токены на двух машинах живут до первого обновления: машина, которая обновится второй, получит invalid_grant. Когда мне понадобилось перенести аккаунт на сервер, из этого вышла отдельная команда, о ней ниже.

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

Здесь же я и обжёгся. Однажды я перезапустил демон через launchctl kickstart -k, чтобы подхватить новую версию. С -k launchd убивает работающий процесс сигналом SIGKILL, и дописать он ничего не успевает. В тот раз обошлось, но с тех пор демон перезапускается только через ccswap daemon install, который останавливает его нормально.

Переключение и замки Claude Code

Claude Code переписывает токены не только при логине: он обновляет их сам, и сессий может быть несколько. Чтобы две сессии не обновлялись одновременно, он берёт замки — пустые папки, созданные через mkdir. Кто нашёл такую папку, ждёт.

ccswap берёт те же замки в том же порядке:

  1. ~/.claude/.oauth_refresh.lock (через 60 секунд считается брошенным);

  2. ~/.claude.lock (тоже 60 секунд);

  3. ~/.claude/.storage-write.lock (15 секунд).

На всё вместе он ждёт не больше 10 секунд. Не дождался — автоматическое переключение переносится на следующий шаг демона, а ручное ccswap use возвращает ошибку и предлагает повторить через несколько секунд. Под замками ccswap в сеть не ходит. Он сохраняет текущие токены к себе, ещё раз читает запись (вдруг Claude Code успел её переписать), пишет токены нужного аккаунта и сразу отпускает замки.

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

И простая страховка: если access-токену Claude Code осталось жить меньше 6 минут, ccswap не переключает. Иначе можно попасть в момент, когда Claude Code обновляется, и сохранить себе копию с уже сгоревшим refresh-токеном.

Кто сейчас в Claude Code

Логин в Claude Code меняется и без ccswap: вы набрали /login, Claude Code обновил токены. Демон раз в минуту выясняет, чей логин сейчас лежит в записи.

Способов два. Первый — отпечаток: ccswap помнит SHA-256 refresh-токена, который сам туда положил или уже опознал, вместе с именем аккаунта. Совпал хеш — аккаунт известен, в сеть ходить не надо. Второй — спросить профиль: GET /api/oauth/profile по access-токену возвращает email. Профиль нужен примерно раз после каждого обновления токенов в Claude Code, потому что refresh-токен при этом меняется.

С профилем связан первый баг, который нашёл не я, а Руслан, когда ставил ccswap себе. Добавление аккаунта падало с «Anthropic не назвал аккаунт входа». Email в ответе профиля лежит в account.email, а я читал email_address: так поле называется в ответе обмена кода при логине, и имя я перенёс оттуда не глядя.

Если Claude Code давно не запускался и его access-токен истёк, опознать логин нельзя, пока Claude Code не обновится сам. ccswap в этом случае ждёт. Достаточно отправить любое сообщение в claude.

Сколько осталось лимита

Лимиты ccswap берёт из GET /api/oauth/usage: пятичасовое окно, неделя и недельные окна по моделям (Opus, Sonnet), у каждого процент и время сброса. Аккаунт в работе он спрашивает раз в 5 минут, остальные раз в 30: их цифры двигаются, только если вы пользуетесь ими где-то ещё.

С этим эндпоинтом у меня тоже была история. 429 на него приходит по каждому аккаунту отдельно, и не только из-за ccswap: тот же адрес дёргают Claude Code, упёршийся в лимит, и другие инструменты. Первая версия спрашивала чаще и так упиралась в 429, что демон подолгу не знал, сколько осталось. Сейчас после 429 ccswap выжидает Retry-After (не больше часа) и удваивает интервал для этого аккаунта, но не дальше 10 минут. Обратно он ускоряется постепенно: каждые 30 спокойных ответов подряд интервал сокращается на четверть.

С устаревшими цифрами нужна дисциплина. На аккаунт, о котором данные старые, ccswap не переключится никогда. Простоять лишние минуты дешевле, чем уйти на аккаунт, который на самом деле тоже пуст.

Хук и ложные срабатывания

Опрос раз в 5 минут — это долго, если сессия упёрлась в лимит прямо сейчас. Поэтому ccswap daemon install добавляет в настройки Claude Code хук StopFailure. Когда сессия останавливается на ошибке API, Claude Code запускает ccswap signal и передаёт событие. Если это rate_limit и в сообщении сказано про лимит и время сброса, демон считает текущий аккаунт исчерпанным на час и переключает сразу.

У меня часть агентов работает на своём токене (CLAUDE_CODE_OAUTH_TOKEN), мимо логина, которым управляет ccswap. Отличать их я собирался по переменным окружения: ccswap signal пропускает событие, если видит такой токен. Не сработало. На практике хук запускается без CLAUDE_CODE_OAUTH_TOKEN, ANTHROPIC_API_KEY и ANTHROPIC_AUTH_TOKEN, проверке просто нечего видеть. Агент упирался в лимит своего аккаунта, хук срабатывал, и ccswap уводил мой Claude Code с совершенно живого аккаунта.

Напрямую такие сессии не отличить, поэтому сигнал теперь сверяется с цифрами. Сообщение говорит, какое окно кончилось (You've hit your session limit, weekly или имя модели). Свежие данные показывают это окно ниже 80% — значит, лимит кончился у кого-то другого, сигнал игнорируется. Данные старые — сначала спросить лимиты, потом решать. Сигнал в течение минуты после переключения относится к прошлому аккаунту, его ccswap тоже пропускает.

Когда и куда переключаться

Правила по умолчанию:

  • уйти с аккаунта, когда его пятичасовое окно дошло до 95% или недельное (в том числе недельное по модели) — до 98%;

  • выбрать первый подходящий: включён, не ждёт нового логина, данные свежие, все окна ниже порогов; сначала более высокий приоритет;

  • вернуться на аккаунт с более высоким приоритетом, когда его окна опустились на 10 пунктов ниже порогов;

  • между автоматическими переключениями не меньше 10 минут, если только текущий аккаунт не кончился совсем.

Внутри одного приоритета работают две стратегии. fill-first берёт аккаунт, у которого больше всего места. cache-aware сначала тратит то, что скоро сгорит: аккаунт, у которого недельный лимит сбросится раньше. Но переключение сбрасывает кэш промпта во всех запущенных сессиях, и заново прочитанный контекст тоже ест лимит. Поэтому, пока кэш тёплый, cache-aware переходит внутри приоритета только ради заметной выгоды: сброс в пределах суток и хотя бы на час раньше, чем у текущего аккаунта. Тёплый ли кэш, ccswap судит по времени последней записи в транскрипты Claude Code (~/.claude/projects), содержимое не читает. После часа без запросов кэш истёк в любом случае.

Страница со сменами

У демона есть локальная страница (ccswap web, по умолчанию 127.0.0.1:8318). Первую версию сделал Руслан, и выглядела она как панель CLIProxyAPI. Перед публикацией я переделал её с нуля под метафору, которая и есть суть ccswap: аккаунты работают сменами.

Сверху аккаунт, который сейчас в работе: пятичасовой и недельный лимиты с точным временем сброса. Рядом прогноз, кто и примерно во сколько его сменит. Ниже лента за сутки: кто держал Claude Code, кто отдыхает до сброса, кому нужен новый логин. Прогноз — это темп расхода за последний час, продолженный до порога. Если данных меньше чем за 15 минут или окно сбросится раньше, прогноза нет вовсе.

Страница собирается без сборщика и ничего не грузит извне: HTML, CSS, JS и шрифты вшиты в бинарник, строгий CSP запрещает ей ходить куда-то ещё. По умолчанию демон слушает только 127.0.0.1. Чтобы открыть страницу с другой машины, нужно явно разрешить внешний адрес в настройках, и тогда с ключом для чтения её можно только смотреть: менять настройки можно лишь с самой машины или через SSH-туннель.

Перенос аккаунта на другую машину

Раз копировать токены нельзя, нужна команда, которая переносит. ccswap migrate export work -o work.ccswap упаковывает аккаунт в зашифрованный файл (AES-256-GCM, ключ выводится через HKDF из одноразового 160-битного кода) и печатает код. В файл и в лог код не попадает.

Гарантировать я хотел две вещи: ни при каком сбое один и тот же логин не окажется живым на двух машинах, и ни при каком сбое он не пропадёт молча. Пока идёт экспорт, аккаунт заморожен: демон не обновляет его токены и не переключается на него. С исходной машины он удаляется только после того, как файл записан. Импорт сразу обновляет токены, так что второй импорт того же файла получит уже мёртвый логин. Самое неприятное место — когда непонятно, дошла ли до демона команда «удали у себя». Тогда ccswap файл не удаляет и пишет, после какого времени проверить ccswap list и что делать в каждом из исходов.

По мелочи

  • Терминальный интерфейс: ccswap tui.

  • Linux-сервер: демон работает как пользовательский сервис systemd, а на сервере без браузера логин делается вставкой кода. Руслан держит Claude Code на сервере через T3 Code, поэтому ccswap умеет отвечать на ту часть API CLIProxyAPI, которую T3 Code читает, чтобы показать лимиты.

  • Лимиты с другой машины: ccswap remote берёт цифры у другого ccswap и не спрашивает Anthropic дважды об одном аккаунте. Передаются только цифры, токены остаются на месте.

  • Языки: английский и русский. Новый язык — это один JSON-файл, код трогать не нужно.

Отдельно про нагрузку. Когда у меня работало много агентов и load average был за сотню, демон по нескольку минут не отвечал. Виноватым оказался ключ ProcessType со значением Background в описании службы для launchd: такой процесс macOS охотно отправляет в конец очереди. Убрал — прошло.

Правила Anthropic

Об этом нужно сказать прямо, а не мелким шрифтом.

Пользовательские условия Anthropic запрещают делиться аккаунтом: «You may not share your Account login information… You also may not make your Account available to anyone else». Чужие аккаунты в ccswap добавлять нельзя, только свои, на своих машинах. Раздел 3 тех же условий запрещает доступ «through automated or non-human means, whether through a bot, script, or otherwise» (кроме API-ключей и случаев, которые Anthropic разрешает явно) и обход «any of our systems or protective measures».

На странице Legal and compliance у Claude Code есть ещё два пункта, которые касаются ccswap:

  • лимиты Pro и Max «assume ordinary, individual usage of Claude Code and the Agent SDK», и переход на другую подписку, когда кончилась первая, можно прочитать как выход за эти рамки;

  • OAuth-логин предназначен для «ordinary use of Claude Code and other native Anthropic applications», а разработчикам нельзя «collect, store, or intermediate Claude.ai credentials or session tokens».

ccswap — программа, которую вы запускаете сами у себя. Логин проходит через claude auth login, собственный поток Anthropic. Токены своих аккаунтов вы храните у себя, ccswap отправляет их только в Anthropic, авторам не уходит ничего. Но ccswap не нативное приложение Anthropic и токенами пользуется сам: обновляет токены неактивных аккаунтов и спрашивает лимиты. Считается ли это «хранением учётных данных» по этим правилам, текст Anthropic не говорит. Это главный риск: Anthropic оставляет за собой право принимать меры без предупреждения. Ни разрешения, ни запрета на несколько подписок у одного человека я у Anthropic не нашёл.

Я пользуюсь ccswap сам и этот риск принимаю осознанно. Советую либо так же, либо не пользоваться вовсе.

Как поставить

go install github.com/Dudude-bit/ccswap/cmd/ccswap@latest
ccswap daemon install
ccswap adopt work
ccswap login personal

adopt добавляет аккаунт, в котором Claude Code сейчас, login — следующий, через обычный логин Claude Code. Готовые архивы под macOS и Linux (amd64 и arm64) лежат в релизах. Все команды и то, какие запросы ccswap делает к Anthropic и как часто, описаны в README и в документе об устройстве.

Проект на Go, лицензия MIT, около 600 тестов, CI гоняет их на Linux и macOS. Соавтор — Руслан: поддержка Linux, интервалы опроса, первая версия страницы, стратегия cache-aware, ccswap remote, перевод интерфейса и новый TUI. А ещё он первым поставил ccswap себе и нашёл, где тот ломается.

Больше всего мне интересно, у кого он не заведётся: Claude Code обновляется часто, а то, как он хранит логин, — не публичный интерфейс. Если что-то сломалось, issue с выводом ccswap doctor поможет больше, чем звёздочка.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.