@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,371 @@
1
+ /**
2
+ * Resource type catalog commands (Controller).
3
+ *
4
+ * Commands:
5
+ * - aifabrix resource-type create
6
+ * - aifabrix resource-type import
7
+ * - aifabrix resource-type get
8
+ * - aifabrix resource-type list
9
+ *
10
+ * @fileoverview Resource type CLI commands
11
+ * @author AI Fabrix Team
12
+ * @version 1.0.0
13
+ */
14
+
15
+ 'use strict';
16
+
17
+ const chalk = require('chalk');
18
+ const logger = require('../utils/logger');
19
+ const { resolveControllerUrl } = require('../utils/controller-url');
20
+ const { normalizeControllerUrl, resolveEnvironment } = require('../core/config');
21
+ const { getOrRefreshDeviceToken } = require('../utils/token-manager');
22
+ const {
23
+ readResourceTypeCreateFile,
24
+ readResourceTypeCatalogFile
25
+ } = require('../resolvers/resource-type-file');
26
+ const {
27
+ formatBlockingError,
28
+ formatSuccessLine,
29
+ infoLine,
30
+ headerKeyValue
31
+ } = require('../utils/cli-layout-chalk');
32
+ const {
33
+ listResourceTypes,
34
+ getResourceType,
35
+ createResourceTypeIdempotent
36
+ } = require('../api/resource-types.api');
37
+
38
+ const RESOURCE_TYPE_KEY_PATTERN = /^[a-z][a-z0-9-]*$/;
39
+
40
+ /** Parent help for `aifabrix resource-type -h`. */
41
+ const RESOURCE_TYPE_HELP_AFTER = `
42
+
43
+ Examples:
44
+ $ aifabrix resource-type create --key customer --display-name "Customer"
45
+ $ aifabrix resource-type get customer
46
+ $ aifabrix resource-type list --search customer
47
+
48
+ Per-command help: aifabrix resource-type <command> --help
49
+ `;
50
+
51
+ const RESOURCE_TYPE_IMPORT_HELP_AFTER = `
52
+
53
+ Examples:
54
+ $ aifabrix resource-type import --file ./baseline-resourcetypes.json
55
+ $ aifabrix resource-type import --file /path/to/catalog.json
56
+
57
+ `;
58
+
59
+ const RESOURCE_TYPE_CREATE_HELP_AFTER = `
60
+
61
+ Examples:
62
+ $ aifabrix resource-type create --key customer --display-name "Customer"
63
+ $ aifabrix resource-type create --key deal --display-name "Deal" --category business --default-risk-level medium
64
+ $ aifabrix resource-type create --file ./customer.json
65
+
66
+ `;
67
+
68
+ const RESOURCE_TYPE_GET_HELP_AFTER = `
69
+
70
+ Examples:
71
+ $ aifabrix resource-type get customer
72
+ $ aifabrix resource-type get clx1234567890abcdef
73
+
74
+ `;
75
+
76
+ const RESOURCE_TYPE_LIST_HELP_AFTER = `
77
+
78
+ Examples:
79
+ $ aifabrix resource-type list
80
+ $ aifabrix resource-type list --page 1 --page-size 50
81
+ $ aifabrix resource-type list --search customer
82
+
83
+ `;
84
+
85
+ /**
86
+ * @param {string} raw
87
+ * @returns {string}
88
+ */
89
+ function requireResourceTypeKey(raw) {
90
+ const key = String(raw || '').trim();
91
+ if (!key) {
92
+ throw new Error('Resource type key is required (--key <key> or --file <path>).');
93
+ }
94
+ if (!RESOURCE_TYPE_KEY_PATTERN.test(key)) {
95
+ throw new Error(
96
+ 'Resource type key must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens.'
97
+ );
98
+ }
99
+ return key;
100
+ }
101
+
102
+ /**
103
+ * @param {Object} options
104
+ * @returns {Promise<{ controllerUrl: string, authConfig: Object }>}
105
+ */
106
+ async function resolveControllerAndAuth(_options) {
107
+ const controllerUrl = await resolveControllerUrl();
108
+ if (!controllerUrl) {
109
+ throw new Error('Controller URL is required. Run "aifabrix login" first.');
110
+ }
111
+ const normalized = normalizeControllerUrl(controllerUrl);
112
+ const deviceToken = await getOrRefreshDeviceToken(normalized);
113
+ if (!deviceToken || !deviceToken.token) {
114
+ throw new Error(
115
+ `Not authenticated for controller: ${controllerUrl}. ` +
116
+ 'Run "aifabrix login" and try again.'
117
+ );
118
+ }
119
+ return {
120
+ controllerUrl: deviceToken.controller || normalized,
121
+ authConfig: { type: 'bearer', token: deviceToken.token }
122
+ };
123
+ }
124
+
125
+ async function resolveHeaderContext() {
126
+ const env = await resolveEnvironment();
127
+ const controllerUrl = await resolveControllerUrl();
128
+ const normalized = normalizeControllerUrl(controllerUrl);
129
+ return { environment: env || 'dev', controllerUrl: normalized };
130
+ }
131
+
132
+ /**
133
+ * @param {Object} options
134
+ * @returns {Object}
135
+ */
136
+ function buildCreatePayload(options) {
137
+ let base = {};
138
+ if (options.file) {
139
+ base = readResourceTypeCreateFile(options.file);
140
+ }
141
+ const payload = { ...base };
142
+ if (options.key) payload.key = options.key;
143
+ if (options.displayName) payload.displayName = options.displayName;
144
+ if (options.description !== undefined) payload.description = options.description;
145
+ if (options.category) payload.category = options.category;
146
+ if (options.defaultRiskLevel) payload.defaultRiskLevel = options.defaultRiskLevel;
147
+ if (options.status) payload.status = options.status;
148
+ payload.key = requireResourceTypeKey(payload.key);
149
+ if (!payload.displayName) {
150
+ throw new Error('displayName is required (--display-name <name> or in --file).');
151
+ }
152
+ return payload;
153
+ }
154
+
155
+ /**
156
+ * @param {Object} response
157
+ * @returns {any}
158
+ */
159
+ function unwrapControllerData(response) {
160
+ return response?.data?.data ?? response?.data ?? response;
161
+ }
162
+
163
+ /**
164
+ * @param {import('commander').Command} rt
165
+ */
166
+ /**
167
+ * @param {Object[]} rows
168
+ * @param {string} controllerUrl
169
+ * @param {Object} authConfig
170
+ * @returns {Promise<{ created: number, existed: number, total: number }>}
171
+ */
172
+ async function importResourceTypeRows(rows, controllerUrl, authConfig) {
173
+ let created = 0;
174
+ let existed = 0;
175
+ for (const row of rows) {
176
+ const payload = { ...row };
177
+ payload.key = requireResourceTypeKey(payload.key);
178
+ const out = await createResourceTypeIdempotent(controllerUrl, authConfig, payload);
179
+ if (out.created) {
180
+ created += 1;
181
+ continue;
182
+ }
183
+ existed += 1;
184
+ }
185
+ return { created, existed, total: rows.length };
186
+ }
187
+
188
+ function setupResourceTypeImportCommand(rt) {
189
+ rt
190
+ .command('import')
191
+ .description('Import resource types from baseline catalog JSON (idempotent)')
192
+ .addHelpText('after', RESOURCE_TYPE_IMPORT_HELP_AFTER)
193
+ .requiredOption('--file <path>', 'Baseline catalog (resourceTypes[]) or JSON array')
194
+ .action(async(options) => {
195
+ try {
196
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth(options);
197
+ const rows = readResourceTypeCatalogFile(options.file);
198
+ const summary = await importResourceTypeRows(rows, controllerUrl, authConfig);
199
+ logger.log(
200
+ formatSuccessLine(
201
+ `Resource types imported: ${summary.total} total ` +
202
+ `(${summary.created} created, ${summary.existed} already existed)`
203
+ )
204
+ );
205
+ } catch (e) {
206
+ logger.error(formatBlockingError(e.message));
207
+ process.exit(1);
208
+ }
209
+ });
210
+ }
211
+
212
+ function setupResourceTypeCreateCommand(rt) {
213
+ rt
214
+ .command('create')
215
+ .description('Create resource type (idempotent; success if it already exists)')
216
+ .addHelpText('after', RESOURCE_TYPE_CREATE_HELP_AFTER)
217
+ .option('--file <path>', 'Read resource type create payload from JSON file')
218
+ .option('--key <key>', 'Catalog key (lowercase letters, digits, hyphens)')
219
+ .option('--display-name <name>', 'Display name')
220
+ .option('--description <text>', 'Description')
221
+ .option(
222
+ '--category <category>',
223
+ 'business | collaboration | governance | content | communication | people'
224
+ )
225
+ .option('--default-risk-level <level>', 'low | medium | high | critical')
226
+ .option('--status <status>', 'draft | active | deprecated | retired')
227
+ .action(async(options) => {
228
+ try {
229
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth(options);
230
+ const payload = buildCreatePayload(options);
231
+ const out = await createResourceTypeIdempotent(controllerUrl, authConfig, payload);
232
+ const row = unwrapControllerData(out.response);
233
+ const category = row.category || payload.category || 'business';
234
+ const risk = row.defaultRiskLevel || payload.defaultRiskLevel || 'medium';
235
+ if (out.created) {
236
+ logger.log(
237
+ formatSuccessLine(`Resource type created: ${row.key} (category: ${category}, risk: ${risk})`)
238
+ );
239
+ return;
240
+ }
241
+ logger.log(formatSuccessLine(`Resource type exists: ${row.key} (category: ${category}, risk: ${risk})`));
242
+ logger.log(infoLine('No changes were needed.'));
243
+ } catch (e) {
244
+ logger.error(formatBlockingError(e.message));
245
+ process.exit(1);
246
+ }
247
+ });
248
+ }
249
+
250
+ function setupResourceTypeGetCommand(rt) {
251
+ rt
252
+ .command('get <resourceTypeIdOrKey>')
253
+ .description('Get resource type by id or key')
254
+ .addHelpText('after', RESOURCE_TYPE_GET_HELP_AFTER)
255
+ .action(async(resourceTypeIdOrKey, options) => {
256
+ try {
257
+ const { environment, controllerUrl: headerControllerUrl } = await resolveHeaderContext();
258
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth(options);
259
+ const res = await getResourceType(controllerUrl, authConfig, String(resourceTypeIdOrKey).trim());
260
+ const row = unwrapControllerData(res);
261
+ logger.log(
262
+ chalk.bold(`\n🏷️ Resource type in ${environment} environment (${headerControllerUrl}):\n`)
263
+ );
264
+ logger.log(headerKeyValue('Key:', row.key || '—'));
265
+ logger.log(headerKeyValue('Display:', row.displayName || '—'));
266
+ logger.log(headerKeyValue('Category:', row.category || '—'));
267
+ logger.log(headerKeyValue('Default risk:', row.defaultRiskLevel || '—'));
268
+ logger.log(headerKeyValue('Status:', row.status || '—'));
269
+ if (row.description) {
270
+ logger.log(headerKeyValue('Description:', row.description));
271
+ }
272
+ logger.log('');
273
+ } catch (e) {
274
+ logger.error(formatBlockingError(e.message));
275
+ process.exit(1);
276
+ }
277
+ });
278
+ }
279
+
280
+ /**
281
+ * @param {Array<Object>} items
282
+ * @param {string} environment
283
+ * @param {string} controllerUrl
284
+ */
285
+ function displayResourceTypeList(items, environment, controllerUrl) {
286
+ logger.log(
287
+ chalk.bold(`\n🏷️ Resource types in ${environment} environment (${controllerUrl}):\n`)
288
+ );
289
+ if (!Array.isArray(items) || items.length === 0) {
290
+ logger.log(chalk.gray(' No resource types found.\n'));
291
+ return;
292
+ }
293
+
294
+ const KEY_WIDTH = 22;
295
+ const DISPLAY_WIDTH = 30;
296
+ const CATEGORY_WIDTH = 16;
297
+ const RISK_WIDTH = 10;
298
+ const STATUS_WIDTH = 12;
299
+ const SEP_LEN = KEY_WIDTH + DISPLAY_WIDTH + CATEGORY_WIDTH + RISK_WIDTH + STATUS_WIDTH;
300
+
301
+ const keyCol = 'Key'.padEnd(KEY_WIDTH);
302
+ const displayCol = 'Display'.padEnd(DISPLAY_WIDTH);
303
+ const categoryCol = 'Category'.padEnd(CATEGORY_WIDTH);
304
+ const riskCol = 'Risk'.padEnd(RISK_WIDTH);
305
+ const statusCol = 'Status'.padEnd(STATUS_WIDTH);
306
+ logger.log(chalk.gray(`${keyCol}${displayCol}${categoryCol}${riskCol}${statusCol}`));
307
+ logger.log(chalk.gray('-'.repeat(SEP_LEN)));
308
+
309
+ items.forEach((d) => {
310
+ const key = (d?.key ?? '—').toString().padEnd(KEY_WIDTH);
311
+ const displayName = (d?.displayName ?? '—').toString().padEnd(DISPLAY_WIDTH);
312
+ const category = (d?.category ?? '—').toString().padEnd(CATEGORY_WIDTH);
313
+ const risk = (d?.defaultRiskLevel ?? '—').toString().padEnd(RISK_WIDTH);
314
+ const status = (d?.status ?? '—').toString().padEnd(STATUS_WIDTH);
315
+ logger.log(`${key}${displayName}${category}${risk}${status}`);
316
+ });
317
+ logger.log('');
318
+ }
319
+
320
+ function setupResourceTypeListCommand(rt) {
321
+ rt
322
+ .command('list')
323
+ .description('List resource types')
324
+ .addHelpText('after', RESOURCE_TYPE_LIST_HELP_AFTER)
325
+ .option('--page <n>', 'Page', (v) => parseInt(v, 10))
326
+ .option('--page-size <n>', 'Page size', (v) => parseInt(v, 10))
327
+ .option('--search <text>', 'Search by key/displayName/description')
328
+ .option('--category <category>', 'Filter by category')
329
+ .option('--status <status>', 'Filter by status')
330
+ .action(async(options) => {
331
+ try {
332
+ const { environment, controllerUrl: headerControllerUrl } = await resolveHeaderContext();
333
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth(options);
334
+ const res = await listResourceTypes(controllerUrl, authConfig, {
335
+ page: options.page,
336
+ pageSize: options.pageSize,
337
+ search: options.search,
338
+ category: options.category,
339
+ status: options.status
340
+ });
341
+ const payload = unwrapControllerData(res);
342
+ const items = payload?.data ?? payload ?? [];
343
+ displayResourceTypeList(items, environment, headerControllerUrl);
344
+ } catch (e) {
345
+ logger.error(formatBlockingError(e.message));
346
+ process.exit(1);
347
+ }
348
+ });
349
+ }
350
+
351
+ /**
352
+ * @param {import('commander').Command} program
353
+ */
354
+ function setupResourceTypeCommands(program) {
355
+ const rt = program
356
+ .command('resource-type')
357
+ .description('Manage the Controller Resource Type Catalog (COM)')
358
+ .addHelpText('after', RESOURCE_TYPE_HELP_AFTER);
359
+ setupResourceTypeCreateCommand(rt);
360
+ setupResourceTypeImportCommand(rt);
361
+ setupResourceTypeGetCommand(rt);
362
+ setupResourceTypeListCommand(rt);
363
+ }
364
+
365
+ module.exports = {
366
+ setupResourceTypeCommands,
367
+ requireResourceTypeKey,
368
+ buildCreatePayload,
369
+ unwrapControllerData,
370
+ importResourceTypeRows
371
+ };
@@ -263,7 +263,7 @@ async function runUpPlatform(opts = {}) {
263
263
  if (opts.force === true && opts.skipForceClean !== true) {
264
264
  const cleanedApps =
265
265
  preflight.skipClean !== true
266
- ? await upCommon.cleanBuilderAppDirs(PLATFORM_APPS, { silent: true })
266
+ ? await upCommon.cleanBuilderAppDirs(upCommon.PLATFORM_BUILDER_CLEAN_APP_KEYS, { silent: true })
267
267
  : [];
268
268
  forceCleanSummary = { forceSummary: authCtx.forceSummary, cleanedApps };
269
269
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Setup prompts for platform topology mode (single vs full).
2
+ * Setup prompts for platform topology mode (single vs single-traefik; full via CLI only).
3
3
  *
4
4
  * @fileoverview Setup platform mode prompt + CLI option normalization
5
5
  * @author AI Fabrix Team
@@ -9,31 +9,43 @@
9
9
  'use strict';
10
10
 
11
11
  const inquirer = require('inquirer');
12
- const { normalizeSetupPlatformMode, SETUP_PLATFORM_MODE } = require('../core/setup-platform-mode');
12
+ const {
13
+ normalizeSetupPlatformMode,
14
+ SETUP_PLATFORM_MODE,
15
+ SETUP_PLATFORM_MODE_CLI_VALUES,
16
+ getSetupPlatformModePromptChoices
17
+ } = require('../core/setup-platform-mode');
13
18
 
14
19
  /**
15
20
  * Resolve platform mode from CLI options, if provided.
16
21
  * @param {Object} [options]
17
- * @returns {'single'|'full'|null}
22
+ * @returns {'single'|'single-traefik'|'full'|null}
18
23
  */
19
24
  function resolvePlatformModeFromOptions(options = {}) {
20
25
  const raw = options.platform;
21
26
  if (raw === undefined || raw === null || String(raw).trim() === '') {
22
27
  return null;
23
28
  }
24
- const normalized = normalizeSetupPlatformMode(raw);
25
- // Guard: only accept exact canonical values from CLI; reject unexpected to avoid silent typos.
26
29
  const rawNorm = String(raw).trim().toLowerCase();
27
- if (rawNorm !== 'single' && rawNorm !== 'full') {
28
- throw new Error('--platform must be single or full (example: aifabrix setup --platform full)');
30
+ const isCanonical = SETUP_PLATFORM_MODE_CLI_VALUES.includes(rawNorm);
31
+ const isAlias =
32
+ rawNorm === 'ingress' ||
33
+ rawNorm === 'single-ingress' ||
34
+ rawNorm === 'traefik' ||
35
+ rawNorm === 'all' ||
36
+ rawNorm === 'full-platform' ||
37
+ rawNorm === 'platform-full';
38
+ if (!isCanonical && !isAlias) {
39
+ const allowed = SETUP_PLATFORM_MODE_CLI_VALUES.join(', ');
40
+ throw new Error(`--platform must be one of: ${allowed} (example: aifabrix setup --platform single-traefik)`);
29
41
  }
30
- return normalized;
42
+ return normalizeSetupPlatformMode(raw);
31
43
  }
32
44
 
33
45
  /**
34
46
  * @async
35
47
  * @param {Object} [options]
36
- * @returns {Promise<'single'|'full'>}
48
+ * @returns {Promise<'single'|'single-traefik'|'full'>}
37
49
  */
38
50
  async function promptSetupPlatformMode(options = {}) {
39
51
  const fromCli = resolvePlatformModeFromOptions(options);
@@ -44,16 +56,7 @@ async function promptSetupPlatformMode(options = {}) {
44
56
  type: 'list',
45
57
  name: 'mode',
46
58
  message: 'Platform mode:',
47
- choices: [
48
- {
49
- name: 'Single environment — direct ports (no Traefik front door)',
50
- value: SETUP_PLATFORM_MODE.SINGLE
51
- },
52
- {
53
- name: 'Full platform — Traefik front door + miso/dev/tst/pro (shared infra)',
54
- value: SETUP_PLATFORM_MODE.FULL
55
- }
56
- ],
59
+ choices: getSetupPlatformModePromptChoices(),
57
60
  default: SETUP_PLATFORM_MODE.SINGLE
58
61
  }
59
62
  ]);
@@ -62,6 +65,6 @@ async function promptSetupPlatformMode(options = {}) {
62
65
 
63
66
  module.exports = {
64
67
  resolvePlatformModeFromOptions,
65
- promptSetupPlatformMode
68
+ promptSetupPlatformMode,
69
+ getSetupPlatformModePromptChoices
66
70
  };
67
-
@@ -1,7 +1,7 @@
1
1
  /**
2
- * @fileoverview verify-trust <systemKey> — semantic agent metadata validation for all datasources.
2
+ * @fileoverview verify-trust <systemKey> — dual-confidence AI Trust via dataplane (433.0).
3
3
  * @author AI Fabrix Team
4
- * @version 1.0.0
4
+ * @version 2.0.0
5
5
  */
6
6
 
7
7
  'use strict';
@@ -10,83 +10,20 @@ const chalk = require('chalk');
10
10
  const logger = require('../utils/logger');
11
11
  const { getConfig } = require('../core/config');
12
12
  const { setupIntegrationTestAuth } = require('../external-system/test-auth');
13
- const { runDatasourceAgentTrust } = require('../datasource/agent-trust-run');
13
+ const { runSystemVerifyTrust } = require('../api/lifecycle.api');
14
14
  const {
15
15
  resolveExternalIntegrationContext,
16
16
  syncLocalIfRequested
17
17
  } = require('./test-e2e-external');
18
18
 
19
- function pushTrustResult(results, key, trustRun, apiError) {
20
- const failed =
21
- apiError ||
22
- !trustRun ||
23
- trustRun.trustDecision === 'notTrusted' ||
24
- trustRun.validationStatus === 'failed';
25
- results.push({
26
- key,
27
- success: !failed,
28
- error: failed
29
- ? apiError
30
- ? apiError.message || String(apiError)
31
- : trustRun.summary || 'Trust validation failed'
32
- : undefined,
33
- trustRun
34
- });
35
- }
36
-
37
19
  /**
38
20
  * @async
39
- * @param {Object|null} uploadCtx
40
21
  * @param {string} externalSystem
41
22
  * @param {Object} options
42
- * @returns {Promise<{ authConfig: Object, dataplaneUrl: string }>}
43
- */
44
- async function resolveTrustAuthContext(uploadCtx, externalSystem, options) {
45
- if (uploadCtx) {
46
- return { authConfig: uploadCtx.authConfig, dataplaneUrl: uploadCtx.dataplaneUrl };
47
- }
48
- const configObj = await getConfig();
49
- return setupIntegrationTestAuth(
50
- externalSystem,
51
- {
52
- environment: options.env,
53
- authConfig: options.authConfig,
54
- dataplaneUrl: options.dataplaneUrl,
55
- silentResolve: options.silentResolve === true
56
- },
57
- configObj
58
- );
59
- }
60
-
61
- /**
62
- * @async
63
- * @param {string[]} keys
64
- * @param {Object} runOpts
65
- * @param {Array<Object>} results
66
- */
67
- async function runTrustForDatasourceKeys(keys, runOpts, results) {
68
- for (const key of keys) {
69
- try {
70
- const { trustRun, apiError } = await runDatasourceAgentTrust(key, runOpts);
71
- pushTrustResult(results, key, trustRun, apiError);
72
- } catch (err) {
73
- results.push({ key, success: false, error: err.message, trustRun: null });
74
- }
75
- }
76
- }
77
-
78
- /**
79
- * @async
80
- * @param {string} externalSystem
81
- * @param {Object} options
82
- * @returns {Promise<{ success: boolean, results: Array<Object>, systemKey: string }>}
23
+ * @returns {Promise<{ success: boolean, results: Array<Object>, systemKey: string, trustReport?: Object }>}
83
24
  */
84
25
  async function runTestTrustForExternalSystem(externalSystem, options = {}) {
85
- const { systemKey, keys } = resolveExternalIntegrationContext(externalSystem);
86
- if (keys.length === 0) {
87
- logger.log(chalk.yellow(`No datasources found for ${externalSystem}.`));
88
- return { success: true, results: [], systemKey };
89
- }
26
+ const { systemKey } = resolveExternalIntegrationContext(externalSystem);
90
27
 
91
28
  const uploadCtx = await syncLocalIfRequested(systemKey, options);
92
29
  if (uploadCtx?.authConfig && uploadCtx?.dataplaneUrl) {
@@ -95,30 +32,42 @@ async function runTestTrustForExternalSystem(externalSystem, options = {}) {
95
32
  options.silentResolve = true;
96
33
  }
97
34
  if (options.revalidate === true) {
98
- logger.log(chalk.cyan('Forcing revalidation (cache bypass) for each datasource…'));
35
+ logger.log(chalk.cyan('Forcing revalidation (cache bypass)…'));
99
36
  }
100
37
 
101
- const { authConfig, dataplaneUrl } = await resolveTrustAuthContext(
102
- uploadCtx,
103
- externalSystem,
104
- options
105
- );
106
- const results = [];
107
- await runTrustForDatasourceKeys(
108
- keys,
109
- {
110
- app: externalSystem,
111
- environment: options.env,
112
- noSync: true,
113
- revalidate: options.revalidate === true,
114
- summary: options.summary === true,
115
- timeout: options.timeout,
116
- authConfig,
117
- dataplaneUrl
118
- },
119
- results
120
- );
121
- return { success: results.every(r => r.success), results, systemKey };
38
+ let authConfig = options.authConfig;
39
+ let dataplaneUrl = options.dataplaneUrl;
40
+ if (!authConfig || !dataplaneUrl) {
41
+ const configObj = await getConfig();
42
+ const auth = await setupIntegrationTestAuth(
43
+ externalSystem,
44
+ {
45
+ environment: options.env,
46
+ authConfig: options.authConfig,
47
+ dataplaneUrl: options.dataplaneUrl,
48
+ silentResolve: options.silentResolve === true
49
+ },
50
+ configObj
51
+ );
52
+ authConfig = auth.authConfig;
53
+ dataplaneUrl = auth.dataplaneUrl;
54
+ }
55
+
56
+ const trustReport = await runSystemVerifyTrust(dataplaneUrl, authConfig, systemKey, {
57
+ forceRevalidate: options.revalidate === true
58
+ });
59
+
60
+ const results = (trustReport.datasourceRows || []).map(row => ({
61
+ key: row.datasourceKey,
62
+ success: trustReport.verdict === 'VERIFIED' || trustReport.verdict !== 'FAILED',
63
+ trustRun: {
64
+ trustDecision: row.trustDecision,
65
+ validationStatus: row.validationStatus
66
+ }
67
+ }));
68
+
69
+ const success = trustReport.verdict === 'VERIFIED';
70
+ return { success, results, systemKey, trustReport };
122
71
  }
123
72
 
124
73
  module.exports = {
@@ -328,6 +328,35 @@ async function ensureAppFromTemplate(appName, opts = {}) {
328
328
  return primaryCopied;
329
329
  }
330
330
 
331
+ /** Dataplane app key materialized for dataplane install (worker/beat are compose sidecars). */
332
+ const DATAPLANE_BUILDER_APP_KEYS = Object.freeze(['dataplane']);
333
+
334
+ /** Builder dirs removed on up-platform / setup force (includes Celery companions). */
335
+ const PLATFORM_BUILDER_CLEAN_APP_KEYS = Object.freeze([
336
+ 'keycloak',
337
+ 'miso-controller',
338
+ ...DATAPLANE_BUILDER_APP_KEYS
339
+ ]);
340
+
341
+ /**
342
+ * Ensure dataplane application manifest exists under the builder root.
343
+ * Worker/beat sidecars are resolved from parent application.yaml at run/compose time.
344
+ *
345
+ * @param {object} [opts]
346
+ * @param {boolean} [opts.silent]
347
+ * @returns {Promise<boolean>} True if any app template was copied
348
+ */
349
+ async function ensureDataplaneAppsFromTemplate(opts = {}) {
350
+ let anyCopied = false;
351
+ for (const appName of DATAPLANE_BUILDER_APP_KEYS) {
352
+ const copied = await ensureAppFromTemplate(appName, opts);
353
+ if (copied) {
354
+ anyCopied = true;
355
+ }
356
+ }
357
+ return anyCopied;
358
+ }
359
+
331
360
  /**
332
361
  * Merge builder/packages `application.yaml` into `~/.aifabrix/urls.local.yaml`.
333
362
  * Same scan as the end of {@link prepareUrlsLocalRegistryForUpPlatform}; call after platform app
@@ -354,14 +383,17 @@ async function prepareUrlsLocalRegistryForUpPlatform(opts = {}) {
354
383
  const templateOpts = { silent: Boolean(opts.silent) };
355
384
  await ensureAppFromTemplate('keycloak', templateOpts);
356
385
  await ensureAppFromTemplate('miso-controller', templateOpts);
357
- await ensureAppFromTemplate('dataplane', templateOpts);
386
+ await ensureDataplaneAppsFromTemplate(templateOpts);
358
387
  refreshUrlsLocalRegistryForCurrentProject();
359
388
  }
360
389
 
361
390
  module.exports = {
362
391
  applyUpPlatformForceConfig,
363
392
  cleanBuilderAppDirs,
393
+ DATAPLANE_BUILDER_APP_KEYS,
394
+ PLATFORM_BUILDER_CLEAN_APP_KEYS,
364
395
  ensureAppFromTemplate,
396
+ ensureDataplaneAppsFromTemplate,
365
397
  patchEnvOutputPathForDeployOnly,
366
398
  validateEnvOutputPathFolderOrNull,
367
399
  getEnvOutputPathFolder,