@aifabrix/builder 2.45.0 → 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 (352) 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/docs-rules.mdc +3 -1
  4. package/.cursor/rules/project-rules.mdc +1 -0
  5. package/README.md +43 -17
  6. package/integration/hubspot-test/wizard-hubspot-e2e.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 +69 -121
  12. package/lib/api/agent-metadata-validation.api.js +93 -0
  13. package/lib/api/api-response-errors.js +124 -0
  14. package/lib/api/audit-api-helpers.js +68 -0
  15. package/lib/api/audit.api.js +119 -0
  16. package/lib/api/auth-cache.api.js +44 -0
  17. package/lib/api/certificates.api.js +4 -4
  18. package/lib/api/credential.api.js +2 -2
  19. package/lib/api/dataplane-health.api.js +137 -0
  20. package/lib/api/dataplane-sync.api.js +27 -0
  21. package/lib/api/datasources-core.api.js +21 -18
  22. package/lib/api/datasources-extended.api.js +22 -22
  23. package/lib/api/dimensions.api.js +3 -0
  24. package/lib/api/external-systems.api.js +13 -13
  25. package/lib/api/governance-scenario-pack.api.js +159 -0
  26. package/lib/api/governance-scenarios.api.js +39 -0
  27. package/lib/api/groups.api.js +79 -0
  28. package/lib/api/index.js +88 -5
  29. package/lib/api/lifecycle.api.js +77 -0
  30. package/lib/api/pipeline.api.js +7 -5
  31. package/lib/api/protection.api.js +157 -0
  32. package/lib/api/records-bulk.api.js +43 -0
  33. package/lib/api/records-search-parse.js +49 -0
  34. package/lib/api/records-search.api.js +29 -0
  35. package/lib/api/types/agent-metadata-validation.types.js +67 -0
  36. package/lib/api/types/audit.types.js +72 -0
  37. package/lib/api/types/dataplane-health.types.js +27 -0
  38. package/lib/api/types/dimensions.types.js +2 -0
  39. package/lib/api/types/governance-scenarios.types.js +39 -0
  40. package/lib/api/types/protection.types.js +69 -0
  41. package/lib/api/types/records-bulk.types.js +35 -0
  42. package/lib/api/types/records-search.types.js +28 -0
  43. package/lib/api/user-groups.api.js +50 -0
  44. package/lib/api/users.api.js +95 -0
  45. package/lib/api/validation-run.api.js +3 -3
  46. package/lib/api/validation-runner.js +5 -3
  47. package/lib/api/wizard-platform.api.js +3 -3
  48. package/lib/api/wizard.api.js +21 -21
  49. package/lib/app/deploy-config.js +4 -1
  50. package/lib/app/deploy.js +34 -19
  51. package/lib/app/down.js +20 -1
  52. package/lib/app/push.js +4 -5
  53. package/lib/app/register.js +7 -4
  54. package/lib/app/run-env-compose.js +84 -30
  55. package/lib/build/index.js +5 -0
  56. package/lib/certification/sync-system-certification.js +13 -6
  57. package/lib/cli/index.js +8 -2
  58. package/lib/cli/infra-guided-auth.js +109 -0
  59. package/lib/cli/infra-guided.js +91 -69
  60. package/lib/cli/setup-app.help.js +19 -2
  61. package/lib/cli/setup-app.js +58 -23
  62. package/lib/cli/setup-app.test-commands.js +28 -21
  63. package/lib/cli/setup-auth.js +55 -3
  64. package/lib/cli/setup-certification-commands.js +147 -0
  65. package/lib/cli/setup-certification.help.js +141 -0
  66. package/lib/cli/setup-environment.js +19 -2
  67. package/lib/cli/setup-external-system-help.js +62 -0
  68. package/lib/cli/setup-external-system.js +62 -6
  69. package/lib/cli/setup-infra.js +9 -1
  70. package/lib/cli/setup-platform.js +6 -1
  71. package/lib/cli/setup-secrets.js +33 -1
  72. package/lib/cli/setup-utility-help-after.js +65 -0
  73. package/lib/cli/setup-utility-repair.js +198 -0
  74. package/lib/cli/setup-utility-resolve.js +42 -6
  75. package/lib/cli/setup-utility-validate.js +197 -0
  76. package/lib/cli/setup-utility.js +41 -258
  77. package/lib/commands/auth-status-client-tokens.js +214 -0
  78. package/lib/commands/auth-status-dataplane-version.js +269 -0
  79. package/lib/commands/auth-status-display.js +94 -0
  80. package/lib/commands/auth-status.js +111 -53
  81. package/lib/commands/credential-push.js +10 -5
  82. package/lib/commands/datasource-load-export-cli.js +178 -0
  83. package/lib/commands/datasource-test-trust-cli.js +118 -0
  84. package/lib/commands/datasource-unified-test-cli.js +31 -20
  85. package/lib/commands/datasource-unified-test-cli.options.js +22 -3
  86. package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
  87. package/lib/commands/datasource-verify-audit-cli.js +96 -0
  88. package/lib/commands/datasource.js +68 -1
  89. package/lib/commands/dev-show-display.js +8 -1
  90. package/lib/commands/dimension-value.js +43 -13
  91. package/lib/commands/dimension.js +38 -7
  92. package/lib/commands/governance-cli-shared.js +94 -0
  93. package/lib/commands/governance-command-action.js +132 -0
  94. package/lib/commands/governance-download-external.js +34 -0
  95. package/lib/commands/governance-generate-external.js +71 -0
  96. package/lib/commands/governance-upload-external.js +36 -0
  97. package/lib/commands/governance-verify-external.js +64 -0
  98. package/lib/commands/governance.js +144 -0
  99. package/lib/commands/identity-apply-sync.js +131 -0
  100. package/lib/commands/identity-group-cli.js +146 -0
  101. package/lib/commands/identity-membership-role-cache.js +175 -0
  102. package/lib/commands/identity-shared.js +94 -0
  103. package/lib/commands/identity-user-cli.js +158 -0
  104. package/lib/commands/identity.js +43 -0
  105. package/lib/commands/lifecycle-command-action.js +123 -0
  106. package/lib/commands/lifecycle-external.js +268 -0
  107. package/lib/commands/login.js +56 -17
  108. package/lib/commands/protection-cli-leaves.js +286 -0
  109. package/lib/commands/protection.js +50 -0
  110. package/lib/commands/repair-auth-apply.js +127 -0
  111. package/lib/commands/repair-changed-files.js +41 -0
  112. package/lib/commands/repair-datasource-keys.js +4 -1
  113. package/lib/commands/repair-datasource-run.js +59 -0
  114. package/lib/commands/repair-datasource-test-payload.js +259 -0
  115. package/lib/commands/repair-datasource.js +1 -51
  116. package/lib/commands/repair-env-template.js +64 -13
  117. package/lib/commands/repair-external-run.js +216 -0
  118. package/lib/commands/repair-openapi-sync.js +23 -0
  119. package/lib/commands/repair-persist.js +4 -0
  120. package/lib/commands/repair-rbac.js +52 -1
  121. package/lib/commands/repair-steps.js +145 -0
  122. package/lib/commands/repair.js +29 -286
  123. package/lib/commands/secrets-get.js +184 -0
  124. package/lib/commands/secrets-remove-all.js +3 -1
  125. package/lib/commands/setup-image-refresh.js +146 -0
  126. package/lib/commands/setup-modes.js +158 -162
  127. package/lib/commands/setup-platform-auth.js +76 -0
  128. package/lib/commands/setup-prompts-admin.js +244 -0
  129. package/lib/commands/setup-prompts-platform-mode.js +67 -0
  130. package/lib/commands/setup-prompts-validators.js +36 -0
  131. package/lib/commands/setup-prompts.js +24 -76
  132. package/lib/commands/setup-run-context.js +45 -0
  133. package/lib/commands/setup-spinners.js +39 -0
  134. package/lib/commands/setup.js +27 -5
  135. package/lib/commands/teardown.js +39 -0
  136. package/lib/commands/test-e2e-external.js +61 -14
  137. package/lib/commands/test-governance-cli-options.js +47 -0
  138. package/lib/commands/test-governance-external.js +67 -0
  139. package/lib/commands/test-trust-cli-options.js +32 -0
  140. package/lib/commands/test-trust-external.js +126 -0
  141. package/lib/commands/up-common.js +25 -13
  142. package/lib/commands/up-dataplane.js +50 -13
  143. package/lib/commands/upload-publish-context.js +75 -0
  144. package/lib/commands/upload-sidecar-followups.js +89 -0
  145. package/lib/commands/upload.js +108 -105
  146. package/lib/commands/verify-governance-combined-external.js +61 -0
  147. package/lib/commands/verify-governance-command-action.js +64 -0
  148. package/lib/commands/verify-governance-db.js +102 -0
  149. package/lib/commands/verify-governance-external.js +77 -0
  150. package/lib/commands/verify-governance-result-builders.js +71 -0
  151. package/lib/commands/verify-operations-command-action.js +68 -0
  152. package/lib/commands/verify-operations-external.js +268 -0
  153. package/lib/commands/verify-operations-persist.js +75 -0
  154. package/lib/commands/verify-operations-step-plan.js +68 -0
  155. package/lib/commands/verify-operations-steps.js +111 -0
  156. package/lib/commands/verify-trust-command-action.js +70 -0
  157. package/lib/commands/verify-trust-external.js +67 -0
  158. package/lib/commands/wizard-config-normalizer.js +43 -23
  159. package/lib/commands/wizard-core.js +5 -0
  160. package/lib/core/admin-password-generator.js +38 -0
  161. package/lib/core/admin-secrets-bundle.js +111 -0
  162. package/lib/core/admin-secrets-env-overlay.js +62 -0
  163. package/lib/core/admin-secrets.js +32 -4
  164. package/lib/core/config-attach-extensions.js +3 -0
  165. package/lib/core/config-device-dataplane.js +185 -0
  166. package/lib/core/config-platform-controller.js +44 -0
  167. package/lib/core/config-setup-installation-profile.js +42 -0
  168. package/lib/core/config-setup-platform-mode.js +46 -0
  169. package/lib/core/config.js +46 -36
  170. package/lib/core/secrets-admin-env.js +6 -2
  171. package/lib/core/secrets-ensure.js +4 -0
  172. package/lib/core/secrets-env-content.js +54 -48
  173. package/lib/core/secrets-env-write.js +10 -1
  174. package/lib/core/secrets-infra-placeholder-sync.js +2 -0
  175. package/lib/core/setup-installation-profile.js +43 -0
  176. package/lib/core/setup-platform-mode.js +55 -0
  177. package/lib/datasource/agent-trust-debug-log.js +45 -0
  178. package/lib/datasource/agent-trust-fetch.js +115 -0
  179. package/lib/datasource/agent-trust-map.js +101 -0
  180. package/lib/datasource/agent-trust-mock.js +44 -0
  181. package/lib/datasource/agent-trust-run.js +95 -0
  182. package/lib/datasource/agent-trust-timeout.js +25 -0
  183. package/lib/datasource/audit-evidence-display.js +80 -0
  184. package/lib/datasource/audit-evidence-extract-debug.js +115 -0
  185. package/lib/datasource/audit-evidence-extract.js +180 -0
  186. package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
  187. package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
  188. package/lib/datasource/audit-evidence-poll.js +109 -0
  189. package/lib/datasource/audit-evidence-run.js +177 -0
  190. package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
  191. package/lib/datasource/audit-evidence-verify-matrix.js +205 -0
  192. package/lib/datasource/audit-evidence-verify.js +108 -0
  193. package/lib/datasource/bulk-loader-run-batches.js +100 -0
  194. package/lib/datasource/bulk-loader-service-retry.js +52 -0
  195. package/lib/datasource/bulk-loader-service.js +127 -0
  196. package/lib/datasource/capability/run-capability-dimension.js +2 -1
  197. package/lib/datasource/datasource-exporter-resolve.js +82 -0
  198. package/lib/datasource/datasource-exporter-service.js +144 -0
  199. package/lib/datasource/datasource-identifier.js +148 -0
  200. package/lib/datasource/datasource-load-export-context.js +95 -0
  201. package/lib/datasource/integration-context.js +6 -4
  202. package/lib/datasource/local-data-paths.js +145 -0
  203. package/lib/datasource/log-cleaner.js +241 -0
  204. package/lib/datasource/log-viewer.js +111 -6
  205. package/lib/datasource/record-file-parser.js +85 -0
  206. package/lib/datasource/record-mapper.js +129 -0
  207. package/lib/datasource/resolve-app.js +74 -55
  208. package/lib/datasource/test-e2e.js +29 -2
  209. package/lib/datasource/unified-validation-run-body.js +3 -1
  210. package/lib/datasource/unified-validation-run-resolve.js +45 -12
  211. package/lib/datasource/unified-validation-run.js +47 -18
  212. package/lib/datasource/validate.js +37 -20
  213. package/lib/deployment/environment-poll-status.js +138 -0
  214. package/lib/deployment/environment.js +42 -34
  215. package/lib/external-system/download.js +21 -20
  216. package/lib/external-system/generator.js +5 -1
  217. package/lib/external-system/integration-auth-context.js +45 -0
  218. package/lib/external-system/test-auth.js +12 -5
  219. package/lib/external-system/test-system-level.js +25 -4
  220. package/lib/external-system/test.js +9 -7
  221. package/lib/generator/external-controller-manifest.js +15 -0
  222. package/lib/governance/governance-cli-errors.js +96 -0
  223. package/lib/governance/governance-download-display.js +38 -0
  224. package/lib/governance/governance-pack-loader.js +70 -0
  225. package/lib/governance/governance-run-display.js +309 -0
  226. package/lib/identity/identity-apply-core.js +108 -0
  227. package/lib/identity/identity-apply-phases.js +244 -0
  228. package/lib/identity/identity-apply-service.js +77 -0
  229. package/lib/identity/identity-csv-parser.js +114 -0
  230. package/lib/infrastructure/admin-secrets-apply.js +84 -0
  231. package/lib/infrastructure/admin-secrets-ensure.js +130 -0
  232. package/lib/infrastructure/admin-secrets-resolve.js +95 -0
  233. package/lib/infrastructure/admin-secrets-sync.js +91 -0
  234. package/lib/infrastructure/helpers.js +23 -129
  235. package/lib/infrastructure/index.js +5 -6
  236. package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
  237. package/lib/internal/fs-real-sync.js +29 -1
  238. package/lib/lifecycle/certification-progress.js +231 -0
  239. package/lib/lifecycle/dataplane-ui-url.js +145 -0
  240. package/lib/lifecycle/exit-codes.js +24 -0
  241. package/lib/lifecycle/json-envelope.js +25 -0
  242. package/lib/lifecycle/lifecycle-governance-options.js +51 -0
  243. package/lib/lifecycle/logger-quiet.js +29 -0
  244. package/lib/lifecycle/open-api-published.js +59 -0
  245. package/lib/lifecycle/operations-readiness-metrics.js +101 -0
  246. package/lib/lifecycle/product-model.js +196 -0
  247. package/lib/lifecycle/report-display.js +471 -0
  248. package/lib/lifecycle/scenario-upload.js +191 -0
  249. package/lib/lifecycle/verify-step-progress.js +87 -0
  250. package/lib/parameters/infra-kv-discovery.js +27 -2
  251. package/lib/protection/auth-context.js +51 -0
  252. package/lib/protection/convert-batch.js +90 -0
  253. package/lib/protection/load.js +41 -0
  254. package/lib/protection/paths.js +158 -0
  255. package/lib/protection/preflight-datasource-ready.js +46 -0
  256. package/lib/protection/protection-create-probe-lines.js +142 -0
  257. package/lib/protection/protection-create-probes.js +95 -0
  258. package/lib/protection/protection-create-scaffold.js +281 -0
  259. package/lib/protection/protection-display-helpers.js +445 -0
  260. package/lib/protection/protection-display.js +215 -0
  261. package/lib/protection/protection-preset-registry.js +97 -0
  262. package/lib/protection/report-exit.js +62 -0
  263. package/lib/protection/resolve.js +202 -0
  264. package/lib/protection/run-commands-validate.js +130 -0
  265. package/lib/protection/run-commands.js +216 -0
  266. package/lib/protection/run-protection-create-helpers.js +134 -0
  267. package/lib/protection/run-protection-create-probes-flow.js +43 -0
  268. package/lib/protection/run-protection-create.js +191 -0
  269. package/lib/protection/scope.js +22 -0
  270. package/lib/protection/sync-after-upload.js +64 -0
  271. package/lib/protection/upload-batch.js +127 -0
  272. package/lib/protection/validate-batch.js +113 -0
  273. package/lib/protection/validate-local.js +65 -0
  274. package/lib/resolvers/dimension-file.js +29 -4
  275. package/lib/schema/application-schema.json +1 -1
  276. package/lib/schema/datasource-test-run.schema.json +89 -6
  277. package/lib/schema/external-datasource.schema.json +2636 -2588
  278. package/lib/schema/external-system.schema.json +1089 -761
  279. package/lib/schema/flag-map-validation-run.json +2 -2
  280. package/lib/schema/infra.parameter.yaml +45 -0
  281. package/lib/schema/protection.schema.json +374 -0
  282. package/lib/schema/type/credential-field-registry.json +642 -0
  283. package/lib/schema/wizard-config.schema.json +1 -1
  284. package/lib/utils/agent-trust-run-display.js +231 -0
  285. package/lib/utils/agent-trust-run-exit.js +59 -0
  286. package/lib/utils/agent-trust-wait-ui.js +94 -0
  287. package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
  288. package/lib/utils/api.js +55 -31
  289. package/lib/utils/app-relative-path-env.js +112 -0
  290. package/lib/utils/cli-exit-codes.js +30 -0
  291. package/lib/utils/cli-secrets-error-format.js +83 -21
  292. package/lib/utils/cli-sync-options.js +33 -0
  293. package/lib/utils/cli-utils.js +16 -4
  294. package/lib/utils/compose-generate-docker-compose.js +4 -0
  295. package/lib/utils/compose-traefik-ingress-base.js +16 -1
  296. package/lib/utils/config-secrets-path.js +46 -0
  297. package/lib/utils/controller-url.js +88 -1
  298. package/lib/utils/credential-push-ui.js +90 -0
  299. package/lib/utils/credential-secrets-env.js +137 -9
  300. package/lib/utils/dataplane-cli-version-gate.js +202 -0
  301. package/lib/utils/dataplane-cli-version-help.js +74 -0
  302. package/lib/utils/dataplane-resolver.js +129 -2
  303. package/lib/utils/datasource-export-display.js +137 -0
  304. package/lib/utils/datasource-load-display.js +174 -0
  305. package/lib/utils/datasource-load-export-ui.js +120 -0
  306. package/lib/utils/datasource-test-run-capability-scope.js +3 -3
  307. package/lib/utils/datasource-test-run-display.js +3 -3
  308. package/lib/utils/datasource-test-run-tty-log.js +5 -1
  309. package/lib/utils/declarative-url-ports.js +3 -3
  310. package/lib/utils/deployment-auth-mode.js +41 -0
  311. package/lib/utils/developer-volume-id.js +33 -0
  312. package/lib/utils/env-copy.js +14 -9
  313. package/lib/utils/env-kv-orphan-lines.js +43 -0
  314. package/lib/utils/error-formatters/network-errors.js +13 -4
  315. package/lib/utils/external-env-template.js +1 -0
  316. package/lib/utils/external-system-readiness-core.js +10 -0
  317. package/lib/utils/external-system-readiness-display.js +80 -23
  318. package/lib/utils/health-check.js +4 -1
  319. package/lib/utils/help-builder.js +35 -26
  320. package/lib/utils/keycloak-realm-ready.js +101 -0
  321. package/lib/utils/load-cip-capacity-display-config.js +61 -15
  322. package/lib/utils/manifest-source-emit.js +24 -0
  323. package/lib/utils/pipeline-upload-auth-retry.js +82 -0
  324. package/lib/utils/platform-controller-url.js +156 -0
  325. package/lib/utils/postgres-platform-bootstrap.js +199 -0
  326. package/lib/utils/postgres-wipe.js +9 -2
  327. package/lib/utils/resolve-env-mode.js +47 -0
  328. package/lib/utils/secrets-generator.js +31 -25
  329. package/lib/utils/secrets-missing-error.js +164 -0
  330. package/lib/utils/semver-compare.js +127 -0
  331. package/lib/utils/service-url-label.js +81 -0
  332. package/lib/utils/setup-quiet-output.js +25 -0
  333. package/lib/utils/template-helpers.js +15 -0
  334. package/lib/utils/token-manager-client-credentials.js +201 -0
  335. package/lib/utils/token-manager-messages.js +8 -0
  336. package/lib/utils/token-manager-refresh.js +12 -2
  337. package/lib/utils/token-manager.js +86 -105
  338. package/lib/utils/upload-pipeline-ui.js +98 -0
  339. package/lib/utils/upload-sync-options.js +42 -0
  340. package/lib/utils/url-declarative-public-base.js +21 -3
  341. package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
  342. package/lib/utils/validation-poll-ui.js +22 -7
  343. package/lib/utils/validation-run-stage.js +45 -0
  344. package/lib/validation/wizard-config-validator.js +58 -19
  345. package/package.json +1 -1
  346. package/templates/applications/dataplane/application.yaml +2 -1
  347. package/templates/applications/dataplane/env.template +36 -1
  348. package/templates/applications/dataplane/rbac.yaml +4 -0
  349. package/templates/applications/miso-controller/env.template +16 -0
  350. package/templates/external-system/README.md.hbs +116 -11
  351. package/templates/python/docker-compose.hbs +19 -18
  352. package/templates/typescript/docker-compose.hbs +18 -17
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Client token listing and proactive refresh for auth status.
3
+ *
4
+ * @fileoverview Stored app tokens, auto-refresh when credentials exist
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const chalk = require('chalk');
12
+ const config = require('../core/config');
13
+ const logger = require('../utils/logger');
14
+ const { successGlyph, failureGlyph } = require('../utils/cli-test-layout-chalk');
15
+ const {
16
+ getOrRefreshClientToken,
17
+ loadClientCredentials,
18
+ refreshClientToken,
19
+ isTokenExpired
20
+ } = require('../utils/token-manager');
21
+
22
+ /**
23
+ * @param {string} controllerUrl
24
+ * @param {string} storedController
25
+ * @returns {boolean}
26
+ */
27
+ function controllersMatch(controllerUrl, storedController) {
28
+ if (!storedController) {
29
+ return false;
30
+ }
31
+ return config.normalizeControllerUrl(storedController) === config.normalizeControllerUrl(controllerUrl);
32
+ }
33
+
34
+ /**
35
+ * @param {string} environment
36
+ * @param {string} controllerUrl
37
+ * @returns {Promise<Record<string, { controller?: string, token?: string, expiresAt?: string }>>}
38
+ */
39
+ async function getClientEntriesForController(environment, controllerUrl) {
40
+ const configData = await config.getConfig();
41
+ const clients = configData.environments?.[environment]?.clients || {};
42
+ const out = {};
43
+ for (const [appName, tokenData] of Object.entries(clients)) {
44
+ if (controllersMatch(controllerUrl, tokenData?.controller)) {
45
+ out[appName] = tokenData;
46
+ }
47
+ }
48
+ return out;
49
+ }
50
+
51
+ /**
52
+ * Refresh expired client tokens when secrets.local.yaml (or env) has client id/secret.
53
+ *
54
+ * @async
55
+ * @param {string} environment
56
+ * @param {string} controllerUrl
57
+ * @returns {Promise<number>} Count refreshed
58
+ */
59
+ async function refreshExpiredClientTokensWherePossible(environment, controllerUrl) {
60
+ const entries = await getClientEntriesForController(environment, controllerUrl);
61
+ let refreshed = 0;
62
+ for (const appName of Object.keys(entries)) {
63
+ const tokenData = entries[appName];
64
+ if (!tokenData?.token || !isTokenExpired(tokenData.expiresAt)) {
65
+ continue;
66
+ }
67
+ const credentials = await loadClientCredentials(appName);
68
+ if (!credentials?.clientId || !credentials?.clientSecret) {
69
+ continue;
70
+ }
71
+ try {
72
+ await refreshClientToken(environment, appName, controllerUrl);
73
+ refreshed += 1;
74
+ } catch {
75
+ // Skip apps without valid credentials or controller errors
76
+ }
77
+ }
78
+ return refreshed;
79
+ }
80
+
81
+ /**
82
+ * @param {string} expiresAt
83
+ * @returns {'valid'|'expired'|'unknown'}
84
+ */
85
+ function tokenExpiryState(expiresAt) {
86
+ if (!expiresAt) {
87
+ return 'unknown';
88
+ }
89
+ return isTokenExpired(expiresAt) ? 'expired' : 'valid';
90
+ }
91
+
92
+ /**
93
+ * Build rows for stored client tokens table.
94
+ *
95
+ * @async
96
+ * @param {string} environment
97
+ * @param {string} controllerUrl
98
+ * @returns {Promise<Array<{ appName: string, expiresAt: string, state: string, logoutCmd: string }>>}
99
+ */
100
+ async function listClientTokenRows(environment, controllerUrl) {
101
+ const entries = await getClientEntriesForController(environment, controllerUrl);
102
+ const rows = [];
103
+ for (const appName of Object.keys(entries).sort()) {
104
+ const tokenData = entries[appName];
105
+ const state = tokenExpiryState(tokenData.expiresAt);
106
+ const credentials = await loadClientCredentials(appName);
107
+ const canRefresh = !!(credentials?.clientId && credentials?.clientSecret);
108
+ let stateLabel = state;
109
+ if (state === 'expired' && canRefresh) {
110
+ stateLabel = 'expired (credentials available — refreshed on status if possible)';
111
+ } else if (state === 'expired' && !canRefresh) {
112
+ stateLabel = 'expired (no client id/secret in secrets — logout or login)';
113
+ }
114
+ rows.push({
115
+ appName,
116
+ expiresAt: tokenData.expiresAt || '—',
117
+ state: stateLabel,
118
+ logoutCmd: `aifabrix logout -e ${environment} -a ${appName}`
119
+ });
120
+ }
121
+ return rows;
122
+ }
123
+
124
+ /**
125
+ * Print stored application client tokens and logout commands.
126
+ *
127
+ * @async
128
+ * @param {string} environment
129
+ * @param {string} controllerUrl
130
+ * @param {number} [refreshedCount]
131
+ * @returns {Promise<void>}
132
+ */
133
+ async function displayStoredClientTokensSection(environment, controllerUrl, refreshedCount = 0) {
134
+ const rows = await listClientTokenRows(environment, controllerUrl);
135
+ if (rows.length === 0) {
136
+ return;
137
+ }
138
+
139
+ logger.log('');
140
+ logger.log(chalk.bold('Stored application client tokens:'));
141
+ if (refreshedCount > 0) {
142
+ logger.log(
143
+ chalk.gray(
144
+ ` Refreshed ${refreshedCount} expired token(s) using client id/secret from secrets.local.yaml or env.`
145
+ )
146
+ );
147
+ }
148
+ logger.log(chalk.gray(` Environment: ${environment} · Controller: ${controllerUrl}`));
149
+ logger.log('');
150
+ for (const row of rows) {
151
+ const icon = row.state.startsWith('valid') ? successGlyph() : failureGlyph();
152
+ logger.log(` ${icon} ${chalk.cyan(row.appName)}`);
153
+ logger.log(` Expires: ${chalk.gray(row.expiresAt)}`);
154
+ logger.log(` Status: ${row.state}`);
155
+ logger.log(` Clear: ${chalk.gray(row.logoutCmd)}`);
156
+ }
157
+ logger.log('');
158
+ logger.log(chalk.gray(' Clear all client tokens for this environment:'), chalk.gray(`aifabrix logout -e ${environment}`));
159
+ logger.log(chalk.gray(' Clear device login for this controller:'), chalk.gray(`aifabrix logout -c ${controllerUrl}`));
160
+ logger.log('');
161
+ }
162
+
163
+ /**
164
+ * Resolve a working client token (refresh when expired if credentials exist).
165
+ *
166
+ * @async
167
+ * @param {string} controllerUrl
168
+ * @param {string} environment
169
+ * @param {function(string, string, string, string, string): Promise<Object>} validateClientTokenFn
170
+ * @returns {Promise<Object|null>}
171
+ */
172
+ async function tryResolveClientTokenAuth(controllerUrl, environment, validateClientTokenFn) {
173
+ const entries = await getClientEntriesForController(environment, controllerUrl);
174
+ const appNames = Object.keys(entries);
175
+ if (appNames.length === 0) {
176
+ return null;
177
+ }
178
+
179
+ const ordered = ['dataplane', ...appNames.filter(a => a !== 'dataplane').sort()];
180
+ let lastFailure = null;
181
+
182
+ for (const appName of ordered) {
183
+ try {
184
+ const refreshed = await getOrRefreshClientToken(environment, appName, controllerUrl);
185
+ if (!refreshed?.token) {
186
+ continue;
187
+ }
188
+ const stored = await config.getClientToken(environment, appName);
189
+ const result = await validateClientTokenFn(
190
+ refreshed.token,
191
+ controllerUrl,
192
+ environment,
193
+ appName,
194
+ stored?.expiresAt
195
+ );
196
+ if (result.authenticated) {
197
+ return result;
198
+ }
199
+ lastFailure = result;
200
+ } catch {
201
+ // Try next app
202
+ }
203
+ }
204
+
205
+ return lastFailure;
206
+ }
207
+
208
+ module.exports = {
209
+ refreshExpiredClientTokensWherePossible,
210
+ displayStoredClientTokensSection,
211
+ tryResolveClientTokenAuth,
212
+ listClientTokenRows,
213
+ getClientEntriesForController
214
+ };
@@ -0,0 +1,269 @@
1
+ /**
2
+ * Auth-status dataplane version refresh + compatibility resolution (plan 142.0).
3
+ *
4
+ * Extracted from `auth-status.js` to keep that file under the 500-line limit
5
+ * (`.cursor/rules/project-rules.mdc → File and Function Size Limits`). The
6
+ * helpers here are pure-ish (only depend on the dataplane health API and the
7
+ * `device.<controllerUrl>` cache module) so they are easy to mock in unit
8
+ * tests.
9
+ *
10
+ * @fileoverview Resolve dataplane version + Builder CLI compatibility for auth status
11
+ * @author AI Fabrix Team
12
+ * @version 2.0.0
13
+ */
14
+
15
+ 'use strict';
16
+
17
+ const { fetchDataplaneGeneralHealth } = require('../api/dataplane-health.api');
18
+ const {
19
+ updateDeviceDataplaneVersions,
20
+ getDeviceDataplaneVersions
21
+ } = require('../core/config-device-dataplane');
22
+ const {
23
+ getInstalledCliVersion
24
+ } = require('../utils/dataplane-cli-version-gate');
25
+ const { isAtLeast, isValidSemver } = require('../utils/semver-compare');
26
+
27
+ /** Max wall time for post-login version cache refresh (silent after success banner). */
28
+ const LOGIN_POST_REFRESH_BUDGET_MS = 12000;
29
+
30
+ /** Per health probe during login refresh (two endpoints may be tried). */
31
+ const LOGIN_HEALTH_PROBE_TIMEOUT_MS = 6000;
32
+
33
+ /**
34
+ * Resolve when `promise` settles or when `timeoutMs` elapses (returns undefined).
35
+ * @template T
36
+ * @param {Promise<T>} promise
37
+ * @param {number} timeoutMs
38
+ * @returns {Promise<T|undefined>}
39
+ */
40
+ function raceWithTimeout(promise, timeoutMs) {
41
+ return Promise.race([
42
+ promise,
43
+ new Promise((resolve) => {
44
+ setTimeout(() => resolve(undefined), timeoutMs);
45
+ })
46
+ ]);
47
+ }
48
+
49
+ /**
50
+ * Per plan 142.0: invalid semver from the dataplane is logged but does **not**
51
+ * block the CLI; missing min version means no enforcement.
52
+ *
53
+ * @param {string} cliVersion
54
+ * @param {string|undefined} minBuilderCliVersion
55
+ * @returns {boolean}
56
+ */
57
+ function computeCompatibility(cliVersion, minBuilderCliVersion) {
58
+ if (!minBuilderCliVersion) return true;
59
+ if (!isValidSemver(minBuilderCliVersion) || !isValidSemver(cliVersion)) {
60
+ return true;
61
+ }
62
+ return isAtLeast(cliVersion, minBuilderCliVersion);
63
+ }
64
+
65
+ /**
66
+ * Read `device.<controllerUrl>` cache without throwing.
67
+ * @private
68
+ * @async
69
+ * @param {string} controllerUrl
70
+ * @returns {Promise<Object|null>}
71
+ */
72
+ async function safeReadCache(controllerUrl) {
73
+ if (!controllerUrl) return null;
74
+ try {
75
+ return await getDeviceDataplaneVersions(controllerUrl);
76
+ } catch (_error) {
77
+ return null;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Refresh dataplane health, persist `device.<controllerUrl>` version cache,
83
+ * and derive the compatibility verdict against the installed Builder CLI.
84
+ * Falls back to the cached entry when the live probe fails so `auth status`
85
+ * still shows the last-known values offline.
86
+ *
87
+ * @async
88
+ * @param {string} controllerUrl - Controller URL (cache key)
89
+ * @param {string} dataplaneUrl - Dataplane base URL for the health probe
90
+ * @returns {Promise<{
91
+ * dataplaneVersion: string|undefined,
92
+ * minBuilderCliVersion: string|undefined,
93
+ * cliVersion: string,
94
+ * compatible: boolean
95
+ * }>}
96
+ */
97
+ async function refreshDataplaneVersionInfo(controllerUrl, dataplaneUrl, options = {}) {
98
+ const cliVersion = getInstalledCliVersion();
99
+ let dataplaneVersion;
100
+ let minBuilderCliVersion;
101
+ const healthOpts = {};
102
+ if (options.healthTimeoutMs !== undefined && options.healthTimeoutMs !== null) {
103
+ healthOpts.timeoutMs = options.healthTimeoutMs;
104
+ }
105
+
106
+ try {
107
+ const snapshot = await fetchDataplaneGeneralHealth(dataplaneUrl, healthOpts);
108
+ if (snapshot) {
109
+ dataplaneVersion = snapshot.version;
110
+ minBuilderCliVersion = snapshot.minBuilderCliVersion;
111
+ try {
112
+ await updateDeviceDataplaneVersions(controllerUrl, {
113
+ version: dataplaneVersion,
114
+ minBuilderCliVersion: minBuilderCliVersion || null
115
+ });
116
+ } catch (_persistError) {
117
+ // cache write failure must not break the display
118
+ }
119
+ }
120
+ } catch (_fetchError) {
121
+ // ignore — fall through to cached values
122
+ }
123
+
124
+ if (!dataplaneVersion && !minBuilderCliVersion) {
125
+ const cached = await safeReadCache(controllerUrl);
126
+ if (cached) {
127
+ dataplaneVersion = cached.version;
128
+ minBuilderCliVersion = cached.minBuilderCliVersion;
129
+ }
130
+ }
131
+
132
+ const compatible = computeCompatibility(cliVersion, minBuilderCliVersion);
133
+ return { dataplaneVersion, minBuilderCliVersion, cliVersion, compatible };
134
+ }
135
+
136
+ /**
137
+ * Load cached versions only (no live probe). Used when the dataplane is
138
+ * unreachable so `auth status` can still surface a known compatibility floor.
139
+ *
140
+ * @async
141
+ * @param {string} controllerUrl
142
+ * @returns {Promise<{
143
+ * dataplaneVersion: string|undefined,
144
+ * minBuilderCliVersion: string|undefined,
145
+ * cliVersion: string,
146
+ * compatible: boolean
147
+ * }>}
148
+ */
149
+ async function loadCachedVersionInfo(controllerUrl) {
150
+ const cliVersion = getInstalledCliVersion();
151
+ const cached = await safeReadCache(controllerUrl);
152
+ const dataplaneVersion = cached ? cached.version : undefined;
153
+ const minBuilderCliVersion = cached ? cached.minBuilderCliVersion : undefined;
154
+ return {
155
+ dataplaneVersion,
156
+ minBuilderCliVersion,
157
+ cliVersion,
158
+ compatible: computeCompatibility(cliVersion, minBuilderCliVersion)
159
+ };
160
+ }
161
+
162
+ /**
163
+ * Resolve bearer auth after login (device token preferred, else client app token).
164
+ * @private
165
+ * @async
166
+ * @param {string} controllerUrl
167
+ * @param {string} environment
168
+ * @param {Object} loginOptions
169
+ * @returns {Promise<Object|null>}
170
+ */
171
+ async function resolveAuthConfigAfterLogin(controllerUrl, environment, loginOptions) {
172
+ const { getOrRefreshDeviceToken, getDeploymentAuth } = require('../utils/token-manager');
173
+ const device = await getOrRefreshDeviceToken(controllerUrl);
174
+ if (device && device.token) {
175
+ return { type: 'bearer', token: device.token };
176
+ }
177
+ if (!loginOptions.app) {
178
+ return null;
179
+ }
180
+ try {
181
+ return await getDeploymentAuth(controllerUrl, environment, loginOptions.app);
182
+ } catch (_authError) {
183
+ return null;
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Resolve dataplane URL after login (mirrors auth-status silent resolver).
189
+ * @private
190
+ * @async
191
+ * @param {string} controllerUrl
192
+ * @param {string} environment
193
+ * @param {Object} authConfig
194
+ * @returns {Promise<string|null>}
195
+ */
196
+ async function resolveDataplaneUrlAfterLogin(controllerUrl, environment, authConfig) {
197
+ const { findDataplaneServiceAppKey } = require('./wizard-dataplane');
198
+ const { getDataplaneUrl } = require('../datasource/deploy');
199
+ try {
200
+ const dataplaneAppKey = await findDataplaneServiceAppKey(
201
+ controllerUrl,
202
+ environment,
203
+ authConfig
204
+ );
205
+ if (dataplaneAppKey) {
206
+ return await getDataplaneUrl(controllerUrl, dataplaneAppKey, environment, authConfig);
207
+ }
208
+ return await getDataplaneUrl(controllerUrl, 'dataplane', environment, authConfig);
209
+ } catch (_resolveError) {
210
+ return null;
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Best-effort health refresh after login when dataplane URL is discoverable.
216
+ * Never throws — cache failures must not block login success.
217
+ *
218
+ * @async
219
+ * @param {string} controllerUrl - Controller URL (device cache key)
220
+ * @param {Object} [loginOptions]
221
+ * @param {string} [loginOptions.app] - App name (credentials login) for client-token auth
222
+ * @returns {Promise<void>}
223
+ */
224
+ async function tryRefreshDataplaneVersionAfterLoginWork(controllerUrl, loginOptions = {}) {
225
+ const { resolveEnvironment } = require('../core/config');
226
+ const environment = await resolveEnvironment();
227
+ const authConfig = await resolveAuthConfigAfterLogin(
228
+ controllerUrl,
229
+ environment,
230
+ loginOptions
231
+ );
232
+ if (!authConfig) {
233
+ return;
234
+ }
235
+ const dataplaneUrl = await resolveDataplaneUrlAfterLogin(
236
+ controllerUrl,
237
+ environment,
238
+ authConfig
239
+ );
240
+ if (dataplaneUrl) {
241
+ await refreshDataplaneVersionInfo(controllerUrl, dataplaneUrl, {
242
+ healthTimeoutMs: LOGIN_HEALTH_PROBE_TIMEOUT_MS
243
+ });
244
+ }
245
+ }
246
+
247
+ async function tryRefreshDataplaneVersionAfterLogin(controllerUrl, loginOptions = {}) {
248
+ if (!controllerUrl || typeof controllerUrl !== 'string') {
249
+ return;
250
+ }
251
+ try {
252
+ await raceWithTimeout(
253
+ tryRefreshDataplaneVersionAfterLoginWork(controllerUrl, loginOptions),
254
+ LOGIN_POST_REFRESH_BUDGET_MS
255
+ );
256
+ } catch (_error) {
257
+ // best-effort only
258
+ }
259
+ }
260
+
261
+ module.exports = {
262
+ refreshDataplaneVersionInfo,
263
+ loadCachedVersionInfo,
264
+ computeCompatibility,
265
+ tryRefreshDataplaneVersionAfterLogin,
266
+ LOGIN_POST_REFRESH_BUDGET_MS,
267
+ LOGIN_HEALTH_PROBE_TIMEOUT_MS,
268
+ raceWithTimeout
269
+ };
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Auth-status dataplane version block + blocking upgrade UX (plan 142.0).
3
+ *
4
+ * Kept in a separate module so `auth-status.js` stays under the 500-line
5
+ * limit and so the same display helpers can be reused by future dataplane
6
+ * gate surfaces (`upload`, `wizard`, `deploy`, …).
7
+ *
8
+ * @fileoverview TTY rendering for dataplane / Builder CLI compatibility
9
+ * @author AI Fabrix Team
10
+ * @version 2.0.0
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const chalk = require('chalk');
16
+ const logger = require('../utils/logger');
17
+ const {
18
+ successGlyph,
19
+ failureGlyph
20
+ } = require('../utils/cli-test-layout-chalk');
21
+ const {
22
+ formatCliVersionGateError
23
+ } = require('../utils/dataplane-cli-version-help');
24
+
25
+ /**
26
+ * Format a single key/value version line indented under the dataplane block.
27
+ * @private
28
+ * @param {string} label
29
+ * @param {string|undefined|null} value
30
+ * @returns {string}
31
+ */
32
+ function versionLine(label, value) {
33
+ const display = value && String(value).trim() ? chalk.cyan(value) : chalk.gray('—');
34
+ return ` ${label}: ${display}`;
35
+ }
36
+
37
+ /**
38
+ * Render the dataplane version subsection on `aifabrix auth status`.
39
+ *
40
+ * Designed to be called immediately after the existing connection/status line,
41
+ * so the output reads as a single grouped block per layout.md §HEADER BLOCK.
42
+ *
43
+ * @param {Object} params
44
+ * @param {boolean} params.connected - Whether dataplane reachability probe passed
45
+ * @param {string|undefined} params.dataplaneVersion - From `device.<controllerUrl>.dataplane-version`
46
+ * @param {string|undefined} params.minBuilderCliVersion - From `device.<controllerUrl>.dataplane-min-cli-version`
47
+ * @param {string} params.cliVersion - Installed Builder CLI version
48
+ * @param {boolean} params.compatible - true when CLI ≥ minimum (or no minimum enforced)
49
+ * @returns {void}
50
+ */
51
+ function displayDataplaneVersionSection({
52
+ connected,
53
+ dataplaneVersion,
54
+ minBuilderCliVersion,
55
+ cliVersion,
56
+ compatible
57
+ }) {
58
+ if (!connected && !dataplaneVersion) {
59
+ // Nothing to add — keep auth status quiet when the dataplane probe failed
60
+ // and no cached version is known (the reachability line already told the user).
61
+ return;
62
+ }
63
+ logger.log(versionLine('Dataplane version', dataplaneVersion));
64
+ logger.log(versionLine('Min Builder CLI', minBuilderCliVersion));
65
+ logger.log(versionLine('This CLI', cliVersion));
66
+ if (!minBuilderCliVersion) {
67
+ logger.log(` Compatibility: ${chalk.gray('Not enforced')}`);
68
+ return;
69
+ }
70
+ if (compatible) {
71
+ logger.log(` Compatibility: ${successGlyph()} ${chalk.green('OK')}`);
72
+ } else {
73
+ logger.log(` Compatibility: ${failureGlyph()} ${chalk.red('Upgrade required')}`);
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Render the blocking upgrade block (red ✖ + Next actions) under the
79
+ * dataplane subsection when the CLI is incompatible.
80
+ *
81
+ * @param {Object} params
82
+ * @param {string} params.required - Required minimum semver
83
+ * @param {string} params.installed - Currently installed semver
84
+ * @returns {void}
85
+ */
86
+ function displayCliUpgradeRequired({ required, installed }) {
87
+ logger.log('');
88
+ logger.log(formatCliVersionGateError(required, installed));
89
+ }
90
+
91
+ module.exports = {
92
+ displayDataplaneVersionSection,
93
+ displayCliUpgradeRequired
94
+ };