@goodandready/dsh-messenger-gateway 0.4.8 → 0.4.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/lib/index.js CHANGED
@@ -22,7 +22,8 @@ export { Config }
22
22
 
23
23
  function resolveConfig(raw) {
24
24
  // Unwrap once here: everything downstream reads plain values.
25
- const cfg = plainConfig(Config(raw))
25
+ const plain = plainConfig(raw)
26
+ const cfg = plainConfig(Config(plain))
26
27
  const home = process.env.DSH_HOME || join(homedir(), '.dsh')
27
28
  if (!cfg.media.cacheDir) {
28
29
  cfg.media = { ...cfg.media, cacheDir: join(home, 'messenger-gateway', 'cache') }
@@ -141,7 +142,7 @@ function isExcludedSession(sessionId, prefixes) {
141
142
  }
142
143
 
143
144
  export function apply(ctx, config) {
144
- const entry = resolveConfig(structuredClone(config || {}))
145
+ const entry = resolveConfig(structuredClone(plainConfig(config || {})))
145
146
  let gateway
146
147
  let source = () => entry
147
148
  let settingsApi
@@ -1,6 +1,30 @@
1
1
  export function setupSettings(ctx, { namespace, Config, resolveConfig, baseConfig, sync, logger }) {
2
2
  let settingsApi
3
- let source = () => resolveConfig(baseConfig ?? {})
3
+ let currentConfig = resolveConfig(ctx.fiber?.config ?? baseConfig ?? {})
4
+ let source = () => currentConfig
5
+ const watchers = new Set()
6
+ let readLive = () => null
7
+
8
+ const applyLiveConfig = (nextRaw) => {
9
+ const live = nextRaw ?? readLive() ?? ctx.fiber?.config ?? baseConfig
10
+ if (live) {
11
+ currentConfig = resolveConfig(live)
12
+ sync()
13
+ for (const cb of watchers) {
14
+ try { cb(currentConfig) } catch (err) { logger?.debug?.(`${namespace}: watcher error:`, err?.message || err) }
15
+ }
16
+ }
17
+ }
18
+
19
+ const unbinds = []
20
+ if (typeof ctx?.on === 'function') {
21
+ const u1 = ctx.on('loader/volatile-update', () => applyLiveConfig())
22
+ if (typeof u1 === 'function') unbinds.push(u1)
23
+ const u2 = ctx.on('internal/update', (cfg) => applyLiveConfig(cfg))
24
+ if (typeof u2 === 'function') unbinds.push(u2)
25
+ const u3 = ctx.on('app-boot/config-reload', () => applyLiveConfig())
26
+ if (typeof u3 === 'function') unbinds.push(u3)
27
+ }
4
28
 
5
29
  const mountSettings = (sctx) => {
6
30
  const service = sctx?.settings || (typeof sctx?.get === 'function' ? sctx.get('settings') : null)
@@ -26,7 +50,8 @@ export function setupSettings(ctx, { namespace, Config, resolveConfig, baseConfi
26
50
  const getRevision = () => {
27
51
  try {
28
52
  return service.describe?.()?.find?.((row) => row.ns === namespace)?.revision
29
- } catch {
53
+ } catch (err) {
54
+ logger?.debug?.(`${namespace}: getRevision failed:`, err?.message || err)
30
55
  return undefined
31
56
  }
32
57
  }
@@ -47,8 +72,9 @@ export function setupSettings(ctx, { namespace, Config, resolveConfig, baseConfi
47
72
  return null
48
73
  }
49
74
 
75
+ readLive = readLiveSettings
50
76
  const liveVal = readLiveSettings()
51
- let currentConfig = resolveConfig(liveVal ?? baseConfig ?? {})
77
+ if (liveVal) currentConfig = resolveConfig(liveVal)
52
78
  source = () => currentConfig
53
79
 
54
80
  const persist = async (next) => {
@@ -64,6 +90,9 @@ export function setupSettings(ctx, { namespace, Config, resolveConfig, baseConfi
64
90
  }
65
91
  currentConfig = resolveConfig(payload)
66
92
  sync()
93
+ for (const cb of watchers) {
94
+ try { cb(currentConfig) } catch (err) { logger?.debug?.(`${namespace}: watcher error:`, err?.message || err) }
95
+ }
67
96
  return currentConfig
68
97
  }
69
98
 
@@ -72,27 +101,28 @@ export function setupSettings(ctx, { namespace, Config, resolveConfig, baseConfi
72
101
  replace: async (next) => persist(next),
73
102
  update: async (patch) => persist({ ...source(), ...patch }),
74
103
  watch: (cb) => {
75
- if (typeof service.watch === 'function') return service.watch(cb)
76
- return () => {}
104
+ if (typeof cb === 'function') watchers.add(cb)
105
+ const unwatchService = typeof service.watch === 'function' ? service.watch(cb) : null
106
+ return () => {
107
+ watchers.delete(cb)
108
+ if (typeof unwatchService === 'function') unwatchService()
109
+ }
77
110
  },
78
111
  }
79
112
 
80
113
  const onFn = typeof sctx?.on === 'function' ? sctx.on.bind(sctx) : (typeof ctx?.on === 'function' ? ctx.on.bind(ctx) : null)
81
- const disposeDocUpdated = onFn
82
- ? onFn('settings/document-updated', (updatedNs) => {
83
- if (updatedNs === namespace) {
84
- const nextVal = readLiveSettings()
85
- if (nextVal) {
86
- currentConfig = resolveConfig(nextVal)
87
- sync()
88
- }
89
- }
90
- })
91
- : null
114
+ if (onFn) {
115
+ const d1 = onFn('settings/document-updated', (updatedNs) => {
116
+ if (updatedNs === namespace) applyLiveConfig()
117
+ })
118
+ if (typeof d1 === 'function') unbinds.push(d1)
119
+ }
92
120
 
93
121
  if (effect) {
94
122
  effect(() => () => {
95
- if (typeof disposeDocUpdated === 'function') disposeDocUpdated()
123
+ for (const unbind of unbinds) {
124
+ try { unbind() } catch (err) { logger?.debug?.(`${namespace}: unbind error:`, err?.message || err) }
125
+ }
96
126
  settingsApi = undefined
97
127
  }, `${namespace}: settings`)
98
128
  }
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-messenger-gateway",
3
- "version": "0.4.8",
3
+ "version": "0.4.9",
4
4
  "description": "Telegram messenger bridge for DeepSeek Harness: sessions, steer, homes, inline asks, notify bridge, and optional TTS voice notes.",
5
5
  "license": "MIT",
6
6
  "type": "module",