uipath 2.1.21__py3-none-any.whl → 2.1.23__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.
@@ -133,12 +133,14 @@ class SwFileHandler:
133
133
  self,
134
134
  local_files: list[FileInfo],
135
135
  source_code_files: Dict[str, ProjectFile],
136
+ root_files: Dict[str, ProjectFile],
136
137
  ) -> None:
137
138
  """Process all file uploads to the source_code folder.
138
139
 
139
140
  Args:
140
141
  local_files: List of files to upload
141
142
  source_code_files: Dictionary of existing remote files
143
+ root_files: Dictionary of existing root-level files
142
144
 
143
145
  Returns:
144
146
  Set of processed file names
@@ -186,13 +188,25 @@ class SwFileHandler:
186
188
  )
187
189
  )
188
190
  self.console.info(
189
- f"Uploading {click.style(local_file.file_name, fg='cyan')}"
191
+ f"Uploading {click.style(local_file.relative_path, fg='cyan')}"
190
192
  )
191
193
 
192
194
  # identify and add deleted files
193
195
  structural_migration.deleted_resources.extend(
194
196
  self._collect_deleted_files(source_code_files, processed_source_files)
195
197
  )
198
+
199
+ with open(os.path.join(self.directory, "uipath.json"), "r") as f:
200
+ uipath_config = json.load(f)
201
+
202
+ await self._prepare_agent_json_migration(
203
+ structural_migration, root_files, uipath_config
204
+ )
205
+
206
+ await self._prepare_entrypoints_json_migration(
207
+ structural_migration, root_files, uipath_config
208
+ )
209
+
196
210
  await self._studio_client.perform_structural_migration_async(
197
211
  structural_migration
198
212
  )
@@ -303,40 +317,65 @@ class SwFileHandler:
303
317
 
304
318
  return True
305
319
 
306
- async def _update_agent_json(
320
+ async def _prepare_entrypoints_json_migration(
307
321
  self,
308
- agent_json_file: Optional[ProjectFile] = None,
322
+ structural_migration: StructuralMigration,
323
+ root_files: Dict[str, ProjectFile],
324
+ uipath_config: Dict[str, Any],
309
325
  ) -> None:
310
- """Update agent.json file with metadata from uipath.json.
326
+ """Prepare entry-points.json to be included in the same structural migration."""
327
+ existing = root_files.get("entry-points.json")
328
+ if existing:
329
+ try:
330
+ entry_points_json = (
331
+ await self._studio_client.download_file_async(existing.id)
332
+ ).json()
333
+ entry_points_json["entryPoints"] = uipath_config["entryPoints"]
311
334
 
312
- This function:
313
- 1. Downloads existing agent.json if it exists
314
- 2. Updates metadata based on uipath.json content
315
- 3. Increments code version
316
- 4. Updates author from JWT or pyproject.toml
317
- 5. Uploads updated agent.json
335
+ except Exception:
336
+ self.console.warning(
337
+ "Could not parse existing entry-points.json file, using default version"
338
+ )
339
+ structural_migration.modified_resources.append(
340
+ ModifiedResource(
341
+ id=existing.id,
342
+ content_string=json.dumps(entry_points_json),
343
+ )
344
+ )
345
+ self.console.info(
346
+ f"Updating {click.style('entry-points.json', fg='yellow')}"
347
+ )
318
348
 
319
- Args:
320
- agent_json_file: Optional existing agent.json file
349
+ else:
350
+ self.console.warning(
351
+ "'entry-points.json' file does not exist in Studio Web project, initializing using default version"
352
+ )
353
+ entry_points_json = {
354
+ "$schema": "https://cloud.uipath.com/draft/2024-12/entry-point",
355
+ "$id": "entry-points.json",
356
+ "entryPoints": uipath_config["entryPoints"],
357
+ }
358
+ structural_migration.added_resources.append(
359
+ AddedResource(
360
+ file_name="entry-points.json",
361
+ content_string=json.dumps(entry_points_json),
362
+ )
363
+ )
364
+ self.console.info(
365
+ f"Uploading {click.style('entry-points.json', fg='cyan')}"
366
+ )
321
367
 
