sciveo 0.3.2__tar.gz → 0.3.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 (558) hide show
  1. {sciveo-0.3.2 → sciveo-0.3.4}/PKG-INFO +16 -3
  2. sciveo-0.3.2/sciveo.egg-info/PKG-INFO → sciveo-0.3.4/README.md +15 -27
  3. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/sniff/terminal.py +1 -1
  4. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/config.py +3 -0
  5. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/filesystem.py +64 -15
  6. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/server.py +86 -9
  7. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/defaults.py +2 -0
  8. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/version.py +1 -1
  9. sciveo-0.3.2/README.md → sciveo-0.3.4/sciveo.egg-info/PKG-INFO +40 -2
  10. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo.egg-info/SOURCES.txt +2 -0
  11. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_cli_defaults.py +18 -0
  12. sciveo-0.3.4/test/test_cli_regression.py +143 -0
  13. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_nvr.py +75 -0
  14. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_sniff.py +1 -1
  15. sciveo-0.3.4/test/test_storage_downloads.py +145 -0
  16. {sciveo-0.3.2 → sciveo-0.3.4}/MANIFEST.in +0 -0
  17. {sciveo-0.3.2 → sciveo-0.3.4}/pyproject.toml +0 -0
  18. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/__init__.py +0 -0
  19. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/admin/__init__.py +0 -0
  20. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/admin/cli.py +0 -0
  21. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/admin/config.py +0 -0
  22. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/admin/device.py +0 -0
  23. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/admin/diagnostics.py +0 -0
  24. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/admin/network.py +0 -0
  25. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/admin/server.py +0 -0
  26. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/__init__.py +0 -0
  27. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/base.py +0 -0
  28. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/cli.py +0 -0
  29. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/clients/__init__.py +0 -0
  30. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/clients/anthropic.py +0 -0
  31. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/clients/gemini.py +0 -0
  32. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/clients/groq.py +0 -0
  33. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/clients/hf.py +0 -0
  34. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/clients/ollama.py +0 -0
  35. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/clients/openai.py +0 -0
  36. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/configurations/coder.md +0 -0
  37. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/configurations/ops.md +0 -0
  38. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/configurations/researcher.md +0 -0
  39. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/configurations/reviewer.md +0 -0
  40. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/configurations/tester.md +0 -0
  41. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/console.py +0 -0
  42. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/evaluation/__init__.py +0 -0
  43. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/evaluation/cases.py +0 -0
  44. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/evaluation/challenges.py +0 -0
  45. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/evaluation/checks.py +0 -0
  46. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/evaluation/local_chat.py +0 -0
  47. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/evaluation/preflight.py +0 -0
  48. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/evaluation/report.py +0 -0
  49. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/evaluation/runner.py +0 -0
  50. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/media.py +0 -0
  51. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/orchestration.py +0 -0
  52. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/protocol.py +0 -0
  53. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/render.py +0 -0
  54. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/runtime.py +0 -0
  55. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/specialization.py +0 -0
  56. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/thinking.py +0 -0
  57. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/tools/__init__.py +0 -0
  58. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/tools/images.py +0 -0
  59. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/tools/os.py +0 -0
  60. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/agents/tools/web.py +0 -0
  61. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/api/__init__.py +0 -0
  62. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/api/base.py +0 -0
  63. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/api/cli.py +0 -0
  64. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/api/ipc.py +0 -0
  65. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/api/prediction.py +0 -0
  66. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/api/predictors.py +0 -0
  67. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/api/server.py +0 -0
  68. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/api/upload.py +0 -0
  69. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/certification/__init__.py +0 -0
  70. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/certification/cli.py +0 -0
  71. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/certification/evaluator.py +0 -0
  72. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/certification/profiles.py +0 -0
  73. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/certification/renderer.py +0 -0
  74. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/certification/scenarios.py +0 -0
  75. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/chat/__init__.py +0 -0
  76. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/chat/cli.py +0 -0
  77. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/chat/client.py +0 -0
  78. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/chat/crypto.py +0 -0
  79. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/chat/protocol.py +0 -0
  80. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/chat/server.py +0 -0
  81. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/chat/store.py +0 -0
  82. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/chat/ui.py +0 -0
  83. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/cli.py +0 -0
  84. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/common/__init__.py +0 -0
  85. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/common/configuration.py +0 -0
  86. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/common/model.py +0 -0
  87. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/common/optimizers.py +0 -0
  88. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/common/sampling.py +0 -0
  89. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/content/__init__.py +0 -0
  90. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/content/dataset.py +0 -0
  91. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/content/experiment.py +0 -0
  92. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/content/project.py +0 -0
  93. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/content/runner.py +0 -0
  94. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/db/__init__.py +0 -0
  95. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/db/read_table.py +0 -0
  96. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/extensions/__init__.py +0 -0
  97. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/extensions/assets/__init__.py +0 -0
  98. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/extensions/assets/vscode-sciveo-0.1.0.vsix +0 -0
  99. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/extensions/catalog.py +0 -0
  100. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/extensions/cli.py +0 -0
  101. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/help.py +0 -0
  102. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/iam/__init__.py +0 -0
  103. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/iam/access.py +0 -0
  104. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/iam/api.py +0 -0
  105. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/iam/client.py +0 -0
  106. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/iam/database.py +0 -0
  107. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/iam/identity.py +0 -0
  108. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/iam/policy.py +0 -0
  109. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/iam/users.py +0 -0
  110. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/industrial/__init__.py +0 -0
  111. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/industrial/can.py +0 -0
  112. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/industrial/cli.py +0 -0
  113. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/industrial/device.py +0 -0
  114. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/industrial/dlms.py +0 -0
  115. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/industrial/dlms_io.py +0 -0
  116. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/industrial/iec104.py +0 -0
  117. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/industrial/iec104_listener.py +0 -0
  118. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/industrial/iec61850.py +0 -0
  119. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/industrial/modbus/__init__.py +0 -0
  120. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/industrial/modbus_device.py +0 -0
  121. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/industrial/opcua.py +0 -0
  122. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/industrial/sdi12.py +0 -0
  123. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/log.py +0 -0
  124. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/logger.py +0 -0
  125. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/__init__.py +0 -0
  126. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/__init__.py +0 -0
  127. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/cam.py +0 -0
  128. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/cameras.py +0 -0
  129. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/discovery.py +0 -0
  130. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/draw.py +0 -0
  131. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/games/__init__.py +0 -0
  132. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/games/asteroids/__init__.py +0 -0
  133. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/games/asteroids/asteroid.py +0 -0
  134. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/games/asteroids/base_move.py +0 -0
  135. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/games/asteroids/circle.py +0 -0
  136. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/games/asteroids/game.py +0 -0
  137. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/games/asteroids/geometrics.py +0 -0
  138. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/games/asteroids/gravity_ball.py +0 -0
  139. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/games/asteroids/laser.py +0 -0
  140. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/games/asteroids/rocket.py +0 -0
  141. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/games/asteroids/space_asteroids.py +0 -0
  142. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/games/asteroids/volume_bar.py +0 -0
  143. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/gst_server.py +0 -0
  144. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/motion_detection.py +0 -0
  145. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/nvr.py +0 -0
  146. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/onvif.py +0 -0
  147. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/onvif_device.py +0 -0
  148. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/player.py +0 -0
  149. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/readers.py +0 -0
  150. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/recording_timeline.py +0 -0
  151. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/recordings.py +0 -0
  152. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/rpi.py +0 -0
  153. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/rtp.py +0 -0
  154. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/rtsp.py +0 -0
  155. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/rtsp_client.py +0 -0
  156. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/rtsp_scan.py +0 -0
  157. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/settings.py +0 -0
  158. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/storage.py +0 -0
  159. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/system_player.py +0 -0
  160. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/capture/transcode.py +0 -0
  161. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/cli.py +0 -0
  162. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/ml/__init__.py +0 -0
  163. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/ml/base.py +0 -0
  164. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/ml/encoders/__init__.py +0 -0
  165. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/ml/encoders/base.py +0 -0
  166. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/ml/encoders/normalizer.py +0 -0
  167. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/ml/nlp/__init__.py +0 -0
  168. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/ml/nlp/search.py +0 -0
  169. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/ml/time_series/__init__.py +0 -0
  170. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/ml/time_series/dataset.py +0 -0
  171. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/ml/time_series/predictor.py +0 -0
  172. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/ml/time_series/trainer.py +0 -0
  173. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/ml/time_series/window_generator.py +0 -0
  174. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/__init__.py +0 -0
  175. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/base.py +0 -0
  176. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/job_daemon.py +0 -0
  177. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/layouts/__init__.py +0 -0
  178. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/layouts/base.py +0 -0
  179. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/local.py +0 -0
  180. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/pipeline.py +0 -0
  181. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/postprocessors/__init__.py +0 -0
  182. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/postprocessors/base.py +0 -0
  183. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/postprocessors/default.py +0 -0
  184. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/__init__.py +0 -0
  185. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/audio/__init__.py +0 -0
  186. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/audio/audio.py +0 -0
  187. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/audio/audio_extractor_process.py +0 -0
  188. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/aws.py +0 -0
  189. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/base.py +0 -0
  190. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/file/__init__.py +0 -0
  191. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/file/archive.py +0 -0
  192. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/image/__init__.py +0 -0
  193. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/image/album.py +0 -0
  194. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/image/album_in_image.py +0 -0
  195. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/image/depth_esimation.py +0 -0
  196. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/image/embeddings.py +0 -0
  197. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/image/filters.py +0 -0
  198. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/image/generators.py +0 -0
  199. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/image/histogram.py +0 -0
  200. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/image/mask.py +0 -0
  201. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/image/object_detection.py +0 -0
  202. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/image/resize.py +0 -0
  203. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/image/segmentation.py +0 -0
  204. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/image/watermark.py +0 -0
  205. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/media_info.py +0 -0
  206. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/nlp/__init__.py +0 -0
  207. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/nlp/address.py +0 -0
  208. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/qr.py +0 -0
  209. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/sci/__init__.py +0 -0
  210. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/sci/base.py +0 -0
  211. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/sci/dataset.py +0 -0
  212. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/sci/time_series/__init__.py +0 -0
  213. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/sci/time_series/predictor.py +0 -0
  214. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/sci/time_series/trainer.py +0 -0
  215. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/tpu_base.py +0 -0
  216. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/video/__init__.py +0 -0
  217. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/video/generators.py +0 -0
  218. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/video/motion_detection.py +0 -0
  219. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/video/resize.py +0 -0
  220. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/video/video_album.py +0 -0
  221. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/video/video_frames.py +0 -0
  222. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/processors/video/video_resample.py +0 -0
  223. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/queues.py +0 -0
  224. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/server.py +0 -0
  225. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/web/__init__.py +0 -0
  226. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/pipelines/web/server.py +0 -0
  227. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/tools/__init__.py +0 -0
  228. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/media/tools/video_interactive.py +0 -0
  229. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/__init__.py +0 -0
  230. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/base.py +0 -0
  231. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/dataset/__init__.py +0 -0
  232. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/dataset/object_detection.py +0 -0
  233. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/energy/__init__.py +0 -0
  234. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/energy/optimization.py +0 -0
  235. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/energy/solar.py +0 -0
  236. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/evaluation/__init__.py +0 -0
  237. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/evaluation/markdown.py +0 -0
  238. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/evaluation/object_detection.py +0 -0
  239. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/__init__.py +0 -0
  240. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/base.py +0 -0
  241. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/description.py +0 -0
  242. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/embeddings.py +0 -0
  243. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/object_detection.py +0 -0
  244. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/sciveyolo/__init__.py +0 -0
  245. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/sciveyolo/artifact.py +0 -0
  246. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/sciveyolo/engines.py +0 -0
  247. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/sciveyolo/evaluate.py +0 -0
  248. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/sciveyolo/ipc.py +0 -0
  249. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/sciveyolo/model.py +0 -0
  250. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/sciveyolo/results.py +0 -0
  251. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/sciveyolo/runtime.py +0 -0
  252. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/sciveyolo/runtime_stats.py +0 -0
  253. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/sciveyolo/torch_net.py +0 -0
  254. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/sciveyolo/train.py +0 -0
  255. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/sciveyolo/train_checkpoint.py +0 -0
  256. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/sciveyolo/train_data.py +0 -0
  257. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/sciveyolo/train_loss.py +0 -0
  258. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/sciveyolo/train_validate.py +0 -0
  259. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/tools.py +0 -0
  260. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/transformers.py +0 -0
  261. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/images/vlm.py +0 -0
  262. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/nlp/__init__.py +0 -0
  263. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/nlp/embeddings.py +0 -0
  264. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/nlp/tokenizers/__init__.py +0 -0
  265. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/nlp/tokenizers/bpe.py +0 -0
  266. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/video/__init__.py +0 -0
  267. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ml/video/description.py +0 -0
  268. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/monitoring/__init__.py +0 -0
  269. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/monitoring/cli.py +0 -0
  270. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/monitoring/monitor.py +0 -0
  271. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/monitoring/plant/__init__.py +0 -0
  272. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/monitoring/plant/ems300.py +0 -0
  273. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/monitoring/plant/sim.py +0 -0
  274. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/monitoring/plant/tools.py +0 -0
  275. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/monitoring/power.py +0 -0
  276. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/monitoring/start.py +0 -0
  277. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/monitoring/watchdog/__init__.py +0 -0
  278. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/monitoring/watchdog/base.py +0 -0
  279. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/monitoring/watchdog/gateway.py +0 -0
  280. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/monitoring/watchdog/network.py +0 -0
  281. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/monitoring/watchdog/privileged.py +0 -0
  282. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/monitoring/watchdog/process.py +0 -0
  283. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/monitoring/watchdog/repair.py +0 -0
  284. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/__init__.py +0 -0
  285. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/camera.py +0 -0
  286. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/cli.py +0 -0
  287. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/health.py +0 -0
  288. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/http_server.py +0 -0
  289. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/http_tools.py +0 -0
  290. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/interfaces.py +0 -0
  291. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/l2.py +0 -0
  292. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/modbus/__init__.py +0 -0
  293. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/modbus/catalog.py +0 -0
  294. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/modbus/cli.py +0 -0
  295. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/modbus/dashboard.py +0 -0
  296. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/modbus/discovery.py +0 -0
  297. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/modbus/models.py +0 -0
  298. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/modbus/profiles.py +0 -0
  299. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/modbus/server.py +0 -0
  300. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/modbus/session.py +0 -0
  301. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/modbus/sunspec.py +0 -0
  302. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/modbus/terminal.py +0 -0
  303. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/modbus/tools.py +0 -0
  304. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/mqtt.py +0 -0
  305. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/mqtt_server.py +0 -0
  306. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/onvif/__init__.py +0 -0
  307. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/onvif/client.py +0 -0
  308. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/onvif/device.py +0 -0
  309. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/onvif/discovery.py +0 -0
  310. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/onvif/ptz.py +0 -0
  311. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/onvif/security.py +0 -0
  312. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/onvif/server.py +0 -0
  313. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/onvif/services.py +0 -0
  314. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/onvif/settings.py +0 -0
  315. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/onvif/xml.py +0 -0
  316. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/selectors.py +0 -0
  317. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/sniff/__init__.py +0 -0
  318. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/sniff/av1.py +0 -0
  319. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/sniff/capture.py +0 -0
  320. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/sniff/cli.py +0 -0
  321. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/sniff/codecs.py +0 -0
  322. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/sniff/inspector.py +0 -0
  323. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/sniff/modbus.py +0 -0
  324. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/sniff/modbus_cli.py +0 -0
  325. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/sniff/modbus_inventory.py +0 -0
  326. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/sniff/modbus_pdu.py +0 -0
  327. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/sniff/network_cli.py +0 -0
  328. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/sniff/packets.py +0 -0
  329. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/sniff/protocols.py +0 -0
  330. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/sniff/streams.py +0 -0
  331. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/sniff/traffic.py +0 -0
  332. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/sniffer.py +0 -0
  333. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/snmp/__init__.py +0 -0
  334. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/snmp/oids.py +0 -0
  335. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/snmp/server.py +0 -0
  336. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/snmp/snapshot.py +0 -0
  337. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/snmp/tools.py +0 -0
  338. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/ssh.py +0 -0
  339. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/network/tools.py +0 -0
  340. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/notifications/__init__.py +0 -0
  341. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/notifications/base.py +0 -0
  342. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/notifications/slack.py +0 -0
  343. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/notifications/telegram.py +0 -0
  344. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/notifications/viber.py +0 -0
  345. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ops/__init__.py +0 -0
  346. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ops/cli.py +0 -0
  347. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ops/doctor.py +0 -0
  348. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ops/fleet.py +0 -0
  349. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ops/industrial.py +0 -0
  350. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/plant/__init__.py +0 -0
  351. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/plant/measurement.py +0 -0
  352. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/plant/modbus.py +0 -0
  353. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/__init__.py +0 -0
  354. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/auth.py +0 -0
  355. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/cli.py +0 -0
  356. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/cluster/__init__.py +0 -0
  357. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/cluster/client.py +0 -0
  358. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/cluster/config.py +0 -0
  359. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/cluster/controller.py +0 -0
  360. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/cluster/gateway.py +0 -0
  361. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/cluster/metadata.py +0 -0
  362. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/cluster/node.py +0 -0
  363. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/cluster/placement.py +0 -0
  364. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/cluster/protocol.py +0 -0
  365. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/cluster/repair.py +0 -0
  366. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/cluster/server.py +0 -0
  367. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/cors.py +0 -0
  368. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/iam.py +0 -0
  369. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/local.py +0 -0
  370. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/metadata.py +0 -0
  371. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/mutation.py +0 -0
  372. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/policy.py +0 -0
  373. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/post.py +0 -0
  374. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/process.py +0 -0
  375. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/runtime.py +0 -0
  376. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/s3.py +0 -0
  377. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/storage/sync.py +0 -0
  378. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/__init__.py +0 -0
  379. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/array.py +0 -0
  380. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/aws/__init__.py +0 -0
  381. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/aws/priority_queue.py +0 -0
  382. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/aws/s3.py +0 -0
  383. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/cli.py +0 -0
  384. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/common.py +0 -0
  385. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/complexity.py +0 -0
  386. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/compress.py +0 -0
  387. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/configuration.py +0 -0
  388. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/configuration_cli.py +0 -0
  389. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/configuration_tui.py +0 -0
  390. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/crypto.py +0 -0
  391. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/daemon.py +0 -0
  392. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/draw/__init__.py +0 -0
  393. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/draw/contours.py +0 -0
  394. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/formating.py +0 -0
  395. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/hardware.py +0 -0
  396. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/http.py +0 -0
  397. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/logger.py +0 -0
  398. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/logggin.py +0 -0
  399. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/os.py +0 -0
  400. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/preferences.py +0 -0
  401. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/queue.py +0 -0
  402. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/random.py +0 -0
  403. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/remote.py +0 -0
  404. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/simple_counter.py +0 -0
  405. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/sorting.py +0 -0
  406. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/synchronized.py +0 -0
  407. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/timers.py +0 -0
  408. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/tools/totp.py +0 -0
  409. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/__init__.py +0 -0
  410. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/cli.py +0 -0
  411. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/gui/__init__.py +0 -0
  412. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/gui/html/__init__.py +0 -0
  413. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/gui/html/client.py +0 -0
  414. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/gui/html/renderer.py +0 -0
  415. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/gui/html/server.py +0 -0
  416. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/gui/renderer.py +0 -0
  417. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/gui/window.py +0 -0
  418. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/html.py +0 -0
  419. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/tables.py +0 -0
  420. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/__init__.py +0 -0
  421. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/containers.py +0 -0
  422. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/dialogs.py +0 -0
  423. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/documents.py +0 -0
  424. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/gallery.py +0 -0
  425. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/html.py +0 -0
  426. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/images.py +0 -0
  427. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/layout.py +0 -0
  428. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/live.py +0 -0
  429. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/measurements.py +0 -0
  430. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/panels.py +0 -0
  431. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/progress.py +0 -0
  432. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/screen.py +0 -0
  433. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/selectors.py +0 -0
  434. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/status.py +0 -0
  435. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/streaming.py +0 -0
  436. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/tables.py +0 -0
  437. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/terminal.py +0 -0
  438. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/text.py +0 -0
  439. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/themes.py +0 -0
  440. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/trees.py +0 -0
  441. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/ui/term/widgets.py +0 -0
  442. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/web/__init__.py +0 -0
  443. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo/web/common.py +0 -0
  444. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo.egg-info/dependency_links.txt +0 -0
  445. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo.egg-info/entry_points.txt +0 -0
  446. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo.egg-info/requires.txt +0 -0
  447. {sciveo-0.3.2 → sciveo-0.3.4}/sciveo.egg-info/top_level.txt +0 -0
  448. {sciveo-0.3.2 → sciveo-0.3.4}/setup.cfg +0 -0
  449. {sciveo-0.3.2 → sciveo-0.3.4}/setup.py +0 -0
  450. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_admin.py +0 -0
  451. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_admin_concurrency.py +0 -0
  452. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_agents.py +0 -0
  453. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_agents_chat_listener.py +0 -0
  454. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_agents_evaluation.py +0 -0
  455. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_agents_evaluation_extended.py +0 -0
  456. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_agents_image_integration.py +0 -0
  457. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_agents_orchestration.py +0 -0
  458. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_agents_thinking.py +0 -0
  459. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_api_client.py +0 -0
  460. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_camera_discovery.py +0 -0
  461. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_camera_liveness.py +0 -0
  462. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_certification.py +0 -0
  463. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_chat.py +0 -0
  464. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_chint_dtsu666_catalog.py +0 -0
  465. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_cli_delegation.py +0 -0
  466. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_cli_modules.py +0 -0
  467. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_cli_themes.py +0 -0
  468. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_complexity.py +0 -0
  469. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_compress.py +0 -0
  470. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_configuration.py +0 -0
  471. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_configuration_tui.py +0 -0
  472. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_crypto.py +0 -0
  473. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_eval_markdown.py +0 -0
  474. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_extensions_catalog.py +0 -0
  475. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_gui.py +0 -0
  476. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_gui_http.py +0 -0
  477. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_help.py +0 -0
  478. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_iam.py +0 -0
  479. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_iam_database.py +0 -0
  480. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_industrial.py +0 -0
  481. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_industrial_protocols.py +0 -0
  482. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_l2.py +0 -0
  483. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_log.py +0 -0
  484. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_media_cli.py +0 -0
  485. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_ml_datasets.py +0 -0
  486. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_ml_energy.py +0 -0
  487. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_ml_ipc.py +0 -0
  488. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_ml_sciveyolo.py +0 -0
  489. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_ml_vlm.py +0 -0
  490. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_modbus.py +0 -0
  491. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_modbus_dashboard.py +0 -0
  492. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_modbus_models.py +0 -0
  493. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_modbus_sessions.py +0 -0
  494. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_monitor_http.py +0 -0
  495. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_monitoring.py +0 -0
  496. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_network_health.py +0 -0
  497. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_network_interfaces.py +0 -0
  498. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_network_selector.py +0 -0
  499. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_network_sniffer.py +0 -0
  500. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_notifications.py +0 -0
  501. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_nvr_capture.py +0 -0
  502. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_nvr_faulty_video.py +0 -0
  503. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_nvr_transcode.py +0 -0
  504. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_onvif.py +0 -0
  505. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_onvif_ptz.py +0 -0
  506. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_onvif_server.py +0 -0
  507. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_onvif_settings.py +0 -0
  508. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_ops_doctor_fleet.py +0 -0
  509. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_plant_modbus.py +0 -0
  510. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_predictor_transport.py +0 -0
  511. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_preferences.py +0 -0
  512. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_protocol_emulators_integration.py +0 -0
  513. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_protocol_io.py +0 -0
  514. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_recordings.py +0 -0
  515. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_rtsp_play.py +0 -0
  516. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_rtsp_scan.py +0 -0
  517. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_rtsp_viewer.py +0 -0
  518. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_runner.py +0 -0
  519. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_sampling.py +0 -0
  520. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_sciveyolo_ipc.py +0 -0
  521. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_sdm630_catalog.py +0 -0
  522. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_sniff_runtime.py +0 -0
  523. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_snmp.py +0 -0
  524. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_sorting.py +0 -0
  525. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_ssh.py +0 -0
  526. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_storage.py +0 -0
  527. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_storage_auth_hardening.py +0 -0
  528. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_storage_cluster.py +0 -0
  529. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_storage_cors.py +0 -0
  530. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_storage_iam.py +0 -0
  531. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_storage_iam_users.py +0 -0
  532. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_storage_metadata_hardening.py +0 -0
  533. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_storage_security_limits.py +0 -0
  534. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_sungrow_sh10rt_catalog.py +0 -0
  535. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_term_containers.py +0 -0
  536. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_term_dialogs.py +0 -0
  537. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_term_documents.py +0 -0
  538. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_term_gallery.py +0 -0
  539. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_term_html.py +0 -0
  540. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_term_progress.py +0 -0
  541. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_term_reports.py +0 -0
  542. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_term_selectors.py +0 -0
  543. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_term_streaming.py +0 -0
  544. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_term_table_options.py +0 -0
  545. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_term_tables.py +0 -0
  546. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_term_themes.py +0 -0
  547. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_term_views.py +0 -0
  548. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_tokenizers.py +0 -0
  549. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_totp.py +0 -0
  550. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_ui_html.py +0 -0
  551. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_ui_tables.py +0 -0
  552. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_video_codecs.py +0 -0
  553. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_video_player.py +0 -0
  554. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_watchdog_gateway.py +0 -0
  555. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_watchdog_network.py +0 -0
  556. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_watchdog_privileged.py +0 -0
  557. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_watchdog_repair.py +0 -0
  558. {sciveo-0.3.2 → sciveo-0.3.4}/test/test_watchdog_service.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sciveo
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Description-Content-Type: text/markdown
5
5
  Provides-Extra: mon
