@aifabrix/builder 2.53.1 → 2.54.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 (321) hide show
  1. package/bin/aifabrix.js +2 -2
  2. package/lib/api/assistants.api.js +8 -5
  3. package/lib/api/channel-identity.api.js +41 -0
  4. package/lib/api/evidence-authoring.api.js +5 -1
  5. package/lib/api/evidence.api.js +2 -0
  6. package/lib/api/types/evidence.types.js +2 -0
  7. package/lib/api/work-search.api.js +136 -0
  8. package/lib/channels/add-channel.js +163 -126
  9. package/lib/channels/approval-guides/chatgpt.js +14 -18
  10. package/lib/channels/approval-guides/claude.js +29 -0
  11. package/lib/channels/approval-guides/index.js +88 -22
  12. package/lib/channels/approval-guides/mcp-client.js +27 -0
  13. package/lib/channels/approval-guides/microsoft-copilot.js +52 -3
  14. package/lib/channels/approval-guides/slack.js +10 -10
  15. package/lib/channels/approval-guides/teams-bot.js +8 -12
  16. package/lib/channels/channel-add-continuations.js +91 -0
  17. package/lib/channels/channel-add-ensure.js +124 -27
  18. package/lib/channels/channel-add-existing.js +160 -28
  19. package/lib/channels/channel-add-failure.js +60 -0
  20. package/lib/channels/channel-add-remaining.js +486 -0
  21. package/lib/channels/channel-add-tty.js +190 -0
  22. package/lib/channels/channel-add-wizard-config.js +138 -0
  23. package/lib/channels/channel-add-wizard.js +250 -0
  24. package/lib/channels/channel-artifact-generator.js +27 -13
  25. package/lib/channels/channel-cardinality.js +83 -0
  26. package/lib/channels/channel-catalog-icon.js +45 -0
  27. package/lib/channels/channel-cli-instructions.js +75 -0
  28. package/lib/channels/channel-configuration-map.js +213 -0
  29. package/lib/channels/channel-credential.js +11 -14
  30. package/lib/channels/channel-definition-project.js +101 -0
  31. package/lib/channels/channel-definition-registry.js +154 -0
  32. package/lib/channels/channel-description.js +59 -2
  33. package/lib/channels/channel-discovery.js +16 -2
  34. package/lib/channels/channel-external-system.js +86 -9
  35. package/lib/channels/channel-install-envelope-actions.js +88 -0
  36. package/lib/channels/channel-install-envelope-microsoft.js +155 -0
  37. package/lib/channels/channel-install-envelope.js +356 -0
  38. package/lib/channels/channel-install-help-md.js +172 -0
  39. package/lib/channels/channel-install-http-payload.js +62 -0
  40. package/lib/channels/channel-install-inventory.js +115 -0
  41. package/lib/channels/channel-install-profiles.js +90 -0
  42. package/lib/channels/channel-instance-collision.js +118 -0
  43. package/lib/channels/channel-instance-key.js +100 -0
  44. package/lib/channels/channel-instance-materialize.js +240 -0
  45. package/lib/channels/channel-integration-generator.js +16 -9
  46. package/lib/channels/channel-integration-template.js +101 -22
  47. package/lib/channels/channel-inventory-fetch.js +79 -0
  48. package/lib/channels/channel-oauth-preflight.js +104 -0
  49. package/lib/channels/channel-package-version.js +76 -0
  50. package/lib/channels/channel-predefined-client.js +154 -0
  51. package/lib/channels/channel-probe.js +7 -2
  52. package/lib/channels/channel-runtime-resolver.js +180 -72
  53. package/lib/channels/channel-status-display.js +86 -16
  54. package/lib/channels/channel-targets.js +106 -92
  55. package/lib/channels/chatgpt-callback.js +318 -0
  56. package/lib/channels/chatgpt-channel-add.js +229 -0
  57. package/lib/channels/chatgpt-preflight.js +396 -0
  58. package/lib/channels/chatgpt-runtime-contract.js +17 -0
  59. package/lib/channels/chatgpt-setup-values.js +124 -0
  60. package/lib/channels/claude-callback.js +67 -0
  61. package/lib/channels/claude-channel-add.js +254 -0
  62. package/lib/channels/claude-runtime-contract.js +40 -0
  63. package/lib/channels/claude-runtime.js +58 -0
  64. package/lib/channels/claude-setup-values.js +89 -0
  65. package/lib/channels/mcp-client/config-merge.js +148 -0
  66. package/lib/channels/mcp-client/ensure-client.js +231 -0
  67. package/lib/channels/mcp-client/paths.js +53 -0
  68. package/lib/channels/mcp-client/profiles.js +122 -0
  69. package/lib/channels/mcp-client/redirect-uris.js +99 -0
  70. package/lib/channels/mcp-client/runtime.js +59 -0
  71. package/lib/channels/mcp-client-channel-add.js +186 -0
  72. package/lib/channels/microsoft-copilot-channel-add.js +117 -7
  73. package/lib/channels/microsoft-copilot-entra-identifier-uris.js +187 -0
  74. package/lib/channels/microsoft-copilot-entra-sso-form.js +106 -18
  75. package/lib/channels/microsoft-copilot-entra-sso.js +31 -2
  76. package/lib/channels/microsoft-copilot-package-prior.js +107 -0
  77. package/lib/channels/microsoft-copilot-package.js +29 -28
  78. package/lib/channels/microsoft-provisioning/a365.js +80 -0
  79. package/lib/channels/microsoft-provisioning/auth-config.js +240 -0
  80. package/lib/channels/microsoft-provisioning/auth.js +179 -0
  81. package/lib/channels/microsoft-provisioning/cli.js +71 -0
  82. package/lib/channels/microsoft-provisioning/constants.js +75 -0
  83. package/lib/channels/microsoft-provisioning/errors.js +117 -0
  84. package/lib/channels/microsoft-provisioning/index.js +55 -0
  85. package/lib/channels/microsoft-provisioning/install-atk.js +158 -0
  86. package/lib/channels/microsoft-provisioning/install-teams.js +115 -0
  87. package/lib/channels/microsoft-provisioning/login-portal-device.js +236 -0
  88. package/lib/channels/microsoft-provisioning/login-portal-parse.js +71 -0
  89. package/lib/channels/microsoft-provisioning/login-portal-teams.js +193 -0
  90. package/lib/channels/microsoft-provisioning/login-portal.js +230 -0
  91. package/lib/channels/microsoft-provisioning/persist.js +110 -0
  92. package/lib/channels/microsoft-provisioning/teams-bot-args.js +119 -0
  93. package/lib/channels/microsoft-provisioning/teams-bot-controller.js +123 -0
  94. package/lib/channels/microsoft-provisioning/teams-bot-parse.js +134 -0
  95. package/lib/channels/microsoft-provisioning/teams-bot-persist.js +113 -0
  96. package/lib/channels/microsoft-provisioning/teams-bot-provision.js +234 -0
  97. package/lib/channels/microsoft-provisioning/teams-bot-secrets.js +45 -0
  98. package/lib/channels/microsoft-provisioning/teams-cli.js +82 -0
  99. package/lib/channels/runtime-tool-catalog.js +5 -2
  100. package/lib/channels/slack-channel-add-portal.js +130 -0
  101. package/lib/channels/slack-channel-add-tty-auth.js +95 -0
  102. package/lib/channels/slack-channel-add.js +258 -0
  103. package/lib/channels/slack-provisioning/apps.js +181 -0
  104. package/lib/channels/slack-provisioning/auth-store.js +110 -0
  105. package/lib/channels/slack-provisioning/auth-ticket.js +185 -0
  106. package/lib/channels/slack-provisioning/auth.js +115 -0
  107. package/lib/channels/slack-provisioning/callbacks.js +41 -0
  108. package/lib/channels/slack-provisioning/cli.js +226 -0
  109. package/lib/channels/slack-provisioning/constants.js +37 -0
  110. package/lib/channels/slack-provisioning/credentials.js +169 -0
  111. package/lib/channels/slack-provisioning/curl-shim.js +132 -0
  112. package/lib/channels/slack-provisioning/developer-install.js +118 -0
  113. package/lib/channels/slack-provisioning/env-file.js +120 -0
  114. package/lib/channels/slack-provisioning/errors.js +47 -0
  115. package/lib/channels/slack-provisioning/go-ca-bundle.js +137 -0
  116. package/lib/channels/slack-provisioning/index.js +250 -0
  117. package/lib/channels/slack-provisioning/install-cli.js +323 -0
  118. package/lib/channels/slack-provisioning/manifest.js +89 -0
  119. package/lib/channels/slack-provisioning/oauth-lock.js +33 -0
  120. package/lib/channels/slack-provisioning/offer-cli.js +113 -0
  121. package/lib/channels/slack-provisioning/onboard-members.js +122 -0
  122. package/lib/channels/slack-provisioning/onboard.js +89 -0
  123. package/lib/channels/slack-provisioning/persist.js +95 -0
  124. package/lib/channels/slack-provisioning/remaining-urls.js +158 -0
  125. package/lib/channels/slack-provisioning/with-curl-on-path.js +48 -0
  126. package/lib/channels/teams-bot-channel-add.js +94 -0
  127. package/lib/channels/vendor-dataplane-url.js +249 -0
  128. package/lib/cli/setup-certification.help.js +2 -0
  129. package/lib/cli/setup-external-system-help.js +2 -0
  130. package/lib/cli/setup-external-system.js +1 -1
  131. package/lib/commands/channel-add-help.js +366 -0
  132. package/lib/commands/channel-add-setup.js +162 -80
  133. package/lib/commands/channel-configure.js +118 -0
  134. package/lib/commands/channel.js +10 -1
  135. package/lib/commands/governance-upload-external.js +6 -3
  136. package/lib/commands/lifecycle-command-action.js +2 -5
  137. package/lib/commands/lifecycle-external.js +93 -21
  138. package/lib/commands/repair-datasource-test-payload.js +4 -2
  139. package/lib/commands/repair-datasource-webhooks-sync-bridge.js +23 -2
  140. package/lib/commands/repair-datasource.js +3 -0
  141. package/lib/commands/repair-external-run.js +59 -25
  142. package/lib/commands/repair-openapi-rest-runtime.js +127 -0
  143. package/lib/commands/repair-orchestration-tag-merge.js +339 -0
  144. package/lib/commands/repair-rbac-catalog-defaults.js +96 -0
  145. package/lib/commands/repair-rbac-platform-preset.js +26 -15
  146. package/lib/commands/repair-rbac-reader-roles.js +8 -21
  147. package/lib/commands/repair-rbac.js +10 -34
  148. package/lib/commands/repair-system-permissions.js +24 -22
  149. package/lib/commands/role-assistant-test-output.js +14 -0
  150. package/lib/commands/role-assistant.js +59 -21
  151. package/lib/commands/test-e2e-external.js +148 -21
  152. package/lib/commands/upload-credential-push-log.js +2 -1
  153. package/lib/commands/upload-publish-context.js +1 -1
  154. package/lib/commands/upload-role-assistant-shell.js +124 -0
  155. package/lib/commands/upload-sidecar-followups.js +6 -3
  156. package/lib/commands/upload.js +18 -3
  157. package/lib/core/secrets-docker-env.js +4 -1
  158. package/lib/core/secrets-env-content.js +17 -3
  159. package/lib/datasource/capability/capability-manifest.js +0 -3
  160. package/lib/datasource/capability/copy-test-payload.js +3 -0
  161. package/lib/external-system/deploy-helpers.js +26 -1
  162. package/lib/external-system/deploy.js +10 -6
  163. package/lib/external-system/download-helpers.js +29 -2
  164. package/lib/external-system/download-role-assistant.js +83 -4
  165. package/lib/external-system/download.js +40 -30
  166. package/lib/external-system/sync-deploy-manifest.js +3 -2
  167. package/lib/external-system/upload-identity.js +42 -0
  168. package/lib/generator/index.js +19 -15
  169. package/lib/generator/split.js +13 -2
  170. package/lib/lifecycle/role-assistant-governance.js +146 -0
  171. package/lib/lifecycle/scenario-upload.js +181 -30
  172. package/lib/programmatic/builder-api-cors.js +143 -0
  173. package/lib/programmatic/builder-api-request.js +6 -0
  174. package/lib/programmatic/builder-api-route-dispatch.js +56 -42
  175. package/lib/programmatic/builder-api-server.js +8 -1
  176. package/lib/programmatic/capability-registry.js +54 -0
  177. package/lib/programmatic/channel-install-help-resolve.js +359 -0
  178. package/lib/programmatic/channel-install-session.js +218 -0
  179. package/lib/programmatic/help-content/channel-onboarding.md +2 -0
  180. package/lib/programmatic/help-content/cip-overview.md +3 -1
  181. package/lib/programmatic/http-raw-response.js +62 -0
  182. package/lib/programmatic/openapi-channel-install-bindings.js +179 -0
  183. package/lib/programmatic/openapi-channel-install-schemas.js +286 -0
  184. package/lib/programmatic/openapi-component-schemas.js +18 -1
  185. package/lib/programmatic/openapi-description-enricher.js +3 -1
  186. package/lib/programmatic/openapi-descriptions.js +74 -3
  187. package/lib/programmatic/openapi-envelope-examples.js +117 -0
  188. package/lib/programmatic/openapi-operation-bindings.js +4 -1
  189. package/lib/programmatic/openapi-schema-descriptions-channel.js +316 -0
  190. package/lib/programmatic/openapi-spec.js +12 -8
  191. package/lib/programmatic/platform-rbac-catalog.js +36 -18
  192. package/lib/programmatic/route-handler-map.js +51 -1
  193. package/lib/programmatic/route-handlers-context.js +2 -1
  194. package/lib/programmatic/run-auth-permissions.js +54 -0
  195. package/lib/programmatic/run-channel-catalog.js +196 -0
  196. package/lib/programmatic/run-channel-install-artifacts.js +137 -0
  197. package/lib/programmatic/run-channel-install-copilot.js +353 -0
  198. package/lib/programmatic/run-channel-install-options.js +232 -0
  199. package/lib/programmatic/run-channel-install-teams-bot.js +204 -0
  200. package/lib/programmatic/run-channel-install.js +346 -0
  201. package/lib/programmatic/run-channel-package.js +87 -7
  202. package/lib/programmatic/workspace-context.js +2 -0
  203. package/lib/role-assistant/evidence-sync.js +4 -2
  204. package/lib/role-assistant/evidence-validation-issues.js +51 -0
  205. package/lib/role-assistant/knowledge-sync.js +10 -4
  206. package/lib/role-assistant/settings.js +49 -2
  207. package/lib/role-assistant/test-cases-evaluate.js +30 -2
  208. package/lib/role-assistant/test-cases-evidence.js +175 -0
  209. package/lib/role-assistant/test-cases-search.js +229 -0
  210. package/lib/role-assistant/test-cases.js +37 -4
  211. package/lib/role-assistant/test-runner-search.js +149 -0
  212. package/lib/role-assistant/test-runner-workhub-helpers.js +40 -2
  213. package/lib/role-assistant/test-runner-workhub-soft.js +9 -3
  214. package/lib/role-assistant/test-runner-workhub.js +29 -11
  215. package/lib/role-assistant/test-runner.js +16 -1
  216. package/lib/schema/external-datasource.schema.json +112 -18
  217. package/lib/schema/external-system.schema.json +159 -2
  218. package/lib/schema/mcp-client-profile.schema.json +103 -0
  219. package/lib/utils/app-path-resolve.js +4 -0
  220. package/lib/utils/authentication-env-template.js +44 -6
  221. package/lib/utils/credential-secrets-env.js +6 -1
  222. package/lib/utils/env-copy.js +19 -7
  223. package/lib/utils/external-env-template.js +20 -2
  224. package/lib/utils/paths.js +18 -1
  225. package/lib/utils/platform-shell-package.js +69 -0
  226. package/lib/utils/port-resolver.js +50 -1
  227. package/lib/utils/rewrite-name-flag.js +20 -0
  228. package/lib/utils/role-assistant-paths.js +13 -10
  229. package/lib/utils/schema-resolver.js +11 -1
  230. package/lib/utils/secrets-helpers.js +12 -21
  231. package/lib/utils/token-manager.js +10 -5
  232. package/lib/validation/validate.js +40 -0
  233. package/package.json +1 -1
  234. package/templates/README.md +1 -1
  235. package/templates/applications/builder-api/Dockerfile +1 -1
  236. package/templates/applications/builder-api/env.template +7 -0
  237. package/templates/applications/dataplane/application.yaml +6 -5
  238. package/templates/applications/dataplane/env.template +2 -2
  239. package/templates/applications/dataplane/rbac.yaml +7 -2
  240. package/templates/applications/keycloak/Dockerfile +30 -4
  241. package/templates/applications/keycloak/application.yaml +2 -2
  242. package/templates/applications/keycloak/cimd-lenient-parse/AbstractClientIdMetadataDocumentExecutor.java +896 -0
  243. package/templates/applications/keycloak/cimd-lenient-parse/SOURCE.md +19 -0
  244. package/templates/applications/keycloak/env.template +3 -0
  245. package/templates/applications/miso-controller/application.yaml +1 -2
  246. package/templates/applications/miso-controller/env.template +2 -0
  247. package/templates/channels/README.md +5 -5
  248. package/templates/channels/chatgpt/TROUBLESHOOTING.md +51 -0
  249. package/templates/channels/chatgpt/channel.json +59 -0
  250. package/templates/channels/chatgpt/icon.svg +1 -0
  251. package/templates/channels/chatgpt/install.template.md.hbs +20 -95
  252. package/templates/channels/chatgpt/integration/README.md +1 -1
  253. package/templates/channels/chatgpt/integration/application.json +2 -2
  254. package/templates/channels/chatgpt/integration/chatgpt-system.json +49 -4
  255. package/templates/channels/chatgpt/integration/env.template +2 -4
  256. package/templates/channels/chatgpt/integration/rbac.json +13 -0
  257. package/templates/channels/claude/TROUBLESHOOTING.md +41 -0
  258. package/templates/channels/claude/channel.json +59 -0
  259. package/templates/channels/claude/icon.svg +1 -0
  260. package/templates/channels/claude/install.template.md.hbs +21 -0
  261. package/templates/channels/claude/integration/README.md +14 -0
  262. package/templates/channels/claude/integration/application.json +15 -0
  263. package/templates/channels/claude/integration/claude-system.json +59 -0
  264. package/templates/channels/claude/integration/env.template +2 -0
  265. package/templates/channels/claude/integration/rbac.json +13 -0
  266. package/templates/channels/claude/manifest.template.json.hbs +40 -0
  267. package/templates/channels/mcp-client/channel.json +72 -0
  268. package/templates/channels/mcp-client/icon.svg +1 -0
  269. package/templates/channels/mcp-client/install.template.md.hbs +24 -0
  270. package/templates/channels/mcp-client/integration/README.md +13 -0
  271. package/templates/channels/mcp-client/integration/application.json +15 -0
  272. package/templates/channels/mcp-client/integration/env.template +2 -0
  273. package/templates/channels/mcp-client/integration/mcp-client-system.json +72 -0
  274. package/templates/channels/mcp-client/integration/rbac.json +13 -0
  275. package/templates/channels/mcp-client/manifest.template.json.hbs +41 -0
  276. package/templates/channels/mcp-client/profiles/cursor.json +26 -0
  277. package/templates/channels/mcp-client/profiles/github-copilot.json +29 -0
  278. package/templates/channels/mcp-client/profiles/vscode.json +29 -0
  279. package/templates/channels/microsoft-copilot/channel.json +73 -0
  280. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +8 -4
  281. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +65 -5
  282. package/templates/channels/microsoft-copilot/icon.svg +1 -0
  283. package/templates/channels/microsoft-copilot/install.template.md.hbs +9 -9
  284. package/templates/channels/microsoft-copilot/integration/README.md +1 -1
  285. package/templates/channels/microsoft-copilot/integration/application.json +2 -2
  286. package/templates/channels/microsoft-copilot/integration/env.template +3 -6
  287. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +63 -4
  288. package/templates/channels/microsoft-copilot/integration/rbac.json +13 -0
  289. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +5 -5
  290. package/templates/channels/slack/channel.json +223 -0
  291. package/templates/channels/slack/icon.svg +1 -0
  292. package/templates/channels/slack/install.template.md.hbs +36 -28
  293. package/templates/channels/slack/integration/README.md +245 -22
  294. package/templates/channels/slack/integration/application.json +44 -7
  295. package/templates/channels/slack/integration/env.template +15 -6
  296. package/templates/channels/slack/integration/openapi/openapi.json +126 -0
  297. package/templates/channels/slack/integration/rbac.json +84 -0
  298. package/templates/channels/slack/integration/slack-datasource-commands.json +154 -0
  299. package/templates/channels/slack/integration/slack-datasource-conversations.json +28 -3
  300. package/templates/channels/slack/integration/slack-datasource-events.json +154 -0
  301. package/templates/channels/slack/integration/slack-datasource-interactive.json +154 -0
  302. package/templates/channels/slack/integration/slack-datasource-messages.json +40 -2
  303. package/templates/channels/slack/integration/slack-system.json +155 -77
  304. package/templates/channels/slack/manifest.template.json.hbs +5 -2
  305. package/templates/channels/teams-bot/channel.json +161 -0
  306. package/templates/channels/teams-bot/icon.svg +1 -0
  307. package/templates/channels/teams-bot/install.template.md.hbs +12 -13
  308. package/templates/channels/teams-bot/integration/README.md +5 -4
  309. package/templates/channels/teams-bot/integration/application.json +6 -4
  310. package/templates/channels/teams-bot/integration/env.template +5 -4
  311. package/templates/channels/teams-bot/integration/openapi/openapi.json +68 -0
  312. package/templates/channels/teams-bot/integration/rbac.json +21 -0
  313. package/templates/channels/teams-bot/integration/teams-bot-datasource-activities.json +154 -0
  314. package/templates/channels/teams-bot/integration/teams-bot-system.json +128 -23
  315. package/templates/channels/web-embed/channel.json +60 -0
  316. package/templates/channels/web-embed/install.template.md.hbs +1 -1
  317. package/templates/channels/web-embed/integration/README.md +2 -2
  318. package/templates/channels/web-embed/integration/application.json +2 -2
  319. package/templates/channels/web-embed/integration/env.template +1 -1
  320. package/templates/channels/web-embed/integration/rbac.json +13 -0
  321. package/templates/channels/web-embed/integration/web-embed-system.json +50 -4
@@ -7,12 +7,12 @@
7
7
  "key":"external-datasource-schema",
8
8
  "name":"External Data Source Configuration Schema",
9
9
  "description":"JSON schema for validating ExternalDataSource configuration files",
10
- "version":"2.9.7",
10
+ "version":"2.9.9",
11
11
  "type":"schema",
12
12
  "category":"integration",
13
13
  "author":"AI Fabrix Team",
14
14
  "createdAt":"2024-01-01T00:00:00Z",
15
- "updatedAt":"2026-08-21T00:00:00Z",
15
+ "updatedAt":"2026-08-27T00:00:00Z",
16
16
  "compatibility":{
17
17
  "minVersion":"1.0.0",
18
18
  "maxVersion":"3.0.0",
@@ -32,10 +32,19 @@
32
32
  ],
33
33
  "changelog":[
34
34
  {
35
- "version":"2.9.7",
36
- "date":"2026-08-21T00:00:00Z",
35
+ "version":"2.9.9",
36
+ "date":"2026-08-27T00:00:00Z",
37
37
  "changes":[
38
- "Added record to CIP fetch.source for governed local record-storage operations."
38
+ "Added optional testPayload.listWindow {page, pageSize} so capacity and CIP-simulate list probes stop after a few vendor pages. Does not change CIP paginate.maxPages. CIP $defs unchanged (543 visual N/A).",
39
+ "Clarified testPayload descriptions: mapping expectedResult vs capacity expectedRows, write input is exposed shape, listWindow vs live paginate.maxPages."
40
+ ],
41
+ "breaking":false
42
+ },
43
+ {
44
+ "version":"2.9.8",
45
+ "date":"2026-08-25T00:00:00Z",
46
+ "changes":[
47
+ "Added optional testPayload.scenarios items expectedRows, identityKey, numericTolerance, and matchMode for capacity E2E row gold (list/get and named CIP). Mapping-step expectedResult is unchanged."
39
48
  ],
40
49
  "breaking":false
41
50
  },
@@ -528,7 +537,7 @@
528
537
  },
