@uipath/vertical-solutions-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 (60) hide show
  1. package/README.md +80 -0
  2. package/dist/THIRD-PARTY-NOTICES.md +1617 -369
  3. package/dist/createSdk-2f9x47em.js +12 -0
  4. package/dist/deploy-jobs-3k87fkwz.js +23 -0
  5. package/dist/deploy-service-qaykt7er.js +1779 -0
  6. package/dist/deployment-search-arbm2z06.js +2649 -0
  7. package/dist/embedded-file-reader-r66242ey.js +114 -0
  8. package/dist/first-party-service-srxaftkn.js +27 -0
  9. package/dist/index-7a3eqtxv.js +3174 -0
  10. package/dist/index-8s9dxpj4.js +18812 -0
  11. package/dist/index-fyc0k5dd.js +51405 -0
  12. package/dist/index-h43sj8at.js +23075 -0
  13. package/dist/index-kmp9fmhb.js +25 -0
  14. package/dist/index-ngs0a2he.js +15 -0
  15. package/dist/index.js +14 -3
  16. package/dist/index.umd.min-fqefd9kn.js +7814 -0
  17. package/dist/job-commands-azgbrbdb.js +23 -0
  18. package/dist/job-output-t43hzj1f.js +36 -0
  19. package/dist/prompts-dtgt183n.js +260 -0
  20. package/dist/resume-service-mqfnnzb5.js +344 -0
  21. package/dist/seed-service-2d1f7zaz.js +2014 -0
  22. package/dist/seed-templates-sqqqratr.js +14 -0
  23. package/dist/service-errors-jzbq3z7p.js +22 -0
  24. package/dist/solution-auth-zq6gyxsv.js +11 -0
  25. package/dist/solution-deployments-m44fyb88.js +33 -0
  26. package/dist/templates-85yrztpk.js +15 -0
  27. package/dist/tool-342sz3hm.js +439 -0
  28. package/dist/tool-38ad77wd.js +1472 -0
  29. package/dist/tool-4z1dy1rn.js +260 -0
  30. package/dist/tool-53w8skv5.js +262 -0
  31. package/dist/tool-67587985.js +554 -0
  32. package/dist/tool-87e2ps7f.js +13 -0
  33. package/dist/tool-cag2z2d6.js +30 -0
  34. package/dist/tool-e4qgwf9h.js +2281 -0
  35. package/dist/tool-gdtpsdn7.js +342 -0
  36. package/dist/tool-gvmshnq9.js +555 -0
  37. package/dist/tool-h4f967yd.js +73 -0
  38. package/dist/tool-hdxtgckw.js +249 -0
  39. package/dist/tool-jjxbj28g.js +17067 -0
  40. package/dist/tool-keqjsxdx.js +28 -0
  41. package/dist/tool-maw0g02p.js +24 -0
  42. package/dist/tool-mn1wkbhw.js +113 -0
  43. package/dist/tool-n35nrs32.js +4949 -0
  44. package/dist/tool-p589v2r5.js +114 -0
  45. package/dist/tool-pz9qm3dv.js +6723 -0
  46. package/dist/tool-r1ewa0tf.js +5645 -0
  47. package/dist/tool-r2epw08s.js +164 -0
  48. package/dist/tool-rr9fensp.js +12 -0
  49. package/dist/tool-s04z9t80.js +19 -0
  50. package/dist/tool-s9qbxa2g.js +638 -0
  51. package/dist/tool-tjhwq0ez.js +261 -0
  52. package/dist/tool-v15vxc4x.js +183 -0
  53. package/dist/tool-vf2mgth0.js +14286 -0
  54. package/dist/tool-vp7rjr2q.js +22083 -0
  55. package/dist/tool-xd2ejt3h.js +103 -0
  56. package/dist/tool-yx29vb63.js +672 -0
  57. package/dist/tool.js +12 -2
  58. package/dist/watch-job-wh6m7xzq.js +31 -0
  59. package/package.json +3 -3
  60. package/dist/tool-xymb3dsq.js +0 -45085
