Pular para o conteúdo principal

Documentação oficial desta página · v0.20.6

Plugins de Ambiente de Terminal

O Hermes roda comandos de shell através de um conjunto plugável de backends de terminal. Os backends embutidos (local, Docker, Singularity, Modal, Daytona, Vercel Sandbox, SSH) ficam no repositório principal, em tools/environments/. Fornecedores terceiros de sandbox integram como plugins — um repositório de plugin independente instalado em ~/.hermes/plugins/, que registra um backend selecionado pelo usuário exatamente como um embutido, via terminal.backend no config.yaml.

Esta página espelha o guia de Plugins de Navegador — mesmo fluxo de registro, mesma semântica de escopo.

O que um provedor controla​

Um backend registrado participa automaticamente de todas as superfícies do núcleo:

SuperfícieMovida por
Despacho de comandos (terminal, execute_code, ferramentas de arquivo)create_environment()
Seletor de backend do hermes setupdisplay_name, description, setup_instructions(), post_setup()
Seletor de backend de terminal do painel (status da sondagem)probe()
hermes status / hermes doctordoctor_checks()
Dicas de ambiente no prompt de sistemais_remote, env_description
Pular aprovação de comando perigososkip_container_guards
Tratamento de caminho/cwd em contêineris_container
Tradução de caminho de arquivo de cache sincronizadocache_path_base
Remoção de segredos dos subprocessos criadosstrip_env_keys
Isolamento de sandbox por sessão (container_persistent: false)session_isolated_when_nonpersistent

Declarar essas flags no provedor elimina a clássica classe de bug "o novo backend esqueceu o ponto de classificação N" — o núcleo consulta o registro em cada ponto, em vez de uma lista fixa de nomes.

Provedor mínimo​

~/.hermes/plugins/acmebox/__init__.py
from agent.terminal_env_provider import TerminalEnvironmentProvider


class AcmeBoxEnvironment:
"""Precisa satisfazer o contrato duck-typed de BaseEnvironment."""

def __init__(self, cwd, timeout, task_id):
self.cwd, self.timeout, self.task_id = cwd, timeout, task_id

def execute(self, command, timeout=None, **kwargs):
... # roda o comando no sandbox
return {"output": "...", "exit_code": 0}

def cleanup(self):
... # desliga / desconecta


class AcmeBoxProvider(TerminalEnvironmentProvider):
name = "acmebox"
display_name = "AcmeBox"
is_remote = True # os comandos não rodam na máquina local
is_container = True # semântica de caminho/cwd no estilo contêiner

@property
def description(self):
return "Rode comandos em um sandbox na nuvem do AcmeBox."

@property
def cache_path_base(self):
return "~/.hermes" # onde caem os arquivos de cache sincronizados, ou None

@property
def strip_env_keys(self):
return frozenset({"ACMEBOX_TOKEN"})

def is_available(self):
import importlib.util, os
return (
importlib.util.find_spec("acmebox") is not None
and bool(os.getenv("ACMEBOX_TOKEN"))
)

def create_environment(self, *, cwd, timeout, task_id="default",
image=None, container_config=None, **kwargs):
return AcmeBoxEnvironment(cwd, timeout, task_id)


def register(ctx):
ctx.register_terminal_environment_provider(AcmeBoxProvider())
~/.hermes/plugins/acmebox/plugin.yaml
name: acmebox
version: 0.1.0
description: Backend de terminal do sandbox na nuvem AcmeBox
kind: backend

Ative, selecione e rode:

hermes plugins enable acmebox
hermes config set terminal.backend acmebox

Regras​

  • Nomes reservados. Registros que colidem com o nome de um backend embutido (local, docker, singularity, modal, managed_modal, daytona, vercel_sandbox, ssh) são rejeitados. Plugins estendem o conjunto de backends; nunca substituem os backends que estão na árvore do projeto.
  • create_environment precisa aceitar **kwargs e ignorar chaves desconhecidas — é o contrato de compatibilidade futura que permite a assinatura da fábrica evoluir sem quebrar plugins antigos.
  • is_available() / probe() precisam ser baratos. Nada de chamadas de rede — eles rodam durante a checagem de requisitos e na pintura da interface.
  • Falhe de forma suave em todo lugar. Um atributo do provedor que levanta exceção é tratado pelo núcleo com o valor padrão (por exemplo, um skip_container_guards que levanta exceção mantém a camada de aprovação LIGADA). Não use exceções como fluxo de controle.
  • Segredos vão em strip_env_keys. O token do seu fornecedor nunca pode ser legível por um comando de shell escrito pelo modelo; listá-lo o remove de todo subprocesso criado, incondicionalmente, como acontece com o tratamento embutido de MODAL_* / DAYTONA_API_KEY.

Contrato do objeto de ambiente​

create_environment() devolve um objeto que satisfaz a mesma interface duck-typed de tools.environments.base.BaseEnvironment:

  • execute(command, timeout=None, ...) → {"output": str, "exit_code": int}
  • cleanup() — libera recursos; chamado no encerramento da sessão / na coleta por ociosidade
  • Opcional: hooks de persistência espelhando os backends de nuvem embutidos

Herdar de BaseEnvironment é recomendado (você ganha de graça o encanamento compartilhado de sincronia de arquivos e de processos em segundo plano), mas não é obrigatório.

Semântica de isolamento de sessão​

Se o seu sandbox é retomado pelo nome (uma VM durável à qual o backend se reconecta), defina session_isolated_when_nonpersistent = True. Com terminal.container_persistent: false, cada sessão passa a ter a sua própria identidade de sandbox em vez de compartilhar uma — sem isso, duas execuções efêmeras independentes poderiam se conectar à mesma VM viva e apagá-la uma debaixo da outra.