6
6
  Provides-Extra: agent
@@ -2396,6 +2396,19 @@ staging, and `BASE/video` for retained recordings. No separate `tmp`, `video`,
2396
2396
  `motion.src`, or `motion.dst` paths are needed. Camera-specific motion settings
2397
2397
  remain under `motion`.
2398
2398
 
2399
+ To keep temporary video I/O in RAM while retaining recordings on those HDD roots,
2400
+ set `"temp_path": "/mnt/nvr-ram"` to a separately mounted, size-limited tmpfs.
2401
+ Capture, motion processing and upload staging then use `TEMP/tmp2`, `TEMP/tmp3`
2402
+ and `TEMP/tmp4`; archives still use the ordered `BASE/video` roots and their
2403
+ capacity-based cleanup. The same free-space reserve applies to the temporary
2404
+ filesystem, so size it above that reserve plus the expected queued video volume.
2405
+ When temporary space runs low, capture waits while processing drains the queues;
2406
+ it does not switch temporary writes onto an HDD or delete archives to free RAM.
2407
+ Make the NVR service require the tmpfs mount at boot. RAM queues are volatile
2408
+ across reboot; finalized HDD archives remain persistent. Existing HDD queues are
2409
+ not moved or deleted when changing `temp_path`. Point external uploaders at
2410
+ `TEMP/tmp4` when this option is enabled.
2411
+
2399
2412
  Optional per-stage transcoding is disabled unless configured. Put `transcode`
