Skip to content
dsh.fish
Bundle

dsh-opencode-go-key-broker

DSH plugin: OpenCode Go API-key pool with quota-driven automatic switching, a Settings tab and a composer quota badge.

Source
Gdenich
License
MIT
Updated
Updated 2 hours ago

Readme

# dsh-opencode-go-key-broker

DSH-плагин: **автоматическое переключение ключей OpenCode Go по состоянию квоты**
(rolling / weekly / monthly) + отдельная вкладка в Settings для управления пулом.

## Зачем

В настройках моделей у маршрута `opencode-go` можно указать только один ключ
(`apiKeyEnv: OPENCODE_GO_API_KEY`). Когда у ключа выгорает окно квоты, запросы
падают с `QUOTA`, и переключение приходится делать руками.

DSH резолвит ключ маршрута **перед каждым запросом**
(`dsh-llm-pi-ai`: `credentials.resolve(profile.apiKeyEnv)` внутри `prepareCall`),
поэтому достаточно менять значение одного рефа — маршруты, модели и адаптер
трогать не нужно.

## Что делает

Пул хранится как конвенция (её же читает виджет `dsh-opencode-go-usage`):

```yaml
OPENCODE_GO_API_KEY: sk-…        # то, что реально читает маршрут (активный ключ)
OPENCODE_GO_KEY_ACTIVE: go2      # пометка активного (★ в виджете)
OPENCODE_GO_KEY_go1: sk-…
OPENCODE_GO_KEY_go2: sk-…
OPENCODE_GO_KEY_go3: sk-…
```

Две ветки переключения:

1. **Превентивная** — раз в `refreshMs` опрашивает `GET /v1/usage` по каждому
   ключу пула. Если активный ключ «красный» (`status !== "ok"` или
   `percent >= thresholds[window]`), берётся следующий зелёный и остаётся
   активным (sticky), пока не покраснеет сам.
2. **Реактивная** — слушает waterfall `agent/request-error`. При
   `QUOTA` / `INVALID_CREDENTIAL` меняет ключ и возвращает `{ kind: 'retry' }`:
   агент-луп переподготавливает запрос и **перерезолвивает креденшел**, то есть
   повтор идёт уже с новым ключом — в том же ходу, без ожидания следующего.

Запись идёт через `credentials.set/unset` (dsh-credentials-local) — тот же
кросс-процессный лок, что у страницы Models, поэтому ручные правки и UI не
затирают друг друга. Значения ключей не покидают Host: в браузер уходят только
имена, маски и проценты.

## Установка

Из реестра npm:

```sh
dsh plugin --profile desktop add dsh-opencode-go-key-broker
```

Из GitHub (без реестра, ставится сразу после публикации репозитория):

```sh
dsh plugin --profile desktop add github:Gdenich/dsh-opencode-go-key-broker
```

Локально из исходников (для разработки):

```sh
dsh plugin --profile desktop add file:/path/to/dsh-opencode-go-key-broker
```

`dsh plugin add` дописывает пакет в `dsh.profile.bundles` сам — по полю
`dsh.bundle.patch`. Дальше **перезапусти DSH Desktop**: список плагинов и
boot manifest читаются при загрузке профиля.

Две ловушки pnpm, о которых стоит знать:

- свежий релиз не ставится сразу (политика минимального возраста релиза) —
  если нужна именно новая версия, ставь точную: `dsh-opencode-go-key-broker@0.1.1`;
- `dsh.profile.bundles` в `desktop`-профиле правится только вручную или через
  CLI: профиль `desktop` управляется приложением, но список `bundles`,
  разошедшийся с шаблоном поставки, приложение считает пользовательским и не
  перезаписывает.

## Настройка

Политика — в `cordis.patch.yml` (или в override профиля):

