@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
@@ -28,7 +28,14 @@ import ora from 'ora';
28
28
  import { log } from '../utils/logger.js';
29
29
  import { safeExit } from '../utils/safe-exit.js';
30
30
  import { normalizeTarget } from './env.js';
31
+ import { RUNTIME_ENVIRONMENT_IDS } from '../lib/envs.js';
32
+ import { formatChoices } from '../cli-kit/choices.js';
31
33
  import { registerSpinner, unregisterSpinner } from '../utils/spinner-registry.js';
34
+ import { CodificacaoInvalida, decodificarUtf8, semBomInicial } from '../lib/texto-utf8.js';
35
+ import { variaveisDoEnvFile } from '../lib/env-file-do-build.js';
36
+ // bug_678ddc98: detecção de alvo — diretório do app, gerenciador de pacote e
37
+ // layout do standalone (possivelmente aninhado). Ver `src/lib/build-targets.ts`.
38
+ import { detectPackageManager, comandoInstall, comandoRunScript, comandoExecBin, resolveStandaloneLayout, baseDeInjecaoNoTar, } from '../lib/build-targets.js';
32
39
  const CONFIG_FILENAMES = ['neetru.config.json', '.neetru.json'];
33
40
  function fileExists(p) {
34
41
  try {
@@ -318,17 +325,43 @@ function isStandaloneBuildDir(cwd) {
318
325
  * package.json
319
326
  * node_modules/… (mínimo, tracejado pelo Next.js)
320
327
  * .next/… (server chunks, BUILD_ID, etc.)
321
- * .next/static/… (injetado de <cwd>/.next/static)
322
- * public/… (injetado de <cwd>/public)
328
+ * .next/static/… (injetado de <appDir>/.next/static)
329
+ * public/… (injetado de <appDir>/public)
330
+ *
331
+ * bug_678ddc98 (MONOREPO): o layout acima só vale pra repo de app ÚNICO. Num
332
+ * monorepo o Next.js traceja a partir do `outputFileTracingRoot` e ANINHA o app
333
+ * dentro do standalone, porque o `node_modules` tracejado mora na raiz e o
334
+ * `server.js` precisa enxergá-lo subindo a árvore:
335
+ *
336
+ * raiz-do-tar/
337
+ * node_modules/… (tracejado, compartilhado pelo workspace)
338
+ * packages/ui/… (pacotes internos que o app importa)
339
+ * apps/web/server.js (entrypoint REAL → `node apps/web/server.js`)
340
+ * apps/web/.next/…
341
+ * apps/web/.next/static/… (injetado — tem que ir pro prefixo, não pra raiz)
342
+ * apps/web/public/… (idem)
343
+ *
344
+ * Antes deste fix o `static`/`public` iam pra RAIZ do tar enquanto o server
345
+ * rodava de `apps/web/` — o app subia sem CSS/JS e sem os estáticos. `appPrefix`
346
+ * vem de `resolveStandaloneLayout` (`''` em repo de app único: layout idêntico
347
+ * ao de antes, byte a byte).
323
348
  *
324
349
  * Um único `tar` com múltiplos `-C` — não faz staging-copy (não duplica em
325
350
  * disco). Os `--exclude` (inclui ENV_TAR_EXCLUDES) são globais, valem pros três
326
351
  * `-C`.
327
352
  */
328
- async function tarStandalone(cwd, dest, exclude = [], quiet = false) {
329
- const standaloneDir = path.join(cwd, '.next', 'standalone');
330
- const staticDir = path.join(cwd, '.next', 'static');
331
- const publicDir = path.join(cwd, 'public');
353
+ async function tarStandalone(appDir, dest, exclude = [], quiet = false, appPrefix = '') {
354
+ const standaloneDir = path.join(appDir, '.next', 'standalone');
355
+ const staticDir = path.join(appDir, '.next', 'static');
356
+ const publicDir = path.join(appDir, 'public');
357
+ // bug_678ddc98: pra gravar o membro já aninhado (`apps/web/.next/static`) sem
358
+ // `--transform`/`-s` (não portáveis entre GNU tar e bsdtar), o `-C` recua até
359
+ // a raiz de tracing e o membro leva o prefixo junto. Ver `baseDeInjecaoNoTar`.
360
+ const injecao = baseDeInjecaoNoTar(appDir, appPrefix);
361
+ if (!injecao) {
362
+ throw new Error(`Não foi possível mapear o prefixo "${appPrefix}" do standalone pro disco a partir de ${appDir}. ` +
363
+ 'Isso indica um layout de standalone inesperado — abortando pra não empacotar estáticos no caminho errado.');
364
+ }
332
365
  const isWinGnu = process.platform === 'win32' && (await tarIsGnu());
333
366
  const norm = (p) => (isWinGnu ? normalizePathForTar(p) : p);
334
367
  const args = [];
@@ -340,18 +373,18 @@ async function tarStandalone(cwd, dest, exclude = [], quiet = false) {
340
373
  for (const ex of [...exclude, ...ENV_TAR_EXCLUDES]) {
341
374
  args.push(`--exclude=${ex}`);
342
375
  }
343
- // 1) Todo o conteúdo do standalone → RAIZ do tar.
376
+ // 1) Todo o conteúdo do standalone → RAIZ do tar (com o aninhamento que o
377
+ // Next.js produziu, quando houver).
344
378
  args.push('-C', norm(standaloneDir), '.');
345
- // 2) `.next/static` → `./.next/static` (Next.js não copia sozinho). Usar
346
- // `-C <cwd> .next/static` grava members `.next/static/...` (forward-slash
347
- // aceito por bsdtar e GNU tar), que casam com o `.next` já vindo do
348
- // standalone e mergeiam na extração.
379
+ // 2) `.next/static` → `./<prefixo>.next/static` (Next.js não copia sozinho).
380
+ // Forward-slash no membro é aceito por bsdtar e GNU tar, e casa com o
381
+ // `.next` já vindo do standalone — mergeiam na extração.
349
382
  if (fileExists(staticDir)) {
350
- args.push('-C', norm(cwd), '.next/static');
383
+ args.push('-C', norm(injecao.chdir), `${injecao.prefixo}.next/static`);
351
384
  }
352
- // 3) `public` → `./public` (idem — não vem no standalone).
385
+ // 3) `public` → `./<prefixo>public` (idem — não vem no standalone).
353
386
  if (fileExists(publicDir)) {
354
- args.push('-C', norm(cwd), 'public');
387
+ args.push('-C', norm(injecao.chdir), `${injecao.prefixo}public`);
355
388
  }
356
389
  try {
357
390
  await runCmd('tar', args, process.cwd(), undefined, quiet);
@@ -456,70 +489,73 @@ function parseUnixEnvPrefix(script) {
456
489
  * linha vazia (ignorada)
457
490
  *
458
491
  * Não suporta multi-line values — use aspas com \n.
492
+ *
493
+ * Revisão do cli#81 (N-r3-1): o arquivo é lido em bytes e só segue se for
494
+ * UTF-8 (`decodificarUtf8`). Antes, `readFile(…, 'utf8')` trocava cada byte
495
+ * que não é UTF-8 por U+FFFD sem aviso, e um .env ANSI com `NOME_LOJA=Café`
496
+ * entrava no build como `Caf\uFFFD`. A recusa é `CodificacaoInvalida`, que
497
+ * `runBuild` mostra como `invalid_encoding`.
459
498
  */
460
499
  async function loadEnvFile(envFile) {
461
- let content;
500
+ let bruto;
462
501
  try {
463
- content = await fsp.readFile(envFile, 'utf8');
502
+ bruto = await fsp.readFile(envFile);
464
503
  }
465
504
  catch {
466
505
  throw new Error(`--env-file: arquivo não encontrado: ${envFile}`);
467
506
  }
468
- const vars = {};
469
- for (const line of content.split(/\r?\n/)) {
470
- const trimmed = line.trim();
471
- if (!trimmed || trimmed.startsWith('#'))
472
- continue;
473
- const eq = trimmed.indexOf('=');
474
- if (eq === -1)
475
- continue;
476
- const key = trimmed.slice(0, eq).trim();
477
- let val = trimmed.slice(eq + 1).trim();
478
- // Remove aspas simétricas.
479
- if (val.length >= 2) {
480
- const open = val[0];
481
- const close = val[val.length - 1];
482
- if ((open === '"' && close === '"') || (open === "'" && close === "'")) {
483
- val = val.slice(1, -1);
484
- }
485
- }
486
- if (!key)
487
- continue;
488
- vars[key] = val;
489
- }
490
- return vars;
507
+ return variaveisDoEnvFile(semBomInicial(decodificarUtf8(bruto, 'env-file')));
491
508
  }
492
- async function buildNode(cwd, noCache, noInstall = false, extraEnv, targetEnv = 'prod', quiet = false) {
509
+ async function buildNode(cwd, noCache, noInstall = false, extraEnv, targetEnv = 'prod', quiet = false,
510
+ // bug_678ddc98: o gerenciador de pacote deixa de ser `npm` cravado. Quem
511
+ // chama já detectou (lockfile/campo packageManager) e passa aqui — default
512
+ // mantém o comportamento histórico pra qualquer caller que não passe nada.
513
+ pm = { name: 'npm', rootDir: cwd, source: 'default' }) {
493
514
  const lg = scopedLog(quiet);
494
515
  const pkgPath = path.join(cwd, 'package.json');
495
516
  if (!fileExists(pkgPath))
496
517
  throw new Error('Stack node sem package.json.');
497
518
  const pkg = JSON.parse(await fsp.readFile(pkgPath, 'utf8'));
519
+ const install = comandoInstall(pm.name);
520
+ const runBuildScript = comandoRunScript(pm.name, 'build');
498
521
  // bug_93f984a (MEDIUM): npm ci conflita com `next dev` em Windows (file locks
499
522
  // em node_modules/@next/swc-*). Trocar pra `npm install --no-save --no-audit
500
523
  // --no-fund` que respeita locks parciais. Flag --no-install permite skip
501
524
  // total quando dev sabe que node_modules está OK.
502
525
  if (noInstall) {
503
- lg.dim('npm install pulado (--no-install).');
526
+ lg.dim(`${install.cmd} install pulado (--no-install).`);
504
527
  }
505
528
  else {
506
529
  if (noCache) {
507
530
  // BUG-016 fix (2026-05-13): não apagar package-lock.json do usuário.
508
531
  // Em vez disso, limpamos o cache do npm global se `--no-cache` for exigido.
509
- lg.info('Limpando cache do npm (--no-cache)…');
510
- try {
511
- await runCmd('npm', ['cache', 'clean', '--force'], cwd, undefined, quiet);
532
+ // bug_678ddc98: `cache clean --force` é sintaxe de npm/pnpm; yarn e bun
533
+ // usam outra, então pros dois a limpeza é pulada (nunca é fatal — o
534
+ // --no-cache continua valendo pro caminho docker).
535
+ if (pm.name === 'npm' || pm.name === 'pnpm') {
536
+ lg.info(`Limpando cache do ${pm.name} (--no-cache)…`);
537
+ try {
538
+ await runCmd(pm.name, ['cache', 'clean', '--force'], cwd, undefined, quiet);
539
+ }
540
+ catch {
541
+ // Ignora falha de cache clean e prossegue
542
+ }
512
543
  }
513
- catch {
514
- // Ignora falha de cache clean e prossegue
544
+ else {
545
+ lg.dim(` --no-cache ignorado: ${pm.name} não tem \`cache clean --force\`.`);
515
546
  }
516
547
  }
517
548
  if (process.platform === 'win32') {
518
- lg.warn(`Se houver \`next dev\` ativo na pasta, ele pode conflitar com o npm install (file locks).\n` +
549
+ lg.warn(`Se houver \`next dev\` ativo na pasta, ele pode conflitar com o ${install.cmd} install (file locks).\n` +
519
550
  ` Pause o dev server antes do build, ou use --no-install pra pular esta etapa.`);
520
551
  }
521
- lg.info('npm install (não destructivo, respeita locks)…');
522
- await runCmd('npm', ['install', '--no-save', '--no-audit', '--no-fund'], cwd, undefined, quiet);
552
+ // bug_678ddc98: o install roda na RAIZ do workspace (onde mora o lockfile),
553
+ // não no diretório do app — `pnpm install` dentro de `apps/web` sem o
554
+ // lockfile por perto instala um projeto solto e quebra os links dos pacotes
555
+ // internos. Em repo de app único `pm.rootDir === cwd` e nada muda.
556
+ lg.info(`${install.cmd} ${install.args.join(' ')} (não destrutivo, respeita locks)` +
557
+ `${pm.rootDir === cwd ? '' : ` em ${pm.rootDir}`}…`);
558
+ await runCmd(install.cmd, install.args, pm.rootDir, undefined, quiet);
523
559
  }
524
560
  if (pkg.scripts?.build) {
525
561
  // Fix crítico (2026-07-22, incidente pdv-agiliza): `NEXT_PUBLIC_NEETRU_ENV`
@@ -546,13 +582,17 @@ async function buildNode(cwd, noCache, noInstall = false, extraEnv, targetEnv =
546
582
  lg.info(`Script "build" usa env-prefix Unix. Auto-injetando ${Object.keys(mergedEnv).join('+')} via spawn-env (compat Windows).`);
547
583
  lg.dim(` Original: ${pkg.scripts.build}`);
548
584
  lg.dim(` Executando: ${cmd} (com env: ${JSON.stringify(mergedEnv)})`);
549
- // Roda o comando real com env injetado. Usa npx pra resolver bins de
585
+ // Roda o comando real com env injetado, resolvendo bins de
550
586
  // node_modules/.bin (next, vite, tsc, etc).
551
587
  const parts = cmd.split(/\s+/);
552
588
  const bin = parts[0];
553
589
  const args = parts.slice(1);
554
- // npx <bin> respeita node_modules/.bin; se nao tem bin local, fallback ao PATH.
555
- await runCmd('npx', [bin, ...args], cwd, mergedEnv, quiet);
590
+ // bug_678ddc98: `npx` era cravado aqui. Com pnpm isso é um tiro no pé —
591
+ // os bins ficam atrás do store de links e o npx acabaria BAIXANDO o
592
+ // pacote do registry em vez de usar o do workspace. `comandoExecBin`
593
+ // devolve `pnpm exec`/`yarn exec`/`bunx`/`npx` conforme o gerenciador.
594
+ const exec = comandoExecBin(pm.name, bin, args);
595
+ await runCmd(exec.cmd, exec.args, cwd, mergedEnv, quiet);
556
596
  }
557
597
  else {
558
598
  const mergedEnv = { ...(extraEnv ?? {}), ...forcedEnv };
@@ -562,9 +602,11 @@ async function buildNode(cwd, noCache, noInstall = false, extraEnv, targetEnv =
562
602
  lg.info(`Injetando ${Object.keys(extraEnv).length} var(s) de env-file: ${Object.keys(extraEnv).join(', ')}`);
563
603
  }
564
604
  else {
565
- lg.info('npm run build…');
605
+ lg.info(`${runBuildScript.cmd} ${runBuildScript.args.join(' ')}…`);
566
606
  }
567
- await runCmd('npm', ['run', 'build'], cwd, mergedEnv, quiet);
607
+ // O BUILD roda no diretório do APP (não na raiz do workspace): é lá que
608
+ // mora o script `build` e é lá que o Next.js grava o `.next`.
609
+ await runCmd(runBuildScript.cmd, runBuildScript.args, cwd, mergedEnv, quiet);
568
610
  }
569
611
  }
570
612
  // bug_b8e6e1a2: Next.js `output: 'standalone'` — a verdade pós-build é a pasta
@@ -572,8 +614,25 @@ async function buildNode(cwd, noCache, noInstall = false, extraEnv, targetEnv =
572
614
  // Standalone roda via `node server.js` (com seu node_modules mínimo próprio),
573
615
  // NUNCA `next start` — por isso o entrypoint muda pra `server.js` e o
574
616
  // empacotamento pula o node_modules gigante da raiz.
617
+ const layout = resolveStandaloneLayout(cwd);
575
618
  const standalone = isStandaloneBuildDir(cwd) || detectStandaloneOutput(cwd);
576
- return { entrypoint: standalone ? 'server.js' : (pkg.main ?? 'index.js'), standalone };
619
+ // bug_678ddc98: o entrypoint era a string FIXA `'server.js'`. Num monorepo o
620
+ // Next.js aninha o app dentro do standalone (`apps/web/server.js`) e esse
621
+ // caminho fixo apontava pro vazio. Agora vem do layout real pós-build.
622
+ if (standalone && isStandaloneBuildDir(cwd) && !layout) {
623
+ throw new Error('Existe `.next/standalone` mas nenhum `server.js` foi encontrado dentro dele. ' +
624
+ 'Num monorepo o Next.js aninha o app sob o `outputFileTracingRoot` (ex: `apps/web/server.js`) — ' +
625
+ 'confira se o build rodou no diretório certo (use `--app <dir>`) e se `output: "standalone"` está ativo. ' +
626
+ 'Abortando: um tarball sem entrypoint sobe e nunca inicia.');
627
+ }
628
+ if (layout?.appPrefix) {
629
+ lg.dim(` standalone aninhado: entrypoint = ${layout.entrypoint} (monorepo — bug_678ddc98)`);
630
+ }
631
+ return {
632
+ entrypoint: standalone ? (layout?.entrypoint ?? 'server.js') : (pkg.main ?? 'index.js'),
633
+ standalone,
634
+ appPrefix: layout?.appPrefix ?? '',
635
+ };
577
636
  }
578
637
  /**
579
638
  * `''`/whitespace/`null`/`undefined` num campo do manifesto contam como
@@ -714,9 +773,14 @@ async function buildPhp(cwd, quiet = false) {
714
773
  }
715
774
  return { entrypoint: 'index.php' };
716
775
  }
717
- async function buildStatic(cwd, noInstall = false, targetEnv = 'prod', quiet = false) {
776
+ async function buildStatic(cwd, noInstall = false, targetEnv = 'prod', quiet = false,
777
+ // bug_678ddc98: mesmo tratamento do stack node — um export estático também
778
+ // pode viver num monorepo pnpm/yarn.
779
+ pm = { name: 'npm', rootDir: cwd, source: 'default' }) {
718
780
  const lg = scopedLog(quiet);
719
781
  const pkgPath = path.join(cwd, 'package.json');
782
+ const install = comandoInstall(pm.name);
783
+ const runBuildScript = comandoRunScript(pm.name, 'build');
720
784
  if (fileExists(pkgPath)) {
721
785
  try {
722
786
  const pkg = JSON.parse(await fsp.readFile(pkgPath, 'utf8'));
@@ -726,8 +790,9 @@ async function buildStatic(cwd, noInstall = false, targetEnv = 'prod', quiet = f
726
790
  if (process.platform === 'win32') {
727
791
  lg.warn('Se houver `next dev` ativo, pause antes (file locks).');
728
792
  }
729
- lg.info('npm install (não destructivo)…');
730
- await runCmd('npm', ['install', '--no-save', '--no-audit', '--no-fund'], cwd, undefined, quiet);
793
+ lg.info(`${install.cmd} ${install.args.join(' ')} (não destrutivo)…`);
794
+ // Install na raiz do workspace — ver o porquê em `buildNode`.
795
+ await runCmd(install.cmd, install.args, pm.rootDir, undefined, quiet);
731
796
  }
732
797
  // Fix (2026-07-22, achado da revisão adversarial): `--stack static`
733
798
  // também roda `next build` (output:'export') pra produtos Next.js
@@ -735,8 +800,8 @@ async function buildStatic(cwd, noInstall = false, targetEnv = 'prod', quiet = f
735
800
  // .env.production do stack node, mas sem proteção nenhuma até aqui.
736
801
  // Mesmo forçado via spawn-env que buildNode (ver ali pro porquê).
737
802
  lg.dim(` NEXT_PUBLIC_NEETRU_ENV forçado via spawn-env: ${targetEnv} (incidente pdv-agiliza 2026-07-22)`);
738
- lg.info('npm run build…');
739
- await runCmd('npm', ['run', 'build'], cwd, { NEXT_PUBLIC_NEETRU_ENV: targetEnv }, quiet);
803
+ lg.info(`${runBuildScript.cmd} ${runBuildScript.args.join(' ')}…`);
804
+ await runCmd(runBuildScript.cmd, runBuildScript.args, cwd, { NEXT_PUBLIC_NEETRU_ENV: targetEnv }, quiet);
740
805
  }
741
806
  }
742
807
  catch {
@@ -760,8 +825,34 @@ export async function runBuild(opts = {}) {
760
825
  const startedAt = Date.now();
761
826
  if (!quiet)
762
827
  log.banner();
763
- const cwd = process.cwd();
764
- const localCfg = await loadLocalConfig(cwd);
828
+ // bug_678ddc98: duas noções de diretório, antes confundidas num `cwd` só.
829
+ //
830
+ // `invocacaoDir` = onde o dev rodou o comando. Manda no ARTEFATO: é onde
831
+ // sai o `.neetru-build/`, e é contra ele que os caminhos do manifest são
832
+ // relativizados. Precisa continuar assim porque o `neetru deploy` procura
833
+ // `<cwd>/.neetru-build/manifest.json` — se o artefato fosse pro diretório
834
+ // do app, um `neetru build --app apps/web && neetru deploy` da raiz não
835
+ // acharia nada.
836
+ // `cwd` (= diretório do APP) = onde mora o package.json a construir. Manda
837
+ // no BUILD: stack, versão, script `build`, `.next`, standalone, guard.
838
+ //
839
+ // Sem `--app` os dois são o mesmo diretório — comportamento idêntico ao de antes.
840
+ const invocacaoDir = process.cwd();
841
+ const cwd = path.resolve(invocacaoDir, opts.app ?? '.');
842
+ if (opts.app) {
843
+ if (!fileExists(cwd)) {
844
+ const msg = `--app "${opts.app}" não existe (resolvido para ${cwd}).`;
845
+ if (machineOutput)
846
+ emitBuildJsonError(msg, 'app_dir_not_found');
847
+ else
848
+ log.error(msg);
849
+ safeExit(1);
850
+ }
851
+ lg.dim(` app=${path.relative(invocacaoDir, cwd) || '.'} (monorepo — bug_678ddc98)`);
852
+ }
853
+ // O `neetru.config.json` pode morar no app (monorepo) ou na raiz do repo —
854
+ // procura nos dois, app primeiro (mais específico vence).
855
+ const localCfg = (await loadLocalConfig(cwd)) ?? (cwd === invocacaoDir ? null : await loadLocalConfig(invocacaoDir));
765
856
  const slug = opts.product ?? localCfg?.slug;
766
857
  if (!slug) {
767
858
  const msg = '--product <slug> é obrigatório (ou rode em diretório com neetru.config.json).';
@@ -795,7 +886,7 @@ export async function runBuild(opts = {}) {
795
886
  if (opts.targetEnv) {
796
887
  const normalized = normalizeTarget(opts.targetEnv);
797
888
  if (!normalized) {
798
- const msg = `--target-env "${opts.targetEnv}" inválido. Use: dev | workspace | prod.`;
889
+ const msg = `--target-env "${opts.targetEnv}" inválido. Use: ${formatChoices(RUNTIME_ENVIRONMENT_IDS)}.`;
799
890
  if (machineOutput)
800
891
  emitBuildJsonError(msg, 'invalid_target_env');
801
892
  else
@@ -808,27 +899,52 @@ export async function runBuild(opts = {}) {
808
899
  // via spawn-env apenas durante o build — não vão pro Core nem ao manifest.
809
900
  let buildEnvVars;
810
901
  if (opts.envFile) {
811
- const envFilePath = path.resolve(cwd, opts.envFile);
812
- buildEnvVars = await loadEnvFile(envFilePath);
902
+ // Resolvido contra o diretório de INVOCAÇÃO: o dev digitou o caminho a
903
+ // partir de onde ele está, não a partir do diretório do app.
904
+ const envFilePath = path.resolve(invocacaoDir, opts.envFile);
905
+ try {
906
+ buildEnvVars = await loadEnvFile(envFilePath);
907
+ }
908
+ catch (err) {
909
+ if (!(err instanceof CodificacaoInvalida))
910
+ throw err;
911
+ if (machineOutput)
912
+ emitBuildJsonError(err.message, 'invalid_encoding');
913
+ else
914
+ log.error(err.message);
915
+ safeExit(1);
916
+ }
813
917
  lg.info(`Env-file carregado: ${opts.envFile} (${Object.keys(buildEnvVars).length} vars)`);
814
918
  // Loga os nomes (nunca os valores) pra rastreabilidade sem vazar secrets.
815
919
  lg.dim(` vars: ${Object.keys(buildEnvVars).join(', ')}`);
816
920
  }
817
921
  lg.heading(`neetru build`);
818
922
  lg.dim(` slug=${slug} version=${version} stack=${stack}${rawStack === 'nextjs' ? ' (nextjs→node)' : ''}`);
819
- const outputDir = path.resolve(cwd, opts.output ?? '.neetru-build');
923
+ // Artefato sai no diretório de INVOCAÇÃO (ver o bloco de `invocacaoDir`) —
924
+ // é o que mantém `neetru build --app apps/web && neetru deploy` funcionando
925
+ // a partir da raiz do monorepo.
926
+ const outputDir = path.resolve(invocacaoDir, opts.output ?? '.neetru-build');
820
927
  await ensureDir(outputDir);
821
928
  const tarballName = `${slug}-${version}.tar.gz`;
822
929
  let tarballPath = path.join(outputDir, tarballName);
823
930
  let entrypoint;
931
+ // bug_678ddc98 (elo que faltava): o layout aninhado era calculado, usado pro
932
+ // tar e DESCARTADO. O deploy precisa dele pra decidir se manda o entrypoint.
933
+ let appPrefix = '';
824
934
  const spinner = registerSpinner(ora({ text: `Construindo ${stack}…`, color: 'blue' }).start());
825
935
  try {
826
936
  if (stack === 'node') {
827
937
  // bug_25ff8581: passa extraEnv do --env-file para o buildNode.
828
938
  // Incidente 2026-07-22: passa targetEnv — força NEXT_PUBLIC_NEETRU_ENV
829
939
  // via spawn-env, neutralizando .env.local vencendo .env.production.
830
- const r = await buildNode(cwd, !!opts.noCache, !!opts.noInstall, buildEnvVars, targetEnv, quiet);
940
+ // bug_678ddc98: o gerenciador vem do lockfile/campo `packageManager`
941
+ // (npm/pnpm/yarn/bun), não mais cravado em `npm`.
942
+ const pm = detectPackageManager(cwd);
943
+ lg.dim(` gerenciador=${pm.name} (${pm.source}${pm.lockfile ? `: ${pm.lockfile}` : ''})` +
944
+ `${pm.rootDir === cwd ? '' : ` raiz-do-workspace=${path.relative(invocacaoDir, pm.rootDir) || '.'}`}`);
945
+ const r = await buildNode(cwd, !!opts.noCache, !!opts.noInstall, buildEnvVars, targetEnv, quiet, pm);
831
946
  entrypoint = r.entrypoint;
947
+ appPrefix = r.appPrefix;
832
948
  // Incidente 2026-07-22: confirma que o bundle compilado realmente
833
949
  // refletiu o targetEnv (defesa contra scripts de build customizados
834
950
  // que ignorem o spawn-env acima) ANTES de empacotar o tarball.
@@ -840,7 +956,10 @@ export async function runBuild(opts = {}) {
840
956
  // agente da VM estourava o timeout de 120s de extração, bug_e3202fcc, e
841
957
  // bloqueava o deploy). O standalone já traz seu node_modules mínimo.
842
958
  lg.info('Next.js standalone detectado — empacotando output tracejado (sem node_modules da raiz).');
843
- await tarStandalone(cwd, tarballPath, ['*.log'], quiet);
959
+ // bug_678ddc98: `appPrefix` leva `.next/static` e `public` pro lugar
960
+ // onde o `server.js` aninhado vai procurá-los. `''` = layout de app
961
+ // único, idêntico ao anterior.
962
+ await tarStandalone(cwd, tarballPath, ['*.log'], quiet, r.appPrefix);
844
963
  }
845
964
  else {
846
965
  // Não-standalone: o servidor (`next start`/`node`) precisa do
@@ -869,7 +988,7 @@ export async function runBuild(opts = {}) {
869
988
  else if (stack === 'static') {
870
989
  // Incidente 2026-07-22: --stack static também roda `next build`
871
990
  // (output:'export') pra produtos Next.js — mesma proteção do stack node.
872
- const r = await buildStatic(cwd, !!opts.noInstall, targetEnv, quiet);
991
+ const r = await buildStatic(cwd, !!opts.noInstall, targetEnv, quiet, detectPackageManager(cwd));
873
992
  entrypoint = r.entrypoint;
874
993
  await validateBuildEnvManifest(cwd, targetEnv, quiet);
875
994
  await tarDirectory(r.root, tarballPath, ['.git', '.neetru-build'], quiet);
@@ -877,7 +996,7 @@ export async function runBuild(opts = {}) {
877
996
  else {
878
997
  throw new Error(`Stack desconhecida: ${stack}`);
879
998
  }
880
- spinner.succeed(`Empacotado: ${chalk.bold(path.relative(cwd, tarballPath))}`);
999
+ spinner.succeed(`Empacotado: ${chalk.bold(path.relative(invocacaoDir, tarballPath))}`);
881
1000
  unregisterSpinner(spinner);
882
1001
  }
883
1002
  catch (err) {
@@ -913,17 +1032,20 @@ export async function runBuild(opts = {}) {
913
1032
  version,
914
1033
  // Gravamos o stack canônico (nextjs já foi normalizado acima).
915
1034
  stack,
916
- tarball: path.relative(cwd, tarballPath),
1035
+ tarball: path.relative(invocacaoDir, tarballPath),
917
1036
  sha256,
918
1037
  sizeBytes,
919
1038
  entrypoint,
1039
+ // Só grava quando há aninhamento — manifest de app único fica idêntico ao
1040
+ // de antes (nenhuma chave nova), então nada a montante muda de forma.
1041
+ ...(appPrefix ? { appPrefix } : {}),
920
1042
  builtAt: new Date().toISOString(),
921
1043
  };
922
1044
  const manifestPath = path.join(outputDir, 'manifest.json');
923
1045
  await fsp.writeFile(manifestPath, JSON.stringify(manifest, null, 2), 'utf8');
924
1046
  lg.dim(` sha256=${sha256.slice(0, 16)}…`);
925
1047
  lg.dim(` size=${(sizeBytes / 1024 / 1024).toFixed(2)} MB`);
926
- lg.dim(` manifest=${path.relative(cwd, manifestPath)}`);
1048
+ lg.dim(` manifest=${path.relative(invocacaoDir, manifestPath)}`);
927
1049
  lg.success(`Build pronto. Use: ${chalk.bold('neetru deploy --product=' + slug)}`);
928
1050
  // SETOR 5.1: em --json, imprime UMA linha JSON estruturada no stdout no fim.
929
1051
  // Não emite quando chamado internamente pelo deploy (`quiet` sem `json`) — o
@@ -942,7 +1064,7 @@ export async function runBuild(opts = {}) {
942
1064
  sizeBytes,
943
1065
  entrypoint: entrypoint ?? null,
944
1066
  builtAt: manifest.builtAt,
945
- manifestPath: path.relative(cwd, manifestPath),
1067
+ manifestPath: path.relative(invocacaoDir, manifestPath),
946
1068
  durationMs: Date.now() - startedAt,
947
1069
  }));
948
1070
  }
@@ -1,4 +1,5 @@
1
1
  import type { Command } from 'commander';
2
+ import type { NeetruCliError } from '../cli-kit/errors.js';
2
3
  type ActorType = 'agent_claude' | 'human' | 'cli';
3
4
  type ChatChannelType = 'global' | 'product' | 'custom';
4
5
  export interface ChatChannelsOptions {
@@ -44,6 +45,82 @@ export interface ChatIdentityOptions {
44
45
  /** Suprime o log de sucesso (usado pelo --as do send). */
45
46
  quiet?: boolean;
46
47
  }
48
+ /**
49
+ * O que `neetru chat identity <args...>` pede.
50
+ *
51
+ * bug_09574d4b (2026-10-12): o comando só aceitava o apelido POSICIONAL, e não
52
+ * havia subcomando — mas a skill da casa (`neetru-chat`) documenta
53
+ * `identity set "Nome"` e `identity show`. Resultado: `identity show` GRAVAVA o
54
+ * apelido "show", e `identity set "PDV · IA"` gravava "set" e descartava o
55
+ * nome. Sem aviso: a saída era o mesmo "✓ Apelido definido" do caminho feliz.
56
+ *
57
+ * Agora `show`/`ver` lê, `set`/`definir` grava o resto, `clear`/`limpar` limpa,
58
+ * e o posicional antigo (`identity "PDV · IA"`) segue funcionando. O preço: não
59
+ * dá pra ter um apelido que seja SÓ uma dessas palavras.
60
+ *
61
+ * P105 (2026-10-18): o conserto acima cobria só as palavras que ele conhecia.
62
+ * `identity describe` gravava "describe", `identity list` gravava "list",
63
+ * `identity show Fulano` gravava "show Fulano" e `identity set clear` gravava
64
+ * "clear". Agora nenhum verbo de `identity` (os de `LER`/`DEFINIR`/`LIMPAR`)
65
+ * nem sinônimo de um deles (`SYNONYMS`, cli-kit) vira apelido: a CLI recusa e
66
+ * sugere o verbo certo, com o piso de qualidade do gcloud (`MIN_RATIO`) — sem
67
+ * sugestão que passe o piso, lista os três. Um apelido que só COMEÇA com um
68
+ * verbo continua possível pela forma explícita (`identity set "Show de Bola"`);
69
+ * um apelido que É um verbo (`Set`, `Ver`) só pelo `--as` do `send`, e a
70
+ * recusa ensina esse caminho.
71
+ *
72
+ * Os caminhos citados (`neetru chat identity …`, `neetru chat send --as`) saem
73
+ * da árvore do Commander (`contextoDeIdentidadeDaArvore`), não de literal
74
+ * (revisão do #87, BAIXO 5).
75
+ */
76
+ export type PedidoDeIdentidade = {
77
+ acao: 'mostrar';
78
+ } | {
79
+ acao: 'definir';
80
+ apelido: string;
81
+ } | {
82
+ acao: 'erro';
83
+ mensagem: string;
84
+ erro?: NeetruCliError;
85
+ };
86
+ /** Caminho do comando na árvore, sem o `neetru` (ex.: `'chat channels create'`). */
87
+ export declare function caminhoDoComando(cmd: Command | null | undefined): string;
88
+ /** Onde `identity` mora na árvore e por onde se grava um apelido que é verbo. */
89
+ export interface ContextoDeIdentidade {
90
+ /** Caminho do `identity`, sem o `neetru`. */
91
+ comando: string;
92
+ /** Caminho do `send` que declara `--as`, ou `null` se a árvore não tem esse caminho. */
93
+ definirViaSend: string | null;
94
+ }
95
+ /** O que a árvore de verdade dá hoje — travado por teste contra `registerChatCommand`. */
96
+ export declare const CONTEXTO_DE_IDENTIDADE_PADRAO: ContextoDeIdentidade;
97
+ /** Deriva o contexto do nó `identity` do Commander (o que o `.action()` recebe). */
98
+ export declare function contextoDeIdentidadeDaArvore(identity: Command): ContextoDeIdentidade;
99
+ export declare function interpretarArgsDeIdentidade(args: readonly string[], ctx?: ContextoDeIdentidade): PedidoDeIdentidade;
100
+ /**
101
+ * Texto livre POSICIONAL de uma palavra só que é SUBCOMANDO irmão (nome,
102
+ * apelido ou sinônimo): recusa (P105). `neetru chat send history` postava
103
+ * "history" no canal global achando que listava; `neetru chat channels create
104
+ * close` criava o canal "close". Os subcomandos e o caminho citados saem do
105
+ * nó do Commander em execução (`command`), não de literal.
106
+ *
107
+ * `null` quando o texto pode seguir: resposta comum (`show`, `ok`, `top`,
108
+ * `beleza`, `valeu`, `sim`, `blz` — revisão do #87, BAIXO 6) e frase que só
109
+ * começa com uma palavra de comando ("history do dia").
110
+ */
111
+ export declare function verboComoTextoPosicional(input: {
112
+ texto: string;
113
+ command: Command;
114
+ oQue: string;
115
+ escapeHint?: string;
116
+ }): NeetruCliError | null;
117
+ interface ChatIdentityResponse {
118
+ ok: true;
119
+ /** Apelido atual (string vazia = nenhum). */
120
+ displayName: string;
121
+ /** Email da credencial — só no GET. */
122
+ email?: string;
123
+ }
47
124
  /**
48
125
  * `neetru chat identity [apelido]` — apelido inerente à credencial (CH3).
49
126
  *
@@ -55,7 +132,23 @@ export interface ChatIdentityOptions {
55
132
  * O apelido vive em `cli_api_keys/{keyId}.displayName` — é inerente ao token,
56
133
  * não por-mensagem.
57
134
  */
58
- export declare function runChatIdentity(displayName: string | undefined, opts?: ChatIdentityOptions): Promise<void>;
135
+ export declare function runChatIdentity(displayName: string | undefined, opts?: ChatIdentityOptions): Promise<ChatIdentityResponse | undefined>;
136
+ /**
137
+ * `chat send --as <apelido>`.
138
+ *
139
+ * O Core NÃO tem apelido por mensagem — o nome vem da credencial
140
+ * (`cli_api_keys/{keyId}.displayName`). Então `--as` só pode PERSISTIR. O defeito
141
+ * (bug_09574d4b) não era persistir: era persistir CALADO — e o help da versão
142
+ * publicada dizia só "antes de enviar", então quem lia concluía, com razão, que
143
+ * valia para aquela mensagem. Credencial compartilhada entre sessões fazia uma
144
+ * renomear a outra sem ninguém ver.
145
+ *
146
+ * Agora: se o apelido já é o pedido, nada muda (nem chamada de escrita); se muda,
147
+ * o aviso vai pro STDERR dizendo de quem para quem e que vale para as próximas
148
+ * mensagens da credencial. STDERR porque em `--json` o stdout é contrato de uma
149
+ * linha.
150
+ */
151
+ export declare function aplicarApelidoDoSend(apelidoBruto: string, json: boolean): Promise<void>;
59
152
  export interface ChatHistoryOptions {
60
153
  /** Canal de destino (novo — tem precedência sobre product). */
61
154
  channel?: string;