@goodandready/dsh-clinebot 0.6.4 → 0.6.6

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.
@@ -1,6 +1,5 @@
1
1
  import { createHash } from 'node:crypto'
2
2
  export {
3
- toCredentialRef,
4
3
  resolveApiKey,
5
4
  saveCredentialKey,
6
5
  deleteCredentialKey,
@@ -16,8 +15,6 @@ export {
16
15
 
17
16
  import {
18
17
  DEFAULT_MODEL_ID,
19
- PROVIDER_ID,
20
- PROVIDER_DISPLAY_NAME,
21
18
  parsePlanIncludedModels,
22
19
  } from './models.js'
23
20
 
@@ -28,7 +25,7 @@ export const DEFAULT_SMOKE_TIMEOUT_MS = 25000
28
25
  export const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 30000
29
26
  const USAGE_CACHE_TTL_MS = 60000
30
27
 
31
- export { PROVIDER_ID, PROVIDER_DISPLAY_NAME, DEFAULT_MODEL_ID }
28
+ export { DEFAULT_MODEL_ID }
32
29
 
33
30
  // Credentials helpers extracted to ./credential-refs.js
34
31
 
@@ -25,7 +25,7 @@ function credentialsService(ctx) {
25
25
  }
26
26
  }
27
27
 
28
- export async function toCredentialRef(name) {
28
+ async function toCredentialRef(name) {
29
29
  try {
30
30
  const mod = await import('@deepseek-ai/dsh-credentials')
31
31
  if (typeof mod.credentialRef === 'function') {
package/lib/http.js CHANGED
@@ -46,7 +46,7 @@ function header(request, name) {
46
46
  return Array.isArray(value) ? value[0] : value
47
47
  }
48
48
 
49
- export function isLoopbackAddress(value) {
49
+ function isLoopbackAddress(value) {
50
50
  const address = String(value || '').toLowerCase().replace(/^\[|\]$/g, '')
51
51
  return address === 'localhost' || address === 'localhost.' || address === '::1'
52
52
  || address.startsWith('127.')
@@ -7,7 +7,7 @@
7
7
  * no known or overridden rate stays `unpriced` rather than being shown as $0.
8
8
  */
9
9
 
10
- export const PRICE_TABLE_VERSION = 1
10
+ const PRICE_TABLE_VERSION = 1
11
11
 
12
12
  const OPEN_WEIGHT = Object.freeze({ input: 0.15, output: 0.6, cacheRead: 0.003, cacheWrite: null })
13
13
  const CLAUDE = Object.freeze({ input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 })
package/lib/models.js CHANGED
@@ -72,7 +72,7 @@ export function getOriginalModelMaxTokens(nameOrId) {
72
72
  return 16384
73
73
  }
74
74
 
75
- export function getOriginalModelProvider(nameOrId) {
75
+ function getOriginalModelProvider(nameOrId) {
76
76
  const norm = String(nameOrId || '').toLowerCase()
77
77
  if (norm.includes('kimi')) return 'Moonshot AI'
78
78
  if (norm.includes('qwen')) return 'Alibaba Cloud'
@@ -43,7 +43,7 @@ export function resolvePathWithHome(p) {
43
43
  return p
44
44
  }
45
45
 
46
- export async function checkRegisteredInLlm(ctx) {
46
+ async function checkRegisteredInLlm(ctx) {
47
47
  try {
48
48
  const llm = ctx?.get?.('llm')
49
49
  if (llm && typeof llm.listProviders === 'function') {
@@ -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 stats (real DSH stream metrics)
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
- // 7. Subcommand /cline test [model] / smoke
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
- // 8. Subcommand /cline quota or /cline balance
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 subscription quota, models, accounts and test connectivity (/cline [quota|models|accounts|switch <name>|test [model]|ping|rotate])',
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
  })
@@ -84,7 +84,7 @@ function localDateKey(date) {
84
84
  }
85
85
 
86
86
  /** Get active .dsh directory, respecting DSH_HOME and HOME. */
87
- export function getDshHome() {
87
+ function getDshHome() {
88
88
  if (process.env.DSH_HOME) return process.env.DSH_HOME
89
89
  const home = process.env.HOME || homedir()
90
90
  return path.join(home, '.dsh')
@@ -412,7 +412,7 @@ export function resetStats(customPath) {
412
412
  * @param {number} [days]
413
413
  * @param {number} [to]
414
414
  */
415
- export function getHeatmap(days = 7, to = Date.now(), model = '') {
415
+ function getHeatmap(days = 7, to = Date.now(), model = '') {
416
416
  const count = Math.max(1, Math.min(95, Number(days) || 7))
417
417
  const end = new Date(Number.isFinite(Number(to)) ? Number(to) : Date.now())
418
418
  const start = new Date(end.getFullYear(), end.getMonth(), end.getDate() - (count - 1))
@@ -9,17 +9,17 @@
9
9
  import { createRequire } from 'node:module'
10
10
 
11
11
  const require = createRequire(import.meta.url)
12
- let packageVersion = '0.6.4'
12
+ let packageVersion = '0.6.6'
13
13
  try {
14
14
  packageVersion = require('../../package.json').version || packageVersion
15
15
  } catch {
16
16
  /* fallback when package.json is unavailable */
17
17
  }
18
18
 
19
- export const USER_AGENT = `@goodandready/dsh-clinebot/${packageVersion}`
20
- export const DEFAULT_CONNECT_TIMEOUT_MS = 15000
21
- export const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 30000
22
- export const DEFAULT_MAX_BODY_BYTES = 10 * 1024 * 1024
19
+ const USER_AGENT = `@goodandready/dsh-clinebot/${packageVersion}`
20
+ const DEFAULT_CONNECT_TIMEOUT_MS = 15000
21
+ const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 30000
22
+ const DEFAULT_MAX_BODY_BYTES = 10 * 1024 * 1024
23
23
 
24
24
  /**
25
25
  * Build the abort-shaped error every transport caller understands.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-clinebot",
3
- "version": "0.6.4",
3
+ "version": "0.6.6",
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",
@@ -100,6 +100,7 @@
100
100
  "@deepseek-ai/dsh-llm": "0.2.1-rc.2",
101
101
  "@deepseek-ai/schemastery": "~3.18.5",
102
102
  "@types/node": "^24.0.0",
103
+ "esbuild": "^0.28.2",
103
104
  "eslint": "^9.39.5",
104
105
  "typescript": "^6.0.3"
105
106
  },