Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>CodsterNew to Visual Studio Code? Get it now.
Codster

Codster

Codster

|
2 installs
| (0) | Free
AI-агент для VS Code и Cursor: чат, инструменты, правки файлов, терминал, diff и откат. Бесплатный лимит без API-ключа, модели «Хокку», «Танка», «Ода» и «Эпос».
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Codster — AI-агент для VS Code и Cursor

Cursor-подобный AI-ассистент прямо в VS Code: чат с агентом, инструменты, правки файлов, терминал и диффы. Ключ провайдера не нужен — бесплатный лимит включается при первом запуске. Сайт проекта — codster.app.

Visual Studio Marketplace Installs VS Code License

Чат Codster в панели VS Code: агент правит файл и показывает unified diff Выбор модели в композере Codster: «Хокку», «Танка», «Ода», «Эпос» Умения проекта: SKILL.md с секретами и историей изменений

Чат, режимы Agent / Ask / Plan, правки файлов, поиск по коду, терминал, диффы, подтверждения и откат изменений. Модели — «Хокку», «Танка», «Ода» и «Эпос»: они различаются глубиной размышлений, по умолчанию «Ода» (oda).

Почему Codster

  • Агент, а не автодополнение. Читает проект, ищет по коду, правит файлы, запускает тесты и проверки — и показывает unified diff на каждую правку.
  • Бесплатно начать, без API-ключа. Пробный лимит включается сам при первом запуске: ни email, ни карты, ни OPENAI_API_KEY в настройках.
  • Всё обратимо. Чекпоинт на каждый запрос и откат одной кнопкой: Codster: Отменить последние изменения агента.
  • Работает в вашем редакторе. VS Code 1.90+ и Cursor: их панель, дерево файлов, терминал и диагностика — расширение, а не форк редактора.
  • Ваши правила без переноса. .codster/rules.md, .cursor/rules/**/*.mdc, .cursorrules, AGENTS.md и .github/copilot-instructions.md читаются как есть.
  • Опасное блокируется. sudo, rm -rf /, curl | sh не выполняются, .env не попадает в поиск, запись в .git запрещена.
  • Можно держать у себя. Свой бэкенд (codster.cloudBaseUrl) и любой OpenAI-совместимый апстрим на сервере.

Скриншоты выше — панель расширения в VS Code; они снимаются с живой разметки (make screenshots), а не рисуются руками. Кадры десктоп-приложения лежат рядом в docs/ (chat.png, models.png, skills.png и остальные).


Что умеет

  • Agent mode — читает проект, ищет по коду, правит файлы, запускает тесты и проверки.
  • Ask mode — только чтение и ответы по коду, без изменений.
  • Plan mode — сначала изучает код и выдаёт план, без правок.
  • Стриминг ответа и отображение reasoning_content, если модель его возвращает.
  • Правки через точный old_string → new_string — как в Cursor, с показом unified diff.
  • Подтверждения по желанию — правки файлов и команды по умолчанию идут без вопроса, а галочка в настройках возвращает подтверждение (можно разрешить на всю сессию).
  • Откат изменений агента одной кнопкой (чекпоинт на каждый запрос).
  • Несколько чатов параллельно — у каждого свой агент, свои подтверждения и свои чекпоинты. Полоса чатов над полем ввода прокручивается колесом мыши влево-вправо, поэтому вкладки за правым краем доступны и без тачпада.
  • Контекст: активный файл, выделение, диагностика, @file-упоминания, правила проекта. Набрали @ в поле ввода (об этом же напоминает плейсхолдер) — открывается список файлов; скрепка рядом с режимом вставляет @ за вас и открывает тот же список, значка @ на кнопке больше нет.
  • Изображения: вставьте скриншот из буфера (⌘V / Ctrl+V), нажмите кнопку с картинкой в поле ввода или команду «Codster: Прикрепить изображения к чату», либо перетащите файл в чат с зажатым Shift. PNG, JPEG, GIF, WebP; до 4 штук на сообщение, файл до 10 МБ, крупные снимки сжимаются автоматически. Shift нужен из-за VS Code: во время перетаскивания без него платформа ставит webview pointer-events: none, и события drag/drop до панели просто не доходят (в Cursor это поведение отличается).
  • Правила проекта: .codster/rules.md, .cursor/rules/**/*.mdc, .cursorrules, AGENTS.md, .github/copilot-instructions.md.
  • Ключа провайдера на клиенте нет вовсе: его держит бэкенд, поэтому в настройках нечего хранить в SecretStorage и нечего синхронизировать.
  • Любой OpenAI-совместимый апстрим задаётся на сервере (CODSTER_UPSTREAM_BASE_URL), а не в расширении (см. ниже).
  • Защита: команды вроде sudo, rm -rf /, curl | sh блокируются; .env не попадает в поиск; запись в .git запрещена; выход за workspace по симлинкам блокируется.

Первый запуск сразу рабочий: пробный режим включается сам, а экран из docs/onboarding.png (кнопка «Попробовать без регистрации») показывается, только если бэкенд не ответил и триал не удался.


Установка

Из Marketplace

Страница расширения → Install, либо из терминала:

code --install-extension codster.codster

После установки: значок Codster на Activity Bar или команда Codster: Открыть чат. В Cursor расширение ставится из .vsix (см. ниже).

Из готового .vsix

VSIX="$(ls -t codster-*.vsix | head -n 1)"

# VS Code
code --install-extension "$VSIX"

# Cursor (если установлен CLI)
cursor --install-extension "$VSIX"

Либо в редакторе: Extensions → … → Install from VSIX… и выбрать файл.

Обновление после изменений

Если расширение уже установлено, не нужно повторять npm install и вручную искать VSIX:

npm run update

Команда:

  • поднимает patch-версию;
  • собирает VSIX;
  • обновляет расширение с --force в VS Code и Cursor, где оно уже установлено;
  • печатает напоминание перезагрузить окно.

После неё: Cmd/Ctrl+Shift+P → Developer: Reload Window.

Если раньше расширение ставилось под старым именем tolk.tolk-code, удалите его: оно добавляет в левую панель вторую кнопку с прежней иконкой.

code --uninstall-extension tolk.tolk-code
cursor --uninstall-extension tolk.tolk-code

scripts/install.sh делает это сам.

Если редактор не даёт переустановить расширение на ходу, закройте его и выполните:

npm run update -- --no-bump

Затем откройте редактор снова.

После установки откройте иконку Codster на activity bar.

Из исходников

npm install
npm run check      # typecheck + 52 unit-теста + сборка
npm run update     # bump версии + сборка VSIX + установка в найденные редакторы
npm run package    # соберёт codster-<version>.vsix

Требования: Node.js 18+ для сборки, VS Code 1.90+.


Старт: аккаунт, а не токен

Первый запуск не просит ни Base URL, ни ключа, и не показывает форму регистрации. Сценарий один, как в Cursor и Claude:

  1. Откройте папку проекта — панель Codster откроется справа. В десктопе без аргументов папку создавать не нужно: при первом запуске Codster сам заводит Документы/Codster/Проект и открывает её.
  2. Пишите задачу сразу: при первом запуске включается бесплатный доступ без регистрации — небольшой лимит, без email, пароля и карты. Аккаунт (Создать аккаунт) даёт лимит больше и сразу заводит организацию.
  3. Модель уже выбрана — её каталог приходит из аккаунта.

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

Аккаунт — это и есть бесплатный вход в продукт: ключ провайдера живёт на сервере, поэтому регистрация заменяет возню с API-ключом. В обмен на неё вы получаете:

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

Модель переключается в композере — чип слева от кнопки отправки, там же видно, что выбрано. Список приходит из /v1/models и содержит только то, что доступно аккаунту или его организациям; выдать id, которого нет в каталоге, нельзя — прокси такой запрос отклонит (unknown_model).

Модели включаются в настройках, раздел «Модель»: строка на каждую модель каталога с галочкой, как в Cursor. В композере видны только включённые, а пустой список означает «все доступные», поэтому после установки ничего включать не нужно. Набор хранится в codster.enabledModels (id из каталога); если модель убрали из каталога или выключили текущую, клиент сам переводит выбор на доступную.

Глубина размышлений не настройка пользователя: она задаётся моделью. Каталог приносит reasoning_effort для каждой модели, клиент отправляет ровно это значение, а прокси повторно ограничивает его, так что попросить больше, чем модель продана, нельзя. Поле в настройках убрано, а VS Code-настройка codster.reasoningEffort помечена устаревшей и перезаписывается из каталога.

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

Self-hosted вместо своего ключа

