@uipath/solution-tool 1.204.0 → 1.205.0-preview.258

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 (122) hide show
  1. package/README.md +3 -3
  2. package/dist/THIRD-PARTY-NOTICES.md +2 -2
  3. package/dist/archive-extract-x35t9ktj.js +120 -0
  4. package/dist/cleanup-command-service-rc5e42hk.js +149 -0
  5. package/dist/deploy-delete-service-qdbtj3qd.js +25 -0
  6. package/dist/deploy-run-service-m9mg34n5.js +25 -0
  7. package/dist/{deploy-status-service-3req7hje.js → deploy-status-service-kz3nk43q.js} +41 -33
  8. package/dist/deploy.js +23 -17
  9. package/dist/{deployment-search-3mah6f0a.js → deployment-search-jvwyaqek.js} +16 -10
  10. package/dist/{embedded-file-reader-rf2g1kfj.js → embedded-file-reader-z4becc02.js} +2 -2
  11. package/dist/{feed-resolver-bxj9xkgr.js → feed-resolver-3xb7ayx4.js} +10 -9
  12. package/dist/feed-scope-failure-qps17dv3.js +19 -0
  13. package/dist/{feed-session-sxga31vn.js → feed-session-7xqphdrg.js} +3 -3
  14. package/dist/governance-options-amc1v319.js +32 -0
  15. package/dist/index-6wak6hg6.js +667 -0
  16. package/dist/index-prm27bvm.js +138 -0
  17. package/dist/index.js +27 -29
  18. package/dist/init.js +11 -10
  19. package/dist/{list-s5pkkefj.js → list-7jy11vb4.js} +10 -8
  20. package/dist/models/pack-command-types.d.ts +15 -0
  21. package/dist/node-p1scvsw8.js +86 -0
  22. package/dist/pack-command-service-8etwpgm9.js +35 -0
  23. package/dist/pack-project-hjvbvhgx.js +97 -0
  24. package/dist/pack.js +24 -14
  25. package/dist/package-id-c18sx4mz.js +13 -0
  26. package/dist/packager-tool-09fwj7e8.js +171 -0
  27. package/dist/{packager-tool-nfmygm61.js → packager-tool-361zx07h.js} +74 -23
  28. package/dist/packager-tool-37jmavap.js +42 -0
  29. package/dist/{packager-tool-px1j9kre.js → packager-tool-3bwnkjdq.js} +2 -2
  30. package/dist/{packager-tool-gb35b4qs.js → packager-tool-43ahv5mn.js} +192 -96
  31. package/dist/packager-tool-47wzvqw0.js +86 -0
  32. package/dist/{packager-tool-gfhdpkp3.js → packager-tool-4bq36bf5.js} +25 -4
  33. package/dist/packager-tool-4jekgh4s.js +517 -0
  34. package/dist/{packager-tool-zns6n8zp.js → packager-tool-5ddtwzva.js} +1 -1
  35. package/dist/packager-tool-5edvzz80.js +543 -0
  36. package/dist/packager-tool-5y09xhx5.js +1333 -0
  37. package/dist/packager-tool-60af1hw2.js +26 -0
  38. package/dist/{packager-tool-f5gq77m5.js → packager-tool-61pwb567.js} +2 -2
  39. package/dist/{packager-tool-kdwsw2x7.js → packager-tool-69jmxeh0.js} +196 -1519
  40. package/dist/{packager-tool-yrz4mwfn.js → packager-tool-6kz1pv9f.js} +1373 -486
  41. package/dist/{packager-tool-stsnbewn.js → packager-tool-6x35kvbx.js} +17 -8
  42. package/dist/packager-tool-6ybywxxn.js +1809 -0
  43. package/dist/{packager-tool-wfkv9v8b.js → packager-tool-85pen8t9.js} +2196 -3455
  44. package/dist/packager-tool-8ptbmmmp.js +2132 -0
  45. package/dist/{packager-tool-9ex75r32.js → packager-tool-8q4tvv8d.js} +6 -4
  46. package/dist/packager-tool-8rg0s4w9.js +109 -0
  47. package/dist/{packager-tool-7gr2nphb.js → packager-tool-97rcgwvv.js} +224 -6
  48. package/dist/packager-tool-act5r386.js +239 -0
  49. package/dist/packager-tool-amhyxzz6.js +125 -0
  50. package/dist/{packager-tool-j3520gxw.js → packager-tool-an5bpbwe.js} +3556 -1646
  51. package/dist/{packager-tool-e2rtr49j.js → packager-tool-b2nhscfk.js} +15 -31
  52. package/dist/packager-tool-bqw7edje.js +188 -0
  53. package/dist/{packager-tool-4gs8y6pr.js → packager-tool-dwxswyv4.js} +149 -53
  54. package/dist/{packager-tool-fehvgd51.js → packager-tool-e7vczxa8.js} +357 -1370
  55. package/dist/{packager-tool-v8bkkqdb.js → packager-tool-gmwva4yv.js} +5 -5
  56. package/dist/packager-tool-gs3f3tf7.js +103 -0
  57. package/dist/packager-tool-kkfb5kgn.js +182 -0
  58. package/dist/{packager-tool-ds45xby2.js → packager-tool-m74zqwmh.js} +104 -14
  59. package/dist/{packager-tool-fv7xyzg2.js → packager-tool-pbmqk39a.js} +126 -128
  60. package/dist/packager-tool-q5j6k3fy.js +1510 -0
  61. package/dist/packager-tool-qk21gk5m.js +445 -0
  62. package/dist/{packager-tool-bqre28m1.js → packager-tool-qw47eyw7.js} +1 -1
  63. package/dist/packager-tool-resolver-994z3jkj.js +23 -0
  64. package/dist/{packager-tool-gab9y5ct.js → packager-tool-t8gh825q.js} +37 -13
  65. package/dist/{packager-tool-kswwd3hp.js → packager-tool-tx834spe.js} +2376 -4077
  66. package/dist/{packager-tool-zf3929se.js → packager-tool-v1f5e8t9.js} +11 -1
  67. package/dist/{packager-tool-g8kjzrg7.js → packager-tool-wknfv4p5.js} +13 -11
  68. package/dist/{packager-tool-1rzssy0x.js → packager-tool-wy4fx250.js} +19 -18
  69. package/dist/packager-tool-y2k2f6yg.js +68 -0
  70. package/dist/packager-tool.js +2 -2
  71. package/dist/prepare-solution-resources-m1djfj62.js +29 -0
  72. package/dist/project-command-utils-t6pmpvq3.js +90 -0
  73. package/dist/project-contributions-79ndycrx.js +32 -0
  74. package/dist/publish.js +13 -12
  75. package/dist/resource-mutations-c3krjqze.js +36 -0
  76. package/dist/resource-version-lookup-0e3e2j58.js +99 -0
  77. package/dist/resource.js +10 -9
  78. package/dist/restore-command-service-eyx911f2.js +155 -0
  79. package/dist/run-service-p1vzzqe3.js +428 -0
  80. package/dist/services/deploy-activate-service.d.ts +10 -5
  81. package/dist/services/deploy-delete-service.d.ts +8 -4
  82. package/dist/services/deploy-run-service.d.ts +34 -1
  83. package/dist/services/deploy-uninstall-service.d.ts +21 -3
  84. package/dist/services/deploy-upgrade-service.d.ts +35 -1
  85. package/dist/services/deployment-feed-locator.d.ts +15 -4
  86. package/dist/services/deployment-search-client.d.ts +5 -53
  87. package/dist/services/deployment-search.d.ts +58 -3
  88. package/dist/services/deployment-state.d.ts +84 -24
  89. package/dist/services/deployment-validation.d.ts +5 -0
  90. package/dist/services/emit-runtime-dependencies.d.ts +2 -1
  91. package/dist/services/feed-resolver.d.ts +6 -4
  92. package/dist/services/feed-selection.d.ts +47 -0
  93. package/dist/services/http-status.d.ts +2 -7
  94. package/dist/services/pack-command-service.d.ts +9 -0
  95. package/dist/services/pack-project-selection.d.ts +51 -0
  96. package/dist/services/pack-project.d.ts +26 -0
  97. package/dist/services/pack-service.d.ts +7 -3
  98. package/dist/services/package-search-client.d.ts +7 -0
  99. package/dist/services/packager-tool-resolver.d.ts +7 -1
  100. package/dist/services/packages-list-service.d.ts +1 -1
  101. package/dist/services/permanent-deletion-consent.d.ts +110 -0
  102. package/dist/services/pipeline-deploy-poll.d.ts +7 -0
  103. package/dist/services/prepare-solution-resources.d.ts +12 -2
  104. package/dist/services/project-contributions.d.ts +5 -4
  105. package/dist/services/search-failure.d.ts +9 -13
  106. package/dist/services/solution-packager-helpers.d.ts +5 -2
  107. package/dist/services/solution-path.d.ts +1 -1
  108. package/dist/services/uninstall-consent.d.ts +82 -0
  109. package/dist/ship-telemetry-myes7ak5.js +21 -0
  110. package/dist/solution-auth-aead70sw.js +11 -0
  111. package/dist/templates/AGENTS.md +125 -12
  112. package/dist/tool.js +27 -29
  113. package/dist/utils/deployment-errors.d.ts +9 -1
  114. package/package.json +2 -2
  115. package/dist/deploy-delete-service-1zydyyhj.js +0 -22
  116. package/dist/feed-scope-failure-2kndwpf6.js +0 -18
  117. package/dist/packager-tool-f2e9rzxk.js +0 -104
  118. package/dist/packager-tool-resolver-6zwhec62.js +0 -20
  119. package/dist/packager-tool-zdfn32bc.js +0 -4465
  120. package/dist/prepare-solution-resources-jpb5as2s.js +0 -25
  121. package/dist/project-contributions-6wpff2hm.js +0 -30
  122. package/dist/services/legacy-deployment-search-v3.d.ts +0 -23
