@aifabrix/builder 2.44.6 → 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.
Files changed (86) hide show
  1. package/.cursor/rules/cli-layout.mdc +7 -3
  2. package/jest.projects.js +56 -0
  3. package/lib/app/helpers.js +3 -3
  4. package/lib/app/index.js +3 -3
  5. package/lib/app/register.js +7 -6
  6. package/lib/app/restart-display.js +52 -21
  7. package/lib/app/rotate-secret.js +7 -6
  8. package/lib/app/run-helpers.js +15 -8
  9. package/lib/app/run.js +57 -9
  10. package/lib/app/show-display.js +7 -0
  11. package/lib/app/show.js +87 -5
  12. package/lib/build/index.js +9 -5
  13. package/lib/cli/infra-guided.js +42 -27
  14. package/lib/cli/installation-log-command.js +73 -0
  15. package/lib/cli/setup-app.js +11 -1
  16. package/lib/cli/setup-auth.js +94 -49
  17. package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
  18. package/lib/cli/setup-infra-up-platform-action.js +131 -0
  19. package/lib/cli/setup-infra.js +60 -119
  20. package/lib/cli/setup-platform.js +1 -1
  21. package/lib/cli/setup-utility-resolve.js +132 -0
  22. package/lib/cli/setup-utility.js +65 -51
  23. package/lib/commands/app-logs.js +81 -33
  24. package/lib/commands/auth-config.js +116 -18
  25. package/lib/commands/setup-modes.js +19 -6
  26. package/lib/commands/setup-prompts.js +41 -8
  27. package/lib/commands/setup.js +114 -9
  28. package/lib/commands/teardown.js +54 -5
  29. package/lib/commands/up-common.js +48 -14
  30. package/lib/commands/up-dataplane.js +21 -18
  31. package/lib/commands/up-miso.js +12 -8
  32. package/lib/commands/upload.js +5 -3
  33. package/lib/core/audit-logger.js +1 -34
  34. package/lib/core/config-admin-email.js +56 -0
  35. package/lib/core/config-normalize.js +60 -0
  36. package/lib/core/config-registered-controller-urls.js +54 -0
  37. package/lib/core/config.js +33 -50
  38. package/lib/core/secrets-ensure-infra.js +1 -1
  39. package/lib/core/secrets-env-content.js +86 -90
  40. package/lib/core/secrets-env-declarative-expand.js +170 -0
  41. package/lib/core/secrets-env-write.js +2 -0
  42. package/lib/core/secrets-load.js +106 -102
  43. package/lib/external-system/deploy.js +5 -1
  44. package/lib/internal/node-fs.js +2 -0
  45. package/lib/schema/application-schema.json +4 -0
  46. package/lib/schema/infra.parameter.yaml +10 -0
  47. package/lib/utils/app-config-resolver.js +24 -1
  48. package/lib/utils/applications-config-defaults.js +206 -0
  49. package/lib/utils/auth-config-validator.js +2 -12
  50. package/lib/utils/bash-secret-env.js +1 -1
  51. package/lib/utils/compose-generate-docker-compose.js +111 -6
  52. package/lib/utils/compose-generator.js +17 -8
  53. package/lib/utils/controller-url.js +50 -7
  54. package/lib/utils/env-copy.js +99 -14
  55. package/lib/utils/env-template.js +5 -1
  56. package/lib/utils/health-check-url.js +18 -15
  57. package/lib/utils/health-check.js +7 -5
  58. package/lib/utils/infra-optional-service-flags.js +69 -0
  59. package/lib/utils/installation-log-core.js +282 -0
  60. package/lib/utils/installation-log-record.js +237 -0
  61. package/lib/utils/installation-log.js +123 -0
  62. package/lib/utils/log-redaction.js +105 -0
  63. package/lib/utils/manifest-location.js +164 -0
  64. package/lib/utils/manifest-source-emit.js +162 -0
  65. package/lib/utils/paths.js +238 -89
  66. package/lib/utils/remote-secrets-loader.js +7 -1
  67. package/lib/utils/run-cli-flags.js +29 -0
  68. package/lib/utils/secrets-canonical.js +10 -3
  69. package/lib/utils/secrets-path.js +3 -4
  70. package/lib/utils/secrets-utils.js +20 -10
  71. package/lib/utils/system-builder-root.js +10 -2
  72. package/lib/utils/url-declarative-public-base.js +80 -12
  73. package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
  74. package/lib/utils/url-declarative-resolve-build.js +24 -393
  75. package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
  76. package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
  77. package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
  78. package/lib/utils/url-declarative-resolve.js +47 -7
  79. package/lib/utils/url-declarative-runtime-base-path.js +21 -1
  80. package/lib/utils/urls-local-registry-scan.js +103 -0
  81. package/lib/utils/urls-local-registry.js +161 -90
  82. package/package.json +3 -1
  83. package/templates/applications/dataplane/application.yaml +4 -0
  84. package/templates/applications/miso-controller/application.yaml +2 -0
  85. package/templates/applications/miso-controller/env.template +27 -29
  86. package/.npmrc.token +0 -1
@@ -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
+ };