@neetru/cli 2.31.0 → 2.32.1

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 (245) hide show
  1. package/CHANGELOG.md +99 -2
  2. package/README.md +2 -0
  3. package/dist/cli-kit/choices.d.ts +33 -0
  4. package/dist/cli-kit/choices.js +32 -0
  5. package/dist/cli-kit/command-context.d.ts +98 -0
  6. package/dist/cli-kit/command-context.js +108 -0
  7. package/dist/cli-kit/errors.d.ts +79 -0
  8. package/dist/cli-kit/errors.js +94 -0
  9. package/dist/cli-kit/index.d.ts +8 -0
  10. package/dist/cli-kit/index.js +8 -0
  11. package/dist/cli-kit/suggest.d.ts +74 -0
  12. package/dist/cli-kit/suggest.js +207 -0
  13. package/dist/commands/admin/oidc-login.js +1 -1
  14. package/dist/commands/admin/sdk-credential.js +1 -1
  15. package/dist/commands/autocomplete.d.ts +1 -1
  16. package/dist/commands/autocomplete.js +1 -0
  17. package/dist/commands/bug.d.ts +59 -0
  18. package/dist/commands/bug.js +118 -6
  19. package/dist/commands/build.d.ts +24 -1
  20. package/dist/commands/build.js +195 -73
  21. package/dist/commands/chat.d.ts +94 -1
  22. package/dist/commands/chat.js +258 -26
  23. package/dist/commands/customers.js +1 -1
  24. package/dist/commands/db-move.js +3 -3
  25. package/dist/commands/db.js +20 -33
  26. package/dist/commands/deploy.d.ts +91 -2
  27. package/dist/commands/deploy.js +423 -78
  28. package/dist/commands/deployments.js +11 -6
  29. package/dist/commands/dev-context.js +15 -5
  30. package/dist/commands/docs-produto.d.ts +25 -0
  31. package/dist/commands/docs-produto.js +70 -8
  32. package/dist/commands/doctor.js +36 -1
  33. package/dist/commands/domains.js +85 -4
  34. package/dist/commands/env.d.ts +9 -3
  35. package/dist/commands/env.js +23 -3
  36. package/dist/commands/infra-read.js +2 -2
  37. package/dist/commands/init.js +8 -1
  38. package/dist/commands/login.js +35 -8
  39. package/dist/commands/logs.js +12 -4
  40. package/dist/commands/mobile.js +33 -9
  41. package/dist/commands/new.d.ts +29 -1
  42. package/dist/commands/new.js +92 -4
  43. package/dist/commands/open.js +16 -6
  44. package/dist/commands/ops.js +1 -1
  45. package/dist/commands/products-db.d.ts +1 -0
  46. package/dist/commands/products-db.js +41 -8
  47. package/dist/commands/products.d.ts +17 -3
  48. package/dist/commands/products.js +86 -18
  49. package/dist/commands/promote.js +30 -5
  50. package/dist/commands/secrets.d.ts +91 -0
  51. package/dist/commands/secrets.js +471 -0
  52. package/dist/commands/servers.js +8 -5
  53. package/dist/commands/status.js +2 -2
  54. package/dist/commands/support.js +5 -3
  55. package/dist/commands/tenants.js +28 -16
  56. package/dist/commands/upgrade.d.ts +9 -0
  57. package/dist/commands/upgrade.js +50 -1
  58. package/dist/commands/vm-memory.js +1 -0
  59. package/dist/commands/webhooks.d.ts +2 -0
  60. package/dist/commands/webhooks.js +9 -3
  61. package/dist/commands/workspaces.js +42 -11
  62. package/dist/index.js +70 -14
  63. package/dist/lib/api-client.d.ts +14 -0
  64. package/dist/lib/api-client.js +59 -6
  65. package/dist/lib/api-error.d.ts +14 -0
  66. package/dist/lib/api-error.js +107 -46
  67. package/dist/lib/build-targets.d.ts +127 -0
  68. package/dist/lib/build-targets.js +348 -0
  69. package/dist/lib/cli-error.d.ts +13 -0
  70. package/dist/lib/cli-error.js +24 -0
  71. package/dist/lib/cli-write.d.ts +43 -9
  72. package/dist/lib/cli-write.js +154 -45
  73. package/dist/lib/config-schema.d.ts +8 -8
  74. package/dist/lib/db-local/db-json.d.ts +1 -1
  75. package/dist/lib/db-local/db-json.js +13 -5
  76. package/dist/lib/db-local/env.d.ts +15 -3
  77. package/dist/lib/db-local/env.js +37 -10
  78. package/dist/lib/db-pipeline/build-deps.d.ts +5 -1
  79. package/dist/lib/db-pipeline/build-deps.js +23 -3
  80. package/dist/lib/db-pipeline/types.d.ts +6 -2
  81. package/dist/lib/db-pipeline/types.js +0 -18
  82. package/dist/lib/deploy-target-contract.d.ts +152 -0
  83. package/dist/lib/deploy-target-contract.js +411 -0
  84. package/dist/lib/env-file-do-build.d.ts +19 -0
  85. package/dist/lib/env-file-do-build.js +44 -0
  86. package/dist/lib/envs.d.ts +108 -0
  87. package/dist/lib/envs.js +127 -0
  88. package/dist/lib/fs-seguro.d.ts +63 -0
  89. package/dist/lib/fs-seguro.js +108 -0
  90. package/dist/lib/inquirer-sob-demanda.d.ts +14 -0
  91. package/dist/lib/inquirer-sob-demanda.js +18 -0
  92. package/dist/lib/mobile-local/mobile-json.js +10 -4
  93. package/dist/lib/pickers.d.ts +44 -7
  94. package/dist/lib/pickers.js +65 -7
  95. package/dist/lib/product-docs/sync.d.ts +6 -0
  96. package/dist/lib/product-docs/sync.js +10 -1
  97. package/dist/lib/public-secret-scan.d.ts +44 -3
  98. package/dist/lib/public-secret-scan.js +361 -36
  99. package/dist/lib/render.d.ts +16 -0
  100. package/dist/lib/render.js +34 -4
  101. package/dist/lib/segredo-produto.d.ts +124 -0
  102. package/dist/lib/segredo-produto.js +336 -0
  103. package/dist/lib/texto-utf8.d.ts +96 -0
  104. package/dist/lib/texto-utf8.js +196 -0
  105. package/package.json +2 -1
  106. package/dist/cli-kit/format.js.map +0 -1
  107. package/dist/cli-kit/glyphs.js.map +0 -1
  108. package/dist/cli-kit/index.js.map +0 -1
  109. package/dist/cli-kit/palette.js.map +0 -1
  110. package/dist/commands/add.js.map +0 -1
  111. package/dist/commands/admin/iam-check.js.map +0 -1
  112. package/dist/commands/admin/oidc-login.js.map +0 -1
  113. package/dist/commands/admin/sdk-credential.js.map +0 -1
  114. package/dist/commands/agent-release.js.map +0 -1
  115. package/dist/commands/agent-write.js.map +0 -1
  116. package/dist/commands/ai-models.js.map +0 -1
  117. package/dist/commands/ai.js.map +0 -1
  118. package/dist/commands/api-catalog.js.map +0 -1
  119. package/dist/commands/archive.js.map +0 -1
  120. package/dist/commands/artifact-registry.js.map +0 -1
  121. package/dist/commands/audit.js.map +0 -1
  122. package/dist/commands/auth.js.map +0 -1
  123. package/dist/commands/autocomplete.js.map +0 -1
  124. package/dist/commands/billing.js.map +0 -1
  125. package/dist/commands/bootstrap.js.map +0 -1
  126. package/dist/commands/bug.js.map +0 -1
  127. package/dist/commands/build.js.map +0 -1
  128. package/dist/commands/chat-menu.js.map +0 -1
  129. package/dist/commands/chat.js.map +0 -1
  130. package/dist/commands/cloud-run.js.map +0 -1
  131. package/dist/commands/code.js.map +0 -1
  132. package/dist/commands/config.js.map +0 -1
  133. package/dist/commands/customers.js.map +0 -1
  134. package/dist/commands/db-move.js.map +0 -1
  135. package/dist/commands/db.js.map +0 -1
  136. package/dist/commands/deploy.js.map +0 -1
  137. package/dist/commands/deployments.js.map +0 -1
  138. package/dist/commands/dev-context.js.map +0 -1
  139. package/dist/commands/dev-up.js.map +0 -1
  140. package/dist/commands/dev.js.map +0 -1
  141. package/dist/commands/docs-produto.js.map +0 -1
  142. package/dist/commands/docs.js.map +0 -1
  143. package/dist/commands/doctor.js.map +0 -1
  144. package/dist/commands/domains.js.map +0 -1
  145. package/dist/commands/dr.js.map +0 -1
  146. package/dist/commands/env.js.map +0 -1
  147. package/dist/commands/hosting-write.js.map +0 -1
  148. package/dist/commands/infra-read.js.map +0 -1
  149. package/dist/commands/init.js.map +0 -1
  150. package/dist/commands/login.js.map +0 -1
  151. package/dist/commands/logout.js.map +0 -1
  152. package/dist/commands/logs.js.map +0 -1
  153. package/dist/commands/mobile.js.map +0 -1
  154. package/dist/commands/mocks.js.map +0 -1
  155. package/dist/commands/new.js.map +0 -1
  156. package/dist/commands/open.js.map +0 -1
  157. package/dist/commands/ops.js.map +0 -1
  158. package/dist/commands/products-db.js.map +0 -1
  159. package/dist/commands/products.js.map +0 -1
  160. package/dist/commands/promote.js.map +0 -1
  161. package/dist/commands/publish.js.map +0 -1
  162. package/dist/commands/schema.js.map +0 -1
  163. package/dist/commands/servers.js.map +0 -1
  164. package/dist/commands/smoke.js.map +0 -1
  165. package/dist/commands/status.js.map +0 -1
  166. package/dist/commands/support.js.map +0 -1
  167. package/dist/commands/surface-status.js.map +0 -1
  168. package/dist/commands/tenants.js.map +0 -1
  169. package/dist/commands/ui.js.map +0 -1
  170. package/dist/commands/upgrade.js.map +0 -1
  171. package/dist/commands/validate.js.map +0 -1
  172. package/dist/commands/vm-memory.js.map +0 -1
  173. package/dist/commands/vm.js.map +0 -1
  174. package/dist/commands/webhooks.js.map +0 -1
  175. package/dist/commands/whoami.js.map +0 -1
  176. package/dist/commands/workspaces.js.map +0 -1
  177. package/dist/index.js.map +0 -1
  178. package/dist/lib/agent-artifact-url.js.map +0 -1
  179. package/dist/lib/agent-commands.js.map +0 -1
  180. package/dist/lib/ai/auth-discovery.js.map +0 -1
  181. package/dist/lib/ai/context.js.map +0 -1
  182. package/dist/lib/ai/orchestrator.js.map +0 -1
  183. package/dist/lib/api-client.js.map +0 -1
  184. package/dist/lib/api-error.js.map +0 -1
  185. package/dist/lib/auth.js.map +0 -1
  186. package/dist/lib/cli-read.js.map +0 -1
  187. package/dist/lib/cli-write.js.map +0 -1
  188. package/dist/lib/code-agent-budget.js.map +0 -1
  189. package/dist/lib/code-agent-files.js.map +0 -1
  190. package/dist/lib/code-agent-gateway.js.map +0 -1
  191. package/dist/lib/code-agent-git-tools.js.map +0 -1
  192. package/dist/lib/code-agent-protocol.js.map +0 -1
  193. package/dist/lib/code-agent-session.js.map +0 -1
  194. package/dist/lib/code-agent-settings.js.map +0 -1
  195. package/dist/lib/code-agent-skills.js.map +0 -1
  196. package/dist/lib/code-agent-subagents.js.map +0 -1
  197. package/dist/lib/code-agent-tools.js.map +0 -1
  198. package/dist/lib/code-agent-tui.js.map +0 -1
  199. package/dist/lib/code-agent-types.js.map +0 -1
  200. package/dist/lib/code-agent-ui.js.map +0 -1
  201. package/dist/lib/code-agent-web-tools.js.map +0 -1
  202. package/dist/lib/code-agent-write-tools.js.map +0 -1
  203. package/dist/lib/compose-generator.js.map +0 -1
  204. package/dist/lib/config-schema.js.map +0 -1
  205. package/dist/lib/config.js.map +0 -1
  206. package/dist/lib/db-local/db-json.js.map +0 -1
  207. package/dist/lib/db-local/env.js.map +0 -1
  208. package/dist/lib/db-local/fingerprint.js.map +0 -1
  209. package/dist/lib/db-local/index.js.map +0 -1
  210. package/dist/lib/db-pipeline/build-deps.js.map +0 -1
  211. package/dist/lib/db-pipeline/errors.js.map +0 -1
  212. package/dist/lib/db-pipeline/index.js.map +0 -1
  213. package/dist/lib/db-pipeline/pipeline.js.map +0 -1
  214. package/dist/lib/db-pipeline/rehearse.js.map +0 -1
  215. package/dist/lib/db-pipeline/types.js.map +0 -1
  216. package/dist/lib/dev-local-context.js.map +0 -1
  217. package/dist/lib/iam-preflight.js.map +0 -1
  218. package/dist/lib/mobile-local/index.js.map +0 -1
  219. package/dist/lib/mobile-local/mobile-json.js.map +0 -1
  220. package/dist/lib/pickers.js.map +0 -1
  221. package/dist/lib/product-config.js.map +0 -1
  222. package/dist/lib/product-docs/check.js.map +0 -1
  223. package/dist/lib/product-docs/config.js.map +0 -1
  224. package/dist/lib/product-docs/dates.js.map +0 -1
  225. package/dist/lib/product-docs/frontmatter.js.map +0 -1
  226. package/dist/lib/product-docs/git.js.map +0 -1
  227. package/dist/lib/product-docs/markdown.js.map +0 -1
  228. package/dist/lib/product-docs/ops.js.map +0 -1
  229. package/dist/lib/product-docs/structure.js.map +0 -1
  230. package/dist/lib/product-docs/sync.js.map +0 -1
  231. package/dist/lib/product-docs/taxonomy.js.map +0 -1
  232. package/dist/lib/product-docs/templates.js.map +0 -1
  233. package/dist/lib/public-secret-scan.js.map +0 -1
  234. package/dist/lib/render.js.map +0 -1
  235. package/dist/lib/schema-cache.js.map +0 -1
  236. package/dist/lib/sse.js.map +0 -1
  237. package/dist/lib/telemetry.js.map +0 -1
  238. package/dist/lib/token-resolver.js.map +0 -1
  239. package/dist/lib/url-allowlist.js.map +0 -1
  240. package/dist/lib/version-check.js.map +0 -1
  241. package/dist/utils/logger.js.map +0 -1
  242. package/dist/utils/logo.js.map +0 -1
  243. package/dist/utils/safe-exit.js.map +0 -1
  244. package/dist/utils/spinner-registry.js.map +0 -1
  245. package/dist/version.js.map +0 -1
