RemoteRF 2.0.1__tar.gz → 2.0.3__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-2.0.1/src/remoteRF.egg-info → remoterf-2.0.3}/PKG-INFO +1 -1
- {remoterf-2.0.1 → remoterf-2.0.3}/setup.py +1 -1
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/common/grpc/grpc_pb2.py +7 -7
- remoterf-2.0.3/src/remoteRF/common/utils/process_arg.py +157 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/core/app.py +7 -2
- remoterf-2.0.3/src/remoteRF/drivers/dynamic_device.py +1452 -0
- remoterf-2.0.3/src/remoteRF/drivers/support/__init__.py +1 -0
- remoterf-2.0.3/src/remoteRF/drivers/support/uhd.py +397 -0
- {remoterf-2.0.1 → remoterf-2.0.3/src/remoteRF.egg-info}/PKG-INFO +1 -1
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF.egg-info/SOURCES.txt +5 -1
- remoterf-2.0.3/tests/test_dynamic_device_codegen.py +964 -0
- remoterf-2.0.3/tests/test_process_arg.py +110 -0
- remoterf-2.0.1/src/remoteRF/common/utils/process_arg.py +0 -82
- remoterf-2.0.1/src/remoteRF/drivers/dynamic_device.py +0 -322
- {remoterf-2.0.1 → remoterf-2.0.3}/LICENSE +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/MANIFEST.in +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/README.md +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/setup.cfg +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/__init__.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/common/__init__.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/common/grpc/__init__.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/common/grpc/grpc_pb2_grpc.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/common/tos_notice.txt +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/common/utils/__init__.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/common/utils/ansi_codes.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/common/utils/api_token.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/common/utils/list_string.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/config/__init__.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/config/cert_fetcher.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/config/config.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/core/__init__.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/core/acc_login.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/core/banner_note.txt +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/core/grpc_acc.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/core/grpc_client.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/drivers/__init__.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/drivers/adalm_pluto/__init__.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/drivers/adalm_pluto/pluto_remote.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/remoterf_cli.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF/version.py +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF.egg-info/dependency_links.txt +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF.egg-info/entry_points.txt +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF.egg-info/requires.txt +0 -0
- {remoterf-2.0.1 → remoterf-2.0.3}/src/remoteRF.egg-info/top_level.txt +0 -0
|
@@ -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="2.0.
|
|
9
|
+
version="2.0.3",
|
|
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.",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
3
|
# NO CHECKED-IN PROTOBUF GENCODE
|
|
4
4
|
# source: grpc.proto
|
|
5
|
-
# Protobuf Python Version: 5.
|
|
5
|
+
# Protobuf Python Version: 5.27.1
|
|
6
6
|
"""Generated protocol buffer code."""
|
|
7
7
|
from google.protobuf import descriptor as _descriptor
|
|
8
8
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
@@ -12,8 +12,8 @@ from google.protobuf.internal import builder as _builder
|
|
|
12
12
|
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
13
13
|
_runtime_version.Domain.PUBLIC,
|
|
14
14
|
5,
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
27,
|
|
16
|
+
1,
|
|
17
17
|
'',
|
|
18
18
|
'grpc.proto'
|
|
19
19
|
)
|
|
@@ -24,7 +24,7 @@ _sym_db = _symbol_database.Default()
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ngrpc.proto\x12\tremote_rf\"\xa2\x01\n\x11GenericRPCRequest\x12\x15\n\rfunction_name\x18\x01 \x01(\t\x12\x34\n\x04\x61rgs\x18\x02 \x03(\x0b\x32&.remote_rf.GenericRPCRequest.ArgsEntry\x1a@\n\tArgsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.remote_rf.Argument:\x02\x38\x01\"\x96\x01\n\x12GenericRPCResponse\x12;\n\x07results\x18\x01 \x03(\x0b\x32*.remote_rf.GenericRPCResponse.ResultsEntry\x1a\x43\n\x0cResultsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.remote_rf.Argument:\x02\x38\x01\"\x19\n\nArrayShape\x12\x0b\n\x03\x64im\x18\x01 \x03(\x05\"+\n\rComplexNumber\x12\x0c\n\x04real\x18\x01 \x01(\x02\x12\x0c\n\x04imag\x18\x02 \x01(\x02\"a\n\x11\x43omplexNumpyArray\x12$\n\x05shape\x18\x01 \x01(\x0b\x32\x15.remote_rf.ArrayShape\x12&\n\x04\x64\x61ta\x18\x02 \x03(\x0b\x32\x18.remote_rf.ComplexNumber\"D\n\x0eRealNumpyArray\x12$\n\x05shape\x18\x01 \x01(\x0b\x32\x15.remote_rf.ArrayShape\x12\x0c\n\x04\x64\x61ta\x18\x02 \x03(\x02\"\
|
|
27
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ngrpc.proto\x12\tremote_rf\"\xa2\x01\n\x11GenericRPCRequest\x12\x15\n\rfunction_name\x18\x01 \x01(\t\x12\x34\n\x04\x61rgs\x18\x02 \x03(\x0b\x32&.remote_rf.GenericRPCRequest.ArgsEntry\x1a@\n\tArgsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.remote_rf.Argument:\x02\x38\x01\"\x96\x01\n\x12GenericRPCResponse\x12;\n\x07results\x18\x01 \x03(\x0b\x32*.remote_rf.GenericRPCResponse.ResultsEntry\x1a\x43\n\x0cResultsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.remote_rf.Argument:\x02\x38\x01\"\x19\n\nArrayShape\x12\x0b\n\x03\x64im\x18\x01 \x03(\x05\"+\n\rComplexNumber\x12\x0c\n\x04real\x18\x01 \x01(\x02\x12\x0c\n\x04imag\x18\x02 \x01(\x02\"a\n\x11\x43omplexNumpyArray\x12$\n\x05shape\x18\x01 \x01(\x0b\x32\x15.remote_rf.ArrayShape\x12&\n\x04\x64\x61ta\x18\x02 \x03(\x0b\x32\x18.remote_rf.ComplexNumber\"D\n\x0eRealNumpyArray\x12$\n\x05shape\x18\x01 \x01(\x0b\x32\x15.remote_rf.ArrayShape\x12\x0c\n\x04\x64\x61ta\x18\x02 \x03(\x02\"\xed\x01\n\x08\x41rgument\x12\x16\n\x0cstring_value\x18\x01 \x01(\tH\x00\x12\x15\n\x0bint64_value\x18\x02 \x01(\x03H\x00\x12\x15\n\x0b\x66loat_value\x18\x03 \x01(\x02H\x00\x12\x14\n\nbool_value\x18\x04 \x01(\x08H\x00\x12\x35\n\rcomplex_array\x18\x05 \x01(\x0b\x32\x1c.remote_rf.ComplexNumpyArrayH\x00\x12/\n\nreal_array\x18\x06 \x01(\x0b\x32\x19.remote_rf.RealNumpyArrayH\x00\x12\x14\n\njson_value\x18\x07 \x01(\tH\x00\x42\x07\n\x05value2Q\n\nGenericRPC\x12\x43\n\x04\x43\x61ll\x12\x1c.remote_rf.GenericRPCRequest\x1a\x1d.remote_rf.GenericRPCResponseB\x1e\n\x10\x63om.example.demoB\nDemoProtosb\x06proto3')
|
|
28
28
|
|
|
29
29
|
_globals = globals()
|
|
30
30
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
@@ -53,7 +53,7 @@ if not _descriptor._USE_C_DESCRIPTORS:
|
|
|
53
53
|
_globals['_REALNUMPYARRAY']._serialized_start=514
|
|
54
54
|
_globals['_REALNUMPYARRAY']._serialized_end=582
|
|
55
55
|
_globals['_ARGUMENT']._serialized_start=585
|
|
56
|
-
_globals['_ARGUMENT']._serialized_end=
|
|
57
|
-
_globals['_GENERICRPC']._serialized_start=
|
|
58
|
-
_globals['_GENERICRPC']._serialized_end=
|
|
56
|
+
_globals['_ARGUMENT']._serialized_end=822
|
|
57
|
+
_globals['_GENERICRPC']._serialized_start=824
|
|
58
|
+
_globals['_GENERICRPC']._serialized_end=905
|
|
59
59
|
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
from ..grpc import grpc_pb2, grpc_pb2_grpc
|
|
2
|
+
import json
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
_JSON_TYPE_KEY = "__remoterf_json_type__"
|
|
6
|
+
|
|
7
|
+
def _json_safe(value):
|
|
8
|
+
if hasattr(value, "as_payload"):
|
|
9
|
+
return _json_safe(value.as_payload())
|
|
10
|
+
if isinstance(value, np.ndarray):
|
|
11
|
+
if value.dtype == object:
|
|
12
|
+
raise ValueError(f"Cannot JSON-map object-dtype array: {value!r}")
|
|
13
|
+
array = np.asarray(value)
|
|
14
|
+
if np.iscomplexobj(array):
|
|
15
|
+
data = [[float(num.real), float(num.imag)] for num in array.ravel()]
|
|
16
|
+
return {
|
|
17
|
+
_JSON_TYPE_KEY: "ndarray",
|
|
18
|
+
"dtype": str(array.dtype),
|
|
19
|
+
"shape": list(array.shape),
|
|
20
|
+
"complex": True,
|
|
21
|
+
"data": data,
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
_JSON_TYPE_KEY: "ndarray",
|
|
25
|
+
"dtype": str(array.dtype),
|
|
26
|
+
"shape": list(array.shape),
|
|
27
|
+
"complex": False,
|
|
28
|
+
"data": array.tolist(),
|
|
29
|
+
}
|
|
30
|
+
if isinstance(value, (np.bool_,)):
|
|
31
|
+
return bool(value)
|
|
32
|
+
if isinstance(value, (np.integer,)):
|
|
33
|
+
return int(value)
|
|
34
|
+
if isinstance(value, (np.floating,)):
|
|
35
|
+
return float(value)
|
|
36
|
+
if isinstance(value, complex):
|
|
37
|
+
return {_JSON_TYPE_KEY: "complex", "real": float(value.real), "imag": float(value.imag)}
|
|
38
|
+
if isinstance(value, dict):
|
|
39
|
+
return {str(key): _json_safe(item) for key, item in value.items()}
|
|
40
|
+
if isinstance(value, (list, tuple)):
|
|
41
|
+
return [_json_safe(item) for item in value]
|
|
42
|
+
return value
|
|
43
|
+
|
|
44
|
+
def _json_restore(value):
|
|
45
|
+
if isinstance(value, dict):
|
|
46
|
+
kind = value.get(_JSON_TYPE_KEY)
|
|
47
|
+
if kind == "ndarray":
|
|
48
|
+
shape = tuple(value.get("shape", ()))
|
|
49
|
+
dtype = value.get("dtype")
|
|
50
|
+
if value.get("complex"):
|
|
51
|
+
data = [complex(real, imag) for real, imag in value.get("data", [])]
|
|
52
|
+
return np.array(data, dtype=dtype or np.complex64).reshape(shape)
|
|
53
|
+
return np.array(value.get("data", []), dtype=dtype).reshape(shape)
|
|
54
|
+
if kind == "complex":
|
|
55
|
+
return complex(value.get("real", 0.0), value.get("imag", 0.0))
|
|
56
|
+
return {key: _json_restore(item) for key, item in value.items()}
|
|
57
|
+
if isinstance(value, list):
|
|
58
|
+
return [_json_restore(item) for item in value]
|
|
59
|
+
return value
|
|
60
|
+
|
|
61
|
+
def unmap_arg(arg):
|
|
62
|
+
if arg.HasField('int64_value'):
|
|
63
|
+
return arg.int64_value
|
|
64
|
+
elif arg.HasField('float_value'):
|
|
65
|
+
return arg.float_value
|
|
66
|
+
elif arg.HasField('string_value'):
|
|
67
|
+
return arg.string_value
|
|
68
|
+
elif arg.HasField('bool_value'):
|
|
69
|
+
return arg.bool_value
|
|
70
|
+
elif arg.HasField('real_array'):
|
|
71
|
+
shape = tuple(arg.real_array.shape.dim)
|
|
72
|
+
return np.array(arg.real_array.data, dtype=np.float64).reshape(shape)
|
|
73
|
+
elif arg.HasField('complex_array'):
|
|
74
|
+
shape = tuple(arg.complex_array.shape.dim)
|
|
75
|
+
data = [complex(c.real, c.imag) for c in arg.complex_array.data]
|
|
76
|
+
return np.array(data, dtype=np.complex64).reshape(shape)
|
|
77
|
+
elif arg.HasField('json_value'):
|
|
78
|
+
return _json_restore(json.loads(arg.json_value))
|
|
79
|
+
else:
|
|
80
|
+
raise ValueError(f"Unknown argument type during unmapping: {arg}")
|
|
81
|
+
|
|
82
|
+
def map_arg(value):
|
|
83
|
+
arg = grpc_pb2.Argument()
|
|
84
|
+
|
|
85
|
+
if isinstance(value, (bool, np.bool_)):
|
|
86
|
+
arg.bool_value = bool(value)
|
|
87
|
+
elif hasattr(value, "as_payload"):
|
|
88
|
+
return map_arg(value.as_payload())
|
|
89
|
+
elif value is None or isinstance(value, dict):
|
|
90
|
+
arg.json_value = json.dumps(_json_safe(value), separators=(",", ":"))
|
|
91
|
+
elif isinstance(value, (int, np.integer)):
|
|
92
|
+
arg.int64_value = int(value)
|
|
93
|
+
elif isinstance(value, (float, np.floating)):
|
|
94
|
+
arg.float_value = float(value)
|
|
95
|
+
elif isinstance(value, str):
|
|
96
|
+
arg.string_value = value
|
|
97
|
+
elif isinstance(value, (list, tuple)):
|
|
98
|
+
try:
|
|
99
|
+
array = np.asarray(value)
|
|
100
|
+
except Exception:
|
|
101
|
+
arg.json_value = json.dumps(_json_safe(value), separators=(",", ":"))
|
|
102
|
+
return arg
|
|
103
|
+
if array.dtype == object:
|
|
104
|
+
try:
|
|
105
|
+
array = np.stack(value)
|
|
106
|
+
except Exception:
|
|
107
|
+
arg.json_value = json.dumps(_json_safe(value), separators=(",", ":"))
|
|
108
|
+
return arg
|
|
109
|
+
return map_arg(array)
|
|
110
|
+
elif isinstance(value, np.ndarray):
|
|
111
|
+
if value.dtype == object:
|
|
112
|
+
raise ValueError(f"Cannot map object-dtype array: {value!r}")
|
|
113
|
+
if np.iscomplexobj(value):
|
|
114
|
+
complex_array = arg.complex_array
|
|
115
|
+
complex_array.shape.dim.extend(value.shape)
|
|
116
|
+
for num in value.ravel():
|
|
117
|
+
complex_num = complex_array.data.add()
|
|
118
|
+
complex_num.real = float(num.real)
|
|
119
|
+
complex_num.imag = float(num.imag)
|
|
120
|
+
else:
|
|
121
|
+
float_array = arg.real_array
|
|
122
|
+
float_array.shape.dim.extend(value.shape)
|
|
123
|
+
float_array.data.extend(np.asarray(value, dtype=np.float32).ravel())
|
|
124
|
+
else:
|
|
125
|
+
raise ValueError(f"Unknown argument type during mapping: {value}")
|
|
126
|
+
return arg
|
|
127
|
+
|
|
128
|
+
def map_array_proto(np_array):
|
|
129
|
+
arg = grpc_pb2.Argument()
|
|
130
|
+
|
|
131
|
+
# Check if the array is complex
|
|
132
|
+
if np.iscomplexobj(np_array):
|
|
133
|
+
complex_array = grpc_pb2.ComplexArray()
|
|
134
|
+
for num in np_array.flat:
|
|
135
|
+
complex_number = complex_array.data.add()
|
|
136
|
+
complex_number.real = num.real
|
|
137
|
+
complex_number.imag = num.imag
|
|
138
|
+
arg.complex_array.CopyFrom(complex_array)
|
|
139
|
+
else:
|
|
140
|
+
# Handle as a regular float array
|
|
141
|
+
float_array = grpc_pb2.FloatArray()
|
|
142
|
+
float_array.data.extend(np_array.flat)
|
|
143
|
+
arg.float_array.CopyFrom(float_array)
|
|
144
|
+
|
|
145
|
+
return arg
|
|
146
|
+
|
|
147
|
+
def unmap_array_proto(arg):
|
|
148
|
+
# Check which type of array is available and convert appropriately
|
|
149
|
+
if arg.HasField('complex_array'):
|
|
150
|
+
# Convert ComplexArray to a numpy array of complex numbers
|
|
151
|
+
data = [complex(cn.real, cn.imag) for cn in arg.complex_array.data]
|
|
152
|
+
return np.array(data, dtype=np.complex64)
|
|
153
|
+
elif arg.HasField('float_array'):
|
|
154
|
+
# Convert FloatArray to a numpy array of floats
|
|
155
|
+
return np.array(arg.float_array.data, dtype=np.float32)
|
|
156
|
+
else:
|
|
157
|
+
raise ValueError("Argument does not contain a recognizable array.")
|
|
@@ -17,6 +17,7 @@ session = PromptSession()
|
|
|
17
17
|
DEFAULT_TIMEZONE_NOTE = "All times are in Pacific Time (Los Angeles)"
|
|
18
18
|
DEFAULT_TOS_URL = "https://remoterf.net/tos"
|
|
19
19
|
SERVER_TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
|
|
20
|
+
BANNER_CREDIT = "Created at UCLA by E. Ge and I. Roberts."
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
def _banner_note() -> str:
|
|
@@ -28,6 +29,10 @@ def _banner_note() -> str:
|
|
|
28
29
|
return text or DEFAULT_TIMEZONE_NOTE
|
|
29
30
|
|
|
30
31
|
|
|
32
|
+
def _banner_details() -> str:
|
|
33
|
+
return f"\n{BANNER_CREDIT}\nCurrent version: {print_my_version()}\n{_banner_note()}"
|
|
34
|
+
|
|
35
|
+
|
|
31
36
|
def _tos_notice_text() -> str:
|
|
32
37
|
notice_path = Path(__file__).resolve().parents[1] / "common" / "tos_notice.txt"
|
|
33
38
|
try:
|
|
@@ -66,7 +71,7 @@ def _format_reservation_range(start_time: datetime.datetime, end_time: datetime.
|
|
|
66
71
|
return f"{start} - {end}"
|
|
67
72
|
|
|
68
73
|
def welcome():
|
|
69
|
-
printf(f"Welcome to the RemoteRF Platform", (Sty.BOLD, Sty.BLUE),
|
|
74
|
+
printf(f"Welcome to the RemoteRF Platform", (Sty.BOLD, Sty.BLUE), _banner_details(), (Sty.GRAY))
|
|
70
75
|
try:
|
|
71
76
|
inpu = session.prompt(stylize("Please ", Sty.DEFAULT, "login", Sty.GREEN, " or ", Sty.DEFAULT, "register", Sty.RED, " to continue. (", Sty.DEFAULT, 'l', Sty.GREEN, "/", Sty.DEFAULT, 'r', Sty.RED, "): ", Sty.DEFAULT))
|
|
72
77
|
if inpu == 'r':
|
|
@@ -103,7 +108,7 @@ def welcome():
|
|
|
103
108
|
exit()
|
|
104
109
|
|
|
105
110
|
def title():
|
|
106
|
-
printf(f"Welcome to the RemoteRF Platform", (Sty.BOLD, Sty.BLUE),
|
|
111
|
+
printf(f"Welcome to the RemoteRF Platform", (Sty.BOLD, Sty.BLUE), _banner_details(), (Sty.GRAY))
|
|
107
112
|
printf("Terms of Service: ", Sty.BOLD, _tos_url(), Sty.DEFAULT)
|
|
108
113
|
# printf(f"Logged in as: ", Sty.DEFAULT, f'{account.username}', Sty.MAGENTA)
|
|
109
114
|
printf(f"Input ", Sty.DEFAULT, "'help' ", Sty.BRIGHT_GREEN, "for a list of avaliable commands.", Sty.DEFAULT)
|