databricks-tellr-app 0.4.2.dev10__tar.gz → 0.4.2.dev11__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 (167) hide show
  1. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/PKG-INFO +1 -1
  2. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/databricks_tellr_app.egg-info/PKG-INFO +1 -1
  3. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/pyproject.toml +1 -1
  4. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/settings/design_systems.py +37 -2
  5. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/design_system_service.py +160 -45
  6. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/design_system_templates.py +10 -0
  7. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/README.md +0 -0
  8. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/databricks_tellr_app/__init__.py +0 -0
  9. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/databricks_tellr_app/run.py +0 -0
  10. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/databricks_tellr_app.egg-info/SOURCES.txt +0 -0
  11. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/databricks_tellr_app.egg-info/dependency_links.txt +0 -0
  12. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/databricks_tellr_app.egg-info/requires.txt +0 -0
  13. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/databricks_tellr_app.egg-info/top_level.txt +0 -0
  14. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/setup.cfg +0 -0
  15. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/setup.py +0 -0
  16. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/__init__.py +0 -0
  17. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/__init__.py +0 -0
  18. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/_otel_bootstrap.py +0 -0
  19. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/fixtures/tour_demo_deck.json +0 -0
  20. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/main.py +0 -0
  21. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/mcp_auth.py +0 -0
  22. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/mcp_server.py +0 -0
  23. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/middleware/__init__.py +0 -0
  24. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/middleware/csrf.py +0 -0
  25. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/middleware/request_logging.py +0 -0
  26. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/middleware/security_headers.py +0 -0
  27. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/__init__.py +0 -0
  28. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/_authz.py +0 -0
  29. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/admin.py +0 -0
  30. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/admin_usage.py +0 -0
  31. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/agent_config.py +0 -0
  32. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/chat.py +0 -0
  33. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/deck_contributors.py +0 -0
  34. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/export.py +0 -0
  35. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/feedback.py +0 -0
  36. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/google_slides.py +0 -0
  37. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/images.py +0 -0
  38. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/local_version.py +0 -0
  39. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/profiles.py +0 -0
  40. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/sessions.py +0 -0
  41. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/settings/__init__.py +0 -0
  42. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/settings/contributors.py +0 -0
  43. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/settings/deck_prompts.py +0 -0
  44. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/settings/identities.py +0 -0
  45. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/settings/slide_styles.py +0 -0
  46. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/setup.py +0 -0
  47. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/slides.py +0 -0
  48. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/tools.py +0 -0
  49. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/tour.py +0 -0
  50. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/verification.py +0 -0
  51. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/routes/version.py +0 -0
  52. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/schemas/__init__.py +0 -0
  53. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/schemas/agent_config.py +0 -0
  54. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/schemas/feedback.py +0 -0
  55. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/schemas/requests.py +0 -0
  56. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/schemas/responses.py +0 -0
  57. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/schemas/settings/__init__.py +0 -0
  58. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/schemas/settings/requests.py +0 -0
  59. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/schemas/settings/responses.py +0 -0
  60. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/schemas/streaming.py +0 -0
  61. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/services/__init__.py +0 -0
  62. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/services/chat_service.py +0 -0
  63. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/services/export_job_queue.py +0 -0
  64. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/services/feedback_service.py +0 -0
  65. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/services/job_queue.py +0 -0
  66. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/services/session_manager.py +0 -0
  67. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/services/session_naming.py +0 -0
  68. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/services/usage_events.py +0 -0
  69. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/services/usage_service.py +0 -0
  70. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/utils/__init__.py +0 -0
  71. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/api/utils/validation.py +0 -0
  72. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/core/__init__.py +0 -0
  73. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/core/config_loader.py +0 -0
  74. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/core/context_utils.py +0 -0
  75. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/core/database.py +0 -0
  76. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/core/databricks_client.py +0 -0
  77. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/core/defaults.py +0 -0
  78. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/core/encryption.py +0 -0
  79. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/core/init_default_profile.py +0 -0
  80. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/core/lakebase.py +0 -0
  81. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/core/migrate_profiles_to_agent_config.py +0 -0
  82. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/core/mlflow_agent_spans.py +0 -0
  83. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/core/mlflow_tracing.py +0 -0
  84. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/core/otel_logging.py +0 -0
  85. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/core/permission_context.py +0 -0
  86. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/core/prompt_modules.py +0 -0
  87. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/core/settings_db.py +0 -0
  88. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/core/user_context.py +0 -0
  89. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/__init__.py +0 -0
  90. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/__init__.py +0 -0
  91. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/deck_contributor.py +0 -0
  92. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/design_system.py +0 -0
  93. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/encryption_key.py +0 -0
  94. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/feedback.py +0 -0
  95. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/genie_space.py +0 -0
  96. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/google_global_credentials.py +0 -0
  97. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/google_oauth_token.py +0 -0
  98. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/identity.py +0 -0
  99. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/image.py +0 -0
  100. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/profile.py +0 -0
  101. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/profile_contributor.py +0 -0
  102. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/prompts.py +0 -0
  103. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/request_log.py +0 -0
  104. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/session.py +0 -0
  105. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/slide_deck_prompt.py +0 -0
  106. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/slide_style_library.py +0 -0
  107. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/usage_event.py +0 -0
  108. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/models/user_preference.py +0 -0
  109. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/database/types.py +0 -0
  110. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/domain/__init__.py +0 -0
  111. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/domain/slide.py +0 -0
  112. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/domain/slide_deck.py +0 -0
  113. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/__init__.py +0 -0
  114. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/agent.py +0 -0
  115. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/agent_factory.py +0 -0
  116. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/config_service.py +0 -0
  117. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/config_validator.py +0 -0
  118. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/converter_jail/__init__.py +0 -0
  119. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/converter_jail/ast_guard.py +0 -0
  120. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/converter_jail/codeprep.py +0 -0
  121. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/converter_jail/gslides_runner.py +0 -0
  122. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/converter_jail/jail.py +0 -0
  123. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/converter_jail/pptx_runner.py +0 -0
  124. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/converter_jail/protocol.py +0 -0
  125. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/design_system_compiler.py +0 -0
  126. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/drive_uploader.py +0 -0
  127. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/evaluation/__init__.py +0 -0
  128. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/evaluation/llm_judge.py +0 -0
  129. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/genie_service.py +0 -0
  130. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/google_slides_auth.py +0 -0
  131. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/google_slides_prompts_defaults.py +0 -0
  132. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/html_to_google_slides.py +0 -0
  133. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/html_to_pptx.py +0 -0
  134. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/identity_provider.py +0 -0
  135. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/identity_providers/__init__.py +0 -0
  136. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/identity_providers/account_provider.py +0 -0
  137. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/identity_providers/local_provider.py +0 -0
  138. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/identity_providers/workspace_provider.py +0 -0
  139. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/image_service.py +0 -0
  140. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/image_tools.py +0 -0
  141. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/permission_service.py +0 -0
  142. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/pptx_from_html_huashu.py +0 -0
  143. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/pptx_from_records.py +0 -0
  144. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/pptx_prompts_defaults.py +0 -0
  145. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/profile_service.py +0 -0
  146. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/streaming_callback.py +0 -0
  147. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/tools/__init__.py +0 -0
  148. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/tools/agent_bricks_tool.py +0 -0
  149. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/tools/ds_asset_tool.py +0 -0
  150. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/tools/genie_tool.py +0 -0
  151. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/tools/mcp_tool.py +0 -0
  152. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/tools/model_endpoint_tool.py +0 -0
  153. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/tools/vector_tool.py +0 -0
  154. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/services/validator.py +0 -0
  155. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/utils/__init__.py +0 -0
  156. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/utils/css_utils.py +0 -0
  157. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/utils/ds_asset_utils.py +0 -0
  158. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/utils/error_handling.py +0 -0
  159. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/utils/html_safety.py +0 -0
  160. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/utils/html_utils.py +0 -0
  161. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/utils/image_utils.py +0 -0
  162. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/utils/js_validator.py +0 -0
  163. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/utils/logging_config.py +0 -0
  164. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/utils/pi_filter.py +0 -0
  165. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/utils/slide_hash.py +0 -0
  166. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/utils/spotlight.py +0 -0
  167. {databricks_tellr_app-0.4.2.dev10 → databricks_tellr_app-0.4.2.dev11}/src/utils/text_caps.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: databricks-tellr-app
