@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,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register AIFABRIX_HOME and AIFABRIX_WORK for new shells: Windows user env vars;
|
|
3
|
+
* POSIX writes a sourced script next to config.yaml and ensures a profile snippet.
|
|
4
|
+
*
|
|
5
|
+
* @fileoverview OS/shell registration after dev set-home / dev set-work
|
|
6
|
+
* @author AI Fabrix Team
|
|
7
|
+
* @version 2.0.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const fsp = require('node:fs').promises;
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const os = require('os');
|
|
15
|
+
const { promisify } = require('util');
|
|
16
|
+
const execFileCb = require('child_process').execFile;
|
|
17
|
+
const execFile = promisify(execFileCb);
|
|
18
|
+
|
|
19
|
+
const { getConfigDirForPaths } = require('./paths');
|
|
20
|
+
|
|
21
|
+
const SHELL_ENV_BASENAME = 'aifabrix-shell-env.sh';
|
|
22
|
+
const BLOCK_BEGIN = '# BEGIN aifabrix-builder shell env';
|
|
23
|
+
const BLOCK_END = '# END aifabrix-builder shell env';
|
|
24
|
+
|
|
25
|
+
const PROFILE_BLOCK_RE = /\n?# BEGIN aifabrix-builder shell env\n[\s\S]*?\n# END aifabrix-builder shell env\n?/;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Single-quote a path for POSIX export lines.
|
|
29
|
+
* @param {string} p - Path
|
|
30
|
+
* @returns {string} Quoted for sh
|
|
31
|
+
*/
|
|
32
|
+
function shSingleQuoted(p) {
|
|
33
|
+
const esc = String.fromCharCode(39, 92, 39, 39);
|
|
34
|
+
return `'${String(p).replace(/'/g, esc)}'`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Build body for aifabrix-shell-env.sh (exports only; no secrets).
|
|
39
|
+
* @param {string|null} homeAbs - Resolved home or null
|
|
40
|
+
* @param {string|null} workAbs - Resolved work or null
|
|
41
|
+
* @returns {string} File content
|
|
42
|
+
*/
|
|
43
|
+
function buildPosixShellEnvBody(homeAbs, workAbs) {
|
|
44
|
+
const lines = ['# Managed by aifabrix dev set-home / set-work. Do not edit.'];
|
|
45
|
+
if (homeAbs) {
|
|
46
|
+
lines.push(`export AIFABRIX_HOME=${shSingleQuoted(homeAbs)}`);
|
|
47
|
+
}
|
|
48
|
+
if (workAbs) {
|
|
49
|
+
lines.push(`export AIFABRIX_WORK=${shSingleQuoted(workAbs)}`);
|
|
50
|
+
}
|
|
51
|
+
return `${lines.join('\n')}\n`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Profile snippet that sources the shell env file.
|
|
56
|
+
* @param {string} envFileAbs - Absolute path to aifabrix-shell-env.sh
|
|
57
|
+
* @returns {string} Block including markers
|
|
58
|
+
*/
|
|
59
|
+
function buildProfileBlock(envFileAbs) {
|
|
60
|
+
const q = shSingleQuoted(envFileAbs);
|
|
61
|
+
return `${BLOCK_BEGIN}
|
|
62
|
+
[ -f ${q} ] && . ${q}
|
|
63
|
+
${BLOCK_END}
|
|
64
|
+
`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Resolve YAML path string to absolute or null.
|
|
69
|
+
* @param {*} raw - Config value
|
|
70
|
+
* @returns {string|null}
|
|
71
|
+
*/
|
|
72
|
+
function absFromConfigRaw(raw) {
|
|
73
|
+
if (typeof raw !== 'string') return null;
|
|
74
|
+
const t = raw.trim();
|
|
75
|
+
if (!t) return null;
|
|
76
|
+
return path.resolve(t);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* PowerShell to set or remove a user-scoped environment variable.
|
|
81
|
+
* @param {string} name - Var name (caller must pass safe identifiers only)
|
|
82
|
+
* @param {string|null} value - Absolute path or null to remove
|
|
83
|
+
* @returns {string} PowerShell -Command argument body
|
|
84
|
+
*/
|
|
85
|
+
function psSetUserEnvStatement(name, value) {
|
|
86
|
+
if (value === null || value === undefined || value === '') {
|
|
87
|
+
return `[System.Environment]::SetEnvironmentVariable('${name}', $null, 'User')`;
|
|
88
|
+
}
|
|
89
|
+
const escaped = String(value).replace(/'/g, '\'\'');
|
|
90
|
+
return `[System.Environment]::SetEnvironmentVariable('${name}', '${escaped}', 'User')`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Default rc file to patch (zsh vs bash).
|
|
95
|
+
* @param {string} homedir - User home
|
|
96
|
+
* @returns {string} Absolute profile path
|
|
97
|
+
*/
|
|
98
|
+
function defaultProfilePath(homedir) {
|
|
99
|
+
const shell = process.env.SHELL || '';
|
|
100
|
+
if (shell.includes('zsh')) {
|
|
101
|
+
return path.join(homedir, '.zshrc');
|
|
102
|
+
}
|
|
103
|
+
return path.join(homedir, '.bashrc');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Ensure ~/.zshrc or ~/.bashrc contains a single marked block sourcing envFileAbs.
|
|
108
|
+
* @param {string} envFileAbs - Shell env file
|
|
109
|
+
* @param {object} [overrides] - Test hooks
|
|
110
|
+
* @param {string} [overrides.profilePath] - Profile file to edit
|
|
111
|
+
* @param {string} [overrides.homedir] - Home directory
|
|
112
|
+
* @returns {Promise<void>}
|
|
113
|
+
*/
|
|
114
|
+
async function ensureProfileShellBlock(envFileAbs, overrides = {}) {
|
|
115
|
+
const homedir = overrides.homedir ?? os.homedir();
|
|
116
|
+
const profilePath = overrides.profilePath ?? defaultProfilePath(homedir);
|
|
117
|
+
const snippet = buildProfileBlock(envFileAbs);
|
|
118
|
+
let existing = '';
|
|
119
|
+
try {
|
|
120
|
+
existing = await fsp.readFile(profilePath, 'utf8');
|
|
121
|
+
} catch (err) {
|
|
122
|
+
if (err.code !== 'ENOENT') {
|
|
123
|
+
throw err;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (PROFILE_BLOCK_RE.test(existing)) {
|
|
127
|
+
const next = existing.replace(PROFILE_BLOCK_RE, `\n${snippet.trimEnd()}\n`);
|
|
128
|
+
if (next !== existing) {
|
|
129
|
+
await fsp.writeFile(profilePath, next, 'utf8');
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const sep = existing && !existing.endsWith('\n') ? '\n' : '';
|
|
134
|
+
await fsp.writeFile(profilePath, `${existing}${sep}${snippet}`, 'utf8');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Apply Windows user env for both vars from current config (independent clear per key).
|
|
139
|
+
* @param {string|null} homeAbs
|
|
140
|
+
* @param {string|null} workAbs
|
|
141
|
+
* @param {Function} [execFileImpl] - execFile (for tests)
|
|
142
|
+
* @returns {Promise<void>}
|
|
143
|
+
*/
|
|
144
|
+
async function applyWindowsUserEnv(homeAbs, workAbs, execFileImpl = execFile) {
|
|
145
|
+
const script = `${psSetUserEnvStatement('AIFABRIX_HOME', homeAbs)}; ${psSetUserEnvStatement(
|
|
146
|
+
'AIFABRIX_WORK',
|
|
147
|
+
workAbs
|
|
148
|
+
)}`;
|
|
149
|
+
await execFileImpl(
|
|
150
|
+
'powershell.exe',
|
|
151
|
+
['-NoProfile', '-NonInteractive', '-Command', script],
|
|
152
|
+
{ windowsHide: true }
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Write POSIX shell env file and ensure profile sources it.
|
|
158
|
+
* @param {string|null} homeAbs
|
|
159
|
+
* @param {string|null} workAbs
|
|
160
|
+
* @param {object} overrides - Test hooks (same as ensureProfileShellBlock + getConfigDirForPaths)
|
|
161
|
+
* @returns {Promise<void>}
|
|
162
|
+
*/
|
|
163
|
+
async function applyPosixShellEnv(homeAbs, workAbs, overrides = {}) {
|
|
164
|
+
const configDir = overrides.getConfigDirForPaths ? overrides.getConfigDirForPaths() : getConfigDirForPaths();
|
|
165
|
+
const envFile = path.join(configDir, SHELL_ENV_BASENAME);
|
|
166
|
+
await fsp.mkdir(configDir, { recursive: true });
|
|
167
|
+
const body = buildPosixShellEnvBody(homeAbs, workAbs);
|
|
168
|
+
await fsp.writeFile(envFile, body, { mode: 0o600, flag: 'w' });
|
|
169
|
+
await ensureProfileShellBlock(path.resolve(envFile), overrides);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Sync user/shell environment from saved config.yaml (after set-home or set-work).
|
|
174
|
+
*
|
|
175
|
+
* @async
|
|
176
|
+
* @param {function(): Promise<object>} getConfigFn - Same as config.getConfig
|
|
177
|
+
* @param {object} [overrides] - Optional { platform, execFile, getConfigDirForPaths, profilePath, homedir }
|
|
178
|
+
* @returns {Promise<void>}
|
|
179
|
+
*/
|
|
180
|
+
async function registerAifabrixShellEnvFromConfig(getConfigFn, overrides = {}) {
|
|
181
|
+
const platform = overrides.platform ?? process.platform;
|
|
182
|
+
const config = await getConfigFn();
|
|
183
|
+
const homeAbs = absFromConfigRaw(config['aifabrix-home']);
|
|
184
|
+
const workAbs = absFromConfigRaw(config['aifabrix-work']);
|
|
185
|
+
|
|
186
|
+
if (platform === 'win32') {
|
|
187
|
+
await applyWindowsUserEnv(homeAbs, workAbs, overrides.execFile || execFile);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
await applyPosixShellEnv(homeAbs, workAbs, overrides);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
module.exports = {
|
|
195
|
+
registerAifabrixShellEnvFromConfig,
|
|
196
|
+
buildPosixShellEnvBody,
|
|
197
|
+
buildProfileBlock,
|
|
198
|
+
shSingleQuoted,
|
|
199
|
+
psSetUserEnvStatement,
|
|
200
|
+
ensureProfileShellBlock,
|
|
201
|
+
SHELL_ENV_BASENAME,
|
|
202
|
+
BLOCK_BEGIN,
|
|
203
|
+
BLOCK_END
|
|
204
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote shared builder vs local Docker Desktop — developer-id rules (plan 018).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Fail fast when remote-server is non-localhost but developer-id is not a positive integer
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {string|null|undefined} remoteServer - remote-server from config (URL or host)
|
|
13
|
+
* @returns {boolean} True when hostname is set and not localhost / loopback only
|
|
14
|
+
*/
|
|
15
|
+
function remoteServerHostIsNonLocalhost(remoteServer) {
|
|
16
|
+
const raw =
|
|
17
|
+
remoteServer === null || remoteServer === undefined ? '' : String(remoteServer).trim();
|
|
18
|
+
if (!raw) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(raw) ? raw : `https://${raw}`;
|
|
23
|
+
const u = new URL(withScheme);
|
|
24
|
+
let h = (u.hostname || '').toLowerCase();
|
|
25
|
+
if (h.startsWith('[') && h.endsWith(']')) {
|
|
26
|
+
h = h.slice(1, -1);
|
|
27
|
+
}
|
|
28
|
+
if (!h) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
if (h === 'localhost' || h === '127.0.0.1' || h === '::1') {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
} catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Host for user-facing messages (no path, no credentials).
|
|
42
|
+
* @param {string|null|undefined} remoteServer
|
|
43
|
+
* @returns {string}
|
|
44
|
+
*/
|
|
45
|
+
function remoteServerDisplayHost(remoteServer) {
|
|
46
|
+
const raw =
|
|
47
|
+
remoteServer === null || remoteServer === undefined ? '' : String(remoteServer).trim();
|
|
48
|
+
if (!raw) {
|
|
49
|
+
return '(remote-server)';
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(raw) ? raw : `https://${raw}`;
|
|
53
|
+
const u = new URL(withScheme);
|
|
54
|
+
return u.host || raw;
|
|
55
|
+
} catch {
|
|
56
|
+
return raw;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const REMOTE_DEV_ID_BODY =
|
|
61
|
+
'Remote builder at %s requires a positive developer-id (1, 2, 3, …).\n' +
|
|
62
|
+
'Developer-id 0 is only supported for local Docker Desktop (localhost).\n' +
|
|
63
|
+
'Set developer-id in ~/.aifabrix/config.yaml to match your dev account (e.g. 1 for dev01), ' +
|
|
64
|
+
'or run onboarding against the builder: aifabrix dev init …';
|
|
65
|
+
|
|
66
|
+
const REMOTE_DEV_ID_TAIL =
|
|
67
|
+
'\n\nIf you meant to work only on this machine, set remote-server to use localhost or remove the shared builder URL.';
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* When remote-server points at a shared (non-local) builder, require developer-id ≥ 1.
|
|
71
|
+
* @param {string|null|undefined} remoteServer
|
|
72
|
+
* @param {string|number|null|undefined} developerIdRaw
|
|
73
|
+
* @throws {Error} When remote builder and id missing, non-numeric, or < 1
|
|
74
|
+
*/
|
|
75
|
+
function assertRemoteBuilderDeveloperId(remoteServer, developerIdRaw) {
|
|
76
|
+
if (!remoteServerHostIsNonLocalhost(remoteServer)) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const host = remoteServerDisplayHost(remoteServer);
|
|
80
|
+
const head = REMOTE_DEV_ID_BODY.replace('%s', host);
|
|
81
|
+
|
|
82
|
+
if (developerIdRaw === null || developerIdRaw === undefined || developerIdRaw === '') {
|
|
83
|
+
throw new Error(head + REMOTE_DEV_ID_TAIL);
|
|
84
|
+
}
|
|
85
|
+
const s = String(developerIdRaw).trim();
|
|
86
|
+
if (!/^\d+$/.test(s)) {
|
|
87
|
+
throw new Error(head + REMOTE_DEV_ID_TAIL);
|
|
88
|
+
}
|
|
89
|
+
const n = parseInt(s, 10);
|
|
90
|
+
if (n < 1) {
|
|
91
|
+
throw new Error(head + REMOTE_DEV_ID_TAIL);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = {
|
|
96
|
+
remoteServerHostIsNonLocalhost,
|
|
97
|
+
remoteServerDisplayHost,
|
|
98
|
+
assertRemoteBuilderDeveloperId
|
|
99
|
+
};
|
|
@@ -4,35 +4,131 @@
|
|
|
4
4
|
* @version 2.0.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
const path = require('path');
|
|
7
8
|
const config = require('../core/config');
|
|
8
|
-
const { getCertDir, readClientCertPem } = require('./dev-cert-helper');
|
|
9
|
-
const { getConfigDirForPaths } = require('./paths');
|
|
9
|
+
const { getCertDir, readClientCertPem, readServerCaPem } = require('./dev-cert-helper');
|
|
10
|
+
const { getConfigDirForPaths, getAifabrixHome, getAifabrixWork } = require('./paths');
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
13
|
+
* Single API object so resolveSharedSecretsEndpoint and callers share one getRemoteDevAuth
|
|
14
|
+
* (Jest spies and partial mocks work reliably).
|
|
15
|
+
*/
|
|
16
|
+
const remoteDevAuth = {
|
|
17
|
+
/**
|
|
18
|
+
* Check if a string is an http(s) URL (for aifabrix-secrets remote mode).
|
|
19
|
+
* @param {string} value - Config value
|
|
20
|
+
* @returns {boolean}
|
|
21
|
+
*/
|
|
22
|
+
isRemoteSecretsUrl(value) {
|
|
23
|
+
return typeof value === 'string' && (value.startsWith('http://') || value.startsWith('https://'));
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get Builder Server URL, client cert PEM, and optional dev root CA when remote is configured; otherwise null.
|
|
28
|
+
* serverCaPem comes from ca.pem (saved at dev init); required for Node TLS to private-CA servers.
|
|
29
|
+
* Use for cert-authenticated dev API calls (settings, users, ssh-keys, secrets).
|
|
30
|
+
* @returns {Promise<{ serverUrl: string, clientCertPem: string, serverCaPem: string|null }|null>}
|
|
31
|
+
*/
|
|
32
|
+
async getRemoteDevAuth() {
|
|
33
|
+
const serverUrl = await config.getRemoteServer();
|
|
34
|
+
if (!serverUrl) return null;
|
|
35
|
+
const devId = await config.getDeveloperId();
|
|
36
|
+
const certDir = getCertDir(getConfigDirForPaths(), devId);
|
|
37
|
+
const clientCertPem = readClientCertPem(certDir);
|
|
38
|
+
if (!clientCertPem) return null;
|
|
39
|
+
const serverCaPem = readServerCaPem(certDir);
|
|
40
|
+
return { serverUrl, clientCertPem, serverCaPem };
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Resolve where shared secrets read/write should go. After dev init, config may still hold a
|
|
45
|
+
* server-side filesystem path from GET /api/dev/settings (not an http URL) if merge did not rewrite it.
|
|
46
|
+
* Those paths are not writable on the developer machine; use the Builder secrets API instead when
|
|
47
|
+
* remote-server + client cert are configured and the path is not under aifabrix-home or aifabrix-work.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} configuredPath - config.yaml aifabrix-secrets
|
|
50
|
+
* @returns {Promise<string>} Same string for local file targets, or https?://…/api/dev/secrets for remote API
|
|
51
|
+
*/
|
|
52
|
+
async resolveSharedSecretsEndpoint(configuredPath) {
|
|
53
|
+
if (!configuredPath || typeof configuredPath !== 'string') return configuredPath;
|
|
54
|
+
const trimmed = configuredPath.trim();
|
|
55
|
+
if (!trimmed) return configuredPath;
|
|
56
|
+
if (remoteDevAuth.isRemoteSecretsUrl(trimmed)) return trimmed.replace(/\/+$/, '');
|
|
57
|
+
const auth = await remoteDevAuth.getRemoteDevAuth();
|
|
58
|
+
if (!auth) return configuredPath;
|
|
59
|
+
const abs = normalizeSharedSecretsFilePath(trimmed);
|
|
60
|
+
if (!abs) return configuredPath;
|
|
61
|
+
const home = path.normalize(getAifabrixHome());
|
|
62
|
+
if (isPathUnderDir(abs, home)) return configuredPath;
|
|
63
|
+
const work = getAifabrixWork();
|
|
64
|
+
if (work) {
|
|
65
|
+
const w = path.normalize(work);
|
|
66
|
+
if (isPathUnderDir(abs, w)) return configuredPath;
|
|
67
|
+
}
|
|
68
|
+
const base = String(auth.serverUrl).replace(/\/+$/, '');
|
|
69
|
+
return `${base}/api/dev/secrets`;
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Hostname from a resolved shared-secrets API URL (for CLI labels).
|
|
74
|
+
* @param {string} secretsEndpointUrl - e.g. https://builder02.local/api/dev/secrets
|
|
75
|
+
* @returns {string|null} Hostname, or null if the URL cannot be parsed
|
|
76
|
+
*/
|
|
77
|
+
getSharedSecretsRemoteHostname(secretsEndpointUrl) {
|
|
78
|
+
try {
|
|
79
|
+
const u = new URL(String(secretsEndpointUrl));
|
|
80
|
+
return u.hostname || null;
|
|
81
|
+
} catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Titles/messages for `secret list --shared` when the store is remote.
|
|
88
|
+
* @param {string} secretsEndpointUrl - Resolved secrets API URL
|
|
89
|
+
* @returns {{ title: string, emptyMessage: string }}
|
|
90
|
+
*/
|
|
91
|
+
getSharedSecretsRemoteListLabels(secretsEndpointUrl) {
|
|
92
|
+
const host = remoteDevAuth.getSharedSecretsRemoteHostname(secretsEndpointUrl);
|
|
93
|
+
if (host) {
|
|
94
|
+
return {
|
|
95
|
+
title: `Shared secrets (remote - ${host})`,
|
|
96
|
+
emptyMessage: `No shared secrets (remote - ${host}).`
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
title: 'Shared secrets (remote)',
|
|
101
|
+
emptyMessage: 'No shared secrets (remote).'
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* True when normalized file path is dir or a descendant of base (same volume semantics as path relative checks).
|
|
108
|
+
* @param {string} filePath - Normalized absolute path
|
|
109
|
+
* @param {string} baseDir - Normalized absolute directory
|
|
14
110
|
* @returns {boolean}
|
|
15
111
|
*/
|
|
16
|
-
function
|
|
17
|
-
|
|
112
|
+
function isPathUnderDir(filePath, baseDir) {
|
|
113
|
+
if (!filePath || !baseDir) return false;
|
|
114
|
+
const f = filePath;
|
|
115
|
+
const d = baseDir;
|
|
116
|
+
if (f === d) return true;
|
|
117
|
+
const prefix = d.endsWith(path.sep) ? d : d + path.sep;
|
|
118
|
+
return f.startsWith(prefix);
|
|
18
119
|
}
|
|
19
120
|
|
|
20
121
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* @returns {
|
|
122
|
+
* Normalize configured shared-secrets file path for "is this on the laptop?" checks.
|
|
123
|
+
* @param {string} configured - Raw config value
|
|
124
|
+
* @returns {string} Absolute normalized path
|
|
24
125
|
*/
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
if (!
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (!clientCertPem) return null;
|
|
32
|
-
return { serverUrl, clientCertPem };
|
|
126
|
+
function normalizeSharedSecretsFilePath(configured) {
|
|
127
|
+
const trimmed = String(configured || '').trim();
|
|
128
|
+
if (!trimmed) return '';
|
|
129
|
+
return path.isAbsolute(trimmed)
|
|
130
|
+
? path.normalize(trimmed)
|
|
131
|
+
: path.normalize(path.resolve(process.cwd(), trimmed));
|
|
33
132
|
}
|
|
34
133
|
|
|
35
|
-
module.exports =
|
|
36
|
-
isRemoteSecretsUrl,
|
|
37
|
-
getRemoteDevAuth
|
|
38
|
-
};
|
|
134
|
+
module.exports = remoteDevAuth;
|
|
@@ -11,33 +11,85 @@ const config = require('../core/config');
|
|
|
11
11
|
const { getCertDir } = require('./dev-cert-helper');
|
|
12
12
|
const { getConfigDirForPaths } = require('./paths');
|
|
13
13
|
|
|
14
|
+
function devTlsCertPaths(certDir) {
|
|
15
|
+
return {
|
|
16
|
+
certPath: path.join(certDir, 'cert.pem'),
|
|
17
|
+
keyPath: path.join(certDir, 'key.pem'),
|
|
18
|
+
caPath: path.join(certDir, 'ca.pem')
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function missingClientTlsError(trimmed, certDir) {
|
|
23
|
+
return new Error(
|
|
24
|
+
`docker-endpoint is set (${trimmed}) but client TLS material is missing in ${certDir}. ` +
|
|
25
|
+
'Place cert.pem and key.pem there (from Builder Server issue-cert or `AIFABRIX_DEV_ISSUE_PIN`), ' +
|
|
26
|
+
'or enable TLS skip-verify (`docker-tls-skip-verify: true` or `AIFABRIX_DOCKER_TLS_SKIP_VERIFY=1`) ' +
|
|
27
|
+
'if the daemon does not require client certificates. With skip-verify and no ca.pem, DOCKER_TLS_VERIFY=0. ' +
|
|
28
|
+
'Clear docker-endpoint only if you intend to use the local Docker daemon.'
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function missingCaError(trimmed, certDir) {
|
|
33
|
+
return new Error(
|
|
34
|
+
`docker-endpoint is set (${trimmed}) but ca.pem is missing in ${certDir} and docker-tls-skip-verify is not enabled. ` +
|
|
35
|
+
'Add ca.pem (daemon/CA PEM), or for a self-signed Docker API set docker-tls-skip-verify: true in ~/.aifabrix/config.yaml ' +
|
|
36
|
+
'(or AIFABRIX_DOCKER_TLS_SKIP_VERIFY=1). Skip-verify uses TLS but does not verify the daemon certificate — use only on trusted networks.'
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
14
40
|
/**
|
|
15
|
-
* If remote Docker is configured (docker-endpoint
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
41
|
+
* If remote Docker is configured (docker-endpoint set), returns env vars for Docker CLI:
|
|
42
|
+
* DOCKER_HOST, DOCKER_TLS_VERIFY, and optionally DOCKER_CERT_PATH when client certs exist.
|
|
43
|
+
* When docker-endpoint is set, we do not fall back to the local daemon without that endpoint
|
|
44
|
+
* (avoids accidentally using Docker Desktop while the dev profile targets a remote engine).
|
|
45
|
+
*
|
|
46
|
+
* When **TLS skip-verify** is enabled (config or env) and **ca.pem is missing**, client cert/key are
|
|
47
|
+
* optional: Docker can use DOCKER_TLS_VERIFY=0 with no client certs if the daemon allows it.
|
|
48
|
+
* If **ca.pem is present** (e.g. from Builder Server issue-cert), the daemon certificate is always
|
|
49
|
+
* verified (DOCKER_TLS_VERIFY=1) even when skip-verify is set — better security once a trust anchor exists.
|
|
19
50
|
*
|
|
20
|
-
*
|
|
51
|
+
* Without skip-verify, cert.pem, key.pem, and ca.pem are required in the dev cert directory.
|
|
52
|
+
*
|
|
53
|
+
* @returns {Promise<Object>} Env overlay (empty when docker-endpoint is not set)
|
|
54
|
+
* @throws {Error} When docker-endpoint is set but required TLS material is missing
|
|
21
55
|
*/
|
|
22
56
|
async function getRemoteDockerEnv() {
|
|
23
57
|
const endpoint = await config.getDockerEndpoint();
|
|
24
58
|
if (!endpoint || typeof endpoint !== 'string' || !endpoint.trim()) {
|
|
25
59
|
return {};
|
|
26
60
|
}
|
|
27
|
-
const
|
|
28
|
-
const certDir = getCertDir(getConfigDirForPaths(),
|
|
29
|
-
const certPath =
|
|
30
|
-
const keyPath = path.join(certDir, 'key.pem');
|
|
31
|
-
const caPath = path.join(certDir, 'ca.pem');
|
|
61
|
+
const trimmed = endpoint.trim();
|
|
62
|
+
const certDir = getCertDir(getConfigDirForPaths(), await config.getDeveloperId());
|
|
63
|
+
const { certPath, keyPath, caPath } = devTlsCertPaths(certDir);
|
|
32
64
|
const fs = require('fs');
|
|
33
|
-
|
|
34
|
-
|
|
65
|
+
const skipVerify = await config.getDockerTlsSkipVerify();
|
|
66
|
+
const hasClient = fs.existsSync(certPath) && fs.existsSync(keyPath);
|
|
67
|
+
const hasCa = fs.existsSync(caPath);
|
|
68
|
+
|
|
69
|
+
if (!hasClient) {
|
|
70
|
+
if (!skipVerify) throw missingClientTlsError(trimmed, certDir);
|
|
71
|
+
return { DOCKER_HOST: trimmed, DOCKER_TLS_VERIFY: '0' };
|
|
35
72
|
}
|
|
73
|
+
if (!hasCa && !skipVerify) throw missingCaError(trimmed, certDir);
|
|
74
|
+
const verifyDaemon = hasCa;
|
|
36
75
|
return {
|
|
37
|
-
DOCKER_HOST:
|
|
38
|
-
DOCKER_TLS_VERIFY: '1',
|
|
76
|
+
DOCKER_HOST: trimmed,
|
|
77
|
+
DOCKER_TLS_VERIFY: verifyDaemon ? '1' : '0',
|
|
39
78
|
DOCKER_CERT_PATH: certDir
|
|
40
79
|
};
|
|
41
80
|
}
|
|
42
81
|
|
|
43
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Full environment for child_process exec/spawn: process.env merged with remote Docker vars when configured.
|
|
84
|
+
* @returns {Promise<Object>}
|
|
85
|
+
*/
|
|
86
|
+
async function getDockerExecEnv() {
|
|
87
|
+
const overlay = await getRemoteDockerEnv();
|
|
88
|
+
const merged = { ...process.env };
|
|
89
|
+
if (overlay.DOCKER_HOST && !Object.prototype.hasOwnProperty.call(overlay, 'DOCKER_CERT_PATH')) {
|
|
90
|
+
delete merged.DOCKER_CERT_PATH;
|
|
91
|
+
}
|
|
92
|
+
return { ...merged, ...overlay };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = { getRemoteDockerEnv, getDockerExecEnv };
|
|
@@ -14,16 +14,25 @@ const config = require('../core/config');
|
|
|
14
14
|
* @returns {Promise<Object|null>} Key-value secrets from API or null
|
|
15
15
|
*/
|
|
16
16
|
async function loadRemoteSharedSecrets() {
|
|
17
|
-
const
|
|
17
|
+
const remoteDevAuth = require('./remote-dev-auth');
|
|
18
18
|
const devApi = require('../api/dev.api');
|
|
19
19
|
const configSecretsPath = await config.getSecretsPath();
|
|
20
|
-
if (!configSecretsPath
|
|
20
|
+
if (!configSecretsPath) {
|
|
21
21
|
return null;
|
|
22
22
|
}
|
|
23
|
-
const
|
|
23
|
+
const endpoint = await remoteDevAuth.resolveSharedSecretsEndpoint(configSecretsPath);
|
|
24
|
+
if (!remoteDevAuth.isRemoteSecretsUrl(endpoint)) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const auth = await remoteDevAuth.getRemoteDevAuth();
|
|
24
28
|
if (!auth) return null;
|
|
25
29
|
try {
|
|
26
|
-
const items = await devApi.listSecrets(
|
|
30
|
+
const items = await devApi.listSecrets(
|
|
31
|
+
auth.serverUrl,
|
|
32
|
+
auth.clientCertPem,
|
|
33
|
+
auth.serverCaPem || undefined,
|
|
34
|
+
endpoint
|
|
35
|
+
);
|
|
27
36
|
if (!Array.isArray(items)) return null;
|
|
28
37
|
const obj = {};
|
|
29
38
|
for (const item of items) {
|