replx 1.6.2__tar.gz → 1.6.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. {replx-1.6.2/replx.egg-info → replx-1.6.4}/PKG-INFO +1 -1
  2. {replx-1.6.2 → replx-1.6.4}/replx/__init__.py +1 -1
  3. {replx-1.6.2 → replx-1.6.4}/replx/cli/agent/server/core.py +20 -30
  4. {replx-1.6.2 → replx-1.6.4}/replx/cli/agent/server/handlers/exec.py +2 -3
  5. {replx-1.6.2 → replx-1.6.4}/replx/cli/agent/server/handlers/transfer.py +20 -20
  6. {replx-1.6.2 → replx-1.6.4/replx.egg-info}/PKG-INFO +1 -1
  7. {replx-1.6.2 → replx-1.6.4}/LICENSE +0 -0
  8. {replx-1.6.2 → replx-1.6.4}/README.md +0 -0
  9. {replx-1.6.2 → replx-1.6.4}/pyproject.toml +0 -0
  10. {replx-1.6.2 → replx-1.6.4}/replx/cli/__init__.py +0 -0
  11. {replx-1.6.2 → replx-1.6.4}/replx/cli/agent/__init__.py +0 -0
  12. {replx-1.6.2 → replx-1.6.4}/replx/cli/agent/client/__init__.py +0 -0
  13. {replx-1.6.2 → replx-1.6.4}/replx/cli/agent/client/core.py +0 -0
  14. {replx-1.6.2 → replx-1.6.4}/replx/cli/agent/client/session.py +0 -0
  15. {replx-1.6.2 → replx-1.6.4}/replx/cli/agent/protocol.py +0 -0
  16. {replx-1.6.2 → replx-1.6.4}/replx/cli/agent/server/__init__.py +0 -0
  17. {replx-1.6.2 → replx-1.6.4}/replx/cli/agent/server/__main__.py +0 -0
  18. {replx-1.6.2 → replx-1.6.4}/replx/cli/agent/server/command_dispatcher.py +0 -0
  19. {replx-1.6.2 → replx-1.6.4}/replx/cli/agent/server/connection_manager.py +0 -0
  20. {replx-1.6.2 → replx-1.6.4}/replx/cli/agent/server/handlers/__init__.py +0 -0
  21. {replx-1.6.2 → replx-1.6.4}/replx/cli/agent/server/handlers/filesystem.py +0 -0
  22. {replx-1.6.2 → replx-1.6.4}/replx/cli/agent/server/handlers/repl.py +0 -0
  23. {replx-1.6.2 → replx-1.6.4}/replx/cli/agent/server/handlers/session.py +0 -0
  24. {replx-1.6.2 → replx-1.6.4}/replx/cli/agent/server/session_manager.py +0 -0
  25. {replx-1.6.2 → replx-1.6.4}/replx/cli/app.py +0 -0
  26. {replx-1.6.2 → replx-1.6.4}/replx/cli/commands/__init__.py +0 -0
  27. {replx-1.6.2 → replx-1.6.4}/replx/cli/commands/device.py +0 -0
  28. {replx-1.6.2 → replx-1.6.4}/replx/cli/commands/exec.py +0 -0
  29. {replx-1.6.2 → replx-1.6.4}/replx/cli/commands/file.py +0 -0
  30. {replx-1.6.2 → replx-1.6.4}/replx/cli/commands/firmware.py +0 -0
  31. {replx-1.6.2 → replx-1.6.4}/replx/cli/commands/package.py +0 -0
  32. {replx-1.6.2 → replx-1.6.4}/replx/cli/commands/utility.py +0 -0
  33. {replx-1.6.2 → replx-1.6.4}/replx/cli/config.py +0 -0
  34. {replx-1.6.2 → replx-1.6.4}/replx/cli/connection.py +0 -0
  35. {replx-1.6.2 → replx-1.6.4}/replx/cli/helpers/__init__.py +0 -0
  36. {replx-1.6.2 → replx-1.6.4}/replx/cli/helpers/compiler.py +0 -0
  37. {replx-1.6.2 → replx-1.6.4}/replx/cli/helpers/environment.py +0 -0
  38. {replx-1.6.2 → replx-1.6.4}/replx/cli/helpers/output.py +0 -0
  39. {replx-1.6.2 → replx-1.6.4}/replx/cli/helpers/registry.py +0 -0
  40. {replx-1.6.2 → replx-1.6.4}/replx/cli/helpers/scanner.py +0 -0
  41. {replx-1.6.2 → replx-1.6.4}/replx/cli/helpers/store.py +0 -0
  42. {replx-1.6.2 → replx-1.6.4}/replx/cli/helpers/updater.py +0 -0
  43. {replx-1.6.2 → replx-1.6.4}/replx/commands.py +0 -0
  44. {replx-1.6.2 → replx-1.6.4}/replx/protocol/__init__.py +0 -0
  45. {replx-1.6.2 → replx-1.6.4}/replx/protocol/repl.py +0 -0
  46. {replx-1.6.2 → replx-1.6.4}/replx/protocol/storage.py +0 -0
  47. {replx-1.6.2 → replx-1.6.4}/replx/terminal.py +0 -0
  48. {replx-1.6.2 → replx-1.6.4}/replx/transport/__init__.py +0 -0
  49. {replx-1.6.2 → replx-1.6.4}/replx/transport/base.py +0 -0
  50. {replx-1.6.2 → replx-1.6.4}/replx/transport/serial.py +0 -0
  51. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/_thread.pyi +0 -0
  52. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/aioble/__init__.pyi +0 -0
  53. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/array.pyi +0 -0
  54. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/asyncio/__init__.pyi +0 -0
  55. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/binascii.pyi +0 -0
  56. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/bluetooth.pyi +0 -0
  57. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/builtins.pyi +0 -0
  58. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/cmath.pyi +0 -0
  59. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/collections.pyi +0 -0
  60. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/cryptolib.pyi +0 -0
  61. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/deflate.pyi +0 -0
  62. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/errno.pyi +0 -0
  63. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/framebuf.pyi +0 -0
  64. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/gc.pyi +0 -0
  65. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/hashlib.pyi +0 -0
  66. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/heapq.pyi +0 -0
  67. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/io.pyi +0 -0
  68. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/json.pyi +0 -0
  69. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/lwip.pyi +0 -0
  70. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/machine.pyi +0 -0
  71. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/math.pyi +0 -0
  72. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/micropython.pyi +0 -0
  73. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/mip/__init__.pyi +0 -0
  74. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/network.pyi +0 -0
  75. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/ntptime.pyi +0 -0
  76. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/os.pyi +0 -0
  77. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/platform.pyi +0 -0
  78. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/random.pyi +0 -0
  79. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/re.pyi +0 -0
  80. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/requests/__init__.pyi +0 -0
  81. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/select.pyi +0 -0
  82. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/socket.pyi +0 -0
  83. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/ssl.pyi +0 -0
  84. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/struct.pyi +0 -0
  85. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/sys.pyi +0 -0
  86. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/time.pyi +0 -0
  87. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/tls.pyi +0 -0
  88. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/uasyncio.pyi +0 -0
  89. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/uctypes.pyi +0 -0
  90. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/urequests.pyi +0 -0
  91. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm/vfs.pyi +0 -0
  92. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/binascii.pyi +0 -0
  93. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/errno.pyi +0 -0
  94. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/hashlib.pyi +0 -0
  95. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/io.pyi +0 -0
  96. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/json.pyi +0 -0
  97. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/machine.pyi +0 -0
  98. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/math.pyi +0 -0
  99. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/micropython.pyi +0 -0
  100. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/network.pyi +0 -0
  101. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/os.pyi +0 -0
  102. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/select.pyi +0 -0
  103. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/socket.pyi +0 -0
  104. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/ssl.pyi +0 -0
  105. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/struct.pyi +0 -0
  106. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/sys.pyi +0 -0
  107. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/time.pyi +0 -0
  108. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/ubinascii.pyi +0 -0
  109. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/ucryptolib.pyi +0 -0
  110. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/uerrno.pyi +0 -0
  111. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/uhashlib.pyi +0 -0
  112. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/uio.pyi +0 -0
  113. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/ujson.pyi +0 -0
  114. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/umachine.pyi +0 -0
  115. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/uos.pyi +0 -0
  116. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/uselect.pyi +0 -0
  117. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/usocket.pyi +0 -0
  118. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/ussl.pyi +0 -0
  119. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/ustruct.pyi +0 -0
  120. {replx-1.6.2 → replx-1.6.4}/replx/typehints/comm_separate/EFR32MG/utime.pyi +0 -0
  121. {replx-1.6.2 → replx-1.6.4}/replx/typehints/core/ESP32/aioespnow.pyi +0 -0
  122. {replx-1.6.2 → replx-1.6.4}/replx/typehints/core/ESP32/esp.pyi +0 -0
  123. {replx-1.6.2 → replx-1.6.4}/replx/typehints/core/ESP32/esp32.pyi +0 -0
  124. {replx-1.6.2 → replx-1.6.4}/replx/typehints/core/ESP32/espnow.pyi +0 -0
  125. {replx-1.6.2 → replx-1.6.4}/replx/typehints/core/MIMXRT1062DVJ6A/mimxrt.pyi +0 -0
  126. {replx-1.6.2 → replx-1.6.4}/replx/typehints/core/RP2350/rp2.pyi +0 -0
  127. {replx-1.6.2 → replx-1.6.4}/replx/utils/__init__.py +0 -0
  128. {replx-1.6.2 → replx-1.6.4}/replx/utils/constants.py +0 -0
  129. {replx-1.6.2 → replx-1.6.4}/replx/utils/device_info.py +0 -0
  130. {replx-1.6.2 → replx-1.6.4}/replx/utils/exceptions.py +0 -0
  131. {replx-1.6.2 → replx-1.6.4}/replx.egg-info/SOURCES.txt +0 -0
  132. {replx-1.6.2 → replx-1.6.4}/replx.egg-info/dependency_links.txt +0 -0
  133. {replx-1.6.2 → replx-1.6.4}/replx.egg-info/entry_points.txt +0 -0
  134. {replx-1.6.2 → replx-1.6.4}/replx.egg-info/requires.txt +0 -0
  135. {replx-1.6.2 → replx-1.6.4}/replx.egg-info/top_level.txt +0 -0
  136. {replx-1.6.2 → replx-1.6.4}/setup.cfg +0 -0
  137. {replx-1.6.2 → replx-1.6.4}/test/test_line_mode_terminal.py +0 -0
  138. {replx-1.6.2 → replx-1.6.4}/test/test_termio.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: replx