3
- Version: 0.4.2.dev10
3
+ Version: 0.4.2.dev11
4
4
  Summary: Tellr application package for Databricks Apps
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: databricks-tellr-app
3
- Version: 0.4.2.dev10
3
+ Version: 0.4.2.dev11
4
4
  Summary: Tellr application package for Databricks Apps
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "databricks-tellr-app"
7
- version = "0.4.2.dev10"
7
+ version = "0.4.2.dev11"
8
8
  description = "Tellr application package for Databricks Apps"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -172,6 +172,28 @@ class DesignSystemDetail(DesignSystemSummary):
172
172
  assets: List[AssetOut]
173
173
 
174
174
 
175
+ class ImportWarningOut(BaseModel):
176
+ """One bundle entry the import ignored, and why."""
177
+ path: str
178
+ reason: str
179
+
180
+
181
+ class DesignSystemImportResult(DesignSystemDetail):
182
+ """The /import response: the imported detail PLUS anything that was ignored.
183
+
184
+ Additive by design. Some entries are individually unusable without making the
185
+ bundle unusable (an unsniffable template screenshot, a non-UTF-8 token source);
186
+ those are dropped so one junk file cannot cost the whole upload. Reporting them
187
+ only to the server log told the user the import had succeeded and left them to
188
+ discover a missing thumbnail themselves.
189
+
190
+ A NON-FATAL list on a 201, and its own model rather than a field on
191
+ :class:`DesignSystemDetail`, so the GET/create/update responses are byte-for-byte
192
+ unchanged and an existing /import client simply ignores a field it does not read.
193
+ """
194
+ warnings: List[ImportWarningOut] = []
195
+
196
+
175
197
  class DesignSystemCreate(BaseModel):
