@aifabrix/builder 2.50.0 → 2.51.1

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 (236) hide show
  1. package/.cursor/plans/Done/157.1-security-quality-violations-by-type.csv +374 -0
  2. package/.cursor/rules/assistant-driven-integration-build.mdc +40 -0
  3. package/integration/hubspot-test/create-hubspot.js +1 -16
  4. package/jest.isolated-projects.js +77 -1
  5. package/jest.projects.js +5 -426
  6. package/lib/api/assistants.api.js +67 -0
  7. package/lib/api/datasources-extended.api.js +6 -6
  8. package/lib/api/evidence-authoring-helpers.js +267 -0
  9. package/lib/api/evidence-authoring.api.js +337 -0
  10. package/lib/api/evidence-kinds.api.js +128 -0
  11. package/lib/api/evidence.api.js +57 -1
  12. package/lib/api/index.js +24 -7
  13. package/lib/api/pipeline.api.js +47 -3
  14. package/lib/api/records-bulk.api.js +28 -3
  15. package/lib/api/resource-types.api.js +4 -4
  16. package/lib/api/roles.api.js +121 -0
  17. package/lib/api/types/evidence-kinds.types.js +42 -0
  18. package/lib/api/types/evidence.types.js +156 -8
  19. package/lib/api/types/roles.types.js +36 -0
  20. package/lib/api/work.api.js +283 -0
  21. package/lib/app/certification-verify-rows.js +1 -1
  22. package/lib/app/run-deploy-job.js +108 -0
  23. package/lib/app/run-helpers.js +10 -3
  24. package/lib/app/run.js +8 -0
  25. package/lib/app/show.js +2 -2
  26. package/lib/certification/merge-certification-from-artifact.js +1 -1
  27. package/lib/channels/add-channel.js +54 -23
  28. package/lib/channels/approval-guides/chatgpt.js +8 -7
  29. package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
  30. package/lib/channels/channel-artifact-generator.js +16 -3
  31. package/lib/channels/channel-credential.js +3 -1
  32. package/lib/channels/channel-probe-evidence.js +79 -0
  33. package/lib/channels/channel-probe.js +265 -13
  34. package/lib/channels/channel-runtime-resolver.js +120 -29
  35. package/lib/channels/channel-status-display.js +44 -0
  36. package/lib/channels/channel-targets.js +7 -0
  37. package/lib/channels/chatgpt-runtime-contract.js +29 -0
  38. package/lib/channels/microsoft-copilot-package.js +280 -0
  39. package/lib/channels/runtime-tool-catalog.js +165 -0
  40. package/lib/cli/index.js +6 -0
  41. package/lib/cli/setup-app.js +26 -5
  42. package/lib/cli/setup-dev.js +23 -7
  43. package/lib/cli/setup-external-system-help.js +8 -2
  44. package/lib/cli/setup-external-system.js +12 -0
  45. package/lib/cli/setup-utility-help-after.js +12 -0
  46. package/lib/cli/setup-utility-validate.js +41 -2
  47. package/lib/commands/app-test.js +2 -14
  48. package/lib/commands/datasource-load-export-cli.js +6 -1
  49. package/lib/commands/deploy-gate-failure-details.js +23 -0
  50. package/lib/commands/deploy-gate-orchestrator.js +64 -18
  51. package/lib/commands/dev-cli-handlers.js +6 -3
  52. package/lib/commands/dev-down.js +2 -6
  53. package/lib/commands/dev-init.js +7 -109
  54. package/lib/commands/dev-refresh.js +197 -0
  55. package/lib/commands/evidence-kind.js +478 -0
  56. package/lib/commands/governance-command-action.js +4 -2
  57. package/lib/commands/governance-generate-external.js +56 -21
  58. package/lib/commands/lifecycle-external.js +10 -4
  59. package/lib/commands/repair-datasource-apply.js +10 -1
  60. package/lib/commands/repair-datasource-run.js +4 -2
  61. package/lib/commands/repair-datasource-test-payload.js +80 -19
  62. package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
  63. package/lib/commands/repair-internal.js +9 -1
  64. package/lib/commands/repair-openapi-export.js +1 -1
  65. package/lib/commands/repair-openapi-sync.js +43 -18
  66. package/lib/commands/resource-type.js +101 -18
  67. package/lib/commands/role-assistant.js +313 -0
  68. package/lib/commands/role.js +452 -0
  69. package/lib/commands/setup.js +1 -1
  70. package/lib/commands/upload-data-sync-finalize.js +5 -4
  71. package/lib/commands/upload-sidecar-followups.js +144 -47
  72. package/lib/commands/upload.js +43 -6
  73. package/lib/commands/validate-json.js +230 -0
  74. package/lib/commands/verify-governance-combined-external.js +73 -9
  75. package/lib/commands/verify-governance-db.js +80 -25
  76. package/lib/commands/verify-governance-external.js +29 -5
  77. package/lib/commands/verify-operations-step-plan.js +78 -40
  78. package/lib/commands/wizard-core-helpers.js +7 -3
  79. package/lib/commands/wizard-core.js +78 -9
  80. package/lib/core/audit-logger.js +1 -1
  81. package/lib/core/secrets-load.js +1 -3
  82. package/lib/datasource/bulk-loader-run-batches.js +4 -2
  83. package/lib/datasource/bulk-loader-service.js +5 -1
  84. package/lib/datasource/capability/relate-operations.js +1 -1
  85. package/lib/datasource/datasource-validate-summary.js +2 -2
  86. package/lib/datasource/validate.js +13 -6
  87. package/lib/external-system/deploy-helpers.js +35 -1
  88. package/lib/external-system/deploy.js +23 -10
  89. package/lib/external-system/download-helpers.js +111 -2
  90. package/lib/external-system/download-role-assistant.js +55 -0
  91. package/lib/external-system/download.js +45 -26
  92. package/lib/external-system/test-helpers.js +1 -1
  93. package/lib/external-system/test.js +39 -3
  94. package/lib/generator/builders.js +3 -0
  95. package/lib/generator/helpers.js +13 -5
  96. package/lib/generator/split-variables.js +9 -3
  97. package/lib/generator/wizard.js +51 -4
  98. package/lib/infrastructure/helpers-docker-check.js +6 -0
  99. package/lib/infrastructure/helpers.js +1 -1
  100. package/lib/lifecycle/access-control-abac.js +121 -0
  101. package/lib/lifecycle/lifecycle-governance-options.js +4 -1
  102. package/lib/lifecycle/product-model.js +3 -2
  103. package/lib/lifecycle/report-display.js +14 -2
  104. package/lib/programmatic/auth-config.js +3 -2
  105. package/lib/programmatic/bearer-auth.js +1 -1
  106. package/lib/programmatic/capability-registry.js +20 -1
  107. package/lib/programmatic/command-options.js +1 -0
  108. package/lib/programmatic/enterprise-mcp-client.js +18 -2
  109. package/lib/programmatic/index.js +4 -0
  110. package/lib/programmatic/manifest-patch-section.js +48 -9
  111. package/lib/programmatic/manifest-section-registry-data.js +3 -2
  112. package/lib/programmatic/manifest-section-registry.js +18 -1
  113. package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
  114. package/lib/programmatic/openapi-component-schemas.js +16 -1
  115. package/lib/programmatic/openapi-description-enricher.js +3 -1
  116. package/lib/programmatic/openapi-descriptions.js +55 -5
  117. package/lib/programmatic/openapi-evidence-schemas.js +161 -0
  118. package/lib/programmatic/openapi-manifest-schemas.js +6 -2
  119. package/lib/programmatic/openapi-operation-bindings.js +99 -0
  120. package/lib/programmatic/openapi-request-schemas.js +36 -1
  121. package/lib/programmatic/openapi-result-schemas.js +29 -12
  122. package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
  123. package/lib/programmatic/openapi-schema-descriptions-core.js +6 -3
  124. package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
  125. package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
  126. package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
  127. package/lib/programmatic/openapi-schema-helpers.js +16 -1
  128. package/lib/programmatic/openapi-spec.js +48 -29
  129. package/lib/programmatic/orchestration-openapi-meta.js +5 -0
  130. package/lib/programmatic/request-timeout.js +3 -0
  131. package/lib/programmatic/route-handler-map.js +89 -2
  132. package/lib/programmatic/run-certify.js +15 -2
  133. package/lib/programmatic/run-deploy.js +74 -0
  134. package/lib/programmatic/run-evidence-authoring.js +80 -0
  135. package/lib/programmatic/run-protection.js +124 -0
  136. package/lib/programmatic/run-repository-emcp.js +11 -3
  137. package/lib/programmatic/run-template-workspace.js +42 -2
  138. package/lib/programmatic/workspace-context.js +6 -0
  139. package/lib/protection/auth-context.js +48 -6
  140. package/lib/protection/paths.js +22 -0
  141. package/lib/protection/run-protection-create-helpers.js +22 -1
  142. package/lib/protection/run-protection-create.js +1 -1
  143. package/lib/protection/sync-after-upload.js +1 -1
  144. package/lib/resolvers/evidence-kind-file.js +230 -0
  145. package/lib/resolvers/resource-type-file.js +36 -1
  146. package/lib/resolvers/role-file.js +227 -0
  147. package/lib/role-assistant/evidence-sync.js +450 -0
  148. package/lib/role-assistant/github-workflow.js +96 -0
  149. package/lib/role-assistant/knowledge-sync.js +465 -0
  150. package/lib/role-assistant/settings.js +126 -0
  151. package/lib/role-assistant/test-cases-evaluate.js +170 -0
  152. package/lib/role-assistant/test-cases-honesty.js +184 -0
  153. package/lib/role-assistant/test-cases.js +364 -0
  154. package/lib/role-assistant/test-runner-workhub-answers.js +203 -0
  155. package/lib/role-assistant/test-runner-workhub.js +385 -0
  156. package/lib/role-assistant/test-runner.js +500 -0
  157. package/lib/schema/application-schema.json +36 -2
  158. package/lib/schema/deployment-rules.yaml +1 -0
  159. package/lib/schema/external-datasource.schema.json +121 -4
  160. package/lib/schema/external-system.schema.json +20 -6
  161. package/lib/schema/infra.parameter.yaml +45 -1
  162. package/lib/schema/role-assistant-settings.schema.json +108 -0
  163. package/lib/schema/type/credential-field-registry.json +19 -3
  164. package/lib/utils/api.js +52 -21
  165. package/lib/utils/app-path-resolve.js +72 -24
  166. package/lib/utils/app-register-config.js +4 -3
  167. package/lib/utils/authentication-shape.js +3 -1
  168. package/lib/utils/cli-sync-options.js +59 -3
  169. package/lib/utils/cli-utils.js +11 -3
  170. package/lib/utils/deployment-validation.js +28 -3
  171. package/lib/utils/dev-ca-install.js +5 -6
  172. package/lib/utils/dev-config.js +2 -2
  173. package/lib/utils/dev-list-sort.js +145 -0
  174. package/lib/utils/dev-platform-tls.js +0 -63
  175. package/lib/utils/docker-build.js +46 -27
  176. package/lib/utils/docker-daemon-tls-ca.js +1 -1
  177. package/lib/utils/docker-exec.js +91 -6
  178. package/lib/utils/docker-run-ephemeral.js +55 -0
  179. package/lib/utils/enterprise-knowledge-paths.js +29 -12
  180. package/lib/utils/env-copy-keycloak.js +196 -0
  181. package/lib/utils/env-copy.js +35 -4
  182. package/lib/utils/error-formatters/error-parser.js +0 -3
  183. package/lib/utils/external-env-template.js +1 -1
  184. package/lib/utils/external-system-readiness-compact-blocks.js +28 -1
  185. package/lib/utils/external-system-readiness-compact-display.js +3 -0
  186. package/lib/utils/health-check-request.js +2 -3
  187. package/lib/utils/help-builder.js +4 -0
  188. package/lib/utils/image-name.js +22 -4
  189. package/lib/utils/logger.js +37 -4
  190. package/lib/utils/manifest-source-emit.js +1 -1
  191. package/lib/utils/paths.js +98 -26
  192. package/lib/utils/pipeline-upload-auth-retry.js +19 -3
  193. package/lib/utils/postgres-platform-bootstrap.js +24 -17
  194. package/lib/utils/postgres-wipe.js +5 -2
  195. package/lib/utils/remote-dev-auth.js +39 -4
  196. package/lib/utils/resolve-docker-image-ref.js +36 -6
  197. package/lib/utils/role-assistant-paths.js +98 -0
  198. package/lib/utils/schema-loader.js +41 -0
  199. package/lib/utils/schema-resolver.js +36 -4
  200. package/lib/utils/token-manager.js +47 -22
  201. package/lib/utils/upload-sync-options.js +17 -3
  202. package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
  203. package/lib/validation/external-manifest-validator.js +31 -8
  204. package/lib/validation/openapi-contract-surface-validator.js +4 -0
  205. package/lib/validation/validate-external-file.js +8 -1
  206. package/lib/validation/validator.js +6 -5
  207. package/package.json +35 -12
  208. package/scripts/export-builder-api-evidence.js +3 -3
  209. package/scripts/lib/plan-actor.js +2 -2
  210. package/scripts/lib/plan-meta.js +1 -1
  211. package/scripts/lib/plan-parse.js +1 -1
  212. package/scripts/lib/plan-prevalidate-gates.js +12 -1
  213. package/scripts/lib/plan-run-report.js +13 -2
  214. package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +10 -10
  215. package/templates/applications/dataplane/application.yaml +47 -17
  216. package/templates/applications/dataplane/env.template +28 -9
  217. package/templates/applications/dataplane/rbac.yaml +78 -2
  218. package/templates/applications/miso-controller/application.yaml +10 -1
  219. package/templates/applications/miso-controller/env.template +13 -8
  220. package/templates/applications/miso-controller/rbac.yaml +72 -0
  221. package/templates/channels/chatgpt/install.template.md.hbs +42 -24
  222. package/templates/channels/chatgpt/integration/README.md +10 -7
  223. package/templates/channels/chatgpt/integration/application.json +2 -2
  224. package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
  225. package/templates/channels/chatgpt/integration/env.template +1 -1
  226. package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
  227. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
  228. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
  229. package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
  230. package/templates/channels/microsoft-copilot/integration/README.md +9 -7
  231. package/templates/channels/microsoft-copilot/integration/application.json +2 -2
  232. package/templates/channels/microsoft-copilot/integration/env.template +1 -1
  233. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
  234. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
  235. package/templates/channels/slack/integration/slack-system.json +60 -2
  236. package/templates/python/requirements.txt +1 -1