322
- Raises:
323
- httpx.HTTPError: If API requests fail
324
- FileNotFoundError: If required files are missing
325
- json.JSONDecodeError: If JSON parsing fails
326
- """
368
+ async def _prepare_agent_json_migration(
369
+ self,
370
+ structural_migration: StructuralMigration,
371
+ root_files: Dict[str, ProjectFile],
372
+ uipath_config: Dict[str, Any],
373
+ ) -> None:
374
+ """Prepare agent.json to be included in the same structural migration."""
327
375
 
328
376
  def get_author_from_token_or_toml() -> str:
329
377
  import jwt
330
378
 
331
- """Extract preferred_username from JWT token or fall back to pyproject.toml author.
332
-
333
- Args:
334
- directory: Project directory containing pyproject.toml
335
-
336
- Returns:
337
- str: Author name from JWT preferred_username or pyproject.toml authors field
338
- """
339
- # Try to get author from JWT token first
340
379
  token = os.getenv("UIPATH_ACCESS_TOKEN")
341
380
  if token:
342
381
  try:
@@ -350,19 +389,15 @@ class SwFileHandler:
350
389
  # If JWT decoding fails, fall back to toml
351
390
  pass
352
391
 
353
- toml_data = read_toml_project(os.path.join(directory, "pyproject.toml"))
354
-
392
+ toml_data = read_toml_project(
393
+ os.path.join(self.directory, "pyproject.toml")
394
+ )
355
395
  return toml_data.get("authors", "").strip()
356
396
 
357
- # Read uipath.json
358
- directory = os.getcwd()
359
- with open(os.path.join(directory, "uipath.json"), "r") as f:
360
- uipath_config = json.load(f)
361
-
362
397
  try:
363
398
  entrypoints = [
364
- {"input": entry_point["input"], "output": entry_point["output"]}
365
- for entry_point in uipath_config["entryPoints"]
399
+ {"input": entrypoint["input"], "output": entrypoint["output"]}
400
+ for entrypoint in uipath_config["entryPoints"]
366
401
  ]
367
402
  except (FileNotFoundError, KeyError) as e:
368
403
  self.console.error(
@@ -388,14 +423,12 @@ class SwFileHandler:
388
423
  ),
389
424
  }
390
425
 
391
- if agent_json_file:
392
- # Download existing agent.json
393
- existing_agent_json = (
394
- await self._studio_client.download_file_async(agent_json_file.id)
395
- ).json()
396
-
426
+ existing = root_files.get("agent.json")
427
+ if existing:
397
428
  try:
398
- # Get current version and increment patch version
429
+ existing_agent_json = (
430
+ await self._studio_client.download_file_async(existing.id)
431
+ ).json()
399
432
  version_parts = existing_agent_json["metadata"]["codeVersion"].split(
400
433
  "."
401
434
  )
@@ -403,20 +436,32 @@ class SwFileHandler:
403
436
  version_parts[-1] = str(int(version_parts[-1]) + 1)
404
437
  agent_json["metadata"]["codeVersion"] = ".".join(version_parts)
405
438
  else:
406
- # If version format is invalid, start from initial version + 1
407
439
  agent_json["metadata"]["codeVersion"] = (
408
440
  AGENT_INITIAL_CODE_VERSION[:-1] + "1"
409
441
  )
410
- except (json.JSONDecodeError, KeyError, ValueError):
442
+ except Exception:
411
443
  self.console.warning(
412
- "Could not parse existing agent.json, using default version"
444
+ "Could not parse existing agent.json file, using default version"
413
445
  )
414
- file, action = await self._studio_client.upload_file_async(
415
- file_content=json.dumps(agent_json),
416
- file_name="agent.json",
417
- remote_file=agent_json_file,
418
- )
419
- self.console.success(f"{action} {click.style('agent.json', fg='cyan')}")
446
+
447
+ structural_migration.modified_resources.append(
448
+ ModifiedResource(
449
+ id=existing.id,
450
+ content_string=json.dumps(agent_json),
451
+ )
452
+ )
453
+ self.console.info(f"Updating {click.style('agent.json', fg='yellow')}")
454
+ else:
455
+ self.console.warning(
456
+ "'agent.json' file does not exist in Studio Web project, initializing using default version"
457
+ )
458
+ structural_migration.added_resources.append(
459
+ AddedResource(
460
+ file_name="agent.json",
461
+ content_string=json.dumps(agent_json),
462
+ )
463
+ )
464
+ self.console.info(f"Uploading {click.style('agent.json', fg='cyan')}")
420
465
 
421
466
  async def upload_source_files(self, config_data: dict[str, Any]) -> None:
422
467
  """Main method to upload source files to the UiPath project.
