@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
@@ -29,7 +29,12 @@ const { checkHealthEndpoint } = require('../utils/health-check');
29
29
  const { validateControllerUrl } = require('../utils/auth-config-validator');
30
30
  const app = require('../app');
31
31
  const { assertDevInfraUp } = require('./dev-infra-gate');
32
- const { ensureAppFromTemplate, validateEnvOutputPathFolderOrNull, refreshUrlsLocalRegistryForCurrentProject } = require('./up-common');
32
+ const { dataplaneDockerHostPort } = require('../utils/declarative-url-ports');
33
+ const {
34
+ ensureDataplaneAppsFromTemplate,
35
+ validateEnvOutputPathFolderOrNull,
36
+ refreshUrlsLocalRegistryForCurrentProject
37
+ } = require('./up-common');
33
38
 
34
39
  const CONTROLLER_HEALTH_PATH = '/health';
35
40
 
@@ -198,8 +203,7 @@ function computeDataplanePortOverride(runEnvKey, developerIdNum) {
198
203
  if (runEnvKey !== 'dev' && runEnvKey !== 'tst') {
199
204
  return undefined;
200
205
  }
201
- const baseHostPort = 3001 + (Number.isFinite(developerIdNum) ? developerIdNum : 0) * 100;
202
- return runEnvKey === 'dev' ? baseHostPort + 10 : baseHostPort + 20;
206
+ return dataplaneDockerHostPort(3001, developerIdNum, runEnvKey);
203
207
  }
204
208
 
