@aifabrix/builder 2.46.0 → 2.47.1
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 +18 -4
- package/lib/cli/infra-guided-auth.js +36 -12
- 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-npm.js +59 -0
- package/lib/cli/setup-platform.js +1 -1
- package/lib/cli/setup-secrets.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/login-device.js +11 -3
- package/lib/commands/login.js +8 -3
- package/lib/commands/npm-config.js +103 -0
- 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/api.js +2 -1
- 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/controller-url.js +4 -1
- 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/dev-platform-tls.js +148 -0
- 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/health-check-request.js +114 -0
- package/lib/utils/health-check-url.js +3 -1
- package/lib/utils/health-check.js +1 -113
- package/lib/utils/help-builder.js +3 -0
- package/lib/utils/keycloak-realm-ready.js +5 -1
- package/lib/utils/localhost-public-host.js +31 -0
- package/lib/utils/npm-user-config.js +145 -0
- package/lib/utils/paths.js +25 -0
- package/lib/utils/platform-controller-url.js +58 -4
- 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/url-declarative-public-base.js +33 -25
- 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,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 = {
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource type file helpers for `aifabrix resource-type create --file`.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Resource type file parsing
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const { existsSync, readFileSync } = require('../internal/fs-real-sync');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {Object} ResourceTypeCreateInput
|
|
16
|
+
* @property {string} key
|
|
17
|
+
* @property {string} displayName
|
|
18
|
+
* @property {string} [description]
|
|
19
|
+
* @property {string} [category]
|
|
20
|
+
* @property {string} [defaultRiskLevel]
|
|
21
|
+
* @property {string} [status]
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const CATEGORIES = new Set([
|
|
25
|
+
'business',
|
|
26
|
+
'collaboration',
|
|
27
|
+
'governance',
|
|
28
|
+
'content',
|
|
29
|
+
'communication',
|
|
30
|
+
'people'
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
const RISK_LEVELS = new Set(['low', 'medium', 'high', 'critical']);
|
|
34
|
+
const STATUSES = new Set(['draft', 'active', 'deprecated', 'retired']);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {string} filePath
|
|
38
|
+
* @returns {object}
|
|
39
|
+
*/
|
|
40
|
+
function loadResourceTypeJsonObject(filePath) {
|
|
41
|
+
const p = path.resolve(String(filePath || '').trim());
|
|
42
|
+
if (!p) {
|
|
43
|
+
throw new Error('--file is required');
|
|
44
|
+
}
|
|
45
|
+
if (!existsSync(p)) {
|
|
46
|
+
throw new Error(`File not found: ${p}`);
|
|
47
|
+
}
|
|
48
|
+
let parsed;
|
|
49
|
+
try {
|
|
50
|
+
parsed = JSON.parse(readFileSync(p, 'utf8'));
|
|
51
|
+
} catch (e) {
|
|
52
|
+
throw new Error(`Invalid JSON in ${p}: ${e.message}`);
|
|
53
|
+
}
|
|
54
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
55
|
+
throw new Error(`Resource type file must be a JSON object: ${p}`);
|
|
56
|
+
}
|
|
57
|
+
return parsed;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @param {object} parsed
|
|
62
|
+
* @param {string} field
|
|
63
|
+
* @param {Set<string>} allowedValues
|
|
64
|
+
* @param {string} label
|
|
65
|
+
* @returns {void}
|
|
66
|
+
*/
|
|
67
|
+
function applyOptionalEnumField(parsed, field, allowedValues, label) {
|
|
68
|
+
if (parsed[field] === undefined) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const value = String(parsed[field]).trim();
|
|
72
|
+
if (!allowedValues.has(value)) {
|
|
73
|
+
throw new Error(`${label} must be one of: ${[...allowedValues].join(', ')}`);
|
|
74
|
+
}
|
|
75
|
+
parsed[field] = value;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param {object} parsed
|
|
80
|
+
* @returns {void}
|
|
81
|
+
*/
|
|
82
|
+
function rejectBaselineCatalogShape(parsed) {
|
|
83
|
+
if (parsed && typeof parsed === 'object' && Array.isArray(parsed.resourceTypes) && !parsed.key) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
'File is a baseline catalog (resourceTypes[]). ' +
|
|
86
|
+
'Use `aifabrix resource-type import --file <path>` to seed all entries, ' +
|
|
87
|
+
'or pass a single resource type object with key and displayName.'
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @param {object} row
|
|
94
|
+
* @param {number} index
|
|
95
|
+
* @returns {ResourceTypeCreateInput}
|
|
96
|
+
*/
|
|
97
|
+
function normalizeResourceTypeCreateRow(row, index) {
|
|
98
|
+
if (!row || typeof row !== 'object') {
|
|
99
|
+
throw new Error(`resourceTypes[${index}] must be a JSON object`);
|
|
100
|
+
}
|
|
101
|
+
const entry = { ...row };
|
|
102
|
+
applyOptionalEnumField(entry, 'category', CATEGORIES, 'category');
|
|
103
|
+
applyOptionalEnumField(entry, 'defaultRiskLevel', RISK_LEVELS, 'defaultRiskLevel');
|
|
104
|
+
applyOptionalEnumField(entry, 'status', STATUSES, 'status');
|
|
105
|
+
const key = String(entry.key || '').trim();
|
|
106
|
+
if (!key) {
|
|
107
|
+
throw new Error(`resourceTypes[${index}].key is required`);
|
|
108
|
+
}
|
|
109
|
+
const displayName = String(entry.displayName || '').trim();
|
|
110
|
+
if (!displayName) {
|
|
111
|
+
throw new Error(`resourceTypes[${index}].displayName is required`);
|
|
112
|
+
}
|
|
113
|
+
entry.key = key;
|
|
114
|
+
entry.displayName = displayName;
|
|
115
|
+
return entry;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @param {string} filePath
|
|
120
|
+
* @returns {ResourceTypeCreateInput}
|
|
121
|
+
*/
|
|
122
|
+
function readResourceTypeCreateFile(filePath) {
|
|
123
|
+
const parsed = loadResourceTypeJsonObject(filePath);
|
|
124
|
+
rejectBaselineCatalogShape(parsed);
|
|
125
|
+
return normalizeResourceTypeCreateRow(parsed, 0);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Read baseline catalog or array file for `resource-type import`.
|
|
130
|
+
* Accepts `baseline-resourcetypes.json` shape or a top-level JSON array.
|
|
131
|
+
*
|
|
132
|
+
* @param {string} filePath
|
|
133
|
+
* @returns {ResourceTypeCreateInput[]}
|
|
134
|
+
*/
|
|
135
|
+
function readResourceTypeCatalogFile(filePath) {
|
|
136
|
+
const parsed = loadResourceTypeJsonObject(filePath);
|
|
137
|
+
let rows;
|
|
138
|
+
if (Array.isArray(parsed)) {
|
|
139
|
+
rows = parsed;
|
|
140
|
+
} else if (Array.isArray(parsed.resourceTypes)) {
|
|
141
|
+
rows = parsed.resourceTypes;
|
|
142
|
+
} else {
|
|
143
|
+
throw new Error(
|
|
144
|
+
'Import file must be a baseline catalog with resourceTypes[] or a JSON array of resource types.'
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
if (rows.length === 0) {
|
|
148
|
+
throw new Error('Import file contains no resource types');
|
|
149
|
+
}
|
|
150
|
+
return rows.map((row, index) => normalizeResourceTypeCreateRow(row, index));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
module.exports = {
|
|
154
|
+
readResourceTypeCreateFile,
|
|
155
|
+
readResourceTypeCatalogFile,
|
|
156
|
+
CATEGORIES,
|
|
157
|
+
RISK_LEVELS,
|
|
158
|
+
STATUSES
|
|
159
|
+
};
|
|
@@ -801,6 +801,22 @@
|
|
|
801
801
|
"type": "string",
|
|
802
802
|
"description": "When running with --reload, override the container command with this command (run from the mounted app root /app). Examples: TypeScript pnpm run reloadStart, Python make reloadStart."
|
|
803
803
|
},
|
|
804
|
+
"imageRun": {
|
|
805
|
+
"type": "string",
|
|
806
|
+
"description": "Compose command when using a pulled image without bind-mount (prepended with cd /app && exec in generated compose)."
|
|
807
|
+
},
|
|
808
|
+
"worker": {
|
|
809
|
+
"type": "boolean",
|
|
810
|
+
"description": "When true, local compose and Azure companion manifests may start additional containers from the same image. Non-empty build.imageRunWorker starts <appKey>-worker; non-empty build.imageRunBeat starts <appKey>-beat. Empty imageRunWorker or imageRunBeat skips that role."
|
|
811
|
+
},
|
|
812
|
+
"imageRunWorker": {
|
|
813
|
+
"type": "string",
|
|
814
|
+
"description": "Compose/Azure startup command for the worker sidecar (<appKey>-worker) when build.worker is true. Omit or set to empty string to skip the worker role."
|
|
815
|
+
},
|
|
816
|
+
"imageRunBeat": {
|
|
817
|
+
"type": "string",
|
|
818
|
+
"description": "Compose/Azure startup command for the beat sidecar (<appKey>-beat) when build.worker is true. Omit or set to empty string to skip the beat role."
|
|
819
|
+
},
|
|
804
820
|
"scripts": {
|
|
805
821
|
"type": "object",
|
|
806
822
|
"description": "Override CLI script commands (aifabrix test, install, lint, test-e2e, test-integration). When a key is missing, language default is used (TypeScript: pnpm test/install/lint/test:e2e/test:integration, Python: make test/install/lint/test:e2e/test-integration).",
|