dsh-opencode-routing 2.2.0 → 2.3.0
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/CONTRIBUTING.md +46 -0
- package/README.ja.md +111 -0
- package/README.ko.md +154 -0
- package/README.md +98 -67
- package/README.ru.md +166 -0
- package/README.zh-CN.md +141 -0
- package/README.zh-TW.md +111 -0
- package/client.js +1221 -990
- package/package.json +11 -1
- package/router.js +12 -0
- package/test.mjs +48 -0
- package/usage.js +45 -17
package/README.ru.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# dsh-opencode-routing
|
|
2
|
+
|
|
3
|
+
[English](README.md) · [简体中文](README.zh-CN.md) · [繁體中文](README.zh-TW.md) · [日本語](README.ja.md) · [한국어](README.ko.md) · **Русский**
|
|
4
|
+
|
|
5
|
+
Плагин для [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`), который объединяет два
|
|
6
|
+
прежних плагина в один пакет: **пул аккаунтов OpenCode с автоматическим переключением A→B и маршрутизацией по
|
|
7
|
+
квоте**, а также **панель использования по аккаунтам и моделям**. И то и другое живёт на одной странице Settings —
|
|
8
|
+
«Аккаунты и использование OpenCode».
|
|
9
|
+
|
|
10
|
+
- **Клиент**: одна `settings.section`, обновляемая каждые 60 с; аккаунты можно добавлять, редактировать,
|
|
11
|
+
переставлять и проверять; карточки моделей несут теги возможностей; обе таблицы прокручиваются перетаскиванием;
|
|
12
|
+
интерфейс говорит на 10 языках (язык браузера имеет приоритет).
|
|
13
|
+
- **Хост**: два эндпоинта — `GET /opencode-usage.json` (использование, `?refresh=1` принудительно обновляет) и
|
|
14
|
+
`GET|POST /opencode-quota-router.json` (пул и маршрутизация; действия ниже).
|
|
15
|
+
|
|
16
|
+
## Установка
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm i dsh-opencode-routing
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Пакет поставляется со своим `cordis.patch.yml`: он объявляет провайдеров `opencode-go` / `opencode-free` и
|
|
23
|
+
**единственную** вставку (`id: opencode-router`). Подобъекты `usage:` и `router:` хранят настройки каждой половины
|
|
24
|
+
(у обеих есть `path`, поэтому они не могут делить одно плоское пространство имён).
|
|
25
|
+
|
|
26
|
+
```yaml
|
|
27
|
+
- insert:
|
|
28
|
+
- id: opencode-router
|
|
29
|
+
name: 'dsh-opencode-routing'
|
|
30
|
+
config:
|
|
31
|
+
usageUrl: https://opencode.ai/inference/go/v1/usage
|
|
32
|
+
apiKeyRef: OPENCODE_API_KEY
|
|
33
|
+
usage:
|
|
34
|
+
path: /opencode-usage
|
|
35
|
+
plan: go
|
|
36
|
+
router:
|
|
37
|
+
path: /opencode-quota-router
|
|
38
|
+
routes:
|
|
39
|
+
- primary: { provider: opencode-go, model: deepseek-v4.1-flash }
|
|
40
|
+
fallback: { provider: deepseek-account, model: deepseek-flash }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Ключи верхнего уровня применяются к обеим половинам (`usageUrl`, `apiKeyRef`, `probeIntervalMs`, …); ключи внутри
|
|
44
|
+
подобъекта переопределяют только свою половину.
|
|
45
|
+
|
|
46
|
+
## Пул аккаунтов и переключение при отказе
|
|
47
|
+
|
|
48
|
+
Порядок пула — это порядок использования. В любой момент «текущим» является ровно один аккаунт, и его ключ
|
|
49
|
+
зеркалируется в ссылку на учётные данные, которую читает провайдер (по умолчанию `OPENCODE_API_KEY`). Поэтому
|
|
50
|
+
переключение аккаунтов не требует изменения конфигурации провайдера: `resolveApiKey` выполняется после хука
|
|
51
|
+
`agent/request`, при каждом потоковом вызове.
|
|
52
|
+
|
|
53
|
+
- **A исчерпан — B подхватывает**: при `QUOTA` / `ACCOUNT_QUOTA` / 429, когда официальный лимит подтверждённо
|
|
54
|
+
израсходован, или при ошибке класса аутентификации (`AUTH`, `INVALID_CREDENTIAL`, `MISSING_CREDENTIAL`,
|
|
55
|
+
`ACCOUNT_SIGN_IN_REQUIRED`, `CLIENT_UPGRADE_REQUIRED`) текущий аккаунт помечается непригодным (до сброса окна, а
|
|
56
|
+
при сбое аутентификации — на 30 минут), маршрутизация переходит к следующему включённому аккаунту и **повторяет
|
|
57
|
+
тот же шаг** — ход никогда не деградирует.
|
|
58
|
+
- **Карантин одной модели**: когда лимит исчерпан только у одной модели, а в пуле ещё есть бюджет, карантин
|
|
59
|
+
применяется только к этой модели (по умолчанию 30 минут); все остальные модели продолжают работать по плану.
|
|
60
|
+
- **Пул исчерпан целиком**: только тогда маршрут откатывается на свой `fallback` (по умолчанию — аккаунт
|
|
61
|
+
DeepSeek), с пробой `GET /inference/go/v1/usage`, не тратящей токены, каждые 30 минут (или при сбросе
|
|
62
|
+
скользящего окна), которая автоматически возвращает на план.
|
|
63
|
+
- **Сброс**: «Сбросить метки блокировки» в Settings очищает все метки исчерпания и аутентификации и немедленно
|
|
64
|
+
возвращает в пул. Аккаунты и ключи не затрагиваются.
|
|
65
|
+
|
|
66
|
+
## Панель использования
|
|
67
|
+
|
|
68
|
+
Официальный эндпоинт `/inference/go/v1/usage` возвращает только округлённые проценты по скользящему / недельному /
|
|
69
|
+
месячному окну и **без разбивки по моделям**, тогда как план тарифицирует каждую модель отдельно (5 ч = 20 %
|
|
70
|
+
месячного лимита, неделя = 50 %, месяц = 100 %). Поэтому страница показывает два набора чисел:
|
|
71
|
+
|
|
72
|
+
- **Официальные проценты** (одна строка на аккаунт): прямо из API — реальное использование аккаунта, включая другие
|
|
73
|
+
клиенты и другие машины.
|
|
74
|
+
- **Деньги по моделям** (таблица моделей): локальная оценка. Она пересканирует `$DSH_HOME/sessions` за последние
|
|
75
|
+
40 дней и учитывает только события `assistant/message`, у которых provider начинается с `opencode`, по публичным
|
|
76
|
+
тарифам (включая 2× пиковое окно DeepSeek). У использования из других инструментов нет локальной записи.
|
|
77
|
+
|
|
78
|
+
«Посмотреть использование» открывает диалог по аккаунту с тремя окнами, их временами сброса и результатом пробы
|
|
79
|
+
(проверяется только этот аккаунт, модели при этом не переключаются).
|
|
80
|
+
|
|
81
|
+
«Изменить ключ» получает текущий ключ аккаунта из `GET /opencode-quota-router.json?key=<account id>` и заполняет
|
|
82
|
+
поле ввода (эндпоинт только локальный; снимок списка всегда несёт лишь маску). Названия моделей следуют регистру
|
|
83
|
+
вендора (`deepseek-v4.1-flash` → `DeepSeek V4.1 Flash`); вендоры с опубликованным фирменным знаком отображаются
|
|
84
|
+
встроенным SVG, остальные — буквенным бейджем. «Следующая проба» — настоящий таймер (по умолчанию 30 минут), и
|
|
85
|
+
страница перерисовывается каждую секунду, поэтому он идёт обратным отсчётом; открытие страницы только читает
|
|
86
|
+
снимок и никогда не запускает пробу.
|
|
87
|
+
|
|
88
|
+
«Что сейчас выгоднее» выводится из выгоды подписки: Go берёт $10 за $100 лимита по прайс-листу (≈ ¥710), поэтому
|
|
89
|
+
OpenCode всегда стоит 10 % официальной цены — на 90 % дешевле. Реальные данные никогда не в пользу официального
|
|
90
|
+
API; кнопка в углу карточки переключает на демонстрационные данные, чтобы увидеть, как выглядел бы такой случай.
|
|
91
|
+
|
|
92
|
+
Столбец возможностей берётся из того же каталожного эндпоинта (`/inference/go/v1/models`): `modalities.input` и
|
|
93
|
+
`capabilities` отображаются как текст / изображение / видео / аудио / PDF, вызовы инструментов, рассуждения, плюс
|
|
94
|
+
длина контекста. Модели с нулевой ценой показывают «безлимит».
|
|
95
|
+
|
|
96
|
+
### Пиковые / непиковые часы и праздники
|
|
97
|
+
|
|
98
|
+
Непиковое окно DeepSeek — всё время вне пекинских понедельника–пятницы 09:00–12:00 и 14:00–18:00 (включая
|
|
99
|
+
обеденный перерыв, ночи, выходные и **официальные государственные праздники**) ровно по половине пиковой цены.
|
|
100
|
+
OpenCode считает пиковыми только UTC понедельник–пятницу 01–04 и 06–10 и **игнорирует китайские государственные
|
|
101
|
+
праздники** (anomalyco/opencode#53071, мейнтейнер: *"Go pricing follows our own schedule, not DeepSeek's holiday
|
|
102
|
+
calendar."*).
|
|
103
|
+
|
|
104
|
+
Плагин поставляет таблицу китайских государственных праздников на 2026–2031 годы. Она применяется к официальной
|
|
105
|
+
стороне только при `holidayAware: true` (`usage.holidayAware`); по умолчанию выключено, поскольку OpenCode в эти дни
|
|
106
|
+
продолжает брать пиковую цену. Таблица живёт в `usage.js` как `HOLIDAYS`, а `isChinaHoliday()` можно вызывать
|
|
107
|
+
отдельно.
|
|
108
|
+
|
|
109
|
+
Цену подписки и её лимит можно переопределить через `usage.planPriceUsd` / `usage.planAllowanceUsd`; скидка на
|
|
110
|
+
странице выводится из этих двух чисел, поэтому изменение плана со стороны OpenCode — это правка конфигурации, а не
|
|
111
|
+
патч.
|
|
112
|
+
|
|
113
|
+
## API настроек
|
|
114
|
+
|
|
115
|
+
`GET /opencode-quota-router.json` возвращает `{mode, reason, active, accounts[], quarantine[], routes[],
|
|
116
|
+
lastFailure}`; ключи аккаунтов всегда маскируются (`hint`). `POST` на тот же путь с `{action, ...}`:
|
|
117
|
+
|
|
118
|
+
| action | аргументы | эффект |
|
|
119
|
+
| --- | --- | --- |
|
|
120
|
+
| `add` | `label?`, `key` | создать аккаунт (ключ ≥ 8 символов), сохранить и проверить |
|
|
121
|
+
| `update` | `id`, `label?` / `enabled?` / `key?` | переименовать, отключить/включить, сменить ключ |
|
|
122
|
+
| `remove` | `id` | удалить аккаунт и его ключ (один всегда остаётся) |
|
|
123
|
+
| `activate` | `id` | переключиться на него сейчас |
|
|
124
|
+
| `move` | `id`, `direction: up\|down` | изменить порядок |
|
|
125
|
+
| `probe` | `id?` | проверить все аккаунты или только один |
|
|
126
|
+
| `reset` | — | очистить метки исчерпания/аутентификации и вернуться в пул |
|
|
127
|
+
|
|
128
|
+
`GET /opencode-usage.json` возвращает `{plan, configuredPlan, detectedPlan, holidayAware, peak, usage,
|
|
129
|
+
accounts[], activeAccount, models[], totals}`; `?refresh=1` сначала принудительно запрашивает свежий официальный
|
|
130
|
+
снимок.
|
|
131
|
+
|
|
132
|
+
## Языки
|
|
133
|
+
|
|
134
|
+
Строки интерфейса лежат в `DICT` в `client.js`: `zh` (исходный), `zh-tw`, `en`, а также `es` / `fr` / `de` / `pt` /
|
|
135
|
+
`ja` / `ko` / `ru` (встраиваются из файлов переводов скриптом `scripts/merge-i18n.mjs`; отсутствующий ключ
|
|
136
|
+
откатывается к `en`, затем к самому ключу). `localeOf()` сопоставляет префикс языка браузера и различает
|
|
137
|
+
упрощённый и традиционный китайский.
|
|
138
|
+
|
|
139
|
+
## Тесты
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
node test.mjs
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Квота модели не требуется: поддельный контекст, поддельное хранилище учётных данных, поддельный webServer и
|
|
146
|
+
локальный эндпоинт использования, отвечающий по каждому API-ключу. Покрытие: составное монтирование, принятие
|
|
147
|
+
аккаунта, карантин одной модели, переключение A→B с зеркалированием ключей, проба по аккаунту, откат на весь пул,
|
|
148
|
+
сброс, валидация действий настроек, представление использования (теги возможностей, безлимитные модели, расчёт
|
|
149
|
+
пик/непик, строки биллинга) и деградация до одного аккаунта.
|
|
150
|
+
|
|
151
|
+
## Связь со старыми пакетами
|
|
152
|
+
|
|
153
|
+
`dsh-opencode-quota-router` и `dsh-opencode-usage-settings` — его предшественники; всё, что они умели, объединено
|
|
154
|
+
здесь. Оба помечены на npm как deprecated и указывают на этот пакет. Репозитории:
|
|
155
|
+
[`2869949486/dsh-opencode-routing`](https://github.com/2869949486/dsh-opencode-routing) (текущий),
|
|
156
|
+
[`dsh-opencode-quota-router`](https://github.com/2869949486/dsh-opencode-quota-router),
|
|
157
|
+
[`dsh-opencode-usage`](https://github.com/2869949486/dsh-opencode-usage).
|
|
158
|
+
|
|
159
|
+
## Участие
|
|
160
|
+
|
|
161
|
+
Issues и pull requests приветствуются — см. [CONTRIBUTING.md](CONTRIBUTING.md). Перед открытием PR запустите
|
|
162
|
+
`node test.mjs`.
|
|
163
|
+
|
|
164
|
+
## Лицензия
|
|
165
|
+
|
|
166
|
+
MIT — см. [LICENSE](LICENSE).
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# dsh-opencode-routing
|
|
2
|
+
|
|
3
|
+
[English](README.md) · **简体中文** · [繁體中文](README.zh-TW.md) · [日本語](README.ja.md) · [한국어](README.ko.md) · [Русский](README.ru.md)
|
|
4
|
+
|
|
5
|
+
一个 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`)插件,把原先分开的两个插件合成一个:
|
|
6
|
+
**OpenCode 账户池 + 额度路由** 和 **用量看板** 现在装在同一个包里,设置里也只有一个页面
|
|
7
|
+
「OpenCode 账户与用量」。
|
|
8
|
+
|
|
9
|
+
- **客户端**:`settings.section` 槽位,60 秒自动刷新;账户池可增删改、模型表带能力标签、两张表都能按住左右拖动;界面支持 10 种语言(跟随浏览器语言)。
|
|
10
|
+
- **Host**:两个只读/管理接口 —— `GET /opencode-usage.json`(用量,`?refresh=1` 强制刷新)与 `GET|POST /opencode-quota-router.json`(账户池与路由,动作见下)。
|
|
11
|
+
|
|
12
|
+
## 安装
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm i dsh-opencode-routing
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
包自带 `cordis.patch.yml`:里面同时声明 `opencode-go` / `opencode-free` 两个 provider 和**一条** insert
|
|
19
|
+
(`id: opencode-router`)。`usage:` 与 `router:` 两个子对象分别对应看板与路由各自的配置项(两边都有 `path`,
|
|
20
|
+
所以必须分开写)。
|
|
21
|
+
|
|
22
|
+
```yaml
|
|
23
|
+
- insert:
|
|
24
|
+
- id: opencode-router
|
|
25
|
+
name: 'dsh-opencode-routing'
|
|
26
|
+
config:
|
|
27
|
+
usageUrl: https://opencode.ai/inference/go/v1/usage
|
|
28
|
+
apiKeyRef: OPENCODE_API_KEY
|
|
29
|
+
usage:
|
|
30
|
+
path: /opencode-usage
|
|
31
|
+
plan: go
|
|
32
|
+
router:
|
|
33
|
+
path: /opencode-quota-router
|
|
34
|
+
routes:
|
|
35
|
+
- primary: { provider: opencode-go, model: deepseek-v4.1-flash }
|
|
36
|
+
fallback: { provider: deepseek-account, model: deepseek-flash }
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
顶层键对两半都生效(`usageUrl`、`apiKeyRef`、`probeIntervalMs` 之类),子对象里的键只覆盖自己那一半。
|
|
40
|
+
|
|
41
|
+
## 账户池与轮替
|
|
42
|
+
|
|
43
|
+
账户顺序就是使用顺序。任何时候只有一个「当前账户」,它的密钥会被镜像到 provider 读取的那个凭据引用
|
|
44
|
+
(默认 `OPENCODE_API_KEY`),因此切换账户不需要改任何 provider 配置:`resolveApiKey` 是在
|
|
45
|
+
`agent/request` 钩子之后、每次流式请求时现取的。
|
|
46
|
+
|
|
47
|
+
- **A 用完自动用 B**:出现 `QUOTA` / `ACCOUNT_QUOTA` / 429 且官方额度确认耗尽,或出现鉴权类错误
|
|
48
|
+
(`AUTH`、`INVALID_CREDENTIAL`、`MISSING_CREDENTIAL`、`ACCOUNT_SIGN_IN_REQUIRED`、`CLIENT_UPGRADE_REQUIRED`)时,
|
|
49
|
+
当前账户被标记为不可用(额度用尽到窗口重置,鉴权失败 30 分钟),路由切到下一个启用账户并在**同一步重试**,
|
|
50
|
+
不会把整轮对话降级。
|
|
51
|
+
- **单模型隔离**:只有一个模型报耗尽、池里还有额度时,只隔离这个模型(默认 30 分钟),其它模型继续走套餐。
|
|
52
|
+
- **整池耗尽**:才把这条路整体回退到 `fallback`(默认 DeepSeek 账户),并每 30 分钟(或 rolling 窗口重置时刻)
|
|
53
|
+
用零 token 的 `GET /inference/go/v1/usage` 探测,恢复后自动回到套餐。
|
|
54
|
+
- **重置**:设置页的「重置封禁标记」清空所有「额度用尽 / 鉴权失败」标记并立刻回到账户池,不动账户与密钥。
|
|
55
|
+
|
|
56
|
+
## 用量看板
|
|
57
|
+
|
|
58
|
+
官方 `/inference/go/v1/usage` 只给一组四舍五入的 rolling / weekly / monthly 百分比,**没有按模型明细**;
|
|
59
|
+
而套餐是按模型各自计额度(5 小时 = 月额度 20%、周 = 50%、月 = 100%)。所以页面有两套数:
|
|
60
|
+
|
|
61
|
+
- **官方百分比**(每个账户一行):来自接口,是账户真实用量,别的客户端、别的机器用了也算进去。
|
|
62
|
+
- **按模型金额**(模型表):本机估算 —— 回扫 `$DSH_HOME/sessions` 近 40 天的会话日志,只统计 provider 以
|
|
63
|
+
`opencode` 开头的 `assistant/message`,按公开单价(含 DeepSeek 峰谷 2×)折算。别的工具用掉的额度没有本地记录。
|
|
64
|
+
|
|
65
|
+
「查看用量」按钮按账户弹窗展示三个窗口的百分比、重置时间与探测结果(只探测该账户,不切换模型)。
|
|
66
|
+
|
|
67
|
+
「编辑密钥」直接用 `GET /opencode-quota-router.json?key=<账户 id>` 取回该账户的当前密钥并预填输入框
|
|
68
|
+
(本机端点;列表快照始终只给掩码)。模型名按厂商习惯大写(`deepseek-v4.1-flash` → `DeepSeek V4.1 Flash`),
|
|
69
|
+
能取到品牌图标的厂商显示 SVG 图标,取不到的保留字母徽标。「下次探测」是真实的定时器(默认 30 分钟一次),
|
|
70
|
+
页面每秒重绘一次,所以会像倒计时一样走;打开页面本身只读快照,不会触发探测。
|
|
71
|
+
|
|
72
|
+
「此刻用哪个更划算」按套餐杠杆算:Go 付 $10 拿到 $100 官方价额度(≈¥710),所以走 OpenCode 恒为官方价的
|
|
73
|
+
10%(便宜 90%)。真实数据里不会出现官方更便宜,卡片右下角的按钮可以切到示例数据,看那种情况长什么样。
|
|
74
|
+
|
|
75
|
+
表里每个模型拆成三行:**缓存命中输入 / 缓存未命中输入 / 输出**(按百万 token 折算),缓存命中那行高亮,
|
|
76
|
+
因为大多数请求落在这条线上。官方单价(人民币/百万 token,低谷价):
|
|
77
|
+
Flash 0.02 / 1 / 4,Pro 0.15 / 4.5 / 13.5;高峰期正好翻倍。
|
|
78
|
+
|
|
79
|
+
套餐价与额度可以覆盖:`usage.planPriceUsd`(默认 $10)与 `usage.planAllowanceUsd`(默认按 `LIMITS` 的
|
|
80
|
+
Flash 月额度 $100)。页面上的折扣是从这两个数推出来的 —— OpenCode 哪天改价或改额度,改配置即可,
|
|
81
|
+
不需要改代码。
|
|
82
|
+
|
|
83
|
+
模型表的「能力」列来自同一个模型目录接口(`/inference/go/v1/models`)的
|
|
84
|
+
`modalities.input` 与 `capabilities`,标出 文本 / 图像 / 视频 / 音频 / PDF、工具调用、推理,以及上下文长度;
|
|
85
|
+
价格为 0 的模型显示「无限」。
|
|
86
|
+
|
|
87
|
+
### 峰谷与节假日
|
|
88
|
+
|
|
89
|
+
官方 API 的空闲时段是北京周一至周五 09:00–12:00、14:00–18:00 之外(含午休、夜间、周末、**法定节假日**),
|
|
90
|
+
价格正好是峰价一半。OpenCode 只照 UTC 周一至周五 01–04、06–10 两个窗口算峰,**不认中国法定节假日**
|
|
91
|
+
(anomalyco/opencode#53071 维护者原话:*"Go pricing follows our own schedule, not DeepSeek's holiday calendar."*)。
|
|
92
|
+
|
|
93
|
+
插件里带 2026–2031 的中国法定节假日表,只有把 `holidayAware: true` 写进配置(`usage.holidayAware`)才会计入
|
|
94
|
+
官方侧折算;默认关闭,因为 OpenCode 侧照旧按峰计费。表在 `usage.js` 的 `HOLIDAYS`,`isChinaHoliday()`
|
|
95
|
+
可单独调用。
|
|
96
|
+
|
|
97
|
+
## 设置接口
|
|
98
|
+
|
|
99
|
+
`GET /opencode-quota-router.json` 返回 `{mode, reason, active, accounts[], quarantine[], routes[], lastFailure}`,
|
|
100
|
+
账户里的密钥永远是掩码(`hint`)。`POST` 同一路径,body `{action, ...}`:
|
|
101
|
+
|
|
102
|
+
| action | 参数 | 作用 |
|
|
103
|
+
| --- | --- | --- |
|
|
104
|
+
| `add` | `label?`, `key` | 新建账户(密钥≥8 字符),写入凭据库并探测 |
|
|
105
|
+
| `update` | `id`, `label?` / `enabled?` / `key?` | 改名、停用/启用、换密钥 |
|
|
106
|
+
| `remove` | `id` | 删除账户并清掉它的密钥(至少留一个) |
|
|
107
|
+
| `activate` | `id` | 立刻切到该账户 |
|
|
108
|
+
| `move` | `id`, `direction: up\|down` | 调整顺序 |
|
|
109
|
+
| `probe` | `id?` | 探测全部账户;带 `id` 只探测这一个 |
|
|
110
|
+
| `reset` | — | 清空封禁标记,回到账户池 |
|
|
111
|
+
|
|
112
|
+
`GET /opencode-usage.json` 返回 `{plan, configuredPlan, detectedPlan, holidayAware, peak, usage, accounts[],
|
|
113
|
+
activeAccount, models[], totals}`;`?refresh=1` 先强制刷新官方快照再返回。
|
|
114
|
+
|
|
115
|
+
## 多语言
|
|
116
|
+
|
|
117
|
+
界面文案在 `client.js` 的 `DICT` 里:`zh`(源)、`zh-tw`、`en`,以及 `es` / `fr` / `de` / `pt` / `ja` / `ko` / `ru`
|
|
118
|
+
(由 `_merge_i18n.mjs` 从翻译文件内联,缺键回退到 `en`,再回退到键名)。`localeOf()` 按浏览器语言前缀匹配,
|
|
119
|
+
中文区分简繁。
|
|
120
|
+
|
|
121
|
+
## 测试
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
node test.mjs
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
不需要任何模型额度:假 context、假凭据库、假 webServer,本地起一个 usage 端点按 API key 返回不同百分比,
|
|
128
|
+
覆盖组合插件挂载、账户收养、单模型隔离、A→B 切换与密钥镜像、按账户探测、整池耗尽回退、重置、设置动作校验、
|
|
129
|
+
用量视图(能力标签、无限额度、峰谷换算)与单账户退化。
|
|
130
|
+
|
|
131
|
+
## 与旧包的关系
|
|
132
|
+
|
|
133
|
+
`dsh-opencode-quota-router` 与 `dsh-opencode-usage-settings` 是它的前身,功能已全部并入本包;
|
|
134
|
+
旧包在 npm 上标记为 deprecated,指向这里。仓库分别是
|
|
135
|
+
[`2869949486/dsh-opencode-routing`](https://github.com/2869949486/dsh-opencode-routing)(当前)、
|
|
136
|
+
[`dsh-opencode-quota-router`](https://github.com/2869949486/dsh-opencode-quota-router)、
|
|
137
|
+
[`dsh-opencode-usage`](https://github.com/2869949486/dsh-opencode-usage)。
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
MIT
|
package/README.zh-TW.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# dsh-opencode-routing
|
|
2
|
+
|
|
3
|
+
[English](README.md) · [简体中文](README.zh-CN.md) · **繁體中文** · [日本語](README.ja.md) · [한국어](README.ko.md) · [Русский](README.ru.md)
|
|
4
|
+
|
|
5
|
+
一個 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`)外掛,把兩個較早的外掛合併成單一套件:具備 **A→B 自動容錯切換與額度路由的 OpenCode 帳戶池**,加上**依帳戶/依模型的使用量儀表板**。兩者都位於同一個設定頁面「OpenCode 帳戶與用量」上。
|
|
6
|
+
|
|
7
|
+
- **用戶端**:一個 `settings.section`,每 60 秒刷新;帳戶可以新增、編輯、重排與探測;模型卡片帶有能力標籤;兩張表格都可拖曳平移;介面說 10 種語言(瀏覽器語言優先)。
|
|
8
|
+
- **宿主端**:兩個端點 —— `GET /opencode-usage.json`(用量,`?refresh=1` 強制刷新)與
|
|
9
|
+
`GET|POST /opencode-quota-router.json`(池與路由;動作見下)。
|
|
10
|
+
|
|
11
|
+
## 安裝
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm i dsh-opencode-routing
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
套件自帶 `cordis.patch.yml`:它宣告 `opencode-go` / `opencode-free` 兩個 provider,以及**單一** insert(`id: opencode-router`)。`usage:` 與 `router:` 兩個子物件分別裝載各自那一半的設定(兩半都擁有自己的 `path`,因此無法共用同一個平面命名空間)。
|
|
18
|
+
|
|
19
|
+
```yaml
|
|
20
|
+
- insert:
|
|
21
|
+
- id: opencode-router
|
|
22
|
+
name: 'dsh-opencode-routing'
|
|
23
|
+
config:
|
|
24
|
+
usageUrl: https://opencode.ai/inference/go/v1/usage
|
|
25
|
+
apiKeyRef: OPENCODE_API_KEY
|
|
26
|
+
usage:
|
|
27
|
+
path: /opencode-usage
|
|
28
|
+
plan: go
|
|
29
|
+
router:
|
|
30
|
+
path: /opencode-quota-router
|
|
31
|
+
routes:
|
|
32
|
+
- primary: { provider: opencode-go, model: deepseek-v4.1-flash }
|
|
33
|
+
fallback: { provider: deepseek-account, model: deepseek-flash }
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
頂層鍵同時套用到兩半(`usageUrl`、`apiKeyRef`、`probeIntervalMs` 等);子物件內的鍵只覆寫該半邊。
|
|
37
|
+
|
|
38
|
+
## 帳戶池與容錯切換
|
|
39
|
+
|
|
40
|
+
池的順序就是使用順序。任一時刻只有一個帳戶是「當前」帳戶,它的金鑰會被鏡射到 provider 讀取的憑證參考(預設 `OPENCODE_API_KEY`)。因此切換帳戶不需要更改 provider 設定:`resolveApiKey` 在 `agent/request` 勾子之後、每一次串流呼叫時執行。
|
|
41
|
+
|
|
42
|
+
- **A 用盡,B 接手**:當官方額度確認用完時遇到 `QUOTA` / `ACCOUNT_QUOTA` / 429,或遇到驗證類錯誤(`AUTH`、`INVALID_CREDENTIAL`、`MISSING_CREDENTIAL`、`ACCOUNT_SIGN_IN_REQUIRED`、`CLIENT_UPGRADE_REQUIRED`),當前帳戶會被標記為不可用(直到視窗重置,驗證失敗則為 30 分鐘),路由移到下一個已啟用的帳戶並**重試同一個步驟** —— 該回合永遠不會被降級。
|
|
43
|
+
- **單一模型隔離**:當只有一個模型超出額度而池仍有預算時,只有該模型被隔離(預設 30 分鐘);其他每個模型繼續使用方案。
|
|
44
|
+
- **整個池用盡**:只有到那時,路由才退回到它的 `fallback`(預設為 DeepSeek 帳戶),並每 30 分鐘(或在滾動視窗重置時)以零 token 的 `GET /inference/go/v1/usage` 探測一次,成功後自動回到方案。
|
|
45
|
+
- **重置**:設定中的「重置封鎖標記」會清除所有用盡/驗證標記並立即回到池。帳戶與金鑰不受影響。
|
|
46
|
+
|
|
47
|
+
## 使用量儀表板
|
|
48
|
+
|
|
49
|
+
官方的 `/inference/go/v1/usage` 端點只回傳四捨五入的滾動/每週/每月百分比,且**沒有依模型拆分**,而方案對每個模型分別計量(5 小時 = 月額度的 20 %,週 = 50 %,月 = 100 %)。因此頁面顯示兩組數字:
|
|
50
|
+
|
|
51
|
+
- **官方百分比**(每個帳戶一行):直接來自 API —— 真實帳戶用量,包含其他用戶端與其他機器。
|
|
52
|
+
- **依模型的金額**(模型表格):本地估算。它會重新掃描 `$DSH_HOME/sessions` 最近 40 天,只計算 provider 以 `opencode` 開頭的 `assistant/message` 事件,並按公開費率計價(包含 DeepSeek 的 2 倍尖峰時段)。其他工具的使用量沒有本地記錄。
|
|
53
|
+
|
|
54
|
+
「檢視用量」會開啟一個依帳戶的對話框,顯示三個視窗、它們的重置時間與探測結果(它只探測該帳戶,且從不切換模型)。
|
|
55
|
+
|
|
56
|
+
「編輯金鑰」會從 `GET /opencode-quota-router.json?key=<account id>` 取得帳戶目前的金鑰並預先填入輸入框(這是僅限本地的端點;清單快照只會帶遮罩值)。模型名稱依廠商的大小寫呈現(`deepseek-v4.1-flash` → `DeepSeek V4.1 Flash`);有公開品牌標誌的廠商會渲染成內嵌 SVG,其餘則保留字母徽章。「下次探測」是真正的計時器(預設 30 分鐘),頁面每秒重新渲染,因此會倒數;開啟頁面只會讀取快照,永遠不會觸發探測。
|
|
57
|
+
|
|
58
|
+
「現在用哪個更划算」由訂閱槓桿推導而來:Go 以 $10 換取定價 $100 的額度(≈ ¥710),所以 OpenCode 的價格永遠是官方價格的 10 % —— 便宜 90 %。真實資料永遠不會偏向官方 API;卡片角落的按鈕會切換到範例資料,讓你看見那種情況會長什麼樣子。
|
|
59
|
+
|
|
60
|
+
能力欄位來自同一個目錄端點(`/inference/go/v1/models`):`modalities.input` 與 `capabilities` 會渲染成 text / image / video / audio / PDF、工具呼叫、推理,以及上下文長度。零定價的模型顯示「無限」。
|
|
61
|
+
|
|
62
|
+
### 尖峰/離峰與假日
|
|
63
|
+
|
|
64
|
+
DeepSeek 的離峰視窗是北京時間週一至週五 09:00–12:00 與 14:00–18:00 之外的所有時間(含午休、夜間、週末與**法定假日**),價格正好是尖峰的一半。OpenCode 只把 UTC 週一至週五 01–04 與 06–10 視為尖峰,並**忽略中國法定假日**(anomalyco/opencode#53071,維護者:*「Go 的定價遵循我們自己的排程,而不是 DeepSeek 的假日行事曆。」*)。
|
|
65
|
+
|
|
66
|
+
外掛內建 2026–2031 年的中國法定假日表。只有在設定 `holidayAware: true`(`usage.holidayAware`)時才會套用到官方那一邊;預設為關閉,因為 OpenCode 在那些日子仍按尖峰計費。該表位於 `usage.js` 中的 `HOLIDAYS`,而 `isChinaHoliday()` 可以單獨呼叫。
|
|
67
|
+
|
|
68
|
+
訂閱價格與其額度可用 `usage.planPriceUsd` / `usage.planAllowanceUsd` 覆寫;頁面上的折扣由這兩個數字推導而來,因此 OpenCode 變更方案只是改設定,而不是打補丁。
|
|
69
|
+
|
|
70
|
+
## 設定 API
|
|
71
|
+
|
|
72
|
+
`GET /opencode-quota-router.json` 回傳 `{mode, reason, active, accounts[], quarantine[], routes[], lastFailure}`;帳戶金鑰一律遮罩(`hint`)。對同一路徑 `POST` 並帶 `{action, ...}`:
|
|
73
|
+
|
|
74
|
+
| 動作 | 參數 | 效果 |
|
|
75
|
+
| --- | --- | --- |
|
|
76
|
+
| `add` | `label?`, `key` | 建立帳戶(金鑰 ≥ 8 字元)、儲存並探測 |
|
|
77
|
+
| `update` | `id`, `label?` / `enabled?` / `key?` | 重新命名、停用/啟用、輪換金鑰 |
|
|
78
|
+
| `remove` | `id` | 刪除帳戶及其金鑰(永遠保留一個) |
|
|
79
|
+
| `activate` | `id` | 立即切換到它 |
|
|
80
|
+
| `move` | `id`, `direction: up\|down` | 重新排序 |
|
|
81
|
+
| `probe` | `id?` | 探測所有帳戶,或只探測一個 |
|
|
82
|
+
| `reset` | — | 清除用盡/驗證標記並回到池 |
|
|
83
|
+
|
|
84
|
+
`GET /opencode-usage.json` 回傳 `{plan, configuredPlan, detectedPlan, holidayAware, peak, usage, accounts[], activeAccount, models[], totals}`;`?refresh=1` 會先強制取得最新的官方快照。
|
|
85
|
+
|
|
86
|
+
## 語言
|
|
87
|
+
|
|
88
|
+
介面字串位於 `client.js` 的 `DICT` 中:`zh`(來源)、`zh-tw`、`en`,加上 `es` / `fr` / `de` / `pt` / `ja` / `ko` / `ru`(由 `scripts/merge-i18n.mjs` 從翻譯檔內嵌;缺少的鍵會退回 `en`,再退回該鍵本身)。`localeOf()` 會比對瀏覽器語言前綴,並區分簡體與繁體中文。
|
|
89
|
+
|
|
90
|
+
## 測試
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
node test.mjs
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
不需要模型額度:偽造的 context、偽造的憑證儲存、偽造的 webServer,以及一個依 API 金鑰回應的本地用量端點。涵蓋範圍:複合掛載、帳戶採用、單一模型隔離、A→B 容錯切換與金鑰鏡射、依帳戶探測、整池回退、重置、設定動作驗證、用量檢視(能力標籤、無限模型、尖峰/離峰計算、計費列)以及單一帳戶降級。
|
|
97
|
+
|
|
98
|
+
## 與舊套件的關係
|
|
99
|
+
|
|
100
|
+
`dsh-opencode-quota-router` 與 `dsh-opencode-usage-settings` 是它的前身;它們所做的一切都合併到這裡。兩者在 npm 上都標記為已棄用並指向這個套件。儲存庫:
|
|
101
|
+
[`2869949486/dsh-opencode-routing`](https://github.com/2869949486/dsh-opencode-routing)(目前版本)、
|
|
102
|
+
[`dsh-opencode-quota-router`](https://github.com/2869949486/dsh-opencode-quota-router)、
|
|
103
|
+
[`dsh-opencode-usage`](https://github.com/2869949486/dsh-opencode-usage)。
|
|
104
|
+
|
|
105
|
+
## 貢獻
|
|
106
|
+
|
|
107
|
+
Issue 與 pull request 都歡迎 —— 參見 [CONTRIBUTING.md](CONTRIBUTING.md)。開 PR 前請先執行 `node test.mjs`。
|
|
108
|
+
|
|
109
|
+
## 授權
|
|
110
|
+
|
|
111
|
+
MIT —— 參見 [LICENSE](LICENSE)。
|