3
- Version: 1.6.2
3
+ Version: 1.6.4
4
4
  Summary: replx is a fast, modern MicroPython CLI: turbo REPL, robust file sync (put/get), project install, mpy-cross integration, and smart port discovery.
5
5
  Author-email: "chanmin.park" <devcamp@gmail.com>
6
6
  License-Expression: MIT
@@ -1,5 +1,5 @@
1
1
  __all__ = ["__version__", "get_version", "__description__"]
2
- __version__ = "1.6.2"
2
+ __version__ = "1.6.4"
3
3
  __description__ = "Fast, modern MicroPython CLI with REPL, file sync, install, and smart port detection."
4
4
  __author__ = "PlanX Lab Development Team"
5
5
 
@@ -60,10 +60,6 @@ class _AgentDatagramProtocol(asyncio.DatagramProtocol):
60
60
  def __init__(self, server: 'AgentServer') -> None:
61
61
  self._server = server
62
62
 
63
- def connection_made(self, transport: asyncio.DatagramTransport) -> None:
64
- send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
65
- self._server.server_socket = send_sock
66
-
67
63
  def datagram_received(self, data: bytes, addr: tuple) -> None:
68
64
  server = self._server
69
65
  if not server.running:
@@ -103,7 +99,6 @@ class AgentServer(
103
99
  ):
