@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
@@ -0,0 +1,356 @@
1
+ /**
2
+ * ChannelInstallEnvelope for CLI --json and MCP (not default HTTP).
3
+ *
4
+ * @fileoverview remainingSpec + verdicts → typed envelope
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { CHANNEL_TARGETS } = require('./channel-targets');
12
+ const { remainingSpec, CHATGPT_REMAINING_STEPS, SLACK_APPS_URL } = require('./channel-add-remaining');
13
+ const { buildChatGptFormValues, CHATGPT_APPS_CREATE_URL } = require('./chatgpt-setup-values');
14
+ const {
15
+ buildClaudeFormValues,
16
+ CLAUDE_CONNECTORS_URL
17
+ } = require('./claude-setup-values');
18
+ const { CLAUDE_REMAINING_STEPS } = require('./claude-runtime-contract');
19
+ const { DEVELOPER_PORTAL_APPS_URL } = require('./approval-guides/microsoft-copilot');
20
+ const {
21
+ userAction,
22
+ openUrlAction,
23
+ copyValueAction,
24
+ remainingStepAction
25
+ } = require('./channel-install-envelope-actions');
26
+ const {
27
+ copilotVaultMissing,
28
+ microsoftLoginWaiting,
29
+ microsoftExtraActions,
30
+ teamsBotWaiting
31
+ } = require('./channel-install-envelope-microsoft');
32
+
33
+ /**
34
+ * @param {string} systemKey
35
+ * @param {string} artifactId
36
+ * @returns {string}
37
+ */
38
+ function channelArtifactHref(systemKey, artifactId) {
39
+ return `/api/v1/builder/systems/${systemKey}/channels/artifacts/${artifactId}`;
40
+ }
41
+
42
+ /**
43
+ * @param {Object} params
44
+ * @returns {Object|null}
45
+ */
46
+ function retryLoginAction(params) {
47
+ if (params.channelTarget === 'slack' && params.ctx && params.ctx.slackConnected === true) {
48
+ return null;
49
+ }
50
+ if (params.channelTarget === 'teams-bot' && !teamsBotWaiting(params)) {
51
+ return null;
52
+ }
53
+ return userAction({
54
+ id: 'retry-login',
55
+ kind: 'retryLogin',
56
+ label: microsoftLoginWaiting(params) ? 'Get a new code' : 'Refresh login',
57
+ required: false
58
+ });
59
+ }
60
+
61
+ /**
62
+ * @param {Object} params
63
+ * @param {Object[]} actions
64
+ * @param {boolean} runtimeOk
65
+ * @returns {Object[]}
66
+ */
67
+ function withRetryLogin(params, actions, runtimeOk) {
68
+ if (!runtimeOk) {
69
+ return actions;
70
+ }
71
+ const retry = retryLoginAction(params);
72
+ return retry ? [...actions, retry] : actions;
73
+ }
74
+
75
+ /**
76
+ * @param {Object} spec
77
+ * @returns {Object[]}
78
+ */
79
+ function actionsFromRemainingSpec(spec) {
80
+ const actions = [];
81
+ const required = spec.handoffRequired;
82
+ if (typeof spec.downloadHref === 'string' && spec.downloadHref.trim()) {
83
+ actions.push(userAction({
84
+ id: 'download-app-package',
85
+ kind: 'downloadArtifact',
86
+ label: spec.downloadHeading || 'Download app package',
87
+ required,
88
+ extra: { href: spec.downloadHref.trim() }
89
+ }));
90
+ }
91
+ const open = openUrlAction(spec.openUrl, spec.openHeading || 'Open', 'open-vendor', required);
92
+ if (open) {
93
+ actions.push(open);
94
+ }
95
+ const activate = openUrlAction(
96
+ spec.activateUrl,
97
+ spec.activateHeading || 'Activate',
98
+ 'activate',
99
+ required
100
+ );
101
+ if (activate) {
102
+ actions.push(activate);
103
+ }
104
+ const admin = openUrlAction(spec.adminUrl, spec.adminHeading || 'Open admin', 'open-admin', required);
105
+ if (admin) {
106
+ actions.push(admin);
107
+ }
108
+ const auth = openUrlAction(spec.authUrl, spec.authHeading || 'Authentication', 'open-auth');
109
+ if (auth) {
110
+ actions.push(auth);
111
+ }
112
+ return actions;
113
+ }
114
+
115
+ function remainingStepActions(steps) {
116
+ if (!Array.isArray(steps)) {
117
+ return [];
118
+ }
119
+ return steps
120
+ .filter((step) => typeof step === 'string' && step.trim())
121
+ .map((step, index) => remainingStepAction(step.trim(), `remaining-${index + 1}`));
122
+ }
123
+
124
+ function chatgptCopyActions(runtimeValues) {
125
+ const values = buildChatGptFormValues({ runtimeValues: runtimeValues || {} });
126
+ return [
127
+ copyValueAction('Name', values.name, 'copy-name'),
128
+ copyValueAction('Description', values.description, 'copy-description'),
129
+ copyValueAction('Server URL', values.serverUrl, 'copy-server-url'),
130
+ copyValueAction('Authentication', values.authentication, 'copy-authentication')
131
+ ].filter(Boolean);
132
+ }
133
+
134
+ function claudeCopyActions(runtimeValues) {
135
+ const values = buildClaudeFormValues({ runtimeValues: runtimeValues || {} });
136
+ return [
137
+ copyValueAction('Name', values.name, 'copy-name'),
138
+ copyValueAction('Server URL', values.serverUrl, 'copy-server-url')
139
+ ].filter(Boolean);
140
+ }
141
+
142
+ function copilotArtifacts(systemKey) {
143
+ return [
144
+ {
145
+ id: 'appPackage',
146
+ kind: 'zip',
147
+ fileName: 'appPackage.zip',
148
+ contentType: 'application/zip',
149
+ href: channelArtifactHref(systemKey, 'appPackage')
150
+ }
151
+ ];
152
+ }
153
+
154
+ /**
155
+ * @param {Object} params
156
+ * @returns {Object}
157
+ */
158
+ function chatgptEnvelopeParts(params) {
159
+ const runtimeOk = resolveVerdicts(params).runtime.ok === true;
160
+ const values = buildChatGptFormValues({ runtimeValues: params.runtimeValues || {} });
161
+ const userActions = [
162
+ openUrlAction(CHATGPT_APPS_CREATE_URL, 'Open ChatGPT Apps', 'open-chatgpt-apps'),
163
+ ...chatgptCopyActions(params.runtimeValues)
164
+ ].filter(Boolean);
165
+ return {
166
+ artifacts: [],
167
+ userActions: runtimeOk ? withRetryLogin(params, userActions, true) : [],
168
+ remainingSteps: runtimeOk ? [...CHATGPT_REMAINING_STEPS] : [],
169
+ useValues: runtimeOk
170
+ ? {
171
+ Name: values.name,
172
+ Description: values.description,
173
+ 'Server URL': values.serverUrl,
174
+ Authentication: values.authentication
175
+ }
176
+ : {}
177
+ };
178
+ }
179
+
180
+ /**
181
+ * @param {Object} params
182
+ * @returns {Object}
183
+ */
184
+ function claudeEnvelopeParts(params) {
185
+ const runtimeOk = resolveVerdicts(params).runtime.ok === true;
186
+ const values = buildClaudeFormValues({ runtimeValues: params.runtimeValues || {} });
187
+ const userActions = [
188
+ openUrlAction(CLAUDE_CONNECTORS_URL, 'Open Claude connectors', 'open-claude-connectors'),
189
+ ...claudeCopyActions(params.runtimeValues)
190
+ ].filter(Boolean);
191
+ return {
192
+ artifacts: [],
193
+ userActions: runtimeOk ? withRetryLogin(params, userActions, true) : [],
194
+ remainingSteps: runtimeOk ? [...CLAUDE_REMAINING_STEPS] : [],
195
+ useValues: runtimeOk
196
+ ? {
197
+ Name: values.name,
198
+ 'Server URL': values.serverUrl
199
+ }
200
+ : {}
201
+ };
202
+ }
203
+
204
+ /**
205
+ * @param {Object} params
206
+ * @returns {Object}
207
+ */
208
+ function runCommandAction(command) {
209
+ if (typeof command !== 'string' || !command.trim()) {
210
+ return null;
211
+ }
212
+ return userAction({
213
+ id: 'run-command',
214
+ kind: 'runCommand',
215
+ label: 'Run this command',
216
+ extra: { command: command.trim() }
217
+ });
218
+ }
219
+
220
+ /**
221
+ * @param {Object} params
222
+ * @returns {Object}
223
+ */
224
+ function slackTokenSubmitAction(params) {
225
+ if (params.channelTarget !== 'slack') {
226
+ return null;
227
+ }
228
+ if (params.ctx && params.ctx.slackConnected === true) {
229
+ return null;
230
+ }
231
+ return userAction({
232
+ id: 'submit-session-token',
233
+ kind: 'submitInput',
234
+ label: 'Paste the challenge code from Slack',
235
+ extra: { field: 'token' }
236
+ });
237
+ }
238
+
239
+ /**
240
+ * @param {Object} params
241
+ * @returns {Object}
242
+ */
243
+ function defaultEnvelopeParts(params) {
244
+ const spec = remainingSpec(params.channelTarget, params.ctx || {}, params.runtimeValues);
245
+ const userActions = actionsFromRemainingSpec(spec);
246
+ const runCommand = runCommandAction(spec.slackSlashCommand);
247
+ if (runCommand) {
248
+ userActions.unshift(runCommand);
249
+ }
250
+ const tokenAction = slackTokenSubmitAction(params);
251
+ if (tokenAction) {
252
+ userActions.push(tokenAction);
253
+ }
254
+ userActions.push(...microsoftExtraActions(params));
255
+ const waitingCopilot =
256
+ params.channelTarget === 'microsoft-copilot' && copilotVaultMissing(params.runtimeValues);
257
+ const waitingTeams = teamsBotWaiting(params);
258
+ if (!waitingCopilot && !waitingTeams) {
259
+ userActions.push(...remainingStepActions(spec.steps));
260
+ }
261
+ const runtimeOk = resolveVerdicts(params).runtime.ok === true;
262
+ return {
263
+ artifacts: params.channelTarget === 'microsoft-copilot'
264
+ ? copilotArtifacts((params.ctx && params.ctx.instanceSystemKey) || params.channelTarget)
265
+ : [],
266
+ userActions: withRetryLogin(params, userActions, runtimeOk),
267
+ remainingSteps: Array.isArray(spec.steps) ? spec.steps.filter(Boolean) : [],
268
+ useValues: Object.fromEntries(
269
+ (spec.useLines || [])
270
+ .map((line) => String(line).split(/:\s(.*)/).filter(Boolean))
271
+ .filter((parts) => parts.length === 2 && !String(parts[1]).startsWith('/'))
272
+ )
273
+ };
274
+ }
275
+
276
+ /**
277
+ * @param {Object} params
278
+ * @returns {Object}
279
+ */
280
+ function resolveVerdicts(params) {
281
+ const provided = params.verdicts && typeof params.verdicts === 'object' ? params.verdicts : {};
282
+ const runtimeOk = provided.runtime ? provided.runtime.ok !== false : true;
283
+ return {
284
+ runtime: provided.runtime || { ok: runtimeOk, label: runtimeOk ? 'Deployed' : 'Unavailable' },
285
+ oauth: provided.oauth || { ok: true, label: 'Connected' },
286
+ vendorSession: provided.vendorSession || { ok: false, label: 'Approval required' }
287
+ };
288
+ }
289
+
290
+ /**
291
+ * @param {Object} verdicts
292
+ * @param {Object[]} userActions
293
+ * @returns {string}
294
+ */
295
+ function resolvePhase(verdicts, userActions) {
296
+ if (!verdicts.runtime || verdicts.runtime.ok !== true) {
297
+ return 'failed';
298
+ }
299
+ const required = (userActions || []).filter((action) => action.required === true);
300
+ const handoff = required.filter((action) => action.kind !== 'remainingVendorStep');
301
+ if (handoff.length > 0) {
302
+ return 'waitingForUser';
303
+ }
304
+ return 'ready';
305
+ }
306
+
307
+ function envelopePartsForTarget(channelTarget, params) {
308
+ if (channelTarget === 'chatgpt') {
309
+ return chatgptEnvelopeParts(params);
310
+ }
311
+ if (channelTarget === 'claude') {
312
+ return claudeEnvelopeParts(params);
313
+ }
314
+ return defaultEnvelopeParts(params);
315
+ }
316
+
317
+ /**
318
+ * @param {Object} params
319
+ * @param {string} params.channelTarget
320
+ * @param {string} [params.displayName]
321
+ * @param {Object|null} [params.ctx]
322
+ * @param {Object|null} [params.runtimeValues]
323
+ * @param {Object} [params.verdicts]
324
+ * @param {string} [params.requestId]
325
+ * @returns {Object}
326
+ */
327
+ function toChannelInstallEnvelope(params) {
328
+ const channelTarget = params.channelTarget;
329
+ const target = CHANNEL_TARGETS[channelTarget] || { displayLabel: channelTarget, key: channelTarget };
330
+ const displayName = params.displayName || (params.runtimeValues && params.runtimeValues.appName) || channelTarget;
331
+ const verdicts = resolveVerdicts(params);
332
+ const parts = envelopePartsForTarget(channelTarget, params);
333
+ const userActions = parts.userActions;
334
+ return {
335
+ channelTarget,
336
+ displayLabel: target.displayLabel || channelTarget,
337
+ displayName,
338
+ phase: resolvePhase(verdicts, userActions),
339
+ verdicts,
340
+ artifacts: parts.artifacts,
341
+ userActions,
342
+ remainingSteps: parts.remainingSteps,
343
+ useValues: parts.useValues,
344
+ fix: verdicts.runtime.ok === true ? null : { detail: 'Fix the Runtime connection, then retry install.' },
345
+ requestId: params.requestId || undefined
346
+ };
347
+ }
348
+
349
+ module.exports = {
350
+ channelArtifactHref,
351
+ toChannelInstallEnvelope,
352
+ SLACK_APPS_URL,
353
+ DEVELOPER_PORTAL_APPS_URL,
354
+ CHATGPT_APPS_CREATE_URL,
355
+ CLAUDE_CONNECTORS_URL
356
+ };
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Default HTTP help markdown from ChannelInstallEnvelope.
3
+ *
4
+ * @fileoverview Same remaining-step content as Canonical TTY / INSTALL.md
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { assertNoSecretsInArtifact } = require('./channel-artifact-generator');
12
+
13
+ /**
14
+ * @param {Object} envelope
15
+ * @returns {boolean}
16
+ */
17
+ function isRuntimeReady(envelope) {
18
+ return Boolean(envelope && envelope.verdicts && envelope.verdicts.runtime && envelope.verdicts.runtime.ok);
19
+ }
20
+
21
+ /**
22
+ * @param {Object} envelope
23
+ * @returns {string[]}
24
+ */
25
+ function statusLines(envelope) {
26
+ const ready = isRuntimeReady(envelope);
27
+ const lines = [`# ${envelope.displayName} — ${envelope.displayLabel}`, '', `Status: ${ready ? 'Ready' : 'Not ready'}`];
28
+ if (!ready) {
29
+ const label =
30
+ (envelope.verdicts && envelope.verdicts.runtime && envelope.verdicts.runtime.label) || 'Unavailable';
31
+ lines.push('', `Runtime: ${label}`);
32
+ if (envelope.fix && envelope.fix.detail) {
33
+ lines.push('', envelope.fix.detail);
34
+ }
35
+ return lines;
36
+ }
37
+ lines.push('', 'Runtime: Deployed');
38
+ if (envelope.verdicts && envelope.verdicts.oauth && envelope.verdicts.oauth.ok) {
39
+ lines.push('OAuth: Connected.');
40
+ }
41
+ return lines;
42
+ }
43
+
44
+ /**
45
+ * @param {Object} action
46
+ * @returns {string|null}
47
+ */
48
+ function renderOpenUrl(action) {
49
+ if (action.kind !== 'openUrl' || !action.href) {
50
+ return null;
51
+ }
52
+ const label = String(action.label || 'Open').trim();
53
+ if (/^(open|sign in|import|publish|approve|download)\b/i.test(label)) {
54
+ return `[${label}](${action.href}).`;
55
+ }
56
+ return `Open [${label}](${action.href}).`;
57
+ }
58
+
59
+ /**
60
+ * @param {Object} action
61
+ * @returns {string|null}
62
+ */
63
+ function renderDownload(action) {
64
+ if (action.kind !== 'downloadArtifact') {
65
+ return null;
66
+ }
67
+ const href = action.href || '';
68
+ if (!href) {
69
+ return null;
70
+ }
71
+ return `[${action.label}](${href})`;
72
+ }
73
+
74
+ /**
75
+ * @param {Object} action
76
+ * @returns {string|null}
77
+ */
78
+ function renderRunCommand(action) {
79
+ if (action.kind !== 'runCommand' || !action.command) {
80
+ return null;
81
+ }
82
+ return ['Run:', '', '```', action.command, '```'].join('\n');
83
+ }
84
+
85
+ /**
86
+ * @param {Object} envelope
87
+ * @returns {string[]}
88
+ */
89
+ function useValueLines(envelope) {
90
+ const fromActions = (envelope.userActions || [])
91
+ .filter((action) => action.kind === 'copyValue' && action.value)
92
+ .map((action) => `- ${action.label}: ${action.value}`);
93
+ if (fromActions.length > 0) {
94
+ return ['Use:', '', ...fromActions];
95
+ }
96
+ const entries = Object.entries(envelope.useValues || {}).filter(([, value]) => String(value || '').trim());
97
+ if (entries.length === 0) {
98
+ return [];
99
+ }
100
+ return ['Use:', '', ...entries.map(([label, value]) => `- ${label}: ${value}`)];
101
+ }
102
+
103
+ /**
104
+ * @param {Object} envelope
105
+ * @returns {string[]}
106
+ */
107
+ function remainingLines(envelope) {
108
+ const steps = Array.isArray(envelope.remainingSteps)
109
+ ? envelope.remainingSteps.filter((step) => typeof step === 'string' && step.trim())
110
+ : [];
111
+ if (steps.length === 0 || !isRuntimeReady(envelope)) {
112
+ return [];
113
+ }
114
+ const label = envelope.displayLabel || 'vendor';
115
+ return [`Remaining ${label} steps:`, '', ...steps.map((step) => `- ${step}`)];
116
+ }
117
+
118
+ /**
119
+ * @param {Object} envelope
120
+ * @returns {string}
121
+ */
122
+ function toChannelHelpMarkdown(envelope) {
123
+ const lines = [...statusLines(envelope)];
124
+ if (isRuntimeReady(envelope)) {
125
+ const opens = (envelope.userActions || []).map(renderOpenUrl).filter(Boolean);
126
+ const downloads = (envelope.userActions || []).map(renderDownload).filter(Boolean);
127
+ const commands = (envelope.userActions || []).map(renderRunCommand).filter(Boolean);
128
+ if (opens.length) {
129
+ lines.push('', ...opens);
130
+ }
131
+ const useLines = useValueLines(envelope);
132
+ if (useLines.length) {
133
+ lines.push('', ...useLines);
134
+ }
135
+ if (downloads.length) {
136
+ lines.push('', ...downloads);
137
+ }
138
+ if (commands.length) {
139
+ lines.push('', ...commands);
140
+ }
141
+ const remaining = remainingLines(envelope);
142
+ if (remaining.length) {
143
+ lines.push('', ...remaining);
144
+ }
145
+ }
146
+ const markdown = `${lines.join('\n').trim()}\n`;
147
+ assertNoSecretsInArtifact(markdown);
148
+ return markdown;
149
+ }
150
+
151
+ /**
152
+ * Help-only fields. Default HTTP also includes phase + userActions via toChannelInstallHttpPayload.
153
+ * @param {Object} envelope
154
+ * @returns {{ helpMarkdown: string, helpMarkdownContentType: string, refreshHintSeconds?: number }}
155
+ */
156
+ function toChannelHelpHttpPayload(envelope) {
157
+ const helpMarkdown = toChannelHelpMarkdown(envelope);
158
+ const payload = {
159
+ helpMarkdown,
160
+ helpMarkdownContentType: 'text/markdown'
161
+ };
162
+ if (envelope.phase === 'waitingForUser') {
163
+ payload.refreshHintSeconds = 15;
164
+ }
165
+ return payload;
166
+ }
167
+
168
+ module.exports = {
169
+ toChannelHelpMarkdown,
170
+ toChannelHelpHttpPayload,
171
+ isRuntimeReady
172
+ };
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Default HTTP JSON for channel install GET/POST/continue.
3
+ *
4
+ * @fileoverview helpMarkdown + phase + safe userActions (not session SSOT)
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { toChannelHelpHttpPayload } = require('./channel-install-help-md');
12
+
13
+ const SECRETISH = /xox[baprs]-|sk-[a-zA-Z0-9]{8,}|Bearer\s+[A-Za-z0-9._-]{12,}/i;
14
+
15
+ /**
16
+ * @param {unknown} value
17
+ * @returns {boolean}
18
+ */
19
+ function looksSecret(value) {
20
+ return typeof value === 'string' && SECRETISH.test(value);
21
+ }
22
+
23
+ /**
24
+ * Strip secret-shaped fields from envelope actions for the default HTTP JSON.
25
+ * @param {Object[]} [actions]
26
+ * @returns {Object[]}
27
+ */
28
+ function safeUserActions(actions) {
29
+ return (Array.isArray(actions) ? actions : []).map((action) => {
30
+ const copy = { ...action };
31
+ if (looksSecret(copy.value)) {
32
+ delete copy.value;
33
+ }
34
+ if (looksSecret(copy.command)) {
35
+ delete copy.command;
36
+ }
37
+ if (looksSecret(copy.text)) {
38
+ delete copy.text;
39
+ }
40
+ return copy;
41
+ });
42
+ }
43
+
44
+ /**
45
+ * Default machine wizard payload. verbose=true still returns the full envelope.
46
+ * @param {Object} envelope
47
+ * @returns {Object}
48
+ */
49
+ function toChannelInstallHttpPayload(envelope) {
50
+ const help = toChannelHelpHttpPayload(envelope);
51
+ return {
52
+ ...help,
53
+ phase: envelope.phase,
54
+ userActions: safeUserActions(envelope.userActions),
55
+ verdicts: envelope.verdicts
56
+ };
57
+ }
58
+
59
+ module.exports = {
60
+ safeUserActions,
61
+ toChannelInstallHttpPayload
62
+ };
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Match Dataplane Connected Systems to a Channel Definition.
3
+ *
4
+ * @fileoverview Builder inventory projection — no second lifecycle store
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /**
12
+ * @param {Object|null} system
13
+ * @returns {string|null}
14
+ */
15
+ function readStamp(block) {
16
+ if (!block || typeof block !== 'object') {
17
+ return null;
18
+ }
19
+ const stamped =
20
+ (typeof block.definitionKey === 'string' && block.definitionKey) ||
21
+ (typeof block.definitionKey === 'string' && block.definitionKey);
22
+ return stamped || null;
23
+ }
24
+
25
+ function readDefinitionKeyFromSystem(system) {
26
+ if (!system || typeof system !== 'object') {
27
+ return null;
28
+ }
29
+ const fromChannel = readStamp(system.channel);
30
+ if (fromChannel) {
31
+ return fromChannel;
32
+ }
33
+ const fallback = system.config && system.config.extensions && system.config.extensions.channel;
34
+ const fromConfig = readStamp(fallback);
35
+ if (fromConfig) {
36
+ return fromConfig;
37
+ }
38
+ if (fallback && typeof fallback.type === 'string' && fallback.type) {
39
+ return null;
40
+ }
41
+ return null;
42
+ }
43
+
44
+ function isInstanceKeyForDefinition(systemKey, definitionKey) {
45
+ return (
46
+ typeof systemKey === 'string' &&
47
+ systemKey !== definitionKey &&
48
+ systemKey.startsWith(`${definitionKey}-`)
49
+ );
50
+ }
51
+
52
+ /**
53
+ * @param {Object} system
54
+ * @returns {Object}
55
+ */
56
+ function toInstallationProjection(system) {
57
+ const runtime =
58
+ system.channel && system.channel.runtimeTarget && system.channel.runtimeTarget.defaultSystemKey
59
+ ? system.channel.runtimeTarget.defaultSystemKey
60
+ : 'runtime-orchestration';
61
+ return {
62
+ key: system.key,
63
+ displayName: system.displayName || system.key,
64
+ version: system.version || system.configVersion || null,
65
+ lifecycle: system.lifecycle || system.status || null,
66
+ availability: system.availability || null,
67
+ authenticationStatus: system.authenticationStatus || system.authStatus || null,
68
+ connectedSystemKey: system.key,
69
+ runtimeTarget: { systemKey: runtime }
70
+ };
71
+ }
72
+
73
+ /**
74
+ * @param {Object[]} systems
75
+ * @param {string} definitionKey
76
+ * @returns {Object[]}
77
+ */
78
+ function installationsForDefinition(systems, definitionKey) {
79
+ return (Array.isArray(systems) ? systems : [])
80
+ .filter((system) => {
81
+ if (!system || typeof system !== 'object' || system.key === definitionKey) {
82
+ return false;
83
+ }
84
+ return (
85
+ readDefinitionKeyFromSystem(system) === definitionKey ||
86
+ isInstanceKeyForDefinition(system.key, definitionKey)
87
+ );
88
+ })
89
+ .map(toInstallationProjection);
90
+ }
91
+
92
+ /**
93
+ * @param {Object[]} installations
94
+ * @returns {{ count: number, statusRollup: string }}
95
+ */
96
+ function summarizeInstallations(installations) {
97
+ const rows = Array.isArray(installations) ? installations : [];
98
+ if (rows.length === 0) {
99
+ return { count: 0, statusRollup: 'none' };
100
+ }
101
+ const statuses = rows.map((row) => row.lifecycle || row.availability || 'unknown');
102
+ const unique = [...new Set(statuses)];
103
+ return {
104
+ count: rows.length,
105
+ statusRollup: unique.length === 1 ? unique[0] : 'mixed'
106
+ };
107
+ }
108
+
109
+ module.exports = {
110
+ readDefinitionKeyFromSystem,
111
+ isInstanceKeyForDefinition,
112
+ toInstallationProjection,
113
+ installationsForDefinition,
114
+ summarizeInstallations
115
+ };