@uipath/orchestrator-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.
@@ -16,6 +16,7 @@ import {
16
16
  FoldersNavigationApi,
17
17
  HttpTriggersApi,
18
18
  InvalidArgumentError,
19
+ JobAttachmentsApi,
19
20
  JobDtoToJSON,
20
21
  JobsApi,
21
22
  LibrariesApi,
@@ -51,12 +52,12 @@ import {
51
52
  TriggerLogsApi,
52
53
  UsersApi,
53
54
  WebhooksApi,
55
+ addDeprecatedOptionAlias,
54
56
  carriedErrorCode,
55
57
  carriedRetry,
56
58
  catchError,
57
59
  createApiClient,
58
60
  createHiddenDeprecatedTenantOption,
59
- createOrchestratorConfig,
60
61
  extractErrorDetails,
61
62
  extractErrorMessage,
62
63
  extractErrorMessageSync,
@@ -64,6 +65,7 @@ import {
64
65
  isGuid,
65
66
  isPreviewBuild,
66
67
  logger,
68
+ nullArrayToEmptyMiddleware,
67
69
  parseHttpStatusFromMessage,
68
70
  pollUntil,
69
71
  processContext,
@@ -78,10 +80,10 @@ import {
78
80
  trackShipSucceeded,
79
81
  unzipSync,
80
82
  uploadJobAttachment
81
- } from "./tool-ckjbpn0a.js";
83
+ } from "./tool-qeyyg5yr.js";
82
84
  import {
83
85
  getFileSystem
84
- } from "./tool-ayyhwvs8.js";
86
+ } from "./tool-6mw7b49q.js";
85
87
  import {
86
88
  __require
87
89
  } from "./tool-1de529jm.js";
@@ -90,7 +92,7 @@ var package_default = {
90
92
  name: "@uipath/orchestrator-tool",
91
93
  author: "UiPath",
92
94
  license: "SEE LICENSE IN LICENSE.txt",
93
- version: "1.204.0",
95
+ version: "1.205.0-preview.258",
94
96
  description: "Manage Orchestrator folders, jobs, processes, and releases.",
95
97
  private: false,
96
98
  repository: {
@@ -1299,7 +1301,7 @@ async function resolveJob(jobKey, options) {
1299
1301
  var ATTACHMENTS_DOWNLOAD_EXAMPLES = [
1300
1302
  {
1301
1303
  Description: "Download an attachment to a specific output path",
1302
- Command: "uip or attachments download a1b2c3d4-0000-0000-0000-000000000001 --output ./report.pdf",
1304
+ Command: "uip or attachments download a1b2c3d4-0000-0000-0000-000000000001 --destination ./report.pdf",
1303
1305
  Output: {
1304
1306
  Code: "AttachmentDownloaded",
1305
1307
  Data: {
@@ -1335,7 +1337,7 @@ var ATTACHMENTS_LIST_EXAMPLES = [
1335
1337
  ];
1336
1338
  var registerAttachmentsCommand = (program) => {
1337
1339
  const attachments = program.command("attachments").description("Manage job attachments. " + "Download files attached to jobs (screenshots, reports, data files). " + "Attachments are tenant-scoped — no folder context needed.");
1338
- attachments.command("download").description("Download a job attachment to local disk. " + "Uses the DownloadAttachmentFile endpoint which returns a redirect to the blob storage URI. " + "Use job 'get' output to find attachment IDs.").argument("<attachment-id>", "Attachment ID (GUID)").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("-o, --output <path>", "Output file path (defaults to attachment-<id> in current directory)").examples(ATTACHMENTS_DOWNLOAD_EXAMPLES).trackedAction(processContext, async (attachmentId, options) => {
1340
+ const download = attachments.command("download").description("Download a job attachment to local disk. " + "Uses the DownloadAttachmentFile endpoint which returns a redirect to the blob storage URI. " + "Use job 'get' output to find attachment IDs.").argument("<attachment-id>", "Attachment ID (GUID)").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("-d, --destination <path>", "Output file path (defaults to attachment-<id> in current directory)").examples(ATTACHMENTS_DOWNLOAD_EXAMPLES).trackedAction(processContext, async (attachmentId, options) => {
1339
1341
  const [apiError, api] = await catchError(createApiClient(AttachmentsApi, {
1340
1342
  tenant: options.tenant
1341
1343
  }));
@@ -1394,8 +1396,8 @@ var registerAttachmentsCommand = (program) => {
1394
1396
  processContext.exit(1);
1395
1397
  return;
1396
1398
  }
1397
- const fileName = options.output || `attachment-${attachmentId}`;
1398
- const { getFileSystem: getFileSystem2 } = await import("./index-r2vc4whd.js");
1399
+ const fileName = options.destination || `attachment-${attachmentId}`;
1400
+ const { getFileSystem: getFileSystem2 } = await import("./index-rkqk9b39.js");
1399
1401
  const fs = getFileSystem2();
1400
1402
  const [writeError] = await catchError(fs.writeFile(fileName, Buffer.from(buffer)));
1401
1403
  if (writeError) {
@@ -1417,38 +1419,32 @@ var registerAttachmentsCommand = (program) => {
1417
1419
  }
1418
1420
  });
1419
1421
  });
1422
+ addDeprecatedOptionAlias(download, "-o, --output <path>", "--destination");
1420
1423
  attachments.command("list").description("List attachments for a job. " + "Shows attachment ID, name, and category. " + "Use the attachment ID with 'attachments download' or 'jobs start --attachment-id'.").requiredOption("--job-key <key>", "Job key (GUID) — list attachments for this job").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--all-fields", "Return all fields from the API response (default: curated subset)").examples(ATTACHMENTS_LIST_EXAMPLES).trackedAction(processContext, async (options) => {
1421
1424
  const resolved = await resolveOrReportError(resolveJob(options.jobKey, { tenant: options.tenant }), "Error listing attachments");
1422
1425
  if (resolved === null)
1423
1426
  return;
1424
- const [cfgError, config] = await catchError(createOrchestratorConfig({
1427
+ const [apiError, api] = await catchError(createApiClient(JobAttachmentsApi, {
1425
1428
  tenant: options.tenant,
1426
1429
  folderId: resolved.folderId.toString()
1427
1430
  }));
1428
- if (cfgError) {
1431
+ if (apiError) {
1429
1432
  OutputFormatter.error({
1430
1433
  Result: RESULTS.Failure,
1431
1434
  Message: "Error listing attachments",
1432
- Instructions: cfgError.message
1435
+ Instructions: apiError.message
1433
1436
  });
1434
1437
  processContext.exit(1);
1435
1438
  return;
1436
1439
  }
1437
- const headers = {
1438
- ...config.headers
1439
- };
1440
- const [error, response] = await catchError(fetch(`${config.basePath}/api/JobAttachments/GetByJobKey?jobKey=${encodeURIComponent(options.jobKey)}`, { headers }));
1441
- if (error || !response.ok) {
1442
- const msg = error ? error.message : `HTTP ${response.status}: ${response.statusText}`;
1443
- OutputFormatter.error({
1444
- Result: RESULTS.Failure,
1445
- Message: "Error listing attachments",
1446
- Instructions: msg
1447
- });
1448
- processContext.exit(1);
1440
+ const [error, response] = await catchError(api.withMiddleware(nullArrayToEmptyMiddleware).jobAttachmentsGetByJobKeyRaw({
1441
+ jobKey: options.jobKey
1442
+ }));
1443
+ if (error) {
1444
+ await reportFailure("Error listing attachments", error);
1449
1445
  return;
1450
1446
  }
1451
- const [parseError, items] = await catchError(response.json());
1447
+ const [parseError, items] = await catchError(response.raw.json());
1452
1448
  if (parseError) {
1453
1449
  OutputFormatter.error({
1454
1450
  Result: RESULTS.Failure,
@@ -3447,6 +3443,7 @@ var registerFeedsCommand = (program) => {
3447
3443
  };
3448
3444
 
3449
3445
  // src/commands/folders.ts
3446
+ var VALID_RUNTIMES_PROCESS_TYPES = ["Process", "Agent", "Function", "Api"];
3450
3447
  var FOLDERS_LIST_EXAMPLES = [
3451
3448
  {
3452
3449
  Description: "List folders in the tenant",
@@ -3944,7 +3941,14 @@ var registerFoldersCommand = (program) => {
3944
3941
  }
3945
3942
  });
3946
3943
  });
3947
- folders.command("runtimes").description("List runtime allocations for a folder. " + "Shows how many runtime slots of each type (Unattended, Attended, NonProduction, etc.) " + "are total, connected, and available in the specified folder. " + "Useful for checking capacity before starting jobs.").argument("<folder-key-or-path>", "Folder key (GUID) or path — use 'folders list' to find").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).examples(FOLDERS_RUNTIMES_EXAMPLES).trackedAction(processContext, async (folderKeyArg, options) => {
3944
+ folders.command("runtimes").description("List runtime allocations for a folder. " + "Shows how many runtime slots of each type (Unattended, Attended, NonProduction, etc.) " + "are total, connected, and available in the specified folder. " + "Useful for checking capacity before starting jobs.").argument("<folder-key-or-path>", "Folder key (GUID) or path — use 'folders list' to find").addOption(new Option("--process-type <type>", "Report the runtimes a job of this process type can use. The Local row then counts only that job's slot pool, as the Start Job dialog does").choices(VALID_RUNTIMES_PROCESS_TYPES).argParser((value) => {
3945
+ const matched = VALID_RUNTIMES_PROCESS_TYPES.find((v) => v.toLowerCase() === value.toLowerCase());
3946
+ if (!matched) {
3947
+ throw new InvalidArgumentError(`Allowed choices are ${VALID_RUNTIMES_PROCESS_TYPES.join(", ")}.`);
3948
+ }
3949
+ return matched;
3950
+ })).addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).examples(FOLDERS_RUNTIMES_EXAMPLES).trackedAction(processContext, async (folderKeyArg, options) => {
3951
+ const processType = options.processType;
3948
3952
  const folderInput = resolveFolderInput(folderKeyArg);
3949
3953
  const [folderError, folder] = await catchError(resolveFolder(folderInput, { tenant: options.tenant }));
3950
3954
  if (folderError) {
@@ -3964,7 +3968,8 @@ var registerFoldersCommand = (program) => {
3964
3968
  return;
3965
3969
  }
3966
3970
  const [getError, result] = await catchError(api.machinesGetRuntimesForFolderByFolderid({
3967
- folderId: folder.id
3971
+ folderId: folder.id,
3972
+ processType
3968
3973
  }));
3969
3974
  if (getError) {
3970
3975
  await reportFailure("Error listing runtimes", getError);
@@ -4136,7 +4141,7 @@ async function downloadAttachment(attachmentId, outputPath, options) {
4136
4141
  if (bufferError) {
4137
4142
  throw new Error(`Failed to read attachment data: ${bufferError.message}`);
4138
4143
  }
4139
- const { getFileSystem: getFileSystem2 } = await import("./index-r2vc4whd.js");
4144
+ const { getFileSystem: getFileSystem2 } = await import("./index-rkqk9b39.js");
4140
4145
  const fs = getFileSystem2();
4141
4146
  const [writeError] = await catchError(fs.writeFile(outputPath, Buffer.from(buffer)));
4142
4147
  if (writeError) {
@@ -4151,7 +4156,7 @@ async function downloadAttachment(attachmentId, outputPath, options) {
4151
4156
 
4152
4157
  // src/utils/attachment-uploader.ts
4153
4158
  async function uploadAttachment(filePath, name, options) {
4154
- const { getFileSystem: getFileSystem2 } = await import("./index-r2vc4whd.js");
4159
+ const { getFileSystem: getFileSystem2 } = await import("./index-rkqk9b39.js");
4155
4160
  const fs = getFileSystem2();
4156
4161
  const [readError, fileBuffer] = await catchError(fs.readFile(filePath));
4157
4162
  if (readError) {
@@ -4195,7 +4200,7 @@ async function parseAttachmentArg(arg) {
4195
4200
  }
4196
4201
  return { name, path };
4197
4202
  }
4198
- const { getFileSystem: getFileSystem2 } = await import("./index-r2vc4whd.js");
4203
+ const { getFileSystem: getFileSystem2 } = await import("./index-rkqk9b39.js");
4199
4204
  const fs = getFileSystem2();
4200
4205
  return { name: fs.path.basename(arg), path: arg };
4201
4206
  }
@@ -4794,7 +4799,7 @@ var registerJobsCommand = (program) => {
4794
4799
  Data: data
4795
4800
  });
4796
4801
  });
4797
- jobs.command("start").description("Start a job for a process. Returns the created job(s) with keys. " + "The <process-key> is the GUID from 'processes list' (not 'packages list'). " + "Folder is optional — if not provided, it is inferred from the process. " + "Use 'packages entry-points' to see input/output argument schemas for the process.").argument("<process-key>", "Process key (GUID) from 'processes list'. Identifies which process to run.").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--folder-path <path>", "Folder path (optional — inferred from process if not provided)").option("--folder-key <key>", "Folder key (optional — inferred from process if not provided)").option("--strategy <strategy>", "Execution strategy (ModernJobsCount, All, Specific, JobsCount)", "ModernJobsCount").option("--jobs-count <number>", "Number of jobs to create (for JobsCount/ModernJobsCount strategy)", "1").option("--input-arguments <json>", "Input arguments as JSON string (mutually exclusive with --input-file). " + "Auto-offloads to InputFile if >10K chars. " + "Use 'packages entry-points' to see the expected schema.").option("--input-file <path>", "Upload a file as the job's InputFile (mutually exclusive with --input-arguments). " + "The file is uploaded as an attachment and its ID is set on the InputFile field.").option("--attachment <[name=]path>", "Upload a file and attach it to the job (repeatable). " + "Format: path (auto-names from filename) or name=path (explicit name). " + "Example: --attachment ./doc.pdf --attachment invoice=./report.pdf", (val, prev) => prev ? [...prev, val] : [val]).option("--attachment-id <guid>", "Attach an existing attachment by ID (repeatable). " + "Use 'attachments list --job-key <key>' to find IDs from a previous job.", (val, prev) => prev ? [...prev, val] : [val]).option("--runtime-type <type>", "Execution runtime (Unattended, Headless, Serverless, NonProduction, Development, TestAutomation). Defaults to folder configuration.").option("--user-keys <keys>", "Comma-separated user keys (GUIDs) to run as. Use 'users list' to find keys.").option("--machine-keys <keys>", "Comma-separated machine keys (GUIDs) to target. Use 'machines list' to find keys.").option("--environment-variables <pairs>", "Environment variables as newline-separated KEY=VALUE pairs").option("--run-as-me", "Run job under the caller's identity").option("--healing-agent", "Enable Healing Agent (Autopilot for Robots) for this run. Use to recover from selector failures and similar runtime errors. Tenant-level Autopilot must already be enabled.").option("--job-priority <priority>", "Job priority (Low, Normal, High)").option("--reference <reference>", "User-specified reference for the job").option("--wait-for-completion", "Poll until the job reaches a terminal state (Successful, Faulted, Stopped) and return its output").option("--timeout <seconds>", "Maximum time to wait for completion in seconds (default: 300)", "300").option("--poll-interval <seconds>", "Seconds between status checks (default: 5)", "5").option("--output-dir <path>", "Directory for downloading OutputFile (default: current directory). Only with --wait-for-completion.").option("--no-download", "Skip automatic OutputFile download. Only with --wait-for-completion.").examples(JOBS_START_EXAMPLES).trackedAction(processContext, async (releaseKey, options) => {
4802
+ jobs.command("start").description("Start a job for a process. Returns the created job(s) with keys. " + "The <process-key> is the GUID from 'processes list' (not 'packages list'). " + "Folder is optional — if not provided, it is inferred from the process. " + "Use 'packages entry-points' to see input/output argument schemas for the process.").argument("<process-key>", "Process key (GUID) from 'processes list'. Identifies which process to run.").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--folder-path <path>", "Folder path (optional — inferred from process if not provided)").option("--folder-key <key>", "Folder key (optional — inferred from process if not provided)").option("--strategy <strategy>", "Execution strategy (ModernJobsCount, All, Specific, JobsCount)", "ModernJobsCount").option("--jobs-count <number>", "Number of jobs to create (for JobsCount/ModernJobsCount strategy)", "1").option("--input-arguments <json>", "Input arguments as JSON string (mutually exclusive with --input-file). " + "Auto-offloads to InputFile if >10K chars. " + "Use 'packages entry-points' to see the expected schema.").option("--input-file <path>", "Upload a file as the job's InputFile (mutually exclusive with --input-arguments). " + "The file is uploaded as an attachment and its ID is set on the InputFile field.").option("--attachment <[name=]path>", "Upload a file and attach it to the job (repeatable). " + "Format: path (auto-names from filename) or name=path (explicit name). " + "Example: --attachment ./doc.pdf --attachment invoice=./report.pdf", (val, prev) => prev ? [...prev, val] : [val]).option("--attachment-id <guid>", "Attach an existing attachment by ID (repeatable). " + "Use 'attachments list --job-key <key>' to find IDs from a previous job.", (val, prev) => prev ? [...prev, val] : [val]).option("--runtime-type <type>", "Execution runtime (Unattended, Headless, Serverless, NonProduction, Development, TestAutomation, Local). Local runs Agent, Function and API workflow jobs on the folder machines' local slots. Defaults to folder configuration.").option("--user-keys <keys>", "Comma-separated user keys (GUIDs) to run as. Use 'users list' to find keys.").option("--machine-keys <keys>", "Comma-separated machine keys (GUIDs) to target. Use 'machines list' to find keys.").option("--environment-variables <pairs>", "Environment variables as newline-separated KEY=VALUE pairs").option("--run-as-me", "Run job under the caller's identity").option("--healing-agent", "Enable Healing Agent (Autopilot for Robots) for this run. Use to recover from selector failures and similar runtime errors. Tenant-level Autopilot must already be enabled.").option("--job-priority <priority>", "Job priority (Low, Normal, High)").option("--reference <reference>", "User-specified reference for the job").option("--wait-for-completion", "Poll until the job reaches a terminal state (Successful, Faulted, Stopped) and return its output").option("--timeout <seconds>", "Maximum time to wait for completion in seconds (default: 300)", "300").option("--poll-interval <seconds>", "Seconds between status checks (default: 5)", "5").option("--output-dir <path>", "Directory for downloading OutputFile (default: current directory). Only with --wait-for-completion.").option("--no-download", "Skip automatic OutputFile download. Only with --wait-for-completion.").examples(JOBS_START_EXAMPLES).trackedAction(processContext, async (releaseKey, options) => {
4798
4803
  const validStrategies = [
4799
4804
  "ModernJobsCount",
4800
4805
  "All",
@@ -4975,7 +4980,8 @@ var registerJobsCommand = (program) => {
4975
4980
  "Serverless",
4976
4981
  "NonProduction",
4977
4982
  "Development",
4978
- "TestAutomation"
4983
+ "TestAutomation",
4984
+ "Local"
4979
4985
  ];
4980
4986
  const matchedRuntimeType = validRuntimeTypes.find((v) => v.toLowerCase() === options.runtimeType?.toLowerCase());
4981
4987
  if (!matchedRuntimeType) {
@@ -5360,7 +5366,7 @@ var registerJobsCommand = (program) => {
5360
5366
  });
5361
5367
  processContext.exit(1);
5362
5368
  });
5363
- jobs.command("healing-data").description("Download healing agent (Autopilot for Robots) recovery data as a ZIP file. " + "Contains screenshots, UI element data, and recovery logs. " + "Only available for jobs where the healing agent was active. " + "Triggers an async export, polls until complete, then downloads the ZIP.").argument("<job-key>", "Job key (GUID)").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--directory-prefix <prefix>", "Path prefix to filter exported files").option("-o, --output <path>", "Output file path (defaults to healing-agent-data-<job-key>.zip)").examples(JOBS_HEALING_DATA_EXAMPLES).trackedAction(processContext, async (jobKey, options) => {
5369
+ const healingData = jobs.command("healing-data").description("Download healing agent (Autopilot for Robots) recovery data as a ZIP file. " + "Contains screenshots, UI element data, and recovery logs. " + "Only available for jobs where the healing agent was active. " + "Triggers an async export, polls until complete, then downloads the ZIP.").argument("<job-key>", "Job key (GUID)").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--directory-prefix <prefix>", "Path prefix to filter exported files").option("-d, --destination <path>", "Output file path (defaults to healing-agent-data-<job-key>.zip)").examples(JOBS_HEALING_DATA_EXAMPLES).trackedAction(processContext, async (jobKey, options) => {
5364
5370
  const resolved = await resolveJobForCommand(jobKey, options.tenant, "Error getting healing data");
5365
5371
  if (resolved === null)
5366
5372
  return;
@@ -5485,8 +5491,8 @@ var registerJobsCommand = (program) => {
5485
5491
  processContext.exit(1);
5486
5492
  return;
5487
5493
  }
5488
- const fileName = options.output || `healing-agent-data-${jobKey}.zip`;
5489
- const { getFileSystem: getFileSystem2 } = await import("./index-r2vc4whd.js");
5494
+ const fileName = options.destination || `healing-agent-data-${jobKey}.zip`;
5495
+ const { getFileSystem: getFileSystem2 } = await import("./index-rkqk9b39.js");
5490
5496
  const fs = getFileSystem2();
5491
5497
  const [writeError] = await catchError(fs.writeFile(fileName, Buffer.from(buffer)));
5492
5498
  if (writeError) {
@@ -5509,6 +5515,7 @@ var registerJobsCommand = (program) => {
5509
5515
  }
5510
5516
  });
5511
5517
  });
5518
+ addDeprecatedOptionAlias(healingData, "-o, --output <path>", "--destination");
5512
5519
  jobs.command("history").description("Get state transition history for a job. " + "Shows timestamps for each state change (Pending → Running → Successful/Faulted). " + "Useful for understanding job lifecycle and identifying delays. " + "Folder context is resolved automatically from the job key.").argument("<job-key>", "Job key (GUID)").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).examples(JOBS_HISTORY_EXAMPLES).trackedAction(processContext, async (jobKey, options) => {
5513
5520
  const command = await resolveJobWithJobsApi(jobKey, options.tenant, "Error getting job history");
5514
5521
  if (command === null)
@@ -5547,7 +5554,7 @@ async function buildJobCompletionOutput(job, elapsedMs, options) {
5547
5554
  data.Duration = `${Math.round(elapsedMs / 1000)}s`;
5548
5555
  }
5549
5556
  if (job.outputFile && options.download !== false) {
5550
- const { getFileSystem: getFileSystem2 } = await import("./index-r2vc4whd.js");
5557
+ const { getFileSystem: getFileSystem2 } = await import("./index-rkqk9b39.js");
5551
5558
  const fsHelper = getFileSystem2();
5552
5559
  const outputDir = options.outputDir || ".";
5553
5560
  const fileName = fsHelper.path.join(outputDir, `output-${job.key || "job"}.dat`);
@@ -6388,6 +6395,12 @@ function formatMachineDetail(machine) {
6388
6395
  HeadlessSlots: machine.headlessSlots ?? 0,
6389
6396
  NonProductionSlots: machine.nonProductionSlots ?? 0,
6390
6397
  TestAutomationSlots: machine.testAutomationSlots ?? 0,
6398
+ AgentSlots: machine.agentSlots ?? 0,
6399
+ FunctionSlots: machine.functionSlots ?? 0,
6400
+ VerticalsRpaSlots: machine.verticalsRpaSlots ?? 0,
6401
+ VerticalsAgentSlots: machine.verticalsAgentSlots ?? 0,
6402
+ VerticalsFunctionSlots: machine.verticalsFunctionSlots ?? 0,
6403
+ ExecutionOptions: machine.executionOptions || "None",
6391
6404
  LicenseKey: machine.licenseKey || ""
6392
6405
  };
6393
6406
  }
@@ -6406,6 +6419,84 @@ function parseSlotOption(value, flag, errorContext) {
6406
6419
  }
6407
6420
  return n;
6408
6421
  }
6422
+ var SLOT_OPTIONS = [
6423
+ {
6424
+ flag: "--unattended-slots",
6425
+ option: "unattendedSlots",
6426
+ field: "unattendedSlots"
6427
+ },
6428
+ {
6429
+ flag: "--headless-slots",
6430
+ option: "headlessSlots",
6431
+ field: "headlessSlots"
6432
+ },
6433
+ {
6434
+ flag: "--non-production-slots",
6435
+ option: "nonProductionSlots",
6436
+ field: "nonProductionSlots"
6437
+ },
6438
+ {
6439
+ flag: "--testing-slots",
6440
+ option: "testingSlots",
6441
+ field: "testAutomationSlots"
6442
+ },
6443
+ {
6444
+ flag: "--agent-slots",
6445
+ option: "agentSlots",
6446
+ field: "agentSlots"
6447
+ },
6448
+ {
6449
+ flag: "--function-slots",
6450
+ option: "functionSlots",
6451
+ field: "functionSlots"
6452
+ },
6453
+ {
6454
+ flag: "--verticals-rpa-slots",
6455
+ option: "verticalsRpaSlots",
6456
+ field: "verticalsRpaSlots"
6457
+ },
6458
+ {
6459
+ flag: "--verticals-agent-slots",
6460
+ option: "verticalsAgentSlots",
6461
+ field: "verticalsAgentSlots"
6462
+ },
6463
+ {
6464
+ flag: "--verticals-function-slots",
6465
+ option: "verticalsFunctionSlots",
6466
+ field: "verticalsFunctionSlots"
6467
+ }
6468
+ ];
6469
+ var VALID_EXECUTION_OPTIONS = ["None", "AgentsOnUnattended", "RunVerticals"];
6470
+ function parseMachineSlotOptions(options, errorContext) {
6471
+ const body = {};
6472
+ for (const slot of SLOT_OPTIONS) {
6473
+ const value = parseSlotOption(options[slot.option], slot.flag, errorContext);
6474
+ if (value === null)
6475
+ return null;
6476
+ if (value !== undefined)
6477
+ body[slot.field] = value;
6478
+ }
6479
+ if (options.executionOptions !== undefined) {
6480
+ const values = options.executionOptions.flatMap((v) => v.split(",")).map((v) => v.trim()).filter((v) => v.length > 0);
6481
+ const matched = values.map((v) => VALID_EXECUTION_OPTIONS.find((valid) => valid.toLowerCase() === v.toLowerCase()));
6482
+ if (matched.length === 0 || matched.some((m) => m === undefined)) {
6483
+ OutputFormatter.error({
6484
+ Result: RESULTS.Failure,
6485
+ Message: errorContext,
6486
+ Instructions: `Invalid --execution-options '${options.executionOptions.join(" ")}'. Must be one or more of: ${VALID_EXECUTION_OPTIONS.join(", ")}`
6487
+ });
6488
+ processContext.exit(1);
6489
+ return null;
6490
+ }
6491
+ const unique = [...new Set(matched)];
6492
+ const flags = unique.filter((m) => m !== "None" || unique.length === 1);
6493
+ body.executionOptions = flags.join(", ");
6494
+ }
6495
+ return body;
6496
+ }
6497
+ function addSlotOptions(command) {
6498
+ return command.option("--unattended-slots <number>", "Number of unattended robot slots to allocate").option("--headless-slots <number>", "Number of headless robot slots to allocate").option("--non-production-slots <number>", "Number of non-production robot slots to allocate").option("--testing-slots <number>", "Number of test automation robot slots to allocate").option("--agent-slots <number>", "Number of Agent jobs that can run in parallel on the machine's Local runtime").option("--function-slots <number>", "Number of Function and API workflow jobs that can run in parallel on the machine's Local runtime").option("--verticals-rpa-slots <number>", "Number of Local RPA slots reserved for Vertical Solution legs. Used only with --execution-options RunVerticals").option("--verticals-agent-slots <number>", "Number of Local Agent slots reserved for Vertical Solution legs. Used only with --execution-options RunVerticals").option("--verticals-function-slots <number>", "Number of Local Function slots reserved for Vertical Solution legs. Used only with --execution-options RunVerticals").option("--execution-options <options...>", "Machine execution options, space-separated: None, AgentsOnUnattended (Function and API workflow jobs " + "use the Unattended slots), RunVerticals (the machine runs Vertical Solution legs on its vertical slots). " + "Replaces the current value");
6499
+ }
6409
6500
  var MACHINES_LIST_EXAMPLES = [
6410
6501
  {
6411
6502
  Description: "List all machines in the tenant",
@@ -6424,6 +6515,12 @@ var MACHINES_LIST_EXAMPLES = [
6424
6515
  HeadlessSlots: 0,
6425
6516
  NonProductionSlots: 0,
6426
6517
  TestAutomationSlots: 0,
6518
+ AgentSlots: 0,
6519
+ FunctionSlots: 0,
6520
+ VerticalsRpaSlots: 0,
6521
+ VerticalsAgentSlots: 0,
6522
+ VerticalsFunctionSlots: 0,
6523
+ ExecutionOptions: "None",
6427
6524
  LicenseKey: "b1c2d3e4-0000-0000-0000-000000000100"
6428
6525
  },
6429
6526
  {
@@ -6437,6 +6534,12 @@ var MACHINES_LIST_EXAMPLES = [
6437
6534
  HeadlessSlots: 0,
6438
6535
  NonProductionSlots: 0,
6439
6536
  TestAutomationSlots: 0,
6537
+ AgentSlots: 0,
6538
+ FunctionSlots: 0,
6539
+ VerticalsRpaSlots: 0,
6540
+ VerticalsAgentSlots: 0,
6541
+ VerticalsFunctionSlots: 0,
6542
+ ExecutionOptions: "None",
6440
6543
  LicenseKey: "b1c2d3e4-0000-0000-0000-000000000200"
6441
6544
  }
6442
6545
  ]
@@ -6460,6 +6563,12 @@ var MACHINES_GET_EXAMPLES = [
6460
6563
  HeadlessSlots: 0,
6461
6564
  NonProductionSlots: 0,
6462
6565
  TestAutomationSlots: 0,
6566
+ AgentSlots: 0,
6567
+ FunctionSlots: 0,
6568
+ VerticalsRpaSlots: 0,
6569
+ VerticalsAgentSlots: 0,
6570
+ VerticalsFunctionSlots: 0,
6571
+ ExecutionOptions: "None",
6463
6572
  LicenseKey: "b1c2d3e4-0000-0000-0000-000000000100"
6464
6573
  }
6465
6574
  }
@@ -6482,6 +6591,38 @@ var MACHINES_CREATE_EXAMPLES = [
6482
6591
  HeadlessSlots: 0,
6483
6592
  NonProductionSlots: 0,
6484
6593
  TestAutomationSlots: 0,
6594
+ AgentSlots: 0,
6595
+ FunctionSlots: 0,
6596
+ VerticalsRpaSlots: 0,
6597
+ VerticalsAgentSlots: 0,
6598
+ VerticalsFunctionSlots: 0,
6599
+ ExecutionOptions: "None",
6600
+ LicenseKey: ""
6601
+ }
6602
+ }
6603
+ },
6604
+ {
6605
+ Description: "Create a template for local Agent and Function jobs, with slots reserved for Vertical Solution legs",
6606
+ Command: 'uip or machines create --name "local-worker" --agent-slots 2 --function-slots 2 --verticals-agent-slots 1 --execution-options RunVerticals',
6607
+ Output: {
6608
+ Code: "MachineCreated",
6609
+ Data: {
6610
+ Name: "local-worker",
6611
+ ID: 104,
6612
+ Key: "a1b2c3d4-0000-0000-0000-000000000011",
6613
+ Type: "Template",
6614
+ Scope: "Default",
6615
+ Description: "",
6616
+ UnattendedSlots: 0,
6617
+ HeadlessSlots: 0,
6618
+ NonProductionSlots: 0,
6619
+ TestAutomationSlots: 0,
6620
+ AgentSlots: 2,
6621
+ FunctionSlots: 2,
6622
+ VerticalsRpaSlots: 0,
6623
+ VerticalsAgentSlots: 1,
6624
+ VerticalsFunctionSlots: 0,
6625
+ ExecutionOptions: "RunVerticals",
6485
6626
  LicenseKey: ""
6486
6627
  }
6487
6628
  }
@@ -6658,19 +6799,10 @@ var registerMachinesCommand = (program) => {
6658
6799
  Data: options.allFields ? resolved.machine : formatMachineDetail(resolved.machine)
6659
6800
  });
6660
6801
  });
6661
- machines.command("create").description("Create a new machine template (robot host definition). " + "Machines define where robots execute and how many license slots are allocated. " + "Use --serverless to create a serverless (cloud-hosted) machine. " + "Slot options control how many concurrent robot sessions of each type are allowed.").requiredOption("-n, --name <name>", "Machine name (must be unique within the tenant)").option("-d, --description <description>", "Machine description").option("--serverless", "Create a serverless (cloud-hosted) machine instead of a standard template").option("--unattended-slots <number>", "Number of unattended robot slots to allocate").option("--headless-slots <number>", "Number of headless robot slots to allocate").option("--non-production-slots <number>", "Number of non-production robot slots to allocate").option("--testing-slots <number>", "Number of test automation robot slots to allocate").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).examples(MACHINES_CREATE_EXAMPLES).trackedAction(processContext, async (options) => {
6662
- const ctx = "Error creating machine";
6663
- const unattended = parseSlotOption(options.unattendedSlots, "--unattended-slots", ctx);
6664
- if (unattended === null)
6665
- return;
6666
- const headless = parseSlotOption(options.headlessSlots, "--headless-slots", ctx);
6667
- if (headless === null)
6668
- return;
6669
- const nonProduction = parseSlotOption(options.nonProductionSlots, "--non-production-slots", ctx);
6670
- if (nonProduction === null)
6671
- return;
6672
- const testing = parseSlotOption(options.testingSlots, "--testing-slots", ctx);
6673
- if (testing === null)
6802
+ const createCommand = machines.command("create").description("Create a new machine template (robot host definition). " + "Machines define where robots execute and how many license slots are allocated. " + "Use --serverless to create a serverless (cloud-hosted) machine. " + "Slot options control how many concurrent robot sessions of each type are allowed.").requiredOption("-n, --name <name>", "Machine name (must be unique within the tenant)").option("-d, --description <description>", "Machine description").option("--serverless", "Create a serverless (cloud-hosted) machine instead of a standard template");
6803
+ addSlotOptions(createCommand).addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).examples(MACHINES_CREATE_EXAMPLES).trackedAction(processContext, async (options) => {
6804
+ const slots = parseMachineSlotOptions(options, "Error creating machine");
6805
+ if (slots === null)
6674
6806
  return;
6675
6807
  const [apiError, api] = await catchError(createApiClient(MachinesApi, { tenant: options.tenant }));
6676
6808
  if (apiError) {
@@ -6678,6 +6810,7 @@ var registerMachinesCommand = (program) => {
6678
6810
  return;
6679
6811
  }
6680
6812
  const machineData = {
6813
+ ...slots,
6681
6814
  name: options.name,
6682
6815
  type: "Template"
6683
6816
  };
@@ -6688,14 +6821,6 @@ var registerMachinesCommand = (program) => {
6688
6821
  if (options.description) {
6689
6822
  machineData.description = options.description;
6690
6823
  }
6691
- if (unattended !== undefined)
6692
- machineData.unattendedSlots = unattended;
6693
- if (headless !== undefined)
6694
- machineData.headlessSlots = headless;
6695
- if (nonProduction !== undefined)
6696
- machineData.nonProductionSlots = nonProduction;
6697
- if (testing !== undefined)
6698
- machineData.testAutomationSlots = testing;
6699
6824
  const [createError, result] = await catchError(api.machinesPost({
6700
6825
  body: machineData
6701
6826
  }));
@@ -6709,7 +6834,8 @@ var registerMachinesCommand = (program) => {
6709
6834
  Data: formatMachineDetail(result)
6710
6835
  });
6711
6836
  });
6712
- machines.command("update").alias("edit").description("Update machine properties (name, description, slot allocations). " + "Only the provided options are updated; omitted fields remain unchanged. " + "Use 'machines get' to see current values before updating.").argument("<machine-key>", "Machine key (GUID) — use 'machines list' to find").option("-n, --name <name>", "New machine name").option("-d, --description <description>", "New machine description").option("--unattended-slots <number>", "Number of unattended robot slots to allocate").option("--headless-slots <number>", "Number of headless robot slots to allocate").option("--non-production-slots <number>", "Number of non-production robot slots to allocate").option("--testing-slots <number>", "Number of test automation robot slots to allocate").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).examples(MACHINES_EDIT_EXAMPLES).trackedAction(processContext, async (machineKey, options) => {
6837
+ const updateCommand = machines.command("update").alias("edit").description("Update machine properties (name, description, slot allocations). " + "Only the provided options are updated; omitted fields remain unchanged. " + "Use 'machines get' to see current values before updating.").argument("<machine-key>", "Machine key (GUID) — use 'machines list' to find").option("-n, --name <name>", "New machine name").option("-d, --description <description>", "New machine description");
6838
+ addSlotOptions(updateCommand).addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).examples(MACHINES_EDIT_EXAMPLES).trackedAction(processContext, async (machineKey, options) => {
6713
6839
  const resolved = await resolveMachineForCommand(machineKey, options.tenant, "Error updating machine");
6714
6840
  if (resolved === null)
6715
6841
  return;
@@ -6721,27 +6847,10 @@ var registerMachinesCommand = (program) => {
6721
6847
  updateData.name = options.name;
6722
6848
  if (options.description)
6723
6849
  updateData.description = options.description;
6724
- const ctx = "Error updating machine";
6725
- const unattended = parseSlotOption(options.unattendedSlots, "--unattended-slots", ctx);
6726
- if (unattended === null)
6727
- return;
6728
- const headless = parseSlotOption(options.headlessSlots, "--headless-slots", ctx);
6729
- if (headless === null)
6730
- return;
6731
- const nonProduction = parseSlotOption(options.nonProductionSlots, "--non-production-slots", ctx);
6732
- if (nonProduction === null)
6733
- return;
6734
- const testing = parseSlotOption(options.testingSlots, "--testing-slots", ctx);
6735
- if (testing === null)
6736
- return;
6737
- if (unattended !== undefined)
6738
- updateData.unattendedSlots = unattended;
6739
- if (headless !== undefined)
6740
- updateData.headlessSlots = headless;
6741
- if (nonProduction !== undefined)
6742
- updateData.nonProductionSlots = nonProduction;
6743
- if (testing !== undefined)
6744
- updateData.testAutomationSlots = testing;
6850
+ const slots = parseMachineSlotOptions(options, "Error updating machine");
6851
+ if (slots === null)
6852
+ return;
6853
+ Object.assign(updateData, slots);
6745
6854
  const [editError] = await catchError(api.machinesPatchById({
6746
6855
  key: resolved.id,
6747
6856
  body: updateData
@@ -7396,7 +7505,7 @@ var registerPackagesCommand = (program) => {
7396
7505
  processContext.exit(1);
7397
7506
  return;
7398
7507
  }
7399
- const { getFileSystem: getFileSystem2 } = await import("./index-r2vc4whd.js");
7508
+ const { getFileSystem: getFileSystem2 } = await import("./index-rkqk9b39.js");
7400
7509
  const fs = getFileSystem2();
7401
7510
  const [readError, fileBuffer] = await catchError(fs.readFile(filePath));
7402
7511
  if (readError || fileBuffer === null) {
@@ -7566,7 +7675,7 @@ var registerPackagesCommand = (program) => {
7566
7675
  await reportFailure("Error downloading package", bufferError);
7567
7676
  return;
7568
7677
  }
7569
- const { getFileSystem: getFileSystem2 } = await import("./index-r2vc4whd.js");
7678
+ const { getFileSystem: getFileSystem2 } = await import("./index-rkqk9b39.js");
7570
7679
  const fs = getFileSystem2();
7571
7680
  const buffer = new Uint8Array(arrayBuffer);
7572
7681
  const [writeError] = await catchError(fs.writeFile(options.destination, buffer));
@@ -11258,6 +11367,185 @@ var registerQueuesCommand = (program) => {
11258
11367
  });
11259
11368
  };
11260
11369
 
11370
+ // src/utils/role-permissions.ts
11371
+ var CATALOG_PAGE_SIZE = 1000;
11372
+ var MAX_SUGGESTIONS = 5;
11373
+ var ACTION_SUFFIX = /\.(View|Edit|Create|Delete)$/;
11374
+ var GROUP_HINTS = {
11375
+ triggers: "Schedules",
11376
+ folders: "Units",
11377
+ actioncatalogs: "TaskCatalogs"
11378
+ };
11379
+ async function fetchPermissionCatalog(api) {
11380
+ const catalog = [];
11381
+ for (let skip = 0;; skip += CATALOG_PAGE_SIZE) {
11382
+ const page = (await api.permissionsGet({
11383
+ $top: CATALOG_PAGE_SIZE,
11384
+ $skip: skip,
11385
+ $orderby: "Name asc"
11386
+ })).value ?? [];
11387
+ catalog.push(...page);
11388
+ if (page.length < CATALOG_PAGE_SIZE)
11389
+ return catalog;
11390
+ }
11391
+ }
11392
+ function planPermissionChanges(input) {
11393
+ const catalogByName = byName(input.catalog);
11394
+ const existingByName = byName(input.role.permissions ?? []);
11395
+ const resolveIn = (...sources) => {
11396
+ const canonical = new Map;
11397
+ for (const name of sources.flatMap((s) => [...s.keys()])) {
11398
+ canonical.set(name.toLowerCase(), name);
11399
+ }
11400
+ return (name) => sources.some((s) => s.has(name)) ? name : canonical.get(name.toLowerCase());
11401
+ };
11402
+ const resolveAdd = resolveIn(catalogByName);
11403
+ const resolveRemove = resolveIn(existingByName, catalogByName);
11404
+ const unknownNames = unique([
11405
+ ...input.add.filter((n) => !resolveAdd(n)),
11406
+ ...input.remove.filter((n) => !resolveRemove(n))
11407
+ ]);
11408
+ if (unknownNames.length > 0) {
11409
+ const names = [
11410
+ ...(catalogByName.size > 0 ? catalogByName : existingByName).keys()
11411
+ ];
11412
+ return {
11413
+ kind: "unknown",
11414
+ unknown: unknownNames.map((name) => ({
11415
+ Name: name,
11416
+ Suggestions: suggestPermissionNames(name, names)
11417
+ })),
11418
+ catalogEmpty: catalogByName.size === 0
11419
+ };
11420
+ }
11421
+ const toAdd = new Set(input.add.map((n) => resolveAdd(n)));
11422
+ const toRemove = new Set(input.remove.map((n) => resolveRemove(n)));
11423
+ const conflicting = [...toAdd].filter((n) => toRemove.has(n));
11424
+ if (conflicting.length > 0) {
11425
+ return { kind: "conflicting", conflicting };
11426
+ }
11427
+ const rejected = rejectedForRoleType(input.role.type, toAdd, catalogByName);
11428
+ if (rejected.length > 0) {
11429
+ return {
11430
+ kind: "wrong-scope",
11431
+ roleType: input.role.type,
11432
+ rejected
11433
+ };
11434
+ }
11435
+ const permissions = [...existingByName.values()].map((p) => {
11436
+ if (toAdd.has(p.name))
11437
+ return { ...p, isGranted: true };
11438
+ if (toRemove.has(p.name))
11439
+ return { ...p, isGranted: false };
11440
+ return p;
11441
+ });
11442
+ for (const name of toAdd) {
11443
+ const template = catalogByName.get(name);
11444
+ if (!existingByName.has(name) && template) {
11445
+ permissions.push({ ...template, isGranted: true });
11446
+ }
11447
+ }
11448
+ const wasGranted = (name) => existingByName.get(name)?.isGranted === true;
11449
+ return {
11450
+ kind: "ok",
11451
+ permissions,
11452
+ added: [...toAdd].filter((n) => !wasGranted(n)),
11453
+ removed: [...toRemove].filter(wasGranted)
11454
+ };
11455
+ }
11456
+ function rejectedForRoleType(roleType, names, catalogByName) {
11457
+ if (roleType !== "Folder" && roleType !== "Tenant")
11458
+ return [];
11459
+ const rejected = [];
11460
+ for (const name of names) {
11461
+ const scope = catalogByName.get(name)?.scope;
11462
+ if (!scope || !ACTION_SUFFIX.test(name))
11463
+ continue;
11464
+ const isFolderScope = scope === "Folder";
11465
+ if (roleType === "Folder" !== isFolderScope) {
11466
+ rejected.push({ Name: name, Scope: scope });
11467
+ }
11468
+ }
11469
+ return rejected;
11470
+ }
11471
+ function suggestPermissionNames(input, names) {
11472
+ const candidates = names.filter((n) => n.includes("."));
11473
+ const lower = input.toLowerCase();
11474
+ const dot = lower.indexOf(".");
11475
+ const group = dot === -1 ? lower : lower.slice(0, dot);
11476
+ const action = dot === -1 ? "" : lower.slice(dot + 1);
11477
+ const suggestions = new Set;
11478
+ const inGroup = (g) => candidates.filter((n) => n.toLowerCase().startsWith(`${g}.`));
11479
+ const hinted = GROUP_HINTS[group]?.toLowerCase();
11480
+ if (hinted) {
11481
+ const exact = candidates.find((n) => n.toLowerCase() === `${hinted}.${action}`);
11482
+ for (const n of exact ? [exact] : inGroup(hinted))
11483
+ suggestions.add(n);
11484
+ }
11485
+ const maxEdits = Math.max(2, Math.floor(input.length / 4));
11486
+ const nearMisses = candidates.map((n) => ({ n, d: editDistance(lower, n.toLowerCase()) })).filter(({ d }) => d <= maxEdits).sort((a, b) => a.d - b.d || a.n.localeCompare(b.n));
11487
+ for (const { n } of nearMisses)
11488
+ suggestions.add(n);
11489
+ for (const n of inGroup(group))
11490
+ suggestions.add(n);
11491
+ return [...suggestions].slice(0, MAX_SUGGESTIONS);
11492
+ }
11493
+ function unknownPermissionsFailure(roleKey, unknown, catalogEmpty) {
11494
+ const hints = unknown.filter((u) => u.Suggestions.length > 0).map((u) => `${u.Suggestions.join(", ")} (for ${u.Name})`);
11495
+ const didYouMean = hints.length > 0 ? `Did you mean ${hints.join("; ")}? ` : "";
11496
+ const empty = catalogEmpty ? "The permission catalog came back empty for this login, so no name could be checked. " : "";
11497
+ return {
11498
+ Result: RESULTS.Failure,
11499
+ ErrorCode: "invalid_argument",
11500
+ Message: `Not in this tenant's permission catalog: ${unknown.map((u) => u.Name).join(", ")}. The role was not changed.`,
11501
+ Instructions: `${empty}${didYouMean}Run 'uip or roles permissions list --limit 1000' to see every permission name.`,
11502
+ Retry: "RetryWillNotFix",
11503
+ Data: { RoleKey: roleKey, UnknownPermissions: [...unknown] }
11504
+ };
11505
+ }
11506
+ function rejectedPermissionsFailure(roleKey, roleType, rejected) {
11507
+ const [held, other] = roleType === "Folder" ? ["tenant", "Tenant"] : ["folder", "Folder"];
11508
+ return {
11509
+ Result: RESULTS.Failure,
11510
+ ErrorCode: "invalid_argument",
11511
+ Message: `A ${roleType} role cannot hold ${held} permissions: ${rejected.map((r) => r.Name).join(", ")}. The role was not changed.`,
11512
+ Instructions: `Drop them from --add-permissions, or grant them through a ${other} role ('uip or roles list' shows each role's Type).`,
11513
+ Retry: "RetryWillNotFix",
11514
+ Data: {
11515
+ RoleKey: roleKey,
11516
+ RoleType: roleType,
11517
+ RejectedPermissions: [...rejected]
11518
+ }
11519
+ };
11520
+ }
11521
+ function conflictingPermissionsFailure(roleKey, conflicting) {
11522
+ return {
11523
+ Result: RESULTS.Failure,
11524
+ ErrorCode: "invalid_argument",
11525
+ Message: `Both added and removed: ${conflicting.join(", ")}. The role was not changed.`,
11526
+ Instructions: "Pass each permission to only one of --add-permissions and --remove-permissions.",
11527
+ Retry: "RetryWillNotFix",
11528
+ Data: { RoleKey: roleKey, ConflictingPermissions: [...conflicting] }
11529
+ };
11530
+ }
11531
+ function byName(permissions) {
11532
+ return new Map(permissions.filter((p) => p.name != null).map((p) => [p.name, p]));
11533
+ }
11534
+ function unique(names) {
11535
+ return [...new Set(names)];
11536
+ }
11537
+ function editDistance(a, b) {
11538
+ let previous = Array.from({ length: b.length + 1 }, (_, j) => j);
11539
+ for (let i = 1;i <= a.length; i++) {
11540
+ const current = [i];
11541
+ for (let j = 1;j <= b.length; j++) {
11542
+ current[j] = Math.min(previous[j] + 1, current[j - 1] + 1, previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
11543
+ }
11544
+ previous = current;
11545
+ }
11546
+ return previous[b.length];
11547
+ }
11548
+
11261
11549
  // src/utils/role-resolver.ts
11262
11550
  async function resolveRoleGuid(roleKey) {
11263
11551
  const [apiError, api] = await catchError(createApiClient(RolesApi));
@@ -11305,6 +11593,9 @@ async function resolveUserGuid(userInput) {
11305
11593
 
11306
11594
  // src/commands/roles.ts
11307
11595
  var ROLE_TYPE_ALLOWLIST = ["Tenant", "Folder"];
11596
+ function splitPermissionNames(value) {
11597
+ return value === undefined ? [] : value.split(",").map((p) => p.trim()).filter(Boolean);
11598
+ }
11308
11599
  function parseUserRolePrincipalType(value) {
11309
11600
  switch (value.toLowerCase()) {
11310
11601
  case "user":
@@ -11404,13 +11695,15 @@ var ROLES_CREATE_ROLE_EXAMPLES = [
11404
11695
  ];
11405
11696
  var ROLES_EDIT_ROLE_EXAMPLES = [
11406
11697
  {
11407
- Description: "Add permissions to a role",
11408
- Command: "uip or roles update a1b2c3d4-0000-0000-0000-000000000010 --add-permissions Assets.View,Jobs.View",
11698
+ Description: "Add permissions to a tenant role",
11699
+ Command: "uip or roles update a1b2c3d4-0000-0000-0000-000000000010 --add-permissions Users.View,Robots.View",
11409
11700
  Output: {
11410
11701
  Code: "RoleUpdated",
11411
11702
  Data: {
11412
11703
  Key: "a1b2c3d4-0000-0000-0000-000000000010",
11413
- Status: "Updated successfully"
11704
+ Status: "Updated successfully",
11705
+ AddedPermissions: ["Users.View", "Robots.View"],
11706
+ RemovedPermissions: []
11414
11707
  }
11415
11708
  }
11416
11709
  }
@@ -11685,7 +11978,7 @@ var registerRolesCommand = (program) => {
11685
11978
  }
11686
11979
  });
11687
11980
  });
11688
- rolesCmd.command("update").alias("edit").description("Add or remove permissions on a role. " + "Reads current permissions, applies changes, and saves. " + "Use 'roles permissions list' to find valid permission names, 'roles get' to see current grants.").argument("<role-key>", "Role key (GUID) — use 'roles list' to find").option("--add-permissions <names>", "Comma-separated permission names to add to the role").option("--remove-permissions <names>", "Comma-separated permission names to remove from the role").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).examples(ROLES_EDIT_ROLE_EXAMPLES).trackedAction(processContext, async (roleKey, options) => {
11981
+ rolesCmd.command("update").alias("edit").description("Add or remove permissions on a role. " + "Reads current permissions, applies changes, and saves. " + "Names are checked against the tenant's permission catalog before saving: an unknown name, " + "or a permission whose scope does not fit the role (a tenant permission on a Folder role, a folder permission on a Tenant role), " + "is refused and nothing is saved. Reports the permissions it actually added and removed. " + "Use 'roles permissions list' to find valid permission names, 'roles get' to see current grants.").argument("<role-key>", "Role key (GUID) — use 'roles list' to find").option("--add-permissions <names>", "Comma-separated permission names to add to the role").option("--remove-permissions <names>", "Comma-separated permission names to remove from the role").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).examples(ROLES_EDIT_ROLE_EXAMPLES).trackedAction(processContext, async (roleKey, options) => {
11689
11982
  if (!options.addPermissions && !options.removePermissions) {
11690
11983
  OutputFormatter.error({
11691
11984
  Result: RESULTS.Failure,
@@ -11715,38 +12008,32 @@ var registerRolesCommand = (program) => {
11715
12008
  await reportFailure("Error fetching current role data", getError);
11716
12009
  return;
11717
12010
  }
11718
- const toAdd = options.addPermissions ? new Set(options.addPermissions.split(",").map((p) => p.trim()).filter(Boolean)) : new Set;
11719
- const toRemove = options.removePermissions ? new Set(options.removePermissions.split(",").map((p) => p.trim()).filter(Boolean)) : new Set;
11720
12011
  const [permApiError, permApi] = await catchError(createApiClient(PermissionsApi, { tenant: options.tenant }));
11721
12012
  if (permApiError) {
11722
12013
  await reportFailure("Error fetching available permissions", permApiError);
11723
12014
  return;
11724
12015
  }
11725
- const [allPermsError, allPermsResult] = await catchError(permApi.permissionsGet({ $top: 1000 }));
11726
- if (allPermsError) {
11727
- await reportFailure("Error fetching available permissions", allPermsError);
12016
+ const [catalogError, catalog] = await catchError(fetchPermissionCatalog(permApi));
12017
+ if (catalogError) {
12018
+ await reportFailure("Error fetching available permissions", catalogError);
11728
12019
  return;
11729
12020
  }
11730
- const availableByName = new Map((allPermsResult.value ?? []).filter((p) => p.name != null).map((p) => [p.name, p]));
11731
- const existingByName = new Map((current.permissions ?? []).filter((p) => p.name != null).map((p) => [p.name, p]));
11732
- const permissions = [...existingByName.values()].map((p) => {
11733
- if (p.name && toAdd.has(p.name)) {
11734
- return { ...p, isGranted: true };
11735
- }
11736
- if (p.name && toRemove.has(p.name)) {
11737
- return { ...p, isGranted: false };
11738
- }
11739
- return p;
12021
+ const plan = planPermissionChanges({
12022
+ role: current,
12023
+ catalog,
12024
+ add: splitPermissionNames(options.addPermissions),
12025
+ remove: splitPermissionNames(options.removePermissions)
11740
12026
  });
11741
- for (const name of toAdd) {
11742
- if (!existingByName.has(name)) {
11743
- const template = availableByName.get(name);
11744
- if (template) {
11745
- permissions.push({ ...template, isGranted: true });
11746
- }
11747
- }
12027
+ if (plan.kind !== "ok") {
12028
+ OutputFormatter.error(plan.kind === "unknown" ? unknownPermissionsFailure(roleKey, plan.unknown, plan.catalogEmpty) : plan.kind === "conflicting" ? conflictingPermissionsFailure(roleKey, plan.conflicting) : rejectedPermissionsFailure(roleKey, plan.roleType, plan.rejected));
12029
+ processContext.exit(1);
12030
+ return;
11748
12031
  }
11749
- const body = { ...current, id: numericId, permissions };
12032
+ const body = {
12033
+ ...current,
12034
+ id: numericId,
12035
+ permissions: plan.permissions
12036
+ };
11750
12037
  const [editError] = await catchError(api.rolesPutById({ key: numericId, body }));
11751
12038
  if (editError) {
11752
12039
  await reportFailure("Error updating role", editError);
@@ -11755,7 +12042,12 @@ var registerRolesCommand = (program) => {
11755
12042
  OutputFormatter.success({
11756
12043
  Result: RESULTS.Success,
11757
12044
  Code: "RoleUpdated",
11758
- Data: { Key: roleKey, Status: "Updated successfully" }
12045
+ Data: {
12046
+ Key: roleKey,
12047
+ Status: "Updated successfully",
12048
+ AddedPermissions: plan.added,
12049
+ RemovedPermissions: plan.removed
12050
+ }
11759
12051
  });
11760
12052
  });
11761
12053
  rolesCmd.command("delete").description("Delete a role by key (GUID). " + "Only non-static (user-created) roles can be deleted. " + "Built-in roles (IsStatic=true) cannot be removed. " + "Use 'roles list' to find role keys.").argument("<role-key>", "Role key (GUID) — use 'roles list' to find").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("-y, --yes", "Confirm this irreversible operation (required; the CLI never prompts)").examples(ROLES_DELETE_ROLE_EXAMPLES).trackedAction(processContext, async (roleKey, options) => {
@@ -12219,6 +12511,8 @@ function formatMachineSessionRuntime(runtime) {
12219
12511
  MaintenanceMode: runtime.maintenanceMode || "",
12220
12512
  HostMachineName: runtime.hostMachineName || "",
12221
12513
  RuntimeType: runtime.runtimeType || "",
12514
+ ProcessType: runtime.processType || "",
12515
+ IsVertical: runtime.isVertical ?? false,
12222
12516
  MachineType: runtime.machineType || "",
12223
12517
  MachineScope: runtime.machineScope || "",
12224
12518
  Status: runtime.status || "",
@@ -12354,6 +12648,8 @@ var SESSIONS_LIST_UNATTENDED_EXAMPLES = [
12354
12648
  MaintenanceMode: "Default",
12355
12649
  HostMachineName: "prod-worker-01",
12356
12650
  RuntimeType: "Unattended",
12651
+ ProcessType: "",
12652
+ IsVertical: false,
12357
12653
  MachineType: "Standard",
12358
12654
  MachineScope: "Default",
12359
12655
  Status: "Available",
@@ -13209,13 +13505,14 @@ function resolveBoolOption(value, fallback) {
13209
13505
  return value !== undefined ? value : fallback;
13210
13506
  }
13211
13507
  var VALID_JOB_PRIORITIES = ["Low", "Normal", "High"];
13212
- var VALID_RUNTIME_TYPES = [
13508
+ var VALID_API_RUNTIME_TYPES = [
13213
13509
  "Serverless",
13214
13510
  "Unattended",
13215
13511
  "Headless",
13216
13512
  "NonProduction",
13217
13513
  "AgentService"
13218
13514
  ];
13515
+ var VALID_RUNTIME_TYPES = [...VALID_API_RUNTIME_TYPES, "Local"];
13219
13516
  var VALID_HTTP_METHODS = ["Get", "Post", "Put", "Delete"];
13220
13517
  var VALID_CALLING_MODES = [
13221
13518
  "AsyncRequestReply",
@@ -13245,8 +13542,8 @@ function validateJobPriority(options) {
13245
13542
  options.jobPriority = result.value;
13246
13543
  return true;
13247
13544
  }
13248
- function validateRuntimeType(options) {
13249
- const result = normalizeEnumOption(options.runtimeType, VALID_RUNTIME_TYPES, "--runtime-type");
13545
+ function validateRuntimeType(options, validRuntimeTypes = VALID_RUNTIME_TYPES) {
13546
+ const result = normalizeEnumOption(options.runtimeType, validRuntimeTypes, "--runtime-type");
13250
13547
  if (!result.ok)
13251
13548
  return false;
13252
13549
  options.runtimeType = result.value;
@@ -13724,7 +14021,7 @@ var registerTriggersCommand = (program) => {
13724
14021
  });
13725
14022
  }
13726
14023
  });
13727
- triggers.command("create").description("Create a new trigger. --type selects trigger type: time (default), queue, or api. Requires --name, --release-key, --runtime-type. " + "Time triggers need --cron; queue triggers need --queue-key. " + "API triggers need --slug and --method. " + "Folder is inferred from --release-key; pass --folder-path or --folder-key when your identity only has a folder-level role (such as an external app), since it cannot look processes up tenant-wide. " + "Use 'processes list' to find the release key.").option("--type <type>", "Trigger type (time, queue, api)", "time").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).requiredOption("--name <name>", "Trigger name").option("-d, --description <text>", "Trigger description").requiredOption("--release-key <key>", "Release key (GUID) of the process to trigger").option("--folder-path <path>", "Folder path of the process (e.g., 'Shared'). Optional — inferred from --release-key if not given").option("--folder-key <key>", "Folder key (GUID) of the process. Optional — inferred from --release-key if not given").option("--input-arguments <json>", `Input arguments as JSON string (e.g., '{"Arg1":"value"}')`).option("--calendar-key <key>", "Calendar key (GUID) for excluding non-working days. Use 'calendars list' to find keys.").option("--stop-strategy <strategy>", "How to stop running jobs: SoftStop or Kill").option("--kill-process-expression <expression>", "Cron expression for force-killing jobs (requires --stop-strategy Kill)").option("--cron <expression>", "Cron expression in Quartz 6-field format: 'sec min hour day month weekday' (e.g., '0 0 12 * * ?' for daily at noon). Note: standard Unix 5-field cron is not supported.").option("--time-zone <timezone>", "IANA time zone ID, e.g. 'UTC' or 'Europe/Bucharest' (applies to time-of-day fields)").option("--queue-key <key>", "Queue key (GUID) for queue trigger (queue only)").option("--items-threshold <number>", "Minimum queue items to trigger activation (queue only)", "1").option("--max-jobs <number>", "Maximum concurrent jobs for queue activation (queue only)", "1").option("--items-per-job <number>", "Target ratio of queue items per job (queue only)", "1").option("--activate-on-complete", "Re-trigger when a job completes (queue only)").option("--resume-on-same-context", "Resume suspended jobs on the same machine").option("--run-as-me", "Run job under the trigger creator's identity").option("--disabled", "Create the trigger in disabled state (default: enabled)").requiredOption("--runtime-type <type>", "Execution runtime (Serverless, Unattended, Headless, NonProduction, AgentService)").requiredOption("--job-priority <priority>", "Job execution priority (Low, Normal, High)", "Normal").option("--slug <slug>", "URL slug for API trigger (api only)").option("--method <method>", "HTTP method: Get, Post, Put, Delete (api only)").option("--calling-mode <mode>", "Calling mode: AsyncRequestReply, AsyncCallback, LongPolling, FireAndForget (api only)").option("--target <spec>", "Run target as 'machine=<guid>,user=<guid>,session=<int>' (any field optional). Repeat for multiple targets. Use 'machines list' and 'users list' to find keys.", (val, prev) => prev ? [...prev, val] : [val]).option("--mapping-mode <mode>", "Validation mode for --target: 'dynamic' (default) allows any combination of machine/user/session (session requires machine); 'strict' requires both machine and user on every target (use when the folder has 'Enable account-machine mappings' turned on).", "dynamic").examples(TRIGGERS_CREATE_EXAMPLES).trackedAction(processContext, async (options) => {
14024
+ triggers.command("create").description("Create a new trigger. --type selects trigger type: time (default), queue, or api. Requires --name, --release-key, --runtime-type. " + "Time triggers need --cron; queue triggers need --queue-key. " + "API triggers need --slug and --method. " + "Folder is inferred from --release-key; pass --folder-path or --folder-key when your identity only has a folder-level role (such as an external app), since it cannot look processes up tenant-wide. " + "Use 'processes list' to find the release key.").option("--type <type>", "Trigger type (time, queue, api)", "time").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).requiredOption("--name <name>", "Trigger name").option("-d, --description <text>", "Trigger description").requiredOption("--release-key <key>", "Release key (GUID) of the process to trigger").option("--folder-path <path>", "Folder path of the process (e.g., 'Shared'). Optional — inferred from --release-key if not given").option("--folder-key <key>", "Folder key (GUID) of the process. Optional — inferred from --release-key if not given").option("--input-arguments <json>", `Input arguments as JSON string (e.g., '{"Arg1":"value"}')`).option("--calendar-key <key>", "Calendar key (GUID) for excluding non-working days. Use 'calendars list' to find keys.").option("--stop-strategy <strategy>", "How to stop running jobs: SoftStop or Kill").option("--kill-process-expression <expression>", "Cron expression for force-killing jobs (requires --stop-strategy Kill)").option("--cron <expression>", "Cron expression in Quartz 6-field format: 'sec min hour day month weekday' (e.g., '0 0 12 * * ?' for daily at noon). Note: standard Unix 5-field cron is not supported.").option("--time-zone <timezone>", "IANA time zone ID, e.g. 'UTC' or 'Europe/Bucharest' (applies to time-of-day fields)").option("--queue-key <key>", "Queue key (GUID) for queue trigger (queue only)").option("--items-threshold <number>", "Minimum queue items to trigger activation (queue only)", "1").option("--max-jobs <number>", "Maximum concurrent jobs for queue activation (queue only)", "1").option("--items-per-job <number>", "Target ratio of queue items per job (queue only)", "1").option("--activate-on-complete", "Re-trigger when a job completes (queue only)").option("--resume-on-same-context", "Resume suspended jobs on the same machine").option("--run-as-me", "Run job under the trigger creator's identity").option("--disabled", "Create the trigger in disabled state (default: enabled)").requiredOption("--runtime-type <type>", "Execution runtime (Serverless, Unattended, Headless, NonProduction, AgentService, Local). Local runs Agent, Function and API workflow jobs on the folder machines' local slots; API triggers don't accept it").requiredOption("--job-priority <priority>", "Job execution priority (Low, Normal, High)", "Normal").option("--slug <slug>", "URL slug for API trigger (api only)").option("--method <method>", "HTTP method: Get, Post, Put, Delete (api only)").option("--calling-mode <mode>", "Calling mode: AsyncRequestReply, AsyncCallback, LongPolling, FireAndForget (api only)").option("--target <spec>", "Run target as 'machine=<guid>,user=<guid>,session=<int>' (any field optional). Repeat for multiple targets. Use 'machines list' and 'users list' to find keys.", (val, prev) => prev ? [...prev, val] : [val]).option("--mapping-mode <mode>", "Validation mode for --target: 'dynamic' (default) allows any combination of machine/user/session (session requires machine); 'strict' requires both machine and user on every target (use when the folder has 'Enable account-machine mappings' turned on).", "dynamic").examples(TRIGGERS_CREATE_EXAMPLES).trackedAction(processContext, async (options) => {
13728
14025
  const triggerType = validateTriggerType(options.type);
13729
14026
  if (!triggerType) {
13730
14027
  emitInvalidTypeError(options.type);
@@ -13738,7 +14035,7 @@ var registerTriggersCommand = (program) => {
13738
14035
  await createApiTrigger(options);
13739
14036
  }
13740
14037
  });
13741
- triggers.command("update").description("Update an existing trigger by key (GUID). --type defaults to time. Only provided fields are changed. " + "All types require --folder-path or --folder-key. " + "Use 'triggers list' to retrieve the key.").argument("<trigger-key>", "Trigger key (GUID). Use 'triggers list' to retrieve the key.").option("--type <type>", "Trigger type (time, queue, api)", "time").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--folder-path <path>", "Folder path (e.g., 'Shared')").option("--folder-key <key>", "Folder key (GUID)").option("--name <name>", "New trigger name").option("--release-key <key>", "Release key (GUID) of the process to schedule").option("--cron <expression>", "Cron expression for scheduling (time only)").option("--time-zone <timezone>", "IANA time zone ID, e.g. 'UTC' or 'Europe/Bucharest' (applies to time-of-day fields)").option("--queue-key <key>", "Queue key (GUID) for queue-based trigger (queue only)").option("--runtime-type <type>", "Execution runtime (Serverless, Unattended, Headless, NonProduction, AgentService)").option("--job-priority <priority>", "Job execution priority (Low, Normal, High)").option("--items-threshold <number>", "Minimum queue items to trigger (queue only)").option("--max-jobs <number>", "Maximum concurrent jobs for queue activation (queue only)").option("--items-per-job <number>", "Target ratio of queue items per job (queue only)").option("--activate-on-complete", "Re-trigger when a job completes (queue only)").option("--no-activate-on-complete", "Disable re-trigger on job completion (queue only)").option("--resume-on-same-context", "Resume suspended jobs on same machine").option("--no-resume-on-same-context", "Disable resume on same context").option("--run-as-me", "Run under trigger creator's identity").option("--no-run-as-me", "Disable run-as-me").option("--slug <slug>", "URL slug (api only)").option("--method <method>", "HTTP method: Get, Post, Put, Delete (api only)").option("--calling-mode <mode>", "Calling mode (api only)").option("--target <spec>", "Run target as 'machine=<guid>,user=<guid>,session=<int>' (any field optional). Repeat for multiple targets. Use 'machines list' and 'users list' to find keys.", (val, prev) => prev ? [...prev, val] : [val]).option("--mapping-mode <mode>", "Validation mode for --target: 'dynamic' (default) allows any combination of machine/user/session (session requires machine); 'strict' requires both machine and user on every target (use when the folder has 'Enable account-machine mappings' turned on).", "dynamic").option("--enabled", "Enable the trigger").option("--disabled", "Disable the trigger").examples(TRIGGERS_UPDATE_EXAMPLES).trackedAction(processContext, async (triggerKey, options) => {
14038
+ triggers.command("update").description("Update an existing trigger by key (GUID). --type defaults to time. Only provided fields are changed. " + "All types require --folder-path or --folder-key. " + "Use 'triggers list' to retrieve the key.").argument("<trigger-key>", "Trigger key (GUID). Use 'triggers list' to retrieve the key.").option("--type <type>", "Trigger type (time, queue, api)", "time").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--folder-path <path>", "Folder path (e.g., 'Shared')").option("--folder-key <key>", "Folder key (GUID)").option("--name <name>", "New trigger name").option("--release-key <key>", "Release key (GUID) of the process to schedule").option("--cron <expression>", "Cron expression for scheduling (time only)").option("--time-zone <timezone>", "IANA time zone ID, e.g. 'UTC' or 'Europe/Bucharest' (applies to time-of-day fields)").option("--queue-key <key>", "Queue key (GUID) for queue-based trigger (queue only)").option("--runtime-type <type>", "Execution runtime (Serverless, Unattended, Headless, NonProduction, AgentService, Local). Local runs Agent, Function and API workflow jobs on the folder machines' local slots; API triggers don't accept it").option("--job-priority <priority>", "Job execution priority (Low, Normal, High)").option("--items-threshold <number>", "Minimum queue items to trigger (queue only)").option("--max-jobs <number>", "Maximum concurrent jobs for queue activation (queue only)").option("--items-per-job <number>", "Target ratio of queue items per job (queue only)").option("--activate-on-complete", "Re-trigger when a job completes (queue only)").option("--no-activate-on-complete", "Disable re-trigger on job completion (queue only)").option("--resume-on-same-context", "Resume suspended jobs on same machine").option("--no-resume-on-same-context", "Disable resume on same context").option("--run-as-me", "Run under trigger creator's identity").option("--no-run-as-me", "Disable run-as-me").option("--slug <slug>", "URL slug (api only)").option("--method <method>", "HTTP method: Get, Post, Put, Delete (api only)").option("--calling-mode <mode>", "Calling mode (api only)").option("--target <spec>", "Run target as 'machine=<guid>,user=<guid>,session=<int>' (any field optional). Repeat for multiple targets. Use 'machines list' and 'users list' to find keys.", (val, prev) => prev ? [...prev, val] : [val]).option("--mapping-mode <mode>", "Validation mode for --target: 'dynamic' (default) allows any combination of machine/user/session (session requires machine); 'strict' requires both machine and user on every target (use when the folder has 'Enable account-machine mappings' turned on).", "dynamic").option("--enabled", "Enable the trigger").option("--disabled", "Disable the trigger").examples(TRIGGERS_UPDATE_EXAMPLES).trackedAction(processContext, async (triggerKey, options) => {
13742
14039
  const triggerType = validateTriggerType(options.type);
13743
14040
  if (!triggerType) {
13744
14041
  emitInvalidTypeError(options.type);
@@ -13919,7 +14216,8 @@ async function createScheduleTrigger(options, isCronType) {
13919
14216
  "Unattended",
13920
14217
  "Headless",
13921
14218
  "NonProduction",
13922
- "AgentService"
14219
+ "AgentService",
14220
+ "Local"
13923
14221
  ];
13924
14222
  const matchedRuntimeType = validRuntimeTypes.find((v) => v.toLowerCase() === options.runtimeType?.toLowerCase());
13925
14223
  if (!matchedRuntimeType) {
@@ -14073,13 +14371,7 @@ async function createApiTrigger(options) {
14073
14371
  }
14074
14372
  options.method = matchedMethod;
14075
14373
  if (options.runtimeType) {
14076
- const validRuntimeTypes = [
14077
- "Serverless",
14078
- "Unattended",
14079
- "Headless",
14080
- "NonProduction",
14081
- "AgentService"
14082
- ];
14374
+ const validRuntimeTypes = VALID_API_RUNTIME_TYPES;
14083
14375
  const matchedRuntimeType = validRuntimeTypes.find((v) => v.toLowerCase() === options.runtimeType?.toLowerCase());
14084
14376
  if (!matchedRuntimeType) {
14085
14377
  OutputFormatter.error({
@@ -14224,7 +14516,7 @@ async function updateScheduleTrigger(triggerKey, options, triggerType) {
14224
14516
  async function updateApiTrigger(triggerKey, options) {
14225
14517
  if (!validateMethod(options))
14226
14518
  return;
14227
- if (!validateRuntimeType(options))
14519
+ if (!validateRuntimeType(options, VALID_API_RUNTIME_TYPES))
14228
14520
  return;
14229
14521
  if (!validateJobPriority(options))
14230
14522
  return;
@@ -15832,7 +16124,7 @@ var registerCommands = async (program) => {
15832
16124
  registerFoldersCommand(program);
15833
16125
  registerJobsCommand(program);
15834
16126
  if (isPreviewBuild()) {
15835
- const { registerKbCommand } = await import("./kb-zgjw0vpk.js");
16127
+ const { registerKbCommand } = await import("./kb-rqv35wke.js");
15836
16128
  registerKbCommand(program);
15837
16129
  }
15838
16130
  registerLibrariesCommand(program);
@@ -15854,4 +16146,4 @@ var registerCommands = async (program) => {
15854
16146
 
15855
16147
  export { metadata, registerCommands };
15856
16148
 
15857
- //# debugId=B0B77D77FA56613F64756E2164756E21
16149
+ //# debugId=1D581486482C102364756E2164756E21