@aifabrix/builder 2.45.5 → 2.46.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 (231) hide show
  1. package/.cursor/rules/cli-layout.mdc +2 -0
  2. package/.cursor/rules/cli-product-neutral.mdc +43 -0
  3. package/.cursor/rules/project-rules.mdc +1 -0
  4. package/README.md +43 -17
  5. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
  6. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
  7. package/jest.config.default.js +2 -1
  8. package/jest.config.isolated.js +2 -1
  9. package/jest.global-live-fabrix-hooks.js +36 -0
  10. package/jest.isolated-projects.js +130 -0
  11. package/jest.projects.js +41 -136
  12. package/lib/api/api-response-errors.js +124 -0
  13. package/lib/api/governance-scenario-pack.api.js +159 -0
  14. package/lib/api/index.js +43 -3
  15. package/lib/api/lifecycle.api.js +77 -0
  16. package/lib/api/pipeline.api.js +3 -1
  17. package/lib/api/records-bulk.api.js +14 -1
  18. package/lib/api/validation-runner.js +3 -1
  19. package/lib/app/deploy-config.js +4 -1
  20. package/lib/app/down.js +20 -1
  21. package/lib/app/push.js +4 -5
  22. package/lib/app/register.js +7 -4
  23. package/lib/app/run-env-compose.js +84 -30
  24. package/lib/build/index.js +5 -0
  25. package/lib/certification/sync-system-certification.js +13 -6
  26. package/lib/cli/index.js +4 -0
  27. package/lib/cli/infra-guided-auth.js +109 -0
  28. package/lib/cli/infra-guided.js +84 -81
  29. package/lib/cli/setup-app.help.js +0 -31
  30. package/lib/cli/setup-app.js +28 -2
  31. package/lib/cli/setup-app.test-commands.js +5 -57
  32. package/lib/cli/setup-auth.js +25 -0
  33. package/lib/cli/setup-certification-commands.js +147 -0
  34. package/lib/cli/setup-certification.help.js +141 -0
  35. package/lib/cli/setup-environment.js +19 -2
  36. package/lib/cli/setup-external-system-help.js +62 -0
  37. package/lib/cli/setup-external-system.js +25 -3
  38. package/lib/cli/setup-infra.js +9 -1
  39. package/lib/cli/setup-platform.js +6 -1
  40. package/lib/cli/setup-secrets.js +33 -1
  41. package/lib/cli/setup-utility-help-after.js +22 -2
  42. package/lib/cli/setup-utility-repair.js +198 -0
  43. package/lib/cli/setup-utility-resolve.js +40 -4
  44. package/lib/cli/setup-utility-validate.js +197 -0
  45. package/lib/cli/setup-utility.js +4 -239
  46. package/lib/commands/auth-status-client-tokens.js +214 -0
  47. package/lib/commands/auth-status-dataplane-version.js +58 -20
  48. package/lib/commands/auth-status.js +41 -47
  49. package/lib/commands/datasource-test-trust-cli.js +27 -23
  50. package/lib/commands/datasource-unified-test-cli.js +6 -3
  51. package/lib/commands/datasource-unified-test-cli.options.js +5 -1
  52. package/lib/commands/datasource.js +7 -7
  53. package/lib/commands/dev-show-display.js +8 -1
  54. package/lib/commands/governance-cli-shared.js +94 -0
  55. package/lib/commands/governance-command-action.js +132 -0
  56. package/lib/commands/governance-download-external.js +34 -0
  57. package/lib/commands/governance-generate-external.js +71 -0
  58. package/lib/commands/governance-upload-external.js +36 -0
  59. package/lib/commands/governance-verify-external.js +64 -0
  60. package/lib/commands/governance.js +144 -0
  61. package/lib/commands/lifecycle-command-action.js +123 -0
  62. package/lib/commands/lifecycle-external.js +268 -0
  63. package/lib/commands/login.js +11 -2
  64. package/lib/commands/repair-auth-apply.js +127 -0
  65. package/lib/commands/repair-changed-files.js +41 -0
  66. package/lib/commands/repair-datasource-keys.js +4 -1
  67. package/lib/commands/repair-datasource-run.js +59 -0
  68. package/lib/commands/repair-datasource-test-payload.js +259 -0
  69. package/lib/commands/repair-datasource.js +1 -51
  70. package/lib/commands/repair-env-template.js +64 -13
  71. package/lib/commands/repair-external-run.js +216 -0
  72. package/lib/commands/repair-openapi-sync.js +23 -0
  73. package/lib/commands/repair-persist.js +4 -0
  74. package/lib/commands/repair-rbac.js +52 -1
  75. package/lib/commands/repair-steps.js +145 -0
  76. package/lib/commands/repair.js +29 -296
  77. package/lib/commands/secrets-get.js +184 -0
  78. package/lib/commands/secrets-remove-all.js +3 -1
  79. package/lib/commands/setup-image-refresh.js +146 -0
  80. package/lib/commands/setup-modes.js +158 -162
  81. package/lib/commands/setup-platform-auth.js +76 -0
  82. package/lib/commands/setup-prompts-admin.js +244 -0
  83. package/lib/commands/setup-prompts-platform-mode.js +67 -0
  84. package/lib/commands/setup-prompts-validators.js +36 -0
  85. package/lib/commands/setup-prompts.js +24 -76
  86. package/lib/commands/setup-run-context.js +45 -0
  87. package/lib/commands/setup-spinners.js +39 -0
  88. package/lib/commands/setup.js +27 -5
  89. package/lib/commands/teardown.js +39 -0
  90. package/lib/commands/test-e2e-external.js +24 -8
  91. package/lib/commands/test-governance-external.js +3 -24
  92. package/lib/commands/test-trust-external.js +20 -21
  93. package/lib/commands/up-common.js +25 -13
  94. package/lib/commands/up-dataplane.js +50 -13
  95. package/lib/commands/upload-publish-context.js +75 -0
  96. package/lib/commands/upload-sidecar-followups.js +89 -0
  97. package/lib/commands/upload.js +90 -108
  98. package/lib/commands/verify-governance-combined-external.js +61 -0
  99. package/lib/commands/verify-governance-command-action.js +64 -0
  100. package/lib/commands/verify-governance-db.js +102 -0
  101. package/lib/commands/verify-governance-external.js +77 -0
  102. package/lib/commands/verify-governance-result-builders.js +71 -0
  103. package/lib/commands/verify-operations-command-action.js +68 -0
  104. package/lib/commands/verify-operations-external.js +268 -0
  105. package/lib/commands/verify-operations-persist.js +75 -0
  106. package/lib/commands/verify-operations-step-plan.js +68 -0
  107. package/lib/commands/verify-operations-steps.js +111 -0
  108. package/lib/commands/verify-trust-command-action.js +70 -0
  109. package/lib/commands/verify-trust-external.js +67 -0
  110. package/lib/commands/wizard-config-normalizer.js +43 -23
  111. package/lib/commands/wizard-core.js +2 -0
  112. package/lib/core/admin-password-generator.js +38 -0
  113. package/lib/core/admin-secrets-bundle.js +111 -0
  114. package/lib/core/admin-secrets-env-overlay.js +62 -0
  115. package/lib/core/admin-secrets.js +32 -4
  116. package/lib/core/config-attach-extensions.js +3 -0
  117. package/lib/core/config-platform-controller.js +44 -0
  118. package/lib/core/config-setup-installation-profile.js +42 -0
  119. package/lib/core/config-setup-platform-mode.js +46 -0
  120. package/lib/core/config.js +46 -36
  121. package/lib/core/secrets-admin-env.js +6 -2
  122. package/lib/core/secrets-ensure.js +4 -0
  123. package/lib/core/secrets-env-content.js +54 -48
  124. package/lib/core/secrets-env-write.js +10 -1
  125. package/lib/core/secrets-infra-placeholder-sync.js +2 -0
  126. package/lib/core/setup-installation-profile.js +43 -0
  127. package/lib/core/setup-platform-mode.js +55 -0
  128. package/lib/datasource/agent-trust-debug-log.js +1 -1
  129. package/lib/datasource/agent-trust-run.js +5 -3
  130. package/lib/datasource/audit-evidence-extract-debug.js +30 -18
  131. package/lib/datasource/audit-evidence-extract.js +7 -4
  132. package/lib/datasource/audit-evidence-verify-matrix.js +54 -26
  133. package/lib/datasource/bulk-loader-run-batches.js +15 -3
  134. package/lib/datasource/datasource-load-export-context.js +3 -0
  135. package/lib/datasource/log-cleaner.js +2 -2
  136. package/lib/datasource/log-viewer.js +8 -8
  137. package/lib/datasource/test-e2e.js +4 -1
  138. package/lib/datasource/unified-validation-run-body.js +3 -1
  139. package/lib/datasource/unified-validation-run.js +5 -2
  140. package/lib/deployment/environment-poll-status.js +138 -0
  141. package/lib/deployment/environment.js +42 -34
  142. package/lib/external-system/download.js +21 -20
  143. package/lib/external-system/generator.js +5 -1
  144. package/lib/external-system/integration-auth-context.js +45 -0
  145. package/lib/external-system/test-auth.js +12 -5
  146. package/lib/external-system/test.js +6 -6
  147. package/lib/generator/external-controller-manifest.js +15 -0
  148. package/lib/governance/governance-cli-errors.js +96 -0
  149. package/lib/governance/governance-download-display.js +38 -0
  150. package/lib/governance/governance-run-display.js +215 -25
  151. package/lib/infrastructure/admin-secrets-apply.js +84 -0
  152. package/lib/infrastructure/admin-secrets-ensure.js +130 -0
  153. package/lib/infrastructure/admin-secrets-resolve.js +95 -0
  154. package/lib/infrastructure/admin-secrets-sync.js +91 -0
  155. package/lib/infrastructure/helpers.js +23 -129
  156. package/lib/infrastructure/index.js +5 -6
  157. package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
  158. package/lib/lifecycle/certification-progress.js +231 -0
  159. package/lib/lifecycle/dataplane-ui-url.js +145 -0
  160. package/lib/lifecycle/exit-codes.js +24 -0
  161. package/lib/lifecycle/json-envelope.js +25 -0
  162. package/lib/lifecycle/lifecycle-governance-options.js +51 -0
  163. package/lib/lifecycle/logger-quiet.js +29 -0
  164. package/lib/lifecycle/open-api-published.js +59 -0
  165. package/lib/lifecycle/operations-readiness-metrics.js +101 -0
  166. package/lib/lifecycle/product-model.js +196 -0
  167. package/lib/lifecycle/report-display.js +471 -0
  168. package/lib/lifecycle/scenario-upload.js +191 -0
  169. package/lib/lifecycle/verify-step-progress.js +87 -0
  170. package/lib/parameters/infra-kv-discovery.js +27 -2
  171. package/lib/protection/auth-context.js +10 -4
  172. package/lib/schema/application-schema.json +1 -1
  173. package/lib/schema/datasource-test-run.schema.json +89 -6
  174. package/lib/schema/external-datasource.schema.json +2636 -2622
  175. package/lib/schema/external-system.schema.json +1089 -761
  176. package/lib/schema/infra.parameter.yaml +45 -0
  177. package/lib/schema/type/credential-field-registry.json +103 -97
  178. package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
  179. package/lib/utils/api.js +55 -31
  180. package/lib/utils/app-relative-path-env.js +112 -0
  181. package/lib/utils/cli-secrets-error-format.js +83 -21
  182. package/lib/utils/cli-utils.js +16 -4
  183. package/lib/utils/compose-generate-docker-compose.js +4 -0
  184. package/lib/utils/compose-traefik-ingress-base.js +16 -1
  185. package/lib/utils/config-secrets-path.js +46 -0
  186. package/lib/utils/controller-url.js +88 -1
  187. package/lib/utils/credential-secrets-env.js +137 -9
  188. package/lib/utils/dataplane-resolver.js +129 -2
  189. package/lib/utils/declarative-url-ports.js +3 -3
  190. package/lib/utils/deployment-auth-mode.js +41 -0
  191. package/lib/utils/developer-volume-id.js +33 -0
  192. package/lib/utils/env-copy.js +14 -9
  193. package/lib/utils/env-kv-orphan-lines.js +43 -0
  194. package/lib/utils/error-formatters/network-errors.js +13 -4
  195. package/lib/utils/external-env-template.js +1 -0
  196. package/lib/utils/external-system-readiness-core.js +10 -0
  197. package/lib/utils/external-system-readiness-display.js +80 -23
  198. package/lib/utils/health-check.js +4 -1
  199. package/lib/utils/help-builder.js +11 -8
  200. package/lib/utils/keycloak-realm-ready.js +101 -0
  201. package/lib/utils/load-cip-capacity-display-config.js +61 -15
  202. package/lib/utils/manifest-source-emit.js +2 -0
  203. package/lib/utils/pipeline-upload-auth-retry.js +82 -0
  204. package/lib/utils/platform-controller-url.js +156 -0
  205. package/lib/utils/postgres-platform-bootstrap.js +199 -0
  206. package/lib/utils/postgres-wipe.js +9 -2
  207. package/lib/utils/resolve-env-mode.js +47 -0
  208. package/lib/utils/secrets-generator.js +31 -25
  209. package/lib/utils/secrets-missing-error.js +164 -0
  210. package/lib/utils/service-url-label.js +81 -0
  211. package/lib/utils/setup-quiet-output.js +25 -0
  212. package/lib/utils/template-helpers.js +15 -0
  213. package/lib/utils/token-manager-client-credentials.js +201 -0
  214. package/lib/utils/token-manager-messages.js +8 -0
  215. package/lib/utils/token-manager-refresh.js +12 -2
  216. package/lib/utils/token-manager.js +86 -105
  217. package/lib/utils/upload-pipeline-ui.js +12 -5
  218. package/lib/utils/upload-sync-options.js +42 -0
  219. package/lib/utils/url-declarative-public-base.js +21 -3
  220. package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
  221. package/lib/utils/validation-poll-ui.js +8 -0
  222. package/lib/utils/validation-run-stage.js +45 -0
  223. package/package.json +1 -1
  224. package/templates/applications/dataplane/application.yaml +2 -1
  225. package/templates/applications/dataplane/env.template +24 -1
  226. package/templates/applications/miso-controller/env.template +16 -0
  227. package/templates/external-system/README.md.hbs +74 -17
  228. package/templates/python/docker-compose.hbs +19 -18
  229. package/templates/typescript/docker-compose.hbs +18 -17
  230. package/lib/commands/test-governance-command-action.js +0 -55
  231. package/lib/commands/test-trust-command-action.js +0 -118
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @fileoverview CLI action for `aifabrix verify-trust` (plan 150.0).
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const logger = require('../utils/logger');
10
+ const { handleCommandError } = require('../utils/cli-utils');
11
+ const { runVerifyTrustForExternalSystem } = require('./verify-trust-external');
12
+ const { normalizeTestTrustCliOptions } = require('./test-trust-cli-options');
13
+ const { displayVerifyTrustTTY } = require('../lifecycle/report-display');
14
+ const { exitCodeFromVerdict } = require('../lifecycle/exit-codes');
15
+ const { buildJsonEnvelope } = require('../lifecycle/json-envelope');
16
+
17
+ async function runVerifyTrustCommandAction(appName, options, cmd) {
18
+ const opts = normalizeTestTrustCliOptions(options, cmd);
19
+ const pathsUtil = require('../utils/paths');
20
+ const appType = await pathsUtil.detectAppType(appName).catch(() => null);
21
+ if (!appType || appType.baseDir !== 'integration') {
22
+ throw new Error(
23
+ 'verify-trust applies to external integration folders only (integration/<systemKey>/).'
24
+ );
25
+ }
26
+
27
+ const result = await runVerifyTrustForExternalSystem(appName, {
28
+ env: opts.env,
29
+ verbose: opts.verbose,
30
+ debug: opts.debug,
31
+ noSync: opts.noSync === true,
32
+ revalidate: opts.revalidate === true,
33
+ timeout: opts.timeout
34
+ });
35
+
36
+ if (opts.json) {
37
+ const envelope = buildJsonEnvelope(
38
+ {
39
+ systemKey: result.systemKey,
40
+ command: result.command,
41
+ verdict: result.verdict,
42
+ businessContextConfidencePercent: result.businessContextConfidencePercent,
43
+ datasourceRows: opts.details ? result.datasourceRows : undefined
44
+ },
45
+ opts.details || opts.verbose
46
+ );
47
+ logger.log(JSON.stringify(envelope, null, 2));
48
+ } else {
49
+ displayVerifyTrustTTY(result.systemKey, result, { details: opts.verbose || opts.details });
50
+ }
51
+
52
+ const exitCode = exitCodeFromVerdict(result.verdict);
53
+ if (exitCode !== 0) {
54
+ process.exit(exitCode);
55
+ }
56
+ }
57
+
58
+ async function verifyTrustCommandHandler(appName, options, cmd) {
59
+ try {
60
+ await runVerifyTrustCommandAction(appName, options, cmd);
61
+ } catch (error) {
62
+ handleCommandError(error, 'verify-trust');
63
+ process.exit(3);
64
+ }
65
+ }
66
+
67
+ module.exports = {
68
+ runVerifyTrustCommandAction,
69
+ verifyTrustCommandHandler
70
+ };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * @fileoverview verify-trust orchestration with product-model rollup (plan 150.0).
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { runTestTrustForExternalSystem } = require('./test-trust-external');
10
+ const {
11
+ VERDICT,
12
+ verdictFromPassed,
13
+ worstConfidencePercent
14
+ } = require('../lifecycle/product-model');
15
+
16
+ /**
17
+ * @param {Array<Object>} rows
18
+ * @returns {Array<Object>}
19
+ */
20
+ function mapTrustDatasourceRows(rows) {
21
+ return (rows || []).map(row => {
22
+ const tr = row.trustRun;
23
+ const confidencePercent =
24
+ tr && typeof tr.confidence === 'number' ? Math.round(tr.confidence * 100) : null;
25
+ const verdict = row.success ? VERDICT.VERIFIED : VERDICT.FAILED;
26
+ return {
27
+ datasourceKey: row.key,
28
+ verdict,
29
+ confidencePercent,
30
+ trustDecision: tr?.trustDecision,
31
+ error: row.error
32
+ };
33
+ });
34
+ }
35
+
36
+ /**
37
+ * @async
38
+ * @param {string} systemKey
39
+ * @param {Object} options
40
+ * @returns {Promise<Object>}
41
+ */
42
+ async function runVerifyTrustForExternalSystem(systemKey, options = {}) {
43
+ const { success, results, systemKey: resolvedKey } = await runTestTrustForExternalSystem(
44
+ systemKey,
45
+ options
46
+ );
47
+ const datasourceRows = mapTrustDatasourceRows(results);
48
+ const confidences = datasourceRows
49
+ .map(r => (typeof r.confidencePercent === 'number' ? r.confidencePercent / 100 : null))
50
+ .filter(v => v !== null);
51
+ const businessContextConfidencePercent = worstConfidencePercent(confidences);
52
+ const verdict = verdictFromPassed(success);
53
+
54
+ return {
55
+ systemKey: resolvedKey || systemKey,
56
+ command: 'verify-trust',
57
+ verdict,
58
+ businessContextConfidencePercent,
59
+ datasourceRows,
60
+ results
61
+ };
62
+ }
63
+
64
+ module.exports = {
65
+ runVerifyTrustForExternalSystem,
66
+ mapTrustDatasourceRows
67
+ };
@@ -17,6 +17,7 @@ const VALID_ENTITY_TYPES = new Set([
17
17
  'none'
18
18
  ]);
