@aifabrix/builder 2.53.0 → 2.54.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/bin/aifabrix.js +2 -2
- package/lib/api/assistants.api.js +8 -5
- package/lib/api/channel-identity.api.js +41 -0
- package/lib/api/evidence-authoring.api.js +5 -1
- package/lib/api/evidence.api.js +2 -0
- package/lib/api/types/evidence.types.js +2 -0
- package/lib/api/work-search.api.js +136 -0
- package/lib/channels/add-channel.js +163 -126
- package/lib/channels/approval-guides/chatgpt.js +14 -18
- package/lib/channels/approval-guides/claude.js +29 -0
- package/lib/channels/approval-guides/index.js +88 -22
- package/lib/channels/approval-guides/mcp-client.js +27 -0
- package/lib/channels/approval-guides/microsoft-copilot.js +52 -3
- package/lib/channels/approval-guides/slack.js +10 -10
- package/lib/channels/approval-guides/teams-bot.js +8 -12
- package/lib/channels/channel-add-continuations.js +91 -0
- package/lib/channels/channel-add-ensure.js +124 -27
- package/lib/channels/channel-add-existing.js +160 -28
- package/lib/channels/channel-add-failure.js +60 -0
- package/lib/channels/channel-add-remaining.js +486 -0
- package/lib/channels/channel-add-tty.js +190 -0
- package/lib/channels/channel-add-wizard-config.js +138 -0
- package/lib/channels/channel-add-wizard.js +250 -0
- package/lib/channels/channel-artifact-generator.js +27 -13
- package/lib/channels/channel-cardinality.js +83 -0
- package/lib/channels/channel-catalog-icon.js +45 -0
- package/lib/channels/channel-cli-instructions.js +75 -0
- package/lib/channels/channel-configuration-map.js +213 -0
- package/lib/channels/channel-credential.js +11 -14
- package/lib/channels/channel-definition-project.js +101 -0
- package/lib/channels/channel-definition-registry.js +154 -0
- package/lib/channels/channel-description.js +59 -2
- package/lib/channels/channel-discovery.js +16 -2
- package/lib/channels/channel-external-system.js +86 -9
- package/lib/channels/channel-install-envelope-actions.js +88 -0
- package/lib/channels/channel-install-envelope-microsoft.js +155 -0
- package/lib/channels/channel-install-envelope.js +356 -0
- package/lib/channels/channel-install-help-md.js +172 -0
- package/lib/channels/channel-install-http-payload.js +62 -0
- package/lib/channels/channel-install-inventory.js +115 -0
- package/lib/channels/channel-install-profiles.js +90 -0
- package/lib/channels/channel-instance-collision.js +118 -0
- package/lib/channels/channel-instance-key.js +100 -0
- package/lib/channels/channel-instance-materialize.js +240 -0
- package/lib/channels/channel-integration-generator.js +16 -9
- package/lib/channels/channel-integration-template.js +101 -22
- package/lib/channels/channel-inventory-fetch.js +79 -0
- package/lib/channels/channel-oauth-preflight.js +104 -0
- package/lib/channels/channel-package-version.js +76 -0
- package/lib/channels/channel-predefined-client.js +154 -0
- package/lib/channels/channel-probe.js +7 -2
- package/lib/channels/channel-runtime-resolver.js +180 -72
- package/lib/channels/channel-status-display.js +86 -16
- package/lib/channels/channel-targets.js +106 -92
- package/lib/channels/chatgpt-callback.js +318 -0
- package/lib/channels/chatgpt-channel-add.js +229 -0
- package/lib/channels/chatgpt-preflight.js +396 -0
- package/lib/channels/chatgpt-runtime-contract.js +17 -0
- package/lib/channels/chatgpt-setup-values.js +124 -0
- package/lib/channels/claude-callback.js +67 -0
- package/lib/channels/claude-channel-add.js +254 -0
- package/lib/channels/claude-runtime-contract.js +40 -0
- package/lib/channels/claude-runtime.js +58 -0
- package/lib/channels/claude-setup-values.js +89 -0
- package/lib/channels/mcp-client/config-merge.js +148 -0
- package/lib/channels/mcp-client/ensure-client.js +231 -0
- package/lib/channels/mcp-client/paths.js +53 -0
- package/lib/channels/mcp-client/profiles.js +122 -0
- package/lib/channels/mcp-client/redirect-uris.js +99 -0
- package/lib/channels/mcp-client/runtime.js +59 -0
- package/lib/channels/mcp-client-channel-add.js +186 -0
- package/lib/channels/microsoft-copilot-channel-add.js +117 -7
- package/lib/channels/microsoft-copilot-entra-identifier-uris.js +187 -0
- package/lib/channels/microsoft-copilot-entra-sso-form.js +106 -18
- package/lib/channels/microsoft-copilot-entra-sso.js +31 -2
- package/lib/channels/microsoft-copilot-package-prior.js +107 -0
- package/lib/channels/microsoft-copilot-package.js +29 -28
- package/lib/channels/microsoft-provisioning/a365.js +80 -0
- package/lib/channels/microsoft-provisioning/auth-config.js +240 -0
- package/lib/channels/microsoft-provisioning/auth.js +179 -0
- package/lib/channels/microsoft-provisioning/cli.js +71 -0
- package/lib/channels/microsoft-provisioning/constants.js +75 -0
- package/lib/channels/microsoft-provisioning/errors.js +117 -0
- package/lib/channels/microsoft-provisioning/index.js +55 -0
- package/lib/channels/microsoft-provisioning/install-atk.js +158 -0
- package/lib/channels/microsoft-provisioning/install-teams.js +115 -0
- package/lib/channels/microsoft-provisioning/login-portal-device.js +236 -0
- package/lib/channels/microsoft-provisioning/login-portal-parse.js +71 -0
- package/lib/channels/microsoft-provisioning/login-portal-teams.js +193 -0
- package/lib/channels/microsoft-provisioning/login-portal.js +230 -0
- package/lib/channels/microsoft-provisioning/persist.js +110 -0
- package/lib/channels/microsoft-provisioning/teams-bot-args.js +119 -0
- package/lib/channels/microsoft-provisioning/teams-bot-controller.js +123 -0
- package/lib/channels/microsoft-provisioning/teams-bot-parse.js +134 -0
- package/lib/channels/microsoft-provisioning/teams-bot-persist.js +113 -0
- package/lib/channels/microsoft-provisioning/teams-bot-provision.js +234 -0
- package/lib/channels/microsoft-provisioning/teams-bot-secrets.js +45 -0
- package/lib/channels/microsoft-provisioning/teams-cli.js +82 -0
- package/lib/channels/runtime-tool-catalog.js +5 -2
- package/lib/channels/slack-channel-add-portal.js +130 -0
- package/lib/channels/slack-channel-add-tty-auth.js +95 -0
- package/lib/channels/slack-channel-add.js +258 -0
- package/lib/channels/slack-provisioning/apps.js +181 -0
- package/lib/channels/slack-provisioning/auth-store.js +110 -0
- package/lib/channels/slack-provisioning/auth-ticket.js +185 -0
- package/lib/channels/slack-provisioning/auth.js +115 -0
- package/lib/channels/slack-provisioning/callbacks.js +41 -0
- package/lib/channels/slack-provisioning/cli.js +226 -0
- package/lib/channels/slack-provisioning/constants.js +37 -0
- package/lib/channels/slack-provisioning/credentials.js +169 -0
- package/lib/channels/slack-provisioning/curl-shim.js +132 -0
- package/lib/channels/slack-provisioning/developer-install.js +118 -0
- package/lib/channels/slack-provisioning/env-file.js +120 -0
- package/lib/channels/slack-provisioning/errors.js +47 -0
- package/lib/channels/slack-provisioning/go-ca-bundle.js +137 -0
- package/lib/channels/slack-provisioning/index.js +250 -0
- package/lib/channels/slack-provisioning/install-cli.js +323 -0
- package/lib/channels/slack-provisioning/manifest.js +89 -0
- package/lib/channels/slack-provisioning/oauth-lock.js +33 -0
- package/lib/channels/slack-provisioning/offer-cli.js +113 -0
- package/lib/channels/slack-provisioning/onboard-members.js +122 -0
- package/lib/channels/slack-provisioning/onboard.js +89 -0
- package/lib/channels/slack-provisioning/persist.js +95 -0
- package/lib/channels/slack-provisioning/remaining-urls.js +158 -0
- package/lib/channels/slack-provisioning/with-curl-on-path.js +48 -0
- package/lib/channels/teams-bot-channel-add.js +94 -0
- package/lib/channels/vendor-dataplane-url.js +249 -0
- package/lib/cli/setup-certification.help.js +2 -0
- package/lib/cli/setup-external-system-help.js +2 -0
- package/lib/cli/setup-external-system.js +1 -1
- package/lib/commands/channel-add-help.js +366 -0
- package/lib/commands/channel-add-setup.js +162 -80
- package/lib/commands/channel-configure.js +118 -0
- package/lib/commands/channel.js +10 -1
- package/lib/commands/governance-upload-external.js +6 -3
- package/lib/commands/lifecycle-command-action.js +2 -5
- package/lib/commands/lifecycle-external.js +93 -21
- package/lib/commands/repair-datasource-test-payload.js +4 -2
- package/lib/commands/repair-datasource-webhooks-sync-bridge.js +23 -2
- package/lib/commands/repair-datasource.js +3 -0
- package/lib/commands/repair-external-run.js +59 -25
- package/lib/commands/repair-openapi-rest-runtime.js +127 -0
- package/lib/commands/repair-orchestration-tag-merge.js +339 -0
- package/lib/commands/repair-rbac-catalog-defaults.js +96 -0
- package/lib/commands/repair-rbac-platform-preset.js +26 -15
- package/lib/commands/repair-rbac-reader-roles.js +8 -21
- package/lib/commands/repair-rbac.js +10 -34
- package/lib/commands/repair-system-permissions.js +24 -22
- package/lib/commands/role-assistant-test-output.js +14 -0
- package/lib/commands/role-assistant.js +59 -21
- package/lib/commands/test-e2e-external.js +148 -21
- package/lib/commands/upload-credential-push-log.js +2 -1
- package/lib/commands/upload-publish-context.js +1 -1
- package/lib/commands/upload-role-assistant-shell.js +124 -0
- package/lib/commands/upload-sidecar-followups.js +6 -3
- package/lib/commands/upload.js +18 -3
- package/lib/core/secrets-docker-env.js +4 -1
- package/lib/core/secrets-env-content.js +17 -3
- package/lib/datasource/capability/capability-manifest.js +0 -3
- package/lib/datasource/capability/copy-test-payload.js +3 -0
- package/lib/external-system/deploy-helpers.js +26 -1
- package/lib/external-system/deploy.js +10 -6
- package/lib/external-system/download-helpers.js +29 -2
- package/lib/external-system/download-role-assistant.js +83 -4
- package/lib/external-system/download.js +40 -30
- package/lib/external-system/sync-deploy-manifest.js +3 -2
- package/lib/external-system/upload-identity.js +42 -0
- package/lib/generator/index.js +19 -15
- package/lib/generator/split.js +13 -2
- package/lib/lifecycle/role-assistant-governance.js +146 -0
- package/lib/lifecycle/scenario-upload.js +181 -30
- package/lib/programmatic/builder-api-cors.js +143 -0
- package/lib/programmatic/builder-api-request.js +6 -0
- package/lib/programmatic/builder-api-route-dispatch.js +56 -42
- package/lib/programmatic/builder-api-server.js +8 -1
- package/lib/programmatic/capability-registry.js +54 -0
- package/lib/programmatic/channel-install-help-resolve.js +359 -0
- package/lib/programmatic/channel-install-session.js +218 -0
- package/lib/programmatic/help-content/channel-onboarding.md +2 -0
- package/lib/programmatic/help-content/cip-overview.md +3 -1
- package/lib/programmatic/http-raw-response.js +62 -0
- package/lib/programmatic/openapi-channel-install-bindings.js +179 -0
- package/lib/programmatic/openapi-channel-install-schemas.js +286 -0
- package/lib/programmatic/openapi-component-schemas.js +18 -1
- package/lib/programmatic/openapi-description-enricher.js +3 -1
- package/lib/programmatic/openapi-descriptions.js +74 -3
- package/lib/programmatic/openapi-envelope-examples.js +117 -0
- package/lib/programmatic/openapi-operation-bindings.js +4 -1
- package/lib/programmatic/openapi-schema-descriptions-channel.js +316 -0
- package/lib/programmatic/openapi-spec.js +12 -8
- package/lib/programmatic/platform-rbac-catalog.js +36 -18
- package/lib/programmatic/route-handler-map.js +51 -1
- package/lib/programmatic/route-handlers-context.js +2 -1
- package/lib/programmatic/run-auth-permissions.js +54 -0
- package/lib/programmatic/run-channel-catalog.js +196 -0
- package/lib/programmatic/run-channel-install-artifacts.js +137 -0
- package/lib/programmatic/run-channel-install-copilot.js +353 -0
- package/lib/programmatic/run-channel-install-options.js +232 -0
- package/lib/programmatic/run-channel-install-teams-bot.js +204 -0
- package/lib/programmatic/run-channel-install.js +346 -0
- package/lib/programmatic/run-channel-package.js +87 -7
- package/lib/programmatic/workspace-context.js +2 -0
- package/lib/role-assistant/evidence-sync.js +4 -2
- package/lib/role-assistant/evidence-validation-issues.js +51 -0
- package/lib/role-assistant/knowledge-sync.js +10 -4
- package/lib/role-assistant/settings.js +49 -2
- package/lib/role-assistant/test-cases-evaluate.js +30 -2
- package/lib/role-assistant/test-cases-evidence.js +175 -0
- package/lib/role-assistant/test-cases-search.js +229 -0
- package/lib/role-assistant/test-cases.js +37 -4
- package/lib/role-assistant/test-runner-search.js +149 -0
- package/lib/role-assistant/test-runner-workhub-helpers.js +40 -2
- package/lib/role-assistant/test-runner-workhub-soft.js +9 -3
- package/lib/role-assistant/test-runner-workhub.js +29 -11
- package/lib/role-assistant/test-runner.js +16 -1
- package/lib/schema/external-datasource.schema.json +119 -16
- package/lib/schema/external-system.schema.json +159 -2
- package/lib/schema/mcp-client-profile.schema.json +103 -0
- package/lib/utils/app-path-resolve.js +4 -0
- package/lib/utils/authentication-env-template.js +44 -6
- package/lib/utils/credential-secrets-env.js +6 -1
- package/lib/utils/env-copy.js +19 -7
- package/lib/utils/external-env-template.js +20 -2
- package/lib/utils/paths.js +18 -1
- package/lib/utils/platform-shell-package.js +69 -0
- package/lib/utils/port-resolver.js +50 -1
- package/lib/utils/rewrite-name-flag.js +20 -0
- package/lib/utils/role-assistant-paths.js +13 -10
- package/lib/utils/schema-resolver.js +11 -1
- package/lib/utils/secrets-helpers.js +12 -21
- package/lib/utils/token-manager.js +10 -5
- package/lib/validation/validate.js +40 -0
- package/package.json +1 -1
- package/templates/README.md +1 -1
- package/templates/applications/builder-api/Dockerfile +1 -1
- package/templates/applications/builder-api/env.template +7 -0
- package/templates/applications/dataplane/application.yaml +6 -5
- package/templates/applications/dataplane/env.template +2 -2
- package/templates/applications/dataplane/rbac.yaml +7 -2
- package/templates/applications/keycloak/Dockerfile +30 -4
- package/templates/applications/keycloak/application.yaml +2 -2
- package/templates/applications/keycloak/cimd-lenient-parse/AbstractClientIdMetadataDocumentExecutor.java +896 -0
- package/templates/applications/keycloak/cimd-lenient-parse/SOURCE.md +19 -0
- package/templates/applications/keycloak/env.template +3 -0
- package/templates/applications/miso-controller/application.yaml +1 -2
- package/templates/applications/miso-controller/env.template +2 -0
- package/templates/channels/README.md +5 -5
- package/templates/channels/chatgpt/TROUBLESHOOTING.md +51 -0
- package/templates/channels/chatgpt/channel.json +59 -0
- package/templates/channels/chatgpt/icon.svg +1 -0
- package/templates/channels/chatgpt/install.template.md.hbs +20 -95
- package/templates/channels/chatgpt/integration/README.md +1 -1
- package/templates/channels/chatgpt/integration/application.json +2 -2
- package/templates/channels/chatgpt/integration/chatgpt-system.json +49 -4
- package/templates/channels/chatgpt/integration/env.template +2 -4
- package/templates/channels/chatgpt/integration/rbac.json +13 -0
- package/templates/channels/claude/TROUBLESHOOTING.md +41 -0
- package/templates/channels/claude/channel.json +59 -0
- package/templates/channels/claude/icon.svg +1 -0
- package/templates/channels/claude/install.template.md.hbs +21 -0
- package/templates/channels/claude/integration/README.md +14 -0
- package/templates/channels/claude/integration/application.json +15 -0
- package/templates/channels/claude/integration/claude-system.json +59 -0
- package/templates/channels/claude/integration/env.template +2 -0
- package/templates/channels/claude/integration/rbac.json +13 -0
- package/templates/channels/claude/manifest.template.json.hbs +40 -0
- package/templates/channels/mcp-client/channel.json +72 -0
- package/templates/channels/mcp-client/icon.svg +1 -0
- package/templates/channels/mcp-client/install.template.md.hbs +24 -0
- package/templates/channels/mcp-client/integration/README.md +13 -0
- package/templates/channels/mcp-client/integration/application.json +15 -0
- package/templates/channels/mcp-client/integration/env.template +2 -0
- package/templates/channels/mcp-client/integration/mcp-client-system.json +72 -0
- package/templates/channels/mcp-client/integration/rbac.json +13 -0
- package/templates/channels/mcp-client/manifest.template.json.hbs +41 -0
- package/templates/channels/mcp-client/profiles/cursor.json +26 -0
- package/templates/channels/mcp-client/profiles/github-copilot.json +29 -0
- package/templates/channels/mcp-client/profiles/vscode.json +29 -0
- package/templates/channels/microsoft-copilot/channel.json +73 -0
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +8 -4
- package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +65 -5
- package/templates/channels/microsoft-copilot/icon.svg +1 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +9 -9
- package/templates/channels/microsoft-copilot/integration/README.md +1 -1
- package/templates/channels/microsoft-copilot/integration/application.json +2 -2
- package/templates/channels/microsoft-copilot/integration/env.template +3 -6
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +63 -4
- package/templates/channels/microsoft-copilot/integration/rbac.json +13 -0
- package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +5 -5
- package/templates/channels/slack/channel.json +223 -0
- package/templates/channels/slack/icon.svg +1 -0
- package/templates/channels/slack/install.template.md.hbs +36 -28
- package/templates/channels/slack/integration/README.md +245 -22
- package/templates/channels/slack/integration/application.json +44 -7
- package/templates/channels/slack/integration/env.template +15 -6
- package/templates/channels/slack/integration/openapi/openapi.json +126 -0
- package/templates/channels/slack/integration/rbac.json +84 -0
- package/templates/channels/slack/integration/slack-datasource-commands.json +154 -0
- package/templates/channels/slack/integration/slack-datasource-conversations.json +28 -3
- package/templates/channels/slack/integration/slack-datasource-events.json +154 -0
- package/templates/channels/slack/integration/slack-datasource-interactive.json +154 -0
- package/templates/channels/slack/integration/slack-datasource-messages.json +40 -2
- package/templates/channels/slack/integration/slack-system.json +155 -77
- package/templates/channels/slack/manifest.template.json.hbs +5 -2
- package/templates/channels/teams-bot/channel.json +161 -0
- package/templates/channels/teams-bot/icon.svg +1 -0
- package/templates/channels/teams-bot/install.template.md.hbs +12 -13
- package/templates/channels/teams-bot/integration/README.md +5 -4
- package/templates/channels/teams-bot/integration/application.json +6 -4
- package/templates/channels/teams-bot/integration/env.template +5 -4
- package/templates/channels/teams-bot/integration/openapi/openapi.json +68 -0
- package/templates/channels/teams-bot/integration/rbac.json +21 -0
- package/templates/channels/teams-bot/integration/teams-bot-datasource-activities.json +154 -0
- package/templates/channels/teams-bot/integration/teams-bot-system.json +128 -23
- package/templates/channels/web-embed/channel.json +60 -0
- package/templates/channels/web-embed/install.template.md.hbs +1 -1
- package/templates/channels/web-embed/integration/README.md +2 -2
- package/templates/channels/web-embed/integration/application.json +2 -2
- package/templates/channels/web-embed/integration/env.template +1 -1
- package/templates/channels/web-embed/integration/rbac.json +13 -0
- package/templates/channels/web-embed/integration/web-embed-system.json +50 -4
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wizard prompts from Channel Definition configuration/portalInput.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Prompt only required missing portal fields
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const inquirer = require('inquirer');
|
|
12
|
+
const { getChannelDefinition } = require('./channel-definition-registry');
|
|
13
|
+
const {
|
|
14
|
+
envNameToPropertyKey,
|
|
15
|
+
listDefinitionConfiguration,
|
|
16
|
+
isSecretShapedValue
|
|
17
|
+
} = require('./channel-configuration-map');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {unknown} options
|
|
21
|
+
* @returns {boolean}
|
|
22
|
+
*/
|
|
23
|
+
function isTrueFalseOptions(options) {
|
|
24
|
+
if (!Array.isArray(options) || options.length !== 2) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const values = new Set(options.map((item) => String(item).trim().toLowerCase()));
|
|
28
|
+
return values.has('true') && values.has('false');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {string} field
|
|
33
|
+
* @param {unknown} [options]
|
|
34
|
+
* @returns {string}
|
|
35
|
+
*/
|
|
36
|
+
function inquirerTypeForPortalField(field, options) {
|
|
37
|
+
if (isTrueFalseOptions(options)) {
|
|
38
|
+
return 'confirm';
|
|
39
|
+
}
|
|
40
|
+
if (field === 'select') {
|
|
41
|
+
return 'list';
|
|
42
|
+
}
|
|
43
|
+
if (field === 'password') {
|
|
44
|
+
return 'password';
|
|
45
|
+
}
|
|
46
|
+
return 'input';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {Object} options
|
|
51
|
+
* @param {string} propertyKey
|
|
52
|
+
* @returns {boolean}
|
|
53
|
+
*/
|
|
54
|
+
function isMissingConfigValue(options, propertyKey) {
|
|
55
|
+
const value = options[propertyKey];
|
|
56
|
+
return value === undefined || value === null || value === '';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {Object} row
|
|
61
|
+
* @param {Object} options
|
|
62
|
+
* @returns {boolean}
|
|
63
|
+
*/
|
|
64
|
+
function shouldPromptConfigurationRow(row, options) {
|
|
65
|
+
if (!row || !row.portalInput || !row.portalInput.label) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (isSecretShapedValue(row.value)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
const propertyKey = envNameToPropertyKey(row.name);
|
|
72
|
+
if (!isMissingConfigValue(options, propertyKey)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
return row.required === true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param {Object} row
|
|
80
|
+
* @returns {Object}
|
|
81
|
+
*/
|
|
82
|
+
function configurationQuestionFromRow(row) {
|
|
83
|
+
const portal = row.portalInput;
|
|
84
|
+
const propertyKey = envNameToPropertyKey(row.name);
|
|
85
|
+
const question = {
|
|
86
|
+
type: inquirerTypeForPortalField(portal.field, portal.options),
|
|
87
|
+
name: propertyKey,
|
|
88
|
+
message: portal.label
|
|
89
|
+
};
|
|
90
|
+
if (question.type === 'confirm') {
|
|
91
|
+
question.default = /^(true|1|yes|on)$/i.test(String(row.value || ''));
|
|
92
|
+
return question;
|
|
93
|
+
}
|
|
94
|
+
if (portal.placeholder) {
|
|
95
|
+
question.default = portal.placeholder;
|
|
96
|
+
}
|
|
97
|
+
if (Array.isArray(portal.options)) {
|
|
98
|
+
question.choices = portal.options;
|
|
99
|
+
}
|
|
100
|
+
return question;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @param {Object} definition
|
|
105
|
+
* @param {Object} [options]
|
|
106
|
+
* @returns {Object[]}
|
|
107
|
+
*/
|
|
108
|
+
function missingConfigurationQuestions(definition, options = {}) {
|
|
109
|
+
return listDefinitionConfiguration(definition)
|
|
110
|
+
.filter((row) => shouldPromptConfigurationRow(row, options))
|
|
111
|
+
.map(configurationQuestionFromRow);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @param {string} definitionKey
|
|
116
|
+
* @param {Object} options
|
|
117
|
+
* @param {Object} [deps]
|
|
118
|
+
* @returns {Promise<Object>}
|
|
119
|
+
*/
|
|
120
|
+
async function promptMissingDefinitionConfiguration(definitionKey, options, deps = {}) {
|
|
121
|
+
const definition = getChannelDefinition(definitionKey);
|
|
122
|
+
if (!definition) {
|
|
123
|
+
return {};
|
|
124
|
+
}
|
|
125
|
+
const questions = missingConfigurationQuestions(definition, options);
|
|
126
|
+
if (questions.length === 0) {
|
|
127
|
+
return {};
|
|
128
|
+
}
|
|
129
|
+
const prompt = deps.prompt || ((qs) => inquirer.prompt(qs));
|
|
130
|
+
return prompt(questions);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
module.exports = {
|
|
134
|
+
inquirerTypeForPortalField,
|
|
135
|
+
shouldPromptConfigurationRow,
|
|
136
|
+
missingConfigurationQuestions,
|
|
137
|
+
promptMissingDefinitionConfiguration
|
|
138
|
+
};
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive prompts when `aifabrix channel add` omits the channel target.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview TTY wizard for name + live channel selection; no mutations until dispatch
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const inquirer = require('inquirer');
|
|
12
|
+
const config = require('../core/config');
|
|
13
|
+
const {
|
|
14
|
+
formatSupportedChannelTargetsHelp,
|
|
15
|
+
listWizardChannelChoices
|
|
16
|
+
} = require('./channel-targets');
|
|
17
|
+
const { listMcpClientProfileChoices } = require('./mcp-client/profiles');
|
|
18
|
+
const { promptMissingDefinitionConfiguration } = require('./channel-add-wizard-config');
|
|
19
|
+
|
|
20
|
+
const CHANNEL_ADD_CANCELLED = 'CHANNEL_ADD_CANCELLED';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Usage text when the wizard cannot run (CI, non-TTY, JSON).
|
|
24
|
+
* @returns {string}
|
|
25
|
+
*/
|
|
26
|
+
function formatNonInteractiveChannelAddError() {
|
|
27
|
+
return [
|
|
28
|
+
'Channel add needs a terminal to choose a channel, or an explicit target:',
|
|
29
|
+
' $ aifabrix channel add slack --name "Elsa"',
|
|
30
|
+
formatSupportedChannelTargetsHelp()
|
|
31
|
+
].join('\n');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {Error|null|undefined} error
|
|
36
|
+
* @returns {boolean}
|
|
37
|
+
*/
|
|
38
|
+
function isChannelAddCancelled(error) {
|
|
39
|
+
return Boolean(error && error.code === CHANNEL_ADD_CANCELLED);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @returns {Error}
|
|
44
|
+
*/
|
|
45
|
+
function createChannelAddCancelledError() {
|
|
46
|
+
const error = new Error('Channel add cancelled.');
|
|
47
|
+
error.code = CHANNEL_ADD_CANCELLED;
|
|
48
|
+
return error;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {Error} error
|
|
53
|
+
* @returns {boolean}
|
|
54
|
+
*/
|
|
55
|
+
function isInquirerCancel(error) {
|
|
56
|
+
if (!error) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
if (error.name === 'ExitPromptError') {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
const message = String(error.message || '');
|
|
63
|
+
return /force closed|user force closed|prompt was canceled/i.test(message);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* TTY wizard only; CI and JSON stay on the explicit command.
|
|
68
|
+
* @param {Object} [options]
|
|
69
|
+
* @returns {boolean}
|
|
70
|
+
*/
|
|
71
|
+
function isChannelAddInteractive(options = {}) {
|
|
72
|
+
if (options.json === true) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
if (process.env.CI === 'true' || process.env.CI === '1') {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
return Boolean(process.stdin && process.stdin.isTTY);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {string} value
|
|
83
|
+
* @returns {true|string}
|
|
84
|
+
*/
|
|
85
|
+
function validateChannelName(value) {
|
|
86
|
+
if (!value || !String(value).trim()) {
|
|
87
|
+
return 'Channel name is required';
|
|
88
|
+
}
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @returns {{ type: string, name: string, message: string, validate: Function }}
|
|
94
|
+
*/
|
|
95
|
+
function channelNameQuestion() {
|
|
96
|
+
return {
|
|
97
|
+
type: 'input',
|
|
98
|
+
name: 'name',
|
|
99
|
+
message: 'Channel name:',
|
|
100
|
+
validate: validateChannelName
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @param {Object} options
|
|
106
|
+
* @returns {{ type: string, name: string, message: string, choices: Object[] }}
|
|
107
|
+
*/
|
|
108
|
+
function mcpClientProfileQuestion(options = {}) {
|
|
109
|
+
const already = options.target || options.profile || options.mcpClientProfile;
|
|
110
|
+
return {
|
|
111
|
+
type: 'list',
|
|
112
|
+
name: 'mcpClientProfile',
|
|
113
|
+
message: 'MCP client:',
|
|
114
|
+
choices: listMcpClientProfileChoices(),
|
|
115
|
+
when: (answers) => answers.channelTarget === 'mcp-client' && !already
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @param {Object} options
|
|
121
|
+
* @returns {{ type: string, name: string, message: string, choices: Object[] }}
|
|
122
|
+
*/
|
|
123
|
+
function channelTargetQuestion(options = {}) {
|
|
124
|
+
const choices = listWizardChannelChoices({
|
|
125
|
+
allowExperimental: options.allowExperimental === true
|
|
126
|
+
});
|
|
127
|
+
return {
|
|
128
|
+
type: 'list',
|
|
129
|
+
name: 'channelTarget',
|
|
130
|
+
message: 'Select channel:',
|
|
131
|
+
choices
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @returns {{ type: string, name: string, message: string, default: string }}
|
|
137
|
+
*/
|
|
138
|
+
function channelEnvironmentQuestion() {
|
|
139
|
+
return {
|
|
140
|
+
type: 'input',
|
|
141
|
+
name: 'environment',
|
|
142
|
+
message: 'Environment:',
|
|
143
|
+
default: 'dev'
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @param {Object} [deps]
|
|
149
|
+
* @returns {Promise<string>}
|
|
150
|
+
*/
|
|
151
|
+
async function resolveWizardEnvironment(deps = {}) {
|
|
152
|
+
const resolveEnvironment = deps.resolveEnvironment || (() => config.resolveEnvironment());
|
|
153
|
+
const env = await resolveEnvironment();
|
|
154
|
+
return env && String(env).trim() ? String(env).trim() : '';
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @param {Object} options
|
|
159
|
+
* @param {Object} [deps]
|
|
160
|
+
* @returns {Promise<Object[]>}
|
|
161
|
+
*/
|
|
162
|
+
async function buildChannelAddWizardQuestions(options = {}, deps = {}) {
|
|
163
|
+
const questions = [];
|
|
164
|
+
if (!options.name || !String(options.name).trim()) {
|
|
165
|
+
questions.push(channelNameQuestion());
|
|
166
|
+
}
|
|
167
|
+
questions.push(channelTargetQuestion(options));
|
|
168
|
+
questions.push(mcpClientProfileQuestion(options));
|
|
169
|
+
const env = await resolveWizardEnvironment(deps);
|
|
170
|
+
if (!env) {
|
|
171
|
+
questions.push(channelEnvironmentQuestion());
|
|
172
|
+
}
|
|
173
|
+
return questions;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* @param {Object[]} questions
|
|
178
|
+
* @param {Object} [deps]
|
|
179
|
+
* @returns {Promise<Object>}
|
|
180
|
+
*/
|
|
181
|
+
async function runChannelAddWizardPrompt(questions, deps = {}) {
|
|
182
|
+
const prompt = deps.prompt || ((qs) => inquirer.prompt(qs));
|
|
183
|
+
try {
|
|
184
|
+
return await prompt(questions);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
if (isInquirerCancel(error)) {
|
|
187
|
+
throw createChannelAddCancelledError();
|
|
188
|
+
}
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* @param {Object} options
|
|
195
|
+
* @param {Object} [deps]
|
|
196
|
+
* @returns {void}
|
|
197
|
+
*/
|
|
198
|
+
function assertChannelAddWizardInteractive(options = {}, deps = {}) {
|
|
199
|
+
const isInteractive = deps.isInteractive || isChannelAddInteractive;
|
|
200
|
+
if (!isInteractive(options)) {
|
|
201
|
+
throw new Error(formatNonInteractiveChannelAddError());
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* @param {Object} options
|
|
207
|
+
* @param {Object} answers
|
|
208
|
+
* @returns {{ channelTarget: string, name: string, environment?: string, mcpClientProfile?: string }}
|
|
209
|
+
*/
|
|
210
|
+
function selectionFromWizardAnswers(options, answers) {
|
|
211
|
+
const name = String(answers.name || options.name || '').trim();
|
|
212
|
+
if (validateChannelName(name) !== true) {
|
|
213
|
+
throw new Error('Channel name is required');
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
channelTarget: answers.channelTarget,
|
|
217
|
+
name,
|
|
218
|
+
environment: answers.environment,
|
|
219
|
+
mcpClientProfile: answers.mcpClientProfile || options.target || options.profile
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Prompt for missing channel add inputs. Does not write files or credentials.
|
|
225
|
+
* @param {Object} [options]
|
|
226
|
+
* @param {Object} [deps] - Inject prompt / resolveEnvironment / isInteractive in tests
|
|
227
|
+
* @returns {Promise<{ channelTarget: string, name: string, environment?: string }>}
|
|
228
|
+
*/
|
|
229
|
+
async function promptChannelAddWizard(options = {}, deps = {}) {
|
|
230
|
+
assertChannelAddWizardInteractive(options, deps);
|
|
231
|
+
const questions = await buildChannelAddWizardQuestions(options, deps);
|
|
232
|
+
const answers = await runChannelAddWizardPrompt(questions, deps);
|
|
233
|
+
const selection = selectionFromWizardAnswers(options, answers);
|
|
234
|
+
const config = await promptMissingDefinitionConfiguration(
|
|
235
|
+
selection.channelTarget,
|
|
236
|
+
{ ...options, ...selection },
|
|
237
|
+
deps
|
|
238
|
+
);
|
|
239
|
+
return { ...selection, ...config };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
module.exports = {
|
|
243
|
+
CHANNEL_ADD_CANCELLED,
|
|
244
|
+
formatNonInteractiveChannelAddError,
|
|
245
|
+
isChannelAddCancelled,
|
|
246
|
+
isChannelAddInteractive,
|
|
247
|
+
validateChannelName,
|
|
248
|
+
buildChannelAddWizardQuestions,
|
|
249
|
+
promptChannelAddWizard
|
|
250
|
+
};
|
|
@@ -15,6 +15,7 @@ const Handlebars = require('handlebars');
|
|
|
15
15
|
const { getChannelTarget } = require('./channel-targets');
|
|
16
16
|
const { BUILDER_ROOT } = require('./channel-integration-template');
|
|
17
17
|
const { generateMicrosoftCopilotPackage } = require('./microsoft-copilot-package');
|
|
18
|
+
const { fillSlackManifestDescriptions } = require('./channel-description');
|
|
18
19
|
|
|
19
20
|
const SECRET_PATTERNS = [/xoxb-/i, /xoxp-/i, /client_secret/i, /signing_secret/i, /Bearer\s+/i];
|
|
20
21
|
const PLACEHOLDER_DOMAIN_RE = /\byour-domain\.com\b/i;
|
|
@@ -106,6 +107,23 @@ function renderInstallGuideIfPresent(templatesDir, runtimeValues, outputDir, dry
|
|
|
106
107
|
return { installPath, content };
|
|
107
108
|
}
|
|
108
109
|
|
|
110
|
+
/**
|
|
111
|
+
* @param {Object} params
|
|
112
|
+
* @param {string} templatesDir
|
|
113
|
+
* @param {string} outputPath
|
|
114
|
+
* @returns {{ outputPath: string, artifactHash: string, content: string, installPath?: string }}
|
|
115
|
+
*/
|
|
116
|
+
function generateCopilotChannelArtifact(params, templatesDir, outputPath) {
|
|
117
|
+
return generateMicrosoftCopilotPackage({
|
|
118
|
+
runtimeValues: params.runtimeValues,
|
|
119
|
+
templatesDir,
|
|
120
|
+
outputPath,
|
|
121
|
+
dryRun: params.dryRun === true,
|
|
122
|
+
assertNoSecretsInArtifact,
|
|
123
|
+
renderTemplateFile
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
109
127
|
/**
|
|
110
128
|
* @param {Object} params
|
|
111
129
|
* @param {string} params.projectRoot
|
|
@@ -117,20 +135,16 @@ function renderInstallGuideIfPresent(templatesDir, runtimeValues, outputDir, dry
|
|
|
117
135
|
function generateChannelArtifact(params) {
|
|
118
136
|
const templatesDir = getChannelTemplatesDir(params.channelTarget);
|
|
119
137
|
const outputPath = getArtifactOutputPath(params.projectRoot, params.channelTarget);
|
|
120
|
-
|
|
121
138
|
if (params.channelTarget === 'microsoft-copilot') {
|
|
122
|
-
return
|
|
123
|
-
runtimeValues: params.runtimeValues,
|
|
124
|
-
templatesDir,
|
|
125
|
-
outputPath,
|
|
126
|
-
dryRun: params.dryRun === true,
|
|
127
|
-
assertNoSecretsInArtifact,
|
|
128
|
-
renderTemplateFile
|
|
129
|
-
});
|
|
139
|
+
return generateCopilotChannelArtifact(params, templatesDir, outputPath);
|
|
130
140
|
}
|
|
131
141
|
|
|
142
|
+
const runtimeValues = fillSlackManifestDescriptions(
|
|
143
|
+
params.channelTarget,
|
|
144
|
+
params.runtimeValues
|
|
145
|
+
);
|
|
132
146
|
const manifestTemplate = path.join(templatesDir, 'manifest.template.json.hbs');
|
|
133
|
-
const content = renderTemplateFile(manifestTemplate,
|
|
147
|
+
const content = renderTemplateFile(manifestTemplate, runtimeValues);
|
|
134
148
|
assertNoSecretsInArtifact(content);
|
|
135
149
|
const artifactHash = crypto.createHash('sha256').update(content, 'utf8').digest('hex');
|
|
136
150
|
const outputDir = path.dirname(outputPath);
|
|
@@ -142,13 +156,13 @@ function generateChannelArtifact(params) {
|
|
|
142
156
|
|
|
143
157
|
const install = renderInstallGuideIfPresent(
|
|
144
158
|
templatesDir,
|
|
145
|
-
|
|
159
|
+
runtimeValues,
|
|
146
160
|
outputDir,
|
|
147
161
|
params.dryRun === true
|
|
148
162
|
);
|
|
149
163
|
|
|
150
|
-
const installReady =
|
|
151
|
-
const blocker =
|
|
164
|
+
const installReady = runtimeValues?.installReady === true;
|
|
165
|
+
const blocker = runtimeValues?.discoveryBlocker || null;
|
|
152
166
|
return {
|
|
153
167
|
outputPath,
|
|
154
168
|
artifactHash,
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel Definition instance cardinality (projection + install gate).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview maxInstances is schema; canInstall is Builder-only
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const { getChannelDefinition } = require('./channel-definition-registry');
|
|
12
|
+
const { installationsForDefinition } = require('./channel-install-inventory');
|
|
13
|
+
const { listConnectedSystemsForInventory } = require('./channel-inventory-fetch');
|
|
14
|
+
const { httpError } = require('../programmatic/channel-install-session');
|
|
15
|
+
|
|
16
|
+
const AT_CAP_REASON = 'Maximum installations reached for this channel.';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {Object|null} definition
|
|
20
|
+
* @returns {number|null}
|
|
21
|
+
*/
|
|
22
|
+
function maxInstancesOf(definition) {
|
|
23
|
+
const value = definition && definition.channel && definition.channel.maxInstances;
|
|
24
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value < 1) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return Math.floor(value);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Catalog/detail projection. Not an External System schema field except maxInstances.
|
|
32
|
+
* @param {Object|null} definition
|
|
33
|
+
* @param {Object[]} [installations]
|
|
34
|
+
* @returns {Object}
|
|
35
|
+
*/
|
|
36
|
+
function cardinalityProjection(definition, installations = []) {
|
|
37
|
+
const maxInstances = maxInstancesOf(definition);
|
|
38
|
+
const installationCount = Array.isArray(installations) ? installations.length : 0;
|
|
39
|
+
const projection = {
|
|
40
|
+
installationCount,
|
|
41
|
+
canInstall: true
|
|
42
|
+
};
|
|
43
|
+
if (maxInstances === null) {
|
|
44
|
+
return projection;
|
|
45
|
+
}
|
|
46
|
+
projection.maxInstances = maxInstances;
|
|
47
|
+
projection.canInstall = installationCount < maxInstances;
|
|
48
|
+
if (!projection.canInstall) {
|
|
49
|
+
projection.blockedReason = AT_CAP_REASON;
|
|
50
|
+
}
|
|
51
|
+
return projection;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Refuse a new install when the definition is already at maxInstances.
|
|
56
|
+
* Continue/force of an existing instance skips this gate.
|
|
57
|
+
* @param {import('../programmatic/workspace-context').WorkspaceContext} ctx
|
|
58
|
+
* @param {string} definitionKey
|
|
59
|
+
* @param {Object} [options]
|
|
60
|
+
* @returns {Promise<void>}
|
|
61
|
+
*/
|
|
62
|
+
async function assertChannelCardinality(ctx, definitionKey, options = {}) {
|
|
63
|
+
if (options.continueInstall === true || options.force === true) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const definition = getChannelDefinition(definitionKey);
|
|
67
|
+
if (maxInstancesOf(definition) === null) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const systems = await listConnectedSystemsForInventory(ctx, options.deps || {});
|
|
71
|
+
const installations = installationsForDefinition(systems, definitionKey);
|
|
72
|
+
const projection = cardinalityProjection(definition, installations);
|
|
73
|
+
if (projection.canInstall !== true) {
|
|
74
|
+
throw httpError(projection.blockedReason || AT_CAP_REASON, 409);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = {
|
|
79
|
+
AT_CAP_REASON,
|
|
80
|
+
maxInstancesOf,
|
|
81
|
+
cardinalityProjection,
|
|
82
|
+
assertChannelCardinality
|
|
83
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Catalog icons live next to Channel Definitions (templates/channels/<key>/icon.svg).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Safe data-URL icon for published listChannelTargets rows
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const { TEMPLATES_ROOT, channelDiskFs } = require('./channel-definition-registry');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} raw
|
|
16
|
+
* @returns {string|undefined}
|
|
17
|
+
*/
|
|
18
|
+
function svgDataUrl(raw) {
|
|
19
|
+
const svg = String(raw || '').trim();
|
|
20
|
+
if (!svg.startsWith('<svg') && !svg.startsWith('<?xml')) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
return `data:image/svg+xml;base64,${Buffer.from(svg, 'utf8').toString('base64')}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} definitionKey
|
|
28
|
+
* @returns {string|undefined}
|
|
29
|
+
*/
|
|
30
|
+
function catalogIconHref(definitionKey) {
|
|
31
|
+
if (!definitionKey || typeof definitionKey !== 'string') {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
const iconPath = path.join(TEMPLATES_ROOT, definitionKey, 'icon.svg');
|
|
35
|
+
try {
|
|
36
|
+
return svgDataUrl(channelDiskFs().readFileSync(iconPath, 'utf8'));
|
|
37
|
+
} catch (err) {
|
|
38
|
+
if (err && err.code === 'ENOENT') {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = { catalogIconHref };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated CLI install instructions for provisioning.mode=cli.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Copy-pasteable aifabrix channel add command
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const { listDefinitionConfiguration, envNameToPropertyKey } = require('./channel-configuration-map');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} name
|
|
15
|
+
* @returns {string}
|
|
16
|
+
*/
|
|
17
|
+
function quoteCliName(name) {
|
|
18
|
+
return `"${String(name || '').replace(/"/g, '\\"')}"`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {Object} definition
|
|
23
|
+
* @returns {string[]}
|
|
24
|
+
*/
|
|
25
|
+
function requiredConfigurationFlags(definition) {
|
|
26
|
+
const flags = [];
|
|
27
|
+
for (const row of listDefinitionConfiguration(definition)) {
|
|
28
|
+
if (row.required !== true) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const propertyKey = envNameToPropertyKey(row.name);
|
|
32
|
+
flags.push(`--${propertyKey.replace(/[A-Z]/g, (ch) => `-${ch.toLowerCase()}`)} <${propertyKey}>`);
|
|
33
|
+
}
|
|
34
|
+
return flags;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @param {Object} definition
|
|
39
|
+
* @param {{ name?: string }} [opts]
|
|
40
|
+
* @returns {string}
|
|
41
|
+
*/
|
|
42
|
+
function formatChannelCliInstructions(definition, opts = {}) {
|
|
43
|
+
const name = opts.name || 'Finance';
|
|
44
|
+
const required = requiredConfigurationFlags(definition).join(' ');
|
|
45
|
+
const command = [
|
|
46
|
+
`aifabrix channel add ${definition.key} --name ${quoteCliName(name)}`,
|
|
47
|
+
required
|
|
48
|
+
]
|
|
49
|
+
.filter(Boolean)
|
|
50
|
+
.join(' ');
|
|
51
|
+
return [
|
|
52
|
+
'Installation method: AI Fabrix CLI',
|
|
53
|
+
'',
|
|
54
|
+
command,
|
|
55
|
+
'',
|
|
56
|
+
'Run this from an authenticated AI Fabrix Builder environment.'
|
|
57
|
+
].join('\n');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @param {Object} definition
|
|
62
|
+
* @returns {boolean}
|
|
63
|
+
*/
|
|
64
|
+
function isCliProvisioningMode(definition) {
|
|
65
|
+
const mode = definition && definition.channel && definition.channel.provisioning
|
|
66
|
+
? definition.channel.provisioning.mode
|
|
67
|
+
: '';
|
|
68
|
+
return mode === 'cli';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = {
|
|
72
|
+
formatChannelCliInstructions,
|
|
73
|
+
isCliProvisioningMode,
|
|
74
|
+
requiredConfigurationFlags
|
|
75
|
+
};
|