@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
|
@@ -280,21 +280,146 @@ function storedCountFromResponse(res, fallback) {
|
|
|
280
280
|
* @param {Array<{ key: string, value: string }>} items - Items to send
|
|
281
281
|
* @returns {Promise<{ pushed: number, warning?: string }>}
|
|
282
282
|
*/
|
|
283
|
+
/**
|
|
284
|
+
* For bearerToken auth, credential template resolves security.token only.
|
|
285
|
+
* Legacy .env keys KV_*_APIKEY with a plain token wrongly map to kv://.../apiKey.
|
|
286
|
+
* Prefer token path; drop duplicate apiKey when both would be pushed.
|
|
287
|
+
*
|
|
288
|
+
* @param {Map<string, string>} itemsByKey
|
|
289
|
+
* @param {string} [systemKey]
|
|
290
|
+
* @param {Object} [authConfig]
|
|
291
|
+
*/
|
|
292
|
+
function reconcileBearerTokenSecretPaths(itemsByKey, systemKey, authConfig) {
|
|
293
|
+
const method = (
|
|
294
|
+
authConfig?.method ||
|
|
295
|
+
authConfig?.type ||
|
|
296
|
+
''
|
|
297
|
+
)
|
|
298
|
+
.toString()
|
|
299
|
+
.toLowerCase()
|
|
300
|
+
.replace(/_/g, '');
|
|
301
|
+
if (method !== 'bearertoken' || !systemKey) return;
|
|
302
|
+
const tokenPath = `kv://${systemKey}/token`;
|
|
303
|
+
const apiKeyPath = `kv://${systemKey}/apiKey`;
|
|
304
|
+
if (!itemsByKey.has(apiKeyPath)) return;
|
|
305
|
+
if (!itemsByKey.has(tokenPath)) {
|
|
306
|
+
itemsByKey.set(tokenPath, itemsByKey.get(apiKeyPath));
|
|
307
|
+
}
|
|
308
|
+
itemsByKey.delete(apiKeyPath);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Optional dev/lab dataplane API key (Bearer bypass) for credential secret push only.
|
|
313
|
+
* Does not alter pipeline publish auth when AIFABRIX_DEPLOYMENT_AUTH=client-credentials.
|
|
314
|
+
* @returns {string|null}
|
|
315
|
+
*/
|
|
316
|
+
function resolveLabDataplaneApiKey() {
|
|
317
|
+
const candidates = [
|
|
318
|
+
process.env.AIFABRIX_DATAPLANE_API_KEY,
|
|
319
|
+
process.env.DATAPLANE_API_KEY,
|
|
320
|
+
process.env.API_KEY
|
|
321
|
+
];
|
|
322
|
+
for (const raw of candidates) {
|
|
323
|
+
const trimmed = typeof raw === 'string' ? raw.trim() : '';
|
|
324
|
+
if (trimmed) return trimmed;
|
|
325
|
+
}
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* @param {string} token
|
|
331
|
+
* @returns {{ type: string, token: string }}
|
|
332
|
+
*/
|
|
333
|
+
function apiKeyAuthConfig(token) {
|
|
334
|
+
return { type: 'bearer', token };
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const PERMISSION_DENIED_WARNING =
|
|
338
|
+
'Could not push credential secrets (permission denied or unauthenticated). Ensure dataplane role has credential:create if you use KV_* in .env, or set AIFABRIX_DATAPLANE_API_KEY for local lab bypass.';
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* @param {Object} res
|
|
342
|
+
* @returns {boolean}
|
|
343
|
+
*/
|
|
344
|
+
function isStoreResponseFailed(res) {
|
|
345
|
+
return Boolean(res && (res.success === false || res.data?.success === false));
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* @param {Object} res
|
|
350
|
+
* @returns {number|undefined}
|
|
351
|
+
*/
|
|
352
|
+
function storeResponseStatus(res) {
|
|
353
|
+
return res?.status ?? res?.statusCode;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* @param {Object} res
|
|
358
|
+
* @returns {{ pushed: number, warning: string }}
|
|
359
|
+
*/
|
|
360
|
+
function storeErrorWarning(res) {
|
|
361
|
+
const errMsg =
|
|
362
|
+
res.formattedError ||
|
|
363
|
+
res.data?.formattedError ||
|
|
364
|
+
res.error ||
|
|
365
|
+
res.data?.error ||
|
|
366
|
+
'Failed to push credential secrets to dataplane.';
|
|
367
|
+
return { pushed: 0, warning: errMsg };
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* @param {string} dataplaneUrl
|
|
372
|
+
* @param {Object} authConfig
|
|
373
|
+
* @param {Array<{ key: string, value: string }>} items
|
|
374
|
+
* @returns {Promise<{ pushed: number }|null>}
|
|
375
|
+
*/
|
|
376
|
+
async function tryLabApiKeyRetry(dataplaneUrl, authConfig, items) {
|
|
377
|
+
const labKey = resolveLabDataplaneApiKey();
|
|
378
|
+
if (!labKey || authConfig?.token === labKey) {
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
const retry = await storeCredentialSecrets(
|
|
382
|
+
dataplaneUrl,
|
|
383
|
+
apiKeyAuthConfig(labKey),
|
|
384
|
+
items
|
|
385
|
+
);
|
|
386
|
+
if (isStoreResponseFailed(retry)) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
return { pushed: storedCountFromResponse(retry, items.length) };
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* @param {string} dataplaneUrl
|
|
394
|
+
* @param {Object} authConfig
|
|
395
|
+
* @param {Array<{ key: string, value: string }>} items
|
|
396
|
+
* @param {Object} res
|
|
397
|
+
* @returns {Promise<{ pushed: number, warning?: string }>}
|
|
398
|
+
*/
|
|
399
|
+
async function handleFailedStoreResponse(dataplaneUrl, authConfig, items, res) {
|
|
400
|
+
const status = storeResponseStatus(res);
|
|
401
|
+
if (status === 403 || status === 401) {
|
|
402
|
+
const retryResult = await tryLabApiKeyRetry(dataplaneUrl, authConfig, items);
|
|
403
|
+
if (retryResult) {
|
|
404
|
+
return retryResult;
|
|
405
|
+
}
|
|
406
|
+
return { pushed: 0, warning: PERMISSION_DENIED_WARNING };
|
|
407
|
+
}
|
|
408
|
+
return storeErrorWarning(res);
|
|
409
|
+
}
|
|
410
|
+
|
|
283
411
|
async function sendCredentialSecrets(dataplaneUrl, authConfig, items) {
|
|
284
412
|
try {
|
|
285
413
|
const res = await storeCredentialSecrets(dataplaneUrl, authConfig, items);
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const status = res.status ?? res.statusCode;
|
|
289
|
-
if (status === 403 || status === 401) {
|
|
290
|
-
return { pushed: 0, warning: 'Could not push credential secrets (permission denied or unauthenticated). Ensure dataplane role has credential:create if you use KV_* in .env.' };
|
|
291
|
-
}
|
|
292
|
-
const errMsg = res.formattedError || res.data?.formattedError || res.error || res.data?.error || 'Failed to push credential secrets to dataplane.';
|
|
293
|
-
return { pushed: 0, warning: errMsg };
|
|
414
|
+
if (isStoreResponseFailed(res)) {
|
|
415
|
+
return handleFailedStoreResponse(dataplaneUrl, authConfig, items, res);
|
|
294
416
|
}
|
|
295
417
|
return { pushed: storedCountFromResponse(res, items.length) };
|
|
296
418
|
} catch (err) {
|
|
297
|
-
return {
|
|
419
|
+
return {
|
|
420
|
+
pushed: 0,
|
|
421
|
+
warning: err.message || 'Failed to push credential secrets to dataplane.'
|
|
422
|
+
};
|
|
298
423
|
}
|
|
299
424
|
}
|
|
300
425
|
|
|
@@ -322,6 +447,7 @@ async function pushCredentialSecrets(dataplaneUrl, authConfig, options = {}) {
|
|
|
322
447
|
const itemsByKey = new Map();
|
|
323
448
|
buildItemsFromEnv(envFilePath, secrets, itemsByKey, appName);
|
|
324
449
|
buildItemsFromPayload(payload, secrets, itemsByKey);
|
|
450
|
+
reconcileBearerTokenSecretPaths(itemsByKey, appName, authConfig);
|
|
325
451
|
|
|
326
452
|
const items = Array.from(itemsByKey.entries())
|
|
327
453
|
.filter(([k]) => isValidKvPath(k))
|
|
@@ -362,6 +488,8 @@ module.exports = {
|
|
|
362
488
|
collectKvEnvVarsAsSecretItems,
|
|
363
489
|
collectKvRefsFromPayload,
|
|
364
490
|
pushCredentialSecrets,
|
|
491
|
+
reconcileBearerTokenSecretPaths,
|
|
492
|
+
resolveLabDataplaneApiKey,
|
|
365
493
|
kvEnvKeyToPath,
|
|
366
494
|
getKvPathSegmentForSecurityKey,
|
|
367
495
|
systemKeyToKvPrefix,
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builder CLI ↔ dataplane version gate (plan 142.0).
|
|
3
|
+
*
|
|
4
|
+
* Resolves the dataplane-advertised `minBuilderCliVersion` (from cache or a
|
|
5
|
+
* fresh health probe) and compares it against the installed Builder CLI
|
|
6
|
+
* version. Used at the top of dataplane API entrypoints so an outdated CLI
|
|
7
|
+
* cannot mutate dataplane state silently.
|
|
8
|
+
*
|
|
9
|
+
* Local-only commands (validate, run, up-infra, file generation) do **not**
|
|
10
|
+
* call into this module; the gate is intentionally per-call, not global.
|
|
11
|
+
*
|
|
12
|
+
* @fileoverview Compatibility gate for dataplane API entrypoints
|
|
13
|
+
* @author AI Fabrix Team
|
|
14
|
+
* @version 2.0.0
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const { fetchDataplaneGeneralHealth } = require('../api/dataplane-health.api');
|
|
20
|
+
const {
|
|
21
|
+
getDeviceDataplaneVersions,
|
|
22
|
+
updateDeviceDataplaneVersions,
|
|
23
|
+
isCacheStale
|
|
24
|
+
} = require('../core/config-device-dataplane');
|
|
25
|
+
const { compareSemver, isValidSemver } = require('./semver-compare');
|
|
26
|
+
const { formatCliVersionGateError } = require('./dataplane-cli-version-help');
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get the installed Builder CLI version from `package.json`.
|
|
30
|
+
* Centralized so tests can stub it via Jest module mocks.
|
|
31
|
+
* @returns {string}
|
|
32
|
+
*/
|
|
33
|
+
function getInstalledCliVersion() {
|
|
34
|
+
return require('../../package.json').version;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Internal sentinel error code so callers can distinguish gate failures from
|
|
39
|
+
* generic dataplane errors when needed.
|
|
40
|
+
* @type {string}
|
|
41
|
+
*/
|
|
42
|
+
const ERROR_CODE = 'CLI_VERSION_INCOMPATIBLE';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* True when `err` was thrown by this gate.
|
|
46
|
+
* @param {*} err
|
|
47
|
+
* @returns {boolean}
|
|
48
|
+
*/
|
|
49
|
+
function isCliVersionIncompatibleError(err) {
|
|
50
|
+
return Boolean(err && err.code === ERROR_CODE);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the dataplane minimum Builder CLI version, preferring cached value
|
|
55
|
+
* when fresh; otherwise probe health and persist a new snapshot under
|
|
56
|
+
* `device.<controllerUrl>`.
|
|
57
|
+
*
|
|
58
|
+
* @private
|
|
59
|
+
* @async
|
|
60
|
+
* @param {string} controllerUrl
|
|
61
|
+
* @param {string} dataplaneUrl
|
|
62
|
+
* @param {{ forceRefresh?: boolean }} [options]
|
|
63
|
+
* @returns {Promise<{ minBuilderCliVersion?: string, dataplaneVersion?: string }>}
|
|
64
|
+
*/
|
|
65
|
+
async function resolveMinBuilderCliVersion(controllerUrl, dataplaneUrl, options = {}) {
|
|
66
|
+
const forceRefresh = Boolean(options.forceRefresh);
|
|
67
|
+
if (!forceRefresh) {
|
|
68
|
+
const cached = await safeReadCache(controllerUrl);
|
|
69
|
+
if (cached && !isCacheStale(cached)) {
|
|
70
|
+
return {
|
|
71
|
+
minBuilderCliVersion: cached.minBuilderCliVersion,
|
|
72
|
+
dataplaneVersion: cached.version
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const snapshot = await safeFetch(dataplaneUrl);
|
|
78
|
+
if (!snapshot) {
|
|
79
|
+
// Cannot reach dataplane → do not gate (offline / pre-rollout). Reuse
|
|
80
|
+
// any cached value we may have.
|
|
81
|
+
const cached = await safeReadCache(controllerUrl);
|
|
82
|
+
return {
|
|
83
|
+
minBuilderCliVersion: cached ? cached.minBuilderCliVersion : undefined,
|
|
84
|
+
dataplaneVersion: cached ? cached.version : undefined
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
await safeWriteCache(controllerUrl, {
|
|
89
|
+
version: snapshot.version,
|
|
90
|
+
minBuilderCliVersion: snapshot.minBuilderCliVersion || null
|
|
91
|
+
});
|
|
92
|
+
return {
|
|
93
|
+
minBuilderCliVersion: snapshot.minBuilderCliVersion,
|
|
94
|
+
dataplaneVersion: snapshot.version
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Read cached versions without throwing — gate failures should not mask real
|
|
100
|
+
* dataplane errors caused by config corruption.
|
|
101
|
+
* @private
|
|
102
|
+
* @async
|
|
103
|
+
* @param {string} controllerUrl
|
|
104
|
+
* @returns {Promise<Object|null>}
|
|
105
|
+
*/
|
|
106
|
+
async function safeReadCache(controllerUrl) {
|
|
107
|
+
if (!controllerUrl) return null;
|
|
108
|
+
try {
|
|
109
|
+
return await getDeviceDataplaneVersions(controllerUrl);
|
|
110
|
+
} catch (_error) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Fetch health swallowing transport errors so callers can decide what to do
|
|
117
|
+
* when the dataplane is unreachable.
|
|
118
|
+
* @private
|
|
119
|
+
* @async
|
|
120
|
+
* @param {string} dataplaneUrl
|
|
121
|
+
* @returns {Promise<Object|null>}
|
|
122
|
+
*/
|
|
123
|
+
async function safeFetch(dataplaneUrl) {
|
|
124
|
+
try {
|
|
125
|
+
return await fetchDataplaneGeneralHealth(dataplaneUrl);
|
|
126
|
+
} catch (_error) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Write the cache without throwing — best-effort persistence.
|
|
133
|
+
* @private
|
|
134
|
+
* @async
|
|
135
|
+
* @param {string} controllerUrl
|
|
136
|
+
* @param {Object} versions
|
|
137
|
+
* @returns {Promise<void>}
|
|
138
|
+
*/
|
|
139
|
+
async function safeWriteCache(controllerUrl, versions) {
|
|
140
|
+
if (!controllerUrl) return;
|
|
141
|
+
try {
|
|
142
|
+
await updateDeviceDataplaneVersions(controllerUrl, versions);
|
|
143
|
+
} catch (_error) {
|
|
144
|
+
// intentionally swallowed — cache write failure must not block the call
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Throw when the installed CLI is older than the dataplane minimum.
|
|
150
|
+
*
|
|
151
|
+
* No-op when:
|
|
152
|
+
* - `dataplaneUrl` is empty (no dataplane in play)
|
|
153
|
+
* - Dataplane omits `minBuilderCliVersion` (no enforcement)
|
|
154
|
+
* - Dataplane is unreachable and no cached minimum is known
|
|
155
|
+
* - Cached/advertised minimum is not valid semver (warn-only contract per plan)
|
|
156
|
+
*
|
|
157
|
+
* @async
|
|
158
|
+
* @param {string} dataplaneUrl - Dataplane base URL (e.g. `http://localhost:3201`)
|
|
159
|
+
* @param {Object} [options]
|
|
160
|
+
* @param {string} [options.controllerUrl] - Controller URL for cache lookup/write
|
|
161
|
+
* @param {boolean} [options.forceRefresh] - Skip cache and probe health now
|
|
162
|
+
* @returns {Promise<void>}
|
|
163
|
+
* @throws {Error & { code: 'CLI_VERSION_INCOMPATIBLE', required: string, installed: string, formatted: string }}
|
|
164
|
+
*/
|
|
165
|
+
async function assertDataplaneCliVersionCompatible(dataplaneUrl, options = {}) {
|
|
166
|
+
if (!dataplaneUrl || typeof dataplaneUrl !== 'string') {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const { minBuilderCliVersion } = await resolveMinBuilderCliVersion(
|
|
170
|
+
options.controllerUrl || null,
|
|
171
|
+
dataplaneUrl,
|
|
172
|
+
{ forceRefresh: Boolean(options.forceRefresh) }
|
|
173
|
+
);
|
|
174
|
+
if (!minBuilderCliVersion) return;
|
|
175
|
+
if (!isValidSemver(minBuilderCliVersion)) return;
|
|
176
|
+
|
|
177
|
+
// Go through module.exports so tests can stub `getInstalledCliVersion`.
|
|
178
|
+
const installed = module.exports.getInstalledCliVersion();
|
|
179
|
+
if (!isValidSemver(installed)) return;
|
|
180
|
+
|
|
181
|
+
if (compareSemver(installed, minBuilderCliVersion) >= 0) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const formatted = formatCliVersionGateError(minBuilderCliVersion, installed);
|
|
186
|
+
const error = new Error(
|
|
187
|
+
`Builder CLI ${installed} is below the dataplane minimum (${minBuilderCliVersion}). ` +
|
|
188
|
+
'Upgrade with: npm install -g @aifabrix/builder@latest'
|
|
189
|
+
);
|
|
190
|
+
error.code = ERROR_CODE;
|
|
191
|
+
error.required = minBuilderCliVersion;
|
|
192
|
+
error.installed = installed;
|
|
193
|
+
error.formatted = formatted;
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = {
|
|
198
|
+
assertDataplaneCliVersionCompatible,
|
|
199
|
+
isCliVersionIncompatibleError,
|
|
200
|
+
getInstalledCliVersion,
|
|
201
|
+
ERROR_CODE
|
|
202
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared "Next actions" + blocking error formatter for the Builder CLI ↔
|
|
3
|
+
* dataplane version gate (plan 142.0). Used by `auth status` display and by
|
|
4
|
+
* the gate thrown from dataplane API entrypoints so users see the same bullets
|
|
5
|
+
* wherever the incompatibility surfaces.
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Shared CLI version-gate UX helpers (next actions + blocking)
|
|
8
|
+
* @author AI Fabrix Team
|
|
9
|
+
* @version 2.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
formatBlockingError,
|
|
16
|
+
formatNextActions
|
|
17
|
+
} = require('./cli-test-layout-chalk');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Build the canonical Next actions bullets for a CLI ↔ dataplane mismatch.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} required - Minimum Builder CLI semver advertised by dataplane
|
|
23
|
+
* @param {string} installed - Current Builder CLI semver (from package.json)
|
|
24
|
+
* @returns {string[]} Bullet lines (no prefix)
|
|
25
|
+
*/
|
|
26
|
+
function buildCliVersionUpgradeNextActions(required, installed) {
|
|
27
|
+
const req = (required || '').trim() || 'the dataplane minimum';
|
|
28
|
+
const inst = (installed || '').trim() || 'this CLI';
|
|
29
|
+
return [
|
|
30
|
+
`Upgrade Builder CLI: npm install -g @aifabrix/builder@${req} (or @latest)`,
|
|
31
|
+
'Confirm after upgrade: aifabrix auth status',
|
|
32
|
+
'CI / scripts: aifabrix auth status --validate (exit 3 until upgraded)',
|
|
33
|
+
`Local work (validate, run, up-infra) still works on ${inst}`
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Build the blocking line shown before the Next actions block when the CLI is
|
|
39
|
+
* older than the dataplane minimum.
|
|
40
|
+
*
|
|
41
|
+
* @param {string} required - Required minimum semver
|
|
42
|
+
* @param {string} installed - Currently installed semver
|
|
43
|
+
* @returns {string}
|
|
44
|
+
*/
|
|
45
|
+
function buildCliVersionBlockingLine(required, installed) {
|
|
46
|
+
const req = (required || '').trim() || '(unknown)';
|
|
47
|
+
const inst = (installed || '').trim() || '(unknown)';
|
|
48
|
+
return formatBlockingError(
|
|
49
|
+
`Builder CLI ${inst} is below the dataplane minimum (${req}). Upgrade before running dataplane commands.`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Format the full version-gate error block (blocking line + Next actions).
|
|
55
|
+
* Suitable for `handleCommandError` output and the `auth status` incompatible
|
|
56
|
+
* branch.
|
|
57
|
+
*
|
|
58
|
+
* @param {string} required - Required minimum semver
|
|
59
|
+
* @param {string} installed - Currently installed semver
|
|
60
|
+
* @returns {string} Multi-line, chalk-colored block
|
|
61
|
+
*/
|
|
62
|
+
function formatCliVersionGateError(required, installed) {
|
|
63
|
+
return [
|
|
64
|
+
buildCliVersionBlockingLine(required, installed),
|
|
65
|
+
'',
|
|
66
|
+
formatNextActions(buildCliVersionUpgradeNextActions(required, installed))
|
|
67
|
+
].join('\n');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = {
|
|
71
|
+
buildCliVersionUpgradeNextActions,
|
|
72
|
+
buildCliVersionBlockingLine,
|
|
73
|
+
formatCliVersionGateError
|
|
74
|
+
};
|
|
@@ -8,7 +8,127 @@
|
|
|
8
8
|
* @version 2.0.0
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
const config = require('../core/config');
|
|
11
15
|
const { discoverDataplaneUrl } = require('../commands/wizard-dataplane');
|
|
16
|
+
const pathsUtil = require('./paths');
|
|
17
|
+
const { loadConfigFile } = require('./config-format');
|
|
18
|
+
|
|
19
|
+
/** Matches `templates/applications/dataplane/application.yaml` and `calculateComposePort`. */
|
|
20
|
+
const DEFAULT_DATAPLANE_MANIFEST_PORT = 3001;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} url
|
|
24
|
+
* @returns {number|null}
|
|
25
|
+
*/
|
|
26
|
+
function localhostPortFromUrl(url) {
|
|
27
|
+
if (!url || typeof url !== 'string') return null;
|
|
28
|
+
const normalized = url.replace(/127\.0\.0\.1/g, 'localhost');
|
|
29
|
+
const match = normalized.match(/localhost:(\d+)/);
|
|
30
|
+
if (!match) return null;
|
|
31
|
+
const port = parseInt(match[1], 10);
|
|
32
|
+
return Number.isFinite(port) ? port : null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Read dataplane `port` from materialized builder config or bundled template.
|
|
37
|
+
*
|
|
38
|
+
* @returns {number}
|
|
39
|
+
*/
|
|
40
|
+
function loadDataplaneManifestListenPort() {
|
|
41
|
+
const tryLoad = (configPath) => {
|
|
42
|
+
if (!fs.existsSync(configPath)) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
const variables = loadConfigFile(configPath);
|
|
46
|
+
const port = variables && variables.port !== undefined ? Number(variables.port) : NaN;
|
|
47
|
+
return Number.isFinite(port) && port > 0 ? port : null;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const builderPath = pathsUtil.getBuilderPath('dataplane');
|
|
52
|
+
if (fs.existsSync(builderPath)) {
|
|
53
|
+
try {
|
|
54
|
+
const fromBuilder = tryLoad(pathsUtil.resolveApplicationConfigPath(builderPath));
|
|
55
|
+
if (fromBuilder) {
|
|
56
|
+
return fromBuilder;
|
|
57
|
+
}
|
|
58
|
+
} catch {
|
|
59
|
+
// Fall through to bundled template when builder dir exists but config is missing.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
// ignore
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const templatePath = path.join(
|
|
67
|
+
pathsUtil.getProjectRoot(),
|
|
68
|
+
'templates',
|
|
69
|
+
'applications',
|
|
70
|
+
'dataplane',
|
|
71
|
+
'application.yaml'
|
|
72
|
+
);
|
|
73
|
+
const fromTemplate = tryLoad(templatePath);
|
|
74
|
+
return fromTemplate || DEFAULT_DATAPLANE_MANIFEST_PORT;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Local Docker host port: `application.yaml` `port` (default 3001) + developerId×100.
|
|
79
|
+
* Same rule as {@link calculateComposePort} / `aifabrix up-platform` — not
|
|
80
|
+
* `APP_SERVICE_ENV_DEFAULTS_LOCAL.DATAPLANE_PUBLIC_PORT` (3011), which is for other local profiles.
|
|
81
|
+
*
|
|
82
|
+
* @returns {Promise<string>}
|
|
83
|
+
*/
|
|
84
|
+
async function expectedLocalDataplaneBaseUrl() {
|
|
85
|
+
const developerIdRaw = await config.getDeveloperId();
|
|
86
|
+
const idNum =
|
|
87
|
+
typeof developerIdRaw === 'string'
|
|
88
|
+
? parseInt(developerIdRaw, 10)
|
|
89
|
+
: Number(developerIdRaw);
|
|
90
|
+
const devId = Number.isFinite(idNum) && idNum >= 0 ? idNum : 0;
|
|
91
|
+
const basePort = loadDataplaneManifestListenPort();
|
|
92
|
+
const hostPort = devId === 0 ? basePort : basePort + devId * 100;
|
|
93
|
+
return `http://localhost:${hostPort}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Controller application.url can lag after dev-id changes; prefer builder host port formula for localhost.
|
|
98
|
+
* @param {string} discoveredUrl
|
|
99
|
+
* @returns {Promise<string>}
|
|
100
|
+
*/
|
|
101
|
+
async function alignDiscoveredDataplaneUrl(discoveredUrl) {
|
|
102
|
+
if (!discoveredUrl || !/localhost|127\.0\.0\.1/i.test(discoveredUrl)) {
|
|
103
|
+
return discoveredUrl;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const expectedUrl = await expectedLocalDataplaneBaseUrl();
|
|
107
|
+
const discoveredPort = localhostPortFromUrl(discoveredUrl);
|
|
108
|
+
const expectedPort = localhostPortFromUrl(expectedUrl);
|
|
109
|
+
if (
|
|
110
|
+
discoveredPort !== null &&
|
|
111
|
+
expectedPort !== null &&
|
|
112
|
+
discoveredPort !== expectedPort
|
|
113
|
+
) {
|
|
114
|
+
return expectedUrl;
|
|
115
|
+
}
|
|
116
|
+
} catch {
|
|
117
|
+
// Keep controller-discovered URL when developer config is unavailable.
|
|
118
|
+
}
|
|
119
|
+
return discoveredUrl;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @returns {string|null}
|
|
124
|
+
*/
|
|
125
|
+
function dataplaneUrlFromProcessEnv() {
|
|
126
|
+
const raw = process.env.DP || process.env.DATAPLANE_URL;
|
|
127
|
+
if (!raw || typeof raw !== 'string') {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
return raw.trim().replace(/\/$/, '');
|
|
131
|
+
}
|
|
12
132
|
|
|
13
133
|
/**
|
|
14
134
|
* Resolve dataplane URL by discovering from controller
|
|
@@ -22,9 +142,16 @@ const { discoverDataplaneUrl } = require('../commands/wizard-dataplane');
|
|
|
22
142
|
* @throws {Error} If dataplane URL cannot be resolved
|
|
23
143
|
*/
|
|
24
144
|
async function resolveDataplaneUrl(controllerUrl, environment, authConfig, opts = {}) {
|
|
25
|
-
|
|
145
|
+
const envUrl = dataplaneUrlFromProcessEnv();
|
|
146
|
+
if (envUrl) {
|
|
147
|
+
return envUrl;
|
|
148
|
+
}
|
|
149
|
+
const discovered = await discoverDataplaneUrl(controllerUrl, environment, authConfig, opts);
|
|
150
|
+
return alignDiscoveredDataplaneUrl(discovered);
|
|
26
151
|
}
|
|
27
152
|
|
|
28
153
|
module.exports = {
|
|
29
|
-
resolveDataplaneUrl
|
|
154
|
+
resolveDataplaneUrl,
|
|
155
|
+
expectedLocalDataplaneBaseUrl,
|
|
156
|
+
alignDiscoveredDataplaneUrl
|
|
30
157
|
};
|