@aifabrix/builder 2.53.0 → 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 +119 -16
  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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Builder API HTTP server (plan 154.0 — pre-Nest unified router).
2
+ * Builder API HTTP server (pre-Nest unified router).
3
3
  *
4
4
  * @fileoverview Full V1 route set via capability registry
5
5
  * @author AI Fabrix Team
@@ -18,6 +18,8 @@ const { writeAccessLog } = require('./access-log');
18
18
  const { dispatchCapabilityRoute } = require('./builder-api-route-dispatch');
19
19
  const { renderBuilderApiRedocHtml, resolvePublicBaseUrl } = require('./openapi-docs-serving');
20
20
  const { buildProblemDetails, writeProblemDetails } = require('./http-problem-details');
21
+ const { sendRaw } = require('./http-raw-response');
22
+ const { handleCorsPreflight } = require('./builder-api-cors');
21
23
 
22
24
  /**
23
25
  * @param {import('http').ServerResponse} res
@@ -171,6 +173,10 @@ async function handleRequest(req, res) {
171
173
  const url = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
172
174
  const pathname = url.pathname;
173
175
 
176
+ if (handleCorsPreflight(req, res, requestId, pathname, logRequestOutcome)) {
177
+ return;
178
+ }
179
+
174
180
  if (isRedocHtmlPath(pathname) || isOpenApiJsonPath(pathname)) {
175
181
  handleOpenApiDocs(req, res, pathname, requestId);
176
182
  logRequestOutcome({
@@ -196,6 +202,7 @@ async function handleRequest(req, res) {
196
202
  res,
197
203
  sendJson,
198
204
  sendProblem,
205
+ sendRaw,
199
206
  readJsonBody,
200
207
  resolved,
201
208
  query,
@@ -76,8 +76,62 @@ const CAPABILITIES = Object.freeze([
76
76
  { method: 'GET', route: '/systems/:systemKey/channels/package/zip', handlerId: 'getChannelPackageZip', operationType: 'channel.read', mutating: false },
77
77
  { method: 'GET', route: '/systems/:systemKey/channels/branding', handlerId: 'getChannelBranding', operationType: 'channel.read', mutating: false },
78
78
  { method: 'PATCH', route: '/systems/:systemKey/channels/branding', handlerId: 'patchChannelBranding', operationType: 'channel', mutating: true },
79
+ { method: 'GET', route: '/channels', handlerId: 'listChannelTargets', operationType: 'channel.read', mutating: false },
80
+ { method: 'GET', route: '/channels/:definitionKey', handlerId: 'getChannelDefinitionDetail', operationType: 'channel.read', mutating: false },
81
+ {
82
+ method: 'GET',
83
+ route: '/channels/:definitionKey/installations/:systemKey',
84
+ handlerId: 'getChannelInstallation',
85
+ operationType: 'channel.read',
86
+ mutating: false
87
+ },
88
+ { method: 'POST', route: '/systems/:systemKey/channels/install', handlerId: 'runChannelInstall', operationType: 'channel', mutating: true },
89
+ { method: 'GET', route: '/systems/:systemKey/channels/install', handlerId: 'getChannelInstall', operationType: 'channel.read', mutating: false },
90
+ {
91
+ method: 'GET',
92
+ route: '/systems/:systemKey/channels/install/help.md',
93
+ handlerId: 'getChannelInstallHelp',
94
+ operationType: 'channel.read',
95
+ mutating: false
96
+ },
97
+ {
98
+ method: 'POST',
99
+ route: '/systems/:systemKey/channels/install/continue',
100
+ handlerId: 'continueChannelInstall',
101
+ operationType: 'channel',
102
+ mutating: true
103
+ },
104
+ { method: 'GET', route: '/systems/:systemKey/channels/artifacts', handlerId: 'listChannelArtifacts', operationType: 'channel.read', mutating: false },
105
+ {
106
+ method: 'GET',
107
+ route: '/systems/:systemKey/channels/artifacts/:artifactId',
108
+ handlerId: 'getChannelArtifact',
109
+ operationType: 'channel.read',
110
+ mutating: false
111
+ },
112
+ {
113
+ method: 'GET',
114
+ route: '/systems/:systemKey/channels/callbacks/:provider',
115
+ handlerId: 'getChannelCallback',
116
+ operationType: 'channel',
117
+ mutating: true
118
+ },
119
+ {
120
+ method: 'POST',
121
+ route: '/systems/:systemKey/channels/callbacks/:provider',
122
+ handlerId: 'postChannelCallback',
123
+ operationType: 'channel',
124
+ mutating: true
125
+ },
79
126
  { method: 'GET', route: '/health', handlerId: 'health', operationType: 'infra', mutating: false },
80
127
  { method: 'GET', route: '/health/ready', handlerId: 'ready', operationType: 'infra', mutating: false },
128
+ {
129
+ method: 'GET',
130
+ route: '/auth/permissions',
131
+ handlerId: 'getAuthPermissions',
132
+ operationType: 'infra',
133
+ mutating: false
134
+ },
81
135
  {
82
136
  method: 'GET',
83
137
  route: '/help',
@@ -0,0 +1,359 @@
1
+ /**
2
+ * Resolve channel install help for a definition or instance key.
3
+ *
4
+ * @fileoverview POST identity + per-instance GET (session or synthesized)
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const path = require('path');
12
+ const fs = require('../internal/fs-real-sync');
13
+ const { toChannelInstallEnvelope } = require('../channels/channel-install-envelope');
14
+ const { toChannelInstallHttpPayload } = require('../channels/channel-install-http-payload');
15
+ const { resolveChannelRuntimeValues } = require('../channels/channel-runtime-resolver');
16
+ const { vendorFacingUrlsFromCtx } = require('../channels/vendor-dataplane-url');
17
+ const {
18
+ getChannelDefinition,
19
+ listChannelDefinitionKeys
20
+ } = require('../channels/channel-definition-registry');
21
+ const {
22
+ readChannelInstallSession,
23
+ writeChannelInstallSession,
24
+ httpError
25
+ } = require('./channel-install-session');
26
+ const { peekPortalMicrosoftLogin } = require('../channels/microsoft-provisioning/login-portal');
27
+
28
+ /**
29
+ * @param {Object} payload
30
+ * @param {string} definitionKey
31
+ * @param {string} installationKey
32
+ * @returns {Object}
33
+ */
34
+ function attachInstallIdentity(payload, definitionKey, installationKey) {
35
+ return {
36
+ ...payload,
37
+ definitionKey,
38
+ installationKey,
39
+ systemKey: installationKey
40
+ };
41
+ }
42
+
43
+ /**
44
+ * @param {Object[]} [actions]
45
+ * @param {string} id
46
+ * @returns {string}
47
+ */
48
+ function copyActionValue(actions, id) {
49
+ const row = (actions || []).find((action) => action && action.id === id);
50
+ return row && typeof row.value === 'string' ? row.value.trim() : '';
51
+ }
52
+
53
+ /**
54
+ * @param {Object} session
55
+ * @returns {Object}
56
+ */
57
+ function runtimeValuesFromStoredEnvelope(session) {
58
+ const envelope = session.envelope || {};
59
+ const use = envelope.useValues || {};
60
+ const actions = envelope.userActions || [];
61
+ return {
62
+ appName:
63
+ use.Name ||
64
+ copyActionValue(actions, 'copy-name') ||
65
+ session.displayName ||
66
+ session.name ||
67
+ '',
68
+ appDescription: use.Description || copyActionValue(actions, 'copy-description') || '',
69
+ mcpUrl: use['Server URL'] || copyActionValue(actions, 'copy-server-url') || ''
70
+ };
71
+ }
72
+
73
+ /**
74
+ * @param {Object} session
75
+ * @returns {Object}
76
+ */
77
+ function copilotRuntimeFromSession(session) {
78
+ const peek = peekPortalMicrosoftLogin(session.installationKey) || {};
79
+ return {
80
+ appName: session.displayName || session.name,
81
+ oauthPluginVaultReferenceId: session.oauthPluginVaultReferenceId,
82
+ packageAppId: session.packageAppId,
83
+ microsoftLoginUrl: peek.loginUrl || session.microsoftLoginUrl,
84
+ microsoftLoginUserCode: peek.userCode || session.microsoftLoginUserCode,
85
+ microsoftAuthConfigFallback: session.microsoftAuthConfigFallback === true
86
+ };
87
+ }
88
+
89
+ /**
90
+ * @param {Object} session
91
+ * @returns {Object}
92
+ */
93
+ function refreshCopilotEnvelope(session, ctx) {
94
+ const definitionKey = session.definitionKey || session.channelTarget;
95
+ if (definitionKey !== 'microsoft-copilot') {
96
+ return session.envelope;
97
+ }
98
+ return toChannelInstallEnvelope({
99
+ channelTarget: definitionKey,
100
+ displayName: session.displayName || session.name || 'Microsoft Copilot',
101
+ ctx: {
102
+ instanceSystemKey: session.installationKey,
103
+ projectRoot: ctx && typeof ctx.getUserRoot === 'function' ? ctx.getUserRoot() : undefined
104
+ },
105
+ runtimeValues: copilotRuntimeFromSession(session),
106
+ verdicts: (session.envelope && session.envelope.verdicts) || {
107
+ runtime: { ok: true, label: 'Deployed' }
108
+ }
109
+ });
110
+ }
111
+
112
+ /**
113
+ * Rebuild ChatGPT help so Open ChatGPT Apps uses the current drafts URL.
114
+ * @param {Object} session
115
+ * @returns {Object}
116
+ */
117
+ function refreshChatGptEnvelope(session) {
118
+ const definitionKey = session.definitionKey || session.channelTarget;
119
+ if (definitionKey !== 'chatgpt') {
120
+ return session.envelope;
121
+ }
122
+ return toChannelInstallEnvelope({
123
+ channelTarget: definitionKey,
124
+ displayName: session.displayName || session.name || 'ChatGPT',
125
+ ctx: {},
126
+ runtimeValues: runtimeValuesFromStoredEnvelope(session),
127
+ verdicts: (session.envelope && session.envelope.verdicts) || {
128
+ runtime: { ok: true, label: 'Deployed' }
129
+ }
130
+ });
131
+ }
132
+
133
+ /**
134
+ * Rebuild Claude help so connectors URL and Name/Server URL stay current.
135
+ * @param {Object} session
136
+ * @returns {Object}
137
+ */
138
+ function refreshClaudeEnvelope(session) {
139
+ const definitionKey = session.definitionKey || session.channelTarget;
140
+ if (definitionKey !== 'claude') {
141
+ return session.envelope;
142
+ }
143
+ return toChannelInstallEnvelope({
144
+ channelTarget: definitionKey,
145
+ displayName: session.displayName || session.name || 'Claude',
146
+ ctx: {},
147
+ runtimeValues: runtimeValuesFromStoredEnvelope(session),
148
+ verdicts: (session.envelope && session.envelope.verdicts) || {
149
+ runtime: { ok: true, label: 'Deployed' }
150
+ }
151
+ });
152
+ }
153
+
154
+ /**
155
+ * @param {Object} session
156
+ * @returns {Object}
157
+ */
158
+ function refreshStoredEnvelope(session, ctx) {
159
+ const definitionKey = session.definitionKey || session.channelTarget;
160
+ if (definitionKey === 'microsoft-copilot') {
161
+ return refreshCopilotEnvelope(session, ctx);
162
+ }
163
+ if (definitionKey === 'chatgpt') {
164
+ return refreshChatGptEnvelope(session);
165
+ }
166
+ if (definitionKey === 'claude') {
167
+ return refreshClaudeEnvelope(session);
168
+ }
169
+ return session.envelope;
170
+ }
171
+
172
+ function helpPayloadFromSession(session, ctx) {
173
+ const envelope = refreshStoredEnvelope(session, ctx) || session.envelope;
174
+ if (envelope && typeof envelope === 'object') {
175
+ const payload = toChannelInstallHttpPayload(envelope);
176
+ const definitionKey = session.definitionKey || session.channelTarget;
177
+ const installationKey = session.installationKey;
178
+ if (!definitionKey || !installationKey) {
179
+ return payload;
180
+ }
181
+ return attachInstallIdentity(payload, definitionKey, installationKey);
182
+ }
183
+ const payload = {
184
+ helpMarkdown: session.helpMarkdown,
185
+ helpMarkdownContentType: 'text/markdown'
186
+ };
187
+ if (session.phase === 'waitingForUser') {
188
+ payload.refreshHintSeconds = 15;
189
+ }
190
+ const definitionKey = session.definitionKey || session.channelTarget;
191
+ const installationKey = session.installationKey;
192
+ if (!definitionKey || !installationKey) {
193
+ return payload;
194
+ }
195
+ return attachInstallIdentity(payload, definitionKey, installationKey);
196
+ }
197
+
198
+ /**
199
+ * @param {string} instanceKey
200
+ * @returns {string|null}
201
+ */
202
+ function definitionKeyFromInstanceKey(instanceKey) {
203
+ const keys = listChannelDefinitionKeys().sort((left, right) => right.length - left.length);
204
+ for (const key of keys) {
205
+ if (instanceKey === key || instanceKey.startsWith(`${key}-`)) {
206
+ return key;
207
+ }
208
+ }
209
+ return null;
210
+ }
211
+
212
+ /**
213
+ * Catalog key for install/continue. Path may be the definition or the instance.
214
+ * @param {import('./workspace-context').WorkspaceContext} ctx
215
+ * @returns {string}
216
+ */
217
+ function resolveInstallDefinitionKey(ctx) {
218
+ if (getChannelDefinition(ctx.systemKey)) {
219
+ return ctx.systemKey;
220
+ }
221
+ const session = readChannelInstallSession(ctx);
222
+ const fromSession = session && (session.definitionKey || session.channelTarget);
223
+ if (typeof fromSession === 'string' && getChannelDefinition(fromSession)) {
224
+ return fromSession;
225
+ }
226
+ const fromInstance = definitionKeyFromInstanceKey(ctx.systemKey);
227
+ if (fromInstance && getChannelDefinition(fromInstance)) {
228
+ return fromInstance;
229
+ }
230
+ return ctx.systemKey;
231
+ }
232
+
233
+ /**
234
+ * @param {import('./workspace-context').WorkspaceContext} ctx
235
+ * @returns {Object|null}
236
+ */
237
+ function readInstanceSystemDoc(ctx) {
238
+ const filePath = path.join(ctx.getIntegrationPath(), `${ctx.systemKey}-system.json`);
239
+ if (!fs.existsSync(filePath)) {
240
+ return null;
241
+ }
242
+ try {
243
+ const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
244
+ return parsed && typeof parsed === 'object' ? parsed : null;
245
+ } catch {
246
+ return null;
247
+ }
248
+ }
249
+
250
+ /**
251
+ * @param {Object} doc
252
+ * @param {string} instanceKey
253
+ * @returns {string|null}
254
+ */
255
+ function definitionKeyFromDoc(doc, instanceKey) {
256
+ const stamped = doc && doc.channel && doc.channel.definitionKey;
257
+ if (typeof stamped === 'string' && stamped.trim()) {
258
+ return stamped.trim();
259
+ }
260
+ return definitionKeyFromInstanceKey(instanceKey);
261
+ }
262
+
263
+ /**
264
+ * @param {import('./workspace-context').WorkspaceContext} ctx
265
+ * @param {string} definitionKey
266
+ * @param {string} displayName
267
+ * @param {string} [description]
268
+ * @returns {Object}
269
+ */
270
+ function runtimeValuesForHelp(ctx, definitionKey, displayName, description) {
271
+ const vendor = vendorFacingUrlsFromCtx(ctx);
272
+ return resolveChannelRuntimeValues({
273
+ dataplaneBaseUrl: vendor.dataplaneBaseUrl,
274
+ controllerUrl: vendor.controllerUrl,
275
+ channelTarget: definitionKey,
276
+ appName: displayName,
277
+ instanceSystemKey: ctx.systemKey,
278
+ environment: ctx.environment,
279
+ appDescription: description || ''
280
+ });
281
+ }
282
+
283
+ /**
284
+ * @param {import('./workspace-context').WorkspaceContext} ctx
285
+ * @param {Object} doc
286
+ * @returns {{ payload: Object, envelope: Object }|null}
287
+ */
288
+ function synthesizeInstallHelp(ctx, doc) {
289
+ const installationKey = ctx.systemKey;
290
+ const definitionKey = definitionKeyFromDoc(doc, installationKey);
291
+ if (!definitionKey || !getChannelDefinition(definitionKey)) {
292
+ return null;
293
+ }
294
+ const displayName =
295
+ (typeof doc.displayName === 'string' && doc.displayName.trim()) || installationKey;
296
+ const envelope = toChannelInstallEnvelope({
297
+ channelTarget: definitionKey,
298
+ displayName,
299
+ ctx: {
300
+ dataplaneUrl: ctx.dataplaneUrl,
301
+ environment: ctx.environment,
302
+ projectRoot: ctx.getUserRoot()
303
+ },
304
+ runtimeValues: runtimeValuesForHelp(ctx, definitionKey, displayName, doc.description),
305
+ verdicts: { runtime: { ok: true, label: 'Deployed' } }
306
+ });
307
+ const payload = attachInstallIdentity(
308
+ toChannelInstallHttpPayload(envelope),
309
+ definitionKey,
310
+ installationKey
311
+ );
312
+ writeChannelInstallSession(ctx, {
313
+ name: displayName,
314
+ displayName,
315
+ phase: envelope.phase,
316
+ envelope,
317
+ definitionKey,
318
+ installationKey
319
+ });
320
+ return { payload, envelope };
321
+ }
322
+
323
+ /**
324
+ * @param {import('./workspace-context').WorkspaceContext} ctx
325
+ * @param {Object} [query]
326
+ * @returns {Object}
327
+ */
328
+ function resolveChannelInstallHelp(ctx, query = {}) {
329
+ const session = readChannelInstallSession(ctx);
330
+ if (session) {
331
+ if (query.verbose === 'true' || query.verbose === true) {
332
+ return refreshStoredEnvelope(session, ctx) || session.envelope;
333
+ }
334
+ return helpPayloadFromSession(session, ctx);
335
+ }
336
+ if (getChannelDefinition(ctx.systemKey)) {
337
+ throw httpError('Channel install session not found. POST install first.', 404);
338
+ }
339
+ const doc = readInstanceSystemDoc(ctx);
340
+ if (!doc) {
341
+ throw httpError('Channel install session not found. POST install first.', 404);
342
+ }
343
+ const synthesized = synthesizeInstallHelp(ctx, doc);
344
+ if (!synthesized) {
345
+ throw httpError('Channel install session not found. POST install first.', 404);
346
+ }
347
+ if (query.verbose === 'true' || query.verbose === true) {
348
+ return synthesized.envelope;
349
+ }
350
+ return synthesized.payload;
351
+ }
352
+
353
+ module.exports = {
354
+ attachInstallIdentity,
355
+ helpPayloadFromSession,
356
+ definitionKeyFromInstanceKey,
357
+ resolveInstallDefinitionKey,
358
+ resolveChannelInstallHelp
359
+ };
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Non-secret channel install session under the workspace integration folder.
3
+ *
4
+ * @fileoverview Persist help markdown + envelope for GET continue
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const path = require('path');
12
+ const fs = require('../internal/fs-real-sync');
13
+ const { ensureIntegrationDirectory } = require('./workspace-context');
14
+
15
+ const SESSION_FILE = '.channel-install.json';
16
+
17
+ /**
18
+ * @param {import('./workspace-context').WorkspaceContext} ctx
19
+ * @returns {string}
20
+ */
21
+ function sessionPath(ctx) {
22
+ return path.join(ctx.getIntegrationPath(), SESSION_FILE);
23
+ }
24
+
25
+ /**
26
+ * @param {string} filePath
27
+ * @param {Object} stored
28
+ */
29
+ function writeSessionFile(filePath, stored) {
30
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
31
+ fs.writeFileSync(filePath, `${JSON.stringify(stored, null, 2)}\n`, 'utf8');
32
+ }
33
+
34
+ /**
35
+ * @param {import('./workspace-context').WorkspaceContext} ctx
36
+ * @returns {string|null}
37
+ */
38
+ function instanceSessionPath(ctx) {
39
+ const instanceKey = typeof ctx.instanceSystemKey === 'string' ? ctx.instanceSystemKey.trim() : '';
40
+ if (!instanceKey || instanceKey === ctx.systemKey) {
41
+ return null;
42
+ }
43
+ return path.join(ctx.getIntegrationRoot(), instanceKey, SESSION_FILE);
44
+ }
45
+
46
+ /**
47
+ * @param {import('./workspace-context').WorkspaceContext} ctx
48
+ * @returns {Object|null}
49
+ */
50
+ function readChannelInstallSession(ctx) {
51
+ const filePath = sessionPath(ctx);
52
+ if (!fs.existsSync(filePath)) {
53
+ return null;
54
+ }
55
+ try {
56
+ const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
57
+ return parsed && typeof parsed === 'object' ? parsed : null;
58
+ } catch {
59
+ return null;
60
+ }
61
+ }
62
+
63
+ /**
64
+ * @param {import('./workspace-context').WorkspaceContext} ctx
65
+ * @param {Object} session
66
+ * @returns {Object}
67
+ */
68
+ function writeChannelInstallSession(ctx, session) {
69
+ ensureIntegrationDirectory(ctx);
70
+ const installationKey = ctx.instanceSystemKey || session.installationKey || ctx.systemKey;
71
+ const definitionKey = session.definitionKey || ctx.systemKey;
72
+ const stored = {
73
+ channelTarget: definitionKey,
74
+ definitionKey,
75
+ installationKey,
76
+ name: session.name,
77
+ displayName: session.displayName,
78
+ profile: session.profile || null,
79
+ allowExperimental: session.allowExperimental === true,
80
+ phase: session.phase,
81
+ helpMarkdown: session.helpMarkdown,
82
+ envelope: session.envelope,
83
+ slackAuthTicket: typeof session.slackAuthTicket === 'string' ? session.slackAuthTicket : undefined,
84
+ commandPrefix: trimSessionString(session.commandPrefix),
85
+ packageVersion: trimSessionString(session.packageVersion),
86
+ packageAppId: trimSessionString(session.packageAppId),
87
+ oauthPluginVaultReferenceId: trimSessionString(session.oauthPluginVaultReferenceId),
88
+ microsoftLoginUrl: trimSessionString(session.microsoftLoginUrl),
89
+ microsoftLoginUserCode: trimSessionString(session.microsoftLoginUserCode),
90
+ microsoftAuthConfigFallback: session.microsoftAuthConfigFallback === true ? true : undefined,
91
+ updatedAt: new Date().toISOString()
92
+ };
93
+ writeSessionFile(sessionPath(ctx), stored);
94
+ const extra = instanceSessionPath(ctx);
95
+ if (extra) {
96
+ writeSessionFile(extra, stored);
97
+ }
98
+ return stored;
99
+ }
100
+
101
+ /**
102
+ * @param {string} message
103
+ * @param {number} statusCode
104
+ * @returns {Error}
105
+ */
106
+ function httpError(message, statusCode) {
107
+ const err = new Error(message);
108
+ err.statusCode = statusCode;
109
+ return err;
110
+ }
111
+
112
+ /**
113
+ * @param {unknown} value
114
+ * @returns {string|undefined}
115
+ */
116
+ function trimSessionString(value) {
117
+ if (typeof value !== 'string' || !value.trim()) {
118
+ return undefined;
119
+ }
120
+ return value.trim();
121
+ }
122
+
123
+ /**
124
+ * @param {string} integrationRoot
125
+ * @param {string} folderName
126
+ * @returns {Object|null}
127
+ */
128
+ function readNamedSession(integrationRoot, folderName) {
129
+ const filePath = path.join(integrationRoot, folderName, SESSION_FILE);
130
+ if (!fs.existsSync(filePath)) {
131
+ return null;
132
+ }
133
+ try {
134
+ const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
135
+ return parsed && typeof parsed === 'object' ? parsed : null;
136
+ } catch {
137
+ return null;
138
+ }
139
+ }
140
+
141
+ /**
142
+ * @param {Object|null} session
143
+ * @param {string} definitionKey
144
+ * @returns {boolean}
145
+ */
146
+ function sessionMatchesDefinition(session, definitionKey) {
147
+ if (!session) {
148
+ return false;
149
+ }
150
+ const key = session.definitionKey || session.channelTarget;
151
+ return key === definitionKey;
152
+ }
153
+
154
+ /**
155
+ * Catalog GET uses the definition key; the session lives on the instance folder.
156
+ * @param {import('./workspace-context').WorkspaceContext} ctx
157
+ * @param {string} definitionKey
158
+ * @returns {Object|null}
159
+ */
160
+ function readChannelInstallSessionForDefinition(ctx, definitionKey) {
161
+ const direct = readChannelInstallSession(ctx);
162
+ if (sessionMatchesDefinition(direct, definitionKey)) {
163
+ return direct;
164
+ }
165
+ const root = typeof ctx.getIntegrationRoot === 'function' ? ctx.getIntegrationRoot() : '';
166
+ if (!root || !fs.existsSync(root)) {
167
+ return direct;
168
+ }
169
+ return newestMatchingSession(root, definitionKey, direct);
170
+ }
171
+
172
+ /**
173
+ * @param {string} root
174
+ * @param {string} definitionKey
175
+ * @param {Object|null} seed
176
+ * @returns {Object|null}
177
+ */
178
+ function newestMatchingSession(root, definitionKey, seed) {
179
+ let best = seed;
180
+ let bestAt = (seed && seed.updatedAt) || '';
181
+ for (const name of fs.readdirSync(root)) {
182
+ if (name !== definitionKey && !String(name).startsWith(`${definitionKey}-`)) {
183
+ continue;
184
+ }
185
+ const session = readNamedSession(root, name);
186
+ if (!sessionMatchesDefinition(session, definitionKey)) {
187
+ continue;
188
+ }
189
+ const updated = session.updatedAt || '';
190
+ if (!best || updated >= bestAt) {
191
+ best = session;
192
+ bestAt = updated;
193
+ }
194
+ }
195
+ return best;
196
+ }
197
+
198
+ /**
199
+ * @param {import('./workspace-context').WorkspaceContext} ctx
200
+ * @returns {Object}
201
+ */
202
+ function requireChannelInstallSession(ctx) {
203
+ const session = readChannelInstallSession(ctx);
204
+ if (!session) {
205
+ throw httpError('Channel install session not found. POST install first.', 404);
206
+ }
207
+ return session;
208
+ }
209
+
210
+ module.exports = {
211
+ SESSION_FILE,
212
+ sessionPath,
213
+ readChannelInstallSession,
214
+ readChannelInstallSessionForDefinition,
215
+ writeChannelInstallSession,
216
+ requireChannelInstallSession,
217
+ httpError
218
+ };
@@ -18,6 +18,8 @@ Complete validate and upload for the target systemKey so the dataplane exposes M
18
18
  | slack | Slack workspace assistant |
