pynode-flow 0.2.3__tar.gz → 0.2.5__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 (330) hide show
  1. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/.dockerignore +0 -1
  2. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/.gitignore +5 -0
  3. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/DOCKER.md +7 -0
  4. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/Dockerfile +22 -8
  5. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/Dockerfile.cpu +22 -9
  6. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/PKG-INFO +171 -68
  7. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/README.md +170 -67
  8. pynode_flow-0.2.5/docker-entrypoint.sh +12 -0
  9. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/__init__.py +5 -1
  10. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/_version.py +3 -3
  11. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/api/nodes.py +5 -1
  12. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/CameraNode/camera_node.py +8 -8
  13. pynode_flow-0.2.5/pynode/nodes/ControlSliderNode/control_slider_node.py +208 -0
  14. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/CropNode/crop_node.py +141 -12
  15. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/DelayNode/delay_node.py +2 -2
  16. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/FrameSourceNode/frame_source_node.py +9 -9
  17. pynode_flow-0.2.5/pynode/nodes/FunctionNode/function_node.py +309 -0
  18. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/ImageFormatNode/image_format_node.py +2 -2
  19. pynode_flow-0.2.5/pynode/nodes/ImageUploadNode/image_upload_node.py +239 -0
  20. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/InferenceNode/InferenceEngine/engines/onnx_engine.py +1 -1
  21. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/MQTTNode/mqtt_in_node.py +4 -3
  22. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/MQTTNode/mqtt_out_node.py +1 -1
  23. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/MQTTNode/mqtt_service.py +1 -1
  24. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OmronCameraNode/omron_camera_node.py +7 -7
  25. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/arithmetic_node.py +5 -5
  26. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/blob_detector_node.py +1 -1
  27. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/colormap_node.py +1 -1
  28. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/fft_node.py +1 -1
  29. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/focus_estimation_node.py +1 -1
  30. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/histogram_node.py +2 -2
  31. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/hough_circles_node.py +2 -2
  32. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/in_range_node.py +1 -1
  33. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/perspective_node.py +6 -6
  34. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/realsense_depth_node.py +2 -2
  35. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/Qwen3VLMNode/qwen3_vlm_node.py +1 -1
  36. pynode_flow-0.2.5/pynode/nodes/RangeNode/range_node.py +220 -0
  37. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/RoboflowUploadNode/roboflow_upload_node.py +1 -1
  38. pynode_flow-0.2.5/pynode/nodes/SocketNode/__init__.py +28 -0
  39. pynode_flow-0.2.5/pynode/nodes/SocketNode/interop/README.md +239 -0
  40. pynode_flow-0.2.5/pynode/nodes/SocketNode/interop/nodered-example-flow.json +318 -0
  41. pynode_flow-0.2.5/pynode/nodes/SocketNode/ndjson_protocol.py +178 -0
  42. pynode_flow-0.2.5/pynode/nodes/SocketNode/nodered_snippets.py +147 -0
  43. pynode_flow-0.2.5/pynode/nodes/SocketNode/requirements.txt +4 -0
  44. pynode_flow-0.2.5/pynode/nodes/SocketNode/tcp_in_node.py +269 -0
  45. pynode_flow-0.2.5/pynode/nodes/SocketNode/tcp_out_node.py +270 -0
  46. pynode_flow-0.2.5/pynode/nodes/SocketNode/udp_in_node.py +260 -0
  47. pynode_flow-0.2.5/pynode/nodes/SocketNode/udp_out_node.py +253 -0
  48. pynode_flow-0.2.5/pynode/nodes/SocketNode/udp_probe.py +105 -0
  49. pynode_flow-0.2.5/pynode/nodes/SocketNode/udp_protocol.py +481 -0
  50. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/SplitNode/split_node.py +2 -2
  51. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/TrackerNode/supervision_tracker_node.py +4 -1
  52. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/UltralyticsNode/ultralytics_node.py +5 -5
  53. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/VideoReaderNode/video_reader_node.py +184 -34
  54. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/WebhookNode/webhook_node.py +1 -1
  55. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/base_node.py +15 -9
  56. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/image_utils.py +1 -1
  57. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/info.py +36 -2
  58. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/mDNSNode/mdns_discovery_node.py +1 -1
  59. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/mDNSNode/mdns_manager.py +3 -3
  60. pynode_flow-0.2.5/pynode/nodes/pacing.py +124 -0
  61. pynode_flow-0.2.5/pynode/static/examples/16-socket-bridge.json +75 -0
  62. pynode_flow-0.2.5/pynode/static/examples/17-slider-crop.json +105 -0
  63. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/examples/README.md +14 -4
  64. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/examples/manifest.json +14 -0
  65. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/events.js +35 -0
  66. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/main.js +5 -1
  67. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/nodes.js +56 -2
  68. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/properties.js +182 -1
  69. pynode_flow-0.2.5/pynode/static/js/ui-utils.js +82 -0
  70. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/style.css +226 -0
  71. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode_flow.egg-info/PKG-INFO +171 -68
  72. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode_flow.egg-info/SOURCES.txt +24 -2
  73. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode_flow.egg-info/scm_file_list.json +266 -244
  74. pynode_flow-0.2.5/pynode_flow.egg-info/scm_version.json +8 -0
  75. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pyproject.toml +3 -1
  76. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/setup.sh +4 -5
  77. pynode_flow-0.2.5/tests/test_control_slider_node.py +199 -0
  78. pynode_flow-0.2.5/tests/test_crop_node.py +138 -0
  79. pynode_flow-0.2.5/tests/test_frame_pacing.py +191 -0
  80. pynode_flow-0.2.5/tests/test_function_node.py +200 -0
  81. pynode_flow-0.2.5/tests/test_image_upload_node.py +176 -0
  82. pynode_flow-0.2.5/tests/test_range_node.py +180 -0
  83. pynode_flow-0.2.5/tests/test_socket_nodes.py +1292 -0
  84. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_video_reader_node.py +98 -0
  85. pynode_flow-0.2.3/plan-pynodeRepoReview.prompt.md +0 -117
  86. pynode_flow-0.2.3/plan-pynodeReview-2026-07-18.prompt.md +0 -135
  87. pynode_flow-0.2.3/pynode/nodes/FunctionNode/function_node.py +0 -168
  88. pynode_flow-0.2.3/pynode/nodes/ImageUploadNode/image_upload_node.py +0 -96
  89. pynode_flow-0.2.3/pynode/nodes/RangeNode/range_node.py +0 -115
  90. pynode_flow-0.2.3/pynode/static/js/ui-utils.js +0 -28
  91. pynode_flow-0.2.3/pynode_flow.egg-info/scm_version.json +0 -8
  92. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/.claude/skills/pynode-workflows/SKILL.md +0 -0
  93. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/.github/workflows/ci.yml +0 -0
  94. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/.github/workflows/publish.yaml +0 -0
  95. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/INSTALL.md +0 -0
  96. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/LICENSE +0 -0
  97. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/MANIFEST.in +0 -0
  98. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/docker-compose.yml +0 -0
  99. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/docs/CREATING_NODES.md +0 -0
  100. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/docs/CUSTOM_NODES.md +0 -0
  101. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/docs/DOCKER.md +0 -0
  102. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/docs/EXTENSIBILITY.md +0 -0
  103. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/docs/UI_COMPONENTS.md +0 -0
  104. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/examples/README.md +0 -0
  105. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/examples/camera_workflow.py +0 -0
  106. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/examples/camera_yolo_workflow.py +0 -0
  107. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/install_nodes.bat +0 -0
  108. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/install_nodes.sh +0 -0
  109. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/__main__.py +0 -0
  110. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/_perf.py +0 -0
  111. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/api/__init__.py +0 -0
  112. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/api/helpers.py +0 -0
  113. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/api/services.py +0 -0
  114. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/api/sse.py +0 -0
  115. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/api/uploads.py +0 -0
  116. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/api/workflows.py +0 -0
  117. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/config.py +0 -0
  118. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/install_nodes.py +0 -0
  119. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/main.py +0 -0
  120. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/node_registry.py +0 -0
  121. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/AutoSyncNode/auto_sync_node.py +0 -0
  122. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/BBoxMetricsNode/__init__.py +0 -0
  123. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/BBoxMetricsNode/bbox_metrics_node.py +0 -0
  124. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/BBoxMetricsNode/requirements.txt +0 -0
  125. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/BatchNode/__init__.py +0 -0
  126. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/BatchNode/batch_node.py +0 -0
  127. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/BatchNode/requirements.txt +0 -0
  128. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/CameraNode/__init__.py +0 -0
  129. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/CameraNode/requirements.txt +0 -0
  130. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/ChangeNode/__init__.py +0 -0
  131. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/ChangeNode/change_node.py +0 -0
  132. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/ChangeNode/requirements.txt +0 -0
  133. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/ConfidenceFilterNode/__init__.py +0 -0
  134. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/ConfidenceFilterNode/confidence_filter_node.py +0 -0
  135. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/ConfidenceFilterNode/requirements.txt +0 -0
  136. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/CounterNode/__init__.py +0 -0
  137. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/CounterNode/counter_node.py +0 -0
  138. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/CropNode/__init__.py +0 -0
  139. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/CropNode/requirements.txt +0 -0
  140. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/DebugNode/__init__.py +0 -0
  141. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/DebugNode/debug_node.py +0 -0
  142. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/DebugNode/requirements.txt +0 -0
  143. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/DelayNode/requirements.txt +0 -0
  144. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/DrawPredictionsNode/__init__.py +0 -0
  145. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/DrawPredictionsNode/draw_predictions_node.py +0 -0
  146. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/DrawPredictionsNode/requirements.txt +0 -0
  147. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/ErrorNode/error_node.py +0 -0
  148. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/ErrorNode/requirements.txt +0 -0
  149. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/FilterNode/__init__.py +0 -0
  150. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/FilterNode/filter_node.py +0 -0
  151. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/FilterNode/requirements.txt +0 -0
  152. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/FrameSourceNode/__init__.py +0 -0
  153. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/FrameSourceNode/requirements.txt +0 -0
  154. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/FunctionNode/__init__.py +0 -0
  155. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/FunctionNode/requirements.txt +0 -0
  156. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/GateNode/gate_node.py +0 -0
  157. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/GateNode/requirements.txt +0 -0
  158. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/ImageFormatNode/__init__.py +0 -0
  159. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/ImageFormatNode/requirements.txt +0 -0
  160. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/ImageViewerNode/__init__.py +0 -0
  161. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/ImageViewerNode/image_viewer_node.py +0 -0
  162. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/ImageViewerNode/requirements.txt +0 -0
  163. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/ImageWriterNode/__init__.py +0 -0
  164. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/ImageWriterNode/imagewriter_node.py +0 -0
  165. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/ImageWriterNode/requirements.txt +0 -0
  166. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/InferenceNode/InferenceEngine/__init__.py +0 -0
  167. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/InferenceNode/InferenceEngine/device_detection.py +0 -0
  168. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/InferenceNode/InferenceEngine/engines/base_engine.py +0 -0
  169. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/InferenceNode/InferenceEngine/engines/example_engine_template.py +0 -0
  170. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/InferenceNode/InferenceEngine/engines/pass_engine.py +0 -0
  171. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/InferenceNode/InferenceEngine/engines/ultralytics_engine.py +0 -0
  172. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/InferenceNode/InferenceEngine/inference_engine_factory.py +0 -0
  173. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/InferenceNode/InferenceEngine/readme.md +0 -0
  174. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/InferenceNode/InferenceEngine/result_converters.py +0 -0
  175. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/InferenceNode/inference_node.py +0 -0
  176. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/InferenceNode/requirements.txt +0 -0
  177. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/InjectNode/inject_node.py +0 -0
  178. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/InjectNode/requirements.txt +0 -0
  179. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/JoinNode/__init__.py +0 -0
  180. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/JoinNode/join_node.py +0 -0
  181. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/JoinNode/requirements.txt +0 -0
  182. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/LabelFilterNode/__init__.py +0 -0
  183. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/LabelFilterNode/label_filter_node.py +0 -0
  184. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/LabelFilterNode/requirements.txt +0 -0
  185. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/LinkNode/__init__.py +0 -0
  186. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/LinkNode/link_bus.py +0 -0
  187. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/LinkNode/link_in_node.py +0 -0
  188. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/LinkNode/link_out_node.py +0 -0
  189. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/LinkNode/requirements.txt +0 -0
  190. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/MQTTNode/__init__.py +0 -0
  191. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/MQTTNode/requirements.txt +0 -0
  192. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/MessageWriterNode/__init__.py +0 -0
  193. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/MessageWriterNode/messagereader_node.py +0 -0
  194. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/MessageWriterNode/messagewriter_node.py +0 -0
  195. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OmronCameraNode/__init__.py +0 -0
  196. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OmronCameraNode/requirements.txt +0 -0
  197. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/__init__.py +0 -0
  198. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/bitwise_node.py +0 -0
  199. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/blend_node.py +0 -0
  200. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/blur_node.py +0 -0
  201. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/color_space_node.py +0 -0
  202. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/contours_node.py +0 -0
  203. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/denormalize_coords_node.py +0 -0
  204. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/draw_node.py +0 -0
  205. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/edge_detector_node.py +0 -0
  206. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/equirectangular_node.py +0 -0
  207. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/frequency_filter_node.py +0 -0
  208. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/hough_lines_node.py +0 -0
  209. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/morphology_node.py +0 -0
  210. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/normalize_coords_node.py +0 -0
  211. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/paste_node.py +0 -0
  212. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/requirements.txt +0 -0
  213. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/resize_node.py +0 -0
  214. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/rotate_node.py +0 -0
  215. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/template_match_node.py +0 -0
  216. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/OpenCV/threshold_node.py +0 -0
  217. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/PointInShapeNode/__init__.py +0 -0
  218. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/PointInShapeNode/point_in_shape_node.py +0 -0
  219. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/PointInShapeNode/requirements.txt +0 -0
  220. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/PolygonMetricsNode/__init__.py +0 -0
  221. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/PolygonMetricsNode/polygon_metrics_node.py +0 -0
  222. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/PolygonMetricsNode/requirements.txt +0 -0
  223. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/QueueLengthProbeNode/__init__.py +0 -0
  224. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/QueueLengthProbeNode/queue_length_probe_node.py +0 -0
  225. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/Qwen3VLMNode/__init__.py +0 -0
  226. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/Qwen3VLMNode/requirements.txt +0 -0
  227. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/RESTEndpointNode/__init__.py +0 -0
  228. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/RESTEndpointNode/rest_endpoint_node.py +0 -0
  229. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/RangeNode/__init__.py +0 -0
  230. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/RangeNode/requirements.txt +0 -0
  231. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/RateProbeNode/__init__.py +0 -0
  232. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/RateProbeNode/rate_probe_node.py +0 -0
  233. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/RateProbeNode/requirements.txt +0 -0
  234. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/RoboflowUploadNode/__init__.py +0 -0
  235. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/RoboflowUploadNode/requirements.txt +0 -0
  236. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/SliceNode/README.md +0 -0
  237. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/SliceNode/__init__.py +0 -0
  238. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/SliceNode/merge_predictions_node.py +0 -0
  239. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/SliceNode/requirements.txt +0 -0
  240. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/SliceNode/slice_collector_node.py +0 -0
  241. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/SliceNode/slice_image_node.py +0 -0
  242. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/SplitNode/__init__.py +0 -0
  243. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/SplitNode/requirements.txt +0 -0
  244. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/SwitchNode/requirements.txt +0 -0
  245. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/SwitchNode/switch_node.py +0 -0
  246. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/SyncNode/__init__.py +0 -0
  247. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/SyncNode/sync_node.py +0 -0
  248. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/TemplateNode/requirements.txt +0 -0
  249. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/TemplateNode/template_node.py +0 -0
  250. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/TrackerNode/__init__.py +0 -0
  251. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/TrackerNode/tracker_node.py +0 -0
  252. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/UltralyticsNode/__init__.py +0 -0
  253. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/UltralyticsNode/model_paths.py +0 -0
  254. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/UltralyticsNode/requirements.txt +0 -0
  255. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/UnknownNode/__init__.py +0 -0
  256. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/VLLMNode/__init__.py +0 -0
  257. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/VLLMNode/vllm_node.py +0 -0
  258. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/VideoReaderNode/__init__.py +0 -0
  259. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/VideoWriterNode/__init__.py +0 -0
  260. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/VideoWriterNode/video_writer_node.py +0 -0
  261. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/WebhookNode/__init__.py +0 -0
  262. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/__init__.py +0 -0
  263. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/mDNSNode/__init__.py +0 -0
  264. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/mDNSNode/mdns_broadcast_node.py +0 -0
  265. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/mDNSNode/requirements.txt +0 -0
  266. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/nodes/messages.py +0 -0
  267. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/server.py +0 -0
  268. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/examples/01-hello-world.json +0 -0
  269. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/examples/02-function-transform.json +0 -0
  270. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/examples/03-switch-routing.json +0 -0
  271. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/examples/04-flow-control.json +0 -0
  272. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/examples/05-webcam-viewer.json +0 -0
  273. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/examples/06-webcam-yolo.json +0 -0
  274. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/examples/07-blur-people.json +0 -0
  275. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/examples/08-video-yolo-recorder.json +0 -0
  276. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/examples/09-mqtt-pub-sub.json +0 -0
  277. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/examples/10-person-alert-mqtt.json +0 -0
  278. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/images/favicon4.png +0 -0
  279. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/index.html +0 -0
  280. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/README.md +0 -0
  281. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/auth.js +0 -0
  282. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/clipboard.js +0 -0
  283. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/config.js +0 -0
  284. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/connections.js +0 -0
  285. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/debug.js +0 -0
  286. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/history.js +0 -0
  287. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/minimap.js +0 -0
  288. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/mqtt-services.js +0 -0
  289. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/palette.js +0 -0
  290. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/selection.js +0 -0
  291. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/state.js +0 -0
  292. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/viewport.js +0 -0
  293. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/workflow.js +0 -0
  294. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/static/js/workflows.js +0 -0
  295. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/workflow_engine.py +0 -0
  296. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode/workflow_manager.py +0 -0
  297. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode_flow.egg-info/dependency_links.txt +0 -0
  298. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode_flow.egg-info/entry_points.txt +0 -0
  299. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode_flow.egg-info/requires.txt +0 -0
  300. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/pynode_flow.egg-info/top_level.txt +0 -0
  301. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/requirements.txt +0 -0
  302. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/setup.bat +0 -0
  303. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/setup.cfg +0 -0
  304. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/setup.py +0 -0
  305. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/__init__.py +0 -0
  306. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/conftest.py +0 -0
  307. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_api_auth.py +0 -0
  308. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_api_errors.py +0 -0
  309. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_api_nodes.py +0 -0
  310. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_api_security.py +0 -0
  311. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_api_workflows.py +0 -0
  312. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_base_node_messaging.py +0 -0
  313. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_camera_node_backend.py +0 -0
  314. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_concurrency.py +0 -0
  315. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_config.py +0 -0
  316. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_counter_sse.py +0 -0
  317. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_debug_workflow_scope.py +0 -0
  318. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_delay_rate_limit.py +0 -0
  319. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_device_detection.py +0 -0
  320. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_link_nodes.py +0 -0
  321. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_message_reader_pickle.py +0 -0
  322. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_models_dir.py +0 -0
  323. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_mqtt_services.py +0 -0
  324. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_node_registration.py +0 -0
  325. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_node_types_api.py +0 -0
  326. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_persistence.py +0 -0
  327. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_send_copy_semantics.py +0 -0
  328. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_serialization.py +0 -0
  329. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_workflow_engine.py +0 -0
  330. {pynode_flow-0.2.3 → pynode_flow-0.2.5}/tests/test_workflow_roundtrip.py +0 -0
