@goodandready/dsh-model-sync 0.5.2 → 0.5.3

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.
Files changed (2) hide show
  1. package/lib/index.js +40 -69
  2. package/package.json +2 -2
package/lib/index.js CHANGED
@@ -6,31 +6,31 @@ import { registerHttpApi } from './http.js'
6
6
  import { registerPluginUpdater } from './updater.js'
7
7
 
8
8
  export const name = '@goodandready/dsh-model-sync'
9
- export const inject = ['llm', 'settings', 'webServer', 'credentials']
9
+ export const inject = ['llm', 'webServer', 'credentials']
10
10
 
11
11
  const NS = 'dsh-model-sync'
12
12
 
13
13
  export const Config = Schema.object({
14
- enabled: Schema.boolean().default(true),
15
- intervalMinutes: Schema.number().default(60),
16
- scheduleEnabled: Schema.boolean().default(false),
17
- jitterMinutes: Schema.number().default(0),
18
- ttlMinutes: Schema.number().default(0),
19
- autoApply: Schema.boolean().default(false),
20
- requestTimeoutMs: Schema.number().default(15000),
21
- retryAttempts: Schema.number().default(3),
22
- retryBaseDelayMs: Schema.number().default(250),
23
- retryMaxDelayMs: Schema.number().default(4000),
24
- concurrency: Schema.number().default(4),
25
- circuitBreakerFailures: Schema.number().default(3),
26
- circuitBreakerCooldownMinutes: Schema.number().default(5),
27
- historyLimit: Schema.number().default(50),
28
- notificationLimit: Schema.number().default(50),
29
- diffWebhookUrl: Schema.string().default(),
30
- diffWebhookSecret: Schema.string().default(),
31
- notifyOnModelAdded: Schema.boolean().default(true),
32
- notifyOnModelDeprecated: Schema.boolean().default(true),
33
- notifyOnPriceChanged: Schema.boolean().default(true),
14
+ enabled: Schema.boolean().default(true).volatile(),
15
+ intervalMinutes: Schema.number().default(60).volatile(),
16
+ scheduleEnabled: Schema.boolean().default(false).volatile(),
17
+ jitterMinutes: Schema.number().default(0).volatile(),
18
+ ttlMinutes: Schema.number().default(0).volatile(),
19
+ autoApply: Schema.boolean().default(false).volatile(),
20
+ requestTimeoutMs: Schema.number().default(15000).volatile(),
21
+ retryAttempts: Schema.number().default(3).volatile(),
22
+ retryBaseDelayMs: Schema.number().default(250).volatile(),
23
+ retryMaxDelayMs: Schema.number().default(4000).volatile(),
24
+ concurrency: Schema.number().default(4).volatile(),
25
+ circuitBreakerFailures: Schema.number().default(3).volatile(),
26
+ circuitBreakerCooldownMinutes: Schema.number().default(5).volatile(),
27
+ historyLimit: Schema.number().default(50).volatile(),
28
+ notificationLimit: Schema.number().default(50).volatile(),
29
+ diffWebhookUrl: Schema.string().default().volatile(),
30
+ diffWebhookSecret: Schema.string().default().volatile(),
31
+ notifyOnModelAdded: Schema.boolean().default(true).volatile(),
32
+ notifyOnModelDeprecated: Schema.boolean().default(true).volatile(),
33
+ notifyOnPriceChanged: Schema.boolean().default(true).volatile(),
34
34
  notifications: Schema.array(Schema.object({
35
35
  id: Schema.string().required(),
36
36
  fingerprint: Schema.string().required(),
@@ -44,9 +44,9 @@ export const Config = Schema.object({
44
44
  message: Schema.string().required(),
45
45
  providers: Schema.array(Schema.object({}).loose()).default([]),
46
46
  }).loose()).default([]),
47
- staleGraceRuns: Schema.number().default(2),
48
- lifecycleRetentionRuns: Schema.number().default(20),
49
- lifecycleRevision: Schema.number().default(0),
47
+ staleGraceRuns: Schema.number().default(2).volatile(),
48
+ lifecycleRetentionRuns: Schema.number().default(20).volatile(),
49
+ lifecycleRevision: Schema.number().default(0).volatile(),
50
50
  modelLifecycle: Schema.dict(Schema.dict(Schema.object({
51
51
  status: Schema.string().required(),
52
52
  consecutiveMissing: Schema.number().default(0),
@@ -79,7 +79,7 @@ export const Config = Schema.object({
79
79
  strategy: Schema.string(),
80
80
  updatedAt: Schema.number().default(0),
81
81
  }).loose()).default({}),
82
- modelSelections: Schema.dict(Schema.array(Schema.string())).default({}),
82
+ modelSelections: Schema.dict(Schema.array(Schema.string())).default({}).volatile(),
83
83
  adapterRegistry: Schema.dict(Schema.object({
84
84
  enabled: Schema.boolean().default(true),
85
85
  endpoint: Schema.string().default(''),
@@ -110,6 +110,17 @@ export const Config = Schema.object({
110
110
  }).loose())).default({}),
111
111
  })
112
112
 
113
+ // DSH serves a namespace's settings form from the volatile fields of its profile
114
+ // entry schema, and a volatile field holds a Volatile box rather than its value.
115
+ // Unwrap before any caller reads one, and read lazily: the Loader mutates the
116
+ // boxes in place and re-announces them with loader/volatile-update.
117
+ export function plainConfig(value) {
118
+ if (value === null || typeof value !== 'object') return value
119
+ if (Array.isArray(value)) return value.map(plainConfig)
120
+ if (typeof value.get === 'function') return plainConfig(value.get())
121
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, plainConfig(v)]))
122
+ }
123
+
113
124
  export function apply(ctx, config) {
114
125
  const baseConfig = structuredClone(config || {})
115
126
  const resolveConfig = (value) => Config(structuredClone(value || {}))
@@ -125,50 +136,10 @@ export function apply(ctx, config) {
125
136
  return saveConfigImpl(patch)
126
137
  }
127
138
 
128
- let settingsScope
129
- const bindSettings = (settings, ownerCtx) => {
130
- if (settingsScope || !settings?.register) return false
131
- const scope = settings.register(NS, Config, { base: baseConfig })
132
- settingsScope = scope
133
- liveConfig = resolveConfig(scope.get() || baseConfig)
134
- const stop = scope.watch((next) => {
135
- liveConfig = resolveConfig(next || baseConfig)
136
- reconfigure()
137
- })
138
- let saveQueue = Promise.resolve()
139
- saveConfigImpl = (patch) => {
140
- const nextTask = saveQueue.then(async () => {
141
- const result = await scope.update(patch)
142
- liveConfig = resolveConfig(scope.get() || baseConfig)
143
- return result
144
- })
145
- saveQueue = nextTask.catch(() => {})
146
- return nextTask
147
- }
148
- ownerCtx.effect(() => () => {
149
- settingsScope = undefined
150
- saveConfigImpl = undefined
151
- stop()
152
- }, 'dsh-model-sync: settings')
153
- return true
154
- }
155
-
156
- let settings
157
- try { settings = ctx.get('settings') } catch (e) {
158
- if (typeof ctx.logger?.debug === 'function') ctx.logger.debug('[dsh-model-sync] settings service not immediately available:', e)
159
- }
160
- if (!bindSettings(settings, ctx)) {
161
- ctx.inject(['settings'], (sctx) => {
162
- bindSettings((typeof sctx.get === 'function' ? sctx.get('settings') : null) ?? sctx.settings, sctx)
163
- })
164
- }
165
-
166
-
167
- const service = createProviderService(ctx)
168
- const synchronizer = createModelSynchronizer(ctx, { getConfig: () => liveConfig, saveConfig })
169
- const scheduler = createSyncScheduler(synchronizer, { getConfig: () => liveConfig })
170
- reconfigure = () => scheduler.reconfigure()
171
- scheduler.start()
139
+ // settings.register was removed before 0.1.7-rc.2. The host hands the profile entry
140
+ // config to apply() directly, and liveConfig above is derived from it; the
141
+ // Loader mutates the boxes in place and re-announces them with
142
+ // loader/volatile-update.
172
143
  ctx.effect(() => () => scheduler.stop(), 'dsh-model-sync: scheduler cleanup')
173
144
  ctx.effect(() => {
174
145
  const registrations = registerHttpApi(ctx, synchronizer, { getSchedulerStatus: () => scheduler.status() })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-model-sync",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "Automatic model catalog synchronization for API-key DeepSeek Harness providers.",
5
5
  "keywords": [
6
6
  "deepseek-harness",
@@ -53,7 +53,7 @@
53
53
  "license": "MIT",
54
54
  "peerDependencies": {
55
55
  "@deepseek-ai/cordis": "^4.0.1",
56
- "@deepseek-ai/schemastery": "^3.18.1",
56
+ "@deepseek-ai/schemastery": "^3.18.4",
57
57
  "@deepseek-ai/dsh-llm": "^0.1.7-rc.2 || ^0.2.0-rc.1"
58
58
  }
59
59
  }