@@ -0,0 +1,1510 @@
1
+ import {
2
+ resolveFeedSelectionAsync
3
+ } from "./packager-tool-y2k2f6yg.js";
4
+ import {
5
+ INCOMPLETE_SWEEP_ADVICE,
6
+ findDeploymentAcrossFeeds,
7
+ isNameOrFeedRefusal,
8
+ reportIncompleteSweepAsync,
9
+ retryAdvice
10
+ } from "./packager-tool-4bq36bf5.js";
11
+ import {
12
+ ACKNOWLEDGE_PERMANENT_DELETION_FLAG,
13
+ NO_SERVER_REASON,
14
+ acknowledgedButNotRunHint,
15
+ activateDeployment,
16
+ describeValidationFailureAsync,
17
+ formatPermanentDeletionResources,
18
+ isPermanentDeletionRefusal,
19
+ listPermanentDeletionResourcesAsync,
20
+ pollPipelineDeploymentAsync,
21
+ remainingTimeoutMs,
22
+ resolveParentFolder,
23
+ settlePermanentDeletionRefusalAsync,
24
+ stillRequiresConfirmation
25
+ } from "./packager-tool-qk21gk5m.js";
26
+ import {
27
+ deploymentNextSteps,
28
+ explainRefusal,
29
+ isCurrentRecordAsync,
30
+ needsPendingResources,
31
+ pendingResourceApis,
32
+ readPendingResourcesAsync,
33
+ reportedActivationStatus
34
+ } from "./packager-tool-m74zqwmh.js";
35
+ import {
36
+ DRAFT_VERSION_MISMATCH_CODE,
37
+ coerceWorkflowErrorMessages,
38
+ explainPipelineStatusAfterInstall,
39
+ formatDeploymentFailureDetails,
40
+ formatServiceErrors,
41
+ hasServerErrorCode,
42
+ installLandedDespiteStatus,
43
+ isBareResponse,
44
+ leavesPendingDraft
45
+ } from "./packager-tool-act5r386.js";
46
+ import {
47
+ DeploymentSearchNeedsUserSessionError,
48
+ findDeploymentByName,
49
+ findDeploymentsByName,
50
+ isApplicationSession,
51
+ isAuthRefusal,
52
+ isCompletedUninstall,
53
+ isMissingPipelinesRoute,
54
+ newestDeploymentRow,
55
+ parseSearchPage,
56
+ readDeploymentByKeyAsync,
57
+ searchDeploymentsPage
58
+ } from "./packager-tool-dwxswyv4.js";
59
+ import {
60
+ describeFeedResolutionFailureAsync,
61
+ feedScopeInitOverride,
62
+ feedSelectorFlag,
63
+ folderKeyInitOverride,
64
+ resolveFeedScope,
65
+ resolvePersonalWorkspace
66
+ } from "./packager-tool-6x35kvbx.js";
67
+ import {
68
+ Configuration,
69
+ DeploymentOperation,
70
+ DeploymentStatus,
71
+ PipelinesApi
72
+ } from "./packager-tool-97rcgwvv.js";
73
+ import {
74
+ Configuration as Configuration2,
75
+ DeploymentsApi,
76
+ PackageInfoFromJSON,
77
+ SearchApi,
78
+ SolutionDeploymentAction,
79
+ WorkflowAction
80
+ } from "./packager-tool-an5bpbwe.js";
81
+ import {
82
+ classifyFeedSession,
83
+ feedReadRefusalForApplication,
84
+ feedSessionRefusal
85
+ } from "./packager-tool-qw47eyw7.js";
86
+ import {
87
+ getSolutionAuthContext
88
+ } from "./packager-tool-61pwb567.js";
89
+ import {
90
+ DEFAULT_PAGE_SIZE,
91
+ PollOutcome,
92
+ logger,
93
+ mapPollFailure,
94
+ pollUntil,
95
+ quoteForShell
96
+ } from "./packager-tool-tx834spe.js";
97
+ import {
98
+ catchError,
99
+ extractErrorDetails
100
+ } from "./packager-tool-6ybywxxn.js";
101
+
102
+ // src/services/deploy-activate-service.ts
103
+ var fail = (reason, message, instructions, exitCode = 1) => ({
104
+ ok: false,
105
+ reason,
106
+ message,
107
+ instructions,
108
+ exitCode
109
+ });
110
+ 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>'.";
111
+ async function incompleteSweepRefusalAsync(deploymentName, skipped, result) {
112
+ const details = `Details: ${result.details}`;
113
+ if (!isNameOrFeedRefusal(result.errorCode, result.httpStatus)) {
114
+ return {
115
+ ...fail("request_failed", result.message, `The activation request failed. ${retryAdvice(result.errorCode, result.retry, "Orchestrator")} ${details}`),
116
+ errorCode: result.errorCode,
117
+ retry: result.retry
118
+ };
119
+ }
120
+ const report = await reportIncompleteSweepAsync(deploymentName, skipped);
121
+ return {
122
+ ...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})`),
123
+ errorCode: report.errorCode,
124
+ retry: report.retry
125
+ };
126
+ }
127
+ async function activateDeploymentAsync(deploymentName, options = {}) {
128
+ const [authError, auth] = await catchError(getSolutionAuthContext({
129
+ tenant: options.tenant,
130
+ loginValidity: options.loginValidity,
131
+ envFilePath: options.envFilePath
132
+ }));
133
+ if (authError) {
134
+ return fail("auth_failed", authError.message, AUTH_INSTRUCTIONS);
135
+ }
136
+ const selection = await resolveFeedSelectionAsync(auth, options, "Activating a deployment");
137
+ if (!selection.ok) {
138
+ return {
139
+ ...fail(selection.reason, selection.message, selection.instructions),
140
+ errorCode: selection.errorCode,
141
+ retry: selection.retry
142
+ };
143
+ }
144
+ const { selectedFeed } = selection;
145
+ const sweep = await findDeploymentAcrossFeeds(auth, deploymentName, {
146
+ tenant: options.tenant,
147
+ loginValidity: options.loginValidity,
148
+ envFilePath: options.envFilePath,
149
+ selectedFeed
150
+ });
151
+ const location = sweep.location;
152
+ const scope = location?.scope ?? (selectedFeed ? feedScopeInitOverride(selectedFeed) : undefined);
153
+ const result = await activateDeployment(auth, deploymentName, {
154
+ timeoutSeconds: options.timeout ?? 360,
155
+ pollIntervalMs: options.pollInterval ?? 5000,
156
+ signal: options.signal,
157
+ scope
158
+ });
159
+ if (result.kind === "RequestFailed") {
160
+ const details = `Details: ${result.details}`;
161
+ const known = result.deployment ?? location?.deployment;
162
+ if (!known && !sweep.complete) {
163
+ return incompleteSweepRefusalAsync(deploymentName, sweep.skipped, result);
164
+ }
165
+ const pendingResources = known && needsPendingResources(known) ? await readPendingResourcesAsync(pendingResourceApis(auth), known, scope) : undefined;
166
+ const feedSelector = feedSelectorFlag(location?.feed ?? selectedFeed);
167
+ const explanation = known ? explainRefusal(known, pendingResources, feedSelector) : undefined;
168
+ const instructions = explanation ? [...explanation, details].join(`
169
+ `) : 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}` : selectedFeed ? `No deployment named '${deploymentName}' was found in the selected feed. Check the name with 'uip solution deploy list${feedSelector}', or drop the feed flag to search every feed you can reach. ${details}` : result.phase === "preActivateSteps" ? `Check deployment name. Deploy first with: uip solution deploy run. ${details}` : `Check deployment name matches an existing deployment. ${details}`;
170
+ return fail("request_failed", result.message, instructions);
171
+ }
172
+ if (result.kind === "NoInstance") {
173
+ return {
174
+ ok: true,
175
+ status: "Activation completed (no instance to poll)",
176
+ deploymentName
177
+ };
178
+ }
179
+ if (result.kind === "PollFailed") {
180
+ const { reason, message, exitCode } = mapPollFailure(result.poll, "Activation");
181
+ return fail(reason, message, "Activation may still be in progress.", exitCode);
182
+ }
183
+ if (result.kind === "ActivationFailed") {
184
+ return fail("activation_failed", `Activation failed with status: ${result.status}`, result.errorMessage);
185
+ }
186
+ return {
187
+ ok: true,
188
+ status: result.status,
189
+ deploymentName,
190
+ instanceId: result.instanceId
191
+ };
192
+ }
193
+
194
+ // src/services/search-failure.ts
195
+ async function describeSearchFailureAsync(error, what) {
196
+ const details = await extractErrorDetails(error);
197
+ return {
198
+ message: `Failed to list ${what}: ${details.message}`,
199
+ instructions: searchFailureInstructions(details.errorCode, details.retry),
200
+ errorCode: details.errorCode,
201
+ retry: details.retry,
202
+ context: details.context
203
+ };
204
+ }
205
+ function searchFailureInstructions(errorCode, retry) {
206
+ if (errorCode === "authentication_required") {
207
+ return "Run 'uip login' to refresh the session, then run the command again.";
208
+ }
209
+ if (errorCode === "permission_denied") {
210
+ return "Use an account that can read Automation Solutions in this tenant.";
211
+ }
212
+ if (retry === "RetryLater") {
213
+ return "Wait a moment, then run the command again.";
214
+ }
215
+ return "Check the options and the session ('uip login status --detailed'), then run the command again.";
216
+ }
217
+
218
+ // src/services/deploy-list-service.ts
219
+ var fail2 = (reason, message, instructions, extras = {}) => ({
220
+ ok: false,
221
+ reason,
222
+ message,
223
+ instructions,
224
+ exitCode: 1,
225
+ ...extras
226
+ });
227
+ var refuseFeedRead = (refusal) => fail2("feed_requires_user_session", refusal.message, refusal.instructions, {
228
+ errorCode: "permission_denied",
229
+ retry: "RetryWillNotFix"
230
+ });
231
+ async function listDeploymentsAsync(options = {}) {
232
+ const limit = options.limit ?? DEFAULT_PAGE_SIZE;
233
+ const offset = options.offset ?? 0;
234
+ const [authError, auth] = await catchError(getSolutionAuthContext({
235
+ tenant: options.tenant,
236
+ loginValidity: options.loginValidity,
237
+ envFilePath: options.envFilePath
238
+ }));
239
+ if (authError) {
240
+ return fail2("auth_failed", authError.message, authError.message);
241
+ }
242
+ if (options.personalWorkspace) {
243
+ const refusal = feedReadRefusalForApplication(auth, "Listing the Personal Workspace's deployments");
244
+ if (refusal) {
245
+ return refuseFeedRead(refusal);
246
+ }
247
+ }
248
+ const [folderError, folderPathFilter] = await catchError(resolveParentFolder({
249
+ folderPath: options.folderPath,
250
+ folderKey: options.folderKey,
251
+ tenant: options.tenant,
252
+ loginValidity: options.loginValidity,
253
+ envFilePath: options.envFilePath
254
+ }));
255
+ if (folderError) {
256
+ return fail2("folder_resolution_failed", folderError.message, "Provide a valid --folder-path or --folder-key to filter by folder.");
257
+ }
258
+ const [scopeError, scope] = await catchError(resolveFeedScope({
259
+ personalWorkspace: options.personalWorkspace,
260
+ feed: options.feed,
261
+ tenant: options.tenant,
262
+ loginValidity: options.loginValidity,
263
+ envFilePath: options.envFilePath
264
+ }));
265
+ if (scopeError) {
266
+ const { message, instructions, ...extras } = await describeFeedResolutionFailureAsync(scopeError, options, auth);
267
+ return fail2("feed_resolution_failed", message, instructions, extras);
268
+ }
269
+ if (scope.kind !== "tenant") {
270
+ const refusal = feedReadRefusalForApplication(auth, `Listing the deployments of the feed '${scope.name ?? scope.folderKey}'`);
271
+ if (refusal) {
272
+ return refuseFeedRead(refusal);
273
+ }
274
+ }
275
+ const [listError, result] = await catchError(searchDeploymentsPage(auth, {
276
+ take: limit,
277
+ skip: offset,
278
+ orderByColumn: options.sortBy ?? "startTime",
279
+ orderByDirection: options.sortOrder ?? "Descending",
280
+ operationStatuses: options.operationStatuses ?? [],
281
+ activationStatuses: options.activationStatuses ?? [],
282
+ ...folderPathFilter ? { targetFolderName: folderPathFilter } : {}
283
+ }, feedScopeInitOverride(scope)));
284
+ if (listError instanceof DeploymentSearchNeedsUserSessionError) {
285
+ 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" });
286
+ }
287
+ if (listError) {
288
+ const { message, instructions, ...extras } = await describeSearchFailureAsync(listError, "deployments");
289
+ return fail2("list_request_failed", message, instructions, extras);
290
+ }
291
+ const values = result.values ?? [];
292
+ const deployments = values.map((d) => ({
293
+ key: d.key,
294
+ installDeploymentKey: d.installDeploymentKey,
295
+ name: d.name,
296
+ packageName: d.packageName,
297
+ packageVersion: d.packageVersion,
298
+ currentPackageVersion: d.currentPackageVersion,
299
+ targetPackageVersion: d.targetPackageVersion,
300
+ newPackageVersionAvailable: d.newPackageVersionAvailable,
301
+ operation: d.operation,
302
+ operationStatus: d.operationStatus,
303
+ activationStatus: d.activationStatus,
304
+ folderPath: d.folderPath,
305
+ installedRootFolderKey: d.installedRootFolderKey,
306
+ deploymentCreationTime: d.deploymentCreationTime,
307
+ actions: d.actions ?? []
308
+ }));
309
+ return { ok: true, deployments, limit, offset, total: result.count };
310
+ }
311
+
312
+ // src/services/uninstall-consent.ts
313
+ var JOBS_NOT_IN_FINAL_STATE_CODE = "2824";
314
+ var UNINSTALL_CONFIRMATION_REQUIRED_CODE = "4026";
315
+ function isJobsNotInFinalState(row) {
316
+ return row.errorCode === JOBS_NOT_IN_FINAL_STATE_CODE;
317
+ }
318
+ function isRequiresConfirmation(row) {
319
+ return row.errorKind === "RequiresConfirmation";
320
+ }
321
+ function isForceable(row) {
322
+ return isJobsNotInFinalState(row) || isRequiresConfirmation(row);
323
+ }
324
+ function uninstallConsentFacts(rows) {
325
+ return {
326
+ jobsNotInFinalState: rows.some(isJobsNotInFinalState),
327
+ requiresConfirmation: rows.some(isRequiresConfirmation),
328
+ permanentDeletionResources: selectPermanentDeletionResources(rows)
329
+ };
330
+ }
331
+ function selectPermanentDeletionResources(rows) {
332
+ const byKey = new Map;
333
+ for (const row of rows) {
334
+ if (row.errorKind !== "RequiresConfirmation" || row.level !== "Warning") {
335
+ continue;
336
+ }
337
+ const identity = row.key ?? `${row.kind ?? ""}/${row.name ?? ""}`;
338
+ if (byKey.has(identity)) {
339
+ continue;
340
+ }
341
+ byKey.set(identity, {
342
+ key: row.key ?? undefined,
343
+ kind: row.kind ?? undefined,
344
+ name: row.name ?? undefined
345
+ });
346
+ }
347
+ return [...byKey.values()];
348
+ }
349
+ function isBlocking(row) {
350
+ return !isForceable(row) && row.level !== "Warning" && row.level !== "Info";
351
+ }
352
+ function describeBlockingRows(rows) {
353
+ return rows.filter(isBlocking).map((row) => {
354
+ const subject = row.name ? `${row.kind ? `${row.kind} ` : ""}'${row.name}': ` : "";
355
+ const code = row.errorCode ? `[${row.errorCode}] ` : "";
356
+ const text = row.text || row.errorKind || row.errorType;
357
+ return `${subject}${code}${text}`;
358
+ }).join("; ");
359
+ }
360
+ function classifyUninstallValidation(result) {
361
+ const rows = result.validationErrors ?? [];
362
+ let outcome = "clear";
363
+ if (rows.some(isBlocking)) {
364
+ outcome = "blocked";
365
+ } else if (rows.some(isForceable)) {
366
+ outcome = "force-required";
367
+ }
368
+ return { outcome, ...uninstallConsentFacts(rows) };
369
+ }
370
+ async function readUninstallValidationAsync(api, deploymentName, deploymentKey, scope) {
371
+ if (!deploymentKey) {
372
+ return;
373
+ }
374
+ const [readError, result] = await catchError(() => api.pipelinesGetDeploymentValidationResult({ deploymentName, deploymentAction: "Uninstall" }, scope));
375
+ if (readError || !result || result.deploymentAction !== "Uninstall") {
376
+ return;
377
+ }
378
+ if (!await isCurrentRecordAsync(api, deploymentKey)) {
379
+ return;
380
+ }
381
+ return result;
382
+ }
383
+ function servicesReportJobsNotInFinalState(serviceLines) {
384
+ return serviceLines.some((line) => line.includes(`[${JOBS_NOT_IN_FINAL_STATE_CODE}]`));
385
+ }
386
+ function formatPermanentDeletionResource(resource) {
387
+ const label = resource.name ?? resource.key ?? "unnamed resource";
388
+ return resource.kind ? `${resource.kind} '${label}'` : `'${label}'`;
389
+ }
390
+ function describePermanentDeletion(resources) {
391
+ return resources.length > 0 ? `uninstalling it permanently deletes these resources and their data: ${resources.map(formatPermanentDeletionResource).join(", ")}` : "uninstalling it permanently deletes resources and their data";
392
+ }
393
+ function describeForceableCauses(facts) {
394
+ const causes = [];
395
+ if (facts.jobsNotInFinalState) {
396
+ causes.push("this deployment has uncompleted jobs; --force uninstalls anyway");
397
+ }
398
+ if (facts.requiresConfirmation) {
399
+ causes.push(describePermanentDeletion(facts.permanentDeletionResources));
400
+ }
401
+ return causes.join("; and ");
402
+ }
403
+ function consentData(facts, extra) {
404
+ return {
405
+ DeploymentName: extra.deploymentName,
406
+ ...extra.instanceId ? { InstanceId: extra.instanceId } : {},
407
+ JobsNotInFinalState: facts.jobsNotInFinalState,
408
+ PermanentDeletionResources: facts.permanentDeletionResources.map((resource) => ({
409
+ Key: resource.key ?? null,
410
+ Kind: resource.kind ?? null,
411
+ Name: resource.name ?? null
412
+ }))
413
+ };
414
+ }
415
+
416
+ // src/services/deploy-uninstall-service.ts
417
+ var TERMINAL_STATUSES = new Set([
418
+ "SuccessfulUninstall",
419
+ "FailedUninstall"
420
+ ]);
421
+ var fail3 = (reason, message, instructions, extras = {}) => ({
422
+ ok: false,
423
+ reason,
424
+ message,
425
+ instructions,
426
+ exitCode: extras.exitCode ?? 1,
427
+ errorCode: extras.errorCode,
428
+ retry: extras.retry,
429
+ ...extras.data ? { data: extras.data } : {}
430
+ });
431
+ function forceRetryCommand(deploymentName, feedSelector) {
432
+ return `uip solution deploy uninstall ${quoteForShell(deploymentName)}${feedSelector} --force`;
433
+ }
434
+ function consentFailure(message, instructions, facts, ids) {
435
+ return fail3("uninstall_confirmation_required", message, instructions, {
436
+ errorCode: "invalid_argument",
437
+ retry: "RetryWillNotFix",
438
+ data: consentData(facts, ids)
439
+ });
440
+ }
441
+ function blockedUninstallFailure(deploymentName, feedSelector, rows) {
442
+ return fail3("uninstall_failed", `Deployment '${deploymentName}' was not uninstalled: its Uninstall validation reports errors that --force cannot get past: ${describeBlockingRows(rows)}.`, `Resolve the reason above in Orchestrator, then run the uninstall again: uip solution deploy uninstall ${quoteForShell(deploymentName)}${feedSelector} --yes. It then asks again to confirm the permanent deletion of the resources that were replaced by links.`, { errorCode: "invalid_argument", retry: "RetryLater" });
443
+ }
444
+ async function confirmationRequiredFailureAsync(api, deploymentName, ctx) {
445
+ const { feedSelector, details } = ctx;
446
+ const validation = await readUninstallValidationAsync(api, deploymentName, ctx.deploymentKey, ctx.scope);
447
+ if (validation && classifyUninstallValidation(validation).outcome === "blocked") {
448
+ return blockedUninstallFailure(deploymentName, feedSelector, validation.validationErrors ?? []);
449
+ }
450
+ const facts = {
451
+ ...uninstallConsentFacts(validation?.validationErrors ?? []),
452
+ requiresConfirmation: true
453
+ };
454
+ const causes = describeForceableCauses(facts);
455
+ return consentFailure(`Deployment '${deploymentName}' was not uninstalled: ${causes}.`, `Re-run with --force to confirm: ${forceRetryCommand(deploymentName, feedSelector)}. Details: ${details}`, facts, { deploymentName });
456
+ }
457
+ async function forceableUninstallFailureAsync(api, deploymentName, ctx) {
458
+ const validation = await readUninstallValidationAsync(api, deploymentName, ctx.deploymentKey, ctx.scope);
459
+ let facts;
460
+ if (validation) {
461
+ const classification = classifyUninstallValidation(validation);
462
+ if (classification.outcome !== "force-required") {
463
+ return;
464
+ }
465
+ facts = classification;
466
+ } else if (servicesReportJobsNotInFinalState(ctx.serviceLines)) {
467
+ facts = {
468
+ jobsNotInFinalState: true,
469
+ requiresConfirmation: false,
470
+ permanentDeletionResources: []
471
+ };
472
+ } else {
473
+ return;
474
+ }
475
+ const causes = describeForceableCauses(facts);
476
+ const retry = forceRetryCommand(deploymentName, ctx.feedSelector);
477
+ const waitForJobs = facts.jobsNotInFinalState ? " Or let the jobs finish (or stop them) in Orchestrator, then run the uninstall again without --force." : "";
478
+ const reported = ctx.errors ? `${ctx.errors}
479
+ ` : "";
480
+ return consentFailure(`Uninstall failed with status: FailedUninstall: ${causes}.`, `${reported}Re-run with --force to uninstall anyway: ${retry}.${waitForJobs}`, facts, { deploymentName, instanceId: ctx.instanceId });
481
+ }
482
+ function requestErrorInstructions(deploymentName, errorCode, details, feedSelector = "") {
483
+ switch (errorCode) {
484
+ case "authentication_required":
485
+ return `Your session may have expired. Run 'uip login' and retry. Details: ${details}`;
486
+ case "permission_denied":
487
+ return `Your account lacks permission to uninstall this deployment. Check your Orchestrator roles, then retry. Details: ${details}`;
488
+ case "rate_limited":
489
+ return `Too many requests. Wait a moment and retry. Details: ${details}`;
490
+ case "network_error":
491
+ return `Could not reach Orchestrator. Check your connection and retry. Details: ${details}`;
492
+ case "server_error":
493
+ return `Orchestrator returned a server error. Wait and retry; if it persists, check Orchestrator status. Details: ${details}`;
494
+ case "not_found":
495
+ return `No deployment named '${deploymentName}' was found. Run 'uip solution deploy list${feedSelector}' to see valid deployment names. Details: ${details}`;
496
+ default:
497
+ return `Uninstall request failed. Run 'uip solution deploy list${feedSelector}' to check the deployment's state, then retry. Details: ${details}`;
498
+ }
499
+ }
500
+ function buildDeploymentStateInstructions(deployment, details, feedSelector = "") {
501
+ const availableActions = deployment.actions?.length ? deployment.actions.join(", ") : "none";
502
+ const status = `(ActivationStatus: ${deployment.activationStatus}, OperationStatus: ${deployment.operationStatus})`;
503
+ if (deployment.operationStatus === "InProgress") {
504
+ return `Deployment '${deployment.name}' has an operation in progress ${status}, so it can't be uninstalled yet. Wait for it to finish — run 'uip solution deploy list${feedSelector}' to recheck — then retry. Available actions: ${availableActions}. Details: ${details}`;
505
+ }
506
+ if (deployment.actions?.includes("Uninstall")) {
507
+ return `Deployment '${deployment.name}' lists uninstall as available but Orchestrator rejected the request ${status}. Run 'uip solution deploy list${feedSelector}' to recheck its state, then retry. If it stays stuck, uninstall it from the Orchestrator Solutions UI. Details: ${details}`;
508
+ }
509
+ const removalAdvice = deployment.actions?.includes(WorkflowAction.Delete) ? `Delete it with: uip solution deploy delete ${quoteForShell(deployment.name)}${feedSelector} --yes.` : "Remove it from the Orchestrator Solutions UI.";
510
+ if (deployment.activationStatus === "Inactive") {
511
+ 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}`;
512
+ }
513
+ return `Deployment '${deployment.name}' cannot be uninstalled in its current state ${status}. Available actions: ${availableActions}. Run 'uip solution deploy list${feedSelector}' to recheck its state. ${removalAdvice} Details: ${details}`;
514
+ }
515
+ async function waitForOperationToFinishAsync(auth, deploymentName, opts) {
516
+ let last;
517
+ let byKey = opts.deploymentKey !== undefined;
518
+ let solutionsOnly = false;
519
+ let operationStatus;
520
+ const readState = async () => {
521
+ if (byKey && opts.deploymentKey) {
522
+ const info = await readDeploymentByKeyAsync(auth, opts.deploymentKey, { scope: opts.scope, solutionsOnly });
523
+ if (info) {
524
+ solutionsOnly = info.source === "solutions";
525
+ operationStatus = info.operationStatus;
526
+ return operationStatus;
527
+ }
528
+ byKey = false;
529
+ }
530
+ last = await findDeploymentByName(auth, deploymentName, opts.scope);
531
+ operationStatus = last?.operationStatus;
532
+ return last ? operationStatus : undefined;
533
+ };
534
+ const poll = await pollUntil({
535
+ fn: readState,
536
+ until: (status) => status !== undefined && status !== "InProgress",
537
+ getStatus: (status) => status ?? "Unknown",
538
+ label: `deployment '${deploymentName}' to finish its current operation`,
539
+ logPrefix: "uninstall",
540
+ timeoutMs: opts.remainingMs(),
541
+ intervalMs: opts.intervalMs,
542
+ signal: opts.signal
543
+ });
544
+ if (byKey) {
545
+ const [, row] = await catchError(findDeploymentByName(auth, deploymentName, opts.scope));
546
+ const held = row ?? opts.known;
547
+ last = row ?? (held && operationStatus ? { ...held, operationStatus } : held);
548
+ }
549
+ return { outcome: poll.outcome, state: last, error: poll.error };
550
+ }
551
+ async function requestUninstallAsync(api, auth, deploymentName, ctx) {
552
+ const { options, sweep, scope, intervalMs, remainingMs } = ctx;
553
+ const { location } = sweep;
554
+ const sweepIncomplete = !location && !sweep.complete;
555
+ const feedSelector = feedSelectorFlag(location?.feed ?? ctx.selectedFeed);
556
+ let state = location?.deployment;
557
+ let waited = false;
558
+ let rechecked = false;
559
+ for (;; ) {
560
+ const [uninstallError, result] = await catchError(api.pipelinesUninstall(options.force ? { deploymentName, forceUninstall: true } : { deploymentName }, scope));
561
+ if (!uninstallError) {
562
+ return { ok: true, result };
563
+ }
564
+ const { message, details, errorCode, retry, context } = await extractErrorDetails(uninstallError);
565
+ if (!options.force && hasServerErrorCode(details, UNINSTALL_CONFIRMATION_REQUIRED_CODE)) {
566
+ return confirmationRequiredFailureAsync(api, deploymentName, {
567
+ deploymentKey: state?.key ?? location?.deployment.key,
568
+ scope,
569
+ feedSelector,
570
+ details
571
+ });
572
+ }
573
+ if (errorCode !== "invalid_argument") {
574
+ if (errorCode === "not_found" && sweepIncomplete) {
575
+ return incompleteSweepFailureAsync(deploymentName, sweep, details);
576
+ }
577
+ return fail3("uninstall_request_failed", message, requestErrorInstructions(deploymentName, errorCode, details, feedSelector), { errorCode, retry });
578
+ }
579
+ if (!rechecked && !waited && state?.operationStatus !== "InProgress") {
580
+ rechecked = true;
581
+ const [readError, fresh] = await catchError(findDeploymentByName(auth, deploymentName, scope));
582
+ if (!readError) {
583
+ state = fresh;
584
+ }
585
+ }
586
+ if (!waited && state?.operationStatus === "InProgress") {
587
+ waited = true;
588
+ const wait = await waitForOperationToFinishAsync(auth, deploymentName, {
589
+ scope,
590
+ deploymentKey: state?.key ?? location?.deployment.key,
591
+ known: state,
592
+ intervalMs,
593
+ remainingMs,
594
+ signal: options.signal
595
+ });
596
+ if (wait.outcome === PollOutcome.Timeout) {
597
+ return fail3("uninstall_blocked", `Deployment '${deploymentName}' still had an operation running after ${options.timeout ?? 360}s, so it could not be uninstalled.`, buildDeploymentStateInstructions(wait.state ?? state, details, feedSelector), { exitCode: 2, errorCode: "timeout", retry: "RetryLater" });
598
+ }
599
+ if (wait.outcome !== PollOutcome.Completed) {
600
+ const {
601
+ reason,
602
+ message: pollMessage,
603
+ exitCode,
604
+ errorCode: pollErrorCode,
605
+ retry: pollRetry
606
+ } = mapPollFailure(wait, "Deployment");
607
+ return fail3(reason, pollMessage, `The deployment may still be installing. Check it with: uip solution deploy list${feedSelector}, then retry the uninstall.`, { exitCode, errorCode: pollErrorCode, retry: pollRetry });
608
+ }
609
+ state = wait.state;
610
+ continue;
611
+ }
612
+ if (sweepIncomplete && (!state || isCompletedUninstall(state))) {
613
+ if (isNameOrFeedRefusal(errorCode, context?.httpStatus)) {
614
+ return incompleteSweepFailureAsync(deploymentName, sweep, details);
615
+ }
616
+ return fail3("uninstall_request_failed", message, requestErrorInstructions(deploymentName, errorCode, details, feedSelector), { errorCode, retry });
617
+ }
618
+ const instructions = state ? buildDeploymentStateInstructions(state, details, feedSelector) : ctx.selectedFeed ? `No deployment named '${deploymentName}' was found in the selected feed. Check the name with 'uip solution deploy list${feedSelector}', or drop the feed flag to search every feed you can reach. Details: ${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}`;
619
+ return fail3("uninstall_request_failed", message, instructions, {
620
+ errorCode,
621
+ retry
622
+ });
623
+ }
624
+ }
625
+ async function incompleteSweepFailureAsync(deploymentName, sweep, refusalDetails) {
626
+ const report = await reportIncompleteSweepAsync(deploymentName, sweep.skipped);
627
+ return fail3("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 });
628
+ }
629
+ async function uninstallDeploymentAsync(deploymentName, options = {}) {
630
+ const [authError, auth] = await catchError(getSolutionAuthContext({
631
+ tenant: options.tenant,
632
+ loginValidity: options.loginValidity,
633
+ envFilePath: options.envFilePath
634
+ }));
635
+ if (authError) {
636
+ return fail3("auth_failed", authError.message, authError.message);
637
+ }
638
+ const api = new PipelinesApi(new Configuration({
639
+ basePath: auth.basePath,
640
+ accessToken: auth.accessToken
641
+ }));
642
+ const deadline = Date.now() + (options.timeout ?? 360) * 1000;
643
+ const intervalMs = options.pollInterval ?? 5000;
644
+ const remainingMs = () => Math.max(deadline - Date.now(), 1);
645
+ const selection = await resolveFeedSelectionAsync(auth, options, "Uninstalling a deployment");
646
+ if (!selection.ok) {
647
+ return fail3(selection.reason, selection.message, selection.instructions, {
648
+ errorCode: selection.errorCode,
649
+ retry: selection.retry
650
+ });
651
+ }
652
+ const { selectedFeed } = selection;
653
+ const sweep = await findDeploymentAcrossFeeds(auth, deploymentName, {
654
+ ...options,
655
+ selectedFeed
656
+ });
657
+ const location = sweep.location;
658
+ const scope = location?.scope ?? (selectedFeed ? feedScopeInitOverride(selectedFeed) : undefined);
659
+ const [requestError, requested] = await catchError(requestUninstallAsync(api, auth, deploymentName, {
660
+ options,
661
+ sweep,
662
+ scope,
663
+ selectedFeed,
664
+ intervalMs,
665
+ remainingMs
666
+ }));
667
+ if (requestError) {
668
+ throw requestError;
669
+ }
670
+ if (!requested.ok) {
671
+ return requested;
672
+ }
673
+ const uninstallResult = requested.result;
674
+ if (uninstallResult.complete) {
675
+ return {
676
+ ok: true,
677
+ status: "Uninstall completed immediately",
678
+ deploymentName
679
+ };
680
+ }
681
+ const instanceId = uninstallResult.instanceId;
682
+ if (!instanceId) {
683
+ return {
684
+ ok: true,
685
+ status: "Uninstall scheduled",
686
+ deploymentName,
687
+ scheduled: uninstallResult.scheduled
688
+ };
689
+ }
690
+ const pollResult = await pollUntil({
691
+ fn: () => api.pipelinesGetDeploymentInstanceStatus({ instanceId }, scope),
692
+ until: (result) => TERMINAL_STATUSES.has(result.status),
693
+ getStatus: (result) => result.status,
694
+ label: `uninstall ${deploymentName}`,
695
+ logPrefix: "uninstall",
696
+ timeoutMs: remainingMs(),
697
+ intervalMs,
698
+ signal: options.signal
699
+ });
700
+ if (pollResult.outcome !== PollOutcome.Completed || !pollResult.data) {
701
+ const { reason, message, exitCode, errorCode, retry } = mapPollFailure(pollResult, "Uninstall");
702
+ return fail3(reason, message, "Uninstall may still be in progress.", {
703
+ exitCode,
704
+ errorCode,
705
+ retry
706
+ });
707
+ }
708
+ const finalStatus = pollResult.data;
709
+ if (finalStatus.status !== "SuccessfulUninstall") {
710
+ const workflowErrors = coerceWorkflowErrorMessages(finalStatus.errorMessage).map((e) => e.errorMessage).join(", ");
711
+ const serviceLines = formatServiceErrors(finalStatus.services);
712
+ const errors = [workflowErrors, ...serviceLines].filter(Boolean).join("; ");
713
+ const feedFlag = feedSelectorFlag(location?.feed ?? selectedFeed);
714
+ if (!options.force) {
715
+ const forceable = await forceableUninstallFailureAsync(api, deploymentName, {
716
+ deploymentKey: finalStatus.deploymentKey,
717
+ scope,
718
+ feedSelector: feedFlag,
719
+ instanceId,
720
+ serviceLines,
721
+ errors
722
+ });
723
+ if (forceable) {
724
+ return forceable;
725
+ }
726
+ }
727
+ const consentFlag = options.force ? "--force" : "--yes";
728
+ const classification = {
729
+ errorCode: errors ? "invalid_argument" : "unknown_error",
730
+ retry: "RetryLater"
731
+ };
732
+ return fail3("uninstall_failed", `Uninstall failed with status: ${finalStatus.status}`, errors ? `${errors}
733
+ Resolve the reason above in Orchestrator, then run the uninstall again: uip solution deploy uninstall ${quoteForShell(deploymentName)}${feedFlag} ${consentFlag}` : `Orchestrator ended the uninstall in a FailedUninstall state without an error detail. Run 'uip solution deploy list${feedFlag}' 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 ${quoteForShell(deploymentName)}${feedFlag} --yes. Remove it from the Orchestrator Solutions UI when it does not.`, classification);
734
+ }
735
+ return {
736
+ ok: true,
737
+ status: finalStatus.status,
738
+ deploymentName,
739
+ instanceId
740
+ };
741
+ }
742
+
743
+ // src/services/deploy-upgrade-service.ts
744
+ var fail4 = (reason, message, instructions, extras = {}) => ({
745
+ ok: false,
746
+ reason,
747
+ message,
748
+ instructions,
749
+ exitCode: extras.exitCode ?? 1,
750
+ errorCode: extras.errorCode,
751
+ retry: extras.retry,
752
+ ...extras.pipelineDeploymentId !== undefined && {
753
+ pipelineDeploymentId: extras.pipelineDeploymentId
754
+ },
755
+ ...extras.deploymentKey !== undefined && {
756
+ deploymentKey: extras.deploymentKey
757
+ },
758
+ ...extras.permanentDeletionResources !== undefined && {
759
+ permanentDeletionResources: extras.permanentDeletionResources
760
+ }
761
+ });
762
+ var failPermanentDeletion = (refusal, pipelineDeploymentId, deploymentKey) => fail4(refusal.reason, refusal.message, refusal.instructions, {
763
+ ...refusal.classification,
764
+ pipelineDeploymentId,
765
+ deploymentKey,
766
+ permanentDeletionResources: refusal.resources
767
+ });
768
+ var DEFAULT_TIMEOUT_SECONDS = 300;
769
+ var DEFAULT_POLL_INTERVAL_MS = 5000;
770
+ 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>'.";
771
+ var PACKAGE_INSTRUCTIONS = "Confirm the version is published to the tenant (or Personal Workspace) feed: uip solution packages list.";
772
+ 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.";
773
+ 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.";
774
+ var LOOKUP_FAILED_INSTRUCTIONS = "The deployment lookup failed before any upgrade was requested. Retry, or address the deployment with --name and --package-name.";
775
+ var INVALID_TARGET = {
776
+ errorCode: "invalid_argument",
777
+ retry: "RetryWillNotFix"
778
+ };
779
+ var GUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
780
+ 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.";
781
+ async function upgradeDeploymentAsync(first, second) {
782
+ const options = typeof first === "string" ? { ...second, deploymentKey: first } : first;
783
+ const targetError = validateTarget(options);
784
+ if (targetError) {
785
+ return targetError;
786
+ }
787
+ const [authError, auth] = await catchError(getSolutionAuthContext({
788
+ tenant: options.tenant,
789
+ loginValidity: options.loginValidity,
790
+ envFilePath: options.envFilePath
791
+ }));
792
+ if (authError) {
793
+ return fail4("auth_failed", authError.message, AUTH_INSTRUCTIONS2);
794
+ }
795
+ const pipelinesApi = new PipelinesApi(new Configuration({
796
+ basePath: auth.basePath,
797
+ accessToken: auth.accessToken
798
+ }));
799
+ const target = options.deploymentKey ? await resolveTargetByKeyAsync(pipelinesApi, auth, options, options.deploymentKey) : await resolveTargetByNameAsync(pipelinesApi, auth, options);
800
+ if (!target.ok) {
801
+ return target;
802
+ }
803
+ const { deploymentName, packageName, scope } = target;
804
+ let targetVersion = options.version ?? target.pendingDraftVersion;
805
+ if (targetVersion === undefined) {
806
+ const [versionError, latest] = await catchError(pipelinesApi.pipelinesGetPackageVersion({ packageName, packageVersion: null }, scope));
807
+ if (versionError || !latest) {
808
+ const detail = versionError && await extractErrorDetails(versionError);
809
+ if (!detail || detail.context?.httpStatus === 404) {
810
+ return fail4("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" });
811
+ }
812
+ return fail4("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 });
813
+ }
814
+ targetVersion = latest.packageVersion;
815
+ }
816
+ const installAsync = async () => {
817
+ const [installError, installResult] = await catchError(pipelinesApi.pipelinesInstall({
818
+ deploymentName,
819
+ packageName,
820
+ packageVersion: targetVersion,
821
+ solutionRootFolderName: deploymentName
822
+ }, scope));
823
+ if (installError || !installResult?.pipelineDeploymentId) {
824
+ return describeInstallRefusalAsync(installError, {
825
+ options,
826
+ target,
827
+ deploymentName,
828
+ packageName
829
+ });
830
+ }
831
+ return {
832
+ ok: true,
833
+ pipelineDeploymentId: installResult.pipelineDeploymentId
834
+ };
835
+ };
836
+ const installed = await installAsync();
837
+ if (!installed.ok) {
838
+ return installed;
839
+ }
840
+ const targetFields = {
841
+ ...target.fromVersion !== undefined && {
842
+ fromVersion: target.fromVersion
843
+ },
844
+ ...target.installDeploymentKey !== undefined && {
845
+ installDeploymentKey: target.installDeploymentKey
846
+ },
847
+ ...target.discardedDraftKey !== undefined && {
848
+ discardedDraftKey: target.discardedDraftKey
849
+ }
850
+ };
851
+ if (options.noWait) {
852
+ return {
853
+ ok: true,
854
+ deploymentName,
855
+ toVersion: targetVersion,
856
+ pipelineDeploymentId: installed.pipelineDeploymentId,
857
+ ...targetFields,
858
+ status: "Accepted"
859
+ };
860
+ }
861
+ const timeoutSeconds = Number.isFinite(options.timeout) ? options.timeout : DEFAULT_TIMEOUT_SECONDS;
862
+ const timeoutMs = timeoutSeconds * 1000;
863
+ const deadline = Date.now() + timeoutMs;
864
+ const pollAsync = (pipelineDeploymentId2) => pollPipelineDeploymentAsync(pipelinesApi, auth, {
865
+ pipelineDeploymentId: pipelineDeploymentId2,
866
+ deploymentName,
867
+ scope,
868
+ timeoutMs: remainingTimeoutMs(timeoutMs, deadline),
869
+ intervalMs: Number.isFinite(options.pollInterval) ? options.pollInterval : DEFAULT_POLL_INTERVAL_MS,
870
+ signal: options.signal,
871
+ logPrefix: "deploy upgrade"
872
+ });
873
+ let pipelineDeploymentId = installed.pipelineDeploymentId;
874
+ let poll = await pollAsync(pipelineDeploymentId);
875
+ let permanentlyDeletedResources;
876
+ if (poll.outcome === PollOutcome.Completed && isPermanentDeletionRefusal(poll.data)) {
877
+ const refused = poll.data;
878
+ const refusedKey = refused.deploymentKey || undefined;
879
+ const keepCopiesHint = `discard the draft and upgrade without those links: ${discardRetryCommand(options, target, refusedKey)}, or remove the links from the deployment in Orchestrator first`;
880
+ const consent = await settlePermanentDeletionRefusalAsync({
881
+ api: pipelinesApi,
882
+ deploymentName,
883
+ scope,
884
+ signal: options.signal,
885
+ acknowledge: options.acknowledgePermanentDeletion === true,
886
+ status: refused,
887
+ verb: "Upgrading",
888
+ confirmHint: `re-run the same command with ${ACKNOWLEDGE_PERMANENT_DELETION_FLAG}: ${acknowledgeRetryCommand(options, target)}`,
889
+ keepCopiesHint
890
+ });
891
+ if (!consent.ok) {
892
+ return failPermanentDeletion(consent, pipelineDeploymentId, refusedKey);
893
+ }
894
+ const reinstalled = await installAsync();
895
+ if (!reinstalled.ok) {
896
+ return {
897
+ ...reinstalled,
898
+ instructions: `${reinstalled.instructions} ${acknowledgedButNotRunHint(consent.resources, keepCopiesHint)}`
899
+ };
900
+ }
901
+ pipelineDeploymentId = reinstalled.pipelineDeploymentId;
902
+ poll = await pollAsync(pipelineDeploymentId);
903
+ if (poll.outcome === PollOutcome.Completed && isPermanentDeletionRefusal(poll.data)) {
904
+ const deploymentKey = poll.data?.deploymentKey || undefined;
905
+ return failPermanentDeletion(stillRequiresConfirmation(deploymentName, consent.resources, deploymentKey), pipelineDeploymentId, deploymentKey);
906
+ }
907
+ permanentlyDeletedResources = consent.resources.map((resource) => formatPermanentDeletionResources([resource]));
908
+ }
909
+ const base = {
910
+ deploymentName,
911
+ toVersion: targetVersion,
912
+ pipelineDeploymentId,
913
+ ...targetFields,
914
+ ...permanentlyDeletedResources && { permanentlyDeletedResources }
915
+ };
916
+ if (poll.outcome !== PollOutcome.Completed) {
917
+ const failure = mapPollFailure(poll, `Upgrade ${pipelineDeploymentId}`);
918
+ const isTimeout = poll.outcome === PollOutcome.Timeout;
919
+ const lastStatus = poll.data?.status ?? "unknown";
920
+ return fail4(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)}`, {
921
+ exitCode: failure.exitCode,
922
+ errorCode: failure.errorCode,
923
+ retry: failure.retry
924
+ });
925
+ }
926
+ const finalStatus = poll.data;
927
+ if (!finalStatus) {
928
+ return fail4("deploy_failed", `The upgrade of '${deploymentName}' returned no final status.`, trackingHint(pipelineDeploymentId, target.listFlag));
929
+ }
930
+ const landedDespiteStatus = installLandedDespiteStatus(finalStatus);
931
+ if (finalStatus.status !== "DeploymentSucceeded" && !landedDespiteStatus) {
932
+ return fail4("deploy_failed", `The upgrade of '${deploymentName}' to ${targetVersion} ended in ${finalStatus.status}.`, `${await describeUpgradeFailureAsync(auth.basePath, auth.accessToken, pipelineDeploymentId, finalStatus, scope, options.signal)}
933
+ ${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));
934
+ }
935
+ const described = await describeUpgradedDeploymentAsync(auth, deploymentName, finalStatus, scope, target.listFlag);
936
+ return {
937
+ ok: true,
938
+ ...base,
939
+ deploymentKey: finalStatus.deploymentKey,
940
+ operation: finalStatus.deploymentResult?.operation ?? finalStatus.synthesizedOperation,
941
+ status: finalStatus.status,
942
+ ...finalStatus.deploymentResult?.status && {
943
+ deploymentStatus: finalStatus.deploymentResult.status,
944
+ actions: finalStatus.deploymentResult.actions ?? []
945
+ },
946
+ ...described,
947
+ ...landedDespiteStatus && {
948
+ nextSteps: [
949
+ explainPipelineStatusAfterInstall(finalStatus),
950
+ ...described.nextSteps ?? []
951
+ ]
952
+ }
953
+ };
954
+ }
955
+ async function describeUpgradedDeploymentAsync(auth, deploymentName, finalStatus, scope, listFlag) {
956
+ const record = finalStatus.deploymentResult;
957
+ const actions = record?.actions ?? [];
958
+ if (record?.status === "SuccessfulActivate") {
959
+ return { activationStatus: "Active" };
960
+ }
961
+ const reported = reportedActivationStatus(record);
962
+ const [, rows] = reported ? [undefined, undefined] : await catchError(findDeploymentsByName(auth, deploymentName, scope));
963
+ const row = rows?.find((candidate) => candidate.key === finalStatus.deploymentKey);
964
+ const activationStatus = reported ?? row?.activationStatus ?? (actions.includes("SetupActivation") ? "NeedsSetupToActivate" : actions.includes("Activate") ? "ReadyToActivate" : undefined);
965
+ if (!activationStatus) {
966
+ return {};
967
+ }
968
+ const state = {
969
+ name: deploymentName,
970
+ operation: row?.operation ?? record?.operation ?? finalStatus.synthesizedOperation,
971
+ operationStatus: row?.operationStatus ?? record?.operationStatus ?? "Successful",
972
+ activationStatus,
973
+ packageVersion: row?.packageVersion ?? record?.packageVersion,
974
+ currentPackageVersion: row?.currentPackageVersion ?? record?.packageVersion
975
+ };
976
+ const pendingResources = needsPendingResources(state) && finalStatus.deploymentKey ? await readPendingResourcesAsync(pendingResourceApis(auth), { name: deploymentName, key: finalStatus.deploymentKey }, scope) : undefined;
977
+ const nextSteps = deploymentNextSteps(state, pendingResources, listFlag ? ` ${listFlag}` : "");
978
+ return {
979
+ activationStatus,
980
+ ...pendingResources ? { pendingResources } : {},
981
+ ...nextSteps && nextSteps.length > 0 ? { nextSteps } : {}
982
+ };
983
+ }
984
+ async function describeInstallRefusalAsync(installError, context) {
985
+ const { options, target, deploymentName, packageName } = context;
986
+ let message = "The upgrade request returned no pipeline deployment.";
987
+ let classification = {};
988
+ let instructions = UPGRADE_REFUSED_INSTRUCTIONS;
989
+ if (installError) {
990
+ const detail = await extractErrorDetails(installError);
991
+ message = detail.details ? `${detail.message}: ${detail.details}` : detail.message;
992
+ classification = {
993
+ errorCode: detail.errorCode,
994
+ retry: detail.retry
995
+ };
996
+ if (detail.context?.httpStatus === 404) {
997
+ 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.`;
998
+ }
999
+ if (hasServerErrorCode(detail.details, DRAFT_VERSION_MISMATCH_CODE)) {
1000
+ return fail4("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 ? "" : `
1001
+ 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);
1002
+ }
1003
+ }
1004
+ return fail4("request_failed", message, instructions, classification);
1005
+ }
1006
+ function trackingHint(pipelineDeploymentId, listFlag) {
1007
+ 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.`;
1008
+ }
1009
+ function acknowledgeRetryCommand(options, target) {
1010
+ const version = versionFlag(options);
1011
+ let address = options.deploymentKey ? quoteForShell(options.deploymentKey) : undefined;
1012
+ if (!address) {
1013
+ const workspace = options.personalWorkspace ? " --personal-workspace" : "";
1014
+ address = `--name ${quoteForShell(target.deploymentName)} --package-name ${quoteForShell(target.packageName)}${workspace}`;
1015
+ }
1016
+ return `uip solution deploy upgrade ${address}${version}${tenantFlag(options)} ${ACKNOWLEDGE_PERMANENT_DELETION_FLAG}`;
1017
+ }
1018
+ async function consentDraftDecisionAsync(pipelinesApi, options, target, draft, scope) {
1019
+ if (options.noWait && !options.discardDraft) {
1020
+ return;
1021
+ }
1022
+ if (!await draftAwaitsConsentAsync(pipelinesApi, options, target.deploymentName, draft, scope)) {
1023
+ return;
1024
+ }
1025
+ if (options.discardDraft) {
1026
+ return fail4("pending_draft", `Deployment '${target.deploymentName}' has a pending draft (${draft.key}) that awaits the permanent-deletion confirmation, and --discard-draft would drop the links being confirmed.`, `Nothing was changed. To confirm the deletion and upgrade with those links, re-run without --discard-draft: ${acknowledgeRetryCommand(options, target)}. To keep the installed copies instead, re-run without ${ACKNOWLEDGE_PERMANENT_DELETION_FLAG}, which discards the draft: ${discardRetryCommand(options, target, draft.key)}`, INVALID_TARGET);
1027
+ }
1028
+ return "rerun";
1029
+ }
1030
+ async function draftAwaitsConsentAsync(pipelinesApi, options, deploymentName, draft, scope) {
1031
+ if (!options.acknowledgePermanentDeletion || draft.firstInstall || draft.activationSetup || !draft.targetVersion) {
1032
+ return false;
1033
+ }
1034
+ if (options.version !== undefined && options.version.toLowerCase() !== draft.targetVersion.toLowerCase()) {
1035
+ return false;
1036
+ }
1037
+ const resources = await listPermanentDeletionResourcesAsync(pipelinesApi, deploymentName, draft.key, scope, options.signal);
1038
+ return (resources?.length ?? 0) > 0;
1039
+ }
1040
+ function discardRetryCommand(options, target, draftKey) {
1041
+ const version = `${versionFlag(options)}${tenantFlag(options)}`;
1042
+ const key = draftKey ?? options.deploymentKey;
1043
+ if (key) {
1044
+ return `uip solution deploy upgrade ${quoteForShell(key)} --discard-draft${version}`;
1045
+ }
1046
+ const workspace = options.personalWorkspace ? " --personal-workspace" : "";
1047
+ return `uip solution deploy upgrade --name ${quoteForShell(target.deploymentName)} --package-name ${quoteForShell(target.packageName)}${workspace} --discard-draft${version}`;
1048
+ }
1049
+ function tenantFlag(options) {
1050
+ return options.tenant ? ` --tenant ${quoteForShell(options.tenant)}` : "";
1051
+ }
1052
+ function versionFlag(options) {
1053
+ return options.version ? ` --version ${quoteForShell(options.version)}` : "";
1054
+ }
1055
+ function validateTarget(options) {
1056
+ const byKey = options.deploymentKey !== undefined;
1057
+ const byName = options.deploymentName !== undefined || options.packageName !== undefined;
1058
+ if (options.version !== undefined && options.version.trim() === "") {
1059
+ return fail4("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);
1060
+ }
1061
+ if (byKey && byName) {
1062
+ return fail4("invalid_target", "Pass either a deployment key or --name with --package-name, not both.", TARGET_INSTRUCTIONS, INVALID_TARGET);
1063
+ }
1064
+ if (byKey) {
1065
+ return GUID_PATTERN.test(options.deploymentKey) ? undefined : fail4("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);
1066
+ }
1067
+ if (!options.deploymentName || !options.packageName) {
1068
+ return fail4("invalid_target", "--name and --package-name go together: the Pipelines install route needs both.", TARGET_INSTRUCTIONS, INVALID_TARGET);
1069
+ }
1070
+ return;
1071
+ }
1072
+ async function resolveTargetByNameAsync(pipelinesApi, auth, options) {
1073
+ const deploymentName = options.deploymentName;
1074
+ let scope;
1075
+ if (options.personalWorkspace) {
1076
+ const verdict = classifyFeedSession(auth);
1077
+ if (verdict !== "user") {
1078
+ const { message, instructions } = feedSessionRefusal(verdict, `Upgrading the Personal Workspace deployment '${deploymentName}'`, "address the deployment by key");
1079
+ return fail4("feed_requires_user_session", message, instructions, {
1080
+ errorCode: "permission_denied",
1081
+ retry: "RetryWillNotFix"
1082
+ });
1083
+ }
1084
+ const [pwError, pw] = await catchError(resolvePersonalWorkspace({
1085
+ tenant: options.tenant,
1086
+ loginValidity: options.loginValidity,
1087
+ envFilePath: options.envFilePath
1088
+ }));
1089
+ if (pwError || !pw) {
1090
+ return fail4("personal_workspace_failed", pwError?.message ?? "Personal Workspace could not be resolved.", "Ensure your user has a Personal Workspace, then retry.");
1091
+ }
1092
+ scope = folderKeyInitOverride(pw.key);
1093
+ }
1094
+ const [existsError] = await catchError(pipelinesApi.pipelinesGetPreActivateSteps({ deploymentName }, scope));
1095
+ if (existsError) {
1096
+ const detail = await extractErrorDetails(existsError);
1097
+ const where = scope ? "your Personal Workspace feed" : `the tenant feed of '${auth.tenantName}'`;
1098
+ if (isDeploymentNotFound(detail)) {
1099
+ return fail4("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" });
1100
+ }
1101
+ return fail4("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 });
1102
+ }
1103
+ const [rowsError, rows] = await catchError(findDeploymentsByName(auth, deploymentName, scope));
1104
+ if (rowsError) {
1105
+ const unreadable = rowsError instanceof DeploymentSearchNeedsUserSessionError || classifyFeedSession(auth) === "application" && isAuthRefusal(rowsError);
1106
+ if (!unreadable) {
1107
+ const detail = await extractErrorDetails(rowsError);
1108
+ return fail4("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 });
1109
+ }
1110
+ }
1111
+ if (isCompletedUninstall(rows ? newestDeploymentRow(rows) : undefined)) {
1112
+ return fail4("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);
1113
+ }
1114
+ const resolved = {
1115
+ deploymentName,
1116
+ packageName: options.packageName,
1117
+ listFlag: scope ? "--personal-workspace" : undefined
1118
+ };
1119
+ let discardedDraftKey;
1120
+ const newest = rows ? newestDeploymentRow(rows) : undefined;
1121
+ if (newest?.operationStatus === "Draft") {
1122
+ const draft = {
1123
+ key: newest.key,
1124
+ firstInstall: newest.operation === "Install",
1125
+ activationSetup: newest.operation === "ActivationSetup",
1126
+ targetVersion: newest.targetPackageVersion ?? newest.packageVersion,
1127
+ liveVersion: newest.currentPackageVersion
1128
+ };
1129
+ const consentDraft = await consentDraftDecisionAsync(pipelinesApi, options, resolved, draft, scope);
1130
+ if (consentDraft && consentDraft !== "rerun") {
1131
+ return consentDraft;
1132
+ }
1133
+ if (consentDraft === "rerun") {
1134
+ return {
1135
+ ok: true,
1136
+ ...resolved,
1137
+ scope,
1138
+ pendingDraftVersion: draft.targetVersion ?? undefined
1139
+ };
1140
+ }
1141
+ const refusal = refusePendingDraft(options, resolved, draft);
1142
+ if (refusal) {
1143
+ return refusal;
1144
+ }
1145
+ const discarded = await discardDraftAsync(pipelinesApi, deploymentName, draft.key, scope);
1146
+ if (discarded) {
1147
+ return discarded;
1148
+ }
1149
+ discardedDraftKey = draft.key;
1150
+ }
1151
+ return {
1152
+ ok: true,
1153
+ ...resolved,
1154
+ scope,
1155
+ discardedDraftKey
1156
+ };
1157
+ }
1158
+ function isDeploymentNotFound(detail) {
1159
+ const status = detail.context?.httpStatus;
1160
+ if (status === 404) {
1161
+ return true;
1162
+ }
1163
+ return status === 400 && (/"errorCode"\s*:\s*"4004"/.test(detail.details) || (detail.parsedErrors?.deploymentName ?? []).some((message) => /not found/i.test(message)));
1164
+ }
1165
+ async function resolveTargetByKeyAsync(pipelinesApi, auth, options, deploymentKey) {
1166
+ const [lookupError, info] = await catchError(pipelinesApi.pipelinesGetDeploymentByKey({ key: deploymentKey }));
1167
+ if (lookupError || !info) {
1168
+ const detail = lookupError ? await extractErrorDetails(lookupError) : undefined;
1169
+ if (detail?.context?.httpStatus === 404 && classifyFeedSession(auth) === "user") {
1170
+ return resolveTargetByKeyViaSolutionsAsync(pipelinesApi, auth, options, deploymentKey);
1171
+ }
1172
+ if (!detail || detail.context?.httpStatus === 404) {
1173
+ return fail4("deployment_not_found", `Deployment '${deploymentKey}' was not found in tenant '${auth.tenantName}'.`, DEPLOYMENT_NOT_FOUND_INSTRUCTIONS, { errorCode: "not_found", retry: "RetryWillNotFix" });
1174
+ }
1175
+ return fail4("lookup_failed", detail.details ? `${detail.message}: ${detail.details}` : detail.message, LOOKUP_FAILED_INSTRUCTIONS, { errorCode: detail.errorCode, retry: detail.retry });
1176
+ }
1177
+ if (info.status === DeploymentStatus.SuccessfulUninstall) {
1178
+ return fail4("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);
1179
+ }
1180
+ let scope;
1181
+ if (info.locationKey) {
1182
+ const verdict = classifyFeedSession(auth);
1183
+ if (verdict !== "user") {
1184
+ 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");
1185
+ return fail4("feed_requires_user_session", message, instructions, {
1186
+ errorCode: "permission_denied",
1187
+ retry: "RetryWillNotFix"
1188
+ });
1189
+ }
1190
+ scope = folderKeyInitOverride(info.locationKey);
1191
+ } else if (options.personalWorkspace) {
1192
+ return fail4("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);
1193
+ }
1194
+ const listFlag = info.locationKey ? `--feed ${info.locationKey}` : undefined;
1195
+ let live = info;
1196
+ let discardedDraftKey;
1197
+ if (info.status === DeploymentStatus.Draft) {
1198
+ const draft = {
1199
+ key: info.deploymentKey,
1200
+ firstInstall: info.operation === DeploymentOperation.Install,
1201
+ activationSetup: info.operation === DeploymentOperation.ActivationSetup,
1202
+ targetVersion: info.targetPackageVersion ?? info.packageVersion,
1203
+ liveVersion: info.packageVersion
1204
+ };
1205
+ const consentDraft = await consentDraftDecisionAsync(pipelinesApi, options, {
1206
+ deploymentName: info.deploymentName,
1207
+ packageName: info.packageName,
1208
+ listFlag
1209
+ }, draft, scope);
1210
+ if (consentDraft && consentDraft !== "rerun") {
1211
+ return consentDraft;
1212
+ }
1213
+ if (consentDraft === "rerun") {
1214
+ return {
1215
+ ok: true,
1216
+ deploymentName: info.deploymentName,
1217
+ packageName: info.packageName,
1218
+ scope,
1219
+ listFlag,
1220
+ fromVersion: info.packageVersion ?? undefined,
1221
+ installDeploymentKey: info.installDeploymentKey,
1222
+ pendingDraftVersion: draft.targetVersion ?? undefined
1223
+ };
1224
+ }
1225
+ const refusal = refusePendingDraft(options, {
1226
+ deploymentName: info.deploymentName,
1227
+ packageName: info.packageName,
1228
+ listFlag
1229
+ }, draft);
1230
+ if (refusal) {
1231
+ return refusal;
1232
+ }
1233
+ const discarded = await discardDraftAsync(pipelinesApi, info.deploymentName, draft.key, scope);
1234
+ if (discarded) {
1235
+ return discarded;
1236
+ }
1237
+ const [relookupError, current] = await catchError(pipelinesApi.pipelinesGetDeploymentByKey({
1238
+ key: info.installDeploymentKey
1239
+ }));
1240
+ if (relookupError || !current) {
1241
+ const detail = relookupError ? await extractErrorDetails(relookupError) : undefined;
1242
+ return fail4("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 ${quoteForShell(info.installDeploymentKey)}${versionFlag(options)}${tenantFlag(options)}`, detail ? { errorCode: detail.errorCode, retry: detail.retry } : {});
1243
+ }
1244
+ if (current.status === DeploymentStatus.Draft || current.status === DeploymentStatus.SuccessfulUninstall) {
1245
+ return fail4("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);
1246
+ }
1247
+ live = current;
1248
+ discardedDraftKey = draft.key;
1249
+ }
1250
+ return {
1251
+ ok: true,
1252
+ deploymentName: live.deploymentName,
1253
+ packageName: live.packageName,
1254
+ scope,
1255
+ listFlag,
1256
+ fromVersion: live.packageVersion ?? undefined,
1257
+ installDeploymentKey: live.installDeploymentKey,
1258
+ discardedDraftKey
1259
+ };
1260
+ }
1261
+ async function resolveTargetByKeyViaSolutionsAsync(pipelinesApi, auth, options, deploymentKey) {
1262
+ let scope;
1263
+ if (options.personalWorkspace) {
1264
+ const [pwError, pw] = await catchError(resolvePersonalWorkspace({
1265
+ tenant: options.tenant,
1266
+ loginValidity: options.loginValidity,
1267
+ envFilePath: options.envFilePath
1268
+ }));
1269
+ if (pwError || !pw) {
1270
+ return fail4("personal_workspace_failed", pwError?.message ?? "Personal Workspace could not be resolved.", "Ensure your user has a Personal Workspace, then retry.");
1271
+ }
1272
+ scope = folderKeyInitOverride(pw.key);
1273
+ }
1274
+ const deploymentsApi = new DeploymentsApi(new Configuration2({
1275
+ basePath: auth.basePath,
1276
+ accessToken: auth.accessToken
1277
+ }));
1278
+ const [readError, row] = await catchError(deploymentsApi.deploymentsGetByKey({ deploymentKey }, scope));
1279
+ if (readError || !row) {
1280
+ const detail = readError ? await extractErrorDetails(readError) : undefined;
1281
+ if (!detail || detail.context?.httpStatus === 404) {
1282
+ return fail4("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" });
1283
+ }
1284
+ return fail4("lookup_failed", detail.details ? `${detail.message}: ${detail.details}` : detail.message, LOOKUP_FAILED_INSTRUCTIONS, { errorCode: detail.errorCode, retry: detail.retry });
1285
+ }
1286
+ if (row.status === "SuccessfulUninstall") {
1287
+ return fail4("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);
1288
+ }
1289
+ const target = await resolveTargetByNameAsync(pipelinesApi, auth, {
1290
+ ...options,
1291
+ deploymentName: row.name,
1292
+ packageName: row.packageName
1293
+ });
1294
+ if (!target.ok) {
1295
+ return target;
1296
+ }
1297
+ return { ...target, installDeploymentKey: row.installDeploymentKey };
1298
+ }
1299
+ function refusePendingDraft(options, target, draft) {
1300
+ const targetVersion = draft.targetVersion ?? "<version>";
1301
+ const feed = target.listFlag ? ` ${target.listFlag}` : "";
1302
+ const packageVersion = draft.targetVersion ? quoteForShell(draft.targetVersion) : "<version>";
1303
+ const retryDraft = `uip solution deploy run --name ${quoteForShell(target.deploymentName)} --package-name ${quoteForShell(target.packageName)} --package-version ${packageVersion} --config-file <fixed-config>${feed}${tenantFlag(options)} --yes`;
1304
+ if (draft.firstInstall) {
1305
+ return fail4("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);
1306
+ }
1307
+ if (options.discardDraft) {
1308
+ return;
1309
+ }
1310
+ if (draft.activationSetup) {
1311
+ return fail4("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);
1312
+ }
1313
+ const live = draft.liveVersion ? ` ${draft.liveVersion}` : "";
1314
+ return fail4("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)}
1315
+ To retry the draft itself with a corrected configuration instead: ${retryDraft}`, INVALID_TARGET);
1316
+ }
1317
+ async function discardDraftAsync(pipelinesApi, deploymentName, draftKey, scope) {
1318
+ const [error] = await catchError(pipelinesApi.pipelinesDeleteDeploymentByKey({ deploymentKey: draftKey }, scope));
1319
+ if (!error) {
1320
+ return;
1321
+ }
1322
+ const detail = await extractErrorDetails(error);
1323
+ const status = detail.context?.httpStatus;
1324
+ const hasBody = !isBareResponse(detail.details);
1325
+ if (hasServerErrorCode(detail.details, "DeploymentNotFound") || status === 404 && hasBody) {
1326
+ return;
1327
+ }
1328
+ const message = `The pending draft ${draftKey} of '${deploymentName}' could not be discarded: ${hasBody ? `${detail.message}: ${detail.details}` : detail.message}`;
1329
+ if (status === 405 || status === 404 && !hasBody) {
1330
+ return fail4("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.", {
1331
+ errorCode: status === 405 ? "method_not_allowed" : "not_found",
1332
+ retry: "RetryWillNotFix"
1333
+ });
1334
+ }
1335
+ if (hasServerErrorCode(detail.details, "DeploymentAlreadyInProgress")) {
1336
+ return fail4("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" });
1337
+ }
1338
+ if (hasServerErrorCode(detail.details, "DeploymentInvalidState")) {
1339
+ return fail4("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);
1340
+ }
1341
+ return fail4("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 });
1342
+ }
1343
+ function classifyTerminalFailure(status) {
1344
+ return status === "ValidationFailed" || status === "ConflictFixingError" ? { errorCode: "invalid_argument", retry: "RetryWillNotFix" } : {};
1345
+ }
1346
+ async function describeUpgradeFailureAsync(basePath, accessToken, pipelineDeploymentId, finalStatus, scope, signal) {
1347
+ const fromPipeline = formatDeploymentFailureDetails(pipelineDeploymentId, finalStatus);
1348
+ const explained = finalStatus.validationResult?.validationErrors?.length || finalStatus.validationResult?.conflictErrors?.length || finalStatus.conflictFixingErrors?.length || finalStatus.deploymentScheduleErrors?.length || finalStatus.deploymentResult?.errorMessage;
1349
+ if (explained || !finalStatus.deploymentKey) {
1350
+ return fromPipeline;
1351
+ }
1352
+ const deploymentsApi = new DeploymentsApi(new Configuration2({ basePath, accessToken }));
1353
+ const reason = await describeValidationFailureAsync(deploymentsApi, finalStatus.deploymentKey, SolutionDeploymentAction.Upgrade, scope, signal);
1354
+ return reason === NO_SERVER_REASON ? fromPipeline : `Validation: ${reason}
1355
+ ${fromPipeline}`;
1356
+ }
1357
+
1358
+ // ../solution-sdk/src/legacy-package-search.ts
1359
+ var LEGACY_PACKAGE_SEARCH_ROUTE = "POST /api/search/packages";
1360
+ async function searchPackagesLegacy(auth, query, scope) {
1361
+ const api = new SearchApi(new Configuration2({
1362
+ basePath: auth.basePath,
1363
+ accessToken: auth.accessToken
1364
+ }));
1365
+ const response = await api.searchSearchPackagesRaw({
1366
+ searchFilter: {
1367
+ take: query.take,
1368
+ skip: query.skip,
1369
+ searchTerm: query.searchTerm,
1370
+ orderByColumn: query.orderByColumn,
1371
+ orderByDirection: query.orderByDirection
1372
+ }
1373
+ }, scope);
1374
+ return response.raw.json();
1375
+ }
1376
+
1377
+ // ../solution-sdk/src/package-search.ts
1378
+ var PIPELINES_SEARCH_ROUTE = "POST /v1/pipelines/packages/search";
1379
+
1380
+ class PackageSearchNeedsUserSessionError extends Error {
1381
+ constructor() {
1382
+ super("This server's package search reads user sessions only; an application (client-credentials) login cannot read it. Sign in with 'uip login' to use it.");
1383
+ this.name = "PackageSearchNeedsUserSessionError";
1384
+ }
1385
+ }
1386
+ var PACKAGE_ROWS = {
1387
+ singular: "package",
1388
+ plural: "packages",
1389
+ fromJSON: PackageInfoFromJSON,
1390
+ isRow: (row) => isNonEmptyString(row, "key") && isNonEmptyString(row, "name")
1391
+ };
1392
+ function isNonEmptyString(row, field) {
1393
+ const value = field in row ? row[field] : undefined;
1394
+ return typeof value === "string" && value !== "";
1395
+ }
1396
+ var serversWithoutPipelinesSearch = new Set;
1397
+ async function searchPackagesPage(auth, query, scope) {
1398
+ if (!serversWithoutPipelinesSearch.has(auth.basePath)) {
1399
+ const [error, body2] = await catchError(searchViaPipelines(auth, query, scope));
1400
+ if (!error) {
1401
+ return {
1402
+ ...parseSearchPage(body2, PIPELINES_SEARCH_ROUTE, PACKAGE_ROWS),
1403
+ source: "pipelines"
1404
+ };
1405
+ }
1406
+ if (!await isMissingPipelinesRoute(error)) {
1407
+ throw error;
1408
+ }
1409
+ serversWithoutPipelinesSearch.add(auth.basePath);
1410
+ logger.debug(`${PIPELINES_SEARCH_ROUTE} is not served at ${auth.basePath}; using the deprecated ${LEGACY_PACKAGE_SEARCH_ROUTE}.`);
1411
+ }
1412
+ if (isApplicationSession(auth)) {
1413
+ throw new PackageSearchNeedsUserSessionError;
1414
+ }
1415
+ const body = await searchPackagesLegacy(auth, query, scope);
1416
+ return {
1417
+ ...parseSearchPage(body, LEGACY_PACKAGE_SEARCH_ROUTE, PACKAGE_ROWS),
1418
+ source: "legacy"
1419
+ };
1420
+ }
1421
+ async function searchViaPipelines(auth, query, scope) {
1422
+ const api = new PipelinesApi(new Configuration({
1423
+ basePath: auth.basePath,
1424
+ accessToken: auth.accessToken
1425
+ }));
1426
+ const response = await api.pipelinesSearchPackagesRaw({
1427
+ pipelinePackagesSearchRequest: {
1428
+ take: query.take,
1429
+ skip: query.skip,
1430
+ searchTerm: query.searchTerm,
1431
+ orderByColumn: query.orderByColumn,
1432
+ orderByDirection: query.orderByDirection
1433
+ }
1434
+ }, scope);
1435
+ return response.raw.json();
1436
+ }
1437
+ // src/services/packages-list-service.ts
1438
+ var fail5 = (reason, message, instructions, extras = {}) => ({
1439
+ ok: false,
1440
+ reason,
1441
+ message,
1442
+ instructions,
1443
+ exitCode: 1,
1444
+ ...extras
1445
+ });
1446
+ var refuseFeedRead2 = (refusal) => fail5("feed_requires_user_session", refusal.message, refusal.instructions, {
1447
+ errorCode: "permission_denied",
1448
+ retry: "RetryWillNotFix"
1449
+ });
1450
+ async function listPackagesAsync(options = {}) {
1451
+ const limit = options.limit ?? DEFAULT_PAGE_SIZE;
1452
+ const offset = options.offset ?? 0;
1453
+ const [authError, auth] = await catchError(getSolutionAuthContext({
1454
+ tenant: options.tenant,
1455
+ loginValidity: options.loginValidity,
1456
+ envFilePath: options.envFilePath
1457
+ }));
1458
+ if (authError) {
1459
+ return fail5("auth_failed", authError.message, authError.message);
1460
+ }
1461
+ if (options.personalWorkspace) {
1462
+ const refusal = feedReadRefusalForApplication(auth, "Listing the Personal Workspace's packages");
1463
+ if (refusal) {
1464
+ return refuseFeedRead2(refusal);
1465
+ }
1466
+ }
1467
+ const [scopeError, scope] = await catchError(resolveFeedScope({
1468
+ personalWorkspace: options.personalWorkspace,
1469
+ feed: options.feed,
1470
+ tenant: options.tenant,
1471
+ loginValidity: options.loginValidity,
1472
+ envFilePath: options.envFilePath
1473
+ }));
1474
+ if (scopeError) {
1475
+ const { message, instructions, ...extras } = await describeFeedResolutionFailureAsync(scopeError, options, auth);
1476
+ return fail5("feed_resolution_failed", message, instructions, extras);
1477
+ }
1478
+ if (scope.kind !== "tenant") {
1479
+ const refusal = feedReadRefusalForApplication(auth, `Listing the packages of the feed '${scope.name ?? scope.folderKey}'`);
1480
+ if (refusal) {
1481
+ return refuseFeedRead2(refusal);
1482
+ }
1483
+ }
1484
+ const [listError, result] = await catchError(searchPackagesPage(auth, {
1485
+ take: limit,
1486
+ skip: offset,
1487
+ orderByColumn: options.sortBy ?? "publishDate",
1488
+ orderByDirection: options.sortOrder ?? "Descending",
1489
+ searchTerm: options.name
1490
+ }, feedScopeInitOverride(scope)));
1491
+ if (listError instanceof PackageSearchNeedsUserSessionError) {
1492
+ return fail5("list_request_failed", `Failed to list packages: ${listError.message}`, "Run it with a user session ('uip login'). A client-credentials login cannot list packages on this server.", { errorCode: "permission_denied", retry: "RetryWillNotFix" });
1493
+ }
1494
+ if (listError) {
1495
+ const { message, instructions, ...extras } = await describeSearchFailureAsync(listError, "packages");
1496
+ return fail5("list_request_failed", message, instructions, extras);
1497
+ }
1498
+ const packages = result.values;
1499
+ return {
1500
+ ok: true,
1501
+ packages,
1502
+ limit,
1503
+ offset,
1504
+ total: result.count
1505
+ };
1506
+ }
1507
+
1508
+ export { activateDeploymentAsync, listDeploymentsAsync, uninstallDeploymentAsync, upgradeDeploymentAsync, listPackagesAsync };
1509
+
1510
+ //# debugId=AC06BACB6D23A44764756E2164756E21