@boltic/cli 1.0.5 → 1.0.6-beta.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,15 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(mkdir:*)",
5
+ "Bash(npm test)",
6
+ "Bash(npm test:*)",
7
+ "Bash(ls:*)",
8
+ "Bash(npx jest:*)",
9
+ "Bash(rm:*)",
10
+ "Bash(grep:*)",
11
+ "Bash(touch:*)"
12
+ ],
13
+ "deny": []
14
+ }
15
+ }
package/README.md CHANGED
@@ -4,7 +4,6 @@
4
4
 
5
5
  [![NPM Version](https://img.shields.io/npm/v/@boltic/cli)](https://www.npmjs.com/package/@boltic/cli)
6
6
  [![License](https://img.shields.io/npm/l/@boltic/cli)](./LICENSE)
7
- [![Node Version](https://img.shields.io/node/v/@boltic/cli)](https://nodejs.org/)
8
7
 
9
8
  ---
10
9
 
@@ -43,17 +42,14 @@ You’ll be prompted to enter:
43
42
  - **Name**: Letters and underscores only (e.g., My_Integration)
44
43
  - **Icon**: Select an SVG file from your computer
45
44
  - **Integration Type**:
46
-
47
45
  - Workflow Activity: Reusable components that perform specific tasks
48
46
  - Workflow Trigger: Components that start your workflow based on external events
49
47
  - You can choose to create both types for the same integration
50
48
 
51
49
  - **Descriptions**
52
-
53
50
  - Human-readable and AI-generated
54
51
 
55
52
  - **Integration Group**
56
-
57
53
  - e.g., Analytics, CRM, ERP, Marketing, Payment, Social Media, Other
58
54
 
59
55
  ### ✏️ Edit an Integration
@@ -19,7 +19,7 @@ const setEnvironment = async (environment) => {
19
19
 
20
20
  const getEnvironment = async (configData) => {
21
21
  try {
22
- return configData.environment || "uat";
22
+ return configData?.environment || "bolt";
23
23
  } catch (error) {
24
24
  handleError(error);
25
25
  }
package/cli.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import chalk from "chalk";
2
2
  import fs from "fs";
3
+ import path from "path";
3
4
  import EnvironmentCommands from "./commands/env.js";
4
5
  import IntegrationCommands from "./commands/integration.js";
5
6
  import AuthCommands from "./commands/login.js";
@@ -13,7 +14,8 @@ const createCLI = (consoleUrl, apiUrl, serviceName, env) => {
13
14
  const commands = {
14
15
  login: {
15
16
  description: "Authenticate the user and save access token",
16
- action: () => AuthCommands.handleLogin(consoleUrl, apiUrl, env),
17
+ action: async () =>
18
+ await AuthCommands.handleLogin(consoleUrl, apiUrl, env),
17
19
  },
18
20
  integration: {
19
21
  description: "Manage integrations (create, list)",
@@ -73,8 +75,13 @@ const createCLI = (consoleUrl, apiUrl, serviceName, env) => {
73
75
  return;
74
76
  }
75
77
 
76
- // Check if user is authenticated for all commands except login and help
77
- if (command !== "login" && command !== "help") {
78
+ // Check if user is authenticated for all commands except login, logout, help, and version
79
+ if (
80
+ command !== "login" &&
81
+ command !== "logout" &&
82
+ command !== "help" &&
83
+ command !== "version"
84
+ ) {
78
85
  const secrets = await getAllSecrets();
79
86
  const userData = secrets?.reduce(
80
87
  (acc, { account, password }) => {
@@ -101,9 +108,16 @@ const createCLI = (consoleUrl, apiUrl, serviceName, env) => {
101
108
  };
102
109
 
103
110
  async function showHelp(commands) {
104
- const packageJson = JSON.parse(
105
- fs.readFileSync(new URL("./package.json", import.meta.url))
106
- );
111
+ let packageJson;
112
+ try {
113
+ // Try to read package.json from current directory
114
+ packageJson = JSON.parse(
115
+ fs.readFileSync(path.join(process.cwd(), "package.json"))
116
+ );
117
+ } catch (_) {
118
+ // Fallback version if package.json not found
119
+ packageJson = { version: "1.0.0" };
120
+ }
107
121
  const version = packageJson.version;
108
122
 
109
123
  console.log(chalk.bold.yellow(`\nBoltic CLI Version: ${version}\n`));
@@ -127,9 +141,16 @@ async function handleEnvironment(args) {
127
141
  }
128
142
 
129
143
  async function showVersion() {
130
- const packageJson = JSON.parse(
131
- fs.readFileSync(new URL("./package.json", import.meta.url))
132
- );
144
+ let packageJson;
145
+ try {
146
+ // Try to read package.json from current directory
147
+ packageJson = JSON.parse(
148
+ fs.readFileSync(path.join(process.cwd(), "package.json"))
149
+ );
150
+ } catch (_) {
151
+ // Fallback version if package.json not found
152
+ packageJson = { version: "1.0.0" };
153
+ }
133
154
  const version = packageJson.version;
134
155
  console.log(`Boltic CLI Version: ${version}`);
135
156
  }
@@ -173,6 +173,21 @@ async function handleSync(args) {
173
173
  console.log(chalk.cyan(`\nSyncing integration: ${specContent.name}`));
174
174
 
175
175
  console.log("Validating schemas...");
176
+
177
+ // Validate resources
178
+ const { validateIntegrationSchemas } = await import(
179
+ "../helper/validation.js"
180
+ );
181
+ const validationResult = validateIntegrationSchemas(currentDir);
182
+ if (!validationResult.success) {
183
+ if (Array.isArray(validationResult.errors)) {
184
+ validationResult.errors.forEach((error) => {
185
+ console.error(chalk.red(`\n❌ ${error}`));
186
+ });
187
+ }
188
+ return;
189
+ }
190
+
176
191
  const schemas = await readSchemaFiles(currentDir);
177
192
  schemas.status = "draft";
178
193
 
@@ -256,6 +271,22 @@ async function handlePublish(args) {
256
271
  console.log(chalk.cyan(`\nSyncing integration: ${specContent.name}`));
257
272
 
258
273
  console.log("Validating schemas...");
274
+
275
+ // Validate resources
276
+ const { validateIntegrationSchemas } = await import(
277
+ "../helper/validation.js"
278
+ );
279
+ const validationResult = validateIntegrationSchemas(currentDir);
280
+ if (!validationResult.success) {
281
+ if (Array.isArray(validationResult.errors)) {
282
+ validationResult.errors.forEach((error) => {
283
+ console.error(chalk.red(`\n❌ ${error}`));
284
+ });
285
+ }
286
+
287
+ return;
288
+ }
289
+
259
290
  const schemas = await readSchemaFiles(currentDir);
260
291
  schemas.status = "draft";
261
292
 
@@ -504,6 +535,12 @@ async function handleCreate() {
504
535
  return;
505
536
  }
506
537
 
538
+ const create_catalogue = await confirm({
539
+ message:
540
+ "Would you like to create authentication form for this integration?",
541
+ default: true,
542
+ });
543
+
507
544
  // Create the integration
508
545
  try {
509
546
  const integration = await saveIntegration(
@@ -527,7 +564,7 @@ async function handleCreate() {
527
564
  trigger: trigger_ai_description || "",
528
565
  },
529
566
  },
530
- create_catalogue: true,
567
+ create_catalogue: create_catalogue,
531
568
  }
532
569
  );
533
570
 
package/helper/env.js CHANGED
@@ -6,8 +6,16 @@ import { getAllSecrets } from "./secure-storage.js";
6
6
  * @returns {Object} Environment configuration including apiUrl, token, session, and accountId
7
7
  */
8
8
  export const getCurrentEnv = async () => {
9
- const secrets = await getAllSecrets();
9
+ let secrets;
10
10
  let config;
11
+
12
+ try {
13
+ secrets = await getAllSecrets();
14
+ } catch (_) {
15
+ // If getAllSecrets fails, use default bolt environment
16
+ secrets = null;
17
+ }
18
+
11
19
  if (secrets && secrets.length > 0) {
12
20
  config = secrets.reduce((acc, { account, password }) => {
13
21
  acc[account] = password;
package/helper/folder.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import chalk from "chalk";
2
2
  import fs from "fs";
3
+ import isEmpty from "lodash.isempty";
3
4
  import path from "path";
4
5
  import {
5
6
  authentication,
@@ -48,30 +49,12 @@ export const createIntegrationFolderStructure = async (integration) => {
48
49
  "schemas/resources/resource1.json": JSON.stringify(resource1, null, 4),
49
50
  "schemas/authentication.json": JSON.stringify(authentication, null, 4),
50
51
  "schemas/base.json": JSON.stringify(base(name), null, 4),
51
- "schemas/webhook.json": JSON.stringify(webhook(name), null, 4),
52
+ ...(!isEmpty(trigger_type) && {
53
+ "schemas/webhook.json": JSON.stringify(webhook(name), null, 4),
54
+ }),
52
55
  "spec.json": JSON.stringify(spec, null, 4),
53
- "Authentication.mdx": `# ${name} Authentication
54
-
55
- Describe the authentication process for ${name} integration here.`,
56
- "Documentation.mdx": `# ${name} Documentation
57
-
58
- ${description}
59
-
60
- ## Overview
61
-
62
- Add integration overview here.
63
-
64
- ## Features
65
-
66
- - Feature 1
67
- - Feature 2
68
- - Feature 3
69
-
70
- ## Setup Guide
71
-
72
- 1. Step 1
73
- 2. Step 2
74
- 3. Step 3`,
56
+ "Authentication.mdx": `# ${name} Authentication`,
57
+ "Documentation.mdx": `# ${name} Documentation`,
75
58
  };
76
59
 
77
60
  // Create all files
@@ -0,0 +1,195 @@
1
+ import fs from "fs";
2
+ import isEmpty from "lodash.isempty";
3
+ import path from "path";
4
+
5
+ const readAndParseJson = (filePath, fileLabel, errors) => {
6
+ try {
7
+ const content = fs.readFileSync(filePath, "utf8");
8
+ if (!content.trim()) {
9
+ errors.add(`"${fileLabel}" is empty.`);
10
+ return null;
11
+ }
12
+ return JSON.parse(content);
13
+ } catch (e) {
14
+ errors.add(`Failed to read or parse "${fileLabel}": ${e.message}`);
15
+ return null;
16
+ }
17
+ };
18
+
19
+ const findResourceFieldsWithOptions = (schema) => {
20
+ const resourceFields = [];
21
+ if (Array.isArray(schema?.parameters)) {
22
+ schema.parameters.forEach((param) => {
23
+ if (
24
+ param.name === "resource" &&
25
+ Array.isArray(param.meta?.options)
26
+ ) {
27
+ resourceFields.push(
28
+ ...param.meta.options.map((opt) => opt.value)
29
+ );
30
+ }
31
+ });
32
+ }
33
+ return resourceFields;
34
+ };
35
+
36
+ const findOperationFieldsWithOptions = (schema) => {
37
+ const operationFields = [];
38
+ if (Array.isArray(schema?.parameters)) {
39
+ schema.parameters.forEach((param) => {
40
+ if (
41
+ param.name === "operation" &&
42
+ Array.isArray(param.meta?.options)
43
+ ) {
44
+ operationFields.push(
45
+ ...param.meta.options.map((opt) => opt.value)
46
+ );
47
+ }
48
+ });
49
+ }
50
+ return operationFields;
51
+ };
52
+
53
+ // ─────────────────────────────────────────────────────────────────────────────
54
+ // INDIVIDUAL VALIDATORS
55
+ // ─────────────────────────────────────────────────────────────────────────────
56
+
57
+ const validateDocumentation = (docPath, errors) => {
58
+ if (!fs.existsSync(docPath)) {
59
+ errors.add(`"Documentation.mdx" not found in the root directory.`);
60
+ }
61
+ };
62
+
63
+ const validateSpec = (specPath, errors) => {
64
+ if (!fs.existsSync(specPath)) {
65
+ errors.add(`"spec.json" not found in the root directory.`);
66
+ return null;
67
+ }
68
+ const spec = readAndParseJson(specPath, "spec.json", errors);
69
+ return spec;
70
+ };
71
+
72
+ const validateWebhook = (webhookPath, spec, errors) => {
73
+ const hasTrigger = spec && !isEmpty(spec.trigger_type);
74
+ const hasWebhook = fs.existsSync(webhookPath);
75
+
76
+ if (hasTrigger && !hasWebhook) {
77
+ errors.add(
78
+ `"webhook.json" not found, but trigger_type is defined in spec.json.`
79
+ );
80
+ }
81
+ if (!hasTrigger && hasWebhook) {
82
+ errors.add(
83
+ `"webhook.json" exists but trigger_type is not defined in spec.json.`
84
+ );
85
+ }
86
+ };
87
+
88
+ const validateBaseSchema = (baseSchemaPath, errors) => {
89
+ if (!fs.existsSync(baseSchemaPath)) {
90
+ errors.add(`"base.json" not found in the "schemas" directory.`);
91
+ return null;
92
+ }
93
+ return readAndParseJson(baseSchemaPath, "base.json", errors);
94
+ };
95
+
96
+ const validateResources = (resourcesDir, resourceFields, errors) => {
97
+ if (!fs.existsSync(resourcesDir)) {
98
+ errors.add(`"resources" directory not found in "schemas".`);
99
+ return;
100
+ }
101
+
102
+ const resourceFiles = fs
103
+ .readdirSync(resourcesDir)
104
+ .filter((f) => f.endsWith(".json"))
105
+ .map((f) => path.basename(f, ".json"));
106
+
107
+ // Check for missing resource files
108
+ resourceFields.forEach((field) => {
109
+ if (!resourceFiles.includes(field)) {
110
+ errors.add(`Resource file: "${field}.json" is missing.`);
111
+ }
112
+ });
113
+
114
+ // Validate each resource file
115
+ resourceFiles.forEach((resourceFile) => {
116
+ const filePath = path.join(resourcesDir, `${resourceFile}.json`);
117
+ const schema = readAndParseJson(
118
+ filePath,
119
+ `${resourceFile}.json`,
120
+ errors
121
+ );
122
+ if (!schema) return;
123
+
124
+ const operationFields = findOperationFieldsWithOptions(schema);
125
+
126
+ operationFields.forEach((operation) => {
127
+ const operationMethod = operation.split(".")[1];
128
+
129
+ if (!operationMethod) {
130
+ errors.add(
131
+ `Invalid format for operation "${operation}" in "${resourceFile}.json". Use "resource.operation".`
132
+ );
133
+ return;
134
+ }
135
+
136
+ const methodDef = schema[operationMethod];
137
+ if (!methodDef) {
138
+ errors.add(
139
+ `Operation "${operationMethod}" missing in "${resourceFile}.json".`
140
+ );
141
+ return;
142
+ }
143
+ if (!methodDef.parameters) {
144
+ errors.add(
145
+ `Operation "${operationMethod}" in "${resourceFile}.json" is missing parameters.`
146
+ );
147
+ }
148
+ if (!methodDef.definition) {
149
+ errors.add(
150
+ `Operation "${operationMethod}" in "${resourceFile}.json" is missing definition.`
151
+ );
152
+ }
153
+ });
154
+ });
155
+ };
156
+
157
+ // ─────────────────────────────────────────────────────────────────────────────
158
+ // MAIN FUNCTION
159
+ // ─────────────────────────────────────────────────────────────────────────────
160
+
161
+ export const validateIntegrationSchemas = (currentDir) => {
162
+ const errors = new Set();
163
+
164
+ // Define file paths
165
+ const paths = {
166
+ base: path.join(currentDir, "schemas", "base.json"),
167
+ resources: path.join(currentDir, "schemas", "resources"),
168
+ spec: path.join(currentDir, "spec.json"),
169
+ webhook: path.join(currentDir, "schemas", "webhook.json"),
170
+ documentation: path.join(currentDir, "Documentation.mdx"),
171
+ };
172
+
173
+ // Step-by-step validation
174
+ validateDocumentation(paths.documentation, errors);
175
+
176
+ const spec = validateSpec(paths.spec, errors);
177
+ validateWebhook(paths.webhook, spec, errors);
178
+
179
+ const baseSchema = validateBaseSchema(paths.base, errors);
180
+ const resourceFields = baseSchema
181
+ ? findResourceFieldsWithOptions(baseSchema)
182
+ : [];
183
+
184
+ validateResources(paths.resources, resourceFields, errors);
185
+
186
+ // Return results
187
+ if (errors.size > 0) {
188
+ return {
189
+ success: false,
190
+ errors: Array.from(errors),
191
+ };
192
+ }
193
+
194
+ return { success: true };
195
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boltic/cli",
3
- "version": "1.0.5",
3
+ "version": "1.0.6-beta.1",
4
4
  "description": "A powerful CLI tool for managing Boltic Workflow integrations",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -35,7 +35,8 @@
35
35
  "chalk": "^5.3.0",
36
36
  "keytar": "^7.9.0",
37
37
  "open": "^10.1.0",
38
- "uuid": "^11.1.0"
38
+ "uuid": "^11.1.0",
39
+ "lodash.isempty": "^4.4.0"
39
40
  },
40
41
  "devDependencies": {
41
42
  "@babel/core": "^7.26.9",