rocket-welder-sdk 1.1.32__py3-none-any.whl → 1.1.33__py3-none-any.whl

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.
@@ -0,0 +1,339 @@
1
+ """Unix Domain Socket transport with length-prefix framing.
2
+
3
+ Frame format: [Length: 4 bytes LE][Frame Data: N bytes]
4
+ Unix Domain Sockets provide high-performance IPC on Linux/macOS.
5
+ """
6
+
7
+ import asyncio
8
+ import contextlib
9
+ import os
10
+ import socket
11
+ import struct
12
+ from typing import Optional
13
+
14
+ from .frame_sink import IFrameSink
15
+ from .frame_source import IFrameSource
16
+
17
+
18
+ class UnixSocketFrameSink(IFrameSink):
19
+ """
20
+ Frame sink that writes to a Unix Domain Socket with length-prefix framing.
21
+
22
+ Each frame is prefixed with a 4-byte little-endian length header.
23
+ """
24
+
25
+ def __init__(self, sock: socket.socket, leave_open: bool = False):
26
+ """
27
+ Create a Unix socket frame sink.
28
+
29
+ Args:
30
+ sock: Connected Unix domain socket
31
+ leave_open: If True, doesn't close socket on close
32
+ """
33
+ if sock.family != socket.AF_UNIX:
34
+ raise ValueError("Socket must be a Unix domain socket")
35
+
36
+ self._socket = sock
37
+ self._leave_open = leave_open
38
+ self._closed = False
39
+
40
+ @classmethod
41
+ def connect(cls, socket_path: str) -> "UnixSocketFrameSink":
42
+ """
43
+ Connect to a Unix socket path and create a frame sink.
44
+
45
+ Args:
46
+ socket_path: Path to Unix socket file
47
+
48
+ Returns:
49
+ Connected frame sink
50
+ """
51
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
52
+ sock.connect(socket_path)
53
+ return cls(sock, leave_open=False)
54
+
55
+ @classmethod
56
+ async def connect_async(cls, socket_path: str) -> "UnixSocketFrameSink":
57
+ """
58
+ Connect to a Unix socket path asynchronously and create a frame sink.
59
+
60
+ Args:
61
+ socket_path: Path to Unix socket file
62
+
63
+ Returns:
64
+ Connected frame sink
65
+ """
66
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
67
+ sock.setblocking(False)
68
+ loop = asyncio.get_event_loop()
69
+ await loop.sock_connect(sock, socket_path)
70
+ return cls(sock, leave_open=False)
71
+
72
+ def write_frame(self, frame_data: bytes) -> None:
73
+ """Write frame with 4-byte length prefix to Unix socket."""
74
+ if self._closed:
75
+ raise ValueError("Cannot write to closed sink")
76
+
77
+ # Write 4-byte length prefix (little-endian)
78
+ length_prefix = struct.pack("<I", len(frame_data))
79
+ self._socket.sendall(length_prefix)
80
+
81
+ # Write frame data
82
+ self._socket.sendall(frame_data)
83
+
84
+ async def write_frame_async(self, frame_data: bytes) -> None:
85
+ """Write frame asynchronously."""
86
+ if self._closed:
87
+ raise ValueError("Cannot write to closed sink")
88
+
89
+ loop = asyncio.get_event_loop()
90
+
91
+ # Write 4-byte length prefix (little-endian)
92
+ length_prefix = struct.pack("<I", len(frame_data))
93
+ await loop.sock_sendall(self._socket, length_prefix)
94
+
95
+ # Write frame data
96
+ await loop.sock_sendall(self._socket, frame_data)
97
+
98
+ def flush(self) -> None:
99
+ """Flush is a no-op for Unix sockets (data sent immediately)."""
100
+ pass
101
+
102
+ async def flush_async(self) -> None:
103
+ """Flush asynchronously is a no-op for Unix sockets."""
104
+ pass
105
+
106
+ def close(self) -> None:
107
+ """Close the Unix socket sink."""
108
+ if self._closed:
109
+ return
110
+ self._closed = True
111
+ if not self._leave_open:
112
+ with contextlib.suppress(OSError):
113
+ self._socket.shutdown(socket.SHUT_WR)
114
+ self._socket.close()
115
+
116
+ async def close_async(self) -> None:
117
+ """Close the Unix socket sink asynchronously."""
118
+ self.close()
119
+
120
+
121
+ class UnixSocketFrameSource(IFrameSource):
122
+ """
123
+ Frame source that reads from a Unix Domain Socket with length-prefix framing.
124
+
125
+ Each frame is prefixed with a 4-byte little-endian length header.
126
+ """
127
+
128
+ # Maximum frame size (100 MB)
129
+ MAX_FRAME_SIZE = 100 * 1024 * 1024
130
+
131
+ def __init__(self, sock: socket.socket, leave_open: bool = False):
132
+ """
133
+ Create a Unix socket frame source.
134
+
135
+ Args:
136
+ sock: Connected Unix domain socket
137
+ leave_open: If True, doesn't close socket on close
138
+ """
139
+ if sock.family != socket.AF_UNIX:
140
+ raise ValueError("Socket must be a Unix domain socket")
141
+
142
+ self._socket = sock
143
+ self._leave_open = leave_open
144
+ self._closed = False
145
+ self._end_of_stream = False
146
+
147
+ @classmethod
148
+ def connect(cls, socket_path: str) -> "UnixSocketFrameSource":
149
+ """
150
+ Connect to a Unix socket path and create a frame source.
151
+
152
+ Args:
153
+ socket_path: Path to Unix socket file
154
+
155
+ Returns:
156
+ Connected frame source
157
+ """
158
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
159
+ sock.connect(socket_path)
160
+ return cls(sock, leave_open=False)
161
+
162
+ @classmethod
163
+ async def connect_async(cls, socket_path: str) -> "UnixSocketFrameSource":
164
+ """
165
+ Connect to a Unix socket path asynchronously and create a frame source.
166
+
167
+ Args:
168
+ socket_path: Path to Unix socket file
169
+
170
+ Returns:
171
+ Connected frame source
172
+ """
173
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
174
+ sock.setblocking(False)
175
+ loop = asyncio.get_event_loop()
176
+ await loop.sock_connect(sock, socket_path)
177
+ return cls(sock, leave_open=False)
178
+
179
+ @property
180
+ def has_more_frames(self) -> bool:
181
+ """Check if more frames available."""
182
+ return not self._closed and not self._end_of_stream
183
+
184
+ def _recv_exactly(self, n: int) -> Optional[bytes]:
185
+ """Receive exactly n bytes from socket."""
186
+ data = b""
187
+ while len(data) < n:
188
+ chunk = self._socket.recv(n - len(data))
189
+ if not chunk:
190
+ return data if data else None
191
+ data += chunk
192
+ return data
193
+
194
+ async def _recv_exactly_async(self, n: int) -> Optional[bytes]:
195
+ """Receive exactly n bytes from socket asynchronously."""
196
+ loop = asyncio.get_event_loop()
197
+ data = b""
198
+ while len(data) < n:
199
+ chunk = await loop.sock_recv(self._socket, n - len(data))
200
+ if not chunk:
201
+ return data if data else None
202
+ data += chunk
203
+ return data
204
+
205
+ def read_frame(self) -> Optional[bytes]:
206
+ """Read frame with 4-byte length prefix from Unix socket."""
207
+ if self._closed or self._end_of_stream:
208
+ return None
209
+
210
+ # Read 4-byte length prefix
211
+ length_data = self._recv_exactly(4)
212
+ if length_data is None or len(length_data) < 4:
213
+ self._end_of_stream = True
214
+ return None
215
+
216
+ frame_length = struct.unpack("<I", length_data)[0]
217
+
218
+ if frame_length == 0:
219
+ return b""
220
+
221
+ if frame_length > self.MAX_FRAME_SIZE:
222
+ raise ValueError(f"Frame length {frame_length} exceeds maximum {self.MAX_FRAME_SIZE}")
223
+
224
+ # Read frame data
225
+ frame_data = self._recv_exactly(frame_length)
226
+ if frame_data is None or len(frame_data) < frame_length:
227
+ self._end_of_stream = True
228
+ raise ValueError(
229
+ f"Incomplete frame data: expected {frame_length}, "
230
+ f"got {len(frame_data) if frame_data else 0}"
231
+ )
232
+
233
+ return frame_data
234
+
235
+ async def read_frame_async(self) -> Optional[bytes]:
236
+ """Read frame asynchronously."""
237
+ if self._closed or self._end_of_stream:
238
+ return None
239
+
240
+ # Read 4-byte length prefix
241
+ length_data = await self._recv_exactly_async(4)
242
+ if length_data is None or len(length_data) < 4:
243
+ self._end_of_stream = True
244
+ return None
245
+
246
+ frame_length = struct.unpack("<I", length_data)[0]
247
+
248
+ if frame_length == 0:
249
+ return b""
250
+
251
+ if frame_length > self.MAX_FRAME_SIZE:
252
+ raise ValueError(f"Frame length {frame_length} exceeds maximum {self.MAX_FRAME_SIZE}")
253
+
254
+ # Read frame data
255
+ frame_data = await self._recv_exactly_async(frame_length)
256
+ if frame_data is None or len(frame_data) < frame_length:
257
+ self._end_of_stream = True
258
+ raise ValueError(
259
+ f"Incomplete frame data: expected {frame_length}, "
260
+ f"got {len(frame_data) if frame_data else 0}"
261
+ )
262
+
263
+ return frame_data
264
+
265
+ def close(self) -> None:
266
+ """Close the Unix socket source."""
267
+ if self._closed:
268
+ return
269
+ self._closed = True
270
+ if not self._leave_open:
271
+ with contextlib.suppress(OSError):
272
+ self._socket.shutdown(socket.SHUT_RD)
273
+ self._socket.close()
274
+
275
+ async def close_async(self) -> None:
276
+ """Close the Unix socket source asynchronously."""
277
+ self.close()
278
+
279
+
280
+ class UnixSocketServer:
281
+ """
282
+ Helper class to create a Unix socket server that accepts connections.
283
+ """
284
+
285
+ def __init__(self, socket_path: str):
286
+ """
287
+ Create a Unix socket server.
288
+
289
+ Args:
290
+ socket_path: Path to Unix socket file
291
+ """
292
+ self._socket_path = socket_path
293
+ self._socket: Optional[socket.socket] = None
294
+
295
+ def start(self) -> None:
296
+ """Start listening on the Unix socket."""
297
+ # Remove existing socket file if present
298
+ if os.path.exists(self._socket_path):
299
+ os.unlink(self._socket_path)
300
+
301
+ self._socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
302
+ self._socket.bind(self._socket_path)
303
+ self._socket.listen(1)
304
+
305
+ def accept(self) -> socket.socket:
306
+ """Accept a connection (blocking)."""
307
+ if self._socket is None:
308
+ raise ValueError("Server not started")
309
+
310
+ client, _ = self._socket.accept()
311
+ return client
312
+
313
+ async def accept_async(self) -> socket.socket:
314
+ """Accept a connection asynchronously."""
315
+ if self._socket is None:
316
+ raise ValueError("Server not started")
317
+
318
+ loop = asyncio.get_event_loop()
319
+ self._socket.setblocking(False)
320
+ client, _ = await loop.sock_accept(self._socket)
321
+ return client
322
+
323
+ def stop(self) -> None:
324
+ """Stop the server and clean up the socket file."""
325
+ if self._socket:
326
+ self._socket.close()
327
+ self._socket = None
328
+
329
+ if os.path.exists(self._socket_path):
330
+ os.unlink(self._socket_path)
331
+
332
+ def __enter__(self) -> "UnixSocketServer":
333
+ """Context manager entry."""
334
+ self.start()
335
+ return self
336
+
337
+ def __exit__(self, *args: object) -> None:
338
+ """Context manager exit."""
339
+ self.stop()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rocket-welder-sdk
3
- Version: 1.1.32
3
+ Version: 1.1.33
4
4
  Summary: High-performance video streaming SDK for RocketWelder services using ZeroBuffer IPC
