@aifabrix/builder 2.54.1 → 2.55.2

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 (253) hide show
  1. package/README.md +8 -0
  2. package/lib/agent-kit/apply-ops.js +82 -0
  3. package/lib/agent-kit/checksums.js +28 -0
  4. package/lib/agent-kit/classify.js +113 -0
  5. package/lib/agent-kit/claude-shim.js +61 -0
  6. package/lib/agent-kit/constants.js +55 -0
  7. package/lib/agent-kit/file-list.js +103 -0
  8. package/lib/agent-kit/host-approve.js +172 -0
  9. package/lib/agent-kit/hosting/github.js +63 -0
  10. package/lib/agent-kit/hosts.js +71 -0
  11. package/lib/agent-kit/init.js +319 -0
  12. package/lib/agent-kit/kit-root.js +17 -0
  13. package/lib/agent-kit/managed-section.js +79 -0
  14. package/lib/agent-kit/package-load.js +45 -0
  15. package/lib/agent-kit/plan-ops.js +69 -0
  16. package/lib/agent-kit/remote-prompts.js +272 -0
  17. package/lib/agent-kit/render-host-markdown.js +40 -0
  18. package/lib/agent-kit/repo-guard.js +47 -0
  19. package/lib/agent-kit/report.js +107 -0
  20. package/lib/agent-kit/run.js +82 -0
  21. package/lib/agent-kit/scm/git.js +122 -0
  22. package/lib/agent-kit/start.js +310 -0
  23. package/lib/agent-kit/state-file.js +83 -0
  24. package/lib/agent-kit/status.js +50 -0
  25. package/lib/agent-kit/v1-detect.js +81 -0
  26. package/lib/agent-kit/validate-kit.js +87 -0
  27. package/lib/agent-kit/workspace-name.js +40 -0
  28. package/lib/agent-kit/workspace-skeleton.js +103 -0
  29. package/lib/api/role-assistant-test-pack.api.js +86 -0
  30. package/lib/api/work-search.api.js +31 -4
  31. package/lib/api/work.api.js +19 -12
  32. package/lib/app/deploy.js +7 -4
  33. package/lib/app/list-external-systems.js +2 -1
  34. package/lib/app/offer-create-from-template.js +184 -0
  35. package/lib/app/show-display.js +33 -6
  36. package/lib/app/show.js +25 -2
  37. package/lib/channels/add-channel.js +1 -0
  38. package/lib/channels/approval-guides/claude.js +2 -2
  39. package/lib/channels/channel-add-failure.js +1 -0
  40. package/lib/channels/channel-add-remaining.js +15 -9
  41. package/lib/channels/channel-definition-status.js +115 -0
  42. package/lib/channels/channel-description.js +2 -1
  43. package/lib/channels/channel-install-envelope.js +4 -2
  44. package/lib/channels/channel-integration-generator.js +2 -0
  45. package/lib/channels/channel-runtime-resolver.js +14 -0
  46. package/lib/channels/channel-targets.js +31 -5
  47. package/lib/channels/claude-setup-values.js +4 -1
  48. package/lib/channels/slack-channel-add.js +59 -6
  49. package/lib/channels/slack-mcp-callback.js +51 -0
  50. package/lib/channels/slack-mcp-runtime.js +156 -0
  51. package/lib/channels/slack-provisioning/install-cli.js +14 -2
  52. package/lib/channels/slack-provisioning/with-curl-on-path.js +6 -1
  53. package/lib/cli/index.js +4 -0
  54. package/lib/cli/setup-app.js +8 -1
  55. package/lib/cli/setup-auth.js +11 -2
  56. package/lib/cli/setup-certification-commands.js +2 -2
  57. package/lib/cli/setup-certification.help.js +3 -1
  58. package/lib/commands/agent-kit-help.js +149 -0
  59. package/lib/commands/agent-kit.js +417 -0
  60. package/lib/commands/auth-status-user.js +99 -0
  61. package/lib/commands/auth-status.js +14 -4
  62. package/lib/commands/channel-add-help.js +5 -5
  63. package/lib/commands/datasource-capability-relate-cli.js +5 -4
  64. package/lib/commands/datasource-load-export-cli.js +7 -2
  65. package/lib/commands/datasource-test-trust-cli.js +5 -1
  66. package/lib/commands/governance-command-action.js +5 -4
  67. package/lib/commands/identity-membership-role-cache.js +6 -1
  68. package/lib/commands/lifecycle-command-action.js +16 -15
  69. package/lib/commands/login-credentials.js +8 -3
  70. package/lib/commands/login-device.js +14 -5
  71. package/lib/commands/login.js +14 -1
  72. package/lib/commands/logout.js +50 -15
  73. package/lib/commands/repair-auth-apply.js +1 -1
  74. package/lib/commands/repair-datasource-key-skip.js +39 -0
  75. package/lib/commands/repair-datasource-keys.js +24 -12
  76. package/lib/commands/repair-rbac.js +16 -10
  77. package/lib/commands/role-assistant-copy-output.js +61 -0
  78. package/lib/commands/role-assistant-copy.js +155 -0
  79. package/lib/commands/role-assistant-test-output.js +8 -9
  80. package/lib/commands/role-assistant-test-result-rows.js +42 -0
  81. package/lib/commands/role-assistant.js +140 -59
  82. package/lib/commands/search.js +143 -0
  83. package/lib/commands/test-trust-external.js +7 -5
  84. package/lib/commands/upload-sidecar-followups.js +4 -1
  85. package/lib/commands/verify-governance-command-action.js +9 -7
  86. package/lib/commands/verify-operations-command-action.js +9 -8
  87. package/lib/commands/verify-operations-skip-e2e.js +131 -0
  88. package/lib/commands/verify-operations-step-plan.js +6 -33
  89. package/lib/commands/verify-operations-steps.js +3 -1
  90. package/lib/commands/verify-trust-command-action.js +6 -5
  91. package/lib/datasource/capability/relate-operations.js +28 -10
  92. package/lib/datasource/capability/run-capability-relate.js +48 -25
  93. package/lib/datasource/datasource-identifier.js +13 -5
  94. package/lib/deployment/deployer-poll-run.js +117 -35
  95. package/lib/external-system/deploy-dataplane-published.js +70 -0
  96. package/lib/external-system/deploy.js +12 -2
  97. package/lib/external-system/download-role-assistant.js +41 -15
  98. package/lib/external-system/download.js +8 -3
  99. package/lib/generator/helpers.js +12 -1
  100. package/lib/generator/wizard-datasource-key.js +54 -0
  101. package/lib/generator/wizard.js +55 -54
  102. package/lib/integration-definition/apply-datasource-keys.js +75 -0
  103. package/lib/integration-definition/apply-relationships.js +195 -0
  104. package/lib/integration-definition/apply-static-enterprise.js +232 -0
  105. package/lib/integration-definition/apply-system-file.js +71 -0
  106. package/lib/integration-definition/apply-test-endpoint.js +89 -0
  107. package/lib/integration-definition/apply-viewpoint-defaults.js +157 -0
  108. package/lib/integration-definition/apply.js +78 -59
  109. package/lib/integration-definition/enrich-openapi-operations.js +36 -10
  110. package/lib/integration-definition/enrich-openapi-pick.js +87 -0
  111. package/lib/lifecycle/role-assistant-governance.js +56 -21
  112. package/lib/programmatic/capability-registry.js +1 -0
  113. package/lib/programmatic/index.js +2 -0
  114. package/lib/programmatic/openapi-apply-result-schemas.js +8 -0
  115. package/lib/programmatic/openapi-channel-install-schemas.js +4 -1
  116. package/lib/programmatic/openapi-component-schemas.js +1 -0
  117. package/lib/programmatic/openapi-descriptions.js +9 -3
  118. package/lib/programmatic/openapi-envelope-examples.js +7 -0
  119. package/lib/programmatic/openapi-operation-bindings.js +5 -0
  120. package/lib/programmatic/openapi-request-schemas.js +5 -0
  121. package/lib/programmatic/openapi-schema-descriptions-channel.js +8 -5
  122. package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
  123. package/lib/programmatic/openapi-spec.js +1 -0
  124. package/lib/programmatic/request-timeout.js +3 -0
  125. package/lib/programmatic/route-handler-map.js +7 -0
  126. package/lib/programmatic/run-channel-catalog.js +50 -8
  127. package/lib/programmatic/run-channel-install-options.js +5 -1
  128. package/lib/programmatic/run-download.js +41 -0
  129. package/lib/protection/auth-context.js +3 -1
  130. package/lib/protection/protection-display.js +3 -2
  131. package/lib/protection/report-exit.js +50 -0
  132. package/lib/protection/run-commands.js +2 -0
  133. package/lib/role-assistant/knowledge-sync-settings-push.js +172 -0
  134. package/lib/role-assistant/knowledge-sync.js +9 -126
  135. package/lib/role-assistant/package-validate.js +175 -0
  136. package/lib/role-assistant/test-case-assertion-row.js +36 -0
  137. package/lib/role-assistant/test-case-copy-bind.js +77 -0
  138. package/lib/role-assistant/test-case-copy-expected.js +127 -0
  139. package/lib/role-assistant/test-case-copy-from-work.js +90 -0
  140. package/lib/role-assistant/test-case-copy-interactions.js +67 -0
  141. package/lib/role-assistant/test-case-copy.js +119 -0
  142. package/lib/role-assistant/test-case-expected-catalog.js +135 -0
  143. package/lib/role-assistant/test-case-validate.js +88 -0
  144. package/lib/role-assistant/test-cases-bid.js +117 -4
  145. package/lib/role-assistant/test-cases-evaluate.js +149 -31
  146. package/lib/role-assistant/test-cases-evidence.js +44 -5
  147. package/lib/role-assistant/test-cases-honesty.js +155 -6
  148. package/lib/role-assistant/test-cases-lifecycle.js +297 -0
  149. package/lib/role-assistant/test-cases-search-graph.js +122 -0
  150. package/lib/role-assistant/test-cases-search.js +20 -2
  151. package/lib/role-assistant/test-cases.js +34 -168
  152. package/lib/role-assistant/test-pack-map.js +180 -0
  153. package/lib/role-assistant/test-pack-sync.js +187 -0
  154. package/lib/role-assistant/test-runner-debug.js +54 -0
  155. package/lib/role-assistant/test-runner-respond-body.js +36 -0
  156. package/lib/role-assistant/test-runner-search.js +20 -5
  157. package/lib/role-assistant/test-runner-start-payload.js +63 -10
  158. package/lib/role-assistant/test-runner-workhub-answers.js +45 -16
  159. package/lib/role-assistant/test-runner-workhub.js +45 -21
  160. package/lib/role-assistant/test-runner.js +53 -10
  161. package/lib/schema/external-datasource.schema.json +36 -5
  162. package/lib/schema/external-system.schema.json +20 -2
  163. package/lib/schema/infra-parameter.schema.json +51 -3
  164. package/lib/schema/infra.parameter.yaml +476 -40
  165. package/lib/schema/role-assistant-test-case.schema.json +408 -0
  166. package/lib/schema/type/document-storage.json +1 -1
  167. package/lib/search/dataplane-context.js +23 -0
  168. package/lib/search/search-args.js +211 -0
  169. package/lib/search/search-display.js +238 -0
  170. package/lib/search/search-errors.js +50 -0
  171. package/lib/search/search-prompts.js +119 -0
  172. package/lib/search/search-roles.js +85 -0
  173. package/lib/search/search-run.js +241 -0
  174. package/lib/search/search-skills.js +198 -0
  175. package/lib/utils/api-required-permissions.js +271 -0
  176. package/lib/utils/api.js +16 -13
  177. package/lib/utils/authentication-slot-show.js +153 -0
  178. package/lib/utils/cli-json-exit.js +41 -0
  179. package/lib/utils/cli-utils.js +23 -5
  180. package/lib/utils/controller-url.js +78 -4
  181. package/lib/utils/credential-display.js +43 -1
  182. package/lib/utils/deployment-errors.js +15 -0
  183. package/lib/utils/deployment-validation-helpers.js +4 -1
  184. package/lib/utils/device-code.js +31 -10
  185. package/lib/utils/error-formatters/error-parser.js +16 -1
  186. package/lib/utils/error-formatters/permission-errors.js +19 -5
  187. package/lib/utils/external-system-readiness-compact-blocks.js +5 -1
  188. package/lib/utils/external-system-readiness-compact-display.js +30 -1
  189. package/lib/utils/external-system-validators.js +2 -1
  190. package/lib/utils/help-builder.js +3 -1
  191. package/lib/utils/keycloak-realm-ready.js +5 -2
  192. package/lib/utils/logger.js +1 -0
  193. package/lib/utils/paths.js +26 -5
  194. package/lib/utils/schema-loader.js +37 -0
  195. package/lib/utils/secrets-helpers.js +2 -2
  196. package/lib/utils/write-stream-and-wait.js +63 -0
  197. package/lib/validation/validate-external-file.js +5 -1
  198. package/lib/validation/validate.js +3 -2
  199. package/package.json +20 -6
  200. package/templates/agent-kit/adapters/claude/nested.CLAUDE.md +1 -0
  201. package/templates/agent-kit/adapters/claude/root.CLAUDE.md +5 -0
  202. package/templates/agent-kit/adapters/cursor/commands/cust-0-plan.md +5 -0
  203. package/templates/agent-kit/adapters/cursor/commands/cust-1-validate.md +5 -0
  204. package/templates/agent-kit/adapters/cursor/commands/cust-2-build.md +5 -0
  205. package/templates/agent-kit/adapters/cursor/commands/cust-3-push.md +5 -0
  206. package/templates/agent-kit/adapters/cursor/commands/cust-biz-1-validate.md +5 -0
  207. package/templates/agent-kit/adapters/cursor/commands/cust-biz-2-build.md +5 -0
  208. package/templates/agent-kit/adapters/cursor/commands/cust-biz-3-prove.md +5 -0
  209. package/templates/agent-kit/agent-kit.yaml +45 -0
  210. package/templates/agent-kit/instructions/AGENTKIT.md +104 -0
  211. package/templates/agent-kit/instructions/integration.AGENTS.md +25 -0
  212. package/templates/agent-kit/instructions/role-assistant.AGENTS.md +22 -0
  213. package/templates/agent-kit/instructions/root.AGENTS.md +37 -0
  214. package/templates/agent-kit/parity-inventory.yaml +188 -0
  215. package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +59 -0
  216. package/templates/agent-kit/skills/aifabrix-connected-system/references/contract-checklist.md +40 -0
  217. package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +54 -0
  218. package/templates/agent-kit/skills/aifabrix-connected-system/references/production-safety.md +35 -0
  219. package/templates/agent-kit/skills/aifabrix-connected-system/scripts/reserved-system-key.js +19 -0
  220. package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +52 -0
  221. package/templates/agent-kit/skills/aifabrix-plan/references/challenge-checklist.md +108 -0
  222. package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +34 -0
  223. package/templates/agent-kit/skills/aifabrix-plan/references/plan-template.md +182 -0
  224. package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +51 -0
  225. package/templates/agent-kit/skills/aifabrix-prove/references/demo-template.md +65 -0
  226. package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +68 -0
  227. package/templates/agent-kit/skills/aifabrix-prove/references/learning-template.md +66 -0
  228. package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +43 -0
  229. package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
  230. package/templates/agent-kit/skills/shared/hosts.md +65 -0
  231. package/templates/agent-kit/skills/shared/login.md +16 -0
  232. package/templates/agent-kit/workspace/.cursor/plans/README.md +22 -0
  233. package/templates/agent-kit/workspace/LICENSE.mit +21 -0
  234. package/templates/agent-kit/workspace/README.md +89 -0
  235. package/templates/applications/builder-api/Dockerfile +3 -1
  236. package/templates/applications/dataplane/application.yaml +2 -3
  237. package/templates/applications/dataplane/env.template +5 -5
  238. package/templates/applications/dataplane/rbac.yaml +28 -0
  239. package/templates/applications/keycloak/env.template +3 -3
  240. package/templates/applications/miso-controller/application.yaml +21 -2
  241. package/templates/applications/miso-controller/env.template +96 -25
  242. package/templates/applications/miso-controller/rbac.yaml +5 -0
  243. package/templates/channels/claude/TROUBLESHOOTING.md +3 -0
  244. package/templates/channels/claude/channel.json +1 -1
  245. package/templates/channels/claude/install.template.md.hbs +1 -0
  246. package/templates/channels/claude/integration/claude-system.json +1 -1
  247. package/templates/channels/slack/channel.json +11 -0
  248. package/templates/channels/slack/install.template.md.hbs +9 -0
  249. package/templates/channels/slack/integration/slack-system.json +11 -0
  250. package/templates/channels/slack/manifest.template.json.hbs +6 -3
  251. package/templates/channels/teams-bot/channel.json +1 -0
  252. package/templates/python/docker-compose.hbs +14 -0
  253. package/templates/typescript/docker-compose.hbs +14 -0
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Viewpoint grain and inherited date filter keys for BID apply.
3
+ *
4
+ * @fileoverview Prefer joined storage resourceType; copy date filterable fields
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { remapKey } = require('./apply-datasource-keys');
10
+
11
+ /**
12
+ * @param {unknown} format
13
+ * @returns {boolean}
14
+ */
15
+ function isDateFormat(format) {
16
+ const raw = String(format || '').toLowerCase();
17
+ return raw === 'date' || raw === 'date-time' || raw === 'datetime';
18
+ }
19
+
20
+ /**
21
+ * @param {Record<string, unknown>} body
22
+ * @returns {string[]}
23
+ */
24
+ function dateFilterableKeys(body) {
25
+ const props = body.metadataSchema && body.metadataSchema.properties;
26
+ if (!props || typeof props !== 'object') {
27
+ return [];
28
+ }
29
+ const exposed = body.exposed && Array.isArray(body.exposed.filterable)
30
+ ? body.exposed.filterable
31
+ : [];
32
+ const keys = [];
33
+ for (const [name, node] of Object.entries(props)) {
34
+ if (!node || typeof node !== 'object') {
35
+ continue;
36
+ }
37
+ if (!isDateFormat(node.format)) {
38
+ continue;
39
+ }
40
+ if (exposed.includes(name) || node.filter === true) {
41
+ keys.push(name);
42
+ }
43
+ }
44
+ return keys;
45
+ }
46
+
47
+ /**
48
+ * @param {Record<string, unknown>} viewBody
49
+ * @param {Record<string, unknown>} factBody
50
+ * @returns {void}
51
+ */
52
+ function copyOneDateField(viewBody, factBody, key, props, filterable) {
53
+ const src = factBody.metadataSchema.properties[key];
54
+ if (!props[key]) {
55
+ props[key] = { ...src, filter: true };
56
+ } else if (typeof props[key] === 'object') {
57
+ props[key].filter = true;
58
+ if (!props[key].format && src && src.format) {
59
+ props[key].format = src.format;
60
+ }
61
+ }
62
+ if (!filterable.includes(key)) {
63
+ filterable.push(key);
64
+ }
65
+ }
66
+
67
+ function copyDateFilterableOntoView(viewBody, factBody) {
68
+ const keys = dateFilterableKeys(factBody);
69
+ if (keys.length === 0) {
70
+ return;
71
+ }
72
+ if (!viewBody.metadataSchema || typeof viewBody.metadataSchema !== 'object') {
73
+ viewBody.metadataSchema = { type: 'object', properties: {} };
74
+ }
75
+ const schema = /** @type {Record<string, unknown>} */ (viewBody.metadataSchema);
76
+ if (!schema.properties || typeof schema.properties !== 'object') {
77
+ schema.properties = {};
78
+ }
79
+ const props = /** @type {Record<string, unknown>} */ (schema.properties);
80
+ if (!viewBody.exposed || typeof viewBody.exposed !== 'object') {
81
+ viewBody.exposed = {};
82
+ }
83
+ const exposed = /** @type {Record<string, unknown>} */ (viewBody.exposed);
84
+ const filterable = Array.isArray(exposed.filterable) ? [...exposed.filterable] : [];
85
+ for (const key of keys) {
86
+ copyOneDateField(viewBody, factBody, key, props, filterable);
87
+ }
88
+ exposed.filterable = filterable;
89
+ }
90
+
91
+ /**
92
+ * @param {Record<string, unknown>} body
93
+ * @returns {boolean}
94
+ */
95
+ function isViewpointBody(body) {
96
+ const entityType = String(body.entityType || '');
97
+ const storageType = String(body.storageType || '');
98
+ return entityType === 'viewpoint' || storageType === 'view';
99
+ }
100
+
101
+ /**
102
+ * @param {Array<{ body: Record<string, unknown> }>} dsFiles
103
+ * @returns {Map<string, Record<string, unknown>>}
104
+ */
105
+ function bodiesByKey(dsFiles) {
106
+ const map = new Map();
107
+ for (const file of dsFiles || []) {
108
+ const key = String(file.body && file.body.key ? file.body.key : '').trim();
109
+ if (key) {
110
+ map.set(key, file.body);
111
+ }
112
+ }
113
+ return map;
114
+ }
115
+
116
+ /**
117
+ * @param {Array<{ body: Record<string, unknown> }>} dsFiles
118
+ * @param {unknown[]} relationships
119
+ * @param {Map<string, string>} keyMap
120
+ * @returns {void}
121
+ */
122
+ function applyOneViewpoint(viewBody, rels, keyMap, bodies) {
123
+ const fromKey = String(viewBody.key || '');
124
+ const rel = rels.find((row) => row && remapKey(String(row.from || ''), keyMap) === fromKey);
125
+ if (!rel || typeof rel !== 'object') {
126
+ return;
127
+ }
128
+ const factKey = remapKey(String(rel.to || ''), keyMap);
129
+ const factBody = bodies.get(factKey);
130
+ if (!factBody) {
131
+ return;
132
+ }
133
+ const factType = String(factBody.resourceType || '').trim();
134
+ const viewType = String(viewBody.resourceType || '').trim();
135
+ if (factType && (!viewType || viewType === 'document')) {
136
+ viewBody.resourceType = factType;
137
+ }
138
+ copyDateFilterableOntoView(viewBody, factBody);
139
+ }
140
+
141
+ function applyViewpointDefaults(dsFiles, relationships, keyMap) {
142
+ const bodies = bodiesByKey(dsFiles);
143
+ const rels = Array.isArray(relationships) ? relationships : [];
144
+ for (const file of dsFiles || []) {
145
+ const viewBody = file.body;
146
+ if (!viewBody || !isViewpointBody(viewBody)) {
147
+ continue;
148
+ }
149
+ applyOneViewpoint(viewBody, rels, keyMap, bodies);
150
+ }
151
+ }
152
+
153
+ module.exports = {
154
+ applyViewpointDefaults,
155
+ dateFilterableKeys,
156
+ copyDateFilterableOntoView
157
+ };
@@ -9,6 +9,15 @@ const path = require('path');
9
9
  const fsReal = require('../internal/fs-real-sync');
