@aifabrix/builder 2.45.5 → 2.46.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 (231) hide show
  1. package/.cursor/rules/cli-layout.mdc +2 -0
  2. package/.cursor/rules/cli-product-neutral.mdc +43 -0
  3. package/.cursor/rules/project-rules.mdc +1 -0
  4. package/README.md +43 -17
  5. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
  6. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
  7. package/jest.config.default.js +2 -1
  8. package/jest.config.isolated.js +2 -1
  9. package/jest.global-live-fabrix-hooks.js +36 -0
  10. package/jest.isolated-projects.js +130 -0
  11. package/jest.projects.js +41 -136
  12. package/lib/api/api-response-errors.js +124 -0
  13. package/lib/api/governance-scenario-pack.api.js +159 -0
  14. package/lib/api/index.js +43 -3
  15. package/lib/api/lifecycle.api.js +77 -0
  16. package/lib/api/pipeline.api.js +3 -1
  17. package/lib/api/records-bulk.api.js +14 -1
  18. package/lib/api/validation-runner.js +3 -1
  19. package/lib/app/deploy-config.js +4 -1
  20. package/lib/app/down.js +20 -1
  21. package/lib/app/push.js +4 -5
  22. package/lib/app/register.js +7 -4
  23. package/lib/app/run-env-compose.js +84 -30
  24. package/lib/build/index.js +5 -0
  25. package/lib/certification/sync-system-certification.js +13 -6
  26. package/lib/cli/index.js +4 -0
  27. package/lib/cli/infra-guided-auth.js +109 -0
  28. package/lib/cli/infra-guided.js +84 -81
  29. package/lib/cli/setup-app.help.js +0 -31
  30. package/lib/cli/setup-app.js +28 -2
  31. package/lib/cli/setup-app.test-commands.js +5 -57
  32. package/lib/cli/setup-auth.js +25 -0
  33. package/lib/cli/setup-certification-commands.js +147 -0
  34. package/lib/cli/setup-certification.help.js +141 -0
  35. package/lib/cli/setup-environment.js +19 -2
  36. package/lib/cli/setup-external-system-help.js +62 -0
  37. package/lib/cli/setup-external-system.js +25 -3
  38. package/lib/cli/setup-infra.js +9 -1
  39. package/lib/cli/setup-platform.js +6 -1
  40. package/lib/cli/setup-secrets.js +33 -1
  41. package/lib/cli/setup-utility-help-after.js +22 -2
  42. package/lib/cli/setup-utility-repair.js +198 -0
  43. package/lib/cli/setup-utility-resolve.js +40 -4
  44. package/lib/cli/setup-utility-validate.js +197 -0
  45. package/lib/cli/setup-utility.js +4 -239
  46. package/lib/commands/auth-status-client-tokens.js +214 -0
  47. package/lib/commands/auth-status-dataplane-version.js +58 -20
  48. package/lib/commands/auth-status.js +41 -47
  49. package/lib/commands/datasource-test-trust-cli.js +27 -23
  50. package/lib/commands/datasource-unified-test-cli.js +6 -3
  51. package/lib/commands/datasource-unified-test-cli.options.js +5 -1
  52. package/lib/commands/datasource.js +7 -7
  53. package/lib/commands/dev-show-display.js +8 -1
  54. package/lib/commands/governance-cli-shared.js +94 -0
  55. package/lib/commands/governance-command-action.js +132 -0
  56. package/lib/commands/governance-download-external.js +34 -0
  57. package/lib/commands/governance-generate-external.js +71 -0
  58. package/lib/commands/governance-upload-external.js +36 -0
  59. package/lib/commands/governance-verify-external.js +64 -0
  60. package/lib/commands/governance.js +144 -0
  61. package/lib/commands/lifecycle-command-action.js +123 -0
  62. package/lib/commands/lifecycle-external.js +268 -0
  63. package/lib/commands/login.js +11 -2
  64. package/lib/commands/repair-auth-apply.js +127 -0
  65. package/lib/commands/repair-changed-files.js +41 -0
  66. package/lib/commands/repair-datasource-keys.js +4 -1
  67. package/lib/commands/repair-datasource-run.js +59 -0
  68. package/lib/commands/repair-datasource-test-payload.js +259 -0
  69. package/lib/commands/repair-datasource.js +1 -51
  70. package/lib/commands/repair-env-template.js +64 -13
  71. package/lib/commands/repair-external-run.js +216 -0
  72. package/lib/commands/repair-openapi-sync.js +23 -0
  73. package/lib/commands/repair-persist.js +4 -0
  74. package/lib/commands/repair-rbac.js +52 -1
  75. package/lib/commands/repair-steps.js +145 -0
  76. package/lib/commands/repair.js +29 -296
  77. package/lib/commands/secrets-get.js +184 -0
  78. package/lib/commands/secrets-remove-all.js +3 -1
  79. package/lib/commands/setup-image-refresh.js +146 -0
  80. package/lib/commands/setup-modes.js +158 -162
  81. package/lib/commands/setup-platform-auth.js +76 -0
  82. package/lib/commands/setup-prompts-admin.js +244 -0
  83. package/lib/commands/setup-prompts-platform-mode.js +67 -0
  84. package/lib/commands/setup-prompts-validators.js +36 -0
  85. package/lib/commands/setup-prompts.js +24 -76
  86. package/lib/commands/setup-run-context.js +45 -0
  87. package/lib/commands/setup-spinners.js +39 -0
  88. package/lib/commands/setup.js +27 -5
  89. package/lib/commands/teardown.js +39 -0
  90. package/lib/commands/test-e2e-external.js +24 -8
  91. package/lib/commands/test-governance-external.js +3 -24
  92. package/lib/commands/test-trust-external.js +20 -21
  93. package/lib/commands/up-common.js +25 -13
  94. package/lib/commands/up-dataplane.js +50 -13
  95. package/lib/commands/upload-publish-context.js +75 -0
  96. package/lib/commands/upload-sidecar-followups.js +89 -0
  97. package/lib/commands/upload.js +90 -108
  98. package/lib/commands/verify-governance-combined-external.js +61 -0
  99. package/lib/commands/verify-governance-command-action.js +64 -0
  100. package/lib/commands/verify-governance-db.js +102 -0
  101. package/lib/commands/verify-governance-external.js +77 -0
  102. package/lib/commands/verify-governance-result-builders.js +71 -0
  103. package/lib/commands/verify-operations-command-action.js +68 -0
  104. package/lib/commands/verify-operations-external.js +268 -0
  105. package/lib/commands/verify-operations-persist.js +75 -0
  106. package/lib/commands/verify-operations-step-plan.js +68 -0
  107. package/lib/commands/verify-operations-steps.js +111 -0
  108. package/lib/commands/verify-trust-command-action.js +70 -0
  109. package/lib/commands/verify-trust-external.js +67 -0
  110. package/lib/commands/wizard-config-normalizer.js +43 -23
  111. package/lib/commands/wizard-core.js +2 -0
  112. package/lib/core/admin-password-generator.js +38 -0
  113. package/lib/core/admin-secrets-bundle.js +111 -0
  114. package/lib/core/admin-secrets-env-overlay.js +62 -0
  115. package/lib/core/admin-secrets.js +32 -4
  116. package/lib/core/config-attach-extensions.js +3 -0
  117. package/lib/core/config-platform-controller.js +44 -0
  118. package/lib/core/config-setup-installation-profile.js +42 -0
  119. package/lib/core/config-setup-platform-mode.js +46 -0
  120. package/lib/core/config.js +46 -36
  121. package/lib/core/secrets-admin-env.js +6 -2
  122. package/lib/core/secrets-ensure.js +4 -0
  123. package/lib/core/secrets-env-content.js +54 -48
  124. package/lib/core/secrets-env-write.js +10 -1
  125. package/lib/core/secrets-infra-placeholder-sync.js +2 -0
  126. package/lib/core/setup-installation-profile.js +43 -0
  127. package/lib/core/setup-platform-mode.js +55 -0
  128. package/lib/datasource/agent-trust-debug-log.js +1 -1
  129. package/lib/datasource/agent-trust-run.js +5 -3
  130. package/lib/datasource/audit-evidence-extract-debug.js +30 -18
  131. package/lib/datasource/audit-evidence-extract.js +7 -4
  132. package/lib/datasource/audit-evidence-verify-matrix.js +54 -26
  133. package/lib/datasource/bulk-loader-run-batches.js +15 -3
  134. package/lib/datasource/datasource-load-export-context.js +3 -0
  135. package/lib/datasource/log-cleaner.js +2 -2
  136. package/lib/datasource/log-viewer.js +8 -8
  137. package/lib/datasource/test-e2e.js +4 -1
  138. package/lib/datasource/unified-validation-run-body.js +3 -1
  139. package/lib/datasource/unified-validation-run.js +5 -2
  140. package/lib/deployment/environment-poll-status.js +138 -0
  141. package/lib/deployment/environment.js +42 -34
  142. package/lib/external-system/download.js +21 -20
  143. package/lib/external-system/generator.js +5 -1
  144. package/lib/external-system/integration-auth-context.js +45 -0
  145. package/lib/external-system/test-auth.js +12 -5
  146. package/lib/external-system/test.js +6 -6
  147. package/lib/generator/external-controller-manifest.js +15 -0
  148. package/lib/governance/governance-cli-errors.js +96 -0
  149. package/lib/governance/governance-download-display.js +38 -0
  150. package/lib/governance/governance-run-display.js +215 -25
  151. package/lib/infrastructure/admin-secrets-apply.js +84 -0
  152. package/lib/infrastructure/admin-secrets-ensure.js +130 -0
  153. package/lib/infrastructure/admin-secrets-resolve.js +95 -0
  154. package/lib/infrastructure/admin-secrets-sync.js +91 -0
  155. package/lib/infrastructure/helpers.js +23 -129
  156. package/lib/infrastructure/index.js +5 -6
  157. package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
  158. package/lib/lifecycle/certification-progress.js +231 -0
  159. package/lib/lifecycle/dataplane-ui-url.js +145 -0
  160. package/lib/lifecycle/exit-codes.js +24 -0
  161. package/lib/lifecycle/json-envelope.js +25 -0
  162. package/lib/lifecycle/lifecycle-governance-options.js +51 -0
  163. package/lib/lifecycle/logger-quiet.js +29 -0
  164. package/lib/lifecycle/open-api-published.js +59 -0
  165. package/lib/lifecycle/operations-readiness-metrics.js +101 -0
  166. package/lib/lifecycle/product-model.js +196 -0
  167. package/lib/lifecycle/report-display.js +471 -0
  168. package/lib/lifecycle/scenario-upload.js +191 -0
  169. package/lib/lifecycle/verify-step-progress.js +87 -0
  170. package/lib/parameters/infra-kv-discovery.js +27 -2
  171. package/lib/protection/auth-context.js +10 -4
  172. package/lib/schema/application-schema.json +1 -1
  173. package/lib/schema/datasource-test-run.schema.json +89 -6
  174. package/lib/schema/external-datasource.schema.json +2636 -2622
  175. package/lib/schema/external-system.schema.json +1089 -761
  176. package/lib/schema/infra.parameter.yaml +45 -0
  177. package/lib/schema/type/credential-field-registry.json +103 -97
  178. package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
  179. package/lib/utils/api.js +55 -31
  180. package/lib/utils/app-relative-path-env.js +112 -0
  181. package/lib/utils/cli-secrets-error-format.js +83 -21
  182. package/lib/utils/cli-utils.js +16 -4
  183. package/lib/utils/compose-generate-docker-compose.js +4 -0
  184. package/lib/utils/compose-traefik-ingress-base.js +16 -1
  185. package/lib/utils/config-secrets-path.js +46 -0
  186. package/lib/utils/controller-url.js +88 -1
  187. package/lib/utils/credential-secrets-env.js +137 -9
  188. package/lib/utils/dataplane-resolver.js +129 -2
  189. package/lib/utils/declarative-url-ports.js +3 -3
  190. package/lib/utils/deployment-auth-mode.js +41 -0
  191. package/lib/utils/developer-volume-id.js +33 -0
  192. package/lib/utils/env-copy.js +14 -9
  193. package/lib/utils/env-kv-orphan-lines.js +43 -0
  194. package/lib/utils/error-formatters/network-errors.js +13 -4
  195. package/lib/utils/external-env-template.js +1 -0
  196. package/lib/utils/external-system-readiness-core.js +10 -0
  197. package/lib/utils/external-system-readiness-display.js +80 -23
  198. package/lib/utils/health-check.js +4 -1
  199. package/lib/utils/help-builder.js +11 -8
  200. package/lib/utils/keycloak-realm-ready.js +101 -0
  201. package/lib/utils/load-cip-capacity-display-config.js +61 -15
  202. package/lib/utils/manifest-source-emit.js +2 -0
  203. package/lib/utils/pipeline-upload-auth-retry.js +82 -0
  204. package/lib/utils/platform-controller-url.js +156 -0
  205. package/lib/utils/postgres-platform-bootstrap.js +199 -0
  206. package/lib/utils/postgres-wipe.js +9 -2
  207. package/lib/utils/resolve-env-mode.js +47 -0
  208. package/lib/utils/secrets-generator.js +31 -25
  209. package/lib/utils/secrets-missing-error.js +164 -0
  210. package/lib/utils/service-url-label.js +81 -0
  211. package/lib/utils/setup-quiet-output.js +25 -0
  212. package/lib/utils/template-helpers.js +15 -0
  213. package/lib/utils/token-manager-client-credentials.js +201 -0
  214. package/lib/utils/token-manager-messages.js +8 -0
  215. package/lib/utils/token-manager-refresh.js +12 -2
  216. package/lib/utils/token-manager.js +86 -105
  217. package/lib/utils/upload-pipeline-ui.js +12 -5
  218. package/lib/utils/upload-sync-options.js +42 -0
  219. package/lib/utils/url-declarative-public-base.js +21 -3
  220. package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
  221. package/lib/utils/validation-poll-ui.js +8 -0
  222. package/lib/utils/validation-run-stage.js +45 -0
  223. package/package.json +1 -1
  224. package/templates/applications/dataplane/application.yaml +2 -1
  225. package/templates/applications/dataplane/env.template +24 -1
  226. package/templates/applications/miso-controller/env.template +16 -0
  227. package/templates/external-system/README.md.hbs +74 -17
  228. package/templates/python/docker-compose.hbs +19 -18
  229. package/templates/typescript/docker-compose.hbs +18 -17
  230. package/lib/commands/test-governance-command-action.js +0 -55
  231. package/lib/commands/test-trust-command-action.js +0 -118
