@neetru/cli 2.25.0 → 2.26.1
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/dist/commands/build.js +12 -17
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/deploy.d.ts +6 -0
- package/dist/commands/deploy.js +25 -39
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev-up.d.ts +10 -26
- package/dist/commands/dev-up.js +9 -141
- package/dist/commands/dev-up.js.map +1 -1
- package/dist/commands/logs.d.ts +29 -0
- package/dist/commands/logs.js +161 -0
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/webhooks.d.ts +34 -0
- package/dist/commands/webhooks.js +162 -0
- package/dist/commands/webhooks.js.map +1 -0
- package/dist/index.js +110 -23
- package/dist/index.js.map +1 -1
- package/dist/lib/compose-generator.js +11 -7
- package/dist/lib/compose-generator.js.map +1 -1
- package/dist/utils/safe-exit.d.ts +23 -0
- package/dist/utils/safe-exit.js +53 -1
- package/dist/utils/safe-exit.js.map +1 -1
- package/package.json +1 -1
- package/templates/billing/checkout.ts +25 -9
- package/templates/billing/page.tsx +56 -15
- package/templates/billing/webhooks.ts +83 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { apiRequest } from '../lib/api-client.js';
|
|
4
|
+
import { requireToken, handleApiError } from '../lib/cli-read.js';
|
|
5
|
+
import { renderTable } from '../lib/render.js';
|
|
6
|
+
import { log } from '../utils/logger.js';
|
|
7
|
+
const ALL_EVENTS = [
|
|
8
|
+
'subscription.activated',
|
|
9
|
+
'subscription.cancelled',
|
|
10
|
+
'subscription.payment_failed',
|
|
11
|
+
'subscription.trial_ending',
|
|
12
|
+
'usage.quota_exceeded',
|
|
13
|
+
'account.suspended',
|
|
14
|
+
'account.reactivated',
|
|
15
|
+
'support.ticket_replied',
|
|
16
|
+
];
|
|
17
|
+
export async function runWebhooksRegister(opts) {
|
|
18
|
+
const token = await requireToken(opts.json);
|
|
19
|
+
const spinner = opts.json ? null : ora({ text: 'Registrando webhook…', color: 'blue' }).start();
|
|
20
|
+
let res;
|
|
21
|
+
try {
|
|
22
|
+
res = await apiRequest('/api/cli/v1/webhooks', {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
token,
|
|
25
|
+
body: {
|
|
26
|
+
productId: opts.productId,
|
|
27
|
+
url: opts.url,
|
|
28
|
+
events: opts.events,
|
|
29
|
+
secret: opts.secret,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
spinner?.stop();
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
spinner?.fail('Falha ao registrar webhook.');
|
|
36
|
+
handleApiError(error, opts.json);
|
|
37
|
+
}
|
|
38
|
+
if (opts.json) {
|
|
39
|
+
console.log(JSON.stringify(res));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
log.success('Webhook registrado com sucesso.');
|
|
43
|
+
console.log(` ${chalk.dim('ID:')} ${res.id}`);
|
|
44
|
+
console.log(` ${chalk.dim('URL:')} ${res.url}`);
|
|
45
|
+
console.log(` ${chalk.dim('Eventos:')} ${res.events.join(', ')}`);
|
|
46
|
+
console.log(` ${chalk.dim('Secret:')} ${res.hasSecret ? chalk.green('definido') : chalk.yellow('nenhum')}`);
|
|
47
|
+
console.log(` ${chalk.dim('Status:')} ${res.status}`);
|
|
48
|
+
}
|
|
49
|
+
export async function runWebhooksList(opts) {
|
|
50
|
+
const token = await requireToken(opts.json);
|
|
51
|
+
const spinner = opts.json ? null : ora({ text: 'Buscando webhooks…', color: 'blue' }).start();
|
|
52
|
+
let res;
|
|
53
|
+
try {
|
|
54
|
+
res = await apiRequest(`/api/cli/v1/webhooks?productId=${encodeURIComponent(opts.productId)}`, { token });
|
|
55
|
+
spinner?.stop();
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
spinner?.fail('Falha ao listar webhooks.');
|
|
59
|
+
handleApiError(error, opts.json);
|
|
60
|
+
}
|
|
61
|
+
if (opts.json) {
|
|
62
|
+
console.log(JSON.stringify(res));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (res.count === 0) {
|
|
66
|
+
log.info('Nenhum webhook registrado para este produto.');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
log.heading(`Webhooks — ${res.productId} (${res.count})`);
|
|
70
|
+
renderTable([
|
|
71
|
+
{ header: 'ID', value: (e) => e.id, maxWidth: 24 },
|
|
72
|
+
{ header: 'URL', value: (e) => e.url, maxWidth: 48 },
|
|
73
|
+
{ header: 'EVENTOS', value: (e) => e.events.join(', '), maxWidth: 36 },
|
|
74
|
+
{ header: 'SECRET', value: (e) => (e.hasSecret ? chalk.green('sim') : chalk.yellow('não')) },
|
|
75
|
+
{ header: 'STATUS', value: (e) => e.status === 'active' ? chalk.green(e.status) : chalk.red(e.status) },
|
|
76
|
+
{ header: 'FALHAS', value: (e) => String(e.consecutiveFailures) },
|
|
77
|
+
], res.endpoints);
|
|
78
|
+
}
|
|
79
|
+
export async function runWebhooksUnregister(opts) {
|
|
80
|
+
const token = await requireToken(opts.json);
|
|
81
|
+
const spinner = opts.json ? null : ora({ text: 'Removendo webhook…', color: 'yellow' }).start();
|
|
82
|
+
let res;
|
|
83
|
+
try {
|
|
84
|
+
res = await apiRequest(`/api/cli/v1/webhooks/${encodeURIComponent(opts.id)}?productId=${encodeURIComponent(opts.productId)}`, { method: 'DELETE', token });
|
|
85
|
+
spinner?.stop();
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
spinner?.fail('Falha ao remover webhook.');
|
|
89
|
+
handleApiError(error, opts.json);
|
|
90
|
+
}
|
|
91
|
+
if (opts.json) {
|
|
92
|
+
console.log(JSON.stringify(res));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
log.success('Webhook removido.');
|
|
96
|
+
}
|
|
97
|
+
export async function runWebhooksTest(opts) {
|
|
98
|
+
const token = await requireToken(opts.json);
|
|
99
|
+
const spinner = opts.json ? null : ora({ text: 'Disparando evento de teste…', color: 'blue' }).start();
|
|
100
|
+
let res;
|
|
101
|
+
try {
|
|
102
|
+
res = await apiRequest(`/api/cli/v1/webhooks/${encodeURIComponent(opts.id)}/test?productId=${encodeURIComponent(opts.productId)}`, {
|
|
103
|
+
method: 'POST',
|
|
104
|
+
token,
|
|
105
|
+
body: opts.event ? { productId: opts.productId, event: opts.event } : { productId: opts.productId },
|
|
106
|
+
});
|
|
107
|
+
spinner?.stop();
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
spinner?.fail('Falha ao disparar teste.');
|
|
111
|
+
handleApiError(error, opts.json);
|
|
112
|
+
}
|
|
113
|
+
if (opts.json) {
|
|
114
|
+
console.log(JSON.stringify(res));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
log.success(`Evento de teste "${res.eventType}" enfileirado (${res.eventId}).`);
|
|
118
|
+
console.log(` A entrega é assíncrona. Consulte ${chalk.cyan('neetru webhooks deliveries')} para ver o resultado.`);
|
|
119
|
+
}
|
|
120
|
+
export async function runWebhooksDeliveries(opts) {
|
|
121
|
+
const token = await requireToken(opts.json);
|
|
122
|
+
const params = new URLSearchParams({ productId: opts.productId });
|
|
123
|
+
if (opts.status)
|
|
124
|
+
params.set('status', opts.status);
|
|
125
|
+
if (opts.limit)
|
|
126
|
+
params.set('limit', String(opts.limit));
|
|
127
|
+
const spinner = opts.json ? null : ora({ text: 'Buscando entregas…', color: 'blue' }).start();
|
|
128
|
+
let res;
|
|
129
|
+
try {
|
|
130
|
+
res = await apiRequest(`/api/cli/v1/webhooks/${encodeURIComponent(opts.id)}/deliveries?${params.toString()}`, { token });
|
|
131
|
+
spinner?.stop();
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
spinner?.fail('Falha ao listar entregas.');
|
|
135
|
+
handleApiError(error, opts.json);
|
|
136
|
+
}
|
|
137
|
+
if (opts.json) {
|
|
138
|
+
console.log(JSON.stringify(res));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (res.count === 0) {
|
|
142
|
+
log.info('Nenhuma entrega encontrada para este endpoint.');
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
log.heading(`Entregas — ${res.endpointId} (${res.count})`);
|
|
146
|
+
renderTable([
|
|
147
|
+
{ header: 'EVENTO', value: (d) => d.eventType ?? d.eventId.slice(0, 8), maxWidth: 28 },
|
|
148
|
+
{ header: 'STATUS', value: (d) => {
|
|
149
|
+
switch (d.status) {
|
|
150
|
+
case 'delivered': return chalk.green(d.status);
|
|
151
|
+
case 'failed': return chalk.red(d.status);
|
|
152
|
+
case 'dead': return chalk.red.bold(d.status);
|
|
153
|
+
case 'delivering': return chalk.yellow(d.status);
|
|
154
|
+
default: return chalk.dim(d.status);
|
|
155
|
+
}
|
|
156
|
+
} },
|
|
157
|
+
{ header: 'TENTATIVAS', value: (d) => `${d.attempts}${d.maxAttempts ? `/${d.maxAttempts}` : ''}` },
|
|
158
|
+
{ header: 'CÓDIGO', value: (d) => d.lastStatusCode != null ? String(d.lastStatusCode) : '-' },
|
|
159
|
+
{ header: 'ERRO', value: (d) => d.lastError ?? '-', maxWidth: 32 },
|
|
160
|
+
], res.deliveries);
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/commands/webhooks.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -3,12 +3,15 @@ import { Command } from 'commander';
|
|
|
3
3
|
import { log } from './utils/logger.js';
|
|
4
4
|
import { CLI_VERSION } from './version.js';
|
|
5
5
|
import { installTelemetryFlushHook, markCommandStart, markCommandEnd, } from './lib/telemetry.js';
|
|
6
|
-
import { safeExit } from './utils/safe-exit.js';
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
6
|
+
import { safeExit, isSafeExitHalt } from './utils/safe-exit.js';
|
|
7
|
+
// UV_HANDLE_CLOSING no Windows (bug_818cf6d): o double-destroy de stdin (handler
|
|
8
|
+
// 'exit' redundante) foi removido em 2026-07-15 (ddd479f), MAS isso não bastou
|
|
9
|
+
// — a causa dominante é o `uv_async_t` do undici (fetch) ainda vivo quando o
|
|
10
|
+
// `process.exit()` roda SÍNCRONO. Fix real (2026-07-16, re-confirmado por repro
|
|
11
|
+
// empírico 6/6): `safeExit()` em produção DEFERE o exit um macrotask (~100ms) e
|
|
12
|
+
// lança `SafeExitHalt`; os traps globais abaixo engolem o sinal — e deferem
|
|
13
|
+
// também qualquer erro não-tratado, que senão dispararia o mesmo crash pelo
|
|
14
|
+
// exit síncrono default do Node.
|
|
12
15
|
// H-01 fix (Opus review 2026-05-27): instala flush hook NO ENTRY-POINT,
|
|
13
16
|
// antes de qualquer parse. Garante que beforeExit + exit hooks estao armados
|
|
14
17
|
// pra TODOS os caminhos — sucesso, falha via safeExit, ou exit direto.
|
|
@@ -16,6 +19,26 @@ import { safeExit } from './utils/safe-exit.js';
|
|
|
16
19
|
// pulava safeExit(N != 0) — feature CLI telemetry capturava ~ε% do
|
|
17
20
|
// que se propunha a medir.
|
|
18
21
|
installTelemetryFlushHook();
|
|
22
|
+
// ── Traps globais de saída (bug_818cf6d / UV_HANDLE_CLOSING) ──────────────
|
|
23
|
+
// `safeExit()` lança `SafeExitHalt` em produção pra parar o caller sem
|
|
24
|
+
// `process.exit()` síncrono (que crasharia o undici no Windows). Aqui só
|
|
25
|
+
// engolimos o sinal — o `setTimeout` agendado dentro do `safeExit` faz a saída
|
|
26
|
+
// real. Qualquer OUTRO erro não-tratado é logado (com stack) e sai com exit
|
|
27
|
+
// DEFERIDO pelo mesmo motivo. `log.error` vai em try/catch pra nunca re-entrar.
|
|
28
|
+
function handleFatalSignal(err) {
|
|
29
|
+
if (isSafeExitHalt(err))
|
|
30
|
+
return; // saída já agendada dentro do safeExit
|
|
31
|
+
try {
|
|
32
|
+
log.error(err instanceof Error ? (err.stack ?? err.message) : String(err));
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
/* logger indisponível — segue direto pro exit deferido */
|
|
36
|
+
}
|
|
37
|
+
process.exitCode = 1;
|
|
38
|
+
setTimeout(() => process.exit(1), 100);
|
|
39
|
+
}
|
|
40
|
+
process.on('uncaughtException', handleFatalSignal);
|
|
41
|
+
process.on('unhandledRejection', handleFatalSignal);
|
|
19
42
|
const program = new Command();
|
|
20
43
|
program
|
|
21
44
|
.name('neetru')
|
|
@@ -271,12 +294,11 @@ program
|
|
|
271
294
|
// ── neetru dev ────────────────────────────────────────────────────────
|
|
272
295
|
// `neetru dev` (sem subcomando) = banco local Docker + watch de schema (M1
|
|
273
296
|
// GAP-M1-4) — comportamento legado preservado (action + options intactos).
|
|
274
|
-
// `neetru dev up` =
|
|
275
|
-
//
|
|
276
|
-
// a action de `dev` só dispara quando nenhum subcomando casa.
|
|
297
|
+
// `neetru dev up` = ambiente dev-local (banco + agente) via Docker Compose.
|
|
298
|
+
// Subcomando aditivo: a action de `dev` só dispara quando nenhum subcomando casa.
|
|
277
299
|
const devCmd = program
|
|
278
300
|
.command('dev')
|
|
279
|
-
.description('Ambiente de dev local — banco Docker (default) ou
|
|
301
|
+
.description('Ambiente de dev local — banco Docker (default) ou ambiente completo (`dev up`)')
|
|
280
302
|
.option('--db <name>', 'banco alvo do .neetru/db.json (default: único registrado)')
|
|
281
303
|
.option('--schema <path>', 'caminho do arquivo de schema (default: db/schema.ts)')
|
|
282
304
|
.action(async (opts) => {
|
|
@@ -285,22 +307,12 @@ const devCmd = program
|
|
|
285
307
|
});
|
|
286
308
|
devCmd
|
|
287
309
|
.command('up')
|
|
288
|
-
.description('
|
|
289
|
-
.option('--
|
|
290
|
-
.option('--core-port <port>', 'porta do Core (default 9003)')
|
|
291
|
-
.option('--core-path <path>', 'diretório do Core (default: cwd; precisa ter firebase.json)')
|
|
292
|
-
.option('--skip-core', 'só os emuladores, sem subir o Core')
|
|
293
|
-
.option('--import <dir>', 'importa estado dos emuladores deste diretório (seed portátil)')
|
|
294
|
-
.option('--export-on-exit <dir>', 'exporta o estado dos emuladores ao sair (snapshot)')
|
|
310
|
+
.description('Sobe o ambiente dev-local (banco + agente) via Docker Compose')
|
|
311
|
+
.option('--core-port <port>', 'porta do app SaaS local (default 3000)')
|
|
295
312
|
.action(async (opts) => {
|
|
296
313
|
const { runDevUp } = await import('./commands/dev-up.js');
|
|
297
314
|
await runDevUp({
|
|
298
|
-
only: opts.only,
|
|
299
315
|
corePort: opts.corePort ? Number(opts.corePort) : undefined,
|
|
300
|
-
corePath: opts.corePath,
|
|
301
|
-
skipCore: Boolean(opts.skipCore),
|
|
302
|
-
importDir: opts.import,
|
|
303
|
-
exportOnExit: opts.exportOnExit,
|
|
304
316
|
});
|
|
305
317
|
});
|
|
306
318
|
devCmd
|
|
@@ -401,7 +413,7 @@ program
|
|
|
401
413
|
.option('--target <target>', 'workspace | cloud-run | vm (interativo se omitido)')
|
|
402
414
|
.option('--server <id>', 'id do server (target=vm)')
|
|
403
415
|
.option('--domain <domain>', 'domínio público')
|
|
404
|
-
.option('--port <port>', 'porta interna do app (
|
|
416
|
+
.option('--port <port>', 'porta interna do app (target=cloud-run apenas; em target=vm é ignorado — porta é gerenciada pelo Core)', (v) => Number.parseInt(v, 10))
|
|
405
417
|
.option('--artifact <file>', 'caminho do tarball já buildado')
|
|
406
418
|
.option('--artifact-url <url>', 'URL pública/signed do artifact (GCS, S3)')
|
|
407
419
|
.option('--artifact-sha256 <sha>', 'sha256 do artifact (par com --artifact-url)')
|
|
@@ -1409,6 +1421,7 @@ program
|
|
|
1409
1421
|
.option('--channel <channel>', 'filtrar por channel (stdout | stderr | app)')
|
|
1410
1422
|
.option('--correlation-id <id>', 'filtrar por correlationId (UUID)')
|
|
1411
1423
|
.option('--json', 'saída em JSON (NDJSON linha a linha, útil para pipes)')
|
|
1424
|
+
.option('--local [path]', 'ler NDJSON local do emulador (dev up) em vez do Core; default .neetru/dev/logs.ndjson')
|
|
1412
1425
|
.action(async (opts) => {
|
|
1413
1426
|
const { runLogs } = await import('./commands/logs.js');
|
|
1414
1427
|
await runLogs({
|
|
@@ -1422,6 +1435,7 @@ program
|
|
|
1422
1435
|
channel: opts.channel,
|
|
1423
1436
|
correlationId: opts.correlationId,
|
|
1424
1437
|
json: !!opts.json,
|
|
1438
|
+
local: opts.local,
|
|
1425
1439
|
});
|
|
1426
1440
|
});
|
|
1427
1441
|
// ── neetru vm ─────────────────────────────────────────────────────────
|
|
@@ -2361,6 +2375,79 @@ docsCmd
|
|
|
2361
2375
|
json: !!opts.json,
|
|
2362
2376
|
});
|
|
2363
2377
|
});
|
|
2378
|
+
// ── neetru webhooks ──────────────────────────────────────────────────
|
|
2379
|
+
// Produtos SaaS registram endpoints de webhook pra receber eventos do Core.
|
|
2380
|
+
const webhooksCmd = program
|
|
2381
|
+
.command('webhooks')
|
|
2382
|
+
.description('Gerenciar webhooks de produto (register/list/unregister/test)');
|
|
2383
|
+
webhooksCmd
|
|
2384
|
+
.command('register')
|
|
2385
|
+
.description('Registrar endpoint de webhook para receber eventos do Core')
|
|
2386
|
+
.requiredOption('--product-id <id>', 'ID do produto')
|
|
2387
|
+
.requiredOption('--url <url>', 'URL do endpoint que receberá os eventos')
|
|
2388
|
+
.requiredOption('--events <csv>', 'eventos separados por vírgula')
|
|
2389
|
+
.option('--secret <secret>', 'segredo HMAC para assinar payloads (gerado automaticamente se omitido)')
|
|
2390
|
+
.option('--json', 'saída em JSON')
|
|
2391
|
+
.action(async (opts) => {
|
|
2392
|
+
const { runWebhooksRegister } = await import('./commands/webhooks.js');
|
|
2393
|
+
await runWebhooksRegister({
|
|
2394
|
+
productId: opts.productId,
|
|
2395
|
+
url: opts.url,
|
|
2396
|
+
events: opts.events.split(',').map((s) => s.trim()),
|
|
2397
|
+
secret: opts.secret,
|
|
2398
|
+
json: !!opts.json,
|
|
2399
|
+
});
|
|
2400
|
+
});
|
|
2401
|
+
webhooksCmd
|
|
2402
|
+
.command('list')
|
|
2403
|
+
.description('Listar endpoints de webhook registrados para um produto')
|
|
2404
|
+
.requiredOption('--product-id <id>', 'ID do produto')
|
|
2405
|
+
.option('--json', 'saída em JSON')
|
|
2406
|
+
.action(async (opts) => {
|
|
2407
|
+
const { runWebhooksList } = await import('./commands/webhooks.js');
|
|
2408
|
+
await runWebhooksList({ productId: opts.productId, json: !!opts.json });
|
|
2409
|
+
});
|
|
2410
|
+
webhooksCmd
|
|
2411
|
+
.command('unregister <id>')
|
|
2412
|
+
.description('Remover endpoint de webhook')
|
|
2413
|
+
.requiredOption('--product-id <id>', 'ID do produto')
|
|
2414
|
+
.option('--json', 'saída em JSON')
|
|
2415
|
+
.action(async (id, opts) => {
|
|
2416
|
+
const { runWebhooksUnregister } = await import('./commands/webhooks.js');
|
|
2417
|
+
await runWebhooksUnregister({ id, productId: opts.productId, json: !!opts.json });
|
|
2418
|
+
});
|
|
2419
|
+
webhooksCmd
|
|
2420
|
+
.command('test <id>')
|
|
2421
|
+
.description('Disparar evento de teste para um endpoint')
|
|
2422
|
+
.requiredOption('--product-id <id>', 'ID do produto')
|
|
2423
|
+
.option('--event <event>', 'evento específico a testar (default: primeiro inscrito)')
|
|
2424
|
+
.option('--json', 'saída em JSON')
|
|
2425
|
+
.action(async (id, opts) => {
|
|
2426
|
+
const { runWebhooksTest } = await import('./commands/webhooks.js');
|
|
2427
|
+
await runWebhooksTest({
|
|
2428
|
+
id,
|
|
2429
|
+
productId: opts.productId,
|
|
2430
|
+
event: opts.event,
|
|
2431
|
+
json: !!opts.json,
|
|
2432
|
+
});
|
|
2433
|
+
});
|
|
2434
|
+
webhooksCmd
|
|
2435
|
+
.command('deliveries <id>')
|
|
2436
|
+
.description('Histórico de entregas de um endpoint')
|
|
2437
|
+
.requiredOption('--product-id <id>', 'ID do produto')
|
|
2438
|
+
.option('--status <status>', 'filtrar por status: pending | delivering | delivered | failed | dead')
|
|
2439
|
+
.option('--limit <n>', 'máximo de eventos (1-100)', '20')
|
|
2440
|
+
.option('--json', 'saída em JSON')
|
|
2441
|
+
.action(async (id, opts) => {
|
|
2442
|
+
const { runWebhooksDeliveries } = await import('./commands/webhooks.js');
|
|
2443
|
+
await runWebhooksDeliveries({
|
|
2444
|
+
id,
|
|
2445
|
+
productId: opts.productId,
|
|
2446
|
+
status: opts.status,
|
|
2447
|
+
limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
|
|
2448
|
+
json: !!opts.json,
|
|
2449
|
+
});
|
|
2450
|
+
});
|
|
2364
2451
|
// ── neetru domains ──────────────────────────────────────────────────
|
|
2365
2452
|
// Devs/IAs gerem os domínios de ambiente dos seus produtos (lista/reconcile/retry).
|
|
2366
2453
|
const domainsCmd = program
|