@aifabrix/builder 2.44.5 → 2.45.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/cli-layout.mdc +8 -4
- package/.cursor/rules/project-rules.mdc +1 -1
- package/README.md +15 -23
- package/integration/hubspot-test/README.md +2 -0
- package/integration/hubspot-test/test.js +5 -3
- package/jest.projects.js +104 -2
- package/lib/api/controller-health.api.js +49 -0
- package/lib/api/dimension-values.api.js +82 -0
- package/lib/api/dimensions.api.js +114 -0
- package/lib/api/external-systems.api.js +1 -0
- package/lib/api/integration-clients.api.js +168 -0
- package/lib/api/types/dimension-values.types.js +28 -0
- package/lib/api/types/dimensions.types.js +31 -0
- package/lib/api/types/integration-clients.types.js +45 -0
- package/lib/api/validation-runner.js +46 -25
- package/lib/app/deploy-config.js +11 -1
- package/lib/app/deploy-status-display.js +3 -3
- package/lib/app/deploy.js +36 -14
- package/lib/app/display.js +15 -11
- package/lib/app/helpers.js +3 -3
- package/lib/app/index.js +3 -3
- package/lib/app/push.js +46 -23
- package/lib/app/register.js +7 -6
- package/lib/app/restart-display.js +126 -0
- package/lib/app/rotate-secret.js +7 -6
- package/lib/app/run-container-start.js +12 -6
- package/lib/app/run-env-compose.js +30 -1
- package/lib/app/run-helpers.js +58 -19
- package/lib/app/run-reload-sync.js +148 -0
- package/lib/app/run-resolve-image.js +51 -1
- package/lib/app/run.js +148 -74
- package/lib/app/show-display.js +7 -0
- package/lib/app/show.js +87 -5
- package/lib/build/index.js +83 -49
- package/lib/cli/doctor-check.js +117 -0
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided.js +460 -0
- package/lib/cli/installation-log-command.js +73 -0
- package/lib/cli/setup-app.js +31 -3
- package/lib/cli/setup-auth.js +98 -27
- package/lib/cli/setup-dev-path-commands.js +50 -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 +132 -118
- package/lib/cli/setup-integration-client.js +182 -0
- package/lib/cli/setup-parameters.js +21 -2
- package/lib/cli/setup-platform.js +102 -0
- package/lib/cli/setup-secrets.js +18 -6
- package/lib/cli/setup-utility-resolve.js +132 -0
- package/lib/cli/setup-utility.js +143 -84
- package/lib/commands/app-logs.js +81 -33
- package/lib/commands/auth-config.js +116 -18
- package/lib/commands/datasource-capability-dimension-cli.js +128 -0
- package/lib/commands/datasource-capability-output.js +29 -0
- package/lib/commands/datasource-capability-relate-cli.js +140 -0
- package/lib/commands/datasource-capability.js +411 -0
- package/lib/commands/datasource-unified-test-cli.options.js +1 -1
- package/lib/commands/datasource.js +53 -13
- package/lib/commands/dev-down.js +3 -3
- package/lib/commands/dev-infra-gate.js +32 -0
- package/lib/commands/dev-init.js +13 -7
- package/lib/commands/dimension-value.js +179 -0
- package/lib/commands/dimension.js +330 -0
- package/lib/commands/integration-client.js +430 -0
- package/lib/commands/login-device.js +65 -30
- package/lib/commands/login.js +21 -10
- package/lib/commands/parameters-validate.js +78 -13
- package/lib/commands/repair-datasource-auto-rbac.js +166 -0
- package/lib/commands/repair-datasource-keys.js +10 -5
- package/lib/commands/repair-datasource.js +19 -7
- package/lib/commands/repair-env-template.js +4 -1
- package/lib/commands/repair-openapi-sync.js +172 -0
- package/lib/commands/repair-persist.js +102 -0
- package/lib/commands/repair-rbac-extract.js +27 -0
- package/lib/commands/repair-rbac-migrate.js +186 -0
- package/lib/commands/repair-rbac.js +214 -31
- package/lib/commands/repair-system-alignment.js +246 -0
- package/lib/commands/repair-system-permissions.js +168 -0
- package/lib/commands/repair.js +120 -338
- package/lib/commands/secure.js +1 -1
- package/lib/commands/setup-modes.js +468 -0
- package/lib/commands/setup-prompts.js +421 -0
- package/lib/commands/setup.js +254 -0
- package/lib/commands/teardown.js +277 -0
- package/lib/commands/up-common.js +113 -19
- package/lib/commands/up-dataplane.js +44 -19
- package/lib/commands/up-miso.js +18 -18
- package/lib/commands/upload.js +111 -23
- package/lib/commands/wizard-core-helpers.js +14 -11
- package/lib/commands/wizard-core.js +6 -5
- package/lib/commands/wizard-dataplane.js +2 -2
- package/lib/commands/wizard-entity-selection.js +4 -3
- package/lib/commands/wizard-headless.js +2 -1
- package/lib/commands/wizard.js +2 -1
- package/lib/constants/infra-compose-service-names.js +40 -0
- package/lib/core/audit-logger.js +1 -34
- package/lib/core/config-admin-email.js +56 -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/env-reader.js +16 -3
- package/lib/core/secrets-admin-env.js +101 -0
- package/lib/core/secrets-ensure-infra.js +34 -1
- package/lib/core/secrets-ensure.js +88 -66
- package/lib/core/secrets-env-content.js +428 -0
- package/lib/core/secrets-env-declarative-expand.js +170 -0
- package/lib/core/secrets-env-write.js +29 -1
- package/lib/core/secrets-load.js +252 -0
- package/lib/core/secrets-names.js +32 -0
- package/lib/core/secrets.js +17 -757
- package/lib/datasource/capability/basic-exposure.js +76 -0
- package/lib/datasource/capability/capability-diff-slice.js +41 -0
- package/lib/datasource/capability/capability-key.js +34 -0
- package/lib/datasource/capability/capability-resolve.js +172 -0
- package/lib/datasource/capability/capability-storage-keys.js +22 -0
- package/lib/datasource/capability/copy-operations.js +348 -0
- package/lib/datasource/capability/copy-test-payload.js +139 -0
- package/lib/datasource/capability/create-operations.js +235 -0
- package/lib/datasource/capability/dimension-operations.js +151 -0
- package/lib/datasource/capability/dimension-validate.js +219 -0
- package/lib/datasource/capability/json-pointer.js +31 -0
- package/lib/datasource/capability/reference-rewrite.js +51 -0
- package/lib/datasource/capability/relate-operations.js +325 -0
- package/lib/datasource/capability/relate-validate.js +219 -0
- package/lib/datasource/capability/remove-operations.js +275 -0
- package/lib/datasource/capability/run-capability-copy.js +152 -0
- package/lib/datasource/capability/run-capability-diff.js +135 -0
- package/lib/datasource/capability/run-capability-dimension.js +291 -0
- package/lib/datasource/capability/run-capability-edit.js +377 -0
- package/lib/datasource/capability/run-capability-relate.js +193 -0
- package/lib/datasource/capability/run-capability-remove.js +105 -0
- package/lib/datasource/capability/templates/minimal-fetch.json +18 -0
- package/lib/datasource/capability/validate-capability-slice.js +35 -0
- package/lib/datasource/list.js +136 -23
- package/lib/datasource/log-viewer.js +2 -4
- package/lib/datasource/unified-validation-run.js +51 -16
- package/lib/datasource/validate.js +53 -1
- package/lib/deployment/deploy-poll-ui.js +60 -0
- package/lib/deployment/deployer-status.js +29 -3
- package/lib/deployment/deployer.js +48 -30
- package/lib/deployment/environment.js +7 -2
- package/lib/deployment/poll-interval.js +72 -0
- package/lib/deployment/push.js +11 -9
- package/lib/external-system/deploy.js +9 -2
- package/lib/external-system/download.js +61 -32
- package/lib/external-system/sync-deploy-manifest.js +33 -0
- package/lib/infrastructure/index.js +49 -19
- package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
- package/lib/internal/node-fs.js +2 -0
- package/lib/parameters/infra-kv-discovery.js +29 -4
- package/lib/parameters/infra-parameter-catalog.js +6 -3
- package/lib/parameters/infra-parameter-validate.js +67 -19
- package/lib/resolvers/datasource-resolver.js +53 -0
- package/lib/resolvers/dimension-file.js +52 -0
- package/lib/resolvers/manifest-resolver.js +133 -0
- package/lib/schema/application-schema.json +4 -0
- package/lib/schema/external-datasource.schema.json +183 -53
- package/lib/schema/external-system.schema.json +23 -10
- package/lib/schema/infra.parameter.yaml +26 -1
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/aifabrix-config-dir-walk.js +40 -0
- package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
- package/lib/utils/app-config-resolver.js +24 -1
- package/lib/utils/app-run-containers.js +2 -2
- 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 +59 -0
- package/lib/utils/cli-secrets-error-format.js +78 -0
- package/lib/utils/cli-test-layout-chalk.js +31 -9
- package/lib/utils/cli-utils.js +4 -36
- 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/datasource-test-run-display.js +8 -0
- package/lib/utils/dev-hosts-helper.js +3 -2
- package/lib/utils/dev-init-ssh-merge.js +2 -1
- package/lib/utils/docker-build.js +17 -9
- package/lib/utils/docker-reload-mount.js +127 -0
- package/lib/utils/env-copy.js +99 -14
- package/lib/utils/env-template.js +5 -1
- package/lib/utils/external-readme.js +71 -2
- package/lib/utils/external-system-local-test-tty.js +3 -2
- package/lib/utils/external-system-readiness-core.js +45 -12
- package/lib/utils/external-system-readiness-deploy-display.js +3 -3
- package/lib/utils/external-system-readiness-display-internals.js +33 -3
- package/lib/utils/external-system-readiness-display.js +10 -1
- package/lib/utils/file-upload.js +40 -3
- package/lib/utils/health-check-db-init.js +107 -0
- package/lib/utils/health-check-public-warn.js +69 -0
- package/lib/utils/health-check-url.js +28 -10
- package/lib/utils/health-check.js +139 -107
- package/lib/utils/help-builder.js +5 -1
- package/lib/utils/image-name.js +34 -7
- 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/integration-file-backup.js +74 -0
- package/lib/utils/log-redaction.js +105 -0
- package/lib/utils/manifest-location.js +164 -0
- package/lib/utils/manifest-source-emit.js +162 -0
- package/lib/utils/mutagen-install.js +30 -3
- package/lib/utils/paths.js +308 -76
- package/lib/utils/postgres-wipe.js +212 -0
- package/lib/utils/register-aifabrix-shell-env.js +15 -0
- package/lib/utils/remote-dev-auth.js +21 -5
- package/lib/utils/remote-docker-env.js +9 -1
- package/lib/utils/remote-secrets-loader.js +49 -4
- package/lib/utils/resolve-docker-image-ref.js +9 -3
- package/lib/utils/run-cli-flags.js +29 -0
- package/lib/utils/secrets-ancestor-paths.js +47 -0
- package/lib/utils/secrets-canonical.js +10 -3
- package/lib/utils/secrets-helpers.js +17 -10
- package/lib/utils/secrets-kv-refs.js +42 -0
- package/lib/utils/secrets-kv-scope.js +19 -2
- package/lib/utils/secrets-materialize-local.js +134 -0
- package/lib/utils/secrets-path.js +26 -13
- package/lib/utils/secrets-utils.js +20 -10
- package/lib/utils/system-builder-root.js +42 -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 -388
- 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 +52 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
- package/lib/utils/urls-local-registry-scan.js +103 -0
- package/lib/utils/urls-local-registry.js +158 -76
- package/lib/utils/validation-poll-ui.js +81 -0
- package/lib/utils/validation-run-poll.js +29 -5
- package/lib/utils/with-muted-logger.js +53 -0
- package/package.json +3 -1
- package/templates/applications/dataplane/application.yaml +5 -1
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/env.template +8 -6
- package/templates/applications/miso-controller/application.yaml +9 -0
- package/templates/applications/miso-controller/env.template +27 -29
- package/templates/applications/miso-controller/rbac.yaml +9 -9
- package/templates/external-system/README.md.hbs +83 -123
- package/.npmrc.token +0 -1
- package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/index.json +0 -1
- package/lib/api/service-users.api.js +0 -150
- package/lib/api/types/service-users.types.js +0 -65
- package/lib/cli/setup-service-user.js +0 -187
- package/lib/commands/service-user.js +0 -429
package/lib/commands/upload.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
formatSuccessLine,
|
|
3
|
+
formatWarningLine,
|
|
4
|
+
formatProgress,
|
|
5
|
+
sectionTitle,
|
|
6
|
+
headerKeyValue,
|
|
7
|
+
metadata
|
|
8
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
2
9
|
/**
|
|
3
|
-
* Upload external system to dataplane (
|
|
10
|
+
* Upload external system to dataplane (sync local `integration/<systemKey>/openapi/*.json` when present,
|
|
11
|
+
* then single pipeline upload: validate → publish → controller register).
|
|
4
12
|
*
|
|
5
13
|
* @fileoverview Upload command handler for aifabrix upload <systemKey>
|
|
6
14
|
* @author AI Fabrix Team
|
|
@@ -10,6 +18,8 @@ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
|
10
18
|
const path = require('path');
|
|
11
19
|
const chalk = require('chalk');
|
|
12
20
|
const logger = require('../utils/logger');
|
|
21
|
+
|
|
22
|
+
const SEP = chalk.gray('────────────────────────────────────────');
|
|
13
23
|
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
14
24
|
const { getDeploymentAuth, requireBearerForDataplanePipeline } = require('../utils/token-manager');
|
|
15
25
|
const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
|
|
@@ -38,6 +48,7 @@ const {
|
|
|
38
48
|
} = require('../utils/external-system-readiness-display');
|
|
39
49
|
const { maybeSyncSystemCertificationFromDataplane } = require('../certification/sync-system-certification');
|
|
40
50
|
const { cliOptsSkipCertSync } = require('../certification/cli-cert-sync-skip');
|
|
51
|
+
const { maybeSyncOpenApiFilesForMcp } = require('./repair-openapi-sync');
|
|
41
52
|
|
|
42
53
|
/**
|
|
43
54
|
* Validates system-key format (same as download).
|
|
@@ -86,7 +97,7 @@ async function resolveDataplaneAndAuth(systemKey, opts = {}) {
|
|
|
86
97
|
}
|
|
87
98
|
|
|
88
99
|
if (!silent) {
|
|
89
|
-
logger.log(
|
|
100
|
+
logger.log(metadata('Resolving dataplane URL...'));
|
|
90
101
|
}
|
|
91
102
|
const dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig, { silent });
|
|
92
103
|
return { dataplaneUrl, authConfig, environment };
|
|
@@ -168,7 +179,9 @@ async function pushAndLogCredentialSecrets(dataplaneUrl, authConfig, systemKey,
|
|
|
168
179
|
});
|
|
169
180
|
if (pushResult.pushed > 0) {
|
|
170
181
|
const keyList = pushResult.keys?.length ? ` (${pushResult.keys.join(', ')})` : '';
|
|
171
|
-
logger.log(
|
|
182
|
+
logger.log(
|
|
183
|
+
formatSuccessLine(`Pushed ${pushResult.pushed} credential secret(s) to dataplane${keyList}.`)
|
|
184
|
+
);
|
|
172
185
|
} else {
|
|
173
186
|
logger.log(chalk.yellow('Secret push skipped'));
|
|
174
187
|
}
|
|
@@ -223,7 +236,10 @@ const UPLOAD_PROBE_TEST_DATA = {};
|
|
|
223
236
|
* @param {number|undefined} probeTimeoutMs
|
|
224
237
|
*/
|
|
225
238
|
async function maybeRunUploadProbe(dataplaneUrl, systemKey, authConfig, probeTimeoutMs) {
|
|
226
|
-
logger.log(
|
|
239
|
+
logger.log('');
|
|
240
|
+
logger.log(sectionTitle('Runtime checks (--probe)'));
|
|
241
|
+
logger.log(SEP);
|
|
242
|
+
logger.log(formatProgress('Running runtime checks...'));
|
|
227
243
|
const timeoutMs = probeTimeoutMs === undefined || probeTimeoutMs === null ? 120000 : probeTimeoutMs;
|
|
228
244
|
try {
|
|
229
245
|
const pr = await testSystemViaPipeline(
|
|
@@ -246,10 +262,13 @@ async function maybeRunUploadProbe(dataplaneUrl, systemKey, authConfig, probeTim
|
|
|
246
262
|
|
|
247
263
|
/**
|
|
248
264
|
* Local validation, manifest, payload, and configuration resolution.
|
|
265
|
+
* Does not write *-deploy.json; run `aifabrix json <systemKey>` to regenerate the deployment manifest from sources.
|
|
266
|
+
*
|
|
249
267
|
* @param {string} systemKey
|
|
268
|
+
* @param {{ dryRun?: boolean }} [_opts] - Reserved for callers (e.g. dry-run); manifest is always built from current disk files
|
|
250
269
|
* @returns {Promise<{ manifest: Object, payload: Object }>}
|
|
251
270
|
*/
|
|
252
|
-
async function buildValidatedUploadManifestPayload(systemKey) {
|
|
271
|
+
async function buildValidatedUploadManifestPayload(systemKey, _opts = {}) {
|
|
253
272
|
const validationResult = await validateExternalSystemComplete(systemKey, { type: 'external' });
|
|
254
273
|
throwIfValidationFailed(validationResult);
|
|
255
274
|
logger.log(formatSuccessLine('Local validation passed'));
|
|
@@ -260,24 +279,54 @@ async function buildValidatedUploadManifestPayload(systemKey) {
|
|
|
260
279
|
}
|
|
261
280
|
|
|
262
281
|
/**
|
|
263
|
-
* Upload
|
|
282
|
+
* Upload local `integration/<systemKey>/openapi/*.json` specs when present so dataplane can store
|
|
283
|
+
* vendor OpenAPI keyed by each datasource `openapi.documentKey` (same behavior as repair --api OpenAPI sync).
|
|
284
|
+
* Call this **after** a successful pipeline publish: list/upload endpoints require the external system row.
|
|
285
|
+
* First publish still succeeds because pipeline materializes internal specs from `openapi.operations` when needed.
|
|
286
|
+
*
|
|
264
287
|
* @param {string} systemKey
|
|
265
|
-
* @param {Object} options
|
|
266
288
|
* @param {Object} manifest
|
|
267
|
-
* @
|
|
289
|
+
* @returns {Promise<void>}
|
|
268
290
|
*/
|
|
269
|
-
async function
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
291
|
+
async function logAndSyncLocalOpenApiForUpload(systemKey, manifest) {
|
|
292
|
+
const appPath = getIntegrationPath(systemKey);
|
|
293
|
+
const ei = manifest && manifest.externalIntegration;
|
|
294
|
+
const datasourceFiles = ei && Array.isArray(ei.dataSources) ? ei.dataSources : [];
|
|
295
|
+
const openapiSyncLines = await maybeSyncOpenApiFilesForMcp({
|
|
296
|
+
enabled: true,
|
|
297
|
+
dryRun: false,
|
|
298
|
+
appPath,
|
|
299
|
+
systemKey,
|
|
300
|
+
datasourceFiles
|
|
301
|
+
});
|
|
302
|
+
for (const line of openapiSyncLines) {
|
|
303
|
+
logger.log(line.startsWith('Skipped') ? formatWarningLine(line) : formatSuccessLine(line));
|
|
278
304
|
}
|
|
279
|
-
|
|
280
|
-
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* @param {Object} ctx
|
|
309
|
+
* @param {string} ctx.systemKey
|
|
310
|
+
* @param {Object} ctx.options
|
|
311
|
+
* @param {Object} ctx.manifest
|
|
312
|
+
* @param {Object} ctx.payload
|
|
313
|
+
* @param {string} ctx.environment
|
|
314
|
+
* @param {string} ctx.dataplaneUrl
|
|
315
|
+
* @param {Object} ctx.authConfig
|
|
316
|
+
* @param {Object} ctx.rawRes
|
|
317
|
+
* @returns {Promise<void>}
|
|
318
|
+
*/
|
|
319
|
+
async function handlePublicationAndFollowups(ctx) {
|
|
320
|
+
const {
|
|
321
|
+
systemKey,
|
|
322
|
+
options,
|
|
323
|
+
manifest,
|
|
324
|
+
payload,
|
|
325
|
+
environment,
|
|
326
|
+
dataplaneUrl,
|
|
327
|
+
authConfig,
|
|
328
|
+
rawRes
|
|
329
|
+
} = ctx;
|
|
281
330
|
const publication = unwrapPublicationResult(rawRes);
|
|
282
331
|
if (!publication) {
|
|
283
332
|
throw new Error(
|
|
@@ -296,7 +345,6 @@ async function runUploadPublishAndSummary(systemKey, options, manifest, payload)
|
|
|
296
345
|
if (options.probe) {
|
|
297
346
|
await maybeRunUploadProbe(dataplaneUrl, systemKey, authConfig, options.probeTimeout);
|
|
298
347
|
}
|
|
299
|
-
|
|
300
348
|
const dsKeys = (payload.dataSources || []).map((ds) => ds && ds.key).filter(Boolean);
|
|
301
349
|
await maybeSyncSystemCertificationFromDataplane({
|
|
302
350
|
label: 'upload',
|
|
@@ -308,6 +356,41 @@ async function runUploadPublishAndSummary(systemKey, options, manifest, payload)
|
|
|
308
356
|
});
|
|
309
357
|
}
|
|
310
358
|
|
|
359
|
+
/**
|
|
360
|
+
* Upload path after dry-run check.
|
|
361
|
+
* @param {string} systemKey
|
|
362
|
+
* @param {Object} options
|
|
363
|
+
* @param {Object} manifest
|
|
364
|
+
* @param {Object} payload
|
|
365
|
+
*/
|
|
366
|
+
async function runUploadPublishAndSummary(systemKey, options, manifest, payload) {
|
|
367
|
+
const { dataplaneUrl, authConfig, environment } = await resolveDataplaneAndAuth(systemKey);
|
|
368
|
+
requireBearerForDataplanePipeline(authConfig);
|
|
369
|
+
logger.log('');
|
|
370
|
+
logger.log(sectionTitle('Target'));
|
|
371
|
+
logger.log(SEP);
|
|
372
|
+
logger.log(headerKeyValue('Environment:', environment));
|
|
373
|
+
logger.log(headerKeyValue('Dataplane:', dataplaneUrl));
|
|
374
|
+
logDataplanePipelineWarning();
|
|
375
|
+
if (options.verbose) {
|
|
376
|
+
await maybeRunVerboseServerValidation(dataplaneUrl, authConfig, payload);
|
|
377
|
+
}
|
|
378
|
+
await pushAndLogCredentialSecrets(dataplaneUrl, authConfig, systemKey, payload);
|
|
379
|
+
|
|
380
|
+
const rawRes = await runUploadValidatePublish(dataplaneUrl, authConfig, payload);
|
|
381
|
+
await logAndSyncLocalOpenApiForUpload(systemKey, manifest);
|
|
382
|
+
await handlePublicationAndFollowups({
|
|
383
|
+
systemKey,
|
|
384
|
+
options,
|
|
385
|
+
manifest,
|
|
386
|
+
payload,
|
|
387
|
+
environment,
|
|
388
|
+
dataplaneUrl,
|
|
389
|
+
authConfig,
|
|
390
|
+
rawRes
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
311
394
|
/**
|
|
312
395
|
* Uploads external system: publishes to dataplane and registers with controller (draft).
|
|
313
396
|
* @param {string} systemKey - External system key (integration/<systemKey>/)
|
|
@@ -322,8 +405,13 @@ async function runUploadPublishAndSummary(systemKey, options, manifest, payload)
|
|
|
322
405
|
*/
|
|
323
406
|
async function uploadExternalSystem(systemKey, options = {}) {
|
|
324
407
|
validateSystemKeyFormat(systemKey);
|
|
325
|
-
logger.log(
|
|
326
|
-
|
|
408
|
+
logger.log('');
|
|
409
|
+
logger.log(sectionTitle('Upload external system'));
|
|
410
|
+
logger.log(SEP);
|
|
411
|
+
logger.log(headerKeyValue('System:', systemKey));
|
|
412
|
+
const { manifest, payload } = await buildValidatedUploadManifestPayload(systemKey, {
|
|
413
|
+
dryRun: !!options.dryRun
|
|
414
|
+
});
|
|
327
415
|
if (options.dryRun) {
|
|
328
416
|
logger.log(chalk.yellow('Dry run: would upload payload (no API calls).'));
|
|
329
417
|
logger.log(
|
|
@@ -15,6 +15,7 @@ const { getIntegrationPath } = require('../utils/paths');
|
|
|
15
15
|
const { parseOpenApi, testMcpConnection, credentialSelection } = require('../api/wizard.api');
|
|
16
16
|
const { listCredentials } = require('../api/credentials.api');
|
|
17
17
|
const { listExternalSystems, getExternalSystem } = require('../api/external-systems.api');
|
|
18
|
+
const { formatSuccessLine } = require('../utils/cli-layout-chalk');
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Parse OpenAPI file or URL
|
|
@@ -35,7 +36,7 @@ async function parseOpenApiSource(dataplaneUrl, authConfig, sourceType, sourceDa
|
|
|
35
36
|
if (!parseResponse.success) {
|
|
36
37
|
throw new Error(`OpenAPI parsing failed: ${parseResponse.error || parseResponse.formattedError}`);
|
|
37
38
|
}
|
|
38
|
-
logger.log(
|
|
39
|
+
logger.log(formatSuccessLine(`OpenAPI ${isUrl ? 'URL' : 'file'} parsed successfully`));
|
|
39
40
|
return parseResponse.data?.spec;
|
|
40
41
|
} catch (error) {
|
|
41
42
|
spinner.stop();
|
|
@@ -61,7 +62,7 @@ async function testMcpServerConnection(dataplaneUrl, authConfig, sourceData) {
|
|
|
61
62
|
if (!testResponse.success || !testResponse.data?.connected) {
|
|
62
63
|
throw new Error(`MCP connection failed: ${testResponse.data?.error || 'Unable to connect'}`);
|
|
63
64
|
}
|
|
64
|
-
logger.log(
|
|
65
|
+
logger.log(formatSuccessLine('MCP server connection successful'));
|
|
65
66
|
} catch (error) {
|
|
66
67
|
spinner.stop();
|
|
67
68
|
throw error;
|
|
@@ -145,7 +146,7 @@ async function runCredentialSelectionLoop(dataplaneUrl, authConfig, selectionDat
|
|
|
145
146
|
}
|
|
146
147
|
if (response.success) {
|
|
147
148
|
const actionText = selectionData.action === 'create' ? 'created' : 'selected';
|
|
148
|
-
logger.log(
|
|
149
|
+
logger.log(formatSuccessLine(`Credential ${actionText}`));
|
|
149
150
|
return response.data?.credentialIdOrKey || null;
|
|
150
151
|
}
|
|
151
152
|
const errorMsg = response.error || response.formattedError || response.message || 'Unknown error';
|
|
@@ -308,18 +309,19 @@ function throwConfigGenerationError(generateResponse, options = {}) {
|
|
|
308
309
|
}
|
|
309
310
|
|
|
310
311
|
/**
|
|
311
|
-
* Write debug log to integration/<systemKey>/debug.log
|
|
312
|
+
* Write debug log to integration/<systemKey>/logs/debug.log
|
|
312
313
|
* @async
|
|
313
314
|
* @param {string} appName - Application name
|
|
314
315
|
* @param {string} content - Debug log content
|
|
315
316
|
*/
|
|
316
317
|
async function writeDebugLog(appName, content) {
|
|
317
318
|
try {
|
|
318
|
-
const
|
|
319
|
+
const appDir = getIntegrationPath(appName);
|
|
320
|
+
const dir = path.join(appDir, 'logs');
|
|
319
321
|
await fs.mkdir(dir, { recursive: true });
|
|
320
322
|
const debugPath = path.join(dir, 'debug.log');
|
|
321
323
|
await fs.writeFile(debugPath, content, 'utf8');
|
|
322
|
-
logger.log(chalk.gray(` Debug log saved to integration/${appName}/debug.log`));
|
|
324
|
+
logger.log(chalk.gray(` Debug log saved to integration/${appName}/logs/debug.log`));
|
|
323
325
|
} catch (e) {
|
|
324
326
|
logger.warn(`Could not save debug.log: ${e.message}`);
|
|
325
327
|
}
|
|
@@ -336,13 +338,14 @@ async function writeDebugLog(appName, content) {
|
|
|
336
338
|
async function writeDebugManifest(appName, systemConfig, datasourceConfig) {
|
|
337
339
|
const saved = [];
|
|
338
340
|
try {
|
|
339
|
-
const
|
|
341
|
+
const appDir = getIntegrationPath(appName);
|
|
342
|
+
const dir = path.join(appDir, 'logs');
|
|
340
343
|
await fs.mkdir(dir, { recursive: true });
|
|
341
344
|
if (systemConfig && typeof systemConfig === 'object') {
|
|
342
345
|
const systemPath = path.join(dir, 'debug-system.yaml');
|
|
343
346
|
await fs.writeFile(systemPath, yaml.dump(systemConfig, { lineWidth: -1 }), 'utf8');
|
|
344
347
|
saved.push('debug-system.yaml');
|
|
345
|
-
logger.log(chalk.gray(` Debug manifest saved to integration/${appName}/debug-system.yaml`));
|
|
348
|
+
logger.log(chalk.gray(` Debug manifest saved to integration/${appName}/logs/debug-system.yaml`));
|
|
346
349
|
}
|
|
347
350
|
if (datasourceConfig !== undefined && datasourceConfig !== null) {
|
|
348
351
|
const configs = Array.isArray(datasourceConfig) ? datasourceConfig : [datasourceConfig];
|
|
@@ -351,7 +354,7 @@ async function writeDebugManifest(appName, systemConfig, datasourceConfig) {
|
|
|
351
354
|
const toWrite = configs.length === 1 ? configs[0] : configs;
|
|
352
355
|
await fs.writeFile(datasourcePath, yaml.dump(toWrite, { lineWidth: -1 }), 'utf8');
|
|
353
356
|
saved.push('debug-datasource.yaml');
|
|
354
|
-
logger.log(chalk.gray(` Debug manifest saved to integration/${appName}/debug-datasource.yaml`));
|
|
357
|
+
logger.log(chalk.gray(` Debug manifest saved to integration/${appName}/logs/debug-datasource.yaml`));
|
|
355
358
|
}
|
|
356
359
|
}
|
|
357
360
|
} catch (e) {
|
|
@@ -381,7 +384,7 @@ async function saveDebugManifestOnErrorAndThrow(generateResponse, opts) {
|
|
|
381
384
|
const savedManifest = await writeDebugManifest(appName, systemConfig, datasourceConfig);
|
|
382
385
|
if (debugLog || savedManifest.length > 0) {
|
|
383
386
|
const files = [debugLog && 'debug.log', ...savedManifest].filter(Boolean).join(', ');
|
|
384
|
-
debugManifestHint = `Debug manifest saved to integration/${appName}/ (${files}). Review the log and fix the manifest manually, then run: aifabrix wizard ${appName}`;
|
|
387
|
+
debugManifestHint = `Debug manifest saved to integration/${appName}/logs/ (${files}). Review the log and fix the manifest manually, then run: aifabrix wizard ${appName}`;
|
|
385
388
|
}
|
|
386
389
|
}
|
|
387
390
|
throwConfigGenerationError(generateResponse, { debugManifestHint });
|
|
@@ -398,7 +401,7 @@ async function throwValidationFailureWithDebug(validateResponse, systemConfig, c
|
|
|
398
401
|
);
|
|
399
402
|
if (!debugLog && savedManifest.length === 0) throw new Error(`Configuration validation failed: ${errorMsg}`);
|
|
400
403
|
const files = [debugLog && 'debug.log', ...savedManifest].filter(Boolean).join(', ');
|
|
401
|
-
throw new Error(`Configuration validation failed: ${errorMsg}\n\nDebug manifest saved to integration/${options.appName}/ (${files}). Review the log and fix the manifest manually, then run: aifabrix wizard ${options.appName}`);
|
|
404
|
+
throw new Error(`Configuration validation failed: ${errorMsg}\n\nDebug manifest saved to integration/${options.appName}/logs/ (${files}). Review the log and fix the manifest manually, then run: aifabrix wizard ${options.appName}`);
|
|
402
405
|
}
|
|
403
406
|
|
|
404
407
|
/**
|
|
@@ -13,6 +13,7 @@ const logger = require('../utils/logger');
|
|
|
13
13
|
const { getDeploymentAuth, requireBearerForDataplanePipeline } = require('../utils/token-manager');
|
|
14
14
|
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
15
15
|
const { normalizeWizardConfigs } = require('./wizard-config-normalizer');
|
|
16
|
+
const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-layout-chalk');
|
|
16
17
|
const {
|
|
17
18
|
createWizardSession,
|
|
18
19
|
updateWizardSession,
|
|
@@ -143,7 +144,7 @@ async function handleModeSelection(dataplaneUrl, authConfig, configMode = null,
|
|
|
143
144
|
throw new Error(fullMsg);
|
|
144
145
|
}
|
|
145
146
|
const sessionId = extractSessionId(sessionResponse.data);
|
|
146
|
-
logger.log(
|
|
147
|
+
logger.log(formatSuccessLine(`Session created: ${sessionId}`));
|
|
147
148
|
return { mode, sessionId };
|
|
148
149
|
}
|
|
149
150
|
|
|
@@ -250,7 +251,7 @@ async function handleTypeDetection(dataplaneUrl, authConfig, openapiSpec) {
|
|
|
250
251
|
if (detectResponse.success && detectResponse.data) {
|
|
251
252
|
const detectedType = detectResponse.data;
|
|
252
253
|
const recommendedType = detectedType.recommendedType || detectedType.apiType || 'unknown';
|
|
253
|
-
logger.log(
|
|
254
|
+
logger.log(formatSuccessLine(`API type detected: ${recommendedType}`));
|
|
254
255
|
return detectedType;
|
|
255
256
|
}
|
|
256
257
|
} catch (error) {
|
|
@@ -329,7 +330,7 @@ async function handleConfigurationGeneration(dataplaneUrl, authConfig, options)
|
|
|
329
330
|
await writeDebugLog(options.appName, debugLog);
|
|
330
331
|
}
|
|
331
332
|
}
|
|
332
|
-
logger.log(
|
|
333
|
+
logger.log(formatSuccessLine('Configuration generated successfully'));
|
|
333
334
|
return { systemConfig: normalized.systemConfig, datasourceConfigs: normalized.datasourceConfigs, systemKey: result.systemKey };
|
|
334
335
|
} catch (error) {
|
|
335
336
|
spinner.stop();
|
|
@@ -368,7 +369,7 @@ async function validateWizardConfiguration(dataplaneUrl, authConfig, systemConfi
|
|
|
368
369
|
if (validateResponse.data?.warnings?.length > 0) warnings.push(...validateResponse.data.warnings);
|
|
369
370
|
}
|
|
370
371
|
spinner.stop();
|
|
371
|
-
logger.log(
|
|
372
|
+
logger.log(formatSuccessLine('Configuration validated successfully'));
|
|
372
373
|
if (warnings.length > 0) logger.log(chalk.yellow('\n\u26A0 Warnings:\n' + warnings.map(w => ` - ${w.message || w}`).join('\n')));
|
|
373
374
|
} catch (error) {
|
|
374
375
|
spinner.stop();
|
|
@@ -423,7 +424,7 @@ async function tryUpdateReadmeFromDeploymentDocs(appPath, appName, dataplaneUrl,
|
|
|
423
424
|
* @param {string} format - Project format: yaml | json
|
|
424
425
|
*/
|
|
425
426
|
function logWizardFileSaveFooter(appName, generatedFiles) {
|
|
426
|
-
logger.log(
|
|
427
|
+
logger.log(formatSuccessParagraph('Wizard completed successfully!'));
|
|
427
428
|
logger.log(chalk.green(`\nFiles created in: ${generatedFiles.appPath}`));
|
|
428
429
|
logger.log(chalk.blue('\nNext steps:'));
|
|
429
430
|
logger.log(chalk.gray(` 1. Review the generated files in integration/${appName}/`));
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
const chalk = require('chalk');
|
|
8
8
|
const logger = require('../utils/logger');
|
|
9
|
-
const {
|
|
9
|
+
const { formatProgress, formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
10
10
|
const { getDataplaneUrl } = require('../datasource/deploy');
|
|
11
11
|
const { listEnvironmentApplications } = require('../api/environments.api');
|
|
12
12
|
|
|
@@ -102,7 +102,7 @@ async function tryFallbackDataplaneUrl(controllerUrl, environment, authConfig, s
|
|
|
102
102
|
async function discoverDataplaneUrl(controllerUrl, environment, authConfig, opts = {}) {
|
|
103
103
|
const silent = opts.silent === true;
|
|
104
104
|
if (!silent) {
|
|
105
|
-
logger.log(
|
|
105
|
+
logger.log(formatProgress('Getting dataplane URL from controller...'));
|
|
106
106
|
}
|
|
107
107
|
try {
|
|
108
108
|
const dataplaneAppKey = await findDataplaneServiceAppKey(controllerUrl, environment, authConfig);
|
|
@@ -9,6 +9,7 @@ const logger = require('../utils/logger');
|
|
|
9
9
|
const { discoverEntities } = require('../api/wizard.api');
|
|
10
10
|
const { validateEntityNameForOpenApi } = require('../validation/wizard-datasource-validation');
|
|
11
11
|
const { promptForEntitySelection } = require('../generator/wizard-prompts');
|
|
12
|
+
const { formatSuccessLine } = require('../utils/cli-layout-chalk');
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* If wizard.yaml entity name matches discover-entities list, use it; else warn.
|
|
@@ -22,7 +23,7 @@ function resolvePrefillEntityName(trimmed, entities) {
|
|
|
22
23
|
logger.log(chalk.gray(
|
|
23
24
|
`Using entity from wizard.yaml (${trimmed}). Skipping entity prompts.`
|
|
24
25
|
));
|
|
25
|
-
logger.log(
|
|
26
|
+
logger.log(formatSuccessLine(`Selected entity: ${trimmed}`));
|
|
26
27
|
return trimmed;
|
|
27
28
|
}
|
|
28
29
|
logger.log(chalk.yellow(
|
|
@@ -42,7 +43,7 @@ async function promptForValidatedEntity(entities) {
|
|
|
42
43
|
if (!validation.valid) {
|
|
43
44
|
throw new Error(`Invalid entity '${entityName}'. Available: ${entities.map(e => e.name).join(', ')}`);
|
|
44
45
|
}
|
|
45
|
-
logger.log(
|
|
46
|
+
logger.log(formatSuccessLine(`Selected entity: ${entityName}`));
|
|
46
47
|
return entityName;
|
|
47
48
|
}
|
|
48
49
|
|
|
@@ -63,7 +64,7 @@ async function discoverAndSelectEntity(dataplaneUrl, authConfig, openapiSpec, pr
|
|
|
63
64
|
|
|
64
65
|
if (entities.length === 1) {
|
|
65
66
|
const only = entities[0].name;
|
|
66
|
-
logger.log(
|
|
67
|
+
logger.log(formatSuccessLine(`Only one entity discovered; using: ${only}`));
|
|
67
68
|
return only;
|
|
68
69
|
}
|
|
69
70
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
const chalk = require('chalk');
|
|
8
8
|
const logger = require('../utils/logger');
|
|
9
|
+
const { formatSuccessLine } = require('../utils/cli-layout-chalk');
|
|
9
10
|
const {
|
|
10
11
|
validateWizardConfig: validateWizardConfigFile,
|
|
11
12
|
displayValidationResults
|
|
@@ -141,7 +142,7 @@ async function handleWizardHeadless(options) {
|
|
|
141
142
|
displayValidationResults(validationResult);
|
|
142
143
|
throw new Error('Wizard configuration validation failed');
|
|
143
144
|
}
|
|
144
|
-
logger.log(
|
|
145
|
+
logger.log(formatSuccessLine('Configuration file validated'));
|
|
145
146
|
|
|
146
147
|
const wizardConfig = validationResult.config;
|
|
147
148
|
const appName = wizardConfig.appName;
|
package/lib/commands/wizard.js
CHANGED
|
@@ -50,6 +50,7 @@ const {
|
|
|
50
50
|
ensureIntegrationDir
|
|
51
51
|
} = require('./wizard-helpers');
|
|
52
52
|
const { humanizeAppKey } = require('../generator/wizard-prompts-secondary');
|
|
53
|
+
const { formatSuccessLine } = require('../utils/cli-layout-chalk');
|
|
53
54
|
|
|
54
55
|
/**
|
|
55
56
|
* Map resolved source type/data onto wizard state.source.
|
|
@@ -455,7 +456,7 @@ async function executeWizardFlow(appKey, dataplaneUrl, authConfig, flowOpts = {}
|
|
|
455
456
|
}
|
|
456
457
|
logger.log(chalk.blue('\n\uD83D\uDCCB Step 1: Create Session'));
|
|
457
458
|
const sessionId = await createSessionFromParams(dataplaneUrl, authConfig, mode, systemIdOrKey, appKey);
|
|
458
|
-
logger.log(
|
|
459
|
+
logger.log(formatSuccessLine('Session created'));
|
|
459
460
|
|
|
460
461
|
const platforms = mode === 'add-datasource' ? [] : await getWizardPlatforms(dataplaneUrl, authConfig);
|
|
461
462
|
const state = await runWizardStepsAfterSession(appKey, dataplaneUrl, authConfig, sessionId, {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infra compose service names accepted by `aifabrix restart` and {@link restartService}.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Single source of truth for CLI help + validation
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/** @type {ReadonlyArray<{ name: string, description: string }>} */
|
|
12
|
+
const RESTARTABLE_INFRA_SERVICES = Object.freeze([
|
|
13
|
+
{ name: 'postgres', description: 'PostgreSQL database' },
|
|
14
|
+
{ name: 'redis', description: 'Redis' },
|
|
15
|
+
{ name: 'pgadmin', description: 'pgAdmin 4 web UI (only if enabled when you ran up-infra)' },
|
|
16
|
+
{ name: 'redis-commander', description: 'Redis Commander web UI (only if enabled when you ran up-infra)' },
|
|
17
|
+
{ name: 'traefik', description: 'Traefik reverse proxy (only if enabled when you ran up-infra)' }
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @returns {string[]} service names in compose order
|
|
22
|
+
*/
|
|
23
|
+
function getRestartableInfraServiceNames() {
|
|
24
|
+
return RESTARTABLE_INFRA_SERVICES.map((s) => s.name);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Aligned lines for Commander `addHelpText('after', …)`.
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
function buildRestartInfraHelpLines() {
|
|
32
|
+
const col = 22;
|
|
33
|
+
return RESTARTABLE_INFRA_SERVICES.map((s) => ` ${s.name.padEnd(col)}${s.description}`).join('\n');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
37
|
+
RESTARTABLE_INFRA_SERVICES,
|
|
38
|
+
getRestartableInfraServiceNames,
|
|
39
|
+
buildRestartInfraHelpLines
|
|
40
|
+
};
|
package/lib/core/audit-logger.js
CHANGED
|
@@ -15,6 +15,7 @@ const fs = require('fs').promises;
|
|
|
15
15
|
const path = require('path');
|
|
16
16
|
const os = require('os');
|
|
17
17
|
const paths = require('../utils/paths');
|
|
18
|
+
const { maskSensitiveData } = require('../utils/log-redaction');
|
|
18
19
|
|
|
19
20
|
// Audit log file path (beside config.yaml / CLI system dir for compliance)
|
|
20
21
|
let auditLogPath = null;
|
|
@@ -51,40 +52,6 @@ async function getAuditLogPath() {
|
|
|
51
52
|
return auditLogPath;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
/**
|
|
55
|
-
* Masks sensitive data in strings
|
|
56
|
-
* Prevents secrets, keys, and passwords from appearing in logs
|
|
57
|
-
*
|
|
58
|
-
* @param {string} value - Value to mask
|
|
59
|
-
* @returns {string} Masked value
|
|
60
|
-
*/
|
|
61
|
-
function maskSensitiveData(value) {
|
|
62
|
-
if (!value || typeof value !== 'string') {
|
|
63
|
-
return value;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Mask patterns: passwords, secrets, keys, tokens
|
|
67
|
-
const sensitivePatterns = [
|
|
68
|
-
{ pattern: /password[=:]\s*([^\s]+)/gi, replacement: 'password=***' },
|
|
69
|
-
{ pattern: /secret[=:]\s*([^\s]+)/gi, replacement: 'secret=***' },
|
|
70
|
-
{ pattern: /key[=:]\s*([^\s]+)/gi, replacement: 'key=***' },
|
|
71
|
-
{ pattern: /token[=:]\s*([^\s]+)/gi, replacement: 'token=***' },
|
|
72
|
-
{ pattern: /api[_-]?key[=:]\s*([^\s]+)/gi, replacement: 'api_key=***' }
|
|
73
|
-
];
|
|
74
|
-
|
|
75
|
-
let masked = value;
|
|
76
|
-
for (const { pattern, replacement } of sensitivePatterns) {
|
|
77
|
-
masked = masked.replace(pattern, replacement);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// If value looks like a hash/key (long hex string), mask it
|
|
81
|
-
if (/^[a-f0-9]{32,}$/i.test(masked.trim())) {
|
|
82
|
-
return '***';
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return masked;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
55
|
/**
|
|
89
56
|
* Creates an audit log entry with ISO 27001 compliance
|
|
90
57
|
*
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin email field in `config.yaml` (Keycloak / pgAdmin), written by `aifabrix setup`.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview config.yaml adminEmail helpers (keeps config.js under max-lines)
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {unknown} email
|
|
13
|
+
* @returns {string} Trimmed valid email
|
|
14
|
+
* @throws {Error} When empty or not a plausible email
|
|
15
|
+
*/
|
|
16
|
+
function validateAdminEmailForConfig(email) {
|
|
17
|
+
const value = String(email ?? '').trim();
|
|
18
|
+
if (!value) {
|
|
19
|
+
throw new Error('Admin email must be a non-empty string');
|
|
20
|
+
}
|
|
21
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
|
|
22
|
+
throw new Error('Admin email must be a valid email address');
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {() => Promise<Object>} getConfig
|
|
29
|
+
* @returns {Promise<string>}
|
|
30
|
+
*/
|
|
31
|
+
async function getAdminEmailFromConfig(getConfig) {
|
|
32
|
+
const cfg = await getConfig();
|
|
33
|
+
if (cfg && typeof cfg.adminEmail === 'string' && cfg.adminEmail.trim()) {
|
|
34
|
+
return cfg.adminEmail.trim();
|
|
35
|
+
}
|
|
36
|
+
return '';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {() => Promise<Object>} getConfig
|
|
41
|
+
* @param {(data: Object) => Promise<void>} saveConfig
|
|
42
|
+
* @param {string} email
|
|
43
|
+
* @returns {Promise<void>}
|
|
44
|
+
*/
|
|
45
|
+
async function setAdminEmailInConfig(getConfig, saveConfig, email) {
|
|
46
|
+
const trimmed = validateAdminEmailForConfig(email);
|
|
47
|
+
const cfg = await getConfig();
|
|
48
|
+
cfg.adminEmail = trimmed;
|
|
49
|
+
await saveConfig(cfg);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = {
|
|
53
|
+
validateAdminEmailForConfig,
|
|
54
|
+
getAdminEmailFromConfig,
|
|
55
|
+
setAdminEmailInConfig
|
|
56
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL and developer-id normalization for runtime config.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Shared normalizers used by lib/core/config.js
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Normalize controller URL for consistent storage and lookup
|
|
13
|
+
* @param {string} url - Controller URL to normalize
|
|
14
|
+
* @returns {string|undefined|null} Normalized URL or original falsy
|
|
15
|
+
*/
|
|
16
|
+
function normalizeControllerUrl(url) {
|
|
17
|
+
if (!url || typeof url !== 'string') {
|
|
18
|
+
return url;
|
|
19
|
+
}
|
|
20
|
+
let normalized = url.trim().replace(/\/+$/, '');
|
|
21
|
+
if (!normalized.match(/^https?:\/\//)) {
|
|
22
|
+
normalized = `http://${normalized}`;
|
|
23
|
+
}
|
|
24
|
+
return normalized;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Validate and normalize developer ID
|
|
29
|
+
* @param {*} developerId - Developer ID value (can be string, number, undefined, or null)
|
|
30
|
+
* @returns {string} Normalized developer ID as string
|
|
31
|
+
* @throws {Error} If developer ID is invalid
|
|
32
|
+
*/
|
|
33
|
+
function validateAndNormalizeDeveloperId(developerId) {
|
|
34
|
+
const DEV_ID_DIGITS_REGEX = /^[0-9]+$/;
|
|
35
|
+
|
|
36
|
+
if (typeof developerId === 'undefined' || developerId === null) {
|
|
37
|
+
return '0';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (typeof developerId === 'number') {
|
|
41
|
+
if (developerId < 0 || !Number.isFinite(developerId)) {
|
|
42
|
+
throw new Error('Developer ID must be a non-negative digit string or number');
|
|
43
|
+
}
|
|
44
|
+
return String(developerId);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (typeof developerId === 'string') {
|
|
48
|
+
if (!DEV_ID_DIGITS_REGEX.test(developerId)) {
|
|
49
|
+
throw new Error('Developer ID must be a non-negative digit string or number');
|
|
50
|
+
}
|
|
51
|
+
return developerId;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
throw new Error('Developer ID must be a non-negative digit string or number');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = {
|
|
58
|
+
normalizeControllerUrl,
|
|
59
|
+
validateAndNormalizeDeveloperId
|
|
60
|
+
};
|