@goodandready/dsh-messenger-gateway 0.4.7 → 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/config.js +12 -2
- package/lib/index.js +6 -3
- package/lib/settings-bridge.js +47 -17
- package/lib/telegram-errors.js +0 -0
- package/package.json +1 -1
package/lib/config.js
CHANGED
|
@@ -7,8 +7,8 @@ const HomeEntry = z.object({
|
|
|
7
7
|
})
|
|
8
8
|
|
|
9
9
|
export const PluginConfig = z.object({
|
|
10
|
-
enabled: z.boolean().default(true),
|
|
11
|
-
internalBaseURL: z.string().default('http://127.0.0.1:3080'),
|
|
10
|
+
enabled: z.boolean().volatile().default(true),
|
|
11
|
+
internalBaseURL: z.string().volatile().default('http://127.0.0.1:3080'),
|
|
12
12
|
telegram: z.object({
|
|
13
13
|
enabled: z.boolean().default(false),
|
|
14
14
|
botToken: z.string().role('secret').default(''),
|
|
@@ -102,3 +102,13 @@ export const PluginConfig = z.object({
|
|
|
102
102
|
})
|
|
103
103
|
|
|
104
104
|
export const Config = PluginConfig
|
|
105
|
+
|
|
106
|
+
// Unwrap Volatile boxes before any caller reads a value. The Loader mutates the
|
|
107
|
+
// boxes in place and re-announces them with loader/volatile-update, so this must
|
|
108
|
+
// read the entry config lazily rather than caching the parsed result.
|
|
109
|
+
export function plainConfig(value) {
|
|
110
|
+
if (value === null || typeof value !== 'object') return value
|
|
111
|
+
if (Array.isArray(value)) return value.map(plainConfig)
|
|
112
|
+
if (typeof value.get === 'function') return plainConfig(value.get())
|
|
113
|
+
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, plainConfig(v)]))
|
|
114
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -10,7 +10,8 @@ import {
|
|
|
10
10
|
messengerApiSchema, parseMessengerBody,
|
|
11
11
|
} from './messenger-api.js'
|
|
12
12
|
import { listHomes } from './homes.js'
|
|
13
|
-
|
|
13
|
+
export { plainConfig } from './config.js'
|
|
14
|
+
import { Config, plainConfig } from './config.js'
|
|
14
15
|
import { setupSettings } from './settings-bridge.js'
|
|
15
16
|
|
|
16
17
|
export const name = '@goodandready/dsh-messenger-gateway'
|
|
@@ -20,7 +21,9 @@ export const SETTINGS_NAMESPACE = 'dsh-messenger-gateway'
|
|
|
20
21
|
export { Config }
|
|
21
22
|
|
|
22
23
|
function resolveConfig(raw) {
|
|
23
|
-
|
|
24
|
+
// Unwrap once here: everything downstream reads plain values.
|
|
25
|
+
const plain = plainConfig(raw)
|
|
26
|
+
const cfg = plainConfig(Config(plain))
|
|
24
27
|
const home = process.env.DSH_HOME || join(homedir(), '.dsh')
|
|
25
28
|
if (!cfg.media.cacheDir) {
|
|
26
29
|
cfg.media = { ...cfg.media, cacheDir: join(home, 'messenger-gateway', 'cache') }
|
|
@@ -139,7 +142,7 @@ function isExcludedSession(sessionId, prefixes) {
|
|
|
139
142
|
}
|
|
140
143
|
|
|
141
144
|
export function apply(ctx, config) {
|
|
142
|
-
const entry = resolveConfig(structuredClone(config || {}))
|
|
145
|
+
const entry = resolveConfig(structuredClone(plainConfig(config || {})))
|
|
143
146
|
let gateway
|
|
144
147
|
let source = () => entry
|
|
145
148
|
let settingsApi
|
package/lib/settings-bridge.js
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
export function setupSettings(ctx, { namespace, Config, resolveConfig, baseConfig, sync, logger }) {
|
|
2
2
|
let settingsApi
|
|
3
|
-
let
|
|
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
|
-
|
|
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
|
|
76
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
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
|
}
|
package/lib/telegram-errors.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-messenger-gateway",
|
|
3
|
-
"version": "0.4.
|
|
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",
|