@goodandready/dsh-clinebot 0.6.4 → 0.6.5
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 +9 -0
- package/README.md +1 -1
- package/README.ru.md +1 -1
- package/README.zh.md +1 -1
- package/lib/client.js +24 -14
- package/lib/slash-command.js +30 -5
- package/lib/transport/cline-transport.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,15 @@ 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.6.5] - 2026-10-21
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Grouped model table**: Settings now separates `Included in plan`, `Other models` and `Custom models` with per-group counts.
|
|
12
|
+
- **Extended `/cline`**: `/cline cost` (`/cline budget`) reports estimated UTC day/month spend and guard state; `/cline usage` falls through to the quota report, which now includes the monthly window.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- Headless/web fixture profiles no longer carry the removed `transport` and `proxyMode` fields.
|
|
16
|
+
|
|
8
17
|
## [0.6.4] - 2026-10-21
|
|
9
18
|
|
|
10
19
|
### Added
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
<h3>Native ClineBot / ClinePass Provider Companion for DeepSeek Harness</h3>
|
|
6
6
|
|
|
7
|
-
<p><b>0.6.
|
|
7
|
+
<p><b>0.6.5</b> groups the model table by plan availability and adds <code>/cline cost</code> / monthly quota reporting on top of the analytics work. Chat runs on the native <code>ctx.llm</code> adapter and requires DSH <code>0.2.1-rc.2</code>; the legacy <code>llm-pi-ai</code>, loopback proxy and <code>llm/stream</code> path are removed.</p>
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
10
|
<a href="https://www.npmjs.com/package/@goodandready/dsh-clinebot"><img src="https://img.shields.io/npm/v/@goodandready/dsh-clinebot.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
|
package/README.ru.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
<h3>Нативное подключение провайдера ClineBot / ClinePass для DeepSeek Harness</h3>
|
|
6
6
|
|
|
7
|
-
<p><b>0.6.
|
|
7
|
+
<p><b>0.6.5</b> группирует таблицу моделей по доступности в тарифе и добавляет <code>/cline cost</code> / отчёт по месячной квоте поверх аналитики. Чат работает через нативный адаптер <code>ctx.llm</code>, требуется DSH <code>0.2.1-rc.2</code>; старые <code>llm-pi-ai</code>, loopback-прокси и <code>llm/stream</code> удалены.</p>
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
10
|
<a href="https://www.npmjs.com/package/@goodandready/dsh-clinebot"><img src="https://img.shields.io/npm/v/@goodandready/dsh-clinebot.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
|
package/README.zh.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
<h3>适用于 DeepSeek Harness 的 ClineBot / ClinePass 原生模型提供商伴侣插件</h3>
|
|
6
6
|
|
|
7
|
-
<p><b>0.6.
|
|
7
|
+
<p><b>0.6.5</b> 按套餐可用性对模型表分组,并在分析功能之上添加 <code>/cline cost</code> / 每月配额报告。聊天使用原生 <code>ctx.llm</code> 适配器,需要 DSH <code>0.2.1-rc.2</code>;旧的 <code>llm-pi-ai</code>、回环代理和 <code>llm/stream</code> 已删除。</p>
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
10
|
<a href="https://www.npmjs.com/package/@goodandready/dsh-clinebot"><img src="https://img.shields.io/npm/v/@goodandready/dsh-clinebot.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
|
package/lib/client.js
CHANGED
|
@@ -140,6 +140,7 @@ const en = {
|
|
|
140
140
|
'models.visible_models_hint': 'Choose which models appear in the DSH chat selector (empty means all supported models).',
|
|
141
141
|
'models.in_plan': 'In Plan',
|
|
142
142
|
'models.out_of_plan': 'Out of Plan',
|
|
143
|
+
'models.group_plan': 'Included in plan', 'models.group_other': 'Other models', 'models.group_custom': 'Custom models',
|
|
143
144
|
'models.plan_only': 'Plan Only',
|
|
144
145
|
'models.show_all': 'Show All',
|
|
145
146
|
'models.clean_stale': 'Clean Stale ({count})',
|
|
@@ -425,6 +426,7 @@ const zh = {
|
|
|
425
426
|
'models.visible_models_hint': '选择在 DSH 聊天选择器中显示的模型(为空时显示全部可用模型)。',
|
|
426
427
|
'models.in_plan': '套餐内',
|
|
427
428
|
'models.out_of_plan': '套餐外',
|
|
429
|
+
'models.group_plan': '套餐内模型', 'models.group_other': '其他模型', 'models.group_custom': '自定义模型',
|
|
428
430
|
'models.plan_only': '仅套餐模型',
|
|
429
431
|
'models.show_all': '显示全部',
|
|
430
432
|
'models.clean_stale': '清理过期 ({count})',
|
|
@@ -1085,6 +1087,7 @@ function ensureCss() {
|
|
|
1085
1087
|
.cb-table{font-size:12px}
|
|
1086
1088
|
.cb-table th{padding:5px 8px;border-bottom:1px solid rgba(127,127,127,.18);font-size:11px;font-weight:600;opacity:.8}
|
|
1087
1089
|
.cb-table td{padding:5px 8px;border-bottom:1px solid rgba(127,127,127,.18);font-size:12px}
|
|
1090
|
+
.cb-model-group td{padding:8px 8px 4px;color:var(--dsw-alias-label-secondary);font-size:11px;font-weight:650;background:rgba(127,127,127,.08)}
|
|
1088
1091
|
.cb-progress-track,.cb-bar-track{height:8px;border-radius:999px;background:rgba(127,127,127,.2)}
|
|
1089
1092
|
.cb-progress-bar-fill,.cb-bar-fill,.cb-footFill,.cb-meter-fill{background:#4fbf86}
|
|
1090
1093
|
.cb-progress-bar-warn{background:#d9a24a}
|
|
@@ -3037,7 +3040,6 @@ function ModelsSection({
|
|
|
3037
3040
|
const [editingModelId, setEditingModelId] = React.useState(null)
|
|
3038
3041
|
const [customCtxVal, setCustomCtxVal] = React.useState('')
|
|
3039
3042
|
const [customMaxVal, setCustomMaxVal] = React.useState('')
|
|
3040
|
-
|
|
3041
3043
|
function formatCtx(num) {
|
|
3042
3044
|
num = Number(num) || 200000
|
|
3043
3045
|
if (num >= 1000000) {
|
|
@@ -3050,7 +3052,6 @@ function ModelsSection({
|
|
|
3050
3052
|
}
|
|
3051
3053
|
return String(num)
|
|
3052
3054
|
}
|
|
3053
|
-
|
|
3054
3055
|
const filteredModels = modelsList.filter((m) => {
|
|
3055
3056
|
if (viewFilter === 'vision') {
|
|
3056
3057
|
const hasVision = Boolean(m.input?.includes('image') || m.input?.includes('vision'))
|
|
@@ -3071,10 +3072,13 @@ function ModelsSection({
|
|
|
3071
3072
|
}
|
|
3072
3073
|
return true
|
|
3073
3074
|
})
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3075
|
+
const groupFor = (m) => (m.isCustom ? 'custom' : (m.inPlan ? 'plan' : 'other'))
|
|
3076
|
+
const orderedModels = [...filteredModels].sort((a, b) => {
|
|
3077
|
+
const order = { plan: 0, other: 1, custom: 2 }
|
|
3078
|
+
return order[groupFor(a)] - order[groupFor(b)]
|
|
3079
|
+
})
|
|
3080
|
+
let lastGroup = ''
|
|
3081
|
+
return React.createElement('div', { className: 'cb-section-card' },
|
|
3078
3082
|
defaultModelWarning ? React.createElement('div', { className: 'cb-alert-bad cb-inline-alert', style: { marginBottom: '12px' } }, React.createElement(CbIcon, { name: 'warning', size: 14 }), ` ${defaultModelWarning}`) : null,
|
|
3079
3083
|
React.createElement(VisibleModelsSub, {
|
|
3080
3084
|
modelsList,
|
|
@@ -3138,8 +3142,6 @@ function ModelsSection({
|
|
|
3138
3142
|
)
|
|
3139
3143
|
),
|
|
3140
3144
|
React.createElement('div', { className: 'cb-section-desc' }, t('models.desc')),
|
|
3141
|
-
|
|
3142
|
-
// Search and display filter bar
|
|
3143
3145
|
React.createElement(
|
|
3144
3146
|
'div',
|
|
3145
3147
|
{
|
|
@@ -3214,7 +3216,6 @@ function ModelsSection({
|
|
|
3214
3216
|
)
|
|
3215
3217
|
)
|
|
3216
3218
|
),
|
|
3217
|
-
|
|
3218
3219
|
React.createElement(
|
|
3219
3220
|
'table',
|
|
3220
3221
|
{ className: 'cb-table' },
|
|
@@ -3234,7 +3235,10 @@ function ModelsSection({
|
|
|
3234
3235
|
React.createElement(
|
|
3235
3236
|
'tbody',
|
|
3236
3237
|
null,
|
|
3237
|
-
|
|
3238
|
+
orderedModels.map((m) => {
|
|
3239
|
+
const groupKey = groupFor(m)
|
|
3240
|
+
const showGroup = groupKey !== lastGroup
|
|
3241
|
+
lastGroup = groupKey
|
|
3238
3242
|
const isEnabled = !disabledSet.has(m.id)
|
|
3239
3243
|
const defaultCtx = m.defaultContextLength || m.clineContextLength || 200000
|
|
3240
3244
|
const origCtx = m.originalContextLength || 200000
|
|
@@ -3243,10 +3247,7 @@ function ModelsSection({
|
|
|
3243
3247
|
const origProv = m.originalProvider || 'Original'
|
|
3244
3248
|
const isOverridden = Boolean(m.isContextOverridden)
|
|
3245
3249
|
const isOriginal = isOverridden && (m.contextLength === origCtx && (m.maxTokens || defaultMax) === origMax)
|
|
3246
|
-
|
|
3247
|
-
return React.createElement(
|
|
3248
|
-
'tr',
|
|
3249
|
-
{ key: m.id },
|
|
3250
|
+
const row = React.createElement('tr', { key: m.id },
|
|
3250
3251
|
React.createElement(
|
|
3251
3252
|
'td',
|
|
3252
3253
|
null,
|
|
@@ -3490,6 +3491,15 @@ function ModelsSection({
|
|
|
3490
3491
|
})()
|
|
3491
3492
|
)
|
|
3492
3493
|
)
|
|
3494
|
+
const groupCount = orderedModels.filter((entry) => groupFor(entry) === groupKey).length
|
|
3495
|
+
return React.createElement(React.Fragment, { key: `group-wrap-${m.id}` },
|
|
3496
|
+
showGroup
|
|
3497
|
+
? React.createElement('tr', { className: 'cb-model-group' },
|
|
3498
|
+
React.createElement('td', { colSpan: 5 }, `${t(`models.group_${groupKey}`)} · ${groupCount}`)
|
|
3499
|
+
)
|
|
3500
|
+
: null,
|
|
3501
|
+
row
|
|
3502
|
+
)
|
|
3493
3503
|
})
|
|
3494
3504
|
)
|
|
3495
3505
|
),
|
package/lib/slash-command.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { publicConfig, plainConfig, Config } from './config.js'
|
|
2
2
|
import { resolveActiveAccountKey, formatProgressBar } from './provider-sync.js'
|
|
3
|
+
import { getBudgetSpend } from './stats-storage.js'
|
|
4
|
+
import { budgetStatus, budgetErrorMessage } from './budget.js'
|
|
3
5
|
import { resolveAccountPool, rotateToNextAccount, getLastRotation } from './account-pool.js'
|
|
4
6
|
import {
|
|
5
7
|
getAllModels,
|
|
@@ -148,7 +150,27 @@ export function registerSlashCommand(ctx, { live, getSettingsApi, syncProviderSt
|
|
|
148
150
|
return `Cline API Ping Failed: ${health.error || 'Host unreachable'}`
|
|
149
151
|
}
|
|
150
152
|
|
|
151
|
-
// 6. Subcommand /cline
|
|
153
|
+
// 6. Subcommand /cline cost or /cline budget
|
|
154
|
+
if (subcmd === 'cost' || subcmd === 'budget') {
|
|
155
|
+
const spend = getBudgetSpend()
|
|
156
|
+
const daily = budgetStatus({ dailyBudgetUsd: pub.dailyBudgetUsd }, spend)
|
|
157
|
+
const monthly = budgetStatus({ monthlyBudgetUsd: pub.monthlyBudgetUsd }, spend)
|
|
158
|
+
const money = (value) => `$${Number(value || 0).toFixed(4)}`
|
|
159
|
+
const limit = (value) => Number(value) > 0 ? money(value) : 'off'
|
|
160
|
+
const lines = [
|
|
161
|
+
`ClineBot Estimated Spend · today ${money(spend.dayCost)} · month ${money(spend.monthCost)}`,
|
|
162
|
+
'',
|
|
163
|
+
`Today: ${money(spend.dayCost)} / ${limit(pub.dailyBudgetUsd)}`,
|
|
164
|
+
`Month: ${money(spend.monthCost)} / ${limit(pub.monthlyBudgetUsd)}`,
|
|
165
|
+
`Daily guard: ${daily.blocked ? `BLOCKED — ${budgetErrorMessage(daily)}` : 'ok'}`,
|
|
166
|
+
`Monthly guard: ${monthly.blocked ? `BLOCKED — ${budgetErrorMessage(monthly)}` : 'ok'}`,
|
|
167
|
+
'',
|
|
168
|
+
'Set limits in Settings -> ClineBot -> Advanced.',
|
|
169
|
+
]
|
|
170
|
+
return lines.join('\n')
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// 7. Subcommand /cline stats (real DSH stream metrics)
|
|
152
174
|
if (subcmd === 'stats' || subcmd === 'telemetry') {
|
|
153
175
|
const summary = `ClineBot Telemetry · ${sessionStats.successfulRequests}/${sessionStats.totalRequests} reqs · ~${sessionStats.totalTokensEst} tokens`
|
|
154
176
|
const lines = [
|
|
@@ -177,7 +199,7 @@ export function registerSlashCommand(ctx, { live, getSettingsApi, syncProviderSt
|
|
|
177
199
|
return lines.join('\n')
|
|
178
200
|
}
|
|
179
201
|
|
|
180
|
-
//
|
|
202
|
+
// 8. Subcommand /cline test [model] / smoke
|
|
181
203
|
if (subcmd === 'test' || subcmd === 'smoke') {
|
|
182
204
|
if (param && !isSupportedModel(param, pub.dynamicModels, pub.modelContextOverrides, pub.customModels)) {
|
|
183
205
|
return `ClineBot: Model "${param}" is not recognized. Use /cline models to see available models.`
|
|
@@ -221,7 +243,7 @@ export function registerSlashCommand(ctx, { live, getSettingsApi, syncProviderSt
|
|
|
221
243
|
return `Smoke Test Failed: ${outcome.error} (HTTP ${outcome.status || 'timeout'})${failoverNotice}`
|
|
222
244
|
}
|
|
223
245
|
|
|
224
|
-
//
|
|
246
|
+
// 9. Subcommand /cline quota, /cline usage or /cline balance
|
|
225
247
|
const activeKey = await resolveActiveAccountKey(ctx, live())
|
|
226
248
|
if (!activeKey.value) {
|
|
227
249
|
return 'ClineBot: API key is not configured. Open Settings -> Plugins -> ClineBot.'
|
|
@@ -234,9 +256,11 @@ export function registerSlashCommand(ctx, { live, getSettingsApi, syncProviderSt
|
|
|
234
256
|
|
|
235
257
|
const fiveHour = usage?.windows?.fiveHour
|
|
236
258
|
const weekly = usage?.windows?.weekly
|
|
259
|
+
const monthly = usage?.windows?.monthly
|
|
237
260
|
const reset5h = fiveHour?.resetsAt ? new Date(fiveHour.resetsAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : 'N/A'
|
|
238
261
|
const resetWk = weekly?.resetsAt ? new Date(weekly.resetsAt).toLocaleDateString([], { day: '2-digit', month: '2-digit' }) : 'N/A'
|
|
239
262
|
const resetWkFull = weekly?.resetsAt ? new Date(weekly.resetsAt).toLocaleDateString([], { day: '2-digit', month: '2-digit', year: 'numeric' }) : 'N/A'
|
|
263
|
+
const resetMo = monthly?.resetsAt ? new Date(monthly.resetsAt).toLocaleDateString([], { day: '2-digit', month: '2-digit', year: 'numeric' }) : 'N/A'
|
|
240
264
|
|
|
241
265
|
const planRaw = usage?.plan || 'ClinePass'
|
|
242
266
|
const planShort = planRaw.replace(/\s*\(\$\d+\.\d+\/mo\)/i, '').trim()
|
|
@@ -268,6 +292,7 @@ export function registerSlashCommand(ctx, { live, getSettingsApi, syncProviderSt
|
|
|
268
292
|
'',
|
|
269
293
|
`${pad('5-Hour Quota')}${formatProgressBar(fiveHour?.percentUsed)} (resets: ${reset5h})`,
|
|
270
294
|
`${pad('Weekly Quota')}${formatProgressBar(weekly?.percentUsed)} (resets: ${resetWkFull})`,
|
|
295
|
+
`${pad('Monthly Quota')}${formatProgressBar(monthly?.percentUsed)} (resets: ${resetMo})`,
|
|
271
296
|
)
|
|
272
297
|
|
|
273
298
|
if (fiveHour?.percentUsed >= 95) {
|
|
@@ -315,8 +340,8 @@ export function registerSlashCommand(ctx, { live, getSettingsApi, syncProviderSt
|
|
|
315
340
|
const unregister = commands.register({
|
|
316
341
|
definitionId: '@goodandready/dsh-clinebot',
|
|
317
342
|
name: 'cline',
|
|
318
|
-
description: 'Check ClinePass
|
|
319
|
-
input: { hint: '[quota|models|accounts|switch <name>|test [model]|ping|rotate]' },
|
|
343
|
+
description: 'Check ClinePass quota, cost, models, accounts and connectivity (/cline [quota|usage|cost|models|accounts|switch <name>|test [model]|ping|rotate])',
|
|
344
|
+
input: { hint: '[quota|usage|cost|models|accounts|switch <name>|test [model]|ping|rotate]' },
|
|
320
345
|
handler: commandHandler,
|
|
321
346
|
execute: commandExecute,
|
|
322
347
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-clinebot",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"description": "DeepSeek Harness companion for ClineBot / ClinePass: dynamic subscription models sync, quota exhaustion warnings, session metrics, dedicated settings page, live usage limits, and /cline slash-command.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|