@aifabrix/builder 2.51.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 (162) hide show
  1. package/jest.isolated-projects.js +77 -1
  2. package/jest.projects.js +5 -426
  3. package/lib/api/assistants.api.js +67 -0
  4. package/lib/api/datasources-extended.api.js +6 -6
  5. package/lib/api/evidence-authoring-helpers.js +267 -0
  6. package/lib/api/evidence-authoring.api.js +337 -0
  7. package/lib/api/evidence.api.js +57 -1
  8. package/lib/api/index.js +24 -7
  9. package/lib/api/pipeline.api.js +43 -3
  10. package/lib/api/records-bulk.api.js +28 -3
  11. package/lib/api/types/evidence.types.js +156 -8
  12. package/lib/api/work.api.js +283 -0
  13. package/lib/app/run-deploy-job.js +108 -0
  14. package/lib/app/run-helpers.js +10 -3
  15. package/lib/app/run.js +8 -0
  16. package/lib/channels/add-channel.js +54 -23
  17. package/lib/channels/approval-guides/chatgpt.js +8 -7
  18. package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
  19. package/lib/channels/channel-artifact-generator.js +14 -2
  20. package/lib/channels/channel-credential.js +3 -1
  21. package/lib/channels/channel-probe-evidence.js +79 -0
  22. package/lib/channels/channel-probe.js +265 -13
  23. package/lib/channels/channel-runtime-resolver.js +120 -29
  24. package/lib/channels/channel-status-display.js +44 -0
  25. package/lib/channels/channel-targets.js +7 -0
  26. package/lib/channels/chatgpt-runtime-contract.js +29 -0
  27. package/lib/channels/microsoft-copilot-package.js +280 -0
  28. package/lib/channels/runtime-tool-catalog.js +165 -0
  29. package/lib/cli/index.js +2 -0
  30. package/lib/cli/setup-app.js +4 -0
  31. package/lib/cli/setup-external-system-help.js +8 -2
  32. package/lib/cli/setup-external-system.js +12 -0
  33. package/lib/commands/app-test.js +2 -14
  34. package/lib/commands/datasource-load-export-cli.js +6 -1
  35. package/lib/commands/deploy-gate-failure-details.js +19 -17
  36. package/lib/commands/dev-down.js +2 -6
  37. package/lib/commands/evidence-kind.js +65 -15
  38. package/lib/commands/governance-generate-external.js +56 -21
  39. package/lib/commands/lifecycle-external.js +10 -4
  40. package/lib/commands/repair-datasource-apply.js +10 -1
  41. package/lib/commands/repair-datasource-run.js +4 -2
  42. package/lib/commands/repair-datasource-test-payload.js +80 -19
  43. package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
  44. package/lib/commands/repair-internal.js +9 -1
  45. package/lib/commands/repair-openapi-sync.js +34 -17
  46. package/lib/commands/role-assistant.js +313 -0
  47. package/lib/commands/upload-data-sync-finalize.js +5 -4
  48. package/lib/commands/upload-sidecar-followups.js +144 -47
  49. package/lib/commands/upload.js +43 -6
  50. package/lib/commands/verify-governance-combined-external.js +73 -9
  51. package/lib/commands/verify-governance-db.js +80 -25
  52. package/lib/commands/verify-governance-external.js +29 -5
  53. package/lib/commands/wizard-core-helpers.js +7 -3
  54. package/lib/commands/wizard-core.js +78 -9
  55. package/lib/datasource/bulk-loader-run-batches.js +4 -2
  56. package/lib/datasource/bulk-loader-service.js +5 -1
  57. package/lib/datasource/datasource-validate-summary.js +2 -2
  58. package/lib/datasource/validate.js +13 -6
  59. package/lib/external-system/deploy-helpers.js +35 -1
  60. package/lib/external-system/deploy.js +23 -10
  61. package/lib/external-system/download-helpers.js +111 -2
  62. package/lib/external-system/download-role-assistant.js +55 -0
  63. package/lib/external-system/download.js +45 -26
  64. package/lib/external-system/test-helpers.js +1 -1
  65. package/lib/external-system/test.js +39 -3
  66. package/lib/generator/builders.js +3 -0
  67. package/lib/generator/wizard.js +51 -4
  68. package/lib/lifecycle/access-control-abac.js +121 -0
  69. package/lib/lifecycle/lifecycle-governance-options.js +4 -1
  70. package/lib/lifecycle/product-model.js +3 -2
  71. package/lib/lifecycle/report-display.js +14 -2
  72. package/lib/programmatic/capability-registry.js +20 -1
  73. package/lib/programmatic/command-options.js +1 -0
  74. package/lib/programmatic/index.js +4 -0
  75. package/lib/programmatic/manifest-patch-section.js +48 -9
  76. package/lib/programmatic/manifest-section-registry-data.js +3 -2
  77. package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
  78. package/lib/programmatic/openapi-component-schemas.js +16 -1
  79. package/lib/programmatic/openapi-description-enricher.js +3 -1
  80. package/lib/programmatic/openapi-descriptions.js +55 -5
  81. package/lib/programmatic/openapi-evidence-schemas.js +161 -0
  82. package/lib/programmatic/openapi-manifest-schemas.js +6 -2
  83. package/lib/programmatic/openapi-operation-bindings.js +99 -0
  84. package/lib/programmatic/openapi-request-schemas.js +36 -1
  85. package/lib/programmatic/openapi-result-schemas.js +1 -1
  86. package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
  87. package/lib/programmatic/openapi-schema-descriptions-core.js +1 -1
  88. package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
  89. package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
  90. package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
  91. package/lib/programmatic/openapi-spec.js +47 -28
  92. package/lib/programmatic/orchestration-openapi-meta.js +5 -0
  93. package/lib/programmatic/request-timeout.js +3 -0
  94. package/lib/programmatic/route-handler-map.js +89 -2
  95. package/lib/programmatic/run-certify.js +15 -2
  96. package/lib/programmatic/run-deploy.js +74 -0
  97. package/lib/programmatic/run-evidence-authoring.js +80 -0
  98. package/lib/programmatic/run-protection.js +124 -0
  99. package/lib/programmatic/run-template-workspace.js +28 -1
  100. package/lib/programmatic/workspace-context.js +6 -0
  101. package/lib/protection/auth-context.js +48 -6
  102. package/lib/protection/paths.js +22 -0
  103. package/lib/protection/run-protection-create-helpers.js +22 -1
  104. package/lib/protection/run-protection-create.js +1 -1
  105. package/lib/protection/sync-after-upload.js +1 -1
  106. package/lib/role-assistant/evidence-sync.js +450 -0
  107. package/lib/role-assistant/github-workflow.js +96 -0
  108. package/lib/role-assistant/knowledge-sync.js +465 -0
  109. package/lib/role-assistant/settings.js +126 -0
  110. package/lib/role-assistant/test-cases-evaluate.js +170 -0
  111. package/lib/role-assistant/test-cases-honesty.js +184 -0
  112. package/lib/role-assistant/test-cases.js +364 -0
  113. package/lib/role-assistant/test-runner-workhub-answers.js +203 -0
  114. package/lib/role-assistant/test-runner-workhub.js +385 -0
  115. package/lib/role-assistant/test-runner.js +500 -0
  116. package/lib/schema/application-schema.json +16 -2
  117. package/lib/schema/deployment-rules.yaml +1 -0
  118. package/lib/schema/external-datasource.schema.json +38 -4
  119. package/lib/schema/external-system.schema.json +4 -4
  120. package/lib/schema/infra.parameter.yaml +20 -0
  121. package/lib/schema/role-assistant-settings.schema.json +108 -0
  122. package/lib/utils/api.js +52 -27
  123. package/lib/utils/app-path-resolve.js +72 -24
  124. package/lib/utils/authentication-shape.js +3 -1
  125. package/lib/utils/deployment-validation.js +28 -3
  126. package/lib/utils/docker-run-ephemeral.js +55 -0
  127. package/lib/utils/enterprise-knowledge-paths.js +29 -12
  128. package/lib/utils/env-copy-keycloak.js +196 -0
  129. package/lib/utils/env-copy.js +34 -1
  130. package/lib/utils/external-system-readiness-compact-display.js +3 -0
  131. package/lib/utils/help-builder.js +1 -0
  132. package/lib/utils/paths.js +67 -23
  133. package/lib/utils/pipeline-upload-auth-retry.js +15 -3
  134. package/lib/utils/role-assistant-paths.js +98 -0
  135. package/lib/utils/schema-loader.js +41 -0
  136. package/lib/utils/schema-resolver.js +36 -4
  137. package/lib/utils/token-manager.js +38 -29
  138. package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
  139. package/lib/validation/external-manifest-validator.js +31 -8
  140. package/lib/validation/validate-external-file.js +8 -1
  141. package/package.json +2 -1
  142. package/scripts/export-builder-api-evidence.js +1 -1
  143. package/templates/applications/dataplane/application.yaml +23 -15
  144. package/templates/applications/dataplane/env.template +1 -1
  145. package/templates/applications/dataplane/rbac.yaml +14 -0
  146. package/templates/applications/miso-controller/application.yaml +1 -1
  147. package/templates/applications/miso-controller/env.template +3 -1
  148. package/templates/channels/chatgpt/install.template.md.hbs +42 -24
  149. package/templates/channels/chatgpt/integration/README.md +10 -7
  150. package/templates/channels/chatgpt/integration/application.json +2 -2
  151. package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
  152. package/templates/channels/chatgpt/integration/env.template +1 -1
  153. package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
  154. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
  155. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
  156. package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
  157. package/templates/channels/microsoft-copilot/integration/README.md +9 -7
  158. package/templates/channels/microsoft-copilot/integration/application.json +2 -2
  159. package/templates/channels/microsoft-copilot/integration/env.template +1 -1
  160. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
  161. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
  162. /package/.cursor/plans/{157.1-security-quality-violations-by-type.csv → Done/157.1-security-quality-violations-by-type.csv} +0 -0
