@uipath/solution-tool 1.203.0-preview.205 → 1.204.0-preview.206

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 (76) hide show
  1. package/README.md +2 -2
  2. package/dist/THIRD-PARTY-NOTICES.md +42 -4
  3. package/dist/deploy-delete-service-y9dwehmn.js +22 -0
  4. package/dist/deploy-status-service-nmhnnjmy.js +256 -0
  5. package/dist/deploy.d.ts +11 -4
  6. package/dist/deploy.js +19 -7
  7. package/dist/deployment-search-eb337bjh.js +49 -0
  8. package/dist/{embedded-file-reader-xxqe8vs8.js → embedded-file-reader-rf2g1kfj.js} +2 -2
  9. package/dist/feed-resolver-dmtnswcc.js +27 -0
  10. package/dist/feed-scope-failure-e2yaqfg2.js +18 -0
  11. package/dist/feed-session-sxga31vn.js +17 -0
  12. package/dist/index.js +27 -18
  13. package/dist/init.js +10 -7
  14. package/dist/{list-pytnrana.js → list-rbjqgz6s.js} +9 -6
  15. package/dist/pack.js +14 -11
  16. package/dist/{packager-tool-vas0xg5h.js → packager-tool-2cntqvdv.js} +5 -3
  17. package/dist/{packager-tool-1haahq17.js → packager-tool-3enm2h28.js} +5 -5
  18. package/dist/{packager-tool-2syrt51a.js → packager-tool-3fv6zc86.js} +1062 -825
  19. package/dist/packager-tool-3fyq6wjs.js +5889 -0
  20. package/dist/{packager-tool-k4mskzww.js → packager-tool-5dzjsw5q.js} +3 -3
  21. package/dist/{packager-tool-0f0wt9vh.js → packager-tool-5fjp60dk.js} +3071 -2794
  22. package/dist/{packager-tool-1qnyt7sm.js → packager-tool-752zvh64.js} +756 -359
  23. package/dist/{packager-tool-bkwnetqn.js → packager-tool-83rzavkx.js} +383 -88
  24. package/dist/{packager-tool-7znqtw3f.js → packager-tool-9vvdgbay.js} +8 -6
  25. package/dist/packager-tool-b8pppqbb.js +172 -0
  26. package/dist/packager-tool-bqre28m1.js +43 -0
  27. package/dist/packager-tool-dh0m3ebf.js +173 -0
  28. package/dist/packager-tool-f5gq77m5.js +28 -0
  29. package/dist/{packager-tool-nzghfa48.js → packager-tool-fv7xyzg2.js} +550 -2
  30. package/dist/{packager-tool-3jrq7smt.js → packager-tool-gb35b4qs.js} +206 -289
  31. package/dist/{packager-tool-7rarcemd.js → packager-tool-he7ak44z.js} +16 -74
  32. package/dist/packager-tool-myn57mc3.js +322 -0
  33. package/dist/{packager-tool-jmpgr1an.js → packager-tool-n1nzj2b9.js} +1008 -647
  34. package/dist/packager-tool-n6yb6zp9.js +113 -0
  35. package/dist/{packager-tool-wkj58797.js → packager-tool-qgaqqq41.js} +320 -151
  36. package/dist/{packager-tool-resolver-fmjr60x8.js → packager-tool-resolver-2cbxyq6k.js} +6 -5
  37. package/dist/{packager-tool-2kvztdcy.js → packager-tool-t12hj6pd.js} +1022 -1342
  38. package/dist/{packager-tool-znakt6yw.js → packager-tool-vh4ng74p.js} +8 -6
  39. package/dist/packager-tool-vmsxq1pc.js +104 -0
  40. package/dist/packager-tool-w6jr4638.js +166 -0
  41. package/dist/packager-tool-zf3929se.js +237 -0
  42. package/dist/packager-tool-zns6n8zp.js +837 -0
  43. package/dist/packager-tool-zw3036b8.js +37 -0
  44. package/dist/packager-tool.js +2 -2
  45. package/dist/prepare-solution-resources-p3gzdecy.js +25 -0
  46. package/dist/{project-contributions-cvttys34.js → project-contributions-txe8v6sm.js} +12 -9
  47. package/dist/publish.js +12 -7
  48. package/dist/resource.js +9 -6
  49. package/dist/services/activation.d.ts +17 -2
  50. package/dist/services/deploy-activate-service.d.ts +10 -0
  51. package/dist/services/deploy-delete-service.d.ts +82 -0
  52. package/dist/services/deploy-list-service.d.ts +15 -45
  53. package/dist/services/deploy-run-service.d.ts +36 -23
  54. package/dist/services/deploy-upgrade-service.d.ts +198 -39
  55. package/dist/services/deployment-feed-locator.d.ts +50 -12
  56. package/dist/services/deployment-search-client.d.ts +55 -0
  57. package/dist/services/deployment-search.d.ts +87 -34
  58. package/dist/services/deployment-state.d.ts +119 -0
  59. package/dist/services/feed-resolver.d.ts +31 -0
  60. package/dist/services/feed-session.d.ts +70 -0
  61. package/dist/services/folder-helper.d.ts +4 -27
  62. package/dist/services/http-status.d.ts +9 -0
  63. package/dist/services/incomplete-feed-sweep.d.ts +77 -0
  64. package/dist/services/legacy-deployment-search-v3.d.ts +23 -0
  65. package/dist/services/packages-list-service.d.ts +5 -0
  66. package/dist/services/pipeline-deploy-poll.d.ts +69 -0
  67. package/dist/services/publish-service.d.ts +14 -16
  68. package/dist/services/search-failure.d.ts +27 -0
  69. package/dist/templates/AGENTS.md +60 -20
  70. package/dist/tool.js +27 -18
  71. package/dist/utils/deployment-errors.d.ts +57 -0
  72. package/package.json +2 -2
  73. package/dist/packager-tool-364pcg21.js +0 -51960
  74. package/dist/packager-tool-jz2wbfjz.js +0 -2275
  75. package/dist/prepare-solution-resources-wds3r8bq.js +0 -22
  76. package/dist/services/personal-workspace-deploy.d.ts +0 -49
