@aifabrix/builder 2.51.1 → 2.52.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 (187) hide show
  1. package/jest.isolated-projects.js +25 -2
  2. package/jest.projects.js +4 -1
  3. package/lib/api/datasources-extended.api.js +8 -8
  4. package/lib/api/keycloak.api.js +92 -0
  5. package/lib/api/token-providers.api.js +77 -0
  6. package/lib/api/types/keycloak.types.js +23 -0
  7. package/lib/api/types/token-providers.types.js +65 -0
  8. package/lib/app/down.js +16 -1
  9. package/lib/app/run-container-start.js +4 -0
  10. package/lib/app/run-deploy-job.js +135 -27
  11. package/lib/app/run-env-compose.js +32 -0
  12. package/lib/app/run-env-recovery.js +35 -0
  13. package/lib/app/run-helpers.js +5 -6
  14. package/lib/app/run.js +11 -3
  15. package/lib/channels/add-channel.js +126 -19
  16. package/lib/channels/approval-guides/chatgpt.js +12 -9
  17. package/lib/channels/approval-guides/microsoft-copilot.js +16 -12
  18. package/lib/channels/channel-add-ensure.js +128 -0
  19. package/lib/channels/channel-artifact-generator.js +11 -1
  20. package/lib/channels/channel-branding.js +94 -0
  21. package/lib/channels/channel-credential.js +8 -0
  22. package/lib/channels/channel-discovery.js +330 -0
  23. package/lib/channels/channel-external-system.js +1 -1
  24. package/lib/channels/channel-runtime-resolver.js +103 -14
  25. package/lib/channels/channel-status-display.js +7 -1
  26. package/lib/channels/channel-targets.js +18 -1
  27. package/lib/channels/chatgpt-runtime-contract.js +1 -1
  28. package/lib/channels/microsoft-copilot-channel-add.js +138 -0
  29. package/lib/channels/microsoft-copilot-entra-sso-form.js +180 -0
  30. package/lib/channels/microsoft-copilot-entra-sso.js +264 -0
  31. package/lib/channels/microsoft-copilot-icons.js +74 -0
  32. package/lib/channels/microsoft-copilot-limits.js +84 -0
  33. package/lib/channels/microsoft-copilot-package.js +130 -49
  34. package/lib/channels/microsoft-copilot-validate.js +197 -0
  35. package/lib/cli/index.js +2 -0
  36. package/lib/cli/infra-guided-footers.js +1 -1
  37. package/lib/cli/infra-guided-public-url-warn.js +55 -0
  38. package/lib/cli/infra-guided.js +78 -62
  39. package/lib/cli/setup-app.help.js +4 -0
  40. package/lib/cli/setup-app.test-commands.js +59 -28
  41. package/lib/cli/setup-external-system.js +50 -12
  42. package/lib/cli/setup-infra.js +31 -4
  43. package/lib/commands/channel-add-setup.js +35 -10
  44. package/lib/commands/channel.js +27 -12
  45. package/lib/commands/deploy-gate-orchestrator.js +3 -2
  46. package/lib/commands/governance-verify-external.js +2 -5
  47. package/lib/commands/integration-definition.js +158 -0
  48. package/lib/commands/lifecycle-external.js +14 -7
  49. package/lib/commands/repair-auth-apply.js +19 -5
  50. package/lib/commands/repair-datasource-sync-operations.js +4 -1
  51. package/lib/commands/role-assistant-test-output.js +200 -0
  52. package/lib/commands/role-assistant.js +131 -114
  53. package/lib/commands/test-governance-external.js +2 -5
  54. package/lib/commands/up-miso.js +61 -24
  55. package/lib/commands/upload-data-sync-finalize.js +20 -3
  56. package/lib/commands/verify-governance-external.js +2 -5
  57. package/lib/commands/verify-operations-external.js +2 -5
  58. package/lib/commands/verify-operations-step-plan.js +31 -0
  59. package/lib/commands/verify-trust-command-action.js +2 -5
  60. package/lib/datasource/capability/capability-resolve.js +15 -5
  61. package/lib/datasource/capability/copy-operations.js +14 -4
  62. package/lib/datasource/datasource-identifier.js +4 -3
  63. package/lib/datasource/integration-context.js +3 -3
  64. package/lib/datasource/resolve-app.js +90 -8
  65. package/lib/datasource/test-e2e.js +2 -2
  66. package/lib/datasource/unified-validation-run-body.js +15 -1
  67. package/lib/datasource/unified-validation-run-resolve.js +2 -2
  68. package/lib/external-system/deploy-helpers.js +9 -5
  69. package/lib/external-system/deploy.js +2 -1
  70. package/lib/external-system/test-system-level.js +21 -3
  71. package/lib/external-system/test.js +4 -4
  72. package/lib/generator/builders.js +9 -15
  73. package/lib/infrastructure/index.js +19 -4
  74. package/lib/integration-definition/apply.js +478 -0
  75. package/lib/integration-definition/canonical-json.js +51 -0
  76. package/lib/integration-definition/emit-suite-bid.js +94 -0
  77. package/lib/integration-definition/enrich-crm-field-mappings.js +240 -0
  78. package/lib/integration-definition/enrich-openapi-operations.js +434 -0
  79. package/lib/integration-definition/from-discovery.js +339 -0
  80. package/lib/integration-definition/hash.js +37 -0
  81. package/lib/integration-definition/index.js +37 -0
  82. package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +205 -0
  83. package/lib/integration-definition/resource-type-catalog.js +59 -0
  84. package/lib/integration-definition/system-kind-enum.js +40 -0
  85. package/lib/integration-definition/validate.js +148 -0
  86. package/lib/programmatic/builder-api-request.js +5 -1
  87. package/lib/programmatic/builder-help-governance.js +3 -2
  88. package/lib/programmatic/capability-registry.js +19 -0
  89. package/lib/programmatic/openapi-component-schemas.js +6 -1
  90. package/lib/programmatic/openapi-descriptions.js +40 -0
  91. package/lib/programmatic/openapi-envelope-examples.js +269 -0
  92. package/lib/programmatic/openapi-operation-bindings.js +45 -0
  93. package/lib/programmatic/openapi-request-schemas.js +14 -0
  94. package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
  95. package/lib/programmatic/openapi-result-schemas.js +46 -99
  96. package/lib/programmatic/openapi-schema-descriptions-core.js +7 -4
  97. package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
  98. package/lib/programmatic/openapi-schema-descriptions-operations.js +24 -0
  99. package/lib/programmatic/openapi-schema-helpers.js +9 -9
  100. package/lib/programmatic/openapi-spec.js +11 -1
  101. package/lib/programmatic/orchestration-openapi-meta.js +10 -0
  102. package/lib/programmatic/route-handler-map.js +29 -1
  103. package/lib/programmatic/run-channel-package.js +236 -0
  104. package/lib/programmatic/run-integration-definition.js +69 -0
  105. package/lib/role-assistant/knowledge-markdown-frontmatter.js +75 -0
  106. package/lib/role-assistant/knowledge-sync-logging.js +58 -0
  107. package/lib/role-assistant/knowledge-sync.js +104 -89
  108. package/lib/role-assistant/test-cases-bid.js +155 -0
  109. package/lib/role-assistant/test-cases-evaluate.js +9 -1
  110. package/lib/role-assistant/test-cases-honesty.js +24 -10
  111. package/lib/role-assistant/test-cases.js +28 -0
  112. package/lib/role-assistant/test-runner-bid-apply.js +122 -0
  113. package/lib/role-assistant/test-runner-bid-document.js +278 -0
  114. package/lib/role-assistant/test-runner-workhub-answers.js +289 -46
  115. package/lib/role-assistant/test-runner-workhub.js +121 -25
  116. package/lib/role-assistant/test-suite.js +119 -0
  117. package/lib/schema/application-schema.json +64 -32
  118. package/lib/schema/external-datasource.schema.json +103 -17
  119. package/lib/schema/external-system.schema.json +54 -3
  120. package/lib/schema/infra.parameter.yaml +0 -20
  121. package/lib/schema/integration-definition.schema.json +195 -0
  122. package/lib/utils/authentication-shape.js +6 -0
  123. package/lib/utils/certify-external-workspace.js +40 -0
  124. package/lib/utils/docker-build.js +3 -3
  125. package/lib/utils/docker-run-ephemeral.js +77 -17
  126. package/lib/utils/health-check-public-warn.js +31 -13
  127. package/lib/utils/health-check.js +11 -1
  128. package/lib/utils/help-builder.js +1 -0
  129. package/lib/utils/paths.js +13 -15
  130. package/lib/utils/promote-dev-docker-image.js +97 -18
  131. package/lib/utils/role-assistant-paths.js +76 -14
  132. package/lib/utils/transient-developer-id.js +28 -0
  133. package/lib/utils/upload-sync-options.js +7 -4
  134. package/lib/validation/reserved-system-key-prefixes.js +56 -0
  135. package/lib/validation/validate.js +54 -19
  136. package/package.json +1 -1
  137. package/scripts/pnpm/af.mjs +24 -9
  138. package/scripts/pnpm/help.mjs +1 -1
  139. package/templates/applications/builder-api/application.yaml +3 -3
  140. package/templates/applications/dataplane/application.yaml +5 -5
  141. package/templates/applications/dataplane/env.template +8 -7
  142. package/templates/applications/dataplane/rbac.yaml +10 -10
  143. package/templates/applications/keycloak/application.yaml +2 -2
  144. package/templates/applications/keycloak/env.template +5 -2
  145. package/templates/applications/miso-controller/application.yaml +25 -11
  146. package/templates/applications/miso-controller/env.template +24 -5
  147. package/templates/channels/README.md +11 -1
  148. package/templates/channels/brand/color-192.png +0 -0
  149. package/templates/channels/brand/dark/128.png +0 -0
  150. package/templates/channels/brand/dark/192.png +0 -0
  151. package/templates/channels/brand/dark/256.png +0 -0
  152. package/templates/channels/brand/dark/32.png +0 -0
  153. package/templates/channels/brand/dark/512.png +0 -0
  154. package/templates/channels/brand/dark/64.png +0 -0
  155. package/templates/channels/brand/light/128.png +0 -0
  156. package/templates/channels/brand/light/192.png +0 -0
  157. package/templates/channels/brand/light/256.png +0 -0
  158. package/templates/channels/brand/light/32.png +0 -0
  159. package/templates/channels/brand/light/512.png +0 -0
  160. package/templates/channels/brand/light/64.png +0 -0
  161. package/templates/channels/brand/mono-white/128.png +0 -0
  162. package/templates/channels/brand/mono-white/192.png +0 -0
  163. package/templates/channels/brand/mono-white/256.png +0 -0
  164. package/templates/channels/brand/mono-white/32.png +0 -0
  165. package/templates/channels/brand/mono-white/512.png +0 -0
  166. package/templates/channels/brand/mono-white/64.png +0 -0
  167. package/templates/channels/brand/outline-32.png +0 -0
  168. package/templates/channels/chatgpt/install.template.md.hbs +76 -36
  169. package/templates/channels/chatgpt/integration/README.md +4 -4
  170. package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
  171. package/templates/channels/chatgpt/manifest.template.json.hbs +3 -3
  172. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +10 -16
  173. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
  174. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +27 -0
  175. package/templates/channels/microsoft-copilot/install.template.md.hbs +39 -20
  176. package/templates/channels/microsoft-copilot/integration/README.md +2 -3
  177. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +1 -22
  178. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +7 -7
  179. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +11 -0
  180. package/templates/channels/slack/install.template.md.hbs +6 -1
  181. package/templates/channels/slack/integration/README.md +2 -4
  182. package/templates/channels/teams-bot/install.template.md.hbs +4 -0
  183. package/templates/channels/teams-bot/integration/README.md +3 -5
  184. package/templates/channels/web-embed/integration/web-embed-system.json +1 -16
  185. package/templates/python/docker-compose.hbs +10 -0
  186. package/templates/typescript/docker-compose.hbs +10 -0
  187. /package/.cursor/plans/{Done → Archive}/157.1-security-quality-violations-by-type.csv +0 -0
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Builder API channel packaging handlers (plan 167.0).
3
+ *
4
+ * @fileoverview Package build / branding / zip for workspace channel systemKeys
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const fs = require('../internal/fs-real-sync');
12
+ const path = require('path');
13
+ const { ensureIntegrationDirectory } = require('./workspace-context');
14
+ const { resolveChannelRuntimeValues } = require('../channels/channel-runtime-resolver');
15
+ const { generateChannelArtifact } = require('../channels/channel-artifact-generator');
16
+ const { loadChannelPackageContext } = require('../channels/channel-discovery');
17
+ const {
18
+ readChannelBranding,
19
+ writeChannelBranding,
20
+ mergeChannelBranding
21
+ } = require('../channels/channel-branding');
22
+ const { isChannelTargetKey } = require('../channels/channel-targets');
23
+
24
+ /**
25
+ * @param {import('./workspace-context').WorkspaceContext} ctx
26
+ * @returns {string}
27
+ */
28
+ function workspaceSystemDir(ctx) {
29
+ return ctx.getIntegrationPath();
30
+ }
31
+
32
+ /**
33
+ * Channel target equals workspace systemKey for packaging apps.
34
+ * @param {string} systemKey
35
+ * @returns {string}
36
+ */
37
+ function requireChannelTarget(systemKey) {
38
+ if (!isChannelTargetKey(systemKey)) {
39
+ throw new Error(
40
+ `systemKey "${systemKey}" is not a supported channel packaging target`
41
+ );
42
+ }
43
+ return systemKey;
44
+ }
45
+
46
+ /**
47
+ * @param {import('./workspace-context').WorkspaceContext} ctx
48
+ * @returns {{ type: string, token: string }}
49
+ */
50
+ function authConfigFromContext(ctx) {
51
+ return { type: 'bearer', token: ctx.bearerToken };
52
+ }
53
+
54
+ /**
55
+ * @param {Object} branding
56
+ * @param {string} channelTarget
57
+ * @returns {Object}
58
+ */
59
+ function fallbackPackageContext(branding, channelTarget) {
60
+ return {
61
+ displayName: branding.displayName || channelTarget,
62
+ description: branding.description || '',
63
+ governedToolNames: [],
64
+ mcpServerUrl: null,
65
+ installReady: false,
66
+ blocker: 'discoveryUnavailable',
67
+ authentication: null
68
+ };
69
+ }
70
+
71
+ /**
72
+ * @param {import('./workspace-context').WorkspaceContext} ctx
73
+ * @param {Object} body
74
+ * @param {string} channelTarget
75
+ * @param {Object} branding
76
+ * @param {string} dataplaneUrl
77
+ * @param {Object} packageContext
78
+ * @returns {Object}
79
+ */
80
+ function generatePackageArtifact(ctx, body, channelTarget, branding, dataplaneUrl, packageContext) {
81
+ const appName =
82
+ branding.displayName || body.name || packageContext.displayName || channelTarget;
83
+ const description =
84
+ branding.description || body.description || packageContext.description || '';
85
+ const runtimeValues = resolveChannelRuntimeValues({
86
+ dataplaneBaseUrl: dataplaneUrl,
87
+ channelTarget,
88
+ appName,
89
+ baseName: body.baseName || appName,
90
+ environment: body.environment || ctx.environment || 'dev',
91
+ appDescription: description,
92
+ layerBToolNames: packageContext.governedToolNames || [],
93
+ packageContext,
94
+ iconColorPath: branding.iconColorPath,
95
+ iconOutlinePath: branding.iconOutlinePath
96
+ });
97
+ return generateChannelArtifact({
98
+ projectRoot: ctx.getUserRoot(),
99
+ channelTarget,
100
+ runtimeValues,
101
+ dryRun: body.dryRun === true
102
+ });
103
+ }
104
+
105
+ /**
106
+ * @param {import('./workspace-context').WorkspaceContext} ctx
107
+ * @param {Object} body
108
+ * @returns {Promise<Object>}
109
+ */
110
+ async function buildChannelPackage(ctx, body = {}) {
111
+ const channelTarget = requireChannelTarget(ctx.systemKey);
112
+ ensureIntegrationDirectory(ctx);
113
+ const dir = workspaceSystemDir(ctx);
114
+ const branding = mergeChannelBranding(readChannelBranding(dir), body.branding || {});
115
+ if (body.branding && typeof body.branding === 'object') {
116
+ writeChannelBranding(dir, branding);
117
+ }
118
+ const dataplaneUrl = ctx.dataplaneUrl || body.dataplaneUrl;
119
+ if (!dataplaneUrl) {
120
+ throw new Error('dataplaneUrl is required to build a channel package');
121
+ }
122
+ const packageContext = await loadChannelPackageContext({
123
+ dataplaneBaseUrl: dataplaneUrl,
124
+ authConfig: authConfigFromContext(ctx),
125
+ channelTarget,
126
+ branding
127
+ }).catch(() => fallbackPackageContext(branding, channelTarget));
128
+ const artifact = generatePackageArtifact(
129
+ ctx,
130
+ body,
131
+ channelTarget,
132
+ branding,
133
+ dataplaneUrl,
134
+ packageContext
135
+ );
136
+ return {
137
+ channelTarget,
138
+ installReady: artifact.installReady === true,
139
+ blocker: artifact.blocker || packageContext.blocker || null,
140
+ artifactHash: artifact.artifactHash,
141
+ links: artifact.links || {
142
+ readme: artifact.installPath,
143
+ zip: artifact.zipPath || null
144
+ },
145
+ summary: artifact.content ? JSON.parse(artifact.content) : null
146
+ };
147
+ }
148
+
149
+ /**
150
+ * @param {import('./workspace-context').WorkspaceContext} ctx
151
+ * @returns {Object}
152
+ */
153
+ function getChannelPackageSummary(ctx) {
154
+ const channelTarget = requireChannelTarget(ctx.systemKey);
155
+ const projectRoot = ctx.getUserRoot();
156
+ const summaryPath = path.join(
157
+ projectRoot,
158
+ 'dist',
159
+ 'channels',
160
+ channelTarget,
161
+ `channel-${channelTarget}.json`
162
+ );
163
+ if (!fs.existsSync(summaryPath)) {
164
+ throw new Error(`Channel package summary not found: ${summaryPath}`);
165
+ }
166
+ const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8'));
167
+ return {
168
+ channelTarget,
169
+ summary,
170
+ links: summary.links || null,
171
+ installReady: summary.installReady === true,
172
+ blocker: summary.blocker || null
173
+ };
174
+ }
175
+
176
+ /**
177
+ * @param {import('./workspace-context').WorkspaceContext} ctx
178
+ * @returns {{ fileName: string, contentType: string, byteLength: number, zipBase64: string }}
179
+ */
180
+ function getChannelPackageZip(ctx) {
181
+ const channelTarget = requireChannelTarget(ctx.systemKey);
182
+ const summary = getChannelPackageSummary(ctx).summary;
183
+ const zipPath =
184
+ typeof summary?.links?.zip === 'string' && summary.links.zip
185
+ ? summary.links.zip
186
+ : path.join(
187
+ ctx.getUserRoot(),
188
+ 'dist',
189
+ 'channels',
190
+ channelTarget,
191
+ 'build',
192
+ `appPackage.${summary.environment || 'dev'}.zip`
193
+ );
194
+ if (!fs.existsSync(zipPath)) {
195
+ throw new Error(`Channel package ZIP not found: ${zipPath}`);
196
+ }
197
+ const buffer = fs.readFileSync(zipPath);
198
+ return {
199
+ fileName: path.basename(zipPath),
200
+ contentType: 'application/zip',
201
+ byteLength: buffer.length,
202
+ zipBase64: buffer.toString('base64')
203
+ };
204
+ }
205
+
206
+ /**
207
+ * @param {import('./workspace-context').WorkspaceContext} ctx
208
+ * @param {Object} body
209
+ * @returns {Object}
210
+ */
211
+ function patchChannelBranding(ctx, body = {}) {
212
+ requireChannelTarget(ctx.systemKey);
213
+ ensureIntegrationDirectory(ctx);
214
+ const branding = writeChannelBranding(workspaceSystemDir(ctx), body || {});
215
+ return { systemKey: ctx.systemKey, branding };
216
+ }
217
+
218
+ /**
219
+ * @param {import('./workspace-context').WorkspaceContext} ctx
220
+ * @returns {Object}
221
+ */
222
+ function getChannelBranding(ctx) {
223
+ requireChannelTarget(ctx.systemKey);
224
+ return {
225
+ systemKey: ctx.systemKey,
226
+ branding: readChannelBranding(workspaceSystemDir(ctx))
227
+ };
228
+ }
229
+
230
+ module.exports = {
231
+ buildChannelPackage,
232
+ getChannelPackageSummary,
233
+ getChannelPackageZip,
234
+ patchChannelBranding,
235
+ getChannelBranding
236
+ };
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Apply Builder Integration Definition (BID) into a user workspace.
3
+ *
4
+ * @fileoverview POST …/integration-definition/apply
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const path = require('path');
12
+ const {
13
+ applyIntegrationDefinition,
14
+ withDefinitionHash
15
+ } = require('../integration-definition');
16
+ const {
17
+ runWithWorkspaceContext,
18
+ ensureIntegrationDirectory,
19
+ WORKSPACE_INTEGRATION_FORMAT
20
+ } = require('./workspace-context');
21
+ const { touchWorkspaceMeta } = require('./workspace-meta');
22
+
23
+ const OPENAPI_ENRICHMENT_DIR = path.join(
24
+ __dirname,
25
+ '../integration-definition/openapi-enrichment'
26
+ );
27
+
28
+ /**
29
+ * @param {import('./workspace-context').WorkspaceContext} ctx
30
+ * @param {Object} body
31
+ * @param {Object} [body.integrationDefinition]
32
+ * @param {boolean} [body.force]
33
+ * @param {boolean} [body.dryRun]
34
+ * @returns {Promise<Object>}
35
+ */
36
+ async function applyIntegrationDefinitionInWorkspace(ctx, body = {}) {
37
+ const bidInput = body.integrationDefinition || body.bid || body;
38
+ if (!bidInput || typeof bidInput !== 'object') {
39
+ throw new Error('integrationDefinition object is required in request body');
40
+ }
41
+ const force = body.force === true;
42
+ const dryRun = body.dryRun === true;
43
+ return runWithWorkspaceContext(ctx, async() => {
44
+ ensureIntegrationDirectory(ctx);
45
+ const bid = withDefinitionHash(bidInput);
46
+ const identity = bid.packageIdentity || {};
47
+ const bidKey = String(identity.systemKey || '').trim();
48
+ if (bidKey && bidKey !== ctx.systemKey) {
49
+ throw new Error(
50
+ `BID packageIdentity.systemKey (${bidKey}) must match path systemKey (${ctx.systemKey})`
51
+ );
52
+ }
53
+ const out = await applyIntegrationDefinition(bid, {
54
+ outputDir: ctx.getIntegrationPath(),
55
+ force,
56
+ dryRun,
57
+ openapiBaseDir: OPENAPI_ENRICHMENT_DIR
58
+ });
59
+ touchWorkspaceMeta(ctx, {
60
+ lastOperation: 'integration-definition-apply',
61
+ format: WORKSPACE_INTEGRATION_FORMAT,
62
+ definitionId: out.definitionId,
63
+ definitionHash: out.definitionHash
64
+ });
65
+ return out;
66
+ });
67
+ }
68
+
69
+ module.exports = { applyIntegrationDefinitionInWorkspace };
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Parse optional YAML frontmatter from Role Assistant knowledge markdown.
3
+ *
4
+ * @fileoverview knowledge-markdown-frontmatter
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const yaml = require('js-yaml');
12
+
13
+ const SEARCH_META_KEYS = [
14
+ 'aspect',
15
+ 'knowledgeType',
16
+ 'knowledgeKey',
17
+ 'title',
18
+ 'resourceType',
19
+ 'roleKey'
20
+ ];
21
+
22
+ /**
23
+ * Split markdown into optional YAML frontmatter and body.
24
+ * @param {string} content
25
+ * @returns {{ frontmatter: Object|null, body: string }}
26
+ */
27
+ function splitMarkdownFrontmatter(content) {
28
+ const text = typeof content === 'string' ? content : '';
29
+ if (!text.startsWith('---')) {
30
+ return { frontmatter: null, body: text };
31
+ }
32
+ const end = text.indexOf('\n---', 3);
33
+ if (end < 0) {
34
+ return { frontmatter: null, body: text };
35
+ }
36
+ const raw = text.slice(3, end).trim();
37
+ const body = text.slice(end + 4).replace(/^(?:\r?\n)+/, '');
38
+ if (!raw) {
39
+ return { frontmatter: {}, body };
40
+ }
41
+ try {
42
+ const parsed = yaml.load(raw);
43
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
44
+ return { frontmatter: null, body: text };
45
+ }
46
+ return { frontmatter: parsed, body };
47
+ } catch (_error) {
48
+ return { frontmatter: null, body: text };
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Project searchable metadata fields from frontmatter.
54
+ * @param {Object|null|undefined} frontmatter
55
+ * @returns {Object}
56
+ */
57
+ function knowledgeSearchMetadataFromFrontmatter(frontmatter) {
58
+ const out = {};
59
+ if (!frontmatter || typeof frontmatter !== 'object') {
60
+ return out;
61
+ }
62
+ for (const key of SEARCH_META_KEYS) {
63
+ const value = frontmatter[key];
64
+ if (typeof value === 'string' && value.trim()) {
65
+ out[key] = value.trim();
66
+ }
67
+ }
68
+ return out;
69
+ }
70
+
71
+ module.exports = {
72
+ SEARCH_META_KEYS,
73
+ splitMarkdownFrontmatter,
74
+ knowledgeSearchMetadataFromFrontmatter
75
+ };
@@ -0,0 +1,58 @@
1
+ 'use strict';
2
+
3
+ const logger = require('../utils/logger');
4
+ const { formatSuccessLine, formatWarningLine, metadata } = require('../utils/cli-test-layout-chalk');
5
+
6
+ function logKnowledgeImportSummary(systemKey, result, options = {}) {
7
+ if (result.skipped) {
8
+ if (options.verbose === true) {
9
+ logger.log(metadata(`Knowledge sync skipped (${systemKey}): ${result.reason}`));
10
+ }
11
+ return;
12
+ }
13
+ if (result.dryRun) {
14
+ logger.log(formatWarningLine(
15
+ `Role assistant knowledge dry-run: ${result.fileCount} file(s), ${result.recordCount} record(s), datasource ${result.datasourceKey}`
16
+ ));
17
+ return;
18
+ }
19
+ logger.log(formatSuccessLine(
20
+ `Role assistant knowledge synced (${result.datasourceKey}): ${result.recordCount} record(s) from ${result.fileCount} file(s).`
21
+ ));
22
+ }
23
+
24
+ function logDocumentStorageSettingsPushSummary(systemKey, result, options = {}) {
25
+ if (result.skipped) {
26
+ if (options.verbose === true) {
27
+ logger.log(metadata(`Knowledge document storage settings skipped (${systemKey}): ${result.reason}`));
28
+ }
29
+ return;
30
+ }
31
+ if (result.dryRun) {
32
+ logger.log(formatWarningLine(
33
+ `Knowledge document storage settings dry-run (${systemKey}): ${result.documentStorageKey || '(clear)'}`
34
+ ));
35
+ return;
36
+ }
37
+ logger.log(formatSuccessLine(
38
+ `Knowledge document storage settings updated (${systemKey}): ${result.documentStorageKey || '(cleared)'}`
39
+ ));
40
+ }
41
+
42
+ function logKnowledgeExportSummary(systemKey, result, options = {}) {
43
+ if (result.skipped) {
44
+ if (options.verbose === true) {
45
+ logger.log(metadata(`Knowledge export skipped (${systemKey}): ${result.reason}`));
46
+ }
47
+ return;
48
+ }
49
+ logger.log(formatSuccessLine(
50
+ `Role assistant knowledge exported (${result.datasourceKey}): ${result.count} document(s) to ${result.knowledgePath}.`
51
+ ));
52
+ }
53
+
54
+ module.exports = {
55
+ logKnowledgeImportSummary,
56
+ logDocumentStorageSettingsPushSummary,
57
+ logKnowledgeExportSummary
58
+ };