replx 1.22.2__tar.gz → 1.23.0__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.

Potentially problematic release.


This version of replx might be problematic. Click here for more details.

Files changed (163) hide show
  1. replx-1.23.0/PKG-INFO +1215 -0
  2. replx-1.23.0/README.md +1188 -0
  3. {replx-1.22.2 → replx-1.23.0}/pyproject.toml +1 -1
  4. {replx-1.22.2 → replx-1.23.0}/replx/__init__.py +1 -1
  5. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/exec.py +1 -1
  6. {replx-1.22.2 → replx-1.23.0}/replx/protocol/repl.py +17 -17
  7. {replx-1.22.2 → replx-1.23.0}/replx/terminal.py +1 -2
  8. replx-1.23.0/replx.egg-info/PKG-INFO +1215 -0
  9. replx-1.22.2/PKG-INFO +0 -118
  10. replx-1.22.2/README.md +0 -91
  11. replx-1.22.2/replx.egg-info/PKG-INFO +0 -118
  12. {replx-1.22.2 → replx-1.23.0}/LICENSE +0 -0
  13. {replx-1.22.2 → replx-1.23.0}/replx/__main__.py +0 -0
  14. {replx-1.22.2 → replx-1.23.0}/replx/cli/__init__.py +0 -0
  15. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/__init__.py +0 -0
  16. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/client/__init__.py +0 -0
  17. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/client/core.py +0 -0
  18. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/client/session.py +0 -0
  19. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/protocol.py +0 -0
  20. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/server/__init__.py +0 -0
  21. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/server/__main__.py +0 -0
  22. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/server/command_dispatcher.py +0 -0
  23. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/server/connection_manager.py +0 -0
  24. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/server/core.py +0 -0
  25. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/server/handlers/__init__.py +0 -0
  26. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/server/handlers/exec.py +0 -0
  27. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/server/handlers/filesystem.py +0 -0
  28. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/server/handlers/i2c.py +0 -0
  29. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/server/handlers/repl.py +0 -0
  30. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/server/handlers/session.py +0 -0
  31. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/server/handlers/spi.py +0 -0
  32. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/server/handlers/transfer.py +0 -0
  33. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/server/handlers/uart.py +0 -0
  34. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/server/session_manager.py +0 -0
  35. {replx-1.22.2 → replx-1.23.0}/replx/cli/agent/server/udp.py +0 -0
  36. {replx-1.22.2 → replx-1.23.0}/replx/cli/app.py +0 -0
  37. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/__init__.py +0 -0
  38. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/_common.py +0 -0
  39. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/adc.py +0 -0
  40. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/ble.py +0 -0
  41. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/device.py +0 -0
  42. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/file.py +0 -0
  43. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/firmware.py +0 -0
  44. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/gpio.py +0 -0
  45. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/i2c.py +0 -0
  46. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/i2s.py +0 -0
  47. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/mip.py +0 -0
  48. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/package.py +0 -0
  49. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/pwm.py +0 -0
  50. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/spi.py +0 -0
  51. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/uart.py +0 -0
  52. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/utility.py +0 -0
  53. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/wifi.py +0 -0
  54. {replx-1.22.2 → replx-1.23.0}/replx/cli/commands/ws2812.py +0 -0
  55. {replx-1.22.2 → replx-1.23.0}/replx/cli/config.py +0 -0
  56. {replx-1.22.2 → replx-1.23.0}/replx/cli/connection.py +0 -0
  57. {replx-1.22.2 → replx-1.23.0}/replx/cli/helpers/__init__.py +0 -0
  58. {replx-1.22.2 → replx-1.23.0}/replx/cli/helpers/compiler.py +0 -0
  59. {replx-1.22.2 → replx-1.23.0}/replx/cli/helpers/environment.py +0 -0
  60. {replx-1.22.2 → replx-1.23.0}/replx/cli/helpers/output.py +0 -0
  61. {replx-1.22.2 → replx-1.23.0}/replx/cli/helpers/registry.py +0 -0
  62. {replx-1.22.2 → replx-1.23.0}/replx/cli/helpers/scanner.py +0 -0
  63. {replx-1.22.2 → replx-1.23.0}/replx/cli/helpers/store.py +0 -0
  64. {replx-1.22.2 → replx-1.23.0}/replx/cli/helpers/updater.py +0 -0
  65. {replx-1.22.2 → replx-1.23.0}/replx/commands.py +0 -0
  66. {replx-1.22.2 → replx-1.23.0}/replx/protocol/__init__.py +0 -0
  67. {replx-1.22.2 → replx-1.23.0}/replx/protocol/storage.py +0 -0
  68. {replx-1.22.2 → replx-1.23.0}/replx/transport/__init__.py +0 -0
  69. {replx-1.22.2 → replx-1.23.0}/replx/transport/serial.py +0 -0
  70. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/_thread.pyi +0 -0
  71. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/aioble/__init__.pyi +0 -0
  72. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/aioble/l2cap.pyi +0 -0
  73. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/array.pyi +0 -0
  74. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/asyncio/__init__.pyi +0 -0
  75. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/binascii.pyi +0 -0
  76. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/bluetooth.pyi +0 -0
  77. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/builtins.pyi +0 -0
  78. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/cmath.pyi +0 -0
  79. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/collections.pyi +0 -0
  80. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/cryptolib.pyi +0 -0
  81. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/deflate.pyi +0 -0
  82. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/errno.pyi +0 -0
  83. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/framebuf.pyi +0 -0
  84. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/gc.pyi +0 -0
  85. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/hashlib.pyi +0 -0
  86. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/heapq.pyi +0 -0
  87. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/io.pyi +0 -0
  88. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/json.pyi +0 -0
  89. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/lwip.pyi +0 -0
  90. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/machine.pyi +0 -0
  91. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/math.pyi +0 -0
  92. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/micropython.pyi +0 -0
  93. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/mip/__init__.pyi +0 -0
  94. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/network.pyi +0 -0
  95. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/ntptime.pyi +0 -0
  96. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/os.pyi +0 -0
  97. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/platform.pyi +0 -0
  98. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/random.pyi +0 -0
  99. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/re.pyi +0 -0
  100. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/requests/__init__.pyi +0 -0
  101. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/select.pyi +0 -0
  102. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/socket.pyi +0 -0
  103. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/ssl.pyi +0 -0
  104. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/struct.pyi +0 -0
  105. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/sys.pyi +0 -0
  106. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/time.pyi +0 -0
  107. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/tls.pyi +0 -0
  108. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/uasyncio.pyi +0 -0
  109. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/uctypes.pyi +0 -0
  110. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/ulab.pyi +0 -0
  111. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/urequests.pyi +0 -0
  112. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm/vfs.pyi +0 -0
  113. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/binascii.pyi +0 -0
  114. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/digi/__init__.pyi +0 -0
  115. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/digi/ble.pyi +0 -0
  116. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/errno.pyi +0 -0
  117. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/hashlib.pyi +0 -0
  118. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/io.pyi +0 -0
  119. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/json.pyi +0 -0
  120. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/machine.pyi +0 -0
  121. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/math.pyi +0 -0
  122. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/micropython.pyi +0 -0
  123. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/network.pyi +0 -0
  124. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/os.pyi +0 -0
  125. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/select.pyi +0 -0
  126. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/socket.pyi +0 -0
  127. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/ssl.pyi +0 -0
  128. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/struct.pyi +0 -0
  129. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/sys.pyi +0 -0
  130. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/time.pyi +0 -0
  131. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/ubinascii.pyi +0 -0
  132. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/ucryptolib.pyi +0 -0
  133. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/uerrno.pyi +0 -0
  134. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/uhashlib.pyi +0 -0
  135. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/uio.pyi +0 -0
  136. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/ujson.pyi +0 -0
  137. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/umachine.pyi +0 -0
  138. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/uos.pyi +0 -0
  139. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/uselect.pyi +0 -0
  140. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/usocket.pyi +0 -0
  141. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/ussl.pyi +0 -0
  142. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/ustruct.pyi +0 -0
  143. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/utime.pyi +0 -0
  144. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/xbee/__init__.pyi +0 -0
  145. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/xbee/modem_status.pyi +0 -0
  146. {replx-1.22.2 → replx-1.23.0}/replx/typehints/comm_separate/EFR32MG/xbee/relay.pyi +0 -0
  147. {replx-1.22.2 → replx-1.23.0}/replx/typehints/core/ESP32/aioespnow.pyi +0 -0
  148. {replx-1.22.2 → replx-1.23.0}/replx/typehints/core/ESP32/esp.pyi +0 -0
  149. {replx-1.22.2 → replx-1.23.0}/replx/typehints/core/ESP32/esp32.pyi +0 -0
  150. {replx-1.22.2 → replx-1.23.0}/replx/typehints/core/ESP32/espnow.pyi +0 -0
  151. {replx-1.22.2 → replx-1.23.0}/replx/typehints/core/MIMXRT1062DVJ6A/mimxrt.pyi +0 -0
  152. {replx-1.22.2 → replx-1.23.0}/replx/typehints/core/RP2350/rp2.pyi +0 -0
  153. {replx-1.22.2 → replx-1.23.0}/replx/utils/__init__.py +0 -0
  154. {replx-1.22.2 → replx-1.23.0}/replx/utils/constants.py +0 -0
  155. {replx-1.22.2 → replx-1.23.0}/replx/utils/device_info.py +0 -0
  156. {replx-1.22.2 → replx-1.23.0}/replx/utils/exceptions.py +0 -0
  157. {replx-1.22.2 → replx-1.23.0}/replx.egg-info/SOURCES.txt +0 -0
  158. {replx-1.22.2 → replx-1.23.0}/replx.egg-info/dependency_links.txt +0 -0
  159. {replx-1.22.2 → replx-1.23.0}/replx.egg-info/entry_points.txt +0 -0
  160. {replx-1.22.2 → replx-1.23.0}/replx.egg-info/requires.txt +0 -0
  161. {replx-1.22.2 → replx-1.23.0}/replx.egg-info/top_level.txt +0 -0
  162. {replx-1.22.2 → replx-1.23.0}/setup.cfg +0 -0
  163. {replx-1.22.2 → replx-1.23.0}/test/test_hardware_commands.py +0 -0