@@ -457,8 +502,4 @@ class SwFileHandler:
457
502
  self.include_uv_lock,
458
503
  directories_to_ignore=["evals"],
459
504
  )
460
- await self._process_file_uploads(files, source_code_files)
461
-
462
- await self._update_agent_json(
463
- root_files.get("agent.json", None),
464
- )
505
+ await self._process_file_uploads(files, source_code_files, root_files)
@@ -175,13 +175,20 @@ def get_folder_by_name(
175
175
 
176
176
 
177
177
  class AddedResource(BaseModel):
178
- content_file_path: str
178
+ """Represents a new file to be added during a structural migration."""
179
+
180
+ content_file_path: Optional[str] = None
179
181
  parent_path: Optional[str] = None
182
+ file_name: Optional[str] = None
183
+ content_string: Optional[str] = None
180
184
 
181
185
 
182
186
  class ModifiedResource(BaseModel):
187
+ """Represents a file update during a structural migration."""
188
+
183
189
  id: str
184
- content_file_path: str
190
+ content_file_path: Optional[str] = None
191
+ content_string: Optional[str] = None
185
192
 
186
193
 
187
194
  class StructuralMigration(BaseModel):
@@ -336,6 +343,53 @@ class StudioClient:
336
343
  headers=headers or {},
337
344
  )
338
345
 
346
+ def _resolve_content_and_filename(
347
+ self,
348
+ *,
349
+ content_string: Optional[str],
350
+ content_file_path: Optional[str],
351
+ file_name: Optional[str] = None,
352
+ modified: bool = False,
353
+ ) -> tuple[bytes, Optional[str]]:
354
+ """Resolve multipart content bytes and filename for a resource.
355
+
356
+ Args:
357
+ content_string: Inline content as a string.
358
+ content_file_path: Path to a local file to read if inline content is not provided.
359
+ file_name: Explicit filename to use when adding a new resource.
360
+
361
+ Returns:
362
+ A tuple of (content_bytes, filename).
363
+
364
+ Raises:
365
+ FileNotFoundError: If a provided file path does not exist.
366
+ ValueError: If a filename cannot be determined.
367
+ """
368
+ content_bytes: bytes = b""
369
+ resolved_name: Optional[str] = None
370
+ if content_string is not None:
371
+ content_bytes = content_string.encode("utf-8")
372
+ elif content_file_path:
373
+ if os.path.exists(content_file_path):
374
+ with open(content_file_path, "rb") as f:
375
+ content_bytes = f.read()
376
+ else:
377
+ raise FileNotFoundError(f"File not found: {content_file_path}")
378
+
379
+ if file_name:
380
+ resolved_name = file_name
381
+ elif content_file_path:
382
+ resolved_name = os.path.basename(content_file_path)
383
+ elif not modified:
384
+ raise ValueError(
385
+ "Unable to determine filename for multipart upload. "
386
+ "When providing inline content (content_string), you must also provide file_name. "
387
+ "Alternatively, set content_file_path so the filename can be inferred. "
388
+ f"Received file_name={file_name!r}, content_file_path={content_file_path!r}."
389
+ )
390
+
391
+ return content_bytes, resolved_name
392
+
339
393
  @with_lock_retry
340
394
  async def perform_structural_migration_async(
341
395
  self,
@@ -360,44 +414,37 @@ class StudioClient:
360
414
  (None, deleted_resources_json),
361
415
  )
362
416
  )
363
-
364
417
  for i, added_resource in enumerate(structural_migration.added_resources):