10
10
  const { getIntegrationPath } = require('../utils/paths');
11
11
  const { validateIntegrationDefinition } = require('./validate');
12
+ const {
13
+ canonicalDatasourceKeyMap,
14
+ datasourceFileName
15
+ } = require('./apply-datasource-keys');
16
+ const { applyBidRelationships } = require('./apply-relationships');
17
+ const { applyStaticAndEnterprise } = require('./apply-static-enterprise');
18
+ const { applyViewpointDefaults } = require('./apply-viewpoint-defaults');
19
+ const { stampCredentialTestEndpoint } = require('./apply-test-endpoint');
20
+ const { buildSystemFile } = require('./apply-system-file');
12
21
 
13
22
  const STAMP_FILE = '.aifabrix-integration-definition.json';
14
23
 
@@ -96,6 +105,21 @@ function buildDatasourceSchema(labelField) {
96
105
  };
97
106
  }
98
107
 
108
+ function mergeBidSchemaProperties(body, ds) {
109
+ const extra = ds.metadataSchema && ds.metadataSchema.properties;
110
+ if (!extra || typeof extra !== 'object') {
111
+ return;
112
+ }
113
+ if (!body.metadataSchema || typeof body.metadataSchema !== 'object') {
114
+ body.metadataSchema = { type: 'object', properties: {} };
115
+ }
116
+ const schema = /** @type {Record<string, unknown>} */ (body.metadataSchema);
117
+ if (!schema.properties || typeof schema.properties !== 'object') {
118
+ schema.properties = {};
119
+ }
120
+ Object.assign(schema.properties, extra);
121
+ }
122
+
99
123
  function enrichDatasourceFile(body, ds, enrichOptions) {
100
124
  const { enrichDatasourceFromOpenApi } = require('./enrich-openapi-operations');
101
125
  const openapiSourceRef =
@@ -133,6 +157,8 @@ function buildDatasourceFile(ds, systemKey, capabilities, sync, enrichOptions =
133
157
  entityType: ds.entityType || 'recordStorage',
134
158
  resourceType: ds.resourceType,
135
159
  version: '1.0.0',
160
+ ...(ds.storageType ? { storageType: ds.storageType } : {}),
161
+ ...(ds.dimensions && typeof ds.dimensions === 'object' ? { dimensions: ds.dimensions } : {}),
136
162
  capabilities: buildDatasourceCapabilities(caps, key),
137
163
  // Schema sync is mode/schedule/batchSize only; BID sync.mode=incremental is operational intent.
138
164
  sync: { mode: 'pull', schedule: /** @type {any} */ (sync).schedule || '0 */6 * * *', batchSize: 100 },
@@ -143,6 +169,7 @@ function buildDatasourceFile(ds, systemKey, capabilities, sync, enrichOptions =
143
169
  // Mirror capabilities → openapi.operations + execution.cip.operations stubs (154.7).
144
170
  repairSyncOperationsFromCapabilities(body, []);
145
171
  seedPropertyKeysOnDatasource(body, ds.propertyKeys);
172
+ mergeBidSchemaProperties(body, ds);
146
173
  enrichDatasourceFile(body, ds, enrichOptions);
147
174
  return body;
148
175
  }
@@ -192,42 +219,6 @@ function collectAssociationObjectTypes(datasources, systemOpenapiSourceRef, enri
192
219
  return [...types];
193
220
  }
194
221
 
195
- /**
196
- * @param {Record<string, unknown>} bid
197
- * @returns {Record<string, unknown>}
198
- */
199
- function buildSystemFile(bid) {
200
- const system = /** @type {Record<string, unknown>} */ (bid.system);
201
- const identity = /** @type {Record<string, unknown>} */ (bid.packageIdentity);
202
- const refs = /** @type {Record<string, string>} */ (system.connectionRefs || {});
203
- const tokenRef = refs.token || refs.apiKey || Object.values(refs)[0] || `kv://${identity.systemKey}/token`;
204
- return {
205
- key: system.key,
206
- displayName: system.displayName || identity.displayName,
207
- description: system.description || identity.displayName,
208
- type: system.type || 'openapi',
209
- enabled: true,
210
- systemKind: system.systemKind || 'crm',
211
- authentication: {
212
- authType: 'credential',
213
- credential: {
214
- credentialKey: `${identity.systemKey}-cred`,
215
- method: 'bearerToken',
216
- variables: {
217
- baseUrl: refs.baseUrl || 'https://example.invalid',
218
- headerName: 'Authorization',
219
- prefix: 'Bearer'
220
- },
221
- security: {
222
- token: tokenRef
223
- },
224
- writeOnce: true
225
- }
226
- },
227
- dataSources: (/** @type {any[]} */ (bid.datasources) || []).map((d) => d.key)
228
- };
229
- }
230
-
231
222
  /**
232
223
  * @param {Record<string, unknown>} bid
233
224
  * @param {string[]} datasourceFiles
@@ -290,45 +281,73 @@ function buildEnvTemplate(bid) {
290
281
  * nextStamp: Record<string, unknown>
291
282
  * }}
292
283
  */
284
+ function remappedCapabilities(capabilities, keyMap) {
285
+ const out = {};
286
+ for (const [key, value] of Object.entries(capabilities || {})) {
287
+ out[keyMap.get(key) || key] = value;
288
+ }
289
+ return out;
290
+ }
291
+
292
+ function buildApplyStamp(bid, systemKey, identity) {
293
+ return {
294
+ schemaName: bid.schemaName,
295
+ schemaVersion: bid.schemaVersion,
296
+ definitionId: bid.definitionId,
297
+ definitionHash: bid.definitionHash,
298
+ suiteRunId: /** @type {any} */ (bid.meta).suiteRunId,
299
+ systemKey,
300
+ packageVersion: identity.packageVersion,
301
+ appliedAt: new Date().toISOString()
302
+ };
303
+ }
304
+
293
305
  function buildPackageArtifacts(bid, systemKey, enrichOptions = {}) {
294
306
  const identity = /** @type {Record<string, unknown>} */ (bid.packageIdentity);
295
307
  const datasources = /** @type {Record<string, unknown>[]} */ (bid.datasources || []);
296
- const capabilities = /** @type {Record<string, string[]>} */ (bid.capabilities || {});
308
+ const capabilities = remappedCapabilities(
309
+ /** @type {Record<string, string[]>} */ (bid.capabilities || {}),
310
+ canonicalDatasourceKeyMap(datasources, systemKey)
311
+ );
297
312
  const sync = /** @type {Record<string, unknown>} */ (bid.sync || {});
298
- const system = /** @type {Record<string, unknown>} */ (bid.system || {});
313
+ const systemBlock = /** @type {Record<string, unknown>} */ (bid.system || {});
299
314
  const systemOpenapiSourceRef =
300
- typeof system.openapiSourceRef === 'string' ? system.openapiSourceRef : '';
315
+ typeof systemBlock.openapiSourceRef === 'string' ? systemBlock.openapiSourceRef : '';
301
316
  const associationTargets = collectAssociationObjectTypes(
302
317
  datasources,
303
318
  systemOpenapiSourceRef,
304
319
  enrichOptions
305
320
  );
321
+ const keyMap = canonicalDatasourceKeyMap(datasources, systemKey);
306
322
  const systemFile = `${systemKey}-system.json`;
307
- const datasourceFiles = datasources.map((ds) => `${systemKey}-datasource-${ds.key}.json`);
308
- return {
309
- systemFile,
310
- datasourceFiles,
311
- application: buildApplicationFile(bid, datasourceFiles, systemFile),
312
- system: buildSystemFile(bid),
313
- dsFiles: datasources.map((ds) => ({
314
- name: `${systemKey}-datasource-${ds.key}.json`,
315
- body: buildDatasourceFile(ds, systemKey, capabilities, sync, {
323
+ const dsFiles = datasources.map((ds) => {
324
+ const canonicalKey = keyMap.get(String(ds.key)) || String(ds.key);
325
+ return {
326
+ name: datasourceFileName(systemKey, canonicalKey),
327
+ body: buildDatasourceFile({ ...ds, key: canonicalKey }, systemKey, capabilities, sync, {
316
328
  ...enrichOptions,
317
329
  systemOpenapiSourceRef,
318
330
  associationTargets
319
331
  })
320
- })),
332
+ };
333
+ });
334
+ applyBidRelationships(dsFiles, /** @type {any[]} */ (bid.relationships) || [], keyMap);
335
+ applyStaticAndEnterprise(dsFiles, bid);
336
+ applyViewpointDefaults(dsFiles, /** @type {any[]} */ (bid.relationships) || [], keyMap);
337
+ const system = buildSystemFile(bid);
338
+ stampCredentialTestEndpoint(system, dsFiles);
339
+ return {
340
+ systemFile,
341
+ datasourceFiles: dsFiles.map((file) => file.name),
342
+ application: buildApplicationFile(
343
+ bid,
344
+ dsFiles.map((file) => file.name),
345
+ systemFile
346
+ ),
347
+ system,
348
+ dsFiles,
321
349
  envTemplate: buildEnvTemplate(bid),
322
- nextStamp: {
323
- schemaName: bid.schemaName,
324
- schemaVersion: bid.schemaVersion,
325
- definitionId: bid.definitionId,
326
- definitionHash: bid.definitionHash,
327
- suiteRunId: /** @type {any} */ (bid.meta).suiteRunId,
328
- systemKey,
329
- packageVersion: identity.packageVersion,
330
- appliedAt: new Date().toISOString()
331
- }
350
+ nextStamp: buildApplyStamp(bid, systemKey, identity)
332
351
  };
333
352
  }
334
353
 
@@ -10,6 +10,7 @@ const fsReal = require('../internal/fs-real-sync');
10
10
  const {
11
11
  maybeEnrichCrmStyleFieldMappings
12
12
  } = require('./enrich-crm-field-mappings');
13
+ const { pickBestMatchingOp, entityTokensFromDatasource } = require('./enrich-openapi-pick');
13
14
 
14
15
  const PATH_PARAM_RE = /\{(\w+)\}/g;
15
16
 
@@ -171,7 +172,8 @@ function normalizeOp(method, opPath, op) {
171
172
  method: String(method || '').toUpperCase(),
172
173
  path: String(opPath),
173
174
  operationId: typeof op.operationId === 'string' ? op.operationId : '',
174
- pathParamCount: params.length
175
+ pathParamCount: params.length,
176
+ tags: Array.isArray(op.tags) ? op.tags.map((row) => String(row || '')) : []
175
177
  };
176
178
  }
177
179
 
@@ -212,12 +214,29 @@ function isBatchOrSearch(text) {
212
214
  * @param {Array<{ method: string, path: string, operationId: string, pathParamCount: number }>} ops
213
215
  * @returns {{ method: string, path: string, operationId: string } | null}
214
216
  */
215
- function pickFirstMatchingOp(candidates, predicate, idPattern) {
216
- const hits = candidates.filter(predicate);
217
- return hits.find((op) => idPattern.test(op.operationId)) || hits[0] || null;
217
+ function requiredQueryDefaults(doc, method, opPath) {
218
+ const out = {};
219
+ for (const param of operationParameters(doc, method, opPath)) {
220
+ if (!param || param.in !== 'query' || param.required !== true) {
221
+ continue;
222
+ }
223
+ const name = String(param.name || '').trim();
224
+ if (!name) {
225
+ continue;
226
+ }
227
+ const schema = param.schema && typeof param.schema === 'object' ? param.schema : {};
228
+ if (schema.default !== undefined) {
229
+ out[name] = schema.default;
230
+ } else if (typeof schema.minimum === 'number') {
231
+ out[name] = schema.minimum;
232
+ } else {
233
+ out[name] = `{{${name}}}`;
234
+ }
235
+ }
236
+ return out;
218
237
  }
219
238
 
220
- function pickOperationForCapability(capabilityKey, ops) {
239
+ function pickOperationForCapability(capabilityKey, ops, entityTokens) {
221
240
  const key = String(capabilityKey || '').toLowerCase();
222
241
  const candidates = ops.filter((op) => !isBatchOrSearch(`${op.path} ${op.operationId}`));
223
242
  const rules = {
@@ -256,7 +275,7 @@ function pickOperationForCapability(capabilityKey, ops) {
256
275
  if (!rule) {
257
276
  return null;
258
277
  }
259
- return pickFirstMatchingOp(candidates, rule.predicate, rule.idPattern);
278
+ return pickBestMatchingOp(candidates, rule.predicate, rule.idPattern, entityTokens);
260
279
  }
261
280
 
262
281
  /**
@@ -293,7 +312,11 @@ function bindOpenApiOperation(body, binding, capabilityKey) {
293
312
 
294
313
  function createCipQuery(capabilityKey, bindOptions) {
295
314
  const cap = String(capabilityKey || '').toLowerCase();
296
- const query = {};
315
+ const query = {
316
+ ...(bindOptions.requiredQuery && typeof bindOptions.requiredQuery === 'object'
317
+ ? bindOptions.requiredQuery
318
+ : {})
319
+ };
297
320
  const targets = Array.isArray(bindOptions.associationTargets)
298
321
  ? bindOptions.associationTargets.map((row) => String(row || '').trim()).filter(Boolean)
299
322
  : [];
@@ -394,10 +417,11 @@ function enrichDatasourceFromOpenApi(body, openapiSourceRef, options = {}) {
394
417
  )
395
418
  .map((row) => String(row || '').trim())
396
419
  .filter((row) => row && row !== selfType);
420
+ const entityTokens = entityTokensFromDatasource(body);
397
421
  const bound = [];
398
422
  let samplePath = '';
399
423
  for (const capabilityKey of caps) {
400
- const picked = pickOperationForCapability(capabilityKey, indexed);
424
+ const picked = pickOperationForCapability(capabilityKey, indexed, entityTokens);
401
425
  if (!picked) {
402
426
  continue;
403
427
  }
@@ -408,7 +432,8 @@ function enrichDatasourceFromOpenApi(body, openapiSourceRef, options = {}) {
408
432
  picked.path
409
433
  ),
410
434
  associationTargets,
411
- cursorPagination: operationCursorPagination(doc, picked.method, picked.path)
435
+ cursorPagination: operationCursorPagination(doc, picked.method, picked.path),
436
+ requiredQuery: requiredQueryDefaults(doc, picked.method, picked.path)
412
437
  });
413
438
  bound.push(capabilityKey);
414
439
  if (!samplePath) {
@@ -429,5 +454,6 @@ module.exports = {
429
454
  enrichDatasourceFromOpenApi,
430
455
  objectTypeFromOpenApiPath,
431
456
  operationDeclaresAssociationsQuery,
432
- operationCursorPagination
457
+ operationCursorPagination,
458
+ requiredQueryDefaults
433
459
  };
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Score OpenAPI operations by datasource entity tokens.
3
+ *
4
+ * @fileoverview Prefer path/tag overlap over first GET in a multi-entity spec
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ /**
10
+ * @param {Record<string, unknown>} body
11
+ * @returns {string[]}
12
+ */
13
+ function entityTokensFromDatasource(body) {
14
+ const tokens = new Set();
15
+ for (const raw of [body.key, body.resourceType, body.displayName]) {
16
+ const value = String(raw || '').trim().toLowerCase();
17
+ if (!value) {
18
+ continue;
19
+ }
20
+ tokens.add(value);
21
+ for (const part of value.split(/[-_]/g)) {
22
+ if (part.length > 1) {
23
+ tokens.add(part);
24
+ }
25
+ }
26
+ }
27
+ return [...tokens];
28
+ }
29
+
30
+ /**
31
+ * @param {{ path?: string, operationId?: string, tags?: string[] }} op
32
+ * @param {string[]} tokens
33
+ * @returns {number}
34
+ */
35
+ function scoreOpForEntity(op, tokens) {
36
+ if (!Array.isArray(tokens) || tokens.length === 0) {
37
+ return 0;
38
+ }
39
+ const path = String(op.path || '').toLowerCase();
40
+ const id = String(op.operationId || '').toLowerCase();
41
+ const tags = Array.isArray(op.tags)
42
+ ? op.tags.map((row) => String(row || '').toLowerCase()).join(' ')
43
+ : '';
44
+ const hay = `${path} ${id} ${tags}`;
45
+ const segments = path.split('/').filter(Boolean);
46
+ let score = 0;
47
+ for (const token of tokens) {
48
+ if (segments.includes(token)) {
49
+ score += 20;
50
+ } else if (hay.includes(token)) {
51
+ score += 10;
52
+ }
53
+ }
54
+ return score;
55
+ }
56
+
57
+ /**
58
+ * @param {Array<{ method: string, path: string, operationId: string }>} candidates
59
+ * @param {(op: object) => boolean} predicate
60
+ * @param {RegExp} idPattern
61
+ * @param {string[]} [entityTokens]
62
+ * @returns {object | null}
63
+ */
64
+ function pickBestMatchingOp(candidates, predicate, idPattern, entityTokens) {
65
+ const hits = candidates.filter(predicate);
66
+ if (hits.length === 0) {
67
+ return null;
68
+ }
69
+ const tokens = Array.isArray(entityTokens) ? entityTokens : [];
70
+ if (tokens.length > 0) {
71
+ const scored = hits.map((op) => ({
72
+ op,
73
+ score: scoreOpForEntity(op, tokens) + (idPattern.test(op.operationId) ? 5 : 0)
74
+ }));
75
+ scored.sort((left, right) => right.score - left.score);
76
+ if (scored[0].score > 0) {
77
+ return scored[0].op;
78
+ }
79
+ }
80
+ return hits.find((op) => idPattern.test(op.operationId)) || hits[0] || null;
81
+ }
82
+
83
+ module.exports = {
84
+ entityTokensFromDatasource,
85
+ scoreOpForEntity,
86
+ pickBestMatchingOp
87
+ };
@@ -11,7 +11,9 @@ const { VERDICT } = require('./product-model');
11
11
 
12
12
  const GOVERNANCE_SCENARIOS_MISSING = 'GOVERNANCE_SCENARIOS_MISSING';
13
13
  const GOVERNANCE_NOT_VERIFIED = 'GOVERNANCE_NOT_VERIFIED';
14
+ const OPERATIONS_NOT_VERIFIED = 'OPERATIONS_NOT_VERIFIED';
14
15
  const ROLE_ASSISTANT_GOVERNANCE_WARNING = 'ROLE_ASSISTANT_GOVERNANCE_PACKS';
16
+ const ROLE_ASSISTANT_OPERATIONS_WARNING = 'ROLE_ASSISTANT_CS_OPERATIONS';
15
17
 
16
18
  const VERDICT_RANK = Object.freeze({
17
19
  [VERDICT.FAILED]: 4,
@@ -70,37 +72,50 @@ function rollupGovernanceFromDatasources(datasources) {
70
72
  return worst;
71
73
  }
72
74
 
73
- function stripGovernanceNotVerified(recommendations) {
75
+ function stripCsNotVerified(recommendations) {
74
76
  if (!Array.isArray(recommendations)) {
75
77
  return [];
76
78
  }
77
- return recommendations.filter((rec) => rec && rec.code !== GOVERNANCE_NOT_VERIFIED);
79
+ return recommendations.filter(
80
+ (rec) =>
81
+ rec && rec.code !== GOVERNANCE_NOT_VERIFIED && rec.code !== OPERATIONS_NOT_VERIFIED
82
+ );
78
83
  }
79
84
 
80
- function appendRoleAssistantGovernanceWarning(warnings) {
85
+ function appendRoleAssistantCsWarning(warnings, code, message) {
81
86
  const next = Array.isArray(warnings) ? [...warnings] : [];
82
- const exists = next.some((row) => row && row.code === ROLE_ASSISTANT_GOVERNANCE_WARNING);
87
+ const exists = next.some((row) => row && row.code === code);
83
88
  if (!exists) {
84
- next.push({
85
- code: ROLE_ASSISTANT_GOVERNANCE_WARNING,
86
- message:
87
- 'Role Assistant datasources do not use Connected System governance packs. ' +
88
- 'Isolation is Work Hub cases and role-assistant.yaml.'
89
- });
89
+ next.push({ code, message });
90
90
  }
91
91
  return next;
92
92
  }
93
93
 
94
- /**
95
- * Treat CS-only missing packs as not applicable on a Role Assistant report.
96
- * @param {Object} report
97
- * @returns {Object}
98
- */
99
- function remapRoleAssistantGovernanceReport(report) {
100
- if (!report || typeof report !== 'object') {
101
- return report;
94
+ function notApplicableCsPillar(block, errorCode) {
95
+ const base = block && typeof block === 'object' ? { ...block } : {};
96
+ base.verdict = VERDICT.NOT_APPLICABLE;
97
+ if (errorCode) {
98
+ base.errorCode = errorCode;
102
99
  }
103
- const next = { ...report };
100
+ return base;
101
+ }
102
+
103
+ function remapCsPillars(next) {
104
+ let remapped = false;
105
+ const opsVerdict = String((next.operations && next.operations.verdict) || '');
106
+ if (opsVerdict === VERDICT.FAILED || opsVerdict === VERDICT.NOT_VERIFIED) {
107
+ next.operations = notApplicableCsPillar(next.operations, OPERATIONS_NOT_VERIFIED);
108
+ remapped = true;
109
+ }
110
+ const govVerdict = String((next.governance && next.governance.verdict) || '');
111
+ if (govVerdict === VERDICT.FAILED || govVerdict === VERDICT.NOT_VERIFIED) {
112
+ next.governance = notApplicableCsPillar(next.governance, GOVERNANCE_NOT_VERIFIED);
113
+ remapped = true;
114
+ }
115
+ return remapped;
116
+ }
117
+
118
+ function remapMissingPackRows(report, next) {
104
119
  let remapped = false;
105
120
  if (Array.isArray(report.datasources)) {
106
121
  next.datasources = report.datasources.map((row) => {
@@ -120,11 +135,30 @@ function remapRoleAssistantGovernanceReport(report) {
120
135
  next.governance = notApplicableMissingPack(next.governance);
121
136
  remapped = true;
122
137
  }
138
+ return remapped;
139
+ }
140
+
141
+ function remapRoleAssistantGovernanceReport(report) {
142
+ if (!report || typeof report !== 'object') {
143
+ return report;
144
+ }
145
+ const next = { ...report };
146
+ const remapped = remapMissingPackRows(report, next) || remapCsPillars(next);
123
147
  if (!remapped) {
124
148
  return report;
125
149
  }
126
- next.recommendations = stripGovernanceNotVerified(report.recommendations);
127
- next.warnings = appendRoleAssistantGovernanceWarning(report.warnings);
150
+ next.recommendations = stripCsNotVerified(report.recommendations);
151
+ next.warnings = appendRoleAssistantCsWarning(
152
+ report.warnings,
153
+ ROLE_ASSISTANT_GOVERNANCE_WARNING,
154
+ 'Role Assistant datasources do not use Connected System governance packs. ' +
155
+ 'Isolation is Work Hub cases and role-assistant.yaml.'
156
+ );
157
+ next.warnings = appendRoleAssistantCsWarning(
158
+ next.warnings,
159
+ ROLE_ASSISTANT_OPERATIONS_WARNING,
160
+ 'Role Assistant prove does not require Connected System operations verification.'
161
+ );
128
162
  return next;
129
163
  }
130
164
 
@@ -140,6 +174,7 @@ function lifecyclePillarSatisfied(verdict) {
140
174
  module.exports = {
141
175
  GOVERNANCE_SCENARIOS_MISSING,
142
176
  ROLE_ASSISTANT_GOVERNANCE_WARNING,
177
+ ROLE_ASSISTANT_OPERATIONS_WARNING,
143
178
  governanceIsMissingPack,
144
179
  remapRoleAssistantGovernanceReport,
145
180
  lifecyclePillarSatisfied
@@ -49,6 +49,7 @@ const CAPABILITIES = Object.freeze([
49
49
  { method: 'GET', route: '/systems/:systemKey/lifecycle', handlerId: 'getLifecycleReport', operationType: 'certify.read', mutating: false },
50
50
  { method: 'POST', route: '/systems/:systemKey/upload', handlerId: 'runUpload', operationType: 'apply.dev', mutating: true },
51
51
  { method: 'POST', route: '/systems/:systemKey/deploy', handlerId: 'runDeploySystem', operationType: 'apply.prod', mutating: true },
52
+ { method: 'POST', route: '/systems/:systemKey/download', handlerId: 'runDownload', operationType: 'workspace.write', mutating: true },
52
53
  {
53
54
  method: 'POST',
54
55
  route: '/systems/:systemKey/datasources/:datasourceKey/protection/upload',
@@ -7,6 +7,7 @@
7
7
  const workspaceContext = require('./workspace-context');
8
8
  const runUpload = require('./run-upload');
9
9
  const runDeploy = require('./run-deploy');
10
+ const runDownload = require('./run-download');
10
11
  const runProtection = require('./run-protection');
11
12
  const runWizard = require('./run-wizard');
12
13
  const runRepair = require('./run-repair');
@@ -32,6 +33,7 @@ module.exports = {
32
33
  ...workspaceContext,
33
34
  ...runUpload,
34
35
  ...runDeploy,
36
+ ...runDownload,
35
37
  ...runProtection,
36
38
  ...runWizard,
37
39
  ...runRepair,