@aifabrix/builder 2.46.0 → 2.47.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.
- package/.npmrc.token +1 -0
- package/.temp/jest-protection-convert/run-IFN2nv/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-IvmKLO/.protection/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-convert/run-SdvblF/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-b51TsW/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-fiLmb1/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-qYGHMU/.protection/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-Mq5re8/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-f3u4YL/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-iQtd1O/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-mdmz5M/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-qbKYxQ/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-yzlJy6/hubspot-companies.yaml +17 -0
- package/.temp/validation/summary.txt +5 -0
- package/jest.isolated-projects.js +18 -2
- package/jest.projects.js +24 -0
- package/lib/api/dataplane-health.api.js +19 -7
- package/lib/api/lifecycle.api.js +22 -1
- package/lib/api/resource-types.api.js +107 -0
- package/lib/api/runtime-configuration.api.js +72 -0
- package/lib/api/types/resource-types.types.js +45 -0
- package/lib/api/user-groups.api.js +9 -9
- package/lib/api/users.api.js +8 -8
- package/lib/api/wizard.api.js +10 -2
- package/lib/app/down.js +58 -26
- package/lib/app/index.js +2 -2
- package/lib/app/run-resolve-image.js +2 -1
- package/lib/app/run-stop-containers.js +113 -0
- package/lib/app/run.js +1 -31
- package/lib/app/show-display.js +35 -0
- package/lib/app/show.js +32 -1
- package/lib/build/index.js +17 -5
- package/lib/build/miso-controller-npm-sdk-guard.js +25 -0
- package/lib/channels/add-channel.js +348 -0
- package/lib/channels/channel-artifact-generator.js +141 -0
- package/lib/channels/channel-audit.js +101 -0
- package/lib/channels/channel-auth-context.js +37 -0
- package/lib/channels/channel-credential.js +200 -0
- package/lib/channels/channel-display-name.js +64 -0
- package/lib/channels/channel-external-system.js +106 -0
- package/lib/channels/channel-integration-generator.js +274 -0
- package/lib/channels/channel-probe.js +89 -0
- package/lib/channels/channel-runtime-resolver.js +152 -0
- package/lib/channels/channel-status-display.js +141 -0
- package/lib/channels/channel-targets.js +115 -0
- package/lib/cli/index.js +16 -4
- package/lib/cli/setup-app.js +54 -3
- package/lib/cli/setup-infra-up-dataplane-action.js +2 -2
- package/lib/cli/setup-infra-up-platform-action.js +2 -1
- package/lib/cli/setup-platform.js +1 -1
- package/lib/cli/setup-utility-help-after.js +4 -1
- package/lib/cli/setup-utility-resolve.js +8 -5
- package/lib/commands/app-logs.js +54 -26
- package/lib/commands/app.js +41 -0
- package/lib/commands/auth-status-dataplane-version.js +97 -61
- package/lib/commands/auth-status.js +2 -7
- package/lib/commands/channel.js +58 -0
- package/lib/commands/datasource.js +1 -1
- package/lib/commands/identity-apply-sync.js +49 -8
- package/lib/commands/identity-group-cli.js +66 -26
- package/lib/commands/identity-membership-role-cache.js +58 -37
- package/lib/commands/identity-shared.js +22 -1
- package/lib/commands/identity-user-cli.js +80 -34
- package/lib/commands/repair-auth-apply.js +30 -4
- package/lib/commands/repair-auth-config.js +8 -13
- package/lib/commands/repair-datasource-com-v2.js +15 -0
- package/lib/commands/repair-datasource-service-contract.js +206 -0
- package/lib/commands/repair-datasource.js +53 -18
- package/lib/commands/repair-env-template.js +12 -12
- package/lib/commands/repair-openapi-sync.js +26 -4
- package/lib/commands/repair-steps.js +28 -13
- package/lib/commands/repair-system-alignment.js +7 -4
- package/lib/commands/resource-type.js +371 -0
- package/lib/commands/setup-modes.js +1 -1
- package/lib/commands/setup-prompts-platform-mode.js +24 -21
- package/lib/commands/test-trust-external.js +39 -90
- package/lib/commands/up-common.js +33 -1
- package/lib/commands/up-dataplane.js +8 -4
- package/lib/commands/upload.js +24 -3
- package/lib/commands/verify-trust-command-action.js +4 -1
- package/lib/commands/verify-trust-external.js +12 -20
- package/lib/core/config-setup-platform-mode.js +3 -3
- package/lib/core/setup-platform-mode.js +41 -6
- package/lib/datasource/capability/capability-diff-slice.js +2 -2
- package/lib/datasource/capability/capability-manifest.js +254 -0
- package/lib/datasource/capability/capability-resolve.js +3 -4
- package/lib/datasource/capability/copy-operations.js +5 -3
- package/lib/datasource/capability/remove-operations.js +2 -1
- package/lib/datasource/capability/run-capability-copy.js +32 -4
- package/lib/datasource/capability/run-capability-diff.js +3 -3
- package/lib/datasource/capability/run-capability-dimension.js +2 -4
- package/lib/datasource/capability/run-capability-edit.js +4 -7
- package/lib/datasource/capability/run-capability-relate.js +2 -4
- package/lib/datasource/capability/run-capability-remove.js +2 -4
- package/lib/datasource/capability/validate-capability-slice.js +2 -1
- package/lib/datasource/datasource-identifier.js +2 -1
- package/lib/datasource/datasource-validate-display.js +1 -1
- package/lib/datasource/field-reference-validator.js +24 -3
- package/lib/datasource/orphan-profiles-validator.js +98 -0
- package/lib/datasource/validate.js +41 -23
- package/lib/external-system/download-helpers.js +87 -0
- package/lib/external-system/download.js +61 -122
- package/lib/external-system/generator.js +1 -59
- package/lib/external-system/test.js +10 -15
- package/lib/generator/builders.js +11 -1
- package/lib/generator/external-controller-manifest.js +5 -2
- package/lib/generator/split.js +3 -1
- package/lib/generator/wizard.js +1 -28
- package/lib/identity/fk-reference.js +65 -0
- package/lib/identity/identity-apply-core.js +13 -58
- package/lib/identity/identity-apply-phases.js +5 -1
- package/lib/identity/identity-display.js +243 -0
- package/lib/identity/identity-response-helpers.js +52 -0
- package/lib/identity/identity-sync-errors.js +62 -0
- package/lib/identity/resolve-user-id.js +129 -0
- package/lib/identity/username-default.js +31 -0
- package/lib/lifecycle/certification-progress.js +2 -2
- package/lib/lifecycle/report-display-trust.js +70 -0
- package/lib/lifecycle/report-display.js +14 -13
- package/lib/resolvers/datasource-resolver.js +2 -3
- package/lib/resolvers/resource-type-file.js +159 -0
- package/lib/schema/application-schema.json +16 -0
- package/lib/schema/deployment-rules.yaml +2 -1
- package/lib/schema/external-datasource.schema.json +372 -58
- package/lib/schema/external-system.schema.json +331 -2
- package/lib/schema/type/document-storage.json +9 -5
- package/lib/utils/app-run-containers.js +25 -2
- package/lib/utils/authentication-shape.js +224 -0
- package/lib/utils/build-copy.js +36 -5
- package/lib/utils/build-resolve-image.js +66 -12
- package/lib/utils/celery-companion-apps.js +53 -0
- package/lib/utils/cli-utils.js +4 -0
- package/lib/utils/compose-celery-worker.js +23 -0
- package/lib/utils/compose-generate-docker-compose.js +9 -1
- package/lib/utils/configuration-env-resolver.js +36 -0
- package/lib/utils/credential-secrets-env-kv.js +216 -0
- package/lib/utils/credential-secrets-env.js +52 -262
- package/lib/utils/dataplane-resolver.js +4 -115
- package/lib/utils/declarative-url-ports.js +24 -1
- package/lib/utils/docker-build.js +1 -4
- package/lib/utils/external-env-template.js +10 -3
- package/lib/utils/external-system-validators.js +8 -2
- package/lib/utils/help-builder.js +2 -0
- package/lib/utils/paths.js +25 -0
- package/lib/utils/platform-controller-url.js +2 -1
- package/lib/utils/platform-kv-ref.js +57 -0
- package/lib/utils/runtime-configuration-download.js +189 -0
- package/lib/utils/runtime-configuration-values.js +119 -0
- package/lib/utils/schema-loader.js +19 -4
- package/lib/utils/worker-sidecars.js +142 -0
- package/lib/validation/external-manifest-validator.js +2 -0
- package/lib/validation/runtime-configuration-rules.js +98 -0
- package/lib/validation/validate-display-dimensions.js +154 -0
- package/lib/validation/validate-display-sections.js +276 -0
- package/lib/validation/validate-display-steps.js +67 -0
- package/lib/validation/validate-display.js +24 -402
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +5 -0
- package/templates/applications/dataplane/env.template +26 -13
- package/templates/applications/dataplane/rbac.yaml +108 -1
- package/templates/applications/miso-controller/application.yaml +2 -2
- package/templates/applications/miso-controller/env.template +37 -122
- package/templates/applications/miso-controller/rbac.yaml +28 -13
- package/templates/channels/chatgpt/install.template.md.hbs +49 -0
- package/templates/channels/chatgpt/manifest.template.json.hbs +9 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +49 -0
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +8 -0
- package/templates/channels/slack/install.template.md.hbs +49 -0
- package/templates/channels/slack/manifest.template.json.hbs +66 -0
- package/templates/channels/teams-bot/install.template.md.hbs +49 -0
- package/templates/channels/teams-bot/manifest.template.json.hbs +13 -0
- package/templates/channels/web-embed/install.template.md.hbs +49 -0
- package/templates/channels/web-embed/manifest.template.json.hbs +8 -0
- package/templates/python/docker-compose.hbs +56 -0
- package/templates/typescript/docker-compose.hbs +52 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview TTY layout helpers for aifabrix identity commands (plan 151)
|
|
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('../utils/logger');
|
|
11
|
+
const {
|
|
12
|
+
formatBlockingError,
|
|
13
|
+
formatNextActions,
|
|
14
|
+
headerKeyValue,
|
|
15
|
+
sectionTitle
|
|
16
|
+
} = require('../utils/cli-layout-chalk');
|
|
17
|
+
const { readFkId, readFkKey, readFkName, formatFkLabel } = require('./fk-reference');
|
|
18
|
+
const { unwrapControllerData } = require('./identity-response-helpers');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {Object|null|undefined} response
|
|
22
|
+
* @returns {{ items: Object[], meta: Object|null }}
|
|
23
|
+
*/
|
|
24
|
+
function extractPaginatedList(response) {
|
|
25
|
+
if (!response || response.success === false) {
|
|
26
|
+
return { items: [], meta: null };
|
|
27
|
+
}
|
|
28
|
+
const body = response.data;
|
|
29
|
+
if (body && Array.isArray(body.data)) {
|
|
30
|
+
return { items: body.data, meta: body.meta ?? null };
|
|
31
|
+
}
|
|
32
|
+
const unwrapped = unwrapControllerData(response);
|
|
33
|
+
if (Array.isArray(unwrapped)) {
|
|
34
|
+
return { items: unwrapped, meta: null };
|
|
35
|
+
}
|
|
36
|
+
if (unwrapped && Array.isArray(unwrapped.data)) {
|
|
37
|
+
return { items: unwrapped.data, meta: unwrapped.meta ?? null };
|
|
38
|
+
}
|
|
39
|
+
return { items: [], meta: null };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {Object} user
|
|
44
|
+
* @returns {string}
|
|
45
|
+
*/
|
|
46
|
+
function formatUserDisplayName(user) {
|
|
47
|
+
const display = String(user?.displayName || '').trim();
|
|
48
|
+
if (display) {
|
|
49
|
+
return display;
|
|
50
|
+
}
|
|
51
|
+
const parts = [user?.firstName, user?.lastName].filter(Boolean).map(String);
|
|
52
|
+
const joined = parts.join(' ').trim();
|
|
53
|
+
return joined || '—';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {string} value
|
|
58
|
+
* @param {number} width
|
|
59
|
+
* @returns {string}
|
|
60
|
+
*/
|
|
61
|
+
function padCell(value, width) {
|
|
62
|
+
const text = String(value ?? '—');
|
|
63
|
+
if (text.length <= width) {
|
|
64
|
+
return text.padEnd(width);
|
|
65
|
+
}
|
|
66
|
+
if (width <= 1) {
|
|
67
|
+
return text.slice(0, width);
|
|
68
|
+
}
|
|
69
|
+
return `${text.slice(0, width - 1)}…`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {{ emoji?: string, title: string, context?: string, shown?: number, total?: number|null }} opts
|
|
74
|
+
*/
|
|
75
|
+
function printIdentityListHeader(opts) {
|
|
76
|
+
const emoji = opts.emoji ? `${opts.emoji} ` : '';
|
|
77
|
+
const context = opts.context ? ` (${opts.context})` : '';
|
|
78
|
+
const shown = Number.isFinite(opts.shown) ? opts.shown : 0;
|
|
79
|
+
const total = Number.isFinite(opts.total) ? opts.total : null;
|
|
80
|
+
const count =
|
|
81
|
+
total !== null && total >= shown ? `${shown} shown of ${total}` : `${shown} shown`;
|
|
82
|
+
logger.log(chalk.bold(`\n${emoji}${opts.title}${context} — ${count}:\n`));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {Array<{ label: string, width: number, value: (row: Object) => string }>} columns
|
|
87
|
+
* @param {Object[]} rows
|
|
88
|
+
*/
|
|
89
|
+
function printIdentityTable(columns, rows) {
|
|
90
|
+
const header = columns.map((col) => padCell(col.label, col.width)).join('');
|
|
91
|
+
logger.log(chalk.gray(header));
|
|
92
|
+
logger.log(chalk.gray('-'.repeat(columns.reduce((sum, col) => sum + col.width, 0))));
|
|
93
|
+
rows.forEach((row) => {
|
|
94
|
+
const line = columns.map((col) => padCell(col.value(row), col.width)).join('');
|
|
95
|
+
logger.log(line);
|
|
96
|
+
});
|
|
97
|
+
logger.log('');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @param {Object|null|undefined} meta
|
|
102
|
+
*/
|
|
103
|
+
function printListPaginationFooter(meta) {
|
|
104
|
+
if (!meta || !Number.isFinite(meta.totalItems)) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const page = meta.currentPage ?? meta.page ?? 1;
|
|
108
|
+
const pageSize = meta.pageSize ?? meta.page_size;
|
|
109
|
+
const totalPages = meta.totalPages ?? meta.total_pages;
|
|
110
|
+
const parts = [`total ${meta.totalItems}`];
|
|
111
|
+
if (pageSize) {
|
|
112
|
+
parts.unshift(`page ${page}`);
|
|
113
|
+
}
|
|
114
|
+
if (totalPages) {
|
|
115
|
+
parts.push(`${totalPages} pages`);
|
|
116
|
+
}
|
|
117
|
+
logger.log(chalk.gray(` ${parts.join(' · ')}\n`));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @param {string} title
|
|
122
|
+
* @param {Array<{ label: string, value: string }>} fields
|
|
123
|
+
*/
|
|
124
|
+
function printIdentityRecord(title, fields) {
|
|
125
|
+
logger.log(sectionTitle(title));
|
|
126
|
+
fields.forEach(({ label, value }) => {
|
|
127
|
+
logger.log(` ${headerKeyValue(`${label}:`, value || '—')}`);
|
|
128
|
+
});
|
|
129
|
+
logger.log('');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @param {string} message
|
|
134
|
+
* @param {string[]} [hints]
|
|
135
|
+
*/
|
|
136
|
+
function printIdentityEmpty(message, hints = []) {
|
|
137
|
+
logger.log(chalk.gray(` ${message}\n`));
|
|
138
|
+
if (hints.length > 0) {
|
|
139
|
+
logger.log(formatNextActions(hints));
|
|
140
|
+
logger.log('');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @param {string} entity
|
|
146
|
+
* @param {string} query
|
|
147
|
+
* @param {string[]} [hints]
|
|
148
|
+
*/
|
|
149
|
+
function printIdentityNotFound(entity, query, hints = []) {
|
|
150
|
+
logger.error(formatBlockingError(`${entity} not found: ${query}`));
|
|
151
|
+
if (hints.length > 0) {
|
|
152
|
+
logger.log(formatNextActions(hints));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const USER_LIST_COLUMNS = [
|
|
157
|
+
{ label: 'Id', width: 38, value: (u) => u.id || '—' },
|
|
158
|
+
{ label: 'Username', width: 16, value: (u) => u.username || '—' },
|
|
159
|
+
{ label: 'Name', width: 22, value: (u) => formatUserDisplayName(u) },
|
|
160
|
+
{ label: 'Email', width: 34, value: (u) => u.email || '—' },
|
|
161
|
+
{ label: 'Status', width: 10, value: (u) => u.status || '—' }
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
const GROUP_LIST_COLUMNS = [
|
|
165
|
+
{ label: 'Id', width: 38, value: (g) => g.id || '—' },
|
|
166
|
+
{ label: 'Name', width: 28, value: (g) => g.name || '—' },
|
|
167
|
+
{ label: 'Display name', width: 28, value: (g) => g.displayName || '—' }
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @param {Object[]} memberships
|
|
172
|
+
*/
|
|
173
|
+
function printUserGroupsTable(memberships) {
|
|
174
|
+
printIdentityTable(
|
|
175
|
+
[
|
|
176
|
+
{ label: 'Group key', width: 28, value: (m) => readFkKey(m.groupId) || '—' },
|
|
177
|
+
{ label: 'Group name', width: 28, value: (m) => formatFkLabel(m.groupId) },
|
|
178
|
+
{ label: 'Group id', width: 38, value: (m) => readFkId(m.groupId) || '—' },
|
|
179
|
+
{ label: 'Role hint', width: 12, value: (m) => m.roleHint || '—' }
|
|
180
|
+
],
|
|
181
|
+
memberships
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* @param {Object[]} memberships
|
|
187
|
+
*/
|
|
188
|
+
function printGroupMembersTable(memberships) {
|
|
189
|
+
printIdentityTable(
|
|
190
|
+
[
|
|
191
|
+
{ label: 'User key', width: 22, value: (m) => readFkKey(m.userId) || '—' },
|
|
192
|
+
{ label: 'User name', width: 24, value: (m) => formatFkLabel(m.userId) },
|
|
193
|
+
{ label: 'User id', width: 38, value: (m) => readFkId(m.userId) || '—' },
|
|
194
|
+
{ label: 'Role hint', width: 12, value: (m) => m.roleHint || '—' }
|
|
195
|
+
],
|
|
196
|
+
memberships
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @param {Object} user
|
|
202
|
+
*/
|
|
203
|
+
function printUserRecord(user) {
|
|
204
|
+
printIdentityRecord('User', [
|
|
205
|
+
{ label: 'Id', value: user.id },
|
|
206
|
+
{ label: 'Email', value: user.email },
|
|
207
|
+
{ label: 'Username', value: user.username },
|
|
208
|
+
{ label: 'Name', value: formatUserDisplayName(user) },
|
|
209
|
+
{ label: 'Display name', value: user.displayName },
|
|
210
|
+
{ label: 'Status', value: user.status },
|
|
211
|
+
{ label: 'Verified', value: user.emailVerified ? 'yes' : 'no' }
|
|
212
|
+
]);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* @param {Object} group
|
|
217
|
+
*/
|
|
218
|
+
function printGroupRecord(group) {
|
|
219
|
+
printIdentityRecord('Group', [
|
|
220
|
+
{ label: 'Id', value: group.id },
|
|
221
|
+
{ label: 'Name', value: group.name },
|
|
222
|
+
{ label: 'Display name', value: group.displayName },
|
|
223
|
+
{ label: 'Description', value: group.description }
|
|
224
|
+
]);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
module.exports = {
|
|
228
|
+
extractPaginatedList,
|
|
229
|
+
formatUserDisplayName,
|
|
230
|
+
printIdentityListHeader,
|
|
231
|
+
printIdentityTable,
|
|
232
|
+
printListPaginationFooter,
|
|
233
|
+
printIdentityRecord,
|
|
234
|
+
printIdentityEmpty,
|
|
235
|
+
printIdentityNotFound,
|
|
236
|
+
USER_LIST_COLUMNS,
|
|
237
|
+
GROUP_LIST_COLUMNS,
|
|
238
|
+
printUserGroupsTable,
|
|
239
|
+
printGroupMembersTable,
|
|
240
|
+
printUserRecord,
|
|
241
|
+
printGroupRecord,
|
|
242
|
+
readFkName
|
|
243
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared controller response helpers for identity modules
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {Object} response
|
|
11
|
+
* @returns {any}
|
|
12
|
+
*/
|
|
13
|
+
function unwrapControllerData(response) {
|
|
14
|
+
if (!response || response.success === false) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
return response?.data?.data ?? response?.data ?? response;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {Object} response
|
|
22
|
+
* @returns {string}
|
|
23
|
+
*/
|
|
24
|
+
function apiErrorMessage(response) {
|
|
25
|
+
if (!response) {
|
|
26
|
+
return 'Request failed';
|
|
27
|
+
}
|
|
28
|
+
const err = response.error;
|
|
29
|
+
if (typeof err === 'string') {
|
|
30
|
+
return err;
|
|
31
|
+
}
|
|
32
|
+
if (err && typeof err === 'object') {
|
|
33
|
+
return err.formattedError || err.message || err.error || 'Request failed';
|
|
34
|
+
}
|
|
35
|
+
return response.formattedError || response.message || 'Request failed';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {Object} response
|
|
40
|
+
* @throws {Error}
|
|
41
|
+
*/
|
|
42
|
+
function throwIfApiFailed(response) {
|
|
43
|
+
if (!response || response.success === false) {
|
|
44
|
+
throw new Error(apiErrorMessage(response));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
unwrapControllerData,
|
|
50
|
+
apiErrorMessage,
|
|
51
|
+
throwIfApiFailed
|
|
52
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview User-facing identity sync failure messages
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {Object|null|undefined} stats
|
|
11
|
+
* @returns {boolean}
|
|
12
|
+
*/
|
|
13
|
+
function isZeroProgressSyncFailure(stats) {
|
|
14
|
+
const usersProcessed = Number(stats?.usersProcessed ?? 0);
|
|
15
|
+
const groupsProcessed = Number(stats?.groupsProcessed ?? 0);
|
|
16
|
+
return usersProcessed === 0 && groupsProcessed === 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Build a multi-line CLI message when controller sync stats report errors.
|
|
21
|
+
*
|
|
22
|
+
* @param {Object|null|undefined} stats
|
|
23
|
+
* @returns {string}
|
|
24
|
+
*/
|
|
25
|
+
function buildSyncStatsFailureMessage(stats) {
|
|
26
|
+
const syncErrors = Number(stats?.errors ?? 0);
|
|
27
|
+
const usersProcessed = Number(stats?.usersProcessed ?? 0);
|
|
28
|
+
const groupsProcessed = Number(stats?.groupsProcessed ?? 0);
|
|
29
|
+
|
|
30
|
+
const lines = [
|
|
31
|
+
`Dataplane full sync reported ${syncErrors} error(s).`,
|
|
32
|
+
`usersProcessed=${usersProcessed} groupsProcessed=${groupsProcessed}.`
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
if (isZeroProgressSyncFailure(stats)) {
|
|
36
|
+
lines.push(
|
|
37
|
+
'',
|
|
38
|
+
'The controller accepted the sync request, but no identity data reached the dataplane.',
|
|
39
|
+
'Common causes:',
|
|
40
|
+
' • Expired or invalid dataplane client token (controller → dataplane auth)',
|
|
41
|
+
' • Dataplane unreachable or unhealthy',
|
|
42
|
+
' • Dataplane MISO_CLIENTID / MISO_CLIENTSECRET out of sync with the controller',
|
|
43
|
+
'',
|
|
44
|
+
'Try:',
|
|
45
|
+
' aifabrix auth status',
|
|
46
|
+
' aifabrix run dataplane --reload',
|
|
47
|
+
'Then retry:',
|
|
48
|
+
' aifabrix identity sync -e dev --purge-cache',
|
|
49
|
+
'',
|
|
50
|
+
'If it still fails, check miso-controller logs for the sync request.'
|
|
51
|
+
);
|
|
52
|
+
} else {
|
|
53
|
+
lines.push('Check miso-controller logs for the sync request.');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return lines.join('\n');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = {
|
|
60
|
+
isZeroProgressSyncFailure,
|
|
61
|
+
buildSyncStatsFailureMessage
|
|
62
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Resolve controller user id from id, email, or username
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { getUser, listUsers } = require('../api/users.api');
|
|
10
|
+
const { throwIfApiFailed, unwrapControllerData } = require('./identity-response-helpers');
|
|
11
|
+
|
|
12
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} value
|
|
16
|
+
* @returns {boolean}
|
|
17
|
+
*/
|
|
18
|
+
function isLikelyUuid(value) {
|
|
19
|
+
return UUID_RE.test(String(value || '').trim());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {Object} response
|
|
24
|
+
* @returns {Object[]}
|
|
25
|
+
*/
|
|
26
|
+
function extractUserListItems(response) {
|
|
27
|
+
const data = unwrapControllerData(response);
|
|
28
|
+
if (Array.isArray(data)) {
|
|
29
|
+
return data;
|
|
30
|
+
}
|
|
31
|
+
if (Array.isArray(data?.data)) {
|
|
32
|
+
return data.data;
|
|
33
|
+
}
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @param {Object[]} items
|
|
39
|
+
* @param {string} value
|
|
40
|
+
* @returns {Object|null}
|
|
41
|
+
*/
|
|
42
|
+
function pickUserMatch(items, value) {
|
|
43
|
+
const trimmed = String(value || '').trim();
|
|
44
|
+
if (!trimmed) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const lower = trimmed.toLowerCase();
|
|
48
|
+
return (
|
|
49
|
+
items.find(
|
|
50
|
+
(user) =>
|
|
51
|
+
String(user.email || '').toLowerCase() === lower || String(user.username || '') === trimmed
|
|
52
|
+
) || null
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @async
|
|
58
|
+
* @param {string} controllerUrl
|
|
59
|
+
* @param {Object} authConfig
|
|
60
|
+
* @param {string} idOrEmail
|
|
61
|
+
* @returns {Promise<Object|null>}
|
|
62
|
+
*/
|
|
63
|
+
async function lookupUserByIdOrEmail(controllerUrl, authConfig, idOrEmail) {
|
|
64
|
+
const value = String(idOrEmail || '').trim();
|
|
65
|
+
if (!value) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (isLikelyUuid(value)) {
|
|
70
|
+
const byId = await getUser(controllerUrl, authConfig, value);
|
|
71
|
+
if (byId && byId.success !== false) {
|
|
72
|
+
const row = unwrapControllerData(byId);
|
|
73
|
+
if (row?.id) {
|
|
74
|
+
return row;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (value.includes('@')) {
|
|
81
|
+
const byEmailFilter = await listUsers(controllerUrl, authConfig, {
|
|
82
|
+
filter: `email:eq:${value}`,
|
|
83
|
+
pageSize: 1,
|
|
84
|
+
page: 1
|
|
85
|
+
});
|
|
86
|
+
throwIfApiFailed(byEmailFilter);
|
|
87
|
+
const filterMatch = pickUserMatch(extractUserListItems(byEmailFilter), value);
|
|
88
|
+
if (filterMatch?.id) {
|
|
89
|
+
return filterMatch;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const listed = await listUsers(controllerUrl, authConfig, {
|
|
94
|
+
search: value,
|
|
95
|
+
pageSize: 50,
|
|
96
|
+
page: 1
|
|
97
|
+
});
|
|
98
|
+
throwIfApiFailed(listed);
|
|
99
|
+
return pickUserMatch(extractUserListItems(listed), value);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @async
|
|
104
|
+
* @param {string} controllerUrl
|
|
105
|
+
* @param {Object} authConfig
|
|
106
|
+
* @param {string} idOrEmail
|
|
107
|
+
* @returns {Promise<string>} Controller user id
|
|
108
|
+
* @throws {Error} When value is empty or no matching user exists
|
|
109
|
+
*/
|
|
110
|
+
async function resolveUserIdOrEmail(controllerUrl, authConfig, idOrEmail) {
|
|
111
|
+
const value = String(idOrEmail || '').trim();
|
|
112
|
+
if (!value) {
|
|
113
|
+
throw new Error('idOrEmail is required.');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const user = await lookupUserByIdOrEmail(controllerUrl, authConfig, value);
|
|
117
|
+
if (user?.id) {
|
|
118
|
+
return user.id;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
throw new Error(`User not found: ${value}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = {
|
|
125
|
+
isLikelyUuid,
|
|
126
|
+
extractUserListItems,
|
|
127
|
+
lookupUserByIdOrEmail,
|
|
128
|
+
resolveUserIdOrEmail
|
|
129
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default controller username when callers omit --username.
|
|
3
|
+
* Matches users.csv UserPrincipalName (email) convention.
|
|
4
|
+
*
|
|
5
|
+
* @fileoverview Identity username resolution
|
|
6
|
+
* @author AI Fabrix Team
|
|
7
|
+
* @version 1.0.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {{ username?: string, email?: string }} input
|
|
14
|
+
* @returns {string}
|
|
15
|
+
* @throws {Error} When neither username nor email is non-empty
|
|
16
|
+
*/
|
|
17
|
+
function resolveIdentityUsername(input) {
|
|
18
|
+
const username = String(input?.username || '').trim();
|
|
19
|
+
if (username) {
|
|
20
|
+
return username;
|
|
21
|
+
}
|
|
22
|
+
const email = String(input?.email || '').trim();
|
|
23
|
+
if (email) {
|
|
24
|
+
return email;
|
|
25
|
+
}
|
|
26
|
+
throw new Error('username or email is required');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = {
|
|
30
|
+
resolveIdentityUsername
|
|
31
|
+
};
|
|
@@ -49,7 +49,7 @@ const SILVER_TO_GOLD_CRITERIA = [
|
|
|
49
49
|
{
|
|
50
50
|
id: 'aiTrust',
|
|
51
51
|
label: 'AI Trust ≥ 95%',
|
|
52
|
-
evaluate: ctx => (ctx.report.trust?.
|
|
52
|
+
evaluate: ctx => (ctx.report.trust?.aiTrustPercent ?? 0) >= 95
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
id: 'activeWarnings',
|
|
@@ -164,7 +164,7 @@ function buildCertificationProgress(report, options = {}) {
|
|
|
164
164
|
*/
|
|
165
165
|
function formatCriterionCurrentHint(id, report, openApiPublished) {
|
|
166
166
|
if (id === 'aiTrust') {
|
|
167
|
-
const p = report.trust?.
|
|
167
|
+
const p = report.trust?.aiTrustPercent;
|
|
168
168
|
return typeof p === 'number' ? `(current: ${p}%)` : null;
|
|
169
169
|
}
|
|
170
170
|
if (id === 'operationalReadiness') {
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Trust pillar TTY display helpers for verify-trust (433.0).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
const logger = require('../utils/logger');
|
|
9
|
+
const { successGlyph, failureGlyph } = require('../utils/cli-test-layout-chalk');
|
|
10
|
+
const { VERDICT } = require('./product-model');
|
|
11
|
+
|
|
12
|
+
const SEP = '────────────────────────';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} label
|
|
16
|
+
* @param {number|undefined} percent
|
|
17
|
+
*/
|
|
18
|
+
function displaySubMetricLine(label, percent) {
|
|
19
|
+
logger.log(chalk.white(label));
|
|
20
|
+
logger.log(
|
|
21
|
+
typeof percent === 'number'
|
|
22
|
+
? chalk.cyan.bold(`${percent}%`)
|
|
23
|
+
: chalk.gray('—')
|
|
24
|
+
);
|
|
25
|
+
logger.log('');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {Array<{ severity?: string, code?: string, message?: string }>|undefined} findings
|
|
30
|
+
*/
|
|
31
|
+
function displayTrustFindings(findings) {
|
|
32
|
+
if (!Array.isArray(findings) || findings.length === 0) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
logger.log('');
|
|
36
|
+
logger.log(chalk.white('Findings'));
|
|
37
|
+
logger.log(chalk.gray(SEP));
|
|
38
|
+
for (const finding of findings) {
|
|
39
|
+
const glyph =
|
|
40
|
+
finding.severity === 'error'
|
|
41
|
+
? failureGlyph()
|
|
42
|
+
: finding.severity === 'warning'
|
|
43
|
+
? chalk.yellow('⚠')
|
|
44
|
+
: successGlyph();
|
|
45
|
+
const msg = finding.message || finding.code || '';
|
|
46
|
+
logger.log(` ${glyph} ${msg}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {Array<{ datasourceKey?: string, verdict?: string, trustDecision?: string }>|undefined} datasourceRows
|
|
52
|
+
*/
|
|
53
|
+
function displayTrustDatasourceRows(datasourceRows) {
|
|
54
|
+
if (!Array.isArray(datasourceRows) || datasourceRows.length === 0) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
logger.log('');
|
|
58
|
+
logger.log(chalk.white('Datasources'));
|
|
59
|
+
logger.log(chalk.gray(SEP));
|
|
60
|
+
for (const row of datasourceRows) {
|
|
61
|
+
const glyph = row.verdict === VERDICT.VERIFIED ? successGlyph() : failureGlyph();
|
|
62
|
+
logger.log(` ${row.datasourceKey}`.padEnd(36) + `${glyph} ${row.trustDecision || '—'}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
displaySubMetricLine,
|
|
68
|
+
displayTrustFindings,
|
|
69
|
+
displayTrustDatasourceRows
|
|
70
|
+
};
|
|
@@ -34,6 +34,11 @@ const {
|
|
|
34
34
|
resolveNextStepCommand,
|
|
35
35
|
formatProgressBar
|
|
36
36
|
} = require('./certification-progress');
|
|
37
|
+
const {
|
|
38
|
+
displaySubMetricLine,
|
|
39
|
+
displayTrustFindings,
|
|
40
|
+
displayTrustDatasourceRows
|
|
41
|
+
} = require('./report-display-trust');
|
|
37
42
|
|
|
38
43
|
const SEP = '────────────────────────';
|
|
39
44
|
|
|
@@ -158,19 +163,15 @@ function displayVerifyOperationsTTY(systemKey, result, opts = {}) {
|
|
|
158
163
|
* @param {{ details?: boolean }} opts
|
|
159
164
|
*/
|
|
160
165
|
function displayVerifyTrustTTY(systemKey, result, opts = {}) {
|
|
161
|
-
displayPillarHeader(systemKey, 'AI TRUST');
|
|
162
|
-
displayHeroMetric('
|
|
166
|
+
displayPillarHeader(systemKey, 'AI TRUST REPORT');
|
|
167
|
+
displayHeroMetric('Overall', result.aiTrustPercent);
|
|
168
|
+
logger.log('');
|
|
169
|
+
displaySubMetricLine('Access Confidence', result.accessConfidencePercent);
|
|
170
|
+
displaySubMetricLine('Business Confidence', result.businessConfidencePercent);
|
|
163
171
|
displayVerdictLine(result.verdict, 'AI TRUST VERIFIED', 'AI TRUST FAILED');
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
logger.log(chalk.gray(SEP));
|
|
168
|
-
for (const row of result.datasourceRows) {
|
|
169
|
-
const conf =
|
|
170
|
-
typeof row.confidencePercent === 'number' ? `${row.confidencePercent}%` : '—';
|
|
171
|
-
const glyph = row.verdict === VERDICT.VERIFIED ? successGlyph() : failureGlyph();
|
|
172
|
-
logger.log(` ${row.datasourceKey}`.padEnd(36) + `${glyph} ${conf}`);
|
|
173
|
-
}
|
|
172
|
+
displayTrustFindings(result.findings);
|
|
173
|
+
if (opts.details === true) {
|
|
174
|
+
displayTrustDatasourceRows(result.datasourceRows);
|
|
174
175
|
}
|
|
175
176
|
}
|
|
176
177
|
|
|
@@ -267,7 +268,7 @@ function displayPillarScores(report) {
|
|
|
267
268
|
logger.log(
|
|
268
269
|
formatPillarScoreLine(
|
|
269
270
|
'AI Trust',
|
|
270
|
-
report.trust?.
|
|
271
|
+
report.trust?.aiTrustPercent,
|
|
271
272
|
report.trust?.verdict
|
|
272
273
|
)
|
|
273
274
|
);
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
|
-
const
|
|
13
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
14
14
|
const { resolveValidateInputPath, resolvePathFromIntegrationDatasourceKey } = require('../datasource/validate');
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -41,8 +41,7 @@ function tryResolveDatasourceKeyToLocalPath(datasourceKey) {
|
|
|
41
41
|
* @returns {any}
|
|
42
42
|
*/
|
|
43
43
|
function readJsonFile(jsonPath) {
|
|
44
|
-
|
|
45
|
-
return JSON.parse(raw);
|
|
44
|
+
return loadConfigFile(jsonPath);
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
module.exports = {
|