@@ -0,0 +1,478 @@
1
+ /**
2
+ * Evidence kind catalog commands (Controller).
3
+ *
4
+ * Commands:
5
+ * - aifabrix evidence-kind create|import|export|get|list
6
+ *
7
+ * Distinct from `aifabrix evidence` (Evidence Fabrix v2 rows).
8
+ *
9
+ * @fileoverview Evidence kind catalog CLI commands
10
+ * @author AI Fabrix Team
11
+ * @version 1.0.0
12
+ */
13
+
14
+ 'use strict';
15
+
16
+ const chalk = require('chalk');
17
+ const logger = require('../utils/logger');
18
+ const { resolveControllerUrl } = require('../utils/controller-url');
19
+ const { normalizeControllerUrl, resolveEnvironment } = require('../core/config');
20
+ const { getOrRefreshDeviceToken } = require('../utils/token-manager');
21
+ const {
22
+ readEvidenceKindCreateFile,
23
+ readEvidenceKindCatalogFile,
24
+ writeEvidenceKindCatalogFile,
25
+ mapEvidenceKindFileToApiPayload,
26
+ mapEvidenceKindApiToFileRow
27
+ } = require('../resolvers/evidence-kind-file');
28
+ const {
29
+ formatBlockingError,
30
+ formatSuccessLine,
31
+ infoLine,
32
+ headerKeyValue
33
+ } = require('../utils/cli-layout-chalk');
34
+ const {
35
+ listEvidenceKinds,
36
+ getEvidenceKind,
37
+ createEvidenceKindIdempotent
38
+ } = require('../api/evidence-kinds.api');
39
+
40
+ const EVIDENCE_KIND_KEY_PATTERN = /^[a-z][a-z0-9_]*$/;
41
+
42
+ const EVIDENCE_KIND_HELP_AFTER = `
43
+
44
+ Examples:
45
+ $ aifabrix evidence-kind create --key obligation --display-name "Obligation" --domain governance --purpose "Track commitments"
46
+ $ aifabrix evidence-kind get obligation
47
+ $ aifabrix evidence-kind list --search risk
48
+
49
+ Per-command help: aifabrix evidence-kind <command> --help
50
+ `;
51
+
52
+ const EVIDENCE_KIND_IMPORT_HELP_AFTER = `
53
+
54
+ Examples:
55
+ $ aifabrix evidence-kind import --file ./baseline-evidence-kinds.json
56
+ `;
57
+
58
+ const EVIDENCE_KIND_EXPORT_HELP_AFTER = `
59
+
60
+ Examples:
61
+ $ aifabrix evidence-kind export --file ./evidence-kinds-export.json
62
+ `;
63
+
64
+ const EVIDENCE_KIND_CREATE_HELP_AFTER = `
65
+
66
+ Examples:
67
+ $ aifabrix evidence-kind create --key obligation --display-name "Obligation" --domain governance --purpose "Track commitments"
68
+ $ aifabrix evidence-kind create --key risk --display-name "Risk" --domain governance --purpose "..." --default-severity high
69
+ $ aifabrix evidence-kind create --file ./obligation.json
70
+ `;
71
+
72
+ const EVIDENCE_KIND_GET_HELP_AFTER = `
73
+
74
+ Examples:
75
+ $ aifabrix evidence-kind get obligation
76
+ $ aifabrix evidence-kind get clx1234567890abcdef
77
+ `;
78
+
79
+ const EVIDENCE_KIND_LIST_HELP_AFTER = `
80
+
81
+ Examples:
82
+ $ aifabrix evidence-kind list
83
+ $ aifabrix evidence-kind list --page 1 --page-size 50
84
+ $ aifabrix evidence-kind list --search risk
85
+ `;
86
+
87
+ /**
88
+ * @param {string} raw
89
+ * @returns {string}
90
+ */
91
+ function requireEvidenceKindKey(raw) {
92
+ const key = String(raw || '').trim();
93
+ if (!key) {
94
+ throw new Error('Evidence kind key is required (--key <key> or --file <path>).');
95
+ }
96
+ if (!EVIDENCE_KIND_KEY_PATTERN.test(key)) {
97
+ throw new Error(
98
+ 'Evidence kind key must start with a lowercase letter and contain only lowercase letters, numbers, and underscores.'
99
+ );
100
+ }
101
+ return key;
102
+ }
103
+
104
+ /**
105
+ * @param {Object} _options
106
+ * @returns {Promise<{ controllerUrl: string, authConfig: Object }>}
107
+ */
108
+ async function resolveControllerAndAuth(_options) {
109
+ const controllerUrl = await resolveControllerUrl();
110
+ if (!controllerUrl) {
111
+ throw new Error('Controller URL is required. Run "aifabrix login" first.');
112
+ }
113
+ const normalized = normalizeControllerUrl(controllerUrl);
114
+ const deviceToken = await getOrRefreshDeviceToken(normalized);
115
+ if (!deviceToken || !deviceToken.token) {
116
+ throw new Error(
117
+ `Not authenticated for controller: ${controllerUrl}. ` +
118
+ 'Run "aifabrix login" and try again.'
119
+ );
120
+ }
121
+ return {
122
+ controllerUrl: deviceToken.controller || normalized,
123
+ authConfig: { type: 'bearer', token: deviceToken.token }
124
+ };
125
+ }
126
+
127
+ async function resolveHeaderContext() {
128
+ const env = await resolveEnvironment();
129
+ const controllerUrl = await resolveControllerUrl();
130
+ const normalized = normalizeControllerUrl(controllerUrl);
131
+ return { environment: env || 'dev', controllerUrl: normalized };
132
+ }
133
+
134
+ /**
135
+ * @param {Object} response
136
+ * @returns {any}
137
+ */
138
+ function unwrapControllerData(response) {
139
+ return response?.data?.data ?? response?.data ?? response;
140
+ }
141
+
142
+ /**
143
+ * Build create payload from flags and optional --file (maps domain → domainKey).
144
+ * @param {Object} options
145
+ * @returns {Object}
146
+ */
147
+ function buildCreatePayload(options) {
148
+ let base = {};
149
+ if (options.file) {
150
+ base = readEvidenceKindCreateFile(options.file);
151
+ }
152
+ const merged = { ...base };
153
+ if (options.key) merged.key = options.key;
154
+ if (options.displayName) merged.displayName = options.displayName;
155
+ if (options.description !== undefined) merged.description = options.description;
156
+ if (options.purpose !== undefined) merged.purpose = options.purpose;
157
+ if (options.expectedUse !== undefined) merged.expectedUse = options.expectedUse;
158
+ if (options.defaultSeverity) merged.defaultSeverity = options.defaultSeverity;
159
+ if (options.domain) {
160
+ merged.domain = options.domain;
161
+ delete merged.domainKey;
162
+ }
163
+ if (options.file && !options.domain && base.domainKey) {
164
+ return { ...base, key: requireEvidenceKindKey(merged.key || base.key) };
165
+ }
166
+ const payload = mapEvidenceKindFileToApiPayload(
167
+ {
168
+ ...merged,
169
+ domain: merged.domain || merged.domainKey
170
+ },
171
+ 0
172
+ );
173
+ payload.key = requireEvidenceKindKey(payload.key);
174
+ return payload;
175
+ }
176
+
177
+ /**
178
+ * @param {Object[]} rows
179
+ * @param {string} controllerUrl
180
+ * @param {Object} authConfig
181
+ * @returns {Promise<{ created: number, existed: number, total: number }>}
182
+ */
183
+ async function importEvidenceKindRows(rows, controllerUrl, authConfig) {
184
+ let created = 0;
185
+ let existed = 0;
186
+ for (const payload of rows) {
187
+ payload.key = requireEvidenceKindKey(payload.key);
188
+ const out = await createEvidenceKindIdempotent(controllerUrl, authConfig, payload);
189
+ if (out.created) {
190
+ created += 1;
191
+ continue;
192
+ }
193
+ existed += 1;
194
+ }
195
+ return { created, existed, total: rows.length };
196
+ }
197
+
198
+ /**
199
+ * @param {string} controllerUrl
200
+ * @param {Object} authConfig
201
+ * @returns {Promise<Object[]>}
202
+ */
203
+ async function fetchAllEvidenceKinds(controllerUrl, authConfig) {
204
+ const all = [];
205
+ let page = 1;
206
+ const pageSize = 100;
207
+ for (;;) {
208
+ const res = await listEvidenceKinds(controllerUrl, authConfig, { page, pageSize });
209
+ const payload = unwrapControllerData(res);
210
+ const items = payload?.data ?? payload ?? [];
211
+ if (!Array.isArray(items) || items.length === 0) {
212
+ break;
213
+ }
214
+ all.push(...items);
215
+ if (items.length < pageSize) {
216
+ break;
217
+ }
218
+ page += 1;
219
+ }
220
+ return all;
221
+ }
222
+
223
+ function setupEvidenceKindImportCommand(cmd) {
224
+ cmd
225
+ .command('import')
226
+ .description('Import evidence kinds from baseline JSON (idempotent)')
227
+ .addHelpText('after', EVIDENCE_KIND_IMPORT_HELP_AFTER)
228
+ .requiredOption('--file <path>', 'Baseline catalog (evidenceKinds[]) or JSON array')
229
+ .action(async(options) => {
230
+ try {
231
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth(options);
232
+ const rows = readEvidenceKindCatalogFile(options.file);
233
+ const summary = await importEvidenceKindRows(rows, controllerUrl, authConfig);
234
+ logger.log(
235
+ formatSuccessLine(
236
+ `Evidence kinds imported: ${summary.total} total ` +
237
+ `(${summary.created} created, ${summary.existed} already existed)`
238
+ )
239
+ );
240
+ } catch (e) {
241
+ logger.error(formatBlockingError(e.message));
242
+ process.exit(1);
243
+ }
244
+ });
245
+ }
246
+
247
+ function setupEvidenceKindExportCommand(cmd) {
248
+ cmd
249
+ .command('export')
250
+ .description('Export evidence kinds to baseline-shaped JSON')
251
+ .addHelpText('after', EVIDENCE_KIND_EXPORT_HELP_AFTER)
252
+ .requiredOption('--file <path>', 'Output path for baseline catalog JSON')
253
+ .action(async(options) => {
254
+ try {
255
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth(options);
256
+ const records = await fetchAllEvidenceKinds(controllerUrl, authConfig);
257
+ const rows = records.map(mapEvidenceKindApiToFileRow);
258
+ writeEvidenceKindCatalogFile(options.file, rows);
259
+ logger.log(
260
+ formatSuccessLine(`Evidence kinds exported: ${rows.length} written to ${options.file}`)
261
+ );
262
+ } catch (e) {
263
+ logger.error(formatBlockingError(e.message));
264
+ process.exit(1);
265
+ }
266
+ });
267
+ }
268
+
269
+ function setupEvidenceKindCreateCommand(cmd) {
270
+ cmd
271
+ .command('create')
272
+ .description('Create evidence kind (idempotent; success if it already exists)')
273
+ .addHelpText('after', EVIDENCE_KIND_CREATE_HELP_AFTER)
274
+ .option('--file <path>', 'Read evidence kind create payload from JSON file')
275
+ .option('--key <key>', 'Catalog key (lowercase letters, digits, underscores)')
276
+ .option('--display-name <name>', 'Display name')
277
+ .option('--domain <domain>', 'Enterprise domain key (e.g. governance, sales)')
278
+ .option('--purpose <text>', 'Evidence kind purpose (required)')
279
+ .option('--expected-use <text>', 'Expected use guidance')
280
+ .option('--default-severity <level>', 'low | medium | high | critical')
281
+ .option('--description <text>', 'Description')
282
+ .action(async(options) => {
283
+ try {
284
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth(options);
285
+ const payload = buildCreatePayload(options);
286
+ const out = await createEvidenceKindIdempotent(controllerUrl, authConfig, payload);
287
+ const row = unwrapControllerData(out.response);
288
+ const domain = row.domainKey || payload.domainKey || '—';
289
+ if (out.created) {
290
+ logger.log(formatSuccessLine(`Evidence kind created: ${row.key} (domain: ${domain})`));
291
+ return;
292
+ }
293
+ logger.log(formatSuccessLine(`Evidence kind exists: ${row.key} (domain: ${domain})`));
294
+ logger.log(infoLine('No changes were needed.'));
295
+ } catch (e) {
296
+ logger.error(formatBlockingError(e.message));
297
+ process.exit(1);
298
+ }
299
+ });
300
+ }
301
+
302
+ /**
303
+ * @param {object} contribution
304
+ * @returns {string}
305
+ */
306
+ function formatEvidenceKindContributionLine(contribution) {
307
+ const parts = [
308
+ contribution.key || '—',
309
+ contribution.typeRef || null,
310
+ contribution.referenceValue !== null
311
+ && contribution.referenceValue !== undefined
312
+ && contribution.unit
313
+ ? `${contribution.referenceValue} ${contribution.unit}`
314
+ : contribution.unit || null,
315
+ contribution.primary ? 'primary' : null,
316
+ contribution.attributionKey || null,
317
+ contribution.attributionBoundary || null
318
+ ].filter(Boolean);
319
+ return ` - ${parts.join(' · ')}`;
320
+ }
321
+
322
+ /**
323
+ * @param {object} row
324
+ * @returns {void}
325
+ */
326
+ function logEvidenceKindContributions(row) {
327
+ const contributions = row?.metadata?.contributions;
328
+ if (!Array.isArray(contributions) || contributions.length === 0) {
329
+ return;
330
+ }
331
+ logger.log(headerKeyValue('Contributions:', `${contributions.length}`));
332
+ contributions.forEach((item) => {
333
+ logger.log(formatEvidenceKindContributionLine(item));
334
+ });
335
+ }
336
+
337
+ /**
338
+ * @param {object} row
339
+ * @param {string} environment
340
+ * @param {string} controllerUrl
341
+ * @returns {void}
342
+ */
343
+ function displayEvidenceKindDetail(row, environment, controllerUrl) {
344
+ logger.log(
345
+ chalk.bold(`\n📎 Evidence kind in ${environment} environment (${controllerUrl}):\n`)
346
+ );
347
+ logger.log(headerKeyValue('Key:', row.key || '—'));
348
+ logger.log(headerKeyValue('Display:', row.displayName || '—'));
349
+ logger.log(headerKeyValue('Domain:', row.domainKey || '—'));
350
+ logger.log(headerKeyValue('Purpose:', row.purpose || '—'));
351
+ logger.log(headerKeyValue('Severity:', row.defaultSeverity || '—'));
352
+ logger.log(headerKeyValue('Active:', row.isActive === false ? 'false' : 'true'));
353
+ if (row.source) logger.log(headerKeyValue('Source:', row.source));
354
+ if (row.requiresApproval !== undefined) {
355
+ logger.log(headerKeyValue('Requires approval:', row.requiresApproval ? 'true' : 'false'));
356
+ }
357
+ if (row.expectedUse) logger.log(headerKeyValue('Expected use:', row.expectedUse));
358
+ if (row.description) logger.log(headerKeyValue('Description:', row.description));
359
+ logEvidenceKindContributions(row);
360
+ logger.log('');
361
+ }
362
+
363
+ function setupEvidenceKindGetCommand(cmd) {
364
+ cmd
365
+ .command('get <evidenceKindIdOrKey>')
366
+ .description('Get evidence kind by id or key')
367
+ .addHelpText('after', EVIDENCE_KIND_GET_HELP_AFTER)
368
+ .action(async(evidenceKindIdOrKey, options) => {
369
+ try {
370
+ const { environment, controllerUrl: headerControllerUrl } = await resolveHeaderContext();
371
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth(options);
372
+ const res = await getEvidenceKind(controllerUrl, authConfig, String(evidenceKindIdOrKey).trim());
373
+ const row = unwrapControllerData(res);
374
+ displayEvidenceKindDetail(row, environment, headerControllerUrl);
375
+ } catch (e) {
376
+ logger.error(formatBlockingError(e.message));
377
+ process.exit(1);
378
+ }
379
+ });
380
+ }
381
+
382
+ const EVIDENCE_KIND_LIST_COLUMNS = { key: 24, display: 24, domain: 14, severity: 10, active: 8 };
383
+
384
+ function logEvidenceKindListHeader(columns) {
385
+ const keyCol = 'Key'.padEnd(columns.key);
386
+ const displayCol = 'Display'.padEnd(columns.display);
387
+ const domainCol = 'Domain'.padEnd(columns.domain);
388
+ const severityCol = 'Severity'.padEnd(columns.severity);
389
+ const activeCol = 'Active'.padEnd(columns.active);
390
+ logger.log(chalk.gray(`${keyCol}${displayCol}${domainCol}${severityCol}${activeCol}`));
391
+ logger.log(
392
+ chalk.gray(
393
+ '-'.repeat(
394
+ columns.key + columns.display + columns.domain + columns.severity + columns.active
395
+ )
396
+ )
397
+ );
398
+ }
399
+
400
+ function logEvidenceKindListRows(items, columns) {
401
+ items.forEach((d) => {
402
+ const key = (d?.key ?? '—').toString().padEnd(columns.key);
403
+ const displayName = (d?.displayName ?? '—').toString().padEnd(columns.display);
404
+ const domain = (d?.domainKey ?? '—').toString().padEnd(columns.domain);
405
+ const severity = (d?.defaultSeverity ?? '—').toString().padEnd(columns.severity);
406
+ const active = (d?.isActive === false ? 'false' : 'true').padEnd(columns.active);
407
+ logger.log(`${key}${displayName}${domain}${severity}${active}`);
408
+ });
409
+ }
410
+
411
+ function displayEvidenceKindList(items, environment, controllerUrl) {
412
+ logger.log(
413
+ chalk.bold(`\n📎 Evidence kinds in ${environment} environment (${controllerUrl}):\n`)
414
+ );
415
+ if (!Array.isArray(items) || items.length === 0) {
416
+ logger.log(chalk.gray(' No evidence kinds found.\n'));
417
+ return;
418
+ }
419
+ logEvidenceKindListHeader(EVIDENCE_KIND_LIST_COLUMNS);
420
+ logEvidenceKindListRows(items, EVIDENCE_KIND_LIST_COLUMNS);
421
+ logger.log('');
422
+ }
423
+
424
+ function setupEvidenceKindListCommand(cmd) {
425
+ cmd
426
+ .command('list')
427
+ .description('List evidence kinds')
428
+ .addHelpText('after', EVIDENCE_KIND_LIST_HELP_AFTER)
429
+ .option('--page <n>', 'Page', (v) => parseInt(v, 10))
430
+ .option('--page-size <n>', 'Page size', (v) => parseInt(v, 10))
431
+ .option('--search <text>', 'Search by key/displayName/description')
432
+ .option('--domain-key <domain>', 'Filter by domain key')
433
+ .action(async(options) => {
434
+ try {
435
+ const { environment, controllerUrl: headerControllerUrl } = await resolveHeaderContext();
436
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth(options);
437
+ const res = await listEvidenceKinds(controllerUrl, authConfig, {
438
+ page: options.page,
439
+ pageSize: options.pageSize,
440
+ search: options.search,
441
+ domainKey: options.domainKey
442
+ });
443
+ const payload = unwrapControllerData(res);
444
+ const items = payload?.data ?? payload ?? [];
445
+ displayEvidenceKindList(items, environment, headerControllerUrl);
446
+ } catch (e) {
447
+ logger.error(formatBlockingError(e.message));
448
+ process.exit(1);
449
+ }
450
+ });
451
+ }
452
+
453
+ /**
454
+ * @param {import('commander').Command} program
455
+ */
456
+ function setupEvidenceKindCommands(program) {
457
+ const cmd = program
458
+ .command('evidence-kind')
459
+ .description('Manage the Controller Evidence Kind Catalog')
460
+ .addHelpText('after', EVIDENCE_KIND_HELP_AFTER);
461
+ setupEvidenceKindCreateCommand(cmd);
462
+ setupEvidenceKindImportCommand(cmd);
463
+ setupEvidenceKindExportCommand(cmd);
464
+ setupEvidenceKindGetCommand(cmd);
465
+ setupEvidenceKindListCommand(cmd);
466
+ }
467
+
468
+ module.exports = {
469
+ setupEvidenceKindCommands,
470
+ requireEvidenceKindKey,
471
+ buildCreatePayload,
472
+ unwrapControllerData,
473
+ importEvidenceKindRows,
474
+ mapEvidenceKindFileToApiPayload,
475
+ mapEvidenceKindApiToFileRow,
476
+ displayEvidenceKindDetail,
477
+ formatEvidenceKindContributionLine
478
+ };
@@ -14,6 +14,7 @@ const { displayVerifyGovernanceTTY } = require('../lifecycle/report-display');
14
14
  const { exitCodeFromVerdict } = require('../lifecycle/exit-codes');
