weni-cli 0.1.0__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.
- weni_cli-0.1.0/PKG-INFO +9 -0
- weni_cli-0.1.0/README.md +1 -0
- weni_cli-0.1.0/pyproject.toml +24 -0
- weni_cli-0.1.0/setup.cfg +4 -0
- weni_cli-0.1.0/setup.py +21 -0
- weni_cli-0.1.0/tests/__init__.py +0 -0
- weni_cli-0.1.0/weni_cli/__init__.py +0 -0
- weni_cli-0.1.0/weni_cli/auth.py +52 -0
- weni_cli-0.1.0/weni_cli/cli.py +74 -0
- weni_cli-0.1.0/weni_cli/handler.py +10 -0
- weni_cli-0.1.0/weni_cli/store.py +46 -0
- weni_cli-0.1.0/weni_cli/wsgi.py +33 -0
- weni_cli-0.1.0/weni_cli.egg-info/PKG-INFO +9 -0
- weni_cli-0.1.0/weni_cli.egg-info/SOURCES.txt +16 -0
- weni_cli-0.1.0/weni_cli.egg-info/dependency_links.txt +1 -0
- weni_cli-0.1.0/weni_cli.egg-info/entry_points.txt +2 -0
- weni_cli-0.1.0/weni_cli.egg-info/requires.txt +6 -0
- weni_cli-0.1.0/weni_cli.egg-info/top_level.txt +2 -0
weni_cli-0.1.0/PKG-INFO
ADDED
weni_cli-0.1.0/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Weni-CLI
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "weni-cli"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = ""
|
|
5
|
+
authors = ["Paulo Bernardo <paulo.bernardo@weni.ai>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
|
|
8
|
+
[tool.poetry.dependencies]
|
|
9
|
+
python = "^3.12"
|
|
10
|
+
click = "^8.1.8"
|
|
11
|
+
requests = "^2.32.3"
|
|
12
|
+
flask = "^3.1.0"
|
|
13
|
+
waitress = "^3.0.2"
|
|
14
|
+
pyyaml = "^6.0.2"
|
|
15
|
+
python-slugify = "^8.0.4"
|
|
16
|
+
setuptools = "^75.7.0"
|
|
17
|
+
twine = "^6.0.1"
|
|
18
|
+
|
|
19
|
+
[tool.poetry.scripts]
|
|
20
|
+
weni = "weni_cli.cli:cli"
|
|
21
|
+
|
|
22
|
+
[build-system]
|
|
23
|
+
requires = ["poetry-core"]
|
|
24
|
+
build-backend = "poetry.core.masonry.api"
|
weni_cli-0.1.0/setup.cfg
ADDED
weni_cli-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="weni-cli",
|
|
5
|
+
version="0.1.0",
|
|
6
|
+
packages=find_packages(),
|
|
7
|
+
include_package_data=True,
|
|
8
|
+
install_requires=[
|
|
9
|
+
"Click",
|
|
10
|
+
"requests",
|
|
11
|
+
"flask",
|
|
12
|
+
"waitress",
|
|
13
|
+
"pyyaml",
|
|
14
|
+
"python-slugify",
|
|
15
|
+
],
|
|
16
|
+
entry_points={
|
|
17
|
+
"console_scripts": [
|
|
18
|
+
"weni = weni_cli.cli:cli",
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
from weni_cli.store import (
|
|
4
|
+
STORE_KEYCLOAK_CLIENT_ID,
|
|
5
|
+
STORE_KEYCLOAK_REALM,
|
|
6
|
+
STORE_KEYCLOAK_URL,
|
|
7
|
+
Store,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
DEFAULT_KEYCLOAK_URL = "https://accounts.weni.ai/auth"
|
|
11
|
+
DEFAULT_KEYCLOAK_REALM = "weni"
|
|
12
|
+
DEFAULT_KEYCLOAK_CLIENT_ID = "weni-cli"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Auth:
|
|
16
|
+
keycloak_url = None
|
|
17
|
+
realm = None
|
|
18
|
+
client_id = None
|
|
19
|
+
response_type = "code"
|
|
20
|
+
redirect_uri = "http://localhost:8081/sso-callback"
|
|
21
|
+
|
|
22
|
+
def __init__(self):
|
|
23
|
+
store = Store()
|
|
24
|
+
self.keycloak_url = store.get(STORE_KEYCLOAK_URL, DEFAULT_KEYCLOAK_URL)
|
|
25
|
+
self.realm = store.get(STORE_KEYCLOAK_REALM, DEFAULT_KEYCLOAK_REALM)
|
|
26
|
+
self.client_id = store.get(STORE_KEYCLOAK_CLIENT_ID, DEFAULT_KEYCLOAK_CLIENT_ID)
|
|
27
|
+
|
|
28
|
+
def get_login_url(self) -> str:
|
|
29
|
+
return f"{self.keycloak_url}/realms/{self.realm}/protocol/openid-connect/auth?client_id={self.client_id}&redirect_uri={self.redirect_uri}&response_type={self.response_type}"
|
|
30
|
+
|
|
31
|
+
def exchange_code(self, code) -> str:
|
|
32
|
+
token_url = (
|
|
33
|
+
f"{self.keycloak_url}/realms/{self.realm}/protocol/openid-connect/token"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
data = {
|
|
37
|
+
"client_id": self.client_id,
|
|
38
|
+
"grant_type": "authorization_code",
|
|
39
|
+
"code": code,
|
|
40
|
+
"redirect_uri": self.redirect_uri,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
response = requests.post(
|
|
44
|
+
token_url,
|
|
45
|
+
data=data,
|
|
46
|
+
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
if response.status_code != 200:
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
return response.json().get("access_token", None)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# Main CLI Group
|
|
5
|
+
@click.group()
|
|
6
|
+
def cli():
|
|
7
|
+
"""Weni CLI"""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# Login Command
|
|
11
|
+
@cli.command("login")
|
|
12
|
+
def login():
|
|
13
|
+
"""Login with your Weni account"""
|
|
14
|
+
from weni_cli.commands.login import LoginHandler
|
|
15
|
+
|
|
16
|
+
LoginHandler().execute()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Nested CLI Project Group
|
|
20
|
+
@cli.group()
|
|
21
|
+
def project():
|
|
22
|
+
"""Project commands"""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Project Group Commands
|
|
26
|
+
@project.command("list")
|
|
27
|
+
@click.option("--org", "-o", help="Filter by organization", type=click.UUID)
|
|
28
|
+
def list_projects(org):
|
|
29
|
+
"""List projects"""
|
|
30
|
+
from weni_cli.commands.project_list import ProjectListHandler
|
|
31
|
+
|
|
32
|
+
ProjectListHandler().execute(org_uuid=org)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@project.command("use")
|
|
36
|
+
@click.argument("project_uuid", required=True)
|
|
37
|
+
def use_project(project_uuid):
|
|
38
|
+
"""Set the project to be used in the CLI
|
|
39
|
+
|
|
40
|
+
PROJECT_UUID: The UUID of the project to be used
|
|
41
|
+
"""
|
|
42
|
+
from weni_cli.commands.project_use import ProjectUseHandler
|
|
43
|
+
|
|
44
|
+
ProjectUseHandler().execute(project_uuid=project_uuid)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@project.command("current")
|
|
48
|
+
def current_project():
|
|
49
|
+
"""Show current selected project"""
|
|
50
|
+
from weni_cli.commands.project_current import ProjectCurrentHandler
|
|
51
|
+
|
|
52
|
+
ProjectCurrentHandler().execute()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@project.command("push")
|
|
56
|
+
@click.argument(
|
|
57
|
+
"definition", required=True, type=click.Path(exists=True, dir_okay=False)
|
|
58
|
+
)
|
|
59
|
+
@click.option("--force-update", is_flag=True, help="Force update to the project")
|
|
60
|
+
def push_project(definition, force_update):
|
|
61
|
+
"""Push an Agent definition to the current project
|
|
62
|
+
|
|
63
|
+
DEFINITION: The path to the YAML agent definition file
|
|
64
|
+
"""
|
|
65
|
+
from weni_cli.commands.project_push import ProjectPushHandler
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
ProjectPushHandler().execute(definition=definition, force_update=force_update)
|
|
69
|
+
except Exception as e:
|
|
70
|
+
click.echo(f"Error: {e}")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
if __name__ == "__main__":
|
|
74
|
+
cli()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import os
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
STORE_TOKEN_KEY = "token"
|
|
8
|
+
STORE_PROJECT_UUID_KEY = "project_uuid"
|
|
9
|
+
STORE_WENI_BASE_URL = "weni_base_url"
|
|
10
|
+
STORE_NEXUS_BASE_URL = "nexus_base_url"
|
|
11
|
+
STORE_KEYCLOAK_URL = "keycloak_url"
|
|
12
|
+
STORE_KEYCLOAK_REALM = "keycloak_realm"
|
|
13
|
+
STORE_KEYCLOAK_CLIENT_ID = "keycloak_client_id"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Store:
|
|
17
|
+
file_path = f"{Path.home()}{os.sep}.weni_cli"
|
|
18
|
+
|
|
19
|
+
# Validates that the file exists, if it does not exist, it creates it with an empty dictionary
|
|
20
|
+
def __init__(self):
|
|
21
|
+
with click.open_file(self.file_path, "a+") as file:
|
|
22
|
+
file.seek(0)
|
|
23
|
+
content = file.read()
|
|
24
|
+
if not content:
|
|
25
|
+
file.write("{}")
|
|
26
|
+
|
|
27
|
+
file.close()
|
|
28
|
+
|
|
29
|
+
def get(self, key, default=None):
|
|
30
|
+
with click.open_file(self.file_path, "r") as file:
|
|
31
|
+
content = json.loads(file.read())
|
|
32
|
+
file.close()
|
|
33
|
+
return content.get(key, default)
|
|
34
|
+
|
|
35
|
+
def set(self, key, value):
|
|
36
|
+
content = {}
|
|
37
|
+
with click.open_file(self.file_path, "r") as file:
|
|
38
|
+
content = json.loads(file.read())
|
|
39
|
+
file.close()
|
|
40
|
+
|
|
41
|
+
with click.open_file(self.file_path, "w") as file:
|
|
42
|
+
content[key] = value
|
|
43
|
+
file.write(json.dumps(content))
|
|
44
|
+
file.close()
|
|
45
|
+
|
|
46
|
+
return True
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from flask import Flask, request
|
|
4
|
+
from multiprocessing import Process, Pipe
|
|
5
|
+
|
|
6
|
+
app = Flask(__name__)
|
|
7
|
+
server_process = None
|
|
8
|
+
auth_parent_conn, auth_child_conn = Pipe()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@app.route("/sso-callback", methods=["GET"])
|
|
12
|
+
def sso_callback():
|
|
13
|
+
auth_child_conn.send(request.args.get("code"))
|
|
14
|
+
return "Successfully logged in, you can close this window now"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def serve():
|
|
18
|
+
from waitress import serve
|
|
19
|
+
|
|
20
|
+
global server_process
|
|
21
|
+
|
|
22
|
+
server_process = Process(
|
|
23
|
+
target=serve,
|
|
24
|
+
kwargs={"app": app, "host": "0.0.0.0", "port": 8081, "_quiet": True},
|
|
25
|
+
)
|
|
26
|
+
server_process.start()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def shutdown():
|
|
30
|
+
global server_process
|
|
31
|
+
if server_process is not None:
|
|
32
|
+
server_process.terminate()
|
|
33
|
+
server_process = None
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
setup.py
|
|
4
|
+
tests/__init__.py
|
|
5
|
+
weni_cli/__init__.py
|
|
6
|
+
weni_cli/auth.py
|
|
7
|
+
weni_cli/cli.py
|
|
8
|
+
weni_cli/handler.py
|
|
9
|
+
weni_cli/store.py
|
|
10
|
+
weni_cli/wsgi.py
|
|
11
|
+
weni_cli.egg-info/PKG-INFO
|
|
12
|
+
weni_cli.egg-info/SOURCES.txt
|
|
13
|
+
weni_cli.egg-info/dependency_links.txt
|
|
14
|
+
weni_cli.egg-info/entry_points.txt
|
|
15
|
+
weni_cli.egg-info/requires.txt
|
|
16
|
+
weni_cli.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|