@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
|
@@ -16,6 +16,31 @@ const config = require('./config');
|
|
|
16
16
|
const { decryptSecret, isEncrypted } = require('../utils/secrets-encryption');
|
|
17
17
|
const { ensureSecureFilePermissions } = require('../utils/secure-file-permissions');
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Whether an admin-secrets.env value must be double-quoted for safe round-trip parsing.
|
|
21
|
+
* @param {string} value
|
|
22
|
+
* @returns {boolean}
|
|
23
|
+
*/
|
|
24
|
+
function needsAdminEnvQuoting(value) {
|
|
25
|
+
const s = String(value ?? '');
|
|
26
|
+
return /[,\s#=]/.test(s) || s !== s.trim();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Format one KEY=value line for admin-secrets.env (quotes when needed).
|
|
31
|
+
* @param {string} key
|
|
32
|
+
* @param {string} value - Plaintext value (not yet encrypted)
|
|
33
|
+
* @returns {string}
|
|
34
|
+
*/
|
|
35
|
+
function formatAdminEnvLine(key, value) {
|
|
36
|
+
const safe = String(value ?? '').replace(/\n/g, ' ').trim();
|
|
37
|
+
if (!needsAdminEnvQuoting(safe)) {
|
|
38
|
+
return `${key}=${safe}`;
|
|
39
|
+
}
|
|
40
|
+
const escaped = safe.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
41
|
+
return `${key}="${escaped}"`;
|
|
42
|
+
}
|
|
43
|
+
|
|
19
44
|
/**
|
|
20
45
|
* Parse .env-style content into key-value map (excludes comments and empty lines).
|
|
21
46
|
* Values are trimmed; does not unescape quotes.
|
|
@@ -33,7 +58,9 @@ function parseAdminEnvContent(content) {
|
|
|
33
58
|
if (eq > 0) {
|
|
34
59
|
const key = trimmed.substring(0, eq).trim();
|
|
35
60
|
let value = trimmed.substring(eq + 1);
|
|
36
|
-
if (value.length >= 2 &&
|
|
61
|
+
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
|
|
62
|
+
value = value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
63
|
+
} else if (value.length >= 2 && value.startsWith('\'') && value.endsWith('\'')) {
|
|
37
64
|
value = value.slice(1, -1);
|
|
38
65
|
}
|
|
39
66
|
map[key] = value.trim();
|
|
@@ -94,8 +121,7 @@ function envObjectToContent(obj) {
|
|
|
94
121
|
const lines = [];
|
|
95
122
|
for (const [key, value] of Object.entries(obj)) {
|
|
96
123
|
if (key === undefined || key === '') continue;
|
|
97
|
-
|
|
98
|
-
lines.push(`${key}=${safe}`);
|
|
124
|
+
lines.push(formatAdminEnvLine(key, value));
|
|
99
125
|
}
|
|
100
126
|
return lines.join('\n');
|
|
101
127
|
}
|
|
@@ -103,5 +129,7 @@ function envObjectToContent(obj) {
|
|
|
103
129
|
module.exports = {
|
|
104
130
|
readAndDecryptAdminSecrets,
|
|
105
131
|
parseAdminEnvContent,
|
|
106
|
-
envObjectToContent
|
|
132
|
+
envObjectToContent,
|
|
133
|
+
needsAdminEnvQuoting,
|
|
134
|
+
formatAdminEnvLine
|
|
107
135
|
};
|
|
@@ -41,6 +41,9 @@ function attachConfigExtensions(exportsObj, deps) {
|
|
|
41
41
|
|
|
42
42
|
const { createScopedResourcesPreferenceFunctions } = require('../utils/config-scoped-resources-preference');
|
|
43
43
|
Object.assign(exportsObj, createScopedResourcesPreferenceFunctions(getConfig, saveConfig));
|
|
44
|
+
|
|
45
|
+
const { createSecretsPathFunctions } = require('../utils/config-secrets-path');
|
|
46
|
+
Object.assign(exportsObj, createSecretsPathFunctions(getConfig, saveConfig));
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
module.exports = { attachConfigExtensions };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `platform-controller` field in `config.yaml` — absolute Miso URL for setup / platform flows.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview config.yaml platform-controller helpers
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {() => Promise<Object>} getConfig
|
|
13
|
+
* @returns {Promise<string|null>}
|
|
14
|
+
*/
|
|
15
|
+
async function getPlatformControllerUrlFromConfig(getConfig) {
|
|
16
|
+
const cfg = await getConfig();
|
|
17
|
+
const raw = cfg && cfg['platform-controller'];
|
|
18
|
+
if (typeof raw === 'string' && raw.trim()) {
|
|
19
|
+
return raw.trim().replace(/\/+$/, '');
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {() => Promise<Object>} getConfig
|
|
26
|
+
* @param {(data: Object) => Promise<void>} saveConfig
|
|
27
|
+
* @param {(url: string) => string} normalizeControllerUrl
|
|
28
|
+
* @param {string} url
|
|
29
|
+
* @returns {Promise<void>}
|
|
30
|
+
*/
|
|
31
|
+
async function setPlatformControllerUrlInConfig(getConfig, saveConfig, normalizeControllerUrl, url) {
|
|
32
|
+
if (!url || typeof url !== 'string') {
|
|
33
|
+
throw new Error('Platform controller URL is required and must be a string');
|
|
34
|
+
}
|
|
35
|
+
const normalized = normalizeControllerUrl(url);
|
|
36
|
+
const cfg = await getConfig();
|
|
37
|
+
cfg['platform-controller'] = normalized;
|
|
38
|
+
await saveConfig(cfg);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = {
|
|
42
|
+
getPlatformControllerUrlFromConfig,
|
|
43
|
+
setPlatformControllerUrlInConfig
|
|
44
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `setupInstallationProfile` field in `config.yaml` (`dev` | `pro`).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Persists setup wizard installation profile
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const { normalizeInstallationProfile, INSTALLATION_PROFILE } = require('./setup-installation-profile');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {() => Promise<Object>} getConfig
|
|
15
|
+
* @returns {Promise<'dev'|'pro'>}
|
|
16
|
+
*/
|
|
17
|
+
async function getSetupInstallationProfileFromConfig(getConfig) {
|
|
18
|
+
const cfg = await getConfig();
|
|
19
|
+
if (cfg && cfg.setupInstallationProfile) {
|
|
20
|
+
return normalizeInstallationProfile(cfg.setupInstallationProfile);
|
|
21
|
+
}
|
|
22
|
+
return INSTALLATION_PROFILE.DEV;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {() => Promise<Object>} getConfig
|
|
27
|
+
* @param {(data: Object) => Promise<void>} saveConfig
|
|
28
|
+
* @param {unknown} profile
|
|
29
|
+
* @returns {Promise<'dev'|'pro'>}
|
|
30
|
+
*/
|
|
31
|
+
async function setSetupInstallationProfileInConfig(getConfig, saveConfig, profile) {
|
|
32
|
+
const normalized = normalizeInstallationProfile(profile);
|
|
33
|
+
const cfg = await getConfig();
|
|
34
|
+
cfg.setupInstallationProfile = normalized;
|
|
35
|
+
await saveConfig(cfg);
|
|
36
|
+
return normalized;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
getSetupInstallationProfileFromConfig,
|
|
41
|
+
setSetupInstallationProfileInConfig
|
|
42
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `setupPlatformMode` field in `config.yaml` (`single` | `single-traefik` | `full`).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Persists setup wizard platform mode
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
normalizeSetupPlatformMode,
|
|
13
|
+
SETUP_PLATFORM_MODE
|
|
14
|
+
} = require('./setup-platform-mode');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {() => Promise<Object>} getConfig
|
|
18
|
+
* @returns {Promise<'single'|'single-traefik'|'full'>}
|
|
19
|
+
*/
|
|
20
|
+
async function getSetupPlatformModeFromConfig(getConfig) {
|
|
21
|
+
const cfg = await getConfig();
|
|
22
|
+
if (cfg && cfg.setupPlatformMode) {
|
|
23
|
+
return normalizeSetupPlatformMode(cfg.setupPlatformMode);
|
|
24
|
+
}
|
|
25
|
+
return SETUP_PLATFORM_MODE.SINGLE;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {() => Promise<Object>} getConfig
|
|
30
|
+
* @param {(data: Object) => Promise<void>} saveConfig
|
|
31
|
+
* @param {unknown} mode
|
|
32
|
+
* @returns {Promise<'single'|'single-traefik'|'full'>}
|
|
33
|
+
*/
|
|
34
|
+
async function setSetupPlatformModeInConfig(getConfig, saveConfig, mode) {
|
|
35
|
+
const normalized = normalizeSetupPlatformMode(mode);
|
|
36
|
+
const cfg = await getConfig();
|
|
37
|
+
cfg.setupPlatformMode = normalized;
|
|
38
|
+
await saveConfig(cfg);
|
|
39
|
+
return normalized;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
getSetupPlatformModeFromConfig,
|
|
44
|
+
setSetupPlatformModeInConfig
|
|
45
|
+
};
|
|
46
|
+
|
package/lib/core/config.js
CHANGED
|
@@ -9,14 +9,24 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
const fs = require('fs').promises;
|
|
12
|
-
const path = require('path');
|
|
13
12
|
const yaml = require('js-yaml');
|
|
14
|
-
const os = require('os');
|
|
15
13
|
const { encryptToken, decryptToken, isTokenEncrypted } = require('../utils/token-encryption');
|
|
16
14
|
const { ensureSecureFilePermissions, ensureSecureDirPermissions } = require('../utils/secure-file-permissions');
|
|
17
15
|
const { getRuntimeConfigDir, getRuntimeConfigFile } = require('./config-runtime-paths');
|
|
18
16
|
const { getAdminEmailFromConfig, setAdminEmailInConfig } = require('./config-admin-email');
|
|
17
|
+
const {
|
|
18
|
+
getSetupInstallationProfileFromConfig,
|
|
19
|
+
setSetupInstallationProfileInConfig
|
|
20
|
+
} = require('./config-setup-installation-profile');
|
|
21
|
+
const {
|
|
22
|
+
getSetupPlatformModeFromConfig,
|
|
23
|
+
setSetupPlatformModeInConfig
|
|
24
|
+
} = require('./config-setup-platform-mode');
|
|
19
25
|
const { getRegisteredControllerUrlsWithLoader } = require('./config-registered-controller-urls');
|
|
26
|
+
const {
|
|
27
|
+
getPlatformControllerUrlFromConfig,
|
|
28
|
+
setPlatformControllerUrlInConfig
|
|
29
|
+
} = require('./config-platform-controller');
|
|
20
30
|
const { normalizeControllerUrl, validateAndNormalizeDeveloperId } = require('./config-normalize');
|
|
21
31
|
// Avoid importing paths.js here to prevent circular dependency; use shared runtime config dir helper.
|
|
22
32
|
// Config location: AIFABRIX_CONFIG dirname → AIFABRIX_HOME (with ~/.aifabrix fallback when config lives there) → ~/.aifabrix
|
|
@@ -34,6 +44,9 @@ function applyConfigDefaults(config) {
|
|
|
34
44
|
if (typeof config.environment === 'undefined') {
|
|
35
45
|
config.environment = 'dev';
|
|
36
46
|
}
|
|
47
|
+
if (typeof config.setupPlatformMode === 'undefined') {
|
|
48
|
+
config.setupPlatformMode = 'single';
|
|
49
|
+
}
|
|
37
50
|
// Ensure environments object exists
|
|
38
51
|
if (typeof config.environments !== 'object' || config.environments === null) {
|
|
39
52
|
config.environments = {};
|
|
@@ -59,6 +72,7 @@ function getDefaultConfig() {
|
|
|
59
72
|
return {
|
|
60
73
|
'developer-id': '0',
|
|
61
74
|
environment: 'dev',
|
|
75
|
+
setupPlatformMode: 'single',
|
|
62
76
|
controller: undefined,
|
|
63
77
|
environments: {},
|
|
64
78
|
device: {},
|
|
@@ -230,6 +244,12 @@ async function setAdminEmail(email) {
|
|
|
230
244
|
return setAdminEmailInConfig(getConfig, saveConfig, email);
|
|
231
245
|
}
|
|
232
246
|
|
|
247
|
+
/** @returns {Promise<'dev'|'pro'>} */
|
|
248
|
+
const getSetupInstallationProfile = () => getSetupInstallationProfileFromConfig(getConfig);
|
|
249
|
+
/** @param {unknown} profile @returns {Promise<'dev'|'pro'>} */
|
|
250
|
+
const setSetupInstallationProfile = (profile) =>
|
|
251
|
+
setSetupInstallationProfileInConfig(getConfig, saveConfig, profile);
|
|
252
|
+
|
|
233
253
|
/**
|
|
234
254
|
* Whether Traefik is enabled (`traefik: true` in config; infra compose includes the proxy).
|
|
235
255
|
* @returns {Promise<boolean>}
|
|
@@ -259,6 +279,21 @@ async function setCurrentEnvironment(environment) {
|
|
|
259
279
|
await saveConfig(config);
|
|
260
280
|
}
|
|
261
281
|
|
|
282
|
+
/**
|
|
283
|
+
* @returns {Promise<'single'|'full'>}
|
|
284
|
+
*/
|
|
285
|
+
async function getSetupPlatformMode() {
|
|
286
|
+
return getSetupPlatformModeFromConfig(getConfig);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* @param {unknown} mode
|
|
291
|
+
* @returns {Promise<'single'|'full'>}
|
|
292
|
+
*/
|
|
293
|
+
async function setSetupPlatformMode(mode) {
|
|
294
|
+
return setSetupPlatformModeInConfig(getConfig, saveConfig, mode);
|
|
295
|
+
}
|
|
296
|
+
|
|
262
297
|
/**
|
|
263
298
|
* Set controller URL in configuration
|
|
264
299
|
* @async
|
|
@@ -392,38 +427,11 @@ async function setSecretsEncryptionKey(key) {
|
|
|
392
427
|
*/
|
|
393
428
|
async function ensureSecretsEncryptionKey() {
|
|
394
429
|
const { ensureSecretsEncryptionKey: run } = require('./ensure-encryption-key');
|
|
430
|
+
const { createSecretsPathFunctions } = require('../utils/config-secrets-path');
|
|
431
|
+
const { getSecretsPath } = createSecretsPathFunctions(getConfig, saveConfig);
|
|
395
432
|
await run({ getSecretsEncryptionKey, setSecretsEncryptionKey, getSecretsPath });
|
|
396
433
|
}
|
|
397
434
|
|
|
398
|
-
/**
|
|
399
|
-
* Expand leading ~ to home directory so config paths like ~/.aifabrix/secrets.local.yaml resolve correctly.
|
|
400
|
-
* @param {string} filePath - Path that may start with ~ or ~/
|
|
401
|
-
* @returns {string} Path with ~ expanded, or unchanged if no leading ~
|
|
402
|
-
*/
|
|
403
|
-
function expandTilde(filePath) {
|
|
404
|
-
if (!filePath || typeof filePath !== 'string') return filePath;
|
|
405
|
-
if (filePath === '~') return os.homedir();
|
|
406
|
-
if (filePath.startsWith('~/') || filePath.startsWith('~' + path.sep)) {
|
|
407
|
-
return path.join(os.homedir(), filePath.slice(2));
|
|
408
|
-
}
|
|
409
|
-
return filePath;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
async function getSecretsPath() {
|
|
413
|
-
const config = await getConfig();
|
|
414
|
-
const raw = config['aifabrix-secrets'] || config['secrets-path'] || null;
|
|
415
|
-
return raw ? expandTilde(raw) : null;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
async function setSecretsPath(secretsPath) {
|
|
419
|
-
if (typeof secretsPath !== 'string') {
|
|
420
|
-
throw new Error('Secrets path is required and must be a string');
|
|
421
|
-
}
|
|
422
|
-
const config = await getConfig();
|
|
423
|
-
config['aifabrix-secrets'] = secretsPath.trim() || undefined;
|
|
424
|
-
await saveConfig(config);
|
|
425
|
-
}
|
|
426
|
-
|
|
427
435
|
// Create exports object
|
|
428
436
|
const exportsObj = {
|
|
429
437
|
getConfig,
|
|
@@ -436,6 +444,10 @@ const exportsObj = {
|
|
|
436
444
|
getTlsEnabled,
|
|
437
445
|
getAdminEmail,
|
|
438
446
|
setAdminEmail,
|
|
447
|
+
getSetupInstallationProfile,
|
|
448
|
+
setSetupInstallationProfile,
|
|
449
|
+
getSetupPlatformMode,
|
|
450
|
+
setSetupPlatformMode,
|
|
439
451
|
getTraefikEnabled,
|
|
440
452
|
setCurrentEnvironment,
|
|
441
453
|
resolveEnvironment,
|
|
@@ -446,11 +458,11 @@ const exportsObj = {
|
|
|
446
458
|
getSecretsEncryptionKey,
|
|
447
459
|
setSecretsEncryptionKey,
|
|
448
460
|
ensureSecretsEncryptionKey,
|
|
449
|
-
getSecretsPath,
|
|
450
|
-
setSecretsPath,
|
|
451
461
|
normalizeControllerUrl,
|
|
452
462
|
setControllerUrl,
|
|
453
463
|
getControllerUrl,
|
|
464
|
+
getPlatformControllerUrl: () => getPlatformControllerUrlFromConfig(getConfig),
|
|
465
|
+
setPlatformControllerUrl: (url) => setPlatformControllerUrlInConfig(getConfig, saveConfig, normalizeControllerUrl, url),
|
|
454
466
|
getRegisteredControllerUrls,
|
|
455
467
|
get CONFIG_DIR() {
|
|
456
468
|
return getRuntimeConfigDir();
|
|
@@ -460,9 +472,7 @@ const exportsObj = {
|
|
|
460
472
|
}
|
|
461
473
|
};
|
|
462
474
|
|
|
463
|
-
//
|
|
464
|
-
// After getConfig() or getDeveloperId() is called, config.developerId will be available
|
|
465
|
-
// Developer ID: 0 = default infra, > 0 = developer-specific
|
|
475
|
+
// Developer ID: 0 = default infra, > 0 = developer-specific (getter after getConfig loads cache)
|
|
466
476
|
Object.defineProperty(exportsObj, 'developerId', {
|
|
467
477
|
get() {
|
|
468
478
|
return cachedDeveloperId !== null ? cachedDeveloperId : '0'; // Default to "0" if not loaded yet
|
|
@@ -20,6 +20,7 @@ const {
|
|
|
20
20
|
const { createDefaultSecrets } = require('../utils/secrets-generator');
|
|
21
21
|
const pathsUtil = require('../utils/paths');
|
|
22
22
|
const { loadSecrets } = require('./secrets-load');
|
|
23
|
+
const { formatAdminEnvLine } = require('./admin-secrets');
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Writes admin env key-value pairs to content; encrypts values when encryption key is set.
|
|
@@ -34,7 +35,7 @@ async function formatAdminSecretsContent(adminObj) {
|
|
|
34
35
|
for (const [k, v] of Object.entries(adminObj)) {
|
|
35
36
|
const value = (v === null || v === undefined) ? '' : String(v).replace(/\n/g, ' ').trim();
|
|
36
37
|
const valueToWrite = encryptionKey ? encryptSecret(value, encryptionKey) : value;
|
|
37
|
-
lines.push(
|
|
38
|
+
lines.push(formatAdminEnvLine(k, valueToWrite));
|
|
38
39
|
}
|
|
39
40
|
return lines.join('\n');
|
|
40
41
|
}
|
|
@@ -76,7 +77,10 @@ function buildLocalAdminSecretsObject(secrets, infraDefaults) {
|
|
|
76
77
|
PGADMIN_DEFAULT_PASSWORD: postgresPassword,
|
|
77
78
|
REDIS_HOST: 'local:redis:6379:0:',
|
|
78
79
|
REDIS_COMMANDER_USER: 'admin',
|
|
79
|
-
REDIS_COMMANDER_PASSWORD: postgresPassword
|
|
80
|
+
REDIS_COMMANDER_PASSWORD: postgresPassword,
|
|
81
|
+
KEYCLOAK_ADMIN_USERNAME: 'admin',
|
|
82
|
+
KEYCLOAK_ADMIN_PASSWORD: postgresPassword,
|
|
83
|
+
PLATFORM_ADMIN_PASSWORD: postgresPassword
|
|
80
84
|
};
|
|
81
85
|
}
|
|
82
86
|
|
|
@@ -204,6 +204,8 @@ function summarizeSecretsStoreForLog(target) {
|
|
|
204
204
|
*/
|
|
205
205
|
function logNewSecretValuesWritten(added, target) {
|
|
206
206
|
if (!Array.isArray(added) || added.length === 0) return;
|
|
207
|
+
const { isSetupQuietOutput } = require('../utils/setup-quiet-output');
|
|
208
|
+
if (isSetupQuietOutput()) return;
|
|
207
209
|
const where = summarizeSecretsStoreForLog(target);
|
|
208
210
|
logger.log(
|
|
209
211
|
formatSuccessLine(
|
|
@@ -352,6 +354,8 @@ async function ensureSecretsFromEnvTemplate(envTemplatePathOrContent, options =
|
|
|
352
354
|
* @returns {Promise<void>}
|
|
353
355
|
*/
|
|
354
356
|
async function writeSecretToStoreFile(filePath, key, strValue) {
|
|
357
|
+
const { assertWritableSecretsPathForTests } = require('../utils/aifabrix-test-runtime-guard');
|
|
358
|
+
assertWritableSecretsPathForTests(filePath);
|
|
355
359
|
const existing = loadExistingSecrets(filePath);
|
|
356
360
|
const encryptionKey = await config.getSecretsEncryptionKey();
|
|
357
361
|
let valueToWrite = strValue;
|
|
@@ -15,7 +15,6 @@ const config = require('./config');
|
|
|
15
15
|
const {
|
|
16
16
|
interpolateEnvVars,
|
|
17
17
|
collectMissingSecrets,
|
|
18
|
-
formatMissingSecretsFileInfo,
|
|
19
18
|
replaceKvInContent,
|
|
20
19
|
loadEnvTemplate,
|
|
21
20
|
adjustLocalEnvPortsInContent,
|
|
@@ -43,6 +42,8 @@ const {
|
|
|
43
42
|
rewriteDockerManifestPublicPortEnvLine
|
|
44
43
|
} = require('../utils/docker-manifest-public-port');
|
|
45
44
|
const { loadSecrets } = require('./secrets-load');
|
|
45
|
+
const { buildMissingSecretsErrorMessage } = require('../utils/secrets-missing-error');
|
|
46
|
+
const { commentOrphanKvEnvLines } = require('../utils/env-kv-orphan-lines');
|
|
46
47
|
|
|
47
48
|
/**
|
|
48
49
|
* Resolves kv:// references in environment template
|
|
@@ -54,10 +55,24 @@ const { loadSecrets } = require('./secrets-load');
|
|
|
54
55
|
* @param {string} [environment='local'] - Environment context (docker/local)
|
|
55
56
|
* @param {Object|string|null} [secretsFilePaths] - Paths object with userPath and buildPath, or string path (for backward compatibility)
|
|
56
57
|
* @param {string} [appName] - Application name (optional, for error messages)
|
|
58
|
+
* @param {Object|null} [resolveOpts] - Scoped kv (`envKey`, `effective`) and/or `envTemplatePath` for error hints
|
|
57
59
|
* @returns {Promise<string>} Resolved environment content
|
|
58
60
|
* @throws {Error} If kv:// reference cannot be resolved
|
|
59
61
|
*/
|
|
60
|
-
async function resolveKvReferences(
|
|
62
|
+
async function resolveKvReferences(
|
|
63
|
+
envTemplate,
|
|
64
|
+
secrets,
|
|
65
|
+
environment = 'local',
|
|
66
|
+
secretsFilePaths = null,
|
|
67
|
+
appName = null,
|
|
68
|
+
resolveOpts = null
|
|
69
|
+
) {
|
|
70
|
+
const envTemplatePath =
|
|
71
|
+
resolveOpts && typeof resolveOpts === 'object' ? resolveOpts.envTemplatePath || null : null;
|
|
72
|
+
const scopedKv =
|
|
73
|
+
resolveOpts && typeof resolveOpts === 'object' && (resolveOpts.envKey !== undefined || resolveOpts.effective !== undefined)
|
|
74
|
+
? { envKey: resolveOpts.envKey, effective: resolveOpts.effective }
|
|
75
|
+
: null;
|
|
61
76
|
const os = require('os');
|
|
62
77
|
|
|
63
78
|
let developerId = null;
|
|
@@ -77,9 +92,16 @@ async function resolveKvReferences(envTemplate, secrets, environment = 'local',
|
|
|
77
92
|
const resolved = interpolateEnvVars(envTemplate, envVars);
|
|
78
93
|
const missing = collectMissingSecrets(resolved, secrets, scopedKv);
|
|
79
94
|
if (missing.length > 0) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
95
|
+
throw new Error(
|
|
96
|
+
buildMissingSecretsErrorMessage({
|
|
97
|
+
missing,
|
|
98
|
+
secrets,
|
|
99
|
+
secretsFilePaths,
|
|
100
|
+
appName,
|
|
101
|
+
envTemplatePath,
|
|
102
|
+
envTemplate
|
|
103
|
+
})
|
|
104
|
+
);
|
|
83
105
|
}
|
|
84
106
|
return replaceKvInContent(resolved, secrets, envVars, scopedKv);
|
|
85
107
|
}
|
|
@@ -178,7 +200,11 @@ async function generateEnvContent(appName, secretsPath, environment = 'local', f
|
|
|
178
200
|
const secrets = await loadSecrets(secretsPath, appName);
|
|
179
201
|
const { runEnvKey, effective } = await buildScopedKvContext(appPath, options);
|
|
180
202
|
const scopedKv = { envKey: runEnvKey, effective };
|
|
181
|
-
let resolved = await resolveKvReferences(template, secrets, environment, secretsPaths, appName,
|
|
203
|
+
let resolved = await resolveKvReferences(template, secrets, environment, secretsPaths, appName, {
|
|
204
|
+
envKey: runEnvKey,
|
|
205
|
+
effective,
|
|
206
|
+
envTemplatePath: templatePath
|
|
207
|
+
});
|
|
182
208
|
if (!secretsPath) {
|
|
183
209
|
await materializeResolvedKvSecretsToUserLocal(template, secrets, scopedKv, options);
|
|
184
210
|
}
|
|
@@ -227,42 +253,6 @@ function parseEnvContentToMap(content) {
|
|
|
227
253
|
return map;
|
|
228
254
|
}
|
|
229
255
|
|
|
230
|
-
/**
|
|
231
|
-
* Merges new .env content with existing .env: newly resolved content wins for keys it
|
|
232
|
-
* defines (so project secrets take effect when re-running). Keys only in existing .env
|
|
233
|
-
* are appended so manual additions are kept.
|
|
234
|
-
*
|
|
235
|
-
* @param {string} newContent - Newly generated .env content (from template + loadSecrets)
|
|
236
|
-
* @param {Object.<string, string>} existingMap - Existing key-to-value map from current .env
|
|
237
|
-
* @returns {string} Merged content: new values for keys in newContent, plus extra existing keys
|
|
238
|
-
*/
|
|
239
|
-
function mergeEnvContentPreservingExisting(newContent, existingMap) {
|
|
240
|
-
const lines = newContent.split(/\r?\n/);
|
|
241
|
-
const newKeys = new Set();
|
|
242
|
-
const out = [];
|
|
243
|
-
for (const line of lines) {
|
|
244
|
-
const trimmed = line.trim();
|
|
245
|
-
if (!trimmed || trimmed.startsWith('#')) {
|
|
246
|
-
out.push(line);
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
249
|
-
const eq = trimmed.indexOf('=');
|
|
250
|
-
if (eq > 0) {
|
|
251
|
-
const key = trimmed.substring(0, eq).trim();
|
|
252
|
-
newKeys.add(key);
|
|
253
|
-
}
|
|
254
|
-
out.push(line);
|
|
255
|
-
}
|
|
256
|
-
if (existingMap && Object.keys(existingMap).length > 0) {
|
|
257
|
-
for (const key of Object.keys(existingMap)) {
|
|
258
|
-
if (!newKeys.has(key)) {
|
|
259
|
-
out.push(`${key}=${existingMap[key]}`);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return out.join('\n');
|
|
264
|
-
}
|
|
265
|
-
|
|
266
256
|
/**
|
|
267
257
|
* Merges a key-value map into existing .env file content, preserving comments and blank lines.
|
|
268
258
|
* For each KEY=value line in existing content, replaces value with newMap[KEY] when the key exists
|
|
@@ -320,15 +310,27 @@ function mergeEnvMapIntoContent(existingContent, newMap, options = {}) {
|
|
|
320
310
|
|
|
321
311
|
/**
|
|
322
312
|
* Resolves content to write for .env: merges with existing file when present.
|
|
313
|
+
* Uses {@link mergeEnvMapIntoContent} so new template keys are appended and comments in the
|
|
314
|
+
* existing file are preserved (unlike a full replace).
|
|
315
|
+
*
|
|
323
316
|
* @param {string} resolved - Newly generated content
|
|
324
317
|
* @param {string} pathToPreserve - Path to existing .env to merge from (or null)
|
|
318
|
+
* @param {Object} [options]
|
|
319
|
+
* @param {boolean} [options.freshEnv=false] - When true, write full resolved content (no merge)
|
|
325
320
|
* @returns {string} Content to write
|
|
326
321
|
*/
|
|
327
|
-
function resolveEnvContentToWrite(resolved, pathToPreserve) {
|
|
328
|
-
if (!pathToPreserve || !fs.existsSync(pathToPreserve))
|
|
322
|
+
function resolveEnvContentToWrite(resolved, pathToPreserve, options = {}) {
|
|
323
|
+
if (options.freshEnv === true || !pathToPreserve || !fs.existsSync(pathToPreserve)) {
|
|
324
|
+
return resolved;
|
|
325
|
+
}
|
|
329
326
|
const existingContent = fs.readFileSync(pathToPreserve, 'utf8');
|
|
330
|
-
const
|
|
331
|
-
|
|
327
|
+
const resolvedMap = parseEnvContentToMap(resolved);
|
|
328
|
+
let merged = mergeEnvMapIntoContent(existingContent, resolvedMap, { appendMissingFromNewMap: true });
|
|
329
|
+
if (options.pruneOrphanKv === true) {
|
|
330
|
+
const { content: pruned } = commentOrphanKvEnvLines(merged, new Set(Object.keys(resolvedMap)));
|
|
331
|
+
merged = pruned;
|
|
332
|
+
}
|
|
333
|
+
return merged;
|
|
332
334
|
}
|
|
333
335
|
|
|
334
336
|
/**
|
|
@@ -381,14 +383,18 @@ function resolveEnvContentToWrite(resolved, pathToPreserve) {
|
|
|
381
383
|
async function writeResolvedEnv({ appName, envPath, resolved, variablesPath, secretsPath, opts }) {
|
|
382
384
|
const preservePath = opts.preserveFromPath !== undefined && opts.preserveFromPath !== null ? opts.preserveFromPath : null;
|
|
383
385
|
const pathToPreserve = preservePath !== null ? preservePath : envPath;
|
|
384
|
-
const toWrite = resolveEnvContentToWrite(resolved, pathToPreserve
|
|
386
|
+
const toWrite = resolveEnvContentToWrite(resolved, pathToPreserve, {
|
|
387
|
+
freshEnv: opts.freshEnv === true,
|
|
388
|
+
pruneOrphanKv: opts.envOnly === true && opts.freshEnv !== true
|
|
389
|
+
});
|
|
385
390
|
fs.writeFileSync(envPath, toWrite, { mode: 0o600 });
|
|
386
391
|
|
|
387
392
|
if (!opts.skipOutputPath) {
|
|
388
393
|
const { processEnvVariables } = require('../utils/env-copy');
|
|
389
394
|
await processEnvVariables(envPath, variablesPath, appName, secretsPath, {
|
|
390
395
|
preferLocalEnvOutputPath: opts.preferLocalEnvOutputPath === true,
|
|
391
|
-
appPath: opts.appPath || null
|
|
396
|
+
appPath: opts.appPath || null,
|
|
397
|
+
freshEnv: opts.freshEnv === true
|
|
392
398
|
});
|
|
393
399
|
}
|
|
394
400
|
return envPath;
|
|
@@ -207,7 +207,16 @@ async function resolveAndGetEnvMap(appName, options = {}) {
|
|
|
207
207
|
let content = await secrets.generateEnvContent(appName, secretsPath, environment, force);
|
|
208
208
|
content = await injectRegistryTokens(content, secretsPath, appName);
|
|
209
209
|
content = await injectBashPrefixedExportLines(content, secretsPath, appName);
|
|
210
|
-
|
|
210
|
+
const map = parseEnvContentToMap(content);
|
|
211
|
+
try {
|
|
212
|
+
const adminSecrets = require('./admin-secrets');
|
|
213
|
+
const { applyAdminSecretsPlatformOverlay } = require('./admin-secrets-env-overlay');
|
|
214
|
+
const adminObj = await adminSecrets.readAndDecryptAdminSecrets();
|
|
215
|
+
applyAdminSecretsPlatformOverlay(map, adminObj);
|
|
216
|
+
} catch {
|
|
217
|
+
// admin-secrets.env optional until up-infra/setup
|
|
218
|
+
}
|
|
219
|
+
return map;
|
|
211
220
|
}
|
|
212
221
|
|
|
213
222
|
module.exports = { resolveAndWriteEnvFile, resolveAndGetEnvMap };
|
|
@@ -43,6 +43,8 @@ async function syncLiteralKvSecretsFromCliOverrides(
|
|
|
43
43
|
const val = ipc.generateValueFromCatalogEntry(key, entry, crypto, placeholderContext);
|
|
44
44
|
await setSecretInStore(key, val);
|
|
45
45
|
}
|
|
46
|
+
const { isSetupQuietOutput } = require('../utils/setup-quiet-output');
|
|
47
|
+
if (isSetupQuietOutput()) return;
|
|
46
48
|
logger.log(
|
|
47
49
|
`Updated ${keys.length} secret(s) in store (catalog literals using {{${placeholder}}}).`
|
|
48
50
|
);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup installation profile identifiers (`dev` / `pro`).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Normalization for setup wizard profile (local install, not login environment)
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/** @enum {string} */
|
|
12
|
+
const INSTALLATION_PROFILE = Object.freeze({
|
|
13
|
+
DEV: 'dev',
|
|
14
|
+
PRO: 'pro'
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {unknown} value
|
|
19
|
+
* @returns {'dev'|'pro'}
|
|
20
|
+
*/
|
|
21
|
+
function normalizeInstallationProfile(value) {
|
|
22
|
+
const raw = String(value ?? '')
|
|
23
|
+
.trim()
|
|
24
|
+
.toLowerCase();
|
|
25
|
+
if (raw === INSTALLATION_PROFILE.PRO || raw === 'production') {
|
|
26
|
+
return INSTALLATION_PROFILE.PRO;
|
|
27
|
+
}
|
|
28
|
+
return INSTALLATION_PROFILE.DEV;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {unknown} value
|
|
33
|
+
* @returns {boolean}
|
|
34
|
+
*/
|
|
35
|
+
function isProInstallationProfile(value) {
|
|
36
|
+
return normalizeInstallationProfile(value) === INSTALLATION_PROFILE.PRO;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
INSTALLATION_PROFILE,
|
|
41
|
+
normalizeInstallationProfile,
|
|
42
|
+
isProInstallationProfile
|
|
43
|
+
};
|