classifyre-cli 0.6.3__tar.gz → 0.6.4__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 (373) hide show
  1. classifyre_cli-0.6.4/.turbo/turbo-build.log +3 -0
  2. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/PKG-INFO +1 -1
  3. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/package.json +1 -1
  4. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/pyproject.toml +1 -1
  5. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/scripts/generate_models.py +12 -0
  6. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/augmentation/session.py +19 -162
  7. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/custom/runners/__init__.py +2 -0
  8. classifyre_cli-0.6.4/src/detectors/custom/runners/_custom_detector.py +72 -0
  9. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_factory.py +6 -0
  10. classifyre_cli-0.6.4/src/detectors/custom_detector/__init__.py +9 -0
  11. classifyre_cli-0.6.4/src/detectors/custom_detector/contract.py +45 -0
  12. classifyre_cli-0.6.4/src/detectors/custom_detector/files.py +102 -0
  13. classifyre_cli-0.6.4/src/detectors/custom_detector/preview.py +283 -0
  14. classifyre_cli-0.6.4/src/detectors/custom_detector/results.py +266 -0
  15. classifyre_cli-0.6.4/src/detectors/custom_detector/runner.py +258 -0
  16. classifyre_cli-0.6.4/src/detectors/custom_detector/sdk.py +334 -0
  17. classifyre_cli-0.6.4/src/detectors/custom_detector/session.py +443 -0
  18. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/engine_version.py +41 -1
  19. classifyre_cli-0.6.4/src/file_evaluation/asset_fixture.py +142 -0
  20. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/file_evaluation/runner.py +110 -6
  21. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/main.py +26 -0
  22. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/models/generated_detectors.py +250 -0
  23. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/models/generated_single_asset_scan_results.py +6 -1
  24. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/notebook/child.py +5 -1
  25. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/notebook/cli.py +54 -4
  26. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/notebook/protocol.py +24 -0
  27. classifyre_cli-0.6.4/src/pipeline/asset_payload_server.py +242 -0
  28. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/pipeline/detector_pipeline.py +121 -0
  29. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/pipeline/scan_cache.py +66 -15
  30. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/redaction.py +22 -0
  31. classifyre_cli-0.6.4/tests/detectors/test_custom_detector_session.py +303 -0
  32. classifyre_cli-0.6.4/tests/test_custom_detector_end_to_end.py +152 -0
  33. classifyre_cli-0.6.4/tests/test_custom_detector_evaluation.py +75 -0
  34. classifyre_cli-0.6.4/tests/test_custom_detector_notebook.py +136 -0
  35. classifyre_cli-0.6.4/tests/test_custom_detector_templates.py +86 -0
  36. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_scan_cache.py +46 -0
  37. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/uv.lock +431 -410
  38. classifyre_cli-0.6.3/.turbo/turbo-build.log +0 -3
  39. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/.gitignore +0 -0
  40. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/.python-version +0 -0
  41. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/README.md +0 -0
  42. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/main.py +0 -0
  43. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/scripts/generate_runtime_packages.py +0 -0
  44. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/scripts/generate_sdk_completions.py +0 -0
  45. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/__init__.py +0 -0
  46. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/augmentation/__init__.py +0 -0
  47. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/augmentation/contract.py +0 -0
  48. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/augmentation/preview.py +0 -0
  49. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/augmentation/runner.py +0 -0
  50. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/augmentation/sdk.py +0 -0
  51. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/config.py +0 -0
  52. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/__init__.py +0 -0
  53. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/base.py +0 -0
  54. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/broken_links/__init__.py +0 -0
  55. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/broken_links/detector.py +0 -0
  56. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/config.py +0 -0
  57. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/content/__init__.py +0 -0
  58. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/custom/__init__.py +0 -0
  59. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/custom/detector.py +0 -0
  60. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/custom/extractor.py +0 -0
  61. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_base.py +0 -0
  62. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_gliner2.py +0 -0
  63. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_image_classification.py +0 -0
  64. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_llm.py +0 -0
  65. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_object_detection.py +0 -0
  66. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_regex.py +0 -0
  67. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_tag.py +0 -0
  68. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_text_classification.py +0 -0
  69. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/custom/trainer.py +0 -0
  70. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/dependencies.py +0 -0
  71. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/errors.py +0 -0
  72. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/pii/__init__.py +0 -0
  73. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/pii/detector.py +0 -0
  74. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/secrets/__init__.py +0 -0
  75. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/secrets/detector.py +0 -0
  76. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/threat/__init__.py +0 -0
  77. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/threat/code_security_detector.py +0 -0
  78. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/detectors/threat/yara_detector.py +0 -0
  79. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/file_evaluation/__init__.py +0 -0
  80. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/graph/__init__.py +0 -0
  81. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/graph/edges.py +0 -0
  82. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/models/generated_input.py +0 -0
  83. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/notebook/__init__.py +0 -0
  84. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/notebook/cohort.py +0 -0
  85. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/notebook/contract.py +0 -0
  86. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/notebook/execute.py +0 -0
  87. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/notebook/files.py +0 -0
  88. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/notebook/groups.py +0 -0
  89. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/notebook/outputs.py +0 -0
  90. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/notebook/packages.py +0 -0
  91. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/notebook/redact.py +0 -0
  92. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/notebook/scaffold.py +0 -0
  93. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/notebook/sdk.py +0 -0
  94. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/notebook/serialize.py +0 -0
  95. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/outputs/__init__.py +0 -0
  96. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/outputs/base.py +0 -0
  97. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/outputs/console.py +0 -0
  98. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/outputs/factory.py +0 -0
  99. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/outputs/file.py +0 -0
  100. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/outputs/rest.py +0 -0
  101. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/pipeline/__init__.py +0 -0
  102. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/pipeline/content_provider.py +0 -0
  103. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/pipeline/detector_breaker.py +0 -0
  104. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/pipeline/parsed_content_provider.py +0 -0
  105. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/pipeline/payload_window.py +0 -0
  106. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/pipeline/text_artifact.py +0 -0
  107. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/pipeline/worker_pool.py +0 -0
  108. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/__init__.py +0 -0
  109. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/asset_metadata.py +0 -0
  110. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/atlassian_common.py +0 -0
  111. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/azure_blob_storage/__init__.py +0 -0
  112. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/azure_blob_storage/source.py +0 -0
  113. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/base.py +0 -0
  114. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/confluence/__init__.py +0 -0
  115. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/confluence/source.py +0 -0
  116. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/custom/__init__.py +0 -0
  117. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/custom/env.py +0 -0
  118. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/custom/runner.py +0 -0
  119. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/custom/source.py +0 -0
  120. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/databricks/__init__.py +0 -0
  121. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/databricks/source.py +0 -0
  122. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/delta_lake/__init__.py +0 -0
  123. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/delta_lake/source.py +0 -0
  124. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/dependencies.py +0 -0
  125. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/dropbox/__init__.py +0 -0
  126. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/dropbox/auth.py +0 -0
  127. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/dropbox/source.py +0 -0
  128. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/elasticsearch/__init__.py +0 -0
  129. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/elasticsearch/source.py +0 -0
  130. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/email/__init__.py +0 -0
  131. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/email/source.py +0 -0
  132. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/git/__init__.py +0 -0
  133. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/git/source.py +0 -0
  134. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/google_cloud_storage/__init__.py +0 -0
  135. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/google_cloud_storage/source.py +0 -0
  136. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/google_workspace/__init__.py +0 -0
  137. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/google_workspace/source.py +0 -0
  138. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/hive/__init__.py +0 -0
  139. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/hive/source.py +0 -0
  140. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/hugging_face/__init__.py +0 -0
  141. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/hugging_face/source.py +0 -0
  142. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/iceberg/__init__.py +0 -0
  143. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/iceberg/source.py +0 -0
  144. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/jira/__init__.py +0 -0
  145. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/jira/source.py +0 -0
  146. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/kafka/__init__.py +0 -0
  147. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/kafka/rest.py +0 -0
  148. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/kafka/source.py +0 -0
  149. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/lakehouse_base.py +0 -0
  150. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/local_folder/__init__.py +0 -0
  151. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/local_folder/source.py +0 -0
  152. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/meilisearch/__init__.py +0 -0
  153. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/meilisearch/source.py +0 -0
  154. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/microsoft_365/__init__.py +0 -0
  155. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/microsoft_365/source.py +0 -0
  156. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/mongodb/__init__.py +0 -0
  157. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/mongodb/source.py +0 -0
  158. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/mssql/__init__.py +0 -0
  159. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/mssql/source.py +0 -0
  160. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/mysql/__init__.py +0 -0
  161. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/mysql/source.py +0 -0
  162. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/neo4j/__init__.py +0 -0
  163. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/neo4j/source.py +0 -0
  164. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/notion/__init__.py +0 -0
  165. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/notion/client.py +0 -0
  166. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/notion/source.py +0 -0
  167. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/object_storage/base.py +0 -0
  168. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/opensearch/__init__.py +0 -0
  169. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/opensearch/source.py +0 -0
  170. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/oracle/__init__.py +0 -0
  171. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/oracle/source.py +0 -0
  172. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/postgresql/__init__.py +0 -0
  173. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/postgresql/source.py +0 -0
  174. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/powerbi/__init__.py +0 -0
  175. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/powerbi/source.py +0 -0
  176. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/recipe_normalizer.py +0 -0
  177. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/reddit/__init__.py +0 -0
  178. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/reddit/source.py +0 -0
  179. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/s3_client.py +0 -0
  180. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/s3_compatible_storage/README.md +0 -0
  181. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/s3_compatible_storage/__init__.py +0 -0
  182. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/s3_compatible_storage/source.py +0 -0
  183. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/sandbox/__init__.py +0 -0
  184. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/sandbox/source.py +0 -0
  185. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/search_engine_base.py +0 -0
  186. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/servicedesk/__init__.py +0 -0
  187. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/servicedesk/source.py +0 -0
  188. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/slack/__init__.py +0 -0
  189. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/slack/source.py +0 -0
  190. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/snowflake/__init__.py +0 -0
  191. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/snowflake/source.py +0 -0
  192. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/sqlite/__init__.py +0 -0
  193. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/sqlite/source.py +0 -0
  194. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/tableau/__init__.py +0 -0
  195. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/tableau/source.py +0 -0
  196. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/tabular_base.py +0 -0
  197. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/tabular_utils.py +0 -0
  198. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/wordpress/__init__.py +0 -0
  199. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/wordpress/source.py +0 -0
  200. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/youtube/__init__.py +0 -0
  201. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/sources/youtube/source.py +0 -0
  202. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/telemetry.py +0 -0
  203. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/__init__.py +0 -0
  204. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/archive_extraction.py +0 -0
  205. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/content_extraction.py +0 -0
  206. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/dependency_groups.py +0 -0
  207. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/embedded_files.py +0 -0
  208. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/external_video.py +0 -0
  209. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/file_metadata.py +0 -0
  210. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/file_parser.py +0 -0
  211. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/file_to_images.py +0 -0
  212. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/hashing.py +0 -0
  213. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/legacy_office.py +0 -0
  214. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/payload.py +0 -0
  215. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/range_reader.py +0 -0
  216. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/resources.py +0 -0
  217. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/source_files.py +0 -0
  218. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/sql_lineage.py +0 -0
  219. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/torch_runtime.py +0 -0
  220. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/transcription.py +0 -0
  221. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/urn.py +0 -0
  222. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/uv_sync.py +0 -0
  223. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/validation.py +0 -0
  224. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/src/utils/video_processing.py +0 -0
  225. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/__init__.py +0 -0
  226. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/_lakehouse_fakes.py +0 -0
  227. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/conftest.py +0 -0
  228. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/__init__.py +0 -0
  229. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/broken_links/test_broken_links_detector.py +0 -0
  230. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/conftest.py +0 -0
  231. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/content/__init__.py +0 -0
  232. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/custom/__init__.py +0 -0
  233. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/custom/conftest.py +0 -0
  234. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/custom/test_gliner2_classification.py +0 -0
  235. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/custom/test_invoice_extraction.py +0 -0
  236. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/custom/test_llm_runner.py +0 -0
  237. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/custom/test_pipeline_integration.py +0 -0
  238. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/custom/test_regex_runner.py +0 -0
  239. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/custom/test_runner_hardening.py +0 -0
  240. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/custom/test_transformer_runners.py +0 -0
  241. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/pii/__init__.py +0 -0
  242. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/pii/conftest.py +0 -0
  243. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/pii/sample_invoice.pdf +0 -0
  244. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/pii/test_pii_chunking.py +0 -0
  245. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/pii/test_pii_detector.py +0 -0
  246. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/pii/test_pii_detector_extended.py +0 -0
  247. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/pii/test_pii_regional_entities.py +0 -0
  248. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/pii/test_pii_severity.py +0 -0
  249. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/secrets/__init__.py +0 -0
  250. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/secrets/test_secrets_detector.py +0 -0
  251. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/secrets/test_secrets_detector_extended.py +0 -0
  252. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/test_base_detector.py +0 -0
  253. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/test_custom_detector_examples_runtime.py +0 -0
  254. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/test_detector_catalog_commercial.py +0 -0
  255. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/test_detector_pipeline_types.py +0 -0
  256. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/test_detector_schema_examples.py +0 -0
  257. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/test_detector_types.py +0 -0
  258. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/test_phase2_detectors.py +0 -0
  259. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/test_registry.py +0 -0
  260. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/test_tag_runner.py +0 -0
  261. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/threat/__init__.py +0 -0
  262. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/threat/test_code_security_detector.py +0 -0
  263. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/detectors/threat/test_yara_detector.py +0 -0
  264. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/integration/test_wordpress_broken_links_detector.py +0 -0
  265. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/integration/test_wordpress_links_assets.py +0 -0
  266. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/pipeline/test_detector_breaker.py +0 -0
  267. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/pipeline/test_detector_outcomes.py +0 -0
  268. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/pipeline/test_detector_pipeline.py +0 -0
  269. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/pipeline/test_worker_pool.py +0 -0
  270. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_arrow_ipc_payloads.py +0 -0
  271. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_assets_metadata_catalog.py +0 -0
  272. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_augmentation_contract.py +0 -0
  273. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_augmentation_edges.py +0 -0
  274. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_augmentation_patch.py +0 -0
  275. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_augmentation_scan_cache.py +0 -0
  276. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_augmentation_sdk.py +0 -0
  277. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_augmentation_session.py +0 -0
  278. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_augmentation_tags_end_to_end.py +0 -0
  279. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_azure_blob_storage_source.py +0 -0
  280. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_base_source_attachment.py +0 -0
  281. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_base_source_sampling.py +0 -0
  282. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_byte_valued_fields.py +0 -0
  283. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_config.py +0 -0
  284. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_confluence_source.py +0 -0
  285. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_custom_extractor.py +0 -0
  286. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_custom_source.py +0 -0
  287. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_custom_source_relationships.py +0 -0
  288. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_custom_source_tags_end_to_end.py +0 -0
  289. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_databricks_source.py +0 -0
  290. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_delta_lake_source.py +0 -0
  291. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_dependency_groups.py +0 -0
  292. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_detector_scope.py +0 -0
  293. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_dropbox_auth_command.py +0 -0
  294. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_dropbox_source.py +0 -0
  295. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_elasticsearch_source.py +0 -0
  296. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_email_source.py +0 -0
  297. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_file_evaluation_runner.py +0 -0
  298. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_git_source.py +0 -0
  299. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_google_cloud_storage_source.py +0 -0
  300. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_google_workspace_source.py +0 -0
  301. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_graph_edges.py +0 -0
  302. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_hashing.py +0 -0
  303. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_hive_source.py +0 -0
  304. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_hugging_face_source.py +0 -0
  305. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_iceberg_source.py +0 -0
  306. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_jira_source.py +0 -0
  307. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_kafka_source.py +0 -0
  308. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_lineage_loss_reporting.py +0 -0
  309. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_local_folder_source.py +0 -0
  310. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_main.py +0 -0
  311. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_meilisearch_source.py +0 -0
  312. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_microsoft_365_source.py +0 -0
  313. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_mongodb_source.py +0 -0
  314. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_mssql_source.py +0 -0
  315. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_mysql_source.py +0 -0
  316. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_neo4j_source.py +0 -0
  317. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_notebook_asset_extract.py +0 -0
  318. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_notebook_asset_tags.py +0 -0
  319. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_notebook_cohort.py +0 -0
  320. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_notebook_execute.py +0 -0
  321. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_notebook_files.py +0 -0
  322. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_notebook_groups.py +0 -0
  323. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_notebook_log_levels.py +0 -0
  324. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_notebook_packages.py +0 -0
  325. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_notebook_query_assets.py +0 -0
  326. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_notebook_sdk_exports.py +0 -0
  327. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_notebook_serialize.py +0 -0
  328. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_notebook_templates.py +0 -0
  329. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_notion_source.py +0 -0
  330. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_object_storage_download_efficiency.py +0 -0
  331. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_opensearch_source.py +0 -0
  332. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_oracle_source.py +0 -0
  333. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_outputs.py +0 -0
  334. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_payload_window.py +0 -0
  335. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_payload_window_parquet.py +0 -0
  336. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_postgresql_source.py +0 -0
  337. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_powerbi_source.py +0 -0
  338. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_range_reading.py +0 -0
  339. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_recipe_normalizer.py +0 -0
  340. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_reddit_source.py +0 -0
  341. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_rest_backpressure.py +0 -0
  342. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_runtime_packages.py +0 -0
  343. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_s3_compatible_storage_source.py +0 -0
  344. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_sampling_automatic.py +0 -0
  345. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_sandbox_source.py +0 -0
  346. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_sdk_completions.py +0 -0
  347. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_secret_redaction.py +0 -0
  348. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_servicedesk_source.py +0 -0
  349. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_slack_source.py +0 -0
  350. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_snowflake_source.py +0 -0
  351. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_source_dependency_groups.py +0 -0
  352. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_source_strategy_conformance.py +0 -0
  353. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_sqlite_source.py +0 -0
  354. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_tableau_source.py +0 -0
  355. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_tabular_automatic_sampling.py +0 -0
  356. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_tabular_lineage.py +0 -0
  357. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_tabular_utils.py +0 -0
  358. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_torch_runtime.py +0 -0
  359. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_urn.py +0 -0
  360. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_uv_sync.py +0 -0
  361. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_wordpress_source.py +0 -0
  362. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_youtube_source.py +0 -0
  363. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/test_youtube_source_integration.py +0 -0
  364. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/utils/test_archive_extraction.py +0 -0
  365. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/utils/test_content_extraction.py +0 -0
  366. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/utils/test_embedded_files.py +0 -0
  367. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/utils/test_file_metadata.py +0 -0
  368. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/utils/test_file_parser.py +0 -0
  369. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/utils/test_file_to_images.py +0 -0
  370. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/utils/test_legacy_office.py +0 -0
  371. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/utils/test_payload.py +0 -0
  372. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/utils/test_transcription.py +0 -0
  373. {classifyre_cli-0.6.3 → classifyre_cli-0.6.4}/tests/utils/test_video_processing.py +0 -0