15
15
  const { buildJsonEnvelope } = require('../lifecycle/json-envelope');
16
16
  const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
17
+ const { cliOptsSkipPublish } = require('../utils/cli-sync-options');
17
18
 
18
19
  /**
19
20
  * Commander repeatable-option collector. Splits comma-separated values so both
@@ -31,11 +32,12 @@ function collectString(value, previous) {
31
32
  }
32
33
 
33
34
  function normalizeGovernanceOptions(options, cmd) {
34
- const parent = cmd && cmd.parent ? cmd.parent.opts() : {};
35
+ const parentCmd = cmd && cmd.parent ? cmd.parent : null;
36
+ const parent = parentCmd ? parentCmd.opts() : {};
35
37
  return {
36
38
  env: options.env || parent.env || 'dev',
37
39
  verbose: options.verbose === true || parent.verbose === true,
38
- noPublish: options.noPublish === true || parent.noPublish === true,
40
+ noPublish: cliOptsSkipPublish(options, cmd) || cliOptsSkipPublish(parent, parentCmd),
39
41
  force: options.force === true || parent.force === true,
40
42
  json: options.json === true || parent.json === true,
41
43
  pack: options.pack,
@@ -12,33 +12,21 @@ const {
12
12
  const { runWithVerifyStepProgress } = require('../lifecycle/verify-step-progress');
13
13
 
14
14
  /**
15
- * @async
16
- * @param {string} systemKey
15
+ * @param {string[]} targetKeys
16
+ * @param {Object} ctx
17
17
  * @param {Object} options
18
- * @returns {Promise<Object>}
18
+ * @returns {Promise<Array>}
19
19
  */
