Command not found no terminal do agent: como o Hermes corrigiu um PATH redefinido pelos login shells


Você pede ao agent que rode hermes cron list no terminal dele para ver por que um job agendado nunca disparou. A resposta é uma linha só: bash: hermes: command not found. Você abre a sua própria janela SSH, digita a mesma coisa e funciona. De volta à session, command -v node também volta vazio — mesmo com o node instalado via nvm.

Não é problema de permissão e o Hermes não está quebrado. Alguém redefiniu o PATH, de forma silenciosa e completa.

Quem redefiniu o PATH

Por trás da tool de terminal existe um bash. Para que as configurações de nvm, asdf e pyenv das quais você depende também funcionem para o agent, o Hermes inicia um login shell uma vez, quando cria a session, e captura o ambiente resultante — carregar ~/.profile, ~/.bash_profile e ~/.bashrc é exatamente como esses gerenciadores de versão se adicionam ao PATH. Essa parte é intencional e útil.

O problema é o /etc/profile do Debian e do Ubuntu: ele não estende o PATH, ele reescreve o PATH como uma lista fixa (normalmente /usr/local/bin:/usr/bin:/bin mais alguns diretórios de games). Quando o login shell termina, o snapshot capturado fixa esse PATH redefinido. Tudo o que o Hermes tinha na inicialização some: ~/.local/bin, /opt/hermes/bin, /opt/hermes/.venv/bin e todos os diretórios de shim do nvm.

Na imagem Docker o sintoma é o mais escandaloso: entre em um container com bash -l, ou docker exec … bash -l, e o hermes puro sai com 127. Comandos em background sofrem igual, porque rodam via bash -lic.

A correção: recolocar o que o profile descartou

A reparação não sobrescreve o que o login shell decidiu. Ela concilia os dois:

  • depois que o profile de login roda, qualquer diretório do ambiente de lançamento que tenha sumido é anexado no fim, na ordem relativa original;
  • diretórios que já estão presentes nunca são duplicados;
  • só entradas absolutas têm a existência verificada — uma entrada relativa é resolvida a partir do diretório de trabalho do shell, então checá-la a partir do Python não faria sentido;
  • diretórios ausentes são ignorados em vez de serem enfiados no PATH como peso morto.

Anexar no fim é a escolha que importa. O que o init do seu nvm ou pyenv decidiu continua exatamente onde estava, então um comando que resolvia antes continua resolvendo para o mesmo binário; só ganham correspondência os comandos que não tinham nenhuma.

Comandos em background (o spawn_local do process_registry, bash -lic) recebem o mesmo trecho de restauração na frente, então foreground e background não conseguem discordar sobre qual python ou hermes existe.

A imagem Docker adiciona uma segunda camada: o /etc/profile.d/hermes.sh é instalado na imagem, então todo login shell recoloca os três diretórios da imagem — deixando onde estão os que já aparecem na lista.

# /etc/profile.d/hermes.sh (dentro da imagem)
for __hermes_p in /opt/data/.local/bin /opt/hermes/.venv/bin /opt/hermes/bin; do
    case ":$PATH:" in
        *":$__hermes_p:"*) ;;
        *) PATH="$__hermes_p:$PATH" ;;
    esac
done
unset __hermes_p
export PATH

Confira como o PATH está de verdade

Três linhas dentro do terminal do agent contam a história inteira:

echo $PATH                 # o que o agent realmente recebe
command -v hermes          # vazio significa não encontrado
bash -lc 'echo $PATH'      # o PATH redefinido pelo login shell, para comparação

Tabela de sintoma para causa:

Sintoma Causa real O que fazer
hermes: command not found dentro do Docker O login shell redefiniu o PATH; a imagem não tem o patch de profile.d Atualize a imagem ou o build do Hermes
node / python ausentes mesmo instalados no host O /etc/profile atropelou o init do nvm/pyenv vindo do ~/.profile Corrigido automaticamente no build novo; em builds antigos, liste os arquivos de init explicitamente
Só os comandos em background falham O build antigo não compartilhava o trecho de restauração com o caminho de background Atualize, ou use comandos em foreground por enquanto
O seu próprio script não encontra uma tool O script contava com um diretório que só um shell interativo tem Use caminhos absolutos, ou adicione o arquivo em shell_init_files

Se você tem arquivos de ambiente customizados — uma toolchain interna, por exemplo — diga ao Hermes exatamente o que carregar em vez de esperar que ele adivinhe:

terminal:
  auto_source_bashrc: true      # padrão; uma lista explícita em shell_init_files assume o controle
  shell_init_files:
    - ~/.profile
    - ~/.config/hermes/env.sh

Limites que vale conhecer

  • Só são restaurados os diretórios que estavam presentes no ambiente com que o Hermes foi lançado. O que o seu processo pai nunca teve não é inventado.
  • Se você removeu de propósito um diretório do PATH dentro do seu profile, mas ele ainda estava no PATH de lançamento, ele volta. Esse é o trade-off pretendido: manter funcionando os comandos que já funcionavam.
  • O Windows não é afetado — o PATH nativo é unido por ponto e vírgula e o bash vê uma cópia convertida.
  • Ordem é precedência. Como as entradas restauradas entram no fim, os diretórios de sistema continuam antes dos seus diretórios customizados; não tente “consertar” problemas de PATH colocando um diretório gravável na frente.

Para uma checagem de ambiente mais ampla, rode hermes doctor. O comportamento de espaço de rascunho do terminal está em diretórios temporários do terminal, o layout de containers em modo container compartilhado, e a troca completa do shell em plugins de backend de terminal.

Status da release

A correção entrou na main em 29 de outubro de 2026 (PR #146080). A release atual, v0.22.5 (23 de outubro de 2026), não a inclui, então builds mais antigos ainda podem cair em command not found.

Bugs de PATH são desagradáveis justamente porque nunca levantam erro — alguns diretórios simplesmente somem. Pelo menos agora eles voltam para onde deveriam estar.