@@ -0,0 +1,3 @@
1
+ $ uv sync
2
+ Resolved 325 packages in 269ms
3
+ Checked 51 packages in 3ms
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: classifyre-cli
3
- Version: 0.6.3
3
+ Version: 0.6.4
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.6.3",
3
+ "version": "0.6.4",
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.6.3"
3
+ version = "0.6.4"
4
4
  description = "Classifyre CLI — scan and classify unstructured data sources"
5
5
  readme = "README.md"
6
6
  # Upper bound is load-bearing: onnxruntime publishes no cp315 wheels (notably
@@ -15,6 +15,7 @@ _PIPELINE_TYPE_DEFAULTS: dict[str, str] = {
15
15
  "RegexPipelineSchema": "REGEX",
16
16
  "LLMPipelineSchema": "LLM",
17
17
  "TagPipelineSchema": "TAG",
18
+ "CodeDetectorPipelineSchema": "CODE_DETECTOR",
18
19
  }
19
20
 
20
21
  # Pipeline schema classes whose `severity` field has a string default from JSON schema
@@ -27,6 +28,7 @@ _SEVERITY_ENUM_DEFAULT_CLASSES = {
27
28
  "LLMPipelineSchema": "info",
28
29
  "TextClassificationPipelineSchema": "info",
29
30
  "TagPipelineSchema": "medium",
31
+ "CodeDetectorPipelineSchema": "medium",
30
32
  }
