@aifabrix/builder 2.46.0 → 2.47.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 (175) hide show
  1. package/.npmrc.token +1 -0
  2. package/.temp/jest-protection-convert/run-IFN2nv/.protection/hubspot-companies.json +1 -0
  3. package/.temp/jest-protection-convert/run-IvmKLO/.protection/hubspot-companies.yaml +17 -0
  4. package/.temp/jest-protection-convert/run-SdvblF/.protection/hubspot-companies.json +1 -0
  5. package/.temp/jest-protection-convert/run-b51TsW/.protection/hubspot-companies.json +1 -0
  6. package/.temp/jest-protection-convert/run-fiLmb1/.protection/hubspot-companies.json +1 -0
  7. package/.temp/jest-protection-convert/run-qYGHMU/.protection/hubspot-companies.yaml +17 -0
  8. package/.temp/jest-protection-fixtures/hubspot-Mq5re8/hubspot-companies.yaml +17 -0
  9. package/.temp/jest-protection-fixtures/hubspot-f3u4YL/hubspot-companies.yaml +17 -0
  10. package/.temp/jest-protection-fixtures/hubspot-iQtd1O/hubspot-companies.yaml +17 -0
  11. package/.temp/jest-protection-fixtures/hubspot-mdmz5M/hubspot-companies.yaml +17 -0
  12. package/.temp/jest-protection-fixtures/hubspot-qbKYxQ/hubspot-companies.yaml +17 -0
  13. package/.temp/jest-protection-fixtures/hubspot-yzlJy6/hubspot-companies.yaml +17 -0
  14. package/.temp/validation/summary.txt +5 -0
  15. package/jest.isolated-projects.js +18 -2
  16. package/jest.projects.js +24 -0
  17. package/lib/api/dataplane-health.api.js +19 -7
  18. package/lib/api/lifecycle.api.js +22 -1
  19. package/lib/api/resource-types.api.js +107 -0
  20. package/lib/api/runtime-configuration.api.js +72 -0
  21. package/lib/api/types/resource-types.types.js +45 -0
  22. package/lib/api/user-groups.api.js +9 -9
  23. package/lib/api/users.api.js +8 -8
  24. package/lib/api/wizard.api.js +10 -2
  25. package/lib/app/down.js +58 -26
  26. package/lib/app/index.js +2 -2
  27. package/lib/app/run-resolve-image.js +2 -1
  28. package/lib/app/run-stop-containers.js +113 -0
  29. package/lib/app/run.js +1 -31
  30. package/lib/app/show-display.js +35 -0
  31. package/lib/app/show.js +32 -1
  32. package/lib/build/index.js +17 -5
  33. package/lib/build/miso-controller-npm-sdk-guard.js +25 -0
  34. package/lib/channels/add-channel.js +348 -0
  35. package/lib/channels/channel-artifact-generator.js +141 -0
  36. package/lib/channels/channel-audit.js +101 -0
  37. package/lib/channels/channel-auth-context.js +37 -0
  38. package/lib/channels/channel-credential.js +200 -0
  39. package/lib/channels/channel-display-name.js +64 -0
  40. package/lib/channels/channel-external-system.js +106 -0
  41. package/lib/channels/channel-integration-generator.js +274 -0
  42. package/lib/channels/channel-probe.js +89 -0
  43. package/lib/channels/channel-runtime-resolver.js +152 -0
  44. package/lib/channels/channel-status-display.js +141 -0
  45. package/lib/channels/channel-targets.js +115 -0
  46. package/lib/cli/index.js +16 -4
  47. package/lib/cli/setup-app.js +54 -3
  48. package/lib/cli/setup-infra-up-dataplane-action.js +2 -2
  49. package/lib/cli/setup-infra-up-platform-action.js +2 -1
  50. package/lib/cli/setup-platform.js +1 -1
  51. package/lib/cli/setup-utility-help-after.js +4 -1
  52. package/lib/cli/setup-utility-resolve.js +8 -5
  53. package/lib/commands/app-logs.js +54 -26
  54. package/lib/commands/app.js +41 -0
  55. package/lib/commands/auth-status-dataplane-version.js +97 -61
  56. package/lib/commands/auth-status.js +2 -7
  57. package/lib/commands/channel.js +58 -0
  58. package/lib/commands/datasource.js +1 -1
  59. package/lib/commands/identity-apply-sync.js +49 -8
  60. package/lib/commands/identity-group-cli.js +66 -26
  61. package/lib/commands/identity-membership-role-cache.js +58 -37
  62. package/lib/commands/identity-shared.js +22 -1
  63. package/lib/commands/identity-user-cli.js +80 -34
  64. package/lib/commands/repair-auth-apply.js +30 -4
  65. package/lib/commands/repair-auth-config.js +8 -13
  66. package/lib/commands/repair-datasource-com-v2.js +15 -0
  67. package/lib/commands/repair-datasource-service-contract.js +206 -0
  68. package/lib/commands/repair-datasource.js +53 -18
  69. package/lib/commands/repair-env-template.js +12 -12
  70. package/lib/commands/repair-openapi-sync.js +26 -4
  71. package/lib/commands/repair-steps.js +28 -13
  72. package/lib/commands/repair-system-alignment.js +7 -4
  73. package/lib/commands/resource-type.js +371 -0
  74. package/lib/commands/setup-modes.js +1 -1
  75. package/lib/commands/setup-prompts-platform-mode.js +24 -21
  76. package/lib/commands/test-trust-external.js +39 -90
  77. package/lib/commands/up-common.js +33 -1
  78. package/lib/commands/up-dataplane.js +8 -4
  79. package/lib/commands/upload.js +24 -3
  80. package/lib/commands/verify-trust-command-action.js +4 -1
  81. package/lib/commands/verify-trust-external.js +12 -20
  82. package/lib/core/config-setup-platform-mode.js +3 -3
  83. package/lib/core/setup-platform-mode.js +41 -6
  84. package/lib/datasource/capability/capability-diff-slice.js +2 -2
  85. package/lib/datasource/capability/capability-manifest.js +254 -0
  86. package/lib/datasource/capability/capability-resolve.js +3 -4
  87. package/lib/datasource/capability/copy-operations.js +5 -3
  88. package/lib/datasource/capability/remove-operations.js +2 -1
  89. package/lib/datasource/capability/run-capability-copy.js +32 -4
  90. package/lib/datasource/capability/run-capability-diff.js +3 -3
  91. package/lib/datasource/capability/run-capability-dimension.js +2 -4
  92. package/lib/datasource/capability/run-capability-edit.js +4 -7
  93. package/lib/datasource/capability/run-capability-relate.js +2 -4
  94. package/lib/datasource/capability/run-capability-remove.js +2 -4
  95. package/lib/datasource/capability/validate-capability-slice.js +2 -1
  96. package/lib/datasource/datasource-identifier.js +2 -1
  97. package/lib/datasource/datasource-validate-display.js +1 -1
  98. package/lib/datasource/field-reference-validator.js +24 -3
  99. package/lib/datasource/orphan-profiles-validator.js +98 -0
  100. package/lib/datasource/validate.js +41 -23
  101. package/lib/external-system/download-helpers.js +87 -0
  102. package/lib/external-system/download.js +61 -122
  103. package/lib/external-system/generator.js +1 -59
  104. package/lib/external-system/test.js +10 -15
  105. package/lib/generator/builders.js +11 -1
  106. package/lib/generator/external-controller-manifest.js +5 -2
  107. package/lib/generator/split.js +3 -1
  108. package/lib/generator/wizard.js +1 -28
  109. package/lib/identity/fk-reference.js +65 -0
  110. package/lib/identity/identity-apply-core.js +13 -58
  111. package/lib/identity/identity-apply-phases.js +5 -1
  112. package/lib/identity/identity-display.js +243 -0
  113. package/lib/identity/identity-response-helpers.js +52 -0
  114. package/lib/identity/identity-sync-errors.js +62 -0
  115. package/lib/identity/resolve-user-id.js +129 -0
  116. package/lib/identity/username-default.js +31 -0
  117. package/lib/lifecycle/certification-progress.js +2 -2
  118. package/lib/lifecycle/report-display-trust.js +70 -0
  119. package/lib/lifecycle/report-display.js +14 -13
  120. package/lib/resolvers/datasource-resolver.js +2 -3
  121. package/lib/resolvers/resource-type-file.js +159 -0
  122. package/lib/schema/application-schema.json +16 -0
  123. package/lib/schema/deployment-rules.yaml +2 -1
  124. package/lib/schema/external-datasource.schema.json +372 -58
  125. package/lib/schema/external-system.schema.json +331 -2
  126. package/lib/schema/type/document-storage.json +9 -5
  127. package/lib/utils/app-run-containers.js +25 -2
  128. package/lib/utils/authentication-shape.js +224 -0
  129. package/lib/utils/build-copy.js +36 -5
  130. package/lib/utils/build-resolve-image.js +66 -12
  131. package/lib/utils/celery-companion-apps.js +53 -0
  132. package/lib/utils/cli-utils.js +4 -0
  133. package/lib/utils/compose-celery-worker.js +23 -0
  134. package/lib/utils/compose-generate-docker-compose.js +9 -1
  135. package/lib/utils/configuration-env-resolver.js +36 -0
  136. package/lib/utils/credential-secrets-env-kv.js +216 -0
  137. package/lib/utils/credential-secrets-env.js +52 -262
  138. package/lib/utils/dataplane-resolver.js +4 -115
  139. package/lib/utils/declarative-url-ports.js +24 -1
  140. package/lib/utils/docker-build.js +1 -4
  141. package/lib/utils/external-env-template.js +10 -3
  142. package/lib/utils/external-system-validators.js +8 -2
  143. package/lib/utils/help-builder.js +2 -0
  144. package/lib/utils/paths.js +25 -0
  145. package/lib/utils/platform-controller-url.js +2 -1
  146. package/lib/utils/platform-kv-ref.js +57 -0
  147. package/lib/utils/runtime-configuration-download.js +189 -0
  148. package/lib/utils/runtime-configuration-values.js +119 -0
  149. package/lib/utils/schema-loader.js +19 -4
  150. package/lib/utils/worker-sidecars.js +142 -0
  151. package/lib/validation/external-manifest-validator.js +2 -0
  152. package/lib/validation/runtime-configuration-rules.js +98 -0
  153. package/lib/validation/validate-display-dimensions.js +154 -0
  154. package/lib/validation/validate-display-sections.js +276 -0
  155. package/lib/validation/validate-display-steps.js +67 -0
  156. package/lib/validation/validate-display.js +24 -402
  157. package/package.json +1 -1
  158. package/templates/applications/dataplane/application.yaml +5 -0
  159. package/templates/applications/dataplane/env.template +26 -13
  160. package/templates/applications/dataplane/rbac.yaml +108 -1
  161. package/templates/applications/miso-controller/application.yaml +2 -2
  162. package/templates/applications/miso-controller/env.template +37 -122
  163. package/templates/applications/miso-controller/rbac.yaml +28 -13
  164. package/templates/channels/chatgpt/install.template.md.hbs +49 -0
  165. package/templates/channels/chatgpt/manifest.template.json.hbs +9 -0
  166. package/templates/channels/microsoft-copilot/install.template.md.hbs +49 -0
  167. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +8 -0
  168. package/templates/channels/slack/install.template.md.hbs +49 -0
  169. package/templates/channels/slack/manifest.template.json.hbs +66 -0
  170. package/templates/channels/teams-bot/install.template.md.hbs +49 -0
  171. package/templates/channels/teams-bot/manifest.template.json.hbs +13 -0
  172. package/templates/channels/web-embed/install.template.md.hbs +49 -0
  173. package/templates/channels/web-embed/manifest.template.json.hbs +8 -0
  174. package/templates/python/docker-compose.hbs +56 -0
  175. package/templates/typescript/docker-compose.hbs +52 -0
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Channel install probe checks (plan 152).
3
+ *
4
+ * @fileoverview Callback, assistant catalog, and inbound verification probes
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { ApiClient } = require('../api/index');
12
+ const { fetchDataplaneGeneralHealth } = require('../api/dataplane-health.api');
13
+
14
+ /**
15
+ * @async
16
+ * @param {string} url
17
+ * @returns {Promise<boolean>}
18
+ */
19
+ async function isUrlReachable(url) {
20
+ if (!url) return false;
21
+ const client = new ApiClient(url.split('/api/')[0] || url);
22
+ try {
23
+ const pathPart = url.includes('/api/') ? url.substring(url.indexOf('/api/')) : '/health';
24
+ const res = await client.get(pathPart, { timeoutMs: 8000 });
25
+ return res && res.success !== false;
26
+ } catch {
27
+ return false;
28
+ }
29
+ }
30
+
31
+ /**
32
+ * @async
33
+ * @param {string} dataplaneUrl
34
+ * @param {Object} authConfig
35
+ * @returns {Promise<boolean>}
36
+ */
37
+ async function isAssistantCatalogReachable(dataplaneUrl, authConfig) {
38
+ const client = require('../api/index').createDataplaneApiClient(dataplaneUrl, authConfig);
39
+ try {
40
+ const res = await client.get('/api/v1/workers/available', { timeoutMs: 10000 });
41
+ return res && res.success !== false;
42
+ } catch {
43
+ return false;
44
+ }
45
+ }
46
+
47
+ /**
48
+ * @param {Object|null} channelExt
49
+ * @returns {Object}
50
+ */
51
+ function evaluateInboundVerification(channelExt) {
52
+ const credentialKey = channelExt?.credentialKey || '';
53
+ return {
54
+ slackSigningSecretConfigured: credentialKey.includes('slack'),
55
+ teamsBotOidcValidationConfigured: credentialKey.includes('teams-bot'),
56
+ issuerAudienceConfigured: Boolean(channelExt?.type),
57
+ replayProtectionConfigured: Boolean(channelExt?.status === 'installed' || channelExt?.status === 'validated')
58
+ };
59
+ }
60
+
61
+ /**
62
+ * @async
63
+ * @param {Object} ctx
64
+ * @param {Object|null} channelExt
65
+ * @returns {Promise<Object>}
66
+ */
67
+ async function runChannelProbe(ctx, channelExt) {
68
+ const health = await fetchDataplaneGeneralHealth(ctx.dataplaneUrl, { timeoutMs: 8000 });
69
+ const catalogOk = await isAssistantCatalogReachable(ctx.dataplaneUrl, ctx.authConfig);
70
+ const callbackOk = health?.status === 'healthy' || health?.status === 'ok';
71
+
72
+ const inbound = evaluateInboundVerification(channelExt);
73
+
74
+ return {
75
+ dataplaneHealth: health?.status || 'unknown',
76
+ callbackReachable: callbackOk,
77
+ assistantCatalogReachable: catalogOk,
78
+ credentialValid: Boolean(channelExt?.credentialKey),
79
+ inboundVerification: inbound,
80
+ passed: callbackOk && catalogOk && Boolean(channelExt?.credentialKey)
81
+ };
82
+ }
83
+
84
+ module.exports = {
85
+ isUrlReachable,
86
+ isAssistantCatalogReachable,
87
+ evaluateInboundVerification,
88
+ runChannelProbe
89
+ };
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Resolve runtime URLs for channel templates (no hardcoded env URLs).
3
+ *
4
+ * @fileoverview Channel runtime value resolution (plan 152)
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { getChannelTarget } = require('./channel-targets');
12
+
13
+ /**
14
+ * Normalize dataplane base URL (no trailing slash).
15
+ * @param {string} dataplaneBaseUrl
16
+ * @returns {string}
17
+ */
18
+ function normalizeBaseUrl(dataplaneBaseUrl) {
19
+ if (!dataplaneBaseUrl || typeof dataplaneBaseUrl !== 'string') {
20
+ throw new Error('dataplaneBaseUrl is required');
21
+ }
22
+ return dataplaneBaseUrl.trim().replace(/\/+$/, '');
23
+ }
24
+
25
+ /**
26
+ * @param {string} base
27
+ * @param {string} path
28
+ * @returns {string}
29
+ */
30
+ function joinUrl(base, path) {
31
+ return `${normalizeBaseUrl(base)}${path.startsWith('/') ? path : `/${path}`}`;
32
+ }
33
+
34
+ /**
35
+ * Dataplane external-system route prefix for webhook URLs.
36
+ * @param {string} routeSystemKey
37
+ * @returns {string}
38
+ */
39
+ function externalSystemsPrefix(routeSystemKey) {
40
+ return `/api/v1/external/systems/${routeSystemKey}`;
41
+ }
42
+
43
+ /**
44
+ * Relative endpoint paths per channel target (under external/systems/{routeSystemKey}).
45
+ * @param {string} channelTarget
46
+ * @returns {Record<string, string>}
47
+ */
48
+ function getEndpointPathSuffixes(channelTarget) {
49
+ const maps = {
50
+ slack: {
51
+ eventsUrl: '/events',
52
+ interactionUrl: '/interactive',
53
+ oauthCallbackUrl: '/oauth/callback',
54
+ commandUrl: '/commands'
55
+ },
56
+ 'teams-bot': {
57
+ messagesUrl: '/messages',
58
+ notificationsUrl: '/notifications',
59
+ botConfigUrl: '/bot-config'
60
+ },
61
+ 'microsoft-copilot': {
62
+ openapiUrl: '/openapi.yaml',
63
+ actionsBaseUrl: '/actions'
64
+ },
65
+ chatgpt: {
66
+ openapiUrl: '/openapi.yaml',
67
+ mcpUrl: '/mcp',
68
+ actionsBaseUrl: '/actions'
69
+ },
70
+ 'web-embed': {
71
+ configUrl: '/config'
72
+ }
73
+ };
74
+ return maps[channelTarget] || {};
75
+ }
76
+
77
+ /**
78
+ * Legacy / non-external-system paths (Copilot/ChatGPT until dataplane routes exist).
79
+ * @param {string} channelTarget
80
+ * @returns {Record<string, string>}
81
+ */
82
+ function getLegacyApiPaths(channelTarget) {
83
+ const legacy = {
84
+ 'microsoft-copilot': {
85
+ openapiUrl: '/api/v1/copilot/openapi.yaml',
86
+ actionsBaseUrl: '/api/v1/copilot/actions'
87
+ },
88
+ chatgpt: {
89
+ openapiUrl: '/api/v1/chatgpt/openapi.yaml',
90
+ mcpUrl: '/api/v1/chatgpt/mcp',
91
+ actionsBaseUrl: '/api/v1/chatgpt/actions'
92
+ },
93
+ 'web-embed': {
94
+ configUrl: '/api/v1/web-embed/config'
95
+ }
96
+ };
97
+ return legacy[channelTarget] || {};
98
+ }
99
+
100
+ /**
101
+ * @param {Object} params
102
+ * @param {string} params.dataplaneBaseUrl
103
+ * @param {string} params.channelTarget
104
+ * @param {string} params.appName
105
+ * @param {string} [params.baseName]
106
+ * @param {string} [params.environment]
107
+ * @param {string} params.appDescription
108
+ * @param {string} [params.icon]
109
+ * @returns {Object}
110
+ */
111
+ function resolveChannelRuntimeValues(params) {
112
+ const target = getChannelTarget(params.channelTarget);
113
+ const base = normalizeBaseUrl(params.dataplaneBaseUrl);
114
+ const routeKey = target.routeSystemKey || target.key;
115
+ const systemPrefix = externalSystemsPrefix(routeKey);
116
+ const suffixes = getEndpointPathSuffixes(params.channelTarget);
117
+ const legacy = getLegacyApiPaths(params.channelTarget);
118
+
119
+ const resolved = {
120
+ dataplaneBaseUrl: base,
121
+ channelTarget: params.channelTarget,
122
+ routeSystemKey: routeKey,
123
+ appName: params.appName,
124
+ baseName: params.baseName || params.appName,
125
+ environment: params.environment || 'dev',
126
+ appDescription: params.appDescription,
127
+ icon: params.icon || '',
128
+ credentialKey: target.credentialKey,
129
+ assistantCatalogUrl: joinUrl(base, '/api/v1/workers/available'),
130
+ scopes: []
131
+ };
132
+
133
+ for (const [key, suffix] of Object.entries(suffixes)) {
134
+ resolved[key] = joinUrl(base, `${systemPrefix}${suffix}`);
135
+ }
136
+
137
+ for (const [key, legacyPath] of Object.entries(legacy)) {
138
+ if (!resolved[key]) {
139
+ resolved[key] = joinUrl(base, legacyPath);
140
+ }
141
+ }
142
+
143
+ return resolved;
144
+ }
145
+
146
+ module.exports = {
147
+ normalizeBaseUrl,
148
+ joinUrl,
149
+ externalSystemsPrefix,
150
+ getEndpointPathSuffixes,
151
+ resolveChannelRuntimeValues
152
+ };
@@ -0,0 +1,141 @@
1
+ /**
2
+ * TTY output for app add-channel (plan 152).
3
+ *
4
+ * @fileoverview Channel install status display — customer language: Assistant
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const chalk = require('chalk');
12
+ const logger = require('../utils/logger');
13
+ const {
14
+ formatSuccessLine,
15
+ formatWarningLine,
16
+ formatBlockingError,
17
+ sectionTitle,
18
+ metadata
19
+ } = require('../utils/cli-test-layout-chalk');
20
+
21
+ /** @returns {string} */
22
+ function separatorLine() {
23
+ const gray = chalk.gray;
24
+ return typeof gray === 'function'
25
+ ? gray('────────────────────────────────────────')
26
+ : '────────────────────────────────────────';
27
+ }
28
+
29
+ /**
30
+ * @param {Object} params
31
+ * @param {string} params.channelTarget
32
+ * @param {string} params.name
33
+ * @param {Object|null} params.channelExt
34
+ * @param {string} [params.approvalUrl]
35
+ */
36
+ function displayExistingChannelStatus(params) {
37
+ logger.log(sectionTitle(`${params.channelTarget} channel: ${params.name}`));
38
+ logger.log('');
39
+ logger.log(metadata(`Status: ${params.channelExt?.status || 'unknown'}`));
40
+ if (params.channelExt?.baseName && params.channelExt.baseName !== params.name) {
41
+ logger.log(metadata(`Base name: ${params.channelExt.baseName}`));
42
+ }
43
+ logger.log(metadata(`External System: ${params.channelTarget}`));
44
+ logger.log(metadata(`Credential: ${params.channelExt?.credentialKey || '—'}`));
45
+ if (params.channelExt?.capabilities?.length) {
46
+ logger.log(metadata('Capabilities:'));
47
+ for (const cap of params.channelExt.capabilities) {
48
+ logger.log(` - ${cap}`);
49
+ }
50
+ }
51
+ logger.log('');
52
+ logger.log(formatWarningLine('Channel install already configured. Use --force to regenerate artifacts.'));
53
+ if (params.approvalUrl) {
54
+ logger.log(metadata(`Approval URL: ${params.approvalUrl}`));
55
+ }
56
+ logger.log(separatorLine());
57
+ }
58
+
59
+ /**
60
+ * @param {Object} result
61
+ */
62
+ function displayChannelCoreFields(result) {
63
+ logger.log(sectionTitle(`${result.channelTarget} channel: ${result.name}`));
64
+ logger.log('');
65
+ logger.log(metadata(`Status: ${result.statusLabel}`));
66
+ if (result.baseName && result.baseName !== result.name) {
67
+ logger.log(metadata(`Base name: ${result.baseName}`));
68
+ }
69
+ logger.log(metadata(`External System: ${result.channelTarget}`));
70
+ logger.log(metadata(`Credential: ${result.credentialKey}`));
71
+ if (result.capabilities?.length) {
72
+ logger.log(metadata('Capabilities:'));
73
+ for (const cap of result.capabilities) {
74
+ logger.log(` - ${cap}`);
75
+ }
76
+ }
77
+ if (result.artifactPath) {
78
+ logger.log(metadata(`Artifact: ${result.artifactPath}`));
79
+ }
80
+ if (result.installGuidePath) {
81
+ logger.log(metadata(`Install guide: ${result.installGuidePath}`));
82
+ }
83
+ }
84
+
85
+ /**
86
+ * @param {Object|null} validation
87
+ */
88
+ function displayValidationSection(validation) {
89
+ if (!validation) return;
90
+ logger.log('');
91
+ logger.log(metadata('Validation:'));
92
+ if (validation.callbackReachable) {
93
+ logger.log(formatSuccessLine('Callback reachable'));
94
+ }
95
+ if (validation.credentialValid) {
96
+ logger.log(formatSuccessLine('Credential valid'));
97
+ }
98
+ if (validation.assistantCatalogReachable) {
99
+ logger.log(formatSuccessLine('Assistant catalog reachable'));
100
+ }
101
+ if (validation.inboundVerification) {
102
+ logger.log(formatSuccessLine('Inbound verification configured'));
103
+ }
104
+ }
105
+
106
+ /**
107
+ * @param {Object} result
108
+ */
109
+ function displayApprovalSection(result) {
110
+ if (!result.approvalUrl) return;
111
+ logger.log('');
112
+ logger.log(formatWarningLine('Approval required'));
113
+ logger.log(metadata('Open this URL as platform admin:'));
114
+ logger.log(chalk.cyan(result.approvalUrl));
115
+ logger.log('');
116
+ logger.log(metadata('After approval, run:'));
117
+ logger.log(chalk.white(` aifabrix app add-channel ${result.channelTarget} --name "${result.name}"`));
118
+ }
119
+
120
+ /**
121
+ * @param {Object} result
122
+ */
123
+ function displayChannelInstallResult(result) {
124
+ displayChannelCoreFields(result);
125
+ displayValidationSection(result.validation);
126
+ displayApprovalSection(result);
127
+ logger.log(separatorLine());
128
+ }
129
+
130
+ /**
131
+ * @param {string} message
132
+ */
133
+ function displayChannelError(message) {
134
+ logger.error(formatBlockingError(message));
135
+ }
136
+
137
+ module.exports = {
138
+ displayExistingChannelStatus,
139
+ displayChannelInstallResult,
140
+ displayChannelError
141
+ };
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Channel target registry for assistant communication channels (plan 152).
3
+ *
4
+ * @fileoverview Channel install targets — templates, capabilities, approval semantics
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /** @typedef {'teams-bot'|'slack'|'microsoft-copilot'|'chatgpt'|'web-embed'} ChannelTargetKey */
12
+
13
+ const DEFAULT_CAPABILITIES = ['chat', 'notify'];
14
+
15
+ const CHANNEL_TARGETS = {
16
+ 'teams-bot': {
17
+ key: 'teams-bot',
18
+ channelType: 'teamsBot',
19
+ templateDir: 'teams-bot',
20
+ artifactFileName: 'channel-teams-bot.json',
21
+ artifactFormat: 'json',
22
+ credentialKey: 'teams-bot',
23
+ routeSystemKey: 'teams',
24
+ capabilities: DEFAULT_CAPABILITIES,
25
+ approvalRequired: true,
26
+ experimental: false
27
+ },
28
+ slack: {
29
+ key: 'slack',
30
+ channelType: 'slack',
31
+ templateDir: 'slack',
32
+ artifactFileName: 'channel-slack.json',
33
+ artifactFormat: 'json',
34
+ credentialKey: 'slack-app',
35
+ routeSystemKey: 'slack',
36
+ capabilities: DEFAULT_CAPABILITIES,
37
+ approvalRequired: true,
38
+ experimental: false
39
+ },
40
+ 'microsoft-copilot': {
41
+ key: 'microsoft-copilot',
42
+ channelType: 'microsoftCopilot',
43
+ templateDir: 'microsoft-copilot',
44
+ artifactFileName: 'channel-microsoft-copilot.json',
45
+ artifactFormat: 'json',
46
+ credentialKey: 'microsoft-copilot-channel',
47
+ capabilities: [...DEFAULT_CAPABILITIES, 'enterpriseActions'],
48
+ approvalRequired: true,
49
+ experimental: false
50
+ },
51
+ chatgpt: {
52
+ key: 'chatgpt',
53
+ channelType: 'chatgpt',
54
+ templateDir: 'chatgpt',
55
+ artifactFileName: 'channel-chatgpt.json',
56
+ artifactFormat: 'json',
57
+ credentialKey: 'chatgpt-channel',
58
+ capabilities: ['chat', 'enterpriseActions'],
59
+ approvalRequired: true,
60
+ experimental: false
61
+ },
62
+ 'web-embed': {
63
+ key: 'web-embed',
64
+ channelType: 'webEmbed',
65
+ templateDir: 'web-embed',
66
+ artifactFileName: 'channel-web-embed.json',
67
+ artifactFormat: 'json',
68
+ credentialKey: 'web-embed-channel',
69
+ capabilities: DEFAULT_CAPABILITIES,
70
+ approvalRequired: false,
71
+ experimental: true
72
+ }
73
+ };
74
+
75
+ const CHANNEL_TARGET_KEYS = Object.keys(CHANNEL_TARGETS);
76
+
77
+ /**
78
+ * @param {string} channelTarget
79
+ * @returns {boolean}
80
+ */
81
+ function isChannelTargetKey(channelTarget) {
82
+ return typeof channelTarget === 'string' && CHANNEL_TARGET_KEYS.includes(channelTarget);
83
+ }
84
+
85
+ /**
86
+ * @param {string} channelTarget
87
+ * @returns {typeof CHANNEL_TARGETS[string]}
88
+ */
89
+ function getChannelTarget(channelTarget) {
90
+ if (!isChannelTargetKey(channelTarget)) {
91
+ throw new Error(
92
+ `Unknown channel target "${channelTarget}". Expected one of: ${CHANNEL_TARGET_KEYS.join(', ')}`
93
+ );
94
+ }
95
+ return CHANNEL_TARGETS[channelTarget];
96
+ }
97
+
98
+ /**
99
+ * Default customer-facing description when --description is omitted.
100
+ * @returns {string}
101
+ */
102
+ function getDefaultChannelDescription() {
103
+ return (
104
+ 'Enterprise AI assistant for employees. Provides access to approved assistants, ' +
105
+ 'enterprise knowledge, business workflows, and governed actions through AI Fabrix.'
106
+ );
107
+ }
108
+
109
+ module.exports = {
110
+ CHANNEL_TARGETS,
111
+ CHANNEL_TARGET_KEYS,
112
+ isChannelTargetKey,
113
+ getChannelTarget,
114
+ getDefaultChannelDescription
115
+ };
package/lib/cli/index.js CHANGED
@@ -22,6 +22,7 @@ const { setupExternalSystemCommands } = require('./setup-external-system');
22
22
  const { setupAppCommands: setupAppManagementCommands } = require('../commands/app');
23
23
  const { setupDatasourceCommands } = require('../commands/datasource');
24
24
  const { setupDimensionCommands } = require('../commands/dimension');
25
+ const { setupResourceTypeCommands } = require('../commands/resource-type');
25
26
  const { setupIdentityCommands } = require('../commands/identity');
26
27
  const { setupProtectionCommands } = require('../commands/protection');
27
28
  const { setupCredentialDeploymentCommands } = require('./setup-credential-deployment');
@@ -29,6 +30,18 @@ const { setupIntegrationClientCommands } = require('./setup-integration-client')
29
30
  const { setupPlatformCommands } = require('./setup-platform');
30
31
  const { setupCertificationCommands } = require('./setup-certification-commands');
31
32
  const { setupGovernanceCommands } = require('../commands/governance');
33
+ const { setupChannelCommands } = require('../commands/channel');
34
+
35
+ /**
36
+ * Dev, secrets, parameters, and platform commands (keeps setupCommands under statement limit).
37
+ * @param {import('commander').Command} program
38
+ */
39
+ function setupDevAndPlatformCommands(program) {
40
+ setupDevCommands(program);
41
+ setupSecretsCommands(program);
42
+ setupParametersCommands(program);
43
+ setupPlatformCommands(program);
44
+ }
32
45
 
