@aifabrix/builder 2.44.6 → 2.45.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/.cursor/rules/cli-layout.mdc +7 -3
  2. package/.cursor/rules/docs-rules.mdc +3 -1
  3. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
  4. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
  5. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
  6. package/jest.projects.js +100 -1
  7. package/lib/api/agent-metadata-validation.api.js +93 -0
  8. package/lib/api/audit-api-helpers.js +68 -0
  9. package/lib/api/audit.api.js +119 -0
  10. package/lib/api/auth-cache.api.js +44 -0
  11. package/lib/api/certificates.api.js +4 -4
  12. package/lib/api/credential.api.js +2 -2
  13. package/lib/api/dataplane-health.api.js +137 -0
  14. package/lib/api/dataplane-sync.api.js +27 -0
  15. package/lib/api/datasources-core.api.js +21 -18
  16. package/lib/api/datasources-extended.api.js +22 -22
  17. package/lib/api/dimensions.api.js +3 -0
  18. package/lib/api/external-systems.api.js +13 -13
  19. package/lib/api/governance-scenarios.api.js +39 -0
  20. package/lib/api/groups.api.js +79 -0
  21. package/lib/api/index.js +45 -2
  22. package/lib/api/pipeline.api.js +4 -4
  23. package/lib/api/protection.api.js +157 -0
  24. package/lib/api/records-bulk.api.js +30 -0
  25. package/lib/api/records-search-parse.js +49 -0
  26. package/lib/api/records-search.api.js +29 -0
  27. package/lib/api/types/agent-metadata-validation.types.js +67 -0
  28. package/lib/api/types/audit.types.js +72 -0
  29. package/lib/api/types/dataplane-health.types.js +27 -0
  30. package/lib/api/types/dimensions.types.js +2 -0
  31. package/lib/api/types/governance-scenarios.types.js +39 -0
  32. package/lib/api/types/protection.types.js +69 -0
  33. package/lib/api/types/records-bulk.types.js +35 -0
  34. package/lib/api/types/records-search.types.js +28 -0
  35. package/lib/api/user-groups.api.js +50 -0
  36. package/lib/api/users.api.js +95 -0
  37. package/lib/api/validation-run.api.js +3 -3
  38. package/lib/api/validation-runner.js +3 -3
  39. package/lib/api/wizard-platform.api.js +3 -3
  40. package/lib/api/wizard.api.js +21 -21
  41. package/lib/app/deploy.js +34 -19
  42. package/lib/app/helpers.js +3 -3
  43. package/lib/app/index.js +3 -3
  44. package/lib/app/register.js +7 -6
  45. package/lib/app/restart-display.js +52 -21
  46. package/lib/app/rotate-secret.js +7 -6
  47. package/lib/app/run-helpers.js +15 -8
  48. package/lib/app/run.js +57 -9
  49. package/lib/app/show-display.js +7 -0
  50. package/lib/app/show.js +87 -5
  51. package/lib/build/index.js +9 -5
  52. package/lib/cli/index.js +4 -2
  53. package/lib/cli/infra-guided.js +62 -28
  54. package/lib/cli/installation-log-command.js +73 -0
  55. package/lib/cli/setup-app.help.js +50 -2
  56. package/lib/cli/setup-app.js +44 -25
  57. package/lib/cli/setup-app.test-commands.js +80 -21
  58. package/lib/cli/setup-auth.js +124 -52
  59. package/lib/cli/setup-external-system.js +37 -3
  60. package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
  61. package/lib/cli/setup-infra-up-platform-action.js +131 -0
  62. package/lib/cli/setup-infra.js +60 -119
  63. package/lib/cli/setup-platform.js +1 -1
  64. package/lib/cli/setup-utility-help-after.js +45 -0
  65. package/lib/cli/setup-utility-resolve.js +132 -0
  66. package/lib/cli/setup-utility.js +148 -116
  67. package/lib/commands/app-logs.js +81 -33
  68. package/lib/commands/auth-config.js +116 -18
  69. package/lib/commands/auth-status-dataplane-version.js +231 -0
  70. package/lib/commands/auth-status-display.js +94 -0
  71. package/lib/commands/auth-status.js +70 -6
  72. package/lib/commands/credential-push.js +10 -5
  73. package/lib/commands/datasource-load-export-cli.js +178 -0
  74. package/lib/commands/datasource-test-trust-cli.js +114 -0
  75. package/lib/commands/datasource-unified-test-cli.js +25 -17
  76. package/lib/commands/datasource-unified-test-cli.options.js +18 -3
  77. package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
  78. package/lib/commands/datasource-verify-audit-cli.js +96 -0
  79. package/lib/commands/datasource.js +68 -1
  80. package/lib/commands/dimension-value.js +43 -13
  81. package/lib/commands/dimension.js +38 -7
  82. package/lib/commands/identity-apply-sync.js +131 -0
  83. package/lib/commands/identity-group-cli.js +146 -0
  84. package/lib/commands/identity-membership-role-cache.js +175 -0
  85. package/lib/commands/identity-shared.js +94 -0
  86. package/lib/commands/identity-user-cli.js +158 -0
  87. package/lib/commands/identity.js +43 -0
  88. package/lib/commands/login.js +46 -16
  89. package/lib/commands/protection-cli-leaves.js +286 -0
  90. package/lib/commands/protection.js +50 -0
  91. package/lib/commands/repair.js +13 -3
  92. package/lib/commands/setup-modes.js +19 -6
  93. package/lib/commands/setup-prompts.js +41 -8
  94. package/lib/commands/setup.js +114 -9
  95. package/lib/commands/teardown.js +54 -5
  96. package/lib/commands/test-e2e-external.js +40 -9
  97. package/lib/commands/test-governance-cli-options.js +47 -0
  98. package/lib/commands/test-governance-command-action.js +55 -0
  99. package/lib/commands/test-governance-external.js +88 -0
  100. package/lib/commands/test-trust-cli-options.js +32 -0
  101. package/lib/commands/test-trust-command-action.js +118 -0
  102. package/lib/commands/test-trust-external.js +127 -0
  103. package/lib/commands/up-common.js +48 -14
  104. package/lib/commands/up-dataplane.js +21 -18
  105. package/lib/commands/up-miso.js +12 -8
  106. package/lib/commands/upload.js +38 -15
  107. package/lib/commands/wizard-core.js +3 -0
  108. package/lib/core/audit-logger.js +1 -34
  109. package/lib/core/config-admin-email.js +56 -0
  110. package/lib/core/config-device-dataplane.js +185 -0
  111. package/lib/core/config-normalize.js +60 -0
  112. package/lib/core/config-registered-controller-urls.js +54 -0
  113. package/lib/core/config.js +33 -50
  114. package/lib/core/secrets-ensure-infra.js +1 -1
  115. package/lib/core/secrets-env-content.js +86 -90
  116. package/lib/core/secrets-env-declarative-expand.js +170 -0
  117. package/lib/core/secrets-env-write.js +2 -0
  118. package/lib/core/secrets-load.js +106 -102
  119. package/lib/datasource/agent-trust-debug-log.js +45 -0
  120. package/lib/datasource/agent-trust-fetch.js +115 -0
  121. package/lib/datasource/agent-trust-map.js +101 -0
  122. package/lib/datasource/agent-trust-mock.js +44 -0
  123. package/lib/datasource/agent-trust-run.js +93 -0
  124. package/lib/datasource/agent-trust-timeout.js +25 -0
  125. package/lib/datasource/audit-evidence-display.js +80 -0
  126. package/lib/datasource/audit-evidence-extract-debug.js +103 -0
  127. package/lib/datasource/audit-evidence-extract.js +177 -0
  128. package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
  129. package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
  130. package/lib/datasource/audit-evidence-poll.js +109 -0
  131. package/lib/datasource/audit-evidence-run.js +177 -0
  132. package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
  133. package/lib/datasource/audit-evidence-verify-matrix.js +177 -0
  134. package/lib/datasource/audit-evidence-verify.js +108 -0
  135. package/lib/datasource/bulk-loader-run-batches.js +88 -0
  136. package/lib/datasource/bulk-loader-service-retry.js +52 -0
  137. package/lib/datasource/bulk-loader-service.js +127 -0
  138. package/lib/datasource/capability/run-capability-dimension.js +2 -1
  139. package/lib/datasource/datasource-exporter-resolve.js +82 -0
  140. package/lib/datasource/datasource-exporter-service.js +144 -0
  141. package/lib/datasource/datasource-identifier.js +148 -0
  142. package/lib/datasource/datasource-load-export-context.js +92 -0
  143. package/lib/datasource/integration-context.js +6 -4
  144. package/lib/datasource/local-data-paths.js +145 -0
  145. package/lib/datasource/log-cleaner.js +241 -0
  146. package/lib/datasource/log-viewer.js +111 -6
  147. package/lib/datasource/record-file-parser.js +85 -0
  148. package/lib/datasource/record-mapper.js +129 -0
  149. package/lib/datasource/resolve-app.js +74 -55
  150. package/lib/datasource/test-e2e.js +26 -2
  151. package/lib/datasource/unified-validation-run-resolve.js +45 -12
  152. package/lib/datasource/unified-validation-run.js +43 -17
  153. package/lib/datasource/validate.js +37 -20
  154. package/lib/external-system/deploy.js +5 -1
  155. package/lib/external-system/test-system-level.js +25 -4
  156. package/lib/external-system/test.js +3 -1
  157. package/lib/governance/governance-pack-loader.js +70 -0
  158. package/lib/governance/governance-run-display.js +119 -0
  159. package/lib/identity/identity-apply-core.js +108 -0
  160. package/lib/identity/identity-apply-phases.js +244 -0
  161. package/lib/identity/identity-apply-service.js +77 -0
  162. package/lib/identity/identity-csv-parser.js +114 -0
  163. package/lib/internal/fs-real-sync.js +29 -1
  164. package/lib/internal/node-fs.js +2 -0
  165. package/lib/protection/auth-context.js +45 -0
  166. package/lib/protection/convert-batch.js +90 -0
  167. package/lib/protection/load.js +41 -0
  168. package/lib/protection/paths.js +158 -0
  169. package/lib/protection/preflight-datasource-ready.js +46 -0
  170. package/lib/protection/protection-create-probe-lines.js +142 -0
  171. package/lib/protection/protection-create-probes.js +95 -0
  172. package/lib/protection/protection-create-scaffold.js +281 -0
  173. package/lib/protection/protection-display-helpers.js +445 -0
  174. package/lib/protection/protection-display.js +215 -0
  175. package/lib/protection/protection-preset-registry.js +97 -0
  176. package/lib/protection/report-exit.js +62 -0
  177. package/lib/protection/resolve.js +202 -0
  178. package/lib/protection/run-commands-validate.js +130 -0
  179. package/lib/protection/run-commands.js +216 -0
  180. package/lib/protection/run-protection-create-helpers.js +134 -0
  181. package/lib/protection/run-protection-create-probes-flow.js +43 -0
  182. package/lib/protection/run-protection-create.js +191 -0
  183. package/lib/protection/scope.js +22 -0
  184. package/lib/protection/sync-after-upload.js +64 -0
  185. package/lib/protection/upload-batch.js +127 -0
  186. package/lib/protection/validate-batch.js +113 -0
  187. package/lib/protection/validate-local.js +65 -0
  188. package/lib/resolvers/dimension-file.js +29 -4
  189. package/lib/schema/application-schema.json +4 -0
  190. package/lib/schema/external-datasource.schema.json +47 -13
  191. package/lib/schema/flag-map-validation-run.json +2 -2
  192. package/lib/schema/infra.parameter.yaml +10 -0
  193. package/lib/schema/protection.schema.json +374 -0
  194. package/lib/schema/type/credential-field-registry.json +636 -0
  195. package/lib/schema/wizard-config.schema.json +1 -1
  196. package/lib/utils/agent-trust-run-display.js +231 -0
  197. package/lib/utils/agent-trust-run-exit.js +59 -0
  198. package/lib/utils/agent-trust-wait-ui.js +94 -0
  199. package/lib/utils/app-config-resolver.js +24 -1
  200. package/lib/utils/applications-config-defaults.js +206 -0
  201. package/lib/utils/auth-config-validator.js +2 -12
  202. package/lib/utils/bash-secret-env.js +1 -1
  203. package/lib/utils/cli-exit-codes.js +30 -0
  204. package/lib/utils/cli-sync-options.js +33 -0
  205. package/lib/utils/compose-generate-docker-compose.js +111 -6
  206. package/lib/utils/compose-generator.js +17 -8
  207. package/lib/utils/controller-url.js +50 -7
  208. package/lib/utils/credential-push-ui.js +90 -0
  209. package/lib/utils/dataplane-cli-version-gate.js +202 -0
  210. package/lib/utils/dataplane-cli-version-help.js +74 -0
  211. package/lib/utils/datasource-export-display.js +137 -0
  212. package/lib/utils/datasource-load-display.js +174 -0
  213. package/lib/utils/datasource-load-export-ui.js +120 -0
  214. package/lib/utils/datasource-test-run-capability-scope.js +3 -3
  215. package/lib/utils/datasource-test-run-display.js +3 -3
  216. package/lib/utils/datasource-test-run-tty-log.js +5 -1
  217. package/lib/utils/env-copy.js +99 -14
  218. package/lib/utils/env-template.js +5 -1
  219. package/lib/utils/health-check-url.js +18 -15
  220. package/lib/utils/health-check.js +7 -5
  221. package/lib/utils/help-builder.js +30 -24
  222. package/lib/utils/infra-optional-service-flags.js +69 -0
  223. package/lib/utils/installation-log-core.js +282 -0
  224. package/lib/utils/installation-log-record.js +237 -0
  225. package/lib/utils/installation-log.js +123 -0
  226. package/lib/utils/log-redaction.js +105 -0
  227. package/lib/utils/manifest-location.js +164 -0
  228. package/lib/utils/manifest-source-emit.js +184 -0
  229. package/lib/utils/paths.js +238 -89
  230. package/lib/utils/remote-secrets-loader.js +7 -1
  231. package/lib/utils/run-cli-flags.js +29 -0
  232. package/lib/utils/secrets-canonical.js +10 -3
  233. package/lib/utils/secrets-path.js +3 -4
  234. package/lib/utils/secrets-utils.js +20 -10
  235. package/lib/utils/semver-compare.js +127 -0
  236. package/lib/utils/system-builder-root.js +10 -2
  237. package/lib/utils/upload-pipeline-ui.js +91 -0
  238. package/lib/utils/url-declarative-public-base.js +80 -12
  239. package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
  240. package/lib/utils/url-declarative-resolve-build.js +24 -393
  241. package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
  242. package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
  243. package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
  244. package/lib/utils/url-declarative-resolve.js +47 -7
  245. package/lib/utils/url-declarative-runtime-base-path.js +21 -1
  246. package/lib/utils/urls-local-registry-scan.js +103 -0
  247. package/lib/utils/urls-local-registry.js +161 -90
  248. package/lib/utils/validation-poll-ui.js +14 -7
  249. package/lib/validation/wizard-config-validator.js +58 -19
  250. package/package.json +3 -1
  251. package/templates/applications/dataplane/application.yaml +4 -0
  252. package/templates/applications/dataplane/env.template +13 -1
  253. package/templates/applications/dataplane/rbac.yaml +4 -0
  254. package/templates/applications/miso-controller/application.yaml +2 -0
  255. package/templates/applications/miso-controller/env.template +27 -29
  256. package/templates/external-system/README.md.hbs +56 -8
  257. package/.npmrc.token +0 -1