19
19
  const VALID_PORTAL_FIELDS = new Set(['text', 'textarea', 'select', 'json', 'boolean', 'number']);
20
+ const { repairTestPayload } = require('./repair-datasource-test-payload');
20
21
 
21
22
  /**
22
23
  * Normalize system config fields to schema constraints
@@ -34,6 +35,45 @@ function normalizeSystemConfig(systemConfig) {
34
35
  return systemConfig;
35
36
  }
36
37
 
38
+ /**
39
+ * @param {Object} datasourceConfig - Datasource config (mutated)
40
+ */
41
+ function normalizePortalInput(datasourceConfig) {
42
+ if (!Array.isArray(datasourceConfig.portalInput)) return;
43
+ datasourceConfig.portalInput = datasourceConfig.portalInput.filter(item => {
44
+ if (!item || typeof item !== 'object') return false;
45
+ if (!item.name || !item.field || !item.label) return false;
46
+ return VALID_PORTAL_FIELDS.has(item.field);
47
+ });
48
+ }
49
+
50
+ /**
51
+ * @param {Object} datasourceConfig - Datasource config (mutated)
52
+ */
53
+ function normalizeCipOutputModes(datasourceConfig) {
54
+ const operations = datasourceConfig.execution?.cip?.operations;
55
+ if (!operations) return;
56
+ for (const operation of Object.values(operations)) {
57
+ if (!operation || !Array.isArray(operation.steps)) continue;
58
+ for (const step of operation.steps) {
59
+ if (step?.output?.mode && step.output.mode !== 'records') {
60
+ step.output.mode = 'records';
61
+ }
62
+ }
63
+ }
64
+ }
65
+
66
+ /**
67
+ * @param {Object} datasourceConfig - Datasource config (mutated)
68
+ */
69
+ function normalizeWizardTestPayload(datasourceConfig) {
70
+ const attrs = datasourceConfig.fieldMappings?.attributes;
71
+ if (!datasourceConfig.metadataSchema || !attrs || Object.keys(attrs).length === 0) {
72
+ return;
73
+ }
74
+ repairTestPayload(datasourceConfig, []);
75
+ }
76
+
37
77
  /**
38
78
  * Normalize datasource config fields to schema constraints
39
79
  * @function normalizeDatasourceConfig
@@ -47,29 +87,9 @@ function normalizeDatasourceConfig(datasourceConfig) {
47
87
  if (datasourceConfig.entityType && !VALID_ENTITY_TYPES.has(datasourceConfig.entityType)) {
48
88
  datasourceConfig.entityType = ENTITY_TYPE_FALLBACK;
49
89
  }
50
- if (Array.isArray(datasourceConfig.portalInput)) {
51
- datasourceConfig.portalInput = datasourceConfig.portalInput.filter(item => {
52
- if (!item || typeof item !== 'object') {
53
- return false;
54
- }
55
- if (!item.name || !item.field || !item.label) {
56
- return false;
57
- }
58
- return VALID_PORTAL_FIELDS.has(item.field);
59
- });
60
- }
61
- if (datasourceConfig.execution?.cip?.operations) {
62
- for (const operation of Object.values(datasourceConfig.execution.cip.operations)) {
63
- if (!operation || !Array.isArray(operation.steps)) {
64
- continue;
65
- }
66
- for (const step of operation.steps) {
67
- if (step?.output?.mode && step.output.mode !== 'records') {
68
- step.output.mode = 'records';
69
- }
70
- }
71
- }
72
- }
90
+ normalizePortalInput(datasourceConfig);
91
+ normalizeCipOutputModes(datasourceConfig);
92
+ normalizeWizardTestPayload(datasourceConfig);
73
93
  return datasourceConfig;
74
94
  }
75
95
 
@@ -512,7 +512,9 @@ async function setupDataplaneAndAuth(options, appName) {
512
512
  } catch (error) {
513
513
  throw new Error(`Authentication failed: ${error.message}`);
514
514
  }
515
+ const { applyLabDataplaneApiKeyBearer } = require('../utils/token-manager');
515
516
  requireBearerForDataplanePipeline(authConfig);
517
+ applyLabDataplaneApiKeyBearer(authConfig);
516
518
  const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
517
519
  let dataplaneUrl;
518
520
  try {
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Cryptographically strong passwords for pro setup autogenerate.
3
+ *
4
+ * @fileoverview Pro installation password generation (show-once in setup wizard)
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const crypto = require('crypto');
12
+
13
+ const PRO_PASSWORD_BYTE_LENGTH = 18;
14
+
15
+ /**
16
+ * One random password suitable for admin roles (URL-safe, no quotes).
17
+ * @returns {string}
18
+ */
19
+ function generateRandomAdminPassword() {
20
+ return crypto.randomBytes(PRO_PASSWORD_BYTE_LENGTH).toString('base64url');
21
+ }
22
+
23
+ /**
24
+ * Distinct passwords for infra, Keycloak install, and platform UI login.
25
+ * @returns {{ infra: string, keycloak: string, platform: string }}
26
+ */
27
+ function generateProAdminPasswords() {
28
+ return {
29
+ infra: generateRandomAdminPassword(),
30
+ keycloak: generateRandomAdminPassword(),
31
+ platform: generateRandomAdminPassword()
32
+ };
33
+ }
34
+
35
+ module.exports = {
36
+ generateRandomAdminPassword,
37
+ generateProAdminPasswords
38
+ };
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Map setup password bundles onto admin-secrets.env keys.
3
+ *
4
+ * @fileoverview Single (dev) vs split (pro) admin credential bundles
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const DEFAULT_REDIS_HOST = 'local:redis:6379:0:';
12
+ const DEFAULT_REDIS_COMMANDER_USER = 'admin';
13
+ const DEFAULT_KEYCLOAK_ADMIN_USERNAME = 'admin';
14
+
15
+ /**
16
+ * @typedef {'single'|'split'} AdminPasswordBundleMode
17
+ */
18
+
19
+ /**
20
+ * @typedef {Object} AdminPasswordBundleSingle
21
+ * @property {'single'} mode
22
+ * @property {string} password
23
+ */
24
+
25
+ /**
26
+ * @typedef {Object} AdminPasswordBundleSplit
27
+ * @property {'split'} mode
28
+ * @property {string} infra
29
+ * @property {string} keycloak
30
+ * @property {string} platform
31
+ */
32
+
33
+ /**
34
+ * @param {string} password
35
+ * @returns {AdminPasswordBundleSingle}
36
+ */
37
+ function singlePasswordBundle(password) {
38
+ return { mode: 'single', password: String(password) };
39
+ }
40
+
41
+ /**
42
+ * @param {{ infra: string, keycloak: string, platform: string }} parts
43
+ * @returns {AdminPasswordBundleSplit}
44
+ */
45
+ function splitPasswordBundle(parts) {
46
+ return {
47
+ mode: 'split',
48
+ infra: String(parts.infra),
49
+ keycloak: String(parts.keycloak),
50
+ platform: String(parts.platform)
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Apply bundle onto a decrypted admin-secrets object (mutates copy).
56
+ * @param {Object.<string, string>} adminObj
57
+ * @param {AdminPasswordBundleSingle|AdminPasswordBundleSplit} bundle
58
+ * @returns {Object.<string, string>}
59
+ */
60
+ function applyPasswordBundleToAdminObj(adminObj, bundle) {
61
+ const merged = { ...(adminObj || {}) };
62
+ merged.REDIS_HOST = merged.REDIS_HOST || DEFAULT_REDIS_HOST;
63
+ merged.REDIS_COMMANDER_USER = merged.REDIS_COMMANDER_USER || DEFAULT_REDIS_COMMANDER_USER;
64
+ merged.KEYCLOAK_ADMIN_USERNAME = DEFAULT_KEYCLOAK_ADMIN_USERNAME;
65
+
66
+ if (bundle.mode === 'single') {
67
+ const p = bundle.password;
68
+ merged.POSTGRES_PASSWORD = p;
69
+ merged.PGADMIN_DEFAULT_PASSWORD = p;
70
+ merged.REDIS_COMMANDER_PASSWORD = p;
71
+ merged.KEYCLOAK_ADMIN_PASSWORD = p;
72
+ merged.PLATFORM_ADMIN_PASSWORD = p;
73
+ return merged;
74
+ }
75
+
76
+ merged.POSTGRES_PASSWORD = bundle.infra;
77
+ merged.PGADMIN_DEFAULT_PASSWORD = bundle.infra;
78
+ merged.REDIS_COMMANDER_PASSWORD = bundle.infra;
79
+ merged.KEYCLOAK_ADMIN_PASSWORD = bundle.keycloak;
80
+ merged.PLATFORM_ADMIN_PASSWORD = bundle.platform;
81
+ return merged;
82
+ }
83
+
84
+ /**
85
+ * Build bundle from setup / up-infra CLI options.
86
+ * @param {Object} [options]
87
+ * @returns {AdminPasswordBundleSingle|AdminPasswordBundleSplit|null}
88
+ */
89
+ function passwordBundleFromCliOptions(options = {}) {
90
+ if (options.passwordBundle && options.passwordBundle.mode) {
91
+ return options.passwordBundle;
92
+ }
93
+ const infra = String(options.infraAdminPassword || '').trim();
94
+ const keycloak = String(options.keycloakAdminPassword || '').trim();
95
+ const platform = String(options.platformAdminPassword || '').trim();
96
+ if (infra && keycloak && platform) {
97
+ return splitPasswordBundle({ infra, keycloak, platform });
98
+ }
99
+ const single = String(options.adminPassword || options.adminPwd || '').trim();
100
+ if (single) {
101
+ return singlePasswordBundle(single);
102
+ }
103
+ return null;
104
+ }
105
+
106
+ module.exports = {
107
+ singlePasswordBundle,
108
+ splitPasswordBundle,
109
+ applyPasswordBundleToAdminObj,
110
+ passwordBundleFromCliOptions
111
+ };
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Map decrypted admin-secrets.env into platform container env keys.
3
+ *
4
+ * @fileoverview Overlay KEYCLOAK_* / ONBOARDING_* from admin-secrets (plan 185 Phase 2)
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /**
12
+ * @param {Object.<string, string>} adminObj
13
+ * @returns {Object.<string, string>}
14
+ */
15
+ function buildPlatformEnvOverlayFromAdminSecrets(adminObj) {
16
+ if (!adminObj || typeof adminObj !== 'object') return {};
17
+ const platformPwd =
18
+ adminObj.PLATFORM_ADMIN_PASSWORD ||
19
+ adminObj.ONBOARDING_ADMIN_PASSWORD ||
20
+ adminObj.MISO_ADMIN_PASSWORD ||
21
+ '';
22
+ const overlay = {};
23
+ if (adminObj.KEYCLOAK_ADMIN_USERNAME) {
24
+ overlay.KEYCLOAK_ADMIN_USERNAME = adminObj.KEYCLOAK_ADMIN_USERNAME;
25
+ }
26
+ if (adminObj.KEYCLOAK_ADMIN_PASSWORD) {
27
+ overlay.KEYCLOAK_ADMIN_PASSWORD = adminObj.KEYCLOAK_ADMIN_PASSWORD;
28
+ // Keycloak image reads KC_BOOTSTRAP_* on first start; keep in sync with admin-secrets (pro split bundles).
29
+ overlay.KC_BOOTSTRAP_ADMIN_PASSWORD = adminObj.KEYCLOAK_ADMIN_PASSWORD;
30
+ overlay.KC_BOOTSTRAP_ADMIN_USERNAME =
31
+ adminObj.KEYCLOAK_ADMIN_USERNAME || adminObj.KC_BOOTSTRAP_ADMIN_USERNAME || 'admin';
32
+ }
33
+ if (platformPwd) {
34
+ overlay.PLATFORM_ADMIN_PASSWORD = platformPwd;
35
+ overlay.ONBOARDING_ADMIN_PASSWORD = platformPwd;
36
+ overlay.MISO_ADMIN_PASSWORD = platformPwd;
37
+ }
38
+ const email = adminObj.PGADMIN_DEFAULT_EMAIL || adminObj.ONBOARDING_ADMIN_EMAIL;
39
+ if (email) {
40
+ overlay.ONBOARDING_ADMIN_EMAIL = email;
41
+ }
42
+ return overlay;
43
+ }
44
+
45
+ /**
46
+ * Apply admin-secrets values over resolved app env (admin-secrets wins for platform keys).
47
+ * @param {Object.<string, string>} envMap
48
+ * @param {Object.<string, string>} adminObj
49
+ */
50
+ function applyAdminSecretsPlatformOverlay(envMap, adminObj) {
51
+ const overlay = buildPlatformEnvOverlayFromAdminSecrets(adminObj);
52
+ for (const [key, value] of Object.entries(overlay)) {
53
+ if (value !== undefined && value !== null && String(value).trim() !== '') {
54
+ envMap[key] = String(value);
55
+ }
56
+ }
57
+ }
58
+
59
+ module.exports = {
60
+ buildPlatformEnvOverlayFromAdminSecrets,
61
+ applyAdminSecretsPlatformOverlay
62
+ };
@@ -16,6 +16,31 @@ const config = require('./config');
16
16
  const { decryptSecret, isEncrypted } = require('../utils/secrets-encryption');
17
17
  const { ensureSecureFilePermissions } = require('../utils/secure-file-permissions');
18
18
 
19
+ /**
20
+ * Whether an admin-secrets.env value must be double-quoted for safe round-trip parsing.
21
+ * @param {string} value
22
+ * @returns {boolean}
23
+ */
24
+ function needsAdminEnvQuoting(value) {
25
+ const s = String(value ?? '');
26
+ return /[,\s#=]/.test(s) || s !== s.trim();
27
+ }
28
+
29
+ /**
30
+ * Format one KEY=value line for admin-secrets.env (quotes when needed).
31
+ * @param {string} key
32
+ * @param {string} value - Plaintext value (not yet encrypted)
33
+ * @returns {string}
34
+ */
35
+ function formatAdminEnvLine(key, value) {
36
+ const safe = String(value ?? '').replace(/\n/g, ' ').trim();
37
+ if (!needsAdminEnvQuoting(safe)) {
38
+ return `${key}=${safe}`;
39
+ }
40
+ const escaped = safe.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
41
+ return `${key}="${escaped}"`;
42
+ }
43
+
19
44
  /**
20
45
  * Parse .env-style content into key-value map (excludes comments and empty lines).
21
46
  * Values are trimmed; does not unescape quotes.
@@ -33,7 +58,9 @@ function parseAdminEnvContent(content) {
33
58
  if (eq > 0) {
34
59
  const key = trimmed.substring(0, eq).trim();
35
60
  let value = trimmed.substring(eq + 1);
36
- if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith('\'') && value.endsWith('\''))) {
61
+ if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
62
+ value = value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
63
+ } else if (value.length >= 2 && value.startsWith('\'') && value.endsWith('\'')) {
37
64
  value = value.slice(1, -1);
38
65
  }
39
66
  map[key] = value.trim();
@@ -94,8 +121,7 @@ function envObjectToContent(obj) {
94
121
  const lines = [];
95
122
  for (const [key, value] of Object.entries(obj)) {
96
123
  if (key === undefined || key === '') continue;
97
- const safe = String(value ?? '').replace(/\n/g, ' ').trim();
98
- lines.push(`${key}=${safe}`);
124
+ lines.push(formatAdminEnvLine(key, value));
99
125
  }
100
126
  return lines.join('\n');
101
127
  }
@@ -103,5 +129,7 @@ function envObjectToContent(obj) {
103
129
  module.exports = {
104
130
  readAndDecryptAdminSecrets,
105
131
  parseAdminEnvContent,
106
- envObjectToContent
132
+ envObjectToContent,
133
+ needsAdminEnvQuoting,
134
+ formatAdminEnvLine
107
135
  };
@@ -41,6 +41,9 @@ function attachConfigExtensions(exportsObj, deps) {
41
41
 
42
42
  const { createScopedResourcesPreferenceFunctions } = require('../utils/config-scoped-resources-preference');
43
43
  Object.assign(exportsObj, createScopedResourcesPreferenceFunctions(getConfig, saveConfig));
44
+
45
+ const { createSecretsPathFunctions } = require('../utils/config-secrets-path');
46
+ Object.assign(exportsObj, createSecretsPathFunctions(getConfig, saveConfig));
44
47
  }
45
48
 
46
49
  module.exports = { attachConfigExtensions };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * `platform-controller` field in `config.yaml` — absolute Miso URL for setup / platform flows.
3
+ *
4
+ * @fileoverview config.yaml platform-controller helpers
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /**
12
+ * @param {() => Promise<Object>} getConfig
13
+ * @returns {Promise<string|null>}
14
+ */
15
+ async function getPlatformControllerUrlFromConfig(getConfig) {
16
+ const cfg = await getConfig();
17
+ const raw = cfg && cfg['platform-controller'];
18
+ if (typeof raw === 'string' && raw.trim()) {
19
+ return raw.trim().replace(/\/+$/, '');
20
+ }
21
+ return null;
22
+ }
23
+
24
+ /**
25
+ * @param {() => Promise<Object>} getConfig
26
+ * @param {(data: Object) => Promise<void>} saveConfig
27
+ * @param {(url: string) => string} normalizeControllerUrl
28
+ * @param {string} url
29
+ * @returns {Promise<void>}
30
+ */
31
+ async function setPlatformControllerUrlInConfig(getConfig, saveConfig, normalizeControllerUrl, url) {
32
+ if (!url || typeof url !== 'string') {
33
+ throw new Error('Platform controller URL is required and must be a string');
34
+ }
35
+ const normalized = normalizeControllerUrl(url);
36
+ const cfg = await getConfig();
37
+ cfg['platform-controller'] = normalized;
38
+ await saveConfig(cfg);
39
+ }
40
+
41
+ module.exports = {
42
+ getPlatformControllerUrlFromConfig,
43
+ setPlatformControllerUrlInConfig
44
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * `setupInstallationProfile` field in `config.yaml` (`dev` | `pro`).
3
+ *
4
+ * @fileoverview Persists setup wizard installation profile
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { normalizeInstallationProfile, INSTALLATION_PROFILE } = require('./setup-installation-profile');
12
+
13
+ /**
14
+ * @param {() => Promise<Object>} getConfig
15
+ * @returns {Promise<'dev'|'pro'>}
16
+ */
17
+ async function getSetupInstallationProfileFromConfig(getConfig) {
18
+ const cfg = await getConfig();
19
+ if (cfg && cfg.setupInstallationProfile) {
20
+ return normalizeInstallationProfile(cfg.setupInstallationProfile);
21
+ }
22
+ return INSTALLATION_PROFILE.DEV;
23
+ }
24
+
25
+ /**
26
+ * @param {() => Promise<Object>} getConfig
27
+ * @param {(data: Object) => Promise<void>} saveConfig
28
+ * @param {unknown} profile
29
+ * @returns {Promise<'dev'|'pro'>}
30
+ */
31
+ async function setSetupInstallationProfileInConfig(getConfig, saveConfig, profile) {
32
+ const normalized = normalizeInstallationProfile(profile);
33
+ const cfg = await getConfig();
34
+ cfg.setupInstallationProfile = normalized;
35
+ await saveConfig(cfg);
36
+ return normalized;
37
+ }
38
+
39
+ module.exports = {
40
+ getSetupInstallationProfileFromConfig,
41
+ setSetupInstallationProfileInConfig
42
+ };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * `setupPlatformMode` field in `config.yaml` (`single` | `full`).
3
+ *
4
+ * @fileoverview Persists setup wizard platform mode
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const {
12
+ normalizeSetupPlatformMode,
13
+ SETUP_PLATFORM_MODE
14
+ } = require('./setup-platform-mode');
15
+
16
+ /**
17
+ * @param {() => Promise<Object>} getConfig
18
+ * @returns {Promise<'single'|'full'>}
19
+ */
20
+ async function getSetupPlatformModeFromConfig(getConfig) {
21
+ const cfg = await getConfig();
22
+ if (cfg && cfg.setupPlatformMode) {
23
+ return normalizeSetupPlatformMode(cfg.setupPlatformMode);
24
+ }
25
+ return SETUP_PLATFORM_MODE.SINGLE;
26
+ }
27
+
28
+ /**
29
+ * @param {() => Promise<Object>} getConfig
30
+ * @param {(data: Object) => Promise<void>} saveConfig
31
+ * @param {unknown} mode
32
+ * @returns {Promise<'single'|'full'>}
33
+ */
34
+ async function setSetupPlatformModeInConfig(getConfig, saveConfig, mode) {
35
+ const normalized = normalizeSetupPlatformMode(mode);
36
+ const cfg = await getConfig();
37
+ cfg.setupPlatformMode = normalized;
38
+ await saveConfig(cfg);
39
+ return normalized;
40
+ }
41
+
42
+ module.exports = {
43
+ getSetupPlatformModeFromConfig,
44
+ setSetupPlatformModeInConfig
45
+ };
46
+