@goodandready/dsh-opencode-provider 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +97 -93
- package/LICENSE +21 -21
- package/NOTICE +15 -15
- package/README.md +79 -46
- package/README.ru.md +170 -54
- package/README.zh.md +151 -60
- package/cordis.patch.yml +11 -11
- package/lib/client.js +0 -95
- package/package.json +122 -125
package/README.ru.md
CHANGED
|
@@ -1,87 +1,203 @@
|
|
|
1
|
-
# @goodandready/dsh-opencode-provider
|
|
1
|
+
# 📦 @goodandready/dsh-opencode-provider
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<div align="center">
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
<h3>Плагин-провайдер OpenCode Console и OpenCode Go для DeepSeek Harness</h3>
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
<p>
|
|
8
|
+
<a href="https://goodandready.app/"><img src="https://img.shields.io/badge/🌐_DSH_Hub-goodandready.app-ff4500.svg?style=for-the-badge&labelColor=1a1a2e" alt="Витрина GoodAndReady"></a>
|
|
9
|
+
<a href="https://www.npmjs.com/package/@goodandready/dsh-opencode-provider"><img src="https://img.shields.io/npm/v/@goodandready/dsh-opencode-provider?style=for-the-badge&color=crimson&logo=npm" alt="npm версия"></a>
|
|
10
|
+
<a href="https://github.com/GooDAnDReaDY/dsh-opencode-provider/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=for-the-badge" alt="Лицензия"></a>
|
|
11
|
+
</p>
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
</div>
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
<table align="center">
|
|
16
|
+
<tr>
|
|
17
|
+
<td align="center">
|
|
18
|
+
⭐ <strong>Если вам нравится этот плагин, поставьте ему Star на GitHub</strong> — это покажет мне, что плагин полезен, и добавит мотивации продолжать его развитие.
|
|
19
|
+
<br><br>
|
|
20
|
+
🐛 <strong>Если вы нашли баг или хотите предложить новую функцию</strong>, создайте Issue на GitHub на любом языке — я рассмотрю предложение и реализую полезные улучшения в одной из следующих версий плагина.
|
|
21
|
+
</td>
|
|
22
|
+
</tr>
|
|
23
|
+
</table>
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
- Живой каталог `/v1/models` с резервным generated-каталогом models.dev.
|
|
18
|
-
- Оценки лимитов Go и Go Plus для 5 часов / недели / месяца.
|
|
19
|
-
- Plan-aware picker (Go / Console / FREE).
|
|
20
|
-
- Пул аккаунтов: ротация на 401/403/429 и исчерпанной квоте; cooldown хранится по fingerprint ключа.
|
|
21
|
-
- Health аккаунтов, квоты по каждому аккаунту, правила «модель → аккаунт».
|
|
22
|
-
- Аналитика использования, heatmap запросов, таблицы Models / Days / Sessions.
|
|
23
|
-
- Core-инструменты сохраняются при лимите 128; изображения в tool-result заменяются placeholder.
|
|
24
|
-
- One-click обновление плагина в Settings → OpenCode.
|
|
25
|
+
---
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
Плагин-провайдер OpenCode / OpenCode Go для [DeepSeek Harness](https://deepseek-harness.github.io/deepseek-harness/) (dsh).
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
Плагин регистрирует два нативных LLM-маршрута:
|
|
30
|
+
|
|
31
|
+
| Маршрут | Продукт / Тариф | Базовый эндпоинт | Назначение |
|
|
32
|
+
|---|---|---|---|
|
|
33
|
+
| `opencode` | OpenCode Console / Zen | `https://opencode.ai/inference` | Поминутная тарификация (pay-as-you-go) |
|
|
34
|
+
| `opencode-go` | OpenCode Go / Go Plus | `https://opencode.ai/inference/go` | Фиксированная подписка с лимитами 5h / недели / месяца |
|
|
35
|
+
|
|
36
|
+
Ключи разрешаются динамически на каждый вызов: смена токена в настройках или в файле окружения вступает в силу мгновенно без перезапуска сервера.
|
|
37
|
+
|
|
38
|
+
## Архитектура
|
|
39
|
+
|
|
40
|
+
```mermaid
|
|
41
|
+
graph LR
|
|
42
|
+
subgraph DSH["Ядро DeepSeek Harness"]
|
|
43
|
+
Agent["DSH Агент / Чат"]
|
|
44
|
+
Creds["Сервис учетных записей / auth.json"]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
subgraph Plugin["dsh-opencode-provider"]
|
|
48
|
+
Router{"Выбор маршрута"}
|
|
49
|
+
Pool["Пул аккаунтов<br/>(Авто-ротация и Cooldown)"]
|
|
50
|
+
Heatmap["Телеметрия и Heatmap<br/>(5h / Неделя / Месяц)"]
|
|
51
|
+
|
|
52
|
+
subgraph Transports["4 Протокола передачи (Wire Transports)"]
|
|
53
|
+
T1["OpenAI Chat"]
|
|
54
|
+
T2["Anthropic Messages<br/>(Thinking Signatures)"]
|
|
55
|
+
T3["OpenAI Responses"]
|
|
56
|
+
T4["Google GenAI<br/>(thoughtSignature Replay)"]
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
subgraph OpenCode["Шлюз OpenCode Gateway"]
|
|
61
|
+
Console["opencode (Console / Zen)"]
|
|
62
|
+
Go["opencode-go (Go / Go Plus)"]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
Agent --> Router
|
|
66
|
+
Creds -.-> Pool
|
|
67
|
+
Router -->|opencode| Pool
|
|
68
|
+
Router -->|opencode-go| Pool
|
|
69
|
+
Pool --> Heatmap
|
|
70
|
+
Pool --> Transports
|
|
71
|
+
T1 & T2 & T3 & T4 --> Console & Go
|
|
32
72
|
```
|
|
33
73
|
|
|
34
|
-
|
|
74
|
+
## Полный обзор возможностей
|
|
75
|
+
|
|
76
|
+
- **Четыре нативных транспорта**, выбираемых автоматически под каждую модель:
|
|
77
|
+
- OpenAI Chat Completions (DeepSeek, GLM, Kimi, MiMo, LongCat, Hy3, Step и др.).
|
|
78
|
+
- Anthropic Messages (Claude, MiniMax, Qwen).
|
|
79
|
+
- OpenAI Responses (GPT, Grok, Muse).
|
|
80
|
+
- Google Generative AI (Gemini).
|
|
81
|
+
- **Полноценная поддержка всех типов данных**: текст, рассуждения (reasoning), стриминг вызовов инструментов (tool calls), статистика токенов (usage), корректные finish reasons.
|
|
82
|
+
- **Обработка изображений** через встроенный сервис attachments с нативным рендерингом под протокол конкретного транспорта.
|
|
83
|
+
- **Воспроизведение метаданных провайдеров** при строгих требованиях API:
|
|
84
|
+
- Подписи рассуждений Anthropic (`thinking signatures`).
|
|
85
|
+
- Google `thoughtSignature` в истории вызовов функций.
|
|
86
|
+
- **Пул аккаунтов с интеллектуальной авто-ротацией** до старта генерации:
|
|
87
|
+
- Автоматический переход на следующий аккаунт при 401/403/429 или ответах о превышении квот (400/402/403/429).
|
|
88
|
+
- Недействительный ключ охлаждается 24 часа, превышение квоты — 10 минут (или по заголовку `Retry-After`), rate limit — 60 секунд (или по `Retry-After`).
|
|
89
|
+
- Если запрос уже начал стриминг, он не переключается посреди ответа; следующий запрос берёт следующий готовый ключ.
|
|
90
|
+
- **Квоты по каждому аккаунту**: отображение индивидуального расхода за 5 часов, неделю и месяц; активный аккаунт управляет сайдбаром и панелью.
|
|
91
|
+
- **Правила «модель ➔ аккаунт»**: сопоставление по glob-паттернам закрепляет предпочтительный аккаунт за моделью; остальные аккаунты остаются в резерве.
|
|
92
|
+
- **Почасовой Heatmap запросов**: сетка «день недели × час» в локальном времени плюс суточная полоса агрегации. Фильтр переключает отображение между всеми запросами и конкретной моделью. Лог `~/.dsh/storages/dsh-opencode/requests.jsonl` хранит только метаданные `{at, provider, model, purpose}` (промпты и ответы не сохраняются).
|
|
93
|
+
- **Стабильный заголовок `x-opencode-session`**: вычисляется из устойчивого ID сессии DSH, обеспечивая одинаковый ключ маршрутизации при повторах и диалоге.
|
|
94
|
+
- **Живой каталог моделей**: автоматическое чтение `/v1/models` с резервным fallback из `models.dev`.
|
|
95
|
+
- **Оценки лимитов подписки OpenCode Go**: долларовый лимит и расчетное число запросов для окон 5h, недели и месяца для планов Go и Go Plus.
|
|
96
|
+
- **Команда `/opencode`**: быстрый просмотр статуса, квот, аккаунтов, моделей, правил и запуск теста связи `test [model]`.
|
|
97
|
+
- **Настройки в терминале (TUI)**: раздел `/settings → OpenCode` для профиля terminal с ключом, эндпоинтами, allowlist, активным аккаунтом и таймаутами.
|
|
98
|
+
- **Интеграция с Settings → Models**: карточка ключа в стандартном списке моделей DSH.
|
|
99
|
+
- **One-Click обновление плагина**: кнопка **Update now** в настройках проверяет npm registry, ставит точную версию через DSH CLI и запрашивает перезапуск.
|
|
100
|
+
- **Plan-aware Go picker**: фильтрация моделей вне тарифной сетки Go с бейджами `Go`, `Console` или `FREE`.
|
|
101
|
+
- **Защита лимита MCP-инструментов**: сохраняет системные инструменты DSH (`read`, `write`, `edit`, `bash`, `glob`, `grep`) при сокращении сторонних инструментов под лимит 128.
|
|
102
|
+
- **Панель аналитики использования**: карточки KPI (запросы, токены, успешность, reasoning, кэш, TTFT, задержка), фильтр 7/30/90 дней и таблицы Models / Days / Sessions.
|
|
103
|
+
|
|
104
|
+
## Установка
|
|
105
|
+
|
|
106
|
+
Сборка и упаковка из исходников:
|
|
35
107
|
|
|
36
108
|
```bash
|
|
37
|
-
|
|
109
|
+
npm install
|
|
110
|
+
npm run build
|
|
111
|
+
npm pack
|
|
38
112
|
```
|
|
39
113
|
|
|
40
|
-
|
|
114
|
+
Установка из **упакованного tarball**:
|
|
41
115
|
|
|
42
|
-
|
|
116
|
+
```bash
|
|
117
|
+
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.1.tgz
|
|
118
|
+
# DSH Desktop:
|
|
119
|
+
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.1.tgz
|
|
43
120
|
|
|
44
|
-
|
|
121
|
+
# Либо установка опубликованного пакета из npm:
|
|
122
|
+
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.1
|
|
123
|
+
```
|
|
45
124
|
|
|
46
|
-
|
|
125
|
+
После установки перезапустите dsh (или DSH Desktop) и выберите **OpenCode** или **OpenCode Go** в списке моделей.
|
|
126
|
+
|
|
127
|
+
## Конфигурация
|
|
128
|
+
|
|
129
|
+
Пример файла конфигурации:
|
|
130
|
+
|
|
131
|
+
```yaml
|
|
132
|
+
- id: llm-opencode
|
|
133
|
+
name: "@goodandready/dsh-opencode-provider"
|
|
134
|
+
config:
|
|
135
|
+
apiKeyEnv: OPENCODE_API_KEY # переменная окружения или имя credential
|
|
136
|
+
# apiKey: oc_sk_... # только для локальной сборки, не коммитить!
|
|
137
|
+
openCodeAuthEntry: opencode-go
|
|
138
|
+
requestTimeoutMs: 300000
|
|
139
|
+
streamIdleTimeoutMs: 300000
|
|
140
|
+
visibleModels: []
|
|
141
|
+
modelTransports: {}
|
|
142
|
+
accounts:
|
|
143
|
+
- label: Go #2
|
|
144
|
+
apiKeyEnv: OPENCODE_API_KEY_2
|
|
145
|
+
activeAccount: '' # метка аккаунта для принудительного закрепления
|
|
146
|
+
```
|
|
47
147
|
|
|
48
|
-
|
|
148
|
+
### Источники ключей
|
|
49
149
|
|
|
50
|
-
|
|
150
|
+
1. `config.apiKey` в конфигурации DSH (`role('secret')`).
|
|
151
|
+
2. Системные учетные данные DSH по имени `apiKeyEnv`.
|
|
152
|
+
3. Авто-импорт из `~/.local/share/opencode/auth.json` (по ключу `openCodeAuthEntry`, по умолчанию `opencode-go`), записанный CLI-утилитой OpenCode.
|
|
153
|
+
4. Переменные окружения процесса, проекта `.env` или `$DSH_HOME/.env`.
|
|
51
154
|
|
|
52
|
-
|
|
155
|
+
### Настройка пула аккаунтов и ротации
|
|
53
156
|
|
|
54
|
-
|
|
157
|
+
Добавьте второй аккаунт в **Настройки → OpenCode → Accounts**: укажите метку (например, `Go #2`) и имя переменной/credential (`OPENCODE_API_KEY_2`), вставьте ключ и сохраните. Аккаунт по умолчанию (`default`) всегда обслуживает запросы первым, если не закреплен другой.
|
|
55
158
|
|
|
56
|
-
|
|
159
|
+
При ошибке авторизации, исчерпании лимита или rate limit адаптер помечает ключ периодом охлаждения (cooldown) и немедленно повторяет запрос со следующим доступным аккаунтом. Состояния cooldown сохраняются между перезапусками в `~/.dsh/storages/dsh-opencode/cooldowns.json` по SHA-256 хешу ключа — сырые токены на диск не записываются.
|
|
57
160
|
|
|
58
|
-
|
|
161
|
+
### Правила привязки «модель ➔ аккаунт»
|
|
59
162
|
|
|
60
|
-
|
|
61
|
-
2. При наличии новой версии показывает badge и **Update now**.
|
|
62
|
-
3. Кнопка ставит точную версию через обычный `dsh plugin add`; параллельная установка блокируется, `--force` не используется, установка из worktree или `file:` запрещена.
|
|
63
|
-
4. После успеха карточка просит перезапустить DSH.
|
|
163
|
+
С помощью glob-паттернов можно закрепить определенные модели за выделенными аккаунтами:
|
|
64
164
|
|
|
65
|
-
|
|
165
|
+
```yaml
|
|
166
|
+
modelAccountRules:
|
|
167
|
+
- pattern: "*free*"
|
|
168
|
+
account: "free"
|
|
169
|
+
- pattern: "your-provider-*"
|
|
170
|
+
account: "go"
|
|
171
|
+
```
|
|
66
172
|
|
|
67
|
-
##
|
|
173
|
+
## Страница настроек Web UI
|
|
68
174
|
|
|
69
|
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
-
|
|
175
|
+
В разделе **Настройки → OpenCode**:
|
|
176
|
+
- **Key**: статус ключа, вставка, импорт из OpenCode CLI, очистка и кнопка **Test connection** с замером задержки.
|
|
177
|
+
- **Quota**: шкалы расхода за 5 часов, неделю и месяц с датами сброса и предупреждениями при 80% и 95%.
|
|
178
|
+
- **Accounts**: управление пулом дополнительных учетных записей.
|
|
179
|
+
- **Models**: поиск, обновление каталога, фильтрация allowlist, переопределение транспорта и просмотр стоимости токенов.
|
|
180
|
+
- **Connection**: таймауты, базовые URL и переключатель карточки сайдбара.
|
|
181
|
+
- **Plugin update**: статус версии и кнопка **Update now**.
|
|
73
182
|
|
|
74
|
-
##
|
|
183
|
+
## Команды CLI
|
|
75
184
|
|
|
76
|
-
|
|
185
|
+
```bash
|
|
186
|
+
/opencode status # статус аккаунтов, квоты и правила
|
|
187
|
+
/opencode usage # текущие окна квот
|
|
188
|
+
/opencode accounts # список меток, статус и расход 5h/wk/mo
|
|
189
|
+
/opencode models # список моделей и лимиты Go
|
|
190
|
+
/opencode rules # правила модель ➔ аккаунт
|
|
191
|
+
/opencode test [model] # проверочный запрос к модели
|
|
192
|
+
```
|
|
77
193
|
|
|
78
|
-
##
|
|
194
|
+
## Безопасность и надежность
|
|
79
195
|
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
-
|
|
83
|
-
- Анонимный free lane (`Bearer public` + CLI impersonation) не реализован.
|
|
196
|
+
- Запросы на изменение настроек и ключей разрешены только с loopback (`127.0.0.1` / `::1`) с обязательной проверкой same-origin для защиты от DNS-rebinding атак.
|
|
197
|
+
- Тексты запросов и ответов пользователей никогда не записываются в файлы журнала.
|
|
198
|
+
- При исчерпании квоты на одном ключе диалог пользователя не прерывается благодаря бесшовной ротации.
|
|
84
199
|
|
|
85
|
-
##
|
|
200
|
+
## Лицензия
|
|
86
201
|
|
|
87
|
-
MIT. См.
|
|
202
|
+
MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY). См. [LICENSE](./LICENSE) и [NOTICE](./NOTICE).
|
|
203
|
+
|
package/README.zh.md
CHANGED
|
@@ -1,87 +1,178 @@
|
|
|
1
|
-
# @goodandready/dsh-opencode-provider
|
|
1
|
+
# 📦 @goodandready/dsh-opencode-provider
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<div align="center">
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
<h3>面向 DeepSeek Harness 的 OpenCode Console 与 OpenCode Go 提供商插件</h3>
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
<p>
|
|
8
|
+
<a href="https://goodandready.app/"><img src="https://img.shields.io/badge/🌐_DSH_Hub-goodandready.app-ff4500.svg?style=for-the-badge&labelColor=1a1a2e" alt="GoodAndReady 插件展厅"></a>
|
|
9
|
+
<a href="https://www.npmjs.com/package/@goodandready/dsh-opencode-provider"><img src="https://img.shields.io/npm/v/@goodandready/dsh-opencode-provider?style=for-the-badge&color=crimson&logo=npm" alt="npm 版本"></a>
|
|
10
|
+
<a href="https://github.com/GooDAnDReaDY/dsh-opencode-provider/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=for-the-badge" alt="许可证"></a>
|
|
11
|
+
</p>
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
</div>
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
<table align="center">
|
|
16
|
+
<tr>
|
|
17
|
+
<td align="center">
|
|
18
|
+
⭐ <strong>如果您喜欢这个插件,请在 GitHub 上为它点亮 Star</strong> — 这能让我知道插件对您有用,并鼓励我继续开发和维护它。
|
|
19
|
+
<br><br>
|
|
20
|
+
🐛 <strong>如果您发现 Bug 或希望增加功能</strong>,请使用任意语言在 GitHub 上提交 Issue — 我会评估您的建议,并在后续版本中实现有价值的改进。
|
|
21
|
+
</td>
|
|
22
|
+
</tr>
|
|
23
|
+
</table>
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
- 从 `/v1/models` 读取实时目录,并有 models.dev 生成目录作为回退。
|
|
18
|
-
- Go 与 Go Plus 的 5 小时 / 每周 / 每月订阅额度估算。
|
|
19
|
-
- 计划感知的模型选择器(Go / Console / FREE)。
|
|
20
|
-
- 多账户轮换:401/403/429 或配额耗尽时切换到下一个账户;冷却按密钥指纹持久化。
|
|
21
|
-
- 账户健康、每账户配额、模型 → 账户规则。
|
|
22
|
-
- 用量分析面板、请求热力图、Models / Days / Sessions 表。
|
|
23
|
-
- 核心工具在 128 个工具上限内优先保留;工具结果图片转为占位符。
|
|
24
|
-
- Settings → OpenCode 中的一键插件更新。
|
|
25
|
+
---
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
面向 [DeepSeek Harness](https://deepseek-harness.github.io/deepseek-harness/)(dsh)的 OpenCode / OpenCode Go 提供商插件。
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
插件注册两个原生 LLM 路由:
|
|
30
|
+
|
|
31
|
+
| 路由 | 产品 / 方案 | 基础接口 | 特性说明 |
|
|
32
|
+
|---|---|---|---|
|
|
33
|
+
| `opencode` | OpenCode Console / Zen | `https://opencode.ai/inference` | 按量计费(pay-as-you-go) |
|
|
34
|
+
| `opencode-go` | OpenCode Go / Go Plus | `https://opencode.ai/inference/go` | 订阅专属,含 5小时/每周/每月配额估算 |
|
|
35
|
+
|
|
36
|
+
密钥在每次调用时动态解析:在设置中或环境文件中更换凭据无需重启服务器即可立即生效。
|
|
37
|
+
|
|
38
|
+
## 架构示意
|
|
39
|
+
|
|
40
|
+
```mermaid
|
|
41
|
+
graph LR
|
|
42
|
+
subgraph DSH["DeepSeek Harness 核心"]
|
|
43
|
+
Agent["DSH 智能体 / 聊天"]
|
|
44
|
+
Creds["凭据服务 / auth.json"]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
subgraph Plugin["dsh-opencode-provider"]
|
|
48
|
+
Router{"路由选择器"}
|
|
49
|
+
Pool["多账户连接池<br/>(智能轮换与冷却)"]
|
|
50
|
+
Heatmap["遥测与请求热力图<br/>(5小时 / 周 / 月)"]
|
|
51
|
+
|
|
52
|
+
subgraph Transports["4 种底层传输协议 (Wire Transports)"]
|
|
53
|
+
T1["OpenAI Chat"]
|
|
54
|
+
T2["Anthropic Messages<br/>(Thinking Signatures)"]
|
|
55
|
+
T3["OpenAI Responses"]
|
|
56
|
+
T4["Google GenAI<br/>(thoughtSignature 重放)"]
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
subgraph OpenCode["OpenCode 网关"]
|
|
61
|
+
Console["opencode (Console / Zen)"]
|
|
62
|
+
Go["opencode-go (Go / Go Plus)"]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
Agent --> Router
|
|
66
|
+
Creds -.-> Pool
|
|
67
|
+
Router -->|opencode| Pool
|
|
68
|
+
Router -->|opencode-go| Pool
|
|
69
|
+
Pool --> Heatmap
|
|
70
|
+
Pool --> Transports
|
|
71
|
+
T1 & T2 & T3 & T4 --> Console & Go
|
|
32
72
|
```
|
|
33
73
|
|
|
34
|
-
|
|
74
|
+
## 功能全貌
|
|
75
|
+
|
|
76
|
+
- **4 种原生传输协议**,按模型自动优选:
|
|
77
|
+
- OpenAI Chat Completions(DeepSeek、GLM、Kimi、MiMo、LongCat、Hy3、Step 等)。
|
|
78
|
+
- Anthropic Messages(Claude、MiniMax、Qwen)。
|
|
79
|
+
- OpenAI Responses(GPT、Grok、Muse)。
|
|
80
|
+
- Google Generative AI(Gemini)。
|
|
81
|
+
- **完整数据类型流式支持**:文本、思考推理(reasoning)、流式工具调用(tool calls)、用量统计与 finish reasons。
|
|
82
|
+
- **多模态图像传输**:通过持久化附件服务,按协议原生转换与传输。
|
|
83
|
+
- **协议特定元数据重放**:
|
|
84
|
+
- Anthropic 思考签名(`thinking signatures`)。
|
|
85
|
+
- Google 历史函数调用中的 `thoughtSignature`。
|
|
86
|
+
- **多账户智能无缝轮换**:
|
|
87
|
+
- 在生成流开始前,遇到 401/403/429 或配额用尽响应(400/402/403/429)自动重试下一个账户。
|
|
88
|
+
- 无效密钥冷却 24 小时,超额冷却 10 分钟(或遵循 `Retry-After`),速率限制冷却 60 秒。
|
|
89
|
+
- 正在流式传输中的请求不会被打断,下一个请求会自动使用下一个可用密钥。
|
|
90
|
+
- **多账户额度与健康监控**:每行独立展示 5 小时、每周与每月使用情况,活跃账户驱动侧边栏与配额面板。
|
|
91
|
+
- **模型 ➔ 账户路由规则**:支持 Glob 通配符(如 `*free*`、`deepseek-*`)将模型优先绑定至特定账户,其他账户保留为备用。
|
|
92
|
+
- **本地时间请求热力图**:星期×小时交互式热力图,排除系统心跳与重复记录,仅保存 `{at, provider, model, purpose}` 元数据,不保存任何提示词与回复。
|
|
93
|
+
- **稳定的 `x-opencode-session` 标头**:根据 DSH 会话 ID 派生,确保重试与连续对话复用相同的网关路由键。
|
|
94
|
+
- **实时模型目录**:自动从 `/v1/models` 同步,并以 `models.dev` 生成文件作为回退。
|
|
95
|
+
- **Go 订阅额度估算**:每月美元额度以及 5 小时、每周和每月的估算请求次数。
|
|
96
|
+
- **`/opencode` 斜杠指令**:快速查询状态、配额、账户、模型及执行联通性测试。
|
|
97
|
+
- **Web UI 一键更新**:设置页集成版本比对与一键热更新按钮。
|
|
98
|
+
- **MCP 工具上限防护**:在大规模 MCP 工具集达到 128 工具限制时,优先保留 DSH 核心系统工具。
|
|
99
|
+
|
|
100
|
+
## 安装指南
|
|
101
|
+
|
|
102
|
+
从源码构建与打包:
|
|
35
103
|
|
|
36
104
|
```bash
|
|
37
|
-
|
|
105
|
+
npm install
|
|
106
|
+
npm run build
|
|
107
|
+
npm pack
|
|
38
108
|
```
|
|
39
109
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
## 配置
|
|
43
|
-
|
|
44
|
-
密钥来自 DSH 凭据服务、`OPENCODE_API_KEY` 或 `~/.local/share/opencode/auth.json`。Settings → OpenCode 可以粘贴、导入、清除密钥,并运行 **Test connection**。
|
|
45
|
-
|
|
46
|
-
默认账户优先使用;额外账户按顺序作为回退。账户可按 credential reference 配置。`modelAccountRules` 使用大小写不敏感的 `*` / `?` glob,把匹配模型优先路由到指定账户,其余账户仍作为回退。
|
|
110
|
+
通过**打包文件 (.tgz)**安装:
|
|
47
111
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
侧边栏配额卡默认关闭,可在 Settings → OpenCode 打开。点击卡片打开完整面板,显示账户、5h/周/月剩余额度、重置时间、用量分析和请求热力图。
|
|
53
|
-
|
|
54
|
-
热力图按本地时间聚合真实适配器请求,排除 `session-title` 和 `compaction`,精确重复记录会折叠。数据只写到 `~/.dsh/storages/dsh-opencode/requests.jsonl`,不包含提示词、响应或密钥。成本由 DSH 原生成本显示提供,本插件不添加第二个 composer 成本胶囊。
|
|
112
|
+
```bash
|
|
113
|
+
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.1.tgz
|
|
114
|
+
# DSH 桌面端:
|
|
115
|
+
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.1.tgz
|
|
55
116
|
|
|
56
|
-
|
|
117
|
+
# 或从 npm 安装已发布版本:
|
|
118
|
+
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.1
|
|
119
|
+
```
|
|
57
120
|
|
|
58
|
-
|
|
121
|
+
安装完成后重启 dsh 或 DSH 桌面端,在模型选择器中选择 **OpenCode** 或 **OpenCode Go** 即可。
|
|
122
|
+
|
|
123
|
+
## 配置示例
|
|
124
|
+
|
|
125
|
+
```yaml
|
|
126
|
+
- id: llm-opencode
|
|
127
|
+
name: "@goodandready/dsh-opencode-provider"
|
|
128
|
+
config:
|
|
129
|
+
apiKeyEnv: OPENCODE_API_KEY # 环境变量或 DSH 凭据名
|
|
130
|
+
# apiKey: oc_sk_... # 仅本地调试,严禁提交代码库
|
|
131
|
+
openCodeAuthEntry: opencode-go
|
|
132
|
+
requestTimeoutMs: 300000
|
|
133
|
+
streamIdleTimeoutMs: 300000
|
|
134
|
+
visibleModels: []
|
|
135
|
+
modelTransports: {}
|
|
136
|
+
accounts:
|
|
137
|
+
- label: Go #2
|
|
138
|
+
apiKeyEnv: OPENCODE_API_KEY_2
|
|
139
|
+
activeAccount: '' # 固定指定活跃账户名称
|
|
140
|
+
```
|
|
59
141
|
|
|
60
|
-
|
|
61
|
-
2. 有更新时显示徽章和 **Update now**。
|
|
62
|
-
3. 点击后通过正常的 `dsh plugin add` 安装精确版本;并发安装会被拒绝,不使用 `--force`,不会从 worktree 或 `file:` 安装。
|
|
63
|
-
4. 成功后提示重启 DSH。
|
|
142
|
+
### 密钥解析顺序
|
|
64
143
|
|
|
65
|
-
|
|
144
|
+
1. DSH 配置中的 `config.apiKey`。
|
|
145
|
+
2. DSH 凭据服务中由 `apiKeyEnv` 指定的凭据。
|
|
146
|
+
3. `~/.local/share/opencode/auth.json` 中的 `openCodeAuthEntry` 项(默认 `opencode-go`)。
|
|
147
|
+
4. 进程环境变量或 `$DSH_HOME/.env`。
|
|
66
148
|
|
|
67
|
-
|
|
149
|
+
### 模型到账户路由规则
|
|
68
150
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
-
|
|
72
|
-
|
|
151
|
+
```yaml
|
|
152
|
+
modelAccountRules:
|
|
153
|
+
- pattern: "*free*"
|
|
154
|
+
account: "free"
|
|
155
|
+
- pattern: "your-provider-*"
|
|
156
|
+
account: "go"
|
|
157
|
+
```
|
|
73
158
|
|
|
74
|
-
##
|
|
159
|
+
## 常用指令
|
|
75
160
|
|
|
76
|
-
|
|
161
|
+
```bash
|
|
162
|
+
/opencode status # 查看账户健康状态、配额与路由规则
|
|
163
|
+
/opencode usage # 查看当前配额窗口
|
|
164
|
+
/opencode accounts # 账户列表与 5h/周/月消耗
|
|
165
|
+
/opencode models # 模型列表与 Go 方案限制
|
|
166
|
+
/opencode rules # 模型到账户规则
|
|
167
|
+
/opencode test [model] # 发送最小化测试请求
|
|
168
|
+
```
|
|
77
169
|
|
|
78
|
-
##
|
|
170
|
+
## 安全规范
|
|
79
171
|
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
- 自动更新需要运行时提供 DSH CLI;否则使用 DSH 插件管理器。
|
|
83
|
-
- 匿名免费通道(`Bearer public` + CLI 伪装)未实现。
|
|
172
|
+
- 写入与配置路由均要求 Loopback(`127.0.0.1` / `::1`)与同源检查,防御 DNS 重新绑定。
|
|
173
|
+
- 密钥冷却状态通过 SHA-256 哈希指纹持久化,本地日志不保存提示词与明文密钥。
|
|
84
174
|
|
|
85
|
-
##
|
|
175
|
+
## 许可证
|
|
86
176
|
|
|
87
|
-
MIT
|
|
177
|
+
MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)。详见 [LICENSE](./LICENSE) 与 [NOTICE](./NOTICE)。
|
|
178
|
+
|
package/cordis.patch.yml
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# dsh-opencode-provider bundle layer.
|
|
2
|
-
# Registers the `opencode` (Console/Zen) and `opencode-go` (Go subscription)
|
|
3
|
-
# provider routes. All fields are optional — schema defaults apply.
|
|
4
|
-
#
|
|
5
|
-
# `name` must be the FULL package specifier as installed in the profile's
|
|
6
|
-
# node_modules. Quote it: an unquoted YAML scalar starting with `@` fails.
|
|
7
|
-
- insert:
|
|
8
|
-
- id: llm-opencode
|
|
9
|
-
name: "@goodandready/dsh-opencode-provider"
|
|
10
|
-
config:
|
|
11
|
-
apiKeyEnv: OPENCODE_API_KEY
|
|
1
|
+
# dsh-opencode-provider bundle layer.
|
|
2
|
+
# Registers the `opencode` (Console/Zen) and `opencode-go` (Go subscription)
|
|
3
|
+
# provider routes. All fields are optional — schema defaults apply.
|
|
4
|
+
#
|
|
5
|
+
# `name` must be the FULL package specifier as installed in the profile's
|
|
6
|
+
# node_modules. Quote it: an unquoted YAML scalar starting with `@` fails.
|
|
7
|
+
- insert:
|
|
8
|
+
- id: llm-opencode
|
|
9
|
+
name: "@goodandready/dsh-opencode-provider"
|
|
10
|
+
config:
|
|
11
|
+
apiKeyEnv: OPENCODE_API_KEY
|
package/lib/client.js
CHANGED
|
@@ -1311,96 +1311,6 @@ window.__ModuleLoader__.load({
|
|
|
1311
1311
|
});
|
|
1312
1312
|
}
|
|
1313
1313
|
//#endregion
|
|
1314
|
-
//#region src/client/provider-card.tsx
|
|
1315
|
-
/**
|
|
1316
|
-
* The provider card on Settings → Models. It is deliberately a narrow key
|
|
1317
|
-
* surface: the full connection, account and model controls stay on the
|
|
1318
|
-
* dedicated Settings → OpenCode page, while first-run key entry is available
|
|
1319
|
-
* right where the Models page lists this provider.
|
|
1320
|
-
*/
|
|
1321
|
-
function OpenCodeProviderCard(props) {
|
|
1322
|
-
const { t } = props;
|
|
1323
|
-
const [state, setState] = (0, react.useState)(null);
|
|
1324
|
-
const [draft, setDraft] = (0, react.useState)("");
|
|
1325
|
-
const [notice, setNotice] = (0, react.useState)(null);
|
|
1326
|
-
const [error, setError] = (0, react.useState)(null);
|
|
1327
|
-
const [saving, setSaving] = (0, react.useState)(false);
|
|
1328
|
-
const load = () => {
|
|
1329
|
-
fetchState().then((next) => {
|
|
1330
|
-
setState(next);
|
|
1331
|
-
setError(null);
|
|
1332
|
-
}).catch((caught) => setError(caught instanceof Error ? caught.message : t("failed")));
|
|
1333
|
-
};
|
|
1334
|
-
(0, react.useEffect)(() => {
|
|
1335
|
-
let cancelled = false;
|
|
1336
|
-
fetchState().then((next) => {
|
|
1337
|
-
if (cancelled) return;
|
|
1338
|
-
setState(next);
|
|
1339
|
-
setError(null);
|
|
1340
|
-
}).catch((caught) => {
|
|
1341
|
-
if (!cancelled) setError(caught instanceof Error ? caught.message : t("failed"));
|
|
1342
|
-
});
|
|
1343
|
-
return () => {
|
|
1344
|
-
cancelled = true;
|
|
1345
|
-
};
|
|
1346
|
-
}, [t]);
|
|
1347
|
-
const configured = state?.key.configured === true;
|
|
1348
|
-
const ref = state?.config.apiKeyEnv ?? "OPENCODE_API_KEY";
|
|
1349
|
-
const save = () => {
|
|
1350
|
-
if (draft.trim() === "") return;
|
|
1351
|
-
setSaving(true);
|
|
1352
|
-
setNotice(null);
|
|
1353
|
-
setError(null);
|
|
1354
|
-
saveKey(ref, draft.trim()).then(() => {
|
|
1355
|
-
setDraft("");
|
|
1356
|
-
setNotice(t("saved"));
|
|
1357
|
-
load();
|
|
1358
|
-
}).catch((caught) => setError(caught instanceof Error ? caught.message : t("failed"))).finally(() => setSaving(false));
|
|
1359
|
-
};
|
|
1360
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1361
|
-
className: "oc-card",
|
|
1362
|
-
children: [
|
|
1363
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h3", {
|
|
1364
|
-
className: "oc-card-title",
|
|
1365
|
-
children: [t("title"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1366
|
-
className: `oc-badge ${configured ? "oc-badge-ok" : "oc-badge-warn"}`,
|
|
1367
|
-
children: configured ? t("keyConfigured") : t("keyMissing")
|
|
1368
|
-
})]
|
|
1369
|
-
}),
|
|
1370
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1371
|
-
className: "oc-hint",
|
|
1372
|
-
children: t("keyHint")
|
|
1373
|
-
}),
|
|
1374
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1375
|
-
className: "oc-row",
|
|
1376
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1377
|
-
className: "oc-input",
|
|
1378
|
-
style: { flex: "1 1 220px" },
|
|
1379
|
-
type: "password",
|
|
1380
|
-
autoComplete: "off",
|
|
1381
|
-
placeholder: t("pasteKey"),
|
|
1382
|
-
value: draft,
|
|
1383
|
-
onChange: (event) => setDraft(event.target.value)
|
|
1384
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1385
|
-
type: "button",
|
|
1386
|
-
className: "oc-button oc-button-primary",
|
|
1387
|
-
disabled: saving || draft.trim() === "",
|
|
1388
|
-
onClick: save,
|
|
1389
|
-
children: saving ? t("saving") : t("saveKey")
|
|
1390
|
-
})]
|
|
1391
|
-
}),
|
|
1392
|
-
notice === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1393
|
-
className: "oc-notice",
|
|
1394
|
-
children: notice
|
|
1395
|
-
}),
|
|
1396
|
-
error === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1397
|
-
className: "oc-error",
|
|
1398
|
-
children: error
|
|
1399
|
-
})
|
|
1400
|
-
]
|
|
1401
|
-
});
|
|
1402
|
-
}
|
|
1403
|
-
//#endregion
|
|
1404
1314
|
//#region src/client/settings-draft.ts
|
|
1405
1315
|
function draftOf(state) {
|
|
1406
1316
|
const allIds = state.models.map((model) => model.id);
|
|
@@ -2707,11 +2617,6 @@ window.__ModuleLoader__.load({
|
|
|
2707
2617
|
label: () => t("nav"),
|
|
2708
2618
|
locale: NS
|
|
2709
2619
|
}, OpenCodeSettingsPage));
|
|
2710
|
-
ctx.slots.inject("settings.models.provider-card", () => slots.register({
|
|
2711
|
-
name: "settings.models.provider-card",
|
|
2712
|
-
key: "llm-opencode",
|
|
2713
|
-
locale: NS
|
|
2714
|
-
}, OpenCodeProviderCard));
|
|
2715
2620
|
ctx.slots.inject("main", () => slots.register({
|
|
2716
2621
|
name: "main",
|
|
2717
2622
|
key: "opencode-panel",
|