31
33
 
32
34
 
@@ -60,6 +62,15 @@ def _patch_severity_enum_defaults(source: str) -> str:
60
62
  return source
61
63
 
62
64
 
65
+ def _patch_category_enum_defaults(source: str) -> str:
66
+ """Same fix as the severity one, for the code detector's ``category`` default."""
67
+ pattern = (
68
+ r"(class CodeDetectorPipelineSchema\(.*?category: DetectorCategory \| None = Field\(\n\s+)"
69
+ r"'QUALITY'(\s*,)"
70
+ )
71
+ return re.sub(pattern, r"\1DetectorCategory.QUALITY\2", source, flags=re.DOTALL)
72
+
73
+
63
74
  def run_codegen(input_file):
64
75
  """Generate Pydantic models from a single JSON schema file."""
65
76
  cmd = [
@@ -107,6 +118,7 @@ def main():
107
118
  content = run_codegen(detector_schema)
108
119
  content = _patch_pipeline_type_defaults(content)
109
120
  content = _patch_severity_enum_defaults(content)
121
+ content = _patch_category_enum_defaults(content)
110
122
  (MODEL_DIR / "generated_detectors.py").write_text(content)
111
123
  print("Wrote src/models/generated_detectors.py")
112
124
 
@@ -18,7 +18,6 @@ silently-degraded run visible.
18
18
  from __future__ import annotations
19
19
 
20
20
  import asyncio
21
- import base64
22
21
  import json
23
22
  import logging
24
23
  from collections.abc import Mapping
@@ -29,6 +28,7 @@ from ..graph.edges import edge_from_payload
29
28
  from ..notebook.child import ChildProcessError, NotebookChildProcess
30
29
  from ..notebook.groups import warm_declared_groups
31
30
  from ..notebook.packages import install as install_packages
31
+ from ..pipeline.asset_payload_server import AssetPayloadServer
32
32
  from ..utils.hashing import unhash_id
33
33
  from ..utils.urn import normalize_urn_or_none
34
34
  from .contract import validate_augmentation_notebook
@@ -143,10 +143,6 @@ class AugmentationSession:
143
143
  self._warnings: list[str] = []
144
144
  self._warning_set: set[str] = set()
145
145
  self.stats = AugmentationStats()
146
- # Run-scoped memo of fetched payloads, keyed by fetch candidate id.
147
- # Bounded: one in-flight asset's worth per entry, each capped at
148
- # max_payload_bytes, dropped by evict_asset_cache().
149
- self._memo: dict[str, dict[str, Any]] = {}
150
146
 
151
147
  # -- construction ----------------------------------------------------------
152
148
 
@@ -295,7 +291,6 @@ class AugmentationSession:
295
291
  slot.child.terminate()
296
292
  except Exception:
297
293
  pass
298
- self._memo.clear()
299
294
 
300
295
  # -- per asset -----------------------------------------------------------------
301
296
 
@@ -314,163 +309,34 @@ class AugmentationSession:
314
309
  "mime_type": None,
315
310
  }
