odant-work 1.0.15 → 1.0.16

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 (78) hide show
  1. package/#system/package-manifest.json +51 -43
  2. package/$server/$folder/handlers/methods/create/$handler/class.js +83 -2
  3. package/$server/$folder/lib/icon/icon.js +14 -0
  4. package/$server/$folder/lib/node/node.js +51 -1
  5. package/$server/$folder/lib/tree/tree.js +5 -1
  6. package/.secretsignore +4 -0
  7. package/MODELS/$ai/$folder/$class/$ai/class.js +57 -2
  8. package/MODELS/$ai/$folder/$class/$provider/class.js +1 -1
  9. package/MODELS/$ai/$folder/$class/$provider/handlers/pages/form/provider/$handler/class.js +13 -0
  10. package/MODELS/$ai/$folder/$class/$provider/handlers/pages/form/provider/$handler/provider.js +479 -0
  11. package/MODELS/$ai/$folder/$class/$provider/handlers/pages/form/trainer/$handler/trainer.js +3 -3
  12. package/MODELS/GigaChat/$provider/$folder/$class/$ai/class.js +10 -20
  13. package/MODELS/GigaChat/$provider/class.js +10 -7
  14. package/MODELS/GigaChat/GigaChat 2 Lite/$ai/class.js +15 -0
  15. package/MODELS/GigaChat/GigaChat 2 Max/$ai/class.js +16 -0
  16. package/MODELS/GigaChat/GigaChat 2 Pro/$ai/class.js +15 -0
  17. package/MODELS/GigaChat/GigaChat Embeddings/$ai/class.js +13 -0
  18. package/MODELS/Local/$provider/$folder/$class/$ai/class.js +3 -3
  19. package/MODELS/Local/$provider/class.js +7 -3
  20. package/MODELS/MTS-AI/$provider/$folder/$class/$ai/class.js +8 -0
  21. package/MODELS/MTS-AI/$provider/class.js +8 -0
  22. package/MODELS/MTS-AI/Cotype Lite/$ai/class.js +15 -0
  23. package/MODELS/MTS-AI/Cotype Pro 2/$ai/class.js +15 -0
  24. package/MODELS/MTS-AI/Cotype Vision/$ai/class.js +14 -0
  25. package/MODELS/T-Bank/$provider/$folder/$class/$ai/class.js +8 -0
  26. package/MODELS/T-Bank/$provider/class.js +8 -0
  27. package/MODELS/T-Bank/T-Lite 2.0/$ai/class.js +15 -0
  28. package/MODELS/T-Bank/T-Pro 2.0/$ai/class.js +16 -0
  29. package/MODELS/T-Bank/T-VoiceKit ASR/$ai/class.js +13 -0
  30. package/MODELS/T-Bank/T-VoiceKit TTS/$ai/class.js +13 -0
  31. package/MODELS/Yandex/$provider/$folder/$class/$ai/class.js +9 -0
  32. package/MODELS/Yandex/$provider/class.js +9 -0
  33. package/MODELS/Yandex/Alice AI (YandexGPT 5 Pro)/$ai/class.js +15 -0
  34. package/MODELS/Yandex/Yandex Embeddings/$ai/class.js +13 -0
  35. package/MODELS/Yandex/Yandex SpeechKit ASR/$ai/class.js +13 -0
  36. package/MODELS/Yandex/Yandex SpeechKit TTS/$ai/class.js +13 -0
  37. package/MODELS/Yandex/YandexART/$ai/class.js +13 -0
  38. package/MODELS/Yandex/YandexGPT 5 Lite/$ai/class.js +15 -0
  39. package/MODELS/Yandex/YandexGPT Reasoning/$ai/class.js +22 -0
  40. package/oda/tools/icons/lib/svg/ai.svg +20 -0
  41. package/package.json +1 -1
  42. package/scripts/check-ignored-code.mjs +3 -0
  43. package/scripts/check-secrets.mjs +1 -1
  44. package/sources/data/progress.md +14 -0
  45. package/sources/host/watch.js +13 -2
  46. package/sources/modules/agent/index.js +23 -2
  47. package/sources/modules/trainer/halogen.js +128 -29
  48. package/sources/modules/trainer/huggingface.js +11 -5
  49. package/sources/modules/trainer/index.js +135 -2
  50. package/sources/modules/trainer/providers.js +1213 -0
  51. package/sources/server/class.js +55 -3
  52. package/MODELS/GigaChat/GigaChat Light/$ai/class.js +0 -15
  53. package/MODELS/GigaChat/GigaChat Pro/$ai/class.js +0 -15
  54. package/MODELS/Halogen/$provider/$folder/$class/$ai/class.js +0 -11
  55. package/MODELS/Halogen/$provider/class.js +0 -16
  56. package/MODELS/Halogen/$provider/readme.md +0 -28
  57. package/MODELS/Halogen/Decider-NPU/$ai/class.js +0 -22
  58. package/MODELS/Halogen/Flux2-Klein-NPU/$ai/class.js +0 -21
  59. package/MODELS/Halogen/Qwen3-ASR-NPU/$ai/class.js +0 -21
  60. package/MODELS/Halogen/Qwen3-Embedding-NPU/$ai/class.js +0 -22
  61. package/MODELS/Halogen/Qwen3-Reranker-NPU/$ai/class.js +0 -21
  62. package/MODELS/Halogen/Qwen3-TTS-NPU/$ai/class.js +0 -26
  63. package/MODELS/Halogen/Qwen3.5-2B-NPU/$ai/class.js +0 -23
  64. package/MODELS/Halogen/Qwen3.8-Flash-Next/$ai/class.js +0 -31
  65. package/MODELS/Local/Flux-Image/$ai/class.js +0 -20
  66. package/MODELS/Local/Qwen3-0.6B/$ai/class.js +0 -26
  67. package/MODELS/Local/Qwen3-14B/$ai/class.js +0 -27
  68. package/MODELS/Local/Qwen3-30B-A3B/$ai/class.js +0 -30
  69. package/MODELS/Local/Qwen3-Embedding/$ai/class.js +0 -21
  70. package/MODELS/Local/Qwen3-Voice/$ai/class.js +0 -26
  71. package/MODELS/odant/$provider/$folder/$class/$ai/class.js +0 -11
  72. package/MODELS/odant/$provider/class.js +0 -75
  73. package/MODELS/odant/$provider/readme.md +0 -21
  74. package/MODELS/odant/Qwen3-ASR/$ai/class.js +0 -15
  75. package/MODELS/odant/Qwen3-TTS/$ai/class.js +0 -22
  76. package/MODELS/odant/Qwen3.8 27b/$ai/class.js +0 -11
  77. package/MODELS/odant/Z-Image-Turbo/$ai/class.js +0 -8
  78. package/MODELS/odant/Z-Image-Turbo/$ai/readme.md +0 -24