20
- async function runGovernanceGenerateForExternalSystem(systemKey, options = {}) {
21
- const subjects = buildSubjectsFromOptions(options);
22
- if (subjects.length === 0) {
23
- throw new Error(
24
- 'At least one --subject-user-id or --subject-email is required for governance generate'
25
- );
26
- }
27
-
28
- const { resolvedKey, keys, authConfig, dataplaneUrl } =
29
- await resolveGovernanceIntegrationContext(systemKey, options);
30
-
31
- const profile = (options.certificationProfile || 'bronze').trim().toLowerCase();
32
- const maxSamples = Number(options.maxSamplesPerPartition) || 25;
33
-
34
- const results = await runWithVerifyStepProgress(
20
+ async function generatePacksForDatasourceKeys(targetKeys, ctx, options) {
21
+ const { dataplaneUrl, authConfig, resolvedKey, subjects, profile, maxSamples } = ctx;
22
+ return runWithVerifyStepProgress(
35
23
  'Generating governance scenario packs',
36
24
  async progress => {
37
25
  const rows = [];
38
- for (let index = 0; index < keys.length; index += 1) {
39
- const dsKey = keys[index];
26
+ for (let index = 0; index < targetKeys.length; index += 1) {
27
+ const dsKey = targetKeys[index];
40
28
  if (typeof progress.setLabel === 'function') {
41
- progress.setLabel(`Generating pack: ${dsKey} (${index + 1}/${keys.length})`);
29
+ progress.setLabel(`Generating pack: ${dsKey} (${index + 1}/${targetKeys.length})`);
42
30
  }
43
31
  const row = await generateDatasourceGovernancePack(
44
32
  dataplaneUrl,
@@ -57,6 +45,53 @@ async function runGovernanceGenerateForExternalSystem(systemKey, options = {}) {
57
45
  },
58
46
  options
59
47
  );
48
+ }
49
+
50
+ /**
51
+ * @async
52
+ * @param {string} systemKey
53
+ * @param {Object} options
54
+ * @returns {Promise<Object>}
55
+ */
56
+ async function runGovernanceGenerateForExternalSystem(systemKey, options = {}) {
57
+ const subjects = buildSubjectsFromOptions(options);
58
+ if (subjects.length === 0) {
59
+ throw new Error(
60
+ 'At least one --subject-user-id or --subject-email is required for governance generate'
61
+ );
62
+ }
63
+
64
+ const { resolvedKey, keys, authConfig, dataplaneUrl } =
65
+ await resolveGovernanceIntegrationContext(systemKey, options);
66
+
67
+ const {
68
+ filterAbacRequiredDatasourceKeys
69
+ } = require('../lifecycle/access-control-abac');
70
+ const targetKeys = Array.isArray(options.datasourceKeys) && options.datasourceKeys.length
71
+ ? options.datasourceKeys
72
+ : filterAbacRequiredDatasourceKeys(resolvedKey, keys);
73
+
74
+ if (targetKeys.length === 0) {
75
+ return {
76
+ systemKey: resolvedKey,
77
+ command: 'governance generate',
78
+ generated: 0,
79
+ results: []
80
+ };
81
+ }
82
+
83
+ const results = await generatePacksForDatasourceKeys(
84
+ targetKeys,
85
+ {
86
+ dataplaneUrl,
87
+ authConfig,
88
+ resolvedKey,
89
+ subjects,
90
+ profile: (options.certificationProfile || 'bronze').trim().toLowerCase(),
91
+ maxSamples: Number(options.maxSamplesPerPartition) || 25
92
+ },
93
+ options
94
+ );
60
95
 
61
96
  return {
62
97
  systemKey: resolvedKey,
@@ -98,13 +98,18 @@ async function runLifecycleGovernanceCombinedIfNeeded(
98
98
  */
99
99
  async function runMissingVerifyStepsLocally(systemKey, report, options) {
100
100
  const runOpts = { ...options, noPublish: true };
101
- if (report.operations?.verdict !== VERDICT.VERIFIED) {
101
+ const pathsUtil = require('../utils/paths');
102
+ const appType = await pathsUtil.detectAppType(systemKey).catch(() => null);
103
+ const isRoleAssistantPackage = appType?.baseDir === 'role-assistant';
104
+ // verify-operations is integration/-only; Role Assistant packages use dataplane lifecycle report.
105
+ if (report.operations?.verdict !== VERDICT.VERIFIED && !isRoleAssistantPackage) {
102
106
  await runVerifyOperationsForExternalSystem(systemKey, runOpts);
103
107
  }
104
108
  if (report.trust?.verdict !== VERDICT.VERIFIED) {
105
109
  await runVerifyTrustForExternalSystem(systemKey, runOpts);
106
110
  }
107
- if (report.governance?.verdict !== VERDICT.VERIFIED) {
111
+ if (report.governance?.verdict !== VERDICT.VERIFIED
112
+ && report.governance?.verdict !== VERDICT.NOT_APPLICABLE) {
108
113
  if (lifecycleHasGovernanceSubjects(options)) {
109
114
  const { runVerifyGovernanceCombined } = require('./verify-governance-combined-external');
110
115
  await runVerifyGovernanceCombined(
@@ -243,9 +248,10 @@ async function executeLifecycleWork(systemKey, options = {}) {
243
248
  async function runLifecycleForExternalSystem(systemKey, options = {}) {
244
249
  const pathsUtil = require('../utils/paths');
245
250
  const appType = await pathsUtil.detectAppType(systemKey).catch(() => null);
246
- if (!appType || appType.baseDir !== 'integration') {
251
+ const allowedRoots = new Set(['integration', 'role-assistant']);
252
+ if (!appType || !allowedRoots.has(appType.baseDir)) {
247
253
  throw new Error(
248
- 'lifecycle applies to external integration folders only (integration/<systemKey>/).'
254
+ 'lifecycle applies to external authoring folders only (integration/<systemKey>/ or role-assistant/<roleKey>/).'
249
255
  );
250
256
  }
251
257
 
@@ -84,7 +84,16 @@ function applyBaseDatasourceRepairs(parsed, none, changes, options, helpers) {
84
84
  updated = helpers.repairMetadataSchemaFromAttributes(parsed, changes) || updated;
85
85
  const warnings = Array.isArray(options.warnings) ? options.warnings : [];
86
86
  updated = repairWebhookSection(parsed, changes, warnings) || updated;
87
- updated = repairSyncBridgeSection(parsed, changes, warnings) || updated;
87
+ updated = repairSyncBridgeSection(parsed, changes, warnings, options) || updated;
88
+ if (
89
+ Number.isInteger(options.businessOrder) &&
90
+ options.businessOrder >= 1 &&
91
+ (parsed.businessOrder === undefined || parsed.businessOrder === null)
92
+ ) {
93
+ parsed.businessOrder = options.businessOrder;
94
+ changes.push(`Set businessOrder ${options.businessOrder} (absent; datasource repair order)`);
95
+ updated = true;
96
+ }
88
97
  collectWebhookSyncBridgeSystemWarnings(parsed, options.systemParsed, warnings);
89
98
  return updated;
90
99
  }
@@ -30,7 +30,8 @@ function runDatasourceRepairs(appPath, datasourceFiles, options, dryRun, changes
30
30
  if (!datasourceFiles || datasourceFiles.length === 0) return false;
31
31
  let updated = false;
32
32
  const warnings = Array.isArray(context.warnings) ? context.warnings : [];
33
- for (const fileName of datasourceFiles) {
33
+ for (let i = 0; i < datasourceFiles.length; i += 1) {
34
+ const fileName = datasourceFiles[i];
34
35
  const filePath = path.join(appPath, fileName);
35
36
  if (!fs.existsSync(filePath)) continue;
36
37
  try {
@@ -41,7 +42,8 @@ function runDatasourceRepairs(appPath, datasourceFiles, options, dryRun, changes
41
42
  test: options.test,
42
43
  operations: options.operations,
43
44
  systemParsed: context.systemParsed,
44
- warnings
45
+ warnings,
46
+ businessOrder: i + 1
45
47
  });
46
48
  if (fileUpdated) {
47
49
  updated = true;