316
311
 
317
- def _candidates(self, asset: Any) -> list[str]:
318
- seen: list[str] = []
319
- for candidate in (getattr(asset, "external_url", ""), getattr(asset, "hash", "")):
320
- value = str(candidate or "").strip()
321
- if value and value not in seen:
322
- seen.append(value)
323
- return seen
312
+ @property
313
+ def _payloads(self) -> AssetPayloadServer:
314
+ """The source's shared payload server.
315
+
316
+ Shared with the code detectors that run on the same asset later in the
317
+ scan, so a payload augmentation fetched is not fetched again (G1 R20).
318
+ """
319
+ return AssetPayloadServer.shared(
320
+ self._source, max_payload_bytes=self._limits["max_payload_bytes"]
321
+ )
324
322
 
325
323
  async def _serve_need(self, asset: Any, memo_key: str, frame: dict[str, Any]) -> Any:
326
- op = str(frame.get("op") or "")
327
- if op == "payload":
328
- fetched = await self._fetch_payload(asset)
329
- if fetched is None:
330
- return {"bytes_b64": None, "mime": None}
331
- raw, mime = fetched
332
- return {"bytes_b64": base64.b64encode(raw).decode("ascii"), "mime": mime}
333
- if op == "raw_pages":
334
- pages = await self._fetch_raw_pages(asset)
335
- return {"pages": pages}
336
- if op == "text":
337
- pages = await self._fetch_text_pages(asset)
338
- whole = "".join(pages)
339
- return {"text": whole, "pages": pages}
340
- raise ChildProcessError(f"Unknown payload op {op!r}")
324
+ try:
325
+ return await self._payloads.serve(asset, frame)
326
+ except ValueError as exc:
327
+ raise ChildProcessError(str(exc)) from exc
341
328
 
