classifyre-cli 0.4.121__tar.gz → 0.4.123__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 (309) hide show
  1. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/.turbo/turbo-build.log +1 -1
  2. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/PKG-INFO +1 -1
  3. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/package.json +1 -1
  4. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/pyproject.toml +14 -1
  5. classifyre_cli-0.4.123/scripts/generate_sdk_completions.py +206 -0
  6. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/main.py +17 -2
  7. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/models/generated_input.py +193 -0
  8. classifyre_cli-0.4.123/src/notebook/__init__.py +29 -0
  9. classifyre_cli-0.4.123/src/notebook/cli.py +217 -0
  10. classifyre_cli-0.4.123/src/notebook/contract.py +216 -0
  11. classifyre_cli-0.4.123/src/notebook/execute.py +554 -0
  12. classifyre_cli-0.4.123/src/notebook/outputs.py +160 -0
  13. classifyre_cli-0.4.123/src/notebook/packages.py +166 -0
  14. classifyre_cli-0.4.123/src/notebook/protocol.py +191 -0
  15. classifyre_cli-0.4.123/src/notebook/redact.py +119 -0
  16. classifyre_cli-0.4.123/src/notebook/scaffold.py +61 -0
  17. classifyre_cli-0.4.123/src/notebook/sdk.py +298 -0
  18. classifyre_cli-0.4.123/src/notebook/serialize.py +136 -0
  19. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/asset_metadata.py +32 -11
  20. classifyre_cli-0.4.123/src/sources/custom/__init__.py +3 -0
  21. classifyre_cli-0.4.123/src/sources/custom/env.py +80 -0
  22. classifyre_cli-0.4.123/src/sources/custom/runner.py +345 -0
  23. classifyre_cli-0.4.123/src/sources/custom/source.py +652 -0
  24. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/dependency_groups.py +3 -0
  25. classifyre_cli-0.4.123/tests/test_custom_source.py +590 -0
  26. classifyre_cli-0.4.123/tests/test_notebook_execute.py +366 -0
  27. classifyre_cli-0.4.123/tests/test_notebook_packages.py +142 -0
  28. classifyre_cli-0.4.123/tests/test_notebook_serialize.py +80 -0
  29. classifyre_cli-0.4.123/tests/test_sdk_completions.py +85 -0
  30. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/uv.lock +328 -136
  31. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/.gitignore +0 -0
  32. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/.python-version +0 -0
  33. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/README.md +0 -0
  34. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/main.py +0 -0
  35. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/scripts/generate_models.py +0 -0
  36. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/__init__.py +0 -0
  37. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/config.py +0 -0
  38. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/__init__.py +0 -0
  39. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/base.py +0 -0
  40. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/broken_links/__init__.py +0 -0
  41. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/broken_links/detector.py +0 -0
  42. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/config.py +0 -0
  43. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/content/__init__.py +0 -0
  44. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/custom/__init__.py +0 -0
  45. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/custom/detector.py +0 -0
  46. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/custom/extractor.py +0 -0
  47. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/custom/runners/__init__.py +0 -0
  48. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/custom/runners/_base.py +0 -0
  49. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/custom/runners/_factory.py +0 -0
  50. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/custom/runners/_gliner2.py +0 -0
  51. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/custom/runners/_image_classification.py +0 -0
  52. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/custom/runners/_llm.py +0 -0
  53. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/custom/runners/_object_detection.py +0 -0
  54. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/custom/runners/_regex.py +0 -0
  55. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/custom/runners/_text_classification.py +0 -0
  56. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/custom/trainer.py +0 -0
  57. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/dependencies.py +0 -0
  58. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/engine_version.py +0 -0
  59. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/pii/__init__.py +0 -0
  60. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/pii/detector.py +0 -0
  61. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/secrets/__init__.py +0 -0
  62. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/secrets/detector.py +0 -0
  63. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/threat/__init__.py +0 -0
  64. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/threat/code_security_detector.py +0 -0
  65. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/detectors/threat/yara_detector.py +0 -0
  66. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/file_evaluation/__init__.py +0 -0
  67. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/file_evaluation/runner.py +0 -0
  68. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/models/generated_detectors.py +0 -0
  69. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/models/generated_single_asset_scan_results.py +0 -0
  70. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/outputs/__init__.py +0 -0
  71. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/outputs/base.py +0 -0
  72. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/outputs/console.py +0 -0
  73. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/outputs/factory.py +0 -0
  74. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/outputs/file.py +0 -0
  75. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/outputs/rest.py +0 -0
  76. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/pipeline/__init__.py +0 -0
  77. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/pipeline/content_provider.py +0 -0
  78. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/pipeline/detector_pipeline.py +0 -0
  79. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/pipeline/parsed_content_provider.py +0 -0
  80. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/pipeline/payload_window.py +0 -0
  81. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/pipeline/scan_cache.py +0 -0
  82. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/pipeline/text_artifact.py +0 -0
  83. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/pipeline/worker_pool.py +0 -0
  84. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/__init__.py +0 -0
  85. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/atlassian_common.py +0 -0
  86. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/azure_blob_storage/__init__.py +0 -0
  87. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/azure_blob_storage/source.py +0 -0
  88. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/base.py +0 -0
  89. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/confluence/__init__.py +0 -0
  90. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/confluence/source.py +0 -0
  91. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/databricks/__init__.py +0 -0
  92. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/databricks/source.py +0 -0
  93. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/delta_lake/__init__.py +0 -0
  94. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/delta_lake/source.py +0 -0
  95. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/dependencies.py +0 -0
  96. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/dropbox/__init__.py +0 -0
  97. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/dropbox/auth.py +0 -0
  98. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/dropbox/source.py +0 -0
  99. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/elasticsearch/__init__.py +0 -0
  100. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/elasticsearch/source.py +0 -0
  101. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/email/__init__.py +0 -0
  102. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/email/source.py +0 -0
  103. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/git/__init__.py +0 -0
  104. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/git/source.py +0 -0
  105. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/google_cloud_storage/__init__.py +0 -0
  106. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/google_cloud_storage/source.py +0 -0
  107. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/google_workspace/__init__.py +0 -0
  108. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/google_workspace/source.py +0 -0
  109. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/hive/__init__.py +0 -0
  110. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/hive/source.py +0 -0
  111. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/hugging_face/__init__.py +0 -0
  112. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/hugging_face/source.py +0 -0
  113. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/iceberg/__init__.py +0 -0
  114. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/iceberg/source.py +0 -0
  115. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/jira/__init__.py +0 -0
  116. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/jira/source.py +0 -0
  117. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/kafka/__init__.py +0 -0
  118. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/kafka/rest.py +0 -0
  119. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/kafka/source.py +0 -0
  120. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/lakehouse_base.py +0 -0
  121. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/local_folder/__init__.py +0 -0
  122. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/local_folder/source.py +0 -0
  123. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/meilisearch/__init__.py +0 -0
  124. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/meilisearch/source.py +0 -0
  125. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/microsoft_365/__init__.py +0 -0
  126. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/microsoft_365/source.py +0 -0
  127. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/mongodb/__init__.py +0 -0
  128. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/mongodb/source.py +0 -0
  129. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/mssql/__init__.py +0 -0
  130. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/mssql/source.py +0 -0
  131. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/mysql/__init__.py +0 -0
  132. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/mysql/source.py +0 -0
  133. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/neo4j/__init__.py +0 -0
  134. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/neo4j/source.py +0 -0
  135. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/notion/__init__.py +0 -0
  136. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/notion/client.py +0 -0
  137. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/notion/source.py +0 -0
  138. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/object_storage/base.py +0 -0
  139. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/opensearch/__init__.py +0 -0
  140. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/opensearch/source.py +0 -0
  141. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/oracle/__init__.py +0 -0
  142. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/oracle/source.py +0 -0
  143. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/postgresql/__init__.py +0 -0
  144. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/postgresql/source.py +0 -0
  145. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/powerbi/__init__.py +0 -0
  146. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/powerbi/source.py +0 -0
  147. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/recipe_normalizer.py +0 -0
  148. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/reddit/__init__.py +0 -0
  149. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/reddit/source.py +0 -0
  150. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/s3_client.py +0 -0
  151. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/s3_compatible_storage/README.md +0 -0
  152. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/s3_compatible_storage/__init__.py +0 -0
  153. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/s3_compatible_storage/source.py +0 -0
  154. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/sandbox/__init__.py +0 -0
  155. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/sandbox/source.py +0 -0
  156. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/search_engine_base.py +0 -0
  157. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/servicedesk/__init__.py +0 -0
  158. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/servicedesk/source.py +0 -0
  159. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/slack/__init__.py +0 -0
  160. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/slack/source.py +0 -0
  161. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/snowflake/__init__.py +0 -0
  162. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/snowflake/source.py +0 -0
  163. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/sqlite/__init__.py +0 -0
  164. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/sqlite/source.py +0 -0
  165. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/tableau/__init__.py +0 -0
  166. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/tableau/source.py +0 -0
  167. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/tabular_base.py +0 -0
  168. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/tabular_utils.py +0 -0
  169. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/wordpress/__init__.py +0 -0
  170. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/wordpress/source.py +0 -0
  171. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/youtube/__init__.py +0 -0
  172. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/sources/youtube/source.py +0 -0
  173. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/telemetry.py +0 -0
  174. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/__init__.py +0 -0
  175. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/archive_extraction.py +0 -0
  176. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/content_extraction.py +0 -0
  177. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/embedded_files.py +0 -0
  178. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/external_video.py +0 -0
  179. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/file_metadata.py +0 -0
  180. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/file_parser.py +0 -0
  181. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/file_to_images.py +0 -0
  182. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/hashing.py +0 -0
  183. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/legacy_office.py +0 -0
  184. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/payload.py +0 -0
  185. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/range_reader.py +0 -0
  186. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/resources.py +0 -0
  187. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/torch_runtime.py +0 -0
  188. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/transcription.py +0 -0
  189. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/uv_sync.py +0 -0
  190. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/validation.py +0 -0
  191. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/src/utils/video_processing.py +0 -0
  192. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/__init__.py +0 -0
  193. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/_lakehouse_fakes.py +0 -0
  194. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/conftest.py +0 -0
  195. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/__init__.py +0 -0
  196. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/broken_links/test_broken_links_detector.py +0 -0
  197. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/conftest.py +0 -0
  198. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/content/__init__.py +0 -0
  199. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/custom/__init__.py +0 -0
  200. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/custom/conftest.py +0 -0
  201. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/custom/test_gliner2_classification.py +0 -0
  202. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/custom/test_invoice_extraction.py +0 -0
  203. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/custom/test_llm_runner.py +0 -0
  204. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/custom/test_pipeline_integration.py +0 -0
  205. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/custom/test_regex_runner.py +0 -0
  206. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/custom/test_runner_hardening.py +0 -0
  207. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/custom/test_transformer_runners.py +0 -0
  208. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/pii/__init__.py +0 -0
  209. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/pii/conftest.py +0 -0
  210. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/pii/sample_invoice.pdf +0 -0
  211. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/pii/test_pii_chunking.py +0 -0
  212. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/pii/test_pii_detector.py +0 -0
  213. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/pii/test_pii_detector_extended.py +0 -0
  214. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/pii/test_pii_regional_entities.py +0 -0
  215. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/pii/test_pii_severity.py +0 -0
  216. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/secrets/__init__.py +0 -0
  217. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/secrets/test_secrets_detector.py +0 -0
  218. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/secrets/test_secrets_detector_extended.py +0 -0
  219. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/test_base_detector.py +0 -0
  220. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/test_custom_detector_examples_runtime.py +0 -0
  221. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/test_detector_catalog_commercial.py +0 -0
  222. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/test_detector_pipeline_types.py +0 -0
  223. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/test_detector_schema_examples.py +0 -0
  224. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/test_detector_types.py +0 -0
  225. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/test_phase2_detectors.py +0 -0
  226. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/test_registry.py +0 -0
  227. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/threat/__init__.py +0 -0
  228. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/threat/test_code_security_detector.py +0 -0
  229. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/detectors/threat/test_yara_detector.py +0 -0
  230. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/integration/test_wordpress_broken_links_detector.py +0 -0
  231. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/integration/test_wordpress_links_assets.py +0 -0
  232. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/pipeline/test_detector_outcomes.py +0 -0
  233. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/pipeline/test_detector_pipeline.py +0 -0
  234. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/pipeline/test_worker_pool.py +0 -0
  235. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_arrow_ipc_payloads.py +0 -0
  236. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_assets_metadata_catalog.py +0 -0
  237. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_azure_blob_storage_source.py +0 -0
  238. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_base_source_attachment.py +0 -0
  239. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_base_source_sampling.py +0 -0
  240. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_byte_valued_fields.py +0 -0
  241. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_config.py +0 -0
  242. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_confluence_source.py +0 -0
  243. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_custom_extractor.py +0 -0
  244. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_databricks_source.py +0 -0
  245. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_delta_lake_source.py +0 -0
  246. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_dependency_groups.py +0 -0
  247. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_dropbox_auth_command.py +0 -0
  248. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_dropbox_source.py +0 -0
  249. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_elasticsearch_source.py +0 -0
  250. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_email_source.py +0 -0
  251. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_file_evaluation_runner.py +0 -0
  252. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_git_source.py +0 -0
  253. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_google_cloud_storage_source.py +0 -0
  254. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_google_workspace_source.py +0 -0
  255. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_hashing.py +0 -0
  256. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_hive_source.py +0 -0
  257. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_hugging_face_source.py +0 -0
  258. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_iceberg_source.py +0 -0
  259. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_jira_source.py +0 -0
  260. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_kafka_source.py +0 -0
  261. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_local_folder_source.py +0 -0
  262. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_main.py +0 -0
  263. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_meilisearch_source.py +0 -0
  264. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_microsoft_365_source.py +0 -0
  265. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_mongodb_source.py +0 -0
  266. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_mssql_source.py +0 -0
  267. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_mysql_source.py +0 -0
  268. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_neo4j_source.py +0 -0
  269. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_notion_source.py +0 -0
  270. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_object_storage_download_efficiency.py +0 -0
  271. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_opensearch_source.py +0 -0
  272. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_oracle_source.py +0 -0
  273. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_outputs.py +0 -0
  274. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_payload_window.py +0 -0
  275. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_payload_window_parquet.py +0 -0
  276. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_postgresql_source.py +0 -0
  277. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_powerbi_source.py +0 -0
  278. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_range_reading.py +0 -0
  279. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_recipe_normalizer.py +0 -0
  280. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_reddit_source.py +0 -0
  281. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_rest_backpressure.py +0 -0
  282. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_s3_compatible_storage_source.py +0 -0
  283. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_sampling_automatic.py +0 -0
  284. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_sandbox_source.py +0 -0
  285. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_scan_cache.py +0 -0
  286. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_servicedesk_source.py +0 -0
  287. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_slack_source.py +0 -0
  288. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_snowflake_source.py +0 -0
  289. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_source_dependency_groups.py +0 -0
  290. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_source_strategy_conformance.py +0 -0
  291. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_sqlite_source.py +0 -0
  292. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_tableau_source.py +0 -0
  293. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_tabular_automatic_sampling.py +0 -0
  294. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_tabular_utils.py +0 -0
  295. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_torch_runtime.py +0 -0
  296. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_uv_sync.py +0 -0
  297. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_wordpress_source.py +0 -0
  298. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_youtube_source.py +0 -0
  299. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/test_youtube_source_integration.py +0 -0
  300. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/utils/test_archive_extraction.py +0 -0
  301. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/utils/test_content_extraction.py +0 -0
  302. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/utils/test_embedded_files.py +0 -0
  303. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/utils/test_file_metadata.py +0 -0
  304. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/utils/test_file_parser.py +0 -0
  305. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/utils/test_file_to_images.py +0 -0
  306. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/utils/test_legacy_office.py +0 -0
  307. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/utils/test_payload.py +0 -0
  308. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/utils/test_transcription.py +0 -0
  309. {classifyre_cli-0.4.121 → classifyre_cli-0.4.123}/tests/utils/test_video_processing.py +0 -0
