@boltic/cli 0.0.1

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.
@@ -0,0 +1,952 @@
1
+ import { confirm, input, search } from "@inquirer/prompts";
2
+ import chalk from "chalk";
3
+ import fs from "fs";
4
+ import path from "path";
5
+
6
+ import {
7
+ editIntegration,
8
+ getIntegrationById,
9
+ getIntegrationGroups,
10
+ listAllIntegrations,
11
+ pullIntegration,
12
+ purgeCache,
13
+ saveIntegration,
14
+ sendIntegrationForReview,
15
+ syncIntegration,
16
+ updateIntegration,
17
+ uploadFileToCloud,
18
+ } from "../api/integration.js";
19
+ import {
20
+ createExistingIntegrationsFolder,
21
+ createIntegrationFolderStructure,
22
+ } from "../helper/folder.js";
23
+
24
+ import { getCurrentEnv } from "../helper/env.js";
25
+ import { pickSvgFile } from "../utils/integration.js";
26
+
27
+ // Define commands and their descriptions
28
+ const commands = {
29
+ create: {
30
+ description: "Create a new integration",
31
+ action: handleCreate,
32
+ },
33
+ edit: {
34
+ description: "Edit an existing integration",
35
+ action: handleEdit,
36
+ },
37
+ publish: {
38
+ description: "Publish an integration",
39
+ action: handlePublish,
40
+ },
41
+ sync: {
42
+ description: "Sync a draft integration",
43
+ action: handleSync,
44
+ },
45
+ pull: {
46
+ description: "Pull an integration",
47
+ action: handlePull,
48
+ },
49
+ status: {
50
+ description: "Show detailed information about an integration",
51
+ action: handleStatus,
52
+ },
53
+ help: {
54
+ description: "Show help for integration commands",
55
+ action: showHelp,
56
+ },
57
+ };
58
+
59
+ // Common function to read and parse schema files
60
+ async function readSchemaFiles(currentDir) {
61
+ const schemas = {};
62
+ const schemasDir = path.join(currentDir, "schemas");
63
+ const resourcesDir = path.join(schemasDir, "resources");
64
+
65
+ // Read authentication schema
66
+ const authSchemaPath = path.join(schemasDir, "authentication.json");
67
+ if (fs.existsSync(authSchemaPath)) {
68
+ schemas.authentication = JSON.parse(
69
+ fs.readFileSync(authSchemaPath, "utf8")
70
+ );
71
+ }
72
+
73
+ // Read base schema
74
+ const baseSchemaPath = path.join(schemasDir, "base.json");
75
+ if (fs.existsSync(baseSchemaPath)) {
76
+ schemas.configuration = JSON.parse(
77
+ fs.readFileSync(baseSchemaPath, "utf8")
78
+ );
79
+ }
80
+
81
+ // Read webhook schema
82
+ const webhookSchemaPath = path.join(schemasDir, "webhook.json");
83
+ if (fs.existsSync(webhookSchemaPath)) {
84
+ schemas.webhook = JSON.parse(
85
+ fs.readFileSync(webhookSchemaPath, "utf8")
86
+ );
87
+ }
88
+
89
+ // Read resource schemas
90
+ if (fs.existsSync(resourcesDir)) {
91
+ schemas.resources = {};
92
+ const resourceFiles = fs.readdirSync(resourcesDir);
93
+ for (const file of resourceFiles) {
94
+ if (file.endsWith(".json")) {
95
+ const resourceName = path.basename(file, ".json");
96
+ const resourcePath = path.join(resourcesDir, file);
97
+ const resourceSchema = JSON.parse(
98
+ fs.readFileSync(resourcePath, "utf8")
99
+ );
100
+ schemas.resources[resourceName] = resourceSchema;
101
+ }
102
+ }
103
+ }
104
+
105
+ // Read documentation files
106
+ const authDocPath = path.join(currentDir, "Authentication.mdx");
107
+ const generalDocPath = path.join(currentDir, "Documentation.mdx");
108
+
109
+ if (fs.existsSync(authDocPath)) {
110
+ schemas.authentication_documentation = fs.readFileSync(
111
+ authDocPath,
112
+ "utf8"
113
+ );
114
+ }
115
+
116
+ if (fs.existsSync(generalDocPath)) {
117
+ schemas.documentation = fs.readFileSync(generalDocPath, "utf8");
118
+ }
119
+
120
+ return schemas;
121
+ }
122
+
123
+ // Sync an integration
124
+ async function handleSync(args) {
125
+ // Parse command line arguments
126
+ let currentDir = process.cwd();
127
+ const pathIndex = args.indexOf("--path");
128
+
129
+ if (pathIndex !== -1 && args[pathIndex + 1]) {
130
+ currentDir = args[pathIndex + 1];
131
+ // Validate the provided path
132
+ if (!fs.existsSync(currentDir)) {
133
+ console.error(
134
+ chalk.red(
135
+ `Error: The specified path does not exist: ${currentDir}`
136
+ )
137
+ );
138
+ return;
139
+ }
140
+ }
141
+
142
+ const { apiUrl, token, accountId, session, frontendUrl } =
143
+ await getCurrentEnv();
144
+
145
+ // Read spec.json to get updated integration configuration
146
+ const specPath = path.join(currentDir, "spec.json");
147
+ if (fs.existsSync(specPath)) {
148
+ const specContent = JSON.parse(fs.readFileSync(specPath, "utf8"));
149
+ // Update integration with spec.json content
150
+ const updatedIntegration = await updateIntegration(
151
+ apiUrl,
152
+ token,
153
+ accountId,
154
+ session,
155
+ {
156
+ id: specContent.id,
157
+ name: specContent.name,
158
+ description: specContent.description,
159
+ icon: specContent.icon,
160
+ activity_type: specContent.activity_type,
161
+ trigger_type: specContent.trigger_type,
162
+ meta: specContent.meta,
163
+ }
164
+ );
165
+ if (updatedIntegration) {
166
+ console.log(
167
+ chalk.green(
168
+ "• Integration configuration updated from spec.json"
169
+ )
170
+ );
171
+ }
172
+
173
+ console.log(chalk.cyan(`\nSyncing integration: ${specContent.name}`));
174
+
175
+ console.log("Validating schemas...");
176
+ const schemas = await readSchemaFiles(currentDir);
177
+ schemas.status = "draft";
178
+
179
+ console.log(chalk.green("Schemas validated successfully"));
180
+
181
+ const data = await syncIntegration(apiUrl, token, accountId, session, {
182
+ integration_id: specContent.id,
183
+ ...schemas,
184
+ });
185
+
186
+ await purgeCache(apiUrl, token, accountId, session, {
187
+ integration_id: specContent.id,
188
+ });
189
+
190
+ if (data) {
191
+ console.log(chalk.green("• Integration synced successfully"));
192
+ // Get environment name from consoleUrl
193
+ const workflowUrl = `${frontendUrl}/accounts/${accountId}/workflow/workflow-builder`;
194
+ console.log(chalk.cyan("\nWorkflow URL:"));
195
+ console.log(chalk.underline.blue(workflowUrl));
196
+ } else {
197
+ const errorMessage =
198
+ data?.message || "API Error: Integration syncing failed";
199
+ console.error(
200
+ chalk.red(`• Failed to syncing integration: ${errorMessage}`)
201
+ );
202
+ }
203
+ }
204
+ }
205
+
206
+ // Publish an integration
207
+ async function handlePublish(args) {
208
+ console.log(chalk.green("Publishing integration...\n"));
209
+ // Parse command line arguments
210
+ let currentDir = process.cwd();
211
+ const pathIndex = args.indexOf("--path");
212
+
213
+ if (pathIndex !== -1 && args[pathIndex + 1]) {
214
+ currentDir = args[pathIndex + 1];
215
+ // Validate the provided path
216
+ if (!fs.existsSync(currentDir)) {
217
+ console.error(
218
+ chalk.red(
219
+ `Error: The specified path does not exist: ${currentDir}`
220
+ )
221
+ );
222
+ return;
223
+ }
224
+ }
225
+
226
+ const { apiUrl, token, accountId, session } = await getCurrentEnv();
227
+
228
+ // Read spec.json to get updated integration configuration
229
+ const specPath = path.join(currentDir, "spec.json");
230
+ if (fs.existsSync(specPath)) {
231
+ const specContent = JSON.parse(fs.readFileSync(specPath, "utf8"));
232
+ // Update integration with spec.json content
233
+ const updatedIntegration = await updateIntegration(
234
+ apiUrl,
235
+ token,
236
+ accountId,
237
+ session,
238
+ {
239
+ id: specContent.id,
240
+ name: specContent.name,
241
+ description: specContent.description,
242
+ icon: specContent.icon,
243
+ activity_type: specContent.activity_type,
244
+ trigger_type: specContent.trigger_type,
245
+ meta: specContent.meta,
246
+ }
247
+ );
248
+ if (updatedIntegration) {
249
+ console.log(
250
+ chalk.green(
251
+ "• Integration configuration updated from spec.json"
252
+ )
253
+ );
254
+ }
255
+
256
+ console.log(chalk.cyan(`\nSyncing integration: ${specContent.name}`));
257
+
258
+ console.log("Validating schemas...");
259
+ const schemas = await readSchemaFiles(currentDir);
260
+ schemas.status = "draft";
261
+
262
+ console.log(chalk.green("Schemas validated successfully"));
263
+
264
+ const data = await syncIntegration(apiUrl, token, accountId, session, {
265
+ integration_id: specContent.id,
266
+ ...schemas,
267
+ });
268
+
269
+ if (data) {
270
+ const review_payload = {
271
+ integration_id: specContent.id,
272
+ name: specContent.name,
273
+ description: specContent.description?.integration || "",
274
+ icon: specContent.icon,
275
+ status: "in-review",
276
+ };
277
+
278
+ const review = await sendIntegrationForReview(
279
+ apiUrl,
280
+ token,
281
+ accountId,
282
+ session,
283
+ review_payload
284
+ );
285
+
286
+ if (review) {
287
+ console.log(
288
+ chalk.green(
289
+ "\n✅ Integration sent to review successfully!\n"
290
+ )
291
+ );
292
+ }
293
+ } else {
294
+ console.error(
295
+ chalk.red("\n❌ Error publishing integration:", data.message)
296
+ );
297
+ }
298
+ }
299
+ }
300
+
301
+ // Execute the integration command
302
+ const execute = async (args) => {
303
+ const subCommand = args[0];
304
+
305
+ if (!subCommand) {
306
+ showHelp();
307
+ return;
308
+ }
309
+
310
+ if (!commands[subCommand]) {
311
+ console.log(chalk.red("Unknown or missing integration sub-command.\n"));
312
+ showHelp();
313
+ return;
314
+ }
315
+
316
+ const commandObj = commands[subCommand];
317
+ await commandObj.action(args.slice(1));
318
+ };
319
+
320
+ // Create a new integration
321
+ async function handleCreate() {
322
+ try {
323
+ // Fetch integration groups from API
324
+ const { apiUrl, token, session, accountId } = await getCurrentEnv();
325
+ let integrationGroupsChoices = [];
326
+ try {
327
+ const integrationGroups = await getIntegrationGroups(
328
+ apiUrl,
329
+ accountId,
330
+ token,
331
+ session
332
+ );
333
+
334
+ if (!integrationGroups || !Array.isArray(integrationGroups)) {
335
+ console.error(
336
+ chalk.red(
337
+ "\n❌ Failed to fetch integration groups: Invalid response format"
338
+ )
339
+ );
340
+ return;
341
+ }
342
+
343
+ integrationGroupsChoices = integrationGroups.map((group) => ({
344
+ name: group.name,
345
+ value: group.id,
346
+ }));
347
+
348
+ if (integrationGroupsChoices.length === 0) {
349
+ console.error(
350
+ chalk.red(
351
+ "\n❌ No integration groups available. Please create an integration group first."
352
+ )
353
+ );
354
+ return;
355
+ }
356
+ } catch (error) {
357
+ console.error(
358
+ chalk.red("\n❌ Failed to fetch integration groups: ") +
359
+ (error.message || "Unknown error")
360
+ );
361
+ return;
362
+ }
363
+
364
+ console.log(
365
+ chalk.green(
366
+ "Please provide the following details for the integration:\n"
367
+ )
368
+ );
369
+
370
+ // Prompt for integration details
371
+ const name = await input({
372
+ message: "Integration name (e.g., My_Integration):",
373
+ validate: (input) => {
374
+ const formattedInput = input.trim().replace(/\s+/g, "_");
375
+
376
+ if (!formattedInput) return "Name is required";
377
+ if (formattedInput.length > 50)
378
+ return "Name cannot exceed 50 characters";
379
+ if (!/^[a-zA-Z_]+$/.test(formattedInput)) {
380
+ return "Name can only contain letters and underscores (no numbers or hyphens)";
381
+ }
382
+ return true;
383
+ },
384
+ transform: (input) => input.trim().replace(/\s+/g, "_"),
385
+ });
386
+
387
+ const iconPath = await pickSvgFile();
388
+
389
+ if (!iconPath || !iconPath?.endsWith(".svg")) {
390
+ console.log(
391
+ chalk.yellow(
392
+ "⚠️ File selection was cancelled or not a valid SVG."
393
+ )
394
+ );
395
+ return;
396
+ }
397
+
398
+ if (!iconPath || !iconPath.endsWith(".svg")) {
399
+ console.error(chalk.red("❌ Invalid or no SVG file selected."));
400
+ return;
401
+ }
402
+ let icon = "";
403
+ try {
404
+ const iconData = await uploadFileToCloud(
405
+ apiUrl,
406
+ token,
407
+ accountId,
408
+ session,
409
+ iconPath
410
+ );
411
+ icon = iconData.url;
412
+ console.log(chalk.cyan(`\nIcon: ${icon}`));
413
+ } catch (e) {
414
+ console.error(chalk.red("❌ Could not upload icon: " + e.message));
415
+ return;
416
+ }
417
+
418
+ // Add boolean prompts for activity and trigger with updated terminology
419
+ const isActivity = await confirm({
420
+ message:
421
+ "Would you like to create this integration as a workflow activity? (Activities are reusable components that perform specific tasks in your workflow)",
422
+ default: true,
423
+ });
424
+
425
+ let integration_ai_description = "";
426
+ let trigger_ai_description = "";
427
+ let integration_description = "";
428
+ let trigger_description = "";
429
+
430
+ if (isActivity) {
431
+ integration_description = await input({
432
+ message: "Workflow Activity Description:",
433
+ validate: (input) => {
434
+ if (!input.trim())
435
+ return "Workflow Activity Description is required";
436
+ if (input.length > 300) {
437
+ return "Workflow Activity Description must not exceed 300 characters";
438
+ }
439
+ return true;
440
+ },
441
+ });
442
+
443
+ integration_ai_description = await input({
444
+ message: "Workflow Activity AI Description:",
445
+ validate: (input) => {
446
+ if (!input.trim())
447
+ return "Workflow Activity AI Description is required";
448
+ if (input.length > 300) {
449
+ return "Workflow Activity AI Description must not exceed 300 characters";
450
+ }
451
+ return true;
452
+ },
453
+ });
454
+ }
455
+
456
+ const isTrigger = await confirm({
457
+ message:
458
+ "Would you like to create this integration as a workflow trigger? (Triggers start your workflow based on external events)",
459
+ default: false,
460
+ });
461
+
462
+ if (isTrigger) {
463
+ trigger_description = await input({
464
+ message: "Workflow Trigger Description:",
465
+ validate: (input) => {
466
+ if (!input.trim())
467
+ return "Workflow Trigger Description is required";
468
+ if (input.length > 300) {
469
+ return "Workflow Description must not exceed 300 characters";
470
+ }
471
+ return true;
472
+ },
473
+ });
474
+
475
+ trigger_ai_description = await input({
476
+ message: "Workflow Trigger AI Description:",
477
+ validate: (input) => {
478
+ if (!input.trim())
479
+ return "Workflow Trigger AI Description is required";
480
+ if (input.length > 300) {
481
+ return "Workflow Trigger AI Description must not exceed 300 characters";
482
+ }
483
+ return true;
484
+ },
485
+ });
486
+ }
487
+
488
+ const integrationGroup = await search({
489
+ message: "Search and select an integration group:",
490
+ source: async (term) => {
491
+ if (!term) return integrationGroupsChoices;
492
+ return integrationGroupsChoices.filter((group) =>
493
+ group.name.toLowerCase().includes(term.toLowerCase())
494
+ );
495
+ },
496
+ });
497
+
498
+ if (!isActivity && !isTrigger) {
499
+ console.log(
500
+ chalk.red(
501
+ "\n❌ Both activity and trigger cannot be false. Please select at least one."
502
+ )
503
+ );
504
+ return;
505
+ }
506
+
507
+ // Create the integration
508
+ try {
509
+ const integration = await saveIntegration(
510
+ apiUrl,
511
+ token,
512
+ accountId,
513
+ session,
514
+ {
515
+ name,
516
+ icon,
517
+ activity_type: isActivity ? "customActivity" : null,
518
+ trigger_type: isTrigger ? "CloudTrigger" : null,
519
+ integration_group_id: integrationGroup,
520
+ description: {
521
+ integration: integration_description || "",
522
+ trigger: trigger_description || "",
523
+ },
524
+ meta: {
525
+ ai_description: {
526
+ integration: integration_ai_description || "",
527
+ trigger: trigger_ai_description || "",
528
+ },
529
+ },
530
+ }
531
+ );
532
+
533
+ if (integration) {
534
+ console.log(
535
+ chalk.green("\n✅ Integration created successfully!")
536
+ );
537
+
538
+ // Create folder structure with the integration name
539
+ await createIntegrationFolderStructure(integration);
540
+
541
+ // Also share Documentation URL to the user: https://docs.boltic.io/docs/activityBuilder/develop/boilerplate
542
+ const documentationUrl =
543
+ "https://docs.boltic.io/docs/activityBuilder/develop/boilerplate";
544
+ console.log(
545
+ chalk.cyan(
546
+ "\n📄 Documentation URL: " +
547
+ chalk.underline.blue(documentationUrl)
548
+ )
549
+ );
550
+ }
551
+ } catch (error) {
552
+ console.error(
553
+ chalk.red("\n❌ Failed to create integration: ") +
554
+ (error.message || "Unknown error")
555
+ );
556
+ }
557
+ } catch (error) {
558
+ if (
559
+ error.message &&
560
+ error.message.includes("User force closed the prompt")
561
+ ) {
562
+ console.log(chalk.yellow("\n⚠️ Operation cancelled by user"));
563
+ return;
564
+ }
565
+ // Handle other errors
566
+ console.error(
567
+ chalk.red("\n❌ An error occurred:"),
568
+ error.message || "Unknown error"
569
+ );
570
+ }
571
+ }
572
+
573
+ // Handle edit integration command
574
+ async function handleEdit() {
575
+ console.log(chalk.green("Please select the integration to edit...\n"));
576
+ try {
577
+ const { apiUrl, token, session, accountId } = await getCurrentEnv();
578
+ const integrations = await listAllIntegrations(
579
+ apiUrl,
580
+ token,
581
+ accountId,
582
+ session
583
+ );
584
+
585
+ if (!integrations || !Array.isArray(integrations)) {
586
+ console.error(
587
+ chalk.red(
588
+ "\n❌ Failed to fetch integrations: Invalid response format"
589
+ )
590
+ );
591
+ return;
592
+ }
593
+
594
+ if (integrations.length === 0) {
595
+ console.error(chalk.red("\n❌ No integrations found to edit."));
596
+ return;
597
+ }
598
+
599
+ // Let user select an integration
600
+ const choices =
601
+ integrations
602
+ .filter((integration) =>
603
+ ["customActivity", "CloudTrigger"].includes(
604
+ integration.activity_type || integration.trigger_type
605
+ )
606
+ )
607
+ .map((integration) => ({
608
+ name: `${integration.name} - ${integration.status} - ${integration.activity_type ? `(activity_type: ${integration.activity_type})` : ""} ${integration.trigger_type ? `(trigger_type: ${integration.trigger_type})` : ""}`,
609
+ value: integration,
610
+ })) || [];
611
+
612
+ const selectedIntegration = await search({
613
+ message: "Search and select an integration to edit:",
614
+ source: async (term) => {
615
+ if (!term) return choices;
616
+ return choices?.filter((choice) =>
617
+ choice.name.toLowerCase().includes(term.toLowerCase())
618
+ );
619
+ },
620
+ });
621
+
622
+ console.log(
623
+ chalk.cyan("\nSelected integration:"),
624
+ selectedIntegration.name
625
+ );
626
+
627
+ const draftIntegration = await editIntegration(
628
+ apiUrl,
629
+ token,
630
+ accountId,
631
+ session,
632
+ {
633
+ id: selectedIntegration.id,
634
+ parent_id: selectedIntegration.parent_id,
635
+ status: selectedIntegration.status,
636
+ }
637
+ );
638
+
639
+ if (draftIntegration) {
640
+ const isFolderCreated =
641
+ await createExistingIntegrationsFolder(draftIntegration);
642
+ if (isFolderCreated) {
643
+ console.log(
644
+ chalk.green(
645
+ "\n✅ Integration folder structure created successfully!"
646
+ )
647
+ );
648
+ }
649
+ }
650
+ } catch (error) {
651
+ if (
652
+ error.message &&
653
+ error.message.includes("User force closed the prompt")
654
+ ) {
655
+ console.log(chalk.yellow("\n⚠️ Operation cancelled by user"));
656
+ return;
657
+ }
658
+ // Handle other errors
659
+ console.error(
660
+ chalk.red("\n❌ An error occurred:"),
661
+ error.message || "Unknown error"
662
+ );
663
+ }
664
+ }
665
+
666
+ // Pull the latest content of a particular integration. It will pull the latest content from the API and update the local integration folder with the latest content. Don't create a folder this time but update the contents of the integration
667
+ async function handlePull(args) {
668
+ console.log(chalk.green("Pulling integration...\n"));
669
+ try {
670
+ // Parse command line arguments
671
+ let currentDir = process.cwd();
672
+ const pathIndex = args.indexOf("--path");
673
+
674
+ if (pathIndex !== -1 && args[pathIndex + 1]) {
675
+ currentDir = args[pathIndex + 1];
676
+ // Validate the provided path
677
+ if (!fs.existsSync(currentDir)) {
678
+ console.error(
679
+ chalk.red(
680
+ `Error: The specified path does not exist: ${currentDir}`
681
+ )
682
+ );
683
+ return;
684
+ }
685
+ }
686
+
687
+ const { apiUrl, token, accountId, session } = await getCurrentEnv();
688
+
689
+ const specPath = path.join(currentDir, "spec.json");
690
+ if (fs.existsSync(specPath)) {
691
+ const specContent = JSON.parse(fs.readFileSync(specPath, "utf8"));
692
+ const integration = await pullIntegration(
693
+ apiUrl,
694
+ token,
695
+ accountId,
696
+ session,
697
+ specContent.id
698
+ );
699
+ if (!integration) {
700
+ console.error(
701
+ chalk.red(
702
+ "\n❌ Failed to fetch integration details. Please try again later."
703
+ )
704
+ );
705
+ return;
706
+ }
707
+ const integrationName = specContent.name
708
+ .toLowerCase()
709
+ .replace(/\s+/g, "-");
710
+
711
+ const integrationDir = path.join(process.cwd(), integrationName);
712
+
713
+ if (!fs.existsSync(integrationDir)) {
714
+ console.log(
715
+ chalk.yellow(
716
+ `\nWarning: Directory ${integrationDir} does not exist. Creating it now...`
717
+ )
718
+ );
719
+ fs.mkdirSync(integrationDir, { recursive: true });
720
+ }
721
+
722
+ // Now, replace all the files and content with the latest content and everthing
723
+ const isFolderCreated =
724
+ await createExistingIntegrationsFolder(integration);
725
+ if (isFolderCreated) {
726
+ console.log(
727
+ chalk.green("\n✅ Integration folder updated successfully!")
728
+ );
729
+ }
730
+ } else {
731
+ console.log("No spec.json file found in the current directory.");
732
+ console.log(
733
+ chalk.green(
734
+ "Please select the integration to pull from the list below:"
735
+ )
736
+ );
737
+ const integrations = await listAllIntegrations(
738
+ apiUrl,
739
+ token,
740
+ accountId,
741
+ session
742
+ );
743
+ if (!integrations || !Array.isArray(integrations)) {
744
+ console.error(
745
+ chalk.red(
746
+ "\n❌ Failed to fetch integrations: Invalid response format"
747
+ )
748
+ );
749
+ }
750
+ if (integrations.length === 0) {
751
+ console.error(chalk.red("\n❌ No integrations found."));
752
+ return;
753
+ }
754
+ // Let user select an integration
755
+ const choices =
756
+ integrations
757
+ .filter((integration) =>
758
+ ["customActivity", "CloudTrigger"].includes(
759
+ integration.activity_type ||
760
+ integration.trigger_type
761
+ )
762
+ )
763
+ .map((integration) => ({
764
+ name: `${integration.name} - ${integration.status} - ${integration.activity_type ? `(activity_type: ${integration.activity_type})` : ""} ${integration.trigger_type ? `(trigger_type: ${integration.trigger_type})` : ""}`,
765
+ value: integration,
766
+ })) || [];
767
+
768
+ const selectedIntegration = await search({
769
+ message: "Search and select an integration to edit:",
770
+ source: async (term) => {
771
+ if (!term) return choices;
772
+ return choices?.filter((choice) =>
773
+ choice.name.toLowerCase().includes(term.toLowerCase())
774
+ );
775
+ },
776
+ });
777
+
778
+ console.log(
779
+ chalk.cyan("\nSelected integration:"),
780
+ selectedIntegration.name
781
+ );
782
+ const pulledIntegration = await pullIntegration(
783
+ apiUrl,
784
+ token,
785
+ accountId,
786
+ session,
787
+ selectedIntegration.id
788
+ );
789
+ if (!pulledIntegration) {
790
+ console.error(
791
+ chalk.red(
792
+ "\n❌ Failed to fetch integration details. Please try again later."
793
+ )
794
+ );
795
+ return;
796
+ }
797
+ // Update the integration folder with the latest content
798
+
799
+ const isFolderCreated =
800
+ await createExistingIntegrationsFolder(pulledIntegration);
801
+ if (isFolderCreated) {
802
+ console.log(
803
+ chalk.green(
804
+ "\n✅ Integration folder structure created successfully!"
805
+ )
806
+ );
807
+ }
808
+ }
809
+ } catch (error) {
810
+ if (
811
+ error.message &&
812
+ error.message.includes("User force closed the prompt")
813
+ ) {
814
+ console.log(chalk.yellow("\n⚠️ Operation cancelled by user"));
815
+ return;
816
+ }
817
+ // Handle other errors
818
+ console.error(
819
+ chalk.red("\n❌ An error occurred:"),
820
+ error.message || "Unknown error"
821
+ );
822
+ }
823
+ }
824
+
825
+ // Show help for integration commands
826
+ function showHelp() {
827
+ console.log(chalk.cyan("\nIntegration Commands:\n"));
828
+ Object.entries(commands).forEach(([cmd, details]) => {
829
+ console.log(chalk.bold(`${cmd}`) + ` - ${details.description}`);
830
+ });
831
+ }
832
+
833
+ // Show detailed information about an integration
834
+ async function handleStatus() {
835
+ console.log(chalk.green("Fetching integration information...\n"));
836
+
837
+ try {
838
+ const env = await getCurrentEnv();
839
+ if (!env || !env.token || !env.session) {
840
+ console.error(
841
+ chalk.red("\n❌ Authentication required. Please login first.")
842
+ );
843
+ return;
844
+ }
845
+ const { apiUrl, token, session, accountId } = env;
846
+
847
+ // Fetch all integrations
848
+ const integrations = await listAllIntegrations(
849
+ apiUrl,
850
+ token,
851
+ accountId,
852
+ session
853
+ );
854
+
855
+ if (!integrations || integrations.length === 0) {
856
+ console.log(chalk.yellow("No integrations found."));
857
+ return;
858
+ }
859
+
860
+ // Let user select an integration
861
+ const choices =
862
+ integrations
863
+ .filter((integration) =>
864
+ ["customActivity", "CloudTrigger"].includes(
865
+ integration.activity_type || integration.trigger_type
866
+ )
867
+ )
868
+ .map((integration) => ({
869
+ name: `${integration.name} ${integration.activity_type ? `(activity_type: ${integration.activity_type})` : ""} ${integration.trigger_type ? `(trigger_type: ${integration.trigger_type})` : ""}`,
870
+ value: integration.id,
871
+ })) || [];
872
+
873
+ const selectedIntegration = await search({
874
+ message: "Search and select an integration to edit:",
875
+ source: async (term) => {
876
+ if (!term) return choices;
877
+ return choices?.filter((choice) =>
878
+ choice.name.toLowerCase().includes(term.toLowerCase())
879
+ );
880
+ },
881
+ });
882
+
883
+ // Use this selected integration and do an API call using it's value. There is already an API named getIntegrationById. Use it.
884
+ const integration = await getIntegrationById(
885
+ apiUrl,
886
+ token,
887
+ accountId,
888
+ session,
889
+ selectedIntegration
890
+ );
891
+
892
+ if (!integration) {
893
+ console.log(chalk.yellow("Integration not found."));
894
+ return;
895
+ }
896
+
897
+ // Display integration details
898
+ console.log(chalk.cyan.bold("\n=== Integration Details ==="));
899
+ console.log(chalk.cyan("\nBasic Information:"));
900
+ console.log(`${chalk.dim("ID:")} ${integration.id}`);
901
+ console.log(`${chalk.dim("Name:")} ${integration.name}`);
902
+ console.log(`${chalk.dim("Slug:")} ${integration.slug}`);
903
+ console.log(
904
+ `${chalk.dim("Activity Type:")} ${integration.activity_type}`
905
+ );
906
+ console.log(
907
+ `${chalk.dim("Trigger Type:")} ${integration.trigger_type}`
908
+ );
909
+
910
+ console.log(`${chalk.dim("Description:")} ${integration.description}`);
911
+
912
+ console.log(chalk.cyan("\nStatus Information:"));
913
+ console.log(
914
+ `${chalk.dim("Status:")} ${integration.status === "published" ? chalk.green(integration.status) : chalk.yellow(integration.status)}`
915
+ );
916
+ console.log(
917
+ `${chalk.dim("Active:")} ${integration.active ? chalk.green("Yes") : chalk.red("No")}`
918
+ );
919
+
920
+ console.log(chalk.cyan("\nMeta Information:"));
921
+ console.log(
922
+ `${chalk.dim("AI Description:")} ${integration.meta?.ai_description || "N/A"}`
923
+ );
924
+ console.log(
925
+ `${chalk.dim("Is Trigger:")} ${integration.meta?.is_trigger ? "Yes" : "No"}`
926
+ );
927
+
928
+ console.log(chalk.cyan("\nTimestamps:"));
929
+ console.log(
930
+ `${chalk.dim("Created At:")} ${new Date(integration.created_at).toLocaleString()}`
931
+ );
932
+ console.log(
933
+ `${chalk.dim("Updated At:")} ${new Date(integration.updated_at).toLocaleString()}`
934
+ );
935
+ console.log(`${chalk.dim("Created By:")} ${integration.created_by}`);
936
+ console.log(`${chalk.dim("Modified By:")} ${integration.modified_by}`);
937
+
938
+ if (integration.documentation) {
939
+ console.log(chalk.cyan("\nDocumentation:"));
940
+ console.log(integration.documentation);
941
+ }
942
+ } catch (error) {
943
+ console.error(
944
+ chalk.red("\n❌ Error fetching integration status:"),
945
+ error.message || "Unknown error"
946
+ );
947
+ }
948
+ }
949
+
950
+ export default {
951
+ execute,
952
+ };