104
100
  def __init__(self, port: int = None):
105
101
  self.agent_port = port or DEFAULT_AGENT_PORT
106
- self.server_socket: Optional[socket.socket] = None
107
102
  self.running = False
108
103
 
109
104
  self.connection_manager = ConnectionManager()
@@ -126,6 +121,7 @@ class AgentServer(
126
121
  self._loop: asyncio.AbstractEventLoop | None = None
127
122
  self._stop_event: asyncio.Event | None = None
128
123
  self._datagram_transport: asyncio.DatagramTransport | None = None
124
+ self._send_socket: Optional[socket.socket] = None
129
125
  self._cleaned_up: bool = False
130
126
 
131
127
  @property
@@ -326,6 +322,7 @@ class AgentServer(
326
322
  local_addr=(AGENT_HOST, self.agent_port),
327
323
  )
328
324
  self._datagram_transport = transport
325
+ self._send_socket = transport.get_extra_info('socket')
329
326
  self.running = True
330
327
  print(f'replx agent started — listening on {AGENT_HOST}:{self.agent_port} (UDP)')
331
328
 
@@ -436,6 +433,15 @@ class AgentServer(
436
433
  def _stop_drain_thread(self, conn: BoardConnection):
437
434
  conn.stop_detached()
438
435
 
436
+ def _safe_send(self, data: bytes, addr: tuple) -> None:
437
+ sock = self._send_socket
438
+ if not sock:
439
+ return
440
+ try:
441
+ sock.sendto(data, addr)
442
+ except Exception:
443
+ pass
444
+
439
445
  def _stop_detached_script(self, conn: BoardConnection = None):
440
446
  if conn:
441
447
  if not conn.is_detached():
@@ -467,13 +473,10 @@ class AgentServer(
467
473
  return
468
474
  self.last_seq[client_addr] = seq
469
475
 
470
- try:
471
- self.server_socket.sendto(
472
- AgentProtocol.encode_message(AgentProtocol.create_ack(seq)),
473
- client_addr,
474
- )
475
- except Exception:
476
- pass
476
+ self._safe_send(
477
+ AgentProtocol.encode_message(AgentProtocol.create_ack(seq)),
478
+ client_addr,
479
+ )
477
480
 
478
481
  try:
479
482
  ppid = msg.get('ppid')
@@ -492,13 +495,7 @@ class AgentServer(
492
495
  except Exception as exc:
493
496
  response = AgentProtocol.create_response(seq=seq, error=str(exc))
494
497
 
495
- try:
496
- self.server_socket.sendto(
497
- AgentProtocol.encode_message(response),
498
- client_addr,
499
- )
500
- except Exception:
501
- pass
498
+ self._safe_send(AgentProtocol.encode_message(response), client_addr)
502
499
 
503
500
  def _handle_request(self, data: bytes, client_addr: tuple):
504
501
  try:
@@ -524,13 +521,13 @@ class AgentServer(
524
521
 
525
522
  ack = AgentProtocol.create_ack(seq)
526
523
  ack_data = AgentProtocol.encode_message(ack)
527
- self.server_socket.sendto(ack_data, client_addr)
524
+ self._safe_send(ack_data, client_addr)
528
525
 
529
526
  response = self._handle_message(msg, client_addr)
530
527
 
531
528
  if response is not None:
532
529
  response_data = AgentProtocol.encode_message(response)
533
- self.server_socket.sendto(response_data, client_addr)
530
+ self._safe_send(response_data, client_addr)
534
531
 
535
532
  except Exception as e:
536
533
  try:
@@ -539,7 +536,7 @@ class AgentServer(
539
536
  error=str(e)
540
537
  )
541
538
  error_data = AgentProtocol.encode_message(error_response)
542
- self.server_socket.sendto(error_data, client_addr)
539
+ self._safe_send(error_data, client_addr)
543
540
  except Exception:
544
541
  pass
545
542
 
@@ -598,7 +595,6 @@ class AgentServer(
598
595
  if not conn:
599
596
  raise ValueError(f"Connection {target_port} not found")
600
597
 
601
- # Disconnect handles stopping detached script and drain thread via stop_detached()
602
598
  self.connection_manager.disconnect(target_port)
603
599
  self.session_manager.remove_connection_from_all_sessions(target_port)
604
600
 
@@ -778,13 +774,7 @@ class AgentServer(
778
774
  transport.close()
779
775
  except Exception:
780
776
  pass
781
- elif self.server_socket:
782
- try:
783
- self.server_socket.close()
784
- except Exception:
785
- pass
786
- self.server_socket = None
787
-
777
+ self._send_socket = None
788
778
  self._fast_executor.shutdown(wait=False, cancel_futures=True)
789
779
  self._slow_executor.shutdown(wait=False, cancel_futures=True)
790
780
 
@@ -199,7 +199,7 @@ class ExecCommandsMixin:
199
199
 
200
200
  def send_error(msg: str):
201
201
  error_response = AgentProtocol.create_response(seq=seq, error=msg)
202
- self.server_socket.sendto(AgentProtocol.encode_message(error_response), client_addr)
202
+ self._safe_send(AgentProtocol.encode_message(error_response), client_addr)
203
203
 
204
204
  if not conn or not conn.repl_protocol:
205
205
  send_error("Not connected")
@@ -273,7 +273,6 @@ class ExecCommandsMixin:
273
273
  pass
274
274
 
275
275
  def _run_interactive_thread(self, conn: BoardConnection, script_data: bytes):
276
- sock = self.server_socket
277
276
  client_addr = conn.interactive.client_addr
278
277
  seq = conn.interactive.seq
279
278
 
@@ -293,7 +292,7 @@ class ExecCommandsMixin:
293
292
  if completed:
294
293
  msg['completed'] = True
295
294
  msg['error'] = error
296
- sock.sendto(AgentProtocol.encode_message(msg), client_addr)
295
+ self._safe_send(AgentProtocol.encode_message(msg), client_addr)
297
296
  except Exception:
298
297
  pass
299
298
 
@@ -203,12 +203,12 @@ class TransferCommandsMixin:
203
203
  if not conn or not conn.file_system:
204
204
  error_response = AgentProtocol.create_response(seq=seq, error="Not connected")
205
205
  error_data = AgentProtocol.encode_message(error_response)
206
- self.server_socket.sendto(error_data, client_addr)
206
+ self._safe_send(error_data, client_addr)
207
207
  return
208
208
 
209
209
  ack_msg = AgentProtocol.create_ack(seq)
210
210
  ack_data = AgentProtocol.encode_message(ack_msg)
211
- self.server_socket.sendto(ack_data, client_addr)
211
+ self._safe_send(ack_data, client_addr)
212
212
 
213
213
  real_remote_path = self._to_real_path(remote_path, conn)
214
214
  remote_normalized = real_remote_path.rstrip('/')
@@ -228,7 +228,7 @@ class TransferCommandsMixin:
228
228
  "status": "starting"
229
229
  })
230
230
  progress_data = AgentProtocol.encode_message(progress_msg)
231
- self.server_socket.sendto(progress_data, client_addr)
231
+ self._safe_send(progress_data, client_addr)
232
232
 
233
233
  files_downloaded = 0
234
234
 
@@ -277,7 +277,7 @@ class TransferCommandsMixin:
277
277
  "status": "downloading"
278
278
  })
279
279
  progress_data = AgentProtocol.encode_message(progress_msg)
280
- self.server_socket.sendto(progress_data, client_addr)
280
+ self._safe_send(progress_data, client_addr)
281
281
 
282
282
  final_response = AgentProtocol.create_response(seq=seq, result={
283
283
  "downloaded_dir": remote_path,
@@ -286,12 +286,12 @@ class TransferCommandsMixin:
286
286
  "success": True
287
287
  })
288
288
  final_data = AgentProtocol.encode_message(final_response)
289
- self.server_socket.sendto(final_data, client_addr)
289
+ self._safe_send(final_data, client_addr)
290
290
 
291
291
  except Exception as e:
292
292
  error_response = AgentProtocol.create_response(seq=seq, error=f"getdir_to_local failed: {e}")
293
293
  error_data = AgentProtocol.encode_message(error_response)
294
- self.server_socket.sendto(error_data, client_addr)
294
+ self._safe_send(error_data, client_addr)
295
295
  finally:
296
296
  if conn:
297
297
  conn.release()
@@ -351,18 +351,18 @@ class TransferCommandsMixin:
351
351
  if not conn or not conn.file_system:
352
352
  error_response = AgentProtocol.create_response(seq=seq, error="Not connected")
353
353
  error_data = AgentProtocol.encode_message(error_response)
354
- self.server_socket.sendto(error_data, client_addr)
354
+ self._safe_send(error_data, client_addr)
355
355
  return
356
356
 
357
357
  if not os.path.exists(local_path):
358
358
  error_response = AgentProtocol.create_response(seq=seq, error=f"Local file not found: {local_path}")
359
359
  error_data = AgentProtocol.encode_message(error_response)
360
- self.server_socket.sendto(error_data, client_addr)
360
+ self._safe_send(error_data, client_addr)
361
361
  return
362
362
 
363
363
  ack_msg = AgentProtocol.create_ack(seq)
364
364
  ack_data = AgentProtocol.encode_message(ack_msg)
365
- self.server_socket.sendto(ack_data, client_addr)
365
+ self._safe_send(ack_data, client_addr)
366
366
 
367
367
  file_size = os.path.getsize(local_path)
368
368
  file_name = os.path.basename(local_path)
@@ -374,7 +374,7 @@ class TransferCommandsMixin:
374
374
  "status": "starting"
375
375
  })
376
376
  progress_data = AgentProtocol.encode_message(progress_msg)
377
- self.server_socket.sendto(progress_data, client_addr)
377
+ self._safe_send(progress_data, client_addr)
378
378
 
379
379
  real_remote_path = self._to_real_path(remote_path, conn)
380
380
 
@@ -393,7 +393,7 @@ class TransferCommandsMixin:
393
393
  "status": "uploading"
394
394
  })
