@aifabrix/builder 2.51.1 → 2.52.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/jest.isolated-projects.js +25 -2
- package/jest.projects.js +4 -1
- package/lib/api/datasources-extended.api.js +8 -8
- package/lib/api/keycloak.api.js +92 -0
- package/lib/api/token-providers.api.js +77 -0
- package/lib/api/types/keycloak.types.js +23 -0
- package/lib/api/types/token-providers.types.js +65 -0
- package/lib/app/down.js +16 -1
- package/lib/app/run-container-start.js +4 -0
- package/lib/app/run-deploy-job.js +135 -27
- package/lib/app/run-env-compose.js +32 -0
- package/lib/app/run-env-recovery.js +35 -0
- package/lib/app/run-helpers.js +5 -6
- package/lib/app/run.js +11 -3
- package/lib/channels/add-channel.js +126 -19
- package/lib/channels/approval-guides/chatgpt.js +12 -9
- package/lib/channels/approval-guides/microsoft-copilot.js +16 -12
- package/lib/channels/channel-add-ensure.js +128 -0
- package/lib/channels/channel-artifact-generator.js +11 -1
- package/lib/channels/channel-branding.js +94 -0
- package/lib/channels/channel-credential.js +8 -0
- package/lib/channels/channel-discovery.js +330 -0
- package/lib/channels/channel-external-system.js +1 -1
- package/lib/channels/channel-runtime-resolver.js +103 -14
- package/lib/channels/channel-status-display.js +7 -1
- package/lib/channels/channel-targets.js +18 -1
- package/lib/channels/chatgpt-runtime-contract.js +1 -1
- package/lib/channels/microsoft-copilot-channel-add.js +138 -0
- package/lib/channels/microsoft-copilot-entra-sso-form.js +180 -0
- package/lib/channels/microsoft-copilot-entra-sso.js +264 -0
- package/lib/channels/microsoft-copilot-icons.js +74 -0
- package/lib/channels/microsoft-copilot-limits.js +84 -0
- package/lib/channels/microsoft-copilot-package.js +130 -49
- package/lib/channels/microsoft-copilot-validate.js +197 -0
- package/lib/cli/index.js +2 -0
- package/lib/cli/infra-guided-footers.js +1 -1
- package/lib/cli/infra-guided-public-url-warn.js +55 -0
- package/lib/cli/infra-guided.js +78 -62
- package/lib/cli/setup-app.help.js +4 -0
- package/lib/cli/setup-app.test-commands.js +59 -28
- package/lib/cli/setup-external-system.js +50 -12
- package/lib/cli/setup-infra.js +31 -4
- package/lib/commands/channel-add-setup.js +35 -10
- package/lib/commands/channel.js +27 -12
- package/lib/commands/deploy-gate-orchestrator.js +3 -2
- package/lib/commands/governance-verify-external.js +2 -5
- package/lib/commands/integration-definition.js +158 -0
- package/lib/commands/lifecycle-external.js +14 -7
- package/lib/commands/repair-auth-apply.js +19 -5
- package/lib/commands/repair-datasource-sync-operations.js +4 -1
- package/lib/commands/role-assistant-test-output.js +200 -0
- package/lib/commands/role-assistant.js +131 -114
- package/lib/commands/test-governance-external.js +2 -5
- package/lib/commands/up-miso.js +61 -24
- package/lib/commands/upload-data-sync-finalize.js +20 -3
- package/lib/commands/verify-governance-external.js +2 -5
- package/lib/commands/verify-operations-external.js +2 -5
- package/lib/commands/verify-operations-step-plan.js +31 -0
- package/lib/commands/verify-trust-command-action.js +2 -5
- package/lib/datasource/capability/capability-resolve.js +15 -5
- package/lib/datasource/capability/copy-operations.js +14 -4
- package/lib/datasource/datasource-identifier.js +4 -3
- package/lib/datasource/integration-context.js +3 -3
- package/lib/datasource/resolve-app.js +90 -8
- package/lib/datasource/test-e2e.js +2 -2
- package/lib/datasource/unified-validation-run-body.js +15 -1
- package/lib/datasource/unified-validation-run-resolve.js +2 -2
- package/lib/external-system/deploy-helpers.js +9 -5
- package/lib/external-system/deploy.js +2 -1
- package/lib/external-system/test-system-level.js +21 -3
- package/lib/external-system/test.js +4 -4
- package/lib/generator/builders.js +9 -15
- package/lib/infrastructure/index.js +19 -4
- package/lib/integration-definition/apply.js +478 -0
- package/lib/integration-definition/canonical-json.js +51 -0
- package/lib/integration-definition/emit-suite-bid.js +94 -0
- package/lib/integration-definition/enrich-crm-field-mappings.js +240 -0
- package/lib/integration-definition/enrich-openapi-operations.js +434 -0
- package/lib/integration-definition/from-discovery.js +339 -0
- package/lib/integration-definition/hash.js +37 -0
- package/lib/integration-definition/index.js +37 -0
- package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +205 -0
- package/lib/integration-definition/resource-type-catalog.js +59 -0
- package/lib/integration-definition/system-kind-enum.js +40 -0
- package/lib/integration-definition/validate.js +148 -0
- package/lib/programmatic/builder-api-request.js +5 -1
- package/lib/programmatic/builder-help-governance.js +3 -2
- package/lib/programmatic/capability-registry.js +19 -0
- package/lib/programmatic/openapi-component-schemas.js +6 -1
- package/lib/programmatic/openapi-descriptions.js +40 -0
- package/lib/programmatic/openapi-envelope-examples.js +269 -0
- package/lib/programmatic/openapi-operation-bindings.js +45 -0
- package/lib/programmatic/openapi-request-schemas.js +14 -0
- package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
- package/lib/programmatic/openapi-result-schemas.js +46 -99
- package/lib/programmatic/openapi-schema-descriptions-core.js +7 -4
- package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-operations.js +24 -0
- package/lib/programmatic/openapi-schema-helpers.js +9 -9
- package/lib/programmatic/openapi-spec.js +11 -1
- package/lib/programmatic/orchestration-openapi-meta.js +10 -0
- package/lib/programmatic/route-handler-map.js +29 -1
- package/lib/programmatic/run-channel-package.js +236 -0
- package/lib/programmatic/run-integration-definition.js +69 -0
- package/lib/role-assistant/knowledge-markdown-frontmatter.js +75 -0
- package/lib/role-assistant/knowledge-sync-logging.js +58 -0
- package/lib/role-assistant/knowledge-sync.js +104 -89
- package/lib/role-assistant/test-cases-bid.js +155 -0
- package/lib/role-assistant/test-cases-evaluate.js +9 -1
- package/lib/role-assistant/test-cases-honesty.js +24 -10
- package/lib/role-assistant/test-cases.js +28 -0
- package/lib/role-assistant/test-runner-bid-apply.js +122 -0
- package/lib/role-assistant/test-runner-bid-document.js +278 -0
- package/lib/role-assistant/test-runner-workhub-answers.js +289 -46
- package/lib/role-assistant/test-runner-workhub.js +121 -25
- package/lib/role-assistant/test-suite.js +119 -0
- package/lib/schema/application-schema.json +64 -32
- package/lib/schema/external-datasource.schema.json +103 -17
- package/lib/schema/external-system.schema.json +54 -3
- package/lib/schema/infra.parameter.yaml +0 -20
- package/lib/schema/integration-definition.schema.json +195 -0
- package/lib/utils/authentication-shape.js +6 -0
- package/lib/utils/certify-external-workspace.js +40 -0
- package/lib/utils/docker-build.js +3 -3
- package/lib/utils/docker-run-ephemeral.js +77 -17
- package/lib/utils/health-check-public-warn.js +31 -13
- package/lib/utils/health-check.js +11 -1
- package/lib/utils/help-builder.js +1 -0
- package/lib/utils/paths.js +13 -15
- package/lib/utils/promote-dev-docker-image.js +97 -18
- package/lib/utils/role-assistant-paths.js +76 -14
- package/lib/utils/transient-developer-id.js +28 -0
- package/lib/utils/upload-sync-options.js +7 -4
- package/lib/validation/reserved-system-key-prefixes.js +56 -0
- package/lib/validation/validate.js +54 -19
- package/package.json +1 -1
- package/scripts/pnpm/af.mjs +24 -9
- package/scripts/pnpm/help.mjs +1 -1
- package/templates/applications/builder-api/application.yaml +3 -3
- package/templates/applications/dataplane/application.yaml +5 -5
- package/templates/applications/dataplane/env.template +8 -7
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/application.yaml +2 -2
- package/templates/applications/keycloak/env.template +5 -2
- package/templates/applications/miso-controller/application.yaml +25 -11
- package/templates/applications/miso-controller/env.template +24 -5
- package/templates/channels/README.md +11 -1
- package/templates/channels/brand/color-192.png +0 -0
- package/templates/channels/brand/dark/128.png +0 -0
- package/templates/channels/brand/dark/192.png +0 -0
- package/templates/channels/brand/dark/256.png +0 -0
- package/templates/channels/brand/dark/32.png +0 -0
- package/templates/channels/brand/dark/512.png +0 -0
- package/templates/channels/brand/dark/64.png +0 -0
- package/templates/channels/brand/light/128.png +0 -0
- package/templates/channels/brand/light/192.png +0 -0
- package/templates/channels/brand/light/256.png +0 -0
- package/templates/channels/brand/light/32.png +0 -0
- package/templates/channels/brand/light/512.png +0 -0
- package/templates/channels/brand/light/64.png +0 -0
- package/templates/channels/brand/mono-white/128.png +0 -0
- package/templates/channels/brand/mono-white/192.png +0 -0
- package/templates/channels/brand/mono-white/256.png +0 -0
- package/templates/channels/brand/mono-white/32.png +0 -0
- package/templates/channels/brand/mono-white/512.png +0 -0
- package/templates/channels/brand/mono-white/64.png +0 -0
- package/templates/channels/brand/outline-32.png +0 -0
- package/templates/channels/chatgpt/install.template.md.hbs +76 -36
- package/templates/channels/chatgpt/integration/README.md +4 -4
- package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
- package/templates/channels/chatgpt/manifest.template.json.hbs +3 -3
- package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +10 -16
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
- package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +27 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +39 -20
- package/templates/channels/microsoft-copilot/integration/README.md +2 -3
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +1 -22
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +7 -7
- package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +11 -0
- package/templates/channels/slack/install.template.md.hbs +6 -1
- package/templates/channels/slack/integration/README.md +2 -4
- package/templates/channels/teams-bot/install.template.md.hbs +4 -0
- package/templates/channels/teams-bot/integration/README.md +3 -5
- package/templates/channels/web-embed/integration/web-embed-system.json +1 -16
- package/templates/python/docker-compose.hbs +10 -0
- package/templates/typescript/docker-compose.hbs +10 -0
- /package/.cursor/plans/{Done → Archive}/157.1-security-quality-violations-by-type.csv +0 -0
package/lib/commands/channel.js
CHANGED
|
@@ -16,29 +16,44 @@ const {
|
|
|
16
16
|
sectionTitle,
|
|
17
17
|
metadata
|
|
18
18
|
} = require('../utils/cli-test-layout-chalk');
|
|
19
|
-
const {
|
|
19
|
+
const {
|
|
20
|
+
formatSupportedChannelTargetsHelp,
|
|
21
|
+
isChannelTargetKey
|
|
22
|
+
} = require('../channels/channel-targets');
|
|
20
23
|
const { scaffoldChannelIntegration } = require('../channels/channel-integration-generator');
|
|
21
24
|
const { registerChannelAddCommand } = require('./channel-add-setup');
|
|
22
25
|
|
|
23
26
|
const CHANNEL_HELP_AFTER = `
|
|
24
27
|
Subcommands:
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
add <target> One command: scaffold (if needed) → upload (if needed/--force) → install
|
|
29
|
+
scaffold <target> Optional: create integration/<target>/ files only (local)
|
|
30
|
+
|
|
31
|
+
${formatSupportedChannelTargetsHelp()}
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
web-embed (experimental — scaffold with --allow-experimental)
|
|
33
|
+
Typical flow:
|
|
34
|
+
aifabrix channel add slack --name "Elsa" --probe
|
|
31
35
|
|
|
32
|
-
|
|
36
|
+
# Force re-upload of the communication system, then reinstall channel
|
|
37
|
+
aifabrix channel add slack --name "Elsa" --force --probe
|
|
38
|
+
|
|
39
|
+
Optional low-level steps (usually unnecessary):
|
|
33
40
|
aifabrix channel scaffold slack
|
|
34
41
|
aifabrix upload slack --force
|
|
35
|
-
aifabrix channel add slack --name "Elsa" --probe
|
|
36
42
|
|
|
37
43
|
Controller pipeline apps only: aifabrix app register · aifabrix app list
|
|
38
44
|
|
|
39
45
|
Requires: aifabrix login (for add)
|
|
40
46
|
`;
|
|
41
47
|
|
|
48
|
+
const CHANNEL_SCAFFOLD_HELP_AFTER = `
|
|
49
|
+
${formatSupportedChannelTargetsHelp()}
|
|
50
|
+
|
|
51
|
+
Prefer one command (scaffold + upload + install):
|
|
52
|
+
aifabrix channel add slack --name "Your Assistant" --probe
|
|
53
|
+
|
|
54
|
+
scaffold alone only writes local files under integration/<target>/.
|
|
55
|
+
`;
|
|
56
|
+
|
|
42
57
|
/**
|
|
43
58
|
* @param {import('commander').Command} program
|
|
44
59
|
*/
|
|
@@ -46,7 +61,7 @@ function setupChannelCommands(program) {
|
|
|
46
61
|
const channel = program
|
|
47
62
|
.command('channel')
|
|
48
63
|
.description(
|
|
49
|
-
'Assistant channels (
|
|
64
|
+
'Assistant channels — one-command add (scaffold → upload → install), plus optional scaffold'
|
|
50
65
|
)
|
|
51
66
|
.addHelpText('after', CHANNEL_HELP_AFTER);
|
|
52
67
|
|
|
@@ -57,11 +72,12 @@ function setupChannelCommands(program) {
|
|
|
57
72
|
)
|
|
58
73
|
.option('--force', 'Overwrite generated scaffold files when folder exists')
|
|
59
74
|
.option('--allow-experimental', 'Allow web-embed scaffold')
|
|
75
|
+
.addHelpText('after', CHANNEL_SCAFFOLD_HELP_AFTER)
|
|
60
76
|
.action(async(channelTarget, options) => {
|
|
61
77
|
try {
|
|
62
78
|
if (!isChannelTargetKey(channelTarget)) {
|
|
63
79
|
throw new Error(
|
|
64
|
-
`Unknown channel target "${channelTarget}".
|
|
80
|
+
`Unknown channel target "${channelTarget}". ${formatSupportedChannelTargetsHelp()}`
|
|
65
81
|
);
|
|
66
82
|
}
|
|
67
83
|
const result = await scaffoldChannelIntegration(channelTarget, {
|
|
@@ -75,8 +91,7 @@ function setupChannelCommands(program) {
|
|
|
75
91
|
logger.log(metadata(file));
|
|
76
92
|
}
|
|
77
93
|
logger.log('');
|
|
78
|
-
logger.log(chalk.white('Next
|
|
79
|
-
logger.log(chalk.gray(` aifabrix upload ${channelTarget} --force`));
|
|
94
|
+
logger.log(chalk.white('Next step (recommended):'));
|
|
80
95
|
logger.log(
|
|
81
96
|
chalk.gray(` aifabrix channel add ${channelTarget} --name "Your Assistant" --probe`)
|
|
82
97
|
);
|
|
@@ -142,7 +142,6 @@ async function runDeployBaselineSteps(systemKey, options) {
|
|
|
142
142
|
uploadStatus: 'published',
|
|
143
143
|
minimal: true,
|
|
144
144
|
suppressSummary: true,
|
|
145
|
-
suppressPipelineSpinner: true,
|
|
146
145
|
uploadContext: 'deploy-preflight'
|
|
147
146
|
});
|
|
148
147
|
stepOptions = mergeUploadAuthOptions({ ...options, uploadStatus: 'published' }, uploadCtx);
|
|
@@ -322,7 +321,9 @@ function inferGatePillarFromLabel(label) {
|
|
|
322
321
|
* @returns {Promise<{ baseline: { unitPassed: boolean, integrationPassed: boolean }, gates: Object[] }>}
|
|
323
322
|
*/
|
|
324
323
|
async function runDeployGateOrchestrator(systemKey, options = {}) {
|
|
325
|
-
|
|
324
|
+
// Default: show unit/integration/publish progress (parity with upload).
|
|
325
|
+
// Only hush when caller opts in (json / explicit quietDeployUi).
|
|
326
|
+
const quiet = options.quietDeployUi === true;
|
|
326
327
|
if (!quiet) {
|
|
327
328
|
logger.log('');
|
|
328
329
|
logger.log(sectionTitle('Deploy preflight'));
|
|
@@ -20,12 +20,9 @@ const { VERDICT, verdictFromPassed } = require('../lifecycle/product-model');
|
|
|
20
20
|
*/
|
|
21
21
|
async function runGovernanceVerifyForExternalSystem(systemKey, options = {}) {
|
|
22
22
|
const pathsUtil = require('../utils/paths');
|
|
23
|
+
const { assertCertifyExternalWorkspace } = require('../utils/certify-external-workspace');
|
|
23
24
|
const appType = await pathsUtil.detectAppType(systemKey).catch(() => null);
|
|
24
|
-
|
|
25
|
-
throw new Error(
|
|
26
|
-
'governance verify applies to external integration folders only (integration/<systemKey>/).'
|
|
27
|
-
);
|
|
28
|
-
}
|
|
25
|
+
assertCertifyExternalWorkspace(appType && appType.baseDir, 'governance verify');
|
|
29
26
|
|
|
30
27
|
if (options.pack) {
|
|
31
28
|
const { runTestGovernanceForExternalSystem } = require('./test-governance-external');
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI: integration-definition validate | apply
|
|
3
|
+
* @fileoverview BT → Builder BID commands (no section PATCH orchestration)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
/* eslint-disable no-console */
|
|
9
|
+
|
|
10
|
+
const chalk = require('chalk');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const {
|
|
14
|
+
applyIntegrationDefinition,
|
|
15
|
+
validateIntegrationDefinition,
|
|
16
|
+
withDefinitionHash
|
|
17
|
+
} = require('../integration-definition');
|
|
18
|
+
const { handleCommandError } = require('../utils/cli-utils');
|
|
19
|
+
const {
|
|
20
|
+
formatSuccessLine,
|
|
21
|
+
formatBlockingError,
|
|
22
|
+
failureGlyph
|
|
23
|
+
} = require('../utils/cli-layout-chalk');
|
|
24
|
+
|
|
25
|
+
const PARENT_HELP_AFTER = `
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
$ aifabrix integration-definition validate ./bid.json
|
|
29
|
+
$ aifabrix integration-definition apply ./bid.json --dry-run
|
|
30
|
+
|
|
31
|
+
BID is the minimal business create language (systemKind, resourceType, propertyKeys).
|
|
32
|
+
Apply materializes integration/<systemKey>/ package files (EDS/system).
|
|
33
|
+
AI catalog: GET /help?topic=resourceTypeCatalog on builder-api.
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
const VALIDATE_HELP_AFTER = `
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
$ aifabrix integration-definition validate ./bid.json
|
|
40
|
+
$ aifabrix integration-definition validate ./bid.json --fix-hash
|
|
41
|
+
|
|
42
|
+
Checks JSON Schema, definitionHash, product rules, and active resourceType catalog keys.
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
const APPLY_HELP_AFTER = `
|
|
46
|
+
|
|
47
|
+
Examples:
|
|
48
|
+
$ aifabrix integration-definition apply ./bid.json
|
|
49
|
+
$ aifabrix integration-definition apply ./bid.json --dry-run
|
|
50
|
+
$ aifabrix integration-definition apply ./bid.json --force
|
|
51
|
+
$ aifabrix integration-definition apply ./bid.json --output-dir /tmp/out
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {string} filePath
|
|
56
|
+
* @returns {Record<string, unknown>}
|
|
57
|
+
*/
|
|
58
|
+
function loadBidFile(filePath) {
|
|
59
|
+
const abs = path.resolve(filePath);
|
|
60
|
+
if (!fs.existsSync(abs)) {
|
|
61
|
+
throw new Error(`BID file not found: ${abs}`);
|
|
62
|
+
}
|
|
63
|
+
return JSON.parse(fs.readFileSync(abs, 'utf8'));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param {string} file
|
|
68
|
+
* @param {{ fixHash?: boolean }} options
|
|
69
|
+
* @returns {Promise<void>}
|
|
70
|
+
*/
|
|
71
|
+
async function runValidateCommand(file, options) {
|
|
72
|
+
let bid = loadBidFile(file);
|
|
73
|
+
if (options.fixHash) {
|
|
74
|
+
bid = withDefinitionHash(bid);
|
|
75
|
+
fs.writeFileSync(path.resolve(file), `${JSON.stringify(bid, null, 2)}\n`, 'utf8');
|
|
76
|
+
console.log(formatSuccessLine(`Wrote definitionHash to ${file}`));
|
|
77
|
+
}
|
|
78
|
+
const result = validateIntegrationDefinition(bid);
|
|
79
|
+
if (!result.valid) {
|
|
80
|
+
console.error(formatBlockingError('BID invalid'));
|
|
81
|
+
for (const err of result.errors) {
|
|
82
|
+
console.error(chalk.red(` ${failureGlyph()} ${err}`));
|
|
83
|
+
}
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
console.log(formatSuccessLine('BID valid'));
|
|
87
|
+
console.log(` definitionId: ${result.bid.definitionId}`);
|
|
88
|
+
console.log(` definitionHash: ${result.bid.definitionHash}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @param {string} file
|
|
93
|
+
* @param {{ outputDir?: string, dryRun?: boolean, force?: boolean }} options
|
|
94
|
+
* @returns {Promise<void>}
|
|
95
|
+
*/
|
|
96
|
+
async function runApplyCommand(file, options) {
|
|
97
|
+
const bid = loadBidFile(file);
|
|
98
|
+
const out = await applyIntegrationDefinition(bid, {
|
|
99
|
+
outputDir: options.outputDir,
|
|
100
|
+
dryRun: Boolean(options.dryRun),
|
|
101
|
+
force: Boolean(options.force)
|
|
102
|
+
});
|
|
103
|
+
console.log(formatSuccessLine(`BID apply ${out.status}`));
|
|
104
|
+
console.log(` systemKey: ${out.systemKey}`);
|
|
105
|
+
console.log(` outputDir: ${out.outputDir}`);
|
|
106
|
+
console.log(` definitionHash: ${out.definitionHash}`);
|
|
107
|
+
if (out.files) {
|
|
108
|
+
console.log(` files: ${out.files.join(', ')}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @param {import('commander').Command} program
|
|
114
|
+
*/
|
|
115
|
+
function setupIntegrationDefinitionCommands(program) {
|
|
116
|
+
const cmd = program
|
|
117
|
+
.command('integration-definition')
|
|
118
|
+
.description(
|
|
119
|
+
'Validate or apply a Builder Integration Definition (BID) — minimal business create language for integration packages'
|
|
120
|
+
)
|
|
121
|
+
.addHelpText('after', PARENT_HELP_AFTER);
|
|
122
|
+
|
|
123
|
+
cmd
|
|
124
|
+
.command('validate <file>')
|
|
125
|
+
.description(
|
|
126
|
+
'Validate BID JSON (schema, definitionHash, product rules, resourceType catalog)'
|
|
127
|
+
)
|
|
128
|
+
.option('--fix-hash', 'Rewrite file with computed definitionHash')
|
|
129
|
+
.addHelpText('after', VALIDATE_HELP_AFTER)
|
|
130
|
+
.action(async(file, options) => {
|
|
131
|
+
try {
|
|
132
|
+
await runValidateCommand(file, options);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
handleCommandError(error, 'integration-definition validate');
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
cmd
|
|
139
|
+
.command('apply <file>')
|
|
140
|
+
.description(
|
|
141
|
+
'Idempotently apply BID to integration/<systemKey>/ (Builder-owned EDS/system writes)'
|
|
142
|
+
)
|
|
143
|
+
.option('--output-dir <dir>', 'Override output directory')
|
|
144
|
+
.option('--force', 'Rewrite package even when definitionHash matches stamp')
|
|
145
|
+
.option('--dry-run', 'Show planned files without writing')
|
|
146
|
+
.addHelpText('after', APPLY_HELP_AFTER)
|
|
147
|
+
.action(async(file, options) => {
|
|
148
|
+
try {
|
|
149
|
+
await runApplyCommand(file, options);
|
|
150
|
+
} catch (error) {
|
|
151
|
+
handleCommandError(error, 'integration-definition apply');
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
module.exports = {
|
|
157
|
+
setupIntegrationDefinitionCommands
|
|
158
|
+
};
|
|
@@ -247,23 +247,30 @@ async function executeLifecycleWork(systemKey, options = {}) {
|
|
|
247
247
|
*/
|
|
248
248
|
async function runLifecycleForExternalSystem(systemKey, options = {}) {
|
|
249
249
|
const pathsUtil = require('../utils/paths');
|
|
250
|
+
const { assertCertifyExternalWorkspace } = require('../utils/certify-external-workspace');
|
|
250
251
|
const appType = await pathsUtil.detectAppType(systemKey).catch(() => null);
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
);
|
|
252
|
+
assertCertifyExternalWorkspace(appType && appType.baseDir, 'lifecycle');
|
|
253
|
+
|
|
254
|
+
let resolvedKey = systemKey;
|
|
255
|
+
if (appType.baseDir === 'role-assistant') {
|
|
256
|
+
const { resolveExternalIntegrationContext } = require('./test-e2e-external');
|
|
257
|
+
const { roleAssistantSystemKey } = require('../utils/role-assistant-paths');
|
|
258
|
+
try {
|
|
259
|
+
resolvedKey = resolveExternalIntegrationContext(systemKey).systemKey;
|
|
260
|
+
} catch {
|
|
261
|
+
resolvedKey = roleAssistantSystemKey(systemKey);
|
|
262
|
+
}
|
|
256
263
|
}
|
|
257
264
|
|
|
258
265
|
if (options.run === true) {
|
|
259
266
|
return runWithVerifyStepProgress(
|
|
260
267
|
'Processing',
|
|
261
|
-
async(progress) => executeLifecycleWork(
|
|
268
|
+
async(progress) => executeLifecycleWork(resolvedKey, { ...options, lifecycleProgress: progress }),
|
|
262
269
|
{ json: options.json === true }
|
|
263
270
|
);
|
|
264
271
|
}
|
|
265
272
|
|
|
266
|
-
return executeLifecycleWork(
|
|
273
|
+
return executeLifecycleWork(resolvedKey, options);
|
|
267
274
|
}
|
|
268
275
|
|
|
269
276
|
module.exports = {
|
|
@@ -28,6 +28,7 @@ const ALLOWED_REPAIR_AUTH = [
|
|
|
28
28
|
'oidc',
|
|
29
29
|
'hmac',
|
|
30
30
|
'none',
|
|
31
|
+
'system',
|
|
31
32
|
'bearerforward'
|
|
32
33
|
];
|
|
33
34
|
|
|
@@ -57,8 +58,12 @@ function isAllowedRepairAuth(authOption) {
|
|
|
57
58
|
*/
|
|
58
59
|
function buildAuthenticationForRepair(systemKey, authOption) {
|
|
59
60
|
const normalized = normalizeRepairAuthOption(authOption);
|
|
61
|
+
if (normalized === 'system') {
|
|
62
|
+
return buildSystemAuthentication();
|
|
63
|
+
}
|
|
60
64
|
if (normalized === 'bearerforward') {
|
|
61
|
-
|
|
65
|
+
// Legacy alias: prefer authType system for platform facades.
|
|
66
|
+
return buildSystemAuthentication();
|
|
62
67
|
}
|
|
63
68
|
const schemaMethod =
|
|
64
69
|
normalized === 'queryparam'
|
|
@@ -70,11 +75,19 @@ function buildAuthenticationForRepair(systemKey, authOption) {
|
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
/**
|
|
73
|
-
*
|
|
74
|
-
* @returns {{ authType: '
|
|
78
|
+
* Platform-managed downstream authentication (no workspace kv secret).
|
|
79
|
+
* @returns {{ authType: 'system' }}
|
|
80
|
+
*/
|
|
81
|
+
function buildSystemAuthentication() {
|
|
82
|
+
return { authType: 'system' };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @deprecated Use buildSystemAuthentication — bearerForward is not a public authType.
|
|
87
|
+
* @returns {{ authType: 'system' }}
|
|
75
88
|
*/
|
|
76
89
|
function buildBearerForwardAuthentication() {
|
|
77
|
-
return
|
|
90
|
+
return buildSystemAuthentication();
|
|
78
91
|
}
|
|
79
92
|
|
|
80
93
|
/**
|
|
@@ -99,7 +112,7 @@ function mergePreservedAuthVariables(mergedVariables, existingVars) {
|
|
|
99
112
|
function repairAuthChangeLabel(authOption) {
|
|
100
113
|
const normalized = normalizeRepairAuthOption(authOption);
|
|
101
114
|
if (normalized === 'bearertoken' || normalized === 'bearerkey') return 'bearerToken';
|
|
102
|
-
if (normalized === 'bearerforward') return '
|
|
115
|
+
if (normalized === 'bearerforward') return 'system';
|
|
103
116
|
return normalized;
|
|
104
117
|
}
|
|
105
118
|
|
|
@@ -157,6 +170,7 @@ module.exports = {
|
|
|
157
170
|
normalizeRepairAuthOption,
|
|
158
171
|
isAllowedRepairAuth,
|
|
159
172
|
buildAuthenticationForRepair,
|
|
173
|
+
buildSystemAuthentication,
|
|
160
174
|
buildBearerForwardAuthentication,
|
|
161
175
|
mergePreservedAuthVariables,
|
|
162
176
|
repairAuthChangeLabel,
|
|
@@ -108,7 +108,10 @@ function ensureSyncOperationScaffold(parsed) {
|
|
|
108
108
|
parsed.execution = { engine: 'cip' };
|
|
109
109
|
}
|
|
110
110
|
if (!parsed.execution.cip || typeof parsed.execution.cip !== 'object') {
|
|
111
|
-
parsed.execution.cip = { operations: {} };
|
|
111
|
+
parsed.execution.cip = { version: '1.0', operations: {} };
|
|
112
|
+
}
|
|
113
|
+
if (!parsed.execution.cip.version) {
|
|
114
|
+
parsed.execution.cip.version = '1.0';
|
|
112
115
|
}
|
|
113
116
|
if (!parsed.execution.cip.operations || typeof parsed.execution.cip.operations !== 'object') {
|
|
114
117
|
parsed.execution.cip.operations = {};
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role Assistant test CLI envelope + human summary output.
|
|
3
|
+
* @fileoverview Split from role-assistant.js for max-lines / complexity
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const logger = require('../utils/logger');
|
|
9
|
+
const { VERDICT } = require('../lifecycle/product-model');
|
|
10
|
+
const {
|
|
11
|
+
sectionTitle,
|
|
12
|
+
headerKeyValue,
|
|
13
|
+
formatSuccessLine,
|
|
14
|
+
formatSuccessParagraph,
|
|
15
|
+
formatWarningLine,
|
|
16
|
+
formatBlockingError,
|
|
17
|
+
metadata
|
|
18
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Bucket a lifecycle step status into ok | failed | skipped | other.
|
|
22
|
+
* @param {string} status
|
|
23
|
+
* @returns {'ok'|'failed'|'skipped'|'other'}
|
|
24
|
+
*/
|
|
25
|
+
function bucketStepStatus(status) {
|
|
26
|
+
const normalized = String(status || 'ok').toLowerCase();
|
|
27
|
+
if (normalized === 'ok' || normalized === 'verified' || normalized === 'passed') {
|
|
28
|
+
return 'ok';
|
|
29
|
+
}
|
|
30
|
+
if (normalized === 'failed' || normalized === 'error') {
|
|
31
|
+
return 'failed';
|
|
32
|
+
}
|
|
33
|
+
if (normalized === 'skipped' || normalized === 'n/a' || normalized === 'not_applicable') {
|
|
34
|
+
return 'skipped';
|
|
35
|
+
}
|
|
36
|
+
return 'other';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Summarize lifecycle stepsRun rows (API uses step/status/datasourceKey, not pillar).
|
|
41
|
+
* @param {Array<Object|string>} steps
|
|
42
|
+
* @returns {string}
|
|
43
|
+
*/
|
|
44
|
+
function formatStepsRunSummary(steps) {
|
|
45
|
+
const byStep = Object.create(null);
|
|
46
|
+
for (const row of steps) {
|
|
47
|
+
if (!row || typeof row !== 'object') {
|
|
48
|
+
const label = String(row);
|
|
49
|
+
byStep[label] = byStep[label] || { ok: 0, failed: 0, skipped: 0, other: 0 };
|
|
50
|
+
byStep[label].other += 1;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const stepName = String(row.step || row.pillar || row.name || 'step').trim() || 'step';
|
|
54
|
+
const status = row.status || (row.success === false ? 'failed' : 'ok');
|
|
55
|
+
byStep[stepName] = byStep[stepName] || { ok: 0, failed: 0, skipped: 0, other: 0 };
|
|
56
|
+
byStep[stepName][bucketStepStatus(status)] += 1;
|
|
57
|
+
}
|
|
58
|
+
return Object.keys(byStep)
|
|
59
|
+
.map((stepName) => {
|
|
60
|
+
const counts = byStep[stepName];
|
|
61
|
+
const parts = [];
|
|
62
|
+
if (counts.ok) parts.push(`${counts.ok} ok`);
|
|
63
|
+
if (counts.failed) parts.push(`${counts.failed} failed`);
|
|
64
|
+
if (counts.skipped) parts.push(`${counts.skipped} skipped`);
|
|
65
|
+
if (counts.other) parts.push(`${counts.other} other`);
|
|
66
|
+
return parts.length > 0 ? `${stepName} (${parts.join(', ')})` : stepName;
|
|
67
|
+
})
|
|
68
|
+
.join('; ');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function mapCaseEnvelopeRow(row) {
|
|
72
|
+
return {
|
|
73
|
+
caseId: row.caseId,
|
|
74
|
+
sourceFile: row.sourceFile,
|
|
75
|
+
passed: row.passed,
|
|
76
|
+
lifecycleState: row.lifecycleState,
|
|
77
|
+
assertions: row.assertions,
|
|
78
|
+
failedAssertions: row.failedAssertions,
|
|
79
|
+
warnings: Array.isArray(row.warnings) ? row.warnings : [],
|
|
80
|
+
execution: row.execution || null
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function attachEnvelopeCases(envelope, caseResult) {
|
|
85
|
+
if (Array.isArray(caseResult)) {
|
|
86
|
+
envelope.cases = caseResult.map(mapCaseEnvelopeRow);
|
|
87
|
+
const passed = envelope.cases.filter((c) => c.passed).length;
|
|
88
|
+
envelope.summary = {
|
|
89
|
+
total: envelope.cases.length,
|
|
90
|
+
passed,
|
|
91
|
+
failed: envelope.cases.length - passed
|
|
92
|
+
};
|
|
93
|
+
envelope.case = envelope.cases.length === 1 ? envelope.cases[0] : null;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (caseResult) {
|
|
97
|
+
envelope.case = mapCaseEnvelopeRow(caseResult);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function buildRoleAssistantTestEnvelope(roleKey, verdict, portalUrl, result, opts, caseResult = null) {
|
|
102
|
+
const report = result.report || {};
|
|
103
|
+
const multiCase = Array.isArray(caseResult) && caseResult.length > 1;
|
|
104
|
+
const envelope = {
|
|
105
|
+
roleKey,
|
|
106
|
+
command: 'role-assistant test',
|
|
107
|
+
mode: opts.suite || multiCase ? 'suite' : 'single',
|
|
108
|
+
suite: opts.suite || null,
|
|
109
|
+
verdict,
|
|
110
|
+
portalUrl,
|
|
111
|
+
openApiPublished: result.openApiPublished === true,
|
|
112
|
+
certification: report.certification || null,
|
|
113
|
+
recommendations: Array.isArray(report.recommendations) ? report.recommendations : [],
|
|
114
|
+
warnings: Array.isArray(report.warnings) ? report.warnings : [],
|
|
115
|
+
stepsRun: result.stepsRun || [],
|
|
116
|
+
case: null,
|
|
117
|
+
cases: null,
|
|
118
|
+
summary: null
|
|
119
|
+
};
|
|
120
|
+
attachEnvelopeCases(envelope, caseResult);
|
|
121
|
+
if (opts.verbose || opts.debug) {
|
|
122
|
+
envelope.report = report;
|
|
123
|
+
}
|
|
124
|
+
if (opts.debug) {
|
|
125
|
+
envelope.debug = {
|
|
126
|
+
observation: caseResult && !Array.isArray(caseResult) ? caseResult.observation : null,
|
|
127
|
+
lifecycleResult: result
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
return envelope;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function displayCaseResultSummary(caseResult) {
|
|
134
|
+
if (!caseResult) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
logger.log(headerKeyValue('Case:', caseResult.caseId));
|
|
138
|
+
logger.log(headerKeyValue('Case file:', caseResult.sourceFile));
|
|
139
|
+
logger.log(headerKeyValue('Observed state:', caseResult.lifecycleState));
|
|
140
|
+
if (caseResult.execution) {
|
|
141
|
+
logger.log(headerKeyValue('Execution id:', caseResult.execution.processExecutionId || 'n/a'));
|
|
142
|
+
logger.log(headerKeyValue('Final runtime status:', caseResult.execution.finalStatus || 'n/a'));
|
|
143
|
+
}
|
|
144
|
+
if (caseResult.passed) {
|
|
145
|
+
logger.log(formatSuccessLine('Case assertions passed.'));
|
|
146
|
+
const warnings = Array.isArray(caseResult.warnings) ? caseResult.warnings : [];
|
|
147
|
+
for (const row of warnings) {
|
|
148
|
+
logger.log(formatWarningLine(row.message || String(row)));
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
logger.log(formatBlockingError('Case assertions failed.'));
|
|
153
|
+
for (const row of caseResult.failedAssertions) {
|
|
154
|
+
logger.log(formatWarningLine(row.message));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function displayPortalAndVerdict(portalUrl, verdict) {
|
|
159
|
+
if (portalUrl) {
|
|
160
|
+
logger.log(headerKeyValue('Portal:', portalUrl));
|
|
161
|
+
} else {
|
|
162
|
+
logger.log(formatWarningLine('Portal link unavailable. Configure dataplane public URL and retry.'));
|
|
163
|
+
}
|
|
164
|
+
const verdictMessage =
|
|
165
|
+
verdict === VERDICT.VERIFIED
|
|
166
|
+
? 'Lifecycle checks passed. Open portal for full execution steps and logs.'
|
|
167
|
+
: 'Lifecycle checks are not fully verified. Open portal to inspect blocking steps.';
|
|
168
|
+
if (verdict === VERDICT.VERIFIED) {
|
|
169
|
+
logger.log(formatSuccessParagraph(verdictMessage));
|
|
170
|
+
} else {
|
|
171
|
+
logger.log(formatWarningLine(verdictMessage));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function displayRoleAssistantTestSummary(roleKey, verdict, portalUrl, result, caseResult = null) {
|
|
176
|
+
logger.log('');
|
|
177
|
+
logger.log(sectionTitle(`${String(roleKey).toUpperCase()} ROLE ASSISTANT TEST`));
|
|
178
|
+
logger.log('');
|
|
179
|
+
logger.log(headerKeyValue('Role key:', roleKey));
|
|
180
|
+
displayPortalAndVerdict(portalUrl, verdict);
|
|
181
|
+
const steps = Array.isArray(result.stepsRun) ? result.stepsRun : [];
|
|
182
|
+
if (steps.length > 0) {
|
|
183
|
+
logger.log(metadata(`Steps run: ${formatStepsRunSummary(steps)}`));
|
|
184
|
+
}
|
|
185
|
+
if (Array.isArray(caseResult)) {
|
|
186
|
+
for (const row of caseResult) {
|
|
187
|
+
displayCaseResultSummary(row);
|
|
188
|
+
}
|
|
189
|
+
const passed = caseResult.filter((c) => c.passed).length;
|
|
190
|
+
logger.log(headerKeyValue('Suite summary:', `${passed}/${caseResult.length} passed`));
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
displayCaseResultSummary(caseResult);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
module.exports = {
|
|
197
|
+
formatStepsRunSummary,
|
|
198
|
+
buildRoleAssistantTestEnvelope,
|
|
199
|
+
displayRoleAssistantTestSummary
|
|
200
|
+
};
|