@aifabrix/builder 2.42.1 → 2.44.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/anchor-docs.mdc +15 -0
- package/README.md +2 -2
- package/anchor-docs/README.md +10 -0
- package/anchor-docs/_TEMPLATE +24 -0
- package/bin/aifabrix.js +13 -4
- package/integration/hubspot-test/README.md +157 -0
- package/integration/{hubspot → hubspot-test}/application.json +6 -6
- package/integration/{hubspot → hubspot-test}/create-hubspot.js +10 -10
- package/integration/hubspot-test/env.template +4 -0
- package/integration/hubspot-test/hubspot-test-datasource-company.json +138 -0
- package/integration/hubspot-test/hubspot-test-datasource-contact.json +146 -0
- package/integration/hubspot-test/hubspot-test-datasource-deal.json +146 -0
- package/integration/hubspot-test/hubspot-test-datasource-users.json +76 -0
- package/integration/{hubspot/hubspot-deploy.json → hubspot-test/hubspot-test-deploy.json} +201 -24
- package/integration/{hubspot/hubspot-system.json → hubspot-test/hubspot-test-system.json} +8 -7
- package/integration/hubspot-test/rbac.json +166 -0
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-credential-real.yaml +3 -3
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-env-vars.yaml +2 -2
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-add-datasource.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-create.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-select.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-known-platform.yaml +1 -1
- package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +2 -0
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-mode.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-file.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-url.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-source.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-array-test.yaml +1 -1
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +5 -0
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +5 -0
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-test.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-test.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-dataplane-down-tests.js +1 -7
- package/integration/{hubspot → hubspot-test}/test-dataplane-down.js +3 -3
- package/integration/{hubspot → hubspot-test}/test.js +137 -102
- package/integration/{hubspot → hubspot-test}/wizard-hubspot-e2e.yaml +2 -2
- package/integration/{hubspot → hubspot-test}/wizard-hubspot-platform.yaml +1 -1
- package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
- package/integration/roundtrip-test-local/README.md +144 -0
- package/integration/roundtrip-test-local/application.yaml +13 -0
- package/integration/roundtrip-test-local/env.template +15 -0
- package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
- package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
- package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
- package/integration/roundtrip-test-local2/README.md +144 -0
- package/integration/roundtrip-test-local2/application.yaml +13 -0
- package/integration/roundtrip-test-local2/env.template +15 -0
- package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
- package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
- package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
- package/integration/test/wizard.yaml +8 -0
- package/jest.config.default.js +10 -0
- package/jest.config.integration.fixtures.js +22 -0
- package/jest.config.integration.js +21 -18
- package/jest.config.isolated.js +10 -0
- package/jest.projects.js +288 -0
- package/lib/api/datasources-core.api.js +3 -3
- package/lib/api/dev-mtls-request.js +110 -0
- package/lib/api/dev-server-https.js +145 -0
- package/lib/api/dev.api.js +133 -144
- package/lib/api/index.js +0 -1
- package/lib/api/pipeline.api.js +67 -20
- package/lib/api/service-users.api.js +111 -2
- package/lib/api/types/dev.types.js +4 -3
- package/lib/api/types/pipeline.types.js +8 -5
- package/lib/api/types/service-users.types.js +41 -0
- package/lib/api/types/validation-run.types.js +56 -0
- package/lib/api/validation-run.api.js +99 -0
- package/lib/api/validation-runner.js +99 -0
- package/lib/app/config.js +1 -1
- package/lib/app/deploy-status-display.js +2 -2
- package/lib/app/deploy.js +7 -6
- package/lib/app/display.js +2 -1
- package/lib/app/dockerfile.js +3 -2
- package/lib/app/down.js +2 -1
- package/lib/app/helpers.js +6 -5
- package/lib/app/index.js +27 -8
- package/lib/app/list.js +7 -6
- package/lib/app/push.js +4 -3
- package/lib/app/register.js +19 -8
- package/lib/app/rotate-secret.js +17 -13
- package/lib/app/run-container-start.js +184 -0
- package/lib/app/run-docker-fallback.js +108 -0
- package/lib/app/run-env-compose.js +30 -42
- package/lib/app/run-helpers.js +49 -126
- package/lib/app/run-infra-requirements.js +30 -0
- package/lib/app/run-resolve-image.js +21 -0
- package/lib/app/run.js +74 -21
- package/lib/app/show-display.js +1 -1
- package/lib/app/show.js +1 -1
- package/lib/build/index.js +13 -10
- package/lib/cli/index.js +2 -0
- package/lib/cli/setup-app.help.js +67 -0
- package/lib/cli/setup-app.js +59 -123
- package/lib/cli/setup-app.test-commands.js +179 -0
- package/lib/cli/setup-auth.js +36 -14
- package/lib/cli/setup-credential-deployment.js +22 -8
- package/lib/cli/setup-dev-path-commands.js +124 -0
- package/lib/cli/setup-dev.js +190 -103
- package/lib/cli/setup-environment.js +11 -20
- package/lib/cli/setup-external-system.js +62 -22
- package/lib/cli/setup-infra.js +139 -47
- package/lib/cli/setup-parameters.js +32 -0
- package/lib/cli/setup-secrets.js +147 -10
- package/lib/cli/setup-service-user.js +146 -20
- package/lib/cli/setup-utility.js +47 -19
- package/lib/commands/app-down.js +5 -7
- package/lib/commands/app-install.js +14 -7
- package/lib/commands/app-logs.js +13 -10
- package/lib/commands/app-shell.js +4 -1
- package/lib/commands/app-test.js +25 -19
- package/lib/commands/app.js +22 -10
- package/lib/commands/auth-config.js +10 -14
- package/lib/commands/auth-status.js +4 -3
- package/lib/commands/credential-env.js +4 -3
- package/lib/commands/credential-list.js +5 -4
- package/lib/commands/credential-push.js +4 -3
- package/lib/commands/datasource-unified-test-cli.js +495 -0
- package/lib/commands/datasource-unified-test-cli.options.js +149 -0
- package/lib/commands/datasource-validation-cli.js +129 -0
- package/lib/commands/datasource.js +123 -71
- package/lib/commands/deployment-list.js +6 -5
- package/lib/commands/dev-cli-handlers.js +122 -18
- package/lib/commands/dev-down.js +4 -3
- package/lib/commands/dev-init.js +231 -116
- package/lib/commands/dev-show-display.js +473 -0
- package/lib/commands/login-credentials.js +3 -2
- package/lib/commands/login-device.js +4 -3
- package/lib/commands/login.js +5 -4
- package/lib/commands/logout.js +8 -7
- package/lib/commands/parameters-validate.js +54 -0
- package/lib/commands/repair-datasource.js +314 -68
- package/lib/commands/repair-env-template.js +16 -10
- package/lib/commands/repair-rbac.js +25 -19
- package/lib/commands/repair.js +116 -32
- package/lib/commands/secrets-list.js +23 -12
- package/lib/commands/secrets-remove-all.js +220 -0
- package/lib/commands/secrets-remove.js +22 -13
- package/lib/commands/secrets-set.js +21 -12
- package/lib/commands/secrets-validate.js +20 -7
- package/lib/commands/secure.js +10 -9
- package/lib/commands/service-user.js +243 -13
- package/lib/commands/test-e2e-external.js +27 -1
- package/lib/commands/up-common.js +28 -2
- package/lib/commands/up-dataplane.js +31 -18
- package/lib/commands/up-miso.js +19 -29
- package/lib/commands/upload.js +138 -39
- package/lib/commands/wizard-core-helpers.js +1 -1
- package/lib/commands/wizard-dataplane.js +4 -3
- package/lib/commands/wizard-helpers.js +3 -3
- package/lib/commands/wizard.js +2 -2
- package/lib/core/admin-secrets.js +16 -5
- package/lib/core/audit-logger.js +12 -4
- package/lib/core/config-attach-extensions.js +46 -0
- package/lib/core/config-runtime-paths.js +29 -0
- package/lib/core/config.js +59 -58
- package/lib/core/diff.js +3 -2
- package/lib/core/ensure-encryption-key.js +2 -4
- package/lib/core/secrets-ensure-infra.js +77 -0
- package/lib/core/secrets-ensure.js +120 -64
- package/lib/core/secrets-env-write.js +35 -7
- package/lib/core/secrets-infra-placeholder-sync.js +61 -0
- package/lib/core/secrets.js +228 -42
- package/lib/core/templates-env.js +4 -3
- package/lib/core/templates.js +1 -1
- package/lib/datasource/abac-validator.js +148 -0
- package/lib/datasource/deploy.js +75 -53
- package/lib/datasource/field-reference-validator.js +77 -36
- package/lib/datasource/integration-context.js +63 -0
- package/lib/datasource/list.js +8 -7
- package/lib/datasource/log-viewer.js +252 -0
- package/lib/datasource/resolve-app.js +109 -0
- package/lib/datasource/test-e2e.js +95 -155
- package/lib/datasource/test-integration.js +121 -109
- package/lib/datasource/unified-validation-run-body.js +65 -0
- package/lib/datasource/unified-validation-run-post.js +23 -0
- package/lib/datasource/unified-validation-run-resolve.js +43 -0
- package/lib/datasource/unified-validation-run.js +92 -0
- package/lib/datasource/validate.js +162 -15
- package/lib/deployment/deployer.js +4 -3
- package/lib/deployment/environment.js +7 -6
- package/lib/deployment/push.js +17 -8
- package/lib/external-system/delete.js +4 -3
- package/lib/external-system/deploy.js +131 -53
- package/lib/external-system/download-helpers.js +1 -1
- package/lib/external-system/download.js +7 -6
- package/lib/external-system/generator.js +104 -14
- package/lib/external-system/integration-test-dispatch.js +26 -0
- package/lib/external-system/test-execution.js +5 -1
- package/lib/external-system/test-helpers.js +0 -4
- package/lib/external-system/test-system-level-helpers.js +110 -0
- package/lib/external-system/test-system-level.js +83 -44
- package/lib/external-system/test.js +59 -8
- package/lib/generator/builders.js +23 -11
- package/lib/generator/deploy-manifest-azure-kv.js +81 -0
- package/lib/generator/external-controller-manifest.js +3 -3
- package/lib/generator/external.js +23 -11
- package/lib/generator/helpers.js +71 -12
- package/lib/generator/index.js +8 -4
- package/lib/generator/split-readme.js +12 -7
- package/lib/generator/split-variables.js +2 -1
- package/lib/generator/split.js +46 -11
- package/lib/generator/wizard-readme.js +3 -3
- package/lib/generator/wizard.js +16 -13
- package/lib/infrastructure/compose.js +60 -6
- package/lib/infrastructure/helpers.js +238 -51
- package/lib/infrastructure/index.js +64 -37
- package/lib/infrastructure/services.js +21 -15
- package/lib/internal/fs-real-sync.js +104 -0
- package/lib/internal/node-fs.js +98 -0
- package/lib/parameters/database-secret-values.js +173 -0
- package/lib/parameters/infra-kv-discovery.js +121 -0
- package/lib/parameters/infra-parameter-catalog.js +458 -0
- package/lib/parameters/infra-parameter-validate.js +64 -0
- package/lib/schema/application-schema.json +37 -17
- package/lib/schema/datasource-test-run.schema.json +493 -0
- package/lib/schema/deployment-rules.yaml +102 -63
- package/lib/schema/external-datasource.schema.json +1201 -433
- package/lib/schema/external-system.schema.json +181 -5
- package/lib/schema/flag-map-validation-run.json +31 -0
- package/lib/schema/infra-parameter.schema.json +106 -0
- package/lib/schema/infra.parameter.yaml +421 -0
- package/lib/schema/type/credential-auth-templates.json +40 -0
- package/lib/schema/type/document-storage.json +213 -0
- package/lib/schema/type/message-service.json +123 -0
- package/lib/schema/type/vector-store.json +88 -0
- package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
- package/lib/utils/api-error-handler.js +2 -2
- package/lib/utils/api.js +49 -14
- package/lib/utils/app-config-resolver.js +23 -1
- package/lib/utils/app-register-api.js +3 -2
- package/lib/utils/app-register-auth.js +1 -1
- package/lib/utils/app-register-config.js +4 -4
- package/lib/utils/app-register-display.js +3 -2
- package/lib/utils/app-register-validator.js +3 -2
- package/lib/utils/app-run-containers.js +26 -22
- package/lib/utils/app-scoped-config.js +31 -0
- package/lib/utils/app-service-env-from-builder.js +164 -0
- package/lib/utils/build-copy.js +1 -1
- package/lib/utils/build-helpers.js +20 -20
- package/lib/utils/build-resolve-image.js +165 -0
- package/lib/utils/cli-layout-chalk.js +8 -0
- package/lib/utils/cli-test-layout-chalk.js +267 -0
- package/lib/utils/cli-utils.js +88 -11
- package/lib/utils/compose-db-passwords.js +138 -0
- package/lib/utils/compose-generate-docker-compose.js +216 -0
- package/lib/utils/compose-generator.js +197 -291
- package/lib/utils/compose-miso-env.js +18 -0
- package/lib/utils/compose-traefik-ingress-base.js +158 -0
- package/lib/utils/config-paths.js +209 -6
- package/lib/utils/config-scoped-resources-preference.js +41 -0
- package/lib/utils/controller-deployment-outcome.js +68 -0
- package/lib/utils/credential-display.js +2 -2
- package/lib/utils/credential-secrets-env.js +16 -1
- package/lib/utils/dataplane-pipeline-warning.js +4 -3
- package/lib/utils/datasource-test-run-capability-scope.js +43 -0
- package/lib/utils/datasource-test-run-debug-display.js +137 -0
- package/lib/utils/datasource-test-run-debug-slice.js +93 -0
- package/lib/utils/datasource-test-run-display.js +442 -0
- package/lib/utils/datasource-test-run-exit.js +58 -0
- package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
- package/lib/utils/datasource-test-run-report-version.js +51 -0
- package/lib/utils/datasource-test-run-schema-sync.js +59 -0
- package/lib/utils/datasource-test-run-tty-log.js +81 -0
- package/lib/utils/datasource-validation-watch.js +266 -0
- package/lib/utils/declarative-url-ports.js +47 -0
- package/lib/utils/derive-env-key-from-client-id.js +41 -0
- package/lib/utils/dev-ca-install.js +185 -23
- package/lib/utils/dev-cert-helper.js +266 -17
- package/lib/utils/dev-hosts-helper.js +307 -0
- package/lib/utils/dev-init-cert-hints.js +37 -0
- package/lib/utils/dev-init-health-messages.js +52 -0
- package/lib/utils/dev-init-resolve.js +86 -0
- package/lib/utils/dev-init-ssh-merge.js +65 -0
- package/lib/utils/dev-ssh-config-helper.js +196 -0
- package/lib/utils/dev-user-groups.js +93 -0
- package/lib/utils/docker-build.js +42 -17
- package/lib/utils/docker-exec.js +28 -0
- package/lib/utils/docker-manifest-public-port.js +116 -0
- package/lib/utils/docker-not-running-hint.js +52 -0
- package/lib/utils/docker.js +98 -11
- package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
- package/lib/utils/env-config-loader.js +10 -91
- package/lib/utils/env-copy.js +19 -10
- package/lib/utils/env-map.js +42 -11
- package/lib/utils/env-template.js +2 -2
- package/lib/utils/environment-scoped-resources.js +144 -0
- package/lib/utils/error-formatter.js +125 -9
- package/lib/utils/error-formatters/http-status-errors.js +6 -5
- package/lib/utils/error-formatters/network-errors.js +2 -1
- package/lib/utils/error-formatters/permission-errors.js +2 -1
- package/lib/utils/error-formatters/validation-errors.js +2 -1
- package/lib/utils/external-env-template.js +180 -0
- package/lib/utils/external-readme.js +8 -1
- package/lib/utils/external-system-display.js +277 -136
- package/lib/utils/external-system-local-test-tty.js +389 -0
- package/lib/utils/external-system-readiness-core.js +377 -0
- package/lib/utils/external-system-readiness-deploy-display.js +270 -0
- package/lib/utils/external-system-readiness-display-internals.js +150 -0
- package/lib/utils/external-system-readiness-display.js +186 -0
- package/lib/utils/external-system-test-helpers.js +24 -6
- package/lib/utils/external-system-validators.js +32 -14
- package/lib/utils/health-check-url.js +119 -0
- package/lib/utils/health-check.js +59 -25
- package/lib/utils/help-builder.js +14 -13
- package/lib/utils/image-version.js +4 -8
- package/lib/utils/infra-containers.js +4 -7
- package/lib/utils/infra-env-defaults.js +162 -0
- package/lib/utils/infra-status-display.js +167 -0
- package/lib/utils/infra-status.js +16 -8
- package/lib/utils/local-secrets.js +29 -7
- package/lib/utils/paths.js +136 -48
- package/lib/utils/port-resolver.js +10 -23
- package/lib/utils/redis-env-scope.js +62 -0
- package/lib/utils/register-aifabrix-shell-env.js +204 -0
- package/lib/utils/remote-builder-validation.js +99 -0
- package/lib/utils/remote-dev-auth.js +117 -21
- package/lib/utils/remote-docker-env.js +67 -15
- package/lib/utils/remote-secrets-loader.js +13 -4
- package/lib/utils/resolve-docker-image-ref.js +124 -0
- package/lib/utils/schema-loader.js +22 -9
- package/lib/utils/secrets-bash-kv.js +25 -0
- package/lib/utils/secrets-generator.js +171 -51
- package/lib/utils/secrets-helpers.js +70 -59
- package/lib/utils/secrets-kv-scope.js +60 -0
- package/lib/utils/secrets-utils.js +35 -37
- package/lib/utils/secrets-validation.js +3 -1
- package/lib/utils/secrets-yaml-preserve.js +109 -0
- package/lib/utils/secure-file-permissions.js +91 -0
- package/lib/utils/ssh-key-helper.js +4 -2
- package/lib/utils/template-helpers.js +2 -2
- package/lib/utils/test-log-writer.js +3 -3
- package/lib/utils/token-manager.js +37 -5
- package/lib/utils/url-declarative-public-base.js +188 -0
- package/lib/utils/url-declarative-resolve-build.js +493 -0
- package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
- package/lib/utils/url-declarative-resolve.js +220 -0
- package/lib/utils/url-declarative-token-parse.js +74 -0
- package/lib/utils/url-declarative-url-flags.js +50 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
- package/lib/utils/url-public-path-prefix.js +34 -0
- package/lib/utils/urls-local-registry.js +220 -0
- package/lib/utils/validation-report-tty-kit.js +77 -0
- package/lib/utils/validation-run-poll.js +89 -0
- package/lib/utils/validation-run-post-retry.js +73 -0
- package/lib/utils/validation-run-request.js +98 -0
- package/lib/utils/variable-transformer.js +21 -4
- package/lib/utils/yaml-preserve.js +78 -1
- package/lib/validation/datasource-warnings.js +56 -0
- package/lib/validation/env-template-auth.js +50 -2
- package/lib/validation/external-manifest-validator.js +35 -7
- package/lib/validation/validate-display.js +37 -31
- package/lib/validation/validate.js +9 -10
- package/lib/validation/validator-unresolved-placeholders.js +98 -0
- package/lib/validation/validator.js +32 -78
- package/lib/validation/wizard-config-validator.js +2 -1
- package/package.json +11 -3
- package/scripts/check-datasource-test-run-schema-sync.js +34 -0
- package/scripts/diagnose-cli.js +150 -0
- package/scripts/install-local.js +304 -55
- package/templates/README.md +15 -2
- package/templates/applications/dataplane/application.yaml +52 -2
- package/templates/applications/dataplane/env.template +80 -18
- package/templates/applications/dataplane/rbac.yaml +8 -0
- package/templates/applications/keycloak/application.yaml +9 -1
- package/templates/applications/keycloak/env.template +15 -6
- package/templates/applications/miso-controller/application.yaml +10 -2
- package/templates/applications/miso-controller/env.template +55 -14
- package/templates/applications/miso-controller/rbac.yaml +5 -0
- package/templates/external-system/README.md.hbs +20 -7
- package/templates/external-system/deploy.js.hbs +5 -5
- package/templates/external-system/env.template.hbs +22 -0
- package/templates/external-system/external-datasource.yaml.hbs +197 -118
- package/templates/infra/compose.yaml.hbs +20 -4
- package/templates/python/docker-compose.hbs +16 -0
- package/templates/typescript/docker-compose.hbs +16 -0
- package/integration/hubspot/README.md +0 -102
- package/integration/hubspot/env.template +0 -4
- package/integration/hubspot/hubspot-datasource-company.json +0 -541
- package/integration/hubspot/hubspot-datasource-contact.json +0 -639
- package/integration/hubspot/hubspot-datasource-deal.json +0 -588
- package/integration/hubspot/hubspot-datasource-users.json +0 -116
- package/integration/hubspot/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
- package/lib/api/external-test.api.js +0 -111
- package/lib/schema/env-config.yaml +0 -43
- /package/integration/{hubspot → hubspot-test}/companies.json +0 -0
- /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-app-name.yaml +0 -0
- /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-missing-app.yaml +0 -0
- /package/integration/{hubspot → hubspot-test}/test-dataplane-down-helpers.js +0 -0
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
* @version 2.0.0
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
12
13
|
const path = require('path');
|
|
13
|
-
const chalk = require('chalk');
|
|
14
14
|
const logger = require('../utils/logger');
|
|
15
15
|
const { getAifabrixSecretsPath } = require('../core/config');
|
|
16
16
|
const { saveLocalSecret, saveSecret } = require('../utils/local-secrets');
|
|
17
17
|
const pathsUtil = require('../utils/paths');
|
|
18
|
-
const
|
|
18
|
+
const remoteDevAuth = require('../utils/remote-dev-auth');
|
|
19
19
|
const devApi = require('../api/dev.api');
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -39,20 +39,29 @@ const devApi = require('../api/dev.api');
|
|
|
39
39
|
* @returns {Promise<void>}
|
|
40
40
|
*/
|
|
41
41
|
async function setSharedSecret(key, value, generalSecretsPath) {
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
const target = await remoteDevAuth.resolveSharedSecretsEndpoint(generalSecretsPath);
|
|
43
|
+
if (remoteDevAuth.isRemoteSecretsUrl(target)) {
|
|
44
|
+
const auth = await remoteDevAuth.getRemoteDevAuth();
|
|
44
45
|
if (!auth) {
|
|
45
46
|
throw new Error('Remote server not configured or certificate missing. Run "aifabrix dev init" first.');
|
|
46
47
|
}
|
|
47
|
-
await devApi.addSecret(
|
|
48
|
-
|
|
48
|
+
await devApi.addSecret(
|
|
49
|
+
auth.serverUrl,
|
|
50
|
+
auth.clientCertPem,
|
|
51
|
+
{ key, value },
|
|
52
|
+
auth.serverCaPem || undefined,
|
|
53
|
+
target
|
|
54
|
+
);
|
|
55
|
+
const host = remoteDevAuth.getSharedSecretsRemoteHostname(target);
|
|
56
|
+
const where = host ? `shared secrets (remote - ${host})` : 'shared secrets (remote)';
|
|
57
|
+
logger.log(formatSuccessLine(`Secret '${key}' saved to ${where}.`));
|
|
49
58
|
return;
|
|
50
59
|
}
|
|
51
|
-
const resolvedPath = path.isAbsolute(
|
|
52
|
-
?
|
|
53
|
-
: path.resolve(process.cwd(),
|
|
60
|
+
const resolvedPath = path.isAbsolute(target)
|
|
61
|
+
? target
|
|
62
|
+
: path.resolve(process.cwd(), target);
|
|
54
63
|
await saveSecret(key, value, resolvedPath);
|
|
55
|
-
logger.log(
|
|
64
|
+
logger.log(formatSuccessLine(`Secret '${key}' saved to general secrets file: ${resolvedPath}`));
|
|
56
65
|
}
|
|
57
66
|
|
|
58
67
|
async function handleSecretsSet(key, value, options) {
|
|
@@ -80,8 +89,8 @@ async function handleSecretsSet(key, value, options) {
|
|
|
80
89
|
await setSharedSecret(key, value, generalSecretsPath);
|
|
81
90
|
} else {
|
|
82
91
|
await saveLocalSecret(key, value);
|
|
83
|
-
const userSecretsPath =
|
|
84
|
-
logger.log(
|
|
92
|
+
const userSecretsPath = pathsUtil.getPrimaryUserSecretsLocalPath();
|
|
93
|
+
logger.log(formatSuccessLine(`Secret '${key}' saved to user secrets file: ${userSecretsPath}`));
|
|
85
94
|
}
|
|
86
95
|
}
|
|
87
96
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { formatBlockingError, formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
1
2
|
/**
|
|
2
3
|
* AI Fabrix Builder – Secrets validate command
|
|
3
4
|
*
|
|
@@ -9,9 +10,9 @@
|
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
const chalk = require('chalk');
|
|
12
|
-
const path = require('path');
|
|
13
13
|
const logger = require('../utils/logger');
|
|
14
14
|
const { validateSecretsFile } = require('../utils/secrets-validation');
|
|
15
|
+
const { validateDataplaneSecrets } = require('../utils/token-manager');
|
|
15
16
|
const pathsUtil = require('../utils/paths');
|
|
16
17
|
const secretsEnsure = require('../core/secrets-ensure');
|
|
17
18
|
|
|
@@ -33,18 +34,30 @@ async function handleSecretsValidate(pathArg, options = {}) {
|
|
|
33
34
|
if (target.type === 'file' && target.filePath) {
|
|
34
35
|
filePath = target.filePath;
|
|
35
36
|
} else {
|
|
36
|
-
filePath =
|
|
37
|
+
filePath = pathsUtil.getPrimaryUserSecretsLocalPath();
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
const result = validateSecretsFile(filePath, { checkNaming: Boolean(options.naming) });
|
|
42
|
+
const dataplaneResult = validateDataplaneSecrets(filePath);
|
|
43
|
+
const allValid = result.valid && dataplaneResult.valid;
|
|
41
44
|
if (result.valid) {
|
|
42
|
-
logger.log(
|
|
43
|
-
|
|
45
|
+
logger.log(formatSuccessLine(`Secrets file is valid: ${result.path}`));
|
|
46
|
+
} else {
|
|
47
|
+
logger.log(formatBlockingError(`Validation failed: ${result.path}`));
|
|
48
|
+
result.errors.forEach((err) => logger.log(chalk.yellow(` • ${err}`)));
|
|
44
49
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
if (!dataplaneResult.valid) {
|
|
51
|
+
logger.log(chalk.yellow(`⚠ ${dataplaneResult.hint}`));
|
|
52
|
+
if (result.valid) {
|
|
53
|
+
logger.log(chalk.yellow(' Wizard/dataplane calls may fail until dataplane credentials are present.'));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
valid: allValid,
|
|
58
|
+
errors: allValid ? [] : [...result.errors, ...(dataplaneResult.valid ? [] : [dataplaneResult.hint])],
|
|
59
|
+
dataplaneValid: dataplaneResult.valid
|
|
60
|
+
};
|
|
48
61
|
}
|
|
49
62
|
|
|
50
63
|
module.exports = { handleSecretsValidate };
|
package/lib/commands/secure.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
|
|
1
2
|
/**
|
|
2
3
|
* AI Fabrix Builder - Secure Command
|
|
3
4
|
*
|
|
@@ -32,7 +33,7 @@ async function findSecretsFiles() {
|
|
|
32
33
|
const files = [];
|
|
33
34
|
|
|
34
35
|
// User's secrets file
|
|
35
|
-
const userSecretsPath =
|
|
36
|
+
const userSecretsPath = pathsUtil.getPrimaryUserSecretsLocalPath();
|
|
36
37
|
if (fs.existsSync(userSecretsPath)) {
|
|
37
38
|
files.push({ path: userSecretsPath, type: 'user' });
|
|
38
39
|
}
|
|
@@ -131,7 +132,7 @@ async function getEncryptionKey(options) {
|
|
|
131
132
|
// Check if key already exists in config
|
|
132
133
|
const existingKey = await getSecretsEncryptionKey();
|
|
133
134
|
if (existingKey) {
|
|
134
|
-
logger.log(chalk.yellow('
|
|
135
|
+
logger.log(chalk.yellow('⚠ Encryption key already configured in config.yaml'));
|
|
135
136
|
const useExisting = await inquirer.prompt([{
|
|
136
137
|
type: 'confirm',
|
|
137
138
|
name: 'use',
|
|
@@ -143,18 +144,18 @@ async function getEncryptionKey(options) {
|
|
|
143
144
|
} else {
|
|
144
145
|
encryptionKey = await promptForEncryptionKey();
|
|
145
146
|
await setSecretsEncryptionKey(encryptionKey);
|
|
146
|
-
logger.log(
|
|
147
|
+
logger.log(formatSuccessLine('Encryption key saved to config.yaml'));
|
|
147
148
|
}
|
|
148
149
|
} else {
|
|
149
150
|
encryptionKey = await promptForEncryptionKey();
|
|
150
151
|
await setSecretsEncryptionKey(encryptionKey);
|
|
151
|
-
logger.log(
|
|
152
|
+
logger.log(formatSuccessLine('Encryption key saved to config.yaml'));
|
|
152
153
|
}
|
|
153
154
|
} else {
|
|
154
155
|
// Validate and save the provided key
|
|
155
156
|
validateEncryptionKey(encryptionKey);
|
|
156
157
|
await setSecretsEncryptionKey(encryptionKey);
|
|
157
|
-
logger.log(
|
|
158
|
+
logger.log(formatSuccessLine('Encryption key saved to config.yaml'));
|
|
158
159
|
}
|
|
159
160
|
return encryptionKey;
|
|
160
161
|
}
|
|
@@ -178,12 +179,12 @@ async function processSecretsFiles(secretsFiles, encryptionKey) {
|
|
|
178
179
|
totalValues += result.total;
|
|
179
180
|
|
|
180
181
|
if (result.encrypted > 0) {
|
|
181
|
-
logger.log(chalk.green(`
|
|
182
|
+
logger.log(chalk.green(` ✔ Encrypted ${result.encrypted} of ${result.total} values`));
|
|
182
183
|
} else {
|
|
183
184
|
logger.log(chalk.gray(` - All values already encrypted (${result.total} total)`));
|
|
184
185
|
}
|
|
185
186
|
} catch (error) {
|
|
186
|
-
logger.log(chalk.red(`
|
|
187
|
+
logger.log(chalk.red(` ✖ Error: ${error.message}`));
|
|
187
188
|
}
|
|
188
189
|
}
|
|
189
190
|
|
|
@@ -197,7 +198,7 @@ async function processSecretsFiles(secretsFiles, encryptionKey) {
|
|
|
197
198
|
* @param {number} totalValues - Total number of values
|
|
198
199
|
*/
|
|
199
200
|
function displayEncryptionSummary(filesCount, totalEncrypted, totalValues) {
|
|
200
|
-
logger.log(
|
|
201
|
+
logger.log(formatSuccessParagraph('Encryption complete!'));
|
|
201
202
|
logger.log(chalk.gray(` Files processed: ${filesCount}`));
|
|
202
203
|
logger.log(chalk.gray(` Values encrypted: ${totalEncrypted} of ${totalValues} total`));
|
|
203
204
|
logger.log(chalk.gray(' Encryption key stored in: ~/.aifabrix/config.yaml\n'));
|
|
@@ -224,7 +225,7 @@ async function handleSecure(options) {
|
|
|
224
225
|
const secretsFiles = await findSecretsFiles();
|
|
225
226
|
|
|
226
227
|
if (secretsFiles.length === 0) {
|
|
227
|
-
logger.log(chalk.yellow('
|
|
228
|
+
logger.log(chalk.yellow('⚠ No secrets files found to encrypt'));
|
|
228
229
|
logger.log(chalk.gray(' Create ~/.aifabrix/secrets.local.yaml or configure aifabrix-secrets in config.yaml'));
|
|
229
230
|
return;
|
|
230
231
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { formatBlockingError, formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
1
2
|
/**
|
|
2
3
|
* Service user create command – create service user and get one-time secret
|
|
3
4
|
* POST /api/v1/service-users. Used by `aifabrix service-user create`.
|
|
@@ -12,7 +13,14 @@ const logger = require('../utils/logger');
|
|
|
12
13
|
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
13
14
|
const { getOrRefreshDeviceToken } = require('../utils/token-manager');
|
|
14
15
|
const { normalizeControllerUrl } = require('../core/config');
|
|
15
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
createServiceUser,
|
|
18
|
+
listServiceUsers,
|
|
19
|
+
regenerateSecretServiceUser,
|
|
20
|
+
deleteServiceUser,
|
|
21
|
+
updateGroupsServiceUser,
|
|
22
|
+
updateRedirectUrisServiceUser
|
|
23
|
+
} = require('../api/service-users.api');
|
|
16
24
|
|
|
17
25
|
const ONE_TIME_WARNING =
|
|
18
26
|
'Save this secret now; it will not be shown again.';
|
|
@@ -54,6 +62,12 @@ function extractCreateResponse(response) {
|
|
|
54
62
|
return { clientId, clientSecret };
|
|
55
63
|
}
|
|
56
64
|
|
|
65
|
+
const ID_WIDTH = 38;
|
|
66
|
+
const USERNAME_WIDTH = 22;
|
|
67
|
+
const EMAIL_WIDTH = 28;
|
|
68
|
+
const CLIENT_ID_WIDTH = 24;
|
|
69
|
+
const TABLE_SEPARATOR_LENGTH = 130;
|
|
70
|
+
|
|
57
71
|
/**
|
|
58
72
|
* Log error for failed create response and exit
|
|
59
73
|
* @param {Object} response - API response with success: false
|
|
@@ -62,25 +76,105 @@ function handleCreateError(response) {
|
|
|
62
76
|
const status = response.status;
|
|
63
77
|
const msg = response.formattedError || response.error || 'Request failed';
|
|
64
78
|
if (status === 400) {
|
|
65
|
-
logger.error(
|
|
79
|
+
logger.error(formatBlockingError(`Validation error: ${msg}`));
|
|
66
80
|
} else if (status === 401) {
|
|
67
|
-
logger.error(
|
|
81
|
+
logger.error(formatBlockingError('Unauthorized. Run "aifabrix login" and try again.'));
|
|
68
82
|
} else if (status === 403) {
|
|
69
|
-
logger.error(
|
|
83
|
+
logger.error(formatBlockingError('Missing permission: service-user:create'));
|
|
70
84
|
logger.error(chalk.gray('Your account needs the service-user:create permission on the controller.'));
|
|
71
85
|
} else {
|
|
72
|
-
logger.error(
|
|
86
|
+
logger.error(formatBlockingError(`Failed to create service user: ${msg}`));
|
|
73
87
|
}
|
|
74
88
|
process.exit(1);
|
|
75
89
|
}
|
|
76
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Log error for service-user API response and exit
|
|
93
|
+
* @param {Object} response - API response with success: false
|
|
94
|
+
* @param {string} permissionScope - Permission hint: 'read' | 'update' | 'delete'
|
|
95
|
+
*/
|
|
96
|
+
function handleServiceUserApiError(response, permissionScope) {
|
|
97
|
+
const status = response.status;
|
|
98
|
+
const msg = response.formattedError || response.error || 'Request failed';
|
|
99
|
+
if (status === 400) {
|
|
100
|
+
logger.error(formatBlockingError(`Validation error: ${msg}`));
|
|
101
|
+
} else if (status === 401) {
|
|
102
|
+
logger.error(formatBlockingError('Unauthorized. Run "aifabrix login" and try again.'));
|
|
103
|
+
} else if (status === 403) {
|
|
104
|
+
logger.error(formatBlockingError(`Missing permission: service-user:${permissionScope}`));
|
|
105
|
+
logger.error(chalk.gray(`Your account needs the service-user:${permissionScope} permission on the controller.`));
|
|
106
|
+
} else if (status === 404) {
|
|
107
|
+
logger.error(formatBlockingError('Service user not found.'));
|
|
108
|
+
const detail = response.error || '';
|
|
109
|
+
if (detail) {
|
|
110
|
+
logger.error(chalk.gray(detail));
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
logger.error(formatBlockingError(`Request failed: ${msg}`));
|
|
114
|
+
}
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Resolve controller URL and auth for list/rotate/delete/update (no create-specific validation)
|
|
120
|
+
* @async
|
|
121
|
+
* @param {Object} options - CLI options (controller optional)
|
|
122
|
+
* @returns {Promise<{ controllerUrl: string, authConfig: Object }>}
|
|
123
|
+
*/
|
|
124
|
+
async function resolveControllerAndAuth(options) {
|
|
125
|
+
const controllerUrl = options.controller || (await resolveControllerUrl());
|
|
126
|
+
if (!controllerUrl) {
|
|
127
|
+
logger.error(formatBlockingError('Controller URL is required. Run "aifabrix login" first.'));
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
const authResult = await getServiceUserAuth(controllerUrl);
|
|
131
|
+
if (!authResult || !authResult.token) {
|
|
132
|
+
logger.error(formatBlockingError(`No authentication token for controller: ${controllerUrl}`));
|
|
133
|
+
logger.error(chalk.gray('Run: aifabrix login'));
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
controllerUrl: authResult.controllerUrl,
|
|
138
|
+
authConfig: { type: 'bearer', token: authResult.token }
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Display service user list as a table (id, username, email, clientId, active).
|
|
144
|
+
* API shape: items have id, username, email, status, federatedIdentity.keycloakClientId.
|
|
145
|
+
* @param {Array<{ id?: string, username?: string, email?: string, status?: string, active?: boolean, clientId?: string, federatedIdentity?: { keycloakClientId?: string } }>} items - Service users
|
|
146
|
+
*/
|
|
147
|
+
function displayServiceUserList(items) {
|
|
148
|
+
logger.log(chalk.bold('\n📋 Service users:\n'));
|
|
149
|
+
if (!items || items.length === 0) {
|
|
150
|
+
logger.log(chalk.gray(' No service users found.\n'));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const idCol = 'Id'.padEnd(ID_WIDTH);
|
|
154
|
+
const usernameCol = 'Username'.padEnd(USERNAME_WIDTH);
|
|
155
|
+
const emailCol = 'Email'.padEnd(EMAIL_WIDTH);
|
|
156
|
+
const clientIdCol = 'ClientId'.padEnd(CLIENT_ID_WIDTH);
|
|
157
|
+
const activeCol = 'Active';
|
|
158
|
+
logger.log(chalk.gray(`${idCol}${usernameCol}${emailCol}${clientIdCol}${activeCol}`));
|
|
159
|
+
logger.log(chalk.gray('-'.repeat(TABLE_SEPARATOR_LENGTH)));
|
|
160
|
+
items.forEach((row) => {
|
|
161
|
+
const id = (row.id ?? '').toString().padEnd(ID_WIDTH);
|
|
162
|
+
const username = (row.username ?? '—').padEnd(USERNAME_WIDTH);
|
|
163
|
+
const email = (row.email ?? '—').padEnd(EMAIL_WIDTH);
|
|
164
|
+
const clientId = (row.federatedIdentity?.keycloakClientId ?? row.clientId ?? '—').padEnd(CLIENT_ID_WIDTH);
|
|
165
|
+
const active = row.status === 'active' ? 'yes' : (row.status ?? (row.active === true ? 'yes' : row.active === false ? 'no' : '—'));
|
|
166
|
+
logger.log(`${id}${username}${email}${clientId}${active}`);
|
|
167
|
+
});
|
|
168
|
+
logger.log('');
|
|
169
|
+
}
|
|
170
|
+
|
|
77
171
|
/**
|
|
78
172
|
* Display success output with clientId, clientSecret and one-time warning
|
|
79
173
|
* @param {string} clientId - Service user client ID
|
|
80
174
|
* @param {string} clientSecret - One-time client secret
|
|
81
175
|
*/
|
|
82
176
|
function displayCreateSuccess(clientId, clientSecret) {
|
|
83
|
-
logger.log(chalk.bold('\n
|
|
177
|
+
logger.log(chalk.bold('\n✔ Service user created\n'));
|
|
84
178
|
logger.log(chalk.cyan(' clientId: ') + clientId);
|
|
85
179
|
logger.log(chalk.cyan(' clientSecret: ') + clientSecret);
|
|
86
180
|
logger.log('');
|
|
@@ -114,19 +208,19 @@ function validateServiceUserOptions(options) {
|
|
|
114
208
|
const redirectUris = parseList(options.redirectUris);
|
|
115
209
|
const groupNames = parseList(options.groupNames);
|
|
116
210
|
if (!username) {
|
|
117
|
-
logger.error(
|
|
211
|
+
logger.error(formatBlockingError('Username is required. Use --username <username>.'));
|
|
118
212
|
process.exit(1);
|
|
119
213
|
}
|
|
120
214
|
if (!email) {
|
|
121
|
-
logger.error(
|
|
215
|
+
logger.error(formatBlockingError('Email is required. Use --email <email>.'));
|
|
122
216
|
process.exit(1);
|
|
123
217
|
}
|
|
124
218
|
if (redirectUris.length === 0) {
|
|
125
|
-
logger.error(
|
|
219
|
+
logger.error(formatBlockingError('At least one redirect URI is required. Use --redirect-uris <uri1,uri2,...>.'));
|
|
126
220
|
process.exit(1);
|
|
127
221
|
}
|
|
128
222
|
if (groupNames.length === 0) {
|
|
129
|
-
logger.error(
|
|
223
|
+
logger.error(formatBlockingError('At least one group name is required. Use --group-names <name1,name2,...>.'));
|
|
130
224
|
process.exit(1);
|
|
131
225
|
}
|
|
132
226
|
return {
|
|
@@ -148,12 +242,12 @@ async function resolveOptionsAndAuth(options) {
|
|
|
148
242
|
const validated = validateServiceUserOptions(options);
|
|
149
243
|
const controllerUrl = options.controller || (await resolveControllerUrl());
|
|
150
244
|
if (!controllerUrl) {
|
|
151
|
-
logger.error(
|
|
245
|
+
logger.error(formatBlockingError('Controller URL is required. Run "aifabrix login" first.'));
|
|
152
246
|
process.exit(1);
|
|
153
247
|
}
|
|
154
248
|
const authResult = await getServiceUserAuth(controllerUrl);
|
|
155
249
|
if (!authResult || !authResult.token) {
|
|
156
|
-
logger.error(
|
|
250
|
+
logger.error(formatBlockingError(`No authentication token for controller: ${controllerUrl}`));
|
|
157
251
|
logger.error(chalk.gray('Run: aifabrix login'));
|
|
158
252
|
process.exit(1);
|
|
159
253
|
}
|
|
@@ -194,6 +288,142 @@ async function runServiceUserCreate(options = {}) {
|
|
|
194
288
|
displayCreateSuccess(clientId, clientSecret);
|
|
195
289
|
}
|
|
196
290
|
|
|
291
|
+
/**
|
|
292
|
+
* Require service user id option; exit with message if missing
|
|
293
|
+
* @param {string} [id] - Service user ID from options
|
|
294
|
+
* @returns {string} Trimmed id
|
|
295
|
+
*/
|
|
296
|
+
function requireServiceUserId(id) {
|
|
297
|
+
const trimmed = (id && typeof id === 'string' ? id.trim() : '') || '';
|
|
298
|
+
if (!trimmed) {
|
|
299
|
+
logger.error(formatBlockingError('Service user ID is required. Use --id <uuid>.'));
|
|
300
|
+
process.exit(1);
|
|
301
|
+
}
|
|
302
|
+
return trimmed;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Run service-user list: call GET /api/v1/service-users and display table
|
|
307
|
+
* @async
|
|
308
|
+
* @param {Object} options - CLI options (controller, page, pageSize, sort, filter, search)
|
|
309
|
+
* @returns {Promise<void>}
|
|
310
|
+
*/
|
|
311
|
+
async function runServiceUserList(options = {}) {
|
|
312
|
+
const { controllerUrl, authConfig } = await resolveControllerAndAuth(options);
|
|
313
|
+
const listOptions = {
|
|
314
|
+
page: options.page,
|
|
315
|
+
pageSize: options.pageSize,
|
|
316
|
+
sort: options.sort,
|
|
317
|
+
filter: options.filter,
|
|
318
|
+
search: options.search
|
|
319
|
+
};
|
|
320
|
+
const response = await listServiceUsers(controllerUrl, authConfig, listOptions);
|
|
321
|
+
if (response && response.success === false) {
|
|
322
|
+
handleServiceUserApiError(response, 'read');
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const body = response?.data?.data ?? response?.data ?? response ?? {};
|
|
326
|
+
const items = Array.isArray(body) ? body : (body.data ?? []);
|
|
327
|
+
displayServiceUserList(items);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Run service-user rotate-secret: call POST .../regenerate-secret and print new secret once with warning
|
|
332
|
+
* @async
|
|
333
|
+
* @param {Object} options - CLI options (controller, id required)
|
|
334
|
+
* @returns {Promise<void>}
|
|
335
|
+
*/
|
|
336
|
+
async function runServiceUserRotateSecret(options = {}) {
|
|
337
|
+
const id = requireServiceUserId(options.id);
|
|
338
|
+
const { controllerUrl, authConfig } = await resolveControllerAndAuth(options);
|
|
339
|
+
const response = await regenerateSecretServiceUser(controllerUrl, authConfig, id);
|
|
340
|
+
if (response && response.success === false) {
|
|
341
|
+
handleServiceUserApiError(response, 'update');
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const payload = response?.data?.data ?? response?.data ?? response ?? {};
|
|
345
|
+
const clientSecret = payload?.clientSecret ?? '';
|
|
346
|
+
if (response && response.success === true) {
|
|
347
|
+
logger.log(chalk.bold('\n✔ Secret rotated\n'));
|
|
348
|
+
logger.log(chalk.cyan(' clientSecret: ') + clientSecret);
|
|
349
|
+
logger.log('');
|
|
350
|
+
logger.log(chalk.yellow('⚠ ' + ONE_TIME_WARNING));
|
|
351
|
+
logger.log('');
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Run service-user delete: call DELETE .../service-users/{id} (deactivates the user)
|
|
357
|
+
* @async
|
|
358
|
+
* @param {Object} options - CLI options (controller, id required)
|
|
359
|
+
* @returns {Promise<void>}
|
|
360
|
+
*/
|
|
361
|
+
async function runServiceUserDelete(options = {}) {
|
|
362
|
+
const id = requireServiceUserId(options.id);
|
|
363
|
+
const { controllerUrl, authConfig } = await resolveControllerAndAuth(options);
|
|
364
|
+
const response = await deleteServiceUser(controllerUrl, authConfig, id);
|
|
365
|
+
if (response && response.success === false) {
|
|
366
|
+
handleServiceUserApiError(response, 'delete');
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (response && response.success === true) {
|
|
370
|
+
logger.log(formatSuccessLine('Service user deactivated.\n'));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Run service-user update-groups: call PUT .../groups with groupNames
|
|
376
|
+
* @async
|
|
377
|
+
* @param {Object} options - CLI options (controller, id, groupNames required)
|
|
378
|
+
* @returns {Promise<void>}
|
|
379
|
+
*/
|
|
380
|
+
async function runServiceUserUpdateGroups(options = {}) {
|
|
381
|
+
const id = requireServiceUserId(options.id);
|
|
382
|
+
const groupNames = parseList(options.groupNames);
|
|
383
|
+
if (groupNames.length === 0) {
|
|
384
|
+
logger.error(formatBlockingError('At least one group name is required. Use --group-names <name1,name2,...>.'));
|
|
385
|
+
process.exit(1);
|
|
386
|
+
}
|
|
387
|
+
const { controllerUrl, authConfig } = await resolveControllerAndAuth(options);
|
|
388
|
+
const response = await updateGroupsServiceUser(controllerUrl, authConfig, id, { groupNames });
|
|
389
|
+
if (response && response.success === false) {
|
|
390
|
+
handleServiceUserApiError(response, 'update');
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (response && response.success === true) {
|
|
394
|
+
logger.log(formatSuccessLine('Service user groups updated.\n'));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Run service-user update-redirect-uris: call PUT .../redirect-uris (min 1 URI)
|
|
400
|
+
* @async
|
|
401
|
+
* @param {Object} options - CLI options (controller, id, redirectUris required, min 1)
|
|
402
|
+
* @returns {Promise<void>}
|
|
403
|
+
*/
|
|
404
|
+
async function runServiceUserUpdateRedirectUris(options = {}) {
|
|
405
|
+
const id = requireServiceUserId(options.id);
|
|
406
|
+
const redirectUris = parseList(options.redirectUris);
|
|
407
|
+
if (redirectUris.length === 0) {
|
|
408
|
+
logger.error(formatBlockingError('At least one redirect URI is required. Use --redirect-uris <uri1,uri2,...>.'));
|
|
409
|
+
process.exit(1);
|
|
410
|
+
}
|
|
411
|
+
const { controllerUrl, authConfig } = await resolveControllerAndAuth(options);
|
|
412
|
+
const response = await updateRedirectUrisServiceUser(controllerUrl, authConfig, id, { redirectUris });
|
|
413
|
+
if (response && response.success === false) {
|
|
414
|
+
handleServiceUserApiError(response, 'update');
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (response && response.success === true) {
|
|
418
|
+
logger.log(formatSuccessLine('Service user redirect URIs updated.\n'));
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
197
422
|
module.exports = {
|
|
198
|
-
runServiceUserCreate
|
|
423
|
+
runServiceUserCreate,
|
|
424
|
+
runServiceUserList,
|
|
425
|
+
runServiceUserRotateSecret,
|
|
426
|
+
runServiceUserDelete,
|
|
427
|
+
runServiceUserUpdateGroups,
|
|
428
|
+
runServiceUserUpdateRedirectUris
|
|
199
429
|
};
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
'use strict';
|
|
10
|
+
const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
10
11
|
|
|
11
12
|
const path = require('path');
|
|
12
13
|
const fs = require('fs');
|
|
@@ -84,6 +85,23 @@ function getDatasourceKeys(appPath, configPath, variables, systemKey, systemPars
|
|
|
84
85
|
return keys;
|
|
85
86
|
}
|
|
86
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Full upload to dataplane when --sync (same path as `aifabrix upload <systemKey>`).
|
|
90
|
+
* @param {string} systemKey
|
|
91
|
+
* @param {Object} options
|
|
92
|
+
* @returns {Promise<void>}
|
|
93
|
+
*/
|
|
94
|
+
async function syncLocalIfRequested(systemKey, options) {
|
|
95
|
+
if (options.sync !== true) return;
|
|
96
|
+
logger.log(chalk.cyan('Syncing local config to dataplane…'));
|
|
97
|
+
const { uploadExternalSystem } = require('./upload');
|
|
98
|
+
await uploadExternalSystem(systemKey, {
|
|
99
|
+
verbose: !!options.verbose,
|
|
100
|
+
minimal: true
|
|
101
|
+
});
|
|
102
|
+
logger.log(formatSuccessLine('Sync complete'));
|
|
103
|
+
}
|
|
104
|
+
|
|
87
105
|
/* eslint-disable max-lines-per-function, max-statements -- Load context, then loop over keys */
|
|
88
106
|
/**
|
|
89
107
|
* Runs E2E for all datasources of an external system. Uses each datasource's payloadTemplate (no extra params required).
|
|
@@ -95,6 +113,7 @@ function getDatasourceKeys(appPath, configPath, variables, systemKey, systemPars
|
|
|
95
113
|
* @param {boolean} [options.debug] - Include debug, write log
|
|
96
114
|
* @param {boolean} [options.verbose] - Verbose output
|
|
97
115
|
* @param {boolean} [options.async] - If false, sync mode (default true)
|
|
116
|
+
* @param {boolean} [options.sync] - When true, run full upload (`uploadExternalSystem`) before per-datasource E2E
|
|
98
117
|
* @returns {Promise<{ success: boolean, results: Array<{ key: string, success: boolean, error?: string }> }>}
|
|
99
118
|
*/
|
|
100
119
|
async function runTestE2EForExternalSystem(externalSystem, options = {}) {
|
|
@@ -137,6 +156,8 @@ async function runTestE2EForExternalSystem(externalSystem, options = {}) {
|
|
|
137
156
|
return { success: true, results: [] };
|
|
138
157
|
}
|
|
139
158
|
|
|
159
|
+
await syncLocalIfRequested(systemKey, options);
|
|
160
|
+
|
|
140
161
|
const results = [];
|
|
141
162
|
const opts = {
|
|
142
163
|
app: externalSystem,
|
|
@@ -150,7 +171,12 @@ async function runTestE2EForExternalSystem(externalSystem, options = {}) {
|
|
|
150
171
|
const data = await runDatasourceTestE2E(key, opts);
|
|
151
172
|
const steps = data.steps || data.completedActions || [];
|
|
152
173
|
const failed = data.success === false || steps.some(s => s.success === false || s.error);
|
|
153
|
-
results.push({
|
|
174
|
+
results.push({
|
|
175
|
+
key,
|
|
176
|
+
success: !failed,
|
|
177
|
+
error: failed ? (data.error || 'E2E step failed') : undefined,
|
|
178
|
+
datasourceTestRun: data.datasourceTestRun
|
|
179
|
+
});
|
|
154
180
|
} catch (err) {
|
|
155
181
|
results.push({ key, success: false, error: err.message });
|
|
156
182
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
1
2
|
/**
|
|
2
3
|
* AI Fabrix Builder - Up Commands Shared Helpers
|
|
3
4
|
*
|
|
@@ -165,6 +166,30 @@ function patchEnvOutputPathForDeployOnly(appName) {
|
|
|
165
166
|
}
|
|
166
167
|
}
|
|
167
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Removes builder app directories for the given app names. Only removes paths under the builder root
|
|
171
|
+
* to prevent path traversal. Uses getBuilderPath for each app and validates before removal.
|
|
172
|
+
*
|
|
173
|
+
* @param {string[]} appNames - Application names (e.g. ['keycloak', 'miso-controller', 'dataplane'])
|
|
174
|
+
* @returns {Promise<void>}
|
|
175
|
+
* @throws {Error} If any path is outside builder root (path traversal attempt)
|
|
176
|
+
*/
|
|
177
|
+
async function cleanBuilderAppDirs(appNames) {
|
|
178
|
+
if (!Array.isArray(appNames) || appNames.length === 0) return;
|
|
179
|
+
const builderRoot = path.resolve(pathsUtil.getBuilderRoot());
|
|
180
|
+
for (const appName of appNames) {
|
|
181
|
+
if (!appName || typeof appName !== 'string') continue;
|
|
182
|
+
const appPath = path.resolve(pathsUtil.getBuilderPath(appName));
|
|
183
|
+
if (!appPath.startsWith(builderRoot + path.sep) && appPath !== builderRoot) {
|
|
184
|
+
throw new Error(`Path ${appPath} is outside builder root ${builderRoot}; refusing to clean`);
|
|
185
|
+
}
|
|
186
|
+
if (fs.existsSync(appPath)) {
|
|
187
|
+
fs.rmSync(appPath, { recursive: true });
|
|
188
|
+
logger.log(chalk.blue(`Cleaned builder/${appName}`));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
168
193
|
/**
|
|
169
194
|
* Ensures builder app directory exists from template if application config is missing.
|
|
170
195
|
* If builder/<appName>/application config does not exist, copies from templates/applications/<appName>.
|
|
@@ -189,7 +214,7 @@ async function ensureAppFromTemplate(appName) {
|
|
|
189
214
|
const primaryCopied = await ensureTemplateAtPath(appName, appPath);
|
|
190
215
|
if (primaryCopied) {
|
|
191
216
|
logger.log(chalk.blue(`Creating builder/${appName} from template...`));
|
|
192
|
-
logger.log(
|
|
217
|
+
logger.log(formatSuccessLine(`Copied template for ${appName}`));
|
|
193
218
|
}
|
|
194
219
|
|
|
195
220
|
const cwdBuilderPath = path.join(process.cwd(), 'builder', appName);
|
|
@@ -197,7 +222,7 @@ async function ensureAppFromTemplate(appName) {
|
|
|
197
222
|
const cwdCopied = await ensureTemplateAtPath(appName, cwdBuilderPath);
|
|
198
223
|
if (cwdCopied) {
|
|
199
224
|
logger.log(chalk.blue(`Creating builder/${appName} in project (from template)...`));
|
|
200
|
-
logger.log(
|
|
225
|
+
logger.log(formatSuccessLine(`Copied template for ${appName} into builder/`));
|
|
201
226
|
}
|
|
202
227
|
}
|
|
203
228
|
|
|
@@ -206,6 +231,7 @@ async function ensureAppFromTemplate(appName) {
|
|
|
206
231
|
}
|
|
207
232
|
|
|
208
233
|
module.exports = {
|
|
234
|
+
cleanBuilderAppDirs,
|
|
209
235
|
ensureAppFromTemplate,
|
|
210
236
|
patchEnvOutputPathForDeployOnly,
|
|
211
237
|
validateEnvOutputPathFolderOrNull,
|