@goodandready/dsh-model-sync 0.3.10 → 0.3.11
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/README.md +9 -0
- package/README.ru.md +9 -0
- package/README.zh.md +10 -0
- package/lib/client.js +5 -12
- package/lib/index.js +1 -1
- package/lib/provider-service.js +12 -3
- package/lib/synchronizer.js +645 -636
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -107,6 +107,15 @@ dsh plugin --profile web add @goodandready/dsh-model-sync
|
|
|
107
107
|
|
|
108
108
|
---
|
|
109
109
|
|
|
110
|
+
## 🛠️ Enhancements & Fixes in v0.3.11
|
|
111
|
+
|
|
112
|
+
* 🗂️ **Streamlined Settings UI Surface**:
|
|
113
|
+
* Removed the redundant top-level settings sidebar section fallback (`settings.section`), keeping configuration strictly inside the standard DSH plugin settings card (`settings.plugin.item`).
|
|
114
|
+
* 🛡️ **Hardened Cordis Service Access**:
|
|
115
|
+
* Service access for `settings` and `llm` is now performed via `ctx.get(...)` with safe fallback to direct property inspection, preventing silent resolution failures on Cordis proxy implementations.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
110
119
|
## 🛠️ Enhancements & Fixes in v0.3.8
|
|
111
120
|
|
|
112
121
|
* ⚙️ **Reactive Settings Card Binding (`settingsScope`)**:
|
package/README.ru.md
CHANGED
|
@@ -84,6 +84,15 @@ dsh plugin --profile web add @goodandready/dsh-model-sync
|
|
|
84
84
|
|
|
85
85
|
---
|
|
86
86
|
|
|
87
|
+
## 🛠️ Улучшения и исправления в v0.3.11
|
|
88
|
+
|
|
89
|
+
* 🗂️ **Чистый интерфейс настроек (DSH Design Contract)**:
|
|
90
|
+
* Удалена избыточная регистрация fallback-раздела верхнего уровня бокового меню (`settings.section`), настройки доступны только через стандартную карточку в «Настройки → Плагины → Настройки плагинов» (`settings.plugin.item`).
|
|
91
|
+
* 🛡️ **Надёжный доступ к сервисам Cordis**:
|
|
92
|
+
* Доступ к сервисам ядра (`settings`, `llm`) переведён на `ctx.get(...)` с безопасным fallback к прямым свойствам контекста для исключения ошибок проксирования.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
87
96
|
## 🛠️ Улучшения и исправления в v0.3.8
|
|
88
97
|
|
|
89
98
|
* ⚙️ **Реактивное управление настройками через `settingsScope`**:
|
package/README.zh.md
CHANGED
|
@@ -73,3 +73,13 @@ dsh plugin --profile web add @goodandready/dsh-model-sync
|
|
|
73
73
|
## 📄 开源协议
|
|
74
74
|
|
|
75
75
|
MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)
|
|
76
|
+
|
|
77
|
+
## 🛠️ v0.3.11 改进与修复
|
|
78
|
+
|
|
79
|
+
* 🗂️ **规范化设置界面**:
|
|
80
|
+
* 移除侧边栏顶层多余的 `settings.section` fallback,统一使用标准插件设置卡片 `settings.plugin.item`。
|
|
81
|
+
* 🛡️ **增强 Cordis 服务访问稳定性**:
|
|
82
|
+
* 使用 `ctx.get(...)` 安全获取 `settings` 与 `llm` 服务,避免代理对象属性解析异常。
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
package/lib/client.js
CHANGED
|
@@ -49,7 +49,6 @@ window.__ModuleLoader__.load({
|
|
|
49
49
|
}
|
|
50
50
|
const API = '/dsh-model-sync'
|
|
51
51
|
const CAPABILITY_LABELS = { vision: { en: 'Vision', ru: 'Зрение' }, tools: { en: 'Tools', ru: 'Инструменты' }, reasoning: { en: 'Reasoning', ru: 'Рассуждение' }, embeddings: { en: 'Embeddings', ru: 'Эмбеддинги' } }
|
|
52
|
-
function capabilityKeys(models) { return [...new Set((models ?? []).flatMap((model) => Object.entries(model?.capabilities ?? {}).filter(([, enabled]) => enabled === true).map(([key]) => key)))].sort() }
|
|
53
52
|
function filterModelsByCapabilities(models, selected) { const required = [...new Set((selected ?? []).filter((key) => typeof key === 'string' && key))]; return (models ?? []).filter((model) => required.every((key) => model?.capabilities?.[key] === true)) }
|
|
54
53
|
const NS = 'dsh-model-sync'
|
|
55
54
|
function useLocale(ctx) {
|
|
@@ -86,7 +85,7 @@ window.__ModuleLoader__.load({
|
|
|
86
85
|
const doTry = (provider, modelId) => { setTryState((s)=>({ ...s, [provider+':'+modelId]: { loading: true } })); fetch(`${API}/try`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ provider, model: modelId }) }).then(async r=>{ const b=await r.json(); if(!r.ok) throw new Error(b?.error?.message||`HTTP ${r.status}`); return b }).then(b=>setTryState((s)=>({ ...s, [provider+':'+modelId]: { ok: b.ok, latency: b.latencyMs, status: b.status, error: b.error } }))).catch(e=>setTryState((s)=>({ ...s, [provider+':'+modelId]: { error: String(e.message||e) } }))) }
|
|
87
86
|
const load = React.useCallback(() => { setError(''); return fetch(`${API}/status`).then((r) => r.json()).then(setStatus).catch((e) => setError(String(e?.message ?? e))) }, [])
|
|
88
87
|
const loadCredentials = React.useCallback(() => { setError(''); return fetch(`${API}/credentials`).then(async (r) => { const body = await r.json(); if (!r.ok) throw new Error(body?.error?.message ?? `HTTP ${r.status}`); return body }).then(setCredentialStatus).catch((e) => setError(String(e?.message ?? e))) }, [])
|
|
89
|
-
React.useEffect(() => { ensureStyles(); load(); loadCredentials(); const timer = setInterval(load, 15000); return () => clearInterval(timer) }, [load, loadCredentials])
|
|
88
|
+
React.useEffect(() => { ensureStyles(); load(); loadCredentials(); const timer = setInterval(() => { if (typeof document !== 'undefined' && document.hidden) return; load() }, 15000); return () => clearInterval(timer) }, [load, loadCredentials])
|
|
90
89
|
const execute = (targetProvider, dryRunOverride = dryRun) => { if (!dryRunOverride && removeMissing && !window.confirm(t.removeMissing)) return; setBusy(true); setError(''); fetch(`${API}/run`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ provider: targetProvider || undefined, dryRun: dryRunOverride, removeMissing }) }).then(async (r) => { const body = await r.json(); if (!r.ok) throw new Error(body?.error?.message ?? `HTTP ${r.status}`); return body }).then((body) => { setStatus(body); if (dryRunOverride) setPreview({ ...body, provider: targetProvider || '' }); else setPreview(null) }).catch((e) => setError(String(e?.message ?? e))).finally(() => setBusy(false)) }
|
|
91
90
|
const checkHealth = (targetProvider) => { setBusy(true); setError(''); fetch(`${API}/health`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(targetProvider ? { provider: targetProvider } : {}) }).then(async (r) => { const body = await r.json(); if (!r.ok) throw new Error(body?.error?.message ?? `HTTP ${r.status}`); return body }).then(setStatus).catch((e) => setError(String(e?.message ?? e))).finally(() => setBusy(false)) }
|
|
92
91
|
const checkCredentials = (targetProvider) => { setBusy(true); setError(''); fetch(`${API}/credentials/check`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(targetProvider ? { provider: targetProvider } : {}) }).then(async (r) => { const body = await r.json(); if (!r.ok) throw new Error(body?.error?.message ?? `HTTP ${r.status}`); return body }).then((body) => { setCredentialStatus(body.credentials); setStatus((current) => ({ ...(current ?? {}), lastHealth: body.health })) }).catch((e) => setError(String(e?.message ?? e))).finally(() => setBusy(false)) }
|
|
@@ -184,7 +183,7 @@ window.__ModuleLoader__.load({
|
|
|
184
183
|
function ModelSyncCard({ ctx }) {
|
|
185
184
|
const locale = useLocale(ctx); const t = locale === 'ru' ? ru : en; const [open, setOpen] = React.useState(false)
|
|
186
185
|
const [cardStatus, setCardStatus] = React.useState(null)
|
|
187
|
-
React.useEffect(() => { ensureStyles(); const load = () => fetch(`${API}/status`).then(r=>r.json()).then(setCardStatus).catch(()=>{}); load(); const id=setInterval(load,15000); return ()=>clearInterval(id)}, [])
|
|
186
|
+
React.useEffect(() => { ensureStyles(); const load = () => { if (typeof document !== 'undefined' && document.hidden) return; fetch(`${API}/status`).then(r=>r.json()).then(setCardStatus).catch(()=>{}) }; load(); if (open) return; const id = setInterval(load, 15000); return () => clearInterval(id) }, [open])
|
|
188
187
|
const cardBadge = (cardStatus?.lastRun?.results?.reduce((s,r)=>s+(r.diff?.added?.length||0),0) || 0)
|
|
189
188
|
return React.createElement('li', { className: 'dms-card' + (open ? ' dms-card-open' : '') },
|
|
190
189
|
React.createElement('button', { type: 'button', className: 'dms-card-header', 'aria-expanded': open, 'aria-label': `${t[open ? 'hideSettings' : 'showSettings']}: ${t.title}`, onClick: () => setOpen((value) => !value) },
|
|
@@ -198,15 +197,9 @@ window.__ModuleLoader__.load({
|
|
|
198
197
|
try { ctx.locale?.register?.(NS, { en, ru }) } catch {}
|
|
199
198
|
return () => {}
|
|
200
199
|
}, 'dsh-model-sync: dictionaries')
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
return true
|
|
205
|
-
} catch { return false }
|
|
206
|
-
}
|
|
207
|
-
if (!registerPluginCard()) {
|
|
208
|
-
ctx.slots.inject('settings.section', () => ctx.slots.register({ name: 'settings.section', id: 'dsh-model-sync', order: 25, label: () => 'Model Sync' }, (props) => React.createElement(ModelSyncSection, { ...props, ctx })))
|
|
209
|
-
}
|
|
200
|
+
try {
|
|
201
|
+
ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({ name: 'settings.plugin.item', key: NS, locale: NS, inject: () => ({ ctx }) }, (props) => React.createElement(ModelSyncCard, { ...props, ctx })))
|
|
202
|
+
} catch {}
|
|
210
203
|
}
|
|
211
204
|
module.exports = { apply, inject: ['slots', 'locale', 'settingsScope'], filterModelsByCapabilities }; return module.exports
|
|
212
205
|
},
|
package/lib/index.js
CHANGED
|
@@ -137,7 +137,7 @@ export function apply(ctx, config) {
|
|
|
137
137
|
try { settings = ctx.get('settings') } catch {}
|
|
138
138
|
if (!bindSettings(settings, ctx)) {
|
|
139
139
|
ctx.inject(['settings'], (sctx) => {
|
|
140
|
-
bindSettings(sctx.settings, sctx)
|
|
140
|
+
bindSettings((typeof sctx.get === 'function' ? sctx.get('settings') : null) ?? sctx.settings, sctx)
|
|
141
141
|
})
|
|
142
142
|
}
|
|
143
143
|
|
package/lib/provider-service.js
CHANGED
|
@@ -3,13 +3,22 @@ import { listApiKeyProviders } from './inventory.js'
|
|
|
3
3
|
export function createProviderService(ctx) {
|
|
4
4
|
const readSettings = () => {
|
|
5
5
|
try {
|
|
6
|
-
return ctx.get('settings')
|
|
6
|
+
return (typeof ctx.get === 'function' ? (ctx.get('settings') ?? ctx.settings) : ctx.settings)
|
|
7
7
|
} catch {
|
|
8
|
-
return
|
|
8
|
+
return ctx.settings
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const readLlm = () => {
|
|
13
|
+
try {
|
|
14
|
+
const llm = typeof ctx.get === 'function' ? ctx.get('llm') : null
|
|
15
|
+
return (llm && typeof llm === 'object' && (llm.listConfigurableProviders || llm.listProviders)) ? llm : ctx.llm
|
|
16
|
+
} catch {
|
|
17
|
+
return ctx.llm
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const listProviders = () => listApiKeyProviders(readLlm(), readSettings())
|
|
13
22
|
|
|
14
23
|
return Object.freeze({
|
|
15
24
|
listProviders,
|