@aifabrix/builder 2.54.1 → 2.55.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/README.md +8 -0
- package/lib/agent-kit/apply-ops.js +82 -0
- package/lib/agent-kit/checksums.js +28 -0
- package/lib/agent-kit/classify.js +113 -0
- package/lib/agent-kit/claude-shim.js +61 -0
- package/lib/agent-kit/constants.js +55 -0
- package/lib/agent-kit/file-list.js +103 -0
- package/lib/agent-kit/host-approve.js +130 -0
- package/lib/agent-kit/hosting/github.js +63 -0
- package/lib/agent-kit/hosts.js +71 -0
- package/lib/agent-kit/init.js +319 -0
- package/lib/agent-kit/kit-root.js +17 -0
- package/lib/agent-kit/managed-section.js +79 -0
- package/lib/agent-kit/package-load.js +45 -0
- package/lib/agent-kit/plan-ops.js +69 -0
- package/lib/agent-kit/remote-prompts.js +239 -0
- package/lib/agent-kit/render-host-markdown.js +40 -0
- package/lib/agent-kit/repo-guard.js +47 -0
- package/lib/agent-kit/report.js +107 -0
- package/lib/agent-kit/run.js +82 -0
- package/lib/agent-kit/scm/git.js +122 -0
- package/lib/agent-kit/state-file.js +83 -0
- package/lib/agent-kit/status.js +50 -0
- package/lib/agent-kit/v1-detect.js +81 -0
- package/lib/agent-kit/validate-kit.js +87 -0
- package/lib/agent-kit/workspace-name.js +40 -0
- package/lib/agent-kit/workspace-skeleton.js +103 -0
- package/lib/api/role-assistant-test-pack.api.js +86 -0
- package/lib/api/work-search.api.js +31 -4
- package/lib/api/work.api.js +19 -12
- package/lib/app/list-external-systems.js +2 -1
- package/lib/app/show-display.js +33 -6
- package/lib/app/show.js +25 -2
- package/lib/channels/add-channel.js +1 -0
- package/lib/channels/approval-guides/claude.js +2 -2
- package/lib/channels/channel-add-failure.js +1 -0
- package/lib/channels/channel-add-remaining.js +15 -9
- package/lib/channels/channel-definition-status.js +115 -0
- package/lib/channels/channel-description.js +2 -1
- package/lib/channels/channel-install-envelope.js +4 -2
- package/lib/channels/channel-integration-generator.js +2 -0
- package/lib/channels/channel-runtime-resolver.js +14 -0
- package/lib/channels/channel-targets.js +31 -5
- package/lib/channels/claude-setup-values.js +4 -1
- package/lib/channels/slack-channel-add.js +59 -6
- package/lib/channels/slack-mcp-callback.js +51 -0
- package/lib/channels/slack-mcp-runtime.js +156 -0
- package/lib/channels/slack-provisioning/install-cli.js +14 -2
- package/lib/channels/slack-provisioning/with-curl-on-path.js +6 -1
- package/lib/cli/index.js +4 -0
- package/lib/cli/setup-certification-commands.js +2 -2
- package/lib/cli/setup-certification.help.js +3 -1
- package/lib/commands/agent-kit-help.js +79 -0
- package/lib/commands/agent-kit.js +346 -0
- package/lib/commands/channel-add-help.js +5 -5
- package/lib/commands/datasource-capability-relate-cli.js +5 -4
- package/lib/commands/repair-auth-apply.js +1 -1
- package/lib/commands/repair-datasource-key-skip.js +39 -0
- package/lib/commands/repair-datasource-keys.js +24 -12
- package/lib/commands/repair-rbac.js +16 -10
- package/lib/commands/role-assistant-copy-output.js +61 -0
- package/lib/commands/role-assistant-copy.js +155 -0
- package/lib/commands/role-assistant-test-output.js +8 -9
- package/lib/commands/role-assistant-test-result-rows.js +42 -0
- package/lib/commands/role-assistant.js +117 -48
- package/lib/commands/search.js +143 -0
- package/lib/commands/test-trust-external.js +7 -5
- package/lib/commands/upload-sidecar-followups.js +4 -1
- package/lib/commands/verify-operations-skip-e2e.js +131 -0
- package/lib/commands/verify-operations-step-plan.js +6 -33
- package/lib/commands/verify-operations-steps.js +3 -1
- package/lib/datasource/capability/relate-operations.js +28 -10
- package/lib/datasource/capability/run-capability-relate.js +48 -25
- package/lib/datasource/datasource-identifier.js +13 -5
- package/lib/deployment/deployer-poll-run.js +117 -35
- package/lib/external-system/deploy-dataplane-published.js +70 -0
- package/lib/external-system/deploy.js +12 -2
- package/lib/external-system/download-role-assistant.js +41 -15
- package/lib/external-system/download.js +7 -1
- package/lib/generator/wizard-datasource-key.js +54 -0
- package/lib/generator/wizard.js +55 -54
- package/lib/integration-definition/apply-datasource-keys.js +75 -0
- package/lib/integration-definition/apply-relationships.js +195 -0
- package/lib/integration-definition/apply-static-enterprise.js +229 -0
- package/lib/integration-definition/apply-system-file.js +71 -0
- package/lib/integration-definition/apply-test-endpoint.js +89 -0
- package/lib/integration-definition/apply-viewpoint-defaults.js +157 -0
- package/lib/integration-definition/apply.js +78 -59
- package/lib/integration-definition/enrich-openapi-operations.js +36 -10
- package/lib/integration-definition/enrich-openapi-pick.js +87 -0
- package/lib/lifecycle/role-assistant-governance.js +56 -21
- package/lib/programmatic/capability-registry.js +1 -0
- package/lib/programmatic/index.js +2 -0
- package/lib/programmatic/openapi-apply-result-schemas.js +8 -0
- package/lib/programmatic/openapi-channel-install-schemas.js +4 -1
- package/lib/programmatic/openapi-component-schemas.js +1 -0
- package/lib/programmatic/openapi-descriptions.js +9 -3
- package/lib/programmatic/openapi-envelope-examples.js +7 -0
- package/lib/programmatic/openapi-operation-bindings.js +5 -0
- package/lib/programmatic/openapi-request-schemas.js +5 -0
- package/lib/programmatic/openapi-schema-descriptions-channel.js +8 -5
- package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
- package/lib/programmatic/openapi-spec.js +1 -0
- package/lib/programmatic/request-timeout.js +3 -0
- package/lib/programmatic/route-handler-map.js +7 -0
- package/lib/programmatic/run-channel-catalog.js +50 -8
- package/lib/programmatic/run-channel-install-options.js +5 -1
- package/lib/programmatic/run-download.js +41 -0
- package/lib/protection/auth-context.js +3 -1
- package/lib/protection/protection-display.js +3 -2
- package/lib/protection/report-exit.js +50 -0
- package/lib/protection/run-commands.js +2 -0
- package/lib/role-assistant/package-validate.js +175 -0
- package/lib/role-assistant/test-case-assertion-row.js +36 -0
- package/lib/role-assistant/test-case-copy-bind.js +77 -0
- package/lib/role-assistant/test-case-copy-expected.js +127 -0
- package/lib/role-assistant/test-case-copy-from-work.js +90 -0
- package/lib/role-assistant/test-case-copy-interactions.js +67 -0
- package/lib/role-assistant/test-case-copy.js +119 -0
- package/lib/role-assistant/test-case-expected-catalog.js +135 -0
- package/lib/role-assistant/test-case-validate.js +88 -0
- package/lib/role-assistant/test-cases-bid.js +117 -4
- package/lib/role-assistant/test-cases-evaluate.js +137 -30
- package/lib/role-assistant/test-cases-honesty.js +155 -6
- package/lib/role-assistant/test-cases-lifecycle.js +291 -0
- package/lib/role-assistant/test-cases-search-graph.js +122 -0
- package/lib/role-assistant/test-cases-search.js +20 -2
- package/lib/role-assistant/test-cases.js +34 -168
- package/lib/role-assistant/test-pack-map.js +180 -0
- package/lib/role-assistant/test-pack-sync.js +187 -0
- package/lib/role-assistant/test-runner-debug.js +54 -0
- package/lib/role-assistant/test-runner-respond-body.js +36 -0
- package/lib/role-assistant/test-runner-search.js +20 -5
- package/lib/role-assistant/test-runner-start-payload.js +63 -10
- package/lib/role-assistant/test-runner-workhub-answers.js +45 -16
- package/lib/role-assistant/test-runner-workhub.js +45 -21
- package/lib/role-assistant/test-runner.js +50 -10
- package/lib/schema/external-datasource.schema.json +36 -5
- package/lib/schema/external-system.schema.json +20 -2
- package/lib/schema/role-assistant-test-case.schema.json +408 -0
- package/lib/search/dataplane-context.js +23 -0
- package/lib/search/search-args.js +211 -0
- package/lib/search/search-display.js +238 -0
- package/lib/search/search-errors.js +50 -0
- package/lib/search/search-prompts.js +119 -0
- package/lib/search/search-roles.js +85 -0
- package/lib/search/search-run.js +241 -0
- package/lib/search/search-skills.js +198 -0
- package/lib/utils/authentication-slot-show.js +153 -0
- package/lib/utils/credential-display.js +43 -1
- package/lib/utils/external-system-readiness-compact-blocks.js +5 -1
- package/lib/utils/external-system-readiness-compact-display.js +30 -1
- package/lib/utils/external-system-validators.js +2 -1
- package/lib/utils/help-builder.js +3 -1
- package/lib/utils/schema-loader.js +37 -0
- package/lib/validation/validate-external-file.js +5 -1
- package/lib/validation/validate.js +3 -2
- package/package.json +1 -1
- package/templates/agent-kit/adapters/claude/nested.CLAUDE.md +1 -0
- package/templates/agent-kit/adapters/claude/root.CLAUDE.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-0-plan.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-1-validate.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-2-build.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-3-push.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-1-validate.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-2-build.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-3-prove.md +5 -0
- package/templates/agent-kit/agent-kit.yaml +45 -0
- package/templates/agent-kit/instructions/AGENTKIT.md +83 -0
- package/templates/agent-kit/instructions/integration.AGENTS.md +25 -0
- package/templates/agent-kit/instructions/role-assistant.AGENTS.md +22 -0
- package/templates/agent-kit/instructions/root.AGENTS.md +37 -0
- package/templates/agent-kit/parity-inventory.yaml +188 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +58 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/contract-checklist.md +40 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +54 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/production-safety.md +35 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/scripts/reserved-system-key.js +19 -0
- package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +51 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/challenge-checklist.md +108 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +34 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/plan-template.md +182 -0
- package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +50 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/demo-template.md +65 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +68 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/learning-template.md +66 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +42 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
- package/templates/agent-kit/skills/shared/hosts.md +78 -0
- package/templates/agent-kit/workspace/.cursor/plans/README.md +22 -0
- package/templates/agent-kit/workspace/LICENSE.mit +21 -0
- package/templates/agent-kit/workspace/README.md +89 -0
- package/templates/applications/builder-api/Dockerfile +3 -1
- package/templates/applications/dataplane/application.yaml +2 -2
- package/templates/applications/dataplane/env.template +1 -1
- package/templates/applications/dataplane/rbac.yaml +28 -0
- package/templates/applications/keycloak/env.template +3 -3
- package/templates/applications/miso-controller/application.yaml +1 -1
- package/templates/applications/miso-controller/env.template +8 -9
- package/templates/channels/claude/TROUBLESHOOTING.md +3 -0
- package/templates/channels/claude/channel.json +1 -1
- package/templates/channels/claude/install.template.md.hbs +1 -0
- package/templates/channels/claude/integration/claude-system.json +1 -1
- package/templates/channels/slack/channel.json +11 -0
- package/templates/channels/slack/install.template.md.hbs +9 -0
- package/templates/channels/slack/integration/slack-system.json +11 -0
- package/templates/channels/slack/manifest.template.json.hbs +6 -3
- package/templates/channels/teams-bot/channel.json +1 -0
- package/templates/python/docker-compose.hbs +14 -0
- package/templates/typescript/docker-compose.hbs +14 -0
|
@@ -64,6 +64,7 @@ const CHANNEL_INSTALL_SCHEMA_DESCRIPTIONS = {
|
|
|
64
64
|
provisioning: 'Provisioning mode for this definition.',
|
|
65
65
|
runtimeTarget: 'Default runtime or MCP Connected System.',
|
|
66
66
|
experimental: 'True when install requires allowExperimental.',
|
|
67
|
+
status: 'Catalog availability. Omit or visible: listed and installable. hide is omitted from this catalog. coming-soon: listed read-only; new install is refused.',
|
|
67
68
|
maxInstances: 'Optional cap on Connected System instances for this definition. Omit for unbounded.',
|
|
68
69
|
profiles: 'Optional generic install profiles (for example MCP client targets).'
|
|
69
70
|
},
|
|
@@ -78,6 +79,7 @@ const CHANNEL_INSTALL_SCHEMA_DESCRIPTIONS = {
|
|
|
78
79
|
displayLabel: 'Human-readable interface name.',
|
|
79
80
|
description: 'Short catalog blurb for the portal table. Not remaining-step copy.',
|
|
80
81
|
experimental: 'True when the target requires allowExperimental.',
|
|
82
|
+
status: 'visible: listed and installable. coming-soon: listed read-only. hide is omitted from the catalog.',
|
|
81
83
|
provisioning: 'How Builder provisions this channel (automatic, guided, or cli).',
|
|
82
84
|
runtimeTarget: 'Runtime or MCP Connected System. Not the channel instance key.',
|
|
83
85
|
installationSummary: 'Count and status rollup of Connected System instances for this definition.',
|
|
@@ -88,12 +90,12 @@ const CHANNEL_INSTALL_SCHEMA_DESCRIPTIONS = {
|
|
|
88
90
|
iconHref: 'Data URL for the definition icon.svg from the Builder channel template. Omitted when the template has no icon.',
|
|
89
91
|
maxInstances: 'Optional instance cap from the Channel Definition. Omitted when unbounded.',
|
|
90
92
|
installationCount: 'Number of Connected System instances currently projected for this definition.',
|
|
91
|
-
canInstall: 'False when installationCount is already at maxInstances. Omitted meaning is canInstall true when unbounded.',
|
|
92
|
-
blockedReason: 'Present when canInstall is false. Explains why a new install is refused.'
|
|
93
|
+
canInstall: 'False when coming-soon or when installationCount is already at maxInstances. Omitted meaning is canInstall true when unbounded.',
|
|
94
|
+
blockedReason: 'Present when canInstall is false. Explains why a new install is refused (coming soon or at cap).'
|
|
93
95
|
},
|
|
94
96
|
ListChannelTargetsData: {
|
|
95
97
|
_schema: 'Catalog of live channel / interface targets.',
|
|
96
|
-
channels: 'Live targets
|
|
98
|
+
channels: 'Live targets. hide is omitted. coming-soon is included read-only (canInstall false). Unimplemented keys are omitted.'
|
|
97
99
|
},
|
|
98
100
|
ListChannelTargetsResponse: {
|
|
99
101
|
_schema: '200 response envelope for listChannelTargets.',
|
|
@@ -116,8 +118,9 @@ const CHANNEL_INSTALL_SCHEMA_DESCRIPTIONS = {
|
|
|
116
118
|
installations: 'Connected System instances for this definition. Empty array when none exist.',
|
|
117
119
|
maxInstances: 'Optional instance cap from the Channel Definition. Omitted when unbounded.',
|
|
118
120
|
installationCount: 'Number of Connected System instances currently projected for this definition.',
|
|
119
|
-
canInstall: 'False when installationCount is already at maxInstances.',
|
|
120
|
-
blockedReason: 'Present when canInstall is false.'
|
|
121
|
+
canInstall: 'False when coming-soon or when installationCount is already at maxInstances.',
|
|
122
|
+
blockedReason: 'Present when canInstall is false.',
|
|
123
|
+
status: 'visible or coming-soon. hide definitions return 404 instead of this payload.'
|
|
121
124
|
},
|
|
122
125
|
ChannelDefinitionDetailResponse: {
|
|
123
126
|
_schema: '200 response envelope for getChannelDefinitionDetail.',
|
|
@@ -124,6 +124,12 @@ const OPERATIONS_SCHEMA_DESCRIPTIONS = {
|
|
|
124
124
|
sync: 'When true, force full datasource data sync after deploy.',
|
|
125
125
|
verbose: 'When true, expand deploy summary output.'
|
|
126
126
|
},
|
|
127
|
+
DownloadRequest: {
|
|
128
|
+
_schema: 'Optional flags for workspace download (CLI aifabrix download parity).',
|
|
129
|
+
force: 'When true, overwrite existing local package files.',
|
|
130
|
+
dryRun: 'When true, preview files that would be written without mutating the workspace.',
|
|
131
|
+
format: 'Package file format when downloading a Connected System (json or yaml).'
|
|
132
|
+
},
|
|
127
133
|
ProtectionUploadRequest: {
|
|
128
134
|
_schema: 'Optional flags for protection upload (CLI protection upload parity).',
|
|
129
135
|
dryRun: 'When true, validate only; do not upload or sync.',
|
|
@@ -354,6 +360,19 @@ const OPERATIONS_SCHEMA_DESCRIPTIONS = {
|
|
|
354
360
|
correlationId: 'UUID correlating logs for this request.',
|
|
355
361
|
requestId: 'Optional per-request UUID when emitted by the server.'
|
|
356
362
|
},
|
|
363
|
+
DownloadResultData: {
|
|
364
|
+
_schema: 'Workspace download outcome summary.',
|
|
365
|
+
downloaded: 'True when package files were written (false on dryRun).',
|
|
366
|
+
dryRun: 'True when validate-only dry run was requested.',
|
|
367
|
+
systemKey: 'Downloaded integration or Role Assistant system key.'
|
|
368
|
+
},
|
|
369
|
+
DownloadResultResponse: {
|
|
370
|
+
_schema: '200 response envelope for runDownload.',
|
|
371
|
+
success: 'Always true for successful builder-api JSON responses.',
|
|
372
|
+
data: 'Typed payload; see DownloadResultData.',
|
|
373
|
+
correlationId: 'UUID correlating logs for this request.',
|
|
374
|
+
requestId: 'Optional per-request UUID when emitted by the server.'
|
|
375
|
+
},
|
|
357
376
|
ProtectionUploadData: {
|
|
358
377
|
_schema: 'Protection upload outcome.',
|
|
359
378
|
datasourceKey: 'Datasource key whose protection manifest was uploaded.',
|
|
@@ -88,6 +88,7 @@ function applyOperationDurationHints(capability, operation) {
|
|
|
88
88
|
const durationByHandler = {
|
|
89
89
|
runUpload: Number(process.env.MAX_UPLOAD_TIMEOUT_MS || 300000),
|
|
90
90
|
runDeploySystem: Number(process.env.MAX_DEPLOY_TIMEOUT_MS || 300000),
|
|
91
|
+
runDownload: Number(process.env.MAX_DOWNLOAD_TIMEOUT_MS || 300000),
|
|
91
92
|
runTest: Number(process.env.MAX_TEST_E2E_TIMEOUT_MS || 120000),
|
|
92
93
|
runCertifyPillar: Number(process.env.MAX_CERTIFY_TIMEOUT_MS || 120000)
|
|
93
94
|
};
|
|
@@ -19,6 +19,9 @@ function getTimeoutMsForHandler(handlerId) {
|
|
|
19
19
|
if (handlerId === 'runDeploySystem') {
|
|
20
20
|
return Number(process.env.MAX_DEPLOY_TIMEOUT_MS || 300000);
|
|
21
21
|
}
|
|
22
|
+
if (handlerId === 'runDownload') {
|
|
23
|
+
return Number(process.env.MAX_DOWNLOAD_TIMEOUT_MS || 300000);
|
|
24
|
+
}
|
|
22
25
|
if (handlerId === 'runTest') {
|
|
23
26
|
return Number(process.env.MAX_TEST_E2E_TIMEOUT_MS || 120000);
|
|
24
27
|
}
|
|
@@ -19,6 +19,7 @@ const { runRepair } = require('./run-repair');
|
|
|
19
19
|
const { runValidate } = require('./run-validate');
|
|
20
20
|
const { runPreviewUpload, runUpload } = require('./run-upload');
|
|
21
21
|
const { runDeploySystem } = require('./run-deploy');
|
|
22
|
+
const { runDownload } = require('./run-download');
|
|
22
23
|
const { runProtectionUpload, runProtectionCreate } = require('./run-protection');
|
|
23
24
|
const { runTest } = require('./run-test');
|
|
24
25
|
const { runCertifyPillar } = require('./run-certify');
|
|
@@ -164,6 +165,12 @@ const HANDLER_MAP = {
|
|
|
164
165
|
sync: body.sync === true,
|
|
165
166
|
verbose: body.verbose === true
|
|
166
167
|
}),
|
|
168
|
+
runDownload: (params, auth, body = {}) =>
|
|
169
|
+
runDownload(buildContext(auth, params.systemKey), {
|
|
170
|
+
force: body.force === true,
|
|
171
|
+
dryRun: body.dryRun === true,
|
|
172
|
+
format: body.format
|
|
173
|
+
}),
|
|
167
174
|
runProtectionUpload: (params, auth, body) =>
|
|
168
175
|
runProtectionUpload(buildContext(auth, params.systemKey), params.datasourceKey, {
|
|
169
176
|
dryRun: body.dryRun === true,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const registry = require('../channels/channel-definition-registry');
|
|
12
12
|
const { listLiveChannelTargetKeys, getChannelTarget } = require('../channels/channel-targets');
|
|
13
13
|
const { profilesForCatalogRow } = require('../channels/channel-install-profiles');
|
|
14
14
|
const { toSafeConfiguration, toPortalFields } = require('../channels/channel-configuration-map');
|
|
@@ -23,7 +23,14 @@ const {
|
|
|
23
23
|
} = require('../channels/channel-install-inventory');
|
|
24
24
|
const { listConnectedSystemsForInventory } = require('../channels/channel-inventory-fetch');
|
|
25
25
|
const { cardinalityProjection } = require('../channels/channel-cardinality');
|
|
26
|
+
const {
|
|
27
|
+
applyChannelStatusProjection,
|
|
28
|
+
normalizeChannelStatus
|
|
29
|
+
} = require('../channels/channel-definition-status');
|
|
26
30
|
const { httpError } = require('./channel-install-session');
|
|
31
|
+
const { getExternalSystem } = require('../api/external-systems.api');
|
|
32
|
+
const { unwrapApiData } = require('../utils/external-system-readiness-core');
|
|
33
|
+
const { isNotFoundError } = require('../channels/channel-instance-collision');
|
|
27
34
|
|
|
28
35
|
/**
|
|
29
36
|
* @param {Object} target
|
|
@@ -32,6 +39,7 @@ const { httpError } = require('./channel-install-session');
|
|
|
32
39
|
*/
|
|
33
40
|
function toChannelCatalogRow(target, installations = []) {
|
|
34
41
|
const summary = summarizeInstallations(installations);
|
|
42
|
+
const definition = registry.getChannelDefinition(target.key);
|
|
35
43
|
const row = {
|
|
36
44
|
key: target.key,
|
|
37
45
|
displayLabel: target.displayLabel,
|
|
@@ -49,7 +57,10 @@ function toChannelCatalogRow(target, installations = []) {
|
|
|
49
57
|
}
|
|
50
58
|
return {
|
|
51
59
|
...row,
|
|
52
|
-
...
|
|
60
|
+
...applyChannelStatusProjection(
|
|
61
|
+
normalizeChannelStatus(definition),
|
|
62
|
+
cardinalityProjection(definition, installations)
|
|
63
|
+
)
|
|
53
64
|
};
|
|
54
65
|
}
|
|
55
66
|
|
|
@@ -59,7 +70,7 @@ function toChannelCatalogRow(target, installations = []) {
|
|
|
59
70
|
* @returns {Object}
|
|
60
71
|
*/
|
|
61
72
|
function definitionListFields(definitionKey) {
|
|
62
|
-
const definition = getChannelDefinition(definitionKey);
|
|
73
|
+
const definition = registry.getChannelDefinition(definitionKey);
|
|
63
74
|
if (!definition) {
|
|
64
75
|
return {};
|
|
65
76
|
}
|
|
@@ -99,8 +110,8 @@ async function listChannelTargets(query = {}, ctx = null, deps = {}) {
|
|
|
99
110
|
* @returns {Object}
|
|
100
111
|
*/
|
|
101
112
|
function requireChannelDefinition(definitionKey) {
|
|
102
|
-
const definition = getChannelDefinition(definitionKey);
|
|
103
|
-
if (!definition) {
|
|
113
|
+
const definition = registry.getChannelDefinition(definitionKey);
|
|
114
|
+
if (!definition || normalizeChannelStatus(definition) === 'hide') {
|
|
104
115
|
throw httpError(`Unknown channel definition "${definitionKey}".`, 404);
|
|
105
116
|
}
|
|
106
117
|
return definition;
|
|
@@ -125,6 +136,7 @@ function toSafeDefinition(definition) {
|
|
|
125
136
|
provisioning: channel.provisioning,
|
|
126
137
|
runtimeTarget: channel.runtimeTarget,
|
|
127
138
|
experimental: channel.experimental === true,
|
|
139
|
+
status: normalizeChannelStatus(definition),
|
|
128
140
|
profiles: Array.isArray(channel.profiles) ? channel.profiles : []
|
|
129
141
|
}
|
|
130
142
|
};
|
|
@@ -153,11 +165,39 @@ async function getChannelDefinitionDetail(ctx, definitionKey, deps = {}) {
|
|
|
153
165
|
const installations = installationsForDefinition(systems, definition.key);
|
|
154
166
|
return {
|
|
155
167
|
...toSafeDefinition(definition),
|
|
156
|
-
...
|
|
168
|
+
...applyChannelStatusProjection(
|
|
169
|
+
normalizeChannelStatus(definition),
|
|
170
|
+
cardinalityProjection(definition, installations)
|
|
171
|
+
),
|
|
157
172
|
installations
|
|
158
173
|
};
|
|
159
174
|
}
|
|
160
175
|
|
|
176
|
+
/**
|
|
177
|
+
* @param {Object} ctx
|
|
178
|
+
* @param {string} definitionKey
|
|
179
|
+
* @param {string} systemKey
|
|
180
|
+
* @param {Object} [deps]
|
|
181
|
+
* @returns {Promise<Object>}
|
|
182
|
+
*/
|
|
183
|
+
async function installationFromDirectLookup(ctx, definitionKey, systemKey, deps) {
|
|
184
|
+
const getSystem = deps.getExternalSystem || getExternalSystem;
|
|
185
|
+
try {
|
|
186
|
+
const res = await getSystem(ctx.dataplaneUrl, systemKey, {
|
|
187
|
+
type: 'bearer',
|
|
188
|
+
token: ctx.bearerToken,
|
|
189
|
+
preserveBearer: true
|
|
190
|
+
});
|
|
191
|
+
const system = unwrapApiData(res);
|
|
192
|
+
return installationsForDefinition(system ? [system] : [], definitionKey)[0] || null;
|
|
193
|
+
} catch (err) {
|
|
194
|
+
if (isNotFoundError(err)) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
throw err;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
161
201
|
/**
|
|
162
202
|
* @param {Object} ctx
|
|
163
203
|
* @param {string} definitionKey
|
|
@@ -168,8 +208,10 @@ async function getChannelDefinitionDetail(ctx, definitionKey, deps = {}) {
|
|
|
168
208
|
async function getChannelInstallation(ctx, definitionKey, systemKey, deps = {}) {
|
|
169
209
|
requireChannelDefinition(definitionKey);
|
|
170
210
|
const systems = await listConnectedSystemsForInventory(ctx, deps);
|
|
171
|
-
const
|
|
172
|
-
|
|
211
|
+
const fromList = installationsForDefinition(systems, definitionKey).find(
|
|
212
|
+
(row) => row.key === systemKey
|
|
213
|
+
);
|
|
214
|
+
const found = fromList || (await installationFromDirectLookup(ctx, definitionKey, systemKey, deps));
|
|
173
215
|
if (!found) {
|
|
174
216
|
throw httpError(
|
|
175
217
|
`Channel installation "${systemKey}" was not found for "${definitionKey}".`,
|
|
@@ -56,9 +56,13 @@ function assertChannelInstallTarget(systemKey, body) {
|
|
|
56
56
|
assertInstallProfile(systemKey, body || {});
|
|
57
57
|
try {
|
|
58
58
|
assertChannelTargetInstallable(systemKey, {
|
|
59
|
-
allowExperimental: body && body.allowExperimental === true
|
|
59
|
+
allowExperimental: body && body.allowExperimental === true,
|
|
60
|
+
continueInstall: body && body.continueInstall === true
|
|
60
61
|
});
|
|
61
62
|
} catch (err) {
|
|
63
|
+
if (err && err.statusCode === 403) {
|
|
64
|
+
throw httpError(err.message, 403);
|
|
65
|
+
}
|
|
62
66
|
const live = listLiveChannelTargetKeys({
|
|
63
67
|
allowExperimental: body && body.allowExperimental === true
|
|
64
68
|
}).join(', ');
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Programmatic download wrapper — CLI download parity for builder-api.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview runDownload for POST /systems/:systemKey/download
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const { downloadExternalSystem } = require('../external-system/download');
|
|
12
|
+
const { runWithWorkspaceContext } = require('./workspace-context');
|
|
13
|
+
const { mergeProgrammaticOptions } = require('./command-options');
|
|
14
|
+
const { touchWorkspaceMeta } = require('./workspace-meta');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
18
|
+
* @param {Object} [options]
|
|
19
|
+
* @param {boolean} [options.force]
|
|
20
|
+
* @param {boolean} [options.dryRun]
|
|
21
|
+
* @param {string} [options.format]
|
|
22
|
+
* @returns {Promise<Object>}
|
|
23
|
+
*/
|
|
24
|
+
async function runDownload(ctx, options = {}) {
|
|
25
|
+
const merged = mergeProgrammaticOptions(ctx, options);
|
|
26
|
+
return runWithWorkspaceContext(ctx, async() => {
|
|
27
|
+
await downloadExternalSystem(ctx.systemKey, merged);
|
|
28
|
+
if (!options.dryRun) {
|
|
29
|
+
touchWorkspaceMeta(ctx, { lastOperation: 'download' });
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
downloaded: options.dryRun !== true,
|
|
33
|
+
dryRun: options.dryRun === true,
|
|
34
|
+
systemKey: ctx.systemKey
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
runDownload
|
|
41
|
+
};
|
|
@@ -67,7 +67,9 @@ async function resolveCliProtectionDataplaneContext(options) {
|
|
|
67
67
|
throw new Error('Authentication required. Run "aifabrix login" and try again.');
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
const dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig
|
|
70
|
+
const dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig, {
|
|
71
|
+
silent: options.json === true || options.silent === true
|
|
72
|
+
});
|
|
71
73
|
return { environment, controllerUrl, dataplaneUrl, authConfig };
|
|
72
74
|
}
|
|
73
75
|
|
|
@@ -18,7 +18,7 @@ const {
|
|
|
18
18
|
formatProgress,
|
|
19
19
|
formatNextActions
|
|
20
20
|
} = require('../utils/cli-test-layout-chalk');
|
|
21
|
-
const { reportTaskRows } = require('./report-exit');
|
|
21
|
+
const { reportTaskRows, isProtectionProblemResponse } = require('./report-exit');
|
|
22
22
|
const { describeProtectionRoot } = require('./paths');
|
|
23
23
|
const {
|
|
24
24
|
appendTaskRows,
|
|
@@ -46,7 +46,8 @@ function formatProtectionValidateTTY(report, opts = {}) {
|
|
|
46
46
|
const meta = report?.metadata || {};
|
|
47
47
|
appendProtectionReportHeader(lines, meta);
|
|
48
48
|
const summary = report?.summary || {};
|
|
49
|
-
const
|
|
49
|
+
const problem = isProtectionProblemResponse(report);
|
|
50
|
+
const failCount = problem ? Math.max(1, Number(summary.fail || 0)) : Number(summary.fail || 0);
|
|
50
51
|
const warnCount = Number(summary.warn || 0);
|
|
51
52
|
const aggregate = failCount > 0 ? 'fail' : warnCount > 0 ? 'warn' : 'ok';
|
|
52
53
|
const glyph = aggregate === 'fail' ? '✖' : aggregate === 'warn' ? '⚠' : '✔';
|
|
@@ -23,6 +23,51 @@ function reportTaskRows(report) {
|
|
|
23
23
|
return [];
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* True when the payload is an RFC 7807 / API-client error, not a validate report.
|
|
28
|
+
* @param {*} report
|
|
29
|
+
* @returns {boolean}
|
|
30
|
+
*/
|
|
31
|
+
function isProtectionProblemResponse(report) {
|
|
32
|
+
if (!report || typeof report !== 'object') {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
if (report.success === false) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
const status = Number(report.statusCode || report.status);
|
|
39
|
+
if (Number.isFinite(status) && status >= 400) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
const type = typeof report.type === 'string' ? report.type : '';
|
|
43
|
+
return type.includes('/Errors/') || type.includes('application/problem');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {*} uploaded
|
|
48
|
+
* @returns {string}
|
|
49
|
+
*/
|
|
50
|
+
function protectionUploadFailureMessage(uploaded) {
|
|
51
|
+
if (!uploaded || typeof uploaded !== 'object') {
|
|
52
|
+
return 'Protection upload returned empty response';
|
|
53
|
+
}
|
|
54
|
+
return (
|
|
55
|
+
uploaded.formattedError ||
|
|
56
|
+
uploaded.error ||
|
|
57
|
+
uploaded.detail ||
|
|
58
|
+
`Protection upload failed (HTTP ${uploaded.statusCode || uploaded.status || 0})`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {*} uploaded
|
|
64
|
+
*/
|
|
65
|
+
function assertProtectionUploadSucceeded(uploaded) {
|
|
66
|
+
if (!uploaded || typeof uploaded !== 'object' || isProtectionProblemResponse(uploaded)) {
|
|
67
|
+
throw new Error(protectionUploadFailureMessage(uploaded));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
26
71
|
/**
|
|
27
72
|
* @param {Object} report
|
|
28
73
|
* @param {Object} [opts]
|
|
@@ -33,6 +78,9 @@ function exitCodeFromProtectionReport(report, opts = {}) {
|
|
|
33
78
|
if (!report || typeof report !== 'object') {
|
|
34
79
|
return 3;
|
|
35
80
|
}
|
|
81
|
+
if (isProtectionProblemResponse(report)) {
|
|
82
|
+
return 1;
|
|
83
|
+
}
|
|
36
84
|
const summary = report.summary || {};
|
|
37
85
|
const failCount = Number(summary.fail || 0);
|
|
38
86
|
const warnCount = Number(summary.warn || 0);
|
|
@@ -58,5 +106,7 @@ function exitCodeFromProtectionReport(report, opts = {}) {
|
|
|
58
106
|
|
|
59
107
|
module.exports = {
|
|
60
108
|
reportTaskRows,
|
|
109
|
+
isProtectionProblemResponse,
|
|
110
|
+
assertProtectionUploadSucceeded,
|
|
61
111
|
exitCodeFromProtectionReport
|
|
62
112
|
};
|
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
deleteProtection,
|
|
19
19
|
findProtectionKeyByDatasource
|
|
20
20
|
} = require('../api/protection.api');
|
|
21
|
+
const { assertProtectionUploadSucceeded } = require('./report-exit');
|
|
21
22
|
const { preflightDatasourceReady } = require('./preflight-datasource-ready');
|
|
22
23
|
const { runProtectionUploadDataSync } = require('./run-commands-upload-sync');
|
|
23
24
|
const {
|
|
@@ -63,6 +64,7 @@ async function runProtectionUpload(datasourceKey, manifest, opts, logger) {
|
|
|
63
64
|
|
|
64
65
|
logger.log(formatProgress('Uploading protection manifest...'));
|
|
65
66
|
const uploaded = await uploadProtection(ctx.dataplaneUrl, ctx.authConfig, manifest);
|
|
67
|
+
assertProtectionUploadSucceeded(uploaded);
|
|
66
68
|
logger.log(
|
|
67
69
|
formatSuccessLine(
|
|
68
70
|
`Protection for '${datasourceKey}' uploaded (deploymentId ${uploaded?.deploymentId || '—'}, revision ${uploaded?.revision ?? '—'})`
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role Assistant package folder validation (settings, tests, evidence, knowledge).
|
|
3
|
+
* @fileoverview Walk ra-* governed files. Ignore leftover Connected System artifacts.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('../internal/fs-real-sync');
|
|
10
|
+
const { validateExternalFile, parseJsonFileContent } = require('../validation/validate-external-file');
|
|
11
|
+
const { listTestCaseFiles } = require('./test-cases');
|
|
12
|
+
const { validateTestCaseFile } = require('./test-case-validate');
|
|
13
|
+
|
|
14
|
+
const SETTINGS_TYPE = 'role-assistant-settings';
|
|
15
|
+
const EMPTY_TESTS_SKIP = 'tests/: skipped (no test cases)';
|
|
16
|
+
|
|
17
|
+
function fileRow(filePath, type, result) {
|
|
18
|
+
return {
|
|
19
|
+
file: path.basename(filePath),
|
|
20
|
+
type,
|
|
21
|
+
valid: result.valid,
|
|
22
|
+
path: filePath,
|
|
23
|
+
errors: result.errors || [],
|
|
24
|
+
warnings: result.warnings || []
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function listFilesByExt(dirPath, ext, acc) {
|
|
29
|
+
if (!fs.existsSync(dirPath)) {
|
|
30
|
+
return acc;
|
|
31
|
+
}
|
|
32
|
+
for (const name of fs.readdirSync(dirPath)) {
|
|
33
|
+
if (String(name).startsWith('.')) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const full = path.join(dirPath, name);
|
|
37
|
+
const st = fs.statSync(full);
|
|
38
|
+
if (st.isDirectory()) {
|
|
39
|
+
listFilesByExt(full, ext, acc);
|
|
40
|
+
} else if (String(name).toLowerCase().endsWith(ext)) {
|
|
41
|
+
acc.push(full);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return acc;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function evidenceRows(parsed) {
|
|
48
|
+
if (Array.isArray(parsed)) {
|
|
49
|
+
return parsed;
|
|
50
|
+
}
|
|
51
|
+
if (parsed && typeof parsed === 'object') {
|
|
52
|
+
return [parsed];
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function validateEvidenceJsonFile(filePath) {
|
|
58
|
+
const parseResult = parseJsonFileContent(filePath);
|
|
59
|
+
if (parseResult.error) {
|
|
60
|
+
return { ...parseResult.error, file: filePath, type: 'evidence' };
|
|
61
|
+
}
|
|
62
|
+
const rows = evidenceRows(parseResult.parsed);
|
|
63
|
+
const errors = [];
|
|
64
|
+
if (!rows) {
|
|
65
|
+
errors.push(`${filePath}: Evidence JSON must be an object or array of objects`);
|
|
66
|
+
} else {
|
|
67
|
+
rows.forEach((row, index) => {
|
|
68
|
+
const key = row && typeof row === 'object' && !Array.isArray(row) ? String(row.key || '').trim() : '';
|
|
69
|
+
if (!key) {
|
|
70
|
+
errors.push(`${filePath}: item ${index} is missing key`);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return { valid: errors.length === 0, errors, warnings: [], file: filePath, type: 'evidence' };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function validateKnowledgeMarkdown(filePath) {
|
|
78
|
+
const text = fs.readFileSync(filePath, 'utf8');
|
|
79
|
+
if (!String(text).trim()) {
|
|
80
|
+
return {
|
|
81
|
+
valid: false,
|
|
82
|
+
errors: [`${filePath}: knowledge markdown must not be empty`],
|
|
83
|
+
warnings: [],
|
|
84
|
+
file: filePath,
|
|
85
|
+
type: 'knowledge'
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return { valid: true, errors: [], warnings: [], file: filePath, type: 'knowledge' };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function validateSettingsFile(appPath) {
|
|
92
|
+
const yamlPath = path.join(appPath, 'role-assistant.yaml');
|
|
93
|
+
try {
|
|
94
|
+
const result = await validateExternalFile(yamlPath, SETTINGS_TYPE);
|
|
95
|
+
return fileRow(yamlPath, SETTINGS_TYPE, result);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
return fileRow(yamlPath, SETTINGS_TYPE, {
|
|
98
|
+
valid: false,
|
|
99
|
+
errors: [error.message],
|
|
100
|
+
warnings: []
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function collectDuplicateCaseIdErrors(caseFiles) {
|
|
106
|
+
const seen = new Map();
|
|
107
|
+
const errors = [];
|
|
108
|
+
for (const row of caseFiles) {
|
|
109
|
+
const parseResult = parseJsonFileContent(row.path);
|
|
110
|
+
const caseId = parseResult.parsed && String(parseResult.parsed.caseId || '').trim();
|
|
111
|
+
if (!caseId) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (seen.has(caseId)) {
|
|
115
|
+
errors.push(`${row.path}: duplicate caseId "${caseId}" (also ${seen.get(caseId)})`);
|
|
116
|
+
} else {
|
|
117
|
+
seen.set(caseId, row.path);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return errors;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function validateTestFiles(appPath) {
|
|
124
|
+
const testsDir = path.join(appPath, 'tests');
|
|
125
|
+
const yamlFiles = listTestCaseFiles(testsDir);
|
|
126
|
+
if (yamlFiles.length === 0) {
|
|
127
|
+
return { files: [], errors: [], warnings: [EMPTY_TESTS_SKIP] };
|
|
128
|
+
}
|
|
129
|
+
const files = yamlFiles.map((filePath) => fileRow(filePath, 'role-assistant-test-case', validateTestCaseFile(filePath)));
|
|
130
|
+
const errors = files.flatMap((row) => row.errors);
|
|
131
|
+
errors.push(...collectDuplicateCaseIdErrors(files));
|
|
132
|
+
return { files, errors, warnings: [] };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function validateEvidenceFiles(appPath) {
|
|
136
|
+
const jsonFiles = listFilesByExt(path.join(appPath, 'evidence'), '.json', []);
|
|
137
|
+
const files = jsonFiles.map((filePath) => fileRow(filePath, 'evidence', validateEvidenceJsonFile(filePath)));
|
|
138
|
+
return { files, errors: files.flatMap((row) => row.errors), warnings: [] };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function validateKnowledgeFiles(appPath) {
|
|
142
|
+
const mdFiles = listFilesByExt(path.join(appPath, 'knowledge'), '.md', []).filter((filePath) => {
|
|
143
|
+
return path.dirname(filePath) === path.join(appPath, 'knowledge');
|
|
144
|
+
});
|
|
145
|
+
const files = mdFiles.map((filePath) => fileRow(filePath, 'knowledge', validateKnowledgeMarkdown(filePath)));
|
|
146
|
+
return { files, errors: files.flatMap((row) => row.errors), warnings: [] };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function validateRoleAssistantPackage(appPath) {
|
|
150
|
+
const settings = await validateSettingsFile(appPath);
|
|
151
|
+
const tests = validateTestFiles(appPath);
|
|
152
|
+
const evidence = validateEvidenceFiles(appPath);
|
|
153
|
+
const knowledge = validateKnowledgeFiles(appPath);
|
|
154
|
+
const files = [settings, ...tests.files, ...evidence.files, ...knowledge.files];
|
|
155
|
+
const errors = [
|
|
156
|
+
...settings.errors,
|
|
157
|
+
...tests.errors,
|
|
158
|
+
...evidence.errors,
|
|
159
|
+
...knowledge.errors
|
|
160
|
+
];
|
|
161
|
+
const warnings = [...settings.warnings, ...tests.warnings, ...evidence.warnings, ...knowledge.warnings];
|
|
162
|
+
return {
|
|
163
|
+
valid: errors.length === 0,
|
|
164
|
+
errors,
|
|
165
|
+
warnings,
|
|
166
|
+
files
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
module.exports = {
|
|
171
|
+
EMPTY_TESTS_SKIP,
|
|
172
|
+
validateRoleAssistantPackage,
|
|
173
|
+
validateEvidenceJsonFile,
|
|
174
|
+
validateKnowledgeMarkdown
|
|
175
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared assertion row helpers (pass/fail/warn/notes/skip).
|
|
3
|
+
* @fileoverview Kind flags for Role Assistant test result rows
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
function skipRow(id, message) {
|
|
9
|
+
return { id, passed: true, skip: true, kind: 'skip', message };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function notesRow(id, message) {
|
|
13
|
+
return { id, passed: true, notes: true, kind: 'notes', message };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function withKind(row) {
|
|
17
|
+
if (!row || row.kind) {
|
|
18
|
+
return row;
|
|
19
|
+
}
|
|
20
|
+
if (row.notes === true) {
|
|
21
|
+
return { ...row, kind: 'notes' };
|
|
22
|
+
}
|
|
23
|
+
if (row.skip === true) {
|
|
24
|
+
return { ...row, kind: 'skip' };
|
|
25
|
+
}
|
|
26
|
+
if (row.warn === true) {
|
|
27
|
+
return { ...row, kind: 'warn' };
|
|
28
|
+
}
|
|
29
|
+
return { ...row, kind: row.passed ? 'pass' : 'fail' };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = {
|
|
33
|
+
skipRow,
|
|
34
|
+
notesRow,
|
|
35
|
+
withKind
|
|
36
|
+
};
|