@aifabrix/builder 2.44.6 → 2.45.5

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 (257) hide show
  1. package/.cursor/rules/cli-layout.mdc +7 -3
  2. package/.cursor/rules/docs-rules.mdc +3 -1
  3. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
  4. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
  5. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
  6. package/jest.projects.js +100 -1
  7. package/lib/api/agent-metadata-validation.api.js +93 -0
  8. package/lib/api/audit-api-helpers.js +68 -0
  9. package/lib/api/audit.api.js +119 -0
  10. package/lib/api/auth-cache.api.js +44 -0
  11. package/lib/api/certificates.api.js +4 -4
  12. package/lib/api/credential.api.js +2 -2
  13. package/lib/api/dataplane-health.api.js +137 -0
  14. package/lib/api/dataplane-sync.api.js +27 -0
  15. package/lib/api/datasources-core.api.js +21 -18
  16. package/lib/api/datasources-extended.api.js +22 -22
  17. package/lib/api/dimensions.api.js +3 -0
  18. package/lib/api/external-systems.api.js +13 -13
  19. package/lib/api/governance-scenarios.api.js +39 -0
  20. package/lib/api/groups.api.js +79 -0
  21. package/lib/api/index.js +45 -2
  22. package/lib/api/pipeline.api.js +4 -4
  23. package/lib/api/protection.api.js +157 -0
  24. package/lib/api/records-bulk.api.js +30 -0
  25. package/lib/api/records-search-parse.js +49 -0
  26. package/lib/api/records-search.api.js +29 -0
  27. package/lib/api/types/agent-metadata-validation.types.js +67 -0
  28. package/lib/api/types/audit.types.js +72 -0
  29. package/lib/api/types/dataplane-health.types.js +27 -0
  30. package/lib/api/types/dimensions.types.js +2 -0
  31. package/lib/api/types/governance-scenarios.types.js +39 -0
  32. package/lib/api/types/protection.types.js +69 -0
  33. package/lib/api/types/records-bulk.types.js +35 -0
  34. package/lib/api/types/records-search.types.js +28 -0
  35. package/lib/api/user-groups.api.js +50 -0
  36. package/lib/api/users.api.js +95 -0
  37. package/lib/api/validation-run.api.js +3 -3
  38. package/lib/api/validation-runner.js +3 -3
  39. package/lib/api/wizard-platform.api.js +3 -3
  40. package/lib/api/wizard.api.js +21 -21
  41. package/lib/app/deploy.js +34 -19
  42. package/lib/app/helpers.js +3 -3
  43. package/lib/app/index.js +3 -3
  44. package/lib/app/register.js +7 -6
  45. package/lib/app/restart-display.js +52 -21
  46. package/lib/app/rotate-secret.js +7 -6
  47. package/lib/app/run-helpers.js +15 -8
  48. package/lib/app/run.js +57 -9
  49. package/lib/app/show-display.js +7 -0
  50. package/lib/app/show.js +87 -5
  51. package/lib/build/index.js +9 -5
  52. package/lib/cli/index.js +4 -2
  53. package/lib/cli/infra-guided.js +62 -28
  54. package/lib/cli/installation-log-command.js +73 -0
  55. package/lib/cli/setup-app.help.js +50 -2
  56. package/lib/cli/setup-app.js +44 -25
  57. package/lib/cli/setup-app.test-commands.js +80 -21
  58. package/lib/cli/setup-auth.js +124 -52
  59. package/lib/cli/setup-external-system.js +37 -3
  60. package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
  61. package/lib/cli/setup-infra-up-platform-action.js +131 -0
  62. package/lib/cli/setup-infra.js +60 -119
  63. package/lib/cli/setup-platform.js +1 -1
  64. package/lib/cli/setup-utility-help-after.js +45 -0
  65. package/lib/cli/setup-utility-resolve.js +132 -0
  66. package/lib/cli/setup-utility.js +148 -116
  67. package/lib/commands/app-logs.js +81 -33
  68. package/lib/commands/auth-config.js +116 -18
  69. package/lib/commands/auth-status-dataplane-version.js +231 -0
  70. package/lib/commands/auth-status-display.js +94 -0
  71. package/lib/commands/auth-status.js +70 -6
  72. package/lib/commands/credential-push.js +10 -5
  73. package/lib/commands/datasource-load-export-cli.js +178 -0
  74. package/lib/commands/datasource-test-trust-cli.js +114 -0
  75. package/lib/commands/datasource-unified-test-cli.js +25 -17
  76. package/lib/commands/datasource-unified-test-cli.options.js +18 -3
  77. package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
  78. package/lib/commands/datasource-verify-audit-cli.js +96 -0
  79. package/lib/commands/datasource.js +68 -1
  80. package/lib/commands/dimension-value.js +43 -13
  81. package/lib/commands/dimension.js +38 -7
  82. package/lib/commands/identity-apply-sync.js +131 -0
  83. package/lib/commands/identity-group-cli.js +146 -0
  84. package/lib/commands/identity-membership-role-cache.js +175 -0
  85. package/lib/commands/identity-shared.js +94 -0
  86. package/lib/commands/identity-user-cli.js +158 -0
  87. package/lib/commands/identity.js +43 -0
  88. package/lib/commands/login.js +46 -16
  89. package/lib/commands/protection-cli-leaves.js +286 -0
  90. package/lib/commands/protection.js +50 -0
  91. package/lib/commands/repair.js +13 -3
  92. package/lib/commands/setup-modes.js +19 -6
  93. package/lib/commands/setup-prompts.js +41 -8
  94. package/lib/commands/setup.js +114 -9
  95. package/lib/commands/teardown.js +54 -5
  96. package/lib/commands/test-e2e-external.js +40 -9
  97. package/lib/commands/test-governance-cli-options.js +47 -0
  98. package/lib/commands/test-governance-command-action.js +55 -0
  99. package/lib/commands/test-governance-external.js +88 -0
  100. package/lib/commands/test-trust-cli-options.js +32 -0
  101. package/lib/commands/test-trust-command-action.js +118 -0
  102. package/lib/commands/test-trust-external.js +127 -0
  103. package/lib/commands/up-common.js +48 -14
  104. package/lib/commands/up-dataplane.js +21 -18
  105. package/lib/commands/up-miso.js +12 -8
  106. package/lib/commands/upload.js +38 -15
  107. package/lib/commands/wizard-core.js +3 -0
  108. package/lib/core/audit-logger.js +1 -34
  109. package/lib/core/config-admin-email.js +56 -0
  110. package/lib/core/config-device-dataplane.js +185 -0
  111. package/lib/core/config-normalize.js +60 -0
  112. package/lib/core/config-registered-controller-urls.js +54 -0
  113. package/lib/core/config.js +33 -50
  114. package/lib/core/secrets-ensure-infra.js +1 -1
  115. package/lib/core/secrets-env-content.js +86 -90
  116. package/lib/core/secrets-env-declarative-expand.js +170 -0
  117. package/lib/core/secrets-env-write.js +2 -0
  118. package/lib/core/secrets-load.js +106 -102
  119. package/lib/datasource/agent-trust-debug-log.js +45 -0
  120. package/lib/datasource/agent-trust-fetch.js +115 -0
  121. package/lib/datasource/agent-trust-map.js +101 -0
  122. package/lib/datasource/agent-trust-mock.js +44 -0
  123. package/lib/datasource/agent-trust-run.js +93 -0
  124. package/lib/datasource/agent-trust-timeout.js +25 -0
  125. package/lib/datasource/audit-evidence-display.js +80 -0
  126. package/lib/datasource/audit-evidence-extract-debug.js +103 -0
  127. package/lib/datasource/audit-evidence-extract.js +177 -0
  128. package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
  129. package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
  130. package/lib/datasource/audit-evidence-poll.js +109 -0
  131. package/lib/datasource/audit-evidence-run.js +177 -0
  132. package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
  133. package/lib/datasource/audit-evidence-verify-matrix.js +177 -0
  134. package/lib/datasource/audit-evidence-verify.js +108 -0
  135. package/lib/datasource/bulk-loader-run-batches.js +88 -0
  136. package/lib/datasource/bulk-loader-service-retry.js +52 -0
  137. package/lib/datasource/bulk-loader-service.js +127 -0
  138. package/lib/datasource/capability/run-capability-dimension.js +2 -1
  139. package/lib/datasource/datasource-exporter-resolve.js +82 -0
  140. package/lib/datasource/datasource-exporter-service.js +144 -0
  141. package/lib/datasource/datasource-identifier.js +148 -0
  142. package/lib/datasource/datasource-load-export-context.js +92 -0
  143. package/lib/datasource/integration-context.js +6 -4
  144. package/lib/datasource/local-data-paths.js +145 -0
  145. package/lib/datasource/log-cleaner.js +241 -0
  146. package/lib/datasource/log-viewer.js +111 -6
  147. package/lib/datasource/record-file-parser.js +85 -0
  148. package/lib/datasource/record-mapper.js +129 -0
  149. package/lib/datasource/resolve-app.js +74 -55
  150. package/lib/datasource/test-e2e.js +26 -2
  151. package/lib/datasource/unified-validation-run-resolve.js +45 -12
  152. package/lib/datasource/unified-validation-run.js +43 -17
  153. package/lib/datasource/validate.js +37 -20
  154. package/lib/external-system/deploy.js +5 -1
  155. package/lib/external-system/test-system-level.js +25 -4
  156. package/lib/external-system/test.js +3 -1
  157. package/lib/governance/governance-pack-loader.js +70 -0
  158. package/lib/governance/governance-run-display.js +119 -0
  159. package/lib/identity/identity-apply-core.js +108 -0
  160. package/lib/identity/identity-apply-phases.js +244 -0
  161. package/lib/identity/identity-apply-service.js +77 -0
  162. package/lib/identity/identity-csv-parser.js +114 -0
  163. package/lib/internal/fs-real-sync.js +29 -1
  164. package/lib/internal/node-fs.js +2 -0
  165. package/lib/protection/auth-context.js +45 -0
  166. package/lib/protection/convert-batch.js +90 -0
  167. package/lib/protection/load.js +41 -0
  168. package/lib/protection/paths.js +158 -0
  169. package/lib/protection/preflight-datasource-ready.js +46 -0
  170. package/lib/protection/protection-create-probe-lines.js +142 -0
  171. package/lib/protection/protection-create-probes.js +95 -0
  172. package/lib/protection/protection-create-scaffold.js +281 -0
  173. package/lib/protection/protection-display-helpers.js +445 -0
  174. package/lib/protection/protection-display.js +215 -0
  175. package/lib/protection/protection-preset-registry.js +97 -0
  176. package/lib/protection/report-exit.js +62 -0
  177. package/lib/protection/resolve.js +202 -0
  178. package/lib/protection/run-commands-validate.js +130 -0
  179. package/lib/protection/run-commands.js +216 -0
  180. package/lib/protection/run-protection-create-helpers.js +134 -0
  181. package/lib/protection/run-protection-create-probes-flow.js +43 -0
  182. package/lib/protection/run-protection-create.js +191 -0
  183. package/lib/protection/scope.js +22 -0
  184. package/lib/protection/sync-after-upload.js +64 -0
  185. package/lib/protection/upload-batch.js +127 -0
  186. package/lib/protection/validate-batch.js +113 -0
  187. package/lib/protection/validate-local.js +65 -0
  188. package/lib/resolvers/dimension-file.js +29 -4
  189. package/lib/schema/application-schema.json +4 -0
  190. package/lib/schema/external-datasource.schema.json +47 -13
  191. package/lib/schema/flag-map-validation-run.json +2 -2
  192. package/lib/schema/infra.parameter.yaml +10 -0
  193. package/lib/schema/protection.schema.json +374 -0
  194. package/lib/schema/type/credential-field-registry.json +636 -0
  195. package/lib/schema/wizard-config.schema.json +1 -1
  196. package/lib/utils/agent-trust-run-display.js +231 -0
  197. package/lib/utils/agent-trust-run-exit.js +59 -0
  198. package/lib/utils/agent-trust-wait-ui.js +94 -0
  199. package/lib/utils/app-config-resolver.js +24 -1
  200. package/lib/utils/applications-config-defaults.js +206 -0
  201. package/lib/utils/auth-config-validator.js +2 -12
  202. package/lib/utils/bash-secret-env.js +1 -1
  203. package/lib/utils/cli-exit-codes.js +30 -0
  204. package/lib/utils/cli-sync-options.js +33 -0
  205. package/lib/utils/compose-generate-docker-compose.js +111 -6
  206. package/lib/utils/compose-generator.js +17 -8
  207. package/lib/utils/controller-url.js +50 -7
  208. package/lib/utils/credential-push-ui.js +90 -0
  209. package/lib/utils/dataplane-cli-version-gate.js +202 -0
  210. package/lib/utils/dataplane-cli-version-help.js +74 -0
  211. package/lib/utils/datasource-export-display.js +137 -0
  212. package/lib/utils/datasource-load-display.js +174 -0
  213. package/lib/utils/datasource-load-export-ui.js +120 -0
  214. package/lib/utils/datasource-test-run-capability-scope.js +3 -3
  215. package/lib/utils/datasource-test-run-display.js +3 -3
  216. package/lib/utils/datasource-test-run-tty-log.js +5 -1
  217. package/lib/utils/env-copy.js +99 -14
  218. package/lib/utils/env-template.js +5 -1
  219. package/lib/utils/health-check-url.js +18 -15
  220. package/lib/utils/health-check.js +7 -5
  221. package/lib/utils/help-builder.js +30 -24
  222. package/lib/utils/infra-optional-service-flags.js +69 -0
  223. package/lib/utils/installation-log-core.js +282 -0
  224. package/lib/utils/installation-log-record.js +237 -0
  225. package/lib/utils/installation-log.js +123 -0
  226. package/lib/utils/log-redaction.js +105 -0
  227. package/lib/utils/manifest-location.js +164 -0
  228. package/lib/utils/manifest-source-emit.js +184 -0
  229. package/lib/utils/paths.js +238 -89
  230. package/lib/utils/remote-secrets-loader.js +7 -1
  231. package/lib/utils/run-cli-flags.js +29 -0
  232. package/lib/utils/secrets-canonical.js +10 -3
  233. package/lib/utils/secrets-path.js +3 -4
  234. package/lib/utils/secrets-utils.js +20 -10
  235. package/lib/utils/semver-compare.js +127 -0
  236. package/lib/utils/system-builder-root.js +10 -2
  237. package/lib/utils/upload-pipeline-ui.js +91 -0
  238. package/lib/utils/url-declarative-public-base.js +80 -12
  239. package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
  240. package/lib/utils/url-declarative-resolve-build.js +24 -393
  241. package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
  242. package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
  243. package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
  244. package/lib/utils/url-declarative-resolve.js +47 -7
  245. package/lib/utils/url-declarative-runtime-base-path.js +21 -1
  246. package/lib/utils/urls-local-registry-scan.js +103 -0
  247. package/lib/utils/urls-local-registry.js +161 -90
  248. package/lib/utils/validation-poll-ui.js +14 -7
  249. package/lib/validation/wizard-config-validator.js +58 -19
  250. package/package.json +3 -1
  251. package/templates/applications/dataplane/application.yaml +4 -0
  252. package/templates/applications/dataplane/env.template +13 -1
  253. package/templates/applications/dataplane/rbac.yaml +4 -0
  254. package/templates/applications/miso-controller/application.yaml +2 -0
  255. package/templates/applications/miso-controller/env.template +27 -29
  256. package/templates/external-system/README.md.hbs +56 -8
  257. package/.npmrc.token +0 -1
