pulse-engine 0.2.22__tar.gz → 0.2.23__tar.gz

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 (180) hide show
  1. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/PKG-INFO +1 -1
  2. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/pyproject.toml +1 -1
  3. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cron/pipeline_sync.py +9 -2
  4. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/middleware/tenant.py +13 -1
  5. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/pipeline/config_parser.py +39 -0
  6. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/pipeline/router_pipelines.py +62 -0
  7. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/pipeline/schemas.py +10 -0
  8. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/pipeline/service.py +4 -1
  9. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/README.md +0 -0
  10. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/__init__.py +0 -0
  11. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/__init__.py +0 -0
  12. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/audio_transcription.py +0 -0
  13. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/batcher.py +0 -0
  14. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/digital_news.py +0 -0
  15. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/digital_news_metadata.py +0 -0
  16. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/document_event_campaign_mapper.py +0 -0
  17. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/exceptions.py +0 -0
  18. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/mart_export.py +0 -0
  19. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/models.py +0 -0
  20. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/opensearch_sql.py +0 -0
  21. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/opensearch_storage.py +0 -0
  22. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/prompts.py +0 -0
  23. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/report_generator.py +0 -0
  24. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/speech_content.py +0 -0
  25. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/speech_metadata.py +0 -0
  26. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/transcription/__init__.py +0 -0
  27. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/transcription/base.py +0 -0
  28. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/transcription/config.py +0 -0
  29. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/transcription/factory.py +0 -0
  30. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/transcription/providers/__init__.py +0 -0
  31. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/transcription/providers/deepgram.py +0 -0
  32. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/transcription/providers/openai_whisper.py +0 -0
  33. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/transcription/providers/selfhosted_job.py +0 -0
  34. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/transcription/providers/youtube_captions.py +0 -0
  35. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/transcription/service.py +0 -0
  36. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/transcription/source.py +0 -0
  37. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/twitter.py +0 -0
  38. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/youtube_downloader.py +0 -0
  39. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/adapters/youtube_metadata.py +0 -0
  40. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/api/__init__.py +0 -0
  41. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/api/v1/__init__.py +0 -0
  42. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/api/v1/auth.py +0 -0
  43. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/api/v1/health.py +0 -0
  44. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/api/v1/router.py +0 -0
  45. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cli/__init__.py +0 -0
  46. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cli/main.py +0 -0
  47. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cli/templates/cookiecutter.json +0 -0
  48. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/.gitignore +0 -0
  49. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/Dockerfile +0 -0
  50. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/pipeline.yaml +0 -0
  51. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/pyproject.toml +0 -0
  52. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/src/pulse_{{cookiecutter.product_slug}}/__init__.py +0 -0
  53. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/tests/__init__.py +0 -0
  54. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/tests/unit/__init__.py +0 -0
  55. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/tests/unit/test_manifest.py +0 -0
  56. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/client.py +0 -0
  57. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/config.py +0 -0
  58. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/core/__init__.py +0 -0
  59. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/core/error_handlers.py +0 -0
  60. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/core/exceptions.py +0 -0
  61. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/core/job_token.py +0 -0
  62. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/core/logging.py +0 -0
  63. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/core/scope.py +0 -0
  64. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/core/security.py +0 -0
  65. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cron/__init__.py +0 -0
  66. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cron/models.py +0 -0
  67. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cron/repository.py +0 -0
  68. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cron/router.py +0 -0
  69. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cron/runner.py +0 -0
  70. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cron/schemas.py +0 -0
  71. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cron/service.py +0 -0
  72. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cron/tasks.py +0 -0
  73. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/cron/translator.py +0 -0
  74. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/database.py +0 -0
  75. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/dependencies.py +0 -0
  76. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/deployment/__init__.py +0 -0
  77. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/deployment/infra_provisioner.py +0 -0
  78. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/evolution/__init__.py +0 -0
  79. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/evolution/adapter.py +0 -0
  80. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/evolution/client.py +0 -0
  81. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/evolution/models.py +0 -0
  82. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/evolution/router.py +0 -0
  83. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/evolution/service.py +0 -0
  84. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/extractor/__init__.py +0 -0
  85. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/extractor/adapters/__init__.py +0 -0
  86. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/extractor/base.py +0 -0
  87. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/main.py +0 -0
  88. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/mcp/__init__.py +0 -0
  89. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/mcp/__main__.py +0 -0
  90. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/mcp/server.py +0 -0
  91. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/mcp/tools_kb.py +0 -0
  92. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/mcp/tools_modules.py +0 -0
  93. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/mcp/tools_pipelines.py +0 -0
  94. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/mcp/tools_processor.py +0 -0
  95. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/middleware/__init__.py +0 -0
  96. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/middleware/rate_limit.py +0 -0
  97. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/middleware/request_id.py +0 -0
  98. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/middleware/security_headers.py +0 -0
  99. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/notifications/__init__.py +0 -0
  100. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/notifications/apprise_notifier.py +0 -0
  101. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/pipeline/__init__.py +0 -0
  102. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/pipeline/expression.py +0 -0
  103. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/pipeline/models.py +0 -0
  104. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/pipeline/repositories.py +0 -0
  105. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/pipeline/router_modules.py +0 -0
  106. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/pipeline/translators/__init__.py +0 -0
  107. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/pipeline/translators/airflow_status.py +0 -0
  108. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/pipeline/translators/airflow_translator.py +0 -0
  109. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/pipeline/translators/base.py +0 -0
  110. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/pipeline/translators/prefect_status.py +0 -0
  111. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/pipeline/translators/prefect_translator.py +0 -0
  112. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/__init__.py +0 -0
  113. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/base.py +0 -0
  114. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/core/__init__.py +0 -0
  115. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/core/analysis.py +0 -0
  116. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/core/chunking.py +0 -0
  117. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/core/prompts.py +0 -0
  118. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/core/topic_splitter.py +0 -0
  119. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/defaults/__init__.py +0 -0
  120. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/defaults/core_processor.py +0 -0
  121. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/defaults/postprocessor.py +0 -0
  122. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/defaults/preprocessor.py +0 -0
  123. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/llm/__init__.py +0 -0
  124. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/llm/provider.py +0 -0
  125. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/ocr/gemini.py +0 -0
  126. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/pipeline.py +0 -0
  127. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/postprocessor/__init__.py +0 -0
  128. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/postprocessor/embeddings.py +0 -0
  129. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/postprocessor/tasks.py +0 -0
  130. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/preprocessor/__init__.py +0 -0
  131. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/preprocessor/tasks.py +0 -0
  132. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/router.py +0 -0
  133. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/processor/schemas.py +0 -0
  134. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/registry.py +0 -0
  135. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/reporting/__init__.py +0 -0
  136. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/reporting/agent.py +0 -0
  137. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/reporting/fixtures.py +0 -0
  138. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/reporting/models.py +0 -0
  139. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/reporting/prompts.py +0 -0
  140. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/reporting/service.py +0 -0
  141. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/reporting/tools.py +0 -0
  142. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/reporting/wiring.py +0 -0
  143. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/runners/__init__.py +0 -0
  144. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/runners/lambda_runner.py +0 -0
  145. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/runners/pipeline_runner.py +0 -0
  146. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/runners/prefect_pipeline_flow.py +0 -0
  147. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/runners/prefect_runner.py +0 -0
  148. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/s3.py +0 -0
  149. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/sandbox/__init__.py +0 -0
  150. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/sandbox/backend.py +0 -0
  151. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/sandbox/lambda_microvm.py +0 -0
  152. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/sandbox/microvm_agent/Dockerfile +0 -0
  153. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/sandbox/microvm_agent/__init__.py +0 -0
  154. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/sandbox/microvm_agent/server.py +0 -0
  155. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/sandbox/session.py +0 -0
  156. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/secrets.py +0 -0
  157. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/services/__init__.py +0 -0
  158. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/services/bootstrap.py +0 -0
  159. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/services/opensearch.py +0 -0
  160. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/skills/__init__.py +0 -0
  161. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/skills/catalog.py +0 -0
  162. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/skills/models.py +0 -0
  163. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/skills/repo.py +0 -0
  164. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/skills/seed.py +0 -0
  165. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/skills/seed_data/skill_files.json +0 -0
  166. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/skills/seed_data/skills.json +0 -0
  167. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/skills/sync.py +0 -0
  168. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/skills/tools.py +0 -0
  169. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/storage/__init__.py +0 -0
  170. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/storage/connectors/__init__.py +0 -0
  171. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/storage/connectors/athena.py +0 -0
  172. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/storage/connectors/base.py +0 -0
  173. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/storage/connectors/opensearch.py +0 -0
  174. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/storage/knowledge_base.py +0 -0
  175. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/storage/router.py +0 -0
  176. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/storage/schemas.py +0 -0
  177. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/testing/__init__.py +0 -0
  178. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/testing/fixtures.py +0 -0
  179. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/testing/mocks.py +0 -0
  180. {pulse_engine-0.2.22 → pulse_engine-0.2.23}/src/pulse_engine/worker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pulse-engine