@@ -0,0 +1,231 @@
1
+ /**
2
+ * @fileoverview TTY display for agent metadata trust runs (plan 143).
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const chalk = require('chalk');
10
+ const logger = require('./logger');
11
+ const { sectionTitle, headerKeyValue, metadata: metaGray } = require('./cli-test-layout-chalk');
12
+ const { statusGlyph } = require('./datasource-test-run-display');
13
+ const { trustDecisionToRowStatus } = require('../datasource/agent-trust-map');
14
+
15
+ const SEP = '────────────────────────────────';
16
+
17
+ /** @type {{ datasource: number, trust: number, conf: number, status: number, cache: number }} */
18
+ const TRUST_TABLE_COLS = {
19
+ datasource: 30,
20
+ trust: 24,
21
+ conf: 6,
22
+ status: 8,
23
+ cache: 8
24
+ };
25
+
26
+ function trustGlyph(trustDecision) {
27
+ return statusGlyph(trustDecisionToRowStatus(trustDecision));
28
+ }
29
+
30
+ function publishGateLine(trustDecision, strict) {
31
+ if (trustDecision === 'notTrusted') return chalk.red('blocked (trustDecision notTrusted)');
32
+ if (strict) {
33
+ return trustDecision === 'trusted'
34
+ ? chalk.green('allowed (strict: trusted)')
35
+ : chalk.yellow('blocked (strict requires trusted)');
36
+ }
37
+ return chalk.green('allowed (warning-only); use --strict to require trusted');
38
+ }
39
+
40
+ function logTrustHeader(run, env) {
41
+ logger.log(SEP);
42
+ logger.log(sectionTitle(`Semantic trust — ${run.datasourceKey}`));
43
+ logger.log(headerKeyValue('Environment:', env));
44
+ logger.log(headerKeyValue('System:', run.systemKey || '—'));
45
+ logger.log(SEP);
46
+ logger.log(metaGray(' Layer checked: business metadata (404.5 agent validation)'));
47
+ logger.log(metaGray(' Not checked here: vendor connectivity, sync, record counts (use test-e2e)'));
48
+ logger.log('');
49
+ }
50
+
51
+ function logTrustSummaryBlock(run) {
52
+ const confPct = Math.round((run.confidence || 0) * 100);
53
+ logger.log(
54
+ headerKeyValue(
55
+ 'Trust:',
56
+ `${trustGlyph(run.trustDecision)} ${run.trustDecision} · confidence ${confPct}% · ${run.validationStatus}`
57
+ )
58
+ );
59
+ if (!run.validatedAt) return;
60
+ const hashShort = run.inputHash ? `${String(run.inputHash).slice(0, 8)}…` : '—';
61
+ logger.log(
62
+ metaGray(
63
+ ` Validated: ${run.validatedAt} · inputHash ${hashShort} · cache: ${run.cacheHit ? 'hit' : 'miss'}`
64
+ )
65
+ );
66
+ }
67
+
68
+ function logTrustWarningsAndFindings(run, opts) {
69
+ if (run.summary) {
70
+ logger.log('');
71
+ logger.log(chalk.white(' Summary'));
72
+ logger.log(` ${run.summary}`);
73
+ }
74
+ const warnings = Array.isArray(run.highLevelWarnings) ? run.highLevelWarnings : [];
75
+ const findings = Array.isArray(run.findings) ? run.findings : [];
76
+ const showVerboseFindings = opts.verbose === true && findings.length > 0;
77
+ if (warnings.length && !showVerboseFindings) {
78
+ logger.log('');
79
+ logger.log(chalk.yellow(' Warnings (high level)'));
80
+ warnings.forEach(w => logger.log(` • ${w}`));
81
+ }
82
+ if (!showVerboseFindings) return;
83
+ logger.log('');
84
+ logger.log(chalk.white(' Findings (verbose, max 10)'));
85
+ findings.slice(0, 10).forEach(f => {
86
+ logger.log(` • [${f.severity || 'info'}] ${f.code || 'FINDING'}: ${f.message || ''}`);
87
+ });
88
+ }
89
+
90
+ function logTrustFooter(run, opts) {
91
+ logger.log('');
92
+ logger.log(metaGray(' ── Integration reality (informative) ──'));
93
+ logger.log(metaGray(` Local manifest: ${opts.noSync ? 'not uploaded (--no-sync)' : 'uploaded before run'}`));
94
+ logger.log(metaGray(` Publish gate: ${publishGateLine(run.trustDecision, opts.strict === true)}`));
95
+ logger.log(SEP);
96
+ }
97
+
98
+ function displayAgentTrustRunTTY(run, opts = {}) {
99
+ if (!run || opts.json === true) return;
100
+ const env = opts.environment ? String(opts.environment) : 'dev';
101
+ logTrustHeader(run, env);
102
+ logTrustSummaryBlock(run);
103
+ logTrustWarningsAndFindings(run, opts);
104
+ logTrustFooter(run, opts);
105
+ }
106
+
107
+ function trustCacheLabel(trustRun) {
108
+ if (!trustRun || typeof trustRun !== 'object') return '—';
109
+ if (trustRun.readLatest === true) return 'latest';
110
+ if (trustRun.cacheHit === true) return 'hit';
111
+ if (trustRun.cacheHit === false) return 'miss';
112
+ return '—';
113
+ }
114
+
115
+ /**
116
+ * @param {boolean} showCache
117
+ * @returns {number}
118
+ */
119
+ function trustTableLineWidth(showCache) {
120
+ const { datasource, trust, conf, status, cache } = TRUST_TABLE_COLS;
121
+ let w = 2 + datasource + 1 + trust + 1 + conf + 1 + status;
122
+ if (showCache) w += 1 + cache;
123
+ return w;
124
+ }
125
+
126
+ /**
127
+ * @param {string} key
128
+ * @returns {string}
129
+ */
130
+ function formatTrustTableDatasourceCol(key) {
131
+ return String(key || '').padEnd(TRUST_TABLE_COLS.datasource).slice(0, TRUST_TABLE_COLS.datasource);
132
+ }
133
+
134
+ /**
135
+ * @param {Object|null|undefined} tr
136
+ * @returns {string}
137
+ */
138
+ function formatTrustTableTrustCol(tr) {
139
+ const w = TRUST_TABLE_COLS.trust;
140
+ if (!tr || !tr.trustDecision) return '—'.padEnd(w);
141
+ const decision = String(tr.trustDecision);
142
+ const cell = `${trustGlyph(decision)} ${decision}`;
143
+ return cell.length > w ? cell.slice(0, w) : cell.padEnd(w);
144
+ }
145
+
146
+ /**
147
+ * @param {Object|null|undefined} tr
148
+ * @returns {string}
149
+ */
150
+ function formatTrustTableConfCol(tr) {
151
+ const w = TRUST_TABLE_COLS.conf;
152
+ if (!tr || typeof tr.confidence !== 'number') return '—'.padStart(w);
153
+ return `${Math.round(tr.confidence * 100)}%`.padStart(w);
154
+ }
155
+
156
+ /**
157
+ * @param {Object|null|undefined} tr
158
+ * @param {Object} row
159
+ * @returns {string}
160
+ */
161
+ function formatTrustTableStatusCol(tr, row) {
162
+ const w = TRUST_TABLE_COLS.status;
163
+ const raw = tr ? String(tr.validationStatus || '—') : row.error || 'fail';
164
+ return raw.padEnd(w).slice(0, w);
165
+ }
166
+
167
+ /**
168
+ * @param {boolean} showCache
169
+ */
170
+ function logSystemTrustTableHeader(showCache) {
171
+ const { datasource, trust, conf, status, cache } = TRUST_TABLE_COLS;
172
+ let header = ` ${'Datasource'.padEnd(datasource)} ${'Trust'.padEnd(trust)} ${'Conf.'.padStart(conf)} ${'Status'.padEnd(status)}`;
173
+ if (showCache) header += ` ${'Cache'.padEnd(cache)}`;
174
+ logger.log(chalk.white(header));
175
+ logger.log(chalk.gray(` ${'─'.repeat(trustTableLineWidth(showCache))}`));
176
+ }
177
+
178
+ /**
179
+ * @param {Object} row
180
+ * @param {boolean} showCache
181
+ * @returns {string}
182
+ */
183
+ function formatTrustTableRow(row, showCache) {
184
+ const tr = row.trustRun;
185
+ let line = ` ${formatTrustTableDatasourceCol(row.key)} ${formatTrustTableTrustCol(tr)} ${formatTrustTableConfCol(tr)} ${formatTrustTableStatusCol(tr, row)}`;
186
+ if (showCache) {
187
+ line += ` ${trustCacheLabel(tr).padEnd(TRUST_TABLE_COLS.cache).slice(0, TRUST_TABLE_COLS.cache)}`;
188
+ }
189
+ return line;
190
+ }
191
+
192
+ function logSystemTrustTable(list, opts = {}) {
193
+ const showCache = opts.showCache === true;
194
+ logSystemTrustTableHeader(showCache);
195
+ for (const row of list) {
196
+ logger.log(formatTrustTableRow(row, showCache));
197
+ }
198
+ }
199
+
200
+ function displaySystemTrustRollupTTY(systemKey, rows, opts = {}) {
201
+ if (opts.json === true) return;
202
+ const list = Array.isArray(rows) ? rows : [];
203
+ logger.log(SEP);
204
+ logger.log(sectionTitle(`Semantic trust — system ${systemKey} (${list.length} datasources)`));
205
+ const { deriveSystemStatus } = require('./external-system-system-test-tty');
206
+ const worst = deriveSystemStatus(
207
+ list.map(r => ({
208
+ success: r.success !== false,
209
+ datasourceTestRun: {
210
+ status: r.trustRun ? trustDecisionToRowStatus(r.trustRun.trustDecision) : 'fail'
211
+ }
212
+ }))
213
+ );
214
+ logger.log(
215
+ headerKeyValue('System trust:', `${statusGlyph(worst)} ${worst} (worst-of rollup)`)
216
+ );
217
+ logger.log('');
218
+ logSystemTrustTable(list, opts);
219
+ logger.log('');
220
+ logger.log(metaGray(' ── What this run proved ──'));
221
+ logger.log(metaGray(` ✔ Semantic metadata reviewed for ${list.length} datasource(s) on dataplane`));
222
+ logger.log(metaGray(' ✖ Did not call external APIs or run CIP (not E2E)'));
223
+ logger.log(SEP);
224
+ }
225
+
226
+ module.exports = {
227
+ displayAgentTrustRunTTY,
228
+ displaySystemTrustRollupTTY,
229
+ formatTrustTableRow,
230
+ TRUST_TABLE_COLS
231
+ };
@@ -0,0 +1,59 @@
1
+ /**
2
+ * @fileoverview Exit codes for agent metadata trust CLI runs (plan 143).
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { trustDecisionToRowStatus } = require('../datasource/agent-trust-map');
10
+ const { deriveSystemStatus } = require('./external-system-system-test-tty');
11
+ const { computeExitCodeFromDatasourceTestRun } = require('./datasource-test-run-exit');
12
+
13
+ /**
14
+ * @param {Object|null|undefined} run
15
+ * @param {Object} [opts]
16
+ * @param {boolean} [opts.warningsAsErrors]
17
+ * @param {boolean} [opts.strict]
18
+ * @returns {number}
19
+ */
20
+ function computeExitCodeFromTrustRun(run, opts = {}) {
21
+ if (!run || typeof run !== 'object') return 3;
22
+ const td = run.trustDecision;
23
+ if (run.validationStatus === 'failed' || td === 'notTrusted') return 1;
24
+ if (opts.strict === true && td !== 'trusted') return 1;
25
+ if (opts.warningsAsErrors === true && td === 'usableWithWarnings') return 1;
26
+ if (td === 'trusted' || td === 'usableWithWarnings' || td === 'pending') return 0;
27
+ return 3;
28
+ }
29
+
30
+ /**
31
+ * @param {Array<{ success?: boolean, trustRun?: Object|null }>} rows
32
+ * @param {Object} [opts]
33
+ * @returns {number}
34
+ */
35
+ function computeSystemExitCodeFromTrustRows(rows, opts = {}) {
36
+ const list = Array.isArray(rows) ? rows : [];
37
+ let worst = 0;
38
+ for (const row of list) {
39
+ const code = computeExitCodeFromTrustRun(row && row.trustRun, opts);
40
+ if (code === 3) return 3;
41
+ if (code > worst) worst = code;
42
+ }
43
+ if (worst > 0) return worst;
44
+ const synthetic = list.map(r => {
45
+ const tr = r && r.trustRun;
46
+ const st = tr ? trustDecisionToRowStatus(tr.trustDecision) : 'fail';
47
+ return { success: st !== 'fail', datasourceTestRun: { status: st } };
48
+ });
49
+ const rollup = deriveSystemStatus(synthetic);
50
+ return computeExitCodeFromDatasourceTestRun(
51
+ { status: rollup },
52
+ { warningsAsErrors: opts.warningsAsErrors === true }
53
+ );
54
+ }
55
+
56
+ module.exports = {
57
+ computeExitCodeFromTrustRun,
58
+ computeSystemExitCodeFromTrustRows
59
+ };
@@ -0,0 +1,94 @@
1
+ /**
2
+ * TTY ora spinner for slow agent metadata validation POST (non-cached path).
3
+ *
4
+ * @fileoverview Agent trust wait presentation (aligned with validation-poll-ui)
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const chalk = require('chalk');
12
+ const logger = require('./logger');
13
+
14
+ function shouldUseAgentTrustWaitSpinner() {
15
+ return Boolean(process && process.stdout && process.stdout.isTTY);
16
+ }
17
+
18
+ /**
19
+ * @param {string} [datasourceKey]
20
+ * @param {number} deadlineMs
21
+ * @returns {string}
22
+ */
23
+ function buildAgentTrustWaitSpinnerText(datasourceKey, deadlineMs) {
24
+ const remainingSec = Math.max(0, Math.ceil((deadlineMs - Date.now()) / 1000));
25
+ const key =
26
+ datasourceKey && String(datasourceKey).trim() ? String(datasourceKey).trim() : 'datasource';
27
+ return `Waiting for semantic trust validation… ${key} (~${remainingSec}s left)`;
28
+ }
29
+
30
+ /**
31
+ * Run a blocking validate POST behind a TTY spinner (skipped when not a TTY).
32
+ *
33
+ * @async
34
+ * @param {Function} work - Zero-arg async function performing the HTTP call
35
+ * @param {Object} opts
36
+ * @param {string} [opts.datasourceKey]
37
+ * @param {number} opts.timeoutMs
38
+ * @returns {Promise<*>}
39
+ */
40
+ async function runWithAgentTrustWaitSpinner(work, opts = {}) {
41
+ const timeoutMs =
42
+ Number.isFinite(opts.timeoutMs) && opts.timeoutMs > 0 ? opts.timeoutMs : 120000;
43
+ const deadlineMs = Date.now() + timeoutMs;
44
+ const datasourceKey = opts.datasourceKey;
45
+
46
+ if (!shouldUseAgentTrustWaitSpinner()) {
47
+ return work();
48
+ }
49
+
50
+ const ora = require('ora');
51
+ const spinner = ora({
52
+ text: buildAgentTrustWaitSpinnerText(datasourceKey, deadlineMs),
53
+ spinner: 'dots'
54
+ }).start();
55
+
56
+ const tick = setInterval(() => {
57
+ spinner.text = buildAgentTrustWaitSpinnerText(datasourceKey, deadlineMs);
58
+ }, 1000);
59
+
60
+ try {
61
+ return await work();
62
+ } finally {
63
+ clearInterval(tick);
64
+ spinner.stop();
65
+ }
66
+ }
67
+
68
+ const NON_TTY_LOG_MS = 5000;
69
+
70
+ /**
71
+ * Optional one-line hint on non-TTY before a slow validate (throttled by caller usage).
72
+ *
73
+ * @param {string} [datasourceKey]
74
+ * @param {number} timeoutMs
75
+ */
76
+ function logAgentTrustWaitHintOnce(datasourceKey, timeoutMs) {
77
+ if (shouldUseAgentTrustWaitSpinner()) return;
78
+ const budgetSec = Math.max(1, Math.ceil(timeoutMs / 1000));
79
+ const key =
80
+ datasourceKey && String(datasourceKey).trim() ? String(datasourceKey).trim() : 'datasource';
81
+ logger.log(
82
+ chalk.gray(
83
+ `Waiting for semantic trust validation… ${key} (may take up to ~${budgetSec}s; not cached)`
84
+ )
85
+ );
86
+ }
87
+
88
+ module.exports = {
89
+ shouldUseAgentTrustWaitSpinner,
90
+ buildAgentTrustWaitSpinnerText,
91
+ runWithAgentTrustWaitSpinner,
92
+ logAgentTrustWaitHintOnce,
93
+ NON_TTY_LOG_MS
94
+ };
@@ -12,7 +12,28 @@
12
12
  'use strict';
