@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,241 @@
1
+ /**
2
+ * Remove saved datasource test debug JSON under integration/<app>/logs/.
3
+ * @fileoverview Local cleanup for test / test-integration / test-e2e / verify-trust logs
4
+ * @author AI Fabrix Team
5
+ * @version 1.0.0
6
+ */
7
+
8
+ const path = require('path');
9
+ const fsp = require('node:fs').promises;
10
+ const chalk = require('chalk');
11
+ const logger = require('../utils/logger');
12
+ const { getIntegrationPath, listIntegrationAppNames } = require('../utils/paths');
13
+ const { isStructuralTestLogFileName } = require('./log-viewer');
14
+ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
15
+
16
+ /** @type {readonly string[]} */
17
+ const LOG_CLEAN_TYPES = ['test', 'integration', 'e2e', 'trust', 'all'];
18
+
19
+ /**
20
+ * @param {string} [type]
21
+ * @returns {'test'|'integration'|'e2e'|'trust'|'all'}
22
+ */
23
+ function normalizeLogCleanType(type) {
24
+ const raw = (type === undefined || type === null ? 'all' : String(type)).trim().toLowerCase();
25
+ if (raw === '' || raw === 'all') return 'all';
26
+ if (LOG_CLEAN_TYPES.includes(raw)) return raw;
27
+ throw new Error(
28
+ `Invalid --type "${type}". Use: ${LOG_CLEAN_TYPES.join(', ')}`
29
+ );
30
+ }
31
+
32
+ /**
33
+ * @param {string} fileName
34
+ * @param {'test'|'integration'|'e2e'|'trust'|'all'} type
35
+ * @returns {boolean}
36
+ */
37
+ function matchesLogCleanType(fileName, type) {
38
+ if (!fileName || !fileName.endsWith('.json')) return false;
39
+ if (type === 'all') return true;
40
+ if (type === 'test') return isStructuralTestLogFileName(fileName);
41
+ if (type === 'integration') {
42
+ return fileName.startsWith('test-integration-');
43
+ }
44
+ if (type === 'e2e') return fileName.startsWith('test-e2e-');
45
+ if (type === 'trust') return fileName.startsWith('verify-trust-');
46
+ return false;
47
+ }
48
+
49
+ /**
50
+ * @param {string} logsDir
51
+ * @param {'test'|'integration'|'e2e'|'trust'|'all'} type
52
+ * @returns {Promise<string[]>}
53
+ */
54
+ async function listMatchingLogFiles(logsDir, type) {
55
+ let entries;
56
+ try {
57
+ entries = await fsp.readdir(logsDir, { withFileTypes: true });
58
+ } catch (err) {
59
+ if (err.code === 'ENOENT') return [];
60
+ throw err;
61
+ }
62
+ return entries
63
+ .filter(e => e.isFile() && matchesLogCleanType(e.name, type))
64
+ .map(e => path.join(logsDir, e.name))
65
+ .sort((a, b) => a.localeCompare(b));
66
+ }
67
+
68
+ /**
69
+ * @param {string} appKey
70
+ * @returns {Promise<void>}
71
+ */
72
+ async function assertIntegrationAppExists(appKey) {
73
+ const appPath = getIntegrationPath(appKey);
74
+ try {
75
+ const st = await fsp.stat(appPath);
76
+ if (!st.isDirectory()) {
77
+ throw new Error(`Integration folder is not a directory: ${appPath}`);
78
+ }
79
+ } catch (err) {
80
+ if (err.code === 'ENOENT') {
81
+ throw new Error(`Integration folder not found: ${appPath}`);
82
+ }
83
+ throw err;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * @param {Object} options
89
+ * @param {boolean} [options.all]
90
+ * @param {string} [options.app]
91
+ * @returns {Promise<string[]>}
92
+ */
93
+ async function resolveTargetAppKeys(options) {
94
+ const useAll = Boolean(options.all);
95
+ const app = options.app && typeof options.app === 'string' ? options.app.trim() : '';
96
+ if (useAll && app) {
97
+ throw new Error('Use either --app <systemKey> or --all, not both');
98
+ }
99
+ if (!useAll && !app) {
100
+ throw new Error('Specify --app <systemKey> or --all');
101
+ }
102
+ if (useAll) {
103
+ return listIntegrationAppNames();
104
+ }
105
+ await assertIntegrationAppExists(app);
106
+ return [app];
107
+ }
108
+
109
+ /**
110
+ * @param {string[]} appKeys
111
+ * @param {'test'|'integration'|'e2e'|'trust'|'all'} type
112
+ * @returns {Promise<Array<{ appKey: string, logsDir: string, files: string[] }>>}
113
+ */
114
+ async function collectLogFilesByApp(appKeys, type) {
115
+ const groups = [];
116
+ for (const appKey of appKeys) {
117
+ const logsDir = path.join(getIntegrationPath(appKey), 'logs');
118
+ const files = await listMatchingLogFiles(logsDir, type);
119
+ if (files.length > 0) {
120
+ groups.push({ appKey, logsDir, files });
121
+ }
122
+ }
123
+ return groups;
124
+ }
125
+
126
+ /**
127
+ * @param {Array<{ appKey: string, files: string[] }>} groups
128
+ * @param {boolean} dryRun
129
+ * @returns {Promise<{ removedCount: number, paths: string[] }>}
130
+ */
131
+ async function removeCollectedFiles(groups, dryRun) {
132
+ const paths = groups.flatMap(g => g.files);
133
+ if (dryRun || paths.length === 0) {
134
+ return { removedCount: 0, paths };
135
+ }
136
+ await Promise.all(paths.map(filePath => fsp.unlink(filePath)));
137
+ return { removedCount: paths.length, paths };
138
+ }
139
+
140
+ /**
141
+ * @param {Object} options
142
+ * @param {boolean} options.dryRun
143
+ * @param {'test'|'integration'|'e2e'|'trust'|'all'} options.type
144
+ * @param {Array<{ appKey: string, files: string[] }>} groups
145
+ * @param {number} removedCount
146
+ */
147
+ function printCleanLogsSummary(options, groups, removedCount) {
148
+ const totalFiles = groups.reduce((n, g) => n + g.files.length, 0);
149
+ const appCount = groups.length;
150
+ if (options.dryRun) {
151
+ if (totalFiles === 0) {
152
+ logger.log(chalk.gray('No matching log files found.'));
153
+ return;
154
+ }
155
+ logger.log('');
156
+ logger.log(
157
+ chalk.gray(
158
+ `Would remove ${totalFiles} file(s) from ${appCount} integration folder(s) (type: ${options.type}).`
159
+ )
160
+ );
161
+ return;
162
+ }
163
+ if (totalFiles === 0) {
164
+ logger.log(chalk.gray('No matching log files to remove.'));
165
+ return;
166
+ }
167
+ logger.log('');
168
+ logger.log(formatSuccessLine(`Removed ${removedCount} log file(s) from ${appCount} integration folder(s).`));
169
+ }
170
+
171
+ /**
172
+ * @param {boolean} dryRun
173
+ * @param {Array<{ appKey: string, logsDir: string, files: string[] }>} groups
174
+ */
175
+ function printCleanLogsFileList(dryRun, groups) {
176
+ const prefix = dryRun ? 'Would remove' : 'Removing';
177
+ for (const { appKey, files } of groups) {
178
+ logger.log('');
179
+ logger.log(chalk.bold.white(`${appKey}/logs/`));
180
+ for (const filePath of files) {
181
+ logger.log(chalk.gray(` ${prefix}: ${filePath}`));
182
+ }
183
+ }
184
+ }
185
+
186
+ /**
187
+ * @param {Object} result
188
+ * @param {boolean} result.dryRun
189
+ * @param {string} result.type
190
+ * @param {string[]} result.appKeys
191
+ * @param {string[]} result.paths
192
+ * @param {number} result.removedCount
193
+ */
194
+ function printCleanLogsJson(result) {
195
+ const payload = {
196
+ dryRun: result.dryRun,
197
+ type: result.type,
198
+ appKeys: result.appKeys,
199
+ fileCount: result.paths.length,
200
+ paths: result.paths,
201
+ removedCount: result.removedCount
202
+ };
203
+ logger.log(JSON.stringify(payload, null, 2));
204
+ }
205
+
206
+ /**
207
+ * @async
208
+ * @param {Object} options
209
+ * @param {boolean} [options.all]
210
+ * @param {string} [options.app]
211
+ * @param {string} [options.type]
212
+ * @param {boolean} [options.dryRun]
213
+ * @param {boolean} [options.json]
214
+ * @returns {Promise<{ dryRun: boolean, type: string, appKeys: string[], paths: string[], removedCount: number }>}
215
+ */
216
+ async function runCleanLogs(options = {}) {
217
+ const dryRun = Boolean(options.dryRun);
218
+ const json = Boolean(options.json);
219
+ const type = normalizeLogCleanType(options.type);
220
+ const appKeys = await resolveTargetAppKeys(options);
221
+ const groups = await collectLogFilesByApp(appKeys, type);
222
+ const { removedCount, paths } = await removeCollectedFiles(groups, dryRun);
223
+ const result = { dryRun, type, appKeys, paths, removedCount };
224
+ if (json) {
225
+ printCleanLogsJson(result);
226
+ } else {
227
+ printCleanLogsFileList(dryRun, groups);
228
+ printCleanLogsSummary({ dryRun, type }, groups, removedCount);
229
+ }
230
+ return result;
231
+ }
232
+
233
+ module.exports = {
234
+ LOG_CLEAN_TYPES,
235
+ normalizeLogCleanType,
236
+ matchesLogCleanType,
237
+ listMatchingLogFiles,
238
+ resolveTargetAppKeys,
239
+ collectLogFilesByApp,
240
+ runCleanLogs
241
+ };
@@ -13,7 +13,15 @@ const chalk = require('chalk');
13
13
  const logger = require('../utils/logger');
14
14
  const { resolveAppKeyForDatasource } = require('./resolve-app');
15
15
  const { getIntegrationPath } = require('../utils/paths');
16
- const { sectionTitle, headerKeyValue, formatBlockingError, successGlyph, failureGlyph } = require('../utils/cli-test-layout-chalk');
16
+ const {
17
+ sectionTitle,
18
+ headerKeyValue,
19
+ formatBlockingError,
20
+ successGlyph,
21
+ failureGlyph
22
+ } = require('../utils/cli-test-layout-chalk');
23
+ const { statusGlyph } = require('../utils/datasource-test-run-display');
24
+ const { trustDecisionToRowStatus } = require('./agent-trust-map');
17
25
 
18
26
  /**
19
27
  * Get the path to the latest log file in a directory matching a glob-like pattern
@@ -48,7 +56,13 @@ async function getLatestLogPath(logsDir, pattern) {
48
56
  */
49
57
  function isStructuralTestLogFileName(name) {
50
58
  if (!name || typeof name !== 'string' || !name.endsWith('.json')) return false;
51
- if (name.startsWith('test-e2e-') || name.startsWith('test-integration-')) return false;
59
+ if (
60
+ name.startsWith('test-e2e-') ||
61
+ name.startsWith('test-integration-') ||
62
+ name.startsWith('verify-trust-')
63
+ ) {
64
+ return false;
65
+ }
52
66
  return name.startsWith('test-');
53
67
  }
54
68
 
@@ -232,6 +246,85 @@ function structuralEnvelopeStatusLine(status) {
232
246
  * @param {Object} data - Parsed JSON { request, response?, error? }
233
247
  * @param {string} [fileName] - Log file name for header
234
248
  */
249
+ function trustLogGlyph(trustDecision) {
250
+ return statusGlyph(trustDecisionToRowStatus(trustDecision));
251
+ }
252
+
253
+ function logTrustWarningsSection(run) {
254
+ const warnings = Array.isArray(run.highLevelWarnings) ? run.highLevelWarnings : [];
255
+ if (!warnings.length) return;
256
+ logger.log(sectionTitle('Warnings:'));
257
+ for (const w of warnings) {
258
+ logger.log(` ${chalk.yellow('•')} ${chalk.white(String(w))}`);
259
+ }
260
+ }
261
+
262
+ function logTrustFindingsSection(run) {
263
+ const findings = Array.isArray(run.findings) ? run.findings : [];
264
+ if (!findings.length) return;
265
+ logger.log(sectionTitle('Findings:'));
266
+ const cap = 15;
267
+ for (let i = 0; i < Math.min(cap, findings.length); i += 1) {
268
+ const f = findings[i];
269
+ const code = f?.code || f?.stableCode || 'finding';
270
+ const msg = f?.message ? String(f.message) : '';
271
+ logger.log(` ${chalk.gray(`• ${code}`)} ${msg}`.trim());
272
+ }
273
+ if (findings.length > cap) {
274
+ logger.log(chalk.gray(` … and ${findings.length - cap} more (open JSON file for full list)`));
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Format agent trust log (`datasource verify-trust --debug` → `verify-trust-*.json`).
280
+ * @param {Object} data - Parsed JSON { request, response?, error? }
281
+ * @param {string} [fileName] - Log file name for header
282
+ */
283
+ function formatTrustLog(data, fileName) {
284
+ logger.log('');
285
+ logger.log(sectionTitle('Agent trust log'));
286
+ if (fileName) logger.log(chalk.gray(fileName));
287
+ const req = data.request || {};
288
+ logger.log('');
289
+ logger.log(sectionTitle('Request:'));
290
+ logger.log(headerKeyValue('datasourceKey:', String(req.datasourceKey ?? '—')));
291
+ if (req.revalidate !== undefined) {
292
+ logger.log(chalk.gray(` revalidate: ${req.revalidate}`));
293
+ }
294
+ if (data.error) {
295
+ logger.log('');
296
+ logger.log(formatBlockingError(`Error: ${data.error}`));
297
+ return;
298
+ }
299
+ const run = data.response || {};
300
+ logger.log('');
301
+ logger.log(sectionTitle('Response:'));
302
+ logger.log(headerKeyValue('System:', String(run.systemKey ?? '—')));
303
+ const confPct = Math.round((Number(run.confidence) || 0) * 100);
304
+ logger.log(
305
+ headerKeyValue(
306
+ 'Trust:',
307
+ `${trustLogGlyph(run.trustDecision)} ${run.trustDecision ?? '—'} · ${confPct}% · ${run.validationStatus ?? '—'}`
308
+ )
309
+ );
310
+ if (run.validatedAt) {
311
+ logger.log(chalk.gray(` validatedAt: ${run.validatedAt}`));
312
+ }
313
+ if (run.inputHash) {
314
+ logger.log(chalk.gray(` inputHash: ${truncate(String(run.inputHash), 40)}`));
315
+ }
316
+ if (run.cacheHit !== undefined && run.cacheHit !== null) {
317
+ logger.log(chalk.gray(` cacheHit: ${run.cacheHit}`));
318
+ }
319
+ if (run.summary) {
320
+ logger.log('');
321
+ logger.log(chalk.white(' Summary'));
322
+ logger.log(` ${run.summary}`);
323
+ }
324
+ logTrustWarningsSection(run);
325
+ logTrustFindingsSection(run);
326
+ }
327
+
235
328
  function formatStructuralTestLog(data, fileName) {
236
329
  logger.log('');
237
330
  logger.log(sectionTitle('Structural validation log'));
@@ -263,7 +356,7 @@ function formatStructuralTestLog(data, fileName) {
263
356
  /**
264
357
  * Format log content by type
265
358
  * @param {Object} parsed - Parsed JSON log
266
- * @param {'test'|'test-e2e'|'test-integration'} logType - Log type
359
+ * @param {'test'|'test-e2e'|'test-integration'|'verify-trust'} logType - Log type
267
360
  * @param {string} [fileName] - File name for header
268
361
  */
269
362
  function formatLogContent(parsed, logType, fileName) {
@@ -271,11 +364,15 @@ function formatLogContent(parsed, logType, fileName) {
271
364
  formatStructuralTestLog(parsed, fileName);
272
365
  return;
273
366
  }
367
+ if (logType === 'verify-trust') {
368
+ formatTrustLog(parsed, fileName);
369
+ return;
370
+ }
274
371
  if (logType === 'test-e2e') {
275
372
  formatE2ELog(parsed, fileName);
276
- } else {
277
- formatIntegrationLog(parsed, fileName);
373
+ return;
278
374
  }
375
+ formatIntegrationLog(parsed, fileName);
279
376
  }
280
377
 
281
378
  /**
@@ -313,7 +410,7 @@ function parseDatasourceLogJson(content, logPath) {
313
410
  * @param {Object} options - Options
314
411
  * @param {string} [options.app] - App key (optional, resolved from key if omitted)
315
412
  * @param {string} [options.file] - Path to log file (overrides app resolution)
316
- * @param {'test'|'test-e2e'|'test-integration'} options.logType - Log type
413
+ * @param {'test'|'test-e2e'|'test-integration'|'verify-trust'} options.logType - Log type
317
414
  * @throws {Error} When file not found or invalid JSON
318
415
  */
319
416
  /* eslint-disable-next-line max-statements -- Resolve path, read, parse, format */
@@ -336,6 +433,13 @@ async function runLogViewer(datasourceKey, options) {
336
433
  `No structural validation log found in ${logsDir}. Run: aifabrix datasource test <key> --debug`
337
434
  );
338
435
  }
436
+ } else if (logType === 'verify-trust') {
437
+ logPath = await getLatestLogPath(logsDir, 'verify-trust-');
438
+ if (!logPath) {
439
+ throw new Error(
440
+ `No verify-trust log found in ${logsDir}. Run: aifabrix datasource verify-trust <key> --debug`
441
+ );
442
+ }
339
443
  } else {
340
444
  const pattern = logType === 'test-e2e' ? 'test-e2e-' : 'test-integration-';
341
445
  logPath = await getLatestLogPath(logsDir, pattern);
@@ -360,6 +464,7 @@ module.exports = {
360
464
  formatE2ELog,
361
465
  formatIntegrationLog,
362
466
  formatStructuralTestLog,
467
+ formatTrustLog,
363
468
  parseDatasourceLogJson,
364
469
  runLogViewer
365
470
  };
@@ -0,0 +1,85 @@
1
+ /**
2
+ * @fileoverview Parse JSON array and NDJSON record fixture files (plan 144)
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const fs = require('fs');
10
+
11
+ /**
12
+ * @param {string} content
13
+ * @returns {Object[]}
14
+ */
15
+ function parseNdjsonContent(content) {
16
+ const lines = String(content).split(/\r?\n/);
17
+ const records = [];
18
+ for (let i = 0; i < lines.length; i += 1) {
19
+ const line = lines[i].trim();
20
+ if (!line) continue;
21
+ let parsed;
22
+ try {
23
+ parsed = JSON.parse(line);
24
+ } catch (err) {
25
+ throw new Error(`NDJSON parse error at line ${i + 1}: ${err.message}`);
26
+ }
27
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
28
+ throw new Error(`NDJSON line ${i + 1}: expected a JSON object`);
29
+ }
30
+ records.push(parsed);
31
+ }
32
+ return records;
33
+ }
34
+
35
+ /**
36
+ * @param {string} content
37
+ * @returns {Object[]}
38
+ */
39
+ function parseJsonArrayContent(content) {
40
+ let parsed;
41
+ try {
42
+ parsed = JSON.parse(content);
43
+ } catch (err) {
44
+ throw new Error(`JSON parse error: ${err.message}`);
45
+ }
46
+ if (!Array.isArray(parsed)) {
47
+ throw new Error('JSON file must be a top-level array of objects');
48
+ }
49
+ parsed.forEach((row, index) => {
50
+ if (!row || typeof row !== 'object' || Array.isArray(row)) {
51
+ throw new Error(`JSON array index ${index}: expected an object`);
52
+ }
53
+ });
54
+ return parsed;
55
+ }
56
+
57
+ /**
58
+ * Parse a local record fixture file.
59
+ * @param {string} filePath
60
+ * @param {'json'|'ndjson'} format
61
+ * @returns {Object[]}
62
+ */
63
+ function parseRecordFile(filePath, format) {
64
+ const content = fs.readFileSync(filePath, 'utf8');
65
+ if (format === 'ndjson') {
66
+ return parseNdjsonContent(content);
67
+ }
68
+ return parseJsonArrayContent(content);
69
+ }
70
+
71
+ /**
72
+ * Rough payload size estimate for dry-run (bytes).
73
+ * @param {Object[]} records
74
+ * @returns {number}
75
+ */
76
+ function estimatePayloadBytes(records) {
77
+ return Buffer.byteLength(JSON.stringify(records), 'utf8');
78
+ }
79
+
80
+ module.exports = {
81
+ parseRecordFile,
82
+ parseNdjsonContent,
83
+ parseJsonArrayContent,
84
+ estimatePayloadBytes
85
+ };
@@ -0,0 +1,129 @@
1
+ /**
2
+ * @fileoverview Map fixture payloads to canonical bulk sync records (plan 144)
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ /**
10
+ * @param {Object} obj
11
+ * @returns {boolean}
12
+ */
13
+ function isCanonicalBulkRecord(obj) {
14
+ return (
15
+ obj &&
16
+ typeof obj === 'object' &&
17
+ typeof obj.key === 'string' &&
18
+ obj.key.trim().length > 0 &&
19
+ typeof obj.recordType === 'string' &&
20
+ obj.recordType.trim().length > 0 &&
21
+ obj.metadata !== undefined &&
22
+ obj.metadata !== null &&
23
+ typeof obj.metadata === 'object' &&
24
+ !Array.isArray(obj.metadata)
25
+ );
26
+ }
27
+
28
+ /**
29
+ * @param {string[]} primaryKeyFields
30
+ * @param {Object} payload
31
+ * @returns {string}
32
+ */
33
+ function buildKeyFromPrimaryKey(primaryKeyFields, payload) {
34
+ if (!primaryKeyFields.length) {
35
+ throw new Error('Datasource primaryKey is empty; provide records with a key field');
36
+ }
37
+ if (primaryKeyFields.length === 1) {
38
+ const field = primaryKeyFields[0];
39
+ const value = payload[field];
40
+ if (value === undefined || value === null || String(value).trim() === '') {
41
+ throw new Error(`Missing primary key field '${field}' in record payload`);
42
+ }
43
+ return String(value);
44
+ }
45
+ const parts = [];
46
+ for (const field of primaryKeyFields) {
47
+ const value = payload[field];
48
+ if (value === undefined || value === null || String(value).trim() === '') {
49
+ throw new Error(`Missing composite primary key field '${field}' in record payload`);
50
+ }
51
+ parts.push(`${field}=${String(value)}`);
52
+ }
53
+ return parts.join(',');
54
+ }
55
+
56
+ /**
57
+ * @param {Object} datasource
58
+ * @param {Object} payload
59
+ * @returns {import('../api/types/records-bulk.types').ExternalBulkRecord}
60
+ */
61
+ function mapPayloadToBulkRecord(datasource, payload) {
62
+ const primaryKey = Array.isArray(datasource.primaryKey) ? datasource.primaryKey : [];
63
+ const recordType =
64
+ typeof datasource.resourceType === 'string' && datasource.resourceType.trim()
65
+ ? datasource.resourceType.trim()
66
+ : 'record';
67
+ const key = buildKeyFromPrimaryKey(primaryKey, payload);
68
+ let displayName = '';
69
+ const labelKey = datasource.labelKey;
70
+ if (Array.isArray(labelKey) && labelKey.length > 0) {
71
+ displayName = labelKey
72
+ .map(field => payload[field])
73
+ .filter(v => v !== undefined && v !== null && String(v).trim() !== '')
74
+ .map(v => String(v))
75
+ .join(' ');
76
+ } else if (typeof labelKey === 'string' && labelKey.trim() && payload[labelKey] !== undefined && payload[labelKey] !== null) {
77
+ displayName = String(payload[labelKey]);
78
+ }
79
+ const metadata = { ...payload };
80
+ if (metadata.externalId === undefined) {
81
+ metadata.externalId = key;
82
+ }
83
+ return {
84
+ key,
85
+ displayName: displayName || '',
86
+ recordType,
87
+ metadata
88
+ };
89
+ }
90
+
91
+ /**
92
+ * @param {Object[]} rawRecords
93
+ * @param {Object} datasource
94
+ * @returns {import('../api/types/records-bulk.types').ExternalBulkRecord[]}
95
+ */
96
+ function normalizeRecordsForBulk(rawRecords, datasource) {
97
+ if (!Array.isArray(rawRecords) || rawRecords.length === 0) {
98
+ throw new Error('Record file is empty after parse');
99
+ }
100
+ return rawRecords.map((row, index) => {
101
+ try {
102
+ if (isCanonicalBulkRecord(row)) {
103
+ const meta = { ...row.metadata };
104
+ if (meta.externalId === undefined) {
105
+ meta.externalId = row.key;
106
+ }
107
+ return {
108
+ key: String(row.key).trim(),
109
+ displayName:
110
+ row.displayName !== undefined && row.displayName !== null
111
+ ? String(row.displayName)
112
+ : '',
113
+ recordType: String(row.recordType).trim(),
114
+ metadata: meta
115
+ };
116
+ }
117
+ return mapPayloadToBulkRecord(datasource, row);
118
+ } catch (err) {
119
+ throw new Error(`Record ${index + 1}: ${err.message}`);
120
+ }
121
+ });
122
+ }
123
+
124
+ module.exports = {
125
+ isCanonicalBulkRecord,
126
+ buildKeyFromPrimaryKey,
127
+ mapPayloadToBulkRecord,
128
+ normalizeRecordsForBulk
129
+ };