@@ -0,0 +1,8 @@
1
+ export default {
2
+ "icon": "ai:mts",
3
+ "label": "MTS AI (Cotype)",
4
+ "form": "provider",
5
+ "protocol": "openai",
6
+ "baseUrl": "https://api.mts.ai/v1/chat/completions",
7
+ "apiKey": "secret:mts.json"
8
+ };
@@ -0,0 +1,15 @@
1
+ export default {
2
+ "label": "Cotype Lite",
3
+ "model": "cotype-lite",
4
+ "icon": "ai:mts",
5
+ "modality": "llm",
6
+ "maxTokens": 32768,
7
+ "maxOutput": 8192,
8
+ "functionCalling": true,
9
+ "capabilities": [
10
+ "chat",
11
+ "stream",
12
+ "tools",
13
+ "functions"
14
+ ]
15
+ };
@@ -0,0 +1,15 @@
1
+ export default {
2
+ "label": "Cotype Pro 2",
3
+ "model": "cotype-pro-2",
4
+ "icon": "ai:mts",
5
+ "modality": "llm",
6
+ "maxTokens": 131072,
7
+ "maxOutput": 8192,
8
+ "functionCalling": true,
9
+ "capabilities": [
10
+ "chat",
11
+ "stream",
12
+ "tools",
13
+ "functions"
14
+ ]
15
+ };
@@ -0,0 +1,14 @@
1
+ export default {
2
+ "label": "Cotype Vision",
3
+ "model": "cotype-vl",
4
+ "icon": "ai:mts",
5
+ "modality": "vlm",
6
+ "maxTokens": 32768,
7
+ "maxOutput": 8192,
8
+ "functionCalling": true,
9
+ "capabilities": [
10
+ "chat",
11
+ "stream",
12
+ "vision"
13
+ ]
14
+ };
@@ -0,0 +1,8 @@
1
+ export default {
2
+ "icon": "ai:tbank",
3
+ "description": "Модель провайдера T-Bank AI (T-Pro)",
4
+ "protocol": "openai",
5
+ "baseUrl": "https://api.tbank.ru/v1/chat/completions",
6
+ "functionCalling": true,
7
+ "apiKey": "secret:tbank.json"
8
+ };
@@ -0,0 +1,8 @@
1
+ export default {
2
+ "icon": "ai:tbank",
3
+ "label": "T-Bank AI (T-Pro)",
4
+ "form": "provider",
5
+ "protocol": "openai",
6
+ "baseUrl": "https://api.tbank.ru/v1/chat/completions",
7
+ "apiKey": "secret:tbank.json"
8
+ };
@@ -0,0 +1,15 @@
1
+ export default {
2
+ "label": "T-Lite 2.0",
3
+ "model": "t-tech/T-lite-it-2.0",
4
+ "icon": "ai:tbank",
5
+ "modality": "llm",
6
+ "maxTokens": 131072,
7
+ "maxOutput": 8192,
8
+ "functionCalling": true,
9
+ "capabilities": [
10
+ "chat",
11
+ "stream",
12
+ "tools",
13
+ "functions"
14
+ ]
15
+ };
@@ -0,0 +1,16 @@
1
+ export default {
2
+ "label": "T-Pro 2.0",
3
+ "model": "t-tech/T-pro-it-2.0",
4
+ "icon": "ai:tbank",
5
+ "modality": "llm",
6
+ "maxTokens": 131072,
7
+ "maxOutput": 8192,
8
+ "functionCalling": true,
9
+ "capabilities": [
10
+ "chat",
11
+ "stream",
12
+ "tools",
13
+ "functions",
14
+ "thinking"
15
+ ]
16
+ };
@@ -0,0 +1,13 @@
1
+ export default {
2
+ "label": "T-VoiceKit ASR",
3
+ "model": "t-voicekit-asr",
4
+ "icon": "carbon:microphone",
5
+ "modality": "audio",
6
+ "maxTokens": 4096,
7
+ "maxOutput": 2048,
8
+ "functionCalling": false,
9
+ "capabilities": [
10
+ "stt",
11
+ "asr"
12
+ ]
13
+ };
@@ -0,0 +1,13 @@
1
+ export default {
2
+ "label": "T-VoiceKit TTS",
3
+ "model": "t-voicekit-tts",
4
+ "icon": "carbon:volume-up",
5
+ "modality": "audio",
6
+ "maxTokens": 4096,
7
+ "maxOutput": 2048,
8
+ "functionCalling": false,
9
+ "capabilities": [
10
+ "tts",
11
+ "voice"
12
+ ]
13
+ };
@@ -0,0 +1,9 @@
1
+ export default {
2
+ "icon": "ai:yandex",
3
+ "description": "Модель провайдера Yandex AI (Алиса)",
4
+ "protocol": "yandex",
5
+ "baseUrl": "https://llm.api.cloud.yandex.net/v1/chat/completions",
6
+ "functionCalling": true,
7
+ "apiKey": "secret:yandex.json",
8
+ "folderId": "secret:yandex.json"
9
+ };
@@ -0,0 +1,9 @@
1
+ export default {
2
+ "icon": "ai:yandex",
3
+ "label": "Yandex AI (Алиса)",
4
+ "form": "provider",
5
+ "protocol": "yandex",
6
+ "baseUrl": "https://llm.api.cloud.yandex.net/v1/chat/completions",
7
+ "apiKey": "secret:yandex.json",
8
+ "folderId": "secret:yandex.json"
9
+ };
@@ -0,0 +1,15 @@
1
+ export default {
2
+ "label": "Alice AI (YandexGPT 5 Pro)",
3
+ "model": "yandexgpt/latest",
4
+ "icon": "ai:yandex",
5
+ "modality": "llm",
6
+ "maxTokens": 32768,
7
+ "maxOutput": 8192,
8
+ "functionCalling": true,
9
+ "capabilities": [
10
+ "chat",
11
+ "stream",
12
+ "tools",
13
+ "functions"
14
+ ]
15
+ };
@@ -0,0 +1,13 @@
1
+ export default {
2
+ "label": "Yandex Embeddings",
3
+ "model": "text-search-doc/latest",
4
+ "icon": "carbon:search-locate",
5
+ "modality": "embed",
6
+ "maxTokens": 4096,
7
+ "maxOutput": 1024,
8
+ "functionCalling": false,
9
+ "capabilities": [
10
+ "embeddings",
11
+ "rag"
12
+ ]
13
+ };
@@ -0,0 +1,13 @@
1
+ export default {
2
+ "label": "Yandex SpeechKit ASR",
3
+ "model": "general:rc",
4
+ "icon": "carbon:microphone",
5
+ "modality": "audio",
6
+ "maxTokens": 4096,
7
+ "maxOutput": 2048,
8
+ "functionCalling": false,
9
+ "capabilities": [
10
+ "stt",
11
+ "asr"
12
+ ]
13
+ };
@@ -0,0 +1,13 @@
1
+ export default {
2
+ "label": "Yandex SpeechKit TTS",
3
+ "model": "alena",
4
+ "icon": "carbon:volume-up",
5
+ "modality": "audio",
6
+ "maxTokens": 4096,
7
+ "maxOutput": 2048,
8
+ "functionCalling": false,
9
+ "capabilities": [
10
+ "tts",
11
+ "voice"
12
+ ]
13
+ };
@@ -0,0 +1,13 @@
1
+ export default {
2
+ "label": "YandexART",
3
+ "model": "yandex-art/latest",
4
+ "icon": "ai:stablediffusion",
5
+ "modality": "image",
6
+ "maxTokens": 4096,
7
+ "maxOutput": 1024,
8
+ "functionCalling": false,
9
+ "capabilities": [
10
+ "image",
11
+ "text-to-image"
12
+ ]
13
+ };
@@ -0,0 +1,15 @@
1
+ export default {
2
+ "label": "YandexGPT 5 Lite",
3
+ "model": "yandexgpt-lite/latest",
4
+ "icon": "ai:yandex",
5
+ "modality": "llm",
6
+ "maxTokens": 32768,
7
+ "maxOutput": 8192,
8
+ "functionCalling": true,
9
+ "capabilities": [
10
+ "chat",
11
+ "stream",
12
+ "tools",
13
+ "functions"
14
+ ]
15
+ };
@@ -0,0 +1,22 @@
1
+ export default {
2
+ "label": "YandexGPT Reasoning",
3
+ "model": "yandexgpt/rc",
4
+ "icon": "ai:yandex",
5
+ "modality": "llm",
6
+ "maxTokens": 32768,
7
+ "maxOutput": 8192,
8
+ "functionCalling": true,
9
+ "capabilities": [
10
+ "chat",
11
+ "stream",
12
+ "tools",
13
+ "functions",
14
+ "effort",
15
+ "thinking"
16
+ ],
17
+ "efforts": [
18
+ "low",
19
+ "medium",
20
+ "high"
21
+ ]
22
+ };
@@ -209,4 +209,24 @@
209
209
  <symbol id="characterai">