| Ключ | По умолчанию | Смысл |
| --- | --- | --- |
| `enabled` | `true` | выключить брокер целиком |
| `dryRun` | `true` | только логировать решения, ничего не менять |
| `refreshMs` | `60000` | период опроса `/v1/usage` |
| `timeoutMs` | `15000` | таймаут запроса usage |
| `thresholds.{rolling,weekly,monthly}` | `98` | с какого процента окно «красное» |
| `warnPercent` | `80` | с какого процента показывать «близко» |
| `sticky` | `true` | сидеть на ключе, пока он не покраснеет |
| `prefer` | `first` | кого брать при переключении: `first` \| `headroom` |
| `switchOnError` | `true` | реактивная ветка (retry в том же ходу) |
| `keyNames` | авто | явный список имён пула вместо сканирования файла |

Переключение возможно **только на «не красный» кандидат**: если у всех
остальных ключей окно ≥ порога, статус `rate-limited` или запрос usage не
удался (сеть/401), брокер остаётся на текущем ключе и пишет в журнал
`no-alternative`. Уровень «близко» (`warn`, ≥ `warnPercent`) кандидатом быть
не мешает — он не красный.

**Порядок ввода в эксплуатацию:** оставь `dryRun: true`, поработай, посмотри
«Журнал решений» на вкладке Settings → *OpenCode Go Keys* («переключил бы на
go2, потому что monthly: 96%»), и только потом поставь `dryRun: false`.

Ограничитель: не больше 3 переключений в минуту (защита от циклов при
исчерпанных окнах у всех ключей).

## Вкладка Settings

**Settings → OpenCode Go Keys**: активный ключ и состояние, таблица ключей
(маска, ★ активный, окна rolling/weekly/monthly с процентами и временем до
сброса, уровень ok/близко/лимит/нет данных), кнопки «Сделать активным» и
«Удалить», форма добавления ключа и журнал решений.

## HTTP-маршруты (Host)

| Метод | Путь | Назначение |
| --- | --- | --- |
| GET | `/plugins/dsh-opencode-go-key-broker/snapshot?force=1` | состояние пула и лог |
| POST | `/plugins/dsh-opencode-go-key-broker/activate` `{name}` | форс активного ключа |
| POST | `/plugins/dsh-opencode-go-key-broker/refresh` | опросить usage сейчас |
| POST | `/plugins/dsh-opencode-go-key-broker/diag` | диагностика клиента (пишется в `$DSH_HOME/opencode-go-key-broker-diag.ndjson`) |

Мутации принимаются только с loopback и с того же origin.

## Разработка

```sh
# хост-тесты: самодостаточны, но плагину нужны peer-пакеты DSH,
# поэтому рядом с исходниками должен быть node_modules с ними
ln -sfn "$HOME/.dsh/profiles/desktop/node_modules" node_modules   # или путь к бандлу приложения
node test/host.test.mjs          # 14 сценариев брокера
node test/client-render.test.mjs # 4 блока клиента (React-заглушка, без зависимостей)
```

Правки исходников **не подхватываются автоматически**: профиль ставит пакет как
`file:`-зависимость, pnpm кладёт копию (жёсткими ссылками) в
`node_modules/dsh-opencode-go-key-broker`. После изменения файлов нужно либо
скопировать их в эту папку, либо повторить `dsh plugin --profile desktop add file:<путь>`.
Хост-код и `cordis.patch.yml` читаются при старте — изменения политики и хоста
требуют перезапуска приложения; клиентский бандл отдаётся с диска, ему достаточно
жёсткого рефреша страницы.

## Ограничения

- Ключи в пуле должны быть валидны: невалидный (401) считается «красным».
- `network`/`timeout` при опросе не считаются «красными» — иначе один сетевой
  сбой уводил бы с рабочего ключа.
- Смена ключа меняет аккаунт-контекст: prompt-cache у OpenCode Go привязан к
  ключу, поэтому первый запрос на новом ключе пойдёт без кэша.

Install

dsh plugin --profile web add github:Gdenich/dsh-opencode-go-key-broker

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source