342
329
  async def _fetch_payload(self, asset: Any) -> tuple[bytes, str] | None:
343
330
  """Raw bytes for file-shaped sources; None for row-shaped ones."""
344
- memo = self._memo_for(asset)
345
- if "payload" in memo:
346
- cached = memo["payload"]
347
- return cached if cached is None else (bytes(cached[0]), str(cached[1]))
348
- fetch = getattr(self._source, "fetch_content_bytes", None)
349
- result: tuple[bytes, str] | None = None
350
- if callable(fetch):
351
- for candidate in self._candidates(asset):
352
- try:
353
- result = await fetch(candidate)
354
- except Exception as exc:
355
- logger.debug("Augmentation payload fetch failed for %s: %s", candidate, exc)
356
- continue
357
- if result is not None:
358
- break
359
- if result is not None:
360
- raw, mime = result
361
- cap = self._limits["max_payload_bytes"]
362
- if len(raw) > cap:
363
- logger.debug("Augmentation payload for %s truncated to %d bytes", candidate, cap)
364
- raw = raw[:cap]
365
- result = (raw, mime)
366
- self._remember_bytes(asset, raw, mime)
367
- memo["payload"] = result
368
- return result
331
+ return await self._payloads.payload(asset)
369
332
 
370
333
  async def _fetch_raw_pages(self, asset: Any) -> list[str]:
371
334
  """The connector's own raw representation, page by page (capped)."""