529
538
  "webhook":{
530
539
  "type":"object",
531
- "description":"Vendor webhook change-signal configuration (454.0). Webhook is trigger-only; business data comes from CIP get.",
540
+ "description":"Vendor webhook configuration. events[] that match a capability key invoke that CIP operation; other names trigger SyncIntent and CIP get.",
532
541
  "additionalProperties":false,
533
542
  "properties":{
534
543
  "enabled":{
@@ -551,7 +560,7 @@
551
560
  "type":"string",
552
561
  "minLength":1
553
562
  },
554
- "description":"Vendor event names that trigger SyncIntent creation."
563
+ "description":"Event names that select work. A name that matches capabilities[].key invokes that operation (fieldMappings reshape the body). Other names trigger SyncIntent and CIP get. Do not mix both kinds on one datasource."
555
564
  },
556
565
  "deleteEvents":{
557
566
  "type":"array",
@@ -564,7 +573,7 @@
564
573
  "idPath":{
565
574
  "type":"string",
566
575
  "minLength":1,
567
- "description":"JSONPath to extract vendor object id from webhook payload, e.g. $.objectId"
576
+ "description":"JSONPath to extract vendor object id from webhook payload, e.g. $.objectId. Required for change-signal events; not required when every events[] entry is a capability key."
568
577
  },
569
578
  "eventTypePath":{
570
579
  "type":"string",
@@ -1022,14 +1031,15 @@
1022
1031
  },
1023
1032
  "testPayload":{
1024
1033
  "type":"object",
1025
- "description":"Execution-aware test payload configuration for unit and integration testing. Only the listed properties are allowed at this level; nested shapes for payloadTemplate/expectedResult/fk fixtures are validated strictly by the application validator where required.",
1034
+ "description":"Author-owned proof contract for this Business Entity. Mapping uses payloadTemplate plus expectedResult (one normalized record). Capacity E2E walks scenarios in array order: omit expectedRows for success-only; set expectedRows plus identityKey to prove CIP output rows (list, get, and named viewpoint answers). listWindow is the list probe and does not change live CIP paginate.maxPages. Root is closed — unknown keys fail validate. Role Assistant search is not the owner of gold amounts.",
1026
1035
  "properties":{
1027
1036
  "mode":{
1028
1037
  "type":"string",
1029
1038
  "enum":[
1030
1039
  "mock",
1031
1040
  "live"
1032
- ]
1041
+ ],
1042
+ "description":"live runs against the connected system (certification). mock is offline mapping/unit only."
1033
1043
  },
1034
1044
  "primaryKey":{
1035
1045
  "type":"object",
@@ -1041,9 +1051,29 @@
1041
1051
  "default":true,
1042
1052
  "description":"When true (default if scenarios include create), after create the executor merges returned identifiers into the effective primary key for later get/update/delete in that run. When false, mutations use the static manifest primaryKey (destructive on shared fixtures). Ignored when no create scenario exists."
1043
1053
  },
1054
+ "listWindow":{
1055
+ "type":"object",
1056
+ "description":"Default list probe for capacity and CIP-simulate. Runtime passes CIP page and pageSize so paginate stops after a few pages. Does not change production CIP paginate.maxPages (live completeness). Scenario input.page/pageSize override this step. Omitted defaults: page 1, pageSize 5. limit is not a CIP window key.",
1057
+ "additionalProperties":false,
1058
+ "properties":{
1059
+ "page":{
1060
+ "type":"integer",
1061
+ "minimum":1,
1062
+ "default":1,
1063
+ "description":"1-based list page for the probe (default 1)."
1064
+ },
1065
+ "pageSize":{
1066
+ "type":"integer",
1067
+ "minimum":1,
1068
+ "maximum":100,
1069
+ "default":5,
1070
+ "description":"Rows per probe page (default 5, max 100)."
1071
+ }
1072
+ }
1073
+ },
1044
1074
  "scenarios":{
1045
1075
  "type":"array",
1046
- "description":"Ordered scenario steps for capacity E2E; execution order is array order (optional order field is a diagnostics hint only).",
1076
+ "description":"Ordered capacity E2E steps (array order). Each operation must match a CIP operation key. Writes prove persist on a following get with expectedRows — do not gold create/update/delete rows. Optional order is diagnostics only.",
1047
1077
  "items":{
1048
1078
  "type":"object",
1049
1079
  "required":[
@@ -1052,7 +1082,8 @@
1052
1082
  "properties":{
1053
1083
  "operation":{
1054
1084
  "type":"string",
1055
- "pattern":"^[a-z][a-zA-Z0-9]*$"
1085
+ "pattern":"^[a-z][a-zA-Z0-9]*$",
1086
+ "description":"CIP operation key for this step (list, get, create, update, delete, or a named capability such as a viewpoint answer)."
1056
1087
  },
1057
1088
  "enabled":{
1058
1089
  "type":"boolean",
@@ -1061,14 +1092,58 @@
1061
1092
  },
1062
1093
  "input":{
1063
1094
  "type":"object",
1064
- "additionalProperties":true
1095
+ "additionalProperties":true,
1096
+ "description":"Write scenarios: exposed (normalized) payload a REST or MCP caller would send — not the vendor envelope. List scenarios: optional page/pageSize override of listWindow. Read get typically uses testPayload.primaryKey, not this object."
1065
1097
  },
1066
1098
  "order":{
1067
1099
  "type":"integer",
1068
1100
  "description":"Optional stable sort hint for scenario execution / diagnostics."
1101
+ },
1102
+ "expectedRows":{
1103
+ "type":"array",
1104
+ "description":"Capacity E2E gold for CIP output rows. Omitted means success-only (invoke did not throw). Empty array means expect zero rows (zero is a fact, not unknown). Same contract for record list/get and viewpoint named operations. PostgreSQL remains the calculator — this asserts the result. Requires identityKey when the array has one or more items.",
1105
+ "items":{
1106
+ "type":"object",
1107
+ "additionalProperties":true
1108
+ }
1109
+ },
1110
+ "identityKey":{
1111
+ "type":"string",
1112
+ "minLength":1,
1113
+ "description":"Published output field that uniquely identifies each gold row (this datasource output, often externalId). Required when expectedRows has one or more items. Not a platform-wide vocabulary."
1114
+ },
1115
+ "numericTolerance":{
1116
+ "type":"number",
1117
+ "minimum":0,
1118
+ "default":0,
1119
+ "description":"Absolute tolerance applied only to numeric expected fields (COUNT typically 0). Does not apply to strings."
1120
+ },
1121
+ "matchMode":{
1122
+ "type":"string",
1123
+ "enum":[
1124
+ "subsetIdentities",
1125
+ "exactIdentities"
1126
+ ],
1127
+ "default":"subsetIdentities",
1128
+ "description":"subsetIdentities (default): every gold identity must appear in CIP output (extra live rows allowed — typical for list probes). exactIdentities also fails extra actual identities."
1069
1129
  }
1070
1130
  },
1071
- "additionalProperties":false
1131
+ "additionalProperties":false,
1132
+ "if":{
1133
+ "required":[
1134
+ "expectedRows"
1135
+ ],
1136
+ "properties":{
1137
+ "expectedRows":{
1138
+ "minItems":1
1139
+ }
1140
+ }
1141
+ },
1142
+ "then":{
1143
+ "required":[
1144
+ "identityKey"
1145
+ ]
1146
+ }
1072
1147
  }
1073
1148
  },
1074
1149
  "fk":{
@@ -1094,12 +1169,12 @@
1094
1169
  },
1095
1170
  "payloadTemplate":{
1096
1171
  "type":"object",
1097
- "description":"Sample payload matching the expected API response structure. Used for testing field mappings and metadata schema validation.",
1172
+ "description":"Sample payload that mapping expressions read (typically provider/raw shape for raw.*). Used by the mapping step and expression resolution. Not write scenario input — writes use scenarios[].input in the exposed shape.",
1098
1173
  "additionalProperties":true
1099
1174
  },
1100
1175
  "expectedResult":{
1101
1176
  "type":"object",
1102
- "description":"Expected normalized result after field mapping transformations (optional, for validation)",
1177
+ "description":"Mapping-step gold for ONE normalized record after fieldMappings (shape/subset). Not aggregates, not multi-row CIP output — that is scenarios[].expectedRows.",
1103
1178
  "additionalProperties":true
1104
1179
  }
1105
1180
  },
@@ -2545,7 +2620,7 @@
2545
2620
  "viewpoint",
2546
2621
  "record"
2547
2622
  ],
2548
- "description":"If 'openapi', refer to openapi.operations.*. If 'http', use explicit method/path. If 'datasource', execute another datasource operation. If 'viewpoint', execute saved SQL in fetch.query against records storage (storageType view). If 'record', read or persist governed rows in local record storage with no vendor HTTP; read versus persist follows the CIP operation shape."
2623
+ "description":"If 'openapi', refer to openapi.operations.*. If 'http', use explicit method/path. If 'datasource', execute another datasource operation. If 'viewpoint', execute saved SQL in fetch.query against records storage (storageType view). If 'record', read or persist governed rows in local record storage with no vendor HTTP. Persist vs read follows the CIP operation shape (create|update), not the operation key. Fetch token is singular record; output.mode remains plural records."
2549
2624
  },
2550
2625
  "openapiRef":{
2551
2626
  "type":"string",
@@ -2598,7 +2673,7 @@
2598
2673
  "description":"When true, append fetch items to the pipeline accumulator for a following merge step."
2599
2674
  },
2600
2675
  "query":{
2601
- "description":"When source is openapi/http/datasource: static query parameter object. When source is viewpoint: non-empty SQL SELECT string. ABAC/runtime may still append supported filters at execution for viewpoint."
2676
+ "description":"When source is openapi/http/datasource: static query parameter object. When source is viewpoint: non-empty SQL SELECT string. Forbidden when source is record. ABAC/runtime may still append supported filters at execution for viewpoint."
2602
2677
  },
2603
2678
  "bodyTemplate":{
2604
2679
  "description":"Optional static request body or template for POST/PUT/PATCH fetches.",
@@ -2735,6 +2810,25 @@
2735
2810
  }
2736
2811
  }
