remotivelabs-cli 0.2.0a2__py3-none-any.whl → 0.2.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/cloud/auth/cmd.py +0 -3
- cli/cloud/auth_tokens.py +15 -16
- cli/cloud/organisations.py +2 -2
- cli/cloud/recordings.py +7 -5
- cli/remotive.py +54 -22
- cli/settings/__init__.py +19 -3
- cli/settings/config_file.py +26 -74
- cli/settings/core.py +70 -149
- cli/settings/migration/__init__.py +0 -0
- cli/settings/{migrate_all_token_files.py → migration/migrate_all_token_files.py} +18 -12
- cli/settings/migration/migrate_config_file.py +59 -0
- cli/settings/migration/migrate_legacy_dirs.py +50 -0
- cli/settings/migration/migration_tools.py +36 -0
- cli/settings/state_file.py +32 -0
- cli/settings/token_file.py +92 -61
- cli/topology/cmd.py +19 -16
- cli/typer/typer_utils.py +1 -1
- cli/utils/rest_helper.py +1 -1
- cli/utils/time.py +11 -0
- cli/utils/version_check.py +112 -0
- {remotivelabs_cli-0.2.0a2.dist-info → remotivelabs_cli-0.2.2.dist-info}/METADATA +3 -1
- {remotivelabs_cli-0.2.0a2.dist-info → remotivelabs_cli-0.2.2.dist-info}/RECORD +26 -19
- /cli/settings/{migrate_token_file.py → migration/migrate_token_file.py} +0 -0
- {remotivelabs_cli-0.2.0a2.dist-info → remotivelabs_cli-0.2.2.dist-info}/LICENSE +0 -0
- {remotivelabs_cli-0.2.0a2.dist-info → remotivelabs_cli-0.2.2.dist-info}/WHEEL +0 -0
- {remotivelabs_cli-0.2.0a2.dist-info → remotivelabs_cli-0.2.2.dist-info}/entry_points.txt +0 -0
cli/settings/token_file.py
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import dataclasses
|
|
4
|
-
import json
|
|
5
3
|
import re
|
|
6
|
-
from dataclasses import dataclass
|
|
7
4
|
from datetime import date, datetime
|
|
8
5
|
from typing import Any, Literal
|
|
9
6
|
|
|
10
|
-
|
|
7
|
+
from pydantic import BaseModel, EmailStr, Field, field_validator, model_validator
|
|
8
|
+
|
|
9
|
+
from cli.utils.time import parse_date
|
|
11
10
|
|
|
12
11
|
DEFAULT_EMAIL = "unknown@remotivecloud.com"
|
|
12
|
+
PERSONAL_TOKEN_FILE_PREFIX = "personal-token-"
|
|
13
|
+
SERVICE_ACCOUNT_TOKEN_FILE_PREFIX = "service-account-token-"
|
|
13
14
|
|
|
14
15
|
TokenType = Literal["authorized_user", "service_account"]
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
def
|
|
18
|
-
|
|
19
|
-
return
|
|
18
|
+
def _email_to_safe_filename(email: str) -> str:
|
|
19
|
+
"""Replace any invalid character with an underscore"""
|
|
20
|
+
return re.sub(r'[<>:"/\\|?*]', "_", email)
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
def _parse_token_type(token: str) -> TokenType:
|
|
@@ -27,79 +28,109 @@ def _parse_token_type(token: str) -> TokenType:
|
|
|
27
28
|
raise ValueError(f"Unknown token type for token: {token}")
|
|
28
29
|
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
token_type = _parse_token_type(d["token"])
|
|
33
|
-
return TokenFile(
|
|
34
|
-
version="1.0",
|
|
35
|
-
type=token_type,
|
|
36
|
-
name=d["name"],
|
|
37
|
-
token=d["token"],
|
|
38
|
-
created=_parse_date(d["created"]),
|
|
39
|
-
expires=_parse_date(d["expires"]),
|
|
40
|
-
account=TokenFileAccount(email=DEFAULT_EMAIL),
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
account_email = d.get("account", {}).get("email", DEFAULT_EMAIL)
|
|
44
|
-
return TokenFile(
|
|
45
|
-
version=d["version"],
|
|
46
|
-
type=d["type"],
|
|
47
|
-
name=d["name"],
|
|
48
|
-
token=d["token"],
|
|
49
|
-
created=_parse_date(d["created"]),
|
|
50
|
-
expires=_parse_date(d["expires"]),
|
|
51
|
-
account=TokenFileAccount(email=account_email),
|
|
52
|
-
)
|
|
31
|
+
class TokenFileAccount(BaseModel):
|
|
32
|
+
email: EmailStr = DEFAULT_EMAIL
|
|
53
33
|
|
|
54
34
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
35
|
+
class TokenFile(BaseModel):
|
|
36
|
+
version: str = "1.0"
|
|
37
|
+
type: TokenType
|
|
38
|
+
name: str
|
|
39
|
+
token: str
|
|
40
|
+
created: date
|
|
41
|
+
expires: date
|
|
42
|
+
account: TokenFileAccount = Field(default_factory=TokenFileAccount)
|
|
58
43
|
|
|
59
|
-
|
|
60
|
-
|
|
44
|
+
@field_validator("created", "expires", mode="before")
|
|
45
|
+
@classmethod
|
|
46
|
+
def validate_parse_date(cls, value: str | date) -> date:
|
|
47
|
+
if isinstance(value, date):
|
|
48
|
+
return value
|
|
49
|
+
return parse_date(value)
|
|
61
50
|
|
|
51
|
+
@model_validator(mode="before")
|
|
52
|
+
@classmethod
|
|
53
|
+
def init_with_defaults(cls, json_data: Any) -> Any:
|
|
54
|
+
"""
|
|
55
|
+
Try to migrate old formats and missing fields as best we can.
|
|
62
56
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
NOTE: If we ever need to add a new version (like 2.0), we should add explicit classes for each version (e.g. TokenFileV1,
|
|
58
|
+
TokenFileV2, etc.), each with their own fields. This will allow us to migrate to new versions without breaking
|
|
59
|
+
backwards compatibility.
|
|
60
|
+
"""
|
|
61
|
+
if not isinstance(json_data, dict):
|
|
62
|
+
return json_data
|
|
66
63
|
|
|
64
|
+
if "version" not in json_data:
|
|
65
|
+
json_data["version"] = "1.0"
|
|
67
66
|
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
if "type" not in json_data and "token" in json_data:
|
|
68
|
+
json_data["type"] = _parse_token_type(json_data["token"])
|
|
70
69
|
|
|
70
|
+
if "account" not in json_data:
|
|
71
|
+
json_data["account"] = {"email": DEFAULT_EMAIL}
|
|
72
|
+
elif isinstance(json_data["account"], str):
|
|
73
|
+
json_data["account"] = {"email": json_data["account"]}
|
|
71
74
|
|
|
72
|
-
|
|
73
|
-
class TokenFile:
|
|
74
|
-
version: str
|
|
75
|
-
type: TokenType
|
|
76
|
-
name: str
|
|
77
|
-
token: str
|
|
78
|
-
created: date
|
|
79
|
-
expires: date
|
|
80
|
-
account: TokenFileAccount
|
|
75
|
+
return json_data
|
|
81
76
|
|
|
82
77
|
def get_token_file_name(self) -> str:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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"
|
|
78
|
+
"""
|
|
79
|
+
Returns the name of the token file using the proper file name format.
|
|
80
|
+
"""
|
|
81
|
+
email = _email_to_safe_filename(self.account.email) if self.account is not None else "unknown"
|
|
89
82
|
if self.type == "authorized_user":
|
|
90
83
|
return f"{PERSONAL_TOKEN_FILE_PREFIX}{self.name}-{email}.json"
|
|
91
84
|
return f"{SERVICE_ACCOUNT_TOKEN_FILE_PREFIX}{self.name}-{email}.json"
|
|
92
85
|
|
|
93
86
|
def is_expired(self) -> bool:
|
|
87
|
+
"""
|
|
88
|
+
Returns True if the token is expired, False otherwise.
|
|
89
|
+
"""
|
|
94
90
|
return datetime.today().date() > self.expires
|
|
95
91
|
|
|
96
92
|
def expires_in_days(self) -> int:
|
|
93
|
+
"""
|
|
94
|
+
Returns the number of days until the token expires.
|
|
95
|
+
"""
|
|
97
96
|
return (self.expires - datetime.today().date()).days
|
|
98
97
|
|
|
99
|
-
@
|
|
100
|
-
def from_json_str(data: str) -> TokenFile:
|
|
101
|
-
|
|
98
|
+
@classmethod
|
|
99
|
+
def from_json_str(cls, data: str) -> TokenFile:
|
|
100
|
+
"""
|
|
101
|
+
Creates a TokenFile from a JSON string.
|
|
102
|
+
"""
|
|
103
|
+
return cls.model_validate_json(data)
|
|
104
|
+
|
|
105
|
+
@classmethod
|
|
106
|
+
def from_dict(cls, data: dict[str, Any]) -> TokenFile:
|
|
107
|
+
"""
|
|
108
|
+
Creates a TokenFile from a dictionary.
|
|
109
|
+
"""
|
|
110
|
+
return cls.model_validate(data)
|
|
111
|
+
|
|
112
|
+
def to_json_str(self) -> str:
|
|
113
|
+
"""
|
|
114
|
+
Returns the JSON string representation of the TokenFile.
|
|
115
|
+
"""
|
|
116
|
+
return self.model_dump_json()
|
|
117
|
+
|
|
118
|
+
def to_dict(self) -> dict[str, Any]:
|
|
119
|
+
"""
|
|
120
|
+
Returns the dictionary representation of the TokenFile.
|
|
121
|
+
"""
|
|
122
|
+
return self.model_dump()
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def loads(data: str) -> TokenFile:
|
|
126
|
+
"""
|
|
127
|
+
Creates a TokenFile from a JSON string.
|
|
128
|
+
"""
|
|
129
|
+
return TokenFile.from_json_str(data)
|
|
130
|
+
|
|
102
131
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
132
|
+
def dumps(token_file: TokenFile) -> str:
|
|
133
|
+
"""
|
|
134
|
+
Returns the JSON string representation of the TokenFile.
|
|
135
|
+
"""
|
|
136
|
+
return token_file.to_json_str()
|
cli/topology/cmd.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import dataclasses
|
|
2
4
|
import datetime
|
|
3
5
|
from typing import Any
|
|
@@ -22,30 +24,31 @@ class Subscription:
|
|
|
22
24
|
type: str
|
|
23
25
|
display_name: str
|
|
24
26
|
feature: str
|
|
25
|
-
start_date: str
|
|
26
|
-
end_date: str
|
|
27
|
+
start_date: str # TODO: add datetime
|
|
28
|
+
end_date: str # TODO: add datetime
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
def _print_current_subscription(subscription_info: dict[str, Any]) -> None:
|
|
30
|
-
subscription_type = subscription_info
|
|
32
|
+
subscription_type = subscription_info.get("subscriptionType")
|
|
33
|
+
end_date_str = subscription_info.get("endDate")
|
|
34
|
+
now = datetime.datetime.now()
|
|
35
|
+
|
|
36
|
+
def parse_date(date_str: str | None) -> datetime.datetime | None:
|
|
37
|
+
return datetime.datetime.fromisoformat(date_str) if date_str else None
|
|
38
|
+
|
|
39
|
+
expires = parse_date(end_date_str)
|
|
31
40
|
|
|
32
41
|
if subscription_type == "trial":
|
|
33
|
-
expires
|
|
34
|
-
|
|
35
|
-
console.print(f"Your Topology trial expired {subscription_info['endDate']}, please contact support@remotivelabs.com")
|
|
42
|
+
if expires and expires < now:
|
|
43
|
+
console.print(f"Your Topology trial expired {end_date_str}, please contact support@remotivelabs.com")
|
|
36
44
|
else:
|
|
37
|
-
console.print(f"You already have an active topology trial, it expires {
|
|
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
|
|
45
|
+
console.print(f"You already have an active topology trial, it expires {end_date_str}")
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
elif subscription_type == "paid":
|
|
48
|
+
if expires and expires < now:
|
|
49
|
+
console.print(f"Topology subscription has ended, expired {end_date_str}")
|
|
47
50
|
else:
|
|
48
|
-
console.print(f"You already have an active topology subscription, it expires {
|
|
51
|
+
console.print(f"You already have an active topology subscription, it expires {end_date_str or 'Never'}")
|
|
49
52
|
|
|
50
53
|
else:
|
|
51
54
|
ErrorPrinter.print_generic_error("Unexpected exception, please contact support@remotivelabs.com")
|
cli/typer/typer_utils.py
CHANGED
|
@@ -17,7 +17,7 @@ console = Console()
|
|
|
17
17
|
def create_typer(**kwargs: Any) -> typer.Typer:
|
|
18
18
|
"""Create a Typer instance with default settings."""
|
|
19
19
|
# return typer.Typer(no_args_is_help=True, **kwargs)
|
|
20
|
-
return typer.Typer(cls=OrderCommands, no_args_is_help=True, **kwargs)
|
|
20
|
+
return typer.Typer(cls=OrderCommands, no_args_is_help=True, invoke_without_command=True, **kwargs)
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def print_padded(label: str, right_text: str, length: int = 30) -> None:
|
cli/utils/rest_helper.py
CHANGED
|
@@ -52,7 +52,7 @@ class RestHelper:
|
|
|
52
52
|
# token = os.environ["REMOTIVE_CLOUD_AUTH_TOKEN"]
|
|
53
53
|
# headers = {"Authorization": "Bearer " + token}
|
|
54
54
|
|
|
55
|
-
__headers: Dict[str, str] = {"User-Agent": f"remotivelabs-cli
|
|
55
|
+
__headers: Dict[str, str] = {"User-Agent": f"remotivelabs-cli/{version('remotivelabs-cli')}"}
|
|
56
56
|
__org: str = ""
|
|
57
57
|
|
|
58
58
|
__token: str = ""
|
cli/utils/time.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from datetime import date, datetime
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def parse_date(date_str: str) -> date:
|
|
5
|
+
return parse_datetime(date_str).date()
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def parse_datetime(date_str: str) -> datetime:
|
|
9
|
+
"""Required for pre 3.11"""
|
|
10
|
+
normalized = date_str.replace("Z", "+00:00")
|
|
11
|
+
return datetime.fromisoformat(normalized)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import urllib.request
|
|
7
|
+
from datetime import timedelta
|
|
8
|
+
from importlib import metadata as importlib_metadata
|
|
9
|
+
|
|
10
|
+
from packaging.version import InvalidVersion, Version
|
|
11
|
+
|
|
12
|
+
from cli.errors import ErrorPrinter
|
|
13
|
+
from cli.settings import Settings
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _pypi_latest(
|
|
17
|
+
project: str, *, include_prereleases: bool, timeout: float = 2.5, user_agent: str | None = None
|
|
18
|
+
) -> tuple[str | None, str | None]:
|
|
19
|
+
"""Return (latest_version, project_url) from PyPI, skipping yanked files."""
|
|
20
|
+
url = f"https://pypi.org/pypi/{project}/json"
|
|
21
|
+
headers = {"Accept": "application/json"}
|
|
22
|
+
if user_agent:
|
|
23
|
+
headers["User-Agent"] = user_agent
|
|
24
|
+
req = urllib.request.Request(url, headers=headers)
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|
28
|
+
data = json.load(resp)
|
|
29
|
+
except Exception:
|
|
30
|
+
return None, None # network/404/etc.
|
|
31
|
+
|
|
32
|
+
releases = data.get("releases") or {}
|
|
33
|
+
candidates: list[Version] = []
|
|
34
|
+
for s, files in releases.items():
|
|
35
|
+
try:
|
|
36
|
+
v = Version(s)
|
|
37
|
+
except InvalidVersion:
|
|
38
|
+
continue
|
|
39
|
+
the_files = files or []
|
|
40
|
+
if any(f.get("yanked", False) for f in the_files):
|
|
41
|
+
continue
|
|
42
|
+
if (v.is_prerelease or v.is_devrelease) and not include_prereleases:
|
|
43
|
+
continue
|
|
44
|
+
candidates.append(v)
|
|
45
|
+
|
|
46
|
+
if not candidates:
|
|
47
|
+
return None, None
|
|
48
|
+
|
|
49
|
+
latest = str(max(candidates))
|
|
50
|
+
info = data.get("info") or {}
|
|
51
|
+
proj_url = info.get("project_url") or info.get("package_url") or f"https://pypi.org/project/{project}/"
|
|
52
|
+
return latest, proj_url
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _installed_version(distribution_name: str, fallback: str | None = None) -> str | None:
|
|
56
|
+
try:
|
|
57
|
+
return importlib_metadata.version(distribution_name)
|
|
58
|
+
except importlib_metadata.PackageNotFoundError:
|
|
59
|
+
return fallback
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def check_for_update(project: str, current_version: str, settings: Settings) -> None:
|
|
63
|
+
# Make it possible to disable update check, i.e in CI
|
|
64
|
+
if os.environ.get("PYTHON_DISABLE_UPDATE_CHECK"):
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
# Determine current version
|
|
68
|
+
cur = current_version or _installed_version(project)
|
|
69
|
+
if not cur:
|
|
70
|
+
return # unknown version → skip silently
|
|
71
|
+
|
|
72
|
+
state = settings.read_state_file()
|
|
73
|
+
|
|
74
|
+
if not state.last_update_check_time:
|
|
75
|
+
if os.environ.get("RUNS_IN_DOCKER"):
|
|
76
|
+
# To prevent that we always check update in docker due to ephemeral disks we write an "old" check if the state
|
|
77
|
+
# is missing. If no disk is mounted we will never get the update check but if its mounted properly we will get
|
|
78
|
+
# it on the second attempt. This is good enough
|
|
79
|
+
state.last_update_check_time = (datetime.datetime.now() - timedelta(hours=10)).isoformat()
|
|
80
|
+
settings.write_state_file(state)
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
elif not state.should_perform_update_check():
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
# We end up here if last_update_check_time is None or should_perform_update_check is true
|
|
87
|
+
|
|
88
|
+
include_prereleases = Version(cur).is_prerelease or Version(cur).is_devrelease
|
|
89
|
+
|
|
90
|
+
latest, proj_url = _pypi_latest(
|
|
91
|
+
project, include_prereleases=include_prereleases, user_agent=f"{project}/{cur} (+https://pypi.org/project/{project}/)"
|
|
92
|
+
)
|
|
93
|
+
if latest:
|
|
94
|
+
if Version(latest) > Version(cur):
|
|
95
|
+
_print_update_info(
|
|
96
|
+
cur,
|
|
97
|
+
latest,
|
|
98
|
+
)
|
|
99
|
+
state.last_update_check_time = datetime.datetime.now().isoformat()
|
|
100
|
+
settings.write_state_file(state)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _print_update_info(cur: str, latest: str) -> None:
|
|
104
|
+
instructions = (
|
|
105
|
+
"upgrade with: docker pull remotivelabs/remotivelabs-cli"
|
|
106
|
+
if os.environ.get("RUNS_IN_DOCKER")
|
|
107
|
+
else "upgrade with: pipx install -U remotivelabs-cli"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
ErrorPrinter.print_hint(
|
|
111
|
+
f"Update available: remotivelabs-cli {cur} → {latest} , ({instructions}) we always recommend to use latest version"
|
|
112
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: remotivelabs-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: CLI for operating RemotiveCloud and RemotiveBroker
|
|
5
5
|
Author: Johan Rask
|
|
6
6
|
Author-email: johan.rask@remotivelabs.com
|
|
@@ -13,9 +13,11 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.13
|
|
14
14
|
Requires-Dist: click (<8.2.0)
|
|
15
15
|
Requires-Dist: dacite (>=1.9.2,<2.0.0)
|
|
16
|
+
Requires-Dist: email-validator (>=2.2.0,<3.0.0)
|
|
16
17
|
Requires-Dist: grpc-stubs (>=1.53.0.5)
|
|
17
18
|
Requires-Dist: mypy-protobuf (>=3.0.0)
|
|
18
19
|
Requires-Dist: plotext (>=5.2,<6.0)
|
|
20
|
+
Requires-Dist: pydantic (>=2.11.7,<3.0.0)
|
|
19
21
|
Requires-Dist: pyjwt (>=2.6,<3.0)
|
|
20
22
|
Requires-Dist: python-can (>=4.3.1)
|
|
21
23
|
Requires-Dist: python-socketio (>=4.6.1)
|
|
@@ -14,15 +14,15 @@ 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=
|
|
17
|
+
cli/cloud/auth/cmd.py,sha256=gLmfjIN9Vrytk9BwGH1cq4WeeryOfwTTnQ4yuZrbRcs,2757
|
|
18
18
|
cli/cloud/auth/login.py,sha256=dX6M5ysE0n9Zg3gVT7hJbChxTsmuba-Z-1Or6DCFYis,11511
|
|
19
|
-
cli/cloud/auth_tokens.py,sha256=
|
|
19
|
+
cli/cloud/auth_tokens.py,sha256=K_HSBto2XfbD-Hxhb0SAFhxSDZdSXo961UcvdVWNkZI,12831
|
|
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=iEmGMEzOIvuWomoJZ0WBa3Rmrkrup5UH7wjPMoElSn4,4092
|
|
24
24
|
cli/cloud/projects.py,sha256=ecn5Y8UKhgYnHSJQACUk1GNZt9EF8ug4B-6MCr8rZqM,1487
|
|
25
|
-
cli/cloud/recordings.py,sha256=
|
|
25
|
+
cli/cloud/recordings.py,sha256=In2fKX668CPsEVBAy7zkU92lEnmu3UcnqiVrqsvLNDQ,24961
|
|
26
26
|
cli/cloud/recordings_playback.py,sha256=XZoVyujufMQFN2v_Nwsf8tOqn61yLEpAf2z_u5uhXik,11532
|
|
27
27
|
cli/cloud/resumable_upload.py,sha256=8lEIdncJZoTZzNsQVHH3gm_GunxEmN5JbmWX7awy3p4,3713
|
|
28
28
|
cli/cloud/sample_recordings.py,sha256=RmuT-a2iMwGj3LXVcPkV5l66uFcf7nyWyJciUjnYkk4,721
|
|
@@ -37,24 +37,31 @@ 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
39
|
cli/errors.py,sha256=djODw6sdMJXzOsuAUOP3N13nfmm1sIP3Pe6tllGdozM,1657
|
|
40
|
-
cli/remotive.py,sha256=
|
|
41
|
-
cli/settings/__init__.py,sha256=
|
|
42
|
-
cli/settings/config_file.py,sha256=
|
|
43
|
-
cli/settings/core.py,sha256=
|
|
44
|
-
cli/settings/
|
|
45
|
-
cli/settings/
|
|
46
|
-
cli/settings/
|
|
40
|
+
cli/remotive.py,sha256=xfeekzG6tojXsWZdiGN5ceKCCd8xVNf8v9EUAY2Gnjc,4033
|
|
41
|
+
cli/settings/__init__.py,sha256=t1qkaGrJ4xx8WMHlmBTbQ1VdJL4YOcz8VFfRkGa2_jQ,711
|
|
42
|
+
cli/settings/config_file.py,sha256=6sdHUtZSUIgubwpfwEEn7GarTK1M_iQhtRJZzFDdP5o,2784
|
|
43
|
+
cli/settings/core.py,sha256=IJ62CzPrrvwO46zmvsjKIn6VD4oR9VG5IX29ctd2RO4,11611
|
|
44
|
+
cli/settings/migration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
|
+
cli/settings/migration/migrate_all_token_files.py,sha256=xoVvAqn_tGskEW148uf3xZx1mpJKUnERMTcBo0nkCnI,3010
|
|
46
|
+
cli/settings/migration/migrate_config_file.py,sha256=hw4EpRwJz1zUNxfCOk0PvMuZjAlaGy4m_rDbMsHZO_w,2047
|
|
47
|
+
cli/settings/migration/migrate_legacy_dirs.py,sha256=N0t2io3bT_ub8BcVPw1CeQ4eeexRUiu3jXq3DL018OE,1819
|
|
48
|
+
cli/settings/migration/migrate_token_file.py,sha256=Fp7Z_lNqSdoWY05TYwFW2QH8q9QhmB2TYSok6hV1Mic,1530
|
|
49
|
+
cli/settings/migration/migration_tools.py,sha256=P72tuw6-aS_Kd0qn-0ZecplsYxMTu0LTXM5sMSNTVEM,1378
|
|
50
|
+
cli/settings/state_file.py,sha256=ujTOJgCts-gpM-66EhRYXwO803HAFcmia2Pf_nYGldc,837
|
|
51
|
+
cli/settings/token_file.py,sha256=Po3Vwu5cdT5ZgLO3_ZLEX13_57coqHz1PPu2SQ-202o,4177
|
|
47
52
|
cli/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
53
|
cli/tools/can/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
54
|
cli/tools/can/can.py,sha256=TtP5w8vb0QG4ObNhkWIDpRMdNelirFffoc_lFZy8ePM,2260
|
|
50
55
|
cli/tools/tools.py,sha256=jhLfrFDqkmWV3eBAzNwBf6WgDGrz7sOhgVCia36Twn8,232
|
|
51
|
-
cli/topology/cmd.py,sha256=
|
|
56
|
+
cli/topology/cmd.py,sha256=SQ5wi7KDoh4iR2Ed7gyfGLNj6UE0K6UkksmBMSD2XAk,3981
|
|
52
57
|
cli/typer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
|
-
cli/typer/typer_utils.py,sha256=
|
|
58
|
+
cli/typer/typer_utils.py,sha256=TaJuK1EtE9Gv3DfmoyHPTNKmhiAimuQCHKxQjnUZ7bs,737
|
|
54
59
|
cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
|
-
cli/utils/rest_helper.py,sha256=
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
remotivelabs_cli-0.2.
|
|
59
|
-
remotivelabs_cli-0.2.
|
|
60
|
-
remotivelabs_cli-0.2.
|
|
60
|
+
cli/utils/rest_helper.py,sha256=De-1Z75p-zjA4hZrylVxWn2wqdPB2gvyvA-ixm_dRFo,14141
|
|
61
|
+
cli/utils/time.py,sha256=TEKcNZ-pQoJ7cZ6hQmVD0sTRwRm2rBy51-MuDNdO4S4,296
|
|
62
|
+
cli/utils/version_check.py,sha256=sAkTzNRlgGn4Hto_56J0KHLe-3zK7he7gbUTjrtY1lc,3940
|
|
63
|
+
remotivelabs_cli-0.2.2.dist-info/LICENSE,sha256=qDPP_yfuv1fF-u7EfexN-cN3M8aFgGVndGhGLovLKz0,608
|
|
64
|
+
remotivelabs_cli-0.2.2.dist-info/METADATA,sha256=Dyz6ht0Psz5TCHsxJXxsKIWckmwKEPVsO3amT4GkDng,1518
|
|
65
|
+
remotivelabs_cli-0.2.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
66
|
+
remotivelabs_cli-0.2.2.dist-info/entry_points.txt,sha256=lvDhPgagLqW_KTnLPCwKSqfYlEp-1uYVosRiPjsVj10,45
|
|
67
|
+
remotivelabs_cli-0.2.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|