@goodandready/dsh-clinebot 0.3.8 → 0.3.10
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 +15 -2
- package/{docs/README.ru.md → README.ru.md} +14 -1
- package/{docs/README.zh.md → README.zh.md} +14 -1
- package/lib/client.js +135 -99
- package/lib/cline-client.js +207 -117
- package/lib/http.js +62 -2
- package/lib/index.js +71 -148
- package/lib/updater.js +276 -0
- package/package.json +3 -2
- package/docs/design/DESIGN.md +0 -82
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.3.10] - 2026-09-16
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **UI Color Contrast & Adaptive Theme Compliance** (Gitea Issue #27): Replaced all hardcoded `rgba(...)` background tints and borders with CSS `color-mix(in srgb, var(--dsw-alias-state-...) X%, transparent)` for full legibility across Light and Dark DSH themes.
|
|
12
|
+
- **Kernel Chevron Icon Probe & Fallback** (Gitea Issue #28): Added dynamic safe probe for kernel `IconChevronDownOutline14` from `@deepseek-ai/dsh-client-ui-primitives` with pixel-perfect FallbackChevron and `.cb-chevron` / `.cb-chevron-open` rotation classes.
|
|
13
|
+
- **Single-Bundle Runtime Contract Documentation** (Gitea Issue #29): Formalized client single-bundle architecture and decoupled backend design in `docs/design/DESIGN.md`.
|
|
14
|
+
- **Packaging Sanitization & Denylist Enforcement** (Gitea Issues #25, #26, #30): Purged internal workflow files (`AGENTS.md`, `index.md`, `deploy.sh`, `release-notes.md`) from git tracking and added them to `.gitignore`. Removed redundant duplicate READMEs in `docs/` and outdated root `.tgz` artifacts, reducing unpacked package size to 200 KiB.
|
|
15
|
+
- **Client Module Injection Contract Clarification** (Gitea Issue #32): Documented that `dsh.client.inject: []` in `package.json` is architectural canon for plugins consuming core services (`slots`, `locale`, `settingsScope`) via `exports.inject` rather than require-table imports.
|
|
16
|
+
- **Immediate Quota & Probe Invalidation on Account Switch** (Gitea Issue #31): Connected `clearUsageCache()` and `clearProbeCache()` to account switching routes (`/accounts/active`, `/cline switch`) and `rotateToNextAccount()`, preventing quota telemetry from lagging or sticking to former accounts.
|
|
17
|
+
- **Model Validation in Slash Command** (Gitea Issue #31): Integrated `isSupportedModel()` in `/cline test [model]` to validate target models upfront before issuing upstream requests.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- **LLM Provider Schema Alignment (`reasoningEfforts`)** (GitHub Issue #1, Gitea Issue #35): Fixed Cordis loader validation failure (`$.providers.clinebot.models[0].reasoningEfforts expected false | { [key]: string } but got ["low","medium","high"]`). Properly map array reasoning efforts into a validated object record `{ [effort]: effort }` or `false`, resolving startup provider crash.
|
|
22
|
+
- **Client Localization Fallback Crash** (Gitea Issue #34): Fixed `ReferenceError: ru is not defined` in `lib/client.js` fallback registration path when `ctx.effect` is absent. Removed direct reference to deleted `ru` dictionary.
|
|
23
|
+
- **Settings Persistence Error Visibility** (Gitea Issue #33): Replaced empty/silent `catch {}` blocks around settings persistence in `lib/client.js` and `lib/cline-client.js` with structured warning logs (`ctx.logger.warn`) and user-facing error banners (`setErr`).
|
|
24
|
+
- **Semantic Version Prerelease Comparison in Host Updater** (Gitea Issue #23): Enhanced `isNewerVersion()` in `lib/updater.js` to strictly follow SemVer 2.0.0 rules for pre-release tags, ensuring pre-releases and release candidates update seamlessly.
|
|
25
|
+
- **Hardened Write-Route Security Validation** (Gitea Issue #22): Strengthened `isTrustedSettingsRequest()` in `lib/http.js` to rigorously validate `Origin`, `Host`, `X-Forwarded-Host`, `Referer`, and loopback remote addresses against CSRF, while maintaining full support for reverse proxies and local LAN environments.
|
|
26
|
+
|
|
27
|
+
## [0.3.9] - 2026-09-15
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
- **Stale-While-Revalidate (SWR) Quota Caching**: Integrated SWR caching into `fetchUsageLimits` with a 20-second TTL. Instantaneous (<2ms) responses on `/dsh-clinebot/status` and `/dsh-clinebot/usage` with background revalidation.
|
|
31
|
+
- **Smart Quota-Aware Failover**: Account rotation automatically skips exhausted accounts (>=95% 5h usage) and selects the account with the lowest `percentUsed`. Supports auto-recovery once `resetsAt` timestamp is reached.
|
|
32
|
+
- **One-Click In-App Updater (`/dsh-clinebot/update`)**: Added canonical DSH updater endpoint (`lib/updater.js`) validating loopback origin, same-origin, and `x-dsh-plugin-update` header for safe in-app upgrades.
|
|
33
|
+
- **Complete Chinese Localization (`zh`)**: Added comprehensive native Chinese dictionary (67+ translation keys) to `lib/client.js`.
|
|
34
|
+
- **DSH Canon Compliance**: Cleanly separated external Russian translation (supplied via `dsh-russian-lang`), and normalized all slash command outputs to canonical English.
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
- **Code Deduplication**: Eliminated duplicate credential and account pool helper functions between `lib/index.js` and `lib/cline-client.js`.
|
|
38
|
+
- **Network Keep-Alive & Cloudflare Handling**: Enforced persistent keep-alive connections on outbound requests and improved error extraction on HTML challenge responses.
|
|
39
|
+
|
|
8
40
|
## [0.3.8] - 2026-09-12
|
|
9
41
|
|
|
10
42
|
### Added
|
package/README.md
CHANGED
|
@@ -18,10 +18,20 @@
|
|
|
18
18
|
|
|
19
19
|
<p align="center">
|
|
20
20
|
<a href="README.md"><b>🇬🇧 English</b></a> •
|
|
21
|
-
<a href="
|
|
22
|
-
<a href="
|
|
21
|
+
<a href="README.ru.md"><b>🇷🇺 Русский</b></a> •
|
|
22
|
+
<a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
|
|
23
23
|
</p>
|
|
24
24
|
|
|
25
|
+
<table align="center">
|
|
26
|
+
<tr>
|
|
27
|
+
<td align="center">
|
|
28
|
+
⭐ <strong>If you like this plugin, please star it on GitHub</strong> — it shows me that the plugin is useful to you and motivates me to keep developing it.
|
|
29
|
+
<br><br>
|
|
30
|
+
🐛 <strong>If you find a bug or would like to request a feature</strong>, open a GitHub issue in any language — I will review your proposal and implement useful suggestions in a future plugin version.
|
|
31
|
+
</td>
|
|
32
|
+
</tr>
|
|
33
|
+
</table>
|
|
34
|
+
|
|
25
35
|
</div>
|
|
26
36
|
|
|
27
37
|
---
|
|
@@ -37,6 +47,9 @@ Integrating ClinePass into DeepSeek Harness (DSH) natively poses key challenges:
|
|
|
37
47
|
4. **Credential Security**: Storing API keys directly in plain settings is insecure.
|
|
38
48
|
|
|
39
49
|
**`@goodandready/dsh-clinebot`** provides a complete solution:
|
|
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
|
+
* ⚡ **SWR Quota & Health Caching**: Instantaneous response time (<2ms) on status queries with background revalidation.
|
|
52
|
+
* 🔀 **Smart Quota-Aware Failover**: Automatic multi-account rotation skipping exhausted accounts and recovering when `resetsAt` is reached.
|
|
40
53
|
* 🖥️ **Dedicated Settings Page**: Full-width page in DSH Settings (`Settings → ClineBot`).
|
|
41
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.
|
|
42
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.
|
|
@@ -17,11 +17,21 @@
|
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
19
|
<p align="center">
|
|
20
|
-
<a href="
|
|
20
|
+
<a href="README.md"><b>🇬🇧 English</b></a> •
|
|
21
21
|
<a href="README.ru.md"><b>🇷🇺 Русский</b></a> •
|
|
22
22
|
<a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
|
|
23
23
|
</p>
|
|
24
24
|
|
|
25
|
+
<table align="center">
|
|
26
|
+
<tr>
|
|
27
|
+
<td align="center">
|
|
28
|
+
⭐ <strong>Если вам нравится этот плагин, поставьте ему звезду на GitHub</strong> — это покажет мне, что плагин вам полезен, и будет мотивировать меня развивать его дальше.
|
|
29
|
+
<br><br>
|
|
30
|
+
🐛 <strong>Если вы нашли баг или хотите предложить новый функционал</strong>, создайте issue на GitHub на любом языке — я рассмотрю ваше предложение и реализую полезные идеи в одной из следующих версий плагина.
|
|
31
|
+
</td>
|
|
32
|
+
</tr>
|
|
33
|
+
</table>
|
|
34
|
+
|
|
25
35
|
</div>
|
|
26
36
|
|
|
27
37
|
---
|
|
@@ -37,6 +47,9 @@
|
|
|
37
47
|
4. **Безопасность ключей**: хранение API-токенов в открытых конфигурациях небезопасно.
|
|
38
48
|
|
|
39
49
|
Плагин **`@goodandready/dsh-clinebot`** решает эти задачи «из коробки»:
|
|
50
|
+
* 🚀 **Обновление в один клик**: Обновление плагина прямо из интерфейса DSH или через защищённый loopback-эндпоинт `/dsh-clinebot/update`.
|
|
51
|
+
* ⚡ **SWR-кэширование квот и здоровья**: Мгновенный ответ (<2 мс) на запросы статуса с фоновым обновлением данных без блокировки UI.
|
|
52
|
+
* 🔀 **Smart Quota-Aware Failover**: Интеллектуальная ротация аккаунтов с обходом исчерпанных лимитов и авто-восстановлением при наступлении `resetsAt`.
|
|
40
53
|
* 🖥️ **Отдельная страница в Настройках**: собственная полноэкранная страница в меню Настроек DSH (`Настройки → ClineBot`).
|
|
41
54
|
* 🔄 **Динамическая синхронизация моделей подписки**: автоматическое получение реального списка моделей из `GET /api/v1/users/me/plan` и мгновенное обновление провайдера DSH в один клик.
|
|
42
55
|
* ⚠️ **Предупреждения об исчерпании квоты**: баннеры предупреждения при достижении 80% (внимание) и 95% (исчерпано) 5-часового лимита с таймером сброса.
|
|
@@ -17,11 +17,21 @@
|
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
19
|
<p align="center">
|
|
20
|
-
<a href="
|
|
20
|
+
<a href="README.md"><b>🇬🇧 English</b></a> •
|
|
21
21
|
<a href="README.ru.md"><b>🇷🇺 Русский</b></a> •
|
|
22
22
|
<a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
|
|
23
23
|
</p>
|
|
24
24
|
|
|
25
|
+
<table align="center">
|
|
26
|
+
<tr>
|
|
27
|
+
<td align="center">
|
|
28
|
+
⭐ <strong>如果您喜欢这个插件,请在 GitHub 上为它点亮 Star</strong> — 这能让我知道插件对您有用,并鼓励我继续开发和维护它。
|
|
29
|
+
<br><br>
|
|
30
|
+
🐛 <strong>如果您发现 Bug 或希望增加功能</strong>,请使用任意语言在 GitHub 上提交 Issue — 我会评估您的建议,并在后续版本中实现有价值的改进。
|
|
31
|
+
</td>
|
|
32
|
+
</tr>
|
|
33
|
+
</table>
|
|
34
|
+
|
|
25
35
|
</div>
|
|
26
36
|
|
|
27
37
|
---
|
|
@@ -37,6 +47,9 @@
|
|
|
37
47
|
4. **安全凭据隔离**:禁止在明文配置中直接填写密钥。
|
|
38
48
|
|
|
39
49
|
**`@goodandready/dsh-clinebot`** 完美解决以上痛点:
|
|
50
|
+
* 🚀 **应用内一键更新**:直接在 DSH 界面检查并升级插件,或通过受保护的 `/dsh-clinebot/update` 进行本地安全更新。
|
|
51
|
+
* ⚡ **SWR 配额与健康状态缓存**:状态查询毫秒级响应(<2ms),并在后台静默更新,不阻塞前端渲染。
|
|
52
|
+
* 🔀 **智能配额故障转移 (Smart Failover)**:多账号池自动轮询,避开耗尽账号并在 `resetsAt` 到达后自动恢复。
|
|
40
53
|
* 🖥️ **专属设置大页**:在 DSH 设置中提供全宽独立页面(`设置 → ClineBot`)。
|
|
41
54
|
* 🔄 **订阅模型动态同步**:从官方 `GET /api/v1/users/me/plan` 自动提取真实包含模型,一键原子级同步至 DSH 提供商配置,无需等待插件更新。
|
|
42
55
|
* ⚠️ **额度耗尽实时预警**:当 5 小时滑动窗口达到 80%(警告黄色)和 95%(即将耗尽红色)时展示醒目预警横幅与重置倒计时。
|
package/lib/client.js
CHANGED
|
@@ -9,7 +9,7 @@ window.__ModuleLoader__.load({
|
|
|
9
9
|
const SUBTITLE = 'ClinePass subscription ($9.99/mo) with curated & custom models.'
|
|
10
10
|
const ROUTE_PREFIX = '/dsh-clinebot'
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
const en = {
|
|
13
13
|
title: 'ClineBot',
|
|
14
14
|
subtitle: 'ClinePass subscription ($9.99/mo) with curated & custom models.',
|
|
15
15
|
'settings.loading': 'Loading ClineBot settings…',
|
|
@@ -89,88 +89,98 @@ window.__ModuleLoader__.load({
|
|
|
89
89
|
'diag.unregister_btn': 'Unregister from DSH',
|
|
90
90
|
'diag.unregistering': 'Removing…',
|
|
91
91
|
'diag.unregistered_msg': 'Provider removed from DSH Models',
|
|
92
|
+
'update.available': 'Update available: v{latestVersion} (current: v{currentVersion})',
|
|
93
|
+
'update.btn': 'Update Now',
|
|
94
|
+
'update.updating': 'Updating…',
|
|
95
|
+
'update.done': 'Successfully updated to v{version}! Please restart DSH.',
|
|
96
|
+
'update.failed': 'Update failed: {error}',
|
|
92
97
|
}
|
|
93
98
|
|
|
94
|
-
const
|
|
99
|
+
const zh = {
|
|
95
100
|
title: 'ClineBot',
|
|
96
|
-
subtitle: '
|
|
97
|
-
'settings.loading': '
|
|
98
|
-
'settings.retry': '
|
|
99
|
-
'settings.unavailable': '
|
|
100
|
-
'header.title': '
|
|
101
|
-
'header.sub': '
|
|
102
|
-
'badge.online': '
|
|
103
|
-
'badge.offline': '
|
|
104
|
-
'badge.key_ok': '
|
|
105
|
-
'badge.key_missing': '
|
|
106
|
-
'badge.registered': '
|
|
107
|
-
'badge.not_registered': '
|
|
108
|
-
'key.title': '🔑
|
|
109
|
-
'key.desc': '
|
|
101
|
+
subtitle: 'ClinePass 订阅服务 ($9.99/月),包含精选与自定义模型。',
|
|
102
|
+
'settings.loading': '正在加载 ClineBot 设置…',
|
|
103
|
+
'settings.retry': '重试',
|
|
104
|
+
'settings.unavailable': 'ClineBot 设置不可用(宿主命名空间尚未就绪)。',
|
|
105
|
+
'header.title': 'ClineBot (ClinePass) 模型服务商',
|
|
106
|
+
'header.sub': '通过 ClinePass 订阅 ($9.99/月) 连接主流推理与编程模型。完全兼容 OpenAI 接口,自动追踪滚动限额并在 DSH 中即时生效。',
|
|
107
|
+
'badge.online': '服务在线 ({latency} ms)',
|
|
108
|
+
'badge.offline': '主机无法连接',
|
|
109
|
+
'badge.key_ok': '密钥正常 ({source})',
|
|
110
|
+
'badge.key_missing': '未配置密钥',
|
|
111
|
+
'badge.registered': '已接入 DSH ({count} 个模型)',
|
|
112
|
+
'badge.not_registered': '未接入 DSH',
|
|
113
|
+
'key.title': '🔑 授权与 API 密钥',
|
|
114
|
+
'key.desc': '密钥直接加密保存在 DSH 凭据管理器 (~/.dsh/.credentials.yaml),绝不会以明文形式暴露在普通配置文件中。',
|
|
110
115
|
'key.placeholder_has': '••••••••••••••••••••••••',
|
|
111
|
-
'key.placeholder_empty': '
|
|
112
|
-
'key.show': '
|
|
113
|
-
'key.hide': '
|
|
114
|
-
'key.save': '
|
|
115
|
-
'key.saving': '
|
|
116
|
-
'key.login_fast': '🚀
|
|
117
|
-
'key.login_waiting': '
|
|
118
|
-
'key.env_label': '
|
|
119
|
-
'key.get_key': '
|
|
120
|
-
'key.saved_msg': '
|
|
121
|
-
'key.empty_err': '
|
|
122
|
-
'accounts.title': '👥
|
|
123
|
-
'accounts.desc': '
|
|
124
|
-
'accounts.active_badge': '
|
|
125
|
-
'accounts.pinned_badge': '
|
|
126
|
-
'accounts.pin_btn': '
|
|
127
|
-
'accounts.add_btn': '+
|
|
128
|
-
'accounts.label_placeholder': '
|
|
116
|
+
'key.placeholder_empty': '粘贴 ClinePass 密钥 (cline_...)',
|
|
117
|
+
'key.show': '显示',
|
|
118
|
+
'key.hide': '隐藏',
|
|
119
|
+
'key.save': '保存密钥',
|
|
120
|
+
'key.saving': '正在保存…',
|
|
121
|
+
'key.login_fast': '🚀 浏览器快速登录',
|
|
122
|
+
'key.login_waiting': '等待在浏览器中完成授权…',
|
|
123
|
+
'key.env_label': '凭据环境变量名称:',
|
|
124
|
+
'key.get_key': '前往 app.cline.bot 控制台获取密钥 ↗',
|
|
125
|
+
'key.saved_msg': '密钥已成功保存至 DSH 凭据服务。验证状态:{status}',
|
|
126
|
+
'key.empty_err': '保存前请输入 API 密钥',
|
|
127
|
+
'accounts.title': '👥 多账号故障转移与轮换池 (Failover)',
|
|
128
|
+
'accounts.desc': '添加备用账号。当某个账号触发 429 速率限制或配额耗尽时,ClineBot 会智能无缝切换至剩余额度最高的备用账号。',
|
|
129
|
+
'accounts.active_badge': '当前活动',
|
|
130
|
+
'accounts.pinned_badge': '固定使用',
|
|
131
|
+
'accounts.pin_btn': '设为活动',
|
|
132
|
+
'accounts.add_btn': '+ 添加账号',
|
|
133
|
+
'accounts.label_placeholder': '账号标签(例如:工作账号)',
|
|
129
134
|
'accounts.env_placeholder': 'CLINEBOT_API_KEY_2',
|
|
130
|
-
'quota.title': '📊
|
|
131
|
-
'quota.desc': '
|
|
132
|
-
'quota.account': '
|
|
133
|
-
'quota.refresh': '
|
|
134
|
-
'quota.refreshing': '
|
|
135
|
-
'quota.refreshed_msg': '
|
|
136
|
-
'quota.window_5h': '⏱ 5
|
|
137
|
-
'quota.window_weekly': '📅
|
|
138
|
-
'quota.used': '{pct}%
|
|
139
|
-
'quota.remaining': '
|
|
140
|
-
'quota.reset_at': '
|
|
141
|
-
'models.title': '🎯
|
|
142
|
-
'models.desc': '
|
|
143
|
-
'models.sync': '🔄
|
|
144
|
-
'models.syncing': '
|
|
145
|
-
'models.synced_msg': '
|
|
146
|
-
'models.all': '
|
|
147
|
-
'models.vision': '
|
|
148
|
-
'models.coding': '
|
|
149
|
-
'models.recommended': '
|
|
150
|
-
'models.th_active': '
|
|
151
|
-
'models.th_name': '
|
|
152
|
-
'models.th_id': '
|
|
153
|
-
'models.th_ctx': '
|
|
154
|
-
'models.th_caps': '
|
|
155
|
-
'models.star': '
|
|
156
|
-
'models.auto_new': '
|
|
157
|
-
'stats.title': '📈
|
|
158
|
-
'stats.desc': '
|
|
159
|
-
'stats.requests': '
|
|
160
|
-
'stats.tokens': '
|
|
161
|
-
'stats.latency': '
|
|
162
|
-
'stats.last_req': '
|
|
163
|
-
'diag.title': '⚡
|
|
164
|
-
'diag.desc': '
|
|
165
|
-
'diag.smoke_btn': '
|
|
166
|
-
'diag.smoke_testing': '
|
|
167
|
-
'diag.smoke_ok': '
|
|
168
|
-
'diag.resync_btn': '
|
|
169
|
-
'diag.resyncing': '
|
|
170
|
-
'diag.resynced_msg': '
|
|
171
|
-
'diag.unregister_btn': '
|
|
172
|
-
'diag.unregistering': '
|
|
173
|
-
'diag.unregistered_msg': '
|
|
135
|
+
'quota.title': '📊 ClinePass 用量配额与限额监控',
|
|
136
|
+
'quota.desc': '来自 ClinePass 官方的实时滚动窗口用量限额',
|
|
137
|
+
'quota.account': '账号:{email} · 套餐:{plan}',
|
|
138
|
+
'quota.refresh': '刷新配额',
|
|
139
|
+
'quota.refreshing': '正在刷新…',
|
|
140
|
+
'quota.refreshed_msg': '订阅用量配额已刷新',
|
|
141
|
+
'quota.window_5h': '⏱ 5 小时滚动限额',
|
|
142
|
+
'quota.window_weekly': '📅 每周滚动窗口',
|
|
143
|
+
'quota.used': '已使用 {pct}%',
|
|
144
|
+
'quota.remaining': '剩余可用:{pct}%',
|
|
145
|
+
'quota.reset_at': '重置时间:{time}',
|
|
146
|
+
'models.title': '🎯 DSH 模型选择列表(已启用 {enabled}/{total})',
|
|
147
|
+
'models.desc': '模型目录与 ClinePass 官方订阅实时同步。新订阅模型默认自动启用。取消勾选可在 DSH 对话模型列表中隐藏不需要的模型。',
|
|
148
|
+
'models.sync': '🔄 同步订阅模型',
|
|
149
|
+
'models.syncing': '正在同步…',
|
|
150
|
+
'models.synced_msg': '已成功同步订阅模型:共 {total} 个(发现 {discovered} 个新模型)',
|
|
151
|
+
'models.all': '全部模型',
|
|
152
|
+
'models.vision': '仅视觉 (Vision)',
|
|
153
|
+
'models.coding': '仅编程 (Coding)',
|
|
154
|
+
'models.recommended': '推荐模型',
|
|
155
|
+
'models.th_active': '启用',
|
|
156
|
+
'models.th_name': '模型名称',
|
|
157
|
+
'models.th_id': '模型标识符 (ID)',
|
|
158
|
+
'models.th_ctx': '上下文窗口',
|
|
159
|
+
'models.th_caps': '特性能力',
|
|
160
|
+
'models.star': '推荐',
|
|
161
|
+
'models.auto_new': '新上线',
|
|
162
|
+
'stats.title': '📈 当前 DSH 会话请求遥测',
|
|
163
|
+
'stats.desc': '本地实时监控请求次数、网络延迟以及通过 ClinePass 消耗的估算 Token 数量。',
|
|
164
|
+
'stats.requests': '成功 / 总请求次数',
|
|
165
|
+
'stats.tokens': '会话累计 Token 估算',
|
|
166
|
+
'stats.latency': '最近一次请求延迟',
|
|
167
|
+
'stats.last_req': '最近请求时间',
|
|
168
|
+
'diag.title': '⚡ 诊断工具与服务接入',
|
|
169
|
+
'diag.desc': '配置 API 密钥后,ClineBot 会自动向 DSH 注册模型服务。您可以在此进行连通性测试或强制重新同步。',
|
|
170
|
+
'diag.smoke_btn': '执行连通性冒烟测试 (Ping)',
|
|
171
|
+
'diag.smoke_testing': '正在测试…',
|
|
172
|
+
'diag.smoke_ok': '冒烟测试通过!网络延迟:{latency} ms',
|
|
173
|
+
'diag.resync_btn': '强制重新同步到 DSH',
|
|
174
|
+
'diag.resyncing': '正在同步…',
|
|
175
|
+
'diag.resynced_msg': '模型服务已重新同步到 DSH ({count} 个模型)',
|
|
176
|
+
'diag.unregister_btn': '从 DSH 注销该服务',
|
|
177
|
+
'diag.unregistering': '正在注销…',
|
|
178
|
+
'diag.unregistered_msg': '已从 DSH 模型注册表中移除',
|
|
179
|
+
'update.available': '发现新版本 v{latestVersion}(当前版本 v{currentVersion})',
|
|
180
|
+
'update.btn': '立即更新',
|
|
181
|
+
'update.updating': '正在更新…',
|
|
182
|
+
'update.done': '更新成功至 v{version}!请重启 DSH 服务生效。',
|
|
183
|
+
'update.failed': '更新失败:{error}',
|
|
174
184
|
}
|
|
175
185
|
|
|
176
186
|
function makeT(dict, fallback) {
|
|
@@ -198,7 +208,14 @@ window.__ModuleLoader__.load({
|
|
|
198
208
|
})
|
|
199
209
|
)
|
|
200
210
|
}
|
|
201
|
-
|
|
211
|
+
let ChevronIcon = null
|
|
212
|
+
try {
|
|
213
|
+
const primitives = require('@deepseek-ai/dsh-client-ui-primitives')
|
|
214
|
+
ChevronIcon = primitives && (primitives.IconChevronDownOutline14 || primitives.IconChevronDownOutline)
|
|
215
|
+
} catch (_) {
|
|
216
|
+
ChevronIcon = null
|
|
217
|
+
}
|
|
218
|
+
const Chevron = ChevronIcon || FallbackChevron
|
|
202
219
|
|
|
203
220
|
function ensureCss() {
|
|
204
221
|
if (typeof document === 'undefined') return
|
|
@@ -208,6 +225,8 @@ window.__ModuleLoader__.load({
|
|
|
208
225
|
style.dataset.dshPlugin = NS
|
|
209
226
|
style.textContent = `
|
|
210
227
|
.cb-page{display:flex;flex-direction:column;gap:20px;padding:8px 0 32px;max-width:960px}
|
|
228
|
+
.cb-chevron{display:inline-flex;align-items:center;justify-content:center;transition:transform .16s ease;color:var(--dsw-alias-label-tertiary)}
|
|
229
|
+
.cb-chevron-open{transform:rotate(180deg)}
|
|
211
230
|
.cb-header{display:flex;flex-direction:column;gap:8px;padding-bottom:16px;border-bottom:1px solid var(--dsw-alias-border-l2)}
|
|
212
231
|
.cb-page-title{font-size:22px;font-weight:700;color:var(--dsw-alias-label-primary);display:flex;align-items:center;gap:10px}
|
|
213
232
|
.cb-page-sub{font-size:14px;color:var(--dsw-alias-label-secondary);line-height:1.5}
|
|
@@ -220,9 +239,9 @@ window.__ModuleLoader__.load({
|
|
|
220
239
|
.cb-grid-2{display:grid;grid-template-columns:repeat(auto-fit, minmax(280px, 1fr));gap:14px}
|
|
221
240
|
|
|
222
241
|
.cb-badge{font-size:12px;padding:3px 10px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);display:inline-flex;align-items:center;gap:5px;font-weight:500}
|
|
223
|
-
.cb-badge-ok{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary);background:
|
|
224
|
-
.cb-badge-warn{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary);background:
|
|
225
|
-
.cb-badge-bad{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary);background:
|
|
242
|
+
.cb-badge-ok{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 8%, transparent)}
|
|
243
|
+
.cb-badge-warn{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary);background:color-mix(in srgb, var(--dsw-alias-state-warning-primary) 8%, transparent)}
|
|
244
|
+
.cb-badge-bad{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 8%, transparent)}
|
|
226
245
|
|
|
227
246
|
.cb-input{height:36px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;width:100%;box-sizing:border-box}
|
|
228
247
|
.cb-input:focus{outline:none;border-color:var(--dsw-alias-state-brand-primary)}
|
|
@@ -232,8 +251,8 @@ window.__ModuleLoader__.load({
|
|
|
232
251
|
.cb-btn:hover:not(:disabled){background:var(--dsw-alias-bg-layer-4, var(--dsw-alias-bg-layer-2));border-color:var(--dsw-alias-label-dimmed, var(--dsw-alias-border-l2))}
|
|
233
252
|
.cb-btn-primary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3);border-color:transparent}
|
|
234
253
|
.cb-btn-primary:hover:not(:disabled){background:var(--dsw-alias-label-primary) !important;color:var(--dsw-alias-bg-layer-3) !important;opacity:0.88;visibility:visible !important}
|
|
235
|
-
.cb-btn-danger{color:var(--dsw-alias-state-error-primary);border-color:
|
|
236
|
-
.cb-btn-danger:hover:not(:disabled){background:
|
|
254
|
+
.cb-btn-danger{color:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 30%, transparent)}
|
|
255
|
+
.cb-btn-danger:hover:not(:disabled){background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent) !important;border-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 50%, transparent)}
|
|
237
256
|
.cb-btn-disabled{opacity:0.5;cursor:not-allowed}
|
|
238
257
|
|
|
239
258
|
.cb-bar-container{display:flex;flex-direction:column;gap:6px;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-2)}
|
|
@@ -247,11 +266,11 @@ window.__ModuleLoader__.load({
|
|
|
247
266
|
.cb-table td{padding:10px;border-bottom:1px solid var(--dsw-alias-border-l2);font-size:13px;color:var(--dsw-alias-label-primary)}
|
|
248
267
|
.cb-table tr:hover{background:var(--dsw-alias-bg-layer-2)}
|
|
249
268
|
|
|
250
|
-
.cb-alert-ok{padding:10px 14px;border-radius:8px;background:
|
|
251
|
-
.cb-alert-bad{padding:10px 14px;border-radius:8px;background:
|
|
252
|
-
.cb-alert-err{padding:10px 14px;border-radius:8px;background:
|
|
253
|
-
.cb-banner-warning{padding:12px 16px;border-radius:8px;background:
|
|
254
|
-
.cb-banner-exhausted{padding:12px 16px;border-radius:8px;background:
|
|
269
|
+
.cb-alert-ok{padding:10px 14px;border-radius:8px;background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent);color:var(--dsw-alias-state-success-primary);font-size:13px}
|
|
270
|
+
.cb-alert-bad{padding:10px 14px;border-radius:8px;background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);color:var(--dsw-alias-state-error-primary);font-size:13px}
|
|
271
|
+
.cb-alert-err{padding:10px 14px;border-radius:8px;background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);color:var(--dsw-alias-state-error-primary);font-size:13px}
|
|
272
|
+
.cb-banner-warning{padding:12px 16px;border-radius:8px;background:color-mix(in srgb, var(--dsw-alias-state-warning-primary) 12%, transparent);border:1px solid var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary);font-size:13px;display:flex;align-items:center;gap:10px;font-weight:500}
|
|
273
|
+
.cb-banner-exhausted{padding:12px 16px;border-radius:8px;background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent);border:1px solid var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary);font-size:13px;display:flex;align-items:center;gap:10px;font-weight:600}
|
|
255
274
|
.cb-stat-box{padding:12px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-2);display:flex;flex-direction:column;gap:4px}
|
|
256
275
|
.cb-stat-val{font-size:18px;font-weight:700;color:var(--dsw-alias-label-primary)}
|
|
257
276
|
.cb-stat-lbl{font-size:12px;color:var(--dsw-alias-label-secondary)}
|
|
@@ -353,7 +372,7 @@ window.__ModuleLoader__.load({
|
|
|
353
372
|
|
|
354
373
|
function SettingsPage(props) {
|
|
355
374
|
const ctx = props?.ctx
|
|
356
|
-
const t = props?.t || makeT(
|
|
375
|
+
const t = props?.t || makeT(en, en)
|
|
357
376
|
|
|
358
377
|
const scope = React.useMemo(() => {
|
|
359
378
|
const s = (ctx?.get && ctx.get('lanSettings')) || ctx?.settingsScope
|
|
@@ -577,7 +596,11 @@ window.__ModuleLoader__.load({
|
|
|
577
596
|
// Pin active account
|
|
578
597
|
async function handlePinAccount(accountEnv) {
|
|
579
598
|
if (scope && snapshotStatus === "ready") {
|
|
580
|
-
try {
|
|
599
|
+
try {
|
|
600
|
+
await scope.set("activeAccount", accountEnv)
|
|
601
|
+
} catch (e) {
|
|
602
|
+
console.warn('[dsh-clinebot] Failed to set activeAccount on scope:', e)
|
|
603
|
+
}
|
|
581
604
|
}
|
|
582
605
|
setBusy(`pin-${accountEnv}`)
|
|
583
606
|
setErr('')
|
|
@@ -610,15 +633,26 @@ window.__ModuleLoader__.load({
|
|
|
610
633
|
if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current)
|
|
611
634
|
debounceTimerRef.current = setTimeout(async () => {
|
|
612
635
|
if (scope && snapshotStatus === 'ready') {
|
|
613
|
-
try {
|
|
636
|
+
try {
|
|
637
|
+
await scope.set('disabledModels', nextDisabled)
|
|
638
|
+
} catch (e) {
|
|
639
|
+
console.warn('[dsh-clinebot] Failed to set disabledModels on scope:', e)
|
|
640
|
+
}
|
|
614
641
|
}
|
|
615
642
|
try {
|
|
616
|
-
await fetch(`${ROUTE_PREFIX}/models/toggle`, {
|
|
643
|
+
const res = await fetch(`${ROUTE_PREFIX}/models/toggle`, {
|
|
617
644
|
method: 'POST',
|
|
618
645
|
headers: { 'Content-Type': 'application/json' },
|
|
619
646
|
body: JSON.stringify({ disabledModels: nextDisabled }),
|
|
620
647
|
})
|
|
621
|
-
|
|
648
|
+
const data = await res.json().catch(() => ({}))
|
|
649
|
+
if (!data.ok) {
|
|
650
|
+
setErr(data.error || 'Failed to persist disabled models')
|
|
651
|
+
}
|
|
652
|
+
} catch (e) {
|
|
653
|
+
console.warn('[dsh-clinebot] Failed to persist disabled models:', e)
|
|
654
|
+
setErr(String(e.message || e))
|
|
655
|
+
}
|
|
622
656
|
}, 280)
|
|
623
657
|
}
|
|
624
658
|
|
|
@@ -1178,7 +1212,7 @@ window.__ModuleLoader__.load({
|
|
|
1178
1212
|
// Accordion item for Plugins section
|
|
1179
1213
|
function PluginCard(props) {
|
|
1180
1214
|
const [open, setOpen] = React.useState(false)
|
|
1181
|
-
const t = props?.t || makeT(
|
|
1215
|
+
const t = props?.t || makeT(en, en)
|
|
1182
1216
|
React.useEffect(() => {
|
|
1183
1217
|
ensureCss()
|
|
1184
1218
|
}, [])
|
|
@@ -1209,7 +1243,7 @@ window.__ModuleLoader__.load({
|
|
|
1209
1243
|
React.createElement('div', { style: { fontWeight: 600, fontSize: '15px' } }, t('title')),
|
|
1210
1244
|
React.createElement('div', { style: { fontSize: '13px', color: 'var(--dsw-alias-label-secondary)' } }, t('subtitle'))
|
|
1211
1245
|
),
|
|
1212
|
-
React.createElement('span', {
|
|
1246
|
+
React.createElement('span', { className: 'cb-chevron' + (open ? ' cb-chevron-open' : '') },
|
|
1213
1247
|
React.createElement(Chevron)
|
|
1214
1248
|
)
|
|
1215
1249
|
),
|
|
@@ -1245,7 +1279,9 @@ window.__ModuleLoader__.load({
|
|
|
1245
1279
|
try {
|
|
1246
1280
|
const s = (ctx?.get && ctx.get('lanSettings')) || ctx?.settingsScope
|
|
1247
1281
|
s?.describe?.()?.load?.()
|
|
1248
|
-
} catch (
|
|
1282
|
+
} catch (e) {
|
|
1283
|
+
console.debug?.('[dsh-clinebot] Polling settings mirror:', e)
|
|
1284
|
+
}
|
|
1249
1285
|
}, 1000)
|
|
1250
1286
|
return () => clearInterval(timer)
|
|
1251
1287
|
}
|
|
@@ -1261,12 +1297,12 @@ window.__ModuleLoader__.load({
|
|
|
1261
1297
|
if (ctx.locale && ctx.locale.register) {
|
|
1262
1298
|
if (typeof ctx.effect === 'function') {
|
|
1263
1299
|
ctx.effect(() => {
|
|
1264
|
-
const undo = [addLocale('en', en), addLocale('
|
|
1300
|
+
const undo = [addLocale('en', en), addLocale('zh', zh)]
|
|
1265
1301
|
return () => { for (const off of undo) off() }
|
|
1266
1302
|
}, 'dsh-clinebot: dictionaries')
|
|
1267
1303
|
} else {
|
|
1268
1304
|
addLocale('en', en)
|
|
1269
|
-
addLocale('
|
|
1305
|
+
addLocale('zh', zh)
|
|
1270
1306
|
}
|
|
1271
1307
|
}
|
|
1272
1308
|
|