@neetru/cli 2.12.5 → 2.12.7

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.
@@ -0,0 +1,367 @@
1
+ /**
2
+ * `neetru chat` — canal de chat leve dev→operador do Core.
3
+ *
4
+ * Devs e IAs que trabalham em produtos Neetru usam este canal pra enviar
5
+ * mensagens curtas ao operador do Core sem precisar abrir um bug pra cada
6
+ * detalhe. O histórico é rolante: apenas as últimas 24h são exibidas.
7
+ *
8
+ * Subcomandos:
9
+ * neetru chat send --product <slug> "<mensagem>"
10
+ * Posta uma mensagem no canal do produto. Identifica o ator como 'cli'
11
+ * (padrão); use --actor-type pra sinalizar 'agent_claude' ou 'human'.
12
+ *
13
+ * neetru chat history --product <slug> [--hours <1-24>]
14
+ * Exibe o histórico das últimas N horas (default: 24).
15
+ * Mensagens de devs em azul, do operador em verde.
16
+ *
17
+ * neetru chat --product <slug> (loop interativo)
18
+ * Mostra o histórico atual e entra num loop de envio interativo.
19
+ * Digite a mensagem e pressione Enter. Ctrl-C encerra.
20
+ *
21
+ * Endpoints:
22
+ * POST /api/cli/v1/chat { productSlug, message, actorType? }
23
+ * GET /api/cli/v1/chat?productSlug=<slug>&sinceHours=<n>
24
+ *
25
+ * Rate-limit (server-side): 30 mensagens/hora por (produto + ator).
26
+ */
27
+ import chalk from 'chalk';
28
+ import ora from 'ora';
29
+ import { createInterface } from 'node:readline';
30
+ import { apiRequest } from '../lib/api-client.js';
31
+ import { requireToken, handleApiError } from '../lib/cli-read.js';
32
+ import { log } from '../utils/logger.js';
33
+ import { safeExit } from '../utils/safe-exit.js';
34
+ import { fmtTimestamp } from '../lib/render.js';
35
+ // ─── Helpers de display ───────────────────────────────────────────────────────
36
+ function colorRole(role) {
37
+ if (role === 'operator')
38
+ return chalk.green('operador');
39
+ return chalk.blue('dev');
40
+ }
41
+ function actorLabel(msg) {
42
+ const email = msg.author?.email ?? msg.author?.actorId ?? '?';
43
+ const type = msg.author?.actorType ?? 'cli';
44
+ if (type === 'agent_claude')
45
+ return chalk.magenta(`[IA] ${email}`);
46
+ if (type === 'human')
47
+ return chalk.cyan(`[humano] ${email}`);
48
+ return chalk.dim(`[cli] ${email}`);
49
+ }
50
+ function printMessage(msg) {
51
+ const ts = fmtTimestamp(msg.createdAt);
52
+ const roleTag = colorRole(msg.role);
53
+ const actor = actorLabel(msg);
54
+ console.log(`${chalk.dim(ts)} ${roleTag} ${actor}`);
55
+ console.log(` ${msg.body}`);
56
+ console.log();
57
+ }
58
+ function channelTypeLabel(type) {
59
+ if (type === 'global')
60
+ return chalk.cyan('global');
61
+ if (type === 'product')
62
+ return chalk.blue('produto');
63
+ return chalk.dim('custom');
64
+ }
65
+ function printChannel(ch) {
66
+ const tag = channelTypeLabel(ch.type);
67
+ const slug = ch.productSlug ? chalk.dim(` (${ch.productSlug})`) : '';
68
+ const lastMsg = ch.lastMessageAt
69
+ ? chalk.dim(` última msg: ${fmtTimestamp(ch.lastMessageAt)}`)
70
+ : '';
71
+ console.log(` ${chalk.bold(ch.name)}${slug} ${tag} ${chalk.dim(ch.id)}${lastMsg}`);
72
+ }
73
+ /**
74
+ * Lista os canais de chat abertos.
75
+ *
76
+ * integrationNeeded — registrar em `registerChatCommand`:
77
+ * ```ts
78
+ * chatCmd.command('channels').action(async (opts) => runChatChannels(opts));
79
+ * ```
80
+ */
81
+ export async function runChatChannels(opts) {
82
+ const token = await requireToken(opts.json);
83
+ const spinner = opts.json ? null : ora({ text: 'Buscando canais…', color: 'blue' }).start();
84
+ let res;
85
+ try {
86
+ res = await apiRequest('/api/cli/v1/chat/channels', { token });
87
+ spinner?.stop();
88
+ }
89
+ catch (error) {
90
+ spinner?.fail('Falha ao buscar canais.');
91
+ handleApiError(error, opts.json);
92
+ return;
93
+ }
94
+ if (opts.json) {
95
+ console.log(JSON.stringify(res));
96
+ return;
97
+ }
98
+ console.log(chalk.bold(`\nCanais abertos (${res.count})\n`));
99
+ if (res.count === 0) {
100
+ log.dim('Nenhum canal encontrado.');
101
+ return;
102
+ }
103
+ for (const ch of res.channels)
104
+ printChannel(ch);
105
+ console.log();
106
+ }
107
+ export async function runChatSend(message, opts) {
108
+ const channelId = opts.channel?.trim();
109
+ const productSlug = opts.product?.trim();
110
+ if (!channelId && !productSlug) {
111
+ log.error('Informe --channel <id> (canais) ou --product <slug> (legado).');
112
+ safeExit(1);
113
+ }
114
+ if (!message || !message.trim()) {
115
+ log.error('Mensagem não pode ser vazia.');
116
+ safeExit(1);
117
+ }
118
+ const token = await requireToken(opts.json);
119
+ const spinner = opts.json
120
+ ? null
121
+ : ora({ text: 'Enviando mensagem…', color: 'blue' }).start();
122
+ // Monta o body: channelId tem precedência sobre productSlug (legado).
123
+ const body = {
124
+ message: message.trim(),
125
+ actorType: opts.actorType ?? 'cli',
126
+ };
127
+ if (channelId) {
128
+ body.channelId = channelId;
129
+ }
130
+ else {
131
+ body.productSlug = productSlug;
132
+ if (opts.productId)
133
+ body.productId = opts.productId;
134
+ }
135
+ let res;
136
+ try {
137
+ res = await apiRequest('/api/cli/v1/chat', {
138
+ method: 'POST',
139
+ token,
140
+ body,
141
+ });
142
+ spinner?.stop();
143
+ }
144
+ catch (error) {
145
+ spinner?.fail('Falha ao enviar mensagem.');
146
+ handleApiError(error, opts.json);
147
+ return;
148
+ }
149
+ if (opts.json) {
150
+ console.log(JSON.stringify(res));
151
+ return;
152
+ }
153
+ log.success(`Mensagem enviada (id: ${res.id}).`);
154
+ }
155
+ export async function runChatHistory(opts) {
156
+ const channelId = opts.channel?.trim();
157
+ const productSlug = opts.product?.trim();
158
+ if (!channelId && !productSlug) {
159
+ log.error('Informe --channel <id> (canais) ou --product <slug> (legado).');
160
+ safeExit(1);
161
+ }
162
+ const sinceHours = Math.min(Math.max(Number(opts.hours ?? '24'), 1), 24);
163
+ const token = await requireToken(opts.json);
164
+ const qs = new URLSearchParams({ sinceHours: String(sinceHours) });
165
+ if (channelId) {
166
+ qs.set('channelId', channelId);
167
+ }
168
+ else {
169
+ qs.set('productSlug', productSlug);
170
+ }
171
+ const spinner = opts.json ? null : ora({ text: 'Buscando histórico…', color: 'blue' }).start();
172
+ let res;
173
+ try {
174
+ res = await apiRequest(`/api/cli/v1/chat?${qs.toString()}`, { token });
175
+ spinner?.stop();
176
+ }
177
+ catch (error) {
178
+ spinner?.fail('Falha ao buscar histórico.');
179
+ handleApiError(error, opts.json);
180
+ return;
181
+ }
182
+ if (opts.json) {
183
+ console.log(JSON.stringify(res));
184
+ return;
185
+ }
186
+ const label = channelId ? `canal ${channelId}` : `produto ${productSlug}`;
187
+ console.log(chalk.bold(`\nHistórico — ${label} (últimas ${sinceHours}h · ${res.count} msg)\n`));
188
+ if (res.count === 0) {
189
+ log.dim('Nenhuma mensagem no período.');
190
+ return;
191
+ }
192
+ for (const msg of res.messages) {
193
+ printMessage(msg);
194
+ }
195
+ }
196
+ export async function runChatLoop(opts) {
197
+ const channelId = opts.channel?.trim();
198
+ const productSlug = opts.product?.trim();
199
+ if (!channelId && !productSlug) {
200
+ log.error('Informe --channel <id> (canais) ou --product <slug> (legado).');
201
+ safeExit(1);
202
+ }
203
+ const token = await requireToken(opts.json);
204
+ // Mostrar histórico ao entrar no loop
205
+ const qs = new URLSearchParams({ sinceHours: '24' });
206
+ if (channelId) {
207
+ qs.set('channelId', channelId);
208
+ }
209
+ else {
210
+ qs.set('productSlug', productSlug);
211
+ }
212
+ const label = channelId ? `canal ${channelId}` : `produto ${productSlug}`;
213
+ try {
214
+ const res = await apiRequest(`/api/cli/v1/chat?${qs.toString()}`, { token });
215
+ if (res.count > 0) {
216
+ console.log(chalk.bold(`\nHistórico — ${label} (últimas 24h · ${res.count} msg)\n`));
217
+ for (const msg of res.messages)
218
+ printMessage(msg);
219
+ }
220
+ else {
221
+ console.log(chalk.dim(`\nNenhuma mensagem recente para ${label}.\n`));
222
+ }
223
+ }
224
+ catch {
225
+ // Histórico falhou — continua pro loop de envio mesmo assim
226
+ console.log(chalk.dim('(falha ao carregar histórico — continuando mesmo assim)\n'));
227
+ }
228
+ if (!process.stdin.isTTY) {
229
+ log.error('Terminal não-interativo — use `neetru chat send` para enviar mensagens.');
230
+ safeExit(1);
231
+ }
232
+ console.log(chalk.dim('Digite sua mensagem e pressione Enter. Linha em branco cancela. Ctrl-C encerra.\n'));
233
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
234
+ const askLine = () => new Promise((resolve) => {
235
+ rl.question(chalk.cyan('> '), (answer) => resolve(answer.trim()));
236
+ });
237
+ // eslint-disable-next-line no-constant-condition
238
+ while (true) {
239
+ let line;
240
+ try {
241
+ line = await askLine();
242
+ }
243
+ catch {
244
+ break;
245
+ }
246
+ if (!line) {
247
+ console.log(chalk.dim('(loop encerrado)'));
248
+ break;
249
+ }
250
+ const spinner = ora({ text: 'Enviando…', color: 'blue' }).start();
251
+ const msgBody = {
252
+ message: line,
253
+ actorType: opts.actorType ?? 'cli',
254
+ };
255
+ if (channelId) {
256
+ msgBody.channelId = channelId;
257
+ }
258
+ else {
259
+ msgBody.productSlug = productSlug;
260
+ }
261
+ try {
262
+ const res = await apiRequest('/api/cli/v1/chat', {
263
+ method: 'POST',
264
+ token,
265
+ body: msgBody,
266
+ });
267
+ spinner.stop();
268
+ log.success(`Enviado (id: ${res.id})`);
269
+ }
270
+ catch (error) {
271
+ spinner.fail('Falha ao enviar.');
272
+ if (error instanceof Error) {
273
+ console.error(chalk.red(` ${error.message}`));
274
+ }
275
+ // Não encerra o loop — permite tentar de novo
276
+ }
277
+ }
278
+ rl.close();
279
+ }
280
+ // ─── Registro do comando `chat` em program ────────────────────────────────────
281
+ /**
282
+ * Registra o comando `neetru chat` no programa Commander.
283
+ *
284
+ * Subcomandos:
285
+ * neetru chat channels → lista canais abertos
286
+ * neetru chat send --channel <id> "msg" → envia mensagem num canal
287
+ * neetru chat send --product <slug> "msg" → legado (por produto)
288
+ * neetru chat history --channel <id> → histórico de canal
289
+ * neetru chat history --product <slug> → histórico legado
290
+ * neetru chat --channel <id> → loop interativo de canal
291
+ * neetru chat --product <slug> → loop interativo legado
292
+ *
293
+ * integrationNeeded — adicionar em src/index.ts (ou onde os subcomandos
294
+ * são registrados):
295
+ * ```ts
296
+ * import { registerChatCommand } from './commands/chat.js';
297
+ * // ...
298
+ * registerChatCommand(program);
299
+ * ```
300
+ */
301
+ export function registerChatCommand(program) {
302
+ const chatCmd = program
303
+ .command('chat')
304
+ .description('Canais de chat dev→operador do Core (histórico rolante de 24h). ' +
305
+ 'Use --channel <id> pra entrar num canal (ou --product <slug> legado).')
306
+ .option('--channel <id>', 'id do canal (ex: global)')
307
+ .option('--product <slug>', 'slug do produto — legado (retrocompat)')
308
+ .option('--actor-type <tipo>', 'tipo do ator: cli|human|agent_claude (default: cli)', 'cli')
309
+ .option('--json', 'saída em JSON')
310
+ .action(async (cmdOpts) => {
311
+ if (!cmdOpts.channel && !cmdOpts.product) {
312
+ log.error('Informe --channel <id> (canais) ou --product <slug> (legado).');
313
+ log.dim(' Use: neetru chat --channel global');
314
+ log.dim(' Ou: neetru chat channels (para listar canais)');
315
+ safeExit(1);
316
+ }
317
+ await runChatLoop({
318
+ channel: cmdOpts.channel,
319
+ product: cmdOpts.product,
320
+ actorType: cmdOpts.actorType,
321
+ json: !!cmdOpts.json,
322
+ });
323
+ });
324
+ // ── chat channels ──────────────────────────────────────────────────────────
325
+ chatCmd
326
+ .command('channels')
327
+ .description('Listar canais de chat abertos (inclui o canal Global)')
328
+ .option('--json', 'saída em JSON')
329
+ .action(async (cmdOpts) => {
330
+ await runChatChannels({ json: !!cmdOpts.json });
331
+ });
332
+ // ── chat send ──────────────────────────────────────────────────────────────
333
+ chatCmd
334
+ .command('send <mensagem>')
335
+ .description('Postar uma mensagem num canal')
336
+ .option('--channel <id>', 'id do canal de destino (ex: global)')
337
+ .option('--product <slug>', 'slug do produto — legado (retrocompat)')
338
+ .option('--actor-type <tipo>', 'tipo do ator: cli|human|agent_claude (default: cli)', 'cli')
339
+ .option('--product-id <id>', 'id interno do produto (opcional, legado)')
340
+ .option('--json', 'saída em JSON')
341
+ .action(async (mensagem, cmdOpts) => {
342
+ await runChatSend(mensagem, {
343
+ channel: cmdOpts.channel,
344
+ product: cmdOpts.product,
345
+ actorType: cmdOpts.actorType,
346
+ productId: cmdOpts.productId,
347
+ json: !!cmdOpts.json,
348
+ });
349
+ });
350
+ // ── chat history ───────────────────────────────────────────────────────────
351
+ chatCmd
352
+ .command('history')
353
+ .description('Exibir histórico de um canal (últimas N horas, default: 24)')
354
+ .option('--channel <id>', 'id do canal (ex: global)')
355
+ .option('--product <slug>', 'slug do produto — legado (retrocompat)')
356
+ .option('--hours <n>', 'janela de histórico em horas (1–24, default: 24)', '24')
357
+ .option('--json', 'saída em JSON')
358
+ .action(async (cmdOpts) => {
359
+ await runChatHistory({
360
+ channel: cmdOpts.channel,
361
+ product: cmdOpts.product,
362
+ hours: cmdOpts.hours,
363
+ json: !!cmdOpts.json,
364
+ });
365
+ });
366
+ }
367
+ //# sourceMappingURL=chat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.js","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"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"}
@@ -113,3 +113,22 @@ export interface DbHostsOptions {
113
113
  * ficará ausente/vazio até o Core implementar o enriquecimento.
114
114
  */
115
115
  export declare function runDbHosts(opts?: DbHostsOptions): Promise<void>;
116
+ export interface DbMigrationsCancelOptions {
117
+ json?: boolean;
118
+ }
119
+ /**
120
+ * Cancela uma migração presa em `awaiting_confirmation` ou `pending`,
121
+ * transitando-a para `cancelled` (bug_728fa11c fix).
122
+ *
123
+ * Casos de uso:
124
+ * - Migração classificada como destrutiva pelo classifier ANTIGO — com o
125
+ * db-classifier 0.1.2 seria aditiva, mas continua presa indefinidamente.
126
+ * - Dev quer descartar a migração e submeter uma corrigida.
127
+ *
128
+ * NÃO aplica nada — apenas muda o status para `cancelled`. A migração fica
129
+ * registrada para rastreabilidade de auditoria. Após cancelar, rode
130
+ * `neetru db apply` para submeter uma nova migração.
131
+ *
132
+ * Roles: admin ou operator.
133
+ */
134
+ export declare function runDbMigrationsCancel(migrationId: string, opts?: DbMigrationsCancelOptions): Promise<void>;
@@ -11,6 +11,7 @@
11
11
  * neetru db apply [--dry-run] — pipeline de migração + envia ao Core
12
12
  * neetru db migrations list [--db <id>]
13
13
  * neetru db migrations confirm <id> — confirma migração destrutiva (exige --mfa)
14
+ * neetru db migrations cancel <id> — cancela migração presa em awaiting_confirmation ou pending
14
15
  *
15
16
  * Todos os comandos que chamam o Core usam `apiRequest` do `api-client` —
16
17
  * autenticação via Bearer token resolvido automaticamente do config local.
@@ -1167,8 +1168,76 @@ export async function runDbHosts(opts = {}) {
1167
1168
  { header: 'Bancos', value: (r) => dbDensityLabel(r.databases), maxWidth: 50 },
1168
1169
  ], resp.servers);
