@aifabrix/builder 2.45.0 → 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 (180) hide show
  1. package/.cursor/rules/docs-rules.mdc +3 -1
  2. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
  3. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
  4. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
  5. package/jest.projects.js +44 -1
  6. package/lib/api/agent-metadata-validation.api.js +93 -0
  7. package/lib/api/audit-api-helpers.js +68 -0
  8. package/lib/api/audit.api.js +119 -0
  9. package/lib/api/auth-cache.api.js +44 -0
  10. package/lib/api/certificates.api.js +4 -4
  11. package/lib/api/credential.api.js +2 -2
  12. package/lib/api/dataplane-health.api.js +137 -0
  13. package/lib/api/dataplane-sync.api.js +27 -0
  14. package/lib/api/datasources-core.api.js +21 -18
  15. package/lib/api/datasources-extended.api.js +22 -22
  16. package/lib/api/dimensions.api.js +3 -0
  17. package/lib/api/external-systems.api.js +13 -13
  18. package/lib/api/governance-scenarios.api.js +39 -0
  19. package/lib/api/groups.api.js +79 -0
  20. package/lib/api/index.js +45 -2
  21. package/lib/api/pipeline.api.js +4 -4
  22. package/lib/api/protection.api.js +157 -0
  23. package/lib/api/records-bulk.api.js +30 -0
  24. package/lib/api/records-search-parse.js +49 -0
  25. package/lib/api/records-search.api.js +29 -0
  26. package/lib/api/types/agent-metadata-validation.types.js +67 -0
  27. package/lib/api/types/audit.types.js +72 -0
  28. package/lib/api/types/dataplane-health.types.js +27 -0
  29. package/lib/api/types/dimensions.types.js +2 -0
  30. package/lib/api/types/governance-scenarios.types.js +39 -0
  31. package/lib/api/types/protection.types.js +69 -0
  32. package/lib/api/types/records-bulk.types.js +35 -0
  33. package/lib/api/types/records-search.types.js +28 -0
  34. package/lib/api/user-groups.api.js +50 -0
  35. package/lib/api/users.api.js +95 -0
  36. package/lib/api/validation-run.api.js +3 -3
  37. package/lib/api/validation-runner.js +3 -3
  38. package/lib/api/wizard-platform.api.js +3 -3
  39. package/lib/api/wizard.api.js +21 -21
  40. package/lib/app/deploy.js +34 -19
  41. package/lib/cli/index.js +4 -2
  42. package/lib/cli/infra-guided.js +22 -3
  43. package/lib/cli/setup-app.help.js +50 -2
  44. package/lib/cli/setup-app.js +33 -24
  45. package/lib/cli/setup-app.test-commands.js +80 -21
  46. package/lib/cli/setup-auth.js +30 -3
  47. package/lib/cli/setup-external-system.js +37 -3
  48. package/lib/cli/setup-utility-help-after.js +45 -0
  49. package/lib/cli/setup-utility-resolve.js +2 -2
  50. package/lib/cli/setup-utility.js +99 -81
  51. package/lib/commands/auth-status-dataplane-version.js +231 -0
  52. package/lib/commands/auth-status-display.js +94 -0
  53. package/lib/commands/auth-status.js +70 -6
  54. package/lib/commands/credential-push.js +10 -5
  55. package/lib/commands/datasource-load-export-cli.js +178 -0
  56. package/lib/commands/datasource-test-trust-cli.js +114 -0
  57. package/lib/commands/datasource-unified-test-cli.js +25 -17
  58. package/lib/commands/datasource-unified-test-cli.options.js +18 -3
  59. package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
  60. package/lib/commands/datasource-verify-audit-cli.js +96 -0
  61. package/lib/commands/datasource.js +68 -1
  62. package/lib/commands/dimension-value.js +43 -13
  63. package/lib/commands/dimension.js +38 -7
  64. package/lib/commands/identity-apply-sync.js +131 -0
  65. package/lib/commands/identity-group-cli.js +146 -0
  66. package/lib/commands/identity-membership-role-cache.js +175 -0
  67. package/lib/commands/identity-shared.js +94 -0
  68. package/lib/commands/identity-user-cli.js +158 -0
  69. package/lib/commands/identity.js +43 -0
  70. package/lib/commands/login.js +46 -16
  71. package/lib/commands/protection-cli-leaves.js +286 -0
  72. package/lib/commands/protection.js +50 -0
  73. package/lib/commands/repair.js +13 -3
  74. package/lib/commands/test-e2e-external.js +40 -9
  75. package/lib/commands/test-governance-cli-options.js +47 -0
  76. package/lib/commands/test-governance-command-action.js +55 -0
  77. package/lib/commands/test-governance-external.js +88 -0
  78. package/lib/commands/test-trust-cli-options.js +32 -0
  79. package/lib/commands/test-trust-command-action.js +118 -0
  80. package/lib/commands/test-trust-external.js +127 -0
  81. package/lib/commands/upload.js +34 -13
  82. package/lib/commands/wizard-core.js +3 -0
  83. package/lib/core/config-device-dataplane.js +185 -0
  84. package/lib/datasource/agent-trust-debug-log.js +45 -0
  85. package/lib/datasource/agent-trust-fetch.js +115 -0
  86. package/lib/datasource/agent-trust-map.js +101 -0
  87. package/lib/datasource/agent-trust-mock.js +44 -0
  88. package/lib/datasource/agent-trust-run.js +93 -0
  89. package/lib/datasource/agent-trust-timeout.js +25 -0
  90. package/lib/datasource/audit-evidence-display.js +80 -0
  91. package/lib/datasource/audit-evidence-extract-debug.js +103 -0
  92. package/lib/datasource/audit-evidence-extract.js +177 -0
  93. package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
  94. package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
  95. package/lib/datasource/audit-evidence-poll.js +109 -0
  96. package/lib/datasource/audit-evidence-run.js +177 -0
  97. package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
  98. package/lib/datasource/audit-evidence-verify-matrix.js +177 -0
  99. package/lib/datasource/audit-evidence-verify.js +108 -0
  100. package/lib/datasource/bulk-loader-run-batches.js +88 -0
  101. package/lib/datasource/bulk-loader-service-retry.js +52 -0
  102. package/lib/datasource/bulk-loader-service.js +127 -0
  103. package/lib/datasource/capability/run-capability-dimension.js +2 -1
  104. package/lib/datasource/datasource-exporter-resolve.js +82 -0
  105. package/lib/datasource/datasource-exporter-service.js +144 -0
  106. package/lib/datasource/datasource-identifier.js +148 -0
  107. package/lib/datasource/datasource-load-export-context.js +92 -0
  108. package/lib/datasource/integration-context.js +6 -4
  109. package/lib/datasource/local-data-paths.js +145 -0
  110. package/lib/datasource/log-cleaner.js +241 -0
  111. package/lib/datasource/log-viewer.js +111 -6
  112. package/lib/datasource/record-file-parser.js +85 -0
  113. package/lib/datasource/record-mapper.js +129 -0
  114. package/lib/datasource/resolve-app.js +74 -55
  115. package/lib/datasource/test-e2e.js +26 -2
  116. package/lib/datasource/unified-validation-run-resolve.js +45 -12
  117. package/lib/datasource/unified-validation-run.js +43 -17
  118. package/lib/datasource/validate.js +37 -20
  119. package/lib/external-system/test-system-level.js +25 -4
  120. package/lib/external-system/test.js +3 -1
  121. package/lib/governance/governance-pack-loader.js +70 -0
  122. package/lib/governance/governance-run-display.js +119 -0
  123. package/lib/identity/identity-apply-core.js +108 -0
  124. package/lib/identity/identity-apply-phases.js +244 -0
  125. package/lib/identity/identity-apply-service.js +77 -0
  126. package/lib/identity/identity-csv-parser.js +114 -0
  127. package/lib/internal/fs-real-sync.js +29 -1
  128. package/lib/protection/auth-context.js +45 -0
  129. package/lib/protection/convert-batch.js +90 -0
  130. package/lib/protection/load.js +41 -0
  131. package/lib/protection/paths.js +158 -0
  132. package/lib/protection/preflight-datasource-ready.js +46 -0
  133. package/lib/protection/protection-create-probe-lines.js +142 -0
  134. package/lib/protection/protection-create-probes.js +95 -0
  135. package/lib/protection/protection-create-scaffold.js +281 -0
  136. package/lib/protection/protection-display-helpers.js +445 -0
  137. package/lib/protection/protection-display.js +215 -0
  138. package/lib/protection/protection-preset-registry.js +97 -0
  139. package/lib/protection/report-exit.js +62 -0
  140. package/lib/protection/resolve.js +202 -0
  141. package/lib/protection/run-commands-validate.js +130 -0
  142. package/lib/protection/run-commands.js +216 -0
  143. package/lib/protection/run-protection-create-helpers.js +134 -0
  144. package/lib/protection/run-protection-create-probes-flow.js +43 -0
  145. package/lib/protection/run-protection-create.js +191 -0
  146. package/lib/protection/scope.js +22 -0
  147. package/lib/protection/sync-after-upload.js +64 -0
  148. package/lib/protection/upload-batch.js +127 -0
  149. package/lib/protection/validate-batch.js +113 -0
  150. package/lib/protection/validate-local.js +65 -0
  151. package/lib/resolvers/dimension-file.js +29 -4
  152. package/lib/schema/external-datasource.schema.json +47 -13
  153. package/lib/schema/flag-map-validation-run.json +2 -2
  154. package/lib/schema/protection.schema.json +374 -0
  155. package/lib/schema/type/credential-field-registry.json +636 -0
  156. package/lib/schema/wizard-config.schema.json +1 -1
  157. package/lib/utils/agent-trust-run-display.js +231 -0
  158. package/lib/utils/agent-trust-run-exit.js +59 -0
  159. package/lib/utils/agent-trust-wait-ui.js +94 -0
  160. package/lib/utils/cli-exit-codes.js +30 -0
  161. package/lib/utils/cli-sync-options.js +33 -0
  162. package/lib/utils/credential-push-ui.js +90 -0
  163. package/lib/utils/dataplane-cli-version-gate.js +202 -0
  164. package/lib/utils/dataplane-cli-version-help.js +74 -0
  165. package/lib/utils/datasource-export-display.js +137 -0
  166. package/lib/utils/datasource-load-display.js +174 -0
  167. package/lib/utils/datasource-load-export-ui.js +120 -0
  168. package/lib/utils/datasource-test-run-capability-scope.js +3 -3
  169. package/lib/utils/datasource-test-run-display.js +3 -3
  170. package/lib/utils/datasource-test-run-tty-log.js +5 -1
  171. package/lib/utils/help-builder.js +30 -24
  172. package/lib/utils/manifest-source-emit.js +22 -0
  173. package/lib/utils/semver-compare.js +127 -0
  174. package/lib/utils/upload-pipeline-ui.js +91 -0
  175. package/lib/utils/validation-poll-ui.js +14 -7
  176. package/lib/validation/wizard-config-validator.js +58 -19
  177. package/package.json +1 -1
  178. package/templates/applications/dataplane/env.template +13 -1
  179. package/templates/applications/dataplane/rbac.yaml +4 -0
  180. package/templates/external-system/README.md.hbs +56 -8
@@ -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;
@@ -18,6 +18,9 @@ const { resolveApplicationConfigPath } = require('../utils/app-config-resolver')
18
18
  const { loadConfigFile } = require('../utils/config-format');
19
19
  const { discoverIntegrationFiles, buildEffectiveDatasourceFiles } = require('./repair-internal');
20
20
  const { runDatasourceTestE2E } = require('../datasource/test-e2e');
21
+ const { cliOptsSkipSync } = require('../utils/cli-sync-options');
22
+ const { getConfig } = require('../core/config');
23
+ const { setupIntegrationTestAuth } = require('../external-system/test-auth');
21
24
 
22
25
  /**
23
26
  * Derives datasource key from filename when file has no key (same logic as repair).
@@ -89,17 +92,18 @@ function getDatasourceKeys(appPath, configPath, variables, systemKey, systemPars
89
92
  * unless ``options.noSync`` is true.
90
93
  * @param {string} systemKey
91
94
  * @param {Object} options
92
- * @returns {Promise<void>}
95
+ * @returns {Promise<{ authConfig: Object, dataplaneUrl: string, environment?: string }|null>}
93
96
  */
94
97
  async function syncLocalIfRequested(systemKey, options) {
95
- if (options.noSync === true) return;
98
+ if (cliOptsSkipSync(options)) return null;
96
99
  logger.log(chalk.cyan('Syncing local config to dataplane…'));
97
100
  const { uploadExternalSystem } = require('./upload');
98
- await uploadExternalSystem(systemKey, {
101
+ const uploadCtx = await uploadExternalSystem(systemKey, {
99
102
  verbose: !!options.verbose,
100
103
  minimal: true
101
104
  });
102
105
  logger.log(formatSuccessLine('Sync complete'));
106
+ return uploadCtx && uploadCtx.dataplaneUrl && uploadCtx.authConfig ? uploadCtx : null;
103
107
  }
104
108
 
105
109
  /* eslint-disable max-lines-per-function, max-statements -- Load context, then loop over keys */
@@ -116,7 +120,12 @@ async function syncLocalIfRequested(systemKey, options) {
116
120
  * @param {boolean} [options.noSync] - When true, skip upload (E2E uses dataplane config already deployed)
117
121
  * @returns {Promise<{ success: boolean, results: Array<{ key: string, success: boolean, error?: string }> }>}
118
122
  */
119
- async function runTestE2EForExternalSystem(externalSystem, options = {}) {
123
+ /**
124
+ * Resolve integration folder, system key, and datasource keys for system-level tests.
125
+ * @param {string} externalSystem
126
+ * @returns {{ systemKey: string, keys: string[] }}
127
+ */
128
+ function resolveExternalIntegrationContext(externalSystem) {
120
129
  if (!externalSystem || typeof externalSystem !== 'string') {
121
130
  throw new Error('External system name is required');
122
131
  }
@@ -140,9 +149,9 @@ async function runTestE2EForExternalSystem(externalSystem, options = {}) {
140
149
  );
141
150
  const systemPath = path.join(appPath, systemFiles[0]);
142
151
  const systemParsed = loadConfigFile(systemPath);
143
- const systemKey = systemParsed.key ||
152
+ const systemKey =
153
+ systemParsed.key ||
144
154
  path.basename(systemFiles[0], path.extname(systemFiles[0])).replace(/-system$/, '');
145
-
146
155
  const keys = getDatasourceKeys(
147
156
  appPath,
148
157
  configPath,
@@ -151,12 +160,30 @@ async function runTestE2EForExternalSystem(externalSystem, options = {}) {
151
160
  systemParsed,
152
161
  datasourceFiles
153
162
  );
163
+ return { systemKey, keys };
164
+ }
165
+
166
+ async function runTestE2EForExternalSystem(externalSystem, options = {}) {
167
+ const { systemKey, keys } = resolveExternalIntegrationContext(externalSystem);
154
168
  if (keys.length === 0) {
155
169
  logger.log(chalk.yellow(`No datasources found for ${externalSystem}. Add datasource files and run aifabrix repair.`));
156
170
  return { success: true, results: [] };
157
171
  }
158
172
 
159
- await syncLocalIfRequested(systemKey, options);
173
+ const uploadCtx = await syncLocalIfRequested(systemKey, options);
174
+
175
+ let authConfig;
176
+ let dataplaneUrl;
177
+ if (uploadCtx) {
178
+ ({ authConfig, dataplaneUrl } = uploadCtx);
179
+ } else {
180
+ const configObj = await getConfig();
181
+ ({ authConfig, dataplaneUrl } = await setupIntegrationTestAuth(
182
+ externalSystem,
183
+ { environment: options.env },
184
+ configObj
185
+ ));
186
+ }
160
187
 
161
188
  const results = [];
162
189
  const opts = {
@@ -164,7 +191,9 @@ async function runTestE2EForExternalSystem(externalSystem, options = {}) {
164
191
  environment: options.env,
165
192
  debug: options.debug,
166
193
  verbose: options.verbose,
167
- async: options.async !== false
194
+ async: options.async !== false,
195
+ authConfig,
196
+ dataplaneUrl
168
197
  };
169
198
  for (const key of keys) {
170
199
  try {
@@ -187,5 +216,7 @@ async function runTestE2EForExternalSystem(externalSystem, options = {}) {
187
216
 
188
217
  module.exports = {
189
218
  runTestE2EForExternalSystem,
190
- getDatasourceKeys
219
+ getDatasourceKeys,
220
+ resolveExternalIntegrationContext,
221
+ syncLocalIfRequested
191
222
  };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @fileoverview Normalize test-governance Commander flags
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { cliOptsSkipSync } = require('../utils/cli-sync-options');
10
+
11
+ /**
12
+ * @param {string[]} [raw]
13
+ * @param {string[]} [fallback]
14
+ * @returns {string[]|undefined}
15
+ */
16
+ function collectScenarioIds(raw, fallback) {
17
+ const list = Array.isArray(raw) && raw.length > 0 ? raw : fallback;
18
+ if (!list || list.length === 0) {
19
+ return undefined;
20
+ }
21
+ return list.map(id => String(id).trim()).filter(Boolean);
22
+ }
23
+
24
+ /**
25
+ * @param {Object} options
26
+ * @param {import('commander').Command} [cmd]
27
+ * @returns {Object}
28
+ */
29
+ function normalizeTestGovernanceCliOptions(options, cmd) {
30
+ const raw = cmd && Array.isArray(cmd.rawArgs) ? cmd.rawArgs : [];
31
+ const has = flag => raw.includes(flag);
32
+ return {
33
+ ...options,
34
+ env: options.env || 'dev',
35
+ verbose: options.verbose === true || has('-v') || has('--verbose'),
36
+ json: options.json === true || has('--json'),
37
+ noSync: cliOptsSkipSync(options, cmd),
38
+ pack: options.pack,
39
+ app: options.app,
40
+ scenarioIds: collectScenarioIds(options.scenario)
41
+ };
42
+ }
43
+
44
+ module.exports = {
45
+ normalizeTestGovernanceCliOptions,
46
+ collectScenarioIds
47
+ };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @fileoverview CLI action for `aifabrix test-governance` (plan 406)
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { handleCommandError } = require('../utils/cli-utils');
10
+ const { runTestGovernanceForExternalSystem } = require('./test-governance-external');
11
+ const { normalizeTestGovernanceCliOptions } = require('./test-governance-cli-options');
12
+ const {
13
+ displayGovernanceRunTTY,
14
+ printGovernanceRunJson,
15
+ exitCodeFromGovernanceSummary
16
+ } = require('../governance/governance-run-display');
17
+
18
+ /**
19
+ * @async
20
+ * @param {string} appName
21
+ * @param {Object} options
22
+ * @param {import('commander').Command} [cmd]
23
+ */
24
+ async function runTestGovernanceCommandAction(appName, options, cmd) {
25
+ const opts = normalizeTestGovernanceCliOptions(options, cmd);
26
+ const { result, packPath, systemKey } = await runTestGovernanceForExternalSystem(appName, opts);
27
+
28
+ if (opts.json) {
29
+ printGovernanceRunJson(result);
30
+ } else {
31
+ displayGovernanceRunTTY(systemKey, result, {
32
+ verbose: opts.verbose,
33
+ packPath
34
+ });
35
+ }
36
+
37
+ const exitCode = exitCodeFromGovernanceSummary(result.summary);
38
+ if (exitCode !== 0) {
39
+ process.exit(exitCode);
40
+ }
41
+ }
42
+
43
+ async function testGovernanceCommandHandler(appName, options, cmd) {
44
+ try {
45
+ await runTestGovernanceCommandAction(appName, options, cmd);
46
+ } catch (error) {
47
+ handleCommandError(error, 'test-governance');
48
+ process.exit(3);
49
+ }
50
+ }
51
+
52
+ module.exports = {
53
+ runTestGovernanceCommandAction,
54
+ testGovernanceCommandHandler
55
+ };
@@ -0,0 +1,88 @@
1
+ /**
2
+ * @fileoverview test-governance — run governance scenario pack via dataplane API
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const chalk = require('chalk');
10
+ const logger = require('../utils/logger');
11
+ const { cliOptsSkipSync } = require('../utils/cli-sync-options');
12
+ const { getConfig } = require('../core/config');
13
+ const { setupIntegrationTestAuth } = require('../external-system/test-auth');
14
+ const { runGovernanceScenarios } = require('../api/governance-scenarios.api');
15
+ const {
16
+ resolveGovernancePackPath,
17
+ loadGovernancePackYaml
18
+ } = require('../governance/governance-pack-loader');
19
+
20
+ /**
21
+ * @param {string} systemKey
22
+ * @param {Object} options
23
+ * @returns {Promise<{ authConfig: Object, dataplaneUrl: string }|null>}
24
+ */
25
+ async function syncLocalIfRequested(systemKey, options) {
26
+ if (cliOptsSkipSync(options)) {
27
+ return null;
28
+ }
29
+ const { uploadExternalSystem } = require('./upload');
30
+ logger.log(chalk.cyan('Syncing local config to dataplane…'));
31
+ const uploadCtx = await uploadExternalSystem(systemKey, {
32
+ minimal: true,
33
+ verbose: !!options.verbose
34
+ });
35
+ logger.log(chalk.green('✔ Sync complete'));
36
+ return uploadCtx && uploadCtx.dataplaneUrl && uploadCtx.authConfig ? uploadCtx : null;
37
+ }
38
+
39
+ /**
40
+ * @async
41
+ * @param {string} externalSystem
42
+ * @param {Object} options
43
+ * @returns {Promise<{ result: Object, packPath: string, systemKey: string }>}
44
+ */
45
+ async function runTestGovernanceForExternalSystem(externalSystem, options = {}) {
46
+ const pathsUtil = require('../utils/paths');
47
+ const appType = await pathsUtil.detectAppType(externalSystem).catch(() => null);
48
+ if (!appType || appType.baseDir !== 'integration') {
49
+ throw new Error(
50
+ 'test-governance applies to external integration folders only (integration/<systemKey>/).'
51
+ );
52
+ }
53
+
54
+ const systemKey = externalSystem;
55
+ const packPath = resolveGovernancePackPath(systemKey, {
56
+ pack: options.pack,
57
+ app: options.app
58
+ });
59
+ const pack = loadGovernancePackYaml(packPath);
60
+
61
+ const uploadCtx = await syncLocalIfRequested(systemKey, options);
62
+
63
+ let authConfig;
64
+ let dataplaneUrl;
65
+ if (uploadCtx) {
66
+ ({ authConfig, dataplaneUrl } = uploadCtx);
67
+ } else {
68
+ const configObj = await getConfig();
69
+ ({ authConfig, dataplaneUrl } = await setupIntegrationTestAuth(
70
+ externalSystem,
71
+ { environment: options.env },
72
+ configObj
73
+ ));
74
+ }
75
+
76
+ const body = {
77
+ systemKey,
78
+ pack,
79
+ scenarios: options.scenarioIds
80
+ };
81
+ const result = await runGovernanceScenarios(dataplaneUrl, authConfig, body);
82
+ return { result, packPath, systemKey };
83
+ }
84
+
85
+ module.exports = {
86
+ runTestGovernanceForExternalSystem,
87
+ syncLocalIfRequested
88
+ };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @fileoverview Normalize test-trust Commander flags (defensive for -d/-v/--revalidate).
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const { cliOptsSkipSync } = require('../utils/cli-sync-options');
8
+
9
+ /**
10
+ * @param {Object} options - Commander-parsed options
11
+ * @param {import('commander').Command} [cmd]
12
+ * @returns {Object}
13
+ */
14
+ function normalizeTestTrustCliOptions(options, cmd) {
15
+ const raw = cmd && Array.isArray(cmd.rawArgs) ? cmd.rawArgs : [];
16
+ const has = flag => raw.includes(flag);
17
+ return {
18
+ ...options,
19
+ debug: options.debug === true || has('-d') || has('--debug'),
20
+ verbose: options.verbose === true || has('-v') || has('--verbose'),
21
+ revalidate: options.revalidate === true || has('--revalidate'),
22
+ noSync: cliOptsSkipSync(options, cmd),
23
+ strict: options.strict === true || has('--strict'),
24
+ summary: options.summary === true || has('--summary'),
25
+ json: options.json === true || has('--json'),
26
+ warningsAsErrors: options.warningsAsErrors === true || has('--warnings-as-errors')
27
+ };
28
+ }
29
+
30
+ module.exports = {
31
+ normalizeTestTrustCliOptions
32
+ };