@@ -0,0 +1,280 @@
1
+ /**
2
+ * Microsoft Copilot package artifact generation (plan 164).
3
+ *
4
+ * @fileoverview Build source files, package ZIP, and lifecycle summary for microsoft-copilot channel
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const crypto = require('crypto');
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+ const AdmZip = require('adm-zip');
15
+
16
+ const PLACEHOLDER_ICON_BASE64 =
17
+ 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO2s9WQAAAAASUVORK5CYII=';
18
+
19
+ /**
20
+ * @param {string} outputPath
21
+ * @param {Object} runtimeValues
22
+ * @returns {Object}
23
+ */
24
+ function buildCopilotOutputPaths(outputPath, runtimeValues) {
25
+ const rootDir = path.dirname(outputPath);
26
+ const sourceDir = path.join(rootDir, 'source');
27
+ const buildDir = path.join(rootDir, 'build');
28
+ const installDir = path.join(rootDir, 'install');
29
+ const environment = String(runtimeValues.environment || 'dev').trim() || 'dev';
30
+ const zipFileName = `appPackage.${environment}.zip`;
31
+ return {
32
+ rootDir,
33
+ sourceDir,
34
+ buildDir,
35
+ installDir,
36
+ summaryPath: outputPath,
37
+ zipFileName,
38
+ zipPath: path.join(buildDir, zipFileName),
39
+ validationPath: path.join(installDir, 'validation.json'),
40
+ installPath: path.join(installDir, 'README.md')
41
+ };
42
+ }
43
+
44
+ /**
45
+ * @returns {{ color: Buffer, outline: Buffer }}
46
+ */
47
+ function getPlaceholderIcons() {
48
+ const icon = Buffer.from(PLACEHOLDER_ICON_BASE64, 'base64');
49
+ return { color: icon, outline: icon };
50
+ }
51
+
52
+ /**
53
+ * @param {Object} runtimeValues
54
+ * @param {(templatePath: string, context: Object) => string} renderTemplateFile
55
+ * @param {string} templatesDir
56
+ * @returns {{ sourceFiles: Record<string, string>, installText: string }}
57
+ */
58
+ function renderCopilotTemplates(runtimeValues, renderTemplateFile, templatesDir) {
59
+ const sourceFiles = {
60
+ 'manifest.json': renderTemplateFile(path.join(templatesDir, 'manifest.template.json.hbs'), runtimeValues),
61
+ 'declarativeAgent.json': renderTemplateFile(
62
+ path.join(templatesDir, 'declarative-agent.template.json.hbs'),
63
+ runtimeValues
64
+ ),
65
+ 'ai-plugin.json': renderTemplateFile(
66
+ path.join(templatesDir, 'ai-plugin.template.json.hbs'),
67
+ runtimeValues
68
+ )
69
+ };
70
+ const installText = renderTemplateFile(path.join(templatesDir, 'install.template.md.hbs'), runtimeValues);
71
+ return { sourceFiles, installText };
72
+ }
73
+
74
+ /**
75
+ * @param {Record<string, string>} sourceFiles
76
+ * @param {(content: string) => void} assertNoSecretsInArtifact
77
+ */
78
+ function assertNoSecretsInSourceFiles(sourceFiles, assertNoSecretsInArtifact) {
79
+ for (const content of Object.values(sourceFiles)) {
80
+ assertNoSecretsInArtifact(content);
81
+ }
82
+ }
83
+
84
+ /**
85
+ * @param {Record<string, string>} sourceFiles
86
+ * @param {{ color: Buffer, outline: Buffer }} icons
87
+ * @returns {Buffer}
88
+ */
89
+ function buildCopilotZipBuffer(sourceFiles, icons) {
90
+ const zip = new AdmZip();
91
+ zip.addFile('manifest.json', Buffer.from(sourceFiles['manifest.json'], 'utf8'));
92
+ zip.addFile('declarativeAgent.json', Buffer.from(sourceFiles['declarativeAgent.json'], 'utf8'));
93
+ zip.addFile('ai-plugin.json', Buffer.from(sourceFiles['ai-plugin.json'], 'utf8'));
94
+ zip.addFile('color.png', icons.color);
95
+ zip.addFile('outline.png', icons.outline);
96
+ return zip.toBuffer();
97
+ }
98
+
99
+ /**
100
+ * @param {Object} runtimeValues
101
+ * @param {Object} paths
102
+ * @param {string[]} sourceFileNames
103
+ * @returns {Object}
104
+ */
105
+ function buildCopilotValidation(runtimeValues, paths, sourceFileNames) {
106
+ return {
107
+ tier: 'builderValidated',
108
+ environment: runtimeValues.environment,
109
+ sourceFiles: sourceFileNames,
110
+ zipFile: paths.zipFileName,
111
+ runtimeSystemKey: runtimeValues.runtimeSystemKey,
112
+ runtimeMcpUrl: runtimeValues.mcpUrl,
113
+ runtimeTransport: runtimeValues.runtimeTransport,
114
+ expectedLayerBToolNames: runtimeValues.layerBToolNames || [],
115
+ boundaries: {
116
+ onePackagePerEnvironment: true,
117
+ topLevelVendorToolsExposed: false,
118
+ roleAssistantResolution: 'dynamic-runtime'
119
+ },
120
+ requiresMicrosoftAdmin: [
121
+ 'upload-package',
122
+ 'tenant-approval',
123
+ 'assignment-and-consent',
124
+ 'first-copilot-invoke'
125
+ ]
126
+ };
127
+ }
128
+
129
+ /**
130
+ * @param {Array<string|Buffer>} parts
131
+ * @returns {string}
132
+ */
133
+ function computeCopilotArtifactHash(parts) {
134
+ const hash = crypto.createHash('sha256');
135
+ for (const part of parts) {
136
+ if (Buffer.isBuffer(part)) {
137
+ hash.update(part);
138
+ continue;
139
+ }
140
+ hash.update(String(part), 'utf8');
141
+ }
142
+ return hash.digest('hex');
143
+ }
144
+
145
+ /**
146
+ * @param {Object} runtimeValues
147
+ * @param {Object} paths
148
+ * @param {Object} validation
149
+ * @param {string} artifactHash
150
+ * @returns {Object}
151
+ */
152
+ function buildCopilotSummary(runtimeValues, paths, validation, artifactHash) {
153
+ return {
154
+ channelTarget: runtimeValues.channelTarget,
155
+ name: runtimeValues.appName,
156
+ baseName: runtimeValues.baseName,
157
+ description: runtimeValues.appDescription,
158
+ environment: runtimeValues.environment,
159
+ runtime: {
160
+ runtimeSystemKey: runtimeValues.runtimeSystemKey,
161
+ remoteMcpUrl: runtimeValues.mcpUrl,
162
+ transport: runtimeValues.runtimeTransport,
163
+ assistantCatalogUrl: runtimeValues.assistantCatalogUrl,
164
+ roleAssistantResolution: 'dynamic-runtime',
165
+ layerBToolNames: runtimeValues.layerBToolNames || []
166
+ },
167
+ authentication: {
168
+ mode: runtimeValues.runtimeAuthMode,
169
+ credentialKey: runtimeValues.credentialKey,
170
+ credentialPortalUrl: runtimeValues.credentialPortalUrl,
171
+ scopes: runtimeValues.scopes || []
172
+ },
173
+ package: {
174
+ appId: runtimeValues.packageAppId,
175
+ version: runtimeValues.packageVersion,
176
+ sourceFiles: ['manifest.json', 'declarativeAgent.json', 'ai-plugin.json', 'color.png', 'outline.png'],
177
+ zipFileName: paths.zipFileName,
178
+ installGuide: path.relative(paths.rootDir, paths.installPath),
179
+ validationFile: path.relative(paths.rootDir, paths.validationPath)
180
+ },
181
+ probeTiers: {
182
+ builderValidated: true,
183
+ runtimeValidated: false,
184
+ requiresMicrosoftAdmin: true
185
+ },
186
+ artifact: {
187
+ format: 'microsoft-365-copilot-package',
188
+ hash: artifactHash,
189
+ generatedAt: new Date().toISOString()
190
+ },
191
+ validation
192
+ };
193
+ }
194
+
195
+ /**
196
+ * @param {Object} params
197
+ */
198
+ function writeCopilotArtifacts(params) {
199
+ const { paths, sourceFiles, icons, zipBuffer, installText, validationText, summaryText } = params;
200
+ fs.mkdirSync(paths.sourceDir, { recursive: true });
201
+ fs.mkdirSync(paths.buildDir, { recursive: true });
202
+ fs.mkdirSync(paths.installDir, { recursive: true });
203
+ fs.writeFileSync(path.join(paths.sourceDir, 'manifest.json'), sourceFiles['manifest.json'], 'utf8');
204
+ fs.writeFileSync(
205
+ path.join(paths.sourceDir, 'declarativeAgent.json'),
206
+ sourceFiles['declarativeAgent.json'],
207
+ 'utf8'
208
+ );
209
+ fs.writeFileSync(path.join(paths.sourceDir, 'ai-plugin.json'), sourceFiles['ai-plugin.json'], 'utf8');
210
+ fs.writeFileSync(path.join(paths.sourceDir, 'color.png'), icons.color);
211
+ fs.writeFileSync(path.join(paths.sourceDir, 'outline.png'), icons.outline);
212
+ fs.writeFileSync(paths.zipPath, zipBuffer);
213
+ fs.writeFileSync(paths.installPath, installText, 'utf8');
214
+ fs.writeFileSync(paths.validationPath, validationText, 'utf8');
215
+ fs.writeFileSync(paths.summaryPath, summaryText, 'utf8');
216
+ }
217
+
218
+ /**
219
+ * @param {Object} params
220
+ * @param {Object} params.runtimeValues
221
+ * @param {string} params.templatesDir
222
+ * @param {string} params.outputPath
223
+ * @param {boolean} [params.dryRun]
224
+ * @param {(content: string) => void} params.assertNoSecretsInArtifact
225
+ * @param {(templatePath: string, context: Object) => string} params.renderTemplateFile
226
+ * @returns {{ outputPath: string, artifactHash: string, content: string, installPath: string }}
227
+ */
228
+ function generateMicrosoftCopilotPackage(params) {
229
+ const runtimeValues = params.runtimeValues || {};
230
+ const paths = buildCopilotOutputPaths(params.outputPath, runtimeValues);
231
+ const icons = getPlaceholderIcons();
232
+ const { sourceFiles, installText } = renderCopilotTemplates(
233
+ runtimeValues,
234
+ params.renderTemplateFile,
235
+ params.templatesDir
236
+ );
237
+ assertNoSecretsInSourceFiles(sourceFiles, params.assertNoSecretsInArtifact);
238
+ params.assertNoSecretsInArtifact(installText);
239
+ const zipBuffer = buildCopilotZipBuffer(sourceFiles, icons);
240
+ const validation = buildCopilotValidation(runtimeValues, paths, Object.keys(sourceFiles));
241
+ const validationText = JSON.stringify(validation, null, 2);
242
+ const artifactHash = computeCopilotArtifactHash([
243
+ sourceFiles['manifest.json'],
244
+ sourceFiles['declarativeAgent.json'],
245
+ sourceFiles['ai-plugin.json'],
246
+ installText,
247
+ validationText,
248
+ zipBuffer
249
+ ]);
250
+ const summary = buildCopilotSummary(runtimeValues, paths, validation, artifactHash);
251
+ const summaryText = JSON.stringify(summary, null, 2);
252
+ params.assertNoSecretsInArtifact(summaryText);
253
+ if (!params.dryRun) {
254
+ writeCopilotArtifacts({
255
+ paths,
256
+ sourceFiles,
257
+ icons,
258
+ zipBuffer,
259
+ installText,
260
+ validationText,
261
+ summaryText
262
+ });
263
+ }
264
+ return {
265
+ outputPath: paths.summaryPath,
266
+ artifactHash,
267
+ content: summaryText,
268
+ installPath: paths.installPath
269
+ };
270
+ }
271
+
272
+ module.exports = {
273
+ generateMicrosoftCopilotPackage,
274
+ buildCopilotOutputPaths,
275
+ renderCopilotTemplates,
276
+ buildCopilotZipBuffer,
277
+ buildCopilotValidation,
278
+ buildCopilotSummary,
279
+ computeCopilotArtifactHash
280
+ };
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Resolve Runtime Layer B tool names from Dataplane.
3
+ *
4
+ * @fileoverview Runtime-orchestration MCP tool discovery for channel artifacts and probes
5
+ * @author AI Fabrix Team
6
+ * @version 1.1.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { createDataplaneApiClient } = require('../api/index');
12
+ const { getChannelTarget } = require('./channel-targets');
13
+
14
+ /**
15
+ * Prefer the first-party MCP gateway catalog, then MCP OpenAPI, then legacy paths.
16
+ */
17
+ const TOOL_CATALOG_BUILDERS = Object.freeze([
18
+ (systemKey) =>
19
+ `/api/v1/mcp/servers/tools?filter=${encodeURIComponent(`systemKey:eq:${systemKey}`)}&pageSize=100`,
20
+ (systemKey) => `/api/v1/mcp/${systemKey}/docs/mcp-openapi.json`,
21
+ (systemKey) => `/api/v1/external/systems/${systemKey}/mcp/tools`,
22
+ (systemKey) => `/api/v1/external/systems/${systemKey}/mcp/tools/list`,
23
+ (systemKey) => `/api/v1/external/systems/${systemKey}/mcp/catalog`,
24
+ (systemKey) => `/api/v1/external/systems/${systemKey}/tools`
25
+ ]);
26
+
27
+ /**
28
+ * @param {unknown} value
29
+ * @returns {string[]}
30
+ */
31
+ function extractToolNames(value) {
32
+ if (Array.isArray(value)) {
33
+ return value
34
+ .map((item) => {
35
+ if (typeof item === 'string') return item.trim();
36
+ if (!item || typeof item !== 'object') return '';
37
+ return String(item.name || item.toolName || item.key || '').trim();
38
+ })
39
+ .filter(Boolean);
40
+ }
41
+ if (!value || typeof value !== 'object') {
42
+ return [];
43
+ }
44
+ const body = /** @type {Record<string, unknown>} */ (value);
45
+ if (body.paths && typeof body.paths === 'object') {
46
+ return extractToolNamesFromOpenApiPaths(body.paths);
47
+ }
48
+ const candidates = [body.tools, body.items, body.data, body.result];
49
+ for (const candidate of candidates) {
50
+ const names = extractToolNames(candidate);
51
+ if (names.length > 0) {
52
+ return names;
53
+ }
54
+ }
55
+ return [];
56
+ }
57
+
58
+ /**
59
+ * @param {Record<string, unknown>} paths
60
+ * @returns {string[]}
61
+ */
62
+ function extractToolNamesFromOpenApiPaths(paths) {
63
+ const names = [];
64
+ for (const pathKey of Object.keys(paths || {})) {
65
+ const match = String(pathKey).match(/\/tools\/([^/]+)\/execute$/);
66
+ if (match && match[1] && match[1] !== 'execute') {
67
+ names.push(match[1]);
68
+ }
69
+ }
70
+ return [...new Set(names)];
71
+ }
72
+
73
+ /**
74
+ * @param {string} routeSystemKey
75
+ * @returns {string[]}
76
+ */
77
+ function buildToolCatalogEndpoints(routeSystemKey) {
78
+ return TOOL_CATALOG_BUILDERS.map((build) => build(routeSystemKey));
79
+ }
80
+
81
+ /**
82
+ * @param {Object} response
83
+ * @returns {string[]}
84
+ */
85
+ function extractToolNamesFromClientResponse(response) {
86
+ const direct = extractToolNames(response);
87
+ if (direct.length > 0) return preferLayerBToolNames(direct);
88
+ return preferLayerBToolNames(extractToolNames(response?.data ?? response));
89
+ }
90
+
91
+ /**
92
+ * Prefer MCP Layer B names (`work.start`) over REST-ish operation ids.
93
+ *
94
+ * @param {string[]} names
95
+ * @returns {string[]}
96
+ */
97
+ function preferLayerBToolNames(names) {
98
+ const list = Array.isArray(names) ? names : [];
99
+ const dotted = list.filter((name) => /^[a-z][a-z0-9]*\.[a-z][a-z0-9.]*$/i.test(name));
100
+ return dotted.length > 0 ? dotted : list;
101
+ }
102
+
103
+ /**
104
+ * @param {string} dataplaneUrl
105
+ * @param {Object} authConfig
106
+ * @param {string} channelTarget
107
+ * @returns {Promise<string[]>}
108
+ */
109
+ async function fetchRuntimeLayerBToolNames(dataplaneUrl, authConfig, channelTarget) {
110
+ const target = getChannelTarget(channelTarget);
111
+ const routeSystemKey = target.routeSystemKey || target.key;
112
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
113
+ const endpoints = buildToolCatalogEndpoints(routeSystemKey);
114
+ for (const endpoint of endpoints) {
115
+ try {
116
+ const response = await client.get(endpoint, { timeoutMs: 10000 });
117
+ const names = extractToolNamesFromClientResponse(response);
118
+ if (Array.isArray(names) && names.length > 0) {
119
+ return [...new Set(names)].sort();
120
+ }
121
+ } catch {
122
+ // Try the next endpoint candidate.
123
+ }
124
+ }
125
+ return [];
126
+ }
127
+
128
+ /**
129
+ * True when Runtime MCP gateway docs or tool catalog is reachable.
130
+ *
131
+ * @async
132
+ * @param {string} dataplaneUrl
133
+ * @param {Object} authConfig
134
+ * @param {string} routeSystemKey
135
+ * @returns {Promise<boolean>}
136
+ */
137
+ async function isRuntimeMcpGatewayReachable(dataplaneUrl, authConfig, routeSystemKey) {
138
+ if (!dataplaneUrl || !routeSystemKey) return false;
139
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
140
+ const candidates = [
141
+ `/api/v1/mcp/${routeSystemKey}/docs/mcp-openapi.json`,
142
+ `/api/v1/mcp/servers/tools?filter=${encodeURIComponent(`systemKey:eq:${routeSystemKey}`)}&pageSize=1`
143
+ ];
144
+ for (const endpoint of candidates) {
145
+ try {
146
+ const response = await client.get(endpoint, { timeoutMs: 8000 });
147
+ if (response && response.success !== false) {
148
+ return true;
149
+ }
150
+ } catch {
151
+ // try next
152
+ }
153
+ }
154
+ return false;
155
+ }
156
+
157
+ module.exports = {
158
+ TOOL_CATALOG_BUILDERS,
159
+ extractToolNames,
160
+ extractToolNamesFromOpenApiPaths,
161
+ preferLayerBToolNames,
162
+ buildToolCatalogEndpoints,
163
+ fetchRuntimeLayerBToolNames,
164
+ isRuntimeMcpGatewayReachable
165
+ };
package/lib/cli/index.js CHANGED
@@ -24,6 +24,7 @@ const { setupDatasourceCommands } = require('../commands/datasource');
24
24
  const { setupDimensionCommands } = require('../commands/dimension');