3
- Version: 0.2.22
3
+ Version: 0.2.23
4
4
  Summary: Pulse Engine — Hybrid framework for building Pulse products
5
5
  Author: Pulse Team
6
6
  Requires-Python: >=3.11,<3.13
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pulse-engine"
3
- version = "0.2.22"
3
+ version = "0.2.23"
4
4
  description = "Pulse Engine — Hybrid framework for building Pulse products"
5
5
  authors = ["Pulse Team"]
6
6
  readme = "README.md"
@@ -76,16 +76,23 @@ async def sync_pipeline_schedules(session: AsyncSession, settings: Settings) ->
76
76
  continue
77
77
 
78
78
  name = config.deployment_name
79
+ # Internal trigger endpoint: authenticated by the shared cron-internal
80
+ # token (not a tenant Bearer token) and accepts a JSON body, so the
81
+ # generic cron_job_flow can fire it without Cognito creds or multipart.
79
82
  trigger_url = (
80
- f"{settings.pulse_engine_url.rstrip('/')}/api/v1/pipelines/trigger"
83
+ f"{settings.pulse_engine_url.rstrip('/')}"
84
+ "/api/v1/pipelines/trigger-internal"
81
85
  if settings.pulse_engine_url
82
- else "/api/v1/pipelines/trigger"
86
+ else "/api/v1/pipelines/trigger-internal"
83
87
  )
