dsh-think-translate 1.2.9 → 1.2.10
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.de.md +5 -3
- package/README.es.md +5 -3
- package/README.fr.md +5 -3
- package/README.ja.md +5 -3
- package/README.ko.md +5 -3
- package/README.md +4 -2
- package/README.ru.md +5 -3
- package/README.zh-CN.md +5 -3
- package/lib/client.js +5 -5
- package/lib/discovery.js +162 -0
- package/lib/index.js +28 -163
- package/package.json +6 -2
package/README.de.md
CHANGED
|
@@ -33,7 +33,7 @@ Modelle der DeepSeek-Familie denken oft auf Chinesisch — oder in der Sprache,
|
|
|
33
33
|
- **🎚️ Einstellbarer Übersetzungszeitpunkt** — alles vorübersetzen / alte Ketten lazy laden (Standard) / nur beim Aufklappen
|
|
34
34
|
- **🔗 Dynamische Anbieter-Kette** — die Listenreihenfolge ist die Ausführungsreihenfolge: ziehen zum Sortieren, jede Zeile einzeln ein-/ausschalten. Integriert google gtx / bing / lokales Ollama plus beliebige eigene Endpunkte
|
|
35
35
|
- **🔌 Eigene Anbieter (OpenAI & Anthropic)** — im Panel jeden OpenAI-kompatiblen Endpunkt (`/v1/chat/completions`) oder die **Anthropic Messages API** (Claude) hinzufügen: Typ, Vorlage, Basis-URL, API-Schlüssel, Modell
|
|
36
|
-
- **🪄 DSH-konfigurierte Anbieter übernehmen** — liest schreibgeschützte DSH-Zeilen aus `settings.yaml` (`llm-pi-ai.providers`); eine Schaltfläche liest neu ein und fügt alle der Kette hinzu. Der Schlüssel wird pro Anfrage aus `.credentials.yaml` aufgelöst und nie in der Plugin-Konfiguration gespeichert. Auch die eigene Standardroute des Harness zählt: zeigt `agent-default-model` auf `deepseek-official`, erscheint die offizielle DeepSeek-API als weitere DSH-Zeile
|
|
36
|
+
- **🪄 DSH-konfigurierte Anbieter übernehmen** — liest schreibgeschützte DSH-Zeilen aus `profiles/<profile>/cordis.patch.yml` / älteres `settings.yaml` (`llm-pi-ai.providers`); eine Schaltfläche liest neu ein und fügt alle der Kette hinzu. Der Schlüssel wird pro Anfrage aus `.credentials.yaml` aufgelöst und nie in der Plugin-Konfiguration gespeichert. Auch die eigene Standardroute des Harness zählt: zeigt `default-model` / `agent-default-model` auf `deepseek-official`, erscheint die offizielle DeepSeek-API als weitere DSH-Zeile
|
|
37
37
|
- **⏱️ Robust** — fehlgeschlagene Übersetzungen werden 30 Sekunden im Arbeitsspeicher zwischengespeichert und danach automatisch erneut versucht; jede Anbieterzeile hat eine Test-Schaltfläche
|
|
38
38
|
|
|
39
39
|
## 📦 Installation
|
|
@@ -75,6 +75,8 @@ Für DSH **0.2.0-rc.2** verwenden Sie **dsh-think-translate 1.2.5 oder neuer**.
|
|
|
75
75
|
|
|
76
76
|
**1.2.8 Übertragung und Wiederaufnahme:** Normale HTTP/HTTPS-Übersetzungen nutzen die nativen Netzwerkfunktionen von Node statt eines curl-Prozesses pro Textpaket. SOCKS-Proxys und der Ollama-Installer verwenden weiterhin curl. Fehler bleiben 30 Sekunden im Arbeitsspeicher; danach werden Denk- und Antwortübersetzungen automatisch erneut versucht. Die Zeitlimits berücksichtigen das Laden lokaler Modelle und alternative Anbieter.
|
|
77
77
|
|
|
78
|
+
**1.2.10 DSH-Profilkompatibilität:** Die Erkennung folgt dem laufenden Desktop/Web-Profil und liest `profiles/<profile>/cordis.patch.yml` (`id` + `config`), danach die Home-Patches und Start-Overlays. Das ältere `settings.yaml` bleibt unterstützt. Die Standardmodell-Zeile heißt `default-model`; Zugangsdaten bleiben in `.credentials.yaml` im DSH-Home und nur im Arbeitsspeicher. Die Diagnose zeigt die tatsächlich gelesene Datei.
|
|
79
|
+
|
|
78
80
|
**1.2.9 Sprachkorrekturen:** Japanische Vokabelhinweise gelten nur für Japanisch; OpenAI/Anthropic erhalten den vollständigen Sprachnamen. Bing nutzt `auto-detect` und `zh-Hans`/`zh-Hant`. Die Erkennung chinesischer Prosa blendet Pfade, Code und JSON aus: vereinfachte Prosa wird übersprungen, häufige traditionelle Schriftzeichen lösen eine Übersetzungsanfrage aus, japanische/koreanische Prosa wird übersetzt. Alte Übersetzungen werden einmalig aus dem Cache entfernt; Einstellungen bleiben erhalten.
|
|
79
81
|
|
|
80
82
|
## 🚀 Verwendung
|
|
@@ -83,7 +85,7 @@ Für DSH **0.2.0-rc.2** verwenden Sie **dsh-think-translate 1.2.5 oder neuer**.
|
|
|
83
85
|
2. Die **Zielsprache** wählen (z. B. Deutsch) — Einstellungen, Denkzeilen und Karten wechseln in diese Sprache
|
|
84
86
|
3. Die **Anbieterkette** verwalten (ziehen zum Sortieren, ankreuzen zum Aktivieren):
|
|
85
87
|
- Integriert: **google gtx / bing** (kostenlos, über den Systemproxy) und **lokales Ollama**; vorhandene Software/Modelle werden wiederverwendet. Fehlende Komponenten werden erst über die Vorbereitungsschaltfläche installiert/heruntergeladen, mit Fortschritt (7b/14b oder eigenes Modell)
|
|
86
|
-
- **DSH-Anbieter**: in `settings.yaml` konfigurierte Endpunkte erscheinen automatisch (schreibgeschützt; ankreuzen fügt sie der Kette hinzu). Die Schaltfläche **Aus DSH-Konfiguration übernehmen** direkt unter der Liste liest die Konfiguration neu ein und fügt alle auf einmal hinzu – keine baseURL und kein Schlüssel zum Neutippen, denn der Schlüssel wird aus den DSH-Credentials aufgelöst
|
|
88
|
+
- **DSH-Anbieter**: in `profiles/<profile>/cordis.patch.yml` / älteres `settings.yaml` konfigurierte Endpunkte erscheinen automatisch (schreibgeschützt; ankreuzen fügt sie der Kette hinzu). Die Schaltfläche **Aus DSH-Konfiguration übernehmen** direkt unter der Liste liest die Konfiguration neu ein und fügt alle auf einmal hinzu – keine baseURL und kein Schlüssel zum Neutippen, denn der Schlüssel wird aus den DSH-Credentials aufgelöst
|
|
87
89
|
- Der Schlüssel wird eingetippt oder kommt als **`apiKeyEnv`** aus einer Vorlage bzw. einer DSH-Zeile: diese Zeile zeigt das `env:NAME`-Abzeichen, und der Schlüssel wird pro Anfrage aufgelöst und nie in `config.json` geschrieben. Das Bearbeitungsformular hat kein Feld für die Umgebungsvariable (der Wert bleibt erhalten), aber ein geleertes Feld wird wirklich gelöscht (als explizite Löschung gesendet)
|
|
88
90
|
- Abgewählte Anbieter werden übersprungen. Details stehen in [README.md](README.md)
|
|
89
91
|
4. Nachricht senden und die Think-Zeile aufklappen, um die Übersetzung zu sehen
|
|
@@ -100,7 +102,7 @@ Browser → POST /_xlate/translate (gleiche Origin, kein CORS)
|
|
|
100
102
|
```
|
|
101
103
|
|
|
102
104
|
- Die **Anbieter-Konfiguration** liegt in `config.json` (zur Laufzeit erzeugt, gitignoriert): `chain` (geordnete IDs), `fallback` (enabled + chain, nur Datei), `providers` (je Anbieter `type`/`enabled`/`baseURL`/`apiKey`/`apiKeyEnv`/`model`). Alte `priority`-Konfigurationen werden automatisch migriert; ein Anbieter mit `apiKeyEnv` löst seinen Schlüssel pro Anfrage aus dieser Umgebungsvariable auf (der literale `apiKey` bleibt als Rückfall) und kein aufgelöster Schlüssel wird je in `config.json` zurückgeschrieben; ein `null` in einem Patch löscht das Feld — so leert die Oberfläche eines
|
|
103
|
-
- Die **DSH-Erkennung** liest beim Laden `settings.yaml` (`llm-pi-ai.providers`) und `.credentials.yaml` (`refs`) des Harness; erkannte Anbieter tragen `source: "dsh"`, aufgelöste Schlüssel bleiben im Speicher (nie in `config.json`), und die Route `/_xlate/dsh-scan` liest sie bei Bedarf neu ein
|
|
105
|
+
- Die **DSH-Erkennung** liest beim Laden `profiles/<profile>/cordis.patch.yml` / älteres `settings.yaml` (`llm-pi-ai.providers`) und `.credentials.yaml` (`refs`) des Harness; erkannte Anbieter tragen `source: "dsh"`, aufgelöste Schlüssel bleiben im Speicher (nie in `config.json`), und die Route `/_xlate/dsh-scan` liest sie bei Bedarf neu ein
|
|
104
106
|
- **Host-Hälfte** (`lib/index.js`): Anbieteradapter, LRU-Cache (600), `/_xlate/models`, `/_xlate/model/pull` + `pull-status` (automatische Konfiguration am Ende)
|
|
105
107
|
- **Client-Hälfte** (`lib/client.js`): 8-sprachige UI, satzweise Batch-Übersetzung, Streaming-Think-Zeilen, localStorage-Persistenz
|
|
106
108
|
- Reine Anzeigeschicht: Originale bleiben im Verlauf und im Modellkontext erhalten
|
package/README.es.md
CHANGED
|
@@ -33,7 +33,7 @@ Los modelos de la familia DeepSeek suelen razonar en chino — o en el idioma en
|
|
|
33
33
|
- **🎚️ Momento de traducción ajustable** — pre-traducir todo / carga diferida de cadenas antiguas (por defecto) / solo al expandir
|
|
34
34
|
- **🔗 Cadena de proveedores dinámica** — el orden de la lista es el orden de uso: arrastra para reordenar y activa o desactiva cada fila. Integrados google gtx / bing / Ollama local más cualquier endpoint personalizado
|
|
35
35
|
- **🔌 Proveedores personalizados (OpenAI y Anthropic)** — añade desde el panel cualquier endpoint compatible con OpenAI (`/v1/chat/completions`) o la **Anthropic Messages API** (Claude): tipo, preajuste, URL base, clave de API y modelo
|
|
36
|
-
- **🪄 Hereda los proveedores configurados en DSH** — detecta filas DSH de solo lectura desde `settings.yaml` (`llm-pi-ai.providers`) y un botón reescanea y las añade todas a la cadena; la clave se resuelve en cada petición desde `.credentials.yaml` y nunca se guarda en la config del plugin. La ruta por defecto del propio harness también cuenta: si `agent-default-model` apunta a `deepseek-official`, la API oficial de DeepSeek aparece como una fila DSH más
|
|
36
|
+
- **🪄 Hereda los proveedores configurados en DSH** — detecta filas DSH de solo lectura desde `profiles/<profile>/cordis.patch.yml` / anterior `settings.yaml` (`llm-pi-ai.providers`) y un botón reescanea y las añade todas a la cadena; la clave se resuelve en cada petición desde `.credentials.yaml` y nunca se guarda en la config del plugin. La ruta por defecto del propio harness también cuenta: si `default-model` / `agent-default-model` apunta a `deepseek-official`, la API oficial de DeepSeek aparece como una fila DSH más
|
|
37
37
|
- **⏱️ Resistente** — los fallos se guardan en memoria durante 30 segundos para evitar reintentos continuos y después se reintentan automáticamente; cada proveedor tiene su botón de prueba
|
|
38
38
|
|
|
39
39
|
## 📦 Instalación
|
|
@@ -75,6 +75,8 @@ Para DSH **0.2.0-rc.2**, usa **dsh-think-translate 1.2.5 o posterior**. La versi
|
|
|
75
75
|
|
|
76
76
|
**1.2.8 transporte y recuperación:** las traducciones HTTP/HTTPS normales usan la red nativa de Node en lugar de iniciar curl por cada lote de texto. Los proxies SOCKS y el instalador de Ollama conservan la compatibilidad con curl. Los fallos permanecen en memoria durante 30 segundos y después se reintentan automáticamente las traducciones del razonamiento y las respuestas. Los tiempos de espera permiten cargar modelos locales y probar proveedores alternativos.
|
|
77
77
|
|
|
78
|
+
**1.2.10 compatibilidad con perfiles DSH:** el descubrimiento sigue el perfil Desktop/Web activo y lee `profiles/<profile>/cordis.patch.yml` (`id` + `config`), después los parches del directorio DSH y las superposiciones de inicio. Se mantiene el formato anterior `settings.yaml`. El modelo predeterminado usa `default-model`; las credenciales siguen en `.credentials.yaml` del directorio DSH y solo en memoria. El diagnóstico muestra el archivo leído.
|
|
79
|
+
|
|
78
80
|
**1.2.9 correcciones de idioma:** las pistas de vocabulario japonés solo se aplican al japonés; OpenAI/Anthropic reciben el nombre completo del idioma. Bing usa `auto-detect` y `zh-Hans`/`zh-Hant`. La detección del chino excluye rutas, código y JSON: omite la prosa ya simplificada, solicita conversión al detectar caracteres tradicionales frecuentes y traduce la prosa japonesa/coreana. Se elimina una vez la caché de traducciones anterior, conservando los ajustes.
|
|
79
81
|
|
|
80
82
|
## 🚀 Uso
|
|
@@ -83,7 +85,7 @@ Para DSH **0.2.0-rc.2**, usa **dsh-think-translate 1.2.5 o posterior**. La versi
|
|
|
83
85
|
2. Elige el **idioma de destino** (p. ej. Español) — el panel, las filas y las tarjetas cambian a ese idioma
|
|
84
86
|
3. Gestiona la **cadena de proveedores** (arrastra para ordenar, marca para activar):
|
|
85
87
|
- Integrados: **google gtx / bing** (gratis, mediante el proxy del sistema) y **Ollama local**; se reutilizan el software y los modelos existentes. Los componentes que falten requieren el botón de preparación, con progreso (7b/14b o un modelo personalizado)
|
|
86
|
-
- **Proveedores DSH**: los endpoints ya configurados en `settings.yaml` aparecen solos (solo lectura; márcalos para añadirlos a la cadena). El botón **Importar desde la config de DSH**, justo debajo de la lista, vuelve a escanear y los añade todos de una vez: ni baseURL ni clave que reescribir, porque la clave se resuelve desde las credenciales de DSH
|
|
88
|
+
- **Proveedores DSH**: los endpoints ya configurados en `profiles/<profile>/cordis.patch.yml` / anterior `settings.yaml` aparecen solos (solo lectura; márcalos para añadirlos a la cadena). El botón **Importar desde la config de DSH**, justo debajo de la lista, vuelve a escanear y los añade todos de una vez: ni baseURL ni clave que reescribir, porque la clave se resuelve desde las credenciales de DSH
|
|
87
89
|
- La clave se escribe a mano o llega como **`apiKeyEnv`** desde un preajuste o una fila DSH: esa fila muestra la insignia `env:NAME`, y la clave se resuelve en cada petición sin escribirse nunca en `config.json`. El formulario de edición no tiene campo de variable de entorno (el valor se conserva intacto), pero vaciar un campo lo elimina de verdad (se envía como borrado explícito)
|
|
88
90
|
- Si desmarcas uno, se omite. Los detalles están en [README.md](README.md)
|
|
89
91
|
4. Envía un mensaje y expande la fila Think para ver la traducción
|
|
@@ -100,7 +102,7 @@ navegador → POST /_xlate/translate (mismo origen, sin CORS)
|
|
|
100
102
|
```
|
|
101
103
|
|
|
102
104
|
- La **configuración de proveedores** vive en `config.json` (generado en tiempo de ejecución, ignorado por git): `chain` (ids ordenados), `fallback` (enabled + chain, solo en el archivo), `providers` (por proveedor `type`/`enabled`/`baseURL`/`apiKey`/`apiKeyEnv`/`model`). Las configuraciones antiguas con `priority` se migran solas; un proveedor que declara `apiKeyEnv` resuelve la clave desde esa variable en cada petición (el `apiKey` literal queda como respaldo) y ninguna clave resuelta se escribe de vuelta en `config.json`; un `null` en un parche borra ese campo, que es como la interfaz vacía uno
|
|
103
|
-
- El **descubrimiento DSH** lee `settings.yaml` (`llm-pi-ai.providers`) y `.credentials.yaml` (`refs`) del harness al cargar; los proveedores detectados se marcan con `source: "dsh"`, las claves resueltas se quedan en memoria (nunca en `config.json`) y la ruta `/_xlate/dsh-scan` los relee cuando hace falta
|
|
105
|
+
- El **descubrimiento DSH** lee `profiles/<profile>/cordis.patch.yml` / anterior `settings.yaml` (`llm-pi-ai.providers`) y `.credentials.yaml` (`refs`) del harness al cargar; los proveedores detectados se marcan con `source: "dsh"`, las claves resueltas se quedan en memoria (nunca en `config.json`) y la ruta `/_xlate/dsh-scan` los relee cuando hace falta
|
|
104
106
|
- **Mitad host** (`lib/index.js`): adaptadores de proveedor, caché LRU (600), `/_xlate/models`, `/_xlate/model/pull` + `pull-status` (configura automáticamente al terminar)
|
|
105
107
|
- **Mitad cliente** (`lib/client.js`): UI en 8 idiomas, traducción por lotes, filas Think en streaming, persistencia en localStorage
|
|
106
108
|
- Capa de visualización pura: los originales permanecen en la transcripción y el contexto del modelo
|
package/README.fr.md
CHANGED
|
@@ -33,7 +33,7 @@ Les modèles de la famille DeepSeek raisonnent souvent en chinois — ou dans la
|
|
|
33
33
|
- **🎚️ Moment de traduction réglable** — tout pré-traduire / chargement différé des anciennes chaînes (défaut) / uniquement à l'ouverture
|
|
34
34
|
- **🔗 Chaîne de fournisseurs dynamique** — l'ordre de la liste est l'ordre d'exécution : glissez pour réordonner, activez ou désactivez chaque ligne. Intégrés google gtx / bing / Ollama local, plus tout endpoint personnalisé
|
|
35
35
|
- **🔌 Fournisseurs personnalisés (OpenAI et Anthropic)** — ajoutez depuis le panneau tout endpoint compatible OpenAI (`/v1/chat/completions`) ou l'**Anthropic Messages API** (Claude) : type, préréglage, URL de base, clé API et modèle
|
|
36
|
-
- **🪄 Hérite des fournisseurs configurés dans DSH** — détecte les lignes DSH en lecture seule depuis `settings.yaml` (`llm-pi-ai.providers`), et un bouton relance l'analyse et les ajoute toutes à la chaîne ; la clé est résolue à chaque requête depuis `.credentials.yaml` et n'est jamais écrite dans la config du plugin. La route par défaut du harness compte aussi : si `agent-default-model` désigne `deepseek-official`, l'API officielle DeepSeek apparaît comme une ligne DSH de plus
|
|
36
|
+
- **🪄 Hérite des fournisseurs configurés dans DSH** — détecte les lignes DSH en lecture seule depuis `profiles/<profile>/cordis.patch.yml` / ancien `settings.yaml` (`llm-pi-ai.providers`), et un bouton relance l'analyse et les ajoute toutes à la chaîne ; la clé est résolue à chaque requête depuis `.credentials.yaml` et n'est jamais écrite dans la config du plugin. La route par défaut du harness compte aussi : si `default-model` / `agent-default-model` désigne `deepseek-official`, l'API officielle DeepSeek apparaît comme une ligne DSH de plus
|
|
37
37
|
- **⏱️ Résilient** — les échecs sont conservés en mémoire pendant 30 secondes pour éviter les tentatives répétées, puis réessayés automatiquement ; chaque fournisseur dispose de son bouton de test
|
|
38
38
|
|
|
39
39
|
## 📦 Installation
|
|
@@ -75,6 +75,8 @@ Pour DSH **0.2.0-rc.2**, utilisez **dsh-think-translate 1.2.5 ou une version ult
|
|
|
75
75
|
|
|
76
76
|
**1.2.8 transport et reprise :** les traductions HTTP/HTTPS ordinaires utilisent le réseau natif de Node au lieu de lancer curl pour chaque lot de texte. Les proxys SOCKS et l’installateur Ollama conservent curl. Les échecs restent en mémoire pendant 30 secondes, puis les traductions du raisonnement et des réponses sont réessayées automatiquement. Les délais tiennent compte du chargement des modèles locaux et du recours à d’autres fournisseurs.
|
|
77
77
|
|
|
78
|
+
**1.2.10 compatibilité des profils DSH :** la détection suit le profil Desktop/Web actif et lit `profiles/<profile>/cordis.patch.yml` (`id` + `config`), puis applique les correctifs du répertoire DSH et les surcharges de lancement. L’ancien `settings.yaml` reste pris en charge. Le modèle par défaut utilise `default-model` ; les identifiants restent dans `.credentials.yaml` du répertoire DSH et en mémoire uniquement. Le diagnostic indique le fichier lu.
|
|
79
|
+
|
|
78
80
|
**1.2.9 corrections linguistiques :** les indications de vocabulaire japonais ne concernent que le japonais ; OpenAI/Anthropic reçoivent le nom complet de la langue. Bing utilise `auto-detect` et `zh-Hans`/`zh-Hant`. La détection du chinois écarte chemins, code et JSON : elle ignore la prose déjà simplifiée, demande une conversion en présence de caractères traditionnels courants et traduit la prose japonaise/coréenne. Les anciennes traductions en cache sont supprimées une fois, sans toucher aux réglages.
|
|
79
81
|
|
|
80
82
|
## 🚀 Utilisation
|
|
@@ -83,7 +85,7 @@ Pour DSH **0.2.0-rc.2**, utilisez **dsh-think-translate 1.2.5 ou une version ult
|
|
|
83
85
|
2. Choisissez la **langue cible** (p. ex. Français) — le panneau, les lignes et les cartes basculent dans cette langue
|
|
84
86
|
3. Gérez la **chaîne de fournisseurs** (glisser pour ordonner, cocher pour activer) :
|
|
85
87
|
- Intégrés : **google gtx / bing** (gratuits, via le proxy système) et **Ollama local** ; les logiciels et modèles existants sont réutilisés. Les composants manquants nécessitent le bouton de préparation, avec progression (7b/14b ou modèle personnalisé)
|
|
86
|
-
- **Fournisseurs DSH** : les endpoints déjà configurés dans `settings.yaml` apparaissent seuls (lecture seule ; cochez pour les ajouter à la chaîne). Le bouton **Importer depuis la config DSH**, juste sous la liste, relance l'analyse et les ajoute tous d'un coup : ni baseURL ni clé à ressaisir, la clé étant résolue depuis les identifiants de DSH
|
|
88
|
+
- **Fournisseurs DSH** : les endpoints déjà configurés dans `profiles/<profile>/cordis.patch.yml` / ancien `settings.yaml` apparaissent seuls (lecture seule ; cochez pour les ajouter à la chaîne). Le bouton **Importer depuis la config DSH**, juste sous la liste, relance l'analyse et les ajoute tous d'un coup : ni baseURL ni clé à ressaisir, la clé étant résolue depuis les identifiants de DSH
|
|
87
89
|
- La clé se saisit à la main ou arrive via **`apiKeyEnv`** depuis un préréglage ou une ligne DSH : cette ligne affiche le badge `env:NAME`, et la clé est résolue à chaque requête sans jamais être écrite dans `config.json`. Le formulaire d'édition n'a pas de champ de variable d'environnement (la valeur est conservée telle quelle), mais vider un champ le supprime réellement (envoyé comme suppression explicite)
|
|
88
90
|
- Décocher un fournisseur l'ignore. Détails dans [README.md](README.md)
|
|
89
91
|
4. Envoyez un message et dépliez la ligne Think pour voir la traduction
|
|
@@ -100,7 +102,7 @@ navigateur → POST /_xlate/translate (même origine, sans CORS)
|
|
|
100
102
|
```
|
|
101
103
|
|
|
102
104
|
- La **configuration des fournisseurs** vit dans `config.json` (généré à l'exécution, ignoré par git) : `chain` (ids ordonnés), `fallback` (enabled + chain, fichier uniquement), `providers` (par fournisseur `type`/`enabled`/`baseURL`/`apiKey`/`apiKeyEnv`/`model`). Les anciennes configurations `priority` sont migrées automatiquement ; un fournisseur qui déclare `apiKeyEnv` résout sa clé depuis cette variable à chaque requête (le `apiKey` littéral reste en secours) et aucune clé résolue n'est réécrite dans `config.json` ; un `null` dans un patch supprime ce champ, c'est ainsi que l'interface en vide un
|
|
103
|
-
- La **découverte DSH** lit `settings.yaml` (`llm-pi-ai.providers`) et `.credentials.yaml` (`refs`) du harness au chargement ; les fournisseurs détectés portent `source: "dsh"`, les clés résolues restent en mémoire (jamais dans `config.json`), et la route `/_xlate/dsh-scan` les relit à la demande
|
|
105
|
+
- La **découverte DSH** lit `profiles/<profile>/cordis.patch.yml` / ancien `settings.yaml` (`llm-pi-ai.providers`) et `.credentials.yaml` (`refs`) du harness au chargement ; les fournisseurs détectés portent `source: "dsh"`, les clés résolues restent en mémoire (jamais dans `config.json`), et la route `/_xlate/dsh-scan` les relit à la demande
|
|
104
106
|
- **Moitié host** (`lib/index.js`) : adaptateurs de fournisseurs, cache LRU (600), `/_xlate/models`, `/_xlate/model/pull` + `pull-status` (configuration automatique à la fin)
|
|
105
107
|
- **Moitié client** (`lib/client.js`) : UI en 8 langues, traduction par lots, lignes Think en streaming, persistance localStorage
|
|
106
108
|
- Couche d'affichage pure : les originaux restent dans la transcription et le contexte du modèle
|
package/README.ja.md
CHANGED
|
@@ -33,7 +33,7 @@ DeepSeek 系モデルは中国語で考えることが多く、あるいはた
|
|
|
33
33
|
- **🎚️ 翻訳タイミングを調整可能** — すべて事前翻訳 / 履歴を遅延ロード(既定)/ 展開時のみ翻訳
|
|
34
34
|
- **🔗 動的プロバイダーチェーン** — 一覧の順序がそのまま実行順。ドラッグで並べ替え、行ごとにオン/オフ。組み込みの google gtx / bing / ローカル Ollama に加えて任意のカスタム端点
|
|
35
35
|
- **🔌 カスタムプロバイダー(OpenAI / Anthropic)** — 設定パネルから任意の OpenAI 互換端点(`/v1/chat/completions`)や **Anthropic Messages API**(Claude)を追加:種類・プリセット・ベース URL・API キー・モデル
|
|
36
|
-
- **🪄 DSH 設定のプロバイダーを引き継ぐ** — `settings.yaml`(`llm-pi-ai.providers`)から読み取り専用の DSH 行を自動検出し、ボタン1つで再スキャンしてまとめてチェーンに追加。キーは `.credentials.yaml` からリクエスト時に解決され、プラグインの設定には保存されません。harness 自身の既定ルートも対象です:`agent-default-model` が `deepseek-official` を指していれば、DeepSeek の公式 API も DSH 行として提供されます
|
|
36
|
+
- **🪄 DSH 設定のプロバイダーを引き継ぐ** — `profiles/<profile>/cordis.patch.yml` / 旧形式 `settings.yaml`(`llm-pi-ai.providers`)から読み取り専用の DSH 行を自動検出し、ボタン1つで再スキャンしてまとめてチェーンに追加。キーは `.credentials.yaml` からリクエスト時に解決され、プラグインの設定には保存されません。harness 自身の既定ルートも対象です:`default-model` / `agent-default-model` が `deepseek-official` を指していれば、DeepSeek の公式 API も DSH 行として提供されます
|
|
37
37
|
- **⏱️ 失敗からの復帰** — 失敗結果はメモリ内で30秒間キャッシュして連続再試行を抑え、期限後に自動再試行。各プロバイダーにテストボタンを用意
|
|
38
38
|
|
|
39
39
|
## 📦 インストール
|
|
@@ -75,6 +75,8 @@ DSH **0.2.0-rc.2** では **dsh-think-translate 1.2.5 以降**を使用してく
|
|
|
75
75
|
|
|
76
76
|
**1.2.8 通信と復帰:**通常の HTTP/HTTPS 翻訳は、テキストのバッチごとに curl を起動せず Node の標準ネットワーク機能を使います。SOCKS プロキシと Ollama インストーラーは引き続き curl に対応します。失敗結果をメモリ内に30秒間保持し、その後は思考・回答の翻訳を自動再試行します。タイムアウトはローカルモデルのロードと代替プロバイダーへの切り替えを考慮しています。
|
|
77
77
|
|
|
78
|
+
**1.2.10 DSH プロファイル対応:**実行中の Desktop/Web プロファイルに従い、`profiles/<profile>/cordis.patch.yml`(`id` + `config`)、DSH home のパッチ、起動時のオーバーレイを順に読み込みます。旧形式の `settings.yaml` も対応。既定モデル行は `default-model` に対応し、資格情報は DSH home の `.credentials.yaml` から解決してメモリ内だけに保持します。再スキャン時に実際の設定パスを表示します。
|
|
79
|
+
|
|
78
80
|
**1.2.9 言語修正:**日本語の語彙ヒントは日本語の翻訳先に限定し、OpenAI/Anthropic に完全な言語名を渡します。Bing は `auto-detect` と `zh-Hans`/`zh-Hant` を使用。中国語の判定ではパス・コード・JSON を除外し、簡体字の本文はスキップ、よく使われる繁体字を検出すると変換を依頼、日本語・韓国語の本文は翻訳します。旧訳文キャッシュを一度削除し、設定は保持します。
|
|
79
81
|
|
|
80
82
|
## 🚀 使い方
|
|
@@ -83,7 +85,7 @@ DSH **0.2.0-rc.2** では **dsh-think-translate 1.2.5 以降**を使用してく
|
|
|
83
85
|
2. **対象言語**を選択(例:日本語)— 設定パネル・思考行・タスクカードがすべてその言語に切替
|
|
84
86
|
3. **プロバイダーチェーン**を管理(ドラッグで並べ替え、チェックで有効化):
|
|
85
87
|
- 組み込み:**google gtx / bing**(無料、システムプロキシ経由)と**ローカル Ollama**。既存のソフト・モデルを再利用し、不足分は準備ボタンを押してからインストール・ダウンロード、進捗を表示(7b/14b または任意のモデル)
|
|
86
|
-
- **DSH プロバイダー**:`settings.yaml` に設定済みのエンドポイントが自動表示(読み取り専用、チェックでチェーンに追加)。リスト下の **DSH の設定から取り込む** は再スキャンしてまとめて追加します(baseURL もキーも入力不要。キーは DSH 側の資格情報から解決されます)
|
|
88
|
+
- **DSH プロバイダー**:`profiles/<profile>/cordis.patch.yml` / 旧形式 `settings.yaml` に設定済みのエンドポイントが自動表示(読み取り専用、チェックでチェーンに追加)。リスト下の **DSH の設定から取り込む** は再スキャンしてまとめて追加します(baseURL もキーも入力不要。キーは DSH 側の資格情報から解決されます)
|
|
87
89
|
- キーは直接入力するほか、プリセットや DSH 行が持つ **`apiKeyEnv`** でも指定できます。その行には `env:NAME` バッジが付き、キーはリクエスト時に解決されて `config.json` には保存されません。編集フォームに環境変数の入力欄はありません(値はそのまま保持されます)が、フィールドを空にすると本当に削除されます(明示的な削除として送信)
|
|
88
90
|
- チェックを外すとそのプロバイダーは使われません。詳細は [README.md](README.md)
|
|
89
91
|
4. メッセージを送信し、Think 行を展開して訳文を確認
|
|
@@ -100,7 +102,7 @@ DSH **0.2.0-rc.2** では **dsh-think-translate 1.2.5 以降**を使用してく
|
|
|
100
102
|
```
|
|
101
103
|
|
|
102
104
|
- **プロバイダー設定** は `config.json`(実行時生成・gitignore 対象)にあります:`chain`(順序付き ID)、`fallback`(enabled と chain、設定ファイル専用)、`providers`(各項目の `type`/`enabled`/`baseURL`/`apiKey`/`apiKeyEnv`/`model`)。旧 `priority` 形式は自動移行されます。`apiKeyEnv` を宣言した提供元はリクエスト時にその環境変数からキーを解決し(リテラル `apiKey` はフォールバック)、解決したキーを `config.json` に書き戻すことはありません。パッチ内の `null` はそのフィールドの削除を意味し、UI のクリアはこれを使っています
|
|
103
|
-
- **DSH の自動検出** は読み込み時に harness の `settings.yaml`(`llm-pi-ai.providers`)と `.credentials.yaml`(`refs`)を読みます。検出された提供元は `source: "dsh"` が付き、解決済みのキーはメモリ内のみ(`config.json` には書き込まれません)。`/_xlate/dsh-scan` でいつでも再読み込みできます
|
|
105
|
+
- **DSH の自動検出** は読み込み時に harness の `profiles/<profile>/cordis.patch.yml` / 旧形式 `settings.yaml`(`llm-pi-ai.providers`)と `.credentials.yaml`(`refs`)を読みます。検出された提供元は `source: "dsh"` が付き、解決済みのキーはメモリ内のみ(`config.json` には書き込まれません)。`/_xlate/dsh-scan` でいつでも再読み込みできます
|
|
104
106
|
- **host 側**(`lib/index.js`):プロバイダーアダプタ、LRU キャッシュ(600)、`/_xlate/models` モデル一覧、`/_xlate/model/pull` + `pull-status` モデルダウンロード管理(完了時自動設定)
|
|
105
107
|
- **client 側**(`lib/client.js`):8 言語 UI、文単位バッチ翻訳、ストリーミング Think 行、localStorage 永続化
|
|
106
108
|
- 純表示層:原文は会話履歴とモデルコンテキストに完全保持
|
package/README.ko.md
CHANGED
|
@@ -33,7 +33,7 @@ DeepSeek 계열 모델은 중국어로, 또는 우연히 생각에 쓰는 언어
|
|
|
33
33
|
- **🎚️ 번역 시점 조절** — 모두 사전 번역 / 이전 체인 지연 로딩(기본)/ 펼친 체인만 번역
|
|
34
34
|
- **🔗 동적 제공자 체인** — 목록 순서가 곧 실행 순서. 끌어서 정렬하고 행마다 켜고 끄기. 내장 google gtx / bing / 로컬 Ollama에 더해 임의의 사용자 지정 엔드포인트
|
|
35
35
|
- **🔌 사용자 지정 제공자(OpenAI / Anthropic)** — 설정 패널에서 OpenAI 호환 엔드포인트(`/v1/chat/completions`)나 **Anthropic Messages API**(Claude)를 추가: 유형, 프리셋, 베이스 URL, API 키, 모델
|
|
36
|
-
- **🪄 DSH 설정의 제공자 상속** — `settings.yaml`(`llm-pi-ai.providers`)에서 읽기 전용 DSH 행을 자동 발견하고, 버튼 한 번으로 다시 검사해 모두 체인에 추가. 키는 요청 시 `.credentials.yaml`에서 해석되며 플러그인 설정에 저장되지 않음. harness 자체의 기본 경로도 포함됩니다: `agent-default-model`이 `deepseek-official`을 가리키면 DeepSeek 공식 API도 DSH 행으로 제공됩니다
|
|
36
|
+
- **🪄 DSH 설정의 제공자 상속** — `profiles/<profile>/cordis.patch.yml` / 이전 `settings.yaml`(`llm-pi-ai.providers`)에서 읽기 전용 DSH 행을 자동 발견하고, 버튼 한 번으로 다시 검사해 모두 체인에 추가. 키는 요청 시 `.credentials.yaml`에서 해석되며 플러그인 설정에 저장되지 않음. harness 자체의 기본 경로도 포함됩니다: `default-model` / `agent-default-model`이 `deepseek-official`을 가리키면 DeepSeek 공식 API도 DSH 행으로 제공됩니다
|
|
37
37
|
- **⏱️ 실패 후 복구** — 실패 결과를 메모리에 30초간 캐시해 반복 요청을 줄이고 이후 자동으로 재시도합니다. 제공자마다 테스트 버튼이 있습니다
|
|
38
38
|
|
|
39
39
|
## 📦 설치
|
|
@@ -75,6 +75,8 @@ DSH **0.2.0-rc.2**에서는 **dsh-think-translate 1.2.5 이상**을 사용하세
|
|
|
75
75
|
|
|
76
76
|
**1.2.8 전송 및 복구:** 일반 HTTP/HTTPS 번역은 텍스트 배치마다 curl을 실행하는 대신 Node 기본 네트워크 기능을 사용합니다. SOCKS 프록시와 Ollama 설치 프로그램은 curl 호환 경로를 유지합니다. 실패 결과를 메모리에 30초간 보관한 뒤 사고·답변 번역을 자동 재시도합니다. 타임아웃은 로컬 모델 로드와 다른 제공자로의 전환 시간을 고려합니다.
|
|
77
77
|
|
|
78
|
+
**1.2.10 DSH 프로필 호환:** 실행 중인 Desktop/Web 프로필의 `profiles/<profile>/cordis.patch.yml`(`id` + `config`)을 읽고 DSH 홈 패치와 시작 오버레이를 순서대로 적용합니다. 이전 `settings.yaml`도 지원합니다. 기본 모델 행은 `default-model`을 지원하며 자격 증명은 DSH 홈의 `.credentials.yaml`에서 읽어 메모리에만 유지합니다. 다시 검사하면 실제 설정 경로가 표시됩니다.
|
|
79
|
+
|
|
78
80
|
**1.2.9 언어 수정:** 일본어 어휘 힌트는 일본어 대상에만 적용하며 OpenAI/Anthropic에 전체 언어 이름을 전달합니다. Bing은 `auto-detect`와 `zh-Hans`/`zh-Hant`를 사용합니다. 중국어 감지 시 경로·코드·JSON을 제외하고, 이미 간체인 본문은 건너뛰며 자주 쓰는 번체 글자를 감지하면 변환을 요청합니다. 일본어·한국어 본문은 번역합니다. 이전 번역 캐시만 한 번 삭제하고 설정은 유지합니다.
|
|
79
81
|
|
|
80
82
|
## 🚀 사용법
|
|
@@ -83,7 +85,7 @@ DSH **0.2.0-rc.2**에서는 **dsh-think-translate 1.2.5 이상**을 사용하세
|
|
|
83
85
|
2. **대상 언어** 선택(예: 한국어) — 설정 패널, 사고 행, 작업 카드가 모두 전환
|
|
84
86
|
3. **제공자 체인** 관리(드래그로 순서 변경, 체크로 사용):
|
|
85
87
|
- 내장: **google gtx / bing**(무료, 시스템 프록시 경유)과 **로컬 Ollama**. 기존 소프트웨어와 모델은 재사용하고, 없는 항목은 준비 버튼을 누른 뒤 설치·다운로드하며 진행률을 표시합니다(7b/14b 또는 사용자 지정 모델)
|
|
86
|
-
- **DSH 제공자**: `settings.yaml`에 설정된 엔드포인트가 자동 표시(읽기 전용, 체크하면 체인에 추가). 목록 아래의 **DSH 설정에서 가져오기**는 다시 검사해 한 번에 모두 추가합니다(baseURL과 키를 다시 입력할 필요가 없고, 키는 DSH 자체 자격 증명에서 해석됩니다)
|
|
88
|
+
- **DSH 제공자**: `profiles/<profile>/cordis.patch.yml` / 이전 `settings.yaml`에 설정된 엔드포인트가 자동 표시(읽기 전용, 체크하면 체인에 추가). 목록 아래의 **DSH 설정에서 가져오기**는 다시 검사해 한 번에 모두 추가합니다(baseURL과 키를 다시 입력할 필요가 없고, 키는 DSH 자체 자격 증명에서 해석됩니다)
|
|
87
89
|
- 키는 직접 입력하거나 프리셋·DSH 행이 가진 **`apiKeyEnv`** 로 지정할 수 있습니다. 그런 행에는 `env:NAME` 배지가 붙고 키는 요청 시 해석되며 `config.json`에 저장되지 않습니다. 편집 폼에는 환경 변수 입력란이 없지만(값은 그대로 유지) 필드를 비우면 실제로 삭제됩니다(명시적 삭제로 전송)
|
|
88
90
|
- 체크를 해제하면 해당 제공자는 건너뜁니다. 자세한 내용은 [README.md](README.md)
|
|
89
91
|
4. 메시지를 보내고 Think 행을 펼쳐 번역 확인
|
|
@@ -100,7 +102,7 @@ DSH **0.2.0-rc.2**에서는 **dsh-think-translate 1.2.5 이상**을 사용하세
|
|
|
100
102
|
```
|
|
101
103
|
|
|
102
104
|
- **제공자 설정**은 `config.json`(런타임 생성, gitignore 대상)에 있습니다: `chain`(순서 있는 id), `fallback`(enabled + chain, 설정 파일 전용), `providers`(각 항목의 `type`/`enabled`/`baseURL`/`apiKey`/`apiKeyEnv`/`model`). 예전 `priority` 형식은 자동 마이그레이션됩니다. `apiKeyEnv`를 선언한 제공자는 요청 시 해당 환경 변수에서 키를 해석하며(리터럴 `apiKey`는 폴백), 해석된 키를 `config.json`에 다시 쓰지 않습니다. 패치의 `null`은 해당 필드 삭제를 뜻하며 UI가 그렇게 비웁니다
|
|
103
|
-
- **DSH 자동 발견**은 로드 시 harness의 `settings.yaml`(`llm-pi-ai.providers`)과 `.credentials.yaml`(`refs`)을 읽습니다. 발견된 제공자는 `source: "dsh"`로 표시되고 해석된 키는 메모리에만 남으며(`config.json`에 기록되지 않음), `/_xlate/dsh-scan`으로 필요할 때 다시 읽습니다
|
|
105
|
+
- **DSH 자동 발견**은 로드 시 harness의 `profiles/<profile>/cordis.patch.yml` / 이전 `settings.yaml`(`llm-pi-ai.providers`)과 `.credentials.yaml`(`refs`)을 읽습니다. 발견된 제공자는 `source: "dsh"`로 표시되고 해석된 키는 메모리에만 남으며(`config.json`에 기록되지 않음), `/_xlate/dsh-scan`으로 필요할 때 다시 읽습니다
|
|
104
106
|
- **host 측**(`lib/index.js`): 제공자 어댑터, LRU 캐시(600), `/_xlate/models` 모델 목록, `/_xlate/model/pull` + `pull-status` 모델 다운로드 관리(완료 시 자동 설정)
|
|
105
107
|
- **client 측**(`lib/client.js`): 8개 언어 UI, 문장 배치 번역, 스트리밍 Think 행, localStorage 영구 저장
|
|
106
108
|
- 순수 표시 계층: 원문은 대화 기록과 모델 컨텍스트에 완전 보존
|
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ DeepSeek-class models often reason in Chinese — or in whatever language they h
|
|
|
24
24
|
- **🌍 8 languages, one consistent UI** — 中文 / English / 日本語 / 한국어 / Español / Français / Deutsch / Русский; the settings panel, thinking rows and task cards all follow your choice, and it persists across reloads
|
|
25
25
|
- **🔗 Dynamic provider chain** — order providers by drag-and-drop and enable/disable each one. Built-ins (google gtx, bing, local Ollama) plus any number of custom providers; the chain already ends with the free providers, so a second fallback pass is a config-file option, not a UI one
|
|
26
26
|
- **🔌 Custom providers (OpenAI & Anthropic)** — add arbitrary OpenAI-compatible endpoints (any `/v1/chat/completions` gateway) or native **Anthropic Messages API** endpoints (Claude) from the settings panel: name, type, base URL, API key, model
|
|
27
|
-
- **🪄 DSH provider discovery** — providers already configured in DSH's `settings.yaml` (`llm-pi-ai.providers`, e.g. linuxdo-hub, coding-hub) are auto-discovered and appear in the chain as read-only "DSH" entries, and **one button in settings rescans and adds them all at once**; keys are resolved from `.credentials.yaml` at runtime and never written to the plugin's config. The harness's own default route counts too: when `agent-default-model` points at `deepseek-official`, DeepSeek's API is offered as one more DSH entry
|
|
27
|
+
- **🪄 DSH provider discovery** — providers already configured in DSH's `profiles/<profile>/cordis.patch.yml` / legacy `settings.yaml` (`llm-pi-ai.providers`, e.g. linuxdo-hub, coding-hub) are auto-discovered and appear in the chain as read-only "DSH" entries, and **one button in settings rescans and adds them all at once**; keys are resolved from `.credentials.yaml` at runtime and never written to the plugin's config. The harness's own default route counts too: when `default-model` / `agent-default-model` points at `deepseek-official`, DeepSeek's API is offered as one more DSH entry
|
|
28
28
|
- **🔒 Local Ollama** — reuse an existing installation and model. When enabled Ollama is first in priority, or you click Test, the plugin starts the service and loads the model as needed. Missing software/models require the setup button before installation/download, with live progress
|
|
29
29
|
- **🧠 Zero context cost** — pure display layer: the model still sees the original text, and translated text never consumes the context window
|
|
30
30
|
- **☁️ Google / Bing fallback** — automatic switch when other providers are unavailable (google goes through a Node CONNECT tunnel using the system proxy, bypassing anti-bot blocks)
|
|
@@ -72,6 +72,8 @@ Version **1.2.6** fixes duplicated thinking and answer text in DSH 0.2: the assi
|
|
|
72
72
|
|
|
73
73
|
**1.2.8 transport and recovery:** ordinary HTTP/HTTPS translation requests use native Node networking instead of spawning curl per text batch; SOCKS proxies and the Ollama installer retain curl compatibility. Failures stay in memory for 30 seconds, then reasoning and answer translation resume automatically. Request deadlines allow time for local-model loading and provider fallback.
|
|
74
74
|
|
|
75
|
+
**1.2.10 DSH profile compatibility:** discovery follows the running Desktop/Web profile and reads `profiles/<profile>/cordis.patch.yml` (`id` + `config`), with home patches and launcher overlays applied afterward. Legacy `settings.yaml` is still supported. Default model rows use `default-model`; credentials remain in the DSH home `.credentials.yaml` and stay in memory. Rescan diagnostics show the file actually read.
|
|
76
|
+
|
|
75
77
|
**1.2.9 language fixes:** Japanese vocabulary hints now apply only to Japanese targets; OpenAI/Anthropic requests include a full language name. Bing uses `auto-detect` and maps Chinese targets to `zh-Hans`/`zh-Hant`. Chinese detection examines prose after removing paths, code and JSON: already-Simplified prose is skipped, common Traditional script cues request conversion, and Japanese/Korean prose is translated. Old persisted translation results are cleared once; settings remain intact.
|
|
76
78
|
|
|
77
79
|
## 🚀 Usage
|
|
@@ -102,7 +104,7 @@ browser → POST /_xlate/translate (same-origin, no CORS)
|
|
|
102
104
|
```
|
|
103
105
|
|
|
104
106
|
- **Provider config** lives in `config.json` (runtime, gitignored): `chain` (ordered ids), `fallback` (enabled + chain, config-only), `providers` (per-provider `type`/`enabled`/`baseURL`/`apiKey`/`apiKeyEnv`/`model`). Old `priority`-based configs auto-migrate. A provider that declares `apiKeyEnv` resolves its key from that environment variable at request time (the literal `apiKey` stays as the fallback), and no env-resolved key is ever written back to `config.json`. A `null` field in a config patch deletes that field, which is how the UI clears one.
|
|
105
|
-
- **DSH discovery** reads the harness `settings.yaml` (`llm-pi-ai.providers`) and `.credentials.yaml` (`refs`) on load; discovered providers are marked `source: "dsh"`, resolved keys stay in memory (never written to `config.json`), and a `/_xlate/dsh-scan` route re-reads them on demand.
|
|
107
|
+
- **DSH discovery** reads the harness `profiles/<profile>/cordis.patch.yml` / legacy `settings.yaml` (`llm-pi-ai.providers`) and `.credentials.yaml` (`refs`) on load; discovered providers are marked `source: "dsh"`, resolved keys stay in memory (never written to `config.json`), and a `/_xlate/dsh-scan` route re-reads them on demand.
|
|
106
108
|
- **Host half** (`lib/index.js`): provider adapters, ordered chain + fallback execution, LRU cache (600), per-provider override for tests, `/_xlate/models` listing, `/_xlate/model/pull` + `pull-status` model download management (auto-configures on completion)
|
|
107
109
|
- **Client half** (`lib/client.js`): 8-language UI, drag-reorderable provider list, add/edit/delete custom providers, per-provider test buttons, sentence/paragraph-batched translation, streaming Think rows, localStorage persistence (settings + translation cache)
|
|
108
110
|
- Pure display layer: originals remain in the transcript and model context
|
package/README.ru.md
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
- **🎚️ Настраиваемый момент перевода** — переводить всё / ленивая загрузка старых цепочек (по умолчанию) / только при раскрытии
|
|
34
34
|
- **🔗 Динамическая цепочка провайдеров** — порядок списка и есть порядок обращения: перетаскивайте для сортировки, включайте и выключайте каждую строку. Встроенные google gtx / bing / локальный Ollama плюс любые свои эндпоинты
|
|
35
35
|
- **🔌 Свои провайдеры (OpenAI и Anthropic)** — из панели можно добавить любой OpenAI-совместимый эндпоинт (`/v1/chat/completions`) или **Anthropic Messages API** (Claude): тип, шаблон, базовый URL, ключ API, модель
|
|
36
|
-
- **🪄 Наследование провайдеров из DSH** — читает из `settings.yaml` (`llm-pi-ai.providers`) строки DSH только для чтения; одна кнопка заново сканирует и добавляет их все в цепочку. Ключ разрешается на каждый запрос из `.credentials.yaml` и никогда не попадает в конфиг плагина. Собственный маршрут харнесса тоже учитывается: если `agent-default-model` указывает на `deepseek-official`, официальный API DeepSeek появляется как ещё одна строка DSH
|
|
36
|
+
- **🪄 Наследование провайдеров из DSH** — читает из `profiles/<profile>/cordis.patch.yml` / прежний `settings.yaml` (`llm-pi-ai.providers`) строки DSH только для чтения; одна кнопка заново сканирует и добавляет их все в цепочку. Ключ разрешается на каждый запрос из `.credentials.yaml` и никогда не попадает в конфиг плагина. Собственный маршрут харнесса тоже учитывается: если `default-model` / `agent-default-model` указывает на `deepseek-official`, официальный API DeepSeek появляется как ещё одна строка DSH
|
|
37
37
|
- **⏱️ Восстановление после ошибок** — неудачные переводы сохраняются в памяти на 30 секунд, чтобы избежать частых повторов, затем автоматически повторяются; у каждого провайдера есть кнопка проверки
|
|
38
38
|
|
|
39
39
|
## 📦 Установка
|
|
@@ -75,6 +75,8 @@ New-Item -ItemType Junction -Path "$HOME\.dsh\profiles\node_modules\dsh-think-tr
|
|
|
75
75
|
|
|
76
76
|
**1.2.8 передача запросов и восстановление:** обычные HTTP/HTTPS-запросы перевода используют сетевые средства Node без запуска curl для каждого пакета текста. Для SOCKS-прокси и установщика Ollama сохранён путь через curl. Ошибки хранятся в памяти 30 секунд, после чего перевод рассуждений и ответов автоматически повторяется. Тайм-ауты учитывают загрузку локальной модели и переход к другим провайдерам.
|
|
77
77
|
|
|
78
|
+
**1.2.10 совместимость с профилями DSH:** обнаружение следует активному профилю Desktop/Web и читает `profiles/<profile>/cordis.patch.yml` (`id` + `config`), затем патчи домашнего каталога DSH и параметры запуска. Прежний `settings.yaml` поддерживается. Модель по умолчанию использует `default-model`; ключи читаются из `.credentials.yaml` домашнего каталога DSH и остаются только в памяти. Диагностика показывает прочитанный файл.
|
|
79
|
+
|
|
78
80
|
**1.2.9 языковые исправления:** подсказки японской лексики применяются только к японскому; OpenAI/Anthropic получают полное название языка. Bing использует `auto-detect` и `zh-Hans`/`zh-Hant`. Определение китайского исключает пути, код и JSON: упрощённая проза пропускается, частые традиционные иероглифы вызывают запрос преобразования, японская/корейская проза переводится. Старый кэш переводов очищается один раз, настройки сохраняются.
|
|
79
81
|
|
|
80
82
|
## 🚀 Использование
|
|
@@ -83,7 +85,7 @@ New-Item -ItemType Junction -Path "$HOME\.dsh\profiles\node_modules\dsh-think-tr
|
|
|
83
85
|
2. Выберите **целевой язык** (например, Русский) — панель, строки и карточки переключатся на этот язык
|
|
84
86
|
3. Управляйте **цепочкой провайдеров** (перетаскивание — порядок, галочка — включение):
|
|
85
87
|
- Встроенные: **google gtx / bing** (бесплатно, через системный прокси) и **локальный Ollama**. Существующие программа и модели используются повторно; для установки или скачивания недостающих компонентов нужно нажать кнопку подготовки, прогресс отображается (7b/14b или своя модель)
|
|
86
|
-
- **Провайдеры DSH**: настроенные в `settings.yaml` эндпоинты появляются сами (только чтение; галочка добавляет их в цепочку). Кнопка **Импортировать из конфигурации DSH** сразу под списком заново сканирует конфигурацию и добавляет их все за один раз — ни baseURL, ни ключ вводить не нужно: ключ берётся из учётных данных DSH
|
|
88
|
+
- **Провайдеры DSH**: настроенные в `profiles/<profile>/cordis.patch.yml` / прежний `settings.yaml` эндпоинты появляются сами (только чтение; галочка добавляет их в цепочку). Кнопка **Импортировать из конфигурации DSH** сразу под списком заново сканирует конфигурацию и добавляет их все за один раз — ни baseURL, ни ключ вводить не нужно: ключ берётся из учётных данных DSH
|
|
87
89
|
- Ключ вводится вручную или приходит как **`apiKeyEnv`** из шаблона либо строки DSH: у такой строки появляется значок `env:NAME`, а ключ разрешается на каждый запрос и никогда не пишется в `config.json`. В форме редактирования нет поля переменной окружения (значение сохраняется как есть), но очистка поля действительно его удаляет (отправляется как явное удаление)
|
|
88
90
|
- Снятая галочка означает, что провайдер пропускается. Подробности — в [README.md](README.md)
|
|
89
91
|
4. Отправьте сообщение и разверните строку Think, чтобы увидеть перевод
|
|
@@ -100,7 +102,7 @@ New-Item -ItemType Junction -Path "$HOME\.dsh\profiles\node_modules\dsh-think-tr
|
|
|
100
102
|
```
|
|
101
103
|
|
|
102
104
|
- **Конфигурация провайдеров** живёт в `config.json` (создаётся во время работы, в gitignore): `chain` (упорядоченные id), `fallback` (enabled + chain, только в файле), `providers` (у каждого `type`/`enabled`/`baseURL`/`apiKey`/`apiKeyEnv`/`model`). Старые конфиги с `priority` мигрируют автоматически; провайдер с `apiKeyEnv` разрешает ключ из этой переменной на каждый запрос (литеральный `apiKey` остаётся запасным), и разрешённый ключ никогда не пишется обратно в `config.json`; `null` в патче удаляет поле — так интерфейс и очищает значения
|
|
103
|
-
- **Обнаружение DSH** при загрузке читает `settings.yaml` (`llm-pi-ai.providers`) и `.credentials.yaml` (`refs`) харнесса; найденные провайдеры помечаются `source: "dsh"`, разрешённые ключи остаются в памяти (никогда в `config.json`), а маршрут `/_xlate/dsh-scan` перечитывает их по запросу
|
|
105
|
+
- **Обнаружение DSH** при загрузке читает `profiles/<profile>/cordis.patch.yml` / прежний `settings.yaml` (`llm-pi-ai.providers`) и `.credentials.yaml` (`refs`) харнесса; найденные провайдеры помечаются `source: "dsh"`, разрешённые ключи остаются в памяти (никогда в `config.json`), а маршрут `/_xlate/dsh-scan` перечитывает их по запросу
|
|
104
106
|
- **Host-часть** (`lib/index.js`): адаптеры провайдеров, LRU-кэш (600), `/_xlate/models`, `/_xlate/model/pull` + `pull-status` (автонастройка по завершении)
|
|
105
107
|
- **Client-часть** (`lib/client.js`): интерфейс на 8 языках, пакетный перевод, потоковые строки Think, localStorage
|
|
106
108
|
- Чистый слой отображения: оригиналы остаются в истории и контексте модели
|
package/README.zh-CN.md
CHANGED
|
@@ -33,7 +33,7 @@ DeepSeek 系模型经常用中文思考——或者用它们碰巧习惯的语
|
|
|
33
33
|
- **🎚️ 可调翻译时机** — 三档:全部预翻译 / 懒加载历史(默认)/ 仅展开时翻译
|
|
34
34
|
- **🔗 动态提供方链** — 列表顺序就是投递顺序:拖动排序、逐行开关;内置 google gtx / bing / 本地 Ollama,外加任意数量的自定义端点
|
|
35
35
|
- **🔌 自定义提供方(OpenAI 与 Anthropic)** — 设置面板里可添加任意 OpenAI 兼容端点(任何 `/v1/chat/completions` 网关)或原生 **Anthropic Messages API**(Claude):类型、预设、接口地址、API 密钥、模型
|
|
36
|
-
- **🪄 继承 DSH 已配置的提供方** — 自动发现 `settings.yaml`(`llm-pi-ai.providers`)里的只读 DSH 行,一个按钮重新扫描并全部加入链;密钥在请求时从 `.credentials.yaml` 解析,绝不写入插件配置;harness 自己的默认路线也算:`agent-default-model` 指向 `deepseek-official` 时,DeepSeek 官方 API 也会作为一条 DSH 条目提供
|
|
36
|
+
- **🪄 继承 DSH 已配置的提供方** — 自动发现 `profiles/<profile>/cordis.patch.yml` / 旧版 `settings.yaml`(`llm-pi-ai.providers`)里的只读 DSH 行,一个按钮重新扫描并全部加入链;密钥在请求时从 `.credentials.yaml` 解析,绝不写入插件配置;harness 自己的默认路线也算:`default-model` / `agent-default-model` 指向 `deepseek-official` 时,DeepSeek 官方 API 也会作为一条 DSH 条目提供
|
|
37
37
|
- **⏱️ 失败韧性** — 失败结果仅在内存缓存 30 秒,避免频繁重试;冷却结束后自动重试,每行保留独立的测试按钮
|
|
38
38
|
|
|
39
39
|
## 📦 安装
|
|
@@ -75,6 +75,8 @@ DSH **0.2.0-rc.2** 请使用 **dsh-think-translate 1.2.5 或更新版本**。1.2
|
|
|
75
75
|
|
|
76
76
|
**1.2.8 传输与恢复:**普通 HTTP/HTTPS 翻译请求改用 Node 原生网络,不再为每个文本批次启动 curl;SOCKS 代理和 Ollama 安装器保留 curl 兼容路径。失败结果在内存缓存 30 秒,冷却结束后思考与回答翻译会自动恢复重试。请求超时为本地模型加载和提供方回退留出时间。
|
|
77
77
|
|
|
78
|
+
**1.2.10 DSH 配置兼容:**发现提供方时跟随当前 Desktop/Web profile,读取 `profiles/<profile>/cordis.patch.yml`(`id` + `config`),随后应用 home 补丁与启动覆盖;继续支持旧版 `settings.yaml`。默认模型行支持 `default-model`,凭据仍从 DSH home 的 `.credentials.yaml` 解析且只留在内存;重新扫描会显示实际读取的配置路径。
|
|
79
|
+
|
|
78
80
|
**1.2.9 语言修复:**日语词汇提示仅用于日语目标,OpenAI/Anthropic 请求带上完整语言名称。Bing 使用 `auto-detect`,并将中文目标映射为 `zh-Hans`/`zh-Hant`。中文检测先排除路径、代码和 JSON:已是简体的正文跳过,常用繁体区别字触发转换请求,日语/韩语正文正常翻译。升级时一次性清理旧译文缓存,保留设置。
|
|
79
81
|
|
|
80
82
|
## 🚀 使用
|
|
@@ -83,7 +85,7 @@ DSH **0.2.0-rc.2** 请使用 **dsh-think-translate 1.2.5 或更新版本**。1.2
|
|
|
83
85
|
2. 选择**目标语言**(比如日本語)——设置面板、思考行、任务卡片全部切换为该语言
|
|
84
86
|
3. 管理**供应商链**(拖动排序,勾选即启用):
|
|
85
87
|
- 内置:**google gtx / bing**(免费,使用系统代理)与**本地模型(Ollama)**;复用已有软件和模型,缺少时点击准备按钮后才安装/下载,并显示进度(支持 7b/14b 或自定义模型)
|
|
86
|
-
- **DSH 供应商**:`settings.yaml` 里已配置的端点会自动出现(只读,勾选即加入链);列表下方的 **继承 DSH 已配置的 API** 会重新扫描并一键全部加入——不用重填 baseURL 或密钥(密钥在请求时从 DSH 自己的凭据里解析)
|
|
88
|
+
- **DSH 供应商**:`profiles/<profile>/cordis.patch.yml` / 旧版 `settings.yaml` 里已配置的端点会自动出现(只读,勾选即加入链);列表下方的 **继承 DSH 已配置的 API** 会重新扫描并一键全部加入——不用重填 baseURL 或密钥(密钥在请求时从 DSH 自己的凭据里解析)
|
|
87
89
|
- 密钥可以直接填,也可以由预设或 DSH 行带上 **`apiKeyEnv`**:那类行显示 `env:NAME` 徽标,密钥在请求时解析、绝不写入 `config.json`。编辑表单里没有环境变量输入框(值会原样保留),清空某个字段会真的删除它(以显式删除提交),所以从环境变量名切回字面密钥是可行的
|
|
88
90
|
- 取消勾选即跳过该供应商;更细的说明以 [README.md](README.md) 为准
|
|
89
91
|
4. 发消息让模型思考,展开 Think 行查看译文
|
|
@@ -100,7 +102,7 @@ DSH **0.2.0-rc.2** 请使用 **dsh-think-translate 1.2.5 或更新版本**。1.2
|
|
|
100
102
|
```
|
|
101
103
|
|
|
102
104
|
- **提供方配置** 存在 `config.json`(运行期生成,已 gitignore):`chain`(有序 id)、`fallback`(enabled + chain,仅配置文件)、`providers`(每项的 `type`/`enabled`/`baseURL`/`apiKey`/`apiKeyEnv`/`model`)。旧的 `priority` 配置会自动迁移;声明 `apiKeyEnv` 的提供方在请求时从该环境变量解析密钥(字面 `apiKey` 作为兜底),解析出的密钥绝不写回 `config.json`;patch 里显式的 `null` 表示删除该字段,UI 就是用它清空字段的
|
|
103
|
-
- **DSH 发现** 在加载时读取 harness 的 `settings.yaml`(`llm-pi-ai.providers`)与 `.credentials.yaml`(`refs`);被发现的提供方标记为 `source: "dsh"`,解析出的密钥只留在内存中(绝不写入 `config.json`),`/_xlate/dsh-scan` 路由可按需重新读取
|
|
105
|
+
- **DSH 发现** 在加载时读取 harness 的 `profiles/<profile>/cordis.patch.yml` / 旧版 `settings.yaml`(`llm-pi-ai.providers`)与 `.credentials.yaml`(`refs`);被发现的提供方标记为 `source: "dsh"`,解析出的密钥只留在内存中(绝不写入 `config.json`),`/_xlate/dsh-scan` 路由可按需重新读取
|
|
104
106
|
- **host 半边**(`lib/index.js`):供应商适配器、LRU 缓存(600)、`/_xlate/models` 模型列表、`/_xlate/model/pull` + `pull-status` 模型下载管理(完成后自动配置启用)
|
|
105
107
|
- **client 半边**(`lib/client.js`):8 语言 UI、段落/句子分批翻译、流式 Think 行、设置与译文缓存持久化(localStorage)
|
|
106
108
|
- 纯显示层:原文完整保留在会话日志与模型上下文中
|
package/lib/client.js
CHANGED
|
@@ -23,7 +23,7 @@ window.__ModuleLoader__.load({
|
|
|
23
23
|
// there yet (an older process still serving the port), and the contract test
|
|
24
24
|
// pins PLUGIN_VERSION to package.json so the two can never drift.
|
|
25
25
|
var PLUGIN_NAME = "dsh-think-translate";
|
|
26
|
-
var PLUGIN_VERSION = "1.2.
|
|
26
|
+
var PLUGIN_VERSION = "1.2.10";
|
|
27
27
|
var PLUGIN_REPO = "https://github.com/UncleK/dsh-think-translate";
|
|
28
28
|
// The official MarkdownText contract changed in 0.1.2-alpha: it now takes a
|
|
29
29
|
// nested `labels` object and dereferences it without defaults inside its code
|
|
@@ -2111,8 +2111,8 @@ window.__ModuleLoader__.load({
|
|
|
2111
2111
|
var provs = cfg.providers || {};
|
|
2112
2112
|
|
|
2113
2113
|
// ---- inherit the harness's own providers ------------------------
|
|
2114
|
-
// DSH already knows its endpoints (llm-pi-ai.providers in the
|
|
2115
|
-
//
|
|
2114
|
+
// DSH already knows its endpoints (llm-pi-ai.providers in the active
|
|
2115
|
+
// profile) and where their keys live (.credentials.yaml). The host
|
|
2116
2116
|
// exposes them as read-only `source: 'dsh'` entries and re-reads them on
|
|
2117
2117
|
// demand, so the one-click path is: rescan, then add — never retype a base
|
|
2118
2118
|
// URL or a key per provider.
|
|
@@ -2120,7 +2120,7 @@ window.__ModuleLoader__.load({
|
|
|
2120
2120
|
var dshAvailable = dshAll.filter(function (id) { return chain.indexOf(id) < 0; });
|
|
2121
2121
|
// The host already merges the harness's providers into the config it serves,
|
|
2122
2122
|
// so they show up in the "available providers" list without being asked for.
|
|
2123
|
-
// What the button adds is the RE-READ: editing
|
|
2123
|
+
// What the button adds is the RE-READ: editing the active DSH profile (or
|
|
2124
2124
|
// fixing the home the host looks at) otherwise needs a web restart.
|
|
2125
2125
|
var rescanDsh = function () {
|
|
2126
2126
|
setMsg(t.dshRescanning);
|
|
@@ -2133,7 +2133,7 @@ window.__ModuleLoader__.load({
|
|
|
2133
2133
|
var found = (d.providers || []).length;
|
|
2134
2134
|
setMsg(found
|
|
2135
2135
|
? t.dshRescanDone + " · " + found
|
|
2136
|
-
: t.dshImportEmpty + (d.home ? " · " + d.home + "/settings.yaml" : ""));
|
|
2136
|
+
: t.dshImportEmpty + (d.sourcePath ? " · " + d.sourcePath : (d.home ? " · " + d.home + "/settings.yaml" : "")) + (d.error ? " · " + d.error : ""));
|
|
2137
2137
|
})
|
|
2138
2138
|
.catch(function (e) { setMsg(t.connFail + ": " + (e && e.message ? e.message : e)); });
|
|
2139
2139
|
};
|
package/lib/discovery.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// DSH moved settings into profile-owned Cordis rows. Read only literal values;
|
|
2
|
+
// never evaluate a profile's !!js expressions while discovering providers.
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
4
|
+
import { join, basename } from 'node:path'
|
|
5
|
+
import { load, DEFAULT_SCHEMA, Type } from 'js-yaml'
|
|
6
|
+
|
|
7
|
+
const EXPRESSION = Object.freeze({ dshExpression: true })
|
|
8
|
+
const schema = DEFAULT_SCHEMA.extend([
|
|
9
|
+
new Type('tag:yaml.org,2002:js', { kind: 'scalar', construct: () => EXPRESSION }),
|
|
10
|
+
])
|
|
11
|
+
const object = value => value !== null && typeof value === 'object' && !Array.isArray(value) && value !== EXPRESSION
|
|
12
|
+
const text = value => typeof value === 'string' && value.trim() ? value.trim() : undefined
|
|
13
|
+
|
|
14
|
+
export function parseDshYaml(source) {
|
|
15
|
+
try { return load(source, { schema, maxDepth: 40, maxTotalMergeKeys: 1000 }) }
|
|
16
|
+
// YAMLException includes source lines, which can contain credentials.
|
|
17
|
+
catch { throw new Error('Invalid DSH YAML') }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Cordis replaces a row's config when a later patch supplies that field.
|
|
21
|
+
// Flatten groups and insert lists without following Includes or running code.
|
|
22
|
+
export function dshSections(documents) {
|
|
23
|
+
const rows = new Map()
|
|
24
|
+
function visit(document, parentDisabled = false, ancestors = new Set()) {
|
|
25
|
+
if (!document || typeof document !== 'object' || ancestors.has(document)) return
|
|
26
|
+
const next = new Set(ancestors).add(document)
|
|
27
|
+
if (Array.isArray(document)) {
|
|
28
|
+
for (const row of document) visit(row, parentDisabled, next)
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
if (!object(document)) return
|
|
32
|
+
if (Object.hasOwn(document, 'insert')) visit(document.insert, parentDisabled, next)
|
|
33
|
+
const id = text(document.id)
|
|
34
|
+
if (id) {
|
|
35
|
+
const old = rows.get(id) || {}
|
|
36
|
+
if (old.name && document.name && old.name !== document.name) return
|
|
37
|
+
const row = { ...old, ...document }
|
|
38
|
+
if (parentDisabled) row.disabled = true
|
|
39
|
+
rows.set(id, row)
|
|
40
|
+
if (row.group && Array.isArray(row.config)) visit(row.config, parentDisabled || (row.disabled != null && row.disabled !== false), next)
|
|
41
|
+
} else if (!Object.hasOwn(document, 'insert')) {
|
|
42
|
+
// Legacy settings.yaml uses section names instead of id/config rows.
|
|
43
|
+
for (const [key, config] of Object.entries(document)) {
|
|
44
|
+
if (object(config)) rows.set(key === 'agent-default-model' ? 'default-model' : key, { id: key, config })
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
for (const document of documents) visit(document)
|
|
49
|
+
const sections = new Map()
|
|
50
|
+
for (const [id, row] of rows) {
|
|
51
|
+
const key = id === 'agent-default-model' ? 'default-model' : id
|
|
52
|
+
// Keep a disabled row as a null section so a successful scan can remove
|
|
53
|
+
// its old imported providers, rather than retaining stale credentials.
|
|
54
|
+
sections.set(key, row.disabled != null && row.disabled !== false ? null : (object(row.config) ? row.config : {}))
|
|
55
|
+
}
|
|
56
|
+
return sections
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function providersFromSections(sections) {
|
|
60
|
+
const out = {}
|
|
61
|
+
const providers = sections.get('llm-pi-ai')?.providers
|
|
62
|
+
if (!object(providers)) return out
|
|
63
|
+
for (const [id, raw] of Object.entries(providers)) {
|
|
64
|
+
if (['__proto__', 'constructor', 'prototype'].includes(id)) continue
|
|
65
|
+
if (!object(raw) || raw.enabled === false || raw.disabled === true) continue
|
|
66
|
+
const baseURL = text(raw.baseURL)
|
|
67
|
+
const model = Array.isArray(raw.models) ? raw.models.map(m => object(m) ? text(m.id) : undefined).find(Boolean) : undefined
|
|
68
|
+
// Preserve the existing supported discovery protocol; Responses-only
|
|
69
|
+
// endpoints cannot be driven through our chat/completions adapter.
|
|
70
|
+
if (!baseURL || !model || (raw.api !== undefined && raw.api !== 'openai-completions')) continue
|
|
71
|
+
const entry = { baseURL, model }
|
|
72
|
+
if (text(raw.apiKeyEnv)) entry.apiKeyEnv = text(raw.apiKeyEnv)
|
|
73
|
+
if (text(raw.apiKey)) entry.apiKey = text(raw.apiKey)
|
|
74
|
+
Object.defineProperty(out, id, { value: entry, enumerable: true, configurable: true, writable: true })
|
|
75
|
+
}
|
|
76
|
+
return out
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function defaultRouteFromSections(sections) {
|
|
80
|
+
const config = sections.get('default-model')
|
|
81
|
+
if (config?.provider !== 'deepseek-official' || !text(config.model)) return null
|
|
82
|
+
const deepseek = sections.get('llm-deepseek')
|
|
83
|
+
if (sections.has('llm-deepseek') && deepseek === null) return null
|
|
84
|
+
return {
|
|
85
|
+
id: 'deepseek-official',
|
|
86
|
+
entry: {
|
|
87
|
+
type: 'openai',
|
|
88
|
+
baseURL: text(deepseek?.baseURL) || 'https://api.deepseek.com',
|
|
89
|
+
model: text(config.model), apiKeyEnv: 'DEEPSEEK_API_KEY',
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function extractDshProviders(source) {
|
|
95
|
+
return providersFromSections(dshSections([parseDshYaml(source)]))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function extractHarnessDefaultRoute(source) {
|
|
99
|
+
return defaultRouteFromSections(dshSections([parseDshYaml(source)]))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function extractCredRefs(source) {
|
|
103
|
+
const raw = parseDshYaml(source)?.refs
|
|
104
|
+
if (!object(raw)) return {}
|
|
105
|
+
return Object.fromEntries(Object.entries(raw).filter(([, value]) => typeof value === 'string' && value.length))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function scanDshProviders({ candidates = [], profileContext, modulePath = '', runtimeRows = [], exists = existsSync, readText = p => readFileSync(p, 'utf8') } = {}) {
|
|
109
|
+
// profileContext is supplied by the official launcher and wins over all
|
|
110
|
+
// path guesses, including stale settings.yaml under another DSH_HOME.
|
|
111
|
+
const installed = modulePath.replace(/\\/g, '/').match(/^(.*)\/profiles\/([^/]+)\/node_modules\//)
|
|
112
|
+
let home = text(profileContext?.home) || installed?.[1]
|
|
113
|
+
let profile = text(profileContext?.name) || installed?.[2]
|
|
114
|
+
const has = path => { try { return exists(path) } catch { return false } }
|
|
115
|
+
if (!home) home = candidates.find(dir => has(join(dir, 'settings.yaml')) || has(join(dir, 'cordis.patch.yml')) || ['desktop', 'web'].some(name => has(join(dir, 'profiles', name, 'cordis.patch.yml')))) || candidates[0] || ''
|
|
116
|
+
if (!profile && !profileContext?.dir && !profileContext?.patchPath) {
|
|
117
|
+
const found = ['desktop', 'web'].filter(name => has(join(home, 'profiles', name, 'cordis.patch.yml')))
|
|
118
|
+
if (found.length === 1) profile = found[0]
|
|
119
|
+
// When both profiles exist the caller must identify the running one.
|
|
120
|
+
if (found.length > 1) return { providers: {}, refs: {}, ok: false, diag: { home, profile: null, sourcePath: join(home, 'profiles'), sourcePaths: [], providers: [], error: 'Cannot identify the active DSH profile' } }
|
|
121
|
+
}
|
|
122
|
+
const dir = text(profileContext?.dir) || (profile ? join(home, 'profiles', profile) : undefined)
|
|
123
|
+
const patchPath = text(profileContext?.patchPath) || (dir ? join(dir, 'cordis.patch.yml') : undefined)
|
|
124
|
+
const legacyPath = join(home, 'settings.yaml')
|
|
125
|
+
const modern = Boolean(profileContext?.dir || profileContext?.patchPath || runtimeRows.length
|
|
126
|
+
|| (dir && has(join(dir, 'cordis.yml'))) || (patchPath && has(patchPath)) || has(join(home, 'cordis.patch.yml')))
|
|
127
|
+
const paths = modern ? [dir && join(dir, 'cordis.yml'), patchPath, join(home, 'cordis.patch.yml')].filter(Boolean) : [legacyPath]
|
|
128
|
+
const diag = { home, profile: profile || (dir ? basename(dir) : null), sourcePath: patchPath || paths[0], sourcePaths: [], settingsFound: has(legacyPath), credsFound: false, providers: [], error: null }
|
|
129
|
+
const result = { providers: {}, refs: {}, ok: false, diag }
|
|
130
|
+
try {
|
|
131
|
+
const documents = modern ? [runtimeRows] : []
|
|
132
|
+
for (const path of [...new Set(paths)]) {
|
|
133
|
+
diag.sourcePath = path
|
|
134
|
+
let source
|
|
135
|
+
try { source = readText(path) } catch (error) {
|
|
136
|
+
if (error?.code === 'ENOENT') continue
|
|
137
|
+
throw new Error('Unable to read DSH configuration')
|
|
138
|
+
}
|
|
139
|
+
documents.push(parseDshYaml(source))
|
|
140
|
+
diag.sourcePaths.push(path)
|
|
141
|
+
}
|
|
142
|
+
if (modern && Array.isArray(profileContext?.overlays)) documents.push(profileContext.overlays)
|
|
143
|
+
const sections = dshSections(documents)
|
|
144
|
+
const providers = providersFromSections(sections)
|
|
145
|
+
const def = defaultRouteFromSections(sections)
|
|
146
|
+
for (const [id, entry] of Object.entries(providers)) result.providers[id] = { type: 'openai', source: 'dsh', enabled: false, ...entry }
|
|
147
|
+
if (def && !Object.hasOwn(result.providers, def.id)) result.providers[def.id] = { source: 'dsh', enabled: false, ...def.entry }
|
|
148
|
+
result.ok = sections.has('llm-pi-ai') || sections.has('default-model') || (modern && runtimeRows.length > 0)
|
|
149
|
+
const credsPath = join(home, '.credentials.yaml')
|
|
150
|
+
let credentials = ''
|
|
151
|
+
try { credentials = readText(credsPath); diag.credsFound = true } catch (error) {
|
|
152
|
+
if (error?.code !== 'ENOENT') throw new Error('Unable to read DSH credentials')
|
|
153
|
+
}
|
|
154
|
+
result.refs = extractCredRefs(credentials)
|
|
155
|
+
diag.providers = Object.keys(result.providers)
|
|
156
|
+
diag.sourcePath = diag.sourcePaths.includes(patchPath) ? patchPath : (diag.sourcePaths.at(-1) || patchPath || paths[0])
|
|
157
|
+
} catch (error) {
|
|
158
|
+
result.providers = {}; result.refs = {}; result.ok = false
|
|
159
|
+
diag.error = error.message
|
|
160
|
+
}
|
|
161
|
+
return result
|
|
162
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { randomUUID } from 'node:crypto'
|
|
|
12
12
|
import { createLocalPreparation, ollamaOrigin, localFirst, validOllamaSignature } from './ollama.js'
|
|
13
13
|
import { fileURLToPath } from 'node:url'
|
|
14
14
|
import { requestText, isLoopback } from './transport.js'
|
|
15
|
+
import { scanDshProviders, extractDshProviders, extractCredRefs, extractHarnessDefaultRoute } from './discovery.js'
|
|
15
16
|
|
|
16
17
|
export const name = 'dsh-think-translate'
|
|
17
18
|
|
|
@@ -23,16 +24,8 @@ export const inject = ['webServer', 'subprocess', 'timer']
|
|
|
23
24
|
const CONFIG_PATH = fileURLToPath(new URL('./config.json', import.meta.url))
|
|
24
25
|
const PACKAGE_PATH = fileURLToPath(new URL('../package.json', import.meta.url))
|
|
25
26
|
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
// This mirrors DSH's own resolution — `$DSH_HOME`, then `~/.dsh` (see
|
|
29
|
-
// @deepseek-ai/dsh-home-paths: an explicit path, then DSH_HOME, then
|
|
30
|
-
// homedir()/.dsh, with a blank DSH_HOME treated as unset) — and only then the
|
|
31
|
-
// desktop-app layouts. Picking one guess was the bug: an install whose home is
|
|
32
|
-
// ~/.dsh fell through to %APPDATA%\dsh-desktop\harness, which does not exist
|
|
33
|
-
// there, so discovery silently reported "no provider configured" while the
|
|
34
|
-
// settings file sat right there. The candidates are resolved per call and the
|
|
35
|
-
// first one that actually holds a settings.yaml wins (see resolveHarnessHome).
|
|
27
|
+
// Fallback locations for launchers without profileContext. Modern launchers
|
|
28
|
+
// supply the active profile and home directly; discovery.js gives them priority.
|
|
36
29
|
function harnessHomeCandidates() {
|
|
37
30
|
const out = []
|
|
38
31
|
const env = process.env.DSH_HOME
|
|
@@ -85,6 +78,8 @@ const VALID_TYPES = ['google', 'bing', 'openai', 'anthropic']
|
|
|
85
78
|
|
|
86
79
|
let subprocessRef = null
|
|
87
80
|
let timerRef = null
|
|
81
|
+
let profileContextRef = null
|
|
82
|
+
let discoveryContextRef = null
|
|
88
83
|
|
|
89
84
|
async function localJson(url, options = {}, timeoutMs = 10000) {
|
|
90
85
|
const response = await fetch(url, { ...options, redirect: 'error', signal: AbortSignal.timeout(timeoutMs) })
|
|
@@ -828,162 +823,29 @@ function applyFullProviders(live, fullMap) {
|
|
|
828
823
|
|
|
829
824
|
// ---------------------------------------------------------------- dsh provider discovery
|
|
830
825
|
|
|
831
|
-
//
|
|
832
|
-
//
|
|
833
|
-
// extractor can handle without a full YAML parser.
|
|
834
|
-
// Returns { providerName: { apiKeyEnv, baseURL, model } }.
|
|
835
|
-
//
|
|
836
|
-
// Indentation reference (real file):
|
|
837
|
-
// llm-pi-ai: # 0 spaces (top-level key)
|
|
838
|
-
// providers: # 2 spaces
|
|
839
|
-
// linuxdo-hub: # 4 spaces (provider name)
|
|
840
|
-
// apiKeyEnv: # 6 spaces (provider fields)
|
|
841
|
-
// api: # 6 spaces
|
|
842
|
-
// baseURL: # 6 spaces
|
|
843
|
-
// models: # 6 spaces
|
|
844
|
-
// - id: # 8 spaces (model list item)
|
|
845
|
-
function extractDshProviders(txt) {
|
|
846
|
-
const providers = {}
|
|
847
|
-
// Tolerate sibling keys between "llm-pi-ai:" and the first "providers:"
|
|
848
|
-
// at 2-space indent (Important 5). Body ends at the next column-0 key
|
|
849
|
-
// (Important 2: cosmetic blank lines within the section are NOT a boundary).
|
|
850
|
-
const block = txt.match(/llm-pi-ai:[\s\S]*?\n[ \t]{2}providers:[^\n]*\n([\s\S]*?)(?=\n\S|$)/)
|
|
851
|
-
if (!block) return providers
|
|
852
|
-
const body = block[1]
|
|
853
|
-
// Each provider: " name:" followed by indented fields. Use [ \t] instead
|
|
854
|
-
// of \s so that blank lines between provider blocks naturally end the field
|
|
855
|
-
// group (Important 2). Use lookbehind to avoid consuming the newline
|
|
856
|
-
// separator between provider blocks.
|
|
857
|
-
const providerBlocks = body.match(/(?:^|(?<=\n))[ \t]{4}[a-zA-Z0-9_-]+:[^\n]*\n(?:[ \t]{6}[^\n]*\n?)*/g)
|
|
858
|
-
if (!providerBlocks) return providers
|
|
859
|
-
for (const pb of providerBlocks) {
|
|
860
|
-
const nameMatch = pb.match(/[ \t]{4}([a-zA-Z0-9_-]+):/)
|
|
861
|
-
if (!nameMatch) continue
|
|
862
|
-
const name = nameMatch[1]
|
|
863
|
-
const apiKeyEnv = (pb.match(/[ \t]{6}apiKeyEnv:\s*(\S+)/) || [])[1]
|
|
864
|
-
const api = (pb.match(/[ \t]{6}api:\s*(\S+)/) || [])[1]
|
|
865
|
-
const baseURL = (pb.match(/[ \t]{6}baseURL:\s*(\S+)/) || [])[1]
|
|
866
|
-
// First model id from the models list. Real settings.yaml indents
|
|
867
|
-
// `models:` at 6 spaces and `- id:` at 8 spaces.
|
|
868
|
-
const modelMatch = pb.match(/[ \t]{6}models:[^\n]*\n[ \t]{8}- id:\s*(\S+)/)
|
|
869
|
-
const model = modelMatch ? modelMatch[1] : null
|
|
870
|
-
// Only accept openai-compatible providers (api absent or openai-completions)
|
|
871
|
-
if (baseURL && model && (!api || api === 'openai-completions')) {
|
|
872
|
-
providers[name] = { apiKeyEnv, baseURL, model }
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
return providers
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
// The harness ALSO has one built-in provider route that never shows up in
|
|
879
|
-
// llm-pi-ai.providers: `deepseek-official`, registered by llm-deepseek and
|
|
880
|
-
// configured through `agent-default-model` (provider + model) plus a credential
|
|
881
|
-
// ref. Its transport is OpenAI-compatible — llm-deepseek itself posts to
|
|
882
|
-
// `${baseURL}/chat/completions` with PUBLIC_BASE_URL = https://api.deepseek.com
|
|
883
|
-
// and the DEEPSEEK_API_KEY credential — so this plugin can drive it with the
|
|
884
|
-
// same adapter, which is what lets "inherit from DSH" offer DeepSeek's own API
|
|
885
|
-
// and not just the custom endpoints someone declared by hand.
|
|
886
|
-
const BUILTIN_HARNESS_ROUTES = {
|
|
887
|
-
'deepseek-official': { type: 'openai', baseURL: 'https://api.deepseek.com', apiKeyEnv: 'DEEPSEEK_API_KEY' },
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
// Returns { id, entry } for the harness's default model route when the plugin
|
|
891
|
-
// knows how to speak to it, else null. `refs` is the credential map, used only to
|
|
892
|
-
// confirm the key exists somewhere.
|
|
893
|
-
function extractHarnessDefaultRoute(txt, refs) {
|
|
894
|
-
const block = txt.match(/^agent-default-model:[^\n]*\n((?:[ \t]+[^\n]*\n?)*)/m)
|
|
895
|
-
if (!block) return null
|
|
896
|
-
const provider = (block[1].match(/^[ \t]+provider:[ \t]*(\S+)/m) || [])[1]
|
|
897
|
-
const model = (block[1].match(/^[ \t]+model:[ \t]*(\S+)/m) || [])[1]
|
|
898
|
-
if (!provider || !model) return null
|
|
899
|
-
const route = BUILTIN_HARNESS_ROUTES[provider]
|
|
900
|
-
if (!route) return null
|
|
901
|
-
const entry = { type: route.type, baseURL: route.baseURL, model: model }
|
|
902
|
-
// The ref is the route's documented credential; carry it even when the user
|
|
903
|
-
// keeps the key in the environment instead of .credentials.yaml, because the
|
|
904
|
-
// host resolves apiKeyEnv from both.
|
|
905
|
-
if (route.apiKeyEnv) entry.apiKeyEnv = route.apiKeyEnv
|
|
906
|
-
void refs
|
|
907
|
-
return { id: provider, entry: entry }
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
// (2026-09-01) Parse .credentials.yaml refs. Returns { envName: value }.
|
|
911
|
-
function extractCredRefs(txt) {
|
|
912
|
-
const refs = {}
|
|
913
|
-
const block = txt.match(/refs:\s*\n([\s\S]*?)(?=\n\S|$)/)
|
|
914
|
-
if (!block) return refs
|
|
915
|
-
const body = block[1]
|
|
916
|
-
const pairs = body.match(/^\s{2}[a-zA-Z0-9_-]+:.*$/gm)
|
|
917
|
-
if (!pairs) return refs
|
|
918
|
-
for (const line of pairs) {
|
|
919
|
-
const m = line.match(/^\s{2}([a-zA-Z0-9_-]+):\s*(\S+)/)
|
|
920
|
-
if (m) refs[m[1]] = m[2]
|
|
921
|
-
}
|
|
922
|
-
return refs
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
// DSH credentials refs cache (2026-09-01): env name -> api key.
|
|
926
|
-
// Read from .credentials.yaml (600-perm) at discovery time; values live in
|
|
927
|
-
// memory only and are NEVER persisted to the world-readable config.json.
|
|
826
|
+
// Resolved credentials live only in memory. A failed scan cannot remove the
|
|
827
|
+
// existing DSH entries; only a successfully read provider section can do so.
|
|
928
828
|
const credRefsCache = new Map()
|
|
929
|
-
|
|
930
|
-
// (2026-09-01) True when the last discovery actually parsed the llm-pi-ai
|
|
931
|
-
// providers section. Guards the ghost cleanup in mergeDshProviders: if
|
|
932
|
-
// settings.yaml is temporarily unreadable, discovery returns an empty set and
|
|
933
|
-
// cleanup would otherwise wipe every DSH provider.
|
|
934
829
|
let dshScanOk = false
|
|
935
830
|
|
|
936
|
-
// (2026-09-01) Discover DSH-configured service providers from the harness
|
|
937
|
-
// config files. The returned entries carry apiKeyEnv but NOT the apiKey
|
|
938
|
-
// value itself — the key is resolved in-memory during mergeDshProviders()
|
|
939
|
-
// from credRefsCache so it is never written to disk.
|
|
940
831
|
async function discoverDshProviders() {
|
|
941
|
-
|
|
942
|
-
const out = {}
|
|
943
|
-
const candidates = harnessHomeCandidates()
|
|
944
|
-
const picked = pickHarnessHome(candidates, function (p) { return fs.existsSync(p) })
|
|
945
|
-
const home = picked.home
|
|
946
|
-
const diag = { home: home, candidates: candidates, settingsFound: picked.settingsFound, credsFound: false, providers: [], error: null }
|
|
832
|
+
let runtimeRows = []
|
|
947
833
|
try {
|
|
948
|
-
|
|
949
|
-
const
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
try { credsTxt = fs.readFileSync(credsPath, 'utf8') } catch (e) {}
|
|
953
|
-
diag.credsFound = credsTxt.length > 0
|
|
954
|
-
const providers = extractDshProviders(settingsTxt)
|
|
955
|
-
const refs = extractCredRefs(credsTxt)
|
|
956
|
-
dshScanOk = /llm-pi-ai:[\s\S]*?\n[ \t]{2}providers:/m.test(settingsTxt)
|
|
957
|
-
// Refresh the credential refs cache from the real file (read once per
|
|
958
|
-
// discovery, values live in memory only).
|
|
959
|
-
credRefsCache.clear()
|
|
960
|
-
for (const k of Object.keys(refs)) credRefsCache.set(k, refs[k])
|
|
961
|
-
for (const name of Object.keys(providers)) {
|
|
962
|
-
const p = providers[name]
|
|
963
|
-
if (!p.baseURL || !p.model) { diag.skipped = (diag.skipped || []).concat(name); continue }
|
|
964
|
-
const entry = {
|
|
965
|
-
type: 'openai',
|
|
966
|
-
source: 'dsh',
|
|
967
|
-
baseURL: p.baseURL,
|
|
968
|
-
model: p.model,
|
|
969
|
-
enabled: false, // user must explicitly add to chain
|
|
970
|
-
}
|
|
971
|
-
if (p.apiKeyEnv) entry.apiKeyEnv = p.apiKeyEnv
|
|
972
|
-
out[name] = entry
|
|
973
|
-
diag.providers.push(name)
|
|
834
|
+
// Resolve at scan time: the optional editor may mount after this plugin.
|
|
835
|
+
const editor = discoveryContextRef?.get('configEditor')
|
|
836
|
+
if (typeof editor?.entries === 'function') {
|
|
837
|
+
runtimeRows = editor.entries().map(entry => entry.options)
|
|
974
838
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
dshScanDiag = diag
|
|
986
|
-
return out
|
|
839
|
+
} catch { /* File discovery also works without the optional editor service. */ }
|
|
840
|
+
const scan = scanDshProviders({
|
|
841
|
+
candidates: harnessHomeCandidates(), profileContext: profileContextRef,
|
|
842
|
+
modulePath: fileURLToPath(import.meta.url), runtimeRows,
|
|
843
|
+
})
|
|
844
|
+
dshScanDiag = scan.diag
|
|
845
|
+
dshScanOk = scan.ok
|
|
846
|
+
credRefsCache.clear()
|
|
847
|
+
for (const [key, value] of Object.entries(scan.refs)) credRefsCache.set(key, value)
|
|
848
|
+
return scan.providers
|
|
987
849
|
}
|
|
988
850
|
|
|
989
851
|
// (2026-09-01) Remove a provider id from the chain and fallback chain.
|
|
@@ -1000,7 +862,7 @@ function removeFromChain(cfg, id) {
|
|
|
1000
862
|
// DSH-managed fields (baseURL/model/apiKeyEnv) come from the fresh scan;
|
|
1001
863
|
// only the user-controlled `enabled` flag survives from the persisted entry.
|
|
1002
864
|
// apiKey is resolved from the in-memory credRefsCache (Important 1+3).
|
|
1003
|
-
// Providers that disappeared from
|
|
865
|
+
// Providers that disappeared from the active DSH configuration are cleaned up (Important 4).
|
|
1004
866
|
async function mergeDshProviders(cfg) {
|
|
1005
867
|
const dsh = await discoverDshProviders()
|
|
1006
868
|
const discovered = new Set(Object.keys(dsh))
|
|
@@ -1018,10 +880,10 @@ async function mergeDshProviders(cfg) {
|
|
|
1018
880
|
cfg.providers = cfg.providers || {}
|
|
1019
881
|
cfg.providers[id] = entry
|
|
1020
882
|
}
|
|
1021
|
-
// Ghost cleanup: drop DSH entries that no longer exist in
|
|
883
|
+
// Ghost cleanup: drop DSH entries that no longer exist in the active profile;
|
|
1022
884
|
// also purge their id from the chain to avoid dead references. Only when
|
|
1023
885
|
// the last discovery actually parsed the providers section — otherwise a
|
|
1024
|
-
// transient
|
|
886
|
+
// transient configuration read failure would wipe every DSH provider.
|
|
1025
887
|
if (dshScanOk) {
|
|
1026
888
|
const provs = cfg.providers || {}
|
|
1027
889
|
for (const id of Object.keys(provs)) {
|
|
@@ -1440,6 +1302,9 @@ async function handlePullStatus() {
|
|
|
1440
1302
|
export function apply(ctx) {
|
|
1441
1303
|
subprocessRef = ctx.get('subprocess')
|
|
1442
1304
|
timerRef = ctx.get('timer')
|
|
1305
|
+
// Optional for compatibility with DSH releases predating profiles.
|
|
1306
|
+
profileContextRef = ctx.get('profileContext')
|
|
1307
|
+
discoveryContextRef = ctx
|
|
1443
1308
|
|
|
1444
1309
|
const webServer = ctx.get('webServer')
|
|
1445
1310
|
if (webServer === undefined) return
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-think-translate",
|
|
3
3
|
"description": "Display-layer translation for the DSH web app: thinking chain, task cards, and optional answer text, in 8 target languages (local Ollama / any OpenAI-compatible or Anthropic endpoint / Google / Bing, fail-open)",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.10",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"lib/client.js",
|
|
15
15
|
"lib/ollama.js",
|
|
16
16
|
"lib/transport.js",
|
|
17
|
+
"lib/discovery.js",
|
|
17
18
|
"cordis.patch.yml",
|
|
18
19
|
"demo/demo.gif",
|
|
19
20
|
"demo/demo2.gif"
|
|
@@ -70,5 +71,8 @@
|
|
|
70
71
|
"optional": true
|
|
71
72
|
}
|
|
72
73
|
},
|
|
73
|
-
"license": "MIT"
|
|
74
|
+
"license": "MIT",
|
|
75
|
+
"dependencies": {
|
|
76
|
+
"js-yaml": "4.3.2"
|
|
77
|
+
}
|
|
74
78
|
}
|