@@ -11,8 +11,6 @@
11
11
  const chalk = require('chalk');
12
12
  const { failureGlyph, successGlyph } = require('../utils/cli-test-layout-chalk');
13
13
  const logger = require('../utils/logger');
14
- const config = require('../core/config');
15
- const { getConfig } = config;
16
14
  const { getOrRefreshDeviceToken } = require('../utils/token-manager');
17
15
  const { getAuthUser } = require('../api/auth.api');
18
16
  const { resolveControllerUrl } = require('../utils/controller-url');
@@ -32,6 +30,11 @@ const {
32
30
  EXIT_CLI_VERSION_INCOMPATIBLE,
33
31
  EXIT_OK
34
32
  } = require('../utils/cli-exit-codes');
33
+ const {
34
+ refreshExpiredClientTokensWherePossible,
35
+ displayStoredClientTokensSection,
36
+ tryResolveClientTokenAuth
37
+ } = require('./auth-status-client-tokens');
35
38
 
36
39
  /**
37
40
  * Format expiration date for display
@@ -94,22 +97,6 @@ async function checkDeviceToken(controllerUrl) {
94
97
  }
95
98
  }
96
99
 
97
- /**
98
- * Decrypt token if encrypted
99
- * @async
100
- * @param {string} token - Token to decrypt
101
- * @returns {Promise<string>} Decrypted token
102
- */
103
- async function decryptTokenIfNeeded(token) {
104
- const { decryptToken, isTokenEncrypted } = require('../utils/token-encryption');
105
- const encryptionKey = await config.getSecretsEncryptionKey();
106
-
107
- if (encryptionKey && isTokenEncrypted(token)) {
108
- return await decryptToken(token, encryptionKey);
109
- }
110
- return token;
111
- }
112
-
113
100
  /**
114
101
  * Validate client token and return result
115
102
  * @async
@@ -156,29 +143,14 @@ async function validateClientToken(token, controllerUrl, environment, appName, e
156
143
  }
157
144
 
158
145
  /**
159
- * Check and validate client token
146
+ * Check and validate client token (refreshes when expired if client credentials exist).
160
147
  * @async
161
148
  * @param {string} controllerUrl - Controller URL
162
149
  * @param {string} environment - Environment key
163
150
  * @returns {Promise<Object|null>} Token validation result or null
164
151
  */