176
198
  """Structured (in-app) create — thin. Assets arrive via /import."""
177
199
  # UNCAPPED, matching the unbounded ``design_system.name`` column; ``min_length``
@@ -496,7 +518,11 @@ def list_design_systems(
496
518
  )
497
519
 
498
520
 
499
- @router.post("/import", response_model=DesignSystemDetail, status_code=status.HTTP_201_CREATED)
521
+ @router.post(
522
+ "/import",
523
+ response_model=DesignSystemImportResult,
524
+ status_code=status.HTTP_201_CREATED,
525
+ )
500
526
  async def import_design_system(
501
527
  file: UploadFile = File(...),
502
528
  name: Optional[str] = Form(None),
@@ -517,14 +543,23 @@ async def import_design_system(
517
543
  detail=f"Upload exceeds the maximum bundle size of {MAX_BUNDLE_SIZE_BYTES} bytes",
518
544
  )
519
545
  try:
546
+ # Filled with the entries the import dropped without failing, so the caller
547
+ # learns about them instead of only the server log doing so.
548
+ dropped: list[design_system_service.BundleImportWarning] = []
520
549
  ds = design_system_service.import_bundle(
521
550
  db,
522
551
  zip_bytes=content,
523
552
  user=_current_user(),
524
553
  name_override=name,
525
554
  source_filename=file.filename,
555
+ warnings=dropped,
556
+ )
557
+ return DesignSystemImportResult(
558
+ **_detail(ds).model_dump(),
559
+ warnings=[
560
+ ImportWarningOut(path=w.path, reason=w.reason) for w in dropped
561
+ ],
526
562
  )
527
- return _detail(ds)
528
563
  except DesignSystemNameConflictError as e:
529
564
  db.rollback()
530
565
  raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))
