@aifabrix/builder 2.54.1 → 2.55.2
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 +172 -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 +272 -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/start.js +310 -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/deploy.js +7 -4
- package/lib/app/list-external-systems.js +2 -1
- package/lib/app/offer-create-from-template.js +184 -0
- 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-app.js +8 -1
- package/lib/cli/setup-auth.js +11 -2
- 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 +149 -0
- package/lib/commands/agent-kit.js +417 -0
- package/lib/commands/auth-status-user.js +99 -0
- package/lib/commands/auth-status.js +14 -4
- package/lib/commands/channel-add-help.js +5 -5
- package/lib/commands/datasource-capability-relate-cli.js +5 -4
- package/lib/commands/datasource-load-export-cli.js +7 -2
- package/lib/commands/datasource-test-trust-cli.js +5 -1
- package/lib/commands/governance-command-action.js +5 -4
- package/lib/commands/identity-membership-role-cache.js +6 -1
- package/lib/commands/lifecycle-command-action.js +16 -15
- package/lib/commands/login-credentials.js +8 -3
- package/lib/commands/login-device.js +14 -5
- package/lib/commands/login.js +14 -1
- package/lib/commands/logout.js +50 -15
- 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 +140 -59
- 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-governance-command-action.js +9 -7
- package/lib/commands/verify-operations-command-action.js +9 -8
- 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/commands/verify-trust-command-action.js +6 -5
- 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 +8 -3
- package/lib/generator/helpers.js +12 -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 +232 -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/knowledge-sync-settings-push.js +172 -0
- package/lib/role-assistant/knowledge-sync.js +9 -126
- 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 +149 -31
- package/lib/role-assistant/test-cases-evidence.js +44 -5
- package/lib/role-assistant/test-cases-honesty.js +155 -6
- package/lib/role-assistant/test-cases-lifecycle.js +297 -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 +53 -10
- package/lib/schema/external-datasource.schema.json +36 -5
- package/lib/schema/external-system.schema.json +20 -2
- package/lib/schema/infra-parameter.schema.json +51 -3
- package/lib/schema/infra.parameter.yaml +476 -40
- package/lib/schema/role-assistant-test-case.schema.json +408 -0
- package/lib/schema/type/document-storage.json +1 -1
- 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/api-required-permissions.js +271 -0
- package/lib/utils/api.js +16 -13
- package/lib/utils/authentication-slot-show.js +153 -0
- package/lib/utils/cli-json-exit.js +41 -0
- package/lib/utils/cli-utils.js +23 -5
- package/lib/utils/controller-url.js +78 -4
- package/lib/utils/credential-display.js +43 -1
- package/lib/utils/deployment-errors.js +15 -0
- package/lib/utils/deployment-validation-helpers.js +4 -1
- package/lib/utils/device-code.js +31 -10
- package/lib/utils/error-formatters/error-parser.js +16 -1
- package/lib/utils/error-formatters/permission-errors.js +19 -5
- 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/keycloak-realm-ready.js +5 -2
- package/lib/utils/logger.js +1 -0
- package/lib/utils/paths.js +26 -5
- package/lib/utils/schema-loader.js +37 -0
- package/lib/utils/secrets-helpers.js +2 -2
- package/lib/utils/write-stream-and-wait.js +63 -0
- package/lib/validation/validate-external-file.js +5 -1
- package/lib/validation/validate.js +3 -2
- package/package.json +20 -6
- 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 +104 -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 +59 -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 +52 -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 +51 -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 +43 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
- package/templates/agent-kit/skills/shared/hosts.md +65 -0
- package/templates/agent-kit/skills/shared/login.md +16 -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 -3
- package/templates/applications/dataplane/env.template +5 -5
- package/templates/applications/dataplane/rbac.yaml +28 -0
- package/templates/applications/keycloak/env.template +3 -3
- package/templates/applications/miso-controller/application.yaml +21 -2
- package/templates/applications/miso-controller/env.template +96 -25
- package/templates/applications/miso-controller/rbac.yaml +5 -0
- 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
|
@@ -21,6 +21,14 @@ const OPENAPI_APPLY_RESULT_SCHEMAS = {
|
|
|
21
21
|
},
|
|
22
22
|
['deployed']
|
|
23
23
|
),
|
|
24
|
+
DownloadResultData: frozenObject(
|
|
25
|
+
{
|
|
26
|
+
downloaded: { type: 'boolean' },
|
|
27
|
+
dryRun: { type: 'boolean' },
|
|
28
|
+
systemKey: schemaRef('SystemKey')
|
|
29
|
+
},
|
|
30
|
+
['downloaded', 'dryRun', 'systemKey']
|
|
31
|
+
),
|
|
24
32
|
ProtectionUploadData: frozenObject(
|
|
25
33
|
{
|
|
26
34
|
datasourceKey: schemaRef('DatasourceKey'),
|
|
@@ -90,6 +90,7 @@ const OPENAPI_CHANNEL_INSTALL_RESULT_SCHEMAS = {
|
|
|
90
90
|
provisioning: schemaRef('ChannelProvisioning'),
|
|
91
91
|
runtimeTarget: schemaRef('ChannelRuntimeTarget'),
|
|
92
92
|
experimental: { type: 'boolean' },
|
|
93
|
+
status: { type: 'string', enum: ['visible', 'hide', 'coming-soon'] },
|
|
93
94
|
maxInstances: { type: 'integer', minimum: 1 },
|
|
94
95
|
profiles: { type: 'array', items: schemaRef('ChannelTargetProfile') }
|
|
95
96
|
}),
|
|
@@ -108,7 +109,8 @@ const OPENAPI_CHANNEL_INSTALL_RESULT_SCHEMAS = {
|
|
|
108
109
|
maxInstances: { type: 'integer', minimum: 1 },
|
|
109
110
|
installationCount: { type: 'integer', minimum: 0 },
|
|
110
111
|
canInstall: { type: 'boolean' },
|
|
111
|
-
blockedReason: { type: 'string' }
|
|
112
|
+
blockedReason: { type: 'string' },
|
|
113
|
+
status: { type: 'string', enum: ['visible', 'hide', 'coming-soon'] }
|
|
112
114
|
},
|
|
113
115
|
['key', 'displayName', 'channel', 'installations']
|
|
114
116
|
),
|
|
@@ -140,6 +142,7 @@ const OPENAPI_CHANNEL_INSTALL_RESULT_SCHEMAS = {
|
|
|
140
142
|
displayLabel: { type: 'string' },
|
|
141
143
|
description: { type: 'string' },
|
|
142
144
|
experimental: { type: 'boolean' },
|
|
145
|
+
status: { type: 'string', enum: ['visible', 'hide', 'coming-soon'] },
|
|
143
146
|
provisioning: schemaRef('ChannelProvisioning'),
|
|
144
147
|
runtimeTarget: schemaRef('ChannelRuntimeTarget'),
|
|
145
148
|
installationSummary: schemaRef('ChannelInstallationSummary'),
|
|
@@ -40,6 +40,7 @@ const RESPONSE_ENVELOPES = [
|
|
|
40
40
|
['PreviewUploadResponse', 'PreviewUploadData'],
|
|
41
41
|
['UploadResultResponse', 'UploadResultData'],
|
|
42
42
|
['DeployResultResponse', 'DeployResultData'],
|
|
43
|
+
['DownloadResultResponse', 'DownloadResultData'],
|
|
43
44
|
['ProtectionUploadResponse', 'ProtectionUploadData'],
|
|
44
45
|
['ProtectionCreateResponse', 'ProtectionCreateData'],
|
|
45
46
|
['TestResultResponse', 'TestResultData'],
|
|
@@ -140,6 +140,12 @@ const OPERATION_DESCRIPTIONS = Object.freeze({
|
|
|
140
140
|
'Controller deploy for external systems (CLI aifabrix deploy parity). Optional body.gate matches CLI --gate (operations,trust,governance,full). Certificate seal is a platform side-effect of published Deploy — not a separate certificates issue call. Mutating; use Idempotency-Key.',
|
|
141
141
|
tags: ['apply']
|
|
142
142
|
},
|
|
143
|
+
runDownload: {
|
|
144
|
+
summary: 'Download system package into the workspace',
|
|
145
|
+
description:
|
|
146
|
+
'Fetch the published system from Dataplane into the workspace (CLI aifabrix download parity). Role Assistant packages also pull Evidence, Knowledge, and tests. Mutating; use Idempotency-Key.',
|
|
147
|
+
tags: ['workspace']
|
|
148
|
+
},
|
|
143
149
|
runProtectionUpload: {
|
|
144
150
|
summary: 'Upload protection manifest for a datasource',
|
|
145
151
|
description:
|
|
@@ -269,13 +275,13 @@ const OPERATION_DESCRIPTIONS = Object.freeze({
|
|
|
269
275
|
listChannelTargets: {
|
|
270
276
|
summary: 'List live channel definitions',
|
|
271
277
|
description:
|
|
272
|
-
'List live Channel Definitions. Each row has key, displayLabel, description, experimental, provisioning, runtimeTarget, installationSummary, installationCount, and canInstall. Optional maxInstances and blockedReason when capped. Catalog is information-only; publishing uses POST install. Read-only.',
|
|
278
|
+
'List live Channel Definitions. Each row has key, displayLabel, description, experimental, status, provisioning, runtimeTarget, installationSummary, installationCount, and canInstall. Optional maxInstances and blockedReason when capped or coming-soon. hide definitions are omitted. Catalog is information-only; publishing uses POST install. Read-only.',
|
|
273
279
|
tags: ['infra']
|
|
274
280
|
},
|
|
275
281
|
getChannelDefinitionDetail: {
|
|
276
282
|
summary: 'Get a Channel Definition',
|
|
277
283
|
description:
|
|
278
|
-
'Return a safe Channel Definition (identity, configuration, provisioning, runtimeTarget, profiles, optional maxInstances) plus installations projected from Dataplane Connected Systems. Includes canInstall. Unknown definition keys return 404. Known definitions with no instances return installations []. Read-only.',
|
|
284
|
+
'Return a safe Channel Definition (identity, configuration, provisioning, runtimeTarget, profiles, optional maxInstances, status) plus installations projected from Dataplane Connected Systems. Includes canInstall. Unknown or hide definition keys return 404. Known definitions with no instances return installations []. Read-only.',
|
|
279
285
|
tags: ['infra']
|
|
280
286
|
},
|
|
281
287
|
getChannelInstallation: {
|
|
@@ -287,7 +293,7 @@ const OPERATION_DESCRIPTIONS = Object.freeze({
|
|
|
287
293
|
runChannelInstall: {
|
|
288
294
|
summary: 'Install a channel',
|
|
289
295
|
description:
|
|
290
|
-
'Scaffold, upload, and configure a channel from a Channel Definition. Path systemKey is the definition key. Body name is required; optional systemKey is the Connected System instance key; configuration is a generic propertyKey map. Returns help markdown plus phase, safe userActions, definitionKey, installationKey, and systemKey (the instance). Mutating.',
|
|
296
|
+
'Scaffold, upload, and configure a channel from a Channel Definition. Path systemKey is the definition key. Body name is required; optional systemKey is the Connected System instance key; configuration is a generic propertyKey map. hide and coming-soon definitions return 403; --force does not override. Returns help markdown plus phase, safe userActions, definitionKey, installationKey, and systemKey (the instance). Mutating.',
|
|
291
297
|
tags: ['infra']
|
|
292
298
|
},
|
|
293
299
|
getChannelInstall: {
|
|
@@ -141,6 +141,11 @@ const OPENAPI_ENVELOPE_DATA_EXAMPLES = {
|
|
|
141
141
|
environment: 'dev',
|
|
142
142
|
systemKey: SAMPLE_SYSTEM_KEY
|
|
143
143
|
},
|
|
144
|
+
DownloadResultData: {
|
|
145
|
+
downloaded: true,
|
|
146
|
+
dryRun: false,
|
|
147
|
+
systemKey: SAMPLE_SYSTEM_KEY
|
|
148
|
+
},
|
|
144
149
|
ProtectionUploadData: {
|
|
145
150
|
datasourceKey: SAMPLE_DATASOURCE_KEY,
|
|
146
151
|
uploaded: true,
|
|
@@ -282,6 +287,7 @@ const OPENAPI_ENVELOPE_DATA_EXAMPLES = {
|
|
|
282
287
|
displayLabel: 'ChatGPT',
|
|
283
288
|
description: 'ChatGPT channel',
|
|
284
289
|
experimental: false,
|
|
290
|
+
status: 'visible',
|
|
285
291
|
provisioning: { mode: 'guided' },
|
|
286
292
|
runtimeTarget: { defaultSystemKey: 'runtime-orchestration', configurable: true },
|
|
287
293
|
installationSummary: { count: 0, statusRollup: 'none' },
|
|
@@ -310,6 +316,7 @@ const OPENAPI_ENVELOPE_DATA_EXAMPLES = {
|
|
|
310
316
|
provisioning: { mode: 'guided' },
|
|
311
317
|
runtimeTarget: { defaultSystemKey: 'runtime-orchestration', configurable: true },
|
|
312
318
|
experimental: false,
|
|
319
|
+
status: 'visible',
|
|
313
320
|
profiles: []
|
|
314
321
|
},
|
|
315
322
|
installations: []
|
|
@@ -233,6 +233,11 @@ const OPERATION_BINDINGS = Object.freeze({
|
|
|
233
233
|
requestBody: jsonRequestBody('DeployRequest'),
|
|
234
234
|
successResponse: 'DeployResultResponse'
|
|
235
235
|
},
|
|
236
|
+
runDownload: {
|
|
237
|
+
parameters: [SYSTEM_KEY_PARAM],
|
|
238
|
+
requestBody: jsonRequestBody('DownloadRequest'),
|
|
239
|
+
successResponse: 'DownloadResultResponse'
|
|
240
|
+
},
|
|
236
241
|
runProtectionUpload: {
|
|
237
242
|
parameters: [SYSTEM_KEY_PARAM, DATASOURCE_KEY_PARAM],
|
|
238
243
|
requestBody: jsonRequestBody('ProtectionUploadRequest'),
|
|
@@ -147,6 +147,11 @@ const OPENAPI_REQUEST_SCHEMAS = {
|
|
|
147
147
|
sync: { type: 'boolean' },
|
|
148
148
|
verbose: { type: 'boolean' }
|
|
149
149
|
}),
|
|
150
|
+
DownloadRequest: frozenObject({
|
|
151
|
+
force: { type: 'boolean' },
|
|
152
|
+
dryRun: { type: 'boolean' },
|
|
153
|
+
format: { type: 'string', enum: ['json', 'yaml'] }
|
|
154
|
+
}),
|
|
150
155
|
ProtectionUploadRequest: frozenObject({
|
|
151
156
|
dryRun: { type: 'boolean' },
|
|
152
157
|
sync: { type: 'boolean' },
|
|
@@ -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 ?? '—'})`
|