25
25
  const { setupResourceTypeCommands } = require('../commands/resource-type');
26
26
  const { setupRoleCommands } = require('../commands/role');
27
+ const { setupRoleAssistantCommands } = require('../commands/role-assistant');
27
28
  const { setupEvidenceKindCommands } = require('../commands/evidence-kind');
28
29
  const { setupEnterpriseKnowledgeCommands } = require('../commands/enterprise-knowledge');
29
30
  const { setupIdentityCommands } = require('../commands/identity');
@@ -76,6 +77,7 @@ function setupExtendedCliCommands(program) {
76
77
  setupDimensionCommands(program);
77
78
  setupResourceTypeCommands(program);
78
79
  setupRoleCommands(program);
80
+ setupRoleAssistantCommands(program);
79
81
  setupEvidenceKindCommands(program);
80
82
  setupEnterpriseKnowledgeCommands(program);
81
83
  setupIdentityCommands(program);
@@ -56,12 +56,16 @@ Examples:
56
56
  $ aifabrix validate hubspot-demo
57
57
  $ aifabrix upload hubspot-demo --probe
58
58
  $ aifabrix deploy hubspot-demo --probe -v
59
+ # Prove pillars in tst/CI, then promote (plain deploy seals cert on published Deploy)
59
60
  $ aifabrix deploy ek-sales --gate full --subject-email ops@example.com
61
+ $ aifabrix deploy ek-sales
60
62
 
61
63
  Notes:
62
64
  - Login or set MISO_CONTROLLER_URL + environment (aifabrix login / aifabrix auth).
63
65
  - CI: register once (aifabrix app register <appKey>), store DEV_MISO_CLIENTID and DEV_MISO_CLIENTSECRET in pipeline secrets.
64
66
  - upload = dataplane publish for dev iteration; deploy = controller validate + deploy (Azure or local Docker).
67
+ - Prove (tst/CI): use --gate full (optional --subject-email) before promote; plain deploy promotes without live E2E gates.
68
+ - After a successful published Deploy, the platform seals the integration certificate (no separate certificates issue step).
65
69
  - Default output is a compact status card; use -v for expanded preflight and readiness detail.
66
70
  - Controller deploy always polls to a terminal status (no --poll / --no-poll on this command).
67
71
  - --repository-url overrides repo URL for pipeline validate (default: application.yaml repository.repositoryUrl).
@@ -10,6 +10,7 @@ Examples:
10
10
  $ aifabrix upload hubspot-demo
11
11
  $ aifabrix upload hubspot-demo -v
12
12
  $ aifabrix upload hubspot-demo --probe
13
+ $ aifabrix upload hubspot-demo --timeout 600000
13
14
 
14
15
  # Build and validate payload only (no API calls)
15
16
  $ aifabrix upload hubspot-demo --dry-run
@@ -18,11 +19,16 @@ Examples:
18
19
  $ aifabrix upload .protection
19
20
  $ aifabrix upload .protection --warnings-as-errors
20
21
 
22
+ # Role Assistant package (settings section + Evidence sidecars)
23
+ $ aifabrix upload sales-representative --env dev
24
+
21
25
  Notes:
22
26
  - Requires login (aifabrix auth status).
23
- - Reads files under integration/<systemKey>/ (system JSON, datasources, credentials).
27
+ - Reads files under integration/<systemKey>/ or role-assistant/<roleKey>/ for digital workers.
24
28
  - Default output is a compact readiness summary; use -v for expanded detail and server-side validate warnings.
25
- - --probe runs dataplane runtime checks after publish (slower; common in Session 1).
29
+ - --probe runs dataplane runtime checks after publish (slower).
30
+ - --timeout sets publish budget in ms (HTTP client and dataplane); default 300000.
31
+ - --env selects role-assistant.yaml section (dev|tst|pro) for digital workers; default dev.
26
32
  - test-e2e and test-integration publish local files first by default; use --no-publish on those commands to skip.
27
33
  - After dimension/FK edits on active datasources, use --force (or test-e2e / test-integration / datasource test-* with --sync --force).
28
34
  - For one datasource only: aifabrix datasource upload <datasourceKey>
@@ -45,13 +45,16 @@ async function handleUploadCommandAction(systemKey, options) {
45
45
  return;
46
46
  }
47
47
  const upload = require('../commands/upload');
48
+ const { resolvePipelineUploadTimeoutMs } = require('../api/pipeline.api');
48
49
  const probeTimeout =
49
50
  options.probeTimeout === undefined || options.probeTimeout === null
50
51
  ? 120000
51
52
  : Number(options.probeTimeout);
53
+ const timeoutMs = resolvePipelineUploadTimeoutMs(options.timeout);
52
54
  await upload.uploadExternalSystem(systemKey, {
53
55
  ...options,
54
56
  probeTimeout: Number.isFinite(probeTimeout) ? probeTimeout : 120000,
57
+ timeoutMs,
55
58
  pathOptions: {}
56
59
  });
57
60
  }
