@aifabrix/builder 2.52.0 → 2.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/lib/api/datasources-extended.api.js +8 -8
  2. package/lib/api/keycloak.api.js +92 -0
  3. package/lib/api/token-providers.api.js +77 -0
  4. package/lib/api/types/keycloak.types.js +23 -0
  5. package/lib/api/types/token-providers.types.js +65 -0
  6. package/lib/app/deploy-config.js +3 -2
  7. package/lib/app/down.js +16 -1
  8. package/lib/channels/add-channel.js +134 -57
  9. package/lib/channels/approval-guides/chatgpt.js +1 -1
  10. package/lib/channels/approval-guides/index.js +3 -0
  11. package/lib/channels/approval-guides/microsoft-copilot.js +27 -14
  12. package/lib/channels/channel-add-ensure.js +128 -0
  13. package/lib/channels/channel-add-existing.js +72 -0
  14. package/lib/channels/channel-artifact-generator.js +11 -1
  15. package/lib/channels/channel-branding.js +94 -0
  16. package/lib/channels/channel-credential.js +8 -0
  17. package/lib/channels/channel-discovery.js +330 -0
  18. package/lib/channels/channel-external-system.js +1 -1
  19. package/lib/channels/channel-runtime-resolver.js +107 -14
  20. package/lib/channels/channel-status-display.js +36 -3
  21. package/lib/channels/microsoft-copilot-channel-add.js +138 -0
  22. package/lib/channels/microsoft-copilot-entra-sso-form.js +251 -0
  23. package/lib/channels/microsoft-copilot-entra-sso.js +285 -0
  24. package/lib/channels/microsoft-copilot-icons.js +74 -0
  25. package/lib/channels/microsoft-copilot-limits.js +103 -0
  26. package/lib/channels/microsoft-copilot-package.js +178 -52
  27. package/lib/channels/microsoft-copilot-validate.js +227 -0
  28. package/lib/cli/attach-default-help-examples.js +102 -0
  29. package/lib/cli/index.js +4 -0
  30. package/lib/cli/infra-guided.js +13 -6
  31. package/lib/cli/setup-credential-deployment.js +7 -5
  32. package/lib/cli/setup-infra.js +31 -4
  33. package/lib/cli/setup-utility-validate.js +1 -0
  34. package/lib/commands/channel-add-setup.js +28 -9
  35. package/lib/commands/channel.js +14 -10
  36. package/lib/commands/credential-push.js +37 -5
  37. package/lib/commands/integration-definition.js +158 -0
  38. package/lib/commands/package-score.js +72 -0
  39. package/lib/commands/repair-auth-apply.js +19 -5
  40. package/lib/commands/repair-datasource-sync-operations.js +4 -1
  41. package/lib/commands/role-assistant-test-output.js +200 -0
  42. package/lib/commands/role-assistant.js +163 -166
  43. package/lib/datasource/capability/capability-resolve.js +15 -5
  44. package/lib/datasource/capability/copy-operations.js +14 -4
  45. package/lib/deployment/deployer-poll-run.js +2 -2
  46. package/lib/deployment/deployer.js +1 -1
  47. package/lib/deployment/environment.js +2 -2
  48. package/lib/external-system/deploy.js +11 -21
  49. package/lib/infrastructure/index.js +19 -4
  50. package/lib/integration-definition/apply.js +476 -0
  51. package/lib/integration-definition/canonical-json.js +51 -0
  52. package/lib/integration-definition/emit-suite-bid.js +94 -0
  53. package/lib/integration-definition/enrich-crm-field-mappings.js +240 -0
  54. package/lib/integration-definition/enrich-openapi-operations.js +433 -0
  55. package/lib/integration-definition/from-discovery.js +339 -0
  56. package/lib/integration-definition/hash.js +37 -0
  57. package/lib/integration-definition/index.js +37 -0
  58. package/lib/integration-definition/resource-type-catalog.js +59 -0
  59. package/lib/integration-definition/system-kind-enum.js +40 -0
  60. package/lib/integration-definition/validate.js +148 -0
  61. package/lib/lab/package-parity-score.js +417 -0
  62. package/lib/programmatic/builder-api-request.js +5 -1
  63. package/lib/programmatic/builder-help-governance.js +3 -2
  64. package/lib/programmatic/capability-registry.js +20 -0
  65. package/lib/programmatic/help-content/channel-onboarding.md +35 -0
  66. package/lib/programmatic/help-content/cip-overview.md +27 -0
  67. package/lib/programmatic/help-content/connected-system-ui.md +70 -0
  68. package/lib/programmatic/help-content/dimensions-guide.md +23 -0
  69. package/lib/programmatic/help-content/enterprise-sync-fabrix.md +123 -0
  70. package/lib/programmatic/help-content/overview.md +25 -0
  71. package/lib/programmatic/help-content/subscription-guide.md +27 -0
  72. package/lib/programmatic/help-content/workflow.md +50 -0
  73. package/lib/programmatic/openapi-component-schemas.js +7 -1
  74. package/lib/programmatic/openapi-descriptions.js +46 -0
  75. package/lib/programmatic/openapi-envelope-examples.js +280 -0
  76. package/lib/programmatic/openapi-operation-bindings.js +50 -0
  77. package/lib/programmatic/openapi-request-schemas.js +40 -0
  78. package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
  79. package/lib/programmatic/openapi-result-schemas.js +76 -99
  80. package/lib/programmatic/openapi-schema-descriptions-core.js +25 -4
  81. package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
  82. package/lib/programmatic/openapi-schema-descriptions-operations.js +43 -0
  83. package/lib/programmatic/openapi-schema-helpers.js +9 -9
  84. package/lib/programmatic/openapi-spec.js +11 -1
  85. package/lib/programmatic/orchestration-openapi-meta.js +10 -0
  86. package/lib/programmatic/persist-supplied-openapi.js +180 -0
  87. package/lib/programmatic/route-handler-map.js +35 -1
  88. package/lib/programmatic/run-channel-package.js +236 -0
  89. package/lib/programmatic/run-integration-definition.js +77 -0
  90. package/lib/role-assistant/evidence-sync.js +55 -7
  91. package/lib/role-assistant/knowledge-sync-logging.js +58 -0
  92. package/lib/role-assistant/knowledge-sync.js +88 -93
  93. package/lib/role-assistant/test-cases-bid.js +155 -0
  94. package/lib/role-assistant/test-cases-evaluate.js +9 -1
  95. package/lib/role-assistant/test-cases-honesty.js +24 -10
  96. package/lib/role-assistant/test-cases.js +28 -0
  97. package/lib/role-assistant/test-runner-attachments.js +118 -0
  98. package/lib/role-assistant/test-runner-bid-apply.js +201 -0
  99. package/lib/role-assistant/test-runner-bid-document.js +278 -0
  100. package/lib/role-assistant/test-runner-start-payload.js +96 -0
  101. package/lib/role-assistant/test-runner-workhub-answers.js +249 -20
  102. package/lib/role-assistant/test-runner-workhub-helpers.js +100 -0
  103. package/lib/role-assistant/test-runner-workhub-soft.js +86 -0
  104. package/lib/role-assistant/test-runner-workhub.js +152 -107
  105. package/lib/role-assistant/test-runner.js +5 -63
  106. package/lib/role-assistant/test-suite.js +112 -0
  107. package/lib/schema/application-schema.json +56 -6
  108. package/lib/schema/external-datasource.schema.json +103 -17
  109. package/lib/schema/external-system.schema.json +54 -3
  110. package/lib/schema/integration-definition.schema.json +195 -0
  111. package/lib/utils/authentication-shape.js +6 -0
  112. package/lib/utils/compose-generator.js +10 -1
  113. package/lib/utils/compose-traefik-extra-routes.js +107 -0
  114. package/lib/utils/docker-build.js +2 -2
  115. package/lib/utils/help-builder.js +3 -1
  116. package/lib/utils/promote-dev-docker-image.js +102 -18
  117. package/lib/utils/transient-developer-id.js +28 -0
  118. package/package.json +14 -16
  119. package/templates/README.md +12 -1
  120. package/templates/applications/dataplane/application.yaml +10 -2
  121. package/templates/applications/dataplane/env.template +7 -6
  122. package/templates/applications/miso-controller/application.yaml +23 -8
  123. package/templates/applications/miso-controller/env.template +8 -2
  124. package/templates/applications/miso-controller/rbac.yaml +1 -0
  125. package/templates/channels/README.md +11 -1
  126. package/templates/channels/brand/color-192.png +0 -0
  127. package/templates/channels/brand/dark/128.png +0 -0
  128. package/templates/channels/brand/dark/192.png +0 -0
  129. package/templates/channels/brand/dark/256.png +0 -0
  130. package/templates/channels/brand/dark/32.png +0 -0
  131. package/templates/channels/brand/dark/512.png +0 -0
  132. package/templates/channels/brand/dark/64.png +0 -0
  133. package/templates/channels/brand/light/128.png +0 -0
  134. package/templates/channels/brand/light/192.png +0 -0
  135. package/templates/channels/brand/light/256.png +0 -0
  136. package/templates/channels/brand/light/32.png +0 -0
  137. package/templates/channels/brand/light/512.png +0 -0
  138. package/templates/channels/brand/light/64.png +0 -0
  139. package/templates/channels/brand/mono-white/128.png +0 -0
  140. package/templates/channels/brand/mono-white/192.png +0 -0
  141. package/templates/channels/brand/mono-white/256.png +0 -0
  142. package/templates/channels/brand/mono-white/32.png +0 -0
  143. package/templates/channels/brand/mono-white/512.png +0 -0
  144. package/templates/channels/brand/mono-white/64.png +0 -0
  145. package/templates/channels/brand/outline-32.png +0 -0
  146. package/templates/channels/chatgpt/install.template.md.hbs +6 -4
  147. package/templates/channels/chatgpt/integration/README.md +4 -5
  148. package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
  149. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +14 -16
  150. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
  151. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +30 -0
  152. package/templates/channels/microsoft-copilot/fi-fi.template.json.hbs +7 -0
  153. package/templates/channels/microsoft-copilot/install.template.md.hbs +93 -22
  154. package/templates/channels/microsoft-copilot/integration/README.md +2 -3
  155. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +1 -22
  156. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -19
  157. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +15 -0
  158. package/templates/channels/slack/integration/README.md +2 -4
  159. package/templates/channels/teams-bot/install.template.md.hbs +4 -0
  160. package/templates/channels/teams-bot/integration/README.md +3 -5
  161. package/templates/channels/web-embed/integration/web-embed-system.json +1 -16
  162. package/templates/python/docker-compose.hbs +36 -0
  163. package/templates/typescript/docker-compose.hbs +36 -0
  164. package/.cursor/plans/Done/157.1-security-quality-violations-by-type.csv +0 -374
  165. package/.cursor/process/README.md +0 -9
  166. package/.cursor/rules/anchor-docs.mdc +0 -15
  167. package/.cursor/rules/assistant-driven-integration-build.mdc +0 -40
  168. package/.cursor/rules/cli-layout.mdc +0 -81
  169. package/.cursor/rules/cli-product-neutral.mdc +0 -43
  170. package/.cursor/rules/deploy-rules/docker-image-ip-policy.mdc +0 -49
  171. package/.cursor/rules/docs-rules.mdc +0 -32
  172. package/.cursor/rules/plan-rules/application-lifecycle.mdc +0 -30
  173. package/.cursor/rules/plan-rules/plan-frontmatter.mdc +0 -35
  174. package/.cursor/rules/plan-rules/plan-lifecycle.mdc +0 -31
  175. package/.cursor/rules/plan-rules/plan-validation-gate.mdc +0 -34
  176. package/.cursor/rules/project-rules.mdc +0 -913
  177. package/.cursor/skills/README.md +0 -15
  178. package/.dockerignore +0 -52
  179. package/.eslintignore +0 -22
  180. package/.markdownlint.json +0 -14
  181. package/anchor-docs/README.md +0 -10
  182. package/anchor-docs/_TEMPLATE +0 -24
  183. package/babel.config.js +0 -6
  184. package/bin/builder-api-cleanup.js +0 -26
  185. package/bin/builder-api-nest.js +0 -10
  186. package/bin/builder-api-spike.js +0 -19
  187. package/enterprise-knowledge/README.md +0 -50
  188. package/enterprise-knowledge/enterprise-integration-lifecycle/README.md +0 -22
  189. package/enterprise-knowledge/enterprise-integration-lifecycle/application.json +0 -15
  190. package/enterprise-knowledge/enterprise-integration-lifecycle/deploy.js +0 -69
  191. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-deploy.json +0 -52
  192. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-system.json +0 -29
  193. package/enterprise-knowledge/enterprise-integration-lifecycle/env.template +0 -2
  194. package/integration/README.md +0 -81
  195. package/integration/github/README.md +0 -53
  196. package/integration/github/application.json +0 -21
  197. package/integration/github/deploy.js +0 -72
  198. package/integration/github/env.template +0 -11
  199. package/integration/github/github-datasource-issue-comments.json +0 -239
  200. package/integration/github/github-datasource-issues.json +0 -399
  201. package/integration/github/github-datasource-repository.json +0 -354
  202. package/integration/github/github-deploy.json +0 -1202
  203. package/integration/github/github-system.json +0 -82
  204. package/integration/hubspot-test/README.md +0 -159
  205. package/integration/hubspot-test/application.json +0 -54
  206. package/integration/hubspot-test/companies.json +0 -2048
  207. package/integration/hubspot-test/create-hubspot.js +0 -523
  208. package/integration/hubspot-test/env.template +0 -4
  209. package/integration/hubspot-test/hubspot-test-datasource-company.json +0 -138
  210. package/integration/hubspot-test/hubspot-test-datasource-contact.json +0 -146
  211. package/integration/hubspot-test/hubspot-test-datasource-deal.json +0 -146
  212. package/integration/hubspot-test/hubspot-test-datasource-users.json +0 -76
  213. package/integration/hubspot-test/hubspot-test-deploy.json +0 -2160
  214. package/integration/hubspot-test/hubspot-test-system.json +0 -74
  215. package/integration/hubspot-test/rbac.json +0 -166
  216. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +0 -20
  217. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +0 -9
  218. package/integration/hubspot-test/test-artifacts/wizard-invalid-add-datasource.yaml +0 -5
  219. package/integration/hubspot-test/test-artifacts/wizard-invalid-app-name.yaml +0 -5
  220. package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-create.yaml +0 -7
  221. package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-select.yaml +0 -7
  222. package/integration/hubspot-test/test-artifacts/wizard-invalid-known-platform.yaml +0 -4
  223. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-app.yaml +0 -4
  224. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
  225. package/integration/hubspot-test/test-artifacts/wizard-invalid-mode.yaml +0 -5
  226. package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-file.yaml +0 -5
  227. package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-url.yaml +0 -4
  228. package/integration/hubspot-test/test-artifacts/wizard-invalid-source.yaml +0 -4
  229. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-array-test.yaml +0 -5
  230. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
  231. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
  232. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-test.yaml +0 -5
  233. package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-test.yaml +0 -5
  234. package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +0 -5
  235. package/integration/hubspot-test/test-dataplane-down-helpers.js +0 -249
  236. package/integration/hubspot-test/test-dataplane-down-tests.js +0 -386
  237. package/integration/hubspot-test/test-dataplane-down.js +0 -209
  238. package/integration/hubspot-test/test.js +0 -1588
  239. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +0 -16
  240. package/integration/hubspot-test/wizard-hubspot-platform.yaml +0 -8
  241. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +0 -23
  242. package/integration/roundtrip-test-local/README.md +0 -143
  243. package/integration/roundtrip-test-local/application.yaml +0 -13
  244. package/integration/roundtrip-test-local/env.template +0 -15
  245. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +0 -14
  246. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +0 -61
  247. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +0 -25
  248. package/integration/roundtrip-test-local2/README.md +0 -143
  249. package/integration/roundtrip-test-local2/application.yaml +0 -13
  250. package/integration/roundtrip-test-local2/env.template +0 -15
  251. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +0 -31
  252. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +0 -86
  253. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +0 -25
  254. package/integration/test/wizard.yaml +0 -8
  255. package/jest.config.coverage.js +0 -37
  256. package/jest.config.default.js +0 -11
  257. package/jest.config.integration.fixtures.js +0 -22
  258. package/jest.config.integration.js +0 -33
  259. package/jest.config.isolated.js +0 -11
  260. package/jest.config.manual.js +0 -30
  261. package/jest.global-live-fabrix-hooks.js +0 -36
  262. package/jest.isolated-projects.js +0 -260
  263. package/jest.projects.js +0 -76
  264. package/packages/builder-api/README.md +0 -120
  265. package/packages/builder-api/package.json +0 -30
  266. package/packages/builder-api/src/app.module.ts +0 -7
  267. package/packages/builder-api/src/auth/auth.module.ts +0 -5
  268. package/packages/builder-api/src/external-system/external-system.module.ts +0 -5
  269. package/packages/builder-api/src/main.ts +0 -16
  270. package/packages/builder-api/tsconfig.json +0 -15
  271. package/scripts/check-builder-api-architecture-grep.js +0 -79
  272. package/scripts/check-datasource-test-run-schema-sync.js +0 -34
  273. package/scripts/ci-fix.sh +0 -19
  274. package/scripts/ci-simulate.sh +0 -19
  275. package/scripts/diagnose-cli.js +0 -150
  276. package/scripts/docker_image_content_gate.js +0 -47
  277. package/scripts/docker_image_content_gate.sh +0 -5
  278. package/scripts/export-builder-api-evidence.js +0 -262
  279. package/scripts/install-local.js +0 -490
  280. package/scripts/lib/builder-api-docker-staging-copy.js +0 -60
  281. package/scripts/lib/builder-api-docker-staging-core.js +0 -183
  282. package/scripts/lib/docker-image-content-gate-core.js +0 -166
  283. package/scripts/lib/plan-actor.js +0 -109
  284. package/scripts/lib/plan-config.js +0 -20
  285. package/scripts/lib/plan-gates.js +0 -154
  286. package/scripts/lib/plan-meta.js +0 -104
  287. package/scripts/lib/plan-parse.js +0 -162
  288. package/scripts/lib/plan-prevalidate-gates.js +0 -337
  289. package/scripts/lib/plan-run-core.js +0 -125
  290. package/scripts/lib/plan-run-report.js +0 -119
  291. package/scripts/plan-approve.js +0 -24
  292. package/scripts/plan-archive.js +0 -49
  293. package/scripts/plan-run.js +0 -13
  294. package/scripts/plan-set-actor.js +0 -21
  295. package/scripts/plan-validate.js +0 -80
  296. package/scripts/pnpm/af.mjs +0 -110
  297. package/scripts/pnpm/check-quiet.mjs +0 -32
  298. package/scripts/pnpm/dev-install.mjs +0 -29
  299. package/scripts/pnpm/dev-reload.mjs +0 -18
  300. package/scripts/pnpm/help.mjs +0 -55
  301. package/scripts/pnpm/lib/dev-reload-core.mjs +0 -189
  302. package/scripts/pnpm/lib/dev-reload-log-capture.mjs +0 -67
  303. package/scripts/pnpm/lib/dev-reload-log-utils.mjs +0 -32
  304. package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +0 -124
  305. package/scripts/pnpm/lib/resolve-app-url.mjs +0 -183
  306. package/scripts/pnpm/lib/spinner.mjs +0 -42
  307. package/scripts/pnpm-global-remove.js +0 -48
  308. package/scripts/stage_builder_api_for_docker.js +0 -65
  309. package/scripts/sync-builder-api-template.js +0 -44
  310. package/scripts/test-dataplane-bootstrap.js +0 -331
