@aifabrix/builder 2.45.0 → 2.46.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/docs-rules.mdc +3 -1
- package/.cursor/rules/project-rules.mdc +1 -0
- package/README.md +43 -17
- package/integration/hubspot-test/wizard-hubspot-e2e.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 +130 -0
- package/jest.projects.js +69 -121
- package/lib/api/agent-metadata-validation.api.js +93 -0
- package/lib/api/api-response-errors.js +124 -0
- package/lib/api/audit-api-helpers.js +68 -0
- package/lib/api/audit.api.js +119 -0
- package/lib/api/auth-cache.api.js +44 -0
- package/lib/api/certificates.api.js +4 -4
- package/lib/api/credential.api.js +2 -2
- package/lib/api/dataplane-health.api.js +137 -0
- package/lib/api/dataplane-sync.api.js +27 -0
- package/lib/api/datasources-core.api.js +21 -18
- package/lib/api/datasources-extended.api.js +22 -22
- package/lib/api/dimensions.api.js +3 -0
- package/lib/api/external-systems.api.js +13 -13
- package/lib/api/governance-scenario-pack.api.js +159 -0
- package/lib/api/governance-scenarios.api.js +39 -0
- package/lib/api/groups.api.js +79 -0
- package/lib/api/index.js +88 -5
- package/lib/api/lifecycle.api.js +77 -0
- package/lib/api/pipeline.api.js +7 -5
- package/lib/api/protection.api.js +157 -0
- package/lib/api/records-bulk.api.js +43 -0
- package/lib/api/records-search-parse.js +49 -0
- package/lib/api/records-search.api.js +29 -0
- package/lib/api/types/agent-metadata-validation.types.js +67 -0
- package/lib/api/types/audit.types.js +72 -0
- package/lib/api/types/dataplane-health.types.js +27 -0
- package/lib/api/types/dimensions.types.js +2 -0
- package/lib/api/types/governance-scenarios.types.js +39 -0
- package/lib/api/types/protection.types.js +69 -0
- package/lib/api/types/records-bulk.types.js +35 -0
- package/lib/api/types/records-search.types.js +28 -0
- package/lib/api/user-groups.api.js +50 -0
- package/lib/api/users.api.js +95 -0
- package/lib/api/validation-run.api.js +3 -3
- package/lib/api/validation-runner.js +5 -3
- package/lib/api/wizard-platform.api.js +3 -3
- package/lib/api/wizard.api.js +21 -21
- package/lib/app/deploy-config.js +4 -1
- package/lib/app/deploy.js +34 -19
- package/lib/app/down.js +20 -1
- 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/build/index.js +5 -0
- package/lib/certification/sync-system-certification.js +13 -6
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided-auth.js +109 -0
- package/lib/cli/infra-guided.js +91 -69
- package/lib/cli/setup-app.help.js +19 -2
- package/lib/cli/setup-app.js +58 -23
- package/lib/cli/setup-app.test-commands.js +28 -21
- package/lib/cli/setup-auth.js +55 -3
- 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 +62 -6
- 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 +65 -0
- package/lib/cli/setup-utility-repair.js +198 -0
- package/lib/cli/setup-utility-resolve.js +42 -6
- package/lib/cli/setup-utility-validate.js +197 -0
- package/lib/cli/setup-utility.js +41 -258
- package/lib/commands/auth-status-client-tokens.js +214 -0
- package/lib/commands/auth-status-dataplane-version.js +269 -0
- package/lib/commands/auth-status-display.js +94 -0
- package/lib/commands/auth-status.js +111 -53
- package/lib/commands/credential-push.js +10 -5
- package/lib/commands/datasource-load-export-cli.js +178 -0
- package/lib/commands/datasource-test-trust-cli.js +118 -0
- package/lib/commands/datasource-unified-test-cli.js +31 -20
- package/lib/commands/datasource-unified-test-cli.options.js +22 -3
- package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
- package/lib/commands/datasource-verify-audit-cli.js +96 -0
- package/lib/commands/datasource.js +68 -1
- package/lib/commands/dev-show-display.js +8 -1
- package/lib/commands/dimension-value.js +43 -13
- package/lib/commands/dimension.js +38 -7
- 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 +131 -0
- package/lib/commands/identity-group-cli.js +146 -0
- package/lib/commands/identity-membership-role-cache.js +175 -0
- package/lib/commands/identity-shared.js +94 -0
- package/lib/commands/identity-user-cli.js +158 -0
- package/lib/commands/identity.js +43 -0
- package/lib/commands/lifecycle-command-action.js +123 -0
- package/lib/commands/lifecycle-external.js +268 -0
- package/lib/commands/login.js +56 -17
- package/lib/commands/protection-cli-leaves.js +286 -0
- package/lib/commands/protection.js +50 -0
- package/lib/commands/repair-auth-apply.js +127 -0
- package/lib/commands/repair-changed-files.js +41 -0
- package/lib/commands/repair-datasource-keys.js +4 -1
- package/lib/commands/repair-datasource-run.js +59 -0
- package/lib/commands/repair-datasource-test-payload.js +259 -0
- package/lib/commands/repair-datasource.js +1 -51
- package/lib/commands/repair-env-template.js +64 -13
- package/lib/commands/repair-external-run.js +216 -0
- package/lib/commands/repair-openapi-sync.js +23 -0
- package/lib/commands/repair-persist.js +4 -0
- package/lib/commands/repair-rbac.js +52 -1
- package/lib/commands/repair-steps.js +145 -0
- package/lib/commands/repair.js +29 -286
- 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 +67 -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 +61 -14
- package/lib/commands/test-governance-cli-options.js +47 -0
- package/lib/commands/test-governance-external.js +67 -0
- package/lib/commands/test-trust-cli-options.js +32 -0
- package/lib/commands/test-trust-external.js +126 -0
- package/lib/commands/up-common.js +25 -13
- package/lib/commands/up-dataplane.js +50 -13
- package/lib/commands/upload-publish-context.js +75 -0
- package/lib/commands/upload-sidecar-followups.js +89 -0
- package/lib/commands/upload.js +108 -105
- 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 +70 -0
- package/lib/commands/verify-trust-external.js +67 -0
- package/lib/commands/wizard-config-normalizer.js +43 -23
- package/lib/commands/wizard-core.js +5 -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-device-dataplane.js +185 -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 +55 -0
- package/lib/datasource/agent-trust-debug-log.js +45 -0
- package/lib/datasource/agent-trust-fetch.js +115 -0
- package/lib/datasource/agent-trust-map.js +101 -0
- package/lib/datasource/agent-trust-mock.js +44 -0
- package/lib/datasource/agent-trust-run.js +95 -0
- package/lib/datasource/agent-trust-timeout.js +25 -0
- package/lib/datasource/audit-evidence-display.js +80 -0
- package/lib/datasource/audit-evidence-extract-debug.js +115 -0
- package/lib/datasource/audit-evidence-extract.js +180 -0
- package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
- package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
- package/lib/datasource/audit-evidence-poll.js +109 -0
- package/lib/datasource/audit-evidence-run.js +177 -0
- package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
- package/lib/datasource/audit-evidence-verify-matrix.js +205 -0
- package/lib/datasource/audit-evidence-verify.js +108 -0
- package/lib/datasource/bulk-loader-run-batches.js +100 -0
- package/lib/datasource/bulk-loader-service-retry.js +52 -0
- package/lib/datasource/bulk-loader-service.js +127 -0
- package/lib/datasource/capability/run-capability-dimension.js +2 -1
- package/lib/datasource/datasource-exporter-resolve.js +82 -0
- package/lib/datasource/datasource-exporter-service.js +144 -0
- package/lib/datasource/datasource-identifier.js +148 -0
- package/lib/datasource/datasource-load-export-context.js +95 -0
- package/lib/datasource/integration-context.js +6 -4
- package/lib/datasource/local-data-paths.js +145 -0
- package/lib/datasource/log-cleaner.js +241 -0
- package/lib/datasource/log-viewer.js +111 -6
- package/lib/datasource/record-file-parser.js +85 -0
- package/lib/datasource/record-mapper.js +129 -0
- package/lib/datasource/resolve-app.js +74 -55
- package/lib/datasource/test-e2e.js +29 -2
- package/lib/datasource/unified-validation-run-body.js +3 -1
- package/lib/datasource/unified-validation-run-resolve.js +45 -12
- package/lib/datasource/unified-validation-run.js +47 -18
- package/lib/datasource/validate.js +37 -20
- package/lib/deployment/environment-poll-status.js +138 -0
- package/lib/deployment/environment.js +42 -34
- package/lib/external-system/download.js +21 -20
- package/lib/external-system/generator.js +5 -1
- package/lib/external-system/integration-auth-context.js +45 -0
- package/lib/external-system/test-auth.js +12 -5
- package/lib/external-system/test-system-level.js +25 -4
- package/lib/external-system/test.js +9 -7
- package/lib/generator/external-controller-manifest.js +15 -0
- package/lib/governance/governance-cli-errors.js +96 -0
- package/lib/governance/governance-download-display.js +38 -0
- package/lib/governance/governance-pack-loader.js +70 -0
- package/lib/governance/governance-run-display.js +309 -0
- package/lib/identity/identity-apply-core.js +108 -0
- package/lib/identity/identity-apply-phases.js +244 -0
- package/lib/identity/identity-apply-service.js +77 -0
- package/lib/identity/identity-csv-parser.js +114 -0
- package/lib/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/internal/fs-real-sync.js +29 -1
- 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.js +471 -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 +51 -0
- package/lib/protection/convert-batch.js +90 -0
- package/lib/protection/load.js +41 -0
- package/lib/protection/paths.js +158 -0
- package/lib/protection/preflight-datasource-ready.js +46 -0
- package/lib/protection/protection-create-probe-lines.js +142 -0
- package/lib/protection/protection-create-probes.js +95 -0
- package/lib/protection/protection-create-scaffold.js +281 -0
- package/lib/protection/protection-display-helpers.js +445 -0
- package/lib/protection/protection-display.js +215 -0
- package/lib/protection/protection-preset-registry.js +97 -0
- package/lib/protection/report-exit.js +62 -0
- package/lib/protection/resolve.js +202 -0
- package/lib/protection/run-commands-validate.js +130 -0
- package/lib/protection/run-commands.js +216 -0
- package/lib/protection/run-protection-create-helpers.js +134 -0
- package/lib/protection/run-protection-create-probes-flow.js +43 -0
- package/lib/protection/run-protection-create.js +191 -0
- package/lib/protection/scope.js +22 -0
- package/lib/protection/sync-after-upload.js +64 -0
- package/lib/protection/upload-batch.js +127 -0
- package/lib/protection/validate-batch.js +113 -0
- package/lib/protection/validate-local.js +65 -0
- package/lib/resolvers/dimension-file.js +29 -4
- package/lib/schema/application-schema.json +1 -1
- package/lib/schema/datasource-test-run.schema.json +89 -6
- package/lib/schema/external-datasource.schema.json +2636 -2588
- package/lib/schema/external-system.schema.json +1089 -761
- package/lib/schema/flag-map-validation-run.json +2 -2
- package/lib/schema/infra.parameter.yaml +45 -0
- package/lib/schema/protection.schema.json +374 -0
- package/lib/schema/type/credential-field-registry.json +642 -0
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/agent-trust-run-display.js +231 -0
- package/lib/utils/agent-trust-run-exit.js +59 -0
- package/lib/utils/agent-trust-wait-ui.js +94 -0
- package/lib/utils/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/cli-exit-codes.js +30 -0
- package/lib/utils/cli-secrets-error-format.js +83 -21
- package/lib/utils/cli-sync-options.js +33 -0
- package/lib/utils/cli-utils.js +16 -4
- package/lib/utils/compose-generate-docker-compose.js +4 -0
- package/lib/utils/compose-traefik-ingress-base.js +16 -1
- package/lib/utils/config-secrets-path.js +46 -0
- package/lib/utils/controller-url.js +88 -1
- package/lib/utils/credential-push-ui.js +90 -0
- package/lib/utils/credential-secrets-env.js +137 -9
- package/lib/utils/dataplane-cli-version-gate.js +202 -0
- package/lib/utils/dataplane-cli-version-help.js +74 -0
- package/lib/utils/dataplane-resolver.js +129 -2
- package/lib/utils/datasource-export-display.js +137 -0
- package/lib/utils/datasource-load-display.js +174 -0
- package/lib/utils/datasource-load-export-ui.js +120 -0
- package/lib/utils/datasource-test-run-capability-scope.js +3 -3
- package/lib/utils/datasource-test-run-display.js +3 -3
- package/lib/utils/datasource-test-run-tty-log.js +5 -1
- package/lib/utils/declarative-url-ports.js +3 -3
- package/lib/utils/deployment-auth-mode.js +41 -0
- package/lib/utils/developer-volume-id.js +33 -0
- 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 +1 -0
- package/lib/utils/external-system-readiness-core.js +10 -0
- package/lib/utils/external-system-readiness-display.js +80 -23
- package/lib/utils/health-check.js +4 -1
- package/lib/utils/help-builder.js +35 -26
- 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 +24 -0
- package/lib/utils/pipeline-upload-auth-retry.js +82 -0
- package/lib/utils/platform-controller-url.js +156 -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/secrets-generator.js +31 -25
- package/lib/utils/secrets-missing-error.js +164 -0
- package/lib/utils/semver-compare.js +127 -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 +98 -0
- 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 +22 -7
- package/lib/utils/validation-run-stage.js +45 -0
- package/lib/validation/wizard-config-validator.js +58 -19
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +2 -1
- package/templates/applications/dataplane/env.template +36 -1
- package/templates/applications/dataplane/rbac.yaml +4 -0
- package/templates/applications/miso-controller/env.template +16 -0
- package/templates/external-system/README.md.hbs +116 -11
- package/templates/python/docker-compose.hbs +19 -18
- package/templates/typescript/docker-compose.hbs +18 -17
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build missing-secrets errors with remediation hints (no secret values).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Missing kv:// error message for resolve and env generation
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const { formatMissingSecretsFileInfo } = require('./secrets-helpers');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} line
|
|
15
|
+
* @returns {boolean}
|
|
16
|
+
*/
|
|
17
|
+
function isCommentOrEmptyLine(line) {
|
|
18
|
+
const t = String(line).trim();
|
|
19
|
+
return t === '' || t.startsWith('#');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} kvRef - e.g. kv://hubspot-demo/clientSecret
|
|
24
|
+
* @returns {string}
|
|
25
|
+
*/
|
|
26
|
+
function kvRefToSecretSetKey(kvRef) {
|
|
27
|
+
const s = String(kvRef || '').trim();
|
|
28
|
+
return s.replace(/^kv:\/\//i, '');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {string} envTemplate
|
|
33
|
+
* @param {string} kvRef
|
|
34
|
+
* @returns {string|null}
|
|
35
|
+
*/
|
|
36
|
+
function findActiveEnvLineForKvRef(envTemplate, kvRef) {
|
|
37
|
+
if (!envTemplate || !kvRef) return null;
|
|
38
|
+
const needle = String(kvRef).trim();
|
|
39
|
+
for (const line of envTemplate.split('\n')) {
|
|
40
|
+
if (isCommentOrEmptyLine(line)) continue;
|
|
41
|
+
if (line.includes(needle)) return line.trim();
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const OAUTH_STALE_KV_PATH_RE = /\/(clientId|clientSecret|tokenUrl|authorizationUrl)$/i;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {string[]} missing
|
|
50
|
+
* @returns {boolean}
|
|
51
|
+
*/
|
|
52
|
+
function hasStaleOAuthTemplatePaths(missing) {
|
|
53
|
+
return missing.some((ref) => OAUTH_STALE_KV_PATH_RE.test(String(ref)));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {string} key
|
|
58
|
+
* @param {Object} secrets
|
|
59
|
+
* @returns {boolean}
|
|
60
|
+
*/
|
|
61
|
+
function isPopulatedSecretKey(key, secrets) {
|
|
62
|
+
const value = secrets[key];
|
|
63
|
+
return value !== undefined && value !== null && String(value).trim() !== '';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Other populated secret paths that share the same path segment after the namespace.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} pathStr - Secret path without kv:// (e.g. my-app-e2e/clientId)
|
|
70
|
+
* @param {Object} secrets
|
|
71
|
+
* @returns {string[]}
|
|
72
|
+
*/
|
|
73
|
+
function findPopulatedAlternateKvPaths(pathStr, secrets) {
|
|
74
|
+
if (!secrets || typeof secrets !== 'object' || !pathStr) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
const slash = pathStr.indexOf('/');
|
|
78
|
+
if (slash <= 0) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
const missingNamespace = pathStr.slice(0, slash);
|
|
82
|
+
const segment = pathStr.slice(slash + 1);
|
|
83
|
+
return Object.keys(secrets).filter((key) => {
|
|
84
|
+
if (typeof key !== 'string' || !key.includes('/')) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const keySlash = key.indexOf('/');
|
|
88
|
+
const namespace = key.slice(0, keySlash);
|
|
89
|
+
const keySegment = key.slice(keySlash + 1);
|
|
90
|
+
return (
|
|
91
|
+
keySegment === segment &&
|
|
92
|
+
namespace !== missingNamespace &&
|
|
93
|
+
isPopulatedSecretKey(key, secrets)
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @param {string[]} missing - kv:// refs
|
|
100
|
+
* @param {Object} [secrets]
|
|
101
|
+
* @param {string} [appName]
|
|
102
|
+
* @returns {string[]}
|
|
103
|
+
*/
|
|
104
|
+
function buildKvNamespaceAlternateHints(missing, secrets, appName) {
|
|
105
|
+
const lines = [];
|
|
106
|
+
const seen = new Set();
|
|
107
|
+
for (const ref of missing) {
|
|
108
|
+
const pathStr = kvRefToSecretSetKey(ref);
|
|
109
|
+
for (const alt of findPopulatedAlternateKvPaths(pathStr, secrets)) {
|
|
110
|
+
const line = `Found kv://${alt} in secrets; env.template references kv://${pathStr}.`;
|
|
111
|
+
if (seen.has(line)) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
seen.add(line);
|
|
115
|
+
lines.push(line);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (lines.length === 0) {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
const repairTail = appName
|
|
122
|
+
? `Use aifabrix secret set <key> for each missing path, or aifabrix repair ${appName} to align env.template.`
|
|
123
|
+
: 'Use aifabrix secret set <key> for each missing path, or repair to align env.template.';
|
|
124
|
+
lines.push(repairTail);
|
|
125
|
+
return lines;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @param {Object} opts
|
|
130
|
+
* @param {string[]} opts.missing
|
|
131
|
+
* @param {Object|string|null} [opts.secretsFilePaths]
|
|
132
|
+
* @param {string} [opts.appName]
|
|
133
|
+
* @param {string} [opts.envTemplatePath]
|
|
134
|
+
* @param {string} [opts.envTemplate]
|
|
135
|
+
* @param {Object} [opts.secrets]
|
|
136
|
+
* @returns {string}
|
|
137
|
+
*/
|
|
138
|
+
function buildMissingSecretsErrorMessage(opts) {
|
|
139
|
+
const { missing, secretsFilePaths, appName, envTemplatePath, envTemplate, secrets } = opts;
|
|
140
|
+
const fileInfo = formatMissingSecretsFileInfo(secretsFilePaths);
|
|
141
|
+
const lines = [`Missing secrets: ${missing.join(', ')}`];
|
|
142
|
+
if (fileInfo) lines.push(fileInfo.trim());
|
|
143
|
+
if (envTemplatePath) lines.push(`Env-template-path: ${envTemplatePath}`);
|
|
144
|
+
for (const ref of missing) {
|
|
145
|
+
const templateLine = findActiveEnvLineForKvRef(envTemplate, ref);
|
|
146
|
+
if (templateLine) lines.push(`Env-template-line: ${ref}|${templateLine}`);
|
|
147
|
+
}
|
|
148
|
+
if (appName) lines.push(`App-name: ${appName}`);
|
|
149
|
+
if (hasStaleOAuthTemplatePaths(missing) && appName) {
|
|
150
|
+
lines.push(`Stale-template-hint: oauth-leftover | repair ${appName}`);
|
|
151
|
+
}
|
|
152
|
+
for (const hint of buildKvNamespaceAlternateHints(missing, secrets, appName)) {
|
|
153
|
+
lines.push(`Alternate-kv-hint: ${hint}`);
|
|
154
|
+
}
|
|
155
|
+
lines.push('Remediation-hint: comment-or-delete-template-line | secret-set');
|
|
156
|
+
return `${lines.join('\n')}\n`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
module.exports = {
|
|
160
|
+
kvRefToSecretSetKey,
|
|
161
|
+
findActiveEnvLineForKvRef,
|
|
162
|
+
findPopulatedAlternateKvPaths,
|
|
163
|
+
buildMissingSecretsErrorMessage
|
|
164
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal semver comparator (no `semver` npm dependency).
|
|
3
|
+
*
|
|
4
|
+
* Supports `MAJOR.MINOR.PATCH` and optional prerelease suffix
|
|
5
|
+
* `-<prerelease>` (e.g. `2.45.0-beta.1`). Optional leading `v` is tolerated.
|
|
6
|
+
* Prerelease versions always rank **before** the same MAJOR.MINOR.PATCH
|
|
7
|
+
* without a prerelease (semver §11.3).
|
|
8
|
+
*
|
|
9
|
+
* @fileoverview Internal semver compare for Builder CLI compatibility gate
|
|
10
|
+
* @author AI Fabrix Team
|
|
11
|
+
* @version 2.0.0
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
'use strict';
|
|
15
|
+
|
|
16
|
+
const SEMVER_PATTERN = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z][-0-9A-Za-z.]*))?$/;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Parse a semver-like string into a comparable structure.
|
|
20
|
+
* @param {string} input - Version string (e.g. "2.45.0" or "v2.45.0-beta.1")
|
|
21
|
+
* @returns {{ major:number, minor:number, patch:number, prerelease:string[] }|null}
|
|
22
|
+
* Parsed parts or null when input does not match the supported grammar
|
|
23
|
+
*/
|
|
24
|
+
function parseSemver(input) {
|
|
25
|
+
if (typeof input !== 'string') {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const match = SEMVER_PATTERN.exec(input.trim());
|
|
29
|
+
if (!match) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const [, major, minor, patch, prerelease] = match;
|
|
33
|
+
return {
|
|
34
|
+
major: Number(major),
|
|
35
|
+
minor: Number(minor),
|
|
36
|
+
patch: Number(patch),
|
|
37
|
+
prerelease: prerelease ? prerelease.split('.') : []
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Check whether a string is a supported semver (incl. optional prerelease).
|
|
43
|
+
* @param {string} input
|
|
44
|
+
* @returns {boolean}
|
|
45
|
+
*/
|
|
46
|
+
function isValidSemver(input) {
|
|
47
|
+
return parseSemver(input) !== null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Compare two prerelease identifier arrays per semver §11.4.
|
|
52
|
+
* @private
|
|
53
|
+
* @param {string[]} a
|
|
54
|
+
* @param {string[]} b
|
|
55
|
+
* @returns {-1|0|1}
|
|
56
|
+
*/
|
|
57
|
+
function comparePrereleaseIds(a, b) {
|
|
58
|
+
const len = Math.max(a.length, b.length);
|
|
59
|
+
for (let i = 0; i < len; i += 1) {
|
|
60
|
+
if (a[i] === undefined) return -1;
|
|
61
|
+
if (b[i] === undefined) return 1;
|
|
62
|
+
const aNum = /^\d+$/.test(a[i]);
|
|
63
|
+
const bNum = /^\d+$/.test(b[i]);
|
|
64
|
+
if (aNum && bNum) {
|
|
65
|
+
const diff = Number(a[i]) - Number(b[i]);
|
|
66
|
+
if (diff !== 0) return diff > 0 ? 1 : -1;
|
|
67
|
+
} else if (aNum) {
|
|
68
|
+
return -1;
|
|
69
|
+
} else if (bNum) {
|
|
70
|
+
return 1;
|
|
71
|
+
} else if (a[i] !== b[i]) {
|
|
72
|
+
return a[i] > b[i] ? 1 : -1;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Compare two semver strings.
|
|
80
|
+
*
|
|
81
|
+
* @param {string} a
|
|
82
|
+
* @param {string} b
|
|
83
|
+
* @returns {-1|0|1} `-1` when `a < b`, `0` when equal, `1` when `a > b`
|
|
84
|
+
* @throws {Error} When either argument is not a valid semver
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* compareSemver('2.44.0', '2.45.0') === -1
|
|
88
|
+
* compareSemver('2.45.0', '2.45.0') === 0
|
|
89
|
+
* compareSemver('2.45.0-beta.1', '2.45.0') === -1
|
|
90
|
+
*/
|
|
91
|
+
function compareSemver(a, b) {
|
|
92
|
+
const parsedA = parseSemver(a);
|
|
93
|
+
const parsedB = parseSemver(b);
|
|
94
|
+
if (!parsedA) throw new Error(`Invalid semver: "${a}"`);
|
|
95
|
+
if (!parsedB) throw new Error(`Invalid semver: "${b}"`);
|
|
96
|
+
|
|
97
|
+
if (parsedA.major !== parsedB.major) return parsedA.major > parsedB.major ? 1 : -1;
|
|
98
|
+
if (parsedA.minor !== parsedB.minor) return parsedA.minor > parsedB.minor ? 1 : -1;
|
|
99
|
+
if (parsedA.patch !== parsedB.patch) return parsedA.patch > parsedB.patch ? 1 : -1;
|
|
100
|
+
|
|
101
|
+
// §11.3 — Prerelease has lower precedence than the same MAJOR.MINOR.PATCH release.
|
|
102
|
+
if (parsedA.prerelease.length === 0 && parsedB.prerelease.length === 0) return 0;
|
|
103
|
+
if (parsedA.prerelease.length === 0) return 1;
|
|
104
|
+
if (parsedB.prerelease.length === 0) return -1;
|
|
105
|
+
|
|
106
|
+
return comparePrereleaseIds(parsedA.prerelease, parsedB.prerelease);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Convenience: true when `current >= required`.
|
|
111
|
+
* Returns `true` when `required` is empty/null/undefined (no enforcement).
|
|
112
|
+
* @param {string} current
|
|
113
|
+
* @param {string} [required]
|
|
114
|
+
* @returns {boolean}
|
|
115
|
+
* @throws {Error} If either provided value is not valid semver
|
|
116
|
+
*/
|
|
117
|
+
function isAtLeast(current, required) {
|
|
118
|
+
if (required === undefined || required === null || required === '') return true;
|
|
119
|
+
return compareSemver(current, required) >= 0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
module.exports = {
|
|
123
|
+
parseSemver,
|
|
124
|
+
isValidSemver,
|
|
125
|
+
compareSemver,
|
|
126
|
+
isAtLeast
|
|
127
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infer which platform service a request URL targets (CLI error copy).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Service label heuristics for network error messages
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/** @type {readonly string[]} */
|
|
12
|
+
const DATAPLANE_PATH_PREFIXES = Object.freeze([
|
|
13
|
+
'/api/v1/external',
|
|
14
|
+
'/api/v1/validation',
|
|
15
|
+
'/api/v1/records',
|
|
16
|
+
'/api/v1/wizard',
|
|
17
|
+
'/api/v1/admin',
|
|
18
|
+
'/api/v1/health',
|
|
19
|
+
'/api/v1/dataplane',
|
|
20
|
+
'/api/v1/policy',
|
|
21
|
+
'/api/v1/mcp',
|
|
22
|
+
'/api/v1/ide',
|
|
23
|
+
'/api/v1/knowledgebase'
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
/** @type {readonly string[]} */
|
|
27
|
+
const CONTROLLER_PATH_PREFIXES = Object.freeze([
|
|
28
|
+
'/api/v1/auth',
|
|
29
|
+
'/api/v1/applications',
|
|
30
|
+
'/api/v1/pipeline',
|
|
31
|
+
'/api/v1/environments',
|
|
32
|
+
'/api/v1/deployments',
|
|
33
|
+
'/api/v1/datasources',
|
|
34
|
+
'/api/v1/external-systems',
|
|
35
|
+
'/api/controller'
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {string} url - Full request URL
|
|
40
|
+
* @returns {'dataplane'|'controller'|'service'}
|
|
41
|
+
*/
|
|
42
|
+
function inferServiceLabelFromUrl(url) {
|
|
43
|
+
if (!url || typeof url !== 'string') {
|
|
44
|
+
return 'service';
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const path = new URL(url.trim()).pathname || '';
|
|
48
|
+
for (const prefix of DATAPLANE_PATH_PREFIXES) {
|
|
49
|
+
if (path.startsWith(prefix)) {
|
|
50
|
+
return 'dataplane';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (const prefix of CONTROLLER_PATH_PREFIXES) {
|
|
54
|
+
if (path.startsWith(prefix)) {
|
|
55
|
+
return 'controller';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} catch {
|
|
59
|
+
return 'service';
|
|
60
|
+
}
|
|
61
|
+
return 'service';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param {'dataplane'|'controller'|'service'} label
|
|
66
|
+
* @returns {string}
|
|
67
|
+
*/
|
|
68
|
+
function serviceLabelDisplayName(label) {
|
|
69
|
+
if (label === 'dataplane') {
|
|
70
|
+
return 'dataplane';
|
|
71
|
+
}
|
|
72
|
+
if (label === 'controller') {
|
|
73
|
+
return 'controller';
|
|
74
|
+
}
|
|
75
|
+
return 'service';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = {
|
|
79
|
+
inferServiceLabelFromUrl,
|
|
80
|
+
serviceLabelDisplayName
|
|
81
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Suppress verbose CLI detail during `aifabrix setup` (guided milestones only).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Setup quiet output flag
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @returns {boolean} True while `aifabrix setup` is running
|
|
13
|
+
*/
|
|
14
|
+
function isSetupQuietOutput() {
|
|
15
|
+
try {
|
|
16
|
+
const { isSetupCommandFlow } = require('../commands/setup-run-context');
|
|
17
|
+
return isSetupCommandFlow();
|
|
18
|
+
} catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = {
|
|
24
|
+
isSetupQuietOutput
|
|
25
|
+
};
|
|
@@ -110,6 +110,20 @@ function updateDatabaseConfig(variables, options, appName) {
|
|
|
110
110
|
* @param {Object} options - CLI options
|
|
111
111
|
* @param {Object} config - Final configuration
|
|
112
112
|
*/
|
|
113
|
+
async function patchEnvTemplateRelativePathKey(appPath, appName) {
|
|
114
|
+
const fsSync = require('fs');
|
|
115
|
+
const envTemplatePath = path.join(appPath, 'env.template');
|
|
116
|
+
if (!fsSync.existsSync(envTemplatePath)) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const { renameAppRelativePathEnvKeyInTemplate } = require('./app-relative-path-env');
|
|
120
|
+
const raw = await fs.readFile(envTemplatePath, 'utf8');
|
|
121
|
+
const patched = renameAppRelativePathEnvKeyInTemplate(raw, appName);
|
|
122
|
+
if (patched !== raw) {
|
|
123
|
+
await fs.writeFile(envTemplatePath, patched, 'utf8');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
113
127
|
async function updateTemplateVariables(appPath, appName, options, config) {
|
|
114
128
|
const { resolveApplicationConfigPath } = require('./app-config-resolver');
|
|
115
129
|
const { loadConfigFile, writeConfigFile } = require('./config-format');
|
|
@@ -123,6 +137,7 @@ async function updateTemplateVariables(appPath, appName, options, config) {
|
|
|
123
137
|
updateDatabaseConfig(variables, options, appName);
|
|
124
138
|
|
|
125
139
|
writeConfigFile(variablesPath, variables);
|
|
140
|
+
await patchEnvTemplateRelativePathKey(appPath, appName);
|
|
126
141
|
} catch (error) {
|
|
127
142
|
if (error.message && !error.message.includes('not found')) {
|
|
128
143
|
logger.warn(chalk.yellow(`⚠ Warning: Could not update application config: ${error.message}`));
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client credential loading from secrets.local.yaml and environment.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Client credentials helpers for token-manager
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const yaml = require('js-yaml');
|
|
13
|
+
const logger = require('./logger');
|
|
14
|
+
const pathsUtil = require('./paths');
|
|
15
|
+
|
|
16
|
+
/** App key used for dataplane client credentials in secrets.local.yaml */
|
|
17
|
+
const DATAPLANE_APP_KEY = 'dataplane';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @returns {string}
|
|
21
|
+
*/
|
|
22
|
+
function getSecretsFilePath() {
|
|
23
|
+
return pathsUtil.getPrimaryUserSecretsLocalPath();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Validate that secrets.local.yaml contains dataplane client credentials.
|
|
28
|
+
* @param {string} [secretsFilePath] - Path to secrets file; defaults to ~/.aifabrix/secrets.local.yaml
|
|
29
|
+
* @returns {{ valid: boolean, hint?: string }}
|
|
30
|
+
*/
|
|
31
|
+
function validateDataplaneSecrets(secretsFilePath) {
|
|
32
|
+
const filePath = secretsFilePath || getSecretsFilePath();
|
|
33
|
+
const hint = 'Dataplane credentials are missing. Run: aifabrix app rotate-secret dataplane';
|
|
34
|
+
try {
|
|
35
|
+
if (!fs.existsSync(filePath)) {
|
|
36
|
+
return { valid: false, hint };
|
|
37
|
+
}
|
|
38
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
39
|
+
const secrets = yaml.load(content) || {};
|
|
40
|
+
const clientIdKey = `${DATAPLANE_APP_KEY}-client-idKeyVault`;
|
|
41
|
+
const clientSecretKey = `${DATAPLANE_APP_KEY}-client-secretKeyVault`;
|
|
42
|
+
const hasId =
|
|
43
|
+
secrets[clientIdKey] !== null &&
|
|
44
|
+
secrets[clientIdKey] !== undefined &&
|
|
45
|
+
String(secrets[clientIdKey]).trim() !== '';
|
|
46
|
+
const hasSecret =
|
|
47
|
+
secrets[clientSecretKey] !== null &&
|
|
48
|
+
secrets[clientSecretKey] !== undefined &&
|
|
49
|
+
String(secrets[clientSecretKey]).trim() !== '';
|
|
50
|
+
if (hasId && hasSecret) {
|
|
51
|
+
return { valid: true };
|
|
52
|
+
}
|
|
53
|
+
return { valid: false, hint };
|
|
54
|
+
} catch {
|
|
55
|
+
return { valid: false, hint };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Read client id/secret for one app key from secrets.local.yaml.
|
|
61
|
+
* @param {string} appName - Application key
|
|
62
|
+
* @returns {{ clientId: string, clientSecret: string }|null}
|
|
63
|
+
*/
|
|
64
|
+
function readClientCredentialsFromSecretsFile(appName) {
|
|
65
|
+
try {
|
|
66
|
+
const secretsFile = getSecretsFilePath();
|
|
67
|
+
if (!fs.existsSync(secretsFile)) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const content = fs.readFileSync(secretsFile, 'utf8');
|
|
71
|
+
const secrets = yaml.load(content) || {};
|
|
72
|
+
const clientId = secrets[`${appName}-client-idKeyVault`];
|
|
73
|
+
const clientSecret = secrets[`${appName}-client-secretKeyVault`];
|
|
74
|
+
if (clientId && clientSecret) {
|
|
75
|
+
return {
|
|
76
|
+
clientId: String(clientId),
|
|
77
|
+
clientSecret: String(clientSecret)
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
} catch (error) {
|
|
81
|
+
logger.warn(`Failed to load credentials from secrets.local.yaml: ${error.message}`);
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* App keys to try when resolving client credentials (integration app, then dataplane).
|
|
88
|
+
* @param {string} appName
|
|
89
|
+
* @returns {string[]}
|
|
90
|
+
*/
|
|
91
|
+
function credentialAppKeysToTry(appName) {
|
|
92
|
+
const keys = [appName];
|
|
93
|
+
if (appName !== DATAPLANE_APP_KEY) {
|
|
94
|
+
keys.push(DATAPLANE_APP_KEY);
|
|
95
|
+
}
|
|
96
|
+
return keys;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @returns {{ clientId: string, clientSecret: string }|null}
|
|
101
|
+
*/
|
|
102
|
+
function readClientCredentialsFromEnv() {
|
|
103
|
+
const pairs = [
|
|
104
|
+
[process.env.CLIENTID || process.env.CLIENT_ID, process.env.CLIENTSECRET || process.env.CLIENT_SECRET],
|
|
105
|
+
[
|
|
106
|
+
process.env.MISO_CLIENTID || process.env.MISO_CLIENT_ID,
|
|
107
|
+
process.env.MISO_CLIENTSECRET || process.env.MISO_CLIENT_SECRET
|
|
108
|
+
]
|
|
109
|
+
];
|
|
110
|
+
for (const [clientId, clientSecret] of pairs) {
|
|
111
|
+
if (clientId && clientSecret) {
|
|
112
|
+
return {
|
|
113
|
+
clientId: String(clientId).trim(),
|
|
114
|
+
clientSecret: String(clientSecret).trim()
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Decrypt secure:// client credential entries from secrets.local.yaml when needed.
|
|
123
|
+
* @param {string} appName
|
|
124
|
+
* @param {{ clientId: string, clientSecret: string }} raw
|
|
125
|
+
* @returns {Promise<{ clientId: string, clientSecret: string }|null>}
|
|
126
|
+
*/
|
|
127
|
+
async function decryptClientCredentialsFromSecrets(appName, raw) {
|
|
128
|
+
const { isEncrypted } = require('./secrets-encryption');
|
|
129
|
+
if (!isEncrypted(raw.clientId) && !isEncrypted(raw.clientSecret)) {
|
|
130
|
+
return raw;
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
const { decryptSecretsObject } = require('../core/secrets-load');
|
|
134
|
+
const idKey = `${appName}-client-idKeyVault`;
|
|
135
|
+
const secretKey = `${appName}-client-secretKeyVault`;
|
|
136
|
+
const decrypted = await decryptSecretsObject({
|
|
137
|
+
[idKey]: raw.clientId,
|
|
138
|
+
[secretKey]: raw.clientSecret
|
|
139
|
+
});
|
|
140
|
+
const clientId = decrypted[idKey];
|
|
141
|
+
const clientSecret = decrypted[secretKey];
|
|
142
|
+
if (clientId && clientSecret) {
|
|
143
|
+
return {
|
|
144
|
+
clientId: String(clientId),
|
|
145
|
+
clientSecret: String(clientSecret)
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
} catch (error) {
|
|
149
|
+
logger.warn(`Failed to decrypt client credentials for ${appName}: ${error.message}`);
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @param {string} appName
|
|
156
|
+
* @returns {Promise<{ clientId: string, clientSecret: string }|null>}
|
|
157
|
+
*/
|
|
158
|
+
async function readClientCredentialsFromSecretsFileDecrypted(appName) {
|
|
159
|
+
const raw = readClientCredentialsFromSecretsFile(appName);
|
|
160
|
+
if (!raw) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
return decryptClientCredentialsFromSecrets(appName, raw);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @param {string} appName
|
|
168
|
+
* @returns {Promise<{ credentialApp: string, credentials: { clientId: string, clientSecret: string }|null }>}
|
|
169
|
+
*/
|
|
170
|
+
async function resolveCredentialAppForDeployment(appName) {
|
|
171
|
+
const fromEnv = readClientCredentialsFromEnv();
|
|
172
|
+
if (fromEnv) {
|
|
173
|
+
return { credentialApp: appName, credentials: fromEnv };
|
|
174
|
+
}
|
|
175
|
+
for (const key of credentialAppKeysToTry(appName)) {
|
|
176
|
+
const fromFile = await readClientCredentialsFromSecretsFileDecrypted(key);
|
|
177
|
+
if (fromFile) {
|
|
178
|
+
return { credentialApp: key, credentials: fromFile };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return { credentialApp: appName, credentials: null };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* @param {string} appName
|
|
186
|
+
* @returns {Promise<{ clientId: string, clientSecret: string }|null>}
|
|
187
|
+
*/
|
|
188
|
+
async function loadClientCredentials(appName) {
|
|
189
|
+
if (!appName || typeof appName !== 'string') {
|
|
190
|
+
throw new Error('App name is required and must be a string');
|
|
191
|
+
}
|
|
192
|
+
const resolved = await resolveCredentialAppForDeployment(appName);
|
|
193
|
+
return resolved.credentials;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
module.exports = {
|
|
197
|
+
DATAPLANE_APP_KEY,
|
|
198
|
+
validateDataplaneSecrets,
|
|
199
|
+
loadClientCredentials,
|
|
200
|
+
resolveCredentialAppForDeployment
|
|
201
|
+
};
|
|
@@ -39,6 +39,10 @@ function resetRefreshWarnedUrlsForTesting() {
|
|
|
39
39
|
* @param {string} errorMessage - Full error message to log
|
|
40
40
|
*/
|
|
41
41
|
function warnRefreshTokenExpiredOnce(controllerUrl, errorMessage) {
|
|
42
|
+
const { isSetupPlatformFlow } = require('../commands/setup-run-context');
|
|
43
|
+
if (isSetupPlatformFlow()) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
42
46
|
const key = (controllerUrl && typeof controllerUrl === 'string' && controllerUrl.trim())
|
|
43
47
|
? config.normalizeControllerUrl(controllerUrl)
|
|
44
48
|
: '__no_url__';
|
|
@@ -72,6 +76,10 @@ function formatRefreshFailureMessage(errorMessage, controllerUrl) {
|
|
|
72
76
|
* @param {string} errorMessage - Raw error message
|
|
73
77
|
*/
|
|
74
78
|
function warnRefreshFailureOnce(controllerUrl, errorMessage) {
|
|
79
|
+
const { isSetupPlatformFlow } = require('../commands/setup-run-context');
|
|
80
|
+
if (isSetupPlatformFlow()) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
75
83
|
const key = (controllerUrl && typeof controllerUrl === 'string' && controllerUrl.trim())
|
|
76
84
|
? config.normalizeControllerUrl(controllerUrl)
|
|
77
85
|
: '__no_url__';
|
|
@@ -79,11 +79,21 @@ async function callTokenApi(controllerUrl, credentials) {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
const responseData = response.data;
|
|
82
|
-
|
|
82
|
+
const token =
|
|
83
|
+
responseData?.token ||
|
|
84
|
+
responseData?.data?.token ||
|
|
85
|
+
responseData?.data?.data?.token ||
|
|
86
|
+
responseData?.accessToken;
|
|
87
|
+
if (!token) {
|
|
83
88
|
throw new Error('Invalid response: missing token');
|
|
84
89
|
}
|
|
85
90
|
|
|
86
|
-
return
|
|
91
|
+
return {
|
|
92
|
+
...responseData,
|
|
93
|
+
token,
|
|
94
|
+
expiresIn: responseData.expiresIn ?? responseData.data?.expiresIn,
|
|
95
|
+
expiresAt: responseData.expiresAt ?? responseData.data?.expiresAt
|
|
96
|
+
};
|
|
87
97
|
}
|
|
88
98
|
|
|
89
99
|
/**
|