@uipath/tasks-tool 1.201.0-preview.115 → 1.201.0-preview.121

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 (40) hide show
  1. package/browser.json +2 -1
  2. package/dist/folder-picker-interactive-2kj361mt.js +1850 -0
  3. package/dist/folder-resolver-htdmzdhy.js +92 -0
  4. package/dist/index.js +5 -3
  5. package/dist/tool-0n9mj887.js +2448 -0
  6. package/dist/tool-rsk1xt86.js +6026 -0
  7. package/dist/{tool-vw6gha5d.js → tool-ww671age.js} +7469 -10469
  8. package/dist/tool.js +3 -2
  9. package/package.json +2 -2
  10. package/src/commands/_shared/catalog-options.spec.ts +114 -0
  11. package/src/commands/_shared/catalog-options.ts +122 -0
  12. package/src/commands/_shared/connect.spec.ts +52 -0
  13. package/src/commands/_shared/connect.ts +31 -0
  14. package/src/commands/_shared/constants.ts +1 -0
  15. package/src/commands/_shared/folder-picker-interactive.spec.ts +92 -0
  16. package/src/commands/_shared/folder-picker-interactive.ts +76 -0
  17. package/src/commands/_shared/folder-picker.spec.ts +159 -0
  18. package/src/commands/_shared/folder-picker.ts +113 -0
  19. package/src/commands/_shared/folder-resolver.spec.ts +152 -0
  20. package/src/commands/_shared/folder-resolver.ts +113 -0
  21. package/src/commands/_shared/parse.spec.ts +69 -0
  22. package/src/commands/_shared/parse.ts +43 -0
  23. package/src/commands/catalogs.ts +277 -0
  24. package/src/commands/comments.ts +160 -0
  25. package/src/commands/data.ts +137 -0
  26. package/src/commands/labels.ts +93 -0
  27. package/src/commands/metadata.ts +139 -0
  28. package/src/commands/tasks.spec.ts +1557 -2
  29. package/src/commands/tasks.ts +191 -280
  30. package/src/tool.ts +10 -0
  31. package/src/utils/sdk-client.ts +5 -0
  32. package/tests/assignable-users.e2e.test.ts +58 -4
  33. package/tests/catalogs.e2e.test.ts +393 -0
  34. package/tests/comments.e2e.test.ts +223 -0
  35. package/tests/complete.e2e.test.ts +9 -9
  36. package/tests/data.e2e.test.ts +187 -0
  37. package/tests/get.e2e.test.ts +4 -2
  38. package/tests/labels.e2e.test.ts +184 -0
  39. package/tests/list.e2e.test.ts +110 -0
  40. package/tests/metadata.e2e.test.ts +186 -0