2737
2812
  }
2813
+ },
2814
+ {
2815
+ "if":{
2816
+ "properties":{
2817
+ "source":{
2818
+ "const":"record"
2819
+ }
2820
+ },
2821
+ "required":[
2822
+ "source"
2823
+ ]
2824
+ },
2825
+ "then":{
2826
+ "not":{
2827
+ "required":[
2828
+ "query"
2829
+ ]
2830
+ }
2831
+ }
2738
2832
  }
2739
2833
  ],
2740
2834
  "additionalProperties":false
@@ -7,12 +7,12 @@
7
7
  "key": "external-system-schema",
8
8
  "name": "External System Configuration Schema",
9
9
  "description": "JSON schema for validating ExternalSystem configuration files",
10
- "version": "1.14.0",
10
+ "version": "1.16.0",
11
11
  "type": "schema",
12
12
  "category": "integration",
13
13
  "author": "AI Fabrix Team",
14
14
  "createdAt": "2024-01-01T00:00:00Z",
15
- "updatedAt": "2026-08-07T00:00:00Z",
15
+ "updatedAt": "2026-08-23T00:00:00Z",
16
16
  "compatibility": {
17
17
  "minVersion": "1.4.0",
18
18
  "maxVersion": "2.0.0",
@@ -27,6 +27,22 @@
27
27
  ],
