@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,196 @@
1
+ /**
2
+ * @fileoverview Enterprise AI Certification product vocabulary (plan 150.0).
3
+ * Defines verdict enums, tier labels, and operational readiness weights — product model drives API mapping.
4
+ * @author AI Fabrix Team
5
+ * @version 1.0.0
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ /** @typedef {'VERIFIED'|'FAILED'|'NOT_VERIFIED'} LifecycleVerdict */
11
+
12
+ /** @typedef {'BRONZE'|'SILVER'|'GOLD'|'PLATINUM'|'NONE'} CertificationLevel */
13
+
14
+ /** @typedef {'TECHNICALLY_READY'|'AI_READY'|'ENTERPRISE_AI_READY'|'REGULATED_ENTERPRISE_READY'|'NOT_CERTIFIED'} CertificationStatus */
15
+
16
+ const VERDICT = Object.freeze({
17
+ VERIFIED: 'VERIFIED',
18
+ FAILED: 'FAILED',
19
+ NOT_VERIFIED: 'NOT_VERIFIED'
20
+ });
21
+
22
+ const CERTIFICATION_LEVEL = Object.freeze({
23
+ BRONZE: 'BRONZE',
24
+ SILVER: 'SILVER',
25
+ GOLD: 'GOLD',
26
+ PLATINUM: 'PLATINUM',
27
+ NONE: 'NONE'
28
+ });
29
+
30
+ const CERTIFICATION_STATUS = Object.freeze({
31
+ TECHNICALLY_READY: 'TECHNICALLY_READY',
32
+ AI_READY: 'AI_READY',
33
+ ENTERPRISE_AI_READY: 'ENTERPRISE_AI_READY',
34
+ REGULATED_ENTERPRISE_READY: 'REGULATED_ENTERPRISE_READY',
35
+ NOT_CERTIFIED: 'NOT_CERTIFIED'
36
+ });
37
+
38
+ /** Display labels for certification status (aligned with dataplane 419 enum). */
39
+ const STATUS_DISPLAY = Object.freeze({
40
+ TECHNICALLY_READY: 'TECHNICALLY READY',
41
+ AI_READY: 'AI READY',
42
+ ENTERPRISE_AI_READY: 'ENTERPRISE AI READY',
43
+ REGULATED_ENTERPRISE_READY: 'REGULATED ENTERPRISE READY',
44
+ NOT_CERTIFIED: 'NOT CERTIFIED'
45
+ });
46
+
47
+ /**
48
+ * v1 operational readiness weights (sum = 100).
49
+ * connectivity + contracts + runtime + reliability groups from plan 150.
50
+ */
51
+ const OPERATIONS_WEIGHTS = Object.freeze({
52
+ credentials: 10,
53
+ authentication: 10,
54
+ authorization: 10,
55
+ openApi: 8,
56
+ mappings: 7,
57
+ sync: 5,
58
+ crud: 5,
59
+ execution: 5,
60
+ validation: 10,
61
+ unitTests: 10,
62
+ integrationTests: 10,
63
+ e2eTests: 10
64
+ });
65
+
66
+ const WARNING_CODES = Object.freeze({
67
+ TRUST_NOT_VERIFIED: 'TRUST_NOT_VERIFIED',
68
+ GOVERNANCE_NOT_VERIFIED: 'GOVERNANCE_NOT_VERIFIED',
69
+ OPERATIONS_NOT_VERIFIED: 'OPERATIONS_NOT_VERIFIED',
70
+ LIFECYCLE_API_UNAVAILABLE: 'LIFECYCLE_API_UNAVAILABLE'
71
+ });
72
+
73
+ const RECOMMENDATION_COMMAND_BY_CODE = Object.freeze({
74
+ OPERATIONS_NOT_VERIFIED: 'verify-operations',
75
+ TRUST_NOT_VERIFIED: 'verify-trust',
76
+ GOVERNANCE_NOT_VERIFIED: 'governance verify'
77
+ });
78
+
79
+ /**
80
+ * @param {string} systemKey
81
+ * @param {string} command e.g. verify-trust
82
+ * @returns {string}
83
+ */
84
+ function actionHint(systemKey, command) {
85
+ return `aifabrix ${command} ${systemKey}`;
86
+ }
87
+
88
+ /**
89
+ * Normalize lifecycle recommendation to a copy-pastable CLI command.
90
+ * @param {string} systemKey
91
+ * @param {{ code?: string, action?: string }} rec
92
+ * @returns {string}
93
+ */
94
+ function formatRecommendationAction(systemKey, rec) {
95
+ const action = String(rec.action || '').trim();
96
+ if (action.startsWith('aifabrix ')) {
97
+ return action;
98
+ }
99
+ const fromCode = RECOMMENDATION_COMMAND_BY_CODE[rec.code];
100
+ if (fromCode && systemKey) {
101
+ return actionHint(systemKey, fromCode);
102
+ }
103
+ const legacy = /^Run\s+(verify-[a-z-]+)$/i.exec(action);
104
+ if (legacy && systemKey) {
105
+ return actionHint(systemKey, legacy[1]);
106
+ }
107
+ return action || rec.code || '';
108
+ }
109
+
110
+ /**
111
+ * @param {boolean} passed
112
+ * @returns {LifecycleVerdict}
113
+ */
114
+ function verdictFromPassed(passed) {
115
+ return passed ? VERDICT.VERIFIED : VERDICT.FAILED;
116
+ }
117
+
118
+ /**
119
+ * @param {string} [status]
120
+ * @returns {string}
121
+ */
122
+ function formatCertificationStatusDisplay(status) {
123
+ if (!status) return STATUS_DISPLAY.NOT_CERTIFIED;
124
+ return STATUS_DISPLAY[status] || String(status).replace(/_/g, ' ');
125
+ }
126
+
127
+ /**
128
+ * Compute operational readiness percent from weighted check map.
129
+ * @param {Record<string, boolean>} checks
130
+ * @returns {number}
131
+ */
132
+ function computeOperationalReadinessPercent(checks) {
133
+ let earned = 0;
134
+ let total = 0;
135
+ for (const [key, weight] of Object.entries(OPERATIONS_WEIGHTS)) {
136
+ total += weight;
137
+ if (checks[key] === true) {
138
+ earned += weight;
139
+ }
140
+ }
141
+ if (total === 0) return 0;
142
+ return Math.round((earned / total) * 100);
143
+ }
144
+
145
+ /**
146
+ * Derive connectivity/runtime checks from step outcomes (v1 inference rules).
147
+ * @param {{ validation?: boolean, unit?: boolean, integration?: boolean, e2e?: boolean }} steps
148
+ * @returns {Record<string, boolean>}
149
+ */
150
+ function buildOperationsChecksFromSteps(steps) {
151
+ const validation = steps.validation === true;
152
+ const unit = steps.unit === true;
153
+ const integration = steps.integration === true;
154
+ const e2e = steps.e2e === true;
155
+ return {
156
+ credentials: integration || e2e,
157
+ authentication: integration || e2e,
158
+ authorization: integration || e2e,
159
+ openApi: validation,
160
+ mappings: validation,
161
+ sync: e2e,
162
+ crud: e2e,
163
+ execution: e2e,
164
+ validation,
165
+ unitTests: unit,
166
+ integrationTests: integration,
167
+ e2eTests: e2e
168
+ };
169
+ }
170
+
171
+ /**
172
+ * Worst-of rollup for datasource confidence values (0–1).
173
+ * @param {number[]} values
174
+ * @returns {number|null}
175
+ */
176
+ function worstConfidencePercent(values) {
177
+ const nums = values.filter(v => typeof v === 'number' && !Number.isNaN(v));
178
+ if (nums.length === 0) return null;
179
+ return Math.round(Math.min(...nums) * 100);
180
+ }
181
+
182
+ module.exports = {
183
+ VERDICT,
184
+ CERTIFICATION_LEVEL,
185
+ CERTIFICATION_STATUS,
186
+ STATUS_DISPLAY,
187
+ OPERATIONS_WEIGHTS,
188
+ WARNING_CODES,
189
+ actionHint,
190
+ formatRecommendationAction,
191
+ verdictFromPassed,
192
+ formatCertificationStatusDisplay,
193
+ computeOperationalReadinessPercent,
194
+ buildOperationsChecksFromSteps,
195
+ worstConfidencePercent
196
+ };
@@ -0,0 +1,471 @@
1
+ /**
2
+ * @fileoverview TTY display for Enterprise AI Certification verify + lifecycle commands.
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 {
12
+ sectionTitle,
13
+ formatSuccessParagraph,
14
+ formatBlockingError,
15
+ formatWarningLine,
16
+ successGlyph,
17
+ failureGlyph,
18
+ metadata: metaGray
19
+ } = require('../utils/cli-test-layout-chalk');
20
+ const {
21
+ VERDICT,
22
+ formatCertificationStatusDisplay,
23
+ actionHint,
24
+ formatRecommendationAction
25
+ } = require('./product-model');
26
+ const {
27
+ METRIC_KEYS,
28
+ OPERATIONS_METRIC_LABELS
29
+ } = require('./operations-readiness-metrics');
30
+ const { displayGovernanceVerboseDetails } = require('../governance/governance-run-display');
31
+ const {
32
+ formatCertificationLevel,
33
+ formatCriterionCurrentHint,
34
+ resolveNextStepCommand,
35
+ formatProgressBar
36
+ } = require('./certification-progress');
37
+
38
+ const SEP = '────────────────────────';
39
+
40
+ function displayPillarHeader(systemKey, pillarLabel) {
41
+ logger.log('');
42
+ logger.log(sectionTitle(`${String(systemKey).toUpperCase()} — ${pillarLabel}`));
43
+ }
44
+
45
+ function displayHeroMetric(label, percent) {
46
+ logger.log('');
47
+ logger.log(chalk.white(label));
48
+ if (typeof percent === 'number') {
49
+ logger.log(chalk.cyan.bold(`${percent}%`));
50
+ } else {
51
+ logger.log(chalk.gray('—'));
52
+ }
53
+ logger.log('');
54
+ }
55
+
56
+ function displayVerdictLine(verdict, verifiedLabel, failedLabel) {
57
+ if (verdict === VERDICT.VERIFIED) {
58
+ logger.log(formatSuccessParagraph(verifiedLabel));
59
+ } else {
60
+ logger.log(formatBlockingError(failedLabel));
61
+ }
62
+ }
63
+
64
+ /**
65
+ * @param {string} systemKey
66
+ * @param {Array<{ code: string, action?: string }>} warnings
67
+ */
68
+ function displayPillarWarnings(systemKey, warnings) {
69
+ if (!warnings || warnings.length === 0) return;
70
+ logger.log('');
71
+ for (const w of warnings) {
72
+ const action = w.action || actionHint(systemKey, w.suggestedCommand || 'verify-trust');
73
+ logger.log(formatWarningLine(`${w.message || w.code}. Run: ${action}`));
74
+ }
75
+ }
76
+
77
+ /**
78
+ * @param {Record<string, number>|null} readinessMetrics
79
+ */
80
+ function displayOperationsReadinessMetrics(readinessMetrics) {
81
+ if (!readinessMetrics || typeof readinessMetrics !== 'object') {
82
+ return;
83
+ }
84
+ logger.log('');
85
+ logger.log(chalk.white('Readiness breakdown'));
86
+ logger.log(chalk.gray(SEP));
87
+ for (const key of METRIC_KEYS) {
88
+ const label = OPERATIONS_METRIC_LABELS[key] || key;
89
+ const pct = readinessMetrics[key];
90
+ const value =
91
+ typeof pct === 'number' ? chalk.cyan.bold(`${pct}%`) : chalk.gray('—');
92
+ logger.log(` ${label.padEnd(34)}${value}`);
93
+ }
94
+ }
95
+
96
+ /**
97
+ * @param {Record<string, Record<string, boolean>>} detailsGrouped
98
+ */
99
+ function displayOperationsVerificationSteps(detailsGrouped) {
100
+ logger.log('');
101
+ logger.log(chalk.white('Verification steps'));
102
+ logger.log(chalk.gray(SEP));
103
+ const groups = [
104
+ ['Connectivity', ['credentials', 'authentication', 'authorization']],
105
+ ['Contracts', ['openApi', 'mappings']],
106
+ ['Runtime', ['sync', 'crud', 'execution']],
107
+ ['Reliability', ['validation', 'unitTests', 'integrationTests', 'e2eTests']]
108
+ ];
109
+ const labels = {
110
+ credentials: 'Credentials',
111
+ authentication: 'Authentication',
112
+ authorization: 'Authorization',
113
+ openApi: 'OpenAPI',
114
+ mappings: 'Mappings',
115
+ sync: 'Sync',
116
+ crud: 'CRUD',
117
+ execution: 'Execution',
118
+ validation: 'Validation',
119
+ unitTests: 'Unit Tests',
120
+ integrationTests: 'Integration Tests',
121
+ e2eTests: 'E2E Tests'
122
+ };
123
+ for (const [groupName, keys] of groups) {
124
+ logger.log(chalk.white(groupName));
125
+ for (const key of keys) {
126
+ const passed = detailsGrouped[groupName.toLowerCase()]?.[key] ?? detailsGrouped[key];
127
+ const glyph = passed ? successGlyph() : failureGlyph();
128
+ logger.log(` ${labels[key] || key}`.padEnd(24) + glyph);
129
+ }
130
+ }
131
+ }
132
+
133
+ /**
134
+ * @param {string} systemKey
135
+ * @param {Object} result
136
+ * @param {{ details?: boolean, warnings?: Array }} opts
137
+ */
138
+ function displayVerifyOperationsTTY(systemKey, result, opts = {}) {
139
+ displayPillarHeader(systemKey, 'OPERATIONS');
140
+ displayHeroMetric('Operational Readiness', result.operationalReadinessPercent);
141
+ displayVerdictLine(
142
+ result.verdict,
143
+ 'OPERATIONS VERIFIED',
144
+ 'OPERATIONS FAILED'
145
+ );
146
+ displayPillarWarnings(systemKey, result.warnings);
147
+ if (opts.details === true) {
148
+ displayOperationsReadinessMetrics(result.readinessMetrics);
149
+ if (result.details) {
150
+ displayOperationsVerificationSteps(result.details);
151
+ }
152
+ }
153
+ }
154
+
155
+ /**
156
+ * @param {string} systemKey
157
+ * @param {Object} result
158
+ * @param {{ details?: boolean }} opts
159
+ */
160
+ function displayVerifyTrustTTY(systemKey, result, opts = {}) {
161
+ displayPillarHeader(systemKey, 'AI TRUST');
162
+ displayHeroMetric('AI Business Context Confidence', result.businessContextConfidencePercent);
163
+ displayVerdictLine(result.verdict, 'AI TRUST VERIFIED', 'AI TRUST FAILED');
164
+ if (opts.details === true && Array.isArray(result.datasourceRows)) {
165
+ logger.log('');
166
+ logger.log(chalk.white('Datasources'));
167
+ logger.log(chalk.gray(SEP));
168
+ for (const row of result.datasourceRows) {
169
+ const conf =
170
+ typeof row.confidencePercent === 'number' ? `${row.confidencePercent}%` : '—';
171
+ const glyph = row.verdict === VERDICT.VERIFIED ? successGlyph() : failureGlyph();
172
+ logger.log(` ${row.datasourceKey}`.padEnd(36) + `${glyph} ${conf}`);
173
+ }
174
+ }
175
+ }
176
+
177
+ /**
178
+ * @param {string} systemKey
179
+ * @param {Object} result
180
+ * @param {{ details?: boolean }} opts
181
+ */
182
+ function displayVerifyGovernanceTTY(systemKey, result, opts = {}) {
183
+ displayPillarHeader(systemKey, 'GOVERNANCE');
184
+ displayHeroMetric('Policy Coverage', result.policyCoveragePercent);
185
+ logger.log(chalk.white('Dimension Coverage'));
186
+ logger.log(
187
+ typeof result.dimensionCoveragePercent === 'number'
188
+ ? chalk.cyan.bold(`${result.dimensionCoveragePercent}%`)
189
+ : chalk.gray('—')
190
+ );
191
+ logger.log('');
192
+ const scenarios = result.enforcementScenarios || {};
193
+ const total = scenarios.total || 0;
194
+ const passed = scenarios.passed || 0;
195
+ logger.log(chalk.white('Enforcement Scenarios'));
196
+ logger.log(chalk.cyan.bold(`${passed}/${total} Passed`));
197
+ logger.log('');
198
+ displayVerdictLine(result.verdict, 'GOVERNANCE VERIFIED', 'GOVERNANCE FAILED');
199
+ if (opts.details === true) {
200
+ displayGovernanceVerboseDetails(result);
201
+ }
202
+ }
203
+
204
+ function formatPillarLine(label, verdict) {
205
+ if (verdict === VERDICT.VERIFIED) {
206
+ return `${label.padEnd(24)}${successGlyph()} VERIFIED`;
207
+ }
208
+ if (verdict === VERDICT.FAILED) {
209
+ return `${label.padEnd(24)}${failureGlyph()} FAILED`;
210
+ }
211
+ return `${label.padEnd(24)}— NOT VERIFIED`;
212
+ }
213
+
214
+ /**
215
+ * @param {string} label
216
+ * @param {number|undefined} percent
217
+ * @param {string|undefined} verdict
218
+ * @returns {string}
219
+ */
220
+ function formatPillarScoreLine(label, percent, verdict) {
221
+ const COL_LABEL = 22;
222
+ const COL_PCT = 10;
223
+ const pctPlain =
224
+ typeof percent === 'number' && Number.isFinite(percent)
225
+ ? `${Math.round(percent)}%`
226
+ : '—';
227
+ const pctStr =
228
+ typeof percent === 'number' && Number.isFinite(percent)
229
+ ? chalk.cyan.bold(pctPlain)
230
+ : chalk.gray(pctPlain);
231
+ const verdictPart = formatPillarLine('', verdict).trim();
232
+ const pctGap = Math.max(2, COL_PCT - pctPlain.length);
233
+ return `${label.padEnd(COL_LABEL)}${pctStr}${' '.repeat(pctGap)}${verdictPart}`;
234
+ }
235
+
236
+ /** Longest Silver→Gold criterion label ("Operational Readiness ≥ 90%"). */
237
+ const CRITERION_LABEL_COL = 32;
238
+
239
+ /**
240
+ * @param {string} glyph
241
+ * @param {string} label
242
+ * @param {string|null|undefined} hintPlain
243
+ * @returns {string}
244
+ */
245
+ function formatCriterionProgressLine(glyph, label, hintPlain) {
246
+ const prefix = ` ${glyph} `;
247
+ if (!hintPlain) {
248
+ return `${prefix}${label}`;
249
+ }
250
+ const gap = Math.max(2, CRITERION_LABEL_COL - label.length);
251
+ return `${prefix}${label}${' '.repeat(gap)}${metaGray(hintPlain)}`;
252
+ }
253
+
254
+ /**
255
+ * @param {Object} report
256
+ */
257
+ function displayPillarScores(report) {
258
+ logger.log(chalk.white('Pillar scores'));
259
+ logger.log(chalk.gray(SEP));
260
+ logger.log(
261
+ formatPillarScoreLine(
262
+ 'Operations',
263
+ report.operations?.operationalReadinessPercent,
264
+ report.operations?.verdict
265
+ )
266
+ );
267
+ logger.log(
268
+ formatPillarScoreLine(
269
+ 'AI Trust',
270
+ report.trust?.businessContextConfidencePercent,
271
+ report.trust?.verdict
272
+ )
273
+ );
274
+ logger.log(
275
+ formatPillarScoreLine(
276
+ 'Governance',
277
+ report.governance?.policyCoveragePercent,
278
+ report.governance?.verdict
279
+ )
280
+ );
281
+ logger.log('');
282
+ }
283
+
284
+ /**
285
+ * @param {import('./certification-progress').CertificationProgressViewModel} progress
286
+ */
287
+ function displayHighestTierAchieved(progress) {
288
+ logger.log(
289
+ metaGray(
290
+ `Current: ${formatCertificationLevel(progress.currentLevel)} — highest tier achieved`
291
+ )
292
+ );
293
+ logger.log('');
294
+ }
295
+
296
+ /**
297
+ * @param {import('./certification-progress').CertificationProgressViewModel} progress
298
+ */
299
+ function displayProgressTargetHeader(progress) {
300
+ const targetLabel = formatCertificationLevel(progress.targetLevel);
301
+ const headerSuffix =
302
+ progress.mode === 'progress' && typeof progress.progressPercent === 'number'
303
+ ? ` (${progress.progressPercent}%)`
304
+ : '';
305
+ logger.log(
306
+ chalk.white(
307
+ `Current: ${formatCertificationLevel(progress.currentLevel).toUpperCase()} → Progress to ${targetLabel.toUpperCase()}${headerSuffix}`
308
+ )
309
+ );
310
+ logger.log('');
311
+ }
312
+
313
+ /**
314
+ * @param {Object} report
315
+ * @param {import('./certification-progress').CertificationProgressViewModel} progress
316
+ * @param {boolean} openApiPublished
317
+ */
318
+ function displayProgressCriteriaRows(report, progress, openApiPublished) {
319
+ if (progress.criteria.length === 0) {
320
+ logger.log(
321
+ metaGray(
322
+ `Progress criteria for ${formatCertificationLevel(progress.targetLevel)} are not configured yet.`
323
+ )
324
+ );
325
+ logger.log('');
326
+ return;
327
+ }
328
+ for (const row of progress.criteria) {
329
+ const glyph = row.met ? successGlyph() : failureGlyph();
330
+ const hintPlain = row.met
331
+ ? null
332
+ : formatCriterionCurrentHint(row.id, report, openApiPublished);
333
+ logger.log(formatCriterionProgressLine(glyph, row.label, hintPlain));
334
+ }
335
+ }
336
+
337
+ /**
338
+ * @param {import('./certification-progress').CertificationProgressViewModel} progress
339
+ */
340
+ function displayProgressBarRow(progress) {
341
+ if (progress.mode !== 'progress' || typeof progress.progressPercent !== 'number') {
342
+ return;
343
+ }
344
+ logger.log('');
345
+ const targetLabel = formatCertificationLevel(progress.targetLevel);
346
+ const barPlain = formatProgressBar(progress.progressPercent);
347
+ const pctPlain = `${progress.progressPercent}%`;
348
+ const progressLabel = `${targetLabel} progress`;
349
+ logger.log(
350
+ ` ${progressLabel.padEnd(18)} ${chalk.cyan(barPlain)} ${chalk.cyan.bold(pctPlain)}`
351
+ );
352
+ }
353
+
354
+ /**
355
+ * @param {string} systemKey
356
+ * @param {Object} report
357
+ * @param {import('./certification-progress').CertificationProgressViewModel|null} progress
358
+ * @param {boolean} openApiPublished
359
+ * @param {string|null} dataplaneUrl
360
+ * @param {string|null} [dataplaneUiBaseUrl]
361
+ */
362
+ function displayCertificationProgressBlock(
363
+ systemKey,
364
+ report,
365
+ progress,
366
+ openApiPublished,
367
+ dataplaneUrl,
368
+ dataplaneUiBaseUrl = null
369
+ ) {
370
+ if (!progress) {
371
+ return;
372
+ }
373
+ logger.log(chalk.white('Certification progress'));
374
+ logger.log(chalk.gray(SEP));
375
+ if (progress.highestTierAchieved) {
376
+ displayHighestTierAchieved(progress);
377
+ return;
378
+ }
379
+ displayProgressTargetHeader(progress);
380
+ displayProgressCriteriaRows(report, progress, openApiPublished);
381
+ displayProgressBarRow(progress);
382
+ const nextStep = resolveNextStepCommand(systemKey, progress, {
383
+ dataplaneUrl,
384
+ dataplaneUiBaseUrl
385
+ });
386
+ if (nextStep) {
387
+ logger.log('');
388
+ logger.log(chalk.white('Next step:') + ` ${nextStep}`);
389
+ }
390
+ logger.log('');
391
+ }
392
+
393
+ function displayCertificationSummary(report) {
394
+ const level = report.certification?.level || 'NONE';
395
+ const status = report.certification?.status || 'NOT_CERTIFIED';
396
+ logger.log(chalk.white('Certification'));
397
+ logger.log(chalk.gray(SEP));
398
+ logger.log(chalk.cyan.bold(level));
399
+ logger.log('');
400
+ logger.log(chalk.white('Status'));
401
+ logger.log(chalk.gray(SEP));
402
+ logger.log(formatCertificationStatusDisplay(status));
403
+ logger.log('');
404
+ }
405
+
406
+ /**
407
+ * @param {string} systemKey
408
+ * @param {Array<{ code: string, action?: string }>} recs
409
+ */
410
+ function displayRecommendationsBlock(systemKey, recs) {
411
+ if (recs.length === 0) {
412
+ return;
413
+ }
414
+ logger.log('');
415
+ logger.log(chalk.white('Recommendations'));
416
+ logger.log(chalk.gray(SEP));
417
+ logger.log(metaGray(`${recs.length} improvement${recs.length === 1 ? '' : 's'} available`));
418
+ for (const rec of recs) {
419
+ logger.log(` • ${formatRecommendationAction(systemKey, rec)}`);
420
+ }
421
+ }
422
+
423
+ /**
424
+ * @param {Array<Object>} datasources
425
+ */
426
+ function displayDatasourceBreakdown(datasources) {
427
+ logger.log('');
428
+ logger.log(chalk.white('Datasource breakdown'));
429
+ logger.log(chalk.gray(SEP));
430
+ for (const ds of datasources) {
431
+ logger.log(chalk.white(` ${ds.datasourceKey}`));
432
+ logger.log(` ${formatPillarLine('Operations', ds.operations?.verdict).trim()}`);
433
+ logger.log(` ${formatPillarLine('AI Trust', ds.trust?.verdict).trim()}`);
434
+ logger.log(` ${formatPillarLine('Governance', ds.governance?.verdict).trim()}`);
435
+ }
436
+ }
437
+
438
+ /**
439
+ * @param {string} systemKey
440
+ * @param {Object} report - SystemLifecycleReport shape
441
+ * @param {{ details?: boolean, certificationProgress?: Object|null, openApiPublished?: boolean, dataplaneUrl?: string|null, dataplaneUiBaseUrl?: string|null }} opts
442
+ */
443
+ function displayLifecycleReportTTY(systemKey, report, opts = {}) {
444
+ logger.log('');
445
+ logger.log(sectionTitle(`${String(systemKey).toUpperCase()} CERTIFICATION REPORT`));
446
+ logger.log('');
447
+ displayCertificationSummary(report);
448
+ displayPillarScores(report);
449
+ displayCertificationProgressBlock(
450
+ systemKey,
451
+ report,
452
+ opts.certificationProgress ?? null,
453
+ opts.openApiPublished === true,
454
+ opts.dataplaneUrl ?? null,
455
+ opts.dataplaneUiBaseUrl ?? null
456
+ );
457
+ displayRecommendationsBlock(systemKey, report.recommendations || []);
458
+ if (opts.details === true && Array.isArray(report.datasources) && report.datasources.length > 0) {
459
+ displayDatasourceBreakdown(report.datasources);
460
+ }
461
+ logger.log('');
462
+ }
463
+
464
+ module.exports = {
465
+ displayVerifyOperationsTTY,
466
+ displayVerifyTrustTTY,
467
+ displayVerifyGovernanceTTY,
468
+ displayLifecycleReportTTY,
469
+ displayOperationsReadinessMetrics,
470
+ displayOperationsVerificationSteps
471
+ };