19
19
  | microsoft-copilot | Microsoft Copilot package (runtime-orchestration MCP) |
20
20
  | chatgpt | ChatGPT custom MCP app (runtime-orchestration) |
21
+ | claude | Claude custom connector (runtime-orchestration) |
22
+ | mcp-client | Developer MCP client (Cursor, VS Code, Copilot IDE profile) |
21
23
  | web-embed | Experimental embedded web assistant |
22
24
 
23
25
  Each target requires credentials and deploy approval. Read topic connectedSystemUi after upload for dataplane handoff paths.
@@ -10,7 +10,9 @@ Every operation contains an ordered steps array. Each step is a single keyed obj
10
10
 
11
11
  ## Common step patterns
12
12
 
13
- Fetch resolves data from OpenAPI references, raw HTTP, or another datasource. Map applies fieldMappings to normalize vendor payloads. Filter applies optional ABAC or JMESPath expressions. Output emits records for sync and storage.
13
+ Fetch resolves data from OpenAPI references, raw HTTP, another datasource, a viewpoint SQL query, or local record storage. Map applies fieldMappings to normalize vendor payloads. Filter applies optional ABAC or JMESPath expressions. Output emits records for sync and storage.
14
+
15
+ `fetch.source` is a closed enum: `openapi`, `http`, `datasource`, `viewpoint`, `record`. The fetch token `record` is singular; `output.mode` stays plural `records`. Persist vs read on `record` follows operation `shape` (`create` or `update`), not the capability key. CIP `fetch.source` compiles to COM `source`.
14
16
 
15
17
  Paginate handles cursor, page, or offset strategies for list operations. Python inline runs sandboxed transforms when declarative steps are insufficient.
16
18