uipath 2.0.19.dev1002910038__py3-none-any.whl → 2.0.21__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 uipath might be problematic. Click here for more details.
- uipath/_cli/__init__.py +2 -0
- uipath/_cli/_auth/_auth_server.py +3 -2
- uipath/_cli/_utils/_common.py +21 -0
- uipath/_cli/_utils/_folders.py +29 -0
- uipath/_cli/_utils/_processes.py +49 -0
- uipath/_cli/cli_auth.py +25 -15
- uipath/_cli/cli_init.py +11 -8
- uipath/_cli/cli_invoke.py +105 -0
- uipath/_cli/cli_new.py +11 -5
- uipath/_cli/cli_pack.py +30 -16
- uipath/_cli/cli_publish.py +49 -36
- uipath/_cli/spinner.py +40 -0
- {uipath-2.0.19.dev1002910038.dist-info → uipath-2.0.21.dist-info}/METADATA +2 -2
- {uipath-2.0.19.dev1002910038.dist-info → uipath-2.0.21.dist-info}/RECORD +17 -13
- {uipath-2.0.19.dev1002910038.dist-info → uipath-2.0.21.dist-info}/WHEEL +0 -0
- {uipath-2.0.19.dev1002910038.dist-info → uipath-2.0.21.dist-info}/entry_points.txt +0 -0
- {uipath-2.0.19.dev1002910038.dist-info → uipath-2.0.21.dist-info}/licenses/LICENSE +0 -0
uipath/_cli/__init__.py
CHANGED
|
@@ -6,6 +6,7 @@ import click
|
|
|
6
6
|
from .cli_auth import auth as auth # type: ignore
|
|
7
7
|
from .cli_deploy import deploy as deploy # type: ignore
|
|
8
8
|
from .cli_init import init as init # type: ignore
|
|
9
|
+
from .cli_invoke import invoke as invoke # type: ignore
|
|
9
10
|
from .cli_new import new as new # type: ignore
|
|
10
11
|
from .cli_pack import pack as pack # type: ignore
|
|
11
12
|
from .cli_publish import publish as publish # type: ignore
|
|
@@ -52,3 +53,4 @@ cli.add_command(publish)
|
|
|
52
53
|
cli.add_command(run)
|
|
53
54
|
cli.add_command(deploy)
|
|
54
55
|
cli.add_command(auth)
|
|
56
|
+
cli.add_command(invoke)
|
|
@@ -5,6 +5,7 @@ import socketserver
|
|
|
5
5
|
import ssl
|
|
6
6
|
import time
|
|
7
7
|
|
|
8
|
+
import click
|
|
8
9
|
from dotenv import load_dotenv
|
|
9
10
|
|
|
10
11
|
from ._oidc_utils import get_auth_config
|
|
@@ -38,7 +39,7 @@ def make_request_handler_class(state, code_verifier, token_callback, domain):
|
|
|
38
39
|
content_length = int(self.headers["Content-Length"])
|
|
39
40
|
post_data = self.rfile.read(content_length)
|
|
40
41
|
token_data = json.loads(post_data.decode("utf-8"))
|
|
41
|
-
|
|
42
|
+
click.echo("Received authentication information")
|
|
42
43
|
|
|
43
44
|
self.send_response(200)
|
|
44
45
|
self.end_headers()
|
|
@@ -179,7 +180,7 @@ class HTTPSServer:
|
|
|
179
180
|
while not self.should_shutdown:
|
|
180
181
|
self.httpd.handle_request()
|
|
181
182
|
except KeyboardInterrupt:
|
|
182
|
-
|
|
183
|
+
click.echo("Process interrupted by user")
|
|
183
184
|
finally:
|
|
184
185
|
self.stop()
|
|
185
186
|
|
uipath/_cli/_utils/_common.py
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
1
4
|
import click
|
|
2
5
|
|
|
6
|
+
from ..spinner import Spinner
|
|
7
|
+
|
|
3
8
|
|
|
4
9
|
def environment_options(function):
|
|
5
10
|
function = click.option(
|
|
@@ -22,3 +27,19 @@ def environment_options(function):
|
|
|
22
27
|
help="Use production environment",
|
|
23
28
|
)(function)
|
|
24
29
|
return function
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_env_vars(spinner: Optional[Spinner] = None) -> list[str | None]:
|
|
33
|
+
base_url = os.environ.get("UIPATH_URL")
|
|
34
|
+
token = os.environ.get("UIPATH_ACCESS_TOKEN")
|
|
35
|
+
|
|
36
|
+
if not all([base_url, token]):
|
|
37
|
+
if spinner:
|
|
38
|
+
spinner.stop()
|
|
39
|
+
click.echo(
|
|
40
|
+
"❌ Missing required environment variables. Please check your .env file contains:"
|
|
41
|
+
)
|
|
42
|
+
click.echo("UIPATH_URL, UIPATH_ACCESS_TOKEN")
|
|
43
|
+
click.get_current_context().exit(1)
|
|
44
|
+
|
|
45
|
+
return [base_url, token]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from typing import Optional, Tuple
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
from ..spinner import Spinner
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_personal_workspace_info(
|
|
10
|
+
base_url: str, token: str, spinner: Optional[Spinner] = None
|
|
11
|
+
) -> Tuple[str, str]:
|
|
12
|
+
user_url = f"{base_url}/orchestrator_/odata/Users/UiPath.Server.Configuration.OData.GetCurrentUserExtended?$expand=PersonalWorkspace"
|
|
13
|
+
user_response = requests.get(user_url, headers={"Authorization": f"Bearer {token}"})
|
|
14
|
+
|
|
15
|
+
if user_response.status_code != 200:
|
|
16
|
+
if spinner:
|
|
17
|
+
spinner.stop()
|
|
18
|
+
click.echo("❌ Failed to get user info. Please try reauthenticating.")
|
|
19
|
+
click.get_current_context().exit(1)
|
|
20
|
+
|
|
21
|
+
user_data = user_response.json()
|
|
22
|
+
feed_id = user_data.get("PersonalWorskpaceFeedId")
|
|
23
|
+
folder_id = user_data["PersonalWorkspace"].get("Id")
|
|
24
|
+
if not (feed_id and folder_id):
|
|
25
|
+
if spinner:
|
|
26
|
+
spinner.stop()
|
|
27
|
+
click.echo("❌ No personal workspace found for user")
|
|
28
|
+
click.get_current_context().exit(1)
|
|
29
|
+
return feed_id, folder_id
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import urllib.parse
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
from ..spinner import Spinner
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_release_info(
|
|
12
|
+
base_url: str,
|
|
13
|
+
token: str,
|
|
14
|
+
package_name: str,
|
|
15
|
+
folder_id: str,
|
|
16
|
+
spinner: Optional[Spinner] = None,
|
|
17
|
+
) -> None | tuple[Any, Any] | tuple[None, None]:
|
|
18
|
+
headers = {
|
|
19
|
+
"Authorization": f"Bearer {token}",
|
|
20
|
+
"x-uipath-organizationunitid": str(folder_id),
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
release_url = f"{base_url}/orchestrator_/odata/Releases/UiPath.Server.Configuration.OData.ListReleases?$select=Id,Key&$top=1&$filter=(contains(Name,%27{urllib.parse.quote(package_name)}%27))&$orderby=Name%20asc"
|
|
24
|
+
response = requests.get(release_url, headers=headers)
|
|
25
|
+
if response.status_code == 200:
|
|
26
|
+
try:
|
|
27
|
+
data = json.loads(response.text)
|
|
28
|
+
release_id = data["value"][0]["Id"]
|
|
29
|
+
release_key = data["value"][0]["Key"]
|
|
30
|
+
return release_id, release_key
|
|
31
|
+
except KeyError:
|
|
32
|
+
if spinner:
|
|
33
|
+
spinner.stop()
|
|
34
|
+
click.echo("\n⚠️ Warning: Failed to deserialize release data")
|
|
35
|
+
return None, None
|
|
36
|
+
except IndexError:
|
|
37
|
+
if spinner:
|
|
38
|
+
spinner.stop()
|
|
39
|
+
click.echo(
|
|
40
|
+
"\n❌ Process not found in your workspace. Try publishing it first."
|
|
41
|
+
)
|
|
42
|
+
click.get_current_context().exit(1)
|
|
43
|
+
|
|
44
|
+
else:
|
|
45
|
+
if spinner:
|
|
46
|
+
spinner.stop()
|
|
47
|
+
click.echo("\n⚠️ Warning: Failed to fetch release info")
|
|
48
|
+
click.echo(f"Status code: {response.status_code}")
|
|
49
|
+
return None, None
|
uipath/_cli/cli_auth.py
CHANGED
|
@@ -12,6 +12,7 @@ from ._auth._oidc_utils import get_auth_config, get_auth_url
|
|
|
12
12
|
from ._auth._portal_service import PortalService, select_tenant
|
|
13
13
|
from ._auth._utils import update_auth_file, update_env_file
|
|
14
14
|
from ._utils._common import environment_options
|
|
15
|
+
from .spinner import Spinner
|
|
15
16
|
|
|
16
17
|
load_dotenv()
|
|
17
18
|
|
|
@@ -37,7 +38,7 @@ def set_port():
|
|
|
37
38
|
if is_port_in_use(port_option_two):
|
|
38
39
|
if is_port_in_use(port_option_three):
|
|
39
40
|
raise RuntimeError(
|
|
40
|
-
"All configured ports are in use. Please close applications using ports or configure different ports."
|
|
41
|
+
"❌ All configured ports are in use. Please close applications using ports or configure different ports."
|
|
41
42
|
)
|
|
42
43
|
else:
|
|
43
44
|
port = port_option_three
|
|
@@ -56,6 +57,8 @@ def set_port():
|
|
|
56
57
|
@environment_options
|
|
57
58
|
def auth(domain="alpha"):
|
|
58
59
|
"""Authenticate with UiPath Cloud Platform."""
|
|
60
|
+
spinner = Spinner("Authenticating with UiPath...")
|
|
61
|
+
spinner.start()
|
|
59
62
|
portal_service = PortalService(domain)
|
|
60
63
|
if (
|
|
61
64
|
os.getenv("UIPATH_URL")
|
|
@@ -64,9 +67,13 @@ def auth(domain="alpha"):
|
|
|
64
67
|
):
|
|
65
68
|
try:
|
|
66
69
|
portal_service.ensure_valid_token()
|
|
67
|
-
|
|
70
|
+
spinner.stop()
|
|
71
|
+
click.echo(
|
|
72
|
+
click.style("✓ ", fg="green", bold=True) + "Authentication successful"
|
|
73
|
+
)
|
|
68
74
|
return
|
|
69
75
|
except Exception:
|
|
76
|
+
spinner.stop()
|
|
70
77
|
click.echo(
|
|
71
78
|
"Authentication not found or expired. Please authenticate again."
|
|
72
79
|
)
|
|
@@ -75,23 +82,26 @@ def auth(domain="alpha"):
|
|
|
75
82
|
|
|
76
83
|
webbrowser.open(auth_url, 1)
|
|
77
84
|
auth_config = get_auth_config()
|
|
78
|
-
|
|
85
|
+
spinner.stop()
|
|
79
86
|
print(
|
|
80
87
|
"If a browser window did not open, please open the following URL in your browser:"
|
|
81
88
|
)
|
|
82
89
|
print(auth_url)
|
|
90
|
+
|
|
83
91
|
server = HTTPSServer(port=auth_config["port"])
|
|
84
92
|
token_data = server.start(state, code_verifier, domain)
|
|
85
|
-
try:
|
|
86
|
-
if token_data:
|
|
87
|
-
portal_service.update_token_data(token_data)
|
|
88
|
-
update_auth_file(token_data)
|
|
89
|
-
access_token = token_data["access_token"]
|
|
90
|
-
update_env_file({"UIPATH_ACCESS_TOKEN": access_token})
|
|
91
93
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
if token_data:
|
|
95
|
+
portal_service.update_token_data(token_data)
|
|
96
|
+
update_auth_file(token_data)
|
|
97
|
+
access_token = token_data["access_token"]
|
|
98
|
+
update_env_file({"UIPATH_ACCESS_TOKEN": access_token})
|
|
99
|
+
|
|
100
|
+
tenants_and_organizations = portal_service.get_tenants_and_organizations()
|
|
101
|
+
select_tenant(domain, tenants_and_organizations)
|
|
102
|
+
click.echo(
|
|
103
|
+
click.style("✓ ", fg="green", bold=True)
|
|
104
|
+
+ "Authentication completed successfully!"
|
|
105
|
+
)
|
|
106
|
+
else:
|
|
107
|
+
click.echo("❌ Authentication failed")
|
uipath/_cli/cli_init.py
CHANGED
|
@@ -11,6 +11,7 @@ import click
|
|
|
11
11
|
from ._utils._input_args import generate_args
|
|
12
12
|
from ._utils._parse_ast import generate_bindings_json
|
|
13
13
|
from .middlewares import Middlewares
|
|
14
|
+
from .spinner import Spinner
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
def generate_env_file(target_directory):
|
|
@@ -51,6 +52,7 @@ def get_user_script(directory: str, entrypoint: Optional[str] = None) -> Optiona
|
|
|
51
52
|
@click.argument("entrypoint", required=False, default=None)
|
|
52
53
|
def init(entrypoint: str) -> None:
|
|
53
54
|
"""Initialize a uipath.json configuration file for the script."""
|
|
55
|
+
spinner = Spinner("Initializing UiPath project...")
|
|
54
56
|
current_directory = os.getcwd()
|
|
55
57
|
generate_env_file(current_directory)
|
|
56
58
|
|
|
@@ -67,12 +69,11 @@ def init(entrypoint: str) -> None:
|
|
|
67
69
|
|
|
68
70
|
if not result.should_continue:
|
|
69
71
|
return
|
|
70
|
-
|
|
71
72
|
script_path = get_user_script(current_directory, entrypoint=entrypoint)
|
|
72
73
|
|
|
73
74
|
if not script_path:
|
|
74
75
|
click.get_current_context().exit(1)
|
|
75
|
-
|
|
76
|
+
spinner.start()
|
|
76
77
|
try:
|
|
77
78
|
args = generate_args(script_path)
|
|
78
79
|
|
|
@@ -94,21 +95,23 @@ def init(entrypoint: str) -> None:
|
|
|
94
95
|
# Generate bindings JSON based on the script path
|
|
95
96
|
try:
|
|
96
97
|
bindings_data = generate_bindings_json(script_path)
|
|
97
|
-
|
|
98
98
|
# Add bindings to the config data
|
|
99
99
|
config_data["bindings"] = bindings_data
|
|
100
|
-
|
|
101
|
-
click.echo("Bindings generated successfully.")
|
|
102
100
|
except Exception as e:
|
|
103
|
-
click.echo(f"Warning: Could not generate bindings: {str(e)}")
|
|
101
|
+
click.echo(f"⚠️ Warning: Could not generate bindings: {str(e)}")
|
|
104
102
|
|
|
105
103
|
config_path = "uipath.json"
|
|
106
104
|
with open(config_path, "w") as config_file:
|
|
107
105
|
json.dump(config_data, config_file, indent=4)
|
|
108
106
|
|
|
109
|
-
|
|
107
|
+
spinner.stop()
|
|
108
|
+
click.echo(
|
|
109
|
+
click.style("✓ ", fg="green", bold=True)
|
|
110
|
+
+ f"Configuration file {config_path} created successfully."
|
|
111
|
+
)
|
|
110
112
|
|
|
111
113
|
except Exception as e:
|
|
112
|
-
|
|
114
|
+
spinner.stop()
|
|
115
|
+
click.echo(f"❌ Error generating configuration: {str(e)}")
|
|
113
116
|
click.echo(traceback.format_exc())
|
|
114
117
|
click.get_current_context().exit(1)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# type: ignore
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
import requests
|
|
8
|
+
from dotenv import load_dotenv
|
|
9
|
+
|
|
10
|
+
from .spinner import Spinner
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
import tomllib
|
|
14
|
+
except ImportError:
|
|
15
|
+
import tomli as tomllib
|
|
16
|
+
|
|
17
|
+
from ._utils._common import get_env_vars
|
|
18
|
+
from ._utils._folders import get_personal_workspace_info
|
|
19
|
+
from ._utils._processes import get_release_info
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
load_dotenv()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _read_project_name() -> str:
|
|
26
|
+
current_path = os.getcwd()
|
|
27
|
+
toml_path = os.path.join(current_path, "pyproject.toml")
|
|
28
|
+
if not os.path.isfile(toml_path):
|
|
29
|
+
raise Exception("pyproject.toml not found")
|
|
30
|
+
|
|
31
|
+
with open(toml_path, "rb") as f:
|
|
32
|
+
content = tomllib.load(f)
|
|
33
|
+
if "project" not in content:
|
|
34
|
+
raise Exception("pyproject.toml is missing the required field: project")
|
|
35
|
+
if "name" not in content["project"]:
|
|
36
|
+
raise Exception(
|
|
37
|
+
"pyproject.toml is missing the required field: project.name"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
return content["project"]["name"]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@click.command()
|
|
44
|
+
@click.argument("entrypoint", required=False)
|
|
45
|
+
@click.argument("input", required=False, default="{}")
|
|
46
|
+
def invoke(entrypoint: Optional[str], input: Optional[str]) -> None:
|
|
47
|
+
"""Invoke a remote agent with JSON input."""
|
|
48
|
+
spinner = Spinner("Starting job...")
|
|
49
|
+
spinner.start()
|
|
50
|
+
|
|
51
|
+
current_path = os.getcwd()
|
|
52
|
+
load_dotenv(os.path.join(current_path, ".env"), override=True)
|
|
53
|
+
[base_url, token] = get_env_vars(spinner)
|
|
54
|
+
|
|
55
|
+
url = f"{base_url}/orchestrator_/odata/Jobs/UiPath.Server.Configuration.OData.StartJobs"
|
|
56
|
+
_, personal_workspace_folder_id = get_personal_workspace_info(
|
|
57
|
+
base_url, token, spinner
|
|
58
|
+
)
|
|
59
|
+
project_name = _read_project_name()
|
|
60
|
+
|
|
61
|
+
_, release_key = get_release_info(
|
|
62
|
+
base_url, token, project_name, personal_workspace_folder_id, spinner
|
|
63
|
+
)
|
|
64
|
+
payload = {
|
|
65
|
+
"StartInfo": {
|
|
66
|
+
"ReleaseKey": str(release_key),
|
|
67
|
+
"RunAsMe": True,
|
|
68
|
+
"InputArguments": input,
|
|
69
|
+
"EntryPointPath": entrypoint,
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
headers = {
|
|
73
|
+
"Authorization": f"Bearer {token}",
|
|
74
|
+
"x-uipath-organizationunitid": str(personal_workspace_folder_id),
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
response = requests.post(url, json=payload, headers=headers)
|
|
78
|
+
spinner.stop()
|
|
79
|
+
|
|
80
|
+
if response.status_code == 201:
|
|
81
|
+
job_key = None
|
|
82
|
+
try:
|
|
83
|
+
job_key = response.json()["value"][0]["Key"]
|
|
84
|
+
except KeyError:
|
|
85
|
+
click.echo("Error: Failed to get job key from response")
|
|
86
|
+
click.Abort()
|
|
87
|
+
if job_key:
|
|
88
|
+
job_url = f"{base_url}/orchestrator_/jobs(sidepanel:sidepanel/jobs/{job_key}/details)?fid={personal_workspace_folder_id}"
|
|
89
|
+
click.echo("\n✨ Job started successfully!")
|
|
90
|
+
click.echo(
|
|
91
|
+
"\n🔗 Monitor your job here: "
|
|
92
|
+
+ click.style(
|
|
93
|
+
f"\u001b]8;;{job_url}\u001b\\{job_url}\u001b]8;;\u001b\\",
|
|
94
|
+
fg="bright_blue",
|
|
95
|
+
bold=True,
|
|
96
|
+
)
|
|
97
|
+
+ "\n"
|
|
98
|
+
)
|
|
99
|
+
else:
|
|
100
|
+
click.echo(f"\n❌ Error starting job: {response.text}")
|
|
101
|
+
click.Abort()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
if __name__ == "__main__":
|
|
105
|
+
invoke()
|
uipath/_cli/cli_new.py
CHANGED
|
@@ -6,6 +6,7 @@ import traceback
|
|
|
6
6
|
import click
|
|
7
7
|
|
|
8
8
|
from .middlewares import Middlewares
|
|
9
|
+
from .spinner import Spinner
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def generate_script(target_directory):
|
|
@@ -37,6 +38,7 @@ requires-python = ">=3.9"
|
|
|
37
38
|
@click.command()
|
|
38
39
|
@click.argument("name", type=str, default="")
|
|
39
40
|
def new(name: str):
|
|
41
|
+
spinner = Spinner("Creating new project...")
|
|
40
42
|
directory = os.getcwd()
|
|
41
43
|
|
|
42
44
|
if not name:
|
|
@@ -44,12 +46,15 @@ def new(name: str):
|
|
|
44
46
|
"Please specify a name for your project\n`uipath new hello-world`"
|
|
45
47
|
)
|
|
46
48
|
|
|
47
|
-
click.echo(
|
|
49
|
+
click.echo(
|
|
50
|
+
click.style("✓ ", fg="green", bold=True)
|
|
51
|
+
+ f"Initializing project {name} in current directory.."
|
|
52
|
+
)
|
|
48
53
|
|
|
49
54
|
result = Middlewares.next("new", name)
|
|
50
55
|
|
|
51
56
|
if result.error_message:
|
|
52
|
-
click.echo(result.error_message)
|
|
57
|
+
click.echo("❌ " + result.error_message)
|
|
53
58
|
if result.should_include_stacktrace:
|
|
54
59
|
click.echo(traceback.format_exc())
|
|
55
60
|
click.get_current_context().exit(1)
|
|
@@ -61,13 +66,14 @@ def new(name: str):
|
|
|
61
66
|
return
|
|
62
67
|
|
|
63
68
|
generate_script(directory)
|
|
64
|
-
click.echo("Created main.py file
|
|
69
|
+
click.echo(click.style("✓ ", fg="green", bold=True) + "Created main.py file")
|
|
65
70
|
generate_pyproject(directory, name)
|
|
66
|
-
click.echo("Created pyproject.toml file
|
|
67
|
-
|
|
71
|
+
click.echo(click.style("✓ ", fg="green", bold=True) + "Created pyproject.toml file")
|
|
72
|
+
spinner.start()
|
|
68
73
|
ctx = click.get_current_context()
|
|
69
74
|
init_cmd = ctx.parent.command.get_command(ctx, "init")
|
|
70
75
|
ctx.invoke(init_cmd)
|
|
76
|
+
spinner.stop()
|
|
71
77
|
|
|
72
78
|
click.echo("""` uipath run main.py '{"message": "Hello World!"}' `""")
|
|
73
79
|
|
uipath/_cli/cli_pack.py
CHANGED
|
@@ -12,6 +12,8 @@ try:
|
|
|
12
12
|
except ImportError:
|
|
13
13
|
import tomli as tomllib
|
|
14
14
|
|
|
15
|
+
from .spinner import Spinner
|
|
16
|
+
|
|
15
17
|
schema = "https://cloud.uipath.com/draft/2024-12/entry-point"
|
|
16
18
|
|
|
17
19
|
|
|
@@ -328,7 +330,7 @@ def read_toml_project(file_path: str) -> dict[str, any]:
|
|
|
328
330
|
def get_project_version(directory):
|
|
329
331
|
toml_path = os.path.join(directory, "pyproject.toml")
|
|
330
332
|
if not os.path.exists(toml_path):
|
|
331
|
-
click.echo("Warning: No pyproject.toml found. Using default version 0.0.1")
|
|
333
|
+
click.echo("⚠️ Warning: No pyproject.toml found. Using default version 0.0.1")
|
|
332
334
|
return "0.0.1"
|
|
333
335
|
toml_data = read_toml_project(toml_path)
|
|
334
336
|
return toml_data["version"]
|
|
@@ -341,38 +343,50 @@ def pack(root):
|
|
|
341
343
|
|
|
342
344
|
while not os.path.isfile(os.path.join(root, "uipath.json")):
|
|
343
345
|
click.echo(
|
|
344
|
-
"uipath.json not found. Please run `uipath init` in the project directory."
|
|
346
|
+
"❌ uipath.json not found. Please run `uipath init` in the project directory."
|
|
345
347
|
)
|
|
346
348
|
return
|
|
347
349
|
config = check_config(root)
|
|
348
350
|
if not config["project_name"] or config["project_name"].strip() == "":
|
|
349
|
-
raise Exception("Project name cannot be empty")
|
|
351
|
+
raise Exception("❌ Project name cannot be empty")
|
|
350
352
|
|
|
351
353
|
if not config["description"] or config["description"].strip() == "":
|
|
352
|
-
raise Exception("Project description cannot be empty")
|
|
354
|
+
raise Exception("❌ Project description cannot be empty")
|
|
353
355
|
|
|
354
356
|
if not config["authors"] or config["authors"].strip() == "":
|
|
355
|
-
raise Exception("Project authors cannot be empty")
|
|
357
|
+
raise Exception("❌ Project authors cannot be empty")
|
|
356
358
|
|
|
357
359
|
invalid_chars = ["&", "<", ">", '"', "'", ";"]
|
|
358
360
|
for char in invalid_chars:
|
|
359
361
|
if char in config["project_name"]:
|
|
360
|
-
raise Exception(f"Project name contains invalid character: '{char}'")
|
|
362
|
+
raise Exception(f"❌ Project name contains invalid character: '{char}'")
|
|
361
363
|
|
|
362
364
|
for char in invalid_chars:
|
|
363
365
|
if char in config["description"]:
|
|
364
|
-
raise Exception(
|
|
365
|
-
|
|
366
|
+
raise Exception(
|
|
367
|
+
f"❌ Project description contains invalid character: '{char}'"
|
|
368
|
+
)
|
|
369
|
+
spinner = Spinner(
|
|
366
370
|
f"Packaging project {config['project_name']}:{version or config['version']} description {config['description']} authored by {config['authors']}"
|
|
367
371
|
)
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
372
|
+
try:
|
|
373
|
+
spinner.start()
|
|
374
|
+
pack_fn(
|
|
375
|
+
config["project_name"],
|
|
376
|
+
config["description"],
|
|
377
|
+
config["entryPoints"],
|
|
378
|
+
version or config["version"],
|
|
379
|
+
config["authors"],
|
|
380
|
+
root,
|
|
381
|
+
)
|
|
382
|
+
spinner.stop()
|
|
383
|
+
click.echo(
|
|
384
|
+
click.style("✓ ", fg="green", bold=True) + "Package created successfully!"
|
|
385
|
+
)
|
|
386
|
+
except Exception as e:
|
|
387
|
+
spinner.stop()
|
|
388
|
+
click.echo(f"\n❌ Error: {str(e)}")
|
|
389
|
+
click.Abort()
|
|
376
390
|
|
|
377
391
|
|
|
378
392
|
if __name__ == "__main__":
|
uipath/_cli/cli_publish.py
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
# type: ignore
|
|
2
|
+
import json
|
|
2
3
|
import os
|
|
3
4
|
|
|
4
5
|
import click
|
|
5
6
|
import requests
|
|
6
7
|
from dotenv import load_dotenv
|
|
7
8
|
|
|
9
|
+
from ._utils._common import get_env_vars
|
|
10
|
+
from ._utils._folders import get_personal_workspace_info
|
|
11
|
+
from ._utils._processes import get_release_info
|
|
12
|
+
from .spinner import Spinner
|
|
13
|
+
|
|
8
14
|
|
|
9
15
|
def get_most_recent_package():
|
|
10
16
|
nupkg_files = [f for f in os.listdir(".uipath") if f.endswith(".nupkg")]
|
|
@@ -24,20 +30,6 @@ def get_most_recent_package():
|
|
|
24
30
|
return nupkg_files_with_time[0][0]
|
|
25
31
|
|
|
26
32
|
|
|
27
|
-
def get_env_vars():
|
|
28
|
-
base_url = os.environ.get("UIPATH_URL")
|
|
29
|
-
token = os.environ.get("UIPATH_ACCESS_TOKEN")
|
|
30
|
-
|
|
31
|
-
if not all([base_url, token]):
|
|
32
|
-
click.echo(
|
|
33
|
-
"Missing required environment variables. Please check your .env file contains:"
|
|
34
|
-
)
|
|
35
|
-
click.echo("UIPATH_URL, UIPATH_ACCESS_TOKEN")
|
|
36
|
-
raise click.Abort("Missing environment variables")
|
|
37
|
-
|
|
38
|
-
return [base_url, token]
|
|
39
|
-
|
|
40
|
-
|
|
41
33
|
@click.command()
|
|
42
34
|
@click.option(
|
|
43
35
|
"--tenant",
|
|
@@ -54,6 +46,7 @@ def get_env_vars():
|
|
|
54
46
|
help="Whether to publish to the personal workspace",
|
|
55
47
|
)
|
|
56
48
|
def publish(feed):
|
|
49
|
+
spinner = Spinner()
|
|
57
50
|
current_path = os.getcwd()
|
|
58
51
|
load_dotenv(os.path.join(current_path, ".env"), override=True)
|
|
59
52
|
if feed is None:
|
|
@@ -63,42 +56,32 @@ def publish(feed):
|
|
|
63
56
|
feed_idx = click.prompt("Select feed", type=int)
|
|
64
57
|
feed = "tenant" if feed_idx == 0 else "personal"
|
|
65
58
|
click.echo(f"Selected feed: {feed}")
|
|
59
|
+
|
|
66
60
|
os.makedirs(".uipath", exist_ok=True)
|
|
67
61
|
|
|
68
62
|
# Find most recent .nupkg file in .uipath directory
|
|
69
63
|
most_recent = get_most_recent_package()
|
|
70
64
|
|
|
71
65
|
if not most_recent:
|
|
72
|
-
|
|
66
|
+
spinner.stop()
|
|
67
|
+
click.echo("❌ Error: No package files found in .uipath directory")
|
|
73
68
|
raise click.Abort()
|
|
74
|
-
|
|
69
|
+
|
|
70
|
+
spinner.start(f"Publishing most recent package: {most_recent}")
|
|
75
71
|
|
|
76
72
|
package_to_publish_path = os.path.join(".uipath", most_recent)
|
|
77
73
|
|
|
78
|
-
[base_url, token] = get_env_vars()
|
|
74
|
+
[base_url, token] = get_env_vars(spinner)
|
|
79
75
|
|
|
80
76
|
url = f"{base_url}/orchestrator_/odata/Processes/UiPath.Server.Configuration.OData.UploadPackage()"
|
|
81
77
|
|
|
82
78
|
if feed == "personal":
|
|
83
79
|
# Get current user extended info to get personal workspace ID
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
user_url, headers={"Authorization": f"Bearer {token}"}
|
|
80
|
+
personal_workspace_feed_id, personal_workspace_folder_id = (
|
|
81
|
+
get_personal_workspace_info(base_url, token, spinner)
|
|
87
82
|
)
|
|
88
83
|
|
|
89
|
-
|
|
90
|
-
click.echo("Failed to get user info")
|
|
91
|
-
click.echo(f"Response: {user_response.text}")
|
|
92
|
-
raise click.Abort()
|
|
93
|
-
|
|
94
|
-
user_data = user_response.json()
|
|
95
|
-
personal_workspace_id = user_data.get("PersonalWorskpaceFeedId")
|
|
96
|
-
|
|
97
|
-
if not personal_workspace_id:
|
|
98
|
-
click.echo("No personal workspace found for user")
|
|
99
|
-
raise click.Abort()
|
|
100
|
-
|
|
101
|
-
url = url + "?feedId=" + personal_workspace_id
|
|
84
|
+
url = url + "?feedId=" + personal_workspace_feed_id
|
|
102
85
|
|
|
103
86
|
headers = {"Authorization": f"Bearer {token}"}
|
|
104
87
|
|
|
@@ -106,8 +89,38 @@ def publish(feed):
|
|
|
106
89
|
files = {"file": (package_to_publish_path, f, "application/octet-stream")}
|
|
107
90
|
response = requests.post(url, headers=headers, files=files)
|
|
108
91
|
|
|
92
|
+
spinner.stop()
|
|
93
|
+
|
|
109
94
|
if response.status_code == 200:
|
|
110
|
-
click.echo(
|
|
95
|
+
click.echo(
|
|
96
|
+
click.style("✓ ", fg="green", bold=True) + "Package published successfully!"
|
|
97
|
+
)
|
|
98
|
+
if feed == "personal":
|
|
99
|
+
try:
|
|
100
|
+
data = json.loads(response.text)
|
|
101
|
+
package_name = json.loads(data["value"][0]["Body"])["Id"]
|
|
102
|
+
except json.decoder.JSONDecodeError:
|
|
103
|
+
click.echo("⚠️ Warning: Failed to deserialize package name")
|
|
104
|
+
raise click.Abort() from json.decoder.JSONDecodeError
|
|
105
|
+
release_id, _ = get_release_info(
|
|
106
|
+
base_url, token, package_name, personal_workspace_feed_id, spinner
|
|
107
|
+
)
|
|
108
|
+
if release_id:
|
|
109
|
+
process_url = f"{base_url}/orchestrator_/processes/{release_id}/edit?fid={personal_workspace_folder_id}"
|
|
110
|
+
click.echo(
|
|
111
|
+
"\n🔧 Configure your process: "
|
|
112
|
+
+ click.style(
|
|
113
|
+
f"\u001b]8;;{process_url}\u001b\\{process_url}\u001b]8;;\u001b\\",
|
|
114
|
+
fg="bright_blue",
|
|
115
|
+
bold=True,
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
click.echo(
|
|
119
|
+
"\n💡 Use the link above to configure any environment variables\n"
|
|
120
|
+
)
|
|
121
|
+
else:
|
|
122
|
+
click.echo("⚠️ Warning: Failed to compose process url")
|
|
111
123
|
else:
|
|
112
|
-
click.echo(f"Failed to publish package. Status code: {response.status_code}")
|
|
113
|
-
|
|
124
|
+
click.echo(f"❌ Failed to publish package. Status code: {response.status_code}")
|
|
125
|
+
if response.text:
|
|
126
|
+
click.echo(response.text)
|
uipath/_cli/spinner.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
from rich.live import Live
|
|
5
|
+
from rich.spinner import Spinner as RichSpinner
|
|
6
|
+
from rich.text import Text
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Spinner:
|
|
10
|
+
"""A simple spinner class for terminal output using Rich with pinned spinner."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, message: str = ""):
|
|
13
|
+
self.message = message
|
|
14
|
+
self._console = Console()
|
|
15
|
+
self._live: Optional[Live] = None
|
|
16
|
+
self._spinner = RichSpinner("dots", Text(message))
|
|
17
|
+
|
|
18
|
+
def start(self, message: str = "") -> None:
|
|
19
|
+
"""Start the spinner animation.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
message: Optional new message to display.
|
|
23
|
+
"""
|
|
24
|
+
if message:
|
|
25
|
+
self.message = message
|
|
26
|
+
self._spinner.text = Text(message)
|
|
27
|
+
|
|
28
|
+
self._live = Live(
|
|
29
|
+
self._spinner,
|
|
30
|
+
console=self._console,
|
|
31
|
+
refresh_per_second=10,
|
|
32
|
+
transient=False,
|
|
33
|
+
auto_refresh=True,
|
|
34
|
+
)
|
|
35
|
+
self._live.start()
|
|
36
|
+
|
|
37
|
+
def stop(self) -> None:
|
|
38
|
+
"""Stop the spinner animation and clean up."""
|
|
39
|
+
if self._live:
|
|
40
|
+
self._live.stop()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: uipath
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.21
|
|
4
4
|
Summary: Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools.
|
|
5
5
|
Project-URL: Homepage, https://uipath.com
|
|
6
6
|
Project-URL: Repository, https://github.com/UiPath/uipath-python
|
|
@@ -21,9 +21,9 @@ Requires-Dist: pydantic>=2.11.1
|
|
|
21
21
|
Requires-Dist: pytest-asyncio>=0.25.3
|
|
22
22
|
Requires-Dist: python-dotenv>=1.0.1
|
|
23
23
|
Requires-Dist: requests>=2.32.3
|
|
24
|
+
Requires-Dist: rich>=13.0.0
|
|
24
25
|
Requires-Dist: tenacity>=9.0.0
|
|
25
26
|
Requires-Dist: tomli>=2.2.1
|
|
26
|
-
Requires-Dist: twine>=6.1.0
|
|
27
27
|
Requires-Dist: types-requests>=2.32.0.20250306
|
|
28
28
|
Provides-Extra: langchain
|
|
29
29
|
Requires-Dist: uipath-langchain<0.1.0,>=0.0.88; extra == 'langchain'
|
|
@@ -5,16 +5,18 @@ uipath/_folder_context.py,sha256=oxX7o8ilU9sA_vEKLr2sZZCHnvRwJ0_as-LOMc4hxc4,190
|
|
|
5
5
|
uipath/_uipath.py,sha256=D9UWyRInN2Q8HFEQtYaYzT3DCZ3tW_OCBs_4RRqRVuY,2795
|
|
6
6
|
uipath/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
uipath/_cli/README.md,sha256=GLtCfbeIKZKNnGTCsfSVqRQ27V1btT1i2bSAyW_xZl4,474
|
|
8
|
-
uipath/_cli/__init__.py,sha256=
|
|
9
|
-
uipath/_cli/cli_auth.py,sha256=
|
|
8
|
+
uipath/_cli/__init__.py,sha256=hdwRcD6wsvenqYTU5RImu4enh-RRmkkQ8lgYIC60z1E,1655
|
|
9
|
+
uipath/_cli/cli_auth.py,sha256=y_F5Rm3Juxy5oKn0nyPtMidNf6awywOw9VCLUvnDrCc,3420
|
|
10
10
|
uipath/_cli/cli_deploy.py,sha256=h8qwJkXnW6JURsg4YcocJInGA4dwkl4CZkpT1Cn9A3c,268
|
|
11
|
-
uipath/_cli/cli_init.py,sha256=
|
|
12
|
-
uipath/_cli/
|
|
13
|
-
uipath/_cli/
|
|
14
|
-
uipath/_cli/
|
|
11
|
+
uipath/_cli/cli_init.py,sha256=75wHT0A4LuZFjlBQ8F34958Aq_o_KJdOlTQfwvFUqo4,3916
|
|
12
|
+
uipath/_cli/cli_invoke.py,sha256=evbmP_EAxYbKg-tPB7MKNGJqwNIT3V-GN_COqvf2WyQ,3197
|
|
13
|
+
uipath/_cli/cli_new.py,sha256=ViGqmheiuH5KLKUs0ECtv-xuxH4HtMxfb8bV8D2673M,2183
|
|
14
|
+
uipath/_cli/cli_pack.py,sha256=V5fm3XPJ_vtJ2lrLoQU_jDgAKVe9lsipeB6n_7Nc4WM,13955
|
|
15
|
+
uipath/_cli/cli_publish.py,sha256=X5nMGpMMxrWrm8Kb8p7sYlUcO-K5_roLxVu037HQvQI,4309
|
|
15
16
|
uipath/_cli/cli_run.py,sha256=B5L7fE2IqysIEcweedU8GEy7ekMGwpeRagYBCB_cdQI,4597
|
|
16
17
|
uipath/_cli/middlewares.py,sha256=IiJgjsqrJVKSXx4RcIKHWoH-SqWqpHPbhzkQEybmAos,3937
|
|
17
|
-
uipath/_cli/
|
|
18
|
+
uipath/_cli/spinner.py,sha256=bS-U_HA5yne11ejUERu7CQoXmWdabUD2bm62EfEdV8M,1107
|
|
19
|
+
uipath/_cli/_auth/_auth_server.py,sha256=RAOk15KE6_3FzcLuSz1D_BArf2GJxvhJQ08x_VnL3k4,7100
|
|
18
20
|
uipath/_cli/_auth/_models.py,sha256=sYMCfvmprIqnZxStlD_Dxx2bcxgn0Ri4D7uwemwkcNg,948
|
|
19
21
|
uipath/_cli/_auth/_oidc_utils.py,sha256=WaX9jDlXrlX6yD8i8gsocV8ngjaT72Xd1tvsZMmSbco,2127
|
|
20
22
|
uipath/_cli/_auth/_portal_service.py,sha256=YPL-_Z9oVK-VjQ67m31-t3y3uvNnPl_qubU-m4zlHMU,6042
|
|
@@ -31,9 +33,11 @@ uipath/_cli/_templates/.rels.template,sha256=-fTcw7OA1AcymHr0LzBqbMAAtzZTRXLTNa_
|
|
|
31
33
|
uipath/_cli/_templates/[Content_Types].xml.template,sha256=bYsKDz31PkIF9QksjgAY_bqm57YC8U_owsZeNZAiBxQ,584
|
|
32
34
|
uipath/_cli/_templates/main.py.template,sha256=QB62qX5HKDbW4lFskxj7h9uuxBITnTWqu_DE6asCwcU,476
|
|
33
35
|
uipath/_cli/_templates/package.nuspec.template,sha256=YZyLc-u_EsmIoKf42JsLQ55OGeFmb8VkIU2VF7DFbtw,359
|
|
34
|
-
uipath/_cli/_utils/_common.py,sha256=
|
|
36
|
+
uipath/_cli/_utils/_common.py,sha256=h0-lvaAzz-4iM7WuEqZhlTo5QadBpsQyAdlggx73-PA,1123
|
|
37
|
+
uipath/_cli/_utils/_folders.py,sha256=X1KjB8itJsMceW7eZu9PiKx4hEF5kGRrjfNdrH9hs28,1028
|
|
35
38
|
uipath/_cli/_utils/_input_args.py,sha256=pyQhEcQXHdFHYTVNzvfWp439aii5StojoptnmCv5lfs,4094
|
|
36
39
|
uipath/_cli/_utils/_parse_ast.py,sha256=3XVjnhJNnSfjXlitct91VOtqSl0l-sqDpoWww28mMc0,20663
|
|
40
|
+
uipath/_cli/_utils/_processes.py,sha256=kcsMNlo8yvWwKSBxumAPFDPm67Od1ZpZglfNgvoIBso,1602
|
|
37
41
|
uipath/_services/__init__.py,sha256=VPbwLDsvN26nWZgvR-8_-tc3i0rk5doqjTJbSrK0nN4,818
|
|
38
42
|
uipath/_services/_base_service.py,sha256=3YClCoZBkVQGNJZGy-4NTk-HGsGA61XtwVQFYv9mwWk,7955
|
|
39
43
|
uipath/_services/actions_service.py,sha256=tOu3EwzLrDGXHwqzUsgKI4tbhJHDlumleGywnKH1sAs,15839
|
|
@@ -73,8 +77,8 @@ uipath/tracing/__init__.py,sha256=GimSzv6qkCOlHOG1WtjYKJsZqcXpA28IgoXfR33JhiA,13
|
|
|
73
77
|
uipath/tracing/_otel_exporters.py,sha256=x0PDPmDKJcxashsuehVsSsqBCzRr6WsNFaq_3_HS5F0,3014
|
|
74
78
|
uipath/tracing/_traced.py,sha256=9nEjFjGuxPlJ_4OXoClJ79xcbFK6C8iyI03kQQSDaJg,14834
|
|
75
79
|
uipath/tracing/_utils.py,sha256=5SwsTGpHkIouXBndw-u8eCLnN4p7LM8DsTCCuf2jJgs,10165
|
|
76
|
-
uipath-2.0.
|
|
77
|
-
uipath-2.0.
|
|
78
|
-
uipath-2.0.
|
|
79
|
-
uipath-2.0.
|
|
80
|
-
uipath-2.0.
|
|
80
|
+
uipath-2.0.21.dist-info/METADATA,sha256=3-p6Zhe0gk6mqRivPExTYKPothc746T5U6nwzFST2nQ,6106
|
|
81
|
+
uipath-2.0.21.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
82
|
+
uipath-2.0.21.dist-info/entry_points.txt,sha256=9C2_29U6Oq1ExFu7usihR-dnfIVNSKc-0EFbh0rskB4,43
|
|
83
|
+
uipath-2.0.21.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
|
|
84
|
+
uipath-2.0.21.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|