@aifabrix/builder 2.44.6 → 2.45.5
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/cli-layout.mdc +7 -3
- package/.cursor/rules/docs-rules.mdc +3 -1
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
- package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
- package/jest.projects.js +100 -1
- package/lib/api/agent-metadata-validation.api.js +93 -0
- package/lib/api/audit-api-helpers.js +68 -0
- package/lib/api/audit.api.js +119 -0
- package/lib/api/auth-cache.api.js +44 -0
- package/lib/api/certificates.api.js +4 -4
- package/lib/api/credential.api.js +2 -2
- package/lib/api/dataplane-health.api.js +137 -0
- package/lib/api/dataplane-sync.api.js +27 -0
- package/lib/api/datasources-core.api.js +21 -18
- package/lib/api/datasources-extended.api.js +22 -22
- package/lib/api/dimensions.api.js +3 -0
- package/lib/api/external-systems.api.js +13 -13
- package/lib/api/governance-scenarios.api.js +39 -0
- package/lib/api/groups.api.js +79 -0
- package/lib/api/index.js +45 -2
- package/lib/api/pipeline.api.js +4 -4
- package/lib/api/protection.api.js +157 -0
- package/lib/api/records-bulk.api.js +30 -0
- package/lib/api/records-search-parse.js +49 -0
- package/lib/api/records-search.api.js +29 -0
- package/lib/api/types/agent-metadata-validation.types.js +67 -0
- package/lib/api/types/audit.types.js +72 -0
- package/lib/api/types/dataplane-health.types.js +27 -0
- package/lib/api/types/dimensions.types.js +2 -0
- package/lib/api/types/governance-scenarios.types.js +39 -0
- package/lib/api/types/protection.types.js +69 -0
- package/lib/api/types/records-bulk.types.js +35 -0
- package/lib/api/types/records-search.types.js +28 -0
- package/lib/api/user-groups.api.js +50 -0
- package/lib/api/users.api.js +95 -0
- package/lib/api/validation-run.api.js +3 -3
- package/lib/api/validation-runner.js +3 -3
- package/lib/api/wizard-platform.api.js +3 -3
- package/lib/api/wizard.api.js +21 -21
- package/lib/app/deploy.js +34 -19
- package/lib/app/helpers.js +3 -3
- package/lib/app/index.js +3 -3
- package/lib/app/register.js +7 -6
- package/lib/app/restart-display.js +52 -21
- package/lib/app/rotate-secret.js +7 -6
- package/lib/app/run-helpers.js +15 -8
- package/lib/app/run.js +57 -9
- package/lib/app/show-display.js +7 -0
- package/lib/app/show.js +87 -5
- package/lib/build/index.js +9 -5
- package/lib/cli/index.js +4 -2
- package/lib/cli/infra-guided.js +62 -28
- package/lib/cli/installation-log-command.js +73 -0
- package/lib/cli/setup-app.help.js +50 -2
- package/lib/cli/setup-app.js +44 -25
- package/lib/cli/setup-app.test-commands.js +80 -21
- package/lib/cli/setup-auth.js +124 -52
- package/lib/cli/setup-external-system.js +37 -3
- package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
- package/lib/cli/setup-infra-up-platform-action.js +131 -0
- package/lib/cli/setup-infra.js +60 -119
- package/lib/cli/setup-platform.js +1 -1
- package/lib/cli/setup-utility-help-after.js +45 -0
- package/lib/cli/setup-utility-resolve.js +132 -0
- package/lib/cli/setup-utility.js +148 -116
- package/lib/commands/app-logs.js +81 -33
- package/lib/commands/auth-config.js +116 -18
- package/lib/commands/auth-status-dataplane-version.js +231 -0
- package/lib/commands/auth-status-display.js +94 -0
- package/lib/commands/auth-status.js +70 -6
- package/lib/commands/credential-push.js +10 -5
- package/lib/commands/datasource-load-export-cli.js +178 -0
- package/lib/commands/datasource-test-trust-cli.js +114 -0
- package/lib/commands/datasource-unified-test-cli.js +25 -17
- package/lib/commands/datasource-unified-test-cli.options.js +18 -3
- package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
- package/lib/commands/datasource-verify-audit-cli.js +96 -0
- package/lib/commands/datasource.js +68 -1
- package/lib/commands/dimension-value.js +43 -13
- package/lib/commands/dimension.js +38 -7
- package/lib/commands/identity-apply-sync.js +131 -0
- package/lib/commands/identity-group-cli.js +146 -0
- package/lib/commands/identity-membership-role-cache.js +175 -0
- package/lib/commands/identity-shared.js +94 -0
- package/lib/commands/identity-user-cli.js +158 -0
- package/lib/commands/identity.js +43 -0
- package/lib/commands/login.js +46 -16
- package/lib/commands/protection-cli-leaves.js +286 -0
- package/lib/commands/protection.js +50 -0
- package/lib/commands/repair.js +13 -3
- package/lib/commands/setup-modes.js +19 -6
- package/lib/commands/setup-prompts.js +41 -8
- package/lib/commands/setup.js +114 -9
- package/lib/commands/teardown.js +54 -5
- package/lib/commands/test-e2e-external.js +40 -9
- package/lib/commands/test-governance-cli-options.js +47 -0
- package/lib/commands/test-governance-command-action.js +55 -0
- package/lib/commands/test-governance-external.js +88 -0
- package/lib/commands/test-trust-cli-options.js +32 -0
- package/lib/commands/test-trust-command-action.js +118 -0
- package/lib/commands/test-trust-external.js +127 -0
- package/lib/commands/up-common.js +48 -14
- package/lib/commands/up-dataplane.js +21 -18
- package/lib/commands/up-miso.js +12 -8
- package/lib/commands/upload.js +38 -15
- package/lib/commands/wizard-core.js +3 -0
- package/lib/core/audit-logger.js +1 -34
- package/lib/core/config-admin-email.js +56 -0
- package/lib/core/config-device-dataplane.js +185 -0
- package/lib/core/config-normalize.js +60 -0
- package/lib/core/config-registered-controller-urls.js +54 -0
- package/lib/core/config.js +33 -50
- package/lib/core/secrets-ensure-infra.js +1 -1
- package/lib/core/secrets-env-content.js +86 -90
- package/lib/core/secrets-env-declarative-expand.js +170 -0
- package/lib/core/secrets-env-write.js +2 -0
- package/lib/core/secrets-load.js +106 -102
- package/lib/datasource/agent-trust-debug-log.js +45 -0
- package/lib/datasource/agent-trust-fetch.js +115 -0
- package/lib/datasource/agent-trust-map.js +101 -0
- package/lib/datasource/agent-trust-mock.js +44 -0
- package/lib/datasource/agent-trust-run.js +93 -0
- package/lib/datasource/agent-trust-timeout.js +25 -0
- package/lib/datasource/audit-evidence-display.js +80 -0
- package/lib/datasource/audit-evidence-extract-debug.js +103 -0
- package/lib/datasource/audit-evidence-extract.js +177 -0
- package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
- package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
- package/lib/datasource/audit-evidence-poll.js +109 -0
- package/lib/datasource/audit-evidence-run.js +177 -0
- package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
- package/lib/datasource/audit-evidence-verify-matrix.js +177 -0
- package/lib/datasource/audit-evidence-verify.js +108 -0
- package/lib/datasource/bulk-loader-run-batches.js +88 -0
- package/lib/datasource/bulk-loader-service-retry.js +52 -0
- package/lib/datasource/bulk-loader-service.js +127 -0
- package/lib/datasource/capability/run-capability-dimension.js +2 -1
- package/lib/datasource/datasource-exporter-resolve.js +82 -0
- package/lib/datasource/datasource-exporter-service.js +144 -0
- package/lib/datasource/datasource-identifier.js +148 -0
- package/lib/datasource/datasource-load-export-context.js +92 -0
- package/lib/datasource/integration-context.js +6 -4
- package/lib/datasource/local-data-paths.js +145 -0
- package/lib/datasource/log-cleaner.js +241 -0
- package/lib/datasource/log-viewer.js +111 -6
- package/lib/datasource/record-file-parser.js +85 -0
- package/lib/datasource/record-mapper.js +129 -0
- package/lib/datasource/resolve-app.js +74 -55
- package/lib/datasource/test-e2e.js +26 -2
- package/lib/datasource/unified-validation-run-resolve.js +45 -12
- package/lib/datasource/unified-validation-run.js +43 -17
- package/lib/datasource/validate.js +37 -20
- package/lib/external-system/deploy.js +5 -1
- package/lib/external-system/test-system-level.js +25 -4
- package/lib/external-system/test.js +3 -1
- package/lib/governance/governance-pack-loader.js +70 -0
- package/lib/governance/governance-run-display.js +119 -0
- package/lib/identity/identity-apply-core.js +108 -0
- package/lib/identity/identity-apply-phases.js +244 -0
- package/lib/identity/identity-apply-service.js +77 -0
- package/lib/identity/identity-csv-parser.js +114 -0
- package/lib/internal/fs-real-sync.js +29 -1
- package/lib/internal/node-fs.js +2 -0
- package/lib/protection/auth-context.js +45 -0
- package/lib/protection/convert-batch.js +90 -0
- package/lib/protection/load.js +41 -0
- package/lib/protection/paths.js +158 -0
- package/lib/protection/preflight-datasource-ready.js +46 -0
- package/lib/protection/protection-create-probe-lines.js +142 -0
- package/lib/protection/protection-create-probes.js +95 -0
- package/lib/protection/protection-create-scaffold.js +281 -0
- package/lib/protection/protection-display-helpers.js +445 -0
- package/lib/protection/protection-display.js +215 -0
- package/lib/protection/protection-preset-registry.js +97 -0
- package/lib/protection/report-exit.js +62 -0
- package/lib/protection/resolve.js +202 -0
- package/lib/protection/run-commands-validate.js +130 -0
- package/lib/protection/run-commands.js +216 -0
- package/lib/protection/run-protection-create-helpers.js +134 -0
- package/lib/protection/run-protection-create-probes-flow.js +43 -0
- package/lib/protection/run-protection-create.js +191 -0
- package/lib/protection/scope.js +22 -0
- package/lib/protection/sync-after-upload.js +64 -0
- package/lib/protection/upload-batch.js +127 -0
- package/lib/protection/validate-batch.js +113 -0
- package/lib/protection/validate-local.js +65 -0
- package/lib/resolvers/dimension-file.js +29 -4
- package/lib/schema/application-schema.json +4 -0
- package/lib/schema/external-datasource.schema.json +47 -13
- package/lib/schema/flag-map-validation-run.json +2 -2
- package/lib/schema/infra.parameter.yaml +10 -0
- package/lib/schema/protection.schema.json +374 -0
- package/lib/schema/type/credential-field-registry.json +636 -0
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/agent-trust-run-display.js +231 -0
- package/lib/utils/agent-trust-run-exit.js +59 -0
- package/lib/utils/agent-trust-wait-ui.js +94 -0
- package/lib/utils/app-config-resolver.js +24 -1
- package/lib/utils/applications-config-defaults.js +206 -0
- package/lib/utils/auth-config-validator.js +2 -12
- package/lib/utils/bash-secret-env.js +1 -1
- package/lib/utils/cli-exit-codes.js +30 -0
- package/lib/utils/cli-sync-options.js +33 -0
- package/lib/utils/compose-generate-docker-compose.js +111 -6
- package/lib/utils/compose-generator.js +17 -8
- package/lib/utils/controller-url.js +50 -7
- package/lib/utils/credential-push-ui.js +90 -0
- package/lib/utils/dataplane-cli-version-gate.js +202 -0
- package/lib/utils/dataplane-cli-version-help.js +74 -0
- package/lib/utils/datasource-export-display.js +137 -0
- package/lib/utils/datasource-load-display.js +174 -0
- package/lib/utils/datasource-load-export-ui.js +120 -0
- package/lib/utils/datasource-test-run-capability-scope.js +3 -3
- package/lib/utils/datasource-test-run-display.js +3 -3
- package/lib/utils/datasource-test-run-tty-log.js +5 -1
- package/lib/utils/env-copy.js +99 -14
- package/lib/utils/env-template.js +5 -1
- package/lib/utils/health-check-url.js +18 -15
- package/lib/utils/health-check.js +7 -5
- package/lib/utils/help-builder.js +30 -24
- package/lib/utils/infra-optional-service-flags.js +69 -0
- package/lib/utils/installation-log-core.js +282 -0
- package/lib/utils/installation-log-record.js +237 -0
- package/lib/utils/installation-log.js +123 -0
- package/lib/utils/log-redaction.js +105 -0
- package/lib/utils/manifest-location.js +164 -0
- package/lib/utils/manifest-source-emit.js +184 -0
- package/lib/utils/paths.js +238 -89
- package/lib/utils/remote-secrets-loader.js +7 -1
- package/lib/utils/run-cli-flags.js +29 -0
- package/lib/utils/secrets-canonical.js +10 -3
- package/lib/utils/secrets-path.js +3 -4
- package/lib/utils/secrets-utils.js +20 -10
- package/lib/utils/semver-compare.js +127 -0
- package/lib/utils/system-builder-root.js +10 -2
- package/lib/utils/upload-pipeline-ui.js +91 -0
- package/lib/utils/url-declarative-public-base.js +80 -12
- package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
- package/lib/utils/url-declarative-resolve-build.js +24 -393
- package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
- package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
- package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
- package/lib/utils/url-declarative-resolve.js +47 -7
- package/lib/utils/url-declarative-runtime-base-path.js +21 -1
- package/lib/utils/urls-local-registry-scan.js +103 -0
- package/lib/utils/urls-local-registry.js +161 -90
- package/lib/utils/validation-poll-ui.js +14 -7
- package/lib/validation/wizard-config-validator.js +58 -19
- package/package.json +3 -1
- package/templates/applications/dataplane/application.yaml +4 -0
- package/templates/applications/dataplane/env.template +13 -1
- package/templates/applications/dataplane/rbac.yaml +4 -0
- package/templates/applications/miso-controller/application.yaml +2 -0
- package/templates/applications/miso-controller/env.template +27 -29
- package/templates/external-system/README.md.hbs +56 -8
- package/.npmrc.token +0 -1
package/lib/cli/setup-utility.js
CHANGED
|
@@ -10,45 +10,12 @@ const { formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
|
|
|
10
10
|
const path = require('path');
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const chalk = require('chalk');
|
|
13
|
-
const secrets = require('../core/secrets');
|
|
14
13
|
const generator = require('../generator');
|
|
15
14
|
const logger = require('../utils/logger');
|
|
16
15
|
const { handleCommandError, logOfflinePathWhenType } = require('../utils/cli-utils');
|
|
17
16
|
const { detectAppType, getDeployJsonPath, getResolveAppPath } = require('../utils/paths');
|
|
18
|
-
|
|
19
|
-
const JSON_HELP_AFTER =
|
|
20
|
-
Example:
|
|
21
|
-
$ aifabrix json myapp
|
|
22
|
-
Generates *-deploy.json (or application-schema.json) for commit before deploy.
|
|
23
|
-
`;
|
|
24
|
-
|
|
25
|
-
const VALIDATE_HELP_AFTER = `
|
|
26
|
-
Examples:
|
|
27
|
-
$ aifabrix validate myapp
|
|
28
|
-
$ aifabrix validate myapp --cert-sync
|
|
29
|
-
$ aifabrix validate --integration
|
|
30
|
-
$ aifabrix validate --builder
|
|
31
|
-
`;
|
|
32
|
-
|
|
33
|
-
const REPAIR_HELP_AFTER = `
|
|
34
|
-
Examples:
|
|
35
|
-
$ aifabrix repair hubspot-demo
|
|
36
|
-
Align manifest, system/datasource files, RBAC extract, env.template, and deploy JSON under integration/<systemKey>/.
|
|
37
|
-
|
|
38
|
-
$ aifabrix repair hubspot-demo --dry-run
|
|
39
|
-
Show what would change without writing files.
|
|
40
|
-
|
|
41
|
-
$ aifabrix repair hubspot-demo --auth oauth2
|
|
42
|
-
Set authentication method; updates the system file and env.template (oauth2, aad, apikey, basic, …).
|
|
43
|
-
|
|
44
|
-
$ aifabrix repair hubspot-demo --rbac --expose --sync --test
|
|
45
|
-
Optional datasource fixes: RBAC roles/permissions, exposed.schema from attributes, default sync block, testPayload stubs.
|
|
46
|
-
|
|
47
|
-
$ aifabrix repair hubspot-demo --doc
|
|
48
|
-
Regenerate README.md from the current deployment manifest only (other drift fixes unchanged).
|
|
49
|
-
|
|
50
|
-
Before overwriting files, repair writes timestamped backups under integration/<systemKey>/backup/ (same as datasource capability copy). Use --no-backup to skip.
|
|
51
|
-
`;
|
|
17
|
+
const { setupResolveCommand } = require('./setup-utility-resolve');
|
|
18
|
+
const { JSON_HELP_AFTER, VALIDATE_HELP_AFTER, REPAIR_HELP_AFTER } = require('./setup-utility-help-after');
|
|
52
19
|
|
|
53
20
|
/**
|
|
54
21
|
* Resolve app path and type for split-json (integration first, then builder).
|
|
@@ -121,46 +88,20 @@ function logSplitJsonResult(result) {
|
|
|
121
88
|
}
|
|
122
89
|
}
|
|
123
90
|
|
|
124
|
-
function setupResolveCommand(program) {
|
|
125
|
-
program.command('resolve <app>')
|
|
126
|
-
.description('Generate .env from template; optional validate after')
|
|
127
|
-
.option('-f, --force', 'Generate missing secret keys in secrets file')
|
|
128
|
-
.option('--skip-validation', 'Skip file validation after generating .env')
|
|
129
|
-
.action(async(appName, options) => {
|
|
130
|
-
try {
|
|
131
|
-
const { appPath, envOnly } = await getResolveAppPath(appName);
|
|
132
|
-
const envPath = await secrets.generateEnvFile(
|
|
133
|
-
appName,
|
|
134
|
-
undefined,
|
|
135
|
-
'docker',
|
|
136
|
-
options.force,
|
|
137
|
-
{ appPath, envOnly, skipOutputPath: false, preserveFromPath: null }
|
|
138
|
-
);
|
|
139
|
-
logger.log(`✔ Generated .env file: ${envPath}`);
|
|
140
|
-
if (envOnly) {
|
|
141
|
-
logger.log(chalk.gray(' (env-only mode: validation skipped; no application config file)'));
|
|
142
|
-
} else if (!options.skipValidation) {
|
|
143
|
-
const validate = require('../validation/validate');
|
|
144
|
-
const result = await validate.validateAppOrFile(appName);
|
|
145
|
-
validate.displayValidationResults(result);
|
|
146
|
-
if (!result.valid) {
|
|
147
|
-
logger.log(chalk.yellow('\n⚠ Validation found errors. Fix them before deploying.'));
|
|
148
|
-
process.exit(1);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
} catch (error) {
|
|
152
|
-
handleCommandError(error, 'resolve');
|
|
153
|
-
process.exit(1);
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
|
|
158
91
|
function setupJsonCommand(program) {
|
|
159
|
-
program.command('json <
|
|
92
|
+
program.command('json <appKey|systemKey>')
|
|
160
93
|
.description('Write deployment JSON to disk for version control')
|
|
161
94
|
.addHelpText('after', JSON_HELP_AFTER)
|
|
162
95
|
.action(async(appName, options) => {
|
|
163
96
|
try {
|
|
97
|
+
const resolved = await getResolveAppPath(appName);
|
|
98
|
+
const { emitManifestMetadataLineIfTTY } = require('../utils/manifest-source-emit');
|
|
99
|
+
emitManifestMetadataLineIfTTY(logger, {
|
|
100
|
+
appKey: appName,
|
|
101
|
+
appPath: resolved.appPath,
|
|
102
|
+
envOnly: resolved.envOnly,
|
|
103
|
+
json: false
|
|
104
|
+
});
|
|
164
105
|
const result = await generator.generateDeployJsonWithValidation(appName, options);
|
|
165
106
|
if (result.success) {
|
|
166
107
|
const fileName = result.path.includes('application-schema.json') ? 'application-schema.json' : 'deployment JSON';
|
|
@@ -182,7 +123,7 @@ function setupJsonCommand(program) {
|
|
|
182
123
|
}
|
|
183
124
|
|
|
184
125
|
function setupSplitJsonCommand(program) {
|
|
185
|
-
program.command('split-json <
|
|
126
|
+
program.command('split-json <appKey|systemKey>')
|
|
186
127
|
.description('Split deploy JSON into env.template, application.yaml, rbac, README, …')
|
|
187
128
|
.option('-o, --output <dir>', 'Output directory for component files (defaults to same directory as JSON)')
|
|
188
129
|
.action(async(appName, options) => {
|
|
@@ -289,32 +230,54 @@ function setupRepairCommand(program) {
|
|
|
289
230
|
});
|
|
290
231
|
}
|
|
291
232
|
|
|
233
|
+
async function resolveConvertTargetFormat(options) {
|
|
234
|
+
const config = require('../core/config');
|
|
235
|
+
const effectiveFormat = options.format || (await config.getFormat());
|
|
236
|
+
if (!effectiveFormat) {
|
|
237
|
+
throw new Error(
|
|
238
|
+
'Option --format is required and must be \'json\' or \'yaml\' (or set default with aifabrix dev set-format)'
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
const normalized = effectiveFormat.trim().toLowerCase();
|
|
242
|
+
if (normalized !== 'json' && normalized !== 'yaml') {
|
|
243
|
+
throw new Error('Option --format must be \'json\' or \'yaml\'');
|
|
244
|
+
}
|
|
245
|
+
return normalized;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function logConvertCommandResults(converted, deleted) {
|
|
249
|
+
logger.log(formatSuccessParagraph('Convert complete.'));
|
|
250
|
+
converted.forEach((p) => logger.log(` • ${p}`));
|
|
251
|
+
if (deleted.length > 0) {
|
|
252
|
+
logger.log(chalk.gray(' Removed old files:'));
|
|
253
|
+
deleted.forEach((p) => logger.log(chalk.gray(` ${p}`)));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async function runConvertCommandForApp(appName, options) {
|
|
258
|
+
const normalized = await resolveConvertTargetFormat(options);
|
|
259
|
+
const { isProtectionScope } = require('../protection/scope');
|
|
260
|
+
if (isProtectionScope(appName)) {
|
|
261
|
+
const { runConvertProtectionBatch } = require('../protection/convert-batch');
|
|
262
|
+
const { converted, deleted } = await runConvertProtectionBatch(normalized, {
|
|
263
|
+
force: options.force
|
|
264
|
+
});
|
|
265
|
+
logConvertCommandResults(converted, deleted);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const { runConvert } = require('../commands/convert');
|
|
269
|
+
const { converted, deleted } = await runConvert(appName, { format: normalized, force: options.force });
|
|
270
|
+
logConvertCommandResults(converted, deleted);
|
|
271
|
+
}
|
|
272
|
+
|
|
292
273
|
function setupConvertCommand(program) {
|
|
293
|
-
program.command('convert <
|
|
294
|
-
.description('Convert
|
|
274
|
+
program.command('convert <appKey|systemKey>')
|
|
275
|
+
.description('Convert config files between JSON and YAML (builder/<appKey>/ or integration/<systemKey>/)')
|
|
295
276
|
.option('--format <format>', 'Target format: json | yaml (required unless config format is set)')
|
|
296
277
|
.option('-f, --force', 'Skip confirmation prompt')
|
|
297
278
|
.action(async(appName, options) => {
|
|
298
279
|
try {
|
|
299
|
-
|
|
300
|
-
const effectiveFormat = options.format || (await config.getFormat());
|
|
301
|
-
if (!effectiveFormat) {
|
|
302
|
-
throw new Error(
|
|
303
|
-
'Option --format is required and must be \'json\' or \'yaml\' (or set default with aifabrix dev set-format)'
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
const normalized = effectiveFormat.trim().toLowerCase();
|
|
307
|
-
if (normalized !== 'json' && normalized !== 'yaml') {
|
|
308
|
-
throw new Error('Option --format must be \'json\' or \'yaml\'');
|
|
309
|
-
}
|
|
310
|
-
const { runConvert } = require('../commands/convert');
|
|
311
|
-
const { converted, deleted } = await runConvert(appName, { format: normalized, force: options.force });
|
|
312
|
-
logger.log(formatSuccessParagraph('Convert complete.'));
|
|
313
|
-
converted.forEach(p => logger.log(` • ${p}`));
|
|
314
|
-
if (deleted.length > 0) {
|
|
315
|
-
logger.log(chalk.gray(' Removed old files:'));
|
|
316
|
-
deleted.forEach(p => logger.log(chalk.gray(` ${p}`)));
|
|
317
|
-
}
|
|
280
|
+
await runConvertCommandForApp(appName, options);
|
|
318
281
|
} catch (error) {
|
|
319
282
|
handleCommandError(error, 'convert');
|
|
320
283
|
process.exit(1);
|
|
@@ -323,8 +286,8 @@ function setupConvertCommand(program) {
|
|
|
323
286
|
}
|
|
324
287
|
|
|
325
288
|
function setupShowCommand(program) {
|
|
326
|
-
program.command('show <
|
|
327
|
-
.description('Show app from local tree (default) or controller (--online)')
|
|
289
|
+
program.command('show <appKey|systemKey>')
|
|
290
|
+
.description('Show builder app or external system from local tree (default) or controller (--online)')
|
|
328
291
|
.option('--online', 'Fetch application data from the controller')
|
|
329
292
|
.option('--json', 'Output as JSON')
|
|
330
293
|
.option(
|
|
@@ -353,52 +316,121 @@ function setupSplitJsonConvertShowCommands(program) {
|
|
|
353
316
|
setupShowCommand(program);
|
|
354
317
|
}
|
|
355
318
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
319
|
+
function emitManifestAfterValidateIfApplicable(appOrFile, result, outFormat) {
|
|
320
|
+
if (outFormat === 'json' || !result?.appPath || !appOrFile || typeof appOrFile !== 'string') {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
try {
|
|
324
|
+
if (fs.existsSync(appOrFile) && fs.statSync(appOrFile).isFile()) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
const { emitManifestMetadataLineIfTTY } = require('../utils/manifest-source-emit');
|
|
328
|
+
emitManifestMetadataLineIfTTY(logger, {
|
|
329
|
+
appKey: appOrFile,
|
|
330
|
+
appPath: result.appPath,
|
|
331
|
+
envOnly: false,
|
|
332
|
+
json: false
|
|
333
|
+
});
|
|
334
|
+
} catch {
|
|
335
|
+
/* ignore emit failures */
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async function runValidateBatchBranch(validate, opts) {
|
|
359
340
|
const integration = opts.integration === true;
|
|
360
341
|
const builder = opts.builder === true;
|
|
342
|
+
if (!integration && !builder) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
361
345
|
const outFormat = (opts.format || 'default').toLowerCase();
|
|
346
|
+
const batchResult = integration && builder
|
|
347
|
+
? await validate.validateAll(opts)
|
|
348
|
+
: integration
|
|
349
|
+
? await validate.validateAllIntegrations(opts)
|
|
350
|
+
: await validate.validateAllBuilderApps(opts);
|
|
351
|
+
if (outFormat === 'json') {
|
|
352
|
+
logger.log(JSON.stringify(batchResult, null, 2));
|
|
353
|
+
} else {
|
|
354
|
+
validate.displayBatchValidationResults(batchResult);
|
|
355
|
+
}
|
|
356
|
+
if (!batchResult.valid) process.exit(1);
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
362
359
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
360
|
+
async function runProtectionValidateBranch(appOrFile, opts) {
|
|
361
|
+
const { validateProtectionBatchCommand } = require('../protection/validate-batch');
|
|
362
|
+
const code = await validateProtectionBatchCommand(
|
|
363
|
+
{
|
|
364
|
+
env: opts.env,
|
|
365
|
+
json: (opts.format || '').toLowerCase() === 'json',
|
|
366
|
+
warningsAsErrors: opts.warningsAsErrors === true,
|
|
367
|
+
simulate: opts.simulate === true,
|
|
368
|
+
verbose: opts.verbose === true
|
|
369
|
+
},
|
|
370
|
+
logger
|
|
371
|
+
);
|
|
372
|
+
if (code !== 0) {
|
|
373
|
+
process.exit(code);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function buildValidateJsonPayload(result, appOrFile) {
|
|
378
|
+
let payload = result;
|
|
379
|
+
try {
|
|
380
|
+
if (result?.appPath && appOrFile && typeof appOrFile === 'string') {
|
|
381
|
+
if (!(fs.existsSync(appOrFile) && fs.statSync(appOrFile).isFile())) {
|
|
382
|
+
const { getManifestSourcePayload } = require('../utils/manifest-source-emit');
|
|
383
|
+
payload = { ...result, manifestSource: getManifestSourcePayload(appOrFile, result.appPath) };
|
|
384
|
+
}
|
|
373
385
|
}
|
|
374
|
-
|
|
386
|
+
} catch {
|
|
387
|
+
/* keep plain result */
|
|
388
|
+
}
|
|
389
|
+
return payload;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function displaySingleValidateResult(appOrFile, result, outFormat, validate) {
|
|
393
|
+
emitManifestAfterValidateIfApplicable(appOrFile, result, outFormat);
|
|
394
|
+
if (outFormat === 'json') {
|
|
395
|
+
logger.log(JSON.stringify(buildValidateJsonPayload(result, appOrFile), null, 2));
|
|
375
396
|
return;
|
|
376
397
|
}
|
|
398
|
+
validate.displayValidationResults(result);
|
|
399
|
+
}
|
|
377
400
|
|
|
401
|
+
async function runValidateCommand(appOrFile, options) {
|
|
402
|
+
const validate = require('../validation/validate');
|
|
403
|
+
const opts = options.opts ? options.opts() : options;
|
|
404
|
+
const { isProtectionScope } = require('../protection/scope');
|
|
405
|
+
if (isProtectionScope(appOrFile)) {
|
|
406
|
+
await runProtectionValidateBranch(appOrFile, opts);
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (await runValidateBatchBranch(validate, opts)) {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
const outFormat = (opts.format || 'default').toLowerCase();
|
|
378
413
|
if (!appOrFile || typeof appOrFile !== 'string') {
|
|
379
414
|
logger.log(chalk.red('App name or file path is required, or use --integration / --builder'));
|
|
380
415
|
process.exit(1);
|
|
381
416
|
}
|
|
382
|
-
|
|
383
417
|
const result = await validate.validateAppOrFile(appOrFile, {
|
|
384
418
|
...opts,
|
|
385
419
|
certSync: opts.certSync === true
|
|
386
420
|
});
|
|
387
|
-
|
|
388
|
-
logger.log(JSON.stringify(result, null, 2));
|
|
389
|
-
} else {
|
|
390
|
-
validate.displayValidationResults(result);
|
|
391
|
-
}
|
|
421
|
+
displaySingleValidateResult(appOrFile, result, outFormat, validate);
|
|
392
422
|
if (!result.valid) process.exit(1);
|
|
393
423
|
}
|
|
394
424
|
|
|
395
425
|
function setupValidateDiffCommands(program) {
|
|
396
|
-
program.command('validate [
|
|
397
|
-
.description('Validate
|
|
426
|
+
program.command('validate [appKey|systemKey|file]')
|
|
427
|
+
.description('Validate appKey, systemKey, file path, .protection batch, or all under integration/ and builder/')
|
|
398
428
|
.addHelpText('after', VALIDATE_HELP_AFTER)
|
|
399
429
|
.option('--format <format>', 'Output format: json | default (human-readable)')
|
|
400
430
|
.option('--integration', 'Validate all applications under integration/')
|
|
401
431
|
.option('--builder', 'Validate all applications under builder/')
|
|
432
|
+
.option('--warnings-as-errors', 'For .protection: pass strict=true to dataplane validate')
|
|
433
|
+
.option('--simulate', 'For .protection: run simulate after validate')
|
|
402
434
|
.option(
|
|
403
435
|
'--cert-sync',
|
|
404
436
|
'After successful validation of an external integration, refresh the system file certification block from the dataplane (requires login)'
|
package/lib/commands/app-logs.js
CHANGED
|
@@ -15,6 +15,7 @@ const containerHelpers = require('../utils/app-run-containers');
|
|
|
15
15
|
const { validateAppName } = require('../app/push');
|
|
16
16
|
|
|
17
17
|
const { execWithDockerEnv } = require('../utils/docker-exec');
|
|
18
|
+
const { maskEnvLine } = require('../utils/log-redaction');
|
|
18
19
|
|
|
19
20
|
/** Default number of log lines */
|
|
20
21
|
const DEFAULT_TAIL_LINES = 100;
|
|
@@ -43,36 +44,11 @@ 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.) */
|
|
44
45
|
const ERROR_WORD_FALLBACK_REGEX = /\berror\b/i;
|
|
45
46
|
|
|
46
|
-
/**
|
|
47
|
-
const
|
|
47
|
+
/** Keycloak / Java style: validation failure without the word "error" (would be dropped by `-l error` otherwise) */
|
|
48
|
+
const VALIDATION_FAILURE_LINE_REGEX = /\bvalidation\s+failed\b/i;
|
|
48
49
|
|
|
49
|
-
/**
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
/** URL with embedded credentials: scheme://user:password@host → scheme://user:***@host */
|
|
53
|
-
const URL_CREDENTIAL_PATTERN = /(\w+:\/\/)([^:@]*):([^@]+)@/g;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Masks a single env line if the key looks like a secret or value contains URL credentials
|
|
57
|
-
* @param {string} line - Line in form KEY=value
|
|
58
|
-
* @returns {string} Same line or KEY=*** or value with masked URL credentials
|
|
59
|
-
*/
|
|
60
|
-
function maskEnvLine(line) {
|
|
61
|
-
const eq = line.indexOf('=');
|
|
62
|
-
if (eq <= 0) return line;
|
|
63
|
-
const key = line.slice(0, eq);
|
|
64
|
-
const value = line.slice(eq + 1);
|
|
65
|
-
|
|
66
|
-
const keyForCheck = key.replace(KEY_PREFIXES_TO_STRIP, '');
|
|
67
|
-
const isSecretKey = SECRET_KEY_PATTERN.test(keyForCheck);
|
|
68
|
-
|
|
69
|
-
const maskedValue = value.replace(URL_CREDENTIAL_PATTERN, '$1$2:***@');
|
|
70
|
-
const hasUrlCredentials = maskedValue !== value;
|
|
71
|
-
|
|
72
|
-
if (isSecretKey) return `${key}=***`;
|
|
73
|
-
if (hasUrlCredentials) return `${key}=${maskedValue}`;
|
|
74
|
-
return line;
|
|
75
|
-
}
|
|
50
|
+
/** Other failure phrases treated as error severity for level filtering */
|
|
51
|
+
const FAILURE_PHRASE_AS_ERROR_REGEX = /\b(fatal|failed to run|unable to (start|run)|exception in thread)\b/i;
|
|
76
52
|
|
|
77
53
|
/** Normalize level string to canonical 'debug'|'info'|'warn'|'error'. */
|
|
78
54
|
function normalizeLevel(raw) {
|
|
@@ -104,9 +80,66 @@ function getLogLevel(line) {
|
|
|
104
80
|
const jsonNum = line.match(LEVEL_JSON_NUMERIC_REGEX);
|
|
105
81
|
if (jsonNum) return numericLevelToName(parseInt(jsonNum[1], 10));
|
|
106
82
|
if (ERROR_WORD_FALLBACK_REGEX.test(line)) return 'error';
|
|
83
|
+
if (VALIDATION_FAILURE_LINE_REGEX.test(line)) return 'error';
|
|
84
|
+
if (FAILURE_PHRASE_AS_ERROR_REGEX.test(line)) return 'error';
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Lines after an `error`-level line often have no level prefix (e.g. Keycloak config validation details). */
|
|
89
|
+
const ERROR_FILTER_CONTEXT_UNCLASSIFIED_MAX = 50;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Whether to show a log line when `--level` filtering is on. After an emitted error line, includes
|
|
93
|
+
* up to {@link ERROR_FILTER_CONTEXT_UNCLASSIFIED_MAX} following lines with no parseable level so
|
|
94
|
+
* stack traces and multi-line validation output are not dropped.
|
|
95
|
+
*
|
|
96
|
+
* @param {string} line
|
|
97
|
+
* @param {string|null} minLevel - normalized LOG_LEVELS value or null
|
|
98
|
+
* @param {{ pendingAfterError: number }} state - mutable; only used when minLevel is `error`
|
|
99
|
+
* @returns {boolean}
|
|
100
|
+
*/
|
|
101
|
+
/**
|
|
102
|
+
* @param {string|null} level
|
|
103
|
+
* @param {string} minLevel
|
|
104
|
+
* @param {{ pendingAfterError: number }} state
|
|
105
|
+
* @returns {boolean|null} true if line shown by sticky rule; null to continue
|
|
106
|
+
*/
|
|
107
|
+
function tryStickyErrorContinuation(level, minLevel, state) {
|
|
108
|
+
if (minLevel !== 'error' || state.pendingAfterError <= 0) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
if (level === null || level === undefined) {
|
|
112
|
+
state.pendingAfterError -= 1;
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
if (level !== 'error') {
|
|
116
|
+
state.pendingAfterError = 0;
|
|
117
|
+
}
|
|
107
118
|
return null;
|
|
108
119
|
}
|
|
109
120
|
|
|
121
|
+
function shouldShowFilteredLogLine(line, minLevel, state) {
|
|
122
|
+
if (minLevel === null || minLevel === undefined || minLevel === '' || !LOG_LEVELS.includes(minLevel)) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
if (line.trim() === '' && minLevel === 'error' && state.pendingAfterError > 0) {
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
const level = getLogLevel(line);
|
|
129
|
+
const sticky = tryStickyErrorContinuation(level, minLevel, state);
|
|
130
|
+
if (sticky === true) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (passesLevelFilter(level, minLevel)) {
|
|
135
|
+
if (minLevel === 'error' && level === 'error') {
|
|
136
|
+
state.pendingAfterError = ERROR_FILTER_CONTEXT_UNCLASSIFIED_MAX;
|
|
137
|
+
}
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
110
143
|
/**
|
|
111
144
|
* Whether a line's level passes the minimum level filter (show this level and above).
|
|
112
145
|
* @param {string|null} lineLevel - Level from getLogLevel (null treated as 'info')
|
|
@@ -143,7 +176,12 @@ async function dumpMaskedEnv(containerName) {
|
|
|
143
176
|
lines.forEach((line) => logger.log(maskEnvLine(line)));
|
|
144
177
|
logger.log(chalk.gray('\n--- Logs ---\n'));
|
|
145
178
|
} catch (err) {
|
|
146
|
-
logger.log(
|
|
179
|
+
logger.log(
|
|
180
|
+
chalk.gray(
|
|
181
|
+
'(Could not read container env — the container may be stopped, restarting, or not ready yet. ' +
|
|
182
|
+
'Docker log output below is still shown.)\n'
|
|
183
|
+
)
|
|
184
|
+
);
|
|
147
185
|
}
|
|
148
186
|
}
|
|
149
187
|
|
|
@@ -175,8 +213,9 @@ async function runDockerLogs(containerName, options) {
|
|
|
175
213
|
const proc = spawn('docker', args, { stdio: ['inherit', 'pipe', 'pipe'], env: dockerEnv });
|
|
176
214
|
proc.on('error', reject);
|
|
177
215
|
|
|
216
|
+
const filterState = { pendingAfterError: 0 };
|
|
178
217
|
function onLine(line) {
|
|
179
|
-
if (
|
|
218
|
+
if (shouldShowFilteredLogLine(line, minLevel, filterState)) {
|
|
180
219
|
process.stdout.write(line + '\n');
|
|
181
220
|
}
|
|
182
221
|
}
|
|
@@ -241,8 +280,11 @@ async function runDockerLogsFollow(containerName, tail, minLevel) {
|
|
|
241
280
|
logger.log(chalk.red(`Error: ${err.message}`));
|
|
242
281
|
process.exit(1);
|
|
243
282
|
});
|
|
283
|
+
const filterState = { pendingAfterError: 0 };
|
|
244
284
|
function onLine(line) {
|
|
245
|
-
if (
|
|
285
|
+
if (shouldShowFilteredLogLine(line, level, filterState)) {
|
|
286
|
+
process.stdout.write(line + '\n');
|
|
287
|
+
}
|
|
246
288
|
}
|
|
247
289
|
const rlOut = readline.createInterface({ input: proc.stdout, crlfDelay: Infinity });
|
|
248
290
|
rlOut.on('line', onLine);
|
|
@@ -301,4 +343,10 @@ async function runAppLogs(appKey, options = {}) {
|
|
|
301
343
|
}
|
|
302
344
|
}
|
|
303
345
|
|
|
304
|
-
module.exports = {
|
|
346
|
+
module.exports = {
|
|
347
|
+
runAppLogs,
|
|
348
|
+
maskEnvLine,
|
|
349
|
+
getLogLevel,
|
|
350
|
+
passesLevelFilter,
|
|
351
|
+
shouldShowFilteredLogLine
|
|
352
|
+
};
|