Plugins, die nur halb funktionierten: Hermes hat die Plugin-Namensauflösung endlich vereinheitlicht

Du öffnest den Plugin-Hub von Hermes Desktop und installierst ein Plugin, das ein Desktop-Panel mitbringt. Der Agent-Schalter leuchtet auf, das Panel daneben aber nie — du gräbst nach, und die Tools sind da, die Befehle sind da, nur die UI nicht. Oder etwas Einfacheres: hermes plugins install hermes-memory-wiki meldet Erfolg, du setzt mit hermes plugins enable hermes-memory-wiki nach, und die Antwort ist No plugin named hermes-memory-wiki. Du hast das Ding gerade installiert, und es behauptet, den Namen nicht zu kennen. Du hast ihn nicht falsch erinnert — Hermes hatte drei Namensauflöser, die sich uneinig waren. PR #135030, gemergt am 8. Oktober, hat das aufgeräumt.
Das Symptom: ein Plugin, drei Namen
Beginne mit den Symptomen, denn diese drei Fehler wirken zusammenhanglos und teilen eine Ursache:
- Lässt sich sauber installieren, verweigert aber die Verwaltung. Der Name, unter dem eine Katalog-Installation landet, und der Name im eigenen Manifest des Plugins können differieren. Die Zahlen sind konkret: 17 von 484 Katalog-Einträgen installieren unter einem anderen Manifest-Namen —
hermes-memory-wiki→memory-wiki,touchdesigner→td,hermes-subscription-meter→subscription-meter.installlöst den Katalog-Namen auf, also gelingt es; aberenable,disable,show,removeundupdatelösten den Manifest-Namen oder -Key auf, also scheiterten alle an genau diesem Namen. - Ein Paket erscheint als zwei Zeilen. Klone es selbst nach
~/.hermes/plugins/hermes-subscription-meter, wie es das README des Plugins vorschlägt, und der Desktop-Hub zeigt zwei Zeilen: eine einsame Desktop-Hälfte, die „Install here“ für eine bereits installierte Agent-Hälfte anbietet, plus eine separate Agent-Zeile. Die Desktop-Seite gesellte sich über den Manifest-Namen zur Agent-Zeile und über den Ordnernamen zu ihrer eigenen Hälfte. - Der Schalter deckt nur eine Hälfte ab. Eine Desktop-Hälfte kommt opt-in, also schaltete das Umlegen des Agent-Schalters die Desktop-Hälfte nie mit ein.
Was diese drei gemeinsam haben: Der Name wurde an mehreren Stellen nach unterschiedlichen Regeln aufgelöst.
Die Ursache: fünf Auflöser, fünf Regelsätze
Vor der Änderung gab es fünf Stellen, die einen Plugin-Namen auflösten, verteilt über drei Pfade:
| Wo | Worauf abgeglichen wurde |
|---|---|
hermes_cli/plugins_cmd.py _resolve_plugin_key_and_source, mit Kopien bei _find_plugin_entry, _require_installed_plugin und _user_installed_plugin_dir |
Manifest-Name / kanonischer Key / eindeutiges Blatt / Ordnername — jeweils mit einer anderen unterstützten Teilmenge |
plugins_cmd_install.py _catalog_install_on_disk |
den Katalog-Namen, über den Installations-Record — nur zum Installationszeitpunkt |
Desktop plugin-packages.ts vs. electron/desktop-plugins-root.ts |
einer verknüpfte über den Manifest-Namen, der andere über den Basename des Ordners |
„Jeweils mit einer anderen unterstützten Teilmenge“ ist der Teil, der wirklich weh tat. Der Bug war nicht, dass ein Name nicht funktionierte; er war, dass zwei aufeinanderfolgende Befehle derselben Sequenz sich über die Regeln nicht einig waren, und so kommt ein erfolgreiches install gefolgt von einem fehlschlagenden enable zustande.
Jetzt: ein Auflöser, ein Schalter
Nach der Änderung:
_find_plugin_entryist der einzige Auflöser, verwendet von jedem Verb (enable, disable, show, capabilities, remove, update und sowohl vom Dashboard als auch vom Desktop-Hub). Er versucht zuerst den kanonischen Key oder Manifest-Namen, dann ein eindeutiges nacktes Blatt (langfuse→observability/langfuse), dann den Katalog-Namen, unter dem das Plugin installiert wurde. Er gibt außerdem ein Versprechen: Er rät nie zwischen zwei Kandidaten — Mehrdeutigkeit schlägt fehl, statt einen zu wählen.- Der Katalog-Namen-Abgleich liest den eigenen Record des Installers (
.install-metadata.json), nie eine Datei im Repository des Plugins. Das ist wichtig: Ein per URL installiertes Repo kann den Namen eines Katalog-Eintrags nicht mit etwas beanspruchen, das es selbst mitgeliefert hat. - Ein Schalter schaltet ein ganzes Paket ein. „Enable agent plugin after install“ im Installationsdialog (oder der Agent-Schalter der Zeile) schaltet jetzt auch die Desktop-Hälfte ein — es sei denn, du hast diese Hälfte selbst ausgeschaltet. Umgekehrt funktioniert es nicht: Ausschalten bleibt pro Hälfte, denn die Desktop-Hälfte ist von jedem Profil geteilt.
Auf der Kommandozeile lösen jetzt beide Namen auf:
# der klassische Mismatch: der touchdesigner-Eintrag installiert sich als td
hermes plugins install touchdesigner
hermes plugins enable touchdesigner # dasselbe wie: hermes plugins enable td
# den Schalter schon beim Installieren umlegen und den zweiten Schritt sparen
hermes plugins install touchdesigner --enable
Vorher nahm nur hermes plugins install <name> den Katalog-Namen, und die anderen Verben brachen ab; jetzt akzeptieren enable / disable / show / capabilities / update / remove beide. Unsere Kommando-Referenz zu hermes plugins ist die Flag-Liste, und der Beitrag zum Plugin-Katalog behandelt das Vertrauensmodell hinter den Einträgen.
Außerdem behoben: „Ich habe den Code bearbeitet, und nichts hat sich geändert“
Der gleiche Batch enthält PR #135017, der dieselbe Problemklasse aus Entwicklersicht löst: Du bearbeitest desktop/plugin.js, und der Desktop führt weiterhin eine Kopie aus.
Electron kopiert plugins/<name>/desktop/ nach desktop-plugins/<name>/, und der Renderer lädt diese Kopie. Er kopiert nur dann neu, wenn die mtime der Quell-plugin.js neuer ist und etwas es explizit anfordert; der Hot-Reload-Watcher beobachtet die Kopie, nie die Quelle. Schlimmer: Der Python-Loader folgt einem Symlink in plugins/, aber der Reconcile-Code listete Pakete mit isDirectory() auf — und ein Symlink ist kein Verzeichnis —, also bekam das natürlichste Entwicklungssetup, ein per Symlink verknüpfter Checkout, seine Desktop-Hälfte nie kopiert.
Was es jetzt tut:
# das hier als Dev-Modus behandeln
ln -s ~/src/my-plugin ~/.hermes/plugins/my-plugin
# installierte Kopie an Ort und Stelle bearbeitet? lass doctor dir sagen, ob du eine veraltete ausführst
hermes plugins doctor my-plugin
Per Symlink verknüpfte Pakete werden erkannt (der Marker trägt linked: true), die Kopie synchronisiert sich bei jedem Byte-Unterschied neu statt über ein mtime-Gate (sodass ein git stash pop funktioniert), und die Quelldatei wird beobachtet, sodass ein Speichern das Plugin per Hot-Reload neu lädt. Normale installierte Pakete behalten die mtime-Regel. hermes plugins doctor <id> sagt jetzt klar, dass der Desktop eine veraltete Kopie ausführt, benennt sie und sagt dir, wie du sie aktualisierst (Rescan oder Neustart).
Status: auf main, noch in keinem Tag
Um nicht missverstanden zu werden: #135030, #135017 und #135033 sind alle am 8. Oktober in main gemergt und nicht im Tag v0.21.6 — der Tag wurde vor ihnen geschnitten. Eine Git-Installation, die main trackt, hat den neuen Auflöser heute; alles, was das Tag fährt (Docker-Images eingeschlossen), noch nicht. Der Check ist der Befehl von oben: Wenn hermes plugins enable touchdesigner durchläuft, hast du den neuen Code; wenn er No plugin named sagt, den alten.
Die Kurzfassung
- Die Symptome waren „installiert sauber, verweigert aber die Verwaltung“ und „die Desktop-Hälfte leuchtet nie auf“; die Ursache waren fünf Auflöser mit unterschiedlichen Regeln.
- Es gibt jetzt einen Auflöser,
_find_plugin_entry, mit der Reihenfolge Key/Manifest-Name → eindeutiges Blatt → Katalog-Name aus dem Installations-Record, und er schlägt bei Mehrdeutigkeit fehl, statt zu raten. - Katalog-Namen lösen über den eigenen Record des Installers auf, sodass ein fremdes Repo einen Katalog-Eintrag nicht imitieren kann.
- Ein Schalter pro Paket; Ausschalten bleibt pro Hälfte, denn die Desktop-Hälfte wird über Profile hinweg geteilt.
- Die Desktop-Plugin-Entwicklung über einen Symlink funktioniert jetzt, und
hermes plugins doctorweist auf eine veraltete Kopie hin.
Wenn deine Plugins sauber installiert wurden und du keine Desktop-Plugins baust, ist diese Änderung „mehr vorhersehbares Verhalten“, keine Migration. Für mehr Tiefe lies Plugin-Isolation in einen Hostprozess und das MCP-Trust-Gate für portable Pakete — diese beiden drehen sich darum, wo der Code läuft und was er anfassen darf; dieser hier darum, wie er heißt.