@danhachuel/thunderbolt 0.5.82 → 0.5.84

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/app/main.py CHANGED
@@ -1494,6 +1494,23 @@ def _growth_channel_metrics(channel: dict[str, Any]) -> tuple[list[dict[str, Any
1494
1494
  def render_growth_youtube():
1495
1495
  st.title("Analista Growth Youtube")
1496
1496
  st.caption("Auditoria pública baseada nos 3 pilares críticos e nas métricas secundárias do agente Growth.")
1497
+ growth_settings = read_json("settings.json", {})
1498
+ youtube_data_api_ready = bool(str(growth_settings.get("youtube_api_key") or os.getenv("YOUTUBE_API_KEY") or "").strip())
1499
+ growth_accounts = growth_settings.get("youtube_batch_accounts", [])
1500
+ analytics_accounts_ready = False
1501
+ if isinstance(growth_accounts, list):
1502
+ for growth_account in growth_accounts:
1503
+ if isinstance(growth_account, dict) and youtube_batch_account_status(growth_account, STORAGE).ok:
1504
+ analytics_accounts_ready = True
1505
+ break
1506
+ st.markdown("**Estado das APIs de Growth**")
1507
+ api_status_cols = st.columns(2, gap="small")
1508
+ with api_status_cols[0]:
1509
+ st.caption("YouTube Data API v3: Metadados públicos")
1510
+ _api_status_badge("Configured" if youtube_data_api_ready else "Missing configuration", "ready" if youtube_data_api_ready else "missing")
1511
+ with api_status_cols[1]:
1512
+ st.caption("YouTube Analytics API: Métricas internas")
1513
+ _api_status_badge("Configured" if analytics_accounts_ready else "Missing configuration", "ready" if analytics_accounts_ready else "missing")
1497
1514
  channels = [item for item in read_json("channels.json", []) if isinstance(item, dict) and item.get("active", True) and is_youtube_channel_record(item)]
1498
1515
  if not channels:
1499
1516
  st.info("Cadastre pelo menos um canal YouTube em Canais YouTube para iniciar a análise.")
@@ -268,6 +268,20 @@ def _update(task_id: str, **updates: Any) -> dict[str, Any]:
268
268
  if str(current.get("state") or "") in {"blocked", "cancelled"}:
269
269
  raise PipelineStopped("A tarefa foi parada pelo utilizador.")
270
270
  updates = dict(updates)
271
+ if "progress" in updates:
272
+ try:
273
+ previous_progress = max(0, min(100, int(current.get("progress") or 0)))
274
+ except (TypeError, ValueError):
275
+ previous_progress = 0
276
+ try:
277
+ requested_progress = max(0, min(100, int(updates.get("progress") or 0)))
278
+ except (TypeError, ValueError):
279
+ requested_progress = previous_progress
280
+ # A geração pode ser retomada a partir de um checkpoint avançado,
281
+ # enquanto o subprocesso expõe uma faixa própria para a etapa actual.
282
+ # Nunca deixar a percentagem persistida recuar evita a oscilação visual
283
+ # e mantém o card fiel ao maior avanço já confirmado.
284
+ updates["progress"] = max(previous_progress, requested_progress)
271
285
  updates["orchestration"] = _cascade_metadata(current, updates)
272
286
  updated = update_task(task_id, updates)
273
287
  if not updated:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.5.82",
3
+ "version": "0.5.84",
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",