84
88
 
85
89
  await repo.upsert(
86
90
  tenant_id=tenant_id,
87
91
  name=name,
88
92
  webhook_url=trigger_url,
93
+ webhook_headers={
94
+ "X-Cron-Internal-Token": settings.pulse_cron_internal_token
95
+ },
89
96
  schedule=config.infra.schedule,
90
97
  payload={"pipeline_name": config.name, "yaml_path": str(yaml_path)},
91
98
  job_type="pipeline",
@@ -30,6 +30,13 @@ PUBLIC_PATHS = {
30
30
  }
31
31
 
32
32
 
33
+ # Paths authenticated by the shared X-Cron-Internal-Token (verified in-endpoint)
34
+ # rather than a tenant Bearer token — must bypass TenantMiddleware resolution.
35
+ INTERNAL_TOKEN_PATHS = {
36
+ "/api/v1/pipelines/trigger-internal",
37
+ }
38
+
39
+
33
40
  def _is_cron_internal_path(path: str) -> bool:
34
41
  """True for cron callback endpoints authenticated via X-Cron-Internal-Token."""
35
42
  # /api/v1/cron-jobs/{job_id}/runs/init
@@ -54,7 +61,12 @@ class TenantMiddleware(BaseHTTPMiddleware):
54
61
  async def dispatch(
55
62
  self, request: Request, call_next: RequestResponseEndpoint
56
63
  ) -> Response:
57
- if request.url.path in PUBLIC_PATHS or _is_cron_internal_path(request.url.path):
64
+ path = request.url.path
65
+ if (
66
+ path in PUBLIC_PATHS
67
+ or path in INTERNAL_TOKEN_PATHS
68
+ or _is_cron_internal_path(path)
69
+ ):
58
70
  return await call_next(request)
59
71
 
60
72
  try:
@@ -1,5 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Iterator
4
+ from pathlib import Path
5
+
3
6
  import yaml # type: ignore[import-untyped]
4
7
  from pydantic import ValidationError
5
8
 
@@ -35,6 +38,42 @@ def parse_pipeline_config(yaml_content: str) -> PipelineConfig:
35
38
  return config
36
39
 
37
40
 
41
+ def iter_pipeline_configs(pipeline_dirs: str) -> Iterator[tuple[Path, PipelineConfig]]:
42
+ """Yield ``(path, config)`` for every valid ``*.yaml`` across the dirs.
43
+
44
+ ``pipeline_dirs`` is a colon-separated list (same format as
45
+ ``Settings.pulse_pipeline_dirs``). Missing dirs and files that fail
46
+ validation are skipped silently — callers that care log their own context.
47
+ """
48
+ for raw in pipeline_dirs.split(":"):
49
+ entry = raw.strip()
50
+ if not entry:
51
+ continue
52
+ directory = Path(entry)
53
+ if not directory.is_dir():
54
+ continue
55
+ for yaml_path in sorted(directory.glob("*.yaml")):
56
+ try:
57
+ config = parse_pipeline_config(yaml_path.read_text())
58
+ except PipelineConfigError:
59
+ continue
60
+ yield yaml_path, config
61
+
62
+
63
+ def find_pipeline_config(
64
+ pipeline_dirs: str, pipeline_name: str
65
+ ) -> PipelineConfig | None:
66
+ """Return the first pipeline config whose ``name`` matches, else ``None``.
67
+
68
+ Names are assumed unique across the configured dirs; if two configs share a
69
+ name the first (sorted, per-dir) match wins.
70
+ """
71
+ for _path, config in iter_pipeline_configs(pipeline_dirs):
72
+ if config.name == pipeline_name:
73
+ return config
74
+ return None
75
+
76
+
38
77
  def validate_dag(config: PipelineConfig) -> None:
39
78
  """Validate DAG structure for v2 pipelines."""
40
79
  errors: list[str] = []
@@ -8,6 +8,7 @@ from fastapi import (
8
8
  Depends,
9
9
  File,
10
10
  Form,
11
+ Header,
11
12
  HTTPException,
12
13
  Request,
13
14
  UploadFile,
@@ -19,11 +20,13 @@ from pulse_engine.dependencies import get_db_session
19
20
  from pulse_engine.middleware.tenant import get_tenant_id
20
21
  from pulse_engine.pipeline.config_parser import (
21
22
  PipelineConfigError,
23
+ find_pipeline_config,
22
24
  parse_pipeline_config,
23
25
  )
24
26
  from pulse_engine.pipeline.schemas import (
25
27
  InitRunRequest,
26
28
  InitRunResponse,
29
+ InternalTriggerRequest,
27
30
  PipelineRunListResponse,
28
31
  PipelineRunStatus,
29
32
  PipelineStatusResponse,
@@ -109,6 +112,65 @@ async def trigger_pipeline(
109
112
  return TriggerResponse(run_id=run_id)
110
113
 
111
114
 
115
+ def _verify_cron_internal_token(
116
+ request: Request,
117
+ x_cron_internal_token: Annotated[str | None, Header()] = None,
118
+ ) -> None:
119
+ """Authorize internal callers via the shared cron-internal token."""
120
+ settings = request.app.state.settings
121
+ expected = getattr(settings, "pulse_cron_internal_token", "")
122
+ if not expected or x_cron_internal_token != expected:
123
+ raise HTTPException(
124
+ status_code=status.HTTP_401_UNAUTHORIZED,
125
+ detail="Invalid or missing internal token",
126
+ )
127
+
128
+
129
+ @router.post(
130
+ "/trigger-internal",
131
+ status_code=status.HTTP_202_ACCEPTED,
132
+ response_model=TriggerResponse,
133
+ )
134
+ async def trigger_pipeline_internal(
135
+ body: InternalTriggerRequest,
136
+ request: Request,
137
+ service: Annotated[PipelineService, Depends(get_pipeline_service)] = None, # type: ignore[assignment]
138
+ _: None = Depends(_verify_cron_internal_token),
139
+ ) -> TriggerResponse:
140
+ """Trigger a scheduled pipeline by name, authenticated by cron-internal token.
141
+
142
+ Used by ``cron_job_flow`` for ``type=pipeline`` crons: resolves the pipeline
143
+ config from ``pulse_pipeline_dirs`` server-side (no multipart upload, no
144
+ tenant Bearer token needed). Tenant is taken from the config, mirroring
145
+ ``sync_pipeline_schedules``.
146
+ """
147
+ settings = request.app.state.settings
148
+ config = find_pipeline_config(
149
+ getattr(settings, "pulse_pipeline_dirs", "") or "", body.pipeline_name
150
+ )
151
+ if config is None:
152
+ raise HTTPException(
153
+ status_code=404,
154
+ detail=f"Pipeline '{body.pipeline_name}' not found in configured dirs",
155
+ )
156
+
157
+ tenant_id = config.config.get("tenant_id") or "default"
158
+ try:
159
+ run_id = await service.trigger(
160
+ tenant_id=tenant_id,
161
+ product=config.name,
162
+ orchestrator=config.infra.orchestrator,
163
+ config=config,
164
+ global_config=config.config,
165
+ )
166
+ except PipelineSubmissionError as e:
167
+ raise HTTPException(status_code=502, detail=str(e))
168
+ except PipelineServiceError as e:
169
+ raise HTTPException(status_code=400, detail=str(e))
170
+
171
+ return TriggerResponse(run_id=run_id)
172
+
173
+
112
174
  @router.post(
113
175
  "/runs/init",
114
176
  status_code=status.HTTP_201_CREATED,
@@ -172,6 +172,16 @@ class TriggerPayload(BaseModel):
172
172
  orchestrator: Literal["prefect"] | None = None
173
173
 
174
174
 
175
+ class InternalTriggerRequest(BaseModel):
176
+ """Body for the internal cron-token pipeline trigger.
177
+
178
+ Extra fields (e.g. ``yaml_path``, ``cron_run_id``) sent by ``cron_job_flow``
179
+ are ignored.
180
+ """
181
+
182
+ pipeline_name: str
183
+
184
+
175
185
  class PipelineRunStatus(str, Enum):
176
186
  pending = "pending"
177
187
  running = "running"
@@ -332,10 +332,13 @@ class PipelineService:
332
332
  now = datetime.now(tz=UTC)
333
333
  await self._run_repo.update_status(run.id, "running", started_at=now)
334
334
 
335
+ # Cron/scheduled per-execution runs may retry or run for days after the
336
+ # token is minted; use the same 90-day TTL as the scheduled trigger path
337
+ # so step-status callbacks don't 401 on an expired token.
335
338
  token = self._token_issuer.issue_token(
336
339
  pipeline_run_id=run.id,
337
340
  tenant_id=tenant_id,
338
- ttl_seconds=86400,
341
+ ttl_seconds=7_776_000, # 90 days
339
342
  )
340
343
  return run.id, token
341
344
 
File without changes