@boltic/cli 1.0.6-beta.11 → 1.0.6-beta.14
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/README.md +417 -60
- package/commands/integration.js +229 -33
- package/helper/validation.js +302 -26
- package/llm.txt +295 -0
- package/package.json +3 -3
- package/templates/component-schemas.js +929 -0
- package/templates/schemas.js +27 -15
package/helper/validation.js
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import isEmpty from "lodash.isempty";
|
|
3
3
|
import path from "path";
|
|
4
|
+
import * as componentSchemas from "../templates/component-schemas.js";
|
|
5
|
+
|
|
6
|
+
const validateOptionObject = (options, fieldName, fileLabel, errors) => {
|
|
7
|
+
options.forEach((opt, index) => {
|
|
8
|
+
const missingKeys = ["label", "value", "description"].filter(
|
|
9
|
+
(key) => !(key in opt)
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
if (missingKeys.length > 0) {
|
|
13
|
+
errors.add(
|
|
14
|
+
`"${fieldName}" field in "${fileLabel}" has an option at index ${index} missing keys: ${missingKeys.join(", ")}.`
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
};
|
|
4
19
|
|
|
5
20
|
const readAndParseJson = (filePath, fileLabel, errors) => {
|
|
6
21
|
try {
|
|
@@ -15,8 +30,7 @@ const readAndParseJson = (filePath, fileLabel, errors) => {
|
|
|
15
30
|
return null;
|
|
16
31
|
}
|
|
17
32
|
};
|
|
18
|
-
|
|
19
|
-
const findResourceFieldsWithOptions = (schema) => {
|
|
33
|
+
const findResourceFieldsWithOptions = (schema, fileLabel, errors) => {
|
|
20
34
|
const resourceFields = [];
|
|
21
35
|
if (Array.isArray(schema?.parameters)) {
|
|
22
36
|
schema.parameters.forEach((param) => {
|
|
@@ -24,6 +38,12 @@ const findResourceFieldsWithOptions = (schema) => {
|
|
|
24
38
|
param.name === "resource" &&
|
|
25
39
|
Array.isArray(param.meta?.options)
|
|
26
40
|
) {
|
|
41
|
+
validateOptionObject(
|
|
42
|
+
param.meta.options,
|
|
43
|
+
"resource",
|
|
44
|
+
fileLabel,
|
|
45
|
+
errors
|
|
46
|
+
);
|
|
27
47
|
resourceFields.push(
|
|
28
48
|
...param.meta.options.map((opt) => opt.value)
|
|
29
49
|
);
|
|
@@ -32,24 +52,226 @@ const findResourceFieldsWithOptions = (schema) => {
|
|
|
32
52
|
}
|
|
33
53
|
return resourceFields;
|
|
34
54
|
};
|
|
35
|
-
|
|
36
|
-
|
|
55
|
+
const findOperationFieldsWithOptions = (
|
|
56
|
+
schema,
|
|
57
|
+
fileLabel,
|
|
58
|
+
errors,
|
|
59
|
+
expectedResourceName
|
|
60
|
+
) => {
|
|
37
61
|
const operationFields = [];
|
|
62
|
+
|
|
38
63
|
if (Array.isArray(schema?.parameters)) {
|
|
39
64
|
schema.parameters.forEach((param) => {
|
|
40
65
|
if (
|
|
41
66
|
param.name === "operation" &&
|
|
42
67
|
Array.isArray(param.meta?.options)
|
|
43
68
|
) {
|
|
44
|
-
|
|
45
|
-
|
|
69
|
+
validateOptionObject(
|
|
70
|
+
param.meta.options,
|
|
71
|
+
"operation",
|
|
72
|
+
fileLabel,
|
|
73
|
+
errors
|
|
46
74
|
);
|
|
75
|
+
param.meta.options.forEach((opt, index) => {
|
|
76
|
+
if (typeof opt.value === "string") {
|
|
77
|
+
const parts = opt.value.split(".");
|
|
78
|
+
if (parts.length < 2) {
|
|
79
|
+
errors.add(
|
|
80
|
+
`"operation" field in "${fileLabel}" has an invalid option at index ${index} with value "${opt.value}". Expected format "resource.operation".`
|
|
81
|
+
);
|
|
82
|
+
} else {
|
|
83
|
+
const resource = parts[0];
|
|
84
|
+
if (resource !== expectedResourceName) {
|
|
85
|
+
errors.add(
|
|
86
|
+
`"operation" field in "${fileLabel}" has an inconsistent resource prefix at index ${index}. Found "${resource}" but expected "${expectedResourceName}".`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
operationFields.push(opt.value);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
47
93
|
}
|
|
48
94
|
});
|
|
49
95
|
}
|
|
96
|
+
|
|
50
97
|
return operationFields;
|
|
51
98
|
};
|
|
52
99
|
|
|
100
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
101
|
+
// VALIDATE COMPONENT SCHEMAS
|
|
102
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Extract all possible keys from a component schema structure recursively
|
|
106
|
+
* @param {Object} obj - The object to extract keys from
|
|
107
|
+
* @param {string} prefix - Current key prefix for nested objects
|
|
108
|
+
* @returns {Set<string>} Set of all allowed keys with dot notation for nested paths
|
|
109
|
+
*/
|
|
110
|
+
const extractAllowedKeys = (obj, prefix = "") => {
|
|
111
|
+
const allowedKeys = new Set();
|
|
112
|
+
|
|
113
|
+
Object.keys(obj).forEach((key) => {
|
|
114
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
115
|
+
allowedKeys.add(fullKey);
|
|
116
|
+
|
|
117
|
+
if (
|
|
118
|
+
typeof obj[key] === "object" &&
|
|
119
|
+
obj[key] !== null &&
|
|
120
|
+
!Array.isArray(obj[key])
|
|
121
|
+
) {
|
|
122
|
+
const nestedKeys = extractAllowedKeys(obj[key], fullKey);
|
|
123
|
+
nestedKeys.forEach((nestedKey) => allowedKeys.add(nestedKey));
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
return allowedKeys;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Validate that a schema object doesn't contain any extra keys
|
|
132
|
+
* @param {Object} schemaObj - The schema object to validate
|
|
133
|
+
* @param {Set<string>} allowedKeys - Set of allowed keys
|
|
134
|
+
* @param {string} schemaName - Name of the schema for error messages
|
|
135
|
+
* @param {string} displayType - The display type for error messages
|
|
136
|
+
* @param {Set} errors - Error collection
|
|
137
|
+
* @param {string} prefix - Current key prefix for nested objects
|
|
138
|
+
*/
|
|
139
|
+
const validateSchemaKeys = (
|
|
140
|
+
schemaObj,
|
|
141
|
+
allowedKeys,
|
|
142
|
+
schemaName,
|
|
143
|
+
displayType,
|
|
144
|
+
errors,
|
|
145
|
+
prefix = ""
|
|
146
|
+
) => {
|
|
147
|
+
Object.keys(schemaObj).forEach((key) => {
|
|
148
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
149
|
+
|
|
150
|
+
if (!allowedKeys.has(fullKey)) {
|
|
151
|
+
errors.add(
|
|
152
|
+
`"${schemaName}" has an invalid key "${fullKey}" for displayType "${displayType}".`
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (
|
|
157
|
+
typeof schemaObj[key] === "object" &&
|
|
158
|
+
schemaObj[key] !== null &&
|
|
159
|
+
!Array.isArray(schemaObj[key])
|
|
160
|
+
) {
|
|
161
|
+
validateSchemaKeys(
|
|
162
|
+
schemaObj[key],
|
|
163
|
+
allowedKeys,
|
|
164
|
+
schemaName,
|
|
165
|
+
displayType,
|
|
166
|
+
errors,
|
|
167
|
+
fullKey
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Validate a single component schema against its component type definition
|
|
175
|
+
* @param {Object} schema - The schema to validate
|
|
176
|
+
* @param {string} displayType - The display type to validate against
|
|
177
|
+
* @param {Set} errors - Error collection
|
|
178
|
+
*/
|
|
179
|
+
const validateComponentByType = (schema, displayType, errors) => {
|
|
180
|
+
// Get the component schema definition for this display type
|
|
181
|
+
const componentSchema = componentSchemas[displayType];
|
|
182
|
+
|
|
183
|
+
if (!componentSchema) {
|
|
184
|
+
errors.add(
|
|
185
|
+
`"${schema.name}" has an unsupported displayType "${displayType}".`
|
|
186
|
+
);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!componentSchema.meta) {
|
|
191
|
+
errors.add(
|
|
192
|
+
`Component schema for "${displayType}" is missing meta definition.`
|
|
193
|
+
);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Extract allowed keys from the component schema
|
|
198
|
+
const allowedKeys = extractAllowedKeys(componentSchema.meta);
|
|
199
|
+
|
|
200
|
+
// Validate the schema meta object (excluding displayType which we already handled)
|
|
201
|
+
const { displayType: currentDisplayType, ...restMeta } = schema.meta;
|
|
202
|
+
validateSchemaKeys(
|
|
203
|
+
restMeta,
|
|
204
|
+
allowedKeys,
|
|
205
|
+
schema.name,
|
|
206
|
+
currentDisplayType,
|
|
207
|
+
errors
|
|
208
|
+
);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const validateComponentSchemas = (schemas, errors) => {
|
|
212
|
+
schemas.forEach((schema) => {
|
|
213
|
+
// Basic required field validation
|
|
214
|
+
if (!schema.name) {
|
|
215
|
+
errors.add(`Schema is missing a name.`);
|
|
216
|
+
return; // Can't continue without a name
|
|
217
|
+
}
|
|
218
|
+
if (!schema.meta) {
|
|
219
|
+
errors.add(`"${schema.name}" is missing a meta object.`);
|
|
220
|
+
return; // Can't continue without meta
|
|
221
|
+
}
|
|
222
|
+
if (!schema.meta.displayType) {
|
|
223
|
+
errors.add(`"${schema.name}" is missing a displayType.`);
|
|
224
|
+
return; // Can't continue without displayType
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Optional field validation (these are warnings, not blocking)
|
|
228
|
+
if (!schema.meta.displayName) {
|
|
229
|
+
errors.add(`"${schema.name}" is missing a displayName.`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Only require placeholder if the component schema defines it
|
|
233
|
+
const componentSchema = componentSchemas[schema.meta.displayType];
|
|
234
|
+
if (
|
|
235
|
+
componentSchema &&
|
|
236
|
+
componentSchema.meta &&
|
|
237
|
+
"placeholder" in componentSchema.meta &&
|
|
238
|
+
!schema.meta.placeholder
|
|
239
|
+
) {
|
|
240
|
+
errors.add(`"${schema.name}" is missing a placeholder.`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Only require description if the component schema defines it
|
|
244
|
+
if (
|
|
245
|
+
componentSchema &&
|
|
246
|
+
componentSchema.meta &&
|
|
247
|
+
"description" in componentSchema.meta &&
|
|
248
|
+
!schema.meta.description
|
|
249
|
+
) {
|
|
250
|
+
errors.add(`"${schema.name}" is missing a description.`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// 🚨 Validate for duplicate options with same label and value
|
|
254
|
+
if (Array.isArray(schema.meta.options)) {
|
|
255
|
+
const seen = new Set();
|
|
256
|
+
schema.meta.options.forEach((option, index) => {
|
|
257
|
+
if (option && typeof option === "object") {
|
|
258
|
+
const key = `${option.label}::${option.value}`;
|
|
259
|
+
if (seen.has(key)) {
|
|
260
|
+
errors.add(
|
|
261
|
+
`"${schema.name}" contains duplicate option at index ${index} with label "${option.label}" and value "${option.value}".`
|
|
262
|
+
);
|
|
263
|
+
} else {
|
|
264
|
+
seen.add(key);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Validate against the specific component type schema
|
|
271
|
+
validateComponentByType(schema, schema.meta.displayType, errors);
|
|
272
|
+
});
|
|
273
|
+
};
|
|
274
|
+
|
|
53
275
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
54
276
|
// INDIVIDUAL VALIDATORS
|
|
55
277
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -83,6 +305,18 @@ const validateWebhook = (webhookPath, spec, errors) => {
|
|
|
83
305
|
`"webhook.json" exists but trigger_type is not defined in spec.json.`
|
|
84
306
|
);
|
|
85
307
|
}
|
|
308
|
+
|
|
309
|
+
// Validate webhook schema parameters if webhook exists
|
|
310
|
+
if (hasWebhook) {
|
|
311
|
+
const webhookSchema = readAndParseJson(
|
|
312
|
+
webhookPath,
|
|
313
|
+
"webhook.json",
|
|
314
|
+
errors
|
|
315
|
+
);
|
|
316
|
+
if (webhookSchema && Array.isArray(webhookSchema.parameters)) {
|
|
317
|
+
validateComponentSchemas(webhookSchema.parameters, errors);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
86
320
|
};
|
|
87
321
|
|
|
88
322
|
const validateBaseSchema = (baseSchemaPath, errors) => {
|
|
@@ -90,7 +324,49 @@ const validateBaseSchema = (baseSchemaPath, errors) => {
|
|
|
90
324
|
errors.add(`"base.json" not found in the "schemas" directory.`);
|
|
91
325
|
return null;
|
|
92
326
|
}
|
|
93
|
-
|
|
327
|
+
|
|
328
|
+
const baseSchema = readAndParseJson(baseSchemaPath, "base.json", errors);
|
|
329
|
+
|
|
330
|
+
// Validate base schema parameters
|
|
331
|
+
if (baseSchema && Array.isArray(baseSchema.parameters)) {
|
|
332
|
+
validateComponentSchemas(baseSchema.parameters, errors);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return baseSchema;
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const validateAuthentication = (authPath, errors) => {
|
|
339
|
+
// Authentication is optional, so only validate if it exists
|
|
340
|
+
if (fs.existsSync(authPath)) {
|
|
341
|
+
const authSchema = readAndParseJson(
|
|
342
|
+
authPath,
|
|
343
|
+
"authentication.json",
|
|
344
|
+
errors
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
// Validate authentication schema parameters
|
|
348
|
+
if (authSchema && Array.isArray(authSchema.parameters)) {
|
|
349
|
+
validateComponentSchemas(authSchema.parameters, errors);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Validate authentication type-specific parameters (like api_key, oauth, etc.)
|
|
353
|
+
if (authSchema) {
|
|
354
|
+
Object.keys(authSchema).forEach((key) => {
|
|
355
|
+
if (
|
|
356
|
+
key !== "parameters" &&
|
|
357
|
+
typeof authSchema[key] === "object" &&
|
|
358
|
+
authSchema[key] !== null
|
|
359
|
+
) {
|
|
360
|
+
if (Array.isArray(authSchema[key].parameters)) {
|
|
361
|
+
validateComponentSchemas(
|
|
362
|
+
authSchema[key].parameters,
|
|
363
|
+
errors
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
94
370
|
};
|
|
95
371
|
|
|
96
372
|
const validateResources = (resourcesDir, resourceFields, errors) => {
|
|
@@ -121,7 +397,17 @@ const validateResources = (resourcesDir, resourceFields, errors) => {
|
|
|
121
397
|
);
|
|
122
398
|
if (!schema) return;
|
|
123
399
|
|
|
124
|
-
|
|
400
|
+
// Validate resource file parameters
|
|
401
|
+
if (Array.isArray(schema.parameters)) {
|
|
402
|
+
validateComponentSchemas(schema.parameters, errors);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const operationFields = findOperationFieldsWithOptions(
|
|
406
|
+
schema,
|
|
407
|
+
`${resourceFile}.json`,
|
|
408
|
+
errors,
|
|
409
|
+
resourceFile
|
|
410
|
+
);
|
|
125
411
|
|
|
126
412
|
operationFields.forEach((operation) => {
|
|
127
413
|
const operationMethod = operation.split(".")[1];
|
|
@@ -144,6 +430,11 @@ const validateResources = (resourcesDir, resourceFields, errors) => {
|
|
|
144
430
|
errors.add(
|
|
145
431
|
`Operation "${operationMethod}" in "${resourceFile}.json" is missing parameters.`
|
|
146
432
|
);
|
|
433
|
+
} else {
|
|
434
|
+
// Validate operation parameters using component schemas
|
|
435
|
+
if (Array.isArray(methodDef.parameters)) {
|
|
436
|
+
validateComponentSchemas(methodDef.parameters, errors);
|
|
437
|
+
}
|
|
147
438
|
}
|
|
148
439
|
if (!methodDef.definition) {
|
|
149
440
|
errors.add(
|
|
@@ -154,23 +445,6 @@ const validateResources = (resourcesDir, resourceFields, errors) => {
|
|
|
154
445
|
});
|
|
155
446
|
};
|
|
156
447
|
|
|
157
|
-
// const validateParametersScema = (schema, errors) => {
|
|
158
|
-
// if (!schema || !Array.isArray(schema.parameters)) {
|
|
159
|
-
// errors.add(
|
|
160
|
-
// `Schema is missing or parameters are not defined as an array.`
|
|
161
|
-
// );
|
|
162
|
-
// return;
|
|
163
|
-
// }
|
|
164
|
-
// schema.parameters.forEach((param) => {
|
|
165
|
-
// const { meta } = param;
|
|
166
|
-
// if (!param.name) {
|
|
167
|
-
// errors.add(
|
|
168
|
-
// `Parameter in schema is missing a name. Ensure all parameters have a "name" field.`
|
|
169
|
-
// );
|
|
170
|
-
// }
|
|
171
|
-
// });
|
|
172
|
-
// };
|
|
173
|
-
|
|
174
448
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
175
449
|
// MAIN FUNCTION
|
|
176
450
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -184,6 +458,7 @@ export const validateIntegrationSchemas = (currentDir) => {
|
|
|
184
458
|
resources: path.join(currentDir, "schemas", "resources"),
|
|
185
459
|
spec: path.join(currentDir, "spec.json"),
|
|
186
460
|
webhook: path.join(currentDir, "schemas", "webhook.json"),
|
|
461
|
+
authentication: path.join(currentDir, "schemas", "authentication.json"),
|
|
187
462
|
documentation: path.join(currentDir, "Documentation.mdx"),
|
|
188
463
|
};
|
|
189
464
|
|
|
@@ -192,10 +467,11 @@ export const validateIntegrationSchemas = (currentDir) => {
|
|
|
192
467
|
|
|
193
468
|
const spec = validateSpec(paths.spec, errors);
|
|
194
469
|
validateWebhook(paths.webhook, spec, errors);
|
|
470
|
+
validateAuthentication(paths.authentication, errors);
|
|
195
471
|
|
|
196
472
|
const baseSchema = validateBaseSchema(paths.base, errors);
|
|
197
473
|
const resourceFields = baseSchema
|
|
198
|
-
? findResourceFieldsWithOptions(baseSchema)
|
|
474
|
+
? findResourceFieldsWithOptions(baseSchema, "base.json", errors)
|
|
199
475
|
: [];
|
|
200
476
|
|
|
201
477
|
validateResources(paths.resources, resourceFields, errors);
|
package/llm.txt
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# Boltic CLI - Developer Documentation for AI Assistants
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
The Boltic CLI is a Node.js command-line interface for managing Boltic Workflow integrations. It provides tools for creating, editing, syncing, and publishing integrations to the Boltic platform.
|
|
5
|
+
|
|
6
|
+
**Package**: @boltic/cli
|
|
7
|
+
**Repository**: https://github.com/bolticio/cli
|
|
8
|
+
**License**: ISC
|
|
9
|
+
|
|
10
|
+
## Core Architecture
|
|
11
|
+
|
|
12
|
+
### Entry Point
|
|
13
|
+
- `index.js` - Main entry point and binary executable
|
|
14
|
+
- `cli.js` - Core CLI module with command routing and execution
|
|
15
|
+
|
|
16
|
+
### Command Structure
|
|
17
|
+
The CLI follows a modular command structure:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
boltic [command] [subcommand] [options]
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Main Commands
|
|
24
|
+
|
|
25
|
+
#### Authentication Commands (`commands/login.js`)
|
|
26
|
+
- `boltic login` - Authenticate with Boltic platform
|
|
27
|
+
- `boltic logout` - Clear authentication tokens
|
|
28
|
+
|
|
29
|
+
#### Integration Commands (`commands/integration.js`)
|
|
30
|
+
- `boltic integration create` - Create new integration
|
|
31
|
+
- `boltic integration edit` - Edit existing integration
|
|
32
|
+
- `boltic integration sync` - Sync changes to draft
|
|
33
|
+
- `boltic integration submit` - Submit for review
|
|
34
|
+
- `boltic integration publish` - Submit for review (deprecated, use submit)
|
|
35
|
+
- `boltic integration pull` - Pull latest changes
|
|
36
|
+
- `boltic integration status` - Show integration details
|
|
37
|
+
|
|
38
|
+
#### Environment Commands (`commands/env.js`)
|
|
39
|
+
- `boltic env list` - List available environments
|
|
40
|
+
- `boltic env set` - Set current environment
|
|
41
|
+
- `boltic env show` - Show current environment
|
|
42
|
+
|
|
43
|
+
### API Layer (`api/`)
|
|
44
|
+
- `integration.js` - Integration management API calls
|
|
45
|
+
- `login.js` - Authentication API calls
|
|
46
|
+
- `environment.js` - Environment management API calls
|
|
47
|
+
|
|
48
|
+
### Helper Modules (`helper/`)
|
|
49
|
+
- `validation.js` - Schema validation for integrations
|
|
50
|
+
- `folder.js` - File system operations for integration folders
|
|
51
|
+
- `secure-storage.js` - Secure credential storage using keytar
|
|
52
|
+
- `env.js` - Environment configuration management
|
|
53
|
+
- `error.js` - Error handling and formatting
|
|
54
|
+
- `command-suggestions.js` - Command similarity suggestions
|
|
55
|
+
- `verbose.js` - Verbose logging control
|
|
56
|
+
|
|
57
|
+
### Templates (`templates/`)
|
|
58
|
+
- `schemas.js` - Schema templates for integrations
|
|
59
|
+
|
|
60
|
+
### Utilities (`utils/`)
|
|
61
|
+
- `integration.js` - Integration utility functions
|
|
62
|
+
|
|
63
|
+
## Key Features
|
|
64
|
+
|
|
65
|
+
### Authentication
|
|
66
|
+
- OAuth-based authentication with secure token storage
|
|
67
|
+
- Multiple environment support (bolt, fcz0, fcz5)
|
|
68
|
+
- Automatic token refresh and session management
|
|
69
|
+
|
|
70
|
+
### Integration Management
|
|
71
|
+
- **Create**: Interactive creation wizard with prompts for:
|
|
72
|
+
- Integration name (letters and underscores only)
|
|
73
|
+
- SVG icon upload
|
|
74
|
+
- Integration type (Activity, Trigger, or both)
|
|
75
|
+
- Descriptions (human and AI-generated)
|
|
76
|
+
- Integration group selection
|
|
77
|
+
- **Edit**: Modify existing integrations
|
|
78
|
+
- **Sync**: Upload changes to draft version
|
|
79
|
+
- **Submit**: Submit integration for review
|
|
80
|
+
- **Publish**: Submit integration for review (deprecated, use submit)
|
|
81
|
+
- **Pull**: Download latest changes from cloud
|
|
82
|
+
- **Status**: View integration details and metadata
|
|
83
|
+
|
|
84
|
+
### Validation System
|
|
85
|
+
The validation system (`helper/validation.js`) ensures integration schemas are correct:
|
|
86
|
+
|
|
87
|
+
#### Required Files
|
|
88
|
+
- `Documentation.mdx` - Integration documentation
|
|
89
|
+
- `spec.json` - Integration specification
|
|
90
|
+
- `schemas/base.json` - Base schema configuration
|
|
91
|
+
- `schemas/resources/*.json` - Resource-specific schemas
|
|
92
|
+
- `schemas/webhook.json` - Webhook configuration (if trigger_type defined)
|
|
93
|
+
|
|
94
|
+
#### Schema Structure
|
|
95
|
+
All option objects must include:
|
|
96
|
+
- `label` - Human-readable label
|
|
97
|
+
- `value` - Machine-readable value
|
|
98
|
+
- `description` - Detailed description
|
|
99
|
+
|
|
100
|
+
#### Validation Rules
|
|
101
|
+
- Resource fields must reference existing resource files
|
|
102
|
+
- Operation fields must reference valid operations in resource files
|
|
103
|
+
- Operations must have both `parameters` and `definition` properties
|
|
104
|
+
- Webhook configuration must match trigger_type in spec.json
|
|
105
|
+
|
|
106
|
+
### Environment Configuration
|
|
107
|
+
Three supported environments defined in `config/environments.js`:
|
|
108
|
+
- **bolt** (production): console.boltic.io
|
|
109
|
+
- **fcz0** (staging): fcz0.de
|
|
110
|
+
- **fcz5** (UAT): uat.fcz0.de
|
|
111
|
+
|
|
112
|
+
### File System Operations
|
|
113
|
+
- Automatic folder structure creation
|
|
114
|
+
- JSON file parsing and validation
|
|
115
|
+
- SVG file handling for icons
|
|
116
|
+
- Resource file management
|
|
117
|
+
|
|
118
|
+
## Development Workflow
|
|
119
|
+
|
|
120
|
+
### Testing
|
|
121
|
+
- **Framework**: Jest with comprehensive test coverage
|
|
122
|
+
- **Coverage**: 96.45% statement coverage, 91.68% branch coverage
|
|
123
|
+
- **Test Files**: Located in `__tests__/` directory
|
|
124
|
+
- **Run Tests**: `npm test`
|
|
125
|
+
|
|
126
|
+
### Code Quality
|
|
127
|
+
- **Linting**: ESLint with Prettier integration
|
|
128
|
+
- **Pre-commit**: Husky hooks with lint-staged
|
|
129
|
+
- **Standards**: ES6+ modules, async/await patterns
|
|
130
|
+
|
|
131
|
+
### Dependencies
|
|
132
|
+
**Production**:
|
|
133
|
+
- `@inquirer/prompts` - Interactive CLI prompts
|
|
134
|
+
- `axios` - HTTP client for API calls
|
|
135
|
+
- `chalk` - Terminal string styling
|
|
136
|
+
- `keytar` - Secure credential storage
|
|
137
|
+
- `open` - Browser launching
|
|
138
|
+
- `uuid` - Unique identifier generation
|
|
139
|
+
- `lodash.isempty` - Empty value checking
|
|
140
|
+
|
|
141
|
+
**Development**:
|
|
142
|
+
- `jest` - Testing framework
|
|
143
|
+
- `eslint` - Code linting
|
|
144
|
+
- `prettier` - Code formatting
|
|
145
|
+
- `husky` - Git hooks
|
|
146
|
+
- `nodemon` - Development server
|
|
147
|
+
|
|
148
|
+
## Common Patterns
|
|
149
|
+
|
|
150
|
+
### Error Handling
|
|
151
|
+
- Centralized error handling in `helper/error.js`
|
|
152
|
+
- Axios error interception and formatting
|
|
153
|
+
- User-friendly error messages with suggestions
|
|
154
|
+
|
|
155
|
+
### Async Operations
|
|
156
|
+
- Consistent async/await usage
|
|
157
|
+
- Promise-based API calls
|
|
158
|
+
- Proper error propagation
|
|
159
|
+
|
|
160
|
+
### User Interaction
|
|
161
|
+
- Interactive prompts using @inquirer/prompts
|
|
162
|
+
- Progress indicators for long operations
|
|
163
|
+
- Color-coded output using chalk
|
|
164
|
+
|
|
165
|
+
### File Operations
|
|
166
|
+
- Safe JSON parsing with error handling
|
|
167
|
+
- Atomic file operations
|
|
168
|
+
- Directory existence checking
|
|
169
|
+
|
|
170
|
+
## Configuration
|
|
171
|
+
|
|
172
|
+
### Schema Templates
|
|
173
|
+
Integration schemas are generated from templates in `templates/schemas.js`:
|
|
174
|
+
- **Authentication**: API key-based authentication
|
|
175
|
+
- **Base**: Core integration configuration
|
|
176
|
+
- **Webhook**: Trigger configuration
|
|
177
|
+
- **Resource**: Resource-specific operations
|
|
178
|
+
|
|
179
|
+
### Environment Variables
|
|
180
|
+
- Environment selection stored in secure storage
|
|
181
|
+
- API endpoints configured per environment
|
|
182
|
+
- OAuth client IDs per environment
|
|
183
|
+
|
|
184
|
+
## Security Considerations
|
|
185
|
+
|
|
186
|
+
### Credential Storage
|
|
187
|
+
- Secure token storage using keytar (OS keychain)
|
|
188
|
+
- No plaintext credential storage
|
|
189
|
+
- Automatic token cleanup on logout
|
|
190
|
+
|
|
191
|
+
### API Security
|
|
192
|
+
- OAuth 2.0 authentication flow
|
|
193
|
+
- Bearer token authentication
|
|
194
|
+
- Environment-specific API endpoints
|
|
195
|
+
|
|
196
|
+
### Input Validation
|
|
197
|
+
- Schema validation for all integration files
|
|
198
|
+
- SVG file validation for icons
|
|
199
|
+
- Input sanitization for user prompts
|
|
200
|
+
|
|
201
|
+
## Troubleshooting
|
|
202
|
+
|
|
203
|
+
### Common Issues
|
|
204
|
+
1. **Authentication Errors**: Check network connectivity and credentials
|
|
205
|
+
2. **Validation Errors**: Ensure all required schema fields are present
|
|
206
|
+
3. **File System Errors**: Verify file permissions and paths
|
|
207
|
+
4. **API Errors**: Check environment configuration and network
|
|
208
|
+
|
|
209
|
+
### Debug Mode
|
|
210
|
+
Use `--verbose` flag for detailed logging:
|
|
211
|
+
```bash
|
|
212
|
+
boltic --verbose integration create
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Log Files
|
|
216
|
+
- API calls logged in verbose mode
|
|
217
|
+
- Error stack traces available in debug mode
|
|
218
|
+
- Progress indicators for long operations
|
|
219
|
+
|
|
220
|
+
## Contributing
|
|
221
|
+
|
|
222
|
+
### Code Style
|
|
223
|
+
- ES6+ modules with import/export
|
|
224
|
+
- Async/await for asynchronous operations
|
|
225
|
+
- Functional programming patterns where applicable
|
|
226
|
+
- Comprehensive error handling
|
|
227
|
+
|
|
228
|
+
### Testing Requirements
|
|
229
|
+
- Unit tests for all new features
|
|
230
|
+
- Integration tests for API calls
|
|
231
|
+
- Mocking external dependencies
|
|
232
|
+
- Minimum 95% code coverage
|
|
233
|
+
|
|
234
|
+
### Documentation
|
|
235
|
+
- JSDoc comments for complex functions
|
|
236
|
+
- README updates for new features
|
|
237
|
+
- Schema documentation for integration formats
|
|
238
|
+
- API documentation for external integrations
|
|
239
|
+
|
|
240
|
+
## Integration Schema Format
|
|
241
|
+
|
|
242
|
+
### Base Schema Structure
|
|
243
|
+
```json
|
|
244
|
+
{
|
|
245
|
+
"parameters": [
|
|
246
|
+
{
|
|
247
|
+
"name": "resource",
|
|
248
|
+
"meta": {
|
|
249
|
+
"options": [
|
|
250
|
+
{
|
|
251
|
+
"value": "users",
|
|
252
|
+
"label": "Users",
|
|
253
|
+
"description": "Manage users"
|
|
254
|
+
}
|
|
255
|
+
]
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Resource Schema Structure
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"parameters": [
|
|
266
|
+
{
|
|
267
|
+
"name": "operation",
|
|
268
|
+
"meta": {
|
|
269
|
+
"options": [
|
|
270
|
+
{
|
|
271
|
+
"value": "users.list",
|
|
272
|
+
"label": "List Users",
|
|
273
|
+
"description": "List all users"
|
|
274
|
+
}
|
|
275
|
+
]
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
],
|
|
279
|
+
"list": {
|
|
280
|
+
"parameters": [],
|
|
281
|
+
"definition": {}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Spec.json Format
|
|
287
|
+
```json
|
|
288
|
+
{
|
|
289
|
+
"name": "Integration Name",
|
|
290
|
+
"activity_type": "customActivity",
|
|
291
|
+
"trigger_type": "webhook" // optional
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
This documentation provides a comprehensive guide for AI assistants working with the Boltic CLI codebase, covering architecture, features, development practices, and troubleshooting procedures.
|