210
210
  <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z"/>
211
211
  </symbol>
212
+
213
+ <!-- Yandex AI / Алиса (Яндекс) -->
214
+ <symbol id="yandex">
215
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm3.2 15.5h-2.1v-4.3h-1.1c-1.5 0-2.3.8-2.9 2.3l-.8 2H6.1l1.1-2.5c.6-1.3 1.3-2 2.3-2.3-1.5-.4-2.5-1.6-2.5-3.4 0-2.3 1.6-3.8 4.3-3.8h3.9v12zm-2.1-6.1V7.3h-1.6c-1.5 0-2.3.7-2.3 2s.8 2.1 2.3 2.1h1.6z"/>
216
+ </symbol>
217
+
218
+ <!-- T-Bank AI (T-Pro) -->
219
+ <symbol id="tbank">
220
+ <path d="M4 3h16a1 1 0 0 1 1 1v9c0 4.5-3.8 7.8-9 9-5.2-1.2-9-4.5-9-9V4a1 1 0 0 1 1-1zm3 4v2.5h3.7V17h2.6V9.5H17V7H7z"/>
221
+ </symbol>
222
+
223
+ <!-- MTS AI (Cotype) -->
224
+ <symbol id="mts">
225
+ <path d="M12 2C7.5 2 4 7.2 4 13c0 5 3.5 9 8 9s8-4 8-9c0-5.8-3.5-11-8-11zm0 3.2c2.9 0 5.2 4 5.2 7.8 0 3.4-2.3 6-5.2 6s-5.2-2.6-5.2-6c0-3.8 2.3-7.8 5.2-7.8z"/>
226
+ </symbol>
227
+
228
+ <!-- OpenRouter -->
229
+ <symbol id="openrouter">
230
+ <path d="M12 2L3 7v10l9 5 9-5V7l-9-5zm0 2.3l6.8 3.8L12 11.9 5.2 8.1 12 4.3zM5 9.8l6 3.4v6.5l-6-3.3V9.8zm8 9.9v-6.5l6-3.4v6.6l-6 3.3z"/>
231
+ </symbol>
212
232
  </svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "odant-work",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "ODANT-WORK — платформа цифровой организации (встроенная система учёта, документооборот, управление процессами и приватный мультимодальный ИИ)",
