@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,450 @@
1
+ /**
2
+ * Role Assistant evidence import/export for dataplane evidence lifecycle.
3
+ *
4
+ * @fileoverview Sync local role-assistant evidence rows with dataplane evidence APIs
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const path = require('path');
12
+ const fs = require('../internal/fs-real-sync');
13
+ const yaml = require('js-yaml');
14
+ const logger = require('../utils/logger');
15
+ const { formatSuccessLine, formatWarningLine, metadata } = require('../utils/cli-test-layout-chalk');
16
+ const {
17
+ listEvidence,
18
+ loadEvidenceBulk,
19
+ validateEvidenceCandidate,
20
+ certifyEvidenceCandidate,
21
+ activateEvidenceCandidate
22
+ } = require('../api/evidence.api');
23
+ const { resolveExternalSystemPath } = require('../utils/app-path-resolve');
24
+ const { SYSTEM_KIND_DIGITAL_WORKER } = require('./settings');
25
+
26
+ const EVIDENCE_DIR_NAME = 'evidence';
27
+ const EVIDENCE_EXPORT_FILE = 'evidence.json';
28
+ const VALIDATION_FAILED = 'failed';
29
+ const VALIDATION_PASSED = 'passed';
30
+
31
+ const EVIDENCE_MUTABLE_KEYS = [
32
+ 'key',
33
+ 'resourceType',
34
+ 'roleKey',
35
+ 'kindKey',
36
+ 'kindVersion',
37
+ 'aspect',
38
+ 'knowledgeType',
39
+ 'originalLanguage',
40
+ 'structuredContent',
41
+ 'title',
42
+ 'description',
43
+ 'when',
44
+ 'expectedBehavior',
45
+ 'order',
46
+ 'status',
47
+ 'generationConfidence',
48
+ 'suggestionExplanation',
49
+ 'contributionOverrides',
50
+ 'approval'
51
+ ];
52
+
53
+ /** Read-only projections exported for Builder visibility (ignored on load). */
54
+ const EVIDENCE_EXPORT_PROJECTION_KEYS = [
55
+ 'valueProposal',
56
+ 'contributionProjection',
57
+ 'fingerprint',
58
+ 'provenanceSummary',
59
+ 'trustProjections'
60
+ ];
61
+
62
+ function resolveSystemNode(input) {
63
+ if (!input || typeof input !== 'object') {
64
+ return null;
65
+ }
66
+ return input.system || input.application || null;
67
+ }
68
+
69
+ function isDigitalWorkerModel(input) {
70
+ const systemNode = resolveSystemNode(input);
71
+ if (!systemNode || typeof systemNode.systemKind !== 'string') {
72
+ return false;
73
+ }
74
+ return systemNode.systemKind.trim() === SYSTEM_KIND_DIGITAL_WORKER;
75
+ }
76
+
77
+ function isEvidenceFileName(fileName) {
78
+ const lower = String(fileName || '').toLowerCase();
79
+ return lower.endsWith('.json') || lower.endsWith('.yaml') || lower.endsWith('.yml');
80
+ }
81
+
82
+ function collectEvidenceFiles(dirPath) {
83
+ if (!fs.existsSync(dirPath)) {
84
+ return [];
85
+ }
86
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
87
+ const files = [];
88
+ for (const entry of entries) {
89
+ const fullPath = path.join(dirPath, entry.name);
90
+ if (entry.isDirectory()) {
91
+ files.push(...collectEvidenceFiles(fullPath));
92
+ continue;
93
+ }
94
+ if (isEvidenceFileName(entry.name)) {
95
+ files.push(fullPath);
96
+ }
97
+ }
98
+ return files.sort();
99
+ }
100
+
101
+ function parseEvidenceFile(filePath) {
102
+ const raw = fs.readFileSync(filePath, 'utf8');
103
+ if (filePath.toLowerCase().endsWith('.json')) {
104
+ return JSON.parse(raw);
105
+ }
106
+ return yaml.load(raw);
107
+ }
108
+
109
+ function extractEvidenceRows(payload) {
110
+ if (Array.isArray(payload)) {
111
+ return payload;
112
+ }
113
+ if (!payload || typeof payload !== 'object') {
114
+ return [];
115
+ }
116
+ if (Array.isArray(payload.data)) {
117
+ return payload.data;
118
+ }
119
+ if (Array.isArray(payload.rows)) {
120
+ return payload.rows;
121
+ }
122
+ if (Array.isArray(payload.evidence)) {
123
+ return payload.evidence;
124
+ }
125
+ return [payload];
126
+ }
127
+
128
+ function sanitizeEvidenceRow(row, systemKey, options = {}) {
129
+ const includeProjections = options.includeProjections === true;
130
+ const out = {};
131
+ const keys = includeProjections
132
+ ? [...EVIDENCE_MUTABLE_KEYS, ...EVIDENCE_EXPORT_PROJECTION_KEYS]
133
+ : EVIDENCE_MUTABLE_KEYS;
134
+ for (const key of keys) {
135
+ if (typeof row[key] !== 'undefined') {
136
+ out[key] = row[key];
137
+ }
138
+ }
139
+ if (typeof out.roleKey !== 'string' || !out.roleKey.trim()) {
140
+ out.roleKey = systemKey;
141
+ }
142
+ // Import path: clients cannot load status=active; lifecycle activates after certify.
143
+ if (!includeProjections) {
144
+ out.status = 'candidate';
145
+ }
146
+ if (!out.title || !out.when || !out.expectedBehavior) {
147
+ return null;
148
+ }
149
+ return out;
150
+ }
151
+
152
+ function loadEvidenceRowsFromFolder(evidencePath, systemKey) {
153
+ const files = collectEvidenceFiles(evidencePath);
154
+ const rows = [];
155
+ for (const filePath of files) {
156
+ const parsed = parseEvidenceFile(filePath);
157
+ const extractedRows = extractEvidenceRows(parsed);
158
+ for (const row of extractedRows) {
159
+ if (!row || typeof row !== 'object' || Array.isArray(row)) {
160
+ continue;
161
+ }
162
+ const normalized = sanitizeEvidenceRow(row, systemKey);
163
+ if (!normalized) {
164
+ const shortName = path.relative(evidencePath, filePath);
165
+ throw new Error(
166
+ `Evidence row in ${shortName} is missing required fields title/when/expectedBehavior.`
167
+ );
168
+ }
169
+ rows.push(normalized);
170
+ }
171
+ }
172
+ return { files, rows };
173
+ }
174
+
175
+ function resolveEvidenceDirectory(systemKey, pathOptions = {}) {
176
+ const appPath = resolveExternalSystemPath(systemKey, pathOptions);
177
+ return path.join(appPath, EVIDENCE_DIR_NAME);
178
+ }
179
+
180
+ function normalizeEvidenceRef(row) {
181
+ if (!row || typeof row !== 'object') {
182
+ return '';
183
+ }
184
+ const candidates = [row.id, row.key];
185
+ for (const candidate of candidates) {
186
+ if (typeof candidate === 'string' && candidate.trim()) {
187
+ return candidate.trim();
188
+ }
189
+ }
190
+ return '';
191
+ }
192
+
193
+ function toMatchKey(row) {
194
+ return `${String(row.title || '').trim()}|${String(row.when || '').trim()}|${String(row.expectedBehavior || '').trim()}`;
195
+ }
196
+
197
+ function dedupeRefs(refs) {
198
+ const out = [];
199
+ const seen = new Set();
200
+ for (const ref of refs) {
201
+ if (!ref || seen.has(ref)) {
202
+ continue;
203
+ }
204
+ seen.add(ref);
205
+ out.push(ref);
206
+ }
207
+ return out;
208
+ }
209
+
210
+ function resolveRefsFromLoadedRows(rows) {
211
+ return dedupeRefs((rows || []).map((row) => normalizeEvidenceRef(row)).filter(Boolean));
212
+ }
213
+
214
+ function buildRoleEvidenceIndex(rows) {
215
+ const byKey = new Map();
216
+ for (const row of rows || []) {
217
+ if (!row || typeof row !== 'object') {
218
+ continue;
219
+ }
220
+ const ref = normalizeEvidenceRef(row);
221
+ if (!ref) {
222
+ continue;
223
+ }
224
+ const matchKey = toMatchKey(row);
225
+ if (matchKey !== '||' && !byKey.has(matchKey)) {
226
+ byKey.set(matchKey, ref);
227
+ }
228
+ }
229
+ return byKey;
230
+ }
231
+
232
+ async function resolveEvidenceLifecycleRefs(dataplaneUrl, authConfig, systemKey, loadedRows, preparedRows) {
233
+ const refs = resolveRefsFromLoadedRows(loadedRows);
234
+ if (refs.length >= preparedRows.length || preparedRows.length === 0) {
235
+ return refs;
236
+ }
237
+ const roleRows = await listEvidence(dataplaneUrl, authConfig, { roleKey: systemKey });
238
+ const roleByKey = buildRoleEvidenceIndex(roleRows);
239
+ for (const row of preparedRows) {
240
+ const matchRef = roleByKey.get(toMatchKey(row));
241
+ if (matchRef) {
242
+ refs.push(matchRef);
243
+ }
244
+ }
245
+ return dedupeRefs(refs);
246
+ }
247
+
248
+ function validationFailed(validationResponse) {
249
+ const status = String(validationResponse?.status || '').trim().toLowerCase();
250
+ return status === VALIDATION_FAILED;
251
+ }
252
+
253
+ function isActivatedEvidenceRow(row) {
254
+ const status = String(row?.status || '').trim().toLowerCase();
255
+ return !status || status === 'active';
256
+ }
257
+
258
+ async function runEvidenceLifecycleForRef(dataplaneUrl, authConfig, evidenceRef) {
259
+ const validation = await validateEvidenceCandidate(dataplaneUrl, authConfig, evidenceRef);
260
+ if (validationFailed(validation)) {
261
+ const issues = Array.isArray(validation?.issues) ? validation.issues.length : 0;
262
+ throw new Error(`Validation failed for evidence ${evidenceRef} (${issues} issue${issues === 1 ? '' : 's'}).`);
263
+ }
264
+ if (String(validation?.status || '').trim().toLowerCase() !== VALIDATION_PASSED) {
265
+ throw new Error(`Validation status for evidence ${evidenceRef} is "${validation?.status || 'unknown'}".`);
266
+ }
267
+ await certifyEvidenceCandidate(dataplaneUrl, authConfig, evidenceRef, {});
268
+ const activated = await activateEvidenceCandidate(dataplaneUrl, authConfig, evidenceRef);
269
+ if (!isActivatedEvidenceRow(activated)) {
270
+ throw new Error(`Evidence ${evidenceRef} did not become active after activation.`);
271
+ }
272
+ }
273
+
274
+ function buildFailureSummary(failures) {
275
+ const preview = failures.slice(0, 3).join('; ');
276
+ const more = failures.length > 3 ? ` (and ${failures.length - 3} more)` : '';
277
+ return `${preview}${more}`;
278
+ }
279
+
280
+ function buildEvidenceDryRunSummary(evidencePath, files, rows) {
281
+ return {
282
+ skipped: false,
283
+ dryRun: true,
284
+ evidencePath,
285
+ filesDiscovered: files.length,
286
+ discovered: rows.length
287
+ };
288
+ }
289
+
290
+ function buildEvidenceImportSummary(evidencePath, files, rows, loadResult) {
291
+ return {
292
+ skipped: false,
293
+ evidencePath,
294
+ filesDiscovered: files.length,
295
+ discovered: rows.length,
296
+ loaded: Number(loadResult.inserted || 0) + Number(loadResult.updated || 0),
297
+ validated: 0,
298
+ certified: 0,
299
+ productionReady: 0,
300
+ failed: 0
301
+ };
302
+ }
303
+
304
+ async function loadEvidenceAndResolveRefs(dataplaneUrl, authConfig, systemKey, rows) {
305
+ const loadResult = await loadEvidenceBulk(dataplaneUrl, authConfig, rows, {});
306
+ const refs = await resolveEvidenceLifecycleRefs(dataplaneUrl, authConfig, systemKey, loadResult.data, rows);
307
+ return { loadResult, refs };
308
+ }
309
+
310
+ async function runEvidenceLifecycleBatch(dataplaneUrl, authConfig, refs, summary) {
311
+ const failures = [];
312
+ for (const evidenceRef of refs) {
313
+ try {
314
+ await runEvidenceLifecycleForRef(dataplaneUrl, authConfig, evidenceRef);
315
+ summary.validated += 1;
316
+ summary.certified += 1;
317
+ summary.productionReady += 1;
318
+ } catch (error) {
319
+ summary.failed += 1;
320
+ failures.push(error.message || String(error));
321
+ }
322
+ }
323
+ return failures;
324
+ }
325
+
326
+ function appendUnresolvedEvidenceFailures(summary, failures, refCount, rowCount) {
327
+ if (refCount >= rowCount) {
328
+ return;
329
+ }
330
+ const unresolved = rowCount - refCount;
331
+ summary.failed += unresolved;
332
+ failures.push(`Could not resolve ${unresolved} evidence row(s) to dataplane ids after load.`);
333
+ }
334
+
335
+ function throwWhenEvidenceLifecycleFailed(summary, failures) {
336
+ if (summary.failed === 0) {
337
+ return;
338
+ }
339
+ const err = new Error(`Role assistant evidence lifecycle failed: ${buildFailureSummary(failures)}`);
340
+ err.evidenceSummary = summary;
341
+ throw err;
342
+ }
343
+
344
+ async function importRoleAssistantEvidence(params) {
345
+ const { systemKey, dataplaneUrl, authConfig, model, pathOptions = {}, dryRun = false } = params;
346
+ if (!isDigitalWorkerModel(model)) {
347
+ return { skipped: true, reason: 'not-role-assistant' };
348
+ }
349
+ const evidencePath = resolveEvidenceDirectory(systemKey, pathOptions);
350
+ const { files, rows } = loadEvidenceRowsFromFolder(evidencePath, systemKey);
351
+ if (rows.length === 0) {
352
+ return { skipped: true, reason: 'no-evidence-files', evidencePath };
353
+ }
354
+ if (dryRun) {
355
+ return buildEvidenceDryRunSummary(evidencePath, files, rows);
356
+ }
357
+ const { loadResult, refs } = await loadEvidenceAndResolveRefs(
358
+ dataplaneUrl,
359
+ authConfig,
360
+ systemKey,
361
+ rows
362
+ );
363
+ const summary = buildEvidenceImportSummary(evidencePath, files, rows, loadResult);
364
+ const failures = await runEvidenceLifecycleBatch(dataplaneUrl, authConfig, refs, summary);
365
+ appendUnresolvedEvidenceFailures(summary, failures, refs.length, rows.length);
366
+ throwWhenEvidenceLifecycleFailed(summary, failures);
367
+ return summary;
368
+ }
369
+
370
+ function sortEvidenceRows(rows) {
371
+ return [...rows].sort((left, right) => {
372
+ const titleCmp = String(left.title || '').localeCompare(String(right.title || ''));
373
+ if (titleCmp !== 0) {
374
+ return titleCmp;
375
+ }
376
+ return Number(left.order || 0) - Number(right.order || 0);
377
+ });
378
+ }
379
+
380
+ async function exportRoleAssistantEvidence(params) {
381
+ const { systemKey, dataplaneUrl, authConfig, manifest, pathOptions = {} } = params;
382
+ const model = { system: manifest?.application, dataSources: manifest?.dataSources || [] };
383
+ if (!isDigitalWorkerModel(model)) {
384
+ return { skipped: true, reason: 'not-role-assistant' };
385
+ }
386
+ const rows = await listEvidence(dataplaneUrl, authConfig, { roleKey: systemKey });
387
+ const normalized = sortEvidenceRows(
388
+ rows
389
+ .map((row) => sanitizeEvidenceRow(row, systemKey, { includeProjections: true }))
390
+ .filter(Boolean)
391
+ );
392
+ if (normalized.length === 0) {
393
+ return { skipped: true, reason: 'no-evidence-rows' };
394
+ }
395
+ const evidencePath = resolveEvidenceDirectory(systemKey, pathOptions);
396
+ fs.mkdirSync(evidencePath, { recursive: true });
397
+ const outPath = path.join(evidencePath, EVIDENCE_EXPORT_FILE);
398
+ fs.writeFileSync(outPath, `${JSON.stringify(normalized, null, 2)}\n`, 'utf8');
399
+ return {
400
+ skipped: false,
401
+ count: normalized.length,
402
+ evidencePath,
403
+ filePath: outPath
404
+ };
405
+ }
406
+
407
+ function logRoleAssistantEvidenceImportSummary(systemKey, result, options = {}) {
408
+ if (result.skipped) {
409
+ if (options.verbose === true) {
410
+ logger.log(metadata(`Role assistant evidence skipped (${systemKey}): ${result.reason}`));
411
+ }
412
+ return;
413
+ }
414
+ if (result.dryRun) {
415
+ logger.log(
416
+ formatWarningLine(
417
+ `Role assistant evidence dry-run: ${result.discovered} row(s) from ${result.filesDiscovered} file(s).`
418
+ )
419
+ );
420
+ return;
421
+ }
422
+ logger.log(
423
+ formatSuccessLine(
424
+ `Role assistant evidence applied: discovered ${result.discovered}, loaded ${result.loaded}, `
425
+ + `validated ${result.validated}, certified ${result.certified}, production-ready ${result.productionReady}.`
426
+ )
427
+ );
428
+ }
429
+
430
+ function logRoleAssistantEvidenceExportSummary(systemKey, result, options = {}) {
431
+ if (result.skipped) {
432
+ if (options.verbose === true) {
433
+ logger.log(metadata(`Role assistant evidence export skipped (${systemKey}): ${result.reason}`));
434
+ }
435
+ return;
436
+ }
437
+ logger.log(
438
+ formatSuccessLine(
439
+ `Role assistant evidence exported (${systemKey}): ${result.count} row(s) to ${result.filePath}.`
440
+ )
441
+ );
442
+ }
443
+
444
+ module.exports = {
445
+ isDigitalWorkerModel,
446
+ importRoleAssistantEvidence,
447
+ exportRoleAssistantEvidence,
448
+ logRoleAssistantEvidenceImportSummary,
449
+ logRoleAssistantEvidenceExportSummary
450
+ };
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Role Assistant GitHub workflow scaffolding.
3
+ *
4
+ * @fileoverview Generates a promotion workflow for role-assistant packages
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const fsSync = require('../internal/fs-real-sync');
12
+ const path = require('path');
13
+ const { resolveExternalSystemPath } = require('../utils/app-path-resolve');
14
+
15
+ const WORKFLOW_FILE_NAME = 'role-assistant-promote.yml';
16
+
17
+ function buildRoleAssistantWorkflowContent(systemKey) {
18
+ return `name: Role Assistant Promotion
19
+
20
+ on:
21
+ workflow_dispatch:
22
+ inputs:
23
+ environment:
24
+ description: "Target environment"
25
+ required: true
26
+ type: choice
27
+ options:
28
+ - dev
29
+ - tst
30
+ - pro
31
+ default: dev
32
+
33
+ jobs:
34
+ promote:
35
+ runs-on: ubuntu-latest
36
+ steps:
37
+ - name: Checkout repository
38
+ uses: actions/checkout@v4
39
+
40
+ - name: Setup Node.js
41
+ uses: actions/setup-node@v4
42
+ with:
43
+ node-version: "20"
44
+
45
+ - name: Install AI Fabrix Builder
46
+ run: npm install -g @aifabrix/builder
47
+
48
+ - name: Login to AI Fabrix
49
+ run: |
50
+ set -e
51
+ aifabrix login \\
52
+ --method credentials \\
53
+ --app ${systemKey} \\
54
+ --client-id \${{ secrets.AIFABRIX_CLIENT_ID }} \\
55
+ --client-secret \${{ secrets.AIFABRIX_CLIENT_SECRET }} \\
56
+ --controller \${{ secrets.AIFABRIX_CONTROLLER_URL }} \\
57
+ --environment \${{ inputs.environment }}
58
+
59
+ - name: Validate role assistant package
60
+ run: |
61
+ set -e
62
+ aifabrix validate ${systemKey}
63
+
64
+ - name: Upload role assistant package
65
+ run: |
66
+ set -e
67
+ aifabrix upload ${systemKey} --env \${{ inputs.environment }}
68
+
69
+ - name: Run role assistant lifecycle test
70
+ run: |
71
+ set -e
72
+ aifabrix role-assistant test ${systemKey} --env \${{ inputs.environment }} --json
73
+ `;
74
+ }
75
+
76
+ function resolveRoleAssistantWorkflowPath(systemKey, options = {}) {
77
+ const rootDir = options.roleAssistantPath || resolveExternalSystemPath(systemKey, options.pathOptions || {});
78
+ return path.join(rootDir, '.github', 'workflows', WORKFLOW_FILE_NAME);
79
+ }
80
+
81
+ async function ensureRoleAssistantGithubWorkflow(systemKey, options = {}) {
82
+ const workflowPath = resolveRoleAssistantWorkflowPath(systemKey, options);
83
+ if (!options.force && fsSync.existsSync(workflowPath)) {
84
+ return { created: false, workflowPath, reason: 'already-exists' };
85
+ }
86
+ fsSync.mkdirSync(path.dirname(workflowPath), { recursive: true });
87
+ fsSync.writeFileSync(workflowPath, buildRoleAssistantWorkflowContent(systemKey), 'utf8');
88
+ return { created: true, workflowPath };
89
+ }
90
+
91
+ module.exports = {
92
+ WORKFLOW_FILE_NAME,
93
+ buildRoleAssistantWorkflowContent,
94
+ resolveRoleAssistantWorkflowPath,
95
+ ensureRoleAssistantGithubWorkflow
96
+ };