@goodandready/dsh-clinebot 0.3.14 → 0.3.16

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 CHANGED
@@ -5,6 +5,25 @@ 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.16] - 2026-09-20
9
+
10
+ ### Fixed
11
+ - **Strict Active Subscription Plan Model Filtering (#48)**: Correctly parsed `plan.features.included` array from `GET /users/me/plan` to discover exactly the 11 active ClinePass subscription models. Removed non-subscription models from the default `CLINE_MODELS` list (eliminating out-of-plan failures and preventing 400+ models from cluttering the DSH picker).
12
+ - **Array Parsing in `parsePlanIncludedModels`**: Added support for both string and array inputs (extracting feature entries matching `/includes\s+/i`).
13
+
14
+ ## [0.3.15] - 2026-09-19
15
+
16
+ ### Fixed
17
+ - **The settings form now appears on the plugin's own page.** The seat the Plugins
18
+ page really renders as a plugin page with its configuration is a card in the
19
+ plugin list (`plugins.item`) — the page draws the card's one-liner for
20
+ `view: 'summary'` and the entry itself as the body of that page for
21
+ `view: 'page'`. That is how `@goodandready-private/dsh-agentrouter` and
22
+ `@goodandready/dsh-agent-orchestrator` have always shown their settings, while the
23
+ `plugins.row.config` seat alone left the row without a configure control. The card
24
+ is registered with `id: 'dsh-clinebot'`, order 60 and a static label; the row seat
25
+ and the legacy `settings.plugin.item` card stay as fallbacks.
26
+
8
27
  ## [0.3.14] - 2026-09-19
9
28
 
10
29
  ### Fixed
package/lib/client.js CHANGED
@@ -1441,6 +1441,24 @@ function apply(ctx) {
1441
1441
  }
1442
1442
  }
1443
1443
 
1444
+ // The seat the Plugins page actually renders as the plugin's own page with the
1445
+ // settings form: a card in the plugin list (`plugins.item`), exactly how
1446
+ // @goodandready-private/dsh-agentrouter does it. `view: 'summary'` is the card's
1447
+ // one-liner, `view: 'page'` is the body of that page.
1448
+ registerSlotWhenReady('plugins.item', () =>
1449
+ ctx.slots.register(
1450
+ {
1451
+ name: 'plugins.item',
1452
+ id: NS,
1453
+ order: 60,
1454
+ label: () => 'clinebot',
1455
+ locale: NS,
1456
+ inject: () => ({ ctx }),
1457
+ },
1458
+ (props) => React.createElement(ErrorBoundary, null, React.createElement(PluginCard, { ...props, ctx: (props && props.ctx) || ctx }))
1459
+ )
1460
+ )
1461
+
1444
1462
  // Row seat first (the seat the current core renders), legacy seat after it.
1445
1463
  // The core keys the seat as `<bundle name>#<row id>`; `bundle.name` may be the
1446
1464
  // package name or the short bundle name depending on the manager's view, so both