@@ -1,3 +1,3 @@
1
1
  $ uv sync
2
- Resolved 309 packages in 236ms
2
+ Resolved 323 packages in 9ms
3
3
  Checked 51 packages in 1ms
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: classifyre-cli
3
- Version: 0.4.121
3
+ Version: 0.4.123
4
4
  Summary: Classifyre CLI — scan and classify unstructured data sources
5
5
  License: MIT
6
6
  Keywords: data,ingestion,metadata,pii,secrets,unstructured
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classifyre/cli",
3
- "version": "0.4.121",
3
+ "version": "0.4.123",
4
4
  "private": true,
5
5
  "scripts": {
6
6
  "build": "uv sync",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "classifyre-cli"
3
- version = "0.4.121"
3
+ version = "0.4.123"
4
4
  description = "Classifyre CLI — scan and classify unstructured data sources"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -45,6 +45,13 @@ build-backend = "hatchling.build"
45
45
  packages = ["src"]
46
46
 
47
47
  [dependency-groups]
48
+ # Notebook authoring only. `run_cell` is what makes a bare `df` on a cell's last
49
+ # line display as a table instead of being discarded, and what gives rich
50
+ # outputs a display protocol. Production never installs this: a productized
51
+ # notebook is an ordinary Python module run with `python workflow.py`.
52
+ interactive = [
53
+ "ipython>=8.30.0",
54
+ ]
48
55
  dev = [
49
56
  "ruff>=0.15.13",
50
57
  "mypy>=1.13.0",
@@ -340,6 +347,12 @@ ignore_missing_imports = true
340
347
  module = ["bs4.*", "lxml.*"]
341
348
  ignore_missing_imports = true
342
349
 
350
+ # Notebook authoring extras: present only when the `interactive` group is
351
+ # installed, so the executor imports them defensively.
352
+ [[tool.mypy.overrides]]
353
+ module = ["IPython.*", "matplotlib.*"]
354
+ ignore_missing_imports = true
355
+
343
356
  [[tool.mypy.overrides]]
344
357
  module = [
345
358
  "neo4j.*",
@@ -0,0 +1,206 @@
1
+ """Generate the notebook editor's SDK completions from the SDK itself.
2
+
3
+ The editor has to offer `ctx.secret(...)` and `Asset(...)` with the right
4
+ arguments, and a hand-written list of those would be wrong the first time
5
+ someone adds a parameter. So the list is derived by introspecting the runtime
6
+ objects a notebook actually receives -- add a method to ``Context`` and the
7
+ editor offers it, with its real signature and its real docstring.
8
+
9
+ Run from apps/cli:
10
+
11
+ uv run python scripts/generate_sdk_completions.py
12
+
13
+ ``tests/test_sdk_completions.py`` fails when the checked-in file drifts from
14
+ what this would produce, so the two cannot silently diverge.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import dataclasses
20
+ import inspect
21
+ import json
22
+ import sys
23
+ from pathlib import Path
24
+ from typing import Any
25
+
26
+ CLI_ROOT = Path(__file__).resolve().parents[1]
27
+ sys.path.insert(0, str(CLI_ROOT))
28
+
29
+ from src.notebook.contract import ( # noqa: E402
30
+ OPTIONAL_FUNCTIONS,
31
+ REQUIRED_FUNCTIONS,
32
+ )
33
+ from src.notebook.sdk import Asset, Context # noqa: E402
34
+
35
+ OUTPUT_PATH = (
36
+ CLI_ROOT.parents[1] / "packages" / "schemas" / "src" / "schemas" / "notebook_completions.json"
37
+ )
38
+
39
+
40
+ def _summary(obj: Any) -> str:
41
+ """The whole docstring, which is what the editor shows on demand.
42
+
43
+ Not just the first paragraph: the part of a docstring that changes what an
44
+ author writes is often the caveat below it -- that reading ``ctx.offset``
45
+ hands paging to the notebook, say -- and truncating to a one-liner is
46
+ exactly how that gets lost.
47
+ """
48
+ return (inspect.getdoc(obj) or "").strip()
49
+
50
+
51
+ def _signature(func: Any, *, drop_self: bool = True) -> str:
52
+ try:
53
+ signature = inspect.signature(func)
54
+ except (TypeError, ValueError):
55
+ return "()"
56
+ parameters = [
57
+ parameter
58
+ for name, parameter in signature.parameters.items()
59
+ if not (drop_self and name == "self")
60
+ ]
61
+ return str(signature.replace(parameters=parameters))
62
+
63
+
64
+ def _return_type(func: Any) -> str:
65
+ """The annotated return type, rendered as it appears in the source."""
66
+ try:
67
+ annotation = inspect.signature(func).return_annotation
68
+ except (TypeError, ValueError):
69
+ return ""
70
+ if annotation is inspect.Signature.empty:
71
+ return ""
72
+ return str(annotation).strip("'")
73
+
74
+
75
+ def _snippet(name: str, func: Any) -> str:
76
+ """A tab-stop snippet, so calling a method lands the cursor in its first arg."""
77
+ try:
78
+ signature = inspect.signature(func)
79
+ except (TypeError, ValueError):
80
+ return f"{name}()"
81
+ required = [
82
+ parameter_name
83
+ for parameter_name, parameter in signature.parameters.items()
84
+ if parameter_name != "self"
85
+ and parameter.default is inspect.Parameter.empty
86
+ and parameter.kind not in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD)
87
+ ]
88
+ if not required:
89
+ return f"{name}()"
90
+ placeholders = ", ".join(
91
+ f"${{{index}:{parameter}}}" for index, parameter in enumerate(required, start=1)
92
+ )
93
+ return f"{name}({placeholders})"
94
+
95
+
96
+ #: Public on Context because the runtime reads them, but not part of what a
97
+ #: notebook author writes -- offering them would be noise at best and a
98
+ #: misleading suggestion at worst.
99
+ RUNTIME_ONLY_MEMBERS = frozenset({"set_offset", "offset_consumed", "next_cursor"})
100
+
101
+
102
+ def _members(cls: type) -> list[dict[str, Any]]:
103
+ members: list[dict[str, Any]] = []
104
+ for name, member in sorted(vars(cls).items()):
105
+ if name.startswith("_") or name in RUNTIME_ONLY_MEMBERS:
106
+ continue
107
+ if isinstance(member, property):
108
+ members.append(
109
+ {
110
+ "label": name,
111
+ "kind": "property",
112
+ # A property is read, not called, so showing its getter's
113
+ # "()" would suggest otherwise. Only the type it yields.
114
+ "detail": _return_type(member.fget) if member.fget else "",
115
+ "documentation": _summary(member),
116
+ "insertText": name,
117
+ }
118
+ )
119
+ elif inspect.isfunction(member) or isinstance(member, staticmethod):
120
+ func = member.__func__ if isinstance(member, staticmethod) else member
121
+ members.append(
122
+ {
123
+ "label": name,
124
+ "kind": "method",
125
+ "detail": _signature(func),
126
+ "documentation": _summary(func),
127
+ "insertText": _snippet(name, func),
128
+ }
129
+ )
130
+ return members
131
+
132
+
133
+ def _fields(cls: type) -> list[dict[str, Any]]:
134
+ """Dataclass fields, offered as keyword arguments when constructing one."""
135
+ annotations = getattr(cls, "__annotations__", {})
136
+ dataclass_fields = getattr(cls, "__dataclass_fields__", {})
137
+ fields: list[dict[str, Any]] = []
138
+ for name in annotations:
139
+ if name.startswith("_"):
140
+ continue
141
+ field = dataclass_fields.get(name)
142
+ # dataclasses signal "no default" with MISSING, not Parameter.empty --
143
+ # the wrong sentinel marks every field optional.
144
+ required = field is not None and (
145
+ field.default is dataclasses.MISSING and field.default_factory is dataclasses.MISSING
146
+ )
147
+ fields.append(
148
+ {
149
+ "label": name,
150
+ "kind": "field",
151
+ "detail": str(annotations[name]),
152
+ "required": required,
153
+ "insertText": f"{name}=",
154
+ }
155
+ )
156
+ return fields
157
+
158
+
159
+ def build() -> dict[str, Any]:
160
+ return {
161
+ "$comment": (
162
+ "Generated by apps/cli/scripts/generate_sdk_completions.py from "
163
+ "apps/cli/src/notebook/sdk.py. Do not edit by hand."
164
+ ),
165
+ "module": "classifyre",
166
+ "globals": [
167
+ {
168
+ "label": "ctx",
169
+ "kind": "variable",
170
+ "detail": "Context",
171
+ "documentation": _summary(Context),
172
+ "insertText": "ctx",
173
+ },
174
+ {
175
+ "label": "Asset",
176
+ "kind": "class",
177
+ "detail": "Asset(...)",
178
+ "documentation": _summary(Asset),
179
+ "insertText": "Asset",
180
+ },
181
+ ],
182
+ "objects": {
183
+ "ctx": {"type": "Context", "members": _members(Context)},
184
+ },
185
+ "classes": {
186
+ "Asset": {
187
+ "documentation": _summary(Asset),
188
+ "fields": _fields(Asset),
189
+ },
190
+ },
191
+ "contract": {
192
+ "required": list(REQUIRED_FUNCTIONS),
193
+ "optional": list(OPTIONAL_FUNCTIONS),
194
+ },
195
+ }
196
+
197
+
198
+ def main() -> int:
199
+ payload = build()
200
+ OUTPUT_PATH.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
201
+ print(f"Wrote {OUTPUT_PATH.relative_to(CLI_ROOT.parents[1])}")
202
+ return 0
203
+
204
+
205
+ if __name__ == "__main__":
206
+ raise SystemExit(main())
@@ -743,14 +743,24 @@ def main() -> None:
743
743
  parser = argparse.ArgumentParser(description="Classifyre Metadata Extraction CLI")
744
744
  parser.add_argument(
745
745
  "command",
746
- choices=["test", "extract", "discover", "evaluate-file", "train", "dropbox-auth"],
746
+ choices=[
747
+ "test",
748
+ "extract",
749
+ "discover",
750
+ "notebook",
751
+ "evaluate-file",
752
+ "train",
753
+ "dropbox-auth",
754
+ ],
747
755
  help="Command to run",
748
756
  )
749
757
  parser.add_argument(
750
758
  "recipe",
751
759
  nargs="?",
752
760
  default=None,
753
- help="Path to recipe JSON (or file path for evaluate-file)",
761
+ help=(
762
+ "Path to recipe JSON (a file path for evaluate-file, an execution request for notebook)"
763
+ ),
754
764
  )
755
765
  parser.add_argument("--debug", action="store_true", help="Enable debug logging")
756
766
  parser.add_argument(
@@ -856,6 +866,11 @@ def main() -> None:
856
866
  if args.command == "dropbox-auth":
857
867
  sys.exit(run_dropbox_auth_command(args))
858
868
 
869
+ if args.command == "notebook":
870
+ from .notebook.cli import run_notebook_command
871
+
872
+ sys.exit(run_notebook_command(args))
873
+
859
874
  if args.command == "evaluate-file":
860
875
  run_evaluate_file_command(args)
861
876
  return
@@ -14,6 +14,7 @@ from pydantic import (
14
14
  EmailStr,
15
15
  Field,
16
16
  RootModel,
17
+ constr,
17
18
  )
18
19
 
19
20
 
@@ -59,6 +60,7 @@ class AssetType(StrEnum):
59
60
  HUGGING_FACE = 'HUGGING_FACE'
60
61
  SANDBOX = 'SANDBOX'
61
62
  GIT = 'GIT'
63
+ CUSTOM = 'CUSTOM'
62
64
 
63
65
 
64
66
  class DetectorType(StrEnum):
@@ -391,6 +393,7 @@ class Type(StrEnum):
391
393
  HUGGING_FACE = 'HUGGING_FACE'
392
394
  SANDBOX = 'SANDBOX'
393
395
  GIT = 'GIT'
396
+ CUSTOM = 'CUSTOM'
394
397
 
395
398
 
396
399
  class YouTubeRequired(BaseModel):
@@ -3473,6 +3476,7 @@ class Type22(StrEnum):
3473
3476
  HUGGING_FACE = 'HUGGING_FACE'
3474
3477
  SANDBOX = 'SANDBOX'
3475
3478
  GIT = 'GIT'
3479
+ CUSTOM = 'CUSTOM'
3476
3480
 
3477
3481
 
3478
3482
  class ConfluenceInput(CoreInput):
@@ -5278,6 +5282,151 @@ class GitInput(CoreInput):
5278
5282
  )
5279
5283
 
5280
5284
 
5285
+ class NotebookCodeCell(BaseModel):
5286
+ """
5287
+ A Python cell. Must remain valid standard Python -- IPython magics (%time, !pip) are rejected.
5288
+ """
5289
+
5290
+ model_config = ConfigDict(
5291
+ extra='forbid',
5292
+ )
5293
+ id: str = Field(
5294
+ ...,
5295
+ description='Stable cell identifier, unique within the notebook',
5296
+ pattern='^[A-Za-z0-9_-]{1,64}$',
5297
+ )
5298
+ type: Literal['code']
5299
+ source: str = Field(
5300
+ ..., description='Python source of this cell', max_length=100000
5301
+ )
5302
+
5303
+
5304
+ class NotebookMarkdownCell(BaseModel):
5305
+ """
5306
+ Prose. Never executed; serialized as comments when the notebook is exported to Python.
5307
+ """
5308
+
5309
+ model_config = ConfigDict(
5310
+ extra='forbid',
5311
+ )
5312
+ id: str = Field(
5313
+ ...,
5314
+ description='Stable cell identifier, unique within the notebook',
5315
+ pattern='^[A-Za-z0-9_-]{1,64}$',
5316
+ )
5317
+ type: Literal['markdown']
5318
+ source: str = Field(
5319
+ ..., description='Markdown source of this cell', max_length=100000
5320
+ )
5321
+
5322
+
5323
+ class CustomNotebook(BaseModel):
5324
+ """
5325
+ Cells run in document order in one fresh process per execution. There is no persistent kernel: state is rebuilt from the current cell sources every time.
5326
+ """
5327
+
5328
+ model_config = ConfigDict(
5329
+ extra='forbid',
5330
+ )
5331
+ revision: int | None = Field(
5332
+ 1,
5333
+ description='Monotonic revision, bumped on every save. Used for optimistic locking so two editors cannot silently overwrite each other.',
5334
+ ge=1,
5335
+ )
5336
+ cells: list[NotebookCodeCell | NotebookMarkdownCell] = Field(
5337
+ ...,
5338
+ description='Ordered notebook cells. The assembled code cells must define test_connection() and extract().',
5339
+ max_length=200,
5340
+ min_length=1,
5341
+ )
5342
+
5343
+
5344
+ class CustomRequired(BaseModel):
5345
+ """
5346
+ The Python notebook that implements this connector.
5347
+ """
5348
+
5349
+ model_config = ConfigDict(
5350
+ extra='forbid',
5351
+ )
5352
+ notebook: CustomNotebook
5353
+
5354
+
5355
+ class Secrets(RootModel[str]):
5356
+ root: str = Field(..., max_length=8192)
5357
+
5358
+
5359
+ class CustomMasked(BaseModel):
5360
+ """
5361
+ Encrypted key/value pairs, stored the same way as any other source credential.
5362
+ """
5363
+
5364
+ model_config = ConfigDict(
5365
+ extra='forbid',
5366
+ )
5367
+ secrets: dict[constr(pattern=r'^[A-Za-z_][A-Za-z0-9_]{0,62}$'), Secrets] | None = (
5368
+ Field(
5369
+ {},
5370
+ description='Secret values the notebook reads with ctx.secret("name"). Encrypted at rest and redacted from logs and cell output. Keys must be valid Python identifiers.',
5371
+ max_length=64,
5372
+ validate_default=True,
5373
+ )
5374
+ )
5375
+
5376
+
5377
+ class CustomOptionalLimits(BaseModel):
5378
+ """
5379
+ Bounds applied to every notebook execution and scan.
5380
+ """
5381
+
5382
+ model_config = ConfigDict(
5383
+ extra='forbid',
5384
+ )
5385
+ max_assets: int | None = Field(
5386
+ None,
5387
+ description='Stop a scan after this many assets. Unset means no cap.',
5388
+ ge=1,
5389
+ le=1000000,
5390
+ )
5391
+ timeout_seconds: int | None = Field(
5392
+ 900,
5393
+ description='Kill an execution that runs longer than this. Cells cannot be interrupted from inside Python, so this is the real stop button.',
5394
+ ge=10,
5395
+ le=86400,
5396
+ )
5397
+ max_output_bytes: int | None = Field(
5398
+ 2097152,
5399
+ description='Total serialized cell output kept per execution. Larger outputs are truncated rather than stored.',
5400
+ ge=1024,
5401
+ le=52428800,
5402
+ )
5403
+
5404
+
5405
+ class Variables(RootModel[str]):
5406
+ root: str = Field(..., max_length=8192)
5407
+
5408
+
5409
+ class NotebookPackage(BaseModel):
5410
+ """
5411
+ A Python distribution installed before the notebook runs.
5412
+ """
5413
+
5414
+ model_config = ConfigDict(
5415
+ extra='forbid',
5416
+ )
5417
+ name: str = Field(
5418
+ ...,
5419
+ description="Distribution name as published on the index (for example 'pandas').",
5420
+ pattern='^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$',
5421
+ )
5422
+ version: str | None = Field(
5423
+ None,
5424
+ description="Optional version or specifier: '2.2.0', '>=2.0', '~=1.4'. Empty means latest.",
5425
+ max_length=64,
5426
+ pattern='^$|^(==|>=|<=|~=|!=|>|<).+$|^[0-9][A-Za-z0-9._*+!-]*$',
5427
+ )
5428
+
5429
+
5281
5430
  class YouTubeInput(CoreInput):
5282
5431
  type: Literal['YOUTUBE'] = Field(..., description='Type of the asset or source')
5283
5432
  required: YouTubeRequired
@@ -5413,6 +5562,48 @@ class IcebergInput(CoreInput):
5413
5562
  )
5414
5563
 
5415
5564
 
5565
+ class CustomOptional(BaseModel):
5566
+ model_config = ConfigDict(
5567
+ extra='forbid',
5568
+ )
5569
+ variables: (
5570
+ dict[constr(pattern=r'^[A-Za-z_][A-Za-z0-9_]{0,62}$'), Variables] | None
5571
+ ) = Field(
5572
+ {},
5573
+ description='Non-secret values the notebook reads with ctx.var("name"), such as a base URL or an account id. Stored in clear text. Keys must be valid Python identifiers.',
5574
+ max_length=64,
5575
+ validate_default=True,
5576
+ )
5577
+ limits: CustomOptionalLimits | None = None
5578
+ packages: list[NotebookPackage] | None = Field(
5579
+ [],
5580
+ description="Python packages installed into the run environment before any cell executes. Installed with uv; the base image's own dependencies are always present and do not need listing.",
5581
+ max_length=50,
5582
+ validate_default=True,
5583
+ )
5584
+
5585
+
5586
+ class CustomInput(CoreInput):
5587
+ type: Literal['CUSTOM'] = Field(..., description='Type of the asset or source')
5588
+ required: CustomRequired
5589
+ masked: CustomMasked | None = None
5590
+ optional: CustomOptional | None = None
5591
+ detectors: list[Detector] | None = Field(
5592
+ None, description='Detectors to run on ingested content'
5593
+ )
5594
+ custom_detectors: list[CustomDetectorSelection] | None = Field(
5595
+ None,
5596
+ description='Reusable custom detector IDs selected from the custom detector catalog.',
5597
+ )
5598
+ sampling: SamplingConfig
5599
+ scan_cache: ScanCacheConfig | None = None
5600
+ resources: ResourceOverrides | None = None
5601
+ cleanup_removed_detector_findings: bool | None = Field(
5602
+ True,
5603
+ description='When enabled (default), findings produced by detectors that are no longer configured on this source (removed or disabled) are automatically resolved at the start of the next run, keeping the findings list in step with the current detector set.',
5604
+ )
5605
+
5606
+
5416
5607
  class SourceInput(
5417
5608
  RootModel[
5418
5609
  SandboxInput
@@ -5452,6 +5643,7 @@ class SourceInput(
5452
5643
  | DropboxInput
5453
5644
  | HuggingFaceInput
5454
5645
  | GitInput
5646
+ | CustomInput
5455
5647
  ]
5456
5648
  ):
5457
5649
  root: (
@@ -5492,6 +5684,7 @@ class SourceInput(
5492
5684
  | DropboxInput
5493
5685
  | HuggingFaceInput
5494
5686
  | GitInput
5687
+ | CustomInput
5495
5688
  ) = Field(
5496
5689
  ...,
5497
5690
  description='Merged configuration schema with all source types and common definitions',
@@ -0,0 +1,29 @@
1
+ """Notebook runtime for the CUSTOM source.
2
+
3
+ A notebook is a list of cells, not a running kernel. Every execution assembles
4
+ the current cell sources into one ordinary Python module, runs it in a fresh
5
+ process, serializes what the target cell produced, and exits. Nothing survives
6
+ between executions except the cell sources themselves -- which is what keeps
7
+ "what you see" and "what runs" the same thing.
8
+ """
9
+
10
+ from .contract import ContractReport, ContractViolation, validate_notebook
11
+ from .protocol import ExecutionMode, ExecutionRequest, ExecutionResponse
12
+ from .redact import Redactor
13
+ from .scaffold import cell_for_function, scaffold_cells
14
+ from .serialize import CellSpan, ModuleSource, to_module_source
15
+
16
+ __all__ = [
17
+ "CellSpan",
18
+ "ContractReport",
19
+ "ContractViolation",
20
+ "ExecutionMode",
21
+ "ExecutionRequest",
22
+ "ExecutionResponse",
23
+ "ModuleSource",
24
+ "Redactor",
25
+ "cell_for_function",
26
+ "scaffold_cells",
27
+ "to_module_source",
28
+ "validate_notebook",
29
+ ]