@@ -32,6 +32,9 @@ import { requireToken, handleApiError } from '../lib/cli-read.js';
32
32
  import { log } from '../utils/logger.js';
33
33
  import { safeExit } from '../utils/safe-exit.js';
34
34
  import { fmtTimestamp } from '../lib/render.js';
35
+ import { exitWithCliError } from '../lib/cli-error.js';
36
+ import { isSubcommandVerb, reservedVerbError } from '../cli-kit/suggest.js';
37
+ import { commandDeclaresFlag, declaredFlags } from '../cli-kit/command-context.js';
35
38
  // ─── Helpers de display ───────────────────────────────────────────────────────
36
39
  function colorRole(role) {
37
40
  if (role === 'operator')
@@ -227,6 +230,135 @@ export async function runChatSend(message, opts) {
227
230
  }
228
231
  log.success(`Mensagem enviada (id: ${res.id}).`);
229
232
  }
233
+ const LER = new Set(['show', 'ver', 'get']);
234
+ const DEFINIR = new Set(['set', 'definir']);
235
+ const LIMPAR = new Set(['clear', 'limpar']);
236
+ /** Todos os verbos que `identity` entende — a lista que decide a recusa. */
237
+ const TODOS_OS_VERBOS_DE_IDENTIDADE = [...LER, ...DEFINIR, ...LIMPAR];
238
+ /** Os verbos de `identity` que a sugestão oferece — um por ação acima. */
239
+ const VERBOS_DE_IDENTIDADE = ['show', 'set', 'clear'];
240
+ /** Caminho do comando na árvore, sem o `neetru` (ex.: `'chat channels create'`). */
241
+ export function caminhoDoComando(cmd) {
242
+ const partes = [];
243
+ for (let c = cmd; c && c.parent; c = c.parent)
244
+ partes.unshift(c.name());
245
+ return partes.join(' ');
246
+ }
247
+ /** O que a árvore de verdade dá hoje — travado por teste contra `registerChatCommand`. */
248
+ export const CONTEXTO_DE_IDENTIDADE_PADRAO = {
249
+ comando: 'chat identity',
250
+ definirViaSend: 'chat send',
251
+ };
252
+ /** Deriva o contexto do nó `identity` do Commander (o que o `.action()` recebe). */
253
+ export function contextoDeIdentidadeDaArvore(identity) {
254
+ const send = identity.parent?.commands.find((c) => c.name() === 'send');
255
+ return {
256
+ comando: caminhoDoComando(identity),
257
+ definirViaSend: send && commandDeclaresFlag(send, '--as') ? caminhoDoComando(send) : null,
258
+ };
259
+ }
260
+ function verboComoApelido(verbo, ctx, escapeHint) {
261
+ const erro = reservedVerbError({
262
+ verb: verbo,
263
+ command: ctx.comando,
264
+ what: 'apelido',
265
+ subcommands: VERBOS_DE_IDENTIDADE,
266
+ escapeHint,
267
+ });
268
+ return { acao: 'erro', mensagem: erro.message, erro };
269
+ }
270
+ /** Como gravar um apelido que É um verbo: só pelo `--as` (quando a árvore o tem). */
271
+ function comoGravarVerboComoApelido(apelido, ctx) {
272
+ if (!ctx.definirViaSend)
273
+ return undefined;
274
+ return (`Para usar "${apelido}" como apelido: neetru ${ctx.definirViaSend} --as "${apelido}" "<mensagem>" ` +
275
+ '(grava o apelido na credencial e envia a mensagem).');
276
+ }
277
+ export function interpretarArgsDeIdentidade(args, ctx = CONTEXTO_DE_IDENTIDADE_PADRAO) {
278
+ if (args.length === 0)
279
+ return { acao: 'mostrar' };
280
+ const [primeiro, ...resto] = args;
281
+ const palavra = primeiro.trim().toLowerCase();
282
+ if (args.length === 1 && LER.has(palavra))
283
+ return { acao: 'mostrar' };
284
+ if (args.length === 1 && LIMPAR.has(palavra))
285
+ return { acao: 'definir', apelido: '' };
286
+ if (DEFINIR.has(palavra)) {
287
+ if (resto.length === 0) {
288
+ return {
289
+ acao: 'erro',
290
+ mensagem: `Uso: neetru ${ctx.comando} set "Seu Apelido" (para limpar: neetru ${ctx.comando} clear)`,
291
+ };
292
+ }
293
+ const apelido = resto.join(' ');
294
+ // `identity set show` grava o verbo do mesmo jeito que `identity show` gravava.
295
+ if (isSubcommandVerb(apelido, TODOS_OS_VERBOS_DE_IDENTIDADE)) {
296
+ return verboComoApelido(apelido.trim(), ctx, comoGravarVerboComoApelido(apelido.trim(), ctx));
297
+ }
298
+ return { acao: 'definir', apelido };
299
+ }
300
+ // Verbo na 1ª posição nunca é o começo de um apelido: é a pessoa achando que
301
+ // aquilo é subcomando (`identity describe`, `identity show Fulano`).
302
+ if (isSubcommandVerb(palavra, TODOS_OS_VERBOS_DE_IDENTIDADE)) {
303
+ const texto = args.join(' ');
304
+ return verboComoApelido(primeiro.trim(), ctx, args.length > 1
305
+ ? `Para gravar "${texto}" como apelido: neetru ${ctx.comando} set "${texto}"`
306
+ : comoGravarVerboComoApelido(primeiro.trim(), ctx));
307
+ }
308
+ return { acao: 'definir', apelido: args.join(' ') };
309
+ }
310
+ function subcomandosIrmaos(command) {
311
+ const irmaos = command?.parent?.commands ?? [];
312
+ const apelidos = {};
313
+ for (const c of irmaos)
314
+ for (const a of c.aliases())
315
+ apelidos[a] = c.name();
316
+ return { nomes: irmaos.map((c) => c.name()), apelidos };
317
+ }
318
+ /**
319
+ * Texto livre POSICIONAL de uma palavra só que é SUBCOMANDO irmão (nome,
320
+ * apelido ou sinônimo): recusa (P105). `neetru chat send history` postava
321
+ * "history" no canal global achando que listava; `neetru chat channels create
322
+ * close` criava o canal "close". Os subcomandos e o caminho citados saem do
323
+ * nó do Commander em execução (`command`), não de literal.
324
+ *
325
+ * `null` quando o texto pode seguir: resposta comum (`show`, `ok`, `top`,
326
+ * `beleza`, `valeu`, `sim`, `blz` — revisão do #87, BAIXO 6) e frase que só
327
+ * começa com uma palavra de comando ("history do dia").
328
+ */
329
+ export function verboComoTextoPosicional(input) {
330
+ const t = input.texto.trim();
331
+ const irmaos = subcomandosIrmaos(input.command);
332
+ if (!t || /\s/.test(t) || !isSubcommandVerb(t, [...irmaos.nomes, ...Object.keys(irmaos.apelidos)]))
333
+ return null;
334
+ return reservedVerbError({
335
+ verb: t,
336
+ command: caminhoDoComando(input.command),
337
+ parent: caminhoDoComando(input.command.parent),
338
+ what: input.oQue,
339
+ subcommands: irmaos.nomes,
340
+ aliases: irmaos.apelidos,
341
+ escapeHint: input.escapeHint,
342
+ });
343
+ }
344
+ /**
345
+ * Saída de máquina do `runChatIdentity`, a mesma na leitura e na gravação:
346
+ * `quiet` não imprime nada e `json` imprime a linha do contrato. Devolve `true`
347
+ * quando já respondeu — aí o chamador não imprime o texto humano.
348
+ *
349
+ * `quiet` ANTES de `json` (2026-10-12): era o contrário, e `send --as --json`
350
+ * imprimia DUAS linhas JSON no stdout (a da identidade e a do envio), onde o
351
+ * contrato de máquina é uma.
352
+ */
353
+ function respondeuEmModoMaquina(res, opts) {
354
+ if (opts.quiet)
355
+ return true;
356
+ if (opts.json) {
357
+ console.log(JSON.stringify(res));
358
+ return true;
359
+ }
360
+ return false;
361
+ }
230
362
  /**
231
363
  * `neetru chat identity [apelido]` — apelido inerente à credencial (CH3).
232
364
  *
@@ -250,17 +382,15 @@ export async function runChatIdentity(displayName, opts = {}) {
250
382
  handleApiError(error, opts.json);
251
383
  return;
252
384
  }
253
- if (opts.json) {
254
- console.log(JSON.stringify(res));
255
- return;
256
- }
385
+ if (respondeuEmModoMaquina(res, opts))
386
+ return res;
257
387
  if (res.displayName) {
258
388
  log.info(`Apelido atual: ${chalk.bold(res.displayName)}`);
259
389
  }
260
390
  else {
261
391
  log.info(`Sem apelido — suas mensagens usam o email${res.email ? ` (${res.email})` : ''}.`);
262
392
  }
263
- return;
393
+ return res;
264
394
  }
265
395
  // Com argumento → persiste.
266
396
  const spinner = opts.json || opts.quiet ? null : ora({ text: 'Salvando apelido…', color: 'blue' }).start();
@@ -278,18 +408,56 @@ export async function runChatIdentity(displayName, opts = {}) {
278
408
  handleApiError(error, opts.json);
279
409
  return;
280
410
  }
281
- if (opts.json) {
282
- console.log(JSON.stringify(res));
283
- return;
284
- }
285
- if (opts.quiet)
286
- return;
411
+ if (respondeuEmModoMaquina(res, opts))
412
+ return res;
287
413
  if (res.displayName) {
288
414
  log.success(`Apelido definido: ${chalk.bold(res.displayName)} — suas mensagens no chat aparecem com este nome.`);
289
415
  }
290
416
  else {
291
417
  log.success('Apelido limpo — suas mensagens voltam a usar o email.');
292
418
  }
419
+ return res;
420
+ }
421
+ /**
422
+ * `chat send --as <apelido>`.
423
+ *
424
+ * O Core NÃO tem apelido por mensagem — o nome vem da credencial
425
+ * (`cli_api_keys/{keyId}.displayName`). Então `--as` só pode PERSISTIR. O defeito
426
+ * (bug_09574d4b) não era persistir: era persistir CALADO — e o help da versão
427
+ * publicada dizia só "antes de enviar", então quem lia concluía, com razão, que
428
+ * valia para aquela mensagem. Credencial compartilhada entre sessões fazia uma
429
+ * renomear a outra sem ninguém ver.
430
+ *
431
+ * Agora: se o apelido já é o pedido, nada muda (nem chamada de escrita); se muda,
432
+ * o aviso vai pro STDERR dizendo de quem para quem e que vale para as próximas
433
+ * mensagens da credencial. STDERR porque em `--json` o stdout é contrato de uma
434
+ * linha.
435
+ */
436
+ export async function aplicarApelidoDoSend(apelidoBruto, json) {
437
+ // O Core guarda o apelido aparado; sem o trim, `--as "Dev Core "` gravava e
438
+ // avisava a CADA envio, porque nunca batia com o que voltava.
439
+ const apelido = apelidoBruto.trim();
440
+ const token = await requireToken(json);
441
+ // Leitura MELHOR-ESFORÇO (revisão do #76): se falhar (rede, 5xx), o envio não
442
+ // pode morrer aqui — antes deste PR a mensagem saía. `null` = não sei.
443
+ let antes = null;
444
+ try {
445
+ const atual = await apiRequest('/api/cli/v1/chat/identity', { token });
446
+ const nome = (atual?.displayName ?? '').trim();
447
+ // Sem apelido, o Core devolve o E-MAIL no `displayName` (`resolveDisplayName`
448
+ // = `name || actor.email`). Isso é "sem apelido", não um apelido.
449
+ antes = nome && nome !== atual?.email ? nome : '';
450
+ }
451
+ catch {
452
+ process.stderr.write('⚠ --as: não consegui ler o apelido atual da credencial; gravando mesmo assim.\n');
453
+ }
454
+ if (antes !== null && antes === apelido)
455
+ return;
456
+ await runChatIdentity(apelido, { quiet: true, json });
457
+ const de = antes === null ? '(não lido)' : antes || '(email)';
458
+ process.stderr.write(`⚠ --as MUDOU o apelido da credencial: "${de}" → "${apelido || '(email)'}".\n` +
459
+ ' Vale para TODAS as próximas mensagens deste token — e de quem compartilha a mesma\n' +
460
+ ' credencial. Para assinar só esta mensagem, escreva o nome no corpo.\n');
293
461
  }