@@ -59,7 +59,7 @@ import logging
59
59
  import mimetypes
60
60
  import re
61
61
  import zipfile
62
- from typing import Any, Optional
62
+ from typing import Any, NamedTuple, Optional
63
63
 
64
64
  from sqlalchemy.orm import Session, defer
65
65
 
@@ -78,6 +78,22 @@ logger = logging.getLogger(__name__)
78
78
  MANIFEST_FILENAME = "_ds_manifest.json"
79
79
  DEFAULT_CSS_TOKEN_SOURCE = "colors_and_type.css"
80
80
 
81
+
82
+ class BundleImportWarning(NamedTuple):
83
+ """A bundle entry the import DROPPED without failing the upload.
84
+
85
+ Some entries are individually unusable without making the bundle unusable — an
86
+ unsniffable template screenshot, a CSS token source that is not UTF-8. Dropping
87
+ just that entry is right (one junk file must not cost the whole upload), but the
88
+ caller was then told the import succeeded with nothing to say a file had been
89
+ ignored, so the only record was a server-side log the user cannot see. These ride
90
+ back on the import response instead.
91
+ """
92
+
93
+ path: str
94
+ reason: str
95
+
96
+
81
97
  # Color sub-groups the compiler renders as :root vars. A token name whose first
82
98
  # segment is one of these carries its group in the name (e.g. --brand-accents-lava).
83
99
  _COLOR_SUBGROUPS = frozenset(("core", "accents", "ink", "tints"))
@@ -280,8 +296,14 @@ def _infer_asset_kind(rel_path: str) -> str:
280
296
  # raster-image extension) — stored as a ``template_shot`` asset so the Phase 4
281
297
  # template picker can serve thumbnails. Checked BEFORE ``_should_skip`` (which
282
298
  # excludes ``templates/**`` from generic asset storage).
299
+ #
300
+ # Anchored with ``\Z``, NOT ``$``: in Python ``$`` matches at the end of the string
301
+ # OR immediately before a single trailing newline, so a ``$``-anchored allowlist on
302
+ # a PATH accepts ``<allowed shape>\n`` — ``templates/x/preview.png\n`` was matched,
303
+ # stored, and served. ``\Z`` is the true end of string. Applies to every allowlist
304
+ # regex in this module that judges a path or a filename.
283
305
  _TEMPLATE_PREVIEW_RE = re.compile(
284
- r"^templates/[^/]+/preview[^/]*\.(png|jpe?g|gif|webp)$", re.IGNORECASE
306
+ r"^templates/[^/]+/preview[^/]*\.(png|jpe?g|gif|webp)\Z", re.IGNORECASE
285
307
  )
286
308
 
287
309
  # The SAME screenshot as it actually ships in a real export: DOT-PREFIXED, named
