RemoteRF 0.0.7.47__tar.gz → 0.0.7.48__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.
- {remoterf-0.0.7.47/src/remoteRF.egg-info → remoterf-0.0.7.48}/PKG-INFO +1 -1
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/setup.py +17 -7
- remoterf-0.0.7.48/src/remoteRF/cli.py +141 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/config/cert_fetcher.py +0 -22
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/config/config_cli.py +55 -15
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/core/acc_login.py +1 -1
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/core/version.py +1 -1
- {remoterf-0.0.7.47 → remoterf-0.0.7.48/src/remoteRF.egg-info}/PKG-INFO +1 -1
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF.egg-info/SOURCES.txt +1 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF.egg-info/entry_points.txt +1 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/LICENSE +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/MANIFEST.in +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/README.md +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/setup.cfg +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/__init__.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/common/__init__.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/common/grpc/__init__.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/common/grpc/grpc_pb2.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/common/grpc/grpc_pb2_grpc.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/common/utils/__init__.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/common/utils/ansi_codes.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/common/utils/api_token.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/common/utils/list_string.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/common/utils/process_arg.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/config/__init__.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/core/__init__.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/core/app.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/core/grpc_acc.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/core/grpc_client.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/drivers/__init__.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/drivers/adalm_pluto/__init__.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF/drivers/adalm_pluto/pluto_remote.py +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF.egg-info/dependency_links.txt +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF.egg-info/requires.txt +0 -0
- {remoterf-0.0.7.47 → remoterf-0.0.7.48}/src/remoteRF.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: remoterf
|
|
3
|
-
Version: 0.0.7.
|
|
3
|
+
Version: 0.0.7.48
|
|
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="0.0.7.
|
|
9
|
+
version="0.0.7.48",
|
|
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.",
|
|
@@ -25,11 +25,21 @@ setup(
|
|
|
25
25
|
"Operating System :: OS Independent",
|
|
26
26
|
],
|
|
27
27
|
python_requires='>=3.10',
|
|
28
|
+
# entry_points={
|
|
29
|
+
# 'console_scripts': [
|
|
30
|
+
# 'remoterf-login=remoteRF.core.acc_login:main',
|
|
31
|
+
# 'remoterf-v=remoteRF.core.version:main',
|
|
32
|
+
# 'remoterf-config=remoteRF.config.config_cli:main',
|
|
33
|
+
# ],
|
|
34
|
+
# },
|
|
35
|
+
|
|
28
36
|
entry_points={
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
"console_scripts": [
|
|
38
|
+
"remoterf=remoteRF.cli:main",
|
|
39
|
+
"remoterf-login=remoteRF.core.acc_login:main",
|
|
40
|
+
"remoterf-v=remoteRF.core.version:main",
|
|
41
|
+
"remoterf-config=remoteRF.config.config_cli:main",
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
|
|
35
45
|
)
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# src/remoteRF/cli.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import sys
|
|
6
|
+
from typing import Optional, Sequence
|
|
7
|
+
|
|
8
|
+
from remoteRF.core.acc_login import main as login_main
|
|
9
|
+
from remoteRF.core.version import main as version_main
|
|
10
|
+
from remoteRF.config.config_cli import main as config_main
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
14
|
+
p = argparse.ArgumentParser(
|
|
15
|
+
prog="remoterf",
|
|
16
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
17
|
+
add_help=True,
|
|
18
|
+
description=(
|
|
19
|
+
"RemoteRF unified CLI\n\n"
|
|
20
|
+
"Examples:\n"
|
|
21
|
+
" remoterf --login\n"
|
|
22
|
+
" remoterf -l\n"
|
|
23
|
+
" remoterf --config --addr 164.67.195.207:61005\n"
|
|
24
|
+
" remoterf -c -a 164.67.195.207:61005\n"
|
|
25
|
+
" remoterf --config --wipe\n"
|
|
26
|
+
" remoterf -v\n"
|
|
27
|
+
),
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
mode = p.add_mutually_exclusive_group()
|
|
31
|
+
|
|
32
|
+
mode.add_argument("-l", "--login", action="store_true", help="Login / register (interactive).")
|
|
33
|
+
mode.add_argument("-c", "--config", action="store_true", help="Configure server target / CA cert.")
|
|
34
|
+
mode.add_argument("-v", "--version", action="store_true", help="Print version and exit.")
|
|
35
|
+
|
|
36
|
+
# These are only meaningful with --config, but we accept them at top-level
|
|
37
|
+
# so your UX works as requested.
|
|
38
|
+
p.add_argument(
|
|
39
|
+
"-a", "--addr", "-addr",
|
|
40
|
+
dest="addr",
|
|
41
|
+
default=None,
|
|
42
|
+
help="Server address (host:port). Used with --config / -c.",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
p.add_argument(
|
|
46
|
+
"-w", "--wipe",
|
|
47
|
+
action="store_true",
|
|
48
|
+
help="Wipe local config (~/.config/remoterf). Used with --config / -c.",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
p.add_argument(
|
|
52
|
+
"--yes",
|
|
53
|
+
action="store_true",
|
|
54
|
+
help="Skip confirmation prompts (use with --config --wipe).",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
return p
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
61
|
+
argv = list(sys.argv[1:] if argv is None else argv)
|
|
62
|
+
|
|
63
|
+
parser = build_parser()
|
|
64
|
+
|
|
65
|
+
# No args => help panel
|
|
66
|
+
if len(argv) == 0:
|
|
67
|
+
parser.print_help()
|
|
68
|
+
return 0
|
|
69
|
+
|
|
70
|
+
args, extra = parser.parse_known_args(argv)
|
|
71
|
+
|
|
72
|
+
# --- Version ---
|
|
73
|
+
if args.version:
|
|
74
|
+
if extra:
|
|
75
|
+
parser.error(f"Unexpected arguments for --version: {' '.join(extra)}")
|
|
76
|
+
# If your version_main supports argv, pass [].
|
|
77
|
+
try:
|
|
78
|
+
version_main([])
|
|
79
|
+
except TypeError:
|
|
80
|
+
version_main()
|
|
81
|
+
return 0
|
|
82
|
+
|
|
83
|
+
# --- Login ---
|
|
84
|
+
if args.login:
|
|
85
|
+
if extra:
|
|
86
|
+
parser.error(f"Unexpected arguments for --login: {' '.join(extra)}")
|
|
87
|
+
try:
|
|
88
|
+
login_main([])
|
|
89
|
+
except TypeError:
|
|
90
|
+
login_main()
|
|
91
|
+
return 0
|
|
92
|
+
|
|
93
|
+
# --- Config ---
|
|
94
|
+
if args.config:
|
|
95
|
+
cfg_argv: list[str] = []
|
|
96
|
+
|
|
97
|
+
# Normalize to whatever config_cli expects.
|
|
98
|
+
# If your config_cli supports --wipe/--yes, pass them through.
|
|
99
|
+
if args.wipe:
|
|
100
|
+
cfg_argv.append("--wipe")
|
|
101
|
+
if args.yes:
|
|
102
|
+
cfg_argv.append("--yes")
|
|
103
|
+
|
|
104
|
+
# Address handling
|
|
105
|
+
if args.addr:
|
|
106
|
+
# If config_cli expects a positional addr, pass it positionally:
|
|
107
|
+
cfg_argv.append(args.addr)
|
|
108
|
+
# If your config_cli instead expects --addr, switch to:
|
|
109
|
+
# cfg_argv += ["--addr", args.addr]
|
|
110
|
+
|
|
111
|
+
# Preserve any extra args for config_cli (if you want)
|
|
112
|
+
cfg_argv += extra
|
|
113
|
+
|
|
114
|
+
# If neither addr nor wipe was supplied, show config help-like message.
|
|
115
|
+
if not args.addr and not args.wipe and len(extra) == 0:
|
|
116
|
+
# Call config_cli with --help if it supports it; otherwise print ours.
|
|
117
|
+
try:
|
|
118
|
+
config_main(["--help"])
|
|
119
|
+
except Exception:
|
|
120
|
+
parser.error("Config mode requires --addr <host:port> or --wipe.")
|
|
121
|
+
return 0
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
config_main(cfg_argv)
|
|
125
|
+
except TypeError:
|
|
126
|
+
# Fallback if config_main does not accept argv; last resort:
|
|
127
|
+
old = sys.argv[:]
|
|
128
|
+
try:
|
|
129
|
+
sys.argv = ["remoterf-config", *cfg_argv]
|
|
130
|
+
config_main()
|
|
131
|
+
finally:
|
|
132
|
+
sys.argv = old
|
|
133
|
+
return 0
|
|
134
|
+
|
|
135
|
+
# If user passed only -a/-w without -c/--config, show help
|
|
136
|
+
parser.print_help()
|
|
137
|
+
return 0
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
if __name__ == "__main__":
|
|
141
|
+
raise SystemExit(main())
|
|
@@ -1,26 +1,4 @@
|
|
|
1
1
|
# cert_fetcher.py
|
|
2
|
-
"""
|
|
3
|
-
RemoteRF Client-Side Cert Fetcher (bootstrap)
|
|
4
|
-
|
|
5
|
-
Purpose:
|
|
6
|
-
- Fetch the public CA certificate (ca.crt) from a server's bootstrap endpoint
|
|
7
|
-
(your cert_provider) and save it locally for later use in:
|
|
8
|
-
grpc.ssl_channel_credentials(root_certificates=ca_bytes)
|
|
9
|
-
|
|
10
|
-
Security note (TOFU):
|
|
11
|
-
- This fetch is typically done over plaintext HTTP (or raw TCP). The CA cert is public,
|
|
12
|
-
but authenticity is not guaranteed on first fetch. If you want TOFU done "right",
|
|
13
|
-
you should display the SHA256 fingerprint and require user confirmation elsewhere.
|
|
14
|
-
This module focuses only on: fetch + save + return success.
|
|
15
|
-
|
|
16
|
-
Behavior:
|
|
17
|
-
- Attempts HTTP GET first: http://{host}:{port}/ca.crt
|
|
18
|
-
- If HTTP fails, falls back to raw TCP (reads until EOF).
|
|
19
|
-
- Saves to a per-profile file under ~/.config/remoterf/certs/ by default.
|
|
20
|
-
|
|
21
|
-
Exported API:
|
|
22
|
-
- fetch_and_save_ca_cert(host, port, *, out_path=None, profile=None, timeout_sec=3.0) -> bool
|
|
23
|
-
"""
|
|
24
2
|
|
|
25
3
|
from __future__ import annotations
|
|
26
4
|
|
|
@@ -2,19 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import argparse
|
|
6
5
|
import os
|
|
7
6
|
import sys
|
|
7
|
+
import shutil
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Tuple
|
|
10
10
|
|
|
11
|
-
from .cert_fetcher import fetch_and_save_ca_cert
|
|
11
|
+
from .cert_fetcher import fetch_and_save_ca_cert
|
|
12
12
|
|
|
13
13
|
# -----------------------------
|
|
14
14
|
# Local config locations
|
|
15
15
|
# -----------------------------
|
|
16
16
|
def _config_root() -> Path:
|
|
17
|
-
# Keep it simple and consistent with earlier suggestions.
|
|
18
17
|
return Path(os.path.expanduser("~")) / ".config" / "remoterf"
|
|
19
18
|
|
|
20
19
|
def _env_path() -> Path:
|
|
@@ -40,16 +39,10 @@ def _parse_hostport(s: str) -> Tuple[str, int]:
|
|
|
40
39
|
raise ValueError("Port out of range")
|
|
41
40
|
return host, port
|
|
42
41
|
|
|
43
|
-
|
|
44
42
|
def _write_env_kv(path: Path, kv: dict[str, str]) -> None:
|
|
45
|
-
"""
|
|
46
|
-
Writes/overwrites a simple dotenv-style file.
|
|
47
|
-
(We overwrite the whole file for simplicity and correctness.)
|
|
48
|
-
"""
|
|
49
43
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
50
|
-
lines = []
|
|
44
|
+
lines: list[str] = []
|
|
51
45
|
for k, v in kv.items():
|
|
52
|
-
# Quote only if needed; keep simple and safe.
|
|
53
46
|
if any(c.isspace() for c in v) or any(c in v for c in ['"', "'"]):
|
|
54
47
|
v = v.replace('"', '\\"')
|
|
55
48
|
lines.append(f'{k}="{v}"')
|
|
@@ -57,9 +50,29 @@ def _write_env_kv(path: Path, kv: dict[str, str]) -> None:
|
|
|
57
50
|
lines.append(f"{k}={v}")
|
|
58
51
|
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
59
52
|
|
|
60
|
-
def
|
|
53
|
+
def _confirm_wipe(root: Path) -> bool:
|
|
54
|
+
prompt = (
|
|
55
|
+
f"This will permanently delete ALL RemoteRF config at:\n"
|
|
56
|
+
f" {root}\n\n"
|
|
57
|
+
f"Type 'wipe' to confirm: "
|
|
58
|
+
)
|
|
59
|
+
try:
|
|
60
|
+
return input(prompt).strip().lower() == "wipe"
|
|
61
|
+
except KeyboardInterrupt:
|
|
62
|
+
print("\nCancelled.")
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
def _wipe_config(root: Path) -> None:
|
|
66
|
+
if not root.exists():
|
|
67
|
+
print(f"No config found at: {root}")
|
|
68
|
+
return
|
|
69
|
+
if not root.is_dir():
|
|
70
|
+
raise RuntimeError(f"Config root exists but is not a directory: {root}")
|
|
71
|
+
shutil.rmtree(root)
|
|
72
|
+
print(f"Wiped RemoteRF config: {root}")
|
|
73
|
+
|
|
74
|
+
def main(argv=None) -> None:
|
|
61
75
|
import argparse
|
|
62
|
-
import sys
|
|
63
76
|
|
|
64
77
|
parser = argparse.ArgumentParser(
|
|
65
78
|
prog="remoterf-config",
|
|
@@ -77,13 +90,41 @@ def main() -> None:
|
|
|
77
90
|
help="Server address in host:port form. Example: 164.67.195.207:61005",
|
|
78
91
|
)
|
|
79
92
|
|
|
93
|
+
parser.add_argument(
|
|
94
|
+
"--wipe",
|
|
95
|
+
action="store_true",
|
|
96
|
+
help="Delete ALL existing RemoteRF config at ~/.config/remoterf and exit.",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
parser.add_argument(
|
|
100
|
+
"--yes",
|
|
101
|
+
action="store_true",
|
|
102
|
+
help="Skip confirmation prompts (use with --wipe).",
|
|
103
|
+
)
|
|
104
|
+
|
|
80
105
|
args = parser.parse_args()
|
|
81
106
|
|
|
107
|
+
# ----- Wipe mode -----
|
|
108
|
+
if args.wipe:
|
|
109
|
+
root = _config_root()
|
|
110
|
+
if not args.yes:
|
|
111
|
+
if not _confirm_wipe(root):
|
|
112
|
+
print("Wipe aborted.")
|
|
113
|
+
sys.exit(1)
|
|
114
|
+
try:
|
|
115
|
+
_wipe_config(root)
|
|
116
|
+
sys.exit(0)
|
|
117
|
+
except Exception as e:
|
|
118
|
+
print(f"Error wiping config: {e}", file=sys.stderr)
|
|
119
|
+
sys.exit(1)
|
|
120
|
+
|
|
121
|
+
# ----- Normal configure mode -----
|
|
82
122
|
if not args.addr:
|
|
83
123
|
print(
|
|
84
124
|
"\nError: missing required argument: host:port\n\n"
|
|
85
125
|
"Usage:\n"
|
|
86
|
-
" remoterf-config <host:port>\n
|
|
126
|
+
" remoterf-config <host:port>\n"
|
|
127
|
+
" remoterf-config --wipe [--yes]\n\n"
|
|
87
128
|
"Example:\n"
|
|
88
129
|
" remoterf-config 164.67.195.207:61005\n"
|
|
89
130
|
)
|
|
@@ -95,8 +136,7 @@ def main() -> None:
|
|
|
95
136
|
print(f"Error: invalid addr '{args.addr}': {e}", file=sys.stderr)
|
|
96
137
|
sys.exit(2)
|
|
97
138
|
|
|
98
|
-
|
|
99
|
-
cert_port = grpc_port + 1 # cert-provider convention
|
|
139
|
+
cert_port = grpc_port + 1
|
|
100
140
|
profile = "default"
|
|
101
141
|
timeout_sec = 3.0
|
|
102
142
|
overwrite = True
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: remoterf
|
|
3
|
-
Version: 0.0.7.
|
|
3
|
+
Version: 0.0.7.48
|
|
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
|
|
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
|