2400
2413
  under `cam.CAMERA` to transcode every completed clip before archival and motion
2401
2414
  processing. Put it under `motion.CAMERA` to transcode **only motion-selected**
@@ -2432,7 +2445,7 @@ processing continues without repeatedly encoding the same failed clip. Storage
2432
2445
  write failures retain the source for retry; no incomplete MP4 enters the uploader
2433
2446
  queue. An all-zero motion grid skips video decoding entirely.
2434
2447
 
2435
- All stages use the first available root in list order, skipping absent, unmounted,
2448
+ Stages without a separate `temp_path` use the first available root in list order, skipping absent, unmounted,
2436
2449
  or unwritable roots. Low space does not cause fallback. Free space must cover
2437
2450
  both the incoming clip and the larger of `min_free_bytes` and `min_free_ratio`
2438
2451
  (defaults: 1 GiB and 5%). Capture rechecks
@@ -2473,7 +2486,7 @@ With no usable root, capture waits and queued files are retained until storage
2473
2486
  recovers. This provides fallback, not replication: clips already on a disconnected
2474
2487
  disk become available again when that disk returns.
2475
2488
 
2476
- External uploaders must watch `BASE/tmp4` on **every** configured root, including
2489
+ Without `temp_path`, external uploaders must watch `BASE/tmp4` on **every** configured root, including
2477
2490
  roots that recover after startup. Existing queued files are not relocated when
