@neetru/cli 2.29.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 +141 -0
- package/README.md +63 -17
- 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 +136 -2
- package/dist/commands/deploy.js +547 -69
- package/dist/commands/deployments.js +11 -6
- package/dist/commands/dev-context.js +15 -5
- package/dist/commands/dev-up.d.ts +7 -5
- package/dist/commands/dev-up.js +24 -23
- package/dist/commands/docs-produto.d.ts +90 -0
- package/dist/commands/docs-produto.js +384 -0
- 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.d.ts +2 -0
- package/dist/commands/init.js +14 -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 +31 -1
- package/dist/commands/new.js +94 -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 +142 -20
- package/dist/lib/api-client.d.ts +22 -1
- package/dist/lib/api-client.js +79 -8
- 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/compose-generator.d.ts +13 -9
- package/dist/lib/compose-generator.js +28 -60
- package/dist/lib/config-schema.d.ts +36 -2
- package/dist/lib/config-schema.js +11 -0
- 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/check.d.ts +62 -0
- package/dist/lib/product-docs/check.js +489 -0
- package/dist/lib/product-docs/config.d.ts +66 -0
- package/dist/lib/product-docs/config.js +234 -0
- package/dist/lib/product-docs/dates.d.ts +17 -0
- package/dist/lib/product-docs/dates.js +41 -0
- package/dist/lib/product-docs/frontmatter.d.ts +43 -0
- package/dist/lib/product-docs/frontmatter.js +302 -0
- package/dist/lib/product-docs/git.d.ts +16 -0
- package/dist/lib/product-docs/git.js +92 -0
- package/dist/lib/product-docs/markdown.d.ts +27 -0
- package/dist/lib/product-docs/markdown.js +98 -0
- package/dist/lib/product-docs/ops.d.ts +133 -0
- package/dist/lib/product-docs/ops.js +975 -0
- package/dist/lib/product-docs/structure.d.ts +61 -0
- package/dist/lib/product-docs/structure.js +239 -0
- package/dist/lib/product-docs/sync.d.ts +107 -0
- package/dist/lib/product-docs/sync.js +322 -0
- package/dist/lib/product-docs/taxonomy.d.ts +46 -0
- package/dist/lib/product-docs/taxonomy.js +163 -0
- package/dist/lib/product-docs/templates.d.ts +53 -0
- package/dist/lib/product-docs/templates.js +245 -0
- package/dist/lib/public-secret-scan.d.ts +117 -0
- package/dist/lib/public-secret-scan.js +771 -0
- 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.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/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/templates/dev/Dockerfile.agent +0 -39
- package/templates/dev/agent-entrypoint.sh +0 -41
- package/templates/dev/docker-compose.dev.yml +0 -63
|
@@ -13,6 +13,7 @@ import chalk from 'chalk';
|
|
|
13
13
|
import { apiRequest, CliApiError, CliNetworkError } from '../lib/api-client.js';
|
|
14
14
|
import { requireToken, handleApiError, confirmDestructive, printWriteResult, resolveStepUp, } from '../lib/cli-write.js';
|
|
15
15
|
import { pickProductId, pickServer, pickTenant, promptChoice, promptText } from '../lib/pickers.js';
|
|
16
|
+
import { DEFAULT_TENANT_ENVIRONMENT_ID, ESCOLHAS_AMBIENTE_TENANT, resolverAmbienteTenant, } from '../lib/envs.js';
|
|
16
17
|
import { log } from '../utils/logger.js';
|
|
17
18
|
import { safeExit } from '../utils/safe-exit.js';
|
|
18
19
|
/** Monta o texto do console de deploy (estilo GCP): um bloco por step. */
|
|
@@ -39,12 +40,16 @@ export async function runDeploymentsCreate(opts) {
|
|
|
39
40
|
message: 'Tenant alvo:',
|
|
40
41
|
});
|
|
41
42
|
const version = opts.version ?? (await promptText('Versao:', { json: opts.json }));
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
// bug_7dcb68e2666a43f69ef362d2cbff94ed: POST /api/cli/v1/deployments valida
|
|
44
|
+
// environmentId contra ['dev-local','staging','prod'] por igualdade ESTRITA.
|
|
45
|
+
// Oferecer 'dev' no prompt (e usa-lo como padrao) garantia 400
|
|
46
|
+
// validation_failed. Alfabeto unico em ../lib/envs.js.
|
|
47
|
+
const env = opts.env
|
|
48
|
+
? resolverAmbienteTenant(opts.env, opts.json)
|
|
49
|
+
: (await promptChoice('Ambiente:', [...ESCOLHAS_AMBIENTE_TENANT], {
|
|
50
|
+
default: DEFAULT_TENANT_ENVIRONMENT_ID,
|
|
51
|
+
json: opts.json,
|
|
52
|
+
}));
|
|
48
53
|
const server = await pickServer(token, {
|
|
49
54
|
fromFlag: opts.server,
|
|
50
55
|
status: 'online',
|
|
@@ -7,12 +7,22 @@ import { listProductDatabases, listProducts, listWorkspaces, resolveProductIdent
|
|
|
7
7
|
import { requireToken, handleApiError } from '../lib/cli-read.js';
|
|
8
8
|
import { log } from '../utils/logger.js';
|
|
9
9
|
import { safeExit } from '../utils/safe-exit.js';
|
|
10
|
-
|
|
10
|
+
import { invalidChoiceError } from '../cli-kit/choices.js';
|
|
11
|
+
import { exitWithCliError } from '../lib/cli-error.js';
|
|
12
|
+
/** Ambientes aceitos por `neetru dev sync --env` — validação e mensagem saem daqui. */
|
|
13
|
+
const SYNC_ENVS = ['dev', 'dev-local', 'dev-core'];
|
|
14
|
+
function normalizeEnv(value, json) {
|
|
11
15
|
const raw = (value ?? 'dev').trim().toLowerCase();
|
|
12
|
-
if (raw
|
|
16
|
+
if (SYNC_ENVS.includes(raw))
|
|
13
17
|
return raw;
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
// bug_89d2497e: a lista da mensagem é a MESMA que validou (`SYNC_ENVS`).
|
|
19
|
+
return exitWithCliError(invalidChoiceError({
|
|
20
|
+
flag: '--env',
|
|
21
|
+
value,
|
|
22
|
+
valid: SYNC_ENVS,
|
|
23
|
+
subject: 'Ambiente do dev-local',
|
|
24
|
+
code: 'invalid_env',
|
|
25
|
+
}), json);
|
|
16
26
|
}
|
|
17
27
|
function envMatches(rowEnv, wanted) {
|
|
18
28
|
const env = (rowEnv ?? '').toLowerCase();
|
|
@@ -79,7 +89,7 @@ async function resolveProductForSync(token, opts) {
|
|
|
79
89
|
}
|
|
80
90
|
async function syncFromCore(cwd, opts) {
|
|
81
91
|
const token = await requireToken(opts.json);
|
|
82
|
-
const env = normalizeEnv(opts.env);
|
|
92
|
+
const env = normalizeEnv(opts.env, opts.json);
|
|
83
93
|
const current = resolveDevContext(cwd);
|
|
84
94
|
if (current?.mode === 'prototype' && !opts.product && !opts.productId) {
|
|
85
95
|
log.error('Este projeto está marcado como protótipo local. Use `neetru dev use --product <slug>` para ligar ao Core.');
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `neetru dev up` —
|
|
2
|
+
* `neetru dev up` — banco do produto em container via Docker Compose.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Detecta o banco do projeto em `neetru.config.json`, gera
|
|
5
|
+
* `neetru-compose.dev.yml` e sobe com `docker compose up -d --wait`. O app roda
|
|
6
|
+
* fora do container (`npm run dev`), apontando pra URL que o comando imprime.
|
|
7
|
+
*
|
|
8
|
+
* O agente local saiu em 2026-10-09 (nunca chegou a subir — ver o cabeçalho de
|
|
9
|
+
* `generateComposeDev`); volta quando o agente tiver modo dev de verdade.
|
|
8
10
|
*
|
|
9
11
|
* `runDevExec` (`neetru dev exec`) segue no caminho de emulador Firebase
|
|
10
12
|
* (`firebase emulators:exec`) — é outro comando, pra rodar scripts de CI.
|
package/dist/commands/dev-up.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `neetru dev up` —
|
|
2
|
+
* `neetru dev up` — banco do produto em container via Docker Compose.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Detecta o banco do projeto em `neetru.config.json`, gera
|
|
5
|
+
* `neetru-compose.dev.yml` e sobe com `docker compose up -d --wait`. O app roda
|
|
6
|
+
* fora do container (`npm run dev`), apontando pra URL que o comando imprime.
|
|
7
|
+
*
|
|
8
|
+
* O agente local saiu em 2026-10-09 (nunca chegou a subir — ver o cabeçalho de
|
|
9
|
+
* `generateComposeDev`); volta quando o agente tiver modo dev de verdade.
|
|
8
10
|
*
|
|
9
11
|
* `runDevExec` (`neetru dev exec`) segue no caminho de emulador Firebase
|
|
10
12
|
* (`firebase emulators:exec`) — é outro comando, pra rodar scripts de CI.
|
|
@@ -22,7 +24,7 @@ import { log } from '../utils/logger.js';
|
|
|
22
24
|
import { safeExit, setSafeExitCode } from '../utils/safe-exit.js';
|
|
23
25
|
import { describeDevContext, resolveDevContext, loadLocalProductConfig } from '../lib/dev-local-context.js';
|
|
24
26
|
import { neetruConfigSchema } from '../lib/config-schema.js';
|
|
25
|
-
import { generateComposeDev } from '../lib/compose-generator.js';
|
|
27
|
+
import { generateComposeDev, localDatabaseUrl } from '../lib/compose-generator.js';
|
|
26
28
|
// ---------------------------------------------------------------------------
|
|
27
29
|
// Constantes
|
|
28
30
|
// ---------------------------------------------------------------------------
|
|
@@ -187,20 +189,14 @@ export async function runDevUp(opts = {}, deps = buildDevUpDeps()) {
|
|
|
187
189
|
log.info(' 3. Acesse http://localhost:3000');
|
|
188
190
|
return;
|
|
189
191
|
}
|
|
190
|
-
// ── Produto: gera e sobe Docker Compose com banco
|
|
192
|
+
// ── Produto: gera e sobe Docker Compose com o banco ─────────────────────────
|
|
191
193
|
const localConfig = loadLocalProductConfig(process.cwd());
|
|
192
194
|
const slug = localConfig?.slug ?? 'app';
|
|
193
|
-
log.info(`
|
|
195
|
+
log.info(`Banco local — ${chalk.bold(dbEngine)}`);
|
|
194
196
|
log.dim(` Produto: ${chalk.bold(slug)}`);
|
|
195
|
-
// Gera neetru-compose.dev.yml + Dockerfile.agent
|
|
196
197
|
const composePath = path.join(process.cwd(), 'neetru-compose.dev.yml');
|
|
197
198
|
const appPort = opts.corePort ?? 3000;
|
|
198
|
-
const composeContent = generateComposeDev({
|
|
199
|
-
productSlug: slug,
|
|
200
|
-
dbEngine,
|
|
201
|
-
appPort,
|
|
202
|
-
coreUrl: devContext?.corePath ? `http://localhost:${opts.corePort ?? 9003}` : 'https://core.neetru.com',
|
|
203
|
-
});
|
|
199
|
+
const composeContent = generateComposeDev({ productSlug: slug, dbEngine });
|
|
204
200
|
fsSync.writeFileSync(composePath, composeContent, 'utf8');
|
|
205
201
|
log.info(`Compose gerado: ${chalk.bold('neetru-compose.dev.yml')}`);
|
|
206
202
|
// Sobe com Docker Compose. Detecção cross-platform (antes: paths fixos Unix).
|
|
@@ -213,7 +209,7 @@ export async function runDevUp(opts = {}, deps = buildDevUpDeps()) {
|
|
|
213
209
|
setSafeExitCode(1);
|
|
214
210
|
return;
|
|
215
211
|
}
|
|
216
|
-
const composeSpinner = ora({ text: 'Subindo
|
|
212
|
+
const composeSpinner = ora({ text: 'Subindo o banco…', color: 'blue' }).start();
|
|
217
213
|
try {
|
|
218
214
|
// Docker Compose up — síncrono, espera healthy
|
|
219
215
|
const { execSync } = await import('node:child_process');
|
|
@@ -222,16 +218,21 @@ export async function runDevUp(opts = {}, deps = buildDevUpDeps()) {
|
|
|
222
218
|
stdio: 'pipe',
|
|
223
219
|
timeout: 120_000,
|
|
224
220
|
});
|
|
225
|
-
composeSpinner.succeed('
|
|
221
|
+
composeSpinner.succeed('Banco pronto');
|
|
222
|
+
const dbUrl = localDatabaseUrl(dbEngine);
|
|
226
223
|
log.info('');
|
|
227
|
-
log.info(chalk.bold.green('✓
|
|
228
|
-
log.info(`
|
|
229
|
-
|
|
230
|
-
|
|
224
|
+
log.info(chalk.bold.green('✓ Banco local ativo!'));
|
|
225
|
+
log.info(` Banco: ${dbEngine}`);
|
|
226
|
+
if (dbUrl)
|
|
227
|
+
log.info(` Conexão: ${dbUrl}`);
|
|
228
|
+
log.info('');
|
|
229
|
+
log.info(chalk.bold('Próximos passos:'));
|
|
230
|
+
if (dbUrl)
|
|
231
|
+
log.info(' 1. Use a conexão acima como DATABASE_URL no .env.local');
|
|
232
|
+
log.info(` ${dbUrl ? '2' : '1'}. Rode \`npm run dev\` e acesse http://localhost:${appPort}`);
|
|
231
233
|
log.info('');
|
|
232
234
|
log.dim(' Rode `docker compose -f neetru-compose.dev.yml down` para parar.');
|
|
233
|
-
log.dim(
|
|
234
|
-
log.dim(' O agente vai receber o bundle, npm install, e subir o app automaticamente.');
|
|
235
|
+
log.dim(' O agente local ainda não existe: deploy e migração pelo agente só na VM.');
|
|
235
236
|
}
|
|
236
237
|
catch (err) {
|
|
237
238
|
composeSpinner.fail('Docker Compose falhou.');
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { type CheckResult } from '../lib/product-docs/check.js';
|
|
2
|
+
import { type InitDocsResult } from '../lib/product-docs/ops.js';
|
|
3
|
+
import { type SyncResult } from '../lib/product-docs/sync.js';
|
|
4
|
+
import { NeetruCliError } from '../cli-kit/errors.js';
|
|
5
|
+
export interface DocsInitOptions {
|
|
6
|
+
comApi?: boolean;
|
|
7
|
+
migrar?: boolean;
|
|
8
|
+
dryRun?: boolean;
|
|
9
|
+
json?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function runDocsInit(opts: DocsInitOptions): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Chamado por `neetru init`/`neetru new` depois do scaffold. Nunca derruba o
|
|
14
|
+
* comando chamador: erro vira aviso.
|
|
15
|
+
*/
|
|
16
|
+
export declare function initDocsAfterScaffold(dir: string, opts?: {
|
|
17
|
+
quiet?: boolean;
|
|
18
|
+
}): Promise<InitDocsResult | null>;
|
|
19
|
+
export interface DocsNewOptions {
|
|
20
|
+
categoria?: string;
|
|
21
|
+
subcategoria?: string;
|
|
22
|
+
audience?: string;
|
|
23
|
+
descricao?: string;
|
|
24
|
+
data?: string;
|
|
25
|
+
dryRun?: boolean;
|
|
26
|
+
json?: boolean;
|
|
27
|
+
}
|
|
28
|
+
export declare function runDocsNew(tipo: string, titulo: string, opts: DocsNewOptions): Promise<void>;
|
|
29
|
+
export interface DocsCheckOptions {
|
|
30
|
+
changed?: boolean;
|
|
31
|
+
strict?: boolean;
|
|
32
|
+
offline?: boolean;
|
|
33
|
+
json?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare function runDocsCheck(opts: DocsCheckOptions): Promise<void>;
|
|
36
|
+
export interface DocsSyncOptions {
|
|
37
|
+
dryRun?: boolean;
|
|
38
|
+
offline?: boolean;
|
|
39
|
+
json?: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* As falhas de envio como UM erro tipado com filhos (P105 / bug_89d2497e,
|
|
43
|
+
* etapa 3 aplicada ao `docs sync`). Com docs que deram certo (enviados ou já
|
|
44
|
+
* iguais no Core) a falha se reporta como PARCIAL — no texto ("Falha parcial:
|
|
45
|
+
* 1 de 3 itens falharam") e no JSON (`partial`, `succeeded`, `failed`,
|
|
46
|
+
* `children`) — e sai com o código do filho mais grave. `null` sem falhas.
|
|
47
|
+
*/
|
|
48
|
+
export declare function erroDeSyncComFalhas(r: SyncResult): NeetruCliError | null;
|
|
49
|
+
/** O desfecho do `docs sync`, o MESMO para a saída humana e o `--json`. */
|
|
50
|
+
export interface DesfechoDoSync {
|
|
51
|
+
ok: boolean;
|
|
52
|
+
exit: number;
|
|
53
|
+
/** As falhas de envio como erro tipado, ou `null` sem falhas. */
|
|
54
|
+
erro: NeetruCliError | null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Uma regra só para os dois modos (revisão do #87, BAIXO 2). Qualquer doc que
|
|
58
|
+
* falhou no envio faz o comando falhar com o código do filho mais grave —
|
|
59
|
+
* MESMO que o lote termine num status informativo (`core-sem-suporte` no meio
|
|
60
|
+
* do lote: o doc 1 tomou 500 e o doc 2 tomou 405). Antes a saída humana
|
|
61
|
+
* escondia a falha e saía 0 enquanto o `--json` saía 4. Sem falhas de envio,
|
|
62
|
+
* vale a regra de sempre: status de `SYNC_FAILURE` sai 1, o resto sai 0.
|
|
63
|
+
*/
|
|
64
|
+
export declare function desfechoDoSync(r: SyncResult): DesfechoDoSync;
|
|
65
|
+
export declare function runDocsSync(opts: DocsSyncOptions): Promise<void>;
|
|
66
|
+
export interface DeployDocsPrecheck {
|
|
67
|
+
ran: boolean;
|
|
68
|
+
ok?: boolean;
|
|
69
|
+
counts?: CheckResult['counts'];
|
|
70
|
+
/** Itens do mínimo do primeiro dia / CHANGELOG que faltam (PR002/PR004). */
|
|
71
|
+
diaUm?: string[];
|
|
72
|
+
error?: string;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Antes do deploy: roda `docs check` e SÓ AVISA (decisão do dono 2026-10-03).
|
|
76
|
+
* Nunca lança, nunca encerra o processo.
|
|
77
|
+
*/
|
|
78
|
+
export declare function deployDocsPrecheck(cwd: string, envName: string, json?: boolean): Promise<DeployDocsPrecheck>;
|
|
79
|
+
export interface DeployDocsSync {
|
|
80
|
+
attempted: boolean;
|
|
81
|
+
reason?: string;
|
|
82
|
+
status?: SyncResult['status'];
|
|
83
|
+
message?: string;
|
|
84
|
+
enviados?: number;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Depois de um deploy de PRODUÇÃO bem-sucedido: publica os docs se
|
|
88
|
+
* `docs.publishOnDeploy` estiver ligado. Falha aqui NUNCA derruba o deploy.
|
|
89
|
+
*/
|
|
90
|
+
export declare function deployDocsPostSync(cwd: string, envName: string, json?: boolean): Promise<DeployDocsSync>;
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `neetru docs init | new | check | sync` — documentação do PRODUTO SaaS
|
|
3
|
+
* (PROTOCOLO_DOCS_PRODUTOS_SAAS v1, 2026-10-03).
|
|
4
|
+
*
|
|
5
|
+
* Os outros subcomandos de `neetru docs` (open/publish/list/get/delete) são o
|
|
6
|
+
* control plane dos docs do CORE e ficam em `docs.ts`.
|
|
7
|
+
*
|
|
8
|
+
* Decisões do dono (2026-10-03):
|
|
9
|
+
* - `neetru deploy` só AVISA sobre docs — nunca bloqueia.
|
|
10
|
+
* - Publicar no Core é opcional: `docs.publishOnDeploy: true` (deploy de
|
|
11
|
+
* produção publica no fim) ou manual com `neetru docs sync`.
|
|
12
|
+
* - `docs init --migrar` não move nem renomeia nada.
|
|
13
|
+
*/
|
|
14
|
+
import chalk from 'chalk';
|
|
15
|
+
import { log } from '../utils/logger.js';
|
|
16
|
+
import { safeExit } from '../utils/safe-exit.js';
|
|
17
|
+
import { loadProductRepo, ProductConfigInvalidError, ProductConfigMissingError, } from '../lib/product-docs/config.js';
|
|
18
|
+
import { checkProductDocs } from '../lib/product-docs/check.js';
|
|
19
|
+
import { resolveTaxonomy } from '../lib/product-docs/taxonomy.js';
|
|
20
|
+
import { createProductDoc, DocsNewError, initProductDocs, migrateProductDocs, } from '../lib/product-docs/ops.js';
|
|
21
|
+
import { syncProductDocs } from '../lib/product-docs/sync.js';
|
|
22
|
+
import { NeetruCliError } from '../cli-kit/errors.js';
|
|
23
|
+
import { CliApiError, CliNetworkError } from '../lib/api-client.js';
|
|
24
|
+
import { cliErrorFromApiError } from '../lib/api-error.js';
|
|
25
|
+
import { exitWithCliError } from '../lib/cli-error.js';
|
|
26
|
+
function fail(json, code, message) {
|
|
27
|
+
if (json)
|
|
28
|
+
console.log(JSON.stringify({ ok: false, error: code, message }));
|
|
29
|
+
else
|
|
30
|
+
log.error(message);
|
|
31
|
+
safeExit(1);
|
|
32
|
+
}
|
|
33
|
+
async function loadRepoOrExit(json, cwd = process.cwd()) {
|
|
34
|
+
try {
|
|
35
|
+
return await loadProductRepo(cwd);
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
if (e instanceof ProductConfigMissingError)
|
|
39
|
+
fail(json, 'no_product_config', e.message);
|
|
40
|
+
if (e instanceof ProductConfigInvalidError)
|
|
41
|
+
fail(json, 'invalid_product_config', e.message);
|
|
42
|
+
throw e;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function printInit(r) {
|
|
46
|
+
const verb = r.dryRun ? 'Criaria' : 'Criado';
|
|
47
|
+
if (r.configKeysAdded.length)
|
|
48
|
+
log.success(`neetru.config.json: chave docs (${r.configKeysAdded.join(', ')})${r.dryRun ? ' — simulação' : ''}.`);
|
|
49
|
+
if (r.folders.length)
|
|
50
|
+
log.dim(` ${r.dryRun ? 'Pastas que seriam criadas' : 'Pastas criadas'}: ${r.folders.length}`);
|
|
51
|
+
for (const f of r.created)
|
|
52
|
+
log.success(`${verb}: ${f}`);
|
|
53
|
+
if (r.skipped.length)
|
|
54
|
+
log.dim(` Já existiam (não mexi): ${r.skipped.join(', ')}`);
|
|
55
|
+
if (!r.created.length && !r.folders.length && !r.configKeysAdded.length)
|
|
56
|
+
log.info('Nada a fazer: o mínimo do primeiro dia já existe.');
|
|
57
|
+
if (!r.withApi)
|
|
58
|
+
log.dim(' API_V1.md só entra com --com-api (ou quando o produto declara app móvel/integração).');
|
|
59
|
+
}
|
|
60
|
+
export async function runDocsInit(opts) {
|
|
61
|
+
const repo = await loadRepoOrExit(opts.json);
|
|
62
|
+
if (opts.migrar) {
|
|
63
|
+
const { taxonomy } = await resolveTaxonomy({ offline: true });
|
|
64
|
+
const r = await migrateProductDocs(repo, { dryRun: opts.dryRun, taxonomy });
|
|
65
|
+
if (opts.json) {
|
|
66
|
+
console.log(JSON.stringify({ ok: true, mode: 'migrar', ...r }));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
log.heading(`Migração dos docs de ${repo.slug}${r.dryRun ? ' (simulação — nada foi escrito)' : ''}`);
|
|
70
|
+
const added = r.files.filter((f) => f.acao === 'frontmatter-adicionado');
|
|
71
|
+
log.info(`${r.files.length} arquivo(s) analisado(s); frontmatter ${r.dryRun ? 'seria acrescentado' : 'acrescentado'} em ${added.length}.`);
|
|
72
|
+
const kept = r.files.filter((f) => f.acao !== 'frontmatter-adicionado');
|
|
73
|
+
if (kept.length)
|
|
74
|
+
log.dim(` ${kept.length} já tinham frontmatter e ficaram intocados.`);
|
|
75
|
+
const low = added.reduce((n, f) => n + f.inferencias.filter((i) => i.confianca === 'baixa').length, 0);
|
|
76
|
+
if (low)
|
|
77
|
+
log.warn(`${low} inferência(s) de confiança baixa — revise no relatório.`);
|
|
78
|
+
const moves = r.files.filter((f) => f.sugestaoDestino).length;
|
|
79
|
+
if (moves)
|
|
80
|
+
log.info(`${moves} arquivo(s) com sugestão de novo lugar (nada foi movido).`);
|
|
81
|
+
if (r.legacyNamesFile) {
|
|
82
|
+
log.info(r.legacyNamesFile.created
|
|
83
|
+
? `${r.legacyNamesFile.path}: ${r.legacyNamesFile.count} nome(s) fora da regra tolerados.`
|
|
84
|
+
: `${r.legacyNamesFile.path} já existia — não foi alterado.`);
|
|
85
|
+
}
|
|
86
|
+
if (r.reportPath)
|
|
87
|
+
log.success(`Relatório: ${r.reportPath}`);
|
|
88
|
+
if (r.faltandoPrimeiroDia.length) {
|
|
89
|
+
log.dim(` Falta do mínimo do primeiro dia: ${r.faltandoPrimeiroDia.join(', ')}.`);
|
|
90
|
+
log.dim(' Depois de mover os arquivos, rode `neetru docs init` (sem --migrar) para criar o que falta.');
|
|
91
|
+
}
|
|
92
|
+
log.dim(' Próximo passo: revisar o relatório, mover num PR à parte, `neetru docs check`.');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const r = await initProductDocs(repo, { comApi: opts.comApi, dryRun: opts.dryRun });
|
|
96
|
+
if (opts.json) {
|
|
97
|
+
console.log(JSON.stringify({ ok: true, mode: 'init', ...r }));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
log.heading(`Docs de ${repo.slug}${r.dryRun ? ' (simulação — nada foi escrito)' : ''}`);
|
|
101
|
+
printInit(r);
|
|
102
|
+
log.dim(' Próximos passos: completar os rascunhos, `neetru docs check`.');
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Chamado por `neetru init`/`neetru new` depois do scaffold. Nunca derruba o
|
|
106
|
+
* comando chamador: erro vira aviso.
|
|
107
|
+
*/
|
|
108
|
+
export async function initDocsAfterScaffold(dir, opts = {}) {
|
|
109
|
+
try {
|
|
110
|
+
const repo = await loadProductRepo(dir);
|
|
111
|
+
const r = await initProductDocs(repo);
|
|
112
|
+
if (!opts.quiet) {
|
|
113
|
+
log.info(`Docs: ${r.created.length} arquivo(s) do mínimo do primeiro dia criados em ${r.docsRoot}/ (\`neetru docs check\` confere).`);
|
|
114
|
+
}
|
|
115
|
+
return r;
|
|
116
|
+
}
|
|
117
|
+
catch (e) {
|
|
118
|
+
if (!opts.quiet)
|
|
119
|
+
log.warn(`Não deu para criar os docs (${e.message}). Rode \`neetru docs init\` depois.`);
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
export async function runDocsNew(tipo, titulo, opts) {
|
|
124
|
+
const repo = await loadRepoOrExit(opts.json);
|
|
125
|
+
try {
|
|
126
|
+
const r = await createProductDoc(repo, tipo, titulo, opts);
|
|
127
|
+
if (opts.json) {
|
|
128
|
+
const { content: _content, ...rest } = r;
|
|
129
|
+
console.log(JSON.stringify({ ok: true, ...rest }));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
log.success(`${r.dryRun ? 'Criaria' : 'Criado'}: ${chalk.bold(r.path)}`);
|
|
133
|
+
log.dim(` tipo=${r.type} · ${r.category}/${r.subcategory} · audience=${r.audience} · status=${r.status}`);
|
|
134
|
+
if (r.categoriaPadrao)
|
|
135
|
+
log.dim(' Categoria padrão da pasta — ajuste com --categoria/--subcategoria se o assunto for outro.');
|
|
136
|
+
if (!opts.descricao)
|
|
137
|
+
log.dim(' Troque a `description` provisória por uma frase sua (ou use --descricao).');
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
if (e instanceof DocsNewError)
|
|
141
|
+
fail(opts.json, e.code, e.message);
|
|
142
|
+
throw e;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const LEVEL_LABEL = { erro: 'erro', aviso: 'aviso', sugestao: 'sugestão' };
|
|
146
|
+
function printFindings(res, maxPerLevel = Infinity) {
|
|
147
|
+
const byFile = new Map();
|
|
148
|
+
for (const f of res.findings) {
|
|
149
|
+
if (!byFile.has(f.file))
|
|
150
|
+
byFile.set(f.file, []);
|
|
151
|
+
byFile.get(f.file).push(f);
|
|
152
|
+
}
|
|
153
|
+
let shown = 0;
|
|
154
|
+
for (const [file, list] of byFile) {
|
|
155
|
+
if (shown >= maxPerLevel)
|
|
156
|
+
break;
|
|
157
|
+
console.log(`\n ${chalk.bold(file)}`);
|
|
158
|
+
for (const f of list) {
|
|
159
|
+
const color = f.level === 'erro' ? chalk.red : f.level === 'aviso' ? chalk.yellow : chalk.dim;
|
|
160
|
+
console.log(` ${color(`${f.code} ${LEVEL_LABEL[f.level]}`)}${f.line ? chalk.dim(` :${f.line}`) : ''} ${f.message}`);
|
|
161
|
+
shown++;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
export async function runDocsCheck(opts) {
|
|
166
|
+
const repo = await loadRepoOrExit(opts.json);
|
|
167
|
+
const tax = await resolveTaxonomy({ offline: opts.offline });
|
|
168
|
+
const res = await checkProductDocs(repo, { changed: opts.changed, strict: opts.strict, taxonomy: tax.taxonomy });
|
|
169
|
+
if (opts.json) {
|
|
170
|
+
console.log(JSON.stringify({
|
|
171
|
+
ok: res.ok,
|
|
172
|
+
product: repo.slug,
|
|
173
|
+
counts: res.counts,
|
|
174
|
+
filesChecked: res.filesChecked,
|
|
175
|
+
changed: !!opts.changed,
|
|
176
|
+
changedBase: res.changedBase ?? null,
|
|
177
|
+
changedFallback: !!res.changedFallback,
|
|
178
|
+
strict: !!opts.strict,
|
|
179
|
+
taxonomy: { source: tax.source, protocolVersion: tax.taxonomy.protocolVersion, aviso: tax.aviso ?? null },
|
|
180
|
+
findings: res.findings,
|
|
181
|
+
}));
|
|
182
|
+
if (!res.ok)
|
|
183
|
+
safeExit(1);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
log.heading(`Docs de ${repo.slug}${opts.changed ? ' (só os alterados)' : ''}`);
|
|
187
|
+
if (tax.aviso)
|
|
188
|
+
log.warn(tax.aviso);
|
|
189
|
+
if (res.changedFallback)
|
|
190
|
+
log.warn('Não deu para descobrir os arquivos alterados (sem git ou sem a branch principal) — chequei todos.');
|
|
191
|
+
if (res.findings.length)
|
|
192
|
+
printFindings(res);
|
|
193
|
+
console.log();
|
|
194
|
+
const summary = `${res.filesChecked} doc(s) · ${res.counts.erro} erro(s) · ${res.counts.aviso} aviso(s) · ${res.counts.sugestao} sugestão(ões)`;
|
|
195
|
+
log.dim(` taxonomia ${tax.source === 'core' ? 'do Core' : 'embutida'} (protocolo v${tax.taxonomy.protocolVersion})`);
|
|
196
|
+
if (res.ok) {
|
|
197
|
+
log.success(summary);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
log.error(summary + (opts.strict && res.counts.erro === 0 ? ' — --strict trata aviso como erro' : ''));
|
|
201
|
+
safeExit(1);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function printSync(r) {
|
|
205
|
+
for (const e of r.enviados) {
|
|
206
|
+
log.success(`${r.dryRun ? 'Enviaria' : 'Enviado'}: ${e.slug}${e.version ? ` (v${e.version})` : ''}${e.visibility === 'public' ? chalk.dim(' · público') : ''}`);
|
|
207
|
+
}
|
|
208
|
+
if (r.inalterados.length)
|
|
209
|
+
log.dim(` Sem mudança: ${r.inalterados.length}`);
|
|
210
|
+
for (const b of r.bloqueados)
|
|
211
|
+
log.warn(`Não enviado (erro no check: ${b.codes.join(', ')}): ${b.file}`);
|
|
212
|
+
for (const x of r.recusados)
|
|
213
|
+
log.warn(`Não pode subir: ${x.file} — ${x.motivo}.`);
|
|
214
|
+
// As falhas de envio saem no erro tipado (`erroDeSyncComFalhas`), uma vez só.
|
|
215
|
+
if (r.orfaos.length) {
|
|
216
|
+
log.warn(`${r.orfaos.length} doc(s) existem no Core mas não no repositório (nada foi apagado): ${r.orfaos.join(', ')}`);
|
|
217
|
+
log.dim(' A remoção de docs de produto ainda não existe no Core — eles continuam lá até o Core oferecer isso.');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/** Status de sync que contam como falha do comando `neetru docs sync`. */
|
|
221
|
+
const SYNC_FAILURE = ['parcial', 'sem-login', 'limite-excedido', 'produto-nao-encontrado', 'falha'];
|
|
222
|
+
/**
|
|
223
|
+
* Código de saída de um doc que o Core recusou: a MESMA tabela do
|
|
224
|
+
* `handleApiError` (401→2, 403→3, outro 4xx→1, 5xx e sem resposta→4), lida do
|
|
225
|
+
* próprio `cliErrorFromApiError` em vez de copiada.
|
|
226
|
+
*/
|
|
227
|
+
function exitDaFalha(httpStatus) {
|
|
228
|
+
const erro = httpStatus > 0 ? new CliApiError(httpStatus, 'docs_sync', '') : new CliNetworkError('');
|
|
229
|
+
return cliErrorFromApiError(erro).exit;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* As falhas de envio como UM erro tipado com filhos (P105 / bug_89d2497e,
|
|
233
|
+
* etapa 3 aplicada ao `docs sync`). Com docs que deram certo (enviados ou já
|
|
234
|
+
* iguais no Core) a falha se reporta como PARCIAL — no texto ("Falha parcial:
|
|
235
|
+
* 1 de 3 itens falharam") e no JSON (`partial`, `succeeded`, `failed`,
|
|
236
|
+
* `children`) — e sai com o código do filho mais grave. `null` sem falhas.
|
|
237
|
+
*/
|
|
238
|
+
export function erroDeSyncComFalhas(r) {
|
|
239
|
+
if (r.falhas.length === 0)
|
|
240
|
+
return null;
|
|
241
|
+
const children = r.falhas.map((f) => new NeetruCliError(f.message, {
|
|
242
|
+
code: f.httpStatus > 0 ? `http_${f.httpStatus}` : 'sem_resposta',
|
|
243
|
+
exit: exitDaFalha(f.httpStatus),
|
|
244
|
+
details: { slug: f.slug, httpStatus: f.httpStatus },
|
|
245
|
+
human: `Falha em ${f.slug}: ${f.message}`,
|
|
246
|
+
}));
|
|
247
|
+
return new NeetruCliError(r.message, {
|
|
248
|
+
code: r.status === 'parcial' ? 'docs_sync_parcial' : 'docs_sync_falha',
|
|
249
|
+
children,
|
|
250
|
+
succeeded: r.enviados.length + r.inalterados.length,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Uma regra só para os dois modos (revisão do #87, BAIXO 2). Qualquer doc que
|
|
255
|
+
* falhou no envio faz o comando falhar com o código do filho mais grave —
|
|
256
|
+
* MESMO que o lote termine num status informativo (`core-sem-suporte` no meio
|
|
257
|
+
* do lote: o doc 1 tomou 500 e o doc 2 tomou 405). Antes a saída humana
|
|
258
|
+
* escondia a falha e saía 0 enquanto o `--json` saía 4. Sem falhas de envio,
|
|
259
|
+
* vale a regra de sempre: status de `SYNC_FAILURE` sai 1, o resto sai 0.
|
|
260
|
+
*/
|
|
261
|
+
export function desfechoDoSync(r) {
|
|
262
|
+
const erro = erroDeSyncComFalhas(r);
|
|
263
|
+
if (erro)
|
|
264
|
+
return { ok: false, exit: erro.exit, erro };
|
|
265
|
+
const failed = SYNC_FAILURE.includes(r.status);
|
|
266
|
+
return { ok: !failed, exit: failed ? 1 : 0, erro: null };
|
|
267
|
+
}
|
|
268
|
+
export async function runDocsSync(opts) {
|
|
269
|
+
const repo = await loadRepoOrExit(opts.json);
|
|
270
|
+
const tax = await resolveTaxonomy({ offline: opts.offline });
|
|
271
|
+
const r = await syncProductDocs(repo, {
|
|
272
|
+
taxonomy: tax.taxonomy,
|
|
273
|
+
dryRun: opts.dryRun,
|
|
274
|
+
onWait: opts.json
|
|
275
|
+
? undefined
|
|
276
|
+
: (ms, motivo) => log.dim(` ${motivo === 'ritmo' ? 'Respeitando o limite de envios do Core' : 'O Core pediu para esperar'}: aguardando ${Math.ceil(ms / 1000)} s…`),
|
|
277
|
+
});
|
|
278
|
+
const desfecho = desfechoDoSync(r);
|
|
279
|
+
if (opts.json) {
|
|
280
|
+
// O resultado inteiro de antes e, com falhas de envio, o agregado do erro
|
|
281
|
+
// tipado (`error`, `partial`, `succeeded`, `failed`, `children`).
|
|
282
|
+
let agregado = {};
|
|
283
|
+
if (desfecho.erro) {
|
|
284
|
+
const { ok: _ok, ...resto } = desfecho.erro.toJSON();
|
|
285
|
+
agregado = resto;
|
|
286
|
+
}
|
|
287
|
+
console.log(JSON.stringify({ ok: desfecho.ok, ...r, ...agregado }));
|
|
288
|
+
if (desfecho.exit !== 0)
|
|
289
|
+
safeExit(desfecho.exit);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
log.heading(`Publicar docs de ${repo.slug} no Core${r.dryRun ? ' (simulação)' : ''}`);
|
|
293
|
+
printSync(r);
|
|
294
|
+
if (desfecho.erro) {
|
|
295
|
+
// O cabeçalho é a mensagem do status (inclusive o "Core sem suporte"); as
|
|
296
|
+
// falhas de envio vêm logo abaixo, uma por doc — nunca somem.
|
|
297
|
+
exitWithCliError(desfecho.erro, false);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
if (r.status === 'core-sem-suporte' || r.status === 'desligado' || r.status === 'nada-a-enviar') {
|
|
301
|
+
log.info(r.message);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (desfecho.exit !== 0) {
|
|
305
|
+
log.error(r.message);
|
|
306
|
+
safeExit(desfecho.exit);
|
|
307
|
+
}
|
|
308
|
+
log.success(r.message);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Antes do deploy: roda `docs check` e SÓ AVISA (decisão do dono 2026-10-03).
|
|
312
|
+
* Nunca lança, nunca encerra o processo.
|
|
313
|
+
*/
|
|
314
|
+
export async function deployDocsPrecheck(cwd, envName, json) {
|
|
315
|
+
let repo;
|
|
316
|
+
try {
|
|
317
|
+
repo = await loadProductRepo(cwd);
|
|
318
|
+
}
|
|
319
|
+
catch {
|
|
320
|
+
return { ran: false };
|
|
321
|
+
}
|
|
322
|
+
try {
|
|
323
|
+
const tax = await resolveTaxonomy({ offline: true });
|
|
324
|
+
const res = await checkProductDocs(repo, { taxonomy: tax.taxonomy });
|
|
325
|
+
const diaUm = res.findings
|
|
326
|
+
.filter((f) => (f.code === 'PR002' || f.code === 'PR004') && f.level !== 'sugestao')
|
|
327
|
+
.map((f) => f.message);
|
|
328
|
+
if (!json) {
|
|
329
|
+
const prod = envName === 'prod' || envName === 'production';
|
|
330
|
+
if (res.counts.erro || res.counts.aviso) {
|
|
331
|
+
log.warn(`Docs: ${res.counts.erro} erro(s) e ${res.counts.aviso} aviso(s) — o deploy segue; rode \`neetru docs check\` para ver.`);
|
|
332
|
+
if (prod && diaUm.length) {
|
|
333
|
+
log.warn('Docs: falta o mínimo do primeiro dia para produção:');
|
|
334
|
+
for (const m of diaUm.slice(0, 6))
|
|
335
|
+
log.dim(` · ${m}`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
log.dim(' Docs: check sem erros.');
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return { ran: true, ok: res.ok, counts: res.counts, diaUm };
|
|
343
|
+
}
|
|
344
|
+
catch (e) {
|
|
345
|
+
return { ran: false, error: e.message };
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Depois de um deploy de PRODUÇÃO bem-sucedido: publica os docs se
|
|
350
|
+
* `docs.publishOnDeploy` estiver ligado. Falha aqui NUNCA derruba o deploy.
|
|
351
|
+
*/
|
|
352
|
+
export async function deployDocsPostSync(cwd, envName, json) {
|
|
353
|
+
const prod = envName === 'prod' || envName === 'production';
|
|
354
|
+
if (!prod)
|
|
355
|
+
return { attempted: false, reason: 'nao-producao' };
|
|
356
|
+
let repo;
|
|
357
|
+
try {
|
|
358
|
+
repo = await loadProductRepo(cwd);
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
return { attempted: false, reason: 'sem-config' };
|
|
362
|
+
}
|
|
363
|
+
if (!repo.docs.publishOnDeploy)
|
|
364
|
+
return { attempted: false, reason: 'publishOnDeploy-desligado' };
|
|
365
|
+
try {
|
|
366
|
+
const tax = await resolveTaxonomy({ offline: true });
|
|
367
|
+
const r = await syncProductDocs(repo, { taxonomy: tax.taxonomy });
|
|
368
|
+
if (!json) {
|
|
369
|
+
if (r.status === 'ok')
|
|
370
|
+
log.success(`Docs publicados no Core: ${r.message}`);
|
|
371
|
+
else if (r.status === 'core-sem-suporte' || r.status === 'nada-a-enviar' || r.status === 'desligado')
|
|
372
|
+
log.info(`Docs: ${r.message}`);
|
|
373
|
+
else
|
|
374
|
+
log.warn(`Docs não publicados (${r.message}) — o deploy está OK; tente \`neetru docs sync\`.`);
|
|
375
|
+
}
|
|
376
|
+
return { attempted: true, status: r.status, message: r.message, enviados: r.enviados.length };
|
|
377
|
+
}
|
|
378
|
+
catch (e) {
|
|
379
|
+
if (!json)
|
|
380
|
+
log.warn(`Docs não publicados (${e.message}) — o deploy está OK; tente \`neetru docs sync\`.`);
|
|
381
|
+
return { attempted: true, status: 'falha', message: e.message };
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
//# sourceMappingURL=docs-produto.js.map
|