remotivelabs-cli 0.1.1__py3-none-any.whl → 0.2.0a2__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/cloud/auth/cmd.py +2 -2
- cli/cloud/auth/login.py +2 -1
- cli/cloud/auth_tokens.py +20 -4
- cli/cloud/organisations.py +1 -1
- cli/errors.py +1 -1
- cli/remotive.py +16 -7
- cli/settings/__init__.py +3 -2
- cli/settings/config_file.py +55 -6
- cli/settings/core.py +19 -29
- cli/settings/token_file.py +18 -0
- cli/topology/cmd.py +98 -0
- {remotivelabs_cli-0.1.1.dist-info → remotivelabs_cli-0.2.0a2.dist-info}/METADATA +1 -1
- {remotivelabs_cli-0.1.1.dist-info → remotivelabs_cli-0.2.0a2.dist-info}/RECORD +16 -15
- {remotivelabs_cli-0.1.1.dist-info → remotivelabs_cli-0.2.0a2.dist-info}/LICENSE +0 -0
- {remotivelabs_cli-0.1.1.dist-info → remotivelabs_cli-0.2.0a2.dist-info}/WHEEL +0 -0
- {remotivelabs_cli-0.1.1.dist-info → remotivelabs_cli-0.2.0a2.dist-info}/entry_points.txt +0 -0
cli/cloud/auth/cmd.py
CHANGED
@@ -61,9 +61,9 @@ def print_access_token(
|
|
61
61
|
else:
|
62
62
|
config = settings.get_cli_config()
|
63
63
|
if account in config.accounts:
|
64
|
-
|
64
|
+
token_file_name = config.accounts[account].credentials_file
|
65
65
|
try:
|
66
|
-
print(settings.get_token_file(
|
66
|
+
print(settings.get_token_file(token_file_name).token)
|
67
67
|
except TokenNotFoundError:
|
68
68
|
ErrorPrinter.print_generic_error(f"Token file for {account} could not be found", exit_code=1)
|
69
69
|
else:
|
cli/cloud/auth/login.py
CHANGED
@@ -18,7 +18,8 @@ from typing_extensions import override
|
|
18
18
|
|
19
19
|
from cli.cloud.auth_tokens import do_activate, prompt_to_set_org
|
20
20
|
from cli.errors import ErrorPrinter
|
21
|
-
from cli.settings import
|
21
|
+
from cli.settings import TokenNotFoundError, settings
|
22
|
+
from cli.settings.token_file import TokenFile
|
22
23
|
from cli.utils.rest_helper import RestHelper as Rest
|
23
24
|
|
24
25
|
httpd: HTTPServer
|
cli/cloud/auth_tokens.py
CHANGED
@@ -9,7 +9,9 @@ from rich.table import Table
|
|
9
9
|
from cli.api.cloud import tokens
|
10
10
|
from cli.cloud.organisations import do_select_default_org
|
11
11
|
from cli.errors import ErrorPrinter
|
12
|
-
from cli.settings import
|
12
|
+
from cli.settings import TokenNotFoundError, settings
|
13
|
+
from cli.settings.config_file import Account
|
14
|
+
from cli.settings.token_file import TokenFile
|
13
15
|
from cli.typer import typer_utils
|
14
16
|
from cli.utils.rest_helper import RestHelper as Rest
|
15
17
|
|
@@ -39,8 +41,13 @@ def _prompt_choice( # noqa: C901, PLR0912
|
|
39
41
|
|
40
42
|
for token in choices:
|
41
43
|
account = accounts.get(token.account.email)
|
42
|
-
if account and account.
|
43
|
-
|
44
|
+
if account and account.credentials_file:
|
45
|
+
try:
|
46
|
+
token_file = settings.get_token_file(account.credentials_file)
|
47
|
+
if token_file.name in (token.name or ""):
|
48
|
+
included_tokens.append(token)
|
49
|
+
except TokenNotFoundError:
|
50
|
+
excluded_tokens.append(token)
|
44
51
|
else:
|
45
52
|
excluded_tokens.append(token)
|
46
53
|
|
@@ -49,9 +56,18 @@ def _prompt_choice( # noqa: C901, PLR0912
|
|
49
56
|
|
50
57
|
included_tokens.sort(key=lambda token: token.created, reverse=True)
|
51
58
|
|
59
|
+
def get_token_or_none(account: Optional[Account]) -> Optional[TokenFile]:
|
60
|
+
if account is None:
|
61
|
+
return None
|
62
|
+
try:
|
63
|
+
return settings.get_token_file(account.credentials_file)
|
64
|
+
except TokenNotFoundError:
|
65
|
+
return None
|
66
|
+
|
52
67
|
active_token_index = None
|
53
68
|
for idx, choice in enumerate(included_tokens, start=1):
|
54
|
-
|
69
|
+
active_token = get_token_or_none(active_account)
|
70
|
+
is_active = active_account is not None and active_token is not None and active_token.name == choice.name
|
55
71
|
active_token_index = idx if is_active else active_token_index
|
56
72
|
table.add_row(
|
57
73
|
f"[yellow]{idx}",
|
cli/cloud/organisations.py
CHANGED
@@ -85,7 +85,7 @@ def do_select_default_org(organisation_uid: Optional[str] = None, get: bool = Fa
|
|
85
85
|
else:
|
86
86
|
account = settings.get_cli_config().get_active()
|
87
87
|
if account is not None:
|
88
|
-
token = settings.get_token_file(account.
|
88
|
+
token = settings.get_token_file(account.credentials_file)
|
89
89
|
if token.type != "authorized_user":
|
90
90
|
ErrorPrinter.print_hint(
|
91
91
|
"You must supply the organization name as argument when using a service-account since the "
|
cli/errors.py
CHANGED
cli/remotive.py
CHANGED
@@ -9,14 +9,14 @@ from rich.console import Console
|
|
9
9
|
from trogon import Trogon # type: ignore
|
10
10
|
from typer.main import get_group
|
11
11
|
|
12
|
+
from cli.broker.brokers import app as broker_app
|
13
|
+
from cli.cloud.cloud_cli import app as cloud_app
|
14
|
+
from cli.connect.connect import app as connect_app
|
15
|
+
from cli.settings import settings
|
12
16
|
from cli.settings.migrate_all_token_files import migrate_any_legacy_tokens
|
13
|
-
|
14
|
-
from .
|
15
|
-
from .
|
16
|
-
from .connect.connect import app as connect_app
|
17
|
-
from .settings import settings
|
18
|
-
from .tools.tools import app as tools_app
|
19
|
-
from .typer import typer_utils
|
17
|
+
from cli.tools.tools import app as tools_app
|
18
|
+
from cli.topology.cmd import app as topology_app
|
19
|
+
from cli.typer import typer_utils
|
20
20
|
|
21
21
|
err_console = Console(stderr=True)
|
22
22
|
|
@@ -90,5 +90,14 @@ app.add_typer(
|
|
90
90
|
name="cloud",
|
91
91
|
help="Manage resources in RemotiveCloud",
|
92
92
|
)
|
93
|
+
app.add_typer(
|
94
|
+
topology_app,
|
95
|
+
name="topology",
|
96
|
+
help="""
|
97
|
+
RemotiveTopology actions
|
98
|
+
|
99
|
+
Read more at https://docs.remotivelabs.com/docs/remotive-topology
|
100
|
+
""",
|
101
|
+
)
|
93
102
|
app.add_typer(connect_app, name="connect", help="Integrations with other systems")
|
94
103
|
app.add_typer(tools_app, name="tools")
|
cli/settings/__init__.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
from cli.settings.core import InvalidSettingsFilePathError, Settings, TokenNotFoundError, settings
|
2
|
-
from cli.settings.token_file import TokenFile
|
3
2
|
|
4
|
-
|
3
|
+
# from cli.settings.token_file import TokenFile
|
4
|
+
|
5
|
+
__all__ = ["settings", "TokenNotFoundError", "InvalidSettingsFilePathError", "Settings"]
|
cli/settings/config_file.py
CHANGED
@@ -4,15 +4,64 @@ import dataclasses
|
|
4
4
|
import json
|
5
5
|
from dataclasses import dataclass
|
6
6
|
from json import JSONDecodeError
|
7
|
-
from typing import Dict, Optional
|
7
|
+
from typing import Any, Dict, Optional
|
8
8
|
|
9
9
|
from dacite import from_dict
|
10
10
|
|
11
|
+
from cli.settings.token_file import TokenFile
|
12
|
+
|
13
|
+
|
14
|
+
def upgrade_config(config: dict[str, Any]) -> Optional[dict[str, Any]]:
|
15
|
+
"""
|
16
|
+
Reads a JSON config from in_path, replaces each account's 'credentials_name'
|
17
|
+
with 'credentials_file' (by calling get_filename_for_name), and writes the result
|
18
|
+
back to out_path (or overwrites in_path if out_path is None).
|
19
|
+
"""
|
20
|
+
from cli.settings import TokenNotFoundError, settings
|
21
|
+
|
22
|
+
accounts = config.get("accounts", {})
|
23
|
+
to_delete = []
|
24
|
+
found_old = False
|
25
|
+
for account, info in list(accounts.items()):
|
26
|
+
cred_name = info.pop("credentials_name", None)
|
27
|
+
if not cred_name:
|
28
|
+
continue
|
29
|
+
found_old = True
|
30
|
+
try:
|
31
|
+
cred_file = settings.get_token_file(cred_name).get_token_file_name()
|
32
|
+
except TokenNotFoundError:
|
33
|
+
# schedule this account for removal
|
34
|
+
to_delete.append(account)
|
35
|
+
print(f"Dropping account {account!r}: token file for {cred_name} not found")
|
36
|
+
continue
|
37
|
+
|
38
|
+
info["credentials_file"] = cred_file
|
39
|
+
|
40
|
+
# actually remove them
|
41
|
+
for account in to_delete:
|
42
|
+
del accounts[account]
|
43
|
+
|
44
|
+
if found_old:
|
45
|
+
return config
|
46
|
+
return None
|
47
|
+
|
48
|
+
|
49
|
+
def _from_dict(data: dict[str, Any]) -> ConfigFile:
|
50
|
+
from cli.settings import settings
|
51
|
+
|
52
|
+
config = upgrade_config(data)
|
53
|
+
if config is not None:
|
54
|
+
print("Migrating old configuration format")
|
55
|
+
updated_config: ConfigFile = from_dict(ConfigFile, config)
|
56
|
+
settings.write_config_file(updated_config)
|
57
|
+
return updated_config
|
58
|
+
return from_dict(ConfigFile, data)
|
59
|
+
|
11
60
|
|
12
61
|
def loads(data: str) -> ConfigFile:
|
13
62
|
try:
|
14
63
|
d = json.loads(data)
|
15
|
-
return
|
64
|
+
return _from_dict(d)
|
16
65
|
except JSONDecodeError as e:
|
17
66
|
# ErrorPrinter.print_generic_error("Invalid json format, config.json")
|
18
67
|
raise JSONDecodeError(
|
@@ -26,7 +75,7 @@ def dumps(config: ConfigFile) -> str:
|
|
26
75
|
|
27
76
|
@dataclass
|
28
77
|
class Account:
|
29
|
-
|
78
|
+
credentials_file: str
|
30
79
|
default_organization: Optional[str] = None
|
31
80
|
# Add project as well
|
32
81
|
|
@@ -66,15 +115,15 @@ class ConfigFile:
|
|
66
115
|
if self.accounts:
|
67
116
|
self.accounts.pop(email, None)
|
68
117
|
|
69
|
-
def init_account(self, email: str,
|
118
|
+
def init_account(self, email: str, token_file: TokenFile) -> None:
|
70
119
|
if self.accounts is None:
|
71
120
|
self.accounts = {}
|
72
121
|
|
73
122
|
account = self.accounts.get(email)
|
74
123
|
if not account:
|
75
|
-
account = Account(
|
124
|
+
account = Account(credentials_file=token_file.get_token_file_name())
|
76
125
|
else:
|
77
|
-
account.
|
126
|
+
account.credentials_file = token_file.get_token_file_name()
|
78
127
|
self.accounts[email] = account
|
79
128
|
|
80
129
|
def set_account_field(self, email: str, default_organization: Optional[str] = None) -> ConfigFile:
|
cli/settings/core.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import os
|
4
|
-
import re
|
5
4
|
import shutil
|
6
5
|
import stat
|
7
6
|
import sys
|
@@ -13,7 +12,7 @@ from typing import Optional, Tuple, Union
|
|
13
12
|
from rich.console import Console
|
14
13
|
|
15
14
|
from cli.errors import ErrorPrinter
|
16
|
-
from cli.settings import config_file
|
15
|
+
from cli.settings import config_file, token_file
|
17
16
|
from cli.settings import token_file as tf
|
18
17
|
from cli.settings.config_file import ConfigFile
|
19
18
|
from cli.settings.token_file import TokenFile
|
@@ -27,9 +26,6 @@ DEPRECATED_CONFIG_DIR_PATH = Path.home() / ".remotive"
|
|
27
26
|
|
28
27
|
CLI_CONFIG_FILE_NAME = "config.json"
|
29
28
|
ACTIVE_TOKEN_FILE_NAME = "cloud.secret.token"
|
30
|
-
PERSONAL_TOKEN_FILE_PREFIX = "personal-token-"
|
31
|
-
SERVICE_ACCOUNT_TOKEN_FILE_PREFIX = "service-account-token-"
|
32
|
-
|
33
29
|
|
34
30
|
TokenFileMetadata = Tuple[TokenFile, Path]
|
35
31
|
|
@@ -128,8 +124,9 @@ class Settings:
|
|
128
124
|
|
129
125
|
active_account = self.get_cli_config().get_active()
|
130
126
|
if active_account is not None:
|
131
|
-
|
132
|
-
return self.
|
127
|
+
token_file_name = active_account.credentials_file
|
128
|
+
return self._read_token_file(self.config_dir / token_file_name)
|
129
|
+
|
133
130
|
raise TokenNotFoundError
|
134
131
|
# if not self._active_secret_token_path.exists():
|
135
132
|
# raise TokenNotFoundError("no active token file found")
|
@@ -213,21 +210,14 @@ class Settings:
|
|
213
210
|
Add a personal token
|
214
211
|
"""
|
215
212
|
token_file = tf.loads(token)
|
216
|
-
|
217
|
-
def email_to_safe_filename(email: str) -> str:
|
218
|
-
# Replace any invalid character with an underscore
|
219
|
-
return re.sub(r'[<>:"/\\|?*]', "_", email)
|
220
|
-
|
221
|
-
# From now, user will never be None when adding a token so in this case token_file.user is never None
|
222
|
-
email = email_to_safe_filename(token_file.account.email) if token_file.account is not None else "unknown"
|
223
|
-
file = f"{PERSONAL_TOKEN_FILE_PREFIX}{token_file.name}-{email}.json"
|
213
|
+
file = token_file.get_token_file_name()
|
224
214
|
path = self.config_dir / file
|
225
215
|
if path.exists() and not overwrite_if_exists:
|
226
216
|
raise FileExistsError(f"Token file already exists: {path}")
|
227
217
|
|
228
218
|
self._write_token_file(path, token_file)
|
229
219
|
cli_config = self.get_cli_config()
|
230
|
-
cli_config.init_account(email=token_file.account.email,
|
220
|
+
cli_config.init_account(email=token_file.account.email, token_file=token_file)
|
231
221
|
self._write_config_file(cli_config)
|
232
222
|
|
233
223
|
if activate:
|
@@ -249,21 +239,15 @@ class Settings:
|
|
249
239
|
|
250
240
|
def add_service_account_token(self, token: str) -> TokenFile:
|
251
241
|
token_file = tf.loads(token)
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
return re.sub(r'[<>:"/\\|?*]', "_", email)
|
256
|
-
|
257
|
-
# From now, user will never be None when adding a token so in this case token_file.user is never None
|
258
|
-
|
259
|
-
email = email_to_safe_filename(token_file.account.email) if token_file.account is not None else "unknown"
|
260
|
-
file = f"{SERVICE_ACCOUNT_TOKEN_FILE_PREFIX}{token_file.name}-{email}.json"
|
242
|
+
if token_file.type != "service_account":
|
243
|
+
raise ValueError("Token type MUST be service_account")
|
244
|
+
file = token_file.get_token_file_name()
|
261
245
|
path = self.config_dir / file
|
262
246
|
|
263
247
|
self._write_token_file(path, token_file)
|
264
248
|
print(f"Service account token stored at {path}")
|
265
249
|
cli_config = self.get_cli_config()
|
266
|
-
cli_config.init_account(email=token_file.account.email,
|
250
|
+
cli_config.init_account(email=token_file.account.email, token_file=token_file)
|
267
251
|
self._write_config_file(cli_config)
|
268
252
|
|
269
253
|
# if activate:
|
@@ -293,10 +277,10 @@ class Settings:
|
|
293
277
|
return [f[1] for f in self._list_service_account_tokens()]
|
294
278
|
|
295
279
|
def _list_personal_tokens(self) -> list[TokenFileMetadata]:
|
296
|
-
return self._list_token_files(prefix=PERSONAL_TOKEN_FILE_PREFIX)
|
280
|
+
return self._list_token_files(prefix=token_file.PERSONAL_TOKEN_FILE_PREFIX)
|
297
281
|
|
298
282
|
def _list_service_account_tokens(self) -> list[TokenFileMetadata]:
|
299
|
-
return self._list_token_files(prefix=SERVICE_ACCOUNT_TOKEN_FILE_PREFIX)
|
283
|
+
return self._list_token_files(prefix=token_file.SERVICE_ACCOUNT_TOKEN_FILE_PREFIX)
|
300
284
|
|
301
285
|
def _get_token_by_name(self, name: str) -> TokenFileMetadata:
|
302
286
|
token_files = self._list_token_files()
|
@@ -318,7 +302,9 @@ class Settings:
|
|
318
302
|
return False
|
319
303
|
|
320
304
|
def is_valid_token_file(path: Path) -> bool:
|
321
|
-
is_token_file = path.name.startswith(SERVICE_ACCOUNT_TOKEN_FILE_PREFIX) or path.name.startswith(
|
305
|
+
is_token_file = path.name.startswith(tf.SERVICE_ACCOUNT_TOKEN_FILE_PREFIX) or path.name.startswith(
|
306
|
+
tf.PERSONAL_TOKEN_FILE_PREFIX
|
307
|
+
)
|
322
308
|
has_correct_prefix = path.is_file() and path.name.startswith(prefix)
|
323
309
|
is_active_secret = path == self._active_secret_token_path
|
324
310
|
is_cli_config = path == self._cli_config
|
@@ -347,6 +333,10 @@ class Settings:
|
|
347
333
|
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
|
348
334
|
return path
|
349
335
|
|
336
|
+
# Temporary function while considering how to solve this
|
337
|
+
def write_config_file(self, config: ConfigFile) -> Path:
|
338
|
+
return self._write_config_file(config)
|
339
|
+
|
350
340
|
def _write_config_file(self, config: ConfigFile) -> Path:
|
351
341
|
data = config_file.dumps(config)
|
352
342
|
path = self._write_file(self._cli_config, data)
|
cli/settings/token_file.py
CHANGED
@@ -2,10 +2,13 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import dataclasses
|
4
4
|
import json
|
5
|
+
import re
|
5
6
|
from dataclasses import dataclass
|
6
7
|
from datetime import date, datetime
|
7
8
|
from typing import Any, Literal
|
8
9
|
|
10
|
+
# from cli.settings.core import PERSONAL_TOKEN_FILE_PREFIX, SERVICE_ACCOUNT_TOKEN_FILE_PREFIX
|
11
|
+
|
9
12
|
DEFAULT_EMAIL = "unknown@remotivecloud.com"
|
10
13
|
|
11
14
|
TokenType = Literal["authorized_user", "service_account"]
|
@@ -62,6 +65,10 @@ class TokenFileAccount:
|
|
62
65
|
email: str
|
63
66
|
|
64
67
|
|
68
|
+
PERSONAL_TOKEN_FILE_PREFIX = "personal-token-"
|
69
|
+
SERVICE_ACCOUNT_TOKEN_FILE_PREFIX = "service-account-token-"
|
70
|
+
|
71
|
+
|
65
72
|
@dataclass
|
66
73
|
class TokenFile:
|
67
74
|
version: str
|
@@ -72,6 +79,17 @@ class TokenFile:
|
|
72
79
|
expires: date
|
73
80
|
account: TokenFileAccount
|
74
81
|
|
82
|
+
def get_token_file_name(self) -> str:
|
83
|
+
def email_to_safe_filename(email: str) -> str:
|
84
|
+
# Replace any invalid character with an underscore
|
85
|
+
return re.sub(r'[<>:"/\\|?*]', "_", email)
|
86
|
+
|
87
|
+
# From now, user will never be None when adding a token so in this case token_file.user is never None
|
88
|
+
email = email_to_safe_filename(self.account.email) if self.account is not None else "unknown"
|
89
|
+
if self.type == "authorized_user":
|
90
|
+
return f"{PERSONAL_TOKEN_FILE_PREFIX}{self.name}-{email}.json"
|
91
|
+
return f"{SERVICE_ACCOUNT_TOKEN_FILE_PREFIX}{self.name}-{email}.json"
|
92
|
+
|
75
93
|
def is_expired(self) -> bool:
|
76
94
|
return datetime.today().date() > self.expires
|
77
95
|
|
cli/topology/cmd.py
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
import dataclasses
|
2
|
+
import datetime
|
3
|
+
from typing import Any
|
4
|
+
|
5
|
+
import typer
|
6
|
+
from rich.console import Console
|
7
|
+
|
8
|
+
from cli.errors import ErrorPrinter
|
9
|
+
from cli.settings import TokenNotFoundError, settings
|
10
|
+
from cli.typer import typer_utils
|
11
|
+
from cli.utils.rest_helper import RestHelper
|
12
|
+
|
13
|
+
HELP = """
|
14
|
+
RemotiveTopology commands
|
15
|
+
"""
|
16
|
+
console = Console()
|
17
|
+
app = typer_utils.create_typer(help=HELP)
|
18
|
+
|
19
|
+
|
20
|
+
@dataclasses.dataclass
|
21
|
+
class Subscription:
|
22
|
+
type: str
|
23
|
+
display_name: str
|
24
|
+
feature: str
|
25
|
+
start_date: str
|
26
|
+
end_date: str
|
27
|
+
|
28
|
+
|
29
|
+
def _print_current_subscription(subscription_info: dict[str, Any]) -> None:
|
30
|
+
subscription_type = subscription_info["subscriptionType"]
|
31
|
+
|
32
|
+
if subscription_type == "trial":
|
33
|
+
expires = datetime.datetime.fromisoformat(subscription_info["endDate"])
|
34
|
+
if expires < datetime.datetime.now():
|
35
|
+
console.print(f"Your Topology trial expired {subscription_info['endDate']}, please contact support@remotivelabs.com")
|
36
|
+
else:
|
37
|
+
console.print(f"You already have an active topology trial, it expires {subscription_info['endDate']}")
|
38
|
+
# A paid subscription might not have an endDate
|
39
|
+
elif subscription_type == "paid":
|
40
|
+
if "endDate" in subscription_type:
|
41
|
+
expires = datetime.datetime.fromisoformat(subscription_info["endDate"])
|
42
|
+
else:
|
43
|
+
expires = None
|
44
|
+
|
45
|
+
if expires is not None and expires < datetime.datetime.now():
|
46
|
+
console.print(f"Topology subscription has ended, expired {subscription_info['endDate']}")
|
47
|
+
else:
|
48
|
+
console.print(f"You already have an active topology subscription, it expires {expires if expires is not None else 'Never'}")
|
49
|
+
|
50
|
+
else:
|
51
|
+
ErrorPrinter.print_generic_error("Unexpected exception, please contact support@remotivelabs.com")
|
52
|
+
raise typer.Exit(1)
|
53
|
+
|
54
|
+
|
55
|
+
@app.command("start-trial")
|
56
|
+
def start_trial(
|
57
|
+
organization: str = typer.Option(None, help="Organization to start trial for", envvar="REMOTIVE_CLOUD_ORGANIZATION"),
|
58
|
+
) -> None:
|
59
|
+
"""
|
60
|
+
Allows you ta start a 30 day trial subscription for running RemotiveTopology, you can read more at https://docs.remotivelabs.com/docs/remotive-topology.
|
61
|
+
|
62
|
+
"""
|
63
|
+
RestHelper.use_progress("Checking access tokens...", transient=True)
|
64
|
+
try:
|
65
|
+
_ = settings.get_active_token_file()
|
66
|
+
except TokenNotFoundError:
|
67
|
+
if len(settings.list_personal_token_files()) == 0:
|
68
|
+
console.print(
|
69
|
+
"You must first sign in to RemotiveCloud, please use [bold]remotive cloud auth login[/bold] to sign-in"
|
70
|
+
"This requires a RemotiveCloud account, if you do not have an account you can sign-up at https://cloud.remotivelabs.com"
|
71
|
+
)
|
72
|
+
else:
|
73
|
+
console.print(
|
74
|
+
"You have not active account, please run [bold]remotive cloud auth activate[/bold] to choose an account"
|
75
|
+
"or [bold]remotive cloud auth login[/bold] to sign-in"
|
76
|
+
)
|
77
|
+
return
|
78
|
+
|
79
|
+
has_access = RestHelper.has_access("/api/whoami")
|
80
|
+
if not has_access:
|
81
|
+
ErrorPrinter.print_generic_message("Your current active credentials are not valid")
|
82
|
+
raise typer.Exit(1)
|
83
|
+
|
84
|
+
if organization is None and settings.get_cli_config().get_active_default_organisation() is None:
|
85
|
+
ErrorPrinter.print_hint("You have not specified any organization and no default organization is set")
|
86
|
+
raise typer.Exit(1)
|
87
|
+
|
88
|
+
sub = RestHelper.handle_get(f"/api/bu/{organization}/features/topology", return_response=True, allow_status_codes=[404, 403])
|
89
|
+
if sub.status_code == 404:
|
90
|
+
created = RestHelper.handle_post(f"/api/bu/{organization}/features/topology", return_response=True)
|
91
|
+
console.print(f"Topology trial started, it expires {created.json()['endDate']}")
|
92
|
+
elif sub.status_code == 403:
|
93
|
+
ErrorPrinter.print_generic_error(f"You are not allowed to start-trial topology in organization {organization}")
|
94
|
+
raise typer.Exit(1)
|
95
|
+
else:
|
96
|
+
subscription_info = sub.json()
|
97
|
+
_print_current_subscription(subscription_info)
|
98
|
+
return
|
@@ -14,13 +14,13 @@ cli/broker/scripting.py,sha256=LFLdaBNxe2sfpcxhDmRlAbEorjL3SJZNK-zEdLQ9ySU,3854
|
|
14
14
|
cli/broker/signals.py,sha256=MFj_bOLIxHY1v3XPkKk6n8U3JLaY8nrXHahRQaVse6s,8207
|
15
15
|
cli/cloud/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
16
|
cli/cloud/auth/__init__.py,sha256=MtQ01-n8CgZb9Y_SvxwZUgj44Yo0dFAU3_XwhQiUYtw,54
|
17
|
-
cli/cloud/auth/cmd.py,sha256=
|
18
|
-
cli/cloud/auth/login.py,sha256=
|
19
|
-
cli/cloud/auth_tokens.py,sha256=
|
17
|
+
cli/cloud/auth/cmd.py,sha256=1QEYkaRO4Q1wbOemEwnLCl6YJivC8OdtOguH-bc6N8Y,2930
|
18
|
+
cli/cloud/auth/login.py,sha256=dX6M5ysE0n9Zg3gVT7hJbChxTsmuba-Z-1Or6DCFYis,11511
|
19
|
+
cli/cloud/auth_tokens.py,sha256=RNvvHN9bJ_gfUUa4JL0YXpvTKFYKf922BWjvltElwMg,12964
|
20
20
|
cli/cloud/brokers.py,sha256=QTA9bmaK06LKEccF6IBgWBonC4VFrKwFQBsACX_IzYw,3896
|
21
21
|
cli/cloud/cloud_cli.py,sha256=q-oiaLcKC-BRamXfIFGn-BskRmJ3utA7-tI39lSs3Cs,1309
|
22
22
|
cli/cloud/configs.py,sha256=uv46nUoGXOr99smQHahv_ageDv6bGYfUnlRlxcS5D9A,5125
|
23
|
-
cli/cloud/organisations.py,sha256=
|
23
|
+
cli/cloud/organisations.py,sha256=dX8h1SMLxKBvOaxuiRP6nFnkYdek91fjTRtuNRLRHeQ,4116
|
24
24
|
cli/cloud/projects.py,sha256=ecn5Y8UKhgYnHSJQACUk1GNZt9EF8ug4B-6MCr8rZqM,1487
|
25
25
|
cli/cloud/recordings.py,sha256=B0XOj8LIm3hBqBzVKPLPvPUCXCKZBTEISssrijK481w,24855
|
26
26
|
cli/cloud/recordings_playback.py,sha256=XZoVyujufMQFN2v_Nwsf8tOqn61yLEpAf2z_u5uhXik,11532
|
@@ -36,24 +36,25 @@ cli/cloud/uri.py,sha256=QZCus--KJQlVwGCOzZqiglvj8VvSRKxfVvN33Pilgyg,3616
|
|
36
36
|
cli/connect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
37
|
cli/connect/connect.py,sha256=SH2DNTTVLu2dNpk6xIah1-KJZAqrK_7Skt8RKp8Mjh8,4231
|
38
38
|
cli/connect/protopie/protopie.py,sha256=ElmrGaV0ivb85wo0gLzCAXZhmSmIDASaCVlF1iQblLI,6532
|
39
|
-
cli/errors.py,sha256=
|
40
|
-
cli/remotive.py,sha256=
|
41
|
-
cli/settings/__init__.py,sha256=
|
42
|
-
cli/settings/config_file.py,sha256=
|
43
|
-
cli/settings/core.py,sha256=
|
39
|
+
cli/errors.py,sha256=djODw6sdMJXzOsuAUOP3N13nfmm1sIP3Pe6tllGdozM,1657
|
40
|
+
cli/remotive.py,sha256=5Qt9jayyj1wGSyj2eyLzeJaZ1F8UrtznFzrPPcDqZnY,2988
|
41
|
+
cli/settings/__init__.py,sha256=ocADDtwE8qsVN_0RM71f2VftFFCHdJrcG37hP7QmC3A,238
|
42
|
+
cli/settings/config_file.py,sha256=y5wUg2OILhpkxcamyiTkcCQOgy6ZkiEsLkTZnGdpmfA,4510
|
43
|
+
cli/settings/core.py,sha256=3VQ78muCAcM7buRIhanr6QgF73m3MP0Kdupc6cz3sww,15059
|
44
44
|
cli/settings/migrate_all_token_files.py,sha256=7kvHbpP4BtILJ8kPtb_bFnTnBYX9isZ4rwB5lfnEkbA,2722
|
45
45
|
cli/settings/migrate_token_file.py,sha256=Fp7Z_lNqSdoWY05TYwFW2QH8q9QhmB2TYSok6hV1Mic,1530
|
46
|
-
cli/settings/token_file.py,sha256=
|
46
|
+
cli/settings/token_file.py,sha256=RKtdZYOIb9CJjs2dHFlv6TZHgWuPVonAQphRuosXP0I,3050
|
47
47
|
cli/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
48
48
|
cli/tools/can/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
49
49
|
cli/tools/can/can.py,sha256=TtP5w8vb0QG4ObNhkWIDpRMdNelirFffoc_lFZy8ePM,2260
|
50
50
|
cli/tools/tools.py,sha256=jhLfrFDqkmWV3eBAzNwBf6WgDGrz7sOhgVCia36Twn8,232
|
51
|
+
cli/topology/cmd.py,sha256=Fs6tLomKhaie275Tc3FoKGY5cyWx2EEYdroUcfJBnmk,4033
|
51
52
|
cli/typer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
52
53
|
cli/typer/typer_utils.py,sha256=8SkvG9aKkfK9fTRsLD9pOBtWn9XSwtOXWg2RAk9FhOI,708
|
53
54
|
cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
54
55
|
cli/utils/rest_helper.py,sha256=b_FJY6MxnFSqo11qaHxkBFHfVlKf7Zj28Uxv9Oj7XY4,14141
|
55
|
-
remotivelabs_cli-0.
|
56
|
-
remotivelabs_cli-0.
|
57
|
-
remotivelabs_cli-0.
|
58
|
-
remotivelabs_cli-0.
|
59
|
-
remotivelabs_cli-0.
|
56
|
+
remotivelabs_cli-0.2.0a2.dist-info/LICENSE,sha256=qDPP_yfuv1fF-u7EfexN-cN3M8aFgGVndGhGLovLKz0,608
|
57
|
+
remotivelabs_cli-0.2.0a2.dist-info/METADATA,sha256=rxP2gpSWY3w0Lw47fAveqxqkLySPvo9qdMR67QHNoq4,1430
|
58
|
+
remotivelabs_cli-0.2.0a2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
59
|
+
remotivelabs_cli-0.2.0a2.dist-info/entry_points.txt,sha256=lvDhPgagLqW_KTnLPCwKSqfYlEp-1uYVosRiPjsVj10,45
|
60
|
+
remotivelabs_cli-0.2.0a2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|