294
462
  /** Extrai epoch ms do createdAt serializado (Timestamp/ISO/ms). 0 se inválido. */
295
463
  function createdAtMs(createdAt) {
@@ -549,6 +717,25 @@ export async function runChatLoop(opts) {
549
717
  }
550
718
  }
551
719
  // ─── Registro do comando `chat` em program ────────────────────────────────────
720
+ /**
721
+ * Valor de uma opção que o pai `chat` TAMBÉM declara (`--json`, `--product`…).
722
+ *
723
+ * Sem `enablePositionalOptions`, o commander 12 entrega a opção ao PRIMEIRO
724
+ * ancestral que a conhece — o `chat` — em qualquer posição da linha, e o
725
+ * subcomando recebe `undefined`. Foi o bug_71f42072 em `history`/`send`; a
726
+ * revisão do #76 achou o mesmo em `identity` (`identity set --json` saía em
727
+ * texto) e a varredura achou em `channels`, `channels create` (com `--product`
728
+ * criava canal `custom`) e `channels close`. Prefere o subcomando e sobe pelos
729
+ * pais — `channels create` é NETO do `chat`.
730
+ */
731
+ function opcaoHerdada(command, nome) {
732
+ for (let c = command; c; c = c.parent) {
733
+ const valor = c.opts()[nome];
734
+ if (valor !== undefined)
735
+ return valor;
736
+ }
737
+ return undefined;
738
+ }
552
739
  /**
553
740
  * Registra o comando `neetru chat` no programa Commander.
554
741
  *
@@ -602,9 +789,9 @@ export function registerChatCommand(program) {
602
789
  .command('channels')
603
790
  .description('Canais de chat: listar (default), criar, fechar')
604
791
  .option('--json', 'saída em JSON')
605
- .action(async (cmdOpts) => {
792
+ .action(async (_cmdOpts, command) => {
606
793
  // Sem subcomando → lista os canais abertos.
607
- await runChatChannels({ json: !!cmdOpts.json });
794
+ await runChatChannels({ json: !!opcaoHerdada(command, 'json') });
608
795
  });
609
796
  channelsCmd
610
797
  .command('create <name>')
@@ -612,29 +799,55 @@ export function registerChatCommand(program) {
612
799
  .option('--product <slug>', 'vincula o canal a um produto (type=product)')
613
800
  .option('--type <tipo>', 'product|custom (default: product se --product, senão custom)')
614
801
  .option('--json', 'saída em JSON')
615
- .action(async (name, cmdOpts) => {
802
+ .action(async (name, cmdOpts, command) => {
803
+ // P105: `channels create close` criava o canal "close" para todo mundo.
804
+ const recusa = verboComoTextoPosicional({ texto: name, command, oQue: 'nome de canal' });
805
+ if (recusa) {
806
+ exitWithCliError(recusa, !!opcaoHerdada(command, 'json'));
807
+ return;
808
+ }
616
809
  await runChatChannelCreate(name, {
617
- product: cmdOpts.product,
810
+ product: opcaoHerdada(command, 'product'),
618
811
  type: cmdOpts.type,
619
- json: !!cmdOpts.json,
812
+ json: !!opcaoHerdada(command, 'json'),
620
813
  });
621
814
  });
622
815
  channelsCmd
623
816
  .command('close <id>')
624
817
  .description('Fechar um canal (o Global não pode ser fechado)')
625
818
  .option('--json', 'saída em JSON')
626
- .action(async (id, cmdOpts) => {
627
- await runChatChannelClose(id, { json: !!cmdOpts.json });
819
+ .action(async (id, _cmdOpts, command) => {
820
+ await runChatChannelClose(id, { json: !!opcaoHerdada(command, 'json') });
628
821
  });
629
822
  // ── chat identity (CH3) ─────────────────────────────────────────────────────
630
823
  // Apelido inerente à credencial: a IA se identifica em nome do dev dono do token.
631
824
  chatCmd
632
- .command('identity [apelido]')
825
+ .command('identity [args...]')
633
826
  .alias('apelido')
634
- .description('Ver/definir o apelido da credencial no chat (persiste por token). Vazio limpa. CH3.')
827
+ .description('Apelido da credencial no chat (persiste por token). ' +
828
+ '`identity` ou `identity show` lê; `identity set "Nome"` grava; `identity clear` limpa.')
635
829
  .option('--json', 'saída em JSON')
636
- .action(async (apelido, cmdOpts) => {
637
- await runChatIdentity(apelido, { json: !!cmdOpts.json });
830
+ .action(async (args, _cmdOpts, command) => {
831
+ // O `--json` vai pro pai `chat` (ver `opcaoHerdada`): ler só `cmdOpts.json`
832
+ // deixava este ramo — e o erro de uso em JSON — inalcançável.
833
+ const json = !!opcaoHerdada(command, 'json');
834
+ const pedido = interpretarArgsDeIdentidade(args ?? [], contextoDeIdentidadeDaArvore(command));
835
+ if (pedido.acao === 'erro' && pedido.erro) {
836
+ // P105: verbo reservado como apelido — o erro tipado leva as sugestões.
837
+ exitWithCliError(pedido.erro, json);
838
+ return;
839
+ }
840
+ if (pedido.acao === 'erro') {
841
+ if (json) {
842
+ console.log(JSON.stringify({ ok: false, error: 'uso_invalido', message: pedido.mensagem }));
843
+ }
844
+ else {
845
+ log.error(pedido.mensagem);
846
+ }
847
+ safeExit(1);
848
+ return;
849
+ }
850
+ await runChatIdentity(pedido.acao === 'mostrar' ? undefined : pedido.apelido, { json });
638
851
  });
639
852
  // ── chat send ──────────────────────────────────────────────────────────────
640
853
  // Mensagem pode vir como argumento posicional OU via --mensagem/--message.
@@ -652,15 +865,34 @@ export function registerChatCommand(program) {
652
865
  .option('--product <slug>', 'slug do produto — legado (retrocompat)')
653
866
  .option('--actor-type <tipo>', 'tipo do ator: cli|human|agent_claude (default: cli)', 'cli')
654
867
  .option('--product-id <id>', 'id interno do produto (opcional, legado)')
655
- .option('--as <apelido>', 'definir o apelido da credencial antes de enviar (persiste — CH3)')
868
+ .option('--as <apelido>', 'MUDA o apelido da CREDENCIAL (persiste p/ as próximas mensagens; avisa no stderr). ' +
869
+ 'O Core não tem apelido por mensagem.')
656
870
  .option('--json', 'saída em JSON')
657
871
  .action(async (mensagem, cmdOpts, command) => {
658
872
  // bug_71f42072: mesma colisão de --channel/--canal/--product entre o pai
659
873
  // `chat` e o subcomando. Prefere o subcomando, cai no pai.
660
874
  const parent = command?.parent?.opts?.() ?? {};
661
- // CH3: --as persiste o apelido na credencial antes de postar (quiet).
875
+ const json = !!(cmdOpts.json || parent.json);
876
+ // P105: verbo sozinho no POSICIONAL não vai pro canal — antes de qualquer
877
+ // escrita (inclusive a do --as). A flag explícita continua mandando.
878
+ if (mensagem !== undefined) {
879
+ const flagDaMensagem = declaredFlags(['--mensagem', '--message'], command)[0];
880
+ const recusa = verboComoTextoPosicional({
881
+ texto: mensagem,
882
+ command,
883
+ oQue: 'mensagem posicional',
884
+ escapeHint: flagDaMensagem
885
+ ? `Para enviar essa palavra como mensagem: neetru ${caminhoDoComando(command)} ${flagDaMensagem} ${mensagem.trim()}`
886
+ : undefined,
887
+ });
888
+ if (recusa) {
889
+ exitWithCliError(recusa, json);
890
+ return;
891
+ }
892
+ }
893
+ // CH3: --as persiste o apelido na credencial — agora AVISANDO (bug_09574d4b).
662
894
  if (typeof cmdOpts.as === 'string') {
663
- await runChatIdentity(cmdOpts.as, { quiet: true, json: !!(cmdOpts.json || parent.json) });
895
+ await aplicarApelidoDoSend(cmdOpts.as, json);
664
896
  }
665
897
  const msg = (mensagem ?? cmdOpts.message ?? cmdOpts.mensagem ?? '').toString();
666
898
  await runChatSend(msg, {
@@ -668,7 +900,7 @@ export function registerChatCommand(program) {
668
900
  product: cmdOpts.product ?? parent.product,
669
901
  actorType: cmdOpts.actorType,
670
902
  productId: cmdOpts.productId,
671
- json: !!(cmdOpts.json || parent.json),
903
+ json,
672
904
  });
673
905
  });
674
906
  // ── chat history ───────────────────────────────────────────────────────────
@@ -56,7 +56,7 @@ export async function runCustomersList(opts = {}) {
56
56
  }
57
57
  log.heading(`Customers (${res.count})`);
58
58
  renderTable([
59
- { header: 'ID', value: (r) => r.id, maxWidth: 24 },
59
+ { header: 'ID', value: (r) => r.id, identificador: true },
60
60
  { header: 'NOME', value: (r) => r.name ?? '—', maxWidth: 28 },
61
61
  { header: 'EMAIL', value: (r) => r.contactEmail ?? '—', maxWidth: 30 },
62
62
  { header: 'CNPJ/CPF', value: (r) => r.cnpj ?? '—', maxWidth: 18 },
@@ -11,7 +11,7 @@
11
11
  * (`rolled_back`/`failed`) são reportados EXPLICITAMENTE como "o produto
12
12
  * seguiu na origem" (fail-closed pro estado atual) e saem com exit != 0.
13
13
  * - `move` é destrutivo sobre banco de produção → confirmação interativa
14
- * (pulável com --yes) + step-up MFA (resolveStepUp: sessão 1h → --mfa-token
14
+ * (pulável com --yes) + step-up MFA (resolveStepUp: --mfa-token → sessão 1h
15
15
  * → prompt). `--dry-run` roda só o preflight (sem MFA, sem confirmação).
16
16
  *
17
17
  * SEGURANÇA por construção (iteração 2 — abort/decommission NÃO podem apagar dado
@@ -1004,11 +1004,11 @@ export async function runDbWhere(opts = {}) {
1004
1004
  }
1005
1005
  log.heading(`Bancos do produto "${opts.product}" (${dbs.length})`);
1006
1006
  renderTable([
1007
- { header: 'ID', value: (r) => r.id, maxWidth: 24 },
1007
+ { header: 'ID', value: (r) => r.id, identificador: true },
1008
1008
  { header: 'ENGINE', value: (r) => r.engine ?? '—', maxWidth: 20 },
1009
1009
  { header: 'ENV', value: (r) => r.environment ?? r.env ?? '—', maxWidth: 12 },
1010
1010
  { header: 'STATUS', value: (r) => r.status ?? '—', maxWidth: 14 },
1011
- { header: 'VM (onde vive)', value: (r) => r.serverId ?? '—', maxWidth: 20 },
1011
+ { header: 'VM (onde vive)', value: (r) => r.serverId ?? '—', identificador: true },
1012
1012
  ], dbs);
1013
1013
  }
1014
1014
  //# sourceMappingURL=db-move.js.map
@@ -30,19 +30,17 @@ import { apiRequest, CliApiError } from '../lib/api-client.js';
30
30
  import { handleApiError } from '../lib/api-error.js';
31
31
  import { renderTable, fmtTimestamp } from '../lib/render.js';
32
32
  import { readDbJson, writeDbJson, normalizeEnv, } from '../lib/db-local/index.js';
33
+ // Direto do módulo (não do index): testes mockam `db-local/index.js` só com
34
+ // `normalizeEnv`, e o erro de `--env` precisa existir mesmo assim.
35
+ import { erroAmbienteBancoInvalido } from '../lib/db-local/env.js';
36
+ import { exitWithCliError } from '../lib/cli-error.js';
33
37
  import { safeExit } from '../utils/safe-exit.js';
34
38
  import { runApplyPipeline, runExecSqlPipeline, buildPipelineDeps, isPipelineError, } from '../lib/db-pipeline/index.js';
35
39
  import { resolveStepUp } from '../lib/cli-write.js';
40
+ import { carregarInquirer } from '../lib/inquirer-sob-demanda.js';
36
41
  import { loadProductConfig } from '../lib/product-config.js';
37
- /**
38
- * `inquirer` sob demanda (bug_b1e9af59): ~0,5s de carga, e so os prompts
39
- * interativos usam. Era o unico import estatico dele no grafo de `neetru db`
40
- * — `db list`/`db status`/`db apply --json` (e os testes) pagavam a carga sem
41
- * nunca perguntar nada.
42
- */
43
- async function carregarInquirer() {
44
- return (await import('inquirer')).default;
45
- }
42
+ // `inquirer` sob demanda (bug_b1e9af59): só os prompts interativos o carregam,
43
+ // pelo `carregarInquirer()` único de `lib/inquirer-sob-demanda.ts` (P115).
46
44
  // ---------------------------------------------------------------------------
