@goodandready/dsh-model-sync 0.3.8 → 0.3.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/README.md +13 -0
- package/README.ru.md +13 -0
- package/lib/client.js +2 -1
- package/lib/http.js +17 -5
- package/lib/index.js +9 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -142,6 +142,19 @@ dsh plugin --profile web add @goodandready/dsh-model-sync
|
|
|
142
142
|
|
|
143
143
|
---
|
|
144
144
|
|
|
145
|
+
## 🚀 Enhancements in v0.3.9
|
|
146
|
+
|
|
147
|
+
* 🔒 **Serialized Settings Mutations (`saveConfig`)**:
|
|
148
|
+
* Chained internal settings persistence via an async promise mutex (`saveQueue`).
|
|
149
|
+
* Eliminates race conditions between simultaneous sync runs and user UI interactions, ensuring clean atomic writes to `settings.yaml`.
|
|
150
|
+
* 🛡️ **Hardened Cross-Origin & CSRF Protections**:
|
|
151
|
+
* Enhanced `trusted(req)` checking to evaluate `sec-fetch-site`, `origin`, and `referer` against the HTTP `Host`.
|
|
152
|
+
* Blocks unauthorized cross-origin mutations while preserving legitimate local and reverse-proxy requests.
|
|
153
|
+
* 🎨 **Standardized Plugin Style Discovery**:
|
|
154
|
+
* Added `data-dsh-plugin="dsh-model-sync"` attribute to injected UI style tags in compliance with the DeepSeek Harness plugin authoring standard.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
145
158
|
## 🚀 Enhancements in v0.3.5
|
|
146
159
|
|
|
147
160
|
* ⏱️ **Adaptive Rate Limit & Retry-After Handling**:
|
package/README.ru.md
CHANGED
|
@@ -119,6 +119,19 @@ dsh plugin --profile web add @goodandready/dsh-model-sync
|
|
|
119
119
|
|
|
120
120
|
---
|
|
121
121
|
|
|
122
|
+
## 🚀 Новые возможности в v0.3.9
|
|
123
|
+
|
|
124
|
+
* 🔒 **Атомарная очередь сохранения настроек (`saveConfig`)**:
|
|
125
|
+
* Сериализация внутренних мутаций конфигурации через цепочку промисов (`saveQueue`).
|
|
126
|
+
* Предотвращает состояние гонки между фоновой синхронизацией и действиями пользователя в интерфейсе, гарантируя целостность `settings.yaml`.
|
|
127
|
+
* 🛡️ **Усиленная защита от CSRF и cross-origin атак**:
|
|
128
|
+
* Расширенная валидация запросов `trusted(req)` с проверкой заголовков `sec-fetch-site`, `origin` и `referer` против `Host`.
|
|
129
|
+
* Блокировка межсайтовых вызовов мутаций без нарушения локальных обращений и проксирования.
|
|
130
|
+
* 🎨 **Стандартизация инжекции стилей интерфейса**:
|
|
131
|
+
* Добавлен атрибут `data-dsh-plugin="dsh-model-sync"` для тега стилей в полном соответствии со стандартом разработки плагинов DeepSeek Harness.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
122
135
|
## 🚀 Новые возможности в v0.3.5
|
|
123
136
|
|
|
124
137
|
* ⏱️ **Адаптивная обработка Rate Limit и Retry-After**:
|
package/lib/client.js
CHANGED
|
@@ -33,10 +33,11 @@ window.__ModuleLoader__.load({
|
|
|
33
33
|
function ensureStyles() {
|
|
34
34
|
if (typeof document === 'undefined' || !document.head) return
|
|
35
35
|
const cssId = 'dsh-model-sync/settings.module.css'
|
|
36
|
-
if (document.querySelector('style[data-plugin-css="' + cssId + '"]')) return
|
|
36
|
+
if (document.querySelector('style[data-plugin-css="' + cssId + '"]') || document.querySelector('style[data-dsh-plugin="dsh-model-sync"]')) return
|
|
37
37
|
const tag = document.createElement('style')
|
|
38
38
|
tag.textContent = CSS
|
|
39
39
|
tag.setAttribute('data-plugin', 'dsh-model-sync')
|
|
40
|
+
tag.setAttribute('data-dsh-plugin', 'dsh-model-sync')
|
|
40
41
|
tag.setAttribute('data-plugin-css', cssId)
|
|
41
42
|
document.head.appendChild(tag)
|
|
42
43
|
}
|
package/lib/http.js
CHANGED
|
@@ -8,12 +8,24 @@ function json(res, status, body) {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
function trusted(req) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (!origin) return true
|
|
11
|
+
const fetchSite = req.headers?.['sec-fetch-site']
|
|
12
|
+
if (fetchSite === 'cross-site') return false
|
|
14
13
|
const host = req.headers?.host
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const origin = req.headers?.origin
|
|
15
|
+
if (origin) {
|
|
16
|
+
if (!host) return false
|
|
17
|
+
try { return new URL(origin).host === host } catch { return false }
|
|
18
|
+
}
|
|
19
|
+
const referer = req.headers?.referer
|
|
20
|
+
if (referer) {
|
|
21
|
+
if (!host) return false
|
|
22
|
+
try { return new URL(referer).host === host } catch { return false }
|
|
23
|
+
}
|
|
24
|
+
// If sec-fetch-site is same-origin or same-site, allow
|
|
25
|
+
if (fetchSite === 'same-origin' || fetchSite === 'same-site') return true
|
|
26
|
+
// If sec-fetch-mode is present but origin/referer missing on a cross-site request, reject
|
|
27
|
+
if (req.headers?.['sec-fetch-mode'] && !host) return false
|
|
28
|
+
return true
|
|
17
29
|
}
|
|
18
30
|
|
|
19
31
|
function readJson(req) {
|
package/lib/index.js
CHANGED
|
@@ -115,10 +115,15 @@ export function apply(ctx, config) {
|
|
|
115
115
|
liveConfig = resolveConfig(next || baseConfig)
|
|
116
116
|
reconfigure()
|
|
117
117
|
})
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
let saveQueue = Promise.resolve()
|
|
119
|
+
saveConfigImpl = (patch) => {
|
|
120
|
+
const nextTask = saveQueue.then(async () => {
|
|
121
|
+
const result = await scope.update(patch)
|
|
122
|
+
liveConfig = resolveConfig(scope.get() || baseConfig)
|
|
123
|
+
return result
|
|
124
|
+
})
|
|
125
|
+
saveQueue = nextTask.catch(() => {})
|
|
126
|
+
return nextTask
|
|
122
127
|
}
|
|
123
128
|
ownerCtx.effect(() => () => {
|
|
124
129
|
settingsScope = undefined
|