@@ -296,8 +318,11 @@ _TEMPLATE_PREVIEW_RE = re.compile(
296
318
  # from the general dotfile skip; a dotfile anywhere else stays skipped. Because
297
319
  # a match carries no extension, its media type is NOT guessable from the name and
298
320
  # is sniffed from the content instead (:func:`_sniff_raster_mime`).
321
+ # ``\Z`` for the same reason as _TEMPLATE_PREVIEW_RE: with ``$`` this accepted
322
+ # ``templates/x/.thumbnail\n``, which was then stored AND linked as a template's
323
+ # thumbnail_url.
299
324
  _TEMPLATE_THUMBNAIL_RE = re.compile(
300
- r"^templates/[^/]+/\.?(thumbnail|preview)$", re.IGNORECASE
325
+ r"^templates/[^/]+/\.?(thumbnail|preview)\Z", re.IGNORECASE
301
326
  )
302
327
 
303
328
  # Magic-byte signatures for the raster formats a template thumbnail may be. An
@@ -563,12 +588,20 @@ def import_bundle(
563
588
  user: Optional[str],
564
589
  name_override: Optional[str] = None,
565
590
  source_filename: Optional[str] = None,
591
+ warnings: Optional[list[BundleImportWarning]] = None,
566
592
  ) -> DesignSystem:
567
593
  """Import a ``.zip`` design-system bundle into Lakebase and compile it.
568
594
 
569
595
  Returns the persisted :class:`DesignSystem` (committed, with tokens + assets +
570
596
  ``compiled_style_content``).
571
597
 
598
+ Args:
599
+ warnings: optional collector. Pass a list to receive an
600
+ :class:`BundleImportWarning` for every entry the import DROPPED without
601
+ failing — the caller can then tell the user which files were ignored and
602
+ why. Omitting it keeps the previous signature working for the many
603
+ callers that only want the design system.
604
+
572
605
  Raises:
573
606
  DesignSystemImportError: bundle is not a zip, missing/invalid manifest, or
574
607
  violates a size limit (HTTP 400).
@@ -626,8 +659,10 @@ def import_bundle(
626
659
  # Read the DECLARED CSS token sources ONCE (budgeted); the same bytes are
627
660
  # reused for both token parsing and source-file retention (no double-charge).
628
661
  css_sources = _read_css_sources(zf, root_prefix, manifest, budget)
629
- tokens = _collect_tokens(manifest, _decode_css_texts(css_sources))
630
- assets, files = _collect_assets_and_files(zf, root_prefix, budget, css_sources)
662
+ tokens = _collect_tokens(manifest, _decode_css_texts(css_sources, warnings))
663
+ assets, files = _collect_assets_and_files(
664
+ zf, root_prefix, budget, css_sources, warnings
665
+ )
631
666
 
632
667
  # Stored VERBATIM; the strip decides only whether the brand wrote a description
633
668
  # at all (a whitespace-only one describes nothing, so it stays NULL). Same split
@@ -976,7 +1011,10 @@ def _read_css_sources(
976
1011
  return result
977
1012
 
978
1013
 
979
- def _decode_css_texts(css_sources: "dict[str, bytes]") -> list[str]:
1014
+ def _decode_css_texts(
1015
+ css_sources: "dict[str, bytes]",
1016
+ warnings: "Optional[list[BundleImportWarning]]" = None,
1017
+ ) -> list[str]:
980
1018
  """Decode the pre-read CSS source bytes to UTF-8 text (skip undecodable)."""
981
1019
  texts: list[str] = []
982
1020
  for path, raw in css_sources.items():
@@ -984,31 +1022,98 @@ def _decode_css_texts(css_sources: "dict[str, bytes]") -> list[str]:
984
1022
  texts.append(raw.decode("utf-8"))
985
1023
  except UnicodeDecodeError:
986
1024
  logger.warning("Skipping non-UTF-8 CSS token source: %s", path)
1025
+ if warnings is not None:
1026
+ warnings.append(
1027
+ BundleImportWarning(
1028
+ path,
1029
+ "not valid UTF-8; its design tokens were not read.",
1030
+ )
1031
+ )
987
1032
  return texts
988
1033
 
989
1034
 
990
1035
  def _safe_relpath(rel_path: str) -> Optional[str]:
991
- """Normalize a bundle-relative path; return ``None`` if it is unsafe (zip-slip).
992
-
993
- Rejects absolute paths (POSIX ``/`` or a Windows drive) and any path that
994
- escapes the bundle root via a ``..`` segment. Backslashes are normalized to
995
- ``/`` and ``.``/empty segments are collapsed. Assets are stored as DB bytes
996
- (never written to disk), so this is defence-in-depth — and it keeps the
997
- persisted ``design_system_file.path`` values safe for the later file browser.
1036
+ """The CANONICAL form of a bundle-relative FILE path, or ``None`` to REFUSE it.
1037
+
1038
+ VALIDATION, not laundering: a path is already canonical or it is refused. It is
1039
+ never rewritten into an acceptable form, because rewriting changes the very
1040
+ thing later rules are about to judge. ``assets/innocent/.`` used to normalize to
1041
+ ``assets/innocent`` — losing the ``.`` basename that made it junk — and the
1042
+ entry was stored under a path that was not its own name. Two entries could also
1043
+ collapse onto ONE identity (``assets/./x``, ``assets//x`` and ``assets/x`` all
1044
+ became ``assets/x``), leaving which bytes win to zip ordering.
1045
+
1046
+ Refused: an empty name, a C0/DEL control character (``\\n`` and NUL among them,
1047
+ so no rule downstream has to cope with one), an absolute path (POSIX ``/`` or a
1048
+ Windows drive), and any ``.``, ``..`` or EMPTY segment. Backslashes are still
1049
+ folded to ``/`` first, which is the one tolerance kept deliberately: zips written
1050
+ on Windows use them as separators, and folding cannot erase a segment or change a
1051
+ basename's junk-ness the way the collapses above did.
1052
+
1053
+ A trailing-slash DIRECTORY marker is not a file path and is refused here; callers
1054
+ reach the directory case through :func:`_classify_bundle_entry`, which strips the
1055
+ marker and validates the path it names.
1056
+
1057
+ This is the contract the file browser already relies on: stored
1058
+ ``design_system_file.path`` values are canonical by construction, so
1059
+ ``_validated_file_path`` can reject a non-canonical REQUEST instead of
1060
+ normalizing it into an accepted one.
998
1061
  """
999
1062
  if not rel_path:
1000
1063
  return None
1001
1064
  normalized = rel_path.replace("\\", "/")
1065
+ if any(ord(ch) < 0x20 or ord(ch) == 0x7F for ch in normalized):
1066
+ return None
1002
1067
  if normalized.startswith("/") or re.match(r"^[A-Za-z]:", normalized):
1003
1068
  return None
1004
- parts: list[str] = []
1005
- for segment in normalized.split("/"):
1006
- if segment in ("", "."):
1007
- continue
1008
- if segment == "..":
1009
- return None
1010
- parts.append(segment)
1011
- return "/".join(parts) if parts else None
1069
+ if any(segment in ("", ".", "..") for segment in normalized.split("/")):
1070
+ return None
1071
+ return normalized
1072
+
1073
+
1074
+ # The verdict :func:`_classify_bundle_entry` reaches for one zip entry.
1075
+ _ENTRY_REFUSE = "refuse" # unsafe — reject the whole bundle
1076
+ _ENTRY_SKIP = "skip" # legitimate but never stored (directory, OS junk, dotfile)
1077
+ _ENTRY_FILE = "file" # store it, under the canonical path returned alongside
1078
+
1079
+
1080
+ def _classify_bundle_entry(rel_path: str) -> tuple[str, Optional[str]]:
1081
+ """THE decision for one bundle entry: refuse it, skip it, or store it.
1082
+
1083
+ ONE place that canonicalizes and then applies EVERY rule. Both gates that judge
1084
+ bundle paths call this — the up-front whole-bundle scan
1085
+ (:func:`_assert_bundle_paths_safe`) and the per-entry iterator
1086
+ (:func:`_iter_safe_entries`) — so they cannot disagree about a path shape, and a
1087
+ new rule cannot be added to one and forgotten in the other. They used to decide
1088
+ separately and DID disagree: the scan refused ``assets/innocent/.`` on its raw
1089
+ basename while the iterator accepted the same entry on its normalized one, and
1090
+ the permissive gate was the one that reached storage.
1091
+
1092
+ Returns ``(verdict, canonical_path)``; the path is set only for ``_ENTRY_FILE``.
1093
+ """
1094
+ if rel_path.endswith("/"):
1095
+ # A directory marker. It is never stored, but the path it NAMES still has to
1096
+ # be canonical, so '../evil/' is refused rather than skipped.
1097
+ return (
1098
+ (_ENTRY_SKIP, None)
1099
+ if _safe_relpath(rel_path[:-1]) is not None
1100
+ else (_ENTRY_REFUSE, None)
1101
+ )
1102
+ canonical = _safe_relpath(rel_path)
1103
+ if canonical is None:
1104
+ return _ENTRY_REFUSE, None
1105
+ low = canonical.lower()
1106
+ if low.startswith("__macosx/") or "/__macosx/" in low:
1107
+ return _ENTRY_SKIP, None
1108
+ base = _basename(low)
1109
+ # The ONE dot-prefixed shape the importer stores is a template folder's
1110
+ # screenshot; every other dotfile stays skipped. Tested against the CANONICAL
1111
+ # path — the same string the recognizer sees downstream.
1112
+ if (base == ".ds_store" or base.startswith(".")) and not _is_template_preview(
1113
+ canonical
1114
+ ):
1115
+ return _ENTRY_SKIP, None
1116
+ return _ENTRY_FILE, canonical
1012
1117
 
1013
1118
 
1014
1119
  def _is_symlink(info: zipfile.ZipInfo) -> bool:
@@ -1029,8 +1134,11 @@ def _assert_bundle_paths_safe(zf: zipfile.ZipFile) -> None:
1029
1134
  root-prefix scoping — so a malicious entry that falls OUTSIDE the bundle root
1030
1135
  (e.g. ``../evil.png`` when the manifest sits under ``safe/``) is REJECTED, not
1031
1136
  silently skipped. EVERY entry is checked (no exemption for empty or ``.``-only
1032
- names): absolute paths, ``..`` traversal, and empty/invalid names are refused,
1033
- and symlink entries are refused before any bytes are read.
1137
+ names): absolute paths, ``..`` traversal, control characters, and empty/invalid
1138
+ names are refused, and symlink entries are refused before any bytes are read.
1139
+
1140
+ The path verdict comes from :func:`_classify_bundle_entry`, the SAME classifier
1141
+ :func:`_iter_safe_entries` uses, so the two gates cannot disagree about a shape.
1034
1142
  """
1035
1143
  for info in zf.infolist():
1036
1144
  name = info.filename
@@ -1038,12 +1146,12 @@ def _assert_bundle_paths_safe(zf: zipfile.ZipFile) -> None:
1038
1146
  raise DesignSystemImportError(
1039
1147
  f"Bundle entry '{name}' is a symlink; refusing to import."
1040
1148
  )
1041
- # No ``if name`` guard: an empty (or ``.``-only) name also fails
1042
- # ``_safe_relpath`` and must be rejected, not skipped.
1043
- if _safe_relpath(name) is None:
1149
+ # No ``if name`` guard: an empty (or ``.``-only) name is also refused, not
1150
+ # skipped.
1151
+ if _classify_bundle_entry(name)[0] == _ENTRY_REFUSE:
1044
1152
  raise DesignSystemImportError(
1045
- f"Bundle contains an unsafe path '{name}' (empty, absolute, or "
1046
- "parent-directory traversal); refusing to import."
1153
+ f"Bundle contains an unsafe path '{name}' (empty, absolute, "
1154
+ "non-canonical, or parent-directory traversal); refusing to import."
1047
1155
  )
1048
1156
 
1049
1157
 
@@ -1060,31 +1168,26 @@ def _iter_safe_entries(zf: zipfile.ZipFile, root_prefix: str):
1060
1168
  the recognizer alone reads as working in a unit test and still drops every real
1061
1169
  thumbnail, because the entry never reaches the recognizer.
1062
1170
 
1063
- The zip-slip check runs BEFORE the dotfile decision, so normalization and
1064
- rejection are never skippable by a dot-prefixed name, and the allowlist is
1065
- tested against the NORMALIZED path — the same string the recognizer sees
1066
- downstream, so the gate and the recognizer cannot disagree.
1171
+ Every path rule lives in :func:`_classify_bundle_entry` — the same classifier the
1172
+ up-front scan uses — so an entry is judged on its CANONICAL path and neither gate
1173
+ can be patched into disagreeing with the other. Stripping ``root_prefix`` is the
1174
+ only work done here, and it is bookkeeping rather than a path rule.
1067
1175
  """
