@danhachuel/thunderbolt 0.5.96 → 0.5.98

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
@@ -7982,8 +7982,8 @@ def render_settings():
7982
7982
  composio_user_id = st.text_input("Composio user ID", value=str(settings.get("composio_user_id") or "thunderbolt-local"), key="upload_composio_user_id", help="Identidade estável usada para associar as contas conectadas no Composio.")
7983
7983
  composio_toolkit = st.text_input("Toolkit preferido (opcional)", value=str(settings.get("composio_toolkit") or ""), key="upload_composio_toolkit", help="Deixe vazio para descobrir ferramentas em todos os toolkits.")
7984
7984
  composio_tool_slug = st.text_input("Slug da ferramenta para Automação Youtube", value=str(settings.get("composio_tool_slug") or ""), key="upload_composio_tool_slug", help="Depois de descobrir uma ferramenta em Upload via Composio, copie aqui o slug para a Automação Youtube.")
7985
- composio_file_field = st.text_input("Campo do ficheiro na ferramenta", value=str(settings.get("composio_file_field") or "file"), key="upload_composio_file_field")
7986
- composio_channel_field = st.text_input("Campo do canal na ferramenta", value=str(settings.get("composio_channel_field") or "channel_id"), key="upload_composio_channel_field", help="O worker substitui este campo pelo YouTube channel ID do canal da tarefa, impedindo o envio para outro canal.")
7985
+ composio_file_field = st.text_input("Campo do ficheiro na ferramenta", value=str(settings.get("composio_file_field") or "videoFilePath"), key="upload_composio_file_field", help="Para YOUTUBE_UPLOAD_VIDEO use videoFilePath; o Composio envia para o canal da conta YouTube ligada.")
7986
+ composio_channel_field = st.text_input("Campo do canal na ferramenta (opcional)", value=str(settings.get("composio_channel_field") or ""), key="upload_composio_channel_field", help="As ferramentas YouTube actuais usam a conta ligada e não aceitam channel_id. Preencha apenas se a ferramenta descoberta exigir explicitamente esse campo.")
7987
7987
  composio_privacy_field = st.text_input("Campo de privacidade", value=str(settings.get("composio_privacy_field") or "privacy_status"), key="upload_composio_privacy_field", help="Recebe sempre `unlisted` (Não listado), como no upload oficial.")
7988
7988
  composio_category_field = st.text_input("Campo de categoria", value=str(settings.get("composio_category_field") or "category_id"), key="upload_composio_category_field", help="Recebe sempre `22`, categoria Pessoas e blogs, como no upload oficial.")
7989
7989
  composio_language_field = st.text_input("Campo de idioma", value=str(settings.get("composio_language_field") or "language"), key="upload_composio_language_field", help="Recebe o locale do idioma do vídeo/canal, por exemplo `pt-BR`.")
@@ -7995,7 +7995,7 @@ def render_settings():
7995
7995
  with composio_action_cols[1]:
7996
7996
  test_composio = st.button("Testar configuração", use_container_width=True, key="upload_composio_test")
7997
7997
  if save_composio:
7998
- settings.update({"composio_enabled": bool(composio_enabled), "composio_auto_upload": bool(composio_auto_upload), "composio_api_key": composio_api_key.strip(), "composio_user_id": composio_user_id.strip() or "thunderbolt-local", "composio_toolkit": composio_toolkit.strip(), "composio_tool_slug": composio_tool_slug.strip(), "composio_file_field": composio_file_field.strip() or "file", "composio_channel_field": composio_channel_field.strip() or "channel_id", "composio_privacy_field": composio_privacy_field.strip() or "privacy_status", "composio_category_field": composio_category_field.strip() or "category_id", "composio_language_field": composio_language_field.strip() or "language", "composio_arguments_json": composio_arguments_json.strip() or "{}"})
7998
+ settings.update({"composio_enabled": bool(composio_enabled), "composio_auto_upload": bool(composio_auto_upload), "composio_api_key": composio_api_key.strip(), "composio_user_id": composio_user_id.strip() or "thunderbolt-local", "composio_toolkit": composio_toolkit.strip(), "composio_tool_slug": composio_tool_slug.strip(), "composio_file_field": composio_file_field.strip() or "videoFilePath", "composio_channel_field": composio_channel_field.strip(), "composio_privacy_field": composio_privacy_field.strip() or "privacy_status", "composio_category_field": composio_category_field.strip() or "category_id", "composio_language_field": composio_language_field.strip() or "language", "composio_arguments_json": composio_arguments_json.strip() or "{}"})
7999
7999
  write_json("settings.json", settings)
