RemoteRF 1.1.8__tar.gz → 2.0.1__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 (40) hide show
  1. remoterf-2.0.1/MANIFEST.in +3 -0
  2. {remoterf-1.1.8/src/remoteRF.egg-info → remoterf-2.0.1}/PKG-INFO +1 -1
  3. {remoterf-1.1.8 → remoterf-2.0.1}/setup.py +1 -1
  4. remoterf-2.0.1/src/remoteRF/common/tos_notice.txt +9 -0
  5. remoterf-2.0.1/src/remoteRF/common/utils/ansi_codes.py +178 -0
  6. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/common/utils/process_arg.py +2 -0
  7. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/config/cert_fetcher.py +3 -3
  8. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/config/config.py +86 -7
  9. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/core/app.py +189 -98
  10. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/core/grpc_acc.py +8 -2
  11. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/core/grpc_client.py +34 -7
  12. remoterf-2.0.1/src/remoteRF/drivers/__init__.py +1 -0
  13. remoterf-2.0.1/src/remoteRF/drivers/dynamic_device.py +322 -0
  14. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/remoterf_cli.py +26 -24
  15. {remoterf-1.1.8 → remoterf-2.0.1/src/remoteRF.egg-info}/PKG-INFO +1 -1
  16. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF.egg-info/SOURCES.txt +3 -0
  17. remoterf-1.1.8/MANIFEST.in +0 -2
  18. remoterf-1.1.8/src/remoteRF/common/utils/ansi_codes.py +0 -120
  19. {remoterf-1.1.8 → remoterf-2.0.1}/LICENSE +0 -0
  20. {remoterf-1.1.8 → remoterf-2.0.1}/README.md +0 -0
  21. {remoterf-1.1.8 → remoterf-2.0.1}/setup.cfg +0 -0
  22. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/__init__.py +0 -0
  23. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/common/__init__.py +0 -0
  24. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/common/grpc/__init__.py +0 -0
  25. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/common/grpc/grpc_pb2.py +0 -0
  26. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/common/grpc/grpc_pb2_grpc.py +0 -0
  27. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/common/utils/__init__.py +0 -0
  28. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/common/utils/api_token.py +0 -0
  29. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/common/utils/list_string.py +0 -0
  30. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/config/__init__.py +0 -0
  31. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/core/__init__.py +0 -0
  32. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/core/acc_login.py +0 -0
  33. /remoterf-1.1.8/src/remoteRF/drivers/__init__.py → /remoterf-2.0.1/src/remoteRF/core/banner_note.txt +0 -0
  34. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/drivers/adalm_pluto/__init__.py +0 -0
  35. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/drivers/adalm_pluto/pluto_remote.py +0 -0
  36. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF/version.py +0 -0
  37. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF.egg-info/dependency_links.txt +0 -0
  38. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF.egg-info/entry_points.txt +0 -0
  39. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF.egg-info/requires.txt +0 -0
  40. {remoterf-1.1.8 → remoterf-2.0.1}/src/remoteRF.egg-info/top_level.txt +0 -0
@@ -0,0 +1,3 @@
1
+ # Include all .crt, .key, and .pem files
2
+ recursive-include src/remoteRF *.crt *.key *.pem
3
+ recursive-include src/remoteRF *.txt
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoterf
3
- Version: 1.1.8
3
+ Version: 2.0.1
4
4
  Summary: A python API to remotely access signal centric hardware. Client-side only! Courtesy of Wireless Lab @ UCLA & Prof. Ian Roberts.
5
5
  Author: Ethan Ge
6
6
  Author-email: ethoGalaxy@gmail.com
@@ -6,7 +6,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
6
6
 
