@uipath/solution-tool 1.202.0 → 1.203.0-preview.160

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.
Files changed (46) hide show
  1. package/dist/THIRD-PARTY-NOTICES.md +23 -1
  2. package/dist/deploy.js +7 -6
  3. package/dist/{packager-tool-bt0z25tw.js → embedded-file-reader-xxqe8vs8.js} +98 -16584
  4. package/dist/first-party-service-sd8yaf73.js +27 -0
  5. package/dist/index-dqa169gp.js +25 -0
  6. package/dist/index.js +19 -13
  7. package/dist/init.js +8 -5
  8. package/dist/{list-8hvfmge6.js → list-dadxggxt.js} +5 -4
  9. package/dist/models/pack-command-types.d.ts +33 -5
  10. package/dist/pack.js +12 -6
  11. package/dist/{packager-tool-fjjh5veg.js → packager-tool-0f0wt9vh.js} +353 -1519
  12. package/dist/{packager-tool-pbmpgz04.js → packager-tool-1haahq17.js} +5 -3
  13. package/dist/{packager-tool-rbrcxjch.js → packager-tool-2syrt51a.js} +7 -4
  14. package/dist/packager-tool-3jrq7smt.js +1459 -0
  15. package/dist/packager-tool-53w8skv5.js +262 -0
  16. package/dist/packager-tool-7znqtw3f.js +244 -0
  17. package/dist/packager-tool-bkwnetqn.js +16678 -0
  18. package/dist/{packager-tool-5518wy6n.js → packager-tool-cp07fhx3.js} +9 -6
  19. package/dist/{packager-tool-w1dzcj31.js → packager-tool-cvfbzs9p.js} +359 -95
  20. package/dist/packager-tool-gdtpsdn7.js +342 -0
  21. package/dist/{packager-tool-pyygbnp2.js → packager-tool-htag6yh5.js} +4 -4
  22. package/dist/{packager-tool-3bewrpq4.js → packager-tool-jz2wbfjz.js} +4 -1
  23. package/dist/packager-tool-k4mskzww.js +125 -0
  24. package/dist/{packager-tool-vfcht7hq.js → packager-tool-mj5p341c.js} +11 -12
  25. package/dist/{packager-tool-bme8epz8.js → packager-tool-n4nfqj99.js} +15381 -2421
  26. package/dist/{packager-tool-mcpzmn0v.js → packager-tool-rc5pcf2n.js} +45 -21
  27. package/dist/packager-tool-resolver-fmjr60x8.js +19 -0
  28. package/dist/{packager-tool-pw7v82j2.js → packager-tool-vas0xg5h.js} +1 -1
  29. package/dist/packager-tool-znakt6yw.js +188 -0
  30. package/dist/packager-tool.d.ts +1 -1
  31. package/dist/packager-tool.js +1 -1
  32. package/dist/prepare-solution-resources-wds3r8bq.js +22 -0
  33. package/dist/project-contributions-cvttys34.js +27 -0
  34. package/dist/publish.js +7 -6
  35. package/dist/resource.js +7 -4
  36. package/dist/services/deployment-validation.d.ts +33 -0
  37. package/dist/services/governance-options.d.ts +61 -1
  38. package/dist/services/pack-command-service.d.ts +33 -2
  39. package/dist/services/packager-tool-resolver.d.ts +3 -0
  40. package/dist/services/prepare-solution-resources.d.ts +40 -0
  41. package/dist/services/project-contributions.d.ts +58 -0
  42. package/dist/services/project-type-tools.d.ts +6 -0
  43. package/dist/templates/AGENTS.md +78 -8
  44. package/dist/tool.js +19 -13
  45. package/package.json +3 -2
  46. package/dist/services/validate-appv2-action-schemas.d.ts +0 -10
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getStudioWebAuthWithOrganizationId,
3
3
  useStudioWebAuth
4
- } from "./packager-tool-pbmpgz04.js";
4
+ } from "./packager-tool-1haahq17.js";
5
5
  import {
6
6
  activateDeploymentAsync,
7
7
  deployRunAsync,
@@ -11,11 +11,11 @@ import {
11
11
  listPackagesAsync,
12
12
  uninstallDeploymentAsync,
13
13
  upgradeDeploymentAsync
14
- } from "./packager-tool-mcpzmn0v.js";
14
+ } from "./packager-tool-rc5pcf2n.js";
15
15
  import {
16
16
  SolutionInitError,
17
17
  solutionInitAsync
18
- } from "./packager-tool-pw7v82j2.js";
18
+ } from "./packager-tool-vas0xg5h.js";
19
19
  import {
20
20
  GOVERNANCE_OPTION_INSTRUCTIONS,
21
21
  PackCommandService,
@@ -24,16 +24,22 @@ import {
24
24
  applyAuthConnection,
25
25
  checkSolutionPathAsync,
26
26
  createSolutionPackager,
27
- ensurePackagerTools,
28
- isUnpackableProjectTypeError,
29
27
  packSolutionAsync,
30
28
  readSolutionFileAsUint8Array,
31
29
  resolveSdkLogLevel,
32
30
  validateGovernanceOptions
33
- } from "./packager-tool-bme8epz8.js";
31
+ } from "./packager-tool-n4nfqj99.js";
32
+ import {
33
+ prepareSolutionResources
34
+ } from "./packager-tool-7znqtw3f.js";
35
+ import {
36
+ ensurePackagerTools,
37
+ isUnpackableProjectTypeError,
38
+ toolVerbForProjectType
39
+ } from "./packager-tool-k4mskzww.js";
34
40
  import {
35
41
  publishSolutionAsync
36
- } from "./packager-tool-pyygbnp2.js";
42
+ } from "./packager-tool-htag6yh5.js";
37
43
  import {
38
44
  Configuration,
39
45
  PipelinesApi,
@@ -41,7 +47,7 @@ import {
41
47
  feedScopeInitOverride,
42
48
  listSelectablePublishLocations,
43
49
  resolveFeedScope
44
- } from "./packager-tool-5518wy6n.js";
50
+ } from "./packager-tool-cp07fhx3.js";
45
51
  import {
46
52
  Configuration as Configuration2,
47
53
  DeploymentActivationStatus,
@@ -59,7 +65,7 @@ import {
59
65
  overwriteSolution,
60
66
  searchPackageVersions,
61
67
  solutionExistsOnStudioWeb
62
- } from "./packager-tool-vfcht7hq.js";
68
+ } from "./packager-tool-mj5p341c.js";
63
69
  import {
64
70
  Tokens,
65
71
  applyEntryPointEnhancementAsync,
@@ -71,25 +77,35 @@ import {
71
77
  findMatchingLocalResource,
72
78
  findProjectFile,
73
79
  findResourceByKeyInRcs,
80
+ hasAnyTemplateRow,
81
+ hasBareTemplateRow,
74
82
  isMissingProjectTypeError,
75
83
  isProjectManifestError,
76
84
  normalizeFolderPath,
85
+ normalizeResourceFileCasing,
77
86
  readProjectFile,
78
87
  readSolutionFile,
79
88
  resolveSolutionFilePath,
80
89
  resourceRefreshAsync,
90
+ resourceRemoteSyncAsync,
81
91
  restoreSolutionFile,
92
+ saveCasingNormalized,
82
93
  searchRcsResources,
83
- syncAndLog,
84
94
  syncResourcesFromBindings,
85
95
  writeSolutionFile
86
- } from "./packager-tool-bt0z25tw.js";
96
+ } from "./packager-tool-bkwnetqn.js";
87
97
  import {
88
98
  ToolResult
89
99
  } from "./packager-tool-nzghfa48.js";
