@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,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spawn Slack CLI (`slack api …`) without scattering argv.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Documented CLI only; skipped under Jest unless deps allow it
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { promisify } = require('util');
|
|
11
|
+
const execFileDefault = promisify(require('child_process').execFile);
|
|
12
|
+
const { SLACK_BINARIES } = require('./constants');
|
|
13
|
+
const { slackProvisioningError, wrapSlackCliError } = require('./errors');
|
|
14
|
+
const { withSlackCliPath, slackCliLocalBinDir } = require('./install-cli');
|
|
15
|
+
const { readSlackCredentialEntries, slackUserTokenForTeam } = require('./auth-store');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @returns {boolean}
|
|
19
|
+
*/
|
|
20
|
+
function isJestWorker() {
|
|
21
|
+
return Boolean(process.env.JEST_WORKER_ID);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {Object} [deps]
|
|
26
|
+
* @returns {boolean}
|
|
27
|
+
*/
|
|
28
|
+
function shouldSkipLiveSlackCli(deps = {}) {
|
|
29
|
+
if (deps.allowInTests === true) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return isJestWorker();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @returns {string[]}
|
|
37
|
+
*/
|
|
38
|
+
function slackBinaryCandidates() {
|
|
39
|
+
const local = path.join(slackCliLocalBinDir(), 'slack');
|
|
40
|
+
return [local, ...SLACK_BINARIES];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param {typeof execFileDefault} execFileImpl
|
|
45
|
+
* @param {string} bin
|
|
46
|
+
* @param {Object} [deps]
|
|
47
|
+
* @returns {Promise<boolean>}
|
|
48
|
+
*/
|
|
49
|
+
async function binaryResponds(execFileImpl, bin, deps = {}) {
|
|
50
|
+
try {
|
|
51
|
+
await execFileImpl(bin, ['--version'], {
|
|
52
|
+
timeout: 20000,
|
|
53
|
+
env: withSlackCliPath(deps.env || process.env)
|
|
54
|
+
});
|
|
55
|
+
return true;
|
|
56
|
+
} catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @param {Object} [deps]
|
|
63
|
+
* @returns {Promise<string|null>}
|
|
64
|
+
*/
|
|
65
|
+
async function resolveSlackBinary(deps = {}) {
|
|
66
|
+
if (typeof deps.slackBinary === 'string' && deps.slackBinary.trim()) {
|
|
67
|
+
return deps.slackBinary.trim();
|
|
68
|
+
}
|
|
69
|
+
if (shouldSkipLiveSlackCli(deps)) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const execFileImpl = deps.execFile || execFileDefault;
|
|
73
|
+
for (const bin of slackBinaryCandidates()) {
|
|
74
|
+
if (await binaryResponds(execFileImpl, bin, deps)) {
|
|
75
|
+
return bin;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Slack CLI `api` does not load ~/.slack/credentials.json unless a token is set.
|
|
83
|
+
* @param {Object} [deps]
|
|
84
|
+
* @returns {NodeJS.ProcessEnv}
|
|
85
|
+
*/
|
|
86
|
+
function slackApiChildEnv(deps = {}) {
|
|
87
|
+
const env = withSlackCliPath(deps.env || process.env);
|
|
88
|
+
if (env.SLACK_USER_TOKEN || env.SLACK_BOT_TOKEN) {
|
|
89
|
+
return env;
|
|
90
|
+
}
|
|
91
|
+
if (typeof deps.slackUserToken === 'string' && deps.slackUserToken.trim()) {
|
|
92
|
+
return { ...env, SLACK_USER_TOKEN: deps.slackUserToken.trim() };
|
|
93
|
+
}
|
|
94
|
+
const token = slackUserTokenForTeam(readSlackCredentialEntries(deps), deps.teamId);
|
|
95
|
+
return token ? { ...env, SLACK_USER_TOKEN: token } : env;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Slack CLI 4.x: `slack api <method> --json '{...}' [-w team]`.
|
|
100
|
+
* @param {string} method
|
|
101
|
+
* @param {Object} [payload]
|
|
102
|
+
* @param {Object} [deps]
|
|
103
|
+
* @returns {string[]}
|
|
104
|
+
*/
|
|
105
|
+
function buildSlackApiArgs(method, payload = {}, deps = {}) {
|
|
106
|
+
const args = ['api', method, '--json', JSON.stringify(payload || {}), '--no-color', '-s'];
|
|
107
|
+
if (typeof deps.teamId === 'string' && deps.teamId.trim()) {
|
|
108
|
+
args.push('-w', deps.teamId.trim());
|
|
109
|
+
}
|
|
110
|
+
return args;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {string} stdout
|
|
115
|
+
* @returns {Object}
|
|
116
|
+
*/
|
|
117
|
+
function parseJsonStdout(stdout) {
|
|
118
|
+
const text = String(stdout || '').trim();
|
|
119
|
+
if (!text) {
|
|
120
|
+
return {};
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
return JSON.parse(text);
|
|
124
|
+
} catch {
|
|
125
|
+
const start = text.indexOf('{');
|
|
126
|
+
const end = text.lastIndexOf('}');
|
|
127
|
+
if (start >= 0 && end > start) {
|
|
128
|
+
return JSON.parse(text.slice(start, end + 1));
|
|
129
|
+
}
|
|
130
|
+
throw wrapSlackCliError(new Error('Slack CLI returned non-JSON output'));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @param {unknown} error
|
|
136
|
+
* @returns {Object|null}
|
|
137
|
+
*/
|
|
138
|
+
function jsonBodyFromCliError(error) {
|
|
139
|
+
const stdout = error && error.stdout;
|
|
140
|
+
if (!stdout) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const body = parseJsonStdout(stdout);
|
|
145
|
+
return body && typeof body === 'object' ? body : null;
|
|
146
|
+
} catch {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Documented `slack api <method>` with a JSON payload.
|
|
153
|
+
* @param {string} method
|
|
154
|
+
* @param {Object} [payload]
|
|
155
|
+
* @param {Object} [deps]
|
|
156
|
+
* @returns {Promise<Object>}
|
|
157
|
+
*/
|
|
158
|
+
async function runSlackApi(method, payload = {}, deps = {}) {
|
|
159
|
+
if (typeof deps.slackApi === 'function') {
|
|
160
|
+
return deps.slackApi(method, payload);
|
|
161
|
+
}
|
|
162
|
+
const bin = await resolveSlackBinary(deps);
|
|
163
|
+
if (!bin) {
|
|
164
|
+
throw slackProvisioningError(
|
|
165
|
+
'Slack CLI (`slack`) is not installed or not on PATH.',
|
|
166
|
+
'SLACK_CLI_MISSING'
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
const execFileImpl = deps.execFile || execFileDefault;
|
|
170
|
+
try {
|
|
171
|
+
const result = await execFileImpl(bin, buildSlackApiArgs(method, payload, deps), {
|
|
172
|
+
cwd: deps.cwd,
|
|
173
|
+
env: slackApiChildEnv(deps),
|
|
174
|
+
timeout: deps.timeout || 180000,
|
|
175
|
+
maxBuffer: 2 * 1024 * 1024
|
|
176
|
+
});
|
|
177
|
+
return parseJsonStdout(result && result.stdout);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
const body = jsonBodyFromCliError(error);
|
|
180
|
+
if (body) {
|
|
181
|
+
return body;
|
|
182
|
+
}
|
|
183
|
+
throw wrapSlackCliError(error);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Native Slack CLI install (project-based; may no-op without a Slack app project).
|
|
189
|
+
* @param {string} appId
|
|
190
|
+
* @param {Object} [deps]
|
|
191
|
+
* @returns {Promise<Object>}
|
|
192
|
+
*/
|
|
193
|
+
async function runNativeAppInstall(appId, deps = {}) {
|
|
194
|
+
const bin = await resolveSlackBinary(deps);
|
|
195
|
+
if (!bin || !appId) {
|
|
196
|
+
return {};
|
|
197
|
+
}
|
|
198
|
+
const execFileImpl = deps.execFile || execFileDefault;
|
|
199
|
+
const args = ['app', 'install', '-a', String(appId), '--no-color', '-s', '-f'];
|
|
200
|
+
if (typeof deps.teamId === 'string' && deps.teamId.trim()) {
|
|
201
|
+
args.push('-w', deps.teamId.trim());
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
const result = await execFileImpl(bin, args, {
|
|
205
|
+
cwd: deps.cwd,
|
|
206
|
+
env: slackApiChildEnv(deps),
|
|
207
|
+
timeout: deps.timeout || 180000,
|
|
208
|
+
killSignal: 'SIGKILL',
|
|
209
|
+
maxBuffer: 2 * 1024 * 1024
|
|
210
|
+
});
|
|
211
|
+
return parseJsonStdout(result && result.stdout);
|
|
212
|
+
} catch {
|
|
213
|
+
return {};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
module.exports = {
|
|
218
|
+
isJestWorker,
|
|
219
|
+
shouldSkipLiveSlackCli,
|
|
220
|
+
resolveSlackBinary,
|
|
221
|
+
runSlackApi,
|
|
222
|
+
runNativeAppInstall,
|
|
223
|
+
parseJsonStdout,
|
|
224
|
+
buildSlackApiArgs,
|
|
225
|
+
slackApiChildEnv
|
|
226
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack channel provisioning constants.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Shared keys — never persist Slack tokens
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const STATE_KIND = 'slack-channel';
|
|
10
|
+
const STATE_FILE = 'provisioning-state.json';
|
|
11
|
+
const SLACK_BINARIES = ['slack'];
|
|
12
|
+
const SECRET_KEY_RE =
|
|
13
|
+
/^(accessToken|refreshToken|botToken|bot_token|signingSecret|signing_secret|client_secret|clientSecret|password)$/i;
|
|
14
|
+
const KV_SIGNING_SECRET = 'kv://slack/signingSecret';
|
|
15
|
+
const KV_BOT_TOKEN = 'kv://slack/botToken';
|
|
16
|
+
const KV_CLIENT_SECRET = 'kv://slack/clientSecret';
|
|
17
|
+
/** Slack create/install writes these into integration .env, then dataplane push. */
|
|
18
|
+
const SLACK_PROVISIONED_KV_KEYS = [KV_SIGNING_SECRET, KV_BOT_TOKEN, KV_CLIENT_SECRET];
|
|
19
|
+
const SLACK_APPS_URL = 'https://api.slack.com/apps';
|
|
20
|
+
const SLACK_CLI_DOCS_URL = 'https://docs.slack.dev/tools/slack-cli';
|
|
21
|
+
const SLACK_CLI_INSTALL_SCRIPT_URL = 'https://downloads.slack-edge.com/slack-cli/install.sh';
|
|
22
|
+
const REQUIRED_CALLBACK_KEYS = ['eventsUrl', 'interactionUrl', 'commandUrl'];
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
STATE_KIND,
|
|
26
|
+
STATE_FILE,
|
|
27
|
+
SLACK_BINARIES,
|
|
28
|
+
SECRET_KEY_RE,
|
|
29
|
+
KV_SIGNING_SECRET,
|
|
30
|
+
KV_BOT_TOKEN,
|
|
31
|
+
KV_CLIENT_SECRET,
|
|
32
|
+
SLACK_PROVISIONED_KV_KEYS,
|
|
33
|
+
SLACK_APPS_URL,
|
|
34
|
+
SLACK_CLI_DOCS_URL,
|
|
35
|
+
SLACK_CLI_INSTALL_SCRIPT_URL,
|
|
36
|
+
REQUIRED_CALLBACK_KEYS
|
|
37
|
+
};
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Store Slack secrets locally (secret set) then publish to dataplane.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Never log or persist secret values in dist /
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { storeCredentialSecrets } = require('../../api/credential.api');
|
|
10
|
+
const { KV_BOT_TOKEN, KV_CLIENT_SECRET, KV_SIGNING_SECRET } = require('./constants');
|
|
11
|
+
const { writeSlackSecretsEnvFiles } = require('./env-file');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} [value]
|
|
15
|
+
* @returns {boolean}
|
|
16
|
+
*/
|
|
17
|
+
function hasSecretValue(value) {
|
|
18
|
+
return typeof value === 'string' && Boolean(value.trim());
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {string} kvPath
|
|
23
|
+
* @returns {string}
|
|
24
|
+
*/
|
|
25
|
+
function kvPathToSecretSetKey(kvPath) {
|
|
26
|
+
return String(kvPath || '').replace(/^kv:\/\//, '');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {Object} captured
|
|
31
|
+
* @param {Object} credentials
|
|
32
|
+
* @param {string[]} names
|
|
33
|
+
* @returns {string|undefined}
|
|
34
|
+
*/
|
|
35
|
+
function pickNamedSecret(captured, credentials, names) {
|
|
36
|
+
for (const name of names) {
|
|
37
|
+
if (hasSecretValue(captured[name])) return captured[name];
|
|
38
|
+
if (hasSecretValue(credentials[name])) return credentials[name];
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param {unknown} value
|
|
45
|
+
* @returns {Object}
|
|
46
|
+
*/
|
|
47
|
+
function asSecretMap(value) {
|
|
48
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
if (typeof value === 'string' && value.trim().startsWith('{')) {
|
|
52
|
+
try {
|
|
53
|
+
const parsed = JSON.parse(value);
|
|
54
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
55
|
+
} catch {
|
|
56
|
+
return {};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {Object} captured
|
|
64
|
+
* @returns {{ signingSecret?: string, botToken?: string, clientSecret?: string }}
|
|
65
|
+
*/
|
|
66
|
+
function pickSlackSecrets(captured) {
|
|
67
|
+
const source = captured || {};
|
|
68
|
+
const nested = asSecretMap(source.body || source.data);
|
|
69
|
+
const credentials = asSecretMap(source.credentials || nested.credentials);
|
|
70
|
+
const fromSource = pickNamedSecret(source, credentials, ['signingSecret', 'signing_secret']);
|
|
71
|
+
const fromNested = pickNamedSecret(nested, credentials, ['signingSecret', 'signing_secret']);
|
|
72
|
+
return {
|
|
73
|
+
signingSecret: fromSource || fromNested,
|
|
74
|
+
botToken: pickNamedSecret(source, credentials, ['botToken', 'bot_token', 'access_token'])
|
|
75
|
+
|| pickNamedSecret(nested, credentials, ['botToken', 'bot_token', 'access_token']),
|
|
76
|
+
clientSecret: pickNamedSecret(source, credentials, ['clientSecret', 'client_secret'])
|
|
77
|
+
|| pickNamedSecret(nested, credentials, ['clientSecret', 'client_secret'])
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {{ signingSecret?: string, botToken?: string, clientSecret?: string }} secrets
|
|
83
|
+
* @returns {Array<{ key: string, value: string }>}
|
|
84
|
+
*/
|
|
85
|
+
function slackSecretItems(secrets) {
|
|
86
|
+
const pairs = [
|
|
87
|
+
[KV_SIGNING_SECRET, secrets && secrets.signingSecret],
|
|
88
|
+
[KV_BOT_TOKEN, secrets && secrets.botToken],
|
|
89
|
+
[KV_CLIENT_SECRET, secrets && secrets.clientSecret]
|
|
90
|
+
];
|
|
91
|
+
return pairs
|
|
92
|
+
.filter(([, value]) => hasSecretValue(value))
|
|
93
|
+
.map(([key, value]) => ({ key, value: String(value).trim() }));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @param {Array<{ key: string, value: string }>} items
|
|
98
|
+
* @param {Object} deps
|
|
99
|
+
* @returns {Promise<void>}
|
|
100
|
+
*/
|
|
101
|
+
async function writeSlackSecretsLocal(items, deps) {
|
|
102
|
+
const setSecret =
|
|
103
|
+
deps.setSecretInStore || require('../../core/secrets-ensure').setSecretInStore;
|
|
104
|
+
for (const item of items) {
|
|
105
|
+
await setSecret(kvPathToSecretSetKey(item.key), item.value);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @param {Object} [deps]
|
|
111
|
+
* @returns {Promise<boolean>}
|
|
112
|
+
*/
|
|
113
|
+
async function localSlackSigningPresent(deps = {}) {
|
|
114
|
+
if (typeof deps.hasLocalSlackSigningSecret === 'function') {
|
|
115
|
+
return deps.hasLocalSlackSigningSecret();
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
const { loadSecrets } = require('../../core/secrets');
|
|
119
|
+
const secrets = await loadSecrets();
|
|
120
|
+
return hasSecretValue(secrets && secrets['slack/signingSecret']);
|
|
121
|
+
} catch {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @param {Array<{ key: string }>} items
|
|
128
|
+
* @returns {{ stored: boolean, storedSigningSecret: boolean, storedBotToken: boolean,
|
|
129
|
+
* storedClientSecret: boolean }}
|
|
130
|
+
*/
|
|
131
|
+
function slackStoreFlags(items) {
|
|
132
|
+
const keys = new Set(items.map((item) => item.key));
|
|
133
|
+
return {
|
|
134
|
+
stored: items.length > 0,
|
|
135
|
+
storedSigningSecret: keys.has(KV_SIGNING_SECRET),
|
|
136
|
+
storedBotToken: keys.has(KV_BOT_TOKEN),
|
|
137
|
+
storedClientSecret: keys.has(KV_CLIENT_SECRET)
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Write Slack create/install secrets to local store and integration .env, then push.
|
|
143
|
+
*
|
|
144
|
+
* @param {Object} ctx
|
|
145
|
+
* @param {{ signingSecret?: string, botToken?: string, clientSecret?: string }} secrets
|
|
146
|
+
* @returns {Promise<{ stored: boolean, storedSigningSecret: boolean, storedBotToken: boolean,
|
|
147
|
+
* storedClientSecret: boolean }>}
|
|
148
|
+
*/
|
|
149
|
+
async function storeSlackChannelSecrets(ctx, secrets, deps = {}) {
|
|
150
|
+
const items = slackSecretItems(secrets);
|
|
151
|
+
const flags = slackStoreFlags(items);
|
|
152
|
+
if (!flags.stored) {
|
|
153
|
+
return flags;
|
|
154
|
+
}
|
|
155
|
+
await writeSlackSecretsLocal(items, deps);
|
|
156
|
+
writeSlackSecretsEnvFiles(ctx, items, deps);
|
|
157
|
+
const store = deps.storeCredentialSecrets || storeCredentialSecrets;
|
|
158
|
+
await store(ctx.dataplaneUrl, ctx.authConfig, items);
|
|
159
|
+
return flags;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
module.exports = {
|
|
163
|
+
hasSecretValue,
|
|
164
|
+
kvPathToSecretSetKey,
|
|
165
|
+
pickSlackSecrets,
|
|
166
|
+
slackSecretItems,
|
|
167
|
+
localSlackSigningPresent,
|
|
168
|
+
storeSlackChannelSecrets
|
|
169
|
+
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal curl stand-in for hosts where `curl` is not installed.
|
|
3
|
+
* Supports URL download to stdout or `-o` / `--output` (including clustered `-fLo`).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const https = require('https');
|
|
10
|
+
const http = require('http');
|
|
11
|
+
|
|
12
|
+
const MAX_REDIRECTS = 5;
|
|
13
|
+
|
|
14
|
+
function isHttpUrl(value) {
|
|
15
|
+
return /^https?:\/\//i.test(String(value || ''));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Consume `-o FILE`, `--output FILE`, `-fLo FILE`, or `-oFILE`.
|
|
20
|
+
* @returns {number} last consumed argv index
|
|
21
|
+
*/
|
|
22
|
+
function applyOutputFlag(arg, argv, index, state) {
|
|
23
|
+
if (arg === '-o' || arg === '--output') {
|
|
24
|
+
state.output = argv[index + 1] || '';
|
|
25
|
+
return index + 1;
|
|
26
|
+
}
|
|
27
|
+
if (!arg.startsWith('-') || arg.startsWith('--') || arg === '-') {
|
|
28
|
+
return index;
|
|
29
|
+
}
|
|
30
|
+
const body = arg.slice(1);
|
|
31
|
+
const outputAt = body.indexOf('o');
|
|
32
|
+
if (outputAt < 0) {
|
|
33
|
+
return index;
|
|
34
|
+
}
|
|
35
|
+
const inline = body.slice(outputAt + 1);
|
|
36
|
+
if (inline) {
|
|
37
|
+
state.output = inline;
|
|
38
|
+
return index;
|
|
39
|
+
}
|
|
40
|
+
state.output = argv[index + 1] || '';
|
|
41
|
+
return index + 1;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function parseCurlArgs(argv) {
|
|
45
|
+
const state = { output: '', positionals: [] };
|
|
46
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
47
|
+
const arg = argv[i];
|
|
48
|
+
if (arg === '--url') {
|
|
49
|
+
state.positionals.push(argv[i + 1] || '');
|
|
50
|
+
i += 1;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (typeof arg === 'string' && arg.startsWith('--url=')) {
|
|
54
|
+
state.positionals.push(arg.slice('--url='.length));
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const next = applyOutputFlag(arg, argv, i, state);
|
|
58
|
+
if (next !== i) {
|
|
59
|
+
i = next;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (typeof arg === 'string' && arg.startsWith('-')) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
state.positionals.push(arg);
|
|
66
|
+
}
|
|
67
|
+
const url = state.positionals.find(isHttpUrl) || state.positionals[0] || '';
|
|
68
|
+
return { output: state.output, url };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function followRedirect(url, res, redirects, output, resolve, reject) {
|
|
72
|
+
const status = res.statusCode || 0;
|
|
73
|
+
if (!(status >= 300 && status < 400 && res.headers.location)) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
res.resume();
|
|
77
|
+
if (redirects >= MAX_REDIRECTS) {
|
|
78
|
+
reject(new Error('Too many redirects.'));
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
const nextUrl = new URL(res.headers.location, url).toString();
|
|
82
|
+
download(nextUrl, output, redirects + 1).then(resolve, reject);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function download(url, output, redirects) {
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
if (!isHttpUrl(url)) {
|
|
89
|
+
reject(new Error('URL is required.'));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const client = url.startsWith('https:') ? https : http;
|
|
93
|
+
const req = client.get(url, { timeout: 60000 }, (res) => {
|
|
94
|
+
if (followRedirect(url, res, redirects, output, resolve, reject)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if ((res.statusCode || 0) !== 200) {
|
|
98
|
+
res.resume();
|
|
99
|
+
reject(new Error(`Download failed (${res.statusCode || 0}).`));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const dest = output ? fs.createWriteStream(output) : process.stdout;
|
|
103
|
+
res.pipe(dest);
|
|
104
|
+
dest.on('finish', () => {
|
|
105
|
+
if (output && typeof dest.close === 'function') {
|
|
106
|
+
dest.close(() => resolve());
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
resolve();
|
|
110
|
+
});
|
|
111
|
+
dest.on('error', reject);
|
|
112
|
+
});
|
|
113
|
+
req.on('error', reject);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function main(argv) {
|
|
118
|
+
const { output, url } = parseCurlArgs(argv);
|
|
119
|
+
if (!url) {
|
|
120
|
+
throw new Error('URL is required.');
|
|
121
|
+
}
|
|
122
|
+
await download(url, output, 0);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
module.exports = { parseCurlArgs, download, main, isHttpUrl };
|
|
126
|
+
|
|
127
|
+
if (require.main === module) {
|
|
128
|
+
main(process.argv.slice(2)).catch((error) => {
|
|
129
|
+
process.stderr.write(`${error.message}\n`);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack workspace install via apps.developerInstall (bot token).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview HTTP install for portal; signing secret still comes from create only
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { runSlackApi } = require('./cli');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {unknown} value
|
|
13
|
+
* @returns {string}
|
|
14
|
+
*/
|
|
15
|
+
function trimmedString(value) {
|
|
16
|
+
return typeof value === 'string' && value.trim() ? value.trim() : '';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {...unknown} values
|
|
21
|
+
* @returns {string}
|
|
22
|
+
*/
|
|
23
|
+
function firstTrimmed(...values) {
|
|
24
|
+
for (const value of values) {
|
|
25
|
+
const text = trimmedString(value);
|
|
26
|
+
if (text) {
|
|
27
|
+
return text;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return '';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {Object} [manifest]
|
|
35
|
+
* @returns {string[]}
|
|
36
|
+
*/
|
|
37
|
+
function botScopesFromManifest(manifest) {
|
|
38
|
+
const scopes = manifest && manifest.oauth_config && manifest.oauth_config.scopes;
|
|
39
|
+
const bot = scopes && scopes.bot;
|
|
40
|
+
if (!Array.isArray(bot)) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
return bot.map((scope) => trimmedString(scope)).filter(Boolean);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {unknown} value
|
|
48
|
+
* @returns {Object}
|
|
49
|
+
*/
|
|
50
|
+
function asObject(value) {
|
|
51
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {Object} [body]
|
|
56
|
+
* @returns {{ botToken?: string, teamId?: string, botId?: string }}
|
|
57
|
+
*/
|
|
58
|
+
function pickInstalledSlackTokens(body) {
|
|
59
|
+
if (!body || body.ok === false) {
|
|
60
|
+
return {};
|
|
61
|
+
}
|
|
62
|
+
const tokens = asObject(body.api_access_tokens);
|
|
63
|
+
const botToken = firstTrimmed(tokens.bot, body.bot_token, body.access_token, body.botToken);
|
|
64
|
+
if (!botToken) {
|
|
65
|
+
return {};
|
|
66
|
+
}
|
|
67
|
+
const teamId = firstTrimmed(body.team_id, asObject(body.team).id);
|
|
68
|
+
const botId = firstTrimmed(body.bot_id, asObject(body.bot).id);
|
|
69
|
+
return {
|
|
70
|
+
botToken,
|
|
71
|
+
...(teamId ? { teamId } : {}),
|
|
72
|
+
...(botId ? { botId } : {})
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @param {string} appId
|
|
78
|
+
* @param {Object} [deps]
|
|
79
|
+
* @returns {Object}
|
|
80
|
+
*/
|
|
81
|
+
function developerInstallPayload(appId, deps = {}) {
|
|
82
|
+
const payload = { app_id: appId };
|
|
83
|
+
const scopes = Array.isArray(deps.botScopes)
|
|
84
|
+
? deps.botScopes.map((scope) => trimmedString(scope)).filter(Boolean)
|
|
85
|
+
: [];
|
|
86
|
+
if (scopes.length) {
|
|
87
|
+
payload.bot_scopes = scopes;
|
|
88
|
+
}
|
|
89
|
+
return payload;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Same method Slack CLI uses. Returns bot token without hanging on `app install`.
|
|
94
|
+
* @param {string} appId
|
|
95
|
+
* @param {Object} [deps]
|
|
96
|
+
* @returns {Promise<{ botToken?: string, teamId?: string, botId?: string }>}
|
|
97
|
+
*/
|
|
98
|
+
async function developerInstallApp(appId, deps = {}) {
|
|
99
|
+
if (!appId) {
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const body = await runSlackApi(
|
|
104
|
+
'apps.developerInstall',
|
|
105
|
+
developerInstallPayload(appId, deps),
|
|
106
|
+
deps
|
|
107
|
+
);
|
|
108
|
+
return pickInstalledSlackTokens(body);
|
|
109
|
+
} catch {
|
|
110
|
+
return {};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = {
|
|
115
|
+
botScopesFromManifest,
|
|
116
|
+
pickInstalledSlackTokens,
|
|
117
|
+
developerInstallApp
|
|
118
|
+
};
|