Plugins que só funcionavam pela metade: o Hermes finalmente unificou a resolução de nomes de plugins

Você abre o hub de plugins do Hermes Desktop e instala um plugin que traz um painel de desktop. O switch do Agent acende, mas o painel ao lado dele nunca acende — você vai cavar fundo e as tools estão lá, os comandos estão lá, a UI simplesmente não está. Ou algo mais simples: hermes plugins install hermes-memory-wiki diz que deu certo, você emenda com hermes plugins enable hermes-memory-wiki, e ele responde No plugin named hermes-memory-wiki. Você acabou de instalar a coisa e ela afirma não conhecer o nome. Você não lembrou errado — o Hermes tinha três resolvedores de nomes que discordavam entre si. O PR #135030, mesclado em 8 de outubro, resolveu isso.
O sintoma: um plugin, três nomes
Comece pelos sintomas, porque esses três erros parecem não ter relação e compartilham uma única causa raiz:
- Instala numa boa, mas se recusa a ser gerenciado. O nome sob o qual uma instalação de catálogo aterrissa e o nome no próprio manifest do plugin podem diferir. Os números são específicos: 17 de 484 entradas do catálogo instalam sob um nome de manifest diferente —
hermes-memory-wiki→memory-wiki,touchdesigner→td,hermes-subscription-meter→subscription-meter. Oinstallresolve o nome do catálogo, então dá certo; masenable,disable,show,removeeupdateresolviam o nome ou a chave do manifest, então cada um deles falhava nesse mesmo nome. - Um pacote aparece como duas linhas. Clone você mesmo para
~/.hermes/plugins/hermes-subscription-meter, como o próprio README do plugin sugere, e o hub do desktop mostra duas linhas: uma metade desktop solitária oferecendo “Install here” para uma metade agent que já está instalada, mais uma linha agent separada. O lado desktop se juntava à linha agent pelo nome do manifest e à sua própria metade pelo nome da pasta. - O switch cobre apenas uma metade. A metade desktop vem como opt-in, então ligar o switch do Agent nunca ligava junto a metade desktop.
O que esses três têm em comum: o nome estava sendo resolvido em vários lugares, por regras diferentes.
A causa: cinco resolvedores, cinco conjuntos de regras
Antes da mudança havia cinco lugares que resolviam um nome de plugin, espalhados por três caminhos:
| Onde | O que casava |
|---|---|
hermes_cli/plugins_cmd.py _resolve_plugin_key_and_source, com cópias em _find_plugin_entry, _require_installed_plugin e _user_installed_plugin_dir |
nome do manifest / chave canônica / leaf único / nome da pasta — cada um com um subconjunto suportado diferente |
plugins_cmd_install.py _catalog_install_on_disk |
o nome do catálogo, através do registro de instalação — apenas no momento da instalação |
Desktop plugin-packages.ts vs electron/desktop-plugins-root.ts |
um se juntava pelo nome do manifest, o outro pelo basename da pasta |
“Cada um com um subconjunto suportado diferente” é a parte que realmente doía. O bug não era que um nome não funcionava; era que dois comandos consecutivos na mesma sequência não concordavam nas regras, e é assim que você acaba com um install bem-sucedido seguido de um enable que falha.
Agora: um resolvedor, um switch
Depois da mudança:
_find_plugin_entryé o único resolvedor, usado por todos os verbos (enable, disable, show, capabilities, remove, update, e tanto o dashboard quanto o hub do Desktop). Ele tenta primeiro a chave canônica ou o nome do manifest, depois um leaf nu único (langfuse→observability/langfuse), depois o nome do catálogo sob o qual o plugin foi instalado. E faz uma promessa: nunca adivinha entre dois candidatos — a ambiguidade falha em vez de escolher um.- A correspondência pelo nome do catálogo lê o registro do próprio instalador (
.install-metadata.json), nunca um arquivo dentro do repositório do plugin. Isso importa: um repo instalado por URL não pode reivindicar o nome de uma entrada do catálogo usando algo que ele mesmo entregou. - Um switch liga um pacote inteiro. “Enable agent plugin after install” no diálogo de instalação (ou o switch Agent da linha) agora também liga a metade desktop — a menos que você mesmo tenha desligado essa metade. Não funciona ao contrário: desligar continua por metade, porque a metade desktop é compartilhada por todos os profiles.
Na linha de comando, os dois nomes agora resolvem:
# o descasamento clássico: a entrada touchdesigner instala como td
hermes plugins install touchdesigner
hermes plugins enable touchdesigner # o mesmo que: hermes plugins enable td
# ligue o switch na hora da instalação e pule o segundo passo
hermes plugins install touchdesigner --enable
Antes, só hermes plugins install <name> aceitava o nome do catálogo e os outros verbos davam erro; agora enable / disable / show / capabilities / update / remove aceitam qualquer um dos dois. A nossa referência de comandos hermes plugins é a lista de flags, e o post do catálogo de plugins cobre o modelo de confiança por trás das entradas.
Também corrigido: “Editei o código e nada mudou”
O mesmo lote inclui o PR #135017, que resolve a mesma classe de problema do ponto de vista de quem desenvolve: você edita desktop/plugin.js e o Desktop continua rodando uma cópia.
O Electron copia plugins/<name>/desktop/ para desktop-plugins/<name>/, e o renderer carrega essa cópia. Ele só recopia quando o mtime do plugin.js de origem é mais recente e algo pede explicitamente; o watcher de hot-reload observa a cópia, nunca a origem. Pior: o loader em Python segue um symlink dentro de plugins/, mas o código de reconcile listava pacotes com isDirectory() — e um symlink não é um diretório — então o setup de desenvolvimento mais natural, um checkout com symlink, nunca tinha a metade desktop copiada.
O que ele faz agora:
# trate isto como modo dev
ln -s ~/src/my-plugin ~/.hermes/plugins/my-plugin
# cópia instalada editada no lugar? deixe o doctor dizer se você está rodando uma desatualizada
hermes plugins doctor my-plugin
Pacotes com symlink são reconhecidos (o marcador carrega linked: true), a cópia ressincroniza com qualquer diferença de byte em vez de um gate por mtime (então um git stash pop funciona), e o arquivo de origem é observado, de modo que um save faz o hot-reload do plugin. Pacotes instalados normalmente mantêm a regra do mtime. hermes plugins doctor <id> agora diz claramente que o Desktop está rodando uma cópia desatualizada, nomeia-a e informa como atualizá-la (rescan ou restart).
Status: na main, ainda em nenhuma tag
Para não deixar dúvida: #135030, #135017 e #135033 foram todos mesclados na main em 8 de outubro e não estão na tag v0.21.6 — a tag foi gerada antes deles. Uma instalação via git que acompanha a main tem o novo resolvedor hoje; qualquer coisa rodando a tag (imagens Docker incluídas) ainda não tem. A verificação é o comando de cima: se hermes plugins enable touchdesigner passar, você tem o código novo; se disser No plugin named, você tem o código antigo.
Para fechar
- Os sintomas eram “instala numa boa, mas se recusa a ser gerenciado” e “a metade desktop nunca acende”; a causa eram cinco resolvedores com regras diferentes.
- Agora existe um único resolvedor,
_find_plugin_entry, com a ordem chave/nome do manifest → leaf único → nome do catálogo a partir do registro de instalação, e ele falha na ambiguidade em vez de adivinhar. - Os nomes de catálogo resolvem pelo registro do próprio instalador, então um repo estrangeiro não pode se passar por uma entrada do catálogo.
- Um switch por pacote; desligar continua sendo por metade, porque a metade desktop é compartilhada entre profiles.
- O desenvolvimento de plugins do desktop via symlink agora funciona, e o
hermes plugins doctoraponta para uma cópia desatualizada.
Se os seus plugins instalaram sem problema e você não está construindo plugins de desktop, esta mudança é “comportamento mais previsível”, não uma migração. Para ir mais fundo, leia isolamento de plugins em um processo host e o portão de confiança MCP para pacotes portáteis — esses dois tratam de onde o código roda e o que ele pode tocar; este aqui tratava de como ele é chamado.