@neetru/cli 2.28.7 → 2.28.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +25 -1
  3. package/dist/commands/admin/oidc-login.d.ts +18 -0
  4. package/dist/commands/admin/oidc-login.js +68 -3
  5. package/dist/commands/admin/oidc-login.js.map +1 -1
  6. package/dist/commands/admin/sdk-credential.js +11 -6
  7. package/dist/commands/admin/sdk-credential.js.map +1 -1
  8. package/dist/commands/agent-release.d.ts +2 -0
  9. package/dist/commands/agent-release.js +41 -29
  10. package/dist/commands/agent-release.js.map +1 -1
  11. package/dist/commands/agent-write.d.ts +1 -0
  12. package/dist/commands/agent-write.js +4 -0
  13. package/dist/commands/agent-write.js.map +1 -1
  14. package/dist/commands/ai-models.js +9 -4
  15. package/dist/commands/ai-models.js.map +1 -1
  16. package/dist/commands/archive.js +9 -2
  17. package/dist/commands/archive.js.map +1 -1
  18. package/dist/commands/auth.js +17 -2
  19. package/dist/commands/auth.js.map +1 -1
  20. package/dist/commands/autocomplete.d.ts +1 -1
  21. package/dist/commands/autocomplete.js +2 -0
  22. package/dist/commands/autocomplete.js.map +1 -1
  23. package/dist/commands/chat-menu.js +20 -6
  24. package/dist/commands/chat-menu.js.map +1 -1
  25. package/dist/commands/chat.d.ts +14 -0
  26. package/dist/commands/chat.js +32 -4
  27. package/dist/commands/chat.js.map +1 -1
  28. package/dist/commands/config.js +7 -0
  29. package/dist/commands/config.js.map +1 -1
  30. package/dist/commands/db-move.d.ts +204 -0
  31. package/dist/commands/db-move.js +890 -0
  32. package/dist/commands/db-move.js.map +1 -0
  33. package/dist/commands/db.d.ts +14 -3
  34. package/dist/commands/db.js +102 -67
  35. package/dist/commands/db.js.map +1 -1
  36. package/dist/commands/deploy.d.ts +15 -0
  37. package/dist/commands/deploy.js +42 -1
  38. package/dist/commands/deploy.js.map +1 -1
  39. package/dist/commands/dev-up.d.ts +5 -0
  40. package/dist/commands/dev-up.js +29 -4
  41. package/dist/commands/dev-up.js.map +1 -1
  42. package/dist/commands/doctor.js +12 -6
  43. package/dist/commands/doctor.js.map +1 -1
  44. package/dist/commands/domains.js +48 -36
  45. package/dist/commands/domains.js.map +1 -1
  46. package/dist/commands/init.d.ts +0 -8
  47. package/dist/commands/init.js +14 -3
  48. package/dist/commands/init.js.map +1 -1
  49. package/dist/commands/logs.js +18 -21
  50. package/dist/commands/logs.js.map +1 -1
  51. package/dist/commands/mobile.d.ts +26 -0
  52. package/dist/commands/mobile.js +344 -0
  53. package/dist/commands/mobile.js.map +1 -0
  54. package/dist/commands/ops.d.ts +20 -0
  55. package/dist/commands/ops.js +70 -17
  56. package/dist/commands/ops.js.map +1 -1
  57. package/dist/commands/products-db.d.ts +1 -0
  58. package/dist/commands/products-db.js +15 -15
  59. package/dist/commands/products-db.js.map +1 -1
  60. package/dist/commands/promote.js +13 -32
  61. package/dist/commands/promote.js.map +1 -1
  62. package/dist/commands/schema.d.ts +4 -0
  63. package/dist/commands/schema.js +25 -16
  64. package/dist/commands/schema.js.map +1 -1
  65. package/dist/commands/servers.d.ts +2 -3
  66. package/dist/commands/servers.js +4 -7
  67. package/dist/commands/servers.js.map +1 -1
  68. package/dist/commands/smoke.d.ts +123 -0
  69. package/dist/commands/smoke.js +529 -0
  70. package/dist/commands/smoke.js.map +1 -0
  71. package/dist/commands/vm-memory.d.ts +53 -0
  72. package/dist/commands/vm-memory.js +307 -0
  73. package/dist/commands/vm-memory.js.map +1 -0
  74. package/dist/commands/vm.d.ts +2 -0
  75. package/dist/commands/vm.js +8 -0
  76. package/dist/commands/vm.js.map +1 -1
  77. package/dist/commands/webhooks.d.ts +2 -0
  78. package/dist/commands/webhooks.js +7 -0
  79. package/dist/commands/webhooks.js.map +1 -1
  80. package/dist/index.js +403 -21
  81. package/dist/index.js.map +1 -1
  82. package/dist/lib/agent-artifact-url.d.ts +19 -0
  83. package/dist/lib/agent-artifact-url.js +48 -0
  84. package/dist/lib/agent-artifact-url.js.map +1 -0
  85. package/dist/lib/api-client.d.ts +11 -3
  86. package/dist/lib/api-client.js +55 -24
  87. package/dist/lib/api-client.js.map +1 -1
  88. package/dist/lib/api-error.js +32 -1
  89. package/dist/lib/api-error.js.map +1 -1
  90. package/dist/lib/auth.d.ts +2 -9
  91. package/dist/lib/auth.js +34 -18
  92. package/dist/lib/auth.js.map +1 -1
  93. package/dist/lib/cli-write.d.ts +31 -0
  94. package/dist/lib/cli-write.js +51 -2
  95. package/dist/lib/cli-write.js.map +1 -1
  96. package/dist/lib/mobile-local/index.d.ts +7 -0
  97. package/dist/lib/mobile-local/index.js +7 -0
  98. package/dist/lib/mobile-local/index.js.map +1 -0
  99. package/dist/lib/mobile-local/mobile-json.d.ts +57 -0
  100. package/dist/lib/mobile-local/mobile-json.js +169 -0
  101. package/dist/lib/mobile-local/mobile-json.js.map +1 -0
  102. package/dist/lib/product-config.d.ts +5 -0
  103. package/dist/lib/product-config.js +37 -0
  104. package/dist/lib/product-config.js.map +1 -0
  105. package/dist/lib/telemetry.d.ts +7 -0
  106. package/dist/lib/telemetry.js +9 -0
  107. package/dist/lib/telemetry.js.map +1 -1
  108. package/package.json +1 -1