Ключ провайдера и биллинг живут на сервере, поэтому «свой ключ в расширении» больше не сценарий: клиент не отправляет запросов мимо бэкенда и не хранит ключ. Свой бэкенд подключается адресом — в настройках, раздел Модель, поле «Сервер Codster» (codster.cloudBaseUrl, по умолчанию https://api.codster.app; локальный codsterd из make run-dev подключается тем же полем). Аккаунт живёт на своём сервере: при смене адреса нужно войти или зарегистрироваться на новом.

Сам апстрим задаётся на сервере:

Переменная Значение
CODSTER_UPSTREAM_BASE_URL https://api.deepseek.com — любой OpenAI-совместимый провайдер
CODSTER_UPSTREAM_API_KEY ключ провайдера; без него прокси отвечает 503 upstream_not_configured

Модели, их окно контекста, цены и глубина размышлений читаются из базы сервера (/v1/models), поэтому новая модель не требует релиза расширения. Доступ к модели — тоже данные сервера: таблица model_grants открывает её организации или человеку, models.is_public оставляет открытой для всех, а models.max_reasoning_effort задаёт глубину, с которой модель работает (клиент берёт её из каталога, а прокси повторно ограничивает запрос этим значением). Имя модели у провайдера хранится отдельно, в models.upstream_model (0018_catalog_names.sql): каталог — это продукт Codster, и под ним вызывается нужный апстрим, а в ответе model переписывается обратно на id каталога, поэтому имя провайдера не доходит до клиента ни в каталоге, ни в стриме, ни в ошибках.

Аккаунт Codster

С аккаунтом запросы идут через прокси бэкенда (/v1/chat/completions), а каталог моделей и цены приходят из базы (/v1/models).

  • Секция Аккаунт Codster в настройках стоит первой: вход по email/паролю, кнопка «Создать аккаунт» (регистрация заводит организацию), список организаций и ролей. Вход и регистрация — две вкладки одной формы, поэтому кнопка «Создать аккаунт» открывает форму сразу на нужной вкладке.
  • Регистрация присылает письмо: сервер отправляет на указанный адрес ссылку /verify/<токен>, и клик по ней подтверждает почту (users.email_verified_at). Аккаунт работает и без подтверждения — письмо нужно, чтобы адрес можно было использовать для приглашений и восстановления, а не как турникет. Строка «Почта … не подтверждена» с кнопкой «Отправить письмо ещё раз» (POST /v1/auth/verify/resend) появляется в разделе «Аккаунт», пока адрес не подтверждён: ссылка живёт 7 дней, а новое письмо отменяет старое. Без настроенного SMTP (см. CODSTER_SMTP_*) письмо не уходит, и строка просто не появляется — регистрация от этого не ломается.
  • Refresh-токен хранится в защищённом хранилище: SecretStorage в VS Code/Cursor, зашифрованный файл cloud-refresh.bin в десктопе. Access-токен не персистится и обновляется заранее; сервер ротирует refresh-токен, новая версия сохраняется до выдачи access-токена.
  • Переключателя «через прокси» нет: без сессии запрос не уходит вовсе, потому что ключ провайдера держит сервер. Клиент не отправляет свои счётчики: расход списывает бэкенд по usage апстрима, и локальная оценка тут была бы двойным списанием.
  • Статистику расхода считает только бэкенд: прокси метрит каждый запрос по usage апстрима, а клиент рисует GET /v1/usage/summary как есть — день за днём и сумма за месяц, с лимитом из free. Локального счёта и полей с ценами в интерфейсе нет: две суммы рядом неизбежно расходились бы со счётом.
  • Цены живут в таблицах провайдера, а не одним числом на модель: providers (0017_providers.sql) — это вендор с адресом и своим расписанием, provider_peak_windows держит часы полного тарифа в минутах UTC (у DeepSeek это будни 01:00–04:00 и 06:00–10:00, остальное и все выходные — вдвое дешевле), model_prices — две строки на модель с тремя числами: попадание в кэш промпта, промах и выход. Прокси выбирает строку по моменту запроса и делит prompt_tokens на кэш и остальное, поэтому ночной ход стоит половину, а повторный префикс — около пятидесятой части. В каталоге (GET /v1/models) отдаются pricing.peak, pricing.offpeak, pricing.peak_windows и off_peak — какая ставка действует прямо сейчас.
  • Расписание считается по UTC, а не по зоне сервера: иначе переезд инстанса менял бы счёт. Окно может пересекать полночь и тогда принадлежит дню, в который открылось; пустой список дней — «каждый день»; вендор без окон продаёт одну ставку, и это полная.
  • Адрес бэкенда — настройка codster.cloudBaseUrl; поле живёт в разделе «Модель».
  • Если бэкенд недоступен, чат не падает: ход не начинается, окно открывает раздел «Аккаунт», а причина приходит тостом.

Бесплатный лимит

Аккаунт получает бесплатный лимит на календарный месяц (UTC), считается по фактическому расходу из таблицы потребления. Значение задаётся на сервере: CODSTER_FREE_MONTHLY_USD (по умолчанию 1.00, 0 выключает лимит). Лимит не трогает каталог, умения и /v1/me — только POST /v1/chat/completions.

Месяц — не единственное окно. Как в платных ассистентах, сумма расхода проверяется по четырём окнам сразу, от узкого к широкому:

Окно Длина Доля месячного лимита
session 5 часов, повторяется в течение суток 3.5 %
day календарные сутки UTC 7 %
week ISO-неделя с понедельника 30 %
month календарный месяц UTC 100 %

Доли — это store.LimitShape: узкие окна выводятся из месячной суммы, а не хранятся отдельно. Поэтому CODSTER_FREE_MONTHLY_USD масштабирует всю форму разом, новый тариф получает торможение без сида, а строки plan_limits только переопределяют конкретное окно. Пропорции подобраны под реальное поведение: два полных сеанса в день упираются в дневное окно, четыре полных дня — в недельное, и только потом наступает месяц. Сессия открывается первым платным запросом, а не чтением карточки тарифа.

  • GET /v1/me и ответы логина/регистрации отдают free: { limit_usd, used_usd, resets_at } и plan.limits: [{ window, limit_usd, used_usd, resets_at, started }] — по записи на окно, от узкого к широкому. Если лимит выключен, free нет вовсе.
  • В настройках под аккаунтом появляется полоска «Бесплатный лимит», а в разделе «План и потребление» — полоски всех четырёх окон: 5 часов, день, неделя, месяц.
  • Когда кончается узкое окно, прокси отвечает 402 plan_limit_reached с текстом «Лимит на 5 часов исчерпан: израсходовано $X из $Y. Новая сессия откроется в HH:MM UTC» — покупка тарифа тут не единственный выход, окно обнулится само. Месяц отвечает общим текстом с датой обнуления.
  • Обхода лимита на клиенте нет: без сессии запросы не отправляются, поэтому лимит — единственный счётчик расхода.

Тарифы и оплата

Каталог тарифов — такие же данные, как модели: таблица plans (server/migrations/0006_billing.sql) с сидом Free / Pro 990 ₽ / Max 4 990 ₽ на 30 дней (сюда же входил Team 2 990 ₽, выведенный из каталога миграцией 0016_org_balance.sql). Бесплатный тариф остаётся операторской настройкой CODSTER_FREE_MONTHLY_USD, у платных месячный лимит берётся из строки тарифа (monthly_usd_limit), а окна — из общей формы (0009_limits_seed.sql только фиксирует то, что продаётся, чтобы это было видно оператору).

  • GET /v1/plans отдаёт каталог, GET /v1/me и ответы входа — plan (slug, name, price_rub, limit_usd, used_usd, resets_at, status, paid, auto_renew, limits), org_balances — балансы команд, в которых состоит аккаунт (с лимитом и расходом самого участника), и credits — личный баланс.
  • POST /v1/billing/checkout {plan_id, auto_renew?} создаёт заказ и платёж у провайдера. Тариф всегда личный: организация платит с баланса, поэтому org_id в теле получает 400 org_has_no_plan. auto_renew: true просит провайдера запомнить карту; ответ {order_id, status, confirmation_url, auto_renew} эхом говорит, сохранилась ли она на самом деле. Клиент открывает ссылку в браузере и опрашивает /v1/me, пока тариф не включится.
  • Оплату подтверждает только вебхук POST /v1/billing/webhook. Тело уведомления не подписано, поэтому сервер перечитывает платёж у провайдера, сверяет сумму с заказом и лишь затем включает тариф. Повторное уведомление ничего не продлевает: заказ занимается ровно один раз.
  • Расход идёт водопадом: сначала окна личного тарифа, затем личный баланс, затем баланс организации участника (не выше его личного месячного предела). Запрос, оплаченный командой, пишется в usage_events.org_id, поэтому личная полоска и баланс команды не считают одно и то же дважды. Когда исчерпано всё — 402 plan_limit_reached с названием тарифа.
  • У команды больше нет тарифа: она делит один пополняемый баланс, а org_allocations (PUT /v1/orgs/{id}/allocations/{userID}) задаёт участнику необязательный месячный предел трат с него: limit_usd: null (или отсутствие поля) снимает предел, а 0 — прямой запрет тратить деньги команды. Предел проверяется вместе с остатком баланса, поэтому один человек не вычерпает деньги команды.
  • Тариф Team выведен из каталога (0016_org_balance.sql): он продавал ровно тот пул, которого больше нет. Остатки оплаченных пулов переведены в баланс команд, поэтому ничего не сгорает.
  • Провайдер задаётся окружением: CODSTER_BILLING_PROVIDER = off | dev | yookassa, ключи CODSTER_YOOKASSA_SHOP_ID / CODSTER_YOOKASSA_SECRET_KEY и CODSTER_PUBLIC_BASE_URL (из него строятся return_url и адрес вебхука; провайдеру нужен публичный HTTPS).

Автопродление

Подписка продлевается сама, без возврата в приложение (server/migrations/0011_autorenew.sql). ЮKassa списывает сохранённую карту вне сессии пользователя: первая оплата помечена save_payment_method, провайдер отвечает payment_method.id, и каждая следующая оплата называет этот id вместо страницы подтверждения.

  • POST /v1/billing/autorenew {enabled, org_id?} включает и выключает продление. Включить можно только с сохранённой картой — иначе 409 no_saved_method с подсказкой отметить галочку при оплате; выключить можно всегда. Включение ничего не списывает: первый платёж назначается на конец текущего периода.
  • Галочка «Запомнить карту и продлевать» стоит в списке тарифов, а в карточке плана есть переключатель, который её отражает. Без сохранённой карты переключатель заблокирован и объясняет, как карту сохранить: показывать рабочий тумблер, который нечем оплатить, — обман.
  • Планировщик живёт в процессе codsterd (RunRenewals, интервал CODSTER_RENEW_INTERVAL, по умолчанию 10 минут) и берёт только строки, у которых auto_renew, есть карта и renew_next_at <= now. Поэтому продление не требует ни одного действия пользователя и не дублируется при нескольких подряд прогонах.
  • Политика повторов: списание в конце периода, затем через день, затем через три; после третьей неудачи подписка переходит в past_due, продление выключается, и карточка честно говорит, почему тариф кончился. Пока идут повторы, период ещё не истёк по-настоящему — тариф продолжает работать.
  • Продление создаёт обычный заказ с renewal = true, поэтому счёт и акт печатаются как продление, а не как дубль первой оплаты. Период стыкуется с концом предыдущего (renewal_grace — 4 дня), так что тик воркера и дни повторов не съедают оплаченные сутки.
  • Провайдер без Charge (например, dev выключен, а платёжный шлюз не умеет внесессионные списания) честно продаёт тарифы, но не заявляет продление: сервер не сохраняет карту и отвечает auto_renew: false.

Баланс (доплата вне квоты)

Пополнение — второй способ заплатить, не меняя тариф: когда все окна исчерпаны, ход оплачивает баланс, и ждать обнуления не нужно (server/migrations/0010_credits.sql).

  • POST /v1/billing/topup {amount_rub, org_id?} создаёт заказ вида credit и платёж. Ответ: {order_id, status, confirmation_url, amount_rub, credit_usd}. Доллары считает сервер по курсу CODSTER_CREDIT_RATE_RUB, клиент называет только рубли — курс с маржой не должен приходить снаружи. Границы одной доплаты — CODSTER_CREDIT_MIN_RUB / CODSTER_CREDIT_MAX_RUB.
  • GET /v1/billing/credits[?org_id=] отдаёт {balance_usd, rate_rub, min_rub, entries: [{amount_usd, reason, created_at}]}. Читать баланс организации может любой участник, пополнять — только владелец или администратор. В карточке плана баланс следует выбору «Лично / организация»: у пула свой кошелёк, и показывать под ним личный было бы чужой счёт.
  • Баланс — append-only журнал credit_ledger: пополнение пишет +, списание хода пишет −, ручная правка — ещё одна строка. Строка пополнения ссылается на заказ, а UNIQUE(order_id) гарантирует, что повторный вебхук не начислит дважды. Рядом таблица credit_balances (server/migrations/0012_credit_balances.sql) держит текущую сумму: она двигается тем же оператором, что пишет строку журнала, поэтому чтение баланса не пересчитывает всю историю.
  • Обработка оплаты атомарна: ApplyOrder помечает заказ оплаченным и выдаёт тариф или баланс в одной транзакции. Падение между этими шагами раньше означало «деньги взяты, тарифа нет, повторный вебхук уже ничего не делает» — теперь заказ остаётся pending, и повторная доставка доводит его до конца.
  • Списание идёт после ответа модели, рядом с записью usage_events, поэтому упавшая запись не ломает уже полученный ответ.
  • CODSTER_CREDIT_RATE_RUB=0 выключает доплату целиком: /v1/billing/topup отвечает 503 billing_not_configured, а в интерфейсе блока «Баланс» нет.

Документы по оплатам

По каждой оплате сервер формирует счёт и акт в PDF — свои, без браузера и внешних конвертеров: GET /v1/billing/orders отдаёт список оплаченных заказов, GET /v1/billing/orders/{id}/invoice.pdf и /act.pdf — сами документы. В разделе «План и потребление» у каждого заказа есть кнопки «Счёт PDF» и «Акт PDF»: оболочка скачивает файл с токеном сессии (браузерная ссылка не умеет нести Bearer) и открывает его системным просмотрщиком.

  • Реквизиты продавца — настройки: CODSTER_SELLER_NAME, CODSTER_SELLER_INN, CODSTER_SELLER_KPP, CODSTER_SELLER_ADDRESS, CODSTER_SELLER_VAT. Пока не заданы имя и ИНН, ручки документов отвечают 503 seller_not_configured — лучше отказ, чем счёт с пустыми реквизитами.
  • Покупатель: физлицу счёт выставляется на имя и email аккаунта; для организации печатаются её реквизиты (orgs.legal_name/inn/kpp/address), которые владелец или админ задаёт в том же разделе.
  • PDF собирается пакетом server/internal/pdfdoc: шрифт (DejaVu/Liberation на Linux, Arial на macOS) встраивается в документ, кириллица и текст на выборку работают. Путь к шрифту переопределяется CODSTER_PDF_FONT.
  • Нумерация документов — billing_orders.number (identity), поэтому счёт № 12 остаётся № 12 и после перезапуска.

Пробный режим без регистрации

Первый запуск не должен упираться в форму регистрации, поэтому POST /v1/auth/guest выдаёт одноразовый аккаунт: без email и пароля, с отдельным небольшим лимитом (CODSTER_GUEST_MONTHLY_USD, по умолчанию 0.20).

  • В ответе user.guest: true; клиент по этому флагу показывает «Пробный доступ» вместо имени и предлагает создать аккаунт.
  • Гостю доступны GET /v1/models и прокси, а организации, отчёт о потреблении и каталог умений отвечают 403 account_required — они имеют смысл только у постоянного аккаунта. Расход гостя считает сам прокси, поэтому отправлять счётчики ему нечем и незачем.
  • Когда пробный лимит исчерпан, прокси отвечает 402 trial_limit_reached с советом создать аккаунт; в чате это кнопка Создать аккаунт, а не «подождите месяц».
  • 0 в CODSTER_GUEST_MONTHLY_USD выключает пробный режим целиком: неограниченный гость — это открытый прокси. В этом случае POST /v1/auth/guest отвечает 403 guest_disabled.
  • device_id выбирает клиент, поэтому одного лимита на аккаунт мало: CODSTER_GUEST_DAILY_PER_IP (по умолчанию 3) ограничивает число новых триалов с одного адреса в сутки, а превышение даёт 429 trial_budget с советом создать аккаунт. Перезапуск приложения на устройстве, у которого триал уже есть, бюджет не тратит — иначе человек запер бы себя сам.
  • Сессия гостя переживает перезапуск, как и обычная: refresh-токен лежит в том же хранилище.
  • Пробный режим включается сам при первом запуске оболочки (Account.autoStartTrial): чат работает сразу, форма регистрации не всплывает. Флаг «уже пробовали» хранится в globalState расширения / sessions.json десктопа, поэтому выход из аккаунта не включает триал заново, а неудачная попытка (бэкенд недоступен) повторяется на следующем запуске.

Устройство и адрес входа

У гостя нет ничего, кроме токена, поэтому бэкенд записывает, откуда начался триал. Клиент один раз на установку генерирует device_id (src/device.ts) и хранит его рядом с сессией; дальше POST /v1/auth/guest отправляет его вместе с client, platform, arch, os_version и app_version, а сервер добавляет IP и User-Agent.

  • Таблица devices (server/migrations/0004_devices.sql) хранит по строке на установку: user_id, device_id, платформу, версию приложения, IP, user_agent, флаг guest, created_at и last_seen_at. Адрес — это RemoteAddr сокета. X-Forwarded-For читается только при CODSTER_TRUST_PROXY=true (и тогда берётся последняя запись, добавленная нашим прокси): без этого флага все пользователи за прокси попали бы в одно ведро лимитера, а с ним без прокси любой мог бы подделать адрес.
  • device_id уникален и при повторном запросе возвращает тот же гостевой аккаунт (ON CONFLICT обновляет только «откуда» и время последнего визита). Перезапуск приложения не плодит пустых гостей и не выдаёт новый лимит; переустановка с потерей device_id выглядит как новое устройство — это и есть единственная защита от таких обходов.
  • Ошибка записи устройства не отменяет сессию: пользователь получает токены, а проблема попадает в лог сервера.

Тесты серверной части — server/internal/httpapi/quota_test.go (границы месяца, лимит на аккаунт, отключённый лимит) и server/internal/httpapi/guest_test.go (создание, отдельный лимит, 403 на аккаунтные ручки, выключенный режим, ротация токена, запись устройства и повторный запуск с тем же device_id); в server/internal/store/pg/pg_test.go — upsert устройства.

API аккаунта: POST /v1/auth/register, POST /v1/auth/login, POST /v1/auth/guest, POST /v1/auth/refresh, POST /v1/auth/logout, GET /v1/me, GET /v1/models. Клиент — src/api/cloud.ts, связка с оболочками — src/account.ts.

llm-tolk gateway

В ../Dev/llm-tolk настроен доступ к DeepSeek Flash через OpenAI-совместимый шлюз (https://llm2.tolk.tech/routes/ds-flash-think). Расширение этот адрес больше не выбирает: шлюз включается на сервере —

CODSTER_UPSTREAM_BASE_URL=https://llm2.tolk.tech/routes/ds-flash-think
CODSTER_UPSTREAM_API_KEY=<тот же ключ, что LLM_API_KEY в llm-tolk/.env>

Сам llm-tolk — голосовой ReAct-агент с телефонными инструментами (end_call, wait_user, SIP/RTP), поэтому его тулы не подходят для редактирования IDE-проекта. Codster использует тот же DeepSeek-шлюз и похожую схему tool-loop, но файловые инструменты выполняются локально в extension host, где у них есть доступ к workspace и VS Code API.


Инструменты

Имена и семантика повторяют подход Cursor. В Ask/Plan режимах доступны только read-only инструменты.

Инструмент Назначение Agent Ask Plan
read_file Чтение файла с номерами строк и диапазоном ✓ ✓ ✓
list_dir Структура директорий ✓ ✓ ✓
grep_search Поиск по регулярному выражению через ripgrep ✓ ✓ ✓
code_navigation LSP: определение, использования, символы файла ✓ ✓ ✓
file_search Поиск файлов по имени/пути ✓ ✓ ✓
codebase_search Локальный лексический поиск по смыслу ✓ ✓ ✓
read_lints Диагностика VS Code ✓ ✓ ✓
web_search Веб-поиск через DeepSeek Search API ✓ ✓ ✓
task Субагент-исследователь с отдельным контекстом ✓ ✓ ✓
review_changes Ревью своего diff свежим контекстом ✓ ✓ —
await_command Чтение вывода фоновой команды ✓ ✓ —
edit_file Точечная замена old_string → new_string ✓ — —
write_file Создание/перезапись файла ✓ — —
delete_file Удаление файла (с подтверждением) ✓ — —
run_terminal_cmd Команда с таймаутом или в фоне (background: true) ✓ — —
todo_write План из 3–6 шагов, виден в чате ✓ — —

codebase_search не использует embeddings и не отправляет код в отдельный индекс: это быстрый локальный ранжированный поиск по файлам. Для точных символов лучше grep_search.

grep_search работает через ripgrep: берётся бинарник, вшитый в редактор (@vscode/ripgrep), либо rg из PATH, либо путь из CODSTER_RIPGREP. Это даёт уважение .gitignore, пропуск бинарников и скрытых папок и поиск за миллисекунды. Если бинарника нет или шаблон использует синтаксис, которого нет в Rust-regex (lookaround, backreferences), инструмент откатывается на встроенный скан с --pcre2 и JavaScript-regex, так что поведение остаётся прежним.

code_navigation — второй слой поверх поиска: спрашивает language server редактора через vscode.executeDefinitionProvider, vscode.executeReferenceProvider и vscode.executeDocumentSymbolProvider. В отличие от текстового поиска понимает области видимости и типы, не путает комментарии и одноимённые слова, различает одноимённые методы разных классов. Требует установленного расширения с поддержкой LSP для нужного языка; в desktop-сборке недоступен.

В desktop-сборке grep_search берёт rg из PATH или путь из CODSTER_RIPGREP, а без них работает прежним встроенным сканом.

Параллельные вызовы. Независимые read-only вызовы одного шага выполняются одновременно (PARALLEL_TOOL_LIMIT в src/agent/loop.ts), но результаты ложатся в историю в том порядке, в котором их запросила модель. Всё, что пишет файлы, запускает команды или спрашивает подтверждение, идёт строго по одному: его порядок и диалоги видны пользователю.

Субагенты. task запускает вложенный ход в отдельной истории (src/agent/subagent.ts): субагент читает репозиторий через read-only набор инструментов и возвращает короткий отчёт с path:line. Основной чат получает один результат вместо десятков прочитанных файлов. Вложенность ограничена одним уровнем — у субагента нет ни task, ни review_changes, — а его токены учитываются в общей статистике сессии.

Ревью изменений. review_changes берёт git diff против HEAD плюс список неотслеживаемых файлов (src/agent/git.ts) и отдаёт их субагенту-ревьюеру, у которого свой контекст и своя задача: найти настоящие проблемы, а не пересказать diff.

Фоновые команды. run_terminal_cmd с background: true запускает процесс в отдельной группе и сразу возвращает id (src/agent/background.ts). Дальше await_command читает накопленный лог, ждёт появления нужной строки (pattern) или завершения. Процессы живут до 30 минут и переживают конец хода, поэтому dev-сервер можно поднять в одном сообщении и проверить в следующем.

web_search использует встроенный поисковик DeepSeek (POST /v1/tools/search): возвращает заголовки, URL, выжимки и, по флагу include_content, текст страниц. Параметр count ограничен 15 результатами.


Правила проекта

Поддерживаются те же форматы, что и в Cursor:

.codster/rules.md
.cursor/rules/*.mdc
.cursorrules
AGENTS.md
.github/copilot-instructions.md

Для .mdc читается frontmatter:

---
description: Правила для TypeScript
globs: src/**/*.ts
alwaysApply: false
---

Используй строгий TypeScript и не добавляй any.

Правила с alwaysApply: true или подходящими globs попадают в системный промпт. Дополнительно можно задать codster.customInstructions в настройках.


Настройки

Одна страница вместо отдельных диалогов: слева список разделов, справа содержимое — «Аккаунт», «План и потребление», «Организация», «Модель», «Агент», «Статистика», «Умения», «Рутина». В VS Code страница открывается вкладкой редактора (как в Cursor), а не модалкой поверх чата: чат остаётся на месте, страницу можно держать рядом, а повторный Codster: Настройки показывает уже открытую вкладку и переключает раздел. В десктопе страница занимает окно справа от рейла. «Отмена»/крестик в VS Code закрывают вкладку, в десктопе — прячут страницу. Кнопка расхода в строке состояния открывает сразу «Статистику», Codster: Умения и Codster: Рутина — свои разделы. В узкой панели список разделов становится строкой вкладок. Кнопки «Сохранить»/«Отмена» есть только у разделов с полями формы (модель, агент); в разделе «Модель» модели включаются галочками из каталога — свободного поля для ID модели нет, как и выбора глубины размышлений: её задаёт модель. Аккаунт, умения и рутина применяются сразу, а «Сохранить» в открытой вкладке её не закрывает.

«План и потребление» — карточка текущего личного тарифа и остаток лимита (/v1/me), список тарифов из /v1/plans с кнопкой Оформить, личный баланс и документы. Тарифы личные: команда не покупает тариф, у неё свой баланс в разделе «Организация», поэтому здесь только выбор тарифа, галочка «Запомнить карту и продлевать» и переключатель продления. Под тарифами — список оплат с кнопками «Счёт PDF» и «Акт PDF». Кнопка оплаты открывает страницу провайдера в браузере, карточка обновляется сама, когда вебхук подтвердит платёж. docs/plan.png.

«Организация» — отдельный раздел: Создать организацию (имя спрашивается на месте), участники с ролями, приглашение по email и общий баланс. Раздел показывает по одному состоянию: гостю — ссылку в «Аккаунт», аккаунту без организации — создание, участнику — список команды и остаток общего баланса, а пополнение, лимиты участников и реквизиты для счетов — только владельцу/админу. Команда больше не требует тарифа: баланс пополняется в рублях и расходуется по ценам каталога моделей, когда личный лимит участника израсходован. Приглашение доставляет ссылка: сервер отправляет письмо сам, если настроен SMTP (CODSTER_SMTP_*), иначе ссылка копируется и менеджер передаёт её вручную. Что именно произошло, видно в ответе (emailed) и строке тоста: «письмо отправлено» или «ссылка скопирована». Ссылка ведёт на страницу /join/<код> с кнопкой vscode://codster.codster/join?code=…, которая присоединяет к команде без ввода кода. Приглашение привязано к адресу и живёт 14 дней. Исключение участника работает всегда; аккаунт и его чаты остаются, уходит только членство, а владельца исключить нельзя — иначе балансу некому распоряжаться. Удаление организации возвращает остаток баланса на личный баланс владельца. docs/org.png.

Настройка По умолчанию Описание
codster.model oda ID модели из каталога сервера
codster.enabledModels [] Включённые модели каталога; пустой список — все доступные
codster.reasoningEffort high Глубина размышлений: задаётся моделью, из каталога, устаревшая настройка
codster.confirmEdits false Подтверждать правки
codster.confirmCommands false Подтверждать команды
codster.autoContext true Прикладывать активный файл/выделение
codster.customInstructions "" Дополнительные инструкции
codster.cloudBaseUrl https://api.codster.app Адрес бэкенда Codster (аккаунт, модели, прокси)

Бюджет ответа берётся у модели из каталога (max_output_tokens в БД, 32000 по умолчанию): размышления списываются с того же лимита, поэтому одно число на всех либо обрывало ход на середине мысли, либо просило больше, чем модель продаёт. Прокси всё равно обрезает запрос до каталога, так что старый клиент ничего не сломает. Температура (0.2) и бюджет шагов агента (100) не настраиваются: это не пользовательские ручки, а параметры клиента, которые легко сломать. Бюджет шагов при этом мягкий — за два шага до потолка модель получает просьбу закругляться, а после исчерпания один запрос всё равно уходит без инструментов, чтобы turn закончился итогом, а не пустым сообщением про лимит. Если модель упирается в лимит размышлениями, ход один раз повторяется без размышлений, а пустой ответ теперь подсказывает написать «Продолжи».

Ключа провайдера в настройках нет: он живёт на сервере, и в клиенте нечего хранить. Refresh-токен аккаунта — единственный секрет сессии: SecretStorage в VS Code/Cursor, зашифрованный файл cloud-refresh.bin в десктопе.

Секреты для умений

Умение не хранит токены в своём файле. Значения лежат в хранилище секретов (VS Code SecretStorage, на десктопе — файл под safeStorage: Keychain / DPAPI / libsecret), в настройки и в подсказки уходят только имена. Раздел «Секреты» — в разделе «Умения» страницы настроек (sidebarSkills), там же видно, каких секретов не хватает.

Умение объявляет нужные ключи во frontmatter полем secrets:

---
name: publish-npm
description: Публикует пакет в npm.
icon: package
secrets: npm-token
---

# Publish npm
1. Добавьте в «Умения» → «Секреты» ключ `npm-token`.
2. Запустите `npm publish` через `run_terminal_cmd` с `secrets: ["npm-token"]` — значение придёт переменной окружения `NPM_TOKEN`, в чат и в файл оно не попадает.

Так агент знает заранее: в индексе умений у строки видно secrets: npm-token, недостающие помечены (not stored); при read_skill список повторяется в ответе инструмента. Если значение нужно текстом (локальный конфиг, генератор), есть get_secret; прочитанное значение маскируется при сохранении сессии и в экспорте истории.

Иконка умения

Иконку выбирает пользователь, а не модель: в разделе «Умения» иконка рядом с именем — кнопка, она открывает набор из 20 рисунков (пазл, инструменты, эксперименты, пакет, поиск, релиз, терминал, база, git, безопасность и т. д.). Клик пишет одно поле во frontmatter:

icon: shield
  • Набор — из Lucide (ISC, https://lucide.dev), 20 иконок: их геометрия лежит инлайном в media/chat.js, без зависимости и без шрифта, поэтому иконки одинаковы на любой системе и не зависят от эмодзи-шрифта. Условия лицензии — media/lucide-license.txt, он уезжает и в VSIX, и в приложение.
  • В SKILL.md хранится только ключ набора. Сетка 24×24, обводка 2 → на экране 15–18 px иконка читается как часть остального интерфейса.
  • Переписывается ровно строка icon:: остальной frontmatter и тело не трогаются, файл остаётся в git-диффе одной строкой.
  • Модель иконку не выбирает: skill-creator и ответ чата её не возвращают, при создании умение получает стандартный пазл. Неизвестное значение во frontmatter тоже сводится к пазлу, без «случайной» иконки по хешу имени.
  • Устаревшие эмодзи из старых файлов (🚀, 🧪) по-прежнему понимаются при чтении и сопоставляются с ключом набора.
  • У встроенного умения нет файла на диске, поэтому его иконка не меняется — кнопка неактивна и объясняет почему.

Скрипты и история умений

Умение — это папка .codster/skills/<имя>/, а не один файл: рядом с SKILL.md можно держать scripts/ и references/.

.codster/skills/release-check/
├── SKILL.md              # инструкции, frontmatter
├── scripts/check.sh      # запускаемый код
└── references/notes.md   # длинные описания, примеры
  • read_skill перечисляет файлы умения, чтобы модель не гадала об их наличии.
  • read_skill_file читает один файл: путь относительный, .. и абсолютные пути отклоняются.
  • run_terminal_cmd с параметром skill: "release-check" делает рабочей папкой папку умения и экспортирует SKILL_DIR и CODSTER_SKILL — скрипт запускается как bash "$SKILL_DIR/scripts/check.sh". Подтверждение команды и правила безопасности те же, что и для обычных команд.
  • skill_history возвращает последние коммиты, затрагивающие SKILL.md. Отдельного версионирования нет: умения лежат в репозитории проекта, историю ведёт git (--follow, чтобы пережить переименование). В разделе «Умения» это кнопка «История».

Каталог умений

В разделе «Умения» две вкладки: «Проект» — то, что лежит в .codster/skills, и «Каталог» — общие умения из бэкенда. Личные умения никуда не уезжают сами: в облако попадает только то, что опубликовано кнопкой «Опубликовать в каталог», и только в одну сторону.

  • Кнопка «Опубликовать» отправляет SKILL.md (описание, secrets, тело) плюс файлы из папки: scripts/, references/ — до 20 файлов, 64 КБ каждый, 200 КБ всего.
  • slug — имя, под которым умение видно всем. Оно уникально в каталоге: тот же slug от другого автора даёт 409 slug_taken, а повторная публикация своим автором обновляет запись.
  • «Установить» пишет папку целиком в .codster/skills/<slug>/ и засчитывает установку на аккаунт. Если такое умение уже есть и отличается, кнопка превращается в «Перезаписать» — локальные правки не теряются молча.
  • Пути файлов проверяются на сервере и на клиенте: .., абсолютные пути и SKILL.md во вложениях отклоняются, иначе публикация могла бы писать вне папки умения.
  • Верификация (verified) — отметка модерации, её ставит сервер; публикация её не меняет.
  • Секреты из каталога приезжают только именами: значения остаются в хранилище системы на устройстве.

HTTP: GET /v1/skills?q=&mine=1, GET /v1/skills/{slug}, POST /v1/skills, DELETE /v1/skills/{slug}, POST /v1/skills/{slug}/install.

Рутина

Задачи, которые агент повторяет сам: «прогони тесты и почини падения», «проверь обновления зависимостей», «прибери мёртвый код». Раздел «Рутина» в настройках; на десктопе его открывают ещё кнопкой с часами в композере, пунктом в боковом меню и Cmd/Ctrl+Shift+A, в VS Code — командой Codster: Рутина и значком в заголовке панели чата. docs/automations.png.

  • У задачи есть папка, промпт, название (по умолчанию — первая строка промпта), режим и расписание. Расписаний три:
    • по интервалу — от 5 минут до двух недель. Период до суток — чистый интервал, и часовой пояс ему не нужен;
    • по дням недели — выбрали «по дням недели» и отметили Пн…Вс, задача идёт каждую неделю в эти дни и в указанное время: «по понедельникам и четвергам в 08:30»;
    • раз в месяц — «1-е число» … «31-е» или «последний день»: то, что нужно для отчёта в конце месяца. День, которого в месяце нет (31-е в апреле), приходится на последний день месяца — не пропускается.
  • От суток и выше — ещё и время. Выбрали «каждый день», «каждые 2 дня» или «каждую неделю» — рядом появляется поле «Во сколько» (<input type="time">, по умолчанию 09:00). Тогда запуск привязан к часам, а не к моменту создания: задача, заведённая в 15:30 с временем 09:00, впервые сработает завтра утром, а не через сутки. Слоты считаются по локальному календарю, поэтому переход на летнее время не сдвигает запуск на час. Недельная задача помнит свой день недели и называет его в списке: «каждую неделю по средам в 09:00».
  • Пропущенный слот не сдвигает график. Если приложение было закрыто три дня, дневная задача выполнится один раз при открытии и вернётся на своё время. У задачи без выбранного часа поведение прежнее — чистый интервал от последнего запуска.
  • Задача по суткам и больше без времени — тоже допустима (старые задачи и вызовы без формы): тогда работает интервал, как раньше.
  • Папка закрепляется за задачей. Список в форме — это открытые папки; новая задача по умолчанию берёт текущую. Перед запуском оболочка делает нужную папку активной, а в VS Code ещё и подменяет хост на представление одной папки (src/host/scoped.ts), чтобы относительные пути и поиск не уходили в соседние корни multi-root окна. Если папку удалили с диска, запуск падает с понятной причиной в журнале, а не выполняется в чужом проекте.
  • Все запуски идут в один чат на задачу. Первый запуск создаёт чат, следующие дописывают в него. Иначе задача «каждый час» забивала бы список чатов своими копиями и вытесняла разговоры пользователя.
  • Чат рутины помечен в рейле. Слева от названия — часы того же рисунка, что и у пункта «Рутина». Пока последний запуск новее момента, когда чат открывали, значок жёлтый; открыл чат (клик в рейле, вкладка или отправка сообщения) — снова серый. Отметка «просмотрено» лежит в localStorage вебвью, поэтому жёлтый значок переживает перезапуск, а не мигает при каждом старте.
  • Запуски идут по одному, в порядке ближайшего срока: два агента одновременно дрались бы за один терминал и одну рабочую папку.
  • Настройки подтверждений действуют и здесь: если соответствующий флаг включён, правка файла или команда в автоматическом чате ждёт ответа, а без ответа запрос отклоняется по таймауту, как в обычном чате.
  • Пока редактор закрыт, задач нет. Первый тик после запуска наверстывает пропущенное: задача, у которой срок прошёл ночью, выполнится при открытии, а не через час.
  • Ручной запуск («Запустить сейчас») не меняет расписание: срок всё равно считается от последнего запуска, иначе кнопка сдвигала бы график.
  • О результате сообщает уведомление с кнопкой Открыть чат: в VS Code — информационное сообщение, на десктопе — системное уведомление. Короткий итог и статус (ok / ошибка / остановлено) видны в списке и в журнале последних запусков.
  • Задачи живут в globalState расширения и в sessions.json десктопа, то есть принадлежат пользователю, а не папке проекта.

Код: модель и хранение — src/agent/automations.ts, часы — src/agent/automationScheduler.ts, сообщения обеих оболочек — src/chat/automationBridge.ts. Запуск в чат реализуют сами оболочки (ChatViewProvider.runAutomation, ChatEngine.runAutomation), потому что только они умеют начинать ход агента.


Команды

Команда Действие
Codster: Открыть чат Фокус на чате
Codster: Новый чат Очистить историю
Codster: Настройки Открыть настройки вкладкой (аккаунт, план, модель, агент, умения, рутина)
Codster: Рутина Задачи агента по расписанию
Codster: Отменить последние изменения агента Откатить последний чекпоинт
Codster: Объяснить выделение Prefill запроса по выделению
Codster: Исправить выделение Prefill запроса на исправление

Архитектура

Общее ядро (без зависимостей от VS Code)
├── src/agent/loop.ts         — ReAct loop
├── src/agent/toolDefs.ts     — JSON Schema инструментов
├── src/agent/tools.ts        — файловые операции, поиск, терминал (через WorkspaceHost)
├── src/agent/prompts.ts      — системный промпт и сборка контекста
├── src/agent/context.ts      — активный файл, выделение, @-mentions, диагностика
├── src/agent/rules.ts        — Cursor-совместимые правила
├── src/agent/skills.ts       — умения: разбор SKILL.md, файлы умения, индекс
├── src/agent/skillMarket.ts  — публикация умения в каталог и установка из него
├── src/agent/skillHistory.ts — история коммитов умения через git
├── src/agent/automations.ts  — задачи по расписанию: модель, хранение, журнал запусков
├── src/agent/automationScheduler.ts — часы: что уже пора запускать и по очереди
├── src/agent/checkpoints.ts  — снимки файлов и откат
├── src/api/client.ts         — DeepSeek/OpenAI SSE, tool calls, web search, retry
├── src/chat/turn.ts          — один ход агента: правила, инструменты, события, чекпоинт
├── src/chat/html.ts          — разметка чата, общая для webview и десктопа
├── src/chat/automationBridge.ts — сообщения рутины для обеих оболочек
├── src/host/host.ts          — интерфейс WorkspaceHost
├── src/host/vscode.ts        — реализация на API VS Code
└── src/host/node.ts          — реализация на node:fs (десктоп)

Поверхности
├── src/chat/ChatViewProvider.ts  — VS Code: webview, команды, workspaceState
├── media/chat.js + chat.css      — UI, markdown, diff, approvals, @-mentions
├── media/desktop.css             — десктоп: рейл папок и двухколоночный layout
└── desktop/main.ts + engine.ts   — Electron: окно, IPC, папки, JSON-хранилище
    desktop/fileApps.ts           — кто в системе открывает этот тип (LaunchServices)
    desktop/appIcons.ts           — иконки приложений для меню «открыть»

Бекенд (отдельный Go-модуль, в VSIX не попадает)
├── server/                       — аккаунты, организации, каталог моделей и умений, потребление, прокси LLM

Агентный цикл: system + history → stream → tool_calls → approvals → tools → tool results → stream → … до финального ответа или лимита шагов. Каждый запрос оборачивается чекпоинтом; изменения применяются через WorkspaceEdit (VS Code) или fs.writeFile (десктоп) и сохраняются на диск.

Ядро общается с окружением только через WorkspaceHost: файлы, glob-поиск, диагностика, активный редактор, лог. Поэтому одна и та же логика агента работает и в расширении, и в отдельном окне; новая поверхность добавляет только реализацию хоста и транспорт.


Бекенд

server/ — отдельный Go-модуль: аккаунты, организации и роли, refresh-токены, каталог моделей с ценами, приём агрегированного потребления и прокси POST /v1/chat/completions к провайдеру. Ключ провайдера живёт только на сервере: клиент авторизуется своим access-токеном и не видит наружу ничего, кроме каталога моделей. Чаты, промпты и секреты остаются на устройстве. Проксирование ограничено лимитом тарифа аккаунта: у бесплатного это CODSTER_FREE_MONTHLY_USD, у платного — plans.monthly_usd_limit, а когда личный лимит исчерпан, платят пулы организаций, где аккаунт состоит. Когда исчерпано всё — 402 plan_limit_reached с датой обнуления. Одноразовым гостевым аккаунтам (POST /v1/auth/guest) прокси отвечает отдельным, меньшим лимитом (CODSTER_GUEST_MONTHLY_USD) — это пробный режим без регистрации.

make run-dev   # локальная разработка: PostgreSQL в Docker, codsterd на хосте
make up        # весь бекенд в Docker, ждёт /healthz
make smoke     # сквозная проверка API из контейнера
make logs      # логи обоих сервисов
make down      # остановить, база сохраняется
make reset     # остановить и удалить том с базой

Локальная разработка на macOS

make run-dev — ежедневный сценарий: в Docker поднимается только PostgreSQL, всё остальное работает нативно, без контейнеров и без пересборки образов на каждую правку.

make run-dev      # проверит toolchain, поднимет базу, соберёт и запустит codsterd
make dev-status   # контейнер базы, pid и /healthz
make dev-logs     # хвост лога сервера
make dev-stop     # погасить сервер, база остаётся
make dev-reset    # снести том с базой и поднять заново

Что делает run-dev:

  1. preflight — проверяет docker (и что демон запущен), docker compose, curl, go;
  2. env — создаёт .env из .env.example, если его нет;
  3. dev-db — docker compose up -d postgres и ожидание pg_isready;
  4. dev-serve — собирает server/bin/codsterd, запускает его в фоне на 127.0.0.1:8080 с CODSTER_STORE=postgres и миграциями при старте;
  5. dev-health — ждёт /healthz, иначе печатает лог и падает.

Сервер слушает только localhost, лог и pid — в .dev/ (в git не попадают). Повторный run-dev перезапускает сервер; данные в PostgreSQL сохраняются. Порты и секреты берутся из .env (POSTGRES_PORT=5433, CODSTER_PORT=8080, CODSTER_JWT_SECRET). Redis проекту не нужен: кэша и очередей на сервере нет, единственный контейнер — база.

docker-compose.yml в корне описывает и полный стек: postgres (порт 5433 на хосте, чтобы не спорить с локальным PostgreSQL), codsterd (порт 8080) и два сервиса в профиле tools — smoke (сквозной тест API) и gotest (весь Go-набор вместе с интеграцией в PostgreSQL). Секреты и порты берутся из .env; make env создаёт его из .env.example.

Сервер отдельно, без Docker:

make server-run        # codsterd на in-memory хранилище
make server-test       # тесты без базы
make db                # только PostgreSQL из compose
make server-test-pg    # плюс интеграция с PostgreSQL
make docker-test       # то же, но целиком внутри Docker

Подробности — в server/README.md: эндпоинты, переменные окружения, схема таблиц, политика токенов и что именно уходит на сервер.

Сейчас это самостоятельный сервис: расширение и десктоп пока работают со своим ключом DeepSeek, подключение клиента к бекенду — следующий шаг.


Безопасность и ограничения

  • Агент выполняет команды от вашего пользователя. Подтверждения по умолчанию выключены — за скорость платим вниманием: включите codster.confirmCommands и codster.confirmEdits, если не хотите отпускать агента без присмотра. Катастрофические команды блокируются при любой настройке, а удаление файла спрашивает подтверждение всегда.
  • Заблокированы катастрофические команды (sudo, mkfs, rm -rf /, dd of=/dev/..., curl | sh, fork bomb, shutdown и т.п.).
  • Скрипты умений запускаются тем же run_terminal_cmd и подчиняются тем же подтверждениям и запретам; run_skill-обхода нет. read_skill_file не выходит за папку умения и не читает бинарники.
  • run_terminal_cmd не поддерживает интерактивные программы и долгоживущие dev-серверы; для них есть обычный терминал VS Code.
  • Поиск и codebase_search не индексируют .git, node_modules, dist, build, target, .venv и похожие каталоги. .env исключён, если не указать его явно как path.
  • Рабочая область — первый workspace folder. Multi-root поддержан частично.
  • Только desktop VS Code/Cursor, не web-версия: нужен Node.js API для терминала и файлов.
  • Чекпоинты сохраняются в workspace state (примерно до 4 МБ на чат); если снимков слишком много, самые старые после перезапуска окна пропадут.
  • Чаты работают параллельно и независимо, но правят один и тот же workspace: если два чата меняют один файл, побеждает последняя запись, а откат чекпоинта может вернуть файл к более старому состоянию. Пока в другом чате выполняется запрос, откат блокируется.
  • Активный файл, выделение, правила проекта и @-файлы отправляются в DeepSeek как контекст запроса. Не прикладывайте секреты.
  • История автоматически сжимается перед отправкой, чтобы не выйти за контекстное окно; старые результаты инструментов могут быть сокращены.
  • web_search отправляет только текст запроса в DeepSeek Search API (POST /v1/tools/search), но не напрямую: запрос идёт на бэкенд Codster, который подставляет ключ провайдера. Апстрим должен поддерживать /v1/tools/search — на сторонних шлюзах без него инструмент вернёт ошибку. Найденные страницы приходят уже в виде сниппетов и выжимок.
  • Нет embeddings-индекса: codebase_search работает локально, код не отправляется в отдельный индекс.
  • Нет tab-completion и отдельного inline-режима «Cmd+K»: основной сценарий — чат-агент, который правит файлы через те же инструменты.
  • Проект не аффилирован с Cursor или DeepSeek. Названия инструментов использованы для совместимости workflow.

Разработка

Всё через make: без аргументов он печатает список задач.

make help          # список задач
make install       # npm ci
make check         # typecheck + 268 тестов + бандл
make test          # только тесты
make package       # codster-<version>.vsix
make desktop-build # esbuild → dist-desktop/
make icons         # перегенерировать PNG-иконки из media/icon.svg
make clean         # снести dist, release, coverage

Вручную то же самое:

npm install
npm run watch      # esbuild watch
npm run typecheck
npm run test
npm run build
npm run package

Запуск extension host: откройте папку в VS Code и нажмите F5 (конфигурация в .vscode/launch.json).

Живые тесты против поднятого бекенда (по умолчанию пропускаются):

make run-dev
CODSTER_LIVE_BASE_URL=http://127.0.0.1:8080 npx vitest run src/test/liveMarket.test.ts   # каталог умений
CODSTER_LIVE_BASE_URL=http://127.0.0.1:8080 npx vitest run src/test/liveQuota.test.ts    # бесплатный лимит и 402

UI можно посмотреть без VS Code:

open scripts/preview.html      # окно десктопа
open scripts/preview-web.html  # панель VS Code

Обе страницы используют те же media/chat.js и media/chat.css с мок-хостом; панельная не подключает media/desktop.css.

Логотип и иконки

Исходники — codster-icon-white.svg (знак) и codster-logo-white.svg (знак с надписью) в корне репозитория. Из них собрано всё остальное:

Файл Где используется
src/chat/logo.ts знак и надпись инлайном в разметку чата (<symbol> + <use>), общие для webview и десктопа
media/activity.svg иконка расширения в Activity Bar VS Code и в заголовке панели (монохром, currentColor, знак 18px по центру квадрата 24×24)
build/icon.svg исходник иконки приложения: белая плитка и чёрный знак
media/icon.svg исходник иконки расширения: тёмная плитка и белый знак
media/icon.png иконка расширения для marketplace, 512×512, плитка во весь холст
media/icon-128.png та же иконка в 128×128 — логотип паблишера в портале Marketplace, в VSIX не попадает
build/icon.png иконка приложения для electron-builder, 1024×1024, плитка 84% холста по сетке macOS
  • В интерфейсе логотип наследует currentColor, поэтому автоматически подстраивается под тему VS Code и десктопную панель.
  • Логотип собран из двух независимых символов: знак и надпись. На рейле десктопа знак 30px, надпись 18px (их размеры задаются отдельно в media/desktop.css), в шапке настроек — только знак. Спрайт с надписью добавляется лишь на десктопной поверхности, в webview его нет.
  • viewBox каждого символа — измеренный прямоугольник краски, а не догадка по числам в path: иначе знак съезжает внутри плитки. Тем же приёмом считается глиф для Activity Bar.
  • make icons (или npm run icons) перерисовывает PNG из build/icon.svg и media/icon.svg и заново собирает media/activity.svg: размер и положение знака скрипт берёт из отрисованного глифа, а не из чисел в path, иначе знак уезжает из центра. Другого SVG-рендерера в зависимостях нет, поэтому работает Electron. PNG лежат в git, так что сборка без перегенерации тоже работает.
  • В режиме разработки (npm run desktop:start) Electron показывает иконку Electron; чтобы Dock совпадал с релизом, desktop/main.ts подставляет build/icon.png через app.dock.setIcon.

Скриншоты

Изображения для README не рисуются руками: make screenshots собирает из настоящей разметки (renderChatHtml) две страницы — scripts/preview.html (окно десктопа) и scripts/preview-web.html (панель VS Code) — и снимает их через Electron. Мок-хост, который отвечает вместо расширения, лежит отдельно в scripts/preview-mock.js, поэтому страница превью не отстаёт от продукта.

Кадры панели — docs/web-chat.png, docs/web-models.png, docs/web-skills.png: они и стоят в шапке README, потому что Marketplace показывает расширение, а не десктоп-окно с рейлом. Остальные docs/*.png — десктоп-приложение. Скрипт кладёт копии в site/public/screens/, а на https://codster.app/screens/… они попадают только после make site-publish — без деплоя сайта картинки в README и на странице Marketplace будут битыми.

Десктоп-приложение

Ядро агента не зависит от VS Code, поэтому то же приложение собирается как отдельное окно на Electron. UI — тот же media/chat.js и media/chat.css, то есть ровно тот же вид, что в панели VS Code: те же шрифты, палитра, пузыри сообщений и бренд-акцент. Отдельный media/desktop.css лишь добавляет то, чего в панели нет: полосу рабочей папки с меню проектов и более широкую колонку чата под размер окна. Вместо acquireVsCodeApi() preload отдаёт window.codster с тем же контрактом.

npm run desktop:build    # esbuild → dist-desktop/main.js + preload.js
npm run desktop:start    # собрать и запустить окно
node scripts/run-desktop.mjs ~/project            # открыть конкретную папку
node scripts/run-desktop.mjs ~/project --add-dir ~/shared-lib

Сборка установщиков

npm run desktop:dist:mac   # release/Codster-<version>-mac-universal.dmg + .zip
npm run desktop:dist:win   # release/Codster-<version>-win-x64-setup.exe
npm run desktop:dist       # обе платформы за один прогон (нужен macOS)

macOS-сборка делается только на macOS, Windows-инсталлятор собирается и с macOS (Wine не нужен). По умолчанию подписей нет: на macOS ставится ad-hoc подпись (mac.identity: "-"), на Windows сертификат не используется, поэтому обе системы один раз спросят подтверждение при первом запуске.

  • macOS: приложение блокируется Gatekeeper — «Системные настройки → Конфиденциальность и безопасность → Всё равно открыть», либо xattr -dr com.apple.quarantine /Applications/Codster.app. На Apple Silicon без ad-hoc подписи приложение падает как «повреждённое», поэтому identity: "-" в electron-builder.yml — значение по умолчанию.
  • Подпись Developer ID: npx electron-builder --mac -c.mac.identity="Developer ID Application: … (TEAMID)" — вместо ad-hoc подставляется настоящая личность (сертификат должен лежать в keychain). Нотаризация управляется только переменными окружения: если есть APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD и APPLE_TEAM_ID (или APPLE_API_KEY / APPLE_API_KEY_ID / APPLE_API_ISSUER), @electron/notarize отправляет .app в notarytool и приклеивает тикет. Для ad-hoc сборки эти переменные выставлять нельзя: notarytool отклонит неподписанное приложение и сборка упадёт. Без них в логе будет skipped macOS notarization.
  • Windows: SmartScreen → «Подробнее» → «Выполнить в любом случае». Инсталлятор ставится без прав администратора (perMachine: false).
  • Упакованное приложение открывает папку из --workspace <path>, позиционного аргумента, CODSTER_WORKSPACE или системного диалога «Добавить папку…»; дополнительные папки — --add-dir / CODSTER_ADD_DIRS.

Ридер файлов

В десктопе есть свой просмотрщик кода — отдельная колонка справа. Открывает и закрывает его иконка файла в правом верхнем углу окна (рядом с названием текущего чата); активное состояние подсвечено бренд-цветом.

  • дерево открытой папки с ленивой подгрузкой вложенных; node_modules, .git, dist и прочие тяжёлые каталоги скрыты;
  • поиск по путям прямо в панели (Enter открывает первый результат);
  • превью с номерами строк, языком и размером; строки больше 512 КБ читаются частично и помечаются «обрезано»;
  • изображения (PNG, JPEG, GIF, WebP, SVG) показываются как картинки;
  • HTML открывается в песочнице без скриптов (sandbox, srcdoc), так что страница из проекта не достаёт до интерфейса чата; PDF — встроенным просмотрщиком Chromium;
  • Markdown (md, mdx, markdown) рендерится тем же парсером, что и ответы агента: заголовки, списки, таблицы, цитаты, код с кнопкой «Копировать», ссылки открываются в системе;
  • DOCX и XLSX разбираются на хосте в HTML без внешних зависимостей: у Word — абзацы и таблицы, у Excel — по таблице на лист (до 300 строк × 60 колонок, до 12 листов). Форматирование, картинки и формулы не переносятся;
  • остальные бинарники (.doc, .xls, .pptx и т.п.) — пометкой вместо байтов;
  • кнопка «В чат» вставляет @путь в сообщение, так файл уходит в контекст;
  • клик по файловой ссылке в ответе агента открывает файл в этой панели (с подсветкой нужной строки), а не во внешнем редакторе;
  • «открыть» — сдвоенная кнопка: левая половина открывает в приложении по умолчанию (на ней его иконка), правая — меню со всеми приложениями, которые в системе объявили этот тип файла, сверху помечено «по умолчанию»; внизу меню — «Показать в папке», так что отдельной кнопки для этого нет. Список берётся у LaunchServices (NSWorkspace), поэтому туда попадают только действительно поддерживающие формат программы, а не все установленные. В VS Code списка нет — остаётся одна кнопка с системным обработчиком;
  • пути за пределами открытых папок отклоняются на стороне движка.

Обе панели (папки слева и файлы справа) тянутся за вертикальный разделитель: курсор col-resize, при наведении проступает зелёная линия. Ширины запоминаются между запусками, двойной клик или Home возвращает ширину по умолчанию, стрелки двигают на 16px (Shift — на 32). Чат при этом не сжимается меньше 360px: на узком окне панели упираются в минимум.

В VS Code панель не нужна: там файлы открываются в настоящем редакторе, поэтому весь блок скрыт и работает только при surface-desktop.

Скриншоты

Скриншот можно вставить из буфера, перетащить в окно кнопкой с картинкой или просто сослаться на него путём:

@docs/chat.png @docs/onboarding.png посмотри на эти экраны и найди проблему

Такие ссылки не уходят в модель текстом (раньше они превращались в мусор из байтов): collectPromptContext читает файл и прикладывает его как image_url, показывая миниатюры в сообщении. Лимиты — 4 изображения на сообщение, 8 МБ на файл, поддерживаются PNG, JPEG, GIF, WebP. Если агент сам попытается прочитать картинку через read_file, он получит подсказку прикрепить её к сообщению.

Папки (рейл слева)

Окно устроено как харнес: слева рейл с папками на компьютере, справа — чат выбранной папки.

  • Первый запуск без аргументов сразу рабочий: сохранять нечего и выбирать не из чего, поэтому Codster сам создаёт Документы/Codster/Проект (Проект 2, … если имя занято) и открывает её — как это делают харнесы с папкой по умолчанию. О создании сообщает тост с путём, потому что папка появилась без диалога. Экран «Открыть папку / Создать папку» остаётся аварийным: он показывается, только если папку создать не удалось (нет прав на «Документы», битый путь), и пока папки нет, отправка сообщения заблокирована — агенту нечего читать.
  • Кнопка «Создать папку» (и пункт меню) не спрашивает имя: папка появляется там же, в Документах/Codster, и сразу открывается.
  • + в шапке рейла добавляет папку на диске; она сразу становится активной. Действия строки («новый чат в папке», «убрать папку», «удалить чат» корзиной) появляются при наведении на строку и не срабатывают, пока скрыты.
  • Раскрытая папка показывает свои чаты; + на строке папки создаёт в ней новый чат, × — закрывает чат или убирает саму папку из списка (файлы на диске не трогаются). Последнюю папку убрать нельзя, кроме случая, когда её уже нет на диске: тогда окно возвращается к экрану выбора папки.
  • Клик по чату в другой папке сначала переключает папку, потом открывает диалог.
  • Папку можно переключить и во время работы агента: у каждого чата свой хост, привязанный к его папке, поэтому запущенный turn продолжает писать туда, где начался, а не в открытую сейчас. Его вкладка остаётся в полосе с точкой «работает» и пунктирной рамкой, а имя папки в вкладке показывает, откуда он. Когда turn закончится, точка сменится зелёной меткой «готово» (она же на строке чата в рейле) и пропадёт, как только чат откроют.
  • Активная папка подсвечена, у каждой папки виден счётчик чатов; пропавшая с диска папка показывается зачёркнутой.
  • Агент всегда работает строго в папке своего чата: относительные пути резолвятся только в ней, инструменты не выпускают за её пределы (включая проверку симлинков).
  • Список папок и активная папка сохраняются между запусками. Папки можно задать при запуске: позиционный аргумент, --add-dir, --add-dir=, CODSTER_ADD_DIRS (через :) — они попадают в тот же рейл. База для автоматической и созданной папки — Документы/Codster; CODSTER_FOLDERS_DIR переносит её (нужно для тестов и своих сборок).

Что подменяется на десктопе:

В VS Code На десктопе
WorkspaceHost на API VS Code NodeWorkspaceHost на node:fs: корень проекта, glob-обход, tsc --noEmit для диагностики
acquireVsCodeApi() window.codster (preload + IPC)
workspaceState / globalState JSON в app.getPath("userData")
SecretStorage safeStorage (Keychain / DPAPI / libsecret)
QuickPick и openExternal системные диалоги Electron

Настройки, чаты, список папок и расход сохраняются между запусками. Установщики собирает electron-builder по electron-builder.yml; подпись Developer ID, notarization и автообновление в конфигурацию не входят — для них нужны сертификаты Apple и настройка publish.


Вопросы и ответы

Codster — это бесплатный аналог Cursor или GitHub Copilot? По сценарию — Cursor: агент с инструментами, правками и терминалом. Начать можно бесплатно: при первом запуске включается пробный лимит без регистрации. Аккаунт даёт лимит больше, платные тарифы — ещё больше; расход и остаток видны в разделе «План и потребление».

Нужен ли API-ключ OpenAI, Anthropic или DeepSeek? Нет. Ключ провайдера держит сервер Codster, поэтому в настройках расширения нечего вставлять. Свой ключ и свой сервер — это self-hosted сценарий: codster.cloudBaseUrl плюс CODSTER_UPSTREAM_BASE_URL / CODSTER_UPSTREAM_API_KEY на бэкенде.

Чем это отличается от автодополнения в Copilot? Codster не дописывает строку, а выполняет задачу: читает файлы, ищет по коду, правит их через old_string → new_string, запускает тесты и терминал, показывает diff и умеет откатить весь ход одной кнопкой.

Работает ли в Cursor, VS Codium и на других редакторах? В Cursor — да, ставится из .vsix. Требуется VS Code 1.90+ (или совместимый форк) на macOS, Windows или Linux; собственной сборки Code-OSS нет, расширение работает поверх редактора.

Можно ли обойтись без аккаунта? Да, на пробном режиме: чат, модели и правки доступны, но лимит меньше, а каталог умений и организации не работают. Аккаунт нужен, чтобы продолжить после лимита, синхронизировать историю и работать в команде.

Куда уходят данные? Запросы идут на бэкенд Codster (https://api.codster.app) и дальше к OpenAI-совместимому апстриму; .env не попадает ни в поиск, ни в контекст. Политика — codster.app/privacy, условия — codster.app/terms.

Какие модели и как их переключать? «Хокку», «Танка», «Ода» и «Эпос» — разная глубина размышлений; по умолчанию «Ода». Переключение — чип в композере, набор моделей — галочками в настройках. Каталог приходит с сервера, поэтому новая модель не требует обновления расширения.

Можно ли поставить свои правила и запреты? Да: .codster/rules.md, .cursor/rules/**/*.mdc, .cursorrules, AGENTS.md, .github/copilot-instructions.md и codster.customInstructions попадают в системный промпт. Опасные команды блокируются независимо от настроек.


Лицензия

MIT. См. файл LICENSE.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft