@aifabrix/builder 2.54.1 → 2.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/README.md +8 -0
  2. package/lib/agent-kit/apply-ops.js +82 -0
  3. package/lib/agent-kit/checksums.js +28 -0
  4. package/lib/agent-kit/classify.js +113 -0
  5. package/lib/agent-kit/claude-shim.js +61 -0
  6. package/lib/agent-kit/constants.js +55 -0
  7. package/lib/agent-kit/file-list.js +103 -0
  8. package/lib/agent-kit/host-approve.js +130 -0
  9. package/lib/agent-kit/hosting/github.js +63 -0
  10. package/lib/agent-kit/hosts.js +71 -0
  11. package/lib/agent-kit/init.js +319 -0
  12. package/lib/agent-kit/kit-root.js +17 -0
  13. package/lib/agent-kit/managed-section.js +79 -0
  14. package/lib/agent-kit/package-load.js +45 -0
  15. package/lib/agent-kit/plan-ops.js +69 -0
  16. package/lib/agent-kit/remote-prompts.js +239 -0
  17. package/lib/agent-kit/render-host-markdown.js +40 -0
  18. package/lib/agent-kit/repo-guard.js +47 -0
  19. package/lib/agent-kit/report.js +107 -0
  20. package/lib/agent-kit/run.js +82 -0
  21. package/lib/agent-kit/scm/git.js +122 -0
  22. package/lib/agent-kit/state-file.js +83 -0
  23. package/lib/agent-kit/status.js +50 -0
  24. package/lib/agent-kit/v1-detect.js +81 -0
  25. package/lib/agent-kit/validate-kit.js +87 -0
  26. package/lib/agent-kit/workspace-name.js +40 -0
  27. package/lib/agent-kit/workspace-skeleton.js +103 -0
  28. package/lib/api/role-assistant-test-pack.api.js +86 -0
  29. package/lib/api/work-search.api.js +31 -4
  30. package/lib/api/work.api.js +19 -12
  31. package/lib/app/list-external-systems.js +2 -1
  32. package/lib/app/show-display.js +33 -6
  33. package/lib/app/show.js +25 -2
  34. package/lib/channels/add-channel.js +1 -0
  35. package/lib/channels/approval-guides/claude.js +2 -2
  36. package/lib/channels/channel-add-failure.js +1 -0
  37. package/lib/channels/channel-add-remaining.js +15 -9
  38. package/lib/channels/channel-definition-status.js +115 -0
  39. package/lib/channels/channel-description.js +2 -1
  40. package/lib/channels/channel-install-envelope.js +4 -2
  41. package/lib/channels/channel-integration-generator.js +2 -0
  42. package/lib/channels/channel-runtime-resolver.js +14 -0
  43. package/lib/channels/channel-targets.js +31 -5
  44. package/lib/channels/claude-setup-values.js +4 -1
  45. package/lib/channels/slack-channel-add.js +59 -6
  46. package/lib/channels/slack-mcp-callback.js +51 -0
  47. package/lib/channels/slack-mcp-runtime.js +156 -0
  48. package/lib/channels/slack-provisioning/install-cli.js +14 -2
  49. package/lib/channels/slack-provisioning/with-curl-on-path.js +6 -1
  50. package/lib/cli/index.js +4 -0
  51. package/lib/cli/setup-certification-commands.js +2 -2
  52. package/lib/cli/setup-certification.help.js +3 -1
  53. package/lib/commands/agent-kit-help.js +79 -0
  54. package/lib/commands/agent-kit.js +346 -0
  55. package/lib/commands/channel-add-help.js +5 -5
  56. package/lib/commands/datasource-capability-relate-cli.js +5 -4
  57. package/lib/commands/repair-auth-apply.js +1 -1
  58. package/lib/commands/repair-datasource-key-skip.js +39 -0
  59. package/lib/commands/repair-datasource-keys.js +24 -12
  60. package/lib/commands/repair-rbac.js +16 -10
  61. package/lib/commands/role-assistant-copy-output.js +61 -0
  62. package/lib/commands/role-assistant-copy.js +155 -0
  63. package/lib/commands/role-assistant-test-output.js +8 -9
  64. package/lib/commands/role-assistant-test-result-rows.js +42 -0
  65. package/lib/commands/role-assistant.js +117 -48
  66. package/lib/commands/search.js +143 -0
  67. package/lib/commands/test-trust-external.js +7 -5
  68. package/lib/commands/upload-sidecar-followups.js +4 -1
  69. package/lib/commands/verify-operations-skip-e2e.js +131 -0
  70. package/lib/commands/verify-operations-step-plan.js +6 -33
  71. package/lib/commands/verify-operations-steps.js +3 -1
  72. package/lib/datasource/capability/relate-operations.js +28 -10
  73. package/lib/datasource/capability/run-capability-relate.js +48 -25
  74. package/lib/datasource/datasource-identifier.js +13 -5
  75. package/lib/deployment/deployer-poll-run.js +117 -35
  76. package/lib/external-system/deploy-dataplane-published.js +70 -0
  77. package/lib/external-system/deploy.js +12 -2
  78. package/lib/external-system/download-role-assistant.js +41 -15
  79. package/lib/external-system/download.js +7 -1
  80. package/lib/generator/wizard-datasource-key.js +54 -0
  81. package/lib/generator/wizard.js +55 -54
  82. package/lib/integration-definition/apply-datasource-keys.js +75 -0
  83. package/lib/integration-definition/apply-relationships.js +195 -0
  84. package/lib/integration-definition/apply-static-enterprise.js +229 -0
  85. package/lib/integration-definition/apply-system-file.js +71 -0
  86. package/lib/integration-definition/apply-test-endpoint.js +89 -0
  87. package/lib/integration-definition/apply-viewpoint-defaults.js +157 -0
  88. package/lib/integration-definition/apply.js +78 -59
  89. package/lib/integration-definition/enrich-openapi-operations.js +36 -10
  90. package/lib/integration-definition/enrich-openapi-pick.js +87 -0
  91. package/lib/lifecycle/role-assistant-governance.js +56 -21
  92. package/lib/programmatic/capability-registry.js +1 -0
  93. package/lib/programmatic/index.js +2 -0
  94. package/lib/programmatic/openapi-apply-result-schemas.js +8 -0
  95. package/lib/programmatic/openapi-channel-install-schemas.js +4 -1
  96. package/lib/programmatic/openapi-component-schemas.js +1 -0
  97. package/lib/programmatic/openapi-descriptions.js +9 -3
  98. package/lib/programmatic/openapi-envelope-examples.js +7 -0
  99. package/lib/programmatic/openapi-operation-bindings.js +5 -0
  100. package/lib/programmatic/openapi-request-schemas.js +5 -0
  101. package/lib/programmatic/openapi-schema-descriptions-channel.js +8 -5
  102. package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
  103. package/lib/programmatic/openapi-spec.js +1 -0
  104. package/lib/programmatic/request-timeout.js +3 -0
  105. package/lib/programmatic/route-handler-map.js +7 -0
  106. package/lib/programmatic/run-channel-catalog.js +50 -8
  107. package/lib/programmatic/run-channel-install-options.js +5 -1
  108. package/lib/programmatic/run-download.js +41 -0
  109. package/lib/protection/auth-context.js +3 -1
  110. package/lib/protection/protection-display.js +3 -2
  111. package/lib/protection/report-exit.js +50 -0
  112. package/lib/protection/run-commands.js +2 -0
  113. package/lib/role-assistant/package-validate.js +175 -0
  114. package/lib/role-assistant/test-case-assertion-row.js +36 -0
  115. package/lib/role-assistant/test-case-copy-bind.js +77 -0
  116. package/lib/role-assistant/test-case-copy-expected.js +127 -0
  117. package/lib/role-assistant/test-case-copy-from-work.js +90 -0
  118. package/lib/role-assistant/test-case-copy-interactions.js +67 -0
  119. package/lib/role-assistant/test-case-copy.js +119 -0
  120. package/lib/role-assistant/test-case-expected-catalog.js +135 -0
  121. package/lib/role-assistant/test-case-validate.js +88 -0
  122. package/lib/role-assistant/test-cases-bid.js +117 -4
  123. package/lib/role-assistant/test-cases-evaluate.js +137 -30
  124. package/lib/role-assistant/test-cases-honesty.js +155 -6
  125. package/lib/role-assistant/test-cases-lifecycle.js +291 -0
  126. package/lib/role-assistant/test-cases-search-graph.js +122 -0
  127. package/lib/role-assistant/test-cases-search.js +20 -2
  128. package/lib/role-assistant/test-cases.js +34 -168
  129. package/lib/role-assistant/test-pack-map.js +180 -0
  130. package/lib/role-assistant/test-pack-sync.js +187 -0
  131. package/lib/role-assistant/test-runner-debug.js +54 -0
  132. package/lib/role-assistant/test-runner-respond-body.js +36 -0
  133. package/lib/role-assistant/test-runner-search.js +20 -5
  134. package/lib/role-assistant/test-runner-start-payload.js +63 -10
  135. package/lib/role-assistant/test-runner-workhub-answers.js +45 -16
  136. package/lib/role-assistant/test-runner-workhub.js +45 -21
  137. package/lib/role-assistant/test-runner.js +50 -10
  138. package/lib/schema/external-datasource.schema.json +36 -5
  139. package/lib/schema/external-system.schema.json +20 -2
  140. package/lib/schema/role-assistant-test-case.schema.json +408 -0
  141. package/lib/search/dataplane-context.js +23 -0
  142. package/lib/search/search-args.js +211 -0
  143. package/lib/search/search-display.js +238 -0
  144. package/lib/search/search-errors.js +50 -0
  145. package/lib/search/search-prompts.js +119 -0
  146. package/lib/search/search-roles.js +85 -0
  147. package/lib/search/search-run.js +241 -0
  148. package/lib/search/search-skills.js +198 -0
  149. package/lib/utils/authentication-slot-show.js +153 -0
  150. package/lib/utils/credential-display.js +43 -1
  151. package/lib/utils/external-system-readiness-compact-blocks.js +5 -1
  152. package/lib/utils/external-system-readiness-compact-display.js +30 -1
  153. package/lib/utils/external-system-validators.js +2 -1
  154. package/lib/utils/help-builder.js +3 -1
  155. package/lib/utils/schema-loader.js +37 -0
  156. package/lib/validation/validate-external-file.js +5 -1
  157. package/lib/validation/validate.js +3 -2
  158. package/package.json +1 -1
  159. package/templates/agent-kit/adapters/claude/nested.CLAUDE.md +1 -0
  160. package/templates/agent-kit/adapters/claude/root.CLAUDE.md +5 -0
  161. package/templates/agent-kit/adapters/cursor/commands/cust-0-plan.md +5 -0
  162. package/templates/agent-kit/adapters/cursor/commands/cust-1-validate.md +5 -0
  163. package/templates/agent-kit/adapters/cursor/commands/cust-2-build.md +5 -0
  164. package/templates/agent-kit/adapters/cursor/commands/cust-3-push.md +5 -0
  165. package/templates/agent-kit/adapters/cursor/commands/cust-biz-1-validate.md +5 -0
  166. package/templates/agent-kit/adapters/cursor/commands/cust-biz-2-build.md +5 -0
  167. package/templates/agent-kit/adapters/cursor/commands/cust-biz-3-prove.md +5 -0
  168. package/templates/agent-kit/agent-kit.yaml +45 -0
  169. package/templates/agent-kit/instructions/AGENTKIT.md +83 -0
  170. package/templates/agent-kit/instructions/integration.AGENTS.md +25 -0
  171. package/templates/agent-kit/instructions/role-assistant.AGENTS.md +22 -0
  172. package/templates/agent-kit/instructions/root.AGENTS.md +37 -0
  173. package/templates/agent-kit/parity-inventory.yaml +188 -0
  174. package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +58 -0
  175. package/templates/agent-kit/skills/aifabrix-connected-system/references/contract-checklist.md +40 -0
  176. package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +54 -0
  177. package/templates/agent-kit/skills/aifabrix-connected-system/references/production-safety.md +35 -0
  178. package/templates/agent-kit/skills/aifabrix-connected-system/scripts/reserved-system-key.js +19 -0
  179. package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +51 -0
  180. package/templates/agent-kit/skills/aifabrix-plan/references/challenge-checklist.md +108 -0
  181. package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +34 -0
  182. package/templates/agent-kit/skills/aifabrix-plan/references/plan-template.md +182 -0
  183. package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +50 -0
  184. package/templates/agent-kit/skills/aifabrix-prove/references/demo-template.md +65 -0
  185. package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +68 -0
  186. package/templates/agent-kit/skills/aifabrix-prove/references/learning-template.md +66 -0
  187. package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +42 -0
  188. package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
  189. package/templates/agent-kit/skills/shared/hosts.md +78 -0
  190. package/templates/agent-kit/workspace/.cursor/plans/README.md +22 -0
  191. package/templates/agent-kit/workspace/LICENSE.mit +21 -0
  192. package/templates/agent-kit/workspace/README.md +89 -0
  193. package/templates/applications/builder-api/Dockerfile +3 -1
  194. package/templates/applications/dataplane/application.yaml +2 -2
  195. package/templates/applications/dataplane/env.template +1 -1
  196. package/templates/applications/dataplane/rbac.yaml +28 -0
  197. package/templates/applications/keycloak/env.template +3 -3
  198. package/templates/applications/miso-controller/application.yaml +1 -1
  199. package/templates/applications/miso-controller/env.template +8 -9
  200. package/templates/channels/claude/TROUBLESHOOTING.md +3 -0
  201. package/templates/channels/claude/channel.json +1 -1
  202. package/templates/channels/claude/install.template.md.hbs +1 -0
  203. package/templates/channels/claude/integration/claude-system.json +1 -1
  204. package/templates/channels/slack/channel.json +11 -0
  205. package/templates/channels/slack/install.template.md.hbs +9 -0
  206. package/templates/channels/slack/integration/slack-system.json +11 -0
  207. package/templates/channels/slack/manifest.template.json.hbs +6 -3
  208. package/templates/channels/teams-bot/channel.json +1 -0
  209. package/templates/python/docker-compose.hbs +14 -0
  210. package/templates/typescript/docker-compose.hbs +14 -0
