@uipath/solution-tool 1.201.0-preview.133 → 1.202.0-preview.134

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 (47) hide show
  1. package/dist/deploy.d.ts +6 -1
  2. package/dist/deploy.js +15 -16
  3. package/dist/{index-2fj08e7n.js → index-gpd94m28.js} +27 -27
  4. package/dist/{index-w03qc9m8.js → index-sby22td6.js} +4 -4
  5. package/dist/index.js +16 -19
  6. package/dist/init.js +10 -13
  7. package/dist/{js-yaml-4ypbq2tt.js → js-yaml-b2jq67kn.js} +16 -16
  8. package/dist/{list-skbehtq2.js → list-3mrc9s3f.js} +7 -11
  9. package/dist/models/pack-command-types.d.ts +8 -0
  10. package/dist/pack.js +9 -12
  11. package/dist/{packager-tool-krd5v2r5.js → packager-tool-0qva0ebq.js} +129 -53
  12. package/dist/{packager-tool-0v6na3yp.js → packager-tool-1de529jm.js} +9 -7
  13. package/dist/{packager-tool-9vehmnke.js → packager-tool-3hbsdwjm.js} +6 -661
  14. package/dist/{packager-tool-fzwxq48d.js → packager-tool-3y92zbg2.js} +3 -3
  15. package/dist/{packager-tool-f7kj5fpa.js → packager-tool-5pfxgc8z.js} +19 -11
  16. package/dist/{packager-tool-9w3k4e77.js → packager-tool-b16qemg2.js} +494 -198
  17. package/dist/{packager-tool-zbp5p149.js → packager-tool-bahvrjbv.js} +592 -264
  18. package/dist/{packager-tool-129wn232.js → packager-tool-dfrk01gn.js} +198 -2
  19. package/dist/{packager-tool-67ssxgph.js → packager-tool-fh8pb2th.js} +3 -5
  20. package/dist/{packager-tool-c29pg93e.js → packager-tool-hc6ybtdw.js} +5 -5
  21. package/dist/{packager-tool-bcpknnr8.js → packager-tool-hgx66d1p.js} +1656 -558
  22. package/dist/{packager-tool-2yz7m5ny.js → packager-tool-nq2nq71y.js} +21282 -20197
  23. package/dist/{packager-tool-wjhn59ft.js → packager-tool-t89r1edr.js} +596 -180
  24. package/dist/{packager-tool-vpr77gre.js → packager-tool-x3ehbhsn.js} +3 -2
  25. package/dist/{packager-tool-yktm4v4r.js → packager-tool-xntjj1bt.js} +1 -1
  26. package/dist/packager-tool.d.ts +4 -3
  27. package/dist/packager-tool.js +4 -4
  28. package/dist/publish.js +8 -11
  29. package/dist/resource.js +7 -10
  30. package/dist/services/activation.d.ts +9 -1
  31. package/dist/services/deploy-run-service.d.ts +25 -1
  32. package/dist/services/deploy-uninstall-service.d.ts +20 -1
  33. package/dist/services/deploy-upgrade-service.d.ts +94 -0
  34. package/dist/services/deployment-feed-locator.d.ts +36 -0
  35. package/dist/services/deployment-search.d.ts +88 -7
  36. package/dist/services/pack-command-service.d.ts +15 -0
  37. package/dist/services/package-metadata-rewrite.d.ts +9 -5
  38. package/dist/services/publish-service.d.ts +11 -9
  39. package/dist/services/solution-path.d.ts +34 -0
  40. package/dist/templates/AGENTS.md +183 -16
  41. package/dist/tool.js +18 -21
  42. package/package.json +2 -2
  43. package/dist/browser-strategy-yccf8mtd.js +0 -99
  44. package/dist/node-strategy-12qfy0nv.js +0 -348
  45. package/dist/packager-tool-5arsyj36.js +0 -11
  46. package/dist/packager-tool-7eva0peq.js +0 -265
  47. package/dist/packager-tool-9qecd4wb.js +0 -14
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getStudioWebAuthWithOrganizationId,
3
3
  useStudioWebAuth
4
- } from "./packager-tool-yktm4v4r.js";
4
+ } from "./packager-tool-xntjj1bt.js";
5
5
  import {
6
6
  activateDeploymentAsync,
7
7
  deployRunAsync,
@@ -9,53 +9,54 @@ import {
9
9
  formatDeploymentErrorsAsText,
10
10
  listDeploymentsAsync,
11
11
  listPackagesAsync,
12
- uninstallDeploymentAsync
13
- } from "./packager-tool-wjhn59ft.js";
12
+ uninstallDeploymentAsync,
13
+ upgradeDeploymentAsync
14
+ } from "./packager-tool-t89r1edr.js";
14
15
  import {
15
16
  SolutionInitError,
16
17
  solutionInitAsync
17
- } from "./packager-tool-67ssxgph.js";
18
+ } from "./packager-tool-fh8pb2th.js";
18
19
  import {
19
20
  GOVERNANCE_OPTION_INSTRUCTIONS,
20
21
  PackCommandService,
22
+ SOLUTION_PATH_INSTRUCTIONS,
21
23
  SolutionPackOptions,
22
24
  applyAuthConnection,
25
+ checkSolutionPathAsync,
23
26
  createSolutionPackager,
24
27
  ensurePackagerTools,
25
28
  packSolutionAsync,
26
29
  readSolutionFileAsUint8Array,
27
30
  resolveSdkLogLevel,
28
31
  validateGovernanceOptions
29
- } from "./packager-tool-2yz7m5ny.js";
32
+ } from "./packager-tool-nq2nq71y.js";
30
33
  import {
31
34
  publishSolutionAsync
32
- } from "./packager-tool-f7kj5fpa.js";
35
+ } from "./packager-tool-5pfxgc8z.js";
33
36
  import {
34
37
  Configuration,
35
38
  PipelinesApi,
36
39
  feedResolutionFailureInstructions,
37
40
  feedScopeInitOverride,
38
- folderKeyInitOverride,
39
41
  listSelectablePublishLocations,
40
- resolveFeedScope,
41
- resolvePersonalWorkspace
42
- } from "./packager-tool-c29pg93e.js";
42
+ resolveFeedScope
43
+ } from "./packager-tool-hc6ybtdw.js";
43
44
  import {
44
45
  Configuration as Configuration2,
45
46
  DeploymentActivationStatus,
46
47
  DeploymentOperationStatus,
47
- DeploymentsApi,
48
48
  PackagesApi,
49
49
  SearchApi,
50
+ StudioWebIncompatibleProjectError,
50
51
  bundleSolution,
51
52
  getProjectDebugStatus,
52
53
  importSolution,
53
54
  isDebugProvisioningSucceeded,
54
55
  isDebugProvisioningTerminal,
56
+ overwriteSolution,
55
57
  searchPackageVersions,
56
- solutionExistsOnStudioWeb,
57
- uploadOrOverwriteSolution
58
- } from "./packager-tool-9w3k4e77.js";
58
+ solutionExistsOnStudioWeb
59
+ } from "./packager-tool-b16qemg2.js";
59
60
  import {
60
61
  Tokens,
61
62
  applyEntryPointEnhancementAsync,
@@ -63,6 +64,7 @@ import {
63
64
  createLocalResource,
64
65
  createProjectInSolutionBuilder,
65
66
  createResourceBuilderServices,
67
+ createResourceKindMetadataResolver,
66
68
  findMatchingLocalResource,
67
69
  findProjectFile,
68
70
  findResourceByKeyInRcs,
@@ -76,13 +78,13 @@ import {
76
78
  syncAndLog,
77
79
  syncResourcesFromBindings,
78
80
  writeSolutionFile
79
- } from "./packager-tool-krd5v2r5.js";
81
+ } from "./packager-tool-0qva0ebq.js";
80
82
  import {
81
83
  ToolResult
82
- } from "./packager-tool-vpr77gre.js";
84
+ } from "./packager-tool-x3ehbhsn.js";
83
85
  import {
84
86
  getSolutionAuthContext
85
- } from "./packager-tool-fzwxq48d.js";
87
+ } from "./packager-tool-3y92zbg2.js";
86
88
  import {
87
89
  DEFAULT_PAGE_SIZE,
88
90
  Option,
@@ -90,11 +92,14 @@ import {
90
92
  Pagination,
91
93
  PollOutcome,
92
94
  RESULTS,
95
+ carriedInstructions,
93
96
  catchError,
94
97
  createHiddenDeprecatedTenantOption,
95
98
  extractErrorDetails,
96
99
  extractErrorMessage,
100
+ getFileSystem,
97
101
  getLoginStatusAsync,
102
+ isGuid,
98
103
  isPreviewBuild,
99
104
  logger,
100
105
  mapPackageMetadataOptions,
@@ -102,7 +107,6 @@ import {
102
107
  parseLimit,
103
108
  parseOffset,
104
109
  pollUntil,
105
- previewOnly,
106
110
  processContext,
107
111
  registerPackageMetadataOptions,
108
112
  requireConfirmation,
@@ -113,21 +117,20 @@ import {
113
117
  unsupportedSolutionProjectType,
114
118
  updateUipxSolutionId,
115
119
  withCompleter
116
- } from "./packager-tool-bcpknnr8.js";
117
- import {
118
- getFileSystem
119
- } from "./packager-tool-7eva0peq.js";
120
+ } from "./packager-tool-hgx66d1p.js";
120
121
  import {
122
+ strFromU8,
123
+ unzip,
121
124
  unzipSync
122
- } from "./packager-tool-129wn232.js";
125
+ } from "./packager-tool-dfrk01gn.js";
123
126
  import {
124
127
  __require
125
- } from "./packager-tool-0v6na3yp.js";
128
+ } from "./packager-tool-1de529jm.js";
126
129
  // package.json