@@ -69,28 +69,28 @@ async function detachPolicy(dataplaneUrl, sourceIdOrKey, policyIdOrKey, authConf
69
69
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
70
70
  return await client.delete(`/api/v1/external/${sourceIdOrKey}/policies/${policyIdOrKey}`);
71
71
  }
72
- /** @requiresPermission {Dataplane} external-system:read (sync) */
72
+ /** @requiresPermission {Dataplane} external-data-source:read (sync under /data-storage) */
73
73
  async function listSyncJobs(dataplaneUrl, sourceIdOrKey, authConfig, options = {}) {
74
74
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
75
- return await client.get(`/api/v1/external/${sourceIdOrKey}/sync`, { params: options });
75
+ return await client.get(`/api/v1/data-storage/${sourceIdOrKey}/sync`, { params: options });
76
76
  }
77
77
  async function createSyncJob(dataplaneUrl, sourceIdOrKey, authConfig, syncData) {
78
78
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
79
- return await client.post(`/api/v1/external/${sourceIdOrKey}/sync`, { body: syncData });
79
+ return await client.post(`/api/v1/data-storage/${sourceIdOrKey}/sync`, { body: syncData });
80
80
  }
81
- /** @requiresPermission {Dataplane} external-system:read */
81
+ /** @requiresPermission {Dataplane} external-data-source:read */
82
82
  async function getSyncJob(dataplaneUrl, sourceIdOrKey, syncJobId, authConfig) {
83
83
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
84
- return await client.get(`/api/v1/external/${sourceIdOrKey}/sync/${syncJobId}`);
84
+ return await client.get(`/api/v1/data-storage/${sourceIdOrKey}/sync/${syncJobId}`);
85
85
  }