1068
1176
  for info in zf.infolist():
1069
1177
  name = info.filename
1070
1178
  if not name.startswith(root_prefix):
1071
1179
  continue
1072
1180
  rel_raw = name[len(root_prefix):]
1073
- if not rel_raw or rel_raw.endswith("/"):
1074
- continue # directory entry
1075
- low = rel_raw.lower()
1076
- if low.startswith("__macosx/") or "/__macosx/" in low:
1077
- continue
1078
- safe = _safe_relpath(rel_raw)
1079
- if safe is None:
1181
+ if not rel_raw:
1182
+ continue # the root-prefix directory entry itself
1183
+ verdict, safe = _classify_bundle_entry(rel_raw)
1184
+ if verdict == _ENTRY_REFUSE:
1080
1185
  raise DesignSystemImportError(
1081
- f"Bundle contains an unsafe path '{rel_raw}' (absolute path or "
1082
- "parent-directory traversal); refusing to import."
1186
+ f"Bundle contains an unsafe path '{rel_raw}' (absolute path, "
1187
+ "non-canonical path, or parent-directory traversal); refusing to "
1188
+ "import."
1083
1189
  )
1084
- base = _basename(safe.lower())
1085
- if (base == ".ds_store" or base.startswith(".")) and not _is_template_preview(
1086
- safe
1087
- ):
1190
+ if safe is None: # _ENTRY_SKIP
1088
1191
  continue
