@aifabrix/builder 2.43.0 → 2.44.1
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/.cursor/rules/cli-layout.mdc +75 -0
- package/.cursor/rules/project-rules.mdc +8 -0
- package/.npmrc.token +1 -0
- package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +1 -0
- package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/README.md +1 -1
- 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 +31 -0
- package/integration/hubspot-test/create-hubspot.js +5 -5
- package/integration/hubspot-test/hubspot-test-datasource-company.json +58 -462
- package/integration/hubspot-test/hubspot-test-datasource-contact.json +61 -555
- package/integration/hubspot-test/hubspot-test-datasource-deal.json +63 -506
- package/integration/hubspot-test/hubspot-test-datasource-users.json +42 -83
- package/integration/hubspot-test/hubspot-test-deploy.json +3 -3
- package/integration/hubspot-test/test-dataplane-down-tests.js +1 -7
- package/integration/hubspot-test/test-dataplane-down.js +3 -3
- package/integration/hubspot-test/test.js +35 -43
- 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 +301 -0
- package/lib/api/certificates.api.js +62 -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 +11 -3
- package/lib/api/pipeline.api.js +67 -20
- package/lib/api/types/certificates.types.js +48 -0
- package/lib/api/types/dev.types.js +4 -3
- package/lib/api/types/pipeline.types.js +8 -5
- package/lib/api/types/validation-run.types.js +56 -0
- package/lib/api/validation-run.api.js +111 -0
- package/lib/api/validation-runner.js +109 -0
- package/lib/app/certification-show-enrich.js +129 -0
- package/lib/app/certification-verify-rows.js +60 -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 +16 -7
- package/lib/app/rotate-secret.js +14 -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 +44 -1
- package/lib/app/show.js +93 -9
- package/lib/build/index.js +13 -10
- package/lib/certification/cli-cert-sync-skip.js +21 -0
- package/lib/certification/merge-certification-from-artifact.js +185 -0
- package/lib/certification/post-unified-cert-sync.js +33 -0
- package/lib/certification/sync-after-external-command.js +52 -0
- package/lib/certification/sync-system-certification.js +197 -0
- package/lib/cli/index.js +2 -0
- package/lib/cli/setup-app.help.js +67 -0
- package/lib/cli/setup-app.js +61 -121
- package/lib/cli/setup-app.test-commands.js +195 -0
- package/lib/cli/setup-auth.js +19 -5
- 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 +170 -113
- package/lib/cli/setup-environment.js +7 -1
- package/lib/cli/setup-external-system.js +84 -23
- package/lib/cli/setup-infra.js +126 -47
- package/lib/cli/setup-parameters.js +32 -0
- package/lib/cli/setup-secrets.js +137 -18
- package/lib/cli/setup-service-user.js +1 -1
- package/lib/cli/setup-utility.js +54 -22
- 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 +32 -11
- package/lib/commands/auth-config.js +6 -6
- 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 +428 -0
- package/lib/commands/datasource-unified-test-cli.options.js +191 -0
- package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +106 -0
- package/lib/commands/datasource-validation-cli.js +143 -0
- package/lib/commands/datasource.js +125 -95
- 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 +2 -2
- package/lib/commands/repair.js +21 -3
- package/lib/commands/secrets-list.js +23 -12
- package/lib/commands/secrets-remove-all.js +220 -0
- package/lib/commands/secrets-remove.js +21 -12
- package/lib/commands/secrets-set.js +21 -12
- package/lib/commands/secrets-validate.js +4 -4
- package/lib/commands/secure.js +10 -9
- package/lib/commands/service-user.js +26 -25
- package/lib/commands/test-e2e-external.js +27 -1
- package/lib/commands/up-common.js +3 -2
- package/lib/commands/up-dataplane.js +29 -16
- package/lib/commands/up-miso.js +19 -29
- package/lib/commands/upload.js +149 -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 +14 -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 +55 -56
- package/lib/core/diff.js +3 -2
- package/lib/core/ensure-encryption-key.js +1 -1
- 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 +200 -37
- package/lib/core/templates-env.js +4 -3
- package/lib/datasource/abac-validator.js +1 -10
- package/lib/datasource/deploy.js +75 -53
- package/lib/datasource/field-reference-validator.js +9 -6
- package/lib/datasource/integration-context.js +63 -0
- package/lib/datasource/list.js +8 -7
- package/lib/datasource/log-viewer.js +189 -67
- package/lib/datasource/resolve-app.js +4 -4
- package/lib/datasource/test-e2e.js +113 -146
- package/lib/datasource/test-integration.js +114 -122
- package/lib/datasource/unified-validation-run-body.js +68 -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 +93 -0
- package/lib/datasource/validate.js +157 -13
- 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 +166 -53
- package/lib/external-system/download-helpers.js +1 -1
- package/lib/external-system/download.js +7 -6
- package/lib/external-system/generator.js +92 -6
- 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.js +16 -4
- package/lib/generator/helpers.js +58 -3
- package/lib/generator/index.js +4 -0
- package/lib/generator/split-readme.js +12 -7
- package/lib/generator/split-variables.js +2 -1
- package/lib/generator/split.js +1 -1
- package/lib/generator/wizard-readme.js +3 -3
- package/lib/generator/wizard.js +8 -8
- package/lib/infrastructure/compose.js +70 -7
- package/lib/infrastructure/helpers-docker-check.js +67 -0
- package/lib/infrastructure/helpers.js +203 -42
- package/lib/infrastructure/index.js +31 -18
- package/lib/infrastructure/services.js +21 -67
- 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 +1200 -442
- package/lib/schema/external-system.schema.json +203 -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 +226 -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 +77 -17
- 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 +166 -7
- package/lib/utils/config-scoped-resources-preference.js +41 -0
- package/lib/utils/configuration-env-resolver.js +11 -8
- package/lib/utils/controller-deployment-outcome.js +68 -0
- package/lib/utils/credential-display.js +2 -2
- package/lib/utils/credential-secrets-env.js +5 -5
- 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-certificate-tty.js +82 -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 +459 -0
- package/lib/utils/datasource-test-run-exit.js +83 -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 +35 -8
- package/lib/utils/env-template.js +2 -2
- package/lib/utils/environment-scoped-resources.js +144 -0
- package/lib/utils/error-formatter.js +92 -13
- 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-readme.js +8 -1
- package/lib/utils/external-system-display.js +242 -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-system-test-tty-overview.js +120 -0
- package/lib/utils/external-system-system-test-tty.js +417 -0
- package/lib/utils/external-system-test-helpers.js +24 -6
- package/lib/utils/external-system-validators.js +30 -12
- package/lib/utils/health-check-url.js +119 -0
- package/lib/utils/health-check.js +59 -25
- package/lib/utils/help-builder.js +11 -8
- 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 +3 -4
- package/lib/utils/paths.js +148 -47
- 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 +169 -49
- package/lib/utils/secrets-helpers.js +70 -59
- package/lib/utils/secrets-kv-scope.js +60 -0
- package/lib/utils/secrets-utils.js +32 -38
- package/lib/utils/secrets-validation.js +3 -1
- package/lib/utils/secrets-yaml-preserve.js +109 -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 +1 -2
- 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 +112 -0
- package/lib/utils/validation-run-post-retry.js +85 -0
- package/lib/utils/validation-run-request.js +116 -0
- package/lib/utils/variable-transformer.js +21 -4
- package/lib/utils/yaml-preserve.js +33 -14
- package/lib/validation/datasource-warnings.js +56 -0
- package/lib/validation/env-template-auth.js +1 -1
- package/lib/validation/external-manifest-validator.js +27 -7
- package/lib/validation/validate-display.js +37 -31
- package/lib/validation/validate-external-cert-sync.js +23 -0
- package/lib/validation/validate.js +8 -14
- package/lib/validation/validator-unresolved-placeholders.js +98 -0
- package/lib/validation/validator.js +22 -65
- package/lib/validation/wizard-config-validator.js +2 -1
- package/package.json +9 -4
- package/scripts/check-datasource-test-run-schema-sync.js +34 -0
- package/scripts/diagnose-cli.js +150 -0
- package/scripts/install-local.js +307 -55
- package/scripts/pnpm-global-remove.js +48 -0
- package/templates/README.md +15 -2
- package/templates/applications/dataplane/application.yaml +52 -2
- package/templates/applications/dataplane/env.template +79 -17
- 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 +42 -12
- 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/external-datasource.yaml.hbs +197 -118
- package/templates/infra/compose.yaml.hbs +33 -16
- package/templates/infra/servers.json.hbs +3 -1
- package/templates/python/docker-compose.hbs +16 -0
- package/templates/typescript/docker-compose.hbs +16 -0
- package/lib/api/external-test.api.js +0 -111
- package/lib/schema/env-config.yaml +0 -60
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview aifabrix secret remove-all – remove every secret (user file, shared file, or remote API)
|
|
4
|
+
* @author AI Fabrix Team
|
|
5
|
+
* @version 2.0.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const readline = require('readline');
|
|
11
|
+
const yaml = require('js-yaml');
|
|
12
|
+
const chalk = require('chalk');
|
|
13
|
+
const logger = require('../utils/logger');
|
|
14
|
+
const { getAifabrixSecretsPath } = require('../core/config');
|
|
15
|
+
const pathsUtil = require('../utils/paths');
|
|
16
|
+
const remoteDevAuth = require('../utils/remote-dev-auth');
|
|
17
|
+
const devApi = require('../api/dev.api');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {string} question
|
|
21
|
+
* @returns {Promise<string>}
|
|
22
|
+
*/
|
|
23
|
+
function promptLine(question) {
|
|
24
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
25
|
+
return new Promise(resolve => {
|
|
26
|
+
rl.question(question, answer => {
|
|
27
|
+
rl.close();
|
|
28
|
+
resolve((answer || '').trim());
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Read secret keys from a YAML secrets file.
|
|
35
|
+
* @param {string} filePath - Absolute path
|
|
36
|
+
* @returns {string[]} Sorted unique keys
|
|
37
|
+
*/
|
|
38
|
+
function listKeysFromYamlFile(filePath) {
|
|
39
|
+
if (!fs.existsSync(filePath)) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
44
|
+
const data = yaml.load(content) || {};
|
|
45
|
+
if (typeof data !== 'object' || Array.isArray(data)) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
return Object.keys(data).sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
|
|
49
|
+
} catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Write an empty secrets map to file (same options as single-key remove).
|
|
56
|
+
* @param {string} filePath - Absolute path
|
|
57
|
+
*/
|
|
58
|
+
function writeEmptySecretsFile(filePath) {
|
|
59
|
+
const yamlContent = yaml.dump({}, { indent: 2, lineWidth: -1, noRefs: true, sortKeys: false });
|
|
60
|
+
fs.writeFileSync(filePath, yamlContent, { mode: 0o600 });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {boolean} skipPrompt - When true (--yes), skip confirmation
|
|
65
|
+
* @param {string} whereLabel - Human-readable target
|
|
66
|
+
* @param {number} count - Number of keys
|
|
67
|
+
* @returns {Promise<boolean>}
|
|
68
|
+
*/
|
|
69
|
+
async function confirmRemoveAll(skipPrompt, whereLabel, count) {
|
|
70
|
+
if (skipPrompt) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
logger.log(chalk.yellow(`\nThis will permanently remove all ${count} secret key(s) from ${whereLabel}.`));
|
|
74
|
+
logger.log(chalk.gray('This cannot be undone.\n'));
|
|
75
|
+
const answer = (await promptLine(chalk.bold('Type "yes" to confirm (anything else cancels): '))).toLowerCase();
|
|
76
|
+
return answer === 'yes';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @param {Object} auth - Remote dev auth (serverUrl, clientCertPem, serverCaPem)
|
|
81
|
+
* @param {string} target - Secrets endpoint URL
|
|
82
|
+
* @returns {Promise<string[]>}
|
|
83
|
+
*/
|
|
84
|
+
async function listRemoteSecretKeys(auth, target) {
|
|
85
|
+
const items = await devApi.listSecrets(
|
|
86
|
+
auth.serverUrl,
|
|
87
|
+
auth.clientCertPem,
|
|
88
|
+
auth.serverCaPem || undefined,
|
|
89
|
+
target
|
|
90
|
+
);
|
|
91
|
+
return (Array.isArray(items) ? items : [])
|
|
92
|
+
.map(i => i.name || i.key || '')
|
|
93
|
+
.filter(k => typeof k === 'string' && k.length > 0)
|
|
94
|
+
.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @param {string} target - Secrets endpoint URL
|
|
99
|
+
* @returns {string}
|
|
100
|
+
*/
|
|
101
|
+
function labelForRemoteSharedSecrets(target) {
|
|
102
|
+
const host = remoteDevAuth.getSharedSecretsRemoteHostname(target);
|
|
103
|
+
return host ? `shared secrets (remote - ${host})` : 'shared secrets (remote)';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {Object} auth
|
|
108
|
+
* @param {string[]} keys
|
|
109
|
+
* @param {string} target
|
|
110
|
+
* @returns {Promise<void>}
|
|
111
|
+
*/
|
|
112
|
+
async function deleteRemoteSecretKeys(auth, keys, target) {
|
|
113
|
+
const ca = auth.serverCaPem || undefined;
|
|
114
|
+
for (const key of keys) {
|
|
115
|
+
await devApi.deleteSecret(auth.serverUrl, auth.clientCertPem, key, ca, target);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Remove every shared secret via remote API.
|
|
121
|
+
* @param {string} target - Resolved secrets endpoint URL
|
|
122
|
+
* @param {Object} options - { yes?: boolean }
|
|
123
|
+
* @returns {Promise<void>}
|
|
124
|
+
*/
|
|
125
|
+
async function removeAllRemoteSharedSecrets(target, options) {
|
|
126
|
+
const auth = await remoteDevAuth.getRemoteDevAuth();
|
|
127
|
+
if (!auth) {
|
|
128
|
+
throw new Error('Remote server not configured or certificate missing. Run "aifabrix dev init" first.');
|
|
129
|
+
}
|
|
130
|
+
const keys = await listRemoteSecretKeys(auth, target);
|
|
131
|
+
if (keys.length === 0) {
|
|
132
|
+
logger.log(chalk.gray('No shared secrets to remove.'));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const where = labelForRemoteSharedSecrets(target);
|
|
136
|
+
const ok = await confirmRemoveAll(!!options.yes, where, keys.length);
|
|
137
|
+
if (!ok) {
|
|
138
|
+
logger.log(chalk.gray('Cancelled. No secrets were removed.'));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
await deleteRemoteSecretKeys(auth, keys, target);
|
|
142
|
+
logger.log(formatSuccessLine(`Removed ${keys.length} secret key(s) from ${where}.`));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Remove every key from a shared secrets YAML file.
|
|
147
|
+
* @param {string} resolvedPath - Absolute file path
|
|
148
|
+
* @param {Object} options - { yes?: boolean }
|
|
149
|
+
* @returns {Promise<void>}
|
|
150
|
+
*/
|
|
151
|
+
async function removeAllSharedFileSecrets(resolvedPath, options) {
|
|
152
|
+
const keys = listKeysFromYamlFile(resolvedPath);
|
|
153
|
+
if (keys.length === 0) {
|
|
154
|
+
logger.log(chalk.gray('No shared secrets to remove.'));
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const ok = await confirmRemoveAll(!!options.yes, `shared secrets file (${resolvedPath})`, keys.length);
|
|
158
|
+
if (!ok) {
|
|
159
|
+
logger.log(chalk.gray('Cancelled. No secrets were removed.'));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
writeEmptySecretsFile(resolvedPath);
|
|
163
|
+
logger.log(formatSuccessLine(`Removed ${keys.length} secret key(s) from shared secrets file.`));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Remove every key from shared store (remote API or file).
|
|
168
|
+
* @param {string} generalSecretsPath - Path or URL from config
|
|
169
|
+
* @param {Object} options - { yes?: boolean }
|
|
170
|
+
* @returns {Promise<void>}
|
|
171
|
+
*/
|
|
172
|
+
async function removeAllSharedSecrets(generalSecretsPath, options) {
|
|
173
|
+
const target = await remoteDevAuth.resolveSharedSecretsEndpoint(generalSecretsPath);
|
|
174
|
+
if (remoteDevAuth.isRemoteSecretsUrl(target)) {
|
|
175
|
+
await removeAllRemoteSharedSecrets(target, options);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const resolvedPath = path.isAbsolute(target) ? target : path.resolve(process.cwd(), target);
|
|
179
|
+
await removeAllSharedFileSecrets(resolvedPath, options);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* @param {Object} options - { yes?: boolean }
|
|
184
|
+
* @returns {Promise<void>}
|
|
185
|
+
*/
|
|
186
|
+
async function removeAllUserSecrets(options) {
|
|
187
|
+
const userSecretsPath = pathsUtil.getPrimaryUserSecretsLocalPath();
|
|
188
|
+
const keys = listKeysFromYamlFile(userSecretsPath);
|
|
189
|
+
if (keys.length === 0) {
|
|
190
|
+
logger.log(chalk.gray('No user secrets to remove.'));
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const ok = await confirmRemoveAll(!!options.yes, `user secrets (${userSecretsPath})`, keys.length);
|
|
194
|
+
if (!ok) {
|
|
195
|
+
logger.log(chalk.gray('Cancelled. No secrets were removed.'));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
writeEmptySecretsFile(userSecretsPath);
|
|
199
|
+
logger.log(formatSuccessLine(`Removed ${keys.length} secret key(s) from user secrets.`));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Handle secret remove-all command.
|
|
204
|
+
* @param {Object} options - { shared?: boolean, yes?: boolean }
|
|
205
|
+
* @returns {Promise<void>}
|
|
206
|
+
*/
|
|
207
|
+
async function handleSecretsRemoveAll(options) {
|
|
208
|
+
const isShared = options.shared || options['shared'] || false;
|
|
209
|
+
if (!isShared) {
|
|
210
|
+
await removeAllUserSecrets(options);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const generalSecretsPath = await getAifabrixSecretsPath();
|
|
214
|
+
if (!generalSecretsPath) {
|
|
215
|
+
throw new Error('Shared secrets not configured. Set aifabrix-secrets in config.yaml.');
|
|
216
|
+
}
|
|
217
|
+
await removeAllSharedSecrets(generalSecretsPath, options);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
module.exports = { handleSecretsRemoveAll };
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
* @version 2.0.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
7
8
|
const path = require('path');
|
|
8
9
|
const fs = require('fs');
|
|
9
10
|
const yaml = require('js-yaml');
|
|
10
|
-
const chalk = require('chalk');
|
|
11
11
|
const logger = require('../utils/logger');
|
|
12
12
|
const { getAifabrixSecretsPath } = require('../core/config');
|
|
13
13
|
const pathsUtil = require('../utils/paths');
|
|
14
|
-
const
|
|
14
|
+
const remoteDevAuth = require('../utils/remote-dev-auth');
|
|
15
15
|
const devApi = require('../api/dev.api');
|
|
16
16
|
|
|
17
17
|
/**
|
|
@@ -44,27 +44,36 @@ function removeKeyFromFile(key, filePath) {
|
|
|
44
44
|
* @returns {Promise<void>}
|
|
45
45
|
*/
|
|
46
46
|
async function removeSharedSecret(key, generalSecretsPath) {
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
const target = await remoteDevAuth.resolveSharedSecretsEndpoint(generalSecretsPath);
|
|
48
|
+
if (remoteDevAuth.isRemoteSecretsUrl(target)) {
|
|
49
|
+
const auth = await remoteDevAuth.getRemoteDevAuth();
|
|
49
50
|
if (!auth) {
|
|
50
51
|
throw new Error('Remote server not configured or certificate missing. Run "aifabrix dev init" first.');
|
|
51
52
|
}
|
|
52
53
|
try {
|
|
53
|
-
await devApi.deleteSecret(
|
|
54
|
+
await devApi.deleteSecret(
|
|
55
|
+
auth.serverUrl,
|
|
56
|
+
auth.clientCertPem,
|
|
57
|
+
key,
|
|
58
|
+
auth.serverCaPem || undefined,
|
|
59
|
+
target
|
|
60
|
+
);
|
|
54
61
|
} catch (err) {
|
|
55
62
|
if (err.status === 404) {
|
|
56
63
|
throw new Error(`Secret '${key}' not found.`);
|
|
57
64
|
}
|
|
58
65
|
throw err;
|
|
59
66
|
}
|
|
60
|
-
|
|
67
|
+
const host = remoteDevAuth.getSharedSecretsRemoteHostname(target);
|
|
68
|
+
const where = host ? `shared secrets (remote - ${host})` : 'shared secrets (remote)';
|
|
69
|
+
logger.log(formatSuccessLine(`Secret '${key}' removed from ${where}.`));
|
|
61
70
|
return;
|
|
62
71
|
}
|
|
63
|
-
const resolvedPath = path.isAbsolute(
|
|
64
|
-
?
|
|
65
|
-
: path.resolve(process.cwd(),
|
|
72
|
+
const resolvedPath = path.isAbsolute(target)
|
|
73
|
+
? target
|
|
74
|
+
: path.resolve(process.cwd(), target);
|
|
66
75
|
removeKeyFromFile(key, resolvedPath);
|
|
67
|
-
logger.log(
|
|
76
|
+
logger.log(formatSuccessLine(`Secret '${key}' removed from shared secrets file.`));
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
/**
|
|
@@ -88,9 +97,9 @@ async function handleSecretsRemove(key, options) {
|
|
|
88
97
|
}
|
|
89
98
|
await removeSharedSecret(key, generalSecretsPath);
|
|
90
99
|
} else {
|
|
91
|
-
const userSecretsPath =
|
|
100
|
+
const userSecretsPath = pathsUtil.getPrimaryUserSecretsLocalPath();
|
|
92
101
|
removeKeyFromFile(key, userSecretsPath);
|
|
93
|
-
logger.log(
|
|
102
|
+
logger.log(formatSuccessLine(`Secret '${key}' removed from user secrets.`));
|
|
94
103
|
}
|
|
95
104
|
}
|
|
96
105
|
|
|
@@ -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,7 +10,6 @@
|
|
|
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
15
|
const { validateDataplaneSecrets } = require('../utils/token-manager');
|
|
@@ -34,7 +34,7 @@ async function handleSecretsValidate(pathArg, options = {}) {
|
|
|
34
34
|
if (target.type === 'file' && target.filePath) {
|
|
35
35
|
filePath = target.filePath;
|
|
36
36
|
} else {
|
|
37
|
-
filePath =
|
|
37
|
+
filePath = pathsUtil.getPrimaryUserSecretsLocalPath();
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -42,9 +42,9 @@ async function handleSecretsValidate(pathArg, options = {}) {
|
|
|
42
42
|
const dataplaneResult = validateDataplaneSecrets(filePath);
|
|
43
43
|
const allValid = result.valid && dataplaneResult.valid;
|
|
44
44
|
if (result.valid) {
|
|
45
|
-
logger.log(
|
|
45
|
+
logger.log(formatSuccessLine(`Secrets file is valid: ${result.path}`));
|
|
46
46
|
} else {
|
|
47
|
-
logger.log(
|
|
47
|
+
logger.log(formatBlockingError(`Validation failed: ${result.path}`));
|
|
48
48
|
result.errors.forEach((err) => logger.log(chalk.yellow(` • ${err}`)));
|
|
49
49
|
}
|
|
50
50
|
if (!dataplaneResult.valid) {
|
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`.
|
|
@@ -75,14 +76,14 @@ function handleCreateError(response) {
|
|
|
75
76
|
const status = response.status;
|
|
76
77
|
const msg = response.formattedError || response.error || 'Request failed';
|
|
77
78
|
if (status === 400) {
|
|
78
|
-
logger.error(
|
|
79
|
+
logger.error(formatBlockingError(`Validation error: ${msg}`));
|
|
79
80
|
} else if (status === 401) {
|
|
80
|
-
logger.error(
|
|
81
|
+
logger.error(formatBlockingError('Unauthorized. Run "aifabrix login" and try again.'));
|
|
81
82
|
} else if (status === 403) {
|
|
82
|
-
logger.error(
|
|
83
|
+
logger.error(formatBlockingError('Missing permission: service-user:create'));
|
|
83
84
|
logger.error(chalk.gray('Your account needs the service-user:create permission on the controller.'));
|
|
84
85
|
} else {
|
|
85
|
-
logger.error(
|
|
86
|
+
logger.error(formatBlockingError(`Failed to create service user: ${msg}`));
|
|
86
87
|
}
|
|
87
88
|
process.exit(1);
|
|
88
89
|
}
|
|
@@ -96,20 +97,20 @@ function handleServiceUserApiError(response, permissionScope) {
|
|
|
96
97
|
const status = response.status;
|
|
97
98
|
const msg = response.formattedError || response.error || 'Request failed';
|
|
98
99
|
if (status === 400) {
|
|
99
|
-
logger.error(
|
|
100
|
+
logger.error(formatBlockingError(`Validation error: ${msg}`));
|
|
100
101
|
} else if (status === 401) {
|
|
101
|
-
logger.error(
|
|
102
|
+
logger.error(formatBlockingError('Unauthorized. Run "aifabrix login" and try again.'));
|
|
102
103
|
} else if (status === 403) {
|
|
103
|
-
logger.error(
|
|
104
|
+
logger.error(formatBlockingError(`Missing permission: service-user:${permissionScope}`));
|
|
104
105
|
logger.error(chalk.gray(`Your account needs the service-user:${permissionScope} permission on the controller.`));
|
|
105
106
|
} else if (status === 404) {
|
|
106
|
-
logger.error(
|
|
107
|
+
logger.error(formatBlockingError('Service user not found.'));
|
|
107
108
|
const detail = response.error || '';
|
|
108
109
|
if (detail) {
|
|
109
110
|
logger.error(chalk.gray(detail));
|
|
110
111
|
}
|
|
111
112
|
} else {
|
|
112
|
-
logger.error(
|
|
113
|
+
logger.error(formatBlockingError(`Request failed: ${msg}`));
|
|
113
114
|
}
|
|
114
115
|
process.exit(1);
|
|
115
116
|
}
|
|
@@ -123,12 +124,12 @@ function handleServiceUserApiError(response, permissionScope) {
|
|
|
123
124
|
async function resolveControllerAndAuth(options) {
|
|
124
125
|
const controllerUrl = options.controller || (await resolveControllerUrl());
|
|
125
126
|
if (!controllerUrl) {
|
|
126
|
-
logger.error(
|
|
127
|
+
logger.error(formatBlockingError('Controller URL is required. Run "aifabrix login" first.'));
|
|
127
128
|
process.exit(1);
|
|
128
129
|
}
|
|
129
130
|
const authResult = await getServiceUserAuth(controllerUrl);
|
|
130
131
|
if (!authResult || !authResult.token) {
|
|
131
|
-
logger.error(
|
|
132
|
+
logger.error(formatBlockingError(`No authentication token for controller: ${controllerUrl}`));
|
|
132
133
|
logger.error(chalk.gray('Run: aifabrix login'));
|
|
133
134
|
process.exit(1);
|
|
134
135
|
}
|
|
@@ -173,7 +174,7 @@ function displayServiceUserList(items) {
|
|
|
173
174
|
* @param {string} clientSecret - One-time client secret
|
|
174
175
|
*/
|
|
175
176
|
function displayCreateSuccess(clientId, clientSecret) {
|
|
176
|
-
logger.log(chalk.bold('\n
|
|
177
|
+
logger.log(chalk.bold('\n✔ Service user created\n'));
|
|
177
178
|
logger.log(chalk.cyan(' clientId: ') + clientId);
|
|
178
179
|
logger.log(chalk.cyan(' clientSecret: ') + clientSecret);
|
|
179
180
|
logger.log('');
|
|
@@ -207,19 +208,19 @@ function validateServiceUserOptions(options) {
|
|
|
207
208
|
const redirectUris = parseList(options.redirectUris);
|
|
208
209
|
const groupNames = parseList(options.groupNames);
|
|
209
210
|
if (!username) {
|
|
210
|
-
logger.error(
|
|
211
|
+
logger.error(formatBlockingError('Username is required. Use --username <username>.'));
|
|
211
212
|
process.exit(1);
|
|
212
213
|
}
|
|
213
214
|
if (!email) {
|
|
214
|
-
logger.error(
|
|
215
|
+
logger.error(formatBlockingError('Email is required. Use --email <email>.'));
|
|
215
216
|
process.exit(1);
|
|
216
217
|
}
|
|
217
218
|
if (redirectUris.length === 0) {
|
|
218
|
-
logger.error(
|
|
219
|
+
logger.error(formatBlockingError('At least one redirect URI is required. Use --redirect-uris <uri1,uri2,...>.'));
|
|
219
220
|
process.exit(1);
|
|
220
221
|
}
|
|
221
222
|
if (groupNames.length === 0) {
|
|
222
|
-
logger.error(
|
|
223
|
+
logger.error(formatBlockingError('At least one group name is required. Use --group-names <name1,name2,...>.'));
|
|
223
224
|
process.exit(1);
|
|
224
225
|
}
|
|
225
226
|
return {
|
|
@@ -241,12 +242,12 @@ async function resolveOptionsAndAuth(options) {
|
|
|
241
242
|
const validated = validateServiceUserOptions(options);
|
|
242
243
|
const controllerUrl = options.controller || (await resolveControllerUrl());
|
|
243
244
|
if (!controllerUrl) {
|
|
244
|
-
logger.error(
|
|
245
|
+
logger.error(formatBlockingError('Controller URL is required. Run "aifabrix login" first.'));
|
|
245
246
|
process.exit(1);
|
|
246
247
|
}
|
|
247
248
|
const authResult = await getServiceUserAuth(controllerUrl);
|
|
248
249
|
if (!authResult || !authResult.token) {
|
|
249
|
-
logger.error(
|
|
250
|
+
logger.error(formatBlockingError(`No authentication token for controller: ${controllerUrl}`));
|
|
250
251
|
logger.error(chalk.gray('Run: aifabrix login'));
|
|
251
252
|
process.exit(1);
|
|
252
253
|
}
|
|
@@ -295,7 +296,7 @@ async function runServiceUserCreate(options = {}) {
|
|
|
295
296
|
function requireServiceUserId(id) {
|
|
296
297
|
const trimmed = (id && typeof id === 'string' ? id.trim() : '') || '';
|
|
297
298
|
if (!trimmed) {
|
|
298
|
-
logger.error(
|
|
299
|
+
logger.error(formatBlockingError('Service user ID is required. Use --id <uuid>.'));
|
|
299
300
|
process.exit(1);
|
|
300
301
|
}
|
|
301
302
|
return trimmed;
|
|
@@ -343,7 +344,7 @@ async function runServiceUserRotateSecret(options = {}) {
|
|
|
343
344
|
const payload = response?.data?.data ?? response?.data ?? response ?? {};
|
|
344
345
|
const clientSecret = payload?.clientSecret ?? '';
|
|
345
346
|
if (response && response.success === true) {
|
|
346
|
-
logger.log(chalk.bold('\n
|
|
347
|
+
logger.log(chalk.bold('\n✔ Secret rotated\n'));
|
|
347
348
|
logger.log(chalk.cyan(' clientSecret: ') + clientSecret);
|
|
348
349
|
logger.log('');
|
|
349
350
|
logger.log(chalk.yellow('⚠ ' + ONE_TIME_WARNING));
|
|
@@ -366,7 +367,7 @@ async function runServiceUserDelete(options = {}) {
|
|
|
366
367
|
return;
|
|
367
368
|
}
|
|
368
369
|
if (response && response.success === true) {
|
|
369
|
-
logger.log(
|
|
370
|
+
logger.log(formatSuccessLine('Service user deactivated.\n'));
|
|
370
371
|
}
|
|
371
372
|
}
|
|
372
373
|
|
|
@@ -380,7 +381,7 @@ async function runServiceUserUpdateGroups(options = {}) {
|
|
|
380
381
|
const id = requireServiceUserId(options.id);
|
|
381
382
|
const groupNames = parseList(options.groupNames);
|
|
382
383
|
if (groupNames.length === 0) {
|
|
383
|
-
logger.error(
|
|
384
|
+
logger.error(formatBlockingError('At least one group name is required. Use --group-names <name1,name2,...>.'));
|
|
384
385
|
process.exit(1);
|
|
385
386
|
}
|
|
386
387
|
const { controllerUrl, authConfig } = await resolveControllerAndAuth(options);
|
|
@@ -390,7 +391,7 @@ async function runServiceUserUpdateGroups(options = {}) {
|
|
|
390
391
|
return;
|
|
391
392
|
}
|
|
392
393
|
if (response && response.success === true) {
|
|
393
|
-
logger.log(
|
|
394
|
+
logger.log(formatSuccessLine('Service user groups updated.\n'));
|
|
394
395
|
}
|
|
395
396
|
}
|
|
396
397
|
|
|
@@ -404,7 +405,7 @@ async function runServiceUserUpdateRedirectUris(options = {}) {
|
|
|
404
405
|
const id = requireServiceUserId(options.id);
|
|
405
406
|
const redirectUris = parseList(options.redirectUris);
|
|
406
407
|
if (redirectUris.length === 0) {
|
|
407
|
-
logger.error(
|
|
408
|
+
logger.error(formatBlockingError('At least one redirect URI is required. Use --redirect-uris <uri1,uri2,...>.'));
|
|
408
409
|
process.exit(1);
|
|
409
410
|
}
|
|
410
411
|
const { controllerUrl, authConfig } = await resolveControllerAndAuth(options);
|
|
@@ -414,7 +415,7 @@ async function runServiceUserUpdateRedirectUris(options = {}) {
|
|
|
414
415
|
return;
|
|
415
416
|
}
|
|
416
417
|
if (response && response.success === true) {
|
|
417
|
-
logger.log(
|
|
418
|
+
logger.log(formatSuccessLine('Service user redirect URIs updated.\n'));
|
|
418
419
|
}
|
|
419
420
|
}
|
|
420
421
|
|