28
28
  "dependencies": [],
29
29
  "changelog": [
30
+ {
31
+ "version": "1.16.0",
32
+ "date": "2026-08-23T00:00:00Z",
33
+ "changes": [
34
+ "Added optional channel.maxInstances (integer, minimum 1) for Channel Definition cardinality. Omit for unbounded. Builder refuses new installs at the cap; existing instance keys are not deleted."
35
+ ],
36
+ "breaking": false
37
+ },
38
+ {
39
+ "version": "1.15.0",
40
+ "date": "2026-08-22T00:00:00Z",
41
+ "changes": [
42
+ "Added optional top-level channel specialization for Channel Definition documents and materialized communication Connected Systems (definitionKey, provisioning.mode automatic|guided|cli, runtimeTarget, profiles, packaging)."
43
+ ],
44
+ "breaking": false
45
+ },
30
46
  {
31
47
  "version": "1.14.0",
32
48
  "date": "2026-08-07T00:00:00Z",
@@ -1716,6 +1732,147 @@
1716
1732
  }
1717
1733
  },
1718
1734
  "additionalProperties": false
1735
+ },
1736
+ "channel": {
1737
+ "type": "object",
1738
+ "description": "Optional Channel Definition specialization. Present on Channel Definition documents and materialized communication Connected Systems. Omit on non-channel External Systems.",
1739
+ "additionalProperties": false,
1740
+ "required": [
1741
+ "definitionKey",
1742
+ "provisioning"
1743
+ ],
1744
+ "properties": {
1745
+ "definitionKey": {
1746
+ "type": "string",
1747
+ "description": "Stable catalog identity. Equals key on a definition document; preserved on a materialized instance whose key may differ.",
1748
+ "pattern": "^[a-z0-9-]+$",
1749
+ "minLength": 3,
1750
+ "maxLength": 40
1751
+ },
1752
+ "provisioning": {
1753
+ "type": "object",
1754
+ "additionalProperties": false,
1755
+ "required": [
1756
+ "mode"
1757
+ ],
1758
+ "properties": {
1759
+ "mode": {
1760
+ "type": "string",
1761
+ "enum": [
1762
+ "automatic",
1763
+ "guided",
1764
+ "cli"
1765
+ ],
1766
+ "description": "automatic: API completes with no remaining vendor step. guided: API runs install; vendor leftovers use help.md. cli: hosted API must not run the adapter; return generated CLI instructions."
1767
+ }
1768
+ }
1769
+ },
1770
+ "runtimeTarget": {
1771
+ "type": "object",
1772
+ "additionalProperties": false,
1773
+ "required": [
1774
+ "defaultSystemKey"
1775
+ ],
1776
+ "properties": {
1777
+ "defaultSystemKey": {
1778
+ "type": "string",
1779
+ "pattern": "^[a-z0-9-]+$",
1780
+ "description": "Default runtime or MCP Connected System key. Not the channel instance key."
1781
+ },
1782
+ "configurable": {
1783
+ "type": "boolean",
1784
+ "description": "When true, install may choose an approved runtime or MCP Connected System."
1785
+ }
1786
+ }
1787
+ },
1788
+ "experimental": {
1789
+ "type": "boolean",
1790
+ "default": false
1791
+ },
1792
+ "maxInstances": {
1793
+ "type": "integer",
1794
+ "minimum": 1,
1795
+ "description": "Maximum Connected System instances for this Channel Definition. Omit for unbounded. Builder refuses new installs at the cap; existing instance keys are not deleted."
1796
+ },
1797
+ "profiles": {
1798
+ "type": "array",
1799
+ "items": {
1800
+ "type": "object",
1801
+ "additionalProperties": false,
1802
+ "required": [
1803
+ "key",
1804
+ "displayLabel"
1805
+ ],
1806
+ "properties": {
1807
+ "key": {
1808
+ "type": "string",
1809
+ "minLength": 1
1810
+ },
1811
+ "displayLabel": {
1812
+ "type": "string",
1813
+ "minLength": 1
1814
+ }
1815
+ }
1816
+ }
1817
+ },
1818
+ "artifacts": {
1819
+ "type": "array",
1820
+ "description": "Generic artifact ids produced at install (no vendor URLs).",
1821
+ "items": {
1822
+ "type": "string",
1823
+ "minLength": 1
1824
+ }
1825
+ },
1826
+ "packaging": {
1827
+ "type": "object",
1828
+ "description": "Builder implementation packaging metadata. Not a consumer contract.",
1829
+ "additionalProperties": false,
1830
+ "properties": {
1831
+ "templateDir": {
1832
+ "type": "string"
1833
+ },
1834
+ "channelType": {
1835
+ "type": "string"
1836
+ },
1837
+ "artifactFileName": {
1838
+ "type": "string"
1839
+ },
1840
+ "artifactFormat": {
1841
+ "type": "string"
1842
+ },
1843
+ "credentialKey": {
1844
+ "type": "string"
1845
+ },
1846
+ "capabilities": {
1847
+ "type": "array",
1848
+ "items": {
1849
+ "type": "string"
1850
+ }
1851
+ },
1852
+ "displayLabel": {
1853
+ "type": "string"
1854
+ },
1855
+ "category": {
1856
+ "type": "string"
1857
+ },
1858
+ "wizardOrder": {
1859
+ "type": "integer"
1860
+ },
1861
+ "remainingManualStep": {
1862
+ "type": "string"
1863
+ },
1864
+ "docsAnchor": {
1865
+ "type": "string"
1866
+ },
1867
+ "approvalRequired": {
1868
+ "type": "boolean"
1869
+ },
1870
+ "vendorAuthRequired": {
1871
+ "type": "boolean"
1872
+ }
1873
+ }
1874
+ }
1875
+ }
1719
1876
  }