1089
1192
  yield info, safe
1090
1193
 
@@ -1204,6 +1307,7 @@ def _collect_assets_and_files(
1204
1307
  root_prefix: str,
1205
1308
  budget: "_SizeBudget",
1206
1309
  css_sources: "dict[str, bytes]",
1310
+ warnings: "Optional[list[BundleImportWarning]]" = None,
1207
1311
  ) -> tuple[list[DesignSystemAsset], list[DesignSystemFile]]:
1208
1312
  """Read a bundle into asset rows + file rows in one safety-checked pass.
1209
1313
 
@@ -1268,6 +1372,17 @@ def _collect_assets_and_files(
1268
1372
  rel,
1269
1373
  len(data),
1270
1374
  )
1375
+ # A server-side log alone left the user believing the import was
1376
+ # complete; the same fact rides back on the response.
1377
+ if warnings is not None:
1378
+ warnings.append(
1379
+ BundleImportWarning(
1380
+ rel,
1381
+ "not a PNG, JPEG, GIF or WebP image "
1382
+ f"({len(data)} bytes); this template's thumbnail "
1383
+ "was not stored.",
1384
+ )
1385
+ )
1271
1386
  continue
1272
1387
  mime = sniffed
1273
1388
  else:
@@ -615,6 +615,16 @@ _THUMBNAIL_BASENAME_PREFIXES = ("preview", "thumbnail")
615
615
 
616
616
 
617
617
  def _is_thumbnail_basename(basename: str) -> bool:
618
+ """True for a screenshot basename, with or without a leading dot.
619
+
620
+ A PREFIX match on purpose, matching the breadth of the importer's
621
+ ``preview[^/]*\\.<ext>`` shape. A C0/DEL control character is refused outright:
622
+ the prefix test alone says nothing about what TRAILS the basename, and this is
623
+ the third gate on the same file — the importer now refuses such a path, so this
624
+ is defence in depth that no longer depends on the importer for the property.
625
+ """
626
+ if any(ord(ch) < 0x20 or ord(ch) == 0x7F for ch in basename):
627
+ return False
618
628
  return basename.lstrip(".").startswith(_THUMBNAIL_BASENAME_PREFIXES)
619
629
 
620
630