@aifabrix/builder 2.45.5 → 2.47.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 +2 -0
- package/.cursor/rules/cli-product-neutral.mdc +43 -0
- package/.cursor/rules/project-rules.mdc +1 -0
- package/.npmrc.token +1 -0
- package/.temp/jest-protection-convert/run-IFN2nv/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-IvmKLO/.protection/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-convert/run-SdvblF/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-b51TsW/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-fiLmb1/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-qYGHMU/.protection/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-Mq5re8/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-f3u4YL/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-iQtd1O/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-mdmz5M/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-qbKYxQ/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-yzlJy6/hubspot-companies.yaml +17 -0
- package/.temp/validation/summary.txt +5 -0
- package/README.md +43 -17
- 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/jest.config.default.js +2 -1
- package/jest.config.isolated.js +2 -1
- package/jest.global-live-fabrix-hooks.js +36 -0
- package/jest.isolated-projects.js +146 -0
- package/jest.projects.js +65 -136
- package/lib/api/api-response-errors.js +124 -0
- package/lib/api/dataplane-health.api.js +19 -7
- package/lib/api/governance-scenario-pack.api.js +159 -0
- package/lib/api/index.js +43 -3
- package/lib/api/lifecycle.api.js +98 -0
- package/lib/api/pipeline.api.js +3 -1
- package/lib/api/records-bulk.api.js +14 -1
- package/lib/api/resource-types.api.js +107 -0
- package/lib/api/runtime-configuration.api.js +72 -0
- package/lib/api/types/resource-types.types.js +45 -0
- package/lib/api/user-groups.api.js +9 -9
- package/lib/api/users.api.js +8 -8
- package/lib/api/validation-runner.js +3 -1
- package/lib/api/wizard.api.js +10 -2
- package/lib/app/deploy-config.js +4 -1
- package/lib/app/down.js +78 -27
- package/lib/app/index.js +2 -2
- package/lib/app/push.js +4 -5
- package/lib/app/register.js +7 -4
- package/lib/app/run-env-compose.js +84 -30
- package/lib/app/run-resolve-image.js +2 -1
- package/lib/app/run-stop-containers.js +113 -0
- package/lib/app/run.js +1 -31
- package/lib/app/show-display.js +35 -0
- package/lib/app/show.js +32 -1
- package/lib/build/index.js +22 -5
- package/lib/build/miso-controller-npm-sdk-guard.js +25 -0
- package/lib/certification/sync-system-certification.js +13 -6
- package/lib/channels/add-channel.js +348 -0
- package/lib/channels/channel-artifact-generator.js +141 -0
- package/lib/channels/channel-audit.js +101 -0
- package/lib/channels/channel-auth-context.js +37 -0
- package/lib/channels/channel-credential.js +200 -0
- package/lib/channels/channel-display-name.js +64 -0
- package/lib/channels/channel-external-system.js +106 -0
- package/lib/channels/channel-integration-generator.js +274 -0
- package/lib/channels/channel-probe.js +89 -0
- package/lib/channels/channel-runtime-resolver.js +152 -0
- package/lib/channels/channel-status-display.js +141 -0
- package/lib/channels/channel-targets.js +115 -0
- package/lib/cli/index.js +20 -4
- package/lib/cli/infra-guided-auth.js +109 -0
- package/lib/cli/infra-guided.js +84 -81
- package/lib/cli/setup-app.help.js +0 -31
- package/lib/cli/setup-app.js +82 -5
- package/lib/cli/setup-app.test-commands.js +5 -57
- package/lib/cli/setup-auth.js +25 -0
- package/lib/cli/setup-certification-commands.js +147 -0
- package/lib/cli/setup-certification.help.js +141 -0
- package/lib/cli/setup-environment.js +19 -2
- package/lib/cli/setup-external-system-help.js +62 -0
- package/lib/cli/setup-external-system.js +25 -3
- package/lib/cli/setup-infra-up-dataplane-action.js +2 -2
- package/lib/cli/setup-infra-up-platform-action.js +2 -1
- package/lib/cli/setup-infra.js +9 -1
- package/lib/cli/setup-platform.js +6 -1
- package/lib/cli/setup-secrets.js +33 -1
- package/lib/cli/setup-utility-help-after.js +25 -2
- package/lib/cli/setup-utility-repair.js +198 -0
- package/lib/cli/setup-utility-resolve.js +44 -5
- package/lib/cli/setup-utility-validate.js +197 -0
- package/lib/cli/setup-utility.js +4 -239
- package/lib/commands/app-logs.js +54 -26
- package/lib/commands/app.js +41 -0
- package/lib/commands/auth-status-client-tokens.js +214 -0
- package/lib/commands/auth-status-dataplane-version.js +142 -68
- package/lib/commands/auth-status.js +43 -54
- package/lib/commands/channel.js +58 -0
- package/lib/commands/datasource-test-trust-cli.js +27 -23
- package/lib/commands/datasource-unified-test-cli.js +6 -3
- package/lib/commands/datasource-unified-test-cli.options.js +5 -1
- package/lib/commands/datasource.js +8 -8
- package/lib/commands/dev-show-display.js +8 -1
- package/lib/commands/governance-cli-shared.js +94 -0
- package/lib/commands/governance-command-action.js +132 -0
- package/lib/commands/governance-download-external.js +34 -0
- package/lib/commands/governance-generate-external.js +71 -0
- package/lib/commands/governance-upload-external.js +36 -0
- package/lib/commands/governance-verify-external.js +64 -0
- package/lib/commands/governance.js +144 -0
- package/lib/commands/identity-apply-sync.js +49 -8
- package/lib/commands/identity-group-cli.js +66 -26
- package/lib/commands/identity-membership-role-cache.js +58 -37
- package/lib/commands/identity-shared.js +22 -1
- package/lib/commands/identity-user-cli.js +80 -34
- package/lib/commands/lifecycle-command-action.js +123 -0
- package/lib/commands/lifecycle-external.js +268 -0
- package/lib/commands/login.js +11 -2
- package/lib/commands/repair-auth-apply.js +153 -0
- package/lib/commands/repair-auth-config.js +8 -13
- package/lib/commands/repair-changed-files.js +41 -0
- package/lib/commands/repair-datasource-com-v2.js +15 -0
- package/lib/commands/repair-datasource-keys.js +4 -1
- package/lib/commands/repair-datasource-run.js +59 -0
- package/lib/commands/repair-datasource-service-contract.js +206 -0
- package/lib/commands/repair-datasource-test-payload.js +259 -0
- package/lib/commands/repair-datasource.js +49 -64
- package/lib/commands/repair-env-template.js +76 -25
- package/lib/commands/repair-external-run.js +216 -0
- package/lib/commands/repair-openapi-sync.js +49 -4
- package/lib/commands/repair-persist.js +4 -0
- package/lib/commands/repair-rbac.js +52 -1
- package/lib/commands/repair-steps.js +160 -0
- package/lib/commands/repair-system-alignment.js +7 -4
- package/lib/commands/repair.js +29 -296
- package/lib/commands/resource-type.js +371 -0
- package/lib/commands/secrets-get.js +184 -0
- package/lib/commands/secrets-remove-all.js +3 -1
- package/lib/commands/setup-image-refresh.js +146 -0
- package/lib/commands/setup-modes.js +158 -162
- package/lib/commands/setup-platform-auth.js +76 -0
- package/lib/commands/setup-prompts-admin.js +244 -0
- package/lib/commands/setup-prompts-platform-mode.js +70 -0
- package/lib/commands/setup-prompts-validators.js +36 -0
- package/lib/commands/setup-prompts.js +24 -76
- package/lib/commands/setup-run-context.js +45 -0
- package/lib/commands/setup-spinners.js +39 -0
- package/lib/commands/setup.js +27 -5
- package/lib/commands/teardown.js +39 -0
- package/lib/commands/test-e2e-external.js +24 -8
- package/lib/commands/test-governance-external.js +3 -24
- package/lib/commands/test-trust-external.js +48 -100
- package/lib/commands/up-common.js +57 -13
- package/lib/commands/up-dataplane.js +56 -15
- package/lib/commands/upload-publish-context.js +75 -0
- package/lib/commands/upload-sidecar-followups.js +89 -0
- package/lib/commands/upload.js +113 -110
- package/lib/commands/verify-governance-combined-external.js +61 -0
- package/lib/commands/verify-governance-command-action.js +64 -0
- package/lib/commands/verify-governance-db.js +102 -0
- package/lib/commands/verify-governance-external.js +77 -0
- package/lib/commands/verify-governance-result-builders.js +71 -0
- package/lib/commands/verify-operations-command-action.js +68 -0
- package/lib/commands/verify-operations-external.js +268 -0
- package/lib/commands/verify-operations-persist.js +75 -0
- package/lib/commands/verify-operations-step-plan.js +68 -0
- package/lib/commands/verify-operations-steps.js +111 -0
- package/lib/commands/verify-trust-command-action.js +73 -0
- package/lib/commands/verify-trust-external.js +59 -0
- package/lib/commands/wizard-config-normalizer.js +43 -23
- package/lib/commands/wizard-core.js +2 -0
- package/lib/core/admin-password-generator.js +38 -0
- package/lib/core/admin-secrets-bundle.js +111 -0
- package/lib/core/admin-secrets-env-overlay.js +62 -0
- package/lib/core/admin-secrets.js +32 -4
- package/lib/core/config-attach-extensions.js +3 -0
- package/lib/core/config-platform-controller.js +44 -0
- package/lib/core/config-setup-installation-profile.js +42 -0
- package/lib/core/config-setup-platform-mode.js +46 -0
- package/lib/core/config.js +46 -36
- package/lib/core/secrets-admin-env.js +6 -2
- package/lib/core/secrets-ensure.js +4 -0
- package/lib/core/secrets-env-content.js +54 -48
- package/lib/core/secrets-env-write.js +10 -1
- package/lib/core/secrets-infra-placeholder-sync.js +2 -0
- package/lib/core/setup-installation-profile.js +43 -0
- package/lib/core/setup-platform-mode.js +90 -0
- package/lib/datasource/agent-trust-debug-log.js +1 -1
- package/lib/datasource/agent-trust-run.js +5 -3
- package/lib/datasource/audit-evidence-extract-debug.js +30 -18
- package/lib/datasource/audit-evidence-extract.js +7 -4
- package/lib/datasource/audit-evidence-verify-matrix.js +54 -26
- package/lib/datasource/bulk-loader-run-batches.js +15 -3
- package/lib/datasource/capability/capability-diff-slice.js +2 -2
- package/lib/datasource/capability/capability-manifest.js +254 -0
- package/lib/datasource/capability/capability-resolve.js +3 -4
- package/lib/datasource/capability/copy-operations.js +5 -3
- package/lib/datasource/capability/remove-operations.js +2 -1
- package/lib/datasource/capability/run-capability-copy.js +32 -4
- package/lib/datasource/capability/run-capability-diff.js +3 -3
- package/lib/datasource/capability/run-capability-dimension.js +2 -4
- package/lib/datasource/capability/run-capability-edit.js +4 -7
- package/lib/datasource/capability/run-capability-relate.js +2 -4
- package/lib/datasource/capability/run-capability-remove.js +2 -4
- package/lib/datasource/capability/validate-capability-slice.js +2 -1
- package/lib/datasource/datasource-identifier.js +2 -1
- package/lib/datasource/datasource-load-export-context.js +3 -0
- package/lib/datasource/datasource-validate-display.js +1 -1
- package/lib/datasource/field-reference-validator.js +24 -3
- package/lib/datasource/log-cleaner.js +2 -2
- package/lib/datasource/log-viewer.js +8 -8
- package/lib/datasource/orphan-profiles-validator.js +98 -0
- package/lib/datasource/test-e2e.js +4 -1
- package/lib/datasource/unified-validation-run-body.js +3 -1
- package/lib/datasource/unified-validation-run.js +5 -2
- package/lib/datasource/validate.js +41 -23
- package/lib/deployment/environment-poll-status.js +138 -0
- package/lib/deployment/environment.js +42 -34
- package/lib/external-system/download-helpers.js +87 -0
- package/lib/external-system/download.js +78 -138
- package/lib/external-system/generator.js +1 -55
- package/lib/external-system/integration-auth-context.js +45 -0
- package/lib/external-system/test-auth.js +12 -5
- package/lib/external-system/test.js +16 -21
- package/lib/generator/builders.js +11 -1
- package/lib/generator/external-controller-manifest.js +18 -0
- package/lib/generator/split.js +3 -1
- package/lib/generator/wizard.js +1 -28
- package/lib/governance/governance-cli-errors.js +96 -0
- package/lib/governance/governance-download-display.js +38 -0
- package/lib/governance/governance-run-display.js +215 -25
- package/lib/identity/fk-reference.js +65 -0
- package/lib/identity/identity-apply-core.js +13 -58
- package/lib/identity/identity-apply-phases.js +5 -1
- package/lib/identity/identity-display.js +243 -0
- package/lib/identity/identity-response-helpers.js +52 -0
- package/lib/identity/identity-sync-errors.js +62 -0
- package/lib/identity/resolve-user-id.js +129 -0
- package/lib/identity/username-default.js +31 -0
- package/lib/infrastructure/admin-secrets-apply.js +84 -0
- package/lib/infrastructure/admin-secrets-ensure.js +130 -0
- package/lib/infrastructure/admin-secrets-resolve.js +95 -0
- package/lib/infrastructure/admin-secrets-sync.js +91 -0
- package/lib/infrastructure/helpers.js +23 -129
- package/lib/infrastructure/index.js +5 -6
- package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
- package/lib/lifecycle/certification-progress.js +231 -0
- package/lib/lifecycle/dataplane-ui-url.js +145 -0
- package/lib/lifecycle/exit-codes.js +24 -0
- package/lib/lifecycle/json-envelope.js +25 -0
- package/lib/lifecycle/lifecycle-governance-options.js +51 -0
- package/lib/lifecycle/logger-quiet.js +29 -0
- package/lib/lifecycle/open-api-published.js +59 -0
- package/lib/lifecycle/operations-readiness-metrics.js +101 -0
- package/lib/lifecycle/product-model.js +196 -0
- package/lib/lifecycle/report-display-trust.js +70 -0
- package/lib/lifecycle/report-display.js +472 -0
- package/lib/lifecycle/scenario-upload.js +191 -0
- package/lib/lifecycle/verify-step-progress.js +87 -0
- package/lib/parameters/infra-kv-discovery.js +27 -2
- package/lib/protection/auth-context.js +10 -4
- package/lib/resolvers/datasource-resolver.js +2 -3
- package/lib/resolvers/resource-type-file.js +159 -0
- package/lib/schema/application-schema.json +17 -1
- package/lib/schema/datasource-test-run.schema.json +89 -6
- package/lib/schema/deployment-rules.yaml +2 -1
- package/lib/schema/external-datasource.schema.json +2947 -2619
- package/lib/schema/external-system.schema.json +1418 -761
- package/lib/schema/infra.parameter.yaml +45 -0
- package/lib/schema/type/credential-field-registry.json +103 -97
- package/lib/schema/type/document-storage.json +9 -5
- package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
- package/lib/utils/api.js +55 -31
- package/lib/utils/app-relative-path-env.js +112 -0
- package/lib/utils/app-run-containers.js +25 -2
- package/lib/utils/authentication-shape.js +224 -0
- package/lib/utils/build-copy.js +36 -5
- package/lib/utils/build-resolve-image.js +66 -12
- package/lib/utils/celery-companion-apps.js +53 -0
- package/lib/utils/cli-secrets-error-format.js +83 -21
- package/lib/utils/cli-utils.js +20 -4
- package/lib/utils/compose-celery-worker.js +23 -0
- package/lib/utils/compose-generate-docker-compose.js +13 -1
- package/lib/utils/compose-traefik-ingress-base.js +16 -1
- package/lib/utils/config-secrets-path.js +46 -0
- package/lib/utils/configuration-env-resolver.js +36 -0
- package/lib/utils/controller-url.js +88 -1
- package/lib/utils/credential-secrets-env-kv.js +216 -0
- package/lib/utils/credential-secrets-env.js +175 -257
- package/lib/utils/dataplane-resolver.js +18 -2
- package/lib/utils/declarative-url-ports.js +27 -4
- package/lib/utils/deployment-auth-mode.js +41 -0
- package/lib/utils/developer-volume-id.js +33 -0
- package/lib/utils/docker-build.js +1 -4
- package/lib/utils/env-copy.js +14 -9
- package/lib/utils/env-kv-orphan-lines.js +43 -0
- package/lib/utils/error-formatters/network-errors.js +13 -4
- package/lib/utils/external-env-template.js +11 -3
- package/lib/utils/external-system-readiness-core.js +10 -0
- package/lib/utils/external-system-readiness-display.js +80 -23
- package/lib/utils/external-system-validators.js +8 -2
- package/lib/utils/health-check.js +4 -1
- package/lib/utils/help-builder.js +13 -8
- package/lib/utils/keycloak-realm-ready.js +101 -0
- package/lib/utils/load-cip-capacity-display-config.js +61 -15
- package/lib/utils/manifest-source-emit.js +2 -0
- package/lib/utils/paths.js +25 -0
- package/lib/utils/pipeline-upload-auth-retry.js +82 -0
- package/lib/utils/platform-controller-url.js +157 -0
- package/lib/utils/platform-kv-ref.js +57 -0
- package/lib/utils/postgres-platform-bootstrap.js +199 -0
- package/lib/utils/postgres-wipe.js +9 -2
- package/lib/utils/resolve-env-mode.js +47 -0
- package/lib/utils/runtime-configuration-download.js +189 -0
- package/lib/utils/runtime-configuration-values.js +119 -0
- package/lib/utils/schema-loader.js +19 -4
- package/lib/utils/secrets-generator.js +31 -25
- package/lib/utils/secrets-missing-error.js +164 -0
- package/lib/utils/service-url-label.js +81 -0
- package/lib/utils/setup-quiet-output.js +25 -0
- package/lib/utils/template-helpers.js +15 -0
- package/lib/utils/token-manager-client-credentials.js +201 -0
- package/lib/utils/token-manager-messages.js +8 -0
- package/lib/utils/token-manager-refresh.js +12 -2
- package/lib/utils/token-manager.js +86 -105
- package/lib/utils/upload-pipeline-ui.js +12 -5
- package/lib/utils/upload-sync-options.js +42 -0
- package/lib/utils/url-declarative-public-base.js +21 -3
- package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
- package/lib/utils/validation-poll-ui.js +8 -0
- package/lib/utils/validation-run-stage.js +45 -0
- package/lib/utils/worker-sidecars.js +142 -0
- package/lib/validation/external-manifest-validator.js +2 -0
- package/lib/validation/runtime-configuration-rules.js +98 -0
- package/lib/validation/validate-display-dimensions.js +154 -0
- package/lib/validation/validate-display-sections.js +276 -0
- package/lib/validation/validate-display-steps.js +67 -0
- package/lib/validation/validate-display.js +24 -402
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +7 -1
- package/templates/applications/dataplane/env.template +50 -14
- package/templates/applications/dataplane/rbac.yaml +108 -1
- package/templates/applications/miso-controller/application.yaml +2 -2
- package/templates/applications/miso-controller/env.template +53 -122
- package/templates/applications/miso-controller/rbac.yaml +28 -13
- package/templates/channels/chatgpt/install.template.md.hbs +49 -0
- package/templates/channels/chatgpt/manifest.template.json.hbs +9 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +49 -0
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +8 -0
- package/templates/channels/slack/install.template.md.hbs +49 -0
- package/templates/channels/slack/manifest.template.json.hbs +66 -0
- package/templates/channels/teams-bot/install.template.md.hbs +49 -0
- package/templates/channels/teams-bot/manifest.template.json.hbs +13 -0
- package/templates/channels/web-embed/install.template.md.hbs +49 -0
- package/templates/channels/web-embed/manifest.template.json.hbs +8 -0
- package/templates/external-system/README.md.hbs +74 -17
- package/templates/python/docker-compose.hbs +75 -18
- package/templates/typescript/docker-compose.hbs +70 -17
- package/lib/commands/test-governance-command-action.js +0 -55
- package/lib/commands/test-trust-command-action.js +0 -118
|
@@ -22,6 +22,7 @@ const dockerExec = require('./docker-exec');
|
|
|
22
22
|
const logger = require('./logger');
|
|
23
23
|
const chalk = require('chalk');
|
|
24
24
|
const { successGlyph } = require('./cli-test-layout-chalk');
|
|
25
|
+
const { isSetupQuietOutput } = require('./setup-quiet-output');
|
|
25
26
|
|
|
26
27
|
/** Roles that must never be dropped (Postgres-managed predefined roles). */
|
|
27
28
|
const PROTECTED_ROLES = new Set([
|
|
@@ -190,15 +191,21 @@ async function wipePostgresData() {
|
|
|
190
191
|
}
|
|
191
192
|
|
|
192
193
|
const databases = await listUserDatabases(container, password);
|
|
194
|
+
const quiet = isSetupQuietOutput();
|
|
195
|
+
|
|
193
196
|
for (const dbName of databases) {
|
|
194
197
|
await dropDatabase(container, dbName, password);
|
|
195
|
-
|
|
198
|
+
if (!quiet) {
|
|
199
|
+
logger.log(chalk.gray(` ${successGlyph()} Dropped database ${dbName}`));
|
|
200
|
+
}
|
|
196
201
|
}
|
|
197
202
|
|
|
198
203
|
const roles = await listDropableRoles(container, password);
|
|
199
204
|
for (const role of roles) {
|
|
200
205
|
await dropRole(container, role, password);
|
|
201
|
-
|
|
206
|
+
if (!quiet) {
|
|
207
|
+
logger.log(chalk.gray(` ${successGlyph()} Dropped role ${role}`));
|
|
208
|
+
}
|
|
202
209
|
}
|
|
203
210
|
|
|
204
211
|
return { databases, roles };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chooses `local` vs `docker` for `generateEnvFile` / `resolve` (integration → local, no Docker rewrite).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Resolve environment mode for env generation
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const path = require('path');
|
|
12
|
+
|
|
13
|
+
/** Platform builder apps that run in Docker and need docker host/port transforms on resolve. */
|
|
14
|
+
const PLATFORM_DOCKER_APP_KEYS = new Set(['keycloak', 'miso-controller', 'dataplane']);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {string} appPath - Resolved app directory from getResolveAppPath
|
|
18
|
+
* @returns {boolean}
|
|
19
|
+
*/
|
|
20
|
+
function isIntegrationAppPath(appPath) {
|
|
21
|
+
if (!appPath || typeof appPath !== 'string') return false;
|
|
22
|
+
const normalized = path.resolve(appPath);
|
|
23
|
+
const marker = `${path.sep}integration${path.sep}`;
|
|
24
|
+
return normalized.includes(marker);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {string} appPath - Resolved application directory
|
|
29
|
+
* @param {string} [appName] - App or system key
|
|
30
|
+
* @returns {'local'|'docker'}
|
|
31
|
+
*/
|
|
32
|
+
function resolveGenerateEnvEnvironment(appPath, appName) {
|
|
33
|
+
if (isIntegrationAppPath(appPath)) {
|
|
34
|
+
return 'local';
|
|
35
|
+
}
|
|
36
|
+
const key = String(appName || '').trim().toLowerCase();
|
|
37
|
+
if (PLATFORM_DOCKER_APP_KEYS.has(key)) {
|
|
38
|
+
return 'docker';
|
|
39
|
+
}
|
|
40
|
+
return 'docker';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = {
|
|
44
|
+
PLATFORM_DOCKER_APP_KEYS,
|
|
45
|
+
isIntegrationAppPath,
|
|
46
|
+
resolveGenerateEnvEnvironment
|
|
47
|
+
};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync runtime configuration_values from dataplane on download (values → env.template; manifest → placeholders).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Download-time runtime configuration sync
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const fsPromises = require('fs').promises;
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const yaml = require('js-yaml');
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
getExternalSystemConfigurationResolved,
|
|
16
|
+
getExternalDataSourceConfigurationResolved
|
|
17
|
+
} = require('../api/runtime-configuration.api');
|
|
18
|
+
const {
|
|
19
|
+
retemplateAllVariableDefinitions,
|
|
20
|
+
retemplateDatasourceConfigurationOverrides
|
|
21
|
+
} = require('./configuration-env-resolver');
|
|
22
|
+
const { mergeEnvTemplateWithExisting } = require('../generator/split');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {Object} response - Dataplane API client response
|
|
26
|
+
* @returns {Record<string, string>}
|
|
27
|
+
*/
|
|
28
|
+
function extractResolvedVariableValues(response) {
|
|
29
|
+
if (!response || !response.success) {
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
const body = response.data && typeof response.data === 'object' ? response.data : {};
|
|
33
|
+
const variables = body.variables && typeof body.variables === 'object' ? body.variables : {};
|
|
34
|
+
const out = {};
|
|
35
|
+
for (const [name, entry] of Object.entries(variables)) {
|
|
36
|
+
if (!entry || typeof entry.value !== 'string') {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const value = entry.value.trim();
|
|
40
|
+
if (!value || value.startsWith('kv://')) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
out[name] = value;
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} dataplaneUrl
|
|
50
|
+
* @param {Object} authConfig
|
|
51
|
+
* @param {string} systemKey
|
|
52
|
+
* @param {Array<{ key?: string }>} dataSources
|
|
53
|
+
* @returns {Promise<Record<string, string>>}
|
|
54
|
+
*/
|
|
55
|
+
async function collectResolvedConfigurationEnvUpdates(
|
|
56
|
+
dataplaneUrl,
|
|
57
|
+
authConfig,
|
|
58
|
+
systemKey,
|
|
59
|
+
dataSources
|
|
60
|
+
) {
|
|
61
|
+
const updates = {};
|
|
62
|
+
const systemResponse = await getExternalSystemConfigurationResolved(
|
|
63
|
+
dataplaneUrl,
|
|
64
|
+
systemKey,
|
|
65
|
+
authConfig
|
|
66
|
+
);
|
|
67
|
+
Object.assign(updates, extractResolvedVariableValues(systemResponse));
|
|
68
|
+
|
|
69
|
+
for (const ds of dataSources || []) {
|
|
70
|
+
if (!ds?.key) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const dsResponse = await getExternalDataSourceConfigurationResolved(
|
|
74
|
+
dataplaneUrl,
|
|
75
|
+
ds.key,
|
|
76
|
+
authConfig
|
|
77
|
+
);
|
|
78
|
+
Object.assign(updates, extractResolvedVariableValues(dsResponse));
|
|
79
|
+
}
|
|
80
|
+
return updates;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @param {string} integrationPath
|
|
85
|
+
* @param {Record<string, string>} updates
|
|
86
|
+
* @returns {Promise<boolean>}
|
|
87
|
+
*/
|
|
88
|
+
async function mergeConfigurationUpdatesIntoEnvTemplate(integrationPath, updates) {
|
|
89
|
+
const keys = Object.keys(updates || {});
|
|
90
|
+
if (!keys.length) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const envTemplatePath = path.join(integrationPath, 'env.template');
|
|
94
|
+
const expectedByKey = new Map();
|
|
95
|
+
for (const key of keys) {
|
|
96
|
+
expectedByKey.set(key, `${key}=${updates[key]}`);
|
|
97
|
+
}
|
|
98
|
+
const existingContent = fs.existsSync(envTemplatePath)
|
|
99
|
+
? await fsPromises.readFile(envTemplatePath, 'utf8')
|
|
100
|
+
: '';
|
|
101
|
+
const merged = mergeEnvTemplateWithExisting(existingContent, expectedByKey);
|
|
102
|
+
await fsPromises.writeFile(envTemplatePath, merged, { mode: 0o644, encoding: 'utf8' });
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {string} filePath
|
|
108
|
+
* @returns {Promise<boolean>}
|
|
109
|
+
*/
|
|
110
|
+
async function retemplateConfigurationFile(filePath, { system = false } = {}) {
|
|
111
|
+
if (!filePath || !fs.existsSync(filePath)) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
const raw = await fsPromises.readFile(filePath, 'utf8');
|
|
115
|
+
const isJson = filePath.endsWith('.json');
|
|
116
|
+
const parsed = isJson ? JSON.parse(raw) : yaml.load(raw);
|
|
117
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
if (system) {
|
|
121
|
+
retemplateAllVariableDefinitions(parsed.configuration);
|
|
122
|
+
} else {
|
|
123
|
+
retemplateDatasourceConfigurationOverrides(parsed.configuration);
|
|
124
|
+
}
|
|
125
|
+
const next = isJson ? `${JSON.stringify(parsed, null, 2)}\n` : yaml.dump(parsed, { indent: 2, lineWidth: -1 });
|
|
126
|
+
await fsPromises.writeFile(filePath, next, { mode: 0o644, encoding: 'utf8' });
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @param {string} integrationPath
|
|
132
|
+
* @param {string} systemKey
|
|
133
|
+
* @returns {Promise<void>}
|
|
134
|
+
*/
|
|
135
|
+
async function retemplateDownloadedConfigurationFiles(integrationPath, systemKey) {
|
|
136
|
+
for (const ext of ['yaml', 'json']) {
|
|
137
|
+
const systemPath = path.join(integrationPath, `${systemKey}-system.${ext}`);
|
|
138
|
+
if (fs.existsSync(systemPath)) {
|
|
139
|
+
await retemplateConfigurationFile(systemPath, { system: true });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const entries = await fsPromises.readdir(integrationPath);
|
|
143
|
+
for (const name of entries) {
|
|
144
|
+
if (!name.includes('-datasource-') || (!name.endsWith('.yaml') && !name.endsWith('.json'))) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
await retemplateConfigurationFile(path.join(integrationPath, name), { system: false });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Pulls configuration_values from dataplane, merges env.template, and re-templates manifests.
|
|
153
|
+
*
|
|
154
|
+
* @param {Object} params
|
|
155
|
+
* @param {string} params.dataplaneUrl
|
|
156
|
+
* @param {Object} params.authConfig
|
|
157
|
+
* @param {string} params.systemKey
|
|
158
|
+
* @param {string} params.integrationPath
|
|
159
|
+
* @param {Array<{ key?: string }>} params.dataSources
|
|
160
|
+
* @returns {Promise<{ merged: number, names: string[] }>}
|
|
161
|
+
*/
|
|
162
|
+
async function syncRuntimeConfigurationFromDataplane({
|
|
163
|
+
dataplaneUrl,
|
|
164
|
+
authConfig,
|
|
165
|
+
systemKey,
|
|
166
|
+
integrationPath,
|
|
167
|
+
dataSources
|
|
168
|
+
}) {
|
|
169
|
+
const updates = await collectResolvedConfigurationEnvUpdates(
|
|
170
|
+
dataplaneUrl,
|
|
171
|
+
authConfig,
|
|
172
|
+
systemKey,
|
|
173
|
+
dataSources
|
|
174
|
+
);
|
|
175
|
+
const names = Object.keys(updates);
|
|
176
|
+
if (names.length) {
|
|
177
|
+
await mergeConfigurationUpdatesIntoEnvTemplate(integrationPath, updates);
|
|
178
|
+
}
|
|
179
|
+
await retemplateDownloadedConfigurationFiles(integrationPath, systemKey);
|
|
180
|
+
return { merged: names.length, names };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
module.exports = {
|
|
184
|
+
extractResolvedVariableValues,
|
|
185
|
+
collectResolvedConfigurationEnvUpdates,
|
|
186
|
+
mergeConfigurationUpdatesIntoEnvTemplate,
|
|
187
|
+
retemplateDownloadedConfigurationFiles,
|
|
188
|
+
syncRuntimeConfigurationFromDataplane
|
|
189
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Push runtime configuration_values to dataplane after publish (never logs values).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Post-publish configuration PUT helpers
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
putExternalSystemConfiguration,
|
|
11
|
+
putExternalDataSourceConfiguration
|
|
12
|
+
} = require('../api/runtime-configuration.api');
|
|
13
|
+
const { buildResolvedEnvMapForIntegration } = require('./configuration-env-resolver');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {Array<{ name?: string, location?: string }>|undefined} systemConfiguration
|
|
17
|
+
* @param {Object.<string, string>} envMap
|
|
18
|
+
* @returns {Array<{ name: string, value: string }>}
|
|
19
|
+
*/
|
|
20
|
+
function collectSystemVariablePutRequests(systemConfiguration, envMap) {
|
|
21
|
+
if (!Array.isArray(systemConfiguration)) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
const requests = [];
|
|
25
|
+
for (const item of systemConfiguration) {
|
|
26
|
+
if (!item || !item.name) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const location = String(item.location || 'variable').toLowerCase();
|
|
30
|
+
if (location !== 'variable') {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const name = String(item.name).trim();
|
|
34
|
+
const raw = envMap[name];
|
|
35
|
+
if (raw === undefined || raw === null) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const value = String(raw).trim();
|
|
39
|
+
if (!value || value.startsWith('kv://') || value.includes('{{')) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
requests.push({ name, value });
|
|
43
|
+
}
|
|
44
|
+
return requests;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Datasource overrides are name+value only; values may already be resolved in upload payload.
|
|
49
|
+
*
|
|
50
|
+
* @param {Array<{ name?: string, value?: string }>|undefined} datasourceConfiguration
|
|
51
|
+
* @returns {Array<{ name: string, value: string }>}
|
|
52
|
+
*/
|
|
53
|
+
function collectDatasourceOverridePutRequests(datasourceConfiguration) {
|
|
54
|
+
if (!Array.isArray(datasourceConfiguration)) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
const requests = [];
|
|
58
|
+
for (const item of datasourceConfiguration) {
|
|
59
|
+
if (!item || !item.name) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const name = String(item.name).trim();
|
|
63
|
+
const raw = item.value;
|
|
64
|
+
if (typeof raw !== 'string') {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const value = raw.trim();
|
|
68
|
+
if (!value || value.startsWith('kv://') || value.includes('{{')) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
requests.push({ name, value });
|
|
72
|
+
}
|
|
73
|
+
return requests;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Upserts system-scoped and datasource-scoped configuration values after publish.
|
|
78
|
+
*
|
|
79
|
+
* @param {string} dataplaneUrl
|
|
80
|
+
* @param {Object} authConfig
|
|
81
|
+
* @param {string} systemKey
|
|
82
|
+
* @param {{ application?: { configuration?: unknown[] }, dataSources?: Array<{ key?: string, configuration?: unknown[] }> }} payload
|
|
83
|
+
* @returns {Promise<{ pushed: number, names: string[] }>}
|
|
84
|
+
*/
|
|
85
|
+
async function pushRuntimeConfigurationValues(dataplaneUrl, authConfig, systemKey, payload) {
|
|
86
|
+
const { envMap } = await buildResolvedEnvMapForIntegration(systemKey);
|
|
87
|
+
const names = [];
|
|
88
|
+
let pushed = 0;
|
|
89
|
+
|
|
90
|
+
const systemRequests = collectSystemVariablePutRequests(
|
|
91
|
+
payload?.application?.configuration,
|
|
92
|
+
envMap
|
|
93
|
+
);
|
|
94
|
+
for (const body of systemRequests) {
|
|
95
|
+
await putExternalSystemConfiguration(dataplaneUrl, systemKey, authConfig, body);
|
|
96
|
+
names.push(body.name);
|
|
97
|
+
pushed += 1;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (const ds of payload?.dataSources || []) {
|
|
101
|
+
if (!ds?.key) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const dsRequests = collectDatasourceOverridePutRequests(ds.configuration);
|
|
105
|
+
for (const body of dsRequests) {
|
|
106
|
+
await putExternalDataSourceConfiguration(dataplaneUrl, ds.key, authConfig, body);
|
|
107
|
+
names.push(`${ds.key}:${body.name}`);
|
|
108
|
+
pushed += 1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return { pushed, names };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = {
|
|
116
|
+
collectSystemVariablePutRequests,
|
|
117
|
+
collectDatasourceOverridePutRequests,
|
|
118
|
+
pushRuntimeConfigurationValues
|
|
119
|
+
};
|
|
@@ -28,6 +28,23 @@ function resetValidators() {
|
|
|
28
28
|
externalDataSourceValidator = null;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Register AJV schemas required to compile or validate external-datasource manifests.
|
|
33
|
+
* Includes type schemas and external-system (for $defs/subscriptionFilter cross-ref).
|
|
34
|
+
*
|
|
35
|
+
* @function registerExternalDatasourceAjvDependencies
|
|
36
|
+
* @param {import('ajv').default} ajv - AJV instance
|
|
37
|
+
*/
|
|
38
|
+
function registerExternalDatasourceAjvDependencies(ajv) {
|
|
39
|
+
ajv.addSchema(require('../schema/type/document-storage.json'));
|
|
40
|
+
ajv.addSchema(require('../schema/type/message-service.json'));
|
|
41
|
+
ajv.addSchema(require('../schema/type/vector-store.json'));
|
|
42
|
+
const externalSystemSchema = require('../schema/external-system.schema.json');
|
|
43
|
+
if (!ajv.getSchema(externalSystemSchema.$id)) {
|
|
44
|
+
ajv.addSchema(externalSystemSchema, externalSystemSchema.$id);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
31
48
|
/**
|
|
32
49
|
* Loads and compiles external-system schema
|
|
33
50
|
* Caches the compiled validator for performance
|
|
@@ -115,10 +132,7 @@ function loadExternalDataSourceSchema() {
|
|
|
115
132
|
|
|
116
133
|
const ajv = new Ajv({ allErrors: true, strict: false, strictSchema: false });
|
|
117
134
|
addFormats(ajv);
|
|
118
|
-
|
|
119
|
-
ajv.addSchema(require('../schema/type/document-storage.json'));
|
|
120
|
-
ajv.addSchema(require('../schema/type/message-service.json'));
|
|
121
|
-
ajv.addSchema(require('../schema/type/vector-store.json'));
|
|
135
|
+
registerExternalDatasourceAjvDependencies(ajv);
|
|
122
136
|
externalDataSourceValidator = ajv.compile(schemaToCompile);
|
|
123
137
|
|
|
124
138
|
return externalDataSourceValidator;
|
|
@@ -333,6 +347,7 @@ function detectSchemaType(filePath, content) {
|
|
|
333
347
|
module.exports = {
|
|
334
348
|
loadExternalSystemSchema,
|
|
335
349
|
loadExternalDataSourceSchema,
|
|
350
|
+
registerExternalDatasourceAjvDependencies,
|
|
336
351
|
detectSchemaType,
|
|
337
352
|
detectSchemaTypeFromParsed,
|
|
338
353
|
resetValidators
|
|
@@ -77,42 +77,44 @@ function loadYamlTolerantOfDuplicateKeys(content) {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
|
-
*
|
|
81
|
-
* @param {
|
|
80
|
+
* True when secrets map already has a non-empty value for a kv path (flat or nested).
|
|
81
|
+
* @param {Object} secrets
|
|
82
|
+
* @param {string} pathStr - Path after kv:// (e.g. hubspot-demo/token)
|
|
82
83
|
* @returns {boolean}
|
|
83
84
|
*/
|
|
84
|
-
function
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
function secretPathExistsInStore(secrets, pathStr) {
|
|
86
|
+
if (!secrets || typeof secrets !== 'object' || !pathStr) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const direct = secrets[pathStr];
|
|
90
|
+
if (direct !== undefined && direct !== null && String(direct).trim() !== '') {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
if (!pathStr.includes('/')) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
let current = secrets;
|
|
97
|
+
for (const segment of pathStr.split('/')) {
|
|
98
|
+
if (current === null || current === undefined || typeof current !== 'object' || !(segment in current)) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
current = current[segment];
|
|
102
|
+
}
|
|
103
|
+
return current !== undefined && current !== null && String(current).trim() !== '';
|
|
87
104
|
}
|
|
88
105
|
|
|
89
106
|
/**
|
|
90
|
-
* Finds missing secret keys from template (skips commented and empty lines)
|
|
107
|
+
* Finds missing secret keys from template (skips commented and empty lines).
|
|
108
|
+
* Uses full kv:// paths (e.g. hubspot-demo/token), aligned with collectMissingSecrets.
|
|
91
109
|
* @function findMissingSecretKeys
|
|
92
110
|
* @param {string} envTemplate - Environment template content
|
|
93
111
|
* @param {Object} existingSecrets - Existing secrets object
|
|
94
112
|
* @returns {string[]} Array of missing secret keys
|
|
95
113
|
*/
|
|
96
114
|
function findMissingSecretKeys(envTemplate, existingSecrets) {
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
const lines = envTemplate.split('\n');
|
|
101
|
-
|
|
102
|
-
for (const line of lines) {
|
|
103
|
-
if (isCommentOrEmptyLine(line)) continue;
|
|
104
|
-
let match;
|
|
105
|
-
kvPattern.lastIndex = 0;
|
|
106
|
-
while ((match = kvPattern.exec(line)) !== null) {
|
|
107
|
-
const secretKey = match[1];
|
|
108
|
-
if (!seenKeys.has(secretKey) && !(secretKey in existingSecrets)) {
|
|
109
|
-
missingKeys.push(secretKey);
|
|
110
|
-
seenKeys.add(secretKey);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return missingKeys;
|
|
115
|
+
const { collectUniqueKvPathStrings } = require('./secrets-kv-refs');
|
|
116
|
+
const paths = collectUniqueKvPathStrings(envTemplate);
|
|
117
|
+
return paths.filter(pathStr => !secretPathExistsInStore(existingSecrets, pathStr));
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
/**
|
|
@@ -275,6 +277,8 @@ function loadExistingSecrets(resolvedPath) {
|
|
|
275
277
|
* @throws {Error} If save fails
|
|
276
278
|
*/
|
|
277
279
|
function saveSecretsFile(resolvedPath, secrets) {
|
|
280
|
+
const { assertWritableSecretsPathForTests } = require('./aifabrix-test-runtime-guard');
|
|
281
|
+
assertWritableSecretsPathForTests(resolvedPath);
|
|
278
282
|
const dir = path.dirname(resolvedPath);
|
|
279
283
|
if (!fs.existsSync(dir)) {
|
|
280
284
|
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
@@ -356,6 +360,8 @@ function appendSecretsToFile(resolvedPath, secrets) {
|
|
|
356
360
|
if (!secrets || typeof secrets !== 'object' || Object.keys(secrets).length === 0) {
|
|
357
361
|
return;
|
|
358
362
|
}
|
|
363
|
+
const { assertWritableSecretsPathForTests } = require('./aifabrix-test-runtime-guard');
|
|
364
|
+
assertWritableSecretsPathForTests(resolvedPath);
|
|
359
365
|
const dir = path.dirname(resolvedPath);
|
|
360
366
|
if (!fs.existsSync(dir)) {
|
|
361
367
|
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build missing-secrets errors with remediation hints (no secret values).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Missing kv:// error message for resolve and env generation
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const { formatMissingSecretsFileInfo } = require('./secrets-helpers');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} line
|
|
15
|
+
* @returns {boolean}
|
|
16
|
+
*/
|
|
17
|
+
function isCommentOrEmptyLine(line) {
|
|
18
|
+
const t = String(line).trim();
|
|
19
|
+
return t === '' || t.startsWith('#');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} kvRef - e.g. kv://hubspot-demo/clientSecret
|
|
24
|
+
* @returns {string}
|
|
25
|
+
*/
|
|
26
|
+
function kvRefToSecretSetKey(kvRef) {
|
|
27
|
+
const s = String(kvRef || '').trim();
|
|
28
|
+
return s.replace(/^kv:\/\//i, '');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {string} envTemplate
|
|
33
|
+
* @param {string} kvRef
|
|
34
|
+
* @returns {string|null}
|
|
35
|
+
*/
|
|
36
|
+
function findActiveEnvLineForKvRef(envTemplate, kvRef) {
|
|
37
|
+
if (!envTemplate || !kvRef) return null;
|
|
38
|
+
const needle = String(kvRef).trim();
|
|
39
|
+
for (const line of envTemplate.split('\n')) {
|
|
40
|
+
if (isCommentOrEmptyLine(line)) continue;
|
|
41
|
+
if (line.includes(needle)) return line.trim();
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const OAUTH_STALE_KV_PATH_RE = /\/(clientId|clientSecret|tokenUrl|authorizationUrl)$/i;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {string[]} missing
|
|
50
|
+
* @returns {boolean}
|
|
51
|
+
*/
|
|
52
|
+
function hasStaleOAuthTemplatePaths(missing) {
|
|
53
|
+
return missing.some((ref) => OAUTH_STALE_KV_PATH_RE.test(String(ref)));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {string} key
|
|
58
|
+
* @param {Object} secrets
|
|
59
|
+
* @returns {boolean}
|
|
60
|
+
*/
|
|
61
|
+
function isPopulatedSecretKey(key, secrets) {
|
|
62
|
+
const value = secrets[key];
|
|
63
|
+
return value !== undefined && value !== null && String(value).trim() !== '';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Other populated secret paths that share the same path segment after the namespace.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} pathStr - Secret path without kv:// (e.g. my-app-e2e/clientId)
|
|
70
|
+
* @param {Object} secrets
|
|
71
|
+
* @returns {string[]}
|
|
72
|
+
*/
|
|
73
|
+
function findPopulatedAlternateKvPaths(pathStr, secrets) {
|
|
74
|
+
if (!secrets || typeof secrets !== 'object' || !pathStr) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
const slash = pathStr.indexOf('/');
|
|
78
|
+
if (slash <= 0) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
const missingNamespace = pathStr.slice(0, slash);
|
|
82
|
+
const segment = pathStr.slice(slash + 1);
|
|
83
|
+
return Object.keys(secrets).filter((key) => {
|
|
84
|
+
if (typeof key !== 'string' || !key.includes('/')) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const keySlash = key.indexOf('/');
|
|
88
|
+
const namespace = key.slice(0, keySlash);
|
|
89
|
+
const keySegment = key.slice(keySlash + 1);
|
|
90
|
+
return (
|
|
91
|
+
keySegment === segment &&
|
|
92
|
+
namespace !== missingNamespace &&
|
|
93
|
+
isPopulatedSecretKey(key, secrets)
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @param {string[]} missing - kv:// refs
|
|
100
|
+
* @param {Object} [secrets]
|
|
101
|
+
* @param {string} [appName]
|
|
102
|
+
* @returns {string[]}
|
|
103
|
+
*/
|
|
104
|
+
function buildKvNamespaceAlternateHints(missing, secrets, appName) {
|
|
105
|
+
const lines = [];
|
|
106
|
+
const seen = new Set();
|
|
107
|
+
for (const ref of missing) {
|
|
108
|
+
const pathStr = kvRefToSecretSetKey(ref);
|
|
109
|
+
for (const alt of findPopulatedAlternateKvPaths(pathStr, secrets)) {
|
|
110
|
+
const line = `Found kv://${alt} in secrets; env.template references kv://${pathStr}.`;
|
|
111
|
+
if (seen.has(line)) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
seen.add(line);
|
|
115
|
+
lines.push(line);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (lines.length === 0) {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
const repairTail = appName
|
|
122
|
+
? `Use aifabrix secret set <key> for each missing path, or aifabrix repair ${appName} to align env.template.`
|
|
123
|
+
: 'Use aifabrix secret set <key> for each missing path, or repair to align env.template.';
|
|
124
|
+
lines.push(repairTail);
|
|
125
|
+
return lines;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @param {Object} opts
|
|
130
|
+
* @param {string[]} opts.missing
|
|
131
|
+
* @param {Object|string|null} [opts.secretsFilePaths]
|
|
132
|
+
* @param {string} [opts.appName]
|
|
133
|
+
* @param {string} [opts.envTemplatePath]
|
|
134
|
+
* @param {string} [opts.envTemplate]
|
|
135
|
+
* @param {Object} [opts.secrets]
|
|
136
|
+
* @returns {string}
|
|
137
|
+
*/
|
|
138
|
+
function buildMissingSecretsErrorMessage(opts) {
|
|
139
|
+
const { missing, secretsFilePaths, appName, envTemplatePath, envTemplate, secrets } = opts;
|
|
140
|
+
const fileInfo = formatMissingSecretsFileInfo(secretsFilePaths);
|
|
141
|
+
const lines = [`Missing secrets: ${missing.join(', ')}`];
|
|
142
|
+
if (fileInfo) lines.push(fileInfo.trim());
|
|
143
|
+
if (envTemplatePath) lines.push(`Env-template-path: ${envTemplatePath}`);
|
|
144
|
+
for (const ref of missing) {
|
|
145
|
+
const templateLine = findActiveEnvLineForKvRef(envTemplate, ref);
|
|
146
|
+
if (templateLine) lines.push(`Env-template-line: ${ref}|${templateLine}`);
|
|
147
|
+
}
|
|
148
|
+
if (appName) lines.push(`App-name: ${appName}`);
|
|
149
|
+
if (hasStaleOAuthTemplatePaths(missing) && appName) {
|
|
150
|
+
lines.push(`Stale-template-hint: oauth-leftover | repair ${appName}`);
|
|
151
|
+
}
|
|
152
|
+
for (const hint of buildKvNamespaceAlternateHints(missing, secrets, appName)) {
|
|
153
|
+
lines.push(`Alternate-kv-hint: ${hint}`);
|
|
154
|
+
}
|
|
155
|
+
lines.push('Remediation-hint: comment-or-delete-template-line | secret-set');
|
|
156
|
+
return `${lines.join('\n')}\n`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
module.exports = {
|
|
160
|
+
kvRefToSecretSetKey,
|
|
161
|
+
findActiveEnvLineForKvRef,
|
|
162
|
+
findPopulatedAlternateKvPaths,
|
|
163
|
+
buildMissingSecretsErrorMessage
|
|
164
|
+
};
|