86
- /** @requiresPermission {Dataplane} external-system:update */
86
+ /** @requiresPermission {Dataplane} external-data-source:sync */
87
87
  async function updateSyncJob(dataplaneUrl, sourceIdOrKey, syncJobId, authConfig, updateData) {
88
88
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
89
- return await client.put(`/api/v1/external/${sourceIdOrKey}/sync/${syncJobId}`, { body: updateData });
89
+ return await client.put(`/api/v1/data-storage/${sourceIdOrKey}/sync/${syncJobId}`, { body: updateData });
90
90
  }
91
91
  async function executeSyncJob(dataplaneUrl, sourceIdOrKey, syncJobId, authConfig) {
92
92
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
93
- return await client.post(`/api/v1/external/${sourceIdOrKey}/sync/${syncJobId}/execute`);
93
+ return await client.post(`/api/v1/data-storage/${sourceIdOrKey}/sync/${syncJobId}/execute`);
94
94
  }
95
95
  /** @requiresPermission {Dataplane} external-data-source:sync */
96
96
  async function validateDocuments(dataplaneUrl, sourceIdOrKey, authConfig, validateData) {
@@ -0,0 +1,92 @@
1
+ /**
2
+ * @fileoverview Keycloak status API (Controller) — Entra IdP ids for packaging
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { ApiClient } = require('./index');
10
+
11
+ const KEYCLOAK_STATUS_PATH = '/api/v1/keycloak/status';
12
+
13
+ /**
14
+ * @param {Object} response
15
+ * @returns {import('./types/keycloak.types').KeycloakStatus|null}
16
+ */
17
+ function extractKeycloakStatus(response) {
18
+ if (!response || typeof response !== 'object' || response.success === false) {
19
+ return null;
20
+ }
21
+ const body = response.data ?? response;
22
+ const data = body?.data !== undefined ? body.data : body;
23
+ if (!data || typeof data !== 'object' || Array.isArray(data)) {
24
+ return null;
25
+ }
26
+ return data;
27
+ }
28
+
29
+ /**
30
+ * @param {unknown} value
31
+ * @returns {string|null}
32
+ */
33
+ function trimNonEmptyString(value) {
34
+ if (typeof value !== 'string') {
35
+ return null;
36
+ }
37
+ const trimmed = value.trim();
38
+ return trimmed || null;
39
+ }
40
+
41
+ /**
42
+ * @param {import('./types/keycloak.types').KeycloakStatus|null} status
43
+ * @returns {import('./types/keycloak.types').KeycloakEntraIdentityProvider|null}
44
+ */
45
+ function extractEntraIdentityProvider(status) {
46
+ const raw = status?.entraIdentityProvider;
47
+ if (!raw || typeof raw !== 'object') {
48
+ return null;
49
+ }
50
+ const enterpriseAppId = trimNonEmptyString(raw.enterpriseAppId);
51
+ if (!enterpriseAppId) {
52
+ return null;
53
+ }
54
+ const tenantId = trimNonEmptyString(raw.tenantId);
55
+ return tenantId ? { enterpriseAppId, tenantId } : { enterpriseAppId };
56
+ }
57
+
58
+ /**
59
+ * GET /api/v1/keycloak/status
60
+ * @requiresPermission {Controller} admin:keycloak
61
+ * @async
62
+ * @param {string} controllerUrl - Miso Controller base URL
63
+ * @param {Object} authConfig - Authentication configuration (device Bearer)
64
+ * @returns {Promise<import('./types/keycloak.types').KeycloakStatus|null>}
65
+ */
66
+ async function getKeycloakStatus(controllerUrl, authConfig) {
67
+ const client = new ApiClient(controllerUrl, authConfig);
68
+ const response = await client.get(KEYCLOAK_STATUS_PATH);
69
+ return extractKeycloakStatus(response);
70
+ }
71
+
72
+ /**
73
+ * Resolve Entra SSO enterprise app id from Keycloak status, or null when missing.
74
+ * Does not invent ids.
75
+ * @requiresPermission {Controller} admin:keycloak
76
+ * @async
77
+ * @param {string} controllerUrl - Miso Controller base URL
78
+ * @param {Object} authConfig - Authentication configuration
79
+ * @returns {Promise<import('./types/keycloak.types').KeycloakEntraIdentityProvider|null>}
80
+ */
81
+ async function getKeycloakEntraIdentityProvider(controllerUrl, authConfig) {
82
+ const status = await getKeycloakStatus(controllerUrl, authConfig);
83
+ return extractEntraIdentityProvider(status);
84
+ }
85
+
86
+ module.exports = {
87
+ KEYCLOAK_STATUS_PATH,
88
+ getKeycloakStatus,
89
+ getKeycloakEntraIdentityProvider,
90
+ extractKeycloakStatus,
91
+ extractEntraIdentityProvider
92
+ };
@@ -0,0 +1,77 @@
1
+ /**
2
+ * @fileoverview Admin token providers API (Controller Trust sources)
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { ApiClient } = require('./index');
10
+
11
+ const ADMIN_BASE = '/api/v1/admin/token-providers';
12
+ const MICROSOFT_ENTRA_ID_KEY = 'microsoft-entra-id';
13
+
14
+ /**
15
+ * @param {Object} response
16
+ * @returns {import('./types/token-providers.types').TokenProvider[]}
17
+ */
18
+ function extractTokenProviders(response) {
19
+ const body = response?.data ?? response;
20
+ const data = body?.data ?? body;
21
+ if (Array.isArray(data)) {
22
+ return data;
23
+ }
24
+ if (Array.isArray(data?.items)) {
25
+ return data.items;
26
+ }
27
+ return [];
28
+ }
29
+
30
+ /**
31
+ * List admin token providers (paginated).
32
+ * GET /api/v1/admin/token-providers
33
+ * @requiresPermission {Controller} token-provider:read
34
+ * @async
35
+ * @param {string} controllerUrl - Miso Controller base URL
36
+ * @param {Object} authConfig - Authentication configuration (device Bearer)
37
+ * @param {import('./types/token-providers.types').ListAdminTokenProvidersOptions} options
38
+ * @returns {Promise<Object>} Controller list response
39
+ * @throws {Error} If direction is missing or request fails
40
+ */
41
+ async function listAdminTokenProviders(controllerUrl, authConfig, options = {}) {
42
+ const direction = options?.direction;
43
+ if (direction !== 'inbound' && direction !== 'outbound') {
44
+ throw new Error('direction is required and must be "inbound" or "outbound"');
45
+ }
46
+ const client = new ApiClient(controllerUrl, authConfig);
47
+ const params = { ...options, direction };
48
+ return await client.get(ADMIN_BASE, { params });
49
+ }
50
+
51
+ /**
52
+ * Resolve inbound Microsoft Entra ID Trust source, or null when missing.
53
+ * Does not invent issuer, audience, or scopes.
54
+ * @requiresPermission {Controller} token-provider:read
55
+ * @async
56
+ * @param {string} controllerUrl - Miso Controller base URL
57
+ * @param {Object} authConfig - Authentication configuration
58
+ * @returns {Promise<import('./types/token-providers.types').TokenProvider|null>}
59
+ */
60
+ async function getInboundMicrosoftEntraIdProvider(controllerUrl, authConfig) {
61
+ const response = await listAdminTokenProviders(controllerUrl, authConfig, {
62
+ direction: 'inbound',
63
+ search: MICROSOFT_ENTRA_ID_KEY,
64
+ page: 1,
65
+ pageSize: 50
66
+ });
67
+ const items = extractTokenProviders(response);
68
+ const exact = items.find((item) => item && item.key === MICROSOFT_ENTRA_ID_KEY);
69
+ return exact || null;
70
+ }
71
+
72
+ module.exports = {
73
+ listAdminTokenProviders,
74
+ getInboundMicrosoftEntraIdProvider,
75
+ extractTokenProviders,
76
+ MICROSOFT_ENTRA_ID_KEY
77
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @fileoverview Keycloak status API type definitions (Controller)
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ /**
8
+ * Entra identity provider fragment from Keycloak status.
9
+ * @typedef {Object} KeycloakEntraIdentityProvider
10
+ * @property {string} enterpriseAppId - Entra SSO application (client) id
11
+ * @property {string} [tenantId] - Entra tenant id when known
12
+ */
13
+
14
+ /**
15
+ * Keycloak configuration status payload (`data` of GET /api/v1/keycloak/status).
16
+ * @typedef {Object} KeycloakStatus
17
+ * @property {string} [realm]
18
+ * @property {string} [status]
19
+ * @property {string} [controllerClientId]
20
+ * @property {KeycloakEntraIdentityProvider} [entraIdentityProvider]
21
+ */
22
+
23
+ module.exports = {};
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @fileoverview Token provider API type definitions (Controller admin Trust sources)
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ /**
8
+ * Token provider direction
9
+ * @typedef {'inbound'|'outbound'} TokenProviderDirection
10
+ */
11
+
12
+ /**
13
+ * Token provider subject type
14
+ * @typedef {'user'|'service'|'both'} TokenProviderSubjectType
15
+ */
16
+
17
+ /**
18
+ * Allowed operation on a token provider
19
+ * @typedef {'mcp.invoke'|'dataplane.read'|'dataplane.write'} TokenProviderAllowedOperation
20
+ */
21
+
22
+ /**
23
+ * Admin token provider list item (Controller TokenProvider schema)
24
+ * @typedef {Object} TokenProvider
25
+ * @property {string} id - Unique identifier (cuid)
26
+ * @property {string} key - Unique slug (e.g. microsoft-entra-id)
27
+ * @property {string} displayName - Human-readable name
28
+ * @property {string} issuer - JWT issuer URL (iss claim)
29
+ * @property {string} jwksUri - JWKS endpoint for key verification
30
+ * @property {string|null} [audience] - Expected audience claim (may be null)
31
+ * @property {TokenProviderSubjectType} subjectType - Subject type
32
+ * @property {TokenProviderDirection} direction - inbound or outbound
33
+ * @property {string|null} [identityClaim] - Claim used for user identity
34
+ * @property {string|null} [emailClaim] - Claim used for email
35
+ * @property {boolean} [autoProvision] - Whether to auto-create users
36
+ * @property {string|null} [serviceUserKey] - Service user key for service flow
37
+ * @property {TokenProviderAllowedOperation[]} [allowedOperations] - Allowed operations
38
+ * @property {string[]} [allowedScopes] - OAuth scopes (may be empty)
39
+ * @property {boolean} enabled - Whether this provider is enabled
40
+ * @property {string} [createdAt] - Created at
41
+ * @property {string} [updatedAt] - Updated at
42
+ */
43
+
44
+ /**
45
+ * List admin token providers query options
46
+ * @typedef {Object} ListAdminTokenProvidersOptions
47
+ * @property {TokenProviderDirection} direction - Required by OpenAPI
48
+ * @property {string} [search] - Search across key, displayName, issuer
49
+ * @property {number} [page] - Page number
50
+ * @property {number} [pageSize] - Page size
51
+ * @property {string} [sort] - Sort expression
52
+ * @property {string} [filter] - Filter expression
53
+ * @property {string} [enabled] - 'true' | 'false'
54
+ * @property {TokenProviderSubjectType} [subjectType] - Subject type filter
55
+ */
56
+
57
+ /**
58
+ * Paginated list response for admin token providers
59
+ * @typedef {Object} ListAdminTokenProvidersResponse
60
+ * @property {TokenProvider[]} data - Provider rows
61
+ * @property {Object} meta - Pagination meta
62
+ * @property {Object} [links] - Pagination links
63
+ */
64
+
65
+ module.exports = {};
@@ -78,9 +78,10 @@ async function extractDeploymentConfig(options, _variables) {
78
78
  // Used by guided installers which already show a top-level spinner.
79
79
  silentPoll: options.silentPoll === true,
80
80
  repositoryUrl: options.repositoryUrl,
81
- // Omit default here so deployer can resolve 2s vs 5s from controller deploymentType (GET /api/v1/health)
81
+ // Omit default here so deployer can resolve 1s vs 5s from controller deploymentType (GET /api/v1/health)
82
82
  pollInterval: Number.isFinite(pollIntervalExplicit) ? pollIntervalExplicit : undefined,
83
- pollMaxAttempts: options.pollMaxAttempts || 60,
83
+ // Default 300 attempts: ~5 min at 1s (local) / ~25 min at 5s (azure) — deploy may run tests/gates
84
+ pollMaxAttempts: options.pollMaxAttempts || 300,
84
85
  deploymentAuth: options.deploymentAuth
85
86
  };
86
87
  }
package/lib/app/down.js CHANGED
@@ -111,6 +111,20 @@ async function stopAppContainers(appName, companionKeys, developerId) {
111
111
  }
112
112
  }
