@kumologica/sdk 3.4.0 → 3.5.0-beta2

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.
Files changed (51) hide show
  1. package/cli/commands/create-commands/openapi.js +42 -0
  2. package/cli/commands/create.js +17 -8
  3. package/cli/commands/login.js +87 -0
  4. package/package.json +18 -7
  5. package/src/app/lib/ai/layout.js +75 -0
  6. package/src/app/lib/ai/openai.js +108 -0
  7. package/src/app/lib/ai/prompt.txt +583 -0
  8. package/src/app/lib/aws/ca-cloudwatch-api.js +2 -10
  9. package/src/app/lib/aws/ca-dynamodb-api.js +6 -10
  10. package/src/app/lib/aws/ca-elb-api.js +4 -24
  11. package/src/app/lib/aws/ca-events-api.js +5 -12
  12. package/src/app/lib/aws/ca-iot-api.js +3 -87
  13. package/src/app/lib/aws/ca-s3-api.js +17 -62
  14. package/src/app/lib/aws/ca-sns-api.js +6 -15
  15. package/src/app/lib/aws/ca-sqs-api.js +9 -6
  16. package/src/app/lib/aws/index.js +70 -86
  17. package/src/app/lib/aws/kl-apigw-api.js +40 -0
  18. package/src/app/lib/aws/kl-iam-api.js +5 -5
  19. package/src/app/lib/github/index.js +0 -17
  20. package/src/app/lib/serverless/index.js +1 -1
  21. package/src/app/lib/stores/settings-cloud-store.js +35 -2
  22. package/src/app/main.js +34 -32
  23. package/src/app/preload.js +36 -28
  24. package/src/app/ui/editor-client/public/red/red.js +924 -458
  25. package/src/app/ui/editor-client/public/red/red.min.js +2 -2
  26. package/src/app/ui/editor-client/public/red/style.min.css +1 -1
  27. package/src/app/ui/editor-client/src/js/nodes.js +19 -18
  28. package/src/app/ui/editor-client/src/js/red.js +6 -3
  29. package/src/app/ui/editor-client/src/js/ui/editor.js +70 -70
  30. package/src/app/ui/editor-client/src/js/ui/footer.js +143 -0
  31. package/src/app/ui/editor-client/src/js/ui/search.js +43 -34
  32. package/src/app/ui/editor-client/src/js/ui/sidebar.js +26 -24
  33. package/src/app/ui/editor-client/src/js/ui/signup.js +56 -0
  34. package/src/app/ui/editor-client/src/js/ui/tab-ai.js +210 -0
  35. package/src/app/ui/editor-client/src/js/ui/tab-awsDeploy.js +30 -5
  36. package/src/app/ui/editor-client/src/js/ui/tab-test.js +120 -99
  37. package/src/app/ui/editor-client/src/js/ui/update-panel.js +0 -1
  38. package/src/app/ui/editor-client/src/js/ui/view.js +201 -202
  39. package/src/app/ui/editor-client/src/sass/editor.scss +715 -645
  40. package/src/app/ui/editor-client/src/sass/sidebar.scss +21 -12
  41. package/src/app/ui/editor-client/src/sass/style.scss +101 -0
  42. package/src/app/ui/editor-client/src/sass/tab-ai.scss +68 -0
  43. package/src/app/ui/editor-client/src/sass/workspace.scss +12 -2
  44. package/src/app/ui/editor-client/templates/index.mst +41 -7
  45. package/src/server/DesignerServer.js +2 -1
  46. package/cli/.DS_Store +0 -0
  47. package/fixtures/.DS_Store +0 -0
  48. package/src/app/lib/aws/ca-apigw-api.js +0 -216
  49. package/src/app/lib/aws/ca-codecommit-api.js +0 -63
  50. package/src/app/lib/aws/kl-rekognition-api.js +0 -66
  51. package/src/app/lib/aws/kl-ssm-api.js +0 -24