2478
2491
  migrating configurations. Legacy single-directory `path.tmp`, `path.video`,
2479
2492
  `motion.src`, and `motion.dst` configurations remain supported when `paths` is
@@ -1,28 +1,3 @@
1
- Metadata-Version: 2.1
2
- Name: sciveo
3
- Version: 0.3.2
4
- Description-Content-Type: text/markdown
5
- Provides-Extra: mon
6
- Provides-Extra: agent
7
- Provides-Extra: agent-local
8
- Provides-Extra: net
9
- Provides-Extra: server
10
- Provides-Extra: storage
11
- Provides-Extra: storage-postgres
12
- Provides-Extra: chat
13
- Provides-Extra: media
14
- Provides-Extra: media-ml
15
- Provides-Extra: sciveyolo
16
- Provides-Extra: sciveyolo-gpu
17
- Provides-Extra: sciveyolo-trt
18
- Provides-Extra: web
19
- Provides-Extra: db
20
- Provides-Extra: power
21
- Provides-Extra: industrial
22
- Provides-Extra: energy
23
- Provides-Extra: all
24
- Provides-Extra: ml
25
-
26
1
  # SCIVEO
27
2
 
28
3
  Sciveo is a Python toolkit and CLI for ML/AI workflows, engineering automation,
@@ -2396,6 +2371,19 @@ staging, and `BASE/video` for retained recordings. No separate `tmp`, `video`,
2396
2371
  `motion.src`, or `motion.dst` paths are needed. Camera-specific motion settings