13
13
 
14
14
  const path = require('path');
15
- const fs = require('fs');
15
+ const defaultFs = require('fs');
16
+ const { nodeFs } = require('../internal/node-fs');
17
+
18
+ const realFs = nodeFs();
19
+
20
+ /**
21
+ * Prefer real disk when `fs` is not mocked (avoids `jest.mock('fs')` bleed in other suites).
22
+ * Use `require('fs')` when Jest replaced it so tests with virtual file stores still work.
23
+ *
24
+ * @returns {Pick<import('fs'), 'existsSync'|'renameSync'>}
25
+ */
26
+ function getResolverFs() {
27
+ if (
28
+ typeof jest !== 'undefined' &&
29
+ defaultFs.existsSync &&
30
+ typeof jest.isMockFunction === 'function' &&
31
+ jest.isMockFunction(defaultFs.existsSync)
32
+ ) {
33
+ return defaultFs;
34
+ }
35
+ return realFs;
36
+ }
16
37
 
17
38
  /**
18
39
  * Resolves path to application config file (application.yaml, application.json, or legacy variables.yaml).
@@ -23,6 +44,7 @@ const fs = require('fs');
23
44
  * @throws {Error} If no config file found
24
45
  */
25
46
  function resolveApplicationConfigPath(appPath) {
47
+ const fs = getResolverFs();
26
48
  if (!appPath || typeof appPath !== 'string') {
27
49
  throw new Error('App path is required and must be a string');
28
50
  }
@@ -59,6 +81,7 @@ const RBAC_NAMES = ['rbac.yaml', 'rbac.yml', 'rbac.json'];
59
81
  * @returns {string|null} Absolute path to RBAC file, or null if none exist
60
82
  */
61
83
  function resolveRbacPath(appPath) {
84
+ const fs = getResolverFs();
62
85
  if (!appPath || typeof appPath !== 'string') {
63
86
  throw new Error('App path is required and must be a string');
64
87
  }
@@ -0,0 +1,206 @@
1
+ /**
2
+ * User `applications` entries in config.yaml (read by `aifabrix resolve`, written by `aifabrix run` dev).
3
+ *
4
+ * @fileoverview Per-app reload and `proxy` (Traefik/public URL hints) in ~/.aifabrix/config.yaml
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const config = require('../core/config');
12
+
13
+ /**
14
+ * Persist `applications.<appKey>.reload` from the last `aifabrix run` (dev only; CLI is source of truth).
15
+ *
16
+ * @async
17
+ * @param {string} appKey - Application key
18
+ * @param {boolean} reload - Same as passing `--reload` on run
19
+ * @returns {Promise<void>}
20
+ */
21
+ async function persistApplicationReloadFlag(appKey, reload) {
22
+ if (!appKey || typeof appKey !== 'string') {
23
+ return;
24
+ }
25
+ const cfg = await config.getConfig();
26
+ if (!cfg.applications || typeof cfg.applications !== 'object') {
27
+ cfg.applications = {};
28
+ }
29
+ const prev = cfg.applications[appKey];
30
+ const nextEntry =
31
+ prev && typeof prev === 'object' && !Array.isArray(prev) ? { ...prev, reload: Boolean(reload) } : { reload: Boolean(reload) };
32
+ cfg.applications[appKey] = nextEntry;
33
+ await config.saveConfig(cfg);
34
+ }
35
+
36
+ /**
37
+ * True when `applications.<appKey>.reload` is explicitly **true** (bind-mount + hot reload for `aifabrix run`).
38
+ * Does **not** control other compose overrides (e.g. Python image `command` when the image lacks `uvicorn` on PATH).
39
+ *
40
+ * @param {Object|null|undefined} userConfig - Parsed config.yaml root
41
+ * @param {string} appKey - Application key (folder / app.key)
42
+ * @returns {boolean} True when `applications.<appKey>.reload` is explicitly true
43
+ */
44
+ function isApplicationsReloadDefaultOn(userConfig, appKey) {
45
+ if (!userConfig || !appKey || typeof appKey !== 'string') {
46
+ return false;
47
+ }
48
+ const apps = userConfig.applications;
49
+ if (!apps || typeof apps !== 'object') {
50
+ return false;
51
+ }
52
+ const entry = apps[appKey];
53
+ if (!entry || typeof entry !== 'object') {
54
+ return false;
55
+ }
56
+ return entry.reload === true;
57
+ }
58
+
59
+ /**
60
+ * True when `remote-server` is a non-empty string (after trim).
61
+ *
62
+ * @param {string|null|undefined} remoteServer
63
+ * @returns {boolean}
64
+ */
65
+ function isRemoteServerConfigured(remoteServer) {
66
+ return Boolean(remoteServer && String(remoteServer).trim());
67
+ }
68
+
69
+ /**
70
+ * Whether `build.envOutputPath` should be regenerated with the **local** profile (`generateEnvContent(..., 'local')`).
71
+ * **False** only when **both** `remote-server` is set and `applications.<appKey>.reload` is true — then the host file
72
+ * uses the same **docker**-flavor expansion as `builder/<app>/.env` (copy path). All other cases use **local**.
73
+ *
74
+ * @param {Object|null|undefined} userConfig - Parsed config.yaml root
75
+ * @param {string} appKey - Application key
76
+ * @param {string|null|undefined} remoteServer - `remote-server` value (or null)
77
+ * @returns {boolean}
78
+ */
79
+ function isPreferLocalEnvOutputPath(userConfig, appKey, remoteServer) {
80
+ if (!isRemoteServerConfigured(remoteServer)) {
81
+ return true;
82
+ }
83
+ return !isApplicationsReloadDefaultOn(userConfig, appKey);
84
+ }
85
+
86
+ /**
87
+ * For `aifabrix resolve`: reads `remote-server` and returns {@link isPreferLocalEnvOutputPath}.
88
+ *
89
+ * @async
90
+ * @param {Object|null|undefined} userConfig
91
+ * @param {string} appKey
92
+ * @returns {Promise<boolean>}
93
+ */
94
+ async function resolvePreferLocalEnvOutputPathFlag(userConfig, appKey) {
95
+ let remoteServer = null;
96
+ try {
97
+ const rs = await config.getRemoteServer();
98
+ if (rs && String(rs).trim()) {
99
+ remoteServer = String(rs).trim();
100
+ }
101
+ } catch {
102
+ remoteServer = null;
103
+ }
104
+ return isPreferLocalEnvOutputPath(userConfig, appKey, remoteServer);
105
+ }
106
+
107
+ /**
108
+ * Normalize YAML boolean-ish values.
109
+ * @param {unknown} v
110
+ * @returns {boolean|undefined} undefined if absent / not coercible
111
+ */
112
+ function coerceTriStateBool(v) {
113
+ if (v === true || v === 'true' || v === 'yes' || v === 'on' || v === 1 || v === '1') {
114
+ return true;
115
+ }
116
+ if (v === false || v === 'false' || v === 'no' || v === 'off' || v === 0 || v === '0') {
117
+ return false;
118
+ }
119
+ return undefined;
120
+ }
121
+
122
+ /**
123
+ * Whether `aifabrix run` / resolve should use Traefik-style public URL hints for this app when infra allows.
124
+ * Default **false** when unset. Legacy `noProxy: true` ⇒ false; legacy `noProxy: false` with no `proxy` ⇒ true.
125
+ *
126
+ * @param {Object|null|undefined} userConfig - Parsed config.yaml root
127
+ * @param {string} appKey - Application key
128
+ * @returns {boolean}
129
+ */
130
+ function getApplicationsRunProxyHint(userConfig, appKey) {
131
+ if (!userConfig || !appKey || typeof appKey !== 'string') {
132
+ return false;
133
+ }
134
+ const apps = userConfig.applications;
135
+ if (!apps || typeof apps !== 'object') {
136
+ return false;
137
+ }
138
+ const entry = apps[appKey];
139
+ if (!entry || typeof entry !== 'object') {
140
+ return false;
141
+ }
142
+ const proxyCoerced = coerceTriStateBool(entry.proxy);
143
+ if (proxyCoerced !== undefined) {
144
+ return proxyCoerced;
145
+ }
146
+ const legacyNo = coerceTriStateBool(entry.noProxy);
147
+ if (legacyNo === true) {
148
+ return false;
149
+ }
150
+ if (legacyNo === false) {
151
+ return true;
152
+ }
153
+ return false;
154
+ }
155
+
156
+ /**
157
+ * Persist `applications.<appKey>.proxy` from each `aifabrix run` (same pattern as `reload` for dev-only reload flag).
158
+ * `proxy: true` means use Traefik/front-door URL hints when infra + that app's `application.yaml` allow; `false` means docker `url://` **public** bases for **that app** use localhost + published port. During `resolve`/`run`, each `url://` token uses the **target** app's `applications.<targetKey>.proxy`, not only the app whose `env.template` is being expanded.
159
+ * Removes legacy `noProxy` on the same entry when present.
160
+ *
161
+ * @async
162
+ * @param {string} appKey - Application key
163
+ * @param {boolean} proxyEnabled - Same as a normal `aifabrix run` with proxy hints on (`!isRunCliNoProxy(options)`)
164
+ * @returns {Promise<void>}
165
+ */
166
+ async function persistApplicationRunProxyFlag(appKey, proxyEnabled) {
167
+ if (!appKey || typeof appKey !== 'string') {
168
+ return;
169
+ }
170
+ const cfg = await config.getConfig();
171
+ if (!cfg.applications || typeof cfg.applications !== 'object') {
172
+ cfg.applications = {};
173
+ }
174
+ const prev = cfg.applications[appKey];
175
+ const nextEntry =
176
+ prev && typeof prev === 'object' && !Array.isArray(prev)
177
+ ? { ...prev, proxy: Boolean(proxyEnabled) }
178
+ : { proxy: Boolean(proxyEnabled) };
179
+ if (Object.prototype.hasOwnProperty.call(nextEntry, 'noProxy')) {
180
+ delete nextEntry.noProxy;
181
+ }
182
+ cfg.applications[appKey] = nextEntry;
183
+ await config.saveConfig(cfg);
184
+ }
185
+
186
+ /**
187
+ * Whether declarative `url://*` expansion should treat Traefik as on (infra Traefik + per-app `proxy: true`).
188
+ *
189
+ * @param {Object|null|undefined} userConfig
190
+ * @param {string} appKey
191
+ * @returns {boolean}
192
+ */
193
+ function isDeclarativeTraefikUrlsEnabled(userConfig, appKey) {
194
+ return Boolean(userConfig && userConfig.traefik === true) && getApplicationsRunProxyHint(userConfig, appKey);
195
+ }
196
+
197
+ module.exports = {
198
+ getApplicationsRunProxyHint,
199
+ isApplicationsReloadDefaultOn,
200
+ isDeclarativeTraefikUrlsEnabled,
201
+ isPreferLocalEnvOutputPath,
202
+ isRemoteServerConfigured,
203
+ persistApplicationRunProxyFlag,
204
+ persistApplicationReloadFlag,
205
+ resolvePreferLocalEnvOutputPathFlag
206
+ };
@@ -8,7 +8,7 @@
8
8
  * @version 2.0.0
9
9
  */
10
10
 
11
- const { getControllerUrlFromLoggedInUser } = require('./controller-url');
11
+ const { hasStoredDeviceTokenForController } = require('./controller-url');
12
12
 
13
13
  /**
14
14
  * Validate controller URL format
@@ -72,17 +72,7 @@ async function checkUserLoggedIn(controllerUrl) {
72
72
  if (!controllerUrl) {
73
73
  return false;
74
74
  }
75
-
76
- const normalizedUrl = controllerUrl.trim().replace(/\/+$/, '');
77
- const loggedInControllerUrl = await getControllerUrlFromLoggedInUser();
78
-
79
- if (!loggedInControllerUrl) {
80
- return false;
81
- }
82
-
83
- // Normalize both URLs for comparison
84
- const normalizedLoggedIn = loggedInControllerUrl.trim().replace(/\/+$/, '');
85
- return normalizedLoggedIn === normalizedUrl;
75
+ return hasStoredDeviceTokenForController(controllerUrl);
86
76
  }
87
77
 
88
78
  module.exports = {
@@ -41,7 +41,7 @@ function collectBashPrefixedEnv(secrets) {
41
41
  }
42
42
 
43
43
  /**
44
- * Overlay for `child_process` `env`: values from user + shared + ancestor `secrets.local.yaml`
44
+ * Overlay for `child_process` `env`: values from primary user `secrets.local.yaml` plus `aifabrix-secrets`
45
45
  * for every `BASH_*` key (merged via {@link secretsLoad.loadSecrets}).
46
46
  *
47
47
  * @param {string|null} [secretsPath] - Optional explicit secrets file (same as resolve)