@goodandready/dsh-russian-lang 0.3.26 → 0.3.28
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 +39 -24
- package/bin/dsh-i18n.mjs +150 -25
- package/glossary.json +206 -0
- package/lib/client.js +193 -118
- package/lib/locales/plugins/17-dsh-context.json +1 -1
- package/lib/locales/plugins/26-config-manager.json +1 -1
- package/lib/locales/plugins/34-key-rotation.json +1 -1
- package/lib/locales/plugins/48-task-tracker.json +1 -1
- package/lib/translator.js +4 -1
- package/package.json +4 -2
- package/lib/client.template.js +0 -2800
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<h3>Полная русская локализация, умная типографика и исправление раскладки клавиатуры для DeepSeek Harness</h3>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<a href="https://www.npmjs.com/package/@goodandready/dsh-russian-lang"><img src="https://img.shields.io/badge/npm-v0.3.
|
|
8
|
+
<a href="https://www.npmjs.com/package/@goodandready/dsh-russian-lang"><img src="https://img.shields.io/badge/npm-v0.3.28-6366f1.svg?style=for-the-badge&labelColor=1e1b4b" alt="npm version"></a>
|
|
9
9
|
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-10b981.svg?style=for-the-badge&color=10b981&labelColor=064e3b" alt="license"></a>
|
|
10
10
|
<a href="https://github.com/topics/dsh-plugin"><img src="https://img.shields.io/badge/DSH-Plugin-8b5cf6.svg?style=for-the-badge&labelColor=2e1065" alt="DSH Plugin"></a>
|
|
11
11
|
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node-20%2B-f59e0b.svg?style=for-the-badge&labelColor=451a03" alt="Node version"></a>
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<p align="center">
|
|
15
15
|
<img src="https://img.shields.io/badge/DSH-v0.2.0--rc.2%2B-blue.svg?style=for-the-badge&labelColor=1e1b4b" alt="DSH v0.1.7-rc.2+">
|
|
16
16
|
<img src="https://img.shields.io/badge/Покрытие-100%25-10b981.svg?style=for-the-badge&labelColor=064e3b" alt="Покрытие 100%">
|
|
17
|
-
<img src="https://img.shields.io/badge/Ключей_UI-
|
|
17
|
+
<img src="https://img.shields.io/badge/Ключей_UI-8584-6366f1.svg?style=for-the-badge&labelColor=1e1b4b" alt="8584 ключей">
|
|
18
18
|
<img src="https://img.shields.io/badge/Ручная_вычитка-100%25-0ea5e9.svg?style=for-the-badge&labelColor=082f49" alt="100% ручная вычитка">
|
|
19
19
|
</p>
|
|
20
20
|
|
|
@@ -52,9 +52,9 @@ graph LR
|
|
|
52
52
|
DSH[Web UI Интерфейс] --> Loader[Загрузчик локализации dsh-russian-lang]
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
subgraph TranslationLayer [Слой перевода и адаптации —
|
|
56
|
-
Loader --> CoreDict[58 Пространств имён ядра DSH /
|
|
57
|
-
Loader --> PluginDict[
|
|
55
|
+
subgraph TranslationLayer [Слой перевода и адаптации — 8 584 ключа / 100% вычитано]
|
|
56
|
+
Loader --> CoreDict[58 Пространств имён ядра DSH / 2 442 ключа]
|
|
57
|
+
Loader --> PluginDict[43 Пространства имён плагинов DSH / 6 142 ключа]
|
|
58
58
|
Loader --> PluralEngine[Движок грамматических Plural-форм: 1/2/5]
|
|
59
59
|
end
|
|
60
60
|
|
|
@@ -85,7 +85,7 @@ graph LR
|
|
|
85
85
|
|
|
86
86
|
## ✨ Полный обзор возможностей
|
|
87
87
|
|
|
88
|
-
### 1. 🌐 100% покрытие ядра DSH (
|
|
88
|
+
### 1. 🌐 100% покрытие ядра DSH (58 пространств имён, 2 442 ключа)
|
|
89
89
|
* **Диалоги и переписка (`conversation`)**: лента сообщений, ветки обсуждений, карточки вызова инструментов, статус генерации и рассуждений (thinking traces).
|
|
90
90
|
* **Модели и провайдеры (`settings.models`)**: каталог моделей, параметры инференса, управление токенами и ключами API.
|
|
91
91
|
* **Трейсы рассуждений (`trajectory`)**: детальные шаги, параметры вызовов инструментов, временные метки и визуализация размышлений модели.
|
|
@@ -94,9 +94,9 @@ graph LR
|
|
|
94
94
|
* **Планировщик и инвентарь (`schedule.catalog`, `settings.pluginInventory`, `settings.plugins`)**: фоновые задания, периодичность, фильтры и каталог установленных плагинов.
|
|
95
95
|
* **Права доступа и безопасность (`permission.access`, `plan`, `skill`)**: пресеты прав (полный доступ, только чтение, запись в рабочую область), выполнение планов и навыки.
|
|
96
96
|
|
|
97
|
-
> **О качестве перевода.** Все **
|
|
97
|
+
> **О качестве перевода.** Все **8 584 ключа интерфейса (2 442 ядра + 6 142 плагинов) переведены на 100% и вычитаны вручную**. Черновой машинный перевод полностью замещён проверенными формулировками (`draft = 0`). Перевод регулярно валидируется автоматическими линтерами целостности плейсхолдеров, проверкой соблюдения глоссария (`glossary.json`) и детекторами переполнения верстки. Нашли неточность — кнопка «Сообщить о проблеме перевода» в карточке настроек создаёт готовый issue.
|
|
98
98
|
|
|
99
|
-
### 2. 🧩 Встроенный перевод 65+ плагинов экосистемы DSH (
|
|
99
|
+
### 2. 🧩 Встроенный перевод 65+ плагинов экосистемы DSH (6 142 ключа)
|
|
100
100
|
Локализация автоматически распространяется на все ключевые плагины экосистемы DeepSeek Harness:
|
|
101
101
|
* **`dsh-session-control`** — расширенное управление сессиями: закрепление диалогов, архив по периодам, просмотр расшифровок и фильтрация шума;
|
|
102
102
|
* **`dsh-kanban` и `task-board`** — канбан-доски задач, карточки, чек-листы, дедлайны, критерии готовности (DoD) и шлюз приёмки (Acceptance Gate);
|
|
@@ -149,7 +149,7 @@ graph LR
|
|
|
149
149
|
|
|
150
150
|
### 7. ⚡ Оптимизации производительности и архитектурная безопасность
|
|
151
151
|
* **Пакетная обработка MutationObserver**: оптимизированный наблюдатель мутаций интерфейса с адаптивным debounce (16 мс) предотвращает лавинообразные перерисовки и перерасчёт макета (reflow) при интенсивном рендеринге списков и стриминге LLM-токенов.
|
|
152
|
-
* **Защита HTTP-маршрутов словарей (405 Method Not Allowed)**: серверные эндпоинты `/dict/core`, `/dict/plugins`, `/dict/all
|
|
152
|
+
* **Защита HTTP-маршрутов словарей (405 Method Not Allowed)**: серверные эндпоинты `/api/dsh-russian-lang/dict/core`, `/api/dsh-russian-lang/dict/plugins`, `/api/dsh-russian-lang/dict/all` строго защищены и принимают только безопасные методы GET/HEAD.
|
|
153
153
|
* **Клиентский бандл с запасом**: оптимизированная архитектура и компактный бутстрап обеспечивают размер клиентского скрипта 153.7 KiB (запас более 10 KiB до предельного лимита).
|
|
154
154
|
|
|
155
155
|
### 8. 🔤 Нативная интеграция в ядро
|
|
@@ -167,33 +167,48 @@ dsh plugin --profile web add @goodandready/dsh-russian-lang
|
|
|
167
167
|
|
|
168
168
|
---
|
|
169
169
|
|
|
170
|
-
## ⚙️ Параметры конфигурации (`settings.yaml`)
|
|
170
|
+
## ⚙️ Параметры конфигурации (`settings.yaml` / профиль Cordis)
|
|
171
171
|
|
|
172
|
-
|
|
173
|
-
|
|
172
|
+
В актуальных версиях DSH (v0.2.0-rc.2+, v0.1.7+) конфигурация плагина управляется штатно через managed-форму в веб-интерфейсе (**Настройки → Плагины → Русская локализация**) либо в профиле/конфиге под идентификатором плагина `dsh-russian-lang` (согласно `cordis.patch.yml`).
|
|
173
|
+
|
|
174
|
+
Для полной обратной совместимости с ранними версиями ядра (DSH <= 0.1.6) плагин также автоматически поддерживает чтение из легаси-пространства `russian-lang`.
|
|
175
|
+
|
|
176
|
+
Схема параметров объявлена в [`lib/index.js`](lib/index.js), все поля необязательные:
|
|
174
177
|
|
|
175
178
|
```yaml
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
179
|
+
# Актуальный формат (DSH v0.2.0-rc.2+ / v0.1.7+):
|
|
180
|
+
dsh-russian-lang:
|
|
181
|
+
enabled: true # Русский язык интерфейса (по умолчанию: true)
|
|
182
|
+
overrides: {} # Пользовательские оверрайды ключей (по умолчанию: {})
|
|
183
|
+
typography: # Интеллектуальная типографика (по умолчанию: все opt-in false)
|
|
184
|
+
enabled: false # Ёлочки, длинные тире, неразрывные пробелы
|
|
185
|
+
yo: false # Восстановление безопасной буквы «ё»
|
|
186
|
+
liveInput: false # Живая типографика при вводе в текстовых полях
|
|
187
|
+
agentPrompt: false # Системная инструкция агенту отвечать по-русски (по умолчанию: false)
|
|
188
|
+
agentPromptPreset: technical_expert # Пресет системного промпта (по умолчанию: technical_expert)
|
|
189
|
+
slashAliases: true # Русские алиасы слэш-команд (/помощь, /очистить) (по умолчанию: true)
|
|
190
|
+
layoutConversion: true # Детектор неверной раскладки и подсказка Alt+L (по умолчанию: true)
|
|
191
|
+
quickSwitch: true # Быстрый переключатель RU/EN в статусной строке (по умолчанию: true)
|
|
192
|
+
translateEngine: local # Движок перевода реплик: local (LibreTranslate) или cloud (Google) (по умолчанию: local)
|
|
193
|
+
localApiUrl: http://localhost:5000 # URL локального инстанса LibreTranslate (по умолчанию: http://localhost:5000)
|
|
194
|
+
|
|
195
|
+
# Легаси-формат (DSH <= 0.1.6, поддерживается автоматически как fallback):
|
|
196
|
+
# russian-lang:
|
|
197
|
+
# enabled: true
|
|
198
|
+
# ...
|
|
183
199
|
```
|
|
184
200
|
|
|
185
|
-
|
|
201
|
+
Удобнее всего настраивать параметры визуально в интерфейсе: **Настройки → Плагины → Русская локализация**.
|
|
186
202
|
|
|
187
|
-
Исправление раскладки (<kbd>Alt+L</kbd>) и
|
|
188
|
-
русским языком и отдельных ключей не имеют.
|
|
203
|
+
Исправление раскладки (<kbd>Alt+L</kbd>) и русские алиасы слэш-команд включены по умолчанию (`true`) и могут быть индивидуально отключены в настройках (`layoutConversion: false`, `slashAliases: false`). Браузерный спелчек (`lang="ru-RU"`) активируется автоматически при выборе русской локали.
|
|
189
204
|
|
|
190
205
|
---
|
|
191
206
|
|
|
192
207
|
## 🛠 Разработка и зеркало репозитория
|
|
193
208
|
|
|
194
|
-
Публичный репозиторий на GitHub является дистрибутивным зеркалом релизов `@goodandready/dsh-russian-lang`. Основная разработка, сборочный пайплайн и полный тестовый контур ведутся в
|
|
209
|
+
Публичный репозиторий на GitHub является дистрибутивным зеркалом релизов `@goodandready/dsh-russian-lang`. Основная разработка, сборочный пайплайн и полный тестовый контур ведутся в основном Gitea-репозитории проекта.
|
|
195
210
|
|
|
196
|
-
Pull Requests и Issues на GitHub приветствуются и обрабатываются в обычном порядке! Для внесения правок в логику клиента используйте `lib/client.template.js`. Запуск `npm test` в
|
|
211
|
+
Pull Requests и Issues на GitHub приветствуются и обрабатываются в обычном порядке! Для внесения правок в логику клиента используйте `lib/client.template.js`. Запуск `npm test` в репозитории выполняет полную проверку (270+ модульных тестов Node.js, тесты сборщика, линтеры переводов и глоссария).
|
|
197
212
|
|
|
198
213
|
---
|
|
199
214
|
|
package/bin/dsh-i18n.mjs
CHANGED
|
@@ -6,6 +6,10 @@ import { fileURLToPath } from 'node:url'
|
|
|
6
6
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
7
7
|
const ROOT = path.resolve(__dirname, '..')
|
|
8
8
|
|
|
9
|
+
function escapeRegExp(string) {
|
|
10
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
function getPkgVersion() {
|
|
10
14
|
try {
|
|
11
15
|
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'))
|
|
@@ -41,6 +45,115 @@ dsh-i18n (v${getPkgVersion()}) — Инструмент локализации
|
|
|
41
45
|
`)
|
|
42
46
|
}
|
|
43
47
|
|
|
48
|
+
function extractFromCode(code, result, defaultNs = 'common') {
|
|
49
|
+
let i = 0
|
|
50
|
+
const len = code.length
|
|
51
|
+
|
|
52
|
+
function skipWhitespace(idx) {
|
|
53
|
+
while (idx < len && /\s/.test(code[idx])) idx++
|
|
54
|
+
return idx
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function readString(idx) {
|
|
58
|
+
const quote = code[idx]
|
|
59
|
+
if (quote !== "'" && quote !== '"' && quote !== '`') return null
|
|
60
|
+
let s = ''
|
|
61
|
+
idx++
|
|
62
|
+
while (idx < len) {
|
|
63
|
+
const ch = code[idx]
|
|
64
|
+
if (ch === '\\') {
|
|
65
|
+
if (idx + 1 < len) {
|
|
66
|
+
s += code[idx + 1]
|
|
67
|
+
idx += 2
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (ch === quote) {
|
|
72
|
+
return { val: s, next: idx + 1 }
|
|
73
|
+
}
|
|
74
|
+
s += ch
|
|
75
|
+
idx++
|
|
76
|
+
}
|
|
77
|
+
return null
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
while (i < len) {
|
|
81
|
+
const ch = code[i]
|
|
82
|
+
|
|
83
|
+
// Single-line comment
|
|
84
|
+
if (ch === '/' && code[i + 1] === '/') {
|
|
85
|
+
i += 2
|
|
86
|
+
while (i < len && code[i] !== '\n') i++
|
|
87
|
+
continue
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Multi-line comment
|
|
91
|
+
if (ch === '/' && code[i + 1] === '*') {
|
|
92
|
+
i += 2
|
|
93
|
+
while (i < len && !(code[i] === '*' && code[i + 1] === '/')) i++
|
|
94
|
+
i += 2
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// String literals in normal code
|
|
99
|
+
if (ch === "'" || ch === '"' || ch === '`') {
|
|
100
|
+
const strRes = readString(i)
|
|
101
|
+
if (strRes) {
|
|
102
|
+
i = strRes.next
|
|
103
|
+
continue
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Check for word boundary identifier
|
|
108
|
+
const prevChar = i > 0 ? code[i - 1] : ' '
|
|
109
|
+
const isWordStart = !/[a-zA-Z0-9_$.]/.test(prevChar)
|
|
110
|
+
|
|
111
|
+
if (isWordStart) {
|
|
112
|
+
if (code.startsWith('translate', i) && !/[a-zA-Z0-9_$]/.test(code[i + 9] || '')) {
|
|
113
|
+
let j = skipWhitespace(i + 9)
|
|
114
|
+
if (code[j] === '(') {
|
|
115
|
+
j = skipWhitespace(j + 1)
|
|
116
|
+
const nsStr = readString(j)
|
|
117
|
+
if (nsStr) {
|
|
118
|
+
j = skipWhitespace(nsStr.next)
|
|
119
|
+
if (code[j] === ',') {
|
|
120
|
+
j = skipWhitespace(j + 1)
|
|
121
|
+
const keyStr = readString(j)
|
|
122
|
+
if (keyStr) {
|
|
123
|
+
j = skipWhitespace(keyStr.next)
|
|
124
|
+
if (code[j] === ',' || code[j] === ')') {
|
|
125
|
+
const ns = nsStr.val
|
|
126
|
+
const key = keyStr.val
|
|
127
|
+
if (!result[ns]) result[ns] = {}
|
|
128
|
+
result[ns][key] = result[ns][key] || ''
|
|
129
|
+
i = j
|
|
130
|
+
continue
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
} else if (code.startsWith('t', i) && !/[a-zA-Z0-9_$]/.test(code[i + 1] || '')) {
|
|
137
|
+
let j = skipWhitespace(i + 1)
|
|
138
|
+
if (code[j] === '(') {
|
|
139
|
+
j = skipWhitespace(j + 1)
|
|
140
|
+
const keyStr = readString(j)
|
|
141
|
+
if (keyStr) {
|
|
142
|
+
j = skipWhitespace(keyStr.next)
|
|
143
|
+
if (code[j] === ',' || code[j] === ')') {
|
|
144
|
+
result[defaultNs][keyStr.val] = result[defaultNs][keyStr.val] || ''
|
|
145
|
+
i = j
|
|
146
|
+
continue
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
i++
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
44
157
|
function extractKeys(targetDir, defaultNs = 'common') {
|
|
45
158
|
const result = { [defaultNs]: {} }
|
|
46
159
|
const extensions = new Set(['.js', '.mjs', '.cjs', '.ts', '.tsx', '.jsx'])
|
|
@@ -55,17 +168,7 @@ function extractKeys(targetDir, defaultNs = 'common') {
|
|
|
55
168
|
walk(fullPath)
|
|
56
169
|
} else if (entry.isFile() && extensions.has(path.extname(entry.name))) {
|
|
57
170
|
const code = fs.readFileSync(fullPath, 'utf8')
|
|
58
|
-
|
|
59
|
-
for (const m of tMatches) {
|
|
60
|
-
result[defaultNs][m[1]] = result[defaultNs][m[1]] || ''
|
|
61
|
-
}
|
|
62
|
-
const transMatches = code.matchAll(/\btranslate\(\s*['"`]([a-zA-Z0-9_.-]+)['"`]\s*,\s*['"`]([a-zA-Z0-9_.-]+)['"`]/g)
|
|
63
|
-
for (const m of transMatches) {
|
|
64
|
-
const ns = m[1]
|
|
65
|
-
const key = m[2]
|
|
66
|
-
if (!result[ns]) result[ns] = {}
|
|
67
|
-
result[ns][key] = result[ns][key] || ''
|
|
68
|
-
}
|
|
171
|
+
extractFromCode(code, result, defaultNs)
|
|
69
172
|
}
|
|
70
173
|
}
|
|
71
174
|
}
|
|
@@ -88,22 +191,42 @@ function validateDict(filePath, glossaryPath) {
|
|
|
88
191
|
return false
|
|
89
192
|
}
|
|
90
193
|
|
|
194
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
195
|
+
console.error(`Ошибка структуры JSON в ${filePath}: корневой элемент должен быть объектом`)
|
|
196
|
+
return false
|
|
197
|
+
}
|
|
198
|
+
|
|
91
199
|
let errors = []
|
|
92
200
|
let warnings = []
|
|
93
201
|
let totalKeys = 0
|
|
94
202
|
|
|
95
203
|
let glossary = null
|
|
96
|
-
|
|
97
|
-
|
|
204
|
+
if (glossaryPath) {
|
|
205
|
+
if (!fs.existsSync(glossaryPath)) {
|
|
206
|
+
console.error(`Ошибка: Файл глоссария не найден: ${glossaryPath}`)
|
|
207
|
+
return false
|
|
208
|
+
}
|
|
98
209
|
try {
|
|
99
|
-
|
|
100
|
-
|
|
210
|
+
const parsed = JSON.parse(fs.readFileSync(glossaryPath, 'utf8'))
|
|
211
|
+
glossary = parsed.terms || parsed
|
|
212
|
+
} catch (err) {
|
|
213
|
+
console.error(`Ошибка чтения глоссария ${glossaryPath}: ${err.message}`)
|
|
214
|
+
return false
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
const defaultGPath = path.join(ROOT, 'glossary.json')
|
|
218
|
+
if (fs.existsSync(defaultGPath)) {
|
|
219
|
+
try {
|
|
220
|
+
const parsed = JSON.parse(fs.readFileSync(defaultGPath, 'utf8'))
|
|
221
|
+
glossary = parsed.terms || parsed
|
|
222
|
+
} catch (_) {}
|
|
223
|
+
}
|
|
101
224
|
}
|
|
102
225
|
|
|
103
226
|
function checkString(ns, key, val) {
|
|
104
227
|
totalKeys++
|
|
105
228
|
if (typeof val !== 'string') {
|
|
106
|
-
errors.push(`[${ns}:${key}] Значение должно быть строкой, получено: ${typeof val}`)
|
|
229
|
+
errors.push(`[${ns}:${key}] Значение должно быть строкой, получено: ${val === null ? 'null' : Array.isArray(val) ? 'массив' : typeof val}`)
|
|
107
230
|
return
|
|
108
231
|
}
|
|
109
232
|
|
|
@@ -117,13 +240,12 @@ function validateDict(filePath, glossaryPath) {
|
|
|
117
240
|
}
|
|
118
241
|
|
|
119
242
|
if (glossary) {
|
|
120
|
-
const lower = val.toLowerCase()
|
|
121
243
|
for (const [term, info] of Object.entries(glossary)) {
|
|
122
244
|
if (Array.isArray(info.forbidden)) {
|
|
123
245
|
for (const forbidden of info.forbidden) {
|
|
124
|
-
const
|
|
125
|
-
const re = new RegExp(
|
|
126
|
-
if (re.test(
|
|
246
|
+
const esc = escapeRegExp(forbidden)
|
|
247
|
+
const re = new RegExp(`(?<=[^\\p{L}\\p{N}]|^)${esc}(?=[^\\p{L}\\p{N}]|$)`, 'iu')
|
|
248
|
+
if (re.test(val)) {
|
|
127
249
|
warnings.push(`[${ns}:${key}] Запрещённый термин по глоссарию "${forbidden}" (рекомендуется: "${info.canonical}")`)
|
|
128
250
|
}
|
|
129
251
|
}
|
|
@@ -134,10 +256,12 @@ function validateDict(filePath, glossaryPath) {
|
|
|
134
256
|
|
|
135
257
|
for (const ns of Object.keys(data)) {
|
|
136
258
|
const section = data[ns]
|
|
137
|
-
if (typeof section
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
259
|
+
if (!section || typeof section !== 'object' || Array.isArray(section)) {
|
|
260
|
+
errors.push(`[${ns}] Пространство имён должно быть объектом, получено: ${section === null ? 'null' : Array.isArray(section) ? 'массив' : typeof section}`)
|
|
261
|
+
continue
|
|
262
|
+
}
|
|
263
|
+
for (const [k, v] of Object.entries(section)) {
|
|
264
|
+
checkString(ns, k, v)
|
|
141
265
|
}
|
|
142
266
|
}
|
|
143
267
|
|
|
@@ -214,7 +338,8 @@ if (command === 'extract') {
|
|
|
214
338
|
if (out) {
|
|
215
339
|
fs.mkdirSync(path.dirname(path.resolve(out)), { recursive: true })
|
|
216
340
|
fs.writeFileSync(path.resolve(out), jsonStr + '\n', 'utf8')
|
|
217
|
-
|
|
341
|
+
const totalCount = Object.values(extracted).reduce((sum, nObj) => sum + Object.keys(nObj || {}).length, 0)
|
|
342
|
+
console.log(`✓ Извлечено ключей: ${totalCount} -> ${out}`)
|
|
218
343
|
} else {
|
|
219
344
|
console.log(jsonStr)
|
|
220
345
|
}
|
package/glossary.json
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Канонический глоссарий терминов DeepSeek Harness (DSH) и AI-экосистемы",
|
|
5
|
+
"terms": {
|
|
6
|
+
"workspace": {
|
|
7
|
+
"canonical": "Рабочая область",
|
|
8
|
+
"allowed": [
|
|
9
|
+
"Рабочая область",
|
|
10
|
+
"Воркспейс"
|
|
11
|
+
],
|
|
12
|
+
"forbidden": [
|
|
13
|
+
"Рабочее место",
|
|
14
|
+
"Рабочий стол",
|
|
15
|
+
"Пространство задач"
|
|
16
|
+
],
|
|
17
|
+
"description": "Изолированная рабочая директория/проект агента"
|
|
18
|
+
},
|
|
19
|
+
"subagent": {
|
|
20
|
+
"canonical": "Субагент",
|
|
21
|
+
"allowed": [
|
|
22
|
+
"Субагент",
|
|
23
|
+
"Дочерний агент"
|
|
24
|
+
],
|
|
25
|
+
"forbidden": [
|
|
26
|
+
"Подагент",
|
|
27
|
+
"Младший агент",
|
|
28
|
+
"Вспомогательный агент"
|
|
29
|
+
],
|
|
30
|
+
"description": "Автономный дочерний агент"
|
|
31
|
+
},
|
|
32
|
+
"prompt": {
|
|
33
|
+
"canonical": "Промпт",
|
|
34
|
+
"allowed": [
|
|
35
|
+
"Промпт",
|
|
36
|
+
"Инструкция",
|
|
37
|
+
"Системный промпт"
|
|
38
|
+
],
|
|
39
|
+
"forbidden": [
|
|
40
|
+
"Затравка",
|
|
41
|
+
"Побуждение",
|
|
42
|
+
"Внушение"
|
|
43
|
+
],
|
|
44
|
+
"description": "Инструкция или текстовый ввод для модели"
|
|
45
|
+
},
|
|
46
|
+
"tool": {
|
|
47
|
+
"canonical": "Инструмент",
|
|
48
|
+
"allowed": [
|
|
49
|
+
"Инструмент",
|
|
50
|
+
"Вызов инструмента"
|
|
51
|
+
],
|
|
52
|
+
"forbidden": [
|
|
53
|
+
"Орудие",
|
|
54
|
+
"Утилита"
|
|
55
|
+
],
|
|
56
|
+
"description": "Функция или инструмент, вызываемый агентом (tool/tool call)"
|
|
57
|
+
},
|
|
58
|
+
"session": {
|
|
59
|
+
"canonical": "Сессия",
|
|
60
|
+
"allowed": [
|
|
61
|
+
"Сессия",
|
|
62
|
+
"Диалог",
|
|
63
|
+
"Чат"
|
|
64
|
+
],
|
|
65
|
+
"forbidden": [
|
|
66
|
+
"Заседание",
|
|
67
|
+
"Сеанс связи"
|
|
68
|
+
],
|
|
69
|
+
"description": "Отдельный поток диалога или сессия работы"
|
|
70
|
+
},
|
|
71
|
+
"skill": {
|
|
72
|
+
"canonical": "Навык",
|
|
73
|
+
"allowed": [
|
|
74
|
+
"Навык",
|
|
75
|
+
"Навыки",
|
|
76
|
+
"Skill"
|
|
77
|
+
],
|
|
78
|
+
"forbidden": [
|
|
79
|
+
"Умение",
|
|
80
|
+
"Мастерство"
|
|
81
|
+
],
|
|
82
|
+
"description": "Подключаемый модуль навыков агента"
|
|
83
|
+
},
|
|
84
|
+
"plugin": {
|
|
85
|
+
"canonical": "Плагин",
|
|
86
|
+
"allowed": [
|
|
87
|
+
"Плагин",
|
|
88
|
+
"Плагины"
|
|
89
|
+
],
|
|
90
|
+
"forbidden": [
|
|
91
|
+
"Встраиваемый модуль"
|
|
92
|
+
],
|
|
93
|
+
"description": "Плагин Cordis / DSH"
|
|
94
|
+
},
|
|
95
|
+
"provider": {
|
|
96
|
+
"canonical": "Провайдер",
|
|
97
|
+
"allowed": [
|
|
98
|
+
"Провайдер",
|
|
99
|
+
"Провайдеры"
|
|
100
|
+
],
|
|
101
|
+
"forbidden": [
|
|
102
|
+
"Поставщик",
|
|
103
|
+
"Поставщики"
|
|
104
|
+
],
|
|
105
|
+
"description": "Провайдер LLM API (OpenAI, DeepSeek, Ollama и др.)"
|
|
106
|
+
},
|
|
107
|
+
"token": {
|
|
108
|
+
"canonical": "Токен",
|
|
109
|
+
"allowed": [
|
|
110
|
+
"Токен",
|
|
111
|
+
"Токены"
|
|
112
|
+
],
|
|
113
|
+
"forbidden": [
|
|
114
|
+
"Жетон",
|
|
115
|
+
"Жетоны"
|
|
116
|
+
],
|
|
117
|
+
"description": "Единица расхода контекста LLM"
|
|
118
|
+
},
|
|
119
|
+
"thinking": {
|
|
120
|
+
"canonical": "Размышления",
|
|
121
|
+
"allowed": [
|
|
122
|
+
"Размышления",
|
|
123
|
+
"Рассуждения",
|
|
124
|
+
"Цепочка рассуждений"
|
|
125
|
+
],
|
|
126
|
+
"forbidden": [
|
|
127
|
+
"Думание",
|
|
128
|
+
"Мышление модели"
|
|
129
|
+
],
|
|
130
|
+
"description": "Трейс внутренних рассуждений (thinking traces) модели"
|
|
131
|
+
},
|
|
132
|
+
"effort": {
|
|
133
|
+
"canonical": "Уровень рассуждений",
|
|
134
|
+
"allowed": [
|
|
135
|
+
"Уровень рассуждений",
|
|
136
|
+
"Усилие рассуждений",
|
|
137
|
+
"Усилие"
|
|
138
|
+
],
|
|
139
|
+
"forbidden": [
|
|
140
|
+
"Старание",
|
|
141
|
+
"Напряжение"
|
|
142
|
+
],
|
|
143
|
+
"description": "Параметр reasoning effort модели"
|
|
144
|
+
},
|
|
145
|
+
"approval": {
|
|
146
|
+
"canonical": "Подтверждение",
|
|
147
|
+
"allowed": [
|
|
148
|
+
"Подтверждение",
|
|
149
|
+
"Одобрение"
|
|
150
|
+
],
|
|
151
|
+
"forbidden": [
|
|
152
|
+
"Апробация",
|
|
153
|
+
"Санкционирование"
|
|
154
|
+
],
|
|
155
|
+
"description": "Запрос подтверждения опасного действия пользователем"
|
|
156
|
+
},
|
|
157
|
+
"deliverables": {
|
|
158
|
+
"canonical": "Результаты работы",
|
|
159
|
+
"allowed": [
|
|
160
|
+
"Результаты работы",
|
|
161
|
+
"Файлы результатов",
|
|
162
|
+
"Результаты"
|
|
163
|
+
],
|
|
164
|
+
"forbidden": [
|
|
165
|
+
"Поставки",
|
|
166
|
+
"Доставляемые элементы"
|
|
167
|
+
],
|
|
168
|
+
"description": "Созданные агентом итоговые файлы"
|
|
169
|
+
},
|
|
170
|
+
"branch": {
|
|
171
|
+
"canonical": "Ветка",
|
|
172
|
+
"allowed": [
|
|
173
|
+
"Ветка",
|
|
174
|
+
"Ветвление"
|
|
175
|
+
],
|
|
176
|
+
"forbidden": [
|
|
177
|
+
"Ответвление",
|
|
178
|
+
"Ветвь"
|
|
179
|
+
],
|
|
180
|
+
"description": "Ветка диалога или Git-ветка"
|
|
181
|
+
},
|
|
182
|
+
"checkpoint": {
|
|
183
|
+
"canonical": "Контрольная точка",
|
|
184
|
+
"allowed": [
|
|
185
|
+
"Контрольная точка",
|
|
186
|
+
"Чекпоинт"
|
|
187
|
+
],
|
|
188
|
+
"forbidden": [
|
|
189
|
+
"Точка проверки",
|
|
190
|
+
"Контрольный пункт"
|
|
191
|
+
],
|
|
192
|
+
"description": "Точка сохранения состояния сессии или памяти"
|
|
193
|
+
},
|
|
194
|
+
"command_code": {
|
|
195
|
+
"canonical": "Command Code",
|
|
196
|
+
"allowed": [
|
|
197
|
+
"Command Code"
|
|
198
|
+
],
|
|
199
|
+
"forbidden": [
|
|
200
|
+
"Код команды",
|
|
201
|
+
"Командный код"
|
|
202
|
+
],
|
|
203
|
+
"description": "Название провайдера и расширения Command Code (commandcode.ai)"
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|