395
395
  progress_data = AgentProtocol.encode_message(progress_msg)
396
- self.server_socket.sendto(progress_data, client_addr)
396
+ self._safe_send(progress_data, client_addr)
397
397
 
398
398
  conn.file_system.put(local_path, real_remote_path, progress_callback=progress_callback)
399
399
  bytes_uploaded[0] = file_size
@@ -405,13 +405,13 @@ class TransferCommandsMixin:
405
405
  "success": True
406
406
  })
407
407
  final_data = AgentProtocol.encode_message(final_response)
408
- self.server_socket.sendto(final_data, client_addr)
408
+ self._safe_send(final_data, client_addr)
409
409
 
410
410
  except Exception as e:
411
411
  error_response = AgentProtocol.create_response(seq=seq, error=str(e))
412
412
  error_data = AgentProtocol.encode_message(error_response)
413
413
  try:
414
- self.server_socket.sendto(error_data, client_addr)
414
+ self._safe_send(error_data, client_addr)
415
415
  except Exception:
416
416
  pass
417
417
  finally:
@@ -434,18 +434,18 @@ class TransferCommandsMixin:
434
434
  if not conn or not conn.file_system:
435
435
  error_response = AgentProtocol.create_response(seq=seq, error="Not connected")
436
436
  error_data = AgentProtocol.encode_message(error_response)
