@nio-cli/cli 0.4.0 → 0.6.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 (103) hide show
  1. package/.env.example +22 -9
  2. package/README.md +186 -78
  3. package/dist/adapters/pg/client.js +2 -0
  4. package/dist/adapters/pg/client.js.map +1 -1
  5. package/dist/adapters/pg/login-challenge-repository.js.map +1 -1
  6. package/dist/adapters/sms/whatsapp.js +83 -0
  7. package/dist/adapters/sms/whatsapp.js.map +1 -0
  8. package/dist/app/ai-client.js +2 -2
  9. package/dist/app/ai-client.js.map +1 -1
  10. package/dist/cli/commands/ai.js +2 -2
  11. package/dist/cli/commands/ai.js.map +1 -1
  12. package/dist/cli/commands/auth.js +5 -5
  13. package/dist/cli/commands/auth.js.map +1 -1
  14. package/dist/cli/commands/config.js +17 -7
  15. package/dist/cli/commands/config.js.map +1 -1
  16. package/dist/cli/commands/docs/content.js +80 -11
  17. package/dist/cli/commands/docs/content.js.map +1 -1
  18. package/dist/cli/commands/exec.js +1 -12
  19. package/dist/cli/commands/exec.js.map +1 -1
  20. package/dist/cli/commands/init/index.js +1 -2
  21. package/dist/cli/commands/init/index.js.map +1 -1
  22. package/dist/cli/commands/init/provision-step.js +2 -20
  23. package/dist/cli/commands/init/provision-step.js.map +1 -1
  24. package/dist/cli/commands/plan.js +1 -12
  25. package/dist/cli/commands/plan.js.map +1 -1
  26. package/dist/cli/commands/security.js +15 -15
  27. package/dist/cli/commands/security.js.map +1 -1
  28. package/dist/cli/commands/sync.js +4 -18
  29. package/dist/cli/commands/sync.js.map +1 -1
  30. package/dist/cli/commands/validate-plan.js +1 -10
  31. package/dist/cli/commands/validate-plan.js.map +1 -1
  32. package/dist/cli/copy/flows.json +1 -2
  33. package/dist/cli/copy.js.map +1 -1
  34. package/dist/cli/flows/dependencies.js +0 -4
  35. package/dist/cli/flows/dependencies.js.map +1 -1
  36. package/dist/cli/help-guide.js +69 -0
  37. package/dist/cli/help-guide.js.map +1 -0
  38. package/dist/cli/program.js +3 -3
  39. package/dist/cli/program.js.map +1 -1
  40. package/dist/cli/ui/render.js +0 -27
  41. package/dist/cli/ui/render.js.map +1 -1
  42. package/dist/cli.js +10 -2
  43. package/dist/cli.js.map +1 -1
  44. package/dist/gateway/services/login.js +6 -7
  45. package/dist/gateway/services/login.js.map +1 -1
  46. package/dist/gateway/services/security.js +6 -6
  47. package/dist/gateway/services/security.js.map +1 -1
  48. package/dist/lib/auth/gateway-token.js +3 -0
  49. package/dist/lib/auth/gateway-token.js.map +1 -1
  50. package/dist/lib/auth/nio-config.js +28 -0
  51. package/dist/lib/auth/nio-config.js.map +1 -1
  52. package/dist/lib/clients/client-configs.js +81 -86
  53. package/dist/lib/clients/client-configs.js.map +1 -1
  54. package/dist/lib/deps/dependencies.js +0 -25
  55. package/dist/lib/deps/dependencies.js.map +1 -1
  56. package/dist/lib/deps/dependency-install.js +3 -23
  57. package/dist/lib/deps/dependency-install.js.map +1 -1
  58. package/dist/lib/exec/exec-delegate.js +69 -48
  59. package/dist/lib/exec/exec-delegate.js.map +1 -1
  60. package/dist/lib/exec/plan-delegate.js +25 -34
  61. package/dist/lib/exec/plan-delegate.js.map +1 -1
  62. package/dist/lib/exec/qwen-client.js +74 -0
  63. package/dist/lib/exec/qwen-client.js.map +1 -0
  64. package/dist/lib/exec/validate-plan-delegate.js +13 -30
  65. package/dist/lib/exec/validate-plan-delegate.js.map +1 -1
  66. package/dist/lib/provision/provision.js +2 -2
  67. package/dist/lib/provision/provision.js.map +1 -1
  68. package/dist/lib/skills/patterns.js +15 -24
  69. package/dist/lib/skills/patterns.js.map +1 -1
  70. package/dist/lib/skills/skills.js +1 -1
  71. package/dist/lib/skills/skills.js.map +1 -1
  72. package/dist/mcp-server.js +0 -15
  73. package/dist/mcp-server.js.map +1 -1
  74. package/dist/tools/delegate-exec.js +3 -10
  75. package/dist/tools/delegate-exec.js.map +1 -1
  76. package/dist/tools/plan.js +6 -13
  77. package/dist/tools/plan.js.map +1 -1
  78. package/dist/tools/validate-plan.js +6 -14
  79. package/dist/tools/validate-plan.js.map +1 -1
  80. package/dist/tui/app.js +102 -18
  81. package/dist/tui/app.js.map +1 -1
  82. package/dist/tui/components.js +161 -88
  83. package/dist/tui/components.js.map +1 -1
  84. package/dist/tui/launch.js.map +1 -1
  85. package/dist/tui/opencode.js +27 -0
  86. package/dist/tui/opencode.js.map +1 -1
  87. package/dist/tui/palette.js +12 -5
  88. package/dist/tui/palette.js.map +1 -1
  89. package/dist/tui/prompt-input.js +168 -0
  90. package/dist/tui/prompt-input.js.map +1 -0
  91. package/dist/tui/state.js +252 -20
  92. package/dist/tui/state.js.map +1 -1
  93. package/dist/tui/test-utils.js +24 -0
  94. package/dist/tui/test-utils.js.map +1 -0
  95. package/package.json +2 -2
  96. package/dist/adapters/sms/http-generic.js +0 -71
  97. package/dist/adapters/sms/http-generic.js.map +0 -1
  98. package/dist/cli/commands/clean.js +0 -108
  99. package/dist/cli/commands/clean.js.map +0 -1
  100. package/dist/lib/clients/hooks.js +0 -191
  101. package/dist/lib/clients/hooks.js.map +0 -1
  102. package/dist/lib/exec/exec-engines.js +0 -63
  103. package/dist/lib/exec/exec-engines.js.map +0 -1
package/.env.example CHANGED
@@ -77,18 +77,31 @@ JWT_SECRET=
77
77
  # Porta do proxy Kong publicada no host pelo `dev:kong` (default 8000).
78
78
  # NIO_KONG_PROXY_PORT=8010
79
79
 