@@ -277,12 +277,13 @@ export async function fetchUsageLimits(baseUrl, apiKey, {
277
277
 
278
278
  if (planRes?.ok) {
279
279
  const planData = await planRes.json().catch(() => ({}))
280
- const plan = planData?.data || planData
281
- if (plan?.title || plan?.name) {
282
- planDisplayName = `${plan.title || plan.name} ($${((plan.priceInCents || 999) / 100).toFixed(2)}/mo)`
280
+ const plan = planData?.data?.plan || planData?.data || planData?.plan || planData
281
+ if (plan?.displayName || plan?.title || plan?.name) {
282
+ planDisplayName = `${plan.displayName || plan.title || plan.name} ($${((plan.pricePerSeatCents || plan.priceInCents || 999) / 100).toFixed(2)}/mo)`
283
283
  }
284
- if (Array.isArray(plan?.includedModels)) {
285
- dynamicModels = parsePlanIncludedModels(plan.includedModels)
284
+ const featuresIncluded = plan?.features?.included || plan?.includedModels
285
+ if (featuresIncluded) {
286
+ dynamicModels = parsePlanIncludedModels(featuresIncluded)
286
287
  }
287
288
  }
288
289
  } catch {
package/lib/models.js CHANGED
@@ -136,83 +136,27 @@ export const CLINE_MODELS = Object.freeze([
136
136
  recommended: false,
137
137
  isCustom: false,
138
138
  },
139
- {
140
- id: 'cline-pass/claude-3-7-sonnet',
141
- name: 'Claude 3.7 Sonnet',
142
- description: 'Hybrid reasoning and standard generation model with high coding proficiency.',
143
- contextLength: 200000,
144
- maxTokens: 8192,
145
- input: ['text', 'image'],
146
- category: 'coding',
147
- recommended: true,
148
- isCustom: false,
149
- reasoningEfforts: ['low', 'medium', 'high'],
150
- },
151
- {
152
- id: 'cline-pass/gpt-4.5-preview',
153
- name: 'GPT-4.5 Preview',
154
- description: 'Advanced flagship frontier model with deep world knowledge and intuition.',
155
- contextLength: 128000,
156
- maxTokens: 16384,
157
- input: ['text', 'image'],
158
- category: 'general',
159
- recommended: true,
160
- isCustom: false,
161
- },
162
- {
163
- id: 'cline-pass/o3-mini',
164
- name: 'o3-mini',
165
- description: 'Fast, cost-effective reasoning model specialized for STEM and coding.',
166
- contextLength: 200000,
167
- maxTokens: 65536,
168
- input: ['text'],
169
- category: 'reasoning',
170
- recommended: true,
171
- isCustom: false,
172
- reasoningEfforts: ['low', 'medium', 'high'],
173
- },
174
- {
175
- id: 'cline-pass/gemini-2.5-pro',
176
- name: 'Gemini 2.5 Pro',
177
- description: 'State-of-the-art multimodal reasoning model with extended context.',
178
- contextLength: 1000000,
179
- maxTokens: 8192,
180
- input: ['text', 'image'],
181
- category: 'multimodal',
182
- recommended: true,
183
- isCustom: false,
184
- reasoningEfforts: ['low', 'medium', 'high'],
185
- },
186
- {
187
- id: 'cline-pass/gemini-2.5-flash',
188
- name: 'Gemini 2.5 Flash',
189
- description: 'Ultra-fast multimodal model optimized for real-time agent workflows.',
190
- contextLength: 1000000,
191
- maxTokens: 8192,
192
- input: ['text', 'image'],
193
- category: 'general',
194
- recommended: false,
195
- isCustom: false,
196
- },
197
- {
198
- id: 'cline-pass/qwen-2.5-coder-32b',
199
- name: 'Qwen 2.5 Coder 32B',
200
- description: 'Open-weights powerhouse for code generation, refactoring and bug fixing.',
201
- contextLength: 131072,
202
- maxTokens: 8192,
203
- input: ['text'],
204
- category: 'coding',
205
- recommended: false,
206
- isCustom: false,
207
- },
208
139
  ])
209
140
 
210
141
  /**
211
142
  * Parse human-readable included models string from ClinePass plan features.
212
143
  * Example: "Includes Kimi K3, GLM 5.2, Kimi K2.6, Kimi K2.7 Code, Mimo v2.5, Mimo v2.5 Pro, Minimax M3, Qwen3.7 Plus, Qwen3.7 Max, DeepSeek V4 Pro, and DeepSeek V4 Flash"
213
144
  */
214
- export function parsePlanIncludedModels(includedText) {
215
- if (!includedText || typeof includedText !== 'string') return []
145
+ export function parsePlanIncludedModels(includedInput) {
146
+ if (!includedInput) return []
147
+ let includedText = ''
148
+ if (Array.isArray(includedInput)) {
149
+ const foundStr = includedInput.find((item) => typeof item === 'string' && /includes\s+/i.test(item))
150
+ if (foundStr) {
151
+ includedText = foundStr
152
+ } else {
153
+ includedText = includedInput.filter((x) => typeof x === 'string').join(', ')
154
+ }
155
+ } else if (typeof includedInput === 'string') {
156
+ includedText = includedInput
157
+ } else {
158
+ return []
159
+ }
216
160
  const clean = includedText
217
161
  .replace(/^includes\s+/i, '')
218
162
  .replace(/\band\b/gi, ',')
@@ -17,7 +17,7 @@ export function registerAccountsRoutes(ctx, { live, getSettingsApi, syncProvider
17
17
  try {
18
18
  const bodyBuf = await readBody(req)
19
19
  let body = {}
20
- try { body = JSON.parse(bodyBuf.toString('utf8')) } catch {}
20
+ try { body = JSON.parse(bodyBuf.toString('utf8')) } catch { body = {} }
21
21
  const account = String(body.account || '').trim()
22
22
 
23
23
  clearUsageCache()
@@ -49,7 +49,7 @@ export function registerAccountsRoutes(ctx, { live, getSettingsApi, syncProvider
49
49
  try {
50
50
  const bodyBuf = await readBody(req)
51
51
  let body = {}
52
- try { body = JSON.parse(bodyBuf.toString('utf8')) } catch {}
52
+ try { body = JSON.parse(bodyBuf.toString('utf8')) } catch { body = {} }
53
53
  const activeModels = body.models || publicConfig(live()).enabledModels
54
54
  const result = await upsertPiAiProvider(ctx, live(), activeModels)
55
55
  writeJson(res, 200, { ok: true, provider: result })
@@ -60,7 +60,7 @@ export function registerAuthRoutes(ctx, { live, getSettingsApi, syncProviderStat
60
60
  try {
61
61
  const bodyBuf = await readBody(req)
62
62
  let body = {}
63
- try { body = JSON.parse(bodyBuf.toString('utf8')) } catch {}
63
+ try { body = JSON.parse(bodyBuf.toString('utf8')) } catch { body = {} }
64
64
 
65
65
  const pub = publicConfig(live())
66
66
  const activeKey = await resolveActiveAccountKey(ctx, live())
@@ -75,7 +75,7 @@ export function registerModelsRoutes(ctx, { live, getSettingsApi, syncProviderSt
75
75
  try {
76
76
  const bodyBuf = await readBody(req)
77
77
  let body = {}
78
- try { body = JSON.parse(bodyBuf.toString('utf8')) } catch {}
78
+ try { body = JSON.parse(bodyBuf.toString('utf8')) } catch { body = {} }
79
79
 
80
80
  const settingsApi = getSettingsApi()
81
81
  if (settingsApi?.replace) {
@@ -68,7 +68,7 @@ export function registerSettingsRoutes(ctx, { live, getSettingsApi, syncProvider
68
68
  try {
69
69
  const bodyBuf = await readBody(req)
70
70
  let body = {}
71
- try { body = JSON.parse(bodyBuf.toString('utf8')) } catch {}
71
+ try { body = JSON.parse(bodyBuf.toString('utf8')) } catch { body = {} }
72
72
 
73
73
  const apiKey = String(body.apiKey || '').trim()
74
74
  if (!apiKey) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-clinebot",
3
- "version": "0.3.14",
3
+ "version": "0.3.16",
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",