5
5
  Home-page: https://github.com/modelingevolution/rocket-welder-sdk
6
6
  Author: ModelingEvolution
@@ -31,6 +31,9 @@ Requires-Dist: opencv-python>=4.5.0
31
31
  Requires-Dist: zerobuffer-ipc>=1.1.17
32
32
  Requires-Dist: pydantic>=2.5.0
33
33
  Requires-Dist: py-micro-plumberd>=0.1.8
34
+ Requires-Dist: typing-extensions>=4.0.0
35
+ Provides-Extra: nng
36
+ Requires-Dist: pynng>=0.7.2; extra == "nng"
34
37
  Provides-Extra: dev
35
38
  Requires-Dist: pytest>=7.0; extra == "dev"
36
39
  Requires-Dist: pytest-cov>=4.0; extra == "dev"
@@ -39,6 +42,7 @@ Requires-Dist: black>=22.0; extra == "dev"
39
42
  Requires-Dist: mypy>=1.0; extra == "dev"
40
43
  Requires-Dist: ruff>=0.1.0; extra == "dev"
41
44
  Requires-Dist: types-setuptools; extra == "dev"
45
+ Requires-Dist: pynng>=0.7.2; extra == "dev"
42
46
  Dynamic: author
43
47
  Dynamic: home-page
