@danhachuel/leadmaster 0.1.7 → 0.1.9

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.
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
7
7
  <title>LeadMaster — Painel</title>
8
- <script type="module" crossorigin src="/assets/index-0xtgAs3t.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-pSbjfRli.css">
8
+ <script type="module" crossorigin src="/assets/index-ZsRPRMCF.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-DVFFIa5O.css">
10
10
  </head>
11
11
  <body class="bg-gray-100 min-h-screen">
12
12
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/leadmaster",
3
- "version": "0.1.07",
3
+ "version": "0.1.09",
4
4
  "description": "SaaS local de prospecção e automação: CRM, extratores de leads e agentes de IA — 100% local.",
5
5
  "license": "MIT",
6
6
  "author": "Dan Hachuel",
package/scripts/cli.mjs CHANGED
@@ -23,7 +23,7 @@
23
23
  */
24
24
 
25
25
  import { spawn, spawnSync } from 'node:child_process';
26
- import { existsSync, mkdirSync, readFileSync } from 'node:fs';
26
+ import { existsSync, mkdirSync, readFileSync, appendFileSync } from 'node:fs';
27
27
  import { homedir, platform } from 'node:os';
28
28
  import net from 'node:net';
29
29
  import path from 'node:path';
@@ -282,6 +282,36 @@ async function procurarInstanciaAtiva(portaInicial, maxTentativas = 10) {
282
282
  return null;
283
283
  }
284
284
 
285
+ /**
286
+ * Guarda da consola (anti-EPIPE): se o terminal for fechado (MobaXterm, VS
287
+ * Code), escrever em stdout/stderr gera EPIPE — que o Node trataria como
288
+ * uncaughtException, matando o launcher. Esta guarda:
289
+ * - ignora erros com code EPIPE no evento 'error' do stream;
290
+ * - espelha toda a saída em storage/logs/launcher_console.log;
291
+ * - se a escrita original falhar, retorna true silenciosamente.
292
+ * NUNCA REMOVER (regra inviolável do AGENTS.md).
293
+ */
294
+ function guardConsoleStream(stream, logFile) {
295
+ stream.on('error', (err) => {
296
+ if (err && err.code === 'EPIPE') return; // terminal fechado: ignora
297
+ return; // demais erros de consola nunca devem derrubar o launcher
298
+ });
299
+ const escritaOriginal = stream.write.bind(stream);
300
+ stream.write = (chunk, encoding, callback) => {
301
+ try {
302
+ appendFileSync(logFile, typeof chunk === 'string' ? chunk : Buffer.from(chunk));
303
+ } catch {
304
+ /* espelho de log é best-effort */
305
+ }
306
+ try {
307
+ return escritaOriginal(chunk, encoding, callback);
308
+ } catch {
309
+ return true; // escrita silenciosa sem exceção
310
+ }
311
+ };
312
+ return stream;
313
+ }
314
+
285
315
  // ---------------------------------------------------------------------------
286
316
  // Comandos
287
317
  // ---------------------------------------------------------------------------
@@ -416,9 +446,6 @@ function cmdStart(args) {
416
446
  child.kill('SIGTERM');
417
447
  }
418
448
  };
419
- process.on('SIGINT', () => { stop(); process.exit(0); });
420
- process.on('SIGTERM', () => { stop(); process.exit(0); });
421
- if (platform() === 'win32') process.on('SIGHUP', () => { stop(); process.exit(0); });
422
449
 
423
450
  console.log(`→ Subindo servidor local em ${url} ...`);
424
451
  const health = await waitForHealth(porta, 120000);
@@ -428,11 +455,39 @@ function cmdStart(args) {
428
455
  stop();
429
456
  process.exit(1);
430
457
  }
458
+
459
+ // -------------------------------------------------------------------------
460
+ // Sinais (regra inviolável do AGENTS.md)
461
+ // MobaXterm envia SIGINT/SIGHUP espúrios (perda de foco, minimização): no
462
+ // Windows esses sinais são APENAS ABSORVIDOS (log), sem encerrar o launcher,
463
+ // para o app não morrer sozinho. SIGTERM e SIGBREAK são paradas legítimas e
464
+ // sempre encerram. No Linux/macOS, SIGINT/SIGHUP mantêm o stop gracioso.
465
+ // -------------------------------------------------------------------------
466
+ const ehWindows = platform() === 'win32';
467
+ const naoAbsorver = () => { stop(); process.exit(0); };
468
+ const absorverWindows = (nome) => () => {
469
+ console.log(`[LAUNCHER] Sinal absorvido no Windows (${nome}) — o LeadMaster continua rodando.`);
470
+ };
471
+ process.on('SIGINT', ehWindows ? absorverWindows('SIGINT') : naoAbsorver);
472
+ process.on('SIGHUP', ehWindows ? absorverWindows('SIGHUP') : naoAbsorver);
473
+ process.on('SIGTERM', naoAbsorver);
474
+ if (ehWindows) {
475
+ // SIGBREAK (Ctrl+Break): parada legítima também no Windows.
476
+ process.on('SIGBREAK', naoAbsorver);
477
+ }
478
+
479
+ // Keep-alive: mantém o event loop ativo mesmo sem I/O de console (MobaXterm
480
+ // minimizado / sem stream ativo) — evita o encerramento silencioso.
481
+ setInterval(() => {}, 1000);
482
+
431
483
  console.log(`✓ LeadMaster no ar em ${url} (dados: ${home})`);
432
484
  console.log('');
433
485
  console.log(`ℹ Abra ${url} no navegador quando quiser — o LeadMaster nunca abre janelas sozinho.`);
434
- console.log('');
435
- console.log('Pressione Ctrl+C para encerrar.');
486
+ console.log(
487
+ ehWindows
488
+ ? 'ℹ Para encerrar: Ctrl+Break ou feche a janela (Ctrl+C é absorvido no Windows contra sinais espúrios do MobaXterm).'
489
+ : 'ℹ Para encerrar: Ctrl+C.',
490
+ );
436
491
  })().catch((err) => {
437
492
  console.error('✗ Erro inesperado ao iniciar:', err && err.message ? err.message : err);
438
493
  process.exit(1);
@@ -498,6 +553,15 @@ const rawArgs = process.argv.slice(2);
498
553
  const args = parseArgs(rawArgs);
499
554
  const command = args._[0] || 'start';
500
555
 
556
+ // Guarda da consola (anti-EPIPE) aplicada ANTES de qualquer escrita do
557
+ // launcher: espelha stdout/stderr em storage/logs/launcher_console.log e
558
+ // ignora EPIPE (terminal fechado) — regra inviolável do AGENTS.md.
559
+ const homeLauncher = resolveHome(args);
560
+ const LOG_DIR = path.join(homeLauncher, 'storage', 'logs');
561
+ mkdirSync(LOG_DIR, { recursive: true });
562
+ const CONSOLE_LOG = path.join(LOG_DIR, 'launcher_console.log');
563
+ [process.stdout, process.stderr].forEach((stream) => guardConsoleStream(stream, CONSOLE_LOG));
564
+
501
565
  switch (command) {
502
566
  case 'install': cmdInstall(args); break;
503
567
  case 'start': cmdStart(args); break;