@aifabrix/builder 2.54.1 → 2.55.0

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 (210) 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 +130 -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 +239 -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/state-file.js +83 -0
  23. package/lib/agent-kit/status.js +50 -0
  24. package/lib/agent-kit/v1-detect.js +81 -0
  25. package/lib/agent-kit/validate-kit.js +87 -0
  26. package/lib/agent-kit/workspace-name.js +40 -0
  27. package/lib/agent-kit/workspace-skeleton.js +103 -0
  28. package/lib/api/role-assistant-test-pack.api.js +86 -0
  29. package/lib/api/work-search.api.js +31 -4
  30. package/lib/api/work.api.js +19 -12
  31. package/lib/app/list-external-systems.js +2 -1
  32. package/lib/app/show-display.js +33 -6
  33. package/lib/app/show.js +25 -2
  34. package/lib/channels/add-channel.js +1 -0
  35. package/lib/channels/approval-guides/claude.js +2 -2
  36. package/lib/channels/channel-add-failure.js +1 -0
  37. package/lib/channels/channel-add-remaining.js +15 -9
  38. package/lib/channels/channel-definition-status.js +115 -0
  39. package/lib/channels/channel-description.js +2 -1
  40. package/lib/channels/channel-install-envelope.js +4 -2
  41. package/lib/channels/channel-integration-generator.js +2 -0
  42. package/lib/channels/channel-runtime-resolver.js +14 -0
  43. package/lib/channels/channel-targets.js +31 -5
  44. package/lib/channels/claude-setup-values.js +4 -1
  45. package/lib/channels/slack-channel-add.js +59 -6
  46. package/lib/channels/slack-mcp-callback.js +51 -0
  47. package/lib/channels/slack-mcp-runtime.js +156 -0
  48. package/lib/channels/slack-provisioning/install-cli.js +14 -2
  49. package/lib/channels/slack-provisioning/with-curl-on-path.js +6 -1
  50. package/lib/cli/index.js +4 -0
  51. package/lib/cli/setup-certification-commands.js +2 -2
  52. package/lib/cli/setup-certification.help.js +3 -1
  53. package/lib/commands/agent-kit-help.js +79 -0
  54. package/lib/commands/agent-kit.js +346 -0
  55. package/lib/commands/channel-add-help.js +5 -5
  56. package/lib/commands/datasource-capability-relate-cli.js +5 -4
  57. package/lib/commands/repair-auth-apply.js +1 -1
  58. package/lib/commands/repair-datasource-key-skip.js +39 -0
  59. package/lib/commands/repair-datasource-keys.js +24 -12
  60. package/lib/commands/repair-rbac.js +16 -10
  61. package/lib/commands/role-assistant-copy-output.js +61 -0
  62. package/lib/commands/role-assistant-copy.js +155 -0
  63. package/lib/commands/role-assistant-test-output.js +8 -9
  64. package/lib/commands/role-assistant-test-result-rows.js +42 -0
  65. package/lib/commands/role-assistant.js +117 -48
  66. package/lib/commands/search.js +143 -0
  67. package/lib/commands/test-trust-external.js +7 -5
  68. package/lib/commands/upload-sidecar-followups.js +4 -1
  69. package/lib/commands/verify-operations-skip-e2e.js +131 -0
  70. package/lib/commands/verify-operations-step-plan.js +6 -33
  71. package/lib/commands/verify-operations-steps.js +3 -1
  72. package/lib/datasource/capability/relate-operations.js +28 -10
  73. package/lib/datasource/capability/run-capability-relate.js +48 -25
  74. package/lib/datasource/datasource-identifier.js +13 -5
  75. package/lib/deployment/deployer-poll-run.js +117 -35
  76. package/lib/external-system/deploy-dataplane-published.js +70 -0
  77. package/lib/external-system/deploy.js +12 -2
  78. package/lib/external-system/download-role-assistant.js +41 -15
  79. package/lib/external-system/download.js +7 -1
  80. package/lib/generator/wizard-datasource-key.js +54 -0
  81. package/lib/generator/wizard.js +55 -54
  82. package/lib/integration-definition/apply-datasource-keys.js +75 -0
  83. package/lib/integration-definition/apply-relationships.js +195 -0
  84. package/lib/integration-definition/apply-static-enterprise.js +229 -0
  85. package/lib/integration-definition/apply-system-file.js +71 -0
  86. package/lib/integration-definition/apply-test-endpoint.js +89 -0
  87. package/lib/integration-definition/apply-viewpoint-defaults.js +157 -0
  88. package/lib/integration-definition/apply.js +78 -59
  89. package/lib/integration-definition/enrich-openapi-operations.js +36 -10
  90. package/lib/integration-definition/enrich-openapi-pick.js +87 -0
  91. package/lib/lifecycle/role-assistant-governance.js +56 -21
  92. package/lib/programmatic/capability-registry.js +1 -0
  93. package/lib/programmatic/index.js +2 -0
  94. package/lib/programmatic/openapi-apply-result-schemas.js +8 -0
  95. package/lib/programmatic/openapi-channel-install-schemas.js +4 -1
  96. package/lib/programmatic/openapi-component-schemas.js +1 -0
  97. package/lib/programmatic/openapi-descriptions.js +9 -3
  98. package/lib/programmatic/openapi-envelope-examples.js +7 -0
  99. package/lib/programmatic/openapi-operation-bindings.js +5 -0
  100. package/lib/programmatic/openapi-request-schemas.js +5 -0
  101. package/lib/programmatic/openapi-schema-descriptions-channel.js +8 -5
  102. package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
  103. package/lib/programmatic/openapi-spec.js +1 -0
  104. package/lib/programmatic/request-timeout.js +3 -0
  105. package/lib/programmatic/route-handler-map.js +7 -0
  106. package/lib/programmatic/run-channel-catalog.js +50 -8
  107. package/lib/programmatic/run-channel-install-options.js +5 -1
  108. package/lib/programmatic/run-download.js +41 -0
  109. package/lib/protection/auth-context.js +3 -1
  110. package/lib/protection/protection-display.js +3 -2
  111. package/lib/protection/report-exit.js +50 -0
  112. package/lib/protection/run-commands.js +2 -0
  113. package/lib/role-assistant/package-validate.js +175 -0
  114. package/lib/role-assistant/test-case-assertion-row.js +36 -0
  115. package/lib/role-assistant/test-case-copy-bind.js +77 -0
  116. package/lib/role-assistant/test-case-copy-expected.js +127 -0
  117. package/lib/role-assistant/test-case-copy-from-work.js +90 -0
  118. package/lib/role-assistant/test-case-copy-interactions.js +67 -0
  119. package/lib/role-assistant/test-case-copy.js +119 -0
  120. package/lib/role-assistant/test-case-expected-catalog.js +135 -0
  121. package/lib/role-assistant/test-case-validate.js +88 -0
  122. package/lib/role-assistant/test-cases-bid.js +117 -4
  123. package/lib/role-assistant/test-cases-evaluate.js +137 -30
  124. package/lib/role-assistant/test-cases-honesty.js +155 -6
  125. package/lib/role-assistant/test-cases-lifecycle.js +291 -0
  126. package/lib/role-assistant/test-cases-search-graph.js +122 -0
  127. package/lib/role-assistant/test-cases-search.js +20 -2
  128. package/lib/role-assistant/test-cases.js +34 -168
  129. package/lib/role-assistant/test-pack-map.js +180 -0
  130. package/lib/role-assistant/test-pack-sync.js +187 -0
  131. package/lib/role-assistant/test-runner-debug.js +54 -0
  132. package/lib/role-assistant/test-runner-respond-body.js +36 -0
  133. package/lib/role-assistant/test-runner-search.js +20 -5
  134. package/lib/role-assistant/test-runner-start-payload.js +63 -10
  135. package/lib/role-assistant/test-runner-workhub-answers.js +45 -16
  136. package/lib/role-assistant/test-runner-workhub.js +45 -21
  137. package/lib/role-assistant/test-runner.js +50 -10
  138. package/lib/schema/external-datasource.schema.json +36 -5
  139. package/lib/schema/external-system.schema.json +20 -2
  140. package/lib/schema/role-assistant-test-case.schema.json +408 -0
  141. package/lib/search/dataplane-context.js +23 -0
  142. package/lib/search/search-args.js +211 -0
  143. package/lib/search/search-display.js +238 -0
  144. package/lib/search/search-errors.js +50 -0
  145. package/lib/search/search-prompts.js +119 -0
  146. package/lib/search/search-roles.js +85 -0
  147. package/lib/search/search-run.js +241 -0
  148. package/lib/search/search-skills.js +198 -0
  149. package/lib/utils/authentication-slot-show.js +153 -0
  150. package/lib/utils/credential-display.js +43 -1
  151. package/lib/utils/external-system-readiness-compact-blocks.js +5 -1
  152. package/lib/utils/external-system-readiness-compact-display.js +30 -1
  153. package/lib/utils/external-system-validators.js +2 -1
  154. package/lib/utils/help-builder.js +3 -1
  155. package/lib/utils/schema-loader.js +37 -0
  156. package/lib/validation/validate-external-file.js +5 -1
  157. package/lib/validation/validate.js +3 -2
  158. package/package.json +1 -1
  159. package/templates/agent-kit/adapters/claude/nested.CLAUDE.md +1 -0
  160. package/templates/agent-kit/adapters/claude/root.CLAUDE.md +5 -0
  161. package/templates/agent-kit/adapters/cursor/commands/cust-0-plan.md +5 -0
  162. package/templates/agent-kit/adapters/cursor/commands/cust-1-validate.md +5 -0
  163. package/templates/agent-kit/adapters/cursor/commands/cust-2-build.md +5 -0
  164. package/templates/agent-kit/adapters/cursor/commands/cust-3-push.md +5 -0
  165. package/templates/agent-kit/adapters/cursor/commands/cust-biz-1-validate.md +5 -0
  166. package/templates/agent-kit/adapters/cursor/commands/cust-biz-2-build.md +5 -0
  167. package/templates/agent-kit/adapters/cursor/commands/cust-biz-3-prove.md +5 -0
  168. package/templates/agent-kit/agent-kit.yaml +45 -0
  169. package/templates/agent-kit/instructions/AGENTKIT.md +83 -0
  170. package/templates/agent-kit/instructions/integration.AGENTS.md +25 -0
  171. package/templates/agent-kit/instructions/role-assistant.AGENTS.md +22 -0
  172. package/templates/agent-kit/instructions/root.AGENTS.md +37 -0
  173. package/templates/agent-kit/parity-inventory.yaml +188 -0
  174. package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +58 -0
  175. package/templates/agent-kit/skills/aifabrix-connected-system/references/contract-checklist.md +40 -0
  176. package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +54 -0
  177. package/templates/agent-kit/skills/aifabrix-connected-system/references/production-safety.md +35 -0
  178. package/templates/agent-kit/skills/aifabrix-connected-system/scripts/reserved-system-key.js +19 -0
  179. package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +51 -0
  180. package/templates/agent-kit/skills/aifabrix-plan/references/challenge-checklist.md +108 -0
  181. package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +34 -0
  182. package/templates/agent-kit/skills/aifabrix-plan/references/plan-template.md +182 -0
  183. package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +50 -0
  184. package/templates/agent-kit/skills/aifabrix-prove/references/demo-template.md +65 -0
  185. package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +68 -0
  186. package/templates/agent-kit/skills/aifabrix-prove/references/learning-template.md +66 -0
  187. package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +42 -0
  188. package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
  189. package/templates/agent-kit/skills/shared/hosts.md +78 -0
  190. package/templates/agent-kit/workspace/.cursor/plans/README.md +22 -0
  191. package/templates/agent-kit/workspace/LICENSE.mit +21 -0
  192. package/templates/agent-kit/workspace/README.md +89 -0
  193. package/templates/applications/builder-api/Dockerfile +3 -1
  194. package/templates/applications/dataplane/application.yaml +2 -2
  195. package/templates/applications/dataplane/env.template +1 -1
  196. package/templates/applications/dataplane/rbac.yaml +28 -0
  197. package/templates/applications/keycloak/env.template +3 -3
  198. package/templates/applications/miso-controller/application.yaml +1 -1
  199. package/templates/applications/miso-controller/env.template +8 -9
  200. package/templates/channels/claude/TROUBLESHOOTING.md +3 -0
  201. package/templates/channels/claude/channel.json +1 -1
  202. package/templates/channels/claude/install.template.md.hbs +1 -0
  203. package/templates/channels/claude/integration/claude-system.json +1 -1
  204. package/templates/channels/slack/channel.json +11 -0
  205. package/templates/channels/slack/install.template.md.hbs +9 -0
  206. package/templates/channels/slack/integration/slack-system.json +11 -0
  207. package/templates/channels/slack/manifest.template.json.hbs +6 -3
  208. package/templates/channels/teams-bot/channel.json +1 -0
  209. package/templates/python/docker-compose.hbs +14 -0
  210. package/templates/typescript/docker-compose.hbs +14 -0
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Static file-load CIP + published enterprise REST paths for BID apply.
3
+ *
4
+ * @fileoverview authType none uses record fetch; stamp enterprise path
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const PLACEHOLDER_HOST = /example\.invalid|api\.example\.com/i;
10
+
11
+ /**
12
+ * @param {Record<string, unknown>} bid
13
+ * @returns {boolean}
14
+ */
15
+ function isStaticFileLoadBid(bid) {
16
+ const system = bid && bid.system && typeof bid.system === 'object'
17
+ ? /** @type {Record<string, unknown>} */ (bid.system)
18
+ : {};
19
+ const auth = system.authentication && typeof system.authentication === 'object'
20
+ ? /** @type {Record<string, unknown>} */ (system.authentication)
21
+ : null;
22
+ if (auth && auth.authType === 'none') {
23
+ return true;
24
+ }
25
+ if (String(system.type || '') === 'custom' && !hasVendorBaseUrl(system)) {
26
+ return true;
27
+ }
28
+ return false;
29
+ }
30
+
31
+ /**
32
+ * @param {Record<string, unknown>} system
33
+ * @returns {boolean}
34
+ */
35
+ function hasVendorBaseUrl(system) {
36
+ const refs = system.connectionRefs && typeof system.connectionRefs === 'object'
37
+ ? /** @type {Record<string, string>} */ (system.connectionRefs)
38
+ : {};
39
+ const url = String(refs.baseUrl || '').trim();
40
+ if (!url || PLACEHOLDER_HOST.test(url)) {
41
+ return false;
42
+ }
43
+ return /^https?:\/\//i.test(url);
44
+ }
45
+
46
+ /**
47
+ * @param {Record<string, unknown>} op
48
+ * @returns {void}
49
+ */
50
+ function setRecordFetch(op) {
51
+ const steps = Array.isArray(op.steps) ? op.steps : [];
52
+ let found = false;
53
+ for (const step of steps) {
54
+ if (step && step.fetch && typeof step.fetch === 'object') {
55
+ step.fetch.source = 'record';
56
+ found = true;
57
+ }
58
+ }
59
+ if (!found) {
60
+ steps.unshift({ fetch: { source: 'record', query: {} } });
61
+ if (!steps.some((row) => row && row.output)) {
62
+ steps.push({ output: { mode: 'records' } });
63
+ }
64
+ op.steps = steps;
65
+ }
66
+ op.enabled = true;
67
+ }
68
+
69
+ /**
70
+ * @param {Record<string, unknown>} body
71
+ * @param {string} capKey
72
+ * @returns {void}
73
+ */
74
+ function ensureRecordCapability(body, capKey) {
75
+ if (!body.execution || typeof body.execution !== 'object') {
76
+ body.execution = { engine: 'cip' };
77
+ }
78
+ const execution = /** @type {Record<string, unknown>} */ (body.execution);
79
+ if (!execution.cip || typeof execution.cip !== 'object') {
80
+ execution.cip = { version: '1.0', operations: {} };
81
+ }
82
+ const cip = /** @type {Record<string, unknown>} */ (execution.cip);
83
+ if (!cip.operations || typeof cip.operations !== 'object') {
84
+ cip.operations = {};
85
+ }
86
+ const ops = /** @type {Record<string, unknown>} */ (cip.operations);
87
+ if (!ops[capKey] || typeof ops[capKey] !== 'object') {
88
+ ops[capKey] = { enabled: true, description: `Local ${capKey}`, steps: [] };
89
+ }
90
+ setRecordFetch(/** @type {Record<string, unknown>} */ (ops[capKey]));
91
+ }
92
+
93
+ /**
94
+ * @param {Record<string, unknown>} body
95
+ * @param {string} capKey
96
+ * @param {boolean} overwritePath
97
+ * @returns {void}
98
+ */
99
+ function stampEnterpriseOp(body, capKey, overwritePath) {
100
+ const resourceType = String(body.resourceType || '').trim();
101
+ if (!resourceType) {
102
+ return;
103
+ }
104
+ if (!body.openapi || typeof body.openapi !== 'object') {
105
+ body.openapi = { enabled: true, operations: {} };
106
+ }
107
+ const openapi = /** @type {Record<string, unknown>} */ (body.openapi);
108
+ openapi.enabled = true;
109
+ if (!openapi.operations || typeof openapi.operations !== 'object') {
110
+ openapi.operations = {};
111
+ }
112
+ const ops = /** @type {Record<string, unknown>} */ (openapi.operations);
113
+ const prev = ops[capKey] && typeof ops[capKey] === 'object'
114
+ ? /** @type {Record<string, unknown>} */ (ops[capKey])
115
+ : {};
116
+ const next = { ...prev, operationId: prev.operationId || capKey };
117
+ if (!next.restRuntime) {
118
+ next.restRuntime = { pathKind: 'collectionSubresource' };
119
+ }
120
+ if (overwritePath || !next.path) {
121
+ next.path = `/enterprise/${resourceType}/${capKey}`;
122
+ next.method = next.method || 'POST';
123
+ }
124
+ ops[capKey] = next;
125
+ }
126
+
127
+ /**
128
+ * @param {Record<string, unknown>} body
129
+ * @param {string} capKey
130
+ * @returns {void}
131
+ */
132
+ function ensureCapabilityObject(body, capKey) {
133
+ if (!Array.isArray(body.capabilities)) {
134
+ body.capabilities = [];
135
+ }
136
+ const keys = capabilityKeysOf(body);
137
+ if (keys.includes(capKey) || keys.includes(`${capKey}Basic`)) {
138
+ return;
139
+ }
140
+ body.capabilities.push({
141
+ key: capKey,
142
+ description: `Local ${capKey}`,
143
+ riskLevel: 'medium',
144
+ approvalRequired: false
145
+ });
146
+ }
147
+
148
+ function capabilityKeysOf(body) {
149
+ if (!Array.isArray(body.capabilities)) {
150
+ return ['list', 'get'];
151
+ }
152
+ return body.capabilities
153
+ .map((row) => (row && typeof row === 'object' ? row.key : row))
154
+ .filter((key) => typeof key === 'string' && key.trim())
155
+ .map((key) => String(key).trim());
156
+ }
157
+
158
+ /**
159
+ * @param {Record<string, unknown>} body
160
+ * @returns {boolean}
161
+ */
162
+ function isRecordStorageBody(body) {
163
+ if (String(body.entityType || '') !== 'recordStorage') {
164
+ return false;
165
+ }
166
+ return String(body.storageType || '') !== 'view';
167
+ }
168
+
169
+ function hasWriteCapability(body) {
170
+ return capabilityKeysOf(body).some((key) => /^create/i.test(key) || /^update/i.test(key));
171
+ }
172
+
173
+ function hasVendorWriteBinding(body) {
174
+ const ops = body.openapi && body.openapi.operations;
175
+ if (!ops || typeof ops !== 'object') {
176
+ return false;
177
+ }
178
+ return ['create', 'createBasic', 'update', 'updateBasic'].some((key) => {
179
+ const row = ops[key];
180
+ const method = String((row && row.method) || '').toUpperCase();
181
+ return method === 'POST' || method === 'PUT' || method === 'PATCH';
182
+ });
183
+ }
184
+
185
+ function applyGetOnlyLocalPersist(body) {
186
+ if (hasVendorWriteBinding(body) || hasWriteCapability(body)) {
187
+ return;
188
+ }
189
+ for (const key of ['create', 'update']) {
190
+ ensureCapabilityObject(body, key);
191
+ ensureRecordCapability(body, key);
192
+ stampEnterpriseOp(body, key, true);
193
+ }
194
+ }
195
+
196
+ function applyStaticRecordCaps(body, caps) {
197
+ for (const key of caps.filter((row) => row === 'list' || row === 'get')) {
198
+ ensureRecordCapability(body, key);
199
+ stampEnterpriseOp(body, key, true);
200
+ }
201
+ }
202
+
203
+ function applyStaticAndEnterprise(dsFiles, bid) {
204
+ const staticLoad = isStaticFileLoadBid(bid);
205
+ for (const file of dsFiles || []) {
206
+ const body = file.body;
207
+ if (!body || typeof body !== 'object') {
208
+ continue;
209
+ }
210
+ if (!isRecordStorageBody(body)) {
211
+ continue;
212
+ }
213
+ if (staticLoad) {
214
+ applyStaticRecordCaps(body, capabilityKeysOf(body));
215
+ }
216
+ applyGetOnlyLocalPersist(body);
217
+ for (const key of capabilityKeysOf(body)) {
218
+ stampEnterpriseOp(body, key, staticLoad);
219
+ }
220
+ }
221
+ }
222
+
223
+ module.exports = {
224
+ isStaticFileLoadBid,
225
+ hasVendorBaseUrl,
226
+ applyStaticAndEnterprise,
227
+ stampEnterpriseOp,
228
+ ensureRecordCapability
229
+ };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * BID apply system.json body (auth none for static file-load).
3
+ *
4
+ * @fileoverview System manifest emit for integration-definition apply
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { canonicalDatasourceKeyMap } = require('./apply-datasource-keys');
10
+ const { isStaticFileLoadBid } = require('./apply-static-enterprise');
11
+
12
+ /**
13
+ * @param {Record<string, unknown>} bid
14
+ * @param {Record<string, unknown>} identity
15
+ * @returns {Record<string, unknown>}
16
+ */
17
+ function buildSystemAuthentication(bid, identity) {
18
+ if (isStaticFileLoadBid(bid)) {
19
+ return { authType: 'none' };
20
+ }
21
+ const system = /** @type {Record<string, unknown>} */ (bid.system);
22
+ const refs = /** @type {Record<string, string>} */ (system.connectionRefs || {});
23
+ const tokenRef = refs.token || refs.apiKey || Object.values(refs)[0] || `kv://${identity.systemKey}/token`;
24
+ return {
25
+ authType: 'credential',
26
+ credential: {
27
+ credentialKey: `${identity.systemKey}-cred`,
28
+ method: 'bearerToken',
29
+ variables: {
30
+ baseUrl: refs.baseUrl || 'https://example.invalid',
31
+ headerName: 'Authorization',
32
+ prefix: 'Bearer'
33
+ },
34
+ security: {
35
+ token: tokenRef
36
+ },
37
+ writeOnce: true
38
+ }
39
+ };
40
+ }
41
+
42
+ /**
43
+ * @param {Record<string, unknown>} bid
44
+ * @returns {Record<string, unknown>}
45
+ */
46
+ function buildSystemFile(bid) {
47
+ const system = /** @type {Record<string, unknown>} */ (bid.system);
48
+ const identity = /** @type {Record<string, unknown>} */ (bid.packageIdentity);
49
+ const keyMap = canonicalDatasourceKeyMap(
50
+ /** @type {any[]} */ (bid.datasources) || [],
51
+ identity.systemKey
52
+ );
53
+ const staticLoad = isStaticFileLoadBid(bid);
54
+ return {
55
+ key: system.key,
56
+ displayName: system.displayName || identity.displayName,
57
+ description: system.description || identity.displayName,
58
+ type: system.type || (staticLoad ? 'custom' : 'openapi'),
59
+ enabled: true,
60
+ systemKind: system.systemKind || (staticLoad ? 'custom' : 'crm'),
61
+ authentication: buildSystemAuthentication(bid, identity),
62
+ dataSources: (/** @type {any[]} */ (bid.datasources) || []).map(
63
+ (d) => keyMap.get(String(d.key)) || d.key
64
+ )
65
+ };
66
+ }
67
+
68
+ module.exports = {
69
+ buildSystemAuthentication,
70
+ buildSystemFile
71
+ };
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Stamp credential testEndpoint from the first vendor list GET + required query mins.
3
+ *
4
+ * @fileoverview Honour OpenAPI query minimums on generated probe URLs
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ /**
10
+ * @param {Record<string, unknown>} body
11
+ * @returns {Record<string, unknown>}
12
+ */
13
+ function listFetchQuery(body) {
14
+ const ops = body.execution && body.execution.cip && body.execution.cip.operations;
15
+ const list = ops && ops.list && typeof ops.list === 'object' ? ops.list : null;
16
+ const steps = list && Array.isArray(list.steps) ? list.steps : [];
17
+ const fetchStep = steps.find((row) => row && row.fetch && typeof row.fetch.query === 'object');
18
+ return fetchStep ? /** @type {Record<string, unknown>} */ (fetchStep.fetch.query) : {};
19
+ }
20
+
21
+ /**
22
+ * @param {Record<string, unknown>} query
23
+ * @returns {string}
24
+ */
25
+ function probeQueryString(query) {
26
+ const parts = [];
27
+ for (const [name, value] of Object.entries(query || {})) {
28
+ if (value === undefined || value === null) {
29
+ continue;
30
+ }
31
+ const raw = String(value);
32
+ if (raw.includes('{{')) {
33
+ continue;
34
+ }
35
+ parts.push(`${encodeURIComponent(name)}=${encodeURIComponent(raw)}`);
36
+ }
37
+ return parts.join('&');
38
+ }
39
+
40
+ /**
41
+ * @param {Array<{ body?: Record<string, unknown> }>} dsFiles
42
+ * @returns {string}
43
+ */
44
+ function firstVendorListProbe(dsFiles) {
45
+ for (const file of dsFiles || []) {
46
+ const body = file && file.body;
47
+ const list = body && body.openapi && body.openapi.operations && body.openapi.operations.list;
48
+ if (!list || String(list.method || '').toUpperCase() !== 'GET') {
49
+ continue;
50
+ }
51
+ const opPath = String(list.path || '').trim();
52
+ if (!opPath || opPath.startsWith('/enterprise/')) {
53
+ continue;
54
+ }
55
+ const qs = probeQueryString(listFetchQuery(body));
56
+ return qs ? `${opPath}?${qs}` : opPath;
57
+ }
58
+ return '';
59
+ }
60
+
61
+ /**
62
+ * @param {Record<string, unknown>} system
63
+ * @param {Array<{ body?: Record<string, unknown> }>} dsFiles
64
+ * @returns {void}
65
+ */
66
+ function stampCredentialTestEndpoint(system, dsFiles) {
67
+ const auth = system && system.authentication;
68
+ const cred = auth && auth.credential && typeof auth.credential === 'object'
69
+ ? auth.credential
70
+ : null;
71
+ if (!cred) {
72
+ return;
73
+ }
74
+ if (!cred.variables || typeof cred.variables !== 'object') {
75
+ cred.variables = {};
76
+ }
77
+ if (String(cred.variables.testEndpoint || '').trim()) {
78
+ return;
79
+ }
80
+ const probe = firstVendorListProbe(dsFiles);
81
+ if (probe) {
82
+ cred.variables.testEndpoint = probe;
83
+ }
84
+ }
85
+
86
+ module.exports = {
87
+ stampCredentialTestEndpoint,
88
+ firstVendorListProbe
89
+ };
@@ -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
+ };