@aifabrix/builder 2.45.5 → 2.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/.cursor/rules/cli-layout.mdc +2 -0
  2. package/.cursor/rules/cli-product-neutral.mdc +43 -0
  3. package/.cursor/rules/project-rules.mdc +1 -0
  4. package/README.md +43 -17
  5. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
  6. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
  7. package/jest.config.default.js +2 -1
  8. package/jest.config.isolated.js +2 -1
  9. package/jest.global-live-fabrix-hooks.js +36 -0
  10. package/jest.isolated-projects.js +130 -0
  11. package/jest.projects.js +41 -136
  12. package/lib/api/api-response-errors.js +124 -0
  13. package/lib/api/governance-scenario-pack.api.js +159 -0
  14. package/lib/api/index.js +43 -3
  15. package/lib/api/lifecycle.api.js +77 -0
  16. package/lib/api/pipeline.api.js +3 -1
  17. package/lib/api/records-bulk.api.js +14 -1
  18. package/lib/api/validation-runner.js +3 -1
  19. package/lib/app/deploy-config.js +4 -1
  20. package/lib/app/down.js +20 -1
  21. package/lib/app/push.js +4 -5
  22. package/lib/app/register.js +7 -4
  23. package/lib/app/run-env-compose.js +84 -30
  24. package/lib/build/index.js +5 -0
  25. package/lib/certification/sync-system-certification.js +13 -6
  26. package/lib/cli/index.js +4 -0
  27. package/lib/cli/infra-guided-auth.js +109 -0
  28. package/lib/cli/infra-guided.js +84 -81
  29. package/lib/cli/setup-app.help.js +0 -31
  30. package/lib/cli/setup-app.js +28 -2
  31. package/lib/cli/setup-app.test-commands.js +5 -57
  32. package/lib/cli/setup-auth.js +25 -0
  33. package/lib/cli/setup-certification-commands.js +147 -0
  34. package/lib/cli/setup-certification.help.js +141 -0
  35. package/lib/cli/setup-environment.js +19 -2
  36. package/lib/cli/setup-external-system-help.js +62 -0
  37. package/lib/cli/setup-external-system.js +25 -3
  38. package/lib/cli/setup-infra.js +9 -1
  39. package/lib/cli/setup-platform.js +6 -1
  40. package/lib/cli/setup-secrets.js +33 -1
  41. package/lib/cli/setup-utility-help-after.js +22 -2
  42. package/lib/cli/setup-utility-repair.js +198 -0
  43. package/lib/cli/setup-utility-resolve.js +40 -4
  44. package/lib/cli/setup-utility-validate.js +197 -0
  45. package/lib/cli/setup-utility.js +4 -239
  46. package/lib/commands/auth-status-client-tokens.js +214 -0
  47. package/lib/commands/auth-status-dataplane-version.js +58 -20
  48. package/lib/commands/auth-status.js +41 -47
  49. package/lib/commands/datasource-test-trust-cli.js +27 -23
  50. package/lib/commands/datasource-unified-test-cli.js +6 -3
  51. package/lib/commands/datasource-unified-test-cli.options.js +5 -1
  52. package/lib/commands/datasource.js +7 -7
  53. package/lib/commands/dev-show-display.js +8 -1
  54. package/lib/commands/governance-cli-shared.js +94 -0
  55. package/lib/commands/governance-command-action.js +132 -0
  56. package/lib/commands/governance-download-external.js +34 -0
  57. package/lib/commands/governance-generate-external.js +71 -0
  58. package/lib/commands/governance-upload-external.js +36 -0
  59. package/lib/commands/governance-verify-external.js +64 -0
  60. package/lib/commands/governance.js +144 -0
  61. package/lib/commands/lifecycle-command-action.js +123 -0
  62. package/lib/commands/lifecycle-external.js +268 -0
  63. package/lib/commands/login.js +11 -2
  64. package/lib/commands/repair-auth-apply.js +127 -0
  65. package/lib/commands/repair-changed-files.js +41 -0
  66. package/lib/commands/repair-datasource-keys.js +4 -1
  67. package/lib/commands/repair-datasource-run.js +59 -0
  68. package/lib/commands/repair-datasource-test-payload.js +259 -0
  69. package/lib/commands/repair-datasource.js +1 -51
  70. package/lib/commands/repair-env-template.js +64 -13
  71. package/lib/commands/repair-external-run.js +216 -0
  72. package/lib/commands/repair-openapi-sync.js +23 -0
  73. package/lib/commands/repair-persist.js +4 -0
  74. package/lib/commands/repair-rbac.js +52 -1
  75. package/lib/commands/repair-steps.js +145 -0
  76. package/lib/commands/repair.js +29 -296
  77. package/lib/commands/secrets-get.js +184 -0
  78. package/lib/commands/secrets-remove-all.js +3 -1
  79. package/lib/commands/setup-image-refresh.js +146 -0
  80. package/lib/commands/setup-modes.js +158 -162
  81. package/lib/commands/setup-platform-auth.js +76 -0
  82. package/lib/commands/setup-prompts-admin.js +244 -0
  83. package/lib/commands/setup-prompts-platform-mode.js +67 -0
  84. package/lib/commands/setup-prompts-validators.js +36 -0
  85. package/lib/commands/setup-prompts.js +24 -76
  86. package/lib/commands/setup-run-context.js +45 -0
  87. package/lib/commands/setup-spinners.js +39 -0
  88. package/lib/commands/setup.js +27 -5
  89. package/lib/commands/teardown.js +39 -0
  90. package/lib/commands/test-e2e-external.js +24 -8
  91. package/lib/commands/test-governance-external.js +3 -24
  92. package/lib/commands/test-trust-external.js +20 -21
  93. package/lib/commands/up-common.js +25 -13
  94. package/lib/commands/up-dataplane.js +50 -13
  95. package/lib/commands/upload-publish-context.js +75 -0
  96. package/lib/commands/upload-sidecar-followups.js +89 -0
  97. package/lib/commands/upload.js +90 -108
  98. package/lib/commands/verify-governance-combined-external.js +61 -0
  99. package/lib/commands/verify-governance-command-action.js +64 -0
  100. package/lib/commands/verify-governance-db.js +102 -0
  101. package/lib/commands/verify-governance-external.js +77 -0
  102. package/lib/commands/verify-governance-result-builders.js +71 -0
  103. package/lib/commands/verify-operations-command-action.js +68 -0
  104. package/lib/commands/verify-operations-external.js +268 -0
  105. package/lib/commands/verify-operations-persist.js +75 -0
  106. package/lib/commands/verify-operations-step-plan.js +68 -0
  107. package/lib/commands/verify-operations-steps.js +111 -0
  108. package/lib/commands/verify-trust-command-action.js +70 -0
  109. package/lib/commands/verify-trust-external.js +67 -0
  110. package/lib/commands/wizard-config-normalizer.js +43 -23
  111. package/lib/commands/wizard-core.js +2 -0
  112. package/lib/core/admin-password-generator.js +38 -0
  113. package/lib/core/admin-secrets-bundle.js +111 -0
  114. package/lib/core/admin-secrets-env-overlay.js +62 -0
  115. package/lib/core/admin-secrets.js +32 -4
  116. package/lib/core/config-attach-extensions.js +3 -0
  117. package/lib/core/config-platform-controller.js +44 -0
  118. package/lib/core/config-setup-installation-profile.js +42 -0
  119. package/lib/core/config-setup-platform-mode.js +46 -0
  120. package/lib/core/config.js +46 -36
  121. package/lib/core/secrets-admin-env.js +6 -2
  122. package/lib/core/secrets-ensure.js +4 -0
  123. package/lib/core/secrets-env-content.js +54 -48
  124. package/lib/core/secrets-env-write.js +10 -1
  125. package/lib/core/secrets-infra-placeholder-sync.js +2 -0
  126. package/lib/core/setup-installation-profile.js +43 -0
  127. package/lib/core/setup-platform-mode.js +55 -0
  128. package/lib/datasource/agent-trust-debug-log.js +1 -1
  129. package/lib/datasource/agent-trust-run.js +5 -3
  130. package/lib/datasource/audit-evidence-extract-debug.js +30 -18
  131. package/lib/datasource/audit-evidence-extract.js +7 -4
  132. package/lib/datasource/audit-evidence-verify-matrix.js +54 -26
  133. package/lib/datasource/bulk-loader-run-batches.js +15 -3
  134. package/lib/datasource/datasource-load-export-context.js +3 -0
  135. package/lib/datasource/log-cleaner.js +2 -2
  136. package/lib/datasource/log-viewer.js +8 -8
  137. package/lib/datasource/test-e2e.js +4 -1
  138. package/lib/datasource/unified-validation-run-body.js +3 -1
  139. package/lib/datasource/unified-validation-run.js +5 -2
  140. package/lib/deployment/environment-poll-status.js +138 -0
  141. package/lib/deployment/environment.js +42 -34
  142. package/lib/external-system/download.js +21 -20
  143. package/lib/external-system/generator.js +5 -1
  144. package/lib/external-system/integration-auth-context.js +45 -0
  145. package/lib/external-system/test-auth.js +12 -5
  146. package/lib/external-system/test.js +6 -6
  147. package/lib/generator/external-controller-manifest.js +15 -0
  148. package/lib/governance/governance-cli-errors.js +96 -0
  149. package/lib/governance/governance-download-display.js +38 -0
  150. package/lib/governance/governance-run-display.js +215 -25
  151. package/lib/infrastructure/admin-secrets-apply.js +84 -0
  152. package/lib/infrastructure/admin-secrets-ensure.js +130 -0
  153. package/lib/infrastructure/admin-secrets-resolve.js +95 -0
  154. package/lib/infrastructure/admin-secrets-sync.js +91 -0
  155. package/lib/infrastructure/helpers.js +23 -129
  156. package/lib/infrastructure/index.js +5 -6
  157. package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
  158. package/lib/lifecycle/certification-progress.js +231 -0
  159. package/lib/lifecycle/dataplane-ui-url.js +145 -0
  160. package/lib/lifecycle/exit-codes.js +24 -0
  161. package/lib/lifecycle/json-envelope.js +25 -0
  162. package/lib/lifecycle/lifecycle-governance-options.js +51 -0
  163. package/lib/lifecycle/logger-quiet.js +29 -0
  164. package/lib/lifecycle/open-api-published.js +59 -0
  165. package/lib/lifecycle/operations-readiness-metrics.js +101 -0
  166. package/lib/lifecycle/product-model.js +196 -0
  167. package/lib/lifecycle/report-display.js +471 -0
  168. package/lib/lifecycle/scenario-upload.js +191 -0
  169. package/lib/lifecycle/verify-step-progress.js +87 -0
  170. package/lib/parameters/infra-kv-discovery.js +27 -2
  171. package/lib/protection/auth-context.js +10 -4
  172. package/lib/schema/application-schema.json +1 -1
  173. package/lib/schema/datasource-test-run.schema.json +89 -6
  174. package/lib/schema/external-datasource.schema.json +2636 -2622
  175. package/lib/schema/external-system.schema.json +1089 -761
  176. package/lib/schema/infra.parameter.yaml +45 -0
  177. package/lib/schema/type/credential-field-registry.json +103 -97
  178. package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
  179. package/lib/utils/api.js +55 -31
  180. package/lib/utils/app-relative-path-env.js +112 -0
  181. package/lib/utils/cli-secrets-error-format.js +83 -21
  182. package/lib/utils/cli-utils.js +16 -4
  183. package/lib/utils/compose-generate-docker-compose.js +4 -0
  184. package/lib/utils/compose-traefik-ingress-base.js +16 -1
  185. package/lib/utils/config-secrets-path.js +46 -0
  186. package/lib/utils/controller-url.js +88 -1
  187. package/lib/utils/credential-secrets-env.js +137 -9
  188. package/lib/utils/dataplane-resolver.js +129 -2
  189. package/lib/utils/declarative-url-ports.js +3 -3
  190. package/lib/utils/deployment-auth-mode.js +41 -0
  191. package/lib/utils/developer-volume-id.js +33 -0
  192. package/lib/utils/env-copy.js +14 -9
  193. package/lib/utils/env-kv-orphan-lines.js +43 -0
  194. package/lib/utils/error-formatters/network-errors.js +13 -4
  195. package/lib/utils/external-env-template.js +1 -0
  196. package/lib/utils/external-system-readiness-core.js +10 -0
  197. package/lib/utils/external-system-readiness-display.js +80 -23
  198. package/lib/utils/health-check.js +4 -1
  199. package/lib/utils/help-builder.js +11 -8
  200. package/lib/utils/keycloak-realm-ready.js +101 -0
  201. package/lib/utils/load-cip-capacity-display-config.js +61 -15
  202. package/lib/utils/manifest-source-emit.js +2 -0
  203. package/lib/utils/pipeline-upload-auth-retry.js +82 -0
  204. package/lib/utils/platform-controller-url.js +156 -0
  205. package/lib/utils/postgres-platform-bootstrap.js +199 -0
  206. package/lib/utils/postgres-wipe.js +9 -2
  207. package/lib/utils/resolve-env-mode.js +47 -0
  208. package/lib/utils/secrets-generator.js +31 -25
  209. package/lib/utils/secrets-missing-error.js +164 -0
  210. package/lib/utils/service-url-label.js +81 -0
  211. package/lib/utils/setup-quiet-output.js +25 -0
  212. package/lib/utils/template-helpers.js +15 -0
  213. package/lib/utils/token-manager-client-credentials.js +201 -0
  214. package/lib/utils/token-manager-messages.js +8 -0
  215. package/lib/utils/token-manager-refresh.js +12 -2
  216. package/lib/utils/token-manager.js +86 -105
  217. package/lib/utils/upload-pipeline-ui.js +12 -5
  218. package/lib/utils/upload-sync-options.js +42 -0
  219. package/lib/utils/url-declarative-public-base.js +21 -3
  220. package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
  221. package/lib/utils/validation-poll-ui.js +8 -0
  222. package/lib/utils/validation-run-stage.js +45 -0
  223. package/package.json +1 -1
  224. package/templates/applications/dataplane/application.yaml +2 -1
  225. package/templates/applications/dataplane/env.template +24 -1
  226. package/templates/applications/miso-controller/env.template +16 -0
  227. package/templates/external-system/README.md.hbs +74 -17
  228. package/templates/python/docker-compose.hbs +19 -18
  229. package/templates/typescript/docker-compose.hbs +18 -17
  230. package/lib/commands/test-governance-command-action.js +0 -55
  231. package/lib/commands/test-trust-command-action.js +0 -118
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Throw CLI errors from ApiClient responses with formatted output preserved.
3
+ *
4
+ * @fileoverview Dataplane/controller API failure helpers
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { formatApiError } = require('../utils/api-error-handler');
12
+ const {
13
+ enrichUnresolvableGovernanceSubjectsError
14
+ } = require('../governance/governance-cli-errors');
15
+ const { inferServiceLabelFromUrl } = require('../utils/service-url-label');
16
+
17
+ /**
18
+ * @param {Object|undefined} res - ApiClient response
19
+ * @param {string} fallbackMessage
20
+ * @returns {string}
21
+ */
22
+ function resolveApiErrorMessage(res, fallbackMessage) {
23
+ if (!res) {
24
+ return fallbackMessage;
25
+ }
26
+ const fromError =
27
+ res.error &&
28
+ (res.error.formattedError || res.error.error || res.error.message);
29
+ return (
30
+ res.formattedError ||
31
+ fromError ||
32
+ (typeof res.error === 'string' ? res.error : null) ||
33
+ fallbackMessage
34
+ );
35
+ }
36
+
37
+ /**
38
+ * @param {Object|undefined} res
39
+ * @param {string} endpointUrl
40
+ * @returns {{ endpoint: string, serviceLabel: string }}
41
+ */
42
+ function resolveApiFailureEndpointContext(res, endpointUrl) {
43
+ const endpoint =
44
+ (res && res.errorData && res.errorData.endpointUrl) || endpointUrl || '';
45
+ const serviceLabel =
46
+ (res && res.errorData && res.errorData.serviceLabel) ||
47
+ inferServiceLabelFromUrl(endpoint);
48
+ return { endpoint, serviceLabel };
49
+ }
50
+
51
+ /**
52
+ * @param {Object|undefined} res
53
+ * @param {string} endpoint
54
+ * @param {string} serviceLabel
55
+ * @returns {Object}
56
+ */
57
+ function buildEnrichedApiFailureResponse(res, endpoint, serviceLabel) {
58
+ return {
59
+ ...res,
60
+ errorData: {
61
+ ...(res && res.errorData),
62
+ endpointUrl: endpoint,
63
+ serviceLabel
64
+ }
65
+ };
66
+ }
67
+
68
+ /**
69
+ * @param {Error} err
70
+ * @param {Object|undefined} res
71
+ * @param {string} endpoint
72
+ * @param {string} serviceLabel
73
+ * @param {Object} enriched
74
+ */
75
+ function assignApiFailureErrorFields(err, res, endpoint, serviceLabel, enriched) {
76
+ err.formatted =
77
+ res && res.formattedError ? res.formattedError : formatApiError(enriched, null);
78
+ err.network = Boolean(res && res.network === true);
79
+ err.status = res && typeof res.status === 'number' ? res.status : 0;
80
+ err.endpointUrl = endpoint;
81
+ err.serviceLabel = serviceLabel;
82
+ }
83
+
84
+ /**
85
+ * @param {Object|undefined} res
86
+ * @param {string} endpointUrl
87
+ * @returns {Error}
88
+ */
89
+ function buildApiFailureError(res, endpointUrl, context = {}) {
90
+ const message = resolveApiErrorMessage(res, 'Request failed');
91
+ const err = new Error(message);
92
+ const { endpoint, serviceLabel } = resolveApiFailureEndpointContext(res, endpointUrl);
93
+ const enriched = buildEnrichedApiFailureResponse(res, endpoint, serviceLabel);
94
+ assignApiFailureErrorFields(err, res, endpoint, serviceLabel, enriched);
95
+ if (
96
+ typeof endpoint === 'string' &&
97
+ endpoint.includes('/governance-scenarios/generate')
98
+ ) {
99
+ enrichUnresolvableGovernanceSubjectsError(err, context);
100
+ }
101
+ return err;
102
+ }
103
+
104
+ /**
105
+ * @param {Object} res - ApiClient response (`success: false`)
106
+ * @param {{ fallbackMessage?: string, endpointUrl?: string }} [context]
107
+ * @throws {Error}
108
+ */
109
+ function throwIfApiFailed(res, context = {}) {
110
+ if (!res || res.success !== false) {
111
+ return;
112
+ }
113
+ const endpointUrl =
114
+ context.endpointUrl ||
115
+ (res.errorData && res.errorData.endpointUrl) ||
116
+ '';
117
+ throw buildApiFailureError(res, endpointUrl || context.endpointUrl || '', context);
118
+ }
119
+
120
+ module.exports = {
121
+ throwIfApiFailed,
122
+ buildApiFailureError,
123
+ resolveApiErrorMessage
124
+ };
@@ -0,0 +1,159 @@
1
+ /**
2
+ * @fileoverview Dataplane governance scenario pack CRUD + run API (419.0).
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { createDataplaneApiClient } = require('./index');
10
+ const { throwIfApiFailed } = require('./api-response-errors');
11
+ const { unwrapApiData } = require('../utils/external-system-readiness-core');
12
+ const {
13
+ governanceUploadSchemaHint,
14
+ isGovernanceCoverageSchemaError
15
+ } = require('../governance/governance-cli-errors');
16
+
17
+ /**
18
+ * @param {string} dataplaneUrl
19
+ * @param {string} datasourceKey
20
+ * @param {string} suffix
21
+ * @returns {string}
22
+ */
23
+ function governanceScenarioEndpoint(dataplaneUrl, datasourceKey, suffix) {
24
+ const base = String(dataplaneUrl || '').replace(/\/$/, '');
25
+ return `${base}/api/v1/external/${encodeURIComponent(datasourceKey)}${suffix}`;
26
+ }
27
+
28
+ /**
29
+ * PUT /api/v1/external/{datasourceKey}/governance-scenarios
30
+ * @requiresPermission {Dataplane} external-data-source:update
31
+ * @async
32
+ */
33
+ async function upsertDatasourceGovernancePack(dataplaneUrl, authConfig, datasourceKey, pack) {
34
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
35
+ const res = await client.put(
36
+ `/api/v1/external/${encodeURIComponent(datasourceKey)}/governance-scenarios`,
37
+ { body: { pack } }
38
+ );
39
+ if (res && res.success === false) {
40
+ try {
41
+ throwIfApiFailed(res, {
42
+ endpointUrl: governanceScenarioEndpoint(
43
+ dataplaneUrl,
44
+ datasourceKey,
45
+ '/governance-scenarios'
46
+ )
47
+ });
48
+ } catch (err) {
49
+ if (isGovernanceCoverageSchemaError(err)) {
50
+ err.friendlyHint = governanceUploadSchemaHint(
51
+ (pack && pack.spec && pack.spec.systemKey) || datasourceKey
52
+ );
53
+ }
54
+ throw err;
55
+ }
56
+ }
57
+ return unwrapApiData(res);
58
+ }
59
+
60
+ /**
61
+ * GET /api/v1/external/{datasourceKey}/governance-scenarios
62
+ * @requiresPermission {Dataplane} external-data-source:read
63
+ * @async
64
+ */
65
+ async function getDatasourceGovernancePack(dataplaneUrl, authConfig, datasourceKey) {
66
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
67
+ const res = await client.get(
68
+ `/api/v1/external/${encodeURIComponent(datasourceKey)}/governance-scenarios`
69
+ );
70
+ throwIfApiFailed(res, {
71
+ endpointUrl: governanceScenarioEndpoint(dataplaneUrl, datasourceKey, '/governance-scenarios')
72
+ });
73
+ return unwrapApiData(res);
74
+ }
75
+
76
+ /**
77
+ * POST /api/v1/external/{datasourceKey}/governance-scenarios/run
78
+ * @requiresPermission {Dataplane} governance:evaluate
79
+ * @async
80
+ */
81
+ async function runDatasourceGovernanceScenarios(dataplaneUrl, authConfig, datasourceKey) {
82
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
83
+ const res = await client.post(
84
+ `/api/v1/external/${encodeURIComponent(datasourceKey)}/governance-scenarios/run`,
85
+ { body: {} }
86
+ );
87
+ throwIfApiFailed(res, {
88
+ endpointUrl: governanceScenarioEndpoint(
89
+ dataplaneUrl,
90
+ datasourceKey,
91
+ '/governance-scenarios/run'
92
+ )
93
+ });
94
+ const unwrapped = unwrapApiData(res);
95
+ if (!unwrapped || !unwrapped.summary) {
96
+ throw new Error('Governance scenario run returned an unexpected response shape');
97
+ }
98
+ return unwrapped;
99
+ }
100
+
101
+ /**
102
+ * POST /api/v1/external/{datasourceKey}/governance-scenarios/generate
103
+ * @requiresPermission {Dataplane} external-data-source:update
104
+ * @async
105
+ */
106
+ async function generateDatasourceGovernancePack(
107
+ dataplaneUrl,
108
+ authConfig,
109
+ datasourceKey,
110
+ body
111
+ ) {
112
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
113
+ const res = await client.post(
114
+ `/api/v1/external/${encodeURIComponent(datasourceKey)}/governance-scenarios/generate`,
115
+ { body }
116
+ );
117
+ const subjectEmails = Array.isArray(body.subjects)
118
+ ? body.subjects
119
+ .map(entry => entry && entry.subjectEmail)
120
+ .filter(email => typeof email === 'string' && email.trim())
121
+ : [];
122
+ throwIfApiFailed(res, {
123
+ endpointUrl: governanceScenarioEndpoint(
124
+ dataplaneUrl,
125
+ datasourceKey,
126
+ '/governance-scenarios/generate'
127
+ ),
128
+ subjectEmails
129
+ });
130
+ return unwrapApiData(res);
131
+ }
132
+
133
+ /**
134
+ * GET /api/v1/external/{datasourceKey}/governance-scenarios/versions
135
+ * @requiresPermission {Dataplane} external-data-source:read
136
+ * @async
137
+ */
138
+ async function listDatasourceGovernancePackVersions(dataplaneUrl, authConfig, datasourceKey) {
139
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
140
+ const res = await client.get(
141
+ `/api/v1/external/${encodeURIComponent(datasourceKey)}/governance-scenarios/versions`
142
+ );
143
+ throwIfApiFailed(res, {
144
+ endpointUrl: governanceScenarioEndpoint(
145
+ dataplaneUrl,
146
+ datasourceKey,
147
+ '/governance-scenarios/versions'
148
+ )
149
+ });
150
+ return unwrapApiData(res);
151
+ }
152
+
153
+ module.exports = {
154
+ upsertDatasourceGovernancePack,
155
+ getDatasourceGovernancePack,
156
+ runDatasourceGovernanceScenarios,
157
+ generateDatasourceGovernancePack,
158
+ listDatasourceGovernancePackVersions
159
+ };
package/lib/api/index.js CHANGED
@@ -14,11 +14,13 @@ const { makeApiCall, authenticatedApiCall } = require('../utils/api');
14
14
  class ApiClient {
15
15
  /**
16
16
  * Create an API client instance.
17
- * App endpoints receive token-only auth (Bearer). x-client-id/x-client-secret are not sent to app endpoints.
17
+ * App endpoints receive token-only auth (Bearer). x-client-id/x-client-secret are used only for the controller token endpoint.
18
18
  * @param {string} baseUrl - Base URL for the API (controller URL)
19
19
  * @param {Object} [authConfig] - Authentication configuration
20
- * @param {string} [authConfig.type] - Auth type ('bearer' | 'client-token')
20
+ * @param {string} [authConfig.type] - Auth type ('bearer' | 'client-token' | 'client-credentials')
21
21
  * @param {string} [authConfig.token] - Bearer token
22
+ * @param {string} [authConfig.clientId] - Client ID (client-credentials)
23
+ * @param {string} [authConfig.clientSecret] - Client secret (client-credentials)
22
24
  */
23
25
  constructor(baseUrl, authConfig = {}, clientOptions = {}) {
24
26
  if (baseUrl === null || baseUrl === undefined || typeof baseUrl !== 'string') {
@@ -87,6 +89,41 @@ class ApiClient {
87
89
  return headers;
88
90
  }
89
91
 
92
+ /**
93
+ * True when endpoint is the controller client-credentials token exchange.
94
+ * @private
95
+ * @param {string} endpoint - API endpoint path
96
+ * @returns {boolean}
97
+ */
98
+ _isClientCredentialsTokenEndpoint(endpoint) {
99
+ const path = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
100
+ return path === '/api/v1/auth/token';
101
+ }
102
+
103
+ /**
104
+ * Add x-client-id/x-client-secret only for POST /api/v1/auth/token (never for app endpoints).
105
+ * @private
106
+ * @param {string} endpoint - API endpoint path
107
+ * @param {Object} headers - Request headers built by _buildHeaders
108
+ * @returns {Object}
109
+ */
110
+ _applyClientCredentialHeadersForTokenEndpoint(endpoint, headers) {
111
+ if (this.authConfig.type !== 'client-credentials') {
112
+ return headers;
113
+ }
114
+ if (!this._isClientCredentialsTokenEndpoint(endpoint)) {
115
+ return headers;
116
+ }
117
+ const out = { ...headers };
118
+ if (this.authConfig.clientId) {
119
+ out['x-client-id'] = String(this.authConfig.clientId);
120
+ }
121
+ if (this.authConfig.clientSecret) {
122
+ out['x-client-secret'] = String(this.authConfig.clientSecret);
123
+ }
124
+ return out;
125
+ }
126
+
90
127
  /**
91
128
  * Make a GET request
92
129
  * @async
@@ -141,7 +178,10 @@ class ApiClient {
141
178
  async post(endpoint, options = {}) {
142
179
  await this._ensureDataplaneCliVersionGate();
143
180
  let url = this._buildUrl(endpoint);
144
- const headers = this._buildHeaders(options.headers);
181
+ const headers = this._applyClientCredentialHeadersForTokenEndpoint(
182
+ endpoint,
183
+ this._buildHeaders(options.headers)
184
+ );
145
185
 
146
186
  // Add query parameters if provided
147
187
  if (options.params) {
@@ -0,0 +1,77 @@
1
+ /**
2
+ * @fileoverview Dataplane Enterprise AI Certification lifecycle API client (419.0).
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { createDataplaneApiClient } = require('./index');
10
+ const { unwrapApiData } = require('../utils/external-system-readiness-core');
11
+
12
+ /**
13
+ * GET /api/v1/external/systems/{systemKey}/lifecycle
14
+ * @requiresPermission {Dataplane} external-system:read
15
+ * @async
16
+ * @param {string} dataplaneUrl
17
+ * @param {Object} authConfig
18
+ * @param {string} systemKey
19
+ * @param {{ details?: boolean, summary?: boolean }} [opts]
20
+ * @returns {Promise<Object>}
21
+ */
22
+ async function getSystemLifecycleReport(dataplaneUrl, authConfig, systemKey, opts = {}) {
23
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
24
+ const params = {};
25
+ if (opts.details === true) params.details = true;
26
+ if (opts.summary === true) params.summary = true;
27
+ const res = await client.get(`/api/v1/external/systems/${encodeURIComponent(systemKey)}/lifecycle`, {
28
+ params
29
+ });
30
+ if (res && res.success === false) {
31
+ const msg =
32
+ (res.error && (res.error.formattedError || res.error.error || res.error.message)) ||
33
+ res.formattedError ||
34
+ 'Lifecycle report request failed';
35
+ throw new Error(msg);
36
+ }
37
+ const unwrapped = unwrapApiData(res);
38
+ if (!unwrapped || !unwrapped.systemKey) {
39
+ throw new Error('Lifecycle report returned an unexpected response shape');
40
+ }
41
+ return unwrapped;
42
+ }
43
+
44
+ /**
45
+ * POST /api/v1/external/systems/{systemKey}/lifecycle/run
46
+ * @requiresPermission {Dataplane} external-system:update
47
+ * @async
48
+ * @param {string} dataplaneUrl
49
+ * @param {Object} authConfig
50
+ * @param {string} systemKey
51
+ * @param {Object} [body]
52
+ * @returns {Promise<Object>}
53
+ */
54
+ async function runSystemLifecycle(dataplaneUrl, authConfig, systemKey, body = {}) {
55
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
56
+ const res = await client.post(
57
+ `/api/v1/external/systems/${encodeURIComponent(systemKey)}/lifecycle/run`,
58
+ { body }
59
+ );
60
+ if (res && res.success === false) {
61
+ const msg =
62
+ (res.error && (res.error.formattedError || res.error.error || res.error.message)) ||
63
+ res.formattedError ||
64
+ 'Lifecycle run request failed';
65
+ throw new Error(msg);
66
+ }
67
+ const unwrapped = unwrapApiData(res);
68
+ if (!unwrapped || !unwrapped.report) {
69
+ throw new Error('Lifecycle run returned an unexpected response shape');
70
+ }
71
+ return unwrapped;
72
+ }
73
+
74
+ module.exports = {
75
+ getSystemLifecycleReport,
76
+ runSystemLifecycle
77
+ };
@@ -6,6 +6,7 @@
6
6
 
7
7
  const { ApiClient, createDataplaneApiClient } = require('./index');
8
8
  const { postValidationRunAndOptionalPoll } = require('./validation-runner');
9
+ const { withStageOnValidationRunBody } = require('../utils/validation-run-stage');
9
10
 
10
11
  /**
11
12
  * @param {Object} opts
@@ -30,7 +31,7 @@ function buildValidationRunPayloadTestBody({ validationScope, systemKey, datasou
30
31
  if (validationScope === 'externalDataSource') {
31
32
  body.datasourceKey = datasourceKey;
32
33
  }
33
- return body;
34
+ return withStageOnValidationRunBody(body, body.runType);
34
35
  }
35
36
 
36
37
  /**
@@ -242,6 +243,7 @@ async function testDatasourceViaPipeline({ dataplaneUrl, systemKey, datasourceKe
242
243
  * @param {Object} authConfig - Authentication configuration (must include token for Bearer; client id/secret rejected)
243
244
  * @param {Object} payload - { version, application, dataSources }; optional status (default "draft")
244
245
  * @param {string} [payload.status="draft"] - "draft" or "published"; Builder uses "draft"
246
+ * @param {boolean} [payload.force=false] - When true, bypass guards on active datasource publish
245
247
  * @returns {Promise<Object>} API envelope `{ success, data }` where `data` is PublicationResult (uploadId, system, datasources, generateMcpContract, …)
246
248
  * @throws {Error} If upload fails
247
249
  */
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  const { createDataplaneApiClient } = require('./index');
8
+ const { unwrapApiData } = require('../utils/external-system-readiness-core');
8
9
 
9
10
  /**
10
11
  * Bulk sync external records for a datasource.
@@ -19,10 +20,22 @@ const { createDataplaneApiClient } = require('./index');
19
20
  */
20
21
  async function bulkSyncRecords(dataplaneUrl, sourceIdOrKey, authConfig, body) {
21
22
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
22
- return await client.post(
23
+ const res = await client.post(
23
24
  `/api/v1/data-storage/${encodeURIComponent(sourceIdOrKey)}/records/bulk`,
24
25
  { body }
25
26
  );
27
+ if (res && res.success === false) {
28
+ const msg =
29
+ res.formattedError ||
30
+ res.error ||
31
+ `Bulk sync failed (HTTP ${res.status || 0})`;
32
+ throw new Error(msg);
33
+ }
34
+ const payload = unwrapApiData(res);
35
+ if (!payload || typeof payload !== 'object') {
36
+ throw new Error('Bulk sync returned an unexpected response shape');
37
+ }
38
+ return payload;
26
39
  }
27
40
 
28
41
  module.exports = {
@@ -22,6 +22,7 @@ const { createValidationPollHandlers } = require('../utils/validation-poll-ui');
22
22
  * @param {boolean} opts.useAsync
23
23
  * @param {boolean} opts.noAsync
24
24
  * @param {boolean} [opts.verbosePoll] - Throttled poll lines when no TTY poll UI (see validation-run-poll)
25
+ * @param {boolean} [opts.quietPollUi] - Suppress poll spinner/lines (orchestrated verify-* owns progress)
25
26
  * @param {Function} [opts.onPollProgress] - Extra hook: `(envelope, attemptIndex, meta)` during poll
26
27
  * @returns {Promise<{ envelope: Object|null, apiError: Object|null, pollTimedOut: boolean, incompleteNoAsync: boolean }>}
27
28
  */
@@ -60,7 +61,8 @@ async function postValidationRunAndOptionalPoll(opts) {
60
61
  const deadlineMs = Date.now() + remaining;
61
62
 
62
63
  const pollUi = createValidationPollHandlers(deadlineMs, {
63
- subject: opts.pollSubject
64
+ subject: opts.pollSubject,
65
+ quiet: opts.quietPollUi === true
64
66
  });
65
67
  const mergeOnPollProgress =
66
68
  typeof opts.onPollProgress === 'function'
@@ -60,7 +60,10 @@ async function extractDeploymentConfig(options, _variables) {
60
60
  const { resolveEnvironment } = require('../core/config');
61
61
 
62
62
  const controllerUrl = await resolveControllerUrl();
63
- const envKey = await resolveEnvironment();
63
+ const envKey =
64
+ options && options.envKey
65
+ ? String(options.envKey).trim().toLowerCase()
66
+ : await resolveEnvironment();
64
67
 
65
68
  const pollIntervalExplicit =
66
69
  options.pollInterval !== undefined && options.pollInterval !== null
package/lib/app/down.js CHANGED
@@ -48,6 +48,8 @@ function execAsync(command, options = {}) {
48
48
  * @param {number|string} developerId - Developer ID
49
49
  * @returns {string} Docker volume name
50
50
  */
51
+ const { collectDeveloperVolumeIdSuffixes } = require('../utils/developer-volume-id');
52
+
51
53
  function getAppVolumeName(appName, developerId) {
52
54
  const idNum = typeof developerId === 'string' ? parseInt(developerId, 10) : developerId;
53
55
  if (idNum === 0) {
@@ -56,6 +58,22 @@ function getAppVolumeName(appName, developerId) {
56
58
  return `aifabrix_dev${developerId}_${appName}_data`;
57
59
  }
58
60
 
61
+ /**
62
+ * Docker volume name candidates for an app (handles dev6 vs dev06 naming drift).
63
+ *
64
+ * @param {string} appName
65
+ * @param {string|number} developerId
66
+ * @returns {string[]}
67
+ */
68
+ function getAppVolumeNameCandidates(appName, developerId) {
69
+ const idNum = typeof developerId === 'string' ? parseInt(developerId, 10) : developerId;
70
+ if (idNum === 0) {
71
+ return [`aifabrix_${appName}_data`, `aifabrix_dev0_${appName}_data`];
72
+ }
73
+ const suffixes = collectDeveloperVolumeIdSuffixes(developerId);
74
+ return suffixes.map((suffix) => `aifabrix_dev${suffix}_${appName}_data`);
75
+ }
76
+
59
77
  /**
60
78
  * Stops and removes a specific application's container.
61
79
  * If --volumes is passed, attempts to remove the app's named Docker volume.
@@ -120,6 +138,7 @@ async function downApp(appName, options = {}) {
120
138
 
121
139
  module.exports = {
122
140
  downApp,
123
- getAppVolumeName
141
+ getAppVolumeName,
142
+ getAppVolumeNameCandidates
124
143
  };
125
144
 
package/lib/app/push.js CHANGED
@@ -19,7 +19,7 @@ const {
19
19
 
20
20
  const pushUtils = require('../deployment/push');
21
21
  const logger = require('../utils/logger');
22
- const { detectAppType } = require('../utils/paths');
22
+ const paths = require('../utils/paths');
23
23
 
24
24
  /**
25
25
  * Validate application name format
@@ -92,11 +92,10 @@ function extractImageName(config, appName) {
92
92
  * @throws {Error} If configuration cannot be loaded
93
93
  */
94
94
  async function loadPushConfig(appName, options) {
95
- const { detectAppType, resolveApplicationConfigPath } = require('../utils/paths');
96
95
  const { loadConfigFile } = require('../utils/config-format');
97
- const { appPath } = await detectAppType(appName);
98
96
  try {
99
- const configPath = resolveApplicationConfigPath(appPath);
97
+ const { appPath } = await paths.detectAppType(appName);
98
+ const configPath = paths.resolveApplicationConfigPath(appPath);
100
99
  const config = loadConfigFile(configPath);
101
100
  const effective = getEffectiveConfig(config);
102
101
  const registry = options.registry || effective.image?.registry;
@@ -242,7 +241,7 @@ function displayPushResults(registry, imageName, tags) {
242
241
  */
243
242
  async function pushApp(appName, options = {}) {
244
243
  try {
245
- const { isExternal } = await detectAppType(appName);
244
+ const { isExternal } = await paths.detectAppType(appName);
246
245
  if (isExternal) {
247
246
  logExternalIntegrationPushNotice(appName);
248
247
  return;
@@ -25,6 +25,8 @@ const { callRegisterApi } = require('../utils/app-register-api');
25
25
  const { displayRegistrationResults, getEnvironmentPrefix } = require('../utils/app-register-display');
26
26
  const pathsUtil = require('../utils/paths');
27
27
  const { refreshUrlsLocalRegistryFromBuilder } = require('../utils/urls-local-registry');
28
+ const { resolveControllerUrl } = require('../utils/controller-url');
29
+ const config = require('../core/config');
28
30
 
29
31
  /**
30
32
  * Build registration data payload from app configuration
@@ -152,9 +154,6 @@ async function applyLocalhostPortAdjustment(appConfig, apiUrl, options) {
152
154
  */
153
155
  async function registerApplication(appKey, options = {}) {
154
156
  logger.log(chalk.blue('📋 Registering application...\n'));
155
-
156
- const { resolveControllerUrl } = require('../utils/controller-url');
157
- const config = require('../core/config');
158
157
  await config.ensureSecretsEncryptionKey();
159
158
  const { resolveEnvironment } = config;
160
159
 
@@ -168,7 +167,11 @@ async function registerApplication(appKey, options = {}) {
168
167
  const appConfig = await extractAppConfiguration(finalVariables, appKey, options);
169
168
  await validateAppRegistrationData(appConfig, appKey);
170
169
 
171
- const [controllerUrl, environmentKey] = await Promise.all([resolveControllerUrl(), resolveEnvironment()]);
170
+ const controllerUrl = await resolveControllerUrl();
171
+ const environmentKey =
172
+ options && options.envKey
173
+ ? String(options.envKey).trim().toLowerCase()
174
+ : await resolveEnvironment();
172
175
  const authConfig = await checkAuthentication(controllerUrl, environmentKey);
173
176
  const environment = registerApplicationSchema.environmentId(environmentKey);
174
177