@@ -0,0 +1,344 @@
1
+ /**
2
+ * `neetru mobile` — gerenciamento de Apps Mobile (iOS/Android) do produto via CLI.
3
+ *
4
+ * Subcomandos:
5
+ * neetru mobile init --platform <ios|android> --redirect-uri <url> [...] — registra um app
6
+ * neetru mobile list — lista apps do produto
7
+ * neetru mobile revoke <appId> — revoga (mata sessões ativas)
8
+ *
9
+ * Contrato Core (PR #482 do neetru-core — dev-facing, self-service, igual `db`/`domains`):
10
+ * POST /api/cli/v1/products/{slug}/mobile-apps — role admin|operator
11
+ * GET /api/cli/v1/products/{slug}/mobile-apps — role admin|operator
12
+ * DELETE /api/cli/v1/products/{slug}/mobile-apps/{appId} — role admin ONLY + step-up MFA
13
+ *
14
+ * `redirectUri` PRECISA ser `https://` (Universal Link/App Link, RFC 8252) —
15
+ * o Core rejeita custom scheme (`meuapp://`) com 400. Isso é decisão de
16
+ * segurança do owner, não limitação temporária — a CLI valida o mesmo
17
+ * localmente pra falhar rápido com mensagem clara, mas NUNCA oferece
18
+ * custom scheme como alternativa.
19
+ *
20
+ * O `id` retornado pelo Core é `<slug>__<platform>` — o app é PKCE público
21
+ * (Authorization Code + PKCE, sem client secret).
22
+ *
23
+ * Todos os comandos usam `apiRequest` do `api-client` — autenticação via
24
+ * Bearer token resolvido automaticamente do config local (ou explícito via
25
+ * `requireToken` no fluxo destrutivo, mesmo padrão de `servers.ts`).
26
+ *
27
+ * Plain PT-BR em todas as strings visíveis ao usuário.
28
+ */
29
+ import * as path from 'node:path';
30
+ import chalk from 'chalk';
31
+ import ora from 'ora';
32
+ import { log } from '../utils/logger.js';
33
+ import { apiRequest } from '../lib/api-client.js';
34
+ import { handleApiError } from '../lib/api-error.js';
35
+ import { renderTable } from '../lib/render.js';
36
+ import { requireToken, confirmDestructive, resolveStepUp, printWriteResult, } from '../lib/cli-write.js';
37
+ import { loadProductConfig } from '../lib/product-config.js';
38
+ import { readMobileJson, writeMobileJson, upsertMobileApp, removeMobileApp, } from '../lib/mobile-local/index.js';
39
+ import { safeExit } from '../utils/safe-exit.js';
40
+ /** Caminho canônico do registro local de apps mobile. */
41
+ const MOBILE_JSON_REL = '.neetru/mobile.json';
42
+ function jsonError(message, error = 'error') {
43
+ console.log(JSON.stringify({ ok: false, error, message }));
44
+ }
45
+ function mobileStatusColor(status) {
46
+ switch (status) {
47
+ case 'active': return chalk.green(status);
48
+ case 'revoked':
49
+ case 'inactive': return chalk.red(status);
50
+ case 'error': return chalk.red(status);
51
+ default: return chalk.dim(status);
52
+ }
53
+ }
54
+ export async function runMobileInit(opts = {}) {
55
+ if (!opts.json) {
56
+ log.banner();
57
+ log.heading('neetru mobile init');
58
+ }
59
+ // ── platform ──────────────────────────────────────────────────────────
60
+ const platform = opts.platform;
61
+ if (platform !== 'ios' && platform !== 'android') {
62
+ const msg = `--platform é obrigatório e deve ser "ios" ou "android" (recebido: ${opts.platform ?? '(nenhum)'}).`;
63
+ if (opts.json) {
64
+ jsonError(msg, 'invalid_platform');
65
+ }
66
+ else {
67
+ log.error(msg);
68
+ }
69
+ safeExit(1);
70
+ return;
71
+ }
72
+ // ── redirectUri — SEMPRE https:// (RFC 8252), nunca custom scheme ──────
73
+ const redirectUri = (opts.redirectUri ?? '').trim();
74
+ if (!redirectUri) {
75
+ const msg = '--redirect-uri é obrigatório (ex: https://meuproduto.com/auth/callback).';
76
+ if (opts.json) {
77
+ jsonError(msg, 'redirect_uri_required');
78
+ }
79
+ else {
80
+ log.error(msg);
81
+ }
82
+ safeExit(1);
83
+ return;
84
+ }
85
+ if (!redirectUri.startsWith('https://')) {
86
+ const msg = `--redirect-uri precisa ser https:// (Universal Link/App Link). ` +
87
+ `Custom scheme (ex: meuapp://) NÃO é aceito — decisão de segurança do owner (RFC 8252), ` +
88
+ `não é limitação temporária. Recebido: "${redirectUri}"`;
89
+ if (opts.json) {
90
+ jsonError(msg, 'redirect_uri_not_https');
91
+ }
92
+ else {
93
+ log.error(msg);
94
+ }
95
+ safeExit(1);
96
+ return;
97
+ }
98
+ // ── campos condicionais por plataforma ──────────────────────────────────
99
+ let bundleId;
100
+ let teamId;
101
+ let packageName;
102
+ let shaCertFingerprints;
103
+ if (platform === 'ios') {
104
+ bundleId = opts.bundleId?.trim() || undefined;
105
+ teamId = opts.teamId?.trim() || undefined;
106
+ if (!bundleId) {
107
+ const msg = '--bundle-id é obrigatório para --platform ios (ex: com.empresa.app).';
108
+ if (opts.json) {
109
+ jsonError(msg, 'bundle_id_required');
110
+ }
111
+ else {
112
+ log.error(msg);
113
+ }
114
+ safeExit(1);
115
+ return;
116
+ }
117
+ if (!teamId) {
118
+ const msg = '--team-id é obrigatório para --platform ios (Apple Team ID, 10 caracteres).';
119
+ if (opts.json) {
120
+ jsonError(msg, 'team_id_required');
121
+ }
122
+ else {
123
+ log.error(msg);
124
+ }
125
+ safeExit(1);
126
+ return;
127
+ }
128
+ }
129
+ else {
130
+ packageName = opts.packageName?.trim() || undefined;
131
+ shaCertFingerprints = (opts.shaFingerprints ?? []).map((f) => f.trim()).filter(Boolean);
132
+ if (!packageName) {
133
+ const msg = '--package-name é obrigatório para --platform android (ex: com.empresa.app).';
134
+ if (opts.json) {
135
+ jsonError(msg, 'package_name_required');
136
+ }
137
+ else {
138
+ log.error(msg);
139
+ }
140
+ safeExit(1);
141
+ return;
142
+ }
143
+ if (shaCertFingerprints.length === 0) {
144
+ const msg = '--sha-fingerprint é obrigatório para --platform android (repetível — um por certificado de assinatura).';
145
+ if (opts.json) {
146
+ jsonError(msg, 'sha_fingerprint_required');
147
+ }
148
+ else {
149
+ log.error(msg);
150
+ }
151
+ safeExit(1);
152
+ return;
153
+ }
154
+ }
155
+ // ── slug do produto atual ────────────────────────────────────────────────
156
+ const cfg = await loadProductConfig();
157
+ if (!cfg) {
158
+ const msg = 'neetru.config.json não encontrado. Rode `neetru init` primeiro.';
159
+ if (opts.json) {
160
+ jsonError(msg, 'config_not_found');
161
+ }
162
+ else {
163
+ log.error(msg);
164
+ }
165
+ safeExit(1);
166
+ return;
167
+ }
168
+ const body = { platform, redirectUri };
169
+ if (opts.name)
170
+ body.name = opts.name;
171
+ if (platform === 'ios') {
172
+ body.bundleId = bundleId;
173
+ body.teamId = teamId;
174
+ }
175
+ else {
176
+ body.packageName = packageName;
177
+ body.shaCertFingerprints = shaCertFingerprints;
178
+ }
179
+ // ── registra no Core PRIMEIRO — só grava o stub local DEPOIS do sucesso.
180
+ // Gravar localmente antes e deixar o registro falhar resultaria em entrada
181
+ // órfã em .neetru/mobile.json (mesmo raciocínio de HIGH-3 em `db init`).
182
+ const spinner = opts.json
183
+ ? null
184
+ : ora({ text: `Registrando app mobile (${platform}) no Core…`, color: 'blue' }).start();
185
+ let resp;
186
+ try {
187
+ resp = await apiRequest(`/api/cli/v1/products/${encodeURIComponent(cfg.slug)}/mobile-apps`, { method: 'POST', body });
188
+ spinner?.succeed('App mobile registrado no Core.');
189
+ }
190
+ catch (err) {
191
+ spinner?.fail('Falha ao registrar app mobile no Core.');
192
+ handleApiError(err, opts.json);
193
+ return; // handleApiError nunca retorna mas TS não sabe disso
194
+ }
195
+ // ── grava .neetru/mobile.json somente após sucesso do Core ───────────────
196
+ const mobileJsonPath = path.resolve(process.cwd(), MOBILE_JSON_REL);
197
+ const mobileJson = readMobileJson(mobileJsonPath);
198
+ const localEntry = {
199
+ id: resp.id,
200
+ platform,
201
+ redirectUri,
202
+ oidcClientId: resp.oidcClientId,
203
+ status: 'active',
204
+ };
205
+ if (opts.name)
206
+ localEntry.name = opts.name;
207
+ if (platform === 'ios') {
208
+ localEntry.bundleId = bundleId;
209
+ localEntry.teamId = teamId;
210
+ }
211
+ else {
212
+ localEntry.packageName = packageName;
213
+ localEntry.shaCertFingerprints = shaCertFingerprints;
214
+ }
215
+ writeMobileJson(mobileJsonPath, upsertMobileApp(mobileJson, localEntry));
216
+ if (!opts.json)
217
+ log.success(`App mobile gravado localmente em ${chalk.bold(MOBILE_JSON_REL)}`);
218
+ printWriteResult({
219
+ id: resp.id,
220
+ oidcClientId: resp.oidcClientId,
221
+ platform: resp.platform,
222
+ note: resp.note ?? null,
223
+ }, `App mobile "${resp.id}" registrado — PKCE público (Authorization Code + PKCE, SEM client secret).`, opts.json);
224
+ if (!opts.json) {
225
+ log.dim('');
226
+ log.dim('Próximos passos:');
227
+ log.dim(` 1. Configure no app: ${chalk.bold(`NEETRU_MOBILE_CLIENT_ID=${resp.oidcClientId}`)}`);
228
+ log.dim(' 2. O fluxo é PKCE público (Authorization Code + PKCE, sem client secret) — RFC 8252.');
229
+ if (resp.note)
230
+ log.dim(` Obs: ${resp.note}`);
231
+ }
232
+ }
233
+ export async function runMobileList(opts = {}) {
234
+ if (!opts.json)
235
+ log.banner();
236
+ const cfg = await loadProductConfig();
237
+ if (!cfg) {
238
+ const msg = 'neetru.config.json não encontrado. Rode `neetru init` primeiro.';
239
+ if (opts.json) {
240
+ jsonError(msg, 'config_not_found');
241
+ }
242
+ else {
243
+ log.error(msg);
244
+ }
245
+ safeExit(1);
246
+ return;
247
+ }
248
+ const spinner = opts.json
249
+ ? null
250
+ : ora({ text: `Buscando apps mobile de "${cfg.slug}"…`, color: 'blue' }).start();
251
+ let resp;
252
+ try {
253
+ resp = await apiRequest(`/api/cli/v1/products/${encodeURIComponent(cfg.slug)}/mobile-apps`);
254
+ spinner?.stop();
255
+ }
256
+ catch (err) {
257
+ spinner?.fail('Falha ao buscar apps mobile.');
258
+ handleApiError(err, opts.json);
259
+ return;
260
+ }
261
+ if (opts.json) {
262
+ console.log(JSON.stringify(resp));
263
+ return;
264
+ }
265
+ log.heading(`Apps mobile — "${chalk.bold(cfg.slug)}" (${resp.count})`);
266
+ if (resp.count === 0) {
267
+ log.dim(' Nenhum app mobile registrado.');
268
+ log.dim(' Dica: rode `neetru mobile init --platform <ios|android> --redirect-uri <url> ...` para registrar um.');
269
+ return;
270
+ }
271
+ renderTable([
272
+ { header: 'ID', value: (r) => r.id, maxWidth: 30 },
273
+ { header: 'Platform', value: (r) => r.platform, maxWidth: 10 },
274
+ {
275
+ header: 'Bundle/Package',
276
+ value: (r) => (r.platform === 'ios' ? (r.bundleId ?? '—') : (r.packageName ?? '—')),
277
+ maxWidth: 30,
278
+ },
279
+ { header: 'Status', value: (r) => mobileStatusColor(r.status), maxWidth: 14 },
280
+ { header: 'OIDC Client ID', value: (r) => r.oidcClientId, maxWidth: 34 },
281
+ ], resp.apps);
282
+ }
283
+ /**
284
+ * `neetru mobile revoke <appId>` — role admin ONLY + step-up MFA (destrutivo
285
+ * top-tier, mesmo gate de `servers deactivate`/`oidc-clients` DELETE).
286
+ *
287
+ * Fail-closed em `--json`/sem TTY: `confirmDestructive` e `resolveStepUp`
288
+ * exigem `--yes` e `--mfa-token`/sessão de 1h explícitos nesse modo — nunca
289
+ * travam esperando input interativo.
290
+ */
291
+ export async function runMobileRevoke(appId, opts) {
292
+ if (!appId) {
293
+ const msg = 'appId é obrigatório: neetru mobile revoke <appId>';
294
+ if (opts.json) {
295
+ jsonError(msg, 'app_id_required');
296
+ }
297
+ else {
298
+ log.error(msg);
299
+ }
300
+ safeExit(1);
301
+ return;
302
+ }
303
+ const cfg = await loadProductConfig();
304
+ if (!cfg) {
305
+ const msg = 'neetru.config.json não encontrado. Rode `neetru init` primeiro.';
306
+ if (opts.json) {
307
+ jsonError(msg, 'config_not_found');
308
+ }
309
+ else {
310
+ log.error(msg);
311
+ }
312
+ safeExit(1);
313
+ return;
314
+ }
315
+ const token = await requireToken(opts.json);
316
+ await confirmDestructive(`Revogar o app mobile "${appId}"? Sessões ativas desse app são encerradas IMEDIATAMENTE.`, opts);
317
+ const { headers } = await resolveStepUp(opts);
318
+ const spinner = opts.json
319
+ ? null
320
+ : ora({ text: `Revogando app mobile ${appId}…`, color: 'red' }).start();
321
+ let resp;
322
+ try {
323
+ resp = await apiRequest(`/api/cli/v1/products/${encodeURIComponent(cfg.slug)}/mobile-apps/${encodeURIComponent(appId)}`, { method: 'DELETE', token, headers });
324
+ spinner?.stop();
325
+ }
326
+ catch (err) {
327
+ spinner?.fail('Falha ao revogar app mobile.');
328
+ handleApiError(err, opts.json);
329
+ return;
330
+ }
331
+ // Remove localmente só DEPOIS do sucesso remoto — best-effort, não bloqueia.
332
+ // O Core é a fonte da verdade; uma falha de FS local aqui não deve mascarar
333
+ // um revoke que já aconteceu de verdade no servidor.
334
+ try {
335
+ const mobileJsonPath = path.resolve(process.cwd(), MOBILE_JSON_REL);
336
+ const mobileJson = readMobileJson(mobileJsonPath);
337
+ writeMobileJson(mobileJsonPath, removeMobileApp(mobileJson, resp.id));
338
+ }
339
+ catch {
340
+ /* best-effort — não falha o comando por causa disso */
341
+ }
342
+ printWriteResult({ id: resp.id, status: resp.status }, `App mobile "${resp.id}" revogado — sessões ativas encerradas.`, opts.json);
343
+ }
344
+ //# sourceMappingURL=mobile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobile.js","sourceRoot":"","sources":["../../src/commands/mobile.ts"],"names":[],"mappings":"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"}
@@ -43,6 +43,8 @@ export interface OpsSmokeObservabilityOptions {
43
43
  export declare function runOpsSmokeObservability(opts: OpsSmokeObservabilityOptions): Promise<void>;
44
44
  export interface OpsRegTokenOptions {
45
45
  expiresMin?: number;
46
+ /** `--mfa-token <código>` — TOTP do step-up MFA. */
47
+ mfaToken?: string;
46
48
  json?: boolean;
47
49
  }
48
50
  export declare function runOpsRegToken(serverId: string, opts: OpsRegTokenOptions): Promise<void>;
@@ -59,6 +61,19 @@ export interface OpsOpenapiDiffOptions {
59
61
  }
60
62
  export declare function runOpsOpenapiDiff(opts: OpsOpenapiDiffOptions): Promise<void>;
61
63
  export interface OpsBootstrapAdminOptions {
64
+ /**
65
+ * `--reactivate` — reativar um staff desligado (`disabled:true`). Sem ele o
66
+ * Core recusa com 409 `staff_disabled` (auditoria cli/v1 2026-09-29, H2).
67
+ */
68
+ reactivate?: boolean;
69
+ /** `--mfa-token <código>` — TOTP do step-up MFA (exigido quando já há admin). */
70
+ mfaToken?: string;
71
+ /**
72
+ * `--bootstrap-secret <valor>` (ou env `NEETRU_BOOTSTRAP_SECRET`) — segredo
73
+ * pré-compartilhado exigido pelo Core no bootstrap ovo-galinha (nenhum admin
74
+ * ainda). Vai no header `x-bootstrap-secret`; NUNCA é logado nem impresso.
75
+ */
76
+ bootstrapSecret?: string;
62
77
  json?: boolean;
63
78
  }
64
79
  export declare function runOpsBootstrapAdmin(uid: string, opts: OpsBootstrapAdminOptions): Promise<void>;
@@ -76,6 +91,9 @@ export declare function runOpsSeedApiCatalog(opts: OpsSeedApiCatalogOptions): Pr
76
91
  export interface OpsRehashSdkSecretsOptions {
77
92
  dryRun?: boolean;
78
93
  rotate?: boolean;
94
+ yes?: boolean;
95
+ force?: boolean;
96
+ mfaToken?: string;
79
97
  json?: boolean;
80
98
  }
81
99
  export declare function runOpsRehashSdkSecrets(opts: OpsRehashSdkSecretsOptions): Promise<void>;
@@ -83,6 +101,8 @@ export interface OpsMigrateCliKeysOptions {
83
101
  dryRun?: boolean;
84
102
  autoAdmin?: boolean;
85
103
  allowedTenants?: string;
104
+ /** `--mfa-token <código>` — TOTP do step-up MFA (só fora do --dry-run). */
105
+ mfaToken?: string;
86
106
  json?: boolean;
87
107
  }
88
108
  export declare function runOpsMigrateCliKeys(opts: OpsMigrateCliKeysOptions): Promise<void>;
@@ -21,7 +21,8 @@
21
21
  import chalk from 'chalk';
22
22
  import ora from 'ora';
23
23
  import { apiRequest } from '../lib/api-client.js';
24
- import { requireToken, handleApiError } from '../lib/cli-read.js';
24
+ import { requireToken, resolveToken, handleApiError } from '../lib/cli-read.js';
25
+ import { confirmDestructive, resolveStepUp, withStepUpRetry } from '../lib/cli-write.js';
25
26
  import { log } from '../utils/logger.js';
26
27
  import { safeExit } from '../utils/safe-exit.js';
27
28
  // ─── Helpers ──────────────────────────────────────────────────────────────────
@@ -143,6 +144,9 @@ export async function runOpsSmokeObservability(opts) {
143
144
  }
144
145
  export async function runOpsRegToken(serverId, opts) {
145
146
  const token = await requireToken(opts.json);
147
+ // H5 (auditoria cli/v1 2026-09-29): o token dá credencial de agente pra
148
+ // QUALQUER server — o Core exige step-up MFA (sessão 1h → --mfa-token → prompt).
149
+ const { headers } = await resolveStepUp({ mfaToken: opts.mfaToken, json: opts.json });
146
150
  const spinner = opts.json ? null : ora({ text: `Gerando registration token para ${serverId}…`, color: 'blue' }).start();
147
151
  let res;
148
152
  try {
@@ -150,6 +154,7 @@ export async function runOpsRegToken(serverId, opts) {
150
154
  method: 'POST',
151
155
  body: { serverId, expiresMin: opts.expiresMin ?? 30 },
152
156
  token,
157
+ headers,
153
158
  });
154
159
  spinner?.stop();
155
160
  }
@@ -273,7 +278,12 @@ export async function runOpsOpenapiDiff(opts) {
273
278
  }
274
279
  }
