@danhachuel/thunderbolt 0.7.33 → 0.7.34

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.
@@ -378,6 +378,10 @@ def seed_blueprints() -> None:
378
378
  shutil.copy2(source, target)
379
379
  thumbnail_destination = BLUEPRINTS / "thumbnails"
380
380
  thumbnail_destination.mkdir(parents=True, exist_ok=True)
381
+ legacy_thumbnail = thumbnail_destination / "FINANCE_Thumbnail_Blueprint.md"
382
+ current_thumbnail = thumbnail_destination / "FINANCE USA_Thumbnail_Blueprint.md"
383
+ if legacy_thumbnail.is_file() and not current_thumbnail.exists():
384
+ legacy_thumbnail.replace(current_thumbnail)
381
385
  for source in sorted(SEED_THUMBNAIL_BLUEPRINTS.glob("*.md")):
382
386
  target = thumbnail_destination / source.name
383
387
  if not target.exists():
@@ -397,6 +401,82 @@ def seed_blueprints() -> None:
397
401
  shutil.copy2(pair_source, pair_target)
398
402
 
399
403
 
404
+ def _rename_pending_finance_thumbnail_references(value: Any) -> tuple[Any, bool]:
405
+ """Rename the legacy finance thumbnail only in unfinished work items."""
406
+ legacy = "FINANCE_Thumbnail_Blueprint"
407
+ current = "FINANCE USA_Thumbnail_Blueprint"
408
+ changed = False
409
+
410
+ def replace(item: Any) -> Any:
411
+ nonlocal changed
412
+ if isinstance(item, str):
413
+ if item == legacy:
414
+ changed = True
415
+ return current
416
+ return item
417
+ if isinstance(item, list):
418
+ return [replace(entry) for entry in item]
419
+ if isinstance(item, dict):
420
+ return {key: replace(entry) for key, entry in item.items()}
421
+ return item
422
+
423
+ return replace(value), changed
424
+
425
+
426
+ def migrate_finance_thumbnail_blueprint() -> None:
427
+ """Move pending automation/video records to the renamed finance thumbnail."""
428
+ def load_state(name: str, default: Any) -> Any:
429
+ path = STATE / name
430
+ try:
431
+ return _load_json_unlocked(path)
432
+ except (OSError, json.JSONDecodeError):
433
+ return default
434
+
435
+ def save_state(name: str, value: Any) -> None:
436
+ _atomic_write_unlocked(STATE / name, value)
437
+
438
+ channels = load_state("channels.json", [])
439
+ if isinstance(channels, list):
440
+ updated_channels, channels_changed = _rename_pending_finance_thumbnail_references(channels)
441
+ if channels_changed:
442
+ save_state("channels.json", updated_channels)
443
+
444
+ tasks = load_state("tasks.json", [])
445
+ if isinstance(tasks, list):
446
+ pending_tasks = []
447
+ tasks_changed = False
448
+ for task in tasks:
449
+ if not isinstance(task, dict) or bool(task.get("video_ready")) or str(task.get("state") or "").casefold() == "done":
450
+ pending_tasks.append(task)
451
+ continue
452
+ updated_task, changed = _rename_pending_finance_thumbnail_references(task)
453
+ pending_tasks.append(updated_task)
454
+ tasks_changed = tasks_changed or changed
455
+ if tasks_changed:
456
+ save_state("tasks.json", pending_tasks)
457
+
458
+ batches = load_state("batches.json", [])
459
+ if isinstance(batches, list):
460
+ pending_batches = []
461
+ batches_changed = False
462
+ for batch in batches:
463
+ status = str(batch.get("status") or batch.get("state") or "").casefold() if isinstance(batch, dict) else ""
464
+ if status in {"done", "completed", "published", "cancelled", "canceled"}:
465
+ pending_batches.append(batch)
466
+ continue
467
+ updated_batch, changed = _rename_pending_finance_thumbnail_references(batch)
468
+ pending_batches.append(updated_batch)
469
+ batches_changed = batches_changed or changed
470
+ if batches_changed:
471
+ save_state("batches.json", pending_batches)
472
+
473
+ queues = load_state("queues.json", {})
474
+ if isinstance(queues, dict):
475
+ updated_queues, queues_changed = _rename_pending_finance_thumbnail_references(queues)
476
+ if queues_changed:
477
+ save_state("queues.json", updated_queues)
478
+
479
+
400
480
  def seed_prompt_masters() -> None:
401
481
  """Copy packaged TikTok Prompt Masters without overwriting user files."""
402
482
  if not SEED_TIKTOK_PROMPT_MASTERS.exists():
@@ -444,6 +524,7 @@ def ensure_storage() -> None:
444
524
  target = STATE / filename
445
525
  if not target.exists():
446
526
  atomic_write(target, default)
527
+ migrate_finance_thumbnail_blueprint()
447
528
 
448
529
 
449
530
  _LOCK_TIMEOUT_SECONDS = 30.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.7.33",
3
+ "version": "0.7.34",
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",