@@ -0,0 +1,286 @@
1
+ /**
2
+ * @fileoverview Protection subcommand registration (keeps setupProtectionCommands under line limit).
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const logger = require('../utils/logger');
8
+ const { resolveProtectionArgument } = require('../protection/resolve');
9
+ const {
10
+ runProtectionValidate,
11
+ runProtectionUpload,
12
+ runProtectionList,
13
+ runProtectionShow,
14
+ runProtectionDelete
15
+ } = require('../protection/run-commands');
16
+ const { runProtectionCreate } = require('../protection/run-protection-create');
17
+
18
+ const SHARED_OPTS = [
19
+ ['-e, --env <environment>', 'Environment key'],
20
+ ['-v, --verbose', 'Verbose output']
21
+ ];
22
+
23
+ const PROTECTION_CREATE_HELP_AFTER = `
24
+
25
+ Examples:
26
+ $ aifabrix protection create hubspot-companies --type country-sales
27
+ $ aifabrix protection create hr-persons --type department-manager
28
+ $ aifabrix protection create erp-projects --type project-team
29
+ $ aifabrix protection create hubspot-companies --dimension-key country --dry-run
30
+ $ aifabrix protection create hubspot-companies --type country-sales --force
31
+
32
+ Preset types:
33
+ country-sales, department-manager, customer-team, project-team, static-region, owner-direct
34
+ `;
35
+
36
+ const PROTECTION_VALIDATE_HELP_AFTER = `
37
+
38
+ Examples:
39
+ $ aifabrix protection validate hubspot-companies
40
+ $ aifabrix protection validate hubspot-companies --simulate
41
+ $ aifabrix protection validate hubspot-companies --warnings-as-errors
42
+ $ aifabrix protection validate hubspot-companies --json
43
+ `;
44
+
45
+ const PROTECTION_UPLOAD_HELP_AFTER = `
46
+
47
+ Examples:
48
+ $ aifabrix protection upload hubspot-companies
49
+ $ aifabrix protection upload hubspot-companies --dry-run
50
+ $ aifabrix protection upload hubspot-companies --no-sync
51
+ `;
52
+
53
+ const PROTECTION_LIST_HELP_AFTER = `
54
+
55
+ Examples:
56
+ $ aifabrix protection list
57
+ $ aifabrix protection list --page 1 --page-size 50
58
+ $ aifabrix protection list --filter enabled:eq:true
59
+ $ aifabrix protection list --json
60
+ `;
61
+
62
+ const PROTECTION_SHOW_HELP_AFTER = `
63
+
64
+ Examples:
65
+ $ aifabrix protection show hubspot-companies
66
+ $ aifabrix protection show hubspot-companies -v
67
+ $ aifabrix protection show hubspot-companies --json
68
+ `;
69
+
70
+ const PROTECTION_DELETE_HELP_AFTER = `
71
+
72
+ Examples:
73
+ $ aifabrix protection delete hubspot-companies
74
+ $ aifabrix protection delete hubspot-companies --yes
75
+ `;
76
+
77
+ /**
78
+ * @param {import('commander').Command} cmd
79
+ * @param {string} name
80
+ * @param {string} description
81
+ * @param {Function} action
82
+ * @param {Array<[string, string]>} extraOpts
83
+ * @param {string} helpAfter
84
+ */
85
+ function addProtectionLeaf(cmd, name, description, action, extraOpts = [], helpAfter = '') {
86
+ const leaf = cmd.command(name).description(description);
87
+ for (const [flags, desc] of [...SHARED_OPTS, ...extraOpts]) {
88
+ leaf.option(flags, desc);
89
+ }
90
+ if (helpAfter) {
91
+ leaf.addHelpText('after', helpAfter);
92
+ }
93
+ leaf.action(async(datasourceKey, options) => {
94
+ try {
95
+ const code = await action(datasourceKey, options);
96
+ if (code && code !== 0) process.exit(code);
97
+ } catch (err) {
98
+ const { formatBlockingError } = require('../utils/cli-test-layout-chalk');
99
+ logger.error(formatBlockingError(err.message || String(err)));
100
+ process.exit(1);
101
+ }
102
+ });
103
+ }
104
+
105
+ /**
106
+ * @param {import('commander').Command} protection
107
+ */
108
+ function registerProtectionCreate(protection) {
109
+ const leaf = protection
110
+ .command('create <datasourceKey>')
111
+ .description(
112
+ 'Probe dataplane datasource + Controller dimension, then scaffold under integration/.protection/'
113
+ );
114
+ for (const [flags, desc] of SHARED_OPTS) {
115
+ leaf.option(flags, desc);
116
+ }
117
+ leaf
118
+ .option('--type <type>', 'Preset type (country-sales, department-manager, customer-team, project-team, static-region, owner-direct)')
119
+ .option('--dimension-key <key>', 'Dimension catalog key (inferred from --type when omitted)')
120
+ .option('--field <name>', 'Metadata field used by preset value templates when needed')
121
+ .option('--fk-name <name>', 'FK binding name in expressions (default: same as dimension key)')
122
+ .option('--protection-key <key>', 'metadata.key (default: <datasource>-<dimension>-access)')
123
+ .option('--display-name <text>', 'metadata.displayName')
124
+ .option('--rule-key <key>', 'Rule key inside spec.rules[0]')
125
+ .option('--principal-expression <expr>', 'Group principal expression (default: Sales {{fk.<fk>.metadata.iso3}} Users)')
126
+ .option('--value-expression <expr>', 'Grant valueExpression (default: {{fk.<fk>.metadata.iso2}})')
127
+ .option('--dry-run', 'Print YAML to stdout; do not write file')
128
+ .option('--force', 'Overwrite existing integration/.protection/<datasourceKey> manifest')
129
+ .option('--disabled', 'Set spec.enabled to false', false)
130
+ .addHelpText('after', PROTECTION_CREATE_HELP_AFTER)
131
+ .action(async(datasourceKey, options) => {
132
+ try {
133
+ const code = await runProtectionCreate(datasourceKey, {
134
+ ...options,
135
+ dryRun: options.dryRun === true,
136
+ force: options.force === true,
137
+ verbose: options.verbose === true,
138
+ disabled: options.disabled === true
139
+ }, logger);
140
+ if (code && code !== 0) process.exit(code);
141
+ } catch (err) {
142
+ const { formatBlockingError } = require('../utils/cli-test-layout-chalk');
143
+ if (Array.isArray(err.probeLines)) {
144
+ for (const line of err.probeLines) {
145
+ logger.log(line);
146
+ }
147
+ }
148
+ logger.error(formatBlockingError(err.message || String(err)));
149
+ process.exit(1);
150
+ }
151
+ });
152
+ }
153
+
154
+ /**
155
+ * @param {import('commander').Command} protection
156
+ */
157
+ function registerProtectionValidate(protection) {
158
+ addProtectionLeaf(
159
+ protection,
160
+ 'validate <datasourceKey>',
161
+ 'Validate protection manifest (local schema + dataplane)',
162
+ async(datasourceKey, options) => {
163
+ const resolved = resolveProtectionArgument(datasourceKey);
164
+ return runProtectionValidate(resolved.datasourceKey, resolved.manifest, {
165
+ ...options,
166
+ manifestPath: resolved.manifestPath,
167
+ warningsAsErrors: options.warningsAsErrors === true,
168
+ simulate: options.simulate === true,
169
+ json: options.json === true,
170
+ verbose: options.verbose === true
171
+ }, logger);
172
+ },
173
+ [
174
+ ['--json', 'Output dataplane report as JSON'],
175
+ ['--warnings-as-errors', 'Pass strict=true to dataplane validate'],
176
+ ['--simulate', 'Run simulate after validate passes']
177
+ ],
178
+ PROTECTION_VALIDATE_HELP_AFTER
179
+ );
180
+ }
181
+
182
+ /**
183
+ * @param {import('commander').Command} protection
184
+ */
185
+ function registerProtectionUpload(protection) {
186
+ addProtectionLeaf(
187
+ protection,
188
+ 'upload <datasourceKey>',
189
+ 'Validate and upload protection manifest to dataplane',
190
+ async(datasourceKey, options) => {
191
+ const resolved = resolveProtectionArgument(datasourceKey);
192
+ return runProtectionUpload(resolved.datasourceKey, resolved.manifest, {
193
+ ...options,
194
+ manifestPath: resolved.manifestPath,
195
+ warningsAsErrors: options.warningsAsErrors === true,
196
+ dryRun: options.dryRun === true,
197
+ noSync: options.noSync === true,
198
+ verbose: options.verbose === true
199
+ }, logger);
200
+ },
201
+ [
202
+ ['--dry-run', 'Validate only; no upload or sync'],
203
+ ['--no-sync', 'Skip datasource sync after upload']
204
+ ],
205
+ PROTECTION_UPLOAD_HELP_AFTER
206
+ );
207
+ }
208
+
209
+ /**
210
+ * @param {import('commander').Command} protection
211
+ */
212
+ function registerProtectionList(protection) {
213
+ const leaf = protection.command('list').description('List deployed protection manifests on dataplane');
214
+ for (const [flags, desc] of SHARED_OPTS) {
215
+ leaf.option(flags, desc);
216
+ }
217
+ leaf
218
+ .option('--json', 'Output paginated list as JSON')
219
+ .option('--page <n>', 'Page (1-based)', (v) => parseInt(v, 10))
220
+ .option('--page-size <n>', 'Page size', (v) => parseInt(v, 10))
221
+ .option('--filter <expr>', 'Filter (e.g. enabled:eq:true)')
222
+ .addHelpText('after', PROTECTION_LIST_HELP_AFTER)
223
+ .action(async(options) => {
224
+ try {
225
+ const code = await runProtectionList(
226
+ {
227
+ ...options,
228
+ json: options.json === true,
229
+ verbose: options.verbose === true
230
+ },
231
+ logger
232
+ );
233
+ if (code && code !== 0) process.exit(code);
234
+ } catch (err) {
235
+ const { formatBlockingError } = require('../utils/cli-test-layout-chalk');
236
+ logger.error(formatBlockingError(err.message || String(err)));
237
+ process.exit(1);
238
+ }
239
+ });
240
+ }
241
+
242
+ /**
243
+ * @param {import('commander').Command} protection
244
+ */
245
+ function registerProtectionShow(protection) {
246
+ addProtectionLeaf(
247
+ protection,
248
+ 'show <datasourceKey>',
249
+ 'Show deployed protection state from dataplane',
250
+ async(datasourceKey, options) =>
251
+ runProtectionShow(datasourceKey, {
252
+ ...options,
253
+ json: options.json === true,
254
+ verbose: options.verbose === true
255
+ }, logger),
256
+ [['--json', 'Output merged manifest + status as JSON']],
257
+ PROTECTION_SHOW_HELP_AFTER
258
+ );
259
+ }
260
+
261
+ /**
262
+ * @param {import('commander').Command} protection
263
+ */
264
+ function registerProtectionDelete(protection) {
265
+ addProtectionLeaf(
266
+ protection,
267
+ 'delete <datasourceKey>',
268
+ 'Delete deployed protection for datasource',
269
+ async(datasourceKey, options) =>
270
+ runProtectionDelete(datasourceKey, {
271
+ ...options,
272
+ yes: options.yes === true
273
+ }, logger),
274
+ [['--yes', 'Skip confirmation prompt']],
275
+ PROTECTION_DELETE_HELP_AFTER
276
+ );
277
+ }
278
+
279
+ module.exports = {
280
+ registerProtectionCreate,
281
+ registerProtectionValidate,
282
+ registerProtectionUpload,
283
+ registerProtectionList,
284
+ registerProtectionShow,
285
+ registerProtectionDelete
286
+ };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @fileoverview Protection manifest CLI commands.
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const {
10
+ registerProtectionCreate,
11
+ registerProtectionValidate,
12
+ registerProtectionUpload,
13
+ registerProtectionList,
14
+ registerProtectionShow,
15
+ registerProtectionDelete
16
+ } = require('./protection-cli-leaves');
17
+
18
+ const PROTECTION_HELP_AFTER = `
19
+
20
+ Examples:
21
+ $ aifabrix protection create hubspot-companies --type country-sales
22
+ $ aifabrix protection validate hubspot-companies --simulate
23
+ $ aifabrix protection upload hubspot-companies --dry-run
24
+ $ aifabrix protection list
25
+ $ aifabrix protection show hubspot-companies
26
+ $ aifabrix protection delete hubspot-companies --yes
27
+
28
+ Per-command help: aifabrix protection <command> --help
29
+ `;
30
+
31
+ /**
32
+ * @param {import('commander').Command} program
33
+ */
34
+ function setupProtectionCommands(program) {
35
+ const protection = program
36
+ .command('protection')
37
+ .description('Manage protection manifests in integration/.protection/ (repo root)')
38
+ .addHelpText('after', PROTECTION_HELP_AFTER);
39
+
40
+ registerProtectionCreate(protection);
41
+ registerProtectionValidate(protection);
42
+ registerProtectionUpload(protection);
43
+ registerProtectionList(protection);
44
+ registerProtectionShow(protection);
45
+ registerProtectionDelete(protection);
46
+ }
47
+
48
+ module.exports = {
49
+ setupProtectionCommands
50
+ };
@@ -125,11 +125,21 @@ function applyAuthMethod(ctx) {
125
125
  const existingAuth = ctx.systemParsed.authentication || ctx.systemParsed.auth || {};
126
126
  const { buildAuthenticationFromMethod } = require('../external-system/generator');
127
127
  const newAuth = buildAuthenticationFromMethod(ctx.systemKey, method);
128
- const existingVars = existingAuth.variables && typeof existingAuth.variables === 'object' ? existingAuth.variables : {};
129
- const mergedVariables = { ...newAuth.variables, ...existingVars };
128
+ const existingVars =
129
+ existingAuth.variables && typeof existingAuth.variables === 'object'
130
+ ? existingAuth.variables
131
+ : {};
132
+ // When switching auth method, keep only connectivity fields from the wizard draft.
133
+ // Do not let OAuth-era variables (tokenUrl, scope, headerName, …) override the new method.
134
+ const mergedVariables = { ...newAuth.variables };
135
+ for (const key of ['baseUrl', 'testEndpoint']) {
136
+ if (existingVars[key] !== undefined && existingVars[key] !== null) {
137
+ mergedVariables[key] = existingVars[key];
138
+ }
139
+ }
130
140
  ctx.systemParsed.authentication = {
131
141
  ...newAuth,
132
- variables: Object.keys(mergedVariables).length ? mergedVariables : newAuth.variables
142
+ variables: mergedVariables
133
143
  };
134
144
  if (existingAuth.displayName !== undefined) {
135
145
  ctx.systemParsed.authentication.displayName = existingAuth.displayName;
@@ -9,7 +9,9 @@
9
9
  * - {@link runUpdateImages} — Mode 4: docker pull infra + platform images, up-infra, up-platform.
10
10
  *
11
11
  * Every handler ends with `up-infra` (idempotent, reads flags from
12
- * `config.yaml`) followed by `up-platform`. Modes 1/2/3 also remove
12
+ * `config.yaml`) followed by `up-platform`. Optional service keys
13
+ * (`traefik`, `pgadmin`, `redisCommander`) are written when missing so the
14
+ * file matches effective compose defaults. Modes 1/2/3 also remove
13
15
  * `secrets.local.yaml` so the next platform bootstrap re-resolves
14
16
  * service secrets from the catalog and re-prompts the AI tool wizard.
15
17
  *
@@ -49,6 +51,10 @@ const postgresWipe = require('../utils/postgres-wipe');
49
51
  const setupPrompts = require('./setup-prompts');
50
52
  const { handleLogin } = require('./login');
51
53
  const infraGuided = require('../cli/infra-guided');
54
+ const {
55
+ computeEffectiveInfraOptionalFlags,
56
+ persistMissingInfraOptionalServiceFlags
57
+ } = require('../utils/infra-optional-service-flags');
52
58
 
53
59
  /** Builder app keys touched by `up-platform --force`. */
54
60
  const PLATFORM_APPS = ['keycloak', 'miso-controller', 'dataplane'];
@@ -203,7 +209,9 @@ function stopSpinnerSuccess(spinner, text) {
203
209
  /**
204
210
  * Start infrastructure using the developer's `config.yaml` flags.
205
211
  * Mirrors the relevant slice of `runUpInfraCommand` from `lib/cli/setup-infra.js`
206
- * without re-applying CLI flag persistence (we never receive flags here).
212
+ * without re-applying CLI flag persistence (setup passes no Commander flags).
213
+ * After a successful start, missing `traefik` / `pgadmin` / `redisCommander`
214
+ * keys are written to match effective {@link infra.startInfra} options.
207
215
  *
208
216
  * @async
209
217
  * @param {Object} [overrides] - Optional admin overrides (fresh install only)
@@ -214,17 +222,22 @@ function stopSpinnerSuccess(spinner, text) {
214
222
  async function startInfraFromConfig(overrides = {}) {
215
223
  await config.ensureSecretsEncryptionKey();
216
224
  const cfg = await config.getConfig();
225
+ const emailOverride = String(overrides.adminEmail || '').trim();
226
+ const emailFromConfig = typeof cfg.adminEmail === 'string' ? cfg.adminEmail.trim() : '';
227
+ const adminEmailMerged = emailOverride || emailFromConfig || undefined;
228
+ const effective = computeEffectiveInfraOptionalFlags(cfg, {});
217
229
  await withMutedLogger(async() => {
218
230
  await infra.startInfra(null, {
219
- traefik: cfg.traefik === true,
220
- pgadmin: cfg.pgadmin !== false,
221
- redisCommander: cfg.redisCommander !== false,
231
+ traefik: effective.traefik,
232
+ pgadmin: effective.pgadmin,
233
+ redisCommander: effective.redisCommander,
222
234
  adminPassword: overrides.adminPassword,
223
235
  adminPwd: overrides.adminPassword,
224
- adminEmail: overrides.adminEmail,
236
+ adminEmail: adminEmailMerged,
225
237
  tlsEnabled: cfg.tlsEnabled === true
226
238
  });
227
239
  });
240
+ await persistMissingInfraOptionalServiceFlags(cfg, effective);
228
241
  }
229
242
 
230
243
  /**
@@ -3,7 +3,8 @@
3
3
  *
4
4
  * Three small prompt helpers:
5
5
  * 1. {@link promptModeSelection} - Mode menu shown when infra is already up.
6
- * 2. {@link promptAdminCredentials} - Admin email + password (fresh install only).
6
+ * 2. {@link promptAdminCredentials} - Admin email + password (fresh install only); email default
7
+ * from {@link module:lib/core/config.getAdminEmail} when `adminEmail` exists in config.yaml.
7
8
  * 3. {@link promptAiTool} - AI tool provider + keys, only when the merged
8
9
  * secret cascade (user-local + shared) does not already provide the keys.
9
10
  *
@@ -23,9 +24,11 @@
23
24
 
24
25
  'use strict';
25
26
 
27
+ const path = require('path');
26
28
  const inquirer = require('inquirer');
27
29
  const chalk = require('chalk');
28
30
  const logger = require('../utils/logger');
31
+ const config = require('../core/config');
29
32
  const secretsCore = require('../core/secrets');
30
33
  const secretsEnsure = require('../core/secrets-ensure');
31
34
  const { formatSuccessLine, infoLine } = require('../utils/cli-test-layout-chalk');
@@ -133,6 +136,8 @@ function validatePassword(input) {
133
136
 
134
137
  /**
135
138
  * Prompt for admin email + password (fresh install only).
139
+ * Reads {@link module:lib/core/config.getAdminEmail} so a previous `aifabrix setup` can prefill the
140
+ * email field (user may change it before continuing).
136
141
  * Asks once for the password and once for confirmation; returns trimmed email
137
142
  * and the verbatim password (never logged).
138
143
  *
@@ -140,13 +145,24 @@ function validatePassword(input) {
140
145
  * @returns {Promise<{ adminEmail: string, adminPassword: string }>}
141
146
  */
142
147
  async function promptAdminCredentials() {
148
+ let defaultEmail;
149
+ try {
150
+ const saved = await config.getAdminEmail();
151
+ defaultEmail = saved && saved.trim() ? saved.trim() : undefined;
152
+ } catch {
153
+ defaultEmail = undefined;
154
+ }
155
+ const emailQuestion = {
156
+ type: 'input',
157
+ name: 'adminEmail',
158
+ message: 'Admin email (Keycloak / pgAdmin login):',
159
+ validate: validateEmail
160
+ };
161
+ if (defaultEmail !== undefined) {
162
+ emailQuestion.default = defaultEmail;
163
+ }
143
164
  const answers = await inquirer.prompt([
144
- {
145
- type: 'input',
146
- name: 'adminEmail',
147
- message: 'Admin email (Keycloak / pgAdmin login):',
148
- validate: validateEmail
149
- },
165
+ emailQuestion,
150
166
  {
151
167
  type: 'password',
152
168
  name: 'adminPassword',
@@ -323,6 +339,22 @@ async function promptAiTool(options = {}) {
323
339
  );
324
340
  }
325
341
 
342
+ /**
343
+ * Absolute directory paths (with trailing sep) shown when setup will replace platform apps.
344
+ *
345
+ * @param {string} builderRoot - Absolute or relative builder root
346
+ * @param {string[]} platformApps - App folder names (e.g. `keycloak`)
347
+ * @returns {string[]}
348
+ */
349
+ function formatBuilderPlatformReplaceLines(builderRoot, platformApps) {
350
+ const root = builderRoot && typeof builderRoot === 'string' ? builderRoot : 'builder/';
351
+ const apps = Array.isArray(platformApps) ? platformApps : [];
352
+ return apps.map((a) => {
353
+ const full = path.resolve(path.join(root, a));
354
+ return full.endsWith(path.sep) ? full : `${full}${path.sep}`;
355
+ });
356
+ }
357
+
326
358
  /**
327
359
  * When `aifabrix setup` is about to run `up-platform --force`, it will wipe
328
360
  * platform app directories under `builder/`. If the builder root already
@@ -356,7 +388,7 @@ async function promptBuilderDirConflict(info) {
356
388
  systemNote +
357
389
  '\n\n' +
358
390
  'This setup path will REPLACE the platform app folders under:\n' +
359
- ` ${platformApps.map(a => `builder/${a}/`).join('\n ')}`;
391
+ ` ${formatBuilderPlatformReplaceLines(builderRoot, platformApps).join('\n ')}`;
360
392
 
361
393
  const { action } = await inquirer.prompt([
362
394
  {
@@ -379,6 +411,7 @@ module.exports = {
379
411
  MODE,
380
412
  AI_KEYS,
381
413
  BUILDER_DIR_ACTION,
414
+ formatBuilderPlatformReplaceLines,
382
415
  promptModeSelection,
383
416
  confirmDestructiveMode,
384
417
  promptAdminCredentials,
@@ -29,6 +29,7 @@ const {
29
29
 
30
30
  const setupPrompts = require('./setup-prompts');
31
31
  const setupModes = require('./setup-modes');
32
+ const installationLog = require('../utils/installation-log');
32
33
 
33
34
  const MODE = setupPrompts.MODE;
34
35
 
@@ -57,19 +58,25 @@ async function runFreshInstallFlow(options) {
57
58
  await maybePinDeveloperIdForFreshInstall(options.developer);
58
59
  logger.log(infoLine('No infrastructure detected; starting fresh install.'));
59
60
  const adminCreds = await setupPrompts.promptAdminCredentials();
61
+ await config.setAdminEmail(adminCreds.adminEmail);
60
62
  await setupModes.runFreshInstall(adminCreds);
61
63
  logger.log(formatSuccessParagraph('aifabrix setup complete (fresh install).'));
62
64
  }
63
65
 
66
+ /**
67
+ * @param {boolean} assumeYes
68
+ * @returns {Promise<{ aborted: boolean, mode: string|null }>}
69
+ */
64
70
  async function runExistingInfraFlow(assumeYes) {
65
71
  const mode = await setupPrompts.promptModeSelection();
66
72
  const proceed = await setupPrompts.confirmDestructiveMode(mode, assumeYes);
67
73
  if (!proceed) {
68
74
  logger.log(chalk.yellow('Aborted by user.'));
69
- return;
75
+ return { aborted: true, mode };
70
76
  }
71
77
  await dispatchMode(mode);
72
78
  logger.log(formatSuccessParagraph(`aifabrix setup complete (mode: ${mode}).`));
79
+ return { aborted: false, mode };
73
80
  }
74
81
 
75
82
  /**
@@ -116,6 +123,89 @@ async function dispatchMode(mode) {
116
123
  }
117
124
  }
118
125
 
126
+ /**
127
+ * @param {Object} options
128
+ * @param {boolean} assumeYes
129
+ * @returns {Promise<{ outcome: string, setupMode: string|null }>}
130
+ */
131
+ async function runSetupMainPath(options, assumeYes) {
132
+ logSetupHeader();
133
+ logger.log(formatProgress('Detecting current installation state...'));
134
+ const running = await isInfraRunning();
135
+ if (!running) {
136
+ await runFreshInstallFlow(options);
137
+ return { outcome: 'success', setupMode: 'fresh' };
138
+ }
139
+ const r = await runExistingInfraFlow(assumeYes);
140
+ const outcome = r.aborted ? 'aborted' : 'success';
141
+ return { outcome, setupMode: r.mode };
142
+ }
143
+
144
+ async function readConfigSnapshotForSetupLog() {
145
+ try {
146
+ return await config.getConfig();
147
+ } catch {
148
+ return {};
149
+ }
150
+ }
151
+
152
+ function tryCollectPlatformImagesForLog(outcome) {
153
+ if (outcome !== 'success') return undefined;
154
+ try {
155
+ return installationLog.collectPlatformAppImages(['keycloak', 'miso-controller', 'dataplane'], {});
156
+ } catch {
157
+ return undefined;
158
+ }
159
+ }
160
+
161
+ async function readSetupLogUrlFields() {
162
+ let controllerUrl;
163
+ try {
164
+ controllerUrl = await installationLog.resolveControllerUrlForLog();
165
+ } catch {
166
+ controllerUrl = undefined;
167
+ }
168
+ let adminEmail;
169
+ try {
170
+ adminEmail = await installationLog.resolveAdminEmailPresence();
171
+ } catch {
172
+ adminEmail = 'unset';
173
+ }
174
+ return { controllerUrl, adminEmail };
175
+ }
176
+
177
+ /**
178
+ * @param {Object} payload
179
+ * @param {string} payload.outcome
180
+ * @param {Date} payload.startedAt
181
+ * @param {Date} payload.completedAt
182
+ * @param {Object} payload.options
183
+ * @param {string|null} payload.setupMode
184
+ * @param {Error|null} payload.err
185
+ */
186
+ async function appendSetupInstallationRecord(payload) {
187
+ const cfg = await readConfigSnapshotForSetupLog();
188
+ const platformApps = tryCollectPlatformImagesForLog(payload.outcome);
189
+ const { controllerUrl, adminEmail } = await readSetupLogUrlFields();
190
+ try {
191
+ await installationLog.appendInstallationRecord({
192
+ command: 'setup',
193
+ outcome: payload.outcome,
194
+ startedAt: payload.startedAt,
195
+ completedAt: payload.completedAt,
196
+ options: payload.options,
197
+ setupMode: payload.setupMode || undefined,
198
+ infra: cfg && typeof cfg === 'object' ? { cfg, options: {} } : undefined,
199
+ platformApps,
200
+ configExtra: { controllerUrl, adminEmail },
201
+ error: payload.err || undefined,
202
+ errorCode: payload.err && payload.err.code ? String(payload.err.code) : undefined
203
+ });
204
+ } catch {
205
+ // never block setup on log failure
206
+ }
207
+ }
208
+
119
209
  /**
120
210
  * Run the setup wizard / dispatcher.
121
211
  *
@@ -129,17 +219,32 @@ async function dispatchMode(mode) {
129
219
  */
130
220
  async function handleSetup(options = {}) {
131
221
  const assumeYes = options.yes === true || options.assumeYes === true;
222
+ const startedAt = new Date();
223
+ let outcome = 'success';
224
+ let setupMode = null;
225
+ let err = null;
132
226
 
133
- logSetupHeader();
134
-
135
- logger.log(formatProgress('Detecting current installation state...'));
136
- const running = await isInfraRunning();
227
+ try {
228
+ const main = await runSetupMainPath(options, assumeYes);
229
+ outcome = main.outcome;
230
+ setupMode = main.setupMode;
231
+ } catch (e) {
232
+ outcome = 'failure';
233
+ err = e;
234
+ } finally {
235
+ await appendSetupInstallationRecord({
236
+ outcome,
237
+ startedAt,
238
+ completedAt: new Date(),
239
+ options,
240
+ setupMode,
241
+ err
242
+ });
243
+ }
137
244
 
138
- if (!running) {
139
- await runFreshInstallFlow(options);
140
- return;
245
+ if (err) {
246
+ throw err;
141
247
  }
142
- await runExistingInfraFlow(assumeYes);
143
248
  }
144
249
 
145
250
  module.exports = {