372
- memo = self._memo_for(asset)
373
- if "raw_pages" in memo:
374
- return [str(page) for page in memo["raw_pages"]]
375
- pages: list[str] = []
376
- fetch = getattr(self._source, "fetch_content_pages", None)
377
- cap = self._limits["max_payload_bytes"]
378
- used = 0
379
- if callable(fetch):
380
- for candidate in self._candidates(asset):
381
- try:
382
- async for raw, _text in fetch(candidate):
383
- if not raw:
384
- continue
385
- chunk = str(raw)
386
- if used + len(chunk) > cap:
387
- chunk = chunk[: max(0, cap - used)]
388
- if chunk:
389
- pages.append(chunk)
390
- used += len(chunk)
391
- if used >= cap or len(pages) >= 50:
392
- break
393
- if pages:
394
- break
395
- except Exception as exc:
396
- logger.debug("Augmentation raw fetch failed for %s: %s", candidate, exc)
397
- continue
398
- memo["raw_pages"] = pages
399
- return pages
335
+ return await self._payloads.raw_pages(asset)
400
336
 
401
337
  async def _fetch_text_pages(self, asset: Any) -> list[str]:
402
338
  """Extracted text, page by page (capped)."""
403
- memo = self._memo_for(asset)
404
- if "text_pages" in memo:
405
- return [str(page) for page in memo["text_pages"]]
406
- pages: list[str] = []
407
- fetch = getattr(self._source, "fetch_content_pages", None)
408
- cap = self._limits["max_payload_bytes"]
409
- used = 0
410
- if callable(fetch):
411
- for candidate in self._candidates(asset):
412
- try:
413
- async for _raw, text in fetch(candidate):
414
- if not text:
415
- continue
416
- chunk = str(text)
417
- if used + len(chunk) > cap:
418
- chunk = chunk[: max(0, cap - used)]
419
- if chunk:
420
- pages.append(chunk)
421
- used += len(chunk)
422
- if used >= cap or len(pages) >= 50:
423
- break
424
- if pages:
425
- break
426
- except Exception as exc:
427
- logger.debug("Augmentation text fetch failed for %s: %s", candidate, exc)
428
- continue
429
- if not pages:
430
- provider = getattr(self._source, "fetch_content_bytes", None)
431
- if callable(provider):
432
- fetched = await self._fetch_payload(asset)
433
- if fetched is not None:
434
- raw, mime = fetched
435
- try:
436
- text_pages = await asyncio.to_thread(
437
- list, self._source.iter_asset_pages(raw, mime)
438
- )
439
- for page in text_pages[:50]:
440
- chunk = str(page)
441
- if used + len(chunk) > cap:
442
- chunk = chunk[: max(0, cap - used)]
443
- if chunk:
444
- pages.append(chunk)
445
- used += len(chunk)
446
- if used >= cap:
447
- break
448
- except Exception as exc:
449
- logger.debug("Augmentation text fallback failed: %s", exc)
450
- memo["text_pages"] = pages
451
- return pages
452
-
453
- def _memo_for(self, asset: Any) -> dict[str, Any]:
454
- key = str(getattr(asset, "hash", "") or "")
455
- return self._memo.setdefault(key, {})
456
-
457
- def _remember_bytes(self, asset: Any, raw: bytes, mime: str) -> None:
458
- """Mirror fetched bytes onto the source so the pipeline reuses them.
459
-
460
- ``ParsedContentProvider.fetch_bytes`` consults this memo first instead
461
- of re-downloading — the fetch augmentation already paid for is not
462
- paid twice. Registered under every candidate id the pipeline tries
463
- (external URL, then hash), and dropped by ``evict`` at the end of the
464
- asset.
465
- """
466
- remember = getattr(self._source, "remember_augmentation_bytes", None)
467
- if not callable(remember):
468
- return
469
- for candidate in self._candidates(asset):
470
- try:
471
- remember(candidate, raw, mime)
472
- except Exception:
473
- pass
339
+ return await self._payloads.text_pages(asset)
474
340
 
475
341
  async def augment(self, asset: Any) -> Any:
476
342
  """Enrich one asset. Never raises: failure degrades to a warning."""
@@ -718,13 +584,4 @@ class AugmentationSession:
718
584
 
719
585
  def evict(self, asset: Any) -> None:
720
586
  """Drop one asset's payload memo. Called alongside evict_asset_cache."""
721
- self._memo.pop(str(getattr(asset, "hash", "") or ""), None)
722
- forget = getattr(self._source, "forget_augmentation_bytes", None)
723
- if not callable(forget):
724
- return
725
- # The bytes were mirrored under every candidate id; drop them all.
726
- for candidate in self._candidates(asset):
727
- try:
728
- forget(candidate)
729
- except Exception:
730
- pass
587
+ self._payloads.evict(asset)
@@ -13,6 +13,7 @@ from ._base import (
13
13
  BaseRunner,
14
14
  _resolve_pipeline_severity,
15
15
  )
16
+ from ._custom_detector import CustomDetectorRunner
16
17
  from ._factory import create_runner
