@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/CHANGELOG.md +24 -0
- package/README.md +15 -2
- package/README.ru.md +199 -0
- package/README.zh.md +201 -0
- package/docs/README.ru.md +14 -1
- package/docs/README.zh.md +14 -1
- package/docs/design/DESIGN.md +15 -1
- package/lib/client.js +122 -106
- package/lib/cline-client.js +340 -131
- package/lib/index.js +143 -109
- package/lib/models.js +69 -0
- package/lib/updater.js +245 -0
- package/package.json +1 -1
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) {
|
|
@@ -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(
|
|
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
|
-
//
|
|
601
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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('
|
|
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 {
|