2397
2372
  remain under `motion`.
2398
2373
 
2374
+ To keep temporary video I/O in RAM while retaining recordings on those HDD roots,
2375
+ set `"temp_path": "/mnt/nvr-ram"` to a separately mounted, size-limited tmpfs.
2376
+ Capture, motion processing and upload staging then use `TEMP/tmp2`, `TEMP/tmp3`
2377
+ and `TEMP/tmp4`; archives still use the ordered `BASE/video` roots and their
2378
+ capacity-based cleanup. The same free-space reserve applies to the temporary
2379
+ filesystem, so size it above that reserve plus the expected queued video volume.
2380
+ When temporary space runs low, capture waits while processing drains the queues;
2381
+ it does not switch temporary writes onto an HDD or delete archives to free RAM.
2382
+ Make the NVR service require the tmpfs mount at boot. RAM queues are volatile
2383
+ across reboot; finalized HDD archives remain persistent. Existing HDD queues are
2384
+ not moved or deleted when changing `temp_path`. Point external uploaders at
2385
+ `TEMP/tmp4` when this option is enabled.
2386
+
2399
2387
  Optional per-stage transcoding is disabled unless configured. Put `transcode`
2400
2388
  under `cam.CAMERA` to transcode every completed clip before archival and motion
2401
2389
  processing. Put it under `motion.CAMERA` to transcode **only motion-selected**
@@ -2432,7 +2420,7 @@ processing continues without repeatedly encoding the same failed clip. Storage
2432
2420
  write failures retain the source for retry; no incomplete MP4 enters the uploader
2433
2421
  queue. An all-zero motion grid skips video decoding entirely.
2434
2422
 
2435
- All stages use the first available root in list order, skipping absent, unmounted,
2423
+ Stages without a separate `temp_path` use the first available root in list order, skipping absent, unmounted,
2436
2424
  or unwritable roots. Low space does not cause fallback. Free space must cover
2437
2425
  both the incoming clip and the larger of `min_free_bytes` and `min_free_ratio`
2438
2426
  (defaults: 1 GiB and 5%). Capture rechecks
@@ -2473,7 +2461,7 @@ With no usable root, capture waits and queued files are retained until storage
2473
2461
  recovers. This provides fallback, not replication: clips already on a disconnected
2474
2462
  disk become available again when that disk returns.
2475
2463
 
2476
- External uploaders must watch `BASE/tmp4` on **every** configured root, including
2464
+ Without `temp_path`, external uploaders must watch `BASE/tmp4` on **every** configured root, including
2477
2465
  roots that recover after startup. Existing queued files are not relocated when
2478
2466
  migrating configurations. Legacy single-directory `path.tmp`, `path.video`,
2479
2467
  `motion.src`, and `motion.dst` configurations remain supported when `paths` is
@@ -280,7 +280,7 @@ class CameraDashboard:
280
280
 
281
281
  @staticmethod
282
282
  def selection_marker(record):
283
- return {'rtsp': '▶', 'file': '▣', 'sniff': '◉'}.get(record.get('source_kind'), '>')
283
+ return {'rtsp': '▶', 'file': '▷', 'sniff': '»'}.get(record.get('source_kind'), '>')
284
284
 
285
285
  @staticmethod
286
286
  def label(record):
@@ -97,6 +97,8 @@ class StorageConfig:
97
97
  self.iam = dict(iam or {})
98
98
  self.iam_base_path = os.path.abspath(os.path.expanduser(str(iam_base_path))) if iam_base_path else None
99
99
  self.http = self._normalize_http_config(http)
100
+ if not 0 <= self.http["max_downloads"] < self.threads:
101
+ raise ValueError("Storage http.max_downloads must be zero (unlimited) or smaller than threads")
100
102
  self.limits = dict(limits or {})
101
103
  self.retention = dict(retention or {})
102
104
  if self.resume_free_ratio < self.min_free_ratio:
@@ -406,6 +408,7 @@ class StorageConfig:
406
408
  "read_timeout": max(1.0, float(value.get("read_timeout") or min(timeout, 120))),
407
409
  "lock_timeout": max(0.1, float(value.get("lock_timeout") or 30)),
408
410
  "keepalive": max(1, int(value.get("keepalive") or 65)),
411
+ "max_downloads": int(value.get("max_downloads") or 0),
409
412
  }
410
413
 
411
414
  def db_label(self):
@@ -9,6 +9,7 @@
9
9
  # 2026
10
10
  #
11
11
 
12
+ import contextlib
12
13
  import errno
13
14
  import hashlib
14
15
  import json
@@ -40,6 +41,14 @@ class StorageSizeExceeded(StoragePathError):
40
41
  pass
41
42
 
42
43
 
44
+ class StorageUploadNotFound(StoragePathError):
45
+ pass
46
+
47
+
48
+ class StoragePartNotFound(StoragePathError):
49
+ pass
50
+
51
+
43
52
  class StorageRequestBodyError(StoragePathError):
44
53
  def __init__(self, message, bytes_read=0, expected_size=None):
45
54
  super().__init__(message)
@@ -52,6 +61,7 @@ class StoragePathMapper:
52
61
  MARKER_FILE_NAME = ".sciveo-s3-prefix-marker"
53
62
  ROOT_IDENTITY_FILE = ".sciveo-storage-root.json"
54
63
  MUTATION_DIRECTORY = "mutations"
64
+ UNHEALTHY_RECHECK_INTERVAL = 30.0
55
65
 
56
66
  @classmethod
57
67
  def from_config(cls, config, metadata, namespace="sciveo-s3"):
@@ -1178,6 +1188,8 @@ class StoragePathMapper:
1178
1188
  with self.health_lock:
1179
1189
  state = dict(self.root_health[root_index])
1180
1190
  interval = max(0.0, self.health_check_interval)
1191
+ if not state["healthy"]:
1192
+ interval = min(interval, self.UNHEALTHY_RECHECK_INTERVAL)
1181
1193
  if not force and state["healthy"] and interval <= 0:
1182
1194
  return
1183
1195
  if not force and time.time() - float(state.get("last_checked") or 0.0) < interval:
@@ -1288,7 +1300,7 @@ class StoragePathMapper:
1288
1300
 
1289
1301
  def health_monitor_loop(self):
1290
1302
  intervals = [value for value in (self.health_check_interval, self.capacity_check_interval) if value > 0]
1291
- interval = max(1.0, min(intervals))
1303
+ interval = max(1.0, min(min(intervals), self.UNHEALTHY_RECHECK_INTERVAL))
1292
1304
  while not self.health_stop.wait(interval):
1293
1305
  try:
1294
1306
  self.refresh_unhealthy_roots(force=False)
