remotivelabs-cli 0.2.3__py3-none-any.whl → 0.3.1__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.

Files changed (55) hide show
  1. cli/broker/__init__.py +36 -0
  2. cli/broker/discovery.py +43 -0
  3. cli/broker/export.py +6 -36
  4. cli/broker/files.py +12 -12
  5. cli/broker/lib/broker.py +132 -106
  6. cli/broker/lib/client.py +224 -0
  7. cli/broker/lib/helper.py +277 -0
  8. cli/broker/lib/signalcreator.py +196 -0
  9. cli/broker/license_flows.py +11 -13
  10. cli/broker/playback.py +10 -10
  11. cli/broker/record.py +4 -4
  12. cli/broker/scripting.py +6 -9
  13. cli/broker/signals.py +17 -19
  14. cli/cloud/__init__.py +17 -0
  15. cli/cloud/auth/cmd.py +74 -33
  16. cli/cloud/auth/login.py +42 -54
  17. cli/cloud/auth_tokens.py +40 -247
  18. cli/cloud/brokers.py +5 -9
  19. cli/cloud/configs.py +4 -17
  20. cli/cloud/licenses/__init__.py +0 -0
  21. cli/cloud/licenses/cmd.py +14 -0
  22. cli/cloud/organisations.py +12 -17
  23. cli/cloud/projects.py +3 -3
  24. cli/cloud/recordings.py +35 -61
  25. cli/cloud/recordings_playback.py +22 -22
  26. cli/cloud/resumable_upload.py +6 -6
  27. cli/cloud/service_account_tokens.py +4 -3
  28. cli/cloud/storage/cmd.py +2 -3
  29. cli/cloud/storage/copy.py +2 -1
  30. cli/connect/connect.py +4 -4
  31. cli/connect/protopie/protopie.py +22 -30
  32. cli/remotive.py +16 -26
  33. cli/settings/__init__.py +1 -2
  34. cli/settings/config_file.py +2 -0
  35. cli/settings/core.py +146 -146
  36. cli/settings/migration/migrate_config_file.py +13 -6
  37. cli/settings/migration/migration_tools.py +6 -4
  38. cli/settings/state_file.py +12 -4
  39. cli/tools/can/can.py +4 -7
  40. cli/topology/__init__.py +3 -0
  41. cli/topology/cmd.py +60 -83
  42. cli/topology/start_trial.py +105 -0
  43. cli/typer/typer_utils.py +3 -6
  44. cli/utils/console.py +61 -0
  45. cli/utils/rest_helper.py +33 -31
  46. cli/utils/versions.py +7 -19
  47. {remotivelabs_cli-0.2.3.dist-info → remotivelabs_cli-0.3.1.dist-info}/METADATA +3 -2
  48. remotivelabs_cli-0.3.1.dist-info/RECORD +74 -0
  49. cli/broker/brokers.py +0 -93
  50. cli/cloud/cloud_cli.py +0 -29
  51. cli/errors.py +0 -44
  52. remotivelabs_cli-0.2.3.dist-info/RECORD +0 -67
  53. {remotivelabs_cli-0.2.3.dist-info → remotivelabs_cli-0.3.1.dist-info}/LICENSE +0 -0
  54. {remotivelabs_cli-0.2.3.dist-info → remotivelabs_cli-0.3.1.dist-info}/WHEEL +0 -0
  55. {remotivelabs_cli-0.2.3.dist-info → remotivelabs_cli-0.3.1.dist-info}/entry_points.txt +0 -0
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
- from cli.settings import TokenNotFoundError, settings
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
- print(res.text)
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
- ErrorPrinter.print_generic_error("You did not grant access to RemotiveCloud, login aborted")
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
- ErrorPrinter.print_generic_error(
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
- ErrorPrinter.print_generic_error(f"Unexpected error {error_value}, please contact support@remotivelabs.com")
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,18 +161,17 @@ def create_personal_token() -> None:
170
161
 
171
162
  settings.add_personal_token(response.text, activate=True)
172
163
 
173
- print("Successfully logged on")
164
+ print_success("Logged in")
174
165
 
175
166
 
176
167
  def _do_prompt_to_use_existing_credentials() -> Optional[TokenFile]:
177
- files = settings.list_personal_token_files()
178
- if len(files) > 0:
168
+ token_files = settings.list_personal_token_files()
169
+ if len(token_files) > 0:
179
170
  should_select_token = typer.confirm(
180
171
  "You have credentials available already, would you like to choose one of these instead?", default=True
181
172
  )
182
173
  if should_select_token:
183
174
  token = do_activate(token_name=None)
184
- # token = list_and_select_personal_token(skip_prompt=False, include_service_accounts=True)
185
175
  if token is not None:
186
176
  return token
187
177
  # TODO - fix so this is not needed
@@ -189,7 +179,7 @@ def _do_prompt_to_use_existing_credentials() -> Optional[TokenFile]:
189
179
  return None
190
180
 
191
181
 
192
- def login(headless: bool = False) -> bool: # noqa: C901, PLR0912, PLR0915
182
+ def login(headless: bool = False) -> bool: # noqa: C901, PLR0915
193
183
  """
194
184
  Initiate login
195
185
  """
@@ -197,31 +187,30 @@ def login(headless: bool = False) -> bool: # noqa: C901, PLR0912, PLR0915
197
187
  #
198
188
  # Check login.md flowchart for better understanding
199
189
  #
200
- # 1. Check for active token valid and working credentials
201
- #
202
- try:
203
- activate_token = settings.get_active_token_file()
204
-
205
- if not activate_token.is_expired():
206
- if Rest.has_access("/api/whoami"):
207
- token = _do_prompt_to_use_existing_credentials()
208
- if token is not None:
209
- return True
210
- else:
211
- settings.clear_active_token()
212
- raise TokenNotFoundError()
213
- else:
214
- # TODO - Cleanup token since expired
215
- pass
216
-
217
- except TokenNotFoundError:
218
- #
219
- # 2. If no token was found, let user choose an existing if exists
220
- #
221
- token = _do_prompt_to_use_existing_credentials()
222
- if token is not None:
190
+
191
+ active_token_file = settings.get_active_token_file()
192
+ if active_token_file:
193
+ # check if the active token is valid, if not, prompt user to select a new token
194
+ if not Rest.has_access("/api/whoami") or active_token_file.is_expired():
195
+ settings.clear_active_account()
196
+ newly_activated_token_file = _do_prompt_to_use_existing_credentials()
197
+ if newly_activated_token_file:
198
+ return True
199
+
200
+ # intentional fall through to login since we have no token
201
+
202
+ else:
203
+ # no active token, prompt user to select a new token
204
+ newly_activated_token_file = _do_prompt_to_use_existing_credentials()
205
+ if newly_activated_token_file:
223
206
  return True
224
207
 
208
+ # intentional fall through to login since we have no token
209
+
210
+ #
211
+ # 2. Login in if no valid token found...
212
+ #
213
+
225
214
  prepare_local_webserver()
226
215
 
227
216
  def force_use_webserver_callback() -> bool:
@@ -234,8 +223,8 @@ def login(headless: bool = False) -> bool: # noqa: C901, PLR0912, PLR0915
234
223
  """
235
224
  This will print a url the will trigger a callback later so the webserver must be up and running.
236
225
  """
237
- print("Copy the following link in a browser to login to cloud, and complete the sign-in prompts:")
238
- print("")
226
+ print_unformatted("Copy the following link in a browser to login to cloud, and complete the sign-in prompts:")
227
+ print_newline()
239
228
 
240
229
  url = (
241
230
  f"{Rest.get_base_frontend_url()}/login"
@@ -245,15 +234,15 @@ def login(headless: bool = False) -> bool: # noqa: C901, PLR0912, PLR0915
245
234
  f"&code_challenge={code_challenge}"
246
235
  f"&redirect_uri=http://localhost:{httpd.server_address[1]}"
247
236
  )
248
- console.print(url, style="bold", soft_wrap=True)
237
+ print_url(url)
249
238
  httpd.serve_forever()
250
239
 
251
240
  def login_headless() -> None:
252
241
  """
253
242
  Full headless, opens a browser and expects a auth code to be entered and exchanged for the token
254
243
  """
255
- print("Copy the following link in a browser to login to cloud, and complete the sign-in prompts:")
256
- print("")
244
+ print_unformatted("Copy the following link in a browser to login to cloud, and complete the sign-in prompts:")
245
+ print_newline()
257
246
 
258
247
  url = (
259
248
  f"{Rest.get_base_frontend_url()}/login"
@@ -262,7 +251,7 @@ def login(headless: bool = False) -> bool: # noqa: C901, PLR0912, PLR0915
262
251
  f"&response_type=code"
263
252
  f"&code_challenge={code_challenge}"
264
253
  )
265
- console.print(url, style="bold", soft_wrap=True)
254
+ print_url(url)
266
255
 
267
256
  code = typer.prompt(
268
257
  "Once finished, enter the verification code provided in your browser",
@@ -275,12 +264,11 @@ def login(headless: bool = False) -> bool: # noqa: C901, PLR0912, PLR0915
275
264
  allow_status_codes=[401],
276
265
  )
277
266
  if res.status_code == 401:
278
- ErrorPrinter.print_generic_error(
267
+ print_generic_error(
279
268
  "Failed to fetch token. Please try again, if the problem persists please reach out to support@remotivelabs.com"
280
269
  )
281
270
  sys.exit(1)
282
271
  access_token = res.json()["access_token"]
283
- # res = Rest.handle_get("/api/whoami", return_response=True, access_token=access_token)
284
272
  global short_lived_token # noqa: PLW0603
285
273
  short_lived_token = access_token
286
274
  create_personal_token()
@@ -301,10 +289,10 @@ def login(headless: bool = False) -> bool: # noqa: C901, PLR0912, PLR0915
301
289
  )
302
290
 
303
291
  if not could_open:
304
- print(
292
+ print_generic_error(
305
293
  "Could not open a browser on this machine, this is likely because you are in an environment where no browser is available"
306
294
  )
307
- print("")
295
+ print_newline()
308
296
  if force_use_webserver_callback():
309
297
  login_with_callback_but_copy_url()
310
298
  create_personal_token()
cli/cloud/auth_tokens.py CHANGED
@@ -1,33 +1,23 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import List, Literal, Optional
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
- from cli.api.cloud import tokens
10
8
  from cli.cloud.organisations import do_select_default_org
11
- from cli.errors import ErrorPrinter
12
- from cli.settings import TokenNotFoundError, settings
9
+ from cli.settings import settings
13
10
  from cli.settings.token_file import TokenFile
14
- from cli.typer import typer_utils
11
+ from cli.utils.console import print_generic_error, print_generic_message, print_hint, print_newline, print_success, print_unformatted
15
12
  from cli.utils.rest_helper import RestHelper as Rest
16
13
 
17
- console = Console(stderr=False)
18
- err_console = Console(stderr=True)
19
14
 
20
- app = typer_utils.create_typer()
21
-
22
- PromptType = Literal["activate", "login"]
23
-
24
-
25
- def _prompt_choice( # noqa: C901, PLR0912
15
+ def _prompt_choice( # noqa: C901
26
16
  choices: List[TokenFile],
27
17
  skip_prompt: bool = False,
28
18
  info_message: Optional[str] = None,
29
19
  ) -> Optional[TokenFile]:
30
- accounts = settings.get_cli_config().accounts
20
+ accounts = settings.list_accounts()
31
21
 
32
22
  table = Table("#", "Active", "Type", "Token", "Account", "Created", "Expires")
33
23
 
@@ -37,11 +27,10 @@ def _prompt_choice( # noqa: C901, PLR0912
37
27
  for token in choices:
38
28
  account = accounts.get(token.account.email)
39
29
  if account and account.credentials_file:
40
- try:
41
- token_file = settings.get_token_file(account.credentials_file)
42
- if token_file.name in (token.name or ""):
43
- included_tokens.append(token)
44
- except TokenNotFoundError:
30
+ token_file = settings.get_token_file(account.credentials_file)
31
+ if token_file and token_file.name in (token.name or ""):
32
+ included_tokens.append(token)
33
+ else:
45
34
  excluded_tokens.append(token)
46
35
  else:
47
36
  excluded_tokens.append(token)
@@ -51,13 +40,7 @@ def _prompt_choice( # noqa: C901, PLR0912
51
40
 
52
41
  included_tokens.sort(key=lambda token: token.created, reverse=True)
53
42
 
54
- def get_active_token_or_none() -> Optional[TokenFile]:
55
- try:
56
- return settings.get_active_token_file()
57
- except TokenNotFoundError:
58
- return None
59
-
60
- active_token = get_active_token_or_none()
43
+ active_token = settings.get_active_token_file()
61
44
  active_token_index = None
62
45
  for idx, choice in enumerate(included_tokens, start=1):
63
46
  is_active = active_token is not None and active_token.name == choice.name
@@ -72,14 +55,14 @@ def _prompt_choice( # noqa: C901, PLR0912
72
55
  str(choice.created),
73
56
  str(choice.expires),
74
57
  )
75
- console.print(table)
58
+ print_unformatted(table)
76
59
 
77
60
  if skip_prompt:
78
61
  return None
79
62
 
80
- typer.echo("")
81
- if info_message is not None:
82
- console.print(info_message)
63
+ print_newline()
64
+ if info_message:
65
+ print_generic_message(info_message)
83
66
 
84
67
  selection = typer.prompt(
85
68
  f"Enter the number(# 1-{len(included_tokens)}) of the account to select (q to quit)",
@@ -98,62 +81,8 @@ def _prompt_choice( # noqa: C901, PLR0912
98
81
  return _prompt_choice(included_tokens, skip_prompt, info_message)
99
82
 
100
83
 
101
- # @app.command(name="create")
102
- def create(
103
- activate: bool = typer.Option(False, help="Activate the token for use after download"),
104
- ) -> None:
105
- """
106
- Create a new personal access token in [bold]cloud[/bold] and download locally
107
- """
108
- response = tokens.create()
109
- pat = settings.add_personal_token(response.text())
110
- print(f"Personal access token added: {pat.name}")
111
-
112
- if not activate:
113
- print(f"Use 'remotive cloud auth tokens activate {pat.name}' to use this access token from cli")
114
- else:
115
- settings.activate_token(pat)
116
- print("Token file activated and ready for use")
117
- print("\033[93m This file contains secrets and must be kept safe")
118
-
119
-
120
- # @app.command(name="list", help="List personal credentials in [bold]cloud[/bold]")
121
- def list_tokens() -> None:
122
- Rest.handle_get("/api/me/keys")
123
-
124
-
125
- # @app.command(name="revoke")
126
- def revoke(
127
- name: str = typer.Argument(help="Access token name"),
128
- delete: bool = typer.Option(True, help="Also delete token"),
129
- ) -> None:
130
- """
131
- Revoke personal credentials in cloud and removes the file from filesystem
132
-
133
- If cloud token is not found but token is found on file system it will delete it and
134
- vice versa.
135
- """
136
- _revoke_and_delete_personal_token(name, delete)
137
-
138
-
139
- # @app.command(name="activate")
140
- def activate(
141
- token_name: str = typer.Argument(..., help="Token path, filename or name to activate"),
142
- ) -> None:
143
- """
144
- Activate a credential file to be used for authentication using filename, path or name.
145
-
146
- This will be used as the current access token in all requests.
147
- """
148
- try:
149
- token_file = settings.get_token_file(token_name)
150
- settings.activate_token(token_file)
151
- except TokenNotFoundError:
152
- err_console.print(f":boom: [bold red] Error: [/bold red] Token with filename or name {token_name} could not be found")
153
-
154
-
155
84
  def prompt_to_set_org() -> None:
156
- active_account = settings.get_cli_config().get_active_account()
85
+ active_account = settings.get_active_account()
157
86
  if active_account and not active_account.default_organization:
158
87
  set_default_organisation = typer.confirm(
159
88
  "You have not set a default organization\nWould you like to choose one now?",
@@ -164,45 +93,32 @@ def prompt_to_set_org() -> None:
164
93
  do_select_default_org(get=False)
165
94
 
166
95
 
167
- @app.command("activate")
168
- def select_personal_token(
169
- token_name: str = typer.Argument(None, help="Name, filename or path to a credentials file"),
170
- ) -> None:
171
- """
172
- Activates is setting the current active credentials to use by the CLI, this can be done by specifying a name
173
- of the token or getting prompted and choosing from existing.
174
- """
175
- do_activate(token_name)
176
-
177
-
178
96
  def do_activate(token_name: Optional[str]) -> Optional[TokenFile]:
179
- if token_name is not None:
180
- try:
181
- token_file = settings.get_token_file(token_name)
182
- settings.activate_token(token_file)
183
- return token_file
184
- except TokenNotFoundError:
185
- err_console.print(f":boom: [bold red] Error: [/bold red] Token with filename or name {token_name} could not be found")
97
+ if token_name:
98
+ token_file = settings.get_token_file(token_name)
99
+ if not token_file:
100
+ print_generic_error(f"Token with filename or name {token_name} could not be found")
186
101
  return None
187
- else:
188
- token_files = settings.list_personal_tokens()
189
- token_files.extend(settings.list_service_account_tokens())
190
- if len(token_files) > 0:
191
- token_selected = list_and_select_personal_token(include_service_accounts=True)
192
- if token_selected is not None:
193
- is_logged_in = Rest.has_access("/api/whoami")
194
- if not is_logged_in:
195
- ErrorPrinter.print_generic_error("Could not access RemotiveCloud with selected token")
196
- else:
197
- console.print("[green]Success![/green] Access to RemotiveCloud granted")
198
- # Only select default if activate was done with selection and successful
199
- # and not SA since SA cannot list available organizations
200
- if token_selected.type == "authorized_user":
201
- prompt_to_set_org()
202
- return token_selected
102
+ return settings.activate_token(token_file)
103
+
104
+ token_files = settings.list_personal_token_files()
105
+ token_files.extend(settings.list_service_account_token_files())
106
+ if len(token_files) > 0:
107
+ token_selected = list_and_select_personal_token(include_service_accounts=True)
108
+ if token_selected is not None:
109
+ is_logged_in = Rest.has_access("/api/whoami")
110
+ if not is_logged_in:
111
+ print_generic_error("Could not access RemotiveCloud with selected token")
112
+ else:
113
+ print_success("Access to RemotiveCloud granted")
114
+ # Only select default if activate was done with selection and successful
115
+ # and not SA since SA cannot list available organizations
116
+ if token_selected.type == "authorized_user":
117
+ prompt_to_set_org()
118
+ return token_selected
203
119
 
204
- ErrorPrinter.print_hint("No credentials available, login to activate credentials")
205
- return None
120
+ print_hint("No credentials available, login to activate credentials")
121
+ return None
206
122
 
207
123
 
208
124
  def list_and_select_personal_token(
@@ -210,10 +126,10 @@ def list_and_select_personal_token(
210
126
  include_service_accounts: bool = False,
211
127
  info_message: Optional[str] = None,
212
128
  ) -> Optional[TokenFile]:
213
- personal_tokens = settings.list_personal_tokens()
129
+ personal_tokens = settings.list_personal_token_files()
214
130
 
215
131
  if include_service_accounts:
216
- sa_tokens = settings.list_service_account_tokens()
132
+ sa_tokens = settings.list_service_account_token_files()
217
133
  personal_tokens.extend(sa_tokens)
218
134
 
219
135
  selected_token = _prompt_choice(personal_tokens, skip_prompt=skip_prompt, info_message=info_message)
@@ -221,126 +137,3 @@ def list_and_select_personal_token(
221
137
  settings.activate_token(selected_token)
222
138
 
223
139
  return selected_token
224
-
225
-
226
- # @app.command("select-revoke")
227
- def select_revoke_personal_token() -> None:
228
- """
229
- Prompts a user to select one of the credential files to revoke and delete
230
- """
231
- personal_tokens = settings.list_personal_tokens()
232
- sa_tokens = settings.list_service_account_tokens()
233
- personal_tokens.extend(sa_tokens)
234
-
235
- is_logged_in = Rest.has_access("/api/whoami")
236
- if not is_logged_in:
237
- ErrorPrinter.print_hint("You must be logged in")
238
- raise typer.Exit(0)
239
-
240
- # merged = _merge_local_tokens_with_cloud(personal_tokens)
241
-
242
- selected_token = _prompt_choice(personal_tokens)
243
-
244
- if selected_token is not None:
245
- _revoke_and_delete_personal_token(selected_token.name, True)
246
- # Rest.handle_patch(f"/api/me/keys/{selected_token.name}/revoke", quiet=True, access_token=selected_token.token)
247
- # Rest.handle_delete(f"/api/me/keys/{selected_token.name}", quiet=True, access_token=selected_token.token)
248
- # settings.remove_token_file(selected_token.name)
249
- # active_token = settings.get_active_token_file()
250
- # if active_token.name == selected_token.name:
251
- # settings.clear_active_token()
252
- # select_revoke_personal_token()
253
-
254
-
255
- # @app.command("test-all")
256
- def test_all_personal_tokens() -> None:
257
- """
258
- Tests each credential file to see if it is valid
259
- """
260
- personal_tokens = settings.list_personal_tokens()
261
- personal_tokens.extend(settings.list_service_account_tokens())
262
- if len(personal_tokens) == 0:
263
- console.print("No personal tokens found on disk")
264
- return
265
-
266
- for token in personal_tokens:
267
- r = Rest.handle_get(
268
- "/api/whoami",
269
- allow_status_codes=[401],
270
- access_token=token.token,
271
- use_progress_indicator=True,
272
- return_response=True,
273
- )
274
- if r.status_code == 200:
275
- if token.account is not None:
276
- console.print(f"{token.account.email} ({token.name}) :white_check_mark:")
277
- else:
278
- console.print(f"{token.name} :white_check_mark:")
279
- elif token.account is not None:
280
- console.print(f"{token.account.email} ({token.name}) :x: Failed")
281
- else:
282
- console.print(f"{token.name} :x: Failed")
283
-
284
-
285
- # @app.command(name="list-service-account-tokens-files")
286
- def list_sats_files() -> None:
287
- """
288
- List service account access token files in remotivelabs config directory
289
- """
290
- service_account_files = settings.list_service_account_token_files()
291
- for file in service_account_files:
292
- print(file)
293
-
294
-
295
- @app.command(name="list")
296
- def list_pats_files(
297
- accounts: bool = typer.Option(True, help="Lists all available accounts"),
298
- files: bool = typer.Option(False, help="Shows all token files in config directory"),
299
- ) -> None:
300
- """
301
- Lists available credential files on filesystem
302
- """
303
-
304
- if accounts:
305
- list_and_select_personal_token(skip_prompt=True, include_service_accounts=True, info_message="hello")
306
-
307
- if files:
308
- personal_files = settings.list_personal_token_files()
309
- service_account_files = settings.list_service_account_token_files()
310
- personal_files.extend(service_account_files)
311
- for file in personal_files:
312
- print(file)
313
-
314
-
315
- def _revoke_and_delete_personal_token(name: str, delete: bool) -> None:
316
- token_file = None
317
-
318
- # First we try to find the file and make sure its not the currently active
319
- try:
320
- token_file = settings.get_token_file(name)
321
- active_token = settings.get_active_token_file()
322
- if token_file.name == active_token.name:
323
- ErrorPrinter.print_hint("You cannot revoke the current active token")
324
- return
325
- except TokenNotFoundError:
326
- pass
327
-
328
- # The lets try to revoke from cloud
329
- res_revoke = tokens.revoke(name)
330
- if delete:
331
- res_delete = tokens.delete(name)
332
- if res_delete.is_success:
333
- ErrorPrinter.print_generic_message("Token successfully revoked and deleted")
334
- else:
335
- ErrorPrinter.print_hint(f"Failed to revoke and delete token in cloud: {res_delete.status_code}")
336
- elif res_revoke.is_success:
337
- ErrorPrinter.print_generic_message("Token successfully revoked")
338
- else:
339
- ErrorPrinter.print_hint("Failed to revoke and delete token in cloud")
340
-
341
- # Finally try to remove the file if exists
342
- if token_file is not None:
343
- settings.remove_token_file(token_file.name)
344
- console.print("Successfully deleted token on filesystem")
345
- else:
346
- ErrorPrinter.print_hint("Token not found on filesystem")
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(message)
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("EXAMPLE error encountered: ", err)
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
- print(f"{signal_db_file} downloaded")
105
+ print_success(f"{signal_db_file} downloaded")
106
106
  else:
107
- sys.stderr.write(f"Got unexpected status {download_resp.status_code}\n")
107
+ print_generic_error(f"Got unexpected status {download_resp.status_code}")
108
108
  else:
109
- sys.stderr.write(f"Got unexpected status {get_signed_url_resp.status_code}\n")
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")
File without changes