@@ -0,0 +1,277 @@
1
+ import {
2
+ catchError,
3
+ extractErrorMessage,
4
+ OutputFormatter,
5
+ processContext,
6
+ RESULTS,
7
+ } from "@uipath/common";
8
+ import type { Command } from "commander";
9
+
10
+ import { createTaskCatalogsClient } from "../utils/sdk-client";
11
+ import {
12
+ buildCatalogCreateOptions,
13
+ buildCatalogUpdateOptions,
14
+ type CatalogCreateOptions,
15
+ type CatalogGetOptions,
16
+ type CatalogListOptions,
17
+ type CatalogUpdateOptions,
18
+ } from "./_shared/catalog-options";
19
+ import { connectTasksClient } from "./_shared/connect";
20
+ import { DEFAULT_PAGE_SIZE } from "./_shared/constants";
21
+ import {
22
+ addFolderScopeOptions,
23
+ resolveFolderId,
24
+ } from "./_shared/folder-picker";
25
+ import { parseIntArg } from "./_shared/parse";
26
+
27
+ export const registerCatalogsCommand = (program: Command) => {
28
+ const catalogs = program
29
+ .command("catalogs")
30
+ .description("Manage task catalogs");
31
+
32
+ addFolderScopeOptions(
33
+ catalogs.command("list").description("List task catalogs in a folder"),
34
+ )
35
+ .option(
36
+ "-l, --limit <number>",
37
+ "Maximum number of items to return (fetches all if omitted)",
38
+ )
39
+ .trackedAction(processContext, async (options: CatalogListOptions) => {
40
+ const sdk = await connectTasksClient();
41
+ if (!sdk) return;
42
+
43
+ const folderId = await resolveFolderId(options);
44
+ if (folderId === null) return;
45
+
46
+ let limit: number | undefined;
47
+ if (options.limit) {
48
+ const parsed = parseIntArg(options.limit, "--limit");
49
+ if (parsed === null) return;
50
+ limit = parsed;
51
+ }
52
+
53
+ const taskCatalogs = createTaskCatalogsClient(sdk);
54
+ const allItems: unknown[] = [];
55
+ let cursor: { value: string } | undefined;
56
+
57
+ do {
58
+ const getOptions: Record<string, unknown> = {
59
+ folderId,
60
+ pageSize: DEFAULT_PAGE_SIZE,
61
+ };
62
+ if (cursor) {
63
+ getOptions.cursor = cursor;
64
+ }
65
+
66
+ const [listError, result] = await catchError(
67
+ taskCatalogs.getAll(getOptions),
68
+ );
69
+ if (listError) {
70
+ OutputFormatter.error({
71
+ Result: RESULTS.Failure,
72
+ Message: "Error listing task catalogs",
73
+ Instructions: await extractErrorMessage(listError),
74
+ });
75
+ processContext.exit(1);
76
+ return;
77
+ }
78
+
79
+ if (Array.isArray(result)) {
80
+ allItems.push(...result);
81
+ break;
82
+ }
83
+
84
+ const page = result as unknown as Record<string, unknown>;
85
+ allItems.push(...((page.items ?? []) as unknown[]));
86
+ cursor = page.hasNextPage
87
+ ? (page.nextCursor as { value: string })
88
+ : undefined;
89
+
90
+ if (limit && allItems.length >= limit) {
91
+ break;
92
+ }
93
+ } while (cursor);
94
+
95
+ OutputFormatter.success({
96
+ Result: RESULTS.Success,
97
+ Code: "TaskCatalogList",
98
+ Data: limit ? allItems.slice(0, limit) : allItems,
99
+ });
100
+ });
101
+
102
+ addFolderScopeOptions(
103
+ catalogs
104
+ .command("get")
105
+ .description("Get a task catalog by ID")
106
+ .argument("<id>", "Task catalog ID"),
107
+ ).trackedAction(
108
+ processContext,
109
+ async (id: string, options: CatalogGetOptions) => {
110
+ const sdk = await connectTasksClient();
111
+ if (!sdk) return;
112
+
113
+ const parsedId = parseIntArg(id, "<id>");
114
+ if (parsedId === null) return;
115
+
116
+ const folderId = await resolveFolderId(options);
117
+ if (folderId === null) return;
118
+
119
+ const taskCatalogs = createTaskCatalogsClient(sdk);
120
+ const [getError, catalog] = await catchError(
121
+ taskCatalogs.getById(parsedId, { folderId }),
122
+ );
123
+ if (getError) {
124
+ OutputFormatter.error({
125
+ Result: RESULTS.Failure,
126
+ Message: `Error getting task catalog '${id}'`,
127
+ Instructions: await extractErrorMessage(getError),
128
+ });
129
+ processContext.exit(1);
130
+ return;
131
+ }
132
+
133
+ OutputFormatter.success({
134
+ Result: RESULTS.Success,
135
+ Code: "TaskCatalogDetails",
136
+ Data: catalog,
137
+ });
138
+ },
139
+ );
140
+
141
+ addFolderScopeOptions(
142
+ catalogs
143
+ .command("create")
144
+ .description("Create a task catalog in a folder")
145
+ .requiredOption("--name <name>", "Catalog name (required)"),
146
+ )
147
+ .option("--description <text>", "Catalog description")
148
+ .option("--encrypted", "Encrypt the data of tasks in this catalog")
149
+ .option(
150
+ "--retention-action <action>",
151
+ "Action at the retention limit (Delete, Archive)",
152
+ )
153
+ .option("--retention-period <days>", "Retention period in days")
154
+ .option(
155
+ "--retention-bucket-id <id>",
156
+ "Storage bucket ID (used when retention action is Archive)",
157
+ )
158
+ .trackedAction(
159
+ processContext,
160
+ async (options: CatalogCreateOptions) => {
161
+ const sdk = await connectTasksClient();
162
+ if (!sdk) return;
163
+
164
+ const folderId = await resolveFolderId(options);
165
+ if (folderId === null) return;
166
+
167
+ const writeOptions = buildCatalogCreateOptions(
168
+ options,
169
+ folderId,
170
+ );
171
+ if (!writeOptions) {
172
+ return;
173
+ }
174
+
175
+ const taskCatalogs = createTaskCatalogsClient(sdk);
176
+ const [createError, catalog] = await catchError(
177
+ taskCatalogs.create(options.name, writeOptions),
178
+ );
179
+ if (createError) {
180
+ OutputFormatter.error({
181
+ Result: RESULTS.Failure,
182
+ Message: `Error creating task catalog '${options.name}'`,
183
+ Instructions: await extractErrorMessage(createError),
184
+ });
185
+ processContext.exit(1);
186
+ return;
187
+ }
188
+
189
+ OutputFormatter.success({
190
+ Result: RESULTS.Success,
191
+ Code: "TaskCatalogCreated",
192
+ Data: catalog,
193
+ });
194
+ },
195
+ );
196
+
197
+ addFolderScopeOptions(
198
+ catalogs
199
+ .command("update")
200
+ .description("Update a task catalog")
201
+ .argument("<id>", "Task catalog ID"),
202
+ )
203
+ .option("--name <name>", "New catalog name")
204
+ .option("--description <text>", "Catalog description")
205
+ .option(
206
+ "--retention-action <action>",
207
+ "Action at the retention limit (Delete, Archive)",
208
+ )
209
+ .option("--retention-period <days>", "Retention period in days")
210
+ .option(
211
+ "--retention-bucket-id <id>",
212
+ "Storage bucket ID (used when retention action is Archive)",
213
+ )
214
+ .trackedAction(
215
+ processContext,
216
+ async (id: string, options: CatalogUpdateOptions) => {
217
+ const sdk = await connectTasksClient();
218
+ if (!sdk) return;
219
+
220
+ const parsedId = parseIntArg(id, "<id>");
221
+ if (parsedId === null) return;
222
+
223
+ const hasUpdate =
224
+ options.name !== undefined ||
225
+ options.description !== undefined ||
226
+ options.retentionAction !== undefined ||
227
+ options.retentionPeriod !== undefined ||
228
+ options.retentionBucketId !== undefined;
229
+ if (!hasUpdate) {
230
+ OutputFormatter.error({
231
+ Result: RESULTS.Failure,
232
+ Message: "No catalog changes provided",
233
+ Instructions:
234
+ "Pass at least one of --name, --description, --retention-action, --retention-period, or --retention-bucket-id.",
235
+ });
236
+ processContext.exit(1);
237
+ return;
238
+ }
239
+
240
+ const folderId = await resolveFolderId(options);
241
+ if (folderId === null) return;
242
+
243
+ const writeOptions = buildCatalogUpdateOptions(
244
+ options,
245
+ folderId,
246
+ );
247
+ if (!writeOptions) {
248
+ return;
249
+ }
250
+
251
+ const taskCatalogs = createTaskCatalogsClient(sdk);
252
+ const [updateError] = await catchError(
253
+ taskCatalogs.updateById(parsedId, writeOptions),
254
+ );
255
+ if (updateError) {
256
+ OutputFormatter.error({
257
+ Result: RESULTS.Failure,
258
+ Message: `Error updating task catalog '${id}'`,
259
+ Instructions: await extractErrorMessage(updateError),
260
+ });
261
+ processContext.exit(1);
262
+ return;
263
+ }
264
+
265
+ OutputFormatter.success({
266
+ Result: RESULTS.Success,
267
+ Code: "TaskCatalogUpdated",
268
+ Data: {
269
+ id: parsedId,
270
+ ...(options.name !== undefined
271
+ ? { name: options.name }
272
+ : {}),
273
+ },
274
+ });
275
+ },
276
+ );
277
+ };
@@ -0,0 +1,160 @@
1
+ import {
2
+ catchError,
3
+ extractErrorMessage,
4
+ OutputFormatter,
5
+ processContext,
6
+ RESULTS,
7
+ } from "@uipath/common";
8
+ import type { Command } from "commander";
9
+
10
+ import { connectTasksClient } from "./_shared/connect";
11
+ import { DEFAULT_PAGE_SIZE } from "./_shared/constants";
12
+ import {
13
+ addFolderScopeOptions,
14
+ type FolderScopeOptions,
15
+ resolveFolderId,
16
+ } from "./_shared/folder-picker";
17
+ import { parseIntArg } from "./_shared/parse";
18
+
19
+ interface CommentListOptions extends FolderScopeOptions {
20
+ limit?: string;
21
+ }
22
+
23
+ interface CommentCreateOptions extends FolderScopeOptions {
24
+ text: string;
25
+ }
26
+
27
+ export const registerCommentsCommand = (program: Command) => {
28
+ const comments = program
29
+ .command("comments")
30
+ .description("Manage task comments");
31
+
32
+ addFolderScopeOptions(
33
+ comments
34
+ .command("list")
35
+ .description("List the comments on a task")
36
+ .argument("<task-id>", "Task ID"),
37
+ )
38
+ .option(
39
+ "-l, --limit <number>",
40
+ "Maximum number of items to return (fetches all if omitted)",
41
+ )
42
+ .trackedAction(
43
+ processContext,
44
+ async (taskId: string, options: CommentListOptions) => {
45
+ const sdk = await connectTasksClient();
46
+ if (!sdk) return;
47
+
48
+ const parsedTaskId = parseIntArg(taskId, "<task-id>");
49
+ if (parsedTaskId === null) return;
50
+
51
+ const folderId = await resolveFolderId(options);
52
+ if (folderId === null) return;
53
+
54
+ let limit: number | undefined;
55
+ if (options.limit) {
56
+ const parsed = parseIntArg(options.limit, "--limit");
57
+ if (parsed === null) return;
58
+ limit = parsed;
59
+ }
60
+
61
+ const allItems: unknown[] = [];
62
+ let cursor: { value: string } | undefined;
63
+
64
+ do {
65
+ const getOptions: Record<string, unknown> = {
66
+ folderId,
67
+ pageSize: DEFAULT_PAGE_SIZE,
68
+ };
69
+ if (cursor) {
70
+ getOptions.cursor = cursor;
71
+ }
72
+
73
+ const [listError, result] = await catchError(
74
+ sdk.tasks.getComments(parsedTaskId, getOptions),
75
+ );
76
+ if (listError) {
77
+ OutputFormatter.error({
78
+ Result: RESULTS.Failure,
79
+ Message: "Error listing task comments",
80
+ Instructions: await extractErrorMessage(listError),
81
+ });
82
+ processContext.exit(1);
83
+ return;
84
+ }
85
+
86
+ if (Array.isArray(result)) {
87
+ allItems.push(...result);
88
+ break;
89
+ }
90
+
91
+ const page = result as unknown as Record<string, unknown>;
92
+ allItems.push(...((page.items ?? []) as unknown[]));
93
+ cursor = page.hasNextPage
94
+ ? (page.nextCursor as { value: string })
95
+ : undefined;
96
+
97
+ if (limit && allItems.length >= limit) {
98
+ break;
99
+ }
100
+ } while (cursor);
101
+
102
+ OutputFormatter.success({
103
+ Result: RESULTS.Success,
104
+ Code: "TaskCommentList",
105
+ Data: limit ? allItems.slice(0, limit) : allItems,
106
+ });
107
+ },
108
+ );
109
+
110
+ addFolderScopeOptions(
111
+ comments
112
+ .command("add")
113
+ .description("Add a comment to a task")
114
+ .argument("<task-id>", "Task ID")
115
+ .requiredOption("--text <text>", "Comment text (required)"),
116
+ ).trackedAction(
117
+ processContext,
118
+ async (taskId: string, options: CommentCreateOptions) => {
119
+ const sdk = await connectTasksClient();
120
+ if (!sdk) return;
121
+
122
+ const parsedTaskId = parseIntArg(taskId, "<task-id>");
123
+ if (parsedTaskId === null) return;
124
+
125
+ const folderId = await resolveFolderId(options);
126
+ if (folderId === null) return;
127
+
128
+ if (!options.text) {
129
+ OutputFormatter.error({
130
+ Result: RESULTS.Failure,
131
+ Message: "--text cannot be empty",
132
+ Instructions: "Provide comment text with --text <text>.",
133
+ });
134
+ processContext.exit(1);
135
+ return;
136
+ }
137
+
138
+ const [createError, comment] = await catchError(
139
+ sdk.tasks.createComment(parsedTaskId, options.text, {
140
+ folderId,
141
+ }),
142
+ );
143
+ if (createError) {
144
+ OutputFormatter.error({
145
+ Result: RESULTS.Failure,
146
+ Message: `Error adding comment to task '${taskId}'`,
147
+ Instructions: await extractErrorMessage(createError),
148
+ });
149
+ processContext.exit(1);
150
+ return;
151
+ }
152
+
153
+ OutputFormatter.success({
154
+ Result: RESULTS.Success,
155
+ Code: "TaskCommentCreated",
156
+ Data: comment,
157
+ });
158
+ },
159
+ );
160
+ };
@@ -0,0 +1,137 @@
1
+ import {
2
+ catchError,
3
+ extractErrorMessage,
4
+ extractErrorMessageSync,
5
+ OutputFormatter,
6
+ processContext,
7
+ RESULTS,
8
+ } from "@uipath/common";
9
+ import type { Command } from "commander";
10
+
11
+ import { connectTasksClient } from "./_shared/connect";
12
+ import {
13
+ addFolderScopeOptions,
14
+ type FolderScopeOptions,
15
+ resolveFolderId,
16
+ } from "./_shared/folder-picker";
17
+ import { parseIntArg } from "./_shared/parse";
18
+
19
+ type DataGetOptions = FolderScopeOptions;
20
+
21
+ interface DataSaveOptions extends FolderScopeOptions {
22
+ data: string;
23
+ }
24
+
25
+ export const registerDataCommand = (program: Command) => {
26
+ const data = program.command("data").description("Manage task data");
27
+
28
+ addFolderScopeOptions(
29
+ data
30
+ .command("get")
31
+ .description("Get a task's data")
32
+ .argument("<task-id>", "Task ID"),
33
+ ).trackedAction(
34
+ processContext,
35
+ async (taskId: string, options: DataGetOptions) => {
36
+ const sdk = await connectTasksClient();
37
+ if (!sdk) return;
38
+
39
+ const parsedTaskId = parseIntArg(taskId, "<task-id>");
40
+ if (parsedTaskId === null) return;
41
+
42
+ const folderId = await resolveFolderId(options);
43
+ if (folderId === null) return;
44
+
45
+ const [getError, task] = await catchError(
46
+ sdk.tasks.getDataById(parsedTaskId, { folderId }),
47
+ );
48
+ if (getError) {
49
+ OutputFormatter.error({
50
+ Result: RESULTS.Failure,
51
+ Message: `Error getting data for task '${taskId}'`,
52
+ Instructions: await extractErrorMessage(getError),
53
+ });
54
+ processContext.exit(1);
55
+ return;
56
+ }
57
+
58
+ OutputFormatter.success({
59
+ Result: RESULTS.Success,
60
+ Code: "TaskData",
61
+ Data: task,
62
+ });
63
+ },
64
+ );
65
+
66
+ addFolderScopeOptions(
67
+ data
68
+ .command("save")
69
+ .description("Save a task's data")
70
+ .argument("<task-id>", "Task ID")
71
+ .requiredOption("--data <json>", "Task data as a JSON object"),
72
+ ).trackedAction(
73
+ processContext,
74
+ async (taskId: string, options: DataSaveOptions) => {
75
+ const sdk = await connectTasksClient();
76
+ if (!sdk) return;
77
+
78
+ const parsedTaskId = parseIntArg(taskId, "<task-id>");
79
+ if (parsedTaskId === null) return;
80
+
81
+ const folderId = await resolveFolderId(options);
82
+ if (folderId === null) return;
83
+
84
+ const [parseError, parsedData] = await catchError(
85
+ Promise.resolve().then(
86
+ () => JSON.parse(options.data) as unknown,
87
+ ),
88
+ );
89
+ if (parseError) {
90
+ OutputFormatter.error({
91
+ Result: RESULTS.Failure,
92
+ Message: "Invalid JSON in --data",
93
+ Instructions: extractErrorMessageSync(parseError),
94
+ });
95
+ processContext.exit(1);
96
+ return;
97
+ }
98
+ if (
99
+ typeof parsedData !== "object" ||
100
+ parsedData === null ||
101
+ Array.isArray(parsedData)
102
+ ) {
103
+ OutputFormatter.error({
104
+ Result: RESULTS.Failure,
105
+ Message: "--data must be a JSON object",
106
+ Instructions:
107
+ 'Provide a JSON object, e.g. \'{"field":"value"}\'.',
108
+ });
109
+ processContext.exit(1);
110
+ return;
111
+ }
112
+
113
+ const [saveError] = await catchError(
114
+ sdk.tasks.saveData(
115
+ parsedTaskId,
116
+ parsedData as Record<string, unknown>,
117
+ { folderId },
118
+ ),
119
+ );
120
+ if (saveError) {
121
+ OutputFormatter.error({
122
+ Result: RESULTS.Failure,
123
+ Message: `Error saving data for task '${taskId}'`,
124
+ Instructions: await extractErrorMessage(saveError),
125
+ });
126
+ processContext.exit(1);
127
+ return;
128
+ }
129
+
130
+ OutputFormatter.success({
131
+ Result: RESULTS.Success,
132
+ Code: "TaskDataSaved",
133
+ Data: { taskId: parsedTaskId },
134
+ });
135
+ },
136
+ );
137
+ };
@@ -0,0 +1,93 @@
1
+ import {
2
+ catchError,
3
+ extractErrorMessage,
4
+ extractErrorMessageSync,
5
+ OutputFormatter,
6
+ processContext,
7
+ RESULTS,
8
+ } from "@uipath/common";
9
+ import type { Tag } from "@uipath/uipath-typescript";
10
+ import type { Command } from "commander";
11
+
12
+ import { connectTasksClient } from "./_shared/connect";
13
+ import {
14
+ addFolderScopeOptions,
15
+ type FolderScopeOptions,
16
+ resolveFolderId,
17
+ } from "./_shared/folder-picker";
18
+ import { parseIntArg } from "./_shared/parse";
19
+
20
+ interface LabelsOptions extends FolderScopeOptions {
21
+ labels: string;
22
+ }
23
+
24
+ export const registerLabelsCommand = (program: Command) => {
25
+ addFolderScopeOptions(
26
+ program
27
+ .command("labels")
28
+ .description("Set or clear the labels on a task")
29
+ .argument("<task-id>", "Task ID")
30
+ .requiredOption(
31
+ "--labels <json>",
32
+ 'Labels as a JSON array, e.g. \'[{"name":"region","displayName":"Region","displayValue":"EMEA"}]\' (use \'[]\' to clear)',
33
+ ),
34
+ ).trackedAction(
35
+ processContext,
36
+ async (taskId: string, options: LabelsOptions) => {
37
+ const sdk = await connectTasksClient();
38
+ if (!sdk) return;
39
+
40
+ const parsedTaskId = parseIntArg(taskId, "<task-id>");
41
+ if (parsedTaskId === null) return;
42
+
43
+ const folderId = await resolveFolderId(options);
44
+ if (folderId === null) return;
45
+
46
+ const [parseError, parsedLabels] = await catchError(
47
+ Promise.resolve().then(
48
+ () => JSON.parse(options.labels) as unknown,
49
+ ),
50
+ );
51
+ if (parseError) {
52
+ OutputFormatter.error({
53
+ Result: RESULTS.Failure,
54
+ Message: "Invalid JSON in --labels",
55
+ Instructions: extractErrorMessageSync(parseError),
56
+ });
57
+ processContext.exit(1);
58
+ return;
59
+ }
60
+ if (!Array.isArray(parsedLabels)) {
61
+ OutputFormatter.error({
62
+ Result: RESULTS.Failure,
63
+ Message: "--labels must be a JSON array",
64
+ Instructions:
65
+ 'Provide a JSON array of labels, e.g. \'[{"name":"region","displayName":"Region","displayValue":"EMEA"}]\'.',
66
+ });
67
+ processContext.exit(1);
68
+ return;
69
+ }
70
+
71
+ const [saveError] = await catchError(
72
+ sdk.tasks.saveTags(parsedTaskId, parsedLabels as Tag[], {
73
+ folderId,
74
+ }),
75
+ );
76
+ if (saveError) {
77
+ OutputFormatter.error({
78
+ Result: RESULTS.Failure,
79
+ Message: `Error saving labels on task '${taskId}'`,
80
+ Instructions: await extractErrorMessage(saveError),
81
+ });
82
+ processContext.exit(1);
83
+ return;
84
+ }
85
+
86
+ OutputFormatter.success({
87
+ Result: RESULTS.Success,
88
+ Code: "TaskLabelsSaved",
89
+ Data: { taskId: parsedTaskId, labels: parsedLabels },
90
+ });
91
+ },
92
+ );
93
+ };