@aifabrix/builder 2.45.5 → 2.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/cli-layout.mdc +2 -0
- package/.cursor/rules/cli-product-neutral.mdc +43 -0
- package/.cursor/rules/project-rules.mdc +1 -0
- package/.npmrc.token +1 -0
- package/.temp/jest-protection-convert/run-IFN2nv/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-IvmKLO/.protection/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-convert/run-SdvblF/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-b51TsW/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-fiLmb1/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-qYGHMU/.protection/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-Mq5re8/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-f3u4YL/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-iQtd1O/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-mdmz5M/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-qbKYxQ/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-yzlJy6/hubspot-companies.yaml +17 -0
- package/.temp/validation/summary.txt +5 -0
- package/README.md +43 -17
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
- package/jest.config.default.js +2 -1
- package/jest.config.isolated.js +2 -1
- package/jest.global-live-fabrix-hooks.js +36 -0
- package/jest.isolated-projects.js +146 -0
- package/jest.projects.js +65 -136
- package/lib/api/api-response-errors.js +124 -0
- package/lib/api/dataplane-health.api.js +19 -7
- package/lib/api/governance-scenario-pack.api.js +159 -0
- package/lib/api/index.js +43 -3
- package/lib/api/lifecycle.api.js +98 -0
- package/lib/api/pipeline.api.js +3 -1
- package/lib/api/records-bulk.api.js +14 -1
- package/lib/api/resource-types.api.js +107 -0
- package/lib/api/runtime-configuration.api.js +72 -0
- package/lib/api/types/resource-types.types.js +45 -0
- package/lib/api/user-groups.api.js +9 -9
- package/lib/api/users.api.js +8 -8
- package/lib/api/validation-runner.js +3 -1
- package/lib/api/wizard.api.js +10 -2
- package/lib/app/deploy-config.js +4 -1
- package/lib/app/down.js +78 -27
- package/lib/app/index.js +2 -2
- package/lib/app/push.js +4 -5
- package/lib/app/register.js +7 -4
- package/lib/app/run-env-compose.js +84 -30
- package/lib/app/run-resolve-image.js +2 -1
- package/lib/app/run-stop-containers.js +113 -0
- package/lib/app/run.js +1 -31
- package/lib/app/show-display.js +35 -0
- package/lib/app/show.js +32 -1
- package/lib/build/index.js +22 -5
- package/lib/build/miso-controller-npm-sdk-guard.js +25 -0
- package/lib/certification/sync-system-certification.js +13 -6
- package/lib/channels/add-channel.js +348 -0
- package/lib/channels/channel-artifact-generator.js +141 -0
- package/lib/channels/channel-audit.js +101 -0
- package/lib/channels/channel-auth-context.js +37 -0
- package/lib/channels/channel-credential.js +200 -0
- package/lib/channels/channel-display-name.js +64 -0
- package/lib/channels/channel-external-system.js +106 -0
- package/lib/channels/channel-integration-generator.js +274 -0
- package/lib/channels/channel-probe.js +89 -0
- package/lib/channels/channel-runtime-resolver.js +152 -0
- package/lib/channels/channel-status-display.js +141 -0
- package/lib/channels/channel-targets.js +115 -0
- package/lib/cli/index.js +20 -4
- package/lib/cli/infra-guided-auth.js +109 -0
- package/lib/cli/infra-guided.js +84 -81
- package/lib/cli/setup-app.help.js +0 -31
- package/lib/cli/setup-app.js +82 -5
- package/lib/cli/setup-app.test-commands.js +5 -57
- package/lib/cli/setup-auth.js +25 -0
- package/lib/cli/setup-certification-commands.js +147 -0
- package/lib/cli/setup-certification.help.js +141 -0
- package/lib/cli/setup-environment.js +19 -2
- package/lib/cli/setup-external-system-help.js +62 -0
- package/lib/cli/setup-external-system.js +25 -3
- package/lib/cli/setup-infra-up-dataplane-action.js +2 -2
- package/lib/cli/setup-infra-up-platform-action.js +2 -1
- package/lib/cli/setup-infra.js +9 -1
- package/lib/cli/setup-platform.js +6 -1
- package/lib/cli/setup-secrets.js +33 -1
- package/lib/cli/setup-utility-help-after.js +25 -2
- package/lib/cli/setup-utility-repair.js +198 -0
- package/lib/cli/setup-utility-resolve.js +44 -5
- package/lib/cli/setup-utility-validate.js +197 -0
- package/lib/cli/setup-utility.js +4 -239
- package/lib/commands/app-logs.js +54 -26
- package/lib/commands/app.js +41 -0
- package/lib/commands/auth-status-client-tokens.js +214 -0
- package/lib/commands/auth-status-dataplane-version.js +142 -68
- package/lib/commands/auth-status.js +43 -54
- package/lib/commands/channel.js +58 -0
- package/lib/commands/datasource-test-trust-cli.js +27 -23
- package/lib/commands/datasource-unified-test-cli.js +6 -3
- package/lib/commands/datasource-unified-test-cli.options.js +5 -1
- package/lib/commands/datasource.js +8 -8
- package/lib/commands/dev-show-display.js +8 -1
- package/lib/commands/governance-cli-shared.js +94 -0
- package/lib/commands/governance-command-action.js +132 -0
- package/lib/commands/governance-download-external.js +34 -0
- package/lib/commands/governance-generate-external.js +71 -0
- package/lib/commands/governance-upload-external.js +36 -0
- package/lib/commands/governance-verify-external.js +64 -0
- package/lib/commands/governance.js +144 -0
- package/lib/commands/identity-apply-sync.js +49 -8
- package/lib/commands/identity-group-cli.js +66 -26
- package/lib/commands/identity-membership-role-cache.js +58 -37
- package/lib/commands/identity-shared.js +22 -1
- package/lib/commands/identity-user-cli.js +80 -34
- package/lib/commands/lifecycle-command-action.js +123 -0
- package/lib/commands/lifecycle-external.js +268 -0
- package/lib/commands/login.js +11 -2
- package/lib/commands/repair-auth-apply.js +153 -0
- package/lib/commands/repair-auth-config.js +8 -13
- package/lib/commands/repair-changed-files.js +41 -0
- package/lib/commands/repair-datasource-com-v2.js +15 -0
- package/lib/commands/repair-datasource-keys.js +4 -1
- package/lib/commands/repair-datasource-run.js +59 -0
- package/lib/commands/repair-datasource-service-contract.js +206 -0
- package/lib/commands/repair-datasource-test-payload.js +259 -0
- package/lib/commands/repair-datasource.js +49 -64
- package/lib/commands/repair-env-template.js +76 -25
- package/lib/commands/repair-external-run.js +216 -0
- package/lib/commands/repair-openapi-sync.js +49 -4
- package/lib/commands/repair-persist.js +4 -0
- package/lib/commands/repair-rbac.js +52 -1
- package/lib/commands/repair-steps.js +160 -0
- package/lib/commands/repair-system-alignment.js +7 -4
- package/lib/commands/repair.js +29 -296
- package/lib/commands/resource-type.js +371 -0
- package/lib/commands/secrets-get.js +184 -0
- package/lib/commands/secrets-remove-all.js +3 -1
- package/lib/commands/setup-image-refresh.js +146 -0
- package/lib/commands/setup-modes.js +158 -162
- package/lib/commands/setup-platform-auth.js +76 -0
- package/lib/commands/setup-prompts-admin.js +244 -0
- package/lib/commands/setup-prompts-platform-mode.js +70 -0
- package/lib/commands/setup-prompts-validators.js +36 -0
- package/lib/commands/setup-prompts.js +24 -76
- package/lib/commands/setup-run-context.js +45 -0
- package/lib/commands/setup-spinners.js +39 -0
- package/lib/commands/setup.js +27 -5
- package/lib/commands/teardown.js +39 -0
- package/lib/commands/test-e2e-external.js +24 -8
- package/lib/commands/test-governance-external.js +3 -24
- package/lib/commands/test-trust-external.js +48 -100
- package/lib/commands/up-common.js +57 -13
- package/lib/commands/up-dataplane.js +56 -15
- package/lib/commands/upload-publish-context.js +75 -0
- package/lib/commands/upload-sidecar-followups.js +89 -0
- package/lib/commands/upload.js +113 -110
- package/lib/commands/verify-governance-combined-external.js +61 -0
- package/lib/commands/verify-governance-command-action.js +64 -0
- package/lib/commands/verify-governance-db.js +102 -0
- package/lib/commands/verify-governance-external.js +77 -0
- package/lib/commands/verify-governance-result-builders.js +71 -0
- package/lib/commands/verify-operations-command-action.js +68 -0
- package/lib/commands/verify-operations-external.js +268 -0
- package/lib/commands/verify-operations-persist.js +75 -0
- package/lib/commands/verify-operations-step-plan.js +68 -0
- package/lib/commands/verify-operations-steps.js +111 -0
- package/lib/commands/verify-trust-command-action.js +73 -0
- package/lib/commands/verify-trust-external.js +59 -0
- package/lib/commands/wizard-config-normalizer.js +43 -23
- package/lib/commands/wizard-core.js +2 -0
- package/lib/core/admin-password-generator.js +38 -0
- package/lib/core/admin-secrets-bundle.js +111 -0
- package/lib/core/admin-secrets-env-overlay.js +62 -0
- package/lib/core/admin-secrets.js +32 -4
- package/lib/core/config-attach-extensions.js +3 -0
- package/lib/core/config-platform-controller.js +44 -0
- package/lib/core/config-setup-installation-profile.js +42 -0
- package/lib/core/config-setup-platform-mode.js +46 -0
- package/lib/core/config.js +46 -36
- package/lib/core/secrets-admin-env.js +6 -2
- package/lib/core/secrets-ensure.js +4 -0
- package/lib/core/secrets-env-content.js +54 -48
- package/lib/core/secrets-env-write.js +10 -1
- package/lib/core/secrets-infra-placeholder-sync.js +2 -0
- package/lib/core/setup-installation-profile.js +43 -0
- package/lib/core/setup-platform-mode.js +90 -0
- package/lib/datasource/agent-trust-debug-log.js +1 -1
- package/lib/datasource/agent-trust-run.js +5 -3
- package/lib/datasource/audit-evidence-extract-debug.js +30 -18
- package/lib/datasource/audit-evidence-extract.js +7 -4
- package/lib/datasource/audit-evidence-verify-matrix.js +54 -26
- package/lib/datasource/bulk-loader-run-batches.js +15 -3
- package/lib/datasource/capability/capability-diff-slice.js +2 -2
- package/lib/datasource/capability/capability-manifest.js +254 -0
- package/lib/datasource/capability/capability-resolve.js +3 -4
- package/lib/datasource/capability/copy-operations.js +5 -3
- package/lib/datasource/capability/remove-operations.js +2 -1
- package/lib/datasource/capability/run-capability-copy.js +32 -4
- package/lib/datasource/capability/run-capability-diff.js +3 -3
- package/lib/datasource/capability/run-capability-dimension.js +2 -4
- package/lib/datasource/capability/run-capability-edit.js +4 -7
- package/lib/datasource/capability/run-capability-relate.js +2 -4
- package/lib/datasource/capability/run-capability-remove.js +2 -4
- package/lib/datasource/capability/validate-capability-slice.js +2 -1
- package/lib/datasource/datasource-identifier.js +2 -1
- package/lib/datasource/datasource-load-export-context.js +3 -0
- package/lib/datasource/datasource-validate-display.js +1 -1
- package/lib/datasource/field-reference-validator.js +24 -3
- package/lib/datasource/log-cleaner.js +2 -2
- package/lib/datasource/log-viewer.js +8 -8
- package/lib/datasource/orphan-profiles-validator.js +98 -0
- package/lib/datasource/test-e2e.js +4 -1
- package/lib/datasource/unified-validation-run-body.js +3 -1
- package/lib/datasource/unified-validation-run.js +5 -2
- package/lib/datasource/validate.js +41 -23
- package/lib/deployment/environment-poll-status.js +138 -0
- package/lib/deployment/environment.js +42 -34
- package/lib/external-system/download-helpers.js +87 -0
- package/lib/external-system/download.js +78 -138
- package/lib/external-system/generator.js +1 -55
- package/lib/external-system/integration-auth-context.js +45 -0
- package/lib/external-system/test-auth.js +12 -5
- package/lib/external-system/test.js +16 -21
- package/lib/generator/builders.js +11 -1
- package/lib/generator/external-controller-manifest.js +18 -0
- package/lib/generator/split.js +3 -1
- package/lib/generator/wizard.js +1 -28
- package/lib/governance/governance-cli-errors.js +96 -0
- package/lib/governance/governance-download-display.js +38 -0
- package/lib/governance/governance-run-display.js +215 -25
- package/lib/identity/fk-reference.js +65 -0
- package/lib/identity/identity-apply-core.js +13 -58
- package/lib/identity/identity-apply-phases.js +5 -1
- package/lib/identity/identity-display.js +243 -0
- package/lib/identity/identity-response-helpers.js +52 -0
- package/lib/identity/identity-sync-errors.js +62 -0
- package/lib/identity/resolve-user-id.js +129 -0
- package/lib/identity/username-default.js +31 -0
- package/lib/infrastructure/admin-secrets-apply.js +84 -0
- package/lib/infrastructure/admin-secrets-ensure.js +130 -0
- package/lib/infrastructure/admin-secrets-resolve.js +95 -0
- package/lib/infrastructure/admin-secrets-sync.js +91 -0
- package/lib/infrastructure/helpers.js +23 -129
- package/lib/infrastructure/index.js +5 -6
- package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
- package/lib/lifecycle/certification-progress.js +231 -0
- package/lib/lifecycle/dataplane-ui-url.js +145 -0
- package/lib/lifecycle/exit-codes.js +24 -0
- package/lib/lifecycle/json-envelope.js +25 -0
- package/lib/lifecycle/lifecycle-governance-options.js +51 -0
- package/lib/lifecycle/logger-quiet.js +29 -0
- package/lib/lifecycle/open-api-published.js +59 -0
- package/lib/lifecycle/operations-readiness-metrics.js +101 -0
- package/lib/lifecycle/product-model.js +196 -0
- package/lib/lifecycle/report-display-trust.js +70 -0
- package/lib/lifecycle/report-display.js +472 -0
- package/lib/lifecycle/scenario-upload.js +191 -0
- package/lib/lifecycle/verify-step-progress.js +87 -0
- package/lib/parameters/infra-kv-discovery.js +27 -2
- package/lib/protection/auth-context.js +10 -4
- package/lib/resolvers/datasource-resolver.js +2 -3
- package/lib/resolvers/resource-type-file.js +159 -0
- package/lib/schema/application-schema.json +17 -1
- package/lib/schema/datasource-test-run.schema.json +89 -6
- package/lib/schema/deployment-rules.yaml +2 -1
- package/lib/schema/external-datasource.schema.json +2947 -2619
- package/lib/schema/external-system.schema.json +1418 -761
- package/lib/schema/infra.parameter.yaml +45 -0
- package/lib/schema/type/credential-field-registry.json +103 -97
- package/lib/schema/type/document-storage.json +9 -5
- package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
- package/lib/utils/api.js +55 -31
- package/lib/utils/app-relative-path-env.js +112 -0
- package/lib/utils/app-run-containers.js +25 -2
- package/lib/utils/authentication-shape.js +224 -0
- package/lib/utils/build-copy.js +36 -5
- package/lib/utils/build-resolve-image.js +66 -12
- package/lib/utils/celery-companion-apps.js +53 -0
- package/lib/utils/cli-secrets-error-format.js +83 -21
- package/lib/utils/cli-utils.js +20 -4
- package/lib/utils/compose-celery-worker.js +23 -0
- package/lib/utils/compose-generate-docker-compose.js +13 -1
- package/lib/utils/compose-traefik-ingress-base.js +16 -1
- package/lib/utils/config-secrets-path.js +46 -0
- package/lib/utils/configuration-env-resolver.js +36 -0
- package/lib/utils/controller-url.js +88 -1
- package/lib/utils/credential-secrets-env-kv.js +216 -0
- package/lib/utils/credential-secrets-env.js +175 -257
- package/lib/utils/dataplane-resolver.js +18 -2
- package/lib/utils/declarative-url-ports.js +27 -4
- package/lib/utils/deployment-auth-mode.js +41 -0
- package/lib/utils/developer-volume-id.js +33 -0
- package/lib/utils/docker-build.js +1 -4
- package/lib/utils/env-copy.js +14 -9
- package/lib/utils/env-kv-orphan-lines.js +43 -0
- package/lib/utils/error-formatters/network-errors.js +13 -4
- package/lib/utils/external-env-template.js +11 -3
- package/lib/utils/external-system-readiness-core.js +10 -0
- package/lib/utils/external-system-readiness-display.js +80 -23
- package/lib/utils/external-system-validators.js +8 -2
- package/lib/utils/health-check.js +4 -1
- package/lib/utils/help-builder.js +13 -8
- package/lib/utils/keycloak-realm-ready.js +101 -0
- package/lib/utils/load-cip-capacity-display-config.js +61 -15
- package/lib/utils/manifest-source-emit.js +2 -0
- package/lib/utils/paths.js +25 -0
- package/lib/utils/pipeline-upload-auth-retry.js +82 -0
- package/lib/utils/platform-controller-url.js +157 -0
- package/lib/utils/platform-kv-ref.js +57 -0
- package/lib/utils/postgres-platform-bootstrap.js +199 -0
- package/lib/utils/postgres-wipe.js +9 -2
- package/lib/utils/resolve-env-mode.js +47 -0
- package/lib/utils/runtime-configuration-download.js +189 -0
- package/lib/utils/runtime-configuration-values.js +119 -0
- package/lib/utils/schema-loader.js +19 -4
- package/lib/utils/secrets-generator.js +31 -25
- package/lib/utils/secrets-missing-error.js +164 -0
- package/lib/utils/service-url-label.js +81 -0
- package/lib/utils/setup-quiet-output.js +25 -0
- package/lib/utils/template-helpers.js +15 -0
- package/lib/utils/token-manager-client-credentials.js +201 -0
- package/lib/utils/token-manager-messages.js +8 -0
- package/lib/utils/token-manager-refresh.js +12 -2
- package/lib/utils/token-manager.js +86 -105
- package/lib/utils/upload-pipeline-ui.js +12 -5
- package/lib/utils/upload-sync-options.js +42 -0
- package/lib/utils/url-declarative-public-base.js +21 -3
- package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
- package/lib/utils/validation-poll-ui.js +8 -0
- package/lib/utils/validation-run-stage.js +45 -0
- package/lib/utils/worker-sidecars.js +142 -0
- package/lib/validation/external-manifest-validator.js +2 -0
- package/lib/validation/runtime-configuration-rules.js +98 -0
- package/lib/validation/validate-display-dimensions.js +154 -0
- package/lib/validation/validate-display-sections.js +276 -0
- package/lib/validation/validate-display-steps.js +67 -0
- package/lib/validation/validate-display.js +24 -402
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +7 -1
- package/templates/applications/dataplane/env.template +50 -14
- package/templates/applications/dataplane/rbac.yaml +108 -1
- package/templates/applications/miso-controller/application.yaml +2 -2
- package/templates/applications/miso-controller/env.template +53 -122
- package/templates/applications/miso-controller/rbac.yaml +28 -13
- package/templates/channels/chatgpt/install.template.md.hbs +49 -0
- package/templates/channels/chatgpt/manifest.template.json.hbs +9 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +49 -0
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +8 -0
- package/templates/channels/slack/install.template.md.hbs +49 -0
- package/templates/channels/slack/manifest.template.json.hbs +66 -0
- package/templates/channels/teams-bot/install.template.md.hbs +49 -0
- package/templates/channels/teams-bot/manifest.template.json.hbs +13 -0
- package/templates/channels/web-embed/install.template.md.hbs +49 -0
- package/templates/channels/web-embed/manifest.template.json.hbs +8 -0
- package/templates/external-system/README.md.hbs +74 -17
- package/templates/python/docker-compose.hbs +75 -18
- package/templates/typescript/docker-compose.hbs +70 -17
- package/lib/commands/test-governance-command-action.js +0 -55
- package/lib/commands/test-trust-command-action.js +0 -118
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client token listing and proactive refresh for auth status.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Stored app tokens, auto-refresh when credentials exist
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const chalk = require('chalk');
|
|
12
|
+
const config = require('../core/config');
|
|
13
|
+
const logger = require('../utils/logger');
|
|
14
|
+
const { successGlyph, failureGlyph } = require('../utils/cli-test-layout-chalk');
|
|
15
|
+
const {
|
|
16
|
+
getOrRefreshClientToken,
|
|
17
|
+
loadClientCredentials,
|
|
18
|
+
refreshClientToken,
|
|
19
|
+
isTokenExpired
|
|
20
|
+
} = require('../utils/token-manager');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} controllerUrl
|
|
24
|
+
* @param {string} storedController
|
|
25
|
+
* @returns {boolean}
|
|
26
|
+
*/
|
|
27
|
+
function controllersMatch(controllerUrl, storedController) {
|
|
28
|
+
if (!storedController) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return config.normalizeControllerUrl(storedController) === config.normalizeControllerUrl(controllerUrl);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {string} environment
|
|
36
|
+
* @param {string} controllerUrl
|
|
37
|
+
* @returns {Promise<Record<string, { controller?: string, token?: string, expiresAt?: string }>>}
|
|
38
|
+
*/
|
|
39
|
+
async function getClientEntriesForController(environment, controllerUrl) {
|
|
40
|
+
const configData = await config.getConfig();
|
|
41
|
+
const clients = configData.environments?.[environment]?.clients || {};
|
|
42
|
+
const out = {};
|
|
43
|
+
for (const [appName, tokenData] of Object.entries(clients)) {
|
|
44
|
+
if (controllersMatch(controllerUrl, tokenData?.controller)) {
|
|
45
|
+
out[appName] = tokenData;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Refresh expired client tokens when secrets.local.yaml (or env) has client id/secret.
|
|
53
|
+
*
|
|
54
|
+
* @async
|
|
55
|
+
* @param {string} environment
|
|
56
|
+
* @param {string} controllerUrl
|
|
57
|
+
* @returns {Promise<number>} Count refreshed
|
|
58
|
+
*/
|
|
59
|
+
async function refreshExpiredClientTokensWherePossible(environment, controllerUrl) {
|
|
60
|
+
const entries = await getClientEntriesForController(environment, controllerUrl);
|
|
61
|
+
let refreshed = 0;
|
|
62
|
+
for (const appName of Object.keys(entries)) {
|
|
63
|
+
const tokenData = entries[appName];
|
|
64
|
+
if (!tokenData?.token || !isTokenExpired(tokenData.expiresAt)) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const credentials = await loadClientCredentials(appName);
|
|
68
|
+
if (!credentials?.clientId || !credentials?.clientSecret) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
await refreshClientToken(environment, appName, controllerUrl);
|
|
73
|
+
refreshed += 1;
|
|
74
|
+
} catch {
|
|
75
|
+
// Skip apps without valid credentials or controller errors
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return refreshed;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {string} expiresAt
|
|
83
|
+
* @returns {'valid'|'expired'|'unknown'}
|
|
84
|
+
*/
|
|
85
|
+
function tokenExpiryState(expiresAt) {
|
|
86
|
+
if (!expiresAt) {
|
|
87
|
+
return 'unknown';
|
|
88
|
+
}
|
|
89
|
+
return isTokenExpired(expiresAt) ? 'expired' : 'valid';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Build rows for stored client tokens table.
|
|
94
|
+
*
|
|
95
|
+
* @async
|
|
96
|
+
* @param {string} environment
|
|
97
|
+
* @param {string} controllerUrl
|
|
98
|
+
* @returns {Promise<Array<{ appName: string, expiresAt: string, state: string, logoutCmd: string }>>}
|
|
99
|
+
*/
|
|
100
|
+
async function listClientTokenRows(environment, controllerUrl) {
|
|
101
|
+
const entries = await getClientEntriesForController(environment, controllerUrl);
|
|
102
|
+
const rows = [];
|
|
103
|
+
for (const appName of Object.keys(entries).sort()) {
|
|
104
|
+
const tokenData = entries[appName];
|
|
105
|
+
const state = tokenExpiryState(tokenData.expiresAt);
|
|
106
|
+
const credentials = await loadClientCredentials(appName);
|
|
107
|
+
const canRefresh = !!(credentials?.clientId && credentials?.clientSecret);
|
|
108
|
+
let stateLabel = state;
|
|
109
|
+
if (state === 'expired' && canRefresh) {
|
|
110
|
+
stateLabel = 'expired (credentials available — refreshed on status if possible)';
|
|
111
|
+
} else if (state === 'expired' && !canRefresh) {
|
|
112
|
+
stateLabel = 'expired (no client id/secret in secrets — logout or login)';
|
|
113
|
+
}
|
|
114
|
+
rows.push({
|
|
115
|
+
appName,
|
|
116
|
+
expiresAt: tokenData.expiresAt || '—',
|
|
117
|
+
state: stateLabel,
|
|
118
|
+
logoutCmd: `aifabrix logout -e ${environment} -a ${appName}`
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return rows;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Print stored application client tokens and logout commands.
|
|
126
|
+
*
|
|
127
|
+
* @async
|
|
128
|
+
* @param {string} environment
|
|
129
|
+
* @param {string} controllerUrl
|
|
130
|
+
* @param {number} [refreshedCount]
|
|
131
|
+
* @returns {Promise<void>}
|
|
132
|
+
*/
|
|
133
|
+
async function displayStoredClientTokensSection(environment, controllerUrl, refreshedCount = 0) {
|
|
134
|
+
const rows = await listClientTokenRows(environment, controllerUrl);
|
|
135
|
+
if (rows.length === 0) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
logger.log('');
|
|
140
|
+
logger.log(chalk.bold('Stored application client tokens:'));
|
|
141
|
+
if (refreshedCount > 0) {
|
|
142
|
+
logger.log(
|
|
143
|
+
chalk.gray(
|
|
144
|
+
` Refreshed ${refreshedCount} expired token(s) using client id/secret from secrets.local.yaml or env.`
|
|
145
|
+
)
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
logger.log(chalk.gray(` Environment: ${environment} · Controller: ${controllerUrl}`));
|
|
149
|
+
logger.log('');
|
|
150
|
+
for (const row of rows) {
|
|
151
|
+
const icon = row.state.startsWith('valid') ? successGlyph() : failureGlyph();
|
|
152
|
+
logger.log(` ${icon} ${chalk.cyan(row.appName)}`);
|
|
153
|
+
logger.log(` Expires: ${chalk.gray(row.expiresAt)}`);
|
|
154
|
+
logger.log(` Status: ${row.state}`);
|
|
155
|
+
logger.log(` Clear: ${chalk.gray(row.logoutCmd)}`);
|
|
156
|
+
}
|
|
157
|
+
logger.log('');
|
|
158
|
+
logger.log(chalk.gray(' Clear all client tokens for this environment:'), chalk.gray(`aifabrix logout -e ${environment}`));
|
|
159
|
+
logger.log(chalk.gray(' Clear device login for this controller:'), chalk.gray(`aifabrix logout -c ${controllerUrl}`));
|
|
160
|
+
logger.log('');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Resolve a working client token (refresh when expired if credentials exist).
|
|
165
|
+
*
|
|
166
|
+
* @async
|
|
167
|
+
* @param {string} controllerUrl
|
|
168
|
+
* @param {string} environment
|
|
169
|
+
* @param {function(string, string, string, string, string): Promise<Object>} validateClientTokenFn
|
|
170
|
+
* @returns {Promise<Object|null>}
|
|
171
|
+
*/
|
|
172
|
+
async function tryResolveClientTokenAuth(controllerUrl, environment, validateClientTokenFn) {
|
|
173
|
+
const entries = await getClientEntriesForController(environment, controllerUrl);
|
|
174
|
+
const appNames = Object.keys(entries);
|
|
175
|
+
if (appNames.length === 0) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const ordered = ['dataplane', ...appNames.filter(a => a !== 'dataplane').sort()];
|
|
180
|
+
let lastFailure = null;
|
|
181
|
+
|
|
182
|
+
for (const appName of ordered) {
|
|
183
|
+
try {
|
|
184
|
+
const refreshed = await getOrRefreshClientToken(environment, appName, controllerUrl);
|
|
185
|
+
if (!refreshed?.token) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const stored = await config.getClientToken(environment, appName);
|
|
189
|
+
const result = await validateClientTokenFn(
|
|
190
|
+
refreshed.token,
|
|
191
|
+
controllerUrl,
|
|
192
|
+
environment,
|
|
193
|
+
appName,
|
|
194
|
+
stored?.expiresAt
|
|
195
|
+
);
|
|
196
|
+
if (result.authenticated) {
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
lastFailure = result;
|
|
200
|
+
} catch {
|
|
201
|
+
// Try next app
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return lastFailure;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
module.exports = {
|
|
209
|
+
refreshExpiredClientTokensWherePossible,
|
|
210
|
+
displayStoredClientTokensSection,
|
|
211
|
+
tryResolveClientTokenAuth,
|
|
212
|
+
listClientTokenRows,
|
|
213
|
+
getClientEntriesForController
|
|
214
|
+
};
|
|
@@ -17,13 +17,36 @@
|
|
|
17
17
|
const { fetchDataplaneGeneralHealth } = require('../api/dataplane-health.api');
|
|
18
18
|
const {
|
|
19
19
|
updateDeviceDataplaneVersions,
|
|
20
|
-
getDeviceDataplaneVersions
|
|
20
|
+
getDeviceDataplaneVersions,
|
|
21
|
+
isCacheStale
|
|
21
22
|
} = require('../core/config-device-dataplane');
|
|
22
23
|
const {
|
|
23
24
|
getInstalledCliVersion
|
|
24
25
|
} = require('../utils/dataplane-cli-version-gate');
|
|
25
26
|
const { isAtLeast, isValidSemver } = require('../utils/semver-compare');
|
|
26
27
|
|
|
28
|
+
/** Max wall time for post-login version cache refresh (silent after success banner). */
|
|
29
|
+
const LOGIN_POST_REFRESH_BUDGET_MS = 8000;
|
|
30
|
+
|
|
31
|
+
/** Per health probe during login refresh (legacy /health first, then v1). */
|
|
32
|
+
const LOGIN_HEALTH_PROBE_TIMEOUT_MS = 3000;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Resolve when `promise` settles or when `timeoutMs` elapses (returns undefined).
|
|
36
|
+
* @template T
|
|
37
|
+
* @param {Promise<T>} promise
|
|
38
|
+
* @param {number} timeoutMs
|
|
39
|
+
* @returns {Promise<T|undefined>}
|
|
40
|
+
*/
|
|
41
|
+
function raceWithTimeout(promise, timeoutMs) {
|
|
42
|
+
return Promise.race([
|
|
43
|
+
promise,
|
|
44
|
+
new Promise((resolve) => {
|
|
45
|
+
setTimeout(() => resolve(undefined), timeoutMs);
|
|
46
|
+
})
|
|
47
|
+
]);
|
|
48
|
+
}
|
|
49
|
+
|
|
27
50
|
/**
|
|
28
51
|
* Per plan 142.0: invalid semver from the dataplane is logged but does **not**
|
|
29
52
|
* block the CLI; missing min version means no enforcement.
|
|
@@ -57,28 +80,35 @@ async function safeReadCache(controllerUrl) {
|
|
|
57
80
|
}
|
|
58
81
|
|
|
59
82
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
|
|
64
|
-
|
|
83
|
+
* Build optional fetch args for {@link fetchDataplaneGeneralHealth}.
|
|
84
|
+
* @param {Object} [options]
|
|
85
|
+
* @returns {Object}
|
|
86
|
+
*/
|
|
87
|
+
function buildHealthFetchOptions(options = {}) {
|
|
88
|
+
const healthOpts = {};
|
|
89
|
+
if (options.healthTimeoutMs !== undefined && options.healthTimeoutMs !== null) {
|
|
90
|
+
healthOpts.timeoutMs = options.healthTimeoutMs;
|
|
91
|
+
}
|
|
92
|
+
if (options.endpointOrder) {
|
|
93
|
+
healthOpts.endpointOrder = options.endpointOrder;
|
|
94
|
+
}
|
|
95
|
+
return healthOpts;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Probe dataplane health and persist version fields when available.
|
|
65
100
|
* @async
|
|
66
|
-
* @param {string} controllerUrl
|
|
67
|
-
* @param {string} dataplaneUrl
|
|
68
|
-
* @
|
|
69
|
-
*
|
|
70
|
-
* minBuilderCliVersion: string|undefined,
|
|
71
|
-
* cliVersion: string,
|
|
72
|
-
* compatible: boolean
|
|
73
|
-
* }>}
|
|
101
|
+
* @param {string} controllerUrl
|
|
102
|
+
* @param {string} dataplaneUrl
|
|
103
|
+
* @param {Object} healthOpts
|
|
104
|
+
* @returns {Promise<{ dataplaneVersion: string|undefined, minBuilderCliVersion: string|undefined }>}
|
|
74
105
|
*/
|
|
75
|
-
async function
|
|
76
|
-
const cliVersion = getInstalledCliVersion();
|
|
106
|
+
async function fetchAndPersistHealthSnapshot(controllerUrl, dataplaneUrl, healthOpts) {
|
|
77
107
|
let dataplaneVersion;
|
|
78
108
|
let minBuilderCliVersion;
|
|
79
109
|
|
|
80
110
|
try {
|
|
81
|
-
const snapshot = await fetchDataplaneGeneralHealth(dataplaneUrl);
|
|
111
|
+
const snapshot = await fetchDataplaneGeneralHealth(dataplaneUrl, healthOpts);
|
|
82
112
|
if (snapshot) {
|
|
83
113
|
dataplaneVersion = snapshot.version;
|
|
84
114
|
minBuilderCliVersion = snapshot.minBuilderCliVersion;
|
|
@@ -95,14 +125,63 @@ async function refreshDataplaneVersionInfo(controllerUrl, dataplaneUrl) {
|
|
|
95
125
|
// ignore — fall through to cached values
|
|
96
126
|
}
|
|
97
127
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
128
|
+
return { dataplaneVersion, minBuilderCliVersion };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Fill missing version fields from the device cache when live probe failed.
|
|
133
|
+
* @async
|
|
134
|
+
* @param {string} controllerUrl
|
|
135
|
+
* @param {string|undefined} dataplaneVersion
|
|
136
|
+
* @param {string|undefined} minBuilderCliVersion
|
|
137
|
+
* @returns {Promise<{ dataplaneVersion: string|undefined, minBuilderCliVersion: string|undefined }>}
|
|
138
|
+
*/
|
|
139
|
+
async function resolveVersionFieldsFromCache(controllerUrl, dataplaneVersion, minBuilderCliVersion) {
|
|
140
|
+
if (dataplaneVersion || minBuilderCliVersion) {
|
|
141
|
+
return { dataplaneVersion, minBuilderCliVersion };
|
|
104
142
|
}
|
|
105
143
|
|
|
144
|
+
const cached = await safeReadCache(controllerUrl);
|
|
145
|
+
if (!cached) {
|
|
146
|
+
return { dataplaneVersion, minBuilderCliVersion };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
dataplaneVersion: cached.version,
|
|
151
|
+
minBuilderCliVersion: cached.minBuilderCliVersion
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Refresh dataplane health, persist `device.<controllerUrl>` version cache,
|
|
157
|
+
* and derive the compatibility verdict against the installed Builder CLI.
|
|
158
|
+
* Falls back to the cached entry when the live probe fails so `auth status`
|
|
159
|
+
* still shows the last-known values offline.
|
|
160
|
+
*
|
|
161
|
+
* @async
|
|
162
|
+
* @param {string} controllerUrl - Controller URL (cache key)
|
|
163
|
+
* @param {string} dataplaneUrl - Dataplane base URL for the health probe
|
|
164
|
+
* @returns {Promise<{
|
|
165
|
+
* dataplaneVersion: string|undefined,
|
|
166
|
+
* minBuilderCliVersion: string|undefined,
|
|
167
|
+
* cliVersion: string,
|
|
168
|
+
* compatible: boolean
|
|
169
|
+
* }>}
|
|
170
|
+
*/
|
|
171
|
+
async function refreshDataplaneVersionInfo(controllerUrl, dataplaneUrl, options = {}) {
|
|
172
|
+
const cliVersion = getInstalledCliVersion();
|
|
173
|
+
const healthOpts = buildHealthFetchOptions(options);
|
|
174
|
+
let { dataplaneVersion, minBuilderCliVersion } = await fetchAndPersistHealthSnapshot(
|
|
175
|
+
controllerUrl,
|
|
176
|
+
dataplaneUrl,
|
|
177
|
+
healthOpts
|
|
178
|
+
);
|
|
179
|
+
({ dataplaneVersion, minBuilderCliVersion } = await resolveVersionFieldsFromCache(
|
|
180
|
+
controllerUrl,
|
|
181
|
+
dataplaneVersion,
|
|
182
|
+
minBuilderCliVersion
|
|
183
|
+
));
|
|
184
|
+
|
|
106
185
|
const compatible = computeCompatibility(cliVersion, minBuilderCliVersion);
|
|
107
186
|
return { dataplaneVersion, minBuilderCliVersion, cliVersion, compatible };
|
|
108
187
|
}
|
|
@@ -158,33 +237,6 @@ async function resolveAuthConfigAfterLogin(controllerUrl, environment, loginOpti
|
|
|
158
237
|
}
|
|
159
238
|
}
|
|
160
239
|
|
|
161
|
-
/**
|
|
162
|
-
* Resolve dataplane URL after login (mirrors auth-status silent resolver).
|
|
163
|
-
* @private
|
|
164
|
-
* @async
|
|
165
|
-
* @param {string} controllerUrl
|
|
166
|
-
* @param {string} environment
|
|
167
|
-
* @param {Object} authConfig
|
|
168
|
-
* @returns {Promise<string|null>}
|
|
169
|
-
*/
|
|
170
|
-
async function resolveDataplaneUrlAfterLogin(controllerUrl, environment, authConfig) {
|
|
171
|
-
const { findDataplaneServiceAppKey } = require('./wizard-dataplane');
|
|
172
|
-
const { getDataplaneUrl } = require('../datasource/deploy');
|
|
173
|
-
try {
|
|
174
|
-
const dataplaneAppKey = await findDataplaneServiceAppKey(
|
|
175
|
-
controllerUrl,
|
|
176
|
-
environment,
|
|
177
|
-
authConfig
|
|
178
|
-
);
|
|
179
|
-
if (dataplaneAppKey) {
|
|
180
|
-
return await getDataplaneUrl(controllerUrl, dataplaneAppKey, environment, authConfig);
|
|
181
|
-
}
|
|
182
|
-
return await getDataplaneUrl(controllerUrl, 'dataplane', environment, authConfig);
|
|
183
|
-
} catch (_resolveError) {
|
|
184
|
-
return null;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
240
|
/**
|
|
189
241
|
* Best-effort health refresh after login when dataplane URL is discoverable.
|
|
190
242
|
* Never throws — cache failures must not block login success.
|
|
@@ -195,29 +247,48 @@ async function resolveDataplaneUrlAfterLogin(controllerUrl, environment, authCon
|
|
|
195
247
|
* @param {string} [loginOptions.app] - App name (credentials login) for client-token auth
|
|
196
248
|
* @returns {Promise<void>}
|
|
197
249
|
*/
|
|
250
|
+
async function tryRefreshDataplaneVersionAfterLoginWork(controllerUrl, loginOptions = {}) {
|
|
251
|
+
const cached = await safeReadCache(controllerUrl);
|
|
252
|
+
if (cached && !isCacheStale(cached)) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const { resolveEnvironment } = require('../core/config');
|
|
257
|
+
const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
|
|
258
|
+
const environment = await resolveEnvironment();
|
|
259
|
+
const authConfig = await resolveAuthConfigAfterLogin(
|
|
260
|
+
controllerUrl,
|
|
261
|
+
environment,
|
|
262
|
+
loginOptions
|
|
263
|
+
);
|
|
264
|
+
if (!authConfig) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
let dataplaneUrl = null;
|
|
268
|
+
try {
|
|
269
|
+
dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig, {
|
|
270
|
+
silent: true
|
|
271
|
+
});
|
|
272
|
+
} catch (_resolveError) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (dataplaneUrl) {
|
|
276
|
+
await refreshDataplaneVersionInfo(controllerUrl, dataplaneUrl, {
|
|
277
|
+
healthTimeoutMs: LOGIN_HEALTH_PROBE_TIMEOUT_MS,
|
|
278
|
+
endpointOrder: 'legacy-first'
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
198
283
|
async function tryRefreshDataplaneVersionAfterLogin(controllerUrl, loginOptions = {}) {
|
|
199
284
|
if (!controllerUrl || typeof controllerUrl !== 'string') {
|
|
200
285
|
return;
|
|
201
286
|
}
|
|
202
287
|
try {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
controllerUrl,
|
|
207
|
-
environment,
|
|
208
|
-
loginOptions
|
|
288
|
+
await raceWithTimeout(
|
|
289
|
+
tryRefreshDataplaneVersionAfterLoginWork(controllerUrl, loginOptions),
|
|
290
|
+
LOGIN_POST_REFRESH_BUDGET_MS
|
|
209
291
|
);
|
|
210
|
-
if (!authConfig) {
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
const dataplaneUrl = await resolveDataplaneUrlAfterLogin(
|
|
214
|
-
controllerUrl,
|
|
215
|
-
environment,
|
|
216
|
-
authConfig
|
|
217
|
-
);
|
|
218
|
-
if (dataplaneUrl) {
|
|
219
|
-
await refreshDataplaneVersionInfo(controllerUrl, dataplaneUrl);
|
|
220
|
-
}
|
|
221
292
|
} catch (_error) {
|
|
222
293
|
// best-effort only
|
|
223
294
|
}
|
|
@@ -227,5 +298,8 @@ module.exports = {
|
|
|
227
298
|
refreshDataplaneVersionInfo,
|
|
228
299
|
loadCachedVersionInfo,
|
|
229
300
|
computeCompatibility,
|
|
230
|
-
tryRefreshDataplaneVersionAfterLogin
|
|
301
|
+
tryRefreshDataplaneVersionAfterLogin,
|
|
302
|
+
LOGIN_POST_REFRESH_BUDGET_MS,
|
|
303
|
+
LOGIN_HEALTH_PROBE_TIMEOUT_MS,
|
|
304
|
+
raceWithTimeout
|
|
231
305
|
};
|
|
@@ -11,13 +11,10 @@
|
|
|
11
11
|
const chalk = require('chalk');
|
|
12
12
|
const { failureGlyph, successGlyph } = require('../utils/cli-test-layout-chalk');
|
|
13
13
|
const logger = require('../utils/logger');
|
|
14
|
-
const config = require('../core/config');
|
|
15
|
-
const { getConfig } = config;
|
|
16
14
|
const { getOrRefreshDeviceToken } = require('../utils/token-manager');
|
|
17
15
|
const { getAuthUser } = require('../api/auth.api');
|
|
18
16
|
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
19
|
-
const {
|
|
20
|
-
const { getDataplaneUrl } = require('../datasource/deploy');
|
|
17
|
+
const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
|
|
21
18
|
const { checkDataplaneHealth } = require('../utils/dataplane-health');
|
|
22
19
|
const {
|
|
23
20
|
displayDataplaneVersionSection,
|
|
@@ -32,6 +29,11 @@ const {
|
|
|
32
29
|
EXIT_CLI_VERSION_INCOMPATIBLE,
|
|
33
30
|
EXIT_OK
|
|
34
31
|
} = require('../utils/cli-exit-codes');
|
|
32
|
+
const {
|
|
33
|
+
refreshExpiredClientTokensWherePossible,
|
|
34
|
+
displayStoredClientTokensSection,
|
|
35
|
+
tryResolveClientTokenAuth
|
|
36
|
+
} = require('./auth-status-client-tokens');
|
|
35
37
|
|
|
36
38
|
/**
|
|
37
39
|
* Format expiration date for display
|
|
@@ -94,22 +96,6 @@ async function checkDeviceToken(controllerUrl) {
|
|
|
94
96
|
}
|
|
95
97
|
}
|
|
96
98
|
|
|
97
|
-
/**
|
|
98
|
-
* Decrypt token if encrypted
|
|
99
|
-
* @async
|
|
100
|
-
* @param {string} token - Token to decrypt
|
|
101
|
-
* @returns {Promise<string>} Decrypted token
|
|
102
|
-
*/
|
|
103
|
-
async function decryptTokenIfNeeded(token) {
|
|
104
|
-
const { decryptToken, isTokenEncrypted } = require('../utils/token-encryption');
|
|
105
|
-
const encryptionKey = await config.getSecretsEncryptionKey();
|
|
106
|
-
|
|
107
|
-
if (encryptionKey && isTokenEncrypted(token)) {
|
|
108
|
-
return await decryptToken(token, encryptionKey);
|
|
109
|
-
}
|
|
110
|
-
return token;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
99
|
/**
|
|
114
100
|
* Validate client token and return result
|
|
115
101
|
* @async
|
|
@@ -156,29 +142,14 @@ async function validateClientToken(token, controllerUrl, environment, appName, e
|
|
|
156
142
|
}
|
|
157
143
|
|
|
158
144
|
/**
|
|
159
|
-
* Check and validate client token
|
|
145
|
+
* Check and validate client token (refreshes when expired if client credentials exist).
|
|
160
146
|
* @async
|
|
161
147
|
* @param {string} controllerUrl - Controller URL
|
|
162
148
|
* @param {string} environment - Environment key
|
|
163
149
|
* @returns {Promise<Object|null>} Token validation result or null
|
|
164
150
|
*/
|
|
165
151
|
async function checkClientToken(controllerUrl, environment) {
|
|
166
|
-
|
|
167
|
-
const environments = configData.environments || {};
|
|
168
|
-
const envConfig = environments[environment];
|
|
169
|
-
|
|
170
|
-
if (!envConfig || !envConfig.clients) {
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
for (const [appName, tokenData] of Object.entries(envConfig.clients)) {
|
|
175
|
-
if (tokenData.controller === controllerUrl && tokenData.token) {
|
|
176
|
-
const token = await decryptTokenIfNeeded(tokenData.token);
|
|
177
|
-
return await validateClientToken(token, controllerUrl, environment, appName, tokenData.expiresAt);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return null;
|
|
152
|
+
return tryResolveClientTokenAuth(controllerUrl, environment, validateClientToken);
|
|
182
153
|
}
|
|
183
154
|
|
|
184
155
|
/**
|
|
@@ -239,11 +210,7 @@ function displayTokenInfo(tokenInfo) {
|
|
|
239
210
|
*/
|
|
240
211
|
async function resolveDataplaneUrlSilent(controllerUrl, environment, authConfig) {
|
|
241
212
|
try {
|
|
242
|
-
|
|
243
|
-
if (dataplaneAppKey) {
|
|
244
|
-
return await getDataplaneUrl(controllerUrl, dataplaneAppKey, environment, authConfig);
|
|
245
|
-
}
|
|
246
|
-
return await getDataplaneUrl(controllerUrl, 'dataplane', environment, authConfig);
|
|
213
|
+
return await resolveDataplaneUrl(controllerUrl, environment, authConfig, { silent: true });
|
|
247
214
|
} catch {
|
|
248
215
|
return null;
|
|
249
216
|
}
|
|
@@ -382,18 +349,34 @@ async function buildDataplaneInfo(controllerUrl, environment, authConfig) {
|
|
|
382
349
|
* @param {boolean} [options.validate] - If true, exit with code 1 when not authenticated, or code 3 when CLI is older than dataplane minimum
|
|
383
350
|
* @returns {Promise<void>} Resolves when status is displayed
|
|
384
351
|
*/
|
|
352
|
+
/**
|
|
353
|
+
* Prefer a working device token; fall back to environment client credentials when the
|
|
354
|
+
* stored device token is expired or rejected (common in long-lived dev VMs).
|
|
355
|
+
*
|
|
356
|
+
* @async
|
|
357
|
+
* @param {string} controllerUrl
|
|
358
|
+
* @param {string} environment
|
|
359
|
+
* @returns {Promise<Object|null>}
|
|
360
|
+
*/
|
|
361
|
+
async function resolveAuthenticatedTokenInfo(controllerUrl, environment) {
|
|
362
|
+
const deviceResult = await checkDeviceToken(controllerUrl);
|
|
363
|
+
if (deviceResult && deviceResult.authenticated) {
|
|
364
|
+
return deviceResult;
|
|
365
|
+
}
|
|
366
|
+
const clientResult = await checkClientToken(controllerUrl, environment);
|
|
367
|
+
if (clientResult && clientResult.authenticated) {
|
|
368
|
+
return clientResult;
|
|
369
|
+
}
|
|
370
|
+
return deviceResult || clientResult || null;
|
|
371
|
+
}
|
|
372
|
+
|
|
385
373
|
async function handleAuthStatus(options = {}) {
|
|
386
374
|
const { resolveEnvironment } = require('../core/config');
|
|
387
375
|
const controllerUrl = await resolveControllerUrl();
|
|
388
376
|
const environment = await resolveEnvironment();
|
|
389
377
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
// If no device token, check client token
|
|
394
|
-
if (!tokenInfo) {
|
|
395
|
-
tokenInfo = await checkClientToken(controllerUrl, environment);
|
|
396
|
-
}
|
|
378
|
+
const refreshedCount = await refreshExpiredClientTokensWherePossible(environment, controllerUrl);
|
|
379
|
+
const tokenInfo = await resolveAuthenticatedTokenInfo(controllerUrl, environment);
|
|
397
380
|
|
|
398
381
|
let dataplaneInfo = null;
|
|
399
382
|
if (tokenInfo && tokenInfo.authenticated && tokenInfo.token) {
|
|
@@ -402,6 +385,7 @@ async function handleAuthStatus(options = {}) {
|
|
|
402
385
|
}
|
|
403
386
|
|
|
404
387
|
displayStatus(controllerUrl, environment, tokenInfo, dataplaneInfo);
|
|
388
|
+
await displayStoredClientTokensSection(environment, controllerUrl, refreshedCount);
|
|
405
389
|
|
|
406
390
|
if (options.validate) {
|
|
407
391
|
if (!tokenInfo || !tokenInfo.authenticated) {
|
|
@@ -427,10 +411,7 @@ async function getValidatedAuthContext() {
|
|
|
427
411
|
const controllerUrl = await resolveControllerUrl();
|
|
428
412
|
const environment = await resolveEnvironment();
|
|
429
413
|
|
|
430
|
-
|
|
431
|
-
if (!tokenInfo) {
|
|
432
|
-
tokenInfo = await checkClientToken(controllerUrl, environment);
|
|
433
|
-
}
|
|
414
|
+
const tokenInfo = await resolveAuthenticatedTokenInfo(controllerUrl, environment);
|
|
434
415
|
|
|
435
416
|
if (!tokenInfo || !tokenInfo.authenticated || !tokenInfo.token) {
|
|
436
417
|
throw new Error('Not authenticated. Run "aifabrix login" or configure client credentials.');
|
|
@@ -442,4 +423,12 @@ async function getValidatedAuthContext() {
|
|
|
442
423
|
return { controllerUrl, environment, authConfig, dataplaneUrl };
|
|
443
424
|
}
|
|
444
425
|
|
|
445
|
-
module.exports = {
|
|
426
|
+
module.exports = {
|
|
427
|
+
handleAuthStatus,
|
|
428
|
+
getValidatedAuthContext,
|
|
429
|
+
resolveAuthenticatedTokenInfo,
|
|
430
|
+
checkDeviceToken,
|
|
431
|
+
checkClientToken,
|
|
432
|
+
refreshExpiredClientTokensWherePossible,
|
|
433
|
+
displayStoredClientTokensSection
|
|
434
|
+
};
|