1169
1170
  }
1171
+ /**
1172
+ * Cancela uma migração presa em `awaiting_confirmation` ou `pending`,
1173
+ * transitando-a para `cancelled` (bug_728fa11c fix).
1174
+ *
1175
+ * Casos de uso:
1176
+ * - Migração classificada como destrutiva pelo classifier ANTIGO — com o
1177
+ * db-classifier 0.1.2 seria aditiva, mas continua presa indefinidamente.
1178
+ * - Dev quer descartar a migração e submeter uma corrigida.
1179
+ *
1180
+ * NÃO aplica nada — apenas muda o status para `cancelled`. A migração fica
1181
+ * registrada para rastreabilidade de auditoria. Após cancelar, rode
1182
+ * `neetru db apply` para submeter uma nova migração.
1183
+ *
1184
+ * Roles: admin ou operator.
1185
+ */
1186
+ export async function runDbMigrationsCancel(migrationId, opts = {}) {
1187
+ if (!opts.json)
1188
+ log.banner();
1189
+ if (!migrationId) {
1190
+ const msg = '<migrationId> é obrigatório.';
1191
+ if (opts.json) {
1192
+ jsonError(msg, 'missing_migration_id');
1193
+ }
1194
+ else {
1195
+ log.error(msg);
1196
+ }
1197
+ safeExit(1);
1198
+ return;
1199
+ }
1200
+ const spinner = opts.json
1201
+ ? null
1202
+ : ora({ text: `Cancelando migração ${migrationId}…`, color: 'yellow' }).start();
1203
+ let resp;
1204
+ try {
1205
+ resp = await apiRequest(`/api/cli/v1/db/migrations/${encodeURIComponent(migrationId)}/cancel`, { method: 'POST' });
1206
+ spinner?.succeed(`Migração ${chalk.bold(resp.migrationId)} cancelada — status: ${resp.status}`);
1207
+ }
1208
+ catch (err) {
1209
+ spinner?.fail('Falha ao cancelar migração.');
1210
+ if (err instanceof CliApiError && err.status === 409) {
1211
+ const msg = `Não foi possível cancelar: ${err.message}\n` +
1212
+ `Apenas migrações em "pending" ou "awaiting_confirmation" podem ser canceladas.`;
1213
+ if (opts.json) {
1214
+ console.log(JSON.stringify({ ok: false, error: 'migration_not_cancellable', message: msg }));
1215
+ safeExit(4);
1216
+ }
1217
+ else {
1218
+ log.error(msg);
1219
+ safeExit(4);
1220
+ }
1221
+ return;
1222
+ }
1223
+ if (opts.json) {
1224
+ jsonError(err.message ?? String(err), 'cancel_failed');
1225
+ safeExit(4);
1226
+ }
1227
+ handleApiError(err);
1228
+ return;
1229
+ }
1230
+ if (opts.json) {
1231
+ console.log(JSON.stringify(resp));
1232
+ return;
1233
+ }
1234
+ log.dim('');
1235
+ log.dim(` Migração cancelada: ${chalk.bold(migrationId)}`);
1236
+ log.dim(` Para reenviar: neetru db apply`);
1237
+ }
1170
1238
  // ── D-3: runDbMigrate e runDbSeed removidos (legado Sprint 10) ──────────────
1171
1239
  // Esses comandos operavam via schema.manifest.json (Sprint 10) e foram cortados
1172
1240
  // junto com o branch legado do `db init`. O fluxo canônico é:
1173
1241
  // neetru db init → neetru db apply → neetru db migrations confirm (se destrutiva)
1242
+ // → neetru db migrations cancel (se presa em awaiting_confirmation)
1174
1243
  //# sourceMappingURL=db.js.map