44
48
  Dynamic: requires-python
@@ -169,7 +173,7 @@ Start by testing your container locally before deploying to Neuron:
169
173
 
170
174
  ```bash
171
175
  # Build your container
172
- docker build -t my-ai-app:v1 -f examples/python/Dockerfile .
176
+ docker build -t my-ai-app:v1 -f python/examples/Dockerfile .
173
177
 
174
178
  # Test with a video file
175
179
  docker run --rm \
@@ -223,6 +227,15 @@ docker run --rm \
223
227
  my-ai-app:v1
224
228
  ```
225
229
 
230
+ You can also see preview in your terminal.
231
+ ```bash
232
+ docker run --rm \
233
+ -e CONNECTION_STRING="mjpeg+tcp://<neuron-ip>:<tcp-server-sink-port>?preview=true" \
234
+ -e DISPLAY=$DISPLAY \
235
+ -v /tmp/.X11-unix:/tmp/.X11-unix \
236
+ --network host my-ai-app:v1
237
+ ```
238
+
226
239
  This allows you to:
227
240
  - Test your AI processing with real camera feeds
228
241
  - Debug frame processing logic
@@ -0,0 +1,37 @@
1
+ rocket_welder_sdk/__init__.py,sha256=nxaj88kgewtAbyTw7wLGvgjIkY9VOYUuBVtzw1MMW7c,2033
2
+ rocket_welder_sdk/bytes_size.py,sha256=Myl29-wyWCIYdbMmgaxXebT8Dz8_Fwcr3fnfaNW81P0,7463
3
+ rocket_welder_sdk/connection_string.py,sha256=NIC6OiOXF-DeBFCWzgMFOWsenrSS45hY81j_HLMSpgo,9974
4
+ rocket_welder_sdk/controllers.py,sha256=X7D7OyiuHhGTrmrSNG2Tljsy1_4PGRObytB4dLyvRrg,32600
5
+ rocket_welder_sdk/frame_metadata.py,sha256=8AbCgSQ17QeCHSzdQiSN0E1KxE5rrNpOlTYZ2em8kes,3903
6
+ rocket_welder_sdk/gst_metadata.py,sha256=jEQvZX4BdR6OR3lqp12PV-HEXZhcxfiS010diA2CbMM,14213
7
+ rocket_welder_sdk/keypoints_protocol.py,sha256=NKiSPrevWG4_RrD6jtFxPjwftlaPWe1CqoFVKRMwp4k,21858
8
+ rocket_welder_sdk/opencv_controller.py,sha256=MDM6_yFBB9BaMa5jnZRqw7xZZB-WuLr7EPrrfHQ2DK4,9905
9
+ rocket_welder_sdk/periodic_timer.py,sha256=hnObybmrnf3J47QrNKJhYAytLKwria016123NvPRfQ0,9369
10
+ rocket_welder_sdk/py.typed,sha256=0cXFZXmes4Y-vnl4lO3HtyyyWaFNw85B7tJdFeCtHDc,67
11
+ rocket_welder_sdk/rocket_welder_client.py,sha256=mguSN8E4nV8JsNOJvhHFNv7yIz7Ppb25_-9m68d6iD4,15244
12
+ rocket_welder_sdk/segmentation_result.py,sha256=q3n8m1sooPcattfXisL9em5gZKbEZ1ueGqeXfOrdaHY,13417
13
+ rocket_welder_sdk/external_controls/__init__.py,sha256=ldOLGhLLS5BQL8m4VKFYV0SvsNNlV2tghlc7rkqadU8,699
14
+ rocket_welder_sdk/external_controls/contracts.py,sha256=3DU6pdpteN50gF2fsS7C2279dGjDa0tZLrLntkBa2LM,2607
15
+ rocket_welder_sdk/external_controls/contracts_old.py,sha256=XWriuXJZu5caTSS0bcTIOZcKnj-IRCm96voA4gqLBfU,2980
16
+ rocket_welder_sdk/high_level/__init__.py,sha256=5oTCBL2qMGonCytHDckmX1b1U97-7Xb8Bg9Z70nR7fc,1759
17
+ rocket_welder_sdk/high_level/connection_strings.py,sha256=4undnkbWZ837vY-o6ybIj1827F2VlUbCJX4m3Filz-s,10469
18
+ rocket_welder_sdk/high_level/data_context.py,sha256=Pmwsl9MgBfKA9BqmBJmAVRvnaqPdjnq2rZEA_pzzYsw,4585
19
+ rocket_welder_sdk/high_level/schema.py,sha256=UlefNAV2UL9eO_Th2q19a7tf_eoaeLE92fYZ5LNq7-M,4849
20
+ rocket_welder_sdk/high_level/transport_protocol.py,sha256=lvLVHikW_MNZhXqaXlyXscT68OklyMbGs8DnDEMCtgE,4515
21
+ rocket_welder_sdk/transport/__init__.py,sha256=L-wl0YWhVbSrof94Zt4g7SIM-wTPzujdVOsEfJHiJT4,978
22
+ rocket_welder_sdk/transport/frame_sink.py,sha256=16dUefZF1QJv62Ig0ezPR6nEho_7A3WJu4M9_PPMqJM,2164
23
+ rocket_welder_sdk/transport/frame_source.py,sha256=G1rBAQS1AgOOdtASB0_CYon8g20hUGXpP2exCp5hlhk,2169
24
+ rocket_welder_sdk/transport/nng_transport.py,sha256=o-qgcmHCGnwtdPe-mwwrC-a9H0rgS-VdH2QvU-6kFlI,5838
25
+ rocket_welder_sdk/transport/stream_transport.py,sha256=FhxFlZT-CTo6aPq6VclM1A_ecqfeHcwR4Ty1vmlt3W0,5886
26
+ rocket_welder_sdk/transport/tcp_transport.py,sha256=Tui6nKgu50C1KV_UDANQKgVK3M52XN7zrkZTcWIpGmY,4745
27
+ rocket_welder_sdk/transport/unix_socket_transport.py,sha256=6N_V5nsoxlI5-oR1watCxU-jWVbXw4OQGhcTvCkKZh4,10612
28
+ rocket_welder_sdk/ui/__init__.py,sha256=5-fCkv3vG0VVVZkselrifkMmx8d51EJp3nNT9mt4JK4,886
29
+ rocket_welder_sdk/ui/controls.py,sha256=cRBxdUDWMOYlCxtw1PV11fTlte6qLxSfCGpr9bQiLcE,11162
30
+ rocket_welder_sdk/ui/icons.py,sha256=DcDklZkPmiEzlOD4IR7VTJOtGPCuuh_OM_WN7ScghWE,8934592
31
+ rocket_welder_sdk/ui/ui_events_projection.py,sha256=siiNhjLEBOPfTKw1ZhOPGkwIN5rLDH7V9VCZTNrhEtQ,7836
32
+ rocket_welder_sdk/ui/ui_service.py,sha256=uRdpyJGoCQmtOli_HKSrxLwhZYG-XRuHIYdkmFz1zNk,12026
33
+ rocket_welder_sdk/ui/value_types.py,sha256=f7OA_9zgXEDPoITc8v8SfAR23I4XeFhE3E2_GcAbR6k,1616
34
+ rocket_welder_sdk-1.1.33.dist-info/METADATA,sha256=WK6edMkZs55WIe1AOJjECFfHB2SmTMS31IpschstdpE,24847
35
+ rocket_welder_sdk-1.1.33.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
+ rocket_welder_sdk-1.1.33.dist-info/top_level.txt,sha256=2iZvBjnwVCUW-uDE23-eJld5PZ9-mlPI69QiXM5IrTA,18
37
+ rocket_welder_sdk-1.1.33.dist-info/RECORD,,
@@ -1,22 +0,0 @@
1
- rocket_welder_sdk/__init__.py,sha256=tL2NPprMSh19MDUtLsk1YByB0YTLp6nNAkWv2fxNKuM,2001
2
- rocket_welder_sdk/bytes_size.py,sha256=Myl29-wyWCIYdbMmgaxXebT8Dz8_Fwcr3fnfaNW81P0,7463
3
- rocket_welder_sdk/connection_string.py,sha256=NIC6OiOXF-DeBFCWzgMFOWsenrSS45hY81j_HLMSpgo,9974
4
- rocket_welder_sdk/controllers.py,sha256=iegNko2F22jYQgJBHr_fTtkmCf7pCnjgeVXjwqt9j88,31028
5
- rocket_welder_sdk/gst_metadata.py,sha256=jEQvZX4BdR6OR3lqp12PV-HEXZhcxfiS010diA2CbMM,14213
6
- rocket_welder_sdk/opencv_controller.py,sha256=MDM6_yFBB9BaMa5jnZRqw7xZZB-WuLr7EPrrfHQ2DK4,9905
7
- rocket_welder_sdk/periodic_timer.py,sha256=hnObybmrnf3J47QrNKJhYAytLKwria016123NvPRfQ0,9369
8
- rocket_welder_sdk/py.typed,sha256=0cXFZXmes4Y-vnl4lO3HtyyyWaFNw85B7tJdFeCtHDc,67
9
- rocket_welder_sdk/rocket_welder_client.py,sha256=u2XYY1RVyP2rKu2zUZpObKVa4nlTKDUNI0YlFXt8ePE,14401
10
- rocket_welder_sdk/external_controls/__init__.py,sha256=ldOLGhLLS5BQL8m4VKFYV0SvsNNlV2tghlc7rkqadU8,699
11
- rocket_welder_sdk/external_controls/contracts.py,sha256=3DU6pdpteN50gF2fsS7C2279dGjDa0tZLrLntkBa2LM,2607
12
- rocket_welder_sdk/external_controls/contracts_old.py,sha256=XWriuXJZu5caTSS0bcTIOZcKnj-IRCm96voA4gqLBfU,2980
13
- rocket_welder_sdk/ui/__init__.py,sha256=5-fCkv3vG0VVVZkselrifkMmx8d51EJp3nNT9mt4JK4,886
14
- rocket_welder_sdk/ui/controls.py,sha256=cRBxdUDWMOYlCxtw1PV11fTlte6qLxSfCGpr9bQiLcE,11162
15
- rocket_welder_sdk/ui/icons.py,sha256=DcDklZkPmiEzlOD4IR7VTJOtGPCuuh_OM_WN7ScghWE,8934592
16
- rocket_welder_sdk/ui/ui_events_projection.py,sha256=siiNhjLEBOPfTKw1ZhOPGkwIN5rLDH7V9VCZTNrhEtQ,7836
17
- rocket_welder_sdk/ui/ui_service.py,sha256=uRdpyJGoCQmtOli_HKSrxLwhZYG-XRuHIYdkmFz1zNk,12026
18
- rocket_welder_sdk/ui/value_types.py,sha256=f7OA_9zgXEDPoITc8v8SfAR23I4XeFhE3E2_GcAbR6k,1616
19
- rocket_welder_sdk-1.1.32.dist-info/METADATA,sha256=nPxntb-mslyKp43lLBFHIixy7OAuBA4GpcQ9_hFlgXQ,24438
20
- rocket_welder_sdk-1.1.32.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
- rocket_welder_sdk-1.1.32.dist-info/top_level.txt,sha256=2iZvBjnwVCUW-uDE23-eJld5PZ9-mlPI69QiXM5IrTA,18
22
- rocket_welder_sdk-1.1.32.dist-info/RECORD,,