@@ -11,7 +11,6 @@ __pycache__/
11
11
  *.egg-info
12
12
  dist/
13
13
  build/
14
- .git/
15
14
  .gitignore
16
15
  .vscode/
17
16
  .idea/
@@ -152,6 +152,8 @@ Thumbs.db
152
152
  *.json
153
153
  # ...but keep the bundled, GUI-loadable example workflows
154
154
  !pynode/static/examples/*.json
155
+ # ...and the example Node-RED flow shipped with the SocketNode nodes
156
+ !pynode/nodes/SocketNode/interop/*.json
155
157
 
156
158
  # PyTorch model files
157
159
  *.pt
@@ -194,3 +196,6 @@ checkpoints/
194
196
  output/
195
197
  output_low/
196
198
  output_not/
199
+
200
+ # Working prompt / plan files (local scratch, not part of the project)
201
+ *.prompt.md
@@ -65,6 +65,13 @@ The following directories are mounted for persistence:
65
65
 
66
66
  ## Building
67
67
 
68
+ ### Version
69
+
70
+ The version shown in the UI is derived automatically from git history during
71
+ the Docker build — no extra flags or environment variables are needed. Tagged
72
+ commits display a clean version (e.g. `v0.2.3`); untagged commits include the
73
+ distance and short SHA (e.g. `v0.2.3-1-gcd272cf`).
74
+
68
75
  ### Custom CUDA Version
69
76
 
70
77
  To use a different CUDA version, edit the `Dockerfile`:
@@ -1,4 +1,4 @@
1
- FROM docker.io/nvidia/cuda:12.6.0-devel-ubuntu22.04
1
+ FROM docker.io/nvidia/cuda:12.6.0-runtime-ubuntu22.04
2
2
 
3
3
  # Set environment variables
4
4
  ENV DEBIAN_FRONTEND=noninteractive
@@ -12,6 +12,7 @@ RUN apt-get update && apt-get install -y \
12
12
  python3-venv \
13
13
  git \
14
14
  wget \
15
+ gosu \
15
16
  gfortran \
16
17
  libgl1-mesa-glx \
17
18
  libgtk-3-0 \
@@ -44,16 +45,29 @@ WORKDIR /app
44
45
  # Copy application files
45
46
  COPY . /app/
46
47
 
47
- # Make scripts executable
48
- RUN chmod +x /app/setup.sh /app/install_nodes.sh
49
-
50
- # Run setup script
51
- RUN bash -c "source /app/setup.sh < /dev/null"
48
+ # Derive the display version from git history, then remove .git to save
49
+ # space. SETUPTOOLS_SCM_PRETEND_VERSION keeps pip install happy (PEP 440);
50
+ # the real version string is written to _version.py afterwards.
51
+ RUN chmod +x /app/setup.sh /app/install_nodes.sh && \
52
+ RAW=$(git -C /app describe --tags --always 2>/dev/null || true) && \
53
+ if echo "$RAW" | grep -qE '^v?[0-9]+\.[0-9]+'; then \
54
+ VERSION=$(echo "$RAW" | sed 's/^v//'); \
55
+ elif [ -n "$RAW" ]; then \
56
+ VERSION="0.0.0+g${RAW}"; \
57
+ else \
58
+ VERSION="0.0.0"; \
59
+ fi && \
60
+ rm -rf /app/.git && \
61
+ SETUPTOOLS_SCM_PRETEND_VERSION=0.0.0 bash -c "source /app/setup.sh < /dev/null" && \
62
+ printf '__version__ = "%s"\n' "$VERSION" > /app/pynode/_version.py
52
63
 
53
64
  # Create a non-root user and give it ownership of the app directory
54
65
  RUN useradd --create-home --uid 1000 appuser \
55
66
  && chown -R appuser:appuser /app
56
- USER appuser
67
+
68
+ # Entrypoint ensures bind-mounted directories are writable by appuser
69
+ COPY docker-entrypoint.sh /app/docker-entrypoint.sh
70
+ RUN chmod +x /app/docker-entrypoint.sh
57
71
 
58
72
  # Expose port
59
73
  EXPOSE 5000
@@ -62,5 +76,5 @@ EXPOSE 5000
62
76
  HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \
63
77
  CMD ["/app/appenv/bin/python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:5000/')"]
64
78
 
65
- # Run the application in production mode
79
+ ENTRYPOINT ["/app/docker-entrypoint.sh"]
66
80
  CMD ["/app/appenv/bin/python", "-m", "pynode", "--production", "--host", "0.0.0.0", "--port", "5000"]
@@ -6,6 +6,7 @@ ENV PYTHONUNBUFFERED=1
6
6
 
7
7
  # Install system dependencies
8
8
  RUN apt-get update && apt-get install -y \
9
+ gosu \
9
10
  libgl1-mesa-glx \
10
11
  libglib2.0-0 \
11
12
  libsm6 \
@@ -19,15 +20,24 @@ WORKDIR /app
19
20
  # Copy application files
20
21
  COPY . /app/
21
22
 
22
- # Create virtual environment
23
- RUN python3 -m venv appenv
24
-
25
- # Activate venv and install dependencies (declared in pyproject.toml;
26
- # CPU-only torch is installed first so the vision extra reuses it)
27
- RUN . appenv/bin/activate && \
23
+ # Derive the display version from git history, then remove .git to save
24
+ # space. SETUPTOOLS_SCM_PRETEND_VERSION keeps pip install happy (PEP 440);
25
+ # the real version string is written to _version.py afterwards.
26
+ RUN RAW=$(git describe --tags --always 2>/dev/null || true) && \
27
+ if echo "$RAW" | grep -qE '^v?[0-9]+\.[0-9]+'; then \
28
+ VERSION=$(echo "$RAW" | sed 's/^v//'); \
29
+ elif [ -n "$RAW" ]; then \
30
+ VERSION="0.0.0+g${RAW}"; \
31
+ else \
32
+ VERSION="0.0.0"; \
33
+ fi && \
34
+ rm -rf /app/.git && \
35
+ python3 -m venv appenv && \
36
+ . appenv/bin/activate && \
28
37
  pip install --upgrade pip && \
29
38
  pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu && \
30
- pip install -e ".[vision,mqtt]"
39
+ SETUPTOOLS_SCM_PRETEND_VERSION=0.0.0 pip install -e ".[vision,mqtt]" && \
40
+ printf '__version__ = "%s"\n' "$VERSION" > /app/pynode/_version.py
31
41
 
32
42
  # Install per-node dependencies (best-effort: a node whose vendor SDK is not
33
43
  # on PyPI, e.g. OmronCameraNode's stapipy, is skipped without failing the build)
@@ -36,7 +46,10 @@ RUN . appenv/bin/activate && pynode-install-nodes || true
36
46
  # Create a non-root user and give it ownership of the app directory
37
47
  RUN useradd --create-home --uid 1000 appuser \
38
48
  && chown -R appuser:appuser /app
39
- USER appuser
49
+
50
+ # Entrypoint ensures bind-mounted directories are writable by appuser
51
+ COPY docker-entrypoint.sh /app/docker-entrypoint.sh
52
+ RUN chmod +x /app/docker-entrypoint.sh
40
53
 
41
54
  # Expose port
42
55
  EXPOSE 5000
@@ -45,5 +58,5 @@ EXPOSE 5000
45
58
  HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \
46
59
  CMD ["/app/appenv/bin/python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:5000/')"]
47
60
 
48
- # Run the application in production mode
61
+ ENTRYPOINT ["/app/docker-entrypoint.sh"]
49
62
  CMD ["/app/appenv/bin/python", "-m", "pynode", "--production", "--host", "0.0.0.0", "--port", "5000"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pynode-flow
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: Python based Node-RED-like visual workflow system
5
5
  Author: olkham
6
6
  License-Expression: MIT
@@ -76,26 +76,41 @@ https://github.com/user-attachments/assets/0b53085a-2cc6-4c26-bd43-e0de1e0716a2
76
76
  - **Fully Extensible**: Third-party nodes can be added without modifying core code
77
77
  - **UI Components**: Nodes can define interactive controls (buttons, toggles, displays) in their cards
78
78
  - **Node-RED Compatible Messages**: Message structure with `payload` and `topic` fields
79
- - **Built-in Nodes**:
80
- - **InjectNode**: Generate messages with configurable payloads
81
- - **FunctionNode**: Execute custom Python code on messages
82
- - **DebugNode**: Display messages in the debug panel
83
- - **ChangeNode**: Modify message properties
84
- - **SwitchNode**: Route messages based on conditions
85
- - **DelayNode**: Delay message delivery
86
- - **GateNode**: Control message flow with real-time toggle
87
- - **LinkNode**: Pass messages between flows via named channels (Link In / Link Out)
88
- - **RateProbeNode**: Monitor message throughput
89
- - **QueueLengthProbeNode**: Monitor queue lengths
90
- - **CounterNode**: Count messages
91
- - **MQTTNode**: MQTT communication
92
- - **MessageWriterNode**: Save message data
93
- - **VideoWriterNode**: Save video output
94
- - **Vision Nodes**: Camera input, YOLO detection, tracking, image processing, display, and file output
79
+ - **Multiple Workspaces**: Several independent flows, each with its own deploy/run state
95
80
  - **REST API**: Complete API for programmatic workflow management
96
81
  - **Export/Import**: Save and load workflows as JSON
97
82
  - **Dynamic Properties**: Node properties and UI components defined in node classes
98
83
 
84
+ ### Built-in Nodes
85
+
86
+ Around 85 node types ship with PyNode, grouped by palette category. Every node
87
+ carries its own documentation in the editor's **Information** panel (ℹ️ tab).
88
+
89
+ | Category | Nodes |
90
+ | --- | --- |
91
+ | **Common** | Inject, Debug, Link In / Link Out (named channels between flows) |
92
+ | **Function** | Function (custom Python), Change, Filter, Delay, Batch, Join, Split, Range, Slider |
93
+ | **Input** | Camera, Frame Source, Video Reader, Image Upload, Message Reader, Omron Camera |
94
+ | **Output** | Image Viewer, Image Writer, Video Writer, Message Writer, Roboflow Upload |
95
+ | **Logic** | Switch (route on conditions), Gate, Sync, Auto Sync |
96
+ | **Network** | MQTT In / Out, UDP In / Out, TCP In / Out, REST Endpoint, Webhook, mDNS Broadcast / Discovery |
97
+ | **Vision** | YOLO, Inference, Tracker, Supervision Tracker, Crop, Slice Image / Slice Collector, Draw Predictions, Confidence Filter, Label Filter, Merge Predictions, Image Format, Qwen3-VL, vLLM |
98
+ | **Analysis** | BBox Metrics, Polygon Metrics, Point in Shape |
99
+ | **OpenCV** | ~28 image-processing nodes: Blur, Threshold, Morphology, Edge Detector, Find Contours, Perspective, Resize, Rotate, Colormap, Histogram, FFT, Template Match, RealSense Depth, and more |
100
+ | **Node Probes** | Rate Probe, Queue Length Probe, Counter |
101
+
102
+ Highlights:
103
+
104
+ - **Slider** — an interactive on-card slider that stamps a live value onto any
105
+ `msg` path; drag it and downstream nodes update immediately.
106
+ - **UDP In / Out, TCP In / Out** — move messages (including video frames)
107
+ between PyNode instances or to anything else that speaks the wire format.
108
+ Their Information panel contains a ready-made **Node-RED flow you can copy
109
+ and import**, pre-set to the node's port.
110
+ - **Video Reader** — video-file playback with on-card transport controls.
111
+ - **Inference** — a multi-backend inference node (ONNX Runtime, Ultralytics)
112
+ that discovers available engines at startup.
113
+
99
114
  ## Quick Start
100
115
 
101
116
  ### Install from PyPI (recommended for users)
@@ -116,7 +131,7 @@ pynode
116
131
  or specify a port:
117
132
 
118
133
  ```bash
119
- pynode --port 500
134
+ pynode --port 8080
120
135
  ```
121
136
 
122
137
  See [INSTALL.md](INSTALL.md) for the full list of extras (`vision`, `mqtt`,
@@ -196,10 +211,11 @@ Navigate to `http://localhost:5000`
196
211
 
197
212
  ### Load an Example Workflow
198
213
 
199
- Ten ready-made learning workflows ship with PyNode — from a two-node hello
214
+ Twelve ready-made learning workflows ship with PyNode — from a two-node hello
200
215
  world up to a camera → YOLO → filter → MQTT alert pipeline (including a "blur
201
- every detected person" privacy flow). Load any of them straight from the
202
- editor: **☰ menu → Examples → pick one**, then press **Deploy**. See
216
+ every detected person" privacy flow, a UDP socket bridge and a slider-driven
217
+ crop). Load any of them straight from the editor: **☰ menu → Examples → pick
218
+ one**, then press **Deploy**. See
203
219
  [pynode/static/examples/README.md](pynode/static/examples/README.md) for the
204
220
  guided tour and per-example prerequisites.
205
221
 
@@ -289,58 +305,53 @@ PyNode is designed to be easily extended with custom nodes:
289
305
  ```text
290
306
  pynode/ # Project root
291
307
  ├── pynode/ # Main package
292
- │ ├── __init__.py
293
308
  │ ├── __main__.py # Entry point for 'python -m pynode'
294
309
  │ ├── _version.py # Version (generated by setuptools_scm at build)
295
310
  │ ├── main.py # CLI application
296
- │ ├── server.py # Flask REST API with SSE support
297
- │ ├── workflow_engine.py # Workflow management
298
- │ ├── models/ # ML model storage
311
+ │ ├── config.py # Data/models directory resolution
312
+ │ ├── server.py # Flask app factory, static routes, auth
313
+ │ ├── node_registry.py # Node type discovery and metadata cache
314
+ │ ├── workflow_engine.py # Message routing and node execution
315
+ │ ├── workflow_manager.py # Multiple workflows, persistence and backups
316
+ │ ├── install_nodes.py # 'pynode-install-nodes' per-node dependency installer
317
+ │ ├── api/ # REST API blueprints
318
+ │ │ ├── nodes.py # Node CRUD, actions, frames, uploads
319
+ │ │ ├── workflows.py # Workflow + multi-workflow endpoints
320
+ │ │ ├── services.py # Shared services (MQTT brokers)
321
+ │ │ ├── sse.py # Server-sent events (debug stream)
322
+ │ │ └── uploads.py
299
323
  │ ├── nodes/ # Node implementations (each in its own folder)
300
- │ │ ├── __init__.py
301
324
  │ │ ├── base_node.py # BaseNode class
325
+ │ │ ├── info.py # Info builder for node help panels
326
+ │ │ ├── messages.py # Message construction helpers
302
327
  │ │ ├── InjectNode/ # Generate messages
303
328
  │ │ ├── FunctionNode/ # Custom Python code
304
- │ │ ├── DebugNode/ # Debug output
305
- │ │ ├── ChangeNode/ # Modify messages
306
- │ │ ├── SwitchNode/ # Route based on conditions
307
- │ │ ├── DelayNode/ # Delay messages
308
- │ │ ├── GateNode/ # Control message flow
309
- │ │ ├── RateProbeNode/ # Monitor throughput
310
- │ │ ├── QueueLengthProbeNode/ # Monitor queue lengths
311
- │ │ ├── CounterNode/ # Count messages
312
- │ │ ├── CameraNode/ # Camera input
329
+ │ │ ├── SocketNode/ # UDP/TCP nodes + Node-RED interop flow
313
330
  │ │ ├── UltralyticsNode/ # YOLO detection
314
- │ │ ├── ImageViewerNode/ # Display images
315
- │ │ ├── ImageWriterNode/ # Save images
316
- │ │ ├── TrackerNode/ # Object tracking
317
- │ │ ├── MQTTNode/ # MQTT communication
318
- │ │ ├── MessageWriterNode/ # Save message data
319
- │ │ ├── VideoWriterNode/ # Save video
320
- │ │ ├── OpenCV/ # OpenCV operations
321
- │ │ └── ... # 30+ other node types
331
+ │ │ ├── InferenceNode/ # Multi-backend inference engines
332
+ │ │ ├── OpenCV/ # ~28 OpenCV operations
333
+ │ │ └── ... # ~50 node folders in total
322
334
  │ └── static/ # Web UI
323
335
  │ ├── index.html
324
336
  │ ├── style.css
325
- │ ├── js/ # JavaScript modules
326
- │ │ ├── nodes.js
327
- │ │ ├── events.js
328
- │ │ ├── connections.js
329
- │ │ ├── debug.js
337
+ │ ├── js/ # JavaScript modules (ES modules, no build step)
338
+ │ │ ├── main.js # Entry point
339
+ │ │ ├── nodes.js # Node rendering
340
+ │ │ ├── properties.js # Properties panel
341
+ │ │ ├── selection.js # Selection + Information panel
330
342
  │ │ └── ...
343
+ │ ├── examples/ # Bundled example workflows + manifest.json
331
344
  │ └── images/ # UI assets
332
- ├── examples/ # Example workflows and tutorials
333
- │ ├── camera_workflow.py
334
- │ ├── camera_yolo_workflow.py
335
- │ └── README.md
336
- ├── readmes/ # Extended documentation
337
- ├── models/ # ML models (YOLO, etc.)
345
+ ├── tests/ # pytest suite
346
+ ├── examples/ # Programmatic (Python API) examples
338
347
  ├── docs/ # Documentation files
339
348
  │ ├── CUSTOM_NODES.md # Guide to creating custom nodes
349
+ │ ├── CREATING_NODES.md # Node authoring reference
340
350
  │ ├── UI_COMPONENTS.md # Guide to node UI components
341
351
  │ └── EXTENSIBILITY.md # Extensibility overview
342
- ├── _backup/ # Workflow backups
343
- ├── setup.py # Package installation
352
+ ├── workflows/ # Persisted workflows (workflow.json + _backups/)
353
+ ├── models/ # Downloaded/exported model weights
354
+ ├── setup.py # Shim; metadata lives in pyproject.toml
344
355
  ├── setup.bat / setup.sh # Setup scripts
345
356
  ├── requirements.txt # Convenience installer (deps live in pyproject.toml)
346
357
  ├── pyproject.toml # Package metadata, dependencies and build config
@@ -349,10 +360,12 @@ pynode/ # Project root
349
360
  ├── docker-compose.yml # Docker compose config
350
361
  ├── Dockerfile # Docker build (CUDA)
351
362
  ├── Dockerfile.cpu # Docker build (CPU only)
352
- ├── README.md
353
- └── workflow.json # Current workflow
363
+ └── README.md
354
364
  ```
355
365
 
366
+ > `workflows/` and `models/` are the defaults for a source checkout; both move
367
+ > with `--data-dir` / `--models-dir` (see above).
368
+
356
369
  ## Creating Custom Nodes
357
370
 
358
371
  PyNode is fully extensible. All node information (visual properties, property schemas, and behavior) is contained within the node class itself. The main application has no hardcoded knowledge of specific node types.
@@ -423,28 +436,59 @@ Messages follow the Node-RED format:
423
436
 
424
437
  ## API Endpoints
425
438
 
439
+ All `/api/` routes require the API key when the server is started with
440
+ `--api-key` (see [Securing PyNode](#securing-pynode)).
441
+
442
+ ### Node types
443
+ - `GET /api/node-types` - Palette metadata for every registered node type
444
+ (properties, colours, info HTML)
445
+
426
446
  ### Nodes
427
447
  - `GET /api/nodes` - List all nodes
428
448
  - `POST /api/nodes` - Create a node
429
449
  - `GET /api/nodes/<id>` - Get node details
430
450
  - `PUT /api/nodes/<id>` - Update node
431
451
  - `DELETE /api/nodes/<id>` - Delete node
432
- - `POST /api/nodes/<id>/<action>` - Trigger node action
452
+ - `POST /api/nodes/<id>/<action>` - Trigger a node action (e.g. inject)
453
+ - `GET|POST /api/nodes/<id>/enabled` - Read or set the node's enabled state
454
+ - `PUT /api/nodes/<id>/position` - Move a node on the canvas
455
+ - `GET /api/nodes/<id>/frame` - Latest frame from an image-producing node
456
+ - `GET /api/nodes/<id>/stream` - MJPEG stream from an image-producing node
457
+ - `GET /api/nodes/<id>/rate` - Throughput for a probe node
433
458
 
434
459
  ### Connections
435
460
  - `POST /api/connections` - Create connection
436
461
  - `DELETE /api/connections` - Delete connection
437
462
 
438
- ### Workflow
463
+ ### Workflow (the active flow)
439
464
  - `GET /api/workflow` - Export workflow
440
465
  - `POST /api/workflow` - Import workflow
441
- - `POST /api/workflow/start` - Start workflow
442
- - `POST /api/workflow/stop` - Stop workflow
466
+ - `POST /api/workflow/deploy-changes` - Deploy (optionally only modified nodes)
467
+ - `POST /api/workflow/restart` - Restart the deployed workflow
468
+ - `POST /api/workflow/stop` - Stop the deployed workflow
469
+ - `POST /api/workflow/save` - Persist to disk
470
+ - `GET /api/workflow/deployed` - The currently deployed definition
443
471
  - `GET /api/workflow/stats` - Get statistics
444
472
 
473
+ ### Workflows (multiple flows)
474
+ - `GET /api/workflows` - List workflows
475
+ - `POST /api/workflows` - Create a workflow
476
+ - `PUT /api/workflows/<id>` - Rename/update a workflow
477
+ - `DELETE /api/workflows/<id>` - Delete a workflow
478
+ - `PUT /api/workflows/active` - Switch the active workflow
479
+
445
480
  ### Debug
446
481
  - `GET /api/nodes/<id>/debug` - Get debug messages
447
482
  - `DELETE /api/nodes/<id>/debug` - Clear debug messages
483
+ - `GET /api/debug/stream` - Server-sent event stream of debug messages
484
+
485
+ ### Other
486
+ - `GET /api/version` - Running PyNode version
487
+ - `GET /api/link-channels` - Known Link In/Out channel names
488
+ - `GET|POST /api/services/mqtt`, `GET|PUT|DELETE /api/services/mqtt/<id>`,
489
+ `POST /api/services/mqtt/test` - Shared MQTT broker definitions
490
+ - `POST /api/upload/file`, `POST /api/nodes/<id>/upload_image`,
491
+ `POST /api/nodes/<id>/upload_video` - File uploads
448
492
 
449
493
  ## Example Programmatic Usage
450
494
 
@@ -479,12 +523,16 @@ print(messages)
479
523
 
480
524
  1. **Add Nodes**: Drag nodes from the palette onto the canvas
481
525
  2. **Connect Nodes**: Click an output port and drag to an input port
482
- 3. **Configure Nodes**: Click a node to show its properties panel
526
+ 3. **Configure Nodes**: Click a node to show its properties panel; the **ℹ️
527
+ Information** tab in the right sidebar documents the selected node
483
528
  4. **Test Workflow**:
484
- - Click **Start** to activate the workflow
529
+ - Click **Deploy** to activate the workflow (the ▼ menu offers Deploy
530
+ Modified / Full, Restart and Stop)
485
531
  - Use the **Inject** button on inject nodes to send messages
486
- - View output in the debug panel at the bottom
487
- 5. **Save/Load**: Use Export/Import buttons to save workflows
532
+ - View output in the **🐛 Debug** tab of the right sidebar
533
+ 5. **Multiple Flows**: Use the **+** tab to add another workspace; each flow
534
+ deploys and runs independently
535
+ 6. **Save/Load**: Use ☰ menu → Export / Import to save workflows as JSON
488
536
 
489
537
  ## Extending the System
490
538
 
@@ -528,7 +576,7 @@ Nodes can:
528
576
  - ⬜ SAM3 Node
529
577
  - ✅ REST Endpoint Node
530
578
  - ✅ Webhook Node
531
- - ⬜ UDP/TCP Node
579
+ - ✅ UDP/TCP Node
532
580
 
533
581
  ### Example Flow Documentation Needed
534
582
  - ⬜ Bird seed level monitor
@@ -541,9 +589,64 @@ Nodes can:
541
589
  - ✅ YOLO: Add custom model support
542
590
  - ✅ YOLO: Add custom target HW string
543
591
  - ⬜ Roboflow: RF-DETR
544
- - ⬜ Roboflow: Upload images
592
+ - ✅ Roboflow: Upload images
545
593
  - ⬜ DeepSort: Add option to use a different feature extractor model
546
594
 
595
+ ## Changelog
596
+
597
+ Versions follow the `vX.Y.Z` git tags. The running build is shown next to the
598
+ title in the editor and returned by `GET /api/version`.
599
+
600
+ ### 0.2.4
601
+
602
+ **New nodes**
603
+
604
+ - **UDP In / Out** and **TCP In / Out** (`SocketNode`) — move messages,
605
+ including video frames, between PyNode instances or any program that speaks
606
+ the wire format. UDP uses a chunked binary protocol (PNB1) for large
607
+ payloads; TCP uses newline-delimited JSON. Each node's Information panel
608
+ carries a **copy-and-import Node-RED flow** pre-set to that node's port,
609
+ with a worked interop example and a standalone `udp_probe.py` diagnostic in
610
+ `pynode/nodes/SocketNode/interop/`.
611
+ - **Slider** (`ControlSliderNode`) — an interactive on-card slider that stamps
612
+ a live value onto any `msg` path, with *Send on Change* to emit while
613
+ dragging even with no input wired.
614
+
615
+ **Improvements**
616
+
617
+ - **Range** node: configurable output format.
618
+ - **Crop** node: custom-path bbox source with format and coordinate-space
619
+ selectors.
620
+ - **Image Upload** node: *Repeat Send* re-emits the image at a set rate (and
621
+ the pacing bug that capped 30fps at ~11fps is fixed).
622
+ - **Function** node: built-in code templates.
623
+ - Wider type hints, payload validation and error handling across nodes.
624
+ - Docker: CUDA 12.6 runtime base image, version reporting, and better CUDA
625
+ detection in `setup.sh`.
626
+ - Two new bundled examples: **UDP Socket Bridge** and **Slider-Driven Crop**.
627
+
628
+ ### 0.2.3
629
+
630
+ - Performance: fewer message copies for nodes with a single output.
631
+ - YOLO node: fixed model storage locations (weights now land in the models
632
+ directory rather than the working directory).
633
+
634
+ ### 0.2.2
635
+
636
+ - **Link In / Link Out** nodes for passing messages between flows by channel
637
+ name.
638
+ - Per-flow debug panel and per-flow run state.
639
+ - Camera and rate-probe fixes; MQTT improvements.
640
+ - The first bundled example workflows and general UI/UX fixes.
641
+
642
+ ### 0.2.1
643
+
644
+ - Removed the end-of-life Geti node and its SDK dependency.
645
+
646
+ ### 0.2.0
647
+
648
+ - First PyPI release as **`pynode-flow`**.
649
+
547
650
  ## License
548
651
 
549
652
  MIT License - Feel free to use and modify.