remotivelabs-cli 0.0.26__tar.gz → 0.0.28__tar.gz

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.
Files changed (44) hide show
  1. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/PKG-INFO +1 -1
  2. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/broker/lib/broker.py +24 -3
  3. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/broker/signals.py +9 -1
  4. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/cloud/auth_tokens.py +38 -4
  5. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/cloud/cloud_cli.py +2 -16
  6. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/cloud/configs.py +9 -0
  7. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/cloud/filestorage.py +20 -5
  8. remotivelabs_cli-0.0.28/cli/cloud/organisations.py +30 -0
  9. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/cloud/projects.py +2 -2
  10. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/cloud/recordings.py +22 -10
  11. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/cloud/resumable_upload.py +24 -1
  12. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/pyproject.toml +1 -1
  13. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/LICENSE +0 -0
  14. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/README.md +0 -0
  15. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/__about__.py +0 -0
  16. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/__init__.py +0 -0
  17. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/broker/brokers.py +0 -0
  18. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/broker/export.py +0 -0
  19. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/broker/files.py +0 -0
  20. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/broker/lib/__about__.py +0 -0
  21. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/broker/license_flows.py +0 -0
  22. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/broker/licenses.py +0 -0
  23. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/broker/playback.py +0 -0
  24. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/broker/record.py +0 -0
  25. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/broker/scripting.py +0 -0
  26. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/cloud/__init__.py +0 -0
  27. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/cloud/auth.py +0 -0
  28. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/cloud/brokers.py +0 -0
  29. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/cloud/recordings_playback.py +0 -0
  30. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/cloud/rest_helper.py +0 -0
  31. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/cloud/sample_recordings.py +0 -0
  32. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/cloud/service_account_tokens.py +0 -0
  33. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/cloud/service_accounts.py +0 -0
  34. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/connect/__init__.py +0 -0
  35. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/connect/connect.py +0 -0
  36. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/connect/protopie/protopie.py +0 -0
  37. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/errors.py +0 -0
  38. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/remotive.py +0 -0
  39. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/requirements.txt +0 -0
  40. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/settings.py +0 -0
  41. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/tools/__init__.py +0 -0
  42. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/tools/can/__init__.py +0 -0
  43. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/tools/can/can.py +0 -0
  44. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.28}/cli/tools/tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: remotivelabs-cli
3
- Version: 0.0.26
3
+ Version: 0.0.28
4
4
  Summary: CLI for operating RemotiveCloud and RemotiveBroker
5
5
  Author: Johan Rask
6
6
  Author-email: johan.rask@remotivelabs.com
@@ -360,7 +360,7 @@ class Broker:
360
360
  namespaces.append(network_info.namespace.name)
361
361
  return namespaces
362
362
 
363
- def list_signal_names(self) -> List[Dict[str, str]]:
363
+ def list_signal_names(self) -> List[Dict[str, Any]]:
364
364
  # Lists available signals
365
365
  configuration = self.system_stub.GetConfiguration(br.common_pb2.Empty())
366
366
 
@@ -369,9 +369,30 @@ class Broker:
369
369
  res = self.system_stub.ListSignals(network_info.namespace)
370
370
  for finfo in res.frame:
371
371
  # f: br.common_pb2.FrameInfo = finfo
372
- signal_names.append({"signal": finfo.signalInfo.id.name, "namespace": network_info.namespace.name})
372
+ receivers = []
373
373
  for sinfo in finfo.childInfo:
374
- signal_names.append({"signal": sinfo.id.name, "namespace": network_info.namespace.name})
374
+ rec = list(map(lambda r: r, sinfo.metaData.receiver))
375
+ receivers.extend(rec)
376
+ signal_names.append(
377
+ {
378
+ "signal": sinfo.id.name,
379
+ "namespace": network_info.namespace.name,
380
+ "receivers": rec,
381
+ "min": sinfo.metaData.min,
382
+ "max": sinfo.metaData.max,
383
+ }
384
+ )
385
+
386
+ signal_names.append(
387
+ {
388
+ "signal": finfo.signalInfo.id.name,
389
+ "namespace": network_info.namespace.name,
390
+ "senders": list(map(lambda s: s, finfo.signalInfo.metaData.sender)),
391
+ "receivers": list(set(receivers)),
392
+ "cycletime": finfo.signalInfo.metaData.cycleTime,
393
+ }
394
+ )
395
+
375
396
  return signal_names