275
280
  export async function runOpsBootstrapAdmin(uid, opts) {
276
- const token = await requireToken(opts.json);
281
+ const bootstrapSecret = (opts.bootstrapSecret ?? process.env.NEETRU_BOOTSTRAP_SECRET ?? '').trim();
282
+ // Sem admin no Core o gate é o segredo, não o Bearer — com o segredo em mãos
283
+ // a sessão CLI é opcional (deploy fresco, ninguém logado ainda).
284
+ const token = bootstrapSecret
285
+ ? ((await resolveToken()) ?? undefined)
286
+ : await requireToken(opts.json);
277
287
  if (!uid || !uid.trim()) {
278
288
  if (opts.json) {
279
289
  console.log(JSON.stringify({ ok: false, error: 'validation', message: 'uid é obrigatório.' }));
@@ -284,25 +294,28 @@ export async function runOpsBootstrapAdmin(uid, opts) {
284
294
  safeExit(1);
285
295
  }
286
296
  const spinner = opts.json ? null : ora({ text: `Promovendo ${uid} a admin…`, color: 'blue' }).start();
287
- let res;
288
- try {
289
- res = await apiRequest('/api/cli/v1/ops/bootstrap-admin', {
290
- method: 'POST',
291
- body: { uid },
292
- token,
293
- });
294
- spinner?.stop();
295
- }
296
- catch (error) {
297
+ // H2 (auditoria cli/v1 2026-09-29): com admin existente o Core exige step-up
298
+ // MFA; no bootstrap ovo-galinha (sem admin) não. Como o CLI não sabe de
299
+ // antemão, manda a sessão de 1h/--mfa-token se houver e, no `401
300
+ // mfa_required`, prompta o TOTP e repete (fail-closed em --json/não-TTY).
301
+ const res = await withStepUpRetry({ mfaToken: opts.mfaToken, json: opts.json }, (headers) => apiRequest('/api/cli/v1/ops/bootstrap-admin', {
302
+ method: 'POST',
303
+ body: { uid, ...(opts.reactivate ? { reactivate: true } : {}) },
304
+ token,
305
+ headers: bootstrapSecret ? { ...headers, 'x-bootstrap-secret': bootstrapSecret } : headers,
306
+ }), (error) => {
297
307
  spinner?.fail('Falha ao promover admin.');
298
- handleApiError(error, opts.json);
299
- }
308
+ return handleApiError(error, opts.json);
309
+ }, () => spinner?.stop());
310
+ spinner?.stop();
300
311
  if (opts.json) {
301
312
  console.log(JSON.stringify(res));
302
313
  return;
303
314
  }
304
315
  const data = res;
305
316
  log.success(`${String(data.email ?? uid)} promovido a admin (${String(data.action ?? '')}).`);
317
+ if (opts.reactivate)
318
+ log.dim(' (reativação explícita — registrada na auditoria)');
306
319
  console.log(chalk.dim(` ${String(data.message ?? '')}`));
307
320
  }
308
321
  export async function runOpsBackfillIncidentsSource(opts) {
@@ -434,6 +447,19 @@ export async function runOpsSeedApiCatalog(opts) {
434
447
  }
435
448
  export async function runOpsRehashSdkSecrets(opts) {
436
449
  const token = await requireToken(opts.json);
450
+ // SEGURANÇA (auditoria UX round-2): `--rotate` é IRREVERSÍVEL — gera novos
451
+ // clientSecrets e invalida TODOS os antigos, quebrando qualquer produto SaaS
452
+ // que ainda use o secret antigo. Antes rodava direto (sem confirmação nem MFA).
453
+ // Gate igual ao resto das destrutivas top-tier: confirmDestructive (y/N,
454
+ // bypass --yes/--force, fail-closed em --json/não-TTY) + resolveStepUp (sessão
455
+ // MFA de 1h → --mfa-token → prompt). O `--dry-run` continua isento (não aplica),
456
+ // e o re-hash in-place (sem --rotate) preserva o secret, então não é gated.
457
+ const mfaHeaders = {};
458
+ if (opts.rotate && !opts.dryRun) {
459
+ await confirmDestructive('Rotacionar os clientSecrets de TODOS os sdk_clients? Os secrets antigos deixam de funcionar imediatamente (irreversível).', { yes: opts.yes, force: opts.force, json: opts.json });
460
+ const { headers } = await resolveStepUp({ mfaToken: opts.mfaToken, json: opts.json });
461
+ Object.assign(mfaHeaders, headers);
462
+ }
437
463
  const spinner = opts.json
438
464
  ? null
439
465
  : ora({ text: `Re-hash SDK secrets${opts.dryRun ? ' (dry-run)' : ''}${opts.rotate ? ' [ROTATE]' : ''}…`, color: 'blue' }).start();
@@ -443,6 +469,7 @@ export async function runOpsRehashSdkSecrets(opts) {
443
469
  method: 'POST',
444
470
  body: { dryRun: opts.dryRun ?? false, rotate: opts.rotate ?? false },
445
471
  token,
472
+ headers: mfaHeaders,
446
473
  });
447
474
  spinner?.stop();
448
475
  }
@@ -492,6 +519,13 @@ export async function runOpsMigrateCliKeys(opts) {
492
519
  }
493
520
  }
494
521
  }
