@aifabrix/builder 2.44.4 ā 2.44.6
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 +1 -1
- package/.cursor/rules/project-rules.mdc +1 -1
- package/.npmrc.token +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 +68 -17
- 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/types/wizard.types.js +2 -1
- 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/push.js +46 -23
- package/lib/app/register.js +1 -1
- package/lib/app/restart-display.js +95 -0
- package/lib/app/rotate-secret.js +1 -1
- package/lib/app/run-container-start.js +12 -6
- package/lib/app/run-env-compose.js +30 -1
- package/lib/app/run-helpers.js +44 -12
- package/lib/app/run-reload-sync.js +148 -0
- package/lib/app/run-resolve-image.js +51 -1
- package/lib/app/run.js +99 -73
- package/lib/build/index.js +75 -45
- package/lib/cli/doctor-check.js +117 -0
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided.js +445 -0
- package/lib/cli/setup-app.help.js +1 -1
- package/lib/cli/setup-app.js +20 -2
- package/lib/cli/setup-app.test-commands.js +9 -5
- package/lib/cli/setup-auth.js +26 -0
- package/lib/cli/setup-dev-path-commands.js +50 -3
- package/lib/cli/setup-infra.js +138 -61
- 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.js +97 -33
- 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 +225 -19
- package/lib/commands/repair-system-alignment.js +246 -0
- package/lib/commands/repair-system-permissions.js +168 -0
- package/lib/commands/repair.js +120 -354
- package/lib/commands/secure.js +1 -1
- package/lib/commands/setup-modes.js +455 -0
- package/lib/commands/setup-prompts.js +388 -0
- package/lib/commands/setup.js +149 -0
- package/lib/commands/teardown.js +228 -0
- package/lib/commands/test-e2e-external.js +4 -3
- package/lib/commands/up-common.js +97 -12
- package/lib/commands/up-dataplane.js +33 -11
- package/lib/commands/up-miso.js +7 -11
- package/lib/commands/upload.js +109 -23
- package/lib/commands/wizard-core-helpers.js +14 -11
- package/lib/commands/wizard-core.js +58 -15
- package/lib/commands/wizard-dataplane.js +2 -2
- package/lib/commands/wizard-entity-selection.js +72 -14
- package/lib/commands/wizard-headless.js +7 -3
- package/lib/commands/wizard-helpers.js +13 -1
- package/lib/commands/wizard.js +210 -61
- package/lib/constants/infra-compose-service-names.js +40 -0
- 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 +432 -0
- package/lib/core/secrets-env-write.js +27 -1
- package/lib/core/secrets-load.js +248 -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 +4 -1
- package/lib/external-system/download.js +61 -32
- package/lib/external-system/sync-deploy-manifest.js +33 -0
- package/lib/generator/wizard-prompts.js +7 -1
- package/lib/generator/wizard.js +34 -0
- package/lib/infrastructure/index.js +49 -19
- package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -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/external-datasource.schema.json +183 -53
- package/lib/schema/external-system.schema.json +23 -10
- package/lib/schema/infra.parameter.yaml +26 -11
- package/lib/schema/wizard-config.schema.json +2 -2
- package/lib/utils/aifabrix-config-dir-walk.js +40 -0
- package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
- package/lib/utils/app-run-containers.js +2 -2
- 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/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/external-readme.js +117 -4
- 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 +19 -4
- package/lib/utils/health-check.js +135 -105
- package/lib/utils/help-builder.js +5 -1
- package/lib/utils/image-name.js +34 -7
- package/lib/utils/integration-file-backup.js +74 -0
- package/lib/utils/mutagen-install.js +30 -3
- package/lib/utils/paths.js +108 -25
- 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 +42 -3
- package/lib/utils/resolve-docker-image-ref.js +9 -3
- package/lib/utils/secrets-ancestor-paths.js +47 -0
- 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 +24 -10
- package/lib/utils/secrets-utils.js +2 -2
- package/lib/utils/system-builder-root.js +34 -0
- package/lib/utils/url-declarative-resolve-build.js +6 -1
- package/lib/utils/url-declarative-runtime-base-path.js +32 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
- package/lib/utils/urls-local-registry.js +73 -20
- 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 +1 -1
- package/templates/applications/dataplane/application.yaml +1 -1
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/env.template +8 -6
- package/templates/applications/miso-controller/application.yaml +7 -0
- package/templates/applications/miso-controller/env.template +7 -7
- package/templates/applications/miso-controller/rbac.yaml +9 -9
- package/templates/external-system/README.md.hbs +89 -102
- 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
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walk ancestors of `startDir` for `<dir>/.aifabrix/config.yaml`.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Config directory discovery from cwd
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const path = require('path');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} startDir
|
|
15
|
+
* @param {(p: string) => boolean} existsSyncFn
|
|
16
|
+
* @returns {string|null} Absolute path to `.aifabrix` directory containing `config.yaml`
|
|
17
|
+
*/
|
|
18
|
+
function findAifabrixConfigDirFromAncestors(startDir, existsSyncFn) {
|
|
19
|
+
if (!startDir || typeof startDir !== 'string') {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
let dir = path.resolve(startDir);
|
|
23
|
+
const maxSteps = 64;
|
|
24
|
+
for (let i = 0; i < maxSteps; i += 1) {
|
|
25
|
+
const candidate = path.join(dir, '.aifabrix', 'config.yaml');
|
|
26
|
+
if (existsSyncFn(candidate)) {
|
|
27
|
+
return path.join(dir, '.aifabrix');
|
|
28
|
+
}
|
|
29
|
+
const parent = path.dirname(dir);
|
|
30
|
+
if (parent === dir) {
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
dir = parent;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = {
|
|
39
|
+
findAifabrixConfigDirFromAncestors
|
|
40
|
+
};
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Shared by `paths.getConfigDirForPaths` and `config.getConfigDir` (no circular imports).
|
|
4
4
|
*
|
|
5
5
|
* When `AIFABRIX_HOME` is set to the POSIX home (builder-server pattern) but the real
|
|
6
|
-
* config file is under `~/.aifabrix/config.yaml`, use that nested directory
|
|
7
|
-
*
|
|
6
|
+
* config file is under `~/.aifabrix/config.yaml`, use that nested directory.
|
|
7
|
+
* With no env override, walks up from cwd for `<ancestor>/.aifabrix/config.yaml`, else `~/.aifabrix`.
|
|
8
8
|
*
|
|
9
9
|
* Relative `AIFABRIX_HOME` / `AIFABRIX_CONFIG` values are anchored to the user home
|
|
10
10
|
* directory (not `process.cwd()`), so a mistaken `aifabrix-training` does not become
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
const { existsSync } = require('../internal/fs-real-sync');
|
|
22
22
|
const path = require('path');
|
|
23
23
|
const os = require('os');
|
|
24
|
+
const { findAifabrixConfigDirFromAncestors } = require('./aifabrix-config-dir-walk');
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* @returns {string}
|
|
@@ -102,6 +103,23 @@ function resolveAifabrixConfigEnvPath(raw) {
|
|
|
102
103
|
return resolveAifabrixHomeLikePath(raw);
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
/**
|
|
107
|
+
* When neither `AIFABRIX_CONFIG` nor `AIFABRIX_HOME` is set, look for `<ancestor>/.aifabrix/config.yaml`
|
|
108
|
+
* walking up from `process.cwd()`. Skipped under Jest so suites do not pick up the workspace config.
|
|
109
|
+
*
|
|
110
|
+
* @returns {string|null} Directory containing `config.yaml`, or null
|
|
111
|
+
*/
|
|
112
|
+
function findAifabrixConfigDirWalkingUpFromCwd() {
|
|
113
|
+
if (process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
return findAifabrixConfigDirFromAncestors(process.cwd(), existsSync);
|
|
118
|
+
} catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
105
123
|
/**
|
|
106
124
|
* @returns {string} Absolute directory containing `config.yaml`
|
|
107
125
|
*/
|
|
@@ -122,11 +140,16 @@ function getAifabrixRuntimeConfigDir() {
|
|
|
122
140
|
}
|
|
123
141
|
return homeDir;
|
|
124
142
|
}
|
|
143
|
+
const fromCwd = findAifabrixConfigDirWalkingUpFromCwd();
|
|
144
|
+
if (fromCwd) {
|
|
145
|
+
return fromCwd;
|
|
146
|
+
}
|
|
125
147
|
return path.join(safeHomedir(), '.aifabrix');
|
|
126
148
|
}
|
|
127
149
|
|
|
128
150
|
module.exports = {
|
|
129
151
|
getAifabrixRuntimeConfigDir,
|
|
130
152
|
resolveAifabrixHomeLikePath,
|
|
131
|
-
resolveAifabrixConfigEnvPath
|
|
153
|
+
resolveAifabrixConfigEnvPath,
|
|
154
|
+
findAifabrixConfigDirWalkingUpFromCwd
|
|
132
155
|
};
|
|
@@ -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,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 user + shared + ancestor `secrets.local.yaml`
|
|
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
|
/**
|
|
@@ -300,12 +300,20 @@ function appendValidationIssueLines(lines, envelope, maxIssues = 5) {
|
|
|
300
300
|
const code = iss && iss.code ? chalk.red(`[${iss.code}] `) : '';
|
|
301
301
|
const msg = iss && iss.message ? String(iss.message) : JSON.stringify(iss);
|
|
302
302
|
lines.push(` ${code}${chalk.yellow(msg)}`);
|
|
303
|
+
appendDpSec013Details(lines, iss);
|
|
303
304
|
}
|
|
304
305
|
if (issues.length > cap) {
|
|
305
306
|
lines.push(chalk.gray(` ⦠and ${issues.length - cap} more (see --json or debug full/raw)`));
|
|
306
307
|
}
|
|
307
308
|
}
|
|
308
309
|
|
|
310
|
+
function appendDpSec013Details(lines, iss) {
|
|
311
|
+
if (!iss || iss.code !== 'DP-SEC-013') return;
|
|
312
|
+
const perm = iss.details && iss.details.resolvedPermission ? String(iss.details.resolvedPermission) : '';
|
|
313
|
+
if (!perm) return;
|
|
314
|
+
lines.push(` ${chalk.gray('Missing permission:')} ${chalk.white(perm)}`);
|
|
315
|
+
}
|
|
316
|
+
|
|
309
317
|
/**
|
|
310
318
|
* @param {string[]} lines
|
|
311
319
|
* @param {Object} envelope
|
|
@@ -12,6 +12,7 @@ const path = require('path');
|
|
|
12
12
|
const readline = require('readline');
|
|
13
13
|
const chalk = require('chalk');
|
|
14
14
|
const { nodeFs } = require('../internal/node-fs');
|
|
15
|
+
const { formatSuccessLine } = require('./cli-layout-chalk');
|
|
15
16
|
|
|
16
17
|
const IPV4_RE = /^(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)){3}$/;
|
|
17
18
|
|
|
@@ -238,7 +239,7 @@ async function resolveHostsIpForInit(hostname, hostsIp, logger) {
|
|
|
238
239
|
async function appendHostsBlockOrPrintManual(hostsPath, block, line, logger) {
|
|
239
240
|
try {
|
|
240
241
|
await nodeFs().promises.appendFile(hostsPath, block, { encoding: 'utf8' });
|
|
241
|
-
logger.log(chalk.green(
|
|
242
|
+
logger.log(chalk.green(' ') + formatSuccessLine(`Updated ${hostsPath}`) + '\n');
|
|
242
243
|
} catch (e) {
|
|
243
244
|
if (e.code === 'EACCES' || e.code === 'EPERM') {
|
|
244
245
|
logger.log(chalk.yellow(` ā Could not write ${hostsPath} (permission denied).`));
|
|
@@ -261,7 +262,7 @@ async function appendHostsBlockOrPrintManual(hostsPath, block, line, logger) {
|
|
|
261
262
|
async function tryWriteHostsEntry(hostsPath, hostnames, ip, skipConfirm, logger) {
|
|
262
263
|
const missing = hostnames.filter((h) => !hostsFileHasHostname(hostsPath, h));
|
|
263
264
|
if (missing.length === 0) {
|
|
264
|
-
logger.log(chalk.green(
|
|
265
|
+
logger.log(chalk.green(' ') + formatSuccessLine(`Required hostnames are already listed in ${hostsPath}. Nothing to do.`) + '\n');
|
|
265
266
|
return;
|
|
266
267
|
}
|
|
267
268
|
const line = `${ip} ${missing.join(' ')}`;
|
|
@@ -6,6 +6,7 @@ const chalk = require('chalk');
|
|
|
6
6
|
const config = require('../core/config');
|
|
7
7
|
const logger = require('./logger');
|
|
8
8
|
const { ensureDevSshConfigBlock } = require('./dev-ssh-config-helper');
|
|
9
|
+
const { successGlyph } = require('./cli-layout-chalk');
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Hostname from Builder Server URL (for sync-ssh-host fallback).
|
|
@@ -46,7 +47,7 @@ async function mergeDevSshConfigAfterInit(baseUrl, devId) {
|
|
|
46
47
|
);
|
|
47
48
|
} else {
|
|
48
49
|
logger.log(
|
|
49
|
-
chalk.green('
|
|
50
|
+
`${chalk.green(' ')}${successGlyph()}${chalk.green(' SSH config updated: ')}` +
|
|
50
51
|
chalk.cyan(`Host ${res.hostAlias}`) +
|
|
51
52
|
chalk.gray(` ā ${res.configPath}`)
|
|
52
53
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { formatSuccessLine } = require('./cli-test-layout-chalk');
|
|
1
|
+
const { formatSuccessLine, headerKeyValue, formatWarningLine } = require('./cli-test-layout-chalk');
|
|
2
2
|
/**
|
|
3
3
|
* Docker Build Utilities
|
|
4
4
|
*
|
|
@@ -177,7 +177,6 @@ async function executeDockerBuild(imageName, dockerfilePath, contextPath, tag, b
|
|
|
177
177
|
const spinner = ora({ text: 'Starting Docker build...', spinner: 'dots' }).start();
|
|
178
178
|
const fsSync = require('fs');
|
|
179
179
|
const path = require('path');
|
|
180
|
-
const { getRemoteDockerEnv } = require('./remote-docker-env');
|
|
181
180
|
dockerfilePath = path.resolve(dockerfilePath);
|
|
182
181
|
contextPath = path.resolve(contextPath);
|
|
183
182
|
|
|
@@ -196,7 +195,8 @@ async function executeDockerBuild(imageName, dockerfilePath, contextPath, tag, b
|
|
|
196
195
|
}
|
|
197
196
|
|
|
198
197
|
const isTest = process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined;
|
|
199
|
-
const
|
|
198
|
+
const { getDockerExecEnv } = require('./remote-docker-env');
|
|
199
|
+
const dockerCliEnv = isTest ? { ...process.env } : await getDockerExecEnv();
|
|
200
200
|
const resolvedBuildArgs = buildArgs && typeof buildArgs === 'object' ? buildArgs : {};
|
|
201
201
|
return new Promise((resolve, reject) => {
|
|
202
202
|
runDockerBuildProcess({
|
|
@@ -207,7 +207,7 @@ async function executeDockerBuild(imageName, dockerfilePath, contextPath, tag, b
|
|
|
207
207
|
spinner,
|
|
208
208
|
resolve,
|
|
209
209
|
reject,
|
|
210
|
-
env:
|
|
210
|
+
env: dockerCliEnv,
|
|
211
211
|
buildArgs: resolvedBuildArgs,
|
|
212
212
|
noCache
|
|
213
213
|
});
|
|
@@ -258,14 +258,20 @@ async function executeBuild(imageName, dockerfilePath, contextPath, tag, options
|
|
|
258
258
|
*/
|
|
259
259
|
async function executeDockerBuildWithTag(effectiveImageName, imageName, dockerfilePath, contextPath, tag, options) {
|
|
260
260
|
const logger = require('../utils/logger');
|
|
261
|
-
const chalk = require('chalk');
|
|
262
261
|
|
|
263
|
-
logger.log(
|
|
264
|
-
logger.log(
|
|
262
|
+
logger.log(headerKeyValue('Dockerfile:', dockerfilePath));
|
|
263
|
+
logger.log(headerKeyValue('Build context:', contextPath));
|
|
265
264
|
|
|
266
265
|
await executeBuild(effectiveImageName, dockerfilePath, contextPath, tag, options);
|
|
267
266
|
|
|
268
|
-
|
|
267
|
+
const wantCompatTag =
|
|
268
|
+
effectiveImageName !== imageName &&
|
|
269
|
+
options &&
|
|
270
|
+
options.base === true;
|
|
271
|
+
if (!wantCompatTag) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
269
275
|
try {
|
|
270
276
|
const { promisify } = require('util');
|
|
271
277
|
const { exec } = require('child_process');
|
|
@@ -275,7 +281,9 @@ async function executeDockerBuildWithTag(effectiveImageName, imageName, dockerfi
|
|
|
275
281
|
await run(`docker tag ${effectiveImageName}:${tag} ${imageName}:${tag}`, { env });
|
|
276
282
|
logger.log(formatSuccessLine(`Tagged image: ${imageName}:${tag}`));
|
|
277
283
|
} catch (err) {
|
|
278
|
-
logger.log(
|
|
284
|
+
logger.log(
|
|
285
|
+
formatWarningLine(`Could not create compatibility tag ${imageName}:${tag} - ${err.message}`)
|
|
286
|
+
);
|
|
279
287
|
}
|
|
280
288
|
}
|
|
281
289
|
|