17
18
  from ._gliner2 import (
18
19
  GLiNER2Runner,
@@ -35,6 +36,7 @@ __all__ = [
35
36
  "_IMAGE_CONTENT_TYPES",
36
37
  "_TEXT_CONTENT_TYPES",
37
38
  "BaseRunner",
39
+ "CustomDetectorRunner",
38
40
  "GLiNER2Runner",
39
41
  "ImageClassificationRunner",
40
42
  "LLMRunner",
@@ -0,0 +1,72 @@
1
+ """Runner for the ``CODE_DETECTOR`` pipeline type: a code detector.
2
+
3
+ Like ``TAG`` it never runs over a page of text: ``detect()`` returns nothing and
4
+ no content type is advertised, so the text, binary and link passes never
5
+ schedule it and the worker pool never receives it. The detector pipeline
6
+ recognises it by this class and calls it once per asset, after every other
7
+ detector, through the :class:`CustomDetectorSession` it owns.
8
+
9
+ The session is created lazily on first use and bound to the scan's source,
10
+ because it needs the source to serve payload and holds child processes that
11
+ must not exist in the worker pool's processes.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from typing import Any
17
+
18
+ from ....models.generated_detectors import CodeDetectorPipelineSchema, PipelineResult
19
+ from ....models.generated_single_asset_scan_results import DetectionResult
20
+ from ._base import BaseRunner
21
+
22
+
23
+ class CustomDetectorRunner(BaseRunner):
24
+ """Asset-scoped runner: judged per asset by a notebook, never per page."""
25
+
26
+ asset_scoped = True
27
+
28
+ def __init__(
29
+ self,
30
+ schema: CodeDetectorPipelineSchema,
31
+ detector_key: str = "",
32
+ detector_name: str = "",
33
+ ) -> None:
34
+ self.schema = schema
35
+ self._detector_key = detector_key
36
+ self._detector_name = detector_name
37
+ self._session: Any = None
38
+
39
+ def run(self, text: str) -> PipelineResult:
40
+ return PipelineResult(metadata={"runner": "CODE_DETECTOR"})
41
+
42
+ def detect(self, content: str | bytes, content_type: str) -> list[DetectionResult]:
43
+ """Never called per page. The pipeline calls ``session().detect(asset)``."""
44
+ return []
45
+
46
+ def get_supported_content_types(self) -> list[str]:
47
+ return []
48
+
49
+ @property
50
+ def deterministic(self) -> bool:
51
+ return self.schema.deterministic is not False
52
+
53
+ @property
54
+ def needs_findings(self) -> bool:
55
+ return bool(self.schema.needs_findings)
56
+
57
+ def session(self, source: Any) -> Any:
58
+ """The run's session, created on first use."""
59
+ if self._session is None:
60
+ from ...custom_detector.session import CustomDetectorSession
61
+
62
+ self._session = CustomDetectorSession(
63
+ key=self._detector_key,
64
+ name=self._detector_name,
65
+ schema=self.schema,
66
+ source=source,
67
+ )
68
+ return self._session
69
+
70
+ def close(self) -> None:
71
+ if self._session is not None:
72
+ self._session.close()
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from ....models.generated_detectors import (
6
+ CodeDetectorPipelineSchema,
6
7
  GLiNER2PipelineSchema,
7
8
  ImageClassificationPipelineSchema,
8
9
  LLMPipelineSchema,
@@ -12,6 +13,7 @@ from ....models.generated_detectors import (
12
13
  TextClassificationPipelineSchema,
13
14
  )
14
15
  from ._base import BaseRunner
16
+ from ._custom_detector import CustomDetectorRunner
15
17
  from ._gliner2 import GLiNER2Runner
16
18
  from ._image_classification import ImageClassificationRunner
17
19
  from ._llm import LLMRunner
@@ -30,6 +32,7 @@ def create_runner(
30
32
  | ImageClassificationPipelineSchema
31
33
  | ObjectDetectionPipelineSchema
32
34
  | TagPipelineSchema
35
+ | CodeDetectorPipelineSchema
33
36
  ),
34
37
  detector_key: str = "",
35
38
  detector_name: str = "",
@@ -50,5 +53,8 @@ def create_runner(
50
53
  # detector that is not supposed to run anything at all.
51
54
  if isinstance(schema, TagPipelineSchema):
52
55
  return TagRunner(schema, detector_key, detector_name)
56
+ # Same reason as TAG: a code detector must never fall through to GLiNER2.
57
+ if isinstance(schema, CodeDetectorPipelineSchema):
58
+ return CustomDetectorRunner(schema, detector_key, detector_name)
53
59
  # GLiNER2PipelineSchema is the default / backward-compat path
54
60
  return GLiNER2Runner(schema, detector_key, detector_name)
@@ -0,0 +1,9 @@
1
+ """Code detectors: the ``CODE_DETECTOR`` pipeline type.
2
+
3
+ A notebook that defines ``detect(asset, ctx)`` and yields ``Finding`` objects,
4
+ run in an isolated child process once per asset, after every other detector.
5
+ """
6
+
7
+ from .session import CustomDetectorSession, DetectOutcome
8
+
9
+ __all__ = ["CustomDetectorSession", "DetectOutcome"]
@@ -0,0 +1,45 @@
1
+ """The interface a code detector's notebook must cover.
2
+
3
+ A code detector answers one question -- "what is wrong with this asset?" -- so
4
+ ``detect(asset, ctx)`` is required and ``setup(ctx)`` optional. Checked at the
5
+ three moments the connector contract is: on save (the API's cheap check),
6
+ before a preview, and before every scan, because the recipe travels to a job
7
+ that has to defend itself.
8
+
9
+ Only the shape is checked, through the shared ``validate_module`` /
10
+ ``validate_notebook``. Isolation is the child process and its scrubbed
11
+ environment, not anything an AST can decide.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from collections.abc import Iterable, Mapping
17
+
18
+ from ...notebook.contract import ContractReport, validate_module, validate_notebook
19
+ from ...notebook.serialize import ModuleSource
20
+
21
+ REQUIRED_FUNCTIONS = ("detect",)
22
+
23
+ #: ``setup(ctx)`` runs once per worker before the first asset: load a list,
24
+ #: a model, a lookup table into ``ctx.state``.
25
+ OPTIONAL_FUNCTIONS = ("setup",)
26
+
27
+
28
+ def validate_detector_module(module: ModuleSource) -> ContractReport:
29
+ """Parse the assembled module and check ``detect()`` exists."""
30
+ return validate_module(module, required=REQUIRED_FUNCTIONS)
31
+
32
+
33
+ def validate_detector_notebook(
34
+ cells: Iterable[Mapping[str, object] | object],
35
+ ) -> ContractReport:
36
+ """Validate a code detector's cells."""
37
+ return validate_notebook(cells, required=REQUIRED_FUNCTIONS)
38
+
39
+
40
+ __all__ = [
41
+ "OPTIONAL_FUNCTIONS",
42
+ "REQUIRED_FUNCTIONS",
43
+ "validate_detector_module",
44
+ "validate_detector_notebook",
45
+ ]
@@ -0,0 +1,102 @@
1
+ """Materialize a code detector's uploaded files for ``ctx.file(name)``.
2
+
3
+ Downloaded by the parent, never by the child: the child is started without the
4
+ API URL or the callback key, and handing it either would undo the isolation it
5
+ exists for. The API injects ``files_runtime`` -- one entry per file, with a
6
+ content hash and a download URL -- into the pipeline schema at dispatch.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+ import os
13
+ import re
14
+ import shutil
15
+ import tempfile
16
+ from collections.abc import Iterable, Mapping
17
+ from pathlib import Path
18
+ from typing import Any
19
+ from urllib.parse import quote
20
+
21
+ import requests
22
+
23
+ from ...outputs.base import DEFAULT_REST_TIMEOUT_SEC
24
+ from ...outputs.rest import INTERNAL_KEY_HEADER
25
+ from ...utils.source_files import CHUNK_BYTES, api_base_url
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+ #: A directory prepared by the caller (the preview job's init container, a
30
+ #: test) wins over downloading.
31
+ FILES_DIR_ENV = "CLASSIFYRE_DETECTOR_FILES_DIR"
32
+
33
+
34
+ def _safe_name(name: str) -> str:
35
+ base = Path(name).name or "file"
36
+ return re.sub(r"[^A-Za-z0-9._ -]", "_", base)[:200] or "file"
37
+
38
+
39
+ def _entries(files_runtime: Iterable[Any] | None) -> list[dict[str, Any]]:
40
+ entries: list[dict[str, Any]] = []
41
+ for raw in files_runtime or []:
42
+ entry = raw.model_dump() if hasattr(raw, "model_dump") else raw
43
+ if isinstance(entry, Mapping) and entry.get("id") and entry.get("name"):
44
+ entries.append(dict(entry))
45
+ return entries
46
+
47
+
48
+ def materialize_detector_files(
49
+ detector_id: str | None,
50
+ files_runtime: Iterable[Any] | None,
51
+ ) -> Path | None:
52
+ """Download every file into a fresh temp dir; None when there are none.
53
+
54
+ A file that fails to download is logged and skipped: a rule that reads one
55
+ list out of three should still see the two that arrived, and a rule that
56
+ needs the missing one fails loudly on ``ctx.file(name)`` with its name.
57
+ """
58
+ prepared = os.environ.get(FILES_DIR_ENV)
59
+ if prepared and Path(prepared).is_dir():
60
+ return Path(prepared)
61
+ entries = _entries(files_runtime)
62
+ if not entries:
63
+ return None
64
+ destination = Path(tempfile.mkdtemp(prefix="classifyre-detector-files-"))
65
+ headers = {"Connection": "close"}
66
+ key = os.environ.get("CLASSIFYRE_INTERNAL_KEY", "").strip()
67
+ if key:
68
+ headers[INTERNAL_KEY_HEADER] = key
69
+ written = 0
70
+ with requests.Session() as session:
71
+ for entry in entries:
72
+ url = str(entry.get("url") or "").strip()
73
+ if not url:
74
+ if not detector_id:
75
+ continue
76
+ url = (
77
+ f"{api_base_url()}/custom-detectors/{quote(detector_id, safe='')}"
78
+ f"/files/{quote(str(entry['id']), safe='')}/content"
79
+ )
80
+ target = destination / _safe_name(str(entry["name"]))
81
+ try:
82
+ with session.get(
83
+ url, headers=headers, stream=True, timeout=(30, DEFAULT_REST_TIMEOUT_SEC)
84
+ ) as response:
85
+ response.raise_for_status()
86
+ with target.open("wb") as handle:
87
+ for chunk in response.iter_content(chunk_size=CHUNK_BYTES):
88
+ if chunk:
89
+ handle.write(chunk)
90
+ except (requests.RequestException, OSError) as exc:
91
+ logger.warning("Could not download detector file %s: %s", entry.get("name"), exc)
92
+ target.unlink(missing_ok=True)
93
+ continue
94
+ written += 1
95
+ if not written:
96
+ shutil.rmtree(destination, ignore_errors=True)
97
+ return None
98
+ logger.info("Made %d detector file(s) available to ctx.files", written)
99
+ return destination
100
+
101
+
102
+ __all__ = ["FILES_DIR_ENV", "materialize_detector_files"]