@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/lib/cli-write.js
CHANGED
|
@@ -20,6 +20,9 @@ import { requireToken, handleApiError } from './cli-read.js';
|
|
|
20
20
|
import { CliApiError } from './api-client.js';
|
|
21
21
|
import { log } from '../utils/logger.js';
|
|
22
22
|
import { safeExit } from '../utils/safe-exit.js';
|
|
23
|
+
import { EXIT_CODES, NeetruCliError } from '../cli-kit/errors.js';
|
|
24
|
+
import { adviseCommand, adviseFlag, declaredFlags } from '../cli-kit/command-context.js';
|
|
25
|
+
import { exitWithCliError } from './cli-error.js';
|
|
23
26
|
export { requireToken, handleApiError };
|
|
24
27
|
function getMfaSessionFilePath() {
|
|
25
28
|
return join(homedir(), '.config', 'neetru-cli', 'mfa-session.json');
|
|
@@ -89,6 +92,55 @@ export async function clearMfaSession() {
|
|
|
89
92
|
// Ignora — arquivo já inexistente ou sem permissão.
|
|
90
93
|
}
|
|
91
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* uid dono de um token de sessão MFA. O Core emite
|
|
97
|
+
* `<uid_base64url>.<issuedAt_hex>.<hmac_sha256_hex>` (`createCliMfaSessionToken`
|
|
98
|
+
* em `src/lib/auth-mfa-session.ts`): o uid é a parte ASSINADA, não segredo — o
|
|
99
|
+
* servidor só aceita a sessão se ele bater com o uid do Bearer. `null` se o
|
|
100
|
+
* formato não for reconhecido.
|
|
101
|
+
*/
|
|
102
|
+
export function mfaSessionOwnerUid(token) {
|
|
103
|
+
const parts = token.split('.');
|
|
104
|
+
if (parts.length !== 3 || !parts[0])
|
|
105
|
+
return null;
|
|
106
|
+
try {
|
|
107
|
+
const uid = Buffer.from(parts[0], 'base64url').toString('utf8');
|
|
108
|
+
return /^[\x21-\x7e]+$/.test(uid) ? uid : null;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Descarta a sessão MFA salva quando ela não é do `uid` que acabou de logar.
|
|
116
|
+
*
|
|
117
|
+
* A8 (2026-10-13): `neetru login` com outra conta trocava o Bearer mas deixava
|
|
118
|
+
* o `mfa-session.json` da conta anterior. O Core recusa sessão de outro uid,
|
|
119
|
+
* então TODO comando destrutivo tomava 401 `mfa_required` por até 1h (e, antes
|
|
120
|
+
* do conserto da precedência em `resolveStepUp`, nem `--mfa-token` salvava).
|
|
121
|
+
* Sessão de dono desconhecido (arquivo corrompido, formato não reconhecido)
|
|
122
|
+
* também sai — o servidor recusaria do mesmo jeito. Sessão do MESMO uid fica
|
|
123
|
+
* (re-login na mesma conta não deve exigir TOTP de novo). Retorna `true` se
|
|
124
|
+
* apagou.
|
|
125
|
+
*/
|
|
126
|
+
export async function discardMfaSessionOfOtherUid(uid) {
|
|
127
|
+
const path = getMfaSessionFilePath();
|
|
128
|
+
if (!existsSync(path))
|
|
129
|
+
return false;
|
|
130
|
+
let owner = null;
|
|
131
|
+
try {
|
|
132
|
+
const data = JSON.parse(readFileSync(path, 'utf8'));
|
|
133
|
+
if (typeof data.mfaSessionToken === 'string')
|
|
134
|
+
owner = mfaSessionOwnerUid(data.mfaSessionToken);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// corrompido → dono desconhecido → descarta
|
|
138
|
+
}
|
|
139
|
+
if (owner !== null && owner === uid)
|
|
140
|
+
return false;
|
|
141
|
+
await clearMfaSession();
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
92
144
|
/**
|
|
93
145
|
* Pede uma confirmação interativa `y/N` no terminal. Resolve `true` se o
|
|
94
146
|
* operador digitou `y`/`yes` (case-insensitive); `false` caso contrário.
|
|
@@ -108,6 +160,31 @@ function promptYesNo(question) {
|
|
|
108
160
|
});
|
|
109
161
|
});
|
|
110
162
|
}
|
|
163
|
+
/** As flags que pulam a confirmação, na ordem em que o conselho as cita. */
|
|
164
|
+
const FLAGS_DE_CONFIRMACAO = ['--yes', '--force'];
|
|
165
|
+
/**
|
|
166
|
+
* O que dizer quando a confirmação não pode ser perguntada (P105 /
|
|
167
|
+
* bug_89d2497e). Cita só as flags de confirmação que o comando EM EXECUÇÃO
|
|
168
|
+
* declara: o texto fixo "use --yes (ou --force)" chegava a 8 comandos que
|
|
169
|
+
* declaram `--yes` mas não `--force` (medido na árvore do Commander em
|
|
170
|
+
* 2026-10-18: `servers pgbouncer sync-userlist`, `products memory set`,
|
|
171
|
+
* `vm memory set`, `db move start|abort|decommission`, `promote execute`,
|
|
172
|
+
* `mobile revoke`) — quem seguisse com `--force` tomava "unknown option".
|
|
173
|
+
* Sem nenhuma das duas (ou fora do Commander, como no menu), não cita flag.
|
|
174
|
+
*/
|
|
175
|
+
export function confirmationAdvice(modo) {
|
|
176
|
+
const flags = declaredFlags(FLAGS_DE_CONFIRMACAO);
|
|
177
|
+
if (modo === 'json') {
|
|
178
|
+
return flags.length > 0
|
|
179
|
+
? `Operação destrutiva em modo --json exige ${flags[0]}.`
|
|
180
|
+
: 'Operação destrutiva em modo --json exige confirmação, e este comando não tem como confirmar sem perguntar.';
|
|
181
|
+
}
|
|
182
|
+
if (flags.length === 0) {
|
|
183
|
+
return 'Terminal não-interativo: esta operação destrutiva só pode ser confirmada num terminal interativo.';
|
|
184
|
+
}
|
|
185
|
+
const lista = flags.length > 1 ? `${flags[0]} (ou ${flags[1]})` : flags[0];
|
|
186
|
+
return `Terminal não-interativo: use ${lista} para confirmar operações destrutivas.`;
|
|
187
|
+
}
|
|
111
188
|
/**
|
|
112
189
|
* Gate de confirmação pra operações DESTRUTIVAS (suspend, deactivate, delete,
|
|
113
190
|
* rollback, archive, yank). Se `--yes`/`--force` foi passado, retorna sem
|
|
@@ -129,7 +206,7 @@ export async function confirmDestructive(message, opts) {
|
|
|
129
206
|
console.log(JSON.stringify({
|
|
130
207
|
ok: false,
|
|
131
208
|
error: 'confirmation_required',
|
|
132
|
-
message: '
|
|
209
|
+
message: confirmationAdvice('json'),
|
|
133
210
|
}));
|
|
134
211
|
safeExit(1);
|
|
135
212
|
}
|
|
@@ -137,7 +214,7 @@ export async function confirmDestructive(message, opts) {
|
|
|
137
214
|
const confirmed = await promptYesNo('Confirmar?');
|
|
138
215
|
if (!confirmed) {
|
|
139
216
|
if (!process.stdin.isTTY) {
|
|
140
|
-
log.error('
|
|
217
|
+
log.error(confirmationAdvice('nao-interativo'));
|
|
141
218
|
}
|
|
142
219
|
else {
|
|
143
220
|
log.dim('Operação cancelada.');
|
|
@@ -157,21 +234,56 @@ function promptLine(question) {
|
|
|
157
234
|
});
|
|
158
235
|
});
|
|
159
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* O step-up que dá pra mandar SEM interação, na ordem de precedência:
|
|
239
|
+
* 1. `--mfa-token` explícito → `X-Neetru-MFA-Token`;
|
|
240
|
+
* 2. sessão de 1h salva (`~/.config/neetru-cli/mfa-session.json`) →
|
|
241
|
+
* `X-Neetru-MFA-Session` (salvo `skipSession`).
|
|
242
|
+
* `{}` se não houver nenhum dos dois (aí o caller prompta ou falha fechado).
|
|
243
|
+
*
|
|
244
|
+
* A8 (2026-10-13): o código explícito VENCE a sessão. Antes `resolveStepUp`
|
|
245
|
+
* olhava a sessão primeiro e devolvia só `X-Neetru-MFA-Session`, descartando o
|
|
246
|
+
* `--mfa-token` em silêncio — enquanto `withStepUpRetry` fazia o contrário.
|
|
247
|
+
* Depois de `neetru login` com outra conta (sessão do uid anterior no disco,
|
|
248
|
+
* que o Core recusa), ~40 comandos destrutivos davam 401 por até 1h MESMO com
|
|
249
|
+
* `--mfa-token`. Quem digita o código está pedindo que ele seja usado. As duas
|
|
250
|
+
* funções agora decidem por aqui — uma regra só.
|
|
251
|
+
*/
|
|
252
|
+
function nonInteractiveStepUp(opts) {
|
|
253
|
+
const code = (opts.mfaToken ?? '').trim();
|
|
254
|
+
const savedSession = opts.skipSession ? null : loadMfaSession();
|
|
255
|
+
// Código explícito + sessão salva: manda OS DOIS (revisão do #78). O Core
|
|
256
|
+
// (`requireCliStepUpMfa`) tenta a sessão primeiro e só usa o TOTP se ela não
|
|
257
|
+
// servir — e o TOTP é de USO ÚNICO (`mfa-lockout`). Mandar só o código gastava
|
|
258
|
+
// o código à toa quando a sessão era válida, e o 2º comando com o mesmo código
|
|
259
|
+
// (agente com o código ditado, script) falhava com mfa_invalid. Com os dois, a
|
|
260
|
+
// sessão de OUTRA conta (o caso do A8) cai e o código ainda salva o pedido.
|
|
261
|
+
if (code) {
|
|
262
|
+
return savedSession
|
|
263
|
+
? { 'X-Neetru-MFA-Token': code, 'X-Neetru-MFA-Session': savedSession }
|
|
264
|
+
: { 'X-Neetru-MFA-Token': code };
|
|
265
|
+
}
|
|
266
|
+
if (savedSession)
|
|
267
|
+
return { 'X-Neetru-MFA-Session': savedSession };
|
|
268
|
+
return {};
|
|
269
|
+
}
|
|
160
270
|
/**
|
|
161
271
|
* Resolve o que uma operação destrutiva TOP-TIER precisa mandar pro servidor:
|
|
162
|
-
* - `headers`: `X-Neetru-MFA-
|
|
163
|
-
* (
|
|
272
|
+
* - `headers`: `X-Neetru-MFA-Token` (TOTP fresco) OU `X-Neetru-MFA-Session`
|
|
273
|
+
* (sessão de 1h) e/ou `X-Neetru-Dry-Run`.
|
|
164
274
|
*
|
|
165
275
|
* Ordem de preferência:
|
|
166
276
|
* 1. dry-run: sem MFA, só `X-Neetru-Dry-Run: true`.
|
|
167
|
-
* 2.
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
* 3.
|
|
171
|
-
*
|
|
172
|
-
*
|
|
277
|
+
* 2. `--mfa-token` explícito: manda `X-Neetru-MFA-Token` — e também a sessão
|
|
278
|
+
* salva, se houver (o Core tenta a sessão antes e não gasta o TOTP à toa).
|
|
279
|
+
* O código NUNCA é descartado em favor da sessão (A8).
|
|
280
|
+
* 3. Sessão de 1h salva localmente: usa `X-Neetru-MFA-Session` sem pedir
|
|
281
|
+
* TOTP. Silencioso — o operador não precisa saber que a sessão está ativa.
|
|
282
|
+
* 4. Prompt interativo do TOTP. Em `--json`/não-interativo sem código nem
|
|
283
|
+
* sessão, encerra com instrução clara — o servidor recusaria de qualquer
|
|
284
|
+
* forma (fail-closed).
|
|
173
285
|
*
|
|
174
|
-
* feature mfa-session (2026-06-12): o item
|
|
286
|
+
* feature mfa-session (2026-06-12): o item 3 elimina o re-prompt TOTP a cada
|
|
175
287
|
* comando destrutivo quando o operador já rodou `neetru auth mfa-session`.
|
|
176
288
|
*/
|
|
177
289
|
export async function resolveStepUp(opts) {
|
|
@@ -180,34 +292,35 @@ export async function resolveStepUp(opts) {
|
|
|
180
292
|
headers['X-Neetru-Dry-Run'] = 'true';
|
|
181
293
|
return { headers };
|
|
182
294
|
}
|
|
183
|
-
// ──
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
if (!
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
|
|
295
|
+
// ── Prioridades 2 e 3: código explícito, depois sessão de 1h ────────────
|
|
296
|
+
const ready = nonInteractiveStepUp(opts);
|
|
297
|
+
if (Object.keys(ready).length > 0)
|
|
298
|
+
return { headers: ready };
|
|
299
|
+
// ── Prioridade 4: prompt do TOTP ─────────────────────────────────────────
|
|
300
|
+
// bug auditoria 2026-06-30: em --json o prompt interativo de TOTP NÃO pode
|
|
301
|
+
// rodar NEM com TTY — ele escreveria a pergunta no stdout (corrompe o JSON)
|
|
302
|
+
// e penduraria um consumidor programático. Fail-CLOSED em --json OU sem TTY;
|
|
303
|
+
// só promptar em terminal interativo humano (não-json + TTY).
|
|
304
|
+
if (opts.json || opts.nonInteractive || !process.stdin.isTTY) {
|
|
305
|
+
// bug_89d2497e: o conselho só cita `--mfa-token` se o comando em execução
|
|
306
|
+
// declara a flag (mesmo defeito do bug_757b9746, do lado do step-up). O
|
|
307
|
+
// código de saída segue 1 e o JSON segue `{ ok, error:'mfa_required', message }`.
|
|
308
|
+
// P105: o comando alternativo (`neetru auth mfa-session`) também só é
|
|
309
|
+
// citado se existe na árvore do Commander.
|
|
310
|
+
const sessao = (withCommand, withoutCommand) => adviseCommand({ command: 'auth mfa-session', withCommand, withoutCommand });
|
|
311
|
+
const msg = adviseFlag({
|
|
312
|
+
flag: '--mfa-token',
|
|
313
|
+
withFlag: sessao('Operação destrutiva top-tier exige step-up MFA: passe --mfa-token <código TOTP> ' +
|
|
314
|
+
'(modo --json/não-interativo) ou rode `neetru auth mfa-session` para obter uma sessão de 1h.', 'Operação destrutiva top-tier exige step-up MFA: passe --mfa-token <código TOTP> ' +
|
|
315
|
+
'(modo --json/não-interativo).'),
|
|
316
|
+
withoutFlag: sessao('Operação destrutiva top-tier exige step-up MFA: rode `neetru auth mfa-session` ' +
|
|
317
|
+
'para obter uma sessão de 1h e repita o comando.', 'Operação destrutiva top-tier exige step-up MFA, e esta CLI não tem, neste comando, ' +
|
|
318
|
+
'como enviá-lo em modo --json/não-interativo.'),
|
|
319
|
+
});
|
|
320
|
+
exitWithCliError(new NeetruCliError(msg, { code: 'mfa_required', exit: EXIT_CODES.client }), opts.json);
|
|
321
|
+
return { headers }; // safeExit não retorna em prod; satisfaz o tipo + testes
|
|
210
322
|
}
|
|
323
|
+
const code = await promptLine('Código MFA (TOTP) para confirmar:');
|
|
211
324
|
if (!code) {
|
|
212
325
|
log.error('Código MFA não fornecido — operação cancelada.');
|
|
213
326
|
safeExit(1);
|
|
@@ -237,13 +350,9 @@ export function isMfaRequiredError(error) {
|
|
|
237
350
|
export async function withStepUpRetry(opts, doRequest, onError,
|
|
238
351
|
/** Chamado antes do prompt TOTP do retry (ex.: parar o spinner). */
|
|
239
352
|
beforePrompt) {
|
|
240
|
-
|
|
241
|
-
const
|
|
242
|
-
const
|
|
243
|
-
if (code)
|
|
244
|
-
first['X-Neetru-MFA-Token'] = code;
|
|
245
|
-
else if (savedSession)
|
|
246
|
-
first['X-Neetru-MFA-Session'] = savedSession;
|
|
353
|
+
// Mesma precedência do `resolveStepUp` (A8): código explícito, depois sessão.
|
|
354
|
+
const first = nonInteractiveStepUp(opts);
|
|
355
|
+
const code = first['X-Neetru-MFA-Token'];
|
|
247
356
|
let firstError;
|
|
248
357
|
try {
|
|
249
358
|
return await doRequest(first);
|
|
@@ -114,6 +114,10 @@ export declare const neetruConfigSchema: z.ZodObject<{
|
|
|
114
114
|
slug: string;
|
|
115
115
|
runtime: "nextjs" | "node-api";
|
|
116
116
|
$schema?: string | undefined;
|
|
117
|
+
deploy?: z.objectOutputType<{
|
|
118
|
+
region: z.ZodOptional<z.ZodString>;
|
|
119
|
+
serviceName: z.ZodOptional<z.ZodString>;
|
|
120
|
+
}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
117
121
|
tenantId?: string | undefined;
|
|
118
122
|
docs?: z.objectOutputType<{
|
|
119
123
|
protocolVersion: z.ZodOptional<z.ZodNumber>;
|
|
@@ -131,10 +135,6 @@ export declare const neetruConfigSchema: z.ZodObject<{
|
|
|
131
135
|
ctaHref?: string | undefined;
|
|
132
136
|
ctaLabel?: string | undefined;
|
|
133
137
|
} | undefined;
|
|
134
|
-
deploy?: z.objectOutputType<{
|
|
135
|
-
region: z.ZodOptional<z.ZodString>;
|
|
136
|
-
serviceName: z.ZodOptional<z.ZodString>;
|
|
137
|
-
}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
138
138
|
database?: {
|
|
139
139
|
engine?: "firestore-instance" | "vm-postgres-single" | "vm-mysql-single" | "nosql-vm" | undefined;
|
|
140
140
|
dbId?: string | undefined;
|
|
@@ -144,6 +144,10 @@ export declare const neetruConfigSchema: z.ZodObject<{
|
|
|
144
144
|
slug: string;
|
|
145
145
|
runtime: "nextjs" | "node-api";
|
|
146
146
|
$schema?: string | undefined;
|
|
147
|
+
deploy?: z.objectInputType<{
|
|
148
|
+
region: z.ZodOptional<z.ZodString>;
|
|
149
|
+
serviceName: z.ZodOptional<z.ZodString>;
|
|
150
|
+
}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
147
151
|
tenantId?: string | undefined;
|
|
148
152
|
docs?: z.objectInputType<{
|
|
149
153
|
protocolVersion: z.ZodOptional<z.ZodNumber>;
|
|
@@ -161,10 +165,6 @@ export declare const neetruConfigSchema: z.ZodObject<{
|
|
|
161
165
|
ctaHref?: string | undefined;
|
|
162
166
|
ctaLabel?: string | undefined;
|
|
163
167
|
} | undefined;
|
|
164
|
-
deploy?: z.objectInputType<{
|
|
165
|
-
region: z.ZodOptional<z.ZodString>;
|
|
166
|
-
serviceName: z.ZodOptional<z.ZodString>;
|
|
167
|
-
}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
168
168
|
database?: {
|
|
169
169
|
engine?: "firestore-instance" | "vm-postgres-single" | "vm-mysql-single" | "nosql-vm" | undefined;
|
|
170
170
|
dbId?: string | undefined;
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
import * as fs from 'node:fs';
|
|
12
12
|
import * as path from 'node:path';
|
|
13
13
|
import { randomBytes } from 'node:crypto';
|
|
14
|
+
import { avisarSobraDeTemporario, removerArquivo } from '../fs-seguro.js';
|
|
15
|
+
import { NEETRU_ENVS } from './env.js';
|
|
14
16
|
/** Registry v2 vazio — usado quando o arquivo ainda não existe. */
|
|
15
17
|
function emptyDbJson() {
|
|
16
18
|
return { version: 2, databases: [] };
|
|
@@ -18,7 +20,8 @@ function emptyDbJson() {
|
|
|
18
20
|
// ---------------------------------------------------------------------------
|
|
19
21
|
// migrateDbJson — qualquer entrada → v2 canônico
|
|
20
22
|
// ---------------------------------------------------------------------------
|
|
21
|
-
|
|
23
|
+
// P105: as chaves de `ids` são o alfabeto de banco — a mesma lista que valida.
|
|
24
|
+
const VALID_ENV_KEYS = NEETRU_ENVS;
|
|
22
25
|
function isPlainObject(value) {
|
|
23
26
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
24
27
|
}
|
|
@@ -144,12 +147,17 @@ export function writeDbJson(filePath, data) {
|
|
|
144
147
|
fs.renameSync(tmpPath, filePath);
|
|
145
148
|
}
|
|
146
149
|
catch (err) {
|
|
147
|
-
// Best-effort: remove o temp pra não deixar lixo se algo falhou.
|
|
150
|
+
// Best-effort: remove o temp pra não deixar lixo se algo falhou. Não usa
|
|
151
|
+
// `fs.rmSync`: com acento no caminho (`C:\Users\João\...`), no Node 24 do
|
|
152
|
+
// Windows ele volta sem erro e não apaga (ver `lib/fs-seguro.ts`).
|
|
153
|
+
// `removerArquivo` apaga e confere; se ainda assim falhar, o erro que
|
|
154
|
+
// importa é o original, que segue abaixo — e a sobra vira aviso no stderr
|
|
155
|
+
// com o caminho, em vez de sumir calada (revisão do #79).
|
|
148
156
|
try {
|
|
149
|
-
|
|
157
|
+
removerArquivo(tmpPath);
|
|
150
158
|
}
|
|
151
|
-
catch {
|
|
152
|
-
|
|
159
|
+
catch (limpeza) {
|
|
160
|
+
avisarSobraDeTemporario(tmpPath, limpeza);
|
|
153
161
|
}
|
|
154
162
|
throw err;
|
|
155
163
|
}
|
|
@@ -23,8 +23,20 @@
|
|
|
23
23
|
* Ref: glossário 0.1.4 exporta normalizeEnvId() que usa 'prod' — use-o para
|
|
24
24
|
* queries inter-sistema quando disponível.
|
|
25
25
|
*/
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
import type { NeetruCliError } from '../../cli-kit/errors.js';
|
|
27
|
+
/**
|
|
28
|
+
* Os 3 ambientes canônicos persistidos no `.neetru/db.json`. Fonte única: a
|
|
29
|
+
* validação e a lista de "valores válidos" das mensagens saem daqui
|
|
30
|
+
* (bug_89d2497e — antes a lista era copiada à mão em 5 mensagens).
|
|
31
|
+
*/
|
|
32
|
+
export declare const NEETRU_ENVS: readonly ["dev-local", "staging", "production"];
|
|
33
|
+
export type NeetruEnv = (typeof NEETRU_ENVS)[number];
|
|
34
|
+
/**
|
|
35
|
+
* Erro de `--env` inválido nos comandos de banco (bug_89d2497e): os valores
|
|
36
|
+
* válidos saem de `NEETRU_ENVS` e os apelidos de `ENV_ALIASES` — as mesmas
|
|
37
|
+
* fontes que validam. Sai como erro do cliente (exit 1), código `invalid_env`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function erroAmbienteBancoInvalido(input: unknown): NeetruCliError;
|
|
28
40
|
/**
|
|
29
41
|
* Normaliza um nome de ambiente para um dos 3 valores canônicos.
|
|
30
42
|
*
|
|
@@ -32,6 +44,6 @@ export type NeetruEnv = 'dev-local' | 'staging' | 'production';
|
|
|
32
44
|
* no stderr listando o valor canônico. Nunca normaliza silenciosamente.
|
|
33
45
|
*
|
|
34
46
|
* Faz lowercase + trim antes do lookup. Entrada não-string, vazia ou
|
|
35
|
-
* desconhecida lança
|
|
47
|
+
* desconhecida lança o `NeetruCliError` de `erroAmbienteBancoInvalido`.
|
|
36
48
|
*/
|
|
37
49
|
export declare function normalizeEnv(input: string): NeetruEnv;
|
package/dist/lib/db-local/env.js
CHANGED
|
@@ -23,6 +23,13 @@
|
|
|
23
23
|
* Ref: glossário 0.1.4 exporta normalizeEnvId() que usa 'prod' — use-o para
|
|
24
24
|
* queries inter-sistema quando disponível.
|
|
25
25
|
*/
|
|
26
|
+
import { invalidChoiceError } from '../../cli-kit/choices.js';
|
|
27
|
+
/**
|
|
28
|
+
* Os 3 ambientes canônicos persistidos no `.neetru/db.json`. Fonte única: a
|
|
29
|
+
* validação e a lista de "valores válidos" das mensagens saem daqui
|
|
30
|
+
* (bug_89d2497e — antes a lista era copiada à mão em 5 mensagens).
|
|
31
|
+
*/
|
|
32
|
+
export const NEETRU_ENVS = ['dev-local', 'staging', 'production'];
|
|
26
33
|
/** Mapa de aliases aceitos → valor canônico. */
|
|
27
34
|
const ENV_ALIASES = {
|
|
28
35
|
dev: 'dev-local',
|
|
@@ -43,6 +50,31 @@ const ALIAS_NOTICE = {
|
|
|
43
50
|
stg: 'staging',
|
|
44
51
|
prod: 'production',
|
|
45
52
|
};
|
|
53
|
+
/** `dev/local → dev-local, stg → staging, prod → production` — de `ENV_ALIASES`. */
|
|
54
|
+
function apelidosLegiveis() {
|
|
55
|
+
const porDestino = new Map();
|
|
56
|
+
for (const [apelido, destino] of Object.entries(ENV_ALIASES)) {
|
|
57
|
+
if (apelido === destino)
|
|
58
|
+
continue;
|
|
59
|
+
porDestino.set(destino, [...(porDestino.get(destino) ?? []), apelido]);
|
|
60
|
+
}
|
|
61
|
+
return [...porDestino].map(([destino, apelidos]) => `${apelidos.join('/')} → ${destino}`).join(', ');
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Erro de `--env` inválido nos comandos de banco (bug_89d2497e): os valores
|
|
65
|
+
* válidos saem de `NEETRU_ENVS` e os apelidos de `ENV_ALIASES` — as mesmas
|
|
66
|
+
* fontes que validam. Sai como erro do cliente (exit 1), código `invalid_env`.
|
|
67
|
+
*/
|
|
68
|
+
export function erroAmbienteBancoInvalido(input) {
|
|
69
|
+
return invalidChoiceError({
|
|
70
|
+
flag: '--env',
|
|
71
|
+
value: typeof input === 'string' ? input : `(${typeof input})`,
|
|
72
|
+
valid: NEETRU_ENVS,
|
|
73
|
+
subject: 'Ambiente',
|
|
74
|
+
code: 'invalid_env',
|
|
75
|
+
hint: `Apelidos aceitos: ${apelidosLegiveis()}.`,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
46
78
|
/**
|
|
47
79
|
* Normaliza um nome de ambiente para um dos 3 valores canônicos.
|
|
48
80
|
*
|
|
@@ -50,20 +82,15 @@ const ALIAS_NOTICE = {
|
|
|
50
82
|
* no stderr listando o valor canônico. Nunca normaliza silenciosamente.
|
|
51
83
|
*
|
|
52
84
|
* Faz lowercase + trim antes do lookup. Entrada não-string, vazia ou
|
|
53
|
-
* desconhecida lança
|
|
85
|
+
* desconhecida lança o `NeetruCliError` de `erroAmbienteBancoInvalido`.
|
|
54
86
|
*/
|
|
55
87
|
export function normalizeEnv(input) {
|
|
56
|
-
if (typeof input !== 'string')
|
|
57
|
-
throw
|
|
58
|
-
`Valores aceitos: dev-local, staging, production.`);
|
|
59
|
-
}
|
|
88
|
+
if (typeof input !== 'string')
|
|
89
|
+
throw erroAmbienteBancoInvalido(input);
|
|
60
90
|
const key = input.trim().toLowerCase();
|
|
61
91
|
const canonical = ENV_ALIASES[key];
|
|
62
|
-
if (!canonical)
|
|
63
|
-
throw
|
|
64
|
-
`Valores aceitos: dev-local, staging, production. ` +
|
|
65
|
-
`Aliases aceitos: dev/local → dev-local, stg → staging, prod → production.`);
|
|
66
|
-
}
|
|
92
|
+
if (!canonical)
|
|
93
|
+
throw erroAmbienteBancoInvalido(input);
|
|
67
94
|
// D-4: aviso explícito quando alias não-canônico é usado
|
|
68
95
|
const notice = ALIAS_NOTICE[key];
|
|
69
96
|
if (notice) {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { type RehearseEngine } from './rehearse.js';
|
|
2
2
|
import type { PipelineDeps } from './types.js';
|
|
3
|
-
export
|
|
3
|
+
export interface OpcoesGenerateSql {
|
|
4
|
+
/** Remove o `--out` temporário. Injetável só para teste. */
|
|
5
|
+
remover?: (dir: string) => Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
export declare function generateSql(schemaPath: string, engine?: RehearseEngine, { remover }?: OpcoesGenerateSql): Promise<string>;
|
|
4
8
|
/**
|
|
5
9
|
* Constrói o conjunto de `PipelineDeps` de PRODUÇÃO.
|
|
6
10
|
*
|
|
@@ -25,6 +25,7 @@ import * as path from 'node:path';
|
|
|
25
25
|
import { spawn } from 'node:child_process';
|
|
26
26
|
import { classifyMigration } from '@neetru/db-classifier';
|
|
27
27
|
import { apiRequest } from '../api-client.js';
|
|
28
|
+
import { avisarSobraDeTemporario, removerDiretorio } from '../fs-seguro.js';
|
|
28
29
|
import { buildRehearseInContainer, buildDockerExecFn, } from './rehearse.js';
|
|
29
30
|
/**
|
|
30
31
|
* Prova em COMPILE-TIME que `classifyMigration` do `@neetru/db-classifier` é
|
|
@@ -273,7 +274,7 @@ function makeTableAndIndexIdempotent(sql) {
|
|
|
273
274
|
.replace(/CREATE TABLE (?!IF NOT EXISTS\b)/g, 'CREATE TABLE IF NOT EXISTS ')
|
|
274
275
|
.replace(/CREATE (UNIQUE )?INDEX (?!IF NOT EXISTS\b)/g, (_match, unique) => `CREATE ${unique ?? ''}INDEX IF NOT EXISTS `);
|
|
275
276
|
}
|
|
276
|
-
export function generateSql(schemaPath, engine = 'postgres') {
|
|
277
|
+
export function generateSql(schemaPath, engine = 'postgres', { remover = removerDiretorio } = {}) {
|
|
277
278
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'neetru-drizzle-'));
|
|
278
279
|
return new Promise((resolve, reject) => {
|
|
279
280
|
const child = spawn('npx', [
|
|
@@ -333,8 +334,27 @@ export function generateSql(schemaPath, engine = 'postgres') {
|
|
|
333
334
|
reject(new Error(`falha lendo o .sql gerado em ${tmpDir}: ${err.message}`));
|
|
334
335
|
}
|
|
335
336
|
});
|
|
336
|
-
}).
|
|
337
|
-
|
|
337
|
+
}).then(
|
|
338
|
+
// Limpa o --out temporário. Não usa `fs.rmSync`: o `os.tmpdir()` de quem tem
|
|
339
|
+
// acento na home (`C:\Users\João\AppData\Local\Temp`) faz o rmSync do
|
|
340
|
+
// Node 24 no Windows voltar sem erro e não apagar nada — um `neetru-drizzle-*`
|
|
341
|
+
// perdido por geração. `removerDiretorio` apaga e confere (ver `lib/fs-seguro.ts`).
|
|
342
|
+
//
|
|
343
|
+
// A falha da limpeza nunca derruba o resultado nem esconde o erro da
|
|
344
|
+
// geração, mas também não some calada: vira aviso no stderr com o caminho
|
|
345
|
+
// que sobrou (revisão do #79).
|
|
346
|
+
async (sql) => {
|
|
347
|
+
try {
|
|
348
|
+
await remover(tmpDir);
|
|
349
|
+
}
|
|
350
|
+
catch (limpeza) {
|
|
351
|
+
avisarSobraDeTemporario(tmpDir, limpeza);
|
|
352
|
+
}
|
|
353
|
+
return sql;
|
|
354
|
+
}, async (err) => {
|
|
355
|
+
// O erro da geração é o que importa: falha na limpeza não pode escondê-lo.
|
|
356
|
+
await remover(tmpDir).catch((limpeza) => avisarSobraDeTemporario(tmpDir, limpeza));
|
|
357
|
+
throw err;
|
|
338
358
|
});
|
|
339
359
|
}
|
|
340
360
|
/**
|
|
@@ -16,12 +16,16 @@
|
|
|
16
16
|
* Postgres e sem rede. A fiação real (drizzle-kit / container / HTTP) vive
|
|
17
17
|
* no `buildPipelineDeps()`.
|
|
18
18
|
*/
|
|
19
|
+
import type { NeetruEnv } from '../db-local/env.js';
|
|
19
20
|
/** As 5 fases do pipeline, na ordem de execução. */
|
|
20
21
|
export type PipelinePhase = 'fingerprint' | 'generate' | 'rehearse' | 'classify' | 'push';
|
|
21
22
|
/** Severidade global da migração — espelha o `@neetru/db-classifier`. */
|
|
22
23
|
export type MigrationSeverity = 'aditiva' | 'destrutiva';
|
|
23
|
-
/**
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Os 3 ambientes canônicos do `neetru db apply` — o alfabeto de BANCO
|
|
26
|
+
* (`NEETRU_ENVS`, `db-local/env.ts`). P105: era a união escrita à mão.
|
|
27
|
+
*/
|
|
28
|
+
export type ApplyEnv = NeetruEnv;
|
|
25
29
|
/**
|
|
26
30
|
* Relatório de classificação — subconjunto estrutural do `ClassificationReport`
|
|
27
31
|
* do `@neetru/db-classifier`. Tipado localmente pra que o orquestrador e seus
|
|
@@ -1,20 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `@neetru/db-pipeline` — contratos do orquestrador do `neetru db apply`.
|
|
3
|
-
*
|
|
4
|
-
* O pipeline roda CLIENT-SIDE as fases 1-5 do `neetru db apply`:
|
|
5
|
-
* 1. `fingerprint` — hash determinístico do schema.
|
|
6
|
-
* 2. `generate` — `drizzle-kit generate` produz o `.sql`.
|
|
7
|
-
* 3. `rehearse` — ensaia o `.sql` num Postgres efêmero real.
|
|
8
|
-
* 4. `classify` — `@neetru/db-classifier` marca aditiva/destrutiva.
|
|
9
|
-
* 5. `push` — adquire lock + envia o artefato validado pro Core.
|
|
10
|
-
*
|
|
11
|
-
* O pipeline NÃO decide política — ele produz um `.sql` validado e
|
|
12
|
-
* classificado e o envia. A decisão de aplicar (ou exigir confirmação numa
|
|
13
|
-
* migração destrutiva) é do Core.
|
|
14
|
-
*
|
|
15
|
-
* Tudo aqui é deps-injetado: o orquestrador é unit-testável sem Docker, sem
|
|
16
|
-
* Postgres e sem rede. A fiação real (drizzle-kit / container / HTTP) vive
|
|
17
|
-
* no `buildPipelineDeps()`.
|
|
18
|
-
*/
|
|
19
1
|
export {};
|
|
20
2
|
//# sourceMappingURL=types.js.map
|