@aifabrix/builder 2.6.3 → 2.8.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/.cursor/rules/project-rules.mdc +680 -0
- package/bin/aifabrix.js +4 -0
- package/lib/app-config.js +10 -0
- package/lib/app-deploy.js +18 -0
- package/lib/app-dockerfile.js +15 -0
- package/lib/app-prompts.js +172 -9
- package/lib/app-push.js +15 -0
- package/lib/app-register.js +14 -0
- package/lib/app-run.js +25 -0
- package/lib/app.js +30 -13
- package/lib/audit-logger.js +9 -4
- package/lib/build.js +8 -0
- package/lib/cli.js +99 -2
- package/lib/commands/datasource.js +94 -0
- package/lib/commands/login.js +40 -3
- package/lib/config.js +121 -114
- package/lib/datasource-deploy.js +182 -0
- package/lib/datasource-diff.js +73 -0
- package/lib/datasource-list.js +138 -0
- package/lib/datasource-validate.js +63 -0
- package/lib/diff.js +266 -0
- package/lib/environment-deploy.js +305 -0
- package/lib/external-system-deploy.js +262 -0
- package/lib/external-system-generator.js +187 -0
- package/lib/schema/application-schema.json +869 -698
- package/lib/schema/external-datasource.schema.json +512 -0
- package/lib/schema/external-system.schema.json +262 -0
- package/lib/schema/infrastructure-schema.json +1 -1
- package/lib/secrets.js +20 -1
- package/lib/templates.js +32 -1
- package/lib/utils/device-code.js +10 -2
- package/lib/utils/env-copy.js +24 -0
- package/lib/utils/env-endpoints.js +50 -11
- package/lib/utils/schema-loader.js +220 -0
- package/lib/utils/schema-resolver.js +174 -0
- package/lib/utils/secrets-helpers.js +65 -17
- package/lib/utils/token-encryption.js +68 -0
- package/lib/validate.js +299 -0
- package/lib/validator.js +47 -3
- package/package.json +1 -1
- package/tatus +181 -0
- package/templates/external-system/external-datasource.json.hbs +55 -0
- package/templates/external-system/external-system.json.hbs +37 -0
package/lib/validate.js
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main Validation Command
|
|
3
|
+
*
|
|
4
|
+
* Validates applications or external integration files.
|
|
5
|
+
* Supports app name validation (including externalIntegration block) or direct file validation.
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Main validation command for AI Fabrix Builder
|
|
8
|
+
* @author AI Fabrix Team
|
|
9
|
+
* @version 2.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const chalk = require('chalk');
|
|
15
|
+
const validator = require('./validator');
|
|
16
|
+
const { resolveExternalFiles } = require('./utils/schema-resolver');
|
|
17
|
+
const { loadExternalSystemSchema, loadExternalDataSourceSchema, detectSchemaType } = require('./utils/schema-loader');
|
|
18
|
+
const { formatValidationErrors } = require('./utils/error-formatter');
|
|
19
|
+
const logger = require('./utils/logger');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Validates a single external file against its schema
|
|
23
|
+
*
|
|
24
|
+
* @async
|
|
25
|
+
* @function validateExternalFile
|
|
26
|
+
* @param {string} filePath - Path to the file
|
|
27
|
+
* @param {string} type - File type: 'system' | 'datasource'
|
|
28
|
+
* @returns {Promise<Object>} Validation result
|
|
29
|
+
*/
|
|
30
|
+
async function validateExternalFile(filePath, type) {
|
|
31
|
+
if (!fs.existsSync(filePath)) {
|
|
32
|
+
throw new Error(`File not found: ${filePath}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
36
|
+
let parsed;
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
parsed = JSON.parse(content);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return {
|
|
42
|
+
valid: false,
|
|
43
|
+
errors: [`Invalid JSON syntax: ${error.message}`],
|
|
44
|
+
warnings: []
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let validate;
|
|
49
|
+
if (type === 'system') {
|
|
50
|
+
validate = loadExternalSystemSchema();
|
|
51
|
+
} else if (type === 'datasource') {
|
|
52
|
+
validate = loadExternalDataSourceSchema();
|
|
53
|
+
} else {
|
|
54
|
+
throw new Error(`Unknown file type: ${type}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const valid = validate(parsed);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
valid,
|
|
61
|
+
errors: valid ? [] : formatValidationErrors(validate.errors),
|
|
62
|
+
warnings: []
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Validates application or external integration file
|
|
68
|
+
* Detects if input is app name or file path and validates accordingly
|
|
69
|
+
*
|
|
70
|
+
* @async
|
|
71
|
+
* @function validateAppOrFile
|
|
72
|
+
* @param {string} appOrFile - Application name or file path
|
|
73
|
+
* @returns {Promise<Object>} Validation result with aggregated results
|
|
74
|
+
* @throws {Error} If validation fails
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* const result = await validateAppOrFile('myapp');
|
|
78
|
+
* // Returns: { valid: true, application: {...}, externalFiles: [...] }
|
|
79
|
+
*/
|
|
80
|
+
async function validateAppOrFile(appOrFile) {
|
|
81
|
+
if (!appOrFile || typeof appOrFile !== 'string') {
|
|
82
|
+
throw new Error('App name or file path is required');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Check if it's a file path (exists and is a file)
|
|
86
|
+
const isFilePath = fs.existsSync(appOrFile) && fs.statSync(appOrFile).isFile();
|
|
87
|
+
|
|
88
|
+
if (isFilePath) {
|
|
89
|
+
// Validate single file
|
|
90
|
+
const schemaType = detectSchemaType(appOrFile);
|
|
91
|
+
let result;
|
|
92
|
+
|
|
93
|
+
if (schemaType === 'application') {
|
|
94
|
+
// For application files, we'd need to transform them first
|
|
95
|
+
// For now, just validate JSON structure
|
|
96
|
+
const content = fs.readFileSync(appOrFile, 'utf8');
|
|
97
|
+
try {
|
|
98
|
+
JSON.parse(content);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
return {
|
|
101
|
+
valid: false,
|
|
102
|
+
errors: [`Invalid JSON syntax: ${error.message}`],
|
|
103
|
+
warnings: []
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// Note: Full application validation requires variables.yaml transformation
|
|
107
|
+
// This is a simplified validation for direct JSON files
|
|
108
|
+
result = {
|
|
109
|
+
valid: true,
|
|
110
|
+
errors: [],
|
|
111
|
+
warnings: ['Application file validation is simplified. Use app name for full validation.']
|
|
112
|
+
};
|
|
113
|
+
} else if (schemaType === 'external-system') {
|
|
114
|
+
result = await validateExternalFile(appOrFile, 'system');
|
|
115
|
+
} else if (schemaType === 'external-datasource') {
|
|
116
|
+
result = await validateExternalFile(appOrFile, 'datasource');
|
|
117
|
+
} else {
|
|
118
|
+
throw new Error(`Unknown schema type: ${schemaType}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
valid: result.valid,
|
|
123
|
+
file: appOrFile,
|
|
124
|
+
type: schemaType,
|
|
125
|
+
errors: result.errors,
|
|
126
|
+
warnings: result.warnings
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Treat as app name
|
|
131
|
+
const appName = appOrFile;
|
|
132
|
+
|
|
133
|
+
// Validate application
|
|
134
|
+
const appValidation = await validator.validateApplication(appName);
|
|
135
|
+
|
|
136
|
+
// Check for externalIntegration block
|
|
137
|
+
const variablesPath = path.join(process.cwd(), 'builder', appName, 'variables.yaml');
|
|
138
|
+
if (!fs.existsSync(variablesPath)) {
|
|
139
|
+
return {
|
|
140
|
+
valid: appValidation.valid,
|
|
141
|
+
application: appValidation,
|
|
142
|
+
externalFiles: []
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const yamlLib = require('js-yaml');
|
|
147
|
+
const content = fs.readFileSync(variablesPath, 'utf8');
|
|
148
|
+
let variables;
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
variables = yamlLib.load(content);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
return {
|
|
154
|
+
valid: appValidation.valid,
|
|
155
|
+
application: appValidation,
|
|
156
|
+
externalFiles: [],
|
|
157
|
+
warnings: [`Could not parse variables.yaml to check externalIntegration: ${error.message}`]
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// If no externalIntegration block, return app validation only
|
|
162
|
+
if (!variables.externalIntegration) {
|
|
163
|
+
return {
|
|
164
|
+
valid: appValidation.valid,
|
|
165
|
+
application: appValidation,
|
|
166
|
+
externalFiles: []
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Resolve and validate external files
|
|
171
|
+
const externalFiles = await resolveExternalFiles(appName);
|
|
172
|
+
const externalValidations = [];
|
|
173
|
+
|
|
174
|
+
for (const fileInfo of externalFiles) {
|
|
175
|
+
try {
|
|
176
|
+
const validation = await validateExternalFile(fileInfo.path, fileInfo.type);
|
|
177
|
+
externalValidations.push({
|
|
178
|
+
file: fileInfo.fileName,
|
|
179
|
+
path: fileInfo.path,
|
|
180
|
+
type: fileInfo.type,
|
|
181
|
+
...validation
|
|
182
|
+
});
|
|
183
|
+
} catch (error) {
|
|
184
|
+
externalValidations.push({
|
|
185
|
+
file: fileInfo.fileName,
|
|
186
|
+
path: fileInfo.path,
|
|
187
|
+
type: fileInfo.type,
|
|
188
|
+
valid: false,
|
|
189
|
+
errors: [error.message],
|
|
190
|
+
warnings: []
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Aggregate results
|
|
196
|
+
const allValid = appValidation.valid && externalValidations.every(v => v.valid);
|
|
197
|
+
const allErrors = [
|
|
198
|
+
...appValidation.errors,
|
|
199
|
+
...externalValidations.flatMap(v => v.errors.map(e => `${v.file}: ${e}`))
|
|
200
|
+
];
|
|
201
|
+
const allWarnings = [
|
|
202
|
+
...appValidation.warnings,
|
|
203
|
+
...externalValidations.flatMap(v => v.warnings)
|
|
204
|
+
];
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
valid: allValid,
|
|
208
|
+
application: appValidation,
|
|
209
|
+
externalFiles: externalValidations,
|
|
210
|
+
errors: allErrors,
|
|
211
|
+
warnings: allWarnings
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Displays validation results in a user-friendly format
|
|
217
|
+
*
|
|
218
|
+
* @function displayValidationResults
|
|
219
|
+
* @param {Object} result - Validation result from validateAppOrFile
|
|
220
|
+
*/
|
|
221
|
+
function displayValidationResults(result) {
|
|
222
|
+
if (result.valid) {
|
|
223
|
+
logger.log(chalk.green('\n✓ Validation passed!'));
|
|
224
|
+
} else {
|
|
225
|
+
logger.log(chalk.red('\n✗ Validation failed!'));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Display application validation
|
|
229
|
+
if (result.application) {
|
|
230
|
+
logger.log(chalk.blue('\nApplication:'));
|
|
231
|
+
if (result.application.valid) {
|
|
232
|
+
logger.log(chalk.green(' ✓ Application configuration is valid'));
|
|
233
|
+
} else {
|
|
234
|
+
logger.log(chalk.red(' ✗ Application configuration has errors:'));
|
|
235
|
+
result.application.errors.forEach(error => {
|
|
236
|
+
logger.log(chalk.red(` • ${error}`));
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
if (result.application.warnings && result.application.warnings.length > 0) {
|
|
240
|
+
result.application.warnings.forEach(warning => {
|
|
241
|
+
logger.log(chalk.yellow(` ⚠ ${warning}`));
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Display external files validation
|
|
247
|
+
if (result.externalFiles && result.externalFiles.length > 0) {
|
|
248
|
+
logger.log(chalk.blue('\nExternal Integration Files:'));
|
|
249
|
+
result.externalFiles.forEach(file => {
|
|
250
|
+
if (file.valid) {
|
|
251
|
+
logger.log(chalk.green(` ✓ ${file.file} (${file.type})`));
|
|
252
|
+
} else {
|
|
253
|
+
logger.log(chalk.red(` ✗ ${file.file} (${file.type}):`));
|
|
254
|
+
file.errors.forEach(error => {
|
|
255
|
+
logger.log(chalk.red(` • ${error}`));
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
if (file.warnings && file.warnings.length > 0) {
|
|
259
|
+
file.warnings.forEach(warning => {
|
|
260
|
+
logger.log(chalk.yellow(` ⚠ ${warning}`));
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Display file validation (for direct file validation)
|
|
267
|
+
if (result.file) {
|
|
268
|
+
logger.log(chalk.blue(`\nFile: ${result.file}`));
|
|
269
|
+
logger.log(chalk.blue(`Type: ${result.type}`));
|
|
270
|
+
if (result.valid) {
|
|
271
|
+
logger.log(chalk.green(' ✓ File is valid'));
|
|
272
|
+
} else {
|
|
273
|
+
logger.log(chalk.red(' ✗ File has errors:'));
|
|
274
|
+
result.errors.forEach(error => {
|
|
275
|
+
logger.log(chalk.red(` • ${error}`));
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
279
|
+
result.warnings.forEach(warning => {
|
|
280
|
+
logger.log(chalk.yellow(` ⚠ ${warning}`));
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Display aggregated warnings
|
|
286
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
287
|
+
logger.log(chalk.yellow('\nWarnings:'));
|
|
288
|
+
result.warnings.forEach(warning => {
|
|
289
|
+
logger.log(chalk.yellow(` • ${warning}`));
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
module.exports = {
|
|
295
|
+
validateAppOrFile,
|
|
296
|
+
displayValidationResults,
|
|
297
|
+
validateExternalFile
|
|
298
|
+
};
|
|
299
|
+
|
package/lib/validator.js
CHANGED
|
@@ -14,6 +14,8 @@ const path = require('path');
|
|
|
14
14
|
const yaml = require('js-yaml');
|
|
15
15
|
const Ajv = require('ajv');
|
|
16
16
|
const applicationSchema = require('./schema/application-schema.json');
|
|
17
|
+
const externalSystemSchema = require('./schema/external-system.schema.json');
|
|
18
|
+
const externalDataSourceSchema = require('./schema/external-datasource.schema.json');
|
|
17
19
|
const { transformVariablesForValidation } = require('./utils/variable-transformer');
|
|
18
20
|
const { checkEnvironment } = require('./utils/environment-checker');
|
|
19
21
|
const { formatValidationErrors } = require('./utils/error-formatter');
|
|
@@ -56,13 +58,45 @@ async function validateVariables(appName) {
|
|
|
56
58
|
const transformed = transformVariablesForValidation(variables, appName);
|
|
57
59
|
|
|
58
60
|
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
61
|
+
// Register external schemas with their $id (GitHub raw URLs)
|
|
62
|
+
// Create copies to avoid modifying the original schemas
|
|
63
|
+
const externalSystemSchemaCopy = { ...externalSystemSchema };
|
|
64
|
+
const externalDataSourceSchemaCopy = { ...externalDataSourceSchema };
|
|
65
|
+
// Remove $schema for draft-2020-12 to avoid AJV issues
|
|
66
|
+
if (externalDataSourceSchemaCopy.$schema && externalDataSourceSchemaCopy.$schema.includes('2020-12')) {
|
|
67
|
+
delete externalDataSourceSchemaCopy.$schema;
|
|
68
|
+
}
|
|
69
|
+
ajv.addSchema(externalSystemSchemaCopy, externalSystemSchema.$id);
|
|
70
|
+
ajv.addSchema(externalDataSourceSchemaCopy, externalDataSourceSchema.$id);
|
|
59
71
|
const validate = ajv.compile(applicationSchema);
|
|
60
72
|
const valid = validate(transformed);
|
|
61
73
|
|
|
74
|
+
// Additional explicit validation for external type
|
|
75
|
+
const errors = valid ? [] : formatValidationErrors(validate.errors);
|
|
76
|
+
const warnings = [];
|
|
77
|
+
|
|
78
|
+
// If type is external, perform additional checks
|
|
79
|
+
if (variables.app && variables.app.type === 'external') {
|
|
80
|
+
// Check for externalIntegration block
|
|
81
|
+
if (!variables.externalIntegration) {
|
|
82
|
+
errors.push('externalIntegration block is required when app.type is "external"');
|
|
83
|
+
} else {
|
|
84
|
+
// Validate externalIntegration structure
|
|
85
|
+
if (!variables.externalIntegration.schemaBasePath) {
|
|
86
|
+
errors.push('externalIntegration.schemaBasePath is required');
|
|
87
|
+
}
|
|
88
|
+
if (!variables.externalIntegration.systems || !Array.isArray(variables.externalIntegration.systems) || variables.externalIntegration.systems.length === 0) {
|
|
89
|
+
errors.push('externalIntegration.systems must be a non-empty array');
|
|
90
|
+
}
|
|
91
|
+
// Note: dataSources can be empty, so we don't validate that here
|
|
92
|
+
// File existence is validated during build/deploy, not during schema validation
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
62
96
|
return {
|
|
63
|
-
valid,
|
|
64
|
-
errors
|
|
65
|
-
warnings
|
|
97
|
+
valid: valid && errors.length === 0,
|
|
98
|
+
errors,
|
|
99
|
+
warnings
|
|
66
100
|
};
|
|
67
101
|
}
|
|
68
102
|
|
|
@@ -239,6 +273,16 @@ function validateDeploymentJson(deployment) {
|
|
|
239
273
|
}
|
|
240
274
|
|
|
241
275
|
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
276
|
+
// Register external schemas with their $id (GitHub raw URLs)
|
|
277
|
+
// Create copies to avoid modifying the original schemas
|
|
278
|
+
const externalSystemSchemaCopy = { ...externalSystemSchema };
|
|
279
|
+
const externalDataSourceSchemaCopy = { ...externalDataSourceSchema };
|
|
280
|
+
// Remove $schema for draft-2020-12 to avoid AJV issues
|
|
281
|
+
if (externalDataSourceSchemaCopy.$schema && externalDataSourceSchemaCopy.$schema.includes('2020-12')) {
|
|
282
|
+
delete externalDataSourceSchemaCopy.$schema;
|
|
283
|
+
}
|
|
284
|
+
ajv.addSchema(externalSystemSchemaCopy, externalSystemSchema.$id);
|
|
285
|
+
ajv.addSchema(externalDataSourceSchemaCopy, externalDataSourceSchema.$id);
|
|
242
286
|
const validate = ajv.compile(applicationSchema);
|
|
243
287
|
const valid = validate(deployment);
|
|
244
288
|
|
package/package.json
CHANGED
package/tatus
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
|
|
2
|
+
SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS
|
|
3
|
+
|
|
4
|
+
Commands marked with * may be preceded by a number, _N.
|
|
5
|
+
Notes in parentheses indicate the behavior if _N is given.
|
|
6
|
+
A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
|
|
7
|
+
|
|
8
|
+
h H Display this help.
|
|
9
|
+
q :q Q :Q ZZ Exit.
|
|
10
|
+
---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
MMOOVVIINNGG
|
|
13
|
+
|
|
14
|
+
e ^E j ^N CR * Forward one line (or _N lines).
|
|
15
|
+
y ^Y k ^K ^P * Backward one line (or _N lines).
|
|
16
|
+
f ^F ^V SPACE * Forward one window (or _N lines).
|
|
17
|
+
b ^B ESC-v * Backward one window (or _N lines).
|
|
18
|
+
z * Forward one window (and set window to _N).
|
|
19
|
+
w * Backward one window (and set window to _N).
|
|
20
|
+
ESC-SPACE * Forward one window, but don't stop at end-of-file.
|
|
21
|
+
d ^D * Forward one half-window (and set half-window to _N).
|
|
22
|
+
u ^U * Backward one half-window (and set half-window to _N).
|
|
23
|
+
ESC-) RightArrow * Right one half screen width (or _N positions).
|
|
24
|
+
ESC-( LeftArrow * Left one half screen width (or _N positions).
|
|
25
|
+
ESC-} ^RightArrow Right to last column displayed.
|
|
26
|
+
ESC-{ ^LeftArrow Left to first column.
|
|
27
|
+
F Forward forever; like "tail -f".
|
|
28
|
+
ESC-F Like F but stop when search pattern is found.
|
|
29
|
+
r ^R ^L Repaint screen.
|
|
30
|
+
R Repaint screen, discarding buffered input.
|
|
31
|
+
---------------------------------------------------
|
|
32
|
+
Default "window" is the screen height.
|
|
33
|
+
Default "half-window" is half of the screen height.
|
|
34
|
+
---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
SSEEAARRCCHHIINNGG
|
|
37
|
+
|
|
38
|
+
/_p_a_t_t_e_r_n * Search forward for (_N-th) matching line.
|
|
39
|
+
?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line.
|
|
40
|
+
n * Repeat previous search (for _N-th occurrence).
|
|
41
|
+
N * Repeat previous search in reverse direction.
|
|
42
|
+
ESC-n * Repeat previous search, spanning files.
|
|
43
|
+
ESC-N * Repeat previous search, reverse dir. & spanning files.
|
|
44
|
+
^O^N ^On * Search forward for (_N-th) OSC8 hyperlink.
|
|
45
|
+
^O^P ^Op * Search backward for (_N-th) OSC8 hyperlink.
|
|
46
|
+
^O^L ^Ol Jump to the currently selected OSC8 hyperlink.
|
|
47
|
+
ESC-u Undo (toggle) search highlighting.
|
|
48
|
+
ESC-U Clear search highlighting.
|
|
49
|
+
&_p_a_t_t_e_r_n * Display only matching lines.
|
|
50
|
+
---------------------------------------------------
|
|
51
|
+
A search pattern may begin with one or more of:
|
|
52
|
+
^N or ! Search for NON-matching lines.
|
|
53
|
+
^E or * Search multiple files (pass thru END OF FILE).
|
|
54
|
+
^F or @ Start search at FIRST file (for /) or last file (for ?).
|
|
55
|
+
^K Highlight matches, but don't move (KEEP position).
|
|
56
|
+
^R Don't use REGULAR EXPRESSIONS.
|
|
57
|
+
^S _n Search for match in _n-th parenthesized subpattern.
|
|
58
|
+
^W WRAP search if no match found.
|
|
59
|
+
^L Enter next character literally into pattern.
|
|
60
|
+
---------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
JJUUMMPPIINNGG
|
|
63
|
+
|
|
64
|
+
g < ESC-< * Go to first line in file (or line _N).
|
|
65
|
+
G > ESC-> * Go to last line in file (or line _N).
|
|
66
|
+
p % * Go to beginning of file (or _N percent into file).
|
|
67
|
+
t * Go to the (_N-th) next tag.
|
|
68
|
+
T * Go to the (_N-th) previous tag.
|
|
69
|
+
{ ( [ * Find close bracket } ) ].
|
|
70
|
+
} ) ] * Find open bracket { ( [.
|
|
71
|
+
ESC-^F _<_c_1_> _<_c_2_> * Find close bracket _<_c_2_>.
|
|
72
|
+
ESC-^B _<_c_1_> _<_c_2_> * Find open bracket _<_c_1_>.
|
|
73
|
+
---------------------------------------------------
|
|
74
|
+
Each "find close bracket" command goes forward to the close bracket
|
|
75
|
+
matching the (_N-th) open bracket in the top line.
|
|
76
|
+
Each "find open bracket" command goes backward to the open bracket
|
|
77
|
+
matching the (_N-th) close bracket in the bottom line.
|
|
78
|
+
|
|
79
|
+
m_<_l_e_t_t_e_r_> Mark the current top line with <letter>.
|
|
80
|
+
M_<_l_e_t_t_e_r_> Mark the current bottom line with <letter>.
|
|
81
|
+
'_<_l_e_t_t_e_r_> Go to a previously marked position.
|
|
82
|
+
'' Go to the previous position.
|
|
83
|
+
^X^X Same as '.
|
|
84
|
+
ESC-m_<_l_e_t_t_e_r_> Clear a mark.
|
|
85
|
+
---------------------------------------------------
|
|
86
|
+
A mark is any upper-case or lower-case letter.
|
|
87
|
+
Certain marks are predefined:
|
|
88
|
+
^ means beginning of the file
|
|
89
|
+
$ means end of the file
|
|
90
|
+
---------------------------------------------------------------------------
|
|
91
|
+
|
|
92
|
+
CCHHAANNGGIINNGG FFIILLEESS
|
|
93
|
+
|
|
94
|
+
:e [_f_i_l_e] Examine a new file.
|
|
95
|
+
^X^V Same as :e.
|
|
96
|
+
:n * Examine the (_N-th) next file from the command line.
|
|
97
|
+
:p * Examine the (_N-th) previous file from the command line.
|
|
98
|
+
:x * Examine the first (or _N-th) file from the command line.
|
|
99
|
+
^O^O Open the currently selected OSC8 hyperlink.
|
|
100
|
+
:d Delete the current file from the command line list.
|
|
101
|
+
= ^G :f Print current file name.
|
|
102
|
+
---------------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS
|
|
105
|
+
|
|
106
|
+
-_<_f_l_a_g_> Toggle a command line option [see OPTIONS below].
|
|
107
|
+
--_<_n_a_m_e_> Toggle a command line option, by name.
|
|
108
|
+
__<_f_l_a_g_> Display the setting of a command line option.
|
|
109
|
+
___<_n_a_m_e_> Display the setting of an option, by name.
|
|
110
|
+
+_c_m_d Execute the less cmd each time a new file is examined.
|
|
111
|
+
|
|
112
|
+
!_c_o_m_m_a_n_d Execute the shell command with $SHELL.
|
|
113
|
+
#_c_o_m_m_a_n_d Execute the shell command, expanded like a prompt.
|
|
114
|
+
|XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command.
|
|
115
|
+
s _f_i_l_e Save input to a file.
|
|
116
|
+
v Edit the current file with $VISUAL or $EDITOR.
|
|
117
|
+
V Print version number of "less".
|
|
118
|
+
---------------------------------------------------------------------------
|
|
119
|
+
|
|
120
|
+
OOPPTTIIOONNSS
|
|
121
|
+
|
|
122
|
+
Most options may be changed either on the command line,
|
|
123
|
+
or from within less by using the - or -- command.
|
|
124
|
+
Options may be given in one of two forms: either a single
|
|
125
|
+
character preceded by a -, or a name preceded by --.
|
|
126
|
+
|
|
127
|
+
-? ........ --help
|
|
128
|
+
Display help (from command line).
|
|
129
|
+
-a ........ --search-skip-screen
|
|
130
|
+
Search skips current screen.
|
|
131
|
+
-A ........ --SEARCH-SKIP-SCREEN
|
|
132
|
+
Search starts just after target line.
|
|
133
|
+
-b [_N] .... --buffers=[_N]
|
|
134
|
+
Number of buffers.
|
|
135
|
+
-B ........ --auto-buffers
|
|
136
|
+
Don't automatically allocate buffers for pipes.
|
|
137
|
+
-c ........ --clear-screen
|
|
138
|
+
Repaint by clearing rather than scrolling.
|
|
139
|
+
-d ........ --dumb
|
|
140
|
+
Dumb terminal.
|
|
141
|
+
-D xx_c_o_l_o_r . --color=xx_c_o_l_o_r
|
|
142
|
+
Set screen colors.
|
|
143
|
+
-e -E .... --quit-at-eof --QUIT-AT-EOF
|
|
144
|
+
Quit at end of file.
|
|
145
|
+
-f ........ --force
|
|
146
|
+
Force open non-regular files.
|
|
147
|
+
-F ........ --quit-if-one-screen
|
|
148
|
+
Quit if entire file fits on first screen.
|
|
149
|
+
-g ........ --hilite-search
|
|
150
|
+
Highlight only last match for searches.
|
|
151
|
+
-G ........ --HILITE-SEARCH
|
|
152
|
+
Don't highlight any matches for searches.
|
|
153
|
+
-h [_N] .... --max-back-scroll=[_N]
|
|
154
|
+
Backward scroll limit.
|
|
155
|
+
-i ........ --ignore-case
|
|
156
|
+
Ignore case in searches that do not contain uppercase.
|
|
157
|
+
-I ........ --IGNORE-CASE
|
|
158
|
+
Ignore case in all searches.
|
|
159
|
+
-j [_N] .... --jump-target=[_N]
|
|
160
|
+
Screen position of target lines.
|
|
161
|
+
-J ........ --status-column
|
|
162
|
+
Display a status column at left edge of screen.
|
|
163
|
+
-k _f_i_l_e ... --lesskey-file=_f_i_l_e
|
|
164
|
+
Use a compiled lesskey file.
|
|
165
|
+
-K ........ --quit-on-intr
|
|
166
|
+
Exit less in response to ctrl-C.
|
|
167
|
+
-L ........ --no-lessopen
|
|
168
|
+
Ignore the LESSOPEN environment variable.
|
|
169
|
+
-m -M .... --long-prompt --LONG-PROMPT
|
|
170
|
+
Set prompt style.
|
|
171
|
+
-n ......... --line-numbers
|
|
172
|
+
Suppress line numbers in prompts and messages.
|
|
173
|
+
-N ......... --LINE-NUMBERS
|
|
174
|
+
Display line number at start of each line.
|
|
175
|
+
-o [_f_i_l_e] .. --log-file=[_f_i_l_e]
|
|
176
|
+
Copy to log file (standard input only).
|
|
177
|
+
-O [_f_i_l_e] .. --LOG-FILE=[_f_i_l_e]
|
|
178
|
+
Copy to log file (unconditionally overwrite).
|
|
179
|
+
-p _p_a_t_t_e_r_n . --pattern=[_p_a_t_t_e_r_n]
|
|
180
|
+
Start at pattern (from command line).
|
|
181
|
+
-P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"key": "{{datasourceKey}}",
|
|
3
|
+
"displayName": "{{datasourceDisplayName}}",
|
|
4
|
+
"description": "{{datasourceDescription}}",
|
|
5
|
+
"systemKey": "{{systemKey}}",
|
|
6
|
+
"entityKey": "{{entityKey}}",
|
|
7
|
+
"resourceType": "{{resourceType}}",
|
|
8
|
+
"enabled": true,
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"metadataSchema": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"properties": {}
|
|
13
|
+
},
|
|
14
|
+
"fieldMappings": {
|
|
15
|
+
"accessFields": ["id", "name"],
|
|
16
|
+
"fields": {
|
|
17
|
+
"id": {
|
|
18
|
+
"expression": "{{raw.id}}",
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "Unique identifier",
|
|
21
|
+
"required": true
|
|
22
|
+
},
|
|
23
|
+
"name": {
|
|
24
|
+
"expression": "{{raw.name}}",
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "Display name",
|
|
27
|
+
"required": true
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"exposed": {
|
|
32
|
+
"fields": ["id", "name"],
|
|
33
|
+
"description": "Exposed fields for {{datasourceDisplayName}}"
|
|
34
|
+
}{{#if (eq systemType "openapi")}},
|
|
35
|
+
"openapi": {
|
|
36
|
+
"enabled": true,
|
|
37
|
+
"documentKey": "{{systemKey}}-api",
|
|
38
|
+
"baseUrl": "https://api.example.com",
|
|
39
|
+
"resourcePath": "/{{entityKey}}",
|
|
40
|
+
"operations": {
|
|
41
|
+
"list": {
|
|
42
|
+
"operationId": "list{{entityKey}}",
|
|
43
|
+
"method": "GET",
|
|
44
|
+
"path": "/{{entityKey}}"
|
|
45
|
+
},
|
|
46
|
+
"get": {
|
|
47
|
+
"operationId": "get{{entityKey}}",
|
|
48
|
+
"method": "GET",
|
|
49
|
+
"path": "/{{entityKey}}/{id}"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"autoRbac": true
|
|
53
|
+
}{{/if}}
|
|
54
|
+
}
|
|
55
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"key": "{{systemKey}}",
|
|
3
|
+
"displayName": "{{systemDisplayName}}",
|
|
4
|
+
"description": "{{systemDescription}}",
|
|
5
|
+
"type": "{{systemType}}",
|
|
6
|
+
"enabled": true,
|
|
7
|
+
"environment": {
|
|
8
|
+
"baseUrl": "https://api.example.com"
|
|
9
|
+
},
|
|
10
|
+
"authentication": {
|
|
11
|
+
"mode": "{{authType}}"{{#if (eq authType "oauth2")}},
|
|
12
|
+
"oauth2": {
|
|
13
|
+
"tokenUrl": "https://api.example.com/oauth/token",
|
|
14
|
+
"clientId": "kv://{{systemKey}}-oauth2-client-id",
|
|
15
|
+
"clientSecret": "kv://{{systemKey}}-oauth2-client-secret",
|
|
16
|
+
"scopes": []
|
|
17
|
+
}{{/if}}{{#if (eq authType "apikey")}},
|
|
18
|
+
"apikey": {
|
|
19
|
+
"headerName": "X-API-Key",
|
|
20
|
+
"key": "kv://{{systemKey}}-api-key"
|
|
21
|
+
}{{/if}}{{#if (eq authType "basic")}},
|
|
22
|
+
"basic": {
|
|
23
|
+
"username": "kv://{{systemKey}}-username",
|
|
24
|
+
"password": "kv://{{systemKey}}-password"
|
|
25
|
+
}{{/if}}
|
|
26
|
+
}{{#if (eq systemType "openapi")}},
|
|
27
|
+
"openapi": {
|
|
28
|
+
"documentKey": "{{systemKey}}-api",
|
|
29
|
+
"autoDiscoverEntities": false
|
|
30
|
+
}{{/if}}{{#if (eq systemType "mcp")}},
|
|
31
|
+
"mcp": {
|
|
32
|
+
"serverUrl": "https://mcp.example.com",
|
|
33
|
+
"toolPrefix": "{{systemKey}}"
|
|
34
|
+
}{{/if}},
|
|
35
|
+
"tags": []
|
|
36
|
+
}
|
|
37
|
+
|