1720
1877
  },
1721
1878
  "additionalProperties": false
@@ -0,0 +1,103 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "MCP client installation profile",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "required": ["key", "displayName", "transport", "config", "oauth", "reload", "uninstall"],
7
+ "properties": {
8
+ "key": {
9
+ "type": "string",
10
+ "pattern": "^[a-z][a-z0-9-]*$"
11
+ },
12
+ "displayName": {
13
+ "type": "string",
14
+ "minLength": 1
15
+ },
16
+ "transport": {
17
+ "type": "string",
18
+ "enum": ["streamable-http"]
19
+ },
20
+ "config": {
21
+ "type": "object",
22
+ "additionalProperties": false,
23
+ "required": ["scope", "relativePath", "serversKey", "serverKey"],
24
+ "properties": {
25
+ "scope": {
26
+ "type": "string",
27
+ "enum": ["user", "project"]
28
+ },
29
+ "relativePath": {
30
+ "type": "string",
31
+ "minLength": 1
32
+ },
33
+ "serversKey": {
34
+ "type": "string",
35
+ "enum": ["mcpServers", "servers"]
36
+ },
37
+ "serverKey": {
38
+ "type": "string",
39
+ "minLength": 1
40
+ }
41
+ }
42
+ },
43
+ "oauth": {
44
+ "type": "object",
45
+ "additionalProperties": false,
46
+ "required": [
47
+ "integrationClientKey",
48
+ "keycloakClientId",
49
+ "registrationMode",
50
+ "redirectUris"
51
+ ],
52
+ "properties": {
53
+ "integrationClientKey": {
54
+ "type": "string",
55
+ "minLength": 1
56
+ },
57
+ "keycloakClientId": {
58
+ "type": "string",
59
+ "minLength": 1
60
+ },
61
+ "registrationMode": {
62
+ "type": "string",
63
+ "enum": ["predefinedClient", "cimd"]
64
+ },
65
+ "redirectUris": {
66
+ "type": "array",
67
+ "minItems": 1,
68
+ "items": {
69
+ "type": "string",
70
+ "minLength": 1
71
+ }
72
+ },
73
+ "cimdDocumentHosts": {
74
+ "type": "array",
75
+ "items": {
76
+ "type": "string"
77
+ }
78
+ }
79
+ }
80
+ },
81
+ "reload": {
82
+ "type": "object",
83
+ "additionalProperties": false,
84
+ "required": ["remainingStep"],
85
+ "properties": {
86
+ "remainingStep": {
87
+ "type": "string",
88
+ "minLength": 1
89
+ }
90
+ }
91
+ },
92
+ "uninstall": {
93
+ "type": "object",
94
+ "additionalProperties": false,
95
+ "required": ["removeServerKeyOnly"],
96
+ "properties": {
97
+ "removeServerKeyOnly": {
98
+ "type": "boolean"
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
@@ -160,6 +160,7 @@ function resolveFromExistingPathsBackwardCompatible(
160
160
  * Resolves authoring tree for an external system or Enterprise Knowledge anchor.
161
161
  * @param {string} appName - App or anchor key
162
162
  * @param {Object} [options]
163
+ * @param {string} [options.appPath] - Explicit package directory (channel instance upload)
163
164
  * @param {string} [options.workspace] - integration | enterprise-knowledge | role-assistant
164
165
  * @param {string} [options.systemKind] - Explicit systemKind override
165
166
  * @returns {{ appPath: string, baseDir: 'integration' | 'enterprise-knowledge' | 'role-assistant' }}
@@ -168,6 +169,9 @@ function resolveAppWorkspace(appName, options = {}) {
168
169
  if (!appName || typeof appName !== 'string') {
169
170
  throw new Error('App name is required and must be a string');
170
171
  }
172
+ if (typeof options.appPath === 'string' && options.appPath.trim()) {
173
+ return workspacePair(path.resolve(options.appPath.trim()), WORKSPACE_INTEGRATION);
174
+ }
171
175
  const { getIntegrationPath } = require('./paths');
172
176
  const workspaceOverride = normalizeWorkspaceOverride(options.workspace);
173
177
  const explicitKind = typeof options.systemKind === 'string' ? options.systemKind.trim() : '';
@@ -13,7 +13,7 @@ const {
13
13
  securityKeyToVar,
14
14
  systemKeyToKvPrefix
15
15
  } = require('./credential-secrets-env');
16
- const { getKvPathSegmentForSecurityKey } = require('./credential-secrets-env-kv');
16
+ const { getKvPathSegmentForSecurityKey, isValidKvPath } = require('./credential-secrets-env-kv');
17
17
 
18
18
  /**
19
19
  * @param {string} slotKey
@@ -53,6 +53,45 @@ function buildSlotKvPath(systemKey, slotKey, securityKey) {
53
53
  return `kv://${systemKey}/${segment}`;
54
54
  }
55
55
 
56
+ /**
57
+ * Prefer the credential's kv:// path; invent a slot path only when missing.
58
+ * @param {unknown} securityVal
59
+ * @param {string} systemKey
60
+ * @param {string} slotKey
61
+ * @param {string} securityKey
62
+ * @returns {string|null}
63
+ */
64
+ function kvPathFromSecurityValue(securityVal, systemKey, slotKey, securityKey) {
65
+ if (typeof securityVal === 'string' && isValidKvPath(securityVal)) {
66
+ return securityVal.trim();
67
+ }
68
+ return buildSlotKvPath(systemKey, slotKey, securityKey);
69
+ }
70
+
71
+ /**
72
+ * Env name from an existing kv:// path so nested slot paths stay nested
73
+ * and shared secrets (same path, two usages) share one KV_* line.
74
+ * @param {string} prefix
75
+ * @param {string} kvPath
76
+ * @param {string} slotKey
77
+ * @param {string} securityKey
78
+ * @returns {string}
79
+ */
80
+ function envNameFromKvPath(prefix, kvPath, slotKey, securityKey) {
81
+ const parts = String(kvPath || '')
82
+ .replace(/^kv:\/\//, '')
83
+ .split('/')
84
+ .filter(Boolean);
85
+ if (parts.length >= 3) {
86
+ const folder = parts[1].replace(/-/g, '_').toUpperCase();
87
+ return `KV_${prefix}_${folder}_${securityKeyToVar(parts[parts.length - 1])}`;
88
+ }
89
+ if (parts.length === 2) {
90
+ return `KV_${prefix}_${securityKeyToVar(parts[1])}`;
91
+ }
92
+ return buildSlotEnvName(prefix, slotKey, securityKey);
93
+ }
94
+
56
95
  /**
57
96
  * @param {Array} effective
58
97
  * @param {Set<string>} seenNames
@@ -79,13 +118,12 @@ function addSecurityKeysFromAuthBlock(
79
118
  if (typeof securityVal === 'string' && isPlatformCatalogKvRef(securityVal)) {
80
119
  continue;
81
120
  }
82
- const envName = buildSlotEnvName(prefix, slotKey, key);
83
- if (seenNames.has(envName)) {
121
+ const pathVal = kvPathFromSecurityValue(securityVal, systemKey, slotKey, key);
122
+ if (!pathVal) {
84
123
  continue;
85
124
  }
86
- const pathVal =
87
- kvEnvKeyToPath(envName, systemKey) || buildSlotKvPath(systemKey, slotKey, key);
88
- if (!pathVal) {
125
+ const envName = envNameFromKvPath(prefix, pathVal, slotKey, key);
126
+ if (seenNames.has(envName)) {
89
127
  continue;
90
128
  }
91
129
  effective.push({
@@ -262,8 +262,13 @@ async function pushCredentialSecrets(dataplaneUrl, authConfig, options = {}) {
262
262
  buildItemsFromPayload(payload, secrets, itemsByKey);
263
263
  reconcileBearerTokenSecretPaths(itemsByKey, appName, authConfig);
264
264
 
265
+ const excluded = new Set(
266
+ Array.isArray(options.excludeKeys)
267
+ ? options.excludeKeys.map((key) => String(key).trim()).filter(Boolean)
268
+ : []
269
+ );
265
270
  const items = Array.from(itemsByKey.entries())
266
- .filter(([k]) => isValidKvPath(k))
271
+ .filter(([k]) => isValidKvPath(k) && !excluded.has(k))
267
272
  .map(([key, value]) => ({ key, value }));
268
273
 
269
274
  if (items.length === 0) return { pushed: 0, skipped: true };