@@ -98,10 +101,19 @@ function setupUploadCommand(program) {
98
101
  .option('-v, --verbose', 'Expanded upload summary and server-side pipeline validate warnings before publish')
99
102
  .option('--probe', 'After publish, run dataplane runtime checks (validation/run); slower')
100
103
  .option('--probe-timeout <ms>', 'Timeout for --probe (default: 120000)', '120000')
104
+ .option(
105
+ '--timeout <ms>',
106
+ 'Publish timeout in ms for HTTP and dataplane budget (default: 300000; min 5000, max 2700000)'
107
+ )
101
108
  .option(
102
109
  '--force',
103
110
  'Bypass schema guards on active datasources during publish (dangerous; shows a warning)'
104
111
  )
112
+ .option(
113
+ '-e, --env <env>',
114
+ 'Role Assistant settings environment section: dev, tst, or pro (default: dev)',
115
+ 'dev'
116
+ )
105
117
  .action(async(systemKey, options) => {
106
118
  try {
107
119
  await handleUploadCommandAction(systemKey, options);
@@ -166,20 +166,8 @@ async function runDockerExec(containerName, testCmd, envFilePath = null) {
166
166
  * @returns {Promise<number>} Exit code
167
167
  */
168
168
  async function runDockerRunEphemeral(fullImage, testCmd, envFilePath = null) {
169
- const { getDockerExecEnv } = require('../utils/remote-docker-env');
170
- const dockerEnv = await getDockerExecEnv();
171
- return new Promise((resolve) => {
172
- const args = ['run', '--rm'];
173
- if (envFilePath) args.push('--env-file', envFilePath);
174
- args.push(fullImage, 'sh', '-c', testCmd);
175
- const proc = spawn('docker', args, {
176
- stdio: 'inherit',
177
- shell: false,
178
- env: dockerEnv
179
- });
180
- proc.on('close', code => resolve(code !== null ? code : 1));
181
- proc.on('error', () => resolve(1));
182
- });
169
+ const { runDockerRunEphemeral: runEphemeral } = require('../utils/docker-run-ephemeral');
170
+ return runEphemeral(fullImage, testCmd, envFilePath);
183
171
  }
184
172
 
185
173
  /**
@@ -76,7 +76,8 @@ async function datasourceLoadAction(datasourceKey, options) {
76
76
  dryRun: options.dryRun,
77
77
  batchSize: options.batchSize,
78
78
  syncType: options.syncType,
79
- format: options.format
79
+ format: options.format,
80
+ timeoutMs: options.timeout
80
81
  });
81
82
 
82
83
  if (options.json) {
@@ -141,6 +142,10 @@ function setupDatasourceLoadExportCommands(datasource) {
141
142
  'Dataplane syncType',
142
143
  'incremental'
143
144
  )
145
+ .option(
146
+ '--timeout <ms>',
147
+ 'Per-batch HTTP timeout for records/bulk (default 120000)'
148
+ )
144
149
  .option('--dry-run', 'Parse and validate only; no upload')
145
150
  .option('-v, --verbose', 'Per-batch progress')
146
151
  .option('--json', 'Machine-readable result JSON')
@@ -113,6 +113,24 @@ function formatOperationsGateFailure(result) {
113
113
  return 'E2E validation did not succeed for one or more datasources';
114
114
  }
115
115
 
116
+ /**
117
+ * @param {Object} row
118
+ * @returns {string|null}
119
+ */
120
+ function formatFailedDatasourceRowHint(row) {
121
+ if (!row || row.success !== false) {
122
+ return null;
123
+ }
124
+ const key = row.key || row.datasourceKey || 'datasource';
125
+ const issues = row.datasourceTestRun?.validation?.issues;
126
+ if (Array.isArray(issues) && issues.length > 0) {
127
+ const first = issues[0];
128
+ const code = first.code ? `${first.code}: ` : '';
129
+ return `${key}: ${code}${first.message || 'validation failed'}`;
130
+ }
131
+ return row.error ? `${key}: ${row.error}` : null;
132
+ }
133
+
116
134
  /**
117
135
  * @param {Object} result
118
136
  * @returns {string}
@@ -125,23 +143,7 @@ function formatIntegrationGateFailure(result) {
125
143
  return String(result.message);
126
144
  }
127
145
  const rows = Array.isArray(result?.datasourceResults) ? result.datasourceResults : [];
128
- const hints = [];
129
- for (const row of rows) {
130
- if (!row || row.success !== false) {
131
- continue;
132
- }
133
- const key = row.key || row.datasourceKey || 'datasource';
134
- const issues = row.datasourceTestRun?.validation?.issues;
135
- if (Array.isArray(issues) && issues.length > 0) {
136
- const first = issues[0];
137
- const code = first.code ? `${first.code}: ` : '';
138
- hints.push(`${key}: ${code}${first.message || 'validation failed'}`);
139
- continue;
140
- }
141
- if (row.error) {
142
- hints.push(`${key}: ${row.error}`);
143
- }
144
- }
146
+ const hints = rows.map(formatFailedDatasourceRowHint).filter(Boolean);
145
147
  if (hints.length > 0) {
146
148
  return hints.slice(0, 3).join('; ');
147
149
  }
@@ -23,18 +23,14 @@ const execAsync = promisify(exec);
23
23
  * @returns {Promise<void>}
24
24
  */
25
25
  async function stopMutagenSessions(developerId) {
26
- const { getMutagenPath } = require('../utils/mutagen');
26
+ const { getMutagenPath, listSyncSessionNames } = require('../utils/mutagen');
27
27
  const mutagenPath = await getMutagenPath();
28
28
  if (!mutagenPath) {
29
29
  logger.log(chalk.gray('Mutagen not installed; no sync sessions to stop.'));
30
30
  return;
31
31
  }
32
32
  try {
33
- const { stdout } = await execAsync(`"${mutagenPath}" sync list --template '{{.Name}}'`, {
34
- encoding: 'utf8',
35
- timeout: 5000
36
- });
37
- const sessions = (stdout || '').trim().split('\n').filter(Boolean);
33
+ const sessions = await listSyncSessionNames(mutagenPath);
38
34
  const prefix = `aifabrix-${developerId}-`;
39
35
  const toTerminate = sessions.filter(name => name.startsWith(prefix));
40
36
  for (const name of toTerminate) {