365
- if os.path.exists(added_resource.content_file_path):
366
- with open(added_resource.content_file_path, "rb") as f:
367
- content = f.read()
368
-
369
- filename = os.path.basename(added_resource.content_file_path)
370
- files.append((f"AddedResources[{i}].Content", (filename, content)))
371
-
372
- if added_resource.parent_path:
373
- files.append(
374
- (
375
- f"AddedResources[{i}].ParentPath",
376
- (None, added_resource.parent_path),
377
- )
378
- )
379
- else:
380
- raise FileNotFoundError(
381
- f"File not found: {added_resource.content_file_path}"
382
- )
418
+ content_bytes, filename = self._resolve_content_and_filename(
419
+ content_string=added_resource.content_string,
420
+ content_file_path=added_resource.content_file_path,
421
+ file_name=added_resource.file_name,
422
+ )
383
423
 
384
- for i, modified_resource in enumerate(structural_migration.modified_resources):
385
- if os.path.exists(modified_resource.content_file_path):
386
- with open(modified_resource.content_file_path, "rb") as f:
387
- content = f.read()
424
+ files.append((f"AddedResources[{i}].Content", (filename, content_bytes)))
388
425
 
389
- filename = os.path.basename(modified_resource.content_file_path)
390
- files.append((f"ModifiedResources[{i}].Content", (filename, content)))
426
+ if added_resource.parent_path:
391
427
  files.append(
392
428
  (
393
- f"ModifiedResources[{i}].Id",
394
- (None, modified_resource.id),
429
+ f"AddedResources[{i}].ParentPath",
430
+ (None, added_resource.parent_path),
395
431
  )
396
432
  )
