@fazer-ai/agents 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +112 -0
  3. package/dist/agents/claude.js +152 -0
  4. package/dist/agents/codex.js +155 -0
  5. package/dist/agents/detect.js +15 -0
  6. package/dist/agents/handoff.js +22 -0
  7. package/dist/agents/hermes-skills.js +177 -0
  8. package/dist/agents/hermes.js +474 -0
  9. package/dist/agents/index.js +57 -0
  10. package/dist/agents/manual.js +22 -0
  11. package/dist/agents/other.js +39 -0
  12. package/dist/agents/shell.js +15 -0
  13. package/dist/agents/types.js +2 -0
  14. package/dist/config.js +48 -0
  15. package/dist/exec.js +279 -0
  16. package/dist/hostinger.js +75 -0
  17. package/dist/hub-command.js +144 -0
  18. package/dist/index.js +726 -0
  19. package/dist/licenses.js +93 -0
  20. package/dist/mcp.js +100 -0
  21. package/dist/oauth.js +578 -0
  22. package/dist/onboarding-marker.js +48 -0
  23. package/dist/preferences.js +40 -0
  24. package/dist/skills/agents-dev/SKILL.md +37 -0
  25. package/dist/skills/agents-dev/gotchas.md +6 -0
  26. package/dist/skills/agents-dev/guardrails.md +6 -0
  27. package/dist/skills/agents-dev/references/00-get-the-code.md +28 -0
  28. package/dist/skills/agents-dev/references/01-layout-and-bun-check.md +29 -0
  29. package/dist/skills/agents-dev/references/02-free-full-and-invariants.md +7 -0
  30. package/dist/skills/agents-dev/references/03-implement.md +9 -0
  31. package/dist/skills/agents-dev/references/04-own-image-and-deploy.md +13 -0
  32. package/dist/skills/agents-onboarding/SKILL.md +80 -0
  33. package/dist/skills/agents-onboarding/gotchas.md +157 -0
  34. package/dist/skills/agents-onboarding/guardrails.md +65 -0
  35. package/dist/skills/agents-onboarding/references/00-prereqs-and-access.md +37 -0
  36. package/dist/skills/agents-onboarding/references/01-vps-dns-ssh.md +67 -0
  37. package/dist/skills/agents-onboarding/references/01b-brownfield.md +70 -0
  38. package/dist/skills/agents-onboarding/references/01c-pick-tier.md +61 -0
  39. package/dist/skills/agents-onboarding/references/02-coolify.md +109 -0
  40. package/dist/skills/agents-onboarding/references/03-chatwoot-pro.md +61 -0
  41. package/dist/skills/agents-onboarding/references/04-agents-image.md +46 -0
  42. package/dist/skills/agents-onboarding/references/05-langfuse.md +45 -0
  43. package/dist/skills/agents-onboarding/references/06-setup-and-mcp.md +47 -0
  44. package/dist/skills/agents-onboarding/references/08-agent-import.md +55 -0
  45. package/dist/skills/agents-onboarding/references/09-chatwoot-bind.md +41 -0
  46. package/dist/skills/agents-onboarding/references/10-validate-e2e.md +34 -0
  47. package/dist/skills/agents-onboarding/references/agent-features.md +61 -0
  48. package/dist/skills/agents-onboarding/references/chatwoot-hub-register.md +69 -0
  49. package/dist/skills/agents-onboarding/references/deploy-b-portainer.md +138 -0
  50. package/dist/skills/agents-onboarding/references/deploy-c-compose.md +64 -0
  51. package/dist/skills/agents-onboarding/samples/agents/README.md +23 -0
  52. package/dist/skills/agents-onboarding/samples/agents/maria-clinica-moreira.json +313 -0
  53. package/dist/skills/agents-onboarding/scripts/chatwoot-admin.py +248 -0
  54. package/dist/skills/agents-onboarding/scripts/coolify.py +552 -0
  55. package/dist/skills/agents-onboarding/scripts/docker-status.py +129 -0
  56. package/dist/skills/agents-onboarding/scripts/gen-onboarding-env.ts +187 -0
  57. package/dist/skills/agents-onboarding/scripts/harbor-login.py +118 -0
  58. package/dist/skills/agents-onboarding/scripts/langfuse-verify.py +118 -0
  59. package/dist/skills/agents-onboarding/scripts/portainer-brownfield.py +115 -0
  60. package/dist/skills/agents-onboarding/scripts/remote.py +198 -0
  61. package/dist/skills/agents-onboarding/scripts/sshkey.py +140 -0
  62. package/dist/skills/agents-onboarding/templates/chatwoot/.env.example +30 -0
  63. package/dist/skills/agents-onboarding/templates/chatwoot/README.md +65 -0
  64. package/dist/skills/agents-onboarding/templates/chatwoot/docker-compose.coolify.yml +136 -0
  65. package/dist/skills/agents-onboarding/templates/chatwoot/docker-compose.yml +139 -0
  66. package/dist/skills/agents-onboarding/templates/docker-compose.coolify.yml +73 -0
  67. package/dist/skills/agents-onboarding/templates/docker-compose.portainer.yml +132 -0
  68. package/dist/skills/agents-onboarding/templates/docker-compose.prod.yml +85 -0
  69. package/dist/skills/agents-onboarding/templates/langfuse/.env.example +59 -0
  70. package/dist/skills/agents-onboarding/templates/langfuse/README.md +132 -0
  71. package/dist/skills/agents-onboarding/templates/langfuse/docker-compose.coolify.yml +189 -0
  72. package/dist/skills/agents-onboarding/templates/langfuse/docker-compose.yml +185 -0
  73. package/dist/skills/agents-operation/SKILL.md +42 -0
  74. package/dist/skills/agents-operation/gotchas.md +61 -0
  75. package/dist/skills/agents-operation/guardrails.md +26 -0
  76. package/dist/skills/agents-operation/references/00-production-safety.md +24 -0
  77. package/dist/skills/agents-operation/references/01-diagnose.md +34 -0
  78. package/dist/skills/agents-operation/references/02-reproduce.md +22 -0
  79. package/dist/skills/agents-operation/references/03-adjust.md +36 -0
  80. package/dist/skills/agents-operation/references/04-validate-and-apply.md +31 -0
  81. package/dist/ui-select.js +279 -0
  82. package/dist/ui.js +167 -0
  83. package/package.json +53 -0
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: agents-dev
3
+ description: "Modo desenvolvedor do fazer.ai agents: trabalhar no código-fonte. Clona o repo (Free público ou Pro via git proxy do hub), orienta nas boas práticas e invariantes (separação Free/Full, aditividade e convenções de docs/), pergunta proativamente o que o usuário quer implementar e conduz a implementação, e ajuda a gerar a própria imagem de deploy. Use quando o usuário quer DESENVOLVER, estender ou contribuir com o código do fazer.ai agents, não apenas subir (onboarding) ou operar (operação) uma instância."
4
+ ---
5
+
6
+ # Modo desenvolvedor do fazer.ai agents
7
+
8
+ Leva um desenvolvedor de "quero mexer no código" até "implementou com as boas práticas do projeto e, se quiser, gerou a própria imagem". Audiência: **desenvolvedor**, não operador. Para subir uma instância do zero use a skill `agents-onboarding`; para debugar/ajustar uma instância em produção use `agents-operation`.
9
+
10
+ ## ⚠️ Distribuição: o Pro é privado
11
+
12
+ - O **repositório Pro/Full** (`fazer-ai/agents-pro`) e a **imagem Pro** são **privados**. **Nunca** publique o código, a imagem, ou trechos exclusivos do Full em local público (gist, fork público, registry público, post, screenshot).
13
+ - O acesso ao Pro é concedido individualmente. Vazar repo/imagem quebra esse modelo.
14
+ - **Sugestões e contribuições vão para o repositório Free** (open-source): `fazer-ai/agents`. Abra issues/PRs lá.
15
+
16
+ ## Fluxo
17
+
18
+ 1. **Obter o código** (`references/00-get-the-code.md`), bifurca por edição: Free clona o repo público (sem credencial); Pro clona via git proxy do hub (credencial per-user).
19
+ 2. **Layout + porta de qualidade** (`references/01-layout-and-bun-check.md`): mapa do repo, `bun install`/`bun dev`, o ciclo `bun check`.
20
+ 3. **Aditividade + invariantes** (`references/02-free-full-and-invariants.md`): aditividade e onde estão as convenções e os docs por subsistema.
21
+ 4. **Implementação conduzida** (`references/03-implement.md`): perguntar o que implementar, desenhar desafiando premissas, implementar no estilo vizinho, validar.
22
+ 5. **Imagem própria + deploy** (`references/04-own-image-and-deploy.md`): gerar a própria imagem e plugá-la num deploy (casa com o Tier C da `agents-onboarding`).
23
+
24
+ ## Guardrails
25
+
26
+ Ver `guardrails.md` (Pro privado, segredos fora do repo, `bun check` verde) e `gotchas.md` (armadilhas de DB/RLS/CSP).
27
+
28
+ ## Skills irmãs
29
+
30
+ - `agents-onboarding`: subir uma instância nova num VPS (do zero ao agente).
31
+ - `agents-operation`: debugar/ajustar uma instância **em produção**.
32
+
33
+ ## Pendências
34
+
35
+ - Confirmar o **nome client-facing** (marca do produto) do Free; o repo é `fazer-ai/agents`.
36
+ - **Caminho Pro depende do hub:** o git proxy do código-fonte (`fazer-ai/agents-pro`) ainda será exposto; até lá, o clone Pro de `references/00-get-the-code.md` é o contrato assumido.
37
+ - **Wording de não-redistribuição (Pro):** é conteúdo público-facing; confirmar a redação com a revisão de licenciamento.
@@ -0,0 +1,6 @@
1
+ # Gotchas: modo desenvolvedor
2
+
3
+ - **`prisma migrate reset` cru apaga os grants do role runtime** → todo query falha com Postgres `42501` (`permission denied for schema public`) no próximo boot. Use `bun db:reset` (ou re-rode `bun db:bootstrap` após qualquer reset, inclusive um `migrate dev` que reseta por drift).
4
+ - **`psql` cru com a `DATABASE_URL` não enxerga linhas tenant-scoped:** a `DATABASE_URL` é o role runtime não-superuser com RLS; sem contexto de tenant, tabelas como `agents` voltam zero linhas (silencioso, parece "não existe"). Para diagnosticar, conecte como superuser (`MIGRATION_DATABASE_URL`, read-only) ou rode `SET app.tenant_id = '<id>'` na sessão antes do `SELECT`.
5
+ - **Porta do Postgres:** se 5432 já estiver em uso, escolha a próxima livre e setar `POSTGRES_PORT` no `.env`.
6
+ - **Editar script inline no `index.html`** exige `bun run build` antes de servir em produção: o hash no header de CSP precisa bater com o HTML servido, senão o browser bloqueia o script.
@@ -0,0 +1,6 @@
1
+ # Guardrails: modo desenvolvedor
2
+
3
+ - **Pro privado:** nunca publicar o repo (`fazer-ai/agents-pro`), a imagem, ou trechos exclusivos do Full em local público.
4
+ - **Segredos fora do repo:** nunca commitar `.env`, chaves ou tokens; confira `.env.example` ao adicionar env var. Nunca logar tokens/credenciais.
5
+ - **Qualidade:** `bun check` verde antes de concluir. Nunca `prisma migrate reset` cru; use `bun db:reset`.
6
+ - **Estilo:** PT-BR com acentuação; sem em-dash; `fazer.ai` minúsculo; comentários só com tag.
@@ -0,0 +1,28 @@
1
+ # 00: Obter o código (Free ou Pro)
2
+
3
+ Bifurca por edição. Pergunte ao desenvolvedor qual ele tem acesso, ou deduza: sem credencial da comunidade = Free.
4
+
5
+ ## Free (repo público)
6
+
7
+ Clone direto, sem credencial:
8
+ ```sh
9
+ git clone https://github.com/fazer-ai/agents.git
10
+ ```
11
+ (O repo Free é `fazer-ai/agents`; a marca client-facing do produto ainda pode mudar.)
12
+
13
+ ## Pro (repo privado via git proxy do hub)
14
+
15
+ O código Pro/Full (`fazer-ai/agents-pro`) é privado. O acesso é por **credencial per-user do hub** (a mesma que serve a marketplace de skills), não por convite direto no GitHub.
16
+
17
+ 1. Garanta o login no hub (`app.fazer.ai`) e a credencial git/NPM **per-user** (via o MCP `app-fazer-ai`; dry-run, depois apply com OK). Uma credencial por usuário, válida em todas as suas máquinas.
18
+ 2. Clone autenticado pelo git proxy do hub:
19
+ ```sh
20
+ git clone https://<user>:<token>@app.fazer.ai/git/<repo-do-codigo>.git
21
+ ```
22
+ 3. **Nunca** logar o token nem commitá-lo.
23
+
24
+ > **Dependência do hub (em aberto):** hoje o git proxy do hub serve a marketplace de **skills**, não o código-fonte do fazer.ai agents. O endpoint que serve `fazer-ai/agents-pro` ainda será exposto no hub; até lá, o caminho Pro não é exercitável e o path acima (`/git/<repo-do-codigo>`) é o contrato assumido, a confirmar quando o hub publicar.
25
+
26
+ ## Não-redistribuição (Pro)
27
+
28
+ Código e imagem Pro são privados e concedidos individualmente. Nunca publique repo, imagem ou trechos exclusivos do Full em local público (gist, fork público, registry público, post, screenshot).
@@ -0,0 +1,29 @@
1
+ # 01: Layout do repo + porta de qualidade
2
+
3
+ ## Mapa
4
+ - `src/api/`: backend Elysia (features/, lib/, middlewares/), a camada REST.
5
+ - `src/modules/`: **a lógica de negócio por subsistema** (agents, chatwoot, debounce, rag, stt, tts, vault, mcp, …); cada um casa com uma doc em `docs/`. É onde a maior parte do trabalho de feature acontece.
6
+ - `src/graph/`: o runtime do agente (LangGraph TS): grafo, runtime, checkpointer, tools.
7
+ - `src/lib/`: utilitários compartilhados (tenancy, crypto, etc.).
8
+ - `src/client/`: frontend React (pages/, components/, contexts/, lib/, locales/).
9
+ - `prisma/`: schema e migrations.
10
+ - `docs/`: guia por subsistema (uma doc por subsistema; leia a relevante antes de mexer).
11
+ - `scripts/`: utilitários (`set-admin.ts`, etc.).
12
+ - `public/`: assets e `index.html`.
13
+
14
+ ## Subir local
15
+ ```sh
16
+ bun install
17
+ bun dev # hot reload, porta 3000
18
+ ```
19
+ Antes de configurar `DATABASE_URL` no `.env`, cheque PostgreSQL já em uso (`ss -tlnp | grep 543`); o default é 5432, senão use a próxima porta livre via `POSTGRES_PORT`.
20
+
21
+ ## Porta de qualidade
22
+ ```sh
23
+ bun check # lint Biome + tsc + i18n + testes
24
+ ```
25
+ Rode **depois de todas as mudanças**; só conclua com `bun check` verde. Nunca rode um `prisma migrate reset` cru (apaga os grants do role runtime); use `bun db:reset`.
26
+
27
+ O `i18n:extract` (dentro do `bun check`) **escreve** chaves pt-BR novas nos locales de `src/client`: depois de um `bun check`, revise o diff dos locales e **traduza** as chaves auto-adicionadas (defaults conflitantes no `t()` fazem o extract falhar).
28
+
29
+ As convenções completas (estilo, theming, CSP, encryption, i18n, UX) estão nas instruções do projeto (carregadas automaticamente ao abrir o repo) e nos docs em `docs/`.
@@ -0,0 +1,7 @@
1
+ # 02: Aditividade + invariantes
2
+
3
+ ## Aditividade
4
+ Ao mexer no código, mantenha as mudanças **aditivas**: schema sempre com `tenant_id`; env vars, endpoints e UI adicionam, nunca removem o que já existe. As convenções de código e de contribuição do projeto estão nas instruções do projeto na raiz.
5
+
6
+ ## Invariantes (leia a doc certa ANTES de mexer no subsistema)
7
+ Os pontos fixos do projeto (multi-tenancy/RLS, "um core três transportes", roteamento BrowserRouter, CSP, encryption, i18n, UX/skeletons) estão nas instruções do projeto na raiz e detalhados por subsistema em `docs/` (cada doc cobre um subsistema: tenancy, graph, chatwoot, mcp, logs, etc.). Aponte e leia a doc do subsistema **antes** de editá-lo.
@@ -0,0 +1,9 @@
1
+ # 03: Implementação conduzida
2
+
3
+ 1. **Pergunte proativamente** o que o desenvolvedor quer implementar; não assuma o escopo.
4
+ 2. **Desenhe desafiando premissas** (o projeto valoriza questionar antes de executar): aponte problemas, proponha alternativas com justificativa concreta no código ou nas refs.
5
+ 3. **Implemente no estilo do código vizinho**: mesma densidade de comentários, naming e idioma. Comentários só com tag (`// TODO:`, `// NOTE:`, `// FIXME:`).
6
+ 4. **Respeite a marcação Free/Full** (`references/02-free-full-and-invariants.md`) em tudo que adicionar; confira o `.env.example` ao adicionar env var.
7
+ 5. **Valide:** `bun check` verde antes de concluir.
8
+
9
+ Estilo: sem em-dash; `fazer.ai` sempre minúsculo; PT-BR com acentuação onde houver texto pt-BR.
@@ -0,0 +1,13 @@
1
+ # 04: Gerar a própria imagem + deploy
2
+
3
+ Para rodar a sua versão modificada num servidor, gere a própria imagem e plugue num deploy.
4
+
5
+ ## Build da imagem
6
+ ```sh
7
+ docker build -t <seu-registry>/<seu-nome>:<tag> .
8
+ docker push <seu-registry>/<seu-nome>:<tag>
9
+ ```
10
+ O `Dockerfile` do repo já define o boot `bootstrap → migrate deploy → serve` como CMD. **Não** sobrescreva `command:` no compose (sobrescrever crash-loopa).
11
+
12
+ ## Plugar no deploy
13
+ Casa com o **Tier C** da `agents-onboarding` (compose genérico): aponte a imagem para a sua, setando a env `AGENTS_IMAGE` (ou edite o compose). As duas roles de DB (superuser para migrate/bootstrap; não-superuser para runtime) e os demais invariantes de deploy estão em `docs/deploy.md`.
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: agents-onboarding
3
+ description: "Conduz a jornada de onboarding 'do zero ao agente de atendimento' do fazer.ai agents num VPS, escolhendo o orquestrador de deploy (Tier A Coolify, B Portainer, C compose genérico para VM crua ou qualquer painel). Provisiona DNS/SSH pelo MCP Hostinger, faz deploy de Chatwoot + fazer.ai agents + Langfuse com TLS, roda o /setup e importa o agente via MCP, pluga o Agent Bot do Chatwoot e valida ponta a ponta (playground + WhatsApp + traces no Langfuse). Use quando o usuário quiser subir/onboardar uma instância nova do fazer.ai agents a partir do zero num VPS, em qualquer um desses orquestradores."
4
+ ---
5
+
6
+ # Onboarding fazer.ai agents: do zero ao agente (multi-plataforma)
7
+
8
+ Leva uma VPS de "nada" até "agente de atendimento de IA rodando, testado e plugado numa caixa de entrada real". Esta skill é o **bundle executado pelo agente** (você): opera o VPS via SSH + a API do orquestrador escolhido (Coolify / Portainer / compose genérico), e controla o fazer.ai agents via **MCP** (OAuth, dry-run + audit).
9
+
10
+ ## Enquadre a jornada em 4 fases (diga isso ao usuário no começo)
11
+
12
+ No início, resuma pro usuário o caminho em **4 fases**, pra ele saber onde está e o que vem. Use estas palavras (não os nomes técnicos das etapas):
13
+
14
+ 1. **O agente**: a ferramenta que conduz o onboarding (esta, que ele já escolheu) e os acessos que ela precisa (VPS, domínio) prontos.
15
+ 2. **Onde hospedar**: subir a base na infraestrutura dele, o provedor de nuvem + o painel que gerencia os serviços.
16
+ 3. **O Chatwoot**: a plataforma de atendimento onde as conversas acontecem.
17
+ 4. **Configurar o agente**: importar o agente de IA, ligar na caixa de entrada e testar de ponta a ponta.
18
+
19
+ É um mapa pro usuário, não o roteiro técnico: as etapas numeradas abaixo (0 a 10) são o seu passo a passo e detalham essas fases. Ao virar de fase, avise ("terminamos de preparar os acessos; agora vou escolher onde hospedar e subir a base") pra a jornada não parecer uma caixa-preta.
20
+
21
+ ## Antes de qualquer coisa
22
+
23
+ 1. **Leia [`guardrails.md`](guardrails.md) inteiro.** Tem fronteiras duras (só a VPS e o domínio indicados, licença única do hub, MCP dry-run, nada de produção de terceiros, nada de segredo em log). Cruzar qualquer uma é parar e perguntar.
24
+ 2. **Leia [`gotchas.md`](gotchas.md).** São as armadilhas conhecidas que, se ignoradas, fazem você redescobrir do jeito difícil (FQDN que não dirige o Traefik, embedding por-tenant, Langfuse sem blob storage, persistência de branding etc.).
25
+ 3. Confira pré-requisitos em [`references/00-prereqs-and-access.md`](references/00-prereqs-and-access.md): MCPs ligados (Hostinger ×3; o hub `app-fazer-ai` **não** é MCP da sessão, suas ops saem pelo proxy `bunx @fazer-ai/agents hub …`), acesso SSH, e o contrato do ambiente.
26
+
27
+ ## Como operar (princípios)
28
+
29
+ - **Uma pergunta de cada vez, pela ferramenta de pergunta estruturada (NUNCA um questionário em texto):** quando o agente tiver uma ferramenta de pergunta multiple-choice (**Claude Code: `AskUserQuestion`; Hermes: `clarify`**, setas + opções), **use-a, uma pergunta por mensagem**. Sem ela (Codex/genérico), pergunte em texto, ainda **1-2 itens por vez** (só junte 2 se correlatos, ex.: IP do VPS + caminho da chave SSH). Pergunte na ordem do fluxo, só quando a etapa precisar; **espere a resposta** antes de avançar. Despejar 5-6 campos numa mensagem é o anti-padrão.
30
+ - **Leia o que o CLI já entregou, NUNCA re-pergunte o que já foi decidido:** antes de perguntar qualquer coisa, cheque o contexto que o CLI deixou: (1) os **MCPs conectados**, e se `hostinger-dns`/`hostinger-vps`/`hostinger-domains` estão presentes, o **provider JÁ é Hostinger** (escolhido no CLI); use-os, nunca pergunte "se for Hostinger"; (2) o marcador **`~/.fazer-ai/onboarding.json`** (`chatwootSource` = Chatwoot novo vs. existente/BYO; quando novo, `chatwootTier` + `chatwootLicenseId`; já escolhidos, ver [`references/00-prereqs-and-access.md`](references/00-prereqs-and-access.md)). Re-perguntar provider/origem/tier/licença já definidos é erro.
31
+ - **Listar NÃO é escolher; input do usuário você SEMPRE pergunta:** separe três categorias e trate cada uma certo. **(a) Decidido pelo CLI** (provider/tier/licença) → não re-pergunte (acima). **(b) Sondável tecnicamente** (há acesso SSH? qual orquestrador já está instalado? o A-record propagou?) → sonde, não pergunte. **(c) Escolha do usuário** (**qual VPS, qual domínio raiz, nome de exibição, quais credenciais**): **SEMPRE pergunte**, e **mesmo (especialmente) quando o MCP lista várias opções, apresente-as e deixe o usuário escolher; NUNCA infira nem chute**. Ter o MCP da conta conectado é pra você **listar e perguntar melhor**, não pra decidir pelo usuário; escolher uma VPS/um domínio por conta própria ("só tinha que escolher uma") é **erro**: pare e pergunte. Autonomia é nos **passos técnicos** (deploy, config, comandos), nunca nos **inputs** do usuário.
32
+ - **Deploy por tier, espinha compartilhada:** o *deploy* (subir Chatwoot + fazer.ai agents + Langfuse com TLS) muda por orquestrador; depois dele, a espinha (etapas 6-10: `/setup` → MCP → import → bind → E2E) é **idêntica** em qualquer tier. A etapa 1c escolhe o tier e fixa o **contrato** que o deploy entrega à espinha.
33
+ - **Narre o progresso serviço a serviço, nunca fique em silêncio numa espera longa:** o deploy sobe os serviços em sequência (tipicamente o painel, depois o Chatwoot, depois o fazer.ai agents, depois o Langfuse), e cada um leva vários minutos. Ajuste a contagem ao caso real: com um Chatwoot que já existe (BYO) o Chatwoot não é subido, no Tier C pode nem haver painel. Antes de começar cada um, diga o que vai subir e quanto falta ("vou subir o Chatwoot agora; depois dele faltam 2 serviços"); quando terminar, confirme e anuncie o próximo ("Chatwoot no ar, agora o fazer.ai agents"). Enquanto uma instalação longa roda (imagem baixando, container subindo), dê um sinal de vida em vez de sumir por 5+ minutos. O usuário deve sempre saber **em qual serviço você está** e **quantos faltam**. Cada referência de deploy (02 a 05) reforça isso no começo.
34
+ - **MCP-only para a config do fazer.ai agents** (import, vault, tenant-settings, KB, plugar Chatwoot): **só** as MCP tools (dry-run + audit + fence de tenant), **nunca** REST direto, `/mcp` por fora do harness, leitura do código/bundle pra achar endpoints, nem API key pra contornar. **Se as tools MCP não estão expostas na sessão, PARE e peça ao usuário pra reiniciar o harness** (elas só carregam no boot); não há "fallback REST". SSH/psql/Rails runner só para **infra** (orquestrador, Chatwoot internals), de forma **transitória**. Detalhe e o gate em [`references/06-setup-and-mcp.md`](references/06-setup-and-mcp.md).
35
+ - **Windows/PowerShell: o shell só ORQUESTRA, nunca carrega o código.** Se o seu shell é PowerShell (a maioria das runs Windows; confira o ambiente), tratá-lo como bash é o erro que **mais quebra a run**, e os helpers de caso específico **não te cobrem nos ad-hoc**. **NUNCA** ponha payload numa linha de comando: nada de here-string `@'…'@ | (ssh|python|bash)`, de `ssh <host> '…código…'` com aspas aninhadas/`{{…}}`/`$()`/`(`, de `\` no fim da linha (continuação no PowerShell é `` ` ``, não `\`), de `'{…json…}' | helper` (pipe de payload), nem de `echo`/`Set-Content`/`Out-File > arquivo`. **SEMPRE** escreva o payload num **arquivo** (com a ferramenta de edição, zero shell, sem BOM) e rode apontando pro arquivo: bash/Python local → `bash x.sh`/`python x.py`; bash remoto → `scripts/remote.py --script-file x.sh`; psql/`rails runner` num container remoto → `scripts/remote.py --in-container <c> --exec "<prog>" --script-file x.sql`; JSON de API → `coolify.py … --json-file x.json` (nunca pipe); config do fazer.ai agents → MCP. O PowerShell pode ter variável/loop/`Start-Sleep`; só não pode **carregar o código**. Tabela completa e modos de falha em [`gotchas.md`](gotchas.md).
36
+ - **Idempotência / brownfield:** a VPS pode já ter um orquestrador (Coolify, Portainer, ou outro painel) e/ou outros serviços, em qualquer combinação. Antes de instalar, **sonde e decida por serviço** (etapa 1b, [`references/01b-brownfield.md`](references/01b-brownfield.md)): reaproveite o que está saudável, **nunca destrua** dados do usuário.
37
+ - **Nomes nunca hardcoded:** o nome de exibição/projeto vem do usuário (passo 1) e alimenta o projeto do orquestrador, a org/projeto do Langfuse e o tenant do fazer.ai agents (o `companyName` do `/setup`).
38
+ - **Prévia primeiro, e explique a ação em português claro:** toda mudança que grava algo (ativar a licença, ligar o Chatwoot no agente, criar credenciais) roda primeiro numa **prévia** que só mostra o que vai acontecer, sem efeito; e só grava de verdade depois do "pode ir". Ao pedir o OK, **descreva a ação e o efeito em linguagem de usuário**, sem jargão interno: diga *o que* muda e *por que*, não o nome da tool nem o mecanismo. Mecanismo (pro seu uso, não pra repetir ao usuário): writes do hub (proxy `hub …`) e write tools de MCP previewam por padrão e só aplicam com `--apply`/`dry_run:false`. As frases boas e ruins de cada ponto de aprovação estão em [`guardrails.md`](guardrails.md).
39
+
40
+ ## A jornada (ordem importa)
41
+
42
+ Abra a referência da etapa **antes** de executá-la (carga sob demanda). O fluxo é **0 → 1 → 1b → 1c**, então o **deploy do tier** escolhido em 1c (Tier A = etapas 2-5; B/C = o doc do tier), convergindo na **espinha 6-10** (igual em todos). As trilhas A (Coolify) e B (Portainer) são as maduras; a C (compose genérico) é mais nova, então trate-a como primeira run guiada (ver [`references/01c-pick-tier.md`](references/01c-pick-tier.md)).
43
+
44
+ | # | Etapa | Referência |
45
+ |---|-------|-----------|
46
+ | 0 | Pré-requisitos, MCPs, acesso | [`references/00-prereqs-and-access.md`](references/00-prereqs-and-access.md) |
47
+ | 1 | VPS + DNS (A-records `agentes./chatwoot./langfuse.` + painel do tier) + SSH | [`references/01-vps-dns-ssh.md`](references/01-vps-dns-ssh.md) |
48
+ | 1b | **Inventário brownfield**: sondar (read-only) e decidir por-serviço (reusar/instalar/sinalizar) | [`references/01b-brownfield.md`](references/01b-brownfield.md) |
49
+ | 1c | **Selecionar o tier** de deploy + fixar o **contrato** (o que o deploy entrega à espinha) | [`references/01c-pick-tier.md`](references/01c-pick-tier.md) |
50
+ | 2 | **Tier A** · Coolify: reusar/instalar, API Access, **Instance Domain** (`coolify.<root>`) | [`references/02-coolify.md`](references/02-coolify.md) |
51
+ | 3 | Deploy **Chatwoot** (Pro **ou** OSS pelo marcador; Pro: API do Coolify, login Harbor). **`chatwootSource: existing` PULA este passo** e usa o Chatwoot que já existe | [`references/03-chatwoot-pro.md`](references/03-chatwoot-pro.md) |
52
+ | 4 | Deploy **fazer.ai agents** (edição Free/Pro pelo marcador, `templates/docker-compose.coolify.yml`, bootstrap 2-roles + migrate) | [`references/04-agents-image.md`](references/04-agents-image.md) |
53
+ | 5 | Deploy **Langfuse** (+ **MinIO S3 obrigatório**) | [`references/05-langfuse.md`](references/05-langfuse.md) |
54
+ | 6 | fazer.ai agents `/setup` (cria admin SUPER_ADMIN) → conectar **MCP** (OAuth) → **alvo de tenant** (`tenant_list`; passar `tenant` nas tools) | [`references/06-setup-and-mcp.md`](references/06-setup-and-mcp.md) |
55
+ | 8 | **Import do agente** (`agent_import`; padrão **Maria**/Clínica Moreira, vendorado em `samples/agents/maria-clinica-moreira.json`) + embedding por-tenant + reindex/retry da KB | [`references/08-agent-import.md`](references/08-agent-import.md) |
56
+ | 8b | **Pós-import (gate)**: resolver avisos (KB→READY + grounding; STT/TTS/visão) + features opcionais (voz, Google OAuth) | [`references/agent-features.md`](references/agent-features.md) |
57
+ | 9 | Plugar Chatwoot no fazer.ai agents (`deployment_connect` → `set_accounts` → `inbox_bind`) | [`references/09-chatwoot-bind.md`](references/09-chatwoot-bind.md) |
58
+ | 9b | **Licenciar Chatwoot no hub** (Kanban/Pro): com licença disponível (CLI/`hub licenses`) é **happy-path** (`hub create-instance → hub attach-license → Refresh`); sem licença → OSS sem Kanban | [`references/chatwoot-hub-register.md`](references/chatwoot-hub-register.md) |
59
+ | 10 | Validar **E2E** (playground + grounding → **integração via Inbox API** → traces; WhatsApp real opcional) | [`references/10-validate-e2e.md`](references/10-validate-e2e.md) |
60
+
61
+ **O deploy (etapa 2) ramifica por tier.** As linhas 2-5 acima são a trilha do **Tier A (Coolify)**. Para os outros, escolha em 1c, **substitua 2-5** pelo doc único do tier e convirja direto no **6** (todos entregam o mesmo [contrato](references/01c-pick-tier.md)):
62
+
63
+ - **Tier B** (Portainer): [`references/deploy-b-portainer.md`](references/deploy-b-portainer.md)
64
+ - **Tier C** (compose genérico, VM crua ou qualquer painel): [`references/deploy-c-compose.md`](references/deploy-c-compose.md)
65
+
66
+ ## Gates de conta (o usuário cria cada admin)
67
+
68
+ O **usuário** cria o 1º admin no browser do orquestrador (Coolify/Portainer), do Chatwoot e do fazer.ai agents (`/setup`). Você **entrega o link + a instrução e espera** (no Coolify, `coolify.py wait-admin`; no fazer.ai agents, a URL `/setup` com o token do boot), **nunca** cria essas contas por conta própria. Depois do admin criado, o token e o resto da config são com você. **Exceção: o Langfuse** é headless (`LANGFUSE_INIT_*`, etapa 5): você semeia a conta (usuário OWNER) e o operador só faz **login** (`/auth/sign-in`). Detalhes em `guardrails.md`.
69
+
70
+ ## Fora de escopo desta skill (por enquanto)
71
+
72
+ - **Trilhas dedicadas por painel** (Easypanel/Dokploy/CapRover/etc.): por escolha, não existem. Use o **Tier C** (compose genérico) e adapte ao painel com seu conhecimento dele.
73
+ - Migração/atualização de serviços incompatíveis (a etapa 1b **detecta e sinaliza**; a migração em si é decisão do usuário).
74
+ - Caminho manual (sem IA) e adapters de agente não-Claude-Code (Codex/Hermes).
75
+
76
+ Os três tiers de deploy (A/B/C) estão **dentro** do escopo (a etapa 1c roteia).
77
+
78
+ ## Critério de aceite (E2E, objetivo final)
79
+
80
+ A run está **provada** quando: o agente responde no **playground** E numa **mensagem real de WhatsApp** (incoming → webhook → debounce → turn → modelo real → resposta entregue na conversa do Chatwoot); a KB está **grounding** (docs READY, resposta usa o conteúdo indexado); e os **traces aparecem no Langfuse** (ingestion 207). Detalhe e checklist em [`references/10-validate-e2e.md`](references/10-validate-e2e.md).
@@ -0,0 +1,157 @@
1
+ # Gotchas: armadilhas conhecidas
2
+
3
+ Cada uma é uma armadilha conhecida. Leia a da etapa antes de executá-la.
4
+
5
+ ## Infra / Coolify
6
+
7
+ ### FQDN não dirige o Traefik (503 em todo serviço)
8
+
9
+ Pra um **service** do Coolify, o Traefik lê `service_applications.fqdn` no DB; o env `SERVICE_FQDN_*` é **derivado** dele e NÃO move a rota. Sintoma: o serviço sobe mas dá 503 (cert 000), e o fazer.ai agents bootou com `publicUrl` sslip.io.
10
+
11
+ Fix via `scripts/coolify.py` (base64-pipa o psql; o restart não monta curl com token à mão):
12
+
13
+ ```sh
14
+ python3 scripts/coolify.py list-apps --ssh root@<VPS_IP> # ache o id
15
+ python3 scripts/coolify.py set-fqdn --ssh root@<VPS_IP> --app-id <id> --fqdn https://agentes.<seu-dominio>
16
+ python3 scripts/coolify.py api-post --base-url http://<VPS_IP>:8000 --token-file coolify.token --path /services/<uuid>/restart
17
+ ```
18
+
19
+ **Preserve a porta** quando o template tem (Langfuse: `...:3000`). Verifique por sslip.io enquanto o DNS não propaga.
20
+
21
+ ### Windows/Git Bash: arg com `/` inicial vira caminho do Windows
22
+
23
+ No Git Bash/MSYS do Windows, um argumento que começa com `/` (ex.: `coolify.py api-post --path /services/<uuid>/restart`, ou um path de `docker exec`) é convertido pra caminho do Windows (`C:/Program Files/Git/services/...`) **antes** do Python/comando ver, e a chamada bate em rota errada (404/alvo errado). Prefixe com `MSYS_NO_PATHCONV=1` (ou passe a barra dupla `//services/...`). Só morde no Git Bash; PowerShell puro e Linux/macOS não.
24
+
25
+ ### `docker_compose_raw` precisa ser base64
26
+
27
+ `POST /api/v1/services` com o compose cru → 422 "should be base64 encoded". O `scripts/coolify.py create-service` faz o base64 do `--compose-file`; se POSTar à mão via `api-post`, encode antes.
28
+
29
+ ### NÃO sobrescrever `command:` no compose do fazer.ai agents
30
+
31
+ O boot (`bootstrap → migrate → serve`) é o CMD da imagem. Um `command:` override já derivou pro `./server` obsoleto e crash-loopou (`exec: ./server: not found`). Não declare `command:` no compose do fazer.ai agents.
32
+
33
+ ### Instance Domain do Coolify
34
+
35
+ Sem setar o Instance Domain (`coolify.<seu-dominio>`) o painel fica só em `http://IP:8000` (HTTP puro, sem TLS). Exige o A-record.
36
+
37
+ ### Coolify não conecta no próprio servidor (localhost Unreachable) = chave grudada
38
+
39
+ O Coolify alcança o servidor `localhost` por SSH do container `coolify` pro host (`root@host.docker.internal`), com uma chave que o instalador adiciona às **chaves autorizadas do root** via `cat >>`. Se a última linha do arquivo não terminava em newline (uma chave **colada pelo painel da VPS** chega sem `\n` final), a chave do Coolify **gruda** no fim da linha anterior e deixa de ser entrada válida → servidor `localhost` **Unreachable** → **todo deploy falha** (sem erro de SSH claro; a UI só mostra inacessível). Determinístico: repete a cada install nessa condição. **Não edite as chaves autorizadas na mão** (SSH→PowerShell mangla o quoting). Fix idempotente, logo após o Coolify subir e **antes** de deployar:
40
+ ```sh
41
+ python3 scripts/coolify.py heal-localhost --ssh root@<VPS_IP> # normaliza as chaves + verifica container->host
42
+ ```
43
+ `reachable:true` → segue. Detalhe em `references/02-coolify.md`.
44
+
45
+ ### `prisma migrate reset` quebra a runtime role (local/dev)
46
+
47
+ Reset recria o schema `public` e leva junto os grants da app role → próximo boot dá `42501 permission denied for schema public`. Nunca rode bare `migrate reset`; use `bun db:reset` (ou re-rode `bun db:bootstrap`).
48
+
49
+ ### Service compose: introspecção é por filesystem + `docker compose`, não pela fila de deploy
50
+
51
+ Um **service** compose do Coolify NÃO popula `application_deployment_queues` (essa tabela é de *applications*; consultá-la pra um service devolve fila vazia e te faz achar que o deploy não rodou). O estado de um service vive em `/data/coolify/services/<uuid>/` e valida por `docker compose config`/`docker compose ps`. Pra checar se subiu: `docker compose -p <uuid> ps` (ou `ls /data/coolify/services/<uuid>/`), não a fila.
52
+
53
+ ### O `start` da API pode não materializar containers: NÃO suba por `docker compose` à mão
54
+
55
+ Num Coolify **recém-instalado** (ou logo após reiniciar o container `coolify`/o Docker), o `POST /api/v1/services/<uuid>/start` pode responder `200 "starting request queued"` e a fila preparar projeto/rede/compose em `/data/coolify/services/<uuid>/` **sem criar os containers** (o worker/Horizon do Coolify não processou o job). A tentação é subir na mão (`docker compose -p <uuid> up -d` no diretório do serviço). **Não faça.** Os containers até sobem (`Up healthy` no `docker ps`), mas ficam **fora da gestão do Coolify**: a UI mostra o serviço **Exited/Degraded** (o Coolify não registrou o deploy), e um Restart/Redeploy pela UI depois **conflita** ou re-cria por cima. Sintoma exato (numa run real): `docker ps` mostra tudo `Up (healthy)`, a UI do Coolify mostra `Exited`. Em vez de contornar: confirme que o container `coolify` está `Up (healthy)` e que **nada o reiniciou no meio do deploy** (evite `docker restart coolify`/`systemctl restart docker` durante o deploy, que zera a fila; faça o Instance Domain ANTES de deployar os serviços, não no meio), e **re-dispare o deploy pela API/UI do Coolify**. Se persistir, investigue o worker do Coolify; nunca substitua o deploy do orquestrador por `docker compose` manual.
56
+
57
+ ### Imagem grande: não faça `docker pull` em foreground (estoura o timeout do harness)
58
+
59
+ Um `docker pull` de imagem grande (Chatwoot Pro) passa de 3 min e o harness mata o comando (exit 124): você acha que falhou e re-tenta à toa. Para **confirmar auth/existência** sem baixar, use `docker manifest inspect <imagem>` (segundos); **deixe o Coolify puxar** a imagem no deploy (assíncrono), não você em foreground.
60
+
61
+ ### Comandos dentro de container (runner/tinker): via helper que é dono do payload
62
+
63
+ Não monte one-liners de console (Rails runner do Chatwoot, `artisan tinker`/PsySH) à mão por `ssh … "docker exec … --execute='App\Models\User…'"` **dentro do PowerShell**: o `\` de namespace e as aspas são mangled (PHP `T_NS_SEPARATOR` parse error; echo do PsySH polui o stdout). Use o helper que **é dono do payload** e o passa base64 (como o `scripts/coolify.py` já faz pro psql), sem quoting manual atravessando PowerShell→SSH.
64
+
65
+ ### `docker ps --format '{{…}}'` à mão quebra no PowerShell→SSH
66
+
67
+ O agente improvisa `ssh … "docker ps --format '{{.Names}}\t{{.Status}}'"` pra ver o que subiu, mas no PowerShell→SSH o `{{…}}` e o `\t` são mangled (vira comando quebrado e você lê "nada rodando" num host que TEM containers). Use o helper que é dono do payload: `scripts/docker-status.py --ssh root@<HOST>` (ou `--project <uuid>` p/ um service do Coolify, `--all` p/ incluir parados). Ele roda o ssh por argv direto (chaves intactas) e devolve JSON normalizado.
68
+
69
+ ### Windows/PowerShell: NUNCA payload inline, sempre arquivo + interpretador (a armadilha #1)
70
+
71
+ A que MAIS quebra a run, sempre num contexto novo (e os helpers de caso específico **não te cobrem nos ad-hoc**): a cada novo (gerar uma key, um restart, uma query) o agente cai no default e monta o comando como se o PowerShell fosse bash. Modos de falha reais já vistos:
72
+
73
+ - **aspas comidas:** `ssh <host> '… --format "{{.Name}}" | grep -vE "^(a|b)$"'` → o Windows engole as `"` internas ao repassar pro `ssh.exe`; o bash recebe `{{.Name}}`/`^(a|b)$` **sem** aspas → `syntax error near unexpected token '('`.
74
+ - **BOM na here-string:** `@'…set -euo pipefail…'@ | ssh … 'bash -s'` → o PowerShell prefixa um **BOM UTF-8** na 1ª linha; o bash lê `set` como comando inexistente, o guard **não arma**, e o resto (um `rm -rf`) roda sem proteção.
75
+ - **`\` de continuação:** quebrar um comando com `\` no fim da linha → no PowerShell continuação é `` ` `` (backtick); a 2ª linha vira comando solto (`O termo '\ && docker restart' não é reconhecido`) e o ssh fica com aspas abertas (`unexpected EOF while looking for matching "`).
76
+ - **namespace `\`:** `rails runner`/`tinker` com `App\Models\User` inline → o `\` é comido → `T_NS_SEPARATOR`.
77
+ - **payload via pipe:** `'{…json…}' | helper` ou `@'…python…'@ | python -` → here-string/pipe carregam o payload com o BOM/encoding do PowerShell.
78
+ - **acentos viram `?`:** qualquer texto com acento montado/pipeado inline (uma mensagem de teste `Olá, quais convênios vocês aceitam?`, o nome de uma conta) volta corrompido (`Ol?, quais conv?nios voc?s aceitam?`): o PowerShell re-encoda o pipe/argv na code page do console, não em UTF-8. O fix é o mesmo: o texto vai num **arquivo UTF-8** e a ferramenta/helper aponta pro arquivo (`--data @msg.json`, `--script-file`, `--json-file`), nunca inline.
79
+
80
+ Não há heredoc (`<<'EOF'` é só POSIX) nem `<` de stdin no PowerShell, por isso o agente é empurrado pra essas traduções frágeis. **A regra que fecha a classe inteira: o shell só ORQUESTRA (variável, loop, `Start-Sleep`, chamar o interpretador); NUNCA carrega o código.** Escreva o payload num **arquivo** (com a ferramenta de edição, zero shell, sem BOM) e rode apontando pro arquivo:
81
+
82
+ | Você quer… | NÃO (inline) | SIM (payload em arquivo) |
83
+ |---|---|---|
84
+ | bash remoto | `ssh <host> '…script…'`, `@'…'@ \| ssh` | `remote.py --script-file x.sh` |
85
+ | psql num container | `ssh <host> "docker exec … psql -c \"…\""` | `remote.py --in-container <db> --exec "psql -U u -d d -v ON_ERROR_STOP=1" --script-file q.sql` |
86
+ | rails runner / tinker | `ssh <host> "docker exec … rails runner …"` | `remote.py --in-container <c> --exec "bundle exec rails runner -" --script-file t.rb` |
87
+ | Python local | `@'…'@ \| python -` | escreva o `.py`, rode `python x.py` |
88
+ | JSON de API | `'{…}' \| helper`, `-d '{…}'` | `coolify.py api-post … --json-file x.json` |
89
+ | criar/editar arquivo | `echo`/`Set-Content`/`Out-File >` | a ferramenta de edição do agente |
90
+ | config do fazer.ai agents | (qualquer inline acima) | tools de **MCP** |
91
+
92
+ `remote.py` alimenta o `bash -s` (ou `docker exec -i … psql/runner`) remoto via **stdin por argv direto**: aspas, `$()`, `{{…}}`, `(`, `\`, heredoc e múltiplas linhas chegam **byte a byte em qualquer SO**. Saída ao vivo (instalação longa não estoura timeout), exit code propagado; `--capture` devolve `{ok,exit_code,stdout,stderr}` JSON pra parsear; `--dry-run` mostra o argv. **Só** um comando de **uma linha sem** `"`/`$()`/`{{…}}`/`(`/`\` pode ir inline (`ssh <host> 'hostname; docker ps -q'`). Os helpers de caso específico seguem como atalho (`docker-status.py` p/ `docker ps`, `coolify.py` p/ os fluxos de Coolify, `chatwoot-admin.py` p/ ler o admin/token do Chatwoot); pra **qualquer outro** script remoto/console, `remote.py`.
93
+
94
+ ## Edições (imagens Free/Pro)
95
+
96
+ ### fazer.ai agents Pro ≠ licença Chatwoot avulsa (precisa da comunidade)
97
+
98
+ A edição **Pro** do fazer.ai agents (`edition: "pro"`, marcador) usa imagem privada no Harbor (projeto `agents`), liberada **só pra membros da comunidade** (`isCommunityGrant`). Uma licença Chatwoot Pro **avulsa** NÃO desbloqueia o fazer.ai agents. A robot do Harbor é **per-user** (cobre a união dos projetos a que o usuário tem acesso): se Chatwoot e o fazer.ai agents são ambos Pro, é **um único** `docker login`: não logue duas vezes. `free` = imagem pública, **sem** `docker login`.
99
+
100
+ ### Chatwoot OSS não faz `docker login` nem usa Baileys
101
+
102
+ `chatwootTier: "community"` (OSS) usa a imagem pública `ghcr.io/fazer-ai/chatwoot` (nosso fork), **sem** `docker login` no Harbor e **sem** o `baileys-api` (`COMPOSE_PROFILES` vazio). Só o `pro` faz `docker login` no Harbor + imagem privada `chatwoot-pro` + Baileys. **Não** rode `docker login` nem provisione credencial do Harbor no caminho OSS (não há licença, e o pull público não precisa dela).
103
+
104
+ ## Langfuse
105
+
106
+ ### One-click sem MinIO = traces somem em silêncio
107
+
108
+ Langfuse v3 exige S3 blob storage na ingestion. O one-click sobe sem MinIO e com `LANGFUSE_S3_*` vazias → `POST /ingestion` dá HTTP 500 e os traces nunca chegam; o `GET /projects` (só Postgres) retorna 200 e mascara. **Use `templates/langfuse/docker-compose.coolify.yml`** (com MinIO) e valide com `scripts/langfuse-verify.py ingestion` (espere 207, não 500). Detalhe em `references/05-langfuse.md`.
109
+
110
+ ## Config do fazer.ai agents (pós-import)
111
+
112
+ ### MCP/SUPER_ADMIN: mire o tenant com o argumento `tenant`, não crie um tenant
113
+
114
+ O token MCP do admin do `/setup` é **fleet-level** (`whoami` → `tenantId: null`): ele não tem tenant embutido. Toda tool per-tenant (agent_import, vault, deployment_connect, …) exige o argumento **`tenant`** (slug ou id de `tenant_list`). O erro clássico: a tool reclama *"fleet-level … pass `tenant`"*/*"no tenant target"* e o agente conclui que **falta um tenant** e chama `tenant_create` → cria um tenant **órfão**, e o import cai no lugar errado. Há **um** tenant (o do `/setup`); rode `tenant_list` e passe o `tenant`. Detalhe em `references/06-setup-and-mcp.md`.
115
+
116
+ ### Embedding é por-tenant (sem ele a KB não indexa)
117
+
118
+ Sem `PUT /v1/tenant-settings/embedding {…, credentialRef}` (ou com a credencial ainda **pendente**), a KB **não indexa**: os docs ficam `UNINDEXED` e o `knowledge_reindex` volta `blocked` + `fillAt` (deeplink pra preencher), **não** FAILED (pré-requisito faltando não é falha). É no nível do **tenant**, não por-KB nem da chave do modelo do agente.
119
+
120
+ ### reindex de docs FAILED pede `include_failed`
121
+
122
+ `knowledge_reindex` (REST: `POST /v1/knowledge/bases/:id/reindex`) varre só `UNINDEXED` por padrão. Pra recuperar docs **FAILED** (erro real de ingestão) em lote, passe `include_failed:true`; ou `POST /v1/knowledge/documents/:id/retry` por doc.
123
+
124
+ ### agent_import resolve credenciais por nome
125
+
126
+ O export referencia tudo **por nome**, e os nomes não existem no tenant novo. O `agent_import` cria as credenciais faltantes como **pending + deeplink** e emite o aviso `credentialPending`: o usuário só preenche o segredo. Exceções que não viram pending: kinds de OAuth gerenciado (`google_oauth`, `mcp_oauth`) e kinds que exigem `baseURL`/`paramName` → caem em `credentialNotFound`. Detalhe em `references/08-agent-import.md`.
127
+
128
+ ### Chatwoot bind: `POST /deployment` registra o deployment; quem conecta as CONTAS é o `/accounts`
129
+
130
+ `POST /v1/chatwoot/deployment` valida o token (via `/profile`), **persiste o deployment** (baseUrl + adminToken criptografado na linha do deployment) e retorna as contas alcançáveis. O que ele **não** faz é conectar/sincronizar as contas individuais: isso é o `PUT /v1/chatwoot/deployment/accounts {accountIds:[...]}`. Depois `PATCH /v1/chatwoot/inboxes/:id {agentId}` provisiona o Agent Bot + webhook (não precisa setar `webhook_url` à mão). Detalhe em `references/09-chatwoot-bind.md`.
131
+
132
+ ## Footguns de API (campos exatos)
133
+
134
+ ### `POST /api/v1/api-keys`: o campo é `displayName`
135
+
136
+ Mintar a API key do fazer.ai agents com `{ "name": ... }` → 422. O campo é `displayName`. O `token` vem só uma vez.
137
+
138
+ ### vault POST usa `baseUrl` (camelCase)
139
+
140
+ `POST /v1/vault` espera `baseUrl` (camelCase); mandar `baseURL` faz a entrada nascer sem base URL e o wiring do Langfuse falha ("requires a base URL"). Atenção: o endpoint `PUT /v1/tenant-settings/embedding` usa a forma `baseURL` (maiúsculo), os dois diferem.
141
+
142
+ ## Ambiente do agente (CLI / box)
143
+
144
+ ### Box bun-only: não chame `node`
145
+
146
+ A máquina do operador pode ter **só `bun`** (sem `node` no PATH): `node helper.js` dá `CommandNotFoundException`. Não escreva helpers Node ad-hoc e não invoque `node`: rode os scripts com **`bun`** (e prefira os helpers vendorados da skill, `scripts/*.py`/`*.ts`, em vez de improvisar). As ops do hub saem pelo proxy `bunx @fazer-ai/agents hub …`, não por um helper Node escrito na hora.
147
+
148
+ ### Esperas de gate humano: em background, nunca em foreground
149
+
150
+ Os polls que esperam uma ação do usuário no browser (`coolify.py wait-admin` no admin do Coolify, `sshkey.py wait-access` na chave SSH) bloqueiam por minutos. Em **foreground** eles travam o seu turno o tempo todo, sem nada a fazer no meio, e queimam tempo/tokens à toa (o operador vê "Running 1 shell command… (1m+)"). Rode-os **non-blocking / em background** e retome quando o comando sair (mesma regra do `docker pull` de imagem grande). Passe uma janela de attempts larga pro gate humano (ex.: `wait-admin --attempts 120`, ~10 min) e **não avance pro passo seguinte antes do `ok:true`**. No Claude Code o mecanismo é o `Bash` com `run_in_background` (dispara detached e te re-aciona no exit), não um shell foreground.
151
+
152
+ ## Pendências conhecidas (não bloqueiam o core)
153
+
154
+ - **TTS:** precisa de chave ElevenLabs real.
155
+ - **Visão:** precisa de chave Gemini válida.
156
+ - **WhatsApp físico:** opcional; exige um número que o usuário controle. A integração Chatwoot→fazer.ai agents já é provada sem aparelho via Inbox API (etapa 10); o físico só confirma o transporte real.
157
+ - **Kanban:** condicional à licença, **não** "opcional". Com licença disponível (CLI/`hub licenses`), habilitar é **happy-path** (licenciar no hub + Refresh; ver `references/chatwoot-hub-register.md`); imagem Pro sozinha não basta. Sem licença → OSS, sem Kanban.
@@ -0,0 +1,65 @@
1
+ # Guardrails: fronteiras que NÃO se cruzam
2
+
3
+ Valem em qualquer execução desta skill. Cruzar qualquer uma é **parar e perguntar**.
4
+
5
+ ## Escopo de operação
6
+
7
+ - **Opere SÓ na infraestrutura que o usuário forneceu e autorizou para este onboarding:** a VPS indicada (`<VPS_IP>`), o domínio indicado (`<seu-dominio>`), e a licença + registry credential do Chatwoot Pro indicadas (`<LICENSE_ID>` / `<REGISTRY_CRED_ID>`). Tudo isso é dado de entrada; as refs usam placeholders.
8
+ - **Nunca toque em outras VPS, domínios, instâncias ou licenças da conta do usuário.** A mesma conta (provedor de VPS, DNS, hub `app-fazer-ai`) pode hospedar **produção de terceiros**. Uma write tool errada derruba o serviço de outro cliente, e o token do hub costuma ter `mcp:admin`. Antes de qualquer write no hub (ou ação destrutiva na VPS), **confirme que o alvo é o recurso certo**; na dúvida, pare e pergunte.
9
+ - **Ações destrutivas** (recreate/stop/restart/firewall/reset de VPS, reinstalar Coolify, wipe de volume) **só com OK explícito** e só no recurso confirmado como descartável. Em brownfield (VPS já populada), **nunca** destrua dados do usuário: detecte e reaproveite.
10
+
11
+ ## Produção e mutações
12
+
13
+ - **Nunca** modificar produção a menos que o usuário peça aquela mudança específica. Autorização a um objetivo não é autorização para tocar produção nem para escolher o método.
14
+ - **Nunca** editar o DB de produção direto para mudar estado de aplicação: usar a UI/API da própria app. (Durante o provisionamento inicial, antes da app estar no ar, mexer no DB/console via `psql`/Rails runner é aceitável e transitório; uma vez em produção, use a UI/API.)
15
+ - **Writes do hub** (via proxy `bunx @fazer-ai/agents hub …`) e **write tools de MCP** são **dry-run por padrão**. Aplicar (`--apply` / `dry_run:false`) só com OK explícito do usuário para aquela ação.
16
+
17
+ ## Como pedir aprovação e falar com o usuário (frases boas × ruins)
18
+
19
+ Quando você pede um "pode ir" ou faz uma pergunta, o usuário é uma pessoa que quer o agente de atendimento no ar, **não** um engenheiro do produto. Descreva **o que** vai mudar e **por que**, em português comum. **Corte o jargão interno:** nomes de tool (`deployment_connect`, `agent_import`), termos de arquitetura ("dry-run", "idempotente", "tenant", "marcador", "write no hub", "credencial per-user do Harbor", "robot"), nomes de env var e caminhos de arquivo. Isso é vocabulário seu pra executar, não pra repetir ao usuário. Regra prática: se a frase só faz sentido pra quem leu esta skill, reescreva.
20
+
21
+ Cada ponto de aprovação, com uma frase **ruim** (jargão) e uma **boa** (clara):
22
+
23
+ - **Ativar a licença Pro / Kanban (hub):**
24
+ - Ruim: "Posso aplicar o write no hub pra atachar a licença e rodar o refresh da assinatura? É idempotente."
25
+ - Boa: "Vou ativar o seu plano Pro nesta instalação, o que libera o Kanban no Chatwoot. Posso seguir?"
26
+ - **Baixar a imagem Pro (credencial do registro privado):**
27
+ - Ruim: "Vou provisionar a registry credential per-user do Harbor pelo proxy do hub e fazer o docker login."
28
+ - Boa: "Vou liberar o acesso à versão Pro pra baixar os programas no servidor. Isso usa a sua assinatura; nenhuma senha passa por mim."
29
+ - (Só peça se de fato houver uma escolha; se a edição já foi decidida no início, é um passo automático, não uma pergunta.)
30
+ - **Ligar o Chatwoot ao agente (conectar deployment + contas + inbox):**
31
+ - Ruim: "Rodo o `deployment_connect` e depois o `inbox_bind` (dry_run:false) pra provisionar o Agent Bot e o webhook?"
32
+ - Boa: "Vou conectar o seu Chatwoot ao agente e ligar o robô na caixa de entrada, pra ele começar a responder as conversas. Posso aplicar?"
33
+ - **Importar o agente:**
34
+ - Ruim: "Vou chamar o `agent_import` no tenant `<slug>` com o export vendorado."
35
+ - Boa: "Vou criar o agente de atendimento (a Maria, uma recepcionista de exemplo) na sua conta. Ele nasce desligado e em modo de teste, então não fala com cliente nenhum até você liberar. Posso criar?"
36
+ - **Ligar o Langfuse (traços/monitoramento):**
37
+ - Ruim: "Rodo o `langfuse_connect` inline com as keys que semeei e ligo o tracing no tenant-settings."
38
+ - Boa: "Vou ligar o painel que registra as conversas do agente (pra você acompanhar e depurar depois). Posso seguir?"
39
+ - **Criar credenciais que faltam (para o usuário preencher):**
40
+ - Ruim: "O import gerou entradas pending; te mando o deeplink de fill do vault."
41
+ - Boa: "Faltam algumas chaves (por exemplo a da OpenAI) pro agente funcionar. Vou te mandar um link direto pra você colar cada uma com segurança; elas não passam por mim."
42
+ - **Ação que apaga ou reinicia algo no servidor (recreate/stop/restart/firewall/apagar volume):**
43
+ - Ruim: "Posso rodar o recreate da VM / wipe do volume?"
44
+ - Boa: "Pra seguir eu preciso reiniciar o serviço X no servidor, o que deixa ele fora do ar por alguns instantes. Confirma que posso?" (E se a ação apaga dados, diga isso explicitamente e espere um "sim" claro.)
45
+ - **Ir para produção (agente atende clientes reais):**
46
+ - Ruim: "Faço o `agent_update` com `mode:production`?"
47
+ - Boa: "O teste passou. Quando você quiser, eu coloco o agente pra atender clientes de verdade, é a sua decisão. Me avisa quando for a hora."
48
+
49
+ Duas regras que valem em todo ponto: (1) uma ação que grava algo roda primeiro em **prévia** (sem efeito) e só vale de verdade com o "pode ir", então deixe claro que nada foi feito ainda; (2) o usuário **cria as contas de admin** (Coolify/Chatwoot/fazer.ai agents) no navegador dele: você entrega o link e a instrução e **espera**, sem criar por conta própria (ver "Gates de criação de conta").
50
+
51
+ ## Segredos
52
+
53
+ - **Nenhum segredo em repo, log, commit ou arquivo plano.** Cada segredo vive no destino final: env do serviço no Coolify / DB do Chatwoot / **vault do fazer.ai agents** / store de MCP do harness (o token do fazer.ai agents fica no harness do agente).
54
+ - **Nunca `cat`/imprima arquivos de credencial locais** do CLI/agente: `~/.fazer-ai/oauth.json` (refresh token do hub), `*.token`, `*.keys.json`, `/root/.docker/config.json`. Para checar presença, teste só a existência (`Test-Path` / `[ -f … ]`) ou um `grep -q` **sem** imprimir o conteúdo; despejar o arquivo no output coloca o segredo no transcript (que persiste em disco). Mesma regra dos logs: redija (`sed -E 's/(token=|password=|secret=)[^ ]+/\1[REDACTED]/'`) antes de mostrar.
55
+ - Segredos de infra usados pelo agente são **buscados transitoriamente** no momento do uso (token do Chatwoot via Rails runner, senha do Coolify-db via env do container), nunca persistidos em disco.
56
+ - Credenciais do **usuário** (OpenAI/ElevenLabs/Gemini/Asaas) entram como **pending + deeplink**: o usuário preenche no console; nunca passam pelo agente.
57
+
58
+ ## Gates de criação de conta
59
+
60
+ - O **usuário** cria o 1º admin no browser do orquestrador (Coolify/Portainer), do Chatwoot e do fazer.ai agents (`/setup`). O agente **entrega o link + a instrução e espera** o usuário criar; **nunca** cria essas contas por conta própria (não há atalho: nada de CLI/console/auto-seed criando esses admins). Na agents, a URL `/setup` com o token impresso no boot. Depois da conta criada, o agente segue (obtém o token via Rails runner transitório, deploy, config).
61
+ - **Exceção: o Langfuse** é provisionado **headless** (`LANGFUSE_INIT_*`, etapa 5): o agente semeia usuário (OWNER) + org + projeto + keys num deploy só, com signup fechado desde o boot. O usuário **não cria conta** lá, só faz **login** na conta semeada (`/auth/sign-in`) e troca a senha. É o padrão oficial do Langfuse e evita a janela de signup aberto (o Langfuse não tem o gate primeiro-admin-depois-fecha do Coolify/agents).
62
+
63
+ ## Estilo
64
+
65
+ - PT-BR com acentuação correta. Nada de em-dash (use vírgula/ponto/dois-pontos). `fazer.ai` sempre minúsculo (slugs `fazer-ai` ok).
@@ -0,0 +1,37 @@
1
+ # 00: Pré-requisitos e acesso
2
+
3
+ ## MCPs (ligar ANTES de começar; reiniciar a sessão cedo)
4
+
5
+ - **Hostinger** (3 servers via npx, stdio, `HOSTINGER_API_TOKEN`): `hostinger-dns`, `hostinger-vps`, `hostinger-domains`. DNS é o uso central (etapa 1: A-records); VPS/domains pra descoberta/gestão. **Só quando a infra é Hostinger**: em outro provider não há esses MCPs (ver "Outro provider" na `01-vps-dns-ssh.md`). Chame as tools pelo **nome completo** `mcp__hostinger-{dns,vps,domains}__<Tool>` (ex.: `mcp__hostinger-vps__VPS_getVirtualMachinesV1`); o nome cru (`VPS_...`, `domains_...`) devolve *No such tool available*.
6
+ - **Hub `app-fazer-ai`**: **não** é conectado como MCP na sua sessão. As ops do hub que o onboarding precisa (registry credential do Harbor, cadastro/atacha da instância na licença) saem pelo **proxy do CLI**: `bunx @fazer-ai/agents hub <op>` (usa o OAuth do `~/.fazer-ai/oauth.json` do bootstrap; dry-run por padrão, `--apply` pra escrever). Você ganha só essas ops, sem token `mcp:admin` na sessão. Detalhe em `03-chatwoot-pro.md` / `chatwoot-hub-register.md` / `04-agents-image.md`; limites em `guardrails.md`.
7
+ - **agents** (OAuth): conectado SÓ na etapa 6, depois do `/setup` (antes disso a instância nem existe).
8
+ - Depois de adicionar os MCPs, **reinicie a sessão do harness** pra eles ficarem disponíveis na execução.
9
+
10
+ ## Estado do CLI de onboarding (em disco, `~/.fazer-ai/`)
11
+
12
+ O CLI `@fazer-ai/agents` roda ANTES do handoff e deixa marcadores que você deve **ler em vez de re-perguntar**:
13
+
14
+ - **`onboarding.json`**: `{ chatwootSource: "new" | "existing", chatwootTier?: "pro" | "community", chatwootLicenseId?, edition: "free" | "pro" }`. **Eixos independentes:** a **origem** do Chatwoot (`chatwootSource`: `new` = subir um novo, aí `chatwootTier` diz a edição; `existing` = plugar num Chatwoot que **já existe** / BYO, **sem** tier nem licença), a edição do **Chatwoot** quando é novo (`chatwootTier`, etapa 3 / `chatwoot-hub-register.md` / `templates/chatwoot/README.md`) e a edição da **fazer.ai agents** (`edition`, etapa 4 / `04-agents-image.md`: `pro` = imagem privada Harbor; `free` = pública). É a escolha **explícita** do operador; respeite-a (inclusive `existing` = não provisionar Chatwoot; `community`/`free` = seguir sem Pro, mesmo que haja licença/acesso no hub).
15
+ - **`hostinger.json`**: `{ token }` da API Hostinger (quando o provider é Hostinger); o CLI também já o injeta nos MCPs.
16
+ - **`preferences.json`**: defaults de UX do CLI (agente/provider/última licença); informativo, não load-bearing.
17
+
18
+ Marcador ausente (fallback por token, ou outro ponto de entrada) → decida pelo hub via proxy: `bunx @fazer-ai/agents hub licenses` / `hub whoami`.
19
+
20
+ ## Acesso (fornecido pelo usuário)
21
+
22
+ > Peça **cada item quando a etapa que o usa chegar**, 1-2 por mensagem; **não** despeje esta lista inteira de uma vez (princípio "Uma pergunta de cada vez" no SKILL.md). Ex.: IP do VPS na 01 (a chave SSH só se a sondagem falhar, ver 01); domínio na 01 (DNS); nome de exibição na 01/07; chave do provedor de modelo só perto do import/E2E; número de WhatsApp só na etapa 10.
23
+
24
+ - **VPS:** `root@<VPS_IP>` (no Hostinger, o id da VM é `<VPS_ID>`). **Qual VPS é escolha do usuário**: se a conta tem mais de uma, liste e **pergunte** (nunca escolha). Acesso SSH como `root` (sonde primeiro; só peça ou gere chave se faltar). Coolify pode já estar instalado (brownfield) ou não. Sondagem + comando SSH na `01-vps-dns-ssh.md`.
25
+ - **Sem VPS ainda?** Sugira adquirir uma. Recomendado: Hostinger, pelo [link de parceiro fazer.ai](https://www.hostg.xyz/SHJfs) (cupom `FAZERAI` = 10% de desconto na primeira compra). Em **outro provider**, o usuário cria a VPS lá e fornece IP + chave SSH; o fluxo segue igual (ver `01-vps-dns-ssh.md`).
26
+ - **Domínio:** `<seu-dominio>`. **O domínio raiz é escolha do usuário**: liste os domínios da conta e **pergunte qual** (nunca assuma). Os subdomínios de onboarding ficam livres pra apontar (`agentes.`, `chatwoot.`, `coolify.`, `langfuse.`).
27
+ - **Chave do provedor de modelo** (OpenAI ou outro): o usuário fornece pra preencher o vault do agente (playground/E2E). As demais credenciais entram via deeplink (pending).
28
+ - **Número de WhatsApp** que o usuário controle, se for validar o transporte real no E2E (etapa 10).
29
+
30
+ ## Operacional (comandos remotos)
31
+
32
+ - Chamadas de rede via Bash (ssh/curl) precisam de `dangerouslyDisableSandbox: true`.
33
+ - Pra evitar inferno de aspas em SSH/psql/Rails runner: **base64 local → pipe → `base64 -d`** no destino. É o padrão recorrente de todo o fluxo (ex.: `echo <B64> | base64 -d | docker exec -i coolify-db psql ...`).
34
+
35
+ ## Gates de conta
36
+
37
+ - O **usuário** cria o 1º admin de Coolify/Portainer/Chatwoot/Langfuse/agents no browser; você entrega link + instrução e **espera**, nunca cria por conta própria. Detalhe em `guardrails.md`.
@@ -0,0 +1,67 @@
1
+ # 01: VPS + DNS + SSH
2
+
3
+ ## VPS: qual VPS é escolha do usuário (pergunte, não escolha)
4
+
5
+ O **`<VPS_IP>` vem do usuário**, nunca de um chute. Se ele não disse qual e o MCP lista mais de uma VM na conta (`VPS_getVirtualMachinesV1`), **apresente as opções (id, IP, hostname, plano) e pergunte qual usar**: ter o MCP conectado não autoriza escolher por ele. Confirmada a VPS, **nunca toque em outra** da conta (ver `guardrails.md`). O orquestrador (Coolify, Portainer, outro painel, ou nenhum) já está instalado (brownfield) ou será instalado no deploy do tier (escolhido em 1c).
6
+
7
+ ## SSH: sondar o acesso antes de gerar chave
8
+
9
+ O acesso à VPS é o 1º ponto onde a run costuma emperrar (a run real bateu em `Permission denied`). O jeito de não emperrar: **sonde antes de pedir qualquer coisa**, e quando precisar de uma chave nova, **guie o cadastro em passos curtos e espere**. **Nunca peça "o caminho da chave SSH" de cara** e **nunca use senha de root** (só chave). Ordem:
10
+
11
+ **1. Sonde o acesso** (sem perguntar nada): tente logar com as chaves que o operador já tem (default em `~/.ssh` + agent):
12
+ ```sh
13
+ ssh -o ConnectTimeout=12 -o BatchMode=yes -o StrictHostKeyChecking=accept-new root@<VPS_IP> 'echo OK; hostname'
14
+ ```
15
+ - Saiu `OK` → **há acesso**; siga, **não pergunte nada de chave**. Anote qual chave funcionou pra reusar no resto do fluxo.
16
+ - `Permission denied (publickey…)` ou exit ≠ 0 → **sem acesso ainda**. Antes de gerar uma chave nova, tente a dedicada de uma run anterior: se `~/.ssh/fazer-ai-agents` existe, sonde de novo com ela (`-i ~/.ssh/fazer-ai-agents`). Logou → já está cadastrada, **reuse** (não gere outra nem peça re-paste). Senão, passo 2. (`BatchMode=yes` evita travar pedindo senha; `dangerouslyDisableSandbox: true` é obrigatório por ser rede.)
17
+
18
+ **2. Sem acesso: gere a chave dedicada e mostre a pública pro operador cadastrar.** Use `scripts/sshkey.py` (não monte o `ssh-keygen` à mão): ele chama o `ssh-keygen` com **argv direto**, então a passphrase vazia passa em qualquer SO. No PowerShell (Windows) o `ssh-keygen -N ""` cru **perde** o argumento vazio, cai no prompt interativo e **trava**.
19
+ ```sh
20
+ python3 scripts/sshkey.py generate --name fazer-ai-agents --comment fazer-ai-onboarding
21
+ ```
22
+ Saída JSON com `public_key` (idempotente: se a chave já existe, só reimprime). **Use o nome FIXO `fazer-ai-agents`, literal, nunca um sufixo inventado nem um placeholder** (`fazer-ai-<algo>`): com nome estável o generate reusa a MESMA chave em toda run, e o operador cola a pública **uma vez só**. Nome novo a cada vez faz a chave que ele colou "sumir" (o agente passa a usar outra) e força re-paste, o atrito recorrente aqui. Mostre a linha `ssh-ed25519 …` e **guie o cadastro no painel em passos curtos** (você **não** faz isto pela API, ver *Nota MCP*), em linguagem clara: "gerei uma chave de acesso; cole a linha abaixo no painel da sua VPS pra eu conseguir entrar":
23
+ - **Hostinger:** painel da VPS → card **"Chave SSH"** → **"Gerenciar"** → **"+ Chave SSH"** → cole a chave pública → **"Salvar"**.
24
+ - **Outro provedor:** o equivalente no painel dele ("SSH Keys" / "Add SSH key" da VPS).
25
+
26
+ **3. Espere o cadastro e confirme sozinho.** Em vez de pedir "me avise quando cadastrar", deixe o helper **aguardar** o acesso (faz poll do SSH e detecta sozinho quando a chave entra). **Rode em background, não em foreground** (o poll trava o seu turno enquanto o operador cola a chave); retome quando sair:
27
+ ```sh
28
+ python3 scripts/sshkey.py wait-access --ssh root@<VPS_IP> --ssh-opts "-i ~/.ssh/fazer-ai-agents" # em background, non-blocking
29
+ ```
30
+ `ok:true` → há acesso, siga. `ok:false` (timeout) → aí sim volte ao operador (ver troubleshooting abaixo). Daí use o **comando de trabalho** no resto do fluxo, com a chave que funcionou (a dedicada `~/.ssh/fazer-ai-agents`, ou a existente que a sondagem achou no passo 1):
31
+ ```sh
32
+ ssh -o IdentitiesOnly=yes -o IdentityAgent=none -o ConnectTimeout=12 -o BatchMode=yes \
33
+ -o StrictHostKeyChecking=accept-new -i ~/.ssh/fazer-ai-agents root@<VPS_IP>
34
+ ```
35
+ Bash com rede → `dangerouslyDisableSandbox: true`. Um comando de **uma linha sem** `"`/`$()`/`{{…}}`/`(` pode ir inline; qualquer **script não-trivial** (multi-linha, com aspas/`$()`/`{{…}}`/heredoc) vai por arquivo `.sh` + `scripts/remote.py`, nunca inline no `ssh <host> '…'` nem por here-string no PowerShell (aspas comidas + BOM; ver `gotchas.md`). **Windows:** passe o caminho da chave no `--ssh-opts` (helpers preservam as barras `\`); todo helper que fala SSH aceita `--ssh-opts "-i <caminho>"`.
36
+
37
+ ### Se persistir "Permission denied (publickey,password)"
38
+
39
+ O sintoma da run real. Não é um beco sem saída; quase sempre é uma destas causas, na ordem pra checar:
40
+
41
+ 1. **A chave ainda não foi salva no painel** (ou salvou em outra VPS). É a causa nº 1: o poll do passo 3 continua `ok:false` porque a chave não entrou. Peça ao operador, **em linguagem simples**, pra confirmar que colou a chave **nesta** VPS e clicou Salvar: "não consegui entrar ainda; você chegou a colar aquela chave no painel desta VPS e salvar?".
42
+ 2. **Chave errada apontada.** Confirme que o `-i` aponta pra chave cuja **pública** foi cadastrada (`~/.ssh/fazer-ai-agents` se você gerou no passo 2). Se a sondagem do passo 1 achou uma chave existente, use **essa**, não a dedicada.
43
+ 3. **Provedor só injeta a chave em provisionamento.** Em alguns provedores, cadastrar a chave no painel só a aplica ao **criar/recriar** a VM, não numa VM já rodando (é o caso da API Hostinger, ver *Nota MCP*). Se o painel não aplica a quente, o operador precisa adicioná-la por um acesso que já funcione (um console web do provedor, ou uma chave que já entra) e colá-la nas chaves autorizadas do root (o arquivo padrão de chaves SSH, em `~/.ssh/`). **Nunca** recrie a VM pra forçar a chave: isso apaga os dados.
44
+ 4. **Login de root desabilitado.** Se `PermitRootLogin` está `no` no `sshd`, nem a chave certa entra como root. Aí o operador loga com o usuário dele (com sudo) e o fluxo segue com esse usuário + `sudo` (o `remote.py` tem `--sudo`).
45
+
46
+ Só depois de descartar 1 e 2 (as comuns) leve 3/4 ao operador. Enquanto isso, **não** caia pra senha de root nem invente contornos.
47
+
48
+ ### Nota MCP: cadastre a chave pelo painel, não pela API
49
+ A API de chaves não serve aqui: `attach`/`create` registram a chave mas não a injetam numa VM em execução (só aplicam em provisionamento/`recreate`, que apaga dados). Por isso a skill cadastra a chave **pelo painel da VPS** e confirma o acesso **por sondagem** (passo 3).
50
+
51
+ ## DNS (MCP Hostinger, domínio `<seu-dominio>`)
52
+
53
+ O **domínio raiz (`<seu-dominio>`) é escolha do usuário**: liste os domínios da conta (`domains_getDomainListV1`) e **pergunte qual usar como raiz**: nunca assuma um porque "estava na conta". Definido o raiz, crie os A-records apontando pra `<VPS_IP>` (os três da app são o contrato; ver 1c):
54
+ - `agentes.<seu-dominio>`: fazer.ai agents
55
+ - `chatwoot.<seu-dominio>`: Chatwoot (Pro ou OSS)
56
+ - `langfuse.<seu-dominio>`: Langfuse (recomendado)
57
+ - **painel do orquestrador** (se houver e você quiser um domínio limpo): `coolify.` (Tier A) / `portainer.` (Tier B); outro painel usa o próprio; no compose genérico (Tier C) pode não haver painel.
58
+
59
+ Tools do `hostinger-dns`: `DNS_getDNSRecordsV1` (inspecionar), `DNS_updateDNSRecordsV1` (setar). **Monitore a propagação** antes de prosseguir: o ACME (Traefik do Coolify, Caddy do Portainer, ou o proxy do tier) só emite o certificado quando o A-record resolve. Sem isso, os serviços sobem mas ficam 503/sem TLS.
60
+
61
+ ## Outro provider (VPS/DNS fora da Hostinger)
62
+
63
+ Se o usuário usa outro provider de VPS e/ou DNS, **não há MCP da Hostinger**. Pergunte qual provider ele usa e conduza com base no seu conhecimento dele. Só o **provisionamento de VPS/DNS** muda de ferramenta; do SSH em diante (deploy do tier, agents, branding, bind) o fluxo é idêntico.
64
+
65
+ - **DNS:** crie os **mesmos A-records** (`agentes.`/`chatwoot.`/`langfuse.` + o painel do tier → IP da VPS) pelo painel/CLI/API do provider do usuário. Monitore a propagação igual (o ACME só emite o cert quando o A-record resolve).
66
+ - **VPS:** o usuário cria a VPS no provider dele e fornece **IP + chave SSH**. Confirme que a porta 22 está aberta e que dá pra logar como root (ou com sudo). O resto da `01` (comando SSH, base64-pipe) vale igual.
67
+ - **Sem VPS ainda?** Sugira adquirir (recomendado: Hostinger, [link de parceiro fazer.ai](https://www.hostg.xyz/SHJfs), cupom `FAZERAI` = 10% de desconto na primeira compra). Detalhe na `00-prereqs-and-access.md`.