@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.
- package/CHANGELOG.md +78 -0
- package/README.md +25 -1
- package/dist/commands/admin/oidc-login.d.ts +18 -0
- package/dist/commands/admin/oidc-login.js +68 -3
- package/dist/commands/admin/oidc-login.js.map +1 -1
- package/dist/commands/admin/sdk-credential.js +11 -6
- package/dist/commands/admin/sdk-credential.js.map +1 -1
- package/dist/commands/agent-release.d.ts +2 -0
- package/dist/commands/agent-release.js +41 -29
- package/dist/commands/agent-release.js.map +1 -1
- package/dist/commands/agent-write.d.ts +1 -0
- package/dist/commands/agent-write.js +4 -0
- package/dist/commands/agent-write.js.map +1 -1
- package/dist/commands/ai-models.js +9 -4
- package/dist/commands/ai-models.js.map +1 -1
- package/dist/commands/archive.js +9 -2
- package/dist/commands/archive.js.map +1 -1
- package/dist/commands/auth.js +17 -2
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/autocomplete.d.ts +1 -1
- package/dist/commands/autocomplete.js +2 -0
- package/dist/commands/autocomplete.js.map +1 -1
- package/dist/commands/chat-menu.js +20 -6
- package/dist/commands/chat-menu.js.map +1 -1
- package/dist/commands/chat.d.ts +14 -0
- package/dist/commands/chat.js +32 -4
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/config.js +7 -0
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/db-move.d.ts +204 -0
- package/dist/commands/db-move.js +890 -0
- package/dist/commands/db-move.js.map +1 -0
- package/dist/commands/db.d.ts +14 -3
- package/dist/commands/db.js +102 -67
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/deploy.d.ts +15 -0
- package/dist/commands/deploy.js +42 -1
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev-up.d.ts +5 -0
- package/dist/commands/dev-up.js +29 -4
- package/dist/commands/dev-up.js.map +1 -1
- package/dist/commands/doctor.js +12 -6
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/domains.js +48 -36
- package/dist/commands/domains.js.map +1 -1
- package/dist/commands/init.d.ts +0 -8
- package/dist/commands/init.js +14 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/logs.js +18 -21
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/mobile.d.ts +26 -0
- package/dist/commands/mobile.js +344 -0
- package/dist/commands/mobile.js.map +1 -0
- package/dist/commands/ops.d.ts +20 -0
- package/dist/commands/ops.js +70 -17
- package/dist/commands/ops.js.map +1 -1
- package/dist/commands/products-db.d.ts +1 -0
- package/dist/commands/products-db.js +15 -15
- package/dist/commands/products-db.js.map +1 -1
- package/dist/commands/promote.js +13 -32
- package/dist/commands/promote.js.map +1 -1
- package/dist/commands/schema.d.ts +4 -0
- package/dist/commands/schema.js +25 -16
- package/dist/commands/schema.js.map +1 -1
- package/dist/commands/servers.d.ts +2 -3
- package/dist/commands/servers.js +4 -7
- package/dist/commands/servers.js.map +1 -1
- package/dist/commands/smoke.d.ts +123 -0
- package/dist/commands/smoke.js +529 -0
- package/dist/commands/smoke.js.map +1 -0
- package/dist/commands/vm-memory.d.ts +53 -0
- package/dist/commands/vm-memory.js +307 -0
- package/dist/commands/vm-memory.js.map +1 -0
- package/dist/commands/vm.d.ts +2 -0
- package/dist/commands/vm.js +8 -0
- package/dist/commands/vm.js.map +1 -1
- package/dist/commands/webhooks.d.ts +2 -0
- package/dist/commands/webhooks.js +7 -0
- package/dist/commands/webhooks.js.map +1 -1
- package/dist/index.js +403 -21
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-artifact-url.d.ts +19 -0
- package/dist/lib/agent-artifact-url.js +48 -0
- package/dist/lib/agent-artifact-url.js.map +1 -0
- package/dist/lib/api-client.d.ts +11 -3
- package/dist/lib/api-client.js +55 -24
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/api-error.js +32 -1
- package/dist/lib/api-error.js.map +1 -1
- package/dist/lib/auth.d.ts +2 -9
- package/dist/lib/auth.js +34 -18
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/cli-write.d.ts +31 -0
- package/dist/lib/cli-write.js +51 -2
- package/dist/lib/cli-write.js.map +1 -1
- package/dist/lib/mobile-local/index.d.ts +7 -0
- package/dist/lib/mobile-local/index.js +7 -0
- package/dist/lib/mobile-local/index.js.map +1 -0
- package/dist/lib/mobile-local/mobile-json.d.ts +57 -0
- package/dist/lib/mobile-local/mobile-json.js +169 -0
- package/dist/lib/mobile-local/mobile-json.js.map +1 -0
- package/dist/lib/product-config.d.ts +5 -0
- package/dist/lib/product-config.js +37 -0
- package/dist/lib/product-config.js.map +1 -0
- package/dist/lib/telemetry.d.ts +7 -0
- package/dist/lib/telemetry.js +9 -0
- package/dist/lib/telemetry.js.map +1 -1
- 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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"}
|
package/dist/commands/ops.d.ts
CHANGED
|
@@ -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>;
|
package/dist/commands/ops.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
})
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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
|
-
'
|
|
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, {
|
|
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
|
});
|