@goodandready/dsh-russian-lang 0.3.0 → 0.3.1
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 +1 -1
- package/bin/dsh-i18n.mjs +247 -0
- package/lib/client.js +753 -471
- package/lib/index.js +69 -13
- package/lib/locales/plugins/33-messenger-gateway.json +1 -1
- package/lib/locales/plugins/35-gitea.json +1 -1
- package/lib/locales/plugins/40-clinebot.json +1 -1
- package/lib/locales/plugins/41-lanmode.json +1 -1
- package/lib/locales/plugins/48-task-tracker.json +1 -1
- package/lib/locales/plugins/54-workshop-market.json +1 -1
- package/lib/locales/plugins/56-cron.json +1 -1
- package/lib/locales/plugins/58-grok-xsearch.json +1 -1
- package/lib/locales/plugins/59-goal.json +1 -1
- package/lib/locales/plugins/60-model-sync.json +1 -1
- package/lib/locales/plugins/61-issue-reporter.json +1 -1
- package/lib/locales/plugins/62-model-search.json +1 -1
- package/lib/locales/plugins/63-agent-orchestrator.json +1 -1
- package/lib/locales/plugins/65-subscriptions.json +1 -1
- package/lib/locales/plugins/67-voice.json +1 -1
- package/lib/locales/plugins/68-image-gen.json +1 -1
- package/lib/locales/plugins/70-hypotheses.json +1 -1
- package/lib/locales/plugins/72-server-monitor.json +1 -1
- package/lib/locales/plugins/73-agentrouter.json +1 -1
- package/lib/locales/plugins/77-github-ops.json +1 -0
- package/lib/locales/plugins/78-web-gateway.json +1 -0
- package/lib/locales/plugins/79-approval-gate.json +1 -0
- package/lib/locales.js +50 -12
- package/lib/pure.js +251 -13
- package/package.json +5 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"dsh-image-gen":{"card.actionRemoveBg":"Удалить фон","card.actionReroll":"Перегенерировать","card.actionUpscale":"Увеличить разрешение","card.copyPrompt":"Скопировать промпт","card.failed":"Ошибка генерации","card.generating":"Генерация изображения…","card.image":"Результат Image Studio","card.origPrompt":"Исходный промпт","card.remix":"Ремикс","card.removeBg":"Удалить фон","card.reroll":"Перегенерировать","card.responsiveTabs":"Адаптивные экраны","card.seedPrefix":"Seed","card.sizePrefix":"Size","card.toolCall":"Вызов генерации изображения","card.unknownError":"An unknown error occurred during generation.","card.upscale":"Увеличить","card.vary":"Вариация","diagnostics.desc":"Быстрая проверка статуса подключения и задержки выбранного API сервиса генерации.","diagnostics.ready":"В норме","diagnostics.test":"Проверить подключение","diagnostics.testing":"Проверка...","diagnostics.title":"Диагностика подключения провайдера","f.apiKeyEnv":"Справка по API-ключу","f.apiKeyEnvHint":"Ссылка на учетные данные / переменная окружения, содержащая ключ API FAL. Префикс авторизации \"Key \" добавляется автоматически.","f.autoEnhancePrompt":"Auto Enhance Prompt","f.autoEnhancePromptHint":"Enrich prompts with photography, lighting, and composition quality tokens.","f.baseURL":"Базовый URL очереди","f.baseURLHint":"Базовый URL очереди FAL (queue.fal.run).","f.cacheByPrompt":"Cache by Prompt Only","f.cacheByPromptHint":"Reuse existing result if the prompt has already been generated.","f.cacheBySeed":"Cache by Seed + Prompt","f.cacheBySeedHint":"Reuse existing result if the exact same seed and prompt are requested.","f.cacheTtlDays":"Cache Retention (Legacy)","f.cacheTtlDaysHint":"Legacy name for retention period in days.","f.customBaseURL":"Пользовательский API: базовый URL","f.customBaseURLHint":"Корень API без завершающего слеша. Запрос отправляется на {base}/images/generations.","f.customKeyEnv":"Пользовательский API: ссылка на ключ","f.customKeyEnvHint":"Ссылка на учётные данные / переменная окружения с API-ключом. Пустое значение означает отсутствие заголовка авторизации для шлюзов, которым он не требуется.","f.customModel":"Пользовательский API: модель","f.customModelHint":"Идентификатор модели, отправленный в этот API, например, gpt-image-1.","f.customSize":"Пользовательский API: фиксированный размер","f.customSizeHint":"Оставьте пустым, и именованный размер будет переведён автоматически (square_hd → 1024x1024). Указывайте размер только для API, требующих конкретных размеров.","f.dailyBudgetUsd":"Дневной бюджет ($ USD)","f.dailyBudgetUsdHint":"Дневной бюджет ($ USD)","f.defaultFormat":"Формат по умолчанию","f.defaultFormatHint":"Используется, если вызов инструмента не содержит output_format.","f.defaultSize":"Размер изображения по умолчанию","f.defaultSizeHint":"Используется, когда вызов инструмента пропускает параметр image_size.","f.defaultStylePreset":"Default Style Preset","f.defaultStylePresetHint":"Default style preset applied to generations (e.g. cinematic, anime, photorealistic).","f.deliverAs":"Как изображение попадает в чат","f.deliverAsHint":"ссылка — результат представляет собой текст со ссылкой, и карта отображает изображение из неё; работает с любой моделью чата. изображение — результат содержит само изображение, которое текстовые модели чата не могут прочитать, поэтому для этого требуется dsh-vision-bridge или модель с поддержкой зрения.","f.diskCache":"Дисковый кэш по хэшу","f.diskCacheHint":"Дисковый кэш по хэшу","f.enableLlmEnhancer":"Включить","f.enableLlmEnhancerHint":"Включить","f.enabled":"Включить","f.enabledHint":"Включить","f.enhanceBelowChars":"Улучшение промптов","f.enhanceBelowCharsHint":"Улучшение промптов","f.enhanceModel":"Улучшение промптов","f.enhanceModelHint":"Улучшение промптов","f.enhancePrompt":"Улучшение промптов","f.enhancePromptHint":"Улучшение промптов","f.geminiKeyEnv":"Gemini Key Reference","f.geminiKeyEnvHint":"Credential reference holding Google Gemini API key.","f.geminiModel":"Gemini Model ID","f.geminiModelHint":"Google Gemini image generation model identifier.","f.historyLimit":"History Limit","f.historyLimitHint":"Maximum number of recent generation records kept in memory.","f.inherit":"По умолчанию / Наследовать","f.localA1111Url":"Local A1111 URL (Legacy)","f.localA1111UrlHint":"Direct Automatic1111 endpoint URL.","f.localBaseURL":"Local Server URL","f.localBaseURLHint":"Server address (e.g. http://127.0.0.1:8188 for ComfyUI, http://127.0.0.1:7860 for A1111).","f.localCfg":"CFG Scale","f.localCfgHint":"Classifier-free guidance scale.","f.localComfyUrl":"Local ComfyUI URL (Legacy)","f.localComfyUrlHint":"Direct ComfyUI endpoint URL.","f.localKind":"Local Engine Architecture","f.localKindHint":"Select between ComfyUI API or Automatic1111 WebUI backend.","f.localModel":"Local Model / Checkpoint","f.localModelHint":"Checkpoint name or ComfyUI workflow name.","f.localSteps":"Sampling Steps","f.localStepsHint":"Number of denoising sampling steps.","f.loopGuardLimit":"Лимит защитного цикла (Loop Guard)","f.loopGuardLimitHint":"Лимит защитного цикла (Loop Guard)","f.model":"Модель (FAL)","f.modelHint":"Идентификатор модели FAL, вызывается как {baseURL}/{model}. Используется, когда провайдер — fal.","f.needCodex":"Built-in Codex subscription","f.needCustom":"Requires OpenAI-compatible endpoint","f.needFal":"Requires FAL API key","f.needGemini":"Requires Google Gemini API key","f.needGrok":"Built-in Grok subscription","f.needLocal":"Local ComfyUI or A1111","f.needReplicate":"Requires Replicate API token","f.needSeedream":"Requires SeaDream API key","f.outputDir":"Выходной каталог","f.outputDirHint":"Относительно рабочего каталога сессии; абсолютный путь используется как есть.","f.pollIntervalMs":"Интервал опроса (мс)","f.pollIntervalMsHint":"Интервал опроса статуса во время выполнения задачи.","f.provider":"Провайдер","f.providerHint":"fal — очередь FAL, настроенная ниже. custom — любой API, совместимый с OpenAI, настраивается далее.","f.pruneDays":"Срок хранения файлов (дней)","f.pruneDaysHint":"Срок хранения файлов (дней)","f.qualityGate":"Качество изображения","f.qualityGateHint":"Качество изображения","f.replicateKeyEnv":"Replicate Key Reference","f.replicateKeyEnvHint":"Credential reference holding the Replicate API token.","f.replicateModel":"Replicate Model ID","f.replicateModelHint":"Target model identifier on Replicate.","f.seedreamBaseURL":"SeaDream Base URL","f.seedreamBaseURLHint":"SeaDream API endpoint URL (ByteDance/Volcengine Ark).","f.seedreamKeyEnv":"SeaDream Key Reference","f.seedreamKeyEnvHint":"Credential reference holding the SeaDream API key.","f.seedreamModel":"SeaDream Model ID","f.seedreamModelHint":"ByteDance SeaDream model identifier.","f.stylePreset":"Суффикс художественного стиля","f.stylePresetHint":"Суффикс художественного стиля","f.subscriptionQuality":"Subscription Quality","f.subscriptionQualityHint":"Target quality profile when using Codex or Grok subscriptions.","f.timeoutMs":"Таймаут (мс)","f.timeoutMsHint":"Общее время ожидания генерации: отправка + опрос + загрузка.","gallery.copy_link":"Копировать Markdown-ссылку","gallery.copy_prompt":"Копировать промпт","gallery.empty":"Галерея пуста. Сгенерируйте первое изображение в чате!","gallery.inspector":"Детали записи генерации","gallery.loading":"Загрузка галереи…","gallery.title":"Галерея Image Studio","p.apiKeyEnv":"Пример: FAL_API_KEY","p.baseURL":"Пример: https://queue.fal.run","p.cacheTtlDays":"0","p.customBaseURL":"Пример: https://api.openai.com/v1","p.customKeyEnv":"Пример: OPENAI_API_KEY","p.customModel":"Пример: gpt-image-1","p.customSize":"Пример: 1024x1024","p.dailyBudgetUsd":"0.00","p.defaultStylePreset":"none","p.enhanceBelowChars":"200","p.enhanceModel":"Leave empty for default chat model","p.geminiKeyEnv":"GEMINI_API_KEY","p.geminiModel":"gemini-2.0-flash-exp-image-generation","p.historyLimit":"50","p.localA1111Url":"http://127.0.0.1:7860","p.localBaseURL":"http://127.0.0.1:8188","p.localCfg":"7","p.localComfyUrl":"http://127.0.0.1:8188","p.localModel":"v1-5-pruned-emaonly.safetensors","p.localSteps":"20","p.loopGuardLimit":"3","p.model":"Пример: fal-ai/flux-2/klein/9b","p.outputDir":"Пример: сгенерированные/изображения","p.pollIntervalMs":"Пример: 2000","p.pruneDays":"0","p.replicateKeyEnv":"REPLICATE_API_TOKEN","p.replicateModel":"black-forest-labs/flux-schnell","p.seedreamBaseURL":"https://api.bytedanceapi.com/v1","p.seedreamKeyEnv":"SEEDREAM_API_KEY","p.seedreamModel":"seedream-4.0","p.timeoutMs":"Пример: 180000","remix.copied":"✓ Copied!","remix.copy_instruction":"Copy Instruction","remix.creative_btn":"Creative (0.65)","remix.creativity_label":"Creativity / Denoising Strength","remix.drawer_title":"Variations & Style Remix Workbench","remix.prompt_placeholder":"Describe stylistic adjustments, new background, or variations...","remix.subtle_btn":"Subtle (0.25)","settings.collapse":"Свернуть","settings.credentialHint":"API-ключи находятся в разделе Учетные данные в указанном наборе ссылок (Настройки → Учетные данные).","settings.description":"Генерация изображений с помощью ИИ, дорисовка, вариации, апскейл и векторизация.","settings.discard":"Сбросить изменения","settings.expand":"Развернуть","settings.invalidNumber":"Пожалуйста, введите корректное число","settings.navLabel":"Генерация изображений","settings.overridden":"переопределено","settings.readOnly":"Это развертывание доступно только для чтения: настройки нельзя изменить через графический интерфейс.","settings.reset":"Сбросить по умолчанию","settings.save":"Сохранить","settings.saveFailed":"Не удалось сохранить настройки. Пожалуйста, проверьте учетные данные и попробуйте снова.","settings.saving":"Сохранение…","settings.title":"Студия изображений","settings.unavailable":"Настройки плагина инициализируются. Они появятся автоматически через несколько секунд.","settings.unsaved":"Несохраненные изменения","stat.active_provider":"Активный провайдер","stat.budget_cache":"Бюджет и кэш","stat.format_size":"Стандартные параметры","stat.safety_status":"Безопасность и Loop Guard","tab.cache":"Кэш и хранилище","tab.enhancer":"Промпт и стили","tab.gallery":"Галерея","tab.general":"Общие","tab.provider":"Провайдер","tab.safety":"Безопасность и бюджет","updater.check":"Check updates","updater.checking":"Checking…","updater.current":"Current version","updater.failed":"Update check failed: ","updater.latest":"Latest version","updater.success":"Plugin updated successfully! Please restart DSH service.","updater.title":"Plugin Updates","updater.upToDate":"Up to date","updater.updateNow":"Update to v{version}","updater.updating":"Updating…"}}
|
|
1
|
+
{"dsh-image-gen":{"card.actionRemoveBg":"Удалить фон","card.actionReroll":"Перегенерировать","card.actionUpscale":"Увеличить разрешение","card.copyPrompt":"Скопировать промпт","card.failed":"Ошибка генерации","card.generating":"Генерация изображения…","card.image":"Результат Image Studio","card.origPrompt":"Исходный промпт","card.remix":"Ремикс","card.removeBg":"Удалить фон","card.reroll":"Перегенерировать","card.responsiveTabs":"Адаптивные экраны","card.seedPrefix":"Seed","card.sizePrefix":"Size","card.toolCall":"Вызов генерации изображения","card.unknownError":"An unknown error occurred during generation.","card.upscale":"Увеличить","card.vary":"Вариация","diagnostics.desc":"Быстрая проверка статуса подключения и задержки выбранного API сервиса генерации.","diagnostics.ready":"В норме","diagnostics.test":"Проверить подключение","diagnostics.testing":"Проверка...","diagnostics.title":"Диагностика подключения провайдера","f.apiKeyEnv":"Ссылка на ключ FAL","f.apiKeyEnvHint":"Имя записи в хранилище учётных данных, содержащей API-ключ FAL.","f.autoEnhancePrompt":"Auto Enhance Prompt","f.autoEnhancePromptHint":"Enrich prompts with photography, lighting, and composition quality tokens.","f.baseURL":"Базовый URL FAL","f.baseURLHint":"Эндпоинт сервиса очередей FAL.","f.cacheByPrompt":"Cache by Prompt Only","f.cacheByPromptHint":"Reuse existing result if the prompt has already been generated.","f.cacheBySeed":"Cache by Seed + Prompt","f.cacheBySeedHint":"Reuse existing result if the exact same seed and prompt are requested.","f.cacheTtlDays":"Cache Retention (Legacy)","f.cacheTtlDaysHint":"Legacy name for retention period in days.","f.customBaseURL":"Пользовательский базовый URL API","f.customBaseURLHint":"Корень OpenAI-совместимого эндпоинта изображений (например https://api.openai.com/v1).","f.customKeyEnv":"Ссылка на пользовательский ключ","f.customKeyEnvHint":"Имя записи в хранилище учётных данных, содержащей API-ключ для пользовательского эндпоинта.","f.customModel":"Пользовательский ID модели","f.customModelHint":"Имя модели, отправляемое на пользовательский эндпоинт изображений.","f.customSize":"Пользовательский размер изображения","f.customSizeHint":"Фиксированный размер, отправляемый на пользовательский эндпоинт (например 1024x1024).","f.dailyBudgetUsd":"Дневной бюджет ($ USD)","f.dailyBudgetUsdHint":"Дневной бюджет ($ USD)","f.defaultFormat":"Формат вывода","f.defaultFormatHint":"Целевой формат файла (PNG, JPEG или WEBP).","f.defaultSize":"Соотношение сторон по умолчанию","f.defaultSizeHint":"Соотношение сторон и разрешение, применяемые если image_size не указан.","f.defaultStylePreset":"Default Style Preset","f.defaultStylePresetHint":"Default style preset applied to generations (e.g. cinematic, anime, photorealistic).","f.deliverAs":"Режим доставки","f.deliverAsHint":"Как изображения передаются в диалог: \"link\" (безопасно для текстовых LLM) или \"image\" (мультимодальное зрение).","f.diskCache":"Дисковый кэш по хэшу","f.diskCacheHint":"Дисковый кэш по хэшу","f.enableLlmEnhancer":"Включить","f.enableLlmEnhancerHint":"Включить","f.enabled":"Включить","f.enabledHint":"Включить","f.enhanceBelowChars":"Улучшение промптов","f.enhanceBelowCharsHint":"Улучшение промптов","f.enhanceModel":"Улучшение промптов","f.enhanceModelHint":"Улучшение промптов","f.enhancePrompt":"Улучшение промптов","f.enhancePromptHint":"Улучшение промптов","f.geminiKeyEnv":"Gemini Key Reference","f.geminiKeyEnvHint":"Credential reference holding Google Gemini API key.","f.geminiModel":"Gemini Model ID","f.geminiModelHint":"Google Gemini image generation model identifier.","f.historyLimit":"History Limit","f.historyLimitHint":"Maximum number of recent generation records kept in memory.","f.inherit":"По умолчанию / Наследовать","f.localA1111Url":"Local A1111 URL (Legacy)","f.localA1111UrlHint":"Direct Automatic1111 endpoint URL.","f.localBaseURL":"Local Server URL","f.localBaseURLHint":"Server address (e.g. http://127.0.0.1:8188 for ComfyUI, http://127.0.0.1:7860 for A1111).","f.localCfg":"CFG Scale","f.localCfgHint":"Classifier-free guidance scale.","f.localComfyUrl":"Local ComfyUI URL (Legacy)","f.localComfyUrlHint":"Direct ComfyUI endpoint URL.","f.localKind":"Local Engine Architecture","f.localKindHint":"Select between ComfyUI API or Automatic1111 WebUI backend.","f.localModel":"Local Model / Checkpoint","f.localModelHint":"Checkpoint name or ComfyUI workflow name.","f.localSteps":"Sampling Steps","f.localStepsHint":"Number of denoising sampling steps.","f.loopGuardLimit":"Лимит защитного цикла (Loop Guard)","f.loopGuardLimitHint":"Лимит защитного цикла (Loop Guard)","f.model":"ID модели FAL","f.modelHint":"Целевая модель на эндпоинте FAL (например fal-ai/flux-2/klein/9b).","f.needCodex":"Built-in Codex subscription","f.needCustom":"Requires OpenAI-compatible endpoint","f.needFal":"Requires FAL API key","f.needGemini":"Requires Google Gemini API key","f.needGrok":"Built-in Grok subscription","f.needLocal":"Local ComfyUI or A1111","f.needReplicate":"Requires Replicate API token","f.needSeedream":"Requires SeaDream API key","f.outputDir":"Каталог сохранения","f.outputDirHint":"Путь к каталогу на диске, куда сохраняются сгенерированные изображения.","f.pollIntervalMs":"Интервал опроса (мс)","f.pollIntervalMsHint":"Частота проверки статуса задачи в асинхронной очереди.","f.provider":"Провайдер изображений","f.providerHint":"Бэкенд-сервис для генерации изображений.","f.pruneDays":"Срок хранения файлов (дней)","f.pruneDaysHint":"Срок хранения файлов (дней)","f.qualityGate":"Качество изображения","f.qualityGateHint":"Качество изображения","f.replicateKeyEnv":"Replicate Key Reference","f.replicateKeyEnvHint":"Credential reference holding the Replicate API token.","f.replicateModel":"Replicate Model ID","f.replicateModelHint":"Target model identifier on Replicate.","f.seedreamBaseURL":"SeaDream Base URL","f.seedreamBaseURLHint":"SeaDream API endpoint URL (ByteDance/Volcengine Ark).","f.seedreamKeyEnv":"SeaDream Key Reference","f.seedreamKeyEnvHint":"Credential reference holding the SeaDream API key.","f.seedreamModel":"SeaDream Model ID","f.seedreamModelHint":"ByteDance SeaDream model identifier.","f.stylePreset":"Суффикс художественного стиля","f.stylePresetHint":"Суффикс художественного стиля","f.subscriptionQuality":"Subscription Quality","f.subscriptionQualityHint":"Target quality profile when using Codex or Grok subscriptions.","f.timeoutMs":"Таймаут (мс)","f.timeoutMsHint":"Максимальный общий таймаут до прерывания запроса генерации.","gallery.copy_link":"Копировать Markdown-ссылку","gallery.copy_prompt":"Копировать промпт","gallery.empty":"Галерея пуста. Сгенерируйте первое изображение в чате!","gallery.inspector":"Детали записи генерации","gallery.loading":"Загрузка галереи…","gallery.title":"Галерея Image Studio","p.apiKeyEnv":"FAL_API_KEY","p.baseURL":"https://queue.fal.run","p.cacheTtlDays":"0","p.customBaseURL":"https://api.openai.com/v1","p.customKeyEnv":"OPENAI_API_KEY","p.customModel":"dall-e-3","p.customSize":"1024x1024","p.dailyBudgetUsd":"0.00","p.defaultStylePreset":"none","p.enhanceBelowChars":"200","p.enhanceModel":"Leave empty for default chat model","p.geminiKeyEnv":"GEMINI_API_KEY","p.geminiModel":"gemini-2.0-flash-exp-image-generation","p.historyLimit":"50","p.localA1111Url":"http://127.0.0.1:7860","p.localBaseURL":"http://127.0.0.1:8188","p.localCfg":"7","p.localComfyUrl":"http://127.0.0.1:8188","p.localModel":"v1-5-pruned-emaonly.safetensors","p.localSteps":"20","p.loopGuardLimit":"3","p.model":"fal-ai/flux-2/klein/9b","p.outputDir":"generated/images","p.pollIntervalMs":"2000","p.pruneDays":"0","p.replicateKeyEnv":"REPLICATE_API_TOKEN","p.replicateModel":"black-forest-labs/flux-schnell","p.seedreamBaseURL":"https://api.bytedanceapi.com/v1","p.seedreamKeyEnv":"SEEDREAM_API_KEY","p.seedreamModel":"seedream-4.0","p.timeoutMs":"180000","remix.copied":"✓ Copied!","remix.copy_instruction":"Copy Instruction","remix.creative_btn":"Creative (0.65)","remix.creativity_label":"Creativity / Denoising Strength","remix.drawer_title":"Variations & Style Remix Workbench","remix.prompt_placeholder":"Describe stylistic adjustments, new background, or variations...","remix.subtle_btn":"Subtle (0.25)","settings.collapse":"Свернуть","settings.credentialHint":"Ключи API хранятся в учётных данных DSH (~/.dsh/.credentials.yaml) и не попадают в файлы конфигурации.","settings.description":"Генерация изображений с помощью ИИ, дорисовка, вариации, апскейл и векторизация.","settings.discard":"Сбросить изменения","settings.expand":"Развернуть","settings.invalidNumber":"Пожалуйста, введите корректное число","settings.navLabel":"Студия изображений","settings.overridden":"переопределено","settings.readOnly":"Настройки доступны только для чтения в этом контексте.","settings.reset":"Сбросить по умолчанию","settings.save":"Сохранить настройки","settings.saveFailed":"Не удалось сохранить настройки. Пожалуйста, проверьте учетные данные и попробуйте снова.","settings.saving":"Сохранение…","settings.title":"Студия изображений","settings.unavailable":"Настройки плагина инициализируются. Они появятся автоматически через несколько секунд.","settings.unsaved":"Несохраненные изменения","stat.active_provider":"Активный провайдер","stat.budget_cache":"Бюджет и кэш","stat.format_size":"Стандартные параметры","stat.safety_status":"Безопасность и Loop Guard","tab.cache":"Кэш и хранилище","tab.enhancer":"Промпт и стили","tab.gallery":"Галерея","tab.general":"Общие","tab.provider":"Провайдер","tab.safety":"Безопасность и бюджет","updater.check":"Check updates","updater.checking":"Checking…","updater.current":"Current version","updater.failed":"Update check failed: ","updater.latest":"Latest version","updater.success":"Plugin updated successfully! Please restart DSH service.","updater.title":"Plugin Updates","updater.upToDate":"Up to date","updater.updateNow":"Update to v{version}","updater.updating":"Updating…"}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"dsh-hypotheses":{"notificationHome":"Home мессенджера","notificationHomeHint":"Использует существующий Telegram home мессенджера; credentials остаются в gateway.","notificationHomePlaceholder":"Например: default","notifications":"Уведомления о завершении расследования","notificationsHint":"Отправлять один отчёт после явного завершения гипотезы.","save":"Сохранить","saved":"Сохранено","saving":"Сохранение…","settingsLoading":"Загрузка настроек уведомлений…","settingsUnavailable":"Настройки уведомлений недоступны."}}
|
|
1
|
+
{"@goodandready/dsh-hypotheses":{"active":"Активные","archived":"В архиве","clusters":"Кластеры","confidence":"Уверенность","confidenceHistory":"История уверенности","empty":"Гипотезы пока не зафиксированы.","evidence":"Факты","loading":"Загрузка аналитики…","noEvidence":"Факты не зафиксированы.","noHistory":"История пересчёта отсутствует.","notificationHome":"Home мессенджера","notificationHomeHint":"Использует существующий Telegram home мессенджера; credentials остаются в gateway.","notificationHomePlaceholder":"Например: default","notifications":"Уведомления о завершении расследования","notificationsHint":"Отправлять один отчёт после явного завершения гипотезы.","recentEvidence":"Последние факты","refresh":"Обновить","resolved":"Подтверждённые","save":"Сохранить","saved":"Сохранено","saving":"Сохранение…","settingsLoading":"Загрузка настроек уведомлений…","settingsUnavailable":"Настройки уведомлений недоступны.","stale":"Устаревшие","subtitle":"Анализ и проверка гипотез расследования (только чтение)","title":"Гипотезы","truncated":"Часть записей скрыта из-за ограничений отображения.","unavailable":"Аналитика в данный момент недоступна."},"dsh-hypotheses":{"active":"Активные","archived":"В архиве","clusters":"Кластеры","confidence":"Уверенность","confidenceHistory":"История уверенности","empty":"Гипотезы пока не зафиксированы.","evidence":"Факты","loading":"Загрузка аналитики…","noEvidence":"Факты не зафиксированы.","noHistory":"История пересчёта отсутствует.","notificationHome":"Home мессенджера","notificationHomeHint":"Использует существующий Telegram home мессенджера; credentials остаются в gateway.","notificationHomePlaceholder":"Например: default","notifications":"Уведомления о завершении расследования","notificationsHint":"Отправлять один отчёт после явного завершения гипотезы.","recentEvidence":"Последние факты","refresh":"Обновить","resolved":"Подтверждённые","save":"Сохранить","saved":"Сохранено","saving":"Сохранение…","settingsLoading":"Загрузка настроек уведомлений…","settingsUnavailable":"Настройки уведомлений недоступны.","stale":"Устаревшие","subtitle":"Анализ и проверка гипотез расследования (только чтение)","title":"Гипотезы","truncated":"Часть записей скрыта из-за ограничений отображения.","unavailable":"Аналитика в данный момент недоступна."}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"dsh-server-monitor":{"addServer":"Добавить сервер","address":"Адрес","architecture":"Архитектура","auth":"Способ аутентификации","available":"Доступно","cancel":"Отмена","command":"Команда","confirmDelete":"Удалить этот профиль сервера?","containers":"Контейнеры","cpu":"Процессор","deleteServer":"Удалить профиль","disks":"Диски","editServer":"Редактировать сервер","error":"Ошибка","filesystem":"Файловая система","host":"Хост","hostname":"Имя узла","image":"Образ","interface":"Сетевой интерфейс","keepSecret":"Оставьте поле пустым, чтобы сохранить текущее значение.","kernel":"Ядро","key":"Закрытый ключ","keyHint":"Ключ хранится только в хранилище этого плагина.","keyPath":"Путь к ключу","load":"Средняя нагрузка","loading":"Загрузка…","memory":"Память","mount":"Точка монтирования","name":"Название","network":"Сеть","noServers":"Серверы не настроены.","noSnapshot":"Снимок состояния недоступен.","os":"Операционная система","passphrase":"Парольная фраза ключа","password":"Пароль","port":"Порт","ports":"Прослушиваемые порты","processes":"Наиболее ресурсоёмкие процессы","protocol":"Протокол","resources":"Ресурсы","rx":"Получено (RX)","saved":"Сохранено","saving":"Сохранение…","select":"Выберите сервер","servers":"Серверы","state":"Состояние","subtitle":"Состояние Linux-серверов — только для просмотра","swap":"Область подкачки","system":"Система","testing":"Проверка подключения…","title":"Монитор серверов","tx":"Передано (TX)","unavailable":"Недоступно","uptime":"Время работы","used":"Использовано","username":"Имя пользователя"}}
|
|
1
|
+
{"dsh-server-monitor":{"active":"Активен","add":"Добавить сервер","addServer":"Добавить сервер","address":"Адрес","architecture":"Архитектура","auth":"Способ аутентификации","available":"Доступно","cancel":"Отмена","checkUpdateFailed":"Сбой проверки","command":"Команда","confirmDelete":"Удалить этот профиль сервера?","connected":"В сети","containers":"Контейнеры","copied":"Скопировано!","copyInstallCmd":"Скопировать команду настройки сервера","cpu":"Процессор","delete":"Удалить","deleteServer":"Удалить профиль","disks":"Диски","editServer":"Редактировать сервер","error":"Ошибка","filesystem":"Файловая система","generateKey":"Сгенерировать SSH-ключ","generating":"Генерация…","guideDescription":"Мониторинг статуса Linux-серверов (только чтение)","host":"Хост","hostname":"Имя узла","image":"Образ","interface":"Сетевой интерфейс","keepSecret":"Оставьте поле пустым, чтобы сохранить текущее значение.","kernel":"Ядро","key":"Закрытый ключ","keyHint":"Ключ хранится только в хранилище этого плагина.","keyPath":"Путь к ключу","keygenSection":"Настройка SSH-ключа","load":"Средняя нагрузка","loading":"Загрузка…","memory":"Память","mount":"Точка монтирования","name":"Название","network":"Сеть","noServers":"Серверы не настроены.","noSnapshot":"Снимок состояния недоступен.","offline":"Не в сети","os":"Операционная система","partial":"Частично","passphrase":"Парольная фраза ключа","password":"Пароль","port":"Порт","ports":"Прослушиваемые порты","processes":"Наиболее ресурсоёмкие процессы","protocol":"Протокол","refresh":"Обновить","resources":"Ресурсы","rx":"Получено (RX)","save":"Сохранить сервер","saved":"Сохранено","saving":"Сохранение…","select":"Выберите сервер","servers":"Серверы","shareWorkspaceHint":"Сохранено в ~/.dsh/keys/id_ed25519_dsh (совместимо с Remote Workspace)","state":"Состояние","subtitle":"Состояние Linux-серверов — только для просмотра","swap":"Область подкачки","system":"Система","test":"Проверить подключение","testAfterInstall":"Команда выполнена — проверить подключение","testing":"Проверка подключения…","title":"Монитор серверов","tx":"Передано (TX)","unavailable":"Недоступно","upToDate":"Актуальная версия","updateAvailable":"Доступно обновление: v{v}","updateChecking":"Проверка обновлений…","updateFailed":"Сбой обновления: {msg}","updateNow":"Обновить сейчас","updateSuccess":"Обновлено до v{v}. Перезапустите сервис DSH для применения изменений.","updating":"Обновление…","uptime":"Время работы","used":"Использовано","username":"Имя пользователя","version":"Версия"}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"agentrouter":{"apiKeyEnv":"Имя переменной окружения / ключа API","apiKeyHint":"Укажите ключ ar-… в учётных данных DSH или в переменной окружения процесса. Не вставляйте секрет в эту форму.","baseUrl":"Базовый URL","defaultModel":"Модель по умолчанию","enabled":"Включено","keyMissing":"ключ отсутствует","loading":"Загрузка настроек…","refresh":"Обновить","registerProvider":"Зарегистрировать в моделях","save":"Сохранить","saved":"Сохранено","smokeChat":"Тестовый чат","subtitle":"Маршрутизация агентов и интеграция моделей через локальный или удалённый прокси","title":"AgentRouter","unavailable":"Настройки недоступны (статус снимка: {status}). Пространство имён хоста отсутствует."},"dsh-agentrouter":{"apiKeyEnv":"Имя переменной окружения / ключа API","apiKeyHint":"Укажите ключ ar-… в учётных данных DSH или в переменной окружения процесса. Не вставляйте секрет в эту форму.","baseUrl":"Базовый URL","defaultModel":"Модель по умолчанию","enabled":"Включено","keyMissing":"ключ отсутствует","loading":"Загрузка настроек…","refresh":"Обновить","registerProvider":"Зарегистрировать в моделях","save":"Сохранить","saved":"Сохранено","smokeChat":"Тестовый чат","subtitle":"Маршрутизация агентов и интеграция моделей через локальный или удалённый прокси","title":"AgentRouter","unavailable":"Настройки недоступны (статус снимка: {status}). Пространство имён хоста отсутствует."}}
|
|
1
|
+
{"@goodandready-private/dsh-agentrouter":{"apiKeyEnv":"Имя переменной окружения / ключа API","apiKeyHint":"Укажите ключ ar-… в учётных данных DSH или в переменной окружения процесса. Не вставляйте секрет в эту форму.","baseUrl":"Базовый URL","defaultModel":"Модель по умолчанию","enabled":"Включено","keyMissing":"ключ отсутствует","loading":"Загрузка настроек…","refresh":"Обновить","registerProvider":"Зарегистрировать в моделях","save":"Сохранить","saved":"Сохранено","smokeChat":"Тестовый чат","subtitle":"Маршрутизация агентов и интеграция моделей через локальный или удалённый прокси","title":"AgentRouter","unavailable":"Настройки недоступны (статус снимка: {status}). Пространство имён хоста отсутствует."},"agentrouter":{"apiKeyEnv":"Имя переменной окружения / ключа API","apiKeyHint":"Укажите ключ ar-… в учётных данных DSH или в переменной окружения процесса. Не вставляйте секрет в эту форму.","baseUrl":"Базовый URL","defaultModel":"Модель по умолчанию","enabled":"Включено","keyMissing":"ключ отсутствует","loading":"Загрузка настроек…","refresh":"Обновить","registerProvider":"Зарегистрировать в моделях","save":"Сохранить","saved":"Сохранено","smokeChat":"Тестовый чат","subtitle":"Маршрутизация агентов и интеграция моделей через локальный или удалённый прокси","title":"AgentRouter","unavailable":"Настройки недоступны (статус снимка: {status}). Пространство имён хоста отсутствует."},"dsh-agentrouter":{"apiKeyEnv":"Имя переменной окружения / ключа API","apiKeyHint":"Укажите ключ ar-… в учётных данных DSH или в переменной окружения процесса. Не вставляйте секрет в эту форму.","baseUrl":"Базовый URL","defaultModel":"Модель по умолчанию","enabled":"Включено","keyMissing":"ключ отсутствует","loading":"Загрузка настроек…","refresh":"Обновить","registerProvider":"Зарегистрировать в моделях","save":"Сохранить","saved":"Сохранено","smokeChat":"Тестовый чат","subtitle":"Маршрутизация агентов и интеграция моделей через локальный или удалённый прокси","title":"AgentRouter","unavailable":"Настройки недоступны (статус снимка: {status}). Пространство имён хоста отсутствует."}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"dsh-github-ops":{"approvalMode":"Подтверждение записи","approvalModeHint":"\"auto\" разрешает согласованные неразрушающие операции записи без запроса; удаление релиза, тега, секрета, переменной, набора правил, защиты ветки или отмена запуска всегда запрашивают подтверждение. \"ask\" запрашивает каждое действие записи; \"off\" передаёт контроль хосту.","baseUrl":"Базовый URL API","baseUrlHint":"Измените для GitHub Enterprise; по умолчанию используется публичный API.","cardHint":"Токен берётся из учётных данных DSH; в настройках хранится только его имя.","defaultRepository":"Репозиторий по умолчанию","defaultRepositoryHint":"owner/repo, используемый если вызов инструмента опускает репозиторий. Оставьте пустым, чтобы всегда указывать явно.","invalidNumber":"Введите положительное число.","maxRetries":"Повторы при ошибке чтения","maxRetriesHint":"Повтор чтения (GET/HEAD) при сетевом сбое, таймауте или 5xx. Запись никогда не повторяется автоматически.","noChanges":"Нет изменений для сохранения.","reviewRules":"Переопределение правил ревью (JSON)","reviewRulesHint":"Необязательно. sensitivePaths, sensitiveSeverity, attentionPaths, migrationPaths, testsRequired, sourcePatterns, testPatterns, largeDiffLines. Пример: {\"largeDiffLines\": 400, \"sensitivePaths\": [\".env\", \"private/**\"]}","save":"Сохранить","saved":"Сохранено","savedPartial":"Сохранено, за исключением: ","saving":"Сохранение…","statusLoading":"Чтение настроек…","statusReadOnly":"Настройки доступны только для чтения в этой сессии.","statusUnavailable":"Сервис настроек недоступен в этой сборке: значения здесь редактировать нельзя.","sub":"Релизы, теги, защищённый шлюз API, публикация зеркал и настройки репозитория.","timeoutMs":"Таймаут запроса, мс","timeoutMsHint":"Предельное время выполнения одного запроса к GitHub до его прерывания.","title":"Операции с GitHub","tokenEnv":"Имя учётных данных","tokenEnvHint":"Имя записи в хранилище учётных данных DSH, содержащей токен GitHub. Сам токен здесь никогда не сохраняется.","tokenSource":"Источник доступа","tokenSourceHint":"\"auto\" проверяет учётные данные DSH, затем переменную окружения, затем сессию gh CLI. Зафиксируйте значение, чтобы использовать только один источник."}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"@goodandready/dsh-web-gateway":{"allowInternalUrls":"Разрешить внутренние URL для извлечения","allowInternalUrlsDesc":"Опасно: разрешает извлечение адресов, разрешающихся в loopback или приватные сети. Оставляйте выключенным без явной необходимости.","cacheTtlMs":"Время жизни кэша (мс)","cacheTtlMsDesc":"Срок хранения результатов в оперативной памяти. 0 отключает кэширование.","crawl4aiTokenEnv":"Имя учётных данных Crawl4AI","crawl4aiUrl":"Базовый URL Crawl4AI","crawl4aiUrlDesc":"Необязательный Crawl4AI для извлечения страниц. Пустое значение отключает его.","defaultLimit":"Лимит результатов по умолчанию","defaultLimitDesc":"Количество результатов по умолчанию для вызова web_gateway_search.","exaApiKeyEnv":"Имя учётных данных Exa","firecrawlApiKeyEnv":"Имя учётных данных Firecrawl","header_desc":"Резервный веб-поиск (Tavily → Firecrawl → Exa → SearXNG) и извлечение страниц при исчерпании лимитов встроенных web_search / web_fetch.","keysDesc":"Храните секретные значения в Настройки → Учётные данные под этими именами. Не вставляйте сами API-ключи сюда.","maxLimit":"Жёсткий лимит результатов","maxLimitDesc":"Максимальное количество результатов за один поисковый запрос.","save":"Сохранить","saved":"Настройки сохранены","saving":"Сохранение…","searxngUrl":"Базовый URL SearXNG","searxngUrlDesc":"Необязательный локальный SearXNG (например http://127.0.0.1:8080). Пустое значение отключает этот резерв.","settings.loading":"Загрузка настроек Web Gateway…","settings.retry":"Повторить","settings.unavailable":"Область настроек недоступна (пространство имён хоста не готово).","subtitle":"Отказоустойчивая цепочка поиска и извлечения веб-страниц","tavilyApiKeyEnv":"Имя учётных данных Tavily","timeoutMs":"Таймаут провайдера (мс)","timeoutMsDesc":"HTTP-таймаут на одного провайдера в миллисекундах.","title":"Веб-шлюз (Web Gateway)","updater_check":"Проверить обновления","updater_checking":"Проверка…","updater_current":"Текущая","updater_failed":"Сбой проверки обновлений","updater_latest":"Последняя","updater_success":"Обновлено. Перезапустите DSH для загрузки новой версии.","updater_title":"Версия плагина","updater_unavailable":"Автоматическое обновление недоступно для этого профиля.","updater_up_to_date":"Актуальная версия","updater_update_now":"Обновить до {version}","updater_updating":"Обновление…"},"dsh-web-gateway":{"allowInternalUrls":"Разрешить внутренние URL для извлечения","allowInternalUrlsDesc":"Опасно: разрешает извлечение адресов, разрешающихся в loopback или приватные сети. Оставляйте выключенным без явной необходимости.","cacheTtlMs":"Время жизни кэша (мс)","cacheTtlMsDesc":"Срок хранения результатов в оперативной памяти. 0 отключает кэширование.","crawl4aiTokenEnv":"Имя учётных данных Crawl4AI","crawl4aiUrl":"Базовый URL Crawl4AI","crawl4aiUrlDesc":"Необязательный Crawl4AI для извлечения страниц. Пустое значение отключает его.","defaultLimit":"Лимит результатов по умолчанию","defaultLimitDesc":"Количество результатов по умолчанию для вызова web_gateway_search.","exaApiKeyEnv":"Имя учётных данных Exa","firecrawlApiKeyEnv":"Имя учётных данных Firecrawl","header_desc":"Резервный веб-поиск (Tavily → Firecrawl → Exa → SearXNG) и извлечение страниц при исчерпании лимитов встроенных web_search / web_fetch.","keysDesc":"Храните секретные значения в Настройки → Учётные данные под этими именами. Не вставляйте сами API-ключи сюда.","maxLimit":"Жёсткий лимит результатов","maxLimitDesc":"Максимальное количество результатов за один поисковый запрос.","save":"Сохранить","saved":"Настройки сохранены","saving":"Сохранение…","searxngUrl":"Базовый URL SearXNG","searxngUrlDesc":"Необязательный локальный SearXNG (например http://127.0.0.1:8080). Пустое значение отключает этот резерв.","settings.loading":"Загрузка настроек Web Gateway…","settings.retry":"Повторить","settings.unavailable":"Область настроек недоступна (пространство имён хоста не готово).","subtitle":"Отказоустойчивая цепочка поиска и извлечения веб-страниц","tavilyApiKeyEnv":"Имя учётных данных Tavily","timeoutMs":"Таймаут провайдера (мс)","timeoutMsDesc":"HTTP-таймаут на одного провайдера в миллисекундах.","title":"Веб-шлюз (Web Gateway)","updater_check":"Проверить обновления","updater_checking":"Проверка…","updater_current":"Текущая","updater_failed":"Сбой проверки обновлений","updater_latest":"Последняя","updater_success":"Обновлено. Перезапустите DSH для загрузки новой версии.","updater_title":"Версия плагина","updater_unavailable":"Автоматическое обновление недоступно для этого профиля.","updater_up_to_date":"Актуальная версия","updater_update_now":"Обновить до {version}","updater_updating":"Обновление…"}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"@goodandready/dsh-approval-gate":{"approvalRequired":"Требуется подтверждение","askCredentialInArgv":"Раскрытые данные авторизации передаются как аргументы процесса; используйте безопасный хелпер или проверьте перед разрешением","askDynamicArguments":"Раскрытые аргументы чувствительной команды не могут быть проверены; проверьте перед разрешением","askDynamicCommand":"Раскрытое имя команды не может быть проверено; проверьте перед разрешением","askDynamicRedirect":"Раскрытая цель перенаправления не может быть проверена; проверьте перед разрешением","askInterpreterPayload":"Побочные эффекты интерпретатора не проанализированы полностью; проверьте перед разрешением","askScriptFile":"Содержимое файла скрипта недоступно для проверки; проверьте перед разрешением","askShellStdin":"Ввод shell не полностью доступен для проверки; проверьте перед разрешением","askUnknown":"Команда не может быть полностью проверена; проверьте перед разрешением","askUnknownFormat":"Формат вызова инструмента не может быть проверен; проверьте перед разрешением","askUnknownWriteTarget":"Целевой файл для записи не определён; проверьте перед разрешением","askUnparseable":"Синтаксис shell не может быть полностью проверен; проверьте перед разрешением","blockedPrefix":"Заблокировано правилом dsh-approval-gate","commandLabel":"Команда","detailDanglingEscape":"незавершённое экранирование","detailInvalidHeredoc":"недопустимый разделитель here-document","detailProcessSubstitution":"подстановка процесса требует подтверждения","detailUnclosedBacktick":"незакрытая подстановка в обратных кавычках","detailUnclosedCommandSubstitution":"незакрытая подстановка команды","detailUnclosedDoubleQuote":"незакрытая двойная кавычка","detailUnclosedParameterExpansion":"незакрытое раскрытие параметров","detailUnclosedProcessSubstitution":"незакрытая подстановка процесса","detailUnclosedSingleQuote":"незакрытая одинарная кавычка","detailUnsupported":"неподдерживаемый синтаксис shell","detailUnsupportedGrouping":"синтаксис группировки shell не поддерживается анализатором","detailUnterminatedHeredoc":"терминатор here-document не найден","detectedLabel":"Обнаружено","parseReasonLabel":"Причина разбора","ruleCurlPipe":"Передача загруженного содержимого в конвейер shell","ruleDestructiveSql":"Разрушающий SQL-запрос","ruleDeviceWrite":"Запись в устройство или защищённый файл","ruleEval":"Динамическое выполнение shell (eval)","ruleFindDelete":"find -delete","ruleGitClean":"Разрушающий git clean","ruleGitResetHard":"Разрушающий git reset --hard","ruleInterpreterPayload":"Небезопасный код интерпретатора","ruleMkfsDevice":"Форматирование файловой системы","ruleNestedShell":"Превышен лимит глубины вложенного shell","ruleProcessSignal":"Завершение процесса","ruleRecursiveRm":"Рекурсивное удаление файлов","ruleSecretWrite":"Запись в защищённый файл с секретами","ruleServiceControl":"Изменение состояния службы","ruleUnknown":"Сработало блокирующее правило безопасности"},"dsh-approval-gate":{"approvalRequired":"Требуется подтверждение","askCredentialInArgv":"Раскрытые данные авторизации передаются как аргументы процесса; используйте безопасный хелпер или проверьте перед разрешением","askDynamicArguments":"Раскрытые аргументы чувствительной команды не могут быть проверены; проверьте перед разрешением","askDynamicCommand":"Раскрытое имя команды не может быть проверено; проверьте перед разрешением","askDynamicRedirect":"Раскрытая цель перенаправления не может быть проверена; проверьте перед разрешением","askInterpreterPayload":"Побочные эффекты интерпретатора не проанализированы полностью; проверьте перед разрешением","askScriptFile":"Содержимое файла скрипта недоступно для проверки; проверьте перед разрешением","askShellStdin":"Ввод shell не полностью доступен для проверки; проверьте перед разрешением","askUnknown":"Команда не может быть полностью проверена; проверьте перед разрешением","askUnknownFormat":"Формат вызова инструмента не может быть проверен; проверьте перед разрешением","askUnknownWriteTarget":"Целевой файл для записи не определён; проверьте перед разрешением","askUnparseable":"Синтаксис shell не может быть полностью проверен; проверьте перед разрешением","blockedPrefix":"Заблокировано правилом dsh-approval-gate","commandLabel":"Команда","detailDanglingEscape":"незавершённое экранирование","detailInvalidHeredoc":"недопустимый разделитель here-document","detailProcessSubstitution":"подстановка процесса требует подтверждения","detailUnclosedBacktick":"незакрытая подстановка в обратных кавычках","detailUnclosedCommandSubstitution":"незакрытая подстановка команды","detailUnclosedDoubleQuote":"незакрытая двойная кавычка","detailUnclosedParameterExpansion":"незакрытое раскрытие параметров","detailUnclosedProcessSubstitution":"незакрытая подстановка процесса","detailUnclosedSingleQuote":"незакрытая одинарная кавычка","detailUnsupported":"неподдерживаемый синтаксис shell","detailUnsupportedGrouping":"синтаксис группировки shell не поддерживается анализатором","detailUnterminatedHeredoc":"терминатор here-document не найден","detectedLabel":"Обнаружено","parseReasonLabel":"Причина разбора","ruleCurlPipe":"Передача загруженного содержимого в конвейер shell","ruleDestructiveSql":"Разрушающий SQL-запрос","ruleDeviceWrite":"Запись в устройство или защищённый файл","ruleEval":"Динамическое выполнение shell (eval)","ruleFindDelete":"find -delete","ruleGitClean":"Разрушающий git clean","ruleGitResetHard":"Разрушающий git reset --hard","ruleInterpreterPayload":"Небезопасный код интерпретатора","ruleMkfsDevice":"Форматирование файловой системы","ruleNestedShell":"Превышен лимит глубины вложенного shell","ruleProcessSignal":"Завершение процесса","ruleRecursiveRm":"Рекурсивное удаление файлов","ruleSecretWrite":"Запись в защищённый файл с секретами","ruleServiceControl":"Изменение состояния службы","ruleUnknown":"Сработало блокирующее правило безопасности"}}
|
package/lib/locales.js
CHANGED
|
@@ -7,6 +7,29 @@ const CORE_FILE = join(__dirname, "locales", "core.json")
|
|
|
7
7
|
const PLUGINS_DIR = join(__dirname, "locales", "plugins")
|
|
8
8
|
const ZH_RU_FILE = join(__dirname, "locales", "zh-ru.json")
|
|
9
9
|
|
|
10
|
+
let _pluginCache = null
|
|
11
|
+
let _pluginIndex = null
|
|
12
|
+
|
|
13
|
+
function ensurePluginCache() {
|
|
14
|
+
if (_pluginCache && _pluginIndex) return
|
|
15
|
+
_pluginCache = {}
|
|
16
|
+
_pluginIndex = new Map()
|
|
17
|
+
if (existsSync(PLUGINS_DIR)) {
|
|
18
|
+
for (const file of readdirSync(PLUGINS_DIR).sort()) {
|
|
19
|
+
if (!file.endsWith(".json")) continue
|
|
20
|
+
try {
|
|
21
|
+
const data = JSON.parse(readFileSync(join(PLUGINS_DIR, file), "utf8"))
|
|
22
|
+
for (const [ns, entries] of Object.entries(data)) {
|
|
23
|
+
_pluginCache[ns] = Object.assign(_pluginCache[ns] || {}, entries)
|
|
24
|
+
_pluginIndex.set(ns.toLowerCase(), ns)
|
|
25
|
+
const baseName = ns.replace(/^@[^/]+\//, "")
|
|
26
|
+
_pluginIndex.set(baseName.toLowerCase(), ns)
|
|
27
|
+
}
|
|
28
|
+
} catch (_) { /* bestEffort */ void _; }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
10
33
|
export function getCoreDictionaries() {
|
|
11
34
|
if (existsSync(CORE_FILE)) {
|
|
12
35
|
try {
|
|
@@ -17,19 +40,34 @@ export function getCoreDictionaries() {
|
|
|
17
40
|
}
|
|
18
41
|
|
|
19
42
|
export function getPluginDictionaries() {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
43
|
+
ensurePluginCache()
|
|
44
|
+
return Object.assign(
|
|
45
|
+
{},
|
|
46
|
+
_pluginCache
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function getPluginDictionariesByNames(names) {
|
|
51
|
+
ensurePluginCache()
|
|
52
|
+
if (!names || (Array.isArray(names) && names.length === 0)) {
|
|
53
|
+
return Object.assign(
|
|
54
|
+
{},
|
|
55
|
+
_pluginCache
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
const nameList = Array.isArray(names) ? names : String(names).split(",")
|
|
59
|
+
const result = {}
|
|
60
|
+
for (const rawName of nameList) {
|
|
61
|
+
const trimmed = String(rawName).trim()
|
|
62
|
+
if (!trimmed) continue
|
|
63
|
+
const targetNs = _pluginIndex.get(trimmed.toLowerCase())
|
|
64
|
+
if (targetNs && _pluginCache[targetNs]) {
|
|
65
|
+
result[targetNs] = _pluginCache[targetNs]
|
|
66
|
+
} else if (_pluginCache[trimmed]) {
|
|
67
|
+
result[trimmed] = _pluginCache[trimmed]
|
|
30
68
|
}
|
|
31
69
|
}
|
|
32
|
-
return
|
|
70
|
+
return result
|
|
33
71
|
}
|
|
34
72
|
|
|
35
73
|
export function getAllDictionaries() {
|
|
@@ -41,7 +79,7 @@ export function getAllDictionaries() {
|
|
|
41
79
|
export function getZhRuMap() {
|
|
42
80
|
if (existsSync(ZH_RU_FILE)) {
|
|
43
81
|
try {
|
|
44
|
-
return JSON.parse(readFileSync(
|
|
82
|
+
return JSON.parse(readFileSync(ZG_RU_FILE, "utf8"))
|
|
45
83
|
} catch (_) { /* bestEffort */ void _; }
|
|
46
84
|
}
|
|
47
85
|
return {}
|
package/lib/pure.js
CHANGED
|
@@ -17,7 +17,50 @@
|
|
|
17
17
|
// ---------------------------------------------------------------- форматы
|
|
18
18
|
|
|
19
19
|
const numberFormat = new Intl.NumberFormat('ru-RU')
|
|
20
|
+
const compactNumberFormat = new Intl.NumberFormat('ru-RU', { notation: 'compact', compactDisplay: 'short' })
|
|
20
21
|
const relativeTimeFormat = new Intl.RelativeTimeFormat('ru-RU', { numeric: 'auto' })
|
|
22
|
+
|
|
23
|
+
const datePresetFormats = {
|
|
24
|
+
short: new Intl.DateTimeFormat('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric' }),
|
|
25
|
+
shortDateTime: new Intl.DateTimeFormat('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }),
|
|
26
|
+
medium: new Intl.DateTimeFormat('ru-RU', { day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' }),
|
|
27
|
+
long: new Intl.DateTimeFormat('ru-RU', { day: 'numeric', month: 'long', year: 'numeric' }),
|
|
28
|
+
time: new Intl.DateTimeFormat('ru-RU', { hour: '2-digit', minute: '2-digit', second: '2-digit' }),
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const formatDate = (val, style = 'short') => {
|
|
32
|
+
if (val === null || val === undefined || val === '') return ''
|
|
33
|
+
const d = val instanceof Date ? val : new Date(typeof val === 'number' && val < 1e12 ? val * 1000 : val)
|
|
34
|
+
if (isNaN(d.getTime())) return String(val)
|
|
35
|
+
if (typeof style === 'string' && datePresetFormats[style]) {
|
|
36
|
+
return datePresetFormats[style].format(d)
|
|
37
|
+
}
|
|
38
|
+
if (typeof style === 'object' && style !== null) {
|
|
39
|
+
try {
|
|
40
|
+
return new Intl.DateTimeFormat('ru-RU', style).format(d)
|
|
41
|
+
} catch (_) {
|
|
42
|
+
return datePresetFormats.short.format(d)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return datePresetFormats.short.format(d)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const formatCompactNumber = (val) => {
|
|
49
|
+
if (val === null || val === undefined || val === '') return ''
|
|
50
|
+
const n = typeof val === 'number' ? val : Number(val)
|
|
51
|
+
return isNaN(n) ? String(val) : compactNumberFormat.format(n)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const formatTokens = (count, compact = false) => {
|
|
55
|
+
if (count === null || count === undefined || count === '') return ''
|
|
56
|
+
const n = typeof count === 'number' ? count : Number(count)
|
|
57
|
+
if (isNaN(n)) return String(count)
|
|
58
|
+
const tokenWord = plural(n, ['токен', 'токена', 'токенов'])
|
|
59
|
+
if (compact && n >= 1000) {
|
|
60
|
+
return `${compactNumberFormat.format(n)} токенов`
|
|
61
|
+
}
|
|
62
|
+
return `${numberFormat.format(n)} ${tokenWord}`
|
|
63
|
+
}
|
|
21
64
|
const currencyFormats = new Map()
|
|
22
65
|
|
|
23
66
|
const getCurrencyFormat = (cur) => {
|
|
@@ -119,24 +162,64 @@ export const inflect = (phrase, cName) => {
|
|
|
119
162
|
|
|
120
163
|
// ------------------------------------------------- подстановка и плюрализация
|
|
121
164
|
|
|
165
|
+
const pluralRules = new Intl.PluralRules('ru-RU')
|
|
166
|
+
|
|
167
|
+
/** Русская форма числительного: one | few | many | other. */
|
|
168
|
+
export const pluralForm = (n) => pluralRules.select(n)
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Универсальная русская плюрализация 1, 2-4, 5+
|
|
172
|
+
* @param {number|string} count - Число элементов
|
|
173
|
+
* @param {string[]|string} forms - Массив [one, few, many] или строка первого варианта
|
|
174
|
+
* @param {boolean} [withCount=false] - Включать ли число в результат ("5 задач" vs "задач")
|
|
175
|
+
*/
|
|
176
|
+
export function plural(count, forms, withCount = false) {
|
|
177
|
+
const n = typeof count === 'number' ? count : (Number(count) || 0)
|
|
178
|
+
let one = '', few = '', many = ''
|
|
179
|
+
if (Array.isArray(forms)) {
|
|
180
|
+
one = forms[0] || ''
|
|
181
|
+
few = forms[1] || one
|
|
182
|
+
many = forms[2] || few || one
|
|
183
|
+
} else if (typeof forms === 'string') {
|
|
184
|
+
one = forms
|
|
185
|
+
few = arguments[2] || one
|
|
186
|
+
many = arguments[3] || few || one
|
|
187
|
+
withCount = arguments[4] || false
|
|
188
|
+
} else {
|
|
189
|
+
return ''
|
|
190
|
+
}
|
|
191
|
+
const form = pluralRules.select(n)
|
|
192
|
+
let word = many
|
|
193
|
+
if (form === 'one') word = one
|
|
194
|
+
else if (form === 'few') word = few
|
|
195
|
+
else if (form === 'many') word = many
|
|
196
|
+
|
|
197
|
+
return withCount ? `${formatNumber(n)} ${word}` : word
|
|
198
|
+
}
|
|
199
|
+
|
|
122
200
|
export const fill = (template, params) => {
|
|
123
201
|
if (!params || typeof params !== 'object') return String(template)
|
|
124
|
-
return String(template).replace(/\{(\w+)(?::(
|
|
202
|
+
return String(template).replace(/\{(\w+)(?::([^}]+))?\}/g, (match, name, spec) => {
|
|
125
203
|
if (!(name in params)) return match
|
|
126
204
|
const val = params[name]
|
|
205
|
+
if (!spec) return String(val)
|
|
127
206
|
if (spec === 'number') return formatNumber(val)
|
|
207
|
+
if (spec === 'compact') return formatCompactNumber(val)
|
|
208
|
+
if (spec === 'tokens') return formatTokens(val)
|
|
209
|
+
if (spec === 'tokens:compact') return formatTokens(val, true)
|
|
128
210
|
if (spec === 'reltime') return formatRelativeTime(val)
|
|
129
211
|
if (spec === 'currency') return formatCurrency(val, params.currency || 'RUB')
|
|
212
|
+
if (spec === 'date') return formatDate(val)
|
|
213
|
+
if (spec.startsWith('date:')) return formatDate(val, spec.slice(5))
|
|
214
|
+
if (spec.startsWith('plural:')) {
|
|
215
|
+
const parts = spec.slice(7).split(',')
|
|
216
|
+
return plural(val, parts)
|
|
217
|
+
}
|
|
130
218
|
if (INFLECT_CASES.has(spec)) return inflect(String(val), spec)
|
|
131
219
|
return String(val)
|
|
132
220
|
})
|
|
133
221
|
}
|
|
134
222
|
|
|
135
|
-
const pluralRules = new Intl.PluralRules('ru-RU')
|
|
136
|
-
|
|
137
|
-
/** Русская форма числительного: one | few | many | other. */
|
|
138
|
-
export const pluralForm = (n) => pluralRules.select(n)
|
|
139
|
-
|
|
140
223
|
// ------------------------------------------------------ поисковая морфология
|
|
141
224
|
|
|
142
225
|
export const stemRussian = (word) => {
|
|
@@ -197,7 +280,13 @@ export const ERROR_MAP = {
|
|
|
197
280
|
500: { title: 'Внутренняя ошибка сервера', message: 'На стороне сервера произошел сбой', hint: 'Попробуйте повторить запрос позже или проверьте серверные логи.' },
|
|
198
281
|
502: { title: 'Ошибочный шлюз (Bad Gateway)', message: 'Промежуточный прокси не получил корректный ответ', hint: 'Проверьте работу нижележащей службы или upstream-сервера.' },
|
|
199
282
|
503: { title: 'Служба временно недоступна', message: 'Сервер перегружен или находится на обслуживании', hint: 'Попробуйте повторить операцию через некоторое время.' },
|
|
200
|
-
504: { title: 'Шлюз не отвечает (Gateway Timeout)', message: 'Превышено время ожидания ответа от upstream-сервера', hint: 'Попробуйте повторить запрос позже.' }
|
|
283
|
+
504: { title: 'Шлюз не отвечает (Gateway Timeout)', message: 'Превышено время ожидания ответа от upstream-сервера', hint: 'Попробуйте повторить запрос позже.' },
|
|
284
|
+
FETCH_FAILED: { title: 'Сетевой запрос не удался', message: 'Не удалось связаться с сервером (Failed to fetch)', hint: 'Проверьте сетевое подключение и доступность адреса.' },
|
|
285
|
+
CORS_ERROR: { title: 'Ошибка CORS', message: 'Запрос заблокирован политикой безопасности браузера', hint: 'Проверьте заголовки разрешённых источников на сервере.' },
|
|
286
|
+
WS_CLOSED: { title: 'Разрыв WebSocket', message: 'Связь с сервером по WebSocket прервана', hint: 'Проверьте работу сервера и сетевой статус.' },
|
|
287
|
+
QUOTA_EXCEEDED: { title: 'Исчерпана квота API', message: 'Превышен лимит запросов или баланс провайдера', hint: 'Проверьте баланс API-ключа в кабинете провайдера.' },
|
|
288
|
+
CONTEXT_OVERFLOW: { title: 'Превышен контекст', message: 'Объём диалога превышает размер контекстного окна', hint: 'Очистите или сожмите историю сообщений сессии.' },
|
|
289
|
+
MODEL_NOT_FOUND: { title: 'Модель не найдена', message: 'Указанная модель отсутствует или недоступна', hint: 'Проверьте идентификатор модели в настройках.' }
|
|
201
290
|
}
|
|
202
291
|
|
|
203
292
|
const fromMap = (key, rawMsg) => ({
|
|
@@ -213,9 +302,25 @@ export const humanizeError = (err) => {
|
|
|
213
302
|
if (lookupKey && ERROR_MAP[lookupKey]) return fromMap(lookupKey, rawMsg)
|
|
214
303
|
if (/rate limit|too many requests/i.test(rawMsg)) return fromMap(429, rawMsg)
|
|
215
304
|
if (/unauthorized|invalid token|invalid api key/i.test(rawMsg)) return fromMap(401, rawMsg)
|
|
305
|
+
if (/failed to fetch|network\s*error|load\s*failed|net::err_/i.test(rawMsg)) return fromMap('FETCH_FAILED', rawMsg)
|
|
306
|
+
if (/cors|cross-origin/i.test(rawMsg)) return fromMap('CORS_ERROR', rawMsg)
|
|
307
|
+
if (/websocket.*(?:closed|failed)/i.test(rawMsg)) return fromMap('WS_CLOSED', rawMsg)
|
|
308
|
+
if (/insufficient_quota|quota\s*exceeded/i.test(rawMsg)) return fromMap('QUOTA_EXCEEDED', rawMsg)
|
|
309
|
+
if (/context.*(?:exceeded|overflow|length)/i.test(rawMsg)) return fromMap('CONTEXT_OVERFLOW', rawMsg)
|
|
310
|
+
if (/model.*not.*found|model.*does not exist/i.test(rawMsg)) return fromMap('MODEL_NOT_FOUND', rawMsg)
|
|
216
311
|
return { code: 'UNKNOWN', title: 'Ошибка операции', message: rawMsg, hint: 'Проверьте параметры операции и логи.', raw: rawMsg }
|
|
217
312
|
}
|
|
218
313
|
|
|
314
|
+
export const formatErrorToast = (err) => {
|
|
315
|
+
const h = humanizeError(err)
|
|
316
|
+
if (!h) return null
|
|
317
|
+
return {
|
|
318
|
+
type: 'error',
|
|
319
|
+
title: h.title,
|
|
320
|
+
description: h.message + (h.hint ? ' ' + h.hint : '')
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
219
324
|
// -------------------------------------------------------- отчёт об ошибке
|
|
220
325
|
|
|
221
326
|
/** Версию передаёт вызывающий: в бандл её подставляет build.py из package.json,
|
|
@@ -486,20 +591,38 @@ export const SYSTEM_PROMPT_PRESETS = {
|
|
|
486
591
|
technical_expert: {
|
|
487
592
|
id: 'technical_expert',
|
|
488
593
|
label: 'Технический эксперт',
|
|
489
|
-
desc: '
|
|
490
|
-
text: 'Отвечай пользователю на русском языке. Используй точную
|
|
594
|
+
desc: 'Точная терминология, чистый код и русские комментарии',
|
|
595
|
+
text: 'Отвечай пользователю на русском языке. Используй точную инженерную терминологию, пиши чистый код и русские комментарии.'
|
|
491
596
|
},
|
|
492
597
|
tech_writer: {
|
|
493
598
|
id: 'tech_writer',
|
|
494
599
|
label: 'Технический писатель',
|
|
495
|
-
desc: 'Структурированные тексты, Markdown
|
|
496
|
-
text: 'Отвечай пользователю на русском языке. Оформляй документацию
|
|
600
|
+
desc: 'Структурированные тексты, Markdown и выверенная типографика',
|
|
601
|
+
text: 'Отвечай пользователю на русском языке. Оформляй документацию в Markdown, строго соблюдай правила русской типографики («ёлочки», тире, «ё»).'
|
|
497
602
|
},
|
|
498
603
|
concise: {
|
|
499
604
|
id: 'concise',
|
|
500
605
|
label: 'Лаконичный режим',
|
|
501
|
-
desc: 'Краткие ёмкие ответы без
|
|
502
|
-
text: 'Отвечай пользователю на русском языке максимально кратко и ёмко. Без
|
|
606
|
+
desc: 'Краткие ёмкие ответы без вводных слов и воды',
|
|
607
|
+
text: 'Отвечай пользователю на русском языке максимально кратко и ёмко. Без вводных слов и воды, сразу код или решение.'
|
|
608
|
+
},
|
|
609
|
+
code_reviewer: {
|
|
610
|
+
id: 'code_reviewer',
|
|
611
|
+
label: 'Код-ревьюер',
|
|
612
|
+
desc: 'Поиск багов, безопасность и архитектурный аудит',
|
|
613
|
+
text: 'Анализируй код и отвечай на русском языке. Ищи уязвимости, граничные случаи и нарушения контрактов, предлагая исправления.'
|
|
614
|
+
},
|
|
615
|
+
architect: {
|
|
616
|
+
id: 'architect',
|
|
617
|
+
label: 'Системный архитектор',
|
|
618
|
+
desc: 'Контракты API/CLI, модульность и надёжность',
|
|
619
|
+
text: 'Отвечай на русском языке как архитектор. Проектируй модульные системы, чёткие контракты API/CLI и надёжные решения без лишней сложности.'
|
|
620
|
+
},
|
|
621
|
+
tutor: {
|
|
622
|
+
id: 'tutor',
|
|
623
|
+
label: 'Наставник (ментор)',
|
|
624
|
+
desc: 'Понятные объяснения сложных концепций и пошаговый разбор',
|
|
625
|
+
text: 'Отвечай на русском языке как наставник. Объясняй сложные темы простыми словами, наглядными аналогиями и пошаговыми примерами.'
|
|
503
626
|
}
|
|
504
627
|
}
|
|
505
628
|
|
|
@@ -548,3 +671,118 @@ export const exportSessionToMarkdown = (session, options = {}) => {
|
|
|
548
671
|
md += `*Сгенерировано с помощью [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) и @goodandready/dsh-russian-lang*\n`
|
|
549
672
|
return md
|
|
550
673
|
}
|
|
674
|
+
|
|
675
|
+
// -------------------------------------------------- инспектор переводов (#298)
|
|
676
|
+
|
|
677
|
+
export const findTranslationKey = (text, dicts = {}, overrides = {}, zhRu = {}) => {
|
|
678
|
+
if (!text || typeof text !== 'string') return null
|
|
679
|
+
const trimmed = text.trim()
|
|
680
|
+
if (!trimmed) return null
|
|
681
|
+
|
|
682
|
+
if (overrides && typeof overrides === 'object') {
|
|
683
|
+
for (const [k, v] of Object.entries(overrides)) {
|
|
684
|
+
if (v === text || (typeof v === 'string' && v.trim() === trimmed)) {
|
|
685
|
+
return { key: k, value: v, source: 'override' }
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
if (dicts && typeof dicts === 'object') {
|
|
691
|
+
for (const [ns, map] of Object.entries(dicts)) {
|
|
692
|
+
if (!map || typeof map !== 'object') continue
|
|
693
|
+
for (const [k, v] of Object.entries(map)) {
|
|
694
|
+
if (v === text || (typeof v === 'string' && v.trim() === trimmed)) {
|
|
695
|
+
return { ns, key: k, value: v, source: 'dictionary' }
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
if (zhRu && typeof zhRu === 'object') {
|
|
702
|
+
for (const [zh, ru] of Object.entries(zhRu)) {
|
|
703
|
+
if (ru === text || (typeof ru === 'string' && ru.trim() === trimmed)) {
|
|
704
|
+
return { zh, key: zh, value: ru, source: 'dom_zh' }
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
return null
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// ------------------------------------------ двуязычный поиск команд (#300)
|
|
713
|
+
|
|
714
|
+
export const bilingualCommandMatch = (query, command, options = {}) => {
|
|
715
|
+
if (!query || !command) return { matched: false, score: 0 }
|
|
716
|
+
const q = String(query).trim().toLowerCase()
|
|
717
|
+
if (!q) return { matched: false, score: 0 }
|
|
718
|
+
|
|
719
|
+
const cmdObj = typeof command === 'string' ? { title: command } : command
|
|
720
|
+
const candidates = [
|
|
721
|
+
cmdObj.title,
|
|
722
|
+
cmdObj.label,
|
|
723
|
+
cmdObj.name,
|
|
724
|
+
cmdObj.originalTitle,
|
|
725
|
+
cmdObj.enTitle,
|
|
726
|
+
cmdObj.id,
|
|
727
|
+
cmdObj.description,
|
|
728
|
+
cmdObj.category,
|
|
729
|
+
...(Array.isArray(cmdObj.keywords) ? cmdObj.keywords : [])
|
|
730
|
+
].filter((c) => typeof c === 'string' && c.trim())
|
|
731
|
+
|
|
732
|
+
if (!candidates.length) return { matched: false, score: 0 }
|
|
733
|
+
|
|
734
|
+
const queryVariants = [{ text: q, kind: 'direct' }]
|
|
735
|
+
|
|
736
|
+
const lat2cyr = translit(q, LAYOUT_LAT_TO_CYR)
|
|
737
|
+
if (lat2cyr !== q) queryVariants.push({ text: lat2cyr, kind: 'layout' })
|
|
738
|
+
|
|
739
|
+
const cyr2lat = translit(q, LAYOUT_CYR_TO_LAT)
|
|
740
|
+
if (cyr2lat !== q) queryVariants.push({ text: cyr2lat, kind: 'layout' })
|
|
741
|
+
|
|
742
|
+
const phonLat2Cyr = phoneticTranslit(q, 'lat2cyr')
|
|
743
|
+
if (phonLat2Cyr && phonLat2Cyr !== q && phonLat2Cyr !== lat2cyr) {
|
|
744
|
+
queryVariants.push({ text: phonLat2Cyr, kind: 'phonetic' })
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
const phonCyr2Lat = phoneticTranslit(q, 'cyr2lat')
|
|
748
|
+
if (phonCyr2Lat && phonCyr2Lat !== q && phonCyr2Lat !== cyr2lat) {
|
|
749
|
+
queryVariants.push({ text: phonCyr2Lat, kind: 'phonetic' })
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
let bestScore = 0
|
|
753
|
+
let bestVariant = null
|
|
754
|
+
let bestCandidate = null
|
|
755
|
+
|
|
756
|
+
for (const variant of queryVariants) {
|
|
757
|
+
for (const cand of candidates) {
|
|
758
|
+
const score = fuzzyMatchRu(variant.text, cand)
|
|
759
|
+
if (score > bestScore) {
|
|
760
|
+
bestScore = score
|
|
761
|
+
bestVariant = variant
|
|
762
|
+
bestCandidate = cand
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
const threshold = options.threshold ?? 50
|
|
768
|
+
return {
|
|
769
|
+
matched: bestScore >= threshold,
|
|
770
|
+
score: bestScore,
|
|
771
|
+
matchedText: bestCandidate,
|
|
772
|
+
variant: bestVariant ? bestVariant.text : q,
|
|
773
|
+
kind: bestVariant ? bestVariant.kind : 'none'
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
export const filterCommands = (query, commands = [], options = {}) => {
|
|
778
|
+
if (!Array.isArray(commands)) return []
|
|
779
|
+
if (!query || !String(query).trim()) return commands
|
|
780
|
+
const results = []
|
|
781
|
+
for (const cmd of commands) {
|
|
782
|
+
const res = bilingualCommandMatch(query, cmd, options)
|
|
783
|
+
if (res.matched) {
|
|
784
|
+
results.push({ item: cmd, ...res })
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
return results.sort((a, b) => b.score - a.score).map((r) => r.item)
|
|
788
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-russian-lang",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Russian localization for DeepSeek Harness (DSH v0.1.6-alpha.1+) web UI: adds Русский as native language option. 100.0% coverage across 11,295 keys (127 namespaces). Features full DSH v0.1.6-alpha.1 core support (terminal, archive, command palette, Auto review, trajectory, PTC/Creator presets) and complete GoodAndReady plugin catalog (subscriptions, tts, image-gen, voice, key-limits, notify, commandcode, plugin-console).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
"lib",
|
|
15
|
+
"bin",
|
|
15
16
|
"cordis.patch.yml",
|
|
16
17
|
"README.md",
|
|
17
18
|
"README.ru.md",
|
|
@@ -65,5 +66,8 @@
|
|
|
65
66
|
"homepage": "https://github.com/GooDAnDReaDY/dsh-russian-lang#readme",
|
|
66
67
|
"bugs": {
|
|
67
68
|
"url": "https://github.com/GooDAnDReaDY/dsh-russian-lang/issues"
|
|
69
|
+
},
|
|
70
|
+
"bin": {
|
|
71
|
+
"dsh-i18n": "./bin/dsh-i18n.mjs"
|
|
68
72
|
}
|
|
69
73
|
}
|