Des plugins qui ne marchaient qu'à moitié : Hermes unifie enfin la résolution des noms de plugins

Vous ouvrez le hub de plugins de Hermes Desktop et installez un plugin qui livre un panneau desktop. L’interrupteur Agent s’allume, mais le panneau à côté ne s’allume jamais — vous creusez et les outils sont là, les commandes sont là, l’interface n’y est simplement pas. Ou plus simple : hermes plugins install hermes-memory-wiki dit que c’est bon, vous enchaînez avec hermes plugins enable hermes-memory-wiki, et la réponse est No plugin named hermes-memory-wiki. Vous venez d’installer le truc et il prétend ne pas connaître le nom. Vous ne l’avez pas mal retenu — Hermes avait trois résolveurs de noms qui se contredisaient. La PR #135030, fusionnée le 8 octobre, a nettoyé ça.
Le symptôme : un plugin, trois noms
Commençons par les symptômes, parce que ces trois erreurs semblent sans rapport et partagent une seule cause racine :
- S’installe bien, refuse d’être géré. Le nom sous lequel une installation depuis le catalogue atterrit et le nom dans le manifeste du plugin peuvent différer. Les chiffres sont précis : 17 des 484 entrées du catalogue s’installent sous un nom de manifeste différent —
hermes-memory-wiki→memory-wiki,touchdesigner→td,hermes-subscription-meter→subscription-meter.installrésout le nom de catalogue, donc il réussit ; maisenable,disable,show,removeetupdaterésolvaient le nom de manifeste ou la clé, donc chacun d’eux échouait sur ce même nom. - Un paquet s’affiche en deux lignes. Clonez-le vous-même dans
~/.hermes/plugins/hermes-subscription-meter, comme le suggère le README du plugin, et le hub desktop montre deux lignes : une moitié desktop seule proposant « Install here » pour une moitié agent déjà installée, plus une ligne agent distincte. Le côté desktop rejoignait la ligne agent par nom de manifeste et sa propre moitié par nom de dossier. - L’interrupteur ne couvre qu’une moitié. Une moitié desktop est livrée en opt-in, donc activer l’interrupteur Agent n’activait jamais la moitié desktop avec lui.
Ce que ces trois cas ont en commun : le nom était résolu à plusieurs endroits, selon des règles différentes.
La cause : cinq résolveurs, cinq jeux de règles
Avant le changement, il y avait cinq endroits qui résolvaient un nom de plugin, répartis sur trois chemins :
| Où | Sur quoi ça matchait |
|---|---|
hermes_cli/plugins_cmd.py _resolve_plugin_key_and_source, avec des copies à _find_plugin_entry, _require_installed_plugin et _user_installed_plugin_dir |
nom de manifeste / clé canonique / feuille unique / nom de dossier — chacun avec un sous-ensemble pris en charge différent |
plugins_cmd_install.py _catalog_install_on_disk |
le nom de catalogue, via l’enregistrement d’installation — au moment de l’installation seulement |
Desktop plugin-packages.ts vs electron/desktop-plugins-root.ts |
l’un joignait sur le nom de manifeste, l’autre sur le basename du dossier |
« Chacun avec un sous-ensemble pris en charge différent » est la partie qui faisait vraiment mal. Le bug n’était pas qu’un nom ne marchait pas ; c’était que deux commandes consécutives de la même séquence ne s’accordaient pas sur les règles, ce qui explique comment on finit avec un install réussi suivi d’un enable qui échoue.
Maintenant : un résolveur, un interrupteur
Après le changement :
_find_plugin_entryest le résolveur unique, utilisé par chaque verbe (enable, disable, show, capabilities, remove, update, et à la fois le dashboard et le hub Desktop). Il essaie d’abord la clé canonique ou le nom de manifeste, puis une feuille nue unique (langfuse→observability/langfuse), puis le nom de catalogue sous lequel le plugin a été installé. Il fait aussi une promesse : il ne devine jamais entre deux candidats — une ambiguïté échoue au lieu de choisir.- La correspondance par nom de catalogue lit le propre enregistrement de l’installateur (
.install-metadata.json), jamais un fichier à l’intérieur du dépôt du plugin. C’est important : un dépôt installé par URL ne peut pas revendiquer le nom d’une entrée de catalogue avec quelque chose qu’il a livré lui-même. - Un interrupteur active tout un paquet. « Enable agent plugin after install » dans la boîte de dialogue d’installation (ou l’interrupteur Agent de la ligne) active désormais aussi la moitié desktop — sauf si vous avez désactivé cette moitié vous-même. Ça ne marche pas dans l’autre sens : la désactivation reste par moitié, parce que la moitié desktop est partagée par tous les profils.
En ligne de commande, les deux noms se résolvent désormais :
# le mismatch classique : l'entrée touchdesigner s'installe en td
hermes plugins install touchdesigner
hermes plugins enable touchdesigner # identique à : hermes plugins enable td
# activez l'interrupteur au moment de l'installation et sautez la deuxième étape
hermes plugins install touchdesigner --enable
Avant, seul hermes plugins install <name> prenait le nom de catalogue et les autres verbes échouaient ; maintenant enable / disable / show / capabilities / update / remove acceptent l’un ou l’autre. Notre référence de commande hermes plugins est la liste des flags, et l’article sur le catalogue de plugins couvre le modèle de confiance derrière les entrées.
Aussi corrigé : « J’ai modifié le code et rien n’a changé »
Le même lot inclut la PR #135017, qui résout la même classe de problème du point de vue d’un développeur : vous modifiez desktop/plugin.js et le Desktop continue d’exécuter une copie.
Electron copie plugins/<name>/desktop/ vers desktop-plugins/<name>/, et le renderer charge cette copie. Il ne recopie que lorsque le mtime du plugin.js source est plus récent et que quelque chose le demande explicitement ; le watcher de hot-reload surveille la copie, jamais la source. Pire : le loader Python suit un symlink à l’intérieur de plugins/, mais le code de réconciliation listait les paquets avec isDirectory() — et un symlink n’est pas un répertoire — donc la configuration de développement la plus naturelle, un checkout symbolique, ne se voyait jamais copier sa moitié desktop.
Ce qu’il fait maintenant :
# traitez ceci comme le mode dev
ln -s ~/src/my-plugin ~/.hermes/plugins/my-plugin
# copie installée modifiée sur place ? laissez doctor vous dire si vous exécutez une copie obsolète
hermes plugins doctor my-plugin
Les paquets symboliques sont reconnus (le marqueur porte linked: true), la copie se resynchronise sur n’importe quelle différence d’octet plutôt que sur un verrou de mtime (donc un git stash pop fonctionne), et le fichier source est surveillé pour qu’une sauvegarde recharge à chaud le plugin. Les paquets installés normalement conservent la règle du mtime. hermes plugins doctor <id> dit maintenant clairement que le Desktop exécute une copie obsolète, la nomme, et vous indique comment la rafraîchir (rescan ou redémarrage).
Statut : sur main, dans aucun tag pour l’instant
Pour être clair : #135030, #135017 et #135033 ont toutes été fusionnées dans main le 8 octobre et ne sont pas dans le tag v0.21.6 — le tag a été découpé avant elles. Une installation git qui suit main a le nouveau résolveur dès aujourd’hui ; tout ce qui tourne sur le tag (images Docker comprises) ne l’a pas encore. Le test est la commande ci-dessus : si hermes plugins enable touchdesigner passe, vous avez le nouveau code ; si la réponse est No plugin named, vous avez l’ancien code.
Pour conclure
- Les symptômes étaient « s’installe bien, refuse d’être géré » et « la moitié desktop ne s’allume jamais » ; la cause était cinq résolveurs avec des règles différentes.
- Il y a un seul résolveur maintenant,
_find_plugin_entry, avec l’ordre clé/nom de manifeste → feuille unique → nom de catalogue issu de l’enregistrement d’installation, et il échoue en cas d’ambiguïté au lieu de deviner. - Les noms de catalogue se résolvent via le propre enregistrement de l’installateur, donc un dépôt étranger ne peut pas usurper une entrée de catalogue.
- Un interrupteur par paquet ; la désactivation reste par moitié, parce que la moitié desktop est partagée entre les profils.
- Le développement de plugins desktop via un symlink fonctionne maintenant, et
hermes plugins doctorpointe une copie obsolète.
Si vos plugins se sont installés proprement et que vous ne construisez pas de plugins desktop, ce changement est « un comportement plus prévisible », pas une migration. Pour aller plus loin, lisez l’isolation des plugins dans un processus hôte et le point de contrôle de confiance MCP pour les paquets portables — ces deux-là portent sur où le code s’exécute et ce qu’il peut toucher ; celui-ci portait sur son nom.