80
- # 2º fator (SMS OTP) — spec 0004 / ADR 0006. Sem essas 3, o login com auth_2
81
- # responde 503 "2FA não configurado" (o login de 1 fator segue normal).
82
- # SMS_ENDPOINT_URL=https://api.seu-provedor.com/v2/sms
83
- # SMS_AUTH_HEADER=X-API-TOKEN: seu-token # linha "Nome: valor"
84
- # SMS_BODY_TEMPLATE={"to":"{to}","message":"{text}"} # JSON com {to}/{text}/{from}
85
- # SMS_FROM=NIO # opcional, se o template usar {from}
80
+ # 2º fator (WhatsApp OTP via Meta Graph) — spec 0004 / ADR 0006. Sem endpoint
81
+ # + token, o login com auth_2 responde 503 "2FA não configurado" (o login de
82
+ # 1 fator segue normal).
83
+ # WHATSAPP_ENDPOINT_URL=https://graph.facebook.com/v25.0/1076830002188066/messages
84
+ # WHATSAPP_TOKEN=<bearer da Meta Graph> # app secret; rotaciona ~24h
85
+ # WHATSAPP_TEMPLATE_NAME=autenticao # template de autenticação aprovado
86
+ # WHATSAPP_TEMPLATE_LANGUAGE=pt_BR # variação do template
86
87
  #
87
- # DEV: se SMS_ENDPOINT_URL for loopback (127.0.0.1/localhost — o mock
88
- # `bun run dev:sms-echo`), o gateway entra em "modo echo": NENHUM SMS real sai.
88
+ # DEV: se WHATSAPP_ENDPOINT_URL for loopback (127.0.0.1/localhost — o mock
89
+ # `bun run dev:whatsapp-echo`), o gateway entra em "modo echo": NENHUM WhatsApp real sai.
89
90
  # A CLI avisa e mostra o código direto; `nio security status` mostra o backend.
90
91
 
91
- # Client de IA (nio ai) — Headroom, proxy de compressão em container (ADR 0007).
92
+ # Client de IA (nio ai) — backend OpenAI-compatível que o provider `opencode`
93
+ # consome (o SDK anexa /chat/completions). Estes são os knobs da "troca" do client.
94
+ # Motor = provider DEDICADO (openai-compatible) direto no backend; o provider `opencode`
95
+ # fica no default (big-pickle), fora do motor da CLI.
96
+ # NIO_AI_PROVIDER=nio-local # id do provider dedicado no opencode.json
97
+ # NIO_AI_BASE_URL=http://192.168.0.140:8001/v1 # backend de IA (default); raiz /v1
98
+ # NIO_AI_MODEL=RedHatAI/Qwen3.8-27B-INT4 # id do model EXATO que o backend serve (sem prefixo de provider)
99
+ # NIO_AI_CONTEXT=65536 # janela de contexto (tokens) declarada p/ o model; 0 = usa catálogo
100
+ # NIO_AI_OUTPUT=2048 # teto de tokens de saída reservados dentro da janela
101
+ # NIO_AI_MAX_INPUT=32000 # hard cap de input por prompt (0 = desativa a trava)
102
+ # ^ NIO_AI_* NÃO valem no .env do projeto (cwd é allowlist) — ponha em ~/.nio/config.env ou no shell.
103
+ #
104
+ # Headroom — proxy de compressão em container (ADR 0007), DORMENTE (ADR 0010).
92
105
  # NIO_HEADROOM_PORT=8787 # porta do proxy no host
93
106
  # NIO_HEADROOM_URL=http://127.0.0.1:8787/v1 # override total (o provider do OpenCode aponta pra cá)
94
107
  # NIO_HEADROOM_OPENAI_TARGET=https://opencode.ai/zen/v1 # upstream OpenAI-compatível (default: OpenCode Zen)
package/README.md CHANGED
@@ -5,7 +5,8 @@ responde um wizard, e a CLI — com auxílio de IA via MCP — materializa o amb
5
5
  toolchains, linguagens, frameworks, dotfiles, aliases e IDE. A entidade central é
6
6
  a **Sessão**: um ambiente isolado, com UUID, persistido no Postgres.
7
7
 
8
- > Documentação completa no terminal ou como página: `nio docs` · `nio docs --html --open`.
8
+ > **Guia rápido de uso:** `nio --help` (primeiros passos + como usar a interface do `nio ai`).
9
+ > **Manual completo:** `nio docs` no terminal · `nio docs --html --open` como página.
9
10
 
10
11
  ---
11
12
 
