@aifabrix/builder 2.45.0 → 2.45.5

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 (180) hide show
  1. package/.cursor/rules/docs-rules.mdc +3 -1
  2. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
  3. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
  4. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
  5. package/jest.projects.js +44 -1
  6. package/lib/api/agent-metadata-validation.api.js +93 -0
  7. package/lib/api/audit-api-helpers.js +68 -0
  8. package/lib/api/audit.api.js +119 -0
  9. package/lib/api/auth-cache.api.js +44 -0
  10. package/lib/api/certificates.api.js +4 -4
  11. package/lib/api/credential.api.js +2 -2
  12. package/lib/api/dataplane-health.api.js +137 -0
  13. package/lib/api/dataplane-sync.api.js +27 -0
  14. package/lib/api/datasources-core.api.js +21 -18
  15. package/lib/api/datasources-extended.api.js +22 -22
  16. package/lib/api/dimensions.api.js +3 -0
  17. package/lib/api/external-systems.api.js +13 -13
  18. package/lib/api/governance-scenarios.api.js +39 -0
  19. package/lib/api/groups.api.js +79 -0
  20. package/lib/api/index.js +45 -2
  21. package/lib/api/pipeline.api.js +4 -4
  22. package/lib/api/protection.api.js +157 -0
  23. package/lib/api/records-bulk.api.js +30 -0
  24. package/lib/api/records-search-parse.js +49 -0
  25. package/lib/api/records-search.api.js +29 -0
  26. package/lib/api/types/agent-metadata-validation.types.js +67 -0
  27. package/lib/api/types/audit.types.js +72 -0
  28. package/lib/api/types/dataplane-health.types.js +27 -0
  29. package/lib/api/types/dimensions.types.js +2 -0
  30. package/lib/api/types/governance-scenarios.types.js +39 -0
  31. package/lib/api/types/protection.types.js +69 -0
  32. package/lib/api/types/records-bulk.types.js +35 -0
  33. package/lib/api/types/records-search.types.js +28 -0
  34. package/lib/api/user-groups.api.js +50 -0
  35. package/lib/api/users.api.js +95 -0
  36. package/lib/api/validation-run.api.js +3 -3
  37. package/lib/api/validation-runner.js +3 -3
  38. package/lib/api/wizard-platform.api.js +3 -3
  39. package/lib/api/wizard.api.js +21 -21
  40. package/lib/app/deploy.js +34 -19
  41. package/lib/cli/index.js +4 -2
  42. package/lib/cli/infra-guided.js +22 -3
  43. package/lib/cli/setup-app.help.js +50 -2
  44. package/lib/cli/setup-app.js +33 -24
  45. package/lib/cli/setup-app.test-commands.js +80 -21
  46. package/lib/cli/setup-auth.js +30 -3
  47. package/lib/cli/setup-external-system.js +37 -3
  48. package/lib/cli/setup-utility-help-after.js +45 -0
  49. package/lib/cli/setup-utility-resolve.js +2 -2
  50. package/lib/cli/setup-utility.js +99 -81
  51. package/lib/commands/auth-status-dataplane-version.js +231 -0
  52. package/lib/commands/auth-status-display.js +94 -0
  53. package/lib/commands/auth-status.js +70 -6
  54. package/lib/commands/credential-push.js +10 -5
  55. package/lib/commands/datasource-load-export-cli.js +178 -0
  56. package/lib/commands/datasource-test-trust-cli.js +114 -0
  57. package/lib/commands/datasource-unified-test-cli.js +25 -17
  58. package/lib/commands/datasource-unified-test-cli.options.js +18 -3
  59. package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
  60. package/lib/commands/datasource-verify-audit-cli.js +96 -0
  61. package/lib/commands/datasource.js +68 -1
  62. package/lib/commands/dimension-value.js +43 -13
  63. package/lib/commands/dimension.js +38 -7
  64. package/lib/commands/identity-apply-sync.js +131 -0
  65. package/lib/commands/identity-group-cli.js +146 -0
  66. package/lib/commands/identity-membership-role-cache.js +175 -0
  67. package/lib/commands/identity-shared.js +94 -0
  68. package/lib/commands/identity-user-cli.js +158 -0
  69. package/lib/commands/identity.js +43 -0
  70. package/lib/commands/login.js +46 -16
  71. package/lib/commands/protection-cli-leaves.js +286 -0
  72. package/lib/commands/protection.js +50 -0
  73. package/lib/commands/repair.js +13 -3
  74. package/lib/commands/test-e2e-external.js +40 -9
  75. package/lib/commands/test-governance-cli-options.js +47 -0
  76. package/lib/commands/test-governance-command-action.js +55 -0
  77. package/lib/commands/test-governance-external.js +88 -0
  78. package/lib/commands/test-trust-cli-options.js +32 -0
  79. package/lib/commands/test-trust-command-action.js +118 -0
  80. package/lib/commands/test-trust-external.js +127 -0
  81. package/lib/commands/upload.js +34 -13
  82. package/lib/commands/wizard-core.js +3 -0
  83. package/lib/core/config-device-dataplane.js +185 -0
  84. package/lib/datasource/agent-trust-debug-log.js +45 -0
  85. package/lib/datasource/agent-trust-fetch.js +115 -0
  86. package/lib/datasource/agent-trust-map.js +101 -0
  87. package/lib/datasource/agent-trust-mock.js +44 -0
  88. package/lib/datasource/agent-trust-run.js +93 -0
  89. package/lib/datasource/agent-trust-timeout.js +25 -0
  90. package/lib/datasource/audit-evidence-display.js +80 -0
  91. package/lib/datasource/audit-evidence-extract-debug.js +103 -0
  92. package/lib/datasource/audit-evidence-extract.js +177 -0
  93. package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
  94. package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
  95. package/lib/datasource/audit-evidence-poll.js +109 -0
  96. package/lib/datasource/audit-evidence-run.js +177 -0
  97. package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
  98. package/lib/datasource/audit-evidence-verify-matrix.js +177 -0
  99. package/lib/datasource/audit-evidence-verify.js +108 -0
  100. package/lib/datasource/bulk-loader-run-batches.js +88 -0
  101. package/lib/datasource/bulk-loader-service-retry.js +52 -0
  102. package/lib/datasource/bulk-loader-service.js +127 -0
  103. package/lib/datasource/capability/run-capability-dimension.js +2 -1
  104. package/lib/datasource/datasource-exporter-resolve.js +82 -0
  105. package/lib/datasource/datasource-exporter-service.js +144 -0
  106. package/lib/datasource/datasource-identifier.js +148 -0
  107. package/lib/datasource/datasource-load-export-context.js +92 -0
  108. package/lib/datasource/integration-context.js +6 -4
  109. package/lib/datasource/local-data-paths.js +145 -0
  110. package/lib/datasource/log-cleaner.js +241 -0
  111. package/lib/datasource/log-viewer.js +111 -6
  112. package/lib/datasource/record-file-parser.js +85 -0
  113. package/lib/datasource/record-mapper.js +129 -0
  114. package/lib/datasource/resolve-app.js +74 -55
  115. package/lib/datasource/test-e2e.js +26 -2
  116. package/lib/datasource/unified-validation-run-resolve.js +45 -12
  117. package/lib/datasource/unified-validation-run.js +43 -17
  118. package/lib/datasource/validate.js +37 -20
  119. package/lib/external-system/test-system-level.js +25 -4
  120. package/lib/external-system/test.js +3 -1
  121. package/lib/governance/governance-pack-loader.js +70 -0
  122. package/lib/governance/governance-run-display.js +119 -0
  123. package/lib/identity/identity-apply-core.js +108 -0
  124. package/lib/identity/identity-apply-phases.js +244 -0
  125. package/lib/identity/identity-apply-service.js +77 -0
  126. package/lib/identity/identity-csv-parser.js +114 -0
  127. package/lib/internal/fs-real-sync.js +29 -1
  128. package/lib/protection/auth-context.js +45 -0
  129. package/lib/protection/convert-batch.js +90 -0
  130. package/lib/protection/load.js +41 -0
  131. package/lib/protection/paths.js +158 -0
  132. package/lib/protection/preflight-datasource-ready.js +46 -0
  133. package/lib/protection/protection-create-probe-lines.js +142 -0
  134. package/lib/protection/protection-create-probes.js +95 -0
  135. package/lib/protection/protection-create-scaffold.js +281 -0
  136. package/lib/protection/protection-display-helpers.js +445 -0
  137. package/lib/protection/protection-display.js +215 -0
  138. package/lib/protection/protection-preset-registry.js +97 -0
  139. package/lib/protection/report-exit.js +62 -0
  140. package/lib/protection/resolve.js +202 -0
  141. package/lib/protection/run-commands-validate.js +130 -0
  142. package/lib/protection/run-commands.js +216 -0
  143. package/lib/protection/run-protection-create-helpers.js +134 -0
  144. package/lib/protection/run-protection-create-probes-flow.js +43 -0
  145. package/lib/protection/run-protection-create.js +191 -0
  146. package/lib/protection/scope.js +22 -0
  147. package/lib/protection/sync-after-upload.js +64 -0
  148. package/lib/protection/upload-batch.js +127 -0
  149. package/lib/protection/validate-batch.js +113 -0
  150. package/lib/protection/validate-local.js +65 -0
  151. package/lib/resolvers/dimension-file.js +29 -4
  152. package/lib/schema/external-datasource.schema.json +47 -13
  153. package/lib/schema/flag-map-validation-run.json +2 -2
  154. package/lib/schema/protection.schema.json +374 -0
  155. package/lib/schema/type/credential-field-registry.json +636 -0
  156. package/lib/schema/wizard-config.schema.json +1 -1
  157. package/lib/utils/agent-trust-run-display.js +231 -0
  158. package/lib/utils/agent-trust-run-exit.js +59 -0
  159. package/lib/utils/agent-trust-wait-ui.js +94 -0
  160. package/lib/utils/cli-exit-codes.js +30 -0
  161. package/lib/utils/cli-sync-options.js +33 -0
  162. package/lib/utils/credential-push-ui.js +90 -0
  163. package/lib/utils/dataplane-cli-version-gate.js +202 -0
  164. package/lib/utils/dataplane-cli-version-help.js +74 -0
  165. package/lib/utils/datasource-export-display.js +137 -0
  166. package/lib/utils/datasource-load-display.js +174 -0
  167. package/lib/utils/datasource-load-export-ui.js +120 -0
  168. package/lib/utils/datasource-test-run-capability-scope.js +3 -3
  169. package/lib/utils/datasource-test-run-display.js +3 -3
  170. package/lib/utils/datasource-test-run-tty-log.js +5 -1
  171. package/lib/utils/help-builder.js +30 -24
  172. package/lib/utils/manifest-source-emit.js +22 -0
  173. package/lib/utils/semver-compare.js +127 -0
  174. package/lib/utils/upload-pipeline-ui.js +91 -0
  175. package/lib/utils/validation-poll-ui.js +14 -7
  176. package/lib/validation/wizard-config-validator.js +58 -19
  177. package/package.json +1 -1
  178. package/templates/applications/dataplane/env.template +13 -1
  179. package/templates/applications/dataplane/rbac.yaml +4 -0
  180. package/templates/external-system/README.md.hbs +56 -8
