dsh-rustvy
Rustvy for DeepSeek Harness: vendor marks in the model picker, and a model list kept in step with what the provider actually serves — the host half mirrors the catalog and logos to loopback and regenerates the pi-ai route's models from the route's own endpoint, showing the drift before anything is written. 适合需要直观识别模型厂商并保持模型列表与服务端同步的用户。
Install
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:koliy82/dsh-rustvyREADME
Read the full README ↗Регистрация на Rustvy по реферальной ссылке автора: При регистрации по этой ссылке вы получаете скидку 10% и бонусные токены. Подробные условия акции:
Rustvy (dsh-rustvy)
Русский | English
Значки производителей в списке моделей DeepSeek Harness и список моделей, который остаётся настолько же актуальным, насколько актуален сам провайдер. Один плагин, одно зеркало, без перезапусков.
Что он делает
У плагина две задачи, и обе работают от одного зеркала публичного каталога провайдера.
Значки. Один монохромный значок производителя перед каждой строкой модели в
списке выбора — нарисованный так же, как его рисует собственная панель провайдера:
CSS-маска, заливаемая currentColor, поэтому один файл на производителя
обслуживает обе темы оформления и все состояния строки — наведение, выбор,
отключение — без цветовых вариантов.
| Где | Что появляется |
|---|---|
| Меню моделей в композере | монохромный значок 16 px перед каждой строкой модели |
| Кнопка модели в композере | значок выбранной модели перед её названием (14 px) |
Всплывающее окно команды /model | тот же значок на каждой строке |
Скины, перерисовывающие список (dsh-claude-style) | тот же значок; собственная плашка скина в этой строке прячется, чтобы две плашки не встретились |
Список моделей. Профиль подключается к стороннему провайдеру через вручную
объявленный маршрут @deepseek-ai/dsh-llm-pi-ai, и именно список models этого
маршрута определяет, что вообще можно выбрать в интерфейсе: harness предлагает
модель только тогда, когда её отдаёт маршрут. Список, написанный вручную, устаревает
в тот день, когда провайдер выпускает новую модель — новая невидима, а снятая с
публикации остаётся выбираемой и падает уже на запросе. Плагин читает, что отдаёт
собственный эндпоинт маршрута, превращает это в точный формат записей адаптера,
показывает расхождение с установленным списком и по команде записывает его обратно
через ctx.configEditor.
Зачем это нужно
Потому что обе поломки маршрута, который поддерживают вручную, — тихие. Модель, которую провайдер добавил, не появляется, и никто не говорит почему: ошибки нет, есть только отсутствие. Модель, которую провайдер снял, по-прежнему видна, и её выбор даёт сетевую ошибку, которая выглядит как проблема со связью, а не как устаревшая строка в YAML-файле.
И то и другое лечится чтением данных у провайдера вместо их переписывания. Список, который записывает этот плагин, несёт настоящие размер окна контекста, лимит вывода, входные модальности и уровни рассуждения, поэтому модель, получившая поддержку изображений или новый уровень мышления, описана верно в тот же день, а не наследует запасное значение, которое кто-то когда-то угадал.
А поскольку значки берутся из того же зеркала, совершенно новая модель приходит уже с кешированным значком своего производителя: зеркало перечитывает каталог в тот момент, когда эндпоинт называет модель, которой оно раньше не видело, — то есть ровно тогда, когда выходит релиз.
Ничего из этого не привязано к одному провайдеру. Сайт-источник, маршрут и строка
профиля — настройки; https://rustvy.xyz и rustvy являются значениями по
умолчанию.
Установка
dsh plugin --profile web add dsh-rustvy
Либо прямо с GitHub, если нужна самая свежая ревизия, а не опубликованная версия:
dsh plugin --profile web add github:koliy82/dsh-rustvy
Голый koliy82/dsh-rustvy без схемы не принимается: разбор источника установки
знает имя в реестре, git-спецификацию, tarball и абсолютный локальный путь, а
git-спецификации нужна схема либо сокращение github:.
Подтверждение сборки не запрашивается ни в том ни в другом случае: у пакета нет ни install-скрипта, ни зависимостей, поэтому pnpm нечего собирать.
Из локальной копии этого репозитория:
pwsh -File .\install.ps1
install.ps1 копирует пакет в \node_modules\dsh-rustvy как настоящий
каталог и дописывает имя в dsh.profile.bundles манифеста профиля. Работающий
harness подхватывает строку по изменению манифеста.
После этого один раз перезапустите harness или обновите страницу: значки и страница настроек появляются на лету, а строка загрузчика читается на композиции.
Маршрут, в который пишется список моделей
Значки работают без всего остального. Половине со списком моделей нужен маршрут
@deepseek-ai/dsh-llm-pi-ai, в который можно писать. Добавьте в
cordis.patch.yml профиля — или отредактируйте в Настройки → Плагины →
Конфигурация плагинов:
- id: llm-pi-ai
name: "@deepseek-ai/dsh-llm-pi-ai"
config:
providers:
rustvy:
displayName: rustvy
apiKeyEnv: RUSTVY_API_KEY
api: openai-completions
baseURL: https://rustvy.xyz/v1
models:
- id: placeholder
name: placeholder
Список models обязан только разбираться: этот плагин его заменяет. Все остальные
поля при перегенерации переносятся без изменений, потому что переключатель
совместимости протокола или ссылка на учётные данные — это знание о вашем
развёртывании, которого нет ни в одном каталоге.
apiKeyEnv задаёт, откуда маршрут берёт ключ; он разрешается на каждый запрос через
механизм учётных данных harness — сначала унаследованное окружение процесса, затем
$DSH_HOME/.credentials.yaml, затем .env в каталоге запуска или в $DSH_HOME.
Сам ключ в конфигурационные файлы не попадает. Плагин при чтении списка моделей
подставляет ключ только тогда, когда окружение процесса уже содержит переменную с
таким именем; эндпоинт, под который он написан, отвечает и без ключа.
Чтобы сделать модель основной:
- id: agent-default-model
name: "@deepseek-ai/dsh-agent-default-model"
config:
provider: rustvy
model: claude-opus-5-5
reasoningEffort: high
Как пользоваться
Откройте Настройки → Rustvy. Страница показывает, что держит зеркало, что отдаёт провайдер и каково точное расхождение между ними. Кнопка «Обновить каталог и иконки» освежает обе половины, «Записать список моделей» записывает сгенерированный список. Без одного из этих нажатий не пишется ничего, если вы сами не попросили об этом в настройках.
Настройки
Настройки → Rustvy
Значки:
| Параметр | По умолчанию | Смысл |
|---|---|---|
| Показывать иконки моделей | вкл | общий выключатель значков; в выключенном виде убирает все значки и не трогает список моделей |
| Иконка в кнопке выбранной модели | вкл | значок и в кнопке модели композера, а не только в строках меню |
| Прятать логотипы других скинов | вкл | прятать собственную плашку скина в строке, где значок нарисован этим плагином |
| Размер значка, px | 16 | от 10 до 32 |
Список моделей:
| Параметр | По умолчанию | Смысл |
|---|---|---|
| Держать список моделей маршрута актуальным | вкл | общий выключатель; в выключенном виде YAML маршрута не трогается |
| Записывать список | при нажатии «Обновить» | только отдельной кнопкой — самостоятельно не пишет никогда · при нажатии «Обновить» — пишет по кнопке обновления · сам, при каждом обновлении — пишет и при фоновом суточном обновлении |
| Название модели | как на сайте провайдера | отображаемое имя провайдера либо сырой идентификатор |
| Предлагать уровень «без рассуждений» | вкл | добавлять уровень off рассуждающей модели, которая его не объявила |
| Оставлять модели, которые провайдер больше не отдаёт | выкл | сохранять установленную модель, которую эндпоинт больше не отдаёт, — на случай временно ошибочного списка, а не действительно снятой модели |
| Маршрут провайдера | rustvy | какой ключ внутри providers записывать |
| Строка профиля | llm-pi-ai | какая строка загрузчика его содержит |
| Сайт провайдера | https://rustvy.xyz | откуда читать каталог и логотипы |
Режим записи по умолчанию намеренно средний: нажатие кнопки — это решение человека, а фоновое истечение срока — нет, и переписывать конфигурацию загрузчика работающего профиля, когда никто не просил, — это то, на что стоит спросить разрешение.
Каждый адрес и файл, с которым работает плагин, виден на странице: сайт, страница
каталога и эндпоинт списка моделей — ссылками, открывающимися в новой вкладке, а
пути к зеркалу, настройкам, копии для отката и патчу профиля — текстом с кнопкой
копирования. Кнопка «Показать блок для ручной правки» выдаёт ровно тот YAML,
который получился бы при автоматической записи, так что желающий вставить его в
cordis.patch.yml сам не переписывает краткий пересказ.
У одного поля нет элемента управления, потому что оно принадлежит YAML профиля, а не
форме: modelZones (по умолчанию ["standard"]) — зоны каталога, которые
допускаются, когда эндпоинт недоступен и состав списка определяет зеркало.
Настройки хранятся в \.dsh-rustvy\config.json. Две переменные окружения
перекрывают их — для развёртывания, которое направляет зеркало в другое место или
хочет смонтировать плагин неработающим: DSH_RUSTVY_ORIGIN и
DSH_RUSTVY_DISABLED=1.
Откуда берутся картинки
В сам пакет ничего не вшито. Хост-половина зеркалит два публичных объекта с сайта провайдера в профиль, а браузерная половина читает их с локального адреса:
- каталог моделей — сайт рисует свою таблицу моделей из JSON-острова в
собственной странице (``), поэтому
неаутентифицированный читатель получает ровно те данные, которые видит панель.
Зеркалится в
\.dsh-rustvy\catalog.json. - файлы логотипов (
/logos/.svg) — копируются в\.dsh-rustvy\logos\при первом обращении.
Оба читаются по собственному исходящему маршруту процесса (lib/net.js): сначала
живой глобальный диспетчер, чтобы прокси, установленный dsh-proxy-gui или любым
другим маршрутом процесса, соблюдался, затем соединитель, построенный здесь из
HTTPS_PROXY / ALL_PROXY / HTTP_PROXY, — на случай, когда диспетчер не
установлен. NO_PROXY учитывается в обоих случаях. Сама страница наружу не выходит:
браузер спрашивает значок у 127.0.0.1, и именно поэтому всё работает на машине,
чей браузер до источника не достаёт вовсе.
Каталог обновляется раз в 24 часа и по требованию; холодное зеркало ожидается до 6 секунд, а устаревшее обновляется в фоне, пока строки продолжают показывать имеющиеся значки. Когда источник недоступен, кеш на диске по-прежнему отдаёт всё, что в нём есть.
Откуда берётся список моделей
Два источника в таком порядке, потому что отвечают они на разные вопросы:
- собственный эндпоинт маршрута (
GET {baseURL}/models) — авторитетный список состава: ровно те модели, которые можно назвать в запросе, каждая со своимиcontext_k,max_output_tokens,modalitiesиreasoning_levels. Это тот же эндпоинт, который опрашивает собственное обнаружение адаптера. - зеркало острова каталога — названия и запасной вариант. Это единственный
источник, несущий отображаемое имя, производителя и имя файла логотипа, поэтому
он соединяется с отдаваемым списком по идентификатору. Когда эндпоинт не
отвечает, состав определяет сам остров по своим полям
enabledиzone, так что маршрут остаётся актуальным и во время сбоя, а не застывает на последнем удачном чтении. На здоровом источнике оба списка совпадают точно.
Модель, которую эндпоинт отдаёт, а зеркало ни разу не видело, — признак того, что остров отстал от боевого API, а это происходит ровно в момент релиза, поэтому тогда остров перечитывается; ожидание ограничено тем же коротким сроком, что и для холодного зеркала.
Что именно записывается
Каждая сгенерированная запись несёт только те поля, которые объявлены в схеме
конфигурации адаптера, потому что список models профиля маршрута заменяет
каталог маршрута, а неизвестное поле в лучшем случае будет отброшено:
| Поле | Откуда берётся |
|---|---|
id | идентификатор отдаваемой модели — то, что нужно назвать в запросе |
name | отображаемое имя из острова либо идентификатор, если имени нет |
contextWindow | context_k × 1000 |
maxTokens | max_output_tokens |
input | modalities, отфильтрованные до text/image |
reasoningEfforts | reasoning_levels, отображённые на уровни адаптера, либо false для нерассуждающей модели |
compat | не генерируется никогда — существующее всегда переносится |
Поле, о котором никто не высказался, пропускается, а не заполняется значением по
умолчанию, чтобы запасное значение самого адаптера оставалось в силе и написанное
вручную не закреплялось молча. compat — единственное, что переносится без
изменений: переключатель совместимости протокола это знание о шлюзе, которого нет
ни в одном каталоге.
Уровни рассуждения отображаются на словарь адаптера (off, minimal, low,
medium, high, xhigh, max); неизвестный ему уровень отбрасывается и сообщается
примечанием, а не отказом всей записи. Необъявленные уровни адаптер не
поддерживает, а объявленный off без боевого значения означает «предлагать, ничего
не отправляя» — поэтому этот переключатель включён по умолчанию.
Что запись отклоняет
Пустой список не записывается никогда: единственный способ его получить — неудачное
чтение, а маршрут без моделей не обслуживает ничего. Актуальный список тоже не
записывается, потому что configEditor.edit() согласует весь загрузчик, и тратить
одно согласование на отсутствие изменений означало бы перезагрузить работающий
профиль впустую. Каждая запись сохраняет предыдущий документ в
\.dsh-rustvy\models-backup.json, а кнопка «Откатить к предыдущему»
возвращает его обратно; состояние, которое откат вытесняет, само становится
следующей копией, поэтому откат тоже можно откатить.
Как строка сопоставляется со значком
Строки сопоставляются по видимому названию в таком порядке:
- идентификатор модели из зеркального каталога (
claude-opus-5-5,abyss/glm-5.3, …); - её отображаемое имя (
OPUS 5.5); - идентификатор, с которого название начинается, — скин может оставить в том же
элементе собственную строку описания, поэтому название часто является префиксом,
а не всей строкой; побеждает самый длинный подходящий идентификатор, чтобы
claude-opus-5-5никогда не досталсяclaude-opus-5; - форма идентификатора — для модели, которой зеркало ни разу не видело:
claude-*→ Anthropic,gpt-*/o1…o9→ OpenAI,glm-*→ Zhipu,kimi-*/k3→ Moonshot,qwen-*→ Alibaba Qwen,deepseek-*→ DeepSeek,ahmia-*→ Ahmia,mimo-*→ Xiaomi,fable-*→ Anthropic,gemini-*→ Google,grok-*→ xAI.
Модель, не подошедшая ни под что, не получает значка вовсе: выдуманная иконка была бы неправильным ответом, а не отсутствующим. Модель, которая подошла, но чей логотип зеркало получить не смогло, получает вместо пустого квадрата плашку с монограммой — первыми буквами производителя: маска с неработающим URL закрасилась бы сплошным квадратом.
Строки уровней усилия, заголовки производителей, корневые ячейки меню, поле поиска и строки о сбое каталога не подходят ни под одно правило и остаются без значка.
Маршруты
Одна регистрация префикса на собственном веб-сервере harness. Маршруты плагинов не
видны фильтру запросов хоста, поэтому каждый из них проверяет собеседника сам:
только локальный адрес, только локальный Host, только same-origin
Sec-Fetch-Site и только совпадающий Origin.
| Маршрут | Метод | Ответ |
|---|---|---|
/dsh-rustvy/catalog.json | GET | зеркальный каталог плюс настройки, с которыми браузерная половина рисует |
/dsh-rustvy/logo/.svg | GET, HEAD | один кешированный логотип, при первом запросе забираемый с источника; 404, когда такого значка у |