@cyberismo/cli 0.0.18 → 0.0.20
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/dist/index.js +296 -218
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +590 -322
package/dist/index.js
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
details. You should have received a copy of the GNU Affero General Public
|
|
12
12
|
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
13
13
|
*/
|
|
14
|
-
import {
|
|
14
|
+
import { constants, existsSync } from 'node:fs';
|
|
15
|
+
import { access, lstat, readFile } from 'node:fs/promises';
|
|
15
16
|
import { resolve } from 'node:path';
|
|
16
17
|
import { Argument, Command, Option } from 'commander';
|
|
17
18
|
import confirm from '@inquirer/confirm';
|
|
@@ -81,8 +82,7 @@ async function importableModules(options) {
|
|
|
81
82
|
const importableModules = await commandHandler.command(Cmd.show, ['importableModules'], copyOptions);
|
|
82
83
|
if (!importableModules?.payload ||
|
|
83
84
|
importableModules.payload.length === 0) {
|
|
84
|
-
|
|
85
|
-
process.exit(1);
|
|
85
|
+
program.error('No modules available');
|
|
86
86
|
}
|
|
87
87
|
// return potential importable modules
|
|
88
88
|
const choices = importableModules.payload?.map((module) => ({
|
|
@@ -100,61 +100,6 @@ const program = new Command();
|
|
|
100
100
|
// Ensure that all names have the same guideline.
|
|
101
101
|
const nameGuideline = 'Name can contain letters (a-z|A-Z), spaces, underscores or hyphens.';
|
|
102
102
|
const pathGuideline = 'Path to the project root. Mandatory if not running inside a project tree.';
|
|
103
|
-
const additionalHelpForCreate = `Sub-command help:
|
|
104
|
-
create attachment <cardKey> <filename>, where
|
|
105
|
-
<cardKey> is card key of a card to have the attachment,
|
|
106
|
-
<filename> is attachment filename.
|
|
107
|
-
|
|
108
|
-
create card <template> [cardKey], where
|
|
109
|
-
<template> Template to use. You can list the templates in a project with "show templates" command.
|
|
110
|
-
[cardKey] Parent card's card key. If defined, new card will be created as a child card to that card.
|
|
111
|
-
|
|
112
|
-
create cardType <name> <workflow>, where
|
|
113
|
-
<name> Name for cardType. ${nameGuideline}
|
|
114
|
-
<workflow> Workflow for the card type. You can list workflows in a project with "show workflows" command.
|
|
115
|
-
|
|
116
|
-
create fieldType <name> <dataType>, where
|
|
117
|
-
<name> Name for fieldType. ${nameGuideline}
|
|
118
|
-
<dataType> Type of field. You can list field types in a project with "show fieldTypes" command.
|
|
119
|
-
|
|
120
|
-
create graphModel <name>, where
|
|
121
|
-
<name> Name for graph model. ${nameGuideline}
|
|
122
|
-
|
|
123
|
-
create graphView <name>, where
|
|
124
|
-
<name> Name for graph view. ${nameGuideline}
|
|
125
|
-
|
|
126
|
-
create label <cardKey> <labelName>, where
|
|
127
|
-
<cardKey> Card key of the label
|
|
128
|
-
<labelName> Name for the new label
|
|
129
|
-
|
|
130
|
-
create link <source> <destination> <linkType> [description], where
|
|
131
|
-
<source> Source card key of the link
|
|
132
|
-
<destination> Destination card key of the link
|
|
133
|
-
<linkType> Link type to create
|
|
134
|
-
[description] Link description
|
|
135
|
-
|
|
136
|
-
create linkType <name>, where
|
|
137
|
-
<name> Name for linkType. ${nameGuideline}
|
|
138
|
-
|
|
139
|
-
create project <name> <prefix> <path>, where
|
|
140
|
-
<name> Name of the project.
|
|
141
|
-
<prefix> Prefix for the project.
|
|
142
|
-
<path> Path where to create the project
|
|
143
|
-
|
|
144
|
-
create report <name>, where
|
|
145
|
-
<name> Name for report. ${nameGuideline}
|
|
146
|
-
|
|
147
|
-
create template <name> [content], where
|
|
148
|
-
<name> Name for template. ${nameGuideline}
|
|
149
|
-
[content] If empty, template is created with default values. Template content must conform to schema "templateSchema.json"
|
|
150
|
-
|
|
151
|
-
create workflow <name> [content], where
|
|
152
|
-
<name> Name for workflow. ${nameGuideline}
|
|
153
|
-
[content] If empty, workflow is created with default values. Workflow content must conform to schema "workflowSchema.json"
|
|
154
|
-
|
|
155
|
-
create <resourceName> [content], where
|
|
156
|
-
<resourceName> Name of the resource (e.g. <prefix>/<type>/<identifier>)
|
|
157
|
-
[content] If empty, resource is created with default values. Content must conform to its resource schema.`;
|
|
158
103
|
const additionalHelpForRemove = `Sub-command help:
|
|
159
104
|
remove attachment <cardKey> <filename>, where
|
|
160
105
|
<cardKey> is card key of the owning card,
|
|
@@ -211,6 +156,17 @@ const additionalHelpForUpdate = `Sub-command help:
|
|
|
211
156
|
const contextOption = new Option('-c, --context [context]', 'Context to run the logic programs in.')
|
|
212
157
|
.choices(validContexts)
|
|
213
158
|
.default('app');
|
|
159
|
+
const pathOption = new Option('-p, --project-path <path>', pathGuideline);
|
|
160
|
+
// Custom Command class with pathOption pre-configured
|
|
161
|
+
class CommandWithPath extends Command {
|
|
162
|
+
createCommand(name) {
|
|
163
|
+
return new CommandWithPath(name);
|
|
164
|
+
}
|
|
165
|
+
constructor(name) {
|
|
166
|
+
super(name);
|
|
167
|
+
this.addOption(pathOption);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
214
170
|
// Main CLI program.
|
|
215
171
|
program
|
|
216
172
|
.name('cyberismo')
|
|
@@ -220,7 +176,8 @@ program
|
|
|
220
176
|
.addOption(new Option('-L, --log-level <level>', 'Set the log level')
|
|
221
177
|
.choices(['trace', 'debug', 'info', 'warn', 'error', 'fatal'])
|
|
222
178
|
.default('fatal'));
|
|
223
|
-
const addCmd =
|
|
179
|
+
const addCmd = new CommandWithPath('add').description('Add items to the project');
|
|
180
|
+
program.addCommand(addCmd);
|
|
224
181
|
// Add card to a template
|
|
225
182
|
addCmd
|
|
226
183
|
.command('card')
|
|
@@ -228,7 +185,6 @@ addCmd
|
|
|
228
185
|
.argument('<template>', 'Template for a new card. \nYou can list the templates in a project with "show templates" command.')
|
|
229
186
|
.argument('<cardType>', 'Card type to use for the new card. \nYou can list the card types in a project with "show cardTypes" command.')
|
|
230
187
|
.argument('[cardKey]', "Parent card's card key")
|
|
231
|
-
.option('-p, --project-path [path]', `${pathGuideline}`)
|
|
232
188
|
.option('-r, --repeat <quantity>', 'Add multiple cards to a template')
|
|
233
189
|
.action(async (template, cardType, cardKey, options) => {
|
|
234
190
|
const result = await commandHandler.command(Cmd.add, ['card', template, cardType, cardKey], Object.assign({}, options, program.opts()));
|
|
@@ -238,7 +194,6 @@ addCmd
|
|
|
238
194
|
.command('hub')
|
|
239
195
|
.description('Add a hub to the project')
|
|
240
196
|
.argument('<location>', 'Hub URL. Default hub can be added by using "default"')
|
|
241
|
-
.option('-p, --project-path [path]', `${pathGuideline}`)
|
|
242
197
|
.action(async (location, options) => {
|
|
243
198
|
if (location === 'default') {
|
|
244
199
|
location = DEFAULT_HUB;
|
|
@@ -246,15 +201,13 @@ addCmd
|
|
|
246
201
|
const result = await commandHandler.command(Cmd.add, ['hub', location], Object.assign({}, options, program.opts()));
|
|
247
202
|
handleResponse(result);
|
|
248
203
|
});
|
|
249
|
-
const calculate =
|
|
250
|
-
|
|
251
|
-
.description('Used for running logic programs');
|
|
204
|
+
const calculate = new CommandWithPath('calc').description('Used for running logic programs');
|
|
205
|
+
program.addCommand(calculate);
|
|
252
206
|
calculate
|
|
253
207
|
.command('generate')
|
|
254
208
|
.description('Generate a logic program')
|
|
255
209
|
.argument('<destination>', 'Path to an output file. Command writes the logic program to this file.')
|
|
256
210
|
.argument('[query]', 'Query to run')
|
|
257
|
-
.option('-p, --project-path [path]', `${pathGuideline}`)
|
|
258
211
|
.action(async (destination, query, options) => {
|
|
259
212
|
const result = await commandHandler.command(Cmd.calc, ['generate', destination, query], Object.assign({}, options, program.opts()));
|
|
260
213
|
handleResponse(result);
|
|
@@ -264,88 +217,138 @@ calculate
|
|
|
264
217
|
.description('Run a logic program')
|
|
265
218
|
.argument('<filePath>', 'Path to the logic program')
|
|
266
219
|
.addOption(contextOption)
|
|
267
|
-
.option('-p, --project-path [path]', `${pathGuideline}`)
|
|
268
220
|
.action(async (filePath, options) => {
|
|
269
221
|
const result = await commandHandler.command(Cmd.calc, ['run', filePath], Object.assign({}, options, program.opts()));
|
|
270
222
|
handleResponse(result);
|
|
271
223
|
});
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
.
|
|
277
|
-
.
|
|
278
|
-
.argument('
|
|
279
|
-
.argument('
|
|
280
|
-
.
|
|
281
|
-
.
|
|
224
|
+
const createCmd = new CommandWithPath('create').description('Create cards, resources and other project items');
|
|
225
|
+
program.addCommand(createCmd);
|
|
226
|
+
// Create attachment subcommand
|
|
227
|
+
createCmd
|
|
228
|
+
.command('attachment')
|
|
229
|
+
.description('Create an attachment for a card')
|
|
230
|
+
.argument('<cardKey>', 'Card key of the card to attach to')
|
|
231
|
+
.argument('<filename>', 'Path to the file to attach')
|
|
232
|
+
.action(async (cardKey, filename, options) => {
|
|
233
|
+
const result = await commandHandler.command(Cmd.create, ['attachment', cardKey, filename], Object.assign({}, options, program.opts()));
|
|
234
|
+
handleResponse(result);
|
|
235
|
+
});
|
|
236
|
+
// Create card subcommand
|
|
237
|
+
createCmd
|
|
238
|
+
.command('card')
|
|
239
|
+
.description('Create a card from a template')
|
|
240
|
+
.argument('<template>', 'Template to use. You can list templates with "show templates" command')
|
|
241
|
+
.argument('[parentCardKey]', "Parent card's card key. If defined, new card will be created as a child")
|
|
242
|
+
.action(async (template, parentCardKey, options) => {
|
|
243
|
+
const result = await commandHandler.command(Cmd.create, ['card', template, parentCardKey].filter(Boolean), Object.assign({}, options, program.opts()));
|
|
244
|
+
handleResponse(result);
|
|
245
|
+
});
|
|
246
|
+
// Create cardType subcommand
|
|
247
|
+
createCmd
|
|
248
|
+
.command('cardType')
|
|
249
|
+
.description('Create a new card type')
|
|
250
|
+
.argument('<name>', `Name for card type. ${nameGuideline}`)
|
|
251
|
+
.argument('<workflow>', 'Workflow for the card type. You can list workflows with "show workflows" command')
|
|
252
|
+
.action(async (name, workflow, options) => {
|
|
253
|
+
const result = await commandHandler.command(Cmd.create, ['cardType', name, workflow], Object.assign({}, options, program.opts()));
|
|
254
|
+
handleResponse(result);
|
|
255
|
+
});
|
|
256
|
+
// Create fieldType subcommand
|
|
257
|
+
createCmd
|
|
258
|
+
.command('fieldType')
|
|
259
|
+
.description('Create a new field type')
|
|
260
|
+
.argument('<name>', `Name for field type. ${nameGuideline}`)
|
|
261
|
+
.argument('<dataType>', 'Type of field. You can list field types with "show fieldTypes" command')
|
|
262
|
+
.action(async (name, dataType, options) => {
|
|
263
|
+
const result = await commandHandler.command(Cmd.create, ['fieldType', name, dataType], Object.assign({}, options, program.opts()));
|
|
264
|
+
handleResponse(result);
|
|
265
|
+
});
|
|
266
|
+
// Create graphModel subcommand
|
|
267
|
+
createCmd
|
|
268
|
+
.command('graphModel')
|
|
269
|
+
.description('Create a new graph model')
|
|
270
|
+
.argument('<name>', `Name for graph model. ${nameGuideline}`)
|
|
271
|
+
.action(async (name, options) => {
|
|
272
|
+
const result = await commandHandler.command(Cmd.create, ['graphModel', name], Object.assign({}, options, program.opts()));
|
|
273
|
+
handleResponse(result);
|
|
274
|
+
});
|
|
275
|
+
// Create graphView subcommand
|
|
276
|
+
createCmd
|
|
277
|
+
.command('graphView')
|
|
278
|
+
.description('Create a new graph view')
|
|
279
|
+
.argument('<name>', `Name for graph view. ${nameGuideline}`)
|
|
280
|
+
.action(async (name, options) => {
|
|
281
|
+
const result = await commandHandler.command(Cmd.create, ['graphView', name], Object.assign({}, options, program.opts()));
|
|
282
|
+
handleResponse(result);
|
|
283
|
+
});
|
|
284
|
+
// Create calculation subcommand
|
|
285
|
+
createCmd
|
|
286
|
+
.command('calculation')
|
|
287
|
+
.description('Create a new calculation')
|
|
288
|
+
.argument('<name>', `Name for calculation. ${nameGuideline}`)
|
|
289
|
+
.action(async (name, options) => {
|
|
290
|
+
const result = await commandHandler.command(Cmd.create, ['calculation', name], Object.assign({}, options, program.opts()));
|
|
291
|
+
handleResponse(result);
|
|
292
|
+
});
|
|
293
|
+
// Create label subcommand
|
|
294
|
+
createCmd
|
|
295
|
+
.command('label')
|
|
296
|
+
.description('Create a label on a card')
|
|
297
|
+
.argument('<cardKey>', 'Card key')
|
|
298
|
+
.argument('<labelName>', 'Name for the new label')
|
|
299
|
+
.action(async (cardKey, labelName, options) => {
|
|
300
|
+
const result = await commandHandler.command(Cmd.create, ['label', cardKey, labelName], Object.assign({}, options, program.opts()));
|
|
301
|
+
handleResponse(result);
|
|
302
|
+
});
|
|
303
|
+
// Create link subcommand
|
|
304
|
+
createCmd
|
|
305
|
+
.command('link')
|
|
306
|
+
.description('Create a link between two cards')
|
|
307
|
+
.argument('<source>', 'Source card key')
|
|
308
|
+
.argument('<destination>', 'Destination card key')
|
|
309
|
+
.argument('<linkType>', 'Link type to create')
|
|
310
|
+
.argument('[description]', 'Optional link description')
|
|
311
|
+
.action(async (source, destination, linkType, description, options) => {
|
|
312
|
+
const result = await commandHandler.command(Cmd.create, ['link', source, destination, linkType, description].filter(Boolean), Object.assign({}, options, program.opts()));
|
|
313
|
+
handleResponse(result);
|
|
314
|
+
});
|
|
315
|
+
// Create linkType subcommand
|
|
316
|
+
createCmd
|
|
317
|
+
.command('linkType')
|
|
318
|
+
.description('Create a new link type')
|
|
319
|
+
.argument('<name>', `Name for link type. ${nameGuideline}`)
|
|
320
|
+
.action(async (name, options) => {
|
|
321
|
+
const result = await commandHandler.command(Cmd.create, ['linkType', name], Object.assign({}, options, program.opts()));
|
|
322
|
+
handleResponse(result);
|
|
323
|
+
});
|
|
324
|
+
// Create project subcommand
|
|
325
|
+
createCmd
|
|
326
|
+
.command('project')
|
|
327
|
+
.description('Create a new project')
|
|
328
|
+
.argument('<name>', 'Project name')
|
|
329
|
+
.argument('<prefix>', 'Project prefix')
|
|
330
|
+
.argument('<path>', 'Path where to create the project')
|
|
331
|
+
.argument('[category]', 'Project category (optional)')
|
|
332
|
+
.argument('[description]', 'Project description (optional)')
|
|
282
333
|
.option('-s, --skipModuleImport', 'Skip importing modules when creating a project')
|
|
283
|
-
.action(async (
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
const resourceName = type.split('/').length === 3;
|
|
288
|
-
function nameOfFirstArgument(type) {
|
|
289
|
-
if (type === 'attachment' || type === 'label')
|
|
290
|
-
return 'cardKey';
|
|
291
|
-
if (type === 'card')
|
|
292
|
-
return 'template';
|
|
293
|
-
if (type === 'link')
|
|
294
|
-
return 'source';
|
|
295
|
-
return 'name';
|
|
296
|
-
}
|
|
297
|
-
function nameOfSecondArgument(type) {
|
|
298
|
-
if (type === 'attachment')
|
|
299
|
-
return 'fileName';
|
|
300
|
-
if (type === 'cardType')
|
|
301
|
-
return 'workflow';
|
|
302
|
-
if (type === 'fieldType')
|
|
303
|
-
return 'dataType';
|
|
304
|
-
if (type === 'label')
|
|
305
|
-
return 'labelName';
|
|
306
|
-
if (type === 'link')
|
|
307
|
-
return 'destination';
|
|
308
|
-
if (type === 'project')
|
|
309
|
-
return 'prefix';
|
|
310
|
-
return type;
|
|
311
|
-
}
|
|
312
|
-
if (!target && !resourceName) {
|
|
313
|
-
program.error(`missing required argument <${nameOfFirstArgument(type)}>`);
|
|
314
|
-
}
|
|
315
|
-
if (!resourceName &&
|
|
316
|
-
!parameter1 &&
|
|
317
|
-
type !== 'card' &&
|
|
318
|
-
type !== 'graphModel' &&
|
|
319
|
-
type !== 'graphView' &&
|
|
320
|
-
type !== 'linkType' &&
|
|
321
|
-
type !== 'report' &&
|
|
322
|
-
type !== 'template' &&
|
|
323
|
-
type !== 'workflow') {
|
|
324
|
-
program.error(`missing required argument <${nameOfSecondArgument(type)}>`);
|
|
325
|
-
}
|
|
326
|
-
if (resourceName &&
|
|
327
|
-
(type.includes('cardTypes') || type.includes('fieldTypes')) &&
|
|
328
|
-
!target) {
|
|
329
|
-
program.error(`missing required argument <${nameOfSecondArgument(type)}>`);
|
|
330
|
-
}
|
|
331
|
-
if (type === 'project') {
|
|
332
|
-
if (!parameter2) {
|
|
333
|
-
program.error(`missing required argument <path>`);
|
|
334
|
-
}
|
|
335
|
-
// Project path must be set to 'options' when creating a project.
|
|
336
|
-
options.projectPath = parameter2;
|
|
337
|
-
}
|
|
334
|
+
.action(async (name, prefix, path, category, description, options) => {
|
|
335
|
+
// Project path must be set to 'options' when creating a project
|
|
336
|
+
options.projectPath = path;
|
|
338
337
|
const commandOptions = Object.assign({}, options, program.opts());
|
|
339
|
-
const result = await commandHandler.command(Cmd.create, [
|
|
340
|
-
// Post-handling after creating a new project
|
|
341
|
-
if (
|
|
342
|
-
!commandOptions.skipModuleImport &&
|
|
343
|
-
result.statusCode === 200) {
|
|
338
|
+
const result = await commandHandler.command(Cmd.create, ['project', name, prefix, path, category || '', description || ''], commandOptions);
|
|
339
|
+
// Post-handling after creating a new project
|
|
340
|
+
if (!commandOptions.skipModuleImport && result.statusCode === 200) {
|
|
344
341
|
try {
|
|
345
342
|
// add default hub
|
|
346
|
-
await commandHandler.command(Cmd.add, ['hub', DEFAULT_HUB], commandOptions);
|
|
343
|
+
const addHubResult = await commandHandler.command(Cmd.add, ['hub', DEFAULT_HUB], commandOptions);
|
|
344
|
+
if (addHubResult.statusCode !== 200) {
|
|
345
|
+
program.error(`Project creation failed: could not add default hub - ${addHubResult.message || 'Unknown error'}`);
|
|
346
|
+
}
|
|
347
347
|
// fetch modules from default hub
|
|
348
|
-
await commandHandler.command(Cmd.fetch, ['hubs'], commandOptions);
|
|
348
|
+
const fetchResult = await commandHandler.command(Cmd.fetch, ['hubs'], commandOptions);
|
|
349
|
+
if (fetchResult.statusCode !== 200) {
|
|
350
|
+
program.error(`Project creation failed: could not fetch hub data - ${fetchResult.message || 'Unknown error'}`);
|
|
351
|
+
}
|
|
349
352
|
// show importable modules
|
|
350
353
|
const choices = await importableModules(commandOptions);
|
|
351
354
|
const selectedModules = await checkbox({
|
|
@@ -354,13 +357,21 @@ program
|
|
|
354
357
|
choices,
|
|
355
358
|
});
|
|
356
359
|
// finally, import the selected modules
|
|
360
|
+
const failedModules = [];
|
|
357
361
|
for (const module of selectedModules) {
|
|
358
|
-
await commandHandler.command(Cmd.import, [
|
|
362
|
+
const importResult = await commandHandler.command(Cmd.import, [
|
|
359
363
|
'module',
|
|
360
364
|
module.location,
|
|
361
365
|
module.branch ?? '',
|
|
362
366
|
module.private ? 'true' : 'false',
|
|
363
367
|
], { ...commandOptions, skipMigrationLog: true });
|
|
368
|
+
if (importResult.statusCode !== 200) {
|
|
369
|
+
console.warn(`Failed to import module '${module.name}':`, importResult.message || 'Unknown error');
|
|
370
|
+
failedModules.push(module.name);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (failedModules.length > 0) {
|
|
374
|
+
console.warn(`\nSome modules failed to import: ${failedModules.join(', ')}`);
|
|
364
375
|
}
|
|
365
376
|
}
|
|
366
377
|
catch (error) {
|
|
@@ -372,24 +383,61 @@ program
|
|
|
372
383
|
}
|
|
373
384
|
handleResponse(result);
|
|
374
385
|
});
|
|
386
|
+
// Create report subcommand
|
|
387
|
+
createCmd
|
|
388
|
+
.command('report')
|
|
389
|
+
.description('Create a new report')
|
|
390
|
+
.argument('<name>', `Name for report. ${nameGuideline}`)
|
|
391
|
+
.action(async (name, options) => {
|
|
392
|
+
const result = await commandHandler.command(Cmd.create, ['report', name], Object.assign({}, options, program.opts()));
|
|
393
|
+
handleResponse(result);
|
|
394
|
+
});
|
|
395
|
+
// Create template subcommand
|
|
396
|
+
createCmd
|
|
397
|
+
.command('template')
|
|
398
|
+
.description('Create a new template')
|
|
399
|
+
.argument('<name>', `Name for template. ${nameGuideline}`)
|
|
400
|
+
.argument('[content]', 'Template content. If empty, template is created with default values. Must conform to templateSchema.json')
|
|
401
|
+
.action(async (name, content, options) => {
|
|
402
|
+
const result = await commandHandler.command(Cmd.create, ['template', name, content].filter(Boolean), Object.assign({}, options, program.opts()));
|
|
403
|
+
handleResponse(result);
|
|
404
|
+
});
|
|
405
|
+
// Create workflow subcommand
|
|
406
|
+
createCmd
|
|
407
|
+
.command('workflow')
|
|
408
|
+
.description('Create a new workflow')
|
|
409
|
+
.argument('<name>', `Name for workflow. ${nameGuideline}`)
|
|
410
|
+
.argument('[content]', 'Workflow content. If empty, workflow is created with default values. Must conform to workflowSchema.json')
|
|
411
|
+
.action(async (name, content, options) => {
|
|
412
|
+
const result = await commandHandler.command(Cmd.create, ['workflow', name, content].filter(Boolean), Object.assign({}, options, program.opts()));
|
|
413
|
+
handleResponse(result);
|
|
414
|
+
});
|
|
415
|
+
// Create new resource subcommand
|
|
416
|
+
createCmd
|
|
417
|
+
.command('resource')
|
|
418
|
+
.description('Create a new resource')
|
|
419
|
+
.argument('<resourceName>', 'Resource name (e.g. <prefix>/<type>/<identifier>)')
|
|
420
|
+
.argument('[content]', 'Resource content. If empty, resource is created with default values. Must conform to its resource schema')
|
|
421
|
+
.action(async (resourceName, content, options) => {
|
|
422
|
+
const result = await commandHandler.command(Cmd.create, [resourceName, content].filter(Boolean), Object.assign({}, options, program.opts()));
|
|
423
|
+
handleResponse(result);
|
|
424
|
+
});
|
|
375
425
|
// Edit command
|
|
376
|
-
|
|
377
|
-
.command('edit')
|
|
426
|
+
const editCmd = new CommandWithPath('edit')
|
|
378
427
|
.description('Edit a card')
|
|
379
|
-
.argument('<cardKey>', 'Card key of card')
|
|
380
|
-
|
|
381
|
-
|
|
428
|
+
.argument('<cardKey>', 'Card key of card');
|
|
429
|
+
program.addCommand(editCmd);
|
|
430
|
+
editCmd.action(async (cardKey, options) => {
|
|
382
431
|
const result = await commandHandler.command(Cmd.edit, [cardKey], Object.assign({}, options, program.opts()));
|
|
383
432
|
handleResponse(result);
|
|
384
433
|
});
|
|
385
434
|
// Export command
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
435
|
+
const exportCmd = new CommandWithPath('export').description('Export a project or a card');
|
|
436
|
+
program.addCommand(exportCmd);
|
|
437
|
+
exportCmd
|
|
389
438
|
.addArgument(new Argument('<format>', 'Export format').choices(Object.values(ExportFormats)))
|
|
390
439
|
.argument('<output>', 'Output path')
|
|
391
440
|
.argument('[cardKey]', 'Export a specific card by card key. If omitted, exports the whole site.')
|
|
392
|
-
.option('-p, --project-path [path]', `${pathGuideline}`)
|
|
393
441
|
.option('-r, --recursive', 'Export cards under the specified card recursively')
|
|
394
442
|
.option('-t, --title [title]', 'Title of the exported document(pdf export only)')
|
|
395
443
|
.option('-n, --name [name]', 'Name of the exported document(pdf export only)')
|
|
@@ -405,13 +453,15 @@ program
|
|
|
405
453
|
recursive: options.recursive,
|
|
406
454
|
cardKey: cardKey,
|
|
407
455
|
}, options.logLevel, (current, total) => {
|
|
408
|
-
if (!progress.isActive) {
|
|
456
|
+
if (!progress.isActive && total !== undefined) {
|
|
409
457
|
progress.start(total, 0);
|
|
410
458
|
}
|
|
411
|
-
if (progress.getTotal() !== total) {
|
|
459
|
+
if (total !== undefined && progress.getTotal() !== total) {
|
|
412
460
|
progress.setTotal(total);
|
|
413
461
|
}
|
|
414
|
-
|
|
462
|
+
if (current !== undefined) {
|
|
463
|
+
progress.update(current);
|
|
464
|
+
}
|
|
415
465
|
});
|
|
416
466
|
progress.stop();
|
|
417
467
|
if (errors.length > 0) {
|
|
@@ -436,22 +486,17 @@ program
|
|
|
436
486
|
const result = await commandHandler.command(Cmd.export, [format, output, cardKey], Object.assign({}, options, program.opts()));
|
|
437
487
|
handleResponse(result);
|
|
438
488
|
});
|
|
439
|
-
const fetchCmd =
|
|
440
|
-
|
|
441
|
-
.description('Retrieve external data to local file system.')
|
|
442
|
-
.option('-p, --project-path [path]', `${pathGuideline}`);
|
|
489
|
+
const fetchCmd = new CommandWithPath('fetch').description('Retrieve external data to local file system.');
|
|
490
|
+
program.addCommand(fetchCmd);
|
|
443
491
|
fetchCmd
|
|
444
492
|
.command('hubs')
|
|
445
493
|
.description('Retrieves module lists from hubs')
|
|
446
|
-
.option('-p, --project-path [path]', `${pathGuideline}`)
|
|
447
494
|
.action(async (options) => {
|
|
448
495
|
const result = await commandHandler.command(Cmd.fetch, ['hubs'], Object.assign({}, options, program.opts()));
|
|
449
496
|
handleResponse(result);
|
|
450
497
|
});
|
|
451
|
-
const importCmd =
|
|
452
|
-
|
|
453
|
-
.description('Import modules and data into the project')
|
|
454
|
-
.option('-p, --project-path [path]', `${pathGuideline}`);
|
|
498
|
+
const importCmd = new CommandWithPath('import').description('Import modules and data into the project');
|
|
499
|
+
program.addCommand(importCmd);
|
|
455
500
|
// Import module
|
|
456
501
|
importCmd
|
|
457
502
|
.command('module')
|
|
@@ -459,7 +504,6 @@ importCmd
|
|
|
459
504
|
.argument('[source]', 'Path to import from or module name. If omitted, shows interactive selection')
|
|
460
505
|
.argument('[branch]', 'When using git URL defines the branch. Default: main')
|
|
461
506
|
.argument('[useCredentials]', 'When using git URL uses credentials for cloning. Default: false')
|
|
462
|
-
.option('-p, --project-path [path]', `${pathGuideline}`)
|
|
463
507
|
.action(async (source, branch, useCredentials, options) => {
|
|
464
508
|
let resolvedSource = source;
|
|
465
509
|
let resolvedBranch = branch;
|
|
@@ -525,20 +569,68 @@ importCmd
|
|
|
525
569
|
.command('csv')
|
|
526
570
|
.description('Imports cards from a csv file')
|
|
527
571
|
.argument('<csvFile>', 'File to import from')
|
|
528
|
-
.argument('[cardKey]', '
|
|
529
|
-
.option('-p, --project-path [path]', `${pathGuideline}`)
|
|
572
|
+
.argument('[cardKey]', 'Parent card key. If defined, cards are created as children of this card')
|
|
530
573
|
.action(async (csvFile, cardKey, options) => {
|
|
531
574
|
const result = await commandHandler.command(Cmd.import, ['csv', csvFile, cardKey], Object.assign({}, options, program.opts()));
|
|
532
575
|
handleResponse(result);
|
|
533
576
|
});
|
|
534
|
-
//
|
|
577
|
+
// Migrate command
|
|
535
578
|
program
|
|
536
|
-
.command('
|
|
579
|
+
.command('migrate')
|
|
580
|
+
.description('Migrate project schema to a newer version.')
|
|
581
|
+
.argument('[version]', 'Target schema version. If not provided, migrates to the latest version. Can only migrate one version at a time when specified.')
|
|
582
|
+
.option('-p, --project-path [path]', `${pathGuideline}`)
|
|
583
|
+
.option('-b, --backup <directory>', 'Create a backup before migration in the specified directory. Directory must exist.')
|
|
584
|
+
.option('-t, --timeout <minutes>', 'Timeout for migration in minutes (default: 2 minutes)', '2')
|
|
585
|
+
.action(async (version, options) => {
|
|
586
|
+
if (version) {
|
|
587
|
+
const versionNumber = parseInt(version);
|
|
588
|
+
if (isNaN(versionNumber)) {
|
|
589
|
+
console.error(`Error: migration version is not a number: '${version}'`);
|
|
590
|
+
process.exit(1);
|
|
591
|
+
}
|
|
592
|
+
if (versionNumber <= 0) {
|
|
593
|
+
console.error(`Error: migration version must be above zero: '${version}'`);
|
|
594
|
+
process.exit(1);
|
|
595
|
+
}
|
|
596
|
+
if (options.backup) {
|
|
597
|
+
options.backup = resolve(options.backup.toString().trim());
|
|
598
|
+
if (!existsSync(options.backup)) {
|
|
599
|
+
console.error(`Error: Backup directory does not exist: ${options.backup}`);
|
|
600
|
+
process.exit(1);
|
|
601
|
+
}
|
|
602
|
+
try {
|
|
603
|
+
await access(options.backup, constants.W_OK);
|
|
604
|
+
}
|
|
605
|
+
catch {
|
|
606
|
+
console.error(`Error: Cannot write to backup directory: ${options.backup}`);
|
|
607
|
+
process.exit(1);
|
|
608
|
+
}
|
|
609
|
+
if (!(await lstat(options.backup)).isDirectory()) {
|
|
610
|
+
console.error(`Error: Backup directory is a file: ${options.backup}`);
|
|
611
|
+
process.exit(1);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
if (options.timeout !== undefined) {
|
|
616
|
+
const timeoutMinutes = Number(options.timeout);
|
|
617
|
+
if (isNaN(timeoutMinutes) || timeoutMinutes <= 0) {
|
|
618
|
+
console.error(`Error: Timeout must be a positive number`);
|
|
619
|
+
process.exit(1);
|
|
620
|
+
}
|
|
621
|
+
// Convert minutes to milliseconds
|
|
622
|
+
options.timeout = timeoutMinutes * 60 * 1000;
|
|
623
|
+
}
|
|
624
|
+
const result = await commandHandler.command(Cmd.migrate, [version], Object.assign({}, options, program.opts()));
|
|
625
|
+
handleResponse(result);
|
|
626
|
+
});
|
|
627
|
+
// Move command
|
|
628
|
+
const moveCmd = new CommandWithPath('move')
|
|
537
629
|
.description('Moves a card from root to under another card, from under another card to root, or from under a one card to another.')
|
|
538
630
|
.argument('[source]', 'Source Card key that needs to be moved')
|
|
539
|
-
.argument('[destination]', 'Destination Card key where "source" is moved to. If moving to root, use "root"')
|
|
540
|
-
|
|
541
|
-
|
|
631
|
+
.argument('[destination]', 'Destination Card key where "source" is moved to. If moving to root, use "root"');
|
|
632
|
+
program.addCommand(moveCmd);
|
|
633
|
+
moveCmd.action(async (source, destination, options) => {
|
|
542
634
|
const result = await commandHandler.command(Cmd.move, [source, destination], Object.assign({}, options, program.opts()));
|
|
543
635
|
handleResponse(result);
|
|
544
636
|
});
|
|
@@ -549,16 +641,13 @@ program
|
|
|
549
641
|
.action(async (dir) => {
|
|
550
642
|
await previewSite(dir || '.', true);
|
|
551
643
|
});
|
|
552
|
-
const rank =
|
|
553
|
-
|
|
554
|
-
.description('Manage card ranking and ordering')
|
|
555
|
-
.option('-p, --project-path [path]', `${pathGuideline}`);
|
|
644
|
+
const rank = new CommandWithPath('rank').description('Manage card ranking and ordering');
|
|
645
|
+
program.addCommand(rank);
|
|
556
646
|
rank
|
|
557
647
|
.command('card')
|
|
558
648
|
.description('Set the rank of a card. Ranks define the order in which cards are shown.')
|
|
559
649
|
.argument('<cardKey>', 'Card key of the card to be moved')
|
|
560
650
|
.argument('<afterCardKey>', 'Card key of the card that the card should be after. Use "first" to rank the card first.')
|
|
561
|
-
.option('-p, --project-path [path]', `${pathGuideline}`)
|
|
562
651
|
.action(async (cardKey, afterCardKey, options) => {
|
|
563
652
|
const result = await commandHandler.command(Cmd.rank, ['card', cardKey, afterCardKey], Object.assign({}, options, program.opts()));
|
|
564
653
|
handleResponse(result);
|
|
@@ -566,22 +655,20 @@ rank
|
|
|
566
655
|
rank
|
|
567
656
|
.command('rebalance')
|
|
568
657
|
.description('Rebalance the rank of all cards in the project. Can be also used, if ranks do not exist')
|
|
569
|
-
.argument('[parentCardKey]', 'if null, rebalance the whole project, otherwise rebalance only the direct children
|
|
570
|
-
.option('-p, --project-path [path]', `${pathGuideline}`)
|
|
658
|
+
.argument('[parentCardKey]', 'if null, rebalance the whole project, otherwise rebalance only the direct children')
|
|
571
659
|
.action(async (cardKey, options) => {
|
|
572
660
|
const result = await commandHandler.command(Cmd.rank, ['rebalance', cardKey], Object.assign({}, options, program.opts()));
|
|
573
661
|
handleResponse(result);
|
|
574
662
|
});
|
|
575
663
|
// Remove command
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
664
|
+
const removeCmd = new CommandWithPath('remove').description('Remove cards, resources and other project items');
|
|
665
|
+
program.addCommand(removeCmd);
|
|
666
|
+
removeCmd
|
|
579
667
|
.argument('<type>', `removable types: '${Parser.listTargets('remove').join("', '")}', or resource name (e.g. <prefix>/<type>/<identifier>)`, Parser.parseRemoveTypes)
|
|
580
668
|
.argument('[parameter1]', 'Depends on context; see below for specific remove operation')
|
|
581
669
|
.argument('[parameter2]', 'Depends on context; see below for specific remove operation')
|
|
582
670
|
.argument('[parameter3]', 'Depends on context; see below for specific remove operation')
|
|
583
671
|
.addHelpText('after', additionalHelpForRemove)
|
|
584
|
-
.option('-p, --project-path [path]', `${pathGuideline}`)
|
|
585
672
|
.action(async (type, parameter1, parameter2, parameter3, options) => {
|
|
586
673
|
if (type) {
|
|
587
674
|
if (!parameter1) {
|
|
@@ -620,36 +707,33 @@ program
|
|
|
620
707
|
}
|
|
621
708
|
});
|
|
622
709
|
// Rename command
|
|
623
|
-
|
|
624
|
-
.command('rename')
|
|
710
|
+
const renameCmd = new CommandWithPath('rename')
|
|
625
711
|
.description('Change project prefix and rename all the content with the new prefix')
|
|
626
|
-
.argument('<to>', 'New project prefix')
|
|
627
|
-
|
|
628
|
-
|
|
712
|
+
.argument('<to>', 'New project prefix');
|
|
713
|
+
program.addCommand(renameCmd);
|
|
714
|
+
renameCmd.action(async (to, options) => {
|
|
629
715
|
const result = await commandHandler.command(Cmd.rename, [to], Object.assign({}, options, program.opts()));
|
|
630
716
|
handleResponse(result);
|
|
631
717
|
});
|
|
632
718
|
// Report command
|
|
633
|
-
|
|
634
|
-
.command('report')
|
|
719
|
+
const reportCmd = new CommandWithPath('report')
|
|
635
720
|
.description('Runs a report')
|
|
636
721
|
.argument('<parameters>', 'Path to parameters file. This file defines which report to run and what parameters to use.')
|
|
637
722
|
.argument('[output]', 'Optional output file; if omitted output will be directed to stdout')
|
|
638
|
-
.addOption(contextOption)
|
|
639
|
-
|
|
640
|
-
|
|
723
|
+
.addOption(contextOption);
|
|
724
|
+
program.addCommand(reportCmd);
|
|
725
|
+
reportCmd.action(async (parameters, output, options) => {
|
|
641
726
|
const result = await commandHandler.command(Cmd.report, [parameters, output], Object.assign({}, options, program.opts()));
|
|
642
727
|
handleResponse(result);
|
|
643
728
|
});
|
|
644
729
|
// Show command
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
730
|
+
const showCmd = new CommandWithPath('show').description('Shows details from a project');
|
|
731
|
+
program.addCommand(showCmd);
|
|
732
|
+
showCmd
|
|
648
733
|
.argument('<type>', `details can be seen from: ${Parser.listTargets('show').join(', ')}`, Parser.parseShowTypes)
|
|
649
734
|
.argument('[typeDetail]', 'additional information about the requested type; for example a card key')
|
|
650
735
|
.option('-d --details', 'Certain types (such as cards) can have additional details')
|
|
651
736
|
.option('-a --showAll', 'Show all modules, irregardless if it has been imported or not. Only with "show importableModules"')
|
|
652
|
-
.option('-p, --project-path [path]', `${pathGuideline}`)
|
|
653
737
|
.option('-u --show-use', 'Show where resource is used. Only used with resources, otherwise will be ignored.')
|
|
654
738
|
.action(async (type, typeDetail, options) => {
|
|
655
739
|
if (type !== '') {
|
|
@@ -666,48 +750,43 @@ program
|
|
|
666
750
|
}
|
|
667
751
|
});
|
|
668
752
|
// Transition command
|
|
669
|
-
|
|
670
|
-
.command('transition')
|
|
753
|
+
const transitionCmd = new CommandWithPath('transition')
|
|
671
754
|
.description('Transition a card to the specified state')
|
|
672
755
|
.argument('<cardKey>', 'card key of a card')
|
|
673
|
-
.argument('<transition>', 'Workflow state transition that is done.\nYou can list the workflows in a project with "show workflows" command.\nYou can see the available transitions with "show workflow <name>" command.')
|
|
674
|
-
|
|
675
|
-
|
|
756
|
+
.argument('<transition>', 'Workflow state transition that is done.\nYou can list the workflows in a project with "show workflows" command.\nYou can see the available transitions with "show workflow <name>" command.');
|
|
757
|
+
program.addCommand(transitionCmd);
|
|
758
|
+
transitionCmd.action(async (cardKey, transition, options) => {
|
|
676
759
|
const result = await commandHandler.command(Cmd.transition, [cardKey, transition], Object.assign({}, options, program.opts()));
|
|
677
760
|
handleResponse(result);
|
|
678
761
|
});
|
|
679
762
|
// Update command
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
763
|
+
const updateCmd = new CommandWithPath('update').description('Update resource details');
|
|
764
|
+
program.addCommand(updateCmd);
|
|
765
|
+
updateCmd
|
|
683
766
|
.argument('<resourceName>', 'Resource name')
|
|
684
767
|
.argument('<operation>', 'Type of change, either "add", "change", "rank" or "remove" ')
|
|
685
768
|
.argument('<key>', 'Detail to be changed')
|
|
686
769
|
.argument('<value>', 'Value for a detail')
|
|
687
770
|
.argument('[newValue]', 'When using "change" define new value for detail.\nWhen using "remove" provide optional replacement value for removed value')
|
|
688
|
-
.option('-m, --mapping-file
|
|
689
|
-
.option('-p, --project-path [path]', `${pathGuideline}`)
|
|
771
|
+
.option('-m, --mapping-file <path>', 'Path to JSON file containing workflow state mapping (only used when changing workflow)')
|
|
690
772
|
.addHelpText('after', additionalHelpForUpdate)
|
|
691
773
|
.action(async (resourceName, operation, key, value, newValue, options) => {
|
|
692
774
|
const result = await commandHandler.command(Cmd.update, [resourceName, operation, key, value, newValue], Object.assign({}, options, program.opts()));
|
|
693
775
|
handleResponse(result);
|
|
694
776
|
});
|
|
695
777
|
// Updates all modules, or specific named module in the project.
|
|
696
|
-
|
|
697
|
-
.command('update-modules')
|
|
778
|
+
const updateModulesCmd = new CommandWithPath('update-modules')
|
|
698
779
|
.description('Updates to latest versions either all modules or a specific module')
|
|
699
|
-
.argument('[moduleName]', 'Module name')
|
|
700
|
-
|
|
701
|
-
|
|
780
|
+
.argument('[moduleName]', 'Module name');
|
|
781
|
+
program.addCommand(updateModulesCmd);
|
|
782
|
+
updateModulesCmd.action(async (moduleName, options) => {
|
|
702
783
|
const result = await commandHandler.command(Cmd.updateModules, [moduleName], Object.assign({}, options, program.opts()), credentials());
|
|
703
784
|
handleResponse(result);
|
|
704
785
|
});
|
|
705
786
|
// Validate command
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
.option('-p, --project-path [path]', `${pathGuideline}`)
|
|
710
|
-
.action(async (options) => {
|
|
787
|
+
const validateCmd = new CommandWithPath('validate').description('Validate project structure');
|
|
788
|
+
program.addCommand(validateCmd);
|
|
789
|
+
validateCmd.action(async (options) => {
|
|
711
790
|
const result = await commandHandler.command(Cmd.validate, [], Object.assign({}, options, program.opts()));
|
|
712
791
|
handleResponse(result);
|
|
713
792
|
});
|
|
@@ -716,12 +795,11 @@ program
|
|
|
716
795
|
// There is 10 sec timeout on the prompt. If user does not reply, then
|
|
717
796
|
// it is assumed that validation errors do not matter and application
|
|
718
797
|
// start is resumed.
|
|
719
|
-
|
|
720
|
-
.command('app')
|
|
798
|
+
const appCmd = new CommandWithPath('app')
|
|
721
799
|
.description('Starts the cyberismo app, accessible with a web browser at http://localhost:3000')
|
|
722
|
-
.option('-w, --watch-resource-changes', 'Project watches changes in .cards folder resources')
|
|
723
|
-
|
|
724
|
-
|
|
800
|
+
.option('-w, --watch-resource-changes', 'Project watches changes in .cards folder resources');
|
|
801
|
+
program.addCommand(appCmd);
|
|
802
|
+
appCmd.action(async (options) => {
|
|
725
803
|
// validate project
|
|
726
804
|
const result = await commandHandler.command(Cmd.validate, [], Object.assign({}, options, program.opts()));
|
|
727
805
|
if (!result.message) {
|