@goodandready/dsh-clinebot 0.3.7 → 0.3.9

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/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
- const en = {
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 ru = {
99
+ const zh = {
95
100
  title: 'ClineBot',
96
- subtitle: 'Подписка ClinePass ($9.99/мес) с каталогом проверенных моделей.',
97
- 'settings.loading': 'Загрузка настроек ClineBot…',
98
- 'settings.retry': 'Повторить попытку',
99
- 'settings.unavailable': 'Настройки ClineBot недоступны (пространство хоста ещё не готово).',
100
- 'header.title': 'Провайдер ClineBot (ClinePass)',
101
- 'header.sub': 'Подключение моделей по подписке ClinePass ($9.99/мес). OpenAI API совместимый, автоматическое отслеживание лимитов и моментальная авторегистрация в DSH.',
102
- 'badge.online': 'Сервер доступен ({latency} мс)',
103
- 'badge.offline': 'Недоступен',
104
- 'badge.key_ok': 'Ключ ✓ ({source})',
105
- 'badge.key_missing': 'Ключ отсутствует',
106
- 'badge.registered': 'Зарегистрирован в DSH ({count} мод.)',
107
- 'badge.not_registered': 'Не зарегистрирован',
108
- 'key.title': '🔑 Авторизация и API-ключ',
109
- 'key.desc': 'Ключ сохраняется в системное хранилище DSH Credentials (~/.dsh/.credentials.yaml) и никогда не попадает в открытые файлы настроек.',
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': 'Вставьте ключ ClinePass (cline_...)',
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': 'Получить ключ в консоли app.cline.bot ↗',
120
- 'key.saved_msg': 'Ключ сохранён в DSH Credentials. Валидация: {status}',
121
- 'key.empty_err': 'Введите API-ключ перед сохранением',
122
- 'accounts.title': '👥 Пул аккаунтов и авторотация (Failover)',
123
- 'accounts.desc': 'Добавьте запасные аккаунты. При исчерпании лимитов (429) ClineBot автоматически переключит запрос на следующий доступный аккаунт.',
124
- 'accounts.active_badge': 'Активен',
125
- 'accounts.pinned_badge': 'Закреплен',
126
- 'accounts.pin_btn': 'Сделать активным',
127
- 'accounts.add_btn': '+ Добавить аккаунт',
128
- 'accounts.label_placeholder': 'Название (например, Work)',
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': '📊 Остаток лимитов подписки (ClinePass Quota)',
131
- 'quota.desc': 'Официальные лимиты скользящих окон запросов ClinePass',
132
- 'quota.account': 'Аккаунт: {email} · Тариф: {plan}',
133
- 'quota.refresh': 'Обновить квоты',
134
- 'quota.refreshing': 'Обновление…',
135
- 'quota.refreshed_msg': 'Лимиты подписки обновлены',
136
- 'quota.window_5h': '⏱ 5-часовое скользящее окно (Rolling Limit)',
137
- 'quota.window_weekly': '📅 Недельное окно (Weekly Window)',
138
- 'quota.used': '{pct}% использовано',
139
- 'quota.remaining': 'Осталось: {pct}%',
140
- 'quota.reset_at': 'Сброс: {time}',
141
- 'models.title': '🎯 Модели в пикере DSH ({enabled} из {total} включено)',
142
- 'models.desc': 'Список синхронизируется напрямую из подписки ClinePass. Новые модели появляются автоматически. Снимите галочку, чтобы скрыть ненужную модель из чата.',
143
- 'models.sync': '🔄 Проверить модели плана',
144
- 'models.syncing': 'Проверка…',
145
- 'models.synced_msg': 'Синхронизировано моделей подписки: {total} ({discovered} получено напрямую из плана)',
146
- 'models.all': 'Все',
147
- 'models.vision': 'Только Vision',
148
- 'models.coding': 'Только Кодинг',
149
- 'models.recommended': 'Рекомендованные',
150
- 'models.th_active': 'Вкл',
151
- 'models.th_name': 'Название модели',
152
- 'models.th_id': 'Model ID',
153
- 'models.th_ctx': 'Контекст',
154
- 'models.th_caps': 'Возможности',
155
- 'models.star': 'Star',
156
- 'models.auto_new': 'Новая',
157
- 'stats.title': '📈 Статистика текущей сессии DSH',
158
- 'stats.desc': 'Локальный мониторинг запросов, задержек сети и ориентировочного расхода токенов через ClinePass.',
159
- 'stats.requests': 'Успешных запросов / Всего',
160
- 'stats.tokens': 'Оценка токенов сессии',
161
- 'stats.latency': 'Последняя задержка (Latency)',
162
- 'stats.last_req': 'Время последнего запроса',
163
- 'diag.title': '⚡ Диагностика и авторегистрация в DSH',
164
- 'diag.desc': 'ClineBot автоматически регистрируется в DSH Models при наличии ключа. Вы можете запустить проверку связи или принудительно пересинхронизировать модели.',
165
- 'diag.smoke_btn': 'Запустить Smoke Test (Ping)',
166
- 'diag.smoke_testing': 'Тестирование…',
167
- 'diag.smoke_ok': 'Smoke тест пройден! Задержка: {latency} мс',
168
- 'diag.resync_btn': 'Принудительно обновить в DSH',
169
- 'diag.resyncing': 'Обновление…',
170
- 'diag.resynced_msg': 'Провайдер обновлен в DSH Models ({count} моделей)',
171
- 'diag.unregister_btn': 'Удалить из DSH Models',
172
- 'diag.unregistering': 'Удаление…',
173
- 'diag.unregistered_msg': 'Провайдер удален из DSH Models',
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) {
@@ -353,7 +363,7 @@ window.__ModuleLoader__.load({
353
363
 
354
364
  function SettingsPage(props) {
355
365
  const ctx = props?.ctx
356
- const t = props?.t || makeT(ru, en)
366
+ const t = props?.t || makeT(en, en)
357
367
 
358
368
  const scope = React.useMemo(() => {
359
369
  const s = (ctx?.get && ctx.get('lanSettings')) || ctx?.settingsScope
@@ -597,8 +607,33 @@ window.__ModuleLoader__.load({
597
607
  }
598
608
  }
599
609
 
600
- // Toggle model exclusion (disabledModels logic)
601
- async function handleToggleModel(id) {
610
+ // Debounced persistence for model exclusion to avoid stuttering and request thrashing
611
+ const debounceTimerRef = typeof React.useRef === 'function' ? React.useRef(null) : { current: null }
612
+
613
+ React.useEffect(() => {
614
+ return () => {
615
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current)
616
+ }
617
+ }, [])
618
+
619
+ function debounceSaveDisabledModels(nextDisabled) {
620
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current)
621
+ debounceTimerRef.current = setTimeout(async () => {
622
+ if (scope && snapshotStatus === 'ready') {
623
+ try { await scope.set('disabledModels', nextDisabled) } catch (_) {}
624
+ }
625
+ try {
626
+ await fetch(`${ROUTE_PREFIX}/models/toggle`, {
627
+ method: 'POST',
628
+ headers: { 'Content-Type': 'application/json' },
629
+ body: JSON.stringify({ disabledModels: nextDisabled }),
630
+ })
631
+ } catch {}
632
+ }, 280)
633
+ }
634
+
635
+ // Toggle model exclusion (disabledModels logic) with instant UI state update & debounced network persist
636
+ function handleToggleModel(id) {
602
637
  if (!draft) return
603
638
  const currentDisabled = new Set(draft.disabledModels || [])
604
639
  if (currentDisabled.has(id)) {
@@ -613,21 +648,11 @@ window.__ModuleLoader__.load({
613
648
  const nextEnabled = allIds.filter((mId) => !currentDisabled.has(mId))
614
649
 
615
650
  setDraft({ ...draft, disabledModels: nextDisabled, enabledModels: nextEnabled })
616
-
617
- if (scope && snapshotStatus === 'ready') {
618
- try { await scope.set('disabledModels', nextDisabled) } catch (_) {}
619
- }
620
- try {
621
- await fetch(`${ROUTE_PREFIX}/models/toggle`, {
622
- method: 'POST',
623
- headers: { 'Content-Type': 'application/json' },
624
- body: JSON.stringify({ disabledModels: nextDisabled }),
625
- })
626
- } catch {}
651
+ debounceSaveDisabledModels(nextDisabled)
627
652
  }
628
653
 
629
- // Select all / filter models
630
- async function handleSetModelsFilter(type) {
654
+ // Select all / filter models with instant UI state update & debounced network persist
655
+ function handleSetModelsFilter(type) {
631
656
  if (!status?.availableModels) return
632
657
  const all = status.availableModels
633
658
  let allowed = new Set()
@@ -645,16 +670,7 @@ window.__ModuleLoader__.load({
645
670
  const nextEnabled = Array.from(allowed)
646
671
 
647
672
  setDraft({ ...draft, disabledModels: nextDisabled, enabledModels: nextEnabled })
648
- if (scope && snapshotStatus === 'ready') {
649
- try { await scope.set('disabledModels', nextDisabled) } catch (_) {}
650
- }
651
- try {
652
- await fetch(`${ROUTE_PREFIX}/models/toggle`, {
653
- method: 'POST',
654
- headers: { 'Content-Type': 'application/json' },
655
- body: JSON.stringify({ disabledModels: nextDisabled }),
656
- })
657
- } catch {}
673
+ debounceSaveDisabledModels(nextDisabled)
658
674
  }
659
675
 
660
676
  if (snapshotStatus === 'unavailable') {
@@ -1172,7 +1188,7 @@ window.__ModuleLoader__.load({
1172
1188
  // Accordion item for Plugins section
1173
1189
  function PluginCard(props) {
1174
1190
  const [open, setOpen] = React.useState(false)
1175
- const t = props?.t || makeT(ru, en)
1191
+ const t = props?.t || makeT(en, en)
1176
1192
  React.useEffect(() => {
1177
1193
  ensureCss()
1178
1194
  }, [])
@@ -1255,7 +1271,7 @@ window.__ModuleLoader__.load({
1255
1271
  if (ctx.locale && ctx.locale.register) {
1256
1272
  if (typeof ctx.effect === 'function') {
1257
1273
  ctx.effect(() => {
1258
- const undo = [addLocale('en', en), addLocale('ru', ru)]
1274
+ const undo = [addLocale('en', en), addLocale('zh', zh)]
1259
1275
  return () => { for (const off of undo) off() }
1260
1276
  }, 'dsh-clinebot: dictionaries')
1261
1277
  } else {