@aifabrix/builder 2.39.3 → 2.40.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 +6 -6
- package/README.md +2 -2
- package/babel.config.js +6 -0
- package/integration/hubspot/README.md +53 -141
- package/integration/hubspot/application.yaml +37 -0
- package/integration/hubspot/env.template +2 -11
- package/integration/hubspot/hubspot-deploy.json +1 -0
- package/integration/hubspot/test.js +5 -5
- package/lib/api/credentials.api.js +5 -5
- package/lib/api/deployments.api.js +2 -2
- package/lib/api/pipeline.api.js +17 -17
- package/lib/api/wizard.api.js +2 -2
- package/lib/app/config.js +11 -6
- package/lib/app/deploy-config.js +13 -16
- package/lib/app/deploy.js +29 -22
- package/lib/app/display.js +1 -1
- package/lib/app/dockerfile.js +11 -12
- package/lib/app/helpers.js +51 -13
- package/lib/app/index.js +14 -2
- package/lib/app/prompts.js +37 -45
- package/lib/app/push.js +8 -11
- package/lib/app/readme.js +16 -12
- package/lib/app/register.js +1 -1
- package/lib/app/run-helpers.js +31 -22
- package/lib/app/run.js +44 -5
- package/lib/app/show-display.js +104 -44
- package/lib/app/show.js +123 -43
- package/lib/build/index.js +11 -18
- package/lib/cli/setup-app.js +36 -29
- package/lib/cli/setup-auth.js +18 -15
- package/lib/cli/setup-credential-deployment.js +3 -1
- package/lib/cli/setup-external-system.js +35 -16
- package/lib/cli/setup-infra.js +45 -23
- package/lib/cli/setup-utility.js +79 -31
- package/lib/commands/app-logs.js +28 -20
- package/lib/commands/app.js +30 -26
- package/lib/commands/convert.js +202 -0
- package/lib/commands/credential-list.js +78 -17
- package/lib/commands/datasource.js +24 -24
- package/lib/commands/deployment-list.js +13 -6
- package/lib/commands/up-common.js +80 -42
- package/lib/commands/up-dataplane.js +15 -14
- package/lib/commands/up-miso.js +15 -14
- package/lib/commands/upload.js +163 -0
- package/lib/commands/wizard-core.js +5 -4
- package/lib/core/diff.js +84 -9
- package/lib/core/key-generator.js +9 -12
- package/lib/core/secrets-docker-env.js +2 -2
- package/lib/core/secrets.js +3 -2
- package/lib/core/templates.js +2 -2
- package/lib/datasource/deploy.js +2 -1
- package/lib/deployment/deployer.js +76 -48
- package/lib/external-system/delete.js +0 -1
- package/lib/external-system/deploy-helpers.js +5 -6
- package/lib/external-system/deploy.js +7 -2
- package/lib/external-system/download-helpers.js +4 -4
- package/lib/external-system/download.js +11 -10
- package/lib/external-system/generator.js +19 -17
- package/lib/external-system/test.js +10 -15
- package/lib/generator/builders.js +1 -1
- package/lib/generator/external-controller-manifest.js +26 -29
- package/lib/generator/external-schema-utils.js +6 -18
- package/lib/generator/external.js +32 -27
- package/lib/generator/github.js +1 -1
- package/lib/generator/helpers.js +12 -19
- package/lib/generator/index.js +15 -15
- package/lib/generator/parse-image.js +35 -0
- package/lib/generator/split-readme.js +105 -0
- package/lib/generator/split-variables.js +149 -0
- package/lib/generator/split.js +86 -246
- package/lib/generator/wizard.js +46 -69
- package/lib/schema/application-schema.json +4 -4
- package/lib/schema/external-datasource.schema.json +5 -0
- package/lib/schema/external-system.schema.json +10 -0
- package/lib/utils/app-config-resolver.js +52 -0
- package/lib/utils/app-register-api.js +1 -1
- package/lib/utils/app-register-auth.js +1 -1
- package/lib/utils/app-register-config.js +16 -23
- package/lib/utils/app-register-validator.js +2 -2
- package/lib/utils/cli-utils.js +47 -3
- package/lib/utils/config-format.js +154 -0
- package/lib/utils/config-paths.js +19 -52
- package/lib/utils/config-tokens.js +1 -0
- package/lib/utils/docker-build.js +71 -94
- package/lib/utils/dockerfile-utils.js +1 -1
- package/lib/utils/env-copy.js +4 -4
- package/lib/utils/env-ports.js +2 -2
- package/lib/utils/error-formatter.js +1 -1
- package/lib/utils/error-formatters/validation-errors.js +1 -1
- package/lib/utils/external-readme.js +12 -5
- package/lib/utils/external-system-test-helpers.js +2 -0
- package/lib/utils/health-check.js +55 -66
- package/lib/utils/image-version.js +12 -21
- package/lib/utils/paths.js +39 -66
- package/lib/utils/port-resolver.js +8 -8
- package/lib/utils/schema-loader.js +22 -0
- package/lib/utils/schema-resolver.js +23 -33
- package/lib/utils/secrets-helpers.js +7 -7
- package/lib/utils/secrets-utils.js +10 -12
- package/lib/utils/template-helpers.js +13 -13
- package/lib/utils/token-manager.js +20 -2
- package/lib/utils/variable-transformer.js +2 -2
- package/lib/validation/validate-display.js +3 -4
- package/lib/validation/validate.js +33 -27
- package/lib/validation/validator.js +50 -30
- package/package.json +2 -1
- package/templates/README.md +1 -1
- package/templates/applications/README.md.hbs +3 -3
- package/templates/applications/miso-controller/env.template +3 -1
- package/templates/external-system/README.md.hbs +4 -4
- package/integration/hubspot/variables.yaml +0 -17
- /package/templates/applications/dataplane/{variables.yaml → application.yaml} +0 -0
- /package/templates/applications/keycloak/{variables.yaml → application.yaml} +0 -0
- /package/templates/applications/miso-controller/{variables.yaml → application.yaml} +0 -0
|
@@ -8,11 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
const { handleCommandError } = require('../utils/cli-utils');
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
* Sets up external system commands
|
|
13
|
-
* @param {Command} program - Commander program instance
|
|
14
|
-
*/
|
|
15
|
-
function setupExternalSystemCommands(program) {
|
|
11
|
+
function setupDownloadCommand(program) {
|
|
16
12
|
program.command('download <system-key>')
|
|
17
13
|
.description('Download external system from dataplane to local development structure')
|
|
18
14
|
.option('--dry-run', 'Show what would be downloaded without actually downloading')
|
|
@@ -25,17 +21,32 @@ function setupExternalSystemCommands(program) {
|
|
|
25
21
|
process.exit(1);
|
|
26
22
|
}
|
|
27
23
|
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function setupUploadCommand(program) {
|
|
27
|
+
program.command('upload <system-key>')
|
|
28
|
+
.description('Upload external system to dataplane (upload → validate → publish; no controller deploy)')
|
|
29
|
+
.option('--dry-run', 'Validate and build payload only; no API calls')
|
|
30
|
+
.option('--dataplane <url>', 'Dataplane URL (default: discovered from controller)')
|
|
31
|
+
.action(async(systemKey, options) => {
|
|
32
|
+
try {
|
|
33
|
+
const upload = require('../commands/upload');
|
|
34
|
+
await upload.uploadExternalSystem(systemKey, options);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
handleCommandError(error, 'upload');
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
28
41
|
|
|
42
|
+
function setupDeleteCommand(program) {
|
|
29
43
|
program.command('delete <system-key>')
|
|
30
44
|
.description('Delete external system from dataplane (also deletes all associated datasources)')
|
|
31
|
-
.option('--type <type>', 'Application type (external
|
|
45
|
+
.option('--type <type>', 'Application type (default: external; use "external" to target integration/<app>)')
|
|
32
46
|
.option('--yes', 'Skip confirmation prompt')
|
|
33
47
|
.option('--force', 'Skip confirmation prompt (alias for --yes)')
|
|
34
48
|
.action(async(systemKey, options) => {
|
|
35
49
|
try {
|
|
36
|
-
if (options.type !== 'external') {
|
|
37
|
-
throw new Error('Delete command for external systems requires --type external');
|
|
38
|
-
}
|
|
39
50
|
const externalDelete = require('../external-system/delete');
|
|
40
51
|
await externalDelete.deleteExternalSystem(systemKey, options);
|
|
41
52
|
} catch (error) {
|
|
@@ -43,7 +54,9 @@ function setupExternalSystemCommands(program) {
|
|
|
43
54
|
process.exit(1);
|
|
44
55
|
}
|
|
45
56
|
});
|
|
57
|
+
}
|
|
46
58
|
|
|
59
|
+
function setupExternalSystemTestCommands(program) {
|
|
47
60
|
program.command('test <app>')
|
|
48
61
|
.description('Run unit tests for external system (local validation, no API calls)')
|
|
49
62
|
.option('-d, --datasource <key>', 'Test specific datasource only')
|
|
@@ -53,15 +66,12 @@ function setupExternalSystemCommands(program) {
|
|
|
53
66
|
const test = require('../external-system/test');
|
|
54
67
|
const results = await test.testExternalSystem(appName, options);
|
|
55
68
|
test.displayTestResults(results, options.verbose);
|
|
56
|
-
if (!results.valid)
|
|
57
|
-
process.exit(1);
|
|
58
|
-
}
|
|
69
|
+
if (!results.valid) process.exit(1);
|
|
59
70
|
} catch (error) {
|
|
60
71
|
handleCommandError(error, 'test');
|
|
61
72
|
process.exit(1);
|
|
62
73
|
}
|
|
63
74
|
});
|
|
64
|
-
|
|
65
75
|
program.command('test-integration <app>')
|
|
66
76
|
.description('Run integration tests via dataplane pipeline API')
|
|
67
77
|
.option('-d, --datasource <key>', 'Test specific datasource only')
|
|
@@ -74,9 +84,7 @@ function setupExternalSystemCommands(program) {
|
|
|
74
84
|
const test = require('../external-system/test');
|
|
75
85
|
const results = await test.testExternalSystemIntegration(appName, options);
|
|
76
86
|
test.displayIntegrationTestResults(results, options.verbose);
|
|
77
|
-
if (!results.success)
|
|
78
|
-
process.exit(1);
|
|
79
|
-
}
|
|
87
|
+
if (!results.success) process.exit(1);
|
|
80
88
|
} catch (error) {
|
|
81
89
|
handleCommandError(error, 'test-integration');
|
|
82
90
|
process.exit(1);
|
|
@@ -84,4 +92,15 @@ function setupExternalSystemCommands(program) {
|
|
|
84
92
|
});
|
|
85
93
|
}
|
|
86
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Sets up external system commands
|
|
97
|
+
* @param {Command} program - Commander program instance
|
|
98
|
+
*/
|
|
99
|
+
function setupExternalSystemCommands(program) {
|
|
100
|
+
setupDownloadCommand(program);
|
|
101
|
+
setupUploadCommand(program);
|
|
102
|
+
setupDeleteCommand(program);
|
|
103
|
+
setupExternalSystemTestCommands(program);
|
|
104
|
+
}
|
|
105
|
+
|
|
87
106
|
module.exports = { setupExternalSystemCommands };
|
package/lib/cli/setup-infra.js
CHANGED
|
@@ -48,11 +48,7 @@ async function runUpInfraCommand(options) {
|
|
|
48
48
|
await infra.startInfra(developerId, { traefik: useTraefik });
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
* Sets up infrastructure commands
|
|
53
|
-
* @param {Command} program - Commander program instance
|
|
54
|
-
*/
|
|
55
|
-
function setupInfraCommands(program) {
|
|
51
|
+
function setupUpInfraCommand(program) {
|
|
56
52
|
program.command('up-infra')
|
|
57
53
|
.description('Start local infrastructure: Postgres, Redis, optional Traefik')
|
|
58
54
|
.option('-d, --developer <id>', 'Set developer ID and start infrastructure')
|
|
@@ -66,7 +62,9 @@ function setupInfraCommands(program) {
|
|
|
66
62
|
process.exit(1);
|
|
67
63
|
}
|
|
68
64
|
});
|
|
65
|
+
}
|
|
69
66
|
|
|
67
|
+
function setupUpPlatformCommand(program) {
|
|
70
68
|
program.command('up-platform')
|
|
71
69
|
.description('Start platform (Keycloak, Miso Controller, Dataplane) from community images; infra must be up')
|
|
72
70
|
.option('-r, --registry <url>', 'Override registry for all apps (e.g. myacr.azurecr.io)')
|
|
@@ -81,7 +79,9 @@ function setupInfraCommands(program) {
|
|
|
81
79
|
process.exit(1);
|
|
82
80
|
}
|
|
83
81
|
});
|
|
82
|
+
}
|
|
84
83
|
|
|
84
|
+
function setupUpMisoCommand(program) {
|
|
85
85
|
program.command('up-miso')
|
|
86
86
|
.description('Install keycloak and miso-controller from images (no build). Infra must be up. For dataplane use up-dataplane. Uses auto-generated secrets for testing.')
|
|
87
87
|
.option('-r, --registry <url>', 'Override registry for all apps (e.g. myacr.azurecr.io)')
|
|
@@ -95,7 +95,9 @@ function setupInfraCommands(program) {
|
|
|
95
95
|
process.exit(1);
|
|
96
96
|
}
|
|
97
97
|
});
|
|
98
|
+
}
|
|
98
99
|
|
|
100
|
+
function setupUpDataplaneCommand(program) {
|
|
99
101
|
program.command('up-dataplane')
|
|
100
102
|
.description('Register, deploy, then run dataplane app locally in dev (always local deployment; requires login, environment must be dev)')
|
|
101
103
|
.option('-r, --registry <url>', 'Override registry for dataplane image')
|
|
@@ -109,7 +111,9 @@ function setupInfraCommands(program) {
|
|
|
109
111
|
process.exit(1);
|
|
110
112
|
}
|
|
111
113
|
});
|
|
114
|
+
}
|
|
112
115
|
|
|
116
|
+
function setupDownInfraCommand(program) {
|
|
113
117
|
program.command('down-infra [app]')
|
|
114
118
|
.description('Stop and remove local infrastructure services or a specific application')
|
|
115
119
|
.option('-v, --volumes', 'Remove volumes (deletes all data)')
|
|
@@ -118,34 +122,30 @@ function setupInfraCommands(program) {
|
|
|
118
122
|
if (typeof appName === 'string' && appName.trim().length > 0) {
|
|
119
123
|
await appLib.downApp(appName, { volumes: !!options.volumes });
|
|
120
124
|
} else {
|
|
121
|
-
if (options.volumes)
|
|
122
|
-
|
|
123
|
-
} else {
|
|
124
|
-
await infra.stopInfra();
|
|
125
|
-
}
|
|
125
|
+
if (options.volumes) await infra.stopInfraWithVolumes();
|
|
126
|
+
else await infra.stopInfra();
|
|
126
127
|
}
|
|
127
128
|
} catch (error) {
|
|
128
129
|
handleCommandError(error, 'down-infra');
|
|
129
130
|
process.exit(1);
|
|
130
131
|
}
|
|
131
132
|
});
|
|
133
|
+
}
|
|
132
134
|
|
|
135
|
+
function setupDoctorCommand(program) {
|
|
133
136
|
program.command('doctor')
|
|
134
137
|
.description('Check environment and configuration')
|
|
135
138
|
.action(async() => {
|
|
136
139
|
try {
|
|
137
140
|
const result = await validator.checkEnvironment();
|
|
138
141
|
logger.log('\n🔍 AI Fabrix Environment Check\n');
|
|
139
|
-
|
|
140
142
|
logger.log(`Docker: ${result.docker === 'ok' ? '✅ Running' : '❌ Not available'}`);
|
|
141
143
|
logger.log(`Ports: ${result.ports === 'ok' ? '✅ Available' : '⚠️ Some ports in use'}`);
|
|
142
144
|
logger.log(`Secrets: ${result.secrets === 'ok' ? '✅ Configured' : '❌ Missing'}`);
|
|
143
|
-
|
|
144
145
|
if (result.recommendations.length > 0) {
|
|
145
146
|
logger.log('\n📋 Recommendations:');
|
|
146
147
|
result.recommendations.forEach(rec => logger.log(` • ${rec}`));
|
|
147
148
|
}
|
|
148
|
-
|
|
149
149
|
if (result.docker === 'ok') {
|
|
150
150
|
try {
|
|
151
151
|
const health = await infra.checkInfraHealth();
|
|
@@ -158,37 +158,35 @@ function setupInfraCommands(program) {
|
|
|
158
158
|
logger.log('\n🏥 Infrastructure: Not running');
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
|
-
|
|
162
161
|
logger.log('');
|
|
163
162
|
} catch (error) {
|
|
164
163
|
handleCommandError(error, 'doctor');
|
|
165
164
|
process.exit(1);
|
|
166
165
|
}
|
|
167
166
|
});
|
|
167
|
+
}
|
|
168
168
|
|
|
169
|
+
function setupStatusCommand(program) {
|
|
169
170
|
program.command('status')
|
|
170
171
|
.description('Show detailed infrastructure service status and running applications')
|
|
171
172
|
.action(async() => {
|
|
172
173
|
try {
|
|
173
174
|
const status = await infra.getInfraStatus();
|
|
174
175
|
logger.log('\n📊 Infrastructure Status\n');
|
|
175
|
-
|
|
176
176
|
Object.entries(status).forEach(([service, info]) => {
|
|
177
|
-
const
|
|
178
|
-
const icon = normalizedStatus === 'running' ? '✅' : '❌';
|
|
177
|
+
const icon = String(info.status).trim().toLowerCase() === 'running' ? '✅' : '❌';
|
|
179
178
|
logger.log(`${icon} ${service}:`);
|
|
180
179
|
logger.log(` Status: ${info.status}`);
|
|
181
180
|
logger.log(` Port: ${info.port}`);
|
|
182
181
|
logger.log(` URL: ${info.url}`);
|
|
183
182
|
logger.log('');
|
|
184
183
|
});
|
|
185
|
-
|
|
186
184
|
const apps = await infra.getAppStatus();
|
|
187
185
|
if (apps.length > 0) {
|
|
188
186
|
logger.log('📱 Running Applications\n');
|
|
189
187
|
apps.forEach((appInfo) => {
|
|
190
|
-
const
|
|
191
|
-
const icon =
|
|
188
|
+
const s = String(appInfo.status).trim().toLowerCase();
|
|
189
|
+
const icon = s.includes('running') || s.includes('up') ? '✅' : '❌';
|
|
192
190
|
logger.log(`${icon} ${appInfo.name}:`);
|
|
193
191
|
logger.log(` Container: ${appInfo.container}`);
|
|
194
192
|
logger.log(` Port: ${appInfo.port}`);
|
|
@@ -202,13 +200,22 @@ function setupInfraCommands(program) {
|
|
|
202
200
|
process.exit(1);
|
|
203
201
|
}
|
|
204
202
|
});
|
|
203
|
+
}
|
|
205
204
|
|
|
205
|
+
const INFRA_SERVICES = ['postgres', 'redis', 'pgadmin', 'redis-commander', 'traefik'];
|
|
206
|
+
|
|
207
|
+
function setupRestartCommand(program) {
|
|
206
208
|
program.command('restart <service>')
|
|
207
|
-
.description('Restart a
|
|
209
|
+
.description('Restart an infrastructure service or a Docker application (builder/<app>)')
|
|
208
210
|
.action(async(service) => {
|
|
209
211
|
try {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
+
if (INFRA_SERVICES.includes(service)) {
|
|
213
|
+
await infra.restartService(service);
|
|
214
|
+
logger.log(`✅ ${service} service restarted successfully`);
|
|
215
|
+
} else {
|
|
216
|
+
await appLib.restartApp(service);
|
|
217
|
+
logger.log(`✅ ${service} restarted successfully`);
|
|
218
|
+
}
|
|
212
219
|
} catch (error) {
|
|
213
220
|
handleCommandError(error, 'restart');
|
|
214
221
|
process.exit(1);
|
|
@@ -216,4 +223,19 @@ function setupInfraCommands(program) {
|
|
|
216
223
|
});
|
|
217
224
|
}
|
|
218
225
|
|
|
226
|
+
/**
|
|
227
|
+
* Sets up infrastructure commands
|
|
228
|
+
* @param {Command} program - Commander program instance
|
|
229
|
+
*/
|
|
230
|
+
function setupInfraCommands(program) {
|
|
231
|
+
setupUpInfraCommand(program);
|
|
232
|
+
setupUpPlatformCommand(program);
|
|
233
|
+
setupUpMisoCommand(program);
|
|
234
|
+
setupUpDataplaneCommand(program);
|
|
235
|
+
setupDownInfraCommand(program);
|
|
236
|
+
setupDoctorCommand(program);
|
|
237
|
+
setupStatusCommand(program);
|
|
238
|
+
setupRestartCommand(program);
|
|
239
|
+
}
|
|
240
|
+
|
|
219
241
|
module.exports = { setupInfraCommands };
|
package/lib/cli/setup-utility.js
CHANGED
|
@@ -12,9 +12,21 @@ const chalk = require('chalk');
|
|
|
12
12
|
const secrets = require('../core/secrets');
|
|
13
13
|
const generator = require('../generator');
|
|
14
14
|
const logger = require('../utils/logger');
|
|
15
|
-
const { handleCommandError } = require('../utils/cli-utils');
|
|
15
|
+
const { handleCommandError, logOfflinePathWhenType } = require('../utils/cli-utils');
|
|
16
16
|
const { detectAppType, getDeployJsonPath } = require('../utils/paths');
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Resolve app path and type for split-json (integration first, then builder).
|
|
20
|
+
*
|
|
21
|
+
* @param {string} appName - Application name
|
|
22
|
+
* @param {Object} [_options] - Command options (reserved)
|
|
23
|
+
* @returns {Promise<{appPath: string, appType: string}>}
|
|
24
|
+
*/
|
|
25
|
+
async function resolveSplitJsonApp(appName, _options) {
|
|
26
|
+
const { appPath, appType } = await detectAppType(appName);
|
|
27
|
+
return { appPath, appType };
|
|
28
|
+
}
|
|
29
|
+
|
|
18
30
|
/**
|
|
19
31
|
* Handles split-json command logic
|
|
20
32
|
* @async
|
|
@@ -23,15 +35,22 @@ const { detectAppType, getDeployJsonPath } = require('../utils/paths');
|
|
|
23
35
|
* @returns {Promise<Object>} Paths to generated files
|
|
24
36
|
*/
|
|
25
37
|
async function handleSplitJsonCommand(appName, options) {
|
|
26
|
-
const { appPath, appType } = await
|
|
38
|
+
const { appPath, appType } = await resolveSplitJsonApp(appName, options);
|
|
39
|
+
logOfflinePathWhenType(appPath);
|
|
27
40
|
|
|
28
41
|
const outputDir = options.output || appPath;
|
|
29
42
|
if (appType === 'external') {
|
|
43
|
+
const deployJsonPath = getDeployJsonPath(appName, 'external', true);
|
|
44
|
+
if (fs.existsSync(deployJsonPath)) {
|
|
45
|
+
return generator.splitDeployJson(deployJsonPath, outputDir);
|
|
46
|
+
}
|
|
30
47
|
const schemaPath = path.join(appPath, 'application-schema.json');
|
|
31
|
-
if (
|
|
32
|
-
|
|
48
|
+
if (fs.existsSync(schemaPath)) {
|
|
49
|
+
return generator.splitExternalApplicationSchema(schemaPath, outputDir);
|
|
33
50
|
}
|
|
34
|
-
|
|
51
|
+
throw new Error(
|
|
52
|
+
`No deployment or schema file found. Expected one of:\n • ${deployJsonPath}\n • ${schemaPath}\n\nRun "aifabrix json ${appName}" to generate the deploy JSON, or provide application-schema.json.`
|
|
53
|
+
);
|
|
35
54
|
}
|
|
36
55
|
|
|
37
56
|
const deployJsonPath = getDeployJsonPath(appName, appType, true);
|
|
@@ -50,18 +69,20 @@ async function handleSplitJsonCommand(appName, options) {
|
|
|
50
69
|
function logSplitJsonResult(result) {
|
|
51
70
|
logger.log(chalk.green('\n✓ Successfully split deployment JSON into component files:'));
|
|
52
71
|
logger.log(` • env.template: ${result.envTemplate}`);
|
|
53
|
-
logger.log(` •
|
|
72
|
+
logger.log(` • application.yaml: ${result.variables}`);
|
|
73
|
+
if (result.systemFile) {
|
|
74
|
+
logger.log(` • system: ${result.systemFile}`);
|
|
75
|
+
}
|
|
76
|
+
if (result.datasourceFiles && result.datasourceFiles.length > 0) {
|
|
77
|
+
result.datasourceFiles.forEach(filePath => logger.log(` • datasource: ${filePath}`));
|
|
78
|
+
}
|
|
54
79
|
if (result.rbac) {
|
|
55
80
|
logger.log(` • rbac.yml: ${result.rbac}`);
|
|
56
81
|
}
|
|
57
82
|
logger.log(` • README.md: ${result.readme}`);
|
|
58
83
|
}
|
|
59
84
|
|
|
60
|
-
|
|
61
|
-
* Sets up utility commands
|
|
62
|
-
* @param {Command} program - Commander program instance
|
|
63
|
-
*/
|
|
64
|
-
function setupUtilityCommands(program) {
|
|
85
|
+
function setupResolveCommand(program) {
|
|
65
86
|
program.command('resolve <app>')
|
|
66
87
|
.description('Generate .env file from template and validate application files')
|
|
67
88
|
.option('-f, --force', 'Generate missing secret keys in secrets file')
|
|
@@ -70,7 +91,6 @@ function setupUtilityCommands(program) {
|
|
|
70
91
|
try {
|
|
71
92
|
const envPath = await secrets.generateEnvFile(appName, undefined, 'docker', options.force);
|
|
72
93
|
logger.log(`✓ Generated .env file: ${envPath}`);
|
|
73
|
-
|
|
74
94
|
if (!options.skipValidation) {
|
|
75
95
|
const validate = require('../validation/validate');
|
|
76
96
|
const result = await validate.validateAppOrFile(appName);
|
|
@@ -85,26 +105,24 @@ function setupUtilityCommands(program) {
|
|
|
85
105
|
process.exit(1);
|
|
86
106
|
}
|
|
87
107
|
});
|
|
108
|
+
}
|
|
88
109
|
|
|
110
|
+
function setupJsonCommand(program) {
|
|
89
111
|
program.command('json <app>')
|
|
90
112
|
.description('Generate deployment JSON to disk (<app>-deploy.json). Use before commit so version control has the correct file.')
|
|
91
|
-
.option('--type <type>', 'Application type (external) - if set, only checks integration folder')
|
|
92
113
|
.action(async(appName, options) => {
|
|
93
114
|
try {
|
|
94
115
|
const result = await generator.generateDeployJsonWithValidation(appName, options);
|
|
95
116
|
if (result.success) {
|
|
96
117
|
const fileName = result.path.includes('application-schema.json') ? 'application-schema.json' : 'deployment JSON';
|
|
97
118
|
logger.log(`✓ Generated ${fileName}: ${result.path}`);
|
|
98
|
-
|
|
99
119
|
if (result.validation.warnings && result.validation.warnings.length > 0) {
|
|
100
120
|
logger.log('\n⚠️ Warnings:');
|
|
101
|
-
result.validation.warnings.forEach(
|
|
121
|
+
result.validation.warnings.forEach(w => logger.log(` • ${w}`));
|
|
102
122
|
}
|
|
103
123
|
} else {
|
|
104
124
|
logger.log('❌ Validation failed:');
|
|
105
|
-
|
|
106
|
-
result.validation.errors.forEach(error => logger.log(` • ${error}`));
|
|
107
|
-
}
|
|
125
|
+
(result.validation.errors || []).forEach(e => logger.log(` • ${e}`));
|
|
108
126
|
process.exit(1);
|
|
109
127
|
}
|
|
110
128
|
} catch (error) {
|
|
@@ -112,21 +130,41 @@ function setupUtilityCommands(program) {
|
|
|
112
130
|
process.exit(1);
|
|
113
131
|
}
|
|
114
132
|
});
|
|
133
|
+
}
|
|
115
134
|
|
|
135
|
+
function setupSplitJsonConvertShowCommands(program) {
|
|
116
136
|
program.command('split-json <app>')
|
|
117
|
-
.description('Split deployment JSON into component files (env.template,
|
|
137
|
+
.description('Split deployment JSON into component files (env.template, application.yaml, rbac.yml, README.md)')
|
|
118
138
|
.option('-o, --output <dir>', 'Output directory for component files (defaults to same directory as JSON)')
|
|
119
|
-
.option('--type <type>', 'Application type (external) - if set, only checks integration folder')
|
|
120
139
|
.action(async(appName, options) => {
|
|
121
140
|
try {
|
|
122
|
-
|
|
123
|
-
logSplitJsonResult(result);
|
|
141
|
+
logSplitJsonResult(await handleSplitJsonCommand(appName, options));
|
|
124
142
|
} catch (error) {
|
|
125
143
|
handleCommandError(error, 'split-json');
|
|
126
144
|
process.exit(1);
|
|
127
145
|
}
|
|
128
146
|
});
|
|
129
147
|
|
|
148
|
+
program.command('convert <app>')
|
|
149
|
+
.description('Convert integration/external system and datasource config files between JSON and YAML')
|
|
150
|
+
.option('--format <format>', 'Target format: json | yaml (required)')
|
|
151
|
+
.option('-f, --force', 'Skip confirmation prompt')
|
|
152
|
+
.action(async(appName, options) => {
|
|
153
|
+
try {
|
|
154
|
+
const { runConvert } = require('../commands/convert');
|
|
155
|
+
const { converted, deleted } = await runConvert(appName, { format: options.format, force: options.force });
|
|
156
|
+
logger.log(chalk.green('\n✓ Convert complete.'));
|
|
157
|
+
converted.forEach(p => logger.log(` • ${p}`));
|
|
158
|
+
if (deleted.length > 0) {
|
|
159
|
+
logger.log(chalk.gray(' Removed old files:'));
|
|
160
|
+
deleted.forEach(p => logger.log(chalk.gray(` ${p}`)));
|
|
161
|
+
}
|
|
162
|
+
} catch (error) {
|
|
163
|
+
handleCommandError(error, 'convert');
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
130
168
|
program.command('show <appKey>')
|
|
131
169
|
.description('Show application info from local builder/ or integration/ (offline) or from controller (--online)')
|
|
132
170
|
.option('--online', 'Fetch application data from the controller')
|
|
@@ -140,18 +178,17 @@ function setupUtilityCommands(program) {
|
|
|
140
178
|
process.exit(1);
|
|
141
179
|
}
|
|
142
180
|
});
|
|
181
|
+
}
|
|
143
182
|
|
|
183
|
+
function setupValidateDiffCommands(program) {
|
|
144
184
|
program.command('validate <appOrFile>')
|
|
145
185
|
.description('Validate application or external integration file')
|
|
146
|
-
.option('--type <type>', 'Application type (external) - if set, only checks integration folder')
|
|
147
186
|
.action(async(appOrFile, options) => {
|
|
148
187
|
try {
|
|
149
188
|
const validate = require('../validation/validate');
|
|
150
189
|
const result = await validate.validateAppOrFile(appOrFile, options);
|
|
151
190
|
validate.displayValidationResults(result);
|
|
152
|
-
if (!result.valid)
|
|
153
|
-
process.exit(1);
|
|
154
|
-
}
|
|
191
|
+
if (!result.valid) process.exit(1);
|
|
155
192
|
} catch (error) {
|
|
156
193
|
handleCommandError(error, 'validate');
|
|
157
194
|
process.exit(1);
|
|
@@ -160,14 +197,14 @@ function setupUtilityCommands(program) {
|
|
|
160
197
|
|
|
161
198
|
program.command('diff <file1> <file2>')
|
|
162
199
|
.description('Compare two configuration files (for deployment pipeline)')
|
|
163
|
-
.
|
|
200
|
+
.option('--no-validate', 'Skip schema validation (type check still applied)')
|
|
201
|
+
.action(async(file1, file2, cmd) => {
|
|
164
202
|
try {
|
|
165
203
|
const diff = require('../core/diff');
|
|
166
|
-
const
|
|
204
|
+
const opts = cmd.opts();
|
|
205
|
+
const result = await diff.compareFiles(file1, file2, { validate: opts.validate !== false });
|
|
167
206
|
diff.formatDiffOutput(result);
|
|
168
|
-
if (!result.identical)
|
|
169
|
-
process.exit(1);
|
|
170
|
-
}
|
|
207
|
+
if (!result.identical) process.exit(1);
|
|
171
208
|
} catch (error) {
|
|
172
209
|
handleCommandError(error, 'diff');
|
|
173
210
|
process.exit(1);
|
|
@@ -175,4 +212,15 @@ function setupUtilityCommands(program) {
|
|
|
175
212
|
});
|
|
176
213
|
}
|
|
177
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Sets up utility commands
|
|
217
|
+
* @param {Command} program - Commander program instance
|
|
218
|
+
*/
|
|
219
|
+
function setupUtilityCommands(program) {
|
|
220
|
+
setupResolveCommand(program);
|
|
221
|
+
setupJsonCommand(program);
|
|
222
|
+
setupSplitJsonConvertShowCommands(program);
|
|
223
|
+
setupValidateDiffCommands(program);
|
|
224
|
+
}
|
|
225
|
+
|
|
178
226
|
module.exports = { setupUtilityCommands };
|
package/lib/commands/app-logs.js
CHANGED
|
@@ -35,9 +35,15 @@ const LEVEL_AFTER_PREFIX_REGEX = /(?:^|\s)(DEBUG|INFO|WARN|WARNING|ERROR)\s*:/i;
|
|
|
35
35
|
/** Level after word boundary (e.g. "[pino]error: msg" or BOM + "error:") so "error:" is detected anywhere */
|
|
36
36
|
const LEVEL_WORD_BOUNDARY_REGEX = /\b(DEBUG|INFO|WARN|WARNING|ERROR)\s*:/i;
|
|
37
37
|
|
|
38
|
-
/** JSON "level" field pattern */
|
|
38
|
+
/** JSON "level" field pattern (string: "error", "info", etc.) */
|
|
39
39
|
const LEVEL_JSON_REGEX = /"level"\s*:\s*"(\w+)"/i;
|
|
40
40
|
|
|
41
|
+
/** JSON "level" numeric (pino/bunyan: 50=error, 60=fatal, 40=warn, 30=info, 20=debug, 10=trace) */
|
|
42
|
+
const LEVEL_JSON_NUMERIC_REGEX = /"level"\s*:\s*(\d+)/;
|
|
43
|
+
|
|
44
|
+
/** Fallback: line contains whole-word "error" or "Error" when no other level detected (catches stack traces, "Error: msg", etc.) */
|
|
45
|
+
const ERROR_WORD_FALLBACK_REGEX = /\berror\b/i;
|
|
46
|
+
|
|
41
47
|
/** Env key patterns that indicate a secret (mask value) */
|
|
42
48
|
const SECRET_KEY_PATTERN = /password|secret|token|credential|api[_-]?key/i;
|
|
43
49
|
|
|
@@ -69,6 +75,20 @@ function maskEnvLine(line) {
|
|
|
69
75
|
return line;
|
|
70
76
|
}
|
|
71
77
|
|
|
78
|
+
/** Normalize level string to canonical 'debug'|'info'|'warn'|'error'. */
|
|
79
|
+
function normalizeLevel(raw) {
|
|
80
|
+
const s = (raw || '').toLowerCase();
|
|
81
|
+
return s === 'warning' ? 'warn' : s;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Map pino/bunyan numeric level (10–60) to level string. */
|
|
85
|
+
function numericLevelToName(num) {
|
|
86
|
+
if (num >= 50) return 'error';
|
|
87
|
+
if (num >= 40) return 'warn';
|
|
88
|
+
if (num >= 30) return 'info';
|
|
89
|
+
return 'debug';
|
|
90
|
+
}
|
|
91
|
+
|
|
72
92
|
/**
|
|
73
93
|
* Extract log level from a line (prefix like INFO:/error: or JSON "level":"info").
|
|
74
94
|
* Supports: INFO:, ERROR:, error:, info: (miso-controller/pino), WARN:, DEBUG:, and "level":"x" in JSON.
|
|
@@ -77,26 +97,14 @@ function maskEnvLine(line) {
|
|
|
77
97
|
*/
|
|
78
98
|
function getLogLevel(line) {
|
|
79
99
|
if (!line || typeof line !== 'string') return null;
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
const afterPrefixMatch = line.match(LEVEL_AFTER_PREFIX_REGEX);
|
|
86
|
-
if (afterPrefixMatch) {
|
|
87
|
-
const raw = afterPrefixMatch[1].toLowerCase();
|
|
88
|
-
return raw === 'warning' ? 'warn' : raw;
|
|
89
|
-
}
|
|
90
|
-
const wordBoundaryMatch = line.match(LEVEL_WORD_BOUNDARY_REGEX);
|
|
91
|
-
if (wordBoundaryMatch) {
|
|
92
|
-
const raw = wordBoundaryMatch[1].toLowerCase();
|
|
93
|
-
return raw === 'warning' ? 'warn' : raw;
|
|
94
|
-
}
|
|
95
|
-
const jsonMatch = line.match(LEVEL_JSON_REGEX);
|
|
96
|
-
if (jsonMatch) {
|
|
97
|
-
const raw = jsonMatch[1].toLowerCase();
|
|
98
|
-
return raw === 'warning' ? 'warn' : raw;
|
|
100
|
+
const prefixRe = [LEVEL_PREFIX_REGEX, LEVEL_AFTER_PREFIX_REGEX, LEVEL_WORD_BOUNDARY_REGEX, LEVEL_JSON_REGEX];
|
|
101
|
+
for (const re of prefixRe) {
|
|
102
|
+
const m = line.match(re);
|
|
103
|
+
if (m) return normalizeLevel(m[1]);
|
|
99
104
|
}
|
|
105
|
+
const jsonNum = line.match(LEVEL_JSON_NUMERIC_REGEX);
|
|
106
|
+
if (jsonNum) return numericLevelToName(parseInt(jsonNum[1], 10));
|
|
107
|
+
if (ERROR_WORD_FALLBACK_REGEX.test(line)) return 'error';
|
|
100
108
|
return null;
|
|
101
109
|
}
|
|
102
110
|
|
package/lib/commands/app.js
CHANGED
|
@@ -17,21 +17,11 @@ const { rotateSecret } = require('../app/rotate-secret');
|
|
|
17
17
|
const { showApp } = require('../app/show');
|
|
18
18
|
const { runAppDeploymentList } = require('./deployment-list');
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
* @param {Command} program - Commander program instance
|
|
23
|
-
*/
|
|
24
|
-
function setupAppCommands(program) {
|
|
25
|
-
const app = program
|
|
26
|
-
.command('app')
|
|
27
|
-
.description('Manage applications');
|
|
28
|
-
|
|
29
|
-
// Register command (controller and environment from config.yaml)
|
|
30
|
-
app
|
|
31
|
-
.command('register <appKey>')
|
|
20
|
+
function setupAppRegisterCommand(app) {
|
|
21
|
+
app.command('register <appKey>')
|
|
32
22
|
.description('Register application and get pipeline credentials')
|
|
33
|
-
.option('-p, --port <port>', 'Application port (default: from
|
|
34
|
-
.option('-u, --url <url>', 'Application URL. If omitted: app.url, deployment.dataplaneUrl or deployment.appUrl in
|
|
23
|
+
.option('-p, --port <port>', 'Application port (default: from application.yaml)')
|
|
24
|
+
.option('-u, --url <url>', 'Application URL. If omitted: app.url, deployment.dataplaneUrl or deployment.appUrl in application.yaml; else http://localhost:{build.localPort or port}')
|
|
35
25
|
.option('-n, --name <name>', 'Override display name')
|
|
36
26
|
.option('-d, --description <desc>', 'Override description')
|
|
37
27
|
.action(async(appKey, options) => {
|
|
@@ -42,10 +32,10 @@ function setupAppCommands(program) {
|
|
|
42
32
|
process.exit(1);
|
|
43
33
|
}
|
|
44
34
|
});
|
|
35
|
+
}
|
|
45
36
|
|
|
46
|
-
|
|
47
|
-
app
|
|
48
|
-
.command('list')
|
|
37
|
+
function setupAppListCommand(app) {
|
|
38
|
+
app.command('list')
|
|
49
39
|
.description('List applications')
|
|
50
40
|
.action(async(options) => {
|
|
51
41
|
try {
|
|
@@ -55,10 +45,10 @@ function setupAppCommands(program) {
|
|
|
55
45
|
process.exit(1);
|
|
56
46
|
}
|
|
57
47
|
});
|
|
48
|
+
}
|
|
58
49
|
|
|
59
|
-
|
|
60
|
-
app
|
|
61
|
-
.command('rotate-secret <appKey>')
|
|
50
|
+
function setupAppRotateSecretCommand(app) {
|
|
51
|
+
app.command('rotate-secret <appKey>')
|
|
62
52
|
.description('Rotate pipeline ClientSecret for an application')
|
|
63
53
|
.action(async(appKey, options) => {
|
|
64
54
|
try {
|
|
@@ -68,11 +58,12 @@ function setupAppCommands(program) {
|
|
|
68
58
|
process.exit(1);
|
|
69
59
|
}
|
|
70
60
|
});
|
|
61
|
+
}
|
|
71
62
|
|
|
72
|
-
|
|
73
|
-
app
|
|
74
|
-
.command('show <appKey>')
|
|
63
|
+
function setupAppShowCommand(app) {
|
|
64
|
+
app.command('show <appKey>')
|
|
75
65
|
.description('Show application from controller (online). Same as aifabrix show <appKey> --online')
|
|
66
|
+
.option('--online', 'Fetch from controller (default for this command)')
|
|
76
67
|
.option('--json', 'Output as JSON')
|
|
77
68
|
.option('--permissions', 'Show only list of permissions')
|
|
78
69
|
.action(async(appKey, options) => {
|
|
@@ -83,10 +74,10 @@ function setupAppCommands(program) {
|
|
|
83
74
|
process.exit(1);
|
|
84
75
|
}
|
|
85
76
|
});
|
|
77
|
+
}
|
|
86
78
|
|
|
87
|
-
|
|
88
|
-
app
|
|
89
|
-
.command('deployment <appKey>')
|
|
79
|
+
function setupAppDeploymentCommand(app) {
|
|
80
|
+
app.command('deployment <appKey>')
|
|
90
81
|
.description('List last N deployments for an application in current environment (default pageSize=50)')
|
|
91
82
|
.option('--controller <url>', 'Controller URL (default: from config)')
|
|
92
83
|
.option('--environment <env>', 'Environment key (default: from config)')
|
|
@@ -106,5 +97,18 @@ function setupAppCommands(program) {
|
|
|
106
97
|
});
|
|
107
98
|
}
|
|
108
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Setup application management commands
|
|
102
|
+
* @param {Command} program - Commander program instance
|
|
103
|
+
*/
|
|
104
|
+
function setupAppCommands(program) {
|
|
105
|
+
const app = program.command('app').description('Manage applications');
|
|
106
|
+
setupAppRegisterCommand(app);
|
|
107
|
+
setupAppListCommand(app);
|
|
108
|
+
setupAppRotateSecretCommand(app);
|
|
109
|
+
setupAppShowCommand(app);
|
|
110
|
+
setupAppDeploymentCommand(app);
|
|
111
|
+
}
|
|
112
|
+
|
|
109
113
|
module.exports = { setupAppCommands };
|
|
110
114
|
|