@aifabrix/builder 2.43.0 → 2.44.1
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/anchor-docs.mdc +15 -0
- package/.cursor/rules/cli-layout.mdc +75 -0
- package/.cursor/rules/project-rules.mdc +8 -0
- package/.npmrc.token +1 -0
- package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +1 -0
- package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/README.md +1 -1
- package/anchor-docs/README.md +10 -0
- package/anchor-docs/_TEMPLATE +24 -0
- package/bin/aifabrix.js +13 -4
- package/integration/hubspot-test/README.md +31 -0
- package/integration/hubspot-test/create-hubspot.js +5 -5
- package/integration/hubspot-test/hubspot-test-datasource-company.json +58 -462
- package/integration/hubspot-test/hubspot-test-datasource-contact.json +61 -555
- package/integration/hubspot-test/hubspot-test-datasource-deal.json +63 -506
- package/integration/hubspot-test/hubspot-test-datasource-users.json +42 -83
- package/integration/hubspot-test/hubspot-test-deploy.json +3 -3
- package/integration/hubspot-test/test-dataplane-down-tests.js +1 -7
- package/integration/hubspot-test/test-dataplane-down.js +3 -3
- package/integration/hubspot-test/test.js +35 -43
- package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
- package/integration/roundtrip-test-local/README.md +144 -0
- package/integration/roundtrip-test-local/application.yaml +13 -0
- package/integration/roundtrip-test-local/env.template +15 -0
- package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
- package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
- package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
- package/integration/roundtrip-test-local2/README.md +144 -0
- package/integration/roundtrip-test-local2/application.yaml +13 -0
- package/integration/roundtrip-test-local2/env.template +15 -0
- package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
- package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
- package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
- package/integration/test/wizard.yaml +8 -0
- package/jest.config.default.js +10 -0
- package/jest.config.integration.fixtures.js +22 -0
- package/jest.config.integration.js +21 -18
- package/jest.config.isolated.js +10 -0
- package/jest.projects.js +301 -0
- package/lib/api/certificates.api.js +62 -0
- package/lib/api/datasources-core.api.js +3 -3
- package/lib/api/dev-mtls-request.js +110 -0
- package/lib/api/dev-server-https.js +145 -0
- package/lib/api/dev.api.js +133 -144
- package/lib/api/index.js +11 -3
- package/lib/api/pipeline.api.js +67 -20
- package/lib/api/types/certificates.types.js +48 -0
- package/lib/api/types/dev.types.js +4 -3
- package/lib/api/types/pipeline.types.js +8 -5
- package/lib/api/types/validation-run.types.js +56 -0
- package/lib/api/validation-run.api.js +111 -0
- package/lib/api/validation-runner.js +109 -0
- package/lib/app/certification-show-enrich.js +129 -0
- package/lib/app/certification-verify-rows.js +60 -0
- package/lib/app/config.js +1 -1
- package/lib/app/deploy-status-display.js +2 -2
- package/lib/app/deploy.js +7 -6
- package/lib/app/display.js +2 -1
- package/lib/app/dockerfile.js +3 -2
- package/lib/app/down.js +2 -1
- package/lib/app/helpers.js +6 -5
- package/lib/app/index.js +27 -8
- package/lib/app/list.js +7 -6
- package/lib/app/push.js +4 -3
- package/lib/app/register.js +16 -7
- package/lib/app/rotate-secret.js +14 -13
- package/lib/app/run-container-start.js +184 -0
- package/lib/app/run-docker-fallback.js +108 -0
- package/lib/app/run-env-compose.js +30 -42
- package/lib/app/run-helpers.js +49 -126
- package/lib/app/run-infra-requirements.js +30 -0
- package/lib/app/run-resolve-image.js +21 -0
- package/lib/app/run.js +74 -21
- package/lib/app/show-display.js +44 -1
- package/lib/app/show.js +93 -9
- package/lib/build/index.js +13 -10
- package/lib/certification/cli-cert-sync-skip.js +21 -0
- package/lib/certification/merge-certification-from-artifact.js +185 -0
- package/lib/certification/post-unified-cert-sync.js +33 -0
- package/lib/certification/sync-after-external-command.js +52 -0
- package/lib/certification/sync-system-certification.js +197 -0
- package/lib/cli/index.js +2 -0
- package/lib/cli/setup-app.help.js +67 -0
- package/lib/cli/setup-app.js +61 -121
- package/lib/cli/setup-app.test-commands.js +195 -0
- package/lib/cli/setup-auth.js +19 -5
- package/lib/cli/setup-credential-deployment.js +22 -8
- package/lib/cli/setup-dev-path-commands.js +124 -0
- package/lib/cli/setup-dev.js +170 -113
- package/lib/cli/setup-environment.js +7 -1
- package/lib/cli/setup-external-system.js +84 -23
- package/lib/cli/setup-infra.js +126 -47
- package/lib/cli/setup-parameters.js +32 -0
- package/lib/cli/setup-secrets.js +137 -18
- package/lib/cli/setup-service-user.js +1 -1
- package/lib/cli/setup-utility.js +54 -22
- package/lib/commands/app-down.js +5 -7
- package/lib/commands/app-install.js +14 -7
- package/lib/commands/app-logs.js +13 -10
- package/lib/commands/app-shell.js +4 -1
- package/lib/commands/app-test.js +25 -19
- package/lib/commands/app.js +32 -11
- package/lib/commands/auth-config.js +6 -6
- package/lib/commands/auth-status.js +4 -3
- package/lib/commands/credential-env.js +4 -3
- package/lib/commands/credential-list.js +5 -4
- package/lib/commands/credential-push.js +4 -3
- package/lib/commands/datasource-unified-test-cli.js +428 -0
- package/lib/commands/datasource-unified-test-cli.options.js +191 -0
- package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +106 -0
- package/lib/commands/datasource-validation-cli.js +143 -0
- package/lib/commands/datasource.js +125 -95
- package/lib/commands/deployment-list.js +6 -5
- package/lib/commands/dev-cli-handlers.js +122 -18
- package/lib/commands/dev-down.js +4 -3
- package/lib/commands/dev-init.js +231 -116
- package/lib/commands/dev-show-display.js +473 -0
- package/lib/commands/login-credentials.js +3 -2
- package/lib/commands/login-device.js +4 -3
- package/lib/commands/login.js +5 -4
- package/lib/commands/logout.js +8 -7
- package/lib/commands/parameters-validate.js +54 -0
- package/lib/commands/repair-datasource.js +314 -68
- package/lib/commands/repair-env-template.js +2 -2
- package/lib/commands/repair.js +21 -3
- package/lib/commands/secrets-list.js +23 -12
- package/lib/commands/secrets-remove-all.js +220 -0
- package/lib/commands/secrets-remove.js +21 -12
- package/lib/commands/secrets-set.js +21 -12
- package/lib/commands/secrets-validate.js +4 -4
- package/lib/commands/secure.js +10 -9
- package/lib/commands/service-user.js +26 -25
- package/lib/commands/test-e2e-external.js +27 -1
- package/lib/commands/up-common.js +3 -2
- package/lib/commands/up-dataplane.js +29 -16
- package/lib/commands/up-miso.js +19 -29
- package/lib/commands/upload.js +149 -39
- package/lib/commands/wizard-core-helpers.js +1 -1
- package/lib/commands/wizard-dataplane.js +4 -3
- package/lib/commands/wizard-helpers.js +3 -3
- package/lib/commands/wizard.js +2 -2
- package/lib/core/admin-secrets.js +14 -5
- package/lib/core/audit-logger.js +12 -4
- package/lib/core/config-attach-extensions.js +46 -0
- package/lib/core/config-runtime-paths.js +29 -0
- package/lib/core/config.js +55 -56
- package/lib/core/diff.js +3 -2
- package/lib/core/ensure-encryption-key.js +1 -1
- package/lib/core/secrets-ensure-infra.js +77 -0
- package/lib/core/secrets-ensure.js +120 -64
- package/lib/core/secrets-env-write.js +35 -7
- package/lib/core/secrets-infra-placeholder-sync.js +61 -0
- package/lib/core/secrets.js +200 -37
- package/lib/core/templates-env.js +4 -3
- package/lib/datasource/abac-validator.js +1 -10
- package/lib/datasource/deploy.js +75 -53
- package/lib/datasource/field-reference-validator.js +9 -6
- package/lib/datasource/integration-context.js +63 -0
- package/lib/datasource/list.js +8 -7
- package/lib/datasource/log-viewer.js +189 -67
- package/lib/datasource/resolve-app.js +4 -4
- package/lib/datasource/test-e2e.js +113 -146
- package/lib/datasource/test-integration.js +114 -122
- package/lib/datasource/unified-validation-run-body.js +68 -0
- package/lib/datasource/unified-validation-run-post.js +23 -0
- package/lib/datasource/unified-validation-run-resolve.js +43 -0
- package/lib/datasource/unified-validation-run.js +93 -0
- package/lib/datasource/validate.js +157 -13
- package/lib/deployment/deployer.js +4 -3
- package/lib/deployment/environment.js +7 -6
- package/lib/deployment/push.js +17 -8
- package/lib/external-system/delete.js +4 -3
- package/lib/external-system/deploy.js +166 -53
- package/lib/external-system/download-helpers.js +1 -1
- package/lib/external-system/download.js +7 -6
- package/lib/external-system/generator.js +92 -6
- package/lib/external-system/integration-test-dispatch.js +26 -0
- package/lib/external-system/test-execution.js +5 -1
- package/lib/external-system/test-helpers.js +0 -4
- package/lib/external-system/test-system-level-helpers.js +110 -0
- package/lib/external-system/test-system-level.js +83 -44
- package/lib/external-system/test.js +59 -8
- package/lib/generator/builders.js +23 -11
- package/lib/generator/deploy-manifest-azure-kv.js +81 -0
- package/lib/generator/external.js +16 -4
- package/lib/generator/helpers.js +58 -3
- package/lib/generator/index.js +4 -0
- package/lib/generator/split-readme.js +12 -7
- package/lib/generator/split-variables.js +2 -1
- package/lib/generator/split.js +1 -1
- package/lib/generator/wizard-readme.js +3 -3
- package/lib/generator/wizard.js +8 -8
- package/lib/infrastructure/compose.js +70 -7
- package/lib/infrastructure/helpers-docker-check.js +67 -0
- package/lib/infrastructure/helpers.js +203 -42
- package/lib/infrastructure/index.js +31 -18
- package/lib/infrastructure/services.js +21 -67
- package/lib/internal/fs-real-sync.js +104 -0
- package/lib/internal/node-fs.js +98 -0
- package/lib/parameters/database-secret-values.js +173 -0
- package/lib/parameters/infra-kv-discovery.js +121 -0
- package/lib/parameters/infra-parameter-catalog.js +458 -0
- package/lib/parameters/infra-parameter-validate.js +64 -0
- package/lib/schema/application-schema.json +37 -17
- package/lib/schema/datasource-test-run.schema.json +493 -0
- package/lib/schema/deployment-rules.yaml +102 -63
- package/lib/schema/external-datasource.schema.json +1200 -442
- package/lib/schema/external-system.schema.json +203 -5
- package/lib/schema/flag-map-validation-run.json +31 -0
- package/lib/schema/infra-parameter.schema.json +106 -0
- package/lib/schema/infra.parameter.yaml +421 -0
- package/lib/schema/type/credential-auth-templates.json +40 -0
- package/lib/schema/type/document-storage.json +226 -0
- package/lib/schema/type/message-service.json +123 -0
- package/lib/schema/type/vector-store.json +88 -0
- package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
- package/lib/utils/api-error-handler.js +2 -2
- package/lib/utils/api.js +77 -17
- package/lib/utils/app-register-api.js +3 -2
- package/lib/utils/app-register-auth.js +1 -1
- package/lib/utils/app-register-config.js +4 -4
- package/lib/utils/app-register-display.js +3 -2
- package/lib/utils/app-register-validator.js +3 -2
- package/lib/utils/app-run-containers.js +26 -22
- package/lib/utils/app-scoped-config.js +31 -0
- package/lib/utils/app-service-env-from-builder.js +164 -0
- package/lib/utils/build-copy.js +1 -1
- package/lib/utils/build-helpers.js +20 -20
- package/lib/utils/build-resolve-image.js +165 -0
- package/lib/utils/cli-layout-chalk.js +8 -0
- package/lib/utils/cli-test-layout-chalk.js +267 -0
- package/lib/utils/cli-utils.js +88 -11
- package/lib/utils/compose-db-passwords.js +138 -0
- package/lib/utils/compose-generate-docker-compose.js +216 -0
- package/lib/utils/compose-generator.js +197 -291
- package/lib/utils/compose-miso-env.js +18 -0
- package/lib/utils/compose-traefik-ingress-base.js +158 -0
- package/lib/utils/config-paths.js +166 -7
- package/lib/utils/config-scoped-resources-preference.js +41 -0
- package/lib/utils/configuration-env-resolver.js +11 -8
- package/lib/utils/controller-deployment-outcome.js +68 -0
- package/lib/utils/credential-display.js +2 -2
- package/lib/utils/credential-secrets-env.js +5 -5
- package/lib/utils/dataplane-pipeline-warning.js +4 -3
- package/lib/utils/datasource-test-run-capability-scope.js +43 -0
- package/lib/utils/datasource-test-run-certificate-tty.js +82 -0
- package/lib/utils/datasource-test-run-debug-display.js +137 -0
- package/lib/utils/datasource-test-run-debug-slice.js +93 -0
- package/lib/utils/datasource-test-run-display.js +459 -0
- package/lib/utils/datasource-test-run-exit.js +83 -0
- package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
- package/lib/utils/datasource-test-run-report-version.js +51 -0
- package/lib/utils/datasource-test-run-schema-sync.js +59 -0
- package/lib/utils/datasource-test-run-tty-log.js +81 -0
- package/lib/utils/datasource-validation-watch.js +266 -0
- package/lib/utils/declarative-url-ports.js +47 -0
- package/lib/utils/derive-env-key-from-client-id.js +41 -0
- package/lib/utils/dev-ca-install.js +185 -23
- package/lib/utils/dev-cert-helper.js +266 -17
- package/lib/utils/dev-hosts-helper.js +307 -0
- package/lib/utils/dev-init-cert-hints.js +37 -0
- package/lib/utils/dev-init-health-messages.js +52 -0
- package/lib/utils/dev-init-resolve.js +86 -0
- package/lib/utils/dev-init-ssh-merge.js +65 -0
- package/lib/utils/dev-ssh-config-helper.js +196 -0
- package/lib/utils/dev-user-groups.js +93 -0
- package/lib/utils/docker-build.js +42 -17
- package/lib/utils/docker-exec.js +28 -0
- package/lib/utils/docker-manifest-public-port.js +116 -0
- package/lib/utils/docker-not-running-hint.js +52 -0
- package/lib/utils/docker.js +98 -11
- package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
- package/lib/utils/env-config-loader.js +10 -91
- package/lib/utils/env-copy.js +19 -10
- package/lib/utils/env-map.js +35 -8
- package/lib/utils/env-template.js +2 -2
- package/lib/utils/environment-scoped-resources.js +144 -0
- package/lib/utils/error-formatter.js +92 -13
- package/lib/utils/error-formatters/http-status-errors.js +6 -5
- package/lib/utils/error-formatters/network-errors.js +2 -1
- package/lib/utils/error-formatters/permission-errors.js +2 -1
- package/lib/utils/error-formatters/validation-errors.js +2 -1
- package/lib/utils/external-readme.js +8 -1
- package/lib/utils/external-system-display.js +242 -136
- package/lib/utils/external-system-local-test-tty.js +389 -0
- package/lib/utils/external-system-readiness-core.js +377 -0
- package/lib/utils/external-system-readiness-deploy-display.js +270 -0
- package/lib/utils/external-system-readiness-display-internals.js +150 -0
- package/lib/utils/external-system-readiness-display.js +186 -0
- package/lib/utils/external-system-system-test-tty-overview.js +120 -0
- package/lib/utils/external-system-system-test-tty.js +417 -0
- package/lib/utils/external-system-test-helpers.js +24 -6
- package/lib/utils/external-system-validators.js +30 -12
- package/lib/utils/health-check-url.js +119 -0
- package/lib/utils/health-check.js +59 -25
- package/lib/utils/help-builder.js +11 -8
- package/lib/utils/image-version.js +4 -8
- package/lib/utils/infra-containers.js +4 -7
- package/lib/utils/infra-env-defaults.js +162 -0
- package/lib/utils/infra-status-display.js +167 -0
- package/lib/utils/infra-status.js +16 -8
- package/lib/utils/local-secrets.js +3 -4
- package/lib/utils/paths.js +148 -47
- package/lib/utils/port-resolver.js +10 -23
- package/lib/utils/redis-env-scope.js +62 -0
- package/lib/utils/register-aifabrix-shell-env.js +204 -0
- package/lib/utils/remote-builder-validation.js +99 -0
- package/lib/utils/remote-dev-auth.js +117 -21
- package/lib/utils/remote-docker-env.js +67 -15
- package/lib/utils/remote-secrets-loader.js +13 -4
- package/lib/utils/resolve-docker-image-ref.js +124 -0
- package/lib/utils/schema-loader.js +22 -9
- package/lib/utils/secrets-bash-kv.js +25 -0
- package/lib/utils/secrets-generator.js +169 -49
- package/lib/utils/secrets-helpers.js +70 -59
- package/lib/utils/secrets-kv-scope.js +60 -0
- package/lib/utils/secrets-utils.js +32 -38
- package/lib/utils/secrets-validation.js +3 -1
- package/lib/utils/secrets-yaml-preserve.js +109 -0
- package/lib/utils/ssh-key-helper.js +4 -2
- package/lib/utils/template-helpers.js +2 -2
- package/lib/utils/test-log-writer.js +3 -3
- package/lib/utils/token-manager.js +1 -2
- package/lib/utils/url-declarative-public-base.js +188 -0
- package/lib/utils/url-declarative-resolve-build.js +493 -0
- package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
- package/lib/utils/url-declarative-resolve.js +220 -0
- package/lib/utils/url-declarative-token-parse.js +74 -0
- package/lib/utils/url-declarative-url-flags.js +50 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
- package/lib/utils/url-public-path-prefix.js +34 -0
- package/lib/utils/urls-local-registry.js +220 -0
- package/lib/utils/validation-report-tty-kit.js +77 -0
- package/lib/utils/validation-run-poll.js +112 -0
- package/lib/utils/validation-run-post-retry.js +85 -0
- package/lib/utils/validation-run-request.js +116 -0
- package/lib/utils/variable-transformer.js +21 -4
- package/lib/utils/yaml-preserve.js +33 -14
- package/lib/validation/datasource-warnings.js +56 -0
- package/lib/validation/env-template-auth.js +1 -1
- package/lib/validation/external-manifest-validator.js +27 -7
- package/lib/validation/validate-display.js +37 -31
- package/lib/validation/validate-external-cert-sync.js +23 -0
- package/lib/validation/validate.js +8 -14
- package/lib/validation/validator-unresolved-placeholders.js +98 -0
- package/lib/validation/validator.js +22 -65
- package/lib/validation/wizard-config-validator.js +2 -1
- package/package.json +9 -4
- package/scripts/check-datasource-test-run-schema-sync.js +34 -0
- package/scripts/diagnose-cli.js +150 -0
- package/scripts/install-local.js +307 -55
- package/scripts/pnpm-global-remove.js +48 -0
- package/templates/README.md +15 -2
- package/templates/applications/dataplane/application.yaml +52 -2
- package/templates/applications/dataplane/env.template +79 -17
- package/templates/applications/dataplane/rbac.yaml +8 -0
- package/templates/applications/keycloak/application.yaml +9 -1
- package/templates/applications/keycloak/env.template +15 -6
- package/templates/applications/miso-controller/application.yaml +10 -2
- package/templates/applications/miso-controller/env.template +42 -12
- package/templates/applications/miso-controller/rbac.yaml +5 -0
- package/templates/external-system/README.md.hbs +20 -7
- package/templates/external-system/deploy.js.hbs +5 -5
- package/templates/external-system/external-datasource.yaml.hbs +197 -118
- package/templates/infra/compose.yaml.hbs +33 -16
- package/templates/infra/servers.json.hbs +3 -1
- package/templates/python/docker-compose.hbs +16 -0
- package/templates/typescript/docker-compose.hbs +16 -0
- package/lib/api/external-test.api.js +0 -111
- package/lib/schema/env-config.yaml +0 -60
|
@@ -46,6 +46,41 @@ function isCommentOrEmptyLine(line) {
|
|
|
46
46
|
/** Regex for kv:// path (allows slashes, e.g. kv://hubspot/clientId) */
|
|
47
47
|
const KV_REF_PATTERN = /kv:\/\/([a-zA-Z0-9_\-/]+)/g;
|
|
48
48
|
|
|
49
|
+
const { resolveBashKvFromProcessEnv } = require('./secrets-bash-kv');
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Last-resort when infra.parameter.yaml cannot be read (e.g. Jest suites that mock `fs`;
|
|
53
|
+
* catalog uses `node:fs`, which is often the same mocked instance). Must stay in sync with
|
|
54
|
+
* `generator.type: emptyAllowed` keys in lib/schema/infra.parameter.yaml.
|
|
55
|
+
*/
|
|
56
|
+
const EMPTY_ALLOWED_KV_FALLBACK = new Set(['redis-passwordKeyVault']);
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Infra catalog keys with generator `emptyAllowed` may be absent from the secrets file;
|
|
60
|
+
* they resolve like an explicit empty string (e.g. local Redis has no password).
|
|
61
|
+
* @param {string} pathStr - kv path segment (flat key or path with slashes)
|
|
62
|
+
* @returns {boolean}
|
|
63
|
+
*/
|
|
64
|
+
function isKvKeyAllowedEmptyWhenAbsent(pathStr) {
|
|
65
|
+
if (!pathStr || typeof pathStr !== 'string') return false;
|
|
66
|
+
try {
|
|
67
|
+
const { getInfraParameterCatalog } = require('../parameters/infra-parameter-catalog');
|
|
68
|
+
return getInfraParameterCatalog().isKeyAllowedEmpty(pathStr);
|
|
69
|
+
} catch {
|
|
70
|
+
try {
|
|
71
|
+
const {
|
|
72
|
+
readRelaxedEmptyAllowedKeySet,
|
|
73
|
+
DEFAULT_CATALOG_PATH
|
|
74
|
+
} = require('../parameters/infra-parameter-catalog');
|
|
75
|
+
const set = readRelaxedEmptyAllowedKeySet(DEFAULT_CATALOG_PATH);
|
|
76
|
+
if (set && set.has(pathStr)) return true;
|
|
77
|
+
} catch {
|
|
78
|
+
/* ignore */
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return EMPTY_ALLOWED_KV_FALLBACK.has(pathStr);
|
|
82
|
+
}
|
|
83
|
+
|
|
49
84
|
/**
|
|
50
85
|
* Find object key that matches part case-insensitively.
|
|
51
86
|
* @param {Object} obj - Object to search
|
|
@@ -98,19 +133,30 @@ function getValueByPath(secrets, pathStr) {
|
|
|
98
133
|
return getValueByNestedPath(secrets, pathStr.split('/'));
|
|
99
134
|
}
|
|
100
135
|
|
|
136
|
+
const { getValueByPathWithEnvScope, mergeSecretsWithPrefixedCopies } =
|
|
137
|
+
require('./secrets-kv-scope').createScopedKvHelpers(getValueByPath);
|
|
138
|
+
|
|
139
|
+
function resolveKvRefValue(secretsMap, pathStr, envKey, effective) {
|
|
140
|
+
const v = getValueByPathWithEnvScope(secretsMap, pathStr, envKey, effective);
|
|
141
|
+
if (v !== undefined && v !== null) return v;
|
|
142
|
+
return resolveBashKvFromProcessEnv(pathStr);
|
|
143
|
+
}
|
|
144
|
+
|
|
101
145
|
/**
|
|
102
146
|
* Collect missing kv:// secrets referenced in content (skips commented and empty lines).
|
|
103
147
|
* Supports path-style refs (e.g. kv://hubspot/clientId). Returns unique refs.
|
|
104
148
|
* @function collectMissingSecrets
|
|
105
149
|
* @param {string} content - Text content
|
|
106
150
|
* @param {Object} secrets - Available secrets (flat or nested)
|
|
151
|
+
* @param {{ effective?: boolean, envKey?: string }|null} [scopedKv] - Optional env-scoped kv resolution
|
|
107
152
|
* @returns {string[]} Array of missing kv://<path> references (unique)
|
|
108
153
|
*/
|
|
109
|
-
function collectMissingSecrets(content, secrets) {
|
|
154
|
+
function collectMissingSecrets(content, secrets, scopedKv = null) {
|
|
155
|
+
const effective = Boolean(scopedKv && scopedKv.effective && scopedKv.envKey);
|
|
156
|
+
const secretsMap = effective ? mergeSecretsWithPrefixedCopies(secrets, scopedKv.envKey) : secrets;
|
|
110
157
|
const seen = new Set();
|
|
111
158
|
const missing = [];
|
|
112
|
-
const
|
|
113
|
-
for (const line of lines) {
|
|
159
|
+
for (const line of content.split('\n')) {
|
|
114
160
|
if (isCommentOrEmptyLine(line)) continue;
|
|
115
161
|
let match;
|
|
116
162
|
KV_REF_PATTERN.lastIndex = 0;
|
|
@@ -118,8 +164,8 @@ function collectMissingSecrets(content, secrets) {
|
|
|
118
164
|
const pathStr = match[1];
|
|
119
165
|
if (seen.has(pathStr)) continue;
|
|
120
166
|
seen.add(pathStr);
|
|
121
|
-
const value =
|
|
122
|
-
if (value === undefined || value === null) {
|
|
167
|
+
const value = resolveKvRefValue(secretsMap, pathStr, scopedKv?.envKey, effective);
|
|
168
|
+
if ((value === undefined || value === null) && !isKvKeyAllowedEmptyWhenAbsent(pathStr)) {
|
|
123
169
|
missing.push(`kv://${pathStr}`);
|
|
124
170
|
}
|
|
125
171
|
}
|
|
@@ -157,14 +203,20 @@ function formatMissingSecretsFileInfo(secretsFilePaths) {
|
|
|
157
203
|
* @param {string} content - Text content containing kv:// references
|
|
158
204
|
* @param {Object} secrets - Secrets map (flat or nested)
|
|
159
205
|
* @param {Object} envVars - Environment variables map for nested interpolation
|
|
206
|
+
* @param {{ effective?: boolean, envKey?: string }|null} [scopedKv] - Optional env-scoped kv resolution
|
|
160
207
|
* @returns {string} Content with kv:// references replaced
|
|
161
208
|
*/
|
|
162
|
-
function replaceKvInContent(content, secrets, envVars) {
|
|
209
|
+
function replaceKvInContent(content, secrets, envVars, scopedKv = null) {
|
|
210
|
+
const effective = Boolean(scopedKv && scopedKv.effective && scopedKv.envKey);
|
|
211
|
+
const secretsMap = effective ? mergeSecretsWithPrefixedCopies(secrets, scopedKv.envKey) : secrets;
|
|
163
212
|
const lines = content.split('\n');
|
|
164
213
|
const result = lines.map(line => {
|
|
165
214
|
if (isCommentOrEmptyLine(line)) return line;
|
|
166
215
|
return line.replace(KV_REF_PATTERN, (match, pathStr) => {
|
|
167
|
-
let value =
|
|
216
|
+
let value = resolveKvRefValue(secretsMap, pathStr, scopedKv?.envKey, effective);
|
|
217
|
+
if ((value === undefined || value === null) && isKvKeyAllowedEmptyWhenAbsent(pathStr)) {
|
|
218
|
+
value = '';
|
|
219
|
+
}
|
|
168
220
|
if (typeof value === 'string') {
|
|
169
221
|
value = value.replace(/\$\{([A-Z_]+)\}/g, (m, envVar) => {
|
|
170
222
|
return envVars[envVar] || m;
|
|
@@ -247,44 +299,23 @@ function getPortFromEnvContent(envContent) {
|
|
|
247
299
|
}
|
|
248
300
|
|
|
249
301
|
/**
|
|
250
|
-
*
|
|
251
|
-
* @function applyDeveloperIdAdjustment
|
|
252
|
-
* @param {number} baseAppPort - Base application port
|
|
253
|
-
* @param {number} devIdNum - Developer ID number
|
|
254
|
-
* @returns {number} Adjusted port
|
|
255
|
-
*/
|
|
256
|
-
function applyDeveloperIdAdjustment(baseAppPort, devIdNum) {
|
|
257
|
-
return devIdNum === 0 ? baseAppPort : (baseAppPort + (devIdNum * 100));
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Calculate application port following override chain and developer-id adjustment
|
|
262
|
-
* Override chain: env-config.yaml → config.yaml → application.yaml port
|
|
302
|
+
* Resolve manifest listen port (container port) for the app from override chain.
|
|
263
303
|
* @async
|
|
264
|
-
* @function calculateAppPort
|
|
265
304
|
* @param {string} [variablesPath] - Path to application config
|
|
266
|
-
* @param {Object} localEnv -
|
|
305
|
+
* @param {Object} localEnv - Merged local env from defaults + config.yaml
|
|
267
306
|
* @param {string} envContent - Environment content for fallback
|
|
268
|
-
* @
|
|
269
|
-
* @returns {Promise<number>} Final application port with developer-id adjustment
|
|
307
|
+
* @returns {Promise<number>} Base listen port (before +10 / +dev*100 local host math)
|
|
270
308
|
*/
|
|
271
|
-
async function
|
|
272
|
-
// Start with env-config value
|
|
309
|
+
async function resolveManifestListenPort(variablesPath, localEnv, envContent) {
|
|
273
310
|
let baseAppPort = getPortFromLocalEnv(localEnv);
|
|
274
|
-
|
|
275
|
-
// Override with application config port (strongest)
|
|
276
311
|
const variablesPort = getPortFromVariablesFile(variablesPath);
|
|
277
312
|
if (variablesPort !== null) {
|
|
278
313
|
baseAppPort = variablesPort;
|
|
279
314
|
}
|
|
280
|
-
|
|
281
|
-
// Fallback to env content if still no port
|
|
282
315
|
if (baseAppPort === null || baseAppPort === undefined) {
|
|
283
316
|
baseAppPort = getPortFromEnvContent(envContent);
|
|
284
317
|
}
|
|
285
|
-
|
|
286
|
-
// Apply developer-id adjustment
|
|
287
|
-
return applyDeveloperIdAdjustment(baseAppPort, devIdNum);
|
|
318
|
+
return baseAppPort;
|
|
288
319
|
}
|
|
289
320
|
|
|
290
321
|
/**
|
|
@@ -325,16 +356,6 @@ function getPortVarFromEnvTemplatePath(variablesPath) {
|
|
|
325
356
|
}
|
|
326
357
|
}
|
|
327
358
|
|
|
328
|
-
/**
|
|
329
|
-
* Adjust infra-related ports in resolved .env content for local environment.
|
|
330
|
-
* Own case: when we generate .env for envOutputPath (not reload), we use localPort (application.yaml build.localPort or port).
|
|
331
|
-
* Sets PORT and the template port var (e.g. MISO_PORT) to localPort so the generated .env is correct for local use.
|
|
332
|
-
* @async
|
|
333
|
-
* @function adjustLocalEnvPortsInContent
|
|
334
|
-
* @param {string} envContent - Resolved .env content
|
|
335
|
-
* @param {string} [variablesPath] - Path to application config (to read port and template port var)
|
|
336
|
-
* @returns {Promise<string>} Updated content with local ports
|
|
337
|
-
*/
|
|
338
359
|
/**
|
|
339
360
|
* Gets developer ID number
|
|
340
361
|
* @async
|
|
@@ -418,12 +439,13 @@ async function buildEnvVarsForInterpolation(devIdNum) {
|
|
|
418
439
|
async function adjustLocalEnvPortsInContent(envContent, variablesPath) {
|
|
419
440
|
const devIdNum = await getDeveloperIdNumber();
|
|
420
441
|
const localEnv = await getLocalEnvWithOverrides();
|
|
442
|
+
const { localHostPort } = require('./declarative-url-ports');
|
|
421
443
|
|
|
422
|
-
const
|
|
423
|
-
const appPort =
|
|
444
|
+
const baseListen = await resolveManifestListenPort(variablesPath, localEnv, envContent);
|
|
445
|
+
const appPort = localHostPort(baseListen, devIdNum);
|
|
424
446
|
|
|
425
447
|
let updated = updatePortVariable(envContent, appPort);
|
|
426
|
-
updated = updateLocalhostUrls(updated,
|
|
448
|
+
updated = updateLocalhostUrls(updated, baseListen, appPort);
|
|
427
449
|
updated = await rewriteInfraEndpoints(updated, 'local');
|
|
428
450
|
|
|
429
451
|
const envVars = await buildEnvVarsForInterpolation(devIdNum);
|
|
@@ -437,18 +459,6 @@ async function adjustLocalEnvPortsInContent(envContent, variablesPath) {
|
|
|
437
459
|
return updated;
|
|
438
460
|
}
|
|
439
461
|
|
|
440
|
-
/**
|
|
441
|
-
* Ensure secrets map is non-empty or throw a friendly guidance error
|
|
442
|
-
* @function ensureNonEmptySecrets
|
|
443
|
-
* @param {Object} secrets - Secrets map
|
|
444
|
-
* @throws {Error} If secrets is empty
|
|
445
|
-
*/
|
|
446
|
-
function ensureNonEmptySecrets(secrets) {
|
|
447
|
-
if (Object.keys(secrets || {}).length === 0) {
|
|
448
|
-
throw new Error('No secrets file found. Please create ~/.aifabrix/secrets.local.yaml or configure aifabrix-secrets in config.yaml');
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
|
|
452
462
|
/**
|
|
453
463
|
* Validate secrets against the env template (skips commented and empty lines)
|
|
454
464
|
* @function validateSecrets
|
|
@@ -465,6 +475,8 @@ module.exports = {
|
|
|
465
475
|
loadEnvConfig,
|
|
466
476
|
interpolateEnvVars,
|
|
467
477
|
collectMissingSecrets,
|
|
478
|
+
resolveBashKvFromProcessEnv,
|
|
479
|
+
mergeSecretsWithPrefixedCopies,
|
|
468
480
|
formatMissingSecretsFileInfo,
|
|
469
481
|
replaceKvInContent,
|
|
470
482
|
resolveServicePortsInEnvContent,
|
|
@@ -473,7 +485,6 @@ module.exports = {
|
|
|
473
485
|
adjustLocalEnvPortsInContent,
|
|
474
486
|
readYamlAtPath,
|
|
475
487
|
applyCanonicalSecretsOverride,
|
|
476
|
-
ensureNonEmptySecrets,
|
|
477
488
|
validateSecrets,
|
|
478
489
|
rewriteInfraEndpoints,
|
|
479
490
|
getEnvHosts
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Env-scoped kv:// resolution helpers (prefixed keys + in-memory aliases).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Split from secrets-helpers for file size limits
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {Function} getValueByPath - From secrets-helpers
|
|
13
|
+
* @returns {{ getValueByPathWithEnvScope: Function, mergeSecretsWithPrefixedCopies: Function }}
|
|
14
|
+
*/
|
|
15
|
+
function createScopedKvHelpers(getValueByPath) {
|
|
16
|
+
/**
|
|
17
|
+
* @param {Object} secrets
|
|
18
|
+
* @param {string} pathStr
|
|
19
|
+
* @param {string|null|undefined} envKey
|
|
20
|
+
* @param {boolean} effective
|
|
21
|
+
* @returns {*}
|
|
22
|
+
*/
|
|
23
|
+
function getValueByPathWithEnvScope(secrets, pathStr, envKey, effective) {
|
|
24
|
+
if (!effective || !envKey) {
|
|
25
|
+
return getValueByPath(secrets, pathStr);
|
|
26
|
+
}
|
|
27
|
+
const prefix = `${String(envKey).toLowerCase()}-`;
|
|
28
|
+
const prefixedPath = prefix + pathStr;
|
|
29
|
+
const fromPrefixed = getValueByPath(secrets, prefixedPath);
|
|
30
|
+
if (fromPrefixed !== undefined && fromPrefixed !== null) {
|
|
31
|
+
return fromPrefixed;
|
|
32
|
+
}
|
|
33
|
+
return getValueByPath(secrets, pathStr);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {Object} secrets
|
|
38
|
+
* @param {string} envKey
|
|
39
|
+
* @returns {Object}
|
|
40
|
+
*/
|
|
41
|
+
function mergeSecretsWithPrefixedCopies(secrets, envKey) {
|
|
42
|
+
if (!secrets || typeof secrets !== 'object' || !envKey) {
|
|
43
|
+
return secrets;
|
|
44
|
+
}
|
|
45
|
+
const prefix = `${String(envKey).toLowerCase()}-`;
|
|
46
|
+
const merged = { ...secrets };
|
|
47
|
+
for (const [k, v] of Object.entries(secrets)) {
|
|
48
|
+
if (typeof k !== 'string' || !k || k.startsWith(prefix)) continue;
|
|
49
|
+
const pk = prefix + k;
|
|
50
|
+
if (merged[pk] === undefined && v !== undefined && v !== null) {
|
|
51
|
+
merged[pk] = v;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return merged;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { getValueByPathWithEnvScope, mergeSecretsWithPrefixedCopies };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = { createScopedKvHelpers };
|
|
@@ -19,25 +19,18 @@ const { getContainerPort } = require('./port-resolver');
|
|
|
19
19
|
const { loadYamlTolerantOfDuplicateKeys } = require('./secrets-generator');
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
* Parses secrets YAML content with fallback for duplicate keys
|
|
22
|
+
* Parses secrets YAML content with fallback for duplicate keys and broken concat files
|
|
23
|
+
* (e.g. empty map `{}` then appended key lines — invalid without `---`).
|
|
23
24
|
* @param {string} content - Raw file content
|
|
24
25
|
* @returns {Object} Parsed secrets object
|
|
25
26
|
*/
|
|
26
27
|
function parseSecretsContent(content) {
|
|
27
|
-
|
|
28
|
-
return yaml.load(content);
|
|
29
|
-
} catch (yamlErr) {
|
|
30
|
-
const msg = yamlErr.message || '';
|
|
31
|
-
if (msg.includes('duplicate') || msg.includes('duplicated mapping')) {
|
|
32
|
-
return loadYamlTolerantOfDuplicateKeys(content);
|
|
33
|
-
}
|
|
34
|
-
throw yamlErr;
|
|
35
|
-
}
|
|
28
|
+
return loadYamlTolerantOfDuplicateKeys(content);
|
|
36
29
|
}
|
|
37
30
|
|
|
38
31
|
/**
|
|
39
32
|
* Loads secrets from file with cascading lookup support
|
|
40
|
-
* First checks
|
|
33
|
+
* First checks the primary user secrets file (see getPrimaryUserSecretsLocalPath in paths.js), then aifabrix-secrets from config.yaml
|
|
41
34
|
*
|
|
42
35
|
* @async
|
|
43
36
|
* @function loadSecretsFromFile
|
|
@@ -51,7 +44,7 @@ async function loadSecretsFromFile(filePath) {
|
|
|
51
44
|
|
|
52
45
|
try {
|
|
53
46
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
54
|
-
const secrets =
|
|
47
|
+
const secrets = parseSecretsContent(content);
|
|
55
48
|
|
|
56
49
|
if (!secrets || typeof secrets !== 'object') {
|
|
57
50
|
return {};
|
|
@@ -65,17 +58,15 @@ async function loadSecretsFromFile(filePath) {
|
|
|
65
58
|
}
|
|
66
59
|
|
|
67
60
|
/**
|
|
68
|
-
* Loads user secrets from
|
|
61
|
+
* Loads user secrets from getPrimaryUserSecretsLocalPath() (same dir as config.yaml resolution).
|
|
69
62
|
* Used as the master source when merging with project/public secrets: user values win,
|
|
70
63
|
* missing keys are filled from the public (aifabrix-secrets) file.
|
|
71
|
-
* Does not use config.yaml aifabrix-home so the merge always sees the actual user file.
|
|
72
64
|
*
|
|
73
65
|
* @function loadPrimaryUserSecrets
|
|
74
66
|
* @returns {Object} Loaded secrets object or empty object
|
|
75
67
|
*/
|
|
76
68
|
function loadPrimaryUserSecrets() {
|
|
77
|
-
const
|
|
78
|
-
const userSecretsPath = path.join(primaryDir, 'secrets.local.yaml');
|
|
69
|
+
const userSecretsPath = pathsUtil.getPrimaryUserSecretsLocalPath();
|
|
79
70
|
if (!fs.existsSync(userSecretsPath)) {
|
|
80
71
|
return {};
|
|
81
72
|
}
|
|
@@ -98,32 +89,12 @@ function loadPrimaryUserSecrets() {
|
|
|
98
89
|
}
|
|
99
90
|
|
|
100
91
|
/**
|
|
101
|
-
*
|
|
102
|
-
* Uses paths.getAifabrixHome() to respect config.yaml aifabrix-home override
|
|
92
|
+
* Same as {@link loadPrimaryUserSecrets} (legacy name; kept for callers).
|
|
103
93
|
* @function loadUserSecrets
|
|
104
94
|
* @returns {Object} Loaded secrets object or empty object
|
|
105
95
|
*/
|
|
106
96
|
function loadUserSecrets() {
|
|
107
|
-
|
|
108
|
-
if (!fs.existsSync(userSecretsPath)) {
|
|
109
|
-
return {};
|
|
110
|
-
}
|
|
111
|
-
ensureSecureFilePermissions(userSecretsPath);
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
const content = fs.readFileSync(userSecretsPath, 'utf8');
|
|
115
|
-
const secrets = parseSecretsContent(content);
|
|
116
|
-
if (!secrets || typeof secrets !== 'object') {
|
|
117
|
-
throw new Error(`Invalid secrets file format: ${userSecretsPath}`);
|
|
118
|
-
}
|
|
119
|
-
return secrets;
|
|
120
|
-
} catch (error) {
|
|
121
|
-
if (error.message.includes('Invalid secrets file format')) {
|
|
122
|
-
throw error;
|
|
123
|
-
}
|
|
124
|
-
logger.warn(`Warning: Could not read secrets file ${userSecretsPath}: ${error.message}`);
|
|
125
|
-
return {};
|
|
126
|
-
}
|
|
97
|
+
return loadPrimaryUserSecrets();
|
|
127
98
|
}
|
|
128
99
|
|
|
129
100
|
/**
|
|
@@ -155,6 +126,28 @@ function loadDefaultSecrets() {
|
|
|
155
126
|
}
|
|
156
127
|
}
|
|
157
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Creates the primary user secrets file if missing (empty map) for first-run installs.
|
|
131
|
+
* Uses the same directory as {@link loadPrimaryUserSecrets} (config dir / ~/.aifabrix).
|
|
132
|
+
*
|
|
133
|
+
* @function ensurePrimaryUserSecretsFileExists
|
|
134
|
+
*/
|
|
135
|
+
function ensurePrimaryUserSecretsFileExists() {
|
|
136
|
+
const userSecretsPath = pathsUtil.getPrimaryUserSecretsLocalPath();
|
|
137
|
+
const primaryDir = path.dirname(userSecretsPath);
|
|
138
|
+
if (fs.existsSync(userSecretsPath)) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (!fs.existsSync(primaryDir)) {
|
|
142
|
+
fs.mkdirSync(primaryDir, { recursive: true, mode: 0o700 });
|
|
143
|
+
}
|
|
144
|
+
const header =
|
|
145
|
+
'# Local secrets for AI Fabrix CLI (kv:// references in env.template resolve here)\n' +
|
|
146
|
+
'# Keys are appended by `aifabrix resolve` / ensure — do not prefix with JSON `{}`.\n';
|
|
147
|
+
fs.writeFileSync(userSecretsPath, header, { mode: 0o600 });
|
|
148
|
+
ensureSecureFilePermissions(userSecretsPath);
|
|
149
|
+
}
|
|
150
|
+
|
|
158
151
|
/**
|
|
159
152
|
* Builds a map of hostname to service name from environment config
|
|
160
153
|
* @function buildHostnameToServiceMap
|
|
@@ -214,6 +207,7 @@ module.exports = {
|
|
|
214
207
|
loadPrimaryUserSecrets,
|
|
215
208
|
loadUserSecrets,
|
|
216
209
|
loadDefaultSecrets,
|
|
210
|
+
ensurePrimaryUserSecretsFileExists,
|
|
217
211
|
buildHostnameToServiceMap,
|
|
218
212
|
resolveUrlPort
|
|
219
213
|
};
|
|
@@ -63,7 +63,9 @@ function validateSecretsFile(filePath, options = {}) {
|
|
|
63
63
|
if (!filePath || typeof filePath !== 'string') {
|
|
64
64
|
return { valid: false, errors: ['Path is required'], path: '' };
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
// Always normalize to an OS-resolved absolute path. This makes behavior consistent
|
|
67
|
+
// on Windows when callers pass POSIX-style absolute paths like "/nonexistent/foo".
|
|
68
|
+
const resolvedPath = path.resolve(filePath);
|
|
67
69
|
if (!fs.existsSync(resolvedPath)) {
|
|
68
70
|
return { valid: false, errors: [`File not found: ${resolvedPath}`], path: resolvedPath };
|
|
69
71
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YAML merge utilities that preserve comments/blank lines for flat secrets files.
|
|
3
|
+
*
|
|
4
|
+
* Intended for `secrets.local.yaml` which is typically a simple `key: value` mapping
|
|
5
|
+
* with user-owned comments. When the file isn't flat (complex YAML), callers should
|
|
6
|
+
* fall back to a full YAML rewrite.
|
|
7
|
+
*
|
|
8
|
+
* @fileoverview Comment-preserving YAML merge helpers
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const keyLineRe = /^(\s*)([^#:\n]+):\s*(.*)$/;
|
|
12
|
+
|
|
13
|
+
function isCommentOrBlank(line) {
|
|
14
|
+
const t = line.trim();
|
|
15
|
+
return !t || t.startsWith('#');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function parseFlatKeyLine(line) {
|
|
19
|
+
const m = line.match(keyLineRe);
|
|
20
|
+
if (!m) return null;
|
|
21
|
+
const indent = m[1] || '';
|
|
22
|
+
const key = (m[2] || '').trim();
|
|
23
|
+
const rest = m[3] || '';
|
|
24
|
+
if (!key) return null;
|
|
25
|
+
return { indent, key, rest };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function splitInlineComment(rest) {
|
|
29
|
+
const idx = rest.indexOf(' #');
|
|
30
|
+
return { inlineComment: idx >= 0 ? rest.slice(idx) : '' };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function formatYamlScalarForFlatLine(yaml, dumpOpts, value) {
|
|
34
|
+
const dumped = yaml.dump({ _k: value }, dumpOpts);
|
|
35
|
+
const lines = dumped.split(/\r?\n/).filter(Boolean);
|
|
36
|
+
if (lines.length !== 1) return null;
|
|
37
|
+
const idx = lines[0].indexOf(':');
|
|
38
|
+
if (idx < 0) return null;
|
|
39
|
+
return lines[0].slice(idx + 1).trimStart();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function appendMissingKeys(out, seen, desiredSecrets, yaml, dumpOpts) {
|
|
43
|
+
for (const key of Object.keys(desiredSecrets)) {
|
|
44
|
+
if (seen.has(key)) continue;
|
|
45
|
+
const scalar = formatYamlScalarForFlatLine(yaml, dumpOpts, desiredSecrets[key]);
|
|
46
|
+
if (scalar === null) return null;
|
|
47
|
+
out.push(`${key}: ${scalar}`.trimEnd());
|
|
48
|
+
seen.add(key);
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function mergeExistingLinesPreservingComments(lines, desiredSecrets, yaml, dumpOpts) {
|
|
54
|
+
const out = [];
|
|
55
|
+
const seen = new Set();
|
|
56
|
+
const encountered = new Set();
|
|
57
|
+
|
|
58
|
+
for (const line of lines) {
|
|
59
|
+
if (isCommentOrBlank(line)) {
|
|
60
|
+
out.push(line);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const parsed = parseFlatKeyLine(line);
|
|
64
|
+
if (!parsed) return null;
|
|
65
|
+
const { indent, key, rest } = parsed;
|
|
66
|
+
|
|
67
|
+
if (encountered.has(key)) continue;
|
|
68
|
+
encountered.add(key);
|
|
69
|
+
|
|
70
|
+
if (!Object.prototype.hasOwnProperty.call(desiredSecrets, key)) continue;
|
|
71
|
+
|
|
72
|
+
const { inlineComment } = splitInlineComment(rest);
|
|
73
|
+
const scalar = formatYamlScalarForFlatLine(yaml, dumpOpts, desiredSecrets[key]);
|
|
74
|
+
if (scalar === null) return null;
|
|
75
|
+
seen.add(key);
|
|
76
|
+
out.push(`${indent}${key}: ${scalar}${inlineComment}`.trimEnd());
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return { out, seen };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Merge a desired flat secrets object into existing file content while preserving comments/blank lines.
|
|
84
|
+
* Supports deletes: keys present in existing content but absent in desired are removed.
|
|
85
|
+
*
|
|
86
|
+
* Returns null when content cannot be treated as a flat key-value file.
|
|
87
|
+
*
|
|
88
|
+
* @param {string} existingContent
|
|
89
|
+
* @param {Record<string, any>} desiredSecrets
|
|
90
|
+
* @param {{ yaml: any, dumpOpts: any }} yamlCtx
|
|
91
|
+
* @returns {string|null}
|
|
92
|
+
*/
|
|
93
|
+
function mergeFlatSecretsYamlPreservingComments(existingContent, desiredSecrets, yamlCtx) {
|
|
94
|
+
if (typeof existingContent !== 'string') return null;
|
|
95
|
+
if (!desiredSecrets || typeof desiredSecrets !== 'object') return null;
|
|
96
|
+
if (!yamlCtx || !yamlCtx.yaml) return null;
|
|
97
|
+
|
|
98
|
+
const { yaml, dumpOpts } = yamlCtx;
|
|
99
|
+
const lines = existingContent.split(/\r?\n/);
|
|
100
|
+
const merged = mergeExistingLinesPreservingComments(lines, desiredSecrets, yaml, dumpOpts);
|
|
101
|
+
if (!merged) return null;
|
|
102
|
+
|
|
103
|
+
const appended = appendMissingKeys(merged.out, merged.seen, desiredSecrets, yaml, dumpOpts);
|
|
104
|
+
if (appended === null) return null;
|
|
105
|
+
return appended.join('\n');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = { mergeFlatSecretsYamlPreservingComments };
|
|
109
|
+
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const fs = require('fs');
|
|
8
|
+
const { nodeFs } = require('../internal/node-fs');
|
|
8
9
|
const path = require('path');
|
|
9
10
|
const os = require('os');
|
|
10
11
|
const { execSync } = require('child_process');
|
|
@@ -40,9 +41,10 @@ function getDefaultEd25519PrivateKeyPath() {
|
|
|
40
41
|
* @returns {string} Resolved SSH dir path
|
|
41
42
|
*/
|
|
42
43
|
function ensureSshDir(sshDir) {
|
|
44
|
+
const syncFs = nodeFs();
|
|
43
45
|
const dir = sshDir || getDefaultSshDir();
|
|
44
|
-
if (!
|
|
45
|
-
|
|
46
|
+
if (!syncFs.existsSync(dir)) {
|
|
47
|
+
syncFs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
46
48
|
}
|
|
47
49
|
return dir;
|
|
48
50
|
}
|
|
@@ -35,7 +35,7 @@ async function loadTemplateVariables(templateName) {
|
|
|
35
35
|
} catch (error) {
|
|
36
36
|
// Template application.yaml not found or invalid, continue without it
|
|
37
37
|
if (error.code !== 'ENOENT') {
|
|
38
|
-
logger.warn(chalk.yellow(
|
|
38
|
+
logger.warn(chalk.yellow(`⚠ Warning: Could not load template application.yaml: ${error.message}`));
|
|
39
39
|
}
|
|
40
40
|
return null;
|
|
41
41
|
}
|
|
@@ -125,7 +125,7 @@ async function updateTemplateVariables(appPath, appName, options, config) {
|
|
|
125
125
|
writeConfigFile(variablesPath, variables);
|
|
126
126
|
} catch (error) {
|
|
127
127
|
if (error.message && !error.message.includes('not found')) {
|
|
128
|
-
logger.warn(chalk.yellow(
|
|
128
|
+
logger.warn(chalk.yellow(`⚠ Warning: Could not update application config: ${error.message}`));
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Test log writer - writes debug logs to integration/<
|
|
2
|
+
* Test log writer - writes debug logs to integration/<systemKey>/logs/
|
|
3
3
|
* Sanitization (tokens, secrets) is done by dataplane before responses are returned.
|
|
4
4
|
*
|
|
5
5
|
* @fileoverview Write test request/response logs for debugging
|
|
@@ -29,9 +29,9 @@ function sanitizeForLog(obj, seen = new Set()) {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* Write test log to integration/<
|
|
32
|
+
* Write test log to integration/<systemKey>/logs/<logType>-<timestamp>.json
|
|
33
33
|
* @async
|
|
34
|
-
* @param {string} appKey -
|
|
34
|
+
* @param {string} appKey - Integration folder name under integration/ (used for path)
|
|
35
35
|
* @param {Object} data - Log data (request, response) - will be sanitized
|
|
36
36
|
* @param {string} [logType] - Log type prefix (default: test-integration)
|
|
37
37
|
* @param {string} [integrationBaseDir] - Base dir for integration (default: cwd/integration)
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
const fs = require('fs');
|
|
13
|
-
const path = require('path');
|
|
14
13
|
const yaml = require('js-yaml');
|
|
15
14
|
const config = require('../core/config');
|
|
16
15
|
const logger = require('./logger');
|
|
@@ -25,7 +24,7 @@ const { warnRefreshFailureOnce, warnRefreshTokenExpiredOnce } = require('./token
|
|
|
25
24
|
const DATAPLANE_APP_KEY = 'dataplane';
|
|
26
25
|
|
|
27
26
|
function getSecretsFilePath() {
|
|
28
|
-
return
|
|
27
|
+
return pathsUtil.getPrimaryUserSecretsLocalPath();
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
/**
|