@@ -0,0 +1,118 @@
1
+ /**
2
+ * @fileoverview CLI action for `aifabrix test-trust` (plan 143).
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const chalk = require('chalk');
10
+ const logger = require('../utils/logger');
11
+ const { handleCommandError } = require('../utils/cli-utils');
12
+ const { runTestTrustForExternalSystem } = require('./test-trust-external');
13
+ const {
14
+ displaySystemTrustRollupTTY,
15
+ displayAgentTrustRunTTY
16
+ } = require('../utils/agent-trust-run-display');
17
+ const { computeSystemExitCodeFromTrustRows } = require('../utils/agent-trust-run-exit');
18
+ const { writeTrustDebugLogAndPrint } = require('../datasource/agent-trust-debug-log');
19
+ const { normalizeTestTrustCliOptions } = require('./test-trust-cli-options');
20
+
21
+ /**
22
+ * @async
23
+ * @param {string} appName
24
+ * @param {Object} options
25
+ */
26
+ async function writeSystemTrustDebugLogs(appName, results, options) {
27
+ if (!options.debug) return;
28
+ logger.log('');
29
+ logger.log(chalk.gray('Debug logs:'));
30
+ for (const row of results) {
31
+ await writeTrustDebugLogAndPrint(appName, row.key, {
32
+ request: {
33
+ datasourceKey: row.key,
34
+ revalidate: options.revalidate === true,
35
+ noSync: options.noSync === true
36
+ },
37
+ response: row.trustRun,
38
+ error: row.error || null
39
+ });
40
+ }
41
+ }
42
+
43
+ function displaySystemTrustResults(systemKey, appName, results, options) {
44
+ if (options.json) {
45
+ logger.log(JSON.stringify(results, null, 2));
46
+ return;
47
+ }
48
+ if (options.summary) {
49
+ for (const row of results) {
50
+ const tr = row.trustRun;
51
+ const line = tr
52
+ ? `${row.key}: ${tr.trustDecision} (${tr.validationStatus})`
53
+ : `${row.key}: fail (${row.error || 'error'})`;
54
+ logger.log(line);
55
+ }
56
+ return;
57
+ }
58
+
59
+ const displayOpts = {
60
+ strict: options.strict === true,
61
+ showCache: options.verbose === true || options.revalidate === true
62
+ };
63
+ displaySystemTrustRollupTTY(systemKey || appName, results, displayOpts);
64
+
65
+ if (!options.verbose) return;
66
+
67
+ for (const row of results) {
68
+ if (!row.trustRun) continue;
69
+ displayAgentTrustRunTTY(row.trustRun, {
70
+ environment: options.env,
71
+ verbose: true,
72
+ noSync: options.noSync === true,
73
+ strict: options.strict === true
74
+ });
75
+ }
76
+ }
77
+
78
+ async function runTestTrustCommandAction(appName, options, cmd) {
79
+ const opts = normalizeTestTrustCliOptions(options, cmd);
80
+ const pathsUtil = require('../utils/paths');
81
+ const appType = await pathsUtil.detectAppType(appName).catch(() => null);
82
+ if (!appType || appType.baseDir !== 'integration') {
83
+ throw new Error(
84
+ 'test-trust applies to external integration folders only (integration/<systemKey>/).'
85
+ );
86
+ }
87
+ const { success, results, systemKey } = await runTestTrustForExternalSystem(appName, {
88
+ env: opts.env,
89
+ verbose: opts.verbose,
90
+ debug: opts.debug,
91
+ noSync: opts.noSync === true,
92
+ revalidate: opts.revalidate === true,
93
+ summary: opts.summary === true,
94
+ timeout: opts.timeout
95
+ });
96
+ displaySystemTrustResults(systemKey, appName, results, opts);
97
+ await writeSystemTrustDebugLogs(appName, results, opts);
98
+ const exitCode = computeSystemExitCodeFromTrustRows(results, {
99
+ warningsAsErrors: opts.warningsAsErrors === true,
100
+ strict: opts.strict === true
101
+ });
102
+ if (exitCode !== 0) process.exit(exitCode);
103
+ if (!success) process.exit(1);
104
+ }
105
+
106
+ async function testTrustCommandHandler(appName, options, cmd) {
107
+ try {
108
+ await runTestTrustCommandAction(appName, options, cmd);
109
+ } catch (error) {
110
+ handleCommandError(error, 'test-trust');
111
+ process.exit(3);
112
+ }
113
+ }
114
+
115
+ module.exports = {
116
+ runTestTrustCommandAction,
117
+ testTrustCommandHandler
118
+ };
@@ -0,0 +1,127 @@
1
+ /**
2
+ * @fileoverview test-trust <systemKey> — semantic agent metadata validation for all datasources.
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const chalk = require('chalk');
10
+ const logger = require('../utils/logger');
11
+ const { cliOptsSkipSync } = require('../utils/cli-sync-options');
12
+ const { getConfig } = require('../core/config');
13
+ const { setupIntegrationTestAuth } = require('../external-system/test-auth');
14
+ const { runDatasourceAgentTrust } = require('../datasource/agent-trust-run');
15
+ const { resolveExternalIntegrationContext } = require('./test-e2e-external');
16
+
17
+ /**
18
+ * @param {string} systemKey
19
+ * @param {Object} options
20
+ * @returns {Promise<{ authConfig: Object, dataplaneUrl: string }|null>}
21
+ */
22
+ async function syncLocalIfRequested(systemKey, options) {
23
+ if (cliOptsSkipSync(options)) return null;
24
+ const { uploadExternalSystem } = require('./upload');
25
+ logger.log(chalk.cyan('Syncing local config to dataplane…'));
26
+ const uploadCtx = await uploadExternalSystem(systemKey, {
27
+ minimal: true,
28
+ verbose: !!options.verbose
29
+ });
30
+ logger.log(chalk.green('✔ Sync complete'));
31
+ return uploadCtx && uploadCtx.dataplaneUrl && uploadCtx.authConfig ? uploadCtx : null;
32
+ }
33
+
34
+ function pushTrustResult(results, key, trustRun, apiError) {
35
+ const failed =
36
+ apiError ||
37
+ !trustRun ||
38
+ trustRun.trustDecision === 'notTrusted' ||
39
+ trustRun.validationStatus === 'failed';
40
+ results.push({
41
+ key,
42
+ success: !failed,
43
+ error: failed
44
+ ? apiError
45
+ ? apiError.message || String(apiError)
46
+ : trustRun.summary || 'Trust validation failed'
47
+ : undefined,
48
+ trustRun
49
+ });
50
+ }
51
+
52
+ /**
53
+ * @async
54
+ * @param {Object|null} uploadCtx
55
+ * @param {string} externalSystem
56
+ * @param {Object} options
57
+ * @returns {Promise<{ authConfig: Object, dataplaneUrl: string }>}
58
+ */
59
+ async function resolveTrustAuthContext(uploadCtx, externalSystem, options) {
60
+ if (uploadCtx) {
61
+ return { authConfig: uploadCtx.authConfig, dataplaneUrl: uploadCtx.dataplaneUrl };
62
+ }
63
+ const configObj = await getConfig();
64
+ return setupIntegrationTestAuth(externalSystem, { environment: options.env }, configObj);
65
+ }
66
+
67
+ /**
68
+ * @async
69
+ * @param {string[]} keys
70
+ * @param {Object} runOpts
71
+ * @param {Array<Object>} results
72
+ */
73
+ async function runTrustForDatasourceKeys(keys, runOpts, results) {
74
+ for (const key of keys) {
75
+ try {
76
+ const { trustRun, apiError } = await runDatasourceAgentTrust(key, runOpts);
77
+ pushTrustResult(results, key, trustRun, apiError);
78
+ } catch (err) {
79
+ results.push({ key, success: false, error: err.message, trustRun: null });
80
+ }
81
+ }
82
+ }
83
+
84
+ /**
85
+ * @async
86
+ * @param {string} externalSystem
87
+ * @param {Object} options
88
+ * @returns {Promise<{ success: boolean, results: Array<Object>, systemKey: string }>}
89
+ */
90
+ async function runTestTrustForExternalSystem(externalSystem, options = {}) {
91
+ const { systemKey, keys } = resolveExternalIntegrationContext(externalSystem);
92
+ if (keys.length === 0) {
93
+ logger.log(chalk.yellow(`No datasources found for ${externalSystem}.`));
94
+ return { success: true, results: [], systemKey };
95
+ }
96
+
97
+ const uploadCtx = await syncLocalIfRequested(systemKey, options);
98
+ if (options.revalidate === true) {
99
+ logger.log(chalk.cyan('Forcing revalidation (cache bypass) for each datasource…'));
100
+ }
101
+
102
+ const { authConfig, dataplaneUrl } = await resolveTrustAuthContext(
103
+ uploadCtx,
104
+ externalSystem,
105
+ options
106
+ );
107
+ const results = [];
108
+ await runTrustForDatasourceKeys(
109
+ keys,
110
+ {
111
+ app: externalSystem,
112
+ environment: options.env,
113
+ noSync: true,
114
+ revalidate: options.revalidate === true,
115
+ summary: options.summary === true,
116
+ timeout: options.timeout,
117
+ authConfig,
118
+ dataplaneUrl
119
+ },
120
+ results
121
+ );
122
+ return { success: results.every(r => r.success), results, systemKey };
123
+ }
124
+
125
+ module.exports = {
126
+ runTestTrustForExternalSystem
127
+ };
@@ -25,6 +25,8 @@ const { getDeploymentAuth, requireBearerForDataplanePipeline } = require('../uti
25
25
  const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
26
26
  const { getIntegrationPath } = require('../utils/paths');
27
27
  const { pushCredentialSecrets } = require('../utils/credential-secrets-env');
28
+ const { runWithCredentialPushSpinner } = require('../utils/credential-push-ui');
29
+ const { runWithUploadPipelineSpinner } = require('../utils/upload-pipeline-ui');
28
30
  const {
29
31
  buildResolvedEnvMapForIntegration,
30
32
  resolveConfigurationValues,
@@ -49,6 +51,7 @@ const {
49
51
  const { maybeSyncSystemCertificationFromDataplane } = require('../certification/sync-system-certification');
50
52
  const { cliOptsSkipCertSync } = require('../certification/cli-cert-sync-skip');
51
53
  const { maybeSyncOpenApiFilesForMcp } = require('./repair-openapi-sync');
54
+ const { assertDataplaneCliVersionCompatible } = require('../utils/dataplane-cli-version-gate');
52
55
 
53
56
  /**
54
57
  * Validates system-key format (same as download).
@@ -170,13 +173,17 @@ async function resolvePayloadConfiguration(systemKey, payload) {
170
173
  * @param {string} systemKey - System key
171
174
  * @param {Object} payload - Upload payload
172
175
  */
173
- async function pushAndLogCredentialSecrets(dataplaneUrl, authConfig, systemKey, payload) {
176
+ async function pushAndLogCredentialSecrets(dataplaneUrl, authConfig, systemKey, payload, uiOpts = {}) {
174
177
  const envFilePath = path.join(getIntegrationPath(systemKey), '.env');
175
- const pushResult = await pushCredentialSecrets(dataplaneUrl, authConfig, {
176
- envFilePath,
177
- appName: systemKey,
178
- payload
179
- });
178
+ const pushResult = await runWithCredentialPushSpinner(
179
+ () =>
180
+ pushCredentialSecrets(dataplaneUrl, authConfig, {
181
+ envFilePath,
182
+ appName: systemKey,
183
+ payload
184
+ }),
185
+ { systemKey, json: uiOpts.json === true }
186
+ );
180
187
  if (pushResult.pushed > 0) {
181
188
  const keyList = pushResult.keys?.length ? ` (${pushResult.keys.join(', ')})` : '';
182
189
  logger.log(
@@ -364,8 +371,13 @@ async function handlePublicationAndFollowups(ctx) {
364
371
  * @param {Object} payload
365
372
  */
366
373
  async function runUploadPublishAndSummary(systemKey, options, manifest, payload) {
367
- const { dataplaneUrl, authConfig, environment } = await resolveDataplaneAndAuth(systemKey);
374
+ const { dataplaneUrl, authConfig, environment } = await resolveDataplaneAndAuth(systemKey, {
375
+ silent: options.silentResolve === true
376
+ });
368
377
  requireBearerForDataplanePipeline(authConfig);
378
+ const controllerUrl = await resolveControllerUrl();
379
+ // Plan 142.0: gate dataplane API entrypoints by the dataplane-advertised minimum Builder CLI version.
380
+ await assertDataplaneCliVersionCompatible(dataplaneUrl, { controllerUrl });
369
381
  logger.log('');
370
382
  logger.log(sectionTitle('Target'));
371
383
  logger.log(SEP);
@@ -375,10 +387,18 @@ async function runUploadPublishAndSummary(systemKey, options, manifest, payload)
375
387
  if (options.verbose) {
376
388
  await maybeRunVerboseServerValidation(dataplaneUrl, authConfig, payload);
377
389
  }
378
- await pushAndLogCredentialSecrets(dataplaneUrl, authConfig, systemKey, payload);
390
+ await pushAndLogCredentialSecrets(dataplaneUrl, authConfig, systemKey, payload, {
391
+ json: options.json === true
392
+ });
379
393
 
380
- const rawRes = await runUploadValidatePublish(dataplaneUrl, authConfig, payload);
381
- await logAndSyncLocalOpenApiForUpload(systemKey, manifest);
394
+ const rawRes = await runWithUploadPipelineSpinner(
395
+ async() => {
396
+ const res = await runUploadValidatePublish(dataplaneUrl, authConfig, payload);
397
+ await logAndSyncLocalOpenApiForUpload(systemKey, manifest);
398
+ return res;
399
+ },
400
+ { systemKey, json: options.json === true }
401
+ );
382
402
  await handlePublicationAndFollowups({
383
403
  systemKey,
384
404
  options,
@@ -389,6 +409,7 @@ async function runUploadPublishAndSummary(systemKey, options, manifest, payload)
389
409
  authConfig,
390
410
  rawRes
391
411
  });
412
+ return { dataplaneUrl, authConfig, environment };
392
413
  }
393
414
 
394
415
  /**
@@ -400,7 +421,7 @@ async function runUploadPublishAndSummary(systemKey, options, manifest, payload)
400
421
  * @param {boolean} [options.probe] - POST validation/run after publish
401
422
  * @param {number} [options.probeTimeout] - Probe timeout ms (default 120000)
402
423
  * @param {boolean} [options.minimal] - Short summary only
403
- * @returns {Promise<void>}
424
+ * @returns {Promise<{ dataplaneUrl: string, authConfig: Object, environment: string }|void>}
404
425
  * @throws {Error} If validation or API calls fail
405
426
  */
406
427
  async function uploadExternalSystem(systemKey, options = {}) {
@@ -417,9 +438,9 @@ async function uploadExternalSystem(systemKey, options = {}) {
417
438
  logger.log(
418
439
  chalk.gray(` System: ${manifest.key}, version: ${payload.version}, datasources: ${payload.dataSources.length}`)
419
440
  );
420
- return;
441
+ return undefined;
421
442
  }
422
- await runUploadPublishAndSummary(systemKey, options, manifest, payload);
443
+ return runUploadPublishAndSummary(systemKey, options, manifest, payload);
423
444
  }
424
445
 
425
446
  module.exports = {
@@ -525,6 +525,9 @@ async function setupDataplaneAndAuth(options, appName) {
525
525
  ' aifabrix login'
526
526
  );
527
527
  }
528
+ // Plan 142.0: gate wizard dataplane calls by the dataplane-advertised minimum Builder CLI version.
529
+ const { assertDataplaneCliVersionCompatible } = require('../utils/dataplane-cli-version-gate');
530
+ await assertDataplaneCliVersionCompatible(dataplaneUrl, { controllerUrl });
528
531
  return { dataplaneUrl, authConfig };
529
532
  }
530
533
 
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Persist dataplane version metadata under `device.<controllerUrl>` in
3
+ * `~/.aifabrix/config.yaml` for the Builder CLI compatibility gate (plan 142.0).
4
+ *
5
+ * Only public dataplane fields are written here. Token / refresh-token entries
6
+ * on the same device entry are preserved untouched.
7
+ *
8
+ * Schema additions (kebab-case, matching `developer-id` style):
9
+ * - `dataplane-version` ← health `version`
10
+ * - `dataplane-min-cli-version` ← health `minBuilderCliVersion` (omitted when not enforced)
11
+ * - `dataplane-checked-at` ← ISO timestamp for cache TTL (default 5 min)
12
+ *
13
+ * @fileoverview Cache dataplane health under existing `device.<controllerUrl>` entry
14
+ * @author AI Fabrix Team
15
+ * @version 2.0.0
16
+ */
17
+
18
+ 'use strict';
19
+
20
+ const { getConfig, saveConfig, normalizeControllerUrl } = require('./config');
21
+
22
+ const DEFAULT_CACHE_TTL_MS = 5 * 60 * 1000;
23
+
24
+ /**
25
+ * Resolve the device entry key matching `controllerUrl`, taking URL
26
+ * normalization into account so callers do not need to worry about trailing
27
+ * slashes or scheme differences.
28
+ * @private
29
+ * @param {Object} deviceMap - `config.device` map
30
+ * @param {string} normalizedUrl
31
+ * @returns {string|null} The key under `config.device` or null when missing
32
+ */
33
+ function findDeviceKey(deviceMap, normalizedUrl) {
34
+ if (!deviceMap || typeof deviceMap !== 'object') return null;
35
+ if (deviceMap[normalizedUrl]) return normalizedUrl;
36
+ for (const storedUrl of Object.keys(deviceMap)) {
37
+ if (normalizeControllerUrl(storedUrl) === normalizedUrl) {
38
+ return storedUrl;
39
+ }
40
+ }
41
+ return null;
42
+ }
43
+
44
+ /**
45
+ * Read the dataplane cache currently persisted under `device.<controllerUrl>`.
46
+ *
47
+ * @async
48
+ * @param {string} controllerUrl
49
+ * @returns {Promise<{
50
+ * version?: string,
51
+ * minBuilderCliVersion?: string,
52
+ * checkedAt?: string
53
+ * }|null>} The cached snapshot, or null when no entry exists
54
+ * @throws {Error} If controllerUrl is missing
55
+ */
56
+ async function getDeviceDataplaneVersions(controllerUrl) {
57
+ if (!controllerUrl || typeof controllerUrl !== 'string') {
58
+ throw new Error('controllerUrl is required and must be a string');
59
+ }
60
+ const config = await getConfig();
61
+ const normalizedUrl = normalizeControllerUrl(controllerUrl);
62
+ const key = findDeviceKey(config.device, normalizedUrl);
63
+ if (!key) return null;
64
+ const entry = config.device[key];
65
+ if (!entry || typeof entry !== 'object') return null;
66
+ const snapshot = {};
67
+ if (typeof entry['dataplane-version'] === 'string') {
68
+ snapshot.version = entry['dataplane-version'];
69
+ }
70
+ if (typeof entry['dataplane-min-cli-version'] === 'string') {
71
+ snapshot.minBuilderCliVersion = entry['dataplane-min-cli-version'];
72
+ }
73
+ if (typeof entry['dataplane-checked-at'] === 'string') {
74
+ snapshot.checkedAt = entry['dataplane-checked-at'];
75
+ }
76
+ if (Object.keys(snapshot).length === 0) return null;
77
+ return snapshot;
78
+ }
79
+
80
+ /**
81
+ * Apply the `dataplane-version` field to a device entry in-place.
82
+ * @private
83
+ * @param {Object} entry
84
+ * @param {string|null|undefined} version
85
+ */
86
+ function applyDataplaneVersionField(entry, version) {
87
+ if (typeof version === 'string' && version.trim()) {
88
+ entry['dataplane-version'] = version.trim();
89
+ } else if (version === null || version === '') {
90
+ delete entry['dataplane-version'];
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Apply the `dataplane-min-cli-version` field to a device entry in-place.
96
+ * Treat absent / empty as "no enforcement" → clear the cached floor so an
97
+ * older value never lingers after the operator removes the env variable.
98
+ * @private
99
+ * @param {Object} entry
100
+ * @param {string|null|undefined} minCli
101
+ */
102
+ function applyMinCliField(entry, minCli) {
103
+ if (typeof minCli === 'string' && minCli.trim()) {
104
+ entry['dataplane-min-cli-version'] = minCli.trim();
105
+ return;
106
+ }
107
+ if (minCli === null || minCli === '' || minCli === undefined) {
108
+ delete entry['dataplane-min-cli-version'];
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Compute the timestamp persisted as `dataplane-checked-at`.
114
+ * @private
115
+ * @param {string|undefined} checkedAt
116
+ * @returns {string} ISO timestamp
117
+ */
118
+ function resolveCheckedAt(checkedAt) {
119
+ if (typeof checkedAt === 'string' && checkedAt.trim()) {
120
+ return checkedAt.trim();
121
+ }
122
+ return new Date().toISOString();
123
+ }
124
+
125
+ /**
126
+ * Persist dataplane version metadata under the device entry for `controllerUrl`.
127
+ *
128
+ * Token / refresh-token entries are preserved; only the `dataplane-*` keys are
129
+ * written. When `versions.minBuilderCliVersion` is empty/undefined the cache
130
+ * key is removed (dataplane does not enforce a minimum).
131
+ *
132
+ * @async
133
+ * @param {string} controllerUrl - Controller URL (will be normalized)
134
+ * @param {{
135
+ * version?: string,
136
+ * minBuilderCliVersion?: string|null,
137
+ * checkedAt?: string
138
+ * }} versions
139
+ * @returns {Promise<void>}
140
+ * @throws {Error} If controllerUrl is missing
141
+ */
142
+ async function updateDeviceDataplaneVersions(controllerUrl, versions = {}) {
143
+ if (!controllerUrl || typeof controllerUrl !== 'string') {
144
+ throw new Error('controllerUrl is required and must be a string');
145
+ }
146
+ const config = await getConfig();
147
+ if (!config.device || typeof config.device !== 'object') {
148
+ config.device = {};
149
+ }
150
+ const normalizedUrl = normalizeControllerUrl(controllerUrl);
151
+ const key = findDeviceKey(config.device, normalizedUrl) || normalizedUrl;
152
+ if (!config.device[key] || typeof config.device[key] !== 'object') {
153
+ config.device[key] = {};
154
+ }
155
+ const entry = config.device[key];
156
+
157
+ applyDataplaneVersionField(entry, versions.version);
158
+ applyMinCliField(entry, versions.minBuilderCliVersion);
159
+ entry['dataplane-checked-at'] = resolveCheckedAt(versions.checkedAt);
160
+
161
+ await saveConfig(config);
162
+ }
163
+
164
+ /**
165
+ * True when the cached snapshot is older than the configured TTL (or missing).
166
+ *
167
+ * @param {{checkedAt?: string}|null} snapshot
168
+ * @param {number} [ttlMs] - TTL in milliseconds (defaults to 5 minutes)
169
+ * @param {Date|number} [now] - Override "now" for tests
170
+ * @returns {boolean}
171
+ */
172
+ function isCacheStale(snapshot, ttlMs = DEFAULT_CACHE_TTL_MS, now = Date.now()) {
173
+ if (!snapshot || !snapshot.checkedAt) return true;
174
+ const checkedAtMs = Date.parse(snapshot.checkedAt);
175
+ if (Number.isNaN(checkedAtMs)) return true;
176
+ const reference = typeof now === 'number' ? now : now.getTime();
177
+ return reference - checkedAtMs >= ttlMs;
178
+ }
179
+
180
+ module.exports = {
181
+ getDeviceDataplaneVersions,
182
+ updateDeviceDataplaneVersions,
183
+ isCacheStale,
184
+ DEFAULT_CACHE_TTL_MS
185
+ };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @fileoverview Debug log writer for agent trust CLI runs (plan 143).
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const chalk = require('chalk');
8
+ const path = require('path');
9
+ const logger = require('../utils/logger');
10
+ const { getIntegrationPath } = require('../utils/paths');
11
+ const { writeTestLog } = require('../utils/test-log-writer');
12
+
13
+ /**
14
+ * @async
15
+ * @param {string} appKey - Integration folder key
16
+ * @param {string} datasourceKey
17
+ * @param {Object} payload
18
+ * @returns {Promise<string|null>} Log path or null when skipped
19
+ */
20
+ async function writeTrustDebugLog(appKey, datasourceKey, payload) {
21
+ const logPath = await writeTestLog(
22
+ appKey,
23
+ payload,
24
+ `test-trust-${datasourceKey}`,
25
+ path.dirname(getIntegrationPath(appKey))
26
+ );
27
+ return logPath;
28
+ }
29
+
30
+ /**
31
+ * @async
32
+ * @param {string} appKey
33
+ * @param {string} datasourceKey
34
+ * @param {Object} payload
35
+ */
36
+ async function writeTrustDebugLogAndPrint(appKey, datasourceKey, payload) {
37
+ const logPath = await writeTrustDebugLog(appKey, datasourceKey, payload);
38
+ logger.log(chalk.gray(` Debug log: ${logPath}`));
39
+ return logPath;
40
+ }
41
+
42
+ module.exports = {
43
+ writeTrustDebugLog,
44
+ writeTrustDebugLogAndPrint
45
+ };