33
46
  /**
34
47
  * Sets up all CLI commands on the Commander program instance
@@ -42,18 +55,17 @@ function setupCommands(program) {
42
55
  setupGovernanceCommands(program);
43
56
  setupEnvironmentCommands(program);
44
57
  setupAppManagementCommands(program);
58
+ setupChannelCommands(program);
45
59
  setupDatasourceCommands(program);
46
60
  setupDimensionCommands(program);
61
+ setupResourceTypeCommands(program);
47
62
  setupIdentityCommands(program);
48
63
  setupProtectionCommands(program);
49
64
  setupUtilityCommands(program);
50
65
  setupCredentialDeploymentCommands(program);
51
66
  setupIntegrationClientCommands(program);
52
67
  setupExternalSystemCommands(program);
53
- setupDevCommands(program);
54
- setupSecretsCommands(program);
55
- setupParametersCommands(program);
56
- setupPlatformCommands(program);
68
+ setupDevAndPlatformCommands(program);
57
69
  }
58
70
 
59
71
  module.exports = {
@@ -112,6 +112,49 @@ function normalizeExternalOptions(options) {
112
112
  return normalized;
113
113
  }
114
114
 
115
+ /**
116
+ * Whether normalized options include all external CLI fields for headless create.
117
+ * @param {Object} normalized - Normalized external create options
118
+ * @returns {boolean}
119
+ */
120
+ function hasFullExternalCliOptions(normalized) {
121
+ return Boolean(
122
+ normalized.systemKey &&
123
+ normalized.systemDisplayName &&
124
+ normalized.systemDescription &&
125
+ normalized.systemType &&
126
+ normalized.authType &&
127
+ normalized.entityType &&
128
+ normalized.datasourceCount
129
+ );
130
+ }
131
+
132
+ /**
133
+ * Map positional systemKey and apply headless defaults for external create.
134
+ * @param {string} appName - Positional appKey or systemKey from CLI
135
+ * @param {Object} options - Raw CLI options
136
+ * @returns {Object} Normalized options ready for createApp
137
+ */
138
+ function prepareExternalCreateOptions(appName, options) {
139
+ const normalized = normalizeExternalOptions(options);
140
+ const isExternalType = options.type === 'external' || !options.type;
141
+
142
+ if (isExternalType && appName) {
143
+ normalized.systemKey = appName;
144
+ }
145
+
146
+ if (isExternalType && hasFullExternalCliOptions(normalized)) {
147
+ if (!Object.prototype.hasOwnProperty.call(normalized, 'github')) {
148
+ normalized.github = false;
149
+ }
150
+ if (!Object.prototype.hasOwnProperty.call(normalized, 'controller')) {
151
+ normalized.controller = false;
152
+ }
153
+ }
154
+
155
+ return normalized;
156
+ }
157
+
115
158
  /**
116
159
  * Validate required options for non-interactive external creation
117
160
  * @param {Object} normalizedOptions - Normalized options
@@ -149,7 +192,7 @@ async function handleCreateCommand(appName, options) {
149
192
  }
150
193
 
151
194
  const wizardOptions = { app: appName, ...options };
152
- const normalizedOptions = normalizeExternalOptions(options);
195
+ const normalizedOptions = prepareExternalCreateOptions(appName, options);
153
196
 
154
197
  const isExternalType = options.type === 'external' || !options.type;
155
198
  const isNonInteractive = process.stdin && process.stdin.isTTY === false;
@@ -279,7 +322,10 @@ function setupBuildRunLogsDownCommands(program) {
279
322
  .option('-f, --force-template', 'Force rebuild from template')
280
323
  .option('--no-cache', 'Full Docker rebuild (disable layer cache); use after Dockerfile or context fixes')
281
324
  .option('-t, --tag <tag>', 'Image tag (default: latest). Set image.tag in application.yaml to match for deploy.')
282
- .option('--base', 'Also tag the manifest base image name (after developer-scoped build when developer id > 0)')
325
+ .option(
326
+ '--base',
327
+ 'When manifest omits image: also tag the base repository after a developer-scoped build (ignored when application.yaml defines image)'
328
+ )
283
329
  .action(async(appName, options) => {
284
330
  try {
285
331
  const imageTag = await app.buildApp(appName, options);
@@ -437,4 +483,9 @@ function setupAppCommands(program) {
437
483
  setupPushDeployDockerfileCommands(program);
438
484
  }
439
485
 
440
- module.exports = { setupAppCommands };
486
+ module.exports = {
487
+ setupAppCommands,
488
+ normalizeExternalOptions,
489
+ prepareExternalCreateOptions,
490
+ validateNonInteractiveExternalOptions
491
+ };
@@ -15,7 +15,7 @@ const { handleCommandError, isAuthenticationError } = require('../utils/cli-util
15
15
  const { resolveControllerUrl } = require('../utils/controller-url');
16
16
  const { handleLogin } = require('../commands/login');
17
17
  const { handleUpDataplane } = require('../commands/up-dataplane');
18
- const { cleanBuilderAppDirs } = require('../commands/up-common');
18
+ const { cleanBuilderAppDirs, DATAPLANE_BUILDER_APP_KEYS } = require('../commands/up-common');
19
19
  const { runGuidedUpDataplane } = require('./infra-guided');
20
20
  const { recordInfraInstallationCommand } = require('./installation-log-command');
21
21
 
@@ -26,7 +26,7 @@ const { recordInfraInstallationCommand } = require('./installation-log-command')
26
26
  async function runUpDataplaneWork(options) {
27
27
  let cleanedAppKeys = [];
28
28
  if (options.force) {
29
- const c = await cleanBuilderAppDirs(['dataplane']);
29
+ const c = await cleanBuilderAppDirs([...DATAPLANE_BUILDER_APP_KEYS]);
30
30
  cleanedAppKeys = Array.isArray(c) ? c : [];
31
31
  }
32
32
  if (!options.verbose) {
@@ -19,6 +19,7 @@ const { handleUpDataplane } = require('../commands/up-dataplane');
19
19
  const {
20
20
  applyUpPlatformForceConfig,
21
21
  cleanBuilderAppDirs,
22
+ PLATFORM_BUILDER_CLEAN_APP_KEYS,
22
23
  prepareUrlsLocalRegistryForUpPlatform
23
24
  } = require('../commands/up-common');
24
25
  const { runGuidedUpPlatform } = require('./infra-guided');
@@ -35,7 +36,7 @@ async function runUpPlatformWork(options) {
35
36
  let platformForceCleanSummary = null;
36
37
  if (options.force) {
37
38
  const forceSummary = await applyUpPlatformForceConfig({ silent: !options.verbose });
38
- const cleanedApps = await cleanBuilderAppDirs(PLATFORM_APPS, {
39
+ const cleanedApps = await cleanBuilderAppDirs(PLATFORM_BUILDER_CLEAN_APP_KEYS, {
39
40
  silent: !options.verbose
40
41
  });
41
42
  cleanedAppKeys = Array.isArray(cleanedApps) ? cleanedApps : [];
@@ -54,7 +54,7 @@ function registerSetup(program) {
54
54
  .command('setup')
55
55
  .description('Install or refresh the full AI Fabrix platform (infra + miso + dataplane)')
56
56
  .option('-d, --developer <id>', 'Pin developer ID before fresh install (fresh path only; ignored when infra is already up)')
57
- .option('--platform <mode>', 'Platform mode: single (direct ports) or full (Traefik front door + multi-env)')
57
+ .option('--platform <mode>', 'Platform mode: single, single-traefik (Traefik + TLS), or full (multi-env; advanced)')
58
58
  .option('--installation <profile>', 'Installation profile: dev (one password) or pro (autogen/manual)')
59
59
  .option('--pro-password-mode <mode>', 'Pro profile only: autogen or manual')
60
60
  .option('--admin-email <email>', 'Admin email (non-interactive fresh install)')
@@ -46,8 +46,11 @@ Examples:
46
46
  $ aifabrix resolve hubspot-demo
47
47
  External integration: resolves integration/<systemKey>/env.template to .env using local host ports (no Docker rewrite).
48
48
 
49
+ $ aifabrix resolve dataplane --force
50
+ Seed missing secret keys and replace the full .env from env.template (no merge with an existing file).
51
+
49
52
  $ aifabrix resolve dataplane --fresh
50
- Regenerate the full .env from env.template (no merge with an existing file). Use after adding many new variables or when a normal resolve did not pick up new template lines.
53
+ Replace the full .env only (no merge; does not seed missing secret keys). Same .env output as --force when secrets are already set.
51
54
 
52
55
  $ aifabrix secret set hubspot-demo/token "<your-token>"
53
56
  Store a secret when resolve reports a missing kv:// reference.