mint-sdk 1.2.2__tar.gz → 1.2.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 (425) hide show
  1. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/PKG-INFO +1 -1
  2. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/_version.py +2 -2
  3. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/bundled/frontend.json +11 -2
  4. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/filesystem.py +232 -38
  5. mint_sdk-1.2.4/tests/test_filesystem.py +915 -0
  6. mint_sdk-1.2.2/tests/test_filesystem.py +0 -485
  7. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/.gitignore +0 -0
  8. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/README.md +0 -0
  9. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/pyproject.toml +0 -0
  10. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/__init__.py +0 -0
  11. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/_discover.py +0 -0
  12. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/_envelope.py +0 -0
  13. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/_files.py +0 -0
  14. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/_optional.py +0 -0
  15. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/_prompt.py +0 -0
  16. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/_staging_base.py +0 -0
  17. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/_transport.py +0 -0
  18. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/actors.py +0 -0
  19. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_artifact.py +0 -0
  20. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_commands.py +0 -0
  21. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_endpoint.py +0 -0
  22. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_hook.py +0 -0
  23. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_migration.py +0 -0
  24. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_router.py +0 -0
  25. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_schema.py +0 -0
  26. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_service.py +0 -0
  27. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_setting.py +0 -0
  28. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_wiring.py +0 -0
  29. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_command.py +0 -0
  30. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_backend_templates.py +0 -0
  31. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_commands.py +0 -0
  32. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_frontend_assay_templates.py +0 -0
  33. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_frontend_core_templates.py +0 -0
  34. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_frontend_helpers.py +0 -0
  35. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_frontend_lab_templates.py +0 -0
  36. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_frontend_pack_templates.py +0 -0
  37. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_frontend_panel_templates.py +0 -0
  38. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_frontend_templates.py +0 -0
  39. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_frontend_workflow_templates.py +0 -0
  40. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_router_templates.py +0 -0
  41. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_field_specs.py +0 -0
  42. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_frontend_commands.py +0 -0
  43. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_frontend_routes.py +0 -0
  44. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_naming.py +0 -0
  45. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_project.py +0 -0
  46. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_r_analysis_command.py +0 -0
  47. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_r_templates.py +0 -0
  48. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_source_edits.py +0 -0
  49. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_template_catalog.py +0 -0
  50. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/ai_generated_instructions.md.template +0 -0
  51. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/ai_instructions.md.template +0 -0
  52. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/analysis.py +0 -0
  53. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/api_errors.py +0 -0
  54. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/app.py +0 -0
  55. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli.py +0 -0
  56. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_build.py +0 -0
  57. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_build_cmd.py +0 -0
  58. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/__init__.py +0 -0
  59. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/_utils.py +0 -0
  60. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/add_scaffold_cmd.py +0 -0
  61. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/add_templates_cmd.py +0 -0
  62. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/admin_cmd.py +0 -0
  63. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/auth_cmd.py +0 -0
  64. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/debug_cmd.py +0 -0
  65. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/experiment_cmd.py +0 -0
  66. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/init_cmd.py +0 -0
  67. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/plugin_cmd.py +0 -0
  68. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/project_cmd.py +0 -0
  69. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/status_cmd.py +0 -0
  70. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/status_formatting.py +0 -0
  71. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/update_cmd.py +0 -0
  72. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_data_templates.py +0 -0
  73. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_entry.py +0 -0
  74. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/__init__.py +0 -0
  75. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/_config.py +0 -0
  76. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/_exceptions.py +0 -0
  77. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/_http.py +0 -0
  78. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/_types.py +0 -0
  79. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/client.py +0 -0
  80. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/resources/__init__.py +0 -0
  81. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/resources/admin.py +0 -0
  82. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/resources/auth.py +0 -0
  83. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/resources/experiments.py +0 -0
  84. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/resources/objects.py +0 -0
  85. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/resources/plugins.py +0 -0
  86. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/resources/projects.py +0 -0
  87. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/resources/updates.py +0 -0
  88. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/compat.py +0 -0
  89. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/config.py +0 -0
  90. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/context.py +0 -0
  91. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/contract.py +0 -0
  92. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/contract_ts_types.py +0 -0
  93. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/contract_typescript.py +0 -0
  94. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/data_store.py +0 -0
  95. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/db_commands.py +0 -0
  96. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/dependency_locks.py +0 -0
  97. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/dependency_policy.py +0 -0
  98. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deploy_command.py +0 -0
  99. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deprecated_api_catalog.py +0 -0
  100. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deprecated_apis.py +0 -0
  101. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deprecated_frontend_destructure_patterns.py +0 -0
  102. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deprecated_frontend_doc_text_patterns.py +0 -0
  103. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deprecated_frontend_expression_patterns.py +0 -0
  104. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deprecated_frontend_import_patterns.py +0 -0
  105. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deprecated_frontend_patterns.py +0 -0
  106. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deprecated_frontend_tag_patterns.py +0 -0
  107. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deps_command.py +0 -0
  108. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/design_validation.py +0 -0
  109. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/dev_command.py +0 -0
  110. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/dev_proxy_config.py +0 -0
  111. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/__init__.py +0 -0
  112. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/cache.py +0 -0
  113. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/cli_docs.py +0 -0
  114. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/contract_docs.py +0 -0
  115. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/formatter.py +0 -0
  116. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/frontend_component_catalog.py +0 -0
  117. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/frontend_component_chooser.py +0 -0
  118. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/frontend_css_formatters.py +0 -0
  119. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/frontend_detail_formatters.py +0 -0
  120. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/frontend_extractor.py +0 -0
  121. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/manifest_lookup.py +0 -0
  122. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/python_detail_formatters.py +0 -0
  123. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/python_extractor.py +0 -0
  124. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/python_introspection.py +0 -0
  125. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/reference_docs.py +0 -0
  126. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/reference_guides.py +0 -0
  127. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/reference_search.py +0 -0
  128. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/search.py +0 -0
  129. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/suggestions.py +0 -0
  130. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/template_data.py +0 -0
  131. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/template_docs.py +0 -0
  132. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/template_search.py +0 -0
  133. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/template_usage.py +0 -0
  134. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/terminal_style.py +0 -0
  135. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/topic_routes.py +0 -0
  136. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs_command.py +0 -0
  137. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_boot.py +0 -0
  138. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_command.py +0 -0
  139. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_database_checks.py +0 -0
  140. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_decorator_checks.py +0 -0
  141. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_deprecated_api_usage.py +0 -0
  142. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_deprecated_frontend.py +0 -0
  143. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_deprecated_python.py +0 -0
  144. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_fixes.py +0 -0
  145. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_checks.py +0 -0
  146. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_component_guidance.py +0 -0
  147. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_component_patterns.py +0 -0
  148. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_guidance.py +0 -0
  149. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_import_guidance.py +0 -0
  150. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_sdk_catalog.py +0 -0
  151. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_sdk_patterns.py +0 -0
  152. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_sidebar_guidance.py +0 -0
  153. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_source_utils.py +0 -0
  154. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_guidance.py +0 -0
  155. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_import_rules.py +0 -0
  156. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_models.py +0 -0
  157. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_output.py +0 -0
  158. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_platform_constraints.py +0 -0
  159. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_plugin_metadata.py +0 -0
  160. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_r_command.py +0 -0
  161. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_scan_config.py +0 -0
  162. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_sdk_checks.py +0 -0
  163. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_source_files.py +0 -0
  164. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_workspace.py +0 -0
  165. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/endpoint.py +0 -0
  166. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/ephemeral_results.py +0 -0
  167. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/events_router.py +0 -0
  168. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/exceptions.py +0 -0
  169. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/export.py +0 -0
  170. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/export_common.py +0 -0
  171. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/export_tree.py +0 -0
  172. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/frontend_assets.py +0 -0
  173. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/generated_router.py +0 -0
  174. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/info_command.py +0 -0
  175. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_ai.py +0 -0
  176. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_backend_templates.py +0 -0
  177. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_command.py +0 -0
  178. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_files.py +0 -0
  179. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_frontend_templates.py +0 -0
  180. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_mode_templates.py +0 -0
  181. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_naming.py +0 -0
  182. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_post_scaffold.py +0 -0
  183. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_script_templates.py +0 -0
  184. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_templates.py +0 -0
  185. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_versions.py +0 -0
  186. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_workflow_templates.py +0 -0
  187. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/instrument.py +0 -0
  188. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/integrations.py +0 -0
  189. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/job_manager.py +0 -0
  190. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/job_notifications.py +0 -0
  191. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/job_profiles.py +0 -0
  192. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/job_sessions.py +0 -0
  193. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/job_staging.py +0 -0
  194. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/job_stream.py +0 -0
  195. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/jobs.py +0 -0
  196. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/lcms.py +0 -0
  197. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/link_command.py +0 -0
  198. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/local_database.py +0 -0
  199. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/logging.py +0 -0
  200. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/logs_command.py +0 -0
  201. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/__init__.py +0 -0
  202. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/_alembic/env.py +0 -0
  203. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/_alembic/script.py.mako +0 -0
  204. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/_alembic_runtime.py +0 -0
  205. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/base.py +0 -0
  206. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/conformance.py +0 -0
  207. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/errors.py +0 -0
  208. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/locking.py +0 -0
  209. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/ops.py +0 -0
  210. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/runner.py +0 -0
  211. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/runtime.py +0 -0
  212. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/models.py +0 -0
  213. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/package_metadata.py +0 -0
  214. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/path_staging.py +0 -0
  215. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/payloads.py +0 -0
  216. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/permissions.py +0 -0
  217. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/platform_daemon.py +0 -0
  218. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/platform_daemon_supervisor.py +0 -0
  219. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/plugin.py +0 -0
  220. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/plugin_database.py +0 -0
  221. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/plugin_decorators.py +0 -0
  222. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/plugin_exports.py +0 -0
  223. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/plugin_frontend.py +0 -0
  224. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/plugin_lifecycle.py +0 -0
  225. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/plugin_persistence.py +0 -0
  226. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/plugin_settings.py +0 -0
  227. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/project_layout.py +0 -0
  228. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/project_python.py +0 -0
  229. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/py.typed +0 -0
  230. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/pyproject_edit.py +0 -0
  231. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/r.py +0 -0
  232. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/remote_context.py +0 -0
  233. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/repositories.py +0 -0
  234. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/result_schema.py +0 -0
  235. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/runtime.py +0 -0
  236. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/runtime_dependencies.py +0 -0
  237. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/s3.py +0 -0
  238. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/schema.py +0 -0
  239. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/sdk_generate_command.py +0 -0
  240. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/settings_router.py +0 -0
  241. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/assets/BaseModal-CJrCXCfY.js +0 -0
  242. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/assets/ExperimentPopover-IJzKM7P3.js +0 -0
  243. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/assets/ExperimentSelectorModal-N0ANhxH5.js +0 -0
  244. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/assets/SettingsModal-Bg6ixjmN.js +0 -0
  245. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/assets/Skeleton-BdmBYpaw.js +0 -0
  246. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/assets/chunk-5LNFb4hk.js +0 -0
  247. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/assets/plotly.min-DHWXmmi3.js +0 -0
  248. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/generated-ui.css +0 -0
  249. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/generated-ui.js +0 -0
  250. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/index.html +0 -0
  251. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/temp_byte_quota.py +0 -0
  252. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/__init__.py +0 -0
  253. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/assay_matrix.py +0 -0
  254. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/base.py +0 -0
  255. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/calibration_curve.py +0 -0
  256. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/catalog.py +0 -0
  257. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/dose_response.py +0 -0
  258. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/flow_cytometry_panel.py +0 -0
  259. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/instrument_run.py +0 -0
  260. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/packs.py +0 -0
  261. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/plate_map.py +0 -0
  262. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/preset_catalog.py +0 -0
  263. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/preset_helpers.py +0 -0
  264. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/presets.py +0 -0
  265. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/protocol_steps.py +0 -0
  266. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/qpcr_plate.py +0 -0
  267. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/reagent_list.py +0 -0
  268. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/sample_prep.py +0 -0
  269. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/sample_sheet.py +0 -0
  270. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/time_course.py +0 -0
  271. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/testing/__init__.py +0 -0
  272. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/testing/harness.py +0 -0
  273. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/testing/plugins.py +0 -0
  274. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/testing/recording_context.py +0 -0
  275. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/testing/subprocess.py +0 -0
  276. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/token_auth.py +0 -0
  277. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/update_command.py +0 -0
  278. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/verify_command.py +0 -0
  279. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/wire.py +0 -0
  280. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/__init__.py +0 -0
  281. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/conftest.py +0 -0
  282. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/scaffold_helpers.py +0 -0
  283. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_backend_artifact.py +0 -0
  284. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_backend_endpoint.py +0 -0
  285. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_backend_hook.py +0 -0
  286. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_backend_migration.py +0 -0
  287. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_backend_router.py +0 -0
  288. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_backend_schema.py +0 -0
  289. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_backend_service.py +0 -0
  290. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_backend_setting.py +0 -0
  291. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_command.py +0 -0
  292. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_r_templates.py +0 -0
  293. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_alembic_metadata.py +0 -0
  294. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_alembic_runtime.py +0 -0
  295. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_alembic_runtime_concurrency.py +0 -0
  296. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_alembic_runtime_safety.py +0 -0
  297. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_api_errors.py +0 -0
  298. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_app.py +0 -0
  299. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_calendar_contract.py +0 -0
  300. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_cli.py +0 -0
  301. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_cli_command_utils.py +0 -0
  302. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_cli_extras.py +0 -0
  303. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_cli_status_formatting.py +0 -0
  304. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_cli_typer.py +0 -0
  305. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_cli_utils.py +0 -0
  306. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/__init__.py +0 -0
  307. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_admin_plugin_update_api.py +0 -0
  308. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_analysis_artifacts_api.py +0 -0
  309. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_auth_api.py +0 -0
  310. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_client.py +0 -0
  311. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_config.py +0 -0
  312. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_exceptions.py +0 -0
  313. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_experiments_api.py +0 -0
  314. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_http.py +0 -0
  315. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_objects_api.py +0 -0
  316. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_compat.py +0 -0
  317. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_config_runtime.py +0 -0
  318. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_contract.py +0 -0
  319. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_data_store.py +0 -0
  320. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_db_commands.py +0 -0
  321. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_dependency_locks.py +0 -0
  322. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_dependency_policy.py +0 -0
  323. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_deploy_command.py +0 -0
  324. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_deprecated_frontend_patterns.py +0 -0
  325. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_design_validation.py +0 -0
  326. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_dev_command.py +0 -0
  327. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_discover.py +0 -0
  328. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_docs_command.py +0 -0
  329. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_docs_manifest_lookup.py +0 -0
  330. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_docs_python_detail_formatters.py +0 -0
  331. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_docs_python_extractor.py +0 -0
  332. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_docs_python_introspection.py +0 -0
  333. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_docs_template_search.py +0 -0
  334. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_command.py +0 -0
  335. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_decorator_checks.py +0 -0
  336. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_fixes_versions.py +0 -0
  337. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_import_rules.py +0 -0
  338. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_output.py +0 -0
  339. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_platform_constraints.py +0 -0
  340. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_r_command.py +0 -0
  341. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_sdk_checks.py +0 -0
  342. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_workspace.py +0 -0
  343. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_endpoint_host_integration.py +0 -0
  344. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_endpoint_routing.py +0 -0
  345. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_ephemeral_results.py +0 -0
  346. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_error_envelope.py +0 -0
  347. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_events_router.py +0 -0
  348. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_exceptions.py +0 -0
  349. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_experiment_endpoint_scope.py +0 -0
  350. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_export.py +0 -0
  351. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_foreground_daemon.py +0 -0
  352. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_frontend_assets.py +0 -0
  353. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_generated_ui_example.py +0 -0
  354. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_generated_ui_router.py +0 -0
  355. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_info_command.py +0 -0
  356. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_init_command.py +0 -0
  357. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_init_modes.py +0 -0
  358. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_init_versions.py +0 -0
  359. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_instrument.py +0 -0
  360. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_job_definition.py +0 -0
  361. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_job_manager.py +0 -0
  362. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_job_manager_activity.py +0 -0
  363. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_job_notification_bridge.py +0 -0
  364. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_job_results.py +0 -0
  365. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_job_router.py +0 -0
  366. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_job_staging.py +0 -0
  367. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_job_stream.py +0 -0
  368. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_jobs.py +0 -0
  369. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_lazy_exports.py +0 -0
  370. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_lcms.py +0 -0
  371. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_link_command.py +0 -0
  372. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_local_database.py +0 -0
  373. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_logs_command.py +0 -0
  374. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_migration_base.py +0 -0
  375. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_migration_conformance.py +0 -0
  376. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_migration_errors.py +0 -0
  377. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_migration_locking.py +0 -0
  378. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_migration_ops.py +0 -0
  379. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_migration_runner.py +0 -0
  380. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_migrations_lazy_import.py +0 -0
  381. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_models.py +0 -0
  382. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_models_color.py +0 -0
  383. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_notification_contract.py +0 -0
  384. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_path_staging.py +0 -0
  385. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_permission_decorators.py +0 -0
  386. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_permissions.py +0 -0
  387. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_platform_cli_commands.py +0 -0
  388. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_platform_daemon.py +0 -0
  389. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_platform_daemon_supervisor.py +0 -0
  390. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_plugin.py +0 -0
  391. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_plugin_actor.py +0 -0
  392. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_plugin_database.py +0 -0
  393. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_plugin_decorators.py +0 -0
  394. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_plugin_save_load.py +0 -0
  395. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_plugin_settings_model.py +0 -0
  396. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_plugin_test_harness.py +0 -0
  397. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_project_layout.py +0 -0
  398. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_project_layout_integration.py +0 -0
  399. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_prompt.py +0 -0
  400. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_r_bridge.py +0 -0
  401. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_recording_context.py +0 -0
  402. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_remote_calendar_dispatch.py +0 -0
  403. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_remote_context.py +0 -0
  404. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_remote_notification_dispatch.py +0 -0
  405. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_remote_transport.py +0 -0
  406. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_repository_contract.py +0 -0
  407. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_runtime.py +0 -0
  408. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_runtime_dependencies.py +0 -0
  409. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_schema.py +0 -0
  410. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_sdk_dependency_ownership.py +0 -0
  411. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_sdk_documentation.py +0 -0
  412. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_sdk_generate_command.py +0 -0
  413. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_security_boundaries.py +0 -0
  414. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_settings_atomic_patch.py +0 -0
  415. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_settings_router.py +0 -0
  416. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_staged_job_runtime.py +0 -0
  417. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_temp_byte_quota.py +0 -0
  418. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_templates.py +0 -0
  419. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_testing.py +0 -0
  420. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_token_auth.py +0 -0
  421. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_typed_actor_contract.py +0 -0
  422. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_update_command.py +0 -0
  423. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_verify_command.py +0 -0
  424. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_wire.py +0 -0
  425. {mint_sdk-1.2.2 → mint_sdk-1.2.4}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: mint-sdk
