@aifabrix/builder 2.41.0 → 2.42.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/docs-rules.mdc +30 -0
- package/README.md +1 -1
- package/integration/hubspot/README.md +8 -4
- package/integration/hubspot/application.json +54 -0
- package/integration/hubspot/create-hubspot.js +9 -136
- package/integration/hubspot/env.template +3 -4
- package/integration/hubspot/hubspot-datasource-company.json +343 -5
- package/integration/hubspot/hubspot-datasource-contact.json +413 -5
- package/integration/hubspot/hubspot-datasource-deal.json +341 -4
- package/integration/hubspot/hubspot-datasource-users.json +116 -0
- package/integration/hubspot/hubspot-deploy.json +1250 -108
- package/integration/hubspot/hubspot-system.json +15 -32
- package/integration/hubspot/test-dataplane-down-tests.js +17 -16
- package/integration/hubspot/test-dataplane-down.js +2 -2
- package/jest.config.manual.js +2 -1
- package/lib/api/external-test.api.js +111 -0
- package/lib/api/index.js +42 -19
- package/lib/api/pipeline.api.js +66 -120
- package/lib/api/types/pipeline.types.js +37 -0
- package/lib/api/wizard-platform.api.js +61 -0
- package/lib/api/wizard.api.js +34 -1
- package/lib/app/config.js +23 -11
- package/lib/app/index.js +3 -1
- package/lib/app/prompts.js +44 -29
- package/lib/app/readme.js +8 -3
- package/lib/app/run-env-compose.js +64 -1
- package/lib/app/run-helpers.js +1 -1
- package/lib/app/show-display.js +1 -1
- package/lib/cli/setup-app.js +42 -11
- package/lib/cli/setup-credential-deployment.js +31 -6
- package/lib/cli/setup-dev.js +27 -0
- package/lib/cli/setup-environment.js +12 -4
- package/lib/cli/setup-external-system.js +19 -4
- package/lib/cli/setup-infra.js +54 -14
- package/lib/cli/setup-utility.js +117 -21
- package/lib/commands/credential-env.js +162 -0
- package/lib/commands/credential-list.js +17 -22
- package/lib/commands/credential-push.js +96 -0
- package/lib/commands/datasource.js +77 -6
- package/lib/commands/dev-init.js +39 -1
- package/lib/commands/repair-auth-config.js +99 -0
- package/lib/commands/repair-datasource-keys.js +208 -0
- package/lib/commands/repair-datasource.js +235 -0
- package/lib/commands/repair-env-template.js +348 -0
- package/lib/commands/repair-internal.js +85 -0
- package/lib/commands/repair-rbac.js +158 -0
- package/lib/commands/repair.js +507 -0
- package/lib/commands/test-e2e-external.js +165 -0
- package/lib/commands/upload.js +71 -40
- package/lib/commands/wizard-core-helpers.js +226 -4
- package/lib/commands/wizard-core.js +67 -29
- package/lib/commands/wizard-dataplane.js +1 -1
- package/lib/commands/wizard-entity-selection.js +43 -0
- package/lib/commands/wizard-headless.js +44 -5
- package/lib/commands/wizard-helpers.js +7 -3
- package/lib/commands/wizard.js +86 -64
- package/lib/core/config.js +7 -1
- package/lib/core/secrets.js +33 -12
- package/lib/datasource/deploy.js +12 -3
- package/lib/datasource/test-e2e.js +219 -0
- package/lib/datasource/test-integration.js +154 -0
- package/lib/deployment/deployer.js +7 -5
- package/lib/external-system/download.js +182 -204
- package/lib/external-system/generator.js +204 -56
- package/lib/external-system/test-execution.js +2 -1
- package/lib/external-system/test-system-level.js +73 -0
- package/lib/external-system/test.js +51 -18
- package/lib/generator/external-controller-manifest.js +29 -2
- package/lib/generator/external-schema-utils.js +1 -1
- package/lib/generator/external.js +10 -3
- package/lib/generator/index.js +4 -1
- package/lib/generator/split-readme.js +1 -0
- package/lib/generator/split-variables.js +7 -1
- package/lib/generator/split.js +194 -54
- package/lib/generator/wizard-prompts-secondary.js +294 -0
- package/lib/generator/wizard-prompts.js +105 -106
- package/lib/generator/wizard-readme.js +88 -0
- package/lib/generator/wizard.js +147 -158
- package/lib/infrastructure/compose.js +11 -1
- package/lib/infrastructure/index.js +11 -3
- package/lib/infrastructure/services.js +22 -11
- package/lib/schema/application-schema.json +8 -5
- package/lib/schema/external-datasource.schema.json +49 -26
- package/lib/schema/external-system.schema.json +82 -6
- package/lib/schema/wizard-config.schema.json +16 -0
- package/lib/utils/api.js +38 -10
- package/lib/utils/auth-headers.js +8 -7
- package/lib/utils/compose-generator.js +1 -1
- package/lib/utils/compose-handlebars-helpers.js +11 -0
- package/lib/utils/config-format-preference.js +51 -0
- package/lib/utils/config-format.js +36 -0
- package/lib/utils/configuration-env-resolver.js +179 -0
- package/lib/utils/credential-display.js +83 -0
- package/lib/utils/credential-secrets-env.js +115 -25
- package/lib/utils/dataplane-pipeline-warning.js +28 -0
- package/lib/utils/deployment-validation-helpers.js +4 -4
- package/lib/utils/dev-ca-install.js +139 -0
- package/lib/utils/env-copy.js +23 -3
- package/lib/utils/error-formatters/http-status-errors.js +0 -1
- package/lib/utils/error-formatters/permission-errors.js +0 -1
- package/lib/utils/error-formatters/validation-errors.js +0 -1
- package/lib/utils/external-readme.js +56 -29
- package/lib/utils/external-system-display.js +59 -1
- package/lib/utils/external-system-test-helpers.js +21 -8
- package/lib/utils/external-system-validators.js +3 -0
- package/lib/utils/file-upload.js +20 -50
- package/lib/utils/help-builder.js +1 -0
- package/lib/utils/infra-status.js +50 -44
- package/lib/utils/local-secrets.js +5 -5
- package/lib/utils/paths.js +85 -4
- package/lib/utils/secrets-canonical.js +93 -0
- package/lib/utils/secrets-generator.js +20 -0
- package/lib/utils/secrets-helpers.js +75 -89
- package/lib/utils/test-log-writer.js +56 -0
- package/lib/utils/token-manager.js +24 -32
- package/lib/validation/env-template-auth.js +157 -0
- package/lib/validation/env-template-kv.js +41 -0
- package/lib/validation/external-manifest-validator.js +25 -0
- package/lib/validation/external-system-auth-rules.js +86 -0
- package/lib/validation/validate-batch.js +149 -0
- package/lib/validation/validate-datasource-keys-api.js +33 -0
- package/lib/validation/validate-display.js +94 -16
- package/lib/validation/validate.js +25 -12
- package/lib/validation/validator.js +7 -9
- package/lib/validation/wizard-datasource-validation.js +50 -0
- package/package.json +7 -2
- package/templates/applications/dataplane/application.yaml +1 -1
- package/templates/applications/dataplane/env.template +5 -5
- package/templates/applications/dataplane/rbac.yaml +2 -2
- package/templates/applications/miso-controller/env.template +1 -1
- package/templates/external-system/README.md.hbs +65 -25
- package/templates/external-system/deploy.js.hbs +4 -2
- package/templates/external-system/external-datasource.yaml.hbs +217 -0
- package/templates/external-system/external-system.json.hbs +1 -18
- package/templates/infra/compose.yaml.hbs +6 -0
- package/templates/python/docker-compose.hbs +4 -4
- package/templates/typescript/docker-compose.hbs +4 -4
- package/integration/hubspot/application.yaml +0 -37
|
@@ -7,18 +7,20 @@
|
|
|
7
7
|
const inquirer = require('inquirer');
|
|
8
8
|
const path = require('path');
|
|
9
9
|
const fs = require('fs').promises;
|
|
10
|
+
const { formatCredentialWithStatus } = require('../utils/credential-display');
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Prompt for wizard mode selection
|
|
13
14
|
* @async
|
|
14
15
|
* @function promptForMode
|
|
15
16
|
* @param {string} [defaultMode] - Default value ('create-system' | 'add-datasource')
|
|
17
|
+
* @param {boolean} [allowAddDatasource=true] - If false, only show "Create a new external system"
|
|
16
18
|
* @returns {Promise<string>} Selected mode ('create-system' | 'add-datasource')
|
|
17
19
|
*/
|
|
18
|
-
async function promptForMode(defaultMode) {
|
|
20
|
+
async function promptForMode(defaultMode, allowAddDatasource = true) {
|
|
19
21
|
const choices = [
|
|
20
22
|
{ name: 'Create a new external system', value: 'create-system' },
|
|
21
|
-
{ name: 'Add datasource to existing system', value: 'add-datasource' }
|
|
23
|
+
...(allowAddDatasource ? [{ name: 'Add datasource to existing system', value: 'add-datasource' }] : [])
|
|
22
24
|
];
|
|
23
25
|
const { mode } = await inquirer.prompt([
|
|
24
26
|
{
|
|
@@ -26,6 +28,7 @@ async function promptForMode(defaultMode) {
|
|
|
26
28
|
name: 'mode',
|
|
27
29
|
message: 'What would you like to do?',
|
|
28
30
|
choices,
|
|
31
|
+
pageSize: 10,
|
|
29
32
|
default: defaultMode && choices.some(c => c.value === defaultMode) ? defaultMode : undefined
|
|
30
33
|
}
|
|
31
34
|
]);
|
|
@@ -35,12 +38,58 @@ async function promptForMode(defaultMode) {
|
|
|
35
38
|
/**
|
|
36
39
|
* Prompt for existing system ID or key (for add-datasource mode).
|
|
37
40
|
* Only external systems (OpenAPI, MCP, custom) support add-datasource; webapps do not.
|
|
41
|
+
* When a list is available, use promptForExistingSystem instead to show a selection list.
|
|
38
42
|
* @async
|
|
39
43
|
* @function promptForSystemIdOrKey
|
|
40
44
|
* @param {string} [defaultValue] - Default value (e.g. from loaded wizard.yaml)
|
|
41
45
|
* @returns {Promise<string>} System ID or key
|
|
42
46
|
*/
|
|
43
47
|
async function promptForSystemIdOrKey(defaultValue) {
|
|
48
|
+
return promptForExistingSystemInput(defaultValue);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Prompt to select an existing external system: show a list from the dataplane when available, otherwise ask for ID/key.
|
|
53
|
+
* @async
|
|
54
|
+
* @function promptForExistingSystem
|
|
55
|
+
* @param {Array<{key?: string, id?: string, displayName?: string}>} [systemsList] - External systems from GET /api/v1/external/systems (or empty/null on error)
|
|
56
|
+
* @param {string} [defaultValue] - Default value (e.g. from loaded wizard.yaml)
|
|
57
|
+
* @returns {Promise<string>} Selected system ID or key
|
|
58
|
+
*/
|
|
59
|
+
async function promptForExistingSystem(systemsList = [], defaultValue) {
|
|
60
|
+
const list = Array.isArray(systemsList) ? systemsList : [];
|
|
61
|
+
if (list.length > 0) {
|
|
62
|
+
const choices = list.map((s) => {
|
|
63
|
+
const value = s.key ?? s.id ?? '';
|
|
64
|
+
const displayName = s.displayName ?? s.name ?? value;
|
|
65
|
+
const name = displayName === value ? String(value) : `${displayName} (${value})`;
|
|
66
|
+
return { name: String(name), value };
|
|
67
|
+
}).filter((c) => c.value);
|
|
68
|
+
if (choices.length === 0) {
|
|
69
|
+
return promptForExistingSystemInput(defaultValue);
|
|
70
|
+
}
|
|
71
|
+
const { systemIdOrKey } = await inquirer.prompt([
|
|
72
|
+
{
|
|
73
|
+
type: 'list',
|
|
74
|
+
name: 'systemIdOrKey',
|
|
75
|
+
message: 'Select an existing external system (not a webapp):',
|
|
76
|
+
choices,
|
|
77
|
+
pageSize: 10
|
|
78
|
+
}
|
|
79
|
+
]);
|
|
80
|
+
return systemIdOrKey;
|
|
81
|
+
}
|
|
82
|
+
return promptForExistingSystemInput(defaultValue);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Prompt for external system ID or key via free-text input (used when list is empty or API failed).
|
|
87
|
+
* @async
|
|
88
|
+
* @function promptForExistingSystemInput
|
|
89
|
+
* @param {string} [defaultValue] - Default value
|
|
90
|
+
* @returns {Promise<string>} System ID or key
|
|
91
|
+
*/
|
|
92
|
+
async function promptForExistingSystemInput(defaultValue) {
|
|
44
93
|
const { systemIdOrKey } = await inquirer.prompt([
|
|
45
94
|
{
|
|
46
95
|
type: 'input',
|
|
@@ -78,7 +127,8 @@ async function promptForSourceType(platforms = []) {
|
|
|
78
127
|
type: 'list',
|
|
79
128
|
name: 'sourceType',
|
|
80
129
|
message: 'What is your source type?',
|
|
81
|
-
choices
|
|
130
|
+
choices,
|
|
131
|
+
pageSize: 10
|
|
82
132
|
}
|
|
83
133
|
]);
|
|
84
134
|
return sourceType;
|
|
@@ -189,9 +239,10 @@ async function promptForMcpServer() {
|
|
|
189
239
|
/**
|
|
190
240
|
* Prompt for credential action (skip / create new / use existing).
|
|
191
241
|
* Choose Skip if you don't have credentials yet; you can add them later in env.template.
|
|
242
|
+
* When "Use existing" is chosen, the caller should fetch credentials and call promptForExistingCredential.
|
|
192
243
|
* @async
|
|
193
244
|
* @function promptForCredentialAction
|
|
194
|
-
* @returns {Promise<Object>} Object with action ('skip'|'create'|'select') and
|
|
245
|
+
* @returns {Promise<Object>} Object with action ('skip'|'create'|'select'); credentialIdOrKey only when action is 'select' and chosen via promptForExistingCredential
|
|
195
246
|
*/
|
|
196
247
|
async function promptForCredentialAction() {
|
|
197
248
|
const { action } = await inquirer.prompt([
|
|
@@ -206,79 +257,65 @@ async function promptForCredentialAction() {
|
|
|
206
257
|
]
|
|
207
258
|
}
|
|
208
259
|
]);
|
|
209
|
-
|
|
260
|
+
return { action };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Prompt to select an existing credential: show a list from the dataplane when available, otherwise ask for ID/key.
|
|
265
|
+
* @async
|
|
266
|
+
* @function promptForExistingCredential
|
|
267
|
+
* @param {Array<{key?: string, id?: string, credentialKey?: string, displayName?: string, name?: string, status?: string}>} [credentialsList] - Credentials from GET /api/v1/wizard/credentials (or empty/null on error)
|
|
268
|
+
* @returns {Promise<{credentialIdOrKey: string}>} Selected credential ID or key
|
|
269
|
+
*/
|
|
270
|
+
async function promptForExistingCredential(credentialsList = []) {
|
|
271
|
+
const list = Array.isArray(credentialsList) ? credentialsList : [];
|
|
272
|
+
if (list.length > 0) {
|
|
273
|
+
const choices = list.map((c) => {
|
|
274
|
+
const { name: baseName, statusFormatted, statusLabel } = formatCredentialWithStatus(c);
|
|
275
|
+
const name = statusFormatted
|
|
276
|
+
? `${statusFormatted} ${baseName}${statusLabel}`
|
|
277
|
+
: String(baseName);
|
|
278
|
+
const value = c.key ?? c.id ?? c.credentialKey ?? '';
|
|
279
|
+
return { name, value };
|
|
280
|
+
}).filter((c) => c.value);
|
|
281
|
+
if (choices.length === 0) {
|
|
282
|
+
return promptForExistingCredentialInput();
|
|
283
|
+
}
|
|
210
284
|
const { credentialIdOrKey } = await inquirer.prompt([
|
|
211
285
|
{
|
|
212
|
-
type: '
|
|
286
|
+
type: 'list',
|
|
213
287
|
name: 'credentialIdOrKey',
|
|
214
|
-
message: '
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return 'Credential ID or key is required (or choose Skip at the previous step)';
|
|
218
|
-
}
|
|
219
|
-
return true;
|
|
220
|
-
}
|
|
288
|
+
message: 'Select a credential:',
|
|
289
|
+
choices,
|
|
290
|
+
pageSize: 10
|
|
221
291
|
}
|
|
222
292
|
]);
|
|
223
|
-
return {
|
|
293
|
+
return { credentialIdOrKey };
|
|
224
294
|
}
|
|
225
|
-
return
|
|
295
|
+
return promptForExistingCredentialInput();
|
|
226
296
|
}
|
|
227
297
|
|
|
228
298
|
/**
|
|
229
|
-
*
|
|
230
|
-
* Empty input means skip.
|
|
299
|
+
* Prompt for credential ID or key via free-text input (used when list is empty or API failed).
|
|
231
300
|
* @async
|
|
232
|
-
* @function
|
|
233
|
-
* @
|
|
234
|
-
* @returns {Promise<Object>} { credentialIdOrKey: string } or { skip: true } if user leaves empty
|
|
301
|
+
* @function promptForExistingCredentialInput
|
|
302
|
+
* @returns {Promise<{credentialIdOrKey: string}>}
|
|
235
303
|
*/
|
|
236
|
-
async function
|
|
237
|
-
const msg = previousError
|
|
238
|
-
? `Credential not found or invalid (${String(previousError).slice(0, 60)}). Enter ID/key or leave empty to skip:`
|
|
239
|
-
: 'Enter credential ID or key (or leave empty to skip):';
|
|
304
|
+
async function promptForExistingCredentialInput() {
|
|
240
305
|
const { credentialIdOrKey } = await inquirer.prompt([
|
|
241
306
|
{
|
|
242
307
|
type: 'input',
|
|
243
308
|
name: 'credentialIdOrKey',
|
|
244
|
-
message:
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Prompt for known platform selection
|
|
254
|
-
* @async
|
|
255
|
-
* @function promptForKnownPlatform
|
|
256
|
-
* @param {Array<{key: string, displayName?: string}>} [platforms] - List of available platforms (if provided)
|
|
257
|
-
* @returns {Promise<string>} Selected platform key
|
|
258
|
-
*/
|
|
259
|
-
async function promptForKnownPlatform(platforms = []) {
|
|
260
|
-
// Default platforms if none provided
|
|
261
|
-
const defaultPlatforms = [
|
|
262
|
-
{ name: 'HubSpot', value: 'hubspot' },
|
|
263
|
-
{ name: 'Salesforce', value: 'salesforce' },
|
|
264
|
-
{ name: 'Zendesk', value: 'zendesk' },
|
|
265
|
-
{ name: 'Slack', value: 'slack' },
|
|
266
|
-
{ name: 'Microsoft 365', value: 'microsoft365' }
|
|
267
|
-
];
|
|
268
|
-
|
|
269
|
-
const choices = platforms.length > 0
|
|
270
|
-
? platforms.map(p => ({ name: p.displayName || p.key, value: p.key }))
|
|
271
|
-
: defaultPlatforms;
|
|
272
|
-
|
|
273
|
-
const { platform } = await inquirer.prompt([
|
|
274
|
-
{
|
|
275
|
-
type: 'list',
|
|
276
|
-
name: 'platform',
|
|
277
|
-
message: 'Select a platform:',
|
|
278
|
-
choices
|
|
309
|
+
message: 'Enter credential ID or key (must exist on the dataplane):',
|
|
310
|
+
validate: (input) => {
|
|
311
|
+
if (!input || typeof input !== 'string' || input.trim().length === 0) {
|
|
312
|
+
return 'Credential ID or key is required (or choose Skip at the previous step)';
|
|
313
|
+
}
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
279
316
|
}
|
|
280
317
|
]);
|
|
281
|
-
return
|
|
318
|
+
return { credentialIdOrKey: credentialIdOrKey.trim() };
|
|
282
319
|
}
|
|
283
320
|
|
|
284
321
|
/**
|
|
@@ -351,49 +388,6 @@ async function promptForUserPreferences() {
|
|
|
351
388
|
};
|
|
352
389
|
}
|
|
353
390
|
|
|
354
|
-
/**
|
|
355
|
-
* Prompt for configuration review and editing
|
|
356
|
-
* @async
|
|
357
|
-
* @function promptForConfigReview
|
|
358
|
-
* @param {Object} systemConfig - System configuration
|
|
359
|
-
* @param {Object[]} datasourceConfigs - Array of datasource configurations
|
|
360
|
-
* @returns {Promise<Object>} Object with review decision and optionally edited configs
|
|
361
|
-
*/
|
|
362
|
-
async function promptForConfigReview(systemConfig, datasourceConfigs) {
|
|
363
|
-
// eslint-disable-next-line no-console
|
|
364
|
-
console.log('\n📋 Generated Configuration:');
|
|
365
|
-
// eslint-disable-next-line no-console
|
|
366
|
-
console.log('\nSystem Configuration:');
|
|
367
|
-
// eslint-disable-next-line no-console
|
|
368
|
-
console.log(JSON.stringify(systemConfig, null, 2));
|
|
369
|
-
// eslint-disable-next-line no-console
|
|
370
|
-
console.log('\nDatasource Configurations:');
|
|
371
|
-
datasourceConfigs.forEach((ds, index) => {
|
|
372
|
-
// eslint-disable-next-line no-console
|
|
373
|
-
console.log(`\nDatasource ${index + 1}:`);
|
|
374
|
-
// eslint-disable-next-line no-console
|
|
375
|
-
console.log(JSON.stringify(ds, null, 2));
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
const { action } = await inquirer.prompt([
|
|
379
|
-
{
|
|
380
|
-
type: 'list',
|
|
381
|
-
name: 'action',
|
|
382
|
-
message: 'What would you like to do?',
|
|
383
|
-
choices: [
|
|
384
|
-
{ name: 'Accept and save', value: 'accept' },
|
|
385
|
-
{ name: 'Cancel', value: 'cancel' }
|
|
386
|
-
]
|
|
387
|
-
}
|
|
388
|
-
]);
|
|
389
|
-
|
|
390
|
-
if (action === 'cancel') {
|
|
391
|
-
return { action: 'cancel' };
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
return { action: 'accept' };
|
|
395
|
-
}
|
|
396
|
-
|
|
397
391
|
/**
|
|
398
392
|
* Prompt for application name
|
|
399
393
|
* @async
|
|
@@ -440,19 +434,24 @@ async function promptForRunWithSavedConfig() {
|
|
|
440
434
|
return run;
|
|
441
435
|
}
|
|
442
436
|
|
|
437
|
+
const secondary = require('./wizard-prompts-secondary');
|
|
438
|
+
|
|
443
439
|
module.exports = {
|
|
444
440
|
promptForMode,
|
|
445
441
|
promptForSystemIdOrKey,
|
|
442
|
+
promptForExistingSystem,
|
|
446
443
|
promptForSourceType,
|
|
447
444
|
promptForOpenApiFile,
|
|
448
445
|
promptForOpenApiUrl,
|
|
449
446
|
promptForMcpServer,
|
|
450
447
|
promptForCredentialAction,
|
|
451
|
-
|
|
452
|
-
|
|
448
|
+
promptForExistingCredential,
|
|
449
|
+
promptForCredentialIdOrKeyRetry: secondary.promptForCredentialIdOrKeyRetry,
|
|
450
|
+
promptForKnownPlatform: secondary.promptForKnownPlatform,
|
|
451
|
+
promptForEntitySelection: secondary.promptForEntitySelection,
|
|
453
452
|
promptForUserIntent,
|
|
454
453
|
promptForUserPreferences,
|
|
455
|
-
promptForConfigReview,
|
|
454
|
+
promptForConfigReview: secondary.promptForConfigReview,
|
|
456
455
|
promptForAppName,
|
|
457
456
|
promptForRunWithSavedConfig
|
|
458
457
|
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wizard README generation - used by wizard file generator
|
|
3
|
+
* @fileoverview Generates README.md for external system wizard output
|
|
4
|
+
* @author AI Fabrix Team
|
|
5
|
+
* @version 2.0.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const fs = require('fs').promises;
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const chalk = require('chalk');
|
|
13
|
+
const logger = require('../utils/logger');
|
|
14
|
+
const { generateExternalReadmeContent } = require('../utils/external-readme');
|
|
15
|
+
|
|
16
|
+
const FORMAT_EXT = { yaml: '.yaml', json: '.json' };
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Converts a string to a schema-valid key segment (lowercase letters, numbers, hyphens only).
|
|
20
|
+
* @param {string} str - Raw entity type or key segment (may be camelCase)
|
|
21
|
+
* @returns {string} Segment matching ^[a-z0-9-]+$
|
|
22
|
+
*/
|
|
23
|
+
function toKeySegment(str) {
|
|
24
|
+
if (!str || typeof str !== 'string') return 'default';
|
|
25
|
+
const withHyphens = str.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
26
|
+
const sanitized = withHyphens.replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
27
|
+
return sanitized || 'default';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Generate README.md with basic documentation
|
|
32
|
+
* @async
|
|
33
|
+
* @function generateReadme
|
|
34
|
+
* @param {Object} options - Options for README generation
|
|
35
|
+
* @param {string} options.appPath - Application directory path
|
|
36
|
+
* @param {string} options.appName - Application name
|
|
37
|
+
* @param {string} options.systemKey - System key
|
|
38
|
+
* @param {Object} options.systemConfig - System configuration
|
|
39
|
+
* @param {Object[]} options.datasourceConfigs - Array of datasource configurations
|
|
40
|
+
* @param {string} [options.aiGeneratedContent] - Optional AI-generated README content from dataplane
|
|
41
|
+
* @param {string} [options.format] - Output format: 'yaml' or 'json'
|
|
42
|
+
* @throws {Error} If generation fails
|
|
43
|
+
*/
|
|
44
|
+
async function generateReadme(options) {
|
|
45
|
+
const { appPath, appName, systemKey, systemConfig, datasourceConfigs, aiGeneratedContent, format } = options;
|
|
46
|
+
try {
|
|
47
|
+
const readmePath = path.join(appPath, 'README.md');
|
|
48
|
+
|
|
49
|
+
if (aiGeneratedContent) {
|
|
50
|
+
await fs.writeFile(readmePath, aiGeneratedContent, 'utf8');
|
|
51
|
+
logger.log(chalk.green('✓ Generated README.md (AI-generated from dataplane)'));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const ext = FORMAT_EXT[format === 'json' ? 'json' : 'yaml'] || '.yaml';
|
|
56
|
+
const datasources = (Array.isArray(datasourceConfigs) ? datasourceConfigs : []).map((ds, index) => {
|
|
57
|
+
const entityType = ds.entityType || ds.entityKey || ds.key?.split('-').pop() || `datasource${index + 1}`;
|
|
58
|
+
const keySegment = toKeySegment(entityType);
|
|
59
|
+
const datasourceKey = ds.key || `${systemKey}-${keySegment}`;
|
|
60
|
+
const datasourceKeyOnly = datasourceKey.includes('-') && datasourceKey.startsWith(`${systemKey}-`)
|
|
61
|
+
? datasourceKey.substring(systemKey.length + 1)
|
|
62
|
+
: keySegment;
|
|
63
|
+
return {
|
|
64
|
+
key: datasourceKey,
|
|
65
|
+
entityType,
|
|
66
|
+
displayName: ds.displayName || ds.name || ds.key || `Datasource ${index + 1}`,
|
|
67
|
+
fileName: `${systemKey}-datasource-${datasourceKeyOnly}${ext}`
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const readmeContent = generateExternalReadmeContent({
|
|
72
|
+
appName,
|
|
73
|
+
systemKey,
|
|
74
|
+
systemType: systemConfig.type || systemConfig.systemType,
|
|
75
|
+
displayName: systemConfig.displayName,
|
|
76
|
+
description: systemConfig.description,
|
|
77
|
+
fileExt: ext,
|
|
78
|
+
datasources
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
await fs.writeFile(readmePath, readmeContent, 'utf8');
|
|
82
|
+
logger.log(chalk.green('✓ Generated README.md (template)'));
|
|
83
|
+
} catch (error) {
|
|
84
|
+
throw new Error(`Failed to generate README.md: ${error.message}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = { generateReadme };
|