@uipath/packager-tool-workflowcompiler-browser 0.0.33 → 1.197.0-preview.64

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.
@@ -62,9 +62,28 @@ export interface IProjectBundleExecutor {
62
62
  * Waits until all compatible projects have submitted their options,
63
63
  * then executes a single bundled remote-agent operation and resolves all pending promises.
64
64
  */
65
+ /**
66
+ * Shape of the `POST /odata/Attachments` response. Orchestrator returns
67
+ * PascalCase (`Id`, `BlobFileAccess.Uri`); the camelCase fallbacks mirror the
68
+ * defensive reads in `@uipath/orchestrator-sdk`'s `uploadJobAttachment`.
69
+ */
70
+ interface AttachmentResponse {
71
+ Id?: string;
72
+ id?: string;
73
+ BlobFileAccess?: { Uri: string };
74
+ blobFileAccess?: { Uri: string };
75
+ }
76
+
65
77
  export class ProjectBundleExecutor implements IProjectBundleExecutor {
66
78
  static readonly SESSION_TIMEOUT_MS = 1_800_000;
67
79
 
80
+ /**
81
+ * Maximum length (in characters) of the inline `inputArguments` payload
82
+ * accepted by the serverless jobs endpoint. Beyond this the arguments must
83
+ * be uploaded as a file attachment and referenced via `inputFile`.
84
+ */
85
+ static readonly MAX_INLINE_INPUT_ARGUMENTS_LENGTH = 10_000;
86
+
68
87
  private readonly sessions = new Map<string, BundleSession>();
69
88
  private readonly pendingSessionCreations = new Map<
70
89
  string,
@@ -416,6 +435,10 @@ export class ProjectBundleExecutor implements IProjectBundleExecutor {
416
435
  return {
417
436
  RelativePath: project?.ProjectRelativePath ?? "",
418
437
  DesignId: project?.Id ?? "",
438
+ ProjectStorageId:
439
+ entry.operation === BundleOperation.Pack
440
+ ? (entry.options.projectStorageId ?? "")
441
+ : "",
419
442
  PackageId:
420
443
  entry.operation === BundleOperation.Pack
421
444
  ? entry.options.package.id
@@ -444,19 +467,60 @@ export class ProjectBundleExecutor implements IProjectBundleExecutor {
444
467
  if (firstEntry.operation === BundleOperation.Pack) {
445
468
  executorInfo.Configuration = context.configuration;
446
469
  const pkg = firstEntry.options.package;
470
+ executorInfo.Package = {
471
+ Name: pkg.id,
472
+ Version: pkg.version,
473
+ Author: pkg.author ?? "",
474
+ Description: pkg.description ?? "",
475
+ ReleaseNotes: pkg.releaseNotes ?? "",
476
+ Tags: pkg.tags ?? "",
477
+ IconUrl: pkg.iconUrl ?? "",
478
+ ProjectUrl: pkg.projectUrl ?? "",
479
+ RepositoryType: pkg.repositoryType ?? "",
480
+ RepositoryUrl: pkg.repositoryUrl ?? "",
481
+ RepositoryBranch: pkg.repositoryBranch ?? "",
482
+ RepositoryCommit: pkg.repositoryCommit ?? "",
483
+ };
484
+ // Flat fields retained for backward compatibility with agents that
485
+ // predate the nested Package structure.
447
486
  executorInfo.Name = pkg.id;
448
487
  executorInfo.Version = pkg.version;
449
488
  executorInfo.Author = pkg.author ?? "";
450
489
  executorInfo.Description = pkg.description ?? "";
490
+ executorInfo.ReleaseNotes = pkg.releaseNotes ?? "";
451
491
  }
452
492
 
453
- const body = {
493
+ const inputArguments = JSON.stringify(executorInfo);
494
+
495
+ const body: Record<string, unknown> = {
454
496
  source: "StudioWeb",
455
497
  targetFramework: context.targetFramework,
456
498
  jobType: "PublishStudioProject",
457
- inputArguments: JSON.stringify(executorInfo),
458
499
  };
459
500
 
501
+ // The serverless jobs endpoint caps inline inputArguments at
502
+ // MAX_INLINE_INPUT_ARGUMENTS_LENGTH characters. For larger payloads we
503
+ // upload the arguments as a file attachment and reference it via
504
+ // inputFile instead (the two are mutually exclusive on the server).
505
+ if (
506
+ inputArguments.length >
507
+ ProjectBundleExecutor.MAX_INLINE_INPUT_ARGUMENTS_LENGTH
508
+ ) {
509
+ context.logger?.info(
510
+ translate.t(
511
+ "toolWorkflowCompilerBrowser.info.offloadingInputArguments",
512
+ { length: inputArguments.length },
513
+ ),
514
+ );
515
+ body.inputFile = await this.uploadInputArgumentsFileAsync(
516
+ inputArguments,
517
+ connection,
518
+ context.logger,
519
+ );
520
+ } else {
521
+ body.inputArguments = inputArguments;
522
+ }
523
+
460
524
  const headers: Record<string, string> = {
461
525
  "Content-Type": "application/json",
462
526
  Authorization: `Bearer ${connection.accessToken}`,
@@ -484,6 +548,86 @@ export class ProjectBundleExecutor implements IProjectBundleExecutor {
484
548
  }
485
549
  }
486
550
 
551
+ /**
552
+ * Uploads the input arguments JSON as a file attachment and returns the
553
+ * attachment id, to be passed as `inputFile` on the serverless job request.
554
+ * Mirrors the two-step flow used by the Orchestrator UI: create the
555
+ * attachment (which returns a signed blob write URI) then PUT the contents
556
+ * to that URI using the storage-provided headers.
557
+ */
558
+ private async uploadInputArgumentsFileAsync(
559
+ inputArguments: string,
560
+ connection: ConnectionInfo,
561
+ logger?: IToolLogger,
562
+ ): Promise<string> {
563
+ const baseUrl = toRelativeUrl(connection.cloudUrl!);
564
+ const attachmentsUrl = `${baseUrl}/orchestrator_/odata/Attachments`;
565
+
566
+ const headers: Record<string, string> = {
567
+ "Content-Type": "application/json",
568
+ Authorization: `Bearer ${connection.accessToken}`,
569
+ "X-UIPATH-OrganizationUnitId": connection.folderId ?? "",
570
+ };
571
+ if (connection.tenantId) {
572
+ headers["x-uipath-tenantid"] = connection.tenantId;
573
+ }
574
+
575
+ const createResponse = await fetch(attachmentsUrl, {
576
+ method: "POST",
577
+ headers,
578
+ body: JSON.stringify({ name: "input.json" }),
579
+ });
580
+ if (!createResponse.ok) {
581
+ const errorText = await createResponse.text();
582
+ throw new Error(
583
+ translate.t(
584
+ "toolWorkflowCompilerBrowser.errors.createAttachmentFailed",
585
+ { status: createResponse.status, errorText },
586
+ ),
587
+ );
588
+ }
589
+
590
+ const attachment = (await createResponse.json()) as AttachmentResponse;
591
+ const attachmentId = attachment.Id ?? attachment.id ?? "";
592
+ const writeUri =
593
+ attachment.BlobFileAccess?.Uri ?? attachment.blobFileAccess?.Uri;
594
+ if (!attachmentId || !writeUri) {
595
+ throw new Error(
596
+ translate.t(
597
+ "toolWorkflowCompilerBrowser.errors.attachmentResponseMalformed",
598
+ ),
599
+ );
600
+ }
601
+
602
+ // PUT the raw bytes to the SAS blob URI. The Azure blob endpoint
603
+ // requires the BlockBlob type header; matches the proven contract in
604
+ // `@uipath/orchestrator-sdk`'s uploadJobAttachment.
605
+ const uploadResponse = await fetch(writeUri, {
606
+ method: "PUT",
607
+ headers: {
608
+ "Content-Type": "application/octet-stream",
609
+ "x-ms-blob-type": "BlockBlob",
610
+ },
611
+ body: inputArguments,
612
+ });
613
+ if (!uploadResponse.ok) {
614
+ throw new Error(
615
+ translate.t(
616
+ "toolWorkflowCompilerBrowser.errors.uploadInputArgumentsFailed",
617
+ {
618
+ status: uploadResponse.status,
619
+ statusText: uploadResponse.statusText,
620
+ },
621
+ ),
622
+ );
623
+ }
624
+
625
+ logger?.info(
626
+ `[BundleExecutor] Uploaded input arguments as attachment ${attachmentId}`,
627
+ );
628
+ return attachmentId;
629
+ }
630
+
487
631
  private resolveAll(session: BundleSession, result: ToolResult): void {
488
632
  for (const [, entry] of session.projects) {
489
633
  entry.resolve(result);