@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.
- package/LICENSE +21 -0
- package/README.md +112 -0
- package/dist/agents/claude.js +152 -0
- package/dist/agents/codex.js +155 -0
- package/dist/agents/detect.js +15 -0
- package/dist/agents/handoff.js +22 -0
- package/dist/agents/hermes-skills.js +177 -0
- package/dist/agents/hermes.js +474 -0
- package/dist/agents/index.js +57 -0
- package/dist/agents/manual.js +22 -0
- package/dist/agents/other.js +39 -0
- package/dist/agents/shell.js +15 -0
- package/dist/agents/types.js +2 -0
- package/dist/config.js +48 -0
- package/dist/exec.js +279 -0
- package/dist/hostinger.js +75 -0
- package/dist/hub-command.js +144 -0
- package/dist/index.js +726 -0
- package/dist/licenses.js +93 -0
- package/dist/mcp.js +100 -0
- package/dist/oauth.js +578 -0
- package/dist/onboarding-marker.js +48 -0
- package/dist/preferences.js +40 -0
- package/dist/skills/agents-dev/SKILL.md +37 -0
- package/dist/skills/agents-dev/gotchas.md +6 -0
- package/dist/skills/agents-dev/guardrails.md +6 -0
- package/dist/skills/agents-dev/references/00-get-the-code.md +28 -0
- package/dist/skills/agents-dev/references/01-layout-and-bun-check.md +29 -0
- package/dist/skills/agents-dev/references/02-free-full-and-invariants.md +7 -0
- package/dist/skills/agents-dev/references/03-implement.md +9 -0
- package/dist/skills/agents-dev/references/04-own-image-and-deploy.md +13 -0
- package/dist/skills/agents-onboarding/SKILL.md +80 -0
- package/dist/skills/agents-onboarding/gotchas.md +157 -0
- package/dist/skills/agents-onboarding/guardrails.md +65 -0
- package/dist/skills/agents-onboarding/references/00-prereqs-and-access.md +37 -0
- package/dist/skills/agents-onboarding/references/01-vps-dns-ssh.md +67 -0
- package/dist/skills/agents-onboarding/references/01b-brownfield.md +70 -0
- package/dist/skills/agents-onboarding/references/01c-pick-tier.md +61 -0
- package/dist/skills/agents-onboarding/references/02-coolify.md +109 -0
- package/dist/skills/agents-onboarding/references/03-chatwoot-pro.md +61 -0
- package/dist/skills/agents-onboarding/references/04-agents-image.md +46 -0
- package/dist/skills/agents-onboarding/references/05-langfuse.md +45 -0
- package/dist/skills/agents-onboarding/references/06-setup-and-mcp.md +47 -0
- package/dist/skills/agents-onboarding/references/08-agent-import.md +55 -0
- package/dist/skills/agents-onboarding/references/09-chatwoot-bind.md +41 -0
- package/dist/skills/agents-onboarding/references/10-validate-e2e.md +34 -0
- package/dist/skills/agents-onboarding/references/agent-features.md +61 -0
- package/dist/skills/agents-onboarding/references/chatwoot-hub-register.md +69 -0
- package/dist/skills/agents-onboarding/references/deploy-b-portainer.md +138 -0
- package/dist/skills/agents-onboarding/references/deploy-c-compose.md +64 -0
- package/dist/skills/agents-onboarding/samples/agents/README.md +23 -0
- package/dist/skills/agents-onboarding/samples/agents/maria-clinica-moreira.json +313 -0
- package/dist/skills/agents-onboarding/scripts/chatwoot-admin.py +248 -0
- package/dist/skills/agents-onboarding/scripts/coolify.py +552 -0
- package/dist/skills/agents-onboarding/scripts/docker-status.py +129 -0
- package/dist/skills/agents-onboarding/scripts/gen-onboarding-env.ts +187 -0
- package/dist/skills/agents-onboarding/scripts/harbor-login.py +118 -0
- package/dist/skills/agents-onboarding/scripts/langfuse-verify.py +118 -0
- package/dist/skills/agents-onboarding/scripts/portainer-brownfield.py +115 -0
- package/dist/skills/agents-onboarding/scripts/remote.py +198 -0
- package/dist/skills/agents-onboarding/scripts/sshkey.py +140 -0
- package/dist/skills/agents-onboarding/templates/chatwoot/.env.example +30 -0
- package/dist/skills/agents-onboarding/templates/chatwoot/README.md +65 -0
- package/dist/skills/agents-onboarding/templates/chatwoot/docker-compose.coolify.yml +136 -0
- package/dist/skills/agents-onboarding/templates/chatwoot/docker-compose.yml +139 -0
- package/dist/skills/agents-onboarding/templates/docker-compose.coolify.yml +73 -0
- package/dist/skills/agents-onboarding/templates/docker-compose.portainer.yml +132 -0
- package/dist/skills/agents-onboarding/templates/docker-compose.prod.yml +85 -0
- package/dist/skills/agents-onboarding/templates/langfuse/.env.example +59 -0
- package/dist/skills/agents-onboarding/templates/langfuse/README.md +132 -0
- package/dist/skills/agents-onboarding/templates/langfuse/docker-compose.coolify.yml +189 -0
- package/dist/skills/agents-onboarding/templates/langfuse/docker-compose.yml +185 -0
- package/dist/skills/agents-operation/SKILL.md +42 -0
- package/dist/skills/agents-operation/gotchas.md +61 -0
- package/dist/skills/agents-operation/guardrails.md +26 -0
- package/dist/skills/agents-operation/references/00-production-safety.md +24 -0
- package/dist/skills/agents-operation/references/01-diagnose.md +34 -0
- package/dist/skills/agents-operation/references/02-reproduce.md +22 -0
- package/dist/skills/agents-operation/references/03-adjust.md +36 -0
- package/dist/skills/agents-operation/references/04-validate-and-apply.md +31 -0
- package/dist/ui-select.js +279 -0
- package/dist/ui.js +167 -0
- package/package.json +53 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Pós-import: resolver avisos + ligar features do agente
|
|
2
|
+
|
|
3
|
+
Depois do import (`08`), o agente entra **disabled + test**. Antes de validar (`10`), **resolva todos os
|
|
4
|
+
avisos de configuração** (não bloqueiam o boot, mas degradam a qualidade) e ligue as features opcionais que
|
|
5
|
+
o agente usa. Trate a seção 1 como **gate**, não como "nice to have".
|
|
6
|
+
|
|
7
|
+
## 1. Avisos pós-import (gate, obrigatório)
|
|
8
|
+
|
|
9
|
+
O editor do agente lista "Avisos de configuração". Resolva **cada um** (ou desligue a feature
|
|
10
|
+
conscientemente):
|
|
11
|
+
|
|
12
|
+
- **KB sem indexar.** Os docs do import entram UNINDEXED e só indexam com o embedding por-tenant ligado
|
|
13
|
+
+ o OpenAI preenchido (sem isso ficam UNINDEXED, não FAILED). Sequência (detalhe em
|
|
14
|
+
[`08-agent-import.md`](08-agent-import.md) §4-5): setar embedding por-tenant → `knowledge_reindex` da
|
|
15
|
+
base (devolve `blocked` + `fillAt` se falta preencher a credencial; `include_failed:true` recupera
|
|
16
|
+
FAILED reais), até **todos READY**.
|
|
17
|
+
Depois **verifique grounding**: pergunte no playground algo que só a KB sabe e confirme que a resposta usa
|
|
18
|
+
o conteúdo indexado. KB não-READY = sem grounding = critério de aceite (`10`) não batido.
|
|
19
|
+
- **STT/TTS/visão ligados sem chave.** Conecte a credencial (deeplink) **ou** desligue a feature. Não deixe
|
|
20
|
+
o aviso aberto.
|
|
21
|
+
|
|
22
|
+
## 2. Voz (STT/TTS): opcional, por-agente
|
|
23
|
+
|
|
24
|
+
MCP `agent_settings_set` (dry-run por padrão; `credentialRef` aceita o **nome** da entrada do vault):
|
|
25
|
+
|
|
26
|
+
```jsonc
|
|
27
|
+
agent_settings_set {
|
|
28
|
+
"agent_id": "<id>",
|
|
29
|
+
"stt": { "enabled": true, "provider": "openai", "model": "whisper-1", "language": "pt", "credentialRef": "<nome no vault>" },
|
|
30
|
+
"tts": { "mode": "mirror", "provider": "openai", "model": "tts-1", "voice": "alloy", "credentialRef": "<nome no vault>" }
|
|
31
|
+
} // dry_run:false pra aplicar
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- Campos (fonte `src/modules/stt/settings.ts`, `src/modules/tts/settings.ts`): STT
|
|
35
|
+
`enabled/provider/model/language/credentialRef/baseURL`; TTS
|
|
36
|
+
`mode (never|mirror|preference)/provider/model/voice/credentialRef/normalize`. ElevenLabs exige `voice`.
|
|
37
|
+
- **`agent_settings_set` é tool separada do `agent_update`** (este é nome/enabled/mode/modelConfig). Os
|
|
38
|
+
blocos de comportamento (stt/tts/split/handoff/etc.) vão pelo `agent_settings_set`.
|
|
39
|
+
- REST equivalente: `PATCH /api/v1/agents/:id { settings: { stt, tts } }` (aqui o `credentialRef` tem que
|
|
40
|
+
ser `vault:<id>`; REST não resolve por nome).
|
|
41
|
+
- A chave (OpenAI/ElevenLabs) é credencial do vault, preenchida por deeplink como qualquer outra (`08` §2).
|
|
42
|
+
|
|
43
|
+
## 3. Google (Calendar/Drive): opcional, **fora do MCP por design**
|
|
44
|
+
|
|
45
|
+
As tools de Google usam uma credencial kind `google_oauth`. **Não há MCP write tool pra conectar o Google**
|
|
46
|
+
(o segredo e o consent nunca cruzam o MCP). É um fluxo de **console** (o usuário faz):
|
|
47
|
+
|
|
48
|
+
1. Pré: no Google Cloud, um OAuth 2.0 Client ID (Web) com a redirect URI
|
|
49
|
+
**`${PUBLIC_URL}/api/v1/oauth/google/callback`** registrada.
|
|
50
|
+
2. No console `/resources/vault`: criar credencial kind `google_oauth` com Client ID + Client Secret.
|
|
51
|
+
3. Na `GoogleOAuthSection`: escolher os scopes (Calendar/Drive/...) → "Sign in with Google" → popup de
|
|
52
|
+
consent → o callback grava os tokens (criptografados) na entrada do vault.
|
|
53
|
+
4. Status: "Connected as <email>". A partir daí, as tools de Calendar/Drive resolvem por `vault:<id>` (token
|
|
54
|
+
renovado automaticamente).
|
|
55
|
+
|
|
56
|
+
Endpoints (fonte `src/api/v1/oauth-google.controller.ts`): `POST /api/v1/vault/:id/oauth/google/authorize {scopes}`,
|
|
57
|
+
`GET /api/v1/oauth/google/callback`, `GET .../status`, `POST .../disconnect`. Não confundir com o login
|
|
58
|
+
social do `docs/google-oauth.md` (é outra coisa).
|
|
59
|
+
|
|
60
|
+
> Princípio: tudo o mais é MCP-first, mas **o connect do Google é console-only por design**. Entregue o
|
|
61
|
+
> link/instrução ao usuário; o agente não vê o segredo.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Licenciar a instância Chatwoot no hub (Kanban/Pro)
|
|
2
|
+
|
|
3
|
+
Habilita as features **Pro** (Kanban etc.) numa instância Chatwoot **Pro já deployada**. Duas coisas são
|
|
4
|
+
necessárias e **distintas**:
|
|
5
|
+
|
|
6
|
+
- A **imagem Pro** (`harbor.fazer.ai/chatwoot/fazer-ai/chatwoot-pro`) traz o **código** (driver de Kanban,
|
|
7
|
+
Baileys). Sem ela, não há Pro. A edição é escolhida no deploy (ver [`03-chatwoot-pro.md`](03-chatwoot-pro.md)).
|
|
8
|
+
- A **assinatura no hub** dá a habilitação em runtime: **imagem Pro sem assinatura ativa = features
|
|
9
|
+
travadas.** Por isso existe o passo do Refresh (não é restart de container).
|
|
10
|
+
|
|
11
|
+
## Quando: happy-path se há licença
|
|
12
|
+
|
|
13
|
+
A edição é decidida **no deploy** pelo marcador do CLI `~/.fazer-ai/onboarding.json`
|
|
14
|
+
(`chatwootTier` + `chatwootLicenseId`), com fallback pro `hub licenses` se o marcador faltar:
|
|
15
|
+
- **`chatwootTier: "pro"`** (ou, sem marcador, há licença CHATWOOT no hub) → deploy da **imagem Pro** e **estes passos são happy-path**: registrar + atachar (use `chatwootLicenseId`) + Refresh pra ligar o Kanban. Não pule.
|
|
16
|
+
- **`chatwootTier: "community"`** (ou, sem marcador, sem licença) → deploy da imagem **OSS** (sem Kanban) e segue; nada a fazer aqui.
|
|
17
|
+
- **`chatwootSource: "existing"`** (Chatwoot BYO, sem `chatwootTier` no marcador) → detecte Pro/OSS pela **imagem** (etapa 1b); não assuma Pro. Um Pro existente **sem** Kanban pode ser licenciado por estes passos, mas **não é forçado** (só se o usuário quiser); OSS não tem Kanban.
|
|
18
|
+
|
|
19
|
+
> **Sem licença e o usuário quer Kanban?** Sugira virar membro Pro da comunidade do Lucas Moreira
|
|
20
|
+
> ([lucasmoreira.ai](https://lucasmoreira.ai)): ganha licença grátis do Kanban (1 conta no plano mensal,
|
|
21
|
+
> 2 ilimitadas no anual). Depois é só rodar o CLI de novo e escolher "já me tornei membro" pra a nova
|
|
22
|
+
> licença aparecer; ou seguir em OSS.
|
|
23
|
+
|
|
24
|
+
> Pré-requisitos: `FRONTEND_URL` setado no container do Chatwoot (vira o host que identifica a instância e
|
|
25
|
+
> gateia o Refresh). As ops do hub saem pelo **proxy do CLI** (`bunx @fazer-ai/agents hub …`), que usa
|
|
26
|
+
> o OAuth do `~/.fazer-ai/oauth.json` do bootstrap (sem hub MCP na sessão); se ele expirou (erro de auth),
|
|
27
|
+
> o operador re-roda o CLI. Writes do hub são **dry-run por padrão** (aplique com `--apply`); mexa só na sua
|
|
28
|
+
> própria licença/instância (ver `guardrails.md`).
|
|
29
|
+
|
|
30
|
+
## Passos
|
|
31
|
+
|
|
32
|
+
> **Ao pedir o OK do usuário** para aplicar (passos 2 a 4), fale em linguagem de usuário: "vou ativar o seu plano Pro nesta instalação, o que libera o Kanban no Chatwoot". **Não** cite `attach-license`, "Refresh da assinatura", "idempotente" nem os comandos: são o seu mecanismo. Frases boas × ruins em `guardrails.md`.
|
|
33
|
+
|
|
34
|
+
1. **Identidade da instância.** Pegue o host/identifier que o hub casa, direto do Chatwoot (read-only, sem hub):
|
|
35
|
+
```sh
|
|
36
|
+
python3 scripts/chatwoot-admin.py installation-id --ssh root@<VPS_IP> --container <chatwoot-rails-container>
|
|
37
|
+
```
|
|
38
|
+
Devolve `frontend_url` (o host) + `installation_identifier`. O `identifier` que o hub usa é o **host** (ex.: `chatwoot.<seu-dominio>`).
|
|
39
|
+
2. **Instância no hub.** Confira se já existe e crie se faltar (dry-run primeiro; `--apply` pra valer):
|
|
40
|
+
```sh
|
|
41
|
+
bunx @fazer-ai/agents hub instances
|
|
42
|
+
bunx @fazer-ai/agents hub create-instance --identifier chatwoot.<seu-dominio> --apply
|
|
43
|
+
```
|
|
44
|
+
3. **Atacha a licença** (uma feature por instância; os tipos têm que bater). O `--instance` é o id que aparece em `hub instances`:
|
|
45
|
+
```sh
|
|
46
|
+
bunx @fazer-ai/agents hub attach-license --license <licença CHATWOOT> --instance <id> --apply
|
|
47
|
+
```
|
|
48
|
+
4. **Refresh + verify na instância** (o botão "Refresh" do super admin) via `scripts/chatwoot-admin.py`, que roda o job e reporta os configs da assinatura (NÃO despeja valores crus que poderiam ser segredo):
|
|
49
|
+
```sh
|
|
50
|
+
python3 scripts/chatwoot-admin.py refresh-subscription --ssh root@<VPS_IP> --container <chatwoot-rails-container>
|
|
51
|
+
```
|
|
52
|
+
**`jitter_applied: true` é obrigatório** (o script já passa). Sem ele, o job só se reagenda (janela determinística de até 30 min) e o sync nem roda. A saída traz `config_keys` (deve listar os `FAZER_AI_SUBSCRIPTION_*`) e `diagnostics` (`SYNC_ERROR_MESSAGE` nil = ok; `VERIFIED_AT` recente = ok). No super admin (`/super_admin/settings`), "fazer.ai Subscription" fica ativa e o Kanban aparece.
|
|
53
|
+
|
|
54
|
+
## Erros comuns
|
|
55
|
+
|
|
56
|
+
- **`hub …` diz que "o hub não respondeu ao refresh da sessão" / instabilidade:** é **transitório** (a
|
|
57
|
+
sessão segue válida; o refresh token nem foi consumido): **rode o MESMO comando de novo** em instantes.
|
|
58
|
+
Só **"sessão expirada/ausente"** (erro de auth real) pede re-rodar o CLI de onboarding pra logar no
|
|
59
|
+
browser. Em nenhum dos casos contorne o `hub` indo por REST/MCP por fora: ou re-tenta, ou re-loga.
|
|
60
|
+
- **Kanban não aparece com imagem Pro:** faltou o passo 3. A imagem traz o código; a assinatura libera em
|
|
61
|
+
runtime. Rode o Refresh.
|
|
62
|
+
- **`FRONTEND_URL` vazio:** o controller do Refresh recusa, e o `installation_host` enviado ao hub fica
|
|
63
|
+
vazio. Sete antes.
|
|
64
|
+
- **403 / inativo no `/api/ping`:** a licença não está atachada à instância certa no hub. Confira
|
|
65
|
+
`bunx @fazer-ai/agents hub get-license --license <id>` / `hub get-instance --instance <id>` (casamento por identifier/host).
|
|
66
|
+
- **Assinatura "out of sync" > 3 dias:** o job auto-desativa (`auto_deactivate_if_stale`). Rode o Refresh
|
|
67
|
+
pra re-sincronizar.
|
|
68
|
+
|
|
69
|
+
OSS não tem nada disso (sem Kanban). Migrar OSS → Pro = re-deploy com a imagem Pro + estes passos.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Deploy Tier B: Portainer
|
|
2
|
+
|
|
3
|
+
Adapter de deploy **Tier B**. O agente, da máquina do operador, dirige um **Portainer** pela HTTP API:
|
|
4
|
+
gera os secrets, deploya cada stack a partir de uma string de compose, e sobe um **Caddy bundled** que
|
|
5
|
+
termina TLS com certificado real automático (sem labels Traefik, sem polling do socket). É o companion
|
|
6
|
+
por-plataforma do [contrato (1c)](01c-pick-tier.md); os invariantes (duas roles de DB, pgvector, réplica
|
|
7
|
+
única) valem igual. Para BYO-proxy use `templates/docker-compose.prod.yml`; o Tier B usa o Caddy-bundled
|
|
8
|
+
`templates/docker-compose.portainer.yml`.
|
|
9
|
+
|
|
10
|
+
## Artefatos (incluídos nesta skill, relativos à raiz dela)
|
|
11
|
+
|
|
12
|
+
| Artefato | Papel |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| `templates/docker-compose.portainer.yml` | agents + Postgres + **Caddy bundled** (auto-HTTPS). Self-contained (uma string pro Portainer). |
|
|
15
|
+
| `scripts/gen-onboarding-env.ts` | Gera o `.env` (duas roles de DB, JWT/ENCRYPTION, `CADDY_DOMAIN`, `ACME_EMAIL` opcional). |
|
|
16
|
+
| `templates/chatwoot/` | Stack do Chatwoot, Pro (Harbor) ou OSS (público): um compose genérico, edição por env. Ver [`03-chatwoot-pro.md`](03-chatwoot-pro.md). |
|
|
17
|
+
| `templates/langfuse/` | Langfuse opcional (tracing); inclui o MinIO que a ingestion v3 exige. Ver [`05-langfuse.md`](05-langfuse.md). |
|
|
18
|
+
| `scripts/portainer-brownfield.py` | Descoberta brownfield read-only (inventário + decisão por serviço) via a API do Portainer. |
|
|
19
|
+
|
|
20
|
+
## Por que self-contained + Caddy bundled
|
|
21
|
+
|
|
22
|
+
A API "deploy from string" do Portainer recebe **um** documento de compose, então o
|
|
23
|
+
`templates/docker-compose.portainer.yml` repete app + postgres e adiciona um serviço `caddy`. O Caddy monta o
|
|
24
|
+
Caddyfile a partir do env no boot (`CADDY_DOMAIN` → o app; `PORTAINER_DOMAIN` opcional → o painel via
|
|
25
|
+
`host.docker.internal:9443`) e obtém certs via ACME (tls-alpn-01 / http-01).
|
|
26
|
+
|
|
27
|
+
## Pré-requisitos
|
|
28
|
+
|
|
29
|
+
- **DNS**: A-records pro FQDN do app (ex. `agentes.<domínio>`) e, se quiser o painel num domínio limpo,
|
|
30
|
+
`portainer.<domínio>` → o IP do VPS. O ACME valida contra eles, então têm que resolver **antes** do deploy.
|
|
31
|
+
- **Credenciais de registry** pras imagens privadas (configure uma vez no Portainer, passe `Registries:[id]`):
|
|
32
|
+
- `ghcr.io` pra `ghcr.io/fazer-ai/agents` (e pgvector/baileys). Token do GitHub com `read:packages`.
|
|
33
|
+
- `harbor.fazer.ai` pro Chatwoot **Pro** (e fazer.ai agents Pro): a registry credential **per-user**, provisionada
|
|
34
|
+
pelo proxy do CLI (`bunx @fazer-ai/agents hub registry-credential --apply --out harbor.secret`; imprime o
|
|
35
|
+
`username`, grava o secret em `harbor.secret`). Chatwoot OSS não precisa de registry privado.
|
|
36
|
+
|
|
37
|
+
## 1. Instalar o Portainer
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
docker volume create portainer_data
|
|
41
|
+
docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always \
|
|
42
|
+
-v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data \
|
|
43
|
+
portainer/portainer-ce:lts
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
O Portainer 2.39+ exige criar o primeiro admin numa janela que tranca ~5 min após o start (reinicie o container pra reabrir). O **usuário** cria o 1º admin **no browser** em `https://<ip>:9443` (você entrega o link e **espera**) e, em *User settings → Access tokens*, gera uma **API key** (`<api-key>`) que te passa. Nunca crie o admin por conta própria.
|
|
47
|
+
|
|
48
|
+
Com a API key (header `X-API-Key`), ache o endpoint id (o ambiente Docker local, geralmente `1`; crie se a lista vier vazia):
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
curl -sk https://localhost:9443/api/endpoints -H "X-API-Key: <api-key>"
|
|
52
|
+
# se vazio, crie o endpoint local:
|
|
53
|
+
curl -sk -X POST https://localhost:9443/api/endpoints -H "X-API-Key: <api-key>" -F Name=local -F EndpointCreationType=1
|
|
54
|
+
```
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 2. Registrar os registries privados (uma vez)
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
curl -sk -X POST https://localhost:9443/api/registries -H "X-API-Key: <api-key>" -H 'Content-Type: application/json' \
|
|
61
|
+
-d '{"Name":"ghcr","Type":3,"URL":"ghcr.io","Authentication":true,"Username":"<gh-user>","Password":"<gh-token>"}'
|
|
62
|
+
# Só Pro:
|
|
63
|
+
curl -sk -X POST https://localhost:9443/api/registries -H "X-API-Key: <api-key>" -H 'Content-Type: application/json' \
|
|
64
|
+
-d '{"Name":"harbor","Type":3,"URL":"harbor.fazer.ai","Authentication":true,"Username":"robot$...","Password":"<secret>"}'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`Type:3` é registry Custom (serve pra qualquer). Capture cada `Id` e passe no array `Registries` do stack
|
|
68
|
+
pra o `pull_policy: always` autenticar.
|
|
69
|
+
|
|
70
|
+
## 3. Deploy do stack agents
|
|
71
|
+
|
|
72
|
+
Gere o env, depois crie o stack a partir da string de compose:
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
bun scripts/gen-onboarding-env.ts --public-url https://agentes.<domínio> --acme-email voce@<domínio>
|
|
76
|
+
# -> .env com CADDY_DOMAIN, as duas URLs de role do DB, JWT_SECRET, ENCRYPTION_KEY. Adicione
|
|
77
|
+
# PORTAINER_DOMAIN pra também servir o painel pelo mesmo Caddy.
|
|
78
|
+
|
|
79
|
+
curl -sk -X POST "https://localhost:9443/api/stacks/create/standalone/string?endpointId=1" \
|
|
80
|
+
-H "X-API-Key: <api-key>" -H 'Content-Type: application/json' \
|
|
81
|
+
-d "$(jq -n --arg c "$(cat templates/docker-compose.portainer.yml)" --argjson env "$ENV_JSON" \
|
|
82
|
+
'{Name:"agents", StackFileContent:$c, Env:$env, Registries:[<ghcr-id>]}')"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`Env` é um array de `{name,value}` do `.env` gerado. O entrypoint da imagem roda **db-bootstrap →
|
|
86
|
+
migrate → serve**; o Caddy obtém o(s) cert(s) no primeiro boot. Atualize um stack depois com
|
|
87
|
+
`PUT /api/stacks/{id}?endpointId=1` (`{StackFileContent, Env, PullImage:true, Prune:false}`).
|
|
88
|
+
|
|
89
|
+
## 4. Verificar
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
curl -sS -o /dev/null -w '%{http_code} verify=%{ssl_verify_result}\n' https://agentes.<domínio>/api/health # 200 verify=0
|
|
93
|
+
echo | openssl s_client -connect agentes.<domínio>:443 -servername agentes.<domínio> 2>/dev/null \
|
|
94
|
+
| openssl x509 -noout -issuer # issuer= ... O=Let's Encrypt
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Um "stack deployed" verde no Portainer não basta: confirme que o issuer do cert é Let's Encrypt e que
|
|
98
|
+
`/api/auth/me` retorna JSON (`setupRequired:true` num DB novo). HTTP→HTTPS é um 308 do Caddy.
|
|
99
|
+
|
|
100
|
+
## 5. Chatwoot + Langfuse
|
|
101
|
+
|
|
102
|
+
Deploye `templates/chatwoot/` (Pro vs OSS por edição, ver [`03-chatwoot-pro.md`](03-chatwoot-pro.md)) e, se
|
|
103
|
+
selecionado, `templates/langfuse/` (ver [`05-langfuse.md`](05-langfuse.md)) do mesmo jeito (stack-create from
|
|
104
|
+
string + `Env[]` + os `Registries` relevantes). Frontear `chatwoot.<domínio>` / `langfuse.<domínio>` com
|
|
105
|
+
TLS: aponte um site Caddy pra porta publicada de cada um, ou dê a cada um seu stack e deixe o Caddy
|
|
106
|
+
compartilhado fazer o proxy.
|
|
107
|
+
|
|
108
|
+
## Brownfield: o Portainer já tem serviços
|
|
109
|
+
|
|
110
|
+
Quando o operador escolheu Portainer mas **já roda Portainer + alguns serviços** (ex. Chatwoot), sonde
|
|
111
|
+
antes de instalar e decida **por serviço**, nunca destrua dados do operador. A sondagem é nativa da API do
|
|
112
|
+
Portainer (o equivalente Coolify é a etapa [`01b-brownfield.md`](01b-brownfield.md)):
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
PORTAINER_API_KEY=$KEY PORTAINER_ENDPOINT_ID=1 python3 scripts/portainer-brownfield.py
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Ele lista os stacks, faz fingerprint de cada container por imagem, sinaliza **quem ocupa 80/443** (um
|
|
119
|
+
ingress existente faz o stack Caddy-bundled conflitar → reuse-o ou troque pro `templates/docker-compose.prod.yml`
|
|
120
|
+
BYO-proxy) e imprime uma matriz de decisão: `agents` saudável → reusa; Chatwoot presente → reusa
|
|
121
|
+
(`chatwoot-pro` Harbor e OSS são ambos válidos); Chatwoot ausente → instala Pro se há assinatura no hub,
|
|
122
|
+
senão OSS; Langfuse ausente → instala só se selecionado.
|
|
123
|
+
|
|
124
|
+
## Gotcha: o header de auth do Chatwoot pelo Caddy bundled
|
|
125
|
+
|
|
126
|
+
O Chatwoot documenta o header de auth da API como `api_access_token` (underscores), mas reverse proxies
|
|
127
|
+
descartam headers com underscore no nome (nginx tem `underscores_in_headers off`; o Caddy bundled do
|
|
128
|
+
Chatwoot também descarta). Efeito: o mesmo admin token enviado ao Chatwoot **pelo Caddy** como
|
|
129
|
+
`api_access_token` é rejeitado (401), enquanto **direto no puma** (sem proxy) autentica (200). O Rails
|
|
130
|
+
(Rack) mapeia `-` e `_` pra mesma env var, então a grafia com **hífen** `api-access-token` é lida igual
|
|
131
|
+
pelo Chatwoot **e** sobrevive ao Caddy (200). O fazer.ai agents **manda o hífen** por padrão, então `deployment_connect`
|
|
132
|
+
e as tools HTTP do agente funcionam contra a URL pública fronteada pelo Caddy sem workaround. Se você
|
|
133
|
+
escrever uma integração Chatwoot à mão, use `api-access-token`, nunca o underscore.
|
|
134
|
+
|
|
135
|
+
## O que entrega ao contrato
|
|
136
|
+
|
|
137
|
+
Os 5 outputs do [contrato (1c)](01c-pick-tier.md): `agentes.`/`chatwoot.`/`langfuse.` em HTTPS, agents com as
|
|
138
|
+
duas roles + token de `/setup`, admin token do Chatwoot, Langfuse+MinIO. → siga pra **etapa 6**.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Deploy Tier C: Compose genérico (VM crua ou painel sem trilha dedicada)
|
|
2
|
+
|
|
3
|
+
O caminho catch-all: tudo que não é Coolify (Tier A) nem Portainer (Tier B). Cobre dois casos com a mesma
|
|
4
|
+
base de artefatos:
|
|
5
|
+
|
|
6
|
+
- **VM crua**, só Docker + `docker compose`: você sobe cada stack à mão e cuida do TLS.
|
|
7
|
+
- **Painel sem trilha dedicada** (Easypanel, Dokploy, CapRover, etc.): **não há doc específico na skill, por
|
|
8
|
+
escolha.** Pegue os passos genéricos abaixo e adapte ao painel com o que você já sabe dele (como ele cria
|
|
9
|
+
um projeto Compose, injeta env, e anexa domínio + emite TLS). O alvo é sempre o
|
|
10
|
+
[contrato (1c)](01c-pick-tier.md); o painel é só o meio.
|
|
11
|
+
|
|
12
|
+
## Tem um painel? Adapte, não procure trilha
|
|
13
|
+
|
|
14
|
+
O padrão de qualquer painel PaaS (Easypanel/Dokploy/CapRover/…) é o mesmo:
|
|
15
|
+
|
|
16
|
+
- O **proxy do painel** (Traefik/nginx embutido) detém 80/443 e emite Let's Encrypt ao **anexar um domínio**
|
|
17
|
+
a um serviço. Logo, use o `templates/docker-compose.prod.yml` (BYO-proxy, **sem** o Caddy bundled) e deixe o painel
|
|
18
|
+
rotear + certificar. Um Caddy nosso brigaria pelas portas.
|
|
19
|
+
- O env vem do **`.env` que você controla** (não há magic vars do Coolify): gere com o
|
|
20
|
+
`scripts/gen-onboarding-env.ts` e cole as vars no serviço pelo painel.
|
|
21
|
+
- Cada stack (fazer.ai agents, Chatwoot, Langfuse) vira um projeto Compose; anexe `agentes.`/`chatwoot.`/`langfuse.` a
|
|
22
|
+
cada um. Detalhes de UI/API variam por produto e versão: resolva com seu conhecimento do painel da run.
|
|
23
|
+
|
|
24
|
+
Daqui em diante os passos são os mesmos da VM crua; só muda o "como" você aplica o compose + env.
|
|
25
|
+
|
|
26
|
+
## TLS na VM crua: duas opções
|
|
27
|
+
|
|
28
|
+
- **Caddy bundled** (recomendado se a VM tem 80/443 **livres**): use o `templates/docker-compose.portainer.yml` pra
|
|
29
|
+
agents. Ele já traz um Caddy que emite Let's Encrypt automático a partir de `CADDY_DOMAIN`/`ACME_EMAIL`
|
|
30
|
+
(gerados no `.env`).
|
|
31
|
+
- **BYO-proxy** (se já há nginx/Caddy/Traefik na 443, ou é um painel): use o `templates/docker-compose.prod.yml` (sem
|
|
32
|
+
Caddy) e aponte o proxy pra porta publicada do app. O inventário ([1b](01b-brownfield.md)) diz quem ocupa
|
|
33
|
+
80/443.
|
|
34
|
+
|
|
35
|
+
## Passos
|
|
36
|
+
|
|
37
|
+
1. **DNS primeiro** (A-records resolvendo antes do ACME; ver [1c](01c-pick-tier.md)).
|
|
38
|
+
2. **Env:** `bun scripts/gen-onboarding-env.ts --public-url https://agentes.<domínio> --acme-email
|
|
39
|
+
voce@<domínio>` gera o `.env` (duas roles, secrets, URLs, `CADDY_DOMAIN`). Chatwoot/Langfuse têm env
|
|
40
|
+
próprio (ver [`03-chatwoot-pro.md`](03-chatwoot-pro.md) e [`05-langfuse.md`](05-langfuse.md)).
|
|
41
|
+
3. **Suba cada stack** (a partir da raiz desta skill, com o `.env` ao lado; num painel, o equivalente é
|
|
42
|
+
criar cada projeto Compose):
|
|
43
|
+
```sh
|
|
44
|
+
docker compose -f templates/docker-compose.portainer.yml up -d # agents + postgres + Caddy (ou .prod.yml + proxy)
|
|
45
|
+
docker compose -f templates/chatwoot/docker-compose.yml up -d # Pro vs OSS pelo env (03-chatwoot-pro.md)
|
|
46
|
+
docker compose -f templates/langfuse/docker-compose.yml up -d # com MinIO (obrigatório)
|
|
47
|
+
```
|
|
48
|
+
4. **Boot do fazer.ai agents:** o CMD da imagem faz `bootstrap → migrate → serve`; **não** sobrescreva `command:`.
|
|
49
|
+
5. **Token do `/setup`** e **admin token do Chatwoot** saem dos logs (`docker compose logs`) / Rails runner,
|
|
50
|
+
igual aos outros tiers.
|
|
51
|
+
6. **Verifique** (200 + cert Let's Encrypt) e **siga pra etapa 6**.
|
|
52
|
+
|
|
53
|
+
## Brownfield
|
|
54
|
+
|
|
55
|
+
Se a VM/painel já roda algum serviço, sonde com a etapa [1b](01b-brownfield.md) (a sondagem via
|
|
56
|
+
`docker ps`/`ss` cobre o caso sem painel; num painel, use a API/UI dele pra inventariar) e **reuse** o que
|
|
57
|
+
estiver saudável.
|
|
58
|
+
|
|
59
|
+
## O que entrega ao contrato
|
|
60
|
+
|
|
61
|
+
Os 5 outputs do [1c](01c-pick-tier.md). **Lacuna conhecida:** quando você usa o Caddy bundled só pra agents, o
|
|
62
|
+
fronting TLS de `chatwoot.`/`langfuse.` precisa de um site Caddy/proxy adicional apontando pra porta
|
|
63
|
+
publicada de cada um (a seção "Chatwoot + Langfuse" de [`deploy-b-portainer.md`](deploy-b-portainer.md)
|
|
64
|
+
descreve esse mesmo padrão).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Sample agents
|
|
2
|
+
|
|
3
|
+
Exemplos de agentes no schema de export **`fazer-ai.agent` v1** (o mesmo formato que `POST /v1/agents/import`
|
|
4
|
+
/ a write tool `agent_import` do MCP consomem). Servem para testar o import, o onboarding e a documentação.
|
|
5
|
+
|
|
6
|
+
| Arquivo | Persona |
|
|
7
|
+
| --- | --- |
|
|
8
|
+
| `maria-clinica-moreira.json` | "Maria", recepção de uma clínica fictícia (Clínica Moreira): agendamento, FAQ, voz, KB. |
|
|
9
|
+
|
|
10
|
+
## Credenciais são por NOME, não por valor
|
|
11
|
+
|
|
12
|
+
O export **não carrega segredos**: cada credencial é referenciada por **nome** (`credentialRef`). Ao importar
|
|
13
|
+
num tenant novo, os refs não resolvem automaticamente: crie entradas no vault com os mesmos nomes (ou
|
|
14
|
+
re-aponte via `PATCH /v1/agents/:id`). Os nomes neste sample são genéricos de propósito:
|
|
15
|
+
|
|
16
|
+
- `OpenAI`: modelo (`gpt-5.4-mini`) + STT.
|
|
17
|
+
- `ElevenLabs`: TTS.
|
|
18
|
+
- `Google Gemini`: visão.
|
|
19
|
+
- `Google OAuth2`: integrações Google Calendar + Drive.
|
|
20
|
+
- `Asaas`: integração de cobrança.
|
|
21
|
+
|
|
22
|
+
IDs específicos de ambiente foram neutralizados (ex.: o Google Calendar usa `primary`). Não há chaves,
|
|
23
|
+
tokens ou IDs reais no arquivo: pode versionar e publicar à vontade.
|