127
130
  var package_default = {
128
131
  name: "@uipath/solution-tool",
129
132
  license: "MIT",
130
- version: "1.201.0-preview.133",
133
+ version: "1.202.0-preview.134",
131
134
  description: "Create, pack, publish, and deploy UiPath Automation Solutions.",
132
135
  repository: {
133
136
  type: "git",
@@ -188,7 +191,6 @@ var package_default = {
188
191
  "@uipath/filesystem": "workspace:*",
189
192
  "@uipath/orchestrator-sdk": "workspace:*",
190
193
  "@uipath/packager-tool-connector": "workspace:*",
191
- "@uipath/packager-tool-webapp": "workspace:*",
192
194
  "@uipath/pipelines-sdk": "workspace:*",
193
195
  "@uipath/project-executor": "workspace:*",
194
196
  "@uipath/resource-builder-sdk": "^2025.11.0-alpha4202-3459",
@@ -211,13 +213,12 @@ class CleanupCommandService {
211
213
  this.fs = getFileSystem();
212
214
  }
213
215
  async execute(solutionPath, options, context) {
214
- const absoluteSolutionPath = this.fs.path.resolve(solutionPath);
215
- const [statError, stats] = await catchError(this.fs.stat(absoluteSolutionPath));
216
- if (statError || !stats) {
217
- const msg = statError?.message ?? `Solution path does not exist: ${absoluteSolutionPath}`;
218
- this.reportFailureAndExit(context, msg, "Ensure the solution path exists and points to a solution directory (containing .uipx) or a .uis file.");
216
+ const check = await checkSolutionPathAsync(this.fs, solutionPath);
217
+ if (!check.ok) {
218
+ this.reportFailureAndExit(context, check.message, SOLUTION_PATH_INSTRUCTIONS, check.statError, check.errorCode);
219
219
  return;
220
220
  }
221
+ const { absoluteSolutionPath } = check;
221
222
  logger.info(options.dryRun ? `
222
223
  Detecting unused items (dry-run)...
223
224
  ` : `
@@ -229,11 +230,11 @@ Removing unused items...
229
230
  const [error, result] = await catchError(this.executeAsync(solutionPath, options));
230
231
  if (error) {
231
232
  const msg = error instanceof Error ? error.message : String(error);
232
- this.reportFailureAndExit(context, msg, "Check that the solution can be loaded, the required packager tools are available, and the current login has access to the tenant.", error);
233
+ this.reportFailureAndExit(context, msg, carriedInstructions(error) ?? "Check that the solution can be loaded, the required packager tools are available, and the current login has access to the tenant.", error);
233
234
  return;
234
235
  }
235
236
  if (result.errorCode !== "SUCCESS" /* Success */) {
236
- this.reportFailureAndExit(context, `Error code: ${result.errorCode} : ${result.message}`, result.message ?? "");
237
+ this.reportFailureAndExit(context, `Error code: ${result.errorCode} : ${result.message}`, result.instructions ?? result.message ?? "");
237
238
  return;
238
239
  }
239
240
  const changedFilesByProject = result.details?.changedFilesByProject ?? {};
@@ -248,11 +249,12 @@ Removing unused items...
248
249
  }
249
250
  });
250
251
  }
251
- reportFailureAndExit(context, message, instructions, error) {
252
+ reportFailureAndExit(context, message, instructions, error, errorCode) {
252
253
  OutputFormatter.error({
253
254
  Result: RESULTS.Failure,
254
255
  Message: message,
255
- Instructions: instructions
256
+ Instructions: instructions,
257
+ ErrorCode: errorCode
256
258
  });
257
259
  if (error instanceof Error && error.stack) {
258
260
  logger.error("Stack trace:");
@@ -261,22 +263,14 @@ Removing unused items...
261
263
  context.exit(1);
262
264
  }
263
265
  async executeAsync(solutionPath, options) {
264
- const absoluteSolutionPath = this.fs.path.resolve(solutionPath);
265
266
  if (options.loginValidity !== undefined && Number.isNaN(options.loginValidity)) {
266
267
  return new ToolResult("INTERNAL_ERROR" /* InternalError */, `Invalid --login-validity value: ${options.loginValidity}`, []);
267
268
  }
268
- if (!await this.fs.exists(absoluteSolutionPath)) {
269
- return new ToolResult("INTERNAL_ERROR" /* InternalError */, `Solution path does not exist: ${absoluteSolutionPath}`, []);
270
- }
271
- const [statError, stats] = await catchError(this.fs.stat(absoluteSolutionPath));
272
- if (statError || !stats) {
273
- return new ToolResult("INTERNAL_ERROR" /* InternalError */, statError?.message ?? `Cannot access solution path: ${absoluteSolutionPath}`, []);
274
- }
275
- const isDirectory = stats.isDirectory();
276
- const isUisFile = stats.isFile() && absoluteSolutionPath.endsWith(".uis");
277
- if (!isDirectory && !isUisFile) {
278
- return new ToolResult("INTERNAL_ERROR" /* InternalError */, `Solution path must be a directory or a .uis file: ${absoluteSolutionPath}`, []);
269
+ const check = await checkSolutionPathAsync(this.fs, solutionPath);
270
+ if (!check.ok) {
271
+ return new ToolResult("INTERNAL_ERROR" /* InternalError */, check.message, []);
279
272
  }
273
+ const { absoluteSolutionPath, isUisFile } = check;
280
274
  if (isUisFile && !options.dryRun) {
281
275
  return new ToolResult("INTERNAL_ERROR" /* InternalError */, `In-place cleanup of a .uis file is not supported: changes would be written to a temporary extraction and then discarded, leaving '${absoluteSolutionPath}' unchanged. Re-run with --dry-run to only detect unused items, or point at an unpacked solution directory to modify it in place.`, []);
282
276
  }
@@ -294,7 +288,7 @@ Removing unused items...
294
288
  return packager.cleanupSolutionAsync(cleanupOptions);
295
289
  });
296
290
  if (error) {
297
- return new ToolResult("INTERNAL_ERROR" /* InternalError */, error instanceof Error ? error.message : String(error), []);
291
+ return new ToolResult("INTERNAL_ERROR" /* InternalError */, error instanceof Error ? error.message : String(error), [], carriedInstructions(error));
298
292
  }
299
293
  return result;
300
294
  }
@@ -310,7 +304,8 @@ Removing unused items...
310
304
  skipImports: options.skipImports ?? false
311
305
  };
312
306
  cleanupOptions.logLevel = resolveSdkLogLevel();
313
- cleanupOptions.excludeConfiguredSources = false;
307
+ cleanupOptions.nuGetSourcesConfigPath = options.nugetSourcesConfigPath;
308
+ cleanupOptions.excludeConfiguredSources = options.excludeConfiguredSources ?? false;
314
309
  cleanupOptions.feedFolder = options.feedFolder;
315
310
  await applyAuthConnection(cleanupOptions, options.loginValidity);
316
311
  return cleanupOptions;
@@ -357,11 +352,13 @@ var SOLUTION_CLEANUP_EXAMPLES = [
357
352
  }
358
353
  ];
359
354
  var registerCleanupCommand = (program) => {
360
- program.command("cleanup").description("Remove unused items from every project in a UiPath solution. Accepts a solution directory (containing .uipx) or a .uis file. For a directory the project sources are modified in place. Pass --dry-run to only detect unused items.").argument("<solutionPath>", "Path to a solution directory (containing .uipx) or a .uis file").option("--dry-run", "Detect unused items but do not modify any project files.").option("--skip-imports", "Do not clean up now-unused references to removed items in the project's files.").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("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", (value) => Number.parseInt(value, 10), 10).examples(SOLUTION_CLEANUP_EXAMPLES).trackedAction(processContext, async (solutionPath, options) => {
355
+ program.command("cleanup").description("Remove unused items from every project in a UiPath solution. Accepts a solution directory (containing .uipx) or a .uis file. For a directory the project sources are modified in place. Pass --dry-run to only detect unused items.").argument("<solutionPath>", "Path to a solution directory (containing .uipx) or a .uis file").option("--dry-run", "Detect unused items but do not modify any project files.").option("--skip-imports", "Do not clean up now-unused references to removed items in the project's files.").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("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", (value) => Number.parseInt(value, 10), 10).examples(SOLUTION_CLEANUP_EXAMPLES).trackedAction(processContext, async (solutionPath, options) => {
361
356
  const service = new CleanupCommandService;
362
357
  const cleanupOptions = {
363
358
  dryRun: options.dryRun,
364
359
  skipImports: options.skipImports,
360
+ nugetSourcesConfigPath: options.nugetSourcesConfigPath,
361
+ excludeConfiguredSources: options.excludeConfiguredSources,
365
362
  feedFolder: options.feedFolder,
366
363
  loginValidity: options.loginValidity
367
364
  };
@@ -1079,9 +1076,7 @@ var SOLUTION_DEPLOY_LIST_EXAMPLES = [
1079
1076
  ];
1080
1077
  var registerDeployListCommand = (program) => {
1081
1078
  const command = program.command("list").description("List solution deployments. Shows deployment name, status, package version (current/target), pending operation, and folder path. --operation-status/--activation-status filter server-side; --folder-path filters after fetching.").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--folder-path <path>", "Filter by parent folder path (e.g., 'Shared')").option("--folder-key <key>", "Filter by parent folder key (GUID). Alternative to --folder-path.").option("-l, --limit <number>", "Max deployments to fetch. Note: --folder-path filtering happens after fetching, so increase this value if results seem incomplete.", parseLimit, DEFAULT_PAGE_SIZE).option("--offset <number>", "Number of deployments to skip (applied before --folder-path/--folder-key filtering)", parseOffset, 0).option("--sort-by <column>", "Column to sort by (default: startTime)", "startTime").option("--sort-order <direction>", "Sort direction: Ascending or Descending (default: Descending)", "Descending").option("--operation-status <statuses>", `Filter by deployment operation status, server-side (comma-separated). One or more of: ${OPERATION_STATUS_VALUES.join(", ")}.`).option("--activation-status <statuses>", `Filter by deployment activation status, server-side (comma-separated). One or more of: ${ACTIVATION_STATUS_VALUES.join(", ")}.`).option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10);
1082
- if (isPreviewBuild()) {
1083
- command.addOption(new Option("--feed <name-or-key>", "Only list deployments from this feed (name or folder key, from 'solution feeds list'). Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace")).option("--personal-workspace", "Only list deployments from the current user's Personal Workspace feed.");
1084
- }
1079
+ command.addOption(new Option("--feed <name-or-key>", "Only list deployments from this feed (name or folder key, from 'solution feeds list'). Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace")).option("--personal-workspace", "Only list deployments from the current user's Personal Workspace feed.");
1085
1080
  command.examples(SOLUTION_DEPLOY_LIST_EXAMPLES).trackedAction(processContext, async (options) => {
1086
1081
  const operationStatus = parseStatusFilter(options.operationStatus, OPERATION_STATUS_VALUES, "--operation-status");
1087
1082
  if (!operationStatus.ok) {
@@ -1236,6 +1231,38 @@ var SOLUTION_DEPLOY_RUN_EXAMPLES = [
1236
1231
  NextSteps: "Deployment created in your Personal Workspace and will auto-activate. Check status with: uip solution deploy list."
1237
1232
  }
1238
1233
  }
1234
+ },
1235
+ {
1236
+ Description: "Deploy into the Personal Workspace and wait for the install to finish before returning",
1237
+ Command: "uip solution deploy run --name my-deployment --package-name my-package --package-version 1.0.0 --folder-name MySolution --personal-workspace --wait",
1238
+ Output: {
1239
+ Code: "SolutionDeployRun",
1240
+ Data: {
1241
+ Status: "DeploymentSucceeded",
1242
+ DeploymentName: "my-deployment",
1243
+ PackageName: "my-package",
1244
+ PackageVersion: "1.0.0",
1245
+ FolderName: "MySolution",
1246
+ FolderPath: "alice@uipath.com/MySolution",
1247
+ ActivationStatus: "Auto"
1248
+ }
1249
+ }
1250
+ },
1251
+ {
1252
+ Description: "Deploy a second, separate copy of a package that is already deployed (each copy gets its own Orchestrator folder)",
1253
+ Command: 'uip solution deploy run --name my-deployment-2 --package-name my-package --package-version 1.0.0 --folder-name MySolutionCopy --parent-folder-path "Shared" --yes',
1254
+ Output: {
1255
+ Code: "SolutionDeployRun",
1256
+ Data: {
1257
+ Status: "DeploymentSucceeded",
1258
+ DeploymentKey: "a1b2c3d4-0000-0000-0000-000000000002",
1259
+ PipelineDeploymentId: "b2c3d4e5-0000-0000-0000-000000000002",
1260
+ InstanceId: "c3d4e5f6-0000-0000-0000-000000000002",
1261
+ FolderName: "MySolutionCopy",
1262
+ FolderPath: "Shared/MySolutionCopy",
1263
+ ActivationStatus: "SuccessfulActivate"
1264
+ }
1265
+ }
1239
1266
  }
1240
1267
  ];
1241
1268
  var SOLUTION_DEPLOY_RUN_FEED_EXAMPLE = {
@@ -1256,14 +1283,12 @@ var SOLUTION_DEPLOY_RUN_FEED_EXAMPLE = {
1256
1283
  }
1257
1284
  };
1258
1285
  var registerDeployRunCommand = (program) => {
1259
- const command = program.command("run").description("Deploy a solution package to Orchestrator. Creates a folder, provisions resources, and (by default) activates the solution. Pass --skip-activate to leave the deployment in 'Inactive (Ready to activate)' state and activate later with 'deploy activate'. To customize resource settings before deploying, use 'config get' to fetch the default config, 'config set' / 'config link' to edit it, then pass it here with --config-file.").requiredOption("-n, --name <deployment-name>", "Name for the deployment").addOption(withCompleter(new Option("--package-name <name>", "Solution package name to deploy. Use 'solution packages list' to find available names.").makeOptionMandatory(), packageNameCompleter)).addOption(withCompleter(new Option("--package-version <version>", "Solution package version to deploy. Use 'solution packages list' to find available versions.").makeOptionMandatory(), packageVersionCompleter)).requiredOption("--folder-name <name>", "Name for the new Orchestrator folder created for this deployment (created under --parent-folder-path if specified)").addOption(new Option("--parent-folder-path <path>", "Parent folder path (e.g., 'Shared'). Solution folder is created under this.").conflicts("parentFolderKey")).option("--parent-folder-key <key>", "Parent folder key (GUID). Alternative to --parent-folder-path.").addOption(new Option("--personal-workspace", "Deploy into the current user's Personal Workspace. Resolves the workspace name from Orchestrator automatically. Mutually exclusive with --parent-folder-path and --parent-folder-key.").conflicts(["parentFolderPath", "parentFolderKey"])).option("--config-file <path>", "JSON or YAML configuration file. Generate with: uip solution deploy config get <name> -d config.json, then customize with 'config set' or 'config link'.").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--timeout <seconds>", "Timeout in seconds applied per polling phase — deploy and (when not skipped) activate (default: 360 each)", (v) => parseInt(v, 10), 360).option("--poll-interval <ms>", "Milliseconds between status polls during the deploy and activate phases (default: 5000)", (v) => parseInt(v, 10), 5000).option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).option("--skip-activate", "Skip auto-activation; leave the deployment in 'Inactive (Ready to activate)' state. Activate later with 'uip solution deploy activate <name>'.");
1260
- if (isPreviewBuild()) {
1261
- command.addOption(new Option("--feed <name-or-key>", "Deploy a package from a specific feed (name or folder key, from 'solution feeds list') instead of the tenant feed. Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace"));
1262
- }
1263
- command.examples(isPreviewBuild() ? [
1286
+ const command = program.command("run").description("Deploy a solution package to Orchestrator. Creates a folder, provisions resources, and (by default) activates the solution. This always creates a NEW deployment in a NEW folder — it never updates an existing one — so if the package is already deployed the command stops and points you at the upgrade path; pass --yes to deploy a second, separate copy anyway. Pass --skip-activate to leave the deployment in 'Inactive (Ready to activate)' state and activate later with 'deploy activate'. To customize resource settings before deploying, use 'config get' to fetch the default config, 'config set' / 'config link' to edit it, then pass it here with --config-file.").requiredOption("-n, --name <deployment-name>", "Name for the deployment").addOption(withCompleter(new Option("--package-name <name>", "Solution package name to deploy. Use 'solution packages list' to find available names.").makeOptionMandatory(), packageNameCompleter)).addOption(withCompleter(new Option("--package-version <version>", "Solution package version to deploy. Use 'solution packages list' to find available versions.").makeOptionMandatory(), packageVersionCompleter)).requiredOption("--folder-name <name>", "Name for the new Orchestrator folder created for this deployment (created under --parent-folder-path if specified)").addOption(new Option("--parent-folder-path <path>", "Parent folder path (e.g., 'Shared'). Solution folder is created under this.").conflicts("parentFolderKey")).option("--parent-folder-key <key>", "Parent folder key (GUID). Alternative to --parent-folder-path.").addOption(new Option("--personal-workspace", "Deploy into the current user's Personal Workspace. Resolves the workspace name from Orchestrator automatically. Mutually exclusive with --parent-folder-path and --parent-folder-key.").conflicts(["parentFolderPath", "parentFolderKey"])).option("--config-file <path>", "JSON or YAML configuration file. Generate with: uip solution deploy config get <name> -d config.json, then customize with 'config set' or 'config link'.").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--timeout <seconds>", "Timeout in seconds applied per polling phase — deploy and (when not skipped) activate (default: 360 each)", (v) => parseInt(v, 10), 360).option("--poll-interval <ms>", "Milliseconds between status polls during the deploy and activate phases (default: 5000)", (v) => parseInt(v, 10), 5000).option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).option("--wait", "Wait for a --personal-workspace / --feed deploy to finish instead of returning as soon as it starts. Bounded by --timeout. The tenant path always waits, so this changes nothing there.").option("--skip-activate", "Skip auto-activation; leave the deployment in 'Inactive (Ready to activate)' state. Activate later with 'uip solution deploy activate <name>'.").option("-y, --yes", "Deploy even when this package already has a deployment. Without it, deploy run looks the package up and stops if it is already deployed. That check is best-effort, not a guarantee: it reads the Solutions search, which needs a user session, so an application (client-credentials) login deploys without it, and a lookup failure only warns.");
1287
+ command.addOption(new Option("--feed <name-or-key>", "Deploy a package from a specific feed (name or folder key, from 'solution feeds list') instead of the tenant feed. Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace"));
1288
+ command.examples([
1264
1289
  ...SOLUTION_DEPLOY_RUN_EXAMPLES,
1265
1290
  SOLUTION_DEPLOY_RUN_FEED_EXAMPLE
1266
- ] : SOLUTION_DEPLOY_RUN_EXAMPLES).trackedAction(processContext, async (options) => {
1291
+ ]).trackedAction(processContext, async (options) => {
1267
1292
  const result = await deployRunAsync({
1268
1293
  tenant: options.tenant,
1269
1294
  deploymentName: options.name,
@@ -1275,17 +1300,21 @@ var registerDeployRunCommand = (program) => {
1275
1300
  personalWorkspace: options.personalWorkspace,
1276
1301
  feed: options.feed,
1277
1302
  configFile: options.configFile,
1303
+ wait: options.wait,
1278
1304
  timeout: options.timeout,
1279
1305
  pollInterval: options.pollInterval,
1280
1306
  loginValidity: options.loginValidity,
1281
1307
  skipActivate: options.skipActivate,
1308
+ yes: options.yes,
1282
1309
  signal: processContext.pollSignal
1283
1310
  });
1284
1311
  if (!result.ok) {
1285
1312
  OutputFormatter.error({
1286
1313
  Result: RESULTS.Failure,
1287
1314
  Message: result.message,
1288
- Instructions: result.instructions
1315
+ Instructions: result.instructions,
1316
+ ErrorCode: result.errorCode,
1317
+ Retry: result.retry
1289
1318
  });
1290
1319
  processContext.exit(result.exitCode);
1291
1320
  return;
@@ -1433,7 +1462,9 @@ var registerDeployUninstallCommand = (program) => {
1433
1462
  OutputFormatter.error({
1434
1463
  Result: RESULTS.Failure,
1435
1464
  Message: result.message,
1436
- Instructions: result.instructions
1465
+ Instructions: result.instructions,
1466
+ ErrorCode: result.errorCode,
1467
+ Retry: result.retry
1437
1468
  });
1438
1469
  processContext.exit(result.exitCode);
1439
1470
  return;
@@ -1473,97 +1504,6 @@ var registerDeployUninstallCommand = (program) => {
1473
1504
  });
1474
1505
  };
1475
1506
 
1476
- // src/services/deploy-upgrade-service.ts
1477
- var fail = (reason, message, instructions, exitCode = 1) => ({
1478
- ok: false,
1479
- reason,
1480
- message,
1481
- instructions,
1482
- exitCode
1483
- });
1484
- var AUTH_INSTRUCTIONS = "Run 'uip login' to refresh credentials, then retry. If the deployment is in a different tenant, switch with 'uip login tenant set <tenant>'.";
1485
- async function upgradeDeploymentAsync(deploymentKey, options = {}) {
1486
- const [authError, auth] = await catchError(getSolutionAuthContext({
1487
- tenant: options.tenant,
1488
- loginValidity: options.loginValidity,
1489
- envFilePath: options.envFilePath
1490
- }));
1491
- if (authError) {
1492
- return fail("auth_failed", authError.message, AUTH_INSTRUCTIONS);
1493
- }
1494
- const config = new Configuration2({
1495
- basePath: auth.basePath,
1496
- accessToken: auth.accessToken
1497
- });
1498
- const deploymentsApi = new DeploymentsApi(config);
1499
- const searchApi = new SearchApi(config);
1500
- let feedScope;
1501
- if (options.personalWorkspace) {
1502
- const [pwError, pw] = await catchError(resolvePersonalWorkspace({
1503
- tenant: options.tenant,
1504
- loginValidity: options.loginValidity
1505
- }));
1506
- if (pwError || !pw) {
1507
- return fail("personal_workspace_failed", pwError?.message ?? "Personal Workspace could not be resolved.", "Ensure your user has a Personal Workspace, then retry.");
1508
- }
1509
- feedScope = folderKeyInitOverride(pw.key);
1510
- }
1511
- const [getError, deployment] = await catchError(deploymentsApi.deploymentsGetByKey({ deploymentKey }, feedScope));
1512
- if (getError || !deployment) {
1513
- const detail = getError && await extractErrorDetails(getError);
1514
- return fail("deployment_not_found", detail ? detail.details ? `${detail.message}: ${detail.details}` : detail.message : `Deployment '${deploymentKey}' was not found.`, "Run 'uip solution deploy list' to find the deployment key. Add '--personal-workspace' if it lives in your Personal Workspace.");
1515
- }
1516
- const upgradeScope = feedScope;
1517
- const take = 50;
1518
- let pkg;
1519
- for (let skip = 0;; ) {
1520
- const [searchError, packages] = await catchError(searchApi.searchSearchPackages({
1521
- searchFilter: {
1522
- searchTerm: deployment.packageName,
1523
- take,
1524
- skip
1525
- }
1526
- }, feedScope));
1527
- if (searchError) {
1528
- const { message, details } = await extractErrorDetails(searchError);
1529
- return fail("package_not_found", details ? `${message}: ${details}` : message, "Confirm the package is published to the tenant (or Personal Workspace) feed.");
1530
- }
1531
- const values = packages.values ?? [];
1532
- pkg = values.find((p) => p.name === deployment.packageName);
1533
- if (pkg || values.length < take) {
1534
- break;
1535
- }
1536
- skip += values.length;
1537
- }
1538
- if (!pkg) {
1539
- return fail("package_not_found", `Package '${deployment.packageName}' was not found in the feed.`, "Confirm the package is published to the tenant (or Personal Workspace) feed.");
1540
- }
1541
- if (options.version && options.version !== pkg.latestVersion) {
1542
- return fail("unsupported_version", `Upgrade supports only the latest published version ('${pkg.latestVersion}'); requested '${options.version}'.`, "Publish the desired version as the latest, or omit '--version' to upgrade to the newest.");
1543
- }
1544
- const currentVersion = deployment.currentPackageVersion ?? deployment.packageVersion;
1545
- if (currentVersion === pkg.latestVersion) {
1546
- return fail("no_upgrade_available", `Deployment '${deployment.name}' is already at the latest published version (${pkg.latestVersion}).`, "Publish a newer version of the package, then retry.");
1547
- }
1548
- const [upgradeError] = await catchError(deploymentsApi.deploymentsUpgrade({
1549
- deploymentKey,
1550
- packageVersionKey: pkg.packageVersionKey,
1551
- deploymentUpgradeRequest: {
1552
- packageVersionKey: pkg.packageVersionKey
1553
- }
1554
- }, upgradeScope));
1555
- if (upgradeError) {
1556
- const { message, details } = await extractErrorDetails(upgradeError);
1557
- return fail("request_failed", details ? `${message}: ${details}` : message, "The upgrade could not be started. Check the deployment key and that a newer version is published.");
1558
- }
1559
- return {
1560
- ok: true,
1561
- deploymentName: deployment.name,
1562
- fromVersion: currentVersion,
1563
- toVersion: pkg.latestVersion
1564
- };
1565
- }
1566
-
1567
1507
  // src/commands/deploy-upgrade.ts
1568
1508
  var SOLUTION_DEPLOY_UPGRADE_EXAMPLES = [
1569
1509
  {
@@ -1572,21 +1512,39 @@ var SOLUTION_DEPLOY_UPGRADE_EXAMPLES = [
1572
1512
  Output: {
1573
1513
  Code: "SolutionDeployUpgrade",
1574
1514
  Data: {
1575
- Status: "UpgradeInitiated",
1515
+ Status: "Successful",
1516
+ DeploymentName: "billing-solution",
1517
+ FromVersion: "1.0.0",
1518
+ ToVersion: "2.0.0",
1519
+ VersionChangeKey: "b2c3d4e5-0000-0000-0000-000000000002"
1520
+ }
1521
+ }
1522
+ },
1523
+ {
1524
+ Description: "Start the upgrade without waiting for it to finish",
1525
+ Command: "uip solution deploy upgrade a1b2c3d4-0000-0000-0000-000000000001 --no-wait",
1526
+ Output: {
1527
+ Code: "SolutionDeployUpgrade",
1528
+ Data: {
1529
+ Status: "Draft",
1576
1530
  DeploymentName: "billing-solution",
1577
1531
  FromVersion: "1.0.0",
1578
- ToVersion: "2.0.0"
1532
+ ToVersion: "2.0.0",
1533
+ VersionChangeKey: "b2c3d4e5-0000-0000-0000-000000000002"
1579
1534
  }
1580
1535
  }
1581
1536
  }
1582
1537
  ];
1583
1538
  var registerDeployUpgradeCommand = (program) => {
1584
- program.command("upgrade").description("Upgrade an existing deployment to a newer package version in place — the CLI equivalent of the Orchestrator UI 'Upgrade' button. Use this when 'deploy run' is blocked because a deployment already exists. Initiates the upgrade; track it with 'deploy list'.").argument("<deployment-key>", "Key of the deployment to upgrade. Use 'solution deploy list' to find it.").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--version <version>", "Target package version (default: the newest available). Only the latest published version is supported today.").option("--personal-workspace", "Upgrade a deployment in your Personal Workspace feed instead of the tenant.").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).examples(SOLUTION_DEPLOY_UPGRADE_EXAMPLES).trackedAction(processContext, async (deploymentKey, options) => {
1539
+ program.command("upgrade").description("Upgrade an existing deployment to a newer package version in place — the CLI equivalent of the Orchestrator UI 'Upgrade' button. Use this when 'deploy run' is blocked because a deployment already exists. The deployment's existing configuration is preserved, so values already set on it (for example a credential asset's secret) are not reset to the package defaults. Waits for the upgrade to finish unless --no-wait is passed.").argument("<deployment-key>", "Key of the deployment to upgrade. Use 'solution deploy list' to find it.").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--version <version>", "Target package version (default: the newest available). Only the latest published version is supported today.").option("--personal-workspace", "Upgrade a deployment in your Personal Workspace feed instead of the tenant.").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).option("--no-wait", "Return as soon as the upgrade is accepted instead of waiting for it to finish.").option("--timeout <seconds>", "Seconds to wait for the upgrade to finish (default: 300)", (v) => parseInt(v, 10), 300).option("--poll-interval <ms>", "Milliseconds between status polls (default: 5000)", (v) => parseInt(v, 10), 5000).examples(SOLUTION_DEPLOY_UPGRADE_EXAMPLES).trackedAction(processContext, async (deploymentKey, options) => {
1585
1540
  const result = await upgradeDeploymentAsync(deploymentKey, {
1586
1541
  tenant: options.tenant,
1587
1542
  version: options.version,
1588
1543
  personalWorkspace: options.personalWorkspace,
1589
- loginValidity: options.loginValidity
1544
+ loginValidity: options.loginValidity,
1545
+ noWait: options.wait === false,
1546
+ timeout: options.timeout,
1547
+ pollInterval: options.pollInterval
1590
1548
  });
1591
1549
  if (!result.ok) {
1592
1550
  OutputFormatter.error({
@@ -1601,10 +1559,11 @@ var registerDeployUpgradeCommand = (program) => {
1601
1559
  Result: RESULTS.Success,
1602
1560
  Code: "SolutionDeployUpgrade",
1603
1561
  Data: {
1604
- Status: "UpgradeInitiated",
1562
+ Status: result.status,
1605
1563
  DeploymentName: result.deploymentName,
1606
1564
  FromVersion: result.fromVersion,
1607
- ToVersion: result.toVersion
1565
+ ToVersion: result.toVersion,
1566
+ VersionChangeKey: result.versionChangeKey
1608
1567
  }
1609
1568
  });
1610
1569
  });
@@ -1619,9 +1578,57 @@ var registerDeployCommand = (program) => {
1619
1578
  registerDeployActivateCommand(deploy);
1620
1579
  registerDeployUninstallCommand(deploy);
1621
1580
  registerDeployListCommand(deploy);
1622
- previewOnly(() => registerDeployUpgradeCommand(deploy));
1581
+ registerDeployUpgradeCommand(deploy);
1623
1582
  };
1624
1583
 
1584
+ // src/services/agents-briefing.ts
1585
+ import { fileURLToPath } from "node:url";
1586
+ var BRIEFING_FILENAMES = ["AGENTS.md", "CLAUDE.md"];
1587
+ var CLI_VERSION_PLACEHOLDER = "{{cli_version}}";
1588
+ function agentsTemplateCandidateUrls() {
1589
+ return [
1590
+ new URL("./templates/AGENTS.md", import.meta.url),
1591
+ new URL("../../templates/AGENTS.md", import.meta.url)
1592
+ ];
1593
+ }
1594
+ async function renderBriefingTemplateAsync() {
1595
+ const fs = getFileSystem();
1596
+ const candidateUrls = agentsTemplateCandidateUrls();
1597
+ let templateContent;
1598
+ for (const candidateUrl of candidateUrls) {
1599
+ const candidatePath = fileURLToPath(candidateUrl);
1600
+ const [, content] = await catchError(fs.readFile(candidatePath, "utf-8"));
1601
+ if (content !== undefined && content !== null) {
1602
+ templateContent = content;
1603
+ break;
1604
+ }
1605
+ }
1606
+ if (templateContent === undefined) {
1607
+ throw new Error(`AGENTS.md template asset not found in package. Tried: ${candidateUrls.map((u) => fileURLToPath(u)).join(", ")}`);
1608
+ }
1609
+ return templateContent.replaceAll(CLI_VERSION_PLACEHOLDER, package_default.version);
1610
+ }
1611
+ async function writeMissingBriefingFilesAsync(solutionDir) {
1612
+ const fs = getFileSystem();
1613
+ const targets = BRIEFING_FILENAMES.map((name) => fs.path.join(solutionDir, name));
1614
+ const presence = await Promise.all(targets.map((path) => fs.exists(path)));
1615
+ const missing = targets.filter((_, index) => !presence[index]);
1616
+ if (missing.length === 0) {
1617
+ return [];
1618
+ }
1619
+ const content = await renderBriefingTemplateAsync();
1620
+ const written = [];
1621
+ for (const path of missing) {
1622
+ const [error] = await catchError(fs.writeFile(path, content));
1623
+ if (error) {
1624
+ logger.warn(`Could not write ${path}: ${error.message}`);
1625
+ continue;
1626
+ }
1627
+ written.push(path);
1628
+ }
1629
+ return written;
1630
+ }
1631
+
1625
1632
  // src/services/archive-extract.ts
1626
1633
  function resolveZipEntryPath(fs, destinationDir, entryName) {
1627
1634
  if (entryName.includes("\x00")) {
@@ -1660,6 +1667,36 @@ async function extractArchive(archivePath, destinationDir) {
1660
1667
  }
1661
1668
  await extractZipDataToDir(fs, data, destinationDir);
1662
1669
  }
1670
+ var SOLUTION_STORAGE_ENTRY = "SolutionStorage.json";
1671
+ function isRootUipxEntry(name) {
1672
+ return !name.includes("/") && name.endsWith(".uipx");
1673
+ }
1674
+ function readSolutionIdField(bytes) {
1675
+ if (!bytes) {
1676
+ return;
1677
+ }
1678
+ const [parseError, parsed] = catchError(() => JSON.parse(strFromU8(bytes)));
1679
+ if (parseError || !parsed) {
1680
+ return;
1681
+ }
1682
+ const id = parsed.SolutionId;
1683
+ return typeof id === "string" && isGuid(id) ? id : undefined;
1684
+ }
1685
+ function readBundledSolutionId(archive) {
1686
+ const [unzipError, entries] = catchError(() => unzipSync(archive, {
1687
+ filter: (file) => file.name === SOLUTION_STORAGE_ENTRY || isRootUipxEntry(file.name)
1688
+ }));
1689
+ if (unzipError || !entries) {
1690
+ logger.debug(`Could not inspect the solution archive for its SolutionId: ${unzipError?.message ?? "no entries"}`);
1691
+ return;
1692
+ }
1693
+ const rootUipxName = Object.keys(entries).find(isRootUipxEntry);
1694
+ const solutionId = readSolutionIdField(entries[SOLUTION_STORAGE_ENTRY]) ?? readSolutionIdField(rootUipxName ? entries[rootUipxName] : undefined);
1695
+ if (!solutionId) {
1696
+ logger.debug("No SolutionId found in the solution archive.");
1697
+ }
1698
+ return solutionId;
1699
+ }
1663
1700
 
1664
1701
  // src/commands/download.ts
1665
1702
  var SOLUTION_DOWNLOAD_EXAMPLES = [
@@ -1672,13 +1709,17 @@ var SOLUTION_DOWNLOAD_EXAMPLES = [
1672
1709
  Status: "Solution exported successfully",
1673
1710
  SolutionId: "a1b2c3d4-0000-0000-0000-000000000001",
1674
1711
  Output: "./downloads/a1b2c3d4-0000-0000-0000-000000000001.uis",
1675
- ExtractedTo: "./downloads/a1b2c3d4-0000-0000-0000-000000000001"
1712
+ ExtractedTo: "./downloads/a1b2c3d4-0000-0000-0000-000000000001",
1713
+ BriefingFiles: [
1714
+ "./downloads/a1b2c3d4-0000-0000-0000-000000000001/AGENTS.md",
1715
+ "./downloads/a1b2c3d4-0000-0000-0000-000000000001/CLAUDE.md"
1716
+ ]
1676
1717
  }
1677
1718
  }
1678
1719
  }
1679
1720
  ];
1680
1721
  function registerDownloadCommand(program) {
1681
- const download = program.command("download").description("Download/export a solution from Studio Web").argument("<solution-id>", "Solution UUID to export").option("-d, --destination <dir>", "Destination directory for the downloaded file", ".").option("-n, --name <name>", "Output filename (without extension). Existing files are overwritten; --extract uses this name for the directory").option("--extract", "Extract the downloaded .uis zip into a directory", false).examples(SOLUTION_DOWNLOAD_EXAMPLES);
1722
+ const download = program.command("download").description("Download/export a solution from Studio Web").argument("<solution-id>", "Solution UUID to export").option("-d, --destination <dir>", "Destination directory for the downloaded file", ".").option("-n, --name <name>", "Output filename (without extension). Existing files are overwritten; --extract uses this name for the directory").option("--extract", "Extract the downloaded .uis zip into a directory, and add AGENTS.md / CLAUDE.md there if the archive did not already carry them", false).examples(SOLUTION_DOWNLOAD_EXAMPLES);
1682
1723
  useStudioWebAuth(download, { requireOrganizationId: true }).trackedAction(processContext, async (solutionId, options, command) => {
1683
1724
  const [error] = await catchError(downloadSolution(solutionId, options, command));
1684
1725
  if (error) {
@@ -1714,9 +1755,11 @@ async function downloadSolution(solutionId, options, command) {
1714
1755
  await fs.writeFile(outputPath, new Uint8Array(arrayBuffer));
1715
1756
  logger.info(`Downloaded solution to ${outputPath}`);
1716
1757
  const extractedTo = options.extract ? fs.path.join(outputDir, filename.replace(/\.uis$/, "")) : undefined;
1758
+ let briefingFiles = [];
1717
1759
  if (extractedTo) {
1718
1760
  await extractArchive(outputPath, extractedTo);
1719
1761
  logger.info(`Extracted to ${extractedTo}`);
1762
+ briefingFiles = await writeBriefingFiles(extractedTo);
1720
1763
  }
1721
1764
  OutputFormatter.success({
1722
1765
  Result: RESULTS.Success,
@@ -1725,10 +1768,22 @@ async function downloadSolution(solutionId, options, command) {
1725
1768
  Status: "Solution exported successfully",
1726
1769
  SolutionId: solutionId,
1727
1770
  Output: outputPath,
1728
- ...extractedTo ? { ExtractedTo: extractedTo } : {}
1771
+ ...extractedTo ? { ExtractedTo: extractedTo } : {},
1772
+ ...briefingFiles.length > 0 ? { BriefingFiles: briefingFiles } : {}
1729
1773
  }
1730
1774
  });
1731
1775
  }
1776
+ async function writeBriefingFiles(solutionDir) {
1777
+ const [error, written] = await catchError(writeMissingBriefingFilesAsync(solutionDir));
1778
+ if (error) {
1779
+ logger.warn(`Extracted the solution but could not write its AGENTS.md / CLAUDE.md: ${error.message}`);
1780
+ return [];
1781
+ }
1782
+ for (const path of written) {
1783
+ logger.info(`Wrote agent instructions to ${path}`);
1784
+ }
1785
+ return written;
1786
+ }
1732
1787
  function getOutputFilename(name) {
1733
1788
  validatePlainFilename(name);
1734
1789
  return name.endsWith(".uis") ? name : `${name}.uis`;
@@ -1803,18 +1858,10 @@ var registerFeedsCommand = (program) => {
1803
1858
  };
1804
1859
 
1805
1860
  // src/commands/new.ts
1806
- import { fileURLToPath } from "node:url";
1807
1861
  var SOLUTION_INIT_STAGE_MESSAGES = {
1808
1862
  directory: "Failed to create solution directory",
1809
1863
  briefing: "Failed to write AI-agent instruction files"
1810
1864
  };
1811
- var CLI_VERSION_PLACEHOLDER = "{{cli_version}}";
1812
- function agentsTemplateCandidateUrls() {
1813
- return [
1814
- new URL("./templates/AGENTS.md", import.meta.url),
1815
- new URL("../../templates/AGENTS.md", import.meta.url)
1816
- ];
1817
- }
1818
1865
  var SOLUTION_NEW_EXAMPLES = [
1819
1866
  {
1820
1867
  Description: "Initialize a new empty UiPath solution",
@@ -1829,26 +1876,9 @@ var SOLUTION_NEW_EXAMPLES = [
1829
1876
  }
1830
1877
  }
1831
1878
  ];
1832
- async function renderBriefingTemplate() {
1833
- const fs = getFileSystem();
1834
- const candidateUrls = agentsTemplateCandidateUrls();
1835
- let templateContent;
1836
- for (const candidateUrl of candidateUrls) {
1837
- const candidatePath = fileURLToPath(candidateUrl);
1838
- const [, content] = await catchError(fs.readFile(candidatePath, "utf-8"));
1839
- if (content !== undefined && content !== null) {
1840
- templateContent = content;
1841
- break;
1842
- }
1843
- }
1844
- if (templateContent === undefined) {
1845
- throw new Error(`AGENTS.md template asset not found in package. Tried: ${candidateUrls.map((u) => fileURLToPath(u)).join(", ")}`);
1846
- }
1847
- return templateContent.replaceAll(CLI_VERSION_PLACEHOLDER, package_default.version);
1848
- }
1849
1879
  var registerNewCommand = (program) => {
1850
1880
  program.command("init").description("Initialize a new empty UiPath solution. Creates a directory named after the solution containing a .uipx manifest file with an empty Projects array, plus AGENTS.md and CLAUDE.md briefing files that orient AI coding agents to the solution. Use 'solution projects add' to add projects afterward.").argument("<solutionName>", "Solution name or path. Creates a directory with this name containing a .uipx manifest file.").examples(SOLUTION_NEW_EXAMPLES).trackedAction(processContext, async (solutionName) => {
1851
- const [briefingError, briefingContent] = await catchError(renderBriefingTemplate());
1881
+ const [briefingError, briefingContent] = await catchError(renderBriefingTemplateAsync());
1852
1882
  if (briefingError) {
1853
1883
  OutputFormatter.error({
1854
1884
  Result: RESULTS.Failure,
@@ -1963,7 +1993,7 @@ var SOLUTION_PACK_EXAMPLES = [
1963
1993
  }
1964
1994
  ];
1965
1995
  var registerPackCommand = (program) => {
1966
- 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("--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"));
1996
+ 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"));
1967
1997
  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) => {
1968
1998
  if (!options.dryRun && outputPath === undefined) {
1969
1999
  OutputFormatter.error({
@@ -1995,6 +2025,7 @@ var registerPackCommand = (program) => {
1995
2025
  loginValidity: options.loginValidity,
1996
2026
  dryRun: options.dryRun,
1997
2027
  nugetSourcesConfigPath: options.nugetSourcesConfigPath,
2028
+ excludeConfiguredSources: options.excludeConfiguredSources,
1998
2029
  feedFolder: options.feedFolder,
1999
2030
  signingCertificatePath: options.signingCertificatePath,
2000
2031
  signingCertificatePassword: resolveEnvReference(options.signingCertificatePassword),
@@ -2040,10 +2071,7 @@ function packageDeleteFailureInstructions(scope, packageName) {
2040
2071
  if (scope.kind === "folder") {
2041
2072
  return `List packages in that feed with: uip solution packages list --feed <name-or-key> --name ${packageName}.`;
2042
2073
  }
2043
- if (isPreviewBuild()) {
2044
- return `${base} If the package was published to a Personal Workspace or folder feed, add --personal-workspace or --feed.`;
2045
- }
2046
- return `${base} If the package was published to a Personal Workspace, add --personal-workspace.`;
2074
+ return `${base} If the package was published to a Personal Workspace or folder feed, add --personal-workspace or --feed.`;
2047
2075
  }
2048
2076
  async function resolvePackageVersionKey(auth, scope, packageName, packageVersion) {
2049
2077
  const config = new Configuration2({
@@ -2112,9 +2140,7 @@ async function deleteScopedPackageVersion(auth, scope, packageName, packageVersi
2112
2140
  }
2113
2141
  var registerPackagesDeleteCommand = (program) => {
2114
2142
  const command = program.command("delete").description("Delete a specific version of a solution package. " + "Use 'solution packages list' to find available package names and versions.").argument("<package-name>", "The name of the package").argument("<package-version>", "The version to delete (e.g. 1.0.0)").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).option("-y, --yes", "Confirm this irreversible operation (required; the CLI never prompts)").option("--personal-workspace", "Delete the package version from the current user's Personal Workspace feed.");
2115
- if (isPreviewBuild()) {
2116
- command.addOption(new Option("--feed <name-or-key>", "Delete the package version from this feed (name or folder key, from 'solution feeds list'). Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace"));
2117
- }
2143
+ command.addOption(new Option("--feed <name-or-key>", "Delete the package version from this feed (name or folder key, from 'solution feeds list'). Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace"));
2118
2144
  command.examples(SOLUTION_PACKAGES_DELETE_EXAMPLES).trackedAction(processContext, async (packageName, packageVersion, options) => {
2119
2145
  if (!requireConfirmation(options, `delete package '${packageName}@${packageVersion}'`)) {
2120
2146
  return;
@@ -2376,9 +2402,7 @@ var SOLUTION_PACKAGES_LIST_EXAMPLES = [
2376
2402
  ];
2377
2403
  var registerPackagesListCommand = (program) => {
2378
2404
  const command = program.command("list").description("List solution packages published to UiPath. Returns package name, version, and publish date. Use the package name and version from this output when running 'solution deploy run'.").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("-l, --limit <number>", "Number of packages to return", parseLimit, DEFAULT_PAGE_SIZE).option("--offset <number>", "Number of packages to skip", parseOffset, 0).option("--sort-by <column>", "Column to sort by (default: publishDate)", "publishDate").option("--sort-order <direction>", "Sort direction: Ascending or Descending (default: Descending)", "Descending").option("--name <pattern>", "Filter packages by name (server-side substring match)").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).option("--personal-workspace", "Only list packages in the current user's Personal Workspace feed.");
2379
- if (isPreviewBuild()) {
2380
- command.addOption(new Option("--feed <name-or-key>", "Only list packages in this feed (name or folder key, from 'solution feeds list'). Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace"));
2381
- }
2405
+ command.addOption(new Option("--feed <name-or-key>", "Only list packages in this feed (name or folder key, from 'solution feeds list'). Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace"));
2382
2406
  command.examples(SOLUTION_PACKAGES_LIST_EXAMPLES).trackedAction(processContext, async (options) => {
2383
2407
  const result = await listPackagesAsync({
2384
2408
  tenant: options.tenant,
@@ -3022,6 +3046,14 @@ var registerProjectPublishCommand = (project) => {
3022
3046
  processContext.exit(1);
3023
3047
  return;
3024
3048
  }
3049
+ trackShipSucceeded({
3050
+ ship_kind: "publish",
3051
+ target: "solution_package_feed",
3052
+ project_type: "solution_project",
3053
+ command_name: "uip.solution.projects.publish",
3054
+ package_name: options.packageName,
3055
+ package_version: options.packageVersion
3056
+ });
3025
3057
  OutputFormatter.success({
3026
3058
  Result: RESULTS.Success,
3027
3059
  Code: "SolutionProjectPublish",
@@ -3396,22 +3428,46 @@ function readProjectTypesFromUipx(content) {
3396
3428
  return typeof project.Type === "string" ? project.Type : undefined;
3397
3429
  }).filter((projectType) => projectType !== undefined);
3398
3430
  }
3431
+ var MAX_INSPECTABLE_PACKAGE_BYTES = 256 * 1024 * 1024;
3432
+ function isProjectManifestEntry(name) {
3433
+ return name.endsWith(".uipx");
3434
+ }
3435
+ function unzipProjectManifests(archive) {
3436
+ return new Promise((resolve, reject) => {
3437
+ unzip(archive, { filter: (file) => isProjectManifestEntry(file.name) }, (error, entries) => {
3438
+ if (error) {
3439
+ reject(error);
3440
+ return;
3441
+ }
3442
+ resolve(entries);
3443
+ });
3444
+ });
3445
+ }
3399
3446
  async function inspectSolutionPackageProjectTypes(packagePath) {
3400
3447
  const fs = getFileSystem();
3448
+ const [statError, stats] = await catchError(fs.stat(packagePath));
3449
+ if (statError || !stats) {
3450
+ logger.debug(`[uip.ship.succeeded] Could not stat solution package for project type telemetry: ${statError?.message ?? "no stats"}`);
3451
+ return { projectType: "unknown" };
3452
+ }
3453
+ if (stats.size > MAX_INSPECTABLE_PACKAGE_BYTES) {
3454
+ logger.debug(`[uip.ship.succeeded] Solution package is ${stats.size} bytes, over the ${MAX_INSPECTABLE_PACKAGE_BYTES}-byte inspection cap; skipping project type telemetry.`);
3455
+ return { projectType: "unknown" };
3456
+ }
3401
3457
  const [readError, archive] = await catchError(fs.readFile(packagePath));
3402
3458
  if (readError || !archive) {
3403
3459
  logger.debug(`[uip.ship.succeeded] Could not read solution package for project type telemetry: ${readError?.message ?? "empty archive"}`);
3404
3460
  return { projectType: "unknown" };
3405
3461
  }
3406
- const [unzipError, entries] = await catchError(Promise.resolve().then(() => unzipSync(new Uint8Array(archive))));
3407
- if (unzipError) {
3408
- logger.debug(`[uip.ship.succeeded] Could not inspect solution package zip: ${unzipError.message}`);
3462
+ const [unzipError, entries] = await catchError(unzipProjectManifests(new Uint8Array(archive)));
3463
+ if (unzipError || !entries) {
3464
+ logger.debug(`[uip.ship.succeeded] Could not inspect solution package zip: ${unzipError?.message ?? "no entries"}`);
3409
3465
  return { projectType: "unknown" };
3410
3466
  }
3411
3467
  const decoder = new TextDecoder;
3412
3468
  const projectTypes = [];
3413
3469
  for (const [entryName, entryBytes] of Object.entries(entries)) {
3414
- if (!entryName.endsWith(".uipx")) {
3470
+ if (!isProjectManifestEntry(entryName)) {
3415
3471
  continue;
3416
3472
  }
3417
3473
  const [parseError, parsedTypes] = await catchError(Promise.resolve().then(() => readProjectTypesFromUipx(decoder.decode(entryBytes))));
@@ -3500,6 +3556,7 @@ var NOT_A_ZIP_INSTRUCTIONS = "Ensure file is a valid .zip solution package from:
3500
3556
  var UPLOAD_GENERIC_INSTRUCTIONS = "The upload did not complete. Check the error message; if it persists, retry with UIP_LOG_LEVEL=debug and report the output.";
3501
3557
  var UPLOAD_REJECTED_INSTRUCTIONS = "The solution feed rejected the package contents; re-packing the same source will reproduce it. Fix the project named in the error detail, or report the error code with your package name.";
3502
3558
  var NOT_PACKED_INSTRUCTIONS = "Pack the solution into a .zip first with 'uip solution pack <solution-path> <output-path>', then publish that .zip.";
3559
+ var PROJECT_PACKAGE_INSTRUCTIONS = "A .nupkg is a single project's package (e.g. from 'uip maestro flow pack') and the solution feed does not accept it. Pack the enclosing solution instead: 'uip solution pack <solution-path> <output-dir>', then publish the .zip it writes.";
3503
3560
  var suggestedPackOutput = (src) => `${src.replace(/\.(uis|uipx)$/i, "").replace(/[\\/]+$/, "")}.zip`;
3504
3561
  var POLL_PENDING_INSTRUCTIONS = "Package may still be pending. Check package state with: uip solution packages list.";
3505
3562
  var PUBLISH_INSTRUCTIONS = {
@@ -3507,6 +3564,7 @@ var PUBLISH_INSTRUCTIONS = {
3507
3564
  file_not_found: "Ensure the file path is correct. Build a package first with: uip solution pack",
3508
3565
  not_a_zip: NOT_A_ZIP_INSTRUCTIONS,
3509
3566
  not_packed: NOT_PACKED_INSTRUCTIONS,
3567
+ project_package: PROJECT_PACKAGE_INSTRUCTIONS,
3510
3568
  file_read_failed: "",
3511
3569
  metadata_rewrite_failed: METADATA_REWRITE_INSTRUCTIONS,
3512
3570
  personal_workspace_resolution_failed: "Verify the current user has a Personal Workspace configured in Orchestrator, then retry. To publish to the tenant feed instead, omit --personal-workspace.",
@@ -3525,10 +3583,8 @@ var EXIT_CODES_BY_REASON = {
3525
3583
  };
3526
3584
  var registerPublishCommand = (program) => {
3527
3585
  const command = program.command("publish").description("Publish a solution package (.zip) to the UiPath solution feed. The .zip must be produced by 'solution pack'. By default the package lands in the tenant feed; use --personal-workspace to target the current user's Personal Workspace feed. After publishing, use 'solution deploy run' to deploy it.").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).argument("<packagePath>", "Path to a .zip package file produced by 'solution pack'.").option("--personal-workspace", "Upload the package into the current user's Personal Workspace feed instead of the tenant feed. Resolves the workspace folder key from Orchestrator automatically.").option("--package-name <name>", "Publish under this package name instead of the one the .zip was packed with. Rewrites the archive's solutionMetadata.json in memory (with a fresh package version key); the file on disk is left untouched. The bundled project packages keep their own names and versions.").option("--package-version <version>", "Publish under this package version instead of the one the .zip was packed with. Use it to clear a 'version already exists' rejection when you have the packed .zip but not the solution source. Same in-memory rewrite as --package-name.").option("--wait", "Wait until the published package is Ready or Active").option("--timeout <seconds>", "Timeout in seconds for package state polling (default: 360)", (v) => parseInt(v, 10), 360).option("--poll-interval <ms>", "Milliseconds between package state polls (default: 5000)", (v) => parseInt(v, 10), 5000).option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10);
3528
- if (isPreviewBuild()) {
3529
- command.addOption(new Option("--feed <name-or-key>", "Publish into a feed by its name or folder key (from 'solution feeds list') instead of the tenant feed. Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace"));
3530
- }
3531
- command.examples(isPreviewBuild() ? [...SOLUTION_PUBLISH_EXAMPLES, SOLUTION_PUBLISH_FEED_EXAMPLE] : SOLUTION_PUBLISH_EXAMPLES).trackedAction(processContext, async (packagePath, options) => {
3586
+ command.addOption(new Option("--feed <name-or-key>", "Publish into a feed by its name or folder key (from 'solution feeds list') instead of the tenant feed. Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace"));
3587
+ command.examples([...SOLUTION_PUBLISH_EXAMPLES, SOLUTION_PUBLISH_FEED_EXAMPLE]).trackedAction(processContext, async (packagePath, options) => {
3532
3588
  const result = await publishSolutionAsync(packagePath, {
3533
3589
  tenant: options.tenant,
3534
3590
  personalWorkspace: options.personalWorkspace,
@@ -4193,6 +4249,21 @@ var RESOURCE_GET_EXAMPLES = [
4193
4249
  }
4194
4250
  }
4195
4251
  },
4252
+ {
4253
+ Description: "When the linked live resource has drifted from the local declaration (matchingOverwriteConfiguration: false), the live spec is returned as liveSpec next to the declared spec",
4254
+ Command: "uip solution resources get 0f8a1c9e-2b47-4d0a-9c31-5e2f6d8a7b10",
4255
+ Output: {
4256
+ Code: "ResourceConfiguration",
4257
+ Data: {
4258
+ key: "0f8a1c9e-2b47-4d0a-9c31-5e2f6d8a7b10",
4259
+ spec: { name: "Approval App", version: "7.0.0" },
4260
+ liveSpec: { name: "Approval App", version: "12.0.0" },
4261
+ resourceOverwrite: {
4262
+ matchingOverwriteConfiguration: false
4263
+ }
4264
+ }
4265
+ }
4266
+ },
4196
4267
  {
4197
4268
  Description: "Get the configuration plus all dependency configurations",
4198
4269
  Command: "uip solution resources get fb39bc38-255e-4fea-93c6-7c7eb44d766f --solution-folder ./my-solution --include-dependencies",
@@ -4278,7 +4349,10 @@ var registerResourceGetCommand = (resource) => {
4278
4349
  const includeDeps = options.includeDependencies === true;
4279
4350
  const [fetchError, data] = await catchError(includeDeps ? builder.getAllConfigurationsAsync(resourceKey) : builder.getConfigurationAsync(resourceKey));
4280
4351
  if (!fetchError) {
4281
- await refreshArtefactSpecAsync(builder, data, includeDeps);
4352
+ const resource2 = await refreshArtefactSpecAsync(builder, data, includeDeps);
4353
+ if (!includeDeps) {
4354
+ await attachLiveSpecOnMismatchAsync(services, resource2, data);
4355
+ }
4282
4356
  await builder.disposeAsync();
4283
4357
  OutputFormatter.success({
4284
4358
  Result: RESULTS.Success,
@@ -4352,22 +4426,40 @@ function mapDefinitionToConfiguration(def) {
4352
4426
  isOverridable: def.isOverridable
4353
4427
  };
4354
4428
  }
4429
+ async function attachLiveSpecOnMismatchAsync(services, resource, config) {
4430
+ const overwriteData = config.resourceOverwrite;
4431
+ const overwrite = overwriteData?.resourceOverwrite?.overwrite;
4432
+ if (overwriteData?.matchingOverwriteConfiguration !== false || !overwrite || !resource) {
4433
+ return;
4434
+ }
4435
+ const importer = services.container.resolve(Tokens.IImportResourceHelper);
4436
+ const [exportError, exported] = await catchError(importer.exportResourceOverwriteAsync(resource, overwrite));
4437
+ if (exportError || !exported?.spec) {
4438
+ return;
4439
+ }
4440
+ config.liveSpec = exported.spec;
4441
+ }
4355
4442
  async function refreshArtefactSpecAsync(builder, data, includeDeps) {
4356
4443
  const [loadError, solution] = await catchError(builder.context.loadAsync());
4357
4444
  if (loadError || !solution) {
4358
4445
  return;
4359
4446
  }
4447
+ let requestedResource;
4360
4448
  const configs = includeDeps ? data.resources ?? [] : [data];
4361
4449
  for (const config of configs) {
4362
4450
  if (!config?.key) {
4363
4451
  continue;
4364
4452
  }
4365
4453
  const resource = solution.resources.find((r) => r.key === config.key);
4454
+ if (!includeDeps) {
4455
+ requestedResource = resource;
4456
+ }
4366
4457
  if (!resource?.projectKey) {
4367
4458
  continue;
4368
4459
  }
4369
4460
  config.spec = await applyEntryPointEnhancementAsync(builder.context, resource, config.spec ?? {});
4370
4461
  }
4462
+ return requestedResource;
4371
4463
  }
4372
4464
  async function findRemoteReferenceByKey(services, resourceKey) {
4373
4465
  const rcs = services.container.resolve(Tokens.IResourceCatalogClientProxy);
@@ -4396,9 +4488,130 @@ async function findRemoteReferenceByKey(services, resourceKey) {
4396
4488
  return null;
4397
4489
  }
4398
4490
 
4491
+ // src/services/resource-version-lookup.ts
4492
+ var VERSION_PROPERTY = "version";
4493
+ var LOOKUP_CONCURRENCY = 5;
4494
+ async function resolveResourceVersionsAsync(services, builder, targets) {
4495
+ const versions = new Map;
4496
+ if (targets.length === 0) {
4497
+ return versions;
4498
+ }
4499
+ const resolveKind = await createResourceKindMetadataResolver(services);
4500
+ const versioned = [];
4501
+ for (const target of targets) {
4502
+ const metadata = await resolveKind(target.kind, target.type);
4503
+ if (metadata?.specProperties.has(VERSION_PROPERTY)) {
4504
+ versioned.push(target);
4505
+ }
4506
+ }
4507
+ if (versioned.length === 0) {
4508
+ return versions;
4509
+ }
4510
+ const importer = services.container.resolve(Tokens.IImportResourceHelper);
4511
+ for (let i = 0;i < versioned.length; i += LOOKUP_CONCURRENCY) {
4512
+ const batch = versioned.slice(i, i + LOOKUP_CONCURRENCY);
4513
+ const resolved = await Promise.all(batch.map((target) => target.source === "Local" ? readLocalVersionAsync(builder, target) : readRemoteVersionAsync(importer, target)));
4514
+ for (const [id, version] of resolved) {
4515
+ if (version !== undefined) {
4516
+ versions.set(id, version);
4517
+ }
4518
+ }
4519
+ }
4520
+ return versions;
4521
+ }
4522
+ async function readLocalVersionAsync(builder, target) {
4523
+ const [error, configuration] = await catchError(builder.getConfigurationAsync(target.key));
4524
+ if (error || !configuration) {
4525
+ warnLookupFailed(target, error);
4526
+ return [target.id, undefined];
4527
+ }
4528
+ return [target.id, readVersion(configuration.spec)];
4529
+ }
4530
+ async function readRemoteVersionAsync(importer, target) {
4531
+ const reference = {
4532
+ kind: target.kind,
4533
+ type: target.type,
4534
+ key: target.key,
4535
+ name: target.name,
4536
+ folder: target.folderFullyQualifiedName ? {
4537
+ fullyQualifiedName: target.folderFullyQualifiedName,
4538
+ folderKey: target.folderKey
4539
+ } : undefined
4540
+ };
4541
+ const [error, payload] = await catchError(importer.exportResourceAsync(reference));
4542
+ if (error || !payload) {
4543
+ warnLookupFailed(target, error);
4544
+ return [target.id, undefined];
4545
+ }
4546
+ const definitions = payload.resourceDefinitions ?? [];
4547
+ const primary = definitions.find((d) => d.key === target.key);
4548
+ if (!primary) {
4549
+ logger.warn(`Export for ${target.kind} '${target.name ?? target.key}' returned no definition for that key; version omitted.`);
4550
+ return [target.id, undefined];
4551
+ }
4552
+ return [target.id, readVersion(primary.spec)];
4553
+ }
4554
+ function readVersion(spec) {
4555
+ if (!spec) {
4556
+ return;
4557
+ }
4558
+ let value = spec[VERSION_PROPERTY];
4559
+ if (value === undefined) {
4560
+ for (const [key, candidate] of Object.entries(spec)) {
4561
+ if (key.toLowerCase() === VERSION_PROPERTY) {
4562
+ value = candidate;
4563
+ break;
4564
+ }
4565
+ }
4566
+ }
4567
+ if (value === undefined || value === null || value === "") {
4568
+ return;
4569
+ }
4570
+ return typeof value === "string" ? value : String(value);
4571
+ }
4572
+ function warnLookupFailed(target, error) {
4573
+ logger.warn(`Failed to read the version of ${target.kind} '${target.name ?? target.key}': ${error?.message ?? "no configuration returned"}`);
4574
+ }
4575
+
4399
4576
  // src/commands/resource-list.ts
4577
+ var RESOURCE_LIST_EXAMPLES = [
4578
+ {
4579
+ Description: "List every resource the solution holds and every one available in Orchestrator",
4580
+ Command: "uip solution resources list",
4581
+ Output: {
4582
+ Code: "ResourceList",
4583
+ Data: [
4584
+ {
4585
+ Source: "Remote",
4586
+ Key: "0c2b7f04-1d3e-4a55-9f77-2b1a6d9e4c81",
4587
+ Name: "InvoiceQueue",
4588
+ Kind: "queue",
4589
+ Folder: "Shared"
4590
+ }
4591
+ ]
4592
+ }
4593
+ },
4594
+ {
4595
+ Description: "Show the version deployed per folder for classic Apps. --include-version reads the live `version` spec property, which is what Orchestrator's Automations > Apps page shows; it costs one extra call per resource, so it is off by default",
4596
+ Command: "uip solution resources list --kind app --source remote --include-version",
4597
+ Output: {
4598
+ Code: "ResourceList",
4599
+ Data: [
4600
+ {
4601
+ Source: "Remote",
4602
+ Key: "a9c9d05f-6d85-41cd-8a54-a479d494bedc",
4603
+ Name: "Validation App",
4604
+ Kind: "app",
4605
+ Folder: "Shared/Validation",
4606
+ FolderKey: "0932a1f4-5cd1-44d4-9f02-cdc92244a6d2",
4607
+ Version: "12.0.0"
4608
+ }
4609
+ ]
4610
+ }
4611
+ }
4612
+ ];
4400
4613
  var registerResourceListCommand = (resource) => {
4401
- resource.command("list").description("List resources in the solution and/or available in Orchestrator. " + "Shows local solution resources, remote Orchestrator resources, or both.").option("--solution-folder <path>", "Path to the solution root folder (defaults to current directory)").option("--kind <kind>", "Filter by resource kind (e.g. Queue, Asset, Bucket, Process, Connection, App, Index, Trigger, Entity, ChoiceSet). Any kind RCS indexes is accepted.").option("--search <term>", "Filter resources by name").option("--source <source>", "Resource source: all, local, or remote (default: all)", "all").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).trackedAction(processContext, async (options) => {
4614
+ resource.command("list").description("List resources in the solution and/or available in Orchestrator. " + "Shows local solution resources, remote Orchestrator resources, or both.").option("--solution-folder <path>", "Path to the solution root folder (defaults to current directory)").option("--kind <kind>", "Filter by resource kind (e.g. Queue, Asset, Bucket, Process, Connection, App, Index, Trigger, Entity, ChoiceSet). Any kind RCS indexes is accepted.").option("--search <term>", "Filter resources by name").option("--source <source>", "Resource source: all, local, or remote (default: all)", "all").option("--include-version", "Also report each resource's version, for the kinds that carry one (app, appVersion, package, process). " + "Remote rows report the version live in the folder, local rows the version the solution pins. " + "Costs one extra call per resource, so it is off by default.").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).examples(RESOURCE_LIST_EXAMPLES).trackedAction(processContext, async (options) => {
4402
4615
  const solutionPath = options.solutionFolder ?? ".";
4403
4616
  const fs = getFileSystem();
4404
4617
  const resolvedPath = fs.path.resolve(fs.env.cwd(), solutionPath);
@@ -4455,7 +4668,7 @@ var registerResourceListCommand = (resource) => {
4455
4668
  processContext.exit(1);
4456
4669
  return;
4457
4670
  }
4458
- const [listError, data] = await catchError(listResources(builder, services, source, normalizedKind, options.search));
4671
+ const [listError, data] = await catchError(listResources(builder, services, source, normalizedKind, options.search, options.includeVersion === true));
4459
4672
  await builder.disposeAsync();
4460
4673
  if (listError) {
4461
4674
  OutputFormatter.error({
@@ -4473,7 +4686,14 @@ var registerResourceListCommand = (resource) => {
4473
4686
  });
4474
4687
  });
4475
4688
  };
4476
- async function listResources(builder, services, source, kind, search) {
4689
+ async function listResources(builder, services, source, kind, search, includeVersion = false) {
4690
+ const entries = await collectResources(builder, services, source, kind, search);
4691
+ if (includeVersion) {
4692
+ await annotateVersions(services, builder, entries);
4693
+ }
4694
+ return entries;
4695
+ }
4696
+ async function collectResources(builder, services, source, kind, search) {
4477
4697
  const includeLocal = source === "local" || source === "all";
4478
4698
  const includeRemote = source === "remote" || source === "all";
4479
4699
  if (kind) {
@@ -4491,6 +4711,40 @@ async function listResources(builder, services, source, kind, search) {
4491
4711
  }
4492
4712
  return all;
4493
4713
  }
4714
+ function versionLookupId(entry) {
4715
+ return `${entry.Source}|${entry.Key}|${entry.Folder ?? ""}`;
4716
+ }
4717
+ async function annotateVersions(services, builder, entries) {
4718
+ const targets = new Map;
4719
+ for (const entry of entries) {
4720
+ if (entry.Key === "") {
4721
+ continue;
4722
+ }
4723
+ const id = versionLookupId(entry);
4724
+ if (targets.has(id)) {
4725
+ continue;
4726
+ }
4727
+ targets.set(id, {
4728
+ id,
4729
+ key: entry.Key,
4730
+ kind: entry.Kind,
4731
+ type: entry.Type,
4732
+ name: entry.Name,
4733
+ source: entry.Source === "Local" ? "Local" : "Remote",
4734
+ folderFullyQualifiedName: entry.Folder,
4735
+ folderKey: entry.FolderKey
4736
+ });
4737
+ }
4738
+ const versions = await resolveResourceVersionsAsync(services, builder, [
4739
+ ...targets.values()
4740
+ ]);
4741
+ for (const entry of entries) {
4742
+ const version = versions.get(versionLookupId(entry));
4743
+ if (version !== undefined) {
4744
+ entry.Version = version;
4745
+ }
4746
+ }
4747
+ }
4494
4748
  async function listAllRemoteResources(services, search) {
4495
4749
  const rcs = services.container.resolve(Tokens.IResourceCatalogClientProxy);
4496
4750
  const entries = [];
@@ -4582,6 +4836,7 @@ var RESOURCE_REFRESH_EXAMPLES = [
4582
4836
  Created: 2,
4583
4837
  Imported: 1,
4584
4838
  Skipped: 5,
4839
+ Pruned: 0,
4585
4840
  Warnings: [],
4586
4841
  Status: "Synced 3 resources (5 already in solution)"
4587
4842
  }
@@ -4595,7 +4850,7 @@ var FAILURE_MESSAGE = {
4595
4850
  sync_failed: "Failed to refresh resources"
4596
4851
  };
4597
4852
  var registerResourceRefreshSubcommand = (resource) => {
4598
- 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. " + "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) => {
4853
+ 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) => {
4599
4854
  const loginValidityMinutes = options.loginValidity ? parseInt(options.loginValidity, 10) : undefined;
4600
4855
  const result = await resourceRefreshAsync(options.solutionFolder ?? ".", { loginValidity: loginValidityMinutes });
4601
4856
  if (!result.ok) {
@@ -4607,6 +4862,7 @@ var registerResourceRefreshSubcommand = (resource) => {
4607
4862
  processContext.exit(1);
4608
4863
  return;
4609
4864
  }
4865
+ const prunedNote = result.pruned > 0 ? `, ${result.pruned} duplicates removed` : "";
4610
4866
  OutputFormatter.success({
4611
4867
  Result: RESULTS.Success,
4612
4868
  Code: "ResourceRefresh",
@@ -4614,8 +4870,9 @@ var registerResourceRefreshSubcommand = (resource) => {
4614
4870
  Created: result.created,
4615
4871
  Imported: result.imported,
4616
4872
  Skipped: result.skipped,
4873
+ Pruned: result.pruned,
4617
4874
  Warnings: result.warnings,
4618
- Status: `Synced ${result.created + result.imported} resources (${result.skipped} already in solution)`
4875
+ Status: `Synced ${result.created + result.imported} resources (${result.skipped} already in solution${prunedNote})`
4619
4876
  }
4620
4877
  });
4621
4878
  });
@@ -4768,23 +5025,22 @@ class RestoreCommandService {
4768
5025
  this.fs = getFileSystem();
4769
5026
  }
4770
5027
  async execute(solutionPath, options, context) {
4771
- const absoluteSolutionPath = this.fs.path.resolve(solutionPath);
4772
- const [statError, stats] = await catchError(this.fs.stat(absoluteSolutionPath));
4773
- if (statError || !stats) {
4774
- const msg = statError?.message ?? `Solution path does not exist: ${absoluteSolutionPath}`;
5028
+ const check = await checkSolutionPathAsync(this.fs, solutionPath);
5029
+ if (!check.ok) {
4775
5030
  OutputFormatter.error({
4776
5031
  Result: RESULTS.Failure,
4777
- Message: msg,
4778
- Instructions: "Ensure the solution path exists and points to a solution directory (containing .uipx) or a .uis file."
5032
+ Message: check.message,
5033
+ Instructions: SOLUTION_PATH_INSTRUCTIONS,
5034
+ ErrorCode: check.errorCode
4779
5035
  });
4780
- if (statError?.stack) {
5036
+ if (check.statError?.stack) {
4781
5037
  logger.error("Stack trace:");
4782
- logger.error(statError.stack);
5038
+ logger.error(check.statError.stack);
4783
5039
  }
4784
5040
  context.exit(1);
4785
5041
  return;
4786
5042
  }
4787
- const isUisFile = stats.isFile() && absoluteSolutionPath.endsWith(".uis");
5043
+ const { absoluteSolutionPath, isUisFile } = check;
4788
5044
  const solutionName = isUisFile ? this.fs.path.basename(solutionPath, ".uis") : this.fs.path.basename(solutionPath);
4789
5045
  logger.info(`
4790
5046
  Restoring solution dependencies...
@@ -4798,7 +5054,7 @@ Restoring solution dependencies...
4798
5054
  OutputFormatter.error({
4799
5055
  Result: RESULTS.Failure,
4800
5056
  Message: msg,
4801
- Instructions: "Check that you are logged in (`uip auth login`) and that the solution path is accessible, then rerun: uip solution restore <solutionPath>"
5057
+ Instructions: carriedInstructions(error) ?? "Check that you are logged in (`uip auth login`) and that the solution path is accessible, then rerun: uip solution restore <solutionPath>"
4802
5058
  });
4803
5059
  if (error instanceof Error && error.stack) {
4804
5060
  logger.error("Stack trace:");
@@ -4826,9 +5082,14 @@ Restoring solution dependencies...
4826
5082
  });
4827
5083
  }
4828
5084
  async executeAsync(solutionPath, options) {
4829
- const absoluteSolutionPath = this.fs.path.resolve(solutionPath);
4830
- const stats = await this.fs.stat(absoluteSolutionPath);
4831
- const isUisFile = !!stats && stats.isFile() && absoluteSolutionPath.endsWith(".uis");
5085
+ const check = await checkSolutionPathAsync(this.fs, solutionPath);
5086
+ if (!check.ok) {
5087
+ if (check.statError) {
5088
+ throw check.statError;
5089
+ }
5090
+ return new ToolResult("INTERNAL_ERROR" /* InternalError */, check.message, []);
5091
+ }
5092
+ const { absoluteSolutionPath, isUisFile } = check;
4832
5093
  const solutionName = isUisFile ? this.fs.path.basename(solutionPath, ".uis") : this.fs.path.basename(solutionPath);
4833
5094
  const packager = await this.ensurePackagerAsync();
4834
5095
  let inputPath;
@@ -4852,7 +5113,7 @@ Restoring solution dependencies...
4852
5113
  const restoreOptions = new SolutionPackOptions;
4853
5114
  restoreOptions.inputPath = inputPath;
4854
5115
  restoreOptions.configuration = "Release" /* Release */;
4855
- restoreOptions.excludeConfiguredSources = false;
5116
+ restoreOptions.excludeConfiguredSources = options.excludeConfiguredSources ?? false;
4856
5117
  restoreOptions.package = {
4857
5118
  id: solutionName,
4858
5119
  version: "1.0.0",
@@ -4882,11 +5143,12 @@ var SOLUTION_RESTORE_EXAMPLES = [
4882
5143
  }
4883
5144
  ];
4884
5145
  var registerRestoreCommand = (program) => {
4885
- program.command("restore").description("Restore NuGet dependencies for every project in a UiPath solution using the authenticated session. Accepts a solution directory (containing .uipx) or a .uis file. Resolves dependencies in place and does not produce a package — run it before 'uip solution pack'.").argument("<solutionPath>", "Path to a solution directory (containing .uipx) or a .uis file").option("--nuget-sources-config-path <path>", "Path to NuGet sources configuration file").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("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).examples(SOLUTION_RESTORE_EXAMPLES).trackedAction(processContext, async (solutionPath, options) => {
5146
+ program.command("restore").description("Restore NuGet dependencies for every project in a UiPath solution using the authenticated session. Accepts a solution directory (containing .uipx) or a .uis file. Resolves dependencies in place and does not produce a package — run it before 'uip solution pack'.").argument("<solutionPath>", "Path to a solution directory (containing .uipx) or a .uis file").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("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).examples(SOLUTION_RESTORE_EXAMPLES).trackedAction(processContext, async (solutionPath, options) => {
4886
5147
  const service = new RestoreCommandService;
4887
5148
  await service.execute(solutionPath, {
4888
5149
  loginValidity: options.loginValidity,
4889
5150
  nugetSourcesConfigPath: options.nugetSourcesConfigPath,
5151
+ excludeConfiguredSources: options.excludeConfiguredSources,
4890
5152
  feedFolder: options.feedFolder
4891
5153
  }, processContext);
4892
5154
  });
@@ -4975,7 +5237,7 @@ class ExecutorsFactoryRepository {
4975
5237
  getProjectExecutorFactory(projectType) {
4976
5238
  const factory = this.factoryMap.get(projectType);
4977
5239
  if (!factory) {
4978
- throw new Error(`No executor is registered for project type '${projectType}'. ` + `Registered types: ${this.registeredTypes.join(", ") || "none"}.`);
5240
+ throw new Error(`Cannot run project type '${projectType}': no runner is installed for it. ` + `Project types that can be run: ${this.registeredTypes.join(", ") || "none"}.`);
4979
5241
  }
4980
5242
  return factory;
4981
5243
  }
@@ -5046,7 +5308,7 @@ class ProjectRunner {
5046
5308
  const { projectType, name } = options.project;
5047
5309
  if (!this.canRun(projectType)) {
5048
5310
  const known = this.runnableTypes.join(", ") || "none";
5049
- return RunResult.failed("UnsupportedProjectType" /* UnsupportedProjectType */, `No executor is registered for project type '${projectType}' ` + `(project '${name}'). Runnable types: ${known}.`);
5311
+ return RunResult.failed("UnsupportedProjectType" /* UnsupportedProjectType */, `Cannot run project '${name}' (project type '${projectType}'): ` + `no runner is installed for it. Project types that can be run: ${known}.`);
5050
5312
  }
5051
5313
  let executor;
5052
5314
  try {
@@ -5093,7 +5355,7 @@ async function ensureExecutorTools(projectTypes) {
5093
5355
  if (toolVerb) {
5094
5356
  neededTools.add(toolVerb);
5095
5357
  } else {
5096
- logger.warn(`No CLI tool provides an executor for project type '${type}'. Run may fail.`);
5358
+ logger.warn(`No installable tool runs project type '${type}'. Run may fail.`);
5097
5359
  }
5098
5360
  }
5099
5361
  for (const toolVerb of neededTools) {
@@ -5155,11 +5417,11 @@ async function provisionSolutionForDebugAsync(options, deps = {}) {
5155
5417
  logger.info("Resolving the personal workspace to deploy into...");
5156
5418
  const [locationsError, locations] = await catchError(listLocations(authOptions));
5157
5419
  if (locationsError) {
5158
- return fail2(`Could not resolve a personal workspace to deploy into: ${locationsError.message}`);
5420
+ return fail(`Could not resolve a personal workspace to deploy into: ${locationsError.message}`);
5159
5421
  }
5160
5422
  const personal = locations.find((l) => l.isMyPersonalWorkspace);
5161
5423
  if (!personal) {
5162
- return fail2("This account has no personal workspace, so there is nowhere to deploy the solution for a debug run. " + "Ask an administrator to enable a personal workspace, or run with --no-provision-resources — " + "the run then starts without a deployment, and cross-project calls will not resolve.");
5424
+ return fail("This account has no personal workspace, so there is nowhere to deploy the solution for a debug run. " + "Ask an administrator to enable a personal workspace, or run with --no-provision-resources — " + "the run then starts without a deployment, and cross-project calls will not resolve.");
5163
5425
  }
5164
5426
  const version = options.version ?? nextDebugVersion();
5165
5427
  const fs = getFileSystem();
@@ -5173,13 +5435,13 @@ async function provisionSolutionForDebugAsync(options, deps = {}) {
5173
5435
  outputPath
5174
5436
  });
5175
5437
  if (!uploaded.ok) {
5176
- return fail2(uploaded.message);
5438
+ return fail(uploaded.message);
5177
5439
  }
5178
5440
  const packageVersionKey = uploaded.packageVersionKey;
5179
5441
  logger.info("Provisioning the solution's resources for the run...");
5180
5442
  const [builderError, builder] = await catchError(getBuilder(options.solutionDir));
5181
5443
  if (builderError) {
5182
- return fail2(`Could not read the solution: ${builderError.message}`);
5444
+ return fail(`Could not read the solution: ${builderError.message}`);
5183
5445
  }
5184
5446
  const sessionId = newSessionId();
5185
5447
  const [debugError, debug] = await catchError(builder.debugProjectAsync(personal.key, options.solutionName, sessionId, packageVersionKey, true));
@@ -5190,11 +5452,11 @@ async function provisionSolutionForDebugAsync(options, deps = {}) {
5190
5452
  }
5191
5453
  await builder.disposeAsync().catch(() => {});
5192
5454
  if (debugError) {
5193
- return fail2(`Provisioning the solution failed: ${debugError.message}`);
5455
+ return fail(`Provisioning the solution failed: ${debugError.message}`);
5194
5456
  }
5195
5457
  if (debug.complete === false) {
5196
5458
  if (!solutionKey) {
5197
- return fail2("The cloud is still provisioning the solution, but its key could not be read back from the .uipx, so the provisioning cannot be waited on. Retry the run.");
5459
+ return fail("The cloud is still provisioning the solution, but its key could not be read back from the .uipx, so the provisioning cannot be waited on. Retry the run.");
5198
5460
  }
5199
5461
  return waitForProvisioningAsync({
5200
5462
  solutionKey,
@@ -5212,7 +5474,7 @@ async function provisionSolutionForDebugAsync(options, deps = {}) {
5212
5474
  packageVersionKey
5213
5475
  };
5214
5476
  }
5215
- function fail2(message) {
5477
+ function fail(message) {
5216
5478
  return { ok: false, message };
5217
5479
  }
5218
5480
  async function packAndUploadAsync(args) {
@@ -5285,13 +5547,13 @@ async function waitForProvisioningAsync(args) {
5285
5547
  signal: processContext.pollSignal
5286
5548
  });
5287
5549
  if (poll.outcome !== PollOutcome.Completed || !poll.data) {
5288
- return fail2(describeIncompleteWait(poll, timeoutMs));
5550
+ return fail(describeIncompleteWait(poll, timeoutMs));
5289
5551
  }
5290
5552
  const info = poll.data;
5291
5553
  if (!isDebugProvisioningSucceeded(info.status)) {
5292
5554
  const details = (info.errors ?? []).map((error) => error.resource ? `${error.resource.kind ?? "resource"} '${error.resource.name ?? error.resource.key ?? ""}': ${error.message}` : error.message).join(`
5293
5555
  `);
5294
- return fail2(`Provisioning the solution failed (${info.status}).` + (details ? `
5556
+ return fail(`Provisioning the solution failed (${info.status}).` + (details ? `
5295
5557
  ${details}` : ""));
5296
5558
  }
5297
5559
  logger.info("Solution provisioned.");
@@ -5337,7 +5599,7 @@ async function runSolutionAsync(options, deps, cancellationToken) {
5337
5599
  }) : Promise.resolve(undefined)
5338
5600
  ]);
5339
5601
  if (executorOutcome.status === "rejected") {
5340
- return RunResult.failed("PrepareFailed" /* PrepareFailed */, `Could not load the executor for project type '${selected.projectType}': ${describeError(executorOutcome.reason)}`);
5602
+ return RunResult.failed("PrepareFailed" /* PrepareFailed */, `Could not load the runner for project type '${selected.projectType}': ${describeError(executorOutcome.reason)}`);
5341
5603
  }
5342
5604
  if (provisionOutcome.status === "rejected") {
5343
5605
  return RunResult.failed("PrepareFailed" /* PrepareFailed */, `Could not deploy the solution before the run: ${describeError(provisionOutcome.reason)}`);
@@ -5401,7 +5663,9 @@ async function resolveConnection(folderPath, options, deps) {
5401
5663
  };
5402
5664
  }
5403
5665
  function describeError(reason) {
5404
- return reason instanceof Error ? reason.message : String(reason);
5666
+ const message = reason instanceof Error ? reason.message : String(reason);
5667
+ const instructions = carriedInstructions(reason);
5668
+ return instructions ? `${message} ${instructions}` : message;
5405
5669
  }
5406
5670
  function selectProject(runnable, requested) {
5407
5671
  if (requested) {
@@ -5519,32 +5783,53 @@ function describeJson(value) {
5519
5783
  // src/commands/upload.ts
5520
5784
  var SOLUTION_UPLOAD_EXAMPLES = [
5521
5785
  {
5522
- Description: "Upload a solution directory to Studio Web as a new solution",
5786
+ Description: "Upload a solution directory. Studio Web has no solution with its SolutionId, so it is imported as new and the local .uipx is updated with the assigned id (reported under Data.LocalSolutionIdUpdated).",
5523
5787
  Command: "uip solution upload ./my-solution",
5524
5788
  Output: {
5525
5789
  Code: "SolutionUpload",
5526
5790
  Data: {
5527
5791
  Status: "Uploaded successfully",
5792
+ Action: "Imported",
5528
5793
  SolutionId: "a1b2c3d4-0000-0000-0000-000000000001",
5529
- DesignerUrl: "https://cloud.uipath.com/myorg/mytenant/studio_/designer/p1?solutionId=a1b2c3d4-0000-0000-0000-000000000001"
5794
+ DesignerUrl: "https://cloud.uipath.com/myorg/studio_/designer/p1?solutionId=a1b2c3d4-0000-0000-0000-000000000001",
5795
+ LocalSolutionIdUpdated: {
5796
+ Path: "/workspace/my-solution/my-solution.uipx",
5797
+ From: "00000000-0000-0000-0000-0000000000ff",
5798
+ To: "a1b2c3d4-0000-0000-0000-000000000001"
5799
+ }
5530
5800
  }
5531
5801
  }
5532
5802
  },
5533
5803
  {
5534
- Description: "Force-overwrite the existing Studio Web solution that the local .uipx points to (destroys cloud version history)",
5535
- Command: "uip solution upload ./my-solution --force",
5804
+ Description: "Same command when the solution already exists on Studio Web: it is overwritten in place, with the replaced contents recorded first as a restorable version.",
5805
+ Command: "uip solution upload ./my-solution",
5536
5806
  Output: {
5537
5807
  Code: "SolutionUpload",
5538
5808
  Data: {
5539
5809
  Status: "Uploaded successfully",
5810
+ Action: "Overwritten",
5540
5811
  SolutionId: "a1b2c3d4-0000-0000-0000-000000000001",
5541
- DesignerUrl: "https://cloud.uipath.com/myorg/mytenant/studio_/designer/p1?solutionId=a1b2c3d4-0000-0000-0000-000000000001"
5812
+ DesignerUrl: "https://cloud.uipath.com/myorg/studio_/designer/p1?solutionId=a1b2c3d4-0000-0000-0000-000000000001",
5813
+ SnapshotCreated: true
5814
+ }
5815
+ }
5816
+ },
5817
+ {
5818
+ Description: "Overwrite without recording a pre-overwrite version. Changes made in Studio Web since the last upload are lost; local files are unaffected.",
5819
+ Command: "uip solution upload ./my-solution --no-snapshot",
5820
+ Output: {
5821
+ Code: "SolutionUpload",
5822
+ Data: {
5823
+ Status: "Uploaded successfully",
5824
+ Action: "Overwritten",
5825
+ SolutionId: "a1b2c3d4-0000-0000-0000-000000000001",
5826
+ DesignerUrl: "https://cloud.uipath.com/myorg/studio_/designer/p1?solutionId=a1b2c3d4-0000-0000-0000-000000000001"
5542
5827
  }
5543
5828
  }
5544
5829
  }
5545
5830
  ];
5546
5831
  var registerUploadCommand = (program) => {
5547
- program.command("upload").description("Upload a solution to UiPath Studio Web for browser-based editing. Accepts a solution directory, .uipx file, or .uis file. The command probes Studio Web for the bundled SolutionId: if the cloud solution does not exist it is imported as new; if it already exists the upload is refused unless --force is passed. Forcing replaces the cloud project in place and wipes its Studio Web version history.").argument("<solutionPath>", "Path to a solution directory (containing .uipx), a .uipx file, or a .uis file.").option("--force", "Force-overwrite the existing Studio Web solution referenced by the SolutionId in the local .uipx. This replaces the cloud project in place and destroys its Studio Web version history. Required whenever the cloud already has a solution with that SolutionId; ignored when the cloud has none.").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).examples(SOLUTION_UPLOAD_EXAMPLES).trackedAction(processContext, async (solutionPath, options) => {
5832
+ program.command("upload").description("Upload a solution to UiPath Studio Web for browser-based editing. Accepts a solution directory, .uipx file, or .uis file. If Studio Web has no solution with the bundled SolutionId, the solution is imported as new and the local .uipx is updated with the assigned id; otherwise it is overwritten in place, with the replaced contents recorded first as a restorable version (skip with --no-snapshot). The output field Data.Action says which happened: 'Imported' or 'Overwritten'.").argument("<solutionPath>", "Path to a solution directory (containing .uipx), a .uipx file, or a .uis file.").option("--force", "Skip the existence check and overwrite the Studio Web solution named by the bundled SolutionId directly. Not normally needed an upload overwrites an existing cloud solution on its own. Fails if that solution does not exist. No effect when no SolutionId is bundled (a .uis archive without one): the upload imports as new.").option("--no-snapshot", "Do not record a restorable pre-overwrite version of the cloud solution before replacing its contents. Changes made in Studio Web since the last upload are lost; local files are unaffected. No effect when the upload imports a new solution.").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).examples(SOLUTION_UPLOAD_EXAMPLES).trackedAction(processContext, async (solutionPath, options) => {
5548
5833
  const fs = getFileSystem();
5549
5834
  const [loginError, loginStatus] = await catchError(getLoginStatusAsync({
5550
5835
  ensureTokenValidityMinutes: options.loginValidity
@@ -5604,6 +5889,16 @@ var registerUploadCommand = (program) => {
5604
5889
  if (cleanupError) {
5605
5890
  logger.debug(`Failed to clean up temp dir: ${cleanupError.message}`);
5606
5891
  }
5892
+ if (bundleError instanceof StudioWebIncompatibleProjectError) {
5893
+ OutputFormatter.error({
5894
+ Result: RESULTS.Failure,
5895
+ ErrorCode: "invalid_argument",
5896
+ Message: bundleError.message,
5897
+ Instructions: "Convert the listed project(s) to a project.uiproj manifest before uploading: scaffold the accepted layout with 'uip api-workflow init <name>' inside the solution and move Workflow.json (plus entry-points.json and bindings_v2.json) into it, then remove the project.json-based entry from the .uipx. Note that 'uip solution pack' and 'uip solution publish' accept project.json — only Studio Web upload requires project.uiproj."
5898
+ });
5899
+ processContext.exit(1);
5900
+ return;
5901
+ }
5607
5902
  outputError(bundleError.message);
5608
5903
  return;
5609
5904
  }
@@ -5622,12 +5917,22 @@ var registerUploadCommand = (program) => {
5622
5917
  outputError(`Failed to read file: ${uisPath}${readError ? ` — ${readError.message}` : ""}`);
5623
5918
  return;
5624
5919
  }
5920
+ const uisFileName = fs.path.basename(uisPath);
5921
+ if (!solutionId) {
5922
+ solutionId = readBundledSolutionId(fileBuffer);
5923
+ if (solutionId) {
5924
+ logger.info(`Read SolutionId ${solutionId} from ${uisFileName}`);
5925
+ }
5926
+ }
5625
5927
  const studioWebConfig = {
5626
5928
  baseUrl,
5627
5929
  authToken: accessToken,
5628
5930
  tenantId
5629
5931
  };
5630
- if (!options.force && solutionId) {
5932
+ let overwriteExisting = false;
5933
+ if (solutionId && options.force) {
5934
+ overwriteExisting = true;
5935
+ } else if (solutionId) {
5631
5936
  const [probeError, exists] = await catchError(solutionExistsOnStudioWeb(studioWebConfig, organizationName, solutionId));
5632
5937
  if (probeError) {
5633
5938
  const errorMessage = await extractErrorMessage(probeError);
@@ -5639,18 +5944,23 @@ var registerUploadCommand = (program) => {
5639
5944
  processContext.exit(1);
5640
5945
  return;
5641
5946
  }
5642
- if (exists) {
5947
+ overwriteExisting = exists;
5948
+ }
5949
+ const createSnapshot = overwriteExisting && options.snapshot !== false;
5950
+ logger.info(overwriteExisting ? `Overwriting existing Studio Web solution ${solutionId}...` : "Importing as a new Studio Web solution...");
5951
+ const [uploadError, result] = await catchError(overwriteExisting && solutionId ? overwriteSolution(studioWebConfig, organizationName, fileBuffer, uisFileName, solutionId, createSnapshot) : importSolution(studioWebConfig, organizationName, fileBuffer, uisFileName));
5952
+ if (uploadError) {
5953
+ if (overwriteExisting && isSolutionNotFoundError(uploadError)) {
5643
5954
  OutputFormatter.error({
5644
5955
  Result: RESULTS.Failure,
5645
- Message: `Studio Web already has a solution with SolutionId ${solutionId}. Refusing to overwrite without --force.`,
5646
- Instructions: "Re-run with --force to replace the cloud solution in place (this destroys its Studio Web version history). To upload as an unrelated new solution instead, scaffold a fresh solution with 'uip solution init' or remove the SolutionId from the local .uipx before uploading."
5956
+ ErrorCode: "not_found",
5957
+ Message: `Studio Web has no solution with SolutionId ${solutionId}, so there is nothing to overwrite. It may have been deleted, or it may belong to a tenant or user you no longer have access to.`,
5958
+ Instructions: `The SolutionId in ${uipxPath ?? uisPath} is stale. Re-run the upload${options.force ? " without --force" : ""} — the existence check will see the id is gone and import as a new Studio Web solution${uipxPath ? ", updating the local .uipx with the new id" : ""}. Nothing was uploaded.`,
5959
+ Retry: "RetryWillNotFix"
5647
5960
  });
5648
5961
  processContext.exit(1);
5649
5962
  return;
5650
5963
  }
5651
- }
5652
- const [uploadError, result] = await catchError(options.force ? uploadOrOverwriteSolution(studioWebConfig, organizationName, fileBuffer, solutionId) : importSolution(studioWebConfig, organizationName, fileBuffer, fs.path.basename(uisPath)));
5653
- if (uploadError) {
5654
5964
  const errorMessage = await extractErrorMessage(uploadError);
5655
5965
  OutputFormatter.error({
5656
5966
  Result: RESULTS.Failure,
@@ -5660,20 +5970,25 @@ var registerUploadCommand = (program) => {
5660
5970
  processContext.exit(1);
5661
5971
  return;
5662
5972
  }
5663
- const returnedSolutionId = result.id ?? result.solutionId ?? solutionId;
5664
- if (returnedSolutionId && uipxPath) {
5665
- await updateUipxSolutionId(fs, uipxPath, returnedSolutionId);
5666
- }
5973
+ const { returnedSolutionId, rewroteLocalSolutionId } = await updateLocalSolutionId(fs, result, solutionId, uipxPath);
5667
5974
  const projectId = result.projects?.[0]?.id;
5668
- const designerSolutionId = returnedSolutionId ?? solutionId;
5669
- const designerUrl = projectId && designerSolutionId ? `${baseUrl}/${organizationName}/studio_/designer/${projectId}?solutionId=${designerSolutionId}` : undefined;
5975
+ const designerUrl = projectId && returnedSolutionId ? `${baseUrl}/${encodeURIComponent(organizationName)}/studio_/designer/${encodeURIComponent(projectId)}?solutionId=${encodeURIComponent(returnedSolutionId)}` : undefined;
5670
5976
  OutputFormatter.success({
5671
5977
  Result: RESULTS.Success,
5672
5978
  Code: "SolutionUpload",
5673
5979
  Data: {
5674
5980
  Status: "Uploaded successfully",
5981
+ Action: overwriteExisting ? "Overwritten" : "Imported",
5675
5982
  SolutionId: returnedSolutionId,
5676
5983
  DesignerUrl: designerUrl,
5984
+ ...createSnapshot ? { SnapshotCreated: true } : {},
5985
+ ...rewroteLocalSolutionId ? {
5986
+ LocalSolutionIdUpdated: {
5987
+ Path: uipxPath,
5988
+ From: solutionId,
5989
+ To: returnedSolutionId
5990
+ }
5991
+ } : {},
5677
5992
  Response: result
5678
5993
  }
5679
5994
  });
@@ -5696,6 +6011,19 @@ var registerUploadCommand = (program) => {
5696
6011
  }
5697
6012
  });
5698
6013
  };
6014
+ async function updateLocalSolutionId(fs, result, solutionId, uipxPath) {
6015
+ const returnedSolutionId = result.id ?? result.solutionId ?? solutionId;
6016
+ const shouldUpdateUipx = uipxPath !== undefined && returnedSolutionId !== undefined && returnedSolutionId !== solutionId;
6017
+ const rewroteLocalSolutionId = shouldUpdateUipx && solutionId !== undefined;
6018
+ if (shouldUpdateUipx && returnedSolutionId && uipxPath) {
6019
+ await updateUipxSolutionId(fs, uipxPath, returnedSolutionId);
6020
+ logger.info(solutionId ? `Studio Web assigned SolutionId ${returnedSolutionId} — the local .uipx now points at it (was ${solutionId}).` : `Studio Web assigned SolutionId ${returnedSolutionId} — the local .uipx now points at it.`);
6021
+ }
6022
+ return { returnedSolutionId, rewroteLocalSolutionId };
6023
+ }
6024
+ function isSolutionNotFoundError(error) {
6025
+ return error instanceof Error && "status" in error && error.status === 404;
6026
+ }
5699
6027
  function outputError(message) {
5700
6028
  OutputFormatter.error({
5701
6029
  Result: RESULTS.Failure,
@@ -5727,12 +6055,12 @@ var registerCommands = async (program) => {
5727
6055
  registerResourceCommand(program);
5728
6056
  registerDeleteCommand(program);
5729
6057
  if (isPreviewBuild()) {
5730
- const { registerListCommand } = await import("./list-skbehtq2.js");
6058
+ const { registerListCommand } = await import("./list-3mrc9s3f.js");
5731
6059
  registerListCommand(program);
5732
6060
  }
5733
- previewOnly(() => registerFeedsCommand(program));
6061
+ registerFeedsCommand(program);
5734
6062
  };
5735
6063
 
5736
6064
  export { metadata, registerCommands };
5737
6065
 
5738
- //# debugId=E1FF366A75A1C81B64756E2164756E21
6066
+ //# debugId=58375CF8CAC8E11064756E2164756E21