205
209
  function assertDevEnvironmentForDataplane(cfg) {
@@ -243,7 +247,7 @@ async function handleUpDataplane(options = {}) {
243
247
  : await resolveEnvironment();
244
248
  const { controllerUrl, authConfig } = await resolveUpDataplaneAuthContext(options, environmentKey);
245
249
 
246
- await ensureAppFromTemplate('dataplane');
250
+ await ensureDataplaneAppsFromTemplate();
247
251
  // If envOutputPath target folder does not exist, set envOutputPath to null
248
252
  validateEnvOutputPathFolderOrNull('dataplane');
249
253
 
@@ -32,6 +32,7 @@ const {
32
32
  resolveConfigurationValues,
33
33
  collectResolvedVariableConfigurationNames
34
34
  } = require('../utils/configuration-env-resolver');
35
+ const { pushRuntimeConfigurationValues } = require('../utils/runtime-configuration-values');
35
36
  const { validateExternalSystemComplete } = require('../validation/validate');
36
37
  const { displayValidationResults } = require('../validation/validate-display');
37
38
  const { generateControllerManifest } = require('../generator/external-controller-manifest');
@@ -219,6 +220,24 @@ async function pushAndLogCredentialSecrets(dataplaneUrl, authConfig, systemKey,
219
220
  if (pushResult.warning) {
220
221
  logger.log(chalk.yellow(`Warning: ${pushResult.warning}`));
221
222
  }
223
+ try {
224
+ const configPush = await pushRuntimeConfigurationValues(
225
+ dataplaneUrl,
226
+ authConfig,
227
+ systemKey,
228
+ payload
229
+ );
230
+ if (configPush.pushed > 0) {
231
+ const nameList = configPush.names.length ? ` (${configPush.names.join(', ')})` : '';
232
+ logger.log(
233
+ formatSuccessLine(
234
+ `Stored ${configPush.pushed} runtime configuration value(s) in dataplane${nameList}.`
235
+ )
236
+ );
237
+ }
238
+ } catch (err) {
239
+ logger.log(chalk.yellow(`Warning: runtime configuration store skipped: ${err.message}`));
240
+ }
222
241
  }
223
242
 
224
243
  /**
@@ -314,7 +333,7 @@ async function buildValidatedUploadManifestPayload(systemKey, opts = {}) {
314
333
  * @param {Object} manifest
315
334
  * @returns {Promise<void>}
316
335
  */
317
- async function logAndSyncLocalOpenApiForUpload(systemKey, manifest) {
336
+ async function logAndSyncLocalOpenApiForUpload(systemKey, manifest, uploadContext = {}) {
318
337
  const appPath = getIntegrationPath(systemKey);
319
338
  const ei = manifest && manifest.externalIntegration;
320
339
  const datasourceFiles = ei && Array.isArray(ei.dataSources) ? ei.dataSources : [];
@@ -323,7 +342,9 @@ async function logAndSyncLocalOpenApiForUpload(systemKey, manifest) {
323
342
  dryRun: false,
324
343
  appPath,
325
344
  systemKey,
326
- datasourceFiles
345
+ datasourceFiles,
346
+ dataplaneUrl: uploadContext.dataplaneUrl,
347
+ authConfig: uploadContext.authConfig
327
348
  });
328
349
  for (const line of openapiSyncLines) {
329
350
  logger.log(line.startsWith('Skipped') ? formatWarningLine(line) : formatSuccessLine(line));
@@ -372,7 +393,7 @@ async function runUploadPublishAndSummary(systemKey, options, manifest, payload)
372
393
  await pushAndLogCredentialSecrets(dataplaneUrl, authConfig, systemKey, payload, {
373
394
  json: options.json === true
374
395
  });
375
- await logAndSyncLocalOpenApiForUpload(systemKey, manifest);
396
+ await logAndSyncLocalOpenApiForUpload(systemKey, manifest, { dataplaneUrl, authConfig });
376
397
  await handlePublicationAndFollowups(
377
398
  {
378
399
  systemKey,
@@ -39,7 +39,10 @@ async function runVerifyTrustCommandAction(appName, options, cmd) {
39
39
  systemKey: result.systemKey,
40
40
  command: result.command,
41
41
  verdict: result.verdict,
42
- businessContextConfidencePercent: result.businessContextConfidencePercent,
42
+ aiTrustPercent: result.aiTrustPercent,
43
+ accessConfidencePercent: result.accessConfidencePercent,
44
+ businessConfidencePercent: result.businessConfidencePercent,
45
+ findings: result.findings,
43
46
  datasourceRows: opts.details ? result.datasourceRows : undefined
44
47
  },
45
48
  opts.details || opts.verbose
@@ -1,17 +1,13 @@
1
1
  /**
2
- * @fileoverview verify-trust orchestration with product-model rollup (plan 150.0).
2
+ * @fileoverview verify-trust orchestration with dual-confidence report (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';
8
8
 
9
9
  const { runTestTrustForExternalSystem } = require('./test-trust-external');
10
- const {
11
- VERDICT,
12
- verdictFromPassed,
13
- worstConfidencePercent
14
- } = require('../lifecycle/product-model');
10
+ const { VERDICT, verdictFromPassed } = require('../lifecycle/product-model');
15
11
 
16
12
  /**
17
13
  * @param {Array<Object>} rows
@@ -20,14 +16,12 @@ const {
20
16
  function mapTrustDatasourceRows(rows) {
21
17
  return (rows || []).map(row => {
22
18
  const tr = row.trustRun;
23
- const confidencePercent =
24
- tr && typeof tr.confidence === 'number' ? Math.round(tr.confidence * 100) : null;
25
19
  const verdict = row.success ? VERDICT.VERIFIED : VERDICT.FAILED;
26
20
  return {
27
21
  datasourceKey: row.key,
28
22
  verdict,
29
- confidencePercent,
30
23
  trustDecision: tr?.trustDecision,
24
+ validationStatus: tr?.validationStatus,
31
25
  error: row.error
32
26
  };
33
27
  });
@@ -40,22 +34,20 @@ function mapTrustDatasourceRows(rows) {
40
34
  * @returns {Promise<Object>}
41
35
  */
42
36
  async function runVerifyTrustForExternalSystem(systemKey, options = {}) {
43
- const { success, results, systemKey: resolvedKey } = await runTestTrustForExternalSystem(
44
- systemKey,
45
- options
46
- );
37
+ const { success, results, systemKey: resolvedKey, trustReport } =
38
+ await runTestTrustForExternalSystem(systemKey, options);
47
39
  const datasourceRows = mapTrustDatasourceRows(results);
48
- const confidences = datasourceRows
49
- .map(r => (typeof r.confidencePercent === 'number' ? r.confidencePercent / 100 : null))
50
- .filter(v => v !== null);
51
- const businessContextConfidencePercent = worstConfidencePercent(confidences);
52
- const verdict = verdictFromPassed(success);
40
+ const report = trustReport || {};
41
+ const verdict = report.verdict ? report.verdict : verdictFromPassed(success);
53
42
 
54
43
  return {
55
44
  systemKey: resolvedKey || systemKey,
56
45
  command: 'verify-trust',
57
46
  verdict,
58
- businessContextConfidencePercent,
47
+ aiTrustPercent: report.aiTrustPercent ?? null,
48
+ accessConfidencePercent: report.accessConfidencePercent ?? null,
49
+ businessConfidencePercent: report.businessConfidencePercent ?? null,
50
+ findings: Array.isArray(report.findings) ? report.findings : [],
59
51
  datasourceRows,
60
52
  results
61
53
  };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `setupPlatformMode` field in `config.yaml` (`single` | `full`).
2
+ * `setupPlatformMode` field in `config.yaml` (`single` | `single-traefik` | `full`).
3
3
  *
4
4
  * @fileoverview Persists setup wizard platform mode
5
5
  * @author AI Fabrix Team
@@ -15,7 +15,7 @@ const {
15
15
 
16
16
  /**
17
17
  * @param {() => Promise<Object>} getConfig
18
- * @returns {Promise<'single'|'full'>}
18
+ * @returns {Promise<'single'|'single-traefik'|'full'>}
19
19
  */
20
20
  async function getSetupPlatformModeFromConfig(getConfig) {
21
21
  const cfg = await getConfig();
@@ -29,7 +29,7 @@ async function getSetupPlatformModeFromConfig(getConfig) {
29
29
  * @param {() => Promise<Object>} getConfig
30
30
  * @param {(data: Object) => Promise<void>} saveConfig
31
31
  * @param {unknown} mode
32
- * @returns {Promise<'single'|'full'>}
32
+ * @returns {Promise<'single'|'single-traefik'|'full'>}
33
33
  */
34
34
  async function setSetupPlatformModeInConfig(getConfig, saveConfig, mode) {
35
35
  const normalized = normalizeSetupPlatformMode(mode);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Setup platform topology mode (`single` | `full`).
2
+ * Setup platform topology mode (`single` | `single-traefik` | `full`).
3
3
  *
4
4
  * This is distinct from `setupInstallationProfile` (dev/pro password bundle) and from
5
5
  * the login/run environment (`dev` | `tst` | `pro`).
@@ -14,12 +14,20 @@
14
14
  /** @enum {string} */
15
15
  const SETUP_PLATFORM_MODE = Object.freeze({
16
16
  SINGLE: 'single',
17
+ SINGLE_TRAEFIK: 'single-traefik',
17
18
  FULL: 'full'
18
19
  });
19
20
 
21
+ /** @type {readonly string[]} */
22
+ const SETUP_PLATFORM_MODE_CLI_VALUES = Object.freeze([
23
+ SETUP_PLATFORM_MODE.SINGLE,
24
+ SETUP_PLATFORM_MODE.SINGLE_TRAEFIK,
25
+ SETUP_PLATFORM_MODE.FULL
26
+ ]);
27
+
20
28
  /**
21
29
  * @param {unknown} value
22
- * @returns {'single'|'full'}
30
+ * @returns {'single'|'single-traefik'|'full'}
23
31
  */
24
32
  function normalizeSetupPlatformMode(value) {
25
33
  const raw = String(value ?? '')
@@ -27,6 +35,8 @@ function normalizeSetupPlatformMode(value) {
27
35
  .toLowerCase();
28
36
  if (raw === SETUP_PLATFORM_MODE.FULL) return SETUP_PLATFORM_MODE.FULL;
29
37
  if (raw === 'all' || raw === 'full-platform' || raw === 'platform-full') return SETUP_PLATFORM_MODE.FULL;
38
+ if (raw === SETUP_PLATFORM_MODE.SINGLE_TRAEFIK) return SETUP_PLATFORM_MODE.SINGLE_TRAEFIK;
39
+ if (raw === 'ingress' || raw === 'single-ingress' || raw === 'traefik') return SETUP_PLATFORM_MODE.SINGLE_TRAEFIK;
30
40
  return SETUP_PLATFORM_MODE.SINGLE;
31
41
  }
32
42
 
@@ -34,22 +44,47 @@ function normalizeSetupPlatformMode(value) {
34
44
  * Derived config flags for a chosen platform mode.
35
45
  *
36
46
  * - single: direct ports, no front door ingress
37
- * - full: enable Traefik ingress + env-scoped resources
47
+ * - single-traefik: Traefik front door (domain + TLS) for one environment only
48
+ * - full: Traefik ingress + env-scoped resources (multi-env; CLI only until menu returns)
38
49
  *
39
- * @param {'single'|'full'} mode
50
+ * @param {'single'|'single-traefik'|'full'} mode
40
51
  * @returns {{ traefik: boolean, useEnvironmentScopedResources: boolean }}
41
52
  */
42
53
  function flagsForSetupPlatformMode(mode) {
43
54
  const m = normalizeSetupPlatformMode(mode);
55
+ const traefik =
56
+ m === SETUP_PLATFORM_MODE.SINGLE_TRAEFIK || m === SETUP_PLATFORM_MODE.FULL;
44
57
  return {
45
- traefik: m === SETUP_PLATFORM_MODE.FULL,
58
+ traefik,
46
59
  useEnvironmentScopedResources: m === SETUP_PLATFORM_MODE.FULL
47
60
  };
48
61
  }
49
62
 
63
+ /**
64
+ * Interactive setup menu choices (full platform hidden until multi-env path ships).
65
+ *
66
+ * @returns {{ name: string, value: string }[]}
67
+ */
68
+ function getSetupPlatformModePromptChoices() {
69
+ return [
70
+ {
71
+ name: 'Single environment — direct ports (no Traefik front door)',
72
+ value: SETUP_PLATFORM_MODE.SINGLE
73
+ },
74
+ {
75
+ name: 'Single environment — Traefik front door (domain + TLS)',
76
+ value: SETUP_PLATFORM_MODE.SINGLE_TRAEFIK
77
+ }
78
+ // Full platform — Traefik front door + miso/dev/tst/pro (shared infra) — menu returns soon.
79
+ // { name: 'Full platform — Traefik front door + miso/dev/tst/pro (shared infra)', value: SETUP_PLATFORM_MODE.FULL },
80
+ ];
81
+ }
82
+
50
83
  module.exports = {
51
84
  SETUP_PLATFORM_MODE,
85
+ SETUP_PLATFORM_MODE_CLI_VALUES,
52
86
  normalizeSetupPlatformMode,
53
- flagsForSetupPlatformMode
87
+ flagsForSetupPlatformMode,
88
+ getSetupPlatformModePromptChoices
54
89
  };
55
90
 
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  const { deepClone } = require('./copy-operations');
10
+ const { capabilitiesIncludes } = require('./capability-manifest');
10
11
 
11
12
  /**
12
13
  * @param {object} doc - Parsed datasource JSON
@@ -16,8 +17,7 @@ const { deepClone } = require('./copy-operations');
16
17
  */
17
18
  function extractCapabilitySliceForDiff(doc, capabilityKey, profileKey) {
18
19
  const cap = capabilityKey;
19
- const listed =
20
- Boolean(Array.isArray(doc.capabilities) && doc.capabilities.includes(cap));
20
+ const listed = capabilitiesIncludes(doc.capabilities, cap);
21
21
  const openapiOp = doc.openapi?.operations?.[cap];
22
22
  const cipOp = doc.execution?.cip?.operations?.[cap];
23
23
  let exposedProfile;
@@ -0,0 +1,254 @@
1
+ /**
2
+ * COM v2 capability object helpers for datasource manifests.
3
+ *
4
+ * @fileoverview capability-manifest
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const FORBIDDEN_ROOT_KEYS = new Set([
12
+ 'context',
13
+ 'origin',
14
+ 'generatedBy',
15
+ 'composition',
16
+ 'generation',
17
+ 'enterpriseResolver'
18
+ ]);
19
+
20
+ const DEFAULT_RISK_BY_KEY = {
21
+ list: 'low',
22
+ get: 'low',
23
+ search: 'low',
24
+ create: 'medium',
25
+ update: 'medium',
26
+ delete: 'high'
27
+ };
28
+
29
+ const RESOURCE_TYPE_MAP = {
30
+ conversation: 'message',
31
+ user: 'employee',
32
+ person: 'employee',
33
+ record: 'document',
34
+ folder: 'document',
35
+ country: 'organization',
36
+ report: 'document',
37
+ calendar: 'event',
38
+ subscription: 'event'
39
+ };
40
+
41
+ /**
42
+ * @param {unknown} entry
43
+ * @returns {string}
44
+ */
45
+ function capabilityKeyFromEntry(entry) {
46
+ if (typeof entry === 'string' && entry.trim()) {
47
+ return entry.trim();
48
+ }
49
+ if (entry && typeof entry === 'object' && typeof entry.key === 'string' && entry.key.trim()) {
50
+ return entry.key.trim();
51
+ }
52
+ return '';
53
+ }
54
+
55
+ /**
56
+ * @param {unknown} capabilities
57
+ * @returns {string[]}
58
+ */
59
+ function listCapabilityKeys(capabilities) {
60
+ if (!Array.isArray(capabilities)) {
61
+ return [];
62
+ }
63
+ return capabilities.map(capabilityKeyFromEntry).filter(Boolean);
64
+ }
65
+
66
+ /**
67
+ * @param {unknown} capabilities
68
+ * @param {string} key
69
+ * @returns {boolean}
70
+ */
71
+ function capabilitiesIncludes(capabilities, key) {
72
+ const target = String(key).toLowerCase();
73
+ return listCapabilityKeys(capabilities).some((item) => item.toLowerCase() === target);
74
+ }
75
+
76
+ /**
77
+ * @param {string} key
78
+ * @param {string} [displayName]
79
+ * @returns {object}
80
+ */
81
+ function buildCapabilityObject(key, displayName) {
82
+ const normalized = String(key || '').trim();
83
+ const label = displayName && String(displayName).trim() ? String(displayName).trim() : 'records';
84
+ return {
85
+ key: normalized,
86
+ description: `${normalized} operation on ${label}`,
87
+ riskLevel: DEFAULT_RISK_BY_KEY[normalized.toLowerCase()] || 'medium',
88
+ approvalRequired: false
89
+ };
90
+ }
91
+
92
+ /**
93
+ * @param {object} parsed
94
+ * @returns {object[]}
95
+ */
96
+ function normalizeCapabilitiesToObjects(parsed) {
97
+ const caps = parsed?.capabilities;
98
+ const displayName = parsed?.displayName || parsed?.key || 'records';
99
+ if (!Array.isArray(caps) || caps.length === 0) {
100
+ return [];
101
+ }
102
+ const out = [];
103
+ const seen = new Set();
104
+ caps.forEach((item) => {
105
+ if (typeof item === 'string' && item.trim()) {
106
+ const key = item.trim();
107
+ if (seen.has(key)) return;
108
+ seen.add(key);
109
+ out.push(buildCapabilityObject(key, displayName));
110
+ return;
111
+ }
112
+ if (!item || typeof item !== 'object') return;
113
+ const key = capabilityKeyFromEntry(item);
114
+ if (!key || seen.has(key)) return;
115
+ seen.add(key);
116
+ const row = buildCapabilityObject(key, displayName);
117
+ if (typeof item.description === 'string' && item.description.trim()) {
118
+ row.description = item.description.trim();
119
+ }
120
+ if (typeof item.riskLevel === 'string' && item.riskLevel.trim()) {
121
+ row.riskLevel = item.riskLevel.trim();
122
+ }
123
+ if (item.approvalRequired === true) {
124
+ row.approvalRequired = true;
125
+ }
126
+ out.push(row);
127
+ });
128
+ return out;
129
+ }
130
+
131
+ /**
132
+ * @param {object} node
133
+ * @returns {void}
134
+ */
135
+ function stripSemanticExtras(node) {
136
+ if (!node || typeof node !== 'object') return;
137
+ const semantic = node.semantic;
138
+ if (semantic && typeof semantic === 'object') {
139
+ delete semantic.categories;
140
+ delete semantic.synonyms;
141
+ if (Object.keys(semantic).length === 0) {
142
+ delete node.semantic;
143
+ }
144
+ }
145
+ Object.values(node).forEach((value) => {
146
+ if (value && typeof value === 'object') {
147
+ if (Array.isArray(value)) {
148
+ value.forEach((item) => stripSemanticExtras(item));
149
+ } else {
150
+ stripSemanticExtras(value);
151
+ }
152
+ }
153
+ });
154
+ }
155
+
156
+ /**
157
+ * @param {object} parsed
158
+ * @param {string[]} changes
159
+ * @returns {boolean}
160
+ */
161
+ function removeForbiddenComRootKeys(parsed, changes) {
162
+ let updated = false;
163
+ FORBIDDEN_ROOT_KEYS.forEach((key) => {
164
+ if (Object.prototype.hasOwnProperty.call(parsed, key)) {
165
+ delete parsed[key];
166
+ changes.push(`Removed forbidden COM v2 root key: ${key}`);
167
+ updated = true;
168
+ }
169
+ });
170
+ return updated;
171
+ }
172
+
173
+ /**
174
+ * @param {object} parsed
175
+ * @param {string[]} changes
176
+ * @returns {boolean}
177
+ */
178
+ function stripFieldMappingSemanticExtras(parsed, changes) {
179
+ if (!parsed.fieldMappings) {
180
+ return false;
181
+ }
182
+ const before = JSON.stringify(parsed.fieldMappings);
183
+ stripSemanticExtras(parsed.fieldMappings);
184
+ if (JSON.stringify(parsed.fieldMappings) === before) {
185
+ return false;
186
+ }
187
+ changes.push('Removed fieldMappings.*.semantic.categories/synonyms (COM v2)');
188
+ return true;
189
+ }
190
+
191
+ /**
192
+ * @param {object} parsed
193
+ * @param {string[]} changes
194
+ * @returns {boolean}
195
+ */
196
+ function applyResourceTypeCatalogMap(parsed, changes) {
197
+ const resourceType = parsed.resourceType;
198
+ if (typeof resourceType !== 'string') {
199
+ return false;
200
+ }
201
+ const mapped = RESOURCE_TYPE_MAP[resourceType.trim()];
202
+ if (!mapped) {
203
+ return false;
204
+ }
205
+ parsed.resourceType = mapped;
206
+ changes.push(`Mapped resourceType ${resourceType} → ${mapped}`);
207
+ return true;
208
+ }
209
+
210
+ /**
211
+ * @param {object} parsed
212
+ * @param {string[]} changes
213
+ * @returns {boolean}
214
+ */
215
+ function migrateCapabilitiesToComObjects(parsed, changes) {
216
+ const caps = parsed.capabilities;
217
+ const needsObjectMigration = Array.isArray(caps)
218
+ && caps.some((item) => typeof item === 'string' || (item && typeof item === 'object' && !item.key));
219
+ const hasStringCapabilities = Array.isArray(caps) && caps.length > 0 && typeof caps[0] === 'string';
220
+ if (!needsObjectMigration && !hasStringCapabilities) {
221
+ return false;
222
+ }
223
+ parsed.capabilities = normalizeCapabilitiesToObjects(parsed);
224
+ changes.push('Migrated capabilities[] to COM v2 capability objects');
225
+ return true;
226
+ }
227
+
228
+ /**
229
+ * @param {object} parsed
230
+ * @param {string[]} changes
231
+ * @returns {boolean}
232
+ */
233
+ function repairComV2Manifest(parsed, changes) {
234
+ if (!parsed || typeof parsed !== 'object') {
235
+ return false;
236
+ }
237
+ return [
238
+ removeForbiddenComRootKeys(parsed, changes),
239
+ stripFieldMappingSemanticExtras(parsed, changes),
240
+ applyResourceTypeCatalogMap(parsed, changes),
241
+ migrateCapabilitiesToComObjects(parsed, changes)
242
+ ].some(Boolean);
243
+ }
244
+
245
+ module.exports = {
246
+ FORBIDDEN_ROOT_KEYS,
247
+ capabilityKeyFromEntry,
248
+ listCapabilityKeys,
249
+ capabilitiesIncludes,
250
+ buildCapabilityObject,
251
+ normalizeCapabilitiesToObjects,
252
+ stripSemanticExtras,
253
+ repairComV2Manifest
254
+ };
@@ -6,6 +6,8 @@
6
6
  * @version 2.0.0
7
7
  */
8
8
 
9
+ const { capabilitiesIncludes } = require('./capability-manifest');
10
+
9
11
  /**
10
12
  * @param {object|undefined} operations
11
13
  * @param {string} logicalKey
@@ -146,10 +148,7 @@ function resolveProfileKeyForLogical(doc, logicalKey) {
146
148
  * @returns {boolean}
147
149
  */
148
150
  function capabilityLogicalExists(doc, name) {
149
- if (
150
- Array.isArray(doc.capabilities) &&
151
- doc.capabilities.some((c) => String(c).toLowerCase() === String(name).toLowerCase())
152
- ) {
151
+ if (capabilitiesIncludes(doc.capabilities, name)) {
153
152
  return true;
154
153
  }
155
154
  if (findMatchingOpsKeys(doc.openapi?.operations, name).length > 0) {
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  const { rewriteCapabilityReferences } = require('./reference-rewrite');
10
+ const { capabilityKeyFromEntry } = require('./capability-manifest');
10
11
  const { jsonPointerPath } = require('./json-pointer');
11
12
  const { buildBasicExposureProfileArray } = require('./basic-exposure');
12
13
  const {
@@ -53,9 +54,10 @@ function removeCapability(doc, logicalKey) {
53
54
  const profileKey = resolveProfileKeyForLogical(doc, logicalKey);
54
55
 
55
56
  if (Array.isArray(doc.capabilities)) {
56
- doc.capabilities = doc.capabilities.filter(
57
- (c) => String(c).toLowerCase() !== String(logicalKey).toLowerCase()
58
- );
57
+ doc.capabilities = doc.capabilities.filter((c) => {
58
+ const key = capabilityKeyFromEntry(c);
59
+ return key.toLowerCase() !== String(logicalKey).toLowerCase();
60
+ });
59
61
  }
60
62
  if (openapiKey && oo) {
61
63
  delete oo[openapiKey];
@@ -17,6 +17,7 @@ const {
17
17
  findMatchingOpsKeys,
18
18
  resolveProfileKeyForLogical
19
19
  } = require('./capability-resolve');
20
+ const { capabilityKeyFromEntry } = require('./capability-manifest');
20
21
 
21
22
  /**
22
23
  * @param {unknown} capabilities
@@ -28,7 +29,7 @@ function findCapabilityIndex(capabilities, logicalName) {
28
29
  return -1;
29
30
  }
30
31
  return capabilities.findIndex(
31
- (c) => String(c).toLowerCase() === String(logicalName).toLowerCase()
32
+ (c) => capabilityKeyFromEntry(c).toLowerCase() === String(logicalName).toLowerCase()
32
33
  );
33
34
  }
34
35
 
@@ -8,6 +8,7 @@
8
8
 
9
9
  const fs = require('node:fs');
10
10
  const path = require('path');
11
+ const { loadConfigFile, writeConfigFile } = require('../../utils/config-format');
11
12
  const { writeBackup } = require('../../utils/integration-file-backup');
12
13
  const {
13
14
  resolveValidateInputPath,
@@ -46,6 +47,33 @@ function resolveCapabilityCopyInputPath(identifier) {
46
47
  return resolveValidateInputPath(trimmed);
47
48
  }
48
49
 
50
+ /**
51
+ * Read a datasource config file (.json, .yaml, or .yml).
52
+ *
53
+ * @param {string} filePath
54
+ * @returns {object}
55
+ */
56
+ function readDatasourceConfigFile(filePath) {
57
+ return loadConfigFile(filePath);
58
+ }
59
+
60
+ /**
61
+ * @param {string} filePath
62
+ * @returns {'json'|'yaml'}
63
+ */
64
+ function inferDatasourceConfigFormat(filePath) {
65
+ const ext = path.extname(filePath).toLowerCase();
66
+ if (ext === '.json') {
67
+ return 'json';
68
+ }
69
+ if (ext === '.yaml' || ext === '.yml') {
70
+ return 'yaml';
71
+ }
72
+ throw new Error(
73
+ `Unsupported datasource config extension: ${ext || '(none)'}. Use .json, .yaml, or .yml`
74
+ );
75
+ }
76
+
49
77
  /**
50
78
  * @param {string} filePath
51
79
  * @param {object} obj
@@ -53,9 +81,9 @@ function resolveCapabilityCopyInputPath(identifier) {
53
81
  */
54
82
  function atomicWriteJson(filePath, obj) {
55
83
  const dir = path.dirname(filePath);
84
+ const format = inferDatasourceConfigFormat(filePath);
56
85
  const tmp = path.join(dir, `.${path.basename(filePath)}.${process.pid}.tmp`);
57
- const payload = `${JSON.stringify(obj, null, 2)}\n`;
58
- fs.writeFileSync(tmp, payload, 'utf8');
86
+ writeConfigFile(tmp, obj, format);
59
87
  fs.renameSync(tmp, filePath);
60
88
  }
61
89
 
@@ -89,8 +117,7 @@ function atomicWriteJson(filePath, obj) {
89
117
  */
90
118
  async function runCapabilityCopy(opts) {
91
119
  const resolvedPath = resolveCapabilityCopyInputPath(opts.fileOrKey.trim());
92
- const raw = fs.readFileSync(resolvedPath, 'utf8');
93
- const parsed = JSON.parse(raw);
120
+ const parsed = readDatasourceConfigFile(resolvedPath);
94
121
 
95
122
  const to = normalizeCapabilityKey(opts.as, 'Target (--as)');
96
123
  const fromRaw = opts.from !== undefined && opts.from !== null ? String(opts.from).trim() : '';
@@ -148,5 +175,6 @@ async function runCapabilityCopy(opts) {
148
175
  module.exports = {
149
176
  runCapabilityCopy,
150
177
  writeBackup,
178
+ readDatasourceConfigFile,
151
179
  atomicWriteJson
152
180
  };