437
- self.server_socket.sendto(error_data, client_addr)
437
+ self._safe_send(error_data, client_addr)
438
438
  return
439
439
 
440
440
  if not os.path.exists(local_path) or not os.path.isdir(local_path):
441
441
  error_response = AgentProtocol.create_response(seq=seq, error=f"Local directory not found: {local_path}")
442
442
  error_data = AgentProtocol.encode_message(error_response)
443
- self.server_socket.sendto(error_data, client_addr)
443
+ self._safe_send(error_data, client_addr)
444
444
  return
445
445
 
446
446
  ack_msg = AgentProtocol.create_ack(seq)
447
447
  ack_data = AgentProtocol.encode_message(ack_msg)
448
- self.server_socket.sendto(ack_data, client_addr)
448
+ self._safe_send(ack_data, client_addr)
449
449
 
450
450
  base_local = os.path.abspath(local_path)
451
451
  real_remote_path = self._to_real_path(remote_path, conn)
@@ -481,7 +481,7 @@ class TransferCommandsMixin:
481
481
  "status": "starting"
482
482
  })
483
483
  progress_data = AgentProtocol.encode_message(progress_msg)
484
- self.server_socket.sendto(progress_data, client_addr)
484
+ self._safe_send(progress_data, client_addr)
485
485
 