7
7
  setup(
8
8
  name="remoterf",
9
- version="1.1.8",
9
+ version="2.0.1",
10
10
  author="Ethan Ge",
11
11
  author_email="ethoGalaxy@gmail.com",
12
12
  description="A python API to remotely access signal centric hardware. Client-side only! Courtesy of Wireless Lab @ UCLA & Prof. Ian Roberts.",
@@ -0,0 +1,9 @@
1
+ RemoteRF 2026
2
+ Terms of Service Acknowledgement
3
+
4
+ By continuing, you confirm that you have reviewed and agree to the
5
+ RemoteRF Terms of Service and understand that use of the client and
6
+ service is at your own risk. RemoteRF is not liable for losses or
7
+ damages arising from use of the platform.
8
+
9
+ Terms of Service: https://remoterf.net/tos
@@ -0,0 +1,178 @@
1
+ import os
2
+ import sys
3
+ from enum import Enum
4
+
5
+ from prompt_toolkit import print_formatted_text
6
+ from prompt_toolkit.formatted_text import FormattedText
7
+
8
+ class Sty(Enum):
9
+ # Basic colors
10
+ RED = 'red'
11
+ GREEN = 'green'
12
+ BLUE = 'blue'
13
+ YELLOW = 'yellow'
14
+ MAGENTA = 'magenta'
15
+ CYAN = 'cyan'
16
+ GRAY = 'gray'
17
+
18
+ # Background colors
19
+ BG_RED = 'bg-red'
20
+ BG_GREEN = 'bg-green'
21
+ BG_BLUE = 'bg-blue'
22
+
23
+ # Bright versions
24
+ BRIGHT_RED = 'bright-red'
25
+ BRIGHT_GREEN = 'bright-green'
26
+ BRIGHT_BLUE = 'bright-blue'
27
+
28
+ # Formatting
29
+ BOLD = 'bold'
30
+ ITALIC = 'italic'
31
+ UNDERLINE = 'underline'
32
+ BLINK = 'blink'
33
+ REVERSE = 'reverse'
34
+
35
+ # Combinations
36
+ ERROR = 'error'
37
+ WARNING = 'warning'
38
+ INFO = 'info'
39
+
40
+ # Special
41
+ SELECTED = 'selected'
42
+ DEFAULT = 'default'
43
+
44
+ _COLOR_STYLE_MAP = {
45
+ 'red': 'fg:ansired',
46
+ 'green': 'fg:ansigreen',
47
+ 'blue': 'fg:ansiblue',
48
+ 'yellow': 'fg:ansiyellow',
49
+ 'magenta': 'fg:ansimagenta',
50
+ 'cyan': 'fg:ansicyan',
51
+ 'gray': 'fg:ansibrightblack',
52
+ 'bg-red': 'bg:ansired',
53
+ 'bg-green': 'bg:ansigreen',
54
+ 'bg-blue': 'bg:ansiblue',
55
+ 'bright-red': 'fg:ansibrightred',
56
+ 'bright-green': 'fg:ansibrightgreen',
57
+ 'bright-blue': 'fg:ansibrightblue',
58
+ }
59
+
60
+ _MONO_STYLES = {
61
+ 'bold': 'bold',
62
+ 'italic': 'italic',
63
+ 'underline': 'underline',
64
+ 'blink': 'blink',
65
+ 'reverse': 'reverse',
66
+ 'error': 'bold',
67
+ 'warning': 'bold',
68
+ 'info': 'italic underline',
69
+ 'selected': 'reverse',
70
+ 'default': '',
71
+ }
72
+
73
+ _COLOR_COMBINATIONS = {
74
+ 'error': 'fg:ansired bold',
75
+ 'warning': 'fg:ansiyellow bold',
76
+ 'info': 'fg:ansiblue italic underline',
77
+ 'selected': 'reverse',
78
+ }
79
+
80
+
81
+ def _env_flag(name: str) -> str:
82
+ return os.environ.get(name, '').strip().lower()
83
+
84
+
85
+ def _supports_color() -> bool:
86
+ override = _env_flag('REMOTERF_COLOR')
87
+ if override in {'0', 'false', 'no', 'off'}:
88
+ return False
89
+ if override in {'1', 'true', 'yes', 'on'}:
90
+ return True
91
+
92
+ if _env_flag('NO_COLOR'):
93
+ return False
94
+
95
+ term = _env_flag('TERM')
96
+ if term == 'dumb':
97
+ return False
98
+
99
+ try:
100
+ if not sys.stdout.isatty():
101
+ return False
102
+ except Exception:
103
+ return False
104
+
105
+ if sys.platform != 'win32':
106
+ return True
107
+
108
+ # Only use color in Windows terminals that commonly support ANSI output.
109
+ return any([
110
+ bool(os.environ.get('WT_SESSION')),
111
+ bool(os.environ.get('ANSICON')),
112
+ os.environ.get('ConEmuANSI', '').upper() == 'ON',
113
+ bool(os.environ.get('COLORTERM')),
114
+ bool(os.environ.get('TERM_PROGRAM')),
115
+ term not in {'', 'dumb'},
116
+ ])
117
+
118
+
119
+ def _style_string(styles) -> str:
120
+ parts = []
121
+ use_color = _supports_color()
122
+ for style_name in styles:
123
+ if use_color and style_name in _COLOR_COMBINATIONS:
124
+ style_value = _COLOR_COMBINATIONS[style_name]
125
+ elif use_color and style_name in _COLOR_STYLE_MAP:
126
+ style_value = _COLOR_STYLE_MAP[style_name]
127
+ else:
128
+ style_value = _MONO_STYLES.get(style_name, '')
129
+
130
+ if not style_value:
131
+ continue
132
+ parts.append(style_value)
133
+ return ' '.join(parts)
134
+
135
+ def printf(*args) -> str:
136
+ if len(args) % 2 != 0:
137
+ raise ValueError('Arguments must be in pairs of two.')
138
+
139
+ # Create formatted text using the defined style
140
+ formatted_text = []
141
+
142
+ for i in range(0, len(args), 2):
143
+ message = args[i]
144
+ styles = args[i+1]
145
+
146
+ if not isinstance(styles, tuple):
147
+ styles = (styles,)
148
+
149
+ resolved_styles = (s.value if isinstance(s, Enum) else s for s in styles)
150
+
151
+ formatted_text.append((_style_string(resolved_styles), message))
152
+
153
+ # Create FormattedText object from pairs
154
+ text = FormattedText(formatted_text)
155
+
156
+ print_formatted_text(text)
157
+ return text
158
+
159
+ def stylize(*args):
160
+ """
161
+ Create a styled prompt text based on pairs of (text, (Sty, ...), ...).
162
+ """
163
+ if len(args) % 2 != 0:
164
+ raise ValueError("Arguments must be in pairs of (text, style_class).")
165
+
166
+ styled_parts = []
167
+ for i in range(0, len(args), 2):
168
+ text = args[i]
169
+ styles = args[i + 1]
170
+
171
+ if not isinstance(styles, tuple):
172
+ styles = (styles,)
173
+
174
+ resolved_styles = (s.value if isinstance(s, Enum) else s for s in styles)
175
+
176
+ styled_parts.append((_style_string(resolved_styles), text))
177
+
178
+ return FormattedText(styled_parts)
@@ -31,6 +31,8 @@ def map_arg(value):
31
31
  arg.string_value = value
32
32
  elif isinstance(value, bool):
33
33
  arg.bool_value = value
34
+ elif isinstance(value, (list, tuple)):
35
+ return map_arg(np.array(value))
34
36
  elif isinstance(value, np.ndarray):
35
37
  if np.iscomplexobj(value):
36
38
  complex_array = arg.complex_array
@@ -12,7 +12,7 @@ from typing import Optional
12
12
  CERT_FILENAME_DEFAULT = "ca.crt"
13
13
 
14
14
  def _default_config_dir() -> Path:
15
- return Path(os.path.expanduser("~")) / ".config" / "remoterf"
15
+ return Path(os.path.expanduser("~")) / ".config" / "remoterf-client"
16
16
 
17
17
  def _default_env_path() -> Path:
18
18
  return _default_config_dir() / ".env"
@@ -119,12 +119,12 @@ def fetch_and_save_ca_cert(
119
119
  env_path: Optional[str | Path] = None,
120
120
  ) -> bool:
121
121
  """
122
- Fetch CA cert and save. Optionally writes REMOTERF_CA_CERT into ~/.config/remoterf/.env
122
+ Fetch CA cert and save. Optionally writes REMOTERF_CA_CERT into ~/.config/remoterf-client/.env
123
123
  WITHOUT quotes.
124
124
 
125
125
  Args:
126
126
  write_env: if True, updates REMOTERF_CA_CERT in env file to point at saved cert
127
- env_path: override env file location; default ~/.config/remoterf/.env
127
+ env_path: override env file location; default ~/.config/remoterf-client/.env
128
128
  """
129
129
  try:
130
130
  if not isinstance(port, int):
@@ -9,13 +9,26 @@ from pathlib import Path
9
9
  from typing import Optional, Sequence, Tuple
10
10
  import argparse
11
11
 
12
+ from ..common.utils import Sty, printf
12
13
  from .cert_fetcher import fetch_and_save_ca_cert
13
14
 
15
+ DEFAULT_TOS_NOTICE = (
16
+ "RemoteRF 2025\n"
17
+ "Terms of Service Acknowledgement\n"
18
+ "\n"
19
+ "By continuing, you confirm that you have reviewed and agree to the\n"
20
+ "RemoteRF Terms of Service and understand that use of the client and\n"
21
+ "service is at your own risk. RemoteRF is not liable for losses or\n"
22
+ "damages arising from use of the platform.\n"
23
+ "\n"
24
+ "Terms of Service: https://remoterf.net/tos\n"
25
+ )
26
+
14
27
  # -----------------------------
15
28
  # Local config locations
16
29
  # -----------------------------
17
30
  def _config_root() -> Path:
18
- return Path(os.path.expanduser("~")) / ".config" / "remoterf"
31
+ return Path(os.path.expanduser("~")) / ".config" / "remoterf-client"
19
32
 
20
33
  def _env_path() -> Path:
21
34
  return _config_root() / ".env"
@@ -72,6 +85,72 @@ def _wipe_config(root: Path) -> None:
72
85
  shutil.rmtree(root)
73
86
  print(f"Wiped RemoteRF config: {root}")
74
87
 
88
+
89
+ def _tos_notice_path() -> Path:
90
+ return Path(__file__).resolve().parents[1] / "common" / "tos_notice.txt"
91
+
92
+
93
+ def _read_tos_notice() -> str:
94
+ try:
95
+ text = _tos_notice_path().read_text(encoding="utf-8").strip()
96
+ except OSError:
97
+ return DEFAULT_TOS_NOTICE.strip()
98
+ return text or DEFAULT_TOS_NOTICE.strip()
99
+
100
+
101
+ def _print_separator() -> None:
102
+ printf("=" * 60, Sty.GRAY)
103
+
104
+
105
+ def _print_tos_notice() -> None:
106
+ for i, line in enumerate(_read_tos_notice().splitlines()):
107
+ stripped = line.strip()
108
+ if not stripped:
109
+ print()
110
+ continue
111
+
112
+ if i == 0:
113
+ printf(stripped, (Sty.BOLD, Sty.BLUE))
114
+ continue
115
+
116
+ if i == 1:
117
+ printf(stripped, (Sty.BOLD, Sty.MAGENTA))
118
+ continue
119
+
120
+ if stripped.lower().startswith("terms of service:"):
121
+ label, value = stripped.split(":", 1)
122
+ printf(f"{label}: ", (Sty.BOLD, Sty.DEFAULT), value.strip(), (Sty.CYAN, Sty.UNDERLINE))
123
+ continue
124
+
125
+ printf(stripped, Sty.DEFAULT)
126
+
127
+
128
+ def _print_config_summary(host: str, grpc_port: int, cert_port: int, ca_out: Path, env_file: Path) -> None:
129
+ _print_separator()
130
+ printf("Configuration Complete!", (Sty.BOLD, Sty.GREEN))
131
+ printf("- Details:", Sty.BOLD)
132
+ printf(" gRPC target: ", (Sty.BOLD, Sty.DEFAULT), f"{host}:{grpc_port}", Sty.CYAN)
133
+ printf(" Cert port : ", (Sty.BOLD, Sty.DEFAULT), f"{host}:{cert_port}", Sty.CYAN)
134
+ printf(" CA cert : ", (Sty.BOLD, Sty.DEFAULT), f"{ca_out}", Sty.GRAY)
135
+ printf(" Env file : ", (Sty.BOLD, Sty.DEFAULT), f"{env_file}", Sty.GRAY)
136
+ _print_separator()
137
+
138
+
139
+ def _confirm_tos() -> bool:
140
+ _print_separator()
141
+ _print_tos_notice()
142
+ _print_separator()
143
+ try:
144
+ reply = input("Continue with configuration? [y/N]: ").strip().lower()
145
+ except KeyboardInterrupt:
146
+ print("\nConfiguration cancelled.")
147
+ return False
148
+ except EOFError:
149
+ print("\nConfiguration cancelled.")
150
+ return False
151
+ print()
152
+ return reply in {"y", "yes"}
153
+
75
154
  def configure(host: str, port: int, cert_port: int) -> int:
76
155
  # Basic validation
77
156
  host = (host or "").strip()
@@ -89,6 +168,10 @@ def configure(host: str, port: int, cert_port: int) -> int:
89
168
  timeout_sec = 3.0
90
169
  overwrite = True
91
170
 
171
+ if not _confirm_tos():
172
+ print("Configuration cancelled. Accept the Terms of Service to continue.")
173
+ return 1
174
+
92
175
  certs_dir = _certs_dir()
93
176
  certs_dir.mkdir(parents=True, exist_ok=True)
94
177
  ca_out = certs_dir / f"{profile}.crt"
@@ -111,11 +194,7 @@ def configure(host: str, port: int, cert_port: int) -> int:
111
194
  "REMOTERF_PROFILE": profile,
112
195
  })
113
196
 
114
- print("RemoteRF configured successfully.")
115
- print(f" gRPC target : {host}:{grpc_port}")
116
- print(f" cert port : {host}:{cert_port}")
117
- print(f" CA cert : {ca_out}")
118
- print(f" env file : {env_file}")
197
+ _print_config_summary(host, grpc_port, cert_port, ca_out, env_file)
119
198
 
120
199
  def wipe_config(*, yes: bool = False) -> int:
121
200
  """
@@ -130,4 +209,4 @@ def wipe_config(*, yes: bool = False) -> int:
130
209
  return 0
131
210
  except Exception as e:
132
211
  print(f"Error wiping config: {e}", file=sys.stderr)
133
- return 1
212
+ return 1