@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
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timestamped backups under integration/<app>/backup/ (same layout as datasource capability copy).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Backup before mutating integration JSON/YAML
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* True if path exists and is a regular file (not mocked by typical existsSync spies in unit tests).
|
|
16
|
+
* @param {string} filePath
|
|
17
|
+
* @returns {boolean}
|
|
18
|
+
*/
|
|
19
|
+
function isRegularFile(filePath) {
|
|
20
|
+
try {
|
|
21
|
+
return fs.statSync(filePath).isFile();
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Copies filePath into neighbor backup/ with ISO timestamp suffix.
|
|
29
|
+
* @param {string} filePath - Absolute path to file to copy
|
|
30
|
+
* @param {boolean} noBackup - When true, skip and return null
|
|
31
|
+
* @returns {string|null} Destination path or null
|
|
32
|
+
*/
|
|
33
|
+
function writeBackup(filePath, noBackup) {
|
|
34
|
+
if (noBackup) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const dir = path.dirname(filePath);
|
|
38
|
+
const backupDir = path.join(dir, 'backup');
|
|
39
|
+
fs.mkdirSync(backupDir, { recursive: true });
|
|
40
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
41
|
+
const base = path.basename(filePath);
|
|
42
|
+
const dest = path.join(backupDir, `${base}.${ts}.bak`);
|
|
43
|
+
fs.copyFileSync(filePath, dest);
|
|
44
|
+
return dest;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Backs up an existing file once per repair run (dedupes by absolute path).
|
|
49
|
+
* @param {string} filePath - Path to file that will be overwritten
|
|
50
|
+
* @param {{ dryRun?: boolean, noBackup?: boolean, backupPaths?: string[], backedUpFiles?: Set<string> }} ctx
|
|
51
|
+
* @returns {string|null}
|
|
52
|
+
*/
|
|
53
|
+
function backupIntegrationFile(filePath, ctx) {
|
|
54
|
+
const { dryRun, noBackup, backupPaths, backedUpFiles } = ctx || {};
|
|
55
|
+
if (dryRun || noBackup || !filePath || !isRegularFile(filePath)) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const abs = path.resolve(filePath);
|
|
59
|
+
if (backedUpFiles) {
|
|
60
|
+
if (backedUpFiles.has(abs)) return null;
|
|
61
|
+
backedUpFiles.add(abs);
|
|
62
|
+
}
|
|
63
|
+
const dest = writeBackup(filePath, false);
|
|
64
|
+
if (dest && Array.isArray(backupPaths)) {
|
|
65
|
+
backupPaths.push(dest);
|
|
66
|
+
}
|
|
67
|
+
return dest;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = {
|
|
71
|
+
writeBackup,
|
|
72
|
+
backupIntegrationFile,
|
|
73
|
+
isRegularFile
|
|
74
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared string redaction for file logs (audit, installation), CLI output, and
|
|
3
|
+
* `aifabrix logs <app>` container env lines (PII / secrets).
|
|
4
|
+
* Single implementation to avoid secret-handling drift between logs.
|
|
5
|
+
*
|
|
6
|
+
* @fileoverview Sensitive substring masking for operational logs
|
|
7
|
+
* @author AI Fabrix Team
|
|
8
|
+
* @version 2.0.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
/** Env key patterns that indicate a secret (mask value) — same rules as aifabrix logs <app>. */
|
|
14
|
+
const SECRET_KEY_PATTERN = /password|secret|token|credential|api[_-]?key/i;
|
|
15
|
+
|
|
16
|
+
/** Prefixes to strip before checking key (avoids masking KEYCLOAK_SERVER_URL etc.) */
|
|
17
|
+
const KEY_PREFIXES_TO_STRIP = /^KEYCLOAK_|^KEY_VAULT_/;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* URL with embedded credentials: scheme://user:password@host → scheme://user:***@host
|
|
21
|
+
* (shared with {@link maskEnvLine} / aifabrix logs <app> env dump)
|
|
22
|
+
*/
|
|
23
|
+
const URL_CREDENTIAL_PATTERN = /(\w+:\/\/)([^:@]*):([^@]+)@/g;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Masks embedded user:password@ in URLs inside any string.
|
|
27
|
+
*
|
|
28
|
+
* @param {string} text
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
function maskUrlEmbeddedCredentials(text) {
|
|
32
|
+
if (!text || typeof text !== 'string') {
|
|
33
|
+
return text;
|
|
34
|
+
}
|
|
35
|
+
return text.replace(URL_CREDENTIAL_PATTERN, '$1$2:***@');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Masks a single env line (KEY=value) for PII/secrets — same behavior as aifabrix logs <app> environment dump.
|
|
40
|
+
*
|
|
41
|
+
* @param {string} line - Line in form KEY=value
|
|
42
|
+
* @returns {string} Same line or KEY=*** or value with masked URL credentials
|
|
43
|
+
*/
|
|
44
|
+
function maskEnvLine(line) {
|
|
45
|
+
const eq = line.indexOf('=');
|
|
46
|
+
if (eq <= 0) {
|
|
47
|
+
return line;
|
|
48
|
+
}
|
|
49
|
+
const key = line.slice(0, eq);
|
|
50
|
+
const value = line.slice(eq + 1);
|
|
51
|
+
|
|
52
|
+
const keyForCheck = key.replace(KEY_PREFIXES_TO_STRIP, '');
|
|
53
|
+
const isSecretKey = SECRET_KEY_PATTERN.test(keyForCheck);
|
|
54
|
+
|
|
55
|
+
const maskedValue = maskUrlEmbeddedCredentials(value);
|
|
56
|
+
const hasUrlCredentials = maskedValue !== value;
|
|
57
|
+
|
|
58
|
+
if (isSecretKey) {
|
|
59
|
+
return `${key}=***`;
|
|
60
|
+
}
|
|
61
|
+
if (hasUrlCredentials) {
|
|
62
|
+
return `${key}=${maskedValue}`;
|
|
63
|
+
}
|
|
64
|
+
return line;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Masks sensitive data in strings (audit/installation log messages, metadata).
|
|
69
|
+
* Also applies URL embedded-credential masking (same as {@link maskEnvLine} values).
|
|
70
|
+
*
|
|
71
|
+
* @param {string} value - Value to mask
|
|
72
|
+
* @returns {string}
|
|
73
|
+
*/
|
|
74
|
+
function maskSensitiveData(value) {
|
|
75
|
+
if (!value || typeof value !== 'string') {
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const sensitivePatterns = [
|
|
80
|
+
{ pattern: /password[=:]\s*([^\s]+)/gi, replacement: 'password=***' },
|
|
81
|
+
{ pattern: /secret[=:]\s*([^\s]+)/gi, replacement: 'secret=***' },
|
|
82
|
+
{ pattern: /key[=:]\s*([^\s]+)/gi, replacement: 'key=***' },
|
|
83
|
+
{ pattern: /token[=:]\s*([^\s]+)/gi, replacement: 'token=***' },
|
|
84
|
+
{ pattern: /api[_-]?key[=:]\s*([^\s]+)/gi, replacement: 'api_key=***' }
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
let masked = value;
|
|
88
|
+
for (const { pattern, replacement } of sensitivePatterns) {
|
|
89
|
+
masked = masked.replace(pattern, replacement);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
masked = maskUrlEmbeddedCredentials(masked);
|
|
93
|
+
|
|
94
|
+
if (/^[a-f0-9]{32,}$/i.test(masked.trim())) {
|
|
95
|
+
return '***';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return masked;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = {
|
|
102
|
+
maskSensitiveData,
|
|
103
|
+
maskEnvLine,
|
|
104
|
+
maskUrlEmbeddedCredentials
|
|
105
|
+
};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical on-disk application manifest discovery (plan 141 Tier 1 + Tier 2).
|
|
3
|
+
* Lazy-loads `./paths` inside functions to avoid circular dependency with `paths.js`.
|
|
4
|
+
*
|
|
5
|
+
* @fileoverview resolveApplicationManifestPathSync for cwd-first manifest picks
|
|
6
|
+
* @author AI Fabrix Team
|
|
7
|
+
* @version 1.0.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const { nodeFs } = require('../internal/node-fs');
|
|
14
|
+
const { resolveApplicationConfigPath } = require('./app-config-resolver');
|
|
15
|
+
|
|
16
|
+
const fsSync = nodeFs();
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @returns {import('./paths')}
|
|
20
|
+
*/
|
|
21
|
+
function getPaths() {
|
|
22
|
+
return require('./paths');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} dir
|
|
27
|
+
* @returns {boolean}
|
|
28
|
+
*/
|
|
29
|
+
function hasApplicationConfig(dir) {
|
|
30
|
+
try {
|
|
31
|
+
resolveApplicationConfigPath(dir);
|
|
32
|
+
return true;
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {string} dir
|
|
40
|
+
* @returns {boolean}
|
|
41
|
+
*/
|
|
42
|
+
function isExistingDir(dir) {
|
|
43
|
+
try {
|
|
44
|
+
return fsSync.existsSync(dir) && fsSync.statSync(dir).isDirectory();
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {string} cwd
|
|
52
|
+
* @param {string} targetKey
|
|
53
|
+
* @returns {{ absolutePath: string, tier: string, appKey: string }|null}
|
|
54
|
+
*/
|
|
55
|
+
function tryTier1Integration(cwd, targetKey) {
|
|
56
|
+
const dir = path.join(cwd, 'integration', targetKey);
|
|
57
|
+
if (!isExistingDir(dir) || !hasApplicationConfig(dir)) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return { absolutePath: path.resolve(dir), tier: 'cwd-integration', appKey: targetKey };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {string} cwd
|
|
65
|
+
* @param {string} targetKey
|
|
66
|
+
* @returns {{ absolutePath: string, tier: string, appKey: string }|null}
|
|
67
|
+
*/
|
|
68
|
+
function tryTier1Builder(cwd, targetKey) {
|
|
69
|
+
const dir = path.join(cwd, 'builder', targetKey);
|
|
70
|
+
if (!isExistingDir(dir) || !hasApplicationConfig(dir)) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
return { absolutePath: path.resolve(dir), tier: 'cwd-builder', appKey: targetKey };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Tier 2: `(aifabrix-work | aifabrix-home)/builder/<key>` when a resolvable application manifest exists.
|
|
78
|
+
*
|
|
79
|
+
* @param {string} targetKey
|
|
80
|
+
* @returns {{ absolutePath: string, tier: string, appKey: string }|null}
|
|
81
|
+
*/
|
|
82
|
+
function tryTier2MaterializationBuilder(targetKey) {
|
|
83
|
+
const { getSystemBuilderRoot } = getPaths();
|
|
84
|
+
const dir = path.join(getSystemBuilderRoot(), targetKey);
|
|
85
|
+
if (!isExistingDir(dir) || !hasApplicationConfig(dir)) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
return { absolutePath: path.resolve(dir), tier: 'system-builder', appKey: targetKey };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @param {string|undefined} cwdOpt
|
|
93
|
+
* @returns {string}
|
|
94
|
+
*/
|
|
95
|
+
function resolveCwdSafe(cwdOpt) {
|
|
96
|
+
try {
|
|
97
|
+
return path.resolve(cwdOpt ? cwdOpt : process.cwd());
|
|
98
|
+
} catch {
|
|
99
|
+
return path.resolve(process.cwd());
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @param {{ targetKey?: string, mode?: string, cwd?: string }|null|undefined} opts
|
|
105
|
+
* @returns {{ targetKey: string, mode: string, cwd: string }|null}
|
|
106
|
+
*/
|
|
107
|
+
function parseManifestResolveOpts(opts) {
|
|
108
|
+
const targetKey = opts && typeof opts.targetKey === 'string' ? opts.targetKey.trim() : '';
|
|
109
|
+
if (!targetKey) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const mode = opts && opts.mode ? opts.mode : 'auto';
|
|
113
|
+
const cwd = resolveCwdSafe(opts && opts.cwd);
|
|
114
|
+
return { targetKey, mode, cwd };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @param {string} mode
|
|
119
|
+
* @param {string} cwd
|
|
120
|
+
* @param {string} targetKey
|
|
121
|
+
* @returns {{ absolutePath: string, tier: string, appKey: string }|null}
|
|
122
|
+
*/
|
|
123
|
+
function tryTier1ByMode(mode, cwd, targetKey) {
|
|
124
|
+
if (mode === 'integration' || mode === 'auto') {
|
|
125
|
+
const integrationHit = tryTier1Integration(cwd, targetKey);
|
|
126
|
+
if (integrationHit) {
|
|
127
|
+
return integrationHit;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (mode === 'builder' || mode === 'auto') {
|
|
131
|
+
const builderHit = tryTier1Builder(cwd, targetKey);
|
|
132
|
+
if (builderHit) {
|
|
133
|
+
return builderHit;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Resolves `application.yaml` (or sibling config) for an app using plan 141 order:
|
|
141
|
+
* `cwd/integration/<key>` → `cwd/builder/<key>` → `(aifabrix-work | aifabrix-home)/builder/<key>`.
|
|
142
|
+
*
|
|
143
|
+
* @param {{ targetKey: string, mode?: 'auto'|'integration'|'builder', cwd?: string }} opts
|
|
144
|
+
* @returns {{ absolutePath: string, tier: string, appKey: string }|null} Null when not found
|
|
145
|
+
*/
|
|
146
|
+
function resolveApplicationManifestPathSync(opts) {
|
|
147
|
+
const parsed = parseManifestResolveOpts(opts);
|
|
148
|
+
if (!parsed) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
const { targetKey, mode, cwd } = parsed;
|
|
152
|
+
const tier1 = tryTier1ByMode(mode, cwd, targetKey);
|
|
153
|
+
if (tier1) {
|
|
154
|
+
return tier1;
|
|
155
|
+
}
|
|
156
|
+
if (mode === 'auto') {
|
|
157
|
+
return tryTier2MaterializationBuilder(targetKey);
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
module.exports = {
|
|
163
|
+
resolveApplicationManifestPathSync
|
|
164
|
+
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan 141 P2: one gray **Manifest:** line on TTY when a command has resolved an application manifest.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview emitManifestMetadataLineIfTTY
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const { resolveApplicationConfigPath } = require('./app-config-resolver');
|
|
13
|
+
const { resolveApplicationManifestPathSync } = require('./manifest-location');
|
|
14
|
+
const { metadata } = require('./cli-test-layout-chalk');
|
|
15
|
+
const pathsUtil = require('./paths');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {string} tier - Tier id from {@link resolveApplicationManifestPathSync}
|
|
19
|
+
* @returns {string}
|
|
20
|
+
*/
|
|
21
|
+
function humanTierLabel(tier) {
|
|
22
|
+
if (tier === 'cwd-integration') return 'cwd/integration';
|
|
23
|
+
if (tier === 'cwd-builder') return 'cwd/builder';
|
|
24
|
+
if (tier === 'system-builder') return 'system-builder';
|
|
25
|
+
return tier;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Gray metadata line: `Manifest: <tier> — <configPath>`.
|
|
30
|
+
*
|
|
31
|
+
* @param {{ tier: string, configPath: string }} args
|
|
32
|
+
* @returns {string}
|
|
33
|
+
*/
|
|
34
|
+
function formatManifestSourceMetadataLine(args) {
|
|
35
|
+
const tier = args && typeof args.tier === 'string' ? args.tier : 'resolved';
|
|
36
|
+
const configPath = args && typeof args.configPath === 'string' ? args.configPath : '';
|
|
37
|
+
return metadata(`Manifest: ${humanTierLabel(tier)} — ${configPath}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {string} appKey
|
|
42
|
+
* @param {string} appPath
|
|
43
|
+
* @param {string} [cwd]
|
|
44
|
+
* @returns {string}
|
|
45
|
+
*/
|
|
46
|
+
function computeTierForAppPath(appKey, appPath, cwd) {
|
|
47
|
+
const hit = resolveApplicationManifestPathSync({
|
|
48
|
+
targetKey: appKey,
|
|
49
|
+
cwd: cwd || process.cwd()
|
|
50
|
+
});
|
|
51
|
+
if (hit && path.resolve(hit.absolutePath) === path.resolve(appPath)) {
|
|
52
|
+
return hit.tier;
|
|
53
|
+
}
|
|
54
|
+
return 'resolved';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Machine-readable manifest provenance for `--json` consumers (plan 141).
|
|
59
|
+
*
|
|
60
|
+
* @param {string} appKey
|
|
61
|
+
* @param {string} appPath - Application directory (absolute)
|
|
62
|
+
* @param {{ cwd?: string }} [opts]
|
|
63
|
+
* @returns {{ tier: string, tierLabel: string, configPath: string }}
|
|
64
|
+
*/
|
|
65
|
+
function getManifestSourcePayload(appKey, appPath, opts = {}) {
|
|
66
|
+
const key = appKey && typeof appKey === 'string' ? appKey.trim() : '';
|
|
67
|
+
const dir = appPath && typeof appPath === 'string' ? appPath.trim() : '';
|
|
68
|
+
if (!key || !dir) {
|
|
69
|
+
return { tier: 'unknown', tierLabel: 'unknown', configPath: '' };
|
|
70
|
+
}
|
|
71
|
+
let configPath = '';
|
|
72
|
+
try {
|
|
73
|
+
configPath = resolveApplicationConfigPath(dir);
|
|
74
|
+
} catch {
|
|
75
|
+
configPath = '';
|
|
76
|
+
}
|
|
77
|
+
let tier = 'resolved';
|
|
78
|
+
try {
|
|
79
|
+
tier = computeTierForAppPath(key, dir, opts.cwd);
|
|
80
|
+
} catch {
|
|
81
|
+
tier = 'resolved';
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
tier,
|
|
85
|
+
tierLabel: humanTierLabel(tier),
|
|
86
|
+
configPath
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @returns {boolean}
|
|
92
|
+
*/
|
|
93
|
+
function isStdoutTty() {
|
|
94
|
+
return Boolean(process.stdout && process.stdout.isTTY);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Logs one gray manifest line when appropriate (TTY, not JSON, not env-only).
|
|
99
|
+
*
|
|
100
|
+
* @param {{ log: function(string): void }} logger - e.g. `require('./logger')`
|
|
101
|
+
* @param {{ appKey: string, appPath: string, envOnly?: boolean, json?: boolean, cwd?: string }} opts
|
|
102
|
+
* @returns {void}
|
|
103
|
+
*/
|
|
104
|
+
function emitManifestMetadataLineIfTTY(logger, opts) {
|
|
105
|
+
if (!logger || typeof logger.log !== 'function') return;
|
|
106
|
+
if (!opts || opts.json || opts.envOnly) return;
|
|
107
|
+
if (!isStdoutTty()) return;
|
|
108
|
+
const appKey = opts.appKey && typeof opts.appKey === 'string' ? opts.appKey.trim() : '';
|
|
109
|
+
const appPath = opts.appPath && typeof opts.appPath === 'string' ? opts.appPath.trim() : '';
|
|
110
|
+
if (!appKey || !appPath) return;
|
|
111
|
+
let configPath;
|
|
112
|
+
try {
|
|
113
|
+
configPath = resolveApplicationConfigPath(appPath);
|
|
114
|
+
} catch {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const tier = computeTierForAppPath(appKey, appPath, opts.cwd);
|
|
118
|
+
logger.log(formatManifestSourceMetadataLine({ tier, configPath }));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* One gray **Manifest:** line for a platform system builder app (`keycloak`, `miso-controller`, `dataplane`).
|
|
123
|
+
*
|
|
124
|
+
* @param {{ log: function(string): void }} logger
|
|
125
|
+
* @param {string} appKey
|
|
126
|
+
* @param {{ envOnly?: boolean, json?: boolean, cwd?: string }} [opts]
|
|
127
|
+
* @returns {void}
|
|
128
|
+
*/
|
|
129
|
+
function emitSystemBuilderAppManifestLineIfTTY(logger, appKey, opts = {}) {
|
|
130
|
+
if (!appKey || typeof appKey !== 'string') return;
|
|
131
|
+
emitManifestMetadataLineIfTTY(logger, {
|
|
132
|
+
appKey,
|
|
133
|
+
appPath: pathsUtil.getBuilderPath(appKey),
|
|
134
|
+
envOnly: !!opts.envOnly,
|
|
135
|
+
json: !!opts.json,
|
|
136
|
+
cwd: opts.cwd
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Gray **Manifest:** line for each platform app (Keycloak, Miso Controller, dataplane), in order.
|
|
142
|
+
* Used after templates exist (e.g. guided `up-platform` after muted registry prep).
|
|
143
|
+
*
|
|
144
|
+
* @param {{ log: function(string): void }} logger
|
|
145
|
+
* @param {{ envOnly?: boolean, json?: boolean, cwd?: string }} [opts]
|
|
146
|
+
* @returns {void}
|
|
147
|
+
*/
|
|
148
|
+
function emitAllPlatformSystemManifestLinesIfTTY(logger, opts = {}) {
|
|
149
|
+
for (const appKey of pathsUtil.SYSTEM_BUILDER_APP_KEYS) {
|
|
150
|
+
emitSystemBuilderAppManifestLineIfTTY(logger, appKey, opts);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = {
|
|
155
|
+
emitManifestMetadataLineIfTTY,
|
|
156
|
+
emitSystemBuilderAppManifestLineIfTTY,
|
|
157
|
+
emitAllPlatformSystemManifestLinesIfTTY,
|
|
158
|
+
formatManifestSourceMetadataLine,
|
|
159
|
+
computeTierForAppPath,
|
|
160
|
+
humanTierLabel,
|
|
161
|
+
getManifestSourcePayload
|
|
162
|
+
};
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
const path = require('path');
|
|
11
11
|
const fs = require('fs');
|
|
12
|
+
const http = require('http');
|
|
12
13
|
const https = require('https');
|
|
13
14
|
const { getAifabrixHome } = require('./paths');
|
|
14
15
|
const { exec } = require('child_process');
|
|
@@ -88,10 +89,36 @@ function fetchLatestRelease() {
|
|
|
88
89
|
* @param {(msg: string) => void} [log] - Optional progress logger
|
|
89
90
|
* @returns {Promise<void>}
|
|
90
91
|
*/
|
|
91
|
-
|
|
92
|
+
/**
|
|
93
|
+
* @param {string} url
|
|
94
|
+
* @param {string} destPath
|
|
95
|
+
* @param {(msg: string) => void} [log]
|
|
96
|
+
* @param {number} [redirectsLeft]
|
|
97
|
+
* @returns {Promise<void>}
|
|
98
|
+
*/
|
|
99
|
+
function downloadToFile(url, destPath, log, redirectsLeft = 8) {
|
|
92
100
|
return new Promise((resolve, reject) => {
|
|
101
|
+
if (redirectsLeft <= 0) {
|
|
102
|
+
reject(new Error('Download redirect loop'));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
93
105
|
const file = fs.createWriteStream(destPath, { flags: 'w' });
|
|
94
|
-
const
|
|
106
|
+
const lib = url.startsWith('http:') ? http : https;
|
|
107
|
+
const req = lib.get(url, { headers: { 'User-Agent': 'aifabrix-builder-cli' } }, (res) => {
|
|
108
|
+
const loc = res.headers.location;
|
|
109
|
+
if ([301, 302, 303, 307, 308].includes(res.statusCode || 0) && loc) {
|
|
110
|
+
file.close();
|
|
111
|
+
fs.unlink(destPath, () => {});
|
|
112
|
+
let nextUrl;
|
|
113
|
+
try {
|
|
114
|
+
nextUrl = new URL(loc, url).href;
|
|
115
|
+
} catch (e) {
|
|
116
|
+
reject(new Error(`Invalid redirect Location: ${loc}`));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
downloadToFile(nextUrl, destPath, log, redirectsLeft - 1).then(resolve).catch(reject);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
95
122
|
if (res.statusCode !== 200) {
|
|
96
123
|
file.close();
|
|
97
124
|
fs.unlink(destPath, () => {});
|
|
@@ -111,7 +138,7 @@ function downloadToFile(url, destPath, log) {
|
|
|
111
138
|
req.setTimeout(120000, () => {
|
|
112
139
|
req.destroy(); reject(new Error('Download timeout'));
|
|
113
140
|
});
|
|
114
|
-
if (typeof log === 'function') log('Downloading Mutagen...');
|
|
141
|
+
if (typeof log === 'function' && redirectsLeft === 8) log('Downloading Mutagen...');
|
|
115
142
|
});
|
|
116
143
|
}
|
|
117
144
|
|