@goodandready/dsh-model-sync 0.5.4 → 0.5.5

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.
@@ -12,7 +12,7 @@ const PROVIDER_ADAPTERS = Object.freeze({
12
12
  clinebot: {
13
13
  endpoint: 'https://api.cline.bot/api/v1/users/me/plan',
14
14
  auth: 'bearer',
15
- parser: 'clinebot-plan',
15
+ parse: 'clinebot-plan',
16
16
  defaultModels: [
17
17
  'cline-pass/deepseek-v4-flash',
18
18
  'cline-pass/deepseek-v4-pro',
@@ -99,12 +99,13 @@ function mappedRows(provider, rows, descriptor) {
99
99
  }
100
100
 
101
101
  function parseRows(provider, payload, descriptor) {
102
- if (descriptor.parse === 'clinebot-plan') {
102
+ const parserKind = descriptor.parse || descriptor.parser
103
+ if (parserKind === 'clinebot-plan') {
103
104
  const plan = payload?.data?.plan || payload?.data || payload?.plan || payload
104
105
  const included = plan?.features?.included || plan?.includedModels || []
105
106
  return parseClinePassModels(included)
106
107
  }
107
- if (descriptor.parse === 'google') {
108
+ if (parserKind === 'google') {
108
109
  const rows = Array.isArray(payload?.models) ? payload.models : []
109
110
  return normalizeModels(provider, rows.map((row) => ({
110
111
  ...row,
@@ -224,9 +225,6 @@ async function fetchSpecific(profile, descriptor, options) {
224
225
  return rows
225
226
  }
226
227
  } catch (error) {
227
- if (descriptor.defaultModels && Array.isArray(descriptor.defaultModels) && descriptor.defaultModels.length > 0) {
228
- return normalizeModels(profile.provider, descriptor.defaultModels.map((id) => ({ id })))
229
- }
230
228
  throw error
231
229
  }
232
230
  if (descriptor.defaultModels && Array.isArray(descriptor.defaultModels) && descriptor.defaultModels.length > 0) {
package/lib/client.js CHANGED
@@ -785,6 +785,20 @@ window.__ModuleLoader__.load({
785
785
  .finally(() => setBusy(false))
786
786
  }
787
787
 
788
+ const resolveSmartAlias = (aliasName) => {
789
+ setResolvingKey(aliasName)
790
+ fetch(`${API}/aliases/resolve?alias=${encodeURIComponent(aliasName)}`)
791
+ .then(async (r) => {
792
+ const res = await r.json()
793
+ if (!r.ok) throw new Error(res.error?.message ?? `HTTP ${r.status}`)
794
+ setResolvedAliases((prev) => ({ ...prev, [aliasName]: res }))
795
+ })
796
+ .catch((e) => {
797
+ setResolvedAliases((prev) => ({ ...prev, [aliasName]: { error: String(e?.message ?? e) } }))
798
+ })
799
+ .finally(() => setResolvingKey(''))
800
+ }
801
+
788
802
  const load = () => {
789
803
  if (typeof document !== 'undefined' && document.hidden) return
790
804
  setError('')
package/lib/index.js CHANGED
@@ -123,7 +123,16 @@ export function plainConfig(value) {
123
123
 
124
124
  export function apply(ctx, config) {
125
125
  const baseConfig = structuredClone(plainConfig(config || {}))
126
- const resolveConfig = (value) => plainConfig(Config(plainConfig(value) || {}))
126
+ const resolveConfig = (value) => {
127
+ try {
128
+ return plainConfig(Config(plainConfig(value) || {}))
129
+ } catch (err) {
130
+ if (typeof ctx?.logger?.warn === "function") {
131
+ ctx.logger.warn("[dsh-model-sync] invalid config payload, fallback to current config:", err?.message || err)
132
+ }
133
+ return liveConfig ?? baseConfig
134
+ }
135
+ }
127
136
  let liveConfig = resolveConfig(baseConfig)
128
137
  let reconfigure = () => {}
129
138
  let saveConfigImpl = async (patch) => {
@@ -165,12 +174,16 @@ export function apply(ctx, config) {
165
174
  reconfigure = () => scheduler.reconfigure()
166
175
  scheduler.start()
167
176
 
168
- if (typeof ctx.on === 'function') {
169
- ctx.on('loader/volatile-update', () => {
177
+ ctx.effect(() => {
178
+ if (typeof ctx.on !== 'function') return () => {}
179
+ const dispose = ctx.on('loader/volatile-update', () => {
170
180
  liveConfig = resolveConfig(config)
171
181
  reconfigure()
172
182
  })
173
- }
183
+ return () => {
184
+ if (typeof dispose === 'function') dispose()
185
+ }
186
+ }, 'dsh-model-sync: volatile-update listener')
174
187
 
175
188
  // settings.register was removed before 0.1.7-rc.2. The host hands the profile entry
176
189
  // config to apply() directly, and liveConfig above is derived from it; the
@@ -189,7 +202,6 @@ export function apply(ctx, config) {
189
202
  manifestUrl: new URL('../package.json', import.meta.url),
190
203
  }), 'dsh-model-sync: plugin updater')
191
204
  ctx.effect(() => {
192
- if (!liveConfig.enabled) return () => {}
193
205
  ctx.provide('modelSync', service)
194
206
  ctx.provide('modelSyncRunner', synchronizer)
195
207
  return () => {}
package/lib/inventory.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { normalizeModels } from './models.js'
2
+ import { sectionOf } from './synchronizer-helpers.js'
2
3
 
3
4
  export const API_KEY_PROVIDER_IDS = Object.freeze([
4
5
  'anthropic',
@@ -42,7 +43,7 @@ const API_KEY_PROVIDER_SET = new Set(API_KEY_PROVIDER_IDS)
42
43
 
43
44
  function settingsProfiles(settings) {
44
45
  try {
45
- return settings?.get('llm-pi-ai')?.providers ?? {}
46
+ return sectionOf(settings, 'llm-pi-ai')?.providers ?? {}
46
47
  } catch {
47
48
  return {}
48
49
  }
@@ -66,7 +67,7 @@ export function listApiKeyProviders(llm, settings) {
66
67
  for (const entry of directory) {
67
68
  let profile = profiles[entry.provider]
68
69
  if (!profile && (entry.provider === 'commandcode' || entry.settingsNs === 'llm-commandcode')) {
69
- const cc = settings?.get?.('llm-commandcode')
70
+ const cc = sectionOf(settings, 'llm-commandcode')
70
71
  profile = {
71
72
  apiKeyEnv: cc?.apiKeyEnv ?? 'COMMANDCODE_API_KEY',
72
73
  credentialRef: cc?.apiKeyEnv ?? 'COMMANDCODE_API_KEY',
@@ -55,12 +55,23 @@ export function descriptorOf(settings, ns) {
55
55
  catch { return undefined }
56
56
  }
57
57
 
58
+ export function sectionOf(settings, ns) {
59
+ try {
60
+ const desc = descriptorOf(settings, ns)
61
+ if (desc && desc.value !== undefined) return desc.value
62
+ if (typeof settings?.get === 'function') return settings.get(ns)
63
+ return undefined
64
+ } catch {
65
+ return undefined
66
+ }
67
+ }
68
+
58
69
  export function profileOf(settings, provider) {
59
70
  try {
60
- const piai = settings?.get(PIAI_NS)?.providers?.[provider]
71
+ const piai = sectionOf(settings, PIAI_NS)?.providers?.[provider]
61
72
  if (piai) return piai
62
73
  if (provider === 'commandcode') {
63
- const cc = settings?.get?.('llm-commandcode')
74
+ const cc = sectionOf(settings, 'llm-commandcode')
64
75
  return {
65
76
  apiKeyEnv: cc?.apiKeyEnv ?? 'COMMANDCODE_API_KEY',
66
77
  credentialRef: cc?.apiKeyEnv ?? 'COMMANDCODE_API_KEY',
@@ -97,7 +108,7 @@ export function adapterErrorType(error) {
97
108
  export async function replaceWithRetry(settings, ns, patchFn, attempts = 3) {
98
109
  let lastError
99
110
  for (let i = 0; i < attempts; i++) {
100
- const section = settings?.get?.(ns)
111
+ const section = sectionOf(settings, ns)
101
112
  const descriptor = descriptorOf(settings, ns)
102
113
  if (!section || !descriptor || typeof settings.replace !== 'function') {
103
114
  throw Object.assign(new Error(`${ns} settings are not writable`), { code: 'SETTINGS_UNAVAILABLE' })
@@ -120,7 +131,7 @@ export async function replaceWithRetry(settings, ns, patchFn, attempts = 3) {
120
131
 
121
132
  export function rotationRefs(settings, provider) {
122
133
  try {
123
- const rows = settings?.get?.('dsh-key-rotation')?.providers
134
+ const rows = sectionOf(settings, 'dsh-key-rotation')?.providers
124
135
  if (!Array.isArray(rows)) return []
125
136
  return rows.filter((row) => row?.provider === provider).flatMap((row) => Array.isArray(row.keys) ? row.keys : [])
126
137
  } catch {
@@ -149,7 +160,7 @@ export async function performHistoryRollback({ ctx, config, providers = [], hist
149
160
  if (!snapshot || !Array.isArray(snapshot.before)) throw Object.assign(new Error(`history snapshot not found: ${provider}`), { code: 'HISTORY_NOT_FOUND' })
150
161
  if (!providers.some((row) => row.provider === provider && row.configured)) throw Object.assign(new Error(`provider is not configured: ${provider}`), { code: 'PROVIDER_NOT_CONFIGURED' })
151
162
  const settings = settingsOf(ctx)
152
- const section = settings?.get?.(PIAI_NS)
163
+ const section = sectionOf(settings, PIAI_NS)
153
164
  const descriptor = descriptorOf(settings, PIAI_NS)
154
165
  if (!section || !descriptor || typeof settings.replace !== 'function') {
155
166
  throw Object.assign(new Error('llm-pi-ai settings are not writable'), { code: 'SETTINGS_UNAVAILABLE' })
@@ -18,6 +18,7 @@ import {
18
18
  settingsOf,
19
19
  llmOf,
20
20
  descriptorOf,
21
+ sectionOf,
21
22
  profileOf,
22
23
  errorMessage,
23
24
  diagnosticMessage,
@@ -488,7 +489,7 @@ export function createModelSynchronizer(ctx, { getConfig = () => ({}), saveConfi
488
489
  if (typeof saveConfig !== 'function') throw Object.assign(new Error('dsh-model-sync settings are not writable'), { code: 'CONFIG_UNAVAILABLE' })
489
490
 
490
491
  const settings = settingsOf(ctx)
491
- const section = settings?.get?.(PIAI_NS)
492
+ const section = sectionOf(settings, PIAI_NS)
492
493
  const descriptor = descriptorOf(settings, PIAI_NS)
493
494
  const current = normalizeModels(provider, profileOf(settings, provider).models ?? row.models)
494
495
  const nextModels = selectedModels.length
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-model-sync",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "Automatic model catalog synchronization for API-key DeepSeek Harness providers.",
5
5
  "keywords": [
6
6
  "deepseek-harness",