@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
@@ -12,22 +12,123 @@ const { unwrapPublicationResult, isPublicationUnchanged } = require('../utils/ex
12
12
  const { logUploadReadinessSummary } = require('../utils/external-system-readiness-display');
13
13
  const { isQuietMinimalSync } = require('../utils/upload-sync-options');
14
14
 
15
+ function buildRoleAssistantModel(payload) {
16
+ return {
17
+ application: payload?.application,
18
+ dataSources: payload?.dataSources || []
19
+ };
20
+ }
21
+
22
+ function buildRoleAssistantSyncContext(ctx, model) {
23
+ // Controller deploy env (e.g. miso) ≠ role-assistant.yaml section (dev|tst|pro).
24
+ const { resolveRoleAssistantSettingsEnvKey } = require('../role-assistant/knowledge-sync');
25
+ const raEnv = resolveRoleAssistantSettingsEnvKey(
26
+ (ctx.options && (ctx.options.env || ctx.options.roleAssistantEnv)) || 'dev'
27
+ );
28
+ return {
29
+ systemKey: ctx.systemKey,
30
+ dataplaneUrl: ctx.dataplaneUrl,
31
+ authConfig: ctx.authConfig,
32
+ model,
33
+ environment: raEnv,
34
+ pathOptions: ctx.options.pathOptions || {},
35
+ dryRun: ctx.options.dryRun === true
36
+ };
37
+ }
38
+
39
+ async function runGovernanceSidecar(ctx, sidecar, quietSidecar) {
40
+ try {
41
+ const { uploadGovernanceScenarioPacks } = require('../lifecycle/scenario-upload');
42
+ const uploaded = await uploadGovernanceScenarioPacks(ctx.systemKey, ctx.dataplaneUrl, ctx.authConfig, {
43
+ silent: quietSidecar
44
+ });
45
+ sidecar.governance = { uploaded: Number(uploaded || 0) };
46
+ } catch (err) {
47
+ sidecar.governance = { skipped: true, reason: err.message || String(err) };
48
+ if (!quietSidecar) {
49
+ logger.log(formatWarningLine(`Governance scenario upload skipped: ${err.message}`));
50
+ }
51
+ }
52
+ }
53
+
54
+ async function runEvidenceSidecar(ctx, model, sidecar, quietSidecar) {
55
+ const { importRoleAssistantEvidence, logRoleAssistantEvidenceImportSummary } = require('../role-assistant/evidence-sync');
56
+ const result = await importRoleAssistantEvidence(buildRoleAssistantSyncContext(ctx, model));
57
+ sidecar.evidence = result;
58
+ if (!quietSidecar) {
59
+ logRoleAssistantEvidenceImportSummary(ctx.systemKey, result, { verbose: ctx.options.verbose === true });
60
+ }
61
+ }
62
+
63
+ async function runKnowledgeSidecar(ctx, model, sidecar, quietSidecar) {
64
+ const {
65
+ importRoleAssistantKnowledge,
66
+ logKnowledgeImportSummary,
67
+ pushRoleAssistantDocumentStorageSettings,
68
+ logDocumentStorageSettingsPushSummary
69
+ } = require('../role-assistant/knowledge-sync');
70
+ const syncCtx = buildRoleAssistantSyncContext(ctx, model);
71
+ try {
72
+ const settingsResult = await pushRoleAssistantDocumentStorageSettings(syncCtx);
73
+ sidecar.documentStorageSettings = settingsResult;
74
+ if (!quietSidecar) {
75
+ logDocumentStorageSettingsPushSummary(ctx.systemKey, settingsResult, {
76
+ verbose: ctx.options.verbose === true
77
+ });
78
+ }
79
+ } catch (err) {
80
+ sidecar.documentStorageSettings = {
81
+ skipped: true,
82
+ reason: err.message || String(err)
83
+ };
84
+ if (!quietSidecar) {
85
+ logger.log(
86
+ formatWarningLine(
87
+ `Knowledge document storage settings update failed: ${err.message}`
88
+ )
89
+ );
90
+ }
91
+ throw err;
92
+ }
93
+ try {
94
+ const result = await importRoleAssistantKnowledge(syncCtx);
95
+ sidecar.knowledge = result;
96
+ if (!quietSidecar) {
97
+ logKnowledgeImportSummary(ctx.systemKey, result, { verbose: ctx.options.verbose === true });
98
+ }
99
+ } catch (err) {
100
+ sidecar.knowledge = { skipped: true, reason: err.message || String(err) };
101
+ if (!quietSidecar) {
102
+ logger.log(formatWarningLine(`Role assistant knowledge sync skipped: ${err.message}`));
103
+ }
104
+ }
105
+ }
106
+
15
107
  /**
16
108
  * Sidecar follow-ups that are independent of pipeline publish hash (scenario packs).
17
109
  * @param {Object} ctx
18
110
  * @returns {Promise<void>}
19
111
  */
20
112
  async function runUploadSidecarFollowups(ctx) {
21
- const { systemKey, options, dataplaneUrl, authConfig } = ctx;
22
- const quietSidecar = isQuietMinimalSync(options);
113
+ const sidecar = {
114
+ governance: null,
115
+ evidence: null,
116
+ knowledge: null,
117
+ documentStorageSettings: null
118
+ };
119
+ const quietSidecar = isQuietMinimalSync(ctx.options);
120
+ const model = buildRoleAssistantModel(ctx.payload);
121
+ await runGovernanceSidecar(ctx, sidecar, quietSidecar);
23
122
  try {
24
- const { uploadGovernanceScenarioPacks } = require('../lifecycle/scenario-upload');
25
- await uploadGovernanceScenarioPacks(systemKey, dataplaneUrl, authConfig, { silent: quietSidecar });
123
+ await runEvidenceSidecar(ctx, model, sidecar, quietSidecar);
26
124
  } catch (err) {
27
- if (!quietSidecar) {
28
- logger.log(formatWarningLine(`Governance scenario upload skipped: ${err.message}`));
125
+ if (err && err.evidenceSummary) {
126
+ sidecar.evidence = err.evidenceSummary;
29
127
  }
128
+ throw err;
30
129
  }
130
+ await runKnowledgeSidecar(ctx, model, sidecar, quietSidecar);
131
+ return sidecar;
31
132
  }
32
133
 
33
134
  /**
@@ -54,61 +155,57 @@ async function resolveUploadProbeData(ctx, maybeRunUploadProbeFn) {
54
155
  return { probeData: null, compact };
55
156
  }
56
157
 
158
+ function logUploadSummary(ctx, publication, probeData) {
159
+ if (ctx.options.suppressSummary === true) {
160
+ return;
161
+ }
162
+ logUploadReadinessSummary({
163
+ environment: ctx.environment,
164
+ dataplaneUrl: ctx.dataplaneUrl,
165
+ systemKey: ctx.systemKey,
166
+ publication,
167
+ manifest: ctx.manifest,
168
+ minimal: !!ctx.options.minimal,
169
+ willProbe: false,
170
+ credentialPush: ctx.credentialPush,
171
+ configPush: ctx.configPush,
172
+ forcePublish: ctx.options.force === true,
173
+ probeData
174
+ });
175
+ }
176
+
177
+ async function runProbeAfterSummary(ctx, maybeRunUploadProbe, compact) {
178
+ if (ctx.options.probe && !compact) {
179
+ await maybeRunUploadProbe(ctx.dataplaneUrl, ctx.systemKey, ctx.authConfig, ctx.options.probeTimeout, {
180
+ compact: false
181
+ });
182
+ }
183
+ }
184
+
185
+ async function runSidecarAndWrap(ctx) {
186
+ const sidecar = await runUploadSidecarFollowups(ctx);
187
+ return { sidecar };
188
+ }
189
+
57
190
  /**
58
191
  * @param {Object} ctx
59
192
  * @param {Function} maybeRunUploadProbe
60
193
  * @returns {Promise<void>}
61
194
  */
62
195
  async function handlePublicationAndFollowups(ctx, maybeRunUploadProbe) {
63
- const {
64
- systemKey,
65
- options,
66
- manifest,
67
- payload,
68
- environment,
69
- dataplaneUrl,
70
- authConfig,
71
- rawRes,
72
- credentialPush,
73
- configPush
74
- } = ctx;
75
- const publication = unwrapPublicationResult(rawRes);
196
+ const publication = unwrapPublicationResult(ctx.rawRes);
76
197
  if (!publication) {
77
198
  throw new Error(
78
199
  'Unexpected response from dataplane upload: missing publication result (uploadId/system/datasources).'
79
200
  );
80
201
  }
81
202
  if (isPublicationUnchanged(publication)) {
82
- await runUploadSidecarFollowups({ systemKey, options, payload, dataplaneUrl, authConfig });
83
- return;
203
+ return runSidecarAndWrap(ctx);
84
204
  }
85
-
86
- const suppressSummary = options.suppressSummary === true;
87
205
  const { probeData, compact } = await resolveUploadProbeData(ctx, maybeRunUploadProbe);
88
-
89
- if (!suppressSummary) {
90
- logUploadReadinessSummary({
91
- environment,
92
- dataplaneUrl,
93
- systemKey,
94
- publication,
95
- manifest,
96
- minimal: !!options.minimal,
97
- willProbe: false,
98
- credentialPush,
99
- configPush,
100
- forcePublish: options.force === true,
101
- probeData
102
- });
103
- }
104
-
105
- if (options.probe && !compact) {
106
- await maybeRunUploadProbe(dataplaneUrl, systemKey, authConfig, options.probeTimeout, {
107
- compact: false
108
- });
109
- }
110
-
111
- await runUploadSidecarFollowups({ systemKey, options, payload, dataplaneUrl, authConfig });
206
+ logUploadSummary(ctx, publication, probeData);
207
+ await runProbeAfterSummary(ctx, maybeRunUploadProbe, compact);
208
+ return runSidecarAndWrap(ctx);
112
209
  }
113
210
 
114
211
  module.exports = {
@@ -47,6 +47,12 @@ const { syncDeployJsonFromSources } = require('../external-system/sync-deploy-ma
47
47
  const { handlePublicationAndFollowups } = require('./upload-sidecar-followups');
48
48
  const { finalizeUploadPublishResult, finishUnchangedUploadPublication } = require('./upload-data-sync-finalize');
49
49
  const { pushAndLogCredentialSecrets } = require('./upload-credential-push-log');
50
+ const {
51
+ SYSTEM_KIND_DIGITAL_WORKER,
52
+ readRoleAssistantSettings,
53
+ resolveRoleAssistantSettingsVersion,
54
+ resolveRoleAssistantEnvironmentSettings
55
+ } = require('../role-assistant/settings');
50
56
 
51
57
  /**
52
58
  * Validates system-key format (same as download).
@@ -123,14 +129,24 @@ async function resolveDataplaneAndAuth(systemKey, opts = {}) {
123
129
  * @param {string} dataplaneUrl - Dataplane base URL
124
130
  * @param {Object} authConfig - Auth config
125
131
  * @param {Object} payload - { version, application, dataSources, status: "draft" }
132
+ * @param {string} systemKey
133
+ * @param {Object} [options]
134
+ * @param {number|string} [options.timeoutMs]
126
135
  * @returns {Promise<Object>} Raw API response envelope
127
136
  */
128
- async function runUploadValidatePublish(dataplaneUrl, authConfig, payload, systemKey) {
137
+ async function runUploadValidatePublish(
138
+ dataplaneUrl,
139
+ authConfig,
140
+ payload,
141
+ systemKey,
142
+ options = {}
143
+ ) {
129
144
  const res = await uploadApplicationViaPipelineWithAuthRetry(
130
145
  dataplaneUrl,
131
146
  authConfig,
132
147
  payload,
133
- systemKey
148
+ systemKey,
149
+ { timeoutMs: options.timeoutMs }
134
150
  );
135
151
  if (res?.success === false) {
136
152
  throw new Error(formatApiError(res, dataplaneUrl));
@@ -231,6 +247,14 @@ async function buildValidatedUploadManifestPayload(systemKey, opts = {}) {
231
247
  force: opts.force === true,
232
248
  status: opts.uploadStatus === 'published' ? 'published' : 'draft'
233
249
  });
250
+ if (String(manifest?.system?.systemKind || '').trim() === SYSTEM_KIND_DIGITAL_WORKER) {
251
+ const settings = readRoleAssistantSettings(systemKey, { required: true, pathOptions });
252
+ resolveRoleAssistantEnvironmentSettings(settings, opts.env || 'dev', { required: true });
253
+ const settingsVersion = resolveRoleAssistantSettingsVersion(settings);
254
+ if (settingsVersion) {
255
+ payload.version = settingsVersion;
256
+ }
257
+ }
234
258
  await resolvePayloadConfiguration(systemKey, payload, pathOptions);
235
259
  return { manifest, payload };
236
260
  }
@@ -264,7 +288,10 @@ async function logAndSyncLocalOpenApiForUpload(systemKey, manifest, uploadContex
264
288
  systemKey,
265
289
  datasourceFiles,
266
290
  dataplaneUrl: uploadContext.dataplaneUrl,
267
- authConfig: uploadContext.authConfig
291
+ authConfig: uploadContext.authConfig,
292
+ // Always replace SpecFile rows keyed by openapi.documentKey so vendor/full
293
+ // specs are not left missing while only thin internal:* slices remain.
294
+ force: true
268
295
  });
269
296
  if (!uploadContext.quiet) {
270
297
  for (const line of openapiSyncLines) {
@@ -293,7 +320,7 @@ async function completeUploadAfterPublish(systemKey, options, manifest, payload,
293
320
  pathOptions: options.pathOptions,
294
321
  quiet: quietCompact
295
322
  });
296
- await handlePublicationAndFollowups(
323
+ const followupResult = await handlePublicationAndFollowups(
297
324
  {
298
325
  systemKey,
299
326
  options,
@@ -309,7 +336,13 @@ async function completeUploadAfterPublish(systemKey, options, manifest, payload,
309
336
  maybeRunUploadProbe
310
337
  );
311
338
  return finalizeUploadPublishResult(
312
- { dataplaneUrl: ctx.dataplaneUrl, authConfig: ctx.authConfig, environment: ctx.environment, uploadSkipped: false },
339
+ {
340
+ dataplaneUrl: ctx.dataplaneUrl,
341
+ authConfig: ctx.authConfig,
342
+ environment: ctx.environment,
343
+ uploadSkipped: false,
344
+ sidecar: followupResult?.sidecar || null
345
+ },
313
346
  payload,
314
347
  options
315
348
  );
@@ -331,7 +364,10 @@ async function runUploadPublishAndSummary(systemKey, options, manifest, payload)
331
364
  payload
332
365
  );
333
366
  const rawRes = await runWithUploadPipelineSpinner(
334
- async() => runUploadValidatePublish(dataplaneUrl, authConfig, payload, systemKey),
367
+ async() =>
368
+ runUploadValidatePublish(dataplaneUrl, authConfig, payload, systemKey, {
369
+ timeoutMs: options.timeoutMs
370
+ }),
335
371
  {
336
372
  systemKey,
337
373
  json: options.json === true,
@@ -367,6 +403,7 @@ async function runUploadPublishAndSummary(systemKey, options, manifest, payload)
367
403
  * @param {boolean} [options.verbose] - Expanded summary and server-side pipeline validate warnings
368
404
  * @param {boolean} [options.probe] - POST validation/run after publish
369
405
  * @param {number} [options.probeTimeout] - Probe timeout ms (default 120000)
406
+ * @param {number} [options.timeoutMs] - Pipeline publish timeout ms (HTTP + dataplane; default 300000)
370
407
  * @param {boolean} [options.minimal] - Short summary (default: compact unless --verbose)
371
408
  * @returns {Promise<{ dataplaneUrl: string, authConfig: Object, environment: string }|void>}
372
409
  * @throws {Error} If validation or API calls fail
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Offline validate then write *-deploy.json (same quality as validate + same write as json).
3
+ *
4
+ * @fileoverview aifabrix validate-json command
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const fs = require('fs');
12
+ const chalk = require('chalk');
13
+ const logger = require('../utils/logger');
14
+ const validate = require('../validation/validate');
15
+ const generator = require('../generator');
16
+ const { listIntegrationAppNames, listBuilderAppNames } = require('../utils/paths');
17
+ const {
18
+ formatSuccessLine,
19
+ formatBlockingError,
20
+ formatWarningLine
21
+ } = require('../utils/cli-test-layout-chalk');
22
+
23
+ /**
24
+ * @param {string} appKey
25
+ * @returns {boolean}
26
+ */
27
+ function looksLikeExistingFilePath(appKey) {
28
+ try {
29
+ return Boolean(appKey && fs.existsSync(appKey) && fs.statSync(appKey).isFile());
30
+ } catch {
31
+ return false;
32
+ }
33
+ }
34
+
35
+ /**
36
+ * @param {object} opts
37
+ * @returns {string[]}
38
+ */
39
+ function collectBatchAppNames(opts) {
40
+ const names = [];
41
+ if (opts.integration === true) {
42
+ names.push(...listIntegrationAppNames());
43
+ }
44
+ if (opts.builder === true) {
45
+ names.push(...listBuilderAppNames());
46
+ }
47
+ return [...new Set(names)];
48
+ }
49
+
50
+ /**
51
+ * @param {string} appKey
52
+ * @param {object} result
53
+ * @param {string} outFormat
54
+ */
55
+ function emitManifestLine(appKey, result, outFormat) {
56
+ if (outFormat === 'json' || !result?.appPath) {
57
+ return;
58
+ }
59
+ try {
60
+ const { emitManifestMetadataLineIfTTY } = require('../utils/manifest-source-emit');
61
+ emitManifestMetadataLineIfTTY(logger, {
62
+ appKey,
63
+ appPath: result.appPath,
64
+ envOnly: false,
65
+ json: false
66
+ });
67
+ } catch {
68
+ /* ignore */
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Validate one app/systemKey; write deploy JSON only when local validate succeeds.
74
+ * @param {string} appKey
75
+ * @param {object} [options]
76
+ * @returns {Promise<{ appKey: string, valid: boolean, validation: object, deploy?: object|null, error?: string }>}
77
+ */
78
+ async function validateThenWriteDeploy(appKey, options = {}) {
79
+ let validation;
80
+ try {
81
+ validation = await validate.validateAppOrFile(appKey, { ...options, certSync: false });
82
+ } catch (err) {
83
+ return {
84
+ appKey,
85
+ valid: false,
86
+ validation: { valid: false, errors: [err.message || String(err)] },
87
+ deploy: null,
88
+ error: err.message || String(err)
89
+ };
90
+ }
91
+ if (!validation.valid) {
92
+ return { appKey, valid: false, validation, deploy: null };
93
+ }
94
+ const deploy = await generator.generateDeployJsonWithValidation(appKey, options);
95
+ const ok = deploy.success === true;
96
+ return {
97
+ appKey,
98
+ valid: ok,
99
+ validation,
100
+ deploy,
101
+ error: ok ? undefined : (deploy.validation?.errors || []).join('; ') || 'deploy JSON validation failed'
102
+ };
103
+ }
104
+
105
+ /**
106
+ * @param {object} item
107
+ * @param {string} outFormat
108
+ */
109
+ function displayOneHumanResult(item, outFormat) {
110
+ emitManifestLine(item.appKey, item.validation, outFormat);
111
+ if (outFormat === 'json') {
112
+ return;
113
+ }
114
+ validate.displayValidationResults(item.validation);
115
+ if (!item.validation.valid) {
116
+ logger.log(formatBlockingError(`Skipped deploy JSON for ${item.appKey} (validation failed)`));
117
+ return;
118
+ }
119
+ if (item.deploy?.success && item.deploy.path) {
120
+ logger.log(formatSuccessLine(`Generated deployment JSON: ${item.deploy.path}`));
121
+ const warnings = item.deploy.validation?.warnings || [];
122
+ if (warnings.length > 0) {
123
+ warnings.forEach((w) => logger.log(formatWarningLine(w)));
124
+ }
125
+ return;
126
+ }
127
+ logger.log(formatBlockingError(`Deploy JSON write failed for ${item.appKey}`));
128
+ (item.deploy?.validation?.errors || []).forEach((e) => logger.log(` • ${e}`));
129
+ }
130
+
131
+ /**
132
+ * @param {Array<object>} items
133
+ * @param {string} outFormat
134
+ */
135
+ function displayBatchSummary(items, outFormat) {
136
+ if (outFormat === 'json') {
137
+ logger.log(JSON.stringify({
138
+ batch: true,
139
+ valid: items.every((i) => i.valid),
140
+ results: items.map((i) => ({
141
+ appKey: i.appKey,
142
+ valid: i.valid,
143
+ path: i.deploy?.path || null,
144
+ error: i.error || null
145
+ }))
146
+ }, null, 2));
147
+ return;
148
+ }
149
+ const passed = items.filter((i) => i.valid);
150
+ const failed = items.filter((i) => !i.valid);
151
+ logger.log('');
152
+ logger.log(chalk.bold('validate-json summary'));
153
+ logger.log(` Passed: ${passed.length}`);
154
+ logger.log(` Failed: ${failed.length}`);
155
+ failed.forEach((i) => {
156
+ logger.log(formatBlockingError(`${i.appKey}${i.error ? `: ${i.error}` : ''}`));
157
+ });
158
+ }
159
+
160
+ /**
161
+ * @param {object} opts
162
+ * @param {string} outFormat
163
+ * @returns {Promise<{ exitCode: number }>}
164
+ */
165
+ async function runValidateJsonBatch(opts, outFormat) {
166
+ const names = collectBatchAppNames(opts);
167
+ const items = [];
168
+ for (const name of names) {
169
+ const item = await validateThenWriteDeploy(name, opts);
170
+ displayOneHumanResult(item, outFormat);
171
+ items.push(item);
172
+ }
173
+ displayBatchSummary(items, outFormat);
174
+ return { exitCode: items.every((i) => i.valid) ? 0 : 1 };
175
+ }
176
+
177
+ /**
178
+ * @param {string} appKey
179
+ * @param {object} opts
180
+ * @param {string} outFormat
181
+ * @returns {Promise<{ exitCode: number }>}
182
+ */
183
+ async function runValidateJsonSingle(appKey, opts, outFormat) {
184
+ if (looksLikeExistingFilePath(appKey)) {
185
+ logger.log(
186
+ chalk.red(
187
+ 'validate-json requires an appKey or systemKey (not a file path). Use aifabrix validate <file> for files.'
188
+ )
189
+ );
190
+ return { exitCode: 1 };
191
+ }
192
+ const item = await validateThenWriteDeploy(appKey, opts);
193
+ if (outFormat === 'json') {
194
+ logger.log(JSON.stringify({
195
+ appKey: item.appKey,
196
+ valid: item.valid,
197
+ path: item.deploy?.path || null,
198
+ validation: item.validation,
199
+ error: item.error || null
200
+ }, null, 2));
201
+ } else {
202
+ displayOneHumanResult(item, outFormat);
203
+ }
204
+ return { exitCode: item.valid ? 0 : 1 };
205
+ }
206
+
207
+ /**
208
+ * @param {string|undefined} appKey
209
+ * @param {object} options - Commander options or opts()
210
+ * @returns {Promise<{ exitCode: number }>}
211
+ */
212
+ async function runValidateJsonCommand(appKey, options) {
213
+ const opts = options.opts ? options.opts() : options;
214
+ const outFormat = (opts.format || 'default').toLowerCase();
215
+ if (opts.integration === true || opts.builder === true) {
216
+ return runValidateJsonBatch(opts, outFormat);
217
+ }
218
+ if (!appKey || typeof appKey !== 'string') {
219
+ logger.log(chalk.red('App or system key is required, or use --integration / --builder'));
220
+ return { exitCode: 1 };
221
+ }
222
+ return runValidateJsonSingle(appKey, opts, outFormat);
223
+ }
224
+
225
+ module.exports = {
226
+ runValidateJsonCommand,
227
+ validateThenWriteDeploy,
228
+ collectBatchAppNames,
229
+ looksLikeExistingFilePath
230
+ };
@@ -10,6 +10,56 @@ const {
10
10
  buildSubjectsFromOptions,
11
11
  resolveGovernanceIntegrationContext
12
12
  } = require('./governance-cli-shared');
13
+ const { VERDICT } = require('../lifecycle/product-model');
14
+ const {
15
+ allDatasourcesAbacNotApplicable,
16
+ filterAbacRequiredDatasourceKeys
17
+ } = require('../lifecycle/access-control-abac');
18
+ const { runSystemLifecycle } = require('../api/lifecycle.api');
19
+
20
+ /**
21
+ * @param {string} resolvedKey
22
+ * @param {string[]} keys
23
+ * @param {Object} [extra]
24
+ * @returns {Object}
25
+ */
26
+ function buildNotApplicableGovernanceResult(resolvedKey, keys, extra = {}) {
27
+ return {
28
+ systemKey: resolvedKey,
29
+ command: 'verify-governance',
30
+ verdict: VERDICT.NOT_APPLICABLE,
31
+ policyCoveragePercent: 100,
32
+ dimensionCoveragePercent: 100,
33
+ enforcementScenarios: { passed: 0, total: 0 },
34
+ datasourceRows: keys.map(datasourceKey => ({
35
+ datasourceKey,
36
+ verdict: VERDICT.NOT_APPLICABLE
37
+ })),
38
+ ...extra
39
+ };
40
+ }
41
+
42
+ /**
43
+ * @param {string} dataplaneUrl
44
+ * @param {Object} authConfig
45
+ * @param {string} resolvedKey
46
+ * @param {string[]} keys
47
+ * @param {Object} [extra]
48
+ * @returns {Promise<Object>}
49
+ */
50
+ async function closeAllGovernanceAsNotApplicable(
51
+ dataplaneUrl,
52
+ authConfig,
53
+ resolvedKey,
54
+ keys,
55
+ extra = {}
56
+ ) {
57
+ await runSystemLifecycle(dataplaneUrl, authConfig, resolvedKey, {
58
+ steps: ['governance'],
59
+ datasourceKeys: keys
60
+ });
61
+ return buildNotApplicableGovernanceResult(resolvedKey, keys, extra);
62
+ }
13
63
 
14
64
  /**
15
65
  * @async
@@ -23,22 +73,32 @@ async function runVerifyGovernanceCombined(systemKey, options = {}) {
23
73
  }
24
74
 
25
75
  const subjects = buildSubjectsFromOptions(options);
76
+ const { resolvedKey, keys, authConfig, dataplaneUrl } =
77
+ await resolveGovernanceIntegrationContext(systemKey, options);
78
+
26
79
  if (subjects.length === 0) {
27
- throw new Error(
28
- 'At least one --subject-user-id or --subject-email is required for verify-governance'
29
- );
80
+ if (!allDatasourcesAbacNotApplicable(resolvedKey, keys)) {
81
+ throw new Error(
82
+ 'At least one --subject-user-id or --subject-email is required for verify-governance'
83
+ );
84
+ }
85
+ return closeAllGovernanceAsNotApplicable(dataplaneUrl, authConfig, resolvedKey, keys);
30
86
  }
31
87
 
32
- const { resolvedKey, authConfig, dataplaneUrl } = await resolveGovernanceIntegrationContext(
33
- systemKey,
34
- options
35
- );
88
+ const requiredKeys = filterAbacRequiredDatasourceKeys(resolvedKey, keys);
89
+ if (requiredKeys.length === 0) {
90
+ return closeAllGovernanceAsNotApplicable(dataplaneUrl, authConfig, resolvedKey, keys, {
91
+ generated: 0,
92
+ generateResults: []
93
+ });
94
+ }
36
95
 
37
96
  const pipelineOpts = {
38
97
  ...options,
39
98
  noPublish: true,
40
99
  authConfig,
41
- dataplaneUrl
100
+ dataplaneUrl,
101
+ datasourceKeys: requiredKeys
42
102
  };
43
103
 
44
104
  const generateResult = await runGovernanceGenerateForExternalSystem(
@@ -46,7 +106,11 @@ async function runVerifyGovernanceCombined(systemKey, options = {}) {
46
106
  pipelineOpts
47
107
  );
48
108
 
49
- const verifyResult = await runVerifyGovernanceForExternalSystem(resolvedKey, pipelineOpts);
109
+ // Verify all datasources so notApplicable keys close as NOT_APPLICABLE.
110
+ const verifyResult = await runVerifyGovernanceForExternalSystem(resolvedKey, {
111
+ ...pipelineOpts,
112
+ datasourceKeys: keys
113
+ });
50
114
 
51
115
  return {
52
116
  ...verifyResult,