@boltic/cli 1.0.5 → 1.0.6-beta.0
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.
- package/api/environment.js +1 -1
- package/cli.js +27 -8
- package/commands/integration.js +38 -1
- package/helper/env.js +9 -1
- package/helper/folder.js +6 -23
- package/helper/validation.js +195 -0
- package/package.json +2 -1
package/api/environment.js
CHANGED
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";
|
|
@@ -73,8 +74,12 @@ const createCLI = (consoleUrl, apiUrl, serviceName, env) => {
|
|
|
73
74
|
return;
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
// Check if user is authenticated for all commands except login and
|
|
77
|
-
if (
|
|
77
|
+
// Check if user is authenticated for all commands except login, help, and version
|
|
78
|
+
if (
|
|
79
|
+
command !== "login" &&
|
|
80
|
+
command !== "help" &&
|
|
81
|
+
command !== "version"
|
|
82
|
+
) {
|
|
78
83
|
const secrets = await getAllSecrets();
|
|
79
84
|
const userData = secrets?.reduce(
|
|
80
85
|
(acc, { account, password }) => {
|
|
@@ -101,9 +106,16 @@ const createCLI = (consoleUrl, apiUrl, serviceName, env) => {
|
|
|
101
106
|
};
|
|
102
107
|
|
|
103
108
|
async function showHelp(commands) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
let packageJson;
|
|
110
|
+
try {
|
|
111
|
+
// Try to read package.json from current directory
|
|
112
|
+
packageJson = JSON.parse(
|
|
113
|
+
fs.readFileSync(path.join(process.cwd(), "package.json"))
|
|
114
|
+
);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
// Fallback version if package.json not found
|
|
117
|
+
packageJson = { version: "1.0.0" };
|
|
118
|
+
}
|
|
107
119
|
const version = packageJson.version;
|
|
108
120
|
|
|
109
121
|
console.log(chalk.bold.yellow(`\nBoltic CLI Version: ${version}\n`));
|
|
@@ -127,9 +139,16 @@ async function handleEnvironment(args) {
|
|
|
127
139
|
}
|
|
128
140
|
|
|
129
141
|
async function showVersion() {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
142
|
+
let packageJson;
|
|
143
|
+
try {
|
|
144
|
+
// Try to read package.json from current directory
|
|
145
|
+
packageJson = JSON.parse(
|
|
146
|
+
fs.readFileSync(path.join(process.cwd(), "package.json"))
|
|
147
|
+
);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
// Fallback version if package.json not found
|
|
150
|
+
packageJson = { version: "1.0.0" };
|
|
151
|
+
}
|
|
133
152
|
const version = packageJson.version;
|
|
134
153
|
console.log(`Boltic CLI Version: ${version}`);
|
|
135
154
|
}
|
package/commands/integration.js
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "1.0.6-beta.0",
|
|
4
4
|
"description": "A powerful CLI tool for managing Boltic Workflow integrations",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"husky": "^9.1.7",
|
|
52
52
|
"jest": "^29.7.0",
|
|
53
53
|
"lint-staged": "^15.4.3",
|
|
54
|
+
"lodash.isempty": "^4.4.0",
|
|
54
55
|
"nodemon": "^3.1.9",
|
|
55
56
|
"prettier": "^3.5.3"
|
|
56
57
|
}
|