remotivelabs-cli 0.0.28__py3-none-any.whl → 0.0.30__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.
- cli/broker/export.py +1 -1
- cli/broker/lib/broker.py +3 -4
- cli/broker/signals.py +1 -1
- cli/cloud/auth.py +10 -53
- cli/cloud/auth_tokens.py +29 -39
- cli/cloud/brokers.py +3 -3
- cli/cloud/recordings.py +42 -6
- cli/cloud/rest_helper.py +11 -7
- cli/cloud/service_account_tokens.py +10 -12
- cli/connect/protopie/protopie.py +1 -1
- cli/remotive.py +1 -2
- cli/settings.py +47 -8
- {remotivelabs_cli-0.0.28.dist-info → remotivelabs_cli-0.0.30.dist-info}/METADATA +1 -1
- {remotivelabs_cli-0.0.28.dist-info → remotivelabs_cli-0.0.30.dist-info}/RECORD +17 -19
- cli/__about__.py +0 -4
- cli/requirements.txt +0 -12
- {remotivelabs_cli-0.0.28.dist-info → remotivelabs_cli-0.0.30.dist-info}/LICENSE +0 -0
- {remotivelabs_cli-0.0.28.dist-info → remotivelabs_cli-0.0.30.dist-info}/WHEEL +0 -0
- {remotivelabs_cli-0.0.28.dist-info → remotivelabs_cli-0.0.30.dist-info}/entry_points.txt +0 -0
cli/broker/export.py
CHANGED
cli/broker/lib/broker.py
CHANGED
@@ -50,7 +50,7 @@ class Broker:
|
|
50
50
|
|
51
51
|
if api_key is None or api_key == "":
|
52
52
|
if url.startswith("https"):
|
53
|
-
self.intercept_channel = br.create_channel(url, None, settings.
|
53
|
+
self.intercept_channel = br.create_channel(url, None, settings.read_secret_token())
|
54
54
|
# TODO - Temporary solution to print proper error message, remove ENV once api-key is gone
|
55
55
|
os.environ["ACCESS_TOKEN"] = "true"
|
56
56
|
else:
|
@@ -261,7 +261,7 @@ class Broker:
|
|
261
261
|
keep_running = True
|
262
262
|
keep_running_during_recording = True
|
263
263
|
|
264
|
-
def exit_on_ctrlc(
|
264
|
+
def exit_on_ctrlc(_sig: Any, _frame: Any) -> None:
|
265
265
|
nonlocal keep_running
|
266
266
|
keep_running = False
|
267
267
|
nonlocal keep_running_during_recording
|
@@ -416,8 +416,7 @@ class Broker:
|
|
416
416
|
)
|
417
417
|
thread.start()
|
418
418
|
# wait for subscription to settle
|
419
|
-
|
420
|
-
return subscription # , thread
|
419
|
+
return self.q.get()
|
421
420
|
|
422
421
|
def validate_and_get_subscribed_signals(
|
423
422
|
self, subscribed_namespaces: List[str], subscribed_signals: List[str]
|
cli/broker/signals.py
CHANGED
cli/cloud/auth.py
CHANGED
@@ -1,8 +1,6 @@
|
|
1
|
-
import os
|
2
1
|
import time
|
3
2
|
import webbrowser
|
4
3
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
5
|
-
from pathlib import Path
|
6
4
|
from threading import Thread
|
7
5
|
from typing import Any
|
8
6
|
|
@@ -14,8 +12,6 @@ from cli import settings
|
|
14
12
|
from . import auth_tokens
|
15
13
|
from .rest_helper import RestHelper as Rest
|
16
14
|
|
17
|
-
APA = settings.CONFIG_DIR_NAME
|
18
|
-
|
19
15
|
HELP = """
|
20
16
|
Manage how you authenticate with our cloud platform
|
21
17
|
"""
|
@@ -23,22 +19,17 @@ Manage how you authenticate with our cloud platform
|
|
23
19
|
httpd: HTTPServer
|
24
20
|
|
25
21
|
app = typer.Typer(help=HELP)
|
26
|
-
|
27
22
|
app.add_typer(auth_tokens.app, name="tokens", help="Manage users personal access tokens")
|
28
|
-
CONFIG_DIR_NAME = settings.CONFIG_DIR_NAME # str(Path.home()) + "/.config/.remotive/"
|
29
|
-
TOKEN_FILE_NAME = settings.TOKEN_FILE_NAME # str(Path.home()) + "/.config/.remotive/cloud.secret.token"
|
30
23
|
|
31
24
|
|
32
25
|
class S(BaseHTTPRequestHandler):
|
33
26
|
def _set_response(self) -> None:
|
34
27
|
self.send_response(200)
|
35
|
-
# self.send_response(301)
|
36
|
-
# self.send_header('Location', 'https://cloud.remotivelabs.com')
|
37
|
-
# self.end_headers()
|
38
28
|
self.send_header("Content-type", "text/html")
|
39
29
|
self.end_headers()
|
40
30
|
|
41
|
-
|
31
|
+
@override
|
32
|
+
def log_message(self, format: Any, *args: Any) -> None: # pylint: disable=W0622,
|
42
33
|
return
|
43
34
|
|
44
35
|
# Please do not change this into lowercase!
|
@@ -54,9 +45,7 @@ class S(BaseHTTPRequestHandler):
|
|
54
45
|
killerthread = Thread(target=httpd.shutdown)
|
55
46
|
killerthread.start()
|
56
47
|
|
57
|
-
|
58
|
-
os.makedirs(CONFIG_DIR_NAME)
|
59
|
-
write_token(path[1:])
|
48
|
+
settings.write_secret_token(path[1:])
|
60
49
|
print("Successfully logged on, you are ready to go with cli")
|
61
50
|
|
62
51
|
|
@@ -80,7 +69,11 @@ def login() -> None:
|
|
80
69
|
be the same as activating a personal access key or service-account access key.
|
81
70
|
"""
|
82
71
|
start_local_webserver()
|
83
|
-
webbrowser.open(
|
72
|
+
webbrowser.open(
|
73
|
+
f"{Rest.get_base_url()}/login?redirectUrl=http://localhost:{httpd.server_address[1]}",
|
74
|
+
new=1,
|
75
|
+
autoraise=True,
|
76
|
+
)
|
84
77
|
|
85
78
|
httpd.serve_forever()
|
86
79
|
|
@@ -98,46 +91,10 @@ def print_access_token() -> None:
|
|
98
91
|
"""
|
99
92
|
Print current active access token
|
100
93
|
"""
|
101
|
-
print(
|
94
|
+
print(settings.read_secret_token())
|
102
95
|
|
103
96
|
|
104
97
|
@app.command(help="Clear access token")
|
105
98
|
def logout() -> None:
|
106
|
-
|
99
|
+
settings.clear_secret_token()
|
107
100
|
print("Access token removed")
|
108
|
-
|
109
|
-
|
110
|
-
def read_token() -> str:
|
111
|
-
# f = open(token_file_name, "r")
|
112
|
-
# token = f.read()
|
113
|
-
# f.close()
|
114
|
-
return settings.read_token()
|
115
|
-
|
116
|
-
|
117
|
-
def read_file_with_path(file: str) -> str:
|
118
|
-
with open(file, "r", encoding="utf8") as f:
|
119
|
-
token = f.read()
|
120
|
-
return token
|
121
|
-
|
122
|
-
|
123
|
-
def read_file(file: str) -> str:
|
124
|
-
with open(str(Path.home()) + f"/.config/.remotive/{file}", "r", encoding="utf8") as f:
|
125
|
-
token = f.read()
|
126
|
-
return token
|
127
|
-
|
128
|
-
|
129
|
-
def write_token(token: str) -> None:
|
130
|
-
with open(TOKEN_FILE_NAME, "w", encoding="utf8") as f:
|
131
|
-
f.write(token)
|
132
|
-
|
133
|
-
|
134
|
-
# Key stuff
|
135
|
-
# f = open(str(Path.home())+ "/.remotivelabs/privatekey.json", "r")
|
136
|
-
# j = json.loads(f.read())
|
137
|
-
# print(j['privateKey'])
|
138
|
-
# key = load_pem_private_key(bytes(j['privateKey'],'UTF-8'), None)
|
139
|
-
# print(key.key_size)
|
140
|
-
#
|
141
|
-
# "exp": datetime.now(tz=timezone.utc)
|
142
|
-
# encoded = jwt.encode({"some": "payload"}, j['privateKey'] , algorithm="RS256", headers={"kid": j["keyId"]})
|
143
|
-
# print(encoded)
|
cli/cloud/auth_tokens.py
CHANGED
@@ -1,18 +1,16 @@
|
|
1
1
|
import json
|
2
|
-
import os
|
3
2
|
import sys
|
4
3
|
from json.decoder import JSONDecodeError
|
5
4
|
from pathlib import Path
|
6
5
|
|
7
6
|
import typer
|
8
7
|
|
8
|
+
from cli import settings
|
9
|
+
|
9
10
|
from .rest_helper import RestHelper as Rest
|
10
11
|
|
11
12
|
app = typer.Typer()
|
12
13
|
|
13
|
-
TOKEN_FILE_NAME = str(Path.home()) + "/.config/.remotive/cloud.secret.token"
|
14
|
-
CONFIG_DIR_NAME = str(Path.home()) + "/.config/.remotive/"
|
15
|
-
|
16
14
|
|
17
15
|
@app.command(name="create", help="Create and download a new personal access token")
|
18
16
|
def get_personal_access_token(activate: bool = typer.Option(False, help="Activate the token for use after download")) -> None: # pylint: disable=W0621
|
@@ -24,12 +22,12 @@ def get_personal_access_token(activate: bool = typer.Option(False, help="Activat
|
|
24
22
|
|
25
23
|
if response.status_code == 200:
|
26
24
|
name = response.json()["name"]
|
27
|
-
|
28
|
-
print(f"Personal access token written to {
|
25
|
+
pat_path = write_personal_token(name, response.text)
|
26
|
+
print(f"Personal access token written to {pat_path}")
|
29
27
|
if not activate:
|
30
|
-
print(f"Use 'remotive cloud auth tokens activate {
|
28
|
+
print(f"Use 'remotive cloud auth tokens activate {pat_path.name}' to use this access token from cli")
|
31
29
|
else:
|
32
|
-
do_activate(
|
30
|
+
do_activate(str(pat_path))
|
33
31
|
print("Token file activated and ready for use")
|
34
32
|
print("\033[93m This file contains secrets and must be kept safe")
|
35
33
|
else:
|
@@ -94,14 +92,15 @@ def activate(file: str = typer.Argument(..., help="File name")) -> None:
|
|
94
92
|
do_activate(file)
|
95
93
|
|
96
94
|
|
95
|
+
# TODO: Move parts of this to settings # pylint: disable=W0511
|
97
96
|
def do_activate(file: str) -> None:
|
98
97
|
# Best effort to read file
|
99
|
-
if
|
100
|
-
token_file = json.loads(read_file_with_path(file))
|
101
|
-
|
102
|
-
elif
|
98
|
+
if Path(file).exists():
|
99
|
+
token_file = json.loads(read_file_with_path(Path(file)))
|
100
|
+
settings.write_secret_token(token_file["token"])
|
101
|
+
elif (settings.CONFIG_DIR_PATH / file).exists():
|
103
102
|
token_file = json.loads(read_file(file))
|
104
|
-
|
103
|
+
settings.write_secret_token(token_file["token"])
|
105
104
|
else:
|
106
105
|
sys.stderr.write("File could not be found \n")
|
107
106
|
|
@@ -111,43 +110,34 @@ def list_files() -> None:
|
|
111
110
|
"""
|
112
111
|
List personal access token files in remotivelabs config directory
|
113
112
|
"""
|
114
|
-
personal_files =
|
113
|
+
personal_files = settings.list_personal_token_files()
|
115
114
|
for file in personal_files:
|
116
115
|
print(file)
|
117
116
|
|
118
117
|
|
118
|
+
# TODO: Move to settings # pylint: disable=W0511
|
119
119
|
def read_file(file: str) -> str:
|
120
120
|
"""
|
121
|
-
Reads a file using file path or if that does not exist check
|
121
|
+
Reads a file using file path or if that does not exist check in config directory
|
122
122
|
"""
|
123
|
-
path = file
|
124
|
-
if not
|
125
|
-
path =
|
126
|
-
if not
|
123
|
+
path = Path(file)
|
124
|
+
if not path.exists():
|
125
|
+
path = settings.CONFIG_DIR_PATH / file
|
126
|
+
if not path.exists():
|
127
127
|
sys.stderr.write(f"Failed to find file using {file} or {path}\n")
|
128
128
|
sys.exit(1)
|
129
|
-
with open(path, "r", encoding="utf8") as f:
|
130
|
-
token = f.read()
|
131
|
-
f.close()
|
132
|
-
return token
|
133
129
|
|
130
|
+
return read_file_with_path(path)
|
134
131
|
|
135
|
-
def read_file_with_path(file: str) -> str:
|
136
|
-
with open(file, "r", encoding="utf8") as f:
|
137
|
-
token = f.read()
|
138
|
-
f.close()
|
139
|
-
return token
|
140
132
|
|
141
|
-
|
142
|
-
def
|
143
|
-
with open(
|
144
|
-
f.
|
145
|
-
f.close()
|
133
|
+
# TODO: Move to settings # pylint: disable=W0511
|
134
|
+
def read_file_with_path(path: Path) -> str:
|
135
|
+
with open(path, "r", encoding="utf8") as f:
|
136
|
+
return f.read()
|
146
137
|
|
147
138
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
return path
|
139
|
+
# TODO: Move to settings # pylint: disable=W0511
|
140
|
+
def write_personal_token(name: str, token: str) -> Path:
|
141
|
+
file = f"personal-token-{name}.json"
|
142
|
+
path = settings.CONFIG_DIR_PATH / file
|
143
|
+
return settings._write_settings_file(path, token) # pylint: disable=W0212
|
cli/cloud/brokers.py
CHANGED
@@ -83,16 +83,16 @@ def logs(
|
|
83
83
|
|
84
84
|
"""
|
85
85
|
|
86
|
-
def exit_on_ctrlc(
|
86
|
+
def exit_on_ctrlc(_sig: Any, _frame: Any) -> None:
|
87
87
|
wsapp.close()
|
88
88
|
os._exit(0)
|
89
89
|
|
90
90
|
os_signal.signal(os_signal.SIGINT, exit_on_ctrlc)
|
91
91
|
|
92
|
-
def on_message(
|
92
|
+
def on_message(_wsapp: Any, message: str) -> None:
|
93
93
|
print(message)
|
94
94
|
|
95
|
-
def on_error(
|
95
|
+
def on_error(_wsapp: Any, err: str) -> None: # pylint: disable=W0613
|
96
96
|
print("EXAMPLE error encountered: ", err)
|
97
97
|
|
98
98
|
Rest.ensure_auth_token()
|
cli/cloud/recordings.py
CHANGED
@@ -58,7 +58,9 @@ def list_recordings(
|
|
58
58
|
"""
|
59
59
|
|
60
60
|
if is_processing:
|
61
|
-
Rest.handle_get(f"/api/project/{project}/files/recording/processing")
|
61
|
+
res = Rest.handle_get(f"/api/project/{project}/files/recording/processing", return_response=True)
|
62
|
+
json_res: List[Dict[str, Any]] = res.json()
|
63
|
+
print(json.dumps(list(filter(lambda r: r["status"] == "RUNNING" or r["status"] == "FAILED", json_res))))
|
62
64
|
else:
|
63
65
|
Rest.handle_get(f"/api/project/{project}/files/recording")
|
64
66
|
|
@@ -79,7 +81,44 @@ def describe(
|
|
79
81
|
# body=json.dumps({'projectUid': target_project}))
|
80
82
|
|
81
83
|
|
82
|
-
|
84
|
+
@app.command(name="import")
|
85
|
+
def import_as_recording(
|
86
|
+
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
87
|
+
path: str = typer.Argument(default=..., help="Remote storage path to file to delete"),
|
88
|
+
) -> None:
|
89
|
+
"""
|
90
|
+
Imports a file from Storage as a recording.
|
91
|
+
NOTE that Storage is not yet available to all customers
|
92
|
+
"""
|
93
|
+
|
94
|
+
if path.startswith("rcs://"):
|
95
|
+
final_path = __check_rcs_path(path)
|
96
|
+
else:
|
97
|
+
ErrorPrinter.print_hint("Path must start with rcs://")
|
98
|
+
sys.exit(1)
|
99
|
+
|
100
|
+
Rest.handle_post(
|
101
|
+
url=f"/api/project/{project}/files/recording",
|
102
|
+
return_response=True,
|
103
|
+
progress_label=f"Importing {final_path}...",
|
104
|
+
body=json.dumps({"path": final_path}),
|
105
|
+
)
|
106
|
+
|
107
|
+
ErrorPrinter.print_hint(
|
108
|
+
f"Import started, you can track progress with 'remotive cloud recordings list --is-processing --project {project}'"
|
109
|
+
)
|
110
|
+
|
111
|
+
|
112
|
+
# Copied from filestorage
|
113
|
+
def __check_rcs_path(path: str) -> str:
|
114
|
+
rcs_path = path.replace("rcs://", "/")
|
115
|
+
if rcs_path.startswith("/."):
|
116
|
+
ErrorPrinter.print_hint("Invalid path")
|
117
|
+
sys.exit(1)
|
118
|
+
return rcs_path
|
119
|
+
|
120
|
+
|
121
|
+
def do_start(name: str, project: str, api_key: str, return_response: bool = False) -> requests.Response:
|
83
122
|
if api_key == "":
|
84
123
|
body = {"size": "S"}
|
85
124
|
else:
|
@@ -125,13 +164,10 @@ def mount( # noqa: C901
|
|
125
164
|
else:
|
126
165
|
r = Rest.handle_get(url=f"/api/project/{project}/brokers/{broker}", return_response=True, allow_status_codes=[404])
|
127
166
|
|
128
|
-
if r is None:
|
129
|
-
sys.exit(1)
|
130
167
|
if r.status_code == 404:
|
131
168
|
if ensure_broker_started:
|
132
169
|
r = do_start(broker, project, "", return_response=True)
|
133
|
-
|
134
|
-
sys.exit(1)
|
170
|
+
|
135
171
|
if r.status_code != 200:
|
136
172
|
print(r.text)
|
137
173
|
sys.exit(1)
|
cli/cloud/rest_helper.py
CHANGED
@@ -8,7 +8,7 @@ import shutil
|
|
8
8
|
import sys
|
9
9
|
from importlib.metadata import version
|
10
10
|
from pathlib import Path
|
11
|
-
from typing import Any, Dict, List, Union
|
11
|
+
from typing import Any, BinaryIO, Dict, List, Union, cast
|
12
12
|
|
13
13
|
import requests
|
14
14
|
from requests.exceptions import JSONDecodeError
|
@@ -87,7 +87,7 @@ class RestHelper:
|
|
87
87
|
# raise typer.Exit()
|
88
88
|
|
89
89
|
# f = open(str(Path.home()) + "/.config/.remotive/cloud.secret.token", "r")
|
90
|
-
token = settings.
|
90
|
+
token = settings.read_secret_token()
|
91
91
|
# os.environ['REMOTIVE_CLOUD_AUTH_TOKEN'] = token
|
92
92
|
|
93
93
|
RestHelper.__headers["Authorization"] = "Bearer " + token.strip()
|
@@ -102,7 +102,7 @@ class RestHelper:
|
|
102
102
|
progress_label: str = "Fetching...",
|
103
103
|
use_progress_indicator: bool = True,
|
104
104
|
timeout: int = 20,
|
105
|
-
) -> requests.Response
|
105
|
+
) -> requests.Response:
|
106
106
|
# pylint: disable=R0913
|
107
107
|
# Returns a Response object if succesfull otherwise None
|
108
108
|
if params is None:
|
@@ -118,7 +118,7 @@ class RestHelper:
|
|
118
118
|
RestHelper.check_api_result(r, allow_status_codes)
|
119
119
|
return r
|
120
120
|
RestHelper.print_api_result(r)
|
121
|
-
|
121
|
+
sys.exit(0)
|
122
122
|
|
123
123
|
@staticmethod
|
124
124
|
def has_access(url: str, params: Any = {}) -> bool: # pylint: disable=W0102
|
@@ -174,7 +174,7 @@ class RestHelper:
|
|
174
174
|
@staticmethod
|
175
175
|
def handle_post( # pylint: disable=W0102
|
176
176
|
url: str, body: Any = None, params: Any = {}, progress_label: str = "Processing...", return_response: bool = False
|
177
|
-
) -> requests.Response
|
177
|
+
) -> requests.Response:
|
178
178
|
# Returns a Response object if succesfull otherwise, None
|
179
179
|
RestHelper.ensure_auth_token()
|
180
180
|
RestHelper.__headers["content-type"] = "application/json"
|
@@ -187,7 +187,7 @@ class RestHelper:
|
|
187
187
|
return r
|
188
188
|
|
189
189
|
RestHelper.print_api_result(r)
|
190
|
-
|
190
|
+
sys.exit(0)
|
191
191
|
|
192
192
|
@staticmethod
|
193
193
|
def handle_put(url: str, body: Any = None, params: Any = {}, return_response: bool = False) -> requests.Response | None: # pylint: disable=W0102
|
@@ -256,8 +256,12 @@ class RestHelper:
|
|
256
256
|
if download_resp.status_code == 200:
|
257
257
|
content_length = int(download_resp.headers["Content-Length"])
|
258
258
|
with open(save_file_name, "wb") as out_file:
|
259
|
+
stream = cast(BinaryIO, download_resp.raw) # we know this is a binary stream, as stream=True is set in the request
|
259
260
|
with wrap_file(
|
260
|
-
|
261
|
+
stream,
|
262
|
+
content_length,
|
263
|
+
refresh_per_second=100,
|
264
|
+
description=f"Downloading to {save_file_name}",
|
261
265
|
) as stream_with_progress:
|
262
266
|
shutil.copyfileobj(stream_with_progress, out_file)
|
263
267
|
else:
|
@@ -1,16 +1,14 @@
|
|
1
1
|
import json
|
2
|
-
import os
|
3
2
|
from pathlib import Path
|
4
3
|
|
5
4
|
import typer
|
6
5
|
|
6
|
+
from cli import settings
|
7
|
+
|
7
8
|
from .rest_helper import RestHelper as Rest
|
8
9
|
|
9
10
|
app = typer.Typer()
|
10
11
|
|
11
|
-
CONFIG_DIR_NAME = str(Path.home()) + "/.config/.remotive/"
|
12
|
-
TOKEN_FILE_NAME = str(Path.home()) + "/.config/.remotive/cloud.secret.token2"
|
13
|
-
|
14
12
|
|
15
13
|
@app.command(name="create", help="Create new access token")
|
16
14
|
def create(
|
@@ -29,7 +27,7 @@ def create(
|
|
29
27
|
|
30
28
|
if response.status_code == 200:
|
31
29
|
name = response.json()["name"]
|
32
|
-
|
30
|
+
write_sa_token(service_account, name, response.text)
|
33
31
|
else:
|
34
32
|
print(f"Got status code: {response.status_code}")
|
35
33
|
print(response.text)
|
@@ -48,8 +46,8 @@ def list_files() -> None:
|
|
48
46
|
"""
|
49
47
|
List personal access token files in remotivelabs config directory
|
50
48
|
"""
|
51
|
-
|
52
|
-
for file in
|
49
|
+
sa_files = settings.list_service_account_token_files()
|
50
|
+
for file in sa_files:
|
53
51
|
print(file)
|
54
52
|
|
55
53
|
|
@@ -62,8 +60,8 @@ def revoke(
|
|
62
60
|
Rest.handle_delete(f"/api/project/{project}/admin/accounts/{service_account}/keys/{name}")
|
63
61
|
|
64
62
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
63
|
+
# TODO: Move to settings # pylint: disable=W0511
|
64
|
+
def write_sa_token(service_account: str, name: str, token: str) -> Path:
|
65
|
+
file = f"service-account-{service_account}-{name}-token.json"
|
66
|
+
path = settings.CONFIG_DIR_PATH / file
|
67
|
+
return settings._write_settings_file(path, token) # pylint: disable=W0212
|
cli/connect/protopie/protopie.py
CHANGED
@@ -167,7 +167,7 @@ def do_connect(
|
|
167
167
|
if broker_url.startswith("https"):
|
168
168
|
if api_key is None:
|
169
169
|
print("No --api-key, reading token from file")
|
170
|
-
x_api_key = settings.
|
170
|
+
x_api_key = settings.read_secret_token()
|
171
171
|
else:
|
172
172
|
x_api_key = api_key
|
173
173
|
elif api_key is not None:
|
cli/remotive.py
CHANGED
@@ -42,8 +42,7 @@ def test_callback(value: int) -> None:
|
|
42
42
|
|
43
43
|
@app.callback()
|
44
44
|
def main(
|
45
|
-
|
46
|
-
the_version: bool = typer.Option(None, "--version", callback=version_callback, is_eager=False, help="Print current version"),
|
45
|
+
_the_version: bool = typer.Option(None, "--version", callback=version_callback, is_eager=False, help="Print current version"),
|
47
46
|
) -> None:
|
48
47
|
# Do other global stuff, handle other global options here
|
49
48
|
return
|
cli/settings.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
import
|
1
|
+
from __future__ import annotations
|
2
|
+
|
2
3
|
import sys
|
3
4
|
from pathlib import Path
|
4
5
|
|
@@ -6,12 +7,19 @@ from rich.console import Console
|
|
6
7
|
|
7
8
|
err_console = Console(stderr=True)
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
# pylint: disable-next=W0511
|
11
|
+
# TODO: We probably want this to be both configurable, and testable. The best solution would probably be to refactor this module into a
|
12
|
+
# proper class, and configure it similar to logging.
|
13
|
+
CONFIG_DIR_PATH = Path.home() / ".config" / ".remotive/"
|
14
|
+
TOKEN_SECRET_FILE_PATH = CONFIG_DIR_PATH / "cloud.secret.token"
|
15
|
+
|
16
|
+
|
17
|
+
class InvalidSettingsFileError(Exception):
|
18
|
+
"""Raised when trying to access an invalid settings file or file path"""
|
11
19
|
|
12
20
|
|
13
|
-
def
|
14
|
-
if not
|
21
|
+
def read_secret_token() -> str:
|
22
|
+
if not TOKEN_SECRET_FILE_PATH.exists():
|
15
23
|
err_console.print(":boom: [bold red]Access failed[/bold red] - No access token found")
|
16
24
|
err_console.print("Login with [italic]remotive cloud auth login[/italic]")
|
17
25
|
err_console.print(
|
@@ -20,6 +28,37 @@ def read_token() -> str:
|
|
20
28
|
)
|
21
29
|
sys.exit(1)
|
22
30
|
|
23
|
-
|
24
|
-
|
25
|
-
|
31
|
+
return _read_file(TOKEN_SECRET_FILE_PATH)
|
32
|
+
|
33
|
+
|
34
|
+
def list_personal_token_files() -> list[Path]:
|
35
|
+
return [f for f in CONFIG_DIR_PATH.iterdir() if f.is_file() and f.name.startswith("personal-")]
|
36
|
+
|
37
|
+
|
38
|
+
def list_service_account_token_files() -> list[Path]:
|
39
|
+
return [f for f in CONFIG_DIR_PATH.iterdir() if f.is_file() and f.name.startswith("service-account-")]
|
40
|
+
|
41
|
+
|
42
|
+
def write_secret_token(secret: str) -> Path:
|
43
|
+
return _write_settings_file(TOKEN_SECRET_FILE_PATH, secret)
|
44
|
+
|
45
|
+
|
46
|
+
def clear_secret_token() -> None:
|
47
|
+
TOKEN_SECRET_FILE_PATH.unlink()
|
48
|
+
|
49
|
+
|
50
|
+
def _read_file(path: Path) -> str:
|
51
|
+
with open(path, "r", encoding="utf-8") as f:
|
52
|
+
return f.read()
|
53
|
+
|
54
|
+
|
55
|
+
def _write_settings_file(path: Path, data: str) -> Path:
|
56
|
+
if CONFIG_DIR_PATH not in path.parents:
|
57
|
+
raise InvalidSettingsFileError(f"file {path} not in settings dir {CONFIG_DIR_PATH}")
|
58
|
+
|
59
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
60
|
+
|
61
|
+
with open(path, "w", encoding="utf8") as f:
|
62
|
+
f.write(data)
|
63
|
+
|
64
|
+
return path
|
@@ -1,45 +1,43 @@
|
|
1
|
-
cli/__about__.py,sha256=qXVkxWb3aPCF-4MjQhB0wqL2GEblEH4Qwk70o29UkJk,122
|
2
1
|
cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
2
|
cli/broker/brokers.py,sha256=oUadEL6xQ4bhXucBH-ZjL67VuERf19kn1g240v_lEpg,3197
|
4
|
-
cli/broker/export.py,sha256=
|
3
|
+
cli/broker/export.py,sha256=3sG9i6ZwOQW6snu87NSzOL2_giQTYQMzQlpPg7z8n78,4431
|
5
4
|
cli/broker/files.py,sha256=_MVwitQ5Z9-lNDb3biXqnlkKti8rizTEw0nnAViussU,4181
|
6
5
|
cli/broker/lib/__about__.py,sha256=xnZ5V6ZcHW9dhWLWdMzVjYJbEnMKpeXm0_S_mbNzypE,141
|
7
|
-
cli/broker/lib/broker.py,sha256=
|
6
|
+
cli/broker/lib/broker.py,sha256=iBv-uegVD6awnhkukV50CcZywIybEO3qvI0YU47KcGo,23949
|
8
7
|
cli/broker/license_flows.py,sha256=qJplaeugkUiypFGPdEIl5Asqlf7W3geJ-wU-QbYMP_8,7216
|
9
8
|
cli/broker/licenses.py,sha256=Ddl243re8RoeP9CoWWbIzwDePQ9l8r7ixmbd1gqn8f0,3973
|
10
9
|
cli/broker/playback.py,sha256=hdDKXGPuIE3gcT-kgQltgn5jsPzK19Yh9hiNcgtkLX0,3992
|
11
10
|
cli/broker/record.py,sha256=Oa6hUpS0Dgnt0f6Ig33vl0Jy8wN7wMXfemaxXWjRVoQ,1414
|
12
11
|
cli/broker/scripting.py,sha256=8577_C6siOk90s4G1ItIfAoFIUAkS0ItUl5kqR0cD-k,3792
|
13
|
-
cli/broker/signals.py,sha256=
|
12
|
+
cli/broker/signals.py,sha256=llok_jUGWOzAiiQUK54uRDnDuonBOAYBDbPdnzCFdog,7075
|
14
13
|
cli/cloud/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
|
-
cli/cloud/auth.py,sha256=
|
16
|
-
cli/cloud/auth_tokens.py,sha256=
|
17
|
-
cli/cloud/brokers.py,sha256=
|
14
|
+
cli/cloud/auth.py,sha256=RBTDUGRBVsK28u-aeqzRIzHnW7FqH4KADlVlQEgBCww,2534
|
15
|
+
cli/cloud/auth_tokens.py,sha256=0U60Gk2-TnAUff5anZmTB1rOEninNvYy1o5ihCqgj8A,4525
|
16
|
+
cli/cloud/brokers.py,sha256=DNj79MTkPylKUQbr-iPUhQgfNJLAW8UehnvgpEmNH_k,3890
|
18
17
|
cli/cloud/cloud_cli.py,sha256=09YCHs8IivYsVJOsxlM5OMEqBdq3QUXtDsktcO8Kjyw,1263
|
19
18
|
cli/cloud/configs.py,sha256=xg3J-kaS-Pp0p9otV2cWl_oOWJzs_jZhXwFHz0gQxvc,4625
|
20
19
|
cli/cloud/filestorage.py,sha256=cCPDYwCyJxP4V_qK1_Gnsg_T-zVsw6QaZdY_l4s4vC0,5445
|
21
20
|
cli/cloud/organisations.py,sha256=txKQmSQEpTmeqlqngai8pwgQQEvRgeDd0dT_VzZ7RNc,752
|
22
21
|
cli/cloud/projects.py,sha256=YrwPJClC2Sq_y1HjPd_tzaiv4GEnnsXSXHBhtQCPdK0,1431
|
23
|
-
cli/cloud/recordings.py,sha256=
|
22
|
+
cli/cloud/recordings.py,sha256=jai5Gim28UmZFGniUI9qKDwtLoi2Nllv4eyPeIk3OAc,25366
|
24
23
|
cli/cloud/recordings_playback.py,sha256=PRzftmvG2iePrL9f6qTEXVOnyJ-etcyzn5w9CCxcSto,11539
|
25
|
-
cli/cloud/rest_helper.py,sha256=
|
24
|
+
cli/cloud/rest_helper.py,sha256=lZp0NjQ8yOaggQGNiqNxHex_YFOmuq0rnLPtpLq3Z3Q,11470
|
26
25
|
cli/cloud/resumable_upload.py,sha256=sYThyhseXRniOMbctbO5p4BGVb9b7BXVBcmcZXwnClM,3550
|
27
26
|
cli/cloud/sample_recordings.py,sha256=OVX32U1dkkkJZysbgr5Dy515oOQKnwBAbZYzV_QUu1g,690
|
28
|
-
cli/cloud/service_account_tokens.py,sha256=
|
27
|
+
cli/cloud/service_account_tokens.py,sha256=263u1bRmBKfYsxL6TV6YjReUBUaVHWc3ETCd7AS3DTU,2297
|
29
28
|
cli/cloud/service_accounts.py,sha256=XOIPobUamCLIaufjyvb33XJDwy6uRqW5ZljZx3GYEfo,1659
|
30
29
|
cli/connect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
31
30
|
cli/connect/connect.py,sha256=U--6dtHxUlvE81J37rABFez4TbF7AXWOpZYZnL7sPMY,3994
|
32
|
-
cli/connect/protopie/protopie.py,sha256=
|
31
|
+
cli/connect/protopie/protopie.py,sha256=KBMbBwdkUVgV2X7AXTHweVqYVHv4akG875FVc36gsyg,6349
|
33
32
|
cli/errors.py,sha256=CXYArw1W82bRFwJkJ3tD-Ek1huKeah502DGMvPxHYFo,1366
|
34
|
-
cli/remotive.py,sha256=
|
35
|
-
cli/
|
36
|
-
cli/settings.py,sha256=MikGisXMNJTGtICBcLhfLZc2_ELCOaZmJspdLNwNRvY,833
|
33
|
+
cli/remotive.py,sha256=z834JeOwENyUM4bS74_zE95sGwu1efgfDVtCLKV5rV0,1789
|
34
|
+
cli/settings.py,sha256=A5rtp_1oix7Com5aHCAHdwJqxoV2LgxpYXwCe40v7oY,2072
|
37
35
|
cli/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
38
36
|
cli/tools/can/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
39
37
|
cli/tools/can/can.py,sha256=8uATViSFlpkdSiIm4fzbuQi1_m7V9Pym-K17TaJQRHU,2262
|
40
38
|
cli/tools/tools.py,sha256=0KU-hXR1f9xHP4BOG9A9eXfmICLmNuQCOU8ueF6iGg0,198
|
41
|
-
remotivelabs_cli-0.0.
|
42
|
-
remotivelabs_cli-0.0.
|
43
|
-
remotivelabs_cli-0.0.
|
44
|
-
remotivelabs_cli-0.0.
|
45
|
-
remotivelabs_cli-0.0.
|
39
|
+
remotivelabs_cli-0.0.30.dist-info/LICENSE,sha256=qDPP_yfuv1fF-u7EfexN-cN3M8aFgGVndGhGLovLKz0,608
|
40
|
+
remotivelabs_cli-0.0.30.dist-info/METADATA,sha256=82x7il8xKpkWGgoxjahiE9bW3QBSiPC--YuUG2d-Prs,1318
|
41
|
+
remotivelabs_cli-0.0.30.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
42
|
+
remotivelabs_cli-0.0.30.dist-info/entry_points.txt,sha256=lvDhPgagLqW_KTnLPCwKSqfYlEp-1uYVosRiPjsVj10,45
|
43
|
+
remotivelabs_cli-0.0.30.dist-info/RECORD,,
|
cli/__about__.py
DELETED
cli/requirements.txt
DELETED
File without changes
|
File without changes
|
File without changes
|