397
- else:
398
- raise FileNotFoundError(
399
- f"File not found: {modified_resource.content_file_path}"
433
+
434
+ for i, modified_resource in enumerate(structural_migration.modified_resources):
435
+ content_bytes, _ = self._resolve_content_and_filename(
436
+ content_string=modified_resource.content_string,
437
+ content_file_path=modified_resource.content_file_path,
438
+ modified=True,
439
+ )
440
+
441
+ files.append((f"ModifiedResources[{i}].Content", content_bytes))
442
+ files.append(
443
+ (
444
+ f"ModifiedResources[{i}].Id",
445
+ (None, modified_resource.id),
400
446
  )
447
+ )
401
448
 
402
449
  response = await self.uipath.api_client.request_async(
403
450
  "POST",
uipath/tracing/_utils.py CHANGED
@@ -37,7 +37,7 @@ class UiPathSpan:
37
37
  )
38
38
  expiry_time_utc: Optional[str] = None
39
39
  folder_key: Optional[str] = field(
40
- default_factory=lambda: env.get("UIPATH_FOLDER_KEY_XYZ", "")
40
+ default_factory=lambda: env.get("UIPATH_FOLDER_KEY", "")
41
41
  )
42
42
  source: Optional[str] = None
43
43
  span_type: str = "Coded Agents"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: uipath
3
- Version: 2.1.21
3
+ Version: 2.1.23
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
@@ -42,7 +42,7 @@ uipath/_cli/_evals/_evaluators/_trajectory_evaluator.py,sha256=dnogQTOskpI4_cNF0
42
42
  uipath/_cli/_evals/_models/__init__.py,sha256=Ewjp3u2YeTH2MmzY9LWf7EIbAoIf_nW9fMYbj7pGlPs,420
43
43
  uipath/_cli/_evals/_models/_evaluation_set.py,sha256=tVHykSget-G3sOCs9bSchMYUTpFqzXVlYYbY8L9SI0c,1518
44
44
  uipath/_cli/_evals/_models/_evaluators.py,sha256=l57NEVyYmzSKuoIXuGkE94Br01hAMg35fiS2MlTkaQM,2115
45
- uipath/_cli/_push/sw_file_handler.py,sha256=tRE9n68xv0r20ulwOyALHtYwzbjGneiASwzNm8xtBN0,16372
45
+ uipath/_cli/_push/sw_file_handler.py,sha256=5zAt03gSlni7RnJmZJP4d4RVC33u8lZWj3W9xwx-CFM,18064
46
46
  uipath/_cli/_runtime/_contracts.py,sha256=WlpaiQAMWCo-JFHjee35Klf49A3GsKjOU1Mf2IpUGHY,16033
47
47
  uipath/_cli/_runtime/_escalation.py,sha256=x3vI98qsfRA-fL_tNkRVTFXioM5Gv2w0GFcXJJ5eQtg,7981
48
48
  uipath/_cli/_runtime/_hitl.py,sha256=aexwe0dIXvh6SlVS1jVnO_aGZc6e3gLsmGkCyha5AHo,11300
@@ -62,7 +62,7 @@ uipath/_cli/_utils/_input_args.py,sha256=3LGNqVpJItvof75VGm-ZNTUMUH9-c7-YgleM5b2
62
62
  uipath/_cli/_utils/_parse_ast.py,sha256=8Iohz58s6bYQ7rgWtOTjrEInLJ-ETikmOMZzZdIY2Co,20072
63
63
  uipath/_cli/_utils/_processes.py,sha256=q7DfEKHISDWf3pngci5za_z0Pbnf_shWiYEcTOTCiyk,1855
64
64
  uipath/_cli/_utils/_project_files.py,sha256=MMexSE6BapxitYBl1UUWy_AgkJMdGNFD1t2MkU0hwz4,13115
65
- uipath/_cli/_utils/_studio_project.py,sha256=iVcCm-aps-EQ7Pym_OWGOA48xeDigzcofUxXKy29x6U,13727
65
+ uipath/_cli/_utils/_studio_project.py,sha256=X4X9HbdbGgLQtJRE-h8rcALCrupWkrpqOfSnt9c-hlo,15541
66
66
  uipath/_cli/_utils/_tracing.py,sha256=2igb03j3EHjF_A406UhtCKkPfudVfFPjUq5tXUEG4oo,1541
67
67
  uipath/_cli/_utils/_uv_helpers.py,sha256=6SvoLnZPoKIxW0sjMvD1-ENV_HOXDYzH34GjBqwT138,3450
68
68
  uipath/_services/__init__.py,sha256=10xtw3ENC30yR9CCq_b94RMZ3YrUeyfHV33yWYUd8tU,896
@@ -112,11 +112,11 @@ uipath/telemetry/_track.py,sha256=I8SzROQcySAXNfxx4QgZlJ6ib8DjTKn50CiZYsbjjr8,45
112
112
  uipath/tracing/__init__.py,sha256=GKRINyWdHVrDsI-8mrZDLdf0oey6GHGlNZTOADK-kgc,224
113
113
  uipath/tracing/_otel_exporters.py,sha256=X7cnuGqvxGbACZuFD2XYTWXwIse8pokOEAjeTPE6DCQ,3158
114
114
  uipath/tracing/_traced.py,sha256=qeVDrds2OUnpdUIA0RhtF0kg2dlAZhyC1RRkI-qivTM,18528
115
- uipath/tracing/_utils.py,sha256=ZeensQexnw69jVcsVrGyED7mPlAU-L1agDGm6_1A3oc,10388
115
+ uipath/tracing/_utils.py,sha256=wJRELaPu69iY0AhV432Dk5QYf_N_ViRU4kAUG1BI1ew,10384
116
116
  uipath/utils/__init__.py,sha256=VD-KXFpF_oWexFg6zyiWMkxl2HM4hYJMIUDZ1UEtGx0,105
117
117
  uipath/utils/_endpoints_manager.py,sha256=iRTl5Q0XAm_YgcnMcJOXtj-8052sr6jpWuPNz6CgT0Q,8408
118
- uipath-2.1.21.dist-info/METADATA,sha256=O_9m-ZcdrpDqGwxFfOtVQDN9TvtCtuaCB33jpMuzLnY,6367
119
- uipath-2.1.21.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
120
- uipath-2.1.21.dist-info/entry_points.txt,sha256=9C2_29U6Oq1ExFu7usihR-dnfIVNSKc-0EFbh0rskB4,43
121
- uipath-2.1.21.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
122
- uipath-2.1.21.dist-info/RECORD,,
118
+ uipath-2.1.23.dist-info/METADATA,sha256=CEP-ypBLLKYH4Tkpc9iWDIUx-uWRHty7PqgnuX3CcTg,6367
119
+ uipath-2.1.23.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
120
+ uipath-2.1.23.dist-info/entry_points.txt,sha256=9C2_29U6Oq1ExFu7usihR-dnfIVNSKc-0EFbh0rskB4,43
121
+ uipath-2.1.23.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
122
+ uipath-2.1.23.dist-info/RECORD,,