486
486
  for dir_path in sorted(dirs_to_create):
487
487
  try:
@@ -518,7 +518,7 @@ class TransferCommandsMixin:
518
518
  "status": "uploading"
519
519
  })
520
520
  progress_data = AgentProtocol.encode_message(progress_msg)
521
- self.server_socket.sendto(progress_data, client_addr)
521
+ self._safe_send(progress_data, client_addr)
522
522
  break
523
523
 
524
524
  except Exception as e:
@@ -535,13 +535,13 @@ class TransferCommandsMixin:
535
535
  "success": True
536
536
  })
537
537
  final_data = AgentProtocol.encode_message(final_response)
538
- self.server_socket.sendto(final_data, client_addr)
538
+ self._safe_send(final_data, client_addr)
539
539
 
540
540
  except Exception as e:
541
541
  error_response = AgentProtocol.create_response(seq=seq, error=str(e))
542
542
  error_data = AgentProtocol.encode_message(error_response)
543
543
  try:
544
- self.server_socket.sendto(error_data, client_addr)
544
+ self._safe_send(error_data, client_addr)
545
545
  except Exception:
546
546
  pass
547
547
  finally:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: replx
3
- Version: 1.6.2
3
+ Version: 1.6.4
4
4
  Summary: replx is a fast, modern MicroPython CLI: turbo REPL, robust file sync (put/get), project install, mpy-cross integration, and smart port discovery.
5
5
  Author-email: "chanmin.park" <devcamp@gmail.com>
6
6
  License-Expression: MIT
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes