@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,95 @@
1
+ /**
2
+ * @fileoverview Users API (Controller /api/v1/users)
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { ApiClient } = require('./index');
10
+
11
+ const BASE = '/api/v1/users';
12
+
13
+ /**
14
+ * List users (paginated)
15
+ * @requiresPermission {Controller} users:read
16
+ * @async
17
+ * @param {string} controllerUrl
18
+ * @param {Object} authConfig
19
+ * @param {Object} [options]
20
+ * @returns {Promise<Object>}
21
+ */
22
+ async function listUsers(controllerUrl, authConfig, options = {}) {
23
+ const client = new ApiClient(controllerUrl, authConfig);
24
+ return await client.get(BASE, { params: options });
25
+ }
26
+
27
+ /**
28
+ * Get user by id
29
+ * @requiresPermission {Controller} users:read
30
+ * @async
31
+ * @param {string} controllerUrl
32
+ * @param {Object} authConfig
33
+ * @param {string} userId
34
+ * @param {Object} [options]
35
+ * @returns {Promise<Object>}
36
+ */
37
+ async function getUser(controllerUrl, authConfig, userId, options = {}) {
38
+ const client = new ApiClient(controllerUrl, authConfig);
39
+ const params = {};
40
+ if (options.includeGroups) {
41
+ params.includeGroups = 'true';
42
+ }
43
+ return await client.get(`${BASE}/${encodeURIComponent(userId)}`, { params });
44
+ }
45
+
46
+ /**
47
+ * Create user
48
+ * @requiresPermission {Controller} users:create
49
+ * @async
50
+ * @param {string} controllerUrl
51
+ * @param {Object} authConfig
52
+ * @param {Object} body
53
+ * @returns {Promise<Object>}
54
+ */
55
+ async function createUser(controllerUrl, authConfig, body) {
56
+ const client = new ApiClient(controllerUrl, authConfig);
57
+ return await client.post(BASE, { body });
58
+ }
59
+
60
+ /**
61
+ * Patch user
62
+ * @requiresPermission {Controller} users:update
63
+ * @async
64
+ * @param {string} controllerUrl
65
+ * @param {Object} authConfig
66
+ * @param {string} userId
67
+ * @param {Object} body
68
+ * @returns {Promise<Object>}
69
+ */
70
+ async function updateUser(controllerUrl, authConfig, userId, body) {
71
+ const client = new ApiClient(controllerUrl, authConfig);
72
+ return await client.patch(`${BASE}/${encodeURIComponent(userId)}`, { body });
73
+ }
74
+
75
+ /**
76
+ * List groups for a user
77
+ * @requiresPermission {Controller} users:read
78
+ * @async
79
+ * @param {string} controllerUrl
80
+ * @param {Object} authConfig
81
+ * @param {string} userId
82
+ * @returns {Promise<Object>}
83
+ */
84
+ async function listUserGroups(controllerUrl, authConfig, userId) {
85
+ const client = new ApiClient(controllerUrl, authConfig);
86
+ return await client.get(`${BASE}/${encodeURIComponent(userId)}/groups`);
87
+ }
88
+
89
+ module.exports = {
90
+ listUsers,
91
+ getUser,
92
+ createUser,
93
+ updateUser,
94
+ listUserGroups
95
+ };
@@ -4,7 +4,7 @@
4
4
  * @version 2.0.0
5
5
  */
6
6
 
7
- const { ApiClient } = require('./index');
7
+ const { createDataplaneApiClient } = require('./index');
8
8
 
9
9
  const POST_PATH = '/api/v1/validation/run';
10
10
 
@@ -52,7 +52,7 @@ function normalizeDataplaneAuth(authConfig) {
52
52
  async function postValidationRun(dataplaneUrl, authConfig, body, transportOpts = {}) {
53
53
  const hdrs = buildClientCredentialHeaders(authConfig);
54
54
  const clientAuth = hdrs ? {} : normalizeDataplaneAuth(authConfig);
55
- const client = new ApiClient(dataplaneUrl, clientAuth);
55
+ const client = createDataplaneApiClient(dataplaneUrl, clientAuth);
56
56
  const postOpts = { body, headers: hdrs || undefined };
57
57
  if (Number.isFinite(transportOpts.timeoutMs) && transportOpts.timeoutMs > 0) {
58
58
  postOpts.timeoutMs = transportOpts.timeoutMs;
@@ -77,7 +77,7 @@ async function getValidationRun(dataplaneUrl, authConfig, testRunId, transportOp
77
77
  }
78
78
  const hdrs = buildClientCredentialHeaders(authConfig);
79
79
  const clientAuth = hdrs ? {} : normalizeDataplaneAuth(authConfig);
80
- const client = new ApiClient(dataplaneUrl, clientAuth);
80
+ const client = createDataplaneApiClient(dataplaneUrl, clientAuth);
81
81
  const path = `${POST_PATH}/${encodeURIComponent(testRunId)}`;
82
82
  const getOpts = { headers: hdrs || undefined };
83
83
  if (Number.isFinite(transportOpts.timeoutMs) && transportOpts.timeoutMs > 0) {
@@ -10,7 +10,6 @@ const { extractTestRunId } = require('./validation-run.api');
10
10
  const { postValidationRunWithTransportRetry } = require('../utils/validation-run-post-retry');
11
11
  const { pollValidationRunUntilComplete } = require('../utils/validation-run-poll');
12
12
  const { createValidationPollHandlers } = require('../utils/validation-poll-ui');
13
- const logger = require('../utils/logger');
14
13
 
15
14
  /**
16
15
  * POST /api/v1/validation/run and (when async) poll GET until reportCompleteness is full.
@@ -60,8 +59,9 @@ async function postValidationRunAndOptionalPoll(opts) {
60
59
  const remaining = Math.max(0, timeoutMs - elapsed);
61
60
  const deadlineMs = Date.now() + remaining;
62
61
 
63
- logger.log('');
64
- const pollUi = createValidationPollHandlers(deadlineMs);
62
+ const pollUi = createValidationPollHandlers(deadlineMs, {
63
+ subject: opts.pollSubject
64
+ });
65
65
  const mergeOnPollProgress =
66
66
  typeof opts.onPollProgress === 'function'
67
67
  ? (envelope, attemptIndex, meta) => {
@@ -4,7 +4,7 @@
4
4
  * @version 2.0.0
5
5
  */
6
6
 
7
- const { ApiClient } = require('./index');
7
+ const { createDataplaneApiClient } = require('./index');
8
8
 
9
9
  /**
10
10
  * Get platform details including available datasources
@@ -19,7 +19,7 @@ const { ApiClient } = require('./index');
19
19
  * @throws {Error} If request fails or platform not found (404)
20
20
  */
21
21
  async function getPlatformDetails(dataplaneUrl, authConfig, platformKey) {
22
- const client = new ApiClient(dataplaneUrl, authConfig);
22
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
23
23
  const response = await client.get(`/api/v1/wizard/platforms/${encodeURIComponent(platformKey)}`);
24
24
  if (!response.success) {
25
25
  const msg = response.status === 404
@@ -45,7 +45,7 @@ async function getPlatformDetails(dataplaneUrl, authConfig, platformKey) {
45
45
  * @throws {Error} If request fails
46
46
  */
47
47
  async function discoverEntities(dataplaneUrl, authConfig, openapiSpec) {
48
- const client = new ApiClient(dataplaneUrl, authConfig);
48
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
49
49
  const response = await client.post('/api/v1/wizard/discover-entities', {
50
50
  body: { openapiSpec }
51
51
  });
@@ -4,7 +4,7 @@
4
4
  * @version 2.0.0
5
5
  */
6
6
 
7
- const { ApiClient } = require('./index');
7
+ const { createDataplaneApiClient } = require('./index');
8
8
  const { uploadFile } = require('../utils/file-upload');
9
9
  const { getPlatformDetails, discoverEntities } = require('./wizard-platform.api');
10
10
 
@@ -22,7 +22,7 @@ const { getPlatformDetails, discoverEntities } = require('./wizard-platform.api'
22
22
  * @throws {Error} If request fails
23
23
  */
24
24
  async function createWizardSession(dataplaneUrl, authConfig, mode, systemIdOrKey = null) {
25
- const client = new ApiClient(dataplaneUrl, authConfig);
25
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
26
26
  const body = { mode };
27
27
  if (systemIdOrKey) {
28
28
  body.systemIdOrKey = systemIdOrKey;
@@ -45,7 +45,7 @@ async function createWizardSession(dataplaneUrl, authConfig, mode, systemIdOrKey
45
45
  * @throws {Error} If request fails
46
46
  */
47
47
  async function getWizardSession(dataplaneUrl, sessionId, authConfig) {
48
- const client = new ApiClient(dataplaneUrl, authConfig);
48
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
49
49
  return await client.get(`/api/v1/wizard/sessions/${sessionId}`);
50
50
  }
51
51
 
@@ -75,7 +75,7 @@ async function getWizardSession(dataplaneUrl, sessionId, authConfig) {
75
75
  * @throws {Error} If request fails
76
76
  */
77
77
  async function updateWizardSession(dataplaneUrl, sessionId, authConfig, updateData) {
78
- const client = new ApiClient(dataplaneUrl, authConfig);
78
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
79
79
  return await client.put(`/api/v1/wizard/sessions/${sessionId}`, {
80
80
  body: updateData
81
81
  });
@@ -94,7 +94,7 @@ async function updateWizardSession(dataplaneUrl, sessionId, authConfig, updateDa
94
94
  * @throws {Error} If request fails
95
95
  */
96
96
  async function deleteWizardSession(dataplaneUrl, sessionId, authConfig) {
97
- const client = new ApiClient(dataplaneUrl, authConfig);
97
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
98
98
  return await client.delete(`/api/v1/wizard/sessions/${sessionId}`);
99
99
  }
100
100
 
@@ -111,7 +111,7 @@ async function deleteWizardSession(dataplaneUrl, sessionId, authConfig) {
111
111
  * @throws {Error} If request fails
112
112
  */
113
113
  async function getWizardProgress(dataplaneUrl, sessionId, authConfig) {
114
- const client = new ApiClient(dataplaneUrl, authConfig);
114
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
115
115
  return await client.get(`/api/v1/wizard/sessions/${sessionId}/progress`);
116
116
  }
117
117
 
@@ -130,7 +130,7 @@ async function getWizardProgress(dataplaneUrl, sessionId, authConfig) {
130
130
  */
131
131
  async function parseOpenApi(dataplaneUrl, authConfig, openApiFilePathOrUrl, isUrl = false) {
132
132
  if (isUrl) {
133
- const client = new ApiClient(dataplaneUrl, authConfig);
133
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
134
134
  return await client.post(`/api/v1/wizard/parse-openapi?url=${encodeURIComponent(openApiFilePathOrUrl)}`);
135
135
  }
136
136
  const url = `${dataplaneUrl.replace(/\/$/, '')}/api/v1/wizard/parse-openapi`;
@@ -153,7 +153,7 @@ async function parseOpenApi(dataplaneUrl, authConfig, openApiFilePathOrUrl, isUr
153
153
  * @throws {Error} If request fails
154
154
  */
155
155
  async function credentialSelection(dataplaneUrl, authConfig, selectionData) {
156
- const client = new ApiClient(dataplaneUrl, authConfig);
156
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
157
157
  return await client.post('/api/v1/wizard/credential-selection', {
158
158
  body: selectionData
159
159
  });
@@ -172,7 +172,7 @@ async function credentialSelection(dataplaneUrl, authConfig, selectionData) {
172
172
  * @throws {Error} If request fails
173
173
  */
174
174
  async function detectType(dataplaneUrl, authConfig, openapiSpec) {
175
- const client = new ApiClient(dataplaneUrl, authConfig);
175
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
176
176
  return await client.post('/api/v1/wizard/detect-type', {
177
177
  body: { openapiSpec }
178
178
  });
@@ -199,7 +199,7 @@ async function detectType(dataplaneUrl, authConfig, openapiSpec) {
199
199
  * @throws {Error} If request fails
200
200
  */
201
201
  async function getPlatformConfig(dataplaneUrl, authConfig, platformKey, config) {
202
- const client = new ApiClient(dataplaneUrl, authConfig);
202
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
203
203
  return await client.post(`/api/v1/wizard/platforms/${encodeURIComponent(platformKey)}/config`, {
204
204
  body: config
205
205
  });
@@ -233,7 +233,7 @@ async function getPlatformConfig(dataplaneUrl, authConfig, platformKey, config)
233
233
  * @throws {Error} If request fails
234
234
  */
235
235
  async function generateConfig(dataplaneUrl, authConfig, config) {
236
- const client = new ApiClient(dataplaneUrl, authConfig);
236
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
237
237
  return await client.post('/api/v1/wizard/generate-config', {
238
238
  body: config
239
239
  });
@@ -252,7 +252,7 @@ async function generateConfig(dataplaneUrl, authConfig, config) {
252
252
  * @throws {Error} If request fails
253
253
  */
254
254
  async function generateConfigStream(dataplaneUrl, authConfig, config) {
255
- const client = new ApiClient(dataplaneUrl, authConfig);
255
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
256
256
  return await client.post('/api/v1/wizard/generate-config-stream', {
257
257
  body: config
258
258
  });
@@ -272,7 +272,7 @@ async function generateConfigStream(dataplaneUrl, authConfig, config) {
272
272
  * @throws {Error} If request fails
273
273
  */
274
274
  async function validateWizardConfig(dataplaneUrl, authConfig, systemConfig, datasourceConfig) {
275
- const client = new ApiClient(dataplaneUrl, authConfig);
275
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
276
276
  return await client.post('/api/v1/wizard/validate', {
277
277
  body: {
278
278
  systemConfig,
@@ -294,7 +294,7 @@ async function validateWizardConfig(dataplaneUrl, authConfig, systemConfig, data
294
294
  * @throws {Error} If request fails
295
295
  */
296
296
  async function validateAllSteps(dataplaneUrl, sessionId, authConfig) {
297
- const client = new ApiClient(dataplaneUrl, authConfig);
297
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
298
298
  return await client.get(`/api/v1/wizard/sessions/${sessionId}/validate`);
299
299
  }
300
300
 
@@ -312,7 +312,7 @@ async function validateAllSteps(dataplaneUrl, sessionId, authConfig) {
312
312
  * @throws {Error} If request fails
313
313
  */
314
314
  async function validateStep(dataplaneUrl, sessionId, authConfig, stepNumber) {
315
- const client = new ApiClient(dataplaneUrl, authConfig);
315
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
316
316
  return await client.post(`/api/v1/wizard/sessions/${sessionId}/validate-step?step=${stepNumber}`);
317
317
  }
318
318
 
@@ -329,7 +329,7 @@ async function validateStep(dataplaneUrl, sessionId, authConfig, stepNumber) {
329
329
  * @throws {Error} If request fails
330
330
  */
331
331
  async function getPreview(dataplaneUrl, sessionId, authConfig) {
332
- const client = new ApiClient(dataplaneUrl, authConfig);
332
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
333
333
  return await client.get(`/api/v1/wizard/preview/${sessionId}`);
334
334
  }
335
335
 
@@ -347,7 +347,7 @@ async function getPreview(dataplaneUrl, sessionId, authConfig) {
347
347
  * @throws {Error} If request fails
348
348
  */
349
349
  async function testMcpConnection(dataplaneUrl, authConfig, serverUrl, token) {
350
- const client = new ApiClient(dataplaneUrl, authConfig);
350
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
351
351
  return await client.post('/api/v1/wizard/test-mcp-connection', {
352
352
  body: {
353
353
  serverUrl,
@@ -369,7 +369,7 @@ async function testMcpConnection(dataplaneUrl, authConfig, serverUrl, token) {
369
369
  * @throws {Error} If request fails
370
370
  */
371
371
  async function getDeploymentDocs(dataplaneUrl, authConfig, systemKey) {
372
- const client = new ApiClient(dataplaneUrl, authConfig);
372
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
373
373
  return await client.get(`/api/v1/wizard/deployment-docs/${systemKey}`);
374
374
  }
375
375
 
@@ -390,7 +390,7 @@ async function getDeploymentDocs(dataplaneUrl, authConfig, systemKey) {
390
390
  * @throws {Error} If request fails
391
391
  */
392
392
  async function postDeploymentDocs(dataplaneUrl, authConfig, systemKey, body = null) {
393
- const client = new ApiClient(dataplaneUrl, authConfig);
393
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
394
394
  return await client.post(`/api/v1/wizard/deployment-docs/${systemKey}`, {
395
395
  body: body || {}
396
396
  });
@@ -410,7 +410,7 @@ async function postDeploymentDocs(dataplaneUrl, authConfig, systemKey, body = nu
410
410
  */
411
411
  async function getWizardPlatforms(dataplaneUrl, authConfig) {
412
412
  try {
413
- const client = new ApiClient(dataplaneUrl, authConfig);
413
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
414
414
  const response = await client.get('/api/v1/wizard/platforms');
415
415
  const platforms = response?.data?.platforms ?? response?.platforms ?? [];
416
416
  return Array.isArray(platforms) ? platforms : [];
@@ -433,7 +433,7 @@ async function getWizardPlatforms(dataplaneUrl, authConfig) {
433
433
  * @throws {Error} If request fails
434
434
  */
435
435
  async function listWizardCredentials(dataplaneUrl, authConfig, options = {}) {
436
- const client = new ApiClient(dataplaneUrl, authConfig);
436
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
437
437
  return await client.get('/api/v1/wizard/credentials', {
438
438
  params: options
439
439
  });
package/lib/app/deploy.js CHANGED
@@ -421,6 +421,35 @@ async function tryExternalDeployFallback(appName, options) {
421
421
  return { usedExternalDeploy: false, result: null };
422
422
  }
423
423
 
424
+ function assertDeployNotProtectionScope(appName) {
425
+ const { isProtectionScope } = require('../protection/scope');
426
+ if (!isProtectionScope(appName)) {
427
+ return;
428
+ }
429
+ const { formatDeployProtectionNotImplementedTTY } = require('../protection/protection-display');
430
+ logger.error(formatDeployProtectionNotImplementedTTY());
431
+ process.exit(1);
432
+ }
433
+
434
+ /**
435
+ * Run external, fallback, or standard deployment paths.
436
+ * @param {string} appName
437
+ * @param {Object} options
438
+ * @returns {Promise<{ result: Object, usedExternalDeploy: boolean, controllerUrl?: string }>}
439
+ */
440
+ async function executeDeployPipeline(appName, options) {
441
+ const externalResult = await handleExternalDeployment(appName, options);
442
+ if (externalResult) {
443
+ return { result: externalResult, usedExternalDeploy: true };
444
+ }
445
+ const fallback = await tryExternalDeployFallback(appName, options);
446
+ if (fallback.result) {
447
+ return { result: fallback.result, usedExternalDeploy: fallback.usedExternalDeploy };
448
+ }
449
+ const { result, controllerUrl } = await executeStandardDeployment(appName, options);
450
+ return { result, usedExternalDeploy: false, controllerUrl };
451
+ }
452
+
424
453
  /**
425
454
  * Deploys application to Miso Controller
426
455
  * Orchestrates manifest generation, key creation, and deployment
@@ -446,26 +475,12 @@ async function deployApp(appName, options = {}) {
446
475
  let usedExternalDeploy = false;
447
476
 
448
477
  try {
449
- if (!appName || typeof appName !== 'string' || appName.trim().length === 0) {
450
- throw new Error('App name is required');
451
- }
478
+ assertDeployNotProtectionScope(appName);
452
479
  validateAppName(appName);
453
-
454
- const externalResult = await handleExternalDeployment(appName, options);
455
- if (externalResult) {
456
- usedExternalDeploy = true;
457
- return { result: externalResult, usedExternalDeploy: true };
458
- }
459
-
460
- const fallback = await tryExternalDeployFallback(appName, options);
461
- if (fallback.result) {
462
- usedExternalDeploy = fallback.usedExternalDeploy;
463
- return { result: fallback.result, usedExternalDeploy };
464
- }
465
-
466
- const { result, controllerUrl: url } = await executeStandardDeployment(appName, options);
467
- controllerUrl = url;
468
- return { result, usedExternalDeploy: false };
480
+ const pipeline = await executeDeployPipeline(appName, options);
481
+ controllerUrl = pipeline.controllerUrl ?? null;
482
+ usedExternalDeploy = pipeline.usedExternalDeploy;
483
+ return { result: pipeline.result, usedExternalDeploy };
469
484
  } catch (error) {
470
485
  await handleDeploymentError(error, appName, controllerUrl, usedExternalDeploy);
471
486
  }
package/lib/cli/index.js CHANGED
@@ -22,7 +22,8 @@ const { setupExternalSystemCommands } = require('./setup-external-system');
22
22
  const { setupAppCommands: setupAppManagementCommands } = require('../commands/app');
23
23
  const { setupDatasourceCommands } = require('../commands/datasource');
24
24
  const { setupDimensionCommands } = require('../commands/dimension');
25
- const { setupDimensionValueCommands } = require('../commands/dimension-value');
25
+ const { setupIdentityCommands } = require('../commands/identity');
26
+ const { setupProtectionCommands } = require('../commands/protection');
26
27
  const { setupCredentialDeploymentCommands } = require('./setup-credential-deployment');
27
28
  const { setupIntegrationClientCommands } = require('./setup-integration-client');
28
29
  const { setupPlatformCommands } = require('./setup-platform');
@@ -39,7 +40,8 @@ function setupCommands(program) {
39
40
  setupAppManagementCommands(program);
40
41
  setupDatasourceCommands(program);
41
42
  setupDimensionCommands(program);
42
- setupDimensionValueCommands(program);
43
+ setupIdentityCommands(program);
44
+ setupProtectionCommands(program);
43
45
  setupUtilityCommands(program);
44
46
  setupCredentialDeploymentCommands(program);
45
47
  setupIntegrationClientCommands(program);
@@ -320,6 +320,7 @@ async function runGuidedUpDataplane(options, handleUpDataplane) {
320
320
  handleUpDataplane({ ...options, platformInstall: true, skipInfraCheck: true })
321
321
  );
322
322
  stopSpinnerSuccess(dpSpin, 'Dataplane ready');
323
+ await refreshDataplaneVersionAfterPlatformInstall();
323
324
  await validateAppErrorLogs(['dataplane']);
324
325
  await logDataplaneReadyFooter();
325
326
  }
@@ -361,15 +362,32 @@ async function runGuidedAuthStep(handleLogin) {
361
362
  const spin = startSpinner('Authenticating...');
362
363
  try {
363
364
  const controllerUrl = await resolveControllerUrl();
364
- await handleLogin({ method: 'device', controller: controllerUrl, environment: 'dev', compact: true });
365
+ await handleLogin({
366
+ method: 'device',
367
+ controller: controllerUrl,
368
+ environment: 'dev',
369
+ compact: true,
370
+ // Dataplane is not up yet during guided platform/dataplane install; refresh after it starts.
371
+ skipDataplaneVersionRefresh: true
372
+ });
365
373
  } catch (authErr) {
366
374
  if (spin) spin.fail('Authentication failed');
367
375
  logger.error(formatBlockingError('Authentication failed'));
368
376
  logger.log(chalk.gray('\nRun:\n af login\n\nThen retry:\n af up-platform\n'));
369
377
  throw authErr;
370
378
  }
371
- // Compact device-login already prints a success line; don't duplicate it.
372
- if (spin) spin.stop();
379
+ // Compact device-login prints "Authentication successful"; close the step spinner cleanly.
380
+ stopSpinnerSuccess(spin, 'Authenticated');
381
+ }
382
+
383
+ async function refreshDataplaneVersionAfterPlatformInstall() {
384
+ try {
385
+ const { tryRefreshDataplaneVersionAfterLogin } = require('../commands/auth-status-dataplane-version');
386
+ const controllerUrl = await resolveControllerUrl();
387
+ await tryRefreshDataplaneVersionAfterLogin(controllerUrl, {});
388
+ } catch {
389
+ // best-effort only
390
+ }
373
391
  }
374
392
 
375
393
  async function runGuidedKeycloakAndMisoControllerPhase(options, handleUpMiso) {
@@ -393,6 +411,7 @@ async function runGuidedDataplaneInstallPhase(options, handleUpDataplane) {
393
411
  handleUpDataplane({ ...options, platformInstall: true, skipInfraCheck: true })
394
412
  );
395
413
  stopSpinnerSuccess(dpSpin, 'Dataplane ready');
414
+ await refreshDataplaneVersionAfterPlatformInstall();
396
415
  }
397
416
 
398
417
  async function runGuidedUpPlatform(options, handleUpMiso, handleUpDataplane, handleLogin, forceCleanSummary = null) {
@@ -56,12 +56,60 @@ Examples:
56
56
  Notes:
57
57
  - To run E2E for one datasource, use:
58
58
  aifabrix datasource test-e2e <datasourceKey>
59
- - External integration: local system and datasource files are published to the dataplane before E2E (same as upload). Use --no-sync to exercise only config already deployed. The old --sync flag is a no-op (kept for scripts).
59
+ - External integration: local system and datasource files are published to the dataplane before E2E (same as upload). Use --no-sync to exercise only config already deployed.
60
+ `;
61
+
62
+ const IDENTITY_HELP_AFTER = `
63
+ Examples:
64
+ # Bulk seed from demo users.csv (test-protection prefix) + sync + cache purge
65
+ $ aifabrix identity apply --file ./users.csv --filter-prefix test-protection -e dev --sync --purge-cache
66
+ $ aifabrix identity sync -e dev --purge-cache
67
+ $ aifabrix identity cache clear
68
+
69
+ # Single user / group
70
+ $ aifabrix identity user create --email alice@example.com --first-name Alice --last-name HR
71
+ $ aifabrix identity group create --name test-protection-sales-fi --description "Sales Finland"
72
+
73
+ Notes:
74
+ - Requires aifabrix login (controller token). See docs/commands/identity-management.md.
75
+ - Prefer identity apply over SQL in tests/fixtures/protection/optional-dev-identity-dataplane.sql.
76
+ `;
77
+
78
+ const TEST_GOVERNANCE_HELP_AFTER = `
79
+ Examples:
80
+ # External system — ABAC visibility acceptance (governance scenario pack)
81
+ $ aifabrix test-governance test-protection
82
+ $ aifabrix test-governance test-protection --pack scenarios/test-protection-v1.yaml
83
+ $ aifabrix test-governance test-protection -v --no-sync
84
+ $ aifabrix test-governance test-protection --scenario country-isolation-maria-fi --json
85
+
86
+ Notes:
87
+ - Separate from test-e2e (vendor connectivity). Does not call external vendor APIs.
88
+ - Load baseline fixtures first (datasource load, protection upload) — not auto-run by this command.
89
+ - Default pack: integration/<systemKey>/scenarios/default.yaml or <systemKey>-v1.yaml
90
+ - Requires governance:evaluate on the operator token (see permissions.md).
91
+ `;
92
+
93
+ const TEST_TRUST_HELP_AFTER = `
94
+ Examples:
95
+ # External system — semantic trust for every datasource (dataplane 404.5)
96
+ $ aifabrix test-trust hubspot
97
+ $ aifabrix test-trust hubspot -v --revalidate
98
+ $ aifabrix test-trust hubspot --no-sync
99
+ $ aifabrix test-trust hubspot --summary --timeout 120000
100
+
101
+ Notes:
102
+ - For one datasource: aifabrix datasource test-trust <datasourceKey>
103
+ - Does not call external APIs (use test-e2e for live proof).
104
+ - Local integration files are published before run unless --no-sync.
60
105
  `;
61
106
 
62
107
  module.exports = {
63
108
  TEST_HELP_AFTER,
64
109
  TEST_INTEGRATION_HELP_AFTER,
65
- TEST_E2E_HELP_AFTER
110
+ TEST_E2E_HELP_AFTER,
111
+ TEST_TRUST_HELP_AFTER,
112
+ TEST_GOVERNANCE_HELP_AFTER,
113
+ IDENTITY_HELP_AFTER
66
114
  };
67
115