@aifabrix/builder 2.44.5 → 2.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/cli-layout.mdc +8 -4
- package/.cursor/rules/project-rules.mdc +1 -1
- package/README.md +15 -23
- package/integration/hubspot-test/README.md +2 -0
- package/integration/hubspot-test/test.js +5 -3
- package/jest.projects.js +104 -2
- package/lib/api/controller-health.api.js +49 -0
- package/lib/api/dimension-values.api.js +82 -0
- package/lib/api/dimensions.api.js +114 -0
- package/lib/api/external-systems.api.js +1 -0
- package/lib/api/integration-clients.api.js +168 -0
- package/lib/api/types/dimension-values.types.js +28 -0
- package/lib/api/types/dimensions.types.js +31 -0
- package/lib/api/types/integration-clients.types.js +45 -0
- package/lib/api/validation-runner.js +46 -25
- package/lib/app/deploy-config.js +11 -1
- package/lib/app/deploy-status-display.js +3 -3
- package/lib/app/deploy.js +36 -14
- package/lib/app/display.js +15 -11
- package/lib/app/helpers.js +3 -3
- package/lib/app/index.js +3 -3
- package/lib/app/push.js +46 -23
- package/lib/app/register.js +7 -6
- package/lib/app/restart-display.js +126 -0
- package/lib/app/rotate-secret.js +7 -6
- package/lib/app/run-container-start.js +12 -6
- package/lib/app/run-env-compose.js +30 -1
- package/lib/app/run-helpers.js +58 -19
- package/lib/app/run-reload-sync.js +148 -0
- package/lib/app/run-resolve-image.js +51 -1
- package/lib/app/run.js +148 -74
- package/lib/app/show-display.js +7 -0
- package/lib/app/show.js +87 -5
- package/lib/build/index.js +83 -49
- package/lib/cli/doctor-check.js +117 -0
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided.js +460 -0
- package/lib/cli/installation-log-command.js +73 -0
- package/lib/cli/setup-app.js +31 -3
- package/lib/cli/setup-auth.js +98 -27
- package/lib/cli/setup-dev-path-commands.js +50 -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 +132 -118
- package/lib/cli/setup-integration-client.js +182 -0
- package/lib/cli/setup-parameters.js +21 -2
- package/lib/cli/setup-platform.js +102 -0
- package/lib/cli/setup-secrets.js +18 -6
- package/lib/cli/setup-utility-resolve.js +132 -0
- package/lib/cli/setup-utility.js +143 -84
- package/lib/commands/app-logs.js +81 -33
- package/lib/commands/auth-config.js +116 -18
- package/lib/commands/datasource-capability-dimension-cli.js +128 -0
- package/lib/commands/datasource-capability-output.js +29 -0
- package/lib/commands/datasource-capability-relate-cli.js +140 -0
- package/lib/commands/datasource-capability.js +411 -0
- package/lib/commands/datasource-unified-test-cli.options.js +1 -1
- package/lib/commands/datasource.js +53 -13
- package/lib/commands/dev-down.js +3 -3
- package/lib/commands/dev-infra-gate.js +32 -0
- package/lib/commands/dev-init.js +13 -7
- package/lib/commands/dimension-value.js +179 -0
- package/lib/commands/dimension.js +330 -0
- package/lib/commands/integration-client.js +430 -0
- package/lib/commands/login-device.js +65 -30
- package/lib/commands/login.js +21 -10
- package/lib/commands/parameters-validate.js +78 -13
- package/lib/commands/repair-datasource-auto-rbac.js +166 -0
- package/lib/commands/repair-datasource-keys.js +10 -5
- package/lib/commands/repair-datasource.js +19 -7
- package/lib/commands/repair-env-template.js +4 -1
- package/lib/commands/repair-openapi-sync.js +172 -0
- package/lib/commands/repair-persist.js +102 -0
- package/lib/commands/repair-rbac-extract.js +27 -0
- package/lib/commands/repair-rbac-migrate.js +186 -0
- package/lib/commands/repair-rbac.js +214 -31
- package/lib/commands/repair-system-alignment.js +246 -0
- package/lib/commands/repair-system-permissions.js +168 -0
- package/lib/commands/repair.js +120 -338
- package/lib/commands/secure.js +1 -1
- package/lib/commands/setup-modes.js +468 -0
- package/lib/commands/setup-prompts.js +421 -0
- package/lib/commands/setup.js +254 -0
- package/lib/commands/teardown.js +277 -0
- package/lib/commands/up-common.js +113 -19
- package/lib/commands/up-dataplane.js +44 -19
- package/lib/commands/up-miso.js +18 -18
- package/lib/commands/upload.js +111 -23
- package/lib/commands/wizard-core-helpers.js +14 -11
- package/lib/commands/wizard-core.js +6 -5
- package/lib/commands/wizard-dataplane.js +2 -2
- package/lib/commands/wizard-entity-selection.js +4 -3
- package/lib/commands/wizard-headless.js +2 -1
- package/lib/commands/wizard.js +2 -1
- package/lib/constants/infra-compose-service-names.js +40 -0
- package/lib/core/audit-logger.js +1 -34
- package/lib/core/config-admin-email.js +56 -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/env-reader.js +16 -3
- package/lib/core/secrets-admin-env.js +101 -0
- package/lib/core/secrets-ensure-infra.js +34 -1
- package/lib/core/secrets-ensure.js +88 -66
- package/lib/core/secrets-env-content.js +428 -0
- package/lib/core/secrets-env-declarative-expand.js +170 -0
- package/lib/core/secrets-env-write.js +29 -1
- package/lib/core/secrets-load.js +252 -0
- package/lib/core/secrets-names.js +32 -0
- package/lib/core/secrets.js +17 -757
- package/lib/datasource/capability/basic-exposure.js +76 -0
- package/lib/datasource/capability/capability-diff-slice.js +41 -0
- package/lib/datasource/capability/capability-key.js +34 -0
- package/lib/datasource/capability/capability-resolve.js +172 -0
- package/lib/datasource/capability/capability-storage-keys.js +22 -0
- package/lib/datasource/capability/copy-operations.js +348 -0
- package/lib/datasource/capability/copy-test-payload.js +139 -0
- package/lib/datasource/capability/create-operations.js +235 -0
- package/lib/datasource/capability/dimension-operations.js +151 -0
- package/lib/datasource/capability/dimension-validate.js +219 -0
- package/lib/datasource/capability/json-pointer.js +31 -0
- package/lib/datasource/capability/reference-rewrite.js +51 -0
- package/lib/datasource/capability/relate-operations.js +325 -0
- package/lib/datasource/capability/relate-validate.js +219 -0
- package/lib/datasource/capability/remove-operations.js +275 -0
- package/lib/datasource/capability/run-capability-copy.js +152 -0
- package/lib/datasource/capability/run-capability-diff.js +135 -0
- package/lib/datasource/capability/run-capability-dimension.js +291 -0
- package/lib/datasource/capability/run-capability-edit.js +377 -0
- package/lib/datasource/capability/run-capability-relate.js +193 -0
- package/lib/datasource/capability/run-capability-remove.js +105 -0
- package/lib/datasource/capability/templates/minimal-fetch.json +18 -0
- package/lib/datasource/capability/validate-capability-slice.js +35 -0
- package/lib/datasource/list.js +136 -23
- package/lib/datasource/log-viewer.js +2 -4
- package/lib/datasource/unified-validation-run.js +51 -16
- package/lib/datasource/validate.js +53 -1
- package/lib/deployment/deploy-poll-ui.js +60 -0
- package/lib/deployment/deployer-status.js +29 -3
- package/lib/deployment/deployer.js +48 -30
- package/lib/deployment/environment.js +7 -2
- package/lib/deployment/poll-interval.js +72 -0
- package/lib/deployment/push.js +11 -9
- package/lib/external-system/deploy.js +9 -2
- package/lib/external-system/download.js +61 -32
- package/lib/external-system/sync-deploy-manifest.js +33 -0
- package/lib/infrastructure/index.js +49 -19
- package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
- package/lib/internal/node-fs.js +2 -0
- package/lib/parameters/infra-kv-discovery.js +29 -4
- package/lib/parameters/infra-parameter-catalog.js +6 -3
- package/lib/parameters/infra-parameter-validate.js +67 -19
- package/lib/resolvers/datasource-resolver.js +53 -0
- package/lib/resolvers/dimension-file.js +52 -0
- package/lib/resolvers/manifest-resolver.js +133 -0
- package/lib/schema/application-schema.json +4 -0
- package/lib/schema/external-datasource.schema.json +183 -53
- package/lib/schema/external-system.schema.json +23 -10
- package/lib/schema/infra.parameter.yaml +26 -1
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/aifabrix-config-dir-walk.js +40 -0
- package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
- package/lib/utils/app-config-resolver.js +24 -1
- package/lib/utils/app-run-containers.js +2 -2
- 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 +59 -0
- package/lib/utils/cli-secrets-error-format.js +78 -0
- package/lib/utils/cli-test-layout-chalk.js +31 -9
- package/lib/utils/cli-utils.js +4 -36
- 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/datasource-test-run-display.js +8 -0
- package/lib/utils/dev-hosts-helper.js +3 -2
- package/lib/utils/dev-init-ssh-merge.js +2 -1
- package/lib/utils/docker-build.js +17 -9
- package/lib/utils/docker-reload-mount.js +127 -0
- package/lib/utils/env-copy.js +99 -14
- package/lib/utils/env-template.js +5 -1
- package/lib/utils/external-readme.js +71 -2
- package/lib/utils/external-system-local-test-tty.js +3 -2
- package/lib/utils/external-system-readiness-core.js +45 -12
- package/lib/utils/external-system-readiness-deploy-display.js +3 -3
- package/lib/utils/external-system-readiness-display-internals.js +33 -3
- package/lib/utils/external-system-readiness-display.js +10 -1
- package/lib/utils/file-upload.js +40 -3
- package/lib/utils/health-check-db-init.js +107 -0
- package/lib/utils/health-check-public-warn.js +69 -0
- package/lib/utils/health-check-url.js +28 -10
- package/lib/utils/health-check.js +139 -107
- package/lib/utils/help-builder.js +5 -1
- package/lib/utils/image-name.js +34 -7
- 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/integration-file-backup.js +74 -0
- package/lib/utils/log-redaction.js +105 -0
- package/lib/utils/manifest-location.js +164 -0
- package/lib/utils/manifest-source-emit.js +162 -0
- package/lib/utils/mutagen-install.js +30 -3
- package/lib/utils/paths.js +308 -76
- package/lib/utils/postgres-wipe.js +212 -0
- package/lib/utils/register-aifabrix-shell-env.js +15 -0
- package/lib/utils/remote-dev-auth.js +21 -5
- package/lib/utils/remote-docker-env.js +9 -1
- package/lib/utils/remote-secrets-loader.js +49 -4
- package/lib/utils/resolve-docker-image-ref.js +9 -3
- package/lib/utils/run-cli-flags.js +29 -0
- package/lib/utils/secrets-ancestor-paths.js +47 -0
- package/lib/utils/secrets-canonical.js +10 -3
- package/lib/utils/secrets-helpers.js +17 -10
- package/lib/utils/secrets-kv-refs.js +42 -0
- package/lib/utils/secrets-kv-scope.js +19 -2
- package/lib/utils/secrets-materialize-local.js +134 -0
- package/lib/utils/secrets-path.js +26 -13
- package/lib/utils/secrets-utils.js +20 -10
- package/lib/utils/system-builder-root.js +42 -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 -388
- 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 +52 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
- package/lib/utils/urls-local-registry-scan.js +103 -0
- package/lib/utils/urls-local-registry.js +158 -76
- package/lib/utils/validation-poll-ui.js +81 -0
- package/lib/utils/validation-run-poll.js +29 -5
- package/lib/utils/with-muted-logger.js +53 -0
- package/package.json +3 -1
- package/templates/applications/dataplane/application.yaml +5 -1
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/env.template +8 -6
- package/templates/applications/miso-controller/application.yaml +9 -0
- package/templates/applications/miso-controller/env.template +27 -29
- package/templates/applications/miso-controller/rbac.yaml +9 -9
- package/templates/external-system/README.md.hbs +83 -123
- package/.npmrc.token +0 -1
- package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/index.json +0 -1
- package/lib/api/service-users.api.js +0 -150
- package/lib/api/types/service-users.types.js +0 -65
- package/lib/cli/setup-service-user.js +0 -187
- package/lib/commands/service-user.js +0 -429
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { formatSuccessLine } = require('./cli-test-layout-chalk');
|
|
1
|
+
const { formatSuccessLine, formatProgress } = require('./cli-test-layout-chalk');
|
|
2
2
|
/**
|
|
3
3
|
* AI Fabrix Builder - App Run Container Helpers
|
|
4
4
|
*
|
|
@@ -118,7 +118,7 @@ async function checkContainerRunning(appName, developerId, debug = false, scopeO
|
|
|
118
118
|
async function stopAndRemoveContainer(appName, developerId, debug = false, scopeOpts = null) {
|
|
119
119
|
try {
|
|
120
120
|
const containerName = getContainerName(appName, developerId, scopeOpts);
|
|
121
|
-
logger.log(
|
|
121
|
+
logger.log(formatProgress(`Stopping container ${containerName}…`));
|
|
122
122
|
const stopCmd = `docker stop ${containerName}`;
|
|
123
123
|
if (debug) {
|
|
124
124
|
logger.log(chalk.gray(`[DEBUG] Executing: ${stopCmd}`));
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User `applications` entries in config.yaml (read by `aifabrix resolve`, written by `aifabrix run` dev).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Per-app reload and `proxy` (Traefik/public URL hints) in ~/.aifabrix/config.yaml
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const config = require('../core/config');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Persist `applications.<appKey>.reload` from the last `aifabrix run` (dev only; CLI is source of truth).
|
|
15
|
+
*
|
|
16
|
+
* @async
|
|
17
|
+
* @param {string} appKey - Application key
|
|
18
|
+
* @param {boolean} reload - Same as passing `--reload` on run
|
|
19
|
+
* @returns {Promise<void>}
|
|
20
|
+
*/
|
|
21
|
+
async function persistApplicationReloadFlag(appKey, reload) {
|
|
22
|
+
if (!appKey || typeof appKey !== 'string') {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const cfg = await config.getConfig();
|
|
26
|
+
if (!cfg.applications || typeof cfg.applications !== 'object') {
|
|
27
|
+
cfg.applications = {};
|
|
28
|
+
}
|
|
29
|
+
const prev = cfg.applications[appKey];
|
|
30
|
+
const nextEntry =
|
|
31
|
+
prev && typeof prev === 'object' && !Array.isArray(prev) ? { ...prev, reload: Boolean(reload) } : { reload: Boolean(reload) };
|
|
32
|
+
cfg.applications[appKey] = nextEntry;
|
|
33
|
+
await config.saveConfig(cfg);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* True when `applications.<appKey>.reload` is explicitly **true** (bind-mount + hot reload for `aifabrix run`).
|
|
38
|
+
* Does **not** control other compose overrides (e.g. Python image `command` when the image lacks `uvicorn` on PATH).
|
|
39
|
+
*
|
|
40
|
+
* @param {Object|null|undefined} userConfig - Parsed config.yaml root
|
|
41
|
+
* @param {string} appKey - Application key (folder / app.key)
|
|
42
|
+
* @returns {boolean} True when `applications.<appKey>.reload` is explicitly true
|
|
43
|
+
*/
|
|
44
|
+
function isApplicationsReloadDefaultOn(userConfig, appKey) {
|
|
45
|
+
if (!userConfig || !appKey || typeof appKey !== 'string') {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const apps = userConfig.applications;
|
|
49
|
+
if (!apps || typeof apps !== 'object') {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const entry = apps[appKey];
|
|
53
|
+
if (!entry || typeof entry !== 'object') {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return entry.reload === true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* True when `remote-server` is a non-empty string (after trim).
|
|
61
|
+
*
|
|
62
|
+
* @param {string|null|undefined} remoteServer
|
|
63
|
+
* @returns {boolean}
|
|
64
|
+
*/
|
|
65
|
+
function isRemoteServerConfigured(remoteServer) {
|
|
66
|
+
return Boolean(remoteServer && String(remoteServer).trim());
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Whether `build.envOutputPath` should be regenerated with the **local** profile (`generateEnvContent(..., 'local')`).
|
|
71
|
+
* **False** only when **both** `remote-server` is set and `applications.<appKey>.reload` is true — then the host file
|
|
72
|
+
* uses the same **docker**-flavor expansion as `builder/<app>/.env` (copy path). All other cases use **local**.
|
|
73
|
+
*
|
|
74
|
+
* @param {Object|null|undefined} userConfig - Parsed config.yaml root
|
|
75
|
+
* @param {string} appKey - Application key
|
|
76
|
+
* @param {string|null|undefined} remoteServer - `remote-server` value (or null)
|
|
77
|
+
* @returns {boolean}
|
|
78
|
+
*/
|
|
79
|
+
function isPreferLocalEnvOutputPath(userConfig, appKey, remoteServer) {
|
|
80
|
+
if (!isRemoteServerConfigured(remoteServer)) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
return !isApplicationsReloadDefaultOn(userConfig, appKey);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* For `aifabrix resolve`: reads `remote-server` and returns {@link isPreferLocalEnvOutputPath}.
|
|
88
|
+
*
|
|
89
|
+
* @async
|
|
90
|
+
* @param {Object|null|undefined} userConfig
|
|
91
|
+
* @param {string} appKey
|
|
92
|
+
* @returns {Promise<boolean>}
|
|
93
|
+
*/
|
|
94
|
+
async function resolvePreferLocalEnvOutputPathFlag(userConfig, appKey) {
|
|
95
|
+
let remoteServer = null;
|
|
96
|
+
try {
|
|
97
|
+
const rs = await config.getRemoteServer();
|
|
98
|
+
if (rs && String(rs).trim()) {
|
|
99
|
+
remoteServer = String(rs).trim();
|
|
100
|
+
}
|
|
101
|
+
} catch {
|
|
102
|
+
remoteServer = null;
|
|
103
|
+
}
|
|
104
|
+
return isPreferLocalEnvOutputPath(userConfig, appKey, remoteServer);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Normalize YAML boolean-ish values.
|
|
109
|
+
* @param {unknown} v
|
|
110
|
+
* @returns {boolean|undefined} undefined if absent / not coercible
|
|
111
|
+
*/
|
|
112
|
+
function coerceTriStateBool(v) {
|
|
113
|
+
if (v === true || v === 'true' || v === 'yes' || v === 'on' || v === 1 || v === '1') {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
if (v === false || v === 'false' || v === 'no' || v === 'off' || v === 0 || v === '0') {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Whether `aifabrix run` / resolve should use Traefik-style public URL hints for this app when infra allows.
|
|
124
|
+
* Default **false** when unset. Legacy `noProxy: true` ⇒ false; legacy `noProxy: false` with no `proxy` ⇒ true.
|
|
125
|
+
*
|
|
126
|
+
* @param {Object|null|undefined} userConfig - Parsed config.yaml root
|
|
127
|
+
* @param {string} appKey - Application key
|
|
128
|
+
* @returns {boolean}
|
|
129
|
+
*/
|
|
130
|
+
function getApplicationsRunProxyHint(userConfig, appKey) {
|
|
131
|
+
if (!userConfig || !appKey || typeof appKey !== 'string') {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
const apps = userConfig.applications;
|
|
135
|
+
if (!apps || typeof apps !== 'object') {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
const entry = apps[appKey];
|
|
139
|
+
if (!entry || typeof entry !== 'object') {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
const proxyCoerced = coerceTriStateBool(entry.proxy);
|
|
143
|
+
if (proxyCoerced !== undefined) {
|
|
144
|
+
return proxyCoerced;
|
|
145
|
+
}
|
|
146
|
+
const legacyNo = coerceTriStateBool(entry.noProxy);
|
|
147
|
+
if (legacyNo === true) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
if (legacyNo === false) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Persist `applications.<appKey>.proxy` from each `aifabrix run` (same pattern as `reload` for dev-only reload flag).
|
|
158
|
+
* `proxy: true` means use Traefik/front-door URL hints when infra + that app's `application.yaml` allow; `false` means docker `url://` **public** bases for **that app** use localhost + published port. During `resolve`/`run`, each `url://` token uses the **target** app's `applications.<targetKey>.proxy`, not only the app whose `env.template` is being expanded.
|
|
159
|
+
* Removes legacy `noProxy` on the same entry when present.
|
|
160
|
+
*
|
|
161
|
+
* @async
|
|
162
|
+
* @param {string} appKey - Application key
|
|
163
|
+
* @param {boolean} proxyEnabled - Same as a normal `aifabrix run` with proxy hints on (`!isRunCliNoProxy(options)`)
|
|
164
|
+
* @returns {Promise<void>}
|
|
165
|
+
*/
|
|
166
|
+
async function persistApplicationRunProxyFlag(appKey, proxyEnabled) {
|
|
167
|
+
if (!appKey || typeof appKey !== 'string') {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const cfg = await config.getConfig();
|
|
171
|
+
if (!cfg.applications || typeof cfg.applications !== 'object') {
|
|
172
|
+
cfg.applications = {};
|
|
173
|
+
}
|
|
174
|
+
const prev = cfg.applications[appKey];
|
|
175
|
+
const nextEntry =
|
|
176
|
+
prev && typeof prev === 'object' && !Array.isArray(prev)
|
|
177
|
+
? { ...prev, proxy: Boolean(proxyEnabled) }
|
|
178
|
+
: { proxy: Boolean(proxyEnabled) };
|
|
179
|
+
if (Object.prototype.hasOwnProperty.call(nextEntry, 'noProxy')) {
|
|
180
|
+
delete nextEntry.noProxy;
|
|
181
|
+
}
|
|
182
|
+
cfg.applications[appKey] = nextEntry;
|
|
183
|
+
await config.saveConfig(cfg);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Whether declarative `url://*` expansion should treat Traefik as on (infra Traefik + per-app `proxy: true`).
|
|
188
|
+
*
|
|
189
|
+
* @param {Object|null|undefined} userConfig
|
|
190
|
+
* @param {string} appKey
|
|
191
|
+
* @returns {boolean}
|
|
192
|
+
*/
|
|
193
|
+
function isDeclarativeTraefikUrlsEnabled(userConfig, appKey) {
|
|
194
|
+
return Boolean(userConfig && userConfig.traefik === true) && getApplicationsRunProxyHint(userConfig, appKey);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = {
|
|
198
|
+
getApplicationsRunProxyHint,
|
|
199
|
+
isApplicationsReloadDefaultOn,
|
|
200
|
+
isDeclarativeTraefikUrlsEnabled,
|
|
201
|
+
isPreferLocalEnvOutputPath,
|
|
202
|
+
isRemoteServerConfigured,
|
|
203
|
+
persistApplicationRunProxyFlag,
|
|
204
|
+
persistApplicationReloadFlag,
|
|
205
|
+
resolvePreferLocalEnvOutputPathFlag
|
|
206
|
+
};
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @version 2.0.0
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
const {
|
|
11
|
+
const { hasStoredDeviceTokenForController } = require('./controller-url');
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Validate controller URL format
|
|
@@ -72,17 +72,7 @@ async function checkUserLoggedIn(controllerUrl) {
|
|
|
72
72
|
if (!controllerUrl) {
|
|
73
73
|
return false;
|
|
74
74
|
}
|
|
75
|
-
|
|
76
|
-
const normalizedUrl = controllerUrl.trim().replace(/\/+$/, '');
|
|
77
|
-
const loggedInControllerUrl = await getControllerUrlFromLoggedInUser();
|
|
78
|
-
|
|
79
|
-
if (!loggedInControllerUrl) {
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Normalize both URLs for comparison
|
|
84
|
-
const normalizedLoggedIn = loggedInControllerUrl.trim().replace(/\/+$/, '');
|
|
85
|
-
return normalizedLoggedIn === normalizedUrl;
|
|
75
|
+
return hasStoredDeviceTokenForController(controllerUrl);
|
|
86
76
|
}
|
|
87
77
|
|
|
88
78
|
module.exports = {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map secrets keys `BASH_<NAME>` to process-style env `{ [NAME]: value }` for child_process env.
|
|
3
|
+
* Same naming rule as kv://BASH_* resolution (see secrets-bash-kv.js).
|
|
4
|
+
*
|
|
5
|
+
* @fileoverview BASH-prefixed secret → exported-style env for Docker/subprocess
|
|
6
|
+
* @author AI Fabrix Team
|
|
7
|
+
* @version 1.0.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const secretsLoad = require('../core/secrets-load');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} suffix - Part after BASH_
|
|
16
|
+
* @returns {boolean}
|
|
17
|
+
*/
|
|
18
|
+
function isValidExportedName(suffix) {
|
|
19
|
+
return Boolean(suffix && /^[A-Za-z_][A-Za-z0-9_]*$/.test(suffix));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Collect `BASH_*` entries from a flat or shallow secrets object.
|
|
24
|
+
*
|
|
25
|
+
* @param {Record<string, unknown>|null|undefined} secrets - Merged secrets map (decrypted)
|
|
26
|
+
* @returns {Record<string, string>} e.g. { NPM_TOKEN: '...' } from BASH_NPM_TOKEN
|
|
27
|
+
*/
|
|
28
|
+
function collectBashPrefixedEnv(secrets) {
|
|
29
|
+
const out = {};
|
|
30
|
+
if (!secrets || typeof secrets !== 'object') return out;
|
|
31
|
+
for (const [k, v] of Object.entries(secrets)) {
|
|
32
|
+
if (typeof k !== 'string' || !k.startsWith('BASH_')) continue;
|
|
33
|
+
if (v === undefined || v === null) continue;
|
|
34
|
+
const str = typeof v === 'string' ? v.trim() : String(v).trim();
|
|
35
|
+
if (!str) continue;
|
|
36
|
+
const suffix = k.slice(5);
|
|
37
|
+
if (!isValidExportedName(suffix)) continue;
|
|
38
|
+
out[suffix] = str;
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Overlay for `child_process` `env`: values from primary user `secrets.local.yaml` plus `aifabrix-secrets`
|
|
45
|
+
* for every `BASH_*` key (merged via {@link secretsLoad.loadSecrets}).
|
|
46
|
+
*
|
|
47
|
+
* @param {string|null} [secretsPath] - Optional explicit secrets file (same as resolve)
|
|
48
|
+
* @param {string|null} [appName] - Optional app name for loadSecrets second arg
|
|
49
|
+
* @returns {Promise<Record<string, string>>}
|
|
50
|
+
*/
|
|
51
|
+
async function getBashPrefixedProcessEnvOverlay(secretsPath = null, appName = null) {
|
|
52
|
+
const secrets = await secretsLoad.loadSecrets(secretsPath, appName);
|
|
53
|
+
return collectBashPrefixedEnv(secrets);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = {
|
|
57
|
+
collectBashPrefixedEnv,
|
|
58
|
+
getBashPrefixedProcessEnvOverlay
|
|
59
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Missing-secrets CLI error lines (layout colors via cli-layout-chalk).
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const chalk = require('chalk');
|
|
10
|
+
const { metadata, sectionTitle } = require('./cli-layout-chalk');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {string[]} messages - Output lines
|
|
14
|
+
* @param {RegExpMatchArray|null} missingSecretsMatch - Missing secrets capture
|
|
15
|
+
*/
|
|
16
|
+
function pushMissingSecretsBodyLines(messages, missingSecretsMatch) {
|
|
17
|
+
if (missingSecretsMatch) {
|
|
18
|
+
const parts = missingSecretsMatch[1]
|
|
19
|
+
.trim()
|
|
20
|
+
.split(',')
|
|
21
|
+
.map((s) => s.trim())
|
|
22
|
+
.filter(Boolean);
|
|
23
|
+
if (parts.length > 1) {
|
|
24
|
+
messages.push(` ${sectionTitle('Missing secrets:')}`);
|
|
25
|
+
for (const ref of parts) {
|
|
26
|
+
messages.push(` ${chalk.cyan('-')} ${chalk.white(ref)}`);
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (parts.length === 1) {
|
|
31
|
+
messages.push(` ${sectionTitle('Missing secrets:')} ${chalk.white(parts[0])}`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
messages.push(` ${sectionTitle('Missing secrets:')}`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
messages.push(` ${chalk.white('Missing secrets in secrets file.')}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {string[]} messages - Output lines
|
|
42
|
+
* @param {RegExpMatchArray|null} fileInfoMatch - File location capture
|
|
43
|
+
* @param {RegExpMatchArray|null} resolveMatch - Resolve command capture
|
|
44
|
+
*/
|
|
45
|
+
function pushSecretsResolutionHintLines(messages, fileInfoMatch, resolveMatch) {
|
|
46
|
+
if (fileInfoMatch) {
|
|
47
|
+
messages.push(` ${metadata('Secrets file location:')} ${chalk.white(fileInfoMatch[1])}`);
|
|
48
|
+
}
|
|
49
|
+
if (resolveMatch) {
|
|
50
|
+
messages.push(
|
|
51
|
+
` ${metadata('Run:')} ${chalk.yellow(`aifabrix resolve ${resolveMatch[1]} to generate missing secrets.`)}`
|
|
52
|
+
);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
messages.push(` ${metadata('Run:')} ${chalk.yellow('aifabrix resolve <app-name> to generate missing secrets.')}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Format secrets-related errors
|
|
60
|
+
* @param {string} errorMsg - Error message
|
|
61
|
+
* @returns {string[]|null} Array of error message lines or null if not a secrets error
|
|
62
|
+
*/
|
|
63
|
+
function formatSecretsError(errorMsg) {
|
|
64
|
+
if (!errorMsg.includes('Missing secrets')) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const messages = [];
|
|
69
|
+
pushMissingSecretsBodyLines(messages, errorMsg.match(/Missing secrets: ([^\n]+)/));
|
|
70
|
+
pushSecretsResolutionHintLines(
|
|
71
|
+
messages,
|
|
72
|
+
errorMsg.match(/Secrets file location: ([^\n]+)/),
|
|
73
|
+
errorMsg.match(/Run "aifabrix resolve ([^"]+)"/)
|
|
74
|
+
);
|
|
75
|
+
return messages;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = { formatSecretsError };
|
|
@@ -7,6 +7,18 @@
|
|
|
7
7
|
|
|
8
8
|
const chalk = require('chalk');
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Invoke chalk[method](text) when the mock/real chalk exposes that method; otherwise return text.
|
|
12
|
+
* Keeps layout helpers working in Jest suites that only stub a subset of chalk.
|
|
13
|
+
* @param {string} method - chalk method name (e.g. 'white', 'bold', 'gray')
|
|
14
|
+
* @param {string} text
|
|
15
|
+
* @returns {string}
|
|
16
|
+
*/
|
|
17
|
+
function chalkStyle(method, text) {
|
|
18
|
+
const fn = chalk[method];
|
|
19
|
+
return typeof fn === 'function' ? fn(text) : text;
|
|
20
|
+
}
|
|
21
|
+
|
|
10
22
|
/** Canonical success glyph (green), layout §CORE / §3. */
|
|
11
23
|
function successGlyph() {
|
|
12
24
|
return chalk.green('✔');
|
|
@@ -35,6 +47,15 @@ function formatSuccessParagraph(message) {
|
|
|
35
47
|
return chalk.green(`\n✔ ${message}`);
|
|
36
48
|
}
|
|
37
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Non-blocking warning (layout §CORE): yellow ⚠ + white detail.
|
|
52
|
+
* @param {string} message - text after the glyph (do not prefix with ⚠)
|
|
53
|
+
* @returns {string}
|
|
54
|
+
*/
|
|
55
|
+
function formatWarningLine(message) {
|
|
56
|
+
return `${chalkStyle('yellow', '⚠')} ${chalkStyle('white', message)}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
38
59
|
/**
|
|
39
60
|
* Blocking error line: red ✖ + red message (layout §18).
|
|
40
61
|
* @param {string} message
|
|
@@ -65,7 +86,7 @@ function formatIssue(title, hint) {
|
|
|
65
86
|
*/
|
|
66
87
|
function formatNextActions(lines) {
|
|
67
88
|
const body = (lines || [])
|
|
68
|
-
.map(line => `${
|
|
89
|
+
.map(line => `${chalkStyle('cyan', '-')} ${chalkStyle('white', line)}`)
|
|
69
90
|
.join('\n');
|
|
70
91
|
return `${sectionTitle('Next actions:')}\n${body}`;
|
|
71
92
|
}
|
|
@@ -87,7 +108,7 @@ function formatDocsLine(label, url) {
|
|
|
87
108
|
* @returns {string}
|
|
88
109
|
*/
|
|
89
110
|
function formatProgress(message) {
|
|
90
|
-
return `${
|
|
111
|
+
return `${chalkStyle('yellow', '⏳')} ${chalkStyle('white', message)}`;
|
|
91
112
|
}
|
|
92
113
|
|
|
93
114
|
/**
|
|
@@ -99,7 +120,7 @@ function formatProgress(message) {
|
|
|
99
120
|
*/
|
|
100
121
|
function formatBulletSection(title, items, opts) {
|
|
101
122
|
const bulletColor = opts && opts.bullet === 'red' ? chalk.red : chalk.cyan;
|
|
102
|
-
const body = (items || []).map(line => `${bulletColor('-')} ${
|
|
123
|
+
const body = (items || []).map(line => `${bulletColor('-')} ${chalkStyle('white', line)}`).join('\n');
|
|
103
124
|
return `${sectionTitle(title)}\n${body}`;
|
|
104
125
|
}
|
|
105
126
|
|
|
@@ -109,7 +130,7 @@ function formatBulletSection(title, items, opts) {
|
|
|
109
130
|
* @returns {string}
|
|
110
131
|
*/
|
|
111
132
|
function sectionTitle(text) {
|
|
112
|
-
return
|
|
133
|
+
return chalkStyle('bold', chalkStyle('white', text));
|
|
113
134
|
}
|
|
114
135
|
|
|
115
136
|
/**
|
|
@@ -119,7 +140,7 @@ function sectionTitle(text) {
|
|
|
119
140
|
* @returns {string}
|
|
120
141
|
*/
|
|
121
142
|
function headerKeyValue(label, value) {
|
|
122
|
-
return `${
|
|
143
|
+
return `${chalkStyle('gray', label)} ${chalkStyle('bold', chalkStyle('white', value))}`;
|
|
123
144
|
}
|
|
124
145
|
|
|
125
146
|
/**
|
|
@@ -201,7 +222,7 @@ function formatDatasourceListRow(rowStatus, name, statusHint) {
|
|
|
201
222
|
? chalk.red('✖')
|
|
202
223
|
: chalk.gray('⏭');
|
|
203
224
|
const hint = statusHint ? ` ${chalk.gray(`(${statusHint})`)}` : '';
|
|
204
|
-
return ` ${sym} ${
|
|
225
|
+
return ` ${sym} ${chalkStyle('white', name)}${hint}`;
|
|
205
226
|
}
|
|
206
227
|
|
|
207
228
|
/**
|
|
@@ -232,13 +253,13 @@ function colorRollupPrefixedLine(line) {
|
|
|
232
253
|
const trimmed = line.trimStart();
|
|
233
254
|
const first = trimmed[0];
|
|
234
255
|
if (first === '✔') {
|
|
235
|
-
return chalk.green('✔') +
|
|
256
|
+
return chalk.green('✔') + chalkStyle('white', trimmed.slice(1));
|
|
236
257
|
}
|
|
237
258
|
if (first === '⚠') {
|
|
238
|
-
return chalk.yellow('⚠') +
|
|
259
|
+
return chalk.yellow('⚠') + chalkStyle('white', trimmed.slice(1));
|
|
239
260
|
}
|
|
240
261
|
if (first === '✖') {
|
|
241
|
-
return chalk.red('✖') +
|
|
262
|
+
return chalk.red('✖') + chalkStyle('white', trimmed.slice(1));
|
|
242
263
|
}
|
|
243
264
|
return line;
|
|
244
265
|
}
|
|
@@ -258,6 +279,7 @@ module.exports = {
|
|
|
258
279
|
failureGlyph,
|
|
259
280
|
formatSuccessLine,
|
|
260
281
|
formatSuccessParagraph,
|
|
282
|
+
formatWarningLine,
|
|
261
283
|
formatBlockingError,
|
|
262
284
|
formatIssue,
|
|
263
285
|
formatNextActions,
|
package/lib/utils/cli-utils.js
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
const path = require('path');
|
|
12
12
|
const chalk = require('chalk');
|
|
13
13
|
const logger = require('./logger');
|
|
14
|
+
const { formatBlockingError, infoLine } = require('./cli-layout-chalk');
|
|
15
|
+
const { formatSecretsError } = require('./cli-secrets-error-format');
|
|
14
16
|
const { getDockerDaemonStartHintSentence, getDockerApiOverTcpHintLines } = require('./docker-not-running-hint');
|
|
15
17
|
|
|
16
18
|
/**
|
|
@@ -231,40 +233,6 @@ function formatAzureError(errorMsg) {
|
|
|
231
233
|
return null;
|
|
232
234
|
}
|
|
233
235
|
|
|
234
|
-
/**
|
|
235
|
-
* Format secrets-related errors
|
|
236
|
-
* @param {string} errorMsg - Error message
|
|
237
|
-
* @returns {string[]|null} Array of error message lines or null if not a secrets error
|
|
238
|
-
*/
|
|
239
|
-
function formatSecretsError(errorMsg) {
|
|
240
|
-
if (!errorMsg.includes('Missing secrets')) {
|
|
241
|
-
return null;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const messages = [];
|
|
245
|
-
const missingSecretsMatch = errorMsg.match(/Missing secrets: ([^\n]+)/);
|
|
246
|
-
const fileInfoMatch = errorMsg.match(/Secrets file location: ([^\n]+)/);
|
|
247
|
-
const resolveMatch = errorMsg.match(/Run "aifabrix resolve ([^"]+)"/);
|
|
248
|
-
|
|
249
|
-
if (missingSecretsMatch) {
|
|
250
|
-
messages.push(` Missing secrets: ${missingSecretsMatch[1]}`);
|
|
251
|
-
} else {
|
|
252
|
-
messages.push(' Missing secrets in secrets file.');
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (fileInfoMatch) {
|
|
256
|
-
messages.push(` Secrets file location: ${fileInfoMatch[1]}`);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (resolveMatch) {
|
|
260
|
-
messages.push(` Run: aifabrix resolve ${resolveMatch[1]} to generate missing secrets.`);
|
|
261
|
-
} else {
|
|
262
|
-
messages.push(' Run: aifabrix resolve <app-name> to generate missing secrets.');
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
return messages;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
236
|
/**
|
|
269
237
|
* Format deployment-related errors
|
|
270
238
|
* @param {string} errorMsg - Error message
|
|
@@ -388,9 +356,9 @@ function formatError(error) {
|
|
|
388
356
|
* @param {string[]} errorMessages - Error message lines
|
|
389
357
|
*/
|
|
390
358
|
function logError(command, errorMessages) {
|
|
391
|
-
logger.error(`\n
|
|
359
|
+
logger.error(`\n${formatBlockingError(`Error in ${command} command:`)}`);
|
|
392
360
|
errorMessages.forEach(msg => logger.error(msg));
|
|
393
|
-
logger.
|
|
361
|
+
logger.log(`\n${infoLine('ℹ Run "aifabrix doctor" for environment diagnostics.')}\n`);
|
|
394
362
|
}
|
|
395
363
|
|
|
396
364
|
/**
|