90
100
  import {
91
101
  getSolutionAuthContext
92
- } from "./packager-tool-rbrcxjch.js";
102
+ } from "./packager-tool-2syrt51a.js";
103
+ import {
104
+ getLoginStatusAsync,
105
+ resolveSolutionDir,
106
+ toPortableRelativePath,
107
+ updateUipxSolutionId
108
+ } from "./packager-tool-3jrq7smt.js";
93
109
  import {
94
110
  DEFAULT_PAGE_SIZE,
95
111
  Option,
@@ -97,13 +113,13 @@ import {
97
113
  Pagination,
98
114
  PollOutcome,
99
115
  RESULTS,
116
+ carriedErrorCode,
100
117
  carriedInstructions,
101
118
  catchError,
102
119
  createHiddenDeprecatedTenantOption,
103
120
  extractErrorDetails,
104
121
  extractErrorMessage,
105
122
  getFileSystem,
106
- getLoginStatusAsync,
107
123
  isGuid,
108
124
  isPreviewBuild,
109
125
  logger,
@@ -115,14 +131,13 @@ import {
115
131
  processContext,
116
132
  registerPackageMetadataOptions,
117
133
  requireConfirmation,
134
+ resolveDeprecatedOptionAlias,
118
135
  resolveEnvReference,
119
- resolveSolutionDir,
120
- toPortableRelativePath,
121
136
  trackShipSucceeded,
122
137
  unsupportedSolutionProjectType,
123
- updateUipxSolutionId,
138
+ warnDeprecatedOptionAlias,
124
139
  withCompleter
125
- } from "./packager-tool-fjjh5veg.js";
140
+ } from "./packager-tool-0f0wt9vh.js";
126
141
  import {
127
142
  strFromU8,
128
143
  unzip,
@@ -134,8 +149,9 @@ import {
134
149
  // package.json
135
150
  var package_default = {
136
151
  name: "@uipath/solution-tool",
152
+ author: "UiPath",
137
153
  license: "SEE LICENSE IN LICENSE.txt",
138
- version: "1.202.0",
154
+ version: "1.203.0-preview.160",
139
155
  description: "Create, pack, publish, and deploy UiPath Automation Solutions.",
140
156
  repository: {
141
157
  type: "git",
@@ -198,7 +214,7 @@ var package_default = {
198
214
  "@uipath/packager-tool-connector": "workspace:*",
199
215
  "@uipath/pipelines-sdk": "workspace:*",
200
216
  "@uipath/project-executor": "workspace:*",
201
- "@uipath/resource-builder-sdk": "^2025.11.0-alpha4202-3459",
217
+ "@uipath/resource-builder-sdk": "2025.11.0-alpha5891-4020",
202
218
  "@uipath/resource-builder-tool": "2025.11.0-alpha4535-3530",
203
219
  "@uipath/solution-packager": "workspace:*",
204
220
  "@uipath/solution-sdk": "workspace:*",
@@ -1634,8 +1650,8 @@ async function writeMissingBriefingFilesAsync(solutionDir) {
1634
1650
  return written;
1635
1651
  }
1636
1652
 
1637
- // src/services/archive-extract.ts
1638
- function resolveZipEntryPath(fs, destinationDir, entryName) {
1653
+ // ../common/src/zip.ts
1654
+ function resolveZipEntryPath(fs, root, entryName) {
1639
1655
  if (entryName.includes("\x00")) {
1640
1656
  throw new Error(`Invalid zip entry path: ${entryName}`);
1641
1657
  }
@@ -1643,24 +1659,51 @@ function resolveZipEntryPath(fs, destinationDir, entryName) {
1643
1659
  if (normalizedEntry.startsWith("/")) {
1644
1660
  throw new Error(`Unsafe absolute zip entry path: ${entryName}`);
1645
1661
  }
1646
- const root = fs.path.resolve(destinationDir);
1647
1662
  const target = fs.path.resolve(root, normalizedEntry);
1648
- const relative = fs.path.relative(root, target);
1649
- if (relative.startsWith("..") || fs.path.isAbsolute(relative)) {
1663
+ if (!isUnder(fs, root, target)) {
1650
1664
  throw new Error(`Unsafe zip entry path: ${entryName}`);
1651
1665
  }
1652
1666
  return target;
1653
1667
  }
1668
+ function isUnder(fs, root, path) {
1669
+ const relative = fs.path.relative(root, path);
1670
+ return relative === "" || !relative.startsWith("..") && !fs.path.isAbsolute(relative);
1671
+ }
1672
+ async function assertNoEscapeThroughSymlink(fs, lexicalRoot, realRoot, path, entryName) {
1673
+ const resolved = await fs.realpath(path);
1674
+ if (isUnder(fs, realRoot, resolved) || isUnder(fs, lexicalRoot, resolved)) {
1675
+ return;
1676
+ }
1677
+ throw new Error(`Unsafe zip entry path (escapes via a symlink at the destination): ${entryName}`);
1678
+ }
1679
+ async function mkdirUnderRoot(fs, lexicalRoot, realRoot, dir, entryName) {
1680
+ const relative = fs.path.relative(lexicalRoot, dir);
1681
+ if (relative === "") {
1682
+ return;
1683
+ }
1684
+ let current = lexicalRoot;
1685
+ for (const segment of relative.split(/[\\/]/)) {
1686
+ if (segment === "") {
1687
+ continue;
1688
+ }
1689
+ current = fs.path.join(current, segment);
1690
+ await fs.mkdir(current);
1691
+ await assertNoEscapeThroughSymlink(fs, lexicalRoot, realRoot, current, entryName);
1692
+ }
1693
+ }
1654
1694
  async function extractZipDataToDir(fs, zipData, destinationDir) {
1655
1695
  await fs.mkdir(destinationDir);
1696
+ const lexicalRoot = fs.path.resolve(destinationDir);
1697
+ const realRoot = await fs.realpath(lexicalRoot);
1656
1698
  const entries = unzipSync(zipData);
1657
1699
  for (const [entryName, content] of Object.entries(entries)) {
1658
- const target = resolveZipEntryPath(fs, destinationDir, entryName);
1700
+ const target = resolveZipEntryPath(fs, lexicalRoot, entryName);
1659
1701
  if (entryName.endsWith("/")) {
1660
- await fs.mkdir(target);
1702
+ await mkdirUnderRoot(fs, lexicalRoot, realRoot, target, entryName);
1661
1703
  continue;
1662
1704
  }
1663
- await fs.mkdir(fs.path.dirname(target));
1705
+ await mkdirUnderRoot(fs, lexicalRoot, realRoot, fs.path.dirname(target), entryName);
1706
+ await assertNoEscapeThroughSymlink(fs, lexicalRoot, realRoot, target, entryName);
1664
1707
  await fs.writeFile(target, content);
1665
1708
  }
1666
1709
  }
@@ -1672,6 +1715,8 @@ async function extractArchive(archivePath, destinationDir) {
1672
1715
  }
1673
1716
  await extractZipDataToDir(fs, data, destinationDir);
1674
1717
  }
1718
+
1719
+ // src/services/archive-extract.ts
1675
1720
  var SOLUTION_STORAGE_ENTRY = "SolutionStorage.json";
1676
1721
  function isRootUipxEntry(name) {
1677
1722
  return !name.includes("/") && name.endsWith(".uipx");
@@ -1987,7 +2032,7 @@ var SOLUTION_PACK_EXAMPLES = [
1987
2032
  },
1988
2033
  {
1989
2034
  Description: "Analyze the RPA projects against the governance policy published in AutomationOps for the signed-in tenant. The value is the product whose policy is downloaded — pass StudioWeb for the default one.",
1990
- Command: "uip solution pack ./my-solution ./dist --version 1.2.0 --automation-ops-profile Development",
2035
+ Command: "uip solution pack ./my-solution ./dist --version 1.2.0 --governance-product Development",
1991
2036
  Output: {
1992
2037
  Code: "SolutionPack",
1993
2038
  Data: {
@@ -1998,7 +2043,7 @@ var SOLUTION_PACK_EXAMPLES = [
1998
2043
  }
1999
2044
  ];
2000
2045
  var registerPackCommand = (program) => {
2001
- const command = program.command("pack").description("Pack a UiPath solution into a deployable .zip package. Accepts a solution directory (containing .uipx) or a .uis file. Output is used with 'solution publish' to upload to UiPath. Pass --dry-run to check that the solution packs without producing a package.").argument("<solutionPath>", "Path to a solution directory (containing .uipx) or a .uis file").argument("[output-path]", "Output directory where the packed solution will be saved. Required unless --dry-run is set.").option("-n, --name <name>", "Package name (default: solution folder name or .uis file name)").option("-v, --version <version>", "Package version (default: 1.0.0)", "1.0.0").option("--nuget-sources-config-path <path>", "Path to NuGet sources configuration file").option("--exclude-configured-sources", "Resolve project dependencies only from the tenant feeds and any --nuget-sources-config-path. Drops the built-in UiPath and nuget.org feeds, so a dependency that is not on those feeds fails to restore.").option("--feed-folder <name>", "Resolve project dependencies from this Orchestrator folder's library feed instead of every tenant feed. Accepts a folder name or fully qualified path.").option("--signing-certificate-path <path>", "Path to .pfx certificate file used to sign each packed project .nupkg").option("--signing-certificate-password <password>", "Certificate password. Use env.ENV_NAME to read from an environment variable.").option("--signing-timestamp-server <url>", "Timestamp server URL").option("--skip-analyze", "Skip the workflow analyzer for every RPA project in the solution. Compilation and validation still run.").addOption(new Option("--governance-file-path <path>", "Analyze every RPA project against this local governance policy file, instead of the analyzer rules shipped with the WorkflowCompiler.").conflicts("automationOpsProfile")).addOption(new Option("--automation-ops-profile <profile>", "Analyze every RPA project against the governance policy published in AutomationOps for the signed-in tenant. The value is the product whose policy is downloaded, e.g. StudioWeb, Development, Business. Mutually exclusive with --governance-file-path.").conflicts("governanceFilePath"));
2046
+ const command = program.command("pack").description("Pack a UiPath solution into a deployable .zip package. Accepts a solution directory (containing .uipx) or a .uis file. Output is used with 'solution publish' to upload to UiPath. Pass --dry-run to check that the solution packs without producing a package.").argument("<solutionPath>", "Path to a solution directory (containing .uipx) or a .uis file").argument("[output-path]", "Output directory where the packed solution will be saved. Required unless --dry-run is set.").option("-n, --name <name>", "Package name (default: solution folder name or .uis file name)").option("-v, --version <version>", "Package version (default: 1.0.0)", "1.0.0").option("--nuget-sources-config-path <path>", "Path to NuGet sources configuration file").option("--exclude-configured-sources", "Resolve project dependencies only from the tenant feeds and any --nuget-sources-config-path. Drops the built-in UiPath and nuget.org feeds, so a dependency that is not on those feeds fails to restore.").option("--feed-folder <name>", "Resolve project dependencies from this Orchestrator folder's library feed instead of every tenant feed. Accepts a folder name or fully qualified path.").option("--signing-certificate-path <path>", "Path to .pfx certificate file used to sign each packed project .nupkg").option("--signing-certificate-password <password>", "Certificate password. Use env.ENV_NAME to read from an environment variable.").option("--signing-timestamp-server <url>", "Timestamp server URL").option("--skip-analyze", "Skip the workflow analyzer for every RPA project in the solution. Compilation and validation still run.").option("--governance-file-path <path>", "Analyze every RPA project against this local governance policy file, instead of the analyzer rules shipped with the WorkflowCompiler. When you are signed in the tenant is asked first — about StudioWeb unless --governance-product names another product — and this file is refused if the tenant publishes a policy of its own.").option("--governance-product <product>", "Analyze every RPA project against the governance policy published in AutomationOps for the signed-in tenant. The value is the product whose policy is downloaded, e.g. StudioWeb, Development, Business.").addOption(new Option("--automation-ops-profile <profile>").hideHelp());
2002
2047
  registerPackageMetadataOptions(command).option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).option("--dry-run", "Run the full pack pipeline against a temporary directory and discard the output. Catches pack-time failures; does not inspect the produced package contents.").examples(SOLUTION_PACK_EXAMPLES).trackedAction(processContext, async (solutionPath, outputPath, options) => {
2003
2048
  if (!options.dryRun && outputPath === undefined) {
2004
2049
  OutputFormatter.error({
@@ -2009,9 +2054,27 @@ var registerPackCommand = (program) => {
2009
2054
  processContext.exit(1);
2010
2055
  return;
2011
2056
  }
2057
+ const product = resolveDeprecatedOptionAlias({
2058
+ preferredValue: options.governanceProduct,
2059
+ deprecatedValue: options.automationOpsProfile,
2060
+ preferredFlag: "--governance-product",
2061
+ deprecatedFlag: "--automation-ops-profile"
2062
+ });
2063
+ if (product.error) {
2064
+ OutputFormatter.error({
2065
+ Result: RESULTS.Failure,
2066
+ Message: product.error.message,
2067
+ Instructions: product.error.instructions
2068
+ });
2069
+ processContext.exit(1);
2070
+ return;
2071
+ }
2072
+ if (product.usedDeprecated) {
2073
+ warnDeprecatedOptionAlias("--automation-ops-profile", "--governance-product");
2074
+ }
2012
2075
  const governanceError = validateGovernanceOptions({
2013
2076
  governanceFilePath: options.governanceFilePath,
2014
- automationOpsProfile: options.automationOpsProfile
2077
+ governanceProduct: product.value
2015
2078
  });
2016
2079
  if (governanceError) {
2017
2080
  OutputFormatter.error({
@@ -2037,7 +2100,7 @@ var registerPackCommand = (program) => {
2037
2100
  signingTimestampServer: options.signingTimestampServer,
2038
2101
  skipAnalyze: options.skipAnalyze,
2039
2102
  governanceFilePath: options.governanceFilePath,
2040
- automationOpsProfile: options.automationOpsProfile,
2103
+ governanceProduct: product.value,
2041
2104
  ...mapPackageMetadataOptions(options)
2042
2105
  };
2043
2106
  await service.execute(solutionPath, packOptions, processContext);
@@ -2454,22 +2517,6 @@ var registerPackagesCommand = (program) => {
2454
2517
  // src/commands/project-add.ts
2455
2518
  import { randomUUID } from "node:crypto";
2456
2519
 
2457
- // src/services/appv2-manifest.ts
2458
- var WEB_APP_MANIFEST = "webAppManifest.json";
2459
- async function resolveAppV2SubType(projectDir) {
2460
- const fs = getFileSystem();
2461
- const manifestPath = fs.path.join(projectDir, WEB_APP_MANIFEST);
2462
- if (!await fs.exists(manifestPath))
2463
- return "Coded";
2464
- const [readError, raw] = await catchError(fs.readFile(manifestPath, "utf-8"));
2465
- if (readError || !raw)
2466
- return "Coded";
2467
- const [parseError, manifest] = catchError(() => JSON.parse(raw));
2468
- if (parseError)
2469
- return "Coded";
2470
- return manifest?.config?.isActionApp === true ? "CodedAction" : "Coded";
2471
- }
2472
-
2473
2520
  // src/commands/project-command-utils.ts
2474
2521
  function rejectUnsupportedProjectType(projectType, failureMessage) {
2475
2522
  const reason = unsupportedSolutionProjectType(projectType);
@@ -2490,6 +2537,17 @@ function projectManifestInstructions(fileName, readProjectError, verb) {
2490
2537
  }
2491
2538
  return `${readProjectError.message} If this is a coded app, scaffold it with 'uip codedapp init <project-path>' — that writes the project.uiproj this command registers — then run 'uip solution projects ${verb} <project-path>' again.`;
2492
2539
  }
2540
+ function resourcesPendingForProjectType(projectType) {
2541
+ return !hasBareTemplateRow(projectType) && hasAnyTemplateRow(projectType);
2542
+ }
2543
+ function hostGeneratesResources(projectType) {
2544
+ return hasBareTemplateRow(projectType);
2545
+ }
2546
+ function resourcesPendingWarning(projectType) {
2547
+ const verb = toolVerbForProjectType(projectType);
2548
+ const needs = verb ? `that needs the '${verb}' tool installed ('uip tools install ${verb}')` : "that needs the tool that owns this project type installed";
2549
+ return `No solution resources were generated for this project yet. Run 'uip solution resources refresh' to create them — ${needs}. 'uip solution pack' and 'uip solution upload' do it too.`;
2550
+ }
2493
2551
 
2494
2552
  // src/commands/project-add.ts
2495
2553
  var PROJECT_ADD_EXAMPLES = [
@@ -2501,7 +2559,8 @@ var PROJECT_ADD_EXAMPLES = [
2501
2559
  Data: {
2502
2560
  Status: "Added successfully",
2503
2561
  Project: "my-project/project.uiproj",
2504
- Solution: "/workspace/my-solution/my-solution.uipx"
2562
+ Solution: "/workspace/my-solution/my-solution.uipx",
2563
+ ResourcesPending: false
2505
2564
  }
2506
2565
  }
2507
2566
  }
@@ -2625,8 +2684,8 @@ var registerProjectAddCommand = (project) => {
2625
2684
  processContext.exit(1);
2626
2685
  return;
2627
2686
  }
2628
- const projectSubType = parsedProject.projectType === "AppV2" ? await resolveAppV2SubType(resolvedProjectPath) : undefined;
2629
- const createError = await createProjectInSolutionBuilder(solutionDir, projectId, projectName, parsedProject.projectType, projectSubType);
2687
+ const resourcesPending = resourcesPendingForProjectType(parsedProject.projectType);
2688
+ const createError = hostGeneratesResources(parsedProject.projectType) ? await createProjectInSolutionBuilder(solutionDir, projectId, projectName, parsedProject.projectType) : null;
2630
2689
  if (createError) {
2631
2690
  const restoreError = await restoreSolutionFile(resolvedSolutionFile, originalSolutionContent);
2632
2691
  OutputFormatter.error({
@@ -2644,13 +2703,17 @@ Rollback failed: ${restoreError.message}` : createError.message
2644
2703
  logger.warn(warning);
2645
2704
  }
2646
2705
  }
2706
+ if (resourcesPending) {
2707
+ logger.warn(resourcesPendingWarning(parsedProject.projectType));
2708
+ }
2647
2709
  OutputFormatter.success({
2648
2710
  Result: RESULTS.Success,
2649
2711
  Code: "ProjectAdd",
2650
2712
  Data: {
2651
2713
  Status: "Added successfully",
2652
2714
  Project: projectRelativePath,
2653
- Solution: resolvedSolutionFile
2715
+ Solution: resolvedSolutionFile,
2716
+ ResourcesPending: resourcesPending
2654
2717
  }
2655
2718
  });
2656
2719
  });
@@ -2667,7 +2730,8 @@ var PROJECT_IMPORT_EXAMPLES = [
2667
2730
  Data: {
2668
2731
  Status: "Imported successfully",
2669
2732
  Project: "external-project/project.uiproj",
2670
- Solution: "/workspace/my-solution/my-solution.uipx"
2733
+ Solution: "/workspace/my-solution/my-solution.uipx",
2734
+ ResourcesPending: false
2671
2735
  }
2672
2736
  }
2673
2737
  }
@@ -2811,8 +2875,8 @@ var registerProjectImportCommand = (project) => {
2811
2875
  await catchError(fs.rm(targetProjectPath));
2812
2876
  await catchError(fs.writeFile(resolvedSolutionFile, originalSolutionContent));
2813
2877
  };
2814
- const projectSubType = parsedProject.projectType === "AppV2" ? await resolveAppV2SubType(targetProjectPath) : undefined;
2815
- const createError = await createProjectInSolutionBuilder(solutionDir, projectId, projectName, parsedProject.projectType, projectSubType);
2878
+ const resourcesPending = resourcesPendingForProjectType(parsedProject.projectType);
2879
+ const createError = hostGeneratesResources(parsedProject.projectType) ? await createProjectInSolutionBuilder(solutionDir, projectId, projectName, parsedProject.projectType) : null;
2816
2880
  if (createError) {
2817
2881
  await rollback();
2818
2882
  OutputFormatter.error({
@@ -2827,13 +2891,17 @@ var registerProjectImportCommand = (project) => {
2827
2891
  for (const warning of syncResult.warnings) {
2828
2892
  logger.warn(warning);
2829
2893
  }
2894
+ if (resourcesPending) {
2895
+ logger.warn(resourcesPendingWarning(parsedProject.projectType));
2896
+ }
2830
2897
  OutputFormatter.success({
2831
2898
  Result: RESULTS.Success,
2832
2899
  Code: "ProjectImport",
2833
2900
  Data: {
2834
2901
  Status: "Imported successfully",
2835
2902
  Project: projectRelativePath,
2836
- Solution: resolvedSolutionFile
2903
+ Solution: resolvedSolutionFile,
2904
+ ResourcesPending: resourcesPending
2837
2905
  }
2838
2906
  });
2839
2907
  });
@@ -3753,6 +3821,7 @@ var registerResourceAddCommand = (resource) => {
3753
3821
  processContext.exit(1);
3754
3822
  return;
3755
3823
  }
3824
+ await normalizeResourceFileCasing(resolvedPath);
3756
3825
  const [builderError, builder] = await catchError(services.getSolutionBuilder(resolvedPath));
3757
3826
  if (builderError) {
3758
3827
  OutputFormatter.error({
@@ -3798,7 +3867,10 @@ var registerResourceAddCommand = (resource) => {
3798
3867
  Data: outcome.data
3799
3868
  });
3800
3869
  } finally {
3801
- const [disposeError] = await catchError(builder.disposeAsync());
3870
+ const disposeError = await saveCasingNormalized(builder);
3871
+ if (!disposeError) {
3872
+ await normalizeResourceFileCasing(resolvedPath);
3873
+ }
3802
3874
  if (disposeError) {
3803
3875
  OutputFormatter.error({
3804
3876
  Result: RESULTS.Failure,
@@ -4062,7 +4134,25 @@ function toResourceAddData(resource, folders, source, status) {
4062
4134
  }
4063
4135
 
4064
4136
  // src/commands/resource-edit.ts
4065
- var GUID_REGEX2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
4137
+ var RESOURCE_KEY_REGEX = /^[A-Za-z0-9._:-]{1,256}$/;
4138
+ var REMOTE_FAILURE_MESSAGE = {
4139
+ solution_not_found: "Failed to edit resource",
4140
+ not_a_solution: "Failed to edit resource",
4141
+ auth_failed: "Failed to authenticate",
4142
+ resource_not_found: "Resource not found in solution",
4143
+ no_remote_reference: "Resource has no cloud definition to sync from",
4144
+ comparison_unavailable: "Could not read the cloud definition to compare against",
4145
+ sync_failed: "Failed to sync resource from the cloud definition",
4146
+ save_failed: "Resource edit completed but the solution could not be saved"
4147
+ };
4148
+ var REMOTE_FAILURE_CODE = {
4149
+ solution_not_found: "not_found",
4150
+ not_a_solution: "invalid_argument",
4151
+ auth_failed: "authentication_required",
4152
+ resource_not_found: "not_found",
4153
+ no_remote_reference: "invalid_argument",
4154
+ comparison_unavailable: "not_found"
4155
+ };
4066
4156
  var NOT_FOUND_PATTERN = /not found/i;
4067
4157
  var RESOURCE_EDIT_EXAMPLES = [
4068
4158
  {
@@ -4107,10 +4197,36 @@ var RESOURCE_EDIT_EXAMPLES = [
4107
4197
  apiVersion: "orchestrator.uipath.com/v1"
4108
4198
  }
4109
4199
  }
4200
+ },
4201
+ {
4202
+ Description: "Show what the cloud definition would overwrite, without writing (no --force)",
4203
+ Command: "uip solution resources edit 8f3a1b2c-... --source remote",
4204
+ Output: {
4205
+ Code: "ResourceRemoteDrift",
4206
+ Data: {
4207
+ Key: "8f3a1b2c-1234-4abc-9def-0123456789ab",
4208
+ Changed: ["description", "fields"],
4209
+ Applied: false,
4210
+ Status: "2 properties differ from the cloud definition. Re-run with --force to overwrite the local values."
4211
+ }
4212
+ }
4213
+ },
4214
+ {
4215
+ Description: "Pull the current cloud definition over the local one (overwrites local edits)",
4216
+ Command: "uip solution resources edit 8f3a1b2c-... --source remote --force",
4217
+ Output: {
4218
+ Code: "ResourceEdited",
4219
+ Data: {
4220
+ Key: "8f3a1b2c-1234-4abc-9def-0123456789ab",
4221
+ Changed: ["fields"],
4222
+ Applied: true,
4223
+ Status: "Overwrote 1 property with the cloud definition"
4224
+ }
4225
+ }
4110
4226
  }
4111
4227
  ];
4112
4228
  var registerResourceEditCommand = (resource) => {
4113
- resource.command("edit").description("Edit a resource's spec properties by key. Pass --patch with a JSON object " + "of property → value; the JSON is merged into the existing spec. Properties " + "the SDK metadata marks as reference/read-only/unknown are silently ignored. " + "Use 'uip solution resources get <key>' to inspect the current spec; the " + "get↔edit shapes match, so a round-trip is straightforward.").argument("<resource-key>", "Solution resource key (GUID)").option("--patch <json>", `JSON object of spec property → value (e.g. '{"retentionPeriod":14}'). Pass '-' to read the JSON from stdin.`).option("--solution-folder <path>", "Path to the solution root folder (defaults to current directory)").examples(RESOURCE_EDIT_EXAMPLES).trackedAction(processContext, async (resourceKey, options) => {
4229
+ resource.command("edit").description("Edit a resource's spec properties by key. Pass --patch with a JSON object " + "of property → value; the JSON is merged into the existing spec. Properties " + "the SDK metadata marks as reference/read-only/unknown are silently ignored. " + "Use 'uip solution resources get <key>' to inspect the current spec; the " + "get↔edit shapes match, so a round-trip is straightforward. " + "Pass --source remote instead of --patch to take the values from the cloud " + "definition the resource was imported from — the only way to pick up a change " + "made in Orchestrator or Data Fabric after the import, since 'resources refresh' " + "never overwrites an imported resource.").argument("<resource-key>", "Solution resource key (a GUID, or a compound key such as MyFunction.api.MyFunction.1:1.0.0)").option("--patch <json>", `JSON object of spec property → value (e.g. '{"retentionPeriod":14}'). Pass '-' to read the JSON from stdin.`).option("--source <source>", "Where the new values come from: 'local' (default, use --patch) or 'remote' (the cloud definition this resource was imported from)", "local").option("--force", "With --source remote, write the cloud definition over the local one. Without it the command only reports what would change and writes nothing.").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10). Only used by --source remote.", "10").option("--solution-folder <path>", "Path to the solution root folder (defaults to current directory)").examples(RESOURCE_EDIT_EXAMPLES).trackedAction(processContext, async (resourceKey, options) => {
4114
4230
  if (!resourceKey || resourceKey.trim().length === 0) {
4115
4231
  OutputFormatter.error({
4116
4232
  Result: RESULTS.Failure,
@@ -4120,15 +4236,53 @@ var registerResourceEditCommand = (resource) => {
4120
4236
  processContext.exit(1);
4121
4237
  return;
4122
4238
  }
4123
- if (!GUID_REGEX2.test(resourceKey)) {
4239
+ if (!RESOURCE_KEY_REGEX.test(resourceKey)) {
4124
4240
  OutputFormatter.error({
4125
4241
  Result: RESULTS.Failure,
4126
- Message: "<resource-key> must be a GUID",
4127
- Instructions: "Resource keys are 8-4-4-4-12 hex GUIDs. Use 'uip solution resources list' to find a valid key."
4242
+ Message: "<resource-key> contains unsupported characters",
4243
+ Instructions: "Resource keys use letters, digits, and '. _ - :' only. Use 'uip solution resources list' to find a valid key."
4128
4244
  });
4129
4245
  processContext.exit(1);
4130
4246
  return;
4131
4247
  }
4248
+ const source = (options.source ?? "local").toLowerCase();
4249
+ if (source !== "local" && source !== "remote") {
4250
+ OutputFormatter.error({
4251
+ Result: RESULTS.Failure,
4252
+ Message: `Invalid --source value "${options.source}"`,
4253
+ Instructions: "Use --source local (with --patch) or --source remote.",
4254
+ ErrorCode: "invalid_argument",
4255
+ Retry: "RetryWillNotFix"
4256
+ });
4257
+ processContext.exit(1);
4258
+ return;
4259
+ }
4260
+ if (source === "local" && options.force === true) {
4261
+ OutputFormatter.error({
4262
+ Result: RESULTS.Failure,
4263
+ Message: "--force only applies to --source remote",
4264
+ Instructions: "Drop --force, or add --source remote to overwrite the local spec with the cloud definition.",
4265
+ ErrorCode: "invalid_argument",
4266
+ Retry: "RetryWillNotFix"
4267
+ });
4268
+ processContext.exit(1);
4269
+ return;
4270
+ }
4271
+ if (source === "remote") {
4272
+ if (options.patch !== undefined) {
4273
+ OutputFormatter.error({
4274
+ Result: RESULTS.Failure,
4275
+ Message: "--patch cannot be combined with --source remote",
4276
+ Instructions: "Drop --patch to take the values from the cloud definition, or drop --source remote to apply your own values.",
4277
+ ErrorCode: "invalid_argument",
4278
+ Retry: "RetryWillNotFix"
4279
+ });
4280
+ processContext.exit(1);
4281
+ return;
4282
+ }
4283
+ await handleRemoteSourceAsync(resourceKey, options);
4284
+ return;
4285
+ }
4132
4286
  const [parseError, properties] = await catchError(buildProperties(options));
4133
4287
  if (parseError) {
4134
4288
  OutputFormatter.error({
@@ -4179,6 +4333,7 @@ var registerResourceEditCommand = (resource) => {
4179
4333
  processContext.exit(1);
4180
4334
  return;
4181
4335
  }
4336
+ await normalizeResourceFileCasing(resolvedPath);
4182
4337
  const [builderError, builder] = await catchError(services.getSolutionBuilder(resolvedPath));
4183
4338
  if (builderError) {
4184
4339
  OutputFormatter.error({
@@ -4209,7 +4364,10 @@ var registerResourceEditCommand = (resource) => {
4209
4364
  Data: config
4210
4365
  });
4211
4366
  } finally {
4212
- const [disposeError] = await catchError(builder.disposeAsync());
4367
+ const disposeError = await saveCasingNormalized(builder);
4368
+ if (!disposeError) {
4369
+ await normalizeResourceFileCasing(resolvedPath);
4370
+ }
4213
4371
  if (disposeError) {
4214
4372
  OutputFormatter.error({
4215
4373
  Result: RESULTS.Failure,
@@ -4221,6 +4379,62 @@ var registerResourceEditCommand = (resource) => {
4221
4379
  }
4222
4380
  });
4223
4381
  };
4382
+ async function handleRemoteSourceAsync(resourceKey, options) {
4383
+ const result = await resourceRemoteSyncAsync(options.solutionFolder ?? ".", resourceKey, {
4384
+ force: options.force === true,
4385
+ loginValidity: options.loginValidity ? parseInt(options.loginValidity, 10) : undefined
4386
+ });
4387
+ if (!result.ok) {
4388
+ OutputFormatter.error({
4389
+ Result: RESULTS.Failure,
4390
+ Message: REMOTE_FAILURE_MESSAGE[result.reason],
4391
+ Instructions: result.reason === "resource_not_found" ? `No resource with key ${resourceKey}. Use 'uip solution resources list' to see available keys.` : result.message,
4392
+ ErrorCode: REMOTE_FAILURE_CODE[result.reason],
4393
+ Retry: result.reason === "sync_failed" ? undefined : "RetryWillNotFix"
4394
+ });
4395
+ processContext.exit(1);
4396
+ return;
4397
+ }
4398
+ const count = result.changed.length;
4399
+ if (!result.applied) {
4400
+ if (count === 0) {
4401
+ OutputFormatter.success({
4402
+ Result: RESULTS.Success,
4403
+ Code: "ResourceRemoteDrift",
4404
+ Data: {
4405
+ Key: result.key,
4406
+ Changed: [],
4407
+ Applied: false,
4408
+ Status: "Already matches the cloud definition — nothing to overwrite"
4409
+ }
4410
+ });
4411
+ return;
4412
+ }
4413
+ OutputFormatter.error({
4414
+ Result: RESULTS.Failure,
4415
+ Message: `${count} ${count === 1 ? "property differs" : "properties differ"} from the cloud definition: ${result.changed.join(", ")}`,
4416
+ Instructions: `Overwriting discards the local values of ${count === 1 ? "that property" : "those properties"}. Re-run with --force to apply: uip solution resources edit ${result.key} --source remote --force`,
4417
+ ErrorCode: "invalid_argument",
4418
+ Retry: "RetryWillNotFix"
4419
+ });
4420
+ processContext.exit(1);
4421
+ return;
4422
+ }
4423
+ const skipped = result.skipped.length;
4424
+ const base = count === 0 ? "Already matched the cloud definition — nothing changed" : `Overwrote ${count} ${count === 1 ? "property" : "properties"} with the cloud definition`;
4425
+ OutputFormatter.success({
4426
+ Result: RESULTS.Success,
4427
+ Code: "ResourceEdited",
4428
+ Data: {
4429
+ Key: result.key,
4430
+ Changed: result.changed,
4431
+ Skipped: result.skipped,
4432
+ Applied: true,
4433
+ Status: skipped === 0 ? base : `${base}. ${skipped} ${skipped === 1 ? "property" : "properties"} could not be written and still differ (${result.skipped.join(", ")}) — the SDK does not let this resource kind set ${skipped === 1 ? "it" : "them"}.`,
4434
+ Spec: result.spec
4435
+ }
4436
+ });
4437
+ }
4224
4438
  async function buildProperties(options) {
4225
4439
  if (options.patch === undefined) {
4226
4440
  return {};
@@ -4850,8 +5064,9 @@ var RESOURCE_REFRESH_EXAMPLES = [
4850
5064
  Imported: 1,
4851
5065
  Skipped: 5,
4852
5066
  Pruned: 0,
5067
+ ReconciledSpecs: 1,
4853
5068
  Warnings: [],
4854
- Status: "Synced 3 resources (5 already in solution)"
5069
+ Status: "Synced 3 resources (5 already in solution). Refresh never overwrites an imported resource — to pull a changed cloud definition, run 'uip solution resources edit <resource-key> --source remote'."
4855
5070
  }
4856
5071
  }
4857
5072
  }
@@ -4863,9 +5078,33 @@ var FAILURE_MESSAGE = {
4863
5078
  sync_failed: "Failed to refresh resources"
4864
5079
  };
4865
5080
  var registerResourceRefreshSubcommand = (resource) => {
4866
- resource.command("refresh").description("Re-scan all projects in the solution and sync resource declarations from their bindings_v2.json files. " + "Creates new resources for bindings not yet in the solution, imports from Orchestrator when a matching resource exists, " + "and removes duplicate copies of the solution's own projects that earlier runs imported from a deployed folder. " + "Run this after editing project bindings to update the solution resource inventory.").option("--solution-folder <path>", "Path to the solution root folder (defaults to current directory)").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", "10").examples(RESOURCE_REFRESH_EXAMPLES).trackedAction(processContext, async (options) => {
5081
+ resource.command("refresh").description("Re-scan all projects in the solution and sync each project's authored files into the solution resource JSONs. " + "Bindings: creates new resources for `bindings_v2.json` entries not yet in the solution, imports from Orchestrator when a matching resource exists, and removes duplicate copies of the solution's own projects that earlier runs imported from a deployed folder. " + "Project contributions: each project's owning tool reports what its authored files add to the solution resources, and refresh persists it onto the matching resource. " + "Run this after editing a project's bindings or any file the project's tool contributes from, to update the solution resource inventory. " + "Refresh is import-only for cloud resources: it never overwrites an imported resource, so local edits survive; a project's own artefact resources are rewritten from the project's files on every run. " + "To pull a cloud definition that changed after the import, use 'uip solution resources edit <resource-key> --source remote'.").option("--solution-folder <path>", "Path to the solution root folder (defaults to current directory)").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", "10").examples(RESOURCE_REFRESH_EXAMPLES).trackedAction(processContext, async (options) => {
4867
5082
  const loginValidityMinutes = options.loginValidity ? parseInt(options.loginValidity, 10) : undefined;
4868
- const result = await resourceRefreshAsync(options.solutionFolder ?? ".", { loginValidity: loginValidityMinutes });
5083
+ const solutionFolder = options.solutionFolder ?? ".";
5084
+ const fs = getFileSystem();
5085
+ let contributions;
5086
+ const [
5087
+ { ensurePackagerTools: ensurePackagerTools2 },
5088
+ { projectSubTypesFrom },
5089
+ {
5090
+ applyProjectContributions,
5091
+ collectProjectContributions,
5092
+ contributionErrorMessages
5093
+ }
5094
+ ] = await Promise.all([
5095
+ import("./packager-tool-resolver-fmjr60x8.js"),
5096
+ import("./prepare-solution-resources-wds3r8bq.js"),
5097
+ import("./project-contributions-cvttys34.js")
5098
+ ]);
5099
+ const resolveProjectSubTypesAsync = async () => {
5100
+ await ensurePackagerTools2(solutionFolder, fs);
5101
+ contributions = await collectProjectContributions(solutionFolder, fs);
5102
+ return projectSubTypesFrom(contributions);
5103
+ };
5104
+ const result = await resourceRefreshAsync(solutionFolder, {
5105
+ loginValidity: loginValidityMinutes,
5106
+ resolveProjectSubTypesAsync
5107
+ });
4869
5108
  if (!result.ok) {
4870
5109
  OutputFormatter.error({
4871
5110
  Result: RESULTS.Failure,
@@ -4876,6 +5115,26 @@ var registerResourceRefreshSubcommand = (resource) => {
4876
5115
  return;
4877
5116
  }
4878
5117
  const prunedNote = result.pruned > 0 ? `, ${result.pruned} duplicates removed` : "";
5118
+ const skippedNote = result.skipped > 0 ? " Refresh never overwrites an imported resource — to pull a changed cloud definition, run 'uip solution resources edit <resource-key> --source remote'." : "";
5119
+ let applied = { updated: 0, warnings: [] };
5120
+ if (contributions) {
5121
+ const [applyError, written] = await catchError(applyProjectContributions(solutionFolder, contributions, undefined, fs));
5122
+ if (applyError) {
5123
+ OutputFormatter.error({
5124
+ Result: RESULTS.Failure,
5125
+ Message: FAILURE_MESSAGE.sync_failed,
5126
+ Instructions: `Bindings synced, but the project contributions could not be written to the solution resources: ${applyError.message}. Fix the reported problem, then re-run 'uip solution resources refresh'.`
5127
+ });
5128
+ processContext.exit(1);
5129
+ return;
5130
+ }
5131
+ applied = written;
5132
+ }
5133
+ const warnings = [
5134
+ ...result.warnings,
5135
+ ...contributions ? contributionErrorMessages(contributions) : [],
5136
+ ...applied.warnings
5137
+ ];
4879
5138
  OutputFormatter.success({
4880
5139
  Result: RESULTS.Success,
4881
5140
  Code: "ResourceRefresh",
@@ -4884,15 +5143,16 @@ var registerResourceRefreshSubcommand = (resource) => {
4884
5143
  Imported: result.imported,
4885
5144
  Skipped: result.skipped,
4886
5145
  Pruned: result.pruned,
4887
- Warnings: result.warnings,
4888
- Status: `Synced ${result.created + result.imported} resources (${result.skipped} already in solution${prunedNote})`
5146
+ ReconciledSpecs: applied.updated,
5147
+ Warnings: warnings,
5148
+ Status: `Synced ${result.created + result.imported} resources (${result.skipped} already in solution${prunedNote}).${skippedNote}`
4889
5149
  }
4890
5150
  });
4891
5151
  });
4892
5152
  };
4893
5153
 
4894
5154
  // src/commands/resource-remove.ts
4895
- var GUID_REGEX3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
5155
+ var RESOURCE_KEY_REGEX2 = /^[A-Za-z0-9._:-]{1,256}$/;
4896
5156
  var RESOURCE_REMOVE_EXAMPLES = [
4897
5157
  {
4898
5158
  Description: "Remove a resource by its solution key",
@@ -4909,7 +5169,7 @@ var RESOURCE_REMOVE_EXAMPLES = [
4909
5169
  }
4910
5170
  ];
4911
5171
  var registerResourceRemoveCommand = (resource) => {
4912
- resource.command("remove").description("Remove a resource from the solution by its key. Use 'uip solution resources list' " + "or 'uip solution resources get' to discover the key first. " + "Purely local — no auth required, doesn't touch any bindings_v2.json.").argument("<resource-key>", "Solution resource key (GUID)").option("--solution-folder <path>", "Path to the solution root folder (defaults to current directory)").examples(RESOURCE_REMOVE_EXAMPLES).trackedAction(processContext, async (resourceKey, options) => {
5172
+ resource.command("remove").description("Remove a resource from the solution by its key. Use 'uip solution resources list' " + "or 'uip solution resources get' to discover the key first. " + "Purely local — no auth required, doesn't touch any bindings_v2.json.").argument("<resource-key>", "Solution resource key (a GUID, or a compound key such as MyFunction.api.MyFunction.1:1.0.0)").option("--solution-folder <path>", "Path to the solution root folder (defaults to current directory)").examples(RESOURCE_REMOVE_EXAMPLES).trackedAction(processContext, async (resourceKey, options) => {
4913
5173
  if (!resourceKey || resourceKey.trim().length === 0) {
4914
5174
  OutputFormatter.error({
4915
5175
  Result: RESULTS.Failure,
@@ -4919,11 +5179,11 @@ var registerResourceRemoveCommand = (resource) => {
4919
5179
  processContext.exit(1);
4920
5180
  return;
4921
5181
  }
4922
- if (!GUID_REGEX3.test(resourceKey)) {
5182
+ if (!RESOURCE_KEY_REGEX2.test(resourceKey)) {
4923
5183
  OutputFormatter.error({
4924
5184
  Result: RESULTS.Failure,
4925
- Message: "<resource-key> must be a GUID",
4926
- Instructions: "Resource keys are 8-4-4-4-12 hex GUIDs. Use 'uip solution resources list' to find a valid key."
5185
+ Message: "<resource-key> contains unsupported characters",
5186
+ Instructions: "Resource keys use letters, digits, and '. _ - :' only. Use 'uip solution resources list' to find a valid key."
4927
5187
  });
4928
5188
  processContext.exit(1);
4929
5189
  return;
@@ -4959,6 +5219,7 @@ var registerResourceRemoveCommand = (resource) => {
4959
5219
  processContext.exit(1);
4960
5220
  return;
4961
5221
  }
5222
+ await normalizeResourceFileCasing(resolvedPath);
4962
5223
  const [builderError, builder] = await catchError(services.getSolutionBuilder(resolvedPath));
4963
5224
  if (builderError) {
4964
5225
  OutputFormatter.error({
@@ -5006,7 +5267,10 @@ var registerResourceRemoveCommand = (resource) => {
5006
5267
  Data: captured
5007
5268
  });
5008
5269
  } finally {
5009
- const [disposeError] = await catchError(builder.disposeAsync());
5270
+ const disposeError = await saveCasingNormalized(builder);
5271
+ if (!disposeError) {
5272
+ await normalizeResourceFileCasing(resolvedPath);
5273
+ }
5010
5274
  if (disposeError) {
5011
5275
  OutputFormatter.error({
5012
5276
  Result: RESULTS.Failure,
@@ -5895,7 +6159,22 @@ var registerUploadCommand = (program) => {
5895
6159
  uisPath = resolvedPath;
5896
6160
  } else if (isDirectory || isUipxFile) {
5897
6161
  const solutionDir = resolveSolutionDir(fs, resolvedPath);
5898
- await syncAndLog(solutionDir);
6162
+ const [prepareError] = await catchError(prepareSolutionResources(solutionDir, fs, {
6163
+ gate: true
6164
+ }));
6165
+ if (prepareError) {
6166
+ const errorCode = carriedErrorCode(prepareError) ?? (isUnpackableProjectTypeError(prepareError) ? "invalid_argument" : undefined);
6167
+ const contributionCode = prepareError.contributionCode;
6168
+ OutputFormatter.error({
6169
+ Result: RESULTS.Failure,
6170
+ Message: prepareError.message,
6171
+ ...errorCode ? { ErrorCode: errorCode } : {},
6172
+ ...typeof contributionCode === "string" ? { Context: { errorCode: contributionCode } } : {},
6173
+ Instructions: carriedInstructions(prepareError) ?? "Fix the reported problem in the project's files, then re-run the upload."
6174
+ });
6175
+ processContext.exit(1);
6176
+ return;
6177
+ }
5899
6178
  tempDir = await fs.getTempDir();
5900
6179
  const [bundleError, bundleResult] = await catchError(bundleSolution(fs, solutionDir, tempDir));
5901
6180
  if (bundleError) {
@@ -6048,27 +6327,17 @@ function httpStatusOf(error) {
6048
6327
  }
6049
6328
  return;
6050
6329
  }
6051
- function probeAuthChallengeOf(error) {
6052
- if (error instanceof Error && "authChallenge" in error) {
6053
- const challenge = error.authChallenge;
6054
- if (challenge && typeof challenge === "object") {
6055
- return challenge;
6056
- }
6057
- }
6058
- return {};
6059
- }
6060
6330
  function isSolutionNotFoundError(error) {
6061
6331
  return httpStatusOf(error) === 404;
6062
6332
  }
6063
6333
  async function buildProbeFailureOutput(probeError, solutionId, identity) {
6064
6334
  const status = httpStatusOf(probeError);
6065
6335
  const isAuthRefusal = status === 401 || status === 403;
6066
- const challenge = probeAuthChallengeOf(probeError);
6067
6336
  const message = isAuthRefusal ? probeError.message : await extractErrorMessage(probeError);
6068
6337
  const output = {
6069
6338
  Result: RESULTS.Failure,
6070
6339
  Message: `Failed to check whether solution ${solutionId} exists on Studio Web: ${message}`,
6071
- Instructions: isAuthRefusal ? probeAuthInstructions(solutionId, identity, challenge) : "Verify your login (uip login) and tenant access, then retry. If the problem persists, contact your UiPath administrator."
6340
+ Instructions: isAuthRefusal ? probeAuthInstructions(solutionId, identity) : "Verify your login (uip login) and tenant access, then retry. If the problem persists, contact your UiPath administrator."
6072
6341
  };
6073
6342
  if (status !== undefined) {
6074
6343
  output.Context = {
@@ -6077,19 +6346,14 @@ async function buildProbeFailureOutput(probeError, solutionId, identity) {
6077
6346
  method: "GET"
6078
6347
  };
6079
6348
  }
6080
- if (status === 401 && identity?.type === "Application" && challenge.error !== "invalid_token") {
6349
+ if (status === 401 && identity?.type === "Application") {
6081
6350
  output.ErrorCode = "permission_denied";
6082
6351
  }
6083
6352
  return output;
6084
6353
  }
6085
- function probeAuthInstructions(solutionId, identity, challenge) {
6086
- const quoted = challenge.description ? ` ("${challenge.description}")` : "";
6087
- if (challenge.error === "invalid_token") {
6088
- return `Studio Web rejected the credential itself with the reason ` + `\`invalid_token\`${quoted} while checking whether solution ` + `${solutionId} exists. That is the platform refusing this token ` + `rather than the grants behind it, so unlike a grants refusal this ` + `one is worth a fresh 'uip login' — or, in a pipeline, a newly ` + `minted client-credentials token. Check too that the token was ` + `issued for the organization and tenant being called. Nothing was ` + `uploaded.`;
6089
- }
6354
+ function probeAuthInstructions(solutionId, identity) {
6090
6355
  const actor = identity?.type === "Application" ? `This session acts as external application ${identity.clientId ?? "(client id unknown)"}: it needs a Studio Web grant that can read a solution.` : identity?.type === "User" ? "This session acts as a user: check that your account has Studio Web access in this tenant." : "Check that this session may read a solution in Studio Web — an external application needs a StudioWebS2S grant, a user needs Studio Web access in the tenant.";
6091
- const reported = challenge.error === "insufficient_scope" ? `Studio Web reported \`insufficient_scope\`${quoted}, so the token is good and its grants are not. ` : "";
6092
- return `Studio Web refused the read, and the session is authenticated, so ` + `re-running 'uip login' mints the same token and fails the same way. ` + `${reported}${actor} Also confirm solution ${solutionId} lives in the organization ` + `and tenant this session logged into. Nothing was uploaded.`;
6356
+ return `Studio Web refused the read, and the session is authenticated, so ` + `re-running 'uip login' mints the same token and fails the same way. ` + `${actor} Also confirm solution ${solutionId} lives in the organization ` + `and tenant this session logged into. Nothing was uploaded.`;
6093
6357
  }
6094
6358
  function outputError(message) {
6095
6359
  OutputFormatter.error({
@@ -6122,7 +6386,7 @@ var registerCommands = async (program) => {
6122
6386
  registerResourceCommand(program);
6123
6387
  registerDeleteCommand(program);
6124
6388
  if (isPreviewBuild()) {
6125
- const { registerListCommand } = await import("./list-8hvfmge6.js");
6389
+ const { registerListCommand } = await import("./list-dadxggxt.js");
6126
6390
  registerListCommand(program);
6127
6391
  }
6128
6392
  registerFeedsCommand(program);
@@ -6130,4 +6394,4 @@ var registerCommands = async (program) => {
6130
6394
 
6131
6395
  export { metadata, registerCommands };
6132
6396
 
6133
- //# debugId=599D94E97BDE9E5C64756E2164756E21
6397
+ //# debugId=5681A456DD88C8F164756E2164756E21