522
+ // M4 (auditoria cli/v1 2026-09-29): aplicar muda o escopo de TODAS as chaves
523
+ // — o Core exige step-up MFA fora do dry-run. Dry-run segue sem MFA.
524
+ const mfaHeaders = {};
525
+ if (!opts.dryRun) {
526
+ const { headers } = await resolveStepUp({ mfaToken: opts.mfaToken, json: opts.json });
527
+ Object.assign(mfaHeaders, headers);
528
+ }
495
529
  const spinner = opts.json
496
530
  ? null
497
531
  : ora({ text: `Migrando cli_api_keys${opts.dryRun ? ' (dry-run)' : ''}…`, color: 'blue' }).start();
@@ -505,6 +539,7 @@ export async function runOpsMigrateCliKeys(opts) {
505
539
  tenantMap,
506
540
  },
507
541
  token,
542
+ headers: mfaHeaders,
508
543
  });
509
544
  spinner?.stop();
510
545
  }
@@ -601,10 +636,12 @@ export function registerOpsCommand(program) {
601
636
  .command('reg-token <serverId>')
602
637
  .description('Gerar registration token para um server (uso único, expira em 30min)')
603
638
  .option('--expires-min <n>', 'minutos até expiração (default: 30)', '30')
639
+ .option('--mfa-token <code>', 'código TOTP para step-up MFA (ou use `neetru auth mfa-session`)')
604
640
  .option('--json', 'saída em JSON')
605
641
  .action(async (serverId, cmdOpts) => {
606
642
  await runOpsRegToken(serverId, {
607
643
  expiresMin: cmdOpts.expiresMin ? Number.parseInt(cmdOpts.expiresMin, 10) : 30,
644
+ mfaToken: cmdOpts.mfaToken,
608
645
  json: !!cmdOpts.json,
609
646
  });
610
647
  });
@@ -638,11 +675,19 @@ export function registerOpsCommand(program) {
638
675
  opsCmd
639
676
  .command('bootstrap-admin <uid>')
640
677
  .description('Promover um usuário a admin (resolve o problema ovo-galinha do 1º admin). ' +
641
- 'Se não existe nenhum admin, o gate está aberto sem auth. ' +
642
- 'Se já existe admin, exige Bearer com role admin.')
678
+ 'Sem admin: exige o segredo de bootstrap (--bootstrap-secret / NEETRU_BOOTSTRAP_SECRET). ' +
679
+ 'Se já existe admin, exige Bearer com role admin + step-up MFA.')
680
+ .option('--reactivate', 'reativar um staff desligado (disabled) — fica registrado na auditoria')
681
+ .option('--bootstrap-secret <valor>', 'segredo de bootstrap (só quando ainda NÃO há admin; ou env NEETRU_BOOTSTRAP_SECRET) — nunca é exibido')
682
+ .option('--mfa-token <code>', 'código TOTP para step-up MFA (ou use `neetru auth mfa-session`)')
643
683
  .option('--json', 'saída em JSON')
644
684
  .action(async (uid, cmdOpts) => {
645
- await runOpsBootstrapAdmin(uid, { json: !!cmdOpts.json });
685
+ await runOpsBootstrapAdmin(uid, {
686
+ reactivate: !!cmdOpts.reactivate,
687
+ bootstrapSecret: cmdOpts.bootstrapSecret,
688
+ mfaToken: cmdOpts.mfaToken,
689
+ json: !!cmdOpts.json,
690
+ });
646
691
  });
647
692
  // ── ops backfill ──────────────────────────────────────────────────────────
648
693
  const backfillCmd = opsCmd
@@ -684,11 +729,17 @@ export function registerOpsCommand(program) {
684
729
  '--rotate: gera novo secret e o imprime UMA VEZ na saída — anote por canal seguro.')
685
730
  .option('--dry-run', 'lista o que seria migrado sem gravar')
686
731
  .option('--rotate', 'gerar novos secrets (invalida os antigos)')
732
+ .option('--yes', 'pula a confirmação interativa do --rotate (necessário em --json/CI)')
733
+ .option('--force', 'alias de --yes')
734
+ .option('--mfa-token <code>', 'código TOTP para o step-up MFA do --rotate')
687
735
  .option('--json', 'saída em JSON')
688
736
  .action(async (cmdOpts) => {
689
737
  await runOpsRehashSdkSecrets({
690
738
  dryRun: !!cmdOpts.dryRun,
691
739
  rotate: !!cmdOpts.rotate,
740
+ yes: !!cmdOpts.yes,
741
+ force: !!cmdOpts.force,
742
+ mfaToken: cmdOpts.mfaToken,
692
743
  json: !!cmdOpts.json,
693
744
  });
694
745
  });
@@ -704,12 +755,14 @@ export function registerOpsCommand(program) {
704
755
  .option('--dry-run', 'lista o que seria atualizado sem gravar')
705
756
  .option('--no-auto-admin', 'não aplica ["*"] automaticamente em admin keys')
706
757
  .option('--allowed-tenants <map>', 'mapeamento keyId→tenants no formato "keyId1:t-1,t-2;keyId2:t-3" (para roles não-admin)')
758
+ .option('--mfa-token <code>', 'código TOTP para step-up MFA (ou use `neetru auth mfa-session`)')
707
759
  .option('--json', 'saída em JSON')
708
760
  .action(async (cmdOpts) => {
709
761
  await runOpsMigrateCliKeys({
710
762
  dryRun: !!cmdOpts.dryRun,
711
763
  autoAdmin: cmdOpts.autoAdmin !== false,
712
764
  allowedTenants: cmdOpts.allowedTenants,
765
+ mfaToken: cmdOpts.mfaToken,
713
766
  json: !!cmdOpts.json,
714
767
  });
715
768
  });