@@ -1,37 +1,81 @@
1
1
  import {
2
- Configuration,
3
- PipelinesApi,
4
- feedResolutionFailureInstructions,
2
+ appendDeploymentStateInstructions,
3
+ deploymentNextSteps,
4
+ explainRefusal,
5
+ needsPendingResources,
6
+ readPendingResourcesAsync
7
+ } from "./packager-tool-w6jr4638.js";
8
+ import {
9
+ INCOMPLETE_SWEEP_ADVICE,
10
+ findDeploymentAcrossFeeds,
11
+ isNameOrFeedRefusal,
12
+ reportIncompleteSweepAsync,
13
+ retryAdvice
14
+ } from "./packager-tool-b8pppqbb.js";
15
+ import {
16
+ DeploymentSearchNeedsUserSessionError,
17
+ MAX_LISTED_DEPLOYMENTS,
18
+ blocksRedeploy,
19
+ deploymentToTerminalPipelineResult,
20
+ findDeploymentBestEffort,
21
+ findDeploymentByName,
22
+ findDeploymentsByName,
23
+ findDeploymentsByPackageName,
24
+ formatExistingDeployments,
25
+ interpretFailedDeployPoll,
26
+ isAuthRefusal,
27
+ isCompletedUninstall,
28
+ isPipelineDeploymentNotFoundError,
29
+ mapOperationStatusToPipelineStatus,
30
+ newestDeploymentRow,
31
+ resolveInstalledFolder,
32
+ searchDeploymentsPage
33
+ } from "./packager-tool-myn57mc3.js";
34
+ import {
35
+ Configuration as Configuration2,
36
+ DeploymentOperation,
37
+ DeploymentStatus,
38
+ PipelinesApi
39
+ } from "./packager-tool-qgaqqq41.js";
40
+ import {
41
+ describeFeedResolutionFailureAsync,
5
42
  feedScopeInitOverride,
43
+ feedSelectorFlag,
6
44
  folderKeyInitOverride,
7
- listSelectablePublishLocations,
8
45
  resolveFeedScope,
9
46
  resolvePersonalWorkspace
10
- } from "./packager-tool-wkj58797.js";
47
+ } from "./packager-tool-dh0m3ebf.js";
11
48
  import {
12
- Configuration as Configuration2,
13
- DeploymentOperationStatus,
49
+ Configuration,
14
50
  DeploymentsApi,
15
51
  SearchApi,
16
52
  SolutionDeploymentAction,
17
- deploymentsAutoInstall,
18
- searchPackageVersions
19
- } from "./packager-tool-2kvztdcy.js";
53
+ WorkflowAction
54
+ } from "./packager-tool-t12hj6pd.js";
55
+ import {
56
+ classifyFeedSession,
57
+ feedReadRefusalForApplication,
58
+ feedSessionRefusal
59
+ } from "./packager-tool-bqre28m1.js";
60
+ import {
61
+ getSolutionAuthContext
62
+ } from "./packager-tool-f5gq77m5.js";
20
63
  import {
21
- getSolutionAuthContext,
22
64
  resolveFolder
23
- } from "./packager-tool-2syrt51a.js";
65
+ } from "./packager-tool-3fv6zc86.js";
24
66
  import {
25
67
  DEFAULT_PAGE_SIZE,
26
68
  PollOutcome,
27
69
  catchError,
28
70
  extractErrorDetails,
29
- getFileSystem,
30
71
  logger,
31
72
  mapPollFailure,
32
73
  pollUntil,
33
74
  processContext
34
- } from "./packager-tool-0f0wt9vh.js";
75
+ } from "./packager-tool-5fjp60dk.js";
76
+ import {
77
+ getFileSystem
78
+ } from "./packager-tool-zf3929se.js";
35
79
  import {
36
80
  __commonJS,
37
81
  __require
@@ -7087,6 +7131,10 @@ function formatDetailedValidationError(error) {
7087
7131
  const prefix = resource ? `${resource}: ` : "";
7088
7132
  return `${formatErrorCode(error.errorCode)}${prefix}${error.errorText}`;
7089
7133
  }
7134
+ function formatDetailedConflictError(error) {
7135
+ const actions = error.conflictFixingActions?.length ? ` (fix: ${error.conflictFixingActions.join(", ")})` : "";
7136
+ return `${formatDetailedValidationError(error)}${actions}`;
7137
+ }
7090
7138
  function formatDetailedScheduleError(error) {
7091
7139
  return `${formatErrorCode(error.errorCode)}${error.errorText}`;
7092
7140
  }
@@ -7123,13 +7171,24 @@ function formatDeploymentIdentifiers(pipelineDeploymentId, status) {
7123
7171
  return `Operation: ${identifiers.join(", ")}`;
7124
7172
  }
7125
7173
  function formatDeploymentFailureDetails(pipelineDeploymentId, status) {
7174
+ return [
7175
+ ...formatPipelineErrors(status),
7176
+ formatDeploymentIdentifiers(pipelineDeploymentId, status)
7177
+ ].join(`
7178
+ `);
7179
+ }
7180
+ function formatPipelineErrors(status) {
7126
7181
  const errorParts = [];
7127
7182
  const validationErrors = status.validationResult?.validationErrors;
7128
7183
  if (validationErrors?.length) {
7129
7184
  errorParts.push(`Validation: ${validationErrors.map(formatDetailedValidationError).join("; ")}`);
7130
7185
  }
7186
+ const conflictErrors = status.validationResult?.conflictErrors;
7187
+ if (conflictErrors?.length) {
7188
+ errorParts.push(`Conflicts: ${conflictErrors.map(formatDetailedConflictError).join("; ")}`);
7189
+ }
7131
7190
  if (status.conflictFixingErrors?.length) {
7132
- errorParts.push(`Conflicts: ${status.conflictFixingErrors.map(formatDetailedValidationError).join("; ")}`);
7191
+ errorParts.push(`Conflict fixing: ${status.conflictFixingErrors.map(formatDetailedValidationError).join("; ")}`);
7133
7192
  }
7134
7193
  if (status.deploymentScheduleErrors?.length) {
7135
7194
  errorParts.push(`Schedule: ${status.deploymentScheduleErrors.map(formatDetailedScheduleError).join("; ")}`);
@@ -7142,215 +7201,26 @@ function formatDeploymentFailureDetails(pipelineDeploymentId, status) {
7142
7201
  if (serviceErrors?.length) {
7143
7202
  errorParts.push(`Services: ${serviceErrors.join("; ")}`);
7144
7203
  }
7145
- errorParts.push(formatDeploymentIdentifiers(pipelineDeploymentId, status));
7146
- return errorParts.join(`
7147
- `);
7148
- }
7149
-
7150
- // src/services/deployment-search.ts
7151
- async function findDeploymentByName(auth, deploymentName, scope) {
7152
- const api = new SearchApi(new Configuration2({
7153
- basePath: auth.basePath,
7154
- accessToken: auth.accessToken
7155
- }));
7156
- const take = 50;
7157
- for (let skip = 0;; ) {
7158
- const result = await api.searchSearchDeployments22({
7159
- deploymentsSearchRequest2: {
7160
- searchTerm: deploymentName,
7161
- take,
7162
- skip,
7163
- operationStatuses: [],
7164
- activationStatuses: []
7165
- }
7166
- }, scope);
7167
- const values = result.values ?? [];
7168
- const match = values.find((deployment) => deployment.name === deploymentName);
7169
- if (match) {
7170
- return match;
7171
- }
7172
- if (values.length < take) {
7173
- return;
7174
- }
7175
- skip += values.length;
7176
- }
7177
- }
7178
- async function findDeploymentsByPackageName(auth, packageName, options = {}) {
7179
- const api = new SearchApi(new Configuration2({
7180
- basePath: auth.basePath,
7181
- accessToken: auth.accessToken
7182
- }));
7183
- const take = 50;
7184
- const maxPages = 20;
7185
- const matches = [];
7186
- for (let page = 0, skip = 0;page < maxPages; page++) {
7187
- const result = await api.searchSearchDeployments22({
7188
- deploymentsSearchRequest2: {
7189
- searchTerm: packageName,
7190
- packageName,
7191
- take,
7192
- skip,
7193
- operationStatuses: [],
7194
- activationStatuses: []
7195
- }
7196
- }, options.scope);
7197
- const values = result.values ?? [];
7198
- for (const deployment of values) {
7199
- if (deployment.packageName === packageName && (options.folderPath === undefined || deployment.folderPath === options.folderPath)) {
7200
- matches.push(deployment);
7201
- }
7202
- }
7203
- if (values.length < take) {
7204
- break;
7205
- }
7206
- skip += values.length;
7207
- }
7208
- return matches;
7209
- }
7210
- function blocksRedeploy(deployment) {
7211
- if (deployment.operationStatus === "InProgress") {
7212
- return true;
7213
- }
7214
- if (deployment.operationStatus === "Successful") {
7215
- return deployment.operation !== "Uninstall";
7216
- }
7217
- if (deployment.operationStatus === "Draft" || deployment.operationStatus === "Failed" || deployment.operationStatus === "FailedRollback") {
7218
- return deployment.operation !== undefined && deployment.operation !== null && deployment.operation !== "Install";
7219
- }
7220
- return false;
7204
+ return errorParts;
7221
7205
  }
7222
- var MAX_LISTED_DEPLOYMENTS = 5;
7223
- function formatExistingDeployments(deployments, folderByKey) {
7224
- const described = deployments.slice(0, MAX_LISTED_DEPLOYMENTS).map((deployment) => {
7225
- const resolved = deployment.installedRootFolderKey ? folderByKey?.get(deployment.installedRootFolderKey) : undefined;
7226
- const folder = resolved ?? deployment.folderPath ?? "an unknown folder";
7227
- const current = deployment.currentPackageVersion ?? deployment.packageVersion ?? "an unknown version";
7228
- const target = deployment.targetPackageVersion ?? deployment.newPackageVersionAvailable;
7229
- const version = target && target !== current ? `${current} → ${target}` : current;
7230
- const state = [
7231
- deployment.operation,
7232
- deployment.operationStatus,
7233
- deployment.activationStatus
7234
- ].filter((part) => part !== undefined && part !== null).join("/");
7235
- return `'${deployment.name}' in ${folder} (version ${version}${state ? `, ${state}` : ""})`;
7236
- }).join("; ");
7237
- const remaining = deployments.length - MAX_LISTED_DEPLOYMENTS;
7238
- return remaining > 0 ? `${described}; and ${remaining} more (see: uip solution deploy list)` : described;
7206
+ function hasServerErrorCode(details, code) {
7207
+ return new RegExp(`"errorCode"\\s*:\\s*"${code}"`).test(details);
7239
7208
  }
7240
- async function findDeploymentBestEffort(auth, deploymentName, scope) {
7241
- const [lookupError, deployment] = await catchError(findDeploymentByName(auth, deploymentName, scope));
7242
- if (lookupError) {
7243
- return;
7244
- }
7245
- return deployment;
7209
+ function isBareResponse(details) {
7210
+ return !/^\s*[{[]/.test(details);
7246
7211
  }
7247
- function appendDraftDeploymentInstructions(instructions, deployment) {
7248
- if (deployment?.operationStatus !== "Draft") {
7249
- return instructions;
7250
- }
7251
- return `${instructions}
7252
- Deployment '${deployment.name}' exists but is still in Draft state (OperationStatus: ${deployment.operationStatus}, ActivationStatus: ${deployment.activationStatus}). Draft deployments are created by deploy runs that did not complete successfully and cannot be activated. Fix the deploy run validation errors, rerun 'uip solution deploy run', then activate after 'uip solution deploy list' shows the deployment is no longer Draft.`;
7212
+ var DRAFT_VERSION_MISMATCH_CODE = "DraftDeploymentHasDifferentPackageVersion";
7213
+ function leavesPendingDraft(status) {
7214
+ return status === "ValidationFailed" || status === "ConflictFixingError" || status === "DeploymentScheduleError";
7253
7215
  }
7254
- async function isPipelineDeploymentNotFoundError(error) {
7255
- if (!error || typeof error !== "object") {
7256
- return false;
7257
- }
7258
- const response = error.response;
7259
- if (!response || typeof response !== "object") {
7260
- return false;
7261
- }
7262
- const status = response.status;
7263
- if (status !== 404) {
7264
- return false;
7265
- }
7266
- const [bodyError, body] = await catchError(response.clone().text());
7267
- if (bodyError || !body) {
7268
- return false;
7269
- }
7270
- return body.includes("PipelineDeploymentNotFound");
7216
+ function installLandedDespiteStatus(status) {
7217
+ const record = status.deploymentResult?.status;
7218
+ return status.status !== "DeploymentSucceeded" && (record === "SuccessfulInstall" || record === "SuccessfulActivate");
7271
7219
  }
7272
- function interpretFailedDeployPoll(deployment) {
7273
- if (!deployment) {
7274
- return;
7275
- }
7276
- if (deployment.operationStatus === "Failed" || deployment.operationStatus === "FailedRollback") {
7277
- return {
7278
- kind: "DeployFailed",
7279
- operationStatus: deployment.operationStatus
7280
- };
7281
- }
7282
- if (deployment.operationStatus !== "Successful") {
7283
- return;
7284
- }
7285
- switch (deployment.activationStatus) {
7286
- case "Active":
7287
- return { kind: "Active", instanceId: deployment.instanceId };
7288
- case "NeedsSetupToActivate":
7289
- return { kind: "NeedsSetupToActivate" };
7290
- case "ReadyToActivate":
7291
- case "Inactive":
7292
- return { kind: "ReadyToActivate" };
7293
- case "ActivationFailed":
7294
- return { kind: "ActivationFailed" };
7295
- default:
7296
- return;
7297
- }
7298
- }
7299
- function isSettledDeployOperationStatus(operationStatus) {
7300
- return operationStatus === "Successful" || operationStatus === "Failed" || operationStatus === "FailedRollback";
7301
- }
7302
- function mapOperationStatusToPipelineStatus(operationStatus) {
7303
- switch (operationStatus) {
7304
- case "Successful":
7305
- return "DeploymentSucceeded";
7306
- case "Failed":
7307
- case "FailedRollback":
7308
- case "Draft":
7309
- return "DeploymentFailed";
7310
- default:
7311
- return;
7312
- }
7313
- }
7314
- function deploymentToTerminalPipelineResult(deployment) {
7315
- const status = mapOperationStatusToPipelineStatus(deployment.operationStatus);
7316
- if (!status) {
7317
- return;
7318
- }
7319
- return {
7320
- status,
7321
- validationResult: { validationErrors: [], conflictErrors: [] },
7322
- deploymentKey: deployment.installDeploymentKey ?? deployment.key,
7323
- configurationKey: deployment.configurationKey,
7324
- instanceId: deployment.instanceId
7325
- };
7326
- }
7327
- async function resolveInstalledFolder(deployment, requested, resolveFolderByKey) {
7328
- const fallback = {
7329
- folderName: requested.folderName,
7330
- folderPath: requested.folderPath,
7331
- requestedMatched: true
7332
- };
7333
- const pathFor = (name, path) => path ?? (requested.parentFolderPath ? `${requested.parentFolderPath}/${name}` : name);
7334
- const folderKey = deployment?.installedRootFolderKey;
7335
- if (folderKey && resolveFolderByKey) {
7336
- const [lookupError, folder] = await catchError(resolveFolderByKey(folderKey));
7337
- if (!lookupError && folder?.name) {
7338
- return {
7339
- folderName: folder.name,
7340
- folderPath: pathFor(folder.name, folder.path ?? deployment?.folderPath ?? undefined),
7341
- requestedMatched: folder.name === requested.folderName
7342
- };
7343
- }
7344
- }
7345
- const resolvedName = deployment?.installedRootFolderName;
7346
- if (resolvedName) {
7347
- return {
7348
- folderName: resolvedName,
7349
- folderPath: pathFor(resolvedName, deployment?.folderPath ?? undefined),
7350
- requestedMatched: resolvedName === requested.folderName
7351
- };
7352
- }
7353
- return fallback;
7220
+ function explainPipelineStatusAfterInstall(status) {
7221
+ const lines = formatPipelineErrors(status);
7222
+ const errors = lines.length > 0 ? ` ${lines.join(" ")}` : "";
7223
+ return `The deploy pipeline ended at ${status.status} although the install landed (the deployment record reports ${status.deploymentResult?.status}): the errors it attached come from the activation validation the pipeline runs after the install, not from the install.${errors}`;
7354
7224
  }
7355
7225
 
7356
7226
  // src/services/activation.ts
@@ -7359,19 +7229,22 @@ var TERMINAL_STATUSES = new Set([
7359
7229
  "FailedActivate"
7360
7230
  ]);
7361
7231
  async function activateDeployment(auth, deploymentName, options) {
7362
- const api = new PipelinesApi(new Configuration({
7232
+ const api = new PipelinesApi(new Configuration2({
7363
7233
  basePath: auth.basePath,
7364
7234
  accessToken: auth.accessToken
7365
7235
  }));
7366
7236
  const [stepsError, steps] = await catchError(api.pipelinesGetPreActivateSteps({ deploymentName }, options.scope));
7367
7237
  if (stepsError) {
7368
- const { message, details } = await extractErrorDetails(stepsError);
7238
+ const { message, details, errorCode, retry, context } = await extractErrorDetails(stepsError);
7369
7239
  const deployment = await findDeploymentBestEffort(auth, deploymentName, options.scope);
7370
7240
  return {
7371
7241
  kind: "RequestFailed",
7372
7242
  phase: "preActivateSteps",
7373
7243
  message,
7374
7244
  details,
7245
+ errorCode,
7246
+ retry,
7247
+ httpStatus: context?.httpStatus,
7375
7248
  deployment
7376
7249
  };
7377
7250
  }
@@ -7383,13 +7256,16 @@ async function activateDeployment(auth, deploymentName, options) {
7383
7256
  }
7384
7257
  const [activateError, activateResult] = await catchError(api.pipelinesActivate({ deploymentName }, options.scope));
7385
7258
  if (activateError) {
7386
- const { message, details } = await extractErrorDetails(activateError);
7259
+ const { message, details, errorCode, retry, context } = await extractErrorDetails(activateError);
7387
7260
  const deployment = await findDeploymentBestEffort(auth, deploymentName, options.scope);
7388
7261
  return {
7389
7262
  kind: "RequestFailed",
7390
7263
  phase: "activate",
7391
7264
  message,
7392
7265
  details,
7266
+ errorCode,
7267
+ retry,
7268
+ httpStatus: context?.httpStatus,
7393
7269
  deployment
7394
7270
  };
7395
7271
  }
@@ -7427,44 +7303,6 @@ async function activateDeployment(auth, deploymentName, options) {
7427
7303
  };
7428
7304
  }
7429
7305
 
7430
- // src/services/deployment-feed-locator.ts
7431
- async function nonTenantFeedKeysAsync(options) {
7432
- const [listError, locations] = await catchError(listSelectablePublishLocations({
7433
- tenant: options.tenant,
7434
- loginValidity: options.loginValidity,
7435
- envFilePath: options.envFilePath
7436
- }));
7437
- if (!listError) {
7438
- const rank = (location) => location.type === "PersonalWorkspace" ? 0 : 1;
7439
- return [...locations].filter((location) => location.type !== "Tenant" && location.key).sort((a, b) => rank(a) - rank(b)).map((location) => location.key);
7440
- }
7441
- const [, personalWorkspace] = await catchError(resolvePersonalWorkspace({
7442
- tenant: options.tenant,
7443
- loginValidity: options.loginValidity,
7444
- envFilePath: options.envFilePath
7445
- }));
7446
- return personalWorkspace?.key ? [personalWorkspace.key] : [];
7447
- }
7448
- async function findDeploymentAcrossFeeds(auth, deploymentName, options) {
7449
- const tenantMatch = await findDeploymentByName(auth, deploymentName);
7450
- if (tenantMatch) {
7451
- return { deployment: tenantMatch };
7452
- }
7453
- const feedKeys = await nonTenantFeedKeysAsync(options);
7454
- for (const folderKey of feedKeys) {
7455
- const scope = folderKeyInitOverride(folderKey);
7456
- const [, deployment] = await catchError(findDeploymentByName(auth, deploymentName, scope));
7457
- if (deployment) {
7458
- return { deployment, scope };
7459
- }
7460
- }
7461
- return;
7462
- }
7463
- async function findDeploymentAcrossFeedsBestEffort(auth, deploymentName, options) {
7464
- const [, location] = await catchError(findDeploymentAcrossFeeds(auth, deploymentName, options));
7465
- return location ?? undefined;
7466
- }
7467
-
7468
7306
  // src/services/deploy-activate-service.ts
7469
7307
  var fail = (reason, message, instructions, exitCode = 1) => ({
7470
7308
  ok: false,
@@ -7474,6 +7312,22 @@ var fail = (reason, message, instructions, exitCode = 1) => ({
7474
7312
  exitCode
7475
7313
  });
7476
7314
  var AUTH_INSTRUCTIONS = "Run 'uip login' to refresh credentials, then retry activation. If the deployment is in a different tenant, switch with 'uip login tenant set <tenant>'.";
7315
+ async function incompleteSweepRefusalAsync(deploymentName, skipped, result) {
7316
+ const details = `Details: ${result.details}`;
7317
+ if (!isNameOrFeedRefusal(result.errorCode, result.httpStatus)) {
7318
+ return {
7319
+ ...fail("request_failed", result.message, `The activation request failed. ${retryAdvice(result.errorCode, result.retry, "Orchestrator")} ${details}`),
7320
+ errorCode: result.errorCode,
7321
+ retry: result.retry
7322
+ };
7323
+ }
7324
+ const report = await reportIncompleteSweepAsync(deploymentName, skipped);
7325
+ return {
7326
+ ...fail("request_failed", report.message, `${INCOMPLETE_SWEEP_ADVICE} Without its feed the activation went to the tenant feed, which refused it. ${report.nextStep} ${details} (feed search: ${report.details})`),
7327
+ errorCode: report.errorCode,
7328
+ retry: report.retry
7329
+ };
7330
+ }
7477
7331
  async function activateDeploymentAsync(deploymentName, options = {}) {
7478
7332
  const [authError, auth] = await catchError(getSolutionAuthContext({
7479
7333
  tenant: options.tenant,
@@ -7483,11 +7337,12 @@ async function activateDeploymentAsync(deploymentName, options = {}) {
7483
7337
  if (authError) {
7484
7338
  return fail("auth_failed", authError.message, AUTH_INSTRUCTIONS);
7485
7339
  }
7486
- const location = await findDeploymentAcrossFeedsBestEffort(auth, deploymentName, {
7340
+ const sweep = await findDeploymentAcrossFeeds(auth, deploymentName, {
7487
7341
  tenant: options.tenant,
7488
7342
  loginValidity: options.loginValidity,
7489
7343
  envFilePath: options.envFilePath
7490
7344
  });
7345
+ const location = sweep.location;
7491
7346
  const result = await activateDeployment(auth, deploymentName, {
7492
7347
  timeoutSeconds: options.timeout ?? 360,
7493
7348
  pollIntervalMs: options.pollInterval ?? 5000,
@@ -7495,8 +7350,20 @@ async function activateDeploymentAsync(deploymentName, options = {}) {
7495
7350
  scope: location?.scope
7496
7351
  });
7497
7352
  if (result.kind === "RequestFailed") {
7498
- const fallback = result.phase === "preActivateSteps" ? `Check deployment name. Deploy first with: uip solution deploy run. Details: ${result.details}` : `Check deployment name matches an existing deployment. Details: ${result.details}`;
7499
- return fail("request_failed", result.message, appendDraftDeploymentInstructions(fallback, result.deployment));
7353
+ const details = `Details: ${result.details}`;
7354
+ const known = result.deployment ?? location?.deployment;
7355
+ if (!known && !sweep.complete) {
7356
+ return incompleteSweepRefusalAsync(deploymentName, sweep.skipped, result);
7357
+ }
7358
+ const pendingResources = known && needsPendingResources(known) ? await readPendingResourcesAsync(new DeploymentsApi(new Configuration({
7359
+ basePath: auth.basePath,
7360
+ accessToken: auth.accessToken
7361
+ })), known.key, location?.scope) : undefined;
7362
+ const feedSelector = feedSelectorFlag(location?.feed);
7363
+ const explanation = known ? explainRefusal(known, pendingResources, feedSelector) : undefined;
7364
+ const instructions = explanation ? [...explanation, details].join(`
7365
+ `) : known ? `Deployment '${known.name}' exists (Operation: ${known.operation}, OperationStatus: ${known.operationStatus}, ActivationStatus: ${known.activationStatus}) but the service refused to activate it. Check it with: uip solution deploy status ${known.key}${feedSelector}. ${details}` : result.phase === "preActivateSteps" ? `Check deployment name. Deploy first with: uip solution deploy run. ${details}` : `Check deployment name matches an existing deployment. ${details}`;
7366
+ return fail("request_failed", result.message, instructions);
7500
7367
  }
7501
7368
  if (result.kind === "NoInstance") {
7502
7369
  return {
@@ -7570,13 +7437,51 @@ async function resolveFolderInfo(options) {
7570
7437
  return { key: resolved.key, path: resolved.path };
7571
7438
  }
7572
7439
 
7440
+ // src/services/search-failure.ts
7441
+ async function describeSearchFailureAsync(error, what, session, { userSessionsOnly }) {
7442
+ const details = await extractErrorDetails(error);
7443
+ if (userSessionsOnly && details.context?.httpStatus === 401 && classifyFeedSession(session) === "application") {
7444
+ return {
7445
+ message: `Failed to list ${what}: Solution Search accepts user sessions only, and this session is an external app.`,
7446
+ instructions: `Run it with a user session ('uip login'). A client-credentials login cannot list solution ${what}.`,
7447
+ errorCode: "permission_denied",
7448
+ retry: "RetryWillNotFix",
7449
+ context: details.context
7450
+ };
7451
+ }
7452
+ return {
7453
+ message: `Failed to list ${what}: ${details.message}`,
7454
+ instructions: searchFailureInstructions(details.errorCode, details.retry),
7455
+ errorCode: details.errorCode,
7456
+ retry: details.retry,
7457
+ context: details.context
7458
+ };
7459
+ }
7460
+ function searchFailureInstructions(errorCode, retry) {
7461
+ if (errorCode === "authentication_required") {
7462
+ return "Run 'uip login' to refresh the session, then run the command again.";
7463
+ }
7464
+ if (errorCode === "permission_denied") {
7465
+ return "Use an account that can read Automation Solutions in this tenant.";
7466
+ }
7467
+ if (retry === "RetryLater") {
7468
+ return "Wait a moment, then run the command again.";
7469
+ }
7470
+ return "Check the options and the session ('uip login status --detailed'), then run the command again.";
7471
+ }
7472
+
7573
7473
  // src/services/deploy-list-service.ts
7574
- var fail2 = (reason, message, instructions, exitCode = 1) => ({
7474
+ var fail2 = (reason, message, instructions, extras = {}) => ({
7575
7475
  ok: false,
7576
7476
  reason,
7577
7477
  message,
7578
7478
  instructions,
7579
- exitCode
7479
+ exitCode: 1,
7480
+ ...extras
7481
+ });
7482
+ var refuseFeedRead = (refusal) => fail2("feed_requires_user_session", refusal.message, refusal.instructions, {
7483
+ errorCode: "permission_denied",
7484
+ retry: "RetryWillNotFix"
7580
7485
  });
7581
7486
  async function listDeploymentsAsync(options = {}) {
7582
7487
  const limit = options.limit ?? DEFAULT_PAGE_SIZE;
@@ -7589,6 +7494,12 @@ async function listDeploymentsAsync(options = {}) {
7589
7494
  if (authError) {
7590
7495
  return fail2("auth_failed", authError.message, authError.message);
7591
7496
  }
7497
+ if (options.personalWorkspace) {
7498
+ const refusal = feedReadRefusalForApplication(auth, "Listing the Personal Workspace's deployments");
7499
+ if (refusal) {
7500
+ return refuseFeedRead(refusal);
7501
+ }
7502
+ }
7592
7503
  const [folderError, folderPathFilter] = await catchError(resolveParentFolder({
7593
7504
  folderPath: options.folderPath,
7594
7505
  folderKey: options.folderKey,
@@ -7607,30 +7518,34 @@ async function listDeploymentsAsync(options = {}) {
7607
7518
  envFilePath: options.envFilePath
7608
7519
  }));
7609
7520
  if (scopeError) {
7610
- return fail2("feed_resolution_failed", scopeError.message, feedResolutionFailureInstructions(options));
7611
- }
7612
- const api = new SearchApi(new Configuration2({
7613
- basePath: auth.basePath,
7614
- accessToken: auth.accessToken
7615
- }));
7616
- const [listError, result] = await catchError(api.searchSearchDeployments22({
7617
- deploymentsSearchRequest2: {
7618
- take: limit,
7619
- skip: offset,
7620
- orderByColumn: options.sortBy ?? "startTime",
7621
- orderByDirection: options.sortOrder ?? "Descending",
7622
- operationStatuses: options.operationStatuses ?? [],
7623
- activationStatuses: options.activationStatuses ?? []
7624
- }
7521
+ const { message, instructions, ...extras } = await describeFeedResolutionFailureAsync(scopeError, options, auth);
7522
+ return fail2("feed_resolution_failed", message, instructions, extras);
7523
+ }
7524
+ if (scope.kind !== "tenant") {
7525
+ const refusal = feedReadRefusalForApplication(auth, `Listing the deployments of the feed '${scope.name ?? scope.folderKey}'`);
7526
+ if (refusal) {
7527
+ return refuseFeedRead(refusal);
7528
+ }
7529
+ }
7530
+ const [listError, result] = await catchError(searchDeploymentsPage(auth, {
7531
+ take: limit,
7532
+ skip: offset,
7533
+ orderByColumn: options.sortBy ?? "startTime",
7534
+ orderByDirection: options.sortOrder ?? "Descending",
7535
+ operationStatuses: options.operationStatuses ?? [],
7536
+ activationStatuses: options.activationStatuses ?? [],
7537
+ ...folderPathFilter ? { targetFolderName: folderPathFilter } : {}
7625
7538
  }, feedScopeInitOverride(scope)));
7626
- if (listError) {
7627
- const { message, details } = await extractErrorDetails(listError);
7628
- return fail2("list_request_failed", message, `Failed to list deployments. Details: ${details}`);
7539
+ if (listError instanceof DeploymentSearchNeedsUserSessionError) {
7540
+ return fail2("list_request_failed", `Failed to list deployments: ${listError.message}`, "Run it with a user session ('uip login'). A client-credentials login cannot list deployments on this server.", { errorCode: "permission_denied", retry: "RetryWillNotFix" });
7629
7541
  }
7630
- let values = result.values ?? [];
7631
- if (folderPathFilter) {
7632
- values = values.filter((d) => d.folderPath === folderPathFilter || d.folderPath?.startsWith(`${folderPathFilter}/`));
7542
+ if (listError) {
7543
+ const { message, instructions, ...extras } = await describeSearchFailureAsync(listError, "deployments", auth, {
7544
+ userSessionsOnly: false
7545
+ });
7546
+ return fail2("list_request_failed", message, instructions, extras);
7633
7547
  }
7548
+ const values = result.values ?? [];
7634
7549
  const deployments = values.map((d) => ({
7635
7550
  key: d.key,
7636
7551
  installDeploymentKey: d.installDeploymentKey,
@@ -7720,7 +7635,7 @@ async function describeValidationFailureAsync(deploymentsApi, deploymentKey, dep
7720
7635
  return messages.length > 0 ? messages.join("; ") : NO_SERVER_REASON;
7721
7636
  }
7722
7637
  async function describeDeploymentFailureAsync(auth, deployment, deploymentAction, scope, signal) {
7723
- const deploymentsApi = new DeploymentsApi(new Configuration2({
7638
+ const deploymentsApi = new DeploymentsApi(new Configuration({
7724
7639
  basePath: auth.basePath,
7725
7640
  accessToken: auth.accessToken
7726
7641
  }));
@@ -7734,79 +7649,96 @@ async function describeDeploymentFailureAsync(auth, deployment, deploymentAction
7734
7649
  return NO_SERVER_REASON;
7735
7650
  }
7736
7651
 
7737
- // src/services/personal-workspace-deploy.ts
7738
- async function deployToFeed(auth, target, params) {
7739
- const config = new Configuration2({
7740
- basePath: auth.basePath,
7741
- accessToken: auth.accessToken
7742
- });
7743
- const searchApi = new SearchApi(config);
7744
- const feedScope = folderKeyInitOverride(target.feedFolderKey);
7745
- const take = 50;
7746
- let pkg;
7747
- for (let skip = 0;; ) {
7748
- const packages = await searchApi.searchSearchPackages({ searchFilter: { searchTerm: params.packageName, take, skip } }, feedScope);
7749
- const values = packages.values ?? [];
7750
- pkg = values.find((p) => p.name === params.packageName);
7751
- if (pkg || values.length < take) {
7752
- break;
7753
- }
7754
- skip += values.length;
7755
- }
7756
- if (!pkg) {
7757
- throw new Error(`Package '${params.packageName}' was not found in the target feed. Publish it there first with: uip solution publish <package> --feed <name-or-key>.`);
7758
- }
7759
- let packageVersionKey = pkg.packageVersionKey;
7760
- if (params.packageVersion && params.packageVersion !== pkg.latestVersion) {
7761
- packageVersionKey = await resolvePackageVersionKey(auth, pkg, params.packageVersion, target.feedFolderKey);
7762
- }
7763
- await deploymentsAutoInstall({ basePath: auth.basePath, accessToken: auth.accessToken }, {
7764
- deploymentName: params.deploymentName,
7765
- packageVersionKey,
7766
- newDeploymentKey: crypto.randomUUID(),
7767
- forceActivate: !params.skipActivate,
7768
- installationFolderKey: target.locationFolderKey,
7769
- pathFullyQualifiedName: target.locationFQN,
7770
- packageAndDeploy: false,
7771
- solutionRootFolderName: params.solutionRootFolderName,
7772
- pauseBeforeRun: false,
7773
- runtimeSetup: { requireServerlessTemplate: true }
7774
- }, target.feedFolderKey);
7652
+ // src/services/pipeline-deploy-poll.ts
7653
+ var PIPELINE_TERMINAL_STATUSES = new Set([
7654
+ "DeploymentSucceeded",
7655
+ "DeploymentFailed",
7656
+ "ValidationFailed",
7657
+ "ConflictFixingError",
7658
+ "DeploymentScheduleError"
7659
+ ]);
7660
+ var NO_ERRORS = {
7661
+ validationResult: { validationErrors: [], conflictErrors: [] },
7662
+ conflictFixingErrors: [],
7663
+ deploymentScheduleErrors: []
7664
+ };
7665
+ function stillInFlight(deploymentKey) {
7775
7666
  return {
7776
- deploymentName: params.deploymentName,
7777
- packageName: params.packageName,
7778
- packageVersion: params.packageVersion || pkg.latestVersion,
7779
- folderFQN: target.locationFQN
7667
+ status: "DeploymentInProgress",
7668
+ deploymentKey: deploymentKey ?? "",
7669
+ configurationKey: "",
7670
+ ...NO_ERRORS
7780
7671
  };
7781
7672
  }
7782
- async function resolvePackageVersionKey(auth, pkg, version, feedFolderKey) {
7783
- const take = 50;
7784
- for (let skip = 0;; ) {
7785
- const page = await searchPackageVersions({ basePath: auth.basePath, accessToken: auth.accessToken }, pkg.key, {
7786
- searchTerm: version,
7787
- take,
7788
- skip,
7789
- excludeInvalidPackages: true
7790
- }, feedFolderKey);
7791
- const match = page.values.find((v) => v.version === version);
7792
- if (match) {
7793
- return match.key;
7794
- }
7795
- if (page.values.length < take) {
7796
- throw new Error(`Version '${version}' of package '${pkg.name}' was not found in the target feed (latest is '${pkg.latestVersion}'). List versions with: uip solution packages list, or publish that version to the feed first.`);
7797
- }
7798
- skip += page.values.length;
7673
+ async function readOutcomeByKeyAsync(api, deploymentKey) {
7674
+ const [error, info] = await catchError(() => api.pipelinesGetDeploymentByKey({ key: deploymentKey }));
7675
+ if (error || !info) {
7676
+ return;
7677
+ }
7678
+ if (info.deploymentKey !== deploymentKey) {
7679
+ return;
7680
+ }
7681
+ if (info.operation === "Uninstall" || info.operationStatus === "Draft") {
7682
+ return stillInFlight(deploymentKey);
7799
7683
  }
7684
+ const status = mapOperationStatusToPipelineStatus(info.operationStatus);
7685
+ if (!status) {
7686
+ return stillInFlight(deploymentKey);
7687
+ }
7688
+ return {
7689
+ status,
7690
+ deploymentKey: info.deploymentKey,
7691
+ configurationKey: "",
7692
+ ...NO_ERRORS,
7693
+ synthesizedOperation: info.operation
7694
+ };
7695
+ }
7696
+ async function pollPipelineDeploymentAsync(api, auth, options) {
7697
+ let observedDeploymentKey;
7698
+ return pollUntil({
7699
+ fn: async () => {
7700
+ try {
7701
+ const status = await api.pipelinesGetPipelineDeploymentStatus({ pipelineDeploymentId: options.pipelineDeploymentId }, options.scope);
7702
+ if (status.deploymentKey) {
7703
+ observedDeploymentKey = status.deploymentKey;
7704
+ }
7705
+ return status;
7706
+ } catch (err) {
7707
+ if (!await isPipelineDeploymentNotFoundError(err)) {
7708
+ throw err;
7709
+ }
7710
+ if (!observedDeploymentKey) {
7711
+ return stillInFlight(undefined);
7712
+ }
7713
+ const byKey = await readOutcomeByKeyAsync(api, observedDeploymentKey);
7714
+ if (byKey) {
7715
+ return byKey;
7716
+ }
7717
+ const deployment = await findDeploymentBestEffort(auth, options.deploymentName, options.scope, { deploymentKey: observedDeploymentKey });
7718
+ if (!deployment || deployment.operationStatus === "Draft") {
7719
+ return stillInFlight(observedDeploymentKey);
7720
+ }
7721
+ const synthesized = deploymentToTerminalPipelineResult(deployment);
7722
+ if (!synthesized) {
7723
+ return stillInFlight(observedDeploymentKey);
7724
+ }
7725
+ return {
7726
+ ...synthesized,
7727
+ synthesizedOperation: deployment.operation
7728
+ };
7729
+ }
7730
+ },
7731
+ until: (result) => PIPELINE_TERMINAL_STATUSES.has(result.status),
7732
+ getStatus: (result) => result.status,
7733
+ label: `deployment ${options.pipelineDeploymentId}`,
7734
+ logPrefix: options.logPrefix,
7735
+ timeoutMs: options.timeoutMs,
7736
+ intervalMs: options.intervalMs,
7737
+ signal: options.signal
7738
+ });
7800
7739
  }
7801
7740
 
7802
7741
  // src/services/deploy-run-service.ts
7803
- var TERMINAL_STATUSES2 = new Set([
7804
- "DeploymentSucceeded",
7805
- "DeploymentFailed",
7806
- "ValidationFailed",
7807
- "ConflictFixingError",
7808
- "DeploymentScheduleError"
7809
- ]);
7810
7742
  var PROCESS_RESOURCE_MAPPING_ERROR = /(?:AgentProcessResource|FlowProcessResource)\s*->\s*ReleaseDto/;
7811
7743
  var PROCESS_KEY_MAPPING_ERROR = /Destination Member:\s*ProcessKey|Invalid argument 'Key'/;
7812
7744
  function buildDeploymentFailureInstructions(instructions, pipelineDeploymentId) {
@@ -7865,7 +7797,13 @@ async function deployRunAsync(options) {
7865
7797
  }
7866
7798
  let feedScope = { kind: "tenant" };
7867
7799
  if (options.personalWorkspace || options.feed !== undefined) {
7868
- const [scopeError, scope] = await catchError(resolveFeedScope({
7800
+ if (options.personalWorkspace) {
7801
+ const refusal = refuseFeedForSession(auth, "Deploying into a Personal Workspace");
7802
+ if (refusal) {
7803
+ return refusal;
7804
+ }
7805
+ }
7806
+ const [scopeError, scope2] = await catchError(resolveFeedScope({
7869
7807
  personalWorkspace: options.personalWorkspace,
7870
7808
  feed: options.feed,
7871
7809
  tenant: options.tenant,
@@ -7875,27 +7813,47 @@ async function deployRunAsync(options) {
7875
7813
  if (scopeError) {
7876
7814
  return fail3(options.feed !== undefined ? "feed_resolution_failed" : "personal_workspace_deploy_failed", scopeError.message, "List the feeds you can deploy from with: uip solution feeds list.");
7877
7815
  }
7878
- feedScope = scope;
7816
+ feedScope = scope2;
7817
+ if (feedScope.kind !== "tenant") {
7818
+ const refusal = refuseFeedForSession(auth, `Deploying into the feed '${feedScope.name ?? feedScope.folderKey}'`);
7819
+ if (refusal) {
7820
+ return refusal;
7821
+ }
7822
+ }
7879
7823
  }
7880
- const blocked = await guardExistingDeployment(auth, options, feedScope);
7824
+ const blocked = adviseForApplication(auth, await guardExistingDeployment(auth, options, feedScope)) ?? adviseForApplication(auth, await guardPendingDraft(auth, options, feedScope));
7881
7825
  if (blocked) {
7882
7826
  return blocked;
7883
7827
  }
7828
+ let parentFolderPath;
7884
7829
  if (feedScope.kind !== "tenant") {
7885
- return deployRunViaFeedAsync(auth, feedScope, options);
7886
- }
7887
- const [folderError, parentFolderPath] = await catchError(resolveParentFolder({
7888
- folderPath: options.parentFolderPath,
7889
- folderKey: options.parentFolderKey,
7890
- personalWorkspace: options.personalWorkspace,
7891
- tenant: options.tenant,
7892
- loginValidity: options.loginValidity,
7893
- envFilePath: options.envFilePath
7894
- }));
7895
- if (folderError) {
7896
- return fail3("folder_resolution_failed", folderError.message, "Provide a valid --parent-folder-path, --parent-folder-key, or --personal-workspace.");
7830
+ const [locationError, location] = await catchError(resolveFeedDeployLocation(feedScope, options));
7831
+ if (locationError) {
7832
+ return fail3("location_outside_feed", locationError.message, "Pass a --parent-folder-path / --parent-folder-key inside the feed folder, or omit it to deploy into the feed folder itself.");
7833
+ }
7834
+ parentFolderPath = location.folderFQN || undefined;
7835
+ } else {
7836
+ const [folderError, resolved] = await catchError(resolveParentFolder({
7837
+ folderPath: options.parentFolderPath,
7838
+ folderKey: options.parentFolderKey,
7839
+ personalWorkspace: options.personalWorkspace,
7840
+ tenant: options.tenant,
7841
+ loginValidity: options.loginValidity,
7842
+ envFilePath: options.envFilePath
7843
+ }));
7844
+ if (folderError) {
7845
+ return fail3("folder_resolution_failed", folderError.message, "Provide a valid --parent-folder-path, --parent-folder-key, or --personal-workspace.");
7846
+ }
7847
+ parentFolderPath = resolved;
7897
7848
  }
7898
- const configuration = new Configuration({
7849
+ const scope = feedScopeInitOverride(feedScope);
7850
+ const feedFields = feedScope.kind === "tenant" ? { feedKind: feedScope.kind } : {
7851
+ feedKind: feedScope.kind,
7852
+ deploymentName: options.deploymentName,
7853
+ packageName: options.packageName,
7854
+ packageVersion: options.packageVersion
7855
+ };
7856
+ const configuration = new Configuration2({
7899
7857
  basePath: auth.basePath,
7900
7858
  accessToken: auth.accessToken
7901
7859
  });
@@ -7924,43 +7882,33 @@ async function deployRunAsync(options) {
7924
7882
  solutionRootFolderName: options.folderName,
7925
7883
  folderFullyQualifiedName: parentFolderPath,
7926
7884
  body: configBody
7927
- }));
7928
- if (installError) {
7929
- const { message, details } = await extractErrorDetails(installError);
7930
- return fail3("install_request_failed", message, `Check package name/version with: uip solution packages list. Details: ${details}`);
7885
+ }, scope));
7886
+ if (installError || !installResult?.pipelineDeploymentId) {
7887
+ if (!installError) {
7888
+ return fail3(installFailureReason(feedScope), "The install request returned no pipeline deployment.", installFailureHint(feedScope, options));
7889
+ }
7890
+ const detail = await extractErrorDetails(installError);
7891
+ if (hasServerErrorCode(detail.details, DRAFT_VERSION_MISMATCH_CODE)) {
7892
+ return fail3("pending_draft", detail.message, `${pendingDraftHint(feedScope, options)} Details: ${detail.details}`, { errorCode: detail.errorCode, retry: detail.retry });
7893
+ }
7894
+ return fail3(installFailureReason(feedScope), detail.message, `${installFailureHint(feedScope, options)} Details: ${detail.details}`, { errorCode: detail.errorCode, retry: detail.retry });
7931
7895
  }
7932
7896
  const pipelineDeploymentId = installResult.pipelineDeploymentId;
7933
- const pollResult = await pollUntil({
7934
- fn: async () => {
7935
- try {
7936
- return await api.pipelinesGetPipelineDeploymentStatus({
7937
- pipelineDeploymentId
7938
- });
7939
- } catch (err) {
7940
- if (!await isPipelineDeploymentNotFoundError(err)) {
7941
- throw err;
7942
- }
7943
- const deployment = await findDeploymentBestEffort(auth, options.deploymentName);
7944
- const synthesized = deployment ? deploymentToTerminalPipelineResult(deployment) : undefined;
7945
- if (!synthesized) {
7946
- throw err;
7947
- }
7948
- return synthesized;
7949
- }
7950
- },
7951
- until: (result) => TERMINAL_STATUSES2.has(result.status),
7952
- getStatus: (result) => result.status,
7953
- label: `deployment ${pipelineDeploymentId}`,
7954
- logPrefix: "deploy",
7897
+ const pollResult = await pollPipelineDeploymentAsync(api, auth, {
7898
+ pipelineDeploymentId,
7899
+ deploymentName: options.deploymentName,
7900
+ scope,
7955
7901
  timeoutMs: (options.timeout ?? 360) * 1000,
7956
7902
  intervalMs: options.pollInterval ?? 5000,
7957
- signal: options.signal
7903
+ signal: options.signal,
7904
+ logPrefix: "deploy"
7958
7905
  });
7959
7906
  const folderPath = parentFolderPath ? `${parentFolderPath}/${options.folderName}` : options.folderName;
7960
7907
  if (pollResult.outcome !== PollOutcome.Completed || !pollResult.data) {
7961
- const recoveryDeployment = await findDeploymentBestEffort(auth, options.deploymentName);
7908
+ const recoveryKey = pollResult.data?.deploymentKey || undefined;
7909
+ const recoveryDeployment = recoveryKey ? await findDeploymentBestEffort(auth, options.deploymentName, scope, { deploymentKey: recoveryKey }) : undefined;
7962
7910
  const recovery = interpretFailedDeployPoll(recoveryDeployment);
7963
- if (recovery?.kind === "Active") {
7911
+ const recoveredSuccess = async () => {
7964
7912
  const resolvedFolder2 = await resolveInstalledFolder(recoveryDeployment, {
7965
7913
  folderName: options.folderName,
7966
7914
  folderPath,
@@ -7968,45 +7916,64 @@ async function deployRunAsync(options) {
7968
7916
  }, makeFolderKeyResolver(options));
7969
7917
  return {
7970
7918
  ok: true,
7919
+ ...feedFields,
7971
7920
  status: "DeploymentSucceeded",
7972
- deploymentKey: recoveryDeployment?.installDeploymentKey ?? recoveryDeployment?.key,
7921
+ deploymentKey: recoveryDeployment?.key,
7973
7922
  pipelineDeploymentId,
7974
- instanceId: recovery.instanceId == null ? undefined : recovery.instanceId,
7923
+ instanceId: recoveryDeployment?.instanceId == null ? undefined : recoveryDeployment.instanceId,
7975
7924
  folderName: resolvedFolder2.folderName,
7976
7925
  folderPath: resolvedFolder2.folderPath,
7977
- ...resolvedFolder2.requestedMatched ? {} : { requestedFolderName: options.folderName },
7926
+ ...resolvedFolder2.requestedMatched ? {} : { requestedFolderName: options.folderName }
7927
+ };
7928
+ };
7929
+ if (recovery?.kind === "Active") {
7930
+ return {
7931
+ ...await recoveredSuccess(),
7978
7932
  activationStatus: "SuccessfulActivate"
7979
7933
  };
7980
7934
  }
7981
- if (recovery?.kind === "NeedsSetupToActivate") {
7982
- return fail3("needs_setup_to_activate", `Deployment '${options.deploymentName}' completed, but activation requires manual configuration.`, `One or more deployed resources need to be configured before activation (commonly Integration Service connections, credentials, or other tenant-specific setup). Open the deployment in Orchestrator, configure the resources flagged "Needs Setup", then activate with: uip solution deploy activate ${options.deploymentName}.`, { pipelineDeploymentId });
7983
- }
7984
- if (recovery?.kind === "ReadyToActivate") {
7985
- return fail3("ready_to_activate", `Deployment '${options.deploymentName}' completed and is ready to activate.`, `Activation did not complete automatically. Activate now with: uip solution deploy activate ${options.deploymentName}.`, { pipelineDeploymentId });
7935
+ if (recoveryDeployment && (recovery?.kind === "NeedsSetupToActivate" || recovery?.kind === "ReadyToActivate")) {
7936
+ return {
7937
+ ...await recoveredSuccess(),
7938
+ ...await describeInstalledNotLiveAsync(auth, recoveryDeployment, scope, feedScope)
7939
+ };
7986
7940
  }
7987
7941
  if (recovery?.kind === "ActivationFailed") {
7988
7942
  return fail3("activation_failed_post_deploy", `Deployment '${options.deploymentName}' completed but activation failed.`, `Inspect activation details in Orchestrator, then retry with: uip solution deploy activate ${options.deploymentName}.`, { pipelineDeploymentId });
7989
7943
  }
7990
7944
  if (recovery?.kind === "DeployFailed") {
7991
- return fail3("deploy_failed", `Deployment '${options.deploymentName}' failed (OperationStatus: ${recovery.operationStatus}).`, `Inspect failure details with: uip solution deploy status ${pipelineDeploymentId}. Fix the underlying issue and retry: uip solution deploy run.`, { pipelineDeploymentId });
7945
+ const reason2 = recoveryDeployment ? await describeDeploymentFailureAsync(auth, recoveryDeployment, SolutionDeploymentAction.Install, scope, options.signal) : NO_SERVER_REASON;
7946
+ return fail3("deploy_failed", `Deployment '${options.deploymentName}' failed (OperationStatus: ${recovery.operationStatus}): ${reason2}`, `${inspectHint(pipelineDeploymentId, feedScope)} Fix the underlying issue and retry: uip solution deploy run.`, { pipelineDeploymentId });
7992
7947
  }
7993
7948
  const { reason, message, exitCode, errorCode, retry } = mapPollFailure(pollResult, "Deployment");
7994
- return fail3(reason, message, `Deployment may still be in progress. Check status with: uip solution deploy status ${pipelineDeploymentId}.`, { exitCode, pipelineDeploymentId, errorCode, retry });
7949
+ return fail3(reason, message, `Deployment may still be in progress. ${followHint(pipelineDeploymentId, feedScope)}`, { exitCode, pipelineDeploymentId, errorCode, retry });
7995
7950
  }
7996
7951
  const finalStatus = pollResult.data;
7997
7952
  if (!finalStatus) {
7998
- return fail3("no_final_status", "Deployment did not return a final status.", `Check status with: uip solution deploy status ${pipelineDeploymentId}.`, { pipelineDeploymentId });
7999
- }
8000
- if (finalStatus.status !== "DeploymentSucceeded") {
8001
- return fail3("deploy_failed", `Deployment failed with status: ${finalStatus.status}`, buildDeploymentFailureInstructions(formatDeploymentFailureDetails(pipelineDeploymentId, finalStatus), pipelineDeploymentId), { pipelineDeploymentId });
8002
- }
8003
- const resolvedFolder = await resolveInstalledFolder(await findDeploymentBestEffort(auth, options.deploymentName), {
7953
+ return fail3("no_final_status", "Deployment did not return a final status.", followHint(pipelineDeploymentId, feedScope), { pipelineDeploymentId });
7954
+ }
7955
+ const landedDespiteStatus = installLandedDespiteStatus(finalStatus);
7956
+ if (finalStatus.status !== "DeploymentSucceeded" && !landedDespiteStatus) {
7957
+ const classification = finalStatus.status === "ValidationFailed" || finalStatus.status === "ConflictFixingError" ? {
7958
+ errorCode: "invalid_argument",
7959
+ retry: "RetryWillNotFix"
7960
+ } : {};
7961
+ const fromPipeline = formatDeploymentFailureDetails(pipelineDeploymentId, finalStatus);
7962
+ const details = finalStatus.synthesizedOperation !== undefined && finalStatus.deploymentKey ? await describeSynthesizedFailureAsync(auth, finalStatus.deploymentKey, scope, options.signal, fromPipeline) : fromPipeline;
7963
+ const draftNote = leavesPendingDraft(finalStatus.status) ? `
7964
+ Nothing was installed. The rejected attempt is kept as a pending draft${finalStatus.deploymentKey ? ` (${finalStatus.deploymentKey})` : ""}: fix the configuration and re-run this command, and the server re-validates that draft with the new file (settings and resource links the file does not name keep the values the draft was rejected with).${finalStatus.deploymentKey ? ` When this was an update of an existing deployment, you can discard the draft and upgrade the live version instead: uip solution deploy upgrade ${finalStatus.deploymentKey} --discard-draft --version ${options.packageVersion}` : ""}` : "";
7965
+ return fail3("deploy_failed", `Deployment failed with status: ${finalStatus.status}`, `${buildDeploymentFailureInstructions(details, pipelineDeploymentId)}${draftNote}`, { pipelineDeploymentId, ...classification });
7966
+ }
7967
+ const resolvedFolder = await resolveInstalledFolder(await findDeploymentBestEffort(auth, options.deploymentName, scope, {
7968
+ deploymentKey: finalStatus.deploymentKey || undefined
7969
+ }), {
8004
7970
  folderName: options.folderName,
8005
7971
  folderPath,
8006
7972
  parentFolderPath
8007
7973
  }, makeFolderKeyResolver(options));
8008
7974
  const baseSuccess = {
8009
7975
  ok: true,
7976
+ ...feedFields,
8010
7977
  status: finalStatus.status,
8011
7978
  deploymentKey: finalStatus.deploymentKey,
8012
7979
  pipelineDeploymentId,
@@ -8015,21 +7982,44 @@ async function deployRunAsync(options) {
8015
7982
  folderPath: resolvedFolder.folderPath,
8016
7983
  ...resolvedFolder.requestedMatched ? {} : { requestedFolderName: options.folderName }
8017
7984
  };
7985
+ if (landedDespiteStatus) {
7986
+ const described = await describeInstalledRunAsync(auth, options.deploymentName, finalStatus, scope, feedScope);
7987
+ return {
7988
+ ...baseSuccess,
7989
+ ...described,
7990
+ nextSteps: [
7991
+ explainPipelineStatusAfterInstall(finalStatus),
7992
+ ...described.nextSteps ?? []
7993
+ ]
7994
+ };
7995
+ }
8018
7996
  if (options.skipActivate) {
8019
7997
  return { ...baseSuccess, activationStatus: "Skipped" };
8020
7998
  }
8021
7999
  const activationResult = await activateDeployment(auth, options.deploymentName, {
8022
8000
  timeoutSeconds: options.timeout ?? 360,
8023
8001
  pollIntervalMs: options.pollInterval ?? 5000,
8024
- signal: options.signal
8002
+ signal: options.signal,
8003
+ scope
8025
8004
  });
8026
8005
  if (activationResult.kind === "RequestFailed") {
8006
+ const refused = activationResult.deployment;
8007
+ if (refused && refused.key === finalStatus.deploymentKey && refused.operationStatus === "Successful" && refused.activationStatus === "NeedsSetupToActivate") {
8008
+ return {
8009
+ ...baseSuccess,
8010
+ ...await describeInstalledNotLiveAsync(auth, refused, scope, feedScope)
8011
+ };
8012
+ }
8027
8013
  const fallback = activationResult.phase === "preActivateSteps" ? `Deployment '${options.deploymentName}' was deployed successfully but pre-activate steps lookup failed. Retry activation with: uip solution deploy activate ${options.deploymentName}. Details: ${activationResult.details}` : `Deployment '${options.deploymentName}' was deployed successfully but activation failed. Retry activation with: uip solution deploy activate ${options.deploymentName}. Details: ${activationResult.details}`;
8028
- return fail3("activation_request_failed", activationResult.message, appendDraftDeploymentInstructions(fallback, activationResult.deployment), { pipelineDeploymentId });
8014
+ const pendingResources = refused && needsPendingResources(refused) ? await readPendingResourcesAsync(new DeploymentsApi(new Configuration({
8015
+ basePath: auth.basePath,
8016
+ accessToken: auth.accessToken
8017
+ })), refused.key, scope) : undefined;
8018
+ return fail3("activation_request_failed", activationResult.message, appendDeploymentStateInstructions(fallback, refused, pendingResources, feedSelectorFlag(feedScope)), { pipelineDeploymentId });
8029
8019
  }
8030
8020
  if (activationResult.kind === "PollFailed") {
8031
8021
  const { message, exitCode, errorCode, retry } = mapPollFailure(activationResult.poll, "Activation");
8032
- return fail3("activation_poll_failed", message, `Deployment '${options.deploymentName}' was deployed successfully but activation polling did not complete. Activation may still be in progress; check with: uip solution deploy list.`, { exitCode, pipelineDeploymentId, errorCode, retry });
8022
+ return fail3("activation_poll_failed", message, `Deployment '${options.deploymentName}' was deployed successfully but activation polling did not complete. Activation may still be in progress; check with: ${listCommand(feedScope)}.`, { exitCode, pipelineDeploymentId, errorCode, retry });
8033
8023
  }
8034
8024
  if (activationResult.kind === "ActivationFailed") {
8035
8025
  return fail3("activation_failed", `Activation failed with status: ${activationResult.status}`, activationResult.errorMessage, { pipelineDeploymentId });
@@ -8039,22 +8029,115 @@ async function deployRunAsync(options) {
8039
8029
  const finalInstanceId = activationInstanceId == null ? baseSuccess.instanceId : activationInstanceId;
8040
8030
  return { ...baseSuccess, activationStatus, instanceId: finalInstanceId };
8041
8031
  }
8042
- var EXISTING_DEPLOYMENT_MARKER = "Deployment exists in a failed or running state";
8043
- function isExistingDeploymentConflict(httpStatus, message, details, parsedErrors) {
8044
- if (httpStatus !== 400) {
8045
- return false;
8032
+ async function describeInstalledRunAsync(auth, deploymentName, finalStatus, scope, feedScope) {
8033
+ const key = finalStatus.deploymentKey || undefined;
8034
+ const row = key ? await findDeploymentBestEffort(auth, deploymentName, scope, {
8035
+ deploymentKey: key
8036
+ }) : undefined;
8037
+ if (row) {
8038
+ return describeInstalledNotLiveAsync(auth, row, scope, feedScope);
8039
+ }
8040
+ const record = finalStatus.deploymentResult;
8041
+ if (record?.status === "SuccessfulActivate") {
8042
+ return { activationStatus: "SuccessfulActivate" };
8043
+ }
8044
+ const actions = record?.actions ?? [];
8045
+ const activationStatus = actions.includes("SetupActivation") ? "NeedsSetupToActivate" : actions.includes("Activate") ? "ReadyToActivate" : "Inactive";
8046
+ return describeInstalledNotLiveAsync(auth, {
8047
+ name: deploymentName,
8048
+ key,
8049
+ operation: record?.operation ?? finalStatus.synthesizedOperation,
8050
+ operationStatus: "Successful",
8051
+ activationStatus,
8052
+ packageVersion: record?.packageVersion,
8053
+ currentPackageVersion: record?.packageVersion
8054
+ }, scope, feedScope);
8055
+ }
8056
+ async function describeInstalledNotLiveAsync(auth, deployment, scope, feedScope) {
8057
+ const pendingResources = needsPendingResources(deployment) && deployment.key ? await readPendingResourcesAsync(new DeploymentsApi(new Configuration({
8058
+ basePath: auth.basePath,
8059
+ accessToken: auth.accessToken
8060
+ })), deployment.key, scope) : undefined;
8061
+ const nextSteps = deploymentNextSteps(deployment, pendingResources, feedSelectorFlag(feedScope));
8062
+ return {
8063
+ activationStatus: deployment.activationStatus ?? "Inactive",
8064
+ ...pendingResources ? { pendingResources } : {},
8065
+ ...nextSteps && nextSteps.length > 0 ? { nextSteps } : {}
8066
+ };
8067
+ }
8068
+ function inspectHint(pipelineDeploymentId, scope) {
8069
+ if (scope.kind === "tenant") {
8070
+ return `Inspect failure details with: uip solution deploy status ${pipelineDeploymentId}.`;
8046
8071
  }
8047
- if (message.includes(EXISTING_DEPLOYMENT_MARKER) || details.includes(EXISTING_DEPLOYMENT_MARKER)) {
8048
- return true;
8072
+ return `Inspect it with: ${listCommand(scope)}.`;
8073
+ }
8074
+ function followHint(pipelineDeploymentId, scope) {
8075
+ if (scope.kind === "tenant") {
8076
+ return `Check status with: uip solution deploy status ${pipelineDeploymentId}.`;
8049
8077
  }
8050
- if (parsedErrors) {
8051
- for (const fieldMessages of Object.values(parsedErrors)) {
8052
- if (fieldMessages.some((m) => m.includes(EXISTING_DEPLOYMENT_MARKER))) {
8053
- return true;
8054
- }
8055
- }
8078
+ return `Check it with: ${listCommand(scope)}.`;
8079
+ }
8080
+ function listCommand(scope) {
8081
+ if (scope.kind === "personal") {
8082
+ return "uip solution deploy list --personal-workspace";
8056
8083
  }
8057
- return false;
8084
+ if (scope.kind === "folder") {
8085
+ return `uip solution deploy list --feed ${scope.folderKey ?? "<name-or-key>"}`;
8086
+ }
8087
+ return "uip solution deploy list";
8088
+ }
8089
+ function adviseForApplication(auth, failure) {
8090
+ if (!failure || classifyFeedSession(auth) !== "application") {
8091
+ return failure;
8092
+ }
8093
+ logger.warn(`${failure.message} Deploying anyway: under an external app (client-credentials) login this check only warns, so a pipeline keeps the outcome it had before the check could run. ${failure.instructions}`);
8094
+ return;
8095
+ }
8096
+ function refuseFeedForSession(session, action) {
8097
+ const verdict = classifyFeedSession(session);
8098
+ if (verdict === "user") {
8099
+ return;
8100
+ }
8101
+ const { message, instructions } = feedSessionRefusal(verdict, action, "deploy the package to the tenant feed");
8102
+ return fail3("feed_requires_user_session", message, instructions, {
8103
+ errorCode: "permission_denied",
8104
+ retry: "RetryWillNotFix"
8105
+ });
8106
+ }
8107
+ async function describeSynthesizedFailureAsync(auth, deploymentKey, scope, signal, fromPipeline) {
8108
+ const deploymentsApi = new DeploymentsApi(new Configuration({
8109
+ basePath: auth.basePath,
8110
+ accessToken: auth.accessToken
8111
+ }));
8112
+ const reason = await describeValidationFailureAsync(deploymentsApi, deploymentKey, SolutionDeploymentAction.Install, scope, signal);
8113
+ return reason === NO_SERVER_REASON ? fromPipeline : `Validation: ${reason}
8114
+ ${fromPipeline}`;
8115
+ }
8116
+ function pendingDraftHint(scope, options) {
8117
+ const cause = `'${options.deploymentName}' has a pending draft for another version: an earlier attempt that never completed, usually one rejected by validation. To retry that draft with a corrected configuration, re-run this command with the draft's --package-version (the message names it).`;
8118
+ if (scope.kind === "folder") {
8119
+ return `${cause} To discard it and upgrade the live deployment to ${options.packageVersion} instead: uip solution deploy upgrade <deployment-key> --discard-draft --version ${options.packageVersion} (Key from ${listCommand(scope)}).`;
8120
+ }
8121
+ const workspace = scope.kind === "personal" ? " --personal-workspace" : "";
8122
+ return `${cause} To discard it and upgrade the live deployment to ${options.packageVersion} instead: uip solution deploy upgrade --name ${options.deploymentName} --package-name ${options.packageName}${workspace} --discard-draft --version ${options.packageVersion} (under an external app, pass the deployment's key in place of --name and --package-name).`;
8123
+ }
8124
+ function installFailureReason(scope) {
8125
+ if (scope.kind === "personal") {
8126
+ return "personal_workspace_deploy_failed";
8127
+ }
8128
+ if (scope.kind === "folder") {
8129
+ return "feed_deploy_failed";
8130
+ }
8131
+ return "install_request_failed";
8132
+ }
8133
+ function installFailureHint(scope, options) {
8134
+ if (scope.kind === "personal") {
8135
+ return "Ensure the package was published to your Personal Workspace first: uip solution publish <package> --personal-workspace.";
8136
+ }
8137
+ if (scope.kind === "folder") {
8138
+ return `Ensure the package was published to the feed first: uip solution publish <package> --feed ${options.feed ?? "<name-or-key>"}.`;
8139
+ }
8140
+ return "Check package name/version with: uip solution packages list.";
8058
8141
  }
8059
8142
  function scopeLabel(scope) {
8060
8143
  if (scope.kind === "personal") {
@@ -8084,7 +8167,7 @@ function buildDeploymentExistsInstructions(options) {
8084
8167
  }
8085
8168
  }
8086
8169
  parts.push("To see every deployment and its state, run: uip solution deploy list.");
8087
- parts.push(scope.kind === "tenant" ? "To create a second, separate deployment anyway, re-run this command with --yes." : "To create a second, separate deployment anyway, re-run with --yes — Orchestrator may still refuse it while the existing deployment is running or in a failed state.");
8170
+ parts.push(scope.kind === "tenant" ? "To create a second, separate deployment anyway, re-run this command with --yes and a different --name: re-using an existing deployment's name updates that deployment in place instead." : "To create a second, separate deployment anyway, re-run with --yes and a different --name (re-using an existing deployment's name updates that deployment in place) — Orchestrator may still refuse it while the existing deployment is running or in a failed state.");
8088
8171
  if (existing.length === 0) {
8089
8172
  parts.unshift(`A deployment of '${packageName}' already exists in ${scopeLabel(scope)}.`);
8090
8173
  } else if (options.includeInventory) {
@@ -8107,6 +8190,31 @@ async function resolveDeploymentFolders(deployments, options) {
8107
8190
  }));
8108
8191
  return resolved;
8109
8192
  }
8193
+ async function guardPendingDraft(auth, options, scope) {
8194
+ const [lookupError, rows] = await catchError(findDeploymentsByName(auth, options.deploymentName, feedScopeInitOverride(scope)));
8195
+ if (lookupError instanceof DeploymentSearchNeedsUserSessionError || lookupError && classifyFeedSession(auth) === "application" && isAuthRefusal(lookupError)) {
8196
+ return;
8197
+ }
8198
+ if (lookupError) {
8199
+ logger.warn(`Skipped the pending-draft check for '${options.deploymentName}': the deployment lookup failed (${lookupError.message}). Deploying anyway — if this deployment has a pending draft for another version, the server may install that draft's version instead of ${options.packageVersion}. Check it with 'uip solution deploy list'.`);
8200
+ return;
8201
+ }
8202
+ const draft = newestDeploymentRow(rows);
8203
+ if (draft?.operationStatus !== "Draft") {
8204
+ return;
8205
+ }
8206
+ const draftVersion = draft.targetPackageVersion ?? draft.packageVersion;
8207
+ if (draft.operation === "ActivationSetup") {
8208
+ return fail3("pending_draft", `Deployment '${options.deploymentName}' has an unfinished Setup activation (${draft.key}): the Orchestrator "Setup activation" wizard was started on version ${draftVersion ?? "<version>"} and not completed.`, `While that draft exists the server would run it in place of this deploy, or refuse a different version. Finish the setup in Orchestrator (Solutions, the deployment, Setup activation); or discard it — in Orchestrator (Delete draft), or with uip solution deploy upgrade ${draft.key} --discard-draft${draft.currentPackageVersion ? ` --version ${draft.currentPackageVersion}` : ""} — then re-run this command.`, { errorCode: "invalid_argument", retry: "RetryWillNotFix" });
8209
+ }
8210
+ if (!draftVersion || draftVersion.toLowerCase() === options.packageVersion.toLowerCase()) {
8211
+ return;
8212
+ }
8213
+ const liveVersion = draft.currentPackageVersion;
8214
+ const discard = draft.operation === "Install" || !liveVersion ? "delete it in Orchestrator (Delete draft)" : `discard it — in Orchestrator (Delete draft), or with uip solution deploy upgrade ${draft.key} --discard-draft --version ${liveVersion}, which also re-applies the live version unchanged —`;
8215
+ return fail3("pending_draft", `Deployment '${options.deploymentName}' has a pending draft (${draft.key}) for version ${draftVersion}: an earlier attempt that never completed, usually one rejected by validation, or one still running.`, `The server reuses that draft for the name, so this request would not deploy ${options.packageVersion}: asking for the live version installs the draft's ${draftVersion} instead, and any other version is refused. To retry the draft with a corrected configuration, re-run with --package-version ${draftVersion}.
8216
+ To deploy ${options.packageVersion} instead, ${discard} then re-run this command.`, { errorCode: "invalid_argument", retry: "RetryWillNotFix" });
8217
+ }
8110
8218
  async function guardExistingDeployment(auth, options, scope) {
8111
8219
  if (options.yes) {
8112
8220
  return;
@@ -8114,8 +8222,12 @@ async function guardExistingDeployment(auth, options, scope) {
8114
8222
  const [lookupError, deployments] = await catchError(findDeploymentsByPackageName(auth, options.packageName, {
8115
8223
  scope: feedScopeInitOverride(scope)
8116
8224
  }));
8225
+ if (lookupError instanceof DeploymentSearchNeedsUserSessionError) {
8226
+ logger.warn(`Skipped the already-deployed check for '${options.packageName}': this server's deployment search reads user sessions only, and this is an application (client-credentials) login. Deploying anyway — if a deployment already exists this creates a second one in its own folder. Check what is already deployed in the Orchestrator Solutions UI, or sign in with 'uip login' and re-run.`);
8227
+ return;
8228
+ }
8117
8229
  if (lookupError) {
8118
- logger.warn(`Skipped the already-deployed check for '${options.packageName}': the deployment lookup failed (${lookupError.message}). Deploying anyway — if a deployment already exists this creates a second one in its own folder. The lookup needs a user session; an application (client-credentials) login cannot read it, and 'uip solution deploy list' reads the same service so it fails the same way. Check what is already deployed in the Orchestrator Solutions UI, or sign in with 'uip login' and re-run.`);
8230
+ logger.warn(`Skipped the already-deployed check for '${options.packageName}': the deployment lookup failed (${lookupError.message}). Deploying anyway — if a deployment already exists this creates a second one in its own folder. 'uip solution deploy list' reads the same search, so it may fail the same way. Check what is already deployed in the Orchestrator Solutions UI, or re-run once the lookup answers.`);
8119
8231
  return;
8120
8232
  }
8121
8233
  const existing = deployments.filter(blocksRedeploy);
@@ -8129,85 +8241,6 @@ async function guardExistingDeployment(auth, options, scope) {
8129
8241
  requestedDeploymentName: options.deploymentName
8130
8242
  }));
8131
8243
  }
8132
- async function deployRunViaFeedAsync(auth, scope, options) {
8133
- const [locationError, location] = await catchError(resolveFeedDeployLocation(scope, options));
8134
- if (locationError) {
8135
- return fail3("location_outside_feed", locationError.message, "Pass a --parent-folder-path / --parent-folder-key inside the feed folder, or omit it to deploy into the feed folder itself.");
8136
- }
8137
- const isPersonal = scope.kind === "personal";
8138
- const [deployError, result] = await catchError(deployToFeed(auth, {
8139
- feedFolderKey: scope.folderKey,
8140
- locationFolderKey: location.folderKey,
8141
- locationFQN: location.folderFQN
8142
- }, {
8143
- packageName: options.packageName,
8144
- packageVersion: options.packageVersion,
8145
- deploymentName: options.deploymentName,
8146
- solutionRootFolderName: options.folderName,
8147
- skipActivate: options.skipActivate
8148
- }));
8149
- if (deployError) {
8150
- const { message, details, context, parsedErrors } = await extractErrorDetails(deployError);
8151
- if (isExistingDeploymentConflict(context?.httpStatus, message, details, parsedErrors)) {
8152
- const [, blockers] = await catchError(findDeploymentsByPackageName(auth, options.packageName, {
8153
- scope: feedScopeInitOverride(scope)
8154
- }));
8155
- return fail3("deployment_exists", message, buildDeploymentExistsInstructions({
8156
- packageName: options.packageName,
8157
- scope,
8158
- existing: blockers ?? [],
8159
- requestedDeploymentName: options.deploymentName,
8160
- includeInventory: true
8161
- }));
8162
- }
8163
- const publishHint = isPersonal ? "uip solution publish <package> --personal-workspace" : `uip solution publish <package> --feed ${options.feed ?? "<name-or-key>"}`;
8164
- return fail3(isPersonal ? "personal_workspace_deploy_failed" : "feed_deploy_failed", message, `Ensure the package was published to the target feed first: ${publishHint}. Details: ${details}`);
8165
- }
8166
- const target = isPersonal ? "your Personal Workspace" : `feed '${scope.name ?? location.folderFQN}'`;
8167
- const started = {
8168
- ok: true,
8169
- status: "DeploymentStarted",
8170
- folderName: options.folderName,
8171
- folderPath: `${result.folderFQN}/${options.folderName}`,
8172
- activationStatus: options.skipActivate ? "Skipped" : "Auto",
8173
- deploymentName: result.deploymentName,
8174
- packageName: result.packageName,
8175
- packageVersion: result.packageVersion,
8176
- feedKind: scope.kind,
8177
- nextSteps: options.skipActivate ? `Deployment created in ${target}. Activate it with: uip solution deploy activate ${result.deploymentName}. Check status with: uip solution deploy list.` : `Deployment created in ${target} and will auto-activate. Check status with: uip solution deploy list.`
8178
- };
8179
- if (!options.wait) {
8180
- return started;
8181
- }
8182
- return awaitFeedDeployAsync(auth, scope, options, started, result.deploymentName);
8183
- }
8184
- async function awaitFeedDeployAsync(auth, scope, options, started, deploymentName) {
8185
- const scopeOverride = feedScopeInitOverride(scope);
8186
- const pollResult = await pollUntil({
8187
- fn: () => findDeploymentByName(auth, deploymentName, scopeOverride),
8188
- until: (deployment2) => isSettledDeployOperationStatus(deployment2?.operationStatus),
8189
- getStatus: (deployment2) => deployment2?.operationStatus ?? "Unknown",
8190
- label: `deployment '${deploymentName}'`,
8191
- logPrefix: "deploy",
8192
- timeoutMs: (options.timeout ?? 360) * 1000,
8193
- intervalMs: options.pollInterval ?? 5000,
8194
- signal: options.signal
8195
- });
8196
- if (pollResult.outcome !== PollOutcome.Completed || !pollResult.data) {
8197
- const { reason, message, exitCode, errorCode, retry } = mapPollFailure(pollResult, "Deployment");
8198
- return fail3(reason, message, appendDraftDeploymentInstructions(`Deployment '${deploymentName}' may still be in progress. Check it with: uip solution deploy list.`, pollResult.data), { exitCode, errorCode, retry });
8199
- }
8200
- const deployment = pollResult.data;
8201
- if (deployment.operationStatus !== "Successful") {
8202
- const reason = await describeDeploymentFailureAsync(auth, deployment, SolutionDeploymentAction.Install, scopeOverride, options.signal);
8203
- return fail3("deploy_failed", `Deployment '${deploymentName}' failed (OperationStatus: ${deployment.operationStatus}): ${reason}`, `Inspect the deployment with: uip solution deploy list, fix the underlying issue, then deploy again.`);
8204
- }
8205
- return {
8206
- ...started,
8207
- status: "DeploymentSucceeded",
8208
- nextSteps: options.skipActivate ? started.nextSteps : undefined
8209
- };
8210
- }
8211
8244
  async function resolveFeedDeployLocation(scope, options) {
8212
8245
  const feedFolder = {
8213
8246
  folderKey: scope.folderKey,
@@ -8234,7 +8267,7 @@ async function resolveFeedDeployLocation(scope, options) {
8234
8267
  }
8235
8268
 
8236
8269
  // src/services/deploy-uninstall-service.ts
8237
- var TERMINAL_STATUSES3 = new Set([
8270
+ var TERMINAL_STATUSES2 = new Set([
8238
8271
  "SuccessfulUninstall",
8239
8272
  "FailedUninstall"
8240
8273
  ]);
@@ -8274,10 +8307,11 @@ function buildDeploymentStateInstructions(deployment, details) {
8274
8307
  if (deployment.actions?.includes("Uninstall")) {
8275
8308
  return `Deployment '${deployment.name}' lists uninstall as available but Orchestrator rejected the request ${status}. Run 'uip solution deploy list' to recheck its state, then retry. If it stays stuck, uninstall it from the Orchestrator Solutions UI. Details: ${details}`;
8276
8309
  }
8310
+ const removalAdvice = deployment.actions?.includes(WorkflowAction.Delete) ? `Delete it with: uip solution deploy delete '${deployment.name}' --yes.` : "Remove it from the Orchestrator Solutions UI.";
8277
8311
  if (deployment.activationStatus === "Inactive") {
8278
- return `Deployment '${deployment.name}' cannot be uninstalled in its current state ${status}. Available actions: ${availableActions}. Superseded or inactive deployments cannot be uninstalled from the CLI — remove them from the Orchestrator Solutions UI. Run 'uip solution deploy list' to see the current deployments. Details: ${details}`;
8312
+ return `Deployment '${deployment.name}' cannot be uninstalled in its current state ${status}. Available actions: ${availableActions}. Superseded or inactive deployments are never uninstallable. ${removalAdvice} Details: ${details}`;
8279
8313
  }
8280
- return `Deployment '${deployment.name}' cannot be uninstalled in its current state ${status}. Available actions: ${availableActions}. Run 'uip solution deploy list' to recheck its state, or remove it from the Orchestrator Solutions UI. Details: ${details}`;
8314
+ return `Deployment '${deployment.name}' cannot be uninstalled in its current state ${status}. Available actions: ${availableActions}. Run 'uip solution deploy list' to recheck its state. ${removalAdvice} Details: ${details}`;
8281
8315
  }
8282
8316
  async function waitForOperationToFinishAsync(auth, deploymentName, opts) {
8283
8317
  let last;
@@ -8297,9 +8331,10 @@ async function waitForOperationToFinishAsync(auth, deploymentName, opts) {
8297
8331
  return { outcome: poll.outcome, state: last, error: poll.error };
8298
8332
  }
8299
8333
  async function requestUninstallAsync(api, auth, deploymentName, ctx) {
8300
- const { options, location, intervalMs, remainingMs } = ctx;
8334
+ const { options, sweep, intervalMs, remainingMs } = ctx;
8335
+ const { location } = sweep;
8336
+ const sweepIncomplete = !location && !sweep.complete;
8301
8337
  let state = location?.deployment;
8302
- let stateUnknown = ctx.lookupError !== undefined;
8303
8338
  let waited = false;
8304
8339
  let rechecked = false;
8305
8340
  for (;; ) {
@@ -8307,8 +8342,11 @@ async function requestUninstallAsync(api, auth, deploymentName, ctx) {
8307
8342
  if (!uninstallError) {
8308
8343
  return { ok: true, result };
8309
8344
  }
8310
- const { message, details, errorCode, retry } = await extractErrorDetails(uninstallError);
8345
+ const { message, details, errorCode, retry, context } = await extractErrorDetails(uninstallError);
8311
8346
  if (errorCode !== "invalid_argument") {
8347
+ if (errorCode === "not_found" && sweepIncomplete) {
8348
+ return incompleteSweepFailureAsync(deploymentName, sweep, details);
8349
+ }
8312
8350
  return fail4("uninstall_request_failed", message, requestErrorInstructions(deploymentName, errorCode, details), { errorCode, retry });
8313
8351
  }
8314
8352
  if (!rechecked && !waited && state?.operationStatus !== "InProgress") {
@@ -8316,7 +8354,6 @@ async function requestUninstallAsync(api, auth, deploymentName, ctx) {
8316
8354
  const [readError, fresh] = await catchError(findDeploymentByName(auth, deploymentName, location?.scope));
8317
8355
  if (!readError) {
8318
8356
  state = fresh;
8319
- stateUnknown = false;
8320
8357
  }
8321
8358
  }
8322
8359
  if (!waited && state?.operationStatus === "InProgress") {
@@ -8341,23 +8378,25 @@ async function requestUninstallAsync(api, auth, deploymentName, ctx) {
8341
8378
  return fail4(reason, pollMessage, `The deployment may still be installing. Check it with: uip solution deploy list, then retry the uninstall.`, { exitCode, errorCode: pollErrorCode, retry: pollRetry });
8342
8379
  }
8343
8380
  state = wait.state;
8344
- stateUnknown = false;
8345
8381
  continue;
8346
8382
  }
8347
- let instructions;
8348
- if (stateUnknown) {
8349
- instructions = `Check the deployment name matches an existing deployment (run 'uip solution deploy list'). Details: ${details}`;
8350
- } else if (state) {
8351
- instructions = buildDeploymentStateInstructions(state, details);
8352
- } else {
8353
- instructions = `No deployment named '${deploymentName}' was found in any feed you can reach (tenant, Personal Workspace, or a folder feed). Check the name with 'uip solution deploy list'. Details: ${details}`;
8383
+ if (sweepIncomplete && (!state || isCompletedUninstall(state))) {
8384
+ if (isNameOrFeedRefusal(errorCode, context?.httpStatus)) {
8385
+ return incompleteSweepFailureAsync(deploymentName, sweep, details);
8386
+ }
8387
+ return fail4("uninstall_request_failed", message, requestErrorInstructions(deploymentName, errorCode, details), { errorCode, retry });
8354
8388
  }
8389
+ const instructions = state ? buildDeploymentStateInstructions(state, details) : `No deployment named '${deploymentName}' was found in any feed you can reach (tenant, Personal Workspace, or a folder feed). Check the name with 'uip solution deploy list'. Details: ${details}`;
8355
8390
  return fail4("uninstall_request_failed", message, instructions, {
8356
8391
  errorCode,
8357
8392
  retry
8358
8393
  });
8359
8394
  }
8360
8395
  }
8396
+ async function incompleteSweepFailureAsync(deploymentName, sweep, refusalDetails) {
8397
+ const report = await reportIncompleteSweepAsync(deploymentName, sweep.skipped);
8398
+ return fail4("uninstall_request_failed", report.message, `${INCOMPLETE_SWEEP_ADVICE} Without its feed the uninstall request went to the tenant feed, which refused it. ${report.nextStep} Details: ${refusalDetails} (feed search: ${report.details})`, { errorCode: report.errorCode, retry: report.retry });
8399
+ }
8361
8400
  async function uninstallDeploymentAsync(deploymentName, options = {}) {
8362
8401
  const [authError, auth] = await catchError(getSolutionAuthContext({
8363
8402
  tenant: options.tenant,
@@ -8367,18 +8406,18 @@ async function uninstallDeploymentAsync(deploymentName, options = {}) {
8367
8406
  if (authError) {
8368
8407
  return fail4("auth_failed", authError.message, authError.message);
8369
8408
  }
8370
- const api = new PipelinesApi(new Configuration({
8409
+ const api = new PipelinesApi(new Configuration2({
8371
8410
  basePath: auth.basePath,
8372
8411
  accessToken: auth.accessToken
8373
8412
  }));
8374
8413
  const deadline = Date.now() + (options.timeout ?? 360) * 1000;
8375
8414
  const intervalMs = options.pollInterval ?? 5000;
8376
8415
  const remainingMs = () => Math.max(deadline - Date.now(), 1);
8377
- const [lookupError, location] = await catchError(findDeploymentAcrossFeeds(auth, deploymentName, options));
8416
+ const sweep = await findDeploymentAcrossFeeds(auth, deploymentName, options);
8417
+ const location = sweep.location;
8378
8418
  const [requestError, requested] = await catchError(requestUninstallAsync(api, auth, deploymentName, {
8379
8419
  options,
8380
- location,
8381
- lookupError,
8420
+ sweep,
8382
8421
  intervalMs,
8383
8422
  remainingMs
8384
8423
  }));
@@ -8407,7 +8446,7 @@ async function uninstallDeploymentAsync(deploymentName, options = {}) {
8407
8446
  }
8408
8447
  const pollResult = await pollUntil({
8409
8448
  fn: () => api.pipelinesGetDeploymentInstanceStatus({ instanceId }, location?.scope),
8410
- until: (result) => TERMINAL_STATUSES3.has(result.status),
8449
+ until: (result) => TERMINAL_STATUSES2.has(result.status),
8411
8450
  getStatus: (result) => result.status,
8412
8451
  label: `uninstall ${deploymentName}`,
8413
8452
  logPrefix: "uninstall",
@@ -8426,7 +8465,7 @@ async function uninstallDeploymentAsync(deploymentName, options = {}) {
8426
8465
  const finalStatus = pollResult.data;
8427
8466
  if (finalStatus.status !== "SuccessfulUninstall") {
8428
8467
  const errors2 = coerceWorkflowErrorMessages(finalStatus.errorMessage).map((e) => e.errorMessage).join(", ");
8429
- return fail4("uninstall_failed", `Uninstall failed with status: ${finalStatus.status}`, errors2 || "Orchestrator ended the uninstall in a FailedUninstall state without an error detail. Run 'uip solution deploy list' to recheck the deployment's state and retry. If it stays in a failed state, remove it from the Orchestrator Solutions UI.");
8468
+ return fail4("uninstall_failed", `Uninstall failed with status: ${finalStatus.status}`, errors2 || `Orchestrator ended the uninstall in a FailedUninstall state without an error detail. Run 'uip solution deploy list' to recheck the deployment's state and retry. If it stays in a failed state and that listing shows Delete among its actions, remove the record with: uip solution deploy delete '${deploymentName}' --yes. Remove it from the Orchestrator Solutions UI when it does not.`);
8430
8469
  }
8431
8470
  return {
8432
8471
  ok: true,
@@ -8437,17 +8476,34 @@ async function uninstallDeploymentAsync(deploymentName, options = {}) {
8437
8476
  }
8438
8477
 
8439
8478
  // src/services/deploy-upgrade-service.ts
8440
- var fail5 = (reason, message, instructions, exitCode = 1) => ({
8479
+ var fail5 = (reason, message, instructions, extras = {}) => ({
8441
8480
  ok: false,
8442
8481
  reason,
8443
8482
  message,
8444
8483
  instructions,
8445
- exitCode
8484
+ exitCode: extras.exitCode ?? 1,
8485
+ errorCode: extras.errorCode,
8486
+ retry: extras.retry
8446
8487
  });
8447
8488
  var DEFAULT_TIMEOUT_SECONDS = 300;
8448
8489
  var DEFAULT_POLL_INTERVAL_MS = 5000;
8449
8490
  var AUTH_INSTRUCTIONS2 = "Run 'uip login' to refresh credentials, then retry. If the deployment is in a different tenant, switch with 'uip login tenant set <tenant>'.";
8450
- async function upgradeDeploymentAsync(deploymentKey, options = {}) {
8491
+ var PACKAGE_INSTRUCTIONS = "Confirm the version is published to the tenant (or Personal Workspace) feed: uip solution packages list.";
8492
+ var TARGET_INSTRUCTIONS = "Address the deployment either by key — uip solution deploy upgrade <deployment-key> — or by name with its package: uip solution deploy upgrade --name <deployment-name> --package-name <package>. 'uip solution deploy list' reports all three.";
8493
+ var DEPLOYMENT_NOT_FOUND_INSTRUCTIONS = "Check the key with 'uip solution deploy list'. A server without the Pipelines deployment lookup answers 404 to every key for an external app; on one of those, address the deployment with --name and --package-name instead.";
8494
+ var LOOKUP_FAILED_INSTRUCTIONS = "The deployment lookup failed before any upgrade was requested. Retry, or address the deployment with --name and --package-name.";
8495
+ var INVALID_TARGET = {
8496
+ errorCode: "invalid_argument",
8497
+ retry: "RetryWillNotFix"
8498
+ };
8499
+ var GUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
8500
+ var UPGRADE_REFUSED_INSTRUCTIONS = "The server refused to start the upgrade. The deployment name may not exist, or may belong to a different package — check both with 'uip solution deploy list'. Another operation may still be running on it (wait, then retry), or it may be in a state the platform does not offer upgrades for.";
8501
+ async function upgradeDeploymentAsync(first, second) {
8502
+ const options = typeof first === "string" ? { ...second, deploymentKey: first } : first;
8503
+ const targetError = validateTarget(options);
8504
+ if (targetError) {
8505
+ return targetError;
8506
+ }
8451
8507
  const [authError, auth] = await catchError(getSolutionAuthContext({
8452
8508
  tenant: options.tenant,
8453
8509
  loginValidity: options.loginValidity,
@@ -8456,140 +8512,442 @@ async function upgradeDeploymentAsync(deploymentKey, options = {}) {
8456
8512
  if (authError) {
8457
8513
  return fail5("auth_failed", authError.message, AUTH_INSTRUCTIONS2);
8458
8514
  }
8459
- const config = new Configuration2({
8515
+ const pipelinesApi = new PipelinesApi(new Configuration2({
8460
8516
  basePath: auth.basePath,
8461
8517
  accessToken: auth.accessToken
8462
- });
8463
- const deploymentsApi = new DeploymentsApi(config);
8464
- const searchApi = new SearchApi(config);
8465
- let feedScope;
8466
- if (options.personalWorkspace) {
8467
- const [pwError, pw] = await catchError(resolvePersonalWorkspace({
8468
- tenant: options.tenant,
8469
- loginValidity: options.loginValidity
8470
- }));
8471
- if (pwError || !pw) {
8472
- return fail5("personal_workspace_failed", pwError?.message ?? "Personal Workspace could not be resolved.", "Ensure your user has a Personal Workspace, then retry.");
8518
+ }));
8519
+ const target = options.deploymentKey ? await resolveTargetByKeyAsync(pipelinesApi, auth, options, options.deploymentKey) : await resolveTargetByNameAsync(pipelinesApi, auth, options);
8520
+ if (!target.ok) {
8521
+ return target;
8522
+ }
8523
+ const { deploymentName, packageName, scope } = target;
8524
+ let targetVersion = options.version;
8525
+ if (targetVersion === undefined) {
8526
+ const [versionError, latest] = await catchError(pipelinesApi.pipelinesGetPackageVersion({ packageName, packageVersion: null }, scope));
8527
+ if (versionError || !latest) {
8528
+ const detail = versionError && await extractErrorDetails(versionError);
8529
+ if (!detail || detail.context?.httpStatus === 404) {
8530
+ return fail5("package_not_found", detail?.details ? `Package '${packageName}' was not found in the feed: ${detail.details}` : `Package '${packageName}' was not found in the feed.`, PACKAGE_INSTRUCTIONS, { errorCode: "not_found", retry: "RetryWillNotFix" });
8531
+ }
8532
+ return fail5("lookup_failed", detail.details ? `${detail.message}: ${detail.details}` : detail.message, `The version lookup for '${packageName}' failed before any upgrade was requested. Retry, or pass --version explicitly to skip the lookup.`, { errorCode: detail.errorCode, retry: detail.retry });
8473
8533
  }
8474
- feedScope = folderKeyInitOverride(pw.key);
8475
- }
8476
- const [getError, deployment] = await catchError(deploymentsApi.deploymentsGetByKey({ deploymentKey }, feedScope));
8477
- if (getError || !deployment) {
8478
- const detail = getError && await extractErrorDetails(getError);
8479
- return fail5("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.");
8480
- }
8481
- const upgradeScope = feedScope;
8482
- const take = 50;
8483
- let pkg;
8484
- for (let skip = 0;; ) {
8485
- const [searchError, packages] = await catchError(searchApi.searchSearchPackages({
8486
- searchFilter: {
8487
- searchTerm: deployment.packageName,
8488
- take,
8489
- skip
8490
- }
8491
- }, feedScope));
8492
- if (searchError) {
8493
- const { message, details } = await extractErrorDetails(searchError);
8494
- return fail5("package_not_found", details ? `${message}: ${details}` : message, "Confirm the package is published to the tenant (or Personal Workspace) feed.");
8495
- }
8496
- const values = packages.values ?? [];
8497
- pkg = values.find((p) => p.name === deployment.packageName);
8498
- if (pkg || values.length < take) {
8499
- break;
8500
- }
8501
- skip += values.length;
8502
- }
8503
- if (!pkg) {
8504
- return fail5("package_not_found", `Package '${deployment.packageName}' was not found in the feed.`, "Confirm the package is published to the tenant (or Personal Workspace) feed.");
8505
- }
8506
- if (options.version && options.version !== pkg.latestVersion) {
8507
- return fail5("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.");
8508
- }
8509
- const currentVersion = deployment.currentPackageVersion ?? deployment.packageVersion;
8510
- if (currentVersion === pkg.latestVersion) {
8511
- return fail5("no_upgrade_available", `Deployment '${deployment.name}' is already at the latest published version (${pkg.latestVersion}).`, "Publish a newer version of the package, then retry.");
8512
- }
8513
- const [upgradeError, draft] = await catchError(deploymentsApi.deploymentsUpgrade({
8514
- deploymentKey,
8515
- packageVersionKey: pkg.packageVersionKey,
8516
- deploymentUpgradeRequest: {
8517
- packageVersionKey: pkg.packageVersionKey
8518
- }
8519
- }, upgradeScope));
8520
- if (upgradeError || !draft) {
8521
- let message = "The upgrade request returned no deployment.";
8522
- if (upgradeError) {
8523
- const detail = await extractErrorDetails(upgradeError);
8534
+ targetVersion = latest.packageVersion;
8535
+ }
8536
+ const [installError, installResult] = await catchError(pipelinesApi.pipelinesInstall({
8537
+ deploymentName,
8538
+ packageName,
8539
+ packageVersion: targetVersion,
8540
+ solutionRootFolderName: deploymentName
8541
+ }, scope));
8542
+ if (installError || !installResult?.pipelineDeploymentId) {
8543
+ let message = "The upgrade request returned no pipeline deployment.";
8544
+ let classification = {};
8545
+ let instructions = UPGRADE_REFUSED_INSTRUCTIONS;
8546
+ if (installError) {
8547
+ const detail = await extractErrorDetails(installError);
8524
8548
  message = detail.details ? `${detail.message}: ${detail.details}` : detail.message;
8549
+ classification = {
8550
+ errorCode: detail.errorCode,
8551
+ retry: detail.retry
8552
+ };
8553
+ if (detail.context?.httpStatus === 404) {
8554
+ instructions = `The server knows no such version of '${packageName}' in this feed. ${PACKAGE_INSTRUCTIONS} 'uip solution deploy list' shows the deployment's package and current version.`;
8555
+ }
8556
+ if (hasServerErrorCode(detail.details, DRAFT_VERSION_MISMATCH_CODE)) {
8557
+ return fail5("pending_draft", message, `'${deploymentName}' has a pending draft for another version: an earlier attempt that never completed, usually one rejected by validation, and the server will not upgrade past it. Discard it and upgrade the live version: ${discardRetryCommand(options, target)}${options.deploymentKey ? "" : `
8558
+ Under an external app the name form cannot find the draft; pass the deployment's key instead (InstallDeploymentKey from the original deploy, or Key from 'uip solution deploy list').`}`, INVALID_TARGET);
8559
+ }
8525
8560
  }
8526
- return fail5("request_failed", message, "The upgrade could not be started. Check the deployment key and that a newer version is published.");
8527
- }
8528
- const [runError] = await catchError(deploymentsApi.deploymentsRun({ deploymentKey: draft.key }, upgradeScope));
8529
- if (runError) {
8530
- const { message, details } = await extractErrorDetails(runError);
8531
- return fail5("install_failed", details ? `${message}: ${details}` : message, `The upgrade was queued as deployment '${draft.key}' but installing it failed. Run 'uip solution deploy list' to inspect its state.`);
8561
+ return fail5("request_failed", message, instructions, classification);
8532
8562
  }
8563
+ const pipelineDeploymentId = installResult.pipelineDeploymentId;
8533
8564
  const base = {
8534
- deploymentName: deployment.name,
8535
- fromVersion: currentVersion,
8536
- toVersion: pkg.latestVersion,
8537
- versionChangeKey: draft.key
8565
+ deploymentName,
8566
+ toVersion: targetVersion,
8567
+ pipelineDeploymentId,
8568
+ ...target.fromVersion !== undefined && {
8569
+ fromVersion: target.fromVersion
8570
+ },
8571
+ ...target.installDeploymentKey !== undefined && {
8572
+ installDeploymentKey: target.installDeploymentKey
8573
+ },
8574
+ ...target.discardedDraftKey !== undefined && {
8575
+ discardedDraftKey: target.discardedDraftKey
8576
+ }
8538
8577
  };
8539
8578
  if (options.noWait) {
8540
- return { ok: true, ...base, status: draft.operationStatus };
8579
+ return { ok: true, ...base, status: "Accepted" };
8541
8580
  }
8542
8581
  const timeoutSeconds = Number.isFinite(options.timeout) ? options.timeout : DEFAULT_TIMEOUT_SECONDS;
8543
- const poll = await pollInstallAsync(deploymentsApi, draft.key, upgradeScope, {
8582
+ const poll = await pollPipelineDeploymentAsync(pipelinesApi, auth, {
8583
+ pipelineDeploymentId,
8584
+ deploymentName,
8585
+ scope,
8544
8586
  timeoutMs: timeoutSeconds * 1000,
8545
- intervalMs: Number.isFinite(options.pollInterval) ? options.pollInterval : DEFAULT_POLL_INTERVAL_MS
8587
+ intervalMs: Number.isFinite(options.pollInterval) ? options.pollInterval : DEFAULT_POLL_INTERVAL_MS,
8588
+ signal: options.signal,
8589
+ logPrefix: "deploy upgrade"
8546
8590
  });
8547
- if (!poll.ok) {
8591
+ if (poll.outcome !== PollOutcome.Completed) {
8592
+ const failure = mapPollFailure(poll, `Upgrade ${pipelineDeploymentId}`);
8548
8593
  const isTimeout = poll.outcome === PollOutcome.Timeout;
8549
- return fail5(isTimeout ? "install_timeout" : poll.failure.reason, isTimeout ? `The upgrade of '${deployment.name}' to ${pkg.latestVersion} did not finish within ${timeoutSeconds}s.` : `${poll.failure.message} while upgrading '${deployment.name}' to ${pkg.latestVersion} (last status: ${poll.lastStatus}).`, isTimeout ? `It may still be running. Track deployment '${draft.key}' with 'uip solution deploy list', or pass a longer --timeout.` : `The upgrade may still be running. Track deployment '${draft.key}' with 'uip solution deploy list', then retry if it did not land.`, poll.failure.exitCode);
8594
+ const lastStatus = poll.data?.status ?? "unknown";
8595
+ return fail5(isTimeout ? "install_timeout" : failure.reason, isTimeout ? `The upgrade of '${deploymentName}' to ${targetVersion} did not finish within ${timeoutSeconds}s (last status: ${lastStatus}).` : `${failure.message} while upgrading '${deploymentName}' to ${targetVersion} (last status: ${lastStatus}).`, isTimeout ? `It may still be running. ${trackingHint(pipelineDeploymentId, target.listFlag)} Or pass a longer --timeout.` : `The upgrade may still be running. ${trackingHint(pipelineDeploymentId, target.listFlag)}`, {
8596
+ exitCode: failure.exitCode,
8597
+ errorCode: failure.errorCode,
8598
+ retry: failure.retry
8599
+ });
8550
8600
  }
8551
- const status = poll.status;
8552
- if (FAILURE_STATUSES.has(status)) {
8553
- return fail5("install_failed", `The upgrade of '${deployment.name}' to ${pkg.latestVersion} ended in ${status}: ${await describeValidationFailureAsync(deploymentsApi, draft.key, SolutionDeploymentAction.Upgrade, upgradeScope)}`, "Fix the reported problem, then retry the upgrade.");
8601
+ const finalStatus = poll.data;
8602
+ if (!finalStatus) {
8603
+ return fail5("deploy_failed", `The upgrade of '${deploymentName}' returned no final status.`, trackingHint(pipelineDeploymentId, target.listFlag));
8604
+ }
8605
+ const landedDespiteStatus = installLandedDespiteStatus(finalStatus);
8606
+ if (finalStatus.status !== "DeploymentSucceeded" && !landedDespiteStatus) {
8607
+ return fail5("deploy_failed", `The upgrade of '${deploymentName}' to ${targetVersion} ended in ${finalStatus.status}.`, `${await describeUpgradeFailureAsync(auth.basePath, auth.accessToken, pipelineDeploymentId, finalStatus, scope, options.signal)}
8608
+ ${leavesPendingDraft(finalStatus.status) ? `Nothing was installed; the deployment stays on the version it was already running. The rejected attempt is kept as a pending draft${finalStatus.deploymentKey ? ` (${finalStatus.deploymentKey})` : ""}, which a plain retry would run into, so once the cause is fixed discard it and retry: ${discardRetryCommand(options, target, finalStatus.deploymentKey || undefined)}` : "Fix the reported problem, then retry the upgrade. The deployment stays on the version it was already running."}`, classifyTerminalFailure(finalStatus.status));
8554
8609
  }
8555
- return { ok: true, ...base, status };
8610
+ const described = await describeUpgradedDeploymentAsync(auth, deploymentName, finalStatus, scope, target.listFlag);
8611
+ return {
8612
+ ok: true,
8613
+ ...base,
8614
+ deploymentKey: finalStatus.deploymentKey,
8615
+ operation: finalStatus.deploymentResult?.operation ?? finalStatus.synthesizedOperation,
8616
+ status: finalStatus.status,
8617
+ ...finalStatus.deploymentResult?.status && {
8618
+ deploymentStatus: finalStatus.deploymentResult.status,
8619
+ actions: finalStatus.deploymentResult.actions ?? []
8620
+ },
8621
+ ...described,
8622
+ ...landedDespiteStatus && {
8623
+ nextSteps: [
8624
+ explainPipelineStatusAfterInstall(finalStatus),
8625
+ ...described.nextSteps ?? []
8626
+ ]
8627
+ }
8628
+ };
8556
8629
  }
8557
- var IN_FLIGHT_STATUSES = new Set([
8558
- DeploymentOperationStatus.Draft,
8559
- DeploymentOperationStatus.InProgress
8560
- ]);
8561
- var FAILURE_STATUSES = new Set([
8562
- DeploymentOperationStatus.Failed,
8563
- DeploymentOperationStatus.FailedRollback
8564
- ]);
8565
- async function pollInstallAsync(deploymentsApi, versionChangeKey, scope, timing) {
8566
- const result = await pollUntil({
8567
- fn: () => deploymentsApi.deploymentsGetByKey({ deploymentKey: versionChangeKey }, scope),
8568
- until: (deployment) => !IN_FLIGHT_STATUSES.has(deployment.operationStatus),
8569
- getStatus: (deployment) => deployment.operationStatus,
8570
- label: `upgrade ${versionChangeKey}`,
8571
- logPrefix: "deploy upgrade",
8572
- timeoutMs: timing.timeoutMs,
8573
- intervalMs: timing.intervalMs
8574
- });
8575
- if (result.outcome === PollOutcome.Completed && result.data) {
8576
- return { ok: true, status: result.data.operationStatus };
8630
+ async function describeUpgradedDeploymentAsync(auth, deploymentName, finalStatus, scope, listFlag) {
8631
+ const record = finalStatus.deploymentResult;
8632
+ const actions = record?.actions ?? [];
8633
+ if (record?.status === "SuccessfulActivate") {
8634
+ return { activationStatus: "Active" };
8635
+ }
8636
+ const [, rows] = await catchError(findDeploymentsByName(auth, deploymentName, scope));
8637
+ const row = rows?.find((candidate) => candidate.key === finalStatus.deploymentKey);
8638
+ const activationStatus = row?.activationStatus ?? (actions.includes("SetupActivation") ? "NeedsSetupToActivate" : actions.includes("Activate") ? "ReadyToActivate" : undefined);
8639
+ if (!activationStatus) {
8640
+ return {};
8641
+ }
8642
+ const state = {
8643
+ name: deploymentName,
8644
+ operation: row?.operation ?? record?.operation ?? finalStatus.synthesizedOperation,
8645
+ operationStatus: row?.operationStatus ?? "Successful",
8646
+ activationStatus,
8647
+ packageVersion: row?.packageVersion ?? record?.packageVersion,
8648
+ currentPackageVersion: row?.currentPackageVersion ?? record?.packageVersion
8649
+ };
8650
+ const pendingResources = needsPendingResources(state) && finalStatus.deploymentKey ? await readPendingResourcesAsync(new DeploymentsApi(new Configuration({
8651
+ basePath: auth.basePath,
8652
+ accessToken: auth.accessToken
8653
+ })), finalStatus.deploymentKey, scope) : undefined;
8654
+ const nextSteps = deploymentNextSteps(state, pendingResources, listFlag ? ` ${listFlag}` : "");
8655
+ return {
8656
+ activationStatus,
8657
+ ...pendingResources ? { pendingResources } : {},
8658
+ ...nextSteps && nextSteps.length > 0 ? { nextSteps } : {}
8659
+ };
8660
+ }
8661
+ function trackingHint(pipelineDeploymentId, listFlag) {
8662
+ return listFlag ? `Track it with: uip solution deploy list ${listFlag} (deploy status reads the tenant feed and cannot see this run).` : `Track it with: uip solution deploy status ${pipelineDeploymentId}, or uip solution deploy list.`;
8663
+ }
8664
+ function discardRetryCommand(options, target, draftKey) {
8665
+ const version = options.version ? ` --version ${options.version}` : "";
8666
+ const key = draftKey ?? options.deploymentKey;
8667
+ if (key) {
8668
+ return `uip solution deploy upgrade ${key} --discard-draft${version}`;
8669
+ }
8670
+ const workspace = options.personalWorkspace ? " --personal-workspace" : "";
8671
+ return `uip solution deploy upgrade --name ${target.deploymentName} --package-name ${target.packageName}${workspace} --discard-draft${version}`;
8672
+ }
8673
+ function validateTarget(options) {
8674
+ const byKey = options.deploymentKey !== undefined;
8675
+ const byName = options.deploymentName !== undefined || options.packageName !== undefined;
8676
+ if (options.version !== undefined && options.version.trim() === "") {
8677
+ return fail5("invalid_target", "--version was given but is empty.", "Pass the version to upgrade to (uip solution packages list shows the published ones), or omit --version to upgrade to the newest.", INVALID_TARGET);
8678
+ }
8679
+ if (byKey && byName) {
8680
+ return fail5("invalid_target", "Pass either a deployment key or --name with --package-name, not both.", TARGET_INSTRUCTIONS, INVALID_TARGET);
8681
+ }
8682
+ if (byKey) {
8683
+ return GUID_PATTERN.test(options.deploymentKey) ? undefined : fail5("invalid_target", `'${options.deploymentKey}' is not a deployment key.`, `A deployment key is a GUID, reported as Key by 'uip solution deploy list'. To address the deployment by name instead: ${TARGET_INSTRUCTIONS}`, INVALID_TARGET);
8684
+ }
8685
+ if (!options.deploymentName || !options.packageName) {
8686
+ return fail5("invalid_target", "--name and --package-name go together: the Pipelines install route needs both.", TARGET_INSTRUCTIONS, INVALID_TARGET);
8687
+ }
8688
+ return;
8689
+ }
8690
+ async function resolveTargetByNameAsync(pipelinesApi, auth, options) {
8691
+ const deploymentName = options.deploymentName;
8692
+ let scope;
8693
+ if (options.personalWorkspace) {
8694
+ const verdict = classifyFeedSession(auth);
8695
+ if (verdict !== "user") {
8696
+ const { message, instructions } = feedSessionRefusal(verdict, `Upgrading the Personal Workspace deployment '${deploymentName}'`, "address the deployment by key");
8697
+ return fail5("feed_requires_user_session", message, instructions, {
8698
+ errorCode: "permission_denied",
8699
+ retry: "RetryWillNotFix"
8700
+ });
8701
+ }
8702
+ const [pwError, pw] = await catchError(resolvePersonalWorkspace({
8703
+ tenant: options.tenant,
8704
+ loginValidity: options.loginValidity,
8705
+ envFilePath: options.envFilePath
8706
+ }));
8707
+ if (pwError || !pw) {
8708
+ return fail5("personal_workspace_failed", pwError?.message ?? "Personal Workspace could not be resolved.", "Ensure your user has a Personal Workspace, then retry.");
8709
+ }
8710
+ scope = folderKeyInitOverride(pw.key);
8711
+ }
8712
+ const [existsError] = await catchError(pipelinesApi.pipelinesGetPreActivateSteps({ deploymentName }, scope));
8713
+ if (existsError) {
8714
+ const detail = await extractErrorDetails(existsError);
8715
+ const where = scope ? "your Personal Workspace feed" : `the tenant feed of '${auth.tenantName}'`;
8716
+ if (isDeploymentNotFound(detail)) {
8717
+ return fail5("deployment_not_found", `Deployment '${deploymentName}' was not found in ${where}. 'deploy upgrade' never creates a deployment.`, `Check the name with 'uip solution deploy list'${scope ? "" : " (add --personal-workspace for a Personal Workspace deployment)"}. For a first deployment use 'uip solution deploy run'.`, { errorCode: "not_found", retry: "RetryWillNotFix" });
8718
+ }
8719
+ return fail5("lookup_failed", detail.details ? `${detail.message}: ${detail.details}` : detail.message, `The existence check for '${deploymentName}' failed before any upgrade was requested. Retry, or address the deployment by key: uip solution deploy upgrade <deployment-key>.`, { errorCode: detail.errorCode, retry: detail.retry });
8720
+ }
8721
+ const [rowsError, rows] = await catchError(findDeploymentsByName(auth, deploymentName, scope));
8722
+ if (rowsError) {
8723
+ const unreadable = rowsError instanceof DeploymentSearchNeedsUserSessionError || classifyFeedSession(auth) === "application" && isAuthRefusal(rowsError);
8724
+ if (!unreadable) {
8725
+ const detail = await extractErrorDetails(rowsError);
8726
+ return fail5("lookup_failed", `The status check for '${deploymentName}' failed: ${detail.message}`, "The deployment exists, but whether it is still installed could not be confirmed, and upgrading an uninstalled name would deploy the package afresh under it. Retry, or address the deployment by key: uip solution deploy upgrade <deployment-key>.", { errorCode: detail.errorCode, retry: detail.retry });
8727
+ }
8728
+ }
8729
+ if (isCompletedUninstall(rows ? newestDeploymentRow(rows) : undefined)) {
8730
+ return fail5("deployment_uninstalled", `Deployment '${deploymentName}' has been uninstalled, so there is nothing to upgrade.`, "The install route would create a new deployment under that name rather than upgrade this one. Deploy the package afresh with 'uip solution deploy run', or pick a live deployment from 'uip solution deploy list'.", INVALID_TARGET);
8731
+ }
8732
+ const resolved = {
8733
+ deploymentName,
8734
+ packageName: options.packageName,
8735
+ listFlag: scope ? "--personal-workspace" : undefined
8736
+ };
8737
+ let discardedDraftKey;
8738
+ const newest = rows ? newestDeploymentRow(rows) : undefined;
8739
+ if (newest?.operationStatus === "Draft") {
8740
+ const draft = {
8741
+ key: newest.key,
8742
+ firstInstall: newest.operation === "Install",
8743
+ activationSetup: newest.operation === "ActivationSetup",
8744
+ targetVersion: newest.targetPackageVersion ?? newest.packageVersion,
8745
+ liveVersion: newest.currentPackageVersion
8746
+ };
8747
+ const refusal = refusePendingDraft(options, resolved, draft);
8748
+ if (refusal) {
8749
+ return refusal;
8750
+ }
8751
+ const discarded = await discardDraftAsync(pipelinesApi, deploymentName, draft.key, scope);
8752
+ if (discarded) {
8753
+ return discarded;
8754
+ }
8755
+ discardedDraftKey = draft.key;
8577
8756
  }
8578
8757
  return {
8579
- ok: false,
8580
- outcome: result.outcome,
8581
- lastStatus: result.data?.operationStatus ?? DeploymentOperationStatus.Draft,
8582
- failure: mapPollFailure(result, `Upgrade ${versionChangeKey}`)
8758
+ ok: true,
8759
+ ...resolved,
8760
+ scope,
8761
+ discardedDraftKey
8583
8762
  };
8584
8763
  }
8764
+ function isDeploymentNotFound(detail) {
8765
+ const status = detail.context?.httpStatus;
8766
+ if (status === 404) {
8767
+ return true;
8768
+ }
8769
+ return status === 400 && (/"errorCode"\s*:\s*"4004"/.test(detail.details) || (detail.parsedErrors?.deploymentName ?? []).some((message) => /not found/i.test(message)));
8770
+ }
8771
+ async function resolveTargetByKeyAsync(pipelinesApi, auth, options, deploymentKey) {
8772
+ const [lookupError, info] = await catchError(pipelinesApi.pipelinesGetDeploymentByKey({ key: deploymentKey }));
8773
+ if (lookupError || !info) {
8774
+ const detail = lookupError ? await extractErrorDetails(lookupError) : undefined;
8775
+ if (detail?.context?.httpStatus === 404 && classifyFeedSession(auth) === "user") {
8776
+ return resolveTargetByKeyViaSolutionsAsync(pipelinesApi, auth, options, deploymentKey);
8777
+ }
8778
+ if (!detail || detail.context?.httpStatus === 404) {
8779
+ return fail5("deployment_not_found", `Deployment '${deploymentKey}' was not found in tenant '${auth.tenantName}'.`, DEPLOYMENT_NOT_FOUND_INSTRUCTIONS, { errorCode: "not_found", retry: "RetryWillNotFix" });
8780
+ }
8781
+ return fail5("lookup_failed", detail.details ? `${detail.message}: ${detail.details}` : detail.message, LOOKUP_FAILED_INSTRUCTIONS, { errorCode: detail.errorCode, retry: detail.retry });
8782
+ }
8783
+ if (info.status === DeploymentStatus.SuccessfulUninstall) {
8784
+ return fail5("deployment_uninstalled", `Deployment '${info.deploymentName}' (${deploymentKey}) has been uninstalled, so there is nothing to upgrade.`, "The install route would create a new deployment under that name rather than upgrade this one. Deploy the package afresh with 'uip solution deploy run', or pick a live deployment from 'uip solution deploy list'.", INVALID_TARGET);
8785
+ }
8786
+ let scope;
8787
+ if (info.locationKey) {
8788
+ const verdict = classifyFeedSession(auth);
8789
+ if (verdict !== "user") {
8790
+ const { message, instructions } = feedSessionRefusal(verdict, `Upgrading deployment '${info.deploymentName}', which is in a Personal Workspace or folder feed (folder key ${info.locationKey}),`, "run the upgrade from the Orchestrator UI");
8791
+ return fail5("feed_requires_user_session", message, instructions, {
8792
+ errorCode: "permission_denied",
8793
+ retry: "RetryWillNotFix"
8794
+ });
8795
+ }
8796
+ scope = folderKeyInitOverride(info.locationKey);
8797
+ } else if (options.personalWorkspace) {
8798
+ return fail5("invalid_target", `Deployment '${info.deploymentName}' (${deploymentKey}) is on the tenant feed, not in a Personal Workspace.`, "Drop --personal-workspace: with a deployment key the feed is read from the deployment itself.", INVALID_TARGET);
8799
+ }
8800
+ const listFlag = info.locationKey ? `--feed ${info.locationKey}` : undefined;
8801
+ let live = info;
8802
+ let discardedDraftKey;
8803
+ if (info.status === DeploymentStatus.Draft) {
8804
+ const draft = {
8805
+ key: info.deploymentKey,
8806
+ firstInstall: info.operation === DeploymentOperation.Install,
8807
+ activationSetup: info.operation === DeploymentOperation.ActivationSetup,
8808
+ targetVersion: info.targetPackageVersion ?? info.packageVersion,
8809
+ liveVersion: info.packageVersion
8810
+ };
8811
+ const refusal = refusePendingDraft(options, {
8812
+ deploymentName: info.deploymentName,
8813
+ packageName: info.packageName,
8814
+ listFlag
8815
+ }, draft);
8816
+ if (refusal) {
8817
+ return refusal;
8818
+ }
8819
+ const discarded = await discardDraftAsync(pipelinesApi, info.deploymentName, draft.key, scope);
8820
+ if (discarded) {
8821
+ return discarded;
8822
+ }
8823
+ const [relookupError, current] = await catchError(pipelinesApi.pipelinesGetDeploymentByKey({
8824
+ key: info.installDeploymentKey
8825
+ }));
8826
+ if (relookupError || !current) {
8827
+ const detail = relookupError ? await extractErrorDetails(relookupError) : undefined;
8828
+ return fail5("lookup_failed", `The pending draft ${draft.key} of '${info.deploymentName}' was discarded, but the live deployment could not be looked up again${detail ? `: ${detail.message}` : "."}`, `Nothing was upgraded. Retry the upgrade: uip solution deploy upgrade ${info.installDeploymentKey}${options.version ? ` --version ${options.version}` : ""}`, detail ? { errorCode: detail.errorCode, retry: detail.retry } : {});
8829
+ }
8830
+ if (current.status === DeploymentStatus.Draft || current.status === DeploymentStatus.SuccessfulUninstall) {
8831
+ return fail5("pending_draft", `The pending draft ${draft.key} of '${info.deploymentName}' was discarded, but the deployment's newest generation is now ${current.status === DeploymentStatus.Draft ? `another draft (${current.deploymentKey})` : "an uninstall"}.`, "Nothing was upgraded. Another operation on this deployment ran in between; check it with 'uip solution deploy list', then retry.", INVALID_TARGET);
8832
+ }
8833
+ live = current;
8834
+ discardedDraftKey = draft.key;
8835
+ }
8836
+ return {
8837
+ ok: true,
8838
+ deploymentName: live.deploymentName,
8839
+ packageName: live.packageName,
8840
+ scope,
8841
+ listFlag,
8842
+ fromVersion: live.packageVersion ?? undefined,
8843
+ installDeploymentKey: live.installDeploymentKey,
8844
+ discardedDraftKey
8845
+ };
8846
+ }
8847
+ async function resolveTargetByKeyViaSolutionsAsync(pipelinesApi, auth, options, deploymentKey) {
8848
+ let scope;
8849
+ if (options.personalWorkspace) {
8850
+ const [pwError, pw] = await catchError(resolvePersonalWorkspace({
8851
+ tenant: options.tenant,
8852
+ loginValidity: options.loginValidity,
8853
+ envFilePath: options.envFilePath
8854
+ }));
8855
+ if (pwError || !pw) {
8856
+ return fail5("personal_workspace_failed", pwError?.message ?? "Personal Workspace could not be resolved.", "Ensure your user has a Personal Workspace, then retry.");
8857
+ }
8858
+ scope = folderKeyInitOverride(pw.key);
8859
+ }
8860
+ const deploymentsApi = new DeploymentsApi(new Configuration({
8861
+ basePath: auth.basePath,
8862
+ accessToken: auth.accessToken
8863
+ }));
8864
+ const [readError, row] = await catchError(deploymentsApi.deploymentsGetByKey({ deploymentKey }, scope));
8865
+ if (readError || !row) {
8866
+ const detail = readError ? await extractErrorDetails(readError) : undefined;
8867
+ if (!detail || detail.context?.httpStatus === 404) {
8868
+ return fail5("deployment_not_found", `Deployment '${deploymentKey}' was not found in ${scope ? "your Personal Workspace feed" : `the tenant feed of '${auth.tenantName}'`}.`, `Check the key with 'uip solution deploy list'. Neither the Pipelines deployment lookup nor the Solutions read found it; the latter reads a key on the tenant feed, or with --personal-workspace in your workspace, so address a folder-feed deployment with --name and --package-name instead.`, { errorCode: "not_found", retry: "RetryWillNotFix" });
8869
+ }
8870
+ return fail5("lookup_failed", detail.details ? `${detail.message}: ${detail.details}` : detail.message, LOOKUP_FAILED_INSTRUCTIONS, { errorCode: detail.errorCode, retry: detail.retry });
8871
+ }
8872
+ if (row.status === "SuccessfulUninstall") {
8873
+ return fail5("deployment_uninstalled", `Deployment '${row.name}' (${deploymentKey}) has been uninstalled, so there is nothing to upgrade.`, "The install route would create a new deployment under that name rather than upgrade this one. Deploy the package afresh with 'uip solution deploy run', or pick a live deployment from 'uip solution deploy list'.", INVALID_TARGET);
8874
+ }
8875
+ const target = await resolveTargetByNameAsync(pipelinesApi, auth, {
8876
+ ...options,
8877
+ deploymentName: row.name,
8878
+ packageName: row.packageName
8879
+ });
8880
+ if (!target.ok) {
8881
+ return target;
8882
+ }
8883
+ return { ...target, installDeploymentKey: row.installDeploymentKey };
8884
+ }
8885
+ function refusePendingDraft(options, target, draft) {
8886
+ const targetVersion = draft.targetVersion ?? "<version>";
8887
+ const feed = target.listFlag ? ` ${target.listFlag}` : "";
8888
+ const retryDraft = `uip solution deploy run --name ${target.deploymentName} --package-name ${target.packageName} --package-version ${targetVersion} --config-file <fixed-config>${feed} --yes`;
8889
+ if (draft.firstInstall) {
8890
+ return fail5("pending_draft", `Deployment '${target.deploymentName}' was never installed: its first install (${draft.key}) is still a pending draft, so there is nothing to upgrade.`, `The draft is an attempt that never completed, usually one rejected by validation, or one still running. To retry it with a corrected configuration: ${retryDraft} (the server re-validates the draft with the new file). To abandon it, delete it in Orchestrator.`, INVALID_TARGET);
8891
+ }
8892
+ if (options.discardDraft) {
8893
+ return;
8894
+ }
8895
+ if (draft.activationSetup) {
8896
+ return fail5("pending_draft", `Deployment '${target.deploymentName}' has an unfinished Setup activation (${draft.key}): the Orchestrator "Setup activation" wizard was started on version ${targetVersion} and not completed.`, `While that draft exists the server would run it in place of this upgrade, or refuse a different version. Finish the setup in Orchestrator (Solutions, the deployment, Setup activation), then retry; or discard it and upgrade the live version: ${discardRetryCommand(options, target, draft.key)}`, INVALID_TARGET);
8897
+ }
8898
+ const live = draft.liveVersion ? ` ${draft.liveVersion}` : "";
8899
+ return fail5("pending_draft", `Deployment '${target.deploymentName}' has a pending draft (${draft.key}) for version ${targetVersion}: an earlier attempt that never completed, usually one rejected by validation, or one still running.`, `The server would run that draft, with the configuration it was built with, in place of this upgrade, or refuse a different version. To discard it and upgrade the live version${live}: ${discardRetryCommand(options, target, draft.key)}
8900
+ To retry the draft itself with a corrected configuration instead: ${retryDraft}`, INVALID_TARGET);
8901
+ }
8902
+ async function discardDraftAsync(pipelinesApi, deploymentName, draftKey, scope) {
8903
+ const [error] = await catchError(pipelinesApi.pipelinesDeleteDeploymentByKey({ deploymentKey: draftKey }, scope));
8904
+ if (!error) {
8905
+ return;
8906
+ }
8907
+ const detail = await extractErrorDetails(error);
8908
+ const status = detail.context?.httpStatus;
8909
+ const hasBody = !isBareResponse(detail.details);
8910
+ if (hasServerErrorCode(detail.details, "DeploymentNotFound") || status === 404 && hasBody) {
8911
+ return;
8912
+ }
8913
+ const message = `The pending draft ${draftKey} of '${deploymentName}' could not be discarded: ${hasBody ? `${detail.message}: ${detail.details}` : detail.message}`;
8914
+ if (status === 405 || status === 404 && !hasBody) {
8915
+ return fail5("discard_draft_failed", message, "This server cannot discard drafts through the Pipelines API yet. Nothing was upgraded. Delete the draft in Orchestrator, then retry the upgrade.", {
8916
+ errorCode: status === 405 ? "method_not_allowed" : "not_found",
8917
+ retry: "RetryWillNotFix"
8918
+ });
8919
+ }
8920
+ if (hasServerErrorCode(detail.details, "DeploymentAlreadyInProgress")) {
8921
+ return fail5("discard_draft_failed", message, "The draft is still being validated. Nothing was upgraded. Wait for its run to finish (uip solution deploy list shows it), then retry.", { errorCode: "invalid_argument", retry: "RetryLater" });
8922
+ }
8923
+ if (hasServerErrorCode(detail.details, "DeploymentInvalidState")) {
8924
+ return fail5("discard_draft_failed", message, "The draft is no longer one that can be discarded: it moved on (it was promoted, or it is a rollback). Nothing was upgraded. Check the deployment with 'uip solution deploy list', then retry.", INVALID_TARGET);
8925
+ }
8926
+ return fail5("discard_draft_failed", message, "Nothing was upgraded. Retry, or delete the draft in Orchestrator and then retry the upgrade.", { errorCode: detail.errorCode, retry: detail.retry });
8927
+ }
8928
+ function classifyTerminalFailure(status) {
8929
+ return status === "ValidationFailed" || status === "ConflictFixingError" ? { errorCode: "invalid_argument", retry: "RetryWillNotFix" } : {};
8930
+ }
8931
+ async function describeUpgradeFailureAsync(basePath, accessToken, pipelineDeploymentId, finalStatus, scope, signal) {
8932
+ const fromPipeline = formatDeploymentFailureDetails(pipelineDeploymentId, finalStatus);
8933
+ const explained = finalStatus.validationResult?.validationErrors?.length || finalStatus.validationResult?.conflictErrors?.length || finalStatus.conflictFixingErrors?.length || finalStatus.deploymentScheduleErrors?.length || finalStatus.deploymentResult?.errorMessage;
8934
+ if (explained || !finalStatus.deploymentKey) {
8935
+ return fromPipeline;
8936
+ }
8937
+ const deploymentsApi = new DeploymentsApi(new Configuration({ basePath, accessToken }));
8938
+ const reason = await describeValidationFailureAsync(deploymentsApi, finalStatus.deploymentKey, SolutionDeploymentAction.Upgrade, scope, signal);
8939
+ return reason === NO_SERVER_REASON ? fromPipeline : `Validation: ${reason}
8940
+ ${fromPipeline}`;
8941
+ }
8585
8942
 
8586
8943
  // src/services/packages-list-service.ts
8587
- var fail6 = (reason, message, instructions, exitCode = 1) => ({
8944
+ var fail6 = (reason, message, instructions, extras = {}) => ({
8588
8945
  ok: false,
8589
8946
  reason,
8590
8947
  message,
8591
8948
  instructions,
8592
- exitCode
8949
+ exitCode: 1,
8950
+ ...extras
8593
8951
  });
8594
8952
  async function listPackagesAsync(options = {}) {
8595
8953
  const limit = options.limit ?? DEFAULT_PAGE_SIZE;
@@ -8610,9 +8968,10 @@ async function listPackagesAsync(options = {}) {
8610
8968
  envFilePath: options.envFilePath
8611
8969
  }));
8612
8970
  if (scopeError) {
8613
- return fail6("feed_resolution_failed", scopeError.message, feedResolutionFailureInstructions(options));
8971
+ const { message, instructions, ...extras } = await describeFeedResolutionFailureAsync(scopeError, options, auth);
8972
+ return fail6("feed_resolution_failed", message, instructions, extras);
8614
8973
  }
8615
- const searchApi = new SearchApi(new Configuration2({
8974
+ const searchApi = new SearchApi(new Configuration({
8616
8975
  basePath: auth.basePath,
8617
8976
  accessToken: auth.accessToken
8618
8977
  }));
@@ -8626,8 +8985,10 @@ async function listPackagesAsync(options = {}) {
8626
8985
  }
8627
8986
  }, feedScopeInitOverride(scope)));
8628
8987
  if (listError) {
8629
- const { message, details } = await extractErrorDetails(listError);
8630
- return fail6("list_request_failed", message, `Failed to list packages. Details: ${details}`);
8988
+ const { message, instructions, ...extras } = await describeSearchFailureAsync(listError, "packages", auth, {
8989
+ userSessionsOnly: true
8990
+ });
8991
+ return fail6("list_request_failed", message, instructions, extras);
8631
8992
  }
8632
8993
  const packages = result.values ?? [];
8633
8994
  return {
@@ -8641,4 +9002,4 @@ async function listPackagesAsync(options = {}) {
8641
9002
 
8642
9003
  export { extractDeploymentErrors, formatDeploymentErrorsAsText, activateDeploymentAsync, listDeploymentsAsync, deployRunAsync, uninstallDeploymentAsync, upgradeDeploymentAsync, listPackagesAsync };
8643
9004
 
8644
- //# debugId=C7F5318965ACD96664756E2164756E21
9005
+ //# debugId=3F2F38B7889B837064756E2164756E21