@danhachuel/thunderbolt 0.5.74 → 0.5.76

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 (2) hide show
  1. package/package.json +1 -1
  2. package/scripts/cli.mjs +53 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.5.74",
3
+ "version": "0.5.76",
4
4
  "description": "Thunderbolt — interface local para operação de canais faceless e motor MoneyPrinterTurbo",
5
5
  "license": "MIT",
6
6
  "main": "scripts/cli.mjs",
package/scripts/cli.mjs CHANGED
@@ -79,7 +79,9 @@ function hasScheduledAutomation() {
79
79
 
80
80
  function hasPendingPipelineWork() {
81
81
  const tasks = readStateJson("tasks.json", []);
82
- return Array.isArray(tasks) && tasks.some((task) => ["queued", "pending", "processing", "running", "in_progress"].includes(String(task?.state || task?.status || "").toLowerCase()));
82
+ // A UI Start coloca a tarefa em `doing`; `to_do` também é consumido pelo
83
+ // pipeline worker. Estes estados não dependem de automação de canal activa.
84
+ return Array.isArray(tasks) && tasks.some((task) => ["to_do", "doing"].includes(String(task?.state || task?.status || "").toLowerCase()));
83
85
  }
84
86
 
85
87
  function ensureRuntimeStorage() {
@@ -276,15 +278,17 @@ proxy.on("error", (error) => {
276
278
  proxy.listen(publicPort, "127.0.0.1", () => {
277
279
  console.log(`Thunderbolt: interface disponível em http://localhost:${publicPort}/`);
278
280
  });
279
- // A worker de automação é necessária para canais com automação activa. É
280
- // iniciada no máximo uma vez por launcher e reiniciada com backoff se terminar
281
- // inesperadamente; não é criada quando não há trabalho agendado.
281
+ // Os workers são geridos pelo launcher para que um clique em Start na UI
282
+ // também active o processamento, mesmo sem automação de canal configurada.
283
+ // Cada worker só existe enquanto a respectiva fila tiver trabalho pendente.
282
284
  let worker = null;
283
- const pipelineWorker = null;
285
+ let pipelineWorker = null;
284
286
  const restartExitCode = 75;
285
287
  let shuttingDown = false;
286
288
  let child;
287
289
  let workerRestartTimer = null;
290
+ let pipelineRestartTimer = null;
291
+ let workerMonitorTimer = null;
288
292
 
289
293
  function startAutomationWorker() {
290
294
  if (shuttingDown || worker || !hasScheduledAutomation()) return;
@@ -308,6 +312,44 @@ function startAutomationWorker() {
308
312
  });
309
313
  }
310
314
 
315
+ function stopPipelineWorker() {
316
+ if (pipelineRestartTimer) {
317
+ clearTimeout(pipelineRestartTimer);
318
+ pipelineRestartTimer = null;
319
+ }
320
+ if (pipelineWorker && !pipelineWorker.killed) pipelineWorker.kill();
321
+ pipelineWorker = null;
322
+ }
323
+
324
+ function startPipelineWorker() {
325
+ if (shuttingDown || pipelineWorker || !hasPendingPipelineWork()) return;
326
+ pipelineWorker = spawn(python, ["-m", "hermes_ui.pipeline_worker"], {
327
+ cwd: root,
328
+ stdio: "inherit",
329
+ env: runtimeEnv,
330
+ windowsHide: false,
331
+ });
332
+ pipelineWorker.on("error", (error) => console.error(`Thunderbolt pipeline worker: ${error.message}`));
333
+ pipelineWorker.on("exit", (code, signal) => {
334
+ pipelineWorker = null;
335
+ if (shuttingDown) return;
336
+ console.error(`Thunderbolt pipeline worker: terminou (código ${code ?? "-"}, sinal ${signal ?? "-"}).`);
337
+ if (hasPendingPipelineWork()) {
338
+ pipelineRestartTimer = setTimeout(() => {
339
+ pipelineRestartTimer = null;
340
+ startPipelineWorker();
341
+ }, 5000);
342
+ }
343
+ });
344
+ }
345
+
346
+ function monitorWorkers() {
347
+ if (shuttingDown) return;
348
+ if (hasPendingPipelineWork()) startPipelineWorker();
349
+ else if (pipelineWorker) stopPipelineWorker();
350
+ startAutomationWorker();
351
+ }
352
+
311
353
  function startStreamlit() {
312
354
  child = spawn(python, [streamlitBootstrap, "run", main, "--server.port", String(backendPort), "--server.address", "127.0.0.1"], {
313
355
  cwd: root,
@@ -347,10 +389,13 @@ const stopWorker = () => {
347
389
  shuttingDown = true;
348
390
  proxy.close();
349
391
  if (workerRestartTimer) clearTimeout(workerRestartTimer);
392
+ if (workerMonitorTimer) clearInterval(workerMonitorTimer);
350
393
  if (worker && !worker.killed) worker.kill();
351
- if (pipelineWorker && !pipelineWorker.killed) pipelineWorker.kill();
394
+ stopPipelineWorker();
352
395
  };
353
396
  process.on("SIGINT", stopWorker);
354
397
  process.on("SIGTERM", stopWorker);
355
- pipelineWorker?.on("error", (error) => console.error(`Thunderbolt pipeline worker: ${error.message}`));
356
- startAutomationWorker();
398
+ monitorWorkers();
399
+ // O launcher verifica apenas os JSONs locais; não existe timer no Streamlit
400
+ // nem worker persistente quando não há tarefa pendente.
401
+ workerMonitorTimer = setInterval(monitorWorkers, 2000);