@@ -1332,8 +1344,7 @@ class StoragePathMapper:
1332
1344
  candidate = os.path.join(self.base_path(path), "multipart", safe_upload_id)
1333
1345
  if os.path.exists(candidate):
1334
1346
  return candidate
1335
- root_index = self.writable_root_indices()[0]
1336
- return os.path.join(self.base_path(self.paths[root_index]), "multipart", safe_upload_id)
1347
+ return os.path.join(self.base_path(self.paths[0]), "multipart", safe_upload_id)
1337
1348
 
1338
1349
  def create_multipart_upload(self, bucket, key, storage_class="STANDARD", content_type=None, metadata=None, tags=None):
1339
1350
  bucket = self.validate_bucket(bucket)
@@ -1367,8 +1378,27 @@ class StoragePathMapper:
1367
1378
  raise StoragePathUnavailable(f"No writable storage paths are available for multipart upload: {last_error}")
1368
1379
 
1369
1380
  def multipart_upload_info(self, upload_id):
1370
- with open(os.path.join(self.multipart_root(upload_id), "source"), "r") as fp:
1371
- return json.load(fp)
1381
+ last_error = None
1382
+ for root_index in range(len(self.paths)):
1383
+ try:
1384
+ with open(os.path.join(self.multipart_root(upload_id, root_index=root_index), "source"), "r") as fp:
1385
+ return json.load(fp)
1386
+ except FileNotFoundError:
1387
+ continue
1388
+ except OSError as error:
1389
+ last_error = self.write_error(root_index, error)
1390
+ if last_error is not None:
1391
+ raise last_error
1392
+ for root_index in range(len(self.paths)):
1393
+ self.assert_root_present(root_index)
1394
+ raise StorageUploadNotFound("Multipart upload does not exist")
1395
+
1396
+ def missing_multipart_file(self, upload_id, root_index):
1397
+ # An abort may remove staging files while a part is still streaming.
1398
+ # Verify the actual disk before classifying this as an upload-level error.
1399
+ self.assert_root_present(root_index)
1400
+ self.multipart_upload_info(upload_id)
1401
+ return StoragePartNotFound("Multipart part does not exist")
1372
1402
 
1373
1403
  def multipart_root_index(self, upload_id):
1374
1404
  info = self.multipart_upload_info(upload_id)
@@ -1393,7 +1423,8 @@ class StoragePathMapper:
1393
1423
  source,
1394
1424
  chunk_size=1024 * 1024,
1395
1425
  expected_size=None,
1396
- max_size=None):
1426
+ max_size=None,
1427
+ publish_lock=None):
1397
1428
  root_index = self.multipart_root_index(upload_id)
1398
1429
  expected_size = max(0, int(expected_size or 0))
1399
1430
  max_size = None if max_size is None else max(0, int(max_size))
@@ -1404,7 +1435,6 @@ class StoragePathMapper:
1404
1435
  size = 0
1405
1436
  tmp_path = None
1406
1437
  try:
1407
- os.makedirs(os.path.dirname(path), exist_ok=True)
1408
1438
  fd, tmp_path = tempfile.mkstemp(prefix=".part-", dir=os.path.dirname(path))
1409
1439
  with os.fdopen(fd, "wb") as fp:
1410
1440
  self.preallocate(fp, expected_size)
@@ -1420,8 +1450,14 @@ class StoragePathMapper:
1420
1450
  fp.truncate(size)
1421
1451
  fp.flush()
1422
1452
  os.fsync(fp.fileno())
1423
- os.replace(tmp_path, path)
1424
- self.fsync_directory(os.path.dirname(path))
1453
+ # Keep streaming abortable; serialize only publication with completion/abort.
1454
+ with publish_lock or contextlib.nullcontext():
1455
+ self.multipart_upload_info(upload_id)
1456
+ os.replace(tmp_path, path)
1457
+ self.fsync_directory(os.path.dirname(path))
1458
+ etag = self.stat_etag(os.stat(path))
1459
+ except FileNotFoundError as e:
1460
+ raise self.missing_multipart_file(upload_id, root_index) from e
1425
1461
  except OSError as e:
1426
1462
  raise self.write_error(root_index, e, source_consumed=size > 0) from e
1427
1463
  finally:
@@ -1430,13 +1466,22 @@ class StoragePathMapper:
1430
1466
  os.unlink(tmp_path)
1431
1467
  except OSError:
1432
1468
  pass
1433
- return path, size, self.stat_etag(os.stat(path))
1469
+ return path, size, etag
1434
1470
 
1435
1471
  def multipart_size(self, upload_id, part_numbers):
1436
- return sum(os.path.getsize(self.multipart_part_path(upload_id, part_number)) for part_number in part_numbers)
1472
+ return sum(self.multipart_part_stat(upload_id, part_number).st_size for part_number in part_numbers)
1437
1473
 
1438
1474
  def multipart_part_etag(self, upload_id, part_number):
1439
- return self.stat_etag(os.stat(self.multipart_part_path(upload_id, part_number)))
1475
+ return self.stat_etag(self.multipart_part_stat(upload_id, part_number))
1476
+
1477
+ def multipart_part_stat(self, upload_id, part_number):
1478
+ root_index = self.multipart_root_index(upload_id)
1479
+ try:
1480
+ return os.stat(self.multipart_part_path(upload_id, part_number))
1481
+ except FileNotFoundError as error:
1482
+ raise self.missing_multipart_file(upload_id, root_index) from error
1483
+ except OSError as error:
1484
+ raise self.write_error(root_index, error) from error
1440
1485
 
1441
1486
  def list_multipart_uploads(self, bucket=None, prefix=""):
1442
1487
  uploads = []
@@ -1505,8 +1550,6 @@ class StoragePathMapper:
1505
1550
  self.preallocate(output, expected_size)
1506
1551
  for part_number in part_numbers:
1507
1552
  part_path = self.multipart_part_path(upload_id, part_number)
1508
- if not os.path.exists(part_path):
1509
- raise StoragePathError(f"Missing multipart part: {part_number}")
1510
1553
  with open(part_path, "rb") as part:
1511
1554
  while True:
1512
1555
  chunk = part.read(chunk_size)
@@ -1519,6 +1562,9 @@ class StoragePathMapper:
1519
1562
  os.fsync(output.fileno())
1520
1563
  os.replace(tmp_path, path)
1521
1564
  self.fsync_directory(directory)
1565
+ except FileNotFoundError as e:
1566
+ self.assert_root_present(root_index)
1567
+ raise self.missing_multipart_file(upload_id, multipart_root_index) from e
1522
1568
  except OSError as e:
1523
1569
  raise self.write_error(root_index, e, source_consumed=False) from e
1524
1570
  finally:
@@ -1540,7 +1586,10 @@ class StoragePathMapper:
1540
1586
  return
1541
1587
  for dirpath, dirnames, filenames in os.walk(root, topdown=False):
1542
1588
  for filename in filenames:
1543
- os.unlink(os.path.join(dirpath, filename))
1589
+ try:
1590
+ os.unlink(os.path.join(dirpath, filename))
1591
+ except FileNotFoundError:
1592
+ pass
1544
1593
  for dirname in dirnames:
1545
1594
  os.rmdir(os.path.join(dirpath, dirname))
1546
1595
  os.rmdir(root)
@@ -18,6 +18,7 @@ import json
18
18
  import mimetypes
19
19
  import os
20
20
  import sys
21
+ import threading
21
22
  import uuid
22
23
  import zlib
