@aifabrix/builder 2.45.0 → 2.45.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/docs-rules.mdc +3 -1
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
- package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
- package/jest.projects.js +44 -1
- package/lib/api/agent-metadata-validation.api.js +93 -0
- package/lib/api/audit-api-helpers.js +68 -0
- package/lib/api/audit.api.js +119 -0
- package/lib/api/auth-cache.api.js +44 -0
- package/lib/api/certificates.api.js +4 -4
- package/lib/api/credential.api.js +2 -2
- package/lib/api/dataplane-health.api.js +137 -0
- package/lib/api/dataplane-sync.api.js +27 -0
- package/lib/api/datasources-core.api.js +21 -18
- package/lib/api/datasources-extended.api.js +22 -22
- package/lib/api/dimensions.api.js +3 -0
- package/lib/api/external-systems.api.js +13 -13
- package/lib/api/governance-scenarios.api.js +39 -0
- package/lib/api/groups.api.js +79 -0
- package/lib/api/index.js +45 -2
- package/lib/api/pipeline.api.js +4 -4
- package/lib/api/protection.api.js +157 -0
- package/lib/api/records-bulk.api.js +30 -0
- package/lib/api/records-search-parse.js +49 -0
- package/lib/api/records-search.api.js +29 -0
- package/lib/api/types/agent-metadata-validation.types.js +67 -0
- package/lib/api/types/audit.types.js +72 -0
- package/lib/api/types/dataplane-health.types.js +27 -0
- package/lib/api/types/dimensions.types.js +2 -0
- package/lib/api/types/governance-scenarios.types.js +39 -0
- package/lib/api/types/protection.types.js +69 -0
- package/lib/api/types/records-bulk.types.js +35 -0
- package/lib/api/types/records-search.types.js +28 -0
- package/lib/api/user-groups.api.js +50 -0
- package/lib/api/users.api.js +95 -0
- package/lib/api/validation-run.api.js +3 -3
- package/lib/api/validation-runner.js +3 -3
- package/lib/api/wizard-platform.api.js +3 -3
- package/lib/api/wizard.api.js +21 -21
- package/lib/app/deploy.js +34 -19
- package/lib/cli/index.js +4 -2
- package/lib/cli/infra-guided.js +22 -3
- package/lib/cli/setup-app.help.js +50 -2
- package/lib/cli/setup-app.js +33 -24
- package/lib/cli/setup-app.test-commands.js +80 -21
- package/lib/cli/setup-auth.js +30 -3
- package/lib/cli/setup-external-system.js +37 -3
- package/lib/cli/setup-utility-help-after.js +45 -0
- package/lib/cli/setup-utility-resolve.js +2 -2
- package/lib/cli/setup-utility.js +99 -81
- package/lib/commands/auth-status-dataplane-version.js +231 -0
- package/lib/commands/auth-status-display.js +94 -0
- package/lib/commands/auth-status.js +70 -6
- package/lib/commands/credential-push.js +10 -5
- package/lib/commands/datasource-load-export-cli.js +178 -0
- package/lib/commands/datasource-test-trust-cli.js +114 -0
- package/lib/commands/datasource-unified-test-cli.js +25 -17
- package/lib/commands/datasource-unified-test-cli.options.js +18 -3
- package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
- package/lib/commands/datasource-verify-audit-cli.js +96 -0
- package/lib/commands/datasource.js +68 -1
- package/lib/commands/dimension-value.js +43 -13
- package/lib/commands/dimension.js +38 -7
- package/lib/commands/identity-apply-sync.js +131 -0
- package/lib/commands/identity-group-cli.js +146 -0
- package/lib/commands/identity-membership-role-cache.js +175 -0
- package/lib/commands/identity-shared.js +94 -0
- package/lib/commands/identity-user-cli.js +158 -0
- package/lib/commands/identity.js +43 -0
- package/lib/commands/login.js +46 -16
- package/lib/commands/protection-cli-leaves.js +286 -0
- package/lib/commands/protection.js +50 -0
- package/lib/commands/repair.js +13 -3
- package/lib/commands/test-e2e-external.js +40 -9
- package/lib/commands/test-governance-cli-options.js +47 -0
- package/lib/commands/test-governance-command-action.js +55 -0
- package/lib/commands/test-governance-external.js +88 -0
- package/lib/commands/test-trust-cli-options.js +32 -0
- package/lib/commands/test-trust-command-action.js +118 -0
- package/lib/commands/test-trust-external.js +127 -0
- package/lib/commands/upload.js +34 -13
- package/lib/commands/wizard-core.js +3 -0
- package/lib/core/config-device-dataplane.js +185 -0
- package/lib/datasource/agent-trust-debug-log.js +45 -0
- package/lib/datasource/agent-trust-fetch.js +115 -0
- package/lib/datasource/agent-trust-map.js +101 -0
- package/lib/datasource/agent-trust-mock.js +44 -0
- package/lib/datasource/agent-trust-run.js +93 -0
- package/lib/datasource/agent-trust-timeout.js +25 -0
- package/lib/datasource/audit-evidence-display.js +80 -0
- package/lib/datasource/audit-evidence-extract-debug.js +103 -0
- package/lib/datasource/audit-evidence-extract.js +177 -0
- package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
- package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
- package/lib/datasource/audit-evidence-poll.js +109 -0
- package/lib/datasource/audit-evidence-run.js +177 -0
- package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
- package/lib/datasource/audit-evidence-verify-matrix.js +177 -0
- package/lib/datasource/audit-evidence-verify.js +108 -0
- package/lib/datasource/bulk-loader-run-batches.js +88 -0
- package/lib/datasource/bulk-loader-service-retry.js +52 -0
- package/lib/datasource/bulk-loader-service.js +127 -0
- package/lib/datasource/capability/run-capability-dimension.js +2 -1
- package/lib/datasource/datasource-exporter-resolve.js +82 -0
- package/lib/datasource/datasource-exporter-service.js +144 -0
- package/lib/datasource/datasource-identifier.js +148 -0
- package/lib/datasource/datasource-load-export-context.js +92 -0
- package/lib/datasource/integration-context.js +6 -4
- package/lib/datasource/local-data-paths.js +145 -0
- package/lib/datasource/log-cleaner.js +241 -0
- package/lib/datasource/log-viewer.js +111 -6
- package/lib/datasource/record-file-parser.js +85 -0
- package/lib/datasource/record-mapper.js +129 -0
- package/lib/datasource/resolve-app.js +74 -55
- package/lib/datasource/test-e2e.js +26 -2
- package/lib/datasource/unified-validation-run-resolve.js +45 -12
- package/lib/datasource/unified-validation-run.js +43 -17
- package/lib/datasource/validate.js +37 -20
- package/lib/external-system/test-system-level.js +25 -4
- package/lib/external-system/test.js +3 -1
- package/lib/governance/governance-pack-loader.js +70 -0
- package/lib/governance/governance-run-display.js +119 -0
- package/lib/identity/identity-apply-core.js +108 -0
- package/lib/identity/identity-apply-phases.js +244 -0
- package/lib/identity/identity-apply-service.js +77 -0
- package/lib/identity/identity-csv-parser.js +114 -0
- package/lib/internal/fs-real-sync.js +29 -1
- package/lib/protection/auth-context.js +45 -0
- package/lib/protection/convert-batch.js +90 -0
- package/lib/protection/load.js +41 -0
- package/lib/protection/paths.js +158 -0
- package/lib/protection/preflight-datasource-ready.js +46 -0
- package/lib/protection/protection-create-probe-lines.js +142 -0
- package/lib/protection/protection-create-probes.js +95 -0
- package/lib/protection/protection-create-scaffold.js +281 -0
- package/lib/protection/protection-display-helpers.js +445 -0
- package/lib/protection/protection-display.js +215 -0
- package/lib/protection/protection-preset-registry.js +97 -0
- package/lib/protection/report-exit.js +62 -0
- package/lib/protection/resolve.js +202 -0
- package/lib/protection/run-commands-validate.js +130 -0
- package/lib/protection/run-commands.js +216 -0
- package/lib/protection/run-protection-create-helpers.js +134 -0
- package/lib/protection/run-protection-create-probes-flow.js +43 -0
- package/lib/protection/run-protection-create.js +191 -0
- package/lib/protection/scope.js +22 -0
- package/lib/protection/sync-after-upload.js +64 -0
- package/lib/protection/upload-batch.js +127 -0
- package/lib/protection/validate-batch.js +113 -0
- package/lib/protection/validate-local.js +65 -0
- package/lib/resolvers/dimension-file.js +29 -4
- package/lib/schema/external-datasource.schema.json +47 -13
- package/lib/schema/flag-map-validation-run.json +2 -2
- package/lib/schema/protection.schema.json +374 -0
- package/lib/schema/type/credential-field-registry.json +636 -0
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/agent-trust-run-display.js +231 -0
- package/lib/utils/agent-trust-run-exit.js +59 -0
- package/lib/utils/agent-trust-wait-ui.js +94 -0
- package/lib/utils/cli-exit-codes.js +30 -0
- package/lib/utils/cli-sync-options.js +33 -0
- package/lib/utils/credential-push-ui.js +90 -0
- package/lib/utils/dataplane-cli-version-gate.js +202 -0
- package/lib/utils/dataplane-cli-version-help.js +74 -0
- package/lib/utils/datasource-export-display.js +137 -0
- package/lib/utils/datasource-load-display.js +174 -0
- package/lib/utils/datasource-load-export-ui.js +120 -0
- package/lib/utils/datasource-test-run-capability-scope.js +3 -3
- package/lib/utils/datasource-test-run-display.js +3 -3
- package/lib/utils/datasource-test-run-tty-log.js +5 -1
- package/lib/utils/help-builder.js +30 -24
- package/lib/utils/manifest-source-emit.js +22 -0
- package/lib/utils/semver-compare.js +127 -0
- package/lib/utils/upload-pipeline-ui.js +91 -0
- package/lib/utils/validation-poll-ui.js +14 -7
- package/lib/validation/wizard-config-validator.js +58 -19
- package/package.json +1 -1
- package/templates/applications/dataplane/env.template +13 -1
- package/templates/applications/dataplane/rbac.yaml +4 -0
- package/templates/external-system/README.md.hbs +56 -8
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Helpers for protection TTY formatters (keeps display module under limits).
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const chalk = require('chalk');
|
|
10
|
+
const {
|
|
11
|
+
sectionTitle,
|
|
12
|
+
headerKeyValue,
|
|
13
|
+
metadata,
|
|
14
|
+
formatStatusKeyValue,
|
|
15
|
+
formatBlockingError,
|
|
16
|
+
formatSuccessLine,
|
|
17
|
+
formatWarningLine
|
|
18
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
19
|
+
|
|
20
|
+
const SEP = chalk.gray('────────────────────────────────────────');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {Object} row
|
|
24
|
+
* @returns {string}
|
|
25
|
+
*/
|
|
26
|
+
function taskStableCode(row) {
|
|
27
|
+
return String(row?.errorCode || row?.stableCode || row?.taskId || 'ISSUE');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {string[]} lines
|
|
32
|
+
* @param {Object[]} rows
|
|
33
|
+
* @param {'FAIL'|'WARN'|'PASS'} status
|
|
34
|
+
* @param {Object} opts
|
|
35
|
+
*/
|
|
36
|
+
function appendTaskRows(lines, rows, status, opts = {}) {
|
|
37
|
+
const cap = opts.verbose ? rows.length : Math.min(rows.length, opts.maxIssues || 10);
|
|
38
|
+
const filtered = rows.filter((r) => String(r?.status || '').toUpperCase() === status);
|
|
39
|
+
if (!filtered.length) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const title =
|
|
43
|
+
status === 'FAIL'
|
|
44
|
+
? 'Validation issues:'
|
|
45
|
+
: status === 'WARN'
|
|
46
|
+
? 'Warnings:'
|
|
47
|
+
: 'Checks passed:';
|
|
48
|
+
lines.push(sectionTitle(title));
|
|
49
|
+
for (let i = 0; i < Math.min(cap, filtered.length); i += 1) {
|
|
50
|
+
const row = filtered[i];
|
|
51
|
+
const code = taskStableCode(row);
|
|
52
|
+
const msg = row?.message ? String(row.message) : '';
|
|
53
|
+
const prefix = status === 'FAIL' ? '✖' : status === 'WARN' ? '⚠' : '✔';
|
|
54
|
+
const lineFn = status === 'WARN' ? formatWarningLine : (t) => t;
|
|
55
|
+
lines.push(lineFn(` ${prefix} ${code} ${msg}`.trim()));
|
|
56
|
+
if (row?.hint) {
|
|
57
|
+
lines.push(chalk.gray(` hint: ${row.hint}`));
|
|
58
|
+
}
|
|
59
|
+
if (row?.schemaPath) {
|
|
60
|
+
lines.push(chalk.gray(` path: ${row.schemaPath}`));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {string[]} lines
|
|
67
|
+
* @param {Object} meta
|
|
68
|
+
*/
|
|
69
|
+
function appendProtectionReportHeader(lines, meta) {
|
|
70
|
+
if (meta.protectionKey) {
|
|
71
|
+
lines.push(headerKeyValue('Protection:', meta.protectionKey));
|
|
72
|
+
}
|
|
73
|
+
if (meta.datasourceKey) {
|
|
74
|
+
lines.push(headerKeyValue('Datasource:', meta.datasourceKey));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param {string[]} lines
|
|
80
|
+
* @param {Object} sim
|
|
81
|
+
*/
|
|
82
|
+
function appendSimulationSection(lines, sim) {
|
|
83
|
+
lines.push(sectionTitle('Simulation:'));
|
|
84
|
+
if (sim.recordsSampled !== undefined && sim.recordsSampled !== null) {
|
|
85
|
+
lines.push(` Records sampled: ${sim.recordsSampled}`);
|
|
86
|
+
}
|
|
87
|
+
if (sim.grantsProjected !== undefined && sim.grantsProjected !== null) {
|
|
88
|
+
lines.push(` Grants projected: ${sim.grantsProjected}`);
|
|
89
|
+
}
|
|
90
|
+
if (sim.unresolvedPrincipals !== undefined && sim.unresolvedPrincipals !== null) {
|
|
91
|
+
lines.push(` Unresolved principals: ${sim.unresolvedPrincipals}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @param {string[]} lines
|
|
97
|
+
* @param {number} failCount
|
|
98
|
+
* @param {number} warnCount
|
|
99
|
+
* @param {Object} opts
|
|
100
|
+
*/
|
|
101
|
+
function appendValidationSummarySection(lines, failCount, warnCount, opts) {
|
|
102
|
+
lines.push(sectionTitle('Summary:'));
|
|
103
|
+
if (failCount > 0 || (opts.warningsAsErrors && warnCount > 0)) {
|
|
104
|
+
lines.push(formatBlockingError('Validation failed'));
|
|
105
|
+
lines.push(headerKeyValue('Overall:', 'Failed'));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
lines.push(formatSuccessLine('Validation passed'));
|
|
109
|
+
lines.push(headerKeyValue('Overall:', warnCount > 0 ? 'Warnings' : 'OK'));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @param {string[]} lines
|
|
114
|
+
* @param {Object[]} results
|
|
115
|
+
* @param {Object} opts
|
|
116
|
+
* @param {string} okLabel
|
|
117
|
+
*/
|
|
118
|
+
function appendBatchResultRows(lines, results, okLabel) {
|
|
119
|
+
for (const row of results) {
|
|
120
|
+
if (row.ok) {
|
|
121
|
+
lines.push(`${row.datasourceKey}`);
|
|
122
|
+
lines.push(formatSuccessLine(` ${okLabel}`));
|
|
123
|
+
} else {
|
|
124
|
+
lines.push(`${row.datasourceKey}`);
|
|
125
|
+
lines.push(formatBlockingError(` ${row.error || 'Failed'}`));
|
|
126
|
+
}
|
|
127
|
+
lines.push('');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @param {string[]} lines
|
|
133
|
+
* @param {number} passed
|
|
134
|
+
* @param {number} failed
|
|
135
|
+
* @param {string} okMessage
|
|
136
|
+
*/
|
|
137
|
+
function appendBatchSummary(lines, passed, failed, okMessage) {
|
|
138
|
+
lines.push(sectionTitle('Summary:'));
|
|
139
|
+
if (failed > 0) {
|
|
140
|
+
lines.push(formatBlockingError(`${passed} passed, ${failed} failed`));
|
|
141
|
+
lines.push(headerKeyValue('Overall:', 'Failed'));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
lines.push(formatSuccessLine(okMessage));
|
|
145
|
+
lines.push(headerKeyValue('Overall:', 'OK'));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @param {Object} manifest
|
|
150
|
+
* @param {Object} status
|
|
151
|
+
* @returns {string}
|
|
152
|
+
*/
|
|
153
|
+
function protectionShowDatasourceKey(manifest, status) {
|
|
154
|
+
return manifest?.datasourceKey || status?.datasourceKey || '—';
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Stacked label + value (wraps long protection keys and hashes cleanly).
|
|
159
|
+
* @param {string[]} lines
|
|
160
|
+
* @param {string} label
|
|
161
|
+
* @param {string|number|null|undefined} value
|
|
162
|
+
*/
|
|
163
|
+
function appendProtectionShowField(lines, label, value) {
|
|
164
|
+
const text =
|
|
165
|
+
value === null || value === undefined || String(value).trim() === '' ? '—' : String(value);
|
|
166
|
+
lines.push(chalk.gray(label));
|
|
167
|
+
lines.push(` ${chalk.bold.white(text)}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @param {string[]} lines
|
|
172
|
+
* @param {string} title
|
|
173
|
+
*/
|
|
174
|
+
function appendProtectionShowSection(lines, title) {
|
|
175
|
+
lines.push('');
|
|
176
|
+
lines.push(sectionTitle(title));
|
|
177
|
+
lines.push(SEP);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* @param {string|undefined|null} iso
|
|
182
|
+
* @returns {string}
|
|
183
|
+
*/
|
|
184
|
+
function formatProtectionShowTimestamp(iso) {
|
|
185
|
+
if (!iso || String(iso).trim() === '' || iso === '—') {
|
|
186
|
+
return '—';
|
|
187
|
+
}
|
|
188
|
+
const d = new Date(iso);
|
|
189
|
+
if (Number.isNaN(d.getTime())) {
|
|
190
|
+
return String(iso);
|
|
191
|
+
}
|
|
192
|
+
return d.toISOString().replace('T', ' ').replace(/\.\d{3}Z$/, ' UTC');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* @param {string|undefined|null} hash
|
|
197
|
+
* @param {boolean} [verbose]
|
|
198
|
+
* @returns {string}
|
|
199
|
+
*/
|
|
200
|
+
function formatProtectionShowHash(hash, verbose) {
|
|
201
|
+
if (!hash || String(hash).trim() === '') {
|
|
202
|
+
return '—';
|
|
203
|
+
}
|
|
204
|
+
const s = String(hash);
|
|
205
|
+
if (verbose || s.length <= 24) {
|
|
206
|
+
return s;
|
|
207
|
+
}
|
|
208
|
+
return `${s.slice(0, 8)}…${s.slice(-6)}`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* @param {string[]} lines
|
|
213
|
+
* @param {Object} status
|
|
214
|
+
* @param {Object} manifest
|
|
215
|
+
*/
|
|
216
|
+
function appendProtectionShowProjectionSection(lines, status, manifest) {
|
|
217
|
+
const grants = Array.isArray(status?.grants) ? status.grants : [];
|
|
218
|
+
const staticCount = grants.filter((g) => g.effectiveValueType === 'static').length;
|
|
219
|
+
const dynamicCount = grants.filter((g) => g.effectiveValueType === 'dynamic').length;
|
|
220
|
+
const ruleCount = Array.isArray(manifest?.spec?.rules) ? manifest.spec.rules.length : 0;
|
|
221
|
+
const grantTotal = status?.grantCount ?? grants.length;
|
|
222
|
+
const projectionAt =
|
|
223
|
+
status?.lastProjectionRunAt || status?.lastSuccessfulProjectionRunAt || '—';
|
|
224
|
+
|
|
225
|
+
appendProtectionShowSection(lines, 'Projection cache');
|
|
226
|
+
appendProtectionShowField(
|
|
227
|
+
lines,
|
|
228
|
+
'Last run',
|
|
229
|
+
formatProtectionShowTimestamp(projectionAt === '—' ? null : projectionAt)
|
|
230
|
+
);
|
|
231
|
+
appendProtectionShowField(lines, 'Rules in manifest', String(ruleCount));
|
|
232
|
+
appendProtectionShowField(
|
|
233
|
+
lines,
|
|
234
|
+
'Grants materialized',
|
|
235
|
+
`${grantTotal} (${staticCount} static, ${dynamicCount} dynamic)`
|
|
236
|
+
);
|
|
237
|
+
appendProtectionShowField(
|
|
238
|
+
lines,
|
|
239
|
+
'Dynamic values materialized',
|
|
240
|
+
String(status?.dynamicValueCount ?? '—')
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* @param {string[]} lines
|
|
246
|
+
* @param {Object} payload
|
|
247
|
+
* @param {Object} [opts]
|
|
248
|
+
*/
|
|
249
|
+
function appendProtectionShowHeader(lines, payload, opts = {}) {
|
|
250
|
+
const { manifest, status } = payload;
|
|
251
|
+
const datasourceKey = protectionShowDatasourceKey(manifest, status);
|
|
252
|
+
const enabled = manifest?.enabled !== false;
|
|
253
|
+
const protectionKey = manifest?.key || status?.protectionKey || '—';
|
|
254
|
+
const revision = String(manifest?.currentRevision ?? status?.currentRevision ?? '—');
|
|
255
|
+
const configHash = manifest?.configHash || status?.configHash || null;
|
|
256
|
+
const lastUpload = manifest?.lastDeployedAt || status?.lastDeployedAt || null;
|
|
257
|
+
|
|
258
|
+
lines.push(chalk.bold.white(datasourceKey));
|
|
259
|
+
lines.push(SEP);
|
|
260
|
+
appendProtectionShowField(lines, 'Status', enabled ? 'Enabled' : 'Disabled');
|
|
261
|
+
|
|
262
|
+
appendProtectionShowSection(lines, 'Manifest');
|
|
263
|
+
appendProtectionShowField(lines, 'Protection key', protectionKey);
|
|
264
|
+
appendProtectionShowField(lines, 'Datasource', datasourceKey);
|
|
265
|
+
appendProtectionShowField(lines, 'Revision', revision);
|
|
266
|
+
|
|
267
|
+
appendProtectionShowSection(lines, 'Deployment');
|
|
268
|
+
appendProtectionShowField(lines, 'Last upload', formatProtectionShowTimestamp(lastUpload));
|
|
269
|
+
appendProtectionShowField(
|
|
270
|
+
lines,
|
|
271
|
+
'Content hash',
|
|
272
|
+
formatProtectionShowHash(configHash, opts.verbose === true)
|
|
273
|
+
);
|
|
274
|
+
if (opts.verbose && configHash) {
|
|
275
|
+
appendProtectionShowField(lines, 'Content hash (full)', configHash);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
appendProtectionShowProjectionSection(lines, status, manifest);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* @param {string[]} lines
|
|
283
|
+
* @param {Object} payload
|
|
284
|
+
*/
|
|
285
|
+
function appendProtectionShowContextFooter(lines, payload) {
|
|
286
|
+
const environment = payload.environment || '—';
|
|
287
|
+
const dataplaneUrl = payload.dataplaneUrl || '';
|
|
288
|
+
lines.push('');
|
|
289
|
+
if (dataplaneUrl) {
|
|
290
|
+
lines.push(metadata(`Environment: ${environment} · Dataplane: ${dataplaneUrl}`));
|
|
291
|
+
} else {
|
|
292
|
+
lines.push(metadata(`Environment: ${environment}`));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* @param {string[]} lines
|
|
298
|
+
* @param {Object} status
|
|
299
|
+
* @param {Object} manifest
|
|
300
|
+
* @deprecated Use appendProtectionShowProjectionSection via appendProtectionShowHeader
|
|
301
|
+
*/
|
|
302
|
+
function appendProtectionShowCounts(lines, status, manifest) {
|
|
303
|
+
appendProtectionShowProjectionSection(lines, status, manifest);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* @param {string[]} lines
|
|
308
|
+
* @param {Object[]} grants
|
|
309
|
+
*/
|
|
310
|
+
/**
|
|
311
|
+
* @param {boolean|undefined} enabled
|
|
312
|
+
* @returns {string}
|
|
313
|
+
*/
|
|
314
|
+
function formatProtectionEnabledLabel(enabled) {
|
|
315
|
+
if (enabled === true) {
|
|
316
|
+
return 'yes';
|
|
317
|
+
}
|
|
318
|
+
if (enabled === false) {
|
|
319
|
+
return 'no';
|
|
320
|
+
}
|
|
321
|
+
return '—';
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* @param {Object} row
|
|
326
|
+
* @returns {string}
|
|
327
|
+
*/
|
|
328
|
+
function protectionListCardTitle(row) {
|
|
329
|
+
return String(row?.datasourceKey || row?.displayName || row?.key || '—').trim() || '—';
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* @param {string[]} lines
|
|
334
|
+
* @param {Object} row
|
|
335
|
+
* @param {Object} [opts]
|
|
336
|
+
*/
|
|
337
|
+
function appendProtectionListCard(lines, row, opts = {}) {
|
|
338
|
+
lines.push(chalk.bold(protectionListCardTitle(row)));
|
|
339
|
+
lines.push(SEP);
|
|
340
|
+
lines.push(headerKeyValue('Protection key:', row?.key ?? '—'));
|
|
341
|
+
lines.push(headerKeyValue('Datasource:', row?.datasourceKey ?? '—'));
|
|
342
|
+
lines.push(headerKeyValue('Display name:', row?.displayName ?? '—'));
|
|
343
|
+
lines.push(headerKeyValue('Enabled:', formatProtectionEnabledLabel(row?.enabled)));
|
|
344
|
+
lines.push(
|
|
345
|
+
headerKeyValue('Revision:', String(row?.currentRevision ?? row?.revision ?? '—'))
|
|
346
|
+
);
|
|
347
|
+
if (opts.verbose && row?.lastDeployedAt) {
|
|
348
|
+
lines.push(headerKeyValue('Last deployed:', row.lastDeployedAt));
|
|
349
|
+
}
|
|
350
|
+
lines.push('');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* @param {string[]} lines
|
|
355
|
+
* @param {Object[]} items
|
|
356
|
+
* @param {Object} [opts]
|
|
357
|
+
*/
|
|
358
|
+
function appendProtectionListCards(lines, items, opts = {}) {
|
|
359
|
+
for (const row of items) {
|
|
360
|
+
appendProtectionListCard(lines, row, opts);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* @param {string[]} lines
|
|
366
|
+
* @param {Object} meta
|
|
367
|
+
* @param {number} shown
|
|
368
|
+
*/
|
|
369
|
+
function appendProtectionListMetaFooter(lines, meta, shown) {
|
|
370
|
+
if (!meta || typeof meta.totalItems !== 'number') {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const page = meta.currentPage ?? 1;
|
|
374
|
+
const pageSize = meta.pageSize ?? shown;
|
|
375
|
+
lines.push(
|
|
376
|
+
chalk.gray(` Showing ${shown} of ${meta.totalItems} (page ${page}, pageSize ${pageSize})`)
|
|
377
|
+
);
|
|
378
|
+
lines.push('');
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* @param {string[]} lines
|
|
383
|
+
* @param {Object} payload
|
|
384
|
+
* @param {Object} [opts]
|
|
385
|
+
*/
|
|
386
|
+
function appendProtectionListTable(lines, payload, opts = {}) {
|
|
387
|
+
const environment = payload.environment || 'dev';
|
|
388
|
+
const dataplaneUrl = payload.dataplaneUrl || '';
|
|
389
|
+
const items = Array.isArray(payload.items) ? payload.items : [];
|
|
390
|
+
const meta = payload.meta || null;
|
|
391
|
+
|
|
392
|
+
lines.push(
|
|
393
|
+
sectionTitle(
|
|
394
|
+
`Protection manifests in ${environment} environment${dataplaneUrl ? ` (${dataplaneUrl})` : ''}:`
|
|
395
|
+
)
|
|
396
|
+
);
|
|
397
|
+
lines.push('');
|
|
398
|
+
|
|
399
|
+
if (!items.length) {
|
|
400
|
+
lines.push(chalk.gray(' No protection manifests found.\n'));
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
appendProtectionListCards(lines, items, opts);
|
|
405
|
+
appendProtectionListMetaFooter(lines, meta, items.length);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function appendProtectionShowVerboseGrants(lines, grants) {
|
|
409
|
+
appendProtectionShowSection(lines, 'Grants by rule');
|
|
410
|
+
const byRule = new Map();
|
|
411
|
+
for (const g of grants) {
|
|
412
|
+
const rk = g.projectionRuleKey || '—';
|
|
413
|
+
if (!byRule.has(rk)) {
|
|
414
|
+
byRule.set(rk, []);
|
|
415
|
+
}
|
|
416
|
+
byRule.get(rk).push(g);
|
|
417
|
+
}
|
|
418
|
+
for (const [ruleKey, grantRows] of byRule) {
|
|
419
|
+
lines.push(` ${ruleKey}`);
|
|
420
|
+
for (const g of grantRows) {
|
|
421
|
+
lines.push(
|
|
422
|
+
chalk.gray(` ${g.dimensionKey} → ${g.effectiveValueType || g.valueType || '—'}`)
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
module.exports = {
|
|
429
|
+
taskStableCode,
|
|
430
|
+
appendTaskRows,
|
|
431
|
+
appendProtectionReportHeader,
|
|
432
|
+
appendSimulationSection,
|
|
433
|
+
appendValidationSummarySection,
|
|
434
|
+
appendBatchResultRows,
|
|
435
|
+
appendBatchSummary,
|
|
436
|
+
appendProtectionShowHeader,
|
|
437
|
+
appendProtectionShowContextFooter,
|
|
438
|
+
appendProtectionShowCounts,
|
|
439
|
+
appendProtectionShowVerboseGrants,
|
|
440
|
+
formatProtectionShowHash,
|
|
441
|
+
formatProtectionShowTimestamp,
|
|
442
|
+
appendProtectionListTable,
|
|
443
|
+
formatProtectionEnabledLabel,
|
|
444
|
+
formatStatusKeyValue
|
|
445
|
+
};
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview TTY formatters for protection CLI (layout-blocks / tty-summary).
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const chalk = require('chalk');
|
|
11
|
+
const {
|
|
12
|
+
sectionTitle,
|
|
13
|
+
headerKeyValue,
|
|
14
|
+
metadata,
|
|
15
|
+
formatBlockingError,
|
|
16
|
+
formatSuccessLine,
|
|
17
|
+
formatWarningLine,
|
|
18
|
+
formatProgress,
|
|
19
|
+
formatNextActions
|
|
20
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
21
|
+
const { reportTaskRows } = require('./report-exit');
|
|
22
|
+
const { describeProtectionRoot } = require('./paths');
|
|
23
|
+
const {
|
|
24
|
+
appendTaskRows,
|
|
25
|
+
appendProtectionReportHeader,
|
|
26
|
+
appendSimulationSection,
|
|
27
|
+
appendValidationSummarySection,
|
|
28
|
+
appendBatchResultRows,
|
|
29
|
+
appendBatchSummary,
|
|
30
|
+
appendProtectionShowHeader,
|
|
31
|
+
appendProtectionShowContextFooter,
|
|
32
|
+
appendProtectionShowVerboseGrants,
|
|
33
|
+
appendProtectionListTable,
|
|
34
|
+
formatStatusKeyValue
|
|
35
|
+
} = require('./protection-display-helpers');
|
|
36
|
+
|
|
37
|
+
const SEP = chalk.gray('────────────────────────────────────────');
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {Object} report
|
|
41
|
+
* @param {Object} [opts]
|
|
42
|
+
* @returns {string}
|
|
43
|
+
*/
|
|
44
|
+
function formatProtectionValidateTTY(report, opts = {}) {
|
|
45
|
+
const lines = [];
|
|
46
|
+
const meta = report?.metadata || {};
|
|
47
|
+
appendProtectionReportHeader(lines, meta);
|
|
48
|
+
const summary = report?.summary || {};
|
|
49
|
+
const failCount = Number(summary.fail || 0);
|
|
50
|
+
const warnCount = Number(summary.warn || 0);
|
|
51
|
+
const aggregate = failCount > 0 ? 'fail' : warnCount > 0 ? 'warn' : 'ok';
|
|
52
|
+
const glyph = aggregate === 'fail' ? '✖' : aggregate === 'warn' ? '⚠' : '✔';
|
|
53
|
+
lines.push(formatStatusKeyValue(aggregate, glyph));
|
|
54
|
+
const rows = reportTaskRows(report);
|
|
55
|
+
appendTaskRows(lines, rows, 'FAIL', opts);
|
|
56
|
+
appendTaskRows(lines, rows, 'WARN', opts);
|
|
57
|
+
if (opts.verbose) {
|
|
58
|
+
appendTaskRows(lines, rows, 'PASS', opts);
|
|
59
|
+
}
|
|
60
|
+
if (opts.simulationBlock) {
|
|
61
|
+
appendSimulationSection(lines, opts.simulationBlock);
|
|
62
|
+
}
|
|
63
|
+
appendValidationSummarySection(lines, failCount, warnCount, opts);
|
|
64
|
+
return lines.join('\n');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {Object} report
|
|
69
|
+
* @param {Object} opts
|
|
70
|
+
*/
|
|
71
|
+
function printProtectionValidateReport(report, opts = {}) {
|
|
72
|
+
if (opts.json) {
|
|
73
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const text = formatProtectionValidateTTY(report, opts);
|
|
77
|
+
if (text) {
|
|
78
|
+
opts.logger.log(text);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {Object[]} results
|
|
84
|
+
* @param {Object} [opts]
|
|
85
|
+
* @returns {string}
|
|
86
|
+
*/
|
|
87
|
+
function formatProtectionBatchFolderLine(opts = {}) {
|
|
88
|
+
const described = describeProtectionRoot();
|
|
89
|
+
const folderPath = opts.folder ? path.resolve(String(opts.folder)) : described.root;
|
|
90
|
+
const lines = [metadata(`Folder: ${described.label} — ${folderPath}`)];
|
|
91
|
+
if (described.migrationHint) {
|
|
92
|
+
lines.push(formatWarningLine(described.migrationHint));
|
|
93
|
+
}
|
|
94
|
+
return lines;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @param {Object[]} results
|
|
99
|
+
* @param {Object} [opts]
|
|
100
|
+
* @returns {string}
|
|
101
|
+
*/
|
|
102
|
+
function formatProtectionBatchValidateTTY(results, opts = {}) {
|
|
103
|
+
const lines = [sectionTitle('Protection validate (batch)'), SEP, 'Scope: .protection'];
|
|
104
|
+
lines.push(...formatProtectionBatchFolderLine(opts));
|
|
105
|
+
lines.push('');
|
|
106
|
+
let passed = 0;
|
|
107
|
+
let failed = 0;
|
|
108
|
+
for (const row of results) {
|
|
109
|
+
if (row.ok) passed += 1;
|
|
110
|
+
else failed += 1;
|
|
111
|
+
}
|
|
112
|
+
appendBatchResultRows(lines, results, 'Validated');
|
|
113
|
+
appendBatchSummary(lines, passed, failed, `All ${passed} manifest(s) validated`);
|
|
114
|
+
return lines.join('\n');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @param {Object[]} results
|
|
119
|
+
* @param {Object} [opts]
|
|
120
|
+
* @returns {string}
|
|
121
|
+
*/
|
|
122
|
+
function formatProtectionBatchUploadTTY(results, opts = {}) {
|
|
123
|
+
const lines = [sectionTitle('Protection upload (batch)'), SEP, 'Scope: .protection'];
|
|
124
|
+
lines.push(...formatProtectionBatchFolderLine(opts));
|
|
125
|
+
lines.push('');
|
|
126
|
+
let passed = 0;
|
|
127
|
+
let failed = 0;
|
|
128
|
+
for (const row of results) {
|
|
129
|
+
if (row.ok) passed += 1;
|
|
130
|
+
else failed += 1;
|
|
131
|
+
}
|
|
132
|
+
appendBatchResultRows(lines, results, 'Validated and uploaded');
|
|
133
|
+
appendBatchSummary(lines, passed, failed, `All ${passed} manifest(s) uploaded`);
|
|
134
|
+
return lines.join('\n');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @returns {string}
|
|
139
|
+
*/
|
|
140
|
+
function formatDeployProtectionNotImplementedTTY() {
|
|
141
|
+
return [
|
|
142
|
+
formatBlockingError('deploy .protection is not supported.'),
|
|
143
|
+
'',
|
|
144
|
+
'Protection manifests are uploaded to the dataplane directly, not via the Miso Controller deploy pipeline.',
|
|
145
|
+
'',
|
|
146
|
+
'Use:',
|
|
147
|
+
' aifabrix upload .protection',
|
|
148
|
+
' aifabrix protection upload <datasourceKey>'
|
|
149
|
+
].join('\n');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* @param {Object} payload
|
|
154
|
+
* @param {Object} [opts]
|
|
155
|
+
* @returns {string}
|
|
156
|
+
*/
|
|
157
|
+
function formatProtectionListTTY(payload, opts = {}) {
|
|
158
|
+
const lines = [];
|
|
159
|
+
appendProtectionListTable(lines, payload, opts);
|
|
160
|
+
return lines.join('\n');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @param {Object} payload
|
|
165
|
+
* @param {Object} [opts]
|
|
166
|
+
* @returns {string}
|
|
167
|
+
*/
|
|
168
|
+
function formatProtectionShowTTY(payload, opts = {}) {
|
|
169
|
+
const lines = [];
|
|
170
|
+
appendProtectionShowHeader(lines, payload, opts);
|
|
171
|
+
const grants = Array.isArray(payload.status?.grants) ? payload.status.grants : [];
|
|
172
|
+
if (opts.verbose && grants.length) {
|
|
173
|
+
appendProtectionShowVerboseGrants(lines, grants);
|
|
174
|
+
}
|
|
175
|
+
appendProtectionShowContextFooter(lines, payload);
|
|
176
|
+
return lines.join('\n');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @param {Object} response
|
|
181
|
+
* @param {string} datasourceKey
|
|
182
|
+
* @returns {string}
|
|
183
|
+
*/
|
|
184
|
+
function formatProtectionDeleteSummaryTTY(response, datasourceKey) {
|
|
185
|
+
const lines = [formatSuccessLine(`Protection for '${datasourceKey}' deleted`)];
|
|
186
|
+
if (response && typeof response === 'object') {
|
|
187
|
+
if (response.dynamicValuesRemoved !== null && response.dynamicValuesRemoved !== undefined) {
|
|
188
|
+
lines.push(` Dynamic values removed: ${response.dynamicValuesRemoved}`);
|
|
189
|
+
}
|
|
190
|
+
if (response.principalGrantsRemoved !== null && response.principalGrantsRemoved !== undefined) {
|
|
191
|
+
lines.push(` Principal grants removed: ${response.principalGrantsRemoved}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return lines.join('\n');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = {
|
|
198
|
+
SEP,
|
|
199
|
+
formatProtectionValidateTTY,
|
|
200
|
+
printProtectionValidateReport,
|
|
201
|
+
formatProtectionBatchValidateTTY,
|
|
202
|
+
formatProtectionBatchUploadTTY,
|
|
203
|
+
formatDeployProtectionNotImplementedTTY,
|
|
204
|
+
formatProtectionListTTY,
|
|
205
|
+
formatProtectionShowTTY,
|
|
206
|
+
formatProtectionDeleteSummaryTTY,
|
|
207
|
+
formatProgress,
|
|
208
|
+
formatSuccessLine,
|
|
209
|
+
formatBlockingError,
|
|
210
|
+
formatWarningLine,
|
|
211
|
+
formatNextActions,
|
|
212
|
+
metadata,
|
|
213
|
+
headerKeyValue,
|
|
214
|
+
sectionTitle
|
|
215
|
+
};
|