uipath 2.0.0.dev2__py3-none-any.whl → 2.0.1.dev1__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/__init__.py +24 -0
- uipath/_cli/README.md +11 -0
- uipath/_cli/__init__.py +54 -0
- uipath/_cli/_auth/_auth_server.py +165 -0
- uipath/_cli/_auth/_models.py +51 -0
- uipath/_cli/_auth/_oidc_utils.py +69 -0
- uipath/_cli/_auth/_portal_service.py +163 -0
- uipath/_cli/_auth/_utils.py +51 -0
- uipath/_cli/_auth/auth_config.json +6 -0
- uipath/_cli/_auth/index.html +167 -0
- uipath/_cli/_auth/localhost.crt +25 -0
- uipath/_cli/_auth/localhost.key +27 -0
- uipath/_cli/_runtime/_contracts.py +429 -0
- uipath/_cli/_runtime/_logging.py +193 -0
- uipath/_cli/_runtime/_runtime.py +264 -0
- uipath/_cli/_templates/.psmdcp.template +9 -0
- uipath/_cli/_templates/.rels.template +5 -0
- uipath/_cli/_templates/[Content_Types].xml.template +9 -0
- uipath/_cli/_templates/main.py.template +25 -0
- uipath/_cli/_templates/package.nuspec.template +10 -0
- uipath/_cli/_utils/_common.py +24 -0
- uipath/_cli/_utils/_input_args.py +126 -0
- uipath/_cli/_utils/_parse_ast.py +542 -0
- uipath/_cli/cli_auth.py +97 -0
- uipath/_cli/cli_deploy.py +13 -0
- uipath/_cli/cli_init.py +113 -0
- uipath/_cli/cli_new.py +76 -0
- uipath/_cli/cli_pack.py +337 -0
- uipath/_cli/cli_publish.py +113 -0
- uipath/_cli/cli_run.py +133 -0
- uipath/_cli/middlewares.py +113 -0
- uipath/_config.py +6 -0
- uipath/_execution_context.py +83 -0
- uipath/_folder_context.py +62 -0
- uipath/_models/__init__.py +37 -0
- uipath/_models/action_schema.py +26 -0
- uipath/_models/actions.py +64 -0
- uipath/_models/assets.py +48 -0
- uipath/_models/connections.py +51 -0
- uipath/_models/context_grounding.py +18 -0
- uipath/_models/context_grounding_index.py +60 -0
- uipath/_models/exceptions.py +6 -0
- uipath/_models/interrupt_models.py +28 -0
- uipath/_models/job.py +66 -0
- uipath/_models/llm_gateway.py +101 -0
- uipath/_models/processes.py +48 -0
- uipath/_models/queues.py +167 -0
- uipath/_services/__init__.py +26 -0
- uipath/_services/_base_service.py +250 -0
- uipath/_services/actions_service.py +271 -0
- uipath/_services/api_client.py +89 -0
- uipath/_services/assets_service.py +257 -0
- uipath/_services/buckets_service.py +268 -0
- uipath/_services/connections_service.py +185 -0
- uipath/_services/connections_service.pyi +50 -0
- uipath/_services/context_grounding_service.py +402 -0
- uipath/_services/folder_service.py +49 -0
- uipath/_services/jobs_service.py +265 -0
- uipath/_services/llm_gateway_service.py +311 -0
- uipath/_services/processes_service.py +168 -0
- uipath/_services/queues_service.py +314 -0
- uipath/_uipath.py +98 -0
- uipath/_utils/__init__.py +17 -0
- uipath/_utils/_endpoint.py +79 -0
- uipath/_utils/_infer_bindings.py +30 -0
- uipath/_utils/_logs.py +15 -0
- uipath/_utils/_request_override.py +18 -0
- uipath/_utils/_request_spec.py +23 -0
- uipath/_utils/_user_agent.py +16 -0
- uipath/_utils/constants.py +25 -0
- uipath/py.typed +0 -0
- {uipath-2.0.0.dev2.dist-info → uipath-2.0.1.dev1.dist-info}/METADATA +2 -3
- uipath-2.0.1.dev1.dist-info/RECORD +75 -0
- uipath-2.0.0.dev2.dist-info/RECORD +0 -4
- {uipath-2.0.0.dev2.dist-info → uipath-2.0.1.dev1.dist-info}/WHEEL +0 -0
- {uipath-2.0.0.dev2.dist-info → uipath-2.0.1.dev1.dist-info}/entry_points.txt +0 -0
uipath/_cli/cli_init.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# type: ignore
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import traceback
|
|
5
|
+
import uuid
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
from ._utils._input_args import generate_args
|
|
11
|
+
from ._utils._parse_ast import generate_bindings_json
|
|
12
|
+
from .middlewares import Middlewares
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def generate_env_file(target_directory):
|
|
16
|
+
env_path = os.path.join(target_directory, ".env")
|
|
17
|
+
|
|
18
|
+
if not os.path.exists(env_path):
|
|
19
|
+
relative_path = os.path.relpath(env_path, target_directory)
|
|
20
|
+
click.echo(f"Created {relative_path} file.")
|
|
21
|
+
with open(env_path, "w") as f:
|
|
22
|
+
f.write("UIPATH_ACCESS_TOKEN=YOUR_TOKEN_HERE\n")
|
|
23
|
+
f.write("UIPATH_URL=https://alpha.uipath.com/ACCOUNT_NAME/TENANT_NAME\n")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_user_script(directory: str, entrypoint: Optional[str] = None) -> Optional[str]:
|
|
27
|
+
"""Find the Python script to process."""
|
|
28
|
+
if entrypoint:
|
|
29
|
+
script_path = os.path.join(directory, entrypoint)
|
|
30
|
+
if not os.path.isfile(script_path):
|
|
31
|
+
click.echo(f"The {entrypoint} file does not exist in the current directory")
|
|
32
|
+
return None
|
|
33
|
+
return script_path
|
|
34
|
+
|
|
35
|
+
python_files = [f for f in os.listdir(directory) if f.endswith(".py")]
|
|
36
|
+
|
|
37
|
+
if not python_files:
|
|
38
|
+
click.echo("No Python files found in the directory")
|
|
39
|
+
return None
|
|
40
|
+
elif len(python_files) == 1:
|
|
41
|
+
return os.path.join(directory, python_files[0])
|
|
42
|
+
else:
|
|
43
|
+
click.echo(
|
|
44
|
+
"Multiple Python files found in the current directory.\nPlease specify the entrypoint: `uipath init <entrypoint_path>`"
|
|
45
|
+
)
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@click.command()
|
|
50
|
+
@click.argument("entrypoint", required=False, default=None)
|
|
51
|
+
def init(entrypoint: str) -> None:
|
|
52
|
+
"""Initialize a uipath.json configuration file for the script."""
|
|
53
|
+
current_directory = os.getcwd()
|
|
54
|
+
generate_env_file(current_directory)
|
|
55
|
+
|
|
56
|
+
result = Middlewares.next("init", entrypoint)
|
|
57
|
+
|
|
58
|
+
if result.error_message:
|
|
59
|
+
click.echo(result.error_message)
|
|
60
|
+
if result.should_include_stacktrace:
|
|
61
|
+
click.echo(traceback.format_exc())
|
|
62
|
+
click.get_current_context().exit(1)
|
|
63
|
+
|
|
64
|
+
if result.info_message:
|
|
65
|
+
click.echo(result.info_message)
|
|
66
|
+
|
|
67
|
+
if not result.should_continue:
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
script_path = get_user_script(current_directory, entrypoint=entrypoint)
|
|
71
|
+
|
|
72
|
+
if not script_path:
|
|
73
|
+
click.get_current_context().exit(1)
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
args = generate_args(script_path)
|
|
77
|
+
|
|
78
|
+
relative_path = os.path.relpath(script_path, current_directory)
|
|
79
|
+
|
|
80
|
+
config_data = {
|
|
81
|
+
"entryPoints": [
|
|
82
|
+
{
|
|
83
|
+
"filePath": relative_path,
|
|
84
|
+
"uniqueId": str(uuid.uuid4()),
|
|
85
|
+
# "type": "process", OR BE doesn't offer json schema support for type: Process
|
|
86
|
+
"type": "agent",
|
|
87
|
+
"input": args["input"],
|
|
88
|
+
"output": args["output"],
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# Generate bindings JSON based on the script path
|
|
94
|
+
try:
|
|
95
|
+
bindings_data = generate_bindings_json(script_path)
|
|
96
|
+
|
|
97
|
+
# Add bindings to the config data
|
|
98
|
+
config_data["bindings"] = bindings_data
|
|
99
|
+
|
|
100
|
+
click.echo("Bindings generated successfully.")
|
|
101
|
+
except Exception as e:
|
|
102
|
+
click.echo(f"Warning: Could not generate bindings: {str(e)}")
|
|
103
|
+
|
|
104
|
+
config_path = "uipath.json"
|
|
105
|
+
with open(config_path, "w") as config_file:
|
|
106
|
+
json.dump(config_data, config_file, indent=4)
|
|
107
|
+
|
|
108
|
+
click.echo(f"Configuration file {config_path} created successfully.")
|
|
109
|
+
|
|
110
|
+
except Exception as e:
|
|
111
|
+
click.echo(f"Error generating configuration: {str(e)}")
|
|
112
|
+
click.echo(traceback.format_exc())
|
|
113
|
+
click.get_current_context().exit(1)
|
uipath/_cli/cli_new.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# type: ignore
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import traceback
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
from .middlewares import Middlewares
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def generate_script(target_directory):
|
|
12
|
+
template_path = os.path.join(
|
|
13
|
+
os.path.dirname(__file__), "_templates/main.py.template"
|
|
14
|
+
)
|
|
15
|
+
target_path = os.path.join(target_directory, "main.py")
|
|
16
|
+
|
|
17
|
+
shutil.copyfile(template_path, target_path)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def generate_pyproject(target_directory, project_name):
|
|
21
|
+
project_toml_path = os.path.join(target_directory, "pyproject.toml")
|
|
22
|
+
toml_content = f"""[project]
|
|
23
|
+
name = "{project_name}"
|
|
24
|
+
version = "0.0.1"
|
|
25
|
+
description = "{project_name}"
|
|
26
|
+
authors = [{{ name = "John Doe", email = "john.doe@myemail.com" }}]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"uipath>=2.0.0"
|
|
29
|
+
]
|
|
30
|
+
requires-python = ">=3.9"
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
with open(project_toml_path, "w") as f:
|
|
34
|
+
f.write(toml_content)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@click.command()
|
|
38
|
+
@click.argument("name", type=str, default="")
|
|
39
|
+
def new(name: str):
|
|
40
|
+
directory = os.getcwd()
|
|
41
|
+
|
|
42
|
+
if not name:
|
|
43
|
+
raise click.UsageError(
|
|
44
|
+
"Please specify a name for your project\n`uipath new hello-world`"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
click.echo(f"Initializing project {name} in current directory..")
|
|
48
|
+
|
|
49
|
+
result = Middlewares.next("new", name)
|
|
50
|
+
|
|
51
|
+
if result.error_message:
|
|
52
|
+
click.echo(result.error_message)
|
|
53
|
+
if result.should_include_stacktrace:
|
|
54
|
+
click.echo(traceback.format_exc())
|
|
55
|
+
click.get_current_context().exit(1)
|
|
56
|
+
|
|
57
|
+
if result.info_message:
|
|
58
|
+
click.echo(result.info_message)
|
|
59
|
+
|
|
60
|
+
if not result.should_continue:
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
generate_script(directory)
|
|
64
|
+
click.echo("Created main.py file.")
|
|
65
|
+
generate_pyproject(directory, name)
|
|
66
|
+
click.echo("Created pyproject.toml file.")
|
|
67
|
+
|
|
68
|
+
ctx = click.get_current_context()
|
|
69
|
+
init_cmd = ctx.parent.command.get_command(ctx, "init")
|
|
70
|
+
ctx.invoke(init_cmd)
|
|
71
|
+
|
|
72
|
+
click.echo("""` uipath run main.py '{"message": "Hello World!"}' `""")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
if __name__ == "__main__":
|
|
76
|
+
new()
|
uipath/_cli/cli_pack.py
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
# type: ignore
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import uuid
|
|
5
|
+
import zipfile
|
|
6
|
+
from string import Template
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
import tomllib
|
|
12
|
+
except ImportError:
|
|
13
|
+
import tomli as tomllib
|
|
14
|
+
|
|
15
|
+
from ._utils._parse_ast import generate_bindings_json
|
|
16
|
+
|
|
17
|
+
schema = "https://cloud.uipath.com/draft/2024-12/entry-point"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def validate_config_structure(config_data):
|
|
21
|
+
required_fields = ["entryPoints"]
|
|
22
|
+
for field in required_fields:
|
|
23
|
+
if field not in config_data:
|
|
24
|
+
raise Exception(f"uipath.json is missing the required field: {field}")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def check_config(directory):
|
|
28
|
+
config_path = os.path.join(directory, "uipath.json")
|
|
29
|
+
toml_path = os.path.join(directory, "pyproject.toml")
|
|
30
|
+
|
|
31
|
+
if not os.path.isfile(config_path):
|
|
32
|
+
raise Exception("uipath.json not found, please run `uipath init`")
|
|
33
|
+
if not os.path.isfile(toml_path):
|
|
34
|
+
raise Exception("pyproject.toml not found")
|
|
35
|
+
|
|
36
|
+
with open(config_path, "r") as config_file:
|
|
37
|
+
config_data = json.load(config_file)
|
|
38
|
+
|
|
39
|
+
validate_config_structure(config_data)
|
|
40
|
+
|
|
41
|
+
toml_data = read_toml_project(toml_path)
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
"project_name": toml_data["name"],
|
|
45
|
+
"description": toml_data["description"],
|
|
46
|
+
"entryPoints": config_data["entryPoints"],
|
|
47
|
+
"version": toml_data["version"],
|
|
48
|
+
"authors": toml_data["authors"],
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def generate_operate_file(entryPoints):
|
|
53
|
+
project_id = str(uuid.uuid4())
|
|
54
|
+
|
|
55
|
+
first_entry = entryPoints[0]
|
|
56
|
+
file_path = first_entry["filePath"]
|
|
57
|
+
type = first_entry["type"]
|
|
58
|
+
|
|
59
|
+
operate_json_data = {
|
|
60
|
+
"$schema": schema,
|
|
61
|
+
"projectId": project_id,
|
|
62
|
+
"main": file_path,
|
|
63
|
+
"contentType": type,
|
|
64
|
+
"targetFramework": "Portable",
|
|
65
|
+
"targetRuntime": "python",
|
|
66
|
+
"runtimeOptions": {"requiresUserInteraction": False, "isAttended": False},
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return operate_json_data
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def generate_entrypoints_file(entryPoints):
|
|
73
|
+
entrypoint_json_data = {
|
|
74
|
+
"$schema": schema,
|
|
75
|
+
"$id": "entry-points.json",
|
|
76
|
+
"entryPoints": entryPoints,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return entrypoint_json_data
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def generate_bindings_content():
|
|
83
|
+
bindings_content = {"version": "2.0", "resources": []}
|
|
84
|
+
|
|
85
|
+
return bindings_content
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def get_proposed_version(directory):
|
|
89
|
+
output_dir = os.path.join(directory, ".uipath")
|
|
90
|
+
if not os.path.exists(output_dir):
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
# Get all .nupkg files
|
|
94
|
+
nupkg_files = [f for f in os.listdir(output_dir) if f.endswith(".nupkg")]
|
|
95
|
+
if not nupkg_files:
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
# Sort by modification time to get most recent
|
|
99
|
+
latest_file = max(
|
|
100
|
+
nupkg_files, key=lambda f: os.path.getmtime(os.path.join(output_dir, f))
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Extract version from filename
|
|
104
|
+
# Remove .nupkg extension first
|
|
105
|
+
name_version = latest_file[:-6]
|
|
106
|
+
# Find 3rd last occurrence of . by splitting and joining parts
|
|
107
|
+
parts = name_version.split(".")
|
|
108
|
+
if len(parts) >= 3:
|
|
109
|
+
version = ".".join(parts[-3:])
|
|
110
|
+
else:
|
|
111
|
+
version = name_version
|
|
112
|
+
|
|
113
|
+
# Increment patch version by 1
|
|
114
|
+
try:
|
|
115
|
+
major, minor, patch = version.split(".")
|
|
116
|
+
new_version = f"{major}.{minor}.{int(patch) + 1}"
|
|
117
|
+
return new_version
|
|
118
|
+
except Exception:
|
|
119
|
+
return "0.0.1"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def generate_content_types_content():
|
|
123
|
+
templates_path = os.path.join(
|
|
124
|
+
os.path.dirname(__file__), "_templates", "[Content_Types].xml.template"
|
|
125
|
+
)
|
|
126
|
+
with open(templates_path, "r") as file:
|
|
127
|
+
content_types_content = file.read()
|
|
128
|
+
return content_types_content
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def generate_nuspec_content(projectName, packageVersion, description, authors):
|
|
132
|
+
variables = {
|
|
133
|
+
"packageName": projectName,
|
|
134
|
+
"packageVersion": packageVersion,
|
|
135
|
+
"description": description,
|
|
136
|
+
"authors": authors,
|
|
137
|
+
}
|
|
138
|
+
templates_path = os.path.join(
|
|
139
|
+
os.path.dirname(__file__), "_templates", "package.nuspec.template"
|
|
140
|
+
)
|
|
141
|
+
with open(templates_path, "r", encoding="utf-8-sig") as f:
|
|
142
|
+
content = f.read()
|
|
143
|
+
return Template(content).substitute(variables)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def generate_rels_content(nuspecPath, psmdcpPath):
|
|
147
|
+
# /package/services/metadata/core-properties/254324ccede240e093a925f0231429a0.psmdcp
|
|
148
|
+
templates_path = os.path.join(
|
|
149
|
+
os.path.dirname(__file__), "_templates", ".rels.template"
|
|
150
|
+
)
|
|
151
|
+
nuspecId = "R" + str(uuid.uuid4()).replace("-", "")[:16]
|
|
152
|
+
psmdcpId = "R" + str(uuid.uuid4()).replace("-", "")[:16]
|
|
153
|
+
variables = {
|
|
154
|
+
"nuspecPath": nuspecPath,
|
|
155
|
+
"nuspecId": nuspecId,
|
|
156
|
+
"psmdcpPath": psmdcpPath,
|
|
157
|
+
"psmdcpId": psmdcpId,
|
|
158
|
+
}
|
|
159
|
+
with open(templates_path, "r", encoding="utf-8-sig") as f:
|
|
160
|
+
content = f.read()
|
|
161
|
+
return Template(content).substitute(variables)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def generate_psmdcp_content(projectName, version, description, authors):
|
|
165
|
+
templates_path = os.path.join(
|
|
166
|
+
os.path.dirname(__file__), "_templates", ".psmdcp.template"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
token = str(uuid.uuid4()).replace("-", "")[:32]
|
|
170
|
+
random_file_name = f"{uuid.uuid4().hex[:16]}.psmdcp"
|
|
171
|
+
variables = {
|
|
172
|
+
"creator": authors,
|
|
173
|
+
"description": description,
|
|
174
|
+
"packageVersion": version,
|
|
175
|
+
"projectName": projectName,
|
|
176
|
+
"publicKeyToken": token,
|
|
177
|
+
}
|
|
178
|
+
with open(templates_path, "r", encoding="utf-8-sig") as f:
|
|
179
|
+
content = f.read()
|
|
180
|
+
|
|
181
|
+
return [random_file_name, Template(content).substitute(variables)]
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def generate_package_descriptor_content(entryPoints):
|
|
185
|
+
files = {
|
|
186
|
+
"operate.json": "content/operate.json",
|
|
187
|
+
"entry-points.json": "content/entry-points.json",
|
|
188
|
+
"bindings.json": "content/bindings_v2.json",
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
for entry in entryPoints:
|
|
192
|
+
files[entry["filePath"]] = entry["filePath"]
|
|
193
|
+
|
|
194
|
+
package_descriptor_content = {
|
|
195
|
+
"$schema": "https://cloud.uipath.com/draft/2024-12/package-descriptor",
|
|
196
|
+
"files": files,
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return package_descriptor_content
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def pack_fn(projectName, description, entryPoints, version, authors, directory):
|
|
203
|
+
operate_file = generate_operate_file(entryPoints)
|
|
204
|
+
entrypoints_file = generate_entrypoints_file(entryPoints)
|
|
205
|
+
|
|
206
|
+
# Get bindings from uipath.json if available
|
|
207
|
+
config_path = os.path.join(directory, "uipath.json")
|
|
208
|
+
if os.path.exists(config_path):
|
|
209
|
+
with open(config_path, "r") as f:
|
|
210
|
+
config_data = json.load(f)
|
|
211
|
+
if "bindings" in config_data:
|
|
212
|
+
bindings_content = config_data["bindings"]
|
|
213
|
+
else:
|
|
214
|
+
# Fall back to generating bindings from the first entry point
|
|
215
|
+
bindings_content = generate_bindings_json(entryPoints[0]["filePath"])
|
|
216
|
+
else:
|
|
217
|
+
# Fall back to generating bindings from the first entry point
|
|
218
|
+
bindings_content = generate_bindings_json(entryPoints[0]["filePath"])
|
|
219
|
+
|
|
220
|
+
content_types_content = generate_content_types_content()
|
|
221
|
+
[psmdcp_file_name, psmdcp_content] = generate_psmdcp_content(
|
|
222
|
+
projectName, version, description, authors
|
|
223
|
+
)
|
|
224
|
+
nuspec_content = generate_nuspec_content(projectName, version, description, authors)
|
|
225
|
+
rels_content = generate_rels_content(
|
|
226
|
+
f"/{projectName}.nuspec",
|
|
227
|
+
f"/package/services/metadata/core-properties/{psmdcp_file_name}",
|
|
228
|
+
)
|
|
229
|
+
package_descriptor_content = generate_package_descriptor_content(entryPoints)
|
|
230
|
+
|
|
231
|
+
# Create .uipath directory if it doesn't exist
|
|
232
|
+
os.makedirs(".uipath", exist_ok=True)
|
|
233
|
+
|
|
234
|
+
# Define the allowlist of file extensions to include
|
|
235
|
+
file_extensions_allowlist = [".py", ".mermaid", ".json"]
|
|
236
|
+
|
|
237
|
+
with zipfile.ZipFile(
|
|
238
|
+
f".uipath/{projectName}.{version}.nupkg", "w", zipfile.ZIP_DEFLATED
|
|
239
|
+
) as z:
|
|
240
|
+
# Add metadata files
|
|
241
|
+
z.writestr(
|
|
242
|
+
f"./package/services/metadata/core-properties/{psmdcp_file_name}",
|
|
243
|
+
psmdcp_content,
|
|
244
|
+
)
|
|
245
|
+
z.writestr("[Content_Types].xml", content_types_content)
|
|
246
|
+
z.writestr(
|
|
247
|
+
"content/package-descriptor.json",
|
|
248
|
+
json.dumps(package_descriptor_content, indent=4),
|
|
249
|
+
)
|
|
250
|
+
z.writestr("content/operate.json", json.dumps(operate_file, indent=4))
|
|
251
|
+
z.writestr("content/entry-points.json", json.dumps(entrypoints_file, indent=4))
|
|
252
|
+
z.writestr("content/bindings_v2.json", json.dumps(bindings_content, indent=4))
|
|
253
|
+
z.writestr(f"{projectName}.nuspec", nuspec_content)
|
|
254
|
+
z.writestr("_rels/.rels", rels_content)
|
|
255
|
+
|
|
256
|
+
# Walk through directory and add all files with extensions in the allowlist
|
|
257
|
+
for root, dirs, files in os.walk(directory):
|
|
258
|
+
# Skip all directories that start with .
|
|
259
|
+
dirs[:] = [d for d in dirs if not d.startswith(".")]
|
|
260
|
+
|
|
261
|
+
for file in files:
|
|
262
|
+
file_extension = os.path.splitext(file)[1].lower()
|
|
263
|
+
if file_extension in file_extensions_allowlist:
|
|
264
|
+
file_path = os.path.join(root, file)
|
|
265
|
+
rel_path = os.path.relpath(file_path, directory)
|
|
266
|
+
try:
|
|
267
|
+
# Try UTF-8 first
|
|
268
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
269
|
+
z.writestr(f"content/{rel_path}", f.read())
|
|
270
|
+
except UnicodeDecodeError:
|
|
271
|
+
# If UTF-8 fails, try with utf-8-sig (for files with BOM)
|
|
272
|
+
try:
|
|
273
|
+
with open(file_path, "r", encoding="utf-8-sig") as f:
|
|
274
|
+
z.writestr(f"content/{rel_path}", f.read())
|
|
275
|
+
except UnicodeDecodeError:
|
|
276
|
+
# If that also fails, try with latin-1 as a fallback
|
|
277
|
+
with open(file_path, "r", encoding="latin-1") as f:
|
|
278
|
+
z.writestr(f"content/{rel_path}", f.read())
|
|
279
|
+
|
|
280
|
+
optional_files = ["pyproject.toml", "README.md"]
|
|
281
|
+
for file in optional_files:
|
|
282
|
+
file_path = os.path.join(directory, file)
|
|
283
|
+
if os.path.exists(file_path):
|
|
284
|
+
try:
|
|
285
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
286
|
+
z.writestr(f"content/{file}", f.read())
|
|
287
|
+
except UnicodeDecodeError:
|
|
288
|
+
with open(file_path, "r", encoding="latin-1") as f:
|
|
289
|
+
z.writestr(f"content/{file}", f.read())
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def read_toml_project(file_path: str) -> dict[str, any]:
|
|
293
|
+
with open(file_path, "rb") as f:
|
|
294
|
+
content = tomllib.load(f)
|
|
295
|
+
return {
|
|
296
|
+
"name": content["project"]["name"],
|
|
297
|
+
"description": content["project"]["description"],
|
|
298
|
+
"version": content["project"]["version"],
|
|
299
|
+
"authors": content["project"].get("authors", [{"name": ""}])[0]["name"],
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def get_project_version(directory):
|
|
304
|
+
toml_path = os.path.join(directory, "pyproject.toml")
|
|
305
|
+
if not os.path.exists(toml_path):
|
|
306
|
+
click.echo("Warning: No pyproject.toml found. Using default version 0.0.1")
|
|
307
|
+
return "0.0.1"
|
|
308
|
+
toml_data = read_toml_project(toml_path)
|
|
309
|
+
return toml_data["version"]
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
@click.command()
|
|
313
|
+
@click.argument("root", type=str, default="./")
|
|
314
|
+
def pack(root):
|
|
315
|
+
version = get_project_version(root)
|
|
316
|
+
|
|
317
|
+
while not os.path.isfile(os.path.join(root, "uipath.json")):
|
|
318
|
+
click.echo(
|
|
319
|
+
"uipath.json not found. Please run `uipath init` in the project directory."
|
|
320
|
+
)
|
|
321
|
+
return
|
|
322
|
+
config = check_config(root)
|
|
323
|
+
click.echo(
|
|
324
|
+
f"Packaging project {config['project_name']}:{version or config['version']} description {config['description']} authored by {config['authors']}"
|
|
325
|
+
)
|
|
326
|
+
pack_fn(
|
|
327
|
+
config["project_name"],
|
|
328
|
+
config["description"],
|
|
329
|
+
config["entryPoints"],
|
|
330
|
+
version or config["version"],
|
|
331
|
+
config["authors"],
|
|
332
|
+
root,
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
if __name__ == "__main__":
|
|
337
|
+
pack()
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# type: ignore
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
import requests
|
|
6
|
+
from dotenv import load_dotenv
|
|
7
|
+
|
|
8
|
+
load_dotenv()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_most_recent_package():
|
|
12
|
+
nupkg_files = [f for f in os.listdir(".uipath") if f.endswith(".nupkg")]
|
|
13
|
+
if not nupkg_files:
|
|
14
|
+
click.echo("No .nupkg file found in .uipath directory")
|
|
15
|
+
return
|
|
16
|
+
|
|
17
|
+
# Get full path and modification time for each file
|
|
18
|
+
nupkg_files_with_time = [
|
|
19
|
+
(f, os.path.getmtime(os.path.join(".uipath", f))) for f in nupkg_files
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
# Sort by modification time (most recent first)
|
|
23
|
+
nupkg_files_with_time.sort(key=lambda x: x[1], reverse=True)
|
|
24
|
+
|
|
25
|
+
# Get most recent file
|
|
26
|
+
return nupkg_files_with_time[0][0]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_env_vars():
|
|
30
|
+
base_url = os.environ.get("UIPATH_URL")
|
|
31
|
+
token = os.environ.get("UIPATH_ACCESS_TOKEN")
|
|
32
|
+
|
|
33
|
+
if not all([base_url, token]):
|
|
34
|
+
click.echo(
|
|
35
|
+
"Missing required environment variables. Please check your .env file contains:"
|
|
36
|
+
)
|
|
37
|
+
click.echo("UIPATH_URL, UIPATH_ACCESS_TOKEN")
|
|
38
|
+
raise click.Abort("Missing environment variables")
|
|
39
|
+
|
|
40
|
+
return [base_url, token]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@click.command()
|
|
44
|
+
@click.option(
|
|
45
|
+
"--tenant",
|
|
46
|
+
"-t",
|
|
47
|
+
"feed",
|
|
48
|
+
flag_value="tenant",
|
|
49
|
+
help="Whether to publish to the tenant package feed",
|
|
50
|
+
)
|
|
51
|
+
@click.option(
|
|
52
|
+
"--personal-workspace",
|
|
53
|
+
"-p",
|
|
54
|
+
"feed",
|
|
55
|
+
flag_value="personal",
|
|
56
|
+
help="Whether to publish to the personal workspace",
|
|
57
|
+
)
|
|
58
|
+
def publish(feed):
|
|
59
|
+
if feed is None:
|
|
60
|
+
click.echo("Select feed type:")
|
|
61
|
+
click.echo(" 0: Tenant package feed")
|
|
62
|
+
click.echo(" 1: Personal workspace")
|
|
63
|
+
feed_idx = click.prompt("Select feed", type=int)
|
|
64
|
+
feed = "tenant" if feed_idx == 0 else "personal"
|
|
65
|
+
click.echo(f"Selected feed: {feed}")
|
|
66
|
+
os.makedirs(".uipath", exist_ok=True)
|
|
67
|
+
|
|
68
|
+
# Find most recent .nupkg file in .uipath directory
|
|
69
|
+
most_recent = get_most_recent_package()
|
|
70
|
+
|
|
71
|
+
if not most_recent:
|
|
72
|
+
click.echo("Error: No package files found in .uipath directory")
|
|
73
|
+
raise click.Abort()
|
|
74
|
+
click.echo(f"Publishing most recent package: {most_recent}")
|
|
75
|
+
|
|
76
|
+
package_to_publish_path = os.path.join(".uipath", most_recent)
|
|
77
|
+
|
|
78
|
+
[base_url, token] = get_env_vars()
|
|
79
|
+
|
|
80
|
+
url = f"{base_url}/orchestrator_/odata/Processes/UiPath.Server.Configuration.OData.UploadPackage()"
|
|
81
|
+
|
|
82
|
+
if feed == "personal":
|
|
83
|
+
# 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}"}
|
|
87
|
+
)
|
|
88
|
+
|
|
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
|
|
102
|
+
|
|
103
|
+
headers = {"Authorization": f"Bearer {token}"}
|
|
104
|
+
|
|
105
|
+
with open(package_to_publish_path, "rb") as f:
|
|
106
|
+
files = {"file": (package_to_publish_path, f, "application/octet-stream")}
|
|
107
|
+
response = requests.post(url, headers=headers, files=files)
|
|
108
|
+
|
|
109
|
+
if response.status_code == 200:
|
|
110
|
+
click.echo("Package published successfully!")
|
|
111
|
+
else:
|
|
112
|
+
click.echo(f"Failed to publish package. Status code: {response.status_code}")
|
|
113
|
+
click.echo(f"Response: {response.text}")
|