uipath 2.0.20__py3-none-any.whl → 2.0.22__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 CHANGED
@@ -6,15 +6,25 @@ 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
12
13
  from .cli_run import run as run # type: ignore
13
14
 
14
15
 
16
+ def _get_safe_version() -> str:
17
+ """Get the version of the uipath package."""
18
+ try:
19
+ version = importlib.metadata.version("uipath")
20
+ return version
21
+ except importlib.metadata.PackageNotFoundError:
22
+ return "unknown"
23
+
24
+
15
25
  @click.group(invoke_without_command=True)
16
26
  @click.version_option(
17
- importlib.metadata.version("uipath"),
27
+ _get_safe_version(),
18
28
  prog_name="uipath",
19
29
  message="%(prog)s version %(version)s",
20
30
  )
@@ -52,3 +62,4 @@ cli.add_command(publish)
52
62
  cli.add_command(run)
53
63
  cli.add_command(deploy)
54
64
  cli.add_command(auth)
65
+ 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
- print("Received authentication information")
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
- print("Process interrupted by user")
183
+ click.echo("Process interrupted by user")
183
184
  finally:
184
185
  self.stop()
185
186
 
@@ -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
- click.echo("Authentication successful")
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
- tenants_and_organizations = portal_service.get_tenants_and_organizations()
93
- select_tenant(domain, tenants_and_organizations)
94
- else:
95
- click.echo("Authentication failed")
96
- except Exception as e:
97
- click.echo(f"Authentication failed: {e}")
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
- click.echo(f"Configuration file {config_path} created successfully.")
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
- click.echo(f"Error generating configuration: {str(e)}")
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(f"Initializing project {name} in current directory..")
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(f"Project description contains invalid character: '{char}'")
365
- click.echo(
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
- pack_fn(
369
- config["project_name"],
370
- config["description"],
371
- config["entryPoints"],
372
- version or config["version"],
373
- config["authors"],
374
- root,
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__":
@@ -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
- click.echo("Error: No package files found in .uipath directory")
66
+ spinner.stop()
67
+ click.echo("❌ Error: No package files found in .uipath directory")
73
68
  raise click.Abort()
74
- click.echo(f"Publishing most recent package: {most_recent}")
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
- user_url = f"{base_url}/orchestrator_/odata/Users/UiPath.Server.Configuration.OData.GetCurrentUserExtended"
85
- user_response = requests.get(
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
- if user_response.status_code != 200:
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("Package published successfully!")
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
- click.echo(f"Response: {response.text}")
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.20
3
+ Version: 2.0.22
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,6 +21,7 @@ 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
27
  Requires-Dist: types-requests>=2.32.0.20250306
@@ -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=CAyMecQhBBD6s4MzYQNa01bz0TIXXCqkQ5DR2kCb00w,1574
9
- uipath/_cli/cli_auth.py,sha256=ANaYUc2q1t0hDbGBRT3ags6K6Lef_3tyC8Mmc611jow,3141
8
+ uipath/_cli/__init__.py,sha256=vGz3vJHkUvgK9_lKdzqiwwHkge1TCALRiOzGGwyr-8E,1885
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=idoqlGhhzXZKmLAg-3JgZ2fYMrK7qFXYV0EhnNaI3bg,3738
12
- uipath/_cli/cli_new.py,sha256=SP7eWOa5valmCpc8UsOCIezL25euhglB3yJkx-N92W8,1903
13
- uipath/_cli/cli_pack.py,sha256=RqrVn0rzUohDtm3Gtuelhvq7u9QaFwbmI-cK47BVE_c,13533
14
- uipath/_cli/cli_publish.py,sha256=XdPVbnZ-9_jlxyYXz2FyHXGMGNnhJ83yM8KnIeYksVo,3669
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/_auth/_auth_server.py,sha256=vrzrE-hDx8exM5p2sFVoT9vKMblOyFWUvFXz-lTXceY,7077
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=CH25AKRosp6t28iAl9SCK2wgBTRr-Bekq5Fu-Mc3Ui0,547
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.20.dist-info/METADATA,sha256=XCYp3HfCcsfIeTj4eceYATLiVFDyyjl30cT52lXxWKo,6078
77
- uipath-2.0.20.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
78
- uipath-2.0.20.dist-info/entry_points.txt,sha256=9C2_29U6Oq1ExFu7usihR-dnfIVNSKc-0EFbh0rskB4,43
79
- uipath-2.0.20.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
80
- uipath-2.0.20.dist-info/RECORD,,
80
+ uipath-2.0.22.dist-info/METADATA,sha256=BYeiQrKY3VIbTpC-hg6at7SrII1qnYfsULP1DGel6yY,6106
81
+ uipath-2.0.22.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
82
+ uipath-2.0.22.dist-info/entry_points.txt,sha256=9C2_29U6Oq1ExFu7usihR-dnfIVNSKc-0EFbh0rskB4,43
83
+ uipath-2.0.22.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
84
+ uipath-2.0.22.dist-info/RECORD,,