23
24
  import xml.etree.ElementTree as ET
@@ -35,6 +36,8 @@ from sciveo.storage.filesystem import (
35
36
  StoragePathError,
36
37
  StoragePathMapper,
37
38
  StoragePathUnavailable,
39
+ StorageUploadNotFound,
40
+ StoragePartNotFound,
38
41
  StorageRequestBodyError,
39
42
  StorageSizeExceeded,
40
43
  )
@@ -235,11 +238,59 @@ class TimedInputStream:
235
238
  return getattr(self.source, name)
236
239
 
237
240
 
241
+ class StorageDownloadSlot:
242
+ """Hold admission until the WSGI server finishes or closes a download."""
243
+
244
+ def __init__(self, slots, close_body):
245
+ self.slots, self.close_body = slots, close_body
246
+
247
+ @classmethod
248
+ def wrap(cls, body, slots, file_wrapper):
249
+ slot = cls(slots, getattr(body, "close", None))
250
+ if isinstance(file_wrapper, type) and isinstance(body, file_wrapper):
251
+ # Gunicorn dispatches to sendfile by checking the response iterable's type.
252
+ try:
253
+ body.close = slot.close
254
+ except (AttributeError, TypeError):
255
+ pass
256
+ else:
257
+ return body
258
+ return StorageDownloadIterator(body, slot.close)
259
+
260
+ def close(self):
261
+ if self.slots is None:
262
+ return
263
+ slots, self.slots = self.slots, None
264
+ close_body, self.close_body = self.close_body, None
265
+ try:
266
+ if close_body is not None:
267
+ close_body()
268
+ finally:
269
+ slots.release()
270
+
271
+
272
+ class StorageDownloadIterator:
273
+ def __init__(self, body, close):
274
+ self.iterator, self.close = iter(body), close
275
+
276
+ def __iter__(self):
277
+ return self
278
+
279
+ def __next__(self):
280
+ try:
281
+ return next(self.iterator)
282
+ except BaseException:
283
+ self.close()
284
+ raise
285
+
286
+
238
287
  class S3StorageService:
239
288
  XMLNS = "http://s3.amazonaws.com/doc/2006-03-01/"
240
289
 
241
290
  def __init__(self, config):
242
291
  self.config = config
292
+ maximum = config.http.get("max_downloads", 0)
293
+ self.download_slots = threading.BoundedSemaphore(maximum) if maximum else None
243
294
  self.config.validate_server_credentials()
244
295
  self.metadata = StorageMetadata.from_config(config)
245
296
  self.mapper = StoragePathMapper.from_config(config, self.metadata)
@@ -627,7 +678,7 @@ class S3StorageService:
627
678
  if max_size is not None:
628
679
  part_options["max_size"] = max_size
629
680
  _, _, etag = self.mapper.write_multipart_part(
630
- upload_id, part_number, self.request_body_stream(), **part_options)
681
+ upload_id, part_number, self.request_body_stream(), publish_lock=self.mutations.object(bucket, key), **part_options)
631
682
  return Response("", status=200, headers={"ETag": f"\"{etag}\""})
632
683
 
633
684
  def complete_multipart_upload(self, bucket, key):
@@ -635,11 +686,12 @@ class S3StorageService:
635
686
  self.mapper.assert_multipart_source(upload_id, bucket, key)
636
687
  parts = self.complete_parts(self.request_data_verified())
637
688
  part_numbers = [part["part_number"] for part in parts]
638
- for part in parts:
639
- actual = self.mapper.multipart_part_etag(upload_id, part["part_number"])
640
- if part["etag"] and part["etag"] != actual:
641
- return self.error("InvalidPart", "Multipart part ETag does not match", 400)
642
689
  with self.mutations.write(bucket, key, serialize_bucket=self.policy.has_quota(bucket)):
690
+ upload_info = self.mapper.assert_multipart_source(upload_id, bucket, key)
691
+ for part in parts:
692
+ actual = self.mapper.multipart_part_etag(upload_id, part["part_number"])
693
+ if part["etag"] and part["etag"] != actual:
694
+ return self.error("InvalidPart", "Multipart part ETag does not match", 400)
643
695
  self.mutation_journal.recover_identity_locked(bucket, key)
644
696
  existing = self.metadata.get_object(bucket, key)
