@goodandready/dsh-clinebot 0.3.25 → 0.4.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 +32 -0
- package/README.md +21 -5
- package/README.ru.md +21 -5
- package/README.zh.md +21 -5
- package/lib/account-pool.js +59 -4
- package/lib/client.js +596 -200
- package/lib/cline-client.js +45 -45
- package/lib/config.js +25 -4
- package/lib/http.js +15 -2
- package/lib/index.js +117 -50
- package/lib/models.js +57 -25
- package/lib/provider-sync.js +60 -22
- package/lib/routes/accounts.js +178 -10
- package/lib/routes/auth.js +80 -38
- package/lib/routes/models.js +32 -29
- package/lib/routes/settings.js +83 -11
- package/lib/slash-command.js +91 -14
- package/lib/telemetry.js +78 -0
- package/package.json +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,38 @@ All notable changes to `@goodandready/dsh-clinebot` will be documented in this f
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.1] - 2026-09-24
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Slash Command Contract Fix**: Fixed `/cline` slash command handler to strictly adhere to DSH `@deepseek-ai/dsh-commands` runtime `CommandResult` contract `{ kind: 'success', text }` instead of returning raw strings, preventing `TypeError: command "cline" handler must return a CommandResult` in DSH chat.
|
|
12
|
+
- **Provider Detection via SettingsForms describe() (#102)**: Updated `checkRegisteredInPiAi` to inspect `settings.describe()` descriptors directly without triggering `TypeError` on missing `get()` method in modern DSH `SettingsForms`. Added safe error absorption for `providers.clinebot` removal when already absent.
|
|
13
|
+
- **Model Version Dots Preservation (#103)**: Fixed model parsing to preserve version dot notation (e.g. `claude-3.5-sonnet`, `deepseek-v4.5`), preventing incorrect dot stripping or catalog matching errors.
|
|
14
|
+
- **Strict Credential Validation & HTTPS Key Verify (#82)**: Hardened `/save-key` and `/config` to strictly prevent bypassing `apiKeyEnv` naming rules (forbidding arbitrary standard env variables such as `OPENAI_API_KEY`), and enforced HTTPS for `/key/verify` remote endpoints.
|
|
15
|
+
- **Settings Rollback & Error Display (#81)**: Implemented optimistic UI rollback and explicit error alert banner when model toggle or settings persistence fails, reloading current server state and properly cleaning up debounced unmount timers.
|
|
16
|
+
- **Snapshot Warning Banner & Failover Localization (#104)**: Restored non-blocking warning banner when host `snapshotStatus === 'unavailable'`, and localized `Last failover: ...` via `t('accounts.last_failover')` across English and Chinese locale dictionaries.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- **Account Pool Form & Management Routes (#88)**: Completed full account pool UI with dedicated "Add Account" form (label, apiKeyEnv with auto-suggestions, secret input with show/hide toggle), account deletion with confirmation, model search input (`modelsSearch`), and category filter tabs (all, chat, coding, reasoning). Registered `POST /dsh-clinebot/accounts` and `POST /dsh-clinebot/accounts/delete` server endpoints.
|
|
20
|
+
- **Provider Key Failover Verification (#85)**: Added behavioral test assertions confirming that `rotateToNextAccount` mutates `llm-pi-ai` provider settings with the rotated account's `apiKeyEnv`, and replaced source code string inspection in `settings-service.test.js` with pure behavioral mock tests.
|
|
21
|
+
- **Repository Hygiene (#91)**: Pruned obsolete remote tracking branches, confirmed duplicate issue label resolution, and ensured clean working tree.
|
|
22
|
+
|
|
23
|
+
## [0.4.0] - 2026-09-24
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
- **One-Click In-App Updater (#91)**: Self-update companion plugin directly from the DSH settings card or via loopback `POST /dsh-clinebot/update` with rate limiting, package manifest verification, and restart indicators.
|
|
27
|
+
- **Real DSH Stream 429 & Quota Failover (#79)**: Intercepts HTTP 429 and quota exhaustion at the `llm/stream` waterfall level, automatically rotating to the next account in the pool with 30s storm protection and immediately synchronizing the provider's `apiKeyEnv` in `llm-pi-ai`.
|
|
28
|
+
- **Dynamic Plan Models Discovery (#90)**: Discovers models dynamically from active ClinePass subscription plan via `POST /dsh-clinebot/models/sync`. Removes hardcoded pricing references from UI and settings.
|
|
29
|
+
- **Stream Telemetry & Token Tracking (#83)**: Tracks real DSH chat requests through the ClineBot provider, prompt and completion tokens from stream usage chunks, stream latency, and error counts since process startup.
|
|
30
|
+
- **Live API Key Verification (#84)**: Direct navigation to `https://app.cline.bot/settings/api-keys` and on-the-fly key verification endpoint `POST /dsh-clinebot/key/verify` displaying account email and subscription plan in the UI.
|
|
31
|
+
- **Behavioral Test Suite (#85)**: Comprehensive test suite testing all write endpoints for HTTP 405 Method Not Allowed, HTTP 403 Forbidden on untrusted origins, service error reporting (503), and behavioral execution of all `/cline` slash subcommands.
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
- **Hardened PUT /dsh-clinebot/config (#89)**: Merges from raw `live()`, accepts only known schema fields, strictly rejects unknown properties with 400 Bad Request, rejects deprecated `enabledModels`, and documents HTTP API in README.
|
|
35
|
+
- **Cordis 4 Clean apply() Return (#89)**: `apply()` now returns `undefined` to eliminate `TypeError: Invalid effect` in Cordis 4, and dead branches without `ctx.inject` were removed.
|
|
36
|
+
- **Strict Credential Naming Pattern (#82)**: Restricts `/dsh-clinebot/save-key` target environment variables strictly to `^CLINEBOT_API_KEY(_[A-Z0-9]+)?$`.
|
|
37
|
+
- **Honest Persistence Reporting (#81)**: Write endpoints report honest errors and HTTP 503 when the DSH settings service is unavailable.
|
|
38
|
+
- **UI and Locale Refinements (#86, #87, #88)**: Eliminated undeclared `ctx` reference in `PluginCard`, removed dead state loops in `SettingsPage`, corrected layout spacing and badge styling across localized cards.
|
|
39
|
+
|
|
8
40
|
## [0.3.25] - 2026-09-24
|
|
9
41
|
|
|
10
42
|
### Fixed
|
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
## ⚡ Overview & The Problem
|
|
40
40
|
|
|
41
|
-
**ClinePass** (`https://cline.bot`) is a
|
|
41
|
+
**ClinePass** (`https://cline.bot`) is a subscription service providing developers with 2–5x higher rate limits across premier open-weights coding and reasoning models through a single OpenAI-compatible endpoint (`https://api.cline.bot/api/v1`).
|
|
42
42
|
|
|
43
43
|
Integrating ClinePass into DeepSeek Harness (DSH) natively poses key challenges:
|
|
44
44
|
1. **No `/v1/models` Discovery**: `GET /v1/models` on `api.cline.bot` returns `404 Not Found`. Dynamic discovery fails silently or leaves the provider with 0 models.
|
|
@@ -49,11 +49,11 @@ Integrating ClinePass into DeepSeek Harness (DSH) natively poses key challenges:
|
|
|
49
49
|
**`@goodandready/dsh-clinebot`** provides a complete solution:
|
|
50
50
|
* 🚀 **One-Click In-App Updater**: Upgrade `@goodandready/dsh-clinebot` directly from the DSH UI or trigger secure loopback updates via `/dsh-clinebot/update`.
|
|
51
51
|
* ⚡ **SWR Quota & Health Caching**: Instantaneous response time (<2ms) on status queries with background revalidation.
|
|
52
|
-
* 🔀 **Smart Quota-Aware Failover**: Automatic multi-account rotation
|
|
52
|
+
* 🔀 **Smart Quota-Aware Failover**: Automatic multi-account rotation on stream HTTP 429 and exhausted quota (with 30s storm protection; current request is not retried, subsequent chat requests use the next available account).
|
|
53
53
|
* 🖥️ **Plugin configuration page**: Open the installed ClineBot plugin and choose configure. The page shows the credential name, models, quota, and accounts. It is not a separate sidebar section.
|
|
54
|
-
* 🔄 **Dynamic Subscription Model Sync**: Automatically pulls real models included in your ClinePass plan directly from `GET /api/v1/users/me/plan` with one-click DSH provider sync.
|
|
54
|
+
* 🔄 **Dynamic Subscription Model Sync**: Automatically pulls real models included in your ClinePass plan directly from `GET /api/v1/users/me/plan` with one-click DSH provider sync. Only actual plan models are registered in DSH, while the built-in catalog serves as a rich properties reference and fallback when unsynced.
|
|
55
55
|
* ⚠️ **Quota Exhaustion Alerts**: Real-time visual warning banners when 5-hour rolling limit reaches 80% (warning) and 95% (exhausted), complete with countdown to reset.
|
|
56
|
-
* 📈 **Session Metrics Telemetry**: Live dashboard tracking
|
|
56
|
+
* 📈 **Session Metrics Telemetry**: Live dashboard tracking real in-flight DSH chat requests through the ClineBot provider, prompt and completion tokens, stream latency, and error counts since process startup.
|
|
57
57
|
* 📊 **Live Quota Dashboard**: Visual progress bars for 5-hour rolling limits and weekly windows from the official `GET /users/me/plan/usage-limits` API.
|
|
58
58
|
* 🔑 **In-UI Key Storage**: Paste your API key directly in the UI; it is saved securely via `ctx.credentials.set()` into `~/.dsh/.credentials.yaml`.
|
|
59
59
|
* 🎯 **Model Picker Management**: Granular checkboxes to choose which models appear in the chat picker.
|
|
@@ -137,7 +137,7 @@ Restart your DeepSeek Harness instance and refresh the browser.
|
|
|
137
137
|
From any DSH chat session, type `/cline` to inspect quota, warning alerts, and session telemetry:
|
|
138
138
|
|
|
139
139
|
```text
|
|
140
|
-
### 🤖 ClinePass Status (ClinePass
|
|
140
|
+
### 🤖 ClinePass Status (ClinePass)
|
|
141
141
|
* Latency: ✅ 210 ms
|
|
142
142
|
* Active Key: CLINEBOT_API_KEY (credentials)
|
|
143
143
|
* Default Model: `cline-pass/deepseek-v4-flash`
|
|
@@ -189,6 +189,22 @@ dsh-clinebot:
|
|
|
189
189
|
|
|
190
190
|
---
|
|
191
191
|
|
|
192
|
+
## 🌐 HTTP API Endpoints
|
|
193
|
+
|
|
194
|
+
All endpoints are registered under `/dsh-clinebot/*` and protected against untrusted cross-site origins (same-origin and loopback allowed):
|
|
195
|
+
|
|
196
|
+
* `GET /dsh-clinebot/status` — Live status report including provider health, active credential, quota, and session metrics.
|
|
197
|
+
* `GET /dsh-clinebot/config` — Diagnostic endpoint returning public configuration without secret keys.
|
|
198
|
+
* `PUT /dsh-clinebot/config` — Update configuration fields. Accepts only known schema properties (unknown fields or deprecated `enabledModels` return `400 Bad Request`).
|
|
199
|
+
* `POST /dsh-clinebot/key/verify` — Validates a candidate API key against `api.cline.bot` and returns account email and plan name.
|
|
200
|
+
* `POST /dsh-clinebot/save-key` — Saves a key into DSH credentials service under a valid `CLINEBOT_API_KEY*` name.
|
|
201
|
+
* `POST /dsh-clinebot/models/sync` — Synchronizes models with your active subscription plan.
|
|
202
|
+
* `POST /dsh-clinebot/models/toggle` — Toggles models via `disabledModels`.
|
|
203
|
+
* `POST /dsh-clinebot/accounts/active` — Pins an active account from the account pool.
|
|
204
|
+
* `POST /dsh-clinebot/smoke` — Runs a live latency test ping.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
192
208
|
## 🧪 Testing
|
|
193
209
|
|
|
194
210
|
Run the automated test suite:
|
package/README.ru.md
CHANGED
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
## ⚡ Обзор и решаемая проблема
|
|
40
40
|
|
|
41
|
-
**ClinePass** (`https://cline.bot`) — сервис единой фиксированной
|
|
41
|
+
**ClinePass** (`https://cline.bot`) — сервис единой фиксированной подписки, предоставляющий разработчикам повышенные лимиты (в 2–5 раз выше стандартных) на передовые open-weights модели программирования и рассуждений через единый OpenAI-совместимый интерфейс (`https://api.cline.bot/api/v1`).
|
|
42
42
|
|
|
43
43
|
Интеграция ClinePass в DeepSeek Harness (DSH) напрямую сопряжена со следующими сложностями:
|
|
44
44
|
1. **Отсутствие эндпоинта `/v1/models`**: запрос `GET /v1/models` к `api.cline.bot` возвращает `404 Not Found`, из-за чего динамический поиск моделей в DSH падает или оставляет список пустым.
|
|
@@ -49,11 +49,11 @@
|
|
|
49
49
|
Плагин **`@goodandready/dsh-clinebot`** решает эти задачи «из коробки»:
|
|
50
50
|
* 🚀 **Обновление в один клик**: Обновление плагина прямо из интерфейса DSH или через защищённый loopback-эндпоинт `/dsh-clinebot/update`.
|
|
51
51
|
* ⚡ **SWR-кэширование квот и здоровья**: Мгновенный ответ (<2 мс) на запросы статуса с фоновым обновлением данных без блокировки UI.
|
|
52
|
-
* 🔀 **Smart Quota-Aware Failover**:
|
|
52
|
+
* 🔀 **Smart Quota-Aware Failover**: Автоматическая ротация аккаунтов в пуле при ошибках 429 и исчерпании квоты в стриме (с 30-секундной защитой от шторма; текущий запрос не повторяется, последующие запросы сразу идут через следующий аккаунт).
|
|
53
53
|
* 🖥️ **Страница конфигурации плагина**: откройте установленный ClineBot и перейдите в настройки плагина. Там имя учётных данных, модели, квота и аккаунты. Отдельного пункта бокового меню нет.
|
|
54
|
-
* 🔄 **Динамическая синхронизация моделей подписки**: автоматическое получение реального списка моделей из `GET /api/v1/users/me/plan` и мгновенное обновление провайдера DSH в один клик.
|
|
54
|
+
* 🔄 **Динамическая синхронизация моделей подписки**: автоматическое получение реального списка моделей из `GET /api/v1/users/me/plan` и мгновенное обновление провайдера DSH в один клик. В DSH регистрируются строго модели активного тарифа, а встроенный каталог служит справочником характеристик и запасным вариантом при недоступности API.
|
|
55
55
|
* ⚠️ **Предупреждения об исчерпании квоты**: баннеры предупреждения при достижении 80% (внимание) и 95% (исчерпано) 5-часового лимита с таймером сброса.
|
|
56
|
-
* 📈 **Метрики сессии**:
|
|
56
|
+
* 📈 **Метрики сессии**: Учет реального трафика DSH-чатов через провайдер ClineBot с момента запуска (успешные, прерванные и ошибочные запросы, токены prompt/completion, задержка стрима).
|
|
57
57
|
* 📊 **Дашборд лимитов подписки (Usage)**: наглядные прогресс-бары расхода 5-часового и недельного скользящего окна из официального API `GET /users/me/plan/usage-limits`.
|
|
58
58
|
* 🔑 **Сохранение ключа прямо из UI**: поле ввода ключа с маскировкой; сохранение напрямую в системный сервис `credentials` (`~/.dsh/.credentials.yaml`) без ручной правки файлов на сервере.
|
|
59
59
|
* 🎯 **Управление моделями в пикере**: включение/выключение отображения конкретных моделей в диалогах чата.
|
|
@@ -134,7 +134,7 @@ dsh plugin --profile web add @goodandready/dsh-clinebot
|
|
|
134
134
|
В любой сессии чата введите команду `/cline` для проверки остатка лимитов, предупреждений и статистики:
|
|
135
135
|
|
|
136
136
|
```text
|
|
137
|
-
### 🤖 ClinePass Status (ClinePass
|
|
137
|
+
### 🤖 ClinePass Status (ClinePass)
|
|
138
138
|
* Пинг хоста: ✅ 210 мс
|
|
139
139
|
* Активный ключ: CLINEBOT_API_KEY (credentials)
|
|
140
140
|
* Модель по умолчанию: `cline-pass/deepseek-v4-flash`
|
|
@@ -184,6 +184,22 @@ dsh-clinebot:
|
|
|
184
184
|
|
|
185
185
|
---
|
|
186
186
|
|
|
187
|
+
## 🌐 Маршруты HTTP API
|
|
188
|
+
|
|
189
|
+
Все маршруты регистрируются с префиксом `/dsh-clinebot/*` и защищены от несанкционированных межсайтовых запросов (разрешены same-origin и loopback):
|
|
190
|
+
|
|
191
|
+
* `GET /dsh-clinebot/status` — Текущее состояние провайдера, активный аккаунт, квоты и статистика сессий.
|
|
192
|
+
* `GET /dsh-clinebot/config` — Диагностический маршрут, возвращающий открытую конфигурацию (без секретов).
|
|
193
|
+
* `PUT /dsh-clinebot/config` — Обновление параметров конфигурации. Принимает только известные поля схемы (неизвестные поля и устаревшее `enabledModels` возвращают `400 Bad Request`).
|
|
194
|
+
* `POST /dsh-clinebot/key/verify` — Проверка API-ключа на сервере `api.cline.bot` с возвратом email и названия тарифа.
|
|
195
|
+
* `POST /dsh-clinebot/save-key` — Сохранение ключа в сервис credentials DSH под именем шаблона `CLINEBOT_API_KEY*`.
|
|
196
|
+
* `POST /dsh-clinebot/models/sync` — Синхронизация списка моделей с официальным тарифом ClinePass.
|
|
197
|
+
* `POST /dsh-clinebot/models/toggle` — Включение и отключение моделей через `disabledModels`.
|
|
198
|
+
* `POST /dsh-clinebot/accounts/active` — Назначение активного аккаунта из пула.
|
|
199
|
+
* `POST /dsh-clinebot/smoke` — Выполнение тестового пинга задержки.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
187
203
|
## 🧪 Тестирование
|
|
188
204
|
|
|
189
205
|
Запуск автоматического набора тестов:
|
package/README.zh.md
CHANGED
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
## ⚡ 概述与解决的核心痛点
|
|
40
40
|
|
|
41
|
-
**ClinePass** (`https://cline.bot`)
|
|
41
|
+
**ClinePass** (`https://cline.bot`) 是一项高性价比订阅服务,为开发者提供主流开源代码模型与推理模型 2–5 倍的高并发调用限额,统一通过 OpenAI 兼容接口 (`https://api.cline.bot/api/v1`) 提供服务。
|
|
42
42
|
|
|
43
43
|
在将 ClinePass 接入 DeepSeek Harness (DSH) 时存在以下挑战:
|
|
44
44
|
1. **缺失 `/v1/models` 接口**:`api.cline.bot` 的 `GET /v1/models` 会直接返回 `404 Not Found`,导致动态模型同步失败或模型列表为空。
|
|
@@ -49,11 +49,11 @@
|
|
|
49
49
|
**`@goodandready/dsh-clinebot`** 完美解决以上痛点:
|
|
50
50
|
* 🚀 **应用内一键更新**:直接在 DSH 界面检查并升级插件,或通过受保护的 `/dsh-clinebot/update` 进行本地安全更新。
|
|
51
51
|
* ⚡ **SWR 配额与健康状态缓存**:状态查询毫秒级响应(<2ms),并在后台静默更新,不阻塞前端渲染。
|
|
52
|
-
* 🔀 **智能配额故障转移 (Smart Failover)
|
|
52
|
+
* 🔀 **智能配额故障转移 (Smart Failover)**:在流式请求遇到 HTTP 429 或配额耗尽时自动轮换多账号(具备 30 秒防风暴保护;当前失败请求不自动重试,后续对话请求无缝使用下一个账号)。
|
|
53
53
|
* 🖥️ **插件配置页**:打开已安装的 ClineBot,进入配置。页面包含凭据名、模型、配额和账号,不单独占用侧边栏。
|
|
54
|
-
* 🔄 **订阅模型动态同步**:从官方 `GET /api/v1/users/me/plan` 自动提取真实包含模型,一键原子级同步至 DSH
|
|
54
|
+
* 🔄 **订阅模型动态同步**:从官方 `GET /api/v1/users/me/plan` 自动提取真实包含模型,一键原子级同步至 DSH 提供商配置。DSH 严格仅注册当前套餐模型,内置目录作为特性参考并在未同步时作为备用。
|
|
55
55
|
* ⚠️ **额度耗尽实时预警**:当 5 小时滑动窗口达到 80%(警告黄色)和 95%(即将耗尽红色)时展示醒目预警横幅与重置倒计时。
|
|
56
|
-
* 📈
|
|
56
|
+
* 📈 **会话统计与指标看板**:实时监控进程启动后经由 ClineBot 提供商发出的真实 DSH 对话请求、Prompt/Completion Token 消耗、流式延迟及错误统计。
|
|
57
57
|
* 📊 **实时用量仪表盘**:调用官方 `GET /users/me/plan/usage-limits` API,实时渲染 5 小时与每周额度进度条及重置倒计时。
|
|
58
58
|
* 🔑 **界面直存密钥**:在 UI 中直接粘贴 API 密钥,通过 `ctx.credentials.set()` 自动安全保存至 `~/.dsh/.credentials.yaml`。
|
|
59
59
|
* 🎯 **模型选择器管理**:支持勾选开启/关闭特定模型在聊天选择器中的显示。
|
|
@@ -136,7 +136,7 @@ dsh plugin --profile web add @goodandready/dsh-clinebot
|
|
|
136
136
|
在任何聊天会话中输入 `/cline` 即可即时检查配额、预警状态与会话指标:
|
|
137
137
|
|
|
138
138
|
```text
|
|
139
|
-
### 🤖 ClinePass Status (ClinePass
|
|
139
|
+
### 🤖 ClinePass Status (ClinePass)
|
|
140
140
|
* 响应延迟: ✅ 210 ms
|
|
141
141
|
* 活跃密钥: CLINEBOT_API_KEY (credentials)
|
|
142
142
|
* 默认模型: `cline-pass/deepseek-v4-flash`
|
|
@@ -186,6 +186,22 @@ dsh-clinebot:
|
|
|
186
186
|
|
|
187
187
|
---
|
|
188
188
|
|
|
189
|
+
## 🌐 HTTP API 接口说明
|
|
190
|
+
|
|
191
|
+
所有接口注册于 `/dsh-clinebot/*` 路径,并受到严格的跨站防护保护(仅允许同源或环回请求):
|
|
192
|
+
|
|
193
|
+
* `GET /dsh-clinebot/status` — 服务运行状态,包括健康探活、当前凭据名称、配额限制及会话统计。
|
|
194
|
+
* `GET /dsh-clinebot/config` — 诊断接口,安全获取脱敏后的公共配置。
|
|
195
|
+
* `PUT /dsh-clinebot/config` — 更新配置项。仅接受模式中的已知字段(未知字段或已废弃的 `enabledModels` 返回 `400 Bad Request`)。
|
|
196
|
+
* `POST /dsh-clinebot/key/verify` — 向 `api.cline.bot` 发送探活请求实时验证密钥,返回绑定邮箱及套餐名称。
|
|
197
|
+
* `POST /dsh-clinebot/save-key` — 将 API 密钥安全存储到 DSH credentials 服务。
|
|
198
|
+
* `POST /dsh-clinebot/models/sync` — 同步 ClinePass 官方套餐内包含的全部动态模型。
|
|
199
|
+
* `POST /dsh-clinebot/models/toggle` — 通过 `disabledModels` 批量切换模型可用性。
|
|
200
|
+
* `POST /dsh-clinebot/accounts/active` — 从多账号池中指定当前主账号。
|
|
201
|
+
* `POST /dsh-clinebot/smoke` — 发起快速探测并返回实时延迟。
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
189
205
|
## 🧪 测试
|
|
190
206
|
|
|
191
207
|
运行自动化测试套件:
|
package/lib/account-pool.js
CHANGED
|
@@ -11,6 +11,27 @@ function textSetting(value) {
|
|
|
11
11
|
return typeof current === 'string' ? current.trim() : ''
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
function arraySetting(value) {
|
|
15
|
+
let current = value
|
|
16
|
+
while (current && typeof current === 'object' && !Array.isArray(current) && typeof current.get === 'function') {
|
|
17
|
+
current = current.get()
|
|
18
|
+
}
|
|
19
|
+
return Array.isArray(current) ? current : []
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function toPlainSnapshot(cfg) {
|
|
23
|
+
if (!cfg || typeof cfg !== 'object') return cfg
|
|
24
|
+
const out = {}
|
|
25
|
+
for (const [k, v] of Object.entries(cfg)) {
|
|
26
|
+
let cur = v
|
|
27
|
+
while (cur && typeof cur === 'object' && !Array.isArray(cur) && typeof cur.get === 'function') {
|
|
28
|
+
cur = cur.get()
|
|
29
|
+
}
|
|
30
|
+
out[k] = cur
|
|
31
|
+
}
|
|
32
|
+
return out
|
|
33
|
+
}
|
|
34
|
+
|
|
14
35
|
export async function resolveAccountPool(ctx, cfg) {
|
|
15
36
|
const apiKeyEnv = textSetting(cfg?.apiKeyEnv) || DEFAULT_API_KEY_ENV
|
|
16
37
|
const defaultSlot = {
|
|
@@ -18,7 +39,7 @@ export async function resolveAccountPool(ctx, cfg) {
|
|
|
18
39
|
label: 'Default',
|
|
19
40
|
apiKeyEnv,
|
|
20
41
|
}
|
|
21
|
-
const accounts =
|
|
42
|
+
const accounts = arraySetting(cfg?.accounts)
|
|
22
43
|
const allSlots = [defaultSlot, ...accounts]
|
|
23
44
|
const activeAccount = textSetting(cfg?.activeAccount)
|
|
24
45
|
const resolved = []
|
|
@@ -72,7 +93,7 @@ export async function rotateToNextAccount(ctx, cfg, reason = 'rate_limit', setti
|
|
|
72
93
|
return { rotated: false, reason, message: 'Pool has only 1 configured account' }
|
|
73
94
|
}
|
|
74
95
|
|
|
75
|
-
const active =
|
|
96
|
+
const active = textSetting(cfg?.activeAccount) || configured[0].apiKeyEnv
|
|
76
97
|
const currentIndex = configured.findIndex((acc) => acc.apiKeyEnv === active)
|
|
77
98
|
|
|
78
99
|
// Candidate pool excluding current account if possible
|
|
@@ -105,7 +126,7 @@ export async function rotateToNextAccount(ctx, cfg, reason = 'rate_limit', setti
|
|
|
105
126
|
let updated = false
|
|
106
127
|
if (settingsApi?.replace) {
|
|
107
128
|
try {
|
|
108
|
-
const next = { ...cfg, activeAccount: nextAcc.apiKeyEnv }
|
|
129
|
+
const next = { ...toPlainSnapshot(cfg), activeAccount: nextAcc.apiKeyEnv }
|
|
109
130
|
await settingsApi.replace(next)
|
|
110
131
|
updated = true
|
|
111
132
|
} catch (err) {
|
|
@@ -140,11 +161,45 @@ export async function rotateToNextAccount(ctx, cfg, reason = 'rate_limit', setti
|
|
|
140
161
|
clearUsageCache()
|
|
141
162
|
clearProbeCache()
|
|
142
163
|
|
|
143
|
-
|
|
164
|
+
const result = {
|
|
144
165
|
rotated: true,
|
|
145
166
|
previousAccount: active,
|
|
146
167
|
activeAccount: nextAcc.apiKeyEnv,
|
|
147
168
|
reason,
|
|
148
169
|
updatedSettings: true,
|
|
149
170
|
}
|
|
171
|
+
|
|
172
|
+
setLastRotation({
|
|
173
|
+
at: Date.now(),
|
|
174
|
+
reason,
|
|
175
|
+
from: active,
|
|
176
|
+
to: nextAcc.apiKeyEnv,
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
return result
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let lastRotation = null
|
|
183
|
+
|
|
184
|
+
export function getLastRotation() {
|
|
185
|
+
return lastRotation
|
|
150
186
|
}
|
|
187
|
+
|
|
188
|
+
export function setLastRotation(info) {
|
|
189
|
+
lastRotation = info
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function isQuotaExceededError(detail) {
|
|
193
|
+
if (!detail || typeof detail !== 'string') return false
|
|
194
|
+
const s = detail.toLowerCase()
|
|
195
|
+
return (
|
|
196
|
+
s.includes('quota') ||
|
|
197
|
+
s.includes('rate limit') ||
|
|
198
|
+
s.includes('exhausted') ||
|
|
199
|
+
s.includes('429') ||
|
|
200
|
+
s.includes('insufficient_quota') ||
|
|
201
|
+
s.includes('credit') ||
|
|
202
|
+
s.includes('too many requests')
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|