165
152
  async function checkClientToken(controllerUrl, environment) {
166
- const configData = await getConfig();
167
- const environments = configData.environments || {};
168
- const envConfig = environments[environment];
169
-
170
- if (!envConfig || !envConfig.clients) {
171
- return null;
172
- }
173
-
174
- for (const [appName, tokenData] of Object.entries(envConfig.clients)) {
175
- if (tokenData.controller === controllerUrl && tokenData.token) {
176
- const token = await decryptTokenIfNeeded(tokenData.token);
177
- return await validateClientToken(token, controllerUrl, environment, appName, tokenData.expiresAt);
178
- }
179
- }
180
-
181
- return null;
153
+ return tryResolveClientTokenAuth(controllerUrl, environment, validateClientToken);
182
154
  }
183
155
 
184
156
  /**
@@ -382,18 +354,34 @@ async function buildDataplaneInfo(controllerUrl, environment, authConfig) {
382
354
  * @param {boolean} [options.validate] - If true, exit with code 1 when not authenticated, or code 3 when CLI is older than dataplane minimum
383
355
  * @returns {Promise<void>} Resolves when status is displayed
384
356
  */
357
+ /**
358
+ * Prefer a working device token; fall back to environment client credentials when the
359
+ * stored device token is expired or rejected (common in long-lived dev VMs).
360
+ *
361
+ * @async
362
+ * @param {string} controllerUrl
363
+ * @param {string} environment
364
+ * @returns {Promise<Object|null>}
365
+ */
366
+ async function resolveAuthenticatedTokenInfo(controllerUrl, environment) {
367
+ const deviceResult = await checkDeviceToken(controllerUrl);
368
+ if (deviceResult && deviceResult.authenticated) {
369
+ return deviceResult;
370
+ }
371
+ const clientResult = await checkClientToken(controllerUrl, environment);
372
+ if (clientResult && clientResult.authenticated) {
373
+ return clientResult;
374
+ }
375
+ return deviceResult || clientResult || null;
376
+ }
377
+
385
378
  async function handleAuthStatus(options = {}) {
386
379
  const { resolveEnvironment } = require('../core/config');
387
380
  const controllerUrl = await resolveControllerUrl();
388
381
  const environment = await resolveEnvironment();
389
382
 
390
- // Check device token first (preferred)
391
- let tokenInfo = await checkDeviceToken(controllerUrl);
392
-
393
- // If no device token, check client token
394
- if (!tokenInfo) {
395
- tokenInfo = await checkClientToken(controllerUrl, environment);
396
- }
383
+ const refreshedCount = await refreshExpiredClientTokensWherePossible(environment, controllerUrl);
384
+ const tokenInfo = await resolveAuthenticatedTokenInfo(controllerUrl, environment);
397
385
 
398
386
  let dataplaneInfo = null;
399
387
  if (tokenInfo && tokenInfo.authenticated && tokenInfo.token) {
@@ -402,6 +390,7 @@ async function handleAuthStatus(options = {}) {
402
390
  }
403
391
 
404
392
  displayStatus(controllerUrl, environment, tokenInfo, dataplaneInfo);
393
+ await displayStoredClientTokensSection(environment, controllerUrl, refreshedCount);
405
394
 
406
395
  if (options.validate) {
407
396
  if (!tokenInfo || !tokenInfo.authenticated) {
@@ -427,10 +416,7 @@ async function getValidatedAuthContext() {
427
416
  const controllerUrl = await resolveControllerUrl();
428
417
  const environment = await resolveEnvironment();
429
418
 
430
- let tokenInfo = await checkDeviceToken(controllerUrl);
431
- if (!tokenInfo) {
432
- tokenInfo = await checkClientToken(controllerUrl, environment);
433
- }
419
+ const tokenInfo = await resolveAuthenticatedTokenInfo(controllerUrl, environment);
434
420
 
435
421
  if (!tokenInfo || !tokenInfo.authenticated || !tokenInfo.token) {
436
422
  throw new Error('Not authenticated. Run "aifabrix login" or configure client credentials.');
@@ -442,4 +428,12 @@ async function getValidatedAuthContext() {
442
428
  return { controllerUrl, environment, authConfig, dataplaneUrl };
443
429
  }
444
430
 
445
- module.exports = { handleAuthStatus, getValidatedAuthContext };
431
+ module.exports = {
432
+ handleAuthStatus,
433
+ getValidatedAuthContext,
434
+ resolveAuthenticatedTokenInfo,
435
+ checkDeviceToken,
436
+ checkClientToken,
437
+ refreshExpiredClientTokensWherePossible,
438
+ displayStoredClientTokensSection
439
+ };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview CLI wiring for `datasource test-trust` (404.5 / plan 143).
2
+ * @fileoverview CLI wiring for `datasource verify-trust` (404.5 / plan 143).
3
3
  * @author AI Fabrix Team
4
4
  * @version 1.0.0
5
5
  */
@@ -15,26 +15,27 @@ const { computeExitCodeFromTrustRun } = require('../utils/agent-trust-run-exit')
15
15
  const { attachDatasourceTestCommonOptions } = require('./datasource-unified-test-cli.options');
16
16
  const { cliOptsSkipSync } = require('../utils/cli-sync-options');
17
17
 
18
- function datasourceTestTrustHelpAfter() {
18
+ function datasourceVerifyTrustHelpAfter() {
19
19
  return `
20
20
  Examples:
21
- $ aifabrix datasource test-trust hubspot-companies
22
- $ aifabrix datasource test-trust hubspot-companies --app test-e2e-hubspot -v
23
- $ aifabrix datasource test-trust hubspot-companies --revalidate --strict
24
- $ aifabrix test-trust test-e2e-hubspot
21
+ $ aifabrix datasource verify-trust hubspot-companies
22
+ $ aifabrix datasource verify-trust hubspot-companies --app test-e2e-hubspot -v
23
+ $ aifabrix datasource verify-trust hubspot-companies --revalidate --strict
24
+ $ aifabrix verify-trust test-e2e-hubspot
25
25
 
26
26
  Notes:
27
27
  - Semantic trust only (agent metadata validation). For live API proof use test-e2e.
28
28
  - Local files are published before run unless --no-sync.
29
- - With --debug, JSON is saved under integration/<app>/logs/ (test-trust-<key>-*.json). View with: aifabrix datasource log-trust <key>
29
+ - With --debug, JSON is saved under integration/<app>/logs/ (verify-trust-<key>-*.json). View with: aifabrix datasource log-trust <key>
30
30
  `;
31
31
  }
32
32
 
33
- async function runDatasourceTestTrustOnce(datasourceKey, options) {
33
+ async function runDatasourceVerifyTrustOnce(datasourceKey, options) {
34
34
  const { trustRun, apiError } = await runDatasourceAgentTrust(datasourceKey, {
35
35
  app: options.app,
36
36
  environment: options.env,
37
37
  noSync: options.noSync === true,
38
+ force: options.force === true,
38
39
  revalidate: options.revalidate === true,
39
40
  summary: options.summary === true,
40
41
  timeout: options.timeout
@@ -42,7 +43,7 @@ async function runDatasourceTestTrustOnce(datasourceKey, options) {
42
43
  return { trustRun, apiError };
43
44
  }
44
45
 
45
- async function finalizeDatasourceTestTrust(datasourceKey, options, result) {
46
+ async function finalizeDatasourceVerifyTrust(datasourceKey, options, result) {
46
47
  const displayOpts = {
47
48
  json: options.json,
48
49
  verbose: options.verbose,
@@ -67,10 +68,10 @@ async function finalizeDatasourceTestTrust(datasourceKey, options, result) {
67
68
  return computeExitCodeFromTrustRun(result.trustRun, exitOpts);
68
69
  }
69
70
 
70
- async function datasourceTestTrustAction(datasourceKey, options, cmd) {
71
+ async function datasourceVerifyTrustAction(datasourceKey, options, cmd) {
71
72
  try {
72
73
  const merged = { ...options, noSync: cliOptsSkipSync(options, cmd) };
73
- const result = await runDatasourceTestTrustOnce(datasourceKey, merged);
74
+ const result = await runDatasourceVerifyTrustOnce(datasourceKey, merged);
74
75
  const { resolveAppKeyForDatasource } = require('../datasource/resolve-app');
75
76
  const { appKey } = await resolveAppKeyForDatasource(datasourceKey, options.app);
76
77
  if (options.debug) {
@@ -80,7 +81,7 @@ async function datasourceTestTrustAction(datasourceKey, options, cmd) {
80
81
  error: result.apiError ? result.apiError.message : null
81
82
  });
82
83
  }
83
- const exitCode = await finalizeDatasourceTestTrust(datasourceKey, merged, result);
84
+ const exitCode = await finalizeDatasourceVerifyTrust(datasourceKey, merged, result);
84
85
  process.exit(exitCode);
85
86
  } catch (err) {
86
87
  logger.error(formatBlockingError('Agent trust validation failed:'), err.message);
@@ -88,10 +89,7 @@ async function datasourceTestTrustAction(datasourceKey, options, cmd) {
88
89
  }
89
90
  }
90
91
 
91
- function setupDatasourceTestTrustCommand(datasource) {
92
- const cmd = datasource
93
- .command('test-trust <datasourceKey>')
94
- .description('Semantic agent metadata validation for one datasource (dataplane 404.5)');
92
+ function attachDatasourceTrustOptions(cmd) {
95
93
  attachDatasourceTestCommonOptions(cmd, {
96
94
  includeNoAsync: false,
97
95
  includePayload: false,
@@ -101,14 +99,20 @@ function setupDatasourceTestTrustCommand(datasource) {
101
99
  cmd
102
100
  .option('--no-sync', 'Skip publishing local integration files before validate')
103
101
  .option('--revalidate', 'Force new validation (ignore inputHash cache)')
104
- .option('--strict', 'Exit 1 unless trustDecision is trusted')
105
- .addHelpText('after', datasourceTestTrustHelpAfter())
106
- .action(datasourceTestTrustAction);
102
+ .option('--strict', 'Exit 1 unless trustDecision is trusted');
103
+ }
104
+
105
+ function setupDatasourceVerifyTrustCommand(datasource) {
106
+ const cmd = datasource
107
+ .command('verify-trust <datasourceKey>')
108
+ .description('Semantic agent metadata validation for one datasource');
109
+ attachDatasourceTrustOptions(cmd);
110
+ cmd.addHelpText('after', datasourceVerifyTrustHelpAfter()).action(datasourceVerifyTrustAction);
107
111
  }
108
112
 
109
113
  module.exports = {
110
- setupDatasourceTestTrustCommand,
111
- runDatasourceTestTrustOnce,
112
- finalizeDatasourceTestTrust,
113
- datasourceTestTrustAction
114
+ setupDatasourceVerifyTrustCommand,
115
+ runDatasourceVerifyTrustOnce,
116
+ finalizeDatasourceVerifyTrust,
117
+ datasourceVerifyTrustAction
114
118
  };
@@ -77,7 +77,8 @@ function buildDatasourceTestRunOpts(options) {
77
77
  timeout: options.timeout,
78
78
  async: options.async !== false,
79
79
  noAsync: options.async === false,
80
- sync: options.sync === true
80
+ sync: options.sync === true,
81
+ force: options.force === true
81
82
  };
82
83
  }
83
84
 
@@ -174,7 +175,8 @@ function buildIntegrationTestOpts(options) {
174
175
  debug: options.debug,
175
176
  verbose: options.verbose,
176
177
  timeout: options.timeout,
177
- sync: options.sync === true
178
+ sync: options.sync === true,
179
+ force: options.force === true
178
180
  };
179
181
  }
180
182
 
@@ -335,7 +337,8 @@ async function runDatasourceTestE2ECliOnce(datasourceKey, options) {
335
337
  minRecordCount: options.minRecordCount,
336
338
  timeout: options.timeout,
337
339
  capabilityKey: options.capabilityKey,
338
- sync: options.sync === true
340
+ sync: options.sync === true,
341
+ force: options.force === true
339
342
  });
340
343
  const unifiedModes =
341
344
  options.json || options.summary || options.warningsAsErrors || options.requireCert;
@@ -44,7 +44,7 @@ Examples:
44
44
  $ aifabrix datasource test-e2e hubspot-users --app test-e2e-hubspot -v --debug
45
45
  $ aifabrix datasource test-e2e my-documents-ds --min-vector-hits 7 -v --debug
46
46
  $ aifabrix datasource test-e2e hubspot-users -a test-e2e-hubspot --no-async
47
- $ aifabrix datasource test-e2e hubspot-users --sync --verify-audit -v
47
+ $ aifabrix datasource test-e2e hubspot-users --sync --force --verify-audit -v
48
48
  $ aifabrix datasource test-e2e hubspot-users read
49
49
 
50
50
  Notes:
@@ -129,6 +129,10 @@ function attachDatasourceTestCommonOptions(cmd, opts) {
129
129
  '--sync',
130
130
  'Publish local integration config to the dataplane before running the test (same as aifabrix upload <systemKey>; requires login)'
131
131
  )
132
+ .option(
133
+ '--force',
134
+ 'With --sync, pass force to upload (bypass active-datasource schema replacement guards; same as aifabrix upload --force)'
135
+ )
132
136
  .option('--json', 'Print raw DatasourceTestRun JSON to stdout')
133
137
  .option('--summary', 'Print compact summary line')
134
138
  .option('--warnings-as-errors', 'Exit 1 when root status is warn')
@@ -2,7 +2,7 @@
2
2
  * AI Fabrix Builder - Datasource Commands
3
3
  *
4
4
  * Handles datasource validation, listing, comparison, deployment, and online validation runs.
5
- * Subcommands `test`, `test-integration`, `test-e2e`, and `test-trust` call the dataplane; `log-*` read saved debug JSON locally; `clean-logs` removes those files. Permissions are summarized in `docs/commands/permissions.md`.
5
+ * Subcommands `test`, `test-integration`, `test-e2e`, and `verify-trust` call the dataplane; `log-*` read saved debug JSON locally; `clean-logs` removes those files. Permissions are summarized in `docs/commands/permissions.md`.
6
6
  *
7
7
  * @fileoverview Datasource management commands for AI Fabrix Builder
8
8
  * @author AI Fabrix Team
@@ -24,7 +24,7 @@ const {
24
24
  setupDatasourceTestIntegrationCommand,
25
25
  setupDatasourceTestE2ECommand
26
26
  } = require('./datasource-unified-test-cli');
27
- const { setupDatasourceTestTrustCommand } = require('./datasource-test-trust-cli');
27
+ const { setupDatasourceVerifyTrustCommand } = require('./datasource-test-trust-cli');
28
28
  const { setupDatasourceVerifyAuditCommand } = require('./datasource-verify-audit-cli');
29
29
  const { setupDatasourceLoadExportCommands } = require('./datasource-load-export-cli');
30
30
  const { setupDatasourceCapabilityCommands } = require('./datasource-capability');
@@ -44,7 +44,7 @@ Examples:
44
44
  $ af ds test-e2e <datasourceKey> --app <integrationFolder> -v
45
45
  $ af ds test-e2e <datasourceKey> --verify-audit
46
46
  $ af ds verify-audit <datasourceKey>
47
- $ af ds test-trust <datasourceKey> --app <integrationFolder> -v
47
+ $ af ds verify-trust <datasourceKey> --app <integrationFolder> -v
48
48
  $ af ds load <datasourceKey> --app <integrationFolder> --dry-run
49
49
  $ af ds export <datasourceKey> --app <integrationFolder> --format ndjson -v
50
50
  $ aifabrix datasource capability copy <file-or-key> --from create --as createBasicTrial --dry-run
@@ -262,18 +262,18 @@ function setupDatasourceCleanLogsCommand(datasource) {
262
262
 
263
263
  function setupDatasourceLogTrustCommand(datasource) {
264
264
  datasource.command('log-trust <datasourceKey>')
265
- .description('Show agent trust log from datasource test-trust (latest test-trust-*.json or --file)')
265
+ .description('Show agent trust log from datasource verify-trust (latest verify-trust-*.json or --file)')
266
266
  .option(
267
267
  '-a, --app <app>',
268
268
  'Integration folder name (optional: resolve from cwd or datasource key if single match)'
269
269
  )
270
- .option('-f, --file <path>', 'Path to log file (default: latest test-trust log in app logs folder)')
270
+ .option('-f, --file <path>', 'Path to log file (default: latest verify-trust log in app logs folder)')
271
271
  .action(async(datasourceKey, options) => {
272
272
  try {
273
273
  await runLogViewer(datasourceKey, {
274
274
  app: options.app,
275
275
  file: options.file,
276
- logType: 'test-trust'
276
+ logType: 'verify-trust'
277
277
  });
278
278
  } catch (error) {
279
279
  logger.error(formatBlockingError('log-trust failed:'), error.message);
@@ -304,7 +304,7 @@ function setupDatasourceCommands(program) {
304
304
  setupDatasourceTestIntegrationCommand(datasource);
305
305
  setupDatasourceTestE2ECommand(datasource);
306
306
  setupDatasourceVerifyAuditCommand(datasource);
307
- setupDatasourceTestTrustCommand(datasource);
307
+ setupDatasourceVerifyTrustCommand(datasource);
308
308
  setupDatasourceLogE2ECommand(datasource);
309
309
  setupDatasourceLogIntegrationCommand(datasource);
310
310
  setupDatasourceLogTestCommand(datasource);
@@ -336,6 +336,8 @@ function logConfigurationSection(p) {
336
336
  logRow('Environment', p.environment);
337
337
  logRow('Controller', p.controller);
338
338
  logRow('Format', p.formatVal);
339
+ logRow('Platform mode', p.setupPlatformMode);
340
+ logRow('Traefik', p.traefikEnabled ? 'on' : 'off');
339
341
  logRow('Scoped resources', p.scopedResourcesLabel);
340
342
  logRow('URLs registry', p.urlsLocalPath);
341
343
  if (!p.hasRemote) {
@@ -386,11 +388,12 @@ async function loadDevShowData(devIdStr) {
386
388
  const devIdNum = parseInt(devIdStr, 10);
387
389
  const ports = devConfig.getDevPorts(Number.isNaN(devIdNum) ? 0 : devIdNum);
388
390
  const [
389
- environment, tlsEnabled, controller, formatRaw, useScoped, secretsRaw,
391
+ environment, tlsEnabled, controller, formatRaw, useScoped, secretsRaw, setupPlatformMode, traefikEnabled,
390
392
  remoteServer, dockerEndpoint, dockerTlsSkipVerify, mutagenFolder, syncSshUser, syncSshHost
391
393
  ] = await Promise.all([
392
394
  config.getCurrentEnvironment(), config.getTlsEnabled(), config.getControllerUrl(),
393
395
  config.getFormat(), config.getUseEnvironmentScopedResources(), config.getAifabrixSecretsPath(),
396
+ config.getSetupPlatformMode(), config.getTraefikEnabled(),
394
397
  config.getRemoteServer(), config.getDockerEndpoint(), config.getDockerTlsSkipVerify(),
395
398
  config.getUserMutagenFolder(), config.getSyncSshUser(), config.getSyncSshHost()
396
399
  ]);
@@ -410,6 +413,8 @@ async function loadDevShowData(devIdStr) {
410
413
  controller,
411
414
  formatVal: formatRaw ?? null,
412
415
  scopedResourcesLabel,
416
+ setupPlatformMode,
417
+ traefikEnabled,
413
418
  workResolved,
414
419
  homeResolved,
415
420
  secretsMain,
@@ -446,6 +451,8 @@ function renderDevShow(data) {
446
451
  tlsEnabled: data.tlsEnabled,
447
452
  controller: data.controller,
448
453
  formatVal: data.formatVal,
454
+ setupPlatformMode: data.setupPlatformMode,
455
+ traefikEnabled: data.traefikEnabled,
449
456
  scopedResourcesLabel: data.scopedResourcesLabel,
450
457
  urlsLocalPath: data.urlsLocalPath,
451
458
  hasRemote: data.hasRemote,
@@ -0,0 +1,94 @@
1
+ /**
2
+ * @fileoverview Shared helpers for governance * CLI commands (424.0).
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const {
8
+ resolveExternalIntegrationContext,
9
+ syncLocalIfRequested
10
+ } = require('./test-e2e-external');
11
+ const { resolveIntegrationAuth } = require('../external-system/integration-auth-context');
12
+ const { cliOptsSkipSync } = require('../utils/cli-sync-options');
13
+ const { fetchDataplaneGeneralHealth } = require('../api/dataplane-health.api');
14
+ const { formatNetworkError } = require('../utils/api-error-handler');
15
+
16
+ /**
17
+ * @param {Object} options
18
+ * @param {string[]} [options.subjectUserIds]
19
+ * @param {string[]} [options.subjectEmails]
20
+ * @returns {Array<{ subjectUserId?: string, subjectEmail?: string }>}
21
+ */
22
+ function buildSubjectsFromOptions(options) {
23
+ const subjects = [];
24
+ const userIds = options.subjectUserIds || [];
25
+ const emails = options.subjectEmails || [];
26
+ for (const subjectUserId of userIds) {
27
+ const id = String(subjectUserId || '').trim();
28
+ if (id) subjects.push({ subjectUserId: id });
29
+ }
30
+ for (const subjectEmail of emails) {
31
+ const email = String(subjectEmail || '').trim();
32
+ if (email) subjects.push({ subjectEmail: email });
33
+ }
34
+ return subjects;
35
+ }
36
+
37
+ /**
38
+ * Fail fast when dataplane health is unreachable (avoids misleading API validation errors).
39
+ * @async
40
+ * @param {string} dataplaneUrl
41
+ * @returns {Promise<void>}
42
+ */
43
+ async function assertDataplaneReachable(dataplaneUrl) {
44
+ const base = String(dataplaneUrl || '').replace(/\/$/, '');
45
+ const snapshot = await fetchDataplaneGeneralHealth(base, { timeoutMs: 8000 });
46
+ if (snapshot) {
47
+ return;
48
+ }
49
+ const endpointUrl = `${base}/api/v1/health`;
50
+ const err = new Error(`Cannot reach dataplane at ${base}`);
51
+ err.network = true;
52
+ err.serviceLabel = 'dataplane';
53
+ err.endpointUrl = endpointUrl;
54
+ err.formatted = formatNetworkError(err.message, {
55
+ endpointUrl,
56
+ serviceLabel: 'dataplane'
57
+ });
58
+ throw err;
59
+ }
60
+
61
+ /**
62
+ * @async
63
+ * @param {string} systemKey
64
+ * @param {Object} options
65
+ * @returns {Promise<{ resolvedKey: string, keys: string[], authConfig: Object, dataplaneUrl: string }>}
66
+ */
67
+ async function resolveGovernanceIntegrationContext(systemKey, options) {
68
+ const { systemKey: resolvedKey, keys } = resolveExternalIntegrationContext(systemKey);
69
+ if (options.authConfig && options.dataplaneUrl) {
70
+ return {
71
+ resolvedKey,
72
+ keys,
73
+ authConfig: options.authConfig,
74
+ dataplaneUrl: options.dataplaneUrl
75
+ };
76
+ }
77
+ if (!cliOptsSkipSync(options)) {
78
+ const uploadCtx = await syncLocalIfRequested(resolvedKey, options);
79
+ if (uploadCtx?.authConfig && uploadCtx?.dataplaneUrl) {
80
+ options.authConfig = uploadCtx.authConfig;
81
+ options.dataplaneUrl = uploadCtx.dataplaneUrl;
82
+ options.silentResolve = true;
83
+ }
84
+ }
85
+ const { authConfig, dataplaneUrl } = await resolveIntegrationAuth(resolvedKey, options);
86
+ await assertDataplaneReachable(dataplaneUrl);
87
+ return { resolvedKey, keys, authConfig, dataplaneUrl };
88
+ }
89
+
90
+ module.exports = {
91
+ buildSubjectsFromOptions,
92
+ assertDataplaneReachable,
93
+ resolveGovernanceIntegrationContext
94
+ };
@@ -0,0 +1,132 @@
1
+ /**
2
+ * @fileoverview CLI actions for `aifabrix governance *` (424.0).
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const logger = require('../utils/logger');
8
+ const { handleCommandError } = require('../utils/cli-utils');
9
+ const { runGovernanceGenerateForExternalSystem } = require('./governance-generate-external');
10
+ const { runGovernanceVerifyForExternalSystem } = require('./governance-verify-external');
11
+ const { runGovernanceUploadForExternalSystem } = require('./governance-upload-external');
12
+ const { runGovernanceDownloadForExternalSystem } = require('./governance-download-external');
13
+ const { displayVerifyGovernanceTTY } = require('../lifecycle/report-display');
14
+ const { exitCodeFromVerdict } = require('../lifecycle/exit-codes');
15
+ const { buildJsonEnvelope } = require('../lifecycle/json-envelope');
16
+ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
17
+
18
+ /**
19
+ * Commander repeatable-option collector. Splits comma-separated values so both
20
+ * `--flag a --flag b` and `--flag a,b` work.
21
+ * @param {string} value
22
+ * @param {string[]} previous
23
+ * @returns {string[]}
24
+ */
25
+ function collectString(value, previous) {
26
+ const parts = String(value)
27
+ .split(',')
28
+ .map(entry => entry.trim())
29
+ .filter(Boolean);
30
+ return previous.concat(parts.length > 0 ? parts : [String(value).trim()].filter(Boolean));
31
+ }
32
+
33
+ function normalizeGovernanceOptions(options, cmd) {
34
+ const parent = cmd && cmd.parent ? cmd.parent.opts() : {};
35
+ return {
36
+ env: options.env || parent.env || 'dev',
37
+ verbose: options.verbose === true || parent.verbose === true,
38
+ noSync: options.noSync === true || parent.noSync === true,
39
+ force: options.force === true || parent.force === true,
40
+ json: options.json === true || parent.json === true,
41
+ pack: options.pack,
42
+ app: options.app,
43
+ scenarioIds: options.scenario || [],
44
+ certificationProfile: options.certificationProfile || 'bronze',
45
+ subjectUserIds: options.subjectUserId || [],
46
+ subjectEmails: options.subjectEmail || [],
47
+ maxSamplesPerPartition: options.maxSamplesPerPartition
48
+ };
49
+ }
50
+
51
+ async function governanceGenerateHandler(systemKey, options, cmd) {
52
+ try {
53
+ const opts = normalizeGovernanceOptions(options, cmd);
54
+ const result = await runGovernanceGenerateForExternalSystem(systemKey, opts);
55
+ if (opts.json) {
56
+ logger.log(JSON.stringify(buildJsonEnvelope(result, opts.verbose), null, 2));
57
+ return;
58
+ }
59
+ for (const row of result.results || []) {
60
+ logger.log(
61
+ formatSuccessLine(
62
+ `${row.datasourceKey}: pack v${row.packVersion} (${row.scenarioCount} scenarios)`
63
+ )
64
+ );
65
+ }
66
+ } catch (error) {
67
+ handleCommandError(error, 'governance generate');
68
+ process.exit(3);
69
+ }
70
+ }
71
+
72
+ async function governanceVerifyHandler(systemKey, options, cmd) {
73
+ try {
74
+ const opts = normalizeGovernanceOptions(options, cmd);
75
+ const result = await runGovernanceVerifyForExternalSystem(systemKey, opts);
76
+ if (opts.json) {
77
+ logger.log(JSON.stringify(buildJsonEnvelope(result, opts.verbose), null, 2));
78
+ } else {
79
+ displayVerifyGovernanceTTY(result.systemKey, result, { details: opts.verbose });
80
+ }
81
+ const exitCode = exitCodeFromVerdict(result.verdict);
82
+ if (exitCode !== 0) {
83
+ process.exit(exitCode);
84
+ }
85
+ } catch (error) {
86
+ handleCommandError(error, 'governance verify');
87
+ process.exit(3);
88
+ }
89
+ }
90
+
91
+ async function governanceUploadHandler(systemKey, options, cmd) {
92
+ try {
93
+ const opts = normalizeGovernanceOptions(options, cmd);
94
+ const result = await runGovernanceUploadForExternalSystem(systemKey, opts);
95
+ if (opts.json) {
96
+ logger.log(JSON.stringify(buildJsonEnvelope(result, opts.verbose), null, 2));
97
+ return;
98
+ }
99
+ logger.log(formatSuccessLine(`Uploaded ${result.uploaded} scenario pack(s)`));
100
+ } catch (error) {
101
+ if (error.friendlyHint) {
102
+ logger.log(error.friendlyHint);
103
+ }
104
+ handleCommandError(error, 'governance upload');
105
+ process.exit(3);
106
+ }
107
+ }
108
+
109
+ async function governanceDownloadHandler(systemKey, options, cmd) {
110
+ try {
111
+ const opts = normalizeGovernanceOptions(options, cmd);
112
+ opts.noSync = true;
113
+ const result = await runGovernanceDownloadForExternalSystem(systemKey, opts);
114
+ if (opts.json) {
115
+ logger.log(JSON.stringify(buildJsonEnvelope(result, opts.verbose), null, 2));
116
+ return;
117
+ }
118
+ logger.log(formatSuccessLine(`Downloaded ${result.written} scenario pack(s)`));
119
+ } catch (error) {
120
+ handleCommandError(error, 'governance download');
121
+ process.exit(3);
122
+ }
123
+ }
124
+
125
+ module.exports = {
126
+ collectString,
127
+ normalizeGovernanceOptions,
128
+ governanceGenerateHandler,
129
+ governanceVerifyHandler,
130
+ governanceUploadHandler,
131
+ governanceDownloadHandler
132
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @fileoverview governance download — pull packs to integration/<systemKey>/scenarios/ (424.0).
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const { downloadGovernanceScenarioPacks } = require('../lifecycle/scenario-upload');
8
+ const { resolveGovernanceIntegrationContext } = require('./governance-cli-shared');
9
+
10
+ /**
11
+ * @async
12
+ * @param {string} systemKey
13
+ * @param {Object} options
14
+ * @returns {Promise<Object>}
15
+ */
16
+ async function runGovernanceDownloadForExternalSystem(systemKey, options = {}) {
17
+ const { resolvedKey, keys, authConfig, dataplaneUrl } =
18
+ await resolveGovernanceIntegrationContext(systemKey, options);
19
+ const written = await downloadGovernanceScenarioPacks(
20
+ resolvedKey,
21
+ keys,
22
+ dataplaneUrl,
23
+ authConfig
24
+ );
25
+ return {
26
+ systemKey: resolvedKey,
27
+ command: 'governance download',
28
+ written
29
+ };
30
+ }
31
+
32
+ module.exports = {
33
+ runGovernanceDownloadForExternalSystem
34
+ };