3
- Version: 1.2.2
3
+ Version: 1.2.4
4
4
  Summary: MINT Plugin SDK - Build analysis plugins for the MINT (Mass-spec INtegrated Toolkit) platform
5
5
  Project-URL: Homepage, https://github.com/MorscherLab/MINT
6
6
  Project-URL: Documentation, https://github.com/MorscherLab/MINT/tree/main/packages/sdk-python#readme
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.2.2'
22
- __version_tuple__ = version_tuple = (1, 2, 2)
21
+ __version__ = version = '1.2.4'
22
+ __version_tuple__ = version_tuple = (1, 2, 4)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "sdk": "frontend",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "categories": {
5
5
  "components": [
6
6
  {
@@ -15274,7 +15274,7 @@
15274
15274
  "params": [
15275
15275
  {
15276
15276
  "name": "options",
15277
- "type": "{ apiBaseUrl?: string }"
15277
+ "type": "FilePickerAdapterOptions & { apiBaseUrl?: string }"
15278
15278
  }
15279
15279
  ],
15280
15280
  "returnType": "PickerAdapter",
@@ -18426,6 +18426,12 @@
18426
18426
  {
18427
18427
  "name": "FilePicker"
18428
18428
  },
18429
+ {
18430
+ "name": "FilePickerAdapterOptions"
18431
+ },
18432
+ {
18433
+ "name": "FilePickerTransport"
18434
+ },
18429
18435
  {
18430
18436
  "name": "FileSelection"
18431
18437
  },
@@ -20262,6 +20268,9 @@
20262
20268
  {
20263
20269
  "name": "createElisaAssayCollection"
20264
20270
  },
20271
+ {
20272
+ "name": "createFilePickerAdapter"
20273
+ },
20265
20274
  {
20266
20275
  "name": "createFlowCytometryAssayCollection"
20267
20276
  },
@@ -12,11 +12,17 @@ that do not match without the listing ever misreporting what is on disk.
12
12
  from __future__ import annotations
13
13
 
14
14
  import os
15
- from collections.abc import Iterable, Sequence
16
- from dataclasses import dataclass
15
+ import stat
16
+ from collections import OrderedDict
17
+ from collections.abc import Iterable, Mapping, Sequence
18
+ from concurrent.futures import Future
19
+ from dataclasses import dataclass, field
17
20
  from datetime import UTC, datetime
18
21
  from enum import StrEnum
22
+ from math import isfinite
19
23
  from pathlib import Path, PurePosixPath
24
+ from threading import Lock
25
+ from time import monotonic
20
26
 
21
27
  from pydantic import BaseModel, ConfigDict
22
28
 
@@ -130,6 +136,18 @@ class DirectoryListing(BaseModel):
130
136
  truncated: bool = False
131
137
 
132
138
 
139
+ @dataclass
140
+ class _DirectorySnapshot:
141
+ entries: list[DirectoryEntry]
142
+ signature: tuple[int, ...]
143
+ timestamp: float
144
+ child_counts: dict[str, int | None] = field(default_factory=dict)
145
+ count_lock: Lock = field(default_factory=Lock)
146
+
147
+
148
+ _CacheKey = tuple[str, Path, Path, PurePosixPath, bool]
149
+
150
+
133
151
  def normalize_type_rules(rules: Iterable[str] | None) -> frozenset[str]:
134
152
  """Normalize user-facing extensions to lowercase, dot-prefixed suffixes.
135
153
 
@@ -168,6 +186,8 @@ def _relative_parts(path: str) -> tuple[str, ...]:
168
186
  if not cleaned:
169
187
  return ()
170
188
  parts = tuple(part for part in cleaned.split("/") if part not in {"", "."})
189
+ if any(part[1:2] == ":" and part[0].lower() in "abcdefghijklmnopqrstuvwxyz" for part in parts):
190
+ raise MountPathError("A mount path must not contain a Windows drive")
171
191
  if any(part == ".." for part in parts):
172
192
  raise MountPathError("A mount path must not escape its mount root")
173
193
  return parts
@@ -177,7 +197,7 @@ def _resolved_root(mount: ServerMount) -> Path:
177
197
  """Resolve a mount root, treating an absent or unreadable one as offline."""
178
198
  try:
179
199
  root = mount.root.resolve(strict=True)
180
- except OSError as exc:
200
+ except (OSError, RuntimeError) as exc:
181
201
  raise MountPathNotFoundError(f"Mount {mount.id!r} is not available") from exc
182
202
  if not root.is_dir():
183
203
  raise MountPathNotFoundError(f"Mount {mount.id!r} is not available")
@@ -187,23 +207,10 @@ def _resolved_root(mount: ServerMount) -> Path:
187
207
  def _is_available(mount: ServerMount) -> bool:
188
208
  try:
189
209
  return mount.root.is_dir()
190
- except OSError:
210
+ except (OSError, RuntimeError):
191
211
  return False
192
212
 
193
213
 
194
- def _child_count(path: Path, *, include_hidden: bool) -> int | None:
195
- """Count a directory's entries, or None when it cannot be read.
196
-
197
- Hidden entries are counted only when the listing itself would show them, so
198
- the number beside a folder agrees with what opening it displays.
199
- """
200
- try:
201
- with os.scandir(path) as entries:
202
- return sum(1 for item in entries if include_hidden or not item.name.startswith("."))
203
- except OSError:
204
- return None
205
-
206
-
207
214
  def _matches_rules(name: str, extension: str, rules: frozenset[str]) -> bool:
208
215
  """Test a filename against the type rules.
209
216
 
@@ -225,6 +232,13 @@ class FileBrowser:
225
232
  The mount list is deployment configuration supplied by the host platform.
226
233
  Everything a caller passes afterwards — mount id, path, type rules — is
227
234
  treated as untrusted input.
235
+
236
+ Directory metadata is cached for ``cache_ttl`` seconds (zero disables
237
+ retention), bounded by ``cache_size`` directories. Every request resolves
238
+ its path and checks the directory signature before reusing metadata.
239
+ ``refresh=True`` or ``clear_cache`` also invalidates descendant listings.
240
+ Optional ``child_count_type_rules`` maps entry kinds to countable suffixes;
241
+ missing kinds or empty rules count nothing. ``None`` counts every visible entry.
228
242
  """
229
243
 
230
244
  def __init__(
@@ -232,14 +246,47 @@ class FileBrowser:
232
246
  mounts: Sequence[ServerMount],
233
247
  *,
234
248
  type_rules: Iterable[str] | None = None,
235
- max_entries: int = 2000,
249
+ max_entries: int | None = 2000,
236
250
  child_count_limit: int = 250,
251
+ child_count_type_rules: Mapping[EntryKind, Iterable[str]] | None = None,
252
+ cache_ttl: float = 300,
253
+ cache_size: int = 128,
254
+ restrict_symlinks_to_directory: bool = False,
237
255
  ) -> None:
256
+ if not isfinite(cache_ttl) or cache_ttl < 0:
257
+ raise ValueError("cache_ttl must be finite and non-negative")
258
+ if not isinstance(cache_size, int) or isinstance(cache_size, bool) or cache_size < 1:
259
+ raise ValueError("cache_size must be a positive integer")
260
+ if max_entries is not None and (not isinstance(max_entries, int) or max_entries < 1):
261
+ raise ValueError("max_entries must be a positive integer or None")
262
+ if child_count_limit < 0:
263
+ raise ValueError("child_count_limit must be non-negative")
238
264
  self._mounts = tuple(mounts)
239
265
  self._by_id = {mount.id: mount for mount in self._mounts}
240
266
  self._default_type_rules = normalize_type_rules(type_rules)
241
267
  self._max_entries = max_entries
242
268
  self._child_count_limit = child_count_limit
269
+ self._child_count_type_rules = (
270
+ {EntryKind(kind): normalize_type_rules(rules) for kind, rules in child_count_type_rules.items()}
271
+ if child_count_type_rules is not None else None
272
+ )
273
+ self._cache_ttl = cache_ttl
274
+ self._cache_size = cache_size
275
+ self._restrict_symlinks_to_directory = restrict_symlinks_to_directory
276
+ # ponytail: bounded by directory count; add an entry budget if very wide mounts exceed memory limits.
277
+ self._cache: OrderedDict[_CacheKey, _DirectorySnapshot] = OrderedDict()
278
+ self._pending: dict[_CacheKey, tuple[tuple[int, ...], Future[_DirectorySnapshot]]] = {}
279
+ self._cache_lock = Lock()
280
+
281
+ def clear_cache(self, mount_id: str | None = None) -> None:
282
+ """Invalidate listings and detach in-flight scans so they cannot refill the cache."""
283
+ if mount_id is not None:
284
+ self._mount(mount_id)
285
+ with self._cache_lock:
286
+ for mapping in (self._cache, self._pending):
287
+ for key in list(mapping):
288
+ if mount_id is None or key[0] == mount_id:
289
+ del mapping[key]
243
290
 
244
291
  def list_mounts(self) -> list[MountInfo]:
245
292
  """Describe every configured mount, flagging the ones that are offline."""
@@ -265,12 +312,13 @@ class FileBrowser:
265
312
  candidate = root.joinpath(*parts)
266
313
  try:
267
314
  resolved = candidate.resolve(strict=True)
268
- except OSError as exc:
315
+ except (OSError, RuntimeError) as exc:
269
316
  raise MountPathNotFoundError(f"{path!r} does not exist in mount {mount_id!r}") from exc
270
317
  # Resolve-then-compare: `parts` is already free of "..", so anything
271
318
  # landing outside the root got there through a symlink.
272
319
  if resolved != root and not resolved.is_relative_to(root):
273
320
  raise MountPathError(f"{path!r} would escape mount {mount_id!r}")
321
+ _relative_parts(resolved.relative_to(root).as_posix())
274
322
  return resolved
275
323
 
276
324
  def list_directory(
@@ -283,6 +331,8 @@ class FileBrowser:
283
331
  sort: SortKey = SortKey.NAME,
284
332
  descending: bool = False,
285
333
  include_hidden: bool = False,
334
+ refresh: bool = False,
335
+ count_children: bool = True,
286
336
  ) -> DirectoryListing:
287
337
  """List one directory inside a mount.
288
338
 
@@ -291,15 +341,39 @@ class FileBrowser:
291
341
  a caller can honestly report "N items, M shown".
292
342
  """
293
343
  mount = self._mount(mount_id)
344
+ parts = _relative_parts(path)
345
+ if not include_hidden and any(part.startswith(".") for part in parts):
346
+ raise MountPathError("Hidden directories are not included")
294
347
  directory = self.resolve(mount_id, path)
295
- if not directory.is_dir():
348
+ root = _resolved_root(mount)
349
+ if not directory.is_relative_to(root):
350
+ raise MountPathError(f"{path!r} would escape mount {mount_id!r}")
351
+ if not include_hidden and any(part.startswith(".") for part in directory.relative_to(root).parts):
352
+ raise MountPathError("Hidden directories are not included")
353
+ try:
354
+ directory_stat = directory.stat(follow_symlinks=False)
355
+ except (OSError, RuntimeError) as exc:
356
+ raise MountPathNotFoundError(f"{path!r} does not exist in mount {mount_id!r}") from exc
357
+ if not stat.S_ISDIR(directory_stat.st_mode):
296
358
  raise MountPathError(f"{path!r} is not a directory in mount {mount_id!r}")
297
359
 
298
360
  rules = self._default_type_rules if type_rules is None else normalize_type_rules(type_rules)
299
- base = PurePosixPath(*_relative_parts(path))
300
- root = _resolved_root(mount)
301
-
302
- entries = self._scan(directory, root, base, rules, include_hidden=include_hidden)
361
+ base = PurePosixPath(*parts)
362
+ if refresh:
363
+ self.clear_cache(mount_id)
364
+ key = (mount_id, root, directory, base, include_hidden)
365
+ signature = (
366
+ directory_stat.st_dev, directory_stat.st_ino,
367
+ directory_stat.st_mtime_ns, directory_stat.st_ctime_ns, directory_stat.st_size,
368
+ )
369
+ snapshot = self._directory_snapshot(key, signature)
370
+ entries = [
371
+ entry.model_copy(update={
372
+ "matches_type_rules": entry.kind is EntryKind.DIRECTORY
373
+ or _matches_rules(entry.name, entry.extension, rules),
374
+ })
375
+ for entry in snapshot.entries
376
+ ]
303
377
  total_count = len(entries)
304
378
  match_count = sum(1 for entry in entries if entry.matches_type_rules)
305
379
 
@@ -314,11 +388,12 @@ class FileBrowser:
314
388
 
315
389
  # Cap after sorting so the cut is deterministic, and say so rather than
316
390
  # silently returning a short listing.
317
- truncated = len(entries) > self._max_entries
391
+ truncated = self._max_entries is not None and len(entries) > self._max_entries
318
392
  if truncated:
319
393
  entries = entries[: self._max_entries]
320
394
 
321
- entries = self._count_children(directory, entries, include_hidden=include_hidden)
395
+ if count_children:
396
+ entries = self._count_children(directory, entries, snapshot, root=root, include_hidden=include_hidden)
322
397
 
323
398
  return DirectoryListing(
324
399
  mount_id=mount_id,
@@ -338,12 +413,53 @@ class FileBrowser:
338
413
  except KeyError as exc:
339
414
  raise MountNotFoundError(f"unknown mount {mount_id!r}") from exc
340
415
 
416
+ def _directory_snapshot(self, key: _CacheKey, signature: tuple[int, ...]) -> _DirectorySnapshot:
417
+ _, root, directory, base, include_hidden = key
418
+ with self._cache_lock:
419
+ cached = self._cache.get(key)
420
+ if (
421
+ cached is not None
422
+ and cached.signature == signature
423
+ and monotonic() - cached.timestamp < self._cache_ttl
424
+ ):
425
+ self._cache.move_to_end(key)
426
+ return cached
427
+ existing = self._pending.get(key)
428
+ owner = existing is None or existing[0] != signature
429
+ if owner:
430
+ future: Future[_DirectorySnapshot] = Future()
431
+ self._pending[key] = (signature, future)
432
+ else:
433
+ future = existing[1]
434
+ if not owner:
435
+ return future.result()
436
+ try:
437
+ snapshot = _DirectorySnapshot(
438
+ self._scan(directory, root, base, include_hidden=include_hidden),
439
+ signature, monotonic(),
440
+ )
441
+ with self._cache_lock:
442
+ if self._pending.get(key) == (signature, future):
443
+ if self._cache_ttl:
444
+ self._cache[key] = snapshot
445
+ self._cache.move_to_end(key)
446
+ while len(self._cache) > self._cache_size:
447
+ self._cache.popitem(last=False)
448
+ del self._pending[key]
449
+ future.set_result(snapshot)
450
+ return snapshot
451
+ except BaseException as exc:
452
+ with self._cache_lock:
453
+ if self._pending.get(key) == (signature, future):
454
+ del self._pending[key]
455
+ future.set_exception(exc)
456
+ raise
457
+
341
458
  def _scan(
342
459
  self,
343
460
  directory: Path,
344
461
  root: Path,
345
462
  base: PurePosixPath,
346
- rules: frozenset[str],
347
463
  *,
348
464
  include_hidden: bool,
349
465
  ) -> list[DirectoryEntry]:
@@ -353,7 +469,7 @@ class FileBrowser:
353
469
  for item in scan:
354
470
  if not include_hidden and item.name.startswith("."):
355
471
  continue
356
- entry = self._describe(item, root, base, rules)
472
+ entry = self._describe(item, root, base, directory=directory, include_hidden=include_hidden)
357
473
  if entry is not None:
358
474
  entries.append(entry)
359
475
  except OSError as exc:
@@ -369,7 +485,9 @@ class FileBrowser:
369
485
  self,
370
486
  directory: Path,
371
487
  entries: list[DirectoryEntry],
488
+ snapshot: _DirectorySnapshot,
372
489
  *,
490
+ root: Path,
373
491
  include_hidden: bool,
374
492
  ) -> list[DirectoryEntry]:
375
493
  """Fill in child counts, but only when the directory is small enough.
@@ -382,10 +500,23 @@ class FileBrowser:
382
500
  directories = [entry for entry in entries if entry.kind is EntryKind.DIRECTORY]
383
501
  if len(directories) > self._child_count_limit:
384
502
  return entries
385
- counted = {
386
- entry.path: _child_count(directory / entry.name, include_hidden=include_hidden)
387
- for entry in directories
388
- }
503
+ # Only this directory's count work is serialized; unrelated scans stay concurrent.
504
+ with snapshot.count_lock:
505
+ for entry in directories:
506
+ if entry.path in snapshot.child_counts:
507
+ continue
508
+ count = None
509
+ try:
510
+ target = (directory / entry.name).resolve(strict=True)
511
+ boundary = directory if self._restrict_symlinks_to_directory else root
512
+ if target.is_relative_to(boundary) and (
513
+ include_hidden or not any(part.startswith(".") for part in target.relative_to(root).parts)
514
+ ):
515
+ count = self._child_count(target, root=root, include_hidden=include_hidden)
516
+ except (OSError, RuntimeError):
517
+ pass
518
+ snapshot.child_counts[entry.path] = count
519
+ counted = dict(snapshot.child_counts)
389
520
  return [
390
521
  entry.model_copy(update={"child_count": counted[entry.path]})
391
522
  if entry.path in counted
@@ -393,26 +524,89 @@ class FileBrowser:
393
524
  for entry in entries
394
525
  ]
395
526
 
527
+ def _child_count(self, directory: Path, *, root: Path, include_hidden: bool) -> int | None:
528
+ """Count matching children without retaining another directory snapshot."""
529
+ rules_by_kind = self._child_count_type_rules
530
+ count = 0
531
+ try:
532
+ with os.scandir(directory) as items:
533
+ for item in items:
534
+ try:
535
+ if not include_hidden and item.name.startswith("."):
536
+ continue
537
+ if _relative_parts(item.name) != (item.name,):
538
+ continue
539
+ lowered = item.name.lower()
540
+ if rules_by_kind is not None and not any(
541
+ lowered.endswith(rule) for rules in rules_by_kind.values() for rule in rules
542
+ ):
543
+ continue
544
+ if item.is_symlink() or item.is_junction():
545
+ entry = self._describe(
546
+ item, root, PurePosixPath(), directory=directory, include_hidden=include_hidden,
547
+ )
548
+ if entry is None:
549
+ continue
550
+ kind = entry.kind
551
+ elif rules_by_kind is None:
552
+ count += 1
553
+ continue
554
+ elif item.is_dir(follow_symlinks=False):
555
+ kind = EntryKind.DIRECTORY
556
+ elif item.is_file(follow_symlinks=False):
557
+ kind = EntryKind.FILE
558
+ else:
559
+ continue
560
+ if rules_by_kind is None or any(
561
+ lowered.endswith(rule) for rule in rules_by_kind.get(kind, ())
562
+ ):
563
+ count += 1
564
+ except (OSError, RuntimeError, ValueError):
565
+ continue
566
+ return count
567
+ except OSError:
568
+ return None
569
+
396
570
  def _describe(
397
571
  self,
398
572
  item: os.DirEntry[str],
399
573
  root: Path,
400
574
  base: PurePosixPath,
401
- rules: frozenset[str],
575
+ *,
576
+ directory: Path,
577
+ include_hidden: bool,
402
578
  ) -> DirectoryEntry | None:
403
579
  """Build one entry, or None when it is unreadable or points outside."""
404
580
  try:
581
+ if _relative_parts(item.name) != (item.name,):
582
+ return None
405
583
  # A symlink out of the mount is not part of the mount. Drop it
406
584
  # rather than listing something the caller could never open.
407
- if item.is_symlink() and not Path(item.path).resolve().is_relative_to(root):
585
+ if item.is_symlink() or item.is_junction():
586
+ target = Path(item.path).resolve(strict=True)
587
+ boundary = directory if self._restrict_symlinks_to_directory else root
588
+ if not target.is_relative_to(boundary):
589
+ return None
590
+ target_parts = _relative_parts(target.relative_to(root).as_posix())
591
+ if not include_hidden and any(part.startswith(".") for part in target_parts):
592
+ return None
593
+ stat_result = target.stat(follow_symlinks=False)
594
+ else:
595
+ # Do not follow an entry that was replaced with a link after scandir.
596
+ stat_result = item.stat(follow_symlinks=False)
597
+ if hasattr(stat_result, "st_reparse_tag") and stat_result.st_reparse_tag in (
598
+ stat.IO_REPARSE_TAG_MOUNT_POINT, stat.IO_REPARSE_TAG_SYMLINK,
599
+ ):
600
+ # A resolved target or ordinary entry may have become a junction meanwhile.
601
+ return None
602
+ is_dir = stat.S_ISDIR(stat_result.st_mode)
603
+ if not is_dir and not stat.S_ISREG(stat_result.st_mode):
408
604
  return None
409
- is_dir = item.is_dir()
410
- stat_result = item.stat()
411
605
  # Inside the guard: an out-of-range mtime raises OSError too, and
412
606
  # one corrupt entry should be skipped like any other unreadable
413
607
  # one rather than failing the whole directory.
414
608
  modified_at = datetime.fromtimestamp(stat_result.st_mtime, tz=UTC)
415
- except (OSError, ValueError, OverflowError):
609
+ except (OSError, RuntimeError, ValueError, OverflowError):
416
610
  return None
417
611
 
418
612
  kind = EntryKind.DIRECTORY if is_dir else EntryKind.FILE
@@ -426,7 +620,7 @@ class FileBrowser:
426
620
  extension=extension,
427
621
  # A directory is always reachable: type rules describe what you can
428
622
  # pick, not where you can go.
429
- matches_type_rules=is_dir or _matches_rules(item.name, extension, rules),
623
+ matches_type_rules=True,
430
624
  child_count=None,
431
625
  )
432
626