@goodandready/dsh-clinebot 0.3.21 → 0.3.22

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,12 @@ 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.22] - 2026-09-24
9
+
10
+ ### Fixed
11
+ - **SettingsPage Crash on Cordis Strict Context (#67 / GitHub #4)**: Fixed unhandled `TypeError: cannot get property "settingsScope" without inject` thrown when accessing `ctx?.settingsScope` under DSH 0.17+ / Cordis v3+ strict proxy contexts. Replaced bare property access with defensive `ctx.get('lanSettings') || ctx.get('settingsScope')` inside `try/catch` across both `settings-page.js` and `entry.js`.
12
+ - **Standalone Unblocked UI Rendering**: When `settingsScope` / `lanSettings` service is absent or not injected in the client fiber, `SettingsPage` snapshot now defaults to `{ status: 'ready', view: null }` instead of permanently hanging on `status: 'loading'`. The settings page is entirely functional via its own HTTP REST endpoints (`/dsh-clinebot/status`, `/save-key`, `/models/toggle`, etc.).
13
+
8
14
  ## [0.3.21] - 2026-09-23
9
15
 
10
16
  ### Fixed
package/lib/client.js CHANGED
@@ -839,13 +839,31 @@ function SettingsPage(props) {
839
839
  const t = props?.t || makeT(en, en)
840
840
 
841
841
  const scope = React.useMemo(() => {
842
- const s = (ctx?.get && ctx.get('lanSettings')) || ctx?.settingsScope
843
- if (!s?.bind) return undefined
842
+ let s = null
844
843
  try {
845
- return s.bind({ namespace: NS })
844
+ if (typeof ctx?.get === 'function') {
845
+ s = ctx.get('lanSettings') || ctx.get('settingsScope')
846
+ }
847
+ } catch (_) {
848
+ s = null
849
+ }
850
+ if (!s) {
851
+ try {
852
+ if (ctx && typeof ctx === 'object') {
853
+ s = ctx.lanSettings || ctx.settingsScope || null
854
+ }
855
+ } catch (_) {
856
+ s = null
857
+ }
858
+ }
859
+ if (!s) return undefined
860
+ try {
861
+ if (typeof s.bind === 'function') return s.bind({ namespace: NS })
862
+ if (typeof s.get === 'function') return s.get(NS)
846
863
  } catch (_) {
847
864
  return undefined
848
865
  }
866
+ return undefined
849
867
  }, [ctx])
850
868
 
851
869
  const subscribe = React.useMemo(() => {
@@ -860,11 +878,11 @@ function SettingsPage(props) {
860
878
  }, [scope])
861
879
 
862
880
  const getSnapshot = React.useCallback(() => {
863
- if (!scope?.getSnapshot) return SNAPSHOT_LOADING
881
+ if (!scope?.getSnapshot) return { status: 'ready', view: null }
864
882
  try {
865
- return scope.getSnapshot() || SNAPSHOT_LOADING
883
+ return scope.getSnapshot() || { status: 'ready', view: null }
866
884
  } catch (_) {
867
- return SNAPSHOT_LOADING
885
+ return { status: 'ready', view: null }
868
886
  }
869
887
  }, [scope])
870
888
 
@@ -1369,28 +1387,48 @@ function PluginCard(props) {
1369
1387
 
1370
1388
  // --- src/client/entry.js ---
1371
1389
  function refreshMirrorUntilVisible(ctx) {
1390
+ const getSettingsService = () => {
1391
+ try {
1392
+ if (typeof ctx?.get === 'function') {
1393
+ const s = ctx.get('lanSettings') || ctx.get('settingsScope')
1394
+ if (s) return s
1395
+ }
1396
+ } catch (_) {}
1397
+ try {
1398
+ if (ctx && typeof ctx === 'object') {
1399
+ return ctx.lanSettings || ctx.settingsScope || null
1400
+ }
1401
+ } catch (_) {}
1402
+ return null
1403
+ }
1404
+
1405
+ const s = getSettingsService()
1406
+ if (!s) return
1407
+
1372
1408
  const visible = () => {
1373
1409
  try {
1374
- const s = (ctx?.get && ctx.get('lanSettings')) || ctx?.settingsScope
1375
- const view = s?.describe?.()?.getSnapshot?.()?.view
1410
+ const current = getSettingsService()
1411
+ const view = current?.describe?.()?.getSnapshot?.()?.view
1376
1412
  return !!view && Array.isArray(view.namespaces) && view.namespaces.some((row) => row.ns === NS)
1377
1413
  } catch (_) {
1378
1414
  return false
1379
1415
  }
1380
1416
  }
1381
- if (visible()) return () => {}
1417
+ if (visible()) return
1418
+
1419
+ if (typeof setInterval !== 'function') return
1420
+
1382
1421
  let tries = 0
1383
1422
  const timer = setInterval(() => {
1384
1423
  if (visible() || tries >= 15) { clearInterval(timer); return }
1385
1424
  tries += 1
1386
1425
  try {
1387
- const s = (ctx?.get && ctx.get('lanSettings')) || ctx?.settingsScope
1388
- s?.describe?.()?.load?.()
1426
+ const current = getSettingsService()
1427
+ current?.describe?.()?.load?.()
1389
1428
  } catch (e) {
1390
1429
  console.debug?.('[dsh-clinebot] Polling settings mirror:', e)
1391
1430
  }
1392
1431
  }, 1000)
1393
- return () => clearInterval(timer)
1394
1432
  }
1395
1433
 
1396
1434
  function apply(ctx) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-clinebot",
3
- "version": "0.3.21",
3
+ "version": "0.3.22",
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",