remotivelabs-cli 0.0.1a2__tar.gz → 0.0.1a4__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.
- remotivelabs_cli-0.0.1a4/LICENSE +17 -0
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/PKG-INFO +1 -1
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/cli/brokers.py +18 -40
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/cli/cloud/auth.py +19 -5
- remotivelabs_cli-0.0.1a4/cli/cloud/auth_keys.py +46 -0
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/cli/cloud/brokers.py +1 -1
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/cli/cloud/cloud_cli.py +5 -12
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/cli/cloud/configs.py +3 -4
- remotivelabs_cli-0.0.1a4/cli/cloud/projects.py +30 -0
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/cli/cloud/rest_helper.py +7 -2
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/cli/cloud/service_account_keys.py +8 -0
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/cli/cloud/service_accounts.py +6 -8
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/cli/remotive.py +1 -5
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/pyproject.toml +1 -3
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/README.md +0 -0
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/cli/__about__.py +0 -0
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/cli/__init__.py +0 -0
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/cli/cloud/__init__.py +0 -0
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/cli/cloud/recordings.py +0 -0
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/cli/lib/__about__.py +0 -0
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/cli/lib/broker.py +0 -0
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/cli/requirements.txt +0 -0
- {remotivelabs_cli-0.0.1a2 → remotivelabs_cli-0.0.1a4}/cli/test/test_simple.py +0 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
Apache-2.0
|
2
|
+
|
3
|
+
RemotiveLabs CLI
|
4
|
+
Copyright 2022 RemotiveLabs <hello@remotivelabs.com>
|
5
|
+
|
6
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
you may not use this file except in compliance with the License.
|
8
|
+
You may obtain a copy of the License at
|
9
|
+
|
10
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
|
12
|
+
Unless required by applicable law or agreed to in writing, software
|
13
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
See the License for the specific language governing permissions and
|
16
|
+
limitations under the License.
|
17
|
+
|
@@ -1,27 +1,17 @@
|
|
1
1
|
import json
|
2
|
-
import time
|
3
|
-
import typer
|
4
|
-
#import network
|
5
|
-
from .lib.broker import Broker
|
6
|
-
#from network import MDNS
|
7
|
-
|
8
|
-
|
9
|
-
import argparse
|
10
|
-
import logging
|
11
2
|
from time import sleep
|
12
|
-
from typing import cast
|
13
3
|
|
4
|
+
import typer
|
14
5
|
from zeroconf import (
|
15
6
|
IPVersion,
|
16
7
|
ServiceBrowser,
|
17
8
|
ServiceStateChange,
|
18
9
|
Zeroconf,
|
19
|
-
ZeroconfServiceTypes,
|
20
10
|
)
|
21
11
|
|
22
|
-
|
23
|
-
|
12
|
+
from .lib.broker import Broker
|
24
13
|
|
14
|
+
app = typer.Typer()
|
25
15
|
|
26
16
|
|
27
17
|
@app.command(help="List signals on broker")
|
@@ -30,7 +20,7 @@ def signals(
|
|
30
20
|
api_key: str = typer.Option("offline", help="Cloud Broker API-KEY", envvar='REMOTIVE_BROKER_API_KEY')
|
31
21
|
):
|
32
22
|
broker = Broker(url, api_key)
|
33
|
-
print("Listing available signals")
|
23
|
+
# print("Listing available signals")
|
34
24
|
available_signals = broker.list_signal_names()
|
35
25
|
print(json.dumps(available_signals))
|
36
26
|
|
@@ -41,17 +31,18 @@ def namespaces(
|
|
41
31
|
api_key: str = typer.Option("offline", help="Cloud Broker API-KEY", envvar='REMOTIVE_BROKER_API_KEY')
|
42
32
|
):
|
43
33
|
broker = Broker(url, api_key)
|
44
|
-
|
45
|
-
print(json.dumps(
|
34
|
+
namespaces_json = broker.list_namespaces()
|
35
|
+
print(json.dumps(namespaces_json))
|
36
|
+
|
46
37
|
|
47
38
|
@app.command(help="Discover brokers on this network")
|
48
39
|
def discover():
|
49
|
-
#print("Not implemented")
|
40
|
+
# print("Not implemented")
|
50
41
|
|
51
42
|
zeroconf = Zeroconf(ip_version=IPVersion.V4Only)
|
52
43
|
|
53
|
-
services = ["_remotivebroker._tcp.local.","_googlecast._tcp.local."]
|
54
|
-
#services = list(ZeroconfServiceTypes.find(zc=zeroconf))
|
44
|
+
services = ["_remotivebroker._tcp.local.", "_googlecast._tcp.local."]
|
45
|
+
# services = list(ZeroconfServiceTypes.find(zc=zeroconf))
|
55
46
|
|
56
47
|
print("\nLooking for RemotiveBrokers on your network, press Ctrl-C to exit...\n")
|
57
48
|
browser = ServiceBrowser(zeroconf, services, handlers=[on_service_state_change])
|
@@ -64,25 +55,11 @@ def discover():
|
|
64
55
|
finally:
|
65
56
|
zeroconf.close()
|
66
57
|
|
67
|
-
#network.MDNS.init()
|
68
|
-
|
69
|
-
# Perform a query for 2000 ms
|
70
|
-
#q = MDNS.query(2000, "broker", MDNS.PROTO_TCP)
|
71
|
-
|
72
|
-
# Print out the query's result
|
73
|
-
#if q is not None:
|
74
|
-
# for elem in q:
|
75
|
-
# print(elem.instance_name())
|
76
|
-
# print(elem.hostname())
|
77
|
-
# print(elem.addr())
|
78
|
-
# print(elem.port())
|
79
|
-
# print(elem.txt())
|
80
|
-
|
81
58
|
|
82
59
|
def on_service_state_change(
|
83
60
|
zeroconf: Zeroconf, service_type: str, name: str, state_change: ServiceStateChange
|
84
61
|
) -> None:
|
85
|
-
#print(f"Service {name} state changed: {state_change}")
|
62
|
+
# print(f"Service {name} state changed: {state_change}")
|
86
63
|
|
87
64
|
if state_change is ServiceStateChange.Removed:
|
88
65
|
print(f"Service {name} was removed")
|
@@ -93,23 +70,24 @@ def on_service_state_change(
|
|
93
70
|
if state_change is ServiceStateChange.Added:
|
94
71
|
print(f"Discovered {name} ")
|
95
72
|
info = zeroconf.get_service_info(service_type, name)
|
96
|
-
#print("Info from zeroconf.get_service_info: %r" % (info))
|
73
|
+
# print("Info from zeroconf.get_service_info: %r" % (info))
|
97
74
|
|
98
75
|
if info:
|
99
|
-
#addresses = ["%s:%d" % (addr, cast(int, info.port)) for addr in info.parsed_scoped_addresses()]
|
76
|
+
# addresses = ["%s:%d" % (addr, cast(int, info.port)) for addr in info.parsed_scoped_addresses()]
|
100
77
|
for addr in info.parsed_scoped_addresses():
|
101
78
|
print(addr)
|
102
|
-
#print(" Weight: %d, priority: %d" % (info.weight, info.priority))
|
103
|
-
#print(f" Server: {info.server}")
|
104
|
-
#if info.properties:
|
79
|
+
# print(" Weight: %d, priority: %d" % (info.weight, info.priority))
|
80
|
+
# print(f" Server: {info.server}")
|
81
|
+
# if info.properties:
|
105
82
|
# print(" Properties are:")
|
106
83
|
# for key, value in info.properties.items():
|
107
84
|
# print(f" {key}: {value}")
|
108
|
-
#else:
|
85
|
+
# else:
|
109
86
|
# print(" No properties")
|
110
87
|
else:
|
111
88
|
print(" No info")
|
112
89
|
print('\n')
|
113
90
|
|
91
|
+
|
114
92
|
if __name__ == "__main__":
|
115
93
|
app()
|
@@ -8,19 +8,21 @@ from pathlib import Path
|
|
8
8
|
from threading import Thread
|
9
9
|
import typer
|
10
10
|
from . import rest_helper as rest
|
11
|
+
from . import auth_keys
|
11
12
|
|
12
13
|
app = typer.Typer()
|
13
|
-
|
14
|
+
app.add_typer(auth_keys.app, name="keys", help="Manage users personal access keys")
|
14
15
|
config_dir_name = str(Path.home()) + "/.config/.remotive/"
|
15
16
|
token_file_name = str(Path.home()) + "/.config/.remotive/cloud.secret.token"
|
16
17
|
|
17
18
|
|
19
|
+
|
18
20
|
class S(BaseHTTPRequestHandler):
|
19
21
|
def _set_response(self):
|
20
22
|
self.send_response(200)
|
21
|
-
#self.send_response(301)
|
22
|
-
#self.send_header('Location', 'https://cloud.remotivelabs.com')
|
23
|
-
#self.end_headers()
|
23
|
+
# self.send_response(301)
|
24
|
+
# self.send_header('Location', 'https://cloud.remotivelabs.com')
|
25
|
+
# self.end_headers()
|
24
26
|
self.send_header('Content-type', 'text/html')
|
25
27
|
self.end_headers()
|
26
28
|
|
@@ -55,26 +57,32 @@ def login():
|
|
55
57
|
webbrowser.open(f'{rest.base_url}/login?redirectUrl=http://localhost:8089', new=1, autoraise=True)
|
56
58
|
start_local_webserver()
|
57
59
|
|
60
|
+
@app.command(help="Who am I?")
|
61
|
+
def whoami():
|
62
|
+
rest.handle_get('/api/whoami')
|
58
63
|
|
59
64
|
@app.command(help="Print access token")
|
60
65
|
def print_access_token():
|
61
66
|
print(read_token())
|
62
67
|
|
68
|
+
|
63
69
|
@app.command(help="List available auth tokens")
|
64
70
|
def list():
|
65
71
|
for file in os.listdir(config_dir_name):
|
66
72
|
print(file)
|
67
73
|
|
74
|
+
|
68
75
|
@app.command(help="Show token file")
|
69
76
|
def describe(
|
70
77
|
file: str = typer.Option(..., help="File name")):
|
71
78
|
print(read_file(file))
|
72
79
|
|
73
80
|
|
81
|
+
|
82
|
+
|
74
83
|
@app.command(help="Activate the account file")
|
75
84
|
def activate(
|
76
85
|
file: str = typer.Option(..., help="File name")):
|
77
|
-
|
78
86
|
# Best effort to read file
|
79
87
|
if os.path.exists(file):
|
80
88
|
token_file = json.loads(read_file_with_path(file))
|
@@ -106,23 +114,29 @@ def read_token():
|
|
106
114
|
f.close()
|
107
115
|
return token
|
108
116
|
|
117
|
+
|
109
118
|
def read_file_with_path(file):
|
110
119
|
f = open(file, "r")
|
111
120
|
token = f.read()
|
112
121
|
f.close()
|
113
122
|
return token
|
114
123
|
|
124
|
+
|
115
125
|
def read_file(file):
|
116
126
|
f = open(str(Path.home()) + f"/.config/.remotive/{file}", "r")
|
117
127
|
token = f.read()
|
118
128
|
f.close()
|
119
129
|
return token
|
120
130
|
|
131
|
+
|
121
132
|
def write_token(token):
|
122
133
|
f = open(token_file_name, "w")
|
123
134
|
f.write(token)
|
124
135
|
f.close()
|
125
136
|
|
137
|
+
|
138
|
+
|
139
|
+
|
126
140
|
# Key stuff
|
127
141
|
# f = open(str(Path.home())+ "/.remotivelabs/privatekey.json", "r")
|
128
142
|
# j = json.loads(f.read())
|
@@ -0,0 +1,46 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
|
3
|
+
import typer
|
4
|
+
from . import rest_helper as rest
|
5
|
+
|
6
|
+
app = typer.Typer()
|
7
|
+
|
8
|
+
|
9
|
+
@app.command(name="create", help="Create and download a new personal access token")
|
10
|
+
def get_personal_access_token():
|
11
|
+
rest.ensure_auth_token()
|
12
|
+
rest.handle_post('/api/me/keys')
|
13
|
+
|
14
|
+
response = rest.handle_post(url='/api/me/keys',
|
15
|
+
return_response=True)
|
16
|
+
|
17
|
+
if response.status_code == 200:
|
18
|
+
name = response.json()['name']
|
19
|
+
write_personal_token(f"personal-{name}-key.json", response.text)
|
20
|
+
else:
|
21
|
+
print(f'Got status code: {response.status_code}')
|
22
|
+
print(response.text)
|
23
|
+
|
24
|
+
|
25
|
+
@app.command(name="list", help="List personal access tokens")
|
26
|
+
def list_personal_access_tokens():
|
27
|
+
rest.ensure_auth_token()
|
28
|
+
rest.handle_get('/api/me/keys')
|
29
|
+
|
30
|
+
|
31
|
+
@app.command(name="revoke", help="Revoke the specified key")
|
32
|
+
def revoke(
|
33
|
+
key_name: str = typer.Option(..., help="Name of the key to revoke")
|
34
|
+
):
|
35
|
+
rest.ensure_auth_token()
|
36
|
+
rest.handle_delete(f'/api/me/keys/{key_name}')
|
37
|
+
|
38
|
+
|
39
|
+
def write_personal_token(file, token):
|
40
|
+
path = str(Path.home()) + f"/.config/.remotive/{file}"
|
41
|
+
f = open(path, "w")
|
42
|
+
f.write(token)
|
43
|
+
f.close()
|
44
|
+
print(f"Personal key written to {path}")
|
45
|
+
print("Use 'remotive cloud auth activate filename' to use this key from cli")
|
46
|
+
print('\033[93m This file contains secrets and must never be compromised')
|
@@ -1,19 +1,11 @@
|
|
1
|
-
import time
|
2
|
-
|
3
1
|
import typer
|
4
|
-
|
5
|
-
from
|
6
|
-
from . import recordings, brokers, configs, auth, service_accounts
|
2
|
+
|
3
|
+
from . import recordings, brokers, configs, auth, service_accounts, projects
|
7
4
|
from . import rest_helper as rest
|
8
5
|
|
9
6
|
app = typer.Typer()
|
10
7
|
|
11
8
|
|
12
|
-
@app.command(help="Who am I?")
|
13
|
-
def whoami():
|
14
|
-
rest.handle_get('/api/whoami')
|
15
|
-
|
16
|
-
|
17
9
|
@app.command(help="List licenses for an organisation")
|
18
10
|
def licenses(
|
19
11
|
organisation: str = typer.Option(..., help="Organisation ID", envvar='REMOTIVE_CLOUD_ORGANISATION'),
|
@@ -41,10 +33,11 @@ def users(
|
|
41
33
|
rest.handle_get(f"/api/bu/{organisation}/users")
|
42
34
|
|
43
35
|
|
36
|
+
app.add_typer(projects.app, name="projects", help="Manage projects")
|
37
|
+
app.add_typer(auth.app, name="auth", help="Manage access to cloud resources")
|
44
38
|
app.add_typer(brokers.app, name="brokers", help="Manage cloud broker lifecycle")
|
45
39
|
app.add_typer(recordings.app, name="recordings", help="Manage recordings")
|
46
|
-
app.add_typer(configs.app, name="
|
47
|
-
app.add_typer(auth.app, name="auth", help="Manage access to cloud resources")
|
40
|
+
app.add_typer(configs.app, name="signal-databases", help="Manage signal databases")
|
48
41
|
app.add_typer(service_accounts.app, name="service-accounts", help="Manage project service account keys")
|
49
42
|
|
50
43
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import json
|
2
|
+
|
3
|
+
import typer
|
4
|
+
from . import rest_helper as rest
|
5
|
+
app = typer.Typer()
|
6
|
+
|
7
|
+
|
8
|
+
@app.command(name="list", help="List your projects")
|
9
|
+
def list_projects(organisation: str = typer.Option(..., help="Organisation ID", envvar='REMOTIVE_CLOUD_ORGANISATION')):
|
10
|
+
r = rest.handle_get(url=f'/api/bu/{organisation}/me', return_response=True)
|
11
|
+
if r.status_code == 200:
|
12
|
+
projects = r.json()['projects']
|
13
|
+
projects = map(lambda p: p['uid'], projects)
|
14
|
+
print(json.dumps(list(projects)))
|
15
|
+
|
16
|
+
|
17
|
+
@app.command(name="create")
|
18
|
+
def create_project(
|
19
|
+
organisation: str = typer.Option(..., help="Organisation ID", envvar='REMOTIVE_CLOUD_ORGANISATION'),
|
20
|
+
project_uid: str = typer.Option(..., help="Project UID"),
|
21
|
+
project_display_name: str = typer.Option(default="", help="Project display name")
|
22
|
+
):
|
23
|
+
|
24
|
+
create_project_req = {
|
25
|
+
'uid': project_uid,
|
26
|
+
'displayName': project_display_name if project_display_name != "" else project_uid,
|
27
|
+
'description': ''
|
28
|
+
}
|
29
|
+
|
30
|
+
rest.handle_post(url=f'/api/bu/{organisation}/project', body=json.dumps(create_project_req))
|
@@ -6,7 +6,7 @@ import json
|
|
6
6
|
import logging
|
7
7
|
from pathlib import Path
|
8
8
|
from os.path import exists
|
9
|
-
|
9
|
+
#from rich import print
|
10
10
|
import typer
|
11
11
|
|
12
12
|
if 'REMOTIVE_CLOUD_HTTP_LOGGING' in os.environ:
|
@@ -51,9 +51,13 @@ def ensure_auth_token():
|
|
51
51
|
headers = {"Authorization": "Bearer " + token.strip()}
|
52
52
|
|
53
53
|
|
54
|
-
def handle_get(url, params={}):
|
54
|
+
def handle_get(url, params={},return_response: bool = False):
|
55
55
|
ensure_auth_token()
|
56
56
|
r = requests.get(f'{base_url}{url}', headers=headers, params=params)
|
57
|
+
|
58
|
+
if return_response:
|
59
|
+
return r
|
60
|
+
|
57
61
|
if r.status_code == 200:
|
58
62
|
print(json.dumps(r.json()))
|
59
63
|
else:
|
@@ -97,3 +101,4 @@ def handle_post(url, body=None, params={}, return_response: bool = False):
|
|
97
101
|
print(r.status_code)
|
98
102
|
else:
|
99
103
|
print(f'Got status code: {r.status_code}')
|
104
|
+
print(r.text)
|
@@ -35,6 +35,14 @@ def list_keys(
|
|
35
35
|
rest.handle_get(f"/api/project/{project}/admin/accounts/{service_account}/keys")
|
36
36
|
|
37
37
|
|
38
|
+
@app.command(name="revoke", help="Revoke service account key")
|
39
|
+
def revoke(
|
40
|
+
service_account: str = typer.Option(..., help="Service account name"),
|
41
|
+
key_name: str = typer.Option(..., help="Key name"),
|
42
|
+
project: str = typer.Option(..., help="Project ID", envvar='REMOTIVE_CLOUD_PROJECT')):
|
43
|
+
rest.handle_delete(f"/api/project/{project}/admin/accounts/{service_account}/keys/{key_name}")
|
44
|
+
|
45
|
+
|
38
46
|
def write_token(file, token):
|
39
47
|
path = str(Path.home()) + f"/.config/.remotive/{file}"
|
40
48
|
f = open(path, "w")
|
@@ -1,10 +1,8 @@
|
|
1
|
+
import json
|
2
|
+
from typing import List
|
3
|
+
|
1
4
|
import typer
|
2
|
-
# from typing_extensions import Annotated
|
3
|
-
from typing import List, Optional
|
4
5
|
|
5
|
-
import json
|
6
|
-
from rich.progress import Progress, SpinnerColumn, TextColumn
|
7
|
-
import sys
|
8
6
|
from . import rest_helper as rest
|
9
7
|
from . import service_account_keys
|
10
8
|
|
@@ -12,12 +10,12 @@ app = typer.Typer()
|
|
12
10
|
|
13
11
|
|
14
12
|
@app.command(name="list", help="List service-accounts")
|
15
|
-
def
|
13
|
+
def list_service_accounts(project: str = typer.Option(..., help="Project ID", envvar='REMOTIVE_CLOUD_PROJECT')):
|
16
14
|
rest.handle_get(f"/api/project/{project}/admin/accounts")
|
17
15
|
|
18
16
|
|
19
17
|
@app.command(name="create", help="Create service account")
|
20
|
-
def
|
18
|
+
def create_service_account(
|
21
19
|
name: str,
|
22
20
|
role: List[str] = typer.Option(..., help="Roles to apply"),
|
23
21
|
project: str = typer.Option(..., help="Project ID", envvar='REMOTIVE_CLOUD_PROJECT')):
|
@@ -29,7 +27,7 @@ def create(
|
|
29
27
|
|
30
28
|
|
31
29
|
@app.command(name="delete", help="Create service account")
|
32
|
-
def
|
30
|
+
def delete_service_account(
|
33
31
|
name: str,
|
34
32
|
project: str = typer.Option(..., help="Project ID", envvar='REMOTIVE_CLOUD_PROJECT')):
|
35
33
|
rest.handle_delete(url=f"/api/project/{project}/admin/accounts/{name}")
|
@@ -1,13 +1,9 @@
|
|
1
1
|
import typer
|
2
2
|
from .brokers import app as broker_app
|
3
3
|
from .cloud.cloud_cli import app as cloud_app
|
4
|
-
|
5
|
-
# from cloud import cloud_cli
|
4
|
+
from rich import print
|
6
5
|
|
7
6
|
app = typer.Typer()
|
8
7
|
|
9
|
-
|
10
|
-
# def run_cli():
|
11
|
-
|
12
8
|
app.add_typer(broker_app, name="broker", help="Manage a single broker - local or cloud")
|
13
9
|
app.add_typer(cloud_app, name="cloud", help="Manage resources in RemotiveCloud", )
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "remotivelabs-cli"
|
3
|
-
version = "0.0.1.
|
3
|
+
version = "0.0.1.alpha4"
|
4
4
|
description = ""
|
5
5
|
authors = ["Johan Rask <johan.rask@remotivelabs.com>"]
|
6
6
|
readme = "README.md"
|
@@ -14,8 +14,6 @@ rich = "^12.6.0"
|
|
14
14
|
cryptography = "40.0.2"
|
15
15
|
pyjwt = "^2.6.0"
|
16
16
|
zeroconf = "^0.64.1"
|
17
|
-
#urllib3 = ">=1.26"
|
18
|
-
#requests = ">=2.25"
|
19
17
|
|
20
18
|
[tool.poetry.scripts]
|
21
19
|
remotive = "cli.remotive:app"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|