@aifabrix/builder 2.44.6 → 2.45.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/cli-layout.mdc +7 -3
- package/.cursor/rules/docs-rules.mdc +3 -1
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
- package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
- package/jest.projects.js +100 -1
- package/lib/api/agent-metadata-validation.api.js +93 -0
- package/lib/api/audit-api-helpers.js +68 -0
- package/lib/api/audit.api.js +119 -0
- package/lib/api/auth-cache.api.js +44 -0
- package/lib/api/certificates.api.js +4 -4
- package/lib/api/credential.api.js +2 -2
- package/lib/api/dataplane-health.api.js +137 -0
- package/lib/api/dataplane-sync.api.js +27 -0
- package/lib/api/datasources-core.api.js +21 -18
- package/lib/api/datasources-extended.api.js +22 -22
- package/lib/api/dimensions.api.js +3 -0
- package/lib/api/external-systems.api.js +13 -13
- package/lib/api/governance-scenarios.api.js +39 -0
- package/lib/api/groups.api.js +79 -0
- package/lib/api/index.js +45 -2
- package/lib/api/pipeline.api.js +4 -4
- package/lib/api/protection.api.js +157 -0
- package/lib/api/records-bulk.api.js +30 -0
- package/lib/api/records-search-parse.js +49 -0
- package/lib/api/records-search.api.js +29 -0
- package/lib/api/types/agent-metadata-validation.types.js +67 -0
- package/lib/api/types/audit.types.js +72 -0
- package/lib/api/types/dataplane-health.types.js +27 -0
- package/lib/api/types/dimensions.types.js +2 -0
- package/lib/api/types/governance-scenarios.types.js +39 -0
- package/lib/api/types/protection.types.js +69 -0
- package/lib/api/types/records-bulk.types.js +35 -0
- package/lib/api/types/records-search.types.js +28 -0
- package/lib/api/user-groups.api.js +50 -0
- package/lib/api/users.api.js +95 -0
- package/lib/api/validation-run.api.js +3 -3
- package/lib/api/validation-runner.js +3 -3
- package/lib/api/wizard-platform.api.js +3 -3
- package/lib/api/wizard.api.js +21 -21
- package/lib/app/deploy.js +34 -19
- package/lib/app/helpers.js +3 -3
- package/lib/app/index.js +3 -3
- package/lib/app/register.js +7 -6
- package/lib/app/restart-display.js +52 -21
- package/lib/app/rotate-secret.js +7 -6
- package/lib/app/run-helpers.js +15 -8
- package/lib/app/run.js +57 -9
- package/lib/app/show-display.js +7 -0
- package/lib/app/show.js +87 -5
- package/lib/build/index.js +9 -5
- package/lib/cli/index.js +4 -2
- package/lib/cli/infra-guided.js +62 -28
- package/lib/cli/installation-log-command.js +73 -0
- package/lib/cli/setup-app.help.js +50 -2
- package/lib/cli/setup-app.js +44 -25
- package/lib/cli/setup-app.test-commands.js +80 -21
- package/lib/cli/setup-auth.js +124 -52
- package/lib/cli/setup-external-system.js +37 -3
- package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
- package/lib/cli/setup-infra-up-platform-action.js +131 -0
- package/lib/cli/setup-infra.js +60 -119
- package/lib/cli/setup-platform.js +1 -1
- package/lib/cli/setup-utility-help-after.js +45 -0
- package/lib/cli/setup-utility-resolve.js +132 -0
- package/lib/cli/setup-utility.js +148 -116
- package/lib/commands/app-logs.js +81 -33
- package/lib/commands/auth-config.js +116 -18
- package/lib/commands/auth-status-dataplane-version.js +231 -0
- package/lib/commands/auth-status-display.js +94 -0
- package/lib/commands/auth-status.js +70 -6
- package/lib/commands/credential-push.js +10 -5
- package/lib/commands/datasource-load-export-cli.js +178 -0
- package/lib/commands/datasource-test-trust-cli.js +114 -0
- package/lib/commands/datasource-unified-test-cli.js +25 -17
- package/lib/commands/datasource-unified-test-cli.options.js +18 -3
- package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
- package/lib/commands/datasource-verify-audit-cli.js +96 -0
- package/lib/commands/datasource.js +68 -1
- package/lib/commands/dimension-value.js +43 -13
- package/lib/commands/dimension.js +38 -7
- package/lib/commands/identity-apply-sync.js +131 -0
- package/lib/commands/identity-group-cli.js +146 -0
- package/lib/commands/identity-membership-role-cache.js +175 -0
- package/lib/commands/identity-shared.js +94 -0
- package/lib/commands/identity-user-cli.js +158 -0
- package/lib/commands/identity.js +43 -0
- package/lib/commands/login.js +46 -16
- package/lib/commands/protection-cli-leaves.js +286 -0
- package/lib/commands/protection.js +50 -0
- package/lib/commands/repair.js +13 -3
- package/lib/commands/setup-modes.js +19 -6
- package/lib/commands/setup-prompts.js +41 -8
- package/lib/commands/setup.js +114 -9
- package/lib/commands/teardown.js +54 -5
- package/lib/commands/test-e2e-external.js +40 -9
- package/lib/commands/test-governance-cli-options.js +47 -0
- package/lib/commands/test-governance-command-action.js +55 -0
- package/lib/commands/test-governance-external.js +88 -0
- package/lib/commands/test-trust-cli-options.js +32 -0
- package/lib/commands/test-trust-command-action.js +118 -0
- package/lib/commands/test-trust-external.js +127 -0
- package/lib/commands/up-common.js +48 -14
- package/lib/commands/up-dataplane.js +21 -18
- package/lib/commands/up-miso.js +12 -8
- package/lib/commands/upload.js +38 -15
- package/lib/commands/wizard-core.js +3 -0
- package/lib/core/audit-logger.js +1 -34
- package/lib/core/config-admin-email.js +56 -0
- package/lib/core/config-device-dataplane.js +185 -0
- package/lib/core/config-normalize.js +60 -0
- package/lib/core/config-registered-controller-urls.js +54 -0
- package/lib/core/config.js +33 -50
- package/lib/core/secrets-ensure-infra.js +1 -1
- package/lib/core/secrets-env-content.js +86 -90
- package/lib/core/secrets-env-declarative-expand.js +170 -0
- package/lib/core/secrets-env-write.js +2 -0
- package/lib/core/secrets-load.js +106 -102
- package/lib/datasource/agent-trust-debug-log.js +45 -0
- package/lib/datasource/agent-trust-fetch.js +115 -0
- package/lib/datasource/agent-trust-map.js +101 -0
- package/lib/datasource/agent-trust-mock.js +44 -0
- package/lib/datasource/agent-trust-run.js +93 -0
- package/lib/datasource/agent-trust-timeout.js +25 -0
- package/lib/datasource/audit-evidence-display.js +80 -0
- package/lib/datasource/audit-evidence-extract-debug.js +103 -0
- package/lib/datasource/audit-evidence-extract.js +177 -0
- package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
- package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
- package/lib/datasource/audit-evidence-poll.js +109 -0
- package/lib/datasource/audit-evidence-run.js +177 -0
- package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
- package/lib/datasource/audit-evidence-verify-matrix.js +177 -0
- package/lib/datasource/audit-evidence-verify.js +108 -0
- package/lib/datasource/bulk-loader-run-batches.js +88 -0
- package/lib/datasource/bulk-loader-service-retry.js +52 -0
- package/lib/datasource/bulk-loader-service.js +127 -0
- package/lib/datasource/capability/run-capability-dimension.js +2 -1
- package/lib/datasource/datasource-exporter-resolve.js +82 -0
- package/lib/datasource/datasource-exporter-service.js +144 -0
- package/lib/datasource/datasource-identifier.js +148 -0
- package/lib/datasource/datasource-load-export-context.js +92 -0
- package/lib/datasource/integration-context.js +6 -4
- package/lib/datasource/local-data-paths.js +145 -0
- package/lib/datasource/log-cleaner.js +241 -0
- package/lib/datasource/log-viewer.js +111 -6
- package/lib/datasource/record-file-parser.js +85 -0
- package/lib/datasource/record-mapper.js +129 -0
- package/lib/datasource/resolve-app.js +74 -55
- package/lib/datasource/test-e2e.js +26 -2
- package/lib/datasource/unified-validation-run-resolve.js +45 -12
- package/lib/datasource/unified-validation-run.js +43 -17
- package/lib/datasource/validate.js +37 -20
- package/lib/external-system/deploy.js +5 -1
- package/lib/external-system/test-system-level.js +25 -4
- package/lib/external-system/test.js +3 -1
- package/lib/governance/governance-pack-loader.js +70 -0
- package/lib/governance/governance-run-display.js +119 -0
- package/lib/identity/identity-apply-core.js +108 -0
- package/lib/identity/identity-apply-phases.js +244 -0
- package/lib/identity/identity-apply-service.js +77 -0
- package/lib/identity/identity-csv-parser.js +114 -0
- package/lib/internal/fs-real-sync.js +29 -1
- package/lib/internal/node-fs.js +2 -0
- package/lib/protection/auth-context.js +45 -0
- package/lib/protection/convert-batch.js +90 -0
- package/lib/protection/load.js +41 -0
- package/lib/protection/paths.js +158 -0
- package/lib/protection/preflight-datasource-ready.js +46 -0
- package/lib/protection/protection-create-probe-lines.js +142 -0
- package/lib/protection/protection-create-probes.js +95 -0
- package/lib/protection/protection-create-scaffold.js +281 -0
- package/lib/protection/protection-display-helpers.js +445 -0
- package/lib/protection/protection-display.js +215 -0
- package/lib/protection/protection-preset-registry.js +97 -0
- package/lib/protection/report-exit.js +62 -0
- package/lib/protection/resolve.js +202 -0
- package/lib/protection/run-commands-validate.js +130 -0
- package/lib/protection/run-commands.js +216 -0
- package/lib/protection/run-protection-create-helpers.js +134 -0
- package/lib/protection/run-protection-create-probes-flow.js +43 -0
- package/lib/protection/run-protection-create.js +191 -0
- package/lib/protection/scope.js +22 -0
- package/lib/protection/sync-after-upload.js +64 -0
- package/lib/protection/upload-batch.js +127 -0
- package/lib/protection/validate-batch.js +113 -0
- package/lib/protection/validate-local.js +65 -0
- package/lib/resolvers/dimension-file.js +29 -4
- package/lib/schema/application-schema.json +4 -0
- package/lib/schema/external-datasource.schema.json +47 -13
- package/lib/schema/flag-map-validation-run.json +2 -2
- package/lib/schema/infra.parameter.yaml +10 -0
- package/lib/schema/protection.schema.json +374 -0
- package/lib/schema/type/credential-field-registry.json +636 -0
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/agent-trust-run-display.js +231 -0
- package/lib/utils/agent-trust-run-exit.js +59 -0
- package/lib/utils/agent-trust-wait-ui.js +94 -0
- package/lib/utils/app-config-resolver.js +24 -1
- package/lib/utils/applications-config-defaults.js +206 -0
- package/lib/utils/auth-config-validator.js +2 -12
- package/lib/utils/bash-secret-env.js +1 -1
- package/lib/utils/cli-exit-codes.js +30 -0
- package/lib/utils/cli-sync-options.js +33 -0
- package/lib/utils/compose-generate-docker-compose.js +111 -6
- package/lib/utils/compose-generator.js +17 -8
- package/lib/utils/controller-url.js +50 -7
- package/lib/utils/credential-push-ui.js +90 -0
- package/lib/utils/dataplane-cli-version-gate.js +202 -0
- package/lib/utils/dataplane-cli-version-help.js +74 -0
- package/lib/utils/datasource-export-display.js +137 -0
- package/lib/utils/datasource-load-display.js +174 -0
- package/lib/utils/datasource-load-export-ui.js +120 -0
- package/lib/utils/datasource-test-run-capability-scope.js +3 -3
- package/lib/utils/datasource-test-run-display.js +3 -3
- package/lib/utils/datasource-test-run-tty-log.js +5 -1
- package/lib/utils/env-copy.js +99 -14
- package/lib/utils/env-template.js +5 -1
- package/lib/utils/health-check-url.js +18 -15
- package/lib/utils/health-check.js +7 -5
- package/lib/utils/help-builder.js +30 -24
- package/lib/utils/infra-optional-service-flags.js +69 -0
- package/lib/utils/installation-log-core.js +282 -0
- package/lib/utils/installation-log-record.js +237 -0
- package/lib/utils/installation-log.js +123 -0
- package/lib/utils/log-redaction.js +105 -0
- package/lib/utils/manifest-location.js +164 -0
- package/lib/utils/manifest-source-emit.js +184 -0
- package/lib/utils/paths.js +238 -89
- package/lib/utils/remote-secrets-loader.js +7 -1
- package/lib/utils/run-cli-flags.js +29 -0
- package/lib/utils/secrets-canonical.js +10 -3
- package/lib/utils/secrets-path.js +3 -4
- package/lib/utils/secrets-utils.js +20 -10
- package/lib/utils/semver-compare.js +127 -0
- package/lib/utils/system-builder-root.js +10 -2
- package/lib/utils/upload-pipeline-ui.js +91 -0
- package/lib/utils/url-declarative-public-base.js +80 -12
- package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
- package/lib/utils/url-declarative-resolve-build.js +24 -393
- package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
- package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
- package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
- package/lib/utils/url-declarative-resolve.js +47 -7
- package/lib/utils/url-declarative-runtime-base-path.js +21 -1
- package/lib/utils/urls-local-registry-scan.js +103 -0
- package/lib/utils/urls-local-registry.js +161 -90
- package/lib/utils/validation-poll-ui.js +14 -7
- package/lib/validation/wizard-config-validator.js +58 -19
- package/package.json +3 -1
- package/templates/applications/dataplane/application.yaml +4 -0
- package/templates/applications/dataplane/env.template +13 -1
- package/templates/applications/dataplane/rbac.yaml +4 -0
- package/templates/applications/miso-controller/application.yaml +2 -0
- package/templates/applications/miso-controller/env.template +27 -29
- package/templates/external-system/README.md.hbs +56 -8
- package/.npmrc.token +0 -1
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical Builder CLI exit codes for scriptable commands.
|
|
3
|
+
*
|
|
4
|
+
* Documented in `docs/commands/authentication.md`. Use in
|
|
5
|
+
* `aifabrix auth status --validate` and any future `--validate`-style flag.
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Exit-code constants for scriptable Builder CLI commands
|
|
8
|
+
* @author AI Fabrix Team
|
|
9
|
+
* @version 2.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
/** Success. */
|
|
15
|
+
const EXIT_OK = 0;
|
|
16
|
+
|
|
17
|
+
/** Not authenticated / invalid token. */
|
|
18
|
+
const EXIT_NOT_AUTHENTICATED = 1;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Authenticated but installed Builder CLI version is older than the dataplane
|
|
22
|
+
* minimum advertised by `GET /api/v1/health → minBuilderCliVersion` (plan 403.0).
|
|
23
|
+
*/
|
|
24
|
+
const EXIT_CLI_VERSION_INCOMPATIBLE = 3;
|
|
25
|
+
|
|
26
|
+
module.exports = {
|
|
27
|
+
EXIT_OK,
|
|
28
|
+
EXIT_NOT_AUTHENTICATED,
|
|
29
|
+
EXIT_CLI_VERSION_INCOMPATIBLE
|
|
30
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Detect skip-sync from Commander options and raw argv (--no-sync → sync: false).
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* True when the user passed `--no-sync` or Commander set `sync: false` / `noSync: true`.
|
|
11
|
+
* Do not use for `protection upload --no-sync` (post-upload datasource sync only).
|
|
12
|
+
*
|
|
13
|
+
* @param {Object|null|undefined} options - Commander-parsed options
|
|
14
|
+
* @param {import('commander').Command} [cmd] - Commander command (for rawArgs fallback)
|
|
15
|
+
* @returns {boolean}
|
|
16
|
+
*/
|
|
17
|
+
function cliOptsSkipSync(options, cmd) {
|
|
18
|
+
if (!options || typeof options !== 'object') {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (options.noSync === true) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
if (options.sync === false) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
const raw = cmd && Array.isArray(cmd.rawArgs) ? cmd.rawArgs : [];
|
|
28
|
+
return raw.includes('--no-sync');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = {
|
|
32
|
+
cliOptsSkipSync
|
|
33
|
+
};
|
|
@@ -15,6 +15,99 @@ const paths = require('./paths');
|
|
|
15
15
|
const { getInfraDirName } = require('../infrastructure/helpers');
|
|
16
16
|
const { resolveMisoEnvironment } = require('./compose-miso-env');
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* When running a pulled Python image (no bind-mounted source), the image CMD may invoke the
|
|
20
|
+
* `uvicorn` console script, which is not always on PATH. Derive a `python -m uvicorn` command from
|
|
21
|
+
* `build.reloadStart` so generated compose matches the same app entrypoint as hot-reload runs.
|
|
22
|
+
*
|
|
23
|
+
* @param {string|undefined} reloadStart - Value from application.yaml `build.reloadStart`
|
|
24
|
+
* @returns {string|null} Shell command fragment (no `cd`, no `exec`; image compose prepends `exec ` in JS)
|
|
25
|
+
*/
|
|
26
|
+
function derivePythonImageStartFromReload(reloadStart) {
|
|
27
|
+
if (typeof reloadStart !== 'string' || !reloadStart.trim()) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const trimmed = reloadStart.trim().replace(/\s+--reload\b/g, '').trim();
|
|
31
|
+
if (/^python3?\s+-m\s+uvicorn\s+/i.test(trimmed)) {
|
|
32
|
+
return normalizeComposeShellPortRef(trimmed);
|
|
33
|
+
}
|
|
34
|
+
if (trimmed.startsWith('uvicorn ')) {
|
|
35
|
+
const rest = trimmed.slice('uvicorn '.length);
|
|
36
|
+
return normalizeComposeShellPortRef(`python -m uvicorn ${rest}`);
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Compose sets `PORT` in the service environment; replace `${PORT:-...}` with `$$PORT` in the
|
|
43
|
+
* fragment so the compose file avoids ambiguous `:` parsing in flow scalars and Compose expands
|
|
44
|
+
* `$$` to `$` for the container shell (which then expands `PORT` from the service environment).
|
|
45
|
+
*
|
|
46
|
+
* @param {string} cmd - Shell command fragment
|
|
47
|
+
* @returns {string} Same fragment with `${PORT:-...}` replaced by `$$PORT` (Compose escapes `$$` → `$` for the shell)
|
|
48
|
+
*/
|
|
49
|
+
function normalizeComposeShellPortRef(cmd) {
|
|
50
|
+
return cmd.replace(/\$\{PORT:-[^}]+\}/g, () => '$$PORT');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Normalizes `build.reloadStart` for Python when using a bind-mounted source in compose.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} raw - Trimmed reload command
|
|
57
|
+
* @returns {string} Command suitable for `cd /app && …` in compose
|
|
58
|
+
*/
|
|
59
|
+
function normalizePythonReloadForComposeMounted(raw) {
|
|
60
|
+
const s = normalizeComposeShellPortRef(raw);
|
|
61
|
+
if (/^python3?\s+-m\s+uvicorn\s+/i.test(s)) {
|
|
62
|
+
return s;
|
|
63
|
+
}
|
|
64
|
+
if (s.startsWith('uvicorn ')) {
|
|
65
|
+
return `python -m uvicorn ${s.slice('uvicorn '.length)}`;
|
|
66
|
+
}
|
|
67
|
+
return s;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Builds the single `reloadStartCommand` passed to compose templates (`command: …` when set).
|
|
72
|
+
*
|
|
73
|
+
* `applications.<app>.reload` in config.yaml (and `aifabrix run --reload`) only controls **bind-mount +
|
|
74
|
+
* `--reload`** via `devMountPath`. For pulled images without a mount, use optional `build.imageRun` in
|
|
75
|
+
* application.yaml, or (Python only) derive from `build.reloadStart` when `imageRun` is unset.
|
|
76
|
+
*
|
|
77
|
+
* @param {string} language - `application.yaml` build.language
|
|
78
|
+
* @param {string|null} devMountPath - Bind mount path when reload sync is active
|
|
79
|
+
* @param {string|undefined} reloadRaw - `build.reloadStart`
|
|
80
|
+
* @param {Object} [build] - `application.yaml` `build` object (`imageRun`, etc.)
|
|
81
|
+
* @returns {string|null}
|
|
82
|
+
*/
|
|
83
|
+
function buildReloadStartCommandForCompose(language, devMountPath, reloadRaw, build = {}) {
|
|
84
|
+
const buildObj = build && typeof build === 'object' ? build : {};
|
|
85
|
+
const imageRunRaw = typeof buildObj.imageRun === 'string' ? buildObj.imageRun.trim() : '';
|
|
86
|
+
const reloadTrimmed = typeof reloadRaw === 'string' ? reloadRaw.trim() : '';
|
|
87
|
+
|
|
88
|
+
if (devMountPath) {
|
|
89
|
+
if (!reloadTrimmed) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
return language === 'python' ? normalizePythonReloadForComposeMounted(reloadTrimmed) : reloadTrimmed;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (imageRunRaw) {
|
|
96
|
+
return normalizeComposeShellPortRef(imageRunRaw);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!reloadTrimmed) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (language === 'python') {
|
|
104
|
+
const imageCmd = derivePythonImageStartFromReload(reloadTrimmed);
|
|
105
|
+
return imageCmd ? `exec ${imageCmd}` : null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
18
111
|
/**
|
|
19
112
|
* Resolve infra `pgpass` path: prefer system config dir; when home differs, allow legacy layout.
|
|
20
113
|
* @param {string|number} devId - Developer id (infra vs infra-dev{n})
|
|
@@ -119,14 +212,15 @@ async function loadComposeHead(deps, appName, appConfig, options) {
|
|
|
119
212
|
* @param {object} head - From loadComposeHead
|
|
120
213
|
* @returns {object}
|
|
121
214
|
*/
|
|
122
|
-
function buildComposeLayouts(deps, appName, appConfig, head) {
|
|
215
|
+
function buildComposeLayouts(deps, appName, appConfig, head, options) {
|
|
123
216
|
const { buildNetworkAndContainerNames, buildServiceConfig, buildVolumesConfig, buildNetworksConfig } = deps;
|
|
124
217
|
const { port, imageOverride, devId, idNum, scoped, remoteServer } = head;
|
|
125
218
|
const { networkName, containerName } = buildNetworkAndContainerNames(appName, devId, idNum, scoped);
|
|
126
219
|
const serviceConfig = buildServiceConfig(appName, appConfig, port, devId, {
|
|
127
220
|
imageOverride,
|
|
128
221
|
scopeOpts: scoped,
|
|
129
|
-
remoteServer
|
|
222
|
+
remoteServer,
|
|
223
|
+
omitAppTraefikLabels: options && options.omitAppTraefikLabels === true
|
|
130
224
|
});
|
|
131
225
|
const volumesConfig = buildVolumesConfig(appName);
|
|
132
226
|
const networksConfig = buildNetworksConfig(appConfig);
|
|
@@ -161,8 +255,13 @@ async function resolveComposePathsAndSecrets(ctx) {
|
|
|
161
255
|
);
|
|
162
256
|
const devMountPath = resolveDevMountPath(options);
|
|
163
257
|
const reloadRaw = appConfig.build?.reloadStart;
|
|
164
|
-
const
|
|
165
|
-
|
|
258
|
+
const language = appConfig.build?.language || appConfig.language || 'typescript';
|
|
259
|
+
const reloadStartCommand = buildReloadStartCommandForCompose(
|
|
260
|
+
language,
|
|
261
|
+
devMountPath,
|
|
262
|
+
reloadRaw,
|
|
263
|
+
appConfig.build
|
|
264
|
+
);
|
|
166
265
|
const infraPgpassPath = resolveInfraPgpassPath(devId, paths, fsSync.existsSync);
|
|
167
266
|
const useInfraPgpass = serviceCf.requiresDatabase && fsSync.existsSync(infraPgpassPath);
|
|
168
267
|
return {
|
|
@@ -185,7 +284,7 @@ async function resolveComposePathsAndSecrets(ctx) {
|
|
|
185
284
|
*/
|
|
186
285
|
async function generateDockerComposeImpl(deps, appName, appConfig, options) {
|
|
187
286
|
const head = await loadComposeHead(deps, appName, appConfig, options);
|
|
188
|
-
const layouts = buildComposeLayouts(deps, appName, appConfig, head);
|
|
287
|
+
const layouts = buildComposeLayouts(deps, appName, appConfig, head, options);
|
|
189
288
|
const side = await resolveComposePathsAndSecrets({
|
|
190
289
|
options,
|
|
191
290
|
appName,
|
|
@@ -213,4 +312,10 @@ function createGenerateDockerCompose(deps) {
|
|
|
213
312
|
return (appName, appConfig, options) => generateDockerComposeImpl(deps, appName, appConfig, options);
|
|
214
313
|
}
|
|
215
314
|
|
|
216
|
-
module.exports = {
|
|
315
|
+
module.exports = {
|
|
316
|
+
createGenerateDockerCompose,
|
|
317
|
+
resolveInfraPgpassPath,
|
|
318
|
+
derivePythonImageStartFromReload,
|
|
319
|
+
buildReloadStartCommandForCompose,
|
|
320
|
+
normalizePythonReloadForComposeMounted
|
|
321
|
+
};
|
|
@@ -282,10 +282,16 @@ function buildRequiresConfig(config) {
|
|
|
282
282
|
* @param {string} [runExtras.imageOverride] - Full image reference for run (e.g. from --image)
|
|
283
283
|
* @param {Object|null} [runExtras.scopeOpts] - Traefik / scoped compose options
|
|
284
284
|
* @param {string|null|undefined} [runExtras.remoteServer] - For ${REMOTE_HOST} in frontDoorRouting.host
|
|
285
|
+
* @param {boolean} [runExtras.omitAppTraefikLabels] - When true (user config `traefik: false`), omit Traefik labels and BASE_PATH env from compose even if application.yaml enables frontDoorRouting
|
|
285
286
|
* @returns {Object} Service configuration
|
|
286
287
|
*/
|
|
287
288
|
function buildServiceConfig(appName, config, port, devId, runExtras = {}) {
|
|
288
|
-
const {
|
|
289
|
+
const {
|
|
290
|
+
imageOverride = null,
|
|
291
|
+
scopeOpts = null,
|
|
292
|
+
remoteServer = null,
|
|
293
|
+
omitAppTraefikLabels = false
|
|
294
|
+
} = runExtras;
|
|
289
295
|
const containerPortValue = getContainerPort(config, 3000);
|
|
290
296
|
const hostPort = port;
|
|
291
297
|
const useTraefikScope =
|
|
@@ -305,13 +311,16 @@ function buildServiceConfig(appName, config, port, devId, runExtras = {}) {
|
|
|
305
311
|
containerPort: containerPortValue, // Container port (always set, equals containerPort if exists, else port)
|
|
306
312
|
hostPort: hostPort, // Host port (options.port if provided, else config.port)
|
|
307
313
|
healthCheck,
|
|
308
|
-
traefik:
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
314
|
+
traefik:
|
|
315
|
+
omitAppTraefikLabels === true
|
|
316
|
+
? { enabled: false }
|
|
317
|
+
: buildTraefikConfig(
|
|
318
|
+
config,
|
|
319
|
+
devId,
|
|
320
|
+
scopeForHealthAndTraefik,
|
|
321
|
+
remoteServer,
|
|
322
|
+
healthCheck.path
|
|
323
|
+
),
|
|
315
324
|
...buildRequiresConfig(config)
|
|
316
325
|
};
|
|
317
326
|
}
|
|
@@ -42,8 +42,38 @@ function normalizeUrl(url) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
45
|
+
* True when `config.yaml` has a non-expired-looking device entry for this controller URL
|
|
46
|
+
* (normalized host/path comparison).
|
|
47
|
+
*
|
|
48
|
+
* @async
|
|
49
|
+
* @param {string} controllerUrl
|
|
50
|
+
* @returns {Promise<boolean>}
|
|
51
|
+
*/
|
|
52
|
+
async function hasStoredDeviceTokenForController(controllerUrl) {
|
|
53
|
+
if (!controllerUrl || typeof controllerUrl !== 'string') {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
const want = normalizeUrl(controllerUrl);
|
|
57
|
+
if (!want) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const userConfig = await config.getConfig();
|
|
62
|
+
if (!userConfig.device || typeof userConfig.device !== 'object') {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
return Object.keys(userConfig.device).some((key) => normalizeUrl(key) === want);
|
|
66
|
+
} catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get controller URL from logged-in user's device tokens.
|
|
73
|
+
* Prefers the entry under {@link config.controller} when it matches a `device` key; otherwise a
|
|
74
|
+
* single `device` entry; with multiple unrelated entries and no matching default, returns **null**
|
|
75
|
+
* (callers must not treat arbitrary key order as the active controller).
|
|
76
|
+
*
|
|
47
77
|
* @async
|
|
48
78
|
* @function getControllerUrlFromLoggedInUser
|
|
49
79
|
* @returns {Promise<string|null>} Controller URL from logged-in user, or null if not found
|
|
@@ -60,11 +90,23 @@ async function getControllerUrlFromLoggedInUser() {
|
|
|
60
90
|
return null;
|
|
61
91
|
}
|
|
62
92
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
93
|
+
const cfgController =
|
|
94
|
+
userConfig.controller && typeof userConfig.controller === 'string'
|
|
95
|
+
? normalizeUrl(userConfig.controller)
|
|
96
|
+
: '';
|
|
97
|
+
if (cfgController) {
|
|
98
|
+
const match = deviceUrls.find((u) => normalizeUrl(u) === cfgController);
|
|
99
|
+
if (match) {
|
|
100
|
+
return normalizeUrl(match);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (deviceUrls.length === 1) {
|
|
105
|
+
return normalizeUrl(deviceUrls[0]);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return null;
|
|
109
|
+
} catch {
|
|
68
110
|
return null;
|
|
69
111
|
}
|
|
70
112
|
}
|
|
@@ -109,6 +151,7 @@ async function resolveControllerUrl() {
|
|
|
109
151
|
|
|
110
152
|
module.exports = {
|
|
111
153
|
getDefaultControllerUrl,
|
|
154
|
+
hasStoredDeviceTokenForController,
|
|
112
155
|
getControllerUrlFromLoggedInUser,
|
|
113
156
|
getControllerFromConfig,
|
|
114
157
|
resolveControllerUrl
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Progress line + optional in-place spinner for credential secret push.
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const chalk = require('chalk');
|
|
10
|
+
const logger = require('./logger');
|
|
11
|
+
const { formatProgress } = require('./cli-test-layout-chalk');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {Object} [options]
|
|
15
|
+
* @param {boolean} [options.json] - When true, skip progress line and spinner
|
|
16
|
+
* @returns {boolean}
|
|
17
|
+
*/
|
|
18
|
+
function shouldShowCredentialPushProgress(options = {}) {
|
|
19
|
+
return options.json !== true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {Object} [options]
|
|
24
|
+
* @returns {boolean}
|
|
25
|
+
*/
|
|
26
|
+
function shouldUseCredentialPushSpinner(options = {}) {
|
|
27
|
+
if (!shouldShowCredentialPushProgress(options)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
return Boolean(process && process.stdout && process.stdout.isTTY);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {string} systemKey
|
|
35
|
+
* @returns {string}
|
|
36
|
+
*/
|
|
37
|
+
function buildCredentialPushSpinnerLabel(systemKey) {
|
|
38
|
+
const key = systemKey && String(systemKey).trim() ? String(systemKey).trim() : 'integration';
|
|
39
|
+
return `Pushing credential secrets for ${key}…`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {string} systemKey
|
|
44
|
+
* @returns {string}
|
|
45
|
+
*/
|
|
46
|
+
function buildCredentialPushSpinnerText(systemKey) {
|
|
47
|
+
return formatProgress(buildCredentialPushSpinnerLabel(systemKey));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Run credential push work with visible progress (⏳ line or ora with label on TTY).
|
|
52
|
+
*
|
|
53
|
+
* @async
|
|
54
|
+
* @param {Function} work - Zero-arg async function performing the push
|
|
55
|
+
* @param {Object} [opts]
|
|
56
|
+
* @param {string} [opts.systemKey]
|
|
57
|
+
* @param {boolean} [opts.json]
|
|
58
|
+
* @returns {Promise<*>}
|
|
59
|
+
*/
|
|
60
|
+
async function runWithCredentialPushSpinner(work, opts = {}) {
|
|
61
|
+
if (!shouldShowCredentialPushProgress(opts)) {
|
|
62
|
+
return work();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!shouldUseCredentialPushSpinner(opts)) {
|
|
66
|
+
logger.log(buildCredentialPushSpinnerText(opts.systemKey));
|
|
67
|
+
return work();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const ora = require('ora');
|
|
71
|
+
const label = buildCredentialPushSpinnerLabel(opts.systemKey);
|
|
72
|
+
const spinner = ora({
|
|
73
|
+
text: chalk.white(label),
|
|
74
|
+
spinner: 'dots'
|
|
75
|
+
}).start();
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
return await work();
|
|
79
|
+
} finally {
|
|
80
|
+
spinner.stop();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = {
|
|
85
|
+
shouldShowCredentialPushProgress,
|
|
86
|
+
shouldUseCredentialPushSpinner,
|
|
87
|
+
buildCredentialPushSpinnerLabel,
|
|
88
|
+
buildCredentialPushSpinnerText,
|
|
89
|
+
runWithCredentialPushSpinner
|
|
90
|
+
};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builder CLI ↔ dataplane version gate (plan 142.0).
|
|
3
|
+
*
|
|
4
|
+
* Resolves the dataplane-advertised `minBuilderCliVersion` (from cache or a
|
|
5
|
+
* fresh health probe) and compares it against the installed Builder CLI
|
|
6
|
+
* version. Used at the top of dataplane API entrypoints so an outdated CLI
|
|
7
|
+
* cannot mutate dataplane state silently.
|
|
8
|
+
*
|
|
9
|
+
* Local-only commands (validate, run, up-infra, file generation) do **not**
|
|
10
|
+
* call into this module; the gate is intentionally per-call, not global.
|
|
11
|
+
*
|
|
12
|
+
* @fileoverview Compatibility gate for dataplane API entrypoints
|
|
13
|
+
* @author AI Fabrix Team
|
|
14
|
+
* @version 2.0.0
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const { fetchDataplaneGeneralHealth } = require('../api/dataplane-health.api');
|
|
20
|
+
const {
|
|
21
|
+
getDeviceDataplaneVersions,
|
|
22
|
+
updateDeviceDataplaneVersions,
|
|
23
|
+
isCacheStale
|
|
24
|
+
} = require('../core/config-device-dataplane');
|
|
25
|
+
const { compareSemver, isValidSemver } = require('./semver-compare');
|
|
26
|
+
const { formatCliVersionGateError } = require('./dataplane-cli-version-help');
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get the installed Builder CLI version from `package.json`.
|
|
30
|
+
* Centralized so tests can stub it via Jest module mocks.
|
|
31
|
+
* @returns {string}
|
|
32
|
+
*/
|
|
33
|
+
function getInstalledCliVersion() {
|
|
34
|
+
return require('../../package.json').version;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Internal sentinel error code so callers can distinguish gate failures from
|
|
39
|
+
* generic dataplane errors when needed.
|
|
40
|
+
* @type {string}
|
|
41
|
+
*/
|
|
42
|
+
const ERROR_CODE = 'CLI_VERSION_INCOMPATIBLE';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* True when `err` was thrown by this gate.
|
|
46
|
+
* @param {*} err
|
|
47
|
+
* @returns {boolean}
|
|
48
|
+
*/
|
|
49
|
+
function isCliVersionIncompatibleError(err) {
|
|
50
|
+
return Boolean(err && err.code === ERROR_CODE);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the dataplane minimum Builder CLI version, preferring cached value
|
|
55
|
+
* when fresh; otherwise probe health and persist a new snapshot under
|
|
56
|
+
* `device.<controllerUrl>`.
|
|
57
|
+
*
|
|
58
|
+
* @private
|
|
59
|
+
* @async
|
|
60
|
+
* @param {string} controllerUrl
|
|
61
|
+
* @param {string} dataplaneUrl
|
|
62
|
+
* @param {{ forceRefresh?: boolean }} [options]
|
|
63
|
+
* @returns {Promise<{ minBuilderCliVersion?: string, dataplaneVersion?: string }>}
|
|
64
|
+
*/
|
|
65
|
+
async function resolveMinBuilderCliVersion(controllerUrl, dataplaneUrl, options = {}) {
|
|
66
|
+
const forceRefresh = Boolean(options.forceRefresh);
|
|
67
|
+
if (!forceRefresh) {
|
|
68
|
+
const cached = await safeReadCache(controllerUrl);
|
|
69
|
+
if (cached && !isCacheStale(cached)) {
|
|
70
|
+
return {
|
|
71
|
+
minBuilderCliVersion: cached.minBuilderCliVersion,
|
|
72
|
+
dataplaneVersion: cached.version
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const snapshot = await safeFetch(dataplaneUrl);
|
|
78
|
+
if (!snapshot) {
|
|
79
|
+
// Cannot reach dataplane → do not gate (offline / pre-rollout). Reuse
|
|
80
|
+
// any cached value we may have.
|
|
81
|
+
const cached = await safeReadCache(controllerUrl);
|
|
82
|
+
return {
|
|
83
|
+
minBuilderCliVersion: cached ? cached.minBuilderCliVersion : undefined,
|
|
84
|
+
dataplaneVersion: cached ? cached.version : undefined
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
await safeWriteCache(controllerUrl, {
|
|
89
|
+
version: snapshot.version,
|
|
90
|
+
minBuilderCliVersion: snapshot.minBuilderCliVersion || null
|
|
91
|
+
});
|
|
92
|
+
return {
|
|
93
|
+
minBuilderCliVersion: snapshot.minBuilderCliVersion,
|
|
94
|
+
dataplaneVersion: snapshot.version
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Read cached versions without throwing — gate failures should not mask real
|
|
100
|
+
* dataplane errors caused by config corruption.
|
|
101
|
+
* @private
|
|
102
|
+
* @async
|
|
103
|
+
* @param {string} controllerUrl
|
|
104
|
+
* @returns {Promise<Object|null>}
|
|
105
|
+
*/
|
|
106
|
+
async function safeReadCache(controllerUrl) {
|
|
107
|
+
if (!controllerUrl) return null;
|
|
108
|
+
try {
|
|
109
|
+
return await getDeviceDataplaneVersions(controllerUrl);
|
|
110
|
+
} catch (_error) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Fetch health swallowing transport errors so callers can decide what to do
|
|
117
|
+
* when the dataplane is unreachable.
|
|
118
|
+
* @private
|
|
119
|
+
* @async
|
|
120
|
+
* @param {string} dataplaneUrl
|
|
121
|
+
* @returns {Promise<Object|null>}
|
|
122
|
+
*/
|
|
123
|
+
async function safeFetch(dataplaneUrl) {
|
|
124
|
+
try {
|
|
125
|
+
return await fetchDataplaneGeneralHealth(dataplaneUrl);
|
|
126
|
+
} catch (_error) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Write the cache without throwing — best-effort persistence.
|
|
133
|
+
* @private
|
|
134
|
+
* @async
|
|
135
|
+
* @param {string} controllerUrl
|
|
136
|
+
* @param {Object} versions
|
|
137
|
+
* @returns {Promise<void>}
|
|
138
|
+
*/
|
|
139
|
+
async function safeWriteCache(controllerUrl, versions) {
|
|
140
|
+
if (!controllerUrl) return;
|
|
141
|
+
try {
|
|
142
|
+
await updateDeviceDataplaneVersions(controllerUrl, versions);
|
|
143
|
+
} catch (_error) {
|
|
144
|
+
// intentionally swallowed — cache write failure must not block the call
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Throw when the installed CLI is older than the dataplane minimum.
|
|
150
|
+
*
|
|
151
|
+
* No-op when:
|
|
152
|
+
* - `dataplaneUrl` is empty (no dataplane in play)
|
|
153
|
+
* - Dataplane omits `minBuilderCliVersion` (no enforcement)
|
|
154
|
+
* - Dataplane is unreachable and no cached minimum is known
|
|
155
|
+
* - Cached/advertised minimum is not valid semver (warn-only contract per plan)
|
|
156
|
+
*
|
|
157
|
+
* @async
|
|
158
|
+
* @param {string} dataplaneUrl - Dataplane base URL (e.g. `http://localhost:3201`)
|
|
159
|
+
* @param {Object} [options]
|
|
160
|
+
* @param {string} [options.controllerUrl] - Controller URL for cache lookup/write
|
|
161
|
+
* @param {boolean} [options.forceRefresh] - Skip cache and probe health now
|
|
162
|
+
* @returns {Promise<void>}
|
|
163
|
+
* @throws {Error & { code: 'CLI_VERSION_INCOMPATIBLE', required: string, installed: string, formatted: string }}
|
|
164
|
+
*/
|
|
165
|
+
async function assertDataplaneCliVersionCompatible(dataplaneUrl, options = {}) {
|
|
166
|
+
if (!dataplaneUrl || typeof dataplaneUrl !== 'string') {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const { minBuilderCliVersion } = await resolveMinBuilderCliVersion(
|
|
170
|
+
options.controllerUrl || null,
|
|
171
|
+
dataplaneUrl,
|
|
172
|
+
{ forceRefresh: Boolean(options.forceRefresh) }
|
|
173
|
+
);
|
|
174
|
+
if (!minBuilderCliVersion) return;
|
|
175
|
+
if (!isValidSemver(minBuilderCliVersion)) return;
|
|
176
|
+
|
|
177
|
+
// Go through module.exports so tests can stub `getInstalledCliVersion`.
|
|
178
|
+
const installed = module.exports.getInstalledCliVersion();
|
|
179
|
+
if (!isValidSemver(installed)) return;
|
|
180
|
+
|
|
181
|
+
if (compareSemver(installed, minBuilderCliVersion) >= 0) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const formatted = formatCliVersionGateError(minBuilderCliVersion, installed);
|
|
186
|
+
const error = new Error(
|
|
187
|
+
`Builder CLI ${installed} is below the dataplane minimum (${minBuilderCliVersion}). ` +
|
|
188
|
+
'Upgrade with: npm install -g @aifabrix/builder@latest'
|
|
189
|
+
);
|
|
190
|
+
error.code = ERROR_CODE;
|
|
191
|
+
error.required = minBuilderCliVersion;
|
|
192
|
+
error.installed = installed;
|
|
193
|
+
error.formatted = formatted;
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = {
|
|
198
|
+
assertDataplaneCliVersionCompatible,
|
|
199
|
+
isCliVersionIncompatibleError,
|
|
200
|
+
getInstalledCliVersion,
|
|
201
|
+
ERROR_CODE
|
|
202
|
+
};
|