@@ -0,0 +1,1779 @@
1
+ import {
2
+ SolutionDeploymentsError,
3
+ isCompletedUninstall,
4
+ isInstalledDeployment,
5
+ listDeploymentsNamed,
6
+ newestDeployment,
7
+ solutionNameFromManifest
8
+ } from "./tool-tjhwq0ez.js";
9
+ import {
10
+ describePauseKind,
11
+ finalizeSucceededJob,
12
+ pauseErrorCode,
13
+ pauseInstructions,
14
+ rejectSeedWithoutWait,
15
+ stoppedWatching
16
+ } from "./tool-342sz3hm.js";
17
+ import {
18
+ configKeyProblem,
19
+ getManifest,
20
+ listAllFolders,
21
+ listPackageVersions,
22
+ listPackages,
23
+ listVerticals,
24
+ normalizeVersions
25
+ } from "./tool-e4qgwf9h.js";
26
+ import"./tool-s9qbxa2g.js";
27
+ import {
28
+ credentialsFailure,
29
+ readServiceUrl,
30
+ rejectBlankOptions
31
+ } from "./tool-p589v2r5.js";
32
+ import {
33
+ watchDeployJob,
34
+ watchOptionsFrom,
35
+ withCapacityRetry
36
+ } from "./tool-gvmshnq9.js";
37
+ import {
38
+ writeProgress,
39
+ writeStepSummary,
40
+ writeSuccessSummary,
41
+ writeWarning
42
+ } from "./tool-v15vxc4x.js";
43
+ import {
44
+ createVssConfig
45
+ } from "./tool-xd2ejt3h.js";
46
+ import {
47
+ toDeployFailure
48
+ } from "./tool-r2epw08s.js";
49
+ import {
50
+ submitDeployJob
51
+ } from "./tool-mn1wkbhw.js";
52
+ import {
53
+ VssApiError,
54
+ scopeQuery,
55
+ vssRequestParsed
56
+ } from "./tool-4z1dy1rn.js";
57
+ import {
58
+ jsonObject,
59
+ optionalText
60
+ } from "./tool-87e2ps7f.js";
61
+ import"./tool-38ad77wd.js";
62
+ import {
63
+ checkBeforeResubmitting,
64
+ routingFlags
65
+ } from "./tool-cag2z2d6.js";
66
+ import {
67
+ failure,
68
+ invalidOption,
69
+ missingOption
70
+ } from "./tool-h4f967yd.js";
71
+ import {
72
+ forDisplay,
73
+ forDisplayIfSet
74
+ } from "./tool-rr9fensp.js";
75
+ import {
76
+ DEPLOY_ACTIONS
77
+ } from "./tool-s04z9t80.js";
78
+ import {
79
+ exports_external
80
+ } from "./tool-vf2mgth0.js";
81
+ import {
82
+ RESULTS,
83
+ canPrompt,
84
+ catchError,
85
+ resolveEnvReference
86
+ } from "./tool-pz9qm3dv.js";
87
+ import {
88
+ getFileSystem
89
+ } from "./tool-hdxtgckw.js";
90
+ import"./tool-1yh8g550.js";
91
+
92
+ // src/services/manifest-configurations.ts
93
+ var SECRET_TERMS = [
94
+ "secret",
95
+ "password",
96
+ "passwd",
97
+ "pwd",
98
+ "token",
99
+ "credential",
100
+ "apikey",
101
+ "api-key",
102
+ "api_key",
103
+ "accesskey",
104
+ "access-key",
105
+ "access_key",
106
+ "privatekey",
107
+ "private-key",
108
+ "private_key",
109
+ "connectionstring",
110
+ "connection-string",
111
+ "connection_string",
112
+ "passphrase"
113
+ ];
114
+ function looksSecret(key) {
115
+ return secretTermIn(key) !== undefined;
116
+ }
117
+ function secretTermIn(key) {
118
+ const lower = key.toLowerCase();
119
+ return SECRET_TERMS.find((term) => lower.includes(term));
120
+ }
121
+ function parseConfigPairs(pairs) {
122
+ const entries = [];
123
+ const literalSecretKeys = [];
124
+ for (const [index, pair] of pairs.entries()) {
125
+ const separator = pair.indexOf("=");
126
+ if (separator < 0) {
127
+ throw new Error(`Invalid --config value #${index + 1}: expected KEY=VALUE, but there is no '='.`);
128
+ }
129
+ const key = pair.slice(0, separator).trim();
130
+ const raw = pair.slice(separator + 1);
131
+ if (!key) {
132
+ throw new Error(`Invalid --config value #${index + 1}: the key before '=' is empty.`);
133
+ }
134
+ const problem = configKeyProblem(key);
135
+ if (problem) {
136
+ throw new Error(`Invalid --config value #${index + 1}: ${problem}`);
137
+ }
138
+ const isEnvReference = raw.startsWith("env.");
139
+ if (!isEnvReference && looksSecret(key)) {
140
+ literalSecretKeys.push(key);
141
+ }
142
+ entries.push({ key, value: raw });
143
+ }
144
+ return { entries, literalSecretKeys };
145
+ }
146
+ function normalizeConfigFileValues(parsed, path) {
147
+ const document = jsonObject.safeParse(parsed);
148
+ if (!document.success) {
149
+ throw new TypeError(`${path} must contain a JSON/YAML object of KEY: VALUE pairs.`);
150
+ }
151
+ const entries = [];
152
+ const literalSecretKeys = [];
153
+ for (const [key, raw] of Object.entries(document.data)) {
154
+ const problem = configKeyProblem(key);
155
+ if (problem)
156
+ throw new Error(`${path}: ${problem}`);
157
+ const value = exports_external.string().safeParse(raw);
158
+ if (!value.success) {
159
+ throw new TypeError(`${path}: the value of "${key}" must be a string.`);
160
+ }
161
+ if (!value.data.startsWith("env.") && looksSecret(key)) {
162
+ literalSecretKeys.push(key);
163
+ }
164
+ entries.push({ key, value: value.data });
165
+ }
166
+ return { entries, literalSecretKeys };
167
+ }
168
+ function mergeConfigurations(fileEntries, flagEntries) {
169
+ const merged = Object.create(null);
170
+ for (const entry of [...fileEntries, ...flagEntries]) {
171
+ merged[entry.key] = entry.value;
172
+ }
173
+ for (const key of Object.keys(merged)) {
174
+ const [error, value] = catchError(() => resolveEnvReference(merged[key]));
175
+ if (error || value === undefined) {
176
+ throw new Error(`Configuration ${key}: ${error?.message ?? "could not be read"}.`);
177
+ }
178
+ merged[key] = value;
179
+ }
180
+ return merged;
181
+ }
182
+
183
+ // src/services/collect-configurations.ts
184
+ var NOTHING_DECLARED = new Map;
185
+ async function collectConfigurations(manifest, options, prompts) {
186
+ const warnings = [];
187
+ const fromFile = await readConfigFile(options.configFile);
188
+ if (!fromFile.ok)
189
+ return fromFile;
190
+ const [parseError, parsed] = catchError(() => parseConfigPairs(options.config ?? []));
191
+ if (parseError || !parsed) {
192
+ return invalidOption(parseError?.message ?? "Could not read --config.", "Use --config KEY=VALUE, and --config KEY=env.MY_VAR for a secret.");
193
+ }
194
+ for (const key of parsed.literalSecretKeys) {
195
+ warnings.push(`'${key}' was given a literal value on the command line, where it lands in ` + `shell history, 'ps' output and CI logs. Prefer --config ${key}=env.MY_VAR.`);
196
+ }
197
+ for (const key of fromFile.literalSecretKeys) {
198
+ warnings.push(`'${key}' has a literal value in ${options.configFile ?? "the config file"}, which is ` + `usually committed. Prefer ${key}: env.MY_VAR and keep the secret in the environment.`);
199
+ }
200
+ const [mergeError, configurations] = catchError(() => mergeConfigurations(fromFile.entries, parsed.entries));
201
+ if (mergeError || !configurations) {
202
+ return invalidOption(mergeError?.message ?? "Could not read the configuration values.", "Set the environment variable, or pass the value with --config KEY=VALUE.");
203
+ }
204
+ const declared = manifest.configurations ?? NOTHING_DECLARED;
205
+ warnings.push(...undeclaredKeys(declared, configurations));
206
+ const missing = missingRequiredKeys(declared, configurations);
207
+ const badDeclaration = reservedDeclaration(missing);
208
+ if (badDeclaration)
209
+ return badDeclaration;
210
+ if (missing.length === 0) {
211
+ return { ok: true, configurations, warnings };
212
+ }
213
+ if (!canPrompt()) {
214
+ return missingOption(`Configuration for ${missing.join(", ")}`, `--config ${missing[0]}=<value> (or --config ${missing[0]}=env.MY_VAR for a secret)`);
215
+ }
216
+ for (const key of missing) {
217
+ const value = await promptRequired(key, declared.get(key), prompts);
218
+ if (value === undefined) {
219
+ return missingOption(`A value for ${key}`, `--config ${key}=<value> (or --config ${key}=env.MY_VAR for a secret)`);
220
+ }
221
+ configurations[key] = value;
222
+ }
223
+ return { ok: true, configurations, warnings };
224
+ }
225
+ var MAX_PROMPT_ATTEMPTS = 10;
226
+ async function promptRequired(key, meta, prompts) {
227
+ for (let attempt = 0;attempt < MAX_PROMPT_ATTEMPTS; attempt += 1) {
228
+ const entered = await prompts.askValue(key, {
229
+ description: meta?.description,
230
+ defaultValue: meta?.value,
231
+ secret: meta?.isSecret || secretTermIn(key) !== undefined
232
+ });
233
+ const trimmed = entered?.trim();
234
+ if (trimmed)
235
+ return trimmed;
236
+ writeWarning(`A value for ${key} is required.`);
237
+ }
238
+ return;
239
+ }
240
+ async function readConfigFile(path) {
241
+ if (path === undefined) {
242
+ return { ok: true, entries: [], literalSecretKeys: [] };
243
+ }
244
+ const fs = getFileSystem();
245
+ if (!await fs.exists(path)) {
246
+ return failure("invalid-option", `--config-file not found: ${path}`, "Check the path, or drop the flag and pass values with --config KEY=VALUE.");
247
+ }
248
+ const [readError, contents] = await catchError(fs.readFile(path, "utf-8"));
249
+ if (readError || contents === null) {
250
+ return failure("invalid-option", `Failed to read ${path}: ${readError?.message ?? "the file is empty"}`, "Check the file is readable, or drop --config-file and pass values with " + "--config KEY=VALUE.");
251
+ }
252
+ const [parseError, parsed] = catchError(() => parseConfigDocument(contents));
253
+ if (parseError || parsed === undefined) {
254
+ return failure("invalid-option", `${path} is not valid JSON or YAML: ${parseError?.message ?? "unknown error"}`, "Fix the file so it parses as a JSON or YAML object of KEY: VALUE pairs.");
255
+ }
256
+ const [normalizeError, values] = catchError(() => normalizeConfigFileValues(parsed, path));
257
+ if (normalizeError || !values) {
258
+ return failure("invalid-option", normalizeError?.message ?? `Could not read ${path}.`, "Fix the file: a flat JSON or YAML object of KEY: VALUE pairs, string values only.");
259
+ }
260
+ return { ok: true, ...values };
261
+ }
262
+ function parseConfigDocument(contents) {
263
+ const trimmed = contents.trim();
264
+ if (trimmed === "")
265
+ throw new Error("the file is empty");
266
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
267
+ return JSON.parse(trimmed);
268
+ }
269
+ const [jsonError, json] = catchError(() => JSON.parse(trimmed));
270
+ if (!jsonError)
271
+ return json;
272
+ return parseFlatYaml(trimmed);
273
+ }
274
+ function parseFlatYaml(trimmed) {
275
+ const result = Object.create(null);
276
+ for (const [index, rawLine] of trimmed.split(/\r?\n/).entries()) {
277
+ const line = rawLine.trim();
278
+ if (line === "" || line.startsWith("#"))
279
+ continue;
280
+ if (/^[ \t]/.test(rawLine)) {
281
+ throw new Error(`line ${index + 1}: nested values are not supported — write one flat "key: value" per line`);
282
+ }
283
+ const separator = line.indexOf(":");
284
+ if (separator <= 0) {
285
+ throw new Error(`line ${index + 1}: expected "key: value"`);
286
+ }
287
+ const key = line.slice(0, separator).trim();
288
+ const problem = configKeyProblem(key);
289
+ if (problem)
290
+ throw new Error(`line ${index + 1}: ${problem}`);
291
+ const raw = line.slice(separator + 1).trim();
292
+ if (raw === "") {
293
+ throw new Error(`line ${index + 1}: "${key}" has no value — write \`${key}: ""\` for an empty ` + 'one, or flatten a nested block into one "key: value" per line');
294
+ }
295
+ result[key] = raw.replace(/^["'](.*)["']$/, "$1");
296
+ }
297
+ return result;
298
+ }
299
+ var DEPLOY_SET_KEYS = new Set([
300
+ "UIPATH_BASE_URL",
301
+ "UIPATH_ORG_ID",
302
+ "UIPATH_ORG_NAME",
303
+ "UIPATH_TENANT_ID",
304
+ "UIPATH_TENANT_NAME",
305
+ "UIPATH_FOLDER_KEY",
306
+ "UIPATH_FOLDER_ID",
307
+ "UIPATH_FOLDER_PATH"
308
+ ]);
309
+ function undeclaredKeys(declared, configurations) {
310
+ return Object.keys(configurations).flatMap((key) => {
311
+ if (DEPLOY_SET_KEYS.has(key)) {
312
+ return [
313
+ `${key} is set by the deploy itself, which takes precedence over this value.`
314
+ ];
315
+ }
316
+ return declared.size === 0 || declared.has(key) ? [] : [
317
+ `${key} is not a configuration this package declares, so nothing reads it.`
318
+ ];
319
+ });
320
+ }
321
+ function reservedDeclaration(missing) {
322
+ for (const key of missing) {
323
+ const problem = configKeyProblem(key);
324
+ if (problem) {
325
+ return invalidOption(`The package manifest declares a configuration it cannot take: ${problem}`, "Fix the configuration keys in the package manifest and publish it again.");
326
+ }
327
+ }
328
+ return;
329
+ }
330
+ function missingRequiredKeys(declared, collected) {
331
+ return [...declared].filter(([key, meta]) => meta.isRequired && (!Object.hasOwn(collected, key) || collected[key].trim() === "")).map(([key]) => key);
332
+ }
333
+
334
+ // src/services/deploy-config.ts
335
+ var CAPS = {
336
+ maxResources: 20,
337
+ maxPropertiesPerResource: 20,
338
+ maxKeyLength: 100,
339
+ maxValueLength: 1024,
340
+ maxNameLength: 200
341
+ };
342
+ var ROUTING_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
343
+ var ROUTING_NAME_RULE = "a routing name must be lowercase letters, digits and hyphens, at most 32 characters";
344
+ var DeployConfigDocumentSchema = exports_external.looseObject({
345
+ version: exports_external.unknown().optional(),
346
+ externalApp: exports_external.unknown().optional(),
347
+ resources: exports_external.unknown().optional()
348
+ });
349
+ var DeployConfigResourceSchema = exports_external.looseObject({
350
+ name: exports_external.string().optional(),
351
+ kind: exports_external.string().optional(),
352
+ configuration: jsonObject.nullish()
353
+ });
354
+ async function discoverDeployConfig(explicitPath) {
355
+ const fs = getFileSystem();
356
+ if (explicitPath !== undefined) {
357
+ if (!await fs.exists(explicitPath)) {
358
+ throw new Error(`--resource-config file not found: ${explicitPath}`);
359
+ }
360
+ return explicitPath;
361
+ }
362
+ const candidate = fs.path.join(fs.env.cwd(), "deploy-config.json");
363
+ return await fs.exists(candidate) ? candidate : undefined;
364
+ }
365
+ function describeValue(value) {
366
+ if (typeof value === "object" && value !== null) {
367
+ return Array.isArray(value) ? "an array" : "an object";
368
+ }
369
+ const text = JSON.stringify(value) ?? String(value);
370
+ return text.length > 40 ? `${text.slice(0, 40)}…` : text;
371
+ }
372
+ async function loadDeployConfig(path) {
373
+ const fs = getFileSystem();
374
+ const [readError, contents] = await catchError(fs.readFile(path, "utf-8"));
375
+ if (readError || contents === null) {
376
+ throw new Error(`Failed to read ${path}: ${readError?.message ?? "the file is empty"}`);
377
+ }
378
+ const [parseError, json] = catchError(() => JSON.parse(contents));
379
+ const document = DeployConfigDocumentSchema.safeParse(json);
380
+ if (parseError || !document.success) {
381
+ throw new Error(`${path} is not valid JSON: ${parseError?.message ?? "it must be a JSON object"}`);
382
+ }
383
+ const { version, externalApp, resources } = document.data;
384
+ const warnings = [];
385
+ if (version !== undefined && version !== 1) {
386
+ throw new Error(`${path}: unsupported version ${describeValue(version)} — only 1 is supported.`);
387
+ }
388
+ if (externalApp !== undefined) {
389
+ warnings.push(`${path}: the 'externalApp' block is ignored — external-app auto-creation was removed. ` + "Register the application yourself and supply its id as 'externalClientId'.");
390
+ }
391
+ const entries = exports_external.array(exports_external.unknown()).safeParse(resources);
392
+ if (!entries.success) {
393
+ throw new TypeError(`${path}: 'resources' must be an array.`);
394
+ }
395
+ return {
396
+ path,
397
+ resources: entries.data.map((entry, index) => parseResource(entry, index, path)),
398
+ warnings
399
+ };
400
+ }
401
+ function parseResource(entry, index, path) {
402
+ const parsed = DeployConfigResourceSchema.safeParse(entry);
403
+ if (!parsed.success) {
404
+ throw resourceError(parsed.error.issues[0]?.path[0], index, path);
405
+ }
406
+ const configuration = {};
407
+ for (const [key, value] of Object.entries(parsed.data.configuration ?? {})) {
408
+ configuration[key] = parseConfigurationEntry(key, value, path);
409
+ }
410
+ return { ...parsed.data, configuration };
411
+ }
412
+ function resourceError(field, index, path) {
413
+ if (field === "name") {
414
+ return new Error(`${path}: 'name' must be a string when present.`);
415
+ }
416
+ if (field === "kind") {
417
+ return new Error(`${path}: 'kind' must be a string when present.`);
418
+ }
419
+ if (field === "configuration") {
420
+ return new Error(`${path}: 'configuration' must be an object.`);
421
+ }
422
+ return new TypeError(`${path}: 'resources[${index}]' must be an object with 'name', 'kind' and 'configuration'.`);
423
+ }
424
+ function parseConfigurationEntry(key, value, path) {
425
+ const reserved = configKeyProblem(key);
426
+ if (reserved) {
427
+ throw new Error(`${path}: ${reserved}`);
428
+ }
429
+ const forbidden = secretTermIn(key);
430
+ if (forbidden) {
431
+ throw new Error(`${path}: '${key}' looks like a secret (contains "${forbidden}"). ` + "This file is meant to be committed and its values are stored unencrypted — " + "pass secret values with --config KEY=env.VAR instead.");
432
+ }
433
+ const text = exports_external.string().safeParse(value);
434
+ if (!text.success) {
435
+ throw new TypeError(`${path}: the value of '${key}' must be a string.`);
436
+ }
437
+ if (key === "routingName" && !ROUTING_NAME_PATTERN.test(text.data)) {
438
+ throw new Error(`${path}: 'routingName' is not valid — ${ROUTING_NAME_RULE}.`);
439
+ }
440
+ return text.data;
441
+ }
442
+
443
+ // src/services/resource-payload.ts
444
+ function buildPayload(resolved, displayNames, warnings) {
445
+ const payload = [];
446
+ for (const [key, values] of resolved) {
447
+ const name = displayNames.get(key) ?? key;
448
+ if (name.length > CAPS.maxNameLength) {
449
+ warnings.push(`Skipping resource '${name.slice(0, 40)}…': the name exceeds ${CAPS.maxNameLength} characters.`);
450
+ continue;
451
+ }
452
+ const configuration = buildResourceConfiguration(name, values, warnings);
453
+ if (Object.keys(configuration).length === 0)
454
+ continue;
455
+ if (payload.length >= CAPS.maxResources) {
456
+ warnings.push(`Dropping resource '${name}': at most ${CAPS.maxResources} resources may be configured.`);
457
+ continue;
458
+ }
459
+ payload.push({ name, configuration });
460
+ }
461
+ return payload;
462
+ }
463
+ function buildResourceConfiguration(name, values, warnings) {
464
+ const configuration = {};
465
+ for (const [property, value] of values) {
466
+ const dropReason = propertyDropReason(property, value, Object.keys(configuration).length);
467
+ if (dropReason !== undefined) {
468
+ warnings.push(`Dropping '${name}.${property}': ${dropReason}`);
469
+ continue;
470
+ }
471
+ configuration[property] = value;
472
+ }
473
+ return configuration;
474
+ }
475
+ function propertyDropReason(property, value, keptSoFar) {
476
+ if (property.length > CAPS.maxKeyLength) {
477
+ return `the property name exceeds ${CAPS.maxKeyLength} characters.`;
478
+ }
479
+ if (value.length > CAPS.maxValueLength) {
480
+ return `the value exceeds ${CAPS.maxValueLength} characters.`;
481
+ }
482
+ if (property === "routingName" && !ROUTING_NAME_PATTERN.test(value)) {
483
+ return `${ROUTING_NAME_RULE}. The deployment will pause for it instead.`;
484
+ }
485
+ if (keptSoFar >= CAPS.maxPropertiesPerResource) {
486
+ return `a resource may carry at most ${CAPS.maxPropertiesPerResource} properties.`;
487
+ }
488
+ return;
489
+ }
490
+
491
+ // src/services/declared-configuration.ts
492
+ function validateRoutingNameAnswer(value) {
493
+ const trimmed = value.trim();
494
+ if (trimmed === "" || ROUTING_NAME_PATTERN.test(trimmed))
495
+ return true;
496
+ return `${ROUTING_NAME_RULE[0].toUpperCase()}${ROUTING_NAME_RULE.slice(1)}.`;
497
+ }
498
+ var NOTHING_DECLARED2 = { properties: [], warnings: [] };
499
+ function parseDeclaredConfiguration(manifest) {
500
+ return manifest.solution?.configuration ?? NOTHING_DECLARED2;
501
+ }
502
+ async function collectDeclaredConfiguration(input) {
503
+ const declaration = parseDeclaredConfiguration(input.manifest);
504
+ const warnings = [...declaration.warnings, ...input.file?.warnings ?? []];
505
+ const { resolved, displayNames, fromFile } = seedFromFile(input, declaration, warnings);
506
+ const unresolvedRequired = [];
507
+ for (const property of declaration.properties) {
508
+ const key = property.resourceName.toLowerCase();
509
+ const values = resolved.get(key) ?? new Map;
510
+ if (!displayNames.has(key)) {
511
+ displayNames.set(key, property.resourceName);
512
+ }
513
+ await resolveDeclaredProperty(property, values, input, warnings, unresolvedRequired);
514
+ resolved.set(key, values);
515
+ }
516
+ if (input.isFreshInstall) {
517
+ warnUnresolvedRequired(unresolvedRequired, warnings);
518
+ }
519
+ const payload = buildPayload(resolved, displayNames, warnings);
520
+ return { payload, warnings, unresolvedRequired, fromFile };
521
+ }
522
+ function seedFromFile(input, declaration, warnings) {
523
+ const resolved = new Map;
524
+ const displayNames = new Map;
525
+ const fromFile = new Map;
526
+ const declared = new Set(declaration.properties.map((property) => property.resourceName.toLowerCase()));
527
+ for (const resource of input.file?.resources ?? []) {
528
+ const name = resolveFileResourceName(resource, input, declaration);
529
+ if (name === undefined) {
530
+ warnings.push("Skipping a deploy-config entry with no 'name': the manifest does not declare " + "exactly one configurable resource, so the name cannot be inferred.");
531
+ continue;
532
+ }
533
+ const key = name.toLowerCase();
534
+ if (declared.size > 0 && !declared.has(key)) {
535
+ warnings.push(`deploy-config names resource '${name}', which this solution's ` + "manifest does not declare as configurable — the entry is ignored.");
536
+ continue;
537
+ }
538
+ displayNames.set(key, name);
539
+ const values = resolved.get(key) ?? new Map;
540
+ const supplied = fromFile.get(key) ?? new Set;
541
+ for (const [property, value] of Object.entries(resource.configuration ?? {})) {
542
+ if (value.trim() === "")
543
+ continue;
544
+ values.set(property, value);
545
+ supplied.add(property);
546
+ }
547
+ resolved.set(key, values);
548
+ fromFile.set(key, supplied);
549
+ }
550
+ return { resolved, displayNames, fromFile };
551
+ }
552
+ async function resolveDeclaredProperty(property, values, input, warnings, unresolvedRequired) {
553
+ const secretTerm = secretTermIn(property.propertyName);
554
+ if (secretTerm) {
555
+ values.delete(property.propertyName);
556
+ warnings.push(`Skipping ${property.resourceName}.${property.propertyName}: its name contains ` + `'${secretTerm}', and per-resource values are stored unencrypted on the deploy ` + "job. Set it in the portal's configuration wizard instead.");
557
+ return;
558
+ }
559
+ if (values.has(property.propertyName))
560
+ return;
561
+ if (property.hasManifestValue)
562
+ return;
563
+ const typed = await askFor(property, input);
564
+ if (typed !== undefined && typed !== "") {
565
+ values.set(property.propertyName, typed);
566
+ } else if (property.required) {
567
+ unresolvedRequired.push(`${property.resourceName}.${property.propertyName}`);
568
+ }
569
+ }
570
+ function warnUnresolvedRequired(unresolvedRequired, warnings) {
571
+ for (const label of unresolvedRequired) {
572
+ warnings.push(`${label} is declared required by the package but no value was supplied — ` + "the deployment will pause and offer the portal's configuration wizard for it. " + "Set it in deploy-config.json to deploy without stopping.");
573
+ }
574
+ }
575
+ async function askFor(property, input) {
576
+ if (!input.isFreshInstall || !canPrompt())
577
+ return;
578
+ const typed = await input.prompts.askValue(`${property.resourceName}.${property.propertyName}`, {
579
+ description: property.description,
580
+ defaultValue: property.defaultValue,
581
+ secret: false,
582
+ ...property.propertyName === "routingName" ? { validate: validateRoutingNameAnswer } : {}
583
+ });
584
+ return typed?.trim();
585
+ }
586
+ function resolveFileResourceName(resource, input, declaration) {
587
+ if (resource.name)
588
+ return resource.name;
589
+ const declaredNames = new Set(declaration.properties.map((property) => property.resourceName));
590
+ if (input.file?.resources.length === 1 && declaredNames.size === 1) {
591
+ return [...declaredNames][0];
592
+ }
593
+ return;
594
+ }
595
+
596
+ // src/services/manifest-preflight.ts
597
+ function hasEntries(section) {
598
+ return Object.keys(section ?? {}).length > 0;
599
+ }
600
+ function findTenantResources(manifest) {
601
+ const findings = [];
602
+ if (manifest.dataFabric?.schemaFilename) {
603
+ findings.push({
604
+ step: "DF_SCHEMA",
605
+ manifestKey: "dataFabric.schemaFilename",
606
+ describe: "a Data Fabric entity schema to import"
607
+ });
608
+ }
609
+ if (manifest.ixp) {
610
+ findings.push({
611
+ step: "IXP",
612
+ manifestKey: "ixp",
613
+ describe: "an IXP project, taxonomy and ML models"
614
+ });
615
+ }
616
+ if (hasEntries(manifest.storageBuckets)) {
617
+ findings.push({
618
+ step: "TOP_BUCKETS",
619
+ manifestKey: "storageBuckets",
620
+ describe: "tenant-level storage buckets"
621
+ });
622
+ }
623
+ if (hasEntries(manifest.indexes)) {
624
+ findings.push({
625
+ step: "TOP_INDEXES",
626
+ manifestKey: "indexes",
627
+ describe: "tenant-level context-grounding indexes"
628
+ });
629
+ }
630
+ if (manifest.webapp) {
631
+ findings.push({
632
+ step: "WEBAPP",
633
+ manifestKey: "webapp",
634
+ describe: "a web application to register and publish"
635
+ });
636
+ }
637
+ return findings;
638
+ }
639
+ function describeTenantResources(findings) {
640
+ return findings.map((f) => ` - ${f.manifestKey} (${f.describe}) — step ${f.step}`).join(`
641
+ `);
642
+ }
643
+
644
+ // src/api/deployments.ts
645
+ var DeploymentSchema = exports_external.looseObject({
646
+ id: exports_external.string(),
647
+ folderKey: exports_external.string(),
648
+ packageVersion: exports_external.string(),
649
+ status: optionalText,
650
+ deploymentName: optionalText
651
+ });
652
+ var DeploymentListSchema = exports_external.looseObject({
653
+ items: exports_external.array(DeploymentSchema).optional()
654
+ });
655
+ async function findCurrentDeployments(config, packageName, signal) {
656
+ const result = await vssRequestParsed(config, "deployments", DeploymentListSchema, {
657
+ query: {
658
+ ...scopeQuery(config),
659
+ packageName,
660
+ isCurrent: "true"
661
+ },
662
+ signal
663
+ });
664
+ return result.items ?? [];
665
+ }
666
+
667
+ // src/services/target/folder-index.ts
668
+ var FOLDER_LIST_CONTEXT = "Could not list Orchestrator folders";
669
+ async function loadFolderLabels(config, listFolders, signal) {
670
+ const [error, folders] = await catchError(listFolders(config, signal));
671
+ const aborted = abortedLookup(signal, error, FOLDER_LIST_CONTEXT);
672
+ if (aborted)
673
+ return aborted;
674
+ if (error || !folders)
675
+ return { ok: true, labels: new Map, read: true };
676
+ return {
677
+ ok: true,
678
+ labels: new Map(folders.map((folder) => [folder.key.toLowerCase(), folder.path])),
679
+ folders,
680
+ read: true
681
+ };
682
+ }
683
+ function pathFor(labels, folderKey) {
684
+ return labels.get(folderKey.toLowerCase());
685
+ }
686
+ function labelFor(labels, folderKey) {
687
+ return pathFor(labels, folderKey) ?? folderKey;
688
+ }
689
+ function sameKey(a, b) {
690
+ return a.toLowerCase() === b.toLowerCase();
691
+ }
692
+ function keyForPath(labels, lowerCasePath) {
693
+ for (const [key, path] of labels) {
694
+ if (path.toLowerCase() === lowerCasePath)
695
+ return key;
696
+ }
697
+ return;
698
+ }
699
+ function abortedLookup(signal, error, context) {
700
+ if (!signal?.aborted)
701
+ return;
702
+ return toDeployFailure(signal.reason ?? error, context);
703
+ }
704
+
705
+ // src/services/target/folder-selector.ts
706
+ function folderSelector(options, folderIndex) {
707
+ const path = options.folderPath?.trim();
708
+ if (options.folderPath !== undefined && !path) {
709
+ return invalidOption("--folder-path cannot be empty.", "Drop --folder-path, or pass a folder path.");
710
+ }
711
+ const folderKey = options.folderKey?.trim();
712
+ if (options.folderKey !== undefined && !folderKey) {
713
+ return invalidOption("--folder-key cannot be empty.", "Drop --folder-key, or pass the folder's key.");
714
+ }
715
+ if (folderKey) {
716
+ return keySelector(folderKey, path, options, folderIndex);
717
+ }
718
+ if (!path)
719
+ return;
720
+ if (!folderIndex.folders)
721
+ return uncheckablePath(path, options);
722
+ const matches = foldersForPath(path, folderIndex.labels);
723
+ if (matches.size === 0)
724
+ return unknownPath(path);
725
+ if (matches.size > 1) {
726
+ return invalidOption(`--folder-path ${path} matches ${matches.size} folders: ` + `${[...matches.values()].join(", ")}.`, "Pass the full path of the one you mean, or --folder-key <guid>.");
727
+ }
728
+ return {
729
+ ok: true,
730
+ flag: `--folder-path ${path}`,
731
+ option: "--folder-path",
732
+ keys: new Set(matches.keys())
733
+ };
734
+ }
735
+ function inFolder(deployment, folder) {
736
+ return folder.keys.has(deployment.folderKey.toLowerCase());
737
+ }
738
+ function unknownPath(path) {
739
+ return invalidOption(`--folder-path ${path} matches no Orchestrator folder in this tenant.`, "Pass the full path as Orchestrator shows it (for example Shared/Production), " + "or the folder's key with --folder-key <guid>.");
740
+ }
741
+ function keySelector(folderKey, path, options, folderIndex) {
742
+ const key = folderKey.toLowerCase();
743
+ const selector = {
744
+ ok: true,
745
+ flag: `--folder-key ${folderKey}`,
746
+ option: "--folder-key",
747
+ keys: new Set([key])
748
+ };
749
+ if (!path)
750
+ return selector;
751
+ if (!folderIndex.folders) {
752
+ return uncheckablePathWithKey(path, folderKey, options) ?? selector;
753
+ }
754
+ const pathKeys = foldersForPath(path, folderIndex.labels);
755
+ if (pathKeys.size === 0)
756
+ return unknownPath(path);
757
+ if (!pathKeys.has(key)) {
758
+ return invalidOption(`--folder-path ${path} and --folder-key ${folderKey} name different folders.`, "Drop one of the two, or correct it so both point at the same folder.");
759
+ }
760
+ return selector;
761
+ }
762
+ function uncheckablePathWithKey(path, folderKey, options) {
763
+ if (options.deploymentName) {
764
+ writeProgress(` Could not read the Orchestrator folders to check --folder-path ${path} ` + `against --folder-key ${folderKey}; picking the deployment by --deployment-name.`);
765
+ return;
766
+ }
767
+ return failure("service-error", `Could not read the Orchestrator folders to check --folder-path ${path} ` + `against --folder-key ${folderKey}.`, "Drop --folder-path and pick the deployment by --folder-key alone, or by " + "--deployment-name <name>, or give this login the right to read Orchestrator folders.", { errorCode: "server_error", retry: "RetryLater" });
768
+ }
769
+ function uncheckablePath(path, options) {
770
+ if (options.deploymentName) {
771
+ writeProgress(` Could not read the Orchestrator folders to check --folder-path ${path}; ` + "picking the deployment by --deployment-name.");
772
+ return;
773
+ }
774
+ return failure("service-error", `Could not read the Orchestrator folders to resolve --folder-path ${path}.`, "Pick the deployment with --folder-key <guid> or --deployment-name <name> instead, " + "or give this login the right to read Orchestrator folders.", { errorCode: "server_error", retry: "RetryLater" });
775
+ }
776
+ function foldersForPath(path, folderLabels) {
777
+ const wanted = path.toLowerCase();
778
+ const entries = [...folderLabels].map(([key, label]) => ({
779
+ key: key.toLowerCase(),
780
+ label,
781
+ lower: label.toLowerCase()
782
+ }));
783
+ const byPath = entries.filter((entry) => entry.lower === wanted);
784
+ const matches = byPath.length > 0 || wanted.includes("/") ? byPath : entries.filter((entry) => entry.lower.split("/").pop() === wanted);
785
+ return new Map(matches.map((entry) => [entry.key.toLowerCase(), entry.label]));
786
+ }
787
+
788
+ // src/services/target/action.ts
789
+ async function resolveAction(existing, options, prompts, folderIndex) {
790
+ const folderLabels = folderIndex.labels;
791
+ const requested = options.action?.toLowerCase();
792
+ const requestedAction = DEPLOY_ACTIONS.find((action) => action === requested);
793
+ if (requested !== undefined && requestedAction === undefined) {
794
+ return invalidOption(`Unknown --action "${options.action}".`, `Use one of: ${DEPLOY_ACTIONS.join(", ")}.`);
795
+ }
796
+ if (requestedAction === "install") {
797
+ return { ok: true, action: "install", explicit: true };
798
+ }
799
+ if (requestedAction === "upgrade" || requestedAction === "downgrade") {
800
+ if (existing.length === 0) {
801
+ return invalidOption(`--action ${requestedAction} needs an existing deployment, but none is installed for this package.`, "Use --action install for a first deployment.");
802
+ }
803
+ const picked2 = await chooseExistingDeployment(existing, options, prompts, folderIndex);
804
+ if (!picked2.ok)
805
+ return picked2;
806
+ if (picked2.deployment === undefined) {
807
+ return ambiguousDeployment(existing, folderLabels);
808
+ }
809
+ return {
810
+ ok: true,
811
+ action: requestedAction,
812
+ deployment: picked2.deployment,
813
+ explicit: true
814
+ };
815
+ }
816
+ if (existing.length === 0) {
817
+ return { ok: true, action: "install", explicit: false };
818
+ }
819
+ if (!canPrompt()) {
820
+ return missingOption(`This package is already deployed (${describeDeployments(existing, folderLabels)}), so the intent`, "--action install|upgrade|downgrade");
821
+ }
822
+ const mode = await prompts.chooseDeployMode(existing.map((deployment) => ({
823
+ deployment,
824
+ folderLabel: labelFor(folderLabels, deployment.folderKey)
825
+ })));
826
+ if (mode === "fresh") {
827
+ return { ok: true, action: "install", explicit: false };
828
+ }
829
+ const picked = await chooseExistingDeployment(existing, options, prompts, folderIndex);
830
+ if (!picked.ok)
831
+ return picked;
832
+ if (picked.deployment === undefined) {
833
+ return ambiguousDeployment(existing, folderLabels);
834
+ }
835
+ return {
836
+ ok: true,
837
+ action: "upgrade",
838
+ deployment: picked.deployment,
839
+ explicit: false
840
+ };
841
+ }
842
+ function ambiguousDeployment(existing, folderLabels) {
843
+ const named = existing.some((deployment) => deployment.deploymentName);
844
+ return failure("ambiguous-deployment", `This package has ${existing.length} current deployments: ${describeDeployments(existing, folderLabels)}.`, named ? "Name the one to act on with --deployment-name <name>, or pin its folder with " + "--folder-key <guid>. Both are shown above." : "Name the one to act on with --folder-key <guid>. The key of each is shown above.", { result: RESULTS.ValidationError, errorCode: "invalid_argument" });
845
+ }
846
+ function describeDeployments(existing, folderLabels) {
847
+ return existing.map((deployment) => {
848
+ const label = pathFor(folderLabels, deployment.folderKey);
849
+ const where = label ? `${label} (${deployment.folderKey})` : deployment.folderKey;
850
+ const named = deployment.deploymentName ? `${deployment.deploymentName} ` : "";
851
+ return `${named}${deployment.packageVersion} in ${where}`;
852
+ }).join("; ");
853
+ }
854
+ function deploymentLabel(deployment, folderLabels) {
855
+ const folder = labelFor(folderLabels, deployment.folderKey);
856
+ return deployment.deploymentName ? `${deployment.deploymentName} — ${folder}` : folder;
857
+ }
858
+ function pickExistingDeployment(existing, options, folderIndex) {
859
+ const folderLabels = folderIndex.labels;
860
+ const folder = folderSelector(options, folderIndex);
861
+ if (folder && !folder.ok)
862
+ return folder;
863
+ const byName = pickByDeploymentName(existing, options, folderLabels, folder);
864
+ if (!byName.ok || byName.deployment)
865
+ return byName;
866
+ if (folder)
867
+ return pickByFolder(existing, folder, options, folderLabels);
868
+ return {
869
+ ok: true,
870
+ deployment: existing.length === 1 ? existing[0] : undefined
871
+ };
872
+ }
873
+ function pickByDeploymentName(existing, options, folderLabels, folder) {
874
+ const none = { ok: true, deployment: undefined };
875
+ const wanted = options.deploymentName?.trim().toLowerCase();
876
+ if (!wanted)
877
+ return none;
878
+ const named = existing.filter((deployment) => deployment.deploymentName?.trim().toLowerCase() === wanted);
879
+ if (named.length === 1) {
880
+ if (folder && !inFolder(named[0], folder)) {
881
+ return conflictingFlags(options, folder);
882
+ }
883
+ return { ok: true, deployment: named[0] };
884
+ }
885
+ if (named.length > 1) {
886
+ return invalidOption(`--deployment-name ${options.deploymentName} matches ${named.length} current ` + `deployments of this package: ${describeDeployments(named, folderLabels)}.`, "Pin the one to act on with --folder-key <guid>.");
887
+ }
888
+ if (!folder && existing.some((d) => d.deploymentName)) {
889
+ return invalidOption(`--deployment-name ${options.deploymentName} does not match any current ` + `deployment of this package. Deployed as: ${describeDeploymentNames(existing)}.`, "Pass one of the names listed, or select by folder with --folder-key.");
890
+ }
891
+ return none;
892
+ }
893
+ function conflictingFlags(options, folder) {
894
+ return invalidOption(`--deployment-name ${options.deploymentName} and ${folder.flag} ` + "name different deployments.", "Drop one of the two, or correct it so both point at the same deployment.");
895
+ }
896
+ function pickByFolder(existing, folder, options, folderLabels) {
897
+ const picked = existing.filter((deployment) => inFolder(deployment, folder));
898
+ if (picked.length === 1) {
899
+ return options.deploymentName && picked[0].deploymentName ? conflictingFlags(options, folder) : { ok: true, deployment: picked[0] };
900
+ }
901
+ if (picked.length > 1) {
902
+ return invalidOption(`${folder.flag} matches ${picked.length} current deployments ` + `of this package: ${describeDeploymentNames(picked)}.`, "Name the one to act on with --deployment-name <name>.");
903
+ }
904
+ return invalidOption(`${folder.flag} does not match any current deployment of this ` + `package. Deployed in: ${describeDeployments(existing, folderLabels)}.`, `Pass the folder of one of the deployments listed, or drop ${folder.option} ` + "and name the deployment with --deployment-name.");
905
+ }
906
+ function describeDeploymentNames(existing) {
907
+ const named = existing.map((deployment) => deployment.deploymentName).filter((name) => Boolean(name));
908
+ return named.length > 0 ? named.join(", ") : "none of them carries a name";
909
+ }
910
+ async function chooseExistingDeployment(existing, options, prompts, folderIndex) {
911
+ const byFlag = pickExistingDeployment(existing, options, folderIndex);
912
+ if (!byFlag.ok || byFlag.deployment || existing.length < 2 || !canPrompt()) {
913
+ return byFlag;
914
+ }
915
+ const picked = await prompts.pickOne("Deployment", existing.map((deployment) => ({
916
+ name: `${deploymentLabel(deployment, folderIndex.labels)} (v${deployment.packageVersion})`,
917
+ value: deployment.id
918
+ })));
919
+ return {
920
+ ok: true,
921
+ deployment: existing.find((deployment) => deployment.id === picked)
922
+ };
923
+ }
924
+
925
+ // src/services/target/deployment-name.ts
926
+ async function resolveDeploymentName(defaultName, parentFolder, options, prompts) {
927
+ if (options.deploymentName)
928
+ return options.deploymentName;
929
+ if (!canPrompt())
930
+ return;
931
+ writeProgress(`
932
+ Deployment name (optional)
933
+ ` + ` Creates a NEW folder "${parentFolder}/<name>" and names the deployment (unique per tenant).
934
+ ` + ` Leave blank to use "${defaultName}"; give one to install this package into more than ` + "one folder (e.g. UAT + Prod).");
935
+ const typed = await prompts.askText("Name");
936
+ return typed?.trim() || undefined;
937
+ }
938
+ function upgradeName(deployment, options) {
939
+ return options.deploymentName || deployment?.deploymentName || undefined;
940
+ }
941
+
942
+ // src/services/target/version.ts
943
+ async function resolveVersion(config, packageName, stage, decision, options, prompts) {
944
+ const [error, nav] = await catchError(listPackageVersions(config, packageName, {
945
+ stage,
946
+ anchorVersion: decision.deployment?.packageVersion,
947
+ after: 100,
948
+ before: 1,
949
+ signal: options.signal
950
+ }));
951
+ if (error || !nav) {
952
+ return toDeployFailure(error, `Could not list versions of ${packageName}`);
953
+ }
954
+ const versions = normalizeVersions(nav);
955
+ if (options.packageVersion !== undefined) {
956
+ if (!options.packageVersion.trim()) {
957
+ return invalidOption("--package-version cannot be empty.", "Pass a package version or omit --package-version to select one.");
958
+ }
959
+ const validated = validateRequestedVersion(options.packageVersion, versions);
960
+ return typeof validated === "string" ? { ok: true, version: validated, versions, picked: false } : validated;
961
+ }
962
+ if (decision.action === "install") {
963
+ return resolveInstallVersion(packageName, versions, prompts);
964
+ }
965
+ if (decision.action === "downgrade") {
966
+ return resolveDowngradeVersion(packageName, stage, decision.deployment, versions, prompts);
967
+ }
968
+ return resolveUpgradeVersion(packageName, stage, decision.deployment, versions, prompts);
969
+ }
970
+ function settleProvisionalAction(action, explicit, versionPicked, version, versions) {
971
+ const settled = reconcileAction(action, version, versions);
972
+ if (!explicit || versionPicked)
973
+ return settled;
974
+ if (settled === action)
975
+ return action;
976
+ return invalidOption(`--action ${action} does not match --package-version ${version}, which is ` + `${settled === "upgrade" ? "newer" : "older"} than the installed version.`, `Pass --action ${settled} for that version, or name the version you meant to ` + `${action} with --package-version.`);
977
+ }
978
+ async function resolveInstallVersion(packageName, versions, prompts) {
979
+ if (versions.length === 0) {
980
+ return failure("unknown-version", `No versions of ${packageName} are published.`, "Check the package name, or ask the package's author whether it has been published.", { result: RESULTS.Failure });
981
+ }
982
+ if (versions.length === 1 || !canPrompt()) {
983
+ return {
984
+ ok: true,
985
+ version: versions[0].version,
986
+ versions,
987
+ picked: false
988
+ };
989
+ }
990
+ const picked = await prompts.pickOne("Version", versions.map((version) => ({
991
+ name: describeVersion(version),
992
+ value: version.version
993
+ })));
994
+ return { ok: true, version: picked, versions, picked: true };
995
+ }
996
+ async function resolveDowngradeVersion(packageName, stage, deployment, versions, prompts) {
997
+ if (!canPrompt()) {
998
+ return missingOption("A version to downgrade to", "--package-version <version>");
999
+ }
1000
+ const older = versions.filter((version) => version.relation === "previous" && version.allowed !== false);
1001
+ if (older.length === 0) {
1002
+ return failure("unknown-version", `${packageName} has no older deployable version at stage "${stage}" ` + `(installed: ${deployment.packageVersion}).`, "Pass --package-version <version> to name an older version, or run " + "--action upgrade instead.", { result: RESULTS.Failure });
1003
+ }
1004
+ const picked = await prompts.pickOne("Version", older.map((version) => ({
1005
+ name: describeVersion(version),
1006
+ value: version.version
1007
+ })));
1008
+ return { ok: true, version: picked, versions, picked: true };
1009
+ }
1010
+ async function resolveUpgradeVersion(packageName, stage, deployment, versions, prompts) {
1011
+ const candidates = versions.filter((version) => version.version !== deployment.packageVersion);
1012
+ if (candidates.length === 0) {
1013
+ return failure("unknown-version", `${packageName} has no other deployable version ` + `(installed: ${deployment.packageVersion}).`, "A newer version has to be published before it can be deployed.", { result: RESULTS.Failure });
1014
+ }
1015
+ if (!canPrompt()) {
1016
+ const upgrade = candidates.find((version) => version.relation === "next" && !isBlockedVersion(version));
1017
+ if (upgrade) {
1018
+ return {
1019
+ ok: true,
1020
+ version: upgrade.version,
1021
+ versions,
1022
+ picked: false
1023
+ };
1024
+ }
1025
+ return failure("unknown-version", `${packageName} is already at the newest deployable version ` + `(${deployment.packageVersion}). Other versions at stage "${stage}": ` + `${candidates.map((v) => describeVersion(v)).join(", ")}.`, "Name one explicitly with --package-version <version> — a downgrade is " + "never chosen automatically.");
1026
+ }
1027
+ const shown = upgradePickerRows(candidates);
1028
+ const picked = await prompts.pickOne("Version", shown.map((version) => ({
1029
+ name: describeVersion(version),
1030
+ value: version.version
1031
+ })));
1032
+ const selected = shown.find((version) => version.version === picked);
1033
+ if (!selected) {
1034
+ return failure("unknown-version", `Unknown version ${picked}.`, "Run the command again and pick a version from the list, or pass " + "--package-version <version>.", { result: RESULTS.ValidationError, errorCode: "invalid_argument" });
1035
+ }
1036
+ if (!isBlockedVersion(selected)) {
1037
+ return { ok: true, version: selected.version, versions, picked: true };
1038
+ }
1039
+ return offerSteppingStone(selected, deployment, versions, prompts);
1040
+ }
1041
+ function upgradePickerRows(candidates) {
1042
+ const shown = candidates.filter((version) => version.relation !== "previous").slice(0, 3);
1043
+ const stone = shown.find((version) => version.requiredVersion)?.requiredVersion;
1044
+ if (stone && shown.every((version) => version.version !== stone)) {
1045
+ const stoneEntry = candidates.find((version) => version.version === stone);
1046
+ if (stoneEntry)
1047
+ shown.push(stoneEntry);
1048
+ }
1049
+ const downgrade = candidates.find((version) => version.relation === "previous");
1050
+ if (downgrade)
1051
+ shown.push(downgrade);
1052
+ return shown;
1053
+ }
1054
+ async function offerSteppingStone(selected, deployment, versions, prompts) {
1055
+ if (!selected.requiredVersion) {
1056
+ return failure("blocked-major-upgrade", `Version ${selected.version} is not deployable from the installed ` + `version ${deployment.packageVersion}.`, "Pick a different version.");
1057
+ }
1058
+ let stone;
1059
+ let required = selected.requiredVersion;
1060
+ const seen = new Set;
1061
+ while (required !== undefined && !seen.has(required)) {
1062
+ seen.add(required);
1063
+ const candidate = versions.find((version) => version.version === required);
1064
+ if (!candidate) {
1065
+ return failure("blocked-major-upgrade", `Upgrading from ${deployment.packageVersion} to ${selected.version} ` + `needs ${required} first, which is not published.`, "That version has to be published before this upgrade can run.");
1066
+ }
1067
+ if (!isBlockedVersion(candidate)) {
1068
+ stone = candidate;
1069
+ break;
1070
+ }
1071
+ required = candidate.requiredVersion;
1072
+ }
1073
+ if (!stone) {
1074
+ return failure("blocked-major-upgrade", `Upgrading from ${deployment.packageVersion} to ${selected.version} ` + "is blocked, and the versions it requires first are blocked too.", "Pick a version the sequential-major rule allows from the installed one.");
1075
+ }
1076
+ const accepted = await prompts.confirmSteppingStone({
1077
+ installed: deployment.packageVersion,
1078
+ wanted: selected.version,
1079
+ stone: stone.version
1080
+ });
1081
+ if (!accepted) {
1082
+ return failure("blocked-major-upgrade", "Deploy canceled: the chosen version cannot be reached in one step.", "Deploy the version the sequential-major rule requires first, then run this " + "command again for the one you wanted.");
1083
+ }
1084
+ return { ok: true, version: stone.version, versions, picked: true };
1085
+ }
1086
+ function describeVersion(version) {
1087
+ if (version.allowed === false && version.requiredVersion) {
1088
+ return `${version.version} (upgrade — blocked: requires ${version.requiredVersion} first)`;
1089
+ }
1090
+ if (version.relation === "current")
1091
+ return `${version.version} (installed)`;
1092
+ if (version.relation === "previous")
1093
+ return `${version.version} (downgrade)`;
1094
+ if (version.relation === "next")
1095
+ return `${version.version} (upgrade)`;
1096
+ return `${version.version} (available)`;
1097
+ }
1098
+ function isBlockedVersion(version) {
1099
+ return version.allowed === false || version.relation === "blocked";
1100
+ }
1101
+ function validateRequestedVersion(requested, versions) {
1102
+ const match = versions.find((version) => version.version === requested);
1103
+ if (!match)
1104
+ return requested;
1105
+ if (isBlockedVersion(match)) {
1106
+ return failure("blocked-major-upgrade", `Version ${requested} cannot be deployed from the installed version.`, match.requiredVersion ? `Majors are breaking changes and cannot be skipped. Deploy ${match.requiredVersion} first: ` + `--package-version ${match.requiredVersion}` : "Majors are breaking changes and cannot be skipped. Deploy the next published major first.");
1107
+ }
1108
+ return requested;
1109
+ }
1110
+ function reconcileAction(action, chosenVersion, versions) {
1111
+ if (action === "install")
1112
+ return action;
1113
+ const match = versions.find((version) => version.version === chosenVersion);
1114
+ if (match?.relation === "previous")
1115
+ return "downgrade";
1116
+ if (match?.relation === "next")
1117
+ return "upgrade";
1118
+ return action;
1119
+ }
1120
+
1121
+ // src/services/target/name-collision.ts
1122
+ var NAME_ATTEMPTS = 10;
1123
+ async function settleNameCollision(input) {
1124
+ const unchanged = {
1125
+ ok: true,
1126
+ action: input.action,
1127
+ deploymentName: input.deploymentName,
1128
+ target: input.target.target,
1129
+ targetLabel: input.target.label
1130
+ };
1131
+ if (input.action !== "install")
1132
+ return unchanged;
1133
+ const lookUp = (candidate) => listDeploymentsNamed(input.config, candidate, input.options.signal);
1134
+ let name = input.deploymentName;
1135
+ const [listError, firstRows] = await catchError(lookUp(name ?? input.defaultName));
1136
+ const aborted = abortedLookup(input.options.signal, listError, "Could not check whether the deployment name is taken");
1137
+ if (aborted)
1138
+ return aborted;
1139
+ if (listError || !firstRows) {
1140
+ if (!allowedToSkipNameCheck(listError, input.config)) {
1141
+ const classified = listError instanceof SolutionDeploymentsError ? listError.errorCode : undefined;
1142
+ const denied = classified === "permission_denied";
1143
+ const needsUserLogin = listError instanceof SolutionDeploymentsError && listError.refusal === "needs-user-session";
1144
+ return failure("service-error", `Could not check whether the deployment name is taken: ${listError instanceof Error ? listError.message : "unknown error"}`, needsUserLogin ? "This server's deployment search reads user logins only, so an " + "application login cannot check the name is free. Run the deploy with " + "a user login ('uip login'), or upgrade the existing deployment with " + "--action upgrade." : denied ? "A fresh install needs to read the Automation Solutions deployments to " + "check the name is free — Automation Solutions adopts a deployment whose " + "name it reuses. Ask for permission to list solution deployments in this " + "tenant, or upgrade the existing deployment with --action upgrade." : "Retry the deploy. If it keeps failing, pass --deployment-name with a name you know is free, or upgrade the existing deployment with --action upgrade.", {
1145
+ errorCode: classified ?? "server_error",
1146
+ retry: denied ? "RetryWillNotFix" : "RetryLater"
1147
+ });
1148
+ }
1149
+ return unchanged;
1150
+ }
1151
+ let deployments = firstRows;
1152
+ for (let attempt = 0;attempt < NAME_ATTEMPTS; attempt += 1) {
1153
+ const effective = name ?? input.defaultName;
1154
+ const newest = newestDeployment(deployments);
1155
+ const held = newest && isCompletedUninstall(newest) ? undefined : newest;
1156
+ if (!held) {
1157
+ return { ...unchanged, deploymentName: name };
1158
+ }
1159
+ const verdict = upgradeInPlaceVerdict(held, input.defaultName, input.packageVersion);
1160
+ if (!canPrompt()) {
1161
+ return nonInteractiveNameConflict(held, effective, verdict, input);
1162
+ }
1163
+ const choice = await askAboutNameCollision(held, effective, verdict, input);
1164
+ if (choice === "abort") {
1165
+ return failure("name-conflict", "Deploy canceled: the deployment name is already taken.", "Pass a --deployment-name that is not in use, or --action upgrade to " + "upgrade the deployment that holds it.");
1166
+ }
1167
+ if (choice === "upgrade" && verdict.ok) {
1168
+ return upgradeHeldDeployment(held, effective, input);
1169
+ }
1170
+ const entered = await input.prompts.askText("New deployment name", await freeNameNear(input.defaultName, lookUp));
1171
+ name = entered?.trim() || undefined;
1172
+ const next = await lookUpAgain(lookUp, name ?? input.defaultName, input);
1173
+ if (!next.ok)
1174
+ return next;
1175
+ deployments = next.rows;
1176
+ }
1177
+ return failure("name-conflict", `Still no free deployment name after ${NAME_ATTEMPTS} tries.`, "Pass one that is not in use with --deployment-name <name>.");
1178
+ }
1179
+ function allowedToSkipNameCheck(error, config) {
1180
+ const code = error instanceof SolutionDeploymentsError ? error.errorCode : undefined;
1181
+ if (code === "permission_denied" || code === "authentication_required") {
1182
+ return isExternalApplication(config);
1183
+ }
1184
+ return code === "configuration_error" && config.sendScopeIds;
1185
+ }
1186
+ function isExternalApplication(config) {
1187
+ if (config.actorType !== "Application")
1188
+ return false;
1189
+ if (config.actorSubType)
1190
+ return config.actorSubType === "service.external";
1191
+ if (config.authFlow) {
1192
+ return config.authFlow === "client_credentials" || config.authFlow === "federated_credentials";
1193
+ }
1194
+ return true;
1195
+ }
1196
+ async function nonInteractiveNameConflict(held, effective, verdict, input) {
1197
+ const parentKey = verdict.ok ? await parentFolderKeyOf(held, input) : undefined;
1198
+ return failure("name-conflict", `A deployment named "${effective}" already exists on this tenant ` + `(${held.packageName} v${held.packageVersion} in ${held.folderPath ?? "an unknown folder"}).` + (verdict.reason ? ` ${verdict.reason}` : ""), verdict.ok ? `Deployment names are unique per tenant. Pass --deployment-name <name> to install ` + `alongside it, or --action upgrade --deployment-name "${held.name}" ` + `--folder-key ${parentKey ?? "<key of the folder it was deployed into>"} to update the one that is already there.` : "Deployment names are unique per tenant. Pass --deployment-name <name> to install alongside it.");
1199
+ }
1200
+ function askAboutNameCollision(held, effective, verdict, input) {
1201
+ return input.prompts.resolveNameCollision({
1202
+ name: effective,
1203
+ solution: held.packageName || "(unknown)",
1204
+ version: held.packageVersion || "(unknown)",
1205
+ folder: held.folderPath ?? held.installedRootFolderKey ?? "(unknown)",
1206
+ status: held.activationStatus ?? held.operationStatus ?? "(unknown)",
1207
+ canUpgradeInPlace: verdict.ok,
1208
+ ...verdict.reason ? { reason: verdict.reason } : {}
1209
+ });
1210
+ }
1211
+ async function upgradeHeldDeployment(held, effective, input) {
1212
+ const parentKey = await parentFolderKeyOf(held, input);
1213
+ if (parentKey === undefined) {
1214
+ return failure("name-conflict", `The deployment "${held.name}" holds this name, but the folder it was deployed into ` + `(${held.folderPath ?? "unknown"}) could not be matched to a folder key.`, `Run the upgrade directly: --action upgrade --deployment-name "${held.name}" ` + "--folder-key <key of that folder>.");
1215
+ }
1216
+ const action = await pivotDirection(input, held);
1217
+ if (typeof action !== "string")
1218
+ return action;
1219
+ writeProgress(` Continuing — this will ${action} the existing deployment "${effective}".`);
1220
+ return {
1221
+ ok: true,
1222
+ action,
1223
+ deploymentName: held.name,
1224
+ target: { folderKey: parentKey },
1225
+ targetLabel: labelFor(input.folderLabels, parentKey)
1226
+ };
1227
+ }
1228
+ async function lookUpAgain(lookUp, name, input) {
1229
+ const [error, rows] = await catchError(lookUp(name));
1230
+ const context = `Could not check whether the deployment name "${name}" is taken`;
1231
+ const aborted = abortedLookup(input.options.signal, error, context);
1232
+ if (aborted)
1233
+ return aborted;
1234
+ if (error || !rows) {
1235
+ return failure("service-error", `${context}: ${error instanceof Error ? error.message : "unknown error"}`, "Retry the deploy. If it keeps failing, pass --deployment-name with a name you know is free.", {
1236
+ errorCode: error instanceof SolutionDeploymentsError ? error.errorCode ?? "server_error" : "server_error",
1237
+ retry: "RetryLater"
1238
+ });
1239
+ }
1240
+ return { ok: true, rows };
1241
+ }
1242
+ async function freeNameNear(base, lookUp) {
1243
+ for (let suffix = 2;suffix < 2 + NAME_ATTEMPTS; suffix += 1) {
1244
+ const candidate = `${base}-${suffix}`;
1245
+ const [error, rows] = await catchError(lookUp(candidate));
1246
+ if (error || !rows)
1247
+ break;
1248
+ if (rows.length === 0)
1249
+ return candidate;
1250
+ }
1251
+ return `${base}-2`;
1252
+ }
1253
+ async function parentFolderKeyOf(held, input) {
1254
+ if (!held.folderPath)
1255
+ return;
1256
+ const wanted = held.folderPath.toLowerCase();
1257
+ const inLabels = keyForPath(input.folderLabels, wanted);
1258
+ if (inLabels)
1259
+ return inLabels;
1260
+ const loaded = await loadFolderLabels(input.config, input.listFolders, input.options.signal);
1261
+ if (!loaded.ok)
1262
+ return;
1263
+ for (const [key, path] of loaded.labels)
1264
+ input.folderLabels.set(key, path);
1265
+ return keyForPath(loaded.labels, wanted);
1266
+ }
1267
+ function upgradeInPlaceVerdict(held, solutionName, packageVersion) {
1268
+ const theirs = held.packageName.trim().toLowerCase();
1269
+ const ours = solutionName.trim().toLowerCase();
1270
+ if (theirs && ours && theirs !== ours) {
1271
+ return {
1272
+ ok: false,
1273
+ reason: `It belongs to a different solution ("${held.packageName}") — it cannot be upgraded to this package.`
1274
+ };
1275
+ }
1276
+ const version = held.packageVersion.trim();
1277
+ if (!version) {
1278
+ return {
1279
+ ok: false,
1280
+ reason: "Its current version is unknown — it cannot be upgraded from here."
1281
+ };
1282
+ }
1283
+ if (version === packageVersion) {
1284
+ return {
1285
+ ok: false,
1286
+ reason: `It is already at ${packageVersion} — there is nothing to upgrade.`
1287
+ };
1288
+ }
1289
+ if (!isInstalledDeployment(held) || !held.installedRootFolderKey) {
1290
+ return {
1291
+ ok: false,
1292
+ reason: "It never finished installing — it cannot be upgraded. Remove it in " + "Automation Solutions, or deploy under a different name."
1293
+ };
1294
+ }
1295
+ return { ok: true };
1296
+ }
1297
+ async function pivotDirection(input, held) {
1298
+ const [error, nav] = await catchError(listPackageVersions(input.config, input.packageName, {
1299
+ stage: input.stage,
1300
+ anchorVersion: held.packageVersion,
1301
+ signal: input.options.signal
1302
+ }));
1303
+ const aborted = abortedLookup(input.options.signal, error, "Could not list package versions");
1304
+ if (aborted)
1305
+ return aborted;
1306
+ if (error || !nav) {
1307
+ return toDeployFailure(error, `Could not list the versions of ${input.packageName} to tell an upgrade ` + "from a downgrade");
1308
+ }
1309
+ const versions = normalizeVersions(nav);
1310
+ const match = versions.find((version) => version.version === input.packageVersion);
1311
+ if (!match) {
1312
+ writeProgress(" Could not tell whether this is an upgrade or a downgrade — submitting it as " + "an upgrade (the service validates the transition).");
1313
+ return "upgrade";
1314
+ }
1315
+ const gate = validateRequestedVersion(match.version, versions);
1316
+ if (typeof gate !== "string")
1317
+ return gate;
1318
+ return match.relation === "previous" ? "downgrade" : "upgrade";
1319
+ }
1320
+
1321
+ // src/services/target/package-name.ts
1322
+ async function resolvePackageName(config, options, prompts, stage) {
1323
+ if (options.packageName)
1324
+ return options.packageName;
1325
+ if (!canPrompt()) {
1326
+ return missingOption("A package name", "--package-name <name>");
1327
+ }
1328
+ const vertical = await resolveVertical(config, options, prompts);
1329
+ if (typeof vertical !== "string")
1330
+ return vertical;
1331
+ const [error, packages] = await catchError(listPackages(config, vertical, stage, options.signal));
1332
+ if (error)
1333
+ return toDeployFailure(error, "Could not list packages");
1334
+ if (!packages || packages.length === 0) {
1335
+ return failure("unknown-package", `No packages are published for vertical "${vertical}".`, "Check the vertical name with --vertical, or ask the package's author whether it has been published.");
1336
+ }
1337
+ return prompts.pickOne("Package", packages.map((pkg) => ({
1338
+ name: pkg.currentVersion ? `${pkg.displayName ?? pkg.packageName} (latest: ${pkg.currentVersion})` : pkg.displayName ?? pkg.packageName,
1339
+ value: pkg.packageName
1340
+ })));
1341
+ }
1342
+ async function resolveVertical(config, options, prompts) {
1343
+ const [error, verticals] = await catchError(listVerticals(config, options.signal));
1344
+ if (error)
1345
+ return toDeployFailure(error, "Could not list verticals");
1346
+ const available = verticals ?? [];
1347
+ if (options.vertical) {
1348
+ const matched = available.find((candidate) => candidate.toLowerCase() === options.vertical?.toLowerCase());
1349
+ if (!matched) {
1350
+ return invalidOption(`Unknown vertical "${options.vertical}".`, available.length > 0 ? `Available verticals: ${available.join(", ")}.` : "The service reports no verticals for this tenant.");
1351
+ }
1352
+ return matched;
1353
+ }
1354
+ if (available.length === 0) {
1355
+ return failure("unknown-package", "The service reports no verticals for this tenant.", "Check that the tenant is onboarded to Vertical Solutions.");
1356
+ }
1357
+ if (available.length === 1)
1358
+ return available[0];
1359
+ return prompts.pickOne("Vertical", available.map((vertical) => ({ name: vertical, value: vertical })));
1360
+ }
1361
+
1362
+ // src/services/target/target-folder.ts
1363
+ async function resolveTargetFolder(config, decision, options, prompts, listFolders, folderIndex) {
1364
+ const folderLabels = folderIndex.labels;
1365
+ if (decision.action !== "install") {
1366
+ const key = decision.deployment.folderKey;
1367
+ return {
1368
+ ok: true,
1369
+ target: { folderKey: key },
1370
+ label: labelFor(folderLabels, key)
1371
+ };
1372
+ }
1373
+ if (options.folderPath !== undefined) {
1374
+ if (!options.folderPath.trim()) {
1375
+ return invalidOption("--folder-path cannot be empty.", "Pass a folder path or use --folder-key <guid>.");
1376
+ }
1377
+ return resolveFolderPath(config, options, listFolders, folderIndex);
1378
+ }
1379
+ const folderKey = options.folderKey?.trim();
1380
+ if (options.folderKey !== undefined && !folderKey) {
1381
+ return invalidOption("--folder-key cannot be empty.", "Drop --folder-key, or pass the folder's key.");
1382
+ }
1383
+ if (folderKey) {
1384
+ return folderByKey(folderKey, folderIndex);
1385
+ }
1386
+ if (!canPrompt()) {
1387
+ return missingOption("A target folder", "--folder-path <path> or --folder-key <guid>");
1388
+ }
1389
+ const [error, folders] = folderIndex.read ? [undefined, folderIndex.folders] : await catchError(listFolders(config, options.signal));
1390
+ const aborted = abortedLookup(options.signal, error, FOLDER_LIST_CONTEXT);
1391
+ if (aborted)
1392
+ return aborted;
1393
+ if (error || !folders) {
1394
+ const typed = await prompts.askText("Target folder name (could not list folders)");
1395
+ return typed?.trim() ? {
1396
+ ok: true,
1397
+ target: { folderName: typed.trim() },
1398
+ label: typed.trim()
1399
+ } : missingOption("A target folder", "--folder-path <path> or --folder-key <guid>");
1400
+ }
1401
+ const selectable = folders.filter((folder) => folder.kind !== "Solution");
1402
+ if (selectable.length === 0) {
1403
+ return failure("invalid-option", "This tenant has no Orchestrator folder to install into.", "Create a folder in Orchestrator first, then re-run the deploy.");
1404
+ }
1405
+ const picked = await prompts.pickOne("Target folder", selectable.map((folder) => ({ name: folder.path, value: folder.key })));
1406
+ const chosen = selectable.find((folder) => folder.key === picked);
1407
+ return {
1408
+ ok: true,
1409
+ target: { folderKey: picked },
1410
+ label: chosen?.path ?? picked
1411
+ };
1412
+ }
1413
+ function folderByKey(folderKey, folderIndex) {
1414
+ const folders = folderIndex.folders;
1415
+ if (!folders) {
1416
+ writeProgress(` Could not read the Orchestrator folders to check --folder-key ${folderKey}; ` + "sending it as given.");
1417
+ return {
1418
+ ok: true,
1419
+ target: { folderKey },
1420
+ label: labelFor(folderIndex.labels, folderKey)
1421
+ };
1422
+ }
1423
+ const match = folders.find((folder) => sameKey(folder.key, folderKey));
1424
+ if (!match) {
1425
+ return invalidOption(`--folder-key ${folderKey} matches no Orchestrator folder in this tenant.`, "Pass the key of an existing folder, or name it with --folder-path <path>.");
1426
+ }
1427
+ if (match.kind === "Solution") {
1428
+ return invalidOption(`--folder-key ${folderKey} is the Solution folder "${match.path}", which ` + "Automation Solutions owns.", "Pass the folder the solution should install UNDER — a Solution folder is " + "created beneath it.");
1429
+ }
1430
+ return { ok: true, target: { folderKey: match.key }, label: match.path };
1431
+ }
1432
+ async function resolveFolderPath(config, options, listFolders, folderIndex) {
1433
+ const wanted = (options.folderPath ?? "").trim();
1434
+ let folders = folderIndex.folders;
1435
+ let error;
1436
+ if (!folders && folderIndex.read) {
1437
+ return unreadableFolderPath(wanted, options, undefined);
1438
+ }
1439
+ if (!folders) {
1440
+ [error, folders] = await catchError(listFolders(config, options.signal));
1441
+ const aborted = abortedLookup(options.signal, error, FOLDER_LIST_CONTEXT);
1442
+ if (aborted)
1443
+ return aborted;
1444
+ }
1445
+ if (error || !folders) {
1446
+ return unreadableFolderPath(wanted, options, error);
1447
+ }
1448
+ const lower = wanted.toLowerCase();
1449
+ const byPath = folders.filter((folder) => folder.path.toLowerCase() === lower);
1450
+ const matches = byPath.length > 0 ? byPath : folders.filter((folder) => folder.displayName.toLowerCase() === lower);
1451
+ if (matches.length === 0) {
1452
+ return invalidOption(`--folder-path ${wanted} matches no Orchestrator folder in this tenant.`, "Pass the full path as Orchestrator shows it (for example Shared/Production), " + "or the folder's key with --folder-key <guid>.");
1453
+ }
1454
+ if (matches.length > 1) {
1455
+ return invalidOption(`--folder-path ${wanted} matches ${matches.length} folders: ` + `${matches.map((folder) => folder.path).join(", ")}.`, "Pass the full path of the one you mean, or --folder-key <guid>.");
1456
+ }
1457
+ const match = matches[0];
1458
+ if (options.folderKey && !sameKey(match.key, options.folderKey)) {
1459
+ return invalidOption(`--folder-path ${wanted} (${match.key}) and --folder-key ${options.folderKey} ` + "name different folders.", "Drop one of the two, or correct it so both point at the same folder.");
1460
+ }
1461
+ if (match.kind === "Solution") {
1462
+ return invalidOption(`--folder-path ${wanted} is a Solution folder, which Automation Solutions owns.`, "Pass the folder the solution should install under — a Solution folder is created " + "beneath it.");
1463
+ }
1464
+ return { ok: true, target: { folderKey: match.key }, label: match.path };
1465
+ }
1466
+ function unreadableFolderPath(wanted, options, error) {
1467
+ const reason = error?.message ?? "unknown error";
1468
+ if (options.folderKey) {
1469
+ writeProgress(` Could not read the Orchestrator folders to check --folder-path ${wanted} ` + `against --folder-key ${options.folderKey} (${reason}); using the key.`);
1470
+ return {
1471
+ ok: true,
1472
+ target: { folderKey: options.folderKey },
1473
+ label: wanted
1474
+ };
1475
+ }
1476
+ if (!wanted.includes("/")) {
1477
+ writeProgress(` Could not read the Orchestrator folders to check --folder-path ${wanted} ` + `(${reason}); sending it to the service as a folder name.`);
1478
+ return { ok: true, target: { folderName: wanted }, label: wanted };
1479
+ }
1480
+ return failure("service-error", `Could not read the Orchestrator folders to resolve --folder-path ${wanted}: ${reason}`, "Pass the folder's key with --folder-key <guid> instead, or give this login the " + "right to read Orchestrator folders.", { errorCode: "server_error", retry: "RetryLater" });
1481
+ }
1482
+
1483
+ // src/services/resolve-target.ts
1484
+ var DEFAULT_STAGE = "stable";
1485
+ async function resolveDeployTarget(config, options, prompts, listFolders = listAllFolders) {
1486
+ const stage = options.stage ?? DEFAULT_STAGE;
1487
+ const packageName = await resolvePackageName(config, options, prompts, stage);
1488
+ if (typeof packageName !== "string")
1489
+ return packageName;
1490
+ const [lookupError, existingAll] = await catchError(findCurrentDeployments(config, packageName, options.signal));
1491
+ if (lookupError) {
1492
+ return toDeployFailure(lookupError, "Could not list current deployments");
1493
+ }
1494
+ const existing = (existingAll ?? []).filter((deployment) => deployment.status === "Installed");
1495
+ const folderIndex = existing.length > 0 || options.folderKey !== undefined ? await loadFolderLabels(config, listFolders, options.signal) : { ok: true, labels: new Map, read: false };
1496
+ if (!folderIndex.ok)
1497
+ return folderIndex;
1498
+ const folderLabels = folderIndex.labels;
1499
+ const decision = await resolveAction(existing, options, prompts, folderIndex);
1500
+ if (!decision.ok)
1501
+ return decision;
1502
+ const target = await resolveTargetFolder(config, decision, options, prompts, listFolders, folderIndex);
1503
+ if (!target.ok)
1504
+ return target;
1505
+ const resolved = await resolveVersion(config, packageName, stage, decision, options, prompts);
1506
+ if (!resolved.ok)
1507
+ return resolved;
1508
+ const { version, versions, picked: versionPicked } = resolved;
1509
+ const [manifestError, manifest] = await catchError(getManifest(config, packageName, version, options.signal));
1510
+ if (manifestError || !manifest) {
1511
+ return toDeployFailure(manifestError, `Could not read the manifest of ${packageName} ${version}`);
1512
+ }
1513
+ const provisionalAction = settleProvisionalAction(decision.action, decision.explicit, versionPicked, version, versions);
1514
+ if (typeof provisionalAction !== "string")
1515
+ return provisionalAction;
1516
+ const defaultName = solutionNameFromManifest(manifest) || packageName;
1517
+ const named = provisionalAction === "install" ? await resolveDeploymentName(defaultName, target.label, options, prompts) : upgradeName(decision.deployment, options);
1518
+ const settled = await settleNameCollision({
1519
+ config,
1520
+ existing,
1521
+ listFolders,
1522
+ action: provisionalAction,
1523
+ deploymentName: named,
1524
+ defaultName,
1525
+ packageName,
1526
+ packageVersion: version,
1527
+ stage,
1528
+ target,
1529
+ folderLabels,
1530
+ options,
1531
+ prompts
1532
+ });
1533
+ if (!settled.ok)
1534
+ return settled;
1535
+ return {
1536
+ ok: true,
1537
+ packageName,
1538
+ packageVersion: version,
1539
+ action: settled.action,
1540
+ target: settled.target,
1541
+ targetLabel: settled.targetLabel,
1542
+ manifest,
1543
+ existing: decision.deployment,
1544
+ deploymentName: settled.deploymentName
1545
+ };
1546
+ }
1547
+
1548
+ // src/services/deploy/prepare-submit.ts
1549
+ async function prepareSubmit(config, options, deps) {
1550
+ const target = await resolveDeployTarget(config, options, deps.target);
1551
+ if (!target.ok)
1552
+ return target;
1553
+ const tenantResources = findTenantResources(target.manifest);
1554
+ if (tenantResources.length > 0) {
1555
+ return failure("tenant-resources-declared", `${target.packageName} ${target.packageVersion} provisions tenant-level resources, ` + `which 'uip vss deploy' does not deploy:
1556
+ ` + describeTenantResources(tenantResources), "Deploying it here would leave those resources unprovisioned while reporting success. " + "Use the vertical-deploy-cli for this package until tenant-level resources are supported.");
1557
+ }
1558
+ const collected = await collectConfigurations(target.manifest, { config: options.config, configFile: options.configFile }, deps.configuration);
1559
+ if (!collected.ok)
1560
+ return collected;
1561
+ for (const warning of collected.warnings)
1562
+ writeWarning(warning);
1563
+ const resourceConfigurations = await buildDeployConfigPayload(options, target.manifest, target.action === "install", deps);
1564
+ if (!resourceConfigurations.ok)
1565
+ return resourceConfigurations;
1566
+ const request = {
1567
+ packageName: target.packageName,
1568
+ packageVersion: target.packageVersion,
1569
+ action: target.action,
1570
+ target: target.target
1571
+ };
1572
+ if (Object.keys(collected.configurations).length > 0) {
1573
+ request.configurations = collected.configurations;
1574
+ }
1575
+ if (resourceConfigurations.value.payload.length > 0) {
1576
+ request.resourceConfigurations = resourceConfigurations.value.payload;
1577
+ }
1578
+ if (target.deploymentName)
1579
+ request.deploymentName = target.deploymentName;
1580
+ addIdentityFields(request, config);
1581
+ return {
1582
+ ok: true,
1583
+ request,
1584
+ manifest: target.manifest,
1585
+ targetLabel: target.targetLabel,
1586
+ resources: resourceConfigurations.value.sources
1587
+ };
1588
+ }
1589
+ function addIdentityFields(request, config) {
1590
+ if (config.actor)
1591
+ request.uploadedBy = config.actor;
1592
+ if (config.organizationName) {
1593
+ request.organizationName = config.organizationName;
1594
+ }
1595
+ if (config.tenantName)
1596
+ request.tenantName = config.tenantName;
1597
+ if (!config.sendScopeIds)
1598
+ return;
1599
+ if (config.organizationId)
1600
+ request.organizationId = config.organizationId;
1601
+ if (config.tenantId)
1602
+ request.tenantId = config.tenantId;
1603
+ }
1604
+ async function buildDeployConfigPayload(options, manifest, isFreshInstall, deps) {
1605
+ const [discoverError, path] = await catchError(discoverDeployConfig(options.resourceConfig));
1606
+ if (discoverError) {
1607
+ return invalidOption(discoverError.message, "Check the path, or drop --resource-config to use ./deploy-config.json when it exists.");
1608
+ }
1609
+ let file;
1610
+ if (path !== undefined) {
1611
+ const [loadError, loaded] = await catchError(loadDeployConfig(path));
1612
+ if (loadError || !loaded) {
1613
+ return invalidOption(loadError?.message ?? `Could not read ${path}.`, "Fix the file, or point --resource-config at a different one.");
1614
+ }
1615
+ file = loaded;
1616
+ }
1617
+ const collected = await collectDeclaredConfiguration({
1618
+ manifest,
1619
+ file,
1620
+ isFreshInstall,
1621
+ prompts: deps.configuration
1622
+ });
1623
+ for (const warning of collected.warnings)
1624
+ writeWarning(warning);
1625
+ return {
1626
+ ok: true,
1627
+ value: {
1628
+ payload: collected.payload,
1629
+ sources: { path: file?.path, fromFile: collected.fromFile }
1630
+ }
1631
+ };
1632
+ }
1633
+ function summarize(prepared) {
1634
+ const { request, targetLabel, resources } = prepared;
1635
+ const configured = Object.keys(request.configurations ?? {});
1636
+ const lines = [
1637
+ `Package: ${request.packageName} ${request.packageVersion}`,
1638
+ `Action: ${request.action ?? "install"}`,
1639
+ `Target: folder "${targetLabel}"`
1640
+ ];
1641
+ if (request.deploymentName) {
1642
+ lines.push(`Name: ${request.deploymentName}`);
1643
+ }
1644
+ if (configured.length > 0) {
1645
+ lines.push(`Config: ${configured.join(", ")}`);
1646
+ }
1647
+ for (const resource of request.resourceConfigurations ?? []) {
1648
+ const supplied = resources.fromFile.get(resource.name.toLowerCase()) ?? new Set;
1649
+ const keys = Object.keys(resource.configuration);
1650
+ const fromFile = keys.filter((key) => supplied.has(key));
1651
+ let source = "";
1652
+ if (resources.path && fromFile.length === keys.length) {
1653
+ source = ` (from ${resources.path})`;
1654
+ } else if (resources.path && fromFile.length > 0) {
1655
+ source = ` (${fromFile.join(", ")} from ${resources.path}; the rest typed)`;
1656
+ }
1657
+ lines.push(`Resource: ${resource.name}: ${keys.join(", ")}${source}`);
1658
+ }
1659
+ return lines.join(`
1660
+ `);
1661
+ }
1662
+
1663
+ // src/services/deploy/watch-to-completion.ts
1664
+ async function watchToCompletion(config, job, options, deps, submitted) {
1665
+ const { serviceUrl, manifest, sentResourceConfigurations, deploymentName } = submitted;
1666
+ const routing = routingFlags(options);
1667
+ const outcome = await watchDeployJob(watchOptionsFrom(options, {
1668
+ jobId: job.id,
1669
+ config,
1670
+ serviceUrl,
1671
+ prompt: deps.watch,
1672
+ sentResourceConfigurations
1673
+ }));
1674
+ switch (outcome.kind) {
1675
+ case "detached":
1676
+ case "timeout":
1677
+ case "lost-contact":
1678
+ return stoppedWatching(outcome, job.id, options.timeout, options);
1679
+ case "succeeded": {
1680
+ writeSuccessSummary(outcome.job, outcome.lastLogMessage);
1681
+ const [extraError, extra] = await catchError(finalizeSucceededJob({
1682
+ config: outcome.config ?? config,
1683
+ job: outcome.job,
1684
+ deploymentName,
1685
+ manifest,
1686
+ options,
1687
+ deps,
1688
+ serviceUrl
1689
+ }));
1690
+ if (extraError) {
1691
+ writeWarning(`The deploy succeeded. Preparing the seed workspace afterwards failed ` + `(${extraError.message}), so nothing was seeded: run 'uip vss deploy seed' ` + "when you are ready.");
1692
+ }
1693
+ return {
1694
+ ok: true,
1695
+ outcome: "succeeded",
1696
+ job: outcome.job,
1697
+ failedSteps: outcome.failedSteps,
1698
+ ...extra ?? {}
1699
+ };
1700
+ }
1701
+ case "paused":
1702
+ return failure("job-paused", `Job ${job.id} is paused: it needs ${describePauseKind(outcome.actionKind)}.` + (outcome.url ? `
1703
+ ${forDisplay(outcome.url)}` : ""), pauseInstructions(outcome.actionKind, `uip vss deploy resume ${job.id}${routing}`), { errorCode: pauseErrorCode(outcome.actionKind) });
1704
+ case "failed":
1705
+ return failure("job-failed", `Job ${job.id} failed` + (outcome.job.error?.step ? ` at step ${forDisplay(outcome.job.error.step)}` : "") + `: ${forDisplayIfSet(outcome.job.error?.message) ?? "unknown error"}`, `The job is checkpointed. Retry from the last successful step with: uip vss deploy resume ${job.id}${routing}`);
1706
+ case "canceled":
1707
+ writeProgress(`
1708
+ Deployment canceled.`);
1709
+ writeStepSummary(outcome.job);
1710
+ return failure("job-canceled", `Job ${job.id} was canceled.`, `Start again with a new deploy, or inspect it with: uip vss deploy status ${job.id}${routing}`);
1711
+ case "rejected":
1712
+ return toDeployFailure(outcome.error, `Could not ${outcome.attempting ?? "read"} job ${job.id}`, {
1713
+ notFound: "job",
1714
+ attempting: outcome.attempting ?? "read",
1715
+ jobId: job.id,
1716
+ routing: options
1717
+ });
1718
+ }
1719
+ }
1720
+
1721
+ // src/services/deploy-service.ts
1722
+ async function runDeploy(options, deps) {
1723
+ const blank = rejectBlankOptions(options);
1724
+ if (blank)
1725
+ return blank;
1726
+ const serviceUrl = readServiceUrl(options.serviceUrl, options.tenant);
1727
+ if (!serviceUrl.ok)
1728
+ return serviceUrl;
1729
+ const seedWithoutWait = rejectSeedWithoutWait(options);
1730
+ if (seedWithoutWait)
1731
+ return seedWithoutWait;
1732
+ const [authError, config] = await catchError(createVssConfig({
1733
+ tenant: options.tenant,
1734
+ loginValidity: options.loginValidity,
1735
+ serviceUrl: serviceUrl.value
1736
+ }));
1737
+ if (authError || !config)
1738
+ return credentialsFailure(authError);
1739
+ const prepared = await prepareSubmit(config, options, deps);
1740
+ if (!prepared.ok)
1741
+ return prepared;
1742
+ const { request, manifest } = prepared;
1743
+ if (options.yes !== true) {
1744
+ if (!canPrompt()) {
1745
+ return failure("missing-option", "Confirmation is required before a deploy runs.", "Re-run with --yes to confirm, or run from an interactive terminal to be asked.", {
1746
+ result: RESULTS.ValidationError,
1747
+ errorCode: "invalid_argument"
1748
+ });
1749
+ }
1750
+ if (!await deps.confirmProceed(summarize(prepared))) {
1751
+ return failure("declined", "Deploy canceled before submitting.", "Nothing was sent to the service. Run the command again when the " + "summary is what you want, or pass --yes to skip this question.");
1752
+ }
1753
+ }
1754
+ const [submitError, job] = await catchError(withCapacityRetry(() => submitDeployJob(config, request, options.signal), "the submit", undefined, options.signal));
1755
+ if (submitError) {
1756
+ if (options.signal?.aborted === true || !(submitError instanceof VssApiError)) {
1757
+ return failure("lost-contact", `The submit of ${request.packageName} ${request.packageVersion} ${options.signal?.aborted === true ? "was interrupted before the service answered" : `did not get an answer the CLI could read (${submitError.message})`}, so it is not known whether a deploy job was created.`, checkBeforeResubmitting(options));
1758
+ }
1759
+ return toDeployFailure(submitError, "Could not submit the deploy job", {
1760
+ routing: options,
1761
+ attempting: "submit"
1762
+ });
1763
+ }
1764
+ writeProgress(`Deploy job ${job.id} accepted and running.`);
1765
+ if (options.wait === false) {
1766
+ return { ok: true, outcome: "submitted", job };
1767
+ }
1768
+ return watchToCompletion(config, job, options, deps, {
1769
+ serviceUrl: serviceUrl.value,
1770
+ manifest,
1771
+ sentResourceConfigurations: (request.resourceConfigurations ?? []).length > 0,
1772
+ deploymentName: request.deploymentName
1773
+ });
1774
+ }
1775
+ export {
1776
+ runDeploy
1777
+ };
1778
+
1779
+ //# debugId=C7ED42996FA9E56D64756E2164756E21