@neetru/cli 2.31.0 → 2.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +91 -2
- package/README.md +2 -0
- package/dist/cli-kit/choices.d.ts +33 -0
- package/dist/cli-kit/choices.js +32 -0
- package/dist/cli-kit/command-context.d.ts +98 -0
- package/dist/cli-kit/command-context.js +108 -0
- package/dist/cli-kit/errors.d.ts +79 -0
- package/dist/cli-kit/errors.js +94 -0
- package/dist/cli-kit/index.d.ts +8 -0
- package/dist/cli-kit/index.js +8 -0
- package/dist/cli-kit/suggest.d.ts +74 -0
- package/dist/cli-kit/suggest.js +207 -0
- package/dist/commands/admin/oidc-login.js +1 -1
- package/dist/commands/admin/sdk-credential.js +1 -1
- package/dist/commands/autocomplete.d.ts +1 -1
- package/dist/commands/autocomplete.js +1 -0
- package/dist/commands/bug.d.ts +59 -0
- package/dist/commands/bug.js +114 -2
- package/dist/commands/build.d.ts +24 -1
- package/dist/commands/build.js +195 -73
- package/dist/commands/chat.d.ts +94 -1
- package/dist/commands/chat.js +258 -26
- package/dist/commands/customers.js +1 -1
- package/dist/commands/db-move.js +3 -3
- package/dist/commands/db.js +17 -24
- package/dist/commands/deploy.d.ts +91 -2
- package/dist/commands/deploy.js +414 -69
- package/dist/commands/deployments.js +11 -6
- package/dist/commands/dev-context.js +15 -5
- package/dist/commands/docs-produto.d.ts +25 -0
- package/dist/commands/docs-produto.js +70 -8
- package/dist/commands/doctor.js +36 -1
- package/dist/commands/domains.js +85 -4
- package/dist/commands/env.d.ts +9 -3
- package/dist/commands/env.js +23 -3
- package/dist/commands/infra-read.js +2 -2
- package/dist/commands/init.js +7 -1
- package/dist/commands/login.js +35 -8
- package/dist/commands/logs.js +12 -4
- package/dist/commands/mobile.js +33 -9
- package/dist/commands/new.d.ts +29 -1
- package/dist/commands/new.js +92 -4
- package/dist/commands/open.js +16 -6
- package/dist/commands/ops.js +1 -1
- package/dist/commands/products-db.d.ts +1 -0
- package/dist/commands/products-db.js +41 -8
- package/dist/commands/products.d.ts +17 -3
- package/dist/commands/products.js +84 -16
- package/dist/commands/promote.js +30 -5
- package/dist/commands/secrets.d.ts +91 -0
- package/dist/commands/secrets.js +471 -0
- package/dist/commands/servers.js +6 -4
- package/dist/commands/status.js +2 -2
- package/dist/commands/support.js +5 -3
- package/dist/commands/tenants.js +28 -16
- package/dist/commands/upgrade.d.ts +9 -0
- package/dist/commands/upgrade.js +50 -1
- package/dist/commands/vm-memory.js +1 -0
- package/dist/commands/webhooks.d.ts +2 -0
- package/dist/commands/webhooks.js +9 -3
- package/dist/commands/workspaces.js +42 -11
- package/dist/index.js +70 -14
- package/dist/lib/api-client.d.ts +14 -0
- package/dist/lib/api-client.js +59 -6
- package/dist/lib/api-error.d.ts +14 -0
- package/dist/lib/api-error.js +107 -46
- package/dist/lib/build-targets.d.ts +127 -0
- package/dist/lib/build-targets.js +348 -0
- package/dist/lib/cli-error.d.ts +13 -0
- package/dist/lib/cli-error.js +24 -0
- package/dist/lib/cli-write.d.ts +43 -9
- package/dist/lib/cli-write.js +154 -45
- package/dist/lib/config-schema.d.ts +8 -8
- package/dist/lib/db-local/db-json.d.ts +1 -1
- package/dist/lib/db-local/db-json.js +13 -5
- package/dist/lib/db-local/env.d.ts +15 -3
- package/dist/lib/db-local/env.js +37 -10
- package/dist/lib/db-pipeline/build-deps.d.ts +5 -1
- package/dist/lib/db-pipeline/build-deps.js +23 -3
- package/dist/lib/db-pipeline/types.d.ts +6 -2
- package/dist/lib/db-pipeline/types.js +0 -18
- package/dist/lib/deploy-target-contract.d.ts +152 -0
- package/dist/lib/deploy-target-contract.js +411 -0
- package/dist/lib/env-file-do-build.d.ts +19 -0
- package/dist/lib/env-file-do-build.js +44 -0
- package/dist/lib/envs.d.ts +108 -0
- package/dist/lib/envs.js +127 -0
- package/dist/lib/fs-seguro.d.ts +63 -0
- package/dist/lib/fs-seguro.js +108 -0
- package/dist/lib/mobile-local/mobile-json.js +10 -4
- package/dist/lib/pickers.d.ts +44 -7
- package/dist/lib/pickers.js +60 -4
- package/dist/lib/product-docs/sync.d.ts +6 -0
- package/dist/lib/product-docs/sync.js +10 -1
- package/dist/lib/public-secret-scan.d.ts +44 -3
- package/dist/lib/public-secret-scan.js +361 -36
- package/dist/lib/render.d.ts +16 -0
- package/dist/lib/render.js +34 -4
- package/dist/lib/segredo-produto.d.ts +124 -0
- package/dist/lib/segredo-produto.js +336 -0
- package/dist/lib/texto-utf8.d.ts +96 -0
- package/dist/lib/texto-utf8.js +196 -0
- package/package.json +2 -1
- package/dist/cli-kit/format.js.map +0 -1
- package/dist/cli-kit/glyphs.js.map +0 -1
- package/dist/cli-kit/index.js.map +0 -1
- package/dist/cli-kit/palette.js.map +0 -1
- package/dist/commands/add.js.map +0 -1
- package/dist/commands/admin/iam-check.js.map +0 -1
- package/dist/commands/admin/oidc-login.js.map +0 -1
- package/dist/commands/admin/sdk-credential.js.map +0 -1
- package/dist/commands/agent-release.js.map +0 -1
- package/dist/commands/agent-write.js.map +0 -1
- package/dist/commands/ai-models.js.map +0 -1
- package/dist/commands/ai.js.map +0 -1
- package/dist/commands/api-catalog.js.map +0 -1
- package/dist/commands/archive.js.map +0 -1
- package/dist/commands/artifact-registry.js.map +0 -1
- package/dist/commands/audit.js.map +0 -1
- package/dist/commands/auth.js.map +0 -1
- package/dist/commands/autocomplete.js.map +0 -1
- package/dist/commands/billing.js.map +0 -1
- package/dist/commands/bootstrap.js.map +0 -1
- package/dist/commands/bug.js.map +0 -1
- package/dist/commands/build.js.map +0 -1
- package/dist/commands/chat-menu.js.map +0 -1
- package/dist/commands/chat.js.map +0 -1
- package/dist/commands/cloud-run.js.map +0 -1
- package/dist/commands/code.js.map +0 -1
- package/dist/commands/config.js.map +0 -1
- package/dist/commands/customers.js.map +0 -1
- package/dist/commands/db-move.js.map +0 -1
- package/dist/commands/db.js.map +0 -1
- package/dist/commands/deploy.js.map +0 -1
- package/dist/commands/deployments.js.map +0 -1
- package/dist/commands/dev-context.js.map +0 -1
- package/dist/commands/dev-up.js.map +0 -1
- package/dist/commands/dev.js.map +0 -1
- package/dist/commands/docs-produto.js.map +0 -1
- package/dist/commands/docs.js.map +0 -1
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/domains.js.map +0 -1
- package/dist/commands/dr.js.map +0 -1
- package/dist/commands/env.js.map +0 -1
- package/dist/commands/hosting-write.js.map +0 -1
- package/dist/commands/infra-read.js.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/login.js.map +0 -1
- package/dist/commands/logout.js.map +0 -1
- package/dist/commands/logs.js.map +0 -1
- package/dist/commands/mobile.js.map +0 -1
- package/dist/commands/mocks.js.map +0 -1
- package/dist/commands/new.js.map +0 -1
- package/dist/commands/open.js.map +0 -1
- package/dist/commands/ops.js.map +0 -1
- package/dist/commands/products-db.js.map +0 -1
- package/dist/commands/products.js.map +0 -1
- package/dist/commands/promote.js.map +0 -1
- package/dist/commands/publish.js.map +0 -1
- package/dist/commands/schema.js.map +0 -1
- package/dist/commands/servers.js.map +0 -1
- package/dist/commands/smoke.js.map +0 -1
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/support.js.map +0 -1
- package/dist/commands/surface-status.js.map +0 -1
- package/dist/commands/tenants.js.map +0 -1
- package/dist/commands/ui.js.map +0 -1
- package/dist/commands/upgrade.js.map +0 -1
- package/dist/commands/validate.js.map +0 -1
- package/dist/commands/vm-memory.js.map +0 -1
- package/dist/commands/vm.js.map +0 -1
- package/dist/commands/webhooks.js.map +0 -1
- package/dist/commands/whoami.js.map +0 -1
- package/dist/commands/workspaces.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/agent-artifact-url.js.map +0 -1
- package/dist/lib/agent-commands.js.map +0 -1
- package/dist/lib/ai/auth-discovery.js.map +0 -1
- package/dist/lib/ai/context.js.map +0 -1
- package/dist/lib/ai/orchestrator.js.map +0 -1
- package/dist/lib/api-client.js.map +0 -1
- package/dist/lib/api-error.js.map +0 -1
- package/dist/lib/auth.js.map +0 -1
- package/dist/lib/cli-read.js.map +0 -1
- package/dist/lib/cli-write.js.map +0 -1
- package/dist/lib/code-agent-budget.js.map +0 -1
- package/dist/lib/code-agent-files.js.map +0 -1
- package/dist/lib/code-agent-gateway.js.map +0 -1
- package/dist/lib/code-agent-git-tools.js.map +0 -1
- package/dist/lib/code-agent-protocol.js.map +0 -1
- package/dist/lib/code-agent-session.js.map +0 -1
- package/dist/lib/code-agent-settings.js.map +0 -1
- package/dist/lib/code-agent-skills.js.map +0 -1
- package/dist/lib/code-agent-subagents.js.map +0 -1
- package/dist/lib/code-agent-tools.js.map +0 -1
- package/dist/lib/code-agent-tui.js.map +0 -1
- package/dist/lib/code-agent-types.js.map +0 -1
- package/dist/lib/code-agent-ui.js.map +0 -1
- package/dist/lib/code-agent-web-tools.js.map +0 -1
- package/dist/lib/code-agent-write-tools.js.map +0 -1
- package/dist/lib/compose-generator.js.map +0 -1
- package/dist/lib/config-schema.js.map +0 -1
- package/dist/lib/config.js.map +0 -1
- package/dist/lib/db-local/db-json.js.map +0 -1
- package/dist/lib/db-local/env.js.map +0 -1
- package/dist/lib/db-local/fingerprint.js.map +0 -1
- package/dist/lib/db-local/index.js.map +0 -1
- package/dist/lib/db-pipeline/build-deps.js.map +0 -1
- package/dist/lib/db-pipeline/errors.js.map +0 -1
- package/dist/lib/db-pipeline/index.js.map +0 -1
- package/dist/lib/db-pipeline/pipeline.js.map +0 -1
- package/dist/lib/db-pipeline/rehearse.js.map +0 -1
- package/dist/lib/db-pipeline/types.js.map +0 -1
- package/dist/lib/dev-local-context.js.map +0 -1
- package/dist/lib/iam-preflight.js.map +0 -1
- package/dist/lib/mobile-local/index.js.map +0 -1
- package/dist/lib/mobile-local/mobile-json.js.map +0 -1
- package/dist/lib/pickers.js.map +0 -1
- package/dist/lib/product-config.js.map +0 -1
- package/dist/lib/product-docs/check.js.map +0 -1
- package/dist/lib/product-docs/config.js.map +0 -1
- package/dist/lib/product-docs/dates.js.map +0 -1
- package/dist/lib/product-docs/frontmatter.js.map +0 -1
- package/dist/lib/product-docs/git.js.map +0 -1
- package/dist/lib/product-docs/markdown.js.map +0 -1
- package/dist/lib/product-docs/ops.js.map +0 -1
- package/dist/lib/product-docs/structure.js.map +0 -1
- package/dist/lib/product-docs/sync.js.map +0 -1
- package/dist/lib/product-docs/taxonomy.js.map +0 -1
- package/dist/lib/product-docs/templates.js.map +0 -1
- package/dist/lib/public-secret-scan.js.map +0 -1
- package/dist/lib/render.js.map +0 -1
- package/dist/lib/schema-cache.js.map +0 -1
- package/dist/lib/sse.js.map +0 -1
- package/dist/lib/telemetry.js.map +0 -1
- package/dist/lib/token-resolver.js.map +0 -1
- package/dist/lib/url-allowlist.js.map +0 -1
- package/dist/lib/version-check.js.map +0 -1
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/logo.js.map +0 -1
- package/dist/utils/safe-exit.js.map +0 -1
- package/dist/utils/spinner-registry.js.map +0 -1
- package/dist/version.js.map +0 -1
package/dist/commands/chat.js
CHANGED
|
@@ -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
|
|
254
|
-
|
|
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
|
|
282
|
-
|
|
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 (
|
|
792
|
+
.action(async (_cmdOpts, command) => {
|
|
606
793
|
// Sem subcomando → lista os canais abertos.
|
|
607
|
-
await runChatChannels({ 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:
|
|
810
|
+
product: opcaoHerdada(command, 'product'),
|
|
618
811
|
type: cmdOpts.type,
|
|
619
|
-
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,
|
|
627
|
-
await runChatChannelClose(id, { 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 [
|
|
825
|
+
.command('identity [args...]')
|
|
633
826
|
.alias('apelido')
|
|
634
|
-
.description('
|
|
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 (
|
|
637
|
-
|
|
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>', '
|
|
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
|
-
|
|
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
|
|
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
|
|
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,
|
|
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 },
|
package/dist/commands/db-move.js
CHANGED
|
@@ -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:
|
|
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,
|
|
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 ?? '—',
|
|
1011
|
+
{ header: 'VM (onde vive)', value: (r) => r.serverId ?? '—', identificador: true },
|
|
1012
1012
|
], dbs);
|
|
1013
1013
|
}
|
|
1014
1014
|
//# sourceMappingURL=db-move.js.map
|
package/dist/commands/db.js
CHANGED
|
@@ -30,6 +30,10 @@ 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';
|
|
@@ -159,8 +163,8 @@ export async function runDbList(opts = {}) {
|
|
|
159
163
|
}
|
|
160
164
|
log.heading(`Bancos (${resp.count})`);
|
|
161
165
|
renderTable([
|
|
162
|
-
{ header: 'ID', value: (r) => r.id,
|
|
163
|
-
{ header: 'Produto', value: (r) => r.productId,
|
|
166
|
+
{ header: 'ID', value: (r) => r.id, identificador: true },
|
|
167
|
+
{ header: 'Produto', value: (r) => r.productId, identificador: true },
|
|
164
168
|
{ header: 'Label', value: (r) => r.label ?? '—', maxWidth: 24 },
|
|
165
169
|
{ header: 'Engine', value: (r) => r.engine, maxWidth: 22 },
|
|
166
170
|
// gestovendas #11 fix: Core retorna `environment`; `env` é alias legado.
|
|
@@ -428,8 +432,9 @@ export async function runDbInit(opts = {}) {
|
|
|
428
432
|
env = opts.env ? normalizeEnv(opts.env) : 'dev-local';
|
|
429
433
|
}
|
|
430
434
|
catch {
|
|
431
|
-
|
|
432
|
-
|
|
435
|
+
// bug_89d2497e: valores válidos da fonte real (`NEETRU_ENVS`); com --json,
|
|
436
|
+
// sai o JSON `invalid_env` como em `db apply`/`db exec-sql`.
|
|
437
|
+
exitWithCliError(erroAmbienteBancoInvalido(opts.env), opts.json);
|
|
433
438
|
return;
|
|
434
439
|
}
|
|
435
440
|
// ── bug_062f7fea (pdv-agiliza): engines vm-* (+ nosql-vm) exigem serverId ──
|
|
@@ -753,14 +758,8 @@ export async function runDbApply(opts = {}) {
|
|
|
753
758
|
env = normalizeEnv(opts.env ?? 'dev-local');
|
|
754
759
|
}
|
|
755
760
|
catch {
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
jsonError(msg, 'invalid_env');
|
|
759
|
-
}
|
|
760
|
-
else {
|
|
761
|
-
log.error(msg);
|
|
762
|
-
}
|
|
763
|
-
safeExit(1);
|
|
761
|
+
// bug_89d2497e: valores válidos da fonte real (`NEETRU_ENVS`), não copiados.
|
|
762
|
+
exitWithCliError(erroAmbienteBancoInvalido(opts.env), opts.json);
|
|
764
763
|
return;
|
|
765
764
|
}
|
|
766
765
|
// Resolve schema path.
|
|
@@ -1103,14 +1102,8 @@ export async function runDbExecSql(opts = {}) {
|
|
|
1103
1102
|
env = normalizeEnv(opts.env ?? 'dev-local');
|
|
1104
1103
|
}
|
|
1105
1104
|
catch {
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
jsonError(msg, 'invalid_env');
|
|
1109
|
-
}
|
|
1110
|
-
else {
|
|
1111
|
-
log.error(msg);
|
|
1112
|
-
}
|
|
1113
|
-
safeExit(1);
|
|
1105
|
+
// bug_89d2497e: valores válidos da fonte real (`NEETRU_ENVS`), não copiados.
|
|
1106
|
+
exitWithCliError(erroAmbienteBancoInvalido(opts.env), opts.json);
|
|
1114
1107
|
return;
|
|
1115
1108
|
}
|
|
1116
1109
|
const canonicalEnv = env;
|
|
@@ -1263,8 +1256,8 @@ export async function runDbMigrationsList(opts = {}) {
|
|
|
1263
1256
|
}
|
|
1264
1257
|
log.heading(`Migrações (${resp.count})`);
|
|
1265
1258
|
renderTable([
|
|
1266
|
-
{ header: 'ID', value: (r) => r.id,
|
|
1267
|
-
{ header: 'Banco', value: (r) => r.dbId ?? '—',
|
|
1259
|
+
{ header: 'ID', value: (r) => r.id, identificador: true },
|
|
1260
|
+
{ header: 'Banco', value: (r) => r.dbId ?? '—', identificador: true },
|
|
1268
1261
|
{ header: 'Status', value: (r) => r.status, maxWidth: 20 },
|
|
1269
1262
|
{ header: 'Severidade', value: (r) => severityColor(r.severity), maxWidth: 14 },
|
|
1270
1263
|
{ header: 'Aplicado', value: (r) => fmtTimestamp(r.appliedAt ?? r.createdAt), maxWidth: 18 },
|
|
@@ -1361,7 +1354,7 @@ export async function runDbBackups(dbId, opts = {}) {
|
|
|
1361
1354
|
}
|
|
1362
1355
|
log.heading(`Backups do banco ${chalk.bold(dbId)} (${resp.count})`);
|
|
1363
1356
|
renderTable([
|
|
1364
|
-
{ header: 'ID', value: (r) => r.id,
|
|
1357
|
+
{ header: 'ID', value: (r) => r.id, identificador: true },
|
|
1365
1358
|
{ header: 'Status', value: (r) => statusColor(r.status), maxWidth: 16 },
|
|
1366
1359
|
{ header: 'Data/Hora', value: (r) => fmtTimestamp(r.timestamp), maxWidth: 22 },
|
|
1367
1360
|
{ header: 'Tamanho (MB)', value: (r) => r.sizeMb != null ? String(r.sizeMb) : '—', maxWidth: 14 },
|
|
@@ -1551,7 +1544,7 @@ export async function runDbHosts(opts = {}) {
|
|
|
1551
1544
|
}
|
|
1552
1545
|
log.heading(`VM Hosts de Banco (${resp.count})`);
|
|
1553
1546
|
renderTable([
|
|
1554
|
-
{ header: 'ID', value: (r) => r.id,
|
|
1547
|
+
{ header: 'ID', value: (r) => r.id, identificador: true },
|
|
1555
1548
|
{ header: 'Nome', value: (r) => r.name ?? '—', maxWidth: 22 },
|
|
1556
1549
|
{ header: 'Região', value: (r) => r.region ?? '—', maxWidth: 14 },
|
|
1557
1550
|
{ 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 (
|
|
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):
|
|
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 {
|