package/src/app/main.js CHANGED
@@ -73,7 +73,7 @@ ipcMain.on('modal-welcome:show-dialog-newproject', async () => {
73
73
  winController.setKeepAppRunningIfAllWindowsClosed(true);
74
74
  currentModalWindow.close();
75
75
  const { version } = require('../../package.json');
76
-
76
+
77
77
  let userDir = codegen.generateInitialCode(
78
78
  userPrefStore.getUserDataPath(),
79
79
  '__untitled__',
@@ -151,7 +151,7 @@ ipcMain.on('menu-help-join-discord', () => {
151
151
  shell.openExternal('https://discord.com/invite/HrTCY8zXxM');
152
152
  });
153
153
 
154
- ipcMain.on('kumologica-contact-us', ()=> {
154
+ ipcMain.on('kumologica-contact-us', () => {
155
155
  shell.openExternal('https://kumologica.com/contact.html');
156
156
  })
157
157
 
@@ -172,56 +172,58 @@ ipcMain.on('modal-projects:create-project', async (event, payload) => {
172
172
 
173
173
  app.addRecentDocument(projectBaseDir);
174
174
  const { version } = require('../../package.json');
175
-
175
+
176
176
  currentModalWindow.close();
177
177
  const projectDir = codegen.generateInitialCode(
178
- projectBaseDir,
179
- projectName,
178
+ projectBaseDir,
179
+ projectName,
180
180
  flow,
181
181
  version
182
- );
182
+ );
183
183
  await openProjectEditor(projectDir);
184
184
  });
185
185
 
186
186
  // Events from menu
187
187
 
188
- ipcMain.on('core:show-import-dialog', ()=> {
188
+ ipcMain.on('core:show-import-dialog', () => {
189
189
  mainWindow.sendEventToRenderer("core:show-import-dialog");
190
190
  });
191
191
 
192
- ipcMain.on('core:show-export-dialog', ()=> {
192
+ ipcMain.on('core:show-export-dialog', () => {
193
193
  mainWindow.sendEventToRenderer("core:show-export-dialog");
194
194
  });
195
195
 
196
- ipcMain.on('core:search', ()=> {
196
+ ipcMain.on('core:search', () => {
197
197
  mainWindow.sendEventToRenderer("core:search");
198
198
  });
199
199
 
200
- ipcMain.on('core:search', ()=> {
200
+ ipcMain.on('core:search', () => {
201
201
  mainWindow.sendEventToRenderer("core:search");
202
202
  });
203
203
 
204
- ipcMain.on('core:toggle-terminal', ()=> {
204
+ ipcMain.on('core:toggle-terminal', () => {
205
205
  mainWindow.sendEventToRenderer("core:toggle-terminal");
206
206
  });
207
- ipcMain.on('core:toggle-palette', ()=> {
207
+ ipcMain.on('core:toggle-palette', () => {
208
208
  mainWindow.sendEventToRenderer("core:toggle-palette");
209
209
  });
210
- ipcMain.on('core:view-main-workspace', ()=> {
210
+ ipcMain.on('core:view-main-workspace', () => {
211
211
  mainWindow.sendEventToRenderer("core:view-main-flow");
212
212
  });
213
- ipcMain.on('core:view-test-workspace', ()=> {
213
+ ipcMain.on('core:view-test-workspace', () => {
214
214
  mainWindow.sendEventToRenderer("core:view-test-flow");
215
215
  });
216
- ipcMain.on('core:view-settings', ()=> {
216
+ ipcMain.on('core:view-settings', () => {
217
217
  mainWindow.sendEventToRenderer("core:toggle-settings");
218
218
  });
219
+
219
220
  ipcMain.on('show-newProject-modal', () => {
220
221
  showNewProjectDialog(true);
221
222
  });
222
223
  ipcMain.on('show-openProject-dialog', () => {
223
224
  showOpenProjectDialog();
224
225
  });
226
+
225
227
  ipcMain.on('app_version', (event) => {
226
228
  event.sender.send('app_version', { version: app.getVersion() });
227
229
  });
@@ -229,13 +231,13 @@ ipcMain.on('main-header:show-modal-home', () => {
229
231
  currentModalWindow = new ModalHome(findRecentProjects());
230
232
  currentModalWindow.load();
231
233
  });
232
- ipcMain.on('navigator:open-new-tab-dialog', ()=> {
234
+ ipcMain.on('navigator:open-new-tab-dialog', () => {
233
235
  currentModalWindow = new ModalNewTab(mainWindow.window, true);
234
236
  currentModalWindow.load();
235
237
  });
236
- ipcMain.on('navigator:open-rename-tab-dialog', (event, payload)=> {
238
+ ipcMain.on('navigator:open-rename-tab-dialog', (event, payload) => {
237
239
  const { tabId, oldTabName } = payload;
238
- currentModalWindow = new ModalRenameTab(mainWindow.window, true, {tabId, oldTabName});
240
+ currentModalWindow = new ModalRenameTab(mainWindow.window, true, { tabId, oldTabName });
239
241
  currentModalWindow.load();
240
242
  });
241
243
 
@@ -347,7 +349,7 @@ ipcMain.on('modal-node-library:restart-requested', async (event, msg) => {
347
349
  mainWindow.reload();
348
350
  });
349
351
 
350
- ipcMain.on('open-new-tab-dialog:close', async(event, payload)=> {
352
+ ipcMain.on('open-new-tab-dialog:close', async (event, payload) => {
351
353
  if (currentModalWindow.isCancellable()) {
352
354
  currentModalWindow.close();
353
355
  } else {
@@ -357,7 +359,7 @@ ipcMain.on('open-new-tab-dialog:close', async(event, payload)=> {
357
359
  }
358
360
  });
359
361
 
360
- ipcMain.on('open-rename-tab-dialog:close', async(event, payload)=> {
362
+ ipcMain.on('open-rename-tab-dialog:close', async (event, payload) => {
361
363
  if (currentModalWindow.isCancellable()) {
362
364
  currentModalWindow.close();
363
365
  } else {
@@ -367,15 +369,15 @@ ipcMain.on('open-rename-tab-dialog:close', async(event, payload)=> {
367
369
  }
368
370
  });
369
371
 
370
- ipcMain.on('open-new-tab-dialog:create-tab', async(event, payload)=> {
372
+ ipcMain.on('open-new-tab-dialog:create-tab', async (event, payload) => {
371
373
  currentModalWindow.close();
372
374
  let tabName = payload['createTabName'];
373
375
  mainWindow.sendEventToRenderer('create-new-tab', { tabName });
374
376
  });
375
377
 
376
- ipcMain.on('open-rename-tab-dialog:rename-tab', async(event, payload)=> {
378
+ ipcMain.on('open-rename-tab-dialog:rename-tab', async (event, payload) => {
377
379
  currentModalWindow.close();
378
- const {tabId, newTabName} = payload;
380
+ const { tabId, newTabName } = payload;
379
381
  mainWindow.sendEventToRenderer('explorer:rename-tab', { tabId, newTabName });
380
382
  });
381
383
 
@@ -405,7 +407,7 @@ function showOpenProjectDialog(parent) {
405
407
  function runtimeEventListener(event) {
406
408
  // console.log('runtimeEventListener hit with event:', event);
407
409
  // runtime events @Terminal
408
- if (event && event.id && event.id == 'terminal-output'
410
+ if (event && event.id && event.id == 'terminal-output'
409
411
  && mainWindow && mainWindow.window && mainWindow.window.webContents) {
410
412
  mainWindow.window.webContents.send('terminal-output', event);
411
413
  }
@@ -421,7 +423,7 @@ function runtimeEventListener(event) {
421
423
  }
422
424
 
423
425
  async function openProjectEditor(projectDir, onDidLoad) {
424
- console.log('[main] openProjectDir:', projectDir)
426
+ console.log('> openProjectDir:', projectDir)
425
427
  try {
426
428
  let startupErrorEvent;
427
429
  startupEmitter = new EventEmitter();
@@ -498,12 +500,12 @@ async function openWelcomePage() {
498
500
  // This project will be created in the dataPath of the store:
499
501
  // ~/Library/Application Support/Kumologica
500
502
  const { version } = require('../../package.json');
501
-
503
+
502
504
  let userDir = codegen.generateInitialCode(
503
505
  userPrefStore.getUserDataPath(),
504
506
  '__untitled__',
505
507
  null,
506
- version
508
+ version
507
509
  );
508
510
 
509
511
  // // lets give a kick to the runtime
@@ -563,15 +565,15 @@ app.on('ready', async () => {
563
565
  userPrefStore = new UserPreferenceStore({ defaults: defaults });
564
566
 
565
567
  // Find out the latest project that user used last time
566
- let projectDir = process.argv[2]? process.argv[2]: findLatestProject(); // argv=[electron_path, main.js-path, kumologica-project-path]
567
-
568
- try{
568
+ let projectDir = process.argv[2] ? process.argv[2] : findLatestProject(); // argv=[electron_path, main.js-path, kumologica-project-path]
569
+
570
+ try {
569
571
  if (projectDir) {
570
572
  await openProjectEditor(projectDir);
571
573
  } else {
572
574
  await openWelcomePage();
573
575
  }
574
- }catch(e) {
576
+ } catch (e) {
575
577
  console.error(e.message);
576
578
  process.exit(1);
577
579
  }
@@ -587,7 +589,7 @@ app.on('open-file', (event, path) => {
587
589
 
588
590
  // Called before quitting...gives us an opportunity to shutdown the child process
589
591
  app.on('before-quit', async () => {
590
- console.log('[main] gracefully shutting down...');
592
+ console.log('> Gracefully shutting down...');
591
593
 
592
594
  // Kill the web process
593
595
  await EditorManager.stop();
@@ -37,6 +37,7 @@ const AzureConfig = require('./lib/stores/azure-config-store');
37
37
  const ProjectInfoConfig = require('./lib/stores/project-info-config-store');
38
38
  const { CloudConfigStore } = require('./lib/stores/settings-cloud-store');
39
39
  const { NetworkConfigStore } = require('./lib/stores/settings-network-store');
40
+ const { OpenAIClient } = require('./lib/ai/openai');
40
41
 
41
42
  const AWSProfile = require('./lib/aws/aws-profile');
42
43
  const AWSDeployer = require('./lib/aws');
@@ -70,10 +71,14 @@ const KUMOLOGICA_NODE_LIB_URL = `${KUMOLOGICA_NODE_LIB_BASEURL}/library.json`;
70
71
  const cloudConfigStore = new CloudConfigStore();
71
72
  const networkConfigStore = new NetworkConfigStore();
72
73
 
74
+ const openAIClient = new OpenAIClient({ version: 'gpt-3.5-turbo' }); //'gpt-4'
75
+
73
76
  // function to be called each time
74
77
  // project changes - either new project created or old project opened.
75
78
  // header.js is the component handling project changes.
76
79
  function loadConfigs() {
80
+ window.__kumologica.settings.settingsConfig = cloudConfigStore;
81
+
77
82
  window.__kumologica.settings.cloudConfig = new CloudConfig.CloudConfigStore({
78
83
  dataPath: window.__kumologica.settings.projectDir,
79
84
  });
@@ -89,7 +94,7 @@ function loadConfigs() {
89
94
  window.__kumologica.settings.testConfig = new TestConfig.TestConfigStore({
90
95
  dataPath: window.__kumologica.settings.projectDir,
91
96
  });
92
-
97
+
93
98
  window.__kumologica.settings.projectInfoConfig = new ProjectInfoConfig.ProjectInfoConfigStore({
94
99
  dataPath: window.__kumologica.settings.projectDir,
95
100
  });
@@ -113,7 +118,7 @@ async function kumohubBuild(projectInfo) {
113
118
 
114
119
  }
115
120
  async function kumohubDeploy(projectInfo, params) {
116
-
121
+
117
122
  const alias = cloudConfigStore.getKumohubAliasInfo(params.profile);
118
123
 
119
124
  params.username = alias.username;
@@ -121,7 +126,7 @@ async function kumohubDeploy(projectInfo, params) {
121
126
  params.account = alias.subscription;
122
127
  params["flow-file-name"] = projectInfo.projectFlowName;
123
128
  params["project-directory"] = projectInfo.projectDir;
124
-
129
+
125
130
  return await deployCli.deploy('kumohub', params, terminalEmitter);
126
131
  }
127
132
 
@@ -158,7 +163,7 @@ async function generateScript(provider, projectInfo, params) {
158
163
 
159
164
  async function listServices(type) {
160
165
  const alias = cloudConfigStore.getAWSAliasInfo(window.__kumologica.cloud.profile);
161
-
166
+
162
167
  let lServices = await awsDeployer.listServices(type, alias.profile);
163
168
  return lServices;
164
169
  }
@@ -197,7 +202,7 @@ async function testFlow(testEvent, targetNode, environmentVariables) {
197
202
  } catch (err) {
198
203
  throw err;
199
204
  }
200
-
205
+
201
206
  headers = { ...headers, 'x-kumologica-testcasenode': targetNode };
202
207
 
203
208
  if (environmentVariables) {
@@ -208,7 +213,7 @@ async function testFlow(testEvent, targetNode, environmentVariables) {
208
213
  };
209
214
  }
210
215
  }
211
-
216
+
212
217
  if (environmentVariables && environmentVariables["AWS_PROFILE"]) {
213
218
  headers = {
214
219
  ...headers,
@@ -218,9 +223,9 @@ async function testFlow(testEvent, targetNode, environmentVariables) {
218
223
  const region = await new AWSProfile().getRegion(
219
224
  environmentVariables["AWS_PROFILE"]
220
225
  );
221
-
226
+
222
227
  headers = { ...headers, 'x-kumologica-aws-region': region };
223
- } else {
228
+ } else {
224
229
  terminalEmitter.emit('terminal-output', " ");
225
230
  terminalEmitter.emit('terminal-output', "AWS_PROFILE property not defined on test tab, default aws cli precedence will be used.");
226
231
  terminalEmitter.emit('terminal-output', "For details see: https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html");
@@ -229,11 +234,11 @@ async function testFlow(testEvent, targetNode, environmentVariables) {
229
234
  terminalEmitter.emit('terminal-output', ` AWS_PROFILE: ${process.env.AWS_PROFILE}`);
230
235
  terminalEmitter.emit('terminal-output', ` AWS_REGION: ${process.env.AWS_REGION}`);
231
236
  terminalEmitter.emit('terminal-output', ` AWS_ACCESS_KEY_ID: ${process.env.AWS_ACCESS_KEY_ID}`);
232
- terminalEmitter.emit('terminal-output', ` AWS_SECRET_ACCESS_KEY: ${process.env. AWS_SECRET_ACCESS_KEY}`);
237
+ terminalEmitter.emit('terminal-output', ` AWS_SECRET_ACCESS_KEY: ${process.env.AWS_SECRET_ACCESS_KEY}`);
233
238
  terminalEmitter.emit('terminal-output', " ");
234
239
  }
235
240
  console.log('[preload] Invoking the testFlow...');
236
- try{
241
+ try {
237
242
  let resp = await requestAsync({
238
243
  uri: `http://127.0.0.1:${port}/`,
239
244
  method: 'POST',
@@ -243,17 +248,17 @@ async function testFlow(testEvent, targetNode, environmentVariables) {
243
248
  });
244
249
  console.log('[preload] Response:', resp.body);
245
250
  return resp;
246
- } catch(err){
251
+ } catch (err) {
247
252
  console.log('[preload] Request end with errors...', e);
248
253
  throw e;
249
- }
254
+ }
250
255
  }
251
256
 
252
257
  /**
253
258
  * Debugger API Client endpoints
254
259
  */
255
260
 
256
- async function addBreakpoint(id){
261
+ async function addBreakpoint(id) {
257
262
  const url = `${DESIGNER_DEBUGGER_URL}/add-breakpoint/${id}`
258
263
 
259
264
  let options = {
@@ -277,7 +282,7 @@ async function addBreakpoint(id){
277
282
  }
278
283
  }
279
284
 
280
- async function removeAllBreakpoints(){
285
+ async function removeAllBreakpoints() {
281
286
  const url = `${DESIGNER_DEBUGGER_URL}/remove-all-breakpoints`
282
287
 
283
288
  let options = {
@@ -294,7 +299,7 @@ async function removeAllBreakpoints(){
294
299
 
295
300
  }
296
301
 
297
- async function isBreakpointFound(id){
302
+ async function isBreakpointFound(id) {
298
303
  const url = `${DESIGNER_DEBUGGER_URL}/contains-breakpoint/${id}`
299
304
 
300
305
  let options = {
@@ -311,7 +316,7 @@ async function isBreakpointFound(id){
311
316
 
312
317
  }
313
318
 
314
- async function toggleBreakpoint(id){
319
+ async function toggleBreakpoint(id) {
315
320
  const url = `${DESIGNER_DEBUGGER_URL}/toggle-breakpoint/${id}`
316
321
 
317
322
  let options = {
@@ -328,7 +333,7 @@ async function toggleBreakpoint(id){
328
333
 
329
334
  }
330
335
 
331
- async function enableDebugger(){
336
+ async function enableDebugger() {
332
337
  const url = `${DESIGNER_DEBUGGER_URL}/enable`
333
338
 
334
339
  let options = {
@@ -345,7 +350,7 @@ async function enableDebugger(){
345
350
  }
346
351
 
347
352
 
348
- async function listBreakpoints(){
353
+ async function listBreakpoints() {
349
354
  const url = `${DESIGNER_DEBUGGER_URL}/list-breakpoints`
350
355
 
351
356
  let options = {
@@ -361,7 +366,7 @@ async function listBreakpoints(){
361
366
  }
362
367
  }
363
368
 
364
- async function redrawBreakpoints(){
369
+ async function redrawBreakpoints() {
365
370
  const url = `${DESIGNER_DEBUGGER_URL}/redraw-breakpoints`
366
371
 
367
372
  let options = {
@@ -377,7 +382,7 @@ async function redrawBreakpoints(){
377
382
  }
378
383
  }
379
384
 
380
- async function disableDebugger(){
385
+ async function disableDebugger() {
381
386
  const url = `${DESIGNER_DEBUGGER_URL}/disable`
382
387
 
383
388
  let options = {
@@ -391,7 +396,7 @@ async function disableDebugger(){
391
396
  } catch (err) {
392
397
  throw err;
393
398
  }
394
- }
399
+ }
395
400
 
396
401
  async function continueDebugger() {
397
402
  const url = `${DESIGNER_DEBUGGER_URL}/continue`
@@ -471,7 +476,7 @@ async function fetchAvailableNodes() {
471
476
 
472
477
  // Inject proxy header if required
473
478
  let proxyHeader = RED.uiSettings.getProxyHeader();
474
- if (proxyHeader){
479
+ if (proxyHeader) {
475
480
  options.proxy = proxyHeader
476
481
  }
477
482
 
@@ -490,15 +495,15 @@ async function fetchAvailableNodes() {
490
495
  }
491
496
  }
492
497
  function kumohubProfiles() {
493
- return cloudConfigStore.getKumohubAliases();
498
+ return cloudConfigStore.getKumohubAliases();
494
499
  }
495
500
 
496
501
  function azureProfiles() {
497
- return cloudConfigStore.getAzureAliases();
502
+ return cloudConfigStore.getAzureAliases();
498
503
  }
499
504
 
500
505
  function awsProfiles() {
501
- return cloudConfigStore.getAWSAliases();
506
+ return cloudConfigStore.getAWSAliases();
502
507
  }
503
508
 
504
509
  function kumohubLogout() {
@@ -510,7 +515,7 @@ async function kumohubLogin(profile) {
510
515
  validateAlias(alias);
511
516
 
512
517
  await kumohub.login(alias.username, alias.password, alias.subscription);
513
- return {alias: alias};
518
+ return { alias: alias };
514
519
  }
515
520
 
516
521
  function kumohubIsLoggedIn(profile) {
@@ -545,7 +550,7 @@ async function azureLogin(profile) {
545
550
  //validateAlias(alias);
546
551
 
547
552
  await azure.login(alias);
548
- return {alias: alias};
553
+ return { alias: alias };
549
554
  }
550
555
 
551
556
  /**
@@ -604,7 +609,7 @@ window.__kumologica.runtime.RED = DesignerServer;
604
609
  window.__kumologica.runtime.port = port;
605
610
 
606
611
  // cloud capability
607
- window.__kumologica.cloud = {provider: {}};
612
+ window.__kumologica.cloud = { provider: {} };
608
613
  window.__kumologica.cloud.provider.aws = "AWS";
609
614
  window.__kumologica.cloud.provider.serverless = "SERVERLESS";
610
615
  window.__kumologica.cloud.provider.github = "GITHUB";
@@ -645,6 +650,9 @@ window.__kumologica.libs.openFolder = openFolder;
645
650
  window.__kumologica.libs.dagre = dagre;
646
651
  window.__kumologica.libs.simpleGit = simpleGit;
647
652
 
653
+ // AI utilities
654
+ window.__kumologica.libs.openAIClient = openAIClient;
655
+
648
656
  /**
649
657
  * Return the name of the valid kumologica flow or undefined otherwise.
650
658
  * @param {} projectDir