47
45
  // Constantes e tipos locais
48
46
  // ---------------------------------------------------------------------------
@@ -159,8 +157,8 @@ export async function runDbList(opts = {}) {
159
157
  }
160
158
  log.heading(`Bancos (${resp.count})`);
161
159
  renderTable([
162
- { header: 'ID', value: (r) => r.id, maxWidth: 28 },
163
- { header: 'Produto', value: (r) => r.productId, maxWidth: 20 },
160
+ { header: 'ID', value: (r) => r.id, identificador: true },
161
+ { header: 'Produto', value: (r) => r.productId, identificador: true },
164
162
  { header: 'Label', value: (r) => r.label ?? '—', maxWidth: 24 },
165
163
  { header: 'Engine', value: (r) => r.engine, maxWidth: 22 },
166
164
  // gestovendas #11 fix: Core retorna `environment`; `env` é alias legado.
@@ -428,8 +426,9 @@ export async function runDbInit(opts = {}) {
428
426
  env = opts.env ? normalizeEnv(opts.env) : 'dev-local';
429
427
  }
430
428
  catch {
431
- log.error(`Ambiente inválido: "${opts.env}". Use dev-local, staging ou production.`);
432
- safeExit(1);
429
+ // bug_89d2497e: valores válidos da fonte real (`NEETRU_ENVS`); com --json,
430
+ // sai o JSON `invalid_env` como em `db apply`/`db exec-sql`.
431
+ exitWithCliError(erroAmbienteBancoInvalido(opts.env), opts.json);
433
432
  return;
434
433
  }
435
434
  // ── bug_062f7fea (pdv-agiliza): engines vm-* (+ nosql-vm) exigem serverId ──
@@ -753,14 +752,8 @@ export async function runDbApply(opts = {}) {
753
752
  env = normalizeEnv(opts.env ?? 'dev-local');
754
753
  }
755
754
  catch {
756
- const msg = `Ambiente inválido: "${opts.env}". Use dev-local, staging ou production.`;
757
- if (opts.json) {
758
- jsonError(msg, 'invalid_env');
759
- }
760
- else {
761
- log.error(msg);
762
- }
763
- safeExit(1);
755
+ // bug_89d2497e: valores válidos da fonte real (`NEETRU_ENVS`), não copiados.
756
+ exitWithCliError(erroAmbienteBancoInvalido(opts.env), opts.json);
764
757
  return;
765
758
  }
766
759
  // Resolve schema path.
@@ -1103,14 +1096,8 @@ export async function runDbExecSql(opts = {}) {
1103
1096
  env = normalizeEnv(opts.env ?? 'dev-local');
1104
1097
  }
1105
1098
  catch {
1106
- const msg = `Ambiente inválido: "${opts.env}". Use dev-local, staging ou production.`;
1107
- if (opts.json) {
1108
- jsonError(msg, 'invalid_env');
1109
- }
1110
- else {
1111
- log.error(msg);
1112
- }
1113
- safeExit(1);
1099
+ // bug_89d2497e: valores válidos da fonte real (`NEETRU_ENVS`), não copiados.
1100
+ exitWithCliError(erroAmbienteBancoInvalido(opts.env), opts.json);
1114
1101
  return;
1115
1102
  }
1116
1103
  const canonicalEnv = env;
@@ -1263,8 +1250,8 @@ export async function runDbMigrationsList(opts = {}) {
1263
1250
  }
1264
1251
  log.heading(`Migrações (${resp.count})`);
1265
1252
  renderTable([
1266
- { header: 'ID', value: (r) => r.id, maxWidth: 28 },
1267
- { header: 'Banco', value: (r) => r.dbId ?? '—', maxWidth: 20 },
1253
+ { header: 'ID', value: (r) => r.id, identificador: true },
1254
+ { header: 'Banco', value: (r) => r.dbId ?? '—', identificador: true },
1268
1255
  { header: 'Status', value: (r) => r.status, maxWidth: 20 },
1269
1256
  { header: 'Severidade', value: (r) => severityColor(r.severity), maxWidth: 14 },
1270
1257
  { header: 'Aplicado', value: (r) => fmtTimestamp(r.appliedAt ?? r.createdAt), maxWidth: 18 },
@@ -1361,7 +1348,7 @@ export async function runDbBackups(dbId, opts = {}) {
1361
1348
  }
1362
1349
  log.heading(`Backups do banco ${chalk.bold(dbId)} (${resp.count})`);
1363
1350
  renderTable([
1364
- { header: 'ID', value: (r) => r.id, maxWidth: 28 },
1351
+ { header: 'ID', value: (r) => r.id, identificador: true },
1365
1352
  { header: 'Status', value: (r) => statusColor(r.status), maxWidth: 16 },
1366
1353
  { header: 'Data/Hora', value: (r) => fmtTimestamp(r.timestamp), maxWidth: 22 },
1367
1354
  { header: 'Tamanho (MB)', value: (r) => r.sizeMb != null ? String(r.sizeMb) : '—', maxWidth: 14 },
@@ -1551,7 +1538,7 @@ export async function runDbHosts(opts = {}) {
1551
1538
  }
1552
1539
  log.heading(`VM Hosts de Banco (${resp.count})`);
1553
1540
  renderTable([
1554
- { header: 'ID', value: (r) => r.id, maxWidth: 20 },
1541
+ { header: 'ID', value: (r) => r.id, identificador: true },
1555
1542
  { header: 'Nome', value: (r) => r.name ?? '—', maxWidth: 22 },
1556
1543
  { header: 'Região', value: (r) => r.region ?? '—', maxWidth: 14 },
1557
1544
  { header: 'Status', value: (r) => statusColor(r.status ?? '—'), maxWidth: 14 },
@@ -1,3 +1,5 @@
1
+ import { type RuntimeEnvironmentId } from '../lib/envs.js';
2
+ import { type BuildManifest } from './build.js';
1
3
  import { type PublicSecretOverrideRecord } from '../lib/public-secret-scan.js';
2
4
  /**
3
5
  * bug_a5786e03 (2026-05-31): o Core/agente só conhecem os stacks canônicos
@@ -134,28 +136,91 @@ export declare function runPublicSecretGate(source: {
134
136
  allowReason?: string;
135
137
  json?: boolean;
136
138
  }): Promise<PublicSecretGateResult>;
139
+ /**
140
+ * bug_678ddc98 (elo que faltava): decide se este deploy precisa DIZER ao Core
141
+ * qual é o entrypoint, em vez de deixar o agente adivinhar.
142
+ *
143
+ * O #68 ensinou o `neetru build` a empacotar monorepo e a calcular o entrypoint
144
+ * aninhado (`apps/web/server.js`), mas a cadeia parava no `manifest.json`:
145
+ * `manifest.entrypoint` nunca foi lido por ninguém — era campo write-only.
146
+ * Num monorepo o tarball fica assim:
147
+ *
148
+ * <versionDir>/node_modules/… ← tracejado, na RAIZ
149
+ * <versionDir>/packages/ui/… ← pacotes internos do workspace
150
+ * <versionDir>/apps/web/server.js ← entrypoint REAL
151
+ * <versionDir>/apps/web/package.json ← o package.json DO APP mora aqui
152
+ *
153
+ * O agente (`neetru-agent-service-vm/src/lib/deploy-plan.ts:117-124`) infere o
154
+ * comando de start olhando SÓ a raiz do versionDir: `server.js` na raiz,
155
+ * `.next/` na raiz, `.next/standalone/server.js`, e `scripts.start` do
156
+ * `package.json` da raiz. Em monorepo os três primeiros não existem, e o
157
+ * quarto não ajuda: o Next.js escreve o package.json do app em
158
+ * `<appPrefix>/package.json` (`next/dist/build/utils.js:1197`), então o que
159
+ * houver na raiz é o package.json do WORKSPACE — que ou não tem `start`, ou
160
+ * tem um `start` de ferramenta de monorepo (`turbo start`), nunca
161
+ * `node apps/web/server.js`. Nos dois casos o agente erra: cai no fallback
162
+ * `'npm start'` (deploy-plan.ts:124) e o systemd entra em crash-loop.
163
+ *
164
+ * REGRA: só mandamos o entrypoint quando o agente NÃO consegue acertar sozinho,
165
+ * isto é quando o standalone está aninhado (`appPrefix` preenchido). Para app
166
+ * único o agente já resolve certo — e mandar `pkg.main`/`index.js` ali
167
+ * ATROPELARIA o `npm start` de produtos como o gestovendas, que dependem de
168
+ * `scripts.start`. Conserto que quebra o caso que funcionava não é conserto.
169
+ *
170
+ * Também não mandamos para stack não-node: ali `entrypoint` é `index.html` /
171
+ * `index.php` / referência de imagem, que não é "arquivo para o `node` rodar".
172
+ *
173
+ * ATENÇÃO — a cadeia ainda NÃO fecha só com isto: o Core descarta o campo hoje
174
+ * (`deployRequestSchema` não o declara, e objeto zod sem `.passthrough()` remove
175
+ * chave desconhecida em silêncio). Ver "o que falta no Core" na descrição do PR.
176
+ */
177
+ export declare function entrypointParaDeploy(manifest: Pick<BuildManifest, 'entrypoint' | 'appPrefix' | 'stack'> | null | undefined): string | undefined;
137
178
  /** `true` pra `prod`/`production` (o Core normaliza `production`→`prod`). */
138
179
  export declare function isProdEnv(envName: string | undefined): boolean;
139
180
  /**
140
181
  * M7 (auditoria cli/v1 2026-09-29): `POST /api/cli/v1/deploy` com
141
182
  * `environmentId` prod exige step-up MFA no Core (mesmo gate de `promote`).
142
- * Resolve os headers (sessão 1h → --mfa-token → prompt TOTP; fail-closed em
183
+ * Resolve os headers (--mfa-token → sessão 1h → prompt TOTP; fail-closed em
143
184
  * --json/--non-interactive/sem TTY) só quando o alvo é prod. Chamado logo
144
185
  * antes do POST — depois do build/upload — pra o TOTP não vencer no meio.
145
186
  */
146
187
  export declare function resolveDeployStepUpHeaders(opts: Pick<DeployOptions, 'mfaToken' | 'json'>, envName: string, nonInteractive: boolean): Promise<Record<string, string>>;
188
+ /**
189
+ * Mostra os `warnings[]` que o Core devolve no deploy.
190
+ *
191
+ * Medido em 2026-10-12: o Core devolve `warnings` nos TRES alvos e o comentario
192
+ * dele diz, literalmente, "`warnings[]` na resposta JSON (o CLI exibe pro
193
+ * operador)" (`cli/v1/deploy/route.ts:309`). `grep warnings` em `deploy.ts`
194
+ * devolvia ZERO — a promessa nunca foi cumprida.
195
+ *
196
+ * O caso que mais doia: a CLI manda o ambiente default e o Core normaliza, e o
197
+ * aviso de que `environmentId` foi ASSUMIDO disparava em todo deploy sem
198
+ * `--env` — e sumia 100% das vezes. O operador achava que tinha escolhido.
199
+ *
200
+ * Vai pro STDERR de proposito: em `--json` o stdout e contrato de maquina (uma
201
+ * linha JSON) e nao pode ser poluido. Quem le JSON ja recebe `warnings` no
202
+ * proprio corpo.
203
+ */
204
+ export declare function mostrarAvisosDoCore(warnings: unknown, json?: boolean): void;
205
+ /**
206
+ * `--env` do deploy workspace/cloud-run: fora do vocabulário, encerra com exit
207
+ * 1 e os valores válidos da MESMA lista que validou (`invalid_env`, com `flag`,
208
+ * `value` e `valid` no JSON). Exportada para teste.
209
+ */
210
+ export declare function validarAmbienteDoDeploy(envName: string, json: boolean | undefined): void;
147
211
  /**
148
212
  * Mapeia o `envName` do deploy ('dev' | 'staging' | 'prod' — vocabulário do
149
213
  * `--env` deste comando) pro `targetEnv` que `neetru build` entende
150
214
  * ('dev' | 'workspace' | 'prod' — vocabulário de `BuildOptions`/SDK).
151
215
  * 'staging' vira 'workspace' (mesmo conceito de ambiente não-dev/não-prod).
152
216
  * Valor desconhecido cai em 'prod' (mesmo default conservador de `runBuild`).
217
+ * P105: a correspondência sai de `VOCABULARIOS_DE_AMBIENTE`, não de `if`s.
153
218
  *
154
219
  * Fix (2026-07-22, achado da revisão adversarial): sem isso, todo build
155
220
  * implícito de `neetru deploy --env dev`/`--env staging` (quando não há
156
221
  * artifact em cache) forçava NEXT_PUBLIC_NEETRU_ENV=prod incondicionalmente.
157
222
  */
158
- export declare function deployEnvToBuildTargetEnv(envName: string): 'dev' | 'workspace' | 'prod';
223
+ export declare function deployEnvToBuildTargetEnv(envName: string): RuntimeEnvironmentId;
159
224
  /**
160
225
  * bug_5b6bb0d7 — segredos NÃO podem viajar por env no deploy.
161
226
  *
@@ -178,6 +243,30 @@ export declare function deployEnvToBuildTargetEnv(envName: string): 'dev' | 'wor
178
243
  * saírem de sincronia, mas exige atualização manual dos dois lados.
179
244
  */
180
245
  export declare const SECRET_ENVVAR_PATTERNS: RegExp[];
246
+ /**
247
+ * Aviso do segredo descartado no `--env-file` de um deploy VM.
248
+ *
249
+ * bug_8b85353b (Parte 3): até aqui a mensagem mandava "usar o Secret Manager e
250
+ * referenciar por nome na VM" — mecanismo que NÃO existia para segredo do
251
+ * próprio produto (era a 4ª cópia do padrão que o core#632 corrigiu nas outras
252
+ * três). O caminho real é `neetru secrets set`, que grava no registro
253
+ * `product_secrets` do Core com o valor direto no Secret Manager. `env` é o
254
+ * `--env` do deploy, para o comando sugerido já sair com o ambiente certo.
255
+ *
256
+ * Revisão do #81 (M1): o comando só é sugerido quando o Core o aceitaria.
257
+ * Chave reservada (o Core já a entrega ao app: credencial SDK/OIDC/CLI,
258
+ * módulo, unit) é recusada em `assertValidProductSecretKey`, e nome fora de
259
+ * CAIXA_ALTA é recusado pela própria CLI (`problemaNaChave`) — sugerir
260
+ * `neetru secrets set` nesses casos mandava o dev para um caminho que falha.
261
+ */
262
+ export declare function avisoSegredoDescartadoNaVm(chave: string, env: string | undefined): string;
263
+ /**
264
+ * O aviso desta chave manda para `neetru secrets set`? Não manda quando o
265
+ * Core reserva a chave — aí o aviso de que o agente ainda não entrega
266
+ * segredos do produto também não cabe.
267
+ */
268
+ export declare function segredoDescartadoApontaSecretsSet(chave: string): boolean;
269
+ export declare function pickTarget(opts: DeployOptions, nonInteractive: boolean, runtime?: string): Promise<'cloud-run' | 'vm' | 'workspace'>;
181
270
  /** Verdicts de uma tentativa de probe. Só `healthy` é terminal-sucesso. */
182
271
  export type HealthProbeVerdict = 'healthy' | 'degraded' | 'warming' | 'unreachable' | 'unverifiable';
183
272
  export interface HealthProbeAttempt {