113
113
 
114
+ /**
115
+ * Resolve developer id for downApp (optional override does not change config).
116
+ * @param {Object} options
117
+ * @returns {Promise<string>}
118
+ */
119
+ async function resolveDownAppDeveloperId(options) {
120
+ const override = options && options.developerId;
121
+ if (override === undefined || override === null || override === '') {
122
+ return config.getDeveloperId();
123
+ }
124
+ const { validateAndNormalizeDeveloperId } = require('../core/config-normalize');
125
+ return validateAndNormalizeDeveloperId(override);
126
+ }
127
+
114
128
  /**
115
129
  * Stops and removes a specific application's container.
116
130
  * If --volumes is passed, attempts to remove the app's named Docker volume.
@@ -122,6 +136,7 @@ async function stopAppContainers(appName, companionKeys, developerId) {
122
136
  * @param {string} appName - Application name
123
137
  * @param {Object} options - Options
124
138
  * @param {boolean} [options.volumes=false] - Remove Docker volume data
139
+ * @param {string|number} [options.developerId] - Target developer for this stop only
125
140
  * @returns {Promise<void>} Resolves when operation completes
126
141
  * @throws {Error} If validation fails or Docker operations error (other than missing volume)
127
142
  *
@@ -136,7 +151,7 @@ async function downApp(appName, options = {}) {
136
151
  validateAppName(appName);
137
152
 
138
153
  const volumes = !!options.volumes;
139
- const developerId = await config.getDeveloperId();
154
+ const developerId = await resolveDownAppDeveloperId(options);
140
155
  const appConfig = loadBuilderApplicationConfig(appName);
141
156
  const companionKeys = getCeleryCompanionAppKeys(appName, appConfig);
142
157
 
@@ -8,11 +8,17 @@
8
8
 
9
9
  'use strict';
10
10
 
11
+ const path = require('path');
11
12
  const { getChannelTarget, getDefaultChannelDescription } = require('./channel-targets');
12
13
  const { resolveAppDescription } = require('./channel-description');
13
14
  const { resolveChannelRuntimeValues } = require('./channel-runtime-resolver');
14
15
  const { generateChannelArtifact } = require('./channel-artifact-generator');
15
16
  const { fetchRuntimeLayerBToolNames } = require('./runtime-tool-catalog');
17
+ const { loadChannelPackageContext } = require('./channel-discovery');
18
+ const {
19
+ readChannelBranding,
20
+ mergeChannelBranding
21
+ } = require('./channel-branding');
16
22
  const {
17
23
  getExistingChannelExtension,
18
24
  buildChannelExtensionBlock,
@@ -37,11 +43,14 @@ const {
37
43
  normalizeBaseChannelName,
38
44
  resolveChannelDisplayName
39
45
  } = require('./channel-display-name');
46
+ const { displayChannelInstallResult } = require('./channel-status-display');
47
+ const { ensureChannelReadyForAdd } = require('./channel-add-ensure');
48
+ const { showExistingChannelStatus } = require('./channel-add-existing');
40
49
  const {
41
- displayExistingChannelStatus,
42
- displayChannelInstallResult,
43
- displayChannelError
44
- } = require('./channel-status-display');
50
+ assertMicrosoftCopilotOauthReference,
51
+ assertMicrosoftCopilotOauthReferenceBeforeForce,
52
+ loadMicrosoftCopilotEntraContext
53
+ } = require('./microsoft-copilot-channel-add');
45
54
 
46
55
  /**
47
56
  * @param {typeof import('./channel-targets').CHANNEL_TARGETS[string]} target
@@ -73,41 +82,6 @@ function toValidationDisplay(validation) {
73
82
  };
74
83
  }
75
84
 
76
- /**
77
- * @param {Object} params
78
- * @returns {Object|null}
79
- */
80
- function showExistingChannelStatus(params) {
81
- const { existing, options, channelTarget, name, baseName, ctx } = params;
82
- if (!existing || options.force || options.dryRun) return null;
83
- const sameInstall =
84
- (existing.baseName && existing.baseName === baseName) ||
85
- (!existing.baseName && existing.name === name);
86
- if (!sameInstall) {
87
- throw new Error(
88
- `Channel already configured as "${existing.name}". ` +
89
- `Use --force to replace with "${name}".`
90
- );
91
- }
92
- const projectRoot = options.projectRoot || process.cwd();
93
- const approvalGuide =
94
- existing.status === 'approvalRequired'
95
- ? buildChannelApprovalGuide(channelTarget, {
96
- projectRoot,
97
- name: existing.name || name,
98
- baseName,
99
- dataplaneBaseUrl: ctx?.dataplaneUrl
100
- })
101
- : null;
102
- displayExistingChannelStatus({
103
- channelTarget,
104
- name: existing.name || name,
105
- channelExt: existing,
106
- approvalGuide
107
- });
108
- return { skipped: true, channelExt: existing };
109
- }
110
-
111
85
  /**
112
86
  * @param {string} channelTarget
113
87
  * @param {Object} artifactParams
@@ -234,7 +208,8 @@ async function completeLiveInstall(params) {
234
208
  name,
235
209
  baseName,
236
210
  artifactPath: artifact.outputPath,
237
- dataplaneBaseUrl: ctx.dataplaneUrl
211
+ dataplaneBaseUrl: ctx.dataplaneUrl,
212
+ environment: ctx.environment || options.environment || 'dev'
238
213
  })
239
214
  : null;
240
215
  if (approvalGuide?.portalUrl) {
@@ -252,6 +227,15 @@ async function completeLiveInstall(params) {
252
227
  capabilities: target.capabilities,
253
228
  artifactPath: artifact.outputPath,
254
229
  installGuidePath: artifact.installPath,
230
+ zipPath: artifact.zipPath,
231
+ installReady: artifact.installReady,
232
+ blocker: artifact.blocker,
233
+ packageReady: artifact.packageReady,
234
+ packageBlocker: artifact.packageBlocker,
235
+ microsoftPublicationPending:
236
+ artifact.microsoftPublicationPending !== undefined
237
+ ? artifact.microsoftPublicationPending
238
+ : status === 'approvalRequired',
255
239
  validation: toValidationDisplay(validation),
256
240
  approvalGuide
257
241
  });
@@ -272,7 +256,10 @@ function finishDryRunInstall(params) {
272
256
  credentialKey: params.target.credentialKey,
273
257
  capabilities: params.target.capabilities,
274
258
  artifactPath: params.artifact.outputPath,
275
- installGuidePath: params.artifact.installPath
259
+ installGuidePath: params.artifact.installPath,
260
+ zipPath: params.artifact.zipPath,
261
+ installReady: params.artifact.installReady,
262
+ blocker: params.artifact.blocker
276
263
  });
277
264
  return { dryRun: true, channelBlock: params.channelBlock, artifact: params.artifact };
278
265
  }
@@ -281,12 +268,78 @@ function finishDryRunInstall(params) {
281
268
  * @param {Object} params
282
269
  * @returns {Promise<{ artifact: Object, channelBlock: Object, status: string }>}
283
270
  */
