CLI global da plataforma ETUS. Um binário só, etus, gera os comandos de todos os apps a partir das ferramentas MCP que o hub libera para você: um app novo com ferramentas MCP vira etus <app> <comando> sem nenhuma mudança aqui.
Para agentes de código (Claude Code, Codex) é a interface mais econômica: o agente só lê --help quando precisa e usa --json para receber a resposta compacta.
Contrato completo: docs/specs/cli.md no repositório do hub (etusdigital/nexus-platforms).
curl -fsSL https://apps.etus.io/cli/install.sh | sh
etus --versionO instalador escolhe o binário da sua máquina (macOS Apple Silicon ou Intel, Linux x86_64 ou aarch64), confere o SHA256SUMS e coloca o etus no PATH.
Outras formas:
# Download direto da release (troque pelo nome da sua máquina)
base=https://github.com/evolution-foundation/etus-cli/releases/latest/download
curl -fsSLO "$base/etus-aarch64-macos" && curl -fsSLO "$base/SHA256SUMS"
shasum -a 256 -c SHA256SUMS --ignore-missing
chmod +x etus-aarch64-macos && sudo mv etus-aarch64-macos /usr/local/bin/etus
# etus-aarch64-macos | etus-x86_64-macos | etus-x86_64-linux | etus-aarch64-linux
# Com o GitHub CLI
gh release download --repo evolution-foundation/etus-cli --pattern etus-aarch64-macos --output etus
chmod +x etus && sudo mv etus /usr/local/bin/etusOu compile (Rust 1.89 ou mais novo):
cargo install --git https://github.com/evolution-foundation/etus-cli --lockedDepois, etus atualizar-cli baixa a versão mais nova sozinho, sem token: lê VERSION, o binário e o SHA256SUMS de https://github.com/evolution-foundation/etus-cli/releases/latest/download/ e só troca o binário se o sha256 conferir. ETUS_CLI_DOWNLOAD_BASE aponta para outro endereço (um espelho, por exemplo). Se os arquivos públicos não existirem, usa GH_TOKEN, GITHUB_TOKEN ou gh auth token na API do GitHub, como reserva para um repositório privado.
etus login # abre o navegador; você entra no hub e clica em Permitir
etus whoami # quem está conectado e a empresa ativa
etus apps # apps que você pode usaretus login --no-browsersó mostra o endereço. Se o navegador estiver em outra máquina, cole no terminal o endereço da página de retorno (http://127.0.0.1:<porta>/callback?...).etus login --somente-leiturapede o escopomcp:read: nenhum comando que altera dados aparece.- Precisa da permissão "Conectar agentes externos" no hub, na empresa ativa.
etus logoutrevoga a sessão no hub e apaga o token desta máquina.
No macOS e no Linux o token fica em um arquivo com permissão 0600 na pasta de configuração (tokens-<hash>.json; etus config mostra o caminho), como fazem o gh, o gcloud e o aws. Nada de token aparece em log ou saída, exceto em etus token.
- Por que não o chaveiro por padrão: o binário é assinado ad hoc, então cada versão nova tem outra identidade e o macOS volta a pedir permissão para ler o item
etus-cli. Num agente de código rodandoetussem terminal, essa janela trava o comando. - Quem vem da 0.2.0: o primeiro comando rodado num terminal lê o token do chaveiro uma vez (pode aparecer uma última janela), grava no arquivo e apaga o item do chaveiro. Sem terminal, o etus não tenta ler o chaveiro: sai com código 3 pedindo
etus login. Se a leitura falhar ou for negada, também pedeetus login. - Usar o chaveiro mesmo assim:
etus config armazenamento chaveiro(move o token para lá) ouETUS_TOKEN_STORE=keychainna sessão. Para voltar:etus config armazenamento arquivo. A variável vale mais que a configuração. - Chaveiro sem terminal: com o chaveiro escolhido no macOS, qualquer comando sem terminal (stdin ou stdout que não é terminal, como em agentes e em
$(...)) sai na hora com código 3 em vez de esperar a janela. Para scripts e agentes, use o arquivo. - Windows: continua no Gerenciador de Credenciais por padrão, que não abre janela;
etus config armazenamento arquivotroca para arquivo. - Linux: não há chaveiro suportado; o token fica sempre no arquivo.
MCP_TOKEN=$(etus token) # access token válido, renovado se estiver perto de expirar
etus token --json # {"accessToken": "...", "expiresAt": 1760000000}O token é um segredo: dá acesso à sua conta enquanto valer. Não cole em chat, log, issue nem arquivo versionado. Para não ficar visível na tela, etus token se recusa a imprimir quando a saída é um terminal (código 2); --force imprime mesmo assim. Útil para suítes de avaliação e scripts que chamam o gateway MCP direto.
etus tarefas --help # comandos do app
etus tarefas listar-tarefas --help # descrição completa e parâmetros
etus tarefas listar-tarefas --view today --today 2026-10-14
etus tarefas criar-tarefa --title "Revisar contrato" --due-date 2026-10-20 --yes
etus reunioes buscar-reunioes --q "preço" --json
etus mural --help
etus empresa listar
etus empresa usar etus # id, slug ou nome
etus skills buscar "planilha de custos"
etus skills ler <id>Nomes, pt-BR primeiro:
| App | Nome | Também aceita |
|---|---|---|
| Tarefas | tarefas |
tasks |
| Reuniões | reunioes |
records, meetings, gravador |
| Mural de Reconhecimento | mural |
muraldereconheci, mural-de-reconhecimento |
| Etus Design | design |
etus-design |
| Etus Work | work |
etus-work |
| Extensões de IA | extensoes |
extensions, ext |
| Rotinas | rotinas |
routines |
| ETUS Platforms | hub |
plataforma, platforms |
Um app novo ganha o nome do próprio app no hub, sem acento e até o primeiro "de" (Agenda de Salas vira agenda); o prefixo MCP vira alias. Os comandos seguem o título da ferramenta quando ele é em pt-BR (Criar tarefa vira criar-tarefa); senão, o nome da ferramenta (tasks__create_task vira create-task). A outra forma sempre funciona: etus tasks create-task e etus tarefas criar-tarefa chamam a mesma ferramenta. etus apps mostra os nomes e os aliases, e etus <app> <comando> --help mostra as outras formas aceitas.
Argumentos:
- flags geradas do JSON Schema:
--project-id x, números,--doneou--done false, listas repetindo a flag (--tag a --tag b) ou em JSON (--tag '["a","b"]'), objetos em JSON,nullpara apagar campos que aceitam nulo; - ou o objeto inteiro:
--json '{"title":"x"}',--input args.json,--input -(stdin). Flags somam e têm prioridade sobre o objeto; - obrigatórios e valores permitidos são conferidos antes de chamar o hub.
Ferramentas que alteram dados pedem confirmação no terminal ou --yes (sem terminal, sem --yes, o comando é recusado com código 2). --dry-run mostra a chamada sem executar.
etus setup # regras, plugins e MCP etus em cada editor do projeto (veja abaixo)
etus ativar radar # só os apps que importam neste projeto, com o guia de cada um (veja abaixo)
etus skills sincronizar # uma skill por app que você usa, na pasta do agente (veja abaixo)
etus --help # apps disponíveis (do cache)
etus <app> --help # comandos do app, os que alteram marcados com [altera]
etus <app> <comando> --help # parâmetros, exemplo e se pede --yes
etus <app> <comando> ... --json # resposta em JSON compactoCom --json, a saída é o structuredContent da ferramenta ou o texto dela já decodificado; erros vão para o stderr como {"error":{"kind","message","exitCode"}}.
| Código | Significado |
|---|---|
| 0 | ok |
| 1 | a ferramenta falhou, ou falha de rede ou do hub |
| 2 | uso errado: flag inválida, obrigatório faltando, escrita sem --yes, cancelado |
| 3 | precisa entrar: rode etus login |
| 4 | sem permissão: ferramenta não liberada, conexão só de leitura, sem "Conectar agentes externos" |
| 5 | ação necessária: a ferramenta pediu que você faça algo numa tela do hub (autorizar, conectar, entrar, pedir acesso); o link está no stderr e no JSON do stdout |
Alguns passos precisam de um clique seu por definição: autorizar um app a agir em seu nome, conectar uma conta (Google, GitHub, Slack), entrar, pedir uma permissão a um admin. Nesses casos a ferramenta não falha: devolve um resultado normal com este structuredContent (contrato da plataforma, docs/specs/action-required.md no hub):
{
"status": "action_required",
"action": {
"kind": "consent | connect | login | permission",
"title": "Conectar o Google",
"message": "Conecte sua conta Google para ler a agenda.",
"url": "https://apps.etus.io/...",
"missing": ["google"],
"expiresAt": "2026-10-14T18:00:00Z"
}
}O etus também reconhece o mesmo objeto quando ele vem só como JSON no texto da ferramenta. Ao receber:
- stdout fica igual a qualquer outra resposta: o resultado da ferramenta (com
--json, o objeto acima compacto), para o agente ler o link; - stderr mostra o título, a mensagem, o que falta e o link (
Abra: https://...); com--json, o erro traz tambémaction:{"error":{"kind":"action_required","message","exitCode":5,"action":{...}}}; - código de saída 5, para scripts e agentes detectarem sem ler o texto.
O etus nunca faz a ação por você: ele só mostra o link (e abre o navegador, se você pedir). Só abre links https (ou http em localhost).
etus <app> <comando> --open # abre o link no navegador (também --abrir)
etus <app> <comando> --wait # mostra o link, espera você concluir e repete a chamada (até 300 s)
etus <app> <comando> --wait=600 --open
etus config abrir-links sim # abre sozinho quando há terminal (nao para desligar)--openabre sempre, com ou sem terminal. Sem a flag, o link só abre sozinho cometus config abrir-links sim(open_actionsno arquivo) ouETUS_OPEN_ACTIONS=1, e ainda assim só quando stdin e stderr são um terminal; agentes rodando sem terminal nunca abrem o navegador sem--open.--wait[=SEGUNDOS](também--aguardar) repete a mesma chamada, com os mesmos argumentos e a mesma confirmação, a primeira vez depois de 2 s e depois a cada 5 s, até a resposta não ser maisaction_requiredou o tempo acabar (padrão 300 s, no máximo 1800). Concluiu: imprime o resultado final e sai com o código dele. O tempo acabou: imprime o último resultado e sai com 5. Num agente, só use--waitquando a pessoa estiver por perto para clicar.- As flags valem antes do app (
etus --open tarefas ...) ou depois do comando. Por issoopen,abrir,waiteaguardarpassam a ser nomes reservados: um parâmetro de ferramenta com esse nome vira--arg-open, como já acontecia comjsoneyes.
etus skills sincronizar (também etus skills sync) gera uma skill por app que você pode usar na empresa ativa, para o agente saber de cara o que cada app faz e quais comandos chamar, sem redescobrir com --help a cada conversa. A regra geral do etus (login, empresa, JSON, códigos de saída, confirmação) fica na skill escrita à mão etus-cli, do pacote etus-core do etus-skills.
etus login
etus skills sincronizar # Claude Code: ~/.claude/skills/etus-<app>/SKILL.md
etus skills sincronizar --agente codex # Codex: ~/.agents/skills/etus-<app>/SKILL.md
etus skills sincronizar --agente todos # os dois
etus skills sincronizar --projeto # pastas do repositório: .claude/skills e .agents/skills
etus skills sincronizar --pasta ./skills # uma pasta qualquer (vale mais que --agente e --projeto)
etus skills sincronizar --dry-run # mostra o que mudaria, sem gravar
etus skills sincronizar --json # {"pastas","criadas","atualizadas","removidas","iguais","ignoradas"}- Do seu catálogo, na hora. Renova o catálogo como
etus atualizar, então respeita a empresa ativa, o--perfile o login só leitura: só entram os apps e comandos liberados para você. - O que cada skill traz. Para que o app serve, como chamar, cada comando com aliases, resumo, se lê, altera ou é destrutivo, a permissão exigida e os parâmetros (obrigatórios com o tipo; opcionais pelo nome), as receitas e os cuidados que o app declara no manifesto (bloco
guide) e as regras de confirmação e de ação necessária. App com mais de 60 comandos lista só os obrigatórios. - Guia do app. Vem de
etus_detalharcomapp:<prefixo>, no hub. Num hub que ainda não serve o guia, a skill sai só das ferramentas, sem receitas nem permissões, e o etus avisa no stderr. - Nome e dono. A pasta é
etus-<app>, com o mesmo nome deetus apps.etus-clié reservado para a skill geral: nunca é gravada nem apagada aqui. Toda skill gerada tem a linha<!-- gerado por etus skills sincronizar: não edite, rode o comando de novo -->logo depois do frontmatter. - Só mexe no que gerou. Atualiza só o que mudou e remove só as pastas
etus-*com essa linha cujo app saiu da sua lista (perdeu acesso, app removido, outra empresa). UmSKILL.mdcom o mesmo nome feito à mão fica como está e aparece como ignorado. Pastas que são links simbólicos não são seguidas. - Idempotente. Sem data no conteúdo: rodar de novo sem mudança no hub não grava nada.
- Rode de novo depois de
etus empresa usar, de ganhar ou perder acesso a um app, ou quando um app publicar versão nova. Depois de gravar, abra uma conversa nova no agente para ele carregar as skills.
etus skills limpar-globais (também clean-global) apaga de ~/.claude/skills e ~/.agents/skills só as pastas etus-<app> com a linha de etus skills sincronizar; as feitas à mão e a etus-cli ficam. Aceita --dry-run e --json.
etus setup (também init) deixa o projeto pronto para os agentes da Etus em cada editor: regras da Etus, plugins dos perfis no Claude Code, o MCP etus e o .etus.json. Precisa de etus login. É o mesmo setup de npx github:evolution-foundation/etus-skills setup: quando a CLI está instalada, o etus-skills delega a ela.
etus setup # detecta o perfil; plataformas do .etus.json (ou todas, na primeira vez)
etus setup codigo,design --projeto ENG # soma perfis; o projeto do Plane vai para o .etus.json
etus setup --target cursor,codex # soma plataformas às do .etus.json (--substituir-alvos troca a lista)
etus setup design --com-templates # liga também os plugins opcionais do perfil
etus setup --mcp-modo completo # modo do MCP etus: quente (padrão), compacto ou completo
etus setup -i # pergunta perfil e plataformas
etus setup sincronizar # aplica de novo o .etus.json, sem login (também `sync`)
etus setup diagnosticar # confere tudo; sai com 1 se houver falha (também `doctor`)
etus --dry-run setup # mostra o que mudaria, sem gravarO que cada editor recebe:
Plataforma (--target) |
Regras | MCP etus |
Skills dos apps (etus ativar) |
|---|---|---|---|
Claude Code (claude-code) |
plugins do perfil em .claude/settings.json |
.mcp.json (type: http) |
.claude/skills |
Cursor (cursor) |
.cursor/rules/etus.mdc |
.cursor/mcp.json |
.agents/skills, ou .claude/skills, que o Cursor também lê |
Codex (codex) |
AGENTS.md |
.codex/config.toml (só em projeto confiável) |
.agents/skills |
Gemini CLI e Antigravity (antigravity, gemini) |
GEMINI.md |
.gemini/settings.json (httpUrl) e .agents/mcp_config.json (serverUrl) |
.agents/skills |
GitHub Copilot (copilot) |
.github/copilot-instructions.md |
.mcp.json, o mesmo do Claude Code (VS Code e Copilot CLI) |
.agents/skills, ou .claude/skills, que o Copilot também lê |
Windsurf (windsurf) |
.windsurfrules |
~/.config/devin/mcp_config.json (usuário; o legado ~/.codeium/windsurf/mcp_config.json quando é ele que existe) |
.agents/skills |
- Regras. Ficam entre
<!-- etus-skills:begin -->e<!-- etus-skills:end -->; o resto do arquivo fica como está. Arquivo existente sem os marcadores só com--force. - Plugins. Os do perfil (
.claude-plugin/profiles.jsondo etus-skills, embutido na CLI; oetus-skillspassa o dele emETUS_SKILLS_PROFILES). Só acrescenta: marketplaceetuscom origem customizada e plugin desligado (false) ficam como estão. - MCP
etus. Endereço<hub>/mcp?modo=quentepor padrão. No Claude Code que passa pelo AI Router o carregamento sob demanda de ferramentas fica desligado, e o modocompletoporia todas as ferramentas em toda sessão; oquentetraz as mais usadas mais as de catálogo (etus_buscar,etus_detalhar,etus_consultar,etus_executar). Umetusque já existe com outro endereço nunca é trocado sem--mcp-modo: o setup avisa e mantém. Os outros servidores do arquivo ficam como estão. O login é OAuth, feito no editor (o setup diz onde), e nenhum token vai para arquivo. - Claude Code:
.mcp.json, nãoclaude mcp add -s local. O arquivo é declarativo e versionável (o time tem o mesmo modo, sem segredo), funciona sem oclaudeinstalado, entra no--dry-rune nodiagnosticar, e serve também ao Copilot. Se houver umetuslocal para a pasta (~/.claude.json), que vale mais que o.mcp.json, o setup avisa como removê-lo. - Não suportado. Copilot coding agent na nuvem não aceita MCP com OAuth. O Windsurf não lê MCP do projeto, por isso o
etusvai no arquivo do usuário. O Codex só lê.codex/config.tomlde projeto confiável. - Skills globais. Se houver skills de
etus skills sincronizarem~/.claude/skillsou~/.agents/skills, o setup sugereetus skills limpar-globais: no global fica só aetus-cli, e o resto vai por projeto cometus ativar.
Um arquivo só, na raiz do repositório, versionável, lido e gravado pelo etus setup, pelo etus ativar e pelo etus-skills. Chaves que a CLI não conhece ficam como estão.
{
"project": "ENG",
"workspace": "etus",
"perfil": ["codigo"],
"targets": ["claude-code", "cursor", "codex"],
"mcp": { "modo": "quente" },
"active": {
"apps": ["etusradar"],
"packs": { "stalk": ["etusclone", "etuspersonas", "etusradar"] },
"agents": { "stalk": ["etusradar"] }
}
}| Chave | O que é | Quem grava |
|---|---|---|
project |
Chave do projeto no Plane (maiúsculas); sem ela, as regras usam o nome do repositório | setup --projeto |
workspace |
Workspace do Plane (padrão etus) |
setup --workspace |
perfil |
Lista de perfis do etus-skills; cada setup <perfil> soma |
setup |
targets |
Plataformas: claude-code, cursor, codex, antigravity, copilot, windsurf. Ausente num arquivo antigo do etus-skills com perfil: só claude-code |
setup --target |
mcp.modo |
quente, compacto ou completo; ausente vale quente |
setup --mcp-modo |
active |
Apps, pacotes e agentes ativos neste projeto | etus ativar e etus desativar |
As skills globais entram em toda sessão, de qualquer projeto. etus ativar (também activate) põe só o que importa dentro do projeto:
etus ativar radar personas # apps, pelos nomes de `etus apps`
etus ativar arbitragem # um pacote: a categoria do app no hub
etus ativar stalk # pacote fixo radar, personas e clone (quando nenhuma categoria do hub tem esse nome)
etus ativar agente:stalk # um agente do Etus Agentes e os apps das ferramentas dele
etus ativar # gera de novo tudo o que já está ativo
etus ativar --agente todos radar # .claude/skills e .agents/skills (padrão: as plataformas do .etus.json)
etus ativos [--json] # o que está ativo e por quê (também `active`)
etus desativar arbitragem # tira e apaga os arquivos gerados (também `deactivate`)- Raiz e estado. A raiz é a do repositório git (ou a pasta atual fora de um), a mesma do
etus setup. O que está ativo fica na chaveactivedo.etus.jsonda raiz (apps,packs,agents); as outras chaves do arquivo ficam como estão. O.etus.jsoné versionável: o time ativa a mesma lista. - App. Grava
.claude/skills/etus-<app>/SKILL.md(Claude Code) e.agents/skills/etus-<app>/SKILL.md(a pasta aberta do padrão SKILL.md, lida por Codex, Cursor, Copilot, Windsurf, Gemini CLI e Antigravity) conforme as plataformas do.etus.json. Cursor e Copilot também leem.claude/skills, então sem Codex, Gemini, Antigravity nem Windsurf a cópia em.agents/skillsnão é feita. Semtargetsno.etus.json: Claude Code, mais.agents/skillsse o projeto temAGENTS.mdou.agents. O conteúdo é o da mesma geração deetus skills sincronizar: comandos, permissões, e o guia do app (para que serve, receitas e cuidados) vindo deetus_detalharcomapp:<prefixo>. App sem guia publicado sai só com os comandos, e oetus ativardiz quais. - Pacote. Uma categoria de apps do hub (
arbitragem,produtividade...), ou o pacote fixostalk(radar, personas e clone) quando nenhuma categoria tem esse nome. A lista de membros é gravada no.etus.json. - Agente.
agente:<slug>(ouagent:<slug>) lê o agente pelo Extensões (extensoes__catalogo_lercomagente:agentes:<slug>), grava.claude/agents/etus-<slug>.mdcom as instruções dele e ativa os apps dos prefixos das ferramentas dele. Sem acesso, o etus mostra o link da ação necessária (código 5) ou o motivo (código 4). - Arquivos gerados fora do git. Cada arquivo gerado entra no
.git/info/exclude, como oetus trabalhofaz com osettings.local.json: o conteúdo depende da empresa ativa e do acesso de cada pessoa, então cada um gera o seu a partir do.etus.json. Arquivo que o repositório já versiona fica versionado. - Só mexe no que gerou. Os arquivos levam a linha
<!-- gerado por etus ativar: não edite, rode o comando de novo -->. Uma skill deetus skills sincronizar --projetocom o mesmo nome é atualizada, mas só as deetus ativarsão apagadas; as feitas à mão aparecem como ignoradas. - Dica. Se houver skills globais de
etus skills sincronizar, o fim doetus ativarsugereetus skills limpar-globais.
etus trabalho marca em qual item do Plane você está trabalhando, para o uso de IA ser medido por empresa, projeto e item no painel do roteador. Contrato completo: docs/specs/work-context.md no repositório do hub.
etus trabalho ENG-123 # marca o item; empresa = empresa ativa do etus
etus trabalho ENG-123 --fase review # plan, impl, review ou qa
etus trabalho ENG-123 --projeto hub:<id> # projeto da Etus: hub:<id>, chat:<id> ou rotina:<id>
etus trabalho ENG-123 --empresa <id> # outra empresa (id da empresa no hub)
etus trabalho --etiqueta estudo-cache # trabalho fora do Plane
etus trabalho --fase qa # só troca a fase do que já está marcado
etus trabalho # o mesmo que `etus trabalho mostrar`
etus trabalho mostrar # estado e a linha do header que vai sair
etus trabalho cabecalho # só a linha x-etus-work, para hooks e scripts
etus trabalho limpar # apaga a marca (volta ao que o branch disser)- Persiste até mudar. Dentro de um repositório, o estado fica em
<git dir>/etus-work(um por worktree); fora, em~/.claude/etus-work(ou$CLAUDE_CONFIG_DIR/etus-work). O formato é o mesmo do utilitário do instalador do roteador (issue,phase,tag,workspace), com as chaves novascompanyeproject. - Empresa automática. Vem da empresa ativa guardada no cache do catálogo (
etus empresa usar), sem chamar a rede. Sem login nem--empresa, fica vazia. - Item pelo branch. Sem item marcado, vale a chave escrita no branch (
feat/eng-123-uploadviraENG-123), como no hook. - Versão do header.
v=2só quando há empresa ou projeto (c,pjevia); senãov=1, que todo leitor antigo entende. A linha tem no máximo 256 caracteres: se passar, cortam-se a etiqueta, o repositório e o workspace, nunca empresa, projeto ou item. --jsonemmostrartrazversion,item,planeProject,project,company,tag,repo,workspace,phase,source,headerestatePath.
- O Claude Code manda em toda chamada os headers de
ANTHROPIC_CUSTOM_HEADERS(um por linha,Nome: valor), lidos doenvdos arquivos de configuração. - Com o comando
etus router setup, a CLI configura o Claude Code nativamente, registrando hooks emSessionStart,CwdChanged,FileChanged(no.git/HEAD) eUserPromptSubmit. A cada evento, o hook lê o arquivo de estado e reescreveANTHROPIC_CUSTOM_HEADERSem.claude/settings.local.jsondo projeto com a linhax-etus-work, mantendo os outros headers que você já tinha. - Além disso, comandos como
etus trabalho <item>atualizam imediatamente o.claude/settings.local.jsonda pasta atual (garantindo também que o arquivo seja ignorado no.git/info/exclude), de modo que as alterações passem a valer a partir do próximo prompt sem reiniciar o Claude Code. O roteador remove o header antes de chamar o provedor.
etus login # uma vez por computador
etus router setup # pega a chave deste computador pelo login, sem colar nada
etus router setup --model dev-continuo # define modelo principal e rápido
etus router setup <SUA_CHAVE> # plano B: informa a chave à mão (também OMNIROUTE_API_KEY)
etus router status # inspeciona a configuração atual e a origem da chave
etus router # o mesmo que etus router status
etus router chave # imprime a chave (é um segredo; quem chama é o Claude Code)- Uma chave por computador. Sem
--chave, oetus router setuptroca a sessão doetus loginpor um token de repasse do hub (60 s, só para o approuter-panel) e pede ao AI Router uma chave para este computador, com o nome da máquina (hostname). A chave aparece no painel do roteador, em Minha chave, e pode ser revogada lá sem afetar os outros computadores. O uso, o orçamento e os limites continuam os da sua chave pessoal: o roteador troca a chave do computador pela sua. - Rodar de novo troca a chave. Cada
etus router setuppede uma chave nova e revoga a anterior deste computador (sessões abertas do Claude Code pegam a nova em até 5 minutos). - Sem texto puro. A chave fica no mesmo lugar do token do login (arquivo 0600 por padrão, ou o chaveiro com
etus config armazenamento chaveiro). O~/.claude/settings.jsonrecebe"apiKeyHelper": "etus router chave"e perde oANTHROPIC_AUTH_TOKEN(e oANTHROPIC_API_KEY). Uma chave antiga em texto puro nosettings.jsonvai para o cofre quando não há login. - Como o Claude Code lê. O Claude Code roda o
apiKeyHelperao abrir e a cada 5 minutos (padrão doCLAUDE_CODE_API_KEY_HELPER_TTL_MS) e manda o valor emAuthorization: Bearere emx-api-keypara oANTHROPIC_BASE_URL.ANTHROPIC_AUTH_TOKENouANTHROPIC_API_KEYexportados no shell têm prioridade sobre oapiKeyHelper; oetus router statusavisa quando isso acontece. Com o chaveiro no macOS, oapiKeyHelpernão roda num terminal e falha: use o arquivo. etus router chaveimprime um segredo. Recusa quando a saída é um terminal (--forceimprime mesmo assim). Não cole a chave em chat, log ou arquivo versionado.- Sem acesso. Sem a permissão "Usar a IA" do AI Router, o comando mostra o link para pedir acesso no hub e sai com código 5; depois de aprovado, rode de novo.
etus logoutrevoga a chave deste computador no roteador e apaga a do cofre (uma chave informada à mão só é apagada). O Claude Code volta a usar o roteador depois deetus logineetus router setup. Também desliga a saída pela casa.
Para quem o admin escolheu no painel do AI Router, as chamadas à Anthropic da conta Claude dedicada a essa pessoa saem pelo IP de casa dela. O computador abre uma conexão de saída até o roteador e só repassa bytes TLS entre o roteador e a Anthropic: nunca vê o token da conta.
etus router setup --saida-casa # setup normal e liga a saída pela casa neste computador
etus router saida-casa ligar # liga num computador já configurado
etus router saida-casa status # agente, estado da conexão e IP de saída visto pela Anthropic
etus router saida-casa desligar # para o agente e apaga o arquivo dele- Só com a chave pelo login. A conexão usa a chave deste computador (
sk-etd-...). Com--chaveouOMNIROUTE_API_KEYo comando recusa: rodeetus logineetus router setup --saida-casa. Com o chaveiro no macOS também recusa, porque o agente roda sem terminal: useetus config armazenamento arquivo. - Um agente do sistema, não um hook. No macOS, um LaunchAgent do usuário (
~/Library/LaunchAgents/io.etus.saida-casa.plist); no Linux, um serviçosystemd --user(~/.config/systemd/user/etus-saida-casa.service). Ele fica de pé independente de quantas sessões do Claude Code estão abertas (uma conexão por computador), volta sozinho depois de queda, suspensão ou troca de rede e também atende o chat da Etus no navegador. O hookSessionStartsó rodaria quando uma sessão abre e precisaria de trava contra duplicatas. Windows ainda não tem suporte. - Lista fechada. Só
api.anthropic.com,console.anthropic.com,platform.claude.comeclaude.ai, na porta 443. Qualquer outro destino é recusado sem consultar o DNS. - O admin também liga. Enquanto o admin não liga a saída pela casa para você no painel, o status mostra "aguardando o admin" e o agente tenta de novo a cada 5 minutos.
- Estado e log.
saida-casa.jsonesaida-casa.log(até 256 KB, depoissaida-casa.log.1) na pasta de configuração do etus (~/Library/Application Support/etusno macOS,~/.config/etusno Linux, ouETUS_CONFIG_DIR). Nenhum dos dois guarda a chave. - Para testar.
etus router tunelroda o túnel em primeiro plano;ETUS_HOME_EGRESS_URLtroca o endereço do túnel (por exemplows://127.0.0.1:20140/etus/home-egress).ETUS_SAIDA_CASA_AGENT_DIRtroca a pasta do agente eETUS_SAIDA_CASA_NO_SERVICE=1não chamalaunchctlnemsystemctl.
Nesta CLI, etus work ... faz o dispatch para o app Etus Work; por isso o rastreamento de tarefas usa o comando canônico etus trabalho.
| O quê | Como |
|---|---|
| Hub | --hub URL, ETUS_HUB_URL ou hub_url no arquivo (padrão https://apps.etus.io) |
| Perfil | --perfil ID ou etus perfil usar ID / etus perfil limpar; vai ao gateway como ?perfil=ID |
| Cache do catálogo | 5 minutos (cache_ttl_secs no arquivo); etus atualizar renova na hora |
| Arquivo | config.json na pasta de configuração do usuário (etus config mostra o caminho) |
| Pastas | ETUS_CONFIG_DIR troca a pasta inteira |
| Links de ação | etus config abrir-links sim ou nao (open_actions no arquivo), ou ETUS_OPEN_ACTIONS=1 ou 0, que vale mais; padrão não abrir sozinho |
| Token | etus config armazenamento arquivo ou chaveiro (token_store no arquivo), ou ETUS_TOKEN_STORE=file ou keychain, que vale mais; padrão arquivo no macOS e no Linux, Gerenciador de Credenciais no Windows |
Autocompletar: etus completions zsh > ~/.zfunc/_etus (também bash, fish, powershell, elvish).
cargo test # unitários e integração com um gateway MCP falso
cargo clippy --all-targets -- -D warnings
cargo fmt --check
cargo test keychain -- --ignored # testa o chaveiro do sistema de verdadeA correção certa para quem quer o chaveiro, e para distribuir o binário, é assinar com um certificado Developer ID (e notarizar). Com uma identidade de código estável entre versões, o macOS reconhece o etus atualizado como o mesmo programa e não pede permissão de novo a cada atualização. Hoje o binário é assinado ad hoc (codesign -dv mostra Signature=adhoc), por isso o arquivo é o padrão. Fica como follow up: assinar e notarizar no fluxo de release e, depois disso, reavaliar o chaveiro como padrão no macOS.
Sem CI: os binários saem de um Mac com Docker (buildx) e rustup.
-
Suba a versão no
Cargo.toml(e noCargo.lock) e escreva a seção dela noCHANGELOG.md. -
scripts/release.sh X.Y.Zgera emdist/vX.Y.Z/:etus-aarch64-macoseetus-x86_64-macos, nativos, assinados ad hoc (codesign -s -);etus-x86_64-linuxeetus-aarch64-linux, estáticos (musl), comdocker buildx build --platform linux/amd64|linux/arm64escripts/release.Dockerfile;SHA256SUMS,VERSIONe um<binário>.sha256por binário.
No fim, confere o sha256, a assinatura, que os de Linux são estáticos e que cada binário responde
etus X.Y.Za--version(os de Linux comdocker run --platform, o Intel com Rosetta). -
Depois do merge na
main,scripts/release.sh X.Y.Z --publish(com amainatualizada e limpa) gera tudo de novo e cria a releasevX.Y.Zcom esses arquivos e as notas doCHANGELOG.md. Oetus atualizar-clie o instalador procuram exatamente esses nomes.