645
697
  self.policy.validate_overwrite(
@@ -648,13 +700,12 @@ class S3StorageService:
648
700
  write_state = {}
649
701
  root_index, path, size, etag = self.complete_multipart_upload_file(
650
702
  bucket, key, upload_id, part_numbers, existing=existing, write_state=write_state)
651
- upload_info = self.mapper.multipart_upload_info(upload_id)
652
703
  content_type = upload_info.get("content_type") or request.headers.get("Content-Type")
653
704
  storage_class = self.storage_class_from_headers(default=upload_info.get("storage_class") or "STANDARD")
654
705
  obj = self.put_object_metadata_after_write(
655
706
  bucket, key, root_index, path, size, etag, content_type, dict(upload_info.get("metadata") or {}),
656
707
  storage_class=storage_class, existing=existing, write_state=write_state, tags=upload_info.get("tags"))
657
- self.mapper.cleanup_multipart(upload_id)
708
+ self.mapper.cleanup_multipart(upload_id)
658
709
  self.record_operation("multipart_complete", bucket=bucket, root_index=root_index, bytes=size)
659
710
 
660
711
  body = [
@@ -671,8 +722,9 @@ class S3StorageService:
671
722
  self.mapper.validate_bucket(bucket)
672
723
  self.mapper.validate_key(key)
673
724
  upload_id = request.args.get("uploadId")
674
- self.mapper.assert_multipart_source(upload_id, bucket, key)
675
- self.mapper.cleanup_multipart(upload_id)
725
+ with self.mutations.object(bucket, key):
726
+ self.mapper.assert_multipart_source(upload_id, bucket, key)
727
+ self.mapper.cleanup_multipart(upload_id)
676
728
  return Response("", status=204)
677
729
 
678
730
  def list_multipart_uploads(self, bucket):
@@ -722,6 +774,25 @@ class S3StorageService:
722
774
  return self.xml(body)
723
775
 
724
776
  def get_object(self, bucket, key):
777
+ if self.download_slots is None:
778
+ return self.get_object_response(bucket, key)
779
+ if not self.download_slots.acquire(blocking=False):
780
+ self.record_failure("failure_download_limit")
781
+ response = self.error("SlowDown", "Concurrent download limit reached; retry later", 503)
782
+ response.headers["Retry-After"] = "1"
783
+ return response
784
+ try:
785
+ response = self.get_object_response(bucket, key)
786
+ if response.status_code in (200, 206):
787
+ response.response = StorageDownloadSlot.wrap(response.response, self.download_slots, request.environ.get("wsgi.file_wrapper"))
788
+ return response
789
+ except BaseException:
790
+ self.download_slots.release()
791
+ raise
792
+ self.download_slots.release()
793
+ return response
794
+
795
+ def get_object_response(self, bucket, key):
725
796
  with self.mutations.object(bucket, key):
726
797
  obj = self.committed_object(bucket, key)
727
798
  if obj is None:
@@ -1813,6 +1884,12 @@ class S3StorageService:
1813
1884
  except StoragePolicyError as e:
1814
1885
  self.record_failure("failure_policy")
1815
1886
  return self.error("PolicyDenied", str(e), 409)
1887
+ except StorageUploadNotFound as e:
1888
+ self.record_failure("failure_no_such_upload")
1889
+ return self.error("NoSuchUpload", str(e), 404)
1890
+ except StoragePartNotFound as e:
1891
+ self.record_failure("failure_invalid_part")
1892
+ return self.error("InvalidPart", str(e), 400)
1816
1893
  except StoragePathError as e:
1817
1894
  self.record_failure("failure_invalid_argument")
1818
1895
  return self.error("InvalidArgument", str(e), 400)
@@ -124,6 +124,8 @@ class CLIOptions:
124
124
  for word in argv:
125
125
  if word.startswith("-"):
126
126
  option = self.parser._parse_optional(word)
127
+ if isinstance(option, list): # Python 3.12.11+ returns a list of matches.
128
+ option = option[0] if len(option) == 1 else None
127
129
  if option and option[0] is not None:
128
130
  explicit.add(option[0].dest)
129
131
  command = "sniff" if args.command == "sniffer" else args.command
@@ -9,4 +9,4 @@
9
9
  # 2026
10
10
  #
11
11
 
12
- __version__ = '0.3.2'
12
+ __version__ = '0.3.4'
@@ -1,3 +1,28 @@
1
+ Metadata-Version: 2.1
2
+ Name: sciveo
3
+ Version: 0.3.4
4
+ Description-Content-Type: text/markdown
5
+ Provides-Extra: mon
6
+ Provides-Extra: agent
7
+ Provides-Extra: agent-local
8
+ Provides-Extra: net
9
+ Provides-Extra: server
10
+ Provides-Extra: storage
11
+ Provides-Extra: storage-postgres
12
+ Provides-Extra: chat
13
+ Provides-Extra: media
14
+ Provides-Extra: media-ml
15
+ Provides-Extra: sciveyolo
16
+ Provides-Extra: sciveyolo-gpu
17
+ Provides-Extra: sciveyolo-trt
18
+ Provides-Extra: web
19
+ Provides-Extra: db
20
+ Provides-Extra: power
21
+ Provides-Extra: industrial
22
+ Provides-Extra: energy
23
+ Provides-Extra: all
24
+ Provides-Extra: ml
25
+
1
26
  # SCIVEO
2
27
 
3
28
  Sciveo is a Python toolkit and CLI for ML/AI workflows, engineering automation,
@@ -2371,6 +2396,19 @@ staging, and `BASE/video` for retained recordings. No separate `tmp`, `video`,
2371
2396
  `motion.src`, or `motion.dst` paths are needed. Camera-specific motion settings
2372
2397
  remain under `motion`.
2373
2398
 
2399
+ To keep temporary video I/O in RAM while retaining recordings on those HDD roots,
2400
+ set `"temp_path": "/mnt/nvr-ram"` to a separately mounted, size-limited tmpfs.
2401
+ Capture, motion processing and upload staging then use `TEMP/tmp2`, `TEMP/tmp3`
2402
+ and `TEMP/tmp4`; archives still use the ordered `BASE/video` roots and their
2403
+ capacity-based cleanup. The same free-space reserve applies to the temporary
2404
+ filesystem, so size it above that reserve plus the expected queued video volume.
2405
+ When temporary space runs low, capture waits while processing drains the queues;
2406
+ it does not switch temporary writes onto an HDD or delete archives to free RAM.
2407
+ Make the NVR service require the tmpfs mount at boot. RAM queues are volatile
2408
+ across reboot; finalized HDD archives remain persistent. Existing HDD queues are
2409
+ not moved or deleted when changing `temp_path`. Point external uploaders at
2410
+ `TEMP/tmp4` when this option is enabled.
2411
+
2374
2412
  Optional per-stage transcoding is disabled unless configured. Put `transcode`
2375
2413
  under `cam.CAMERA` to transcode every completed clip before archival and motion
2376
2414
  processing. Put it under `motion.CAMERA` to transcode **only motion-selected**
@@ -2407,7 +2445,7 @@ processing continues without repeatedly encoding the same failed clip. Storage
2407
2445
  write failures retain the source for retry; no incomplete MP4 enters the uploader
2408
2446
  queue. An all-zero motion grid skips video decoding entirely.
2409
2447
 
2410
- All stages use the first available root in list order, skipping absent, unmounted,
2448
+ Stages without a separate `temp_path` use the first available root in list order, skipping absent, unmounted,
2411
2449
  or unwritable roots. Low space does not cause fallback. Free space must cover
2412
2450
  both the incoming clip and the larger of `min_free_bytes` and `min_free_ratio`
2413
2451
  (defaults: 1 GiB and 5%). Capture rechecks
@@ -2448,7 +2486,7 @@ With no usable root, capture waits and queued files are retained until storage
2448
2486
  recovers. This provides fallback, not replication: clips already on a disconnected
2449
2487
  disk become available again when that disk returns.
2450
2488
 
2451
- External uploaders must watch `BASE/tmp4` on **every** configured root, including
2489
+ Without `temp_path`, external uploaders must watch `BASE/tmp4` on **every** configured root, including
2452
2490
  roots that recover after startup. Existing queued files are not relocated when
2453
2491
  migrating configurations. Legacy single-directory `path.tmp`, `path.video`,
2454
2492
  `motion.src`, and `motion.dst` configurations remain supported when `paths` is
@@ -458,6 +458,7 @@ test/test_chint_dtsu666_catalog.py
458
458
  test/test_cli_defaults.py
459
459
  test/test_cli_delegation.py
460
460
  test/test_cli_modules.py
461
+ test/test_cli_regression.py
461
462
  test/test_cli_themes.py
462
463
  test/test_complexity.py
463
464
  test/test_compress.py
@@ -523,6 +524,7 @@ test/test_storage.py
523
524
  test/test_storage_auth_hardening.py
524
525
  test/test_storage_cluster.py
525
526
  test/test_storage_cors.py
527
+ test/test_storage_downloads.py
526
528
  test/test_storage_iam.py
527
529
  test/test_storage_iam_users.py
528
530
  test/test_storage_metadata_hardening.py
@@ -54,6 +54,24 @@ class TestCLIOptions(unittest.TestCase):
54
54
  args, _ = self.arguments("scan")
55
55
  self.assertEqual((args.timeout, args.theme), (2, "light"))
56
56
 
57
+ def test_explicit_options_support_old_and_new_argparse_results(self):
58
+ self.preferences.update({"scan.port": "22", "timeout": "7", "local-echo": "false", "theme": "amber"})
59
+ options = self.definitions(self.preferences)
60
+ argv = ["scan", "--port", "502", "--timeo=-2", "--handle-local-echo"]
61
+ matches = [(options.actions["port"], "--port", None, None),
62
+ (options.actions["timeout"], "--timeout", "=", "-2"),
63
+ (options.actions["local-echo"], "--handle-local-echo", None, None)]
64
+ for result_type in ("tuple", "list"):
65
+ with self.subTest(result_type=result_type):
66
+ args = options.parser.parse_args(argv)
67
+ results = [(action, name, value) for action, name, _, value in matches]
68
+ if result_type == "list":
69
+ results = [[match] for match in matches]
70
+ with patch.object(options.parser, "_parse_optional", side_effect=results):
71
+ applied = options.apply(args, argv)
72
+ self.assertEqual((args.port, args.timeout, args.handle_local_echo, args.theme), (502, -2, True, "amber"))
73
+ self.assertEqual(applied, {"theme"})
74
+
57
75
  def test_camera_expiry_global_command_and_explicit_precedence(self):
58
76
  options = self.definitions()
59
77
  for command in ("sniff", "nvr", "rtsp"):