271
+ /**
272
+ * @param {Object|null} packageContext
273
+ * @param {Object} options
274
+ * @param {'iconColorPath'|'iconOutlinePath'} key
275
+ * @returns {string|undefined}
276
+ */
277
+ function brandingIconOrOption(packageContext, options, key) {
278
+ if (typeof options[key] === 'string' && options[key].trim()) {
279
+ return options[key].trim();
280
+ }
281
+ if (packageContext && typeof packageContext[key] === 'string' && packageContext[key].trim()) {
282
+ return packageContext[key].trim();
283
+ }
284
+ return undefined;
285
+ }
286
+
287
+ async function resolvePackageContextForInstall(params) {
288
+ const { channelTarget, options, name, description, ctx } = params;
289
+ const packagingTargets = new Set(['chatgpt', 'microsoft-copilot', 'slack', 'teams-bot']);
290
+ if (!packagingTargets.has(channelTarget)) {
291
+ return null;
292
+ }
293
+ const projectRoot = options.projectRoot || process.cwd();
294
+ const workspaceDir = path.join(projectRoot, 'integration', channelTarget);
295
+ const branding = mergeChannelBranding(readChannelBranding(workspaceDir), {
296
+ displayName: options.overrideDisplayName || undefined,
297
+ description: options.overrideDescription || undefined,
298
+ iconColorPath: options.iconColorPath || undefined,
299
+ iconOutlinePath: options.iconOutlinePath || undefined
300
+ });
301
+ let packageContext;
302
+ try {
303
+ packageContext = await loadChannelPackageContext({
304
+ dataplaneBaseUrl: ctx.dataplaneUrl,
305
+ authConfig: ctx.authConfig,
306
+ channelTarget,
307
+ branding
308
+ });
309
+ } catch {
310
+ packageContext = {
311
+ displayName: name,
312
+ description,
313
+ governedToolNames: [],
314
+ mcpServerUrl: null,
315
+ installReady: false,
316
+ blocker: 'discoveryUnavailable',
317
+ authentication: null,
318
+ authenticationCapabilities: []
319
+ };
320
+ }
321
+ return {
322
+ ...packageContext,
323
+ iconColorPath: branding.iconColorPath,
324
+ iconOutlinePath: branding.iconOutlinePath
325
+ };
326
+ }
327
+
284
328
  async function buildInstallArtifacts(params) {
285
329
  const { channelTarget, options, target, existing, name, baseName, description, ctx } = params;
286
- const runtimeToolNames =
287
- channelTarget === 'chatgpt' || channelTarget === 'microsoft-copilot'
288
- ? await fetchRuntimeLayerBToolNames(ctx.dataplaneUrl, ctx.authConfig, channelTarget)
289
- : [];
330
+ const packageContext = await resolvePackageContextForInstall(params);
331
+ let runtimeToolNames = [];
332
+ if (channelTarget === 'chatgpt' || channelTarget === 'microsoft-copilot') {
333
+ runtimeToolNames =
334
+ packageContext?.governedToolNames?.length > 0
335
+ ? packageContext.governedToolNames
336
+ : await fetchRuntimeLayerBToolNames(ctx.dataplaneUrl, ctx.authConfig, channelTarget);
337
+ }
338
+ let entraProvider = null;
339
+ let entraIdentityProvider = null;
340
+ if (channelTarget === 'microsoft-copilot') {
341
+ ({ entraProvider, entraIdentityProvider } = await loadMicrosoftCopilotEntraContext(ctx));
342
+ }
290
343
  const runtimeValues = resolveChannelRuntimeValues({
291
344
  dataplaneBaseUrl: ctx.dataplaneUrl,
292
345
  channelTarget,
@@ -296,7 +349,14 @@ async function buildInstallArtifacts(params) {
296
349
  appDescription: description,
297
350
  icon: options.icon,
298
351
  commandPrefix: options.commandPrefix,
299
- layerBToolNames: runtimeToolNames
352
+ layerBToolNames: runtimeToolNames,
353
+ packageContext,
354
+ entraProvider,
355
+ entraIdentityProvider,
356
+ oauthPluginVaultReferenceId: options.oauthPluginVaultReferenceId,
357
+ scopes: options.scopes,
358
+ iconColorPath: brandingIconOrOption(packageContext, options, 'iconColorPath'),
359
+ iconOutlinePath: brandingIconOrOption(packageContext, options, 'iconOutlinePath')
300
360
  });
301
361
  const projectRoot = options.projectRoot || process.cwd();
302
362
  const artifact = generateArtifactWithAudit(channelTarget, {
@@ -313,17 +373,39 @@ async function buildInstallArtifacts(params) {
313
373
  return { artifact, channelBlock, status };
314
374
  }
315
375
 
376
+ /**
377
+ * @param {string} channelTarget
378
+ * @param {Object} options
379
+ * @returns {Object}
380
+ */
381
+ function assertChannelAddOptions(channelTarget, options) {
382
+ const target = getChannelTarget(channelTarget);
383
+ if (target.experimental) {
384
+ throw new Error(
385
+ `Channel target "${channelTarget}" is experimental. Confirm product requirement before use.`
386
+ );
387
+ }
388
+ if (!options.name || typeof options.name !== 'string' || !options.name.trim()) {
389
+ throw new Error('--name is required for channel add');
390
+ }
391
+ return target;
392
+ }
393
+
316
394
  /**
317
395
  * @param {string} channelTarget
318
396
  * @param {Object} options
319
397
  * @returns {Promise<Object>}
320
398
  */
321
399
  async function runAddChannel(channelTarget, options = {}) {
322
- const { target, name, baseName, description, ctx, existing } = await prepareChannelContext(
400
+ const target = assertChannelAddOptions(channelTarget, options);
401
+ if (channelTarget === 'microsoft-copilot' && options.force === true) {
402
+ await assertMicrosoftCopilotOauthReferenceBeforeForce(options);
403
+ }
404
+ await ensureChannelReadyForAdd(channelTarget, options);
405
+ const { name, baseName, description, ctx, existing } = await prepareChannelContext(
323
406
  channelTarget,
324
407
  options
325
408
  );
326
-
327
409
  const skipped = showExistingChannelStatus({
328
410
  existing,
329
411
  options,
@@ -333,7 +415,9 @@ async function runAddChannel(channelTarget, options = {}) {
333
415
  ctx
334
416
  });
335
417
  if (skipped) return skipped;
336
-
418
+ if (channelTarget === 'microsoft-copilot') {
419
+ await assertMicrosoftCopilotOauthReference({ options, ctx, name, baseName, description });
420
+ }
337
421
  const { artifact, channelBlock, status } = await buildInstallArtifacts({
338
422
  channelTarget,
339
423
  options,
@@ -344,11 +428,9 @@ async function runAddChannel(channelTarget, options = {}) {
344
428
  description,
345
429
  ctx
346
430
  });
347
-
348
431
  if (options.dryRun) {
349
432
  return finishDryRunInstall({ channelTarget, name, baseName, target, artifact, channelBlock });
350
433
  }
351
-
352
434
  return completeLiveInstall({
353
435
  ctx,
354
436
  channelTarget,
@@ -367,12 +449,7 @@ async function runAddChannel(channelTarget, options = {}) {
367
449
  * @param {Object} options
368
450
  */
369
451
  async function addChannelCommand(channelTarget, options = {}) {
370
- try {
371
- await runAddChannel(channelTarget, options);
372
- } catch (err) {
373
- displayChannelError(err.message || String(err));
374
- throw err;
375
- }
452
+ await runAddChannel(channelTarget, options);
376
453
  }
377
454
 
378
455
  module.exports = {
@@ -22,7 +22,7 @@ function buildChatGptApprovalGuide(ctx) {
22
22
  'Enable Developer mode for custom MCP apps (Business: Workspace settings → Apps, or Settings → Apps → Advanced settings; Enterprise/Edu: grant Developer mode under Permissions & roles → Connected data first).',
23
23
  'Create a custom MCP app with the Remote MCP URL from INSTALL.md (channel-chatgpt.json is a deployment reference, not a plugin archive).',
24
24
  'Select OAuth (authorization-code + PKCE S256). Do not configure a custom API key for a remote custom MCP app.',
25
- 'Scan tools, complete OAuth if prompted, review discovered Runtime Layer B tools, then save the app as a draft.',
25
+ 'Scan tools, complete OAuth if prompted, review discovered governed tools, then save the app as a draft.',
26
26
  formatCredentialPortalStep(
27
27
  ctx,
28
28
  `${ctx.credentialKey || 'channel'} credential references (not secrets to paste into ChatGPT)`
@@ -71,6 +71,9 @@ function buildChannelApprovalGuide(channelTarget, context = {}) {
71
71
  : null;
72
72
  const baseCtx = {
73
73
  channelTarget,
74
+ name,
75
+ baseName: context.baseName || name,
76
+ environment: context.environment || 'dev',
74
77
  artifactPath,
75
78
  resumeCommand,
76
79
  credentialKey: target.credentialKey,