remotivelabs-cli 0.3.0__py3-none-any.whl → 0.3.2__py3-none-any.whl
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 remotivelabs-cli might be problematic. Click here for more details.
- cli/broker/__init__.py +36 -0
- cli/broker/discovery.py +43 -0
- cli/broker/export.py +6 -36
- cli/broker/files.py +12 -12
- cli/broker/lib/broker.py +13 -18
- cli/broker/lib/helper.py +6 -7
- cli/broker/license_flows.py +11 -13
- cli/broker/playback.py +10 -10
- cli/broker/record.py +4 -4
- cli/broker/scripting.py +6 -9
- cli/broker/signals.py +17 -19
- cli/cloud/auth/cmd.py +17 -14
- cli/cloud/auth/login.py +16 -26
- cli/cloud/auth_tokens.py +9 -13
- cli/cloud/brokers.py +5 -9
- cli/cloud/configs.py +4 -17
- cli/cloud/organisations.py +8 -10
- cli/cloud/projects.py +3 -3
- cli/cloud/recordings.py +35 -61
- cli/cloud/recordings_playback.py +22 -22
- cli/cloud/resumable_upload.py +6 -6
- cli/cloud/service_account_tokens.py +3 -2
- cli/cloud/storage/cmd.py +2 -3
- cli/cloud/storage/copy.py +2 -1
- cli/connect/connect.py +4 -4
- cli/connect/protopie/protopie.py +21 -29
- cli/remotive.py +4 -7
- cli/settings/core.py +4 -2
- cli/settings/migration/migration_tools.py +2 -1
- cli/tools/can/can.py +4 -7
- cli/topology/__init__.py +3 -0
- cli/topology/cmd.py +61 -83
- cli/topology/start_trial.py +105 -0
- cli/typer/typer_utils.py +3 -6
- cli/utils/console.py +61 -0
- cli/utils/rest_helper.py +23 -16
- cli/utils/versions.py +2 -4
- {remotivelabs_cli-0.3.0.dist-info → remotivelabs_cli-0.3.2.dist-info}/METADATA +1 -1
- remotivelabs_cli-0.3.2.dist-info/RECORD +74 -0
- cli/broker/brokers.py +0 -93
- cli/errors.py +0 -44
- remotivelabs_cli-0.3.0.dist-info/RECORD +0 -71
- {remotivelabs_cli-0.3.0.dist-info → remotivelabs_cli-0.3.2.dist-info}/LICENSE +0 -0
- {remotivelabs_cli-0.3.0.dist-info → remotivelabs_cli-0.3.2.dist-info}/WHEEL +0 -0
- {remotivelabs_cli-0.3.0.dist-info → remotivelabs_cli-0.3.2.dist-info}/entry_points.txt +0 -0
cli/broker/signals.py
CHANGED
|
@@ -12,10 +12,9 @@ from typing import Any, Dict, Iterable, List, TypedDict, Union
|
|
|
12
12
|
import grpc
|
|
13
13
|
import plotext as plt # type: ignore
|
|
14
14
|
import typer
|
|
15
|
-
from rich import print as rich_rprint
|
|
16
15
|
|
|
17
|
-
from cli.errors import ErrorPrinter
|
|
18
16
|
from cli.typer import typer_utils
|
|
17
|
+
from cli.utils.console import print_generic_error, print_generic_message, print_grpc_error, print_hint
|
|
19
18
|
|
|
20
19
|
from .lib.broker import Broker, SubscribableSignal
|
|
21
20
|
|
|
@@ -23,8 +22,6 @@ app = typer_utils.create_typer(help=help)
|
|
|
23
22
|
|
|
24
23
|
DEFAULT_GRPC_URL = "http://localhost:50051"
|
|
25
24
|
|
|
26
|
-
# signal_values:list = list()
|
|
27
|
-
|
|
28
25
|
|
|
29
26
|
class Signals(TypedDict):
|
|
30
27
|
name: str
|
|
@@ -49,18 +46,18 @@ def list_signals(
|
|
|
49
46
|
try:
|
|
50
47
|
broker = Broker(url, api_key)
|
|
51
48
|
available_signals = broker.list_signal_names(prefix=name_starts_with, suffix=name_ends_with)
|
|
52
|
-
|
|
49
|
+
print_generic_message(json.dumps(available_signals))
|
|
53
50
|
except grpc.RpcError as rpc_error:
|
|
54
|
-
|
|
51
|
+
print_grpc_error(rpc_error)
|
|
55
52
|
|
|
56
53
|
|
|
57
54
|
def read_scripted_code_file(file_path: Path) -> bytes:
|
|
58
55
|
try:
|
|
59
|
-
|
|
56
|
+
print_generic_message(str(file_path))
|
|
60
57
|
with open(file_path, "rb") as file:
|
|
61
58
|
return file.read()
|
|
62
59
|
except FileNotFoundError:
|
|
63
|
-
|
|
60
|
+
print_generic_error("File not found. Please check your file path.")
|
|
64
61
|
sys.exit(1)
|
|
65
62
|
|
|
66
63
|
|
|
@@ -100,12 +97,12 @@ def subscribe( # noqa: C901, PLR0913, PLR0915
|
|
|
100
97
|
|
|
101
98
|
if script is None:
|
|
102
99
|
if len(signal) == 0:
|
|
103
|
-
|
|
100
|
+
print_generic_error("You must use --signal or use --script when subscribing")
|
|
104
101
|
sys.exit(1)
|
|
105
102
|
|
|
106
103
|
if script is not None:
|
|
107
104
|
if len(signal) > 0:
|
|
108
|
-
|
|
105
|
+
print_generic_error("You must must not specify --signal when using --script")
|
|
109
106
|
sys.exit(1)
|
|
110
107
|
|
|
111
108
|
plt.title("Signals")
|
|
@@ -153,7 +150,8 @@ def subscribe( # noqa: C901, PLR0913, PLR0915
|
|
|
153
150
|
plt.show()
|
|
154
151
|
|
|
155
152
|
def on_frame_print(x: Iterable[Any]) -> None:
|
|
156
|
-
|
|
153
|
+
# TODO: use log instead of print for debug information?
|
|
154
|
+
print_generic_message(json.dumps(list(x)))
|
|
157
155
|
|
|
158
156
|
os_signal.signal(os_signal.SIGINT, exit_on_ctrlc)
|
|
159
157
|
|
|
@@ -172,7 +170,7 @@ def subscribe( # noqa: C901, PLR0913, PLR0915
|
|
|
172
170
|
def to_subscribable_signal(sig: str):
|
|
173
171
|
arr = sig.split(":")
|
|
174
172
|
if len(arr) != 2:
|
|
175
|
-
|
|
173
|
+
print_hint(f"--signal must have format namespace:signal ({sig})")
|
|
176
174
|
sys.exit(1)
|
|
177
175
|
return SubscribableSignal(namespace=arr[0], name=arr[1])
|
|
178
176
|
|
|
@@ -180,9 +178,9 @@ def subscribe( # noqa: C901, PLR0913, PLR0915
|
|
|
180
178
|
|
|
181
179
|
broker = Broker(url, api_key)
|
|
182
180
|
broker.long_name_subscribe(signals_to_subscribe_to, on_frame_func, on_change_only)
|
|
183
|
-
|
|
181
|
+
print_generic_message("Subscribing to signals, press Ctrl+C to exit")
|
|
184
182
|
except grpc.RpcError as rpc_error:
|
|
185
|
-
|
|
183
|
+
print_grpc_error(rpc_error)
|
|
186
184
|
|
|
187
185
|
|
|
188
186
|
@app.command(help="List namespaces on broker")
|
|
@@ -193,9 +191,9 @@ def namespaces(
|
|
|
193
191
|
try:
|
|
194
192
|
broker = Broker(url, api_key)
|
|
195
193
|
namespaces_json = broker.list_namespaces()
|
|
196
|
-
|
|
194
|
+
print_generic_message(json.dumps(namespaces_json))
|
|
197
195
|
except grpc.RpcError as rpc_error:
|
|
198
|
-
|
|
196
|
+
print_grpc_error(rpc_error)
|
|
199
197
|
|
|
200
198
|
|
|
201
199
|
@app.command()
|
|
@@ -214,11 +212,11 @@ def frame_distribution(
|
|
|
214
212
|
timestamp: str = datetime.now().strftime("%H:%M:%S")
|
|
215
213
|
distribution = data["countsByFrameId"]
|
|
216
214
|
if len(distribution) == 0:
|
|
217
|
-
|
|
215
|
+
print_hint(f"{timestamp} - No frames available")
|
|
218
216
|
else:
|
|
219
217
|
for d in distribution:
|
|
220
|
-
|
|
218
|
+
print_generic_message(f"{timestamp}: {d}")
|
|
221
219
|
|
|
222
220
|
broker.listen_on_frame_distribution(namespace, on_data)
|
|
223
221
|
except grpc.RpcError as rpc_error:
|
|
224
|
-
|
|
222
|
+
print_grpc_error(rpc_error)
|
cli/cloud/auth/cmd.py
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
import sys
|
|
4
5
|
|
|
5
6
|
import typer
|
|
6
7
|
from rich.console import Console
|
|
7
8
|
from rich.table import Table
|
|
8
9
|
|
|
9
10
|
from cli.cloud.auth.login import login as do_login
|
|
10
|
-
from cli.errors import ErrorPrinter
|
|
11
11
|
from cli.settings import settings
|
|
12
12
|
from cli.typer import typer_utils
|
|
13
|
+
from cli.utils.console import print_generic_error, print_generic_message, print_success, print_unformatted
|
|
13
14
|
from cli.utils.rest_helper import RestHelper as Rest
|
|
14
15
|
|
|
15
16
|
from .. import auth_tokens
|
|
@@ -52,24 +53,24 @@ def print_access_token(
|
|
|
52
53
|
if not account:
|
|
53
54
|
active_token = settings.get_active_token() or os.getenv("REMOTIVE_CLOUD_ACCESS_TOKEN", None)
|
|
54
55
|
if not active_token:
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
print_generic_error("You have no active account")
|
|
57
|
+
sys.exit(1)
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
print_generic_message(active_token)
|
|
59
60
|
return
|
|
60
61
|
|
|
61
62
|
accounts = settings.list_accounts()
|
|
62
63
|
if account not in accounts:
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
print_generic_error(f"No account for {account} was found")
|
|
65
|
+
sys.exit(1)
|
|
65
66
|
|
|
66
67
|
token_file_name = accounts[account].credentials_file
|
|
67
68
|
token_file = settings.get_token_file(token_file_name)
|
|
68
69
|
if not token_file:
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
print_generic_error(f"Token file for {account} could not be found")
|
|
71
|
+
sys.exit(1)
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
print_generic_message(token_file.token)
|
|
73
74
|
|
|
74
75
|
|
|
75
76
|
def print_access_token_file() -> None:
|
|
@@ -78,10 +79,10 @@ def print_access_token_file() -> None:
|
|
|
78
79
|
"""
|
|
79
80
|
active_token_file = settings.get_active_token_file()
|
|
80
81
|
if not active_token_file:
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
print_generic_error("You have no active account")
|
|
83
|
+
sys.exit(1)
|
|
83
84
|
|
|
84
|
-
|
|
85
|
+
print_generic_message(str(active_token_file))
|
|
85
86
|
|
|
86
87
|
|
|
87
88
|
@app.command(name="deactivate")
|
|
@@ -90,7 +91,7 @@ def deactivate() -> None:
|
|
|
90
91
|
Clears active account
|
|
91
92
|
"""
|
|
92
93
|
settings.clear_active_account()
|
|
93
|
-
|
|
94
|
+
print_success("Account no longer active")
|
|
94
95
|
|
|
95
96
|
|
|
96
97
|
@app.command("activate")
|
|
@@ -105,6 +106,8 @@ def activate(token_name: str = typer.Argument(None, help="Name, filename or path
|
|
|
105
106
|
def list() -> None:
|
|
106
107
|
"""
|
|
107
108
|
Lists available credential files on filesystem
|
|
109
|
+
|
|
110
|
+
TODO: Support output format
|
|
108
111
|
"""
|
|
109
112
|
accounts = settings.list_accounts()
|
|
110
113
|
|
|
@@ -123,4 +126,4 @@ def list() -> None:
|
|
|
123
126
|
str(token_file.created) if token_file else "",
|
|
124
127
|
str(token_file.expires) if token_file else "",
|
|
125
128
|
)
|
|
126
|
-
|
|
129
|
+
print_unformatted(table)
|
cli/cloud/auth/login.py
CHANGED
|
@@ -13,19 +13,16 @@ from typing import Any, Optional, Tuple
|
|
|
13
13
|
from urllib.parse import parse_qs, urlparse
|
|
14
14
|
|
|
15
15
|
import typer
|
|
16
|
-
from rich.console import Console
|
|
17
16
|
from typing_extensions import override
|
|
18
17
|
|
|
19
18
|
from cli.cloud.auth_tokens import do_activate, prompt_to_set_org
|
|
20
|
-
from cli.errors import ErrorPrinter
|
|
21
19
|
from cli.settings import settings
|
|
22
20
|
from cli.settings.token_file import TokenFile
|
|
21
|
+
from cli.utils.console import print_generic_error, print_newline, print_success, print_unformatted, print_url
|
|
23
22
|
from cli.utils.rest_helper import RestHelper as Rest
|
|
24
23
|
|
|
25
24
|
httpd: HTTPServer
|
|
26
25
|
|
|
27
|
-
console = Console()
|
|
28
|
-
|
|
29
26
|
|
|
30
27
|
def generate_pkce_pair() -> Tuple[str, str]:
|
|
31
28
|
"""
|
|
@@ -79,8 +76,7 @@ class S(BaseHTTPRequestHandler):
|
|
|
79
76
|
allow_status_codes=[401, 400],
|
|
80
77
|
)
|
|
81
78
|
if res.status_code != 200:
|
|
82
|
-
|
|
83
|
-
ErrorPrinter.print_generic_error(
|
|
79
|
+
print_generic_error(
|
|
84
80
|
"Failed to fetch token. Please try again, if the problem persists please reach out to support@remotivelabs.com"
|
|
85
81
|
)
|
|
86
82
|
self.wfile.write(
|
|
@@ -95,7 +91,6 @@ class S(BaseHTTPRequestHandler):
|
|
|
95
91
|
"""Successfully setup CLI, you may close this window now. Return to your terminal to continue""".encode("utf-8")
|
|
96
92
|
)
|
|
97
93
|
access_token = res.json()["access_token"]
|
|
98
|
-
|
|
99
94
|
global short_lived_token # noqa: PLW0603
|
|
100
95
|
short_lived_token = access_token
|
|
101
96
|
|
|
@@ -109,7 +104,7 @@ class S(BaseHTTPRequestHandler):
|
|
|
109
104
|
Run `remotive cloud auth login` to try again.
|
|
110
105
|
""".encode("utf-8")
|
|
111
106
|
)
|
|
112
|
-
|
|
107
|
+
print_generic_error("You did not grant access to RemotiveCloud, login aborted")
|
|
113
108
|
elif error_value == "user_not_exists":
|
|
114
109
|
self.wfile.write(
|
|
115
110
|
"""
|
|
@@ -120,22 +115,19 @@ class S(BaseHTTPRequestHandler):
|
|
|
120
115
|
Once you are signed up, Run `remotive cloud auth login` again.
|
|
121
116
|
""".encode("utf-8")
|
|
122
117
|
)
|
|
123
|
-
|
|
118
|
+
print_generic_error(
|
|
124
119
|
"To use RemotiveCLI you must first sign up at https://cloud.remotivelabs.com and approve our agreements"
|
|
125
120
|
)
|
|
126
121
|
else:
|
|
127
122
|
self.wfile.write(f"Unknown error {error_value}, please contact support@remotivelabs.com".encode("utf-8"))
|
|
128
|
-
|
|
123
|
+
print_generic_error(f"Unexpected error {error_value}, please contact support@remotivelabs.com")
|
|
129
124
|
sys.exit(1)
|
|
130
125
|
|
|
131
126
|
|
|
132
127
|
def prepare_local_webserver(server_class: type = HTTPServer, handler_class: type = S, port: Optional[int] = None) -> None:
|
|
133
128
|
if port is None:
|
|
134
129
|
env_val = os.getenv("REMOTIVE_LOGIN_CALLBACK_PORT" or "")
|
|
135
|
-
if env_val and env_val.isdigit()
|
|
136
|
-
port = int(env_val)
|
|
137
|
-
else:
|
|
138
|
-
port = 0
|
|
130
|
+
port = int(env_val) if env_val and env_val.isdigit() else 0
|
|
139
131
|
|
|
140
132
|
server_address = ("", port)
|
|
141
133
|
global httpd # noqa: PLW0603
|
|
@@ -153,7 +145,6 @@ def create_personal_token() -> None:
|
|
|
153
145
|
email = token["account"]["email"]
|
|
154
146
|
existing_file = settings.get_token_file_by_email(email=email)
|
|
155
147
|
if existing_file is not None:
|
|
156
|
-
# ErrorPrinter.print_hint(f"Revoking and deleting existing credentials [remove_me]{existing_file.name}")
|
|
157
148
|
res = Rest.handle_patch(
|
|
158
149
|
f"/api/me/keys/{existing_file.name}/revoke",
|
|
159
150
|
quiet=True,
|
|
@@ -170,7 +161,7 @@ def create_personal_token() -> None:
|
|
|
170
161
|
|
|
171
162
|
settings.add_personal_token(response.text, activate=True)
|
|
172
163
|
|
|
173
|
-
|
|
164
|
+
print_success("Logged in")
|
|
174
165
|
|
|
175
166
|
|
|
176
167
|
def _do_prompt_to_use_existing_credentials() -> Optional[TokenFile]:
|
|
@@ -232,8 +223,8 @@ def login(headless: bool = False) -> bool: # noqa: C901, PLR0915
|
|
|
232
223
|
"""
|
|
233
224
|
This will print a url the will trigger a callback later so the webserver must be up and running.
|
|
234
225
|
"""
|
|
235
|
-
|
|
236
|
-
|
|
226
|
+
print_unformatted("Copy the following link in a browser to login to cloud, and complete the sign-in prompts:")
|
|
227
|
+
print_newline()
|
|
237
228
|
|
|
238
229
|
url = (
|
|
239
230
|
f"{Rest.get_base_frontend_url()}/login"
|
|
@@ -243,15 +234,15 @@ def login(headless: bool = False) -> bool: # noqa: C901, PLR0915
|
|
|
243
234
|
f"&code_challenge={code_challenge}"
|
|
244
235
|
f"&redirect_uri=http://localhost:{httpd.server_address[1]}"
|
|
245
236
|
)
|
|
246
|
-
|
|
237
|
+
print_url(url)
|
|
247
238
|
httpd.serve_forever()
|
|
248
239
|
|
|
249
240
|
def login_headless() -> None:
|
|
250
241
|
"""
|
|
251
242
|
Full headless, opens a browser and expects a auth code to be entered and exchanged for the token
|
|
252
243
|
"""
|
|
253
|
-
|
|
254
|
-
|
|
244
|
+
print_unformatted("Copy the following link in a browser to login to cloud, and complete the sign-in prompts:")
|
|
245
|
+
print_newline()
|
|
255
246
|
|
|
256
247
|
url = (
|
|
257
248
|
f"{Rest.get_base_frontend_url()}/login"
|
|
@@ -260,7 +251,7 @@ def login(headless: bool = False) -> bool: # noqa: C901, PLR0915
|
|
|
260
251
|
f"&response_type=code"
|
|
261
252
|
f"&code_challenge={code_challenge}"
|
|
262
253
|
)
|
|
263
|
-
|
|
254
|
+
print_url(url)
|
|
264
255
|
|
|
265
256
|
code = typer.prompt(
|
|
266
257
|
"Once finished, enter the verification code provided in your browser",
|
|
@@ -273,12 +264,11 @@ def login(headless: bool = False) -> bool: # noqa: C901, PLR0915
|
|
|
273
264
|
allow_status_codes=[401],
|
|
274
265
|
)
|
|
275
266
|
if res.status_code == 401:
|
|
276
|
-
|
|
267
|
+
print_generic_error(
|
|
277
268
|
"Failed to fetch token. Please try again, if the problem persists please reach out to support@remotivelabs.com"
|
|
278
269
|
)
|
|
279
270
|
sys.exit(1)
|
|
280
271
|
access_token = res.json()["access_token"]
|
|
281
|
-
# res = Rest.handle_get("/api/whoami", return_response=True, access_token=access_token)
|
|
282
272
|
global short_lived_token # noqa: PLW0603
|
|
283
273
|
short_lived_token = access_token
|
|
284
274
|
create_personal_token()
|
|
@@ -299,10 +289,10 @@ def login(headless: bool = False) -> bool: # noqa: C901, PLR0915
|
|
|
299
289
|
)
|
|
300
290
|
|
|
301
291
|
if not could_open:
|
|
302
|
-
|
|
292
|
+
print_generic_error(
|
|
303
293
|
"Could not open a browser on this machine, this is likely because you are in an environment where no browser is available"
|
|
304
294
|
)
|
|
305
|
-
|
|
295
|
+
print_newline()
|
|
306
296
|
if force_use_webserver_callback():
|
|
307
297
|
login_with_callback_but_copy_url()
|
|
308
298
|
create_personal_token()
|
cli/cloud/auth_tokens.py
CHANGED
|
@@ -3,18 +3,14 @@ from __future__ import annotations
|
|
|
3
3
|
from typing import List, Optional
|
|
4
4
|
|
|
5
5
|
import typer
|
|
6
|
-
from rich.console import Console
|
|
7
6
|
from rich.table import Table
|
|
8
7
|
|
|
9
8
|
from cli.cloud.organisations import do_select_default_org
|
|
10
|
-
from cli.errors import ErrorPrinter
|
|
11
9
|
from cli.settings import settings
|
|
12
10
|
from cli.settings.token_file import TokenFile
|
|
11
|
+
from cli.utils.console import print_generic_error, print_generic_message, print_hint, print_newline, print_success, print_unformatted
|
|
13
12
|
from cli.utils.rest_helper import RestHelper as Rest
|
|
14
13
|
|
|
15
|
-
console = Console(stderr=False)
|
|
16
|
-
err_console = Console(stderr=True)
|
|
17
|
-
|
|
18
14
|
|
|
19
15
|
def _prompt_choice( # noqa: C901
|
|
20
16
|
choices: List[TokenFile],
|
|
@@ -59,14 +55,14 @@ def _prompt_choice( # noqa: C901
|
|
|
59
55
|
str(choice.created),
|
|
60
56
|
str(choice.expires),
|
|
61
57
|
)
|
|
62
|
-
|
|
58
|
+
print_unformatted(table)
|
|
63
59
|
|
|
64
60
|
if skip_prompt:
|
|
65
61
|
return None
|
|
66
62
|
|
|
67
|
-
|
|
68
|
-
if info_message
|
|
69
|
-
|
|
63
|
+
print_newline()
|
|
64
|
+
if info_message:
|
|
65
|
+
print_generic_message(info_message)
|
|
70
66
|
|
|
71
67
|
selection = typer.prompt(
|
|
72
68
|
f"Enter the number(# 1-{len(included_tokens)}) of the account to select (q to quit)",
|
|
@@ -101,7 +97,7 @@ def do_activate(token_name: Optional[str]) -> Optional[TokenFile]:
|
|
|
101
97
|
if token_name:
|
|
102
98
|
token_file = settings.get_token_file(token_name)
|
|
103
99
|
if not token_file:
|
|
104
|
-
|
|
100
|
+
print_generic_error(f"Token with filename or name {token_name} could not be found")
|
|
105
101
|
return None
|
|
106
102
|
return settings.activate_token(token_file)
|
|
107
103
|
|
|
@@ -112,16 +108,16 @@ def do_activate(token_name: Optional[str]) -> Optional[TokenFile]:
|
|
|
112
108
|
if token_selected is not None:
|
|
113
109
|
is_logged_in = Rest.has_access("/api/whoami")
|
|
114
110
|
if not is_logged_in:
|
|
115
|
-
|
|
111
|
+
print_generic_error("Could not access RemotiveCloud with selected token")
|
|
116
112
|
else:
|
|
117
|
-
|
|
113
|
+
print_success("Access to RemotiveCloud granted")
|
|
118
114
|
# Only select default if activate was done with selection and successful
|
|
119
115
|
# and not SA since SA cannot list available organizations
|
|
120
116
|
if token_selected.type == "authorized_user":
|
|
121
117
|
prompt_to_set_org()
|
|
122
118
|
return token_selected
|
|
123
119
|
|
|
124
|
-
|
|
120
|
+
print_hint("No credentials available, login to activate credentials")
|
|
125
121
|
return None
|
|
126
122
|
|
|
127
123
|
|
cli/cloud/brokers.py
CHANGED
|
@@ -10,6 +10,7 @@ import typer
|
|
|
10
10
|
import websocket
|
|
11
11
|
|
|
12
12
|
from cli.typer import typer_utils
|
|
13
|
+
from cli.utils.console import print_generic_message
|
|
13
14
|
from cli.utils.rest_helper import RestHelper as Rest
|
|
14
15
|
|
|
15
16
|
app = typer_utils.create_typer()
|
|
@@ -61,12 +62,6 @@ def start(
|
|
|
61
62
|
silent: bool = typer.Option(False, help="Optional specific tag/version"),
|
|
62
63
|
api_key: str = typer.Option("", help="Start with your own api-key"),
|
|
63
64
|
) -> None:
|
|
64
|
-
# with Progress(
|
|
65
|
-
# SpinnerColumn(),
|
|
66
|
-
# TextColumn("[progress.description]{task.description}"),
|
|
67
|
-
# transient=True,
|
|
68
|
-
# ) as progress:
|
|
69
|
-
# progress.add_task(description=f"Starting broker {name}...", total=None)
|
|
70
65
|
do_start(name, project, api_key, tag, return_response=silent)
|
|
71
66
|
|
|
72
67
|
|
|
@@ -81,7 +76,6 @@ def logs(
|
|
|
81
76
|
Exposes broker logs history or real-time tail of the broker.
|
|
82
77
|
|
|
83
78
|
When using --tail option, --history always skipped even if supplied
|
|
84
|
-
|
|
85
79
|
"""
|
|
86
80
|
|
|
87
81
|
def exit_on_ctrlc(_sig: Any, _frame: Any) -> None:
|
|
@@ -91,10 +85,12 @@ def logs(
|
|
|
91
85
|
os_signal.signal(os_signal.SIGINT, exit_on_ctrlc)
|
|
92
86
|
|
|
93
87
|
def on_message(_wsapp: Any, message: str) -> None:
|
|
94
|
-
print
|
|
88
|
+
# TODO: use log instead of print for debug information?
|
|
89
|
+
print_generic_message(message)
|
|
95
90
|
|
|
96
91
|
def on_error(_wsapp: Any, err: str) -> None:
|
|
97
|
-
print
|
|
92
|
+
# TODO: use log instead of print for debug information?
|
|
93
|
+
print_generic_message(f"Error encountered: {err}")
|
|
98
94
|
|
|
99
95
|
Rest.ensure_auth_token()
|
|
100
96
|
# This will work with both http -> ws and https -> wss
|
cli/cloud/configs.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import os.path
|
|
2
2
|
import shutil
|
|
3
|
-
import sys
|
|
4
3
|
from pathlib import Path
|
|
5
4
|
|
|
6
5
|
import requests
|
|
@@ -8,6 +7,7 @@ import typer
|
|
|
8
7
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
9
8
|
|
|
10
9
|
from cli.typer import typer_utils
|
|
10
|
+
from cli.utils.console import print_generic_error, print_success
|
|
11
11
|
from cli.utils.rest_helper import RestHelper as Rest
|
|
12
12
|
|
|
13
13
|
app = typer_utils.create_typer()
|
|
@@ -102,21 +102,8 @@ def download(
|
|
|
102
102
|
if download_resp.status_code == 200:
|
|
103
103
|
with open(signal_db_file, "wb") as out_file:
|
|
104
104
|
shutil.copyfileobj(download_resp.raw, out_file)
|
|
105
|
-
|
|
105
|
+
print_success(f"{signal_db_file} downloaded")
|
|
106
106
|
else:
|
|
107
|
-
|
|
107
|
+
print_generic_error(f"Got unexpected status {download_resp.status_code}")
|
|
108
108
|
else:
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
# @app.command()
|
|
113
|
-
# def upload(file: str, project: str = typer.Option(..., help="Project ID", envvar='REMOTIVE_CLOUD_PROJECT')):
|
|
114
|
-
# files = {'upload_file': open(file, 'rb')}
|
|
115
|
-
# values = {'DB': 'photcat', 'OUT': 'csv', 'SHORT': 'short'}
|
|
116
|
-
# rest.headers["content-type"] = "application/octet-stream"
|
|
117
|
-
# r = requests.get(f"{rest.base_url}/api/project/{project}/files/recording/upload/{file}", headers=rest.headers)
|
|
118
|
-
# print(r.status_code)
|
|
119
|
-
# print(r.text)
|
|
120
|
-
|
|
121
|
-
# pylint: disable=C0301
|
|
122
|
-
# curl -X PUT -H 'Content-Type: application/octet-stream' --upload-file docker-compose.yml 'https://storage.googleapis.com/beamylabs-fileuploads-dev/projects/beamyhack/recording/myrecording?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=recordings-upload-account%40beamycloud-dev.iam.gserviceaccount.com%2F20220729%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20220729T134012Z&X-Goog-Expires=3000&X-Goog-SignedHeaders=content-type%3Bhost&X-Goog-Signature=d1fa7639349d6453aebfce8814d6e5685af03952d07aa4e3cb0d44dba7cf5e572f684c8120dba17cbc7ea6a0ef5450542a3c745c65e04272b34265d0ddcf1b67e6f2b5bfa446264a62d77bd7faabf45ad6bd2aec5225f57004b0a31cfe0480cba063a3807d86346b1da99ecbae3f3e6da8f44f06396dfc1fdc6f89e475abdf969142cef6f369f03aff41000c8abb28aa82185246746fd6c16b6b381baa2d586382a3d3067b6376ddba2b55b2b6f9d942913a1cbfbc61491ba6a615d7d5a0d9a476c357431143e9cea1411dfad9f01b1e1176dc8c056cbf08cccfd401a55d63c19d038f3ab42b712abc48d759047ac07862c4fae937c341e19b568bb60a4e4086'
|
|
109
|
+
print_generic_error(f"Got unexpected status {get_signed_url_resp.status_code}\n")
|
cli/cloud/organisations.py
CHANGED
|
@@ -5,15 +5,13 @@ from dataclasses import dataclass
|
|
|
5
5
|
from typing import List, Optional
|
|
6
6
|
|
|
7
7
|
import typer
|
|
8
|
-
from rich.console import Console
|
|
9
8
|
from rich.table import Table
|
|
10
9
|
|
|
11
|
-
from cli.errors import ErrorPrinter
|
|
12
10
|
from cli.settings import settings
|
|
13
11
|
from cli.typer import typer_utils
|
|
12
|
+
from cli.utils.console import print_generic_error, print_generic_message, print_hint, print_newline, print_unformatted_to_stderr
|
|
14
13
|
from cli.utils.rest_helper import RestHelper
|
|
15
14
|
|
|
16
|
-
console = Console(stderr=False)
|
|
17
15
|
app = typer_utils.create_typer()
|
|
18
16
|
|
|
19
17
|
|
|
@@ -36,9 +34,9 @@ def _prompt_choice(choices: List[Organisation]) -> Optional[Organisation]:
|
|
|
36
34
|
choice.uid,
|
|
37
35
|
":thumbsup:" if current_default_org is not None and current_default_org == choice.uid else "",
|
|
38
36
|
)
|
|
39
|
-
|
|
37
|
+
print_unformatted_to_stderr(table)
|
|
40
38
|
|
|
41
|
-
|
|
39
|
+
print_newline()
|
|
42
40
|
selection = typer.prompt(f"Enter the number(# 1-{len(choices)}) of the organization to select (or q to quit)")
|
|
43
41
|
|
|
44
42
|
if selection == "q":
|
|
@@ -49,7 +47,7 @@ def _prompt_choice(choices: List[Organisation]) -> Optional[Organisation]:
|
|
|
49
47
|
return choices[index]
|
|
50
48
|
raise ValueError
|
|
51
49
|
except ValueError:
|
|
52
|
-
|
|
50
|
+
print_generic_error("Invalid choice, please try again")
|
|
53
51
|
return _prompt_choice(choices)
|
|
54
52
|
|
|
55
53
|
|
|
@@ -75,16 +73,16 @@ def do_select_default_org(organisation_uid: Optional[str] = None, get: bool = Fa
|
|
|
75
73
|
active_account = settings.get_active_account()
|
|
76
74
|
if get:
|
|
77
75
|
if active_account and active_account.default_organization:
|
|
78
|
-
|
|
76
|
+
print_unformatted_to_stderr(active_account.default_organization)
|
|
79
77
|
else:
|
|
80
|
-
|
|
78
|
+
print_unformatted_to_stderr("No default organization set")
|
|
81
79
|
elif organisation_uid is not None:
|
|
82
80
|
settings.set_default_organisation(organisation_uid)
|
|
83
81
|
else:
|
|
84
82
|
if active_account:
|
|
85
83
|
token = settings.get_token_file(active_account.credentials_file)
|
|
86
84
|
if token and token.type != "authorized_user":
|
|
87
|
-
|
|
85
|
+
print_hint(
|
|
88
86
|
"You must supply the organization name as argument when using a service-account since the "
|
|
89
87
|
"service-account is not allowed to list"
|
|
90
88
|
)
|
|
@@ -105,4 +103,4 @@ def do_select_default_org(organisation_uid: Optional[str] = None, get: bool = Fa
|
|
|
105
103
|
def list_orgs() -> None:
|
|
106
104
|
r = RestHelper.handle_get("/api/bu", return_response=True)
|
|
107
105
|
orgs = [{"uid": org["organisation"]["uid"], "displayName": org["organisation"]["displayName"]} for org in r.json()]
|
|
108
|
-
|
|
106
|
+
print_generic_message(json.dumps(orgs))
|
cli/cloud/projects.py
CHANGED
|
@@ -3,6 +3,7 @@ import json
|
|
|
3
3
|
import typer
|
|
4
4
|
|
|
5
5
|
from cli.typer import typer_utils
|
|
6
|
+
from cli.utils.console import print_generic_error, print_generic_message
|
|
6
7
|
from cli.utils.rest_helper import RestHelper as Rest
|
|
7
8
|
|
|
8
9
|
app = typer_utils.create_typer()
|
|
@@ -18,10 +19,9 @@ def list_projects(organization: str = typer.Option(..., help="Organization ID",
|
|
|
18
19
|
# extract the project uid parts
|
|
19
20
|
projects = r.json()["projects"]
|
|
20
21
|
projects = map(lambda p: p["uid"], projects)
|
|
21
|
-
|
|
22
|
+
print_generic_message(json.dumps(list(projects)))
|
|
22
23
|
else:
|
|
23
|
-
|
|
24
|
-
print(r.text)
|
|
24
|
+
print_generic_error(f"Got unexpected status {r.status_code}\n")
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
@app.command(name="create")
|