replx-1.23.0/PKG-INFO ADDED
@@ -0,0 +1,1215 @@
1
+ Metadata-Version: 2.4
2
+ Name: replx
3
+ Version: 1.23.0
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
+ Author-email: "chanmin.park" <devcamp@planxlab.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://planxlab.com
8
+ Project-URL: Repository, https://github.com/PlanXLab/replx
9
+ Project-URL: Issues, https://github.com/PlanXLab/replx/issues
10
+ Keywords: micropython,repl,serial,pyserial,typer,mpy-cross,deploy,gpio,i2c,spi,uart,pwm,adc,i2s,ws2812
11
+ Classifier: Environment :: Console
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Topic :: Software Development :: Embedded Systems
16
+ Classifier: Topic :: System :: Hardware :: Universal Serial Bus (USB)
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: click>=8.4.2
21
+ Requires-Dist: typer>=0.27.0
22
+ Requires-Dist: rich>=13.0
23
+ Requires-Dist: pyserial>=3.5
24
+ Requires-Dist: mpy-cross>=1.27.0
25
+ Requires-Dist: psutil>=7.2.2
26
+ Dynamic: license-file
27
+
28
+ # replx
29
+
30
+ [![PyPI version](https://badge.fury.io/py/replx.svg)](https://badge.fury.io/py/replx)
31
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
32
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
33
+
34
+ `replx` is a CLI tool for MicroPython development that uses a single local agent-based architecture to connect and manage multiple CLI sessions and multiple boards, improving connection efficiency and enabling parallel workflows.
35
+
36
+ ---
37
+
38
+ ## Core Architecture
39
+
40
+ ```mermaid
41
+ flowchart LR
42
+ CLI1[replx CLI A] <-->|UDP/IPC| AGENT[Agent Server]
43
+ CLI2[replx CLI B] <-->|UDP/IPC| AGENT
44
+ CLIN[replx CLI N] <-->|UDP/IPC| AGENT
45
+ AGENT <-->|Serial| DEV1[Board A]
46
+ AGENT <-->|Serial| DEV2[Board B]
47
+ AGENT <-->|Serial| DEVN[Board N]
48
+
49
+ BG[Single Local Background Process]:::note -.-> AGENT
50
+
51
+ classDef note fill:#f8f9fa,stroke:#c9ccd1,color:#444;
52
+ ```
53
+
54
+ Each CLI command communicates with a background Agent Server over UDP/IPC. The Agent Server handles all serial communication with the boards. This design allows multiple terminal sessions to share connection state while consistently handling FG/BG switching, status queries, and command execution.
55
+
56
+ ### Agent Process and Port
57
+
58
+ - `replx` uses one local Agent Server per PC.
59
+ - The agent listens on a UDP port selected from `49152-65535`.
60
+ - The selected port is stored in `~/.replx/.config` as `AGENT_PORT=...` and reused by later CLI commands.
61
+ - If the stored port is occupied by another program and no replx agent is running there, replx selects a new free port and updates `~/.replx/.config`.
62
+
63
+ ### Session / FG / BG / Default
64
+
65
+ - **Session (SID)**: A unique work context created per terminal
66
+ - **FG (Foreground) connection**: The board used as the default target in the current session
67
+ - **BG (Background) connection**: Additional boards attached to the same session alongside FG
68
+ - **Default connection**: Board info saved by `setup`, reused when no port is specified
69
+
70
+ ```mermaid
71
+ flowchart TB
72
+ SID[Session SID]
73
+ FG[FG Board<br/>Default board for session]
74
+ BG1[BG Board 1]
75
+ BG2[BG Board 2]
76
+ DEF[Default Connection<br/>saved by setup]
77
+
78
+ SID --> FG
79
+ SID --> BG1
80
+ SID --> BG2
81
+ DEF -. Used when no FG exists .-> FG
82
+ ```
83
+
84
+ Key rules:
85
+
86
+ 1. A session can have **one FG + multiple BG** boards.
87
+ 2. The FG board can be used without specifying a port in most commands.
88
+ 3. To target a BG board, the port must be specified explicitly.
89
+ 4. After `setup`, the saved default connection makes port-omitted execution straightforward.
90
+
91
+ ### setup and Workspace Scope
92
+
93
+ `setup` is not just a connect command — it initializes the development environment relative to the **current VS Code workspace**.
94
+
95
+ ```mermaid
96
+ flowchart TB
97
+ ROOT[Filesystem Root<br/>C:\\ or /]
98
+ WSA[Workspace A<br/>.vscode/.replx exists]
99
+ WSB[Workspace B<br/>no parent config]
100
+ SUB1[Subfolder A-1]
101
+ SUB2[Subfolder A-2]
102
+ NOTE_ROOT[Filesystem root<br/>cannot be a workspace]
103
+
104
+ ROOT --> WSA
105
+ ROOT --> WSB
106
+ WSA --> SUB1
107
+ WSA --> SUB2
108
+
109
+ SUB1 -. run setup .-> WSA
110
+ SUB2 -. run setup .-> WSA
111
+ WSB -. run setup .-> WSB
112
+ ROOT -. rule .-> NOTE_ROOT
113
+ ```
114
+
115
+ 1. Running `setup` configures MicroPython type hints, VS Code settings, and the default port (Default connection) for that workspace.
116
+ 2. The filesystem root (`C:\`, `/`) cannot be used as a workspace.
117
+ 3. A workspace can contain subfolders — running `setup` from a subfolder applies settings to the parent workspace scope.
118
+ 4. If no workspace config exists in a parent path, each subdirectory is treated as an independent workspace with its own default port.
119
+
120
+ ### Which board does a command target?
121
+
122
+ General commands (`run`, `ls`, `usage`, etc.) resolve the target board using the following flow:
123
+
124
+ ```mermaid
125
+ flowchart TB
126
+ CMD[Command<br/>run / ls / usage ...]
127
+ HASPORT{Port specified?}
128
+ HASFG{Session FG exists?}
129
+ HASDEF{Saved Default exists?}
130
+ USEPORT[Use specified port]
131
+ USEFG[Use FG board]
132
+ USEDEF[Connect via Default and use]
133
+ ERR[No target<br/>run setup first]
134
+
135
+ CMD --> HASPORT
136
+ HASPORT -- yes --> USEPORT
137
+ HASPORT -- no --> HASFG
138
+ HASFG -- yes --> USEFG
139
+ HASFG -- no --> HASDEF
140
+ HASDEF -- yes --> USEDEF
141
+ HASDEF -- no --> ERR
142
+ ```
143
+
144
+ 1. If a port is specified in the command, that port takes priority.
145
+ 2. Otherwise, the session's FG board is used.
146
+ 3. If no FG exists, a connection is attempted using the saved Default.
147
+
148
+ The recommended approach: **use `setup` to make a board the default**, and only specify a port when needed.
149
+
150
+ Both of the following forms work identically:
151
+
152
+ - `replx --port PORT setup`
153
+ - `replx PORT setup` (shorthand)
154
+
155
+ `PORT` depends on the OS:
156
+ - Windows: `COM4`
157
+ - Linux: `/dev/ttyACM0` or `/dev/ttyUSB0`
158
+ - macOS: `/dev/cu.usbmodem14101` or `/dev/cu.usbserial-0001`
159
+
160
+ ### Two command groups
161
+
162
+ #### Commands that work without a port
163
+ These commands are primarily for querying or managing state, not direct board interaction. They operate independently or based on session context. Using `-p/--port` with these commands is an error.
164
+
165
+ - `replx scan` — discover connectable boards (no connection made)
166
+ - `replx status` — check session/FG/BG connection state
167
+ - `replx whoami` — show the FG board for the current session
168
+ - `replx shutdown` — shut down the agent and all connections
169
+ - `replx --help`, `replx COMMAND --help` — help
170
+ - `replx -v` — version
171
+
172
+ #### Commands that require a board connection
173
+ These commands communicate with the board. Except for `setup`, the port can be omitted if an FG or Default connection exists.
174
+
175
+ - Connection/session: `setup`, `fg`, `disconnect`
176
+ - Execution/interaction: `exec`, `run`, `repl`, `shell`
177
+ - Files: `ls`, `cat`, `get`, `put`, `cp`, `mv`, `rm`, `mkdir`, `touch`
178
+ - Board: `usage`, `reset`, `format`, `init`, `wifi`, `firmware`
179
+ - Package/compile: `mip`, `pkg`, `mpy`
180
+ - Hardware: `gpio`, `pwm`, `adc`, `uart`, `spi`, `i2c`, `ble`
181
+
182
+ ---
183
+
184
+ ### Usage Scenarios
185
+
186
+ #### Scenario #1 — Start a new project
187
+ 1. Run `replx scan` to identify the port
188
+ 2. Run `replx PORT setup` to initialize the workspace and save the default connection
189
+ 3. Run `replx ls` to confirm basic operation
190
+
191
+ #### Scenario #2 — Multiple terminals / multiple boards
192
+ 1. Connect the required boards from terminals A and B
193
+ 2. Run `replx status` to check FG/BG state per SID
194
+ 3. Use `replx fg` or `replx PORT fg` to switch as needed
195
+
196
+ #### Scenario #3 — Quick code experimentation
197
+ - One-liner: `replx -c "print('hello')"`
198
+ - Run a file: `replx run app.py`
199
+ - Start REPL: `replx repl`
200
+
201
+ #### Scenario #4 — File and library management
202
+ - File sync: `put/get/ls/cat/...`
203
+ - Interactive file shell: `replx shell`
204
+ - Library update: `replx pkg search` → `replx pkg download` → `replx pkg update core.all`
205
+
206
+ #### Scenario #5 — End of session / cleanup
207
+ - Release port only: `replx disconnect`
208
+ - Full shutdown: `replx shutdown`
209
+
210
+ ---
211
+
212
+ ## Command Reference
213
+
214
+ ### Command Summary
215
+
216
+ #### Connection / Session
217
+
218
+ | Command | Description |
219
+ |---|---|
220
+ | `setup` | Register the board as the default target and initialize the development environment. |
221
+ | `scan` | Discover available ports and board information. |
222
+ | `status` | Show FG/BG connection state per session. |
223
+ | `fg` | Switch the FG board for the current session. |
224
+ | `whoami` | Show the FG board the current session is targeting. |
225
+ | `disconnect` | Release the board connection for the current session. |
226
+ | `shutdown` | Shut down the agent and clean up all session connections. |
227
+
228
+ #### Execution / Interaction
229
+
230
+ | Command | Description |
231
+ |---|---|
232
+ | `exec` (`-c`) | Execute a short Python snippet immediately. |
233
+ | `run` | Run a local Python file on the board. |
234
+ | `repl` | Start an interactive REPL session. |
235
+ | `shell` | Open an interactive shell for board filesystem operations. |
236
+
237
+ #### File Operations
238
+
239
+ | Command | Description |
240
+ |---|---|
241
+ | `ls` | List files and directories at a board path. |
242
+ | `cat` | Print the contents of a board file. |
243
+ | `get` | Download a file or directory from the board to local. |
244
+ | `put` | Upload a local file or directory to the board. |
245
+ | `cp` | Copy files or directories within the board. |
246
+ | `mv` | Move or rename files or directories within the board. |
247
+ | `rm` | Delete files or directories on the board. |
248
+ | `mkdir` | Create a directory on the board. |
249
+ | `touch` | Create an empty file or update its timestamp. |
250
+
251
+ #### Board Management
252
+
253
+ | Command | Description |
254
+ |---|---|
255
+ | `usage` | Show storage and RAM usage with free space. |
256
+ | `reset` | Soft-reset the board. |
257
+ | `format` | Format the board filesystem. |
258
+ | `init` | Run initial setup scripts on the board. |
259
+ | `wifi` | Manage Wi-Fi configuration and connection state. |
260
+ | `firmware` | Check, download, or update board firmware. |
261
+
262
+ #### Package / Compile
263
+
264
+ | Command | Description |
265
+ |---|---|
266
+ | `mip` | Search and install MicroPython packages from the official index or GitHub. |
267
+ | `pkg` | Search, download, and update MicroPython packages. |
268
+ | `mpy` | Compile `.py` files to `.mpy` bytecode. |
269
+
270
+ #### Hardware
271
+
272
+ | Command | Description |
273
+ |---|---|
274
+ | `gpio` | Read, write, and run GPIO sequences. |
275
+ | `pwm` | Generate and monitor PWM signals. |
276
+ | `adc` | Read ADC pins and run a board-side scope UI. |
277
+ | `uart` | Open, write, read, and monitor UART. |
278
+ | `spi` | Open, write, read, and transfer SPI data. |
279
+ | `i2c` | Scan, read, write, and dump I2C devices. |
280
+ | `ble` | Manage BLE radio, scan, advertise, GATT server/client, and L2CAP. |
281
+
282
+ ---
283
+
284
+ ### Connection / Session
285
+
286
+ #### `setup`
287
+ The first command to run when starting a new project. After confirming the board connection, it creates `.vscode/tasks.json`, `.vscode/settings.json`, and `.vscode/launch.json`, configures the Pylance typehint path, and saves the current port as the workspace Default. A port is required. Using `clean` clears all existing connection history and keeps only the current port.
288
+ Settings apply at the workspace level; running from a subfolder applies them to the parent workspace scope. If no config exists in a parent path, each path is treated as an independent workspace with its own default port.
289
+ The filesystem root (`C:\`, `/`) cannot be used as a workspace.
290
+
291
+ Usage:
292
+ ```sh
293
+ replx PORT setup
294
+ replx PORT setup clean
295
+ ```
296
+
297
+ Examples:
298
+ ```sh
299
+ replx COM3 setup
300
+ replx /dev/ttyACM0 setup
301
+ replx /dev/cu.usbmodem14101 setup
302
+ replx COM3 setup clean
303
+ ```
304
+
305
+ #### `scan`
306
+ Use this to identify which port belongs to which board before connecting. Combines already-connected agent ports with freshly scanned ports and displays Port / Version / Core / Device / Manufacturer along with connected and Default status.
307
+
308
+ Usage:
309
+ ```sh
310
+ replx scan
311
+ ```
312
+
313
+ Examples:
314
+ ```sh
315
+ replx scan
316
+ replx COM3 setup # setup using port found by scan
317
+ replx /dev/ttyACM0 setup # Linux example
318
+ replx /dev/cu.usbmodem14101 setup # macOS example
319
+ ```
320
+
321
+ Notes:
322
+ - `scan` only detects boards; it does not establish a connection.
323
+ - Using `-p/--port` with `scan` is an error.
324
+
325
+ #### `status`
326
+ Use this in a multi-terminal environment to inspect FG/BG connections per session at a glance. Shows the current session, foreground/background ports for each session, and the default port.
327
+
328
+ Usage:
329
+ ```sh
330
+ replx status
331
+ ```
332
+
333
+ Examples:
334
+ ```sh
335
+ replx status
336
+ replx fg # switch foreground after checking status
337
+ ```
338
+
339
+ #### `fg`
340
+ Switch the foreground board for the current session. `replx fg` presents an interactive list to choose from; `replx PORT fg` immediately promotes the specified port to FG. Subsequent commands (`ls`, `run`, `cat`, etc.) will target the new FG board.
341
+
342
+ Usage:
343
+ ```sh
344
+ replx fg
345
+ replx PORT fg
346
+ ```
347
+
348
+ Examples:
349
+ ```sh
350
+ replx fg
351
+ replx COM19 fg
352
+ replx /dev/ttyACM0 fg
353
+ replx /dev/cu.usbmodem14101 fg
354
+ ```
355
+
356
+ #### `whoami`
357
+ Instantly shows what FG board the current terminal session is targeting.
358
+
359
+ Usage:
360
+ ```sh
361
+ replx whoami
362
+ ```
363
+
364
+ Examples:
365
+ ```sh
366
+ replx whoami
367
+ replx COM3 fg && replx whoami # confirm target after switching FG
368
+ ```
369
+
370
+ #### `disconnect`
371
+ Disconnects a specific port while keeping the agent process running. `replx disconnect` targets the current session's FG; `replx PORT disconnect` releases the specified port. When a connection is released, it is removed from all sessions that were referencing it.
372
+ If the FG is removed, one of the BG boards becomes FG. If no ports remain, the agent shuts down.
373
+
374
+ Usage:
375
+ ```sh
376
+ replx disconnect
377
+ replx PORT disconnect
378
+ ```
379
+
380
+ Examples:
381
+ ```sh
382
+ replx disconnect
383
+ replx COM3 disconnect
384
+ replx /dev/ttyACM0 disconnect
385
+ replx /dev/cu.usbmodem14101 disconnect
386
+ ```
387
+
388
+ #### `shutdown`
389
+ Use this when you are done working and want to clean up sessions, connections, and the agent in one step. The result varies by state: `Shutdown Complete` on success, `Already Shutdown` if already stopped, `Shutdown Failed` on error. The agent will restart automatically the next time a command requires it.
390
+
391
+ Usage:
392
+ ```sh
393
+ replx shutdown
394
+ ```
395
+
396
+ Examples:
397
+ ```sh
398
+ replx shutdown
399
+ replx shutdown && replx status # confirm state after shutdown
400
+ ```
401
+
402
+ ---
403
+
404
+ ### Execution / Interaction
405
+
406
+ #### `exec` (`-c` alias)
407
+ Run a short MicroPython snippet without a file. Sends the `exec` command to the board once and returns standard output immediately. Multiple statements can be chained with semicolons (`;`).
408
+
409
+ Usage:
410
+ ```sh
411
+ replx exec "CODE"
412
+ replx -c "CODE"
413
+ ```
414
+
415
+ Examples:
416
+ ```sh
417
+ replx -c "print('hello')"
418
+ replx -c "import os; print(os.listdir())"
419
+ replx exec "import machine; print(machine.freq())"
420
+ replx -c "import time; time.sleep(1); print('done')"
421
+ ```
422
+
423
+ #### `run`
424
+ The primary command for running scripts. Default mode transfers a local file to the board and runs it interactively. `-d/--device` runs a file already stored on the board interactively. `-n/--non-interactive` detaches execution without waiting for I/O. `-e/--echo` echoes input to the terminal during interactive runs. `--line text|hex` switches to line input mode: output scrolls in a split region while the input line stays fixed at the bottom. `--non-interactive` and `--echo` cannot be used together, and `--line` cannot be combined with either.
425
+
426
+ Usage:
427
+ ```sh
428
+ replx run SCRIPT
429
+ replx run -d SCRIPT
430
+ replx SCRIPT
431
+ replx run --line text SCRIPT
432
+ replx run --line hex SCRIPT
433
+ ```
434
+
435
+ Examples:
436
+ ```sh
437
+ replx run main.py
438
+ replx main.py # shorthand for .py files
439
+ replx run -d /test.py # run board's /test.py interactively
440
+ replx run -n server.py # non-interactive (no I/O wait)
441
+ replx run -dn /main.py # run board's /main.py non-interactively
442
+ replx run --line text main.py # line mode: type text, Enter to send
443
+ replx run --line hex main.py # line mode: type hex bytes (e.g. 0102ff), Enter to send
444
+ replx main.py --line hex # shortcut form also works
445
+ ```
446
+
447
+ Line mode key bindings:
448
+ - `Enter`: send the typed line to the board
449
+ - `Backspace`: delete last character
450
+ - `Ctrl+U`: clear the input line
451
+ - `Ctrl+C` twice: interrupt and exit
452
+
453
+ #### `repl`
454
+ Enter the board's Friendly REPL to experiment with code line by line. Output is displayed in real time. Type `exit()` to end the REPL session.
455
+
456
+ Usage:
457
+ ```sh
458
+ replx repl
459
+ ```
460
+
461
+ Examples:
462
+ ```sh
463
+ replx repl
464
+ # Inside REPL:
465
+ >>> import os
466
+ >>> os.listdir()
467
+ >>> exit()
468
+ ```
469
+
470
+ Key bindings:
471
+ - `Ctrl+C`: interrupt running code
472
+ - `Ctrl+D`: soft reset
473
+
474
+ #### `shell`
475
+ An interactive shell for continuous board filesystem management. Supports `ls/cat/cp/mv/rm/mkdir/touch` and `cd/pwd` relative to the current path. This is not a Python REPL. Inside the shell, `run` always operates on board files (`-d` mode), and `-n/-e` options are unavailable.
476
+
477
+ Usage:
478
+ ```sh
479
+ replx shell
480
+ ```
481
+
482
+ Available commands inside the shell:
483
+ ```sh
484
+ # Files
485
+ ls cat cp mv rm mkdir touch
486
+
487
+ # Navigation
488
+ cd pwd clear
489
+
490
+ # Execution
491
+ exec run repl
492
+
493
+ # Other
494
+ usage wifi help exit
495
+ ```
496
+
497
+ Examples:
498
+ ```sh
499
+ replx shell
500
+ :/ > ls
501
+ :/ > cd lib
502
+ :/lib > cat boot.py
503
+ :/lib > exit
504
+ ```
505
+
506
+ ---
507
+
508
+ ### File Operations
509
+
510
+ #### `ls`
511
+ List the contents of a board directory. Default shows a single-path listing; `-r/--recursive` outputs the full tree. Folder/file type icons and size information are shown, making it useful for pre/post-deploy comparisons.
512
+
513
+ Usage:
514
+ ```sh
515
+ replx ls [PATH]
516
+ replx ls -r [PATH]
517
+ ```
518
+
519
+ Examples:
520
+ ```sh
521
+ replx ls
522
+ replx ls /lib
523
+ replx ls -r
524
+ replx ls -r /lib
525
+ ```
526
+
527
+ #### `cat`
528
+ View the contents of a board file. Text files are shown as-is; binary files are displayed as a hex dump. `-n/--number` adds line numbers for text. `-L/--lines` selects a line range (`N:M`) for text or a byte range (`N:M`, `N:+M`) for binary.
529
+
530
+ Usage:
531
+ ```sh
532
+ replx cat FILE
533
+ replx cat -n FILE
534
+ replx cat -L N:M FILE
535
+ ```
536
+
537
+ Examples:
538
+ ```sh
539
+ replx cat main.py
540
+ replx cat -n /lib/audio.py
541
+ replx cat -L 1:30 boot.py
542
+ replx cat -L 100:+64 app.mpy
543
+ ```
544
+
545
+ #### `get`
546
+ Download files or directories from the board to local. Remote patterns (`*`, `?`) are resolved on the board side; directories are downloaded recursively. When downloading multiple sources, the last argument `LOCAL` must be a directory.
547
+
548
+ Usage:
549
+ ```sh
550
+ replx get REMOTE LOCAL
551
+ replx get REMOTE... LOCAL
552
+ ```
553
+
554
+ Examples:
555
+ ```sh
556
+ replx get main.py ./
557
+ replx get / ./backup # full board filesystem backup
558
+ replx get /a.py /b.py ./
559
+ replx get /lib/*.mpy ./compiled
560
+ ```
561
+
562
+ #### `put`
563
+ Upload local files or directories to the board. Local wildcards (`*`, `?`) are expanded before upload; directories are transferred recursively. The last argument is always the remote destination path; remote directories are created automatically if needed.
564
+
565
+ Usage:
566
+ ```sh
567
+ replx put LOCAL REMOTE
568
+ replx put LOCAL... REMOTE
569
+ ```
570
+
571
+ Examples:
572
+ ```sh
573
+ replx put main.py /
574
+ replx put ./lib/audio.py /lib
575
+ replx put a.py b.py /lib
576
+ replx put *.py /lib
577
+ ```
578
+
579
+ #### `cp`
580
+ Copy files or directories within the board. Supports single files, directories, multiple sources, and patterns. `-r/--recursive` is required for directories; the destination must be a directory when copying multiple sources.
581
+
582
+ Usage:
583
+ ```sh
584
+ replx cp SRC DEST
585
+ replx cp -r DIR DEST
586
+ ```
587
+
588
+ Examples:
589
+ ```sh
590
+ replx cp /main.py /backup.py
591
+ replx cp x.py y.py /backup
592
+ replx cp /lib/*.mpy /backup
593
+ replx cp -r /lib /lib_backup
594
+ ```
595
+
596
+ #### `mv`
597
+ Move or rename files and directories within the board. Supports single moves, multiple sources, and patterns. `-r/--recursive` is required for directories. When moving multiple sources, verify the destination is a directory first.
598
+
599
+ Usage:
600
+ ```sh
601
+ replx mv SRC DEST
602
+ replx mv -r DIR DEST
603
+ ```
604
+
605
+ Examples:
606
+ ```sh
607
+ replx mv /old.py /new.py
608
+ replx mv /main.py /backup
609
+ replx mv *.py /backup
610
+ replx mv -r /lib/audio /lib/sound
611
+ ```
612
+
613
+ #### `rm`
614
+ Delete files or directories on the board. By default, a confirmation prompt is shown before deletion; `-f/--force` skips the prompt. `-r/--recursive` is required for directories. Pattern deletion (`*.py`, `/lib/*.mpy`) is supported.
615
+
616
+ Usage:
617
+ ```sh
618
+ replx rm FILE
619
+ replx rm -r DIR
620
+ replx rm -f FILE
621
+ ```
622
+
623
+ Examples:
624
+ ```sh
625
+ replx rm /main.py
626
+ replx rm -f /main.py
627
+ replx rm /a.py /b.py
628
+ replx rm /lib/*.mpy
629
+ replx rm -rf /tmp
630
+ ```
631
+
632
+ #### `mkdir`
633
+ Pre-create a directory structure on the board. Multiple paths can be created at once, including nested paths. Existing directories are silently retained.
634
+
635
+ Usage:
636
+ ```sh
637
+ replx mkdir DIR
638
+ replx mkdir DIR...
639
+ ```
640
+
641
+ Examples:
642
+ ```sh
643
+ replx mkdir /lib
644
+ replx mkdir /tests
645
+ replx mkdir /lib/audio /lib/net
646
+ replx mkdir /a/b/c
647
+ ```
648
+
649
+ #### `touch`
650
+ Create an empty file or ensure a file exists. Useful for setting up initial project templates with multiple files at once.
651
+
652
+ Usage:
653
+ ```sh
654
+ replx touch FILE
655
+ replx touch FILE...
656
+ ```
657
+
658
+ Examples:
659
+ ```sh
660
+ replx touch /config.py
661
+ replx touch /a.py /b.py /c.py
662
+ replx touch /lib/__init__.py
663
+ ```
664
+
665
+ ---
666
+
667
+ ### Board Management
668
+
669
+ #### `usage`
670
+ Check RAM (`mem`) and filesystem (`df`) usage simultaneously. Shows a usage bar alongside Used/Free/Total values, making it ideal for capacity checks before uploading.
671
+
672
+ Usage:
673
+ ```sh
674
+ replx usage
675
+ replx PORT usage
676
+ ```
677
+
678
+ Examples:
679
+ ```sh
680
+ replx usage
681
+ replx COM3 usage
682
+ replx /dev/ttyACM0 usage
683
+ replx /dev/cu.usbmodem14101 usage
684
+ ```
685
+
686
+ #### `reset`
687
+ Reset the board's execution state. Default is `--soft` (restart the interpreter); `--hard` performs a hardware reset followed by automatic reconnection. Using `--soft` and `--hard` together is an error.
688
+
689
+ Usage:
690
+ ```sh
691
+ replx reset
692
+ replx reset --soft
693
+ replx reset --hard
694
+ ```
695
+
696
+ Examples:
697
+ ```sh
698
+ replx reset
699
+ replx reset --soft
700
+ replx reset --hard
701
+ ```
702
+
703
+ Options:
704
+ - `--soft`: restart the Python interpreter (default)
705
+ - `--hard`: hardware reset + auto-reconnect
706
+
707
+ #### `format`
708
+ Completely wipe the board filesystem and start fresh. All user files including `boot.py`, `main.py`, and `lib/` are deleted. Some boards may not support the format command — check the error message if it fails.
709
+
710
+ Usage:
711
+ ```sh
712
+ replx format
713
+ ```
714
+
715
+ Examples:
716
+ ```sh
717
+ replx get /*.py ./backup
718
+ replx get /lib ./backup
719
+ replx format
720
+ ```
721
+
722
+ #### `init`
723
+ Restore a board to its initial state and reinstall required libraries. Internally runs `format` followed by sequential core/device package installation. The local package store must be prepared first — run `replx pkg download` before using this for the first time.
724
+
725
+ Usage:
726
+ ```sh
727
+ replx init
728
+ ```
729
+
730
+ Examples:
731
+ ```sh
732
+ replx pkg download
733
+ replx init
734
+ ```
735
+
736
+ Operation order:
737
+ 1. Format filesystem
738
+ 2. Install core libraries
739
+ 3. Install device libraries
740
+
741
+ #### `wifi`
742
+ Manage the board's Wi-Fi status, connection, and boot auto-connect. `wifi connect SSID PW` saves credentials to the board (`wifi_config.py`) and connects immediately. `wifi connect` reconnects using saved settings. `wifi boot on|off` controls whether `boot.py` auto-connects on startup.
743
+
744
+ Usage:
745
+ ```sh
746
+ replx wifi
747
+ replx wifi connect SSID PW
748
+ replx wifi connect
749
+ replx wifi scan
750
+ replx wifi off
751
+ replx wifi boot on|off
752
+ ```
753
+
754
+ Examples:
755
+ ```sh
756
+ replx wifi
757
+ replx wifi scan
758
+ replx wifi connect MySSID MyPassword
759
+ replx wifi connect
760
+ replx wifi boot on
761
+ replx wifi off
762
+ ```
763
+
764
+ #### `firmware`
765
+ Download firmware locally or update via UF2 for supported boards. `download` only refreshes the local store; `update` puts the board into bootloader mode, locates the UF2 drive, and installs the firmware. `-f/--force` reinstalls even when the version matches. Currently officially supported boards: `ticle-lite`, `ticle-sensor`.
766
+
767
+ Usage:
768
+ ```sh
769
+ replx firmware download
770
+ replx firmware update
771
+ replx firmware update -f
772
+ ```
773
+
774
+ Options:
775
+ - `-f, --force`: force reinstall even if version is unchanged
776
+
777
+ Examples:
778
+ ```sh
779
+ replx firmware download
780
+ replx firmware update
781
+ replx firmware update --force
782
+ ```
783
+
784
+ ---
785
+
786
+ ### Package / Compile
787
+
788
+ #### `mip`
789
+ Install MicroPython packages from the official micropython.org index or directly from GitHub. All downloading and compilation happen on the PC — the board does not need a Wi-Fi connection. `.py` source files are compiled to `.mpy` using `mpy-cross` before upload. The target architecture is auto-detected from the connected board via `sys.implementation._mpy`.
790
+
791
+ For packages in the official index, pre-compiled `.mpy` files are fetched directly from the CDN when available (`hashes` field). For `github:org/repo` specs without a `package.json`, replx automatically queries the board's architecture and fetches pre-built `.mpy` binaries from the repository's GitHub Pages CDN (e.g. `emlearn-micropython`).
792
+
793
+ Usage:
794
+ ```sh
795
+ replx mip SUBCOMMAND [args]
796
+ ```
797
+
798
+ Subcommands:
799
+
800
+ | Subcommand | Description |
801
+ |---|---|
802
+ | `search [QUERY]` | List all packages or filter by name/description |
803
+ | `search github:org/repo` | Browse a GitHub repo's package structure |
804
+ | `install TARGET[@version]` | Download, compile (if needed), and install to board |
805
+
806
+ Install Targets:
807
+ ```sh
808
+ requests # latest version from official index
809
+ requests@0.10.0 # specific version
810
+ github:org/repo # GitHub repo root (package.json or CDN fallback)
811
+ github:org/repo/path # GitHub repo sub-path
812
+ https://example.com/f.mpy # direct URL
813
+ ```
814
+
815
+ Install Options:
816
+ - `--device PATH`: board destination path (default: `lib` → `/lib/`)
817
+ - `--no-compile`: upload `.py` files directly without `.mpy` compilation
818
+ - `--index URL`: use a custom package index instead of the default
819
+
820
+ Examples:
821
+ ```sh
822
+ replx mip search
823
+ replx mip search mqtt
824
+ replx mip search github:micropython/micropython-lib
825
+ replx mip install requests
826
+ replx mip install requests@0.10.0
827
+ replx mip install aioble
828
+ replx mip install github:emlearn/emlearn-micropython
829
+ replx mip install github:org/repo/src
830
+ replx mip install --device /lib/ext umqtt.simple
831
+ replx mip install --no-compile logging
832
+ ```
833
+
834
+ Notes:
835
+ - All network I/O happens on the PC — board WiFi is **not** required.
836
+ - `.py` files are compiled to `.mpy` before upload (requires `mpy-cross`).
837
+ - Dependencies declared in `deps` are installed automatically.
838
+ - For packages without `package.json` (e.g. native C modules), replx reads the board's `sys.implementation._mpy` to determine architecture (`armv6m`, `armv7emsp`, `xtensawin`, etc.) and fetches matching pre-built `.mpy` files from GitHub Pages.
839
+
840
+ ---
841
+
842
+ #### `pkg`
843
+ Manages the package workflow (`search → download → update`) between the GitHub remote registry, local store, and board. `search [QUERY]` shows results scoped to the connected board's core/device. `download` fetches the remote registry to the local store. `update TARGET` installs packages to the board using `core.all`, `device.all`, `core.<file>`, or `device.<file>`. `clean` removes the current core/device entries from the local store. `--owner/--repo/--ref` specify the GitHub remote and apply to `search/download/update`.
844
+
845
+ Usage:
846
+ ```sh
847
+ replx pkg SUBCOMMAND [args]
848
+ ```
849
+
850
+ Examples:
851
+ ```sh
852
+ replx pkg search
853
+ replx pkg search audio
854
+ replx pkg search --owner PlanXLab --repo replx_libs --ref main
855
+ replx pkg download
856
+ replx pkg download --owner PlanXLab --repo replx_libs --ref main
857
+ replx pkg update core.all
858
+ replx pkg update device.all
859
+ replx pkg update core.slip.py
860
+ replx pkg update device.termio.py
861
+ replx pkg clean
862
+ ```
863
+
864
+ > Note: `pkg search/download/update` operates within the **core/device scope of the currently connected board**.
865
+
866
+ #### `mpy`
867
+ Compile `.py` files to `.mpy` bytecode. Automatically selects the target architecture based on the connected board's `core` and version. `-o/--output` can only be used when compiling a single file. `mpy-cross` must be installed locally before use.
868
+
869
+ Usage:
870
+ ```sh
871
+ replx mpy FILES...
872
+ replx mpy main.py -o out.mpy
873
+ ```
874
+
875
+ Examples:
876
+ ```sh
877
+ replx mpy main.py
878
+ replx mpy main.py -o build/main.mpy
879
+ replx mpy *.py
880
+ replx mpy src/
881
+ ```
882
+
883
+ ---
884
+
885
+ ### Hardware
886
+
887
+ Hardware commands communicate with on-board peripherals through code generated and executed by the agent. Each command manages bus lifecycle (`open`/`close`), data transfer (`read`/`write`/`xfer`), and optional live monitoring. Pin names use the `GP<num>` format (case-insensitive). Bus settings are stored in agent memory until `close` or agent restart.
888
+
889
+ #### `gpio`
890
+ Read, write, and run sequences on a single GPIO pin. `read` performs a one-shot input read with optional actions (`wait_h`, `wait_l`, `pulse_h`, `pulse_l`). `write` drives the pin high or low. `seq` executes a write+delay pattern in a single board call. `monitor` samples the pin with IRQ edge capture and renders a live scope (requires `termviz` on the board).
891
+
892
+ Usage:
893
+ ```sh
894
+ replx gpio PIN read [ACTION]
895
+ replx gpio PIN write VALUE
896
+ replx gpio PIN seq TOKEN...
897
+ replx gpio PIN monitor
898
+ ```
899
+
900
+ Examples:
901
+ ```sh
902
+ replx gpio GP0 read
903
+ replx gpio GP0 read pulse_h --timeout 500
904
+ replx gpio GP15 write 1
905
+ replx gpio GP15 seq 1 m500 0 m500 --repeat 10
906
+ replx gpio GP2 monitor --interval 5
907
+ ```
908
+
909
+ Options:
910
+ - `--timeout MS`: action timeout (default: 100, 0 = unlimited)
911
+ - `--repeat N`: repeat count for `read`/`seq` (0 = unlimited, default: 1)
912
+ - `--interval MS`: monitor render interval (default: 10, min: 1)
913
+ - `--expr EXPR`: post-process expression; variables: `pulse_us`, `wait_ms`, `value`, `writes`
914
+
915
+ #### `pwm`
916
+ Generate PWM on a single pin. `write` starts or updates PWM with a frequency and one duty specification. `seq` runs a duty+delay pattern in one board call. `stop` halts PWM and drives the pin low. `monitor` measures PWM frequency and duty on an input pin using PIO (RP2350/RP2040) or `time_pulse_us` fallback.
917
+
918
+ Usage:
919
+ ```sh
920
+ replx pwm PIN write --freq HZ --duty-percent P
921
+ replx pwm PIN seq --freq HZ --duty percent TOKEN...
922
+ replx pwm PIN stop
923
+ replx pwm PIN monitor
924
+ ```
925
+
926
+ Examples:
927
+ ```sh
928
+ replx pwm GP15 write --freq 1000 --duty-percent 50
929
+ replx pwm GP15 write --freq 50 --pulse-us 1500
930
+ replx pwm GP15 seq --freq 1000 --duty percent 0 m200 25 m200 50 m200 75 m200 100 m200
931
+ replx pwm GP15 stop
932
+ replx pwm GP16 monitor --timeout 5000
933
+ ```
934
+
935
+ Options:
936
+ - `--freq HZ`: PWM frequency (required for `write`/`seq`)
937
+ - `--duty-percent P`: duty cycle 0–100 (`write`)
938
+ - `--duty-u16 N`: duty cycle 0–65535 (`write`)
939
+ - `--pulse-us US`: pulse width in microseconds (`write`)
940
+ - `--duty MODE`: duty basis for `seq` (`percent`, `u16`, `pulse_us`)
941
+ - `--repeat N`: seq repeat count (0 = unlimited, default: 1)
942
+ - `--timeout MS`: monitor signal timeout (default: 2000)
943
+
944
+ #### `adc`
945
+ Read analog pins or launch a board-side scope UI. `read` samples one to three ADC channels. `scope` runs an interactive waveform viewer on the board (requires `termviz` and `ufilter`).
946
+
947
+ Usage:
948
+ ```sh
949
+ replx adc PIN... read
950
+ replx adc PIN... scope
951
+ ```
952
+
953
+ Examples:
954
+ ```sh
955
+ replx adc GP26 read
956
+ replx adc GP26 GP27 read --repeat 0 --interval 500
957
+ replx adc GP26 GP27 GP28 scope --sample 10
958
+ replx adc GP26 scope --vref 3.3
959
+ ```
960
+
961
+ Options:
962
+ - `--repeat N`: read repeat count (0 = unlimited, default: 1)
963
+ - `--interval MS`: delay between repeated reads (default: 1000)
964
+ - `--vref V`: ADC reference voltage (default: 3.3)
965
+ - `--sample MS`: scope render speed (0, 1, 2, 5, 10, 20, 50, 100; default: 10)
966
+
967
+ #### `uart`
968
+ Manage a UART bus. `open` configures the bus; `write` sends text or hex bytes; `read` receives data; `xfer` writes then reads; `monitor` displays incoming data in real time; `bus` shows settings; `close` releases the peripheral.
969
+
970
+ Usage:
971
+ ```sh
972
+ replx uart open --tx PIN [--rx PIN] [--baud N]
973
+ replx uart write DATA
974
+ replx uart read [NBYTES]
975
+ replx uart xfer DATA [--rx-bytes N]
976
+ replx uart monitor
977
+ replx uart bus
978
+ replx uart close
979
+ ```
980
+
981
+ Examples:
982
+ ```sh
983
+ replx uart open --tx GP0 --rx GP1 --baud 9600
984
+ replx uart write "AT\r\n"
985
+ replx uart write --hex 0102FF
986
+ replx uart read 16 --timeout 3000
987
+ replx uart read --any
988
+ replx uart xfer "AT\r\n" --rx-bytes 32
989
+ replx uart monitor --text
990
+ replx uart monitor --chunk --idle 100
991
+ replx uart close
992
+ ```
993
+
994
+ Options:
995
+ - `--tx PIN`, `--rx PIN`: TX/RX pins (TX required for `open`)
996
+ - `--baud N`: baud rate (default: 115200)
997
+ - `--bits 7|8`, `--parity none|odd|even`, `--stop 1|2`: frame format
998
+ - `--timeout MS`: RX wait timeout (default: 2000, 0 = unlimited)
999
+ - `--any`: drain RX buffer immediately (`read` only)
1000
+ - `--hex`: send hex bytes instead of text (`write` only)
1001
+ - `--text`: stream UTF-8 (`monitor`)
1002
+ - `--chunk`: per-chunk display with timestamp (`monitor`)
1003
+ - `--idle MS`: silence separator threshold (`monitor`, default: 0 = off)
1004
+
1005
+ #### `spi`
1006
+ Manage an SPI bus in master or slave mode. `open` configures the bus; `write`/`read`/`xfer` transfer data; `bus` shows settings; `close` releases the peripheral. `--slave` activates PIO+DMA slave mode (RP2350 only).
1007
+
1008
+ Usage:
1009
+ ```sh
1010
+ replx spi open --sck PIN --mosi PIN [--miso PIN] [--baud N]
1011
+ replx spi write DATA [--cs PIN]
1012
+ replx spi read NBYTES [--cs PIN]
1013
+ replx spi xfer DATA [--cs PIN]
1014
+ replx spi bus
1015
+ replx spi close
1016
+ ```
1017
+
1018
+ Examples:
1019
+ ```sh
1020
+ replx spi open --sck GP2 --mosi GP3 --miso GP4 --baud 2000000
1021
+ replx spi write 0102FF --cs GP5
1022
+ replx spi read 8 --cs GP5 --fill FF
1023
+ replx spi xfer 0102FF --cs GP5
1024
+ replx spi open --sck GP2 --mosi GP3 --slave
1025
+ replx spi close
1026
+ ```
1027
+
1028
+ Options:
1029
+ - `--sck PIN`, `--mosi PIN`, `--miso PIN`: bus pins (SCK/MOSI required)
1030
+ - `--cs PIN`: chip-select pin (slave: `open` only; master: `write`/`read`/`xfer`)
1031
+ - `--baud N`: clock rate (default: 1000000)
1032
+ - `--mode 0-3`: SPI mode (default: 0)
1033
+ - `--bits 8|16`: word size (default: 8)
1034
+ - `--lsb`: LSB-first bit order
1035
+ - `--slave`: enable PIO+DMA slave mode (`open` only)
1036
+ - `--fill HH`: fill byte for master `read` (default: 00)
1037
+ - `--text`: text mode for `write`/`xfer`
1038
+ - `--timeout MS`: slave RX timeout (default: 10000)
1039
+
1040
+ #### `i2c`
1041
+ Manage an I2C bus in controller or target mode. `scan` discovers devices; `open` configures the bus; `read`/`write` transfer data; `dump` reads a register range; `seq` runs a write+delay sequence; `mem` reads the target memory buffer; `bus` shows settings; `close` releases the peripheral. `--target` activates I2CTarget mode (RP2350 only).
1042
+
1043
+ Usage:
1044
+ ```sh
1045
+ replx i2c scan --sda PIN --scl PIN
1046
+ replx i2c open --sda PIN --scl PIN [--freq HZ]
1047
+ replx i2c read ADDR NBYTES [--reg REG]
1048
+ replx i2c write ADDR DATA [--reg REG]
1049
+ replx i2c dump ADDR [REG_START] [REG_END]
1050
+ replx i2c seq ADDR TOKEN...
1051
+ replx i2c bus
1052
+ replx i2c close
1053
+ ```
1054
+
1055
+ Examples:
1056
+ ```sh
1057
+ replx i2c scan --sda GP0 --scl GP1
1058
+ replx i2c open --sda GP0 --scl GP1 --freq 400000
1059
+ replx i2c read 0x68 6 --reg 0x3B
1060
+ replx i2c read 0x68 6 --reg 0x3B --repeat 5 --interval 100
1061
+ replx i2c write 0x68 00 --reg 0x6B
1062
+ replx i2c dump 0x68
1063
+ replx i2c dump 0x68 0x3B 0x48
1064
+ replx i2c seq 0x68 6B00 m100 3B:6
1065
+ replx i2c open --sda GP0 --scl GP1 --target --addr 0x55 --mem-size 256
1066
+ replx i2c mem
1067
+ replx i2c close
1068
+ ```
1069
+
1070
+ Options:
1071
+ - `--sda PIN`, `--scl PIN`: I2C pins (required for `scan`/`open`)
1072
+ - `--freq HZ`: clock frequency (default: 400000)
1073
+ - `--reg REG`: register address for `read`/`write`
1074
+ - `--addr16`: use 16-bit register addresses
1075
+ - `--target`: enable I2CTarget mode (`open` only, RP2350)
1076
+ - `--addr 0xNN`: target address (`open --target`)
1077
+ - `--mem-size N`: target memory buffer size (`open --target`)
1078
+ - `--repeat N`: repeat count for `read`/`seq` (0 = unlimited, default: 1)
1079
+ - `--interval MS`: delay between repeats (default: 0)
1080
+
1081
+ #### `ble`
1082
+ Manage BLE (Bluetooth Low Energy) on the board. No subcommand shows adapter info and radio state. `on`/`off` toggles the radio. `scan` runs a passive advertisement scan with a live panel. `advertise` broadcasts an advertisement packet. `server` starts a GATT server; `client` connects to a GATT server. `ping` tests connectivity without subscribing. `read`/`write` perform one-shot characteristic access. `stream send/recv` use L2CAP CoC channels. Requires `aioble` on the board (`replx mip install aioble`).
1083
+
1084
+ Usage:
1085
+ ```sh
1086
+ replx ble
1087
+ replx ble on|off
1088
+ replx ble scan [REFRESH_MS]
1089
+ replx ble advertise DEVICE_NAME [OPTIONS]
1090
+ replx ble server --svc UUID --char UUID [OPTIONS]
1091
+ replx ble client ADDR/TYPE --svc UUID --char UUID [OPTIONS]
1092
+ replx ble ping ADDR/TYPE
1093
+ replx ble read ADDR SVC_UUID CHAR_UUID
1094
+ replx ble write ADDR SVC_UUID CHAR_UUID HEX
1095
+ replx ble stream send ADDR PSM HEX
1096
+ replx ble stream recv PSM
1097
+ ```
1098
+
1099
+ Advertise Options:
1100
+ - `--service-uuid UUID`: advertised service UUID
1101
+ - `--service-data UUID:hex:DATA` / `UUID:text:TEXT`: service data bytes or UTF-8 text
1102
+ - `--manufacturer-data COMPANY_ID:hex:DATA` / `COMPANY_ID:text:TEXT`: manufacturer data
1103
+ - `--duration-ms MS`: advertising interval in ms (default: 1000)
1104
+ - `--no-connect`: broadcaster mode (non-connectable)
1105
+
1106
+ Server Options:
1107
+ - `--svc UUID`, `--char UUID`: service/characteristic UUID
1108
+ - `--value hex:HEX` / `text:TEXT`: initial characteristic value
1109
+ - `--read`, `--write`, `--notify`, `--indicate`: GATT properties
1110
+ - `--notify-on-change`: auto-notify connected clients when value changes
1111
+ - `--name NAME`: GAP device name (default: replx-ble)
1112
+ - `--interval MS`: auto-notify interval in ms (requires `--notify`)
1113
+
1114
+ Client Options:
1115
+ - `--svc UUID`, `--char UUID`: service/characteristic UUID to connect to
1116
+ - `--value hex:HEX` / `text:TEXT`: initial value to write after connecting
1117
+ - `--notify`: subscribe to notifications after connecting
1118
+
1119
+ Address Format: `ADDR/1` for random address, `ADDR/0` for public. Use the Type shown by `replx ble scan`.
1120
+
1121
+ Examples:
1122
+ ```sh
1123
+ replx ble
1124
+ replx ble on
1125
+ replx ble scan
1126
+ replx ble advertise ticle-lite --service-data 1010:text:hello --no-connect
1127
+ replx ble ping 4D:0E:55:65:19:15/1
1128
+ replx ble server --svc 1010 --char 1011 --name ticle-lite --read --write --notify --notify-on-change
1129
+ replx ble client AA:BB:CC:DD:EE:FF/1 --svc 1010 --char 1011 --notify
1130
+ replx ble read AA:BB:CC:DD:EE:FF/1 1010 1011
1131
+ replx ble write AA:BB:CC:DD:EE:FF/1 1010 1011 0102FF
1132
+ replx ble stream send AA:BB:CC:DD:EE:FF/1 80 0102FF
1133
+ replx ble stream recv 80
1134
+ ```
1135
+
1136
+ Notes:
1137
+ - `scan`, `advertise`, `server`, `stream recv` run until stopped (q or Ctrl+C)
1138
+ - scan panel: press `s` to pause/resume, `f` to toggle named/all devices
1139
+ - server panel: press `v` to switch value view, `c` to change the value
1140
+ - client panel: press `r` to read, `w` to write, `n` to toggle notify
1141
+
1142
+ ---
1143
+
1144
+ ## Troubleshooting
1145
+
1146
+ #### Connection / Session
1147
+
1148
+ | Symptom | Likely Cause | Resolution |
1149
+ |---|---|---|
1150
+ | `No active connections` | `setup` not run, agent stopped | `replx scan` → `replx PORT setup` → `replx status` |
1151
+ | `Not connected` | No FG in current session | Check SID with `replx status`, then run `replx fg` or `replx PORT fg` |
1152
+ | Board shows busy in another terminal | Another SID has it as FG/BG | Finish work in that terminal, or `disconnect`; use `shutdown` if needed |
1153
+ | Port remains occupied | Agent still running | `replx disconnect` (individual) or `replx shutdown` (all) |
1154
+
1155
+ #### Execution (`exec/run/repl/shell`)
1156
+
1157
+ | Symptom | Likely Cause | Resolution |
1158
+ |---|---|---|
1159
+ | `run` fails immediately | Wrong file path | Local file: `run file.py`; board file: `run -d /path/file.py` |
1160
+ | `--non-interactive` and `--echo` conflict | Invalid option combination | Use only one of the two |
1161
+ | Hard to exit REPL | Unaware of exit method | Type `exit()` or press `Ctrl+D` |
1162
+ | Unexpected path in shell | Current directory confusion | Check with `pwd`, navigate with `cd` |
1163
+
1164
+ #### File Operations
1165
+
1166
+ | Symptom | Likely Cause | Resolution |
1167
+ |---|---|---|
1168
+ | Wildcard behaves unexpectedly | Local vs. board expansion difference | `put` expands locally; `get/rm/cp/mv` resolve patterns on the board |
1169
+ | Directory copy/move fails | Missing `-r` | Use `cp -r`, `mv -r`, `rm -r` |
1170
+ | Multiple sources but destination is a file | Misunderstanding of last-arg rule | The last argument is always DEST; it must be a directory for multiple sources |
1171
+ | Cannot recover after deletion | No backup | Back up with `replx get` before deleting |
1172
+
1173
+ #### Package / Compile
1174
+
1175
+ | Symptom | Likely Cause | Resolution |
1176
+ |---|---|---|
1177
+ | `mip install` — package not found | Typo or unofficial package | Try `replx mip search NAME` to confirm exact name |
1178
+ | `mip install github:org/repo` — 404 | Repo has no `package.json` | replx falls back to GitHub Pages CDN; if no CDN files exist for your arch, the package does not support your board |
1179
+ | `mip install` — arch not supported | Board arch not in package's prebuilt list | Check the repo for supported architectures; use `replx -c "import sys; print(sys.implementation._mpy)"` to confirm |
1180
+ | `mip install` — compile error | `mpy-cross` not installed or wrong version | `pip install mpy-cross`, or use `--no-compile` to skip compilation |
1181
+ | No `pkg search` results | Outside current board's core/device scope | Check board with `whoami`, switch board and search again |
1182
+ | `pkg update` fails | Local store not prepared | Run `replx pkg download` first, then retry `replx pkg update ...` |
1183
+ | `mpy` fails | `mpy-cross` not installed | `pip install mpy-cross` |
1184
+ | Architecture error | Missing board connection info | Ensure connection is established (`setup` or auto-connect) |
1185
+
1186
+ #### Format / Init / Firmware
1187
+
1188
+ | Symptom | Likely Cause | Resolution |
1189
+ |---|---|---|
1190
+ | Files lost after `format/init` | Expected behavior (full wipe) | Back up with `get` before formatting; restore with `put`/`pkg update` |
1191
+ | `firmware` unsupported error | Board not supported | Confirm board is a supported model (`ticle-*` series) |
1192
+ | `wifi connect` fails | Wrong SSID/PW, AP not visible | `wifi scan` → re-enter credentials → try `wifi off` then retry |
1193
+
1194
+ #### Hardware
1195
+
1196
+ | Symptom | Likely Cause | Resolution |
1197
+ |---|---|---|
1198
+ | `open` fails with pin error | Invalid pin for the board's channel mapping | Check pin constraints: RP2350 I2C requires SCL=SDA+1; SPI slave requires MOSI=SCK+1 |
1199
+ | `EFR32MG` not supported | I2C/SPI/PWM commands do not support EFR32MG | Use a supported core (RP2350, ESP32, Teensy) |
1200
+ | `monitor`/`scope` shows nothing | Missing board-side library | Install `termviz` (gpio/adc) or `ufilter` (adc scope) via `replx pkg` |
1201
+ | Slave/Target mode fails | Board not supported | PIO-based slave/target modes require RP2350 or RP2040 |
1202
+ | `seq` timeout | Sequence too long for default timeout | Increase `--timeout MS` or set to 0 for unlimited |
1203
+
1204
+ #### BLE
1205
+
1206
+ | Symptom | Likely Cause | Resolution |
1207
+ |---|---|---|
1208
+ | `aioble not found` | Library not installed on board | `replx mip install aioble` |
1209
+ | `ble scan` shows no devices | BLE radio off or no nearby advertisers | `replx ble on`, move devices closer, check `replx ble` for radio state |
1210
+ | `ble client` cannot connect | Wrong address type (random vs. public) | Use the Type shown by `replx ble scan` — `ADDR/1` (random) or `ADDR/0` (public) |
1211
+ | `ble server/client` exits immediately | UUID mismatch or missing `--svc`/`--char` | Confirm UUIDs on both sides; check with `replx ble scan` |
1212
+ | `ble advertise` no response | `--no-connect` set but client expects connectable | Remove `--no-connect` or use a connectable advertisement |
1213
+ | `ble stream` fails | L2CAP PSM not open on the other end | Ensure `stream recv PSM` is running before `stream send` |
1214
+
1215
+ ---