@@ -27,11 +28,14 @@ você → nio (CLI) ──► nio-gateway ──► Postgres (login: senh
27
28
  `EnvironmentBuilder` garante os toolchains, resolve os MCPs e grava o `config`
28
29
  materializado na linha `sessions` do Postgres. A sessão é isolada, tem UUID e
29
30
  pode ser reativada depois (`nio sessions`).
30
- 3. **`nio ai` abre a interface NIO** — o Headroom (proxy de compressão, container
31
- Docker, obrigatório), o `opencode serve` headless (`opencode/big-pickle`, MCP
32
- `nio` + MCPs do perfil), e a UI do NIO em Ink (chat, sidebar, paleta `/`). Com
33
- IDE, roda num terminal integrado dela. A partir daí o agente tem as tools
34
- `nio_*` criar/ativar sessão, re-materializar ambiente, delegar execução.
31
+ 3. **`nio ai` abre a interface NIO** — o `opencode serve` headless
32
+ (`opencode/big-pickle`, MCP `nio` + MCPs do perfil) e o chat do NIO em Ink
33
+ (fluxo em linha estilo Claude Code, paleta `/`, `Tab` troca de modo). O
34
+ Headroom foi desativado ([ADR 0010](docs/adr/0010-headroom-desativado.md)) o
35
+ client fala direto no LLM, **não precisa de Docker**. Com IDE, roda num
36
+ terminal integrado dela. A partir daí o agente tem as tools `nio_*` —
37
+ criar/ativar sessão, re-materializar ambiente, delegar execução. Detalhe de
38
+ uso: **[Interface do `nio ai`](#interface-do-nio-ai-tui)**.
35
39
 
36
40
  O **Postgres é a fonte da verdade** do domínio (usuários, sessões, trilha de
37
41
  auth). A CLI e o gateway só falam com o banco que **você** configurar — não há
@@ -58,7 +62,7 @@ Ficam no PATH: `nio` (CLI), `nio-gateway` (serviço de auth), `nio-cli` e
58
62
  | **`JWT_SECRET`** (segredo do time) | assinar/validar as sessões | mesmo valor em toda máquina |
59
63
  | **OpenCode** | operador de IA | o `nio init` oferece instalar (`npm i -g opencode-ai`) |
60
64
  | **Docker** | **não é necessário** pro `nio ai` (Headroom desativado, ADR 0010 — client fala direto no LLM). Ainda usado por `nio docker` (toolkit/cluster) e pelo gateway conteinerizado | `docker compose version` |
61
- | *(opcional)* provedor de SMS | 2º fator | `SMS_ENDPOINT_URL` + `SMS_AUTH_HEADER` + `SMS_BODY_TEMPLATE` |
65
+ | *(opcional)* WhatsApp Business API (Meta Graph) | 2º fator | `WHATSAPP_ENDPOINT_URL` + `WHATSAPP_TOKEN` (+ template) |
62
66
 
63
67
  ---
64
68
 
@@ -89,10 +93,15 @@ NIO_DATABASE_URL=postgres://usuario:senha@HOST:5432/nio_cli
89
93
  # NIO_DATABASE_SSL=true # só se o banco exigir TLS (gerenciado/nuvem)
90
94
  JWT_SECRET=<mesmo-valor-do-time>
91
95
 
92
- # 2º fator (SMS) — opcional, só no lado do gateway
93
- # SMS_ENDPOINT_URL=https://api.provedor.com/v2/sms
94
- # SMS_AUTH_HEADER=X-API-TOKEN: seu-token
95
- # SMS_BODY_TEMPLATE={"to":"{to}","message":"{text}"}
96
+ # 2º fator (WhatsApp OTP via Meta Graph) — opcional, só no lado do gateway
97
+ # WHATSAPP_ENDPOINT_URL=https://graph.facebook.com/v25.0/1076830002188066/messages
98
+ # WHATSAPP_TOKEN=<bearer da Meta Graph> # app secret; rotaciona ~24h
99
+ # WHATSAPP_TEMPLATE_NAME=autenticao # template de autenticação aprovado
100
+ # WHATSAPP_TEMPLATE_LANGUAGE=pt_BR # variação do template
101
+ #
102
+ # DEV: se WHATSAPP_ENDPOINT_URL for loopback (127.0.0.1/localhost — o mock
103
+ # `bun run dev:whatsapp-echo`), o gateway entra em "modo echo": NENHUM WhatsApp real sai.
104
+ # A CLI avisa e mostra o código direto; `nio security status` mostra o backend.
96
105
  ```
97
106
 
98
107
  > Alternativa pra time: gere o `~/.nio/config.env` uma vez e distribua o arquivo
@@ -102,13 +111,32 @@ JWT_SECRET=<mesmo-valor-do-time>
102
111
  |---|---|---|
103
112
  | `NIO_DATABASE_URL` / `NIO_DATABASE_SSL` | `NIO_` | tudo que toca o banco |
104
113
  | `JWT_SECRET` / `JWT_EXPIRES_IN` | **sem** prefixo (segredo do time) | `nio-gateway` + `nio-cli` |
105
- | `SMS_ENDPOINT_URL` / `SMS_AUTH_HEADER` / `SMS_BODY_TEMPLATE` / `SMS_FROM` | **sem** prefixo | `nio-gateway` |
114
+ | `WHATSAPP_ENDPOINT_URL` / `WHATSAPP_TOKEN` (+ `WHATSAPP_TEMPLATE_NAME` / `WHATSAPP_TEMPLATE_LANGUAGE`) | **sem** prefixo | `nio-gateway` |
106
115
  | `NIO_GATEWAY_HOST` (default `127.0.0.1`) | `NIO_` | `nio-gateway` — `0.0.0.0` p/ Kong em container |
107
116
  | `NIO_GATEWAY_URL` (default `http://127.0.0.1:3000`) | `NIO_` | a CLI acha o nio-gateway (Kong na frente = aponta :8000) |
108
117
 
109
118
  O schema de conexão é sempre `postgres://…`; um destino inválido falha explícito,
110
119
  nunca cai num default silencioso.
111
120
 
121
+ ### Backend de IA próprio (uso fora da rede NIO)
122
+
123
+ O motor de IA (`nio ai`/`exec`/`plan`) fala com um backend OpenAI-compatível
124
+ (`/v1`). **O default aponta pra infra interna** (`http://192.168.0.140:8001/v1`,
125
+ modelo `RedHatAI/Qwen3.8-27B-INT4`) — fora dessa rede, suba seu próprio
126
+ vLLM (ou compatível) e aponte a CLI pra ele em `~/.nio/config.env`
127
+ (`nio config path`; `NIO_AI_*` **não** valem no `.env` do projeto):
128
+
129
+ ```bash
130
+ NIO_AI_BASE_URL=https://seu-vllm:8000/v1 # raiz /v1 (o SDK anexa /chat/completions)
131
+ NIO_AI_MODEL=seu-org/seu-modelo # id EXATO que o backend serve em /v1/models
132
+ NIO_AI_CONTEXT=65536 # max_model_len do backend; 0 = usa catálogo
133
+ NIO_AI_OUTPUT=2048 # teto de saída (input + output ≤ contexto!)
134
+ NIO_AI_MAX_INPUT=32000 # trava de input por prompt (0 = desativa)
135
+ ```
136
+
137
+ Confira com `nio config check` — ele sonda `GET <base>/models` e avisa (sem
138
+ reprovar) se o backend está fora do ar ou não serve o modelo configurado.
139
+
112
140
  ---
113
141
 
114
142
  ## Primeiros passos
@@ -132,7 +160,7 @@ nio-gateway # gateway de auth (a esteira sobe sozinha se faltar)
132
160
  nio register # cria seu usuário na base compartilhada → cai no login
133
161
  nio login # autentica (salva o JWT em ~/.nio/session.json)
134
162
  nio security enable-2fa # (opcional) 2º fator
135
- nio init # monta o ambiente da sessão → `nio ai` (Headroom + OpenCode, num terminal da IDE)
163
+ nio init # monta o ambiente da sessão → `nio ai` (opencode serve + chat NIO, num terminal da IDE)
136
164
  ```
137
165
 
138
166
  O `nio-gateway` só é necessário pros comandos de auth (`login`/`logout`/
@@ -187,29 +215,30 @@ nio whoami # mostra quem está logado (--json pra saída estável)
187
215
  nio logout # revoga a auth_session no banco e limpa a sessão local
188
216
  ```
189
217
 
190
- ### 2º fator (SMS)
218
+ ### 2º fator (WhatsApp)
191
219
 
192
220
  Opt-in por conta. Com `auth_2` ativo, o `nio login` pede um código de 6 dígitos
193
- por SMS; se o SMS não chega, vale um dos 10 **códigos de backup** (mostrados uma
194
- vez no `enable-2fa`).
221
+ enviado por WhatsApp (template de autenticação da Meta Graph); se a mensagem não
222
+ chega, vale um dos 10 **códigos de backup** (mostrados uma vez no `enable-2fa`).
195
223
 
196
224
  ```bash
197
- nio security enable-2fa # cadastra o celular, confirma via SMS, mostra os backups
225
+ nio security enable-2fa # cadastra o celular, confirma via WhatsApp, mostra os backups
198
226
  nio security status # ativo? número (mascarado)? quantos backups restam?
199
227
  nio security disable-2fa
200
228
  nio security regenerate-backup-codes
201
229
  ```
202
230
 
203
231
  O gateway gera/valida o OTP em processo (sem Twilio, sem broker), guarda só o
204
- **HMAC** do código (TTL 5 min, 3 tentativas, uso único) e manda o SMS por um
205
- **adapter HTTP genérico**. Sem `SMS_ENDPOINT_URL` no ambiente, o login com
206
- `auth_2` responde `503 "2FA não configurado"` — o login de 1 fator segue normal.
232
+ **HMAC** do código (TTL 5 min, 3 tentativas, uso único) e manda a mensagem pela
233
+ **WhatsApp Business API (Meta Graph)**. Sem `WHATSAPP_ENDPOINT_URL`/`WHATSAPP_TOKEN`
234
+ no ambiente, o login com `auth_2` responde `503 "2FA não configurado"` — o login
235
+ de 1 fator segue normal.
207
236
  Detalhes: [spec 0004](docs/specs/auth/0004-login-2fa-sms-otp.md) ·
208
237
  [ADR 0006](docs/adr/0006-2fa-sms-otp.md) ·
209
238
  [`docs/arch/ARQUITETURA-GATEWAY.md`](docs/arch/ARQUITETURA-GATEWAY.md).
210
239
 
211
- Pra testar sem SMS real, o repo traz um mock: `bun run dev:sms-echo` sobe um
212
- endpoint local que imprime o código no terminal (aponte `SMS_ENDPOINT_URL` pra ele).
240
+ Pra testar sem WhatsApp real, o repo traz um mock: `bun run dev:whatsapp-echo` sobe
241
+ um endpoint local que imprime o código no terminal (aponte `WHATSAPP_ENDPOINT_URL` pra ele).
213
242
 
214
243
  ---
215
244
 
@@ -219,19 +248,77 @@ No fim do `nio init` a CLI sobe o **client de IA** da sessão — e o mesmo `nio
219
248
  retoma a qualquer momento. Ele:
220
249
 
221
250
  1. **Prepara o `opencode.json`** — grava o provider `opencode` **direto no OpenCode Zen**
222
- (sem `baseURL` de proxy), junto do `model: opencode/big-pickle`, do MCP `nio` e dos MCPs
223
- do perfil. O **Headroom foi desativado** ([ADR 0010](docs/adr/0010-headroom-desativado.md)):
251
+ (sem `baseURL` de proxy), junto do `model: opencode/big-pickle`, do MCP `nio`, dos MCPs
252
+ do perfil e de um bloco `permission` semeado (allowlist só-leitura → `allow`, resto →
253
+ `ask`). O **Headroom foi desativado** ([ADR 0010](docs/adr/0010-headroom-desativado.md)):
224
254
  o client fala direto no LLM, sem compressão — **não precisa de Docker** pro `nio ai`.
225
255
  (O `nio docker headroom` continua existindo, dormente, pra quem quiser subir manualmente.)
226
- 2. **Sobe o `opencode serve` headless e abre a interface NIO** (Ink chat
227
- streamado, sidebar verde, paleta `/` com os comandos e capacidades do NIO). O
228
- motor é o `opencode/big-pickle`; a casca é nossa. Se a sessão tem IDE (VS Code /
229
- Cursor), o `nio init` grava um `.vscode/tasks.json` (task `NIO`, `runOn: folderOpen`)
230
- e o `nio ai` sobe num **terminal integrado da IDE** uma superfície, não duas.
231
- Sem IDE, roda no terminal atual. Sem TTY / sem `opencode` → cai na TUI do OpenCode.
256
+ 2. **Sobe o `opencode serve` headless e abre a interface NIO** (Ink). O motor é o
257
+ `opencode/big-pickle`; a casca é nossa. Se a sessão tem IDE (VS Code / Cursor), o
258
+ `nio init` grava um `.vscode/tasks.json` (task `NIO`, `runOn: folderOpen`) e o
259
+ `nio ai` sobe num **terminal integrado da IDE** — uma superfície, não duas. Sem
260
+ IDE, roda no terminal atual. Sem TTY recusa com mensagem; sem `opencode` no
261
+ PATH → cai na TUI do OpenCode.
262
+
263
+ Ver [`docs/arch/ARQUITETURA-CLIENTE-IA.md`](docs/arch/ARQUITETURA-CLIENTE-IA.md),
264
+ [`docs/arch/ARQUITETURA-TUI-UX-SPRINTS.md`](docs/arch/ARQUITETURA-TUI-UX-SPRINTS.md) e
265
+ [`docs/arch/ARQUITETURA-TUI-INTERACOES-MOTOR.md`](docs/arch/ARQUITETURA-TUI-INTERACOES-MOTOR.md).
266
+
267
+ ### Interface do `nio ai` (TUI)
268
+
269
+ Chat no terminal (Ink) sobre o `opencode/big-pickle`, **em uma superfície só** — no
270
+ estilo do Claude Code. O que o motor faz aparece **em linha**, conforme acontece:
271
+ o raciocínio (`✻`), cada ferramenta (`● nome(args)` + `⎿` a saída), o checklist
272
+ (`☑ ◐ ☐`), os arquivos tocados, o diff da rodada (`✎ N arquivo(s) +x −y`) e os
273
+ tokens/custo no rodapé de cada resposta. Sem sidebar, sem janela extra.
274
+
275
+ **Digitar & enviar**
276
+
277
+ | Tecla | Faz |
278
+ |---|---|
279
+ | `Enter` | envia o prompt |
280
+ | `\` + `Enter` · `Ctrl-J` | quebra linha — o prompt é multi-linha e cresce sozinho |
281
+ | `←` `→` `↑` `↓` | move o cursor dentro do texto |
282
+ | `Ctrl-A` / `Ctrl-E` | início / fim da linha |
283
+ | `Ctrl-W` / `Ctrl-U` / `Ctrl-K` | apaga a palavra anterior / até o início / até o fim |
284
+ | colar um bloco | entra literal (várias linhas **não** enviam sozinhas) |
285
+
286
+ **Modos** — o modo troca o comportamento do agente
287
+
288
+ | Tecla | Faz |
289
+ |---|---|
290
+ | `Tab` | alterna entre os agentes primários do `opencode.json` (`build` → `plan` → …) |
291
+ | `build` | o agente **executa** (edita, roda comando) |
292
+ | `plan` | o agente **só propõe** — não toca nada |
293
+
294
+ O modo atual fica no rodapé: `[build]`.
295
+
296
+ **Paleta de comandos**
297
+
298
+ | Tecla | Faz |
299
+ |---|---|
300
+ | `/` | abre a lista inline — comandos do `nio` + capacidades do operador |
301
+ | `↑` `↓` + `Enter` | roda o comando / manda a capacidade pro agente / abre o painel |
302
+ | `Esc` | fecha a lista — o texto que você já digitou **continua lá** |
303
+
304
+ **Quando o agente te interrompe**
305
+
306
+ - **Permissão** (rodar shell, editar arquivo, chamar MCP…) → modal:
307
+ `a`/`Enter` permite uma vez · `s` permite **sempre** (salva a regra no `opencode.json`) · `d`/`Esc` nega.
308
+ Pedidos em paralelo entram numa **fila** (`+N na fila`); um sub-agente travado é
309
+ reconciliado sozinho em ~4s — não trava mais em "processando".
310
+ - **Pergunta aberta** — o agente termina com "?" → o cue `↳ o nio perguntou` aparece acima do input.
311
+ - **Lista de opções** — o agente listou `1./2./3.` → menu `↑`/`↓`+`Enter` pra escolher; ou ignore e escreva livre.
232
312
 
233
- Ver [`docs/arch/ARQUITETURA-CLIENTE-IA.md`](docs/arch/ARQUITETURA-CLIENTE-IA.md) e
234
- [`docs/arch/ARQUITETURA-CLIENTE-TUI.md`](docs/arch/ARQUITETURA-CLIENTE-TUI.md).
313
+ **Acompanhar & controlar**
314
+
315
+ | Tecla | Faz |
316
+ |---|---|
317
+ | `Ctrl-R` | expande / colapsa o raciocínio (`✻`) — ver o agente "pensar" ao vivo |
318
+ | `Esc` | durante o processamento: **aborta o turno** |
319
+
320
+ `NIO_DEBUG=1 nio ai` grava cada evento cru do motor em `~/.nio/tui.log` (nunca no
321
+ terminal — corromperia o render).
235
322
 
236
323
  > A interface NIO (Ink) está na **fatia 2a** ([ADR 0008](docs/adr/0008-interface-nio-ink.md)).
237
324
  > A paridade completa com o OpenCode (diff viewer, file tree, seletor de modelo…) é a 2b.
@@ -248,19 +335,24 @@ Operações do CLI, **sem o binário na frente** (declarado no cabeçalho da tab
248
335
  Gerada da fonte por `npm run gen:docs`. Ajuda de qualquer comando: `nio <cmd> --help`.
249
336
 
250
337
  <!-- COMMANDS:START -->
251
- <!-- gerado por `bun run gen:docs` — não edite à mão. binário `nio`, 42 comandos. -->
338
+ <!-- gerado por `bun run gen:docs` — não edite à mão. binário `nio`, 60 comandos. -->
252
339
 
253
340
  | Comando | Descrição |
254
341
  | --- | --- |
255
- | `ai` | Abre a interface NIO da sessão ativa (Headroom + opencode serve + Ink) |
256
- | `ai status` | Estado do Headroom (proxy obrigatório do client de IA) |
257
- | `clean-legacy` | Remove commands/skills legados (substituídos) de ~/.claude e ~/.codex |
342
+ | `agents` | Lista os agentes disponíveis |
343
+ | `ai` | Abre a interface NIO da sessão ativa (opencode serve headless + chat Ink) |
344
+ | `ai status` | Estado do Headroom (proxy de compressão desativado por ADR 0010, dormente) |
345
+ | `command [name]` | Cria um comando personalizado pro operador de IA |
258
346
  | `completion [shell]` | Imprime o script de autocomplete (bash\|zsh\|fish). |
259
347
  | `config` | Config compartilhada da equipe (~/.nio/config.env) |
260
348
  | `config check` | Confere se a config está completa e o Postgres responde |
261
349
  | `config path` | Imprime o caminho do arquivo de config |
262
350
  | `config setup` | Wizard: cola os valores do time, testa a conexão e salva |
263
- | `docker` | Camada Docker: MCP Gateway + Portainer, compose, debug e cluster (Swarm) |
351
+ | `debug` | Diagnostica o ambiente e aponta onde está o problema |
352
+ | `deps` | Detecta e (opt-in) instala dependências da sessão ativa |
353
+ | `deps scan` | Escaneia os manifests uma vez e registra o que falta |
354
+ | `deps watch` | Escaneia a cada 10s até Ctrl+C |
355
+ | `docker` | Camada Docker: stack NIO, compose, debug e cluster (Swarm) |
264
356
  | `docker cluster <action> [arg]` | Docker Swarm — stack `nio-cluster` (up\|down\|status\|scale) |
265
357
  | `docker compose <action> [service]` | Wrapper sobre `docker compose` do projeto (up\|down\|restart\|ps\|logs) |
266
358
  | `docker create` | Cria e sobe um container (wizard ou flags) |
@@ -271,28 +363,41 @@ Gerada da fonte por `npm run gen:docs`. Ajuda de qualquer comando: `nio <cmd> --
271
363
  | `docker headroom up` | Sobe o container do Headroom |
272
364
  | `docker orquest [instruction]` | Orquestra os serviços do projeto via compose, dirigido pelo operador (linguagem natural) |
273
365
  | `docker portainer` | Abre o Portainer no navegador |
366
+ | `docker stack` | Stack NIO unificado (gateway · kong · headroom · mcp · portainer) — docker/docker-compose.yml |
367
+ | `docker stack down` | Derruba o stack inteiro |
368
+ | `docker stack status` | ps + health dos 5 serviços |
369
+ | `docker stack up` | Sobe o stack inteiro (build do gateway) + registra o MCP |
274
370
  | `docker toolkit` | Infra NIO: Docker MCP Gateway + Portainer (docker/docker-compose.yml) |
275
371
  | `docker toolkit down` | Derruba a infra e desabilita o MCP no opencode.json |
276
372
  | `docker toolkit status` | Estado dos containers + health dos endpoints |
277
373
  | `docker toolkit up` | Sobe a infra e registra o gateway no opencode.json |
278
374
  | `docs` | Documentação completa da CLI (terminal ou página com --html) |
279
- | `exec` | Delega a implementação a um agente headless num worktree e aguarda. |
375
+ | `exec` | Delega a implementação ao Qwen (vLLM local) num worktree e aguarda. |
280
376
  | `exec-status <jobId>` | Estado de um job de execução (`nio exec`), em JSON |
281
377
  | `init` | Cria nio.json no diretório atual e materializa o ambiente da sessão |
378
+ | `lang` | Conhecimento/config das linguagens (nio-lang) |
379
+ | `lang sync` | Baixa/atualiza o cache de conhecimento das linguagens em ~/.nio/lang |
282
380
  | `login` | Autentica via nio-gateway (túnel HTTP) e salva a sessão localmente (JWT) |
283
381
  | `logout` | Encerra a sessão local e revoga a auth_session no banco |
284
- | `plan` | Roda o engine pensante sobre o projeto e escreve/refina o plan.md da raiz. |
285
- | `register` | Cria um novo usuário no banco (user_cli) e entra (login) |
286
- | `security` | fator do login (SMS OTP + códigos de backup) |
382
+ | `open` | Abre a IDE da sessão ativa na pasta do projeto |
383
+ | `plan` | Roda o Qwen (vLLM local) sobre o projeto e escreve/refina o plan.md da raiz. |
384
+ | `register` | Cria um novo usuário via nio-gateway e entra (login) |
385
+ | `security` | Senha e 2º fator do login (WhatsApp OTP + códigos de backup) |
386
+ | `security change-password` | Troca a senha (exige a senha atual) e encerra todas as sessões |
287
387
  | `security disable-2fa` | Desativa o 2º fator |
288
- | `security enable-2fa` | Ativa o 2º fator via SMS |
388
+ | `security enable-2fa` | Ativa o 2º fator via WhatsApp |
289
389
  | `security regenerate-backup-codes` | Invalida os códigos de backup e gera 10 novos |
290
390
  | `security status` | Mostra o estado do 2º fator |
391
+ | `sessions` | Gerencia as sessões de ambiente (list/activate/pause/delete) |
392
+ | `sessions activate <id>` | Ativa uma sessão (arquiva as demais ativas) |
393
+ | `sessions delete <id>` | Remove uma sessão (irreversível) |
394
+ | `sessions list` | Lista as suas sessões |
395
+ | `sessions pause <id>` | Pausa uma sessão |
291
396
  | `skills` | Skills, commands e agents do nio (lidos do repo aberto via cache) |
292
397
  | `skills status` | Lista os docs do repo de skills (cache local ~/.nio/skills) |
293
398
  | `start` | Conduz a esteira: config → gateway → login → sessão → OpenCode |
294
399
  | `sync` | Instala/atualiza skills, commands e agents nos clientes configurados, a partir do bundle (idempotente); checa atualização do pacote |
295
- | `validate-plan` | Lê o plan.md da raiz e roda o engine pensante para julgar se o plano precisa de uma spec antes de implementar. |
400
+ | `validate-plan` | Lê o plan.md da raiz e roda o Qwen (vLLM local) para julgar se o plano precisa de uma spec antes de implementar. |
296
401
  | `whoami` | Mostra o usuário autenticado |
297
402
  <!-- COMMANDS:END -->
298
403
 
@@ -355,16 +460,16 @@ O servidor `nio-cli` expõe as tools de ambiente v2 (todas passam pelo
355
460
 
356
461
  | Operação | O que faz |
357
462
  | --- | --- |
358
- | `delegate_exec` | Delega a IMPLEMENTAÇÃO a um agente de execução novo (codex ou claude local, na assinatura — sem API) num worktree já criado pelo /implement. |
463
+ | `delegate_exec` | Delega a IMPLEMENTAÇÃO ao Qwen (vLLM local, via API — sem assinatura nem binário externo) num worktree já criado pelo /implement. |
359
464
  | `env_detect_deps` | Roda UM ciclo do watcher de dependências sobre a pasta da sessão: escaneia os manifests (package.json, requirements.txt, Cargo.toml), detecta o que está declarado mas não instalado e registra um evento por dependência nova (idempotente). |
360
465
  | `env_materialize` | Re-materializa o ambiente de uma sessão existente a partir do seu perfil: garante os toolchains de novo, re-resolve os MCPs e reescreve o `config` em `sessions.config`. |
361
466
  | `exec_status` | Estado de uma execução delegada (`nio_delegate_exec`): running \| done \| failed, com o resumo do agente, os arquivos alterados e os checks determinísticos (tamanho, lint, build, testes). |
362
- | `plan` | Roda o engine PENSANTE (claude ou codex local, na assinatura — sem API) sobre a raiz do projeto e escreve/refina o `plan.md` de rascunho pré-SDD. |
467
+ | `plan` | Roda o Qwen (vLLM local, via API) sobre a raiz do projeto e escreve/refina o `plan.md` de rascunho pré-SDD. |
363
468
  | `profile_get` | Consulta o catálogo de perfis de ambiente (hardcoded na CLI). |
364
469
  | `session_activate` | Ativa uma sessão de ambiente do usuário por id (o prefixo do UUID basta). |
365
470
  | `session_create` | Cria uma sessão de ambiente pro usuário autenticado e materializa o perfil escolhido: garante os toolchains, resolve os MCPs e persiste o `config` em `sessions.config`. |
366
471
  | `session_list` | Lista as sessões de ambiente do usuário autenticado (mais recentes primeiro), com id, nome, perfil, status e o `config` materializado. |
367
- | `validate_plan` | Lê o `plan.md` da raiz do projeto e roda o engine PENSANTE (claude ou codex local, na assinatura — sem API) para julgar se o plano é complexo o bastante para virar uma spec SDD antes de implementar. |
472
+ | `validate_plan` | Lê o `plan.md` da raiz do projeto e roda o Qwen (vLLM local, via API) para julgar se o plano é complexo o bastante para virar uma spec SDD antes de implementar. |
368
473
  <!-- TOOLS:END -->
369
474
 
370
475
  ### Recipes de ambiente (repo NIO-SKILLS)
@@ -380,13 +485,13 @@ união em linguagens/frameworks/MCPs).
380
485
 
381
486
  ## Skills, commands e dependências
382
487
 
383
- Além das tools, o nio entrega skills/commands/agents/hooks pros clientes. O conteúdo
488
+ Além das tools, o nio entrega **skills, commands e agents** pros clientes. O conteúdo
384
489
  vive num **repo aberto** — [`hugoreiis12-png/NIO-SKILLS-`](https://github.com/hugoreiis12-png/NIO-SKILLS-) —
385
490
  e **não** é um pacote npm. O CLI baixa o repo (zipball do GitHub, sem precisar de `git`)
386
491
  pra um cache local em **`~/.nio/skills`** e lê de lá. `nio sync` **atualiza o cache**
387
492
  (pull da branch) toda vez, então as skills evoluem sem republicar o CLI; e **auto-detecta**
388
- quais clientes têm o nio configurado, provisionando pra cada um conforme a seleção de
389
- perfil/área do `nio.json`.
493
+ se o OpenCode tem o conector `nio` configurado, provisionando pra ele conforme a seleção
494
+ de perfil/área do `nio.json`.
390
495
 
391
496
  Overrides por ambiente:
392
497
 
@@ -400,13 +505,8 @@ Cada cliente recebe no formato que entende:
400
505
 
401
506
  | Cliente | Onde | Formato |
402
507
  | --------------- | ----------------------------- | ----------------------------------------------------------------------- |
403
- | Claude Code | `~/.claude/{commands,skills,agents}` | arquivos nativos — slash-commands e skills |
404
- | Codex CLI | `~/.codex/` | **dual-write**: cada command/skill vira uma **skill** (`skills/<id>/SKILL.md`, auto-selecionada) **e** um **custom prompt** (`prompts/<id>.md`, no menu `/prompts:<id>`) |
405
- | Cowork/Desktop | — | via **MCP prompts** + resources, servidos ao vivo (ignora `~/.claude`) |
406
-
407
- > Configurar o Codex grava `NIO_CLIENT=codex` no `config.toml`, o que faz o servidor
408
- > provisionar pra `~/.codex` e filtrar os docs pelo surface `codex`. Reinicie o cliente
409
- > depois do sync pra carregar skills/prompts novos.
508
+ | OpenCode | `~/.config/opencode/` | MCP `nio` registrado no `opencode.json`; skills/commands no layout cru do pacote (`skills/<id>/SKILL.md`) |
509
+ | Cowork/Desktop | | via **MCP prompts** + resources, servidos ao vivo |
410
510
 
411
511
  > **Como aparecem no Cowork/Claude Desktop.** Lá as skills chegam como **prompts MCP**,
412
512
  > que o app expõe como **slash-commands** no menu de conectores/"+" (ex.: digite `/` e
@@ -419,12 +519,12 @@ Cada cliente recebe no formato que entende:
419
519
  Um doc pode ser restrito a clientes específicos via frontmatter:
420
520
 
421
521
  ```yaml
422
- clients: claude-code, cowork # vazio/ausente = todos os clientes
522
+ clients: cowork, opencode # vazio/ausente = todos os clientes
423
523
  ```
424
524
 
425
- Valores: `claude-code`, `codex`, `cowork`. O MCP filtra por esse campo, então um skill
426
- marcado `cowork` não é provisionado pro `~/.claude`, e um `claude-code` não aparece como
427
- prompt no Cowork.
525
+ Valores: `cowork`, `opencode`. O MCP filtra por esse campo, então um skill
526
+ marcado `cowork` aparece como prompt no Cowork/Desktop, e um `opencode`
527
+ nos docs provisionados ao operador OpenCode.
428
528
 
429
529
  ### Dependências externas
430
530
 
@@ -434,8 +534,8 @@ Commands/skills podem depender de libs externas, declaradas como arquivos em
434
534
 
435
535
  - **instalador estruturado** (`npm:`, `skills:` = `npx skills add`, `git:`) → oferece
436
536
  rodar com `[y/N]` (comando montado a partir do campo validado, **sem shell**);
437
- - **`manual:`** (plugin de marketplace, passos no cliente, UI) → imprime os passos por
438
- cliente, com os comandos destacados.
537
+ - **`manual:`** (sem instalador automatizável checagem/UI no cliente) → imprime os
538
+ passos, com os comandos destacados.
439
539
 
440
540
  A string `install:` (se houver) é **só exibição** — nunca é executada. A CLI **detecta
441
541
  o que já está instalado** e mostra um selo `✓ instalada` (via `npm ls -g`, dir de
@@ -445,15 +545,14 @@ destino, ou o `detect:` do frontmatter).
445
545
 
446
546
  ## Claude Desktop / Cowork
447
547
 
448
- Selecione **Cowork** na lista de clientes do `nio init`. Com o app instalado e você
449
- logado (`nio login`), a CLI **ativa o conector direto** escreve o `nio` no
450
- `claude_desktop_config.json` com caminhos absolutos (`node` + `dist/mcp-server.js`)
451
- e `NIO_CLIENT=cowork`. Reinicie o Claude Desktop pra carregar. O `nio sync` reafirma
452
- esse config.
453
-
454
- **Fallback `.mcpb`:** se a escrita direta falhar, a CLI gera
455
- `~/Downloads/nio-cli-<versão>.mcpb` e mostra os passos — Configurações → Extensões →
456
- Configurações avançadas → "Extension Developer" → "Install Extension…".
548
+ O Cowork/Claude Desktop é um cliente de **chat** via MCP as skills chegam como
549
+ **prompts MCP** servidos ao vivo, e o conector vive no `claude_desktop_config.json`
550
+ com caminhos absolutos (`node` + `dist/mcp-server.js`) e `NIO_CLIENT=cowork`. Ele
551
+ **não** é mais um alvo do `nio init` (o checkbox oferece OpenCode): o setup
552
+ inicial do conector é manual. O `nio sync` **reafirma** esse config quando o conector
553
+ já existe — com o app instalado e você logado (`nio login`), ele reescreve o entry
554
+ com paths atuais (merge não-destrutivo + backup). Reinicie o Claude Desktop pra
555
+ carregar prompts novos depois do sync.
457
556
 
458
557
  ---
459
558
 
@@ -478,11 +577,12 @@ background em qualquer comando (`update-notifier`).
478
577
  | `Não consegui falar com o nio-gateway` | o `nio-gateway` não está no ar — rode `nio-gateway &` |
479
578
  | `Não autenticado` | `nio register` (1ª vez) e depois `nio login` |
480
579
  | Erro de conexão com o banco | `ECONNREFUSED` = Postgres fora do ar / host errado; `password authentication failed` = credencial; erro de SSL = `NIO_DATABASE_SSL=true` |
481
- | `2FA não configurado no servidor` (503) | faltam as `SMS_*` no ambiente do `nio-gateway` |
482
- | Tools não aparecem no Claude Code | reinicie o cliente depois do `nio init`; cheque com `/mcp` |
580
+ | `2FA não configurado no servidor` (503) | faltam as `WHATSAPP_*` no ambiente do `nio-gateway` |
581
+ | `nio ai` diz `precisa de um terminal interativo` | você está num pipe/CI rode num terminal de verdade |
582
+ | `nio ai` travado em "processando" | `Esc` aborta o turno; o motor recupera permissão perdida sozinho em ~4s. Persistiu? `NIO_DEBUG=1 nio ai` e veja `~/.nio/tui.log` |
583
+ | `nio ai` cai na TUI do OpenCode | falta o binário `opencode` no PATH — `npm i -g opencode-ai` |
483
584
  | Skills não aparecem no Cowork | chegam como **prompts MCP** (slash-commands), não skills autônomas. Cmd+Q e reabra; confirme o conector |
484
585
  | `Conteúdo de skills não encontrado` | cache `~/.nio/skills` vazio — rode `nio sync` com rede, ou `NIO_SKILLS_DIR` pra um checkout local |
485
- | Sobraram comandos antigos | `nio clean-legacy` (`--dry-run` pra revisar antes) |
486
586
 
487
587
  Sempre: `nio debug` mostra o estado de tudo com uma dica por item. E
488
588
  `NIO_DEBUG=1 nio <cmd>` liga log verboso (`[nio:debug]` em stderr): `.env`
@@ -507,7 +607,15 @@ O logo Matrix anima (chuva caindo) toda vez que aparece em terminal interativo.
507
607
 
508
608
  ## Versão
509
609
 
510
- **0.2.0** — v1 da CLI fechada: auth (senha + fator SMS), backend de sessões,
511
- wizard de ambiente, tools MCP de ambiente, camada Docker e o gateway com Kong.
512
- Nasceu de um cliente NOS/Supabase (v1), removido. Histórico cronológico:
513
- [`docs/PROGRESSO.md`](docs/PROGRESSO.md).
610
+ **0.5.0** — a interface do `nio ai` (TUI) fechada: editor multi-linha, fluxo em
611
+ linha estilo Claude Code (raciocínio, ferramentas, checklist, diff, tokens),
612
+ `Tab` troca de modo, paleta `/` executável, fila de permissões (com recuperação
613
+ de pedido perdido de sub-agente), perguntas e menus de opção. Mais o fix da cauda
614
+ de 30s no shutdown que tocava o Postgres.
615
+
616
+ Base (0.2.0–0.4.0): auth (senha + 2º fator SMS OTP), backend de sessões no
617
+ Postgres, wizard de ambiente, tools MCP, camada Docker, gateway com Kong e a
618
+ auditoria de segurança (argon2id + pepper, HIBP k-anonymity, roles de menor
619
+ privilégio). Nasceu de um cliente NOS/Supabase (v1), já removido.
620
+
621
+ Histórico cronológico: [`docs/PROGRESSO.md`](docs/PROGRESSO.md).
@@ -38,6 +38,7 @@ let pool = null;
38
38
  export function getPool() {
39
39
  if (pool)
40
40
  return pool;
41
+ globalThis.__nioPgPoolOpen = true;
41
42
  pool = new Pool({
42
43
  connectionString: readDatabaseUrl(),
43
44
  ssl: readSslOption(),
@@ -87,6 +88,7 @@ export async function closePool() {
87
88
  return;
88
89
  const p = pool;
89
90
  pool = null;
91
+ globalThis.__nioPgPoolOpen = false;
90
92
  await p.end();
91
93
  }
92
94
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/adapters/pg/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/adapters/pg/client.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"login-challenge-repository.js","sourceRoot":"","sources":["../../../src/adapters/pg/login-challenge-repository.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAe7D,MAAM,IAAI,GACR,yFAAyF,CAAC;AAG5F,MAAM,UAAU,aAAa,CAAC,EAAU;IACtC,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,GAAsB;IACzD,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC;QAC3B,OAAO,EAAE,GAAG,CAAC,OAA2B;QACxC,QAAQ,EAAE,GAAG,CAAC,SAAS;QACvB,OAAO,EAAE,GAAG,CAAC,OAAgB;QAC7B,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC9B,SAAS,EAAE,GAAG,CAAC,UAAU;QACzB,UAAU,EAAE,GAAG,CAAC,WAAW;QAC3B,SAAS,EAAE,GAAG,CAAC,UAAU;KAC1B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,8BAA8B;IAC5C,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,KAA6B;YACxC,OAAO,eAAe,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;gBAClC,MAAM,EAAE,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;gBACxE,MAAM,EAAE,CAAC,KAAK,CACZ,yEAAyE,EACzE,CAAC,KAAK,CAAC,MAAM,CAAC,CACf,CAAC;gBACF,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,KAAK,CACxB;;uBAEa,IAAI,EAAE,EACnB,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAC9E,CAAC;gBACF,OAAO,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC;YAC5C,CAAC,CAAC,CAAC;QACL,CAAC;QAED,KAAK,CAAC,QAAQ,CAAC,EAAE;YACf,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;gBAAE,OAAO,IAAI,CAAC;YACpC,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,UAAU,IAAI,sCAAsC,EACpD,CAAC,EAAE,CAAC,CACL,CAAC;YACF,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACxB,OAAO,GAAG,CAAC,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAChD,CAAC;QAED,KAAK,CAAC,iBAAiB,CAAC,EAAE;YACxB,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;gBAAE,OAAO,CAAC,CAAC;YACjC,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,sFAAsF,EACtF,CAAC,EAAE,CAAC,CACL,CAAC;YACF,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,IAAI,CAAC,CAAC,CAAC;QAC5C,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,EAAE;YACd,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;gBAAE,OAAO;YAC/B,MAAM,KAAK,CACT,uFAAuF,EACvF,CAAC,EAAE,CAAC,CACL,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,aAAa;YACjB,MAAM,KAAK,CAAC,uDAAuD,CAAC,CAAC;QACvE,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"login-challenge-repository.js","sourceRoot":"","sources":["../../../src/adapters/pg/login-challenge-repository.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAe7D,MAAM,IAAI,GACR,yFAAyF,CAAC;AAG5F,MAAM,UAAU,aAAa,CAAC,EAAU;IACtC,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,GAAsB;IACzD,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC;QAC3B,OAAO,EAAE,GAAG,CAAC,OAA2B;QACxC,QAAQ,EAAE,GAAG,CAAC,SAAS;QACvB,OAAO,EAAE,GAAG,CAAC,OAAqB;QAClC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC9B,SAAS,EAAE,GAAG,CAAC,UAAU;QACzB,UAAU,EAAE,GAAG,CAAC,WAAW;QAC3B,SAAS,EAAE,GAAG,CAAC,UAAU;KAC1B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,8BAA8B;IAC5C,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,KAA6B;YACxC,OAAO,eAAe,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;gBAClC,MAAM,EAAE,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;gBACxE,MAAM,EAAE,CAAC,KAAK,CACZ,yEAAyE,EACzE,CAAC,KAAK,CAAC,MAAM,CAAC,CACf,CAAC;gBACF,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,KAAK,CACxB;;uBAEa,IAAI,EAAE,EACnB,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAC9E,CAAC;gBACF,OAAO,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC;YAC5C,CAAC,CAAC,CAAC;QACL,CAAC;QAED,KAAK,CAAC,QAAQ,CAAC,EAAE;YACf,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;gBAAE,OAAO,IAAI,CAAC;YACpC,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,UAAU,IAAI,sCAAsC,EACpD,CAAC,EAAE,CAAC,CACL,CAAC;YACF,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACxB,OAAO,GAAG,CAAC,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAChD,CAAC;QAED,KAAK,CAAC,iBAAiB,CAAC,EAAE;YACxB,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;gBAAE,OAAO,CAAC,CAAC;YACjC,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,sFAAsF,EACtF,CAAC,EAAE,CAAC,CACL,CAAC;YACF,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,IAAI,CAAC,CAAC,CAAC;QAC5C,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,EAAE;YACd,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;gBAAE,OAAO;YAC/B,MAAM,KAAK,CACT,uFAAuF,EACvF,CAAC,EAAE,CAAC,CACL,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,aAAa;YACjB,MAAM,KAAK,CAAC,uDAAuD,CAAC,CAAC;QACvE,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,83 @@
1
+ export function formatWhatsAppNumber(phone) {
2
+ return phone.replace(/^\+/, '');
3
+ }
4
+ export function buildPayload(phone, code, env = {}) {
5
+ const payload = {
6
+ messaging_product: 'whatsapp',
7
+ to: formatWhatsAppNumber(phone),
8
+ type: 'template',
9
+ template: {
10
+ name: env.templateName ?? 'autenticao',
11
+ language: { code: env.templateLanguage ?? 'pt_BR' },
12
+ components: [
13
+ {
14
+ type: 'body',
15
+ parameters: [{ type: 'text', text: code }],
16
+ },
17
+ {
18
+ type: 'button',
19
+ sub_type: 'url',
20
+ index: '0',
21
+ parameters: [{ type: 'text', text: code }],
22
+ },
23
+ ],
24
+ },
25
+ };
26
+ return JSON.stringify(payload);
27
+ }
28
+ function readEnv() {
29
+ return {
30
+ url: process.env.WHATSAPP_ENDPOINT_URL?.trim(),
31
+ token: process.env.WHATSAPP_TOKEN?.trim(),
32
+ templateName: process.env.WHATSAPP_TEMPLATE_NAME?.trim(),
33
+ templateLanguage: process.env.WHATSAPP_TEMPLATE_LANGUAGE?.trim(),
34
+ };
35
+ }
36
+ const LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '::1', '[::1]', '0.0.0.0']);
37
+ export function smsMode(env = readEnv()) {
38
+ if (!env.url || !env.token)
39
+ return 'unconfigured';
40
+ try {
41
+ return LOOPBACK_HOSTS.has(new URL(env.url).hostname) ? 'echo' : 'provider';
42
+ }
43
+ catch {
44
+ return 'provider';
45
+ }
46
+ }
47
+ export function smsProviderHost(env = readEnv()) {
48
+ if (!env.url)
49
+ return null;
50
+ try {
51
+ return new URL(env.url).hostname;
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ }
57
+ export function createWhatsAppSender(env = readEnv()) {
58
+ return {
59
+ async sendOtp(to, code) {
60
+ if (!env.url || !env.token) {
61
+ return { status: 'skipped', error: 'WHATSAPP_ENDPOINT_URL / WHATSAPP_TOKEN não configurados' };
62
+ }
63
+ const body = buildPayload(to, code, env);
64
+ try {
65
+ const res = await fetch(env.url, {
66
+ method: 'POST',
67
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${env.token}` },
68
+ body,
69
+ signal: AbortSignal.timeout(10_000),
70
+ });
71
+ if (!res.ok) {
72
+ const detail = (await res.text().catch(() => '')).slice(0, 300);
73
+ return { status: 'failed', error: `WhatsApp respondeu ${res.status} ${detail}`.trim() };
74
+ }
75
+ return { status: 'sent' };
76
+ }
77
+ catch (err) {
78
+ return { status: 'failed', error: err.message };
79
+ }
80
+ },
81
+ };
82
+ }
83
+ //# sourceMappingURL=whatsapp.js.map