@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,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-app virtual-directory env var names ({@code <NAME>_RELATIVE_PATH}) for Builder CLI.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview {@code app.key} → {@code DATAPLANE_RELATIVE_PATH}, {@code MYAPP_RELATIVE_PATH}, etc.
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/** Must match {@link module:lib/utils/url-declarative-vdir-inactive-env} token (avoid circular require). */
|
|
12
|
+
const URL_DECLARATIVE_VDIR_PUBLIC_TOKEN = 'url://vdir-public';
|
|
13
|
+
|
|
14
|
+
/** Suffix for runtime mount vars derived from {@code application.yaml} {@code app.key}. */
|
|
15
|
+
const APP_RELATIVE_PATH_ENV_SUFFIX = '_RELATIVE_PATH';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Apps whose runtime vdir env name is not {@code UPPER(appKey)_RELATIVE_PATH}.
|
|
19
|
+
* @type {Readonly<Record<string, string>>}
|
|
20
|
+
*/
|
|
21
|
+
const APP_RELATIVE_PATH_ENV_KEY_OVERRIDES = Object.freeze({
|
|
22
|
+
keycloak: 'KC_HTTP_RELATIVE_PATH',
|
|
23
|
+
'miso-controller': 'MISO_RELATIVE_PATH'
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/** Always rewritten to "/" when front-door path is inactive (shared infra apps). */
|
|
27
|
+
const INACTIVE_VDIR_PUBLIC_STATIC_ENV_KEYS = Object.freeze([
|
|
28
|
+
'KC_HTTP_RELATIVE_PATH',
|
|
29
|
+
'MISO_RELATIVE_PATH'
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {string} appKey - {@code application.yaml} {@code app.key}
|
|
34
|
+
* @returns {string} e.g. {@code dataplane} → {@code DATAPLANE_RELATIVE_PATH}, {@code myapp} → {@code MYAPP_RELATIVE_PATH}
|
|
35
|
+
*/
|
|
36
|
+
function appKeyToRelativePathEnvKey(appKey) {
|
|
37
|
+
const key = String(appKey || '').trim();
|
|
38
|
+
if (!key) {
|
|
39
|
+
return '';
|
|
40
|
+
}
|
|
41
|
+
const override = APP_RELATIVE_PATH_ENV_KEY_OVERRIDES[key];
|
|
42
|
+
if (override) {
|
|
43
|
+
return override;
|
|
44
|
+
}
|
|
45
|
+
const upper = key.replace(/-/g, '_').toUpperCase();
|
|
46
|
+
return `${upper}${APP_RELATIVE_PATH_ENV_SUFFIX}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Whether {@code key} is a runtime vdir env name (static Keycloak/Miso or {@code NAME_RELATIVE_PATH}).
|
|
51
|
+
* @param {string} key
|
|
52
|
+
* @returns {boolean}
|
|
53
|
+
*/
|
|
54
|
+
function isAppRelativePathEnvKey(key) {
|
|
55
|
+
const name = String(key || '').trim();
|
|
56
|
+
if (!name) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
if (INACTIVE_VDIR_PUBLIC_STATIC_ENV_KEYS.includes(name)) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
return name.endsWith(APP_RELATIVE_PATH_ENV_SUFFIX);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Env keys to rewrite when path is inactive: static Keycloak/Miso + current app vdir key.
|
|
67
|
+
* @param {string|null|undefined} appKey - {@code application.yaml} {@code app.key}
|
|
68
|
+
* @returns {ReadonlySet<string>}
|
|
69
|
+
*/
|
|
70
|
+
function resolveInactiveVdirRewriteEnvKeys(appKey) {
|
|
71
|
+
const keys = new Set(INACTIVE_VDIR_PUBLIC_STATIC_ENV_KEYS);
|
|
72
|
+
const envKey = appKeyToRelativePathEnvKey(appKey);
|
|
73
|
+
if (envKey) {
|
|
74
|
+
keys.add(envKey);
|
|
75
|
+
}
|
|
76
|
+
return keys;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* When an app is created from a product template, rename the template's {@code *_RELATIVE_PATH}
|
|
81
|
+
* line to match {@code app.key} (e.g. {@code DATAPLANE_RELATIVE_PATH} → {@code MYAPP_RELATIVE_PATH}).
|
|
82
|
+
* @param {string} content - {@code env.template} body
|
|
83
|
+
* @param {string} appKey
|
|
84
|
+
* @returns {string}
|
|
85
|
+
*/
|
|
86
|
+
function renameAppRelativePathEnvKeyInTemplate(content, appKey) {
|
|
87
|
+
const targetKey = appKeyToRelativePathEnvKey(appKey);
|
|
88
|
+
if (!targetKey || !content || !content.includes(URL_DECLARATIVE_VDIR_PUBLIC_TOKEN)) {
|
|
89
|
+
return content;
|
|
90
|
+
}
|
|
91
|
+
const escapedToken = URL_DECLARATIVE_VDIR_PUBLIC_TOKEN.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
92
|
+
const lineRe = new RegExp(
|
|
93
|
+
`^([ \\t]*)([A-Z][A-Z0-9_]*${APP_RELATIVE_PATH_ENV_SUFFIX})([ \\t]*=\\s*)${escapedToken}[ \\t]*$`,
|
|
94
|
+
'gm'
|
|
95
|
+
);
|
|
96
|
+
return content.replace(lineRe, (match, indent, currentKey, eq) => {
|
|
97
|
+
if (currentKey === targetKey) {
|
|
98
|
+
return match;
|
|
99
|
+
}
|
|
100
|
+
return `${indent}${targetKey}${eq}${URL_DECLARATIVE_VDIR_PUBLIC_TOKEN}`;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = {
|
|
105
|
+
APP_RELATIVE_PATH_ENV_SUFFIX,
|
|
106
|
+
APP_RELATIVE_PATH_ENV_KEY_OVERRIDES,
|
|
107
|
+
INACTIVE_VDIR_PUBLIC_STATIC_ENV_KEYS,
|
|
108
|
+
appKeyToRelativePathEnvKey,
|
|
109
|
+
isAppRelativePathEnvKey,
|
|
110
|
+
resolveInactiveVdirRewriteEnvKeys,
|
|
111
|
+
renameAppRelativePathEnvKeyInTemplate
|
|
112
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical Builder CLI exit codes for scriptable commands.
|
|
3
|
+
*
|
|
4
|
+
* Documented in `docs/commands/authentication.md`. Use in
|
|
5
|
+
* `aifabrix auth status --validate` and any future `--validate`-style flag.
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Exit-code constants for scriptable Builder CLI commands
|
|
8
|
+
* @author AI Fabrix Team
|
|
9
|
+
* @version 2.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
/** Success. */
|
|
15
|
+
const EXIT_OK = 0;
|
|
16
|
+
|
|
17
|
+
/** Not authenticated / invalid token. */
|
|
18
|
+
const EXIT_NOT_AUTHENTICATED = 1;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Authenticated but installed Builder CLI version is older than the dataplane
|
|
22
|
+
* minimum advertised by `GET /api/v1/health → minBuilderCliVersion` (plan 403.0).
|
|
23
|
+
*/
|
|
24
|
+
const EXIT_CLI_VERSION_INCOMPATIBLE = 3;
|
|
25
|
+
|
|
26
|
+
module.exports = {
|
|
27
|
+
EXIT_OK,
|
|
28
|
+
EXIT_NOT_AUTHENTICATED,
|
|
29
|
+
EXIT_CLI_VERSION_INCOMPATIBLE
|
|
30
|
+
};
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
const chalk = require('chalk');
|
|
10
10
|
const { metadata, sectionTitle } = require('./cli-layout-chalk');
|
|
11
|
+
const { kvRefToSecretSetKey } = require('./secrets-missing-error');
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* @param {string[]} messages - Output lines
|
|
@@ -25,34 +26,72 @@ function pushMissingSecretsBodyLines(messages, missingSecretsMatch) {
|
|
|
25
26
|
for (const ref of parts) {
|
|
26
27
|
messages.push(` ${chalk.cyan('-')} ${chalk.white(ref)}`);
|
|
27
28
|
}
|
|
28
|
-
return;
|
|
29
|
+
return parts;
|
|
29
30
|
}
|
|
30
31
|
if (parts.length === 1) {
|
|
31
32
|
messages.push(` ${sectionTitle('Missing secrets:')} ${chalk.white(parts[0])}`);
|
|
32
|
-
return;
|
|
33
|
+
return parts;
|
|
33
34
|
}
|
|
34
35
|
messages.push(` ${sectionTitle('Missing secrets:')}`);
|
|
35
|
-
return;
|
|
36
|
+
return [];
|
|
36
37
|
}
|
|
37
38
|
messages.push(` ${chalk.white('Missing secrets in secrets file.')}`);
|
|
39
|
+
return [];
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
/**
|
|
41
|
-
* @param {string
|
|
42
|
-
* @
|
|
43
|
-
|
|
43
|
+
* @param {string} errorMsg
|
|
44
|
+
* @returns {Map<string, string>}
|
|
45
|
+
*/
|
|
46
|
+
function parseEnvTemplateLineHints(errorMsg) {
|
|
47
|
+
const map = new Map();
|
|
48
|
+
const re = /^Env-template-line: (kv:\/\/[^\s|]+)\|(.+)$/gm;
|
|
49
|
+
let m;
|
|
50
|
+
while ((m = re.exec(errorMsg)) !== null) {
|
|
51
|
+
map.set(m[1].trim(), m[2].trim());
|
|
52
|
+
}
|
|
53
|
+
return map;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {string[]} messages
|
|
58
|
+
* @param {string[]} missingRefs
|
|
59
|
+
* @param {Map<string, string>} templateLinesByRef
|
|
60
|
+
* @param {string|null} envTemplatePath
|
|
44
61
|
*/
|
|
45
|
-
function
|
|
62
|
+
function pushRemediationHintLines(messages, missingRefs, templateLinesByRef, envTemplatePath) {
|
|
63
|
+
messages.push(` ${sectionTitle('You can fix this two ways:')}`);
|
|
64
|
+
messages.push(
|
|
65
|
+
` ${chalk.cyan('1.')} ${chalk.white('Not used by your external system?')} Comment out or delete the line in env.template.`
|
|
66
|
+
);
|
|
67
|
+
if (envTemplatePath) {
|
|
68
|
+
messages.push(` ${metadata('env.template:')} ${chalk.white(envTemplatePath)}`);
|
|
69
|
+
}
|
|
70
|
+
for (const ref of missingRefs) {
|
|
71
|
+
const line = templateLinesByRef.get(ref);
|
|
72
|
+
if (line) {
|
|
73
|
+
messages.push(` ${metadata('Comment or delete:')} ${chalk.gray(line)}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
messages.push(
|
|
77
|
+
` ${chalk.cyan('2.')} ${chalk.white('Store the value:')} ${metadata('aifabrix secret set')} ${chalk.yellow('<key>')} ${chalk.gray('"<your-value>"')}`
|
|
78
|
+
);
|
|
79
|
+
for (const ref of missingRefs) {
|
|
80
|
+
const key = kvRefToSecretSetKey(ref);
|
|
81
|
+
if (key) {
|
|
82
|
+
messages.push(` ${metadata('Example:')} ${chalk.yellow(`aifabrix secret set ${key} "<your-value>"`)}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @param {string[]} messages
|
|
89
|
+
* @param {RegExpMatchArray|null} fileInfoMatch
|
|
90
|
+
*/
|
|
91
|
+
function pushSecretsFileLocationLine(messages, fileInfoMatch) {
|
|
46
92
|
if (fileInfoMatch) {
|
|
47
93
|
messages.push(` ${metadata('Secrets file location:')} ${chalk.white(fileInfoMatch[1])}`);
|
|
48
94
|
}
|
|
49
|
-
if (resolveMatch) {
|
|
50
|
-
messages.push(
|
|
51
|
-
` ${metadata('Run:')} ${chalk.yellow(`aifabrix resolve ${resolveMatch[1]} to generate missing secrets.`)}`
|
|
52
|
-
);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
messages.push(` ${metadata('Run:')} ${chalk.yellow('aifabrix resolve <app-name> to generate missing secrets.')}`);
|
|
56
95
|
}
|
|
57
96
|
|
|
58
97
|
/**
|
|
@@ -66,13 +105,36 @@ function formatSecretsError(errorMsg) {
|
|
|
66
105
|
}
|
|
67
106
|
|
|
68
107
|
const messages = [];
|
|
69
|
-
pushMissingSecretsBodyLines(messages, errorMsg.match(/Missing secrets: ([^\n]+)/));
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
108
|
+
const missingRefs = pushMissingSecretsBodyLines(messages, errorMsg.match(/Missing secrets: ([^\n]+)/));
|
|
109
|
+
pushSecretsFileLocationLine(messages, errorMsg.match(/Secrets file location: ([^\n]+)/));
|
|
110
|
+
const envTemplatePathMatch = errorMsg.match(/Env-template-path: ([^\n]+)/);
|
|
111
|
+
const envTemplatePath = envTemplatePathMatch ? envTemplatePathMatch[1].trim() : null;
|
|
112
|
+
const templateLinesByRef = parseEnvTemplateLineHints(errorMsg);
|
|
113
|
+
const refs =
|
|
114
|
+
missingRefs.length > 0
|
|
115
|
+
? missingRefs
|
|
116
|
+
: [...templateLinesByRef.keys()];
|
|
117
|
+
if (refs.length > 0) {
|
|
118
|
+
pushRemediationHintLines(messages, refs, templateLinesByRef, envTemplatePath);
|
|
119
|
+
}
|
|
120
|
+
const staleMatch = errorMsg.match(/Stale-template-hint: oauth-leftover \| repair ([^\n]+)/);
|
|
121
|
+
if (staleMatch) {
|
|
122
|
+
messages.push(
|
|
123
|
+
` ${chalk.yellow('⚠')} ${chalk.white('These keys are often left over after switching to apikey/bearerToken auth.')}`
|
|
124
|
+
);
|
|
125
|
+
messages.push(
|
|
126
|
+
` ${metadata('Run:')} ${chalk.yellow(`aifabrix repair ${staleMatch[1].trim()}`)} ${chalk.gray('to comment stale OAuth lines in env.template')}`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
75
129
|
return messages;
|
|
76
130
|
}
|
|
77
131
|
|
|
78
|
-
|
|
132
|
+
/**
|
|
133
|
+
* @param {string} errorMsg
|
|
134
|
+
* @returns {boolean}
|
|
135
|
+
*/
|
|
136
|
+
function isMissingSecretsErrorMessage(errorMsg) {
|
|
137
|
+
return typeof errorMsg === 'string' && errorMsg.includes('Missing secrets');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = { formatSecretsError, isMissingSecretsErrorMessage };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Detect skip-sync from Commander options and raw argv (--no-sync → sync: false).
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* True when the user passed `--no-sync` or Commander set `sync: false` / `noSync: true`.
|
|
11
|
+
* Do not use for `protection upload --no-sync` (post-upload datasource sync only).
|
|
12
|
+
*
|
|
13
|
+
* @param {Object|null|undefined} options - Commander-parsed options
|
|
14
|
+
* @param {import('commander').Command} [cmd] - Commander command (for rawArgs fallback)
|
|
15
|
+
* @returns {boolean}
|
|
16
|
+
*/
|
|
17
|
+
function cliOptsSkipSync(options, cmd) {
|
|
18
|
+
if (!options || typeof options !== 'object') {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (options.noSync === true) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
if (options.sync === false) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
const raw = cmd && Array.isArray(cmd.rawArgs) ? cmd.rawArgs : [];
|
|
28
|
+
return raw.includes('--no-sync');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = {
|
|
32
|
+
cliOptsSkipSync
|
|
33
|
+
};
|
package/lib/utils/cli-utils.js
CHANGED
|
@@ -12,7 +12,7 @@ const path = require('path');
|
|
|
12
12
|
const chalk = require('chalk');
|
|
13
13
|
const logger = require('./logger');
|
|
14
14
|
const { formatBlockingError, infoLine } = require('./cli-layout-chalk');
|
|
15
|
-
const { formatSecretsError } = require('./cli-secrets-error-format');
|
|
15
|
+
const { formatSecretsError, isMissingSecretsErrorMessage } = require('./cli-secrets-error-format');
|
|
16
16
|
const { getDockerDaemonStartHintSentence, getDockerApiOverTcpHintLines } = require('./docker-not-running-hint');
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -337,6 +337,15 @@ function formatError(error) {
|
|
|
337
337
|
return formatFormattedError(error.formatted);
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
+
if (error.network === true) {
|
|
341
|
+
const { formatNetworkError } = require('./api-error-handler');
|
|
342
|
+
const formatted = formatNetworkError(error.message || 'Network error', {
|
|
343
|
+
endpointUrl: error.endpointUrl,
|
|
344
|
+
serviceLabel: error.serviceLabel || 'service'
|
|
345
|
+
});
|
|
346
|
+
return formatFormattedError(formatted);
|
|
347
|
+
}
|
|
348
|
+
|
|
340
349
|
const errorMsg = error.message || '';
|
|
341
350
|
|
|
342
351
|
// Try different error formatters in order of specificity
|
|
@@ -355,10 +364,12 @@ function formatError(error) {
|
|
|
355
364
|
* @param {string} command - Command that failed
|
|
356
365
|
* @param {string[]} errorMessages - Error message lines
|
|
357
366
|
*/
|
|
358
|
-
function logError(command, errorMessages) {
|
|
367
|
+
function logError(command, errorMessages, options = {}) {
|
|
359
368
|
logger.error(`\n${formatBlockingError(`Error in ${command} command:`)}`);
|
|
360
369
|
errorMessages.forEach(msg => logger.error(msg));
|
|
361
|
-
|
|
370
|
+
if (!options.skipDoctor) {
|
|
371
|
+
logger.log(`\n${infoLine('ℹ Run "aifabrix doctor" for environment diagnostics.')}\n`);
|
|
372
|
+
}
|
|
362
373
|
}
|
|
363
374
|
|
|
364
375
|
/**
|
|
@@ -396,7 +407,8 @@ function isAuthenticationError(error) {
|
|
|
396
407
|
*/
|
|
397
408
|
function handleCommandError(error, command) {
|
|
398
409
|
const errorMessages = formatError(error);
|
|
399
|
-
|
|
410
|
+
const skipDoctor = isMissingSecretsErrorMessage(error && error.message);
|
|
411
|
+
logError(command, errorMessages, { skipDoctor });
|
|
400
412
|
if (error.wizardResumeMessage) {
|
|
401
413
|
logger.log(error.wizardResumeMessage);
|
|
402
414
|
}
|
|
@@ -61,6 +61,10 @@ function normalizePythonReloadForComposeMounted(raw) {
|
|
|
61
61
|
if (/^python3?\s+-m\s+uvicorn\s+/i.test(s)) {
|
|
62
62
|
return s;
|
|
63
63
|
}
|
|
64
|
+
const envPrefixed = s.match(/^((?:[A-Za-z_][A-Za-z0-9_]*=\S+\s+)*)(uvicorn\s+.*)$/);
|
|
65
|
+
if (envPrefixed) {
|
|
66
|
+
return `${envPrefixed[1]}python -m uvicorn ${envPrefixed[2].slice('uvicorn '.length)}`;
|
|
67
|
+
}
|
|
64
68
|
if (s.startsWith('uvicorn ')) {
|
|
65
69
|
return `python -m uvicorn ${s.slice('uvicorn '.length)}`;
|
|
66
70
|
}
|
|
@@ -86,6 +86,19 @@ function expandFrontDoorHostPlaceholders(template, developerIdRaw, remoteServer)
|
|
|
86
86
|
return t;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
function templateUsesRemoteHostPlaceholder(hostTemplate) {
|
|
90
|
+
return /\$\{REMOTE_HOST\}/.test(String(hostTemplate || ''));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function resolveTraefikRouterHost(hostTemplate, developerIdRaw, remoteServer) {
|
|
94
|
+
const usesRemote = templateUsesRemoteHostPlaceholder(hostTemplate);
|
|
95
|
+
const hasRemote = Boolean(remoteServer && String(remoteServer).trim());
|
|
96
|
+
if (usesRemote && !hasRemote) {
|
|
97
|
+
return 'localhost';
|
|
98
|
+
}
|
|
99
|
+
return expandFrontDoorHostPlaceholders(hostTemplate, developerIdRaw, remoteServer);
|
|
100
|
+
}
|
|
101
|
+
|
|
89
102
|
/**
|
|
90
103
|
* Traefik PathPrefix / host / TLS from frontDoorRouting (public ingress). Does not include StripPrefix — that follows
|
|
91
104
|
* the in-container health path: private URL is `http://<service>:<port>` plus the probe path; `/dev`, `/tst`, `/auth`,
|
|
@@ -105,7 +118,7 @@ function buildTraefikIngressBase(config, devId, scopeOpts, remoteServer) {
|
|
|
105
118
|
if (!frontDoor.host || typeof frontDoor.host !== 'string') {
|
|
106
119
|
throw new Error('frontDoorRouting.host is required when frontDoorRouting.enabled is true');
|
|
107
120
|
}
|
|
108
|
-
const host =
|
|
121
|
+
const host = resolveTraefikRouterHost(frontDoor.host, devId, remoteServer);
|
|
109
122
|
const basePath = derivePathFromPattern(frontDoor.pattern);
|
|
110
123
|
let pathOut = basePath;
|
|
111
124
|
if (
|
|
@@ -153,6 +166,8 @@ module.exports = {
|
|
|
153
166
|
resolveTraefikTlsEnabled,
|
|
154
167
|
buildDevUsernameForFrontDoorHost,
|
|
155
168
|
expandFrontDoorHostPlaceholders,
|
|
169
|
+
templateUsesRemoteHostPlaceholder,
|
|
170
|
+
resolveTraefikRouterHost,
|
|
156
171
|
buildTraefikIngressBase,
|
|
157
172
|
computeTraefikStripPathPrefix
|
|
158
173
|
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secrets path config helpers.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Keeps lib/core/config.js under max-lines
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const os = require('os');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Expand leading ~ to home directory so config paths like ~/.aifabrix/secrets.local.yaml resolve correctly.
|
|
14
|
+
* @param {string} filePath - Path that may start with ~ or ~/
|
|
15
|
+
* @returns {string} Path with ~ expanded, or unchanged if no leading ~
|
|
16
|
+
*/
|
|
17
|
+
function expandTilde(filePath) {
|
|
18
|
+
if (!filePath || typeof filePath !== 'string') return filePath;
|
|
19
|
+
if (filePath === '~') return os.homedir();
|
|
20
|
+
if (filePath.startsWith('~/') || filePath.startsWith('~' + path.sep)) {
|
|
21
|
+
return path.join(os.homedir(), filePath.slice(2));
|
|
22
|
+
}
|
|
23
|
+
return filePath;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function createSecretsPathFunctions(getConfig, saveConfig) {
|
|
27
|
+
async function getSecretsPath() {
|
|
28
|
+
const cfg = await getConfig();
|
|
29
|
+
const raw = cfg['aifabrix-secrets'] || cfg['secrets-path'] || null;
|
|
30
|
+
return raw ? expandTilde(raw) : null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function setSecretsPath(secretsPath) {
|
|
34
|
+
if (typeof secretsPath !== 'string') {
|
|
35
|
+
throw new Error('Secrets path is required and must be a string');
|
|
36
|
+
}
|
|
37
|
+
const cfg = await getConfig();
|
|
38
|
+
cfg['aifabrix-secrets'] = secretsPath.trim() || undefined;
|
|
39
|
+
await saveConfig(cfg);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return { getSecretsPath, setSecretsPath };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = { createSecretsPathFunctions };
|
|
46
|
+
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
const { getDeveloperIdNumber } = require('./env-map');
|
|
13
13
|
const devConfig = require('./dev-config');
|
|
14
14
|
const config = require('../core/config');
|
|
15
|
+
const { getDeviceToken, isTokenExpired } = require('./token-manager');
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Calculate default controller URL based on developer ID
|
|
@@ -40,7 +41,6 @@ function normalizeUrl(url) {
|
|
|
40
41
|
}
|
|
41
42
|
return url.trim().replace(/\/+$/, '');
|
|
42
43
|
}
|
|
43
|
-
|
|
44
44
|
/**
|
|
45
45
|
* True when `config.yaml` has a non-expired-looking device entry for this controller URL
|
|
46
46
|
* (normalized host/path comparison).
|
|
@@ -68,6 +68,90 @@ async function hasStoredDeviceTokenForController(controllerUrl) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* True when a stored, non-expired device token exists (local config only — no refresh HTTP).
|
|
73
|
+
* Setup uses this to decide whether to skip the device-login step; avoids refresh warnings
|
|
74
|
+
* when the controller is not up yet or `device:` is empty.
|
|
75
|
+
*
|
|
76
|
+
* @async
|
|
77
|
+
* @param {string} controllerUrl
|
|
78
|
+
* @returns {Promise<boolean>}
|
|
79
|
+
*/
|
|
80
|
+
async function isDeviceTokenUsableForController(controllerUrl) {
|
|
81
|
+
if (!(await hasStoredDeviceTokenForController(controllerUrl))) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
const want = normalizeUrl(controllerUrl);
|
|
85
|
+
try {
|
|
86
|
+
const tokenInfo = await getDeviceToken(want);
|
|
87
|
+
return Boolean(tokenInfo && tokenInfo.token && !isTokenExpired(tokenInfo.expiresAt));
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* True when a stored device token can be refreshed (if needed) and validated against the controller API.
|
|
95
|
+
* Prefer this after the controller is reachable (guided platform auth step). Falls back to local expiry-only
|
|
96
|
+
* check when the controller is not reachable yet.
|
|
97
|
+
*
|
|
98
|
+
* @async
|
|
99
|
+
* @param {string} controllerUrl
|
|
100
|
+
* @returns {Promise<boolean>}
|
|
101
|
+
*/
|
|
102
|
+
async function isPlatformAuthValidForController(controllerUrl) {
|
|
103
|
+
if (!(await hasStoredDeviceTokenForController(controllerUrl))) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
const want = normalizeUrl(controllerUrl);
|
|
107
|
+
|
|
108
|
+
if (!(await isControllerHealthReachable(want))) {
|
|
109
|
+
return isDeviceTokenUsableForController(controllerUrl);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const { getOrRefreshDeviceToken } = require('./token-manager');
|
|
113
|
+
const { getAuthUser } = require('../api/auth.api');
|
|
114
|
+
|
|
115
|
+
let tokenResult = null;
|
|
116
|
+
try {
|
|
117
|
+
tokenResult = await getOrRefreshDeviceToken(want);
|
|
118
|
+
} catch {
|
|
119
|
+
tokenResult = null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!tokenResult || !tokenResult.token) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const response = await getAuthUser(want, { type: 'bearer', token: tokenResult.token });
|
|
128
|
+
return Boolean(response.success && response.data && response.data.authenticated !== false);
|
|
129
|
+
} catch {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Best-effort GET /health on the controller (no auth). Used to avoid token refresh HTTP
|
|
136
|
+
* during setup before Miso Controller is running.
|
|
137
|
+
*
|
|
138
|
+
* @async
|
|
139
|
+
* @param {string} controllerUrl
|
|
140
|
+
* @returns {Promise<boolean>}
|
|
141
|
+
*/
|
|
142
|
+
async function isControllerHealthReachable(controllerUrl) {
|
|
143
|
+
const base = normalizeUrl(controllerUrl);
|
|
144
|
+
if (!base) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
const res = await fetch(`${base}/health`, { signal: AbortSignal.timeout(2500) });
|
|
149
|
+
return res.ok;
|
|
150
|
+
} catch {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
71
155
|
/**
|
|
72
156
|
* Get controller URL from logged-in user's device tokens.
|
|
73
157
|
* Prefers the entry under {@link config.controller} when it matches a `device` key; otherwise a
|
|
@@ -152,6 +236,9 @@ async function resolveControllerUrl() {
|
|
|
152
236
|
module.exports = {
|
|
153
237
|
getDefaultControllerUrl,
|
|
154
238
|
hasStoredDeviceTokenForController,
|
|
239
|
+
isDeviceTokenUsableForController,
|
|
240
|
+
isPlatformAuthValidForController,
|
|
241
|
+
isControllerHealthReachable,
|
|
155
242
|
getControllerUrlFromLoggedInUser,
|
|
156
243
|
getControllerFromConfig,
|
|
157
244
|
resolveControllerUrl
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Progress line + optional in-place spinner for credential secret push.
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const chalk = require('chalk');
|
|
10
|
+
const logger = require('./logger');
|
|
11
|
+
const { formatProgress } = require('./cli-test-layout-chalk');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {Object} [options]
|
|
15
|
+
* @param {boolean} [options.json] - When true, skip progress line and spinner
|
|
16
|
+
* @returns {boolean}
|
|
17
|
+
*/
|
|
18
|
+
function shouldShowCredentialPushProgress(options = {}) {
|
|
19
|
+
return options.json !== true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {Object} [options]
|
|
24
|
+
* @returns {boolean}
|
|
25
|
+
*/
|
|
26
|
+
function shouldUseCredentialPushSpinner(options = {}) {
|
|
27
|
+
if (!shouldShowCredentialPushProgress(options)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
return Boolean(process && process.stdout && process.stdout.isTTY);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {string} systemKey
|
|
35
|
+
* @returns {string}
|
|
36
|
+
*/
|
|
37
|
+
function buildCredentialPushSpinnerLabel(systemKey) {
|
|
38
|
+
const key = systemKey && String(systemKey).trim() ? String(systemKey).trim() : 'integration';
|
|
39
|
+
return `Pushing credential secrets for ${key}…`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {string} systemKey
|
|
44
|
+
* @returns {string}
|
|
45
|
+
*/
|
|
46
|
+
function buildCredentialPushSpinnerText(systemKey) {
|
|
47
|
+
return formatProgress(buildCredentialPushSpinnerLabel(systemKey));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Run credential push work with visible progress (⏳ line or ora with label on TTY).
|
|
52
|
+
*
|
|
53
|
+
* @async
|
|
54
|
+
* @param {Function} work - Zero-arg async function performing the push
|
|
55
|
+
* @param {Object} [opts]
|
|
56
|
+
* @param {string} [opts.systemKey]
|
|
57
|
+
* @param {boolean} [opts.json]
|
|
58
|
+
* @returns {Promise<*>}
|
|
59
|
+
*/
|
|
60
|
+
async function runWithCredentialPushSpinner(work, opts = {}) {
|
|
61
|
+
if (!shouldShowCredentialPushProgress(opts)) {
|
|
62
|
+
return work();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!shouldUseCredentialPushSpinner(opts)) {
|
|
66
|
+
logger.log(buildCredentialPushSpinnerText(opts.systemKey));
|
|
67
|
+
return work();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const ora = require('ora');
|
|
71
|
+
const label = buildCredentialPushSpinnerLabel(opts.systemKey);
|
|
72
|
+
const spinner = ora({
|
|
73
|
+
text: chalk.white(label),
|
|
74
|
+
spinner: 'dots'
|
|
75
|
+
}).start();
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
return await work();
|
|
79
|
+
} finally {
|
|
80
|
+
spinner.stop();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = {
|
|
85
|
+
shouldShowCredentialPushProgress,
|
|
86
|
+
shouldUseCredentialPushSpinner,
|
|
87
|
+
buildCredentialPushSpinnerLabel,
|
|
88
|
+
buildCredentialPushSpinnerText,
|
|
89
|
+
runWithCredentialPushSpinner
|
|
90
|
+
};
|