376
397
 
377
398
  def subscribe_on_script(
@@ -30,7 +30,15 @@ class Signals(TypedDict):
30
30
  signal_values: Dict[Any, Any] = {}
31
31
 
32
32
 
33
- @app.command(help="List signals names on broker")
33
+ @app.command(name="list", help="List frame and signal metadata on broker")
34
+ def list_signals(
35
+ url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
36
+ api_key: str = typer.Option(None, help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
37
+ ) -> None:
38
+ signal_names(url, api_key)
39
+
40
+
41
+ @app.command(help="List signals names on broker", deprecated=True)
34
42
  def signal_names(
35
43
  url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
36
44
  api_key: str = typer.Option(None, help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
@@ -1,6 +1,7 @@
1
1
  import json
2
2
  import os
3
3
  import sys
4
+ from json.decoder import JSONDecodeError
4
5
  from pathlib import Path
5
6
 
6
7
  import typer
@@ -42,14 +43,38 @@ def list_personal_access_tokens() -> None:
42
43
  Rest.handle_get("/api/me/keys")
43
44
 
44
45
 
45
- @app.command(name="revoke", help="Revoke the specified access token")
46
- def revoke(name: str = typer.Option(..., help="Name of the access token to revoke")) -> None:
46
+ @app.command(name="revoke")
47
+ def revoke(name_or_file: str = typer.Argument(help="Name or file path of the access token to revoke")) -> None:
48
+ """
49
+ Revoke an access token by token name or path to a file containing that token
50
+
51
+ Name is found in the json file
52
+ ```
53
+ {
54
+ "expires": "2034-07-31",
55
+ "token": "xxx",
56
+ "created": "2024-07-31T09:18:50.406+02:00",
57
+ "name": "token_name"
58
+ }
59
+ ```
60
+ """
61
+ name = name_or_file
62
+ if "." in name_or_file:
63
+ json_str = read_file(name_or_file)
64
+ try:
65
+ name = json.loads(json_str)["name"]
66
+ except JSONDecodeError:
67
+ sys.stderr.write("Failed to parse json, make sure its a correct access token file\n")
68
+ sys.exit(1)
69
+ except KeyError:
70
+ sys.stderr.write("Json does not contain a name property, make sure its a correct access token file\n")
71
+ sys.exit(1)
47
72
  Rest.ensure_auth_token()
48
73
  Rest.handle_delete(f"/api/me/keys/{name}")
49
74
 
50
75
 
51
76
  @app.command()
52
- def describe(file: str = typer.Option(..., help="File name")) -> None:
77
+ def describe(file: str = typer.Argument(help="File name")) -> None:
53
78
  """
54
79
  Show contents of specified access token file
55
80
  """
@@ -92,7 +117,16 @@ def list_files() -> None:
92
117
 
93
118
 
94
119
  def read_file(file: str) -> str:
95
- with open(str(Path.home()) + f"/.config/.remotive/{file}", "r", encoding="utf8") as f:
120
+ """
121
+ Reads a file using file path or if that does not exist check under ~/.config/.remotive
122
+ """
123
+ path = file
124
+ if not Path(file).exists():
125
+ path = str(Path.home()) + f"/.config/.remotive/{file}"
126
+ if not Path(path).exists():
127
+ sys.stderr.write(f"Failed to find file using {file} or {path}\n")
128
+ sys.exit(1)
129
+ with open(path, "r", encoding="utf8") as f:
96
130
  token = f.read()
97
131
  f.close()
98
132
  return token
@@ -1,10 +1,8 @@
1
- import json
2
-
3
1
  import typer
4
2
 
5
3
  from cli.cloud.rest_helper import RestHelper
6
4
 
7
- from . import auth, brokers, configs, filestorage, projects, recordings, sample_recordings, service_accounts
5
+ from . import auth, brokers, configs, filestorage, organisations, projects, recordings, sample_recordings, service_accounts
8
6
 
9
7
  app = typer.Typer()
10
8
 
@@ -17,19 +15,7 @@ def licenses(
17
15
  RestHelper.handle_get(f"/api/bu/{organisation}/licenses", {"filter": filter_option})
18
16
 
19
17
 
20
- @app.command(help="List your available organisations")
21
- def organisations() -> None:
22
- r = RestHelper.handle_get("/api/home", return_response=True)
23
- if r is None:
24
- return
25
- if r.status_code == 200:
26
- j = list(map(lambda x: x["billableUnitUser"]["billableUnit"]["uid"], r.json()))
27
- print(json.dumps(j))
28
- else:
29
- print(f"Got status code: {r.status_code}")
30
- print(r.text)
31
-
32
-
18
+ app.add_typer(organisations.app, name="organisations", help="Manage organisations")
33
19
  app.add_typer(projects.app, name="projects", help="Manage projects")
34
20
  app.add_typer(auth.app, name="auth")
35
21
  app.add_typer(brokers.app, name="brokers", help="Manage cloud broker lifecycle")
@@ -14,6 +14,9 @@ app = typer.Typer()
14
14
 
15
15
  @app.command("list")
16
16
  def list_signal_databases(project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT")) -> None:
17
+ """
18
+ List available signal databases in project
19
+ """
17
20
  Rest.handle_get(f"/api/project/{project}/files/config")
18
21
 
19
22
 
@@ -22,6 +25,9 @@ def delete(
22
25
  signal_db_file: str = typer.Argument("", help="Signal database file"),
23
26
  project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
24
27
  ) -> None:
28
+ """
29
+ Deletes the specified signal database
30
+ """
25
31
  Rest.handle_delete(f"/api/project/{project}/files/config/{signal_db_file}")
26
32
 
27
33
 
@@ -54,6 +60,9 @@ def download(
54
60
  signal_db_file: str = typer.Argument("", help="Signal database file"),
55
61
  project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
56
62
  ) -> None:
63
+ """
64
+ Downloads the specified signal database to disk
65
+ """
57
66
  with Progress(
58
67
  SpinnerColumn(),
59
68
  TextColumn("[progress.description]{task.description}"),
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import os.path
2
4
  import sys
3
5
  from pathlib import Path
@@ -96,11 +98,24 @@ def copy_file( # noqa: C901 # type: ignore[too-many-branches] # pylint: disabl
96
98
  if source.startswith("rcs://"):
97
99
  __copy_to_local(source=source, dest=dest, project=project)
98
100
  else:
99
- __copy_to_remote(source=source, dest=dest, project=project)
101
+ path = Path(source)
102
+ if path.is_dir():
103
+ print("is dir")
104
+ for file_path in path.rglob("*"):
105
+ if file_path.is_file():
106
+ print(file_path)
107
+ __copy_to_remote(source=source, dest=dest, project=project)
108
+ sys.exit(1)
109
+ else:
110
+ __copy_to_remote(source=source, dest=dest, project=project)
100
111
 
101
112
 
102
113
  def __copy_to_remote(source: str, dest: str, project: str) -> None:
103
114
  path = Path(source)
115
+ if path.is_dir():
116
+ print("is dir")
117
+ sys.exit(1)
118
+
104
119
  if not path.exists():
105
120
  ErrorPrinter.print_hint("Source file does not exist")
106
121
  sys.exit(1)
@@ -111,11 +126,11 @@ def __copy_to_remote(source: str, dest: str, project: str) -> None:
111
126
  res = Rest.handle_post(f"/api/project/{project}/files/storage{rcs_path}", return_response=True)
112
127
  if res is None:
113
128
  return
114
- json = res.json()
115
- url = json["url"]
116
- content_type = json["contentType"]
129
+ json_res = res.json()
130
+ url = json_res["url"]
131
+ headers = json_res["headers"]
117
132
  try:
118
- upload.upload_signed_url(url, source, content_type)
133
+ upload.upload_signed_url(url, source, headers)
119
134
  except IsADirectoryError:
120
135
  ErrorPrinter.print_hint(f"Supplied source file '{source}' is a directory but must be a file")
121
136
 
@@ -0,0 +1,30 @@
1
+ import json
2
+ import sys
3
+
4
+ import typer
5
+
6
+ from cli.cloud.rest_helper import RestHelper
7
+
8
+ app = typer.Typer()
9
+
10
+
11
+ @app.command(name="list", help="List your available organisations")
12
+ def list_orgs() -> None:
13
+ r = RestHelper.handle_get("/api/home", return_response=True)
14
+ if r is None:
15
+ return
16
+ if r.status_code == 200:
17
+ j = list(
18
+ map(
19
+ lambda x: {
20
+ "uid": x["billableUnitUser"]["billableUnit"]["uid"],
21
+ "displayName": x["billableUnitUser"]["billableUnit"]["displayName"],
22
+ },
23
+ r.json(),
24
+ )
25
+ )
26
+ print(json.dumps(j))
27
+ else:
28
+ print(f"Got status code: {r.status_code}")
29
+ print(r.text)
30
+ sys.exit(1)
@@ -25,8 +25,8 @@ def list_projects(organisation: str = typer.Option(..., help="Organisation ID",
25
25
 
26
26
  @app.command(name="create")
27
27
  def create_project(
28
+ project_uid: str = typer.Argument(help="Project UID"),
28
29
  organisation: str = typer.Option(..., help="Organisation ID", envvar="REMOTIVE_CLOUD_ORGANISATION"),
29
- project_uid: str = typer.Option(..., help="Project UID"),
30
30
  project_display_name: str = typer.Option(default="", help="Project display name"),
31
31
  ) -> None:
32
32
  create_project_req = {
@@ -39,5 +39,5 @@ def create_project(
39
39
 
40
40
 
41
41
  @app.command(name="delete")
42
- def delete(project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT")) -> None:
42
+ def delete(project: str = typer.Argument(help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT")) -> None:
43
43
  Rest.handle_delete(url=f"/api/project/{project}")
@@ -309,23 +309,35 @@ def upload( # noqa: C901
309
309
  err_console.print(f":boom: [bold red]Got status code[/bold red]: {upload_response.status_code} {upload_response.text}")
310
310
 
311
311
 
312
+ # TODO - Change to use Path for directory # pylint: disable=W0511
312
313
  @app.command()
313
314
  def upload_broker_configuration( # noqa: C901
314
- directory: str = typer.Argument(..., help="Configuration directory"),
315
+ directory: Path = typer.Argument(
316
+ ...,
317
+ exists=True,
318
+ file_okay=False,
319
+ dir_okay=True,
320
+ writable=False,
321
+ readable=True,
322
+ resolve_path=True,
323
+ help="Directory to upload",
324
+ ),
315
325
  recording_session: str = typer.Option(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
316
326
  project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
317
327
  overwrite: bool = typer.Option(False, help="Overwrite existing configuration if it exists"),
318
328
  ) -> None:
329
+ """
330
+ Uploads a broker configuration directory
331
+ """
319
332
  # pylint: disable=R0914,R0915
333
+
320
334
  # Must end with /
321
- if not directory.endswith("/"):
322
- directory = f"{directory}/"
323
335
 
324
336
  #
325
337
  # List files in specified directory. Look for interfaces.json and use that directory where this is located
326
338
  # as configuration home directory
327
339
  #
328
- files = list(filter(lambda item: "interfaces.json" in item, glob.iglob(directory + "**/**", recursive=True)))
340
+ files = list(filter(lambda item: "interfaces.json" in item, glob.iglob(str(directory) + "/**/**", recursive=True)))
329
341
  if len(files) == 0:
330
342
  sys.stderr.write("No interfaces.json found in directory, this file is required")
331
343
  raise typer.Exit(1)
@@ -362,7 +374,7 @@ def upload_broker_configuration( # noqa: C901
362
374
  file_infos = list(
363
375
  map(
364
376
  lambda item: {"local_path": item, "remote_path": f"/{broker_config_dir_name}{item.rsplit(broker_config_dir_name, 1)[-1]}"},
365
- glob.iglob(directory + "**/*.*", recursive=True),
377
+ glob.iglob(str(directory) + "/**/*.*", recursive=True),
366
378
  )
367
379
  )
368
380
 
@@ -406,7 +418,7 @@ def upload_broker_configuration( # noqa: C901
406
418
 
407
419
 
408
420
  @app.command(help="Downloads the specified broker configuration directory as zip file")
409
- def download_configuration(
421
+ def download_broker_configuration(
410
422
  broker_config_name: str = typer.Argument(..., help="Broker config name"),
411
423
  recording_session: str = typer.Option(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
412
424
  project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
@@ -425,7 +437,7 @@ def download_configuration(
425
437
 
426
438
 
427
439
  @app.command(help="Delete the specified broker configuration")
428
- def delete_configuration(
440
+ def delete_broker_configuration(
429
441
  broker_config_name: str = typer.Argument(..., help="Broker config name"),
430
442
  recording_session: str = typer.Option(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
431
443
  project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
@@ -436,11 +448,11 @@ def delete_configuration(
436
448
  @app.command(help="Copy recording to another project")
437
449
  def copy(
438
450
  recording_session: str = typer.Argument(..., help="Recording session id"),
439
- project: str = typer.Option(..., help="Source project", envvar="REMOTIVE_CLOUD_PROJECT"),
440
- destination_project: str = typer.Option(..., help="Destination project"),
451
+ from_project: str = typer.Option(..., help="Source project"),
452
+ to_project: str = typer.Option(..., help="Destination project"),
441
453
  ) -> None:
442
454
  Rest.handle_post(
443
- url=f"/api/project/{project}/files/recording/{recording_session}/copy", body=json.dumps({"projectUid": destination_project})
455
+ url=f"/api/project/{from_project}/files/recording/{recording_session}/copy", body=json.dumps({"projectUid": to_project})
444
456
  )
445
457
 
446
458
 
@@ -1,5 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  import os
2
4
  import sys
5
+ from typing import Dict
3
6
 
4
7
  import requests
5
8
  from rich.progress import wrap_file
@@ -21,11 +24,12 @@ def __get_uploaded_bytes(upload_url: str) -> int:
21
24
  return 0
22
25
 
23
26
 
24
- def upload_signed_url(signed_url: str, source_file_name: str, content_type: str) -> None:
27
+ def with_resumable_upload_signed_url(signed_url: str, source_file_name: str, content_type: str) -> None:
25
28
  """
26
29
  Upload file to file storage with signed url and resumable upload.
27
30
  Resumable upload will only work with the same URL and not if a new signed URL is requested with the
28
31
  same object id.
32
+ :param content_type:
29
33
  :param signed_url:
30
34
  :param source_file_name:
31
35
  :return:
@@ -60,3 +64,22 @@ def upload_signed_url(signed_url: str, source_file_name: str, content_type: str)
60
64
  sys.exit(1)
61
65
 
62
66
  print(f"File {source_file_name} uploaded successfully.")
67
+
68
+
69
+ def upload_signed_url(signed_url: str, source_file_name: str, headers: Dict[str, str]) -> None:
70
+ """
71
+ Upload file to file storage with signed url and resumable upload.
72
+ Resumable upload will only work with the same URL and not if a new signed URL is requested with the
73
+ same object id.
74
+ :param headers:
75
+ :param signed_url:
76
+ :param source_file_name:
77
+ :return:
78
+ """
79
+ with open(source_file_name, "rb") as f:
80
+ response = requests.put(signed_url, headers=headers, timeout=60, data=f)
81
+ if response.status_code not in (200, 201, 308):
82
+ ErrorPrinter.print_generic_error(f"Failed to upload file: {response.status_code} - {response.text}")
83
+ sys.exit(1)
84
+
85
+ print(f"File {source_file_name} uploaded successfully.")
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "remotivelabs-cli"
3
- version = "0.0.26"
3
+ version = "0.0.28"
4
4
  description = "CLI for operating RemotiveCloud and RemotiveBroker"
5
5
  authors = ["Johan Rask <johan.rask@remotivelabs.com>"]
6
6
  readme = "README.md"