5
5
  "type": "module",
6
6
  "main": "sources/work.js",
@@ -69,7 +69,10 @@ if (ignored.length) {
69
69
 
70
70
  // Проверяем также .npmignore через npm pack --dry-run --json (кроме tests и license-server)
71
71
  const NPM_CODE_ROOTS = new Set(['$server', 'sources', 'oda', 'scripts', 'rules', 'MODELS', 'NODES', 'SERVICES']);
72
+ const NPM_INTENTIONAL_EXCLUDE = /^MODELS\/(?:odant|Halogen)\/|^MODELS\/Local\/(?!\$provider\/)/;
72
73
  const npmCandidates = candidates.filter(f => {
74
+ if (NPM_INTENTIONAL_EXCLUDE.test(f))
75
+ return false;
73
76
  const top = f.split('/')[0];
74
77
  return NPM_CODE_ROOTS.has(top) || !f.includes('/');
75
78
  });
@@ -84,7 +84,7 @@ export function scanText(content, ignores = []) {
84
84
  continue;
85
85
  const value = m[0];
86
86
  if (rule === 'keyword-value' || rule === 'bearer-basic-b64') {
87
- const v = (value.match(/['"]([^'"]+)['"]/) || value.match(/[A-Za-z0-9+/]{20,}/) || [])[1] || value;
87
+ const v = (value.match(/['"]([^'"]+)['"]\s*$/) || value.match(/['"]([^'"]+)['"]/) || value.match(/[A-Za-z0-9+/]{20,}/) || [])[1] || value;
88
88
  if (SAFE_VALUE.test(v) || SAFE_MARK.test(v) || SAFE_MARK.test(line))
89
89
  continue;
90
90
  }
@@ -1,6 +1,20 @@
1
1
  # Прогресс: sources/data
2
2
 
3
3
  ## Последние изменения
4
+ - [20:55, 13.11] Архитектура провайдеров и моделей ИИ в `/MODELS`: чистота дистрибутива (исключён `odant`, пустой единый `/MODELS/Local` вместе с контуром машинок Halo), предустановленные российские провайдеры (`GigaChat`, `Yandex`, `T-Bank`, `MTS-AI`), быстрое добавление любых внешних провайдеров и цветовая индикация активности моделей в дереве (`sources/modules/trainer/{providers,halogen,huggingface,index}.js`, `MODELS/$ai/**`, `MODELS/{GigaChat,Yandex,T-Bank,MTS-AI,Local}/**`, `$server/$folder/handlers/methods/create/$handler/class.js`, `$server/$folder/lib/{node/node,icon/icon,tree/tree}.js`, `sources/server/class.js`, `.npmignore`, `odant-work@1.0.16`):
5
+ - **Чистота `/MODELS` в поставке и объединение `Local` + `Halogen`:**
6
+ - Внутренний провайдер разработчика `/MODELS/odant` исключён из npm-дистрибутива (`.npmignore`: `/MODELS/odant/`), а `defaultModel()` в `sources/modules/agent/index.js` автоматически переключается на первую доступную активную модель в `/MODELS`.
7
+ - Ветки `Local` и `Halogen` объединены в единый провайдер **`/MODELS/Local`**, который в чистой поставке полностью пуст — дочерние узлы моделей (`$ai`) создаются в `/MODELS/Local/<Модель>` только при реальной установке/загрузке весов (включая матрицу из 8 GPU+NPU моделей для машинок Halo на базе AMD Ryzen™ AI Max+ PRO 495 192 ГБ).
8
+ - **Предустановленные российские провайдеры ИИ без зашитых ключей:**
9
+ - **`/MODELS/GigaChat` (Сбер):** `GigaChat 2 Lite`, `GigaChat 2 Pro`, `GigaChat 2 Max`, `GigaChat Embeddings`.
10
+ - **`/MODELS/Yandex` (Яндекс AI / Алиса, протокол `yandex`):** `Alice AI (YandexGPT 5 Pro)`, `YandexGPT 5 Lite`, `YandexGPT Reasoning`, `YandexART`, `Yandex SpeechKit ASR`, `Yandex SpeechKit TTS`, `Yandex Embeddings`.
11
+ - **`/MODELS/T-Bank` (Т-Банк AI / T-Pro):** `T-Pro 2.0`, `T-Lite 2.0`, `T-VoiceKit ASR`, `T-VoiceKit TTS`.
12
+ - **`/MODELS/MTS-AI` (МТС AI / Cotype):** `Cotype Pro 2`, `Cotype Lite`, `Cotype Vision`.
13
+ - **Мастер быстрого добавления провайдеров и экран `form/provider`:**
14
+ - При нажатии `+` на `/MODELS` открывается витрина шаблонов мировых и российских провайдеров (`OpenAI`, `Anthropic`, `DeepSeek`, `Gemini`, `OpenRouter`, `Grok`, `Mistral`, `Ollama`, `GigaChat`, `Yandex`, `T-Bank`, `MTS-AI`, `Custom`) с вводом API-ключа и автосозданием набора моделей в 1 клик.
15
+ - Добавлено представление **`form/provider` («Подключение и модели»)** на узлах `$provider` для сохранения ключей в `#secret`, проверки связи с API (`GET /v1/models`), включения/выключения и добавления моделей в 1 клик.
16
+ - **Визуальная индикация активных и неактивных моделей в дереве (`aiActive`):**
17
+ - Активные провайдеры и модели (у которых настроен API-ключ в `#secret` или скачаны локальные веса в `#weights`) помечаются в дереве зелёным индикатором `.ai-dot.active` и полной яркостью, а неактивные — приглушённым серым цветом (`.ai-inactive`, `opacity: 0.48`) с подсказкой причины и скрываются из выпадающего селектора моделей чата.
4
18
  - [20:05, 12.11] Прямая 1-клик регистрация первого администратора (`BOSS`) без настроенного почтового сервера и горячее перечитывание SMTP-настроек из `WORK → Почта` (`$server/#secret/email.json`) и `#system/mail.json` (`sources/host/mail.js`, `sources/host/auth-methods.js`, `sources/modules/user-profile/user-profile.js`, `tests/license-and-deploy.test.js`, `odant-work@1.0.15`):
5
19
  - **Регистрация первого пользователя (`BOSS`) без SMTP:** если на новом компьютере почтовый сервер ещё не настроен (`getMailer() === null`), а в `/USERS` ещё нет зарегистрированного `BOSS`, `user_register_start` возвращает `{ autoCode }`, и `user-profile.js` сразу в 1 клик генерирует криптоключ, регистрирует пользователя как `BOSS` всего `WORK` и выполняет вход без требования почтового кода.
6
20
  - **Настройка почтовика из интерфейса WORK и горячий подхват (`sources/host/mail.js`):** `getMailer()` динамически (по `mtime` без перезапуска сервера) читает настройки SMTP как из `#system/mail.json`, так и из штатного диалога **«Почтовые ящики» (`⚙️` на форме `WORK → Почта`, файл `$server/#secret/email.json`)**, а при отсутствии файлов не засоряет консоль ошибкой `ENOENT`.
@@ -50,14 +50,25 @@ function flush($server) {
50
50
  Promise.resolve(globalThis.WORK_AGENT_CORE?.()).then(c => c?.voice?.resetVoiceProbes?.()).catch(() => {});
51
51
  }
52
52
  for (const f of files) {
53
- const p = '/' + f.replace(/\\/g, '/');
53
+ const norm = f.replace(/\\/g, '/');
54
+ const p = '/' + norm;
55
+ if (!fs.existsSync(f)) {
56
+ const parentDir = path.posix.dirname(norm);
57
+ if (parentDir && parentDir !== '.' && fs.existsSync(parentDir)) {
58
+ Promise.resolve(globalThis.WORK?.get_item('/' + parentDir)).then(item => {
59
+ if (item && !Array.isArray(item))
60
+ item.reset?.();
61
+ }).catch(() => {});
62
+ }
63
+ continue;
64
+ }
54
65
  Promise.resolve(globalThis.WORK?.get_item(p)).then(item => {
55
66
  if (item && !Array.isArray(item))
56
67
  item.reset?.();
57
68
  }).catch(() => {});
58
69
  // Если изменился /X/$type/class.js — сбрасываем также сам узел класса /X
59
70
  const clsMatch = p.match(/^(\/.*?)\/\$[^/]+\/class\.js$/i);
60
- if (clsMatch?.[1]) {
71
+ if (clsMatch?.[1] && fs.existsSync(clsMatch[1].slice(1))) {
61
72
  Promise.resolve(globalThis.WORK?.get_item(clsMatch[1])).then(clsItem => {
62
73
  if (clsItem && !Array.isArray(clsItem))
63
74
  clsItem.reset?.();
@@ -259,7 +259,24 @@ export async function llmWithFallback(primaryPath, fallbackPaths = []) {
259
259
  };
260
260
  return adapter;
261
261
  }
262
- /** Первая модель с capability image (или ai/config.js imageModel). */
262
+ /** Первая доступная чат-модель в /MODELS (активная приоритетно), когда дефолтная модель отсутствует. */
263
+ async function findFirstAvailableChatModel() {
264
+ const root = await WORK.get_item('/MODELS').catch(() => null);
265
+ let firstAny = '';
266
+ for (const provider of (await root?.items) || []) {
267
+ for (const m of (await provider.items) || []) {
268
+ await m.init;
269
+ const caps = Array.isArray(m.capabilities) ? m.capabilities : String(m.capabilities || '').split(/[\s,]+/);
270
+ if (!caps.includes('chat') || typeof m.streamChat !== 'function')
271
+ continue;
272
+ if (!firstAny)
273
+ firstAny = m.path;
274
+ if ((m.aiActive ?? m.DATA?.aiActive) !== false)
275
+ return m.path;
276
+ }
277
+ }
278
+ return firstAny;
279
+ }
263
280
  async function findImageModel(config) {
264
281
  if (config.imageModel) {
265
282
  const m = await WORK.get_item(config.imageModel).catch(() => null);
@@ -530,7 +547,11 @@ export async function createEnv({ place, contextPath, session, host, tz, locatio
530
547
  return [stable, volatile].filter(Boolean).join('\n\n');
531
548
  },
532
549
  async defaultModel() {
533
- return config.model || DEFAULT_MODEL;
550
+ const candidate = config.model || DEFAULT_MODEL;
551
+ const item = await WORK.get_item(candidate).catch(() => null);
552
+ if (item)
553
+ return candidate;
554
+ return (await findFirstAvailableChatModel()) || candidate;
534
555
  },
535
556
  /** Память изменена инструментом memory — пересобрать блок на следующем ходе. */
536
557
  resetMemory() {