8000
8000
  st.success("Configuração Composio guardada.")
8001
8001
  st.rerun()
@@ -1577,7 +1577,17 @@ def _run_task(task: dict[str, Any]) -> dict[str, Any]:
1577
1577
  captions_path=str(artifacts.get("captions") or ""),
1578
1578
  )
1579
1579
  if not result.ok:
1580
- raise PipelineError(result.message)
1580
+ attempts = (result.data or {}).get("attempts") if isinstance(result.data, dict) else None
1581
+ detail = ""
1582
+ if isinstance(attempts, list):
1583
+ failed = [
1584
+ f"{item.get('route')}: {item.get('message')}"
1585
+ for item in attempts
1586
+ if isinstance(item, dict) and item.get("status") in {"failed", "skipped"} and item.get("message")
1587
+ ]
1588
+ if failed:
1589
+ detail = " Detalhes: " + " | ".join(failed[-4:])
1590
+ raise PipelineError(f"{result.message}{detail}")
1581
1591
  artifacts["upload"] = result.data
1582
1592
  return _update(task_id, stage="upload", state="done", progress=100, artifacts=artifacts, error=None)
1583
1593
 
@@ -293,8 +293,8 @@ DEFAULTS: dict[str, Any] = {
293
293
  "composio_user_id": "thunderbolt-local",
294
294
  "composio_toolkit": "",
295
295
  "composio_tool_slug": "",
296
- "composio_file_field": "file",
297
- "composio_channel_field": "channel_id",
296
+ "composio_file_field": "videoFilePath",
297
+ "composio_channel_field": "",
298
298
  "composio_privacy_field": "privacy_status",
299
299
  "composio_category_field": "category_id",
300
300
  "composio_language_field": "language",
@@ -59,10 +59,17 @@ def _response(result: Any) -> dict[str, Any]:
59
59
  raw = _safe_value(result)
60
60
  if not isinstance(raw, dict):
61
61
  raw = {"data": raw}
62
- error = raw.get("error")
62
+ data = raw.get("data")
63
+ if not isinstance(data, dict):
64
+ data = {"value": data} if data is not None else {}
65
+ error = raw.get("error") or raw.get("message") or data.get("error") or data.get("message")
66
+ explicit_success = raw.get("successful")
67
+ if explicit_success is None:
68
+ explicit_success = raw.get("success")
69
+ successful = bool(explicit_success) if explicit_success is not None else not bool(error)
63
70
  return {
64
- "successful": not bool(error),
65
- "data": raw.get("data") or {},
71
+ "successful": successful and not bool(error),
72
+ "data": data,
66
73
  "error": str(error) if error else "",
67
74
  "log_id": str(raw.get("log_id") or raw.get("request_id") or ""),
68
75
  }
@@ -161,7 +168,11 @@ def execute_upload(api_key: str, user_id: str, slug: str, video_path: str, file_
161
168
  version="latest",
162
169
  dangerously_skip_version_check=True,
163
170
  )
164
- return _response(result)
171
+ response = _response(result)
172
+ if not response["successful"] and not response["error"]:
173
+ response["error"] = f"A ferramenta `{slug}` devolveu uma resposta sem sucesso."
174
+ response["tool_slug"] = slug
175
+ return response
165
176
  except ComposioUploadError:
166
177
  raise
167
178
  except Exception as exc:
@@ -229,12 +229,17 @@ def upload_with_default_route(
229
229
 
230
230
 
231
231
  def _composio_upload(settings: dict[str, Any], *, channel: dict[str, Any], **kwargs: Any) -> IntegrationResult:
232
+ slug = str(settings.get("composio_tool_slug") or "").strip()
233
+ youtube_upload_tool = slug.upper() in {"YOUTUBE_UPLOAD_VIDEO", "YOUTUBE_MULTIPART_UPLOAD_VIDEO"}
232
234
  channel_id = str(channel.get("youtube_channel_id") or "").strip()
233
- if not channel_id:
234
- return IntegrationResult(False, "Composio não foi executado: o canal da tarefa não tem um YouTube channel ID sincronizado.", {"missing": ["youtube_channel_id"]})
235
235
  channel_field = str(settings.get("composio_channel_field") or "channel_id").strip()
236
- if not channel_field:
236
+ if not youtube_upload_tool and not channel_id:
237
+ return IntegrationResult(False, "Composio não foi executado: o canal da tarefa não tem um YouTube channel ID sincronizado.", {"missing": ["youtube_channel_id"]})
238
+ if not youtube_upload_tool and not channel_field:
237
239
  return IntegrationResult(False, "Composio não foi executado: configure o campo de canal da ferramenta.", {"missing": ["composio_channel_field"]})
240
+ file_field = str(settings.get("composio_file_field") or "").strip()
241
+ if not file_field or (youtube_upload_tool and file_field == "file"):
242
+ file_field = "videoFilePath"
238
243
  try:
239
244
  arguments = str(settings.get("composio_arguments_json") or "{}").strip() or "{}"
240
245
  import json
@@ -242,7 +247,7 @@ def _composio_upload(settings: dict[str, Any], *, channel: dict[str, Any], **kwa
242
247
  if not isinstance(parsed_arguments, dict):
243
248
  return IntegrationResult(False, "Composio não foi executado: os argumentos JSON devem ser um objecto.", {})
244
249
  locked_values = {
245
- channel_field: channel_id,
250
+ **({channel_field: channel_id} if channel_field and not youtube_upload_tool else {}),
246
251
  str(settings.get("composio_privacy_field") or "privacy_status").strip(): str(kwargs.get("privacy_status") or "unlisted"),
247
252
  str(settings.get("composio_category_field") or "category_id").strip(): str(kwargs.get("category_id") or "22"),
248
253
  str(settings.get("composio_language_field") or "language").strip(): language_locale(kwargs.get("language") or channel.get("language") or "pt"),
@@ -261,7 +266,7 @@ def _composio_upload(settings: dict[str, Any], *, channel: dict[str, Any], **kwa
261
266
  str(settings.get("composio_user_id") or ""),
262
267
  str(settings.get("composio_tool_slug") or ""),
263
268
  str(kwargs.get("video_path") or ""),
264
- str(settings.get("composio_file_field") or "file"),
269
+ file_field,
265
270
  json.dumps(parsed_arguments, ensure_ascii=False),
266
271
  )
267
272
  except ComposioUploadError as exc:
@@ -269,5 +274,5 @@ def _composio_upload(settings: dict[str, Any], *, channel: dict[str, Any], **kwa
269
274
  return IntegrationResult(
270
275
  bool(result.get("successful")),
271
276
  str(result.get("error") or "Upload via Composio concluído."),
272
- {"composio_data": result.get("data") or {}, "log_id": result.get("log_id") or ""},
277
+ {"composio_data": result.get("data") or {}, "log_id": result.get("log_id") or "", "tool_slug": result.get("tool_slug") or slug},
273
278
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.5.96",
3
+ "version": "0.5.98",
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",