@@ -7,7 +7,9 @@
7
7
  'use strict';
8
8
 
9
9
  const { createDataplaneApiClient } = require('./index');
10
+ const { throwIfApiFailed } = require('./api-response-errors');
10
11
  const { unwrapApiData } = require('../utils/external-system-readiness-core');
12
+ const { pickHumanRespondBody } = require('../role-assistant/test-runner-respond-body');
11
13
 
12
14
  /** Work Hub plan create runs LLM planning; 30s default is too short under load/reload. */
13
15
  const WORK_PLAN_TIMEOUT_MS = 120000;
@@ -59,7 +61,10 @@ function assertObjectShape(value, fallbackMessage) {
59
61
  */
60
62
  async function startWorkIntent(dataplaneUrl, authConfig, body) {
61
63
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
62
- const res = await client.post('/api/v1/work/start', { body });
64
+ const res = await client.post('/api/v1/work/start', {
65
+ body,
66
+ timeoutMs: WORK_PLAN_TIMEOUT_MS
67
+ });
63
68
  if (res && res.success === false) {
64
69
  throw new Error(extractApiErrorMessage(res, 'work.start request failed'));
65
70
  }
@@ -70,7 +75,7 @@ async function startWorkIntent(dataplaneUrl, authConfig, body) {
70
75
 
71
76
  /**
72
77
  * GET /api/v1/work/executions/{processExecutionId}
73
- * @requiresPermission {Dataplane} worker-task:read
78
+ * @requiresPermission {Dataplane} worker:read
74
79
  * @async
75
80
  * @param {string} dataplaneUrl
76
81
  * @param {Object} authConfig
@@ -80,12 +85,13 @@ async function startWorkIntent(dataplaneUrl, authConfig, body) {
80
85
  async function getWorkExecutionStatus(dataplaneUrl, authConfig, processExecutionId) {
81
86
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
82
87
  const encodedId = encodeURIComponent(String(processExecutionId));
83
- const res = await client.get(`/api/v1/work/executions/${encodedId}`, {
88
+ const endpoint = `/api/v1/work/executions/${encodedId}`;
89
+ const res = await client.get(endpoint, {
84
90
  timeoutMs: WORK_TASK_TIMEOUT_MS
85
91
  });
86
- if (res && res.success === false) {
87
- throw new Error(extractApiErrorMessage(res, 'work.status request failed'));
88
- }
92
+ throwIfApiFailed(res, {
93
+ endpointUrl: `${String(dataplaneUrl || '').replace(/\/$/, '')}${endpoint}`
94
+ });
89
95
  const unwrapped = unwrapWorkPayload(res);
90
96
  assertObjectShape(unwrapped, 'work.status returned an unexpected response shape');
91
97
  return unwrapped;
@@ -105,7 +111,7 @@ async function respondWorkExecution(dataplaneUrl, authConfig, processExecutionId
105
111
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
106
112
  const encodedId = encodeURIComponent(String(processExecutionId));
107
113
  const res = await client.post(`/api/v1/work/executions/${encodedId}/respond`, {
108
- body,
114
+ body: pickHumanRespondBody(body),
109
115
  timeoutMs: WORK_TASK_TIMEOUT_MS
110
116
  });
111
117
  if (res && res.success === false) {
@@ -143,7 +149,7 @@ async function continueWorkExecution(dataplaneUrl, authConfig, processExecutionI
143
149
 
144
150
  /**
145
151
  * GET /api/v1/work/executions/{processExecutionId}/result
146
- * @requiresPermission {Dataplane} worker-task:read
152
+ * @requiresPermission {Dataplane} worker:read
147
153
  * @async
148
154
  * @param {string} dataplaneUrl
149
155
  * @param {Object} authConfig
@@ -154,6 +160,7 @@ async function continueWorkExecution(dataplaneUrl, authConfig, processExecutionI
154
160
  async function getWorkExecutionResult(dataplaneUrl, authConfig, processExecutionId, query = {}) {
155
161
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
156
162
  const encodedId = encodeURIComponent(String(processExecutionId));
163
+ const endpoint = `/api/v1/work/executions/${encodedId}/result`;
157
164
  const params = {};
158
165
  if (query && typeof query === 'object') {
159
166
  if (query.requestedDepth) {
@@ -166,13 +173,13 @@ async function getWorkExecutionResult(dataplaneUrl, authConfig, processExecution
166
173
  params.projection = String(query.projection);
167
174
  }
168
175
  }
169
- const res = await client.get(`/api/v1/work/executions/${encodedId}/result`, {
176
+ const res = await client.get(endpoint, {
170
177
  params,
171
178
  timeoutMs: WORK_TASK_TIMEOUT_MS
172
179
  });
173
- if (res && res.success === false) {
174
- throw new Error(extractApiErrorMessage(res, 'work.result request failed'));
175
- }
180
+ throwIfApiFailed(res, {
181
+ endpointUrl: `${String(dataplaneUrl || '').replace(/\/$/, '')}${endpoint}`
182
+ });
176
183
  const unwrapped = unwrapWorkPayload(res);
177
184
  assertObjectShape(unwrapped, 'work.result returned an unexpected response shape');
178
185
  return unwrapped;
@@ -15,6 +15,7 @@ const { listExternalSystems } = require('../api/external-systems.api');
15
15
  const { formatApiError } = require('../utils/api-error-handler');
16
16
  const { getListAuthToken } = require('./list');
17
17
  const logger = require('../utils/logger');
18
+ const { formatLifecycleLabel } = require('../utils/external-system-readiness-compact-display');
18
19
 
19
20
  /**
20
21
  * Normalize list response shapes from the dataplane API client.
@@ -56,7 +57,7 @@ function displayExternalSystems(systems, environment, dataplaneUrl, json) {
56
57
  systems.forEach((system) => {
57
58
  const key = system.key || system.systemKey || system.id || '—';
58
59
  const displayName = system.displayName || key;
59
- const status = system.status || 'unknown';
60
+ const status = formatLifecycleLabel(system.status || 'unknown');
60
61
  logger.log(`✔ ${chalk.cyan(key)} - ${displayName} (${status})`);
61
62
  });
62
63
  logger.log(chalk.gray(' To show details: aifabrix show <systemKey> --online\n'));
@@ -11,6 +11,11 @@
11
11
 
12
12
  const chalk = require('chalk');
13
13
  const logger = require('../utils/logger');
14
+ const { formatCredentialShowLine } = require('../utils/authentication-slot-show');
15
+ const {
16
+ formatConnectedSystemLifecycleLabel,
17
+ formatLifecycleLabel
18
+ } = require('../utils/external-system-readiness-compact-display');
14
19
 
15
20
  function logSourceAndHeader(summary) {
16
21
  const isOffline = summary.source === 'offline';
@@ -93,14 +98,36 @@ function logApplicationFields(a) {
93
98
  logApplicationOptional(a);
94
99
  }
95
100
 
101
+ /**
102
+ * Draft / Published / Archived only — never controller `active`.
103
+ * @param {Object} a
104
+ * @param {Object} [summary]
105
+ * @returns {string|null}
106
+ */
107
+ function resolveExternalApplicationStatusLabel(a, summary) {
108
+ const ext = summary && summary.externalSystem;
109
+ const fromDataplane = ext && !ext.error ? (ext.status || ext.dataplaneStatus) : null;
110
+ return formatConnectedSystemLifecycleLabel(fromDataplane || a.status);
111
+ }
112
+
113
+ function logExternalApplicationStatus(a, summary) {
114
+ const label = resolveExternalApplicationStatusLabel(a, summary);
115
+ if (label) logger.log(` Status: ${label}`);
116
+ }
117
+
118
+ function formatDataplaneStatusDisplay(status) {
119
+ if (status === undefined || status === null || status === '' || status === '—') return '—';
120
+ return formatConnectedSystemLifecycleLabel(status) || formatLifecycleLabel(status);
121
+ }
122
+
96
123
  /** Application section for online + external: Key, Display name, Description, Deployment, Status (no Type; Dataplane block follows). */
97
- function logApplicationFieldsOnlineExternal(a) {
124
+ function logApplicationFieldsOnlineExternal(a, summary) {
98
125
  logger.log('🧷 Application - external');
99
126
  logger.log(` Key: ${a.key ?? '—'}`);
100
127
  logger.log(` Display name: ${a.displayName ?? '—'}`);
101
128
  logger.log(` Description: ${a.description ?? '—'}`);
102
129
  logger.log(' Deployment: —');
103
- if (a.status !== undefined && a.status !== '—') logger.log(` Status: ${a.status}`);
130
+ logExternalApplicationStatus(a, summary);
104
131
  }
105
132
 
106
133
  /** Application section for offline + external: Key, Display name, Description, Deployment, Status, Version when present. */
@@ -110,7 +137,7 @@ function logApplicationFieldsOfflineExternal(a) {
110
137
  logger.log(` Display name: ${a.displayName ?? '—'}`);
111
138
  logger.log(` Description: ${a.description ?? '—'}`);
112
139
  logger.log(' Deployment: —');
113
- if (a.status !== undefined && a.status !== '—') logger.log(` Status: ${a.status}`);
140
+ logExternalApplicationStatus(a);
114
141
  if (a.version !== undefined && a.version !== null) logger.log(` Version: ${a.version}`);
115
142
  }
116
143
 
@@ -129,7 +156,7 @@ function logApplicationSection(a, summary) {
129
156
  const onlineExternal = summary.source === 'online' && isExternal;
130
157
  const offlineExternal = summary.source === 'offline' && isExternal;
131
158
  if (onlineExternal) {
132
- logApplicationFieldsOnlineExternal(a);
159
+ logApplicationFieldsOnlineExternal(a, summary);
133
160
  } else if (offlineExternal) {
134
161
  logApplicationFieldsOfflineExternal(a);
135
162
  } else {
@@ -205,8 +232,8 @@ function logDatabasesSection(dbNames) {
205
232
  function logExternalSystemMain(ext) {
206
233
  logger.log('🧩 Dataplane');
207
234
  logger.log(` Version: ${ext.version ?? '—'}`);
208
- logger.log(` Credential: ${ext.credentialId ?? '—'}`);
209
- logger.log(` Status: ${ext.status ?? '—'}`);
235
+ logger.log(` Credential: ${formatCredentialShowLine(ext)}`);
236
+ logger.log(` Status: ${formatDataplaneStatusDisplay(ext.status)}`);
210
237
  logger.log(` API docs: ${ext.openApiDocsPageUrl ?? ext.apiDocumentUrl ?? '—'}`);
211
238
  logger.log(` MCP server: ${ext.mcpServerUrl ?? '—'}`);
212
239
  logger.log(` OpenAPI spec: ${ext.apiDocumentUrl ?? '—'}`);
package/lib/app/show.js CHANGED
@@ -33,6 +33,8 @@ const {
33
33
  attachCertificationVerifyFromDataplane,
34
34
  sanitizeCertificationForJson
35
35
  } = require('./certification-show-enrich');
36
+ const { resolveManifestAuthMethod } = require('../utils/external-system-readiness-compact-display');
37
+ const { resolveAuthenticationSlots } = require('../utils/authentication-slot-show');
36
38
 
37
39
  /**
38
40
  * Attach `runReloadDefault` / `runProxyDefault` from ~/.aifabrix `applications.<appKey>`.
@@ -466,6 +468,13 @@ function pickExternalVersion(res, config, system) {
466
468
  return (res && res.version) ?? config.version ?? system.version ?? '—';
467
469
  }
468
470
 
471
+ function pickCredentialMethod(config, system) {
472
+ const source = config && (config.authentication || config.authentications) ? config : system;
473
+ const method = resolveManifestAuthMethod(source);
474
+ if (!method || method === 'unknown') return undefined;
475
+ return method;
476
+ }
477
+
469
478
  function resolveExternalSystemMeta(res, config, system, application, appKey) {
470
479
  return {
471
480
  displayName: pickExternalDisplayName(res, system, application, appKey),
@@ -501,13 +510,27 @@ function buildBaseExternalResult(configData, appKey, openapiFiles, openapiEndpoi
501
510
  permissions: application.permissions
502
511
  },
503
512
  openapiFiles,
504
- openapiEndpoints
513
+ openapiEndpoints,
514
+ credentialMethod: pickCredentialMethod(config, system),
515
+ authenticationSlots: resolveAuthenticationSlots(config, system)
505
516
  };
506
517
  }
507
518
 
519
+ function applyCredentialFieldsToResult(result, res) {
520
+ if (res.credentialId !== undefined && res.credentialId !== null) result.credentialId = res.credentialId;
521
+ if (res.authenticationStatus !== undefined && res.authenticationStatus !== null) {
522
+ result.authenticationStatus = res.authenticationStatus;
523
+ }
524
+ const resConfig = res.config && typeof res.config === 'object' ? res.config : {};
525
+ const fromConfig = pickCredentialMethod(resConfig, {});
526
+ if (fromConfig) result.credentialMethod = fromConfig;
527
+ const fromSlots = resolveAuthenticationSlots(resConfig, {});
528
+ if (fromSlots.length > 0) result.authenticationSlots = fromSlots;
529
+ }
530
+
508
531
  function applySystemResponseToResult(result, res) {
509
532
  result.dataplaneStatus = res.status;
510
- if (res.credentialId !== undefined && res.credentialId !== null) result.credentialId = res.credentialId;
533
+ applyCredentialFieldsToResult(result, res);
511
534
  if (res.showOpenApiDocs !== undefined) result.showOpenApiDocs = res.showOpenApiDocs;
512
535
  if (res.mcpServerUrl !== undefined && res.mcpServerUrl !== null) result.mcpServerUrl = res.mcpServerUrl;
513
536
  if (res.apiDocumentUrl !== undefined && res.apiDocumentUrl !== null) result.apiDocumentUrl = res.apiDocumentUrl;
@@ -350,6 +350,7 @@ async function resolveInstallRuntimeValues(params, packageContext, runtimeToolNa
350
350
  appDescription: description,
351
351
  icon: options.icon,
352
352
  commandPrefix: options.commandPrefix,
353
+ enableMcpServer: options.enableMcpServer,
353
354
  layerBToolNames: runtimeToolNames,
354
355
  packageContext,
355
356
  entraProvider,
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Claude remaining workspace steps (customer-facing).
3
3
  *
4
- * @fileoverview Short Claude connector Name and Server URL steps — no protocol dump
4
+ * @fileoverview Short Claude connector Name, Server URL, and OAuth steps — no protocol dump
5
5
  */
6
6
 
7
7
  'use strict';
@@ -21,7 +21,7 @@ function buildClaudeApprovalGuide(ctx) {
21
21
  credentialPortalUrl: null,
22
22
  steps: [
23
23
  `Open ${CLAUDE_CONNECTORS_URL}`,
24
- `Add a custom connector with Name: ${name}; Server URL: ${serverUrl}.`
24
+ `Add a custom connector with Name: ${name}; Server URL: ${serverUrl}; Authentication: OAuth.`
25
25
  ]
26
26
  };
27
27
  }
@@ -16,6 +16,7 @@ const BOUNDARY_MATCHERS = [
16
16
  tests: [/unknown channel/i, /already configured/i, /needs a terminal/i, /--name is required/i, /needs --target/i, /unknown mcp client profile/i]
17
17
  },
18
18
  { boundary: 'experimental', tests: [/is experimental/i] },
19
+ { boundary: 'unavailable', tests: [/is coming soon/i, /is not available/i] },
19
20
  { boundary: 'preflight', tests: [/chatgpt preflight/i, /claude preflight/i, /mcp client preflight/i, /authentication required/i, /dataplanebaseurl is required/i] },
20
21
  { boundary: 'callback', tests: [/chatgpt callback/i, /claude callback/i, /mcp client callback/i] },
21
22
  { boundary: 'oauth', tests: [/oauth/i, /trustedissuer/i, /vault-reference/i] },
@@ -30,6 +30,7 @@ const { printMcpClientDryRunTty } = require('./mcp-client-channel-add');
30
30
  const { printClaudeDryRunTty } = require('./claude-channel-add');
31
31
  const { CLAUDE_REMAINING_STEPS } = require('./claude-runtime-contract');
32
32
  const { vendorFacingUrlsFromCtx } = require('./vendor-dataplane-url');
33
+ const { slackMcpRemainingSteps } = require('./slack-mcp-runtime');
33
34
  const {
34
35
  channelDisplayLabel,
35
36
  printValidating,
@@ -280,18 +281,23 @@ function slackRecoveryOrHandshake(ctx, custom) {
280
281
  * @param {Object|null} ctx
281
282
  * @returns {Object}
282
283
  */
283
- function slackRemainingSpec(ctx) {
284
+ function slackRemainingSpec(ctx, runtimeValues) {
284
285
  const custom = slackCustomSteps(ctx);
286
+ const mcpSteps = slackMcpRemainingSteps(runtimeValues);
287
+ let spec;
285
288
  if (ctx && ctx.slackConnected === true) {
286
- return slackConnectedRemaining(ctx, custom);
289
+ spec = slackConnectedRemaining(ctx, custom);
290
+ } else if (ctx && ctx.slackSlashCommand) {
291
+ spec = slackSlashRemaining(ctx, custom);
292
+ } else if (ctx && ctx.slackHandshakeFailed === true) {
293
+ spec = slackFailedRemaining(custom);
294
+ } else {
295
+ spec = slackRecoveryOrHandshake(ctx, custom);
287
296
  }
288
- if (ctx && ctx.slackSlashCommand) {
289
- return slackSlashRemaining(ctx, custom);
297
+ if (mcpSteps.length === 0) {
298
+ return spec;
290
299
  }
291
- if (ctx && ctx.slackHandshakeFailed === true) {
292
- return slackFailedRemaining(custom);
293
- }
294
- return slackRecoveryOrHandshake(ctx, custom);
300
+ return { ...spec, steps: [...(spec.steps || []), ...mcpSteps] };
295
301
  }
296
302
 
297
303
  /**
@@ -350,7 +356,7 @@ function teamsBotRemainingSpec(ctx, runtimeValues) {
350
356
  */
351
357
  function remainingSpec(channelTarget, ctx, runtimeValues) {
352
358
  if (channelTarget === 'slack') {
353
- return slackRemainingSpec(ctx);
359
+ return slackRemainingSpec(ctx, runtimeValues);
354
360
  }
355
361
  if (channelTarget === 'microsoft-copilot') {
356
362
  return copilotRemainingSpec(ctx, runtimeValues);
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Channel Definition catalog/install status (visible | hide | coming-soon).
3
+ *
4
+ * @fileoverview Omit or visible: listed and installable. hide: omitted and refused. coming-soon: listed read-only.
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const registry = require('./channel-definition-registry');
12
+
13
+ const CHANNEL_STATUSES = ['visible', 'hide', 'coming-soon'];
14
+ const COMING_SOON_REASON = 'This channel is coming soon.';
15
+ const HIDE_REASON = 'This channel is not available.';
16
+
17
+ /**
18
+ * @param {Object|null} definition
19
+ * @returns {'visible'|'hide'|'coming-soon'}
20
+ */
21
+ function normalizeChannelStatus(definition) {
22
+ const raw = definition && definition.channel && definition.channel.status;
23
+ if (raw === 'hide' || raw === 'coming-soon') {
24
+ return raw;
25
+ }
26
+ return 'visible';
27
+ }
28
+
29
+ /**
30
+ * Live status from the Channel Definition registry (spy-friendly).
31
+ * @param {string} key
32
+ * @returns {'visible'|'hide'|'coming-soon'}
33
+ */
34
+ function channelStatusOf(key) {
35
+ return normalizeChannelStatus(registry.getChannelDefinition(key));
36
+ }
37
+
38
+ /**
39
+ * @param {string} status
40
+ * @returns {boolean}
41
+ */
42
+ function isCatalogHiddenStatus(status) {
43
+ return status === 'hide';
44
+ }
45
+
46
+ /**
47
+ * @param {string} status
48
+ * @returns {boolean}
49
+ */
50
+ function isInstallableStatus(status) {
51
+ return status === 'visible';
52
+ }
53
+
54
+ /**
55
+ * @param {string} key
56
+ * @param {string} status
57
+ * @returns {Error}
58
+ */
59
+ function statusUnavailableError(key, status) {
60
+ const message =
61
+ status === 'coming-soon'
62
+ ? `Channel target "${key}" is coming soon.`
63
+ : `Channel target "${key}" is not available.`;
64
+ const err = new Error(message);
65
+ err.statusCode = 403;
66
+ return err;
67
+ }
68
+
69
+ /**
70
+ * Refuse new install for hide and coming-soon. Continue of an existing session skips.
71
+ * --force / allowExperimental do not override.
72
+ * @param {string} key
73
+ * @param {{ continueInstall?: boolean }} [options]
74
+ * @returns {void}
75
+ */
76
+ function assertChannelStatusInstallable(key, options = {}) {
77
+ if (options.continueInstall === true) {
78
+ return;
79
+ }
80
+ const status = channelStatusOf(key);
81
+ if (isInstallableStatus(status)) {
82
+ return;
83
+ }
84
+ throw statusUnavailableError(key, status);
85
+ }
86
+
87
+ /**
88
+ * Overlay coming-soon onto a catalog/detail projection (canInstall false).
89
+ * @param {string} status
90
+ * @param {Object} [projection]
91
+ * @returns {Object}
92
+ */
93
+ function applyChannelStatusProjection(status, projection = {}) {
94
+ if (status !== 'coming-soon') {
95
+ return { ...projection, status };
96
+ }
97
+ return {
98
+ ...projection,
99
+ status,
100
+ canInstall: false,
101
+ blockedReason: COMING_SOON_REASON
102
+ };
103
+ }
104
+
105
+ module.exports = {
106
+ CHANNEL_STATUSES,
107
+ COMING_SOON_REASON,
108
+ HIDE_REASON,
109
+ normalizeChannelStatus,
110
+ channelStatusOf,
111
+ isCatalogHiddenStatus,
112
+ isInstallableStatus,
113
+ assertChannelStatusInstallable,
114
+ applyChannelStatusProjection
115
+ };
@@ -99,7 +99,8 @@ function fillSlackManifestDescriptions(channelTarget, runtimeValues) {
99
99
  return {
100
100
  ...runtimeValues,
101
101
  appLongDescription,
102
- appLongDescriptionJson: JSON.stringify(appLongDescription)
102
+ appLongDescriptionJson: JSON.stringify(appLongDescription),
103
+ enableMcpServer: runtimeValues.enableMcpServer === true
103
104
  };
104
105
  }
105
106
 
@@ -135,7 +135,8 @@ function claudeCopyActions(runtimeValues) {
135
135
  const values = buildClaudeFormValues({ runtimeValues: runtimeValues || {} });
136
136
  return [
137
137
  copyValueAction('Name', values.name, 'copy-name'),
138
- copyValueAction('Server URL', values.serverUrl, 'copy-server-url')
138
+ copyValueAction('Server URL', values.serverUrl, 'copy-server-url'),
139
+ copyValueAction('Authentication', values.authentication, 'copy-authentication')
139
140
  ].filter(Boolean);
140
141
  }
141
142
 
@@ -195,7 +196,8 @@ function claudeEnvelopeParts(params) {
195
196
  useValues: runtimeOk
196
197
  ? {
197
198
  Name: values.name,
198
- 'Server URL': values.serverUrl
199
+ 'Server URL': values.serverUrl,
200
+ Authentication: values.authentication
199
201
  }
200
202
  : {}
201
203
  };
@@ -12,6 +12,7 @@
12
12
 
13
13
  const path = require('path');
14
14
  const { getChannelTarget } = require('./channel-targets');
15
+ const { assertChannelStatusInstallable } = require('./channel-definition-status');
15
16
  const { getIntegrationPath } = require('../utils/paths');
16
17
  const {
17
18
  copyChannelIntegrationTemplate,
@@ -29,6 +30,7 @@ const { generateDeployJson } = require('../generator/index');
29
30
  */
30
31
  async function scaffoldChannelIntegration(channelTarget, options = {}) {
31
32
  const target = getChannelTarget(channelTarget);
33
+ assertChannelStatusInstallable(channelTarget, options);
32
34
  if (target.experimental && !options.allowExperimental) {
33
35
  throw new Error(
34
36
  `Channel target "${channelTarget}" is experimental. Pass --allow-experimental to scaffold.`
@@ -36,6 +36,7 @@ const { applyCopilotOauthProfile, applyCopilotRuntimeDomain } = require('./chann
36
36
  const { chatgptDiscoveryBlockerLabel } = require('./chatgpt-setup-values');
37
37
  const { applyMcpClientRuntimeValues } = require('./mcp-client/runtime');
38
38
  const { applyClaudeRuntimeValues } = require('./claude-runtime');
39
+ const { applySlackMcpRuntimeValues } = require('./slack-mcp-runtime');
39
40
 
40
41
  const DEFAULT_SLACK_COMMAND_PREFIX = 'af';
41
42
 
@@ -347,6 +348,7 @@ function applySlackRuntimeValues(resolved, params) {
347
348
  Object.assign(resolved, resolveSlackOAuthManifestFields());
348
349
  delete resolved.oauthCallbackUrl;
349
350
  resolved.commandPrefix = normalizeSlackCommandPrefix(params.commandPrefix);
351
+ resolved.enableMcpServer = isTrueLike(params.enableMcpServer);
350
352
  resolved.packageVersion =
351
353
  typeof params.packageVersion === 'string' && params.packageVersion.trim()
352
354
  ? params.packageVersion.trim()
@@ -356,6 +358,17 @@ function applySlackRuntimeValues(resolved, params) {
356
358
  resolved.appLongDescriptionJson = JSON.stringify(appLongDescription);
357
359
  }
358
360
 
361
+ /**
362
+ * @param {unknown} value
363
+ * @returns {boolean}
364
+ */
365
+ function isTrueLike(value) {
366
+ if (value === true) {
367
+ return true;
368
+ }
369
+ return typeof value === 'string' && /^(true|1|yes|on)$/i.test(value.trim());
370
+ }
371
+
359
372
  /**
360
373
  * Slack slash alias prefix: letters then letters/digits, 1–16 chars.
361
374
  * @param {unknown} raw
@@ -438,6 +451,7 @@ function applyChannelSpecificRuntime(resolved, params) {
438
451
  if (params.channelTarget === 'slack') {
439
452
  applySlackRuntimeValues(resolved, params);
440
453
  applyDiscoveryPackageContext(resolved, params.packageContext);
454
+ applySlackMcpRuntimeValues(resolved, params);
441
455
  }
442
456
  if (params.channelTarget === 'microsoft-copilot') {
443
457
  applyMicrosoftCopilotRuntimeValues(resolved, params);
@@ -14,6 +14,12 @@ const {
14
14
  listChannelDefinitionKeys
15
15
  } = require('./channel-definition-registry');
16
16
  const { toChannelTargetProjection } = require('./channel-definition-project');
17
+ const {
18
+ assertChannelStatusInstallable,
19
+ channelStatusOf,
20
+ isCatalogHiddenStatus,
21
+ isInstallableStatus
22
+ } = require('./channel-definition-status');
17
23
 
18
24
  function buildChannelTargets() {
19
25
  const targets = {};
@@ -48,15 +54,21 @@ function isChannelTargetKey(channelTarget) {
48
54
  */
49
55
  function formatSupportedChannelTargetsHelp() {
50
56
  const targets = resolveChannelTargets();
51
- const keys = Object.keys(targets);
52
- const stable = keys.filter((key) => !targets[key].experimental);
53
- const experimental = keys.filter((key) => targets[key].experimental);
57
+ const installable = listInstallableChannelTargetKeys({ allowExperimental: true });
58
+ const stable = installable.filter((key) => !targets[key].experimental);
59
+ const experimental = installable.filter((key) => targets[key].experimental);
60
+ const comingSoon = listLiveChannelTargetKeys({ allowExperimental: true }).filter(
61
+ (key) => channelStatusOf(key) === 'coming-soon'
62
+ );
54
63
  const lines = ['Supported channels:', ` ${stable.join(', ')}`];
55
64
  if (experimental.length > 0) {
56
65
  lines.push(
57
66
  ` ${experimental.join(', ')} (experimental — scaffold with --allow-experimental)`
58
67
  );
59
68
  }
69
+ if (comingSoon.length > 0) {
70
+ lines.push(` ${comingSoon.join(', ')} (coming soon — not installable)`);
71
+ }
60
72
  return lines.join('\n');
61
73
  }
62
74
 
@@ -81,18 +93,30 @@ function getChannelTarget(channelTarget) {
81
93
  }
82
94
 
83
95
  /**
84
- * Live keys, plus experimental when `--allow-experimental` is set.
96
+ * Live keys (not hide), plus experimental when `--allow-experimental` is set.
85
97
  * @param {{ allowExperimental?: boolean }} [options]
86
98
  * @returns {string[]}
87
99
  */
88
100
  function listLiveChannelTargetKeys(options = {}) {
89
101
  const targets = resolveChannelTargets();
90
102
  return Object.keys(targets).filter((key) => {
103
+ if (isCatalogHiddenStatus(channelStatusOf(key))) {
104
+ return false;
105
+ }
91
106
  const target = targets[key];
92
107
  return !target.experimental || options.allowExperimental === true;
93
108
  });
94
109
  }
95
110
 
111
+ /**
112
+ * Keys that may start a new install (visible). Coming-soon is catalog-only.
113
+ * @param {{ allowExperimental?: boolean }} [options]
114
+ * @returns {string[]}
115
+ */
116
+ function listInstallableChannelTargetKeys(options = {}) {
117
+ return listLiveChannelTargetKeys(options).filter((key) => isInstallableStatus(channelStatusOf(key)));
118
+ }
119
+
96
120
  /**
97
121
  * Inquirer list choices for `aifabrix channel add` (labels from the registry).
98
122
  * @param {{ allowExperimental?: boolean }} [options]
@@ -100,7 +124,7 @@ function listLiveChannelTargetKeys(options = {}) {
100
124
  */
101
125
  function listWizardChannelChoices(options = {}) {
102
126
  const targets = resolveChannelTargets();
103
- return listLiveChannelTargetKeys(options)
127
+ return listInstallableChannelTargetKeys(options)
104
128
  .map((key) => targets[key])
105
129
  .sort((a, b) => (a.wizardOrder || 100) - (b.wizardOrder || 100))
106
130
  .map((target) => ({ name: target.displayLabel, value: target.key }));
@@ -114,6 +138,7 @@ function listWizardChannelChoices(options = {}) {
114
138
  */
115
139
  function assertChannelTargetInstallable(channelTarget, options = {}) {
116
140
  const target = getChannelTarget(channelTarget);
141
+ assertChannelStatusInstallable(channelTarget, options);
117
142
  if (target.experimental && options.allowExperimental !== true) {
118
143
  throw new Error(
119
144
  `Channel target "${channelTarget}" is experimental. Pass --allow-experimental to install.`
@@ -146,6 +171,7 @@ module.exports = {
146
171
  formatSupportedChannelTargetsHelp,
147
172
  formatUnknownChannelTargetError,
148
173
  listLiveChannelTargetKeys,
174
+ listInstallableChannelTargetKeys,
149
175
  listWizardChannelChoices,
150
176
  assertChannelTargetInstallable,
151
177
  getChannelDefinition,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Customer-facing Claude connector form values: Name and Server URL.
2
+ * Customer-facing Claude connector form values: Name, Server URL, OAuth.
3
3
  *
4
4
  * @fileoverview Claude channel form values — describe, don't configure
5
5
  */
@@ -78,6 +78,9 @@ function formatClaudeFormValueLines(values) {
78
78
  if (values.serverUrl) {
79
79
  lines.push(`Server URL: ${values.serverUrl}`);
80
80
  }
81
+